# Runtime Layers

The runtime layers of Agenvoy: session storage, dashboard, pending routing, memory, scheduler, IPC and TUI, with the package that implements each.

| Layer | Package | Responsibility |
|---|---|---|
| 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)` or `RegisterListenerMatch(prefix, match)`, claim via `PickNextMatch(prefix, accept)`. Since v1.3.0 `internal/tools/interactive` also runs a task's background work (since v1.3.2 `background_command` and `background_subagents`; `run_command_background` was renamed and `run_websocket` dropped, Removed in v1.3.2). Since v1.3.8 the legs of one turn form a job file under `sessions/<id>/background/`: the exec loop takes their reports while the task's pending entry exists, and `internal/app` runs a finished job's `objective` and `next` as a new task once the task has ended; resuming the task itself on every report was Removed in v1.3.8 |
| 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 |
| IPC | `internal/runtime/ipc` | since v1.2.0, the daemon's Unix socket `~/.config/agenvoy/daemon.sock` (mode 0600, newline-delimited JSON). Client to daemon frames: `run`, `pending`, `cancel` (with `pause`), `steer`, `reply`, `mcp`, and since v1.3.8 `background_cancel` (one leg); daemon to client: `event`, `ask`, `done`, `verify`, `mcp` (`workdir`: Removed in v1.3.0, along with the agent's `cd`). A `run` frame no longer resumes a pending task with background work as a follow-up (Removed in v1.3.8). A run belongs to the connection that started it: closing that connection cancels the run and keeps its pending task. Confirmations and `ask_user` questions go only to the TUI window (window hash) that started the run; a restricted-path approval carries the password typed in the TUI, which the daemon checks with `sudo -S -v` |
| TUI | `internal/runtime/tui` | bubbletea inline-chat front-end; single-package by design. A client of the daemon over `internal/runtime/ipc`: it reconnects every 5 s and shows `Connecting` while offline, holding plain input, skills and `/pending` until connected, while local `/` commands still run. Since v1.3.0 the work directory belongs to the TUI (`Shift+P`), and a `!` line runs as a shell command in the TUI process before its result is sent to the daemon as a run. Removed in v1.2.0: running agents inside the TUI process was removed; use the daemon, which the TUI reaches over the socket |
