> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent run lifecycle: queued to completed, and the states in between

> How one agent run moves from queued through working and input_required to a terminal completed, failed, or cancelled — and what each error code means when a run stops early.

# Agent run lifecycle

Every AI agent processes work as a **run**: one inbound message handled end to end, one API turn you requested through `POST /api/v1/agents/:id/chat`, `invoke`, or `chat-stream`, or one task accepted from an A2A peer. Each run carries a `status` that advances as the runtime executes it, and it closes in exactly one of three terminal states. This page explains that state machine so you can build dashboards, retries, and approval flows against it.

This is the agent counterpart to [Voice call lifecycle](/concepts/voice-call-lifecycle) and [Delivery lifecycle](/concepts/delivery-lifecycle). The per-event payloads for streaming runs live in the [webhook events reference](/reference/webhook-events); this page stays at the concept level.

## What a run is

A run is the unit of execution between two turns of a conversation:

* **A conversation turn** — one inbound message arrives (chat, SMS, WhatsApp, voice transcript) and the agent thinks, calls tools, and replies. That is one run, regardless of how many tool calls happened inside it.
* **An A2A task** — when a peer tasks your agent over the A2A protocol, or your agent tasks a remote peer, the task itself follows the same lifecycle on both ends: `working → completed | failed | cancelled`, with `input_required` while it waits on a caller.

Runs within one conversation share a budget context — the per-conversation cost counter and token aggregate accumulate across runs — but each run has its own status and its own terminal outcome.

## The state machine

A successful run moves through:

`queued → working → completed`

| State            | Meaning                                                                                                                                                                                                              | What advances it                                                                                                                          |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `queued`         | The run exists but a worker has not picked it up. Runs through the approval gate (see below), campaign queues, and A2A accept paths sit here until a worker starts them.                                             | A worker dequeues the run.                                                                                                                |
| `working`        | The agent is executing — reasoning, calling tools, streaming tokens toward the reply. In a streaming response you see this as the `status` events `started` and `thinking` alongside `token` and `tool_call` events. | The runtime. Non-terminal; the run stays `working` until it resolves or is interrupted.                                                   |
| `input_required` | The run cannot continue until something external provides input — a pending approval decision, or a pause while a human takes over the conversation. The run is not failed; it is parked.                            | The blocking condition resolves (approval granted, human hands control back). The run returns to `working`.                               |
| `completed`      | The reply was delivered and the run closed cleanly. Senders/dashboards treat this as the success outcome.                                                                                                            | The agent finishes its final assistant turn.                                                                                              |
| `failed`         | The run aborted on an error — a cost or token guardrail, a verification failure, or an internal abort. An `error` event with a specific code is emitted before the run lands here.                                   | The guardrail or error that aborted it. You can retry a fresh run; the conversation's accumulated counters still count toward their caps. |
| `cancelled`      | Your side stopped the run deliberately — an API cancel, an A2A `tasks/cancel`, or a queue abort.                                                                                                                     | A cancel request. A cancelled run cannot resume; start a new run instead.                                                                 |

<div class="mint-callout"><strong>Note</strong>: The internal status event names (`started`, `thinking`, `done`) you see on a streaming response are progress signals emitted while the run is in <code>working</code>. They are not distinct run states, and <code>done</code> simply marks the end of the working stream — the record advances to <code>completed</code> at that moment.</div>

## Interrupting transitions

Three classes of actor can push a live run out of the happy path:

* **Guardrails (run → `failed`).** The per-run and per-conversation caps terminate a run with a specific error code — see [Cost controls](/agents/cost-controls#the-three-ceilings) for the full code list: `COST_LIMIT`, `CONVERSATION_COST_CAP_REACHED`, `API_CALL_LIMIT`, `TOKEN_LIMIT`, `TOOL_ITERATION_LIMIT`. A `cost_limit` failure on run *N* does not reset the conversation counter — the next run against the same `conversation_id` probes the same accumulated cost.
* **Approval gate (run → `input_required` → `working`).** A tool marked `confirmation: "always"` holds the run in a pending-approval state while a human decides. Approving re-queues the deferred turn and the run resumes in `working`; rejecting ends that attempt with a blocked result, and the run continues to its next turn or fails. See [Human-in-the-loop oversight](/agents/human-in-the-loop-oversight#approve-a-high-risk-action).
* **Handoff (conversation-level pause).** A human take-over pauses the agent at the conversation level — the runtime checks `agent_active` before every turn and skips while a human holds it. This is a conversation state, not a run status: the run that triggered the escalation has already closed, and no new run starts until control returns. The same applies to AI→AI handoff through [Handoff targets](/agents/handoff-targets), where the receiving agent starts its own fresh run.

## Context-window behavior per run

Each run reads the conversation history, the agent's system prompt, any retrieved knowledge, and the current user message into a bounded context window:

* The run's token usage accumulates against the conversation's `max_tokens_per_conversation` aggregate. When a run would push the conversation past that aggregate it terminates with `TOKEN_LIMIT` — the run fails, not just the reply truncation.
* The runtime caps the message history it loads per run (the window is bounded even when the stored history is long), so a long-lived conversation does not inflate every turn linearly. Older context is summarized or dropped from the window; new messages always win.
* `tool_loop_limit` bounds how many tool-call iterations one run may perform — a run that keeps calling tools past the limit terminates with `TOOL_ITERATION_LIMIT` instead of looping indefinitely.
* Supervision whispers sent on a live conversation are picked up at the start of the next run — they enter the context window as a steering note, not as a user message.

## Observability: status, events, and error codes

Use the status field and the error codes to drive your integration — never parse display text:

| What you read                                                                                     | Where it appears                                                                    | Use it for                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Run `status`                                                                                      | `GET` run/task responses, streaming `status` events (`started`, `thinking`, `done`) | Render progress UI; distinguish `input_required` (waiting on you) from `working` (waiting on the model).                                                            |
| Error `code` on the final `error` event                                                           | Stream or `POST /chat-stream` / `invoke` response                                   | Branch retries: `COST_LIMIT`-class failures mean raise the cap or stop; `TOKEN_LIMIT` means start a new conversation; verification/policy codes mean fix the setup. |
| `agent.*` webhooks (`agent.created`, `agent.updated`, `agent.deployed`, `agent.handoff_occurred`) | Webhook subscriptions                                                               | Lifecycle-adjacent signals — handoff and deploy changes, not per-run states.                                                                                        |
| Approval rows (`GET /agents/tool-approvals?status=pending`)                                       | Approval queue endpoints                                                            | Surface runs parked in `input_required` because a tool needs sign-off.                                                                                              |

Two pitfalls to avoid:

1. **Do not poll for a terminal `agent.run.aborted`-style webhook** — none exists. The terminal signal is the run status itself plus the final `error` event's code.
2. **Do not treat `input_required` as failure.** It is a parked, resumable state. Alerting on it as an error floods your on-call with approvals that will resolve themselves when a human clicks approve.

## Cross-links

* [Handoff targets](/agents/handoff-targets) — the allowlist an agent needs before it can route a conversation to another agent (each handoff starts a fresh incoming run on the target).
* [Human-in-the-loop oversight](/agents/human-in-the-loop-oversight) — the whisper/approve/take-over surface that parks and resumes runs.
* [Cost controls](/agents/cost-controls) — the three ceilings that turn a run from `working` to `failed`.
* [Agent versions](/agents/agent-versions) — versions freeze the prompt/tools a run executes against; a run never mixes configuration across versions.
* [A2A federation](/agents/a2a-federation) — the A2A task lifecycle echoes these states on both your side and the peer's.
