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

# v2 Background Work

Author(s): [@benbrandt](https://github.com/benbrandt)

## Elevator pitch

> What are you proposing to change?

ACP v2 already separates foreground work from background activity: `state_update` reports whether the session is `running`, `requires_action`, or `idle`, and background activity may continue while the session is `idle`. What it does not tell a Client is *whether* anything is still running in the background, *what* it is, or whether the Client can stop it.

This RFD reuses the entities ACP already has instead of adding a new one:

* **Terminals and tool calls can outlive the foreground work that started them.** A backgrounded shell is a [terminal](https://agentclientprotocol.com/rfds/v2/terminal-output.md) that keeps running after control returns to the model. A watcher or a scheduled wakeup is a tool call that stays `pending` or `in_progress` after the session becomes `idle`.
* **Terminals and tool calls can be cancelled individually.** Either can advertise `capabilities.cancel`, and the Client cancels it with a new `terminal/cancel` or `tool_call/cancel` request, without cancelling the session's foreground work.
* **State snapshots list the background work.** Every `state_update` can carry a `background` list referencing the terminals, tool calls, and subagent sessions that are running independently of foreground work. Clients use it to show that something is still running, to warn before closing, and to tell background work apart from stale tool calls and terminals.

## Status quo

> How do things work today and what problems does this cause? Why would we change things?

Coding agents now routinely leave work running after they finish responding:

* **Claude Code / Claude Agent SDK:**
  * `Bash` with `run_in_background`, Ctrl+B, or a command that hits its timeout keeps running after the turn.
  * Subagents run in the background by default.
  * `Monitor` watches a script or WebSocket.
  * `Workflow` runs local or remote multi-agent scripts. A workflow can be paused, which stops its agents but keeps its journal so it can be resumed later.
  * `ScheduleWakeup` and `CronCreate` start new turns later.
  * The SDK reports these as tasks (`task_started`, `task_updated`, `task_notification`) and sends the full live set in `background_tasks_changed`. Task statuses include `paused`.
  * The SDK also reports housekeeping that Claude Code starts on its own, such as memory consolidation, the auto-mode scan, and auto-started WebSocket watchers, and marks those tasks `ambient` so hosts leave them out of activity indicators. Work the model requests, including `Monitor`, is not ambient.
* **Codex:**
  * Unified-exec commands keep running as background terminals after the turn, and collab subagents can outlive the root turn.
  * `ThreadStatus` has no flag for background terminals, so a thread reports `idle` while terminals run.
  * Codex has experimental `thread/backgroundTerminals/list`, `/terminate`, and `/clean` requests, and its TUI has `/ps` and `/stop` for them.
* **Others:** Gemini CLI (`is_background` shells), Cursor (`is_background` terminal commands), and Cline ("Proceed While Running") have similar features. opencode's session status only has `idle`, `busy`, and `retry`.

ACP v2 says this activity may exist ([Foreground and Background Work](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle.md#foreground-and-background-work)) and shows a detached tool call reporting progress while the session stays `idle`. It leaves the rest undefined:

* **Clients can't tell whether the session is quiet.** `idle` means ready for a new prompt, not "nothing is running". Clients can't safely show a session as done, close it to save resources, or warn the user that closing will stop a dev server.
* **Unfinished tool calls are ambiguous.** Agents often leave stale `pending` or `in_progress` tool calls behind, so many Clients mark every unfinished tool call as finished when work ends. That cleanup is wrong for a tool call that is still running in the background, and without that cleanup stale tool calls stay unfinished.
* **Background work can't be stopped individually.** v2 terminals are display-only by design. `session/cancel` only targets the session's current work, and agents differ on whether it stops background work. Only subagents can be cancelled one at a time.
* **Close and resume are underspecified.** `session/close` cancels "any ongoing work", but nothing says Clients will learn what was stopped. After `session/resume`, the Client gets a foreground snapshot but no record of background work that is still running.

Adapters are working around this today. Both `claude-agent-acp` and `codex-acp` implement a private extension (`async_task_spawned`, `async_task_state_update`, `_session/async_task/stop`) that only one Client negotiates, so other Clients see nothing. That extension also adds another entity alongside tool calls, terminals, and subagents, with its own ids, statuses, and output path, for work that already has a tool call or terminal.

## What we propose to do about it

> What are you proposing to improve the situation?

### Background work is terminals, tool calls, and subagents

Background work is session-owned work that is running independently of the session's foreground work. In v2 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](https://agentclientprotocol.com/rfds/v2/terminal-output.md). It is live until its `terminal_update` reports `exitStatus`. The tool call that started it keeps referencing the terminal so the Client can show its output in place, and reports status as the agent's own tool lifecycle does: it may complete once the process has been handed off, as Claude Code's Bash tool does, or stay `in_progress` until the process exits, as Codex's command items do. Either way, the terminal is what is listed. While a terminal is listed, the Agent **SHOULD** keep its output current with `terminal_output_chunk` or replacement snapshots whenever it can read the process output. An Agent that can't read the output may report only the terminal's command and, later, its `exitStatus`. Output is not a liveness signal: a Client **MUST NOT** treat a terminal as hung or exited because its output is missing or stale. Only the `background` list and `exitStatus` say whether it is still running.
* **Tool calls.** Work that continues without a process of its own, such as a watcher, a workflow, or a scheduled wakeup, keeps reporting status on the same `toolCallId`. Its output stays in tool call content.
* **Subagent sessions.** A background child keeps reporting through its [subagent](https://agentclientprotocol.com/rfds/subagents.md) association and `state`.

Terminals, tool calls, and subagents may continue after the session reports `idle`. Their updates do not change the foreground state, as already specified.

No new entity type is added. Anything an agent runs in the background is a process, work started by a tool call, or a subagent. The rare exception can be modeled as a tool call that the agent starts itself.

**Background work is work the user can reason about.** It is something the user or the model started and that the user may want to stop or leave to finish. Internal housekeeping that the agent starts on its own, such as memory consolidation or an auto-started watcher, is not background work in this sense. The Agent **SHOULD NOT** list it, because a Client could only show it as unexplained activity or warn about it before close. If housekeeping needs to be visible, the Agent represents it as a tool call and lists it like any other work.

### Tool call statuses for background work

Today `pending` means the tool call "hasn't started running yet because the input is either streaming or awaiting approval". This RFD generalizes it to "hasn't started running yet", with scheduling as another example reason. It also adds a `paused` status, for work that has started, is not running now, and will not continue until something resumes it:

| Status | Meaning | Background examples |
| - | - | - |
| `pending` | Live, but not started yet | A scheduled wakeup waiting to fire; a cron job waiting for its next run |
| `in_progress` | Actively running | A monitor watching a log or socket; a running workflow |
| `paused` | Started, suspended until resumed | A paused workflow whose agents are stopped but whose state is kept |
| final | `completed`, `failed`, or `cancelled`; no further changes | - |

Clients **SHOULD** show `in_progress` as active, for example with a spinner, `pending` as waiting or scheduled, and `paused` as paused. `paused` is to a tool call what `idle` is to a session: the work still exists and can continue, but nothing is happening now.

Status transitions:

* Statuses never go back to `pending`. When a scheduled tool call fires, the work it triggers is new foreground work, not the tool call running:
  * A scheduled wakeup goes `pending` → `completed` when it fires.
  * A recurring cron job stays `pending` until the job is deleted or the session closes, then becomes `completed` or `cancelled`.
* `in_progress` and `paused` may alternate any number of times. A paused tool call reaches a final status when it is cancelled, fails, or is discarded.
* How work is paused and resumed is up to the Agent, for example a tool the model calls. This RFD does not add Client-initiated pause or resume.

`paused` is a new `ToolCallStatus` variant. Clients that predate it see an unknown status through the existing `Other` fallback and can show it as unfinished.

### Cancellable tool calls

`tool_call_update` gains an optional `capabilities` field with the same shape and patch rules as [subagent capabilities](https://agentclientprotocol.com/rfds/subagents.md):

```json theme={null}
{
  "sessionUpdate": "tool_call_update",
  "toolCallId": "call_012",
  "title": "Watch deploy logs",
  "kind": "other",
  "status": "in_progress",
  "capabilities": {
    "cancel": {}
  }
}
```

* `capabilities` is a nullable patch: omitted leaves it unchanged, `null` clears it, and an object replaces the whole previous value.
* `cancel` is an optional, nullable capability object. Omitted or `null` means unsupported, and `{}` advertises support. It may carry `_meta`. A `pending` or `paused` tool call may advertise it too, for example to delete a scheduled job or discard a paused workflow.
* An agent **SHOULD** clear or stop advertising `cancel` once the tool call reaches a final status.

The Client cancels one tool call with a new `tool_call/cancel` request:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tool_call/cancel",
  "params": {
    "sessionId": "sess_abc123",
    "toolCallId": "call_012"
  }
}
```

* The Client **MUST NOT** send it unless the tool call currently advertises `capabilities.cancel`.
* The Agent responds with an empty result once it has accepted the cancellation. The response does not mean the work has stopped. The Agent confirms that with a `tool_call_update` to a final status, normally `cancelled`, and may send `completed` or `failed` if the work finished first.
* The Agent responds with `-32602` (Invalid params) if the tool call is unknown, already final, or not cancellable.
* Cancelling a tool call does not change the foreground state by itself. The Agent decides what, if anything, to tell the model about it.

This works for foreground tool calls too. A user can stop one long-running step without cancelling the rest of the agent's work.

### Cancellable terminals

[Terminals](https://agentclientprotocol.com/rfds/v2/terminal-output.md) are display-only today. This RFD adds one opt-in control: `terminal_update` gains an optional `capabilities` field with the same shape and patch rules as tool call capabilities.

```json theme={null}
{
  "sessionUpdate": "terminal_update",
  "terminalId": "term_003",
  "command": "npm run dev",
  "cwd": "/workspace/project",
  "capabilities": {
    "cancel": {}
  }
}
```

* `capabilities` and `cancel` follow the same rules as on tool calls. A terminal has no status, so the Agent **SHOULD** clear or stop advertising `cancel` once it reports `exitStatus`.

The Client stops the process with a new `terminal/cancel` request:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 8,
  "method": "terminal/cancel",
  "params": {
    "sessionId": "sess_abc123",
    "terminalId": "term_003"
  }
}
```

* The Client **MUST NOT** send it unless the terminal currently advertises `capabilities.cancel`.
* The Agent responds with an empty result once it has accepted the cancellation. The response does not mean the process has stopped. The Agent confirms that with a `terminal_update` that sets `exitStatus`, for example with the signal used to stop it.
* The Agent responds with `-32602` (Invalid params) if the terminal is unknown, has already exited, or is not cancellable.
* Cancellation stops the process. It does not release the terminal: its output remains available for display and replay.
* Cancelling a terminal does not change the foreground state or the status of any tool call that references it. If foreground work is waiting on the process, the Agent reports the tool call's outcome as it would for any other process exit.

Input, resize, and other interactive control remain out of scope.

### The `background` list on state snapshots

`running`, `requires_action`, and `idle` state updates gain an optional `background` field listing the work that is currently running independently of foreground work:

```json theme={null}
{
  "sessionUpdate": "state_update",
  "state": "idle",
  "stopReason": "end_turn",
  "background": [
    { "type": "terminal", "terminalId": "term_003" },
    { "type": "tool_call", "toolCallId": "call_012" },
    { "type": "tool_call", "toolCallId": "call_019" },
    { "type": "subagent", "sessionId": "sess_child_4" }
  ]
}
```

Rules for the field:

* **It is a snapshot.** Each `state_update` is a complete snapshot, so `background` is too. A concrete list replaces the previous one, and `[]` means no background work is running.
* **Omitted or `null` means the agent is not reporting background work.** It does not mean "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 anyway, or it is malformed, the Client treats background work as unknown until a list is reported again: it does not keep the previous list, consider the session quiet, or treat unfinished tool calls or running terminals as abandoned.
* **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 ([State Transitions](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle.md#state-transitions)), 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` or `tool_call_update` for a tool call, or a `subagent_update` for a child, before the first snapshot that lists it. When a terminal exits or a tool call ends, the Agent **MUST** send its `exitStatus` or final status before the snapshot that removes it from the list, so a Client never treats finished work as abandoned.
* **Follow-up work is reported with the removal.** When the end of background work starts new foreground work, for example a follow-up response to a finished background task, the Agent **SHOULD** report `running` in the same snapshot that removes the item, or earlier. It **SHOULD NOT** send `idle` with an empty list followed immediately by `running`.
* **Items reference existing entities.** `terminal` items reference a terminal by `terminalId` in this session. `tool_call` items reference a tool call by `toolCallId` in this session. `subagent` items reference a child session associated with this session through `subagent_update`. The list is extensible through the usual enum variant extension rules.
* **Unknown items still count.** An item with an unknown `type` does not invalidate the list. It still means background work is running, even when the Client cannot show what it is, so a list of only unknown items is not empty.
* **Paused and pending tool calls stay listed.** A `pending` or `paused` tool call is live background work: closing the session discards it, and the Client should not treat it as abandoned.
* **Only background work is listed.** A terminal, tool call, or child session that foreground work is currently waiting on is foreground work and is not listed. Work moves into the list when it detaches, for example when a shell is backgrounded mid-turn and its terminal is listed.
* **A background child is listed while it has anything running.** Subagent sessions are reusable, and `idle` does not end them. 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.
* **Each piece of work is listed once.** Process-backed work is listed as its terminal. The Agent **MUST NOT** also list a tool call whose only live work is a listed terminal, so a Client counting items never shows one shell as two tasks.

What the Client learns from the list:

* **Whether the session is quiet.** The session has nothing running when the state is `idle` and `background` is `[]`. When `background` only contains `pending` or `paused` tool calls, the session is idle with work scheduled or suspended, and nothing is running right now. Because listed children stay listed while their own background work runs, this also covers descendants. Quiet describes the session at that snapshot, not a promise that it will stay quiet. An Agent may not know ahead of time that finished work will start a follow-up. A Client that frees resources when a session becomes quiet **SHOULD** wait a short grace period after the snapshot that empties the list.
* **What closing will stop.** The Client can warn before `session/close`, for example "Closing will stop 2 background tasks".
* **Which unfinished tool calls and terminals are still live.** A Client **MAY** treat a `pending`, `in_progress`, or `paused` tool call, or a terminal without `exitStatus`, that is not in the latest reported `background` list as abandoned once the session is `idle`. A tool call that references a listed terminal is live, even though it is not listed itself. A Client **MUST NOT** treat anything as abandoned when `background` is not reported.

### Cancellation, close, and resume

**`session/cancel` cancels foreground work only.** Work in the latest `background` list is not part of the session's current work. This matches what Claude Code and Codex already do, and what the subagents RFD specifies for background children.

* When the Client preemptively marks non-finished tool calls as `cancelled` after sending `session/cancel`, it **SHOULD** skip tool calls in the latest `background` list and tool calls that reference a listed terminal.
* Pending permission requests for listed work stay open. Today the Client **MUST** answer every pending permission request in the session with `cancelled` on `session/cancel`. This RFD narrows that to requests for the cancelled foreground work, which is how [elicitations](https://agentclientprotocol.com/protocol/v2/elicitation.md#session-cancellation) already work: the Agent cancels any other request that no longer applies with `$/cancel_request`.
* The Client tells the two apart through the request's `subject`. A permission request for listed work **SHOULD** reference the listed item in `subject`, by `toolCallId` or `terminalId`, and the Agent **MUST** list that work before sending the request. On `session/cancel`, the Client answers with `cancelled` every pending request whose `subject` does not reference work in the latest `background` list, including requests without a `subject`. At worst, the Agent asks again for background work it failed to reference.
* Requests from a background subagent arrive under the child's session id, so `session/cancel` on the parent does not affect them.
* When foreground work fails with the `error` stop reason, the unfinished tool calls the Agent gives a final status are those of the failed work. Listed background terminals and tool calls, and tool calls that reference a listed terminal, keep running.
* Background work is stopped individually with `terminal/cancel`, `tool_call/cancel`, or by cancelling the subagent's session, or all at once with `session/close`.

**`session/close` stops all background work.** "Any ongoing work" in [Closing Active Sessions](https://agentclientprotocol.com/protocol/v2/session-setup.md#closing-active-sessions) includes everything in the `background` list.

* After a successful close, the Client **SHOULD** treat every listed tool call, and every tool call that references a listed terminal, that has not reported a final status as `cancelled`, and every listed terminal that has not reported `exitStatus` as exited with no known exit status. It **MUST** answer any pending permission request for listed work with `cancelled`.
* The Agent **MAY** send final updates for listed work before responding, but doesn't have to. This matches the [subagents RFD](https://agentclientprotocol.com/rfds/subagents.md), where closing a parent cancels background children without requiring new notifications for them.
* Work that is deliberately not owned by the session and survives its close, such as a durable cron job saved in the project, is out of scope and is not listed.

**`session/fork` doesn't copy background work.** A forked session starts with no background work, even if the source session has some. Unfinished tool calls and running terminals copied into the fork's history are not live, and the fork's state snapshots report `background: []` until the fork starts its own background work.

**`session/resume` restores the list.** The resume snapshot required by [Current-State Synchronization](https://agentclientprotocol.com/protocol/v2/session-setup.md#current-state-synchronization) carries `background` like any other snapshot.

* Before that snapshot, the Agent **MUST** send a current `terminal_update` for each listed terminal, a current `tool_call_update` for each listed tool call, and a current `subagent_update` for each listed child, that the requested replay did not already deliver at its current state. When no replay was requested, this means sending all of them, so a Client with no history still knows what each item is.

## Shiny future

> How will things will play out once this feature exists?

A user starts a dev server, asks for a long test run in the background, and schedules a check-in for later. Claude Code also consolidates its memory in the background, but that housekeeping is not listed. When the agent finishes responding, the Client shows the session as idle with "1 running, 1 scheduled" next to it. The dev server's tool call shows its live terminal with a stop button. The background tests finish while the session is idle, and their terminal reports its exit status without the session leaving `idle`.

When the user closes the session, the Client warns that the dev server and the scheduled check-in will be stopped. When they resume the session from another Client, the list of background work and the current state of each item come back with it.

Adapters drop their private async-task extensions. Every Client sees the same background work through terminals, tool calls, and subagents it already renders.

## Implementation details and plan

> Tell me more about your implementation. What is your detailed implementation plan?

### Schema

* **`ToolCallStatus::Pending`:** update the doc comment to "hasn't started running yet", listing streaming input, pending approval, and scheduling as examples.
* **`ToolCallStatus::Paused`:** add a variant for work that has started and is suspended until resumed. Older v2 Clients receive it through the existing `Other` fallback.
* **`ToolCallUpdate.capabilities`:** add `MaybeUndefined<ToolCallCapabilities>`. `ToolCallCapabilities` has `cancel: Option<ToolCallCancelCapability>` and `_meta`, mirroring `SubagentSessionCapabilities`. It is display-only metadata, so a malformed value falls back to unsupported (`DefaultOnError`).
* **`tool_call/cancel`:** add `CancelToolCallRequest { session_id, tool_call_id, _meta }` and `CancelToolCallResponse { _meta }`, client-to-agent. The trait method is `cancel_tool_call` and the method names entry is `tool_call_cancel`.
* **`TerminalUpdate.capabilities`:** add `MaybeUndefined<TerminalCapabilities>`, with `cancel: Option<TerminalCancelCapability>` and `_meta`, handled like `ToolCallCapabilities`.
* **`terminal/cancel`:** add `CancelTerminalRequest { session_id, terminal_id, _meta }` and `CancelTerminalResponse { _meta }`, client-to-agent. The trait method is `cancel_terminal` and the method names entry is `terminal_cancel`. This is separate from the v1 Client terminal methods, which v2 removes.
* **`background` on state updates:** add `background: Option<Vec<BackgroundWork>>` to `RunningStateUpdate`, `RequiresActionStateUpdate`, and `IdleStateUpdate`, and to `UnknownStateUpdate` under `unstable_subagents`. An Agent that can't tell what its foreground work is doing may still know what is running in the background.
  * `BackgroundWork` is an internally tagged enum on `type`: `Terminal { terminal_id, _meta }`, `ToolCall { tool_call_id, _meta }`, `Subagent { session_id, _meta }`, and an `Other` fallback that rejects known discriminators.
  * The Client acts on this list when it cleans up tool calls and terminals and warns before close. Unknown variants are handled by the `Other` fallback and do not invalidate the list. They still indicate background work, even when the Client cannot interpret their details. If an item with a recognized type fails validation, the entire `background` field is treated as unreported. That way a damaged list never makes running work look abandoned.
* **Subagents:** `SubagentUpdate.state` mirrors the child's `StateUpdate`, so the child's own `background` list shows up there too. No separate change is needed.

### Documentation

* `prompt-lifecycle.mdx`: in Foreground and Background Work, describe terminals, tool calls, and subagents as the representation of background work, and add the `background` list to Session States. In Cancellation, say that `session/cancel` does not stop listed work and that Clients skip it when they preemptively mark tool calls as cancelled.
* `tool-calls.mdx`: the generalized `pending` definition, the `paused` status, tool calls that outlive foreground work, `capabilities`, and `tool_call/cancel`. In the terminal section, terminals that outlive their tool call, keeping their output current, terminal `capabilities`, and `terminal/cancel`.
* `tool-calls.mdx`, in Requesting Permission: a request for background work references the listed item in `subject`.
* [Terminal Output RFD](https://agentclientprotocol.com/rfds/v2/terminal-output.md): note that terminals are display-only except for the opt-in cancellation added here.
* `tool-calls.mdx` and `cancellation.mdx`: on `session/cancel`, Clients answer pending permission requests for the cancelled foreground work with `cancelled`, not every pending permission request in the session.
* `session-setup.mdx`: what `session/close` does with listed work, how resume restores the list, and that `session/fork` doesn't copy it.
* `migration.mdx`: mention the `background` list where it describes `idle` and background activity, and that `session/cancel` no longer cancels permission requests for background work.
* [Subagents RFD](https://agentclientprotocol.com/rfds/subagents.md): the Client's optimistic cancellation of a cancelled session's unfinished tool calls skips tool calls in that session's `background` list.
* Each change also goes into the matching `docs/protocol/v2/draft/` page.

### Adapters

| Work | `claude-agent-acp` | `codex-acp` |
| - | - | - |
| Background shell | The Bash tool call completes when the command is backgrounded, as it does today. List its terminal until the SDK task ends, then send `exitStatus`. Claude Code reports no exit code, so the status is empty. The SDK only writes background output to the task's `output_file`, so the adapter reads new output from that file and sends it as chunks. Cancel calls `stopTask`. | The `commandExecution` tool call stays `in_progress` until Codex completes the item, as it does today. List its terminal while the process is in `thread/backgroundTerminals/list`. Codex already streams the process output as deltas. Cancel calls `/terminate`. |
| Background subagent | Existing subagent association, listed while detached. | Existing subagent association, listed while detached. |
| `Monitor`, `Workflow`, `mcp_task` | Keep the launching tool call `in_progress` until the task ends, and report `paused` while a workflow is paused. Cancel calls `stopTask`. | - |
| `ScheduleWakeup`, non-durable `CronCreate` | Keep the tool call `pending` until it fires or is removed. | - |
| Building `background` | From `background_tasks_changed`, which already sends the full live set, skipping entries marked `ambient`. | From `thread/backgroundTerminals/list` and collab agent state. Codex sends no exit events for background terminals, so this has to poll. |

Both adapters can then drop their private `async_task_*` updates in favor of this, and should send final updates for listed work on `session/close`. Neither does today.

claude-agent-acp also has to declare the SDK's `perTaskStopAffordance` whenever it reports `background`. Without it, the SDK kills background tasks when the turn is interrupted, which would break the rule that `session/cancel` leaves listed work running. Today the adapter only declares it for Clients that use the RFD form of subagents.

### Phases

1. Land the schema changes behind `unstable_protocol_v2` with draft docs.
2. Implement in `claude-agent-acp` and `codex-acp`, and in at least one Client.
3. Adjust from that experience before v2 is stabilized.

## Frequently asked questions

> What questions have arisen over the course of authoring this document or during subsequent discussions?

### Why not a single "background activity" boolean on the idle state?

A boolean answers "is anything running?", but not what is running, whether it can be stopped, or which unfinished tool calls and terminals are still live. The list costs little more and answers all of those. A Client that only wants the boolean can check whether the list is empty.

### Why not a new background task entity?

Claude's SDK and the adapters' private extension both model background work as tasks. In ACP almost every such task already has an entity: a terminal for a process, or the tool call that started it, with a title, kind, status, and content. A separate task entity would duplicate that status and output, and Clients would have to merge the two. Background subagents already have their own entity. Making terminals and tool calls cancellable and letting them outlive foreground work covers the same cases without another entity.

### Why list the terminal instead of the launching tool call?

Agents disagree on the launching tool call's status. Claude Code's Bash tool returns as soon as a command is backgrounded, and later `TaskOutput` and `TaskStop` tool calls act on the same process by task id. claude-agent-acp therefore completes the Bash tool call and keeps its terminal open until the task ends. Codex keeps the command item open until the process exits and folds later stdin into the same item, so codex-acp keeps the tool call `in_progress`. Requiring either behavior would make one adapter report a status its agent doesn't have.

The terminal is the same in both: it has its own identity and is live until it reports `exitStatus`. Listing it describes the running process without asking the tool call to stand in for it, and a tool call that references a listed terminal is never treated as abandoned.

Tool calls are still listed for work without a process, such as a monitor, a workflow, or a scheduled wakeup.

### Why does `terminal/cancel` say "cancel" instead of "kill"?

Every other per-item stop in ACP is a cancellation: `session/cancel` for a subagent, `tool_call/cancel`, and `capabilities.cancel`. Using the same word lets a Client drive one stop control from the same capability on every kind of item. "Cancel" also leaves the mechanism to the Agent, which may stop the process gracefully before forcing it, and the `exitStatus` reports what actually happened. It also avoids reusing v1's `terminal/kill`, which goes in the other direction, from Agent to Client, for a terminal the Client owns.

### Doesn't this make terminals more than a display surface?

Only by one opt-in control. An Agent advertises `cancel` only for terminals it can stop, and Clients that ignore the capability see the same display-only terminals as before. Input, resize, and other interactive control remain out of scope.

### Why put the list on `state_update` instead of a separate update?

The state snapshot is already the session-level summary that Clients track, is already re-sent on resume, and may already be repeated with updated information. Putting the list there means a single update tells the Client both whether the agent is busy and whether anything is still running, with no extra resume rules.

### Why can't Clients work out background work from tool call and terminal statuses alone?

They could if every agent reliably finalized every tool call and terminal. In practice, stale `pending` and `in_progress` tool calls are common, and Clients clean them up when work ends. Once tool calls and terminals can legitimately outlive foreground work, Clients need the agent to say which unfinished ones are still live.

### Why are `terminal/cancel` and `tool_call/cancel` requests instead of notifications like `session/cancel`?

The Client needs to know whether the cancellation was accepted. For example, the work may already be finished, or may not be cancellable. A request lets the Agent reject it with `-32602`. The terminal's `exitStatus` or the tool call's status update still confirms that the work actually stopped.

### Why doesn't `session/cancel` stop background work?

Users background work so that it survives the current turn, and agents already behave this way: Claude Code background commands survive an interrupt, Codex `turn/interrupt` leaves background terminals running, and the subagents RFD lets background children continue. Stopping it is a separate, explicit action through `terminal/cancel`, `tool_call/cancel`, or `session/close`.

### What about work that outlives the session?

A durable cron job saved in the project, or a remote agent that keeps running in the cloud, is not stopped by `session/close`, so it doesn't belong in a list Clients use to warn about what closing will stop. Reporting work that isn't owned by a session is out of scope here.

### Should an agent that only writes output to a file use a tool call instead of a terminal?

No. The Claude Agent SDK writes background shell output to a file instead of streaming it, but the adapter can read that file as it grows and send the new bytes as `terminal_output_chunk`. The process is still a process: it has a command, a working directory, byte output, and an exit status, and it is the thing `terminal/cancel` stops. A tool call would lose the byte-faithful display and replay the terminal already provides. An agent that truly cannot read the output can still report the terminal with its command and exit status.

### Why is there no `ambient` flag on list items?

The Claude Agent SDK marks some tasks `ambient` so hosts leave them out of activity indicators. Those are tasks Claude Code starts on its own, such as memory consolidation, the auto-mode scan, skill fork workers, and auto-started WebSocket watchers. Tasks the model requests, including `Monitor`, are not ambient.

The list exists for work the user can reason about: something the user or the model started that the user may want to stop or leave to finish, and that closing the session would stop. Housekeeping fits none of that, so it is left out of the list instead of being listed with a flag that every Client must remember to check. If an agent wants housekeeping to be visible, it reports it as a tool call.

### Why add `paused` instead of using `pending` or `_meta`?

`pending` means work that will start without anyone acting on it, and statuses never go back to it. Paused work has already run, keeps its state, and will not continue until something resumes it. Claude Code pauses workflows this way. A paused item also needs different treatment from a running one: a Client can show the session as not busy while still warning that closing will discard the paused work. Putting the status in `_meta` would hide that from every Client that does not know the agent's extension.

This RFD does not add Client-initiated pause or resume. If agents expose those controls, they can be added later as `pause` and `resume` capabilities next to `cancel`.

### Do we need a "time of next run" for scheduled tool calls?

Possibly. Agents can put it in `_meta` until there is demand for a standard `scheduledFor` field.

### Should the list say whether finishing the work will start a new agent turn?

Claude Code, for example, starts a follow-up turn when a background task finishes. That would help a Client decide whether to keep the session open. It is left out because adapters can't reliably know it ahead of time.

Instead, two ordering rules cover the race between the list emptying and a follow-up turn starting. When the Agent does know that new foreground work is starting, it reports `running` in the snapshot that removes the item, not after an empty `idle` snapshot. When it doesn't, Clients that release resources on a quiet session wait a short grace period first.

### Is there a v1 form?

Not in this RFD. The `background` list belongs on the session-level `state_update`, which only v2 defines. Tool call `capabilities` and `tool_call/cancel` are additive and could be proposed for v1 separately. Terminal cancellation has no v1 equivalent, because v1 terminals are owned by the Client. Until then, v1 Clients rely on adapter-specific extensions.

## Revision history

* 2026-10-27: Added a `paused` tool call status. Defined background work as work the user can reason about, so agent-internal housekeeping is not listed. Unknown item types still count as background work. Agents keep listed terminals' output current. Permission requests for background work reference the listed item, and `session/cancel` answers every other request. Noted that claude-agent-acp must declare `perTaskStopAffordance`.
* 2026-10-26: Terminals are background work in their own right. A backgrounded process is listed as its terminal, whether its launching tool call completes or stays `in_progress`, and terminals can advertise `capabilities.cancel` and be stopped with `terminal/cancel`. Added ordering rules: items are announced before they are listed, final statuses come before removal, and follow-up work reports `running` with the removal. Defined when background subagents are listed, how Clients handle a list that stops being reported, and a grace period before releasing quiet sessions. Added a v1 FAQ.
* 2026-10-21: Initial draft.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.