Supervisor

The supervisor is an out-of-band control plane that runs beside the agent loop — never in your transcript. It watches each turn, keeps the agent on…

It exists to make the loop more precise: fewer side-tracks, fewer "looks done but isn't" finishes, and no re-discovering what a past session already figured out.

The closed loop

text
every turn, FREE:
  self-report  ⊕  detectors (counters)      <- two free signals, fused
        │  agree → act with no model
        │  conflict / `done` → ↓
  verify-gate (model, rare)  → labels the run pass/fail
        │
  distill (on pass)  → lessons + orientation written to memory
        │
  recall (next turn/session)  → inject lessons + orientation
        │
  steer  → advisory re-anchor when the agent loops or stalls

The verify-gate is the reward signal: it labels a run pass/fail, so the supervisor only learns from work it has evidence was correct. Everything injected is advisory — a note the agent reads, never a silent rewrite of its context.

Self-report

When [supervisor.detectors] self_report = true, the agent is instructed to end every turn with a one-word status token:

text
<sup>STATE</sup>          # optionally: <sup>STATE · short reason</sup>

STATE is one of exploring, progressing, blocked, need_input, done. The token is parsed by the supervisor and stripped before display — you never see it. It is the cheapest, most reliable signal of intent, because the agent already knows whether it is stuck, asking you a question, or finished.

StateEffect
doneArms the verify-gate
need_inputTreated as a question — passed to you, never gated (no false-positive verification)
blockedTriggers a steer note
exploring / progressingFused with the counters below

Detectors

Deterministic, free, every turn — they cost nothing and decide when (rarely) to spend a model call.

Both derive from one primitive — information novelty: did the action add new information? A mutation (edit/write) always advances state; a read/search advances only when its result is one not seen recently.

  • Loop — the same result repeats loop_threshold times in a row (default 3). Keyed on the result, so reworded calls that return the same thing are caught too. Unambiguous; no model needed.
  • No-progressno_progress_window actions (default 5) with zero novelty — churn, not genuine work.

The power is in fusing the counter with the self-report: if the counter says "no progress" but the agent reports progressing, that conflict is the real stuck signal. The full fusion table: any done defers to the gate; no-progress while exploring waits; loop, or no-progress otherwise, steers. Agreement needs no model at all.

Verify-gate

When the agent self-reports done and [supervisor.gate] enabled = true, an independent pass checks the result against your request before completion is accepted:

  • Pass → the run is labelled verified; distill is allowed to learn from it.
  • Gaps → an advisory listing the gaps is injected and the turn re-runs, bounded by max_iterations (default 2, to avoid over-verifying). If gaps remain after the bound, the run is marked unverified and distill is suppressed — we never learn from an unverified trajectory.

Steer

When a detector fires (loop, or no-progress that the self-report doesn't excuse), the supervisor queues an advisory re-anchor note — "you've repeated this without new results; try a different approach, or report blocked" — injected at the next request's safe point. It nudges; it never forces.

Condense

When a tool round returns oversized results (over [supervisor.condense] tokens_threshold), one cheap-model call decides per result what the agent actually needs to see for the current task:

  • All relevant → kept in full, byte-for-byte.
  • Partly relevant → only the needed lines. The condenser sees a line-numbered copy and answers with line ranges; the kept lines are reconstructed verbatim from the original — the model never retypes content, so nothing can be mis-copied.
  • Irrelevant → replaced with a short note saying what the output was and why it doesn't help.

It is lossless: the full original is spilled to a session file first, and every condensed result carries the path, so the agent can read any cut span on demand. The hard mcp_response_tokens_threshold prefix-cut still applies afterwards as the ceiling. Fail-open: any condenser error leaves results untouched. Main session only (layers/agents are not condensed).

Relevance is conditioned on the running agent, not just the current request: the first condense call of a session includes the head of the agent's system prompt and distills a short agent profile ("what this agent is for, what output it must never lose"), which is cached for the session (re-distilled automatically if the system prompt changes) and prepended to every later call. Internal mechanic — no config.

Memory: lessons + orientation

The supervisor keeps two kinds of cross-session memory in one backend:

  • Lessons — procedural do / avoid rules, extracted from your corrections. The deep dive lives in Cross-Session Learning.
  • Orientation — durable, descriptive understanding of the subject (architecture, decisions, constraints) that was expensive to discover and would otherwise be re-explored. Stored under memory_type = "orientation" and recalled as working assumptions to verify, never as truth.

The rule for what to store: cache what is expensive to re-derive, never what one search recovers. A symbol's location is cheap (grep finds it); an architectural decision is not.

Self-correcting (the closed loop). Recall is wired back to the verify-gate's verdict: entries that were in context when a run passes get reinforced (importance up); entries present when a run fails after retries are decayed (importance down) and dropped once they fall below a floor. A distill-time pass additionally prunes entries that have gone both stale (older than decay_days) and weak. So memory is validated by outcome, not by assertion — useful knowledge strengthens, misleading or unused knowledge fades out.

Configuration

The supervisor is configured under [supervisor]. It is strict: a missing [supervisor] section — or any required key within it — is a hard parse error. We own the schema, so we fail loudly instead of degrading to silent defaults.

toml
[supervisor]
enabled = true
model   = "anthropic:claude-haiku-4-5"   # shared cheap model for gate/reflection

[supervisor.learning]      # procedural lessons — see 13-learning.md
enabled = true
backend = "file"
max_inject = 5

[supervisor.orientation]   # durable subject understanding
enabled = true
max_inject = 5
decay_days = 90

[supervisor.detectors]     # deterministic, free, every turn
loop_threshold = 3
no_progress_window = 5
self_report = true

[supervisor.gate]          # verify on self-reported `done`
enabled = true
max_iterations = 2

[supervisor.condense]      # task-aware narrowing of oversized tool outputs
enabled = true
tokens_threshold = 2000
model = "anthropic:claude-haiku-4-5"

Every field is documented in [supervisor] — Config Reference.

Invariants

  1. Free signals gate the model. Counters and the self-report run every turn at zero cost; the model (verify-gate / drift confirm) is woken only on a done or a conflict.
  2. Advisory, never silent rewrite. Every injection is a note the agent can reason about. A wrong supervisor degrades gracefully instead of corrupting the run.
  3. Out-of-band. Status tokens are stripped from display; supervisor deliberation never reaches your transcript.

Mechanics at a glance

MechanicWhenCostConfig
Self-reportEvery turnFree[supervisor.detectors] self_report
Detectors (loop / no-progress)Every turnFree[supervisor.detectors]
Verify-gateOn self-reported doneModel (rare)[supervisor.gate]
CondenseOn oversized tool resultsModel (cheap)[supervisor.condense]
SteerOn loop / no-progressFree[supervisor.detectors]
Distill (learn)End of a verified runModel (cheap)[supervisor.learning], [supervisor.orientation]
RecallSession start + per turnEmbedding[supervisor.learning], [supervisor.orientation]