Skip to main content
A prompt starts or contributes to foreground work in a session. Foreground work can also start without a prompt, such as when the Agent follows up on a background task that finished. It reports the same states either way. The Agent may continue that work until it reports idle, including across multiple model exchanges and tool invocations. session/prompt lasts until the Agent inserts the user message into the ACP conversation. This is what acceptance means for a prompt. The response returns that message’s messageId. The Agent reports its content and placement, foreground state, output, and completion through session/update notifications. Before sending prompts, Clients MUST first complete the initialization phase and session setup.

Session updates

Agents send session/update notifications to report streamed content, tool activity, and changes to session state. The sessionUpdate field inside params.update identifies the variant: This is the complete set of standard variants defined in the current SessionUpdate schema, which describes each payload in detail. The walkthrough below illustrates common uses of these updates. The protocol also supports custom and future variants. Clients that do not understand an update SHOULD preserve its raw payload when storing, replaying, proxying, or forwarding session history, and otherwise ignore it or display it generically. Live-only notices SHOULD NOT be replayed, even when preserved as unknown updates. Session updates are not limited to prompt-driven foreground work. For example, Agents may advertise commands after creating a session, and background activity may emit updates while the Agent is idle.

Entity IDs

Messages, tool calls, terminals, plans, and compactions are entities that the Agent creates and then updates by ID across multiple session updates. An entity is identified by its type together with its ID, not by its ID alone: An ID is unique among entities of the same type within a session, not across all entity types, so the same string can identify one entity of each type. User messages, Agent messages, Agent thoughts, and session-directed messages are separate types even though they share the messageId field. For example, an Agent may report a thought and a response derived from the same model output with the same messageId. They remain two distinct entities. The Agent MUST NOT reuse an ID for a different entity of the same type within a session. Clients MUST key entities by both type and ID. An update applies only to the entity of its own type: an agent_thought whose messageId matches an existing Agent message creates or updates a thought and leaves the message unchanged. Likewise, a field that references an entity identifies an entity of one specific type. For example, the messageId returned by session/prompt identifies a user message. An update or chunk for an ID the Client hasn’t seen creates the entity with its defaults and then applies, so a chunk can arrive before any full update. Clients MUST NOT drop it. An entity’s first update or chunk fixes its timeline position in receive order: it follows previously received timeline content and precedes content first received later. Later updates and chunks patch the entity in place without moving it, including after the session reports idle. Updates for different entities may interleave, and a new ID does not end any other entity. Clients that show an entity outside the timeline, such as a plan panel, may ignore its position.

Session-directed messages

The Agent may report outgoing and incoming messages between sessions, separately from user-facing responses and tool calls. The outer params.sessionId identifies the transcript being updated. Every session_message or session_message_chunk requires messageId, scoped to that transcript. Optional senderSessionId and recipientSessionId on session_message identify the participants. Chunks carry only content, as agent_message_chunk does. Include them on the first session_message when available, before the first chunk of a streamed message. Later updates may omit them or add missing identities. For example, an incoming message in the child can name its participants, then stream its content:
Chunks append normal message content for the same IDs. A chunk for a messageId the Client hasn’t seen creates the session-directed message, then appends to it. A session_message upsert may replace the whole content array or patch message metadata by top-level key, as described in Metadata Patches. Omission leaves either field unchanged. null clears it. content: [] also clears accumulated content. Chunk _meta is scoped to that chunk and does not patch message metadata. Clients can render “To …” with a recipient link or “From …” with a sender link. Without sufficient participant metadata, use a generic inter-session presentation and add links when the metadata arrives. Participant IDs follow the same patch rule as content: omission leaves a known value unchanged and null clears it. The two views have independent message IDs. Optional senderMessageId on the incoming view and recipientMessageId on the outgoing view name the other view’s messageId, so a Client can go from either view to the other. They follow the participant-ID rules and can arrive in a later session_message. Clients must not link views by equal IDs alone. The Agent reports each side only when observed. Clients must not synthesize an incoming entry from an outgoing one or forward the content themselves. Neither view acknowledges completed recipient processing. The task a parent gives a subagent, including the first one that starts its work, is a session-directed message. Agents SHOULD report it as an incoming session_message in the child’s transcript, with senderSessionId set to the parent, and SHOULD NOT report it as a user_message. Clients can then render it as a message from the parent session rather than from the user. See Delegated tasks. When the Agent has no evidence of who sent a message the child received, it omits senderSessionId and MUST NOT assume the parent. It links two views only when it can establish that they are the same message, and leaves them unlinked otherwise.

