📖 中文文档:README_zh.md
A coding agent written entirely in the V language, implementing a
ReAct-style agent loop (LLM ↔ tools) in a terminal CLI. This is the long-term
V codeAgents in V project; Phase 1 (current) hardens the tool layer so the
agent is safe to use for editing real code.
-
ReAct loop: the LLM requests tool calls; the agent executes them and feeds results back until the model returns a final answer.
-
Built-in tools:
read_file— paginated text read.write_file— write/overwrite (gated byVCA_ALLOW_WRITE).search_files— regex search (regex.pcre), skips large/binary files.terminal— run a shell command (gated byVCA_ALLOW_EXEC).list_files(Phase 12) — list files/dirs under a path (likels/find), with optional globpatternfilter andrecursivetoggle. Read-only, no write/exec permission needed — handy for the agent to map project structure.web_search(Phase 12) — query the web via DuckDuckGo and return result titles + URLs. Uses the systemcurl, so it is gated byVCA_ALLOW_EXEC(same asterminal); offline or missing curl yields a clear error, never a hang.git(Phase 14) — run a git subcommand in the project (status,diff,log,add,commit -m "...",branch, ...). Gated byVCA_ALLOW_EXECliketerminal; destructive ops (push,reset --hard,clean,rebase,branch -D, ...) route through the sameconfirm()gate, so they never run blind — andcommitwithout-mis rejected (no editor in this env). Handy for the agent to inspect and record its own work.- Syntax highlighting (Phase 15) — a tiny, dependency-free
syntaxmodule highlights V / Go / JavaScript / HTML / bash code, emitting HTML (for the Web UI) or ANSI (for the CLI). In the Web chat, fenced code blocks (v …) are colourised; all output is HTML-escaped to prevent injection. patch(Phase 1) — precise in-place edit:mode=exact: replaceold_string→new_string(one match required, or setreplace_all=true).mode=lines: replace lines[start,end](1-based, inclusive) withreplacement.- Respects
dry_run(report only) and interactive confirmation.
vet/fmt/test(Phase 1) — runv vet ./v fmt -w ./v -silent test .on a target (default = whole project).
-
Provider-agnostic chat client (OpenAI / OpenRouter / Ollama / llama.cpp compatible).
-
Offline
mockprovider for testing with no API key. -
SQLite-backed session persistence (resumable transcript).
-
Append-only event log under
data/vaiv.log. -
Safety by default (Phase 1):
- Writes/exec gated by flags (
VCA_ALLOW_WRITE/VCA_ALLOW_EXEC). - Dangerous
terminalcommands (rm -rf,git push,dd,kill, …) are blocked unless approved (VCA_AUTO_YES=1) or confirmed interactively. dry_runmode:patch/fmtreport intent without modifying files.- Tool output is truncated (
VCA_RESULT_LIMIT, default 8000 bytes) so a huge file/command can't blow up the context window.
- Writes/exec gated by flags (
-
Memory & context (Phase 2):
- Cross-session memory table in SQLite (
MemoryRow): user prefs, project facts survive restarts and are injected into the system prompt on each run. - Session retrieval:
recall(query)keyword search over stored memory. - Context compression: when the transcript exceeds
VCA_COMPACT_THRESHOLDmessages, the middle turns are summarized (LLM when online, deterministic extractive fallback offline) while the system prompt and lastkeep_recentmessages stay verbatim — key info is never lost. - Configurable system prompt via
VCA_SYSTEM/system:in config.yaml.
- Cross-session memory table in SQLite (
-
Multi-agent orchestration (Phase 3): ✓
- A request is decomposed by a
Plannerinto ordered/parallelSubTasks. With a real LLM it asks the model for a JSON plan; offline (mock) it uses a deterministic heuristic (splits numbered/bulleted lists, else one task). - Each
SubTaskruns in an isolated sub-agent (own ReAct loop + own tool context) viarun_parallel(goroutines + channel, dependency-ordered waves) orrun_sequential. - A
Reducermerges sub-results into one answer; a failed subtask is reported inline and does not abort the run (failure isolation). - Enable with
VCA_PARALLEL=1(orparallel: truein config.yaml).
- A request is decomposed by a
-
Resilience (Phase 6): LLM call retry with exponential backoff (network/5xx/429), configurable per-request timeout, and a per-tool consecutive-failure guard that aborts runaway loops (≥3 in a row).
4xxis not retried (bad request / auth). -
Run telemetry & machine-readable output (Phase 7):
- Every
runpopulatesAgent.last_run(provider, model, steps, tool_calls, per-tool counts, errors, duration_ms, finish) — never written into the transcript, so persistence stays clean. - CLI flags:
--jsonemits{"ok":true,"final":"...","stats":{...}}(or an error envelope) for CI /jq;--statsprints a one-line human summary after the answer. Both are offline-testable under themockprovider.
- Every
-
Persistent telemetry & observability (Phase 8): ✓ (v0.5.3)
- Token usage (P8.1): every run now captures
usage{prompt_tokens, completion_tokens}from OpenAI-compatible endpoints (mock = 0). Real providers (e.g. Shangtang, OpenAI) fill it in; missingusagedegrades to 0. - Persistent store (P8.2/P8.3): each run — single, REPL turn, parallel
orchestration, and web chat — is appended to an independent SQLite file
data/vaiv-telemetry.sqlite(separate fromsession.sqliteandvaiv-web.sqlite, never disturbed by sessionreplace()). - CLI report (P8.4):
vaiv --reportprints a human aggregate (total runs, total tokens, duration, by-provider breakdown, recent runs);vaiv --report json(or--report-json) emits a machine-readable JSON object. Both read only the local telemetry DB, so they work fully offline. - Web dashboard (P8.5):
vaiv-webgainsGET /metrics(dark-themed dashboard with run/token/duration cards + per-provider breakdown) andGET /api/metrics(JSON), aggregating the same persisted store. - Tests (P8.6):
telemetry_store_test.v(save/count/summarise/last_n/reset) andllm_test.v(usage parse present & absent) lock in the behaviour.
- Token usage (P8.1): every run now captures
-
Conventions module — user principles & habits (Phase 9): ✓ (v0.6.0)
- The agent can create and maintain the user's principles & habits as
Markdown files, in two scopes:
- system:
~/.vaiv/conventions.md(applies to every vaiv project) - project:
<project_root>/.vaiv/conventions.md(committed, team-shared)
- system:
- Three built-in tools:
convention_add(layer, category, text),convention_list(layer),convention_remove(layer, id).addis gated byVCA_ALLOW_WRITE;removeis destructive and requires confirmation (VCA_AUTO_YES=1to auto-approve, matching the terminal-tool policy). - Proactive injection: at the start of every
run(), the merged system+project conventions are injected into the system prompt (same mechanism as Phase 2 memory, never persisted into the transcript), so the agent follows the user's principles without being reminded each time. - Learning triggers: passive (user says "记住:…" → agent calls
convention_add) and active (agent notices repeated corrections → proposes saving, but confirms first to avoid mis-recording). - Tests (P9.4):
conventions_test.v(add/list/remove, auto-numbering, cross-layer isolation, run() injection) andtools_test.v(convention_*tool end-to-end incl. remove-confirmation gate). - Learning reminders (Phase 9.5): after a successful turn that actually
used tools, vaiv prints a one-line
[tip]reminding you to save a useful principle / habit / gotcha viaconvention_add— so future sessions take fewer detours. The nudge is non-nagging: it only appears while your convention store is still sparse (< 3 entries total across both layers), and it names the tools that fired this run. It is never shown in--jsonmode. (conventions_learning.v+conventions_learning_test.v.) Consistent on CLI and Web: the Web sync endpoint appends the[tip]line to the reply; the streaming endpoint emits a separateevent: tipSSE event that the front-end renders as a distinct green hint bubble (.msg.tip). Web "save as convention" button: the tip bubble carries a "存为约定" button that opens an inline form (scope / category / text) and POSTs to/api/conventionto persist immediately — manual-confirm, no auto-save, and it shares the same store as theconvention_addtool so it takes effect on the next run.
- The agent can create and maintain the user's principles & habits as
Markdown files, in two scopes:
-
Self-update (Phase 10): ✓ (v0.6.1)
vaiv updateself-updates the binary without involving the LLM. It delegates toscripts/vaiv_update.vsh(form C), which can also run standalone viav run scripts/vaiv_update.vsh.- Flow: locate repo root → check
git status(abort on uncommitted changes unless--force) → back up old binaries to.bak→git pull --ff-only→ recompilebin/vaiv+bin/vaiv-web→ runv test→ rollback to.bakon any failure → print the new version fromv.mod. - Safety: never
git reset --hard, never delete user files; backups are renames (.bak), rollback restores them. The V compiler (~/v) is never touched — only vaiv source is pulled and rebuilt. - Flags:
vaiv update --force(skip the uncommitted-changes guard),vaiv update --dry-run(print the steps without touching anything),vaiv update --self-test(run the script's internal pure-function tests). - Verified: uncommitted-changes guard aborts with a clear message;
--dry-runprints the full pipeline correctly; script self-tests pass (4/4).
-
Session management & config introspection (Phase 11): ✓ (v0.6.1)
- Multiple sessions: each conversation is stored in its own SQLite file
under
data/sessions/<name>.sqlite, so you can keep several threads side-by-side. Usevaiv --session <name> "prompt"(or--session <name>alone for REPL) to talk to a specific one; the default isdefault. vaiv sessionslists all stored sessions with their message counts.vaiv export [name]dumps a session's transcript as Markdown (system prompts are skipped), handy for archiving or pasting elsewhere.vaiv configprints the fully-resolved configuration (file → env → defaults merge) with the API key masked, so you can verify exactly what the agent will run with. All four sub-commands run without the LLM.
- Multiple sessions: each conversation is stored in its own SQLite file
under
-
Self-issue structured logging & auto-recording (Phase 19 tier-0): ✓
- Structured problem recording: vaiv can capture runtime issues into an independent SQLite file
data/vaiv-selfissues.sqlite(separate from telemetry and session stores), forming the observation layer for the self-evolution roadmap. SelfIssuefields:severity(crash/error/warn/info),kind(llm_error/tool_error/…),title,detail,source,resolved.- Auto-recording during agent runs: when a
SelfIssueStoreis attached viawith_selfissues(), the agent automatically records problems without interrupting execution:- LLM streaming error →
severity: 'error', kind: 'llm_error' - LLM chat error →
severity: 'error', kind: 'llm_error' - 3 consecutive tool failures →
severity: 'error', kind: 'tool_error' - max_steps exceeded without finishing →
severity: 'warn', kind: 'max_steps'
- LLM streaming error →
- CLI introspection:
vaiv selfissues list [severity],vaiv selfissues add <sev> <kind> <title> [detail],vaiv selfissues resolve <id>,vaiv selfissues retrospective. - Observation-only: never modifies vaiv's own source (zero risk). Higher tiers (automated fix proposals, gated self-evolution) are future roadmap items.
- Structured problem recording: vaiv can capture runtime issues into an independent SQLite file
-
Model registry & manual switching (Phase 16): ✓ (v0.7.1)
- A dedicated entry point for managing models, so you no longer have to hand-edit
config.yaml. Models live in.vaiv/models.yaml(a separate file, so your hand-writtenconfig.yamlcomments stay intact). vaiv modelslists all configured models with an*marking the active one.vaiv models add <provider> <model> [base_url] [api_key]appends a model and persists it; the first model added becomes active automatically.vaiv models use <n>sets the active model (tried first; falls back across enabled models on failure).vaiv models remove <n>/vaiv models enable <n>/vaiv models disable <n>mutate the registry.- In the REPL:
/modelslists,/use <n>hot-switches for the current session only (does not persist). - The Web UI has a 模型 (Models) page at
/models: add / delete / toggle / one-click switch, all driven by the same.vaiv/models.yaml.
- A dedicated entry point for managing models, so you no longer have to hand-edit
-
Session data integrity:
Session.replace()wraps saves inBEGIN/COMMIT/ROLLBACK; ifsave()orCOMMITfails, the transaction is automatically rolled back, preventing partial-write corruption of the transcript. -
Optional Web UI (Phase 4):
- A veb-based web front-end that reuses the same
agentcore (no duplicate ReAct loop). Built as a separatecmd/webentry. - Session list, "new chat", per-session transcript view, and an
SSE-streamed chat (each assistant delta + tool call is pushed
live over Server-Sent Events; falls back to a sync POST).
SSE exit safety: uses
AbortController+beforeunloadto automatically cancel streaming on page leave, with a browser confirmation dialog when a stream is active. - Bilingual UI:
translations/zh.tr+translations/en.tr(Chinese default), loaded by veb'sveb.tr. - SQLite-backed session store (
webstore.v, filedata/vaiv-web.sqlite) — web sessions survive a server restart. The web DB is deliberately isolated from the CLI'sdata/session.sqlite(different tables, different file). - Bilingual UI with live switching: a
中文 / ENswitch in the top bar sets avaiv_langcookie and reloads;GET /lang?l=zh|en&from=<path>flips the language and redirects back. Templates pull strings fromtranslations/zh.tr+translations/en.trviaveb.tr(lang, key). - Run offline: with no API key it auto-uses the
mockprovider, so the whole UI (create → chat → stream) works withv -oandcurl.
- A veb-based web front-end that reuses the same
Requires V 0.5.x at ~/v (or on PATH).
# CLI
v -o bin/vaiv .
./bin/vaiv "read v.mod and tell me the module name"
# Web UI (Phase 4)
v -o bin/vaiv-web cmd/web
./bin/vaiv-web # open http://localhost:3003/
A self-contained V script installs the two binaries into a custom prefix and uninstalls them safely:
# install into ~/.local/bin (default); builds from the current repo tree
v run scripts/vaiv_install.vsh install
# custom prefix + explicit V compiler:
v run scripts/vaiv_install.vsh install --prefix /opt/vaiv --v ~/v/vnew
# clone a fresh copy from the repo URL instead of using the cwd tree:
v run scripts/vaiv_install.vsh install --repo https://github.com/ovlo/vaiv --prefix ~/apps
# uninstall — safe by default: renames to .bak, never deletes
v run scripts/vaiv_install.vsh uninstall
v run scripts/vaiv_install.vsh uninstall --prefix /opt/vaiv --purge # truly remove
Flags: --prefix DIR (install root, binaries → DIR/bin; default ~/.local),
--repo URL (git URL to clone when not already inside the repo), --v PATH
(V compiler; defaults to the v on PATH), --force (overwrite without a
.bak), --purge (uninstall: actually delete instead of .bak), --dry-run
(print every step, touch nothing). Add DIR/bin to your PATH after install.
A self-contained V script produces a minimal .deb that installs vaiv to
/usr/bin/vaiv. It uses dpkg-deb (present on every Debian/Ubuntu host) — no
fpm/ruby needed. Package name + version are read from v.mod automatically.
# build bin/vaiv (if missing) and produce build/vaiv_<ver>_amd64.deb
v run scripts/build_deb.vsh
# preview every step without writing anything
v run scripts/build_deb.vsh --dry-run
# cross-target / custom maintainer
v run scripts/build_deb.vsh --arch arm64 --maintainer "You <you@x.io>"
# inspect and install
dpkg-deb -I build/vaiv_0.7.2_amd64.deb # show control metadata
dpkg-deb -c build/vaiv_0.7.2_amd64.deb # list files that will be installed
sudo dpkg -i build/vaiv_0.7.2_amd64.deb # install
What the script does: it stages a tiny FHS tree
(<name>_<ver>_<arch>/DEBIAN/control + usr/bin/vaiv), writes a control
file (Package/Version/Architecture/Depends: libc6), then runs
dpkg-deb --build --root-owner-group. The package ships only the binary —
vaiv's runtime data (data/, ~/.vaiv) is created on first run, not bundled.
The binary is dynamically linked against glibc (libc/libm/libmvec), so the lone
Depends: libc6 (>= 2.31) is sufficient on any modern Debian/Ubuntu.
Existing staging trees are renamed to .bak (never deleted), per the project's
"never delete" convention. Output goes to build/ by default (--output DIR
to change).
export VCA_API_KEY=sk-... # OpenAI or OpenRouter key
export VCA_MODEL=openai/gpt-4o-mini # or openrouter/...
export VCA_PROVIDER=openrouter # or openai
./bin/vaiv "refactor main.v to use a config struct"
Ollama:
export VCA_PROVIDER=ollama
./bin/vaiv "explain this module"
llama.cpp server (e.g. ./server -m model.gguf):
export VCA_PROVIDER=llamacpp
./bin/vaiv "summarize tools.v"
Both are configured under .vaiv/config.yaml (ollama: / llamacpp: blocks) and
expose an OpenAI-compatible /v1 API, so the same client code path is used.
VCA_ALLOW_WRITE=0 # disable write_file / patch (default on)
VCA_ALLOW_EXEC=0 # disable terminal / vet / fmt / test (default on)
VCA_AUTO_YES=1 # auto-approve risky actions (skip confirmation)
VCA_DRY_RUN=1 # patch/fmt report intent, do not modify files
VCA_RESULT_LIMIT=4000 # cap tool output bytes (default 8000)
VCA_COMPACT_THRESHOLD=40 # compress transcript beyond N msgs (0 = off)
VCA_PARALLEL=1 # decompose request into subtasks, run concurrently
VCA_SYSTEM="You are vaiv, a V coding agent." # override system prompt
vaiv --json "read v.mod" # machine-readable: {"ok","final","stats"}
vaiv --stats "read v.mod" # human one-line run summary after the answer
# also work in parallel mode: VCA_PARALLEL=1 vaiv --json "1. do A\n2. do B"
vaiv --report # aggregate telemetry: total runs, tokens, by-provider
vaiv --report json # same, as a JSON object (CI / jq friendly)
# Note: --report reads only the local data/vaiv-telemetry.sqlite; it needs
# no API key and works fully offline.
vaiv --session work "refactor main.v" # talk to the 'work' session (creates if new)
vaiv --session work # continue 'work' in REPL mode
vaiv sessions # list all sessions with message counts
vaiv export work # dump 'work' transcript as Markdown to stdout
vaiv export work > work-chat.md # ... redirected to a file
vaiv config # print resolved config (api key masked)
vaiv models # list configured models (* = active)
vaiv models add shangtang m1 https://x/v1 # add a model (persists to .vaiv/models.yaml)
vaiv models use 2 # make model #2 the active one
# Each session lives in its own file: data/sessions/<name>.sqlite
# The default session is 'default' (legacy data/session.sqlite is migrated there).
vaiv update # pull latest, recompile bin/vaiv + bin/vaiv-web, run tests
vaiv update --force # ignore uncommitted changes (risk: may merge over local work)
vaiv update --dry-run # show the steps without pulling / compiling
# The updater aborts if you have uncommitted changes (never overwrites your work).
# On any build/test failure it restores the previous binaries from .bak.
System config lives in the repo at .vaiv/config.yaml (committed; secrets via env).
Resolution order: .vaiv/config.yaml → VCA_* env → built-in defaults.
When no API key is present for a remote provider, it auto-downgrades to mock.
| Var | Default | Purpose |
|---|---|---|
| VCA_PROVIDER | mock | mock / openai / openrouter / ollama / llamacpp |
| VCA_API_KEY | (none) | LLM key; missing -> mock (for remote) |
| VCA_MODEL | mock-model | model id |
| VCA_BASE_URL | provider default | API base |
| VCA_WORKDIR | project root (see below) | tool sandbox root |
All runtime state is anchored to the vaiv project root (the directory
containing v.mod), regardless of the directory you launch the binary from.
This means ./bin/vaiv and cd bin && ./vaiv both write to the same place —
never into bin/. The project root is derived from the executable path
(<root>/bin/vaiv → <root>) and confirmed by the presence of v.mod; if
vaiv is installed outside the repo, it falls back to the current directory.
data/session.sqlite # CLI transcript (persists across turns & restarts)
data/vaiv.log # append-only event log
data/vaiv-web.sqlite # Web UI sessions (Phase 4, isolated from CLI)
~/.vaiv/conventions.md # system-level user principles (Phase 9)
<root>/.vaiv/conventions.md # project-level principles (Phase 9, committable)
bin/ is a build-artifact directory (git-ignored) and intentionally holds no
runtime data.
| Var | Default | Purpose |
|---|---|---|
| VCA_MAX_STEPS | 12 | max agent loop iterations |
| VCA_SYSTEM | built-in | system prompt |
| VCA_ALLOW_WRITE | 1 | enable write_file / patch |
| VCA_ALLOW_EXEC | 1 | enable terminal / vet / fmt / test |
| VCA_AUTO_YES | 0 | auto-approve risky actions (skip y/N) |
| VCA_DRY_RUN | 0 | report intent without applying |
| VCA_RESULT_LIMIT | 8000 | cap tool output bytes |
| VCA_COMPACT_THRESHOLD | 0 | compress transcript beyond N msgs |
| VCA_PARALLEL | 0 | decompose request into subtasks, run concurrently |
| VCA_SYSTEM | built-in | override system prompt |
v.mod
main.v # CLI entry (module main)
.vaiv/config.yaml # system config
agent/ # module agent
types.v config.v llm.v tools.v agent.v session.v logger.v
confirm.v # dangerous-command detection + interactive confirm (Phase 1)
execsafe.v # Phase 21: hardened bash-exec module (policy gate, no shell, audit)
patch.v # precise patch tool (Phase 1)
devtools.v # vet / fmt / test tools (Phase 1)
compress.v # context compression (Phase 2)
session.v # SQLite: transcript + MemoryRow (Phase 2)
orchestrator.v # planner + sub-agent runtime + reducer (Phase 3)
webapi.v # Phase 4: veb handlers (REST + SSE + pages)
webstore.v # Phase 4: SQLite-backed web session store (data/vaiv-web.sqlite)
telemetry.v # Phase 7/8 run metrics
telemetry_store.v # Phase 8 persistent telemetry store
conventions.v # Phase 9: user principles & habits (system+project)
*_test.v # unit tests per module (incl. webapi_test.v + webapi_http_test.v)
cmd/web/ # Phase 4 entry: veb WebApp bootstrap
main.v
scripts/
vaiv_update.vsh # Phase 10: self-update logic (also runnable via `v run`)
vaiv_install.vsh # Phase 17: install/uninstall (safe .bak rename)
build_deb.vsh # Phase 21 follow-up: build a .deb via dpkg-deb
web/
templates/ # veb HTML templates (index, new_session, session, about)
static/ # app.js (SSE client) + style.css
translations/ # zh.tr + en.tr (veb i18n, loaded from cwd)
data/ # runtime: session.sqlite + vaiv.log (git-ignored)
agents.md # phased plan + acceptance criteria
v fmt -w . && v vet . && v -silent test .
| Version | Date | Highlights |
|---|---|---|
| v0.7.2 | 2026-07-20 | Hardened bash-exec module (execsafe) + CLI ! command + .deb build script |
| v0.7.1 | 2026-07-15 | Model management entry (CLI + Web), install/uninstall script |
| v0.7.0 | 2026-07-15 | Syntax highlighting module + v-fullstack-dev skill index |
| v0.6.9 | 2026-07-14 | Web UI learning reminder "save as convention" button |
| v0.6.8 | 2026-07-13 | Git tool for agent self-inspection |
| v0.6.7 | 2026-07-13 | GitHub Actions CI gate |
| v0.6.6 | 2026-07-13 | Web UI learning reminder (SSE event: tip) |
| v0.6.5 | 2026-07-13 | Conventions learning reminder system |
| v0.6.4 | 2026-07-13 | list_files + web_search tools |
| v0.6.2 | 2026-07-13 | Multi-session storage + vaiv config introspection |
| v0.6.1 | 2026-07-13 | Self-update (vaiv update) + conventions module |
| v0.6.0 | 2026-07-13 | Conventions module — agent-maintained user principles |
| v0.5.3 | 2026-07-12 | Persistent telemetry, token usage, web dashboard |
| v0.5.2 | 2026-07-12 | Retry hardening, --json/--stats fixes |
| v0.5.0 | 2026-07-12 | Run telemetry + machine-readable output |
| v0.4.7 | 2026-07-12 | LLM retry + timeout + tool-failure guard |
| v0.4.1 | 2026-07-12 | Optional veb web UI |
- Phase 2: memory + context compaction. ✓
- Phase 3: multi-agent orchestration. ✓
- Phase 4: optional veb web UI. ✓ (v0.4.1)
- Phase 6: LLM retry + timeout + tool-failure guard. ✓ (v0.4.7)
- Phase 7: run telemetry + machine-readable
--json/--stats. ✓ (v0.5.0, hardened v0.5.2) - Phase 8: persistent telemetry (token usage, SQLite store,
--report, web dashboard, tests). ✓ (v0.5.3) - Phase 9: conventions module — agent-maintained user principles & habits (system + project layers, injected into system prompt). ✓ (v0.6.0)
- Phase 10: self-update (
vaiv update) — pull + recompile + test with safe rollback, no LLM involved. ✓ (v0.6.1) - Phase 11: session management (
--session,vaiv sessions,vaiv export) + config introspection (vaiv config). ✓ (v0.6.1) - Phase 21: hardened bash-exec module (execsafe: policy-gated, shell-free execvp, audit log) + CLI
!command + .deb build script. ✓ (v0.7.2)