Ben Brandt d3e673b263 fix: end a held turn cancelled mid-followup at its interrupt's idle (#1229)
Found while reviewing #1226. It affects ACP v1 and v2.

## Problem

A turn held open for its background subagents (`Turn.deferredSettle`,
issues #864–#866) was settled `cancelled` inside `cancel()`, before the
interrupt. The followup cycle can be live at that point, writing the
subagents' summary or blocked on a permission request. The interrupt
still flushes that cycle's output, so:
- **v1:** the output arrives after `session/prompt` has answered
`cancelled`, when many clients have stopped listening.
- **v2 (#1226):** the output arrives after `idle cancelled`. The v2
draft says updates after a cancel MUST come before it.

## Change

`cancel()` now settles inline only where the consumer can't:
- **a settling turn,** blocked in its checkpoint preview, which can't
process the interrupt's trailing idle (unchanged);
- **a held turn whose session sits idle,** or has reported no state yet:
the interrupt emits nothing, so no trailing idle comes (unchanged).

**A held turn with a live followup** (`running` or `requires_action`) is
now left to the consumer, like any live turn. It ends at the interrupt's
trailing idle, or at the next prompt's echo if that idle lags, after the
output the interrupt flushed. The force-cancel backstop is armed for it
meanwhile.

Two consequences, both handled:
- **The trailing-idle debt:** the interrupted followup's result is
autonomous, so it records its trailing idle as owed. When that idle ends
the cancelled held turn, it now also pays the debt. Otherwise the debt
would absorb a later issue-#825 signal.
- **Usage:** a held turn can now be ended by the trailing idle, the echo
hand-off, stream end or the backstop, not only by `cancel()`. All of
them take the outcome from one rule, `cancelledOutcome`: a turn that
already has its result keeps that result's usage and `_meta`, as the
inline settle did (issue #844). Today the session's tally equals that
recorded outcome anyway, since followup results aren't accumulated.
Stream end already treats the stored one as authoritative.

## Testing

- **New:** `src/tests/turn-events.test.ts` cancels a held turn
mid-followup. The interrupt flushes more of the summary, then the
followup's result and idle arrive. It asserts:
  - the flushed output comes before the turn ends `cancelled`;
- a wedged next turn still fails at its un-owed idle (issue #825), so no
trailer debt leaked.
- **Updated:** the two existing tests that cancel a held turn in a live
cycle (running, and blocked on a permission request) expected the prompt
to resolve at `cancel()`. Their scripts send the interrupt's trailer
only after the next prompt's echo, so the turn now ends at that echo
hand-off. They keep their issue #825 assertion, and one now also checks
the usage the turn keeps (issue #844).
- **Unchanged:** the tests for an idle hold, which settles at `cancel()`
without pre-counting a trailer, pass as before.
- **Mutations:** settling a live held turn inline again, settling an
idle one at an idle that never comes, or skipping the debt payment each
fail at least one test.

`npm run build`, `npm run check`, `npm run test:run` (2244 passed).
2026-10-02 14:47:06 +00:00
2026-07-21 18:53:56 +02:00
2025-09-02 19:06:32 -03:00
2026-03-26 12:19:06 +01:00

ACP adapter for the Claude Agent SDK

npm

Use Claude Agent SDK from ACP-compatible clients!

This tool implements an ACP agent by using the official Claude Agent SDK, supporting:

  • Context @-mentions
  • Images
  • Tool calls (with permission requests)
  • Compact file changes through the negotiated AIR diff patch extension
  • Following
  • Edit review
  • TODO lists
  • Nested subagent transcripts
  • Interactive (and background) terminals
  • Custom Slash commands
  • Client MCP servers
  • /mcp in the chat: the MCP server status as a list. The adapter runs /mcp reconnect, /mcp enable, and /mcp disable through the SDK control API, because Claude Code refuses them in SDK mode. A reconnect of an ACP server that needs authentication starts MCP OAuth through URL elicitation
  • Session-scoped long-running goals for AIR through the goal extension under _meta.jetbrains.air.goal
  • Structured errors, recovery, and warnings through the opt-in session failure extension
  • Concrete model and effort defaults through the opt-in recommended config value extension
  • Tool permission presentation, editable choices, and durable effects through the permission extension
  • One fact per field in every tool call report, as the ACP tool call contract defines
  • All AIR extensions, capabilities, and _meta keys: AIR extensions

Learn more about the Agent Client Protocol.

To try changes that have landed on main but are not released yet, install from the preview channel — every push to main publishes one. See docs/RELEASES.md.

npm install @agentclientprotocol/claude-agent-acp@preview

Subagent sessions

Subagents are exposed only after bilateral capability negotiation. Until the released ACP SDKs preserve the draft clientCapabilities.subagents field, a supporting client may advertise nativeSubagentSessions in _meta.jetbrains.air.capabilities; the adapter mirrors the capability in its initialize response. The canonical field remains supported and takes precedence once it is available. Without either client signal, Agent/Task lifecycle keeps its legacy ordinary ACP tool-call representation and child interactions stay on the root session. Clients that use the historical _meta["subagent-transcript"] capability or forwardSubagentText session option retain the flattened child transcript behavior. See AIR extensions.

Contribution Policy

This project does not require a Contributor License Agreement (CLA). Instead, contributions are accepted under the following terms:

By contributing to this project, you agree that your contributions will be licensed under the Apache License, Version 2.0. You affirm that you have the legal right to submit your work, that you are not including code you do not have rights to, and that you understand contributions are made without requiring a Contributor License Agreement (CLA).

Description
Pull mirror of agentclientprotocol/claude-agent-acp (Apache-2.0): the ACP bridge that lets Buzz drive Claude Code
Readme 107 MiB
Languages
TypeScript 99.2%
JavaScript 0.6%
Shell 0.2%