Foreground and background work

Foreground work is the Agent’s active response or workflow in the session, including model processing, tool execution, and waiting for user action needed to continue that work. The Agent MUST treat that work as foreground whether it was started by a user prompt or by the Agent, for example to pursue an ongoing goal, continue earlier work, or act on a completed background task. Background activity runs independently of the active response or workflow. A detached task continuing to run, its progress or completion updates, and session metadata updates do not by themselves start foreground work. If the Agent begins a response or workflow to act on a background result, that work is foreground work. The same lifecycle requirements apply regardless of what started foreground work:
  • When foreground work starts, the Agent MUST report running, or requires_action if the work starts blocked on user action.
  • While foreground work is blocked on user action, the Agent SHOULD report requires_action. Once reported, it MUST report running when foreground progress resumes.
  • When foreground work ends, the Agent MUST report idle, even if background activity continues, and SHOULD include the corresponding stop reason.
Agent-initiated work does not require a session/prompt request or a synthetic user message. State updates describe the session’s foreground work, not individual model exchanges, tool calls, or prompt requests. For example, when a background task completes and the Agent starts a follow-up response:
In contrast, if the Agent only reports a detached task’s progress or completion while idle:
A permission request or elicitation does not by itself change the foreground state. If it blocks foreground work, the Agent SHOULD report requires_action. If it belongs only to independent background activity, the session may remain idle. Resolving a request does not itself imply running: the state changes only if foreground activity changes. See Waiting for User Action and Requesting Permission.

Background work

Background work is session-owned work running independently of foreground work. It is represented by entities the Client already tracks:
  • Terminals. A process that keeps running after its tool call returns control to the model, such as a backgrounded shell or dev server, is a terminal. It is live until its terminal_update reports exitStatus. While it is listed, the Agent SHOULD keep its output current whenever it can read the process output. Output is not a liveness signal: a Client MUST NOT treat a terminal as hung or exited because its output is missing or stale.
  • Tool calls. Work without a process of its own, such as a watcher, a workflow, or a scheduled wakeup, keeps reporting status on its tool call. pending and paused tool calls are still live.
  • Subagent sessions. A background child keeps reporting through its subagent association and state.
Work without a process stays on the tool call that started it. When the Agent knows that a tool call’s work continues after the call returns control to the model, it SHOULD keep that tool call pending, in_progress, or paused and list it, instead of giving it a final status. Some background work has no such tool call. No tool call started it, such as a task that a hook started, or the Agent learned of the work only after its tool call reached a final status. The Agent MAY start a tool call of its own for such work, with a title that describes the work, and list it. It sends that tool call like any other, before the first list that names it, even while the session is idle. The Agent does not reopen a tool call that has a final status. It starts a new one instead. Background work is work the user can reason about: something the user or the model started, which the user may want to stop or leave to finish. The Agent SHOULD NOT list housekeeping it starts on its own, such as memory consolidation. If housekeeping needs to be visible, the Agent represents it as a tool call. running, requires_action, idle, and unknown state updates carry an optional background list of the work currently running independently of foreground work:
  • It is a snapshot. A concrete list replaces the previous one, and [] means no background work is running.
  • Omitted or null means not reported, not that nothing is running. An Agent that reports background work MUST include background on every state_update for the session. If a later snapshot omits it, or it is malformed, the Client treats background work as unknown until a list is reported again.
  • The Agent re-sends the state when the list changes. When background work starts or ends, the Agent SHOULD send a repeated state_update with the current state and the updated list. A repeated snapshot is not a state transition, and a repeated idle without a new stop reason is not a new completion.
  • Items are announced before they are listed and finalized before they are removed. The Agent MUST send a terminal_update for a terminal, a tool_call_update for a tool call, or a subagent_update for a child, before the first snapshot that lists it. It MUST send a terminal’s exitStatus or a tool call’s final status before the snapshot that removes it.
  • Known follow-up work is reported with the removal. When finished background work starts new foreground work and the Agent knows this when it removes the item, the Agent SHOULD report running in the same snapshot that removes the item, or earlier. An Agent that only learns of the follow-up later, for example because the underlying agent decides after the work ends, reports running when that work starts. Clients cover that gap with the grace period described below.
  • Only background work is listed, and only once. A terminal or tool call that foreground work is waiting on is foreground work and is not listed. Process-backed work is listed as its terminal, and the Agent MUST NOT also list a tool call whose only live work is a listed terminal.
  • Unknown items still count. An item with an unknown type does not invalidate the list. It still means background work is running.
  • A background child is listed while it has anything running. A detached child is listed while its state is not idle, or while its own background list is non-empty or not reported. An idle child with no background work of its own is not listed, even though it remains associated.
