# Architecture

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

```mermaid
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 under `configs/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 through `exec/subagent.Exec`, which calls `exec.Run(..., isSub=true)` directly, sharing the same provider clients, sandbox, pending registry, and memory layer; `AllowAll` and `WorkDir` flow 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 in `mcp.json`, session config and persona in the SQLite `session` table; 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 `/mcp` actions 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-router` and the MCP protocol to the official go-sdk; a fix belongs in the upstream module rather than a local shim
