Limits
Limit options are session-level controls for long-running work, noisy tools, and provider failures.
Session Options
Option Intent
maxToolRoundsis the tool-iteration budget for a turn.maxParseRetriesis the malformed tool-call recovery budget.toolTimeoutMsis the per-tool timeout in milliseconds.circuitBreakerThresholdis the repeated provider failure threshold.autoCompactandautoCompactThresholdenable context compaction behavior.continuationEnabledandmaxContinuationTurnscontrol continuation injection.
Practical Defaults
Use strict limits for CI, release, and user-facing automation. Use larger budgets for exploratory local coding sessions, but keep verification commands explicit and required when the task has side effects.
Retention Limits
A session keeps its run history, trace events, and subagent task snapshots in
memory. For short runs that is fine; for a session that lives for hours or days
under a cluster workload those in-memory stores grow without bound.
SessionRetentionLimits (CHANGELOG [3.3.0] "SessionRetentionLimits") adds
optional FIFO caps so they don't leak. The default is unbounded — setting no
cap changes no behavior.
Four independent caps; cap any subset, leave the rest unbounded:
All caps are soft: enforcement drops the oldest entry on insert and never returns an error.
Budget Guard
BudgetGuard (CHANGELOG [3.3.0] "BudgetGuard") is a host-supplied cost / quota
contract. The framework does not enforce budgets itself — it defines the
decision points and consults a guard the host plugs in. Three hooks are wired at
the LLM / tool call site:
check_before_llm— before each LLM call.record_after_llm— after each successful LLM call, with the actual provider usage, so the host keeps its running spend total accurate.check_before_tool— before each tool call.
Each check_* returns one of three decisions:
Allow— proceed normally, no event.SoftLimit { resource, consumed, limit, message }— emits anAgentEvent::BudgetThresholdHit { kind: "soft" }and proceeds. In-session hooks can react (auto-compact, swap to a cheaper model next turn).Deny { resource, reason }— aborts the call withCodeError::BudgetExhausted. The session stays open — the caller can retry later or after the host re-allocates budget.
Node — session.setBudgetGuard({...})
Each callback takes a single ctx object (not positional arguments) and
returns a decision dict (or null / { decision: 'allow' } to allow):
The Node bridge fails closed: a check_* callback that does not return
within timeoutMs, or returns something unreadable, is treated as a deny —
a budget control must never silently disable itself when the guard stalls
(CHANGELOG [3.3.0] Fixed "Node BudgetGuard fail-open").
The callback MUST NOT throw. Due to a napi-rs constraint a thrown exception aborts the host process at return-value conversion. Wrap your logic in try/catch and return a decision (e.g. a deny) instead of throwing. Hangs are handled safely by the fail-closed timeout (CHANGELOG [3.3.0] Known limitations).
Python — budget_guard session option
Python supplies a BudgetGuard-shaped object on the budget_guard
SessionOptions field. Methods that aren't defined behave as Allow / no-op.
Python callbacks use positional arguments and the framework catches any
exception they raise (a check_* that raises defaults to Allow):
The decision return dict {"decision": "deny", "resource": ..., "reason": ...}
(and "soft" / "allow") is the same shape on both SDKs.