Architecture
Last updated
A high-level view of how Agenvoy fits together. For per-module diagrams, sequence flows, and the tool-dispatch state machine, jump into the topic-specific pages.
Overview
graph TB
subgraph Entry["Entry · cmd/app"]
App["agen · TUI (socket client) + daemon<br/>agen stop · update · --daemon<br/>non-TTY stdin · MCP server"]
Web["Web dashboard · page/<br/>embedded in the binary<br/>served at 127.0.0.1 / [::1]:17989"]
IPC["Daemon IPC · internal/runtime/ipc<br/>~/.config/agenvoy/daemon.sock · 0600<br/>run · pending · cancel · steer · reply · mcp"]
end
subgraph Engine["Engine · internal/agents/exec"]
Run["exec.Prepare · exec.Run<br/>skill match → named skill → exec/model.Route<br/>session model · dispatcher · tiers · priority"]
Execute["exec.Execute · ≤256 iterations<br/>3-pass parallel tool calls<br/>7 fixed tools loaded, rest by name via run_tool"]
Sub["exec/subagent.Exec<br/>in-process · max(NumCPU/2, 3) concurrent<br/>background legs (TUI)"]
Compact["compact · threshold → old history → tool history<br/>overflow → learned limit → decision prune → trim"]
Decision["decision · internal/agents/decision<br/>TypeSafe / OpenRouter · 3 s typed calls<br/>dispatch · watch · web filter · turn context · summary · trim"]
end
subgraph Pending["Pending · internal/runtime"]
Reg["Prefix-routed listener registry<br/>per-entry buffered=1 reply ch<br/>TUI cli- (per window) · web chat- · Telegram tg- · Discord dc-"]
BG["Background jobs · internal/tools/interactive<br/>cmd_ · sub_ legs grouped per turn<br/>report to open task · run next when ended · cancel"]
end
subgraph Providers["go-llm-router v0.10.1 · 14 provider entries + compat<br/>incl. claude-code · + TypeSafe for decisions"]
P["OpenAI · Codex · Claude · Gemini<br/>Grok · Grok (xAI) · Copilot · DeepSeek<br/>Mistral · NVIDIA · Ollama Cloud · OpenRouter<br/>Cloudflare · + any OpenAI-compatible endpoint<br/>Claude Code · local claude CLI, opt-in"]
end
subgraph Tools["Tool Subsystem · internal/tools"]
T["find_files · read_files · edit_file · file_history<br/>search_web · fetch_page · http_request<br/>subagents · schedules · run_skill · ask_user<br/>chat_history · error_history · find_tools · run_tool<br/>run_command · background_command · background_subagents<br/>public_api_list · reasoning_guide · write_result · html_template"]
MCP["MCP · internal/runtime/mcp<br/>client + server · go-sdk"]
Adapter["toolAdapter · internal/runtime<br/>script_* · ext_*"]
end
subgraph Security["Security · go-pkg/sandbox"]
S["bwrap (Linux) / sandbox-exec (macOS)<br/>filesystem.Policy · DeniedMap"]
end
subgraph Session["Session · internal/session"]
SL["history.json · summary.json<br/>action.log · pending/<br/>fsnotify watch"]
end
subgraph Memory["Memory · SQLite + ToriiDB"]
M["history.db · session · message_meta<br/>messages + FTS5 · action_history<br/>file_history · usage + tool call ids<br/>db_0..db_3 · error_memory 90d TTL"]
end
subgraph Kura["KuraDB · user-registered MCP server"]
K["kura mcp<br/>one entry in mcp.json<br/>mcp__kura__list_rag · mcp__kura__search_rag"]
end
App -->|Unix socket| IPC
IPC --> Run
Web --> Run
Run --> Execute
Execute -->|subagents| Sub
Sub -->|exec.Run isSub| Run
Execute --> Compact
Execute -->|tool gap · skill filter| Decision
Compact --> Decision
Execute -->|Send| Providers
Execute -->|tool calls| Tools
Tools --> MCP
Tools --> Adapter
Tools --> Security
Tools -.->|mcp__kura__*<br/>absent when the server is not registered| Kura
Execute <-->|confirm/ask| Pending
IPC <-->|ask · reply| Pending
Sub <-->|subagent ask_user| Pending
Tools -->|background| BG
BG -->|resume| Run
Execute <--> Memory
Execute <--> Session- graph TB
- subgraph Entry["Entry · cmd/app"]
- App["agen · TUI (socket client) + daemon<br/>agen stop · update · --daemon<br/>non-TTY stdin · MCP server"]
- Web["Web dashboard · page/<br/>embedded in the binary<br/>served at 127.0.0.1 / [::1]:17989"]
- IPC["Daemon IPC · internal/runtime/ipc<br/>~/.config/agenvoy/daemon.sock · 0600<br/>run · pending · cancel · steer · reply · mcp"]
- end
- subgraph Engine["Engine · internal/agents/exec"]
- Run["exec.Prepare · exec.Run<br/>skill match → named skill → exec/model.Route<br/>session model · dispatcher · tiers · priority"]
- Execute["exec.Execute · ≤256 iterations<br/>3-pass parallel tool calls<br/>7 fixed tools loaded, rest by name via run_tool"]
- Sub["exec/subagent.Exec<br/>in-process · max(NumCPU/2, 3) concurrent<br/>background legs (TUI)"]
- Compact["compact · threshold → old history → tool history<br/>overflow → learned limit → decision prune → trim"]
- Decision["decision · internal/agents/decision<br/>TypeSafe / OpenRouter · 3 s typed calls<br/>dispatch · watch · web filter · turn context · summary · trim"]
- end
- subgraph Pending["Pending · internal/runtime"]
- Reg["Prefix-routed listener registry<br/>per-entry buffered=1 reply ch<br/>TUI cli- (per window) · web chat- · Telegram tg- · Discord dc-"]
- BG["Background jobs · internal/tools/interactive<br/>cmd_ · sub_ legs grouped per turn<br/>report to open task · run next when ended · cancel"]
- end
- subgraph Providers["go-llm-router v0.10.1 · 14 provider entries + compat<br/>incl. claude-code · + TypeSafe for decisions"]
- P["OpenAI · Codex · Claude · Gemini<br/>Grok · Grok (xAI) · Copilot · DeepSeek<br/>Mistral · NVIDIA · Ollama Cloud · OpenRouter<br/>Cloudflare · + any OpenAI-compatible endpoint<br/>Claude Code · local claude CLI, opt-in"]
- end
- subgraph Tools["Tool Subsystem · internal/tools"]
- T["find_files · read_files · edit_file · file_history<br/>search_web · fetch_page · http_request<br/>subagents · schedules · run_skill · ask_user<br/>chat_history · error_history · find_tools · run_tool<br/>run_command · background_command · background_subagents<br/>public_api_list · reasoning_guide · write_result · html_template"]
- MCP["MCP · internal/runtime/mcp<br/>client + server · go-sdk"]
- Adapter["toolAdapter · internal/runtime<br/>script_* · ext_*"]
- end
- subgraph Security["Security · go-pkg/sandbox"]
- S["bwrap (Linux) / sandbox-exec (macOS)<br/>filesystem.Policy · DeniedMap"]
- end
- subgraph Session["Session · internal/session"]
- SL["history.json · summary.json<br/>action.log · pending/<br/>fsnotify watch"]
- end
- subgraph Memory["Memory · SQLite + ToriiDB"]
- M["history.db · session · message_meta<br/>messages + FTS5 · action_history<br/>file_history · usage + tool call ids<br/>db_0..db_3 · error_memory 90d TTL"]
- end
- subgraph Kura["KuraDB · user-registered MCP server"]
- K["kura mcp<br/>one entry in mcp.json<br/>mcp__kura__list_rag · mcp__kura__search_rag"]
- end
- App -->|Unix socket| IPC
- IPC --> Run
- Web --> Run
- Run --> Execute
- Execute -->|subagents| Sub
- Sub -->|exec.Run isSub| Run
- Execute --> Compact
- Execute -->|tool gap · skill filter| Decision
- Compact --> Decision
- Execute -->|Send| Providers
- Execute -->|tool calls| Tools
- Tools --> MCP
- Tools --> Adapter
- Tools --> Security
- Tools -.->|mcp__kura__*<br/>absent when the server is not registered| Kura
- Execute <-->|confirm/ask| Pending
- IPC <-->|ask · reply| Pending
- Sub <-->|subagent ask_user| Pending
- Tools -->|background| BG
- BG -->|resume| Run
- Execute <--> Memory
- Execute <--> Session
run_command_background was renamed background_command, and run_websocket, pkg_manage and user-defined api_* tools were dropped (Removed in v1.3.2); use background_command, run_command with sudo or sandbox: false, and the built-in public_api_list instead.Cross-cutting principles
- OS-native sandbox over Go-side filters — security policy is enforced at the OS boundary; new restrictions go into
go-pkg/sandbox, not into agenvoy callers - Prompt as policy — system-prompt protection (the guardrail rules from
configs/jsons/guardrail_rules.json) and the cross-tool output rules live in the system prompt underconfigs/prompts/, while which tool fits which request lives in that tool's own description; adding a category means editing the prompt or the description, not the engine. The permission mode is no longer stated in the system prompt (Removed in v1.3.7); the confirm gate enforces it - In-process over HTTP for subagents —
subagents(mode=invoke)goes throughexec/subagent.Exec, which callsexec.Run(..., isSub=true)directly, sharing the same provider clients, sandbox, pending registry, and memory layer;AllowAllandWorkDirflow through ctx - Read tools fan out, write tools serialize — concurrency is opt-in and requires both "no side effects" and "upstream allows parallelism"
- One config layer per concern — provider credentials in OS keychain, registered models, priority, tiers, and limits in
config.json, MCP inmcp.json, session config and persona in the SQLitesessiontable; each tool author / user touches at most one place - One daemon runs every agent — the browser dashboard, the chat channels, and external callers reach the daemon over HTTP on
127.0.0.1:17989(also[::1]:17989); since v1.2.0 the TUI no longer runs agents or opens MCP connections itself and sends runs, pending resumes, cancels, steers, replies and/mcpactions over the Unix socket ~/.config/agenvoy/daemon.sock (mode 0600, newline-delimited JSON). The dashboard is embedded rather than hosted, so there is no second origin to keep in sync - Vendor code lives upstream — provider adapters moved to
go-llm-routerand the MCP protocol to the official go-sdk; a fix belongs in the upstream module rather than a local shim