The agent loop
The agent layer (vanta-ts/) orchestrates a model, a tool catalog, and a three-tier prompt. Every action it takes is gated through the kernel.
The loop
messages = [system, user]
each iteration (max VANTA_MAX_ITER):
trim/compress → provider.complete(messages, toolSchemas)
no tool calls + non-empty text → DONE
no tool calls + empty → nudge once
for each tool call → persist pending → dispatchTool:
describeForSafety(args) → kernel.assess()
block → tool result "blocked", no execution
ask → permission rules / auto-mode may tighten or auto-confirm;
otherwise prompt; record allow/deny in the kernel
allow → persist started → execute
append tool result + effect disposition; log the event
3 consecutive empty results → stop
Two-layer safety
Safety is enforced twice:
- Kernel
assess()— keyword + scope classification (the boundary). - Tool self-checks — path containment, overwrite approval via
ctx.requestApproval.
describeForSafety sends only the risk-relevant part of an action to assess() — the path or command, never file content — so content keywords can't false-trigger a block.
Tool results are values, not exceptions
Tools return { ok, output }. The loop never crashes on a tool error — a failure is just a result the model can read and react to. Idempotent reads are retried on transient failure (VANTA_TOOL_RETRIES).
Interrupted side effects
Every tool attempt carries an effect disposition:
none— execution did not start, or the interrupted tool is a known read.confirmed— tool code returned a result, whether that result was success or failure.unknown— an effect-capable tool started but was interrupted before returning.
Vanta checkpoints pending before dispatch and started immediately before tool code. On session restore, a dangling mutating call becomes a synthetic unknown result that tells the model to inspect current state before retrying. Unknown plugin and MCP tools are treated as effect-capable. Metadata-only receipts are written to <project>/.vanta/tool-effects.jsonl; arguments and outputs are excluded.
The three-tier prompt
The system prompt is layered so the stable part stays cacheable:
- Stable — identity (the SOUL), the tool catalog, the rules.
- Brain / skills — recalled memory + the skill index.
- Volatile — active goals, paused-carry continuity, current time, recent memory.
Goal awareness
Before any tool call the agent knows internally which active goal the action serves. A goal carried from a previous session launches paused — it won't silently resume until you /goal resume or reference it. Set or resume a goal and it becomes the session's working goal (shown in the footer).
Provider-agnostic
The loop only sees the LLMProvider interface (complete / modelId / contextWindow). Swapping OpenAI for a local Ollama model, or routing cheap vs expensive tasks to different models, never touches the loop. See Providers.
From loops to graphs
A loop decides what happens next inside one bounded worker. A graph coordinates several workers, tools, and people through typed shared state and conditional transitions. Vanta already supports declarative branches, bounded loops, parallel nodes, and approval gates; the remaining product work is durable state handoff, structured review/rework edges, evidence-based graph completion, bounded adaptive topology, and operator replay. See Graph engineering.