The Client uses the list to tell:
  • Whether the session is quiet. Nothing is running when the state is idle and background is []. When the list only contains pending or paused tool calls, work is scheduled or suspended but nothing is running now. A Client that frees resources when a session becomes quiet SHOULD wait a short grace period after the snapshot that empties the list, since finished work may start a follow-up.
  • What closing will stop, so it can warn before session/close.
  • Which unfinished tool calls and terminals are still live. Once the session is idle, a Client MAY treat a pending, in_progress, or paused tool call, or a terminal without exitStatus, that is not in the latest reported list as abandoned. A tool call that references a listed terminal is live. A Client MUST NOT treat anything as abandoned when background is not reported.

Prompt lifecycle

The diagram shows one possible ordering. See Insertion and ordering.

1. User message

The Client sends a user message with session/prompt:
SessionId
required
The ID of the session to send this message to.
ContentBlock[]
required
The contents of the user message, e.g. text, images, files, etc.Clients MUST NOT send an empty prompt array. Agents MAY reject one with -32602 without inserting it.Clients MUST restrict types of content according to the Prompt Capabilities established during initialization. Agents MUST reject a prompt containing a content type that is neither baseline (text, resource_link) nor advertised, or that they don’t understand with -32602, without inserting it. Custom content types need a custom capability, such as an entry in capabilities.session.prompt._meta.
Learn more about Content

2. Prompt accepted

Acceptance means insertion: the Agent has logically added a user message to the ACP conversation, with a messageId and a position relative to other messages. Receipt, queueing, and assigning an ID are not insertion. The Agent MUST respond successfully once the message is inserted, without waiting for foreground work to finish, and MUST NOT do so before insertion. Once inserted, it MUST NOT return an error response, even if failure or request cancellation occurs before the response is sent. It returns the messageId. Subsequent work outcomes are reported as described in Failures and Cancellation. An adapter can use a runtime’s user-message echo or insertion event as evidence of insertion. For a locally handled command that the runtime does not record, it can instead insert a live-only ACP user message. The response contains the inserted user message’s agent-generated messageId:
MessageId
required
The opaque ID of the inserted user message. This field is a required, non-null string. Omission and explicit null are invalid.
The Agent MUST also report the inserted user message through a user_message update with the full content array or streamed user_message_chunk updates. All updates for that message MUST use the returned messageId and MUST NOT precede insertion. Clients MUST tolerate the updates arriving before or after the response.
The Client uses the JSON-RPC response ID to identify its submission, then associates it with (sessionId, messageId). If the message update arrived first, the Client reconciles with that existing message rather than creating a duplicate. Agents MUST assign distinct message IDs to distinct inserted submissions, even if their content is identical. Clients must not infer the association from content or arrival order. Insertion is not a promise of durable storage or model consumption. Every inserted input, including a locally handled command, is reported live, but Agents are not required to persist it or retain it for any minimum period. If the message is retained and replayed, the Agent MUST use the same ID. See Resuming Sessions. A successful prompt response is not an idle signal. Session work and notifications continue independently of the completed prompt request. The response has no fixed order relative to other session updates: the user_message acknowledgment, Agent output, and even the idle state_update that ends the resulting work may arrive before or after it. Clients MUST NOT infer ordering between them. If the response is lost, the submission’s outcome remains uncertain: replay may omit live-only or discarded messages, and a retry can create another submission. The same insertion boundary applies when closing the session cancels a pending prompt: if closing prevents insertion, the request receives -32800 (Request Cancelled). If the message has already been inserted, it receives a successful messageId response. Prompt responses need not precede the close response.

