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
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 stallsThe 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:
<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.
| State | Effect |
|---|---|
done | Arms the verify-gate |
need_input | Treated as a question — passed to you, never gated (no false-positive verification) |
blocked | Triggers a steer note |
exploring / progressing | Fused 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_thresholdtimes in a row (default3). Keyed on the result, so reworded calls that return the same thing are caught too. Unambiguous; no model needed. - No-progress —
no_progress_windowactions (default5) 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(default2, 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.
[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
- 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
doneor a conflict. - Advisory, never silent rewrite. Every injection is a note the agent can reason about. A wrong supervisor degrades gracefully instead of corrupting the run.
- Out-of-band. Status tokens are stripped from display; supervisor deliberation never reaches your transcript.
Mechanics at a glance
| Mechanic | When | Cost | Config |
|---|---|---|---|
| Self-report | Every turn | Free | [supervisor.detectors] self_report |
| Detectors (loop / no-progress) | Every turn | Free | [supervisor.detectors] |
| Verify-gate | On self-reported done | Model (rare) | [supervisor.gate] |
| Condense | On oversized tool results | Model (cheap) | [supervisor.condense] |
| Steer | On loop / no-progress | Free | [supervisor.detectors] |
| Distill (learn) | End of a verified run | Model (cheap) | [supervisor.learning], [supervisor.orientation] |
| Recall | Session start + per turn | Embedding | [supervisor.learning], [supervisor.orientation] |