Commit Graph

2 Commits

Author SHA1 Message Date
Ben Brandt
98b17c28c8 refactor: advertise sessions on the experimental ACP v2 surface (#1239)
Step 4's last session piece for opt-in ACP v2 support (after #1237,
#1238). The v2 surface now serves the whole v2 session baseline, so its
`initialize` response advertises `capabilities.session`. Until now a
conforming v2 client wouldn't call the session methods. Nothing changes
on ACP v1, and the v2 surface stays behind
`CLAUDE_AGENT_ACP_EXPERIMENTAL_V2`.

## Change

`V2_SESSION_CAPABILITIES` is a constant, since it describes what the v2
surface serves:
- **the session baseline:** `session/new`, `list`, `resume` (with replay
since #1237), `close`, `prompt`, `cancel`, and `update`;
- **`session/delete` and `additionalDirectories`.** Not `session/fork`
yet, which v1 advertises but the v2 surface doesn't serve;
- **prompt content and MCP transports,** as the agent's v1 `initialize`
lists them: images and embedded context; stdio and HTTP servers. v2 has
no SSE transport.

The top-level `_meta` (`promptQueueing`, `authStatus`) passes through as
before. `providers` stays unadvertised.

Every v2 golden file changes on its `initialize` line only.

## Release

Titled `refactor:` like the other v2 PRs, so it triggers no release on
its own. This is the point where the v2 surface becomes usable by a
conforming client, so `feat:` would also fit if you want the next
release to be a minor one.

## Verification

The handshake test pins the advertised capabilities, the schema check
validates every recorded `initialize` response, and build, lint and all
2386 tests pass.
2026-10-02 18:52:51 +02:00
Ben Brandt
23b8ebf8c2 refactor: report tool calls on the experimental ACP v2 surface (#1231)
First part of step 4 of opt-in ACP v2 support (#1217, #1219, #1226,
#1230): the v2 surface now sends the agent's tool calls. Nothing changes
on ACP v1.

## Change

`src/v2/tool-call.ts` maps the agent's v1 tool call reports to v2:
- **One message.** v1 reports a new tool call with `tool_call` and
changes it with `tool_call_update`. v2 has one `tool_call_update` that
upserts by `toolCallId`, so both become that.
- **`null` is left out.** Both versions leave an omitted field unchanged
and replace `content` and `locations` as whole arrays. They differ on
`null`: in v1 it leaves a field unchanged (v1's `name` docs say so
explicitly), in v2 it clears it. The agent doesn't send `null` fields
today; a unit test pins the rule.
- **Content blocks pass through.** v2's content blocks are a superset of
v1's.
- **Diff and terminal content still throw**, until their own parts of
step 4.

## Scenario suite

Of the 30 scenarios that stopped at their first `tool_call`:

| Status now | Scenarios |
|---|---|
| passes | 17: `read`, `grep-and-glob`, `bash-error`, `bash-background`,
`skill`, `ask-user-question`, `memory-recall`, `permission-denied`,
`tool-progress`, `task-output-and-stop`, four subagent scenarios, the
three compaction scenarios |
| stops at a permission request (step 5) | 9 |
| stops at diff content (next part of step 4) | 4 |

The 17 new golden files are in
`src/tests/acp-scenarios/__snapshots__/v2/`.

**One new check.** In v1, a `tool_call_update` for an id the client
hasn't seen is an orphan, and clients drop it. In v2 the same message
creates a tool call. A stray update would show up as a blank tool call,
and the schema can't catch that. So every scenario recording, including
those that stop, must give each tool call a title in its first report.
It passes everywhere; making the first report lose its title fails it in
30 scenarios.

## Noted for later, not changed here

- **`_meta` becomes stored state.** In v1, a report's `_meta` belongs to
that report. In v2 it's stored on the tool call, and a concrete value
replaces it. So a v2 client keeps only the last report's
`_meta.claudeCode`: in `permission-denied`, the denial's `toolResponse`
is replaced by the result's `{ toolName }`. No v2 client reads these
keys yet.
- **v2 has a `cancelled` tool status.** v1 reports an interrupted tool
as `failed` with `_meta.claudeCode.nonExecutionKind`. Mapping it to
`cancelled` means deciding where that fact lives.

## Verification

Build, lint and all 2353 tests pass.
2026-10-02 17:20:13 +02:00