Insertion and ordering

running reports foreground work, not insertion. It may precede insertion, the prompt response, and user-message updates, for example while a foreground hook runs. It does not acknowledge a pending prompt’s insertion.

Prompts during active work

Clients MAY send session/prompt in any state, including while foreground work is running or requires_action. The Agent either inserts the prompt right away or holds it and inserts it later. Held prompts are inserted in the order they were received. What inserting during active work means, such as steering the current turn or starting the next one, is up to the Agent. Agents MUST NOT reject a prompt only because foreground work is active. A held prompt’s response stays pending until the prompt is inserted. The Client can withdraw it with $/cancel_request, and session/cancel and session/close answer prompts that haven’t been inserted with -32800. Future capabilities may let Agents advertise steering or queueing behavior.

Prompts that are never inserted

If a request is rejected before insertion, including by a hook or early failure, the Agent MUST return a JSON-RPC error response and MUST NOT emit user-message updates for that submission. Request cancellation before insertion uses -32800. The foreground lifecycle applies independently of the request outcome: report idle when no foreground work remains, not merely because a prompt was rejected. A failed foreground hook can therefore require both a prompt error response and an idle update. If no foreground work started, rejection requires no state transition.

3. Agent reports output

When foreground work starts, the Agent MUST report its active state. Usually this is a state_update notification with state: "running". Work that starts blocked on user action may report requires_action directly. When progress resumes after a reported requires_action, the Agent MUST report running:
The language model MAY respond with text content, tool calls, or both. The Agent reports the model’s output to the Client via session/update notifications. This may include the Agent’s plan for accomplishing the task:
Learn more about Agent Plans
The Agent can report the model’s text response as an agent_message update with the full content array:

Message IDs

The Agent MUST include an opaque messageId on message updates and message chunks. User, agent, and thought messages can each be reported either as a message update with a full content array or as streamed chunks. user_message, agent_message, and agent_thought updates are upserts keyed by messageId: omitted content leaves the existing message content unchanged (or empty for a new messageId), content: null clears it, and concrete content arrays replace the previous content. Chunk updates with the same messageId append content. A chunk for a messageId the Client hasn’t seen creates the message with empty content, then appends to it. Clients apply message updates and chunks in the order they are received for each messageId:
  • A message update without content leaves the current content unchanged, so Agents can update other optional fields without resending content.
  • A message update with content replaces all content currently stored for that message, including content accumulated from earlier chunks.
  • A message update with content: [] or content: null clears the message content.
  • A chunk appends its content to whatever content is current for that message, whether that content came from an earlier message update or earlier chunks.
  • A message update’s top-level _meta patches the message’s stored metadata by top-level key, even when the same update replaces content, as described in Metadata Patches. A chunk’s _meta, when present, is chunk-scoped.
For example, if an Agent sends agent_message with content: [A], then sends agent_message_chunk with B, the rendered message content is [A, B]. If it later sends another agent_message with content: [C], the rendered content becomes [C]. The earlier full content and chunks are replaced. Subsequent chunks append to [C]. For streaming agent text, the Agent can use agent_message_chunk updates:
The Agent can report internal reasoning with the same message-update or chunk patterns. agent_thought updates patch fields for the same thought messageId. agent_thought_chunk updates append new content.
If the model requested tool calls, these are also reported immediately:

