Agent loop — conformance#
Required vs. optional — summary matrix#
| Behavior | Level | Where |
|---|---|---|
| Turn algorithm executes steps in the documented order | MUST | turn-algorithm.md |
max_turns bound, configurable | MUST | turn-algorithm.md |
max_cost_usd bound, configurable | SHOULD | turn-algorithm.md |
max_wall_clock_s bound, configurable | SHOULD | turn-algorithm.md |
| Weighted-token cost model (accounting refinement, not a fourth dimension) | MAY | turn-algorithm.md |
cost_usd computed via provider-declared pricing, accumulated per session | MUST | turn-algorithm.md |
| Cost rolls up the session tree (inherited, only-shrinking budget) | MUST | turn-algorithm.md |
| Graceful "final answer" turn on any bound firing, not a hard error | MUST | turn-algorithm.md |
| Soft-limit mode with user continuation | MAY | turn-algorithm.md |
| Implicit no-tool-calls done detection | MUST | turn-algorithm.md |
| Opt-in explicit terminal-tool done detection | MAY (per tool provider) | turn-algorithm.md |
| Kernel-owned doom-loop hash detector, threshold 3–5 | MUST | turn-algorithm.md |
| Secondary LLM-oracle doom-loop check on long sessions | SHOULD | turn-algorithm.md |
data_source calls execute concurrently within a turn (default, no declaration needed) | MUST | turn-algorithm.md |
resource calls execute sequentially by default (undeclared ConcurrencySpec) | MUST | turn-algorithm.md |
Concurrency governed by ConcurrencySpec, not kind directly | MUST | turn-algorithm.md |
| Ordered, declaration-order hook dispatch across all subscriber modes | MUST | hook-dispatch.md |
observe errors/timeouts never alter payload or abort chain | MUST | hook-dispatch.md |
transform errors/timeouts abort chain + surface hook_error | MUST | hook-dispatch.md |
veto errors/timeouts fail-closed to deny | MUST | hook-dispatch.md |
| Per-subscriber timeout enforcement | MUST | hook-dispatch.md |
Sequential dispatch for transform/veto within one hook point | MUST | hook-dispatch.md |
Concurrent dispatch among consecutive observe subscribers | MAY | hook-dispatch.md |
Third-party veto-mode hook subscription, via agent.hcl declaration | MAY | hook-dispatch.md |
Per-PlanItem (not per-plan) policy evaluation | MUST | plan-apply-gate.md |
Batched UI presentation of multiple ask items | MAY | plan-apply-gate.md |
deny synthesizes a tool_result denial block back to the model | MUST | plan-apply-gate.md |
| Plan mode via tool-schema removal, not runtime interception | MUST | plan-apply-gate.md |
Circuit breaker on repeated deny decisions | SHOULD | plan-apply-gate.md |
data_source/interactive policy precheck (allow/deny only, ask downgrades to deny) | MUST | plan-apply-gate.md |
interactive calls execute sequentially, never concurrently with each other | MUST | plan-apply-gate.md |
Fresh context per child RunSession (no history fork) | MUST (default) | subagents.md |
| Opt-in parent-history forking per profile | MAY | subagents.md |
Configurable max_concurrent_subagents cap | MUST | subagents.md |
| Parent turn joins on all spawned children before proceeding | MUST | subagents.md |
Child lookup via a session_meta.parent_session_id scan (no separate index) | MUST | subagents.md |
| Structural (schema-level) depth-cap enforcement | MUST | subagents.md |
| Default-deny recursion (child spawning further children) | MUST (default) | subagents.md |
| Cascading cancellation to in-flight children | MUST | subagents.md |
| Sibling-to-sibling communication channel | MUST NOT | subagents.md |
Exponential backoff + jitter, retry_after honored | MUST | error-recovery.md |
| Separate per-attempt vs. per-session retry caps | MUST | error-recovery.md |
No retry on context_length_exceeded, auth_error, invalid_request | MUST | error-recovery.md |
| Tool-provider crash surfaced as tool-result error, not session fault | SHOULD | error-recovery.md |
Open questions#
- Veto-hook timeout fail-closed default (
hook-dispatch.md). Chosen over a fail-open-with-explicit-instructions alternative because policy sits at the terminal mutation gate. Worth revisiting if fail-closed proves too disruptive to interactive UX in practice — there is no clear consensus among comparable systems here, only one adjacent precedent this design deliberately diverges from. - Tool-provider crash handling (
error-recovery.md) is reasoned by analogy to the denial-feedback pattern, not a pattern directly established for crashes specifically. Should be revisited if tool-result formatting and feedback conventions evolve to address crash handling directly. - Full context-compaction algorithm (trigger metric, mechanism, tail protection) is deliberately out of scope across this whole directory — "what's worth remembering" and context injection belong to memory/context providers (
memory/README.md,context/README.md), not the kernel loop. This directory's only compaction-adjacent obligation is the reaction to acontext_length_exceedederror (error-recovery.md#model-provider-errors).
Plan-editing granularity at the frontend is not an open question here: it's resolved by frontend/README.md's ClientEvent.plan_decision.corrected_input field, a corrected-input redirect re-validated against the tool's declared input schema before being honored — see plan-apply-gate.md#plan-construction-and-policy-evaluation.