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 |