Session notices

No Client capability is required in v2. Clients that do not implement notice may preserve it through the unknown-update fallback and ignore it or present it generically. This does not give it replay semantics. This is capability-free informational delivery, not an advertisement that the Client will display the notice. At any point while a session exists, including outside prompt processing, the Agent MAY send a live user-facing notice:
severity is required and non-null and initially supports info, warning, and error. It is an open string enum: values beginning with _ are reserved for implementation-specific extensions, while other unknown values are reserved for future ACP severities. Clients preserve unknown values and use a generic notice presentation without inferring additional behavior from them. title is required, non-null, non-empty plain text suitable for a compact presentation. description is optional, nullable plain text with supporting detail. Omission and null both mean no description. _meta is an optional, nullable object scoped to this event. Omission and null both mean no event metadata. Unlike compaction patch fields, these fields do not update or clear an earlier notice. Clients MAY ignore them, and Agents MUST behave as though a notice may not have been received, displayed, or seen. A notice MUST NOT carry information required for protocol correctness, authorization, user action, task completion, or fatal-error reporting. Clients own presentation and dismissal. They may use a toast, banner, inline status region, notification center, or another accessible surface, and may coalesce repeated notices. Severity is only a presentation hint. Even error does not fail a request, stop foreground work, change session state, or imply a prompt stop reason. Dismissal is local and produces no acknowledgement or removal event. Notices have no ID or Agent-managed lifecycle. Each occurrence is an independent live event, not conversation history, and SHOULD NOT be included in session replay. If a condition remains relevant after reconnecting, the Agent may emit a new notice.

Session compaction

No Client capability is required in v2. Clients without compaction-specific handling may preserve, ignore, or generically display these informational updates under the v2 negotiation rules. The Agent MAY report context compaction as one persistent, ID-addressed timeline entity. A single compaction_update with status: "completed" is sufficient. Reporting progress is optional. Summary content is also optional. It may be sent all at once in the summary field or streamed using compaction_summary_chunk for the same compactionId. For example, an Agent can report that compaction has started:
compactionId and status are required and non-null on every compaction_update. The ID is an opaque, Agent-owned string, unique within the session, and MUST NOT be reused for another compaction. Clients initialize a first-seen compaction ID with status in_progress and unset optional fields, then apply the notification. As for every entity, the first notification fixes the compaction’s timeline position. Later updates and chunks for other entities patch them in place. They do not move or split them around the compaction. The Agent MUST report a terminal status when a reported compaction ends. status is an open string enum. Values beginning with _ are reserved for implementation-specific extensions. Other unknown values are reserved for future ACP statuses. Clients preserve unknown strings and present a generic state without inferring lifecycle or control behavior. If the retained summary is available incrementally, the Agent may append one content block at a time:
compactionId and content are required and non-null on each chunk. Chunk _meta is optional and nullable, is scoped to that chunk, and has no patch semantics: omission and null both mean no chunk metadata. An update may omit summary to retain the streamed content. A non-streaming Agent can instead supply the complete summary, and a streaming Agent can use the same field as an authoritative replacement of content received so far:
The following compaction_update fields are optional, nullable patches: For each patch field, omission leaves the stored value unchanged, null clears it, and a concrete value replaces it. On a first-seen ID, omission and null both start with no value. summary: [] also clears the summary. A _meta object patches the stored metadata by top-level key instead of replacing it. See Metadata Patches. Clients apply updates and chunks in receive order for each ID. Chunks append to the current summary. A concrete summary replaces all previously accumulated content, and any subsequent chunks append to that replacement. summary: null or summary: [] clears accumulated content, including partial output before failure or cancellation. The summary is not the Agent’s complete replacement history or generic status text such as “Compaction completed”. It SHOULD faithfully represent the retained, user-displayable summary without internal prompt framing or hidden instructions. Text blocks may contain Markdown. Agents SHOULD omit it when no unencrypted summary is available or when disclosure would reveal context that was not otherwise user-visible. Compactions are replayed like other session entities. See Replay Contents. Compaction does not instruct the Client to discard earlier conversation history. Collapsing it is a local presentation choice. It also does not replace usage_update, which reports current context-window usage and cost rather than a compaction boundary.

