Sessions & Agents
Session
A session is the core unit in Agenvoy. Each session has its own conversation context, memory, agent persona, and tool configuration.
Storage path: ~/.config/agenvoy/sessions/<sid>/
| File | Purpose |
|---|---|
history.json |
Recent conversation, delta-appended |
summary.json / .summary_cursor |
Rolling summary and its incremental cursor |
action.log |
Tool call audit log. One line per event, [timestamp][window_hash][kind][task_hash] body (window_hash is the writing window's hash, the same value events carry as window_hash); the trailing task_hash bracket was added in v1.0.2 so every line can be traced back to the run that wrote it, and the replay parser treats it as optional so pre-v1.0.2 lines still load |
.cmd_history |
TUI input history for this session |
pending/ |
Pending ask_user / confirm metadata, one JSON per task hash |
Everything else moved into ~/.config/agenvoy/.store/history.db:
| SQLite table | Content |
|---|---|
session |
Display name, self_id, persona, model, reasoning level, rule |
note + note_fts5 |
Operator notes and their trigram full-text index (v0.35.3) |
messages + messages_fts5 |
Full message archive and its full-text index |
action_history |
Finished runs and their tool results |
file_history |
Recorded versions of every file the tools changed |
usage |
Per-model token spend, 28-day retention |
Vectors, tool cache, error memory, and running-task presence live in ToriiDB (.store/db_0 ... db_3). Legacy bot.json / bot.md / config.json / status.json / usage.log are migrated into SQLite at daemon startup and no longer written. The state table was dropped in v0.35.3: a running task is now an action:<session_id>:<task_hash> key in ToriiDB, refreshed every 55 s with a 60 s TTL, so a killed process stops being online on its own rather than needing a PID liveness check.
Session prefixes and lifetime
| Prefix | Lifetime |
|---|---|
cli-* |
Permanent — local CLI / TUI work (TUI /new, or POST /v1/session) |
chat-* |
Permanent — web / API work (POST /v1/send with persist=true) |
dc-* |
Permanent (Discord channels) |
tg-* |
Permanent (Telegram chats — per-chat, shared across users in that chat) |
temp-* |
Reaped after 30 min idle (default for POST /v1/send and subagent sessions) |
Cleanup runs every 30 minutes via cron (and once on startup) against the temp-* prefix only — cli-*, chat-*, dc-*, and tg-* are never auto-reaped.
The prefix also decides where a confirmation goes back to, and it is what the TUI session picker groups by: with at least two groups present the picker shows an all tab plus one tab per prefix, current session first. A daemon-side fsnotify watcher logs the ID and configured name of every newly created session directory.
Agent persona
Each session carries its own persona, stored in the session row rather than a file:
| Field | Notes |
|---|---|
name |
Display name |
self_id |
Normalized to lowercase, up to 32 ASCII letters, digits, _ or -; non-empty values must be unique across sessions (a duplicate returns 409); the lookup key for the subagents self_id parameter |
rule |
Persona body, rendered into the system prompt's ## Bot Persona block on each turn |
model / reasoning |
Session model and reasoning level; model: "" resets to auto |
Edit any of them from the TUI with /bot, or through GET / POST /v1/session/:id — one endpoint for the whole session object, with POST as a partial update.
Agent routing
Two ways decide which agent handles a task:
1. Automatic — A dispatcher LLM analyzes the input and picks the best-fit provider via ResolveAgent().
Startup order: exec.Prepare (TUI-only exclusions, /skill-name match) → exec.Start (named skill resolution, agent resolution, session load) → Execute. A named skill that is excluded or not found fails the run.
The :self-id one-shot override prefix was removed; delegate to a named session with subagents(self_id=...), or switch sessions with /sessions.
2. subagents tool — An agent calls another agent in-process (no HTTP) during execution, inheriting AllowAll and WorkDir from the parent ctx.
Subagents run under a collection-only charter — they gather and report, they don't nest or write. The exclusion set is built from three parts:
| Source | Excluded |
|---|---|
| Charter base | subagents, edit_file, generate* (wildcard) |
| TUI-only tools / skills | Whatever configs/jsons/tui_tools.json lists, e.g. extension-upload, extension-install |
Caller's exclude_tools |
Anything the parent passes in |
ask_user is not excluded — subagents can ask the user through the shared pending registry. A subagent's pending question is also published to the owning (parent) session, and the leg waits for that resume and returns the answered result. At most three subagent legs execute concurrently (maxConcurrentSubagents); a fourth waits for a slot while its own MaxSubagentTimeoutMin (30 min) timeout is already running, so wide fan-outs should be dispatched in batches of three. Each result carries a usage prefix that rolls up into the parent session's totals.
Permission mode
| Mode | Behavior |
|---|---|
single-confirm |
Each non-ReadOnly tool call requires user confirmation (the TUI default) |
always-allow |
Tools auto-execute; the LLM is instructed to invoke ask_user first for seven categories of truly irreversible operations |
The seven irreversible categories that still require explicit ask_user under always-allow:
- Filesystem —
rm -rf/rm -r, deleting directories or existing files not produced by this task - Database —
DROP DATABASE/DROP TABLE/TRUNCATE,DELETE/UPDATEwithoutWHERE, any production DSN - Git —
reset --hard,push --forceto main/master, deleting shared branches,clean -fdx - System —
chmod 777/chown -R, edits under/etc//usr//System, launchctl / systemd changes, sudo escalation - Overwrite — an unread non-empty existing file,
.env/ credentials / lock files /.git/index - Cloud & infra —
gcloud/aws/kubectl delete,terraform destroy - Process —
shutdown/reboot,kill -9on system service PIDs
Ordinary writes (edit_file, build and test commands, git status / add / commit, read-only shell) proceed directly. The gate is enforced by the system prompt (configs/prompts/system_prompt/permission/always_allow.md), not by hardcoded Go-side filters.
Per-session concurrency
MaxSessionTasks (NumCPU × 4) limits how many concurrent Execute() calls a single session can run. A task publishes its ToriiDB online marker and registers its cancel function before competing for a slot, so a queued task stays visible and cancellable instead of blocking invisibly. The marker carries a 60 s TTL refreshed every 55 s, so a killed or crashed process stops counting as online by expiry rather than by a PID check. The value is a package constant — it is not read from config.json.
Cancelling work
Cancellation is per task, not per session: POST /v1/session/:id/cancel/:task_hash stops one running task ({"cancelled": true}); when that task hash is not running in this process it records a canceled event and returns {"cancelled": false, "stale": true}. The former once_id identifier was replaced by the task hash. Tool confirmations are answered separately with POST /v1/session/:id/confirm/:confirm_hash, where confirm_hash comes from the confirm event. There is deliberately no cancel-everything variant, so one long-running job can be dropped without disturbing the session's other tasks. In the TUI, Esc during a run opens the cancel-confirmation popup for the active task.
Resuming interrupted work
A run that stopped on an ask_user or a confirmation leaves a pending task behind. GET /v1/session/:id/task lists the ones that are actually resumable: a live run refreshes action:<session_id>:<task_hash> in ToriiDB every 55 s with a 60 s TTL, so a task abandoned by a closed window or a killed process reappears in that list within a minute, while a task still being worked on does not. Answer it with POST .../resume, or drop it with DELETE .../:task_hash. In the TUI this is /pending.
Completed runs stay queryable too: GET /v1/session/:id/task/history lists them as task_hash, end_at, objective, model, and reasoning (optional keyword filter), and GET .../:task_hash/history returns one run's full action record as content.