agentleFS
jpoindexterjpoindexter/Vanta/vanta-ts/CLAUDE.md

Vanta / vanta-ts

CLAUDE.md2 starsChanged 5 months agoMITView on GitHub

The TypeScript agent loop. Read root ../CLAUDE.md for the kernel + project overview. This file is the agent layer's source of truth — don't re-read every file to learn the structure; it's mapped here. Node 22, ESM, "type": "module". Run via tsx (no build step). Native fetch, process.loadEnvFile — no dotenv, no axios. Relative imports use .js extensions (ESM convention; tsx resolves to .ts). TUI uses React 19 + real Ink 7 (ink: "^7.0.6" in package.json — no fork, no…

  • Pipes a download into a shell
  • Reads credentials
  • Installs packages
  • Commits and pushes

The file

We split this CLAUDE.md into the rules it gives an agent

2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Add it to the ALL_TOOLS array in tools/all-tools.ts (not index.ts — that's just buildRegistry now).

    line 228
  2. Very specific:

    Search (Phase 2B): VANTA_SEARCH_PROVIDER (ddg|searxng|serpapi|brave, default ddg) · VANTA_SEARCH_URL (searxng) · SERPAPI_KEY · BRAVE_KEY.

    line 255

≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

CLAUDE.md — vanta-ts (agent layer)

The TypeScript agent loop. Read root ../CLAUDE.md for the kernel + project overview. This file is the agent layer's source of truth — don't re-read every file to learn the structure; it's mapped here.

Runtime

Node 22, ESM, "type": "module". Run via tsx (no build step). Native fetch, process.loadEnvFile — no dotenv, no axios. Relative imports use .js extensions (ESM convention; tsx resolves to .ts). TUI uses React 19 + real Ink 7 (ink: "^7.0.6" in package.json — no fork, no vendor alias). Rendering is inline with <Static> for committed scrollback; no AlternateScreen, no ScrollBox, no mouse-capture/virtual-history layer. Component tests use ui/test-render.tsx (real Ink render over fake stdio, ANSI-stripped frame capture), NOT ink-testing-library. Deps: ink, react.

File map (src/)

FileResponsibility
types.tsCore types: Message, ToolCall, Verdict, Goal, Risk
providers/interface.tsLLMProvider interface, ToolSchema, CompletionResult, StreamChunk (text/thinking/tool_call/done — the universal streaming contract)
providers/openai.tsOpenAI + Ollama/Gemini/OpenRouter (same SDK, baseURL swap). Converts internal↔OpenAI shapes. stream() (token deltas + reasoning_content/reasoning → thinking chunks) + pure foldToolCallDeltas/reasoningDelta
ui/app.tsxThe default TUI App: <Static> committed scrollback + live composer/status rows. Delegates state to ui/reducer.ts, agent I/O to ui/use-agent.ts
ui/focus.tsVANTA-TAB-NAV — pure focus targets/traversal for Tab/Shift+Tab across composer, overlays, and approval actions.
ui/v2/Opt-in mission-control shell selected by VANTA_TUI=v2; wraps the shared v1 engine in durable-state / safety+memory+telemetry rails
ui/reducer.tsState, Action, reduce — pure reducer for the TUI transcript (tested via reducer.test.ts)
ui/use-agent.tsuseAgent hook — sendToAgent fn, queue-drain effect, Esc-abort useInput. Returns {sendToAgent, abortRef}
permissions/request.ts / permissions/grant.tsTyped approval request model + allow/deny rule helpers shared by Ink and desktop
operator-profile/profile.tsOPERATOR-PROFILE — ~/.vanta/operator-profile.json declared vs inferred preferences, drift detection, and tighten-only approval preferences.
preferences/signals.tsPREFERENCE-SIGNALS — append/read/export ~/.vanta/preferences.jsonl chosen-vs-rejected rows for human approval decisions.
fleet/PARALLEL-AGENT-FLEET — worktree-isolated parallel subagent orchestration. vanta fleet run --task ... records team-task states and .vanta/fleets/<id>.json; review prints diff/result state; accept merges one reviewed worker branch. VANTA-SWARM-TMUX: tmux-layout.ts (pure pane-layout plan) + tmux-backend.ts (LIVE: real tmux children, detached session, one pane per worker) → vanta fleet tmux (cli/fleet-tmux-cmd.ts, one vanta run per pane, prints the attach command; proven against real tmux)
batch/batch.tsVANTA-SKILL-BATCH — the PR-workflow layer over fleet. Pure: buildBatchInstruction (appends a test gate so only green workers reach done), prCandidates, buildGhPrArgs, parsePrUrl, createBatchPrs (inject gh/push), formatBatchReport. cli/batch-cmd.ts is the vanta batch run surface (reuses buildFleetAgentDeps from fleet-cmd). Live: gh authed + a remote
workflow/FABRO-WORKFLOW-GRAPH — pure declarative workflow graph layer: zod schema/reference validation, canonical diff, and injected executor for agent/approval/interview nodes plus next/branch/loop/parallel transitions. tools/workflow.ts wires it to kernel assessment, human gates, provider/registry resolution, and spawnSubagent.
goals/GOAL-DEPS — TS-side dependency graph over kernel goals. deps.ts owns .vanta/goal-deps.json, derived blocked state, and wake calculation; the Rust ledger remains the source of truth for goal active/done.
auto-research/AUTO-RESEARCH-LOOP — metric-driven unattended improvement loop. vanta auto-research --objective --metric --bounds creates isolated candidate worktrees, commits candidates, keeps only numeric metric improvements, journals deltas, and stops on no-progress/max-iters.
meta-tune/META-TUNE-INSTRUCTIONS — bounded PROGRAM.md instruction tuning. vanta meta-tune instructions scores variants against eval pass@1/CNG, records the best, and requires explicit approval before adoption. PERSONAL-MODEL-TUNE: lora-dataset.ts (pure: ~/.vanta/preferences.jsonl chosen-vs-rejected → DPO {prompt,chosen,rejected} pairs + readiness gate) → lora-train.ts (orchestrator) → lora_train.py (REAL local peft+transformers LoRA on MPS/CUDA/CPU); auto-tune.ts maybeAutoTune trains unattended each gateway tick when VANTA_LORA_AUTO=1 and enough pairs accrue. CLI: vanta tune lora (cli/tune-cmd.ts)
worktree/manager.tsGit-worktree lifecycle for the fleet: create/list/remove isolated .vanta/worktrees checkouts (one per parallel task), branch naming, cleanup
agent/tool-scope.tsDeferred schema surface: per-turn tool schema subsetting; names/summaries stay prompt-visible while tool_search results expand callable schemas on the next provider call
memory/guardrails.tsFreshness/conflict/provenance guard for recalled brain entries before they influence action
memory/extractor.tsOpt-in VANTA_EXTRACT_MEMORIES=1 post-turn fact extractor — JSON-array only, deduped against brain entries, persists semantic facts with auto-extracted provenance
mem-eval/Memory retrieval evals: fixture corpus plus public LongMemEval/LoCoMo loader/runner/report. vanta eval mem public [dataset-dir] records .vanta/mem-eval-public-results.json; downloaded datasets are runtime-only under ignored .vanta/ paths.
ui/transcript.tsxTranscript row components (assistant/user/tool/note) — inline rendering, no alternate screen
ui/composer.tsxComposer: custom readline (Ctrl+U/W/Esc-abort, up/down history, shift+enter multiline)
ui/launch.tsxrunTuiV2(repoRoot) — prepareRun + maybeCurate + env-selected render. Default is ui/app.tsx; VANTA_TUI=v2 renders ui/v2/app-v2.tsx. vanta uses it on a TTY; readline REPL is the fallback
ui/test-render.tsxTest harness replacing ink-testing-library: renderSync + fake stdio (paused-mode stdin w/ readableLength) + a terminal-grid emulator
(no theme system)The theme system is removed (DECISIONS 2026-06-17) — no ThemeProvider/useTheme/Theme, no term/theme.ts/ui/theme.tsx, no VANTA_THEME//theme. UI components use literal Ink colors (terminal-native white/gray + dimColor) directly
ui/types.tsUI-local types (TuiState, action unions, overlay descriptors)
providers/index.tsresolveProvider(env) — reads VANTA_PROVIDER/VANTA_MODEL. openai/ollama/anthropic/gemini/openrouter/claude-code (gemini+openrouter = OpenAI adapter w/ baseURL swap; claude-code = Anthropic adapter w/ OAuth token)
providers/claude-code-auth.tsv1 G1 — resolveClaudeCodeToken reads a Claude Pro/Max OAuth token (env or ~/.claude/.credentials.json), isTokenExpired. Grey area (ToS); VANTA_PROVIDER=claude-code. No refresh (another agent keeps creds fresh). See DECISIONS 2026-06-02
providers/catalog.tsPROVIDER_CATALOG — small shared {id,label,envVar,defaultModel,signupUrl} list the setup wizard + doctor read. Not the full registry (deferred); extend alongside resolveProvider
setup.tsvanta setup provider/model picker. Pure upsertEnv(existing, updates) (merges into .env, preserves other keys) + buildEnvUpdates; runSetup can receive a live validation hook and only writes after it passes (hidden key prompt, 0600 write)
setup/assistant.tsSetup assistant probes: provider completion, Google OAuth token check/loopback path, MCP mount/list-tools, and configured Telegram Bot API validation. Returns errors-as-values with secret redaction.
setup/capabilities.tsvanta setup capability step — turns on desktop control / voice / auto-tune AND does the machine setup for the operator: runs brew install cliclick, opens the macOS permission panes (platform/macos-prefs.ts), writes the env flags. The OS toggle stays the user's one click
platform/macos-prefs.tsPure URL builder + injectable open runner to jump straight to a specific macOS System Settings privacy pane (Microphone, Screen Recording, Accessibility) so a permission is one click, not a hunt. Used by vanta voice mic / vanta desktop / vanta control
voice/VANTA-VOICE-STT — push-to-talk voice input. ptt-state.ts (PURE state machine + backend selection + transcript post-processing) · mic-capture.ts (ffmpeg avfoundation record; sox/arecord Linux fallback) · whisper-stt.ts (local whisper CLI, proven end-to-end) · ptt-flow.ts (record → transcribe → text, ties the three). Surfaced by the voice_input tool + composer PTT key
comms/slack-channels.tsVANTA-SLACK-CHANNEL-SUGGEST (live layer) — the network/secret boundary: live Slack conversations.list fetch + in-memory channel cache the composer reads while you type #. Ranking/parsing stays pure in repl/slack-suggest.ts; ui/use-slack-channels.ts + ui/channel-palette.tsx are the composer wire
repl/slack-suggest.tsVANTA-SLACK-CHANNEL-SUGGEST — pure #channel autocomplete (ranked prefix-then-substring + cap; sibling to ui/file-index.ts @-mention ranking). Live fetch injected from comms/slack-channels.ts
term/terminal-capture.tsVANTA-TERMINAL-CAPTURE — pure capture-PROCESSING: raw terminal buffer (ANSI/CSI/OSC bytes) → clean control-stripped text. tools/terminal-capture-tool.ts is the LIVE capture (tmux-backed — the node-pty native binding doesn't load on node v24, so tmux scrollback is the real source) exposed as the terminal_capture tool
status.tsvanta status/doctor. Pure formatStatus(report) + gatherStatus(env) (kernel ping only, provider try/catch, key presence, store/goal counts)
safety-client.tsfetch client → kernel. assess/getGoals/proposeApproval/approve/deny/logEvent/status. Zod-validates responses
kernel-launcher.tsensureKernel() — ping, else spawn detached with VANTA_ROOT + cwd, poll 5s
scope.tsresolveInScope(target, root) — path containment, mirrors kernel's inside_scope
tools/types.tsTool (schema + optional describeForSafety + execute), ToolContext, ToolResult
tools/registry.tsToolRegistry port (register/get/list/schemas) + InMemoryToolRegistry default adapter; consumers type against the port, construction sites choose the adapter
tools/roadmap-move.tsKANBAN — roadmap_move tool. Moves a roadmap item to a new status, writes roadmap.json, regenerates HTML. describeForSafety → kernel Allow
roadmap/move.tsmoveRoadmapItem(repoRoot, id, toStatus) — pure fn: read → validate → patch → write → rebuild
roadmap/server.tsKANBAN-S2 — createRoadmapServer(repoRoot) (Node http) + serveRoadmap(repoRoot, port). Routes: GET /roadmap/board (serves roadmap.html), POST /roadmap/move → moveRoadmapItem. vanta roadmap serve wires this
tools/clarify.tsND2 — clarify tool. Agent calls this when intent is ambiguous. Returns a formatted question (+ optional numbered choices) as output; the model surfaces it in its reply and awaits the next turn. End-of-turn design: no ToolContext changes. describeForSafety → "ask user a clarifying question" → kernel Allow
tools/{read-file,write-file,shell-cmd,inspect-state}.tsThe four v0 tools. write-file writes in-repo freely / out-of-repo only into a writable zone; read-file reads in-repo freely / out-of-repo only from a readable zone (both approval-gated by the kernel)
tools/writable-zones.tsSCOPE-1/2 — resolveWritableZones (write_file: ~/Desktop+~/Downloads, VANTA_WRITABLE_DIRS) · resolveReadableZones(env,root) (read_file: project's parent dir + writable zones, VANTA_READABLE_DIRS) · isInZone/expandHome. Kernel still Asks per out-of-root access; the zone lists are the backstop bounding where an approved access lands
tools/{web-search,web-fetch}.tsPhase 2B web tools. web-fetch exports pure extractReadable(html,url); large pages route through tools/extract-pipeline.ts (WEB-EXTRACT-PIPELINE) instead of blind truncation
tools/extract-pipeline.tsWEB-EXTRACT-PIPELINE — pure size-tiered router: classifyExtractTier (as-is/summarize/chunk-synthesize/refuse, thresholds env-configurable), splitIntoChunks, runExtractPipeline (injected Summarizer). routing/extract.ts mirrors routing/vision.ts's auxiliary-model pattern (VANTA_EXTRACT_MODEL/_PROVIDER) for the live summarizer in web-fetch.ts
tools/{write-skill,recall}.tsPhase 2A learning tools. describeForSafety is a constant internal-op string (no path/query → kernel Allow)
tools/{screenshot,browser-navigate,browser-extract}.tsPhase 3 browser tools (lazy playwright-core). Allowlist + requestApproval for new domains. browser-extract exports pure extractFromHtml
tools/describe-image.tsPhase 3 vision — OpenAI vision model on a scoped image (VANTA_VISION_MODEL, default gpt-4o-mini)
tools/run-code.tsPhase 4 — run python/node/rust in an isolated temp dir, timeout, approval-gated
tools/lsp.ts + lsp/ts-service.tsPhase 4 — diagnostics + go-to-definition for .ts/.tsx via the TS compiler API (no separate language server)
tools/git.tsPhase 4 — 6 git tools. status/diff read-only; commit/push/branch/checkout call requestApproval (risk:ask)
browser/allowlist.tsisAllowedDomain/extractDomain — VANTA_ALLOWED_DOMAINS gate for browser tools
providers/anthropic.tsAnthropic adapter (lazy @anthropic-ai/sdk, default claude-sonnet-4-6). complete() + stream() (live text + extended thinking via pure streamAnthropicEvents in anthropic-convert.ts). Pure toAnthropicMessages
tools/delegate.tsPhase 6 — spawns a scoped subagent. Child registry excludes delegate (no runaway recursion)
schedule/cron.tsPhase 6 — isDue (5-field cron) + .vanta/cron.tsv load/add/save
schedule/runner.tsPhase 6 — runDueTasks({dataDir, now, run}) runs due active tasks, passes compact cron wake context to RunTask; one failure doesn't abort the batch
schedule/commands.tsPhase 6 — vanta schedule/cron CLI handlers (extracted to keep cli.ts ≤300)
gateway/run.tsv1 E1/E2/E3 — vanta gateway daemon: drains queued loop wakes first, then gatewayTick cron/factory + pollPlatform messaging + webhook listener in one runGateway loop (SIGINT/SIGTERM-clean). VANTA_GATEWAY_TICK_MS
gateway/webhook.tsv1 E3 — pure verifyGithubSignature (HMAC sha256, constant-time) + resolveDeliver (local/file/telegram) + startWebhookServer (POST, HMAC-gated, 200-fast). VANTA_WEBHOOK_PORT/_SECRET/_PROMPT/_DELIVER
loop/wake.tsWAKE-CONTEXT — WakeContext formatting/encoding, VANTA_LOOP_WAKE_CONTEXT, cron/webhook/loop wake builders, and durable .vanta/loops/wake-events.jsonl queue.
service/launchd.ts + service/manager.tsv1 E1 — pure buildLaunchdPlist + vanta service install|uninstall|status (launchd user agent that keeps vanta gateway alive; captures PATH). macOS only for now
gateway/platforms/base.tsv1 E2 — PlatformAdapter contract (connect/disconnect/send/poll) + InboundMessage/OutboundMessage. One adapter per messaging platform
gateway/platforms/telegram.tsv1 E2 — TelegramAdapter (getUpdates long-poll + sendMessage, no SDK) + pure parseUpdates/parseAllowlist. gateway/run.ts pollPlatform runs inbound→agent→reply. Enabled by VANTA_TELEGRAM_TOKEN; offline-tested, live needs a @BotFather token
mcp/client.tsv1 E5 — dependency-free MCP stdio JSON-RPC client (McpClient + injectable Transport + stdioTransport): initialize/listTools/callTool, concurrent-request correlation. textFromContent pure
mcp/mount.tsv1 E5 + MCP-1 — readMcpConfig (accepts servers+mcpServers keys; resolves VANTA_MCP_SERVERS inline, else merges ./.mcp.json project-level over ~/.vanta/mcp.json user-level) + mountMcpServers (spawn each, register discovered tools as Vanta tools via mcpToolToVantaTool — gated by kernel assess()). Called in prepareRun; no-op without config
mcp/server.tsMCP-3 — Vanta AS an MCP server (mirror of client.ts). runMcpServer/stdioServerTransport + pure handleMessage (initialize/tools/list/tools/call). Every call gated by assess(): block/ask → isError result (headless, no human), only allow executes. resolveServeAllowlist (VANTA_MCP_SERVE_TOOLS, default 9 read-only) bounds exposure. vanta mcp serve
mcp/chicago-*.tsVANTA-CHICAGO-MCP — route Vanta's computer-use actions (screenshot/click/type/key/scroll) through a mounted "CHICAGO" computer-use MCP server instead of a local desktop driver. chicago-route.ts (pure buildChicagoCall) · chicago-connect.ts (REAL mount/connect seam, raw-capable client) · chicago-client.ts (LIVE binding). vanta control (cli/control-cmd.ts) makes native desktop control work out of the box (grants OS permissions + checks cliclick)
tools/mount-mcp.tsMCP-2 — buildMountMcpTool(registry) factory: the mount_mcp tool spawns an MCP server at runtime + registers its tools into the LIVE registry. describeForSafety → "spawn mcp server …" so the kernel gates the spawn. Registered by buildRegistry (needs the registry ref)
plugins/{manifest,context,commands,loader}.tsPLUGIN-FRAMEWORK — opt-in in-process plugins. Strict plugin.json, plugins.enabled allow-list, user plugins under ~/.vanta/plugins, project plugins only with settings trust + VANTA_ENABLE_PROJECT_PLUGINS=true. PluginContext v1 exposes registerTool and registerCommand; plugin tools register as normal tools and remain kernel-gated.
hooks/VANTA-HOOK-TYPES + VANTA-HOOK-AGENT-TYPE + VANTA-HOOK-EVENTS + PAPER-HOOK-EVENTS-27 (shipped) — .vanta/hooks.json 30-event schema + execution. Types: command/shell, http, mcp_tool, prompt, agent; shared controls: timeoutMs, once, statusMessage. Agent hooks are dependency-injected for live tool/prompt/stop/session events to avoid agent-loop import cycles. paper-events.ts tracks arXiv 27-event parity.
subagent/spawn.tsPhase 6 — spawnSubagent runs an isolated worker (own goal/prompt/iter budget), returns verified outcome only
a2a/{types,local}.tsPhase 6 — local in-process A2A message bus (A2ABus, makeMessage). Networked transport = future
projects/rooms.tsPhase 7 — listRooms/resolveRoom over VANTA_PROJECTS_DIR (default ~/Documents/GitHub/_active). vanta room <name> runs rooted there → per-project goal stream
projects/commands.tsPhase 7 — vanta rooms/room/modes CLI handlers + learning suggestion (keeps cli.ts ≤300)
modes/builtin.tsPhase 7 — 8 operator modes as real skills (OPERATOR_MODES, installModes), including solutioning-mode. Run via vanta skill <mode> "<instr>"
modes/learning.tsPhase 7 — recordRun/shouldProposeSkill; after a pattern recurs 3× proposes capturing it as a skill (~/.vanta/usage.tsv)
routing/model-router.tsPhase 7 — classifyTask (cheap/expensive) + resolveRoutedProvider (VANTA_MODEL_CHEAP/_EXPENSIVE; no-op when unset)
routing/vision.tsAUX-VISION — visionEnv (pure) + resolveVisionProvider: image tools resolve a dedicated vision model via VANTA_VISION_MODEL (+ optional VANTA_VISION_PROVIDER), else the active provider. Used by describe_image/look_at_screen/look_at_camera so a text-only main model doesn't break sight. The auxiliary-task pattern, scoped to vision
google/auth.tsPhase 5 — one-time OAuth (runGoogleAuth, loopback) + token store ~/.vanta/google-tokens.json (0600), getAccessToken (auto-refresh)
google/client.tsPhase 5 — googleFetch (Bearer + 401-retry) + pure buildUrl
tools/gmail.tsPhase 5 — gmail_search/gmail_read (read) + gmail_draft/gmail_send (always approval-gated)
tools/calendar.tsPhase 5 — calendar_read + calendar_create/calendar_update (approval-gated)
tools/drive.tsPhase 5 — drive_read + drive_create/drive_update (approval-gated). Pure buildMultipartBody
tools/all-tools.tsThe ALL_TOOLS array (125 built-in tool imports, incl. voice_input + terminal_capture) — the registration site for new tools, extracted from index.ts for the size gate
tools/index.tsbuildRegistry({exclude?}) — registers the 119 ALL_TOOLS (122 with factory-built mount_mcp/tool_search/mcp_auth; workers exclude recursive tools such as delegate as needed); roadmap_add files new cards, roadmap_move changes status
subagent/spawn.tsIsolated worker conversations for delegate/swarm/workflow agent nodes; returns summary AgentOutcome, persists full transcript sidechains under .vanta/sidechains/
store/home.tsresolveVantaHome/skillsDir/memoriesDir/slugifySkillName/ensureVantaStore/commitInHome. The global ~/.vanta store (VANTA_HOME override), git-init'd for free versioning
skills/types.tsSkill, SkillMeta, SkillMatch
skills/frontmatter.tspure parseSkill/serializeSkill (flat YAML frontmatter)
skills/store.tswriteSkill/readSkill/listSkills — ~/.vanta/skills/<slug>/SKILL.md, auto-commits. LEARNED_TAG provenance constant
skills/library.tsinstallSkillLibrary({force,from}) — copies bundled skills-library/ into ~/.vanta/skills (idempotent, skips existing unless --force). libraryDir() resolves to vanta-ts/skills-library/. Called by vanta skills install and auto-run in prepareRun every session, so newly-bundled skills appear without a manual install
skills-library/48 shipped skills at vanta-ts/skills-library/<slug>/SKILL.md: ~10 ported from references + agent-orchestration-workflows, build-mcp-server, vanta-port-adapter, batch (the vanta batch PR-over-fleet workflow), the 14 nd-* executive-function skills, the 9 Boris-Cherny build-catalog skills (standing-loops, keep-green, prod-watch, cluster-feedback, hill-climb, agent-fanout, parallel-verify-workflows, ship-preflight, agentic-build-strategy), the 2 build/verify disciplines (source-grounded — cite official docs over training data; doubt-gate — in-flight adversarial cross-examination), spaced-repetition (beat the forgetting curve: expanding intervals + active recall vs cramming — Ebbinghaus/Leitner), and build-my-profile (gather authorized signal → self-score vs draft/review/buy goals → save to brain user_model, no secrets). Add a <slug>/SKILL.md dir to grow the bundle; it auto-installs on next session
skills/recall.tspure searchSkills(query, skills) — weighted substring ranking
skills/curator.tscurate() — non-destructive: archives only stale vanta-learned skills (reversible→_archive), reports stale hand-authored + long-archived (never deletes), reports overlaps. Provenance via LEARNED_TAG
review/background-review.tsTrack B self-improvement. shouldReview(toolIters, turnIdx, env) (busy/periodic trigger) + reviewTurn() — spawns a tool-restricted agent (recall+write_skill), replays the transcript, captures a skill tagged vanta-learned. Best-effort. Env: VANTA_SELF_IMPROVE/VANTA_REVIEW_MIN_TOOLS/VANTA_REVIEW_EVERY
memory/store.tsappendMemory/readMemory/recentMemory — per-goal summaries ~/.vanta/memories/<goalId>.md
brain/brain.tsTHE brain — one cohesive unit; everything outside brain/ imports from here. Composes the md regions + structured entries behind one surface: remember/recall (recall reinforces)/brainDigest (one composed prompt digest)/sweep/brainHealth. Per-layer best-effort: a broken layer degrades, never breaks
brain/entries.tsStructured memory layer (absorbed brain5d store + neuro 12-axis scoring): typed entries, strength × recency × contradiction-penalized confidence, salience/retrieval bonuses, crystallization (raw→compressed→crystallized at 3/10 retrievals), forgetAfter decay. Tolerant reader (bad entries dropped, corrupt store quarantined — copied, never deleted), one-time brain5d.json migration. ~/.vanta/brain/entries.json, git-versioned
brain/regions.ts + brain/store.tsThe md-region layer: 9 seeded regions (~/.vanta/brain/<region>.md, archived + git-versioned) + the region read/write/digest internals the facade composes
brain/assoc.tsAssociation: memories auto-link to similar neighbors at write time (zero-dep token-overlap similarity, capped sparse graph) + spreading-activation recall — direct hits surface linked neighbors at damped strength (↪, primed not reinforced). Relevance floor keeps one-token noise out of direct hits
brain/consolidate.tsThe sleep pass: near-duplicate memories gist-merge (strength/links/retrievals carried, marked crystallized), decayed swept, hard entry budget (VANTA_BRAIN_MAX_ENTRIES, default 400) drops weakest-first, links healed. Auto-runs from the digest when over budget
brain/learn.tsAuto-learning: post-turn background pass distils 0–3 durable memories from the transcript (user patterns → user_model, facts → semantic, events → episodic, Vanta's own forming personality → identity/reflections; ND-first guidance patterns prioritized). VANTA_BRAIN_LEARN[_EVERY|_MIN_TOOLS]. Hosts surface a 🧠 learned: line
session/background-learning.tsPost-turn LLM forks (skill review, session scratchpad, brain learning, completion verifier, critic; memory extraction when enabled) — gated, best-effort, re-exported via session/after-turn
verify/completion-verifier.tsVANTA-VERIFICATION-AGENT — VANTA_VERIFY=1 trigger detector + timeout-bound verifier call. Completion claims (done/complete/finished/shipped or TaskUpdate completed) get original task/goals + recent tool output evidence; pass logs only, fail appends a ⚠ Verifier: system message.
sessions/store.tsSession persist/resume/fork: saveSession/loadSession/forkSession/listSessions/newSessionId. JSON files ~/.vanta/sessions/<id>.json (id YYYYMMDD-HHMMSS), zod-validated. vanta sessions/resume <id>/--resume; --fork-session creates a new seeded session. createConversation(...,{history}) seeds resumed turns
ui/keybindings.tsKEYBINDING-CUSTOMIZATION — config-driven keybindings: DEFAULT_BINDINGS + ~/.vanta/keybindings.json (zod, hot-reload via loadKeybindings), normalizeChord/displayChord, resolveBindings (overrides layer over defaults), lookupChord/actionForChord/eventToChord. app-keys handleGlobalKey dispatches by resolved chord→action (live on the TUI); useKeybindings() loads it
agent/decision-log.tsDECISION-CLASSIFIER wiring — autoApproveOverridden refuses a blanket auto-approve grant for a user-challenge (overrides operator direction) so it reaches the operator; recordAutoDecision logs auto-decided taste calls for the final gate; gated in dispatch-helpers auto-approve paths, surfaced in /where. Grantless runs byte-identical
acp/edit-policy.tsEXT-ACP-EDIT-DIFF — pre-exec file-edit approval: isSensitivePath (.env/.git/.ssh/keys always re-prompt), decideEditPrompt (session allow_always grant auto-approves non-sensitive edits), buildLineDiff. Tool.describeDiff computes the preview before approval (write/edit-file); ACP session attaches it + arms the per-session grant
permissions/{reply-bus,channel-approver}.tsCHANNEL-PERMISSIONS-WIRE — live chat approvals: VANTA_APPROVER_CHATS + a live channel relay ask-tier approvals as "yes/no <id>" prompts raced vs deny-at-timeout (VANTA_CHANNEL_APPROVAL_TIMEOUT_SEC). Reply bus consumes approval replies in pollPlatformSession (never an agent turn); the relay pumps platform.poll() while the loop is blocked (deadlock breaker) and parks bypassed messages for the main loop. buildCronRunTask(root,{requestApproval})
activity/feed.ts + repl/activity-cmd.tsPCLIP-ACTIVITY-FEED — /activity [--who] [--kind gate|tool|note] [--risk] [--since 30m|2h|3d] [--limit] [text]: who/what/why timeline parsed from events.jsonl (gate events carry kernel verdict→resolution); read-only, no new store
queues/work-queue.ts + cli/queue-cmd.tsPCLIP-WORK-QUEUES — vanta queue define <name> --worker <id> [--template …{input}…] | push | work [--once] | list: repeated inputs each route through the real team path (ledger task → teammate subagent via team-run doRun); items reuse the runner's atomic-claim primitives (QueueLoc.subdir)
mcp/catalog.tsEXT-MCP-CATALOG — vetted MCP servers + vanta mcp catalog/install <name> [--with-tool]; each entry declares transport/authEnv + a read-mostly defaultTools subset (mutating tools opt-in). Adds a per-server tools allowlist to the MCP ServerSchema, honored by the mount loop (filterAllowedTools) so only allowlisted tools mount
gateway/platforms/capabilities.tsMSG-CAPABILITY-DESCRIPTOR — AdapterCapabilities (charLimit/lenUnit/supportsEdit/supportsThreads/markdownDialect) declared on PlatformAdapter (optional; conservative defaults); segmentsFor is the send-path split seam, deliveryMode gives the no-edit one-message-per-segment degrade. telegram/webchat/sms declare + read off the instance
runner/{queue,loop}.tsVANTA-SELF-HOSTED — vanta runner add|list|start [--once] [--interval s]: CI/CD job queue (.vanta/runner-jobs/{queued,running,done}/, atomic rename claim, CI enqueues by dropping JSON) + polling loop with injected executor (cli/runner-cmd.ts: job.agent → external agent session via runExternalAgent, else kernel-gated Vanta one-shot)
team/review-stage.tsPCLIP-APPROVAL-STAGES — named review stages on WorkerTask (reviewStages?): requireStage/decideStage/checkReviewGate (done REFUSED while any stage isn't approved; rejected blocks too)/reviewQueue (pending per reviewer). Gated in advanceTask after the outcome gate; team tool actions require_review/review/reviews. Stage-free tasks unchanged
schedule/script-run.tsHARNESS-CRON-SCRIPT-MODE — runCronScript (sh -c, hard timeout VANTA_CRON_SCRIPT_TIMEOUT_SEC default 60s, 20k cap, errors-as-values). CronEntry mode: no_agent|script_context + script (TSV cols 5/6, legacy 4-col parses): no_agent delivers stdout with NO model call, script_context injects stdout into the agent turn; executor injected via RunDueTasksOptions.runScript, wired in vanta cron + the gateway tick. vanta schedule … --mode … --script …
proactive/{policy,store,tick}.tsKAIROS heartbeat — pure throttle policy (VANTA_PROACTIVE_*), .vanta/proactive.json, tick orchestrator advancing queued loop wakes (see §PAPER-PROACTIVE-TICK)
proactive/{outreach,outreach-store,outreach-send}.tsPROACTIVE-CHANNEL-OUTREACH — Vanta messages you FIRST: pure opt-in policy (VANTA_OUTREACH/_TO=platform:chatId/_INTERVAL_MIN/_MAX_PER_DAY/_BUDGET_SCOPE, silence switch), .vanta/outreach.json, send orchestrator over createAdapter (injected for tests; failed send doesn't consume the throttle). Wired: vanta proactive run pings after advancing wakes; vanta proactive silence <minutes|off>
search/entities.tsBRAIN-ENTITY-SIGNAL — deterministic entity extraction (proper-noun spans/emails/@handles), entity→records link index, mem0's rarity down-weight 1/(1+0.001·(n-1)²), entityScores/entityRank. Blended as a score COMPONENT into lexical relevance (equal-rank RRF fusion measurably hurt: LoCoMo 32.4→14.8; blend 0.25 → 34.9%, LongMemEval unregressed). Stored on BrainEntry.entities at write (legacy backfill on load); recall boost in brain/assoc.ts directHits
search/interface.tsSearchProvider interface, SearchResult, SearchConfig, DEFAULT_MAX_RESULTS
search/{duckduckgo,searxng,serpapi,brave,exa,firecrawl,tavily,parallel,xai}.tsSearch adapters. Each exports a *Provider class + a pure build*Body/map*Response for testing. exa/firecrawl/tavily/xai set filtersDomains=true (native domain filtering, xai capped at 5 vs the shared 10); parallel uses the site: rewrite. xai is a fundamentally different shape — a reasoning model performs the search and returns ONE grounded answer; citation annotations (verified real shape: output[].content[].annotations, NOT a naive top-level output_text/citations — see the code comment citing openclaw/openclaw#13171) map to SearchResult[]. Managed keyed backends auto-detect in priority firecrawl>parallel>tavily>exa>xai (index.ts KEYED table)
search/scope.tsWEB-DOMAIN-SCOPING — pure validateDomainScope/hasDomainScope/scopeQuery (site:/-site: rewrite for providers without native domain filtering)
search/index.tsresolveSearchProvider(env) — reads VANTA_SEARCH_PROVIDER. Mirrors providers/index.ts
prompt.tsbuildSystemPrompt() — tiers: stable (SOUL+tools+rules) / brain / skills / context / errorsLogTier (ERRORS.md, capped 3k) / volatile (Ralph paused continuity + goals+time+memory)
ralph/state.tsHARNESS-RALPH-LOOP — .vanta/ralph-loop.json store: goal, ordered features, status, summaries, files, next action, paused continuity formatter.
context.tstrimMessages() (fallback) + compressMessages(msgs, ctx, summarize, {activeGoalText?}) — injects goal-reminder note after system messages when activeGoalText is set
agent.tscreateConversation() + runAgent() + dispatchTool(). AgentDeps: activeGoalText? (goal re-injection), onIterationCheck? (consecutive failure hook). dispatchTool runs EF-SELFMONITOR heuristic before tool.execute. runTurn tracks consecutiveErrorResults → onText note at threshold
session.tsShared run setup for one-shot + interactive: prepareRun (reads ERRORS.md → errorsLog, Ralph paused continuity), buildSummarizer, writeRunMemory, consoleCallbacks, approver, maybeCurate, reviewAfterTurn, researchGateAfterTurn, inhibitAfterTurn, setShiftAfterTurn. Neither cli.ts nor interactive.ts imports the other
interactive.tsrenderBanner (logo, model, goals, tool + skill inventory) + runChat (the REPL: one createConversation, history persists; slash commands via repl-commands.ts; session save + post-turn review; startup lifecycle hooks; resume fork target)
repl-commands.tsREPL slash commands dispatcher — re-exports SLASH_COMMANDS, executeSlash, runSlashCommand. Handlers live in repl/handlers.ts
repl/catalog.tsSLASH_COMMANDS array + SLASH_HELP — canonical command list driving /help, TUI palette, and validation
repl/handlers.tsHANDLERS registry + dispatch(cmd, arg, ctx) — every slash handler, each a SlashHandler const
repl/init-cmd.tsVANTA-INIT-CMD — `/init [--force--print]writes.claude/CLAUDE.md` with detected stack, visible structure, existing context docs, and working rules; refuses overwrite by default
repl/next.tsND1 — next handler: reads active kernel goals → resend prompt asking agent for one concrete next micro-step
repl/plan-mode.tsND3 — planMode handler: toggles plan-first mode via PLAN_MARKER injection into live system prompt (`/planmode [onoff]`)
repl/where.tsEF-WORKINGMEM — /where handler: lastIntent (last user message) + lastToolCalls (last 5 tool names) as a breadcrumb
repl/inhibit.tsEF-INHIBIT — InhibitState + nextInhibitState/shouldAlertInhibit/buildInhibitText. Post-turn drift counter (3 consecutive non-output turns → alert). VANTA_INHIBIT_THRESHOLD override
repl/set-shift.tsEF-SETSHIFT — SetShiftState + getPrimaryTool/nextSetShiftState/shouldAlertSetShift. Post-turn stuck-loop detector (same primary tool 3 turns → alert). VANTA_SETSHIFT_THRESHOLD override
repl/self-monitor.tsEF-SELFMONITOR — isDestructiveAction/isAdditiveGoal/shouldWarn/buildSelfMonitorText. Synchronous pre-execution heuristic in dispatchTool; fires via deps.onText (zero LLM, never blocks)
repl/error-detect.tsEF-ERRORDETECT — isErrorResult/buildErrorDetectText. Consecutive ok:false or error-keyword results tracked in runTurn; at 3 fires onText note + deps.onIterationCheck? callback
repl/closure-gate.tsEF-CLOSUREGATE — extractWrittenFiles/hasCommitAfterIndex/getInProgressItems/buildClosureGateText. On topic shift (same isTopicShift check), surfaces unclosed write_file calls without a subsequent commit
repl/model-cmd.ts/model handler (extracted from handlers.ts). Bare → prints active model (TUI opens the picker); /model <arg> → parseModelArg (model-switch.ts) → hot-swap convo + setup.provider + persist to .env. Returns SlashResult.provider so the TUI banner refreshes
repl/moim-cmd.ts/moim handler (extracted) — pin/show/clear a top-of-mind note (writes via moim/store, patches the live system prompt)
repl/restart-cmd.tsRESTART — /restart exits with RESTART_EXIT_CODE (75); run.sh's relaunch loop re-execs tsx. Refuses unless VANTA_RELAUNCH is set (run.sh sets it). Hosts: interactive.ts + ui/app.tsx + ui/launch.tsx force the 75 exit
repl/bug-cmd.tsBUG-CAPTURE — /bug <what happened> → pure formatBugRecord (desc + model + last intent + recentToolNames + git state) written to .vanta/bugs/*.md
repl/handoff-cmd.tsHANDOFF-PACKET — /handoff → pure formatHandoffPacket + shared assembleHandoff (goals + git + recent tools + last intent/result + NEXT slot). assembleHandoff is reused by AUTO-HANDOFF
repl/auto-handoff.tsAUTO-HANDOFF — shouldAutoHandoff/maybeAutoHandoff write .vanta/handoff.md when context fill ≥ VANTA_AUTOHANDOFF_THRESHOLD (0.75); prepareRun reloads + consumes it on interactive launches. Both hosts call maybeAutoHandoff post-turn
repl/next.tsND1 /next + GOAL-ACTION: isVagueGoal + shared buildNextStepResend (active goals + choice-reduced backlog → single-micro-step prompt); the /goal handler auto-fires it on a vague goal
repl/stall.tsSTALL-UNBLOCK — post-turn gate (nextStallState/shouldAlertStall/buildStallText). With an active goal + no write/commit for VANTA_STALL_THRESHOLD (4) turns, names the top backlog card as the unblocker. Wired via stallAfterTurn
repl/mode-detect.tsMODE-DETECT — pure detectMode (silent-executor/collaborator/critic/researcher/debugger/assistant) + buildModeHint; both hosts prepend the hint to the SENT message. VANTA_MODE_DETECT=0 disables
tool-retry.tsTOOL-RETRY — shouldRetryTool (idempotent-read allowlist ∧ transient-failure regex) + resolveToolRetries (VANTA_TOOL_RETRIES, default 1). agent.ts dispatchTool retries safe reads on a transient failure. Also exports isTransientError(err) + resolveProviderRetries (VANTA_PROVIDER_RETRIES, default 2), reused by agent/provider-call.ts for the PROVIDER-HARDENING model-call retry
pricing.tsCOST-VISIBLE — estimateCostUsd (approx $/1M table) + isLocalProvider + formatTurnCost + addTurnCost/formatSessionCost (local-free vs frontier split on ReplState.sessionCost). Both hosts print the per-turn footer; /usage shows the split
cost/ledger.ts + cost/attribution.tsPCLIP-COST-ATTRIBUTION — the persisted, cross-session complement to pricing.ts's in-memory session view. ledger.ts: zod-validated .vanta/spend-ledger.jsonl store (appendSpend/listSpend) + recordTurnSpend (the one-line call-site helper, no-ops on null/zero cost). attribution.ts (pure): summarizeSpend (rolls up by goal/agent/provider/model) + filterSpendSince + formatSpendBreakdown. Wired at both cost-computing call sites — interactive-post-turn.ts recordTurnCost (agent:"interactive") and cli/ops.ts buildCronRunTask (agent:"gateway") — each attributing to the active kernel goal id when one exists. Surfaced via /usage breakdown [--since <ISO>] (repl/context-cmds.ts; plain /usage stays the unchanged session-scoped view).
roadmap/add.tsROADMAP-ADD — addRoadmapItem (validate, refuse dup id, append, bump updated, rebuild HTML) — companion to roadmap/move.ts
tools/roadmap-add.tsroadmap_add tool (required id+title; defaults status=next/track=Backlog/size=M) → addRoadmapItem
repl/goal-cmd.ts/goal handler (extracted) — show/set/clear/done; setNewGoal persists + patches the live prompt + fires GOAL-ACTION on a vague goal
repl/open-cmd.tsVANTA-EDITOR — /open <file[:line]> → openInEditor
editor/open.tsVANTA-EDITOR — pure parseFileLine + resolveEditor (VANTA_EDITOR>VISUAL>EDITOR, default code) + editorCommand (code -g / +line / file:line) + openInEditor (detached spawn). vanta open CLI
lint/size.tsCODE-SIZE-GATE — pure analyzeSource (TS compiler API): file≤300, fn≤50, params≤4, cyclomatic≤10 → Violation[] (file:line+limit+fix). LIMITS, formatViolation
lint/run.tsvanta lint [files|--staged] — resolveTargets (explicit=cwd-relative, git=root-relative), lintFiles (reports analyzed vs missing), runLint (exit 1 on violations/missing)
cli.tsThin entry point: bootstrap (findRepoRoot/loadEnv/ensureVantaStore) + startInteractive (TTY-gated first-run wizard) + a COMMANDS lookup table (a returned number = exit code). chat/--resume/resume/run stay explicit for global flags (--init*, --fork-session). Add a command = one table entry.
cli/agents-cmd.tsvanta agents management surface over team/tasks.ts: list/logs/attach/stop/rm/respawn, top-level aliases (attach, logs, respawn, stop, rm), plus vanta daemon status/stop. Gated by settings disableAgentView or VANTA_DISABLE_AGENT_VIEW=1.
cli/auto-research-cmd.tsvanta auto-research --objective --metric --bounds parser and report printer; delegates worker orchestration to auto-research/loop.ts.
cli/meta-tune-cmd.tsvanta meta-tune instructions [--iters N] [--adopt] parser; runs evals with VANTA_PROGRAM_OVERRIDE for candidate scoring and approval-gates writes to PROGRAM.md.
cli/auto-mode-cmd.ts + cli/permission-mode.tsvanta auto-mode defaults/config plus `--permission-mode autodefault parsing (VANTA_AUTO_MODE`).
cli/commands.tsThe vanta <cmd> handlers extracted from cli.ts (CODE-SIZE-GATE): usage/usageExit, runInstruction (shared run/skill/room path), runSessionsList/runSkillsCommand/runMemoryCommand/runVoiceCommand/runHooksCommand/runSkillCommand/runRoomCommand
cli/goals-cmd.tsvanta goals — live kernel goals plus goals/deps.ts graph state rendered through the shared goal ledger formatter.
cli/lifecycle.tsparseLifecycleFlags + runLifecycleHooks: --init runs Setup hooks, --init-only runs Setup + SessionStart and exits, --maintenance sets maintenance context and exits
cli/output-callbacks.tsOutput callback builder for vanta run (text/json/stream-json) split out to keep commands.ts under size limits
compress/types.tsCOMPRESS-NATIVE — ContentType (json/log/binary/text), CompressOptions, CompressResult, DEFAULTS, estTokens
compress/router.tsdetectContentType(text) — pure classifier (binary/log/json/text). compressContent(text,opts) — routes to the right compressor, returns no-op if ratio ≥ threshold
compress/json-crush.tscrushJson(text,opts) — strips whitespace, number precision, long arrays → [n items] stub
compress/log-squash.tssquashLogs(text,opts) — deduplicates repeated log lines, collapses runs
compress/store.tsCCR (Context-Compression Retrieval) stash: ccrId(text) (hash) + storeCcr/retrieveCcr — .vanta/ccr/ files so the agent can ask for the full text after compression
compress/apply.tscompressEnabled(env) + COMPRESS_TOOLS allowlist + shouldCompressTool(name) + applyCompression(toolName,output,opts) — wired into agent.ts dispatchTool; only compresses tools on the allowlist
cli/ops.tsLarger op handlers: dataDirFor/buildCronRunTask/runGatewayCommand/runServiceCommand/runMcpCommand/runRoadmapCommand/runFactoryCommand/runDesktopCommand
desktop-app/Vite + React desktop renderer (AppShell, session sidebar, chat thread, composer, right rail). Build with npm run desktop:build; served from desktop-app/dist/
desktop/assets.tsSafe built-asset resolver for desktop root + /assets/*; falls back to desktop/page.ts when dist is missing
cli-dx/CLI-DX-PACK: prompt-size.ts (token breakdown), completion.ts (shell completion + CLI_COMMANDS), backup.ts (tar ~/.vanta)
permissions/auto-mode.tsAuto permission classifier: default read-only allows, soft-deny presets, and settings.autoMode.rules overrides. Applied after kernel + permission rules; kernel block remains immovable. Operator profile preferences run after this and can only preserve/escalate.
governance/audit.tsPAPER-GOVERNANCE-AUDIT — parses gate events (kind-discriminated JSON emitted by agent/dispatch-helpers.ts auditGate at every applySafetyGate exit) out of events.jsonl, summarizes by kernel risk + final resolution, and renders an external-review markdown report. cli/governance-cmd.ts is vanta governance export [--since <ISO>] [--out <path>] — distinct from vanta audit (dependency-vulnerability scan).

The loop (agent.ts)

messages = [system, user]
each iteration (max VANTA_MAX_ITER=50):
  trim → provider.complete(messages, schemas)
  no tool calls + non-empty text → DONE
  no tool calls + empty → nudge once
  for each tool call → dispatchTool:
    describeForSafety(args) → safety.assess()
      block → tool_result "blocked", no exec
      ask   → permission rules + auto-mode may tighten/auto-confirm; otherwise requestApproval(y/n); propose+approve|deny in kernel
      allow → execute
    append tool_result; logEvent
  3 consecutive empty results → stop

Safety is two-layer: assess() gates on the kernel (keyword/scope). Tools also self-check (path scope, overwrite approval via ctx.requestApproval). describeForSafety sends only the risk-relevant part to assess (path/command, not file content — else content keywords false-trigger).

How to add a tool

  1. New file tools/<name>.ts exporting a Tool: schema (name, description, JSON-schema parameters), describeForSafety (return the safety-relevant string), execute(args, ctx).
  2. Parse args with zod (safeParse) — it's an LLM boundary.
  3. Path args → resolveInScope; return {ok:false} if outside.
  4. Return ToolResult (errors-as-values, never throw across the boundary).
  5. Add it to the ALL_TOOLS array in tools/all-tools.ts (not index.ts — that's just buildRegistry now). Add a test in tools/tools.test.ts.

How to add a provider

Implement LLMProvider (complete/modelId/contextWindow), add a branch in providers/index.ts. Keep the agent loop provider-agnostic — it only sees the interface.

How to add a search provider (Phase 2B)

Implement SearchProvider (id + search(query, config)) in search/<name>.ts; add a branch in search/index.ts. Providers MAY throw on network/auth failure — web-search catches and returns errors-as-values. Keep parse/shape logic in a pure exported fn (parseDdgHtml, mapSearxngJson, …) and unit-test it with an inline fixture (no network). HTML scraping (DDG) uses linkedom; the JSON-API providers use native fetch only.

Key decisions (don't re-litigate without new info)

  • Streaming is the default when the provider supports it (was "non-streaming in v0"; shipped 2026-06-28) — stream() yields incremental StreamChunks (text · thinking for reasoning models · tool_call · done). The loop streams when provider.stream AND onTextDelta are set, else complete(). The universal thinking chunk surfaces any model's reasoning live (OpenAI-adapter reasoning_content/reasoning, Anthropic thinking_delta); backends that hide reasoning (codex) no-op to the spinner. done stays the source of truth, so the loop is byte-identical to the non-streaming path.
  • No Anthropic stub — resolveProvider throws a clear "Phase 4" error instead of a fake adapter. Per global rule: no stubs returning fake values.
  • Kernel is the boundary — TS never decides safety; it asks the kernel. assess before every tool.
  • Tool results are values, not exceptions — {ok, output}. The loop never crashes on a tool error.
  • Search mirrors providers — SearchProvider is the same swap-by-env pattern as LLMProvider. DDG is the keyless default; Searxng (self-host) is the privacy recommendation; SerpAPI/Brave are opt-in with keys.
  • web-search resolves its provider lazily from process.env at call time, so buildRegistry()/ToolContext/the loop stayed unchanged when search was added.

Conventions

ESM .js imports · zod at every LLM/HTTP boundary · errors-as-values in tools · files <300 lines, fns <50 · tsc --noEmit must be clean before done · co-located *.test.ts (vitest). Integration tests in agent.test.ts use a FakeProvider + live kernel; they self-skip if the kernel is down.

Env

VANTA_PROVIDER (openai|ollama|anthropic) · VANTA_MODEL · VANTA_EFFORT_LEVEL (low|medium|high|max) · OPENAI_API_KEY · VANTA_OLLAMA_URL · VANTA_KERNEL_URL · VANTA_MAX_ITER · VANTA_TUI=v2 (opt-in mission-control shell). Defaults in .env.example. Local .env (gitignored) defaults to Ollama qwen2.5:14b.

Search (Phase 2B): VANTA_SEARCH_PROVIDER (ddg|searxng|serpapi|brave, default ddg) · VANTA_SEARCH_URL (searxng) · SERPAPI_KEY · BRAVE_KEY.

Store (Phase 2A): VANTA_HOME overrides the global store dir (default ~/.vanta). Holds skills/ + memories/, git-init'd; writes auto-commit (best-effort). Tests point VANTA_HOME at a temp dir.

Phase 3/4: ANTHROPIC_API_KEY (anthropic provider) · VANTA_VISION_MODEL (describe_image, default gpt-4o-mini) · VANTA_ALLOWED_DOMAINS (comma list; browser tools prompt-approve unlisted domains). Browser tools need npx playwright install chromium for live use (degrade gracefully without it). LSP tools cover .ts/.tsx only.

Phase 7: VANTA_PROJECTS_DIR (project rooms, default ~/Documents/GitHub/_active) · VANTA_MODEL_CHEAP / VANTA_MODEL_EXPENSIVE (task-routed models; unset = no routing) · VANTA_DISABLE_AGENT_VIEW=1 (kill switch for the background agent CLI surface; settings also supports disableAgentView) · VANTA_VERIFY=1 (opt-in post-turn completion verifier).

2026-06-21 operator rocks (all opt-in): VANTA_VOICE_PTT=1 (composer push-to-talk; needs whisper+ffmpeg+Mic permission) · VANTA_STT_MODEL (whisper model, default tiny) · VANTA_TERMINAL_CAPTURE=1 (tmux-backed terminal_capture tool) · VANTA_SLACK_TOKEN/VANTA_SLACK_BOT_TOKEN (#channel autocomplete) · VANTA_CHICAGO_MCP (CHICAGO computer-use MCP server name, default chicago) · VANTA_LORA_AUTO=1 + VANTA_LORA_BASE_MODEL (vanta tune lora personal LoRA; heavy first run downloads the base model).

Phase 5 (comms): VANTA_GOOGLE_CLIENT_ID + VANTA_GOOGLE_CLIENT_SECRET (one-time OAuth client — provision once in Google Cloud Console, then vanta auth google is one click per user). Tokens stored per-user in ~/.vanta/google-tokens.json. Every outbound (send/draft/create/update) is always approval-gated. Comms tools are offline-unit-tested only; live use needs the OAuth client + consent.

Gotchas

  • Run the test suite from vanta-ts/, never the repo root. npx vitest run from the root picks up the root config, which also scans the bundled reference/ quarry repos (goose + other reference agents) — ~229 extra .test.* files that fail under Vanta's config (0-test collection, wrong env, foreign platform assumptions) and report ~159 spurious file failures. From vanta-ts/ the real count is 606 files / 5077 tests. The shell cwd persists across Bash calls, so confirm pwd before a full run.
  • gitleaks pre-commit hook runs gitleaks protect --staged on every commit. Hook lives at scripts/pre-commit; install.sh symlinks it into .git/hooks/. Example files (.env.example, .mcp.json.example) and test fixtures are allowlisted in .gitleaks.toml. If you get a false positive, add a pattern to the allowlists section — don't skip the hook.
  • Web search defaults to auto, NOT plain DDG (the old 403 caveat is resolved). resolveAutoProviders chains keyless reliable providers (bing HTML, jina_ddg) AHEAD of the raw duckduckgo scraper, and web_search only fails if ALL error — so a flaky/IP-blocked DDG day degrades, doesn't break. (html.duckduckgo.com historically 403'd from some datacenter IPs; the auto chain routes around that, and DDG itself returned HTTP 200 from this box on 2026-06-27.) Verified live 2026-06-27: web_search "Hermes agent Nous Research" returned 3 correct titled results with URLs. Force a specific provider with VANTA_SEARCH_PROVIDER=searxng|brave|serpapi (+ VANTA_SEARCH_URL/BRAVE_KEY/SERPAPI_KEY). web-fetch is unaffected (clean Readability markdown).
  • Current source counts beat historical session counts. Real-counted 2026-07-10 (live exports): ALL_TOOLS has 125 built-in tools, buildRegistry().list() reports 129 registered (incl. factory mount_mcp/tool_search/mcp_auth/run_pipeline), and SLASH_COMMANDS.length reports 136 commands (vanta tune is a CLI command, not a slash). Latest full verify (2026-07-10): 1171 TS test files / 12365 tests green (+3 live-gated voice/LoRA tests skip without their opt-in env) · tsc clean · size gate clean. Older counts in session-addition notes are milestones, not current truth — don't trust a count without re-running the export.

Session additions (2026-06-28) — keep current

UNIVERSAL LIVE REASONING DISPLAY — stream any model's thinking, provider-agnostic. From a "the TUI freezes then dumps" report on codex/gpt-5.5: investigated, proved the streaming+spinner layer works 4 independent ways (live provider stream, consumeStream forwarding, a new TUI regression test, a real headless run), and root-caused the freeze to the ChatGPT Codex backend sending zero reasoning events (raw-SSE-confirmed — not fixable; graceful spinner is correct). Then built the real fix: a universal thinking StreamChunk any provider emits when its backend exposes reasoning.

  • Contract + plumbing: StreamChunk gained {type:"thinking",delta}; onThinkingDelta flows consumeStream → provider-call → AgentDeps → the TUI. The reducer holds a transient liveThinking (live-region only, cleared when output text begins or the turn ends); LiveRegion/ThinkingPreview render the model's reasoning dimmed in place of the generic spinner (BusyIndicator chooses).
  • OpenAI adapter (covers DeepSeek-R1, OpenRouter, Ollama, Gemini + any user-added OpenAI-compatible model): pure reasoningDelta(delta) reads reasoning_content (DeepSeek) / reasoning (OpenRouter); openai.ts stream() emits a thinking chunk. Live-verified: DeepSeek-R1 via OpenRouter streamed 163 reasoning chunks.
  • Anthropic now streams at all (it had complete() only — no streaming): streamAnthropicEvents (pure, in anthropic-convert.ts) maps Messages SSE events → chunks (text_delta→text, thinking_delta→thinking, tool_use+input_json_delta→tool_call). AnthropicProvider.stream() is thin glue. TDD'd; verified end-to-end against the REAL SDK parser (Stream.fromSSEResponse over real wire bytes — no key needed). claude-code provider inherits it.
  • codex: no-op (backend hides reasoning) → spinner. Every backend now streams (3 concrete providers + FallbackChain delegate).
  • Also this session: branded install URL curl -fsSL https://vanta.theft.studio/install.sh | bash (Cloudflare Pages custom domain + build-synced install.sh); vanta update fix (cli/update.ts pulls origin/<branch> explicitly — bare git pull --ff-only failed without upstream tracking). Full suite 980 files / 11149 tests green, tsc + size gate clean throughout.

Session additions (2026-06-27) — keep current

RELIABILITY HILL-CLIMB — readiness = measured task-running reliability (6/6 cards proven, all RELIABILITY-*). From "what makes us ready like Hermes/OpenClaw = reliability not features": built the measurement, stress-tested the real agent, found+fixed real bugs. State: docs/reliability-hillclimb.md; tracked pass-rate: docs/reliability-results.md; decision: DECISIONS 2026-06-27.

  • Harnesses (scripts/reliability-*.sh) — two-axis scoring: RELIABILITY (terminates / clean exit / no zombie / survives load = the bar, model-agnostic) vs SUCCESS (also-correct output, model-dependent); exit keys on reliability. smoke (binary gate) · stress (K-repeat + concurrency burst) · longhorizon (long-loop tasks + piped-REPL exit probe) · longrun (one big multi-stage task ×N, RELIABLE + COMPLETED via an artifact check; in-scope /tmp zone) · eval (the tracked scored eval — runs a bounded battery, parses each harness's reliability%, appends a dated row to docs/reliability-results.md; the measurement half of AHE-EVAL-HARNESS).
  • 3 real bugs found by the harnesses + fixed: (1) interactive REPL didn't process.exit on session end → silent MCP-handle hang (cli.ts, both interactive entry points now await startInteractive then exit — commit 79fce703); (2) codex provider had no request/idle timeout (openai.ts had it, codex.ts didn't) → a stalled SSE stream hung forever — codex.ts stream() now arms an AbortController that resets per chunk (resolveProviderTimeoutMs, combined with the user signal — 2a3ef344); (3) turn-loop re-threw every transient provider error (429/timeout/reset) → exit=1 — now agent/provider-call.ts getCompletionWithContextRetry retries transient errors bounded then stops gracefully (7371d765).
  • agent/provider-call.ts (NEW) — the model-call seam extracted from turn-loop.ts (which was 298/300 → 242): getCompletion + getCompletionWithContextRetry. tool-retry.ts gained isTransientError/resolveProviderRetries.
  • Decision (DECISIONS 2026-06-27): vanta run is the headless interface; the REPL is TTY-only — piping a multi-turn conversation in is unsupported (programmatic multi-turn = agent_session/gateway). RELIABILITY-HEADLESS-MULTITURN resolved by decision, not code.
  • Proven (executed runs, not code-path args): long autonomous run 12/12 unattended · provider hardening unit-tested · provider variance codex 100% / ollama 90% · kernel concurrency clean to 1024× parallel assess (no ceiling) · scored eval records dated pass-rate. Full suite 977 files / 11132 tests green throughout; tsc + size gate clean.

Session additions (2026-06-26) — keep current

A2A DELEGATE-A-BUILD — the real agent-to-agent build loop (no terminal needed). Reframed from "watch Claude in a terminal" to true A2A: Vanta hands Claude a task programmatically, Claude builds it headless, returns the result, Vanta verifies. Roadmap: VANTA-A2A-* (5 cards). Tool count 123 built-in at this milestone (added build_with_agent).

  • VANTA-A2A-DELEGATE-BUILD (shipped). call_agent/agent_session gained coding:true → claude --permission-mode acceptEdits so the agent WRITES code, not just answers. Builds default to fast Sonnet (claude's default Opus 15-min-timed-out) and run --output-format stream-json --verbose, parsed by agents/claude-stream.ts (parseClaudeStreamLine) → Vanta streams live progress (⋯ claude: Write(index.html)) instead of a silent block. agent_session(show:true) pops a VISIBLE terminal (agents/visible-terminal.ts, osascript Terminal/iTerm, fail-soft). defaultSpawn ignores stdin (kills the "no stdin in 3s" warning). Live-verified: landing page built in 26s with streamed progress.
  • VANTA-A2A-ROUTE-BUILD (shipped, piece 1). repl/agent-route.ts detects a build verb NEAR an agent name ("have claude build…") as delegation intent (BUILD_NEAR_AGENT); hasBuildIntent steers buildAgentRouteHint to coding:true + verify.
  • VANTA-A2A-BUILD-LOOP (shipped, piece 3). agents/build-loop.ts pure runBuildLoop (delegate + verify injected) → delegate → verify → fix→verify… until pass or maxIters; agent-failure re-delegates with the error; verify only after a successful delegate. tools/build-with-agent.ts wires delegate=call_agent(coding) + verify=expectFiles exist + optional verifyCmd exit 0.
  • VANTA-A2A-KERNEL-VERIFY (shipped). Kernel DESTRUCTIVE denylist dropped the bare "pathlib" (over-matched read-only Path(x).read_text() → blocked Vanta's verify); destructive methods still block (unlink(/rmdir/rmtree). 67 kernel tests.
  • VANTA-A2A-DOCKER-AUTONOMOUS (shipped — piece 2). Real bash builds (npm) use the Docker backend as the containment boundary. The earlier macOS sandbox-exec experiment timed out under Claude's Node runtime and was retired rather than maintained as a second backend. vanta a2a autonomous-status reports Docker, image, credential, and npm-driven proof readiness. See ERRORS.md for the discarded experiment.

Session additions (2026-06-25) — keep current

TALK-TO-CLAUDE-CODE — 5 cards shipped (drive another agent CLI), all LIVE-verified. From a session where Vanta (gpt-5.5) couldn't "talk to Claude Code": it had call_agent but never reached for it, then dead-ended on raw shell/tmux under the auto-sandbox. Built 1→2→3→4→5, each git-tagged card/<ID>:

  • VANTA-AGENT-ROUTING-DISCOVERY (root cause). repl/agent-route.ts (pure hasAgentIntent/detectAgentName/buildAgentRouteHint) prepends a route hint in buildSendText (both hosts; VANTA_AGENT_ROUTE=0 off); agent/tool-scope.ts agents group + HINT keeps call_agent/delegate schemas callable under deferred scoping (the hidden-schema root cause); prompt.ts stable line routes cross-agent intent to call_agent.
  • VANTA-AGENT-SESSION-INTERACTIVE. agent_session tool (open/send/read/close/list) drives a PERSISTENT session over another agent CLI via a detached tmux session. agents/agent-session.ts (pure orchestration over an injected SessionBackend port; open primes past the startup modal — boot-wait + Escape, found live; send polls the pane until output settles) + agents/tmux-session-backend.ts (live execFileSync tmux, send-keys -l). Live: claude remember 42 → recall 42 (context carried across turns).
  • VANTA-CALL-AGENT-STREAM. agents/external-cli.ts runExternalAgent now spawns STREAMED (line-buffered onChunk + heartbeat), RunResult byte-equivalent; ToolContext.onProgress wired to the note StreamEvent in agent/dispatch-tool.ts; call_agent passes ctx.onProgress. Live: 11 chunks streamed before the final return.
  • VANTA-SANDBOX-AGENT-REDIRECT. tools/agent-launch-hint.ts (pure) + shell-cmd.ts sandboxAgentRefusal: a tmux/background agent-launch under the sandbox is refused with text naming call_agent/agent_session. Live: refusal names both; the agent_session backend still spawns under the same sandbox.
  • VANTA-ACP-CLIENT. acp/client.ts (AcpClient + spawnAcpTransport + kernelApprover + runAcpClientSession) — the outbound ACP half (Vanta already serves ACP); vanta acp connect <cmd> [args] -- <prompt> (cli/acp-connect.ts). Live vs real vanta acp serve: initialize + session/new + 2 prompt turns + 4 streamed session/update; an out-of-root write triggered a session/request_permission routed through the kernel → rejected → write blocked.

Counts after: 122 built-in tools (126 registered) · 128 slash commands. Full verify: 967 TS files / 11063 tests green; tsc clean; size gate + boundaries clean. Gotcha learned: verify the real size gate with tsx src/cli.ts lint <files>, NOT src/lint/run.ts <paths> — the latter no-ops on explicit paths (silently passed a file the pre-commit then rejected for cx>10).

Session additions (2026-06-24) — keep current

COFOUNDER-IDEATION-METHODS expanded 10 → 22 + feasibility×creativity routing. The bundled ideation-methods skill (and its tested module solutioning/ideation.ts) absorbed the full creative-method set (the Hermes/SHL0MS creative-ideation skill, MIT — improved, not ported: native procedures, our deterministic tested router beats their prose one). Split for the size gate: solutioning/ideation-catalog.ts (the analytic 10, now each with an origin attribution + a creativity weight 0..1) + solutioning/ideation-creative.ts (12 artist/thinker methods — Oulipo/Queneau-Perec, Story Skeletons/Vonnegut, Chance-Remix/Cage, Pataphysics/Jarry, Pattern Languages/Alexander, Dérive/Debord, Compression-Progress/Schmidhuber, Pólya, Defamiliarization/Shklovsky, Creative Discipline/Tharp, Affinity Diagrams/KJ, Volume Generation). New optional balance signal (feasible|balanced|novel) is the feasibility↔creativity lever a phase/domain-only router can't do: novel → the phase's divergent method (NOVEL_BY_PHASE), feasible → its buildable one (FEASIBLE_BY_PHASE), unset/balanced → the unchanged base route (fully backward-compatible — all prior route tests pass). Added a writing domain (→ Story Skeletons / Oulipo). ideation.ts re-exports the catalog so existing ./ideation.js imports resolve. SKILL.md rewritten for 22 grouped on the axis + a UserPromptSubmit auto-fire trigger (Vanta side of "make it automatic"); the cross-agent half (Claude ~/.claude/settings.json via sync-triggers --claude, Codex AGENTS.md) is roadmap card IDEATION-AUTOFIRE-AGENTS (confirm-gated — writes global agent config). Verified: 962 files / 11018 tests green, tsc clean, size gate clean.

PROVIDER-USER-REGISTRY — add ANY OpenAI-compatible provider with no code change. providers/user-providers.ts mirrors the agents.json / mcp.json pattern: declare {baseURL, keyEnv?, model?, router?} per id in ~/.vanta/providers.json and VANTA_PROVIDER=<id> resolves it. The SECRET never lives in the file — only the env-var NAME (keyEnv); the key stays in .env. Keyless local endpoints work (omit keyEnv); a user entry overrides a built-in of the same id (add OR self-fix a backend without waiting on a release). resolveProvider checks loadUserProviders(env)[id] before the built-in table. providers.json.example + .env.example document it. (commit 6fdca3c)

PROVIDER-ROUTER-FREEFORM + TokenRouter fix. ProviderEntry gained router?:boolean; tokenrouter + openrouter flagged, and the setup wizard's chooseModelStep goes straight to free-typed "enter any model id" for routers (fixes "I pick TokenRouter but it only shows MiniMax" — VANTA_MODEL always accepted any id; the UX now matches). Also corrected the tokenrouter built-in: base URL is api.tokenrouter.com/v1 (not .io) and model ids are bare (MiniMax-M3, not minimax:MiniMax-M3), verified live against a real token. (commits b908aa2, b5955f0)

Session additions (2026-06-22) — keep current

SKILL-TRIGGERS. A skill can declare event triggers in its frontmatter (triggers: [{"event":"PreToolUse","match":"git_push"},{"event":"PostToolUse","when":"errors>=3"}] — single-line JSON, mirrors tags) and they auto-compile into hooks. skills/types.ts (SkillTrigger + triggers? on SkillMeta), skills/frontmatter.ts (parseTriggers, lenient → []), skills/triggers.ts (PURE: compileTriggers→Vanta ShellHook[], compileTriggersForClaude→~/.claude/settings.json entries [Stop, UserPromptSubmit, PreToolUse via the claudeToolMap Vanta→Claude tool map (git_push→matcher Bash + the emitter input-gates on "git push"), and tool errors (PostToolUse+when:error → Claude PostToolUseFailure, since Claude's PostToolUse fires only on success)], buildTriggerNote, mergeVantaHooks/mergeClaudeSettings idempotent+namespaced by the skills trigger-emit command marker), skills/triggers-sync.ts (upsert into ~/.vanta/hooks.json user-scope + opt-in ~/.claude/settings.json). Firing is Rule-Zero-safe: the hook runs vanta skills trigger-emit <slug> <event> which only SURFACES a recall note — Stop→stdout additionalContext (model-facing injection), PreToolUse→stderr heads-up (advisory; action:"block"→exit 2 hard gate), else stdout note (Claude injects). Never runs the skill body. Auto-syncs every session in prepare-helpers.ts loadSkillIndex (Vanta scope); Claude scope is opt-in vanta skills sync-triggers --claude. The emitter's --claude flag reads Claude's PreToolUse stdin payload (tool_input.command) and emits hookSpecificOutput.additionalContext (or permissionDecision:"deny" for action:"block"), input-gated so e.g. ship-preflight fires only before a git push Bash call — matching Vanta, no every-call noise. Demo: ship-preflight (PreToolUse:git_push) + systematic-debugging (PostToolUse:onError) carry triggers. Note: bundled-skill trigger updates need vanta skills install --force to reach ~/.vanta/skills (install skips existing). Also packaged as the standalone public repo jpoindexter/skill-triggers. Verified: 956 files / 10935 tests green, tsc clean, size gate clean.

VANTA-MIGRATE. vanta migrate <openclaw|hermes> (cli/migrate-cmd.ts + migrate/): preview → select → backup → apply importer that brings a competitor agent's skills + MCP servers + model config into ~/.vanta. migrate/parse.ts (pure, tolerant parsers: skills/<slug>/SKILL.md, mcpServers/servers JSON, provider/model config), migrate/plan.ts (pure plan like purge-plan.ts — injected fs reads, conflict detection vs existing ~/.vanta, formatPlan redacts secrets and flags secret env keys), migrate/apply.ts (backs up ~/.vanta first → writeSkill + merge mcp.json + upsertEnv model config; conflicts skipped unless --overwrite; injected for tests). Kernel-gated (assess() block→refuse); reversible (backup-first). Per-item picker (default interactive: numbered list → all/none/1,3/2-4 ranges; footprint flags --skills/--mcp/--model pre-narrow what's offered; --yes imports all unprompted). Pure parsers + selection helpers (numberedItems/parseItemSelection/filterPlanByNumbers/narrowByFootprint) tested offline against fixture ~/.openclaw/~/.hermes trees under migrate/__fixtures__/. vanta migrate is a CLI command (not a slash). Source layout is the de-facto convention (documented assumption; live tree is the boundary). Deferred to child cards (per the card's locked scope): memory/session import, Codex/Claude-Code sources. Verified: 954 files / 10909 tests green, tsc clean, size gate clean.

VANTA-SELF-LEARNING-LOOP (shipped). ONE named closed loop unifying the scattered self-improvement pieces (learning/): loop.ts runLearningCycle = observe → propose (reuses review/background-review.ts reviewTurn) → eval-gate (eval-gate.ts gateSkill: rejects thin bodies, refusal/negative-claim skills that harden into self-imposed limits, and shadows of hand-authored skills — injectable so the AHE skill-eval/ corpus can plug as a stronger gate) → adopt-or-archive (skills/store.ts archiveSkill reversibly retires a gated-out skill, Rule Zero) → measure (ledger.ts → .vanta/learning/ledger.jsonl). Real reuse metric: recalling a learned skill records a kind:"reused" event (tools/recall.ts reuseEvent), so /learning shows minted/refined/adopted + reused + adoption rate over a session. On by default (shouldReview/VANTA_SELF_IMPROVE), adoption gated; wired into reviewAfterTurn. Surfaced via /learning (repl/learning-cmd.ts); documented as the core differentiator on the docs site (self-improvement.md). All deps injected → fully unit-tested. The shareable Skills Hub ecosystem (the broader Hermes gap) is a SEPARATE future card, not this loop card. Verified: 954 files / 10916 tests green.

STATUS-VERBOSITY-CONTEXT. vanta status//status//doctor now adapt verbosity to the active config (fixes @gliang9's "too much info on non-default models" complaint). status.ts: formatStatus(r, {condensed}) drops the full per-provider API-key matrix (shows only the active provider's key) + velocity when condensed, keeps kernel/provider/store/goals/notices, and adds a --verbose hint. isDefaultConfig(env) = the provider is running its catalog defaultModel (out-of-box); resolveStatusCondensed(env,{verbose,isTTY}) = --verbose→full, non-TTY (scripted/piped)→condensed, else condensed only on a non-default model. Wired: vanta status/doctor pass argv (--verbose/-v, VANTA_STATUS_VERBOSE=1) + TTY detect; /status [-v] in the REPL (always TTY → keys on non-default model). Verified: 946 files / 10867 tests green, tsc clean, size gate clean.

FEEDBACK-FEATURE-REQUEST. In-product /feedback (repl/feedback-cmd.ts): /feedback <text> drafts a redacted GitHub issue (redactSecrets runs on the text before title+body), classifies feature-request vs general feedback (enhancement/feedback label), persists a pending draft under .vanta/feedback/, and shows it with a redacted-secret notice. /feedback send files it via kernel-gated gh issue create — safety.assess() block→refuse, allow/ask→proceed (the explicit send IS the human confirm) — to VANTA_FEEDBACK_REPO (default jpoindexter/Vanta). Default off: nothing files until send. Reuses AUTO-ISSUE's injection-safe buildGhIssueArgs + store/secret-scan; the gh runner is injected (makeFeedbackHandler(gh)) so block/allow/ask/gh-failure paths are unit-tested. Live boundary: gh authed. Count: 126 slash commands. Verified: 946 files / 10857 tests green, tsc clean, size gate clean.

PROVIDER-AWARE-WATCHDOG. Closes the "spurious cold-start timeout → cron double-run" bug class (lifted from a real OpenClaw/Hermes user report) structurally, not by tuning. providers/timeout.ts (pure) is one source of truth for the active provider's model-call request timeout: resolveProviderTimeoutSec/Ms(env) — precedence VANTA_PROVIDER_TIMEOUT_SEC > per-provider cold-start default (ollama/lmstudio 1800s, deepseek/nim/nvidia 900s, custom 1200s, else 600s = the verified SDK default → no regression) > global default; watchdogStallMinutes(env, floor) derives the liveness stall window as max(floor, ceil((providerTimeout + 120s margin)/60)). Wired: OpenAIProvider now sets an explicit timeout on the SDK client (opts.timeoutMs ?? resolveProviderTimeoutMs(process.env)) instead of the hidden default, covering all ~20 OpenAI-compat providers incl. the named DeepSeek/Ollama paths; liveness/watchdog.ts resolveWatchdogConfig clamps a too-tight VANTA_WATCHDOG_STALL_MIN up to the provider window so it can never false-fire before a cold call's own timeout could elapse (deliberate behavior change: a 10m floor on the default openai provider now resolves to 12m). Tested against a slow-first-call provider config (providers/timeout.test.ts + extended liveness/watchdog.test.ts). .env.example documents VANTA_PROVIDER_TIMEOUT_SEC. Verified: 945 TS files / 10844 tests green, tsc clean, size gate clean.

Session additions (2026-06-21) — keep current

Operator rocks live-completed (the runtime-gated rocks made real, not stubbed). A grind that drove each rock to a proven-live slice, honest about the hardware/permission boundary. New top-level surfaces are in the file map above; this is the changelog. Counts after the grind: 119 built-in tools (122 registered) · 125 slash commands · 944 TS files / 10831 tests green (+3 live-gated voice/LoRA skipped) · 62 kernel tests green.

  • VANTA-VOICE-STT. Real local whisper push-to-talk, proven end-to-end (macOS say → .aiff → whisper --model tiny → transcript). voice/ = ptt-state.ts (pure state machine + transcript post-processing) · mic-capture.ts (ffmpeg avfoundation; sox/arecord Linux fallback) · whisper-stt.ts (local whisper CLI) · ptt-flow.ts (record→transcribe→text). Trigger surface: the kernel-gated voice_input tool (tools/voice-input-tool.ts) + composer PTT key. Live needs the whisper + ffmpeg CLIs + Microphone permission.
  • VANTA-TERMINAL-CAPTURE. Live terminal capture via tmux (node-pty blocker resolved — its native binding doesn't load on node v24, so tmux scrollback is the real source). term/terminal-capture.ts (pure ANSI/CSI/OSC strip) + tools/terminal-capture-tool.ts (the terminal_capture tool).
  • VANTA-SLACK-CHANNEL-SUGGEST. #channel autocomplete in the composer. Pure ranking in repl/slack-suggest.ts; live conversations.list fetch + cache in comms/slack-channels.ts; composer wire ui/use-slack-channels.ts + ui/channel-palette.tsx. Live-verified against a real Slack workspace. Needs a Slack token for live use.
  • VANTA-SWARM-TMUX. LIVE tmux swarm, proven end-to-end. fleet/tmux-layout.ts (pure pane-layout plan) + fleet/tmux-backend.ts (real tmux children, detached session, one pane per worker) → vanta fleet tmux --task ... (cli/fleet-tmux-cmd.ts, one vanta run per pane, prints the attach command).
  • VANTA-CHICAGO-MCP (native desktop control). Route computer-use actions (screenshot/click/type/key/scroll) through a mounted CHICAGO computer-use MCP server instead of a local driver. mcp/chicago-route.ts (pure buildChicagoCall) · chicago-connect.ts (real mount/connect, raw-capable client) · chicago-client.ts (live binding). vanta control [setup|doctor] (cli/control-cmd.ts) makes native control work out of the box — grants OS permissions + checks cliclick (Vanta already ships vision_action: screencapture → vision grounding → cliclick).
  • PERSONAL-MODEL-TUNE. Real local LoRA training, proven on MPS. meta-tune/lora-dataset.ts (pure: ~/.vanta/preferences.jsonl chosen-vs-rejected → DPO pairs + readiness gate) → lora-train.ts (orchestrator) → lora_train.py (peft+transformers on MPS/CUDA/CPU); auto-tune.ts maybeAutoTune trains unattended each gateway tick when VANTA_LORA_AUTO=1. CLI: vanta tune lora (cli/tune-cmd.ts). The heavy first run downloads a base model (opt-in).
  • Self-serve setup wizard. vanta setup now offers a capability step (setup/capabilities.ts) that does the machine setup for the operator: runs brew install cliclick, opens the macOS permission panes (platform/macos-prefs.ts), and writes the env flags for desktop control / voice / auto-tune. The OS toggle itself stays the user's one click. setup-full.ts surfaces it.
  • Operator UI. VANTA-SETTINGS-PANEL + VANTA-AGENT-DETAIL-UI shipped as pure Ink panels (test-render verified). vision-action gained a mis-click/recover refinement.

Session additions (2026-06-20) — keep current

VANTA-SKILL-BATCH. vanta batch run --task <i> [--task <i> ...] [--base main] is the PR-workflow layer over the parallel worker fleet. batch/batch.ts is pure (test-gate append so only green-tested workers reach done, prCandidates, buildGhPrArgs, parsePrUrl, createBatchPrs with injected gh/push, formatBatchReport); cli/batch-cmd.ts is the CLI surface — it reuses buildFleetAgentDeps (newly exported from cli/fleet-cmd.ts) to spawn worktree-isolated workers, then for each done worker pushes the branch and opens a PR via gh, reporting URLs; a blocked worker gets no PR. Interactive (approves worker actions) so it stays CLI-only, not a TUI slash. Bundled skills-library/batch/SKILL.md (count 43→44). Live needs: gh authed + a remote (documented boundary). vanta batch is a CLI command — no new tool/slash command. Verified: 606 TS files / 5077 tests green, tsc clean, size gate clean. (The lone hooks/file-watch.test.ts flake observed during this slice was root-caused + fixed in HARNESS-TEST-FLAKE-WATCHER below.) Run the suite from vanta-ts/ — from the repo root vitest also scans the bundled reference/ repos and reports spurious failures.

HARNESS-TEST-FLAKE-WATCHER. hooks/file-watch.test.ts was nondeterministic under full-suite load. Root cause: fs.watch (FSEvents) arms asynchronously with no readiness signal, so the test's single trigger write could land before arming and the change event was missed, not delayed — a longer timeout can't recover a lost event. Fix: waitForMarker(marker, trigger) re-fires the trigger (rewrite watched.txt) every 25ms until the marker appears, so a write is guaranteed to land post-arm and is caught within one 50ms debounce. Production needs no change (the watcher is long-lived; files change well after arming) — the fix is test-only, not a cargo-cult settle delay. Rather than fix only the test that happened to fail, swept the whole anti-pattern ("fixed sleep/tick, then assert a positive async result"): added poll helpers waitForFrame(inst, text) + waitUntil(() => predicate) to ui/test-render.tsx, converted the Ink UI tests (mcp-panel/config-panel/sandbox-panel/trust-dialog/composer) to wait on a view-unique marker, and audited every setTimeout sleep in the suite — converting the genuine cases (ui/config-actions, ui/use-submit, uds/peers, agent/permission-gate, mcp/client) to poll-until-condition while leaving legitimate fixed delays (negative assertions, recovery polls, mock delays, interval tests). ui/CLAUDE.md documents the no-fixed-tick rule. 11 files converted; 2 consecutive fully-green full-suite runs (606/606 files, 5077/5077 tests). Also real-counted the source surface and reconciled the stale doc anchors: ALL_TOOLS=105 built-in / buildRegistry().list()=108 registered / SLASH_COMMANDS=110 (root CLAUDE.md said 94/97/103, this file's Gotcha said 95/98/99 — both fixed). Every root cause + the run-from-vanta-ts/ trap logged in ERRORS.md.

Session additions (2026-06-18/19) — keep current

GOAL-DEPS. goals/deps.ts adds a TS-side dependency graph stored at .vanta/goal-deps.json (version:1, edges:[{blockerId,dependentId}]) without changing the Rust kernel goal ledger. /goal blocks <blocker> <dependent> and /goal blocked_by <dependent> <blocker> add edges; /goal status, /goals, and vanta goals render derived blocked_by/blocks state. Completing a blocker with /goal done <id> reports newly unblocked dependents as woke: when all their blockers are done. Verified: 469 TS files / 3679 tests green, tsc clean, size gate clean.

VANTA-HOOK-TYPES. .vanta/hooks.json now supports hook types command/shell, http, mcp_tool, prompt, and agent. Shared controls: timeoutMs, once, statusMessage. HTTP hooks POST hook context JSON and expose only allowedEnvVars; prompt hooks evaluate a provider prompt and can return JSON block/allow verdicts; agent hooks run a tool-enabled worker via spawnSubagent using injected AgentDeps, then convert structured/plain JSON verdicts into allow/block. Verified: 471 TS files / 3687 tests green, tsc clean, size gate clean.

VANTA-HOOK-AGENT-TYPE. Agent hook execution now has a shared agent-hook-deps.ts dependency builder and live REPL/tool call sites pass the same AgentDeps object used by the conversation. type:"agent" hooks can run full multi-turn workers for PreToolUse, PostToolUse, UserPromptSubmit, and Stop; tests cover prompt/stop event dispatch plus a real worker tool call before a structured block verdict. Early Setup/SessionStart lifecycle hooks still run before prepareRun, so they remain providerless by design. Verified: 471 TS files / 3690 tests green, tsc clean, size gate clean.

VANTA-HOOK-EVENTS. .vanta/hooks.json accepts the full 30-event hook vocabulary, and live code emits lifecycle/session, prompt/display, tool-batch, permission, compaction, config, worktree, fleet, subagent, file watcher, cwd-change, MCP notification/elicitation, StopFailure, and TeammateIdle events. Verified: 473 TS files / 3704 tests green, tsc clean, size gate clean.

PAPER-HOOK-EVENTS-27. hooks/paper-events.ts maps the 27 hook events listed in arXiv:2604.14228v1 §6 against Vanta's 30-event hook schema. Tests require all paper events to be present and keep Vanta's deliberate extras explicit: UserPromptExpansion, MessageDisplay, and PostToolBatch. Verified: 479 TS files / 3732 tests green, tsc clean, size gate clean.

PAPER-DEFERRED-SCHEMAS. Tool names + descriptions stay visible in the stable prompt, while agent/tool-scope.ts exposes only a task-relevant full-schema subset to the provider. tool_search remains in the core schema set; when it returns ## tool_name results, toolScopeContext carries those names forward so the next provider call receives those full schemas. Verified: 473 TS files / 3705 tests green, tsc clean, size gate clean.

PAPER-SUBAGENT-SIDECHAIN. subagent/spawn.ts now runs workers through an explicit Conversation so the full worker message history can be written to .vanta/sidechains/<timestamp>-<uuid>.json. Parent tools still receive only the summary AgentOutcome text; the sidechain file carries {version, goal, instruction, model, createdAt, outcome|error, messages} for audit. Verified: 473 TS files / 3706 tests green, tsc clean, size gate clean.

PAPER-SHELL-SANDBOX. shell_cmd now honors VANTA_SHELL_SANDBOX=1 by mapping it to the existing OS sandbox wrapper for shell commands only; run_code remains controlled by global VANTA_SANDBOX=1. Background shell tasks fail closed while sandboxing is enabled, and VANTA_SANDBOX_NET=1 allows network in either mode. Verified: 473 TS files / 3707 tests green, tsc clean, size gate clean.

HARNESS-VISUAL-VERIFY. /verify now appends deterministic close-out requirements generated from git diff HEAD: non-doc changes require rules evidence, runtime code requires a representative command/tool observation, and UI paths/extensions require browser screenshot evidence with a cited path and observation. The logic lives in verify/visual-closeout.ts and preserves the existing cc-verify skill flow. Verified: 474 TS files / 3711 tests green, tsc clean, size gate clean.

SELFHARNESS-NL-ASSERTIONS. verify/nl-assertions.ts adds a separate LLM judge for captured input/output pairs plus plain-English assertions, returning strict pass/fail reports and failing closed on malformed judge output. New nl_assertions tool exposes it to agent/self-repair/eval workflows and can route through VANTA_ASSERTION_PROVIDER / VANTA_ASSERTION_MODEL. Counts: 89 built-in tools (91 registered). Verified: 475 TS files / 3716 tests green, tsc clean, size gate clean.

ARCH-MODULARITY-SKILL. skills-library/vanta-port-adapter/SKILL.md ships a bundled architecture skill for adding swappable Vanta capabilities behind typed ports, adapters, and one resolver/registry, with examples from providers, MCP, loop, and code-intel boundaries plus concrete verification expectations. The bundled-library smoke test now asserts the skill is present. Bundled skill count: 43. Verified: 475 TS files / 3716 tests green, tsc clean, vanta skills lint clean of errors.

PORT-TOOL-REGISTRY-IFACE. tools/registry.ts now exposes a ToolRegistry interface and InMemoryToolRegistry adapter. buildRegistry() returns the port; production construction sites use the concrete adapter explicitly; mount_mcp, tool_search, agent deps, MCP serve/mount, plugins, voice, and hooks keep depending on the interface. tools/registry.test.ts proves a scoped registry implementation can replace the default for a real consumer. Verified: 476 TS files / 3718 tests green, tsc clean, size gate clean.

COMPRESS-ON-ERROR. agent/context-pipeline.ts now detects common provider context-window failures and can force compressMessages after such an error. agent/turn-loop.ts wraps provider calls with one compact+retry attempt; if the retry also hits context length, the turn returns a clean stopped message instead of throwing through the session or retrying indefinitely. Verified: 477 TS files / 3721 tests green, tsc clean, size gate clean.

LIVENESS-WATCHDOG (Part 1 of 2 — building). liveness/watchdog.ts detects silently-stalled loops and surfaces them within one tick. A new LoopState.runStartedAt (stamped by markInProgress(state, true, now) in loop-cmd.ts) lets detectStall (pure) flag a loop whose run has been inProgress past VANTA_WATCHDOG_STALL_MIN (default 30m). checkLiveness scans every loop; surfaceStalls raises a watchdog escalation (which pauses the loop) so a stuck run stops spinning and shows up via vanta loop escalations (idempotent). Wired into gatewayTick (auto-surface each tick) + vanta watchdog [check|run]. Remaining (Part 2): "runaway spawn recursion is halted by the kernel" — a Rust kernel spawn-depth guard; card stays building. Verified: 563 TS files / 4497 tests green, tsc clean, size gate clean.

SELFHARNESS-SELF-CORRECT-LOOP. selfcorrect/loop.ts ties failure→fix→regression-lock into one loop: selfCorrect(failure, deps) confirms the failure reproduces (run), drives a fix (injected), reruns the failing input, and on success records a regression lock — every side effect injected so the orchestration is fully tested (no-failure / fix-error / still-failing / fixed). The self_correct tool (tools/self-correct.ts) wires it live: the failing command is kernel-assessed via describeForSafety; the fix is a spawnSubagent worker (registry excludes delegate/self_correct) whose edits are each kernel-gated (the "gated diff, approve" step); success calls appendLock (reuses the regression_lock store, re-verifiable with regression_lock check). 94 built-in tools (97 registered). Verified: 562 TS files / 4490 tests green, tsc clean, size gate clean.

PAPER-PROACTIVE-TICK. proactive/ adds KAIROS — a focus-aware proactive heartbeat: when Vanta is idle AND the user is away, it advances queued loop wakes on its own under a strict throttle. policy.ts (pure: ProactiveConfig/ProactiveState + decideProactiveTick gating on enabled → queued-work → budget hard-stop (reuses PCLIP-BUDGET-HARDSTOP on budgetScope) → idle-time → cadence → daily cap; recordTick with day-roll; recordActivity; resolveProactiveConfig from VANTA_PROACTIVE_*). store.ts (.vanta/proactive.json + markProactiveActivity). tick.ts (runProactiveTick — load → decide → run one batch → record, injected queue/budget/runner). cli/proactive-cmd.ts → vanta proactive [status|run]: status shows whether it would tick now; run drains queued loop wakes and advances active loops (cron-invocable). User activity is stamped every interactive turn via repl/post-turn-gates.ts (so "away" is real). loop/wake.ts gained peekLoopWakeCount. Off by default (VANTA_PROACTIVE=1). Verified: 561 TS files / 4486 tests green, tsc clean, size gate clean.

SETUP-TERMINAL-BACKEND. vanta setup now offers an Execution backend section (local / sandbox / docker / ssh) — surfacing the run-anywhere backends in the wizard. setup-sections.ts Choice gained an optional env (explicit multi-key writes) so one choice can set the right combo: local → VANTA_EXEC_BACKEND=local+VANTA_SANDBOX=0; sandbox → VANTA_SANDBOX=1; docker → VANTA_EXEC_BACKEND=docker (+ optional VANTA_DOCKER_IMAGE prompt); ssh → local default + a pointer to settings.sshConfigs / vanta ssh <name> (ssh execution is per-command, not a global mode). Auto-surfaces via setup-full.ts's SETTINGS loop. Verified: 558 TS files / 4466 tests green, tsc clean, size gate clean.

BACKEND-DOCKER. exec/backend.ts adds a containerised execution backend over the existing sandbox seam — the first concrete run-anywhere backend. wrapExec(args) is the single wrapper shell_cmd (runLocal) and run_code (runStep) now call instead of maybeSandbox: VANTA_EXEC_BACKEND=docker → docker run --rm (pure buildDockerArgs: mounts project root + writable zones + tmp only, -w root, --network none unless VANTA_SANDBOX_NET=1, then the base command) so out-of-container writes don't persist; else it defers to maybeSandbox (OS sandbox when VANTA_SANDBOX=1, else byte-identical base). Docker CLI absent → clean local fallback (dockerAvailable, injectable). VANTA_DOCKER_IMAGE overrides the default (alpine:latest). The kernel assess() gate is upstream of the wrapper, so it runs identically on every backend. Live-verified: a shell_cmd under VANTA_EXEC_BACKEND=docker ran inside Alpine (container hostname, project root mounted as cwd). Verified: 558 TS files / 4463 tests green, tsc clean, size gate clean.

VANTA-SSH-CONFIGS. Named SSH connection profiles — the run-anywhere foundation (address a host you control by name). ssh/config.ts (pure: SshProfileSchema {name,host,user?,port?,identityFile?,options?} + resolveSshProfile/sshTarget/buildSshArgs/profileNames); settings.sshConfigs array added to SettingsSchema. shell_cmd gains an ssh param — {ssh:"vps", command} runs the command on that host via ssh <args> <command> (kernel still assesses it; describeForSafety surfaces the host; local sandbox skipped since execution is remote; background-over-ssh refused). cli/ssh-cmd.ts adds vanta ssh <name> — resolves the profile and spawns an interactive ssh (stdio inherit). The live connection needs the user's host; profile resolution + ssh-arg construction + spawn wiring are unit-verified. 93 built-in tools (96 registered; vanta ssh is a CLI command, not a tool). Verified: 557 TS files / 4456 tests green, tsc clean, size gate clean.

VANTA-SSH-SESSION. vanta ssh user@host now opens an interactive agent session over SSH (the run-anywhere North Star). ssh/config.ts gains pure parseSshTarget (user@host / host:port → ad-hoc profile, injection-safe via the schema), looksLikeRemoteTarget, and resolveSshTarget (configured profile by name first, else an explicit remote address; a bare unconfigured word resolves to null — never an implicit dial). cli/ssh-cmd.ts planSsh decides the mode: a bare configured-profile name stays the plain shell (back-compat), an explicit user@host (or --agent) opens the agent session by setting VANTA_SSH_SESSION and launching startInteractive (lazy-imported to avoid an import cycle); --shell forces the plain shell. shell_cmd reads VANTA_SSH_SESSION as a session-wide default so tool calls execute on the remote host while the kernel assess() + permission prompts stay local by construction (only command execution is remote; describeForSafety surfaces the session host). The live connection needs the user's host; parsing, resolution, mode selection, and session routing are unit-verified. Boundary: shell_cmd is the remote-exec surface this slice; file/run_code tools stay local. 94 built-in tools (97 registered; vanta ssh is a CLI command, not a tool). Verified: 602 TS files / 5045 tests green, tsc clean, size gate clean.

RUN-ANYWHERE-READINESS-STATUS. vanta run-anywhere status [--json] [--check-release] is the aggregate release-proof surface for Run Anywhere v1. It reads durable receipts only: .vanta/serverless-gateway.json must contain a successful Modal/Telegram prove receipt (provedAt + telegramAcceptedAt), .vanta/channel-proofs.jsonl must contain an accepted Teams round trip, and .vanta/termux-arm64-proof.txt must contain TERMUX_ARM64_E2E_OK release_kernel=1. --check-release also queries the latest GitHub release for vanta-kernel-aarch64-linux-android plus its .sha256 so the operator sees the release-asset prerequisite before touching a phone. vanta backend gateway prove now persists the serverless proof receipt after the real Modal zero→wake/reply→zero check, and scripts/termux-arm64-device-proof.sh --require-release-kernel writes the Termux proof marker for the aggregate command. A missing proof exits 1 and prints the exact next command; no mocks or setup receipts count as release proof.

DESKTOP-VISION-TO-ACTION. The UI-TARS perceive→ground→act→verify→recover loop (vision-action/loop.ts, pure + fully injectable): visionActionStep perceives, grounds the target, acts, re-perceives, and flags a mis-click when the screen doesn't change; runVisionAction adds the recover-and-retry cadence (re-observe up to maxAttempts). New kernel-gated vision_action tool (tools/vision-action.ts + vision-action-run.ts): default substrate is macOS screencapture (perceive) + a vision provider grounding to coordinates (parseGroundResponse, fail-safe to not-found) + cliclick (act) + a vision change-verdict (parseChangedResponse, ambiguous→SAME so an unclear frame retries, never false-succeeds). Each parser/arg-builder is pure + unit-tested; the loop is exhaustively tested with an injected substrate. Live needs (documented boundary): macOS + Screen Recording permission + a vision model + the cliclick helper; OS-level actuation beyond cliclick is the desktop-driver boundary. 95 built-in tools (98 registered). Verified: 604 TS files / 5065 tests green, tsc clean, size gate clean.

PCLIP-BUDGET-HARDSTOP. budget/ adds scoped spend budgets with HARD STOPS — the "walk away safely" rail. budget/types.ts (pure model: statusFor/applySpend/remainingUsd; active→warning at 0.8·limit→exceeded at limit, pauseReason "budget") + budget/store.ts (.vanta/budgets.json, setBudgetLimit/recordSpend reporting the crossing transition once/getBudget/clearBudget) + budget/enforce.ts (enforceScopeBudget records spend and, on the spend that crosses the limit, runs the scope's stop action; for a loop:<id> scope that's pauseLoopForBudget (def active→paused) + removeWakesForGoal (cancels that loop's queued wakes in wake-events.jsonl) — concrete auto-pause + cancel-queued-work; checkLoopBudgetBeforeRun is the pre-run gate). Wired: cli/ops.ts buildCronRunTask records each autonomous/gateway run's cost (estimateCostUsd) against the loop scope resolved from wake ?? wakeContextFromEnv() (gateway sets the wake env on the spawned child); cli/loop-cmd.ts handleRun refuses to iterate an overspent loop via the pre-run gate. wake.ts removeWakesForGoal drops a loop's queued wakes (keeps undecodable lines). New budget tool sets/inspects/clears a scope's limit. 93 built-in tools (96 registered). Verified: 554 TS files / 4438 tests green (2 timer-flake files pass in isolation), tsc clean, size gate clean.

PCLIP-ATOMIC-CHECKOUT. team/checkout.ts adds an atomic task-checkout execution lock so two parallel fleet workers can never double-work one task. checkoutTask({taskId, workerId, dir}) claims a task via an exclusive-create lock file (wx/O_EXCL → atomic at the FS layer; exactly one concurrent caller wins, the rest are refused); the lock record names the holder (workerId/pid/acquired), a lock orphaned by a dead pid is reclaimed once, and release()/withTaskCheckout free it. fleet/fleet.ts runWorker now checks out each task BEFORE creating a worktree or spawning — a refused claim returns a blocked worker without touching the shared task ledger (the holder owns it) or wasting a worktree; the lock is released when execution finishes (checkout/checkoutDir seams on FleetDeps). Tests prove 16 concurrent claims → exactly 1 wins, and a fleet worker refuses an already-held task (no spawn, no worktree). Verified: 550 TS files / 4411 tests green, tsc clean, size gate clean.

Session additions (2026-06-16) — keep current

Size-gate split (file ≤200 soft / ≤300 hard). A refactor pass brought every editable TS file under the soft gate by extracting cohesive seams (no behavior change, re-export for back-compat). New structural files: tools/all-tools.ts (the ALL_TOOLS array — the new tool-registration site; index.ts is now just buildRegistry), tools/{team-run,radar-scan,browser-act-run,drive-write,git-write,calendar-write,gmail-helpers}.ts, context/sanitize.ts, cli/{extra-cmds-2,memory-cmd,skills-cmd,hooks-cmd,loop-cmd-ops,ops-app,startup}.ts, agent/turn-loop.ts, session/{ef-gates,prepare-helpers}.ts, repl/{context-cmds,media-cmds,session-cmds}.ts, providers/{codex-codec,model-caps,anthropic-convert,openai-convert}.ts, google/auth-store.ts, hooks/shell-hook-run.ts, loop/{parsers,stages,stop}.ts, brain/entry-types.ts, desktop/handlers.ts, roadmap/render-assets.ts, ui/{app-regions,mode-line}.tsx + ui/use-session-status.ts. Only factory/* stays exempt. Counts refreshed: 84 built-in tools (86 registered) · 97 slash commands · 3471 TS tests (425 files) · 56 kernel tests.

Session additions (2026-06-15) — keep current

Operator profile + preference signals. operator-profile/profile.ts ships declared vs inferred autonomy/scope/detail/risk preferences and tighten-only approval preferences. preferences/signals.ts appends zod-validated chosen-vs-rejected rows to ~/.vanta/preferences.jsonl; dispatch-helpers.ts records only human approval/denial prompts, never kernel blocks or auto/rule/profile non-human decisions. /preferences export prints the JSONL path/content. Profile inference can read preference rows and infer conservative/narrow behavior from denial-heavy broad/risky actions.

VANTA-TAB-NAV. ui/focus.ts adds the small focus manager; ui/app.tsx builds the visible focus target list and routes Tab/Shift+Tab through it. Composer input is active only when focused; approval actions expose focus IDs (approval-allow|always|deny|never) and Enter activates the focused row. Shift+Tab keeps the legacy mode cycle when the composer is the only focus target.

Session additions (2026-06-14) — keep current

Auto minimalism (rewritten from ponytail MIT, renamed auto). A "do the least that works" discipline, taken non-redundant with Vanta's stack (the size gate already enforces the floor mechanically; CLAUDE.md already enforces anti-drift). Vanta: skills-library/auto/SKILL.md (the generative YAGNI→stdlib→native→dep→one-line ladder + the auto: ceiling-comment convention; auto-installs to ~/.vanta/skills and ~/.claude/skills) + /auto [lite|full|ultra|off|review] (repl/auto-cmd.ts: live system-prompt directive injection mirroring /planmode's marker; review resends a deletion-accounting rubric — distinct from /simplify's general cleanup). Pure helpers (parseAutoArg/buildDirective/stripDirective) unit-tested. Global another agent: ~/.claude/commands/auto.md (a /auto slash command — works in every project) + ~/.claude/skills/auto/SKILL.md. Skipped the upstream's JS hooks, 6 framework rule-copies, benchmarks, pi-extension.

Harness guardrails + solutioning. memory/guardrails.ts labels recalled brain entries as usable only when fresh, non-conflicting, and provenanced; brain recall returns flagged stale/conflicting/weak-provenance entries as "not used" hypotheses and the prompt requires current-state verification before acting. agent/tool-scope.ts scopes large tool catalogs per provider call, the stable prompt lists names + summaries, and tool_search expands searched tools into the next full-schema provider call; VANTA_TOOL_SCOPE=0 restores full exposure. modes/builtin.ts now includes solutioning-mode: goals/brain/research -> ranked what-to-build recommendation with sources -> stop before implementation.

Plugin framework v1. src/plugins/manifest.ts, context.ts, commands.ts, and loader.ts ship the opt-in runtime plugin framework. Enabled plugins in ~/.vanta/plugins/<name>/plugin.json register tools and slash commands through register(ctx); disabled plugins do not import. Plugin tools must be namespaced (plugin_<name>_...), define describeForSafety, avoid collisions, and execute through the existing kernel-gated dispatch path. Project plugins require both plugins.trustProjectPlugins=true and VANTA_ENABLE_PROJECT_PLUGINS=true. npm loading, providers/platforms/hooks, and CLI runtime-plugin management remain deferred.

TUI parity grind + research intake. Built the Claude-Code-shaped surfaces on the 06-13 rebuild and reversed two of its "not built" notes.

  • Approval → per-tool numbered menu (ui/approval-prompt.tsx): typed request context from permissions/request.ts (bash/file edit/file write/web/computer/sandbox/skill), Do you want to proceed? ❯1 Yes / 2 Yes,don't ask again / 3 No / 4 Never allow, ↑↓/Enter/1-4/Esc. Always/Never persist tool-scoped allow/deny rules; kernel block stays immovable.
  • Shift+Tab mode cycle (normal → auto-accept → plan → normal, ▶▶/⏸ indicator). Auto-accept auto-approves the kernel ask tier only (block refused upstream → safe by construction) — this reverses the "auto-approve/yolo skipped" note. Plan reuses real /planmode enforcement.
  • Transcript: tool calls render ⏺ Verb(detail) / ⎿ result (dropped the grouped header) + clean labels for grep/glob/background/refs; syntax-highlighted fenced code (ui/highlight.ts); no Goal/Expected preamble (prompt rule reframed) and no per-turn token dump.
  • Chrome: rounded composer box + blinking cursor (ui/use-blink.ts); truecolor hex theme (matches docs/design-refs); active-goal ◇ line + compact PLAN ▰▰▱▱ ✓◐○ bar + status chips (live ◷ timer via ui/use-clock, MCP ✓); rich pickers (● current).
  • New overlays/commands: /loops (loop dashboard over the real store), /changes (interactive edit-review with git keep/undo), /context (visual per-category token breakdown), styled /goals ledger.
  • Anti-ghosting (2nd vector): streamed text commits to <Static> the moment a tool call follows it (ui/reducer.ts commitText) — see ERRORS.md.
  • Carried-goal-paused-on-launch: a prior-session goal starts PAUSED (no silent re-run on restart); /goal resume activates, /goal clear|drop drops; VANTA_GOAL_RESUME=auto restores old behavior.
  • Research intake → roadmap (now ~741 cards): three reference docs in docs/research/ + ~37 cards from the agent-harness paper (arXiv:2604.14228, PAPER-*), Anatomy of an Agent Harness (HARNESS-*), and Self-Correcting Agent Harness (SELFHARNESS-*).
  • Built-not-wired: term/json-format.ts (Vanta summarizes tool output, so no raw-output display surface yet — VANTA-SHELL-JSON-FORMAT stays next).

8 large rocks — slice 1 of each shipped (operator capabilities). Each rock got one vertical slice: a ~/.vanta/*.jsonl store + a kernel-gated tool + a /-command view, following the world reference pattern. All 8 now have a shipped slice; later slices tracked per-card in roadmap.json.

  • World model (world/store.ts + tools/world.ts + /world) — entities + relations across your systems. Money OS (money/* + /money) — offers/prospects/revenue. Opportunity radar (radar/* + /radar) — scored opportunities. Background teams (team/* + /team) — named worker roster. Life-wide search (search/life.ts + tools/life-search.ts + /lifesearch) — source-cited search across the local stores. Self-repair compartments (self/compartments.ts + /compartments) — body map + max-autonomy per part. Verification organ (/skeptic in repl/think-cmd.ts) — adversarially refute a claim by default.
  • Browser body (8th, browser/act.ts + tools/browser-act.ts → browser_act tool) — the action-OUT surface (navigate/click/type/press/scroll/wait) over the existing playwright acquirePage. classifyAction flags irreversible controls (submit/buy/delete/login/send), secret entry, and Enter; the tool gates any such sequence — plus unlisted domains — through ctx.requestApproval with a masked numbered dry-run preview before launching. Pure riskyActions/previewActions/describeAction (secrets never printed). This is the browser surface of the DESKTOP-* rocks; OS-level coord/a11y targeting + kill-switch remain later slices. 71 tools.
  • Operator /-views barrel through repl/operator-cmds.ts; deep-thinking /skeptic joins /ultrathink//ultracode//deep-research in think-cmd.ts.

Rocks — slice 2 across all 8 (depth pass). Each rock got a second vertical slice (pattern: a NEW pure module imported by the existing tool/command, so slice-2 work stays on disjoint files; built by a parallel-subagent wave + central verify). All green: typecheck clean, 311 tests across the touched files, size gate clean (28 files).

  • Verification organ (keystone) — regression_lock tool (NEW, tools/regression-lock.ts + verify/store.ts verify.jsonl + pure verify/check.ts grading) + /locks view (repl/verify-cmd.ts). lock {claim, command, expect} records a regression case; check [id] re-runs the locked command(s) (each approval-gated), flags a regression if the substring is gone or the command exits non-zero; list shows status. A debugged failure becomes a locked, re-runnable proof — SELFHARNESS-FAILURE-TO-TEST. 72 tools.
  • World — world/conflicts.ts: findConflicts (contradiction = same subject+predicate, different object) + recallWithSources (cited recall); world tool conflicts action, query now cited, /world ⚠-flags conflicts.
  • Money — money/review.ts: suggestPrice (median band) + weeklyReview; money tool price+weekly, /money weekly snapshot.
  • Radar — radar/scan.ts: rankOpportunities (composite pain×signal, recency tie-break) + draftOffer; radar tool scan+offer, /radar ranked.
  • Teams — team/tasks.ts: a task-assignment + legal-transition status ledger (team-tasks.jsonl); team tool dispatch/advance/tasks, /team shows per-worker load + running task. CLI management is live via vanta agents, attach/logs/respawn/stop/rm <id>, and daemon status/stop; stopped/removed tasks are closed for worker load.
  • Life-search — search/life-rank.ts: dependency-free relevance ranker (term density + exact-phrase + title-hit + recency, 0..1); results ranked, /lifesearch relevance bar. (Vector embeddings = later slice, no embed-dep added.)
  • Self-repair — self/detect.ts: detectBroken (per-compartment healthy/impaired/down from real cap checks) + lastKnownGood (newest good git sha per compartment = rollback target) + repair.jsonl markers; /compartments shows health + lkg sha.
  • Browser — browser/observe.ts: summarizeElements/formatElements (interactable targets + suggested selectors); browser_act gains observe:true (appends a grounding block) + a kill-switch VANTA_BROWSER_DISABLED (short-circuits before any launch/approval).

Rocks — slices 3–4 (depth to done) + completion ledger. Drove each rock toward its done-criterion. Full map: docs/research/rocks-completion-ledger.md.

  • S3: radar→money promote (scored opportunity → prospect, cross-rock inflow); world merge/duplicates (world/merge.ts consolidate + re-point relations); self-repair propose-only rollback (self/rollback.ts — prints git checkout <lkg-sha>, never auto-executed, Rule Zero).
  • S4: world freshness/confidence (world/confidence.ts — recall now [likely · 62% · source:ts]); money deliverable+followup record kinds (money/work.ts); life-search change-detecting refresh (search/refresh.ts djb2 per-store digests).
  • Done: World model, Money OS, Verification organ, + DESKTOP-ACTION-SCHEMA & DESKTOP-CONTROL-BOUNDARY (browser surface). Horizon (deliberate, documented boundary): radar live scanning (needs reliable search), life-search embeddings (needs embed model), self-repair auto-rollback + teams live-spawn (need operator sign-off — self-git reset/agent-spawn are Rule-Zero-gated), browser OS-level control (needs a desktop driver). Rocks — horizon builds (operator-authorized 2026-06-14). The 4 named horizon blockers, each with safety rails:
  • Radar live scanning — radar/extract.ts + scan_web action: resolveSearchProvider runs live search, extractOpportunities scores pain/buyer signals, appends candidates; catches search failure → clean value (never throws). Opportunity radar → done.
  • Life-search local embeddings — search/embed.ts + semantic action: ollama /api/embeddings + cosineSim, zero new dep, VANTA_EMBED_MODEL (default nomic-embed-text), graceful lexical fallback. Life-wide search → done.
  • Self-repair auto-rollback — self_repair tool (tools/self-repair.ts): mark (record HEAD as a compartment's lkg) + rollback (kernel-assessed + approval-gated git checkout <lkg> -- <paths>, refuses protected brainstem/skeleton + unscoped limbs, warns on discarded changes) + status. 73 tools.
  • Teams live-spawn — team run action: actually spawns a worker for a dispatched task via spawnSubagent (child registry excludes delegate+team → no recursive fan-out; every worker tool call kernel-gated, same model as delegate), advancing the task running→done/blocked. Background teams → done.
  • Self-repair compartments complete. self/tool-sandbox.ts plans bounded pre-attach tests for vanta-ts/src/tools/*.ts; self_repair sandbox_test forces VANTA_SANDBOX=1 through the shared sandbox wrapper before a new/replaced limb tool is attached; repeated tool failures now surface a Repair loop prompt with /compartments + self_repair sandbox_test.
  • 8/8 operator rocks complete. Remaining horizon: browser OS-level control (needs a desktop driver). Full map: docs/research/rocks-completion-ledger.md.

Reach layer (Agent-Reach pattern, MIT — docs/reach.md + docs/research/agent-reach-eval.md). Vanta's internet-reach capability layer: a channel = ordered, real-probed backends + a doctor. src/reach/: channel.ts (ReachChannel contract + orderedBackends env override), probe.ts (really-executes, not which()), registry.ts (resolveChannel/checkAll), doctor.ts (/reach report), cookie.ts (shared 0600 cookie store for login-walled channels + parseCookieInput for Cookie-Editor JSON/header), channels/{web,search,rss,reddit,bilibili,xueqiu,xiaohongshu}.ts. Tools: rss_read (dependency-free RSS/Atom via reach/rss-parse.ts), cookie_import (kernel-gated credential store, never echoes), reddit_read (search/read via Reddit .json + cookie, reach/reddit-parse.ts), bilibili_read (bili-cli/OpenCLI/public search API), xueqiu_read (cookie-backed quote/search/hot feeds), xiaohongshu_read (OpenCLI-backed search/note/comments/feed; doctor detects xiaohongshu-mcp + mcporter route). Commands: /reach (doctor), /cookie (export guide). Source now reports 133 built-in tools. China reach substrate complete for the currently tracked REACH-* cards.

VANTA-INIT-CMD + lifecycle/session flags. /init [--force|--print] generates .claude/CLAUDE.md for the current project. --init runs Setup hooks before a session; --init-only runs Setup + SessionStart and exits 0; --maintenance adds maintenance context for Setup hooks. --fork-session with resume creates a new seeded session while leaving the original intact. roadmap.json marks VANTA-INIT-CMD, VANTA-INIT-FLAGS, and VANTA-FORK-SESSION shipped. Current command count is 97 slash commands.

VANTA-EFFORT. Effort levels are low|medium|high|max, set by CLI --effort, settings.effortLevel, VANTA_EFFORT_LEVEL, or live /effort <level>. OpenAI o-series receives reasoning_effort for low/high/max; Anthropic Claude thinking-capable models use extended thinking for high/max (8000/32000 budget tokens), while low forces max_tokens=4096.

  • TUI-V2 shell: VANTA_TUI=v2 now selects src/ui/v2/app-v2.tsx, a separate mission-control frame (MissionControlFrame) with durable-state, center transcript/engine, safety/working-memory/telemetry, and command-risk labels. Default and unknown values stay on v1. TUI-V2-RAILS remains open for making every rail value live from session state.
  • Test isolation fix: agent.test.ts now isolates VANTA_HOME to a temp dir — dispatch reads loadRules(process.env) from ~/.vanta/permissions.tsv, so a real allow shell_cmd rule (the "always allow" feature, working as designed) was auto-approving the action the ask-risk integration test expects to prompt on. Pre-existing flake; full suite now deterministic. Full suite: 345 files / 2998 tests green; typecheck clean; size gate clean.

Session additions (2026-06-13) — keep current

TUI full rebuild (Claude method) + command/persistence work. The vendored the forked ink renderer fork never had a <Static> layer (root cause of the ghosting — see [[vanta-tui-rootcause]] in memory); rebuilt the whole interactive surface on real Ink 7 the Claude way: inline render + React <Static> committed scrollback, so the terminal owns history (native selection/scroll/copy, zero ghosting) — no AlternateScreen, ScrollBox, mouse-capture, or virtual-history.

  • New render layer: src/ui/ (app, reducer, transcript, composer, banner/wordmark, overlays, status-bar, markdown, theme context, stream-view, todo-panel, busy, slash/at/use-* hooks). Shared pure helpers moved to a neutral src/term/ (theme, figures, composer-edits, tool-display, tool-result, at-context, capabilities, notify, model-switch, tokens). Deleted: vendor/the forked ink renderer (the fork), the old src/tui/ render layer, src/types/ink.d.ts; the inkr alias is gone (ink = real Ink 7). ONE survivor under the old path: src/tui/mission-control/cockpit-data.ts (data, not render). Default surface — the VANTA_UI2 gate is removed (resume/--no-tui/no-TTY still use the readline REPL).
  • Look (matches modern agent CLIs, source-verified via the terminal-love MCP): borderless VANTA block-wordmark title screen, markdown + GFM tables, grouped tool-call headers (⏺ verbs · N actions · ~Nk tok) over dim per-tool rows with inline diffs, context gauge (48k/200k [██░░] 24%), bordered approval prompt, rotating busy verb + elapsed, responsive (segment-dropping) status bar + prefix-hint line, light/dark theme auto-detect (COLORFGBG), live todo panel. Composer parity: readline chords, history, multiline, queue-while-busy, undo/redo, ^G $EDITOR, image-paste, Esc-interrupt.
  • Architecture-bounded (NOT built, by design): per-section expand/collapse, full-thinking expand, turn-backtrack visual retract, live spawn-tree — all need retroactive repaint of committed rows, which <Static> forbids (the whole point). Auto-approve / "yolo" modes skipped (bypass the kernel = rule zero).
  • Commands → 75: added /setup (opens the provider/model picker) + 8 one-shot CLI verbs surfaced via a subprocess bridge (repl/cli-bridge.ts): /config /settings /models /lint /roadmap /audit /today /import (daemons/interactive verbs stay CLI-only).
  • Persistence: resume (auto-handoff + session-memory) is age-gated — only carried into a restart if recent (VANTA_RESUME_MAX_AGE_MIN, default 120; 0 = always clean), fixing "stuck on an old thread after restart"; Ralph-loop continuity is durable project state at .vanta/ralph-loop.json, surfaced PAUSED until /goal resume or /goal drop; the TUI todo panel is session-scoped (reflects only the agent's todo-tool writes this session, never a stale global plan). Standing goals still persist (/goal clear).
  • terminal-love MCP wired into Vanta via repo .mcp.json (12 Terminal Trove design-reference tools mount as kernel-gated mcp_terminal-love_* tools); also shipped its own v1 (stars enrichment, richer screenshots, search TTL, MCP resources).

Session additions (2026-06-12) — keep current

TUI build plan — Phases 1–4 (the demo aesthetic + the Opus-tier surfaces). Target = docs/design-refs/tui-demo.html; plan + status = ``.

  • Design-token + glyph layer (P1): term/figures.ts (named glyphs ⏺ ❯ ✻ ✔ ✘ ▶ + ASTERISK_FRAMES + verbs). ui/theme.tsx semantic tokens (success/error/warning/info/marker/userMarker; primary = text/white, identity in accent/border/marker). Glyphs wired through ui/transcript.tsx (⏺ assistant marker, ❯ user, ✻ boundary), ui/status-bar.tsx (● ready), spinners (asterisk cycle).
  • Interactive footer (P2): ui/status-bar.tsx — one dim line above the composer: ○/◐/● permission mode · model · "? for shortcuts".
  • Keybinding registry (P3): ui/shortcuts.ts + ui/composer-keys.ts — chord parse/match/format (ctrl+o→^O, shift+tab→⇧⇥), DEFAULT_BINDINGS grouped by KeyContext (global/transcript/composer/palette/at-palette/modal/tabs), useKeybinding(action, handler, {isActive}) + cached merged chord map, optional ~/.vanta/keybindings.json overrides (zod-validated, fail-soft). ui/help-panel.tsx renders from the registry. (Composer emacs/readline + history edge-fall-through already existed.) Not done vs the stricter VANTA-KEYBINDING-* roadmap cards: no hot-reload watcher, no /doctor report, no conflict warnings.
  • Layout & mission-control (P4, opt-in): ui/app.tsx <Static> + live bottom rows. ui/cockpit-panel.tsx — tabbed Kernel verdict ladder / live Goals / live Loops (gatherCockpitData in tui/mission-control/cockpit-data.ts: getGoals + listDefs/loadState/openEscalations, best-effort), opened via /cockpit, switched by the tabs keybinding context. ui/transcript.tsx toolGroupRole brackets consecutive tool calls into one ┌│└ block. ⇧⇥ approval-cycle suppressed while an overlay is open so tabs owns ⇧⇥. Note: this is a lighter opt-in overlay, NOT the TUI-V2 card's always-on 3-column rail surface (that card stays open).
  • Env: documented VANTA_THEME / VANTA_SPINNER / VANTA_VIM + ~/.vanta/keybindings.json in .env.example.

Session additions (2026-06-11) — keep current

v0.2.0. Four Harness rocks + a full size-gate cleanup. All TS now passes file ≤300 / fn ≤50 / params ≤4 / cx ≤10 — no exemptions (factory/* brought into compliance 2026-06-27: run.ts/planner.ts/verifier.ts refactored behavior-preserving, the is_protected_path kernel-mirror kept byte-identical).

  • Session memory (memory/session-memory.ts) — a forked distiller maintains .vanta/session-memory.md during the session (periodic / busy turns); re-injected interior on compaction (context.ts) and into the system prompt on resume (prepareRun). Conversation.setSessionMemory refreshes the live injection. Env: VANTA_SESSION_MEMORY[_EVERY|_MIN_TOOLS]. Post-turn wiring lives in session/after-turn.ts.
  • Streaming tool execution (agent/stream-dispatch.ts) — providers emit a tool_call stream chunk when a block completes; the loop prefetches concurrency-safe reads (CONCURRENCY_SAFE_TOOLS) mid-stream and reuses the in-flight result. openai emits via the pure completedToolCalls fold. done stays the source of truth (backward-compatible).
  • MessageDisplay hook (agent/message-display.ts) — plugins/hooks.ts gains a message_display event (rewrite/suppress) fired before render; raw text stays in the transcript. Strip-<thinking> builtin, opt-in VANTA_STRIP_THINKING. The TUI commit now prefers the (transformed) finalText.
  • Hook engine (hooks/) — .vanta/hooks.json accepts the 30-event hook vocabulary and fires hooks on lifecycle, prompt/display, tool, permission, compaction, config, worktree, fleet, subagent, file watcher, cwd-change, MCP notification/elicitation, StopFailure, and TeammateIdle paths. Types: command/shell, http, mcp_tool, prompt, agent; JSON context on stdin/body/prompt, regex matchers for tool/prompt/file events, plus maintenance and sessionType filters. Agent hooks use injected AgentDeps on live tool/prompt/stop/session paths; early lifecycle hooks are providerless. paper-events.ts verifies Vanta covers the arXiv 27-event hook taxonomy and names the three extra Vanta events. Distinct from the in-process plugins/hooks.ts JS bus.

Decomposition (size grind): new modules from splitting oversized files — session/after-turn.ts, agent/{agent-types,plan-gate,dispatch-tool}.ts, interactive-turn.ts, cli/{extra-cmds,roadmap-cmd}.ts, plus context.ts splitForCompaction. Behavior-preserving extraction + re-export; full suite green throughout. Parallel-subagent waves + central full-suite verification did the bulk.

Cohesive brain (BRAIN-COHESIVE): the brain is one unit — brain/brain.ts facade composing the md regions with a structured entries layer (brain/entries.ts, rebuilt from the dormant brain5d + neuro modules, now deleted). brainDigest (session.ts) injects regions + top scored memories in one digest; the brain tool gains remember/recall actions (recall reinforces → crystallization). Always-on; per-layer best-effort; corrupt store quarantined; legacy brain5d.json migrated. brain/v2.ts (self-evolving substrate) parked — see PARKED.md + DECISIONS 2026-06-11.

Turn-abort hardening (post-incident, see ERRORS.md 2026-06-11): sanitizeMessages now also synthesizes stub results for DANGLING assistant tool calls (a turn aborted mid-dispatch used to brick the session — every later call 400s on the missing tool output; saved sessions resumed the brick). applySafetyGate fails CLOSED but gracefully when the kernel is unreachable (blocked tool result + "restart vanta", instead of throwing through the loop); approval-queue bookkeeping is best-effort. TUI scroll: pgup/pgdn (fn+↑/↓ on Mac) and ⇧↑/⇧↓ fine-scroll through inline <Static> scrollback. VANTA_MOUSE=1 enables mouse-wheel tracking (off by default — mouse tracking blocks native text selection; ⇧↑/↓ + pgup/pgdn scroll without it). Composer already supports ⌥⌫ (kill word) and ^U (kill to start); a literal cmd+⌫ never reaches terminal apps unless the terminal maps it (e.g. iTerm: cmd+⌫ → send hex 0x15).

Comment convention: code comments + commit messages stay Vanta-native — no another agent / CC- / the reference agent provenance (the real claude-code provider integration + roadmap card-ids are kept).

Session additions (2026-06-02/03) — keep current

Providers: VANTA_PROVIDER now also: gemini · openrouter · codex (alias openai-codex, ChatGPT-subscription OAuth via ~/.codex/auth.json, Responses API — providers/codex.ts+codex-auth.ts) · claude-code (Claude sub OAuth). Catalog: providers/catalog.ts.

Multimodal: user Message carries optional images:[{mime,dataBase64}] (types.ts), mapped natively by every provider. Attach via /image <path>, /paste (clipboard, macOS), drag-drop (maybeDroppedImage), or /attachments [clear]. Vision tools describe_image + look_at_screen/look_at_camera (Vanta's eyes) route through resolveVisionProvider (routing/vision.ts): a dedicated vision model via VANTA_VISION_MODEL (+ optional VANTA_VISION_PROVIDER) when set, else the active provider. This is the auxiliary-task pattern — image work runs on a vision-capable model even when the main model is text-only (DeepSeek, local Ollama). Unset = active provider (prior behavior).

Brain (selfhood): brain/regions.ts + brain/store.ts → ~/.vanta/brain/<region>.md (identity[neurodivergent-first]/semantic/episodic/user_model/drives[frugality]/reflections/mood), git-versioned. brain tool (list/read/append/replace). brainDigest injected as a prompt tier — Vanta reads its brain each session.

Skills: skill INDEX injected into the prompt (prompt.ts skillsTier); recall loads the full body on demand.

Memory: stored file capped per goal (VANTA_MEMORY_MAX_BLOCKS, default 50; older blocks git-retained).

Delegate: provider/model params → agent routes a subtask to any backend (e.g. local ollama). delegateEnv overlays the choice.

Kernel safety (src/safety.rs): hardened — normalize_cmd (strips quote/backslash escapes), broadened destructive set, arbitrary-exec vectors (interpreters/eval/pipe/egress) → ASK, absolute-path-outside-root → ASK. Closes bypassable-denylist holes identified in prior-agent audit.

Phase 2 EF gates (2026-06-04): All gates are best-effort, non-blocking, wrapped in try/catch. Post-turn gates (inhibit, set-shift) live as session-scoped state refs in interactive.ts + ui/use-agent.ts, mirroring the researchGateRef pattern. Pre-turn gate (closure-gate) fires inside runUserTurn / sendToAgent alongside complexity-gate and topic-shift check. In-loop gates (self-monitor, error-detect) live in agent.ts dispatchTool / runTurn. New env overrides: VANTA_INHIBIT_THRESHOLD · VANTA_SETSHIFT_THRESHOLD. handlers.ts MUST STAY at 300 lines — new slash handlers go in own files, trade a blank line for the import.

TUI/REPL commands: /history /retry /undo /reset /title /fork /goal /usage /copy /update /image /paste /attachments /next /planmode /where /wm /moim /restart /bug /handoff /open (+ prior model/tools/skills/status/goals/sessions/resume/cron). Composer = custom readline (ui/composer.tsx, Ctrl+U/W/Esc-abort, up/down history, shift+enter multiline). @file autocomplete in TUI → inlines file content as context on submit. Markdown rendering in transcript. Domain-grouped banner (term/capabilities.ts). Braille spinners (VANTA_SPINNER). Drag-and-drop roadmap board: vanta roadmap serve → http://localhost:7789/roadmap/board.

Env added: VANTA_MEMORY_MAX_BLOCKS · VANTA_SPINNER (orbit|dots|pulse|snake|wave). Prompt rule 8 = token/power frugality (prefer local ollama for simple work).

Docs: MANIFESTO.md (north star) · docs/issues-map.md · ROADMAP.md v1.1–v1.5 (parity + UX + autonomy/senses + selfhood + efficiency).

Session additions (2026-06-05) — keep current

AUX-VISION (auxiliary-task vision routing): routing/vision.ts — visionEnv (pure) + resolveVisionProvider. All three image tools (describe_image, look_at_screen, look_at_camera) now resolve via it: a dedicated vision model from VANTA_VISION_MODEL (+ optional VANTA_VISION_PROVIDER) when set, else the active provider (prior behavior). Fixes vision silently breaking when the main model is text-only (DeepSeek V4 Flash, local Ollama). The removed VANTA_VISION_MODEL is back, with new opt-in aux-routing semantics. Next: AUX-MAP generalizes this to a per-function model map (vision · summarize · title · embed).

TUI readability fixes: terminal width fills (no col cap in ui/app.tsx) · slash palette capped to 8 matches (was unbounded → typing / dumped all 37) · palette rewritten to a fixed command column + width-clipped one-line descriptions (ui/slash-palette.tsx clip) instead of ragged space-between · /skills output aligns names + clips descriptions to one line (repl/handlers.ts) · skill INDEX in the system prompt clips each description to ~100 chars (prompt.ts trimSkillDesc) so weak models stop parroting the whole library.

Env added: VANTA_VISION_MODEL · VANTA_VISION_PROVIDER (auxiliary vision routing; documented in .env.example).

Messaging setup wizard (MSG-WIZARD + MSG-REGISTRY): gateway/platforms/registry.ts — MESSAGING_CATALOG (the messaging analogue of providers/catalog.ts): {id,label,implemented,requiredEnv,secretEnv?,enableEnv?,prerequisite?,warning?,setupSteps,signupUrl?} + platformAvailability(p,env) + messagingPlatformById. Only Telegram is implemented (live adapter); iMessage/Signal/WhatsApp are planned (preview-only — the wizard never writes a fake enable flag for a missing adapter). setup-messaging.ts — vanta setup messaging: registry-driven menu with [available|configured|planned] status, renderSetupSteps (prereq + ⚠ warning + numbered steps), configures Telegram for real (VANTA_TELEGRAM_TOKEN via the shared upsertEnv, exported promptSecret from setup.ts). Wired in cli.ts (setup → messaging subcommand). Future adapters (iMessage osascript+chat.db, Signal signal-cli, WhatsApp Node bridge) flip implemented:true + add their adapter. Design: docs/messaging-gateways.md.

Multi-source skill install: skills/library.ts installs from librarySources() = three bundled dirs: vanta-ts/skills-library/ (nd-* + ported references), the repo-root design-system-skills/ (27 design skills + viewer), and ai-engineering-skills/ (13 production-LLM/agent-engineering skills + viewer). The extra libraries stay in their showcase folders (not duplicated) and auto-install into ~/.vanta/skills each session via installSkillLibrary(). installOne() helper keeps the fn small; from still overrides to a single source (tests). Adding a skill source = one entry in librarySources(). Each library's index.html regenerates via scripts/build-skills-index.py <folder> (mistune). Correction: installSkillLibrary writes skill files to ~/.vanta/skills ONLY — there is no ~/.claude/skills copy in the code (older notes claiming "also installed into ~/.claude/skills" are inaccurate). The ~/.claude integration is SKILL-TRIGGERS event hooks (opt-in via vanta skills sync-triggers --claude), not a skills copy.

Session additions (2026-06-07) — keep current

Autonomous roadmap-grind session: 20 cards shipped (statuses + per-card notes in roadmap.json). 1216 TS + 27 Rust tests green; tsc clean; 46 registered tools. New files are in the file-map table above. Highlights:

  • Code-size discipline: CODE-SIZE-GATE (lint/size.ts analyzeSource via TS compiler API: file≤300/fn≤50/params≤4/cx≤10) → vanta lint [--staged] + warn-only pre-commit (VANTA_LINT_BLOCK=1 enforces) + in-agent: write_file runs the gate on every TS write and surfaces violations in the result so the agent born-small/self-corrects. Surfaced 85 pre-existing violations (debt for a paydown card). VANTA-EDITOR (editor/open.ts + vanta open + /open).
  • (earlier in the session):
  • Continuity: repl/handoff-cmd.ts (/handoff + shared assembleHandoff) + repl/auto-handoff.ts (AUTO-HANDOFF: writes .vanta/handoff.md at context fill ≥ threshold, prepareRun reloads + consumes it on interactive launches only — gated on instruction === "interactive session").
  • Operator: GOAL-ACTION (vague goal auto-fires /next via isVagueGoal+buildNextStepResend), STALL-UNBLOCK (repl/stall.ts post-turn gate), MODE-DETECT (repl/mode-detect.ts auto stance, prepends a hint to the SENT message), /restart (repl/restart-cmd.ts exit 75 + run.sh relaunch loop), /bug (repl/bug-cmd.ts).
  • Reliability: TOOL-RETRY (tool-retry.ts, safe retry of idempotent reads in agent.ts dispatchTool), ACTION-PROOF (write_file re-reads + verifies post-write), COST-VISIBLE (pricing.ts per-turn footer + session split on ReplState.sessionCost).
  • Model fix: UX-MODEL-FIX — upsertEnvMigratingLegacy (setup.ts) strips the stale ARGO_* twin on any model write (root cause of "stuck on codex"); /model <arg> now hot-swaps + persists (repl/model-cmd.ts).
  • Prompt rules: rule 10 = Voice (BEHAVIOR-VOICE); rules 1/4/7 folded in REF-FIDELITY / VERIFY-RIGHT + BETTER-ENDINGS / TRUST-LABELS (no new rule numbers — folding avoids prompt bloat).
  • Tooling: roadmap_add tool + roadmap/add.ts.

Env added: VANTA_TOOL_RETRIES · VANTA_STALL_THRESHOLD · VANTA_MODE_DETECT · VANTA_AUTOHANDOFF / VANTA_AUTOHANDOFF_THRESHOLD · VANTA_GOAL_ACTION · VANTA_RELAUNCH (set by run.sh) · VANTA_LINT_BLOCK (pre-commit size enforce) · VANTA_EDITOR (VANTA-EDITOR). All documented in .env.example.

Gotcha: run git from the repo ROOT — the tsx/test commands cd into vanta-ts/ and the shell cwd persists. roadmap.html is gitignored (regenerate via roadmap/build.ts buildRoadmap).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.