Session usage updates

The Agent MAY also report current session context and cumulative cost state with a usage_update:
used and size are required and non-null token counts for the current session context. cost is a patch field: omitting it leaves the last reported cost unchanged, null clears it, and a value replaces it, including when the currency differs. A value requires amount and currency. currency is the unit of amount: an ISO 4217 currency code such as "USD" for money, or another unit such as "credits".

4. Report completion

When foreground work ends, the Agent MUST report idle with a state_update notification and SHOULD include the corresponding StopReason:
The Agent MUST send any pending updates for the foreground work that ended before sending its idle update. Background activity may continue to emit updates afterward. Agents MAY stop foreground work at any point by sending an idle state_update session update. They SHOULD include the corresponding StopReason.

5. Tool invocation and status reporting

Before proceeding with execution, the Agent MAY request permission from the Client via the session/request_permission method. While foreground work is blocked on a permission response or other user action, the Agent SHOULD report requires_action. After reporting it, the Agent MUST report running when foreground progress resumes, or idle if the work ends without resuming. These are aggregate state changes, not updates for each request. See Waiting for User Action. A permission request for independent background activity does not by itself change the foreground state. See Foreground and Background Work. Once permission is granted (if required), the Agent SHOULD invoke the tool and report a status update marking the tool as in_progress:
As the tool runs, the Agent MAY send additional updates, providing real-time feedback about tool execution progress. The Agent streams incremental content items with tool_call_content_chunk:
Clients append each tool_call_content_chunk to the current content for that toolCallId. A later tool_call_update with content replaces the accumulated content. Display-only terminal bytes use terminal_output_chunk, not tool_call_content_chunk. See Display-only Terminals for their per-terminal byte ordering and snapshot semantics. Tools execute on the Agent. Clients may expose tools through MCP servers supplied during session setup. v2 does not define Client file-system or terminal-execution capabilities. Client interactions such as elicitation remain subject to their own negotiated capabilities. When the tool completes, the Agent sends another update with the final status and any content:
Learn more about Tool Calls

6. Continue conversation

The Agent sends the tool results back to the language model as another request. The cycle returns to step 3, continuing until the language model completes its response without requesting additional tool calls or foreground work is stopped by the Agent or cancelled by the Client.

Stop reasons

When an Agent stops foreground work, it SHOULD specify the corresponding StopReason on an idle state_update session update. If the Agent knows it is idle but cannot determine why the work stopped, it may omit the stop reason. Clients MUST recognize idle as the end of foreground work even when the stop reason is omitted, null, malformed, or unknown. Without a recognized stop reason, Clients cannot infer how the work ended.
The Agent has finished foreground work, including a locally handled command that required no model call.
The maximum token limit is reached
The maximum number of model requests for the foreground work is exceeded
The Agent refuses to continue
The user or Client chose to stop the work: with session/cancel, by closing the session, or through an interaction the Agent offers, such as a permission option that tells the Agent to stop
Foreground work ended because something failed. See Failures.
When the Agent stops foreground work for its own reasons, it uses the matching reason: max_tokens or max_turn_requests for limits, refusal for a refusal, and error for anything else, such as an internal timeout. cancelled means the user or Client chose to stop the work, not that the Agent hit a limit or failed. Custom or future stop reasons can be used when Clients can display a generic stopped state. Custom stop reasons MUST begin with _. Unknown non-underscore stop reasons are reserved for future ACP variants.

Failures

