# Architecture Layers

Each layer of Agenvoy, the package that implements it and what it is responsible for.

| Layer | Package | Responsibility |
|---|---|---|
| Entry | `cmd/app` | argv dispatch (`stop` / `update` / `--daemon` / `--enable-claude-code`); MCP server on non-TTY stdin; init sandbox, filesystem policy, MCP manager |
| App services | `internal/app` | services shared by daemon and TUI: agent registry build, `config.json` watcher that reloads the registry and Telegram / Discord, session watcher, MCP setup, chat push hooks, skill runs, daemon spawn |
| Startup | `internal/startup` | start-on-login via a launchd plist (macOS) or systemd service (Linux), toggled from `/config`; moved from `internal/runtime/startup`, which no longer exists |
| Notes | — | operator notes (`internal/note`, the `find_note` tool, `/v1/note*` and the startup notes migration) were removed in v1.0.23; a `note` table left in an older `history.db` is no longer read |
| Runtime singleton | `internal/runtime` | server-mode UID lock; SIGTERM prior server on startup |
| Engine | `internal/agents/exec` | `Prepare` / `Start` (skill match, named skill, model resolution); iteration loop; tool dispatch; dispatcher routing with tiers and priority (`selectAgent.go`, plus the TypeSafe beta dispatcher in `selectAgentBeta.go`; since v1.1.0 a session whose reasoning is `auto` gets its level from the dispatcher's work-kind pick, replacing the global `auto_reasoning` switch, and `pass`-tier models are never picked by the dispatcher or used as fallbacks); cooldown and retry (`retryHandler`); subagent host (`execWithSubagent.go`, at most 3 concurrent) |
| Compaction | `internal/agents/exec/compact` | per-model threshold, tool-history and old-history folding, trim and raw-tool fallbacks |
| Providers | `go-llm-router` v0.8.1 (external module) + `internal/agents/claudeCode` | unified `Agent.Send()` across 13 provider entries plus any OpenAI-compatible endpoint; since v1.1.0 the in-repo `claude-code` provider drives the local `claude` CLI as a model and is off unless Agenvoy is started with `agen --enable-claude-code` (daemon must be stopped); reasoning and fast-mode normalization. Speech-to-text and text-to-speech route separately, backed by OpenAI, Gemini and (since v1.0.17) OpenRouter |
| Tools | `internal/tools` + `internal/runtime/toolAdapter` | built-in / API / script / extension tool definitions |
| MCP | `internal/runtime/mcp` | client and server on the official `modelcontextprotocol/go-sdk`, one package |
| Sandbox | `go-pkg/sandbox` | OS-native isolation, single entry `Wrap()` |
| Filesystem | `go-pkg/filesystem` (+ `reader/`) + `internal/filesystem` | policy-aware writes; ToriiDB pathing |
| Session | `internal/session` | history.json / summary / action.log / pending metadata / fsnotify observer; session config lives in the SQLite `session` table, and liveness is a ToriiDB online marker rather than a persisted `state` row |
| Dashboard | `page/` + `internal/runtime/routes` | Web UI embedded into the binary and served at `/`; `AGENVOY_PAGE_DIR` serves it from disk instead. On startup the daemon also installs a Chrome app launcher pointed at that URL — a bundle under `~/Applications` on macOS, a `.desktop` entry on Linux, a Windows shortcut under WSL — so the dashboard opens as its own window. It is written once and skipped thereafter; a missing Chrome only logs a warning. Since v1.0.1 it also runs offline: `/sw.js` precaches the embedded assets in a service worker and `/vendor/*` serves the third-party libraries from `~/.config/agenvoy/vendor/`, filled once at daemon startup by `internal/runtime/webapp.SyncAsset` and refreshed when the binary version changes |
| Pending | `internal/runtime/pending.go` | prefix-routed confirm/ask listener registry; per-front-end listener via `RegisterListener(prefix)`, claim via `PickNext(prefix)` / `PickNextMatch(prefix, accept)` (`PickNextFor` was removed) |
| Memory | go-sqlkit (SQLite: `session`, `message_meta`, `messages` + FTS5, `action_history`, `file_history`, `usage`) + ToriiDB (`db_0` tool and provider-quota cache, `db_1` chat vectors, `db_2` error memory, `db_3` online markers) | full-text archive (trigram tokenizer), per-model usage (with the tool call ids of each reply since v1.0.26), file snapshots, semantic search, 90-day error memory. The `state` table was dropped in v0.35.3, when liveness moved to ToriiDB online markers; ToriiDB is reached over its own socket since v0.35.3 (now ToriiDB v0.7.0), so the `/v1/toriidb` HTTP gate is gone |
| Scheduler | `internal/runtime` scheduler + fsnotify watcher | cron / one-shot tasks bound to scheduler skills; hot-reload on `{tasks,crons}.json` change |
| TUI | `internal/runtime/tui` | bubbletea inline-chat front-end; single-package by design |
