Documentation v0.30.0

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:

  1. Filesystem — rm -rf / rm -r, deleting directories or existing files not produced by this task
  2. Database — DROP DATABASE / DROP TABLE / TRUNCATE, DELETE / UPDATE without WHERE, any production DSN
  3. Git — reset --hard, push --force to main/master, deleting shared branches, clean -fdx
  4. System — chmod 777 / chown -R, edits under /etc / /usr / /System, launchctl / systemd changes, sudo escalation
  5. Overwrite — an unread non-empty existing file, .env / credentials / lock files / .git/index
  6. Cloud & infra — gcloud / aws / kubectl delete, terraform destroy
  7. Process — shutdown / reboot, kill -9 on 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.

中文