When foreground work ends because something failed, the Agent MUST report idle and SHOULD include the error stop reason. This includes pre-insertion work and failures before any model output. The prompt’s response still follows the insertion boundary. This stop reason carries the failure in an error field beside stopReason, which the Agent SHOULD include:
Error
The failure that ended foreground work, as a JSON-RPC error object with required code and message and optional data. Optional and nullable: omission and null both mean the Agent supplied no failure details.
error belongs to the error stop reason. Beside any other stop reason it is an unknown field. Clients may show message to the user and can handle code as they would for an error response: for example, -32000 (authentication required) can prompt the user to authenticate before sending another prompt. Before reporting the failure, the Agent SHOULD give each tool call that the failed work left unfinished a terminal status, using failed for calls that did not complete. Listed background work, and tool calls that reference a listed terminal, are not part of the failed work and keep running. A failure does not undo the insertion or any side effects the work already had, such as tools that ran. Sending the prompt again submits a new message. The error stop reason is only for failures that end foreground work:
  • A failure before the user message is inserted requires a JSON-RPC error response to session/prompt, whether or not it also ends foreground work.
  • A failure the Agent recovers from while work continues, such as a retried model request or a failed tool call, is reported with a notice or a tool call whose status is failed, and does not change the state.
  • Errors raised while aborting cancelled work are reported with the cancelled stop reason, as described in Cancellation.
  • A failure in background activity while the Agent is idle is not a stop reason, since no foreground work ended.
Clients detect a lost connection or an exited Agent process themselves. No update can report them.

Session states

state_update is a snapshot of the session’s aggregate foreground activity, whether or not a prompt started it. It is not an event for an individual prompt, model call, permission request, or elicitation. For exposed subagents, Agents SHOULD also mirror the same snapshot in the immediate parent’s subagent_update.state. This keeps the parent’s roster and the child’s own stream informed about one logical state. Duplicate snapshots do not represent additional work or usage.
Foreground work is in progress.
No foreground work is active. Background activity may continue.
Foreground work is blocked on user action.
The Agent cannot currently determine foreground activity. For example, a remote child feed may be lost while ACP remains connected.
Agents report unknown on actual loss of observability, not merely after silence. Clients must no longer present a prior running or requires_action as confirmed live activity, though they may retain the last-known state for context. A subsequent running, requires_action, or idle replaces unknown for the same session. unknown is neither a work outcome nor a closure or cancellation, and does not resolve pending requests or automatically revoke child mutation capabilities. The Agent updates those capabilities explicitly if a control becomes unavailable. Background activity MAY continue and emit other session/update notifications while the Agent reports idle. These notifications do not change the state. Each state may carry a background list of that work. Unless the Client has advertised support for a custom or future state, Agents MUST still report running, requires_action, and idle where the state transitions require them. A Client that receives a state it doesn’t recognize MUST NOT treat it as idle or present the previous state as confirmed, and shows a generic busy state until a known state arrives.

Initial state and synchronization

New sessions default to idle until a state update reports otherwise. State updates may arrive before or after the session/new response. Clients MUST NOT overwrite an already-reported state when the response arrives. A separate initial idle notification is optional and does not represent completion. session/resume does not imply idle. The Agent MUST send a snapshot of foreground state after resuming, following any requested history replay and before the successful resume response, even when no replay was requested. Clients MUST use that snapshot rather than infer activity from the response or replayed content. See Current-State Synchronization for ordering and pending interactions.

State transitions

The table describes live state reporting between the three standard states. Initial or resume snapshots report the current state without implying a transition. The unknown state can replace any state on loss of observability. A later current-state snapshot restores a known state without implying that work started or ended while it was unobservable. requires_action is only for blocked foreground work. An outstanding request alone does not justify it, but foreground work that starts blocked may report it directly without an artificial running first. Agents MAY repeat current-state snapshots. Clients MUST apply them idempotently: repeats do not start new work, end additional work, or add usage again. Repeated or resume idle snapshots may carry the last stop reason for context, without creating a new completion or cancellation. A repeated state may carry updated information. Applying that information does not constitute another state transition. When an idle transition ends work, the Agent SHOULD include its stop reason, and MUST use cancelled when confirming foreground cancellation.

Waiting for user action

