A personal AI agent built on the Claude Agent SDK, heavily inspired by OpenClaw.
Fera is an always-on AI assistant with its own persistent memory and personality. It connects to your life through multiple I/O channels and wakes up periodically to check if there's work to do.
+-----------+
| Heartbeat| (wakes every 30 min)
+-----+-----+
|
v
+--------+ +--------+ +-----------+ +----------+
| Signal | | Email | | Fera | | Webhooks |
+--------+ +--------+ | Core | +----------+
| Terminal| | Web UI | | (Claude) | | External |
+--------+ +--------+ +-----------+ | Services |
I/O Channels | +----------+
|
+--------+--------+
| |
+-----+-----+ +------+------+
| Memory | | MCP |
| (persistent| | Connectors |
| journal) | | (Redmine, |
+------------+ | Home Asst, |
| ...) |
+-------------+
The brain. Built on the Claude Agent SDK, this is the central reasoning engine. It has a defined personality, maintains conversational context, and decides what to do based on incoming messages, scheduled wake-ups, and external events.
Pluggable adapters for communication:
- Web UI — browser-based chat interface
- Telegram — bot integration with streaming responses, voice transcription, and file handling
- Email — read-only access + drafts via
himalaya-wrapper - Mattermost — planned
- Signal — planned (via signal-cli)
Each adapter normalizes messages into a common format and routes them through the gateway's event bus.
The agent remembers things across sessions. This goes beyond simple conversation history — it includes facts, preferences, decisions, and learned behaviors stored in a structured journal.
A scheduler that wakes the agent every 30 minutes (configurable). On wake, the agent checks for pending tasks, unread messages, calendar events, or anything else that needs attention. If there's nothing to do, it goes back to sleep.
An HTTP endpoint that external systems can call to notify Fera of events. This enables integrations with CI/CD, monitoring, calendar systems, or anything that can send an HTTP request.
Fera uses the Model Context Protocol to interact with external services:
- Project management (Redmine, GitHub Issues, etc.)
- Home automation (Home Assistant)
- Custom tools as needed
- Language: Python 3.11
- Agent framework: Claude Agent SDK (Python)
- Gateway: WebSocket server (websockets)
- Memory: Markdown files indexed in SQLite (FTS5 + sqlite-vec)
- Embeddings: Local ONNX via fastembed (all-MiniLM-L6-v2)
- Search: Hybrid RRF fusion + optional Haiku reranking/query expansion
- Build: hatchling, managed with uv
- Python 3.11+
- uv
- Claude Code CLI installed.
- Ensure an
ANTHROPIC_API_KEYis in your environment or you have a Claude Code OAuth token (obtained viaclaude setup-token). - (Optional)
ANTHROPIC_API_KEYfor deep search mode (query expansion + reranking)
uv syncTwo terminals:
Terminal 1 — Gateway:
# Without deep search:
uv run fera-gateway
# With deep search (query expansion + reranking):
ANTHROPIC_API_KEY=sk-... uv run fera-gatewayTerminal 2 — Web UI:
uv run fera-webuiThe gateway listens on localhost:8389. The web UI connects and gives you a
chat interface at http://localhost:8080.
Fera reads configuration from $FERA_HOME (defaults to $HOME). All config
files support ${VAR} environment variable substitution.
$FERA_HOME/
config.json # Global config (optional, merged with defaults)
cron.json # Scheduled job definitions (optional)
agents/
main/ # Default agent
config.json # Per-agent config (optional)
workspace/ # Agent workspace files
persona/ # SOUL.md, IDENTITY.md, USER.md, GOALS.md, SOUVENIR.md
memory/ # Markdown memory files
AGENTS.md
TOOLS.md
HEARTBEAT.md # Heartbeat task list
BOOTSTRAP.md
MEMORY.md
data/ # Per-agent runtime data
coding/ # Example second agent
config.json
workspace/
data/
data/
sessions.json # Session name -> SDK session ID mapping
transcripts/ # Per-session JSONL transcript files
Merged with defaults. Only specify keys you want to override.
{
"gateway": {
"host": "127.0.0.1",
"port": 8389,
"auth_token": "auto-generated-on-first-run",
"pool": {
"max_clients": 5,
"idle_timeout_minutes": 30
}
},
"memory": {
"host": "127.0.0.1",
"port": 8390
},
"webui": {
"host": "0.0.0.0",
"port": 8080,
"static_dir": "/opt/fera/webui/dist"
},
"heartbeat": {
"enabled": false,
"interval_minutes": 30,
"active_hours": "08:00-22:00",
"session": "default",
"agent_tools": null
},
"mcp_servers": {
"redmine": {
"type": "sse",
"url": "${REDMINE_MCP_URL}"
}
}
}| Section | Key | Default | Description |
|---|---|---|---|
gateway |
host |
"127.0.0.1" |
Bind address |
port |
8389 |
WebSocket port | |
auth_token |
(auto-generated) | Token for client authentication | |
pool.max_clients |
5 |
Max pooled SDK clients | |
pool.idle_timeout_minutes |
30 |
Idle client eviction timeout | |
memory |
host |
"127.0.0.1" |
Memory server bind address |
port |
8390 |
Memory server port | |
webui |
host |
"0.0.0.0" |
Web UI bind address |
port |
8080 |
Web UI port | |
static_dir |
"/opt/fera/webui/dist" |
Path to built web UI assets | |
heartbeat |
enabled |
false |
Enable periodic heartbeat turns |
interval_minutes |
30 |
Minutes between heartbeat checks | |
active_hours |
"08:00-22:00" |
Time window for heartbeat (local time) | |
session |
"default" |
Session to run heartbeat turns in | |
agent_tools |
null |
Tools for the heartbeat sub-agent. null inherits all parent tools. Set to a list (e.g. ["Read", "Grep", "Bash"]) to restrict. |
|
mcp_servers |
{} |
Global MCP servers available to all agents |
{
"mcp_servers": {
"homeassistant": {
"type": "sse",
"url": "${HA_MCP_URL}"
}
},
"allowed_tools": ["Read", "Glob", "mcp__memory__*", "mcp__homeassistant__*"],
"plugins": [
{"type": "local", "path": "plugins/superpowers"}
],
"adapters": {
"telegram": {
"bot_token": "${TELEGRAM_BOT_TOKEN}",
"allowed_users": [123456789],
"default_session": "default",
"trusted": false
}
}
}| Key | Description |
|---|---|
mcp_servers |
Per-agent MCP servers (merged with global servers) |
allowed_tools |
Explicit tool allowlist. When set, replaces the auto-generated list entirely. Omit to use the default (built-in memory tools + wildcards for all configured MCP servers). Supports exact names and wildcards. Built-in Claude tools: Read, Write, Edit, Bash, Glob, Grep, WebFetch, WebSearch, Task, TodoRead, TodoWrite. Memory MCP tools: mcp__memory__memory_search, mcp__memory__memory_get. Use mcp__<server>__* for MCP server wildcards. |
plugins |
Claude Code plugins to load. Relative paths resolve against workspace. |
adapters.telegram.bot_token |
Telegram bot API token |
adapters.telegram.allowed_users |
List of allowed Telegram user IDs |
adapters.telegram.default_session |
Session name for Telegram conversations |
adapters.telegram.trusted |
If true, skip untrusted content wrapping for text/voice |
Scheduled job definitions triggered by OS crontab via fera-run-job.
{
"jobs": {
"morning-digest": {
"agent": "main",
"payload": "Check Redmine and summarize my open tickets.",
"prompt_mode": "minimal"
},
"medication-reminder": {
"agent": "main",
"session": "default",
"payload": "Remind the user to take their vitamins."
}
}
}| Field | Default | Description |
|---|---|---|
agent |
"main" |
Agent to run the job as |
session |
null |
Named session (persistent, full context). Omit for ephemeral mode. |
payload |
(required) | User message text sent to the agent |
prompt_mode |
"full"/"minimal" |
System prompt mode. Defaults to "full" for session jobs, "minimal" for ephemeral. |
Crontab example:
0 8 * * * fera-run-job morning-digest
0 9 * * * fera-run-job medication-reminder| Command | Description |
|---|---|
fera-gateway |
Start the WebSocket gateway |
fera-memory-server |
Start the memory server |
fera-webui |
Start the web UI server |
fera-create-agent <name> |
Initialize a new agent from templates |
fera-run-job <name> |
Execute a cron job via the gateway |
uv run pytest # run all tests
uv run pytest -v # verbose
make dev # dev container (Podman)
make test # run tests in container| Port | Service | Bind Address | Protocol |
|---|---|---|---|
| 8080 | fera-webui | 0.0.0.0 | HTTP |
| 8389 | fera-gateway | 127.0.0.1 | WebSocket |
| 8390 | fera-memory-server | 127.0.0.1 | HTTP (SSE/MCP) |
The gateway and memory server listen on localhost by default. The web UI binds to 0.0.0.0. For remote access to the gateway, set gateway.host to "0.0.0.0" in config. See INSTALL.md for firewall recommendations.
Early development. Gateway, web UI, and memory system are functional.
- OpenClaw — the primary inspiration for this project
- Claude Agent SDK
- Model Context Protocol