concurrency (with replace, onReplace, and manages) caps and maintains a single live session; spendCaps bounds what sessions may spend; and model (with reasoningEffort) selects the model and its failover behavior. This page covers all three as implemented. For every other agent field, see Agent file.
Concurrency
integer
Cap on live sessions for this agent. Only
1 is accepted — the schema rejects anything else with “Only concurrency: 1 is supported; larger pools are a deliberate non-goal for now”. A concurrency-1 agent keeps at most one live session, called the slot member."auto"
Opts the agent into platform-managed slot replacement.
auto is the only value, and it is an assertion by you: the agent’s state is fully reconstructable from external sources (chat threads, session lists, bindings), so the platform may stop a stale or failed member and spawn a replacement on the latest spec. Requires concurrency to be set.string
The rebuild prompt delivered as a replacement session’s start message, trimmed, 1–20,000 characters. Supports
file: references and the append: directive like other prompt fields. Requires replace: auto (nothing else consumes it); a generic fallback prompt is used when undeclared.string[]
Agent-type names (max 64) whose sessions this agent may stop within its project. Authority belongs to the agent type, not to spawn provenance — a replacement session controls sessions its predecessor spawned. Self-control is always implicit, and unknown names are inert rather than invalid, so the managed agent can be applied or removed independently.
The slot at runtime
The slot is anagent.singleton session binding claimed inside every session-creation transaction. While a live member holds it:
- A trigger routed
kind: spawnconverts to a delivery to the live member instead of creating a second session. - A manual start (UI or
auto.sessions.spawn) is refused with a 409 naming the live session id. - Agent-addressed messages (
auto.sessions.messagewith an agent name) resolve to the live bound member, so peers keep reaching the agent across replacements without tracking session ids. - A
delivertrigger with norouteByresolves the slot member; this shape is only valid on agents withconcurrency: 1, or when a deliver-without-routeBytrigger on the agent declaresonUnmatched: spawn— that spawn claims the slot itself.
Slot replacement
A platform reconciler (5-second cadence) keepsreplace: auto agents healthy in three phases:
- Release. A member that archives itself leaves the pool at the end of its current turn; the slot frees within one sweep.
- Drain. When the applied agent spec drifts from the spec a live member was spawned with, the member receives one durable warning message: the definition changed, automatic replacement happens in five minutes, and it should checkpoint work and archive cleanly with a handoff — or stop itself with respawn intent to reload immediately. A member still live at the deadline is stopped with respawn intent. A spec that changes again mid-grace restarts the window against the new target; a spec that reverts cancels it.
- Replace. A member that failed (within a crash-loop budget of 3 failures per 30-minute sliding window) or was stopped with respawn intent gets a direct replacement: a slot-claiming spawn on the latest spec whose start message carries
onReplace. A plain manual stop without respawn intent is the operator opt-out — nothing respawns.
onReplace rebuild plus onUnmatched: spawn routing — during the brief window when no member is live, triggers routed onUnmatched: spawn (mentions, subscribed replies) spawn the successor carrying the event, while onUnmatched: drop deliveries (reactions, heartbeats) are dropped by design.
A production example, condensed from this repo’s chief-of-staff agent:
.auto/agents/chief-of-staff.yaml
Spend caps
numeric(20,10). YAML numbers, negatives, and exponent notation are rejected at authoring time — quote the value. Zero is valid and means a configured zero-dollar cap.
string
Agent-type cap on spend per UTC calendar day, summed across all of the agent’s sessions. Resolved from the active agent resource at enforcement time, so applying a spec change affects live sessions immediately.
string
Agent-type cap per UTC calendar month. Same resolution semantics as
dailyUsd.string
Lifetime cap for a single session. Copied into the session record at creation and immutable for that session — later spec changes apply only to new sessions.
Enforcement
When any cap is at or over its limit (an effective hit), the platform stops new spend without killing anything:- Session creation is refused while an agent daily/monthly cap (or a requester cap) is hit, with a message pointing at the admin override path.
- Turn-starting commands —
start,startWithMessage,message,answer— on existing sessions are deferred rather than dropped: the command stays pending and is retried on a one-minute backoff until the cap clears.stopandinterruptare never deferred, so a capped session can always be terminated. - Idle capped sessions are parked with reason
billing_spend_cap_exceededand a resume condition tied to the cap: they wake automatically when the window resets or an override lands. A session mid-turn finishes its turn first — enforcement decisions happen between turns.
Spend caps bound model usage recorded to the session. They are a budget-control mechanism, not a security boundary: a capped agent’s session keeps its sandbox and pending state, and resumes exactly where it deferred once the window resets.
Model selection
object
Optional. When omitted, the harness default applies:
claude-code runs anthropic/claude-opus-4-8; codex runs openai/gpt-5.6-sol. When present, id is required."anthropic" | "openai" | "openrouter"
Defaults to the harness’s default provider. Each harness accepts a fixed set:
claude-code → anthropic only; codex → openai or openrouter.string
required
The model to run. Curated per provider:
OpenRouter ids are deliberately uncurated — the catalog is long-tail and changes independently of auto releases; the live service verifies existence against OpenRouter’s
/models.string
A top-level agent field (sibling of
model, not nested in it). Allowed values and defaults are harness-scoped: claude-code accepts low | medium | high | xhigh | max (default high); codex accepts minimal | low | medium | high | xhigh (default medium). Some OpenRouter models narrow the set further (for example z-ai/glm-5.2 accepts only high | xhigh); an effort the resolved model does not support fails validation at apply time.model change affects new sessions only, and a session whose model was explicitly overridden at start has no fallback chain.
Fallbacks
Two mutually exclusive, opt-in fallback surfaces live onmodel. Both exist so a transient provider overload does not fail a turn; both are inert when absent.
array
Same-provider fallback, for any harness. An ordered chain of 1–2 entries, each Validation enforces: every entry resolves under the same harness rules as the primary; resolves to the same provider as the primary; ids are distinct from the primary and each other; and the agent’s
{ id } or { provider, id }:reasoningEffort must be valid for every fallback (the gateway rewrites only the model, never the effort).At runtime, when a request naming the primary exhausts the gateway’s transient-retry budget (HTTP 429/529, roughly 20 seconds of cumulative backoff per model), the gateway rewrites the request to the next fallback and re-runs the loop. The first activation opens a 5-minute sticky window during which primary-named requests go straight to the fallback; after the window, the next request probes the primary again — fail-back is automatic, with no operator action. Requests for other models inside the session (subagents, title generation) pass through untouched.object
OpenRouter-native fallback, valid only when the resolved provider is
openrouter (codex agents today). Mutually exclusive with model.fallbacks.models(required): 1–3 OpenRouter slugs, distinct, excluding the primary id (it is prepended automatically). OpenRouter fails over across the chain natively.provider(optional) routing preferences:allowFallbacks(boolean),order(preferred upstream providers, in order),sort(price|throughput|latency),only/ignore(upstream provider allow/deny lists).
Observing a fallback
When a fallback serves a response, the platform is explicit about it: the response carries anx-auto-model-fallback: <id> header, the session’s conversation gets one status entry per activation (“Model fallback activated: running on fallback model … (primary … overloaded).”), and the auto.sessions.summary MCP tool reports a modelFallback object with the latest activation time and the fallback model. Usage is attributed to the model that actually served each request.
Related pages
- Agent file — the full agent field reference, including session policy (
session.archiveAfterInactive) - Triggers —
routing,routeBy, andonUnmatched, which concurrency semantics build on - Sessions — the session lifecycle these controls govern