requires_action means the foreground workflow as a whole is blocked on user action. The session remains running if other foreground work can still progress, even if some permission requests or elicitations are pending. Clients present those requests independently of the aggregate state. With several outstanding requests, opening or resolving a request does not itself cause a transition. Once requires_action is reported, the Agent MUST report running when progress resumes, even if other requests remain outstanding, or idle if the work ends without resuming. While still blocked, answering one request does not imply running. Declining or cancelling a request may lead to resumed work, a fallback, or completion. Agents SHOULD ignore responses for work that has already ended rather than use them to resume that work.

Work boundaries and local commands

When no foreground activity remains, the Agent MUST report idle. If another foreground interval then begins, it reports an active state again, even when the next interval starts immediately. Consecutive completed turns therefore have an idle boundary, not a continuous running state. Completing an individual prompt, command, or tool call while other foreground activity continues does not require an idle boundary. For prompts sent while work is active, see Prompts During Active Work. Handling an accepted prompt, including a local slash command that performs no model work, MUST use the foreground lifecycle. A local command handled from idle with no user-action wait uses running → idle and SHOULD use end_turn for successful completion. Neither the prompt response nor output notifications substitute for state updates. Foreground work may precede insertion. See Insertion and ordering.

Lifecycle examples

These examples illustrate the lifecycle. Blocked-state examples assume the Agent follows the recommendation to report requires_action. Start, resume-from-blocked, and completion updates are required. Normal completion stop reasons are recommended, and the cancellation stop reason is required. The same rules apply to permissions and both elicitation modes. In URL mode, an accept response leaves requires_action unchanged while the user still needs to finish the external interaction. Late answers may arrive after work ends, including during a later foreground workflow. An idle snapshot on resume may carry a previous stop reason without creating another completion.

Cancellation

Clients MAY cancel the session’s foreground work at any time by sending a session/cancel notification:
session/cancel covers foreground work and prompts received before it that the Agent has not yet inserted. The Agent MUST answer the covered prompts with -32800, as described in request cancellation. A session/prompt received after session/cancel is not cancelled by it. Once a message is inserted, its prompt request still succeeds with its messageId. Cancellation does not undo that insertion. Work in the latest background list is not covered and keeps running. The Agent MAY stop background activity it does not list. The Client SHOULD preemptively mark all non-finished tool calls pertaining to the current active work as cancelled as soon as it sends the session/cancel notification. It SHOULD skip tool calls in the latest background list and tool calls that reference a listed terminal. This mark is a local display hint: the Agent gives those tool calls their final status, and its updates replace the mark. When the Agent stops the cancelled foreground work, it MUST stop waiting for that work’s pending session/request_permission requests and treat them as not approved. It SHOULD cancel each of them with $/cancel_request so the Client can dismiss the prompt. The Client MAY answer a request it knows belongs to the cancelled work with the cancelled outcome without waiting for $/cancel_request. Permission requests for work in the latest background list are not affected. For elicitation requests, see Elicitation Cancellation. When the Agent receives this notification, it SHOULD stop all language model requests and all tool call invocations of the cancelled foreground work as soon as possible. A response can race with session/cancel before cancellation completes. After cancellation completes, the Agent SHOULD ignore late responses or external URL completions rather than use them to resume the cancelled work. After all ongoing operations have been successfully aborted and pending updates have been sent, the Agent MUST send an idle state_update session update with the cancelled stop reason if foreground work was active when cancellation took effect. Cancellation can finish without receiving answers to abandoned user-action requests. If no foreground work was active when cancellation took effect, no additional completion update is required.
API client libraries and tools often throw an exception when their operation is aborted, which may otherwise be surfaced as a generic failure.Clients often display unrecognized errors from the Agent to the user, which would be undesirable for cancellations as they aren’t considered errors.Agents MUST catch these errors and report the semantically meaningful cancelled stop reason, not error, on a state_update notification, so that Clients can reliably confirm the cancellation.
The Agent MAY send session/update notifications with content or tool call updates after receiving the session/cancel notification, but it MUST ensure that it does so before sending the idle state_update session update that reports cancellation. The Client SHOULD still accept tool call updates received after sending session/cancel.