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 |
|---|---|
bot.md |
Agent persona definition (frontmatter + markdown body) |
status.json |
Current execution state and active task list |
action.log |
Tool call audit log |
summary.json / summary.meta.json |
Rolling summary and its incremental cursor |
usage.log |
Per-model token usage records feeding /usage |
input_history |
TUI input history for this session |
mcp.json |
Session-scoped MCP server configuration |
History, summaries, and config flags live in ToriiDB (DBSessionHist, DBSessionSummary, DBConfig) rather than per-session JSON.
Session prefixes and lifetime
| Prefix | Lifetime |
|---|---|
cli-* |
Permanent (created by the TUI /new, or POST /v1/session) |
http-* |
Permanent (created by 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-*, http-*, dc-*, and tg-* are never auto-reaped.
bot.md — Agent Persona
Each session can declare its own persona:
***
name: mobile-builder
***
You are an expert mobile application architect specializing in
SwiftUI, Jetpack Compose, and React Native...
The frontmatter name doubles as a lookup key (GetSessionIDByName); the body is rendered into the system prompt's ## Bot Persona block on each turn. Edit it from the TUI with /bot, or through GET / POST /v1/session/:id/persona.
Agent routing
Three ways decide which agent handles a task:
1. Automatic — A dispatcher LLM analyzes the input and picks the best-fit provider via SelectAgent().
2. :name one-shot override — Prefix any input with :session-name to dispatch one command at the named session without changing the primary pointer:
:mobile-builder build me a SwiftUI login screen
Resolution order in exec.Run: :name session assign → skill match (/skill-name) → model:<hint> routing bias → Execute. An unresolved name fails the run rather than silently falling back.
3. invoke_subagent 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 | invoke_subagent, list_subagent_sessions, write_file, patch_file, generate* (wildcard) |
| TUI-only tools / skills | install_dependence; 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. 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 (default for agen cli) |
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 (write_file / patch_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/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. Excess callers wait via EnterConcurrent(sid) and only appear in status.json once a slot is free. 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_id stops one running task, with task_id taken from /v1/session/:id/status. 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.