Compare commits

...

69 Commits

Author SHA1 Message Date
Slapoguzov Alexander
8bbdea6cc6 fix: honor configured fallback for unavailable restored modes (#1520)
A restored transcript mode such as `dontAsk` is recognized by the Claude
SDK but absent from the ACP mode catalog. It previously reached
initialization and was forced to Manual, skipping a supported
`permissions.defaultMode` from settings.

Use the same catalog for restored-mode selection and initialization.
Supported restored modes retain precedence; an unavailable restored mode
falls back to an available configured mode, then Manual. Explicit
initial modes, bypass restrictions, model-specific Auto handling, and
new-session configuration behavior remain unchanged.

Validation: TypeScript build, ESLint, Prettier, and 243 focused tests
pass. Added regressions cover helper selection plus v1 and v2 responses;
nine checks fail against unchanged production code.

The full local suite **FAILED**: 3,549 passed, 20 failed, 30 skipped.
Four selected failures also reproduce on unchanged base: three
session-index cases (watcher setup, live notification, SDK path-list
parity) and one background-work case. The remaining 16 failures were not
individually classified. No test timeouts or product behavior were
changed to suppress them. Live CLI and IDE permission-enforcement
acceptance remain unverified.

Co-authored-by: Codex <codex@openai.com>
2026-11-03 10:40:14 +01:00
Ben Brandt
1e986b526d refactor: give each session one owner of its background work (#1517)
Follow-up to #1510, #1511 and #1512. Clients receive exactly what they
did before.

## Why

The v2 background work grew as about 14 free functions taking `(session,
sessionId, …)` and reaching into six `Session` fields. Four of them it
kept itself: the task set, terminals, tool calls, and
`reportsBackground`. Three belong to other modules:
`liveBackgroundTasks`, the subagent map, and `toolUseCache`. The repo
guidelines ask for one module that owns the state.

## What changed

- **One owner per session.** `SessionBackgroundWork`, stored on
`Session.background`, owns the task set, terminals, tool calls, output
tails, and whether the client gets the lists. It has one method per
event: a Bash result, tool results, a task's end or pause, a new task
set, and a closed query.
- **The reporter only filters.** The object builds each session's
report, so `BackgroundWorkReporter` only drops unchanged lists and
forwards the rest.
- **No reference back to the session.** A recreated session (sign-out
respawn, provider change) gets a new `Session` under the same id, so an
old consumer must not reach the new one. Callers pass in the state other
modules own.
- **No `toolUseCache` dependency.** Whether a task's launching call has
returned used to be judged by the call still being in `toolUseCache`,
which Claude Code prunes at the tool's result. The object now records
returned calls itself, capped at 1024 like the other per-session sets.
- **Stricter typing on the output path.** Typing the terminals map
properly exposed that the parsed `outputFilePath` was loosely typed, so
it is now checked to be a string.
- **Test doubles.** The hand-built `Session` doubles in
`acp-agent.test.ts` and `session-doubles.ts` now carry the new required
field.

## Tests

- The existing suite passes unchanged.
- One new v2 test covers a task whose `task_started` arrives after its
call returned, so it is listed as `_claude/task`. A first version of
this refactor recorded "returned" per task and silently missed that
case. The test fails against that version, and against listing tasks
before their call returns.

`npm run check`, `npm run build` and `npm run test:run` pass (3556
passed, 30 skipped).
2026-11-02 19:52:25 +01:00
Ben Brandt
53e3eec013 feat: stream the output of v2 background commands (#1512)
Follow-up to #1510 and #1511, phase 2b of the v2 [Background Work
RFD](https://agentclientprotocol.com/rfds/v2/background-work).

Claude Code streams no output of a Bash command that went to the
background: it writes the output to the task's output file. So the
command's terminal showed only the Bash result's placeholder ("Command
running in background with ID …") until the command exited. The v2 spec
says the Agent **SHOULD** keep a listed terminal's output current
whenever it can read the process output.

## What a v2 client gets

- **Snapshot:** the file's first text replaces the placeholder as a
`terminal_output` snapshot.
- **Chunks:** later text is appended as `terminal_output_chunk`s.
- **Task end:** the rest of the file is read before the terminal's
`exitStatus`, as the spec asks.
- **Query close:** reading stops.

## Design

- New `src/background-output.ts` (`BackgroundOutputTail`) follows one
file:
- It polls every 500 ms, because watching a file is not reliable on
every platform and a missing file just means nothing was written yet.
- It reads at most 256 KiB per read, so a burst of output doesn't block
the event loop. Each read is sent before the next, including when
reading to the end at the task's end.
- Past 1 MiB of unread output (a command writing faster than the polls
read, about 512 KiB/s), it skips to the file's last 1 MiB and sends it
as a replacing snapshot, so the terminal shows the latest output without
a gap in it. A file that shrinks is read again from its start, also as a
snapshot.
- Its timer is unref'd, so a long-running command doesn't keep the agent
process alive.
- A `StringDecoder` holds a UTF-8 character split across reads until its
last byte is written.
  - Reads are serialised.
- `background-work.ts` starts the tail once the Bash result's own
updates are sent, so the first output replaces the placeholder rather
than coming before it. It finishes the tail before the terminal's exit
and stops it when the query closes. `acp-agent.ts` only gets wiring.
- The output path comes from the Bash result's text, the only place the
SDK gives it before the task ends. The existing
`asyncTaskOutputFilePath` parser only accepts an absolute path ending in
`tasks/<taskId>.output` for this task. Without a path, the terminal
keeps the placeholder as before.
- v2 only (`reportsBackground`), like the rest of the background work.
v1 clients that render terminals through the `_meta` extension could use
it too. That is a small follow-up, but it changes what v1 clients
receive.

## Tests

- Nine unit tests for `BackgroundOutputTail`:
  - the replacing first read, and appends
  - a file that appears later, and a file that never appears
  - a UTF-8 character split across reads
  - finishing, an empty output, and stopping
  - a backlog past 1 MiB, and a file that shrinks
- One v2 test over a real connection with a real output file. It checks
that the placeholder is replaced, later output arrives as chunks, and
the rest arrives before the exit.
- I checked by mutation that removing the read-before-exit, the
replacing snapshot, the UTF-8 decoder, the backlog cap or the shrink
restart each fails a test.

`npm run check`, `npm run build` and `npm run test:run` pass (3555
passed, 30 skipped).
2026-11-02 19:37:49 +01:00
acp-release-bot[bot]
981bf9a28a chore(main): release 0.100.0 (#1485)
🤖 I have created a release *beep* *boop*
---


##
[0.100.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.99.1...v0.100.0)
(2026-11-02)


### Features

* list v2 background tool calls
([#1511](https://github.com/agentclientprotocol/claude-agent-acp/issues/1511))
([4d79b9f](4d79b9ffbc))
* report subagent working directories
([#1496](https://github.com/agentclientprotocol/claude-agent-acp/issues/1496))
([eb78afa](eb78afa855))
* report v2 background work
([#1510](https://github.com/agentclientprotocol/claude-agent-acp/issues/1510))
([5565259](5565259771))
* report why Claude Code could not start a session
([#1508](https://github.com/agentclientprotocol/claude-agent-acp/issues/1508))
([38cf656](38cf656fc9))


### Bug Fixes

* align session/cancel, session/close, and v2 setup with the ACP v2 spec
([#1494](https://github.com/agentclientprotocol/claude-agent-acp/issues/1494))
([f50598a](f50598a39c))
* forward v2 elicitation values and read dismissed v2 permission prompts
as a refusal
([#1493](https://github.com/agentclientprotocol/claude-agent-acp/issues/1493))
([3bc6711](3bc671193c))
* keep native Windows paths in v2 git patches
([#1492](https://github.com/agentclientprotocol/claude-agent-acp/issues/1492))
([c3428de](c3428de1bf))
* preserve live session settings across provider refreshes
([#1474](https://github.com/agentclientprotocol/claude-agent-acp/issues/1474))
([e01a288](e01a288384))
* reject unreadable skill manifests
([#1482](https://github.com/agentclientprotocol/claude-agent-acp/issues/1482))
([99a6e76](99a6e76158))
* report the fresh context's usage when ExitPlanMode clears context
([#1484](https://github.com/agentclientprotocol/claude-agent-acp/issues/1484))
([2fd46c8](2fd46c866d))
* update to @agentclientprotocol/sdk 2.2.0
([#1491](https://github.com/agentclientprotocol/claude-agent-acp/issues/1491))
([1d2b92f](1d2b92feae))
* update to @agentclientprotocol/sdk 2.4.0
([#1509](https://github.com/agentclientprotocol/claude-agent-acp/issues/1509))
([720bbb3](720bbb3e4c))
* update to Claude Agent SDK 0.3.316 (Claude Code 2.1.316)
([#1507](https://github.com/agentclientprotocol/claude-agent-acp/issues/1507))
([1d03064](1d0306468b))
* validate session, prompt, config, and auth requests per the latest ACP
spec
([#1495](https://github.com/agentclientprotocol/claude-agent-acp/issues/1495))
([d2ef490](d2ef490a31))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-11-02 15:33:56 +01:00
Ben Brandt
4d79b9ffbc feat: list v2 background tool calls (#1511)
Follow-up to #1510, phase 2a of the v2 [Background Work
RFD](https://agentclientprotocol.com/rfds/v2/background-work).

A Monitor, Workflow or backgrounded MCP call returns as soon as its task
starts, so its tool call completed at its result while the task ran on.
#1510 listed such tasks as `_claude/task` items, which clients don't
recognise. This PR lists them as the tool calls that started them, as
the RFD's adapter table describes.

## What a v2 client gets

| When | The tool call |
|---|---|
| Tool result, while the task is in Claude Code's live background task
set | Reports `in_progress` instead of `completed`, and is listed as a
`tool_call` item |
| While listed | Advertises `capabilities.cancel`. `tool_call/cancel`
stops the task with `stopTask`, or returns `-32602` if the call isn't
listed (also when its task ended mid-cancel) |
| `task_updated` with `paused` / `running` | `paused` / `in_progress` |
| The task ends | `completed`, `failed`, or `cancelled` for a stopped
(`killed`) task, with `capabilities: null`, before the list drops it.
The same happens when the live set drops the task without an end event
(`completed`, since nothing was reported), or the query closes
(`cancelled`) |
| `session/resume` | The call's current state, rebuilt from its tool
use, then its capability, before the snapshot |

The task is matched to its call through the structured `taskId` of a
Monitor or Workflow result, or through the live task whose
`task_started.tool_use_id` names the call (backgrounded MCP calls).

`session/cancel` no longer stops background workflows for sessions that
report their background work, because listed work must keep running.
Clients stop them with `tool_call/cancel` instead. v1 and AIR are
unchanged.

A task without such a call is still a `_claude/task` item. It is now
listed only once its `task_started` has arrived and the call that
started it, if any, has returned. Before that, the foreground work is
still waiting on that call, and the spec says foreground work is not
listed.

## Design

- `background-work.ts` holds the logic: registering background tool
calls, pause and end updates, the shared `cancelBackgroundWork` for
`terminal/cancel` and `tool_call/cancel`, and the resume states.
- `acp-agent.ts` only gets wiring.
- `paused` travels as a cast v1 status, alongside the existing
`V2_CANCELLED`.
- `V2Background` advertises and clears the cancel capability.

## Tests

- Six new v2 tests, using the real CLI's event order (tool use, task
joins the live set, `task_started`, tool result) and the `killed` +
`stopped` pair for a stopped task. They cover:
  - listing, pause and resume, and the final status
  - `tool_call/cancel`
  - a query that closes
  - a listed Workflow left running on `session/cancel`
  - resume
  - the `_claude/task` fallback
- I checked by mutation that each new guard fails a test when removed.

`npm run check`, `npm run build` and `npm run test:run` pass (3545
passed, 30 skipped).

## Still to come

- Tailing background Bash `output_file` into terminal output (phase 2b).
- `ScheduleWakeup` / `CronCreate`, which need a signal from the SDK.
- The SHOULD gap about follow-up work is being relaxed upstream in
agentclientprotocol/agent-client-protocol#2745.
2026-11-02 14:40:01 +01:00
Ben Brandt
5565259771 feat: report v2 background work (#1510)
Implements phase 1 of the ACP v2 [Background Work
RFD](https://agentclientprotocol.com/rfds/v2/background-work), now part
of schema v2.0.0-alpha.13. v2 sessions report the work Claude Code keeps
running independently of foreground work as `background` on every
`state_update`.

## What the client gets

The lists are built from Claude Code's live background task set
(`background_tasks_changed`). Tasks Claude Code marks `ambient` are
skipped, since they are housekeeping. The root session and each subagent
session (subagents RFD form) has its own list. Each task is listed once,
in the list of the session that runs it:

| Work | Listed as |
|---|---|
| A Bash command that went to the background | Its display terminal,
from the Bash result until its task ends. While listed, the terminal
advertises `capabilities.cancel`, and `terminal/cancel` stops its task
with `stopTask`. |
| A detached subagent | Its child session, while it works, and while it
is idle with background work of its own. The child reports its own list
on its `state_update` and on the parent's `subagent_update.state`
mirror. |
| Any other task (`Monitor`, `Workflow`, `mcp_task`, …) | A
`_claude/task` item with `taskId` and `description`. Its tool call ends
at its tool result, so it cannot be a `tool_call` item yet. |

The ordering rules the spec requires:
- **List changes:** the state is re-sent with the new list and no stop
reason, so a repeated `idle` is not a new completion.
- **Before a terminal is listed:** a `terminal_update` advertising
`cancel` goes out first.
- **Before a terminal leaves the list:** its `exitStatus` goes out, with
`capabilities: null`. This also holds when the level drops the task
without an end event, or when the query closes (process death, stream
error, sign-out recreation, `session/close`).
- **A child and its parent:** the child's own list goes out before the
parent's list drops it.
- **`session/resume`:** each listed subagent's current `subagent_update`
and each listed terminal's current `terminal_update` come before the
state snapshot.
- **`terminal/cancel`:** it returns `-32602` for a terminal that is not
listed, including one whose command ended while the stop was in flight.

## Gating

Only v2 sessions with `perTaskStopAffordance` report lists. That is the
default for a non-AIR v2 client. Without it, Claude Code's interrupt
kills background tasks, which would break the rule that `session/cancel`
leaves listed work running. v1 and AIR are unchanged.

## Design

- `src/background-work.ts` owns the lists. It builds them from session
state and publishes changes. `acp-agent.ts` only gets wiring calls: one
sync per message-loop iteration, the level update, terminal exits when a
query closes, and the `terminal/cancel` entry point.
- `src/v2/background.ts` adds the lists to v2 state updates and to
subagent state mirrors. The cancel capability lives in `V2Terminals`,
which already owns terminal state.
- `NativeSubagentRuntime.currentUpdate` builds a child's live
`subagent_update` for resume.

## Not in this PR

- **SHOULD gap:** when finished background work starts a follow-up
cycle, the spec wants `running` in the same snapshot that drops the
item. We send `idle []` and then `running`, because the adapter can't
know in advance that Claude Code will start a follow-up. The RFD
acknowledges this.
- **Phase 2:**
- List `Monitor`, `Workflow` and `mcp_task` as `tool_call` items that
stay `in_progress` (or `paused`), with `tool_call/cancel`.
  - Tail background Bash `output_file` into terminal output.
- **Phase 3:** `ScheduleWakeup` / `CronCreate`. The SDK gives no signal
for when one fires or is removed.

## Tests

- New v2 tests cover:
- terminal listing, cancel advertisement, exit ordering, ambient tasks,
and a level drop without an end event
- query death, `terminal/cancel` including the race, and resume for both
terminals and subagents
- a background subagent, an idle subagent kept listed by its own
command, `_claude/task` items, and sessions without
`perTaskStopAffordance`
- I checked by mutation that the guards for each required ordering fail
a test when removed.
- The v2 golden files gain `background: []`, and the subagent ones also
gain child lists.

`npm run check`, `npm run build` and `npm run test:run` pass (3540
passed, 30 skipped).
2026-11-02 14:16:10 +01:00
Ben Brandt
720bbb3e4c fix: update to @agentclientprotocol/sdk 2.4.0 (#1509)
Updates `@agentclientprotocol/sdk` from 2.2.0 to 2.4.0 (schema v1.29.1
and v2.0.0-alpha.13).

## What changed in the protocol

- **v1:** the stable schema and spec pages only changed wording. There
are no requirement changes.
- **v2 `session/cancel`:** the Client no longer answers pending
permission requests with `cancelled`. The Agent MUST stop waiting for
the cancelled work's permission requests and SHOULD cancel each one with
`$/cancel_request`.
- **v2 `session/close`:** the existing MUST to cancel pending
elicitations with `$/cancel_request` before responding now covers
permission requests too.

The adapter already did both: the tool call's signal and the session's
abort controller cancel the request, and a hand-written abort race
stopped the wait. Until now, no test checked that a v2 client actually
receives `$/cancel_request` for a permission request.

## Changes

- Permission requests and elicitations, on both v1 and v2, now use SDK
2.4.0's `stopWaitingOnCancel` option instead of `raceWithAbort`. The SDK
sends `$/cancel_request`, rejects at once with `-32800`, and quietly
drops any late response. Every caller already maps a rejection after an
abort to "Tool use aborted" or to `cancel`, so nothing else changes.
`raceWithAbort` is still used for the eager `tool_call` notification.
- **Tests:**
- **v2:** the existing tests where a `session/cancel` or `session/close`
withdraws a held request now also check that the client received
`$/cancel_request`. For `session/close`, it must arrive before the
response.
- **v1:** new tests over a real SDK connection with a client that holds
requests. These cover the v1 `ClientConnection` wiring, which the
mocked-client tests bypass.
- The v1 mock clients that "ignore cancellation" now model the SDK's
behaviour with a `heldRequest` helper.
- Removed one test that only covered `raceWithAbort` handling a
rejection from the client.
- I checked that the new assertions fail if the `stopWaitingOnCancel`
option is removed or v2 permission requests stop passing their
cancellation signal.

## Follow-ups (not in this PR)

- v2 background work: the `background` list on state updates, the
`paused` status, and `tool_call/cancel` / `terminal/cancel`. The AIR
`asyncTasks` extension stays as it is.
- The new unstable v1 methods (`session/set_title`,
`session/archive`/`unarchive`, `session/status`, `auth/status`).

`npm run check`, `npm run build` and `npm run test:run` pass (3520
passed, 30 skipped).
2026-11-02 11:41:32 +00:00
Ben Brandt
38cf656fc9 feat: report why Claude Code could not start a session (#1508)
When Claude Code refuses to start (invalid proxy URL, wrong organization
sign-in, a required sandbox that can't run, …), `session/new`,
`session/load`, `session/resume` and `session/fork` used to fail with a
generic `Claude Code process exited with code 1. stderr: …` error.

This sets `CLAUDE_CODE_STARTUP_FAILURE_RESULTS=1` on the query env, so
the CLI writes an error result with a `startup_failure_reason` before
exiting on a known startup failure. The SDK then rejects
`initializationResult()` with an error that carries
`startupFailureReason`. The new `src/startup-failure.ts` maps it to an
ACP error:

| `startupFailureReason` | ACP error |
|---|---|
| `gateway_signin_required`, `org_pin_mismatch`, `org_config_refused` |
`authRequired` (signing in again, or with another account, fixes it) |
| `cwd_unavailable` | `invalidParams` (matches the existing cwd
validation) |
| any other reason | `internalError` |

The message is the CLI's own text, which says what to fix (e.g. `Invalid
proxy URL in HTTPS_PROXY: … Fix or unset HTTPS_PROXY and restart Claude
Code.`), and `data.startupFailureReason` carries the reason. Errors
without a reason (older CLIs, unknown causes) pass through unchanged,
and the existing `sessionNotFound` mapping for resumes is untouched.

Checked against the real CLI (SDK 0.3.316): with `HTTPS_PROXY="not a
url"`, `initializationResult()` rejects with `startupFailureReason:
"proxy_invalid"` when the variable is set, and with a plain exit-code
error without it.

## Testing

- v1: `create-session-options.test.ts` covers the env var, each mapping,
and pass-through of errors without a reason.
- v2: `acp-v2.test.ts` covers the `session/new` error over the wire.
- `npm run build`, `npm run check`, `npm run test:run` pass. The
subprocess integration tests also passed locally with this change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-02 11:23:33 +00:00
Ben Brandt
1d0306468b fix: update to Claude Agent SDK 0.3.316 (Claude Code 2.1.316) (#1507)
Updates dependencies to their latest versions.

- `@anthropic-ai/claude-agent-sdk` 0.3.312 → 0.3.316 (bundles Claude
Code 2.1.316)
- `@types/node` 26.6.5 → 26.6.6, `eslint` 10.13.0 → 10.14.0, `globals`
17.13.0 → 17.14.0
- `npm update` for transitive patch/minor bumps in the lockfile
- Bumps the "as of" version in two comments about SDK type gaps
(`Query.getSettings()` still missing from the declarations; `unanswered`
still only in the AskUserQuestion output schema). Both were re-checked
against 0.3.316.

TypeScript 7.0.2 is left out: `@typescript-eslint` 8.72.1 supports
TypeScript `<6.1.0`.

## Changelog review (SDK 0.3.313–0.3.316, Claude Code 2.1.313–2.1.316)

No required changes. The SDK type changes only add fields and union
members (`sandbox_required_unavailable` startup failure reason,
`blockWritesOutsideWorkingDirectories` / `blockReadsExemptPaths`
settings, `pending_elicitation_requests` on `initialize`, alpha
`prewarm()` in `/core`), none of which the adapter reads or needs. No
new `fast_mode_disabled_reason` values accompany the Opus 4.8 fast mode
deprecation.

Follow-up planned separately: use `CLAUDE_CODE_STARTUP_FAILURE_RESULTS`
/ `startup_failure_reason` to report typed startup failures to clients.

## Testing

`npm run build`, `npm run check`, `npm run test:run` pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-02 12:18:36 +01:00
Andrey Bragin
c6de68797b docs: add design-fit and test-duplication guidelines to AGENTS.md (#1480)
Adds guidelines to `AGENTS.md` that raise the baseline quality of new
PRs and features. Changes should fit the existing architecture instead
of working around it, respect the session's turn lifecycle, stay
compatible with existing clients, and come with focused, non-duplicated
tests. Authors and their agents can check these points before review.

- **Coding guidelines** replaces the **Code comments** section and
covers only the code itself. The comment rules are now one bullet, which
also requires comments and docs to be true on every code path they
describe. It adds rules for refusing requests with an actionable
`RequestError`, not reporting success for work started without awaiting
it, not letting results depend on cache state, and stating unenforced
ordering assumptions.
- **Architecture and design fit** is a new section. Build on the module
that owns the concept, put new features and components in dedicated
modules instead of `acp-agent.ts`, prefer extending existing mechanisms,
reuse sibling logic so paths that handle the same input (live stream and
`session/load` replay, command list and dispatch, `prompt` and `steer`)
apply the same rules, keep ACP v1 and v2 on one implementation, use
`startTurn`'s busy check and the turn queue for anything that sends
`session/update`, avoid branching on stale local copies of remote state,
don't shadow existing commands, keep backward compatibility, and explain
any unavoidable workaround.
- **Testing guidelines** is a new section. It adds rules against
duplicate or parametrizable tests, asks to reuse the existing test files
and helpers, to build test doubles from what the real Claude Code CLI
sends, to cover both protocol versions when client-visible output
changes, and to have a test for each behavior the docs promise.
2026-11-02 11:40:07 +01:00
Ben Brandt
eb78afa855 feat: report subagent working directories (#1496)
Schema v1.28.0 / v2.0.0-alpha.11 adds `cwd` and `additionalDirectories`
to `subagent_update` (agentclientprotocol/agent-client-protocol#2593,
#2660). When a child runs in a different directory from its parent, for
example an Agent call with `isolation: "worktree"`, the Agent must
report the child's `cwd` before sending child traffic that depends on
it.

**Signal**
- The agent reads the `cwd` of the child's own hooks, matched by
`agent_id`. The existing per-child configuration hook already runs on
SubagentStart, PreToolUse, PostToolUse and the others.
- Claude Code runs SubagentStart before the child's first model request
and waits for the hook, so the `cwd` reaches the client before the
child's first tool call.
- `worktreePath` in the Agent tool result arrives only when the child
finishes, which is too late.

**Behaviour (RFD form only; AIR is unchanged)**
- `cwd` is sent only when the child's directory differs from its
parent's, compared with `realpath`. A hook that runs before the
announcement puts the `cwd` in the announcing `subagent_update`.
Otherwise a separate `subagent_update { cwd }` follows.
- A child that moves back to its parent's directory gets `cwd: null`. A
child whose hook `cwd` changes later, for example after a `cd` that
Claude Code keeps, is reported again, since that is where it now runs.
- `additionalDirectories` is never sent. Claude Code gives subagents the
session's additional directories, so inheriting them is accurate.
- Replay uses the child's `.meta.json` (`cwd`, `worktreePath`,
`inheritedWorktreePath`) and the first `cwd` in its transcript. After
the load response, a child whose directory no longer exists gets `cwd:
null`.
- v2 passes the fields through unchanged.

**Not covered**
- `remote` isolation, which fires no local hooks.
- After a reconnect to a live session, the `cwd` the runtime holds is
not sent again.
2026-10-30 16:05:38 +01:00
Ben Brandt
d2ef490a31 fix: validate session, prompt, config, and auth requests per the latest ACP spec (#1495)
Request validation from the ACP schema v1.28.0 / v2.0.0-alpha.11 review.
Each change has a test.

**Paths**
- `session/list` rejects a relative `cwd` with -32602. Without a session
index it passes `includeWorktrees: false`, so the filter matches only
sessions of exactly that directory, not other git worktrees of the same
repo.
- Resume, load and fork check `cwd` and `additionalDirectories` before
anything else. Before, a resume with a bad `cwd` tore down the live
session and then failed, and a fork copied the transcript first.
- Relative `additionalDirectories` entries are rejected with -32602.

**Session setup (v2)**
- Uses the SDK's `assertSupportedMcpServers`, which also rejects
duplicate MCP server names with -32602.
- Rejects an empty `prompt` with -32602.
- The v2 initialize response no longer advertises the AIR `asyncTasks`
capability or the `goal` entry, since the v2 app has no handlers for
`_session/async_task/stop` or `_session/goal`. v1 AIR is unchanged.

**Errors**
- `session/fork` of an unknown session returns -32002 with
`data.sessionId` instead of -32603. This matches the Claude SDK's
"Session … not found" and "Invalid sessionId" messages.
- `authenticate` with an unknown `methodId` returns -32602 instead of
-32603.
- `set_config_option` rejects a value whose type doesn't match the
advertised option type. A `type: "boolean"` option takes `true`/`false`,
and a select option takes a string. Model aliases such as `"opus"` are
still accepted.
- An AIR `session/delete` of a session that has no transcript now
succeeds instead of failing with a generic error.

**Prompts and commands**
- v1 `session/prompt` rejects `audio` and unknown content blocks with
-32602 before queueing them. Before, they were silently dropped.
- Advertised command names that contain whitespace are dropped, and of
duplicate names only the first is kept.
- The `/mcp:server:prompt` rewrite now accepts arguments that span
lines, and applies only to the first text block.
2026-10-30 16:03:58 +01:00
Ben Brandt
f50598a39c fix: align session/cancel, session/close, and v2 setup with the ACP v2 spec (#1494)
**`session/cancel` cancels only earlier prompts**
(agentclientprotocol/agent-client-protocol#2685)
- `cancelTurns` used to read the turn queue again after two awaits: the
compaction interrupt and publishing the subagent state. A prompt that
arrived during those awaits was cancelled too, which is -32800 on v2.
- Each prompt now gets a sequence number when `startTurn` receives it.
The cancel records the count before its first await and cancels only
turns at or below it. That includes a prompt received earlier that
`startTurn` had not queued yet.
- Known gap: if the cancelled turn ends during those awaits and Claude
Code starts the newer prompt, the `query.interrupt()` that follows can
still stop that prompt. Fixing it means skipping the interrupt when
nothing the cancel covers is still running, which also affects
cancelling background work, so it is left for later.

**`session/close` cancels pending requests first**
(agentclientprotocol/agent-client-protocol#2683)
- MCP elicitations, AskUserQuestion, the refusal-fallback dialog and
permission requests now combine their callback signal with the session's
`abortController.signal`.
- `teardownSession` therefore sends `$/cancel_request` for each of them
before `session/close` responds, instead of relying on Claude Code to
abort its callback signals in time.

**v2 setup responses** (agentclientprotocol/agent-client-protocol#2688,
#2603)
- If reading the commands for the setup response fails, the agent now
sends them in an `available_commands_update` after the response. Before,
the client was never sent them.
- On v2, MCP OAuth starts after the `session/new`, resume or fork
response is sent, so its URL elicitation no longer reaches the client
before the response.

Not done: withdrawing a held prompt with `$/cancel_request`. The user
message is already in Claude Code's input once it is queued, and
removing it safely needs `cancel_async_message`, which not every CLI
version has. agentclientprotocol/agent-client-protocol#2636 allows
ignoring the cancel and sending the normal response.
2026-10-30 16:03:03 +01:00
Ben Brandt
3bc671193c fix: forward v2 elicitation values and read dismissed v2 permission prompts as a refusal (#1493)
**Elicitation content** (agentclientprotocol/agent-client-protocol#2595)
- v2 lets an elicitation answer hold any JSON value, such as an object.
The agent checked accepted content with the v1 guard, which allows only
strings, numbers, booleans and string arrays. A v2 accept with an object
therefore reached the MCP server as `{ action: "accept", content: {} }`.
- The agent now forwards the accepted content unchanged, and the MCP
server checks it against its own schema. If Claude Code's MCP client
still uses the older value types, it rejects such an answer with an
error instead of passing on empty content.
- AskUserQuestion reads only string, number and boolean picks, so it no
longer shows an object as `[object Object]`.
- The MCP OAuth url-mode check now tests `action !== "accept"` instead
of the v1 guard.

**Permission `cancelled` on v2**
(agentclientprotocol/agent-client-protocol#2582, #2658)
- In v2, `cancelled` also means the user dismissed the prompt or the
client declined it by policy.
- When no `session/cancel` is in progress, a foreground v2 `cancelled`
is now denied as "User refused permission to run tool", the same denial
background subagents already get, and the turn goes on. Before, Claude
Code reported it as "Tool permission request failed".
- During a `session/cancel` it is still aborted. v1 is unchanged.
- Unknown v2 outcomes map to `cancelled`, so they are also denied now
instead of aborted.
2026-10-30 14:44:34 +00:00
Ben Brandt
c3428de1bf fix: keep native Windows paths in v2 git patches (#1492)
ACP v2 now says each `git_patch` path, once unquoted, is the same native
absolute path string as the matching `changes` entry
(agentclientprotocol/agent-client-protocol#2650).

The v2 form converted `C:\work\App.ts` to `C:/work/App.ts`, so on
Windows the patch and `changes[].path` named different strings. The v2
form now keeps the native path, which Git quoting writes as
`"C:\\work\\App.ts"`. Git's own form, which the AIR extension uses,
still uses forward slashes and the `a/`/`b/` prefixes.
2026-10-30 14:39:21 +00:00
Ben Brandt
1d2b92feae fix: update to @agentclientprotocol/sdk 2.2.0 (#1491)
Moves to ACP schema v1.28.0 and v2.0.0-alpha.11.

- v2 dropped JSON-RPC batching, so `AnyWireMessage` is gone. The test
harnesses use `AnyMessage`, and a request id can now be `null` in the
types.
- The v2 elicitation schema now requires `properties`. A forwarded MCP
schema without them, such as `{ "type": "object" }`, gets an empty map,
so clients on SDK 2.2.0 don't reject it.

Runtime changes that come with the SDK and need no agent code:
- The SDK handles a repeated `initialize`, rejects batches, and rejects
messages sent before initialization. A failed `initialize` now leaves
the connection open.
- v2 `usage_update.cost` is now a patch field, where omitted means
unchanged. The agent sends `cost` only on the result update, so the
client keeps showing the last cost during a turn.

Follow-up PRs for the spec changes in this range are stacked on this
branch.
2026-10-30 14:39:09 +00:00
xiaofeilovecoding
99a6e76158 fix: reject unreadable skill manifests (#1482)
Fixes #1478.

Skip SKILL.md candidates when opening or reading the file fails, while
preserving readable unnamed manifests. Do not cache failed reads so
later resolutions can recover.

Adds nine resolver regressions for open/read errors, descriptor cleanup,
recovery, and valid alternatives.

Validation (Windows, Node 24.18.0):
- Nine new tests, TypeScript build, and ESLint pass.
- Full suite: 3,354 passed, 57 failed, 52 skipped. The same 57 failures
reproduce on unmodified 52c241e in the affected files.
- npm run check fails only on unchanged CLAUDE.md: Windows checked out
the symlink as a plain target file. The baseline has the same issue.

Co-authored-by: xiaofeilovecoding <fxy850328059@gmail.com>
2026-10-30 13:57:42 +01:00
Slapoguzov Alexander
e01a288384 fix: preserve live session settings across provider refreshes (#1474)
## Problem

When the provider changes on a running agent (`providers/set`,
`providers/disable`, or logout with a provider override), every open
session gets a new Claude Code process. For example, this happens after
a BYOK key edit or a proxy recovery. The new process started from the
original `session/new` params only. The session lost the model, effort,
mode, Fast mode, ultracode and agent the user had picked, while the
client still showed the old values (IJAI-4592).

## Fix

- After the new process starts, the session's previous choices are
applied again through the same code paths as client requests. The client
then gets a `config_option_update` with the actual configuration.
- If the new provider doesn't support a choice (for example, the new key
has no access to that model), the choice is logged and skipped. The
client sees the real value.
- Effort: only the user's current pin comes back. An effort the user
reset to Default, or that a model switch dropped, stays off.
- Mode, config changes and restarts run one at a time per session. A
choice sent during a restart lands after the restored ones. Other
sessions are not blocked.
- The respawn after a sign-out goes through the same path, so it keeps
the choices too.
- When a provider change overlaps that respawn, it replaces the current
recovered session and holds waiting prompts until the provider update
finishes. Those prompts then run once on the latest route with the
restored choices.
- Closing a session while it restarts no longer leaves a running process
behind.

## Tests

- `provider-session-configuration.test.ts`: the overlapping
sign-out/provider change and two waiting prompts held until the final
replacement initializes.

- `provider-session-configuration.test.ts`: restore order, effort edge
cases, races, and close or dispose during a restart.
- `session-hooks.test.ts`: real Claude Code CLI against a local fake
API. After a refresh, the next request goes out with the chosen model
and `effort: high`.

## Validation

[CI on the published
follow-up](https://github.com/agentclientprotocol/claude-agent-acp/actions/runs/40989963422)
passes formatting, lint, build and tests: 3,474 passed, 32 skipped,
including 37 provider, 107 authorization and five real CLI-hook tests
against a local fake API. Independent review passed; 151 scoped tests
also covered seven additional recovery/failure schedules.

<details><summary>Local baseline comparison and scope</summary>

The added overlap regression failed on the preceding draft and passes
with this repair. Complete local runs used identical locked dependencies
and two workers: baseline 3,431 passed / 19 failed / 30 skipped;
candidate 3,433 passed / the same 19 failed / 30 skipped. All local
failures are existing session-index cases; filesystem watching reported
EMFILE, with two SDK path-list assertions also failing. The complete
Linux CI run above passes.

This follow-up is a normal appended commit preserving the existing
implementation. The PR remains a draft. Released-package Air acceptance
follows merge and release.

</details>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Aleksandr Slapoguzov <aleksandr.slapoguzov@jetbrains.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:57:13 +01:00
Ben Brandt
2fd46c866d fix: report the fresh context's usage when ExitPlanMode clears context (#1484)
Choosing one of `ExitPlanMode`'s "clear context" options restarts
Claude's conversation with only the plan. The ACP session ID and the
turn stay the same, and the adapter resets its context count to 0, but
it didn't tell the client. Clients kept showing the old context's usage
until the fresh context first reported its own, so nothing on the client
side showed that the context had been cleared.

`publishSessionState` now sends a `usage_update` for the empty fresh
context (`used: 0`, the fresh session's context window as `size`) right
after the `current_mode_update` and `config_option_update` it already
sends. `used: 0` slightly understates the fresh context, which already
holds the system prompt and the plan, until Claude's next usage report.

Testing:
- `npm run build`, `npm run test:run`, and `npm run check` pass.
- `clear-context-coordinator.test.ts` checks the update's order and
values.
2026-10-29 22:09:59 +01:00
acp-release-bot[bot]
0f78dbbdd0 chore(main): release 0.99.1 (#1472)
🤖 I have created a release *beep* *boop*
---


##
[0.99.1](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.99.0...v0.99.1)
(2026-10-29)


### Bug Fixes

* add approval id to plan
([#1469](https://github.com/agentclientprotocol/claude-agent-acp/issues/1469))
([4b4b2c4](4b4b2c4de7))
* allow forking sessions into a different directory
([#1483](https://github.com/agentclientprotocol/claude-agent-acp/issues/1483))
([8bd0bc9](8bd0bc9b03))
* detect "Run /login" login-expired messages as authRequired
([#1481](https://github.com/agentclientprotocol/claude-agent-acp/issues/1481))
([fccaebc](fccaebcf87))
* mark plugin, bundled and nested Claude skills as skills for AIR
([#1473](https://github.com/agentclientprotocol/claude-agent-acp/issues/1473))
([52c241e](52c241e715))
* title subagent sessions by their task
([#1479](https://github.com/agentclientprotocol/claude-agent-acp/issues/1479))
([7dcb629](7dcb62923d))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-10-29 20:34:22 +01:00
Ben Brandt
8bd0bc9b03 fix: allow forking sessions into a different directory (#1483)
Fix `session/fork` when the requested working directory differs from the
directory containing the source session's transcript, such as a
subthread in another worktree.

The Claude Agent SDK's `dir` option restricts **source transcript
lookup**; it does not select the fork's runtime directory. Passing ACP's
destination `cwd` therefore prevents the SDK from finding a source
session stored elsewhere.

- Omit `dir` from both SDK fork calls, active-history lookup, and
full-history import so every fork-resolution path searches all projects.
- Continue starting the forked query with the requested `cwd`, MCP
servers, and other session settings.
- Add 12 real-SDK tests using isolated temporary transcripts, covering
same- and cross-directory forks through latest history, active messages,
inactive branches, fingerprints, and live message maps, plus
missing-message errors. Check copied history, fresh UUIDs, and an
unchanged source transcript.
- Extend ACP v2 coverage to verify same- and cross-directory forks
remain immediately promptable and use the requested runtime directory.

## Validation

- Before the fix, all six cross-directory regression cases failed while
the six same-directory cases passed; after the fix, all 12 pass.
- `npm run check` — passed.
- `npm run build` — passed.
- `npm run test:run -- --reporter=dot --silent` — passed: 60 files,
3,437 tests; 4 files and 30 tests skipped.
- `git diff --check` — passed.

Final validation ran on macOS with Node 24. Earlier Windows validation
passed all fork-related tests but encountered unrelated path-separator
assertions and checkout line-ending formatting failures; those are
outside this change.

No authenticated Claude prompt or end-to-end Delta subthread test was
run. The new transcript tests use the real SDK without starting Claude
Code; ACP query execution is mocked in the protocol test.
2026-10-29 19:20:46 +00:00
Mark Tkachenko
52c241e715 fix: mark plugin, bundled and nested Claude skills as skills for AIR (#1473)
AIR lists a command as a skill only when the adapter finds its
`SKILL.md`, and the resolver only probed `<cwd|~>/.claude/skills`.
Plugin skills, CLI-bundled skills and nested `.claude/skills` therefore
reached AIR as plain commands.

The adapter now takes the paths from Claude Code's own skill list,
`getContextUsage().skills.skillFrontmatter`:
- a skill with a `path` gets `kind: "skill"` and `skillPath`; one
without (bundled `simplify`, remote connections) gets `kind` only;
- a skill not in the list falls back to the existing `SKILL.md` lookup;
builtin commands like `compact` stay unmarked;
- the read runs in the background (the first call takes about 1 s) on
the first `system/init` and on every `commands_changed`, and the command
list is re-sent when the result changes.
2026-10-29 14:42:08 +01:00
Ben Brandt
fccaebcf87 fix: detect "Run /login" login-expired messages as authRequired (#1481)
The CLI now reports an expired login (no refresh token) as:

Login expired · Run /login to sign in again, or re-authenticate your
Anthropic profile

and tags the synthetic assistant message `error: "invalid_request"`, not
`authentication_failed`. The adapter only recognised the exact phrase
"Please run /login", so this message streamed to the client as agent
text and the turn failed with a generic internal error instead of
`RequestError.authRequired()`. Clients never learned auth was required,
so they couldn't offer their terminal auth methods. "Not logged in · Run
/login" had the same gap.

Changes:
- Add `isLoginRequiredText()`, which matches "run /login"
case-insensitively, and use it in `isSyntheticLoginMessage()` (live loop
and replay) and the two result-text checks.
- Exclude synthetic usage-limit banners from the login match, since some
mention `/login` to switch accounts and have their own handling.

Tests cover both new wordings, the usage-limit exclusion, and an
end-to-end turn where the `invalid_request`-tagged "Login expired"
message must reject with `authRequired` and not leak "/login" text to
the client.
2026-10-29 13:17:53 +00:00
Ben Brandt
7dcb62923d fix: title subagent sessions by their task (#1479)
A subagent's `subagent_update.title` was its type (`general-purpose`),
or
`name (type)`, so a client showing the title couldn't tell siblings
apart;
the task was only in `description`. The RFD makes the title the parent's
display label and the description the child's role or purpose.

The title is now the agent's name when it has one, else the task's short
description, else its type. The description is the type, unless the type
is already the title.
2026-10-29 11:16:05 +00:00
Kirill Alekhnovich
4b4b2c4de7 fix: add approval id to plan (#1469)
Include the approval tool call ID in each Air plan ID so the client can
bind the plan to its review. Keep the legacy plan ID for other clients.
2026-10-28 16:34:01 +01:00
acp-release-bot[bot]
106e3031e3 chore(main): release 0.99.0 (#1456)
🤖 I have created a release *beep* *boop*
---


##
[0.99.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.98.0...v0.99.0)
(2026-10-28)


### Features

* Add thinking display configuration
([#1081](https://github.com/agentclientprotocol/claude-agent-acp/issues/1081))
([2f1f4d1](2f1f4d1441))
* send embedded blob resources in prompts to Claude
([#1460](https://github.com/agentclientprotocol/claude-agent-acp/issues/1460))
([d0ee308](d0ee30813f))
* tell the client when an MCP server fails to start
([#1464](https://github.com/agentclientprotocol/claude-agent-acp/issues/1464))
([54b35ee](54b35ee0f4))
* update to Claude Agent SDK 0.3.312
([#1471](https://github.com/agentclientprotocol/claude-agent-acp/issues/1471))
([4cb8906](4cb8906d32))
* **v2:** send the current session info on session/resume
([#1462](https://github.com/agentclientprotocol/claude-agent-acp/issues/1462))
([f6ed4a4](f6ed4a4f7d))


### Bug Fixes

* deny a background permission request that the client cancels
([#1466](https://github.com/agentclientprotocol/claude-agent-acp/issues/1466))
([f646c82](f646c821f1))
* don't report decline when the client can't render an MCP elicitation
([#1465](https://github.com/agentclientprotocol/claude-agent-acp/issues/1465))
([54b869c](54b869c990))
* list the current model among the model config option's values
([#1463](https://github.com/agentclientprotocol/claude-agent-acp/issues/1463))
([8e6fe92](8e6fe92a98))
* report EnterPlanMode tool calls with the switch_mode kind
([#1467](https://github.com/agentclientprotocol/claude-agent-acp/issues/1467))
([93c1f43](93c1f43519))
* return ACP error codes for unknown sessions and invalid config values
([#1458](https://github.com/agentclientprotocol/claude-agent-acp/issues/1458))
([c2e85cb](c2e85cb580))
* update ACP SDK to 2.1.0 (schema v1.27.0 / v2.0.0-alpha.10)
([9f9aebf](9f9aebf44b))
* **v2:** keep clients in sync with config changes and replayed tool
calls
([#1461](https://github.com/agentclientprotocol/claude-agent-acp/issues/1461))
([2a0c188](2a0c188a0a))
* **v2:** reject MCP transports and prompt content the agent doesn't
advertise
([#1459](https://github.com/agentclientprotocol/claude-agent-acp/issues/1459))
([2480888](2480888b90))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-10-28 15:24:45 +01:00
Ben Brandt
4cb8906d32 feat: update to Claude Agent SDK 0.3.312 (#1471)
Updates `@anthropic-ai/claude-agent-sdk` from 0.3.310 to 0.3.312, which
bundles Claude Code 2.1.312.

## Changes

- `@anthropic-ai/claude-agent-sdk` 0.3.310 → 0.3.312
- `@typescript-eslint/eslint-plugin` and `@typescript-eslint/parser`
8.72.0 → 8.72.1
- `npm update` for transitive packages (`hono`, `ignore`, `ip-address`,
`magic-string`, `postcss`, `qs`)
- TypeScript stays on 6.0.3. typescript-eslint 8.72.1 only supports
TypeScript below 6.1, so 7.0.2 has to wait.

No code changes were needed for the upgrade itself.

## Report why an AskUserQuestion question went unanswered

SDK 0.3.312 lets an AskUserQuestion answer set
`annotations[question].unanswered` to `skipped`, `replied_in_chat` or
`dismissed`. A session can receive this only if its `system/init` lists
the `ask_user_question_unanswered_v1` capability, because an older CLI
refuses the whole answer when the field is present.

- The session records the capability from `system/init`, the same way it
already records `msg_lifecycle_v1`.
- `applyAskElicitationResponse` takes a `reportUnanswered` option. If
the user declines the form, every question is marked `dismissed`. If the
user accepts it with a question left blank, that question is marked
`skipped`.
- The model does not see this field. It goes into the tool result for
clients and transcripts.
- `annotations` is now typed with `AskUserQuestionOutput`, since as of
0.3.312 only the output schema lists `unanswered`.

`replied_in_chat` is not used because ACP has no way to report that the
user replied in chat instead.

## Testing

`npm run build`, `npm run check` and `npm run test:run` pass. Two new
tests in `src/tests/elicitation.test.ts` cover a declined form and an
accepted form with a blank question.
2026-10-28 15:20:03 +01:00
Ben Brandt
d0ee30813f feat: send embedded blob resources in prompts to Claude (#1460)
Stacked on #1457 (SDK 2.1.0).

`promptToClaude` used to drop embedded `resource` blocks whose content
is a base64 `blob`, even though the agent advertises `embeddedContext`.
They are now sent to Claude:

- **Images** (PNG, JPEG, including `image/jpg`, GIF, WebP) become
`image` blocks, right after the `[@name](uri)` link.
- **PDFs** become `document` blocks.
- **Text types** (`text/*`, JSON, XML, YAML, TOML, JS/TS, shell, SQL,
GraphQL, `+json`/`+xml`) are decoded with their `charset` parameter
(UTF-8 by default, BOM dropped) and sent like text resources.
- **Other types:** v2 rejects them with `-32602`. v1 still skips them,
as before.

Known gap: a replayed user PDF does not appear in the replayed history,
because `document` blocks have no ACP mapping on replay.
2026-10-28 12:45:07 +00:00
Ben Brandt
54b35ee0f4 feat: tell the client when an MCP server fails to start (#1464)
Stacked on #1457 (SDK 2.1.0).

Agents SHOULD let the user know when a client-provided MCP server fails
(agentclientprotocol/agent-client-protocol#2512). Before this PR,
failures only showed up in `/mcp`.

- **On `system`/init:**
  - A client-provided server with status `failed` gets a warning notice.
- A `needs-auth` server gets an info notice when the OAuth flow can't
run: the client has no URL elicitation, or the query has no
`mcpAuthenticate`.
- **Once per server per session.** This holds across repeated inits and
across the session rebuilt after a sign-out.
- **Servers from the user's settings are left to `/mcp`.** Otherwise a
broken one would be reported in every session.
- **Rendering:** v1 clients without the notices capability get a
transcript line instead.
2026-10-28 13:43:34 +01:00
Ben Brandt
93c1f43519 fix: report EnterPlanMode tool calls with the switch_mode kind (#1467)
Stacked on #1457 (SDK 2.1.0).

EnterPlanMode tool calls now use `kind: "switch_mode"`, like
ExitPlanMode (agentclientprotocol/agent-client-protocol#2543). The title
is unchanged.
2026-10-28 13:40:19 +01:00
Ben Brandt
f646c821f1 fix: deny a background permission request that the client cancels (#1466)
Stacked on #1457 (SDK 2.1.0).

Per agentclientprotocol/agent-client-protocol#2508, `session/cancel`
cancels foreground work, and the client answers every pending permission
request with `cancelled`, including background ones.

- **Before:** for a background subagent, which keeps running after the
interrupt, `canUseTool` threw "Tool use aborted". The CLI turned that
into "Tool permission request failed", which the model can take for a
transient failure and retry.
- **Now:** a `cancelled` outcome for background work (decided by
`blocksForeground`, including subagents nested under a backgrounded one)
returns a `deny` with `decisionClassification: "user_reject"`.
- **Foreground** work still throws, and its turn ends through
`query.interrupt()`.

Open question: AskUserQuestion's `cancel` still throws for background
work.
2026-10-28 13:39:46 +01:00
Ben Brandt
54b869c990 fix: don't report decline when the client can't render an MCP elicitation (#1465)
Stacked on #1457 (SDK 2.1.0).

When an MCP server's elicitation is forwarded and the client answers
with an error (for example `-32602` for a form it can't render,
agentclientprotocol/agent-client-protocol#2556), the agent used to tell
the MCP server `decline`. That reported a choice the user never made.

- It now returns `cancel`.
- The same applies to the two early returns where the user never saw
anything: a mode the client didn't advertise, and a request that can't
be converted to ACP. These apply to v1 clients too.
- `decline` now only comes from an actual decline.
2026-10-28 13:39:04 +01:00
Ben Brandt
8e6fe92a98 fix: list the current model among the model config option's values (#1463)
Stacked on #1457 (SDK 2.1.0).

A select option's `currentValue` MUST be one of its `options`
(agentclientprotocol/agent-client-protocol#2557). A session on a model
outside the picker (resumed onto a model the allowlist excludes, or a
refusal fallback) used to report a `currentValue` with no matching
entry.

- The live model is now added as an option, named from its model info,
or by its raw id when there is none.
- The entry is not added to `availableModels`, so it disappears after
you switch to a listed model.
- The special case in `setSessionConfigOption` that accepted an unlisted
`currentValue` is removed.
- Allowlisted models still win alias resolution, so no model outside the
allowlist becomes selectable.
- Effort, Agent, Fast mode and ultracode already kept the invariant.
2026-10-28 13:37:54 +01:00
Ben Brandt
f6ed4a4f7d feat(v2): send the current session info on session/resume (#1462)
Stacked on #1457 (SDK 2.1.0).

On v2 `session/resume`, the agent now sends `session_info_update` with
the current title and `updatedAt`, after any replay and before the
response (agentclientprotocol/agent-client-protocol#2492).

- The agent already sent this update when the title changed. Only the
snapshot on resume was missing.
- The title comes from the same source as `session/list`. If a title has
been published but not yet stored, for example a generated title still
being written, that title is sent instead of the raw first prompt.
- A read failure is logged and never fails the resume.
- v1 is unchanged. The shared helper `publishCurrentSessionInfo` would
let v1 adopt it later.
2026-10-28 13:37:04 +01:00
Ben Brandt
2a0c188a0a fix(v2): keep clients in sync with config changes and replayed tool calls (#1461)
Stacked on #1457 (SDK 2.1.0).

- **`config_option_update` on `session/set_config_option`:** v2 now
sends the update before the response, as `session-config-options.mdx`
says Agents SHOULD. This is v2 only, because v1 tests intentionally
assert that no duplicate is sent.
- **Replayed tool calls reset their fields:** a v1 `tool_call` for an id
the client may already hold (during `session/resume` replay, or an id
already reported on this connection) now sends `null` for the fields it
leaves out: `name`, `title`, `kind`, `status`, `content`, `locations`,
`rawInput`, `rawOutput`. In v2 an omitted field keeps its old value and
`null` resets it (agentclientprotocol/agent-client-protocol#2544).
- This also fixes an earlier bug: the replay's `_meta: null` reset never
reached the wire, because `v2ToolCallPatch` dropped every `null`.
  - The only golden that changes is the replay one.
2026-10-28 13:36:21 +01:00
Ben Brandt
2480888b90 fix(v2): reject MCP transports and prompt content the agent doesn't advertise (#1459)
Stacked on #1457 (SDK 2.1.0).

The v2 spec says Agents MUST reject with `-32602` a transport or content
type they did not advertise. Before this PR, both of these were accepted
and then silently dropped:

- **`acp` MCP servers:** the agent advertises only `stdio` and `http`.
The v1 setup skipped these servers.
- **`audio` blocks:** not advertised, and `promptToClaude` ignored them.
This also applies to a v2 `systemPrompt`, since it is converted the same
way.

Embedded blob `resource` blocks are still accepted; see the blob
resources PR.
2026-10-28 13:35:01 +01:00
Ben Brandt
c2e85cb580 fix: return ACP error codes for unknown sessions and invalid config values (#1458)
Stacked on #1457 (SDK 2.1.0).

The spec now defines these error codes. They are a MUST in v2 and a
SHOULD in v1 (agentclientprotocol/agent-client-protocol#2504,
agentclientprotocol/agent-client-protocol#2497). Before this PR, plain
`Error`s reached clients as `-32603`.

- **`RequestError.sessionNotFound` (`-32002`, `data.sessionId`)** for an
unknown session in prompt, steer, sign-out respawn, `set_config_option`
and `set_mode`. Resume/load of a missing session switches from
`resourceNotFound` (same code, `data.uri`) to `sessionNotFound`; the
fallback that starts a fresh query checks the same code, so it behaves
as before.
- **`RequestError.invalidParams` (`-32602`)** for an unknown config
option, an invalid value (including fast mode and ultracode) and an
unknown or unavailable mode. A failure inside the CLI's
`setPermissionMode` stays `-32603`, since it is an internal error.
- **`session/close` of an unknown session still succeeds.**
agentclientprotocol/agent-client-protocol#2575 makes close idempotent,
and a new test pins this.

This also changes v1 responses, which now carry the codes the v1 schema
defines instead of `-32603`.
2026-10-28 13:34:31 +01:00
Ben Brandt
9f9aebf44b fix: update ACP SDK to 2.1.0 (schema v1.27.0 / v2.0.0-alpha.10)
v2 available commands now carry the required `type: "command"` tag;
SDK 2.1.0 receivers drop commands without it. Read tool locations clamp
`offset: 0` to line 1 since v2 lines are 1-based, and the session_message
casts are gone now that the SDK types senderMessageId/recipientMessageId.
2026-10-28 13:28:03 +01:00
Levin Rickert
2f1f4d1441 feat: Add thinking display configuration (#1081)
Opus 5 and Sonnet 5 are hiding thinking display by default. This PR adds
an env variable to allow users to override this default.

Fixes https://github.com/zed-industries/zed/issues/56375
2026-10-28 07:42:44 +00:00
acp-release-bot[bot]
c90f856c11 chore(main): release 0.98.0 (#1442) 2026-10-27 19:48:23 +01:00
dependabot[bot]
059d76e14a ci(deps): Bump @modelcontextprotocol/sdk from 1.32.1 to 1.32.2 in the minor group (#1449)
Bumps the minor group with 1 update:
[@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk).

Updates `@modelcontextprotocol/sdk` from 1.32.1 to 1.32.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/modelcontextprotocol/typescript-sdk/releases">@​modelcontextprotocol/sdk's
releases</a>.</em></p>
<blockquote>
<h2>1.32.2</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: add explicit | undefined to Transport optional properties (v1.x
backport of <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/1766">#1766</a>)
by <a
href="https://github.com/kishormorol"><code>@​kishormorol</code></a> in
<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2814">modelcontextprotocol/typescript-sdk#2814</a></li>
<li>[v1.x] fix: allow registering after connect() when the capability
was declared by <a
href="https://github.com/claude"><code>@​claude</code></a>[bot] in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2958">modelcontextprotocol/typescript-sdk#2958</a></li>
<li>chore: bump version to 1.32.2 by <a
href="https://github.com/claude"><code>@​claude</code></a>[bot] in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/3089">modelcontextprotocol/typescript-sdk#3089</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/kishormorol"><code>@​kishormorol</code></a>
made their first contribution in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2814">modelcontextprotocol/typescript-sdk#2814</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/modelcontextprotocol/typescript-sdk/compare/1.32.1...1.32.2">https://github.com/modelcontextprotocol/typescript-sdk/compare/1.32.1...1.32.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3b73021a0a"><code>3b73021</code></a>
chore: bump version to 1.32.2 (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/3089">#3089</a>)</li>
<li><a
href="fa56d815ac"><code>fa56d81</code></a>
[v1.x] fix: allow registering after connect() when the capability was
declare...</li>
<li><a
href="6919e9526a"><code>6919e95</code></a>
fix: add explicit | undefined to Transport optional properties (v1.x
backport...</li>
<li>See full diff in <a
href="https://github.com/modelcontextprotocol/typescript-sdk/compare/1.32.1...1.32.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@modelcontextprotocol/sdk&package-manager=npm_and_yarn&previous-version=1.32.1&new-version=1.32.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-27 17:05:30 +01:00
Mark Tkachenko
4324dce4be fix: publish Monitor async tasks as monitors (#1452)
Since #1249 the adapter hides every async task started by the `Monitor`
tool, so Air cannot link the task to the Monitor row and counts the
session as having no background work.

Monitor tasks (and SDK `local_monitor` tasks) are published again, but
as monitors instead of shells: `taskType: "monitor"`, `showInTranscript:
false`, `canStop: false`. That keeps the two reasons from #1249: no
empty shell card, and no Stop that only breaks the agent's wait.
Background Bash, `mcp` and subagent tasks are unchanged.
2026-10-27 16:40:16 +01:00
Ben Brandt
c7c80f7b26 fix(v2): leave the tool call status out of permission subjects (#1447)
## Summary

A v2 `session/request_permission` sends its subject as the tool call
that `renderer.permissionToolCall` builds. For a client that isn't AIR,
that is the upstream shape, which includes `status: "pending"`.

The call is already emitted before the request
(`ensureToolCallEmitted`), and its status belongs to the call's own
updates. The v2 spec describes the subject as "a `toolCall` update
containing details about the operation". A client that applies it as an
update, as that shape invites, would see a call that already reported
progress go back to `pending`. `permissionToolCall` now leaves `status`
out for v2 clients; v1 clients keep the upstream shape.

## Changes

- `src/tool-calls/renderer.ts`: no `status` in a v2 client's permission
tool call.
- v2 golden files: each permission request's subject loses
`"status":"pending"`. Nothing else changes.

## Testing

- `npx vitest run`: 3379 passed, 30 skipped.
- `npm run check`: clean.

Found while adding participant approval of permission requests to Delta,
which applies a tool call subject as an update to its call.
2026-10-27 13:07:48 +00:00
Ben Brandt
8f43ac8089 fix: settle the tool call of a SandboxNetworkAccess permission request (#1448)
## Summary

`SandboxNetworkAccess` is a permission-only callback. The SDK asks under
a synthetic tool use id and never sends a `tool_result` for it.
`ensureToolCallEmitted` emits a `tool_call` for that id before asking,
so clients have the call the request refers to (#851). But nothing ever
ended that call, so clients showed a tool call stuck at `pending`
(related: #1061).

Once the request is answered, the adapter now sends a `tool_call_update`
with the call's final status: `completed` when the network access was
allowed, `failed` otherwise. Delivery follows the same routing as the
call's other updates: the native subagent deliverer when there is one,
otherwise the call's session.

A request withdrawn by a cancel, rather than answered, still leaves the
call unsettled, as before.

## Changes

- `src/acp-agent.ts`: `settleSandboxNetworkCall`, called after the
permission response is decoded.
- `src/tests/acp-scenarios/compare.ts`: documents the new update as an
allowed difference from origin/main for plain and Zed clients.
- `network-permission` golden files (v2 and AIR) gain the one settling
update.

## Testing

- `npx vitest run`: 3379 passed, 30 skipped.
- `npm run check`: clean.

Independent of #1447. The two touch the same v2 golden file, so
whichever lands second needs a snapshot refresh.
2026-10-27 13:55:40 +01:00
Slapoguzov Alexander
0ce42f2c31 feat: add batch session metadata lookup (#1408)
A session browser may load only the 50 most recent conversations, while
pinned sessions and saved folders refer to much older ones. After a
restart, the client still has those IDs but needs their current titles,
locations and archive state to rebuild the saved views. Finding a
handful of retained sessions by paging through the entire history
becomes increasingly expensive as that history grows.

Add `_session/batch_get` to retrieve metadata for known session IDs in
one request; the client chooses how many IDs to send per request.
Successful results use the same row format as `session/list`, including
archived sessions and sessions in worktrees, so the client can merge
them with its recent page and apply its usual display filters.

The response contains the sessions whose metadata is available, in
request order. Other IDs are simply omitted. The client can refresh the
rows it receives while keeping its saved references, so pinned and
folder views stay independent of the recent-list window.

---------

Co-authored-by: Aleksandr Slapoguzov <aleksandr.slapoguzov@jetbrains.com>
2026-10-27 00:44:18 +01:00
dependabot[bot]
782e463ff4 ci(deps): Bump the minor group with 3 updates (#1440)
Bumps the minor group with 3 updates:
[on-finished](https://github.com/jshttp/on-finished),
[rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).

Updates `on-finished` from 2.4.1 to 2.4.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/jshttp/on-finished/releases">on-finished's
releases</a>.</em></p>
<blockquote>
<h2>2.4.2</h2>
<h2>Highlights</h2>
<h3>🔧 Node.js deprecation</h3>
<ul>
<li><code>isFinished(res)</code> now reads <code>writableEnded</code>
instead of the deprecated <code>finished</code> property (<a
href="https://nodejs.org/api/deprecations.html#DEP0136">DEP0136</a>),
with a fallback to <code>finished</code> on older Node.js versions. No
behavior change for consumers. (<a
href="https://redirect.github.com/jshttp/on-finished/issues/54">#54</a>)</li>
</ul>
<h3>📦 Packaging</h3>
<ul>
<li><code>HISTORY.md</code> is no longer shipped in the npm tarball. (<a
href="https://redirect.github.com/jshttp/on-finished/issues/109">#109</a>)</li>
<li><code>package.json</code> now declares <code>&quot;type&quot;:
&quot;commonjs&quot;</code> explicitly. (<a
href="https://redirect.github.com/jshttp/on-finished/issues/108">#108</a>)</li>
</ul>
<h3>🧪 Testing and CI</h3>
<ul>
<li>Added edge case tests for <code>isFinished</code> with invalid
input. (<a
href="https://redirect.github.com/jshttp/on-finished/issues/88">#88</a>)</li>
<li>Node.js 19 through 26 added to the test matrix. (<a
href="https://redirect.github.com/jshttp/on-finished/issues/53">#53</a>,
<a
href="https://redirect.github.com/jshttp/on-finished/issues/106">#106</a>)</li>
<li><code>mocha</code> bumped to 12.0.3 and the CI pipeline refreshed.
(<a
href="https://redirect.github.com/jshttp/on-finished/issues/103">#103</a>)</li>
<li>OSSF Scorecard enabled and security best practices applied to the
workflows. (<a
href="https://redirect.github.com/jshttp/on-finished/issues/47">#47</a>,
<a
href="https://redirect.github.com/jshttp/on-finished/issues/60">#60</a>)</li>
<li>Dependabot now groups GitHub Actions updates. (<a
href="https://redirect.github.com/jshttp/on-finished/issues/104">#104</a>)</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Update node 8 version and pin nyc. Also added missing 9.x to
ci-tests… by <a
href="https://github.com/carpasse"><code>@​carpasse</code></a> in <a
href="https://redirect.github.com/jshttp/on-finished/pull/49">jshttp/on-finished#49</a></li>
<li>Add OSSF scorecard by <a
href="https://github.com/carpasse"><code>@​carpasse</code></a> in <a
href="https://redirect.github.com/jshttp/on-finished/pull/47">jshttp/on-finished#47</a></li>
<li>build: Node.js 19+ by <a
href="https://github.com/bjohansebas"><code>@​bjohansebas</code></a> in
<a
href="https://redirect.github.com/jshttp/on-finished/pull/53">jshttp/on-finished#53</a></li>
<li>ci: apply OSSF Scorecard security best practices by <a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/60">jshttp/on-finished#60</a></li>
<li>chore: add funding to package.json by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/on-finished/pull/76">jshttp/on-finished#76</a></li>
<li>use writableEnd instead finished by <a
href="https://github.com/bjohansebas"><code>@​bjohansebas</code></a> in
<a
href="https://redirect.github.com/jshttp/on-finished/pull/54">jshttp/on-finished#54</a></li>
<li>build(deps): bump ossf/scorecard-action from 2.0.6 to 2.4.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/85">jshttp/on-finished#85</a></li>
<li>build(deps): bump actions/upload-artifact from 3.1.3 to 4.6.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/64">jshttp/on-finished#64</a></li>
<li>build(deps): bump actions/checkout from 3.6.0 to 5.0.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/84">jshttp/on-finished#84</a></li>
<li>build(deps): bump actions/download-artifact from 4.3.0 to 5.0.0 by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/82">jshttp/on-finished#82</a></li>
<li>test: add edge case tests for isFinished with invalid input by <a
href="https://github.com/Ayoub-Mabrouk"><code>@​Ayoub-Mabrouk</code></a>
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/88">jshttp/on-finished#88</a></li>
<li>build(deps): bump actions/checkout from 5.0.0 to 6.0.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/93">jshttp/on-finished#93</a></li>
<li>build(deps): bump actions/upload-artifact from 4.6.2 to 5.0.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/92">jshttp/on-finished#92</a></li>
<li>build(deps): bump actions/download-artifact from 5.0.0 to 6.0.0 by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/91">jshttp/on-finished#91</a></li>
<li>build(deps): bump github/codeql-action from 2.23.2 to 4.31.6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/90">jshttp/on-finished#90</a></li>
<li>build(deps): bump coverallsapp/github-action from 2.3.6 to 2.3.7 by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/89">jshttp/on-finished#89</a></li>
<li>chore: group github actions updates by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/on-finished/pull/104">jshttp/on-finished#104</a></li>
<li>bump Mocha to version 12.0.3 by <a
href="https://github.com/rafapaezbas"><code>@​rafapaezbas</code></a> in
<a
href="https://redirect.github.com/jshttp/on-finished/pull/103">jshttp/on-finished#103</a></li>
<li>ci: add Node 25 and 26 to workflow by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/on-finished/pull/106">jshttp/on-finished#106</a></li>
<li>build(deps): bump the github-actions group with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/105">jshttp/on-finished#105</a></li>
<li>chore: add explicit type commonjs to package.json by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/on-finished/pull/108">jshttp/on-finished#108</a></li>
<li>chore: remove HISTORY.md from being packaged on publish by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/on-finished/pull/109">jshttp/on-finished#109</a></li>
<li>chore(ci): npm-publish via stage publish by <a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
in <a
href="https://redirect.github.com/jshttp/on-finished/pull/111">jshttp/on-finished#111</a></li>
<li>Release: 2.4.2 by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/on-finished/pull/110">jshttp/on-finished#110</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/carpasse"><code>@​carpasse</code></a>
made their first contribution in <a
href="https://redirect.github.com/jshttp/on-finished/pull/49">jshttp/on-finished#49</a></li>
<li><a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
made their first contribution in <a
href="https://redirect.github.com/jshttp/on-finished/pull/60">jshttp/on-finished#60</a></li>
<li><a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
made their first contribution in <a
href="https://redirect.github.com/jshttp/on-finished/pull/85">jshttp/on-finished#85</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/jshttp/on-finished/blob/master/HISTORY.md">on-finished's
changelog</a>.</em></p>
<blockquote>
<h1>2.4.2</h1>
<ul>
<li>Use <code>writableEnded</code> instead of deprecated
<code>finished</code>, with a fallback for older Node.js versions (<a
href="https://redirect.github.com/jshttp/on-finished/issues/54">#54</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9a73f9c1c0"><code>9a73f9c</code></a>
2.4.2 (<a
href="https://redirect.github.com/jshttp/on-finished/issues/110">#110</a>)</li>
<li><a
href="0ec28ddc1d"><code>0ec28dd</code></a>
chore(ci): npm-publish via stage publish (<a
href="https://redirect.github.com/jshttp/on-finished/issues/111">#111</a>)</li>
<li><a
href="4b8ac4669c"><code>4b8ac46</code></a>
chore: remove HISTORY.md from being packaged on publish (<a
href="https://redirect.github.com/jshttp/on-finished/issues/109">#109</a>)</li>
<li><a
href="9c5f7b805b"><code>9c5f7b8</code></a>
chore: add explicit type commonjs to package.json (<a
href="https://redirect.github.com/jshttp/on-finished/issues/108">#108</a>)</li>
<li><a
href="8c33090d5b"><code>8c33090</code></a>
build(deps): bump the github-actions group with 10 updates (<a
href="https://redirect.github.com/jshttp/on-finished/issues/105">#105</a>)</li>
<li><a
href="1ca32c56e7"><code>1ca32c5</code></a>
ci: add Node 25 and 26 to workflow (<a
href="https://redirect.github.com/jshttp/on-finished/issues/106">#106</a>)</li>
<li><a
href="4ea365ed85"><code>4ea365e</code></a>
deps: bump mocha to v12.0.3 and update CI pipeline (<a
href="https://redirect.github.com/jshttp/on-finished/issues/103">#103</a>)</li>
<li><a
href="673ff9fbb2"><code>673ff9f</code></a>
chore: group github actions updates (<a
href="https://redirect.github.com/jshttp/on-finished/issues/104">#104</a>)</li>
<li><a
href="500c84cec0"><code>500c84c</code></a>
build(deps): bump coverallsapp/github-action from 2.3.6 to 2.3.7 (<a
href="https://redirect.github.com/jshttp/on-finished/issues/89">#89</a>)</li>
<li><a
href="1dbd9cd615"><code>1dbd9cd</code></a>
build(deps): bump github/codeql-action from 2.23.2 to 4.31.6 (<a
href="https://redirect.github.com/jshttp/on-finished/issues/90">#90</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/jshttp/on-finished/compare/v2.4.1...v2.4.2">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for on-finished since your current version.</p>
</details>
<br />

Updates `rolldown` from 1.2.14 to 1.2.15
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rolldown/rolldown/releases">rolldown's
releases</a>.</em></p>
<blockquote>
<h2>v1.2.15</h2>
<h2>[1.2.15] - 2026-10-21</h2>
<h3>🚀 Features</h3>
<ul>
<li>rolldown_plugin_vite_resolve: add <code>isBundled</code> to pick the
browser-external stub format (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11260">#11260</a>)
by <a href="https://github.com/h-a-n-a"><code>@​h-a-n-a</code></a></li>
<li>dev: mark entry modules in the HMR module graph (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11252">#11252</a>)
by <a href="https://github.com/h-a-n-a"><code>@​h-a-n-a</code></a></li>
<li>mark module starts with <code>// &lt;id&gt;</code> comments instead
of <code>#region</code> pairs (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11235">#11235</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>plugin: stop <code>this.resolve</code> recursion between plugins
with <code>skipSelf: true</code> (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11181">#11181</a>)
by <a href="https://github.com/anzal1"><code>@​anzal1</code></a></li>
<li>rolldown_plugin_vite_resolve: handle external <code>require</code>
for <code>ssr.target: 'webworker'</code> (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11205">#11205</a>)
by <a
href="https://github.com/sapphi-red"><code>@​sapphi-red</code></a></li>
<li>rolldown_plugin_vite_build_import_analysis: only wrap dynamic
imports with static specifiers in __vitePreload (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10879">#10879</a>)
by <a href="https://github.com/smeng9"><code>@​smeng9</code></a></li>
<li>strict-execution-order: wrap eager modules that forward wrapped
definers (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11186">#11186</a>)
by <a href="https://github.com/hyfdev"><code>@​hyfdev</code></a></li>
<li>chunk-import-map: keep common chunk keys unique when enabling inline
common chunks (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11230">#11230</a>)
by <a href="https://github.com/hyfdev"><code>@​hyfdev</code></a></li>
<li>only register signal-exit handler when a trace subscriber is active
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11208">#11208</a>)
by <a
href="https://github.com/DaniilIsupov"><code>@​DaniilIsupov</code></a></li>
</ul>
<h3>🧪 Testing</h3>
<ul>
<li>fold hand-unrolled Rust test tables (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11212">#11212</a>)
by <a
href="https://github.com/Brooooooklyn"><code>@​Brooooooklyn</code></a></li>
</ul>
<h3>⚙️ Miscellaneous Tasks</h3>
<ul>
<li>deps: upgrade oxc to 0.155.0 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11228">#11228</a>)
by <a href="https://github.com/Dunqing"><code>@​Dunqing</code></a></li>
<li>rollup-tests: retriage failed tests (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11251">#11251</a>)
by <a
href="https://github.com/sapphi-red"><code>@​sapphi-red</code></a></li>
<li>deps: update rollup submodule for tests to v4.64.12 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11249">#11249</a>)
by <a
href="https://github.com/rolldown-guard"><code>@​rolldown-guard</code></a>[bot]</li>
<li>deps: update test262 submodule for tests (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11242">#11242</a>)
by <a
href="https://github.com/rolldown-guard"><code>@​rolldown-guard</code></a>[bot]</li>
</ul>
<h3>❤️ New Contributors</h3>
<ul>
<li><a href="https://github.com/anzal1"><code>@​anzal1</code></a> made
their first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/11181">#11181</a></li>
<li><a href="https://github.com/smeng9"><code>@​smeng9</code></a> made
their first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/10879">#10879</a></li>
<li><a
href="https://github.com/DaniilIsupov"><code>@​DaniilIsupov</code></a>
made their first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/11208">#11208</a></li>
</ul>
<p>Co-authored-by: shulaoda <a
href="mailto:165626830+shulaoda@users.noreply.github.com">165626830+shulaoda@users.noreply.github.com</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rolldown/rolldown/blob/main/CHANGELOG.md">rolldown's
changelog</a>.</em></p>
<blockquote>
<h2>[1.2.15] - 2026-10-21</h2>
<h3>🚀 Features</h3>
<ul>
<li>rolldown_plugin_vite_resolve: add <code>isBundled</code> to pick the
browser-external stub format (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11260">#11260</a>)
by <a href="https://github.com/h-a-n-a"><code>@​h-a-n-a</code></a></li>
<li>dev: mark entry modules in the HMR module graph (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11252">#11252</a>)
by <a href="https://github.com/h-a-n-a"><code>@​h-a-n-a</code></a></li>
<li>mark module starts with <code>// &lt;id&gt;</code> comments instead
of <code>#region</code> pairs (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11235">#11235</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>plugin: stop <code>this.resolve</code> recursion between plugins
with <code>skipSelf: true</code> (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11181">#11181</a>)
by <a href="https://github.com/anzal1"><code>@​anzal1</code></a></li>
<li>rolldown_plugin_vite_resolve: handle external <code>require</code>
for <code>ssr.target: 'webworker'</code> (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11205">#11205</a>)
by <a
href="https://github.com/sapphi-red"><code>@​sapphi-red</code></a></li>
<li>rolldown_plugin_vite_build_import_analysis: only wrap dynamic
imports with static specifiers in __vitePreload (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10879">#10879</a>)
by <a href="https://github.com/smeng9"><code>@​smeng9</code></a></li>
<li>strict-execution-order: wrap eager modules that forward wrapped
definers (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11186">#11186</a>)
by <a href="https://github.com/hyfdev"><code>@​hyfdev</code></a></li>
<li>chunk-import-map: keep common chunk keys unique when enabling inline
common chunks (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11230">#11230</a>)
by <a href="https://github.com/hyfdev"><code>@​hyfdev</code></a></li>
<li>only register signal-exit handler when a trace subscriber is active
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11208">#11208</a>)
by <a
href="https://github.com/DaniilIsupov"><code>@​DaniilIsupov</code></a></li>
</ul>
<h3>🧪 Testing</h3>
<ul>
<li>fold hand-unrolled Rust test tables (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11212">#11212</a>)
by <a
href="https://github.com/Brooooooklyn"><code>@​Brooooooklyn</code></a></li>
</ul>
<h3>⚙️ Miscellaneous Tasks</h3>
<ul>
<li>deps: upgrade oxc to 0.155.0 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11228">#11228</a>)
by <a href="https://github.com/Dunqing"><code>@​Dunqing</code></a></li>
<li>rollup-tests: retriage failed tests (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11251">#11251</a>)
by <a
href="https://github.com/sapphi-red"><code>@​sapphi-red</code></a></li>
<li>deps: update rollup submodule for tests to v4.64.12 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11249">#11249</a>)
by <a
href="https://github.com/rolldown-guard"><code>@​rolldown-guard</code></a>[bot]</li>
<li>deps: update test262 submodule for tests (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11242">#11242</a>)
by <a
href="https://github.com/rolldown-guard"><code>@​rolldown-guard</code></a>[bot]</li>
</ul>
<h3>❤️ New Contributors</h3>
<ul>
<li><a href="https://github.com/anzal1"><code>@​anzal1</code></a> made
their first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/11181">#11181</a></li>
<li><a href="https://github.com/smeng9"><code>@​smeng9</code></a> made
their first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/10879">#10879</a></li>
<li><a
href="https://github.com/DaniilIsupov"><code>@​DaniilIsupov</code></a>
made their first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/11208">#11208</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="89d821ca07"><code>89d821c</code></a>
release: v1.2.15 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11261">#11261</a>)</li>
<li><a
href="3b0841ef80"><code>3b0841e</code></a>
feat(rolldown_plugin_vite_resolve): add <code>isBundled</code> to pick
the browser-exter...</li>
<li><a
href="a973d41247"><code>a973d41</code></a>
chore(deps): upgrade oxc to 0.155.0 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11228">#11228</a>)</li>
<li><a
href="5bfb74e2b0"><code>5bfb74e</code></a>
feat(dev): mark entry modules in the HMR module graph (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11252">#11252</a>)</li>
<li><a
href="fb0a8db6f1"><code>fb0a8db</code></a>
fix(rolldown_plugin_vite_resolve): handle external <code>require</code>
for `ssr.target:...</li>
<li><a
href="547a5a733c"><code>547a5a7</code></a>
fix(rolldown_plugin_vite_build_import_analysis): only wrap dynamic
imports wi...</li>
<li><a
href="648591f0eb"><code>648591f</code></a>
feat: mark module starts with <code>// \&lt;id&gt;</code> comments
instead of <code>#region</code> pairs (...</li>
<li><a
href="3f0374181d"><code>3f03741</code></a>
fix(chunk-import-map): keep common chunk keys unique when enabling
inline com...</li>
<li><a
href="33a987d8bf"><code>33a987d</code></a>
fix: only register signal-exit handler when a trace subscriber is active
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/11">#11</a>...</li>
<li>See full diff in <a
href="https://github.com/rolldown/rolldown/commits/v1.2.15/packages/rolldown">compare
view</a></li>
</ul>
</details>
<br />

Updates `vite` from 8.3.4 to 8.3.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/releases">vite's
releases</a>.</em></p>
<blockquote>
<h2>v8.3.5</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>build:</strong> handle missing chunk import maps (<a
href="https://redirect.github.com/vitejs/vite/issues/23777">#23777</a>)
(<a
href="8d7623d9b5">8d7623d</a>)</li>
<li><strong>build:</strong> include preload dependencies to chunk import
map hashing (<a
href="https://redirect.github.com/vitejs/vite/issues/23793">#23793</a>)
(<a
href="4d16e7c14d">4d16e7c</a>)</li>
<li><strong>bundled-dev:</strong> apply <code>server.origin</code> to
asset URLs (<a
href="https://redirect.github.com/vitejs/vite/issues/23683">#23683</a>)
(<a
href="52ba877693">52ba877</a>)</li>
<li><strong>bundled-dev:</strong> declare utf-8 charset in fallback page
(<a
href="https://redirect.github.com/vitejs/vite/issues/23846">#23846</a>)
(<a
href="197daf6c4c">197daf6</a>)</li>
<li><strong>bundled-dev:</strong> ignore build.chunkImportMap (<a
href="https://redirect.github.com/vitejs/vite/issues/23685">#23685</a>)
(<a
href="cfe917d67d">cfe917d</a>)</li>
<li><strong>bundled-dev:</strong> include user
<code>import.meta.env.*</code> defines in the
<code>import.meta.env</code> object (<a
href="https://redirect.github.com/vitejs/vite/issues/23713">#23713</a>)
(<a
href="6019aa7997">6019aa7</a>)</li>
<li><strong>bundled-dev:</strong> map forwarded error stacks through the
bundle source maps (<a
href="https://redirect.github.com/vitejs/vite/issues/23741">#23741</a>)
(<a
href="11fe1e46ff">11fe1e4</a>)</li>
<li><strong>bundled-dev:</strong> normalize served output files (<a
href="https://redirect.github.com/vitejs/vite/issues/23684">#23684</a>)
(<a
href="df4e02cc83">df4e02c</a>)</li>
<li><strong>bundled-dev:</strong> stop hmr propagation at modules that
are no longer imported (<a
href="https://redirect.github.com/vitejs/vite/issues/23839">#23839</a>)
(<a
href="7471e8c3b3">7471e8c</a>)</li>
<li><strong>css:</strong> don't fail on a literal <code>%</code> in
<code>url()</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/23676">#23676</a>)
(<a
href="50b382d318">50b382d</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23731">#23731</a>)
(<a
href="cf80aa552c">cf80aa5</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23826">#23826</a>)
(<a
href="77d8373510">77d8373</a>)</li>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23733">#23733</a>)
(<a
href="43f0d052e5">43f0d05</a>)</li>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23827">#23827</a>)
(<a
href="c2e04187e4">c2e0418</a>)</li>
<li><strong>optimizer:</strong> fall back to yarn.lock for lockfile hash
(<a
href="https://redirect.github.com/vitejs/vite/issues/23667">#23667</a>)
(<a
href="f20609630f">f206096</a>)</li>
<li><strong>optimizer:</strong> scan <code>oxc.jsxInject</code> imports
(<a
href="https://redirect.github.com/vitejs/vite/issues/23758">#23758</a>)
(<a
href="38f3cb45b1">38f3cb4</a>)</li>
<li><strong>optimizer:</strong> skip patches dir check for yarn.lock
fallback (<a
href="https://redirect.github.com/vitejs/vite/issues/23725">#23725</a>)
(<a
href="8e890e1ce4">8e890e1</a>)</li>
<li><strong>optimizer:</strong> skip patches dir hash for nub lockfile
(<a
href="https://redirect.github.com/vitejs/vite/issues/23761">#23761</a>)
(<a
href="a7d56967ef">a7d5696</a>)</li>
<li><strong>resolve:</strong> support subpath imports with multiple
<code>#</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/23631">#23631</a>)
(<a
href="0d86b3485f">0d86b34</a>)</li>
<li><strong>resolve:</strong> update resolve.exports to 2.0.4 (<a
href="https://redirect.github.com/vitejs/vite/issues/23739">#23739</a>)
(<a
href="4ea708a3ae">4ea708a</a>)</li>
<li><strong>server:</strong> handle socket errors on upgrade requests
not taken by the HMR server (<a
href="https://redirect.github.com/vitejs/vite/issues/23728">#23728</a>)
(<a
href="c2c9ef4dcd">c2c9ef4</a>)</li>
<li><strong>server:</strong> only strip base when path matches a full
segment (<a
href="https://redirect.github.com/vitejs/vite/issues/23641">#23641</a>)
(<a
href="3dfde87b90">3dfde87</a>)</li>
</ul>
<h3>Performance Improvements</h3>
<ul>
<li><strong>ssr:</strong> extract inline source maps without a regexp
(<a
href="https://redirect.github.com/vitejs/vite/issues/23775">#23775</a>)
(<a
href="6a0278055d">6a02780</a>)</li>
</ul>
<h3>Miscellaneous Chores</h3>
<ul>
<li>migrate to Oxlint (<a
href="https://redirect.github.com/vitejs/vite/issues/23746">#23746</a>)
(<a
href="5b73935881">5b73935</a>)</li>
</ul>
<h3>Code Refactoring</h3>
<ul>
<li><strong>deps:</strong> replace <code>@vercel/detect-agent</code>
with <code>std-env</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/23744">#23744</a>)
(<a
href="ce4293f15c">ce4293f</a>)</li>
<li><strong>dev:</strong> consolidate moduleURL to file path utilities
(<a
href="https://redirect.github.com/vitejs/vite/issues/23828">#23828</a>)
(<a
href="df7b40ee9d">df7b40e</a>)</li>
<li><strong>dev:</strong> separate import resolution from URL conversion
(<a
href="https://redirect.github.com/vitejs/vite/issues/23791">#23791</a>)
(<a
href="611668983d">6116689</a>)</li>
<li><strong>dev:</strong> share ModuleURL and BrowserURL conversions (<a
href="https://redirect.github.com/vitejs/vite/issues/23790">#23790</a>)
(<a
href="446954c69c">446954c</a>)</li>
<li><strong>dev:</strong> use ModuleURLs throughout HTML transforms (<a
href="https://redirect.github.com/vitejs/vite/issues/23815">#23815</a>)
(<a
href="a16299092c">a162990</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.3.4...v8.3.5">8.3.5</a>
(2026-10-21)<!-- raw HTML omitted --></h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>build:</strong> handle missing chunk import maps (<a
href="https://redirect.github.com/vitejs/vite/issues/23777">#23777</a>)
(<a
href="8d7623d9b5">8d7623d</a>)</li>
<li><strong>build:</strong> include preload dependencies to chunk import
map hashing (<a
href="https://redirect.github.com/vitejs/vite/issues/23793">#23793</a>)
(<a
href="4d16e7c14d">4d16e7c</a>)</li>
<li><strong>bundled-dev:</strong> apply <code>server.origin</code> to
asset URLs (<a
href="https://redirect.github.com/vitejs/vite/issues/23683">#23683</a>)
(<a
href="52ba877693">52ba877</a>)</li>
<li><strong>bundled-dev:</strong> declare utf-8 charset in fallback page
(<a
href="https://redirect.github.com/vitejs/vite/issues/23846">#23846</a>)
(<a
href="197daf6c4c">197daf6</a>)</li>
<li><strong>bundled-dev:</strong> ignore build.chunkImportMap (<a
href="https://redirect.github.com/vitejs/vite/issues/23685">#23685</a>)
(<a
href="cfe917d67d">cfe917d</a>)</li>
<li><strong>bundled-dev:</strong> include user
<code>import.meta.env.*</code> defines in the
<code>import.meta.env</code> object (<a
href="https://redirect.github.com/vitejs/vite/issues/23713">#23713</a>)
(<a
href="6019aa7997">6019aa7</a>)</li>
<li><strong>bundled-dev:</strong> map forwarded error stacks through the
bundle source maps (<a
href="https://redirect.github.com/vitejs/vite/issues/23741">#23741</a>)
(<a
href="11fe1e46ff">11fe1e4</a>)</li>
<li><strong>bundled-dev:</strong> normalize served output files (<a
href="https://redirect.github.com/vitejs/vite/issues/23684">#23684</a>)
(<a
href="df4e02cc83">df4e02c</a>)</li>
<li><strong>bundled-dev:</strong> stop hmr propagation at modules that
are no longer imported (<a
href="https://redirect.github.com/vitejs/vite/issues/23839">#23839</a>)
(<a
href="7471e8c3b3">7471e8c</a>)</li>
<li><strong>css:</strong> don't fail on a literal <code>%</code> in
<code>url()</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/23676">#23676</a>)
(<a
href="50b382d318">50b382d</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23731">#23731</a>)
(<a
href="cf80aa552c">cf80aa5</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23826">#23826</a>)
(<a
href="77d8373510">77d8373</a>)</li>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23733">#23733</a>)
(<a
href="43f0d052e5">43f0d05</a>)</li>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23827">#23827</a>)
(<a
href="c2e04187e4">c2e0418</a>)</li>
<li><strong>optimizer:</strong> fall back to yarn.lock for lockfile hash
(<a
href="https://redirect.github.com/vitejs/vite/issues/23667">#23667</a>)
(<a
href="f20609630f">f206096</a>)</li>
<li><strong>optimizer:</strong> scan <code>oxc.jsxInject</code> imports
(<a
href="https://redirect.github.com/vitejs/vite/issues/23758">#23758</a>)
(<a
href="38f3cb45b1">38f3cb4</a>)</li>
<li><strong>optimizer:</strong> skip patches dir check for yarn.lock
fallback (<a
href="https://redirect.github.com/vitejs/vite/issues/23725">#23725</a>)
(<a
href="8e890e1ce4">8e890e1</a>)</li>
<li><strong>optimizer:</strong> skip patches dir hash for nub lockfile
(<a
href="https://redirect.github.com/vitejs/vite/issues/23761">#23761</a>)
(<a
href="a7d56967ef">a7d5696</a>)</li>
<li><strong>resolve:</strong> support subpath imports with multiple
<code>#</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/23631">#23631</a>)
(<a
href="0d86b3485f">0d86b34</a>)</li>
<li><strong>resolve:</strong> update resolve.exports to 2.0.4 (<a
href="https://redirect.github.com/vitejs/vite/issues/23739">#23739</a>)
(<a
href="4ea708a3ae">4ea708a</a>)</li>
<li><strong>server:</strong> handle socket errors on upgrade requests
not taken by the HMR server (<a
href="https://redirect.github.com/vitejs/vite/issues/23728">#23728</a>)
(<a
href="c2c9ef4dcd">c2c9ef4</a>)</li>
<li><strong>server:</strong> only strip base when path matches a full
segment (<a
href="https://redirect.github.com/vitejs/vite/issues/23641">#23641</a>)
(<a
href="3dfde87b90">3dfde87</a>)</li>
</ul>
<h3>Performance Improvements</h3>
<ul>
<li><strong>ssr:</strong> extract inline source maps without a regexp
(<a
href="https://redirect.github.com/vitejs/vite/issues/23775">#23775</a>)
(<a
href="6a0278055d">6a02780</a>)</li>
</ul>
<h3>Miscellaneous Chores</h3>
<ul>
<li>migrate to Oxlint (<a
href="https://redirect.github.com/vitejs/vite/issues/23746">#23746</a>)
(<a
href="5b73935881">5b73935</a>)</li>
</ul>
<h3>Code Refactoring</h3>
<ul>
<li><strong>deps:</strong> replace <code>@vercel/detect-agent</code>
with <code>std-env</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/23744">#23744</a>)
(<a
href="ce4293f15c">ce4293f</a>)</li>
<li><strong>dev:</strong> consolidate moduleURL to file path utilities
(<a
href="https://redirect.github.com/vitejs/vite/issues/23828">#23828</a>)
(<a
href="df7b40ee9d">df7b40e</a>)</li>
<li><strong>dev:</strong> separate import resolution from URL conversion
(<a
href="https://redirect.github.com/vitejs/vite/issues/23791">#23791</a>)
(<a
href="611668983d">6116689</a>)</li>
<li><strong>dev:</strong> share ModuleURL and BrowserURL conversions (<a
href="https://redirect.github.com/vitejs/vite/issues/23790">#23790</a>)
(<a
href="446954c69c">446954c</a>)</li>
<li><strong>dev:</strong> use ModuleURLs throughout HTML transforms (<a
href="https://redirect.github.com/vitejs/vite/issues/23815">#23815</a>)
(<a
href="a16299092c">a162990</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="981f07fa45"><code>981f07f</code></a>
release: v8.3.5 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23856">#23856</a>)</li>
<li><a
href="0d86b3485f"><code>0d86b34</code></a>
fix(resolve): support subpath imports with multiple <code>#</code> (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23631">#23631</a>)</li>
<li><a
href="6a0278055d"><code>6a02780</code></a>
perf(ssr): extract inline source maps without a regexp (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23775">#23775</a>)</li>
<li><a
href="8e890e1ce4"><code>8e890e1</code></a>
fix(optimizer): skip patches dir check for yarn.lock fallback (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23725">#23725</a>)</li>
<li><a
href="197daf6c4c"><code>197daf6</code></a>
fix(bundled-dev): declare utf-8 charset in fallback page (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23846">#23846</a>)</li>
<li><a
href="4d16e7c14d"><code>4d16e7c</code></a>
fix(build): include preload dependencies to chunk import map hashing (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23793">#23793</a>)</li>
<li><a
href="df7b40ee9d"><code>df7b40e</code></a>
refactor(dev): consolidate moduleURL to file path utilities (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23828">#23828</a>)</li>
<li><a
href="a16299092c"><code>a162990</code></a>
refactor(dev): use ModuleURLs throughout HTML transforms (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23815">#23815</a>)</li>
<li><a
href="611668983d"><code>6116689</code></a>
refactor(dev): separate import resolution from URL conversion (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23791">#23791</a>)</li>
<li><a
href="446954c69c"><code>446954c</code></a>
refactor(dev): share ModuleURL and BrowserURL conversions (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23790">#23790</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite/commits/v8.3.5/packages/vite">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-26 16:18:48 +01:00
Ben Brandt
c9374abfc7 test: fix integration-run failures in provider, model and session-index tests (#1439)
## Summary

Fixes the 23 failures seen with `RUN_INTEGRATION_TESTS=true`. All are
test-side; no adapter changes.

- **Provider tests (20 failures):** since #1340, `providers/set` lists
the gateway's models with `fetch` in the test process.
`vitest.config.ts` only loaded `setup-no-network.ts` outside integration
runs, so tests that point at `https://gateway.example.com` made real
requests and failed with `fetch failed`. The stub now loads in every
run. `gateway-models.ts` is the only in-process `fetch` caller, and no
integration test targets a real gateway.
- **"the real SDK exposes one plain Opus row":** the CLI catalogue now
also lists pinned older models (`claude-opus-5`, `claude-opus-4-8`, …),
and the alias row's display name is "Opus 5.5". The check now covers
only alias rows (`value` starting with `opus`) and still asserts there
is a single row with no 1M variant.
- **session-index `ENOTEMPTY` in teardown:** integration runs leave
`CLAUDE_CODE_EXECUTABLE` unset, so initialize ran the real `claude auth
status` in the background. It wrote into the temp `CLAUDE_CONFIG_DIR`
while `afterEach` removed it. The file now stubs the executable to
`/usr/bin/false`, as the scenario tests do.
- **session-index relocation test:** the wait ended on any removal from
the old path's subscription. That removal can name the other path's
session, so the test now waits for the relocated session's own id.

## Testing

- The six previously failing files pass with
`RUN_INTEGRATION_TESTS=true` (454 tests).
- `session-index.test.ts` passed 3× with integration on and 1× without
(173 tests). Those failures were intermittent, so a few clean runs don't
fully prove the fix.
- `npx vitest run` (3360 tests) and `npm run check` pass.
- The full integration suite was not rerun end to end.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 14:15:14 +01:00
acp-release-bot[bot]
d52c7c64d8 chore(main): release 0.97.0 (#1437)
🤖 I have created a release *beep* *boop*
---


##
[0.97.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.96.0...v0.97.0)
(2026-10-26)


### Features

* add ultracode session config option
([#1438](https://github.com/agentclientprotocol/claude-agent-acp/issues/1438))
([de96cef](de96cef382))


### Bug Fixes

* bump body-parser from 2.3.0 to 2.4.0
([#1436](https://github.com/agentclientprotocol/claude-agent-acp/issues/1436))
([c26e640](c26e640915))
* distinguish model options with duplicate labels
([#1286](https://github.com/agentclientprotocol/claude-agent-acp/issues/1286))
([434be13](434be13a33))
* keep the usage-limit error when it arrives after compaction
([#1377](https://github.com/agentclientprotocol/claude-agent-acp/issues/1377))
([2a1525e](2a1525e57c))
* pass --settings before the auth status subcommand
([#1392](https://github.com/agentclientprotocol/claude-agent-acp/issues/1392))
([6a33ded](6a33dedcbb))
* preserve foreground fork reports until launch completes
([#1431](https://github.com/agentclientprotocol/claude-agent-acp/issues/1431))
([e369479](e369479f93))
* settle advisor tool results before completing turns
([#1247](https://github.com/agentclientprotocol/claude-agent-acp/issues/1247))
([bed795d](bed795d261))
* settle live steers folded into autonomous cycles
([#1185](https://github.com/agentclientprotocol/claude-agent-acp/issues/1185))
([a4957d1](a4957d17c1))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-10-26 13:46:13 +01:00
Ben Brandt
de96cef382 feat: add ultracode session config option (#1438)
Adds an **Ultracode** session config option. It turns on Claude Code's
dynamic workflows for every task, at any effort level. Clients that
support boolean config options get a native switch; the rest get an
On/Off select. This follows the Fast mode pattern.

## How the CLI handles ultracode (2.1.310, checked against the bundled
binary and a live session)

- **Which models:** ultracode works on exactly the models that support
`xhigh` effort. The CLI uses the same check for both. So Opus 4.7+,
Sonnet 5+, Haiku 5.5 and Fable qualify; Opus 4.6 and Haiku 4.5 don't.
Effort level no longer matters.
- **Workflows:** dynamic workflows must be enabled. They are off by
default on Pro plans, and org or managed policy can turn them off.
- **State reporting:** `query.getSettings()` returns
`applied.ultracodeAvailable` and `applied.ultracodeRequested`. The CLI's
schema says hosts should "offer an Ultracode control only while this is
true". This method exists at runtime but is missing from the SDK's
`.d.ts`, so the adapter reads it with a cast and falls back to settings
and model data when it is absent.
- **Unsupported requests:** `applyFlagSettings({ultracode})` is never
validated. The request is stored and stays in place across model
switches; it takes effect whenever the model supports it.
- **Effort changes:** an `effortLevel` change sent without an
`ultracode` key turns ultracode off.

## Behavior

- **Visibility:** at session start, one `getSettings()` call decides
availability and the starting value. Model switches combine the
workflows result learned at start with the new model's
`supportedEffortLevels`, so the switch path makes no extra control
request.
- **Value:** the session tracks the user's choice, like
`fastModeEnabled`. The option hides on unsupported models and comes back
with the same value.
- **Effort applies:** `effortFlagSettings` now carries the session's
toggle instead of `settings.ultracode`. This applies at the initial
apply, on model switch, and from the effort picker, so effort changes no
longer turn ultracode off.
- **Clear context:** the clear-context restart restores the toggle, as
it does Fast mode.

## Known limitation

If a user types `/effort ultracode on` as a prompt, the toggle does not
update. The adapter already doesn't pick up effort changes typed as
`/effort` commands.

## Testing

- New `src/tests/ultracode-config.test.ts`. It covers the helpers and
session behavior: seeding from the CLI's report, hiding while workflows
are off, following the model's support, keeping the value across effort
and model switches, and rejected applies.
- New clear-context restore test. `effortFlagSettings` tests updated.
- Live probe against CLI 2.1.310 confirmed the `getSettings` fields, the
`xhigh` proxy (`claude-opus-4-6` reports unavailable while the request
stays in place), and the effort/key behavior.
- `npm run check`, `npm run build`, `npm run test:run` all pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 12:34:13 +00:00
Sean McCullough
a4957d17c1 fix: settle live steers folded into autonomous cycles (#1185)
#1233 fixed queued prompts folded into task-notification cycles. One
case remains: a steer sent to the active prompt can be folded into an
autonomous cycle, leaving `session/prompt` pending when the result names
only the steer UUID.

Keep queued prompt matching and also match the active turn's steer UUID.
Exclude settled turns and turns held open for background tasks. Preserve
plural UUID precedence.

The six new settlement cases fail on main and pass with the fix; all 11
new tests pass. Build, lint and format checks pass. The full default
suite reports 2,497 passed and 30 skipped with an identical Node binary
at a separate path. At `/usr/bin/node`, it reports 2,496 passed, one
failed and 30 skipped: an unchanged path-normalization test also fails
on main. No test was weakened. No live-provider tests were run.

Co-authored-by: Sean McCullough <sean@mcculloughs.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 11:35:31 +01:00
Nacho Lopez
bed795d261 fix: settle advisor tool results before completing turns (#1247)
When Claude uses the advisor server tool, a completed turn can fail with
`ACP error -32603: Internal error: Claude ended the turn without
returning results for tool calls: srvtoolu_...`. `server_tool_use` is
tracked as a foreground call, but the matching `advisor_tool_result` is
discarded, leaving that call unfinished at settlement.

Route advisor results through the existing tool-result handler so they
clear the outstanding call and publish a terminal update. Plaintext
advice is displayed, redacted advice gets a readable completion message
without exposing ciphertext, and advisor error results publish a failed
tool status with the error code. Release the advisor's hook callback
immediately because the server tool has no later client-side
`PostToolUse` hook.

Addresses the result-lifecycle part of #914.

Validation:

- Regression coverage through the real prompt consumer for plaintext,
redacted, and error results, including the complete assistant message
that dispatches the advisor call. All three cases fail without the fix.
A dispatched advisor call without a result still fails with
`incomplete_tool_call`.
- `npm run test:run -- src/tests/incomplete-tools.test.ts`: 59 passed.
- `npm run test:run`: 3,055 passed, 30 skipped.
- `npm run build` and `npm run check`: passed.
- Exact-head Linux CI passed in the fork:
https://github.com/mrmans0n/claude-agent-acp/actions/runs/38595756003.

The upstream PR workflows are waiting for maintainer approval to run.
The required upstream `Build` check remains pending; the fork CI result
does not satisfy that merge gate.
2026-10-26 11:28:57 +01:00
Jasper
434be13a33 fix: distinguish model options with duplicate labels (#1286)
Fixes #1246.

When `availableModels` includes aliases and pinned IDs that inherit the
same SDK display name, name-only model pickers show indistinguishable
rows. Append the selectable model ID to names that remain duplicated
after the existing version-label normalization. For example, the four
`Opus 5.5` rows become `Opus 5.5 (opus)`, `Opus 5.5 (opus[1m])`, and
their explicit-ID equivalents.

Keep every option and its ID, description, selection, and recommendation
behavior. Existing distinct labels stay unchanged. Regression tests
exercise the real allowlist transformation and cover duplicate custom
labels too.

Validation:

- Linux, locked dependencies and the repository's `lts/*` Node
configuration: `npm run check`, `npm run build`, and the full test suite
passed at ba37dd8: **2,491 passed, 30 skipped** ([fix
job](https://github.com/jipeng6036-del/claude-agent-acp/actions/runs/37923655565/job/113797353655)).
- Exact baseline 908c994: **2,489 passed, 30 skipped**; both new
regression tests fail against the original implementation. All existing
test outcomes match ([baseline
verification](https://github.com/jipeng6036-del/claude-agent-acp/actions/runs/37923859076)).
- Windows/Node 24: build and 105 focused tests passed. The full suite
has the same 48 failures on baseline and fix; only the two new tests add
passes. Authenticated SDK and JetBrains UI integration were not run.

Signed-off-by: Jasper <241105014+jipeng6036-del@users.noreply.github.com>
Co-authored-by: Jasper <241105014+jipeng6036-del@users.noreply.github.com>
2026-10-26 10:27:07 +00:00
Kerekes Dániel
2a1525e57c fix: keep the usage-limit error when it arrives after compaction (#1377)
Fixes #1370

After a compaction in the same turn, the synthetic usage-limit frame is
dropped by the stale-frame suppression before its failure details are
read. The prompt then ends with a generic internal error instead of the
quota error, so clients can't show the limit UI.

The suppression branch now records the usage-limit title and error
before it skips the frame. Only recognized usage-limit frames are read
there, so a stale replayed frame still can't overwrite the failure of
the current turn.

Added a test for a usage-limit frame after a completed compaction, for
both the legacy `errorKind` and the typed session failure, plus a stale
frame arriving after it. It fails without the change. `npm run check`,
`npm run build` and `npm run test:run` pass.
2026-10-26 10:26:18 +00:00
PerryLink
54f1e21bc4 test: retry the acp-v2 teardown rm on Windows (#1373)
## What breaks

On Windows, `npm run test:run` fails inside `src/tests/acp-v2.test.ts`,
and every failure is raised by the teardown rather than by the behaviour
the test checks. Measured on Windows 11 build 26200, Node 22.22.3,
vitest 5.0.4, two runs:

```
$ node node_modules/vitest/vitest.mjs run src/tests/acp-v2.test.ts
 Test Files  1 failed (1)
      Tests  45 failed | 91 passed (136)
   Duration  11.75s (tests 91%, transform 7%, import 2%)
```

```
 Test Files  1 failed (1)
      Tests  53 failed | 83 passed (136)
   Duration  11.79s (tests 96%, import 2%, transform 2%)
```

Exit code 1 both times. Every failure is the same error, and it comes
from `afterEach`, after the test body has finished:

```
 FAIL  src/tests/acp-v2.test.ts > ACP v2 prompts > does not report the next turn as waiting for a question that its client never settled
Error: EBUSY: resource busy or locked, rmdir 'C:\Users\...\Temp\acp-v2-TNkkd2\project-J9PwxJ'

 FAIL  src/tests/acp-v2.test.ts > ACP v2 prompts > the tool calls that a cancel stops > ends a stopped tool call and its subagent's as cancelled, before it reports idle cancelled
Error: EBUSY: resource busy or locked, rmdir 'C:\Users\...\Temp\acp-v2-O3wrrr\project-NgBUMz'
```

The string `EBUSY` occurs 45 and 53 times in those two logs, matching
the two failure counts. The failing set moves between runs; which test
loses the race depends on when Windows releases each directory handle.

CI runs the suite on `ubuntu-latest` only
(`.github/workflows/ci.yml:20`, `:45`), so CI cannot show this. Anyone
who runs the documented `npm run test:run` on Windows gets a red file
and a non-zero exit.

## Why

The per-test teardown at `src/tests/acp-v2.test.ts:156` is:

```ts
await rm(tempDir, { recursive: true, force: true });
```

`force: true` suppresses ENOENT only; it does not retry EBUSY. Each
test's cwd is a child of `tempDir` (`cwd = await
mkdtemp(path.join(tempDir, "project-"))`, line 147), and Windows keeps a
directory busy briefly after the process holding it exits, so `rmdir`
returns EBUSY, `afterEach` rejects, and vitest marks the test failed.

## The fix

```diff
-  await rm(tempDir, { recursive: true, force: true });
+  await rm(tempDir, { recursive: true, force: true, maxRetries: 10 });
```

`maxRetries` is Node's own retry loop for `fs.rm`, covering EBUSY,
EMFILE, ENFILE, ENOTEMPTY and EPERM with a backoff between attempts. The
repository already uses it for the same class of problem in
`src/tests/session-hooks.test.ts:150`:

```ts
// The CLI may still write the transcript of the last session after dispose.
if (root) await rm(root, { recursive: true, force: true, maxRetries: 10 });
```

## Measured after

```
$ node node_modules/vitest/vitest.mjs run src/tests/acp-v2.test.ts
 Test Files  1 passed (1)
      Tests  136 passed (136)
   Duration  16.16s (tests 96%, transform 2%, import 2%)
```

A second run after restoring the patched file: `Tests 136 passed (136)`,
`Duration 16.56s`, exit code 0, `EBUSY` count 0. `npm run check` (eslint
plus `prettier --check .`) also passes, and `npm run build` (`tsc`,
whose `include` covers `src/**/*.ts` so the test file is typechecked
too) exits 0.

## Scope

1 file changed, 1 insertion, 1 deletion: `src/tests/acp-v2.test.ts`.

Deliberately not changed:

- No other test file, and no teardown other than this one call.
`session-hooks.test.ts` is untouched.
- No assertion, fixture, mock or production code, so the 136 tests still
check exactly what they checked before.
- No CI change. Adding a Windows job is a separate call about CI time
and runner budget, and this fix can be verified on Windows without one.

## Unproven

- I have one Windows 11 machine. On `ubuntu-latest` the teardown did not
fail here before or after the patch, and I did not run the job on Linux;
the Linux side is "no behaviour change" by inspection, not something I
measured.
- `maxRetries: 10` covers a handle that closes shortly after the process
exits. A handle held for longer than the whole retry window would still
surface EBUSY; I did not observe one.
- I ran only this file, not the full suite, so I cannot say whether
other files fail on Windows for other reasons.
2026-10-26 10:25:48 +00:00
dependabot[bot]
01b686713d ci(deps): Bump actions/create-github-app-token from 3.2.0 to 3.3.0 (#1383)
Bumps
[actions/create-github-app-token](https://github.com/actions/create-github-app-token)
from 3.2.0 to 3.3.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/create-github-app-token/releases">actions/create-github-app-token's
releases</a>.</em></p>
<blockquote>
<h2>v3.3.0</h2>
<h2><a
href="https://github.com/actions/create-github-app-token/compare/v3.2.0...v3.3.0">3.3.0</a>
(2026-10-12)</h2>
<h3>Features</h3>
<ul>
<li>update permission inputs (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/399">#399</a>)
(<a
href="a47236f79f">a47236f</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> bump nested undici from 6.24.0 to 6.29.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/403">#403</a>)
(<a
href="12461a3a79">12461a3</a>)</li>
<li><strong>deps:</strong> bump the production-dependencies group across
1 directory with 3 updates (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/395">#395</a>)
(<a
href="a69c19ddfd">a69c19d</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/create-github-app-token/blob/main/CHANGELOG.md">actions/create-github-app-token's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2><a
href="https://github.com/actions/create-github-app-token/compare/v3.2.0...v3.3.0">3.3.0</a>
(2026-10-12)</h2>
<h3>Features</h3>
<ul>
<li>update permission inputs (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/399">#399</a>)
(<a
href="a47236f79f">a47236f</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> bump nested undici from 6.24.0 to 6.29.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/403">#403</a>)
(<a
href="12461a3a79">12461a3</a>)</li>
<li><strong>deps:</strong> bump the production-dependencies group across
1 directory with 3 updates (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/395">#395</a>)
(<a
href="a69c19ddfd">a69c19d</a>)</li>
</ul>
<h2><a
href="https://github.com/actions/create-github-app-token/compare/v3.1.1...v3.2.0">3.2.0</a>
(2026-05-12)</h2>
<h3>Features</h3>
<ul>
<li>add support for enterprise-level GitHub Apps (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/263">#263</a>)
(<a
href="952a2a7073">952a2a7</a>)</li>
<li>support full repository names in <code>repositories</code> input (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/372">#372</a>)
(<a
href="85eb8dd414">85eb8dd</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> bump <code>@​actions/core</code> from 3.0.0
to 3.0.1 in the production-dependencies group (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/364">#364</a>)
(<a
href="43e5c345bf">43e5c34</a>)</li>
<li>validate private-key input (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/376">#376</a>)
(<a
href="f24bbd8964">f24bbd8</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="64ab729a62"><code>64ab729</code></a>
chore(main): release 3.3.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/402">#402</a>)</li>
<li><a
href="f3a828e4b9"><code>f3a828e</code></a>
ci: create signed dist commits in release workflow (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/404">#404</a>)</li>
<li><a
href="655017ffa7"><code>655017f</code></a>
docs: clarify when owner grants access to all repositories (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/378">#378</a>)</li>
<li><a
href="12461a3a79"><code>12461a3</code></a>
fix(deps): bump nested undici from 6.24.0 to 6.29.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/403">#403</a>)</li>
<li><a
href="a47236f79f"><code>a47236f</code></a>
feat: update permission inputs (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/399">#399</a>)</li>
<li><a
href="a69c19ddfd"><code>a69c19d</code></a>
fix(deps): bump the production-dependencies group across 1 directory
with 3 u...</li>
<li><a
href="d5cb7ae41d"><code>d5cb7ae</code></a>
build(deps): bump actions/checkout from 6 to 7 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/384">#384</a>)</li>
<li><a
href="575da866bd"><code>575da86</code></a>
build(deps): bump stefanzweifel/git-auto-commit-action from 7.1.0 to
7.2.0 in...</li>
<li><a
href="e85f677b44"><code>e85f677</code></a>
test: omit stack traces from snapshots (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/401">#401</a>)</li>
<li><a
href="9419396df4"><code>9419396</code></a>
build(deps-dev): bump c8 from 11.0.0 to 12.0.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/400">#400</a>)</li>
<li>Additional commits viewable in <a
href="bcd2ba4921...64ab729a62">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/create-github-app-token&package-manager=github_actions&previous-version=3.2.0&new-version=3.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-10-26 10:24:25 +00:00
KlausUllrich
6a33dedcbb fix: pass --settings before the auth status subcommand (#1392)
`runCliAuthProbe` builds `["auth", "status", "--json"]` and, when the
session has a settings
override, appends `--settings <json>`. `--settings` is a global option
of the Claude Code CLI;
the `auth status` subcommand only accepts `--json` and `--text` (see
`claude auth status --help`).
So every probe with an override fails with `error: unknown option
'--settings'` and an empty
stdout. The catch branch logs `claude auth status failed: …` on every
call and returns
`undefined`. For sessions that pass `_meta.claudeCode.options.settings`,
the session-creation and
per-prompt refreshes therefore never update the auth status. Only the
initialize probe, which has
no override, still works.

This moves `--settings <json>` in front of the subcommand. Without an
override the argv is
unchanged.

**Reproduce** (no credentials needed; an empty config dir answers "not
logged in"):

```sh
d=$(mktemp -d)
env -i HOME="$d" PATH="$PATH" CLAUDE_CONFIG_DIR="$d/.claude" \
  claude auth status --json --settings '{}'
# error: unknown option '--settings'
env -i HOME="$d" PATH="$PATH" CLAUDE_CONFIG_DIR="$d/.claude" \
  claude --settings '{}' auth status --json
# {"loggedIn": false, "authMethod": "none", ...}
```

The option is applied in the global position, not just parsed:
`claude --settings '{"apiKeyHelper":"/bin/echo rt-fake-key"}' auth
status --json` reports
`"loggedIn": true, "authMethod": "api_key_helper"`.

Tested with 10 native Claude Code binaries (linux-x64), each in an empty
`HOME`:

| Claude Code | `auth status --json --settings '{}'` | `--settings '{}'
auth status --json` |
|---|---|---|
| 2.1.286 | unknown option | JSON |
| 2.1.294 | unknown option | JSON |
| 2.1.295, 2.1.296, 2.1.299 | unknown option | JSON |
| 2.1.300, 2.1.301, 2.1.302 | unknown option | JSON |
| 2.1.303 (the SDK's `claudeCodeVersion`) | unknown option | JSON |
| 2.1.304 | unknown option | JSON |

The existing test in `auth-status.test.ts` pinned the old order; it now
expects `--settings`
first. Its mock and `statusCalls()` matched probes by `args[1] ===
"status"`, which a leading
global option shifts, so both now locate the subcommand instead.

`npm run build`, `format:check`, `lint` and `test:run` pass, apart from
one test that fails
identically on `main` without this change.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 10:24:11 +00:00
Slapoguzov Alexander
e369479f93 fix: preserve foreground fork reports until launch completes (#1431)
A foreground fork can stream a copied `tool_result` for the Agent call
that launched it. That placeholder completed the call and removed its
tracked state, so the real report arriving later was dropped. Ignore
only results whose tool ID equals the message's parent tool ID, keeping
the launching call open for its actual report.

Keep other results in the same message and discard the message-level
`tool_use_result` sidecar when its launch result was removed, so it
cannot be attributed to a remaining tool result. Regression coverage
exercises plain and Air clients, root and nested launch results, and a
mixed message containing a genuine nested Agent result.

Fixes #1389.

Validation:

- [Linux
CI](https://github.com/agentclientprotocol/claude-agent-acp/actions/runs/40217593917)
passes the full suite: 3,304 passed, 32 skipped (57 test files passed, 4
skipped). All PR checks are green.
- `npm run check` and `npm run build` pass.
- `vitest run src/tests/acp-agent.test.ts --maxWorkers=1`: 712 passed,
10 skipped. The four new regression cases fail on the original code and
pass with the fix.
- Eight actual-SDK runs against localhost fake gateways complete
successfully without model calls: plain and Air foreground forks, native
Air child sessions, nested forks with the background option omitted or
false, the SDK's fork-inside-fork rejection, a background launch
receipt, and history replay. Foreground reports arrive exactly once and
no launch-result tracking warning remains.

The local full-suite run encountered 19 failures in
`session-index.test.ts` and then stopped progressing, so it was
interrupted. A standalone recursive-file-watcher probe also emits
`EMFILE` on this machine. The successful Linux CI run above provides the
complete-suite verification.

Review dependencies (updated 26 October):

| Related proposal | Relationship to this PR |
| --- | --- |
|
[#1408](https://github.com/agentclientprotocol/claude-agent-acp/pull/1408)
— targeted session lookup | Independent. Both orders merge cleanly onto
the checked main and produce the same tree. |
|
[#1422](https://github.com/agentclientprotocol/claude-agent-acp/pull/1422)
— resumed subagents | Complementary, with no dependency on this PR. It
now conflicts with upstream main in `src/acp-agent.ts`; its author needs
to resolve that conflict, integrate the [separately shared AIR routing
patch](https://github.com/agentclientprotocol/claude-agent-acp/pull/1422#issuecomment-6401008862)
and complete its validation. |

<details>
<summary>Additional local integration validation — 25 October</summary>

A local combination of the recorded main, #1408, #1431, #1422 and the
already-reviewed routing patch passed 199 focused
foreground/resume/native-subagent checks (647 other cases filtered or
skipped), TypeScript build, and two replays of previously captured SDK
frames for AIR and standard subagents. The two captured-frame replays
were independently repeated. No new model calls were made.

The combination is a private test tree, not a published change. It does
not replace full CI for #1422 or live Air acceptance. The routing patch
needed three-way application against that recorded main because its
surrounding source had moved; it applied cleanly that way. The
current-main conflict in #1422 means this historical result does not
establish today’s combined compatibility.

Checked main `739dde3`, #1408 `e3b3aa4`, #1431 `8f96f46`, #1422
`dd3bf2c`. The two orders of #1408 and #1431 produce tree `1716d7e`; the
full local test tree is `61c0447`. Refresh this evidence if these inputs
change.

</details>

Co-authored-by: Codex <codex@openai.com>
2026-10-26 11:20:15 +01:00
dependabot[bot]
c26e640915 fix: bump body-parser from 2.3.0 to 2.4.0 (#1436)
Bumps [body-parser](https://github.com/expressjs/body-parser) from 2.3.0
to 2.4.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/expressjs/body-parser/releases">body-parser's
releases</a>.</em></p>
<blockquote>
<h2>v2.4.0</h2>
<h2>Important: Security</h2>
<ul>
<li>Security fix for <a
href="https://www.cve.org/CVERecord?id=CVE-2026-111181">CVE-2026-111181</a>
(<a
href="https://github.com/expressjs/body-parser/security/advisories/GHSA-27r3-jxmh-p828">GHSA-27r3-jxmh-p828</a>)</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>build(deps): bump actions/checkout from 6.0.3 to 7.0.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/737">expressjs/body-parser#737</a></li>
<li>chore: group github actions updates by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/body-parser/pull/743">expressjs/body-parser#743</a></li>
<li>build(deps): bump the github-actions group with 3 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/745">expressjs/body-parser#745</a></li>
<li>test: fix 'presist' typo in test descriptions by <a
href="https://github.com/maxtaran2010"><code>@​maxtaran2010</code></a>
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/749">expressjs/body-parser#749</a></li>
<li>docs: include security fix in 2.3.0 changes by <a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/748">expressjs/body-parser#748</a></li>
<li>fix: avoid mutating read options when reading body by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/body-parser/pull/742">expressjs/body-parser#742</a></li>
<li>refactor: move getCharset helper to only usage by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/body-parser/pull/707">expressjs/body-parser#707</a></li>
<li>build(deps): bump the github-actions group with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/753">expressjs/body-parser#753</a></li>
<li>refactor: switch to neostandard by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/body-parser/pull/751">expressjs/body-parser#751</a></li>
<li>perf(json): avoid first-char regex for direct JSON bodies by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/body-parser/pull/744">expressjs/body-parser#744</a></li>
<li>docs: fix entity verify error description by <a
href="https://github.com/Rawal27"><code>@​Rawal27</code></a> in <a
href="https://redirect.github.com/expressjs/body-parser/pull/755">expressjs/body-parser#755</a></li>
<li>feat: zstandard support by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/body-parser/pull/586">expressjs/body-parser#586</a></li>
<li>test: remove unused experimentalZstd option by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/body-parser/pull/757">expressjs/body-parser#757</a></li>
<li>ci: run tests on windows and macos matrix by <a
href="https://github.com/kilisamemarisaaa"><code>@​kilisamemarisaaa</code></a>
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/759">expressjs/body-parser#759</a></li>
<li>deps: qs@^6.16.0 by <a
href="https://github.com/krzysdz"><code>@​krzysdz</code></a> in <a
href="https://redirect.github.com/expressjs/body-parser/pull/762">expressjs/body-parser#762</a></li>
<li>build(deps): bump the github-actions group with 3 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/763">expressjs/body-parser#763</a></li>
<li>ci: trigger npm publish on release published and set dist-tag by <a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/768">expressjs/body-parser#768</a></li>
<li>build(deps): bump the github-actions group with 3 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/771">expressjs/body-parser#771</a></li>
<li>fix(test): pin supertest to 7.3.0 to avoid 7.3.1 regression by <a
href="https://github.com/saadkhantareen"><code>@​saadkhantareen</code></a>
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/773">expressjs/body-parser#773</a></li>
<li>perf: inline content stream setup by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/body-parser/pull/750">expressjs/body-parser#750</a></li>
<li>docs: note that req.body may be undefined in v2 by <a
href="https://github.com/Hashim1999164"><code>@​Hashim1999164</code></a>
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/752">expressjs/body-parser#752</a></li>
<li>docs: fix strict option description by <a
href="https://github.com/Rawal27"><code>@​Rawal27</code></a> in <a
href="https://redirect.github.com/expressjs/body-parser/pull/756">expressjs/body-parser#756</a></li>
<li>docs: fix depth error description by <a
href="https://github.com/johnnyhuirilef"><code>@​johnnyhuirilef</code></a>
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/776">expressjs/body-parser#776</a></li>
<li>chore: update dependencies by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/body-parser/pull/778">expressjs/body-parser#778</a></li>
<li>Release: 2.4.0 by <a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
in <a
href="https://redirect.github.com/expressjs/body-parser/pull/777">expressjs/body-parser#777</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/maxtaran2010"><code>@​maxtaran2010</code></a>
made their first contribution in <a
href="https://redirect.github.com/expressjs/body-parser/pull/749">expressjs/body-parser#749</a></li>
<li><a href="https://github.com/Rawal27"><code>@​Rawal27</code></a> made
their first contribution in <a
href="https://redirect.github.com/expressjs/body-parser/pull/755">expressjs/body-parser#755</a></li>
<li><a
href="https://github.com/kilisamemarisaaa"><code>@​kilisamemarisaaa</code></a>
made their first contribution in <a
href="https://redirect.github.com/expressjs/body-parser/pull/759">expressjs/body-parser#759</a></li>
<li><a
href="https://github.com/saadkhantareen"><code>@​saadkhantareen</code></a>
made their first contribution in <a
href="https://redirect.github.com/expressjs/body-parser/pull/773">expressjs/body-parser#773</a></li>
<li><a
href="https://github.com/Hashim1999164"><code>@​Hashim1999164</code></a>
made their first contribution in <a
href="https://redirect.github.com/expressjs/body-parser/pull/752">expressjs/body-parser#752</a></li>
<li><a
href="https://github.com/johnnyhuirilef"><code>@​johnnyhuirilef</code></a>
made their first contribution in <a
href="https://redirect.github.com/expressjs/body-parser/pull/776">expressjs/body-parser#776</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/expressjs/body-parser/compare/v2.3.0...v2.4.0">https://github.com/expressjs/body-parser/compare/v2.3.0...v2.4.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/expressjs/body-parser/blob/master/HISTORY.md">body-parser's
changelog</a>.</em></p>
<blockquote>
<h1>2.4.0</h1>
<ul>
<li>Security fix for <a
href="https://github.com/expressjs/body-parser/security/advisories/GHSA-27r3-jxmh-p828">GHSA-27r3-jxmh-p828</a>
(CVE-2026-111181)</li>
<li>feat: add <code>zstd</code> content-encoding support (<a
href="https://redirect.github.com/expressjs/body-parser/issues/586">#586</a>)
<ul>
<li>Requires Node.js <code>^22.15.0</code> or <code>&gt;=23.8.0</code>;
older versions keep returning <code>415</code> for <code>zstd</code>
encoded bodies</li>
</ul>
</li>
<li>fix: avoid mutating the parser options and the request object while
reading the body (<a
href="https://redirect.github.com/expressjs/body-parser/issues/742">#742</a>,
<a
href="https://redirect.github.com/expressjs/body-parser/issues/750">#750</a>)</li>
<li>perf(json): avoid first-char regex for direct JSON bodies (<a
href="https://redirect.github.com/expressjs/body-parser/issues/744">#744</a>)</li>
<li>deps:
<ul>
<li>content-type@^2.1.0</li>
<li>iconv-lite^0.7.3</li>
<li>qs@^6.16.1</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b4744d739e"><code>b4744d7</code></a>
2.4.0 (<a
href="https://redirect.github.com/expressjs/body-parser/issues/777">#777</a>)</li>
<li><a
href="f7075eae3a"><code>f7075ea</code></a>
fix: prevent process crash from trailing bytes after a compressed
body</li>
<li><a
href="185ff1d555"><code>185ff1d</code></a>
chore: update dependencies (<a
href="https://redirect.github.com/expressjs/body-parser/issues/778">#778</a>)</li>
<li><a
href="73713f76c4"><code>73713f7</code></a>
docs: fix depth error description (<a
href="https://redirect.github.com/expressjs/body-parser/issues/776">#776</a>)</li>
<li><a
href="2dcc826c6d"><code>2dcc826</code></a>
docs: improve readability (<a
href="https://redirect.github.com/expressjs/body-parser/issues/756">#756</a>)</li>
<li><a
href="402dbdc6f1"><code>402dbdc</code></a>
docs: note that req.body may be undefined in v2 (<a
href="https://redirect.github.com/expressjs/body-parser/issues/752">#752</a>)</li>
<li><a
href="9883a1eeb4"><code>9883a1e</code></a>
perf: inline content stream setup (<a
href="https://redirect.github.com/expressjs/body-parser/issues/750">#750</a>)</li>
<li><a
href="02101da415"><code>02101da</code></a>
fix(test): pin supertest to 7.3.0 to avoid 7.3.1 regression (<a
href="https://redirect.github.com/expressjs/body-parser/issues/773">#773</a>)</li>
<li><a
href="822a0fd462"><code>822a0fd</code></a>
build(deps): bump the github-actions group with 3 updates (<a
href="https://redirect.github.com/expressjs/body-parser/issues/771">#771</a>)</li>
<li><a
href="aa9889bc35"><code>aa9889b</code></a>
ci: trigger npm publish on release published and set dist-tag (<a
href="https://redirect.github.com/expressjs/body-parser/issues/768">#768</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/expressjs/body-parser/compare/v2.3.0...v2.4.0">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for body-parser since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=body-parser&package-manager=npm_and_yarn&previous-version=2.3.0&new-version=2.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/agentclientprotocol/claude-agent-acp/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-26 11:19:09 +01:00
acp-release-bot[bot]
f6958db1ee chore(main): release 0.96.0 (#1425)
🤖 I have created a release *beep* *boop*
---


##
[0.96.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.95.0...v0.96.0)
(2026-10-26)


### Features

* report SendMessage delegations as linked session messages
([#1424](https://github.com/agentclientprotocol/claude-agent-acp/issues/1424))
([739dde3](739dde3ae5))
* report the model of each native subagent
([#1432](https://github.com/agentclientprotocol/claude-agent-acp/issues/1432))
([c319e5e](c319e5e542))


### Bug Fixes

* do not hold an AIR load replay until the CLI starts
([#1433](https://github.com/agentclientprotocol/claude-agent-acp/issues/1433))
([eb9d63e](eb9d63e5fd))
* refine RFD child models after a load, and retry a failed model update
([#1434](https://github.com/agentclientprotocol/claude-agent-acp/issues/1434))
([9a8bf71](9a8bf71d45))
* send subagents' current state after a load response
([#1423](https://github.com/agentclientprotocol/claude-agent-acp/issues/1423))
([17a967b](17a967b4da))
* update to Claude Code 2.1.310
([#1435](https://github.com/agentclientprotocol/claude-agent-acp/issues/1435))
([2f3c6bb](2f3c6bb7b3))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-10-26 09:50:05 +00:00
Ben Brandt
2f3c6bb7b3 fix: update to Claude Code 2.1.310 (#1435)
Updates `@anthropic-ai/claude-agent-sdk` from 0.3.309 to **0.3.310**
(Claude Code **2.1.310**), `@agentclientprotocol/sdk` 2.0.1 → 2.0.2 and
`@anthropic-ai/sdk` 1.1.0 → 1.3.0.

TypeScript stays on 6.0.3, because typescript-eslint 8.72 still requires
`typescript <6.1.0`.

## Behaviour changes ACP users may notice

- **2.1.310:** the Monitor tool and plugin monitors now run on Bedrock,
Vertex and Foundry, and with telemetry or nonessential traffic disabled.
On those setups, long foreground sleeps are now blocked. We already hide
Monitor tasks from the client (`local_monitor` / `HIDDEN_TASK_TOOLS`),
so the only visible change is that a blocked sleep shows up as a failed
Bash call.
- **2.1.310:** when an MCP tool call times out because its elicitation
went unanswered, the error now tells Claude not to repeat the call.

## Upstream fixes this picks up

- Requests behind gateways that reject the `effort-2025-11-24` beta or
mid-conversation system messages are retried without them. This affects
our `providers/set` gateway sessions.
- After a "Prompt is too long" error, compaction now trims dense content
(base64, SVG) properly, so the next request no longer fails again.
Compaction in SDK sessions with very large transcripts also uses less
memory.
- File read permission prompts now show where a symbolic link leads.
- Windows: PreToolUse and PermissionRequest hooks no longer let a tool
call through when Git Bash is missing and the hook's command is only
quoted strings.

## Checked, no change needed

- **Agent SDK 0.3.310:** the `sdk.d.ts` diff is doc text only
(`connectorId` in the `sdkMcpServerConfigs` comment, and the provider
list for `'highlights'` thinking display). Every `CLAUDE_*` env var the
adapter reads is still in the 2.1.310 binary.
- **ACP SDK 2.0.2:** only fixes the HTTP transport (CR line endings in
SSE, request-scoped POST failures), which we don't use.
- **`@anthropic-ai/sdk` 1.2/1.3:** adds API endpoints (message threads,
usage/cost reports, Claude Tag); we only use its types.

Verified: `npm run build`, `npm run check`, `npm run test:run` (3323
passed, 30 skipped), and a typecheck of `examples/`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-10-26 10:46:44 +01:00
nikita-ashihmin
9a8bf71d45 fix: refine RFD child models after a load, and retry a failed model update (#1434)
## Summary

Follow-up to #1433, which was merged before these review fixes landed.

- **RFD child models after a load.** On a real `session/load` (session
not live in this process), #1433 also gave `RfdSubagentReplay` no model
list, so after the load response `currentStates` could not map a child's
model to the root session's `model` entry. RFD children kept the raw
model ID. The replay now reads the session's models when needed; only
the in-replay mapping is off while the load replays.
- **Retry after a failed publish.** `reportModel` skipped a model that
matched the stored options, which are written before the publish. A
failed publish then blocked every later report of the same model. It now
compares with the last model the client actually received.
- **Failed load replay.** The replayed child models are also dropped
when the replay itself fails.
- The buffer of an unannounced child collapses only model reports from
`reportModel`; other config updates stay as they are.

## Tests

- `replayRfd` gained a `load` option that replays like a real load;
"sends the model of each child's responses, and after the load response
only a changed entry" uses it (fails without the fix).
- A retry after a failed publish, for the RFD and AIR forms.
- "refines an ended AIR child's model" now goes through the load path.
- `npm run build`, `npm run check`, `npm run test:run`: 3323 passed, 30
skipped.

## Related

- Follow-up to #1433 and #1432
- RFD change:
https://github.com/agentclientprotocol/agent-client-protocol/pull/2482
- RFD section: [Child
configuration](https://github.com/agentclientprotocol/agent-client-protocol/blob/main/docs/rfds/subagents.mdx#child-configuration)
- JetBrains issue:
[IJAI-4754](https://youtrack.jetbrains.com/issue/IJAI-4754)
2026-10-25 23:24:19 -07:00
nikita-ashihmin
eb9d63e5fd fix: do not hold an AIR load replay until the CLI starts (#1433)
## Summary

#1432 made an AIR `session/load` replay wait for the CLI start before it
sent the children's models. So the end of every such replay
(`loadReplayEnd` and the children's `subagent_state_update`) came only
after the CLI started, also for a history without subagents.

- The replay no longer waits. It sends each child's model as the replay
reaches it. During a load the session's model list is not known yet, so
the model goes as its id.
- After the load response, each replayed child whose model the list maps
to another entry gets that entry. This also applies to a child that the
replay already ended. It is a more exact snapshot of the same
configuration.
- `modelEntryFor` and `modelDisplayName` keep their results per model
list. `reportModel` skips a model that the child already has.
- The buffer of a child that is not announced yet keeps only its latest
configuration update, so model reports do not crowd out the child's
output.
- AIR: the runtime forgets the configuration of an ended child.

## Validation

- `npm run build`, `npm run check`
- `npm run test:run`, two runs: 3321 passed, 30 skipped.
- `src/tests/acp-scenarios.test.ts`, 30 runs in 6 parallel workers: no
failure.
- New tests: the load end event before the CLI start, with and without
subagents (both fail on `main`); the post-load refinement of an ended
child; the buffer; the cleanup; the memo after a list change.
- Microbenchmark of 10,000 child responses over 200 children, with 51
models:

  | | before | after |
  |---|---|---|
  | `modelEntryFor`, last entry | 12.16 µs | 0.23 µs |
  | `modelEntryFor`, no match | 64.60 µs | 0.08 µs |
  | `reportModel`, AIR | 13.66 µs | 4.03 µs |
  | `reportModel`, RFD | 12.80 µs | 3.23 µs |

## Related

- Follow-up to
https://github.com/agentclientprotocol/claude-agent-acp/pull/1432
- RFD change:
https://github.com/agentclientprotocol/agent-client-protocol/pull/2482
- JetBrains issue:
[IJAI-4754](https://youtrack.jetbrains.com/issue/IJAI-4754)
2026-10-25 23:05:36 -07:00
nikita-ashihmin
c319e5e542 feat: report the model of each native subagent (#1432)
## Summary

A native subagent child session gets its model only from what the child
reports. The update is a `config_option_update` with the child's session
id and a single `model` select option. This follows the subagents RFD,
[Child
configuration](https://github.com/agentclientprotocol/agent-client-protocol/blob/main/docs/rfds/subagents.mdx#child-configuration)
(see also agentclientprotocol/agent-client-protocol#2482).

## Relation to #1417

#1417 reports a child's model from its responses, in the `rfd` form
only, with the raw model id as the name. This PR keeps that source and
extends it:

- The `air` form gets the same update.
- History replay sends each child's model from its transcript.
- The value maps to an entry of the root session's `model` option, so a
client can compare the child's model with the session's.

There is no model at spawn. The adapter cannot tell the model that
Claude Code picks for a new child, for example with
`CLAUDE_CODE_SUBAGENT_MODEL_FORCE`, or when an alias runs on the
parent's exact model. So the first update comes with the child's first
real response (`message.model`, not `<synthetic>`). A new update goes
only when the entry changes.

The configuration hooks of #1417 (`PostModelSwitch` and the others) now
also run for `air` clients. A hook names the SDK agent id, so in the
`air` form its options go to the agent's current generation session
(`<agent id>:generation:<n>`). In the `air` form, no configuration goes
after a child's end.

## Notification

```json
{
  "sessionId": "<child session id>",
  "update": {
    "sessionUpdate": "config_option_update",
    "configOptions": [{
      "id": "model", "name": "Model", "category": "model", "type": "select",
      "currentValue": "opus[1m]",
      "options": [{ "value": "opus[1m]", "name": "Opus 4.6 (1M context)" }]
    }]
  }
}
```

- The client cannot change a child's configuration, so the select offers
only the current value.
- No field is added to `subagent_spawned` or `subagent_update`.

## Matching a model id to an entry

Best first:

1. the root session's current entry, when it runs the same model up to a
context hint, such as `opus[1m]` or `default` for `claude-opus-4-6`;
2. the listed entry whose value or resolved model is the id;
3. a listed entry that runs the same model up to a context hint.

When several entries run the model, a concrete entry comes before
`default`, unless `default` is the current entry. A model id is compared
as it is, so a dated or provider id (`claude-haiku-4-5-20251001`,
`eu.anthropic.claude-sonnet-5`) matches only the same id:
`ModelInfo.resolvedModel` is the wire id that a response reports. A
model that matches no entry goes as its full id, with its display name
when the session's model list has one. A `PostModelSwitch` hook of a
child uses the same matching.

## Replay

- A child without responses in its transcript gets no model.
- `rfd` form: the replay sends a child's model right after its
announcement, from its first response, and again when a later response
changes it. During a load, the session's model list can be unknown, so
the replay can send the full id. After the load response, a child's
model goes again only when the list maps it to another entry.
- `air` form: the replay ends every child, and nothing may follow the
end. So each child's latest response model goes once the session
creation ended, just before the child's end, as the entry of the
session's model list.

## Validation

- `npm run build`
- `npm run check`
- `npm run test:run`, three runs: 3315 passed, 30 skipped.
- `src/tests/acp-scenarios.test.ts`, 30 runs in 6 parallel workers: no
failure.

New tests cover:

- no model before the child's first response, in both forms;
- an update only when the entry changes;
- no update to an ended `air` child, also with an update in flight at
the end;
- a hook's options under the current `air` generation;
- matching for `opus[1m]` and `default`, concrete entries before
`default`, listed entries, dated and provider ids, and unlisted ids;
- the `PostModelSwitch` entry, in both forms;
- an `air` load replay that maps a child's model only once the session
exists;
- `rfd` replay with an unlisted full id, and no duplicate update after
the load response.

The scenario snapshots for both forms and v2 are updated.
2026-10-25 22:37:14 -07:00
Ben Brandt
739dde3ae5 feat: report SendMessage delegations as linked session messages (#1424)
In the subagents RFD form, a `SendMessage` call to a child now becomes a
message between the two sessions, as a launch's prompt already does
(#1405). Before, it was a tool call of its sender plus an unlinked
message in the child. Every delegation now has a parent-side message,
which a client can anchor a per-delegation card on, and both views name
each other with `senderMessageId`/`recipientMessageId`.

## Live

- The call is held until its final update. The SDK reports that the
message resumed a child before it reports the call's result.
- **Message reached an announced child:** the sender gets the outgoing
view and the child the incoming one (`linkedSessionMessages`). The
call's result isn't shown.
- **Message reached no announced child**, for example one sent to the
parent: the call is shown after all, with its final status
(`unexposedControlFallback`).
- **Call a permission request already showed:** it keeps its updates,
and its message goes only to the child.

## Replay

- A child's history replays right after its launch, so a call from a
parent to its child is always matched to its delivery first.
- The child's view then names the call, and the sender's view goes out
where the call sits.
- A call the replay reaches before its delivery stays a tool call,
unlinked. That can happen with a child's message to a sibling whose
history replays later.

`publishPrompt`'s `replacesLaunch` option is now `replacesCall`.

## Testing

- **Live:** the existing `SendMessage` tests in
`native-subagents.test.ts` and `acp-agent.test.ts` now expect both views
and check that the call and its completed result are hidden. A new test
checks that a call reaching no announced child is shown with its final
status.
- **Replay:** the existing test expects the linked views in place of
`root tool_call toolu_send`. A new test covers a call reached before its
delivery, and fails if delivery ignores that the call was already shown.
- `npm run check`, `npm run build` and `npm run test:run` (3301 tests)
pass locally.

Not tried against a real Claude Code run yet.
2026-10-25 00:14:54 +02:00
Ben Brandt
17a967b4da fix: send subagents' current state after a load response (#1423)
The subagents RFD makes the response to a load (v1 `session/load`, v2
`session/resume` with replay) the freshness boundary. Work states and
`cancel` grants sent before it are history, and "current snapshots are
sent after the response". The adapter sent each replayed child's end
only during the replay, so a client that follows the RFD strictly showed
every child as unknown after a reload. This replaces the RFD carve-out
proposed in agentclientprotocol/agent-client-protocol#2475.

After the response, each replayed child now gets one current
`subagent_update`:

- **A child still running in this process**, as when a live session is
loaded again, reports its live state and `capabilities.cancel`, as it
did live.
- **Any other child** can't be running, because the process that ran it
is gone. Its replayed end becomes its current state, with no grant.

When it's sent:

- **v1:** with the commands, in the timer that follows the setup
response (`afterSetupResponse`).
- **v2:** the surface sends it only once its own resume response is out,
after `setupCommands`. A timer set inside `loadSession` could fire
before that response.

`RfdSubagentReplay.finish` and the new `currentStates` share one
`endState`. `NativeSubagentRuntime.liveWorkState` exposes the live
state.

## Testing

- `sends each child's current state after the load response`: a live
child and a finished one, after the replay and its response.
- `replays a subagent's history to its own session…` (v2) now records
when the client receives the response. It checks that the child's
current `state_update` arrives after it, and that the parent's copy
grants nothing. It fails without the v2 change.
- `npm run check`, `npm run build` and `npm run test:run` (3300 tests)
pass locally.

Not tested end to end: the v1 timer path. It reuses the timer that
already sends v1 commands after the setup response.
2026-10-24 23:28:11 +02:00
acp-release-bot[bot]
ebfbfbdaa9 chore(main): release 0.95.0 (#1391)
🤖 I have created a release *beep* *boop*
---


##
[0.95.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.94.0...v0.95.0)
(2026-10-23)


### Features

* end a turn at its result for AIR clients with backgroundSubagents
([#1262](https://github.com/agentclientprotocol/claude-agent-acp/issues/1262))
([9b3a5c8](9b3a5c891c))
* report read-only subagent configuration
([#1417](https://github.com/agentclientprotocol/claude-agent-acp/issues/1417))
([6ab670b](6ab670baae))
* report RFD delegations as linked session messages
([#1405](https://github.com/agentclientprotocol/claude-agent-acp/issues/1405))
([59e292e](59e292e29f))
* serve experimental ACP v2
([#1400](https://github.com/agentclientprotocol/claude-agent-acp/issues/1400))
([4d378f9](4d378f93f3))
* show messages typed in other Remote Control clients
([#1371](https://github.com/agentclientprotocol/claude-agent-acp/issues/1371))
([5b11f47](5b11f470b1))
* update to Claude Code 2.1.309
([#1413](https://github.com/agentclientprotocol/claude-agent-acp/issues/1413))
([a1521b6](a1521b6196))


### Bug Fixes

* bump fast-uri from 3.1.8 to 3.1.9
([#1410](https://github.com/agentclientprotocol/claude-agent-acp/issues/1410))
([0fe22ae](0fe22ae5a1))
* bump postcss from 8.5.30 to 8.5.31 in the minor group
([#1403](https://github.com/agentclientprotocol/claude-agent-acp/issues/1403))
([ecaa2c9](ecaa2c935b))
* bump the minor group across 1 directory with 2 updates
([#1416](https://github.com/agentclientprotocol/claude-agent-acp/issues/1416))
([f7d6030](f7d6030c04))
* IJAI-3744 keep a session running while its background tasks are open
([#1399](https://github.com/agentclientprotocol/claude-agent-acp/issues/1399))
([e9f986b](e9f986b8a9))
* publish the session title as soon as the CLI has it
([#1406](https://github.com/agentclientprotocol/claude-agent-acp/issues/1406))
([50cd76e](50cd76e78d))
* publish turns that Claude Code starts after end_turn as an async task
in v1
([#1390](https://github.com/agentclientprotocol/claude-agent-acp/issues/1390))
([a1d0eee](a1d0eeeaad))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-10-23 21:58:47 +02:00
Ben Brandt
6ab670baae feat: report read-only subagent configuration (#1417)
## Summary

Expose the observed configuration of RFD subagent sessions using the
same config-option IDs as ordinary sessions, without granting
client-initiated configuration changes.

- Report model from child responses and model-switch hooks, and mode,
applied reasoning effort, and agent type from child-attributed SDK
hooks.
- Merge partial observations into complete, ordered config snapshots;
buffer early observations until truthful child announcement and
serialize concurrent publications.
- Advertise only each observed current value. Do not inherit parent
settings or invent child Fast-mode state.
- Keep child plan-mode and model-switch hooks from changing the parent's
configuration.
- Cover initial/changed settings, announcement ordering, publication
failures, root isolation, and hook registration in both protocol
versions; update v2 scenario snapshots.

## Related

Protocol clarification:
https://github.com/agentclientprotocol/agent-client-protocol/pull/2468

Zed client implementation:
https://github.com/zed-industries/zed/pull/66058

## Validation

Passed after rebasing onto current `main`:

- `npm run build`
- `npm run check`
- `npm run test:run -- --silent`: 3,294 passed, 30 skipped.

No live Claude-to-Zed smoke test was run. Settings are reported only
when observed from the SDK; availability depends on runtime support and
events received.
2026-10-23 21:51:35 +02:00
dependabot[bot]
f7d6030c04 fix: bump the minor group across 1 directory with 2 updates (#1416)
Bumps the minor group with 2 updates in the / directory:
[@opentelemetry/api](https://github.com/open-telemetry/opentelemetry-js)
and [jose](https://github.com/panva/jose).

Updates `@opentelemetry/api` from 1.9.1 to 1.10.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-js/releases">@​opentelemetry/api's
releases</a>.</em></p>
<blockquote>
<h2>api/v1.10.0</h2>
<h2>1.10.0</h2>
<h3>🚀 Features</h3>
<ul>
<li>feat(api): integrate the Logs API into
<code>@opentelemetry/api</code> <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/4862">#4862</a>
<a
href="https://github.com/hectorhdzg"><code>@​hectorhdzg</code></a></li>
<li>feat(api): add imperative <code>context.attach()</code> for setting
context across callback boundaries that <code>with()</code> cannot wrap.
<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6845">#6845</a>
<a href="https://github.com/pichlermarc"><code>@​pichlermarc</code></a>
<ul>
<li><code>attach()</code> returns a <code>Token</code> whose
<code>dispose()</code> method restores the previous context. Use
<code>token.dispose()</code> to detach.</li>
<li><code>attach</code> is an optional method on the
<code>ContextManager</code> interface; when the active context manager
does not implement it, <code>context.attach()</code> logs a warning and
returns a no-op token.</li>
</ul>
</li>
<li>feat(api): add <code>AnyValue</code> type (of type
<code>unknown</code>) and extend <code>Attributes</code> type values to
use <code>AnyValue</code> <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6780">#6780</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a>
<ul>
<li>This is part of supporting <a
href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/4485-extending-attributes-to-support-complex-values.md#how">OTEP
4485</a>.</li>
<li>This is a <strong>breaking change for SDK implementations</strong>,
in that they may need to support more attribute types for any APIs that
accept <code>Attributes</code> or <code>AnyValue</code>. Implementors
should refer to the OTEP 4485 link above for &quot;MUST / SHOULD&quot;
requirements for complex attributes handling. See also the <a
href="https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-core/src/common/attributes.ts">attributes-handling
utilities provided by <code>@opentelemetry/core</code> to help</a>. For
example, <code>cleanSimpleAttributes()</code> can be used to limit
user-provided attributes to the set of value types supported in earlier
<code>@opentelemetry/api</code> versions.</li>
<li>This change is <strong>typically not a breaking change for
users</strong> of <code>@opentelemetry/api</code>. There is one possible
breaking case: If a TypeScript user was using the
<code>Attributes</code> type in their own code and using <a
href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html">TypeScript
Narrowing</a> to reduce an attribute value to an expected type (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6579#issuecomment-4345316200">example
here</a>). It is expected that this usage should be rare and, likely,
limited to SDK implementations, if at all. (If this impacted your code,
<a
href="https://github.com/open-telemetry/opentelemetry-js/issues/new">please
let us know</a>.)</li>
<li><strong>Note for users of attributes:</strong> Simple attributes
SHOULD be used whenever possible. Instrumentations SHOULD assume that
backends do not index individual properties of complex attributes, that
querying or aggregating on such properties is inefficient and
complicated, and that reporting complex attributes carries higher
performance overhead.</li>
</ul>
</li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>fix(api): cap tracestate list-members and total length in the
deprecated <code>TraceState.set()</code> so it stays in step with
<code>TraceState</code> from <code>@opentelemetry/core</code> <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6964">#6964</a>
<a href="https://github.com/Zuhef"><code>@​Zuhef</code></a></li>
<li>fix: resolve the ESM build through the <code>default</code> export
condition instead of <code>import</code>, so a resolver that activates
neither <code>import</code> nor <code>require</code> still finds an
entry point <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/7132">#7132</a>
<a
href="https://github.com/overbalance"><code>@​overbalance</code></a></li>
</ul>
<h3>📚 Documentation</h3>
<ul>
<li>docs(api): complete <code>@since</code> annotations for the public
API <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/7023">#7023</a>
<a
href="https://github.com/nabeelamjadsheikh"><code>@​nabeelamjadsheikh</code></a>
<ul>
<li>Adds the three missing exported-type annotations and the
member-level annotations for methods and properties introduced after
their enclosing type.</li>
<li>Corrects <code>Attributes</code>, <code>AttributeValue</code> and
<code>TracerOptions</code> from <code>1.3.0</code> to
<code>1.1.0</code>; all three were already importable in 1.1.0 via
wildcard re-export.</li>
</ul>
</li>
</ul>
<h3>🏠 Internal</h3>
<ul>
<li>build: migrate the package build from <code>tsc</code> to
<code>tsdown</code>, emitting dual CJS/ESM output from a single
<code>dist/</code> directory <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6293">#6293</a>
<a href="https://github.com/overbalance"><code>@​overbalance</code></a>
<ul>
<li>The public entry points are unchanged:
<code>@opentelemetry/api</code> and
<code>@opentelemetry/api/experimental</code>. This package already
declared an <code>exports</code> map, so deep imports such as
<code>@opentelemetry/api/build/src/...</code> were rejected before this
change and continue to be.</li>
<li>The emitted files moved out of <code>build/src</code> (CJS),
<code>build/esm</code> and <code>build/esnext</code> (ESM) into
<code>dist/</code>, using <code>.cjs</code>/<code>.mjs</code> extensions
with matching <code>.d.cts</code>/<code>.d.mts</code> declarations. The
non-standard <code>esnext</code> condition has been dropped in favour of
the standard <code>import</code>/<code>require</code> conditions.</li>
</ul>
</li>
<li>perf(api): add getGlobal fast-path <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6956">#6956</a>
<a
href="https://github.com/legendecas"><code>@​legendecas</code></a></li>
<li>chore: remove the redundant <code>module</code> field from
<code>package.json</code>; bundlers resolve the ESM build through the
<code>exports</code> map <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/7130">#7130</a>
<a
href="https://github.com/overbalance"><code>@​overbalance</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-js/blob/main/CHANGELOG.md">@​opentelemetry/api's
changelog</a>.</em></p>
<blockquote>
<h2>1.10.0</h2>
<h3>🚀 (Enhancement)</h3>
<ul>
<li>feat(resource): create sync resource with some attributes that
resolve asynchronously <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3460">#3460</a>
<a
href="https://github.com/samimusallam"><code>@​samimusallam</code></a></li>
<li>feat: collect host id for non-cloud environments <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3575">#3575</a>
<a href="https://github.com/mwear"><code>@​mwear</code></a></li>
<li>feat (api-logs): separate Events API into its own package <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3550">3550</a>
<a
href="https://github.com/martinkuba"><code>@​martinkuba</code></a></li>
<li>feat(sdk-metrics): apply binary search in histogram recording <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3539">#3539</a>
<a
href="https://github.com/legendecas"><code>@​legendecas</code></a></li>
<li>perf(propagator-jaeger): improve deserializeSpanContext performance
<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3541">#3541</a>
<a href="https://github.com/doochik"><code>@​doochik</code></a></li>
<li>feat: support TraceState in SamplingResult <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3530">#3530</a>
<a
href="https://github.com/raphael-theriault-swi"><code>@​raphael-theriault-swi</code></a></li>
<li>feat(sdk-trace-base): add diagnostic logging when spans are dropped
<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3610">#3610</a>
<a
href="https://github.com/neoeinstein"><code>@​neoeinstein</code></a></li>
<li>feat: add unit to view instrument selection criteria <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3647">#3647</a>
<a href="https://github.com/jlabatut"><code>@​jlabatut</code></a></li>
<li>feat(tracing): expose dropped counts for attributes, events and
links on span <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3576">#3576</a>
<a href="https://github.com/mohitk05"><code>@​mohitk05</code></a></li>
</ul>
<h3>🐛 (Bug Fix)</h3>
<ul>
<li>fix(core): added falsy check to make otel core work with browser
where webpack config had process as false or null <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3613">#3613</a>
<a
href="https://github.com/ravindra-dyte"><code>@​ravindra-dyte</code></a></li>
<li>fix(instrumentation-http): include query params in http.target <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3646">#3646</a>
<a href="https://github.com/kobi-co"><code>@​kobi-co</code></a></li>
</ul>
<h3>📚 (Refine Doc)</h3>
<ul>
<li>chore: update http example <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3651">#3651</a>
<a
href="https://github.com/JamieDanielson"><code>@​JamieDanielson</code></a></li>
</ul>
<h3>🏠 (Internal)</h3>
<ul>
<li>chore(exporter-jaeger): deprecate jaeger exporter <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3585">#3585</a>
<a
href="https://github.com/pichlermarc"><code>@​pichlermarc</code></a></li>
<li>fix(sdk-metrics): fix flaky LastValueAggregator test by using fake
timer <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3587">#3587</a>
<a
href="https://github.com/pichlermarc"><code>@​pichlermarc</code></a></li>
<li>fix(test): fix failing tests by preventing source-map generation <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/3642">#3642</a>
<a
href="https://github.com/pichlermarc"><code>@​pichlermarc</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="56e6b1bb89"><code>56e6b1b</code></a>
chore: Release API 1.4.1, SDK 1.10.0, Experimental 0.36.0 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3654">#3654</a>)</li>
<li><a
href="ecb5ebe86e"><code>ecb5ebe</code></a>
fix: Minor typing issues for web sandbox (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3660">#3660</a>)</li>
<li><a
href="48ecf229eb"><code>48ecf22</code></a>
feat(sdk-trace-base): add dropped attributes and events count on span
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3576">#3576</a>)</li>
<li><a
href="95bf6fc7cc"><code>95bf6fc</code></a>
fix(build): add <code>example/http</code> to ignore list in
<code>update-ts-configs</code> (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3657">#3657</a>)</li>
<li><a
href="3a2ca58075"><code>3a2ca58</code></a>
feat: add unit to view instrument selection criteria (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3647">#3647</a>)</li>
<li><a
href="ebc8575a0c"><code>ebc8575</code></a>
chore(jeager-exporter): deprecate jaeger exporter (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3585">#3585</a>)</li>
<li><a
href="2f715bd78d"><code>2f715bd</code></a>
feat: allow to add attributes from grpc metadata in the patched server
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3589">#3589</a>)</li>
<li><a
href="cbbdfd3798"><code>cbbdfd3</code></a>
fix(sdk-node): update instrumentations once MeterProvider is initialized
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3624">#3624</a>)</li>
<li><a
href="4a4484a910"><code>4a4484a</code></a>
chore: update http example (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3651">#3651</a>)</li>
<li><a
href="494efffcf4"><code>494efff</code></a>
fix(otlp-exporter-base): fix changelog for merged retry pr (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/3650">#3650</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/open-telemetry/opentelemetry-js/compare/v1.9.1...v1.10.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `jose` from 6.2.12 to 6.2.13
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/panva/jose/releases">jose's
releases</a>.</em></p>
<blockquote>
<h2>v6.2.13</h2>
<h3>Refactor</h3>
<ul>
<li>align terminology with JOSE specifications (<a
href="bafcb74f5f">bafcb74</a>)</li>
<li>preserve underlying error causes (<a
href="724ba24b5a">724ba24</a>),
closes <a
href="https://redirect.github.com/panva/jose/issues/908">#908</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/panva/jose/blob/main/CHANGELOG.md">jose's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/panva/jose/compare/v6.2.12...v6.2.13">6.2.13</a>
(2026-10-20)</h2>
<h3>Refactor</h3>
<ul>
<li>align terminology with JOSE specifications (<a
href="bafcb74f5f">bafcb74</a>)</li>
<li>preserve underlying error causes (<a
href="724ba24b5a">724ba24</a>),
closes <a
href="https://redirect.github.com/panva/jose/issues/908">#908</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="316b7f2cb4"><code>316b7f2</code></a>
chore(release): 6.2.13</li>
<li><a
href="8106395886"><code>8106395</code></a>
chore: bump packages</li>
<li><a
href="724ba24b5a"><code>724ba24</code></a>
refactor: preserve underlying error causes</li>
<li><a
href="b112a5e6e1"><code>b112a5e</code></a>
chore: bump packages</li>
<li><a
href="2a3a525968"><code>2a3a525</code></a>
chore(deps-dev): bump workerd in /tap/workerd</li>
<li><a
href="8f540d89de"><code>8f540d8</code></a>
chore(deps): bump the actions group with 3 updates</li>
<li><a
href="8d6c02b5fa"><code>8d6c02b</code></a>
chore(deps-dev): bump electron from 44.0.0 to 44.4.5 in
/tap/electron</li>
<li><a
href="55c959fd16"><code>55c959f</code></a>
ci: use gh run rerun for retries</li>
<li><a
href="5b00c649d7"><code>5b00c64</code></a>
ci: address zizmor findings and bump shared workflows</li>
<li><a
href="1761d7f1b5"><code>1761d7f</code></a>
chore: bump packages</li>
<li>Additional commits viewable in <a
href="https://github.com/panva/jose/compare/v6.2.12...v6.2.13">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-23 19:49:50 +00:00
Ben Brandt
28a09b1008 chore: remove the unused ts-node devDependency (#1414)
Removes `ts-node` 10.9.2 from devDependencies. Nothing in the repo uses
it: no script, config or test references it. `npm run build` uses `tsc`,
tests run through vitest, and `npm run example:simple-client` uses `node
--experimental-strip-types`.

Verified: `npm run build`, `npm run check`, `npm run test:run`.
2026-10-23 21:43:16 +02:00
132 changed files with 12681 additions and 1463 deletions

View File

@@ -36,7 +36,7 @@ jobs:
# Generating a GitHub token, so that PRs and tags created by
# the release-please-action can trigger actions workflows.
- name: Generate GitHub token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510
id: generate-token
with:
# GitHub App ID secret name
@@ -256,7 +256,7 @@ jobs:
exit 1
- name: Generate token scoped to the registry repo
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510
id: registry-token
with:
app-id: ${{ secrets.REGISTRY_UPDATER_APP_ID }}

View File

@@ -1,3 +1,3 @@
{
".": "0.94.0"
".": "0.100.0"
}

View File

@@ -16,13 +16,85 @@ npm run format # prettier --write
CI runs `format:check`, `lint`, `build` and `test:run`. Run `npm run check` before
opening a PR.
## Code comments
## Coding guidelines
Keep comments straightforward, concise, and concrete. Say what the code does and
why, in plain terms: name the actual state, call, or failure involved instead of
alluding to it. Prefer a short concrete example over an abstract description.
Don't use vague phrases like "keeps it honest" or "the residual case" that make
the reader reconstruct the meaning.
- Keep edits consistent with the surrounding code's formatting, naming and idioms.
- Keep comments concise and concrete. Name the actual state, call or failure
instead of alluding to it, prefer a short example over an abstract
description, and avoid vague phrases like "keeps it honest". A comment or doc
must be true on every code path it describes, and a doc that lists what a
result omits or rejects must name the actual cases.
- When a request can't be served, throw a `RequestError` whose message says what
happened and what to do (e.g. "Try again when the turn ends"). Don't drop the
request silently.
- Don't report success for work you start without awaiting it. If the detached
call can still refuse and its `.catch` only logs, check the refusal condition
first or report the failure to the client.
- A result must not depend on what an earlier call happened to cache. A request
right after a restart, with cold caches, must return the same thing as one
after a warm-up.
- If code relies on an ordering or call sequence that nothing enforces (e.g. an
echo arriving before the result), say so in a short comment.
## Architecture and design fit
- Before coding, read the code the change plugs into and find the module that
owns the state or concept. Build on that module, not beside it.
- Don't add new features or components to `acp-agent.ts`. If no existing module fits the new
code cleanly, create a dedicated one that owns the feature's state and logic,
and keep the change in `acp-agent.ts` to the calls that wire it in.
- Prefer extending an existing mechanism over adding a new one, even if it takes
a small refactor (moving a helper, generalizing a function). Add a parallel
path, special case or flag only when the existing structure can't support the
change.
- Before writing new logic, check whether it already exists in a sibling handler
of the same flow or in a helper that works with the same data, and reuse it.
Paths that handle the same input must apply the same rules through the same
code: the live stream and the `session/load` replay, the command list and
command dispatch, `prompt` and `steer`. Two copies of one check drift apart.
- Put guards, filters and cleanup in the module that owns the state, so every
caller gets them. If guards, flags or fallbacks pile up in one place,
restructure instead of adding another.
- If a result is meant to match another request's output, reuse that request's
logic.
- The adapter serves ACP v1 and v2. A change that is not meant for one protocol
version only must work correctly in both. `src/v2/` converts v2 requests to v1
and reuses the v1 logic; keep v2-only differences in the conversion.
- A session runs one turn at a time. "Claude is working" means `startTurn`'s
check (an unsettled turn in `turnQueue`, or `foregroundWork` for the session);
don't write a narrower one. Anything that sends `session/update` must go
through the turn queue or be refused while the session is busy, or its output
lands inside another turn. Keep the session marked busy across awaited network
calls.
- Don't branch on a local copy of state that Claude Code or a remote service
owns: it goes stale when the other side changes. Have a command set the state
the user asks for instead of toggling it.
- A command or name the adapter adds must not shadow one that Claude Code or the
user's own commands and skills already provide.
- New optional request fields and `_meta` keys must not reject requests that
were valid before.
- If a workaround or new path is unavoidable (e.g. a Claude Code or SDK
limitation), say why in a short code comment and in the PR description.
## Testing guidelines
- Before adding a test, search `src/tests/` for existing tests of the same API
and scenario, and add to their file and `describe` block. Use the helpers in
`helpers.ts` and `session-doubles.ts` instead of copying setup.
- Don't add cases that take the same code path. Tests that differ only in input
and expected output belong in one `it.each`. Don't repeat another test's
assertions; assert only what this test is about.
- Reach a state the shortest way (e.g. set `session.tornDown = true`) instead of
driving a full flow with extra mocks, unless the flow is what you test.
- Build test doubles from what the bundled Claude Code CLI really sends,
including fields like `origin` it echoes back and fields it omits. A double
simpler than the real message hides bugs. If unsure, check against the real
CLI (`npm run test:integration`).
- Cover both ACP v1 and v2 when a change affects what a client receives
(updates, responses, replay). A change to internal logic only does not need
its tests duplicated per version.
- For each behavior the docs or comments promise, including what is omitted or
rejected, have a test that would fail if it broke.
## Pull requests

View File

@@ -1,5 +1,133 @@
# Changelog
## [0.100.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.99.1...v0.100.0) (2026-11-02)
### Features
* list v2 background tool calls ([#1511](https://github.com/agentclientprotocol/claude-agent-acp/issues/1511)) ([4d79b9f](https://github.com/agentclientprotocol/claude-agent-acp/commit/4d79b9ffbce95c23928a5dd771d6c87476d37160))
* report subagent working directories ([#1496](https://github.com/agentclientprotocol/claude-agent-acp/issues/1496)) ([eb78afa](https://github.com/agentclientprotocol/claude-agent-acp/commit/eb78afa8556b8fc4082495e48e3fd1762fb6a50f))
* report v2 background work ([#1510](https://github.com/agentclientprotocol/claude-agent-acp/issues/1510)) ([5565259](https://github.com/agentclientprotocol/claude-agent-acp/commit/5565259771cb74e3a9bdbacfcbce43e061d81422))
* report why Claude Code could not start a session ([#1508](https://github.com/agentclientprotocol/claude-agent-acp/issues/1508)) ([38cf656](https://github.com/agentclientprotocol/claude-agent-acp/commit/38cf656fc9d80fec2d1a16c472779803e2c802d2))
### Bug Fixes
* align session/cancel, session/close, and v2 setup with the ACP v2 spec ([#1494](https://github.com/agentclientprotocol/claude-agent-acp/issues/1494)) ([f50598a](https://github.com/agentclientprotocol/claude-agent-acp/commit/f50598a39c5dc3908d4b98a7213e01d63ea867d5))
* forward v2 elicitation values and read dismissed v2 permission prompts as a refusal ([#1493](https://github.com/agentclientprotocol/claude-agent-acp/issues/1493)) ([3bc6711](https://github.com/agentclientprotocol/claude-agent-acp/commit/3bc671193c6456516fe05a0eaa53c17bdfb6a190))
* keep native Windows paths in v2 git patches ([#1492](https://github.com/agentclientprotocol/claude-agent-acp/issues/1492)) ([c3428de](https://github.com/agentclientprotocol/claude-agent-acp/commit/c3428de1bf411c513b0703ddd56bb6c5fdff583c))
* preserve live session settings across provider refreshes ([#1474](https://github.com/agentclientprotocol/claude-agent-acp/issues/1474)) ([e01a288](https://github.com/agentclientprotocol/claude-agent-acp/commit/e01a28838499dd6dd7c31e35708090b938e852c6))
* reject unreadable skill manifests ([#1482](https://github.com/agentclientprotocol/claude-agent-acp/issues/1482)) ([99a6e76](https://github.com/agentclientprotocol/claude-agent-acp/commit/99a6e76158b9164e51bd62e3b89113beadea5ce2))
* report the fresh context's usage when ExitPlanMode clears context ([#1484](https://github.com/agentclientprotocol/claude-agent-acp/issues/1484)) ([2fd46c8](https://github.com/agentclientprotocol/claude-agent-acp/commit/2fd46c866d4ff2939fe1b1245b15a536d6de7c0b))
* update to @agentclientprotocol/sdk 2.2.0 ([#1491](https://github.com/agentclientprotocol/claude-agent-acp/issues/1491)) ([1d2b92f](https://github.com/agentclientprotocol/claude-agent-acp/commit/1d2b92feae5eeccde3ac738edca1f1d71250762e))
* update to @agentclientprotocol/sdk 2.4.0 ([#1509](https://github.com/agentclientprotocol/claude-agent-acp/issues/1509)) ([720bbb3](https://github.com/agentclientprotocol/claude-agent-acp/commit/720bbb3e4c65874775a0bb9418799baf4b6e3680))
* update to Claude Agent SDK 0.3.316 (Claude Code 2.1.316) ([#1507](https://github.com/agentclientprotocol/claude-agent-acp/issues/1507)) ([1d03064](https://github.com/agentclientprotocol/claude-agent-acp/commit/1d0306468b632c6a56b4fb076ad7a19f23d9e7b5))
* validate session, prompt, config, and auth requests per the latest ACP spec ([#1495](https://github.com/agentclientprotocol/claude-agent-acp/issues/1495)) ([d2ef490](https://github.com/agentclientprotocol/claude-agent-acp/commit/d2ef490a318033049af00cb9164a39ac2dd1eb95))
## [0.99.1](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.99.0...v0.99.1) (2026-10-29)
### Bug Fixes
* add approval id to plan ([#1469](https://github.com/agentclientprotocol/claude-agent-acp/issues/1469)) ([4b4b2c4](https://github.com/agentclientprotocol/claude-agent-acp/commit/4b4b2c4de7c492070f6260cacea5680b2e045a5a))
* allow forking sessions into a different directory ([#1483](https://github.com/agentclientprotocol/claude-agent-acp/issues/1483)) ([8bd0bc9](https://github.com/agentclientprotocol/claude-agent-acp/commit/8bd0bc9b03fb4a30e0b1755128baad12a214a58d))
* detect "Run /login" login-expired messages as authRequired ([#1481](https://github.com/agentclientprotocol/claude-agent-acp/issues/1481)) ([fccaebc](https://github.com/agentclientprotocol/claude-agent-acp/commit/fccaebcf87e8f37144ec1a64920c0c53997edabe))
* mark plugin, bundled and nested Claude skills as skills for AIR ([#1473](https://github.com/agentclientprotocol/claude-agent-acp/issues/1473)) ([52c241e](https://github.com/agentclientprotocol/claude-agent-acp/commit/52c241e71512ec19c4dd79c95c51ce67f04d7aac))
* title subagent sessions by their task ([#1479](https://github.com/agentclientprotocol/claude-agent-acp/issues/1479)) ([7dcb629](https://github.com/agentclientprotocol/claude-agent-acp/commit/7dcb62923d38ac24e386b0626fe123334341c445))
## [0.99.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.98.0...v0.99.0) (2026-10-28)
### Features
* Add thinking display configuration ([#1081](https://github.com/agentclientprotocol/claude-agent-acp/issues/1081)) ([2f1f4d1](https://github.com/agentclientprotocol/claude-agent-acp/commit/2f1f4d144196c7518a7c072f9ae0e70ac9b91a44))
* send embedded blob resources in prompts to Claude ([#1460](https://github.com/agentclientprotocol/claude-agent-acp/issues/1460)) ([d0ee308](https://github.com/agentclientprotocol/claude-agent-acp/commit/d0ee30813fbc79dbe122f7455c8e1746e09be458))
* tell the client when an MCP server fails to start ([#1464](https://github.com/agentclientprotocol/claude-agent-acp/issues/1464)) ([54b35ee](https://github.com/agentclientprotocol/claude-agent-acp/commit/54b35ee0f4d765efc5bc39107a524891266c42ac))
* update to Claude Agent SDK 0.3.312 ([#1471](https://github.com/agentclientprotocol/claude-agent-acp/issues/1471)) ([4cb8906](https://github.com/agentclientprotocol/claude-agent-acp/commit/4cb8906d32a618011cfeba3260e4eb1db2fd829f))
* **v2:** send the current session info on session/resume ([#1462](https://github.com/agentclientprotocol/claude-agent-acp/issues/1462)) ([f6ed4a4](https://github.com/agentclientprotocol/claude-agent-acp/commit/f6ed4a4f7d9dd5b5e7ed4e9973d9661becb9f583))
### Bug Fixes
* deny a background permission request that the client cancels ([#1466](https://github.com/agentclientprotocol/claude-agent-acp/issues/1466)) ([f646c82](https://github.com/agentclientprotocol/claude-agent-acp/commit/f646c821f1b05df471aac8f1018ea08e2a793f6e))
* don't report decline when the client can't render an MCP elicitation ([#1465](https://github.com/agentclientprotocol/claude-agent-acp/issues/1465)) ([54b869c](https://github.com/agentclientprotocol/claude-agent-acp/commit/54b869c990ad3efa25c976e8eb3ff5a37808a6a3))
* list the current model among the model config option's values ([#1463](https://github.com/agentclientprotocol/claude-agent-acp/issues/1463)) ([8e6fe92](https://github.com/agentclientprotocol/claude-agent-acp/commit/8e6fe92a98dab8cec868a8a782e0ded55c94e571))
* report EnterPlanMode tool calls with the switch_mode kind ([#1467](https://github.com/agentclientprotocol/claude-agent-acp/issues/1467)) ([93c1f43](https://github.com/agentclientprotocol/claude-agent-acp/commit/93c1f43519a6a542dc55a39985d0abd12195129d))
* return ACP error codes for unknown sessions and invalid config values ([#1458](https://github.com/agentclientprotocol/claude-agent-acp/issues/1458)) ([c2e85cb](https://github.com/agentclientprotocol/claude-agent-acp/commit/c2e85cb58067d9b00e6609ce9e90d4ec3a5d0feb))
* update ACP SDK to 2.1.0 (schema v1.27.0 / v2.0.0-alpha.10) ([9f9aebf](https://github.com/agentclientprotocol/claude-agent-acp/commit/9f9aebf44bdecab887b03871fee8a2885ceac19a))
* **v2:** keep clients in sync with config changes and replayed tool calls ([#1461](https://github.com/agentclientprotocol/claude-agent-acp/issues/1461)) ([2a0c188](https://github.com/agentclientprotocol/claude-agent-acp/commit/2a0c188a0aebd0ea03fb449f72dfa53f98c7622d))
* **v2:** reject MCP transports and prompt content the agent doesn't advertise ([#1459](https://github.com/agentclientprotocol/claude-agent-acp/issues/1459)) ([2480888](https://github.com/agentclientprotocol/claude-agent-acp/commit/2480888b90498158054266bdb3bd7f22822e1b73))
## [0.98.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.97.0...v0.98.0) (2026-10-27)
### Features
* add batch session metadata lookup ([#1408](https://github.com/agentclientprotocol/claude-agent-acp/issues/1408)) ([0ce42f2](https://github.com/agentclientprotocol/claude-agent-acp/commit/0ce42f2c312d190f78024d8b4b1ae5f00d9e5939))
### Bug Fixes
* publish Monitor async tasks as monitors ([#1452](https://github.com/agentclientprotocol/claude-agent-acp/issues/1452)) ([4324dce](https://github.com/agentclientprotocol/claude-agent-acp/commit/4324dce4be877c55385b3389cebac1c63d08f456))
* settle the tool call of a SandboxNetworkAccess permission request ([#1448](https://github.com/agentclientprotocol/claude-agent-acp/issues/1448)) ([8f43ac8](https://github.com/agentclientprotocol/claude-agent-acp/commit/8f43ac808931a3f14719bbed37b9b153c9933139))
* **v2:** leave the tool call status out of permission subjects ([#1447](https://github.com/agentclientprotocol/claude-agent-acp/issues/1447)) ([c7c80f7](https://github.com/agentclientprotocol/claude-agent-acp/commit/c7c80f7b26f8d90f7f1c828036db64bc46dddddf))
## [0.97.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.96.0...v0.97.0) (2026-10-26)
### Features
* add ultracode session config option ([#1438](https://github.com/agentclientprotocol/claude-agent-acp/issues/1438)) ([de96cef](https://github.com/agentclientprotocol/claude-agent-acp/commit/de96cef382d8daf27301f1f90ae4b5e7f665f73a))
### Bug Fixes
* bump body-parser from 2.3.0 to 2.4.0 ([#1436](https://github.com/agentclientprotocol/claude-agent-acp/issues/1436)) ([c26e640](https://github.com/agentclientprotocol/claude-agent-acp/commit/c26e640915739dc7a3cd0664f8f5879098e0fd83))
* distinguish model options with duplicate labels ([#1286](https://github.com/agentclientprotocol/claude-agent-acp/issues/1286)) ([434be13](https://github.com/agentclientprotocol/claude-agent-acp/commit/434be13a33141af8655c6f7c30f574841bf2279c))
* keep the usage-limit error when it arrives after compaction ([#1377](https://github.com/agentclientprotocol/claude-agent-acp/issues/1377)) ([2a1525e](https://github.com/agentclientprotocol/claude-agent-acp/commit/2a1525e57cd89246d1a50993fd6ba335c4e634ca))
* pass --settings before the auth status subcommand ([#1392](https://github.com/agentclientprotocol/claude-agent-acp/issues/1392)) ([6a33ded](https://github.com/agentclientprotocol/claude-agent-acp/commit/6a33dedcbb90e7bf9db91207f0edbf19ca6d705e))
* preserve foreground fork reports until launch completes ([#1431](https://github.com/agentclientprotocol/claude-agent-acp/issues/1431)) ([e369479](https://github.com/agentclientprotocol/claude-agent-acp/commit/e369479f93b34f84a11103323d097c44df42e02e))
* settle advisor tool results before completing turns ([#1247](https://github.com/agentclientprotocol/claude-agent-acp/issues/1247)) ([bed795d](https://github.com/agentclientprotocol/claude-agent-acp/commit/bed795d261620e6d5113e427a7196d94e66e7034))
* settle live steers folded into autonomous cycles ([#1185](https://github.com/agentclientprotocol/claude-agent-acp/issues/1185)) ([a4957d1](https://github.com/agentclientprotocol/claude-agent-acp/commit/a4957d17c1e1a925ba3b72b29c5dcde4faabbe17))
## [0.96.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.95.0...v0.96.0) (2026-10-26)
### Features
* report SendMessage delegations as linked session messages ([#1424](https://github.com/agentclientprotocol/claude-agent-acp/issues/1424)) ([739dde3](https://github.com/agentclientprotocol/claude-agent-acp/commit/739dde3ae5bb848bdb90e2c9150e2cc4687d480c))
* report the model of each native subagent ([#1432](https://github.com/agentclientprotocol/claude-agent-acp/issues/1432)) ([c319e5e](https://github.com/agentclientprotocol/claude-agent-acp/commit/c319e5e542aabe353bb2c5e177e4c5853392b8c4))
### Bug Fixes
* do not hold an AIR load replay until the CLI starts ([#1433](https://github.com/agentclientprotocol/claude-agent-acp/issues/1433)) ([eb9d63e](https://github.com/agentclientprotocol/claude-agent-acp/commit/eb9d63e5fde9b8fd8fe692dfb3280faba6626a42))
* refine RFD child models after a load, and retry a failed model update ([#1434](https://github.com/agentclientprotocol/claude-agent-acp/issues/1434)) ([9a8bf71](https://github.com/agentclientprotocol/claude-agent-acp/commit/9a8bf71d454fa85d03f55d4101a77e3ea7a149d0))
* send subagents' current state after a load response ([#1423](https://github.com/agentclientprotocol/claude-agent-acp/issues/1423)) ([17a967b](https://github.com/agentclientprotocol/claude-agent-acp/commit/17a967b4dab78bbb9cf9fedae60e6821578dc5cb))
* update to Claude Code 2.1.310 ([#1435](https://github.com/agentclientprotocol/claude-agent-acp/issues/1435)) ([2f3c6bb](https://github.com/agentclientprotocol/claude-agent-acp/commit/2f3c6bb7b3451a00cd77d7bb31c09e690ca220b3))
## [0.95.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.94.0...v0.95.0) (2026-10-23)
### Features
* end a turn at its result for AIR clients with backgroundSubagents ([#1262](https://github.com/agentclientprotocol/claude-agent-acp/issues/1262)) ([9b3a5c8](https://github.com/agentclientprotocol/claude-agent-acp/commit/9b3a5c891c75e78b985adb31f4e4b59a16ad2ebc))
* report read-only subagent configuration ([#1417](https://github.com/agentclientprotocol/claude-agent-acp/issues/1417)) ([6ab670b](https://github.com/agentclientprotocol/claude-agent-acp/commit/6ab670baaefb30aaac2606d8ee6524e08243e5b5))
* report RFD delegations as linked session messages ([#1405](https://github.com/agentclientprotocol/claude-agent-acp/issues/1405)) ([59e292e](https://github.com/agentclientprotocol/claude-agent-acp/commit/59e292e29f773cc6f03c0999993cbe8a2d97a7e3))
* serve experimental ACP v2 ([#1400](https://github.com/agentclientprotocol/claude-agent-acp/issues/1400)) ([4d378f9](https://github.com/agentclientprotocol/claude-agent-acp/commit/4d378f93f36d37ba5ef64af87b617706dd9c6e82))
* show messages typed in other Remote Control clients ([#1371](https://github.com/agentclientprotocol/claude-agent-acp/issues/1371)) ([5b11f47](https://github.com/agentclientprotocol/claude-agent-acp/commit/5b11f470b1c05b114c876b3b6e5b4df9441b1087))
* update to Claude Code 2.1.309 ([#1413](https://github.com/agentclientprotocol/claude-agent-acp/issues/1413)) ([a1521b6](https://github.com/agentclientprotocol/claude-agent-acp/commit/a1521b6196c58dbc5065f9c19fc9694273d3d435))
### Bug Fixes
* bump fast-uri from 3.1.8 to 3.1.9 ([#1410](https://github.com/agentclientprotocol/claude-agent-acp/issues/1410)) ([0fe22ae](https://github.com/agentclientprotocol/claude-agent-acp/commit/0fe22ae5a172b228a574665bb7136b900746041d))
* bump postcss from 8.5.30 to 8.5.31 in the minor group ([#1403](https://github.com/agentclientprotocol/claude-agent-acp/issues/1403)) ([ecaa2c9](https://github.com/agentclientprotocol/claude-agent-acp/commit/ecaa2c935b9f2b63c1846ca17c6c3398390fb0ef))
* bump the minor group across 1 directory with 2 updates ([#1416](https://github.com/agentclientprotocol/claude-agent-acp/issues/1416)) ([f7d6030](https://github.com/agentclientprotocol/claude-agent-acp/commit/f7d6030c04af23171058772480d55f6d2aee5298))
* IJAI-3744 keep a session running while its background tasks are open ([#1399](https://github.com/agentclientprotocol/claude-agent-acp/issues/1399)) ([e9f986b](https://github.com/agentclientprotocol/claude-agent-acp/commit/e9f986b8a941f5c1789078a39e474f247bd0db60))
* publish the session title as soon as the CLI has it ([#1406](https://github.com/agentclientprotocol/claude-agent-acp/issues/1406)) ([50cd76e](https://github.com/agentclientprotocol/claude-agent-acp/commit/50cd76e78dd9bb1ab0e4850a8d95431175ab5947))
* publish turns that Claude Code starts after end_turn as an async task in v1 ([#1390](https://github.com/agentclientprotocol/claude-agent-acp/issues/1390)) ([a1d0eee](https://github.com/agentclientprotocol/claude-agent-acp/commit/a1d0eeeaad3d67a0765f4b89e3d81c1af6ca0dbb))
## [0.94.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.93.0...v0.94.0) (2026-10-19)

View File

@@ -17,6 +17,7 @@ This tool implements an ACP agent by using the official [Claude Agent SDK](https
- Interactive (and background) terminals
- Custom [Slash commands](https://docs.anthropic.com/en/docs/claude-code/slash-commands)
- Client MCP servers
- An Ultracode session config option, which turns on Claude Code's dynamic workflows for every task. It appears when dynamic workflows are enabled and the model supports ultracode (any model with `xhigh` effort)
- `/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](docs/air-extensions.md#goal) under `_meta.jetbrains.air.goal`
- Structured errors, recovery, and warnings through the opt-in [session failure extension](docs/air-extensions.md#session-failure)
@@ -47,6 +48,14 @@ Task subagent as its own child session, following the [subagents RFD](https://gi
`end_turn` or `cancelled` when the SDK gives the reason. A failure is `idle` with the `error`
stop reason and the SDK's error text as its `error`. The child keeps its session for every
delegation to it.
- A child that runs outside its parent's working directory, such as an Agent call with
`isolation: "worktree"`, gets its `cwd` on `subagent_update` before its first tool call. The
adapter takes it from the `cwd` of the child's own hooks: its SubagentStart hook runs before its
first model request. It sends `cwd: null` when the child is back in its parent's directory. A
child in its parent's directory gets no `cwd`, and no child gets `additionalDirectories`: Claude
Code gives a subagent the session's additional directories, so it inherits them. A replay
announces a child with the `cwd` that its transcript recorded, and clears it after the response
when that directory no longer exists.
- Session messages replace the Agent or Task call that launches the child, which its parent is
not shown:
- The launch's prompt is a `session_message` in both transcripts: the parent's outgoing view and

View File

@@ -141,8 +141,8 @@ The `initialize` response of an AIR client carries the agent side of the extensi
}
```
The agent list does not depend on the capabilities that AIR declares, except `sessionIndex`: the agent lists it, together with `sessionArchive`, `sessionRename`, `sessionListSubscribe` and `sessionWorktrees`, only for a client that declared `sessionIndex`.
An extension is active only when the client declared its capability. The client does not declare `sessionArchive`, `sessionRename`, `sessionListSubscribe` and `sessionWorktrees`.
The agent list does not depend on the capabilities that AIR declares, except `sessionIndex`: the agent lists it, together with `sessionBatchGet`, `sessionArchive`, `sessionRename`, `sessionListSubscribe` and `sessionWorktrees`, only for a client that declared `sessionIndex`.
An extension is active only when the client declared its capability. The client does not declare `sessionBatchGet`, `sessionArchive`, `sessionRename`, `sessionListSubscribe` and `sessionWorktrees`.
The response to a client that is not AIR has no `_meta.jetbrains` key.
### Capabilities
@@ -158,6 +158,7 @@ The response to a client that is not AIR has no `_meta.jetbrains` key.
| `planFile` | yes | Sends the path of the plan file in place of the plan text of an ExitPlanMode. | [Plan file](#plan-file) |
| `backgroundSubagents` | yes | Ends a prompt turn at its result while background subagents still run. | [Background subagents](#background-subagents) |
| `sessionIndex` | on request | Pages, orders and annotates `session/list`; adds rename, archive and a list subscription. | [Session index](#session-index) |
| `sessionBatchGet` | with `sessionIndex` | Supports `_session/batch_get` for metadata of known session IDs. | [Batch get](#batch-get) |
| `sessionArchive` | with `sessionIndex` | Nothing on its own: the agent lists it to tell that it supports `_session/archive` and `_session/unarchive`. | [Archive](#archive) |
| `sessionRename` | with `sessionIndex` | Nothing on its own: the agent lists it to tell that it supports `_session/rename`. | [Rename](#rename) |
| `sessionListSubscribe` | with `sessionIndex` | Nothing on its own: the agent lists it to tell that it supports `_session/list/subscribe`. | [List subscription](#list-subscription) |
@@ -514,6 +515,17 @@ While the adapter does not know the path, a report carries no `planFilePath` key
The adapter never sends a blank path, because AIR reads a present non-file value as a retraction.
Without `rawInputRendering`, a `planFile` client gets no copy of the plan in `content` either.
### Approved plan identity
For an AIR client with plan operations, the approved plan ID is `plan-mode:<tool-call ID>`.
Claude uses the SDK tool-call ID of ExitPlanMode.
Each review keeps its own plan. Repeated updates of that review keep the same plan ID.
AIR joins the plan to its review by ID, even when the text includes user edits.
This link needs no extra metadata.
The live result, history replay, and clear-context approval use the same identity.
The identity does not depend on `planFile`: it also applies when the request carries inline plan text.
Other clients keep the `plan-mode` ID.
### Fallback
The plan text goes out as before in these cases:
@@ -707,7 +719,7 @@ A non-empty plan also gets one clear-context choice with the same priority.
After that choice, the adapter stops the old private Claude query and hides that internal rejection.
It starts a new private query under the same ACP session.
It continues the turn with `Implement the following plan:` and the complete plan.
The mode, model, agent, effort, Fast mode, and turn usage stay the same.
The mode, model, agent, effort, Fast mode, ultracode, and turn usage stay the same.
The SDK cannot apply `allowedPrompts` again, so those hints do not survive the handoff.
The adapter does not allow a callback only because the session mode is `bypassPermissions`.
@@ -1282,8 +1294,8 @@ The session index makes `session/list` a bounded, ordered page with row metadata
AIR declares `sessionIndex` in `_meta.jetbrains.air.capabilities`.
Unlike the other capabilities, the agent advertises `sessionIndex` only to a client that declared it, and never under ACP v2.
Exactly when it advertises `sessionIndex`, the agent also advertises `sessionArchive` (it supports `_session/archive` and `_session/unarchive`), `sessionRename` (it supports `_session/rename`), `sessionListSubscribe` (it supports `_session/list/subscribe` and `_session/list/unsubscribe`) and `sessionWorktrees` (rows carry `agentWorktree`, see [List response](#list-response)).
The client does not declare these four; declaring them without `sessionIndex` enables nothing.
Exactly when it advertises `sessionIndex`, the agent also advertises `sessionBatchGet` (it supports `_session/batch_get`), `sessionArchive` (it supports `_session/archive` and `_session/unarchive`), `sessionRename` (it supports `_session/rename`), `sessionListSubscribe` (it supports `_session/list/subscribe` and `_session/list/unsubscribe`) and `sessionWorktrees` (rows carry `agentWorktree`, see [List response](#list-response)).
The client does not declare these five; declaring them without `sessionIndex` enables nothing.
Everything in this section applies only to such a client, except where [Delete and close](#delete-and-close) says otherwise.
A client without the capability gets the `session/list` of before: the SDK `listSessions` call, pages of 1000 by file mtime, `offset:N` cursors, and no row `_meta`.
It gets no new notification, and the adapter starts no watcher for it.
@@ -1442,6 +1454,71 @@ In all of the session index methods and in `session/delete`, a session id matche
Loading an archived session does not unarchive it.
The CLI cleanup still deletes old transcripts (`cleanupPeriodDays`), archived or not.
### Batch get
`_session/batch_get` refreshes metadata for retained session IDs, such as pinned
sessions and folder members outside the recent list page. Check for
`sessionBatchGet` in `initialize.result._meta.jetbrains.air.capabilities` before
calling it.
The params object must contain only `sessionIds`: an array of distinct,
non-blank strings. IDs match exactly, without trimming or case normalization.
Malformed params, duplicates or extra fields return `Invalid params` (`-32602`).
The agent sets no limit on the number of IDs; the client chooses the batch size
for the latency it accepts. Archived sessions and sessions in other projects
or worktrees are eligible.
```json
{
"jsonrpc": "2.0",
"id": 7,
"method": "_session/batch_get",
"params": { "sessionIds": ["oldA", "oldB", "oldC"] }
}
```
```json
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"sessions": [
{
"sessionId": "oldA",
"cwd": "/project",
"title": "Fix the build",
"updatedAt": "2026-01-01T00:00:00.000Z",
"_meta": { "jetbrains": { "air": { "version": 1, "archived": false } } }
}
]
}
}
```
The example IDs are placeholders for stored Claude session UUIDs. `sessions`
contains the same full rows as `session/list` without a project filter, including
archived sessions and `_meta.jetbrains.air`. Copies use the same
newest-readable-copy selection as the list. Returned sessions preserve their
relative request order. Non-UUID IDs and IDs without a listable transcript are
omitted: absent, empty, unreadable or malformed transcripts, sidechains, sessions
whose continuation has history, and transcripts whose cwd cannot be recovered.
A sidecar alone does not supply a row.
`{ "sessionIds": [] }` returns `{ "sessions": [] }` without reading the store.
A store-wide failure, such as an unreadable projects root, rejects the request.
Individual results reflect the store as each ID is read.
Lookup normally addresses only the requested transcript filenames in each project
and reads their metadata. A transcript without a cwd can use a sibling's cwd,
as the list does. This fallback enumerates and stats sibling transcripts only
in the directories of rows still missing a cwd, then reads at most 64 siblings
per directory, newest mtime first. A failed recovery is cached for 60 seconds
unless the directory mtime changes. Other project histories are not enumerated.
The agent looks up 100 IDs at a time; the client may request any number of IDs.
Merge returned rows with the recent page by `sessionId`. A batch read does not
expand an existing list subscription's scope; refresh retained IDs outside that
scope explicitly.
### List subscription
`_session/list/subscribe { "cwd": "…" }` returns `{ "subscriptionId": "…" }`.
@@ -1465,6 +1542,7 @@ From then on the agent sends `_session/list/changes` notifications for the sessi
- `cwd` is required and must be an absolute path; a missing, non-string or relative `cwd` is `-32602`. Other parameters are ignored.
- The scope is the sessions of `cwd` and of the same subdirectory in every existing linked worktree of its repository, as a list with `includeWorktrees: true` resolves it, in any archive state. Sidechain and subagent transcripts are left out, as in the list.
- There is no page or recent-session limit: changes to older pinned or folder sessions within that scope are reported even outside the page AIR loaded. Pinned IDs outside the subscribed project and worktrees are not tracked; `_session/batch_get` does not add them to the subscription. AIR must subscribe to their project scope or refresh them explicitly.
- `sessions` holds the full current row of each session in scope that appeared, or whose `title`, `lastPromptAt`, `state`, `lastTurnEndedAt`, `cost`, `model`, `forkedFrom`, `agentWorktree` or `archived` changed since the last row sent for it on this subscription. A row is exactly the row of `session/list`, `_meta.jetbrains.air` included.
- After subscribe, the first change of each session sends its row in full, even when only `updatedAt` changed. From then on, a change of `updatedAt` alone sends nothing; the new `updatedAt` comes with the next change.
- `removed` holds the ids of sessions that left the scope: deleted (the transcript is gone), or moved to a path outside it. It may name a session the client does not list (one it never listed, one the list hides, such as a session continued in another one when that one changes); the client ignores such an id.
@@ -1491,7 +1569,7 @@ It is idempotent, and an unknown id also returns `{}`.
`session/delete`:
- a `sessionIndex` client: deletes every transcript of the session (empty ones too) and every `<sessionId>/` directory, also one left without a transcript by an earlier failed delete. Anything left behind fails the request. A session that runs here is closed first. As with the CLI, the session is deleted also when another Claude Code process has it open. An unknown session is `-32002`.
- an AIR client without `sessionIndex`: archives the session instead of deleting it (the title records of [Archive](#archive)), because AIR uses delete for "Done" and can reopen the session. A session that runs here is closed first. The list hides archived sessions from such a client too: those whose custom title, as the SDK list reports it, is archived. A session without a transcript fails with the error of the SDK delete, as before.
- an AIR client without `sessionIndex`: archives the session instead of deleting it (the title records of [Archive](#archive)), because AIR uses delete for "Done" and can reopen the session. A session that runs here is closed first. The list hides archived sessions from such a client too: those whose custom title, as the SDK list reports it, is archived. A session without a transcript has nothing to archive, so the delete succeeds, as ACP asks of a session that does not exist.
A `sessionIndex` client sees such a session as archived (`archived: true`), as AIR's native Claude path does.
- any other client: the SDK delete. A session without a transcript (unknown, deleted before, or
never prompted) succeeds, as ACP asks.
@@ -1517,6 +1595,7 @@ Names and semantics follow them; the transport differs, and so do the `archived`
| capability `sessionIndex` (`_meta.jetbrains.air`) | `sessionCapabilities.list.limit` |
| capability `sessionArchive` (`_meta.jetbrains.air`) | `sessionCapabilities.archive` (#2161) |
| capability `sessionRename` (`_meta.jetbrains.air`) | `sessionCapabilities.setTitle` (#1987) |
| capability `sessionBatchGet`, `_session/batch_get` | none |
| `_meta.jetbrains.air.list.limit` | `session/list` `limit` |
| `_meta.jetbrains.air.list.includeWorktrees` | `session/list` `includeWorktrees` |
| `_meta.jetbrains.air.list.archived` (`"unarchived"`, `"archived"`, `"all"`) | `session/list` `archived` (#2161), a boolean there |

694
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.94.0",
"version": "0.100.0",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -65,25 +65,24 @@
"node": ">=22"
},
"dependencies": {
"@agentclientprotocol/sdk": "2.0.1",
"@anthropic-ai/claude-agent-sdk": "0.3.309",
"@agentclientprotocol/sdk": "2.4.0",
"@anthropic-ai/claude-agent-sdk": "0.3.316",
"diff": "9.0.0",
"zod": "4.6.7"
},
"devDependencies": {
"@anthropic-ai/sdk": "1.1.0",
"@anthropic-ai/sdk": "1.3.0",
"@eslint/js": "10.0.1",
"@tsconfig/node22": "22.0.6",
"@types/node": "26.6.5",
"@typescript-eslint/eslint-plugin": "8.72.0",
"@typescript-eslint/parser": "8.72.0",
"@types/node": "26.6.6",
"@typescript-eslint/eslint-plugin": "8.72.1",
"@typescript-eslint/parser": "8.72.1",
"ajv": "8.20.0",
"ajv-formats": "3.0.1",
"eslint": "10.13.0",
"eslint": "10.14.0",
"eslint-config-prettier": "10.1.8",
"globals": "17.13.0",
"globals": "17.14.0",
"prettier": "3.9.10",
"ts-node": "10.9.2",
"typescript": "6.0.3",
"vitest": "5.0.5"
}

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,9 @@ export const AIR_PLAN_FILE_CAPABILITY = "planFile";
* subscription. Advertised only to a client that declares it.
*/
export const AIR_SESSION_INDEX_CAPABILITY = "sessionIndex";
/** Metadata lookup of known ids with `_session/batch_get`. Advertised
* exactly when {@link AIR_SESSION_INDEX_CAPABILITY} is negotiated. */
export const AIR_SESSION_BATCH_GET_CAPABILITY = "sessionBatchGet";
/**
* The agent supports `_session/archive` and `_session/unarchive`. Advertised
* exactly when {@link AIR_SESSION_INDEX_CAPABILITY} is; the client does not

View File

@@ -15,6 +15,8 @@ type AsyncTask = {
taskType: string;
description: string;
showInTranscript: boolean;
/** A Monitor tool call or an SDK `local_monitor` task: published as a monitor the client cannot stop. */
monitor: boolean;
outputFilePath?: string;
toolCallId?: string;
/**
@@ -142,7 +144,8 @@ export class AsyncTaskRuntime {
* the tasks that a tool call in another session started, for example in
* a native subagent session. `toolNameOf`: the name of the tool of a tool
* call, or `undefined` when the tool call is unknown. A task that a
* `Monitor` tool call started is ignored. */
* `Monitor` tool call started is published as a monitor: the SDK reports
* it as `local_bash`, so the adapter names it a monitor. */
private readonly options: {
notices?: boolean;
routeOf?: (toolCallId: string) => Route | undefined;
@@ -155,7 +158,7 @@ export class AsyncTaskRuntime {
const taskId = taskIdOf(message);
if (!taskId) return;
const task = this.task(taskId);
if (isSubagentTask(message) || isMonitorTask(message)) {
if (isSubagentTask(message)) {
this.ignore(task);
return;
}
@@ -186,7 +189,7 @@ export class AsyncTaskRuntime {
const taskId = taskIdOf(message);
if (!taskId) return;
const task = this.task(taskId);
if (isSubagentTask(message) || isMonitorTask(message)) {
if (isSubagentTask(message)) {
this.ignore(task);
return;
}
@@ -355,7 +358,7 @@ export class AsyncTaskRuntime {
const task = this.task(taskId);
if (task.ignored || isTerminal(task.state)) continue;
const levelTaskType = field(item, "taskType", "task_type");
if (levelTaskType === "local_agent" || levelTaskType === "local_monitor") {
if (levelTaskType === "local_agent") {
this.ignore(task);
continue;
}
@@ -408,7 +411,13 @@ export class AsyncTaskRuntime {
canStop(taskId: string): boolean {
const task = this.tasks.get(taskId);
return task?.announced === true && !task.ignored && !task.stopping && !isTerminal(task.state);
return (
task?.announced === true &&
!task.monitor &&
!task.ignored &&
!task.stopping &&
!isTerminal(task.state)
);
}
claimStop(taskId: string): boolean {
@@ -575,6 +584,7 @@ export class AsyncTaskRuntime {
taskType: "task",
description: "Background task",
showInTranscript: true,
monitor: false,
announced: false,
held: false,
deferred: [],
@@ -595,6 +605,7 @@ export class AsyncTaskRuntime {
task.startedObserved = true;
const rawTaskType = field(message, "taskType", "task_type");
if (rawTaskType !== undefined) task.taskType = friendlyTaskType(rawTaskType);
if (rawTaskType === "local_monitor") task.monitor = true;
const description = nonBlankString(message.description);
if (description) task.description = description;
else if (task.description === "Background task")
@@ -652,6 +663,7 @@ export class AsyncTaskRuntime {
private mergeLevel(task: AsyncTask, level: BackgroundTaskLevel): void {
const rawTaskType = field(level, "taskType", "task_type");
if (rawTaskType !== undefined) task.taskType = friendlyTaskType(rawTaskType);
if (rawTaskType === "local_monitor") task.monitor = true;
const description = nonBlankString(level.description);
if (description) {
task.description = description;
@@ -676,25 +688,29 @@ export class AsyncTaskRuntime {
* ends first, or when the prompt result ends the model turn
* ({@link releaseHeld}). No guess by the command text binds a task.
*
* A task that a `Monitor` tool call started is ignored: Monitor streams its
* output to the model only, so the task has nothing for the client. The
* tool_use streams before the tool runs, so the tool name is known when the
* spawn has the tool call id.
* A task that a `Monitor` tool call started is published as a monitor. The
* SDK reports it as `local_bash`, so the adapter names it a monitor instead of
* a shell. It has no transcript card because Monitor streams its output to
* the model only, and no Stop because stopping it only ends the agent's wait
* (the agent's own TaskStop still works). The tool_use streams before the
* tool runs, so the tool name is known when the spawn has the tool call id.
*/
private async announce(
task: AsyncTask,
options: { withoutToolCall?: boolean } = {},
): Promise<void> {
if (task.announced || task.ignored) return;
const toolName = task.toolCallId ? this.options.toolNameOf?.(task.toolCallId) : undefined;
if (toolName !== undefined && HIDDEN_TASK_TOOLS.has(toolName)) {
this.ignore(task);
return;
}
if (!task.toolCallId && !options.withoutToolCall && !isTerminal(task.state)) {
task.held = true;
return;
}
if (task.toolCallId && this.options.toolNameOf?.(task.toolCallId) === "Monitor") {
task.monitor = true;
}
if (task.monitor) {
task.taskType = "monitor";
task.showInTranscript = false;
}
if (task.toolCallId) task.route ??= this.options.routeOf?.(task.toolCallId);
await this.send(task, {
sessionUpdate: "async_task_spawned",
@@ -703,7 +719,7 @@ export class AsyncTaskRuntime {
taskType: task.taskType,
description: task.description,
showInTranscript: task.showInTranscript,
canStop: true,
canStop: !task.monitor,
...(task.outputFilePath ? { outputFilePath: task.outputFilePath } : {}),
...(task.toolCallId ? { toolCallId: task.toolCallId } : {}),
});
@@ -1051,13 +1067,6 @@ function isBackgroundTask(isBackgrounded: unknown, taskType: unknown): boolean {
return taskType !== "local_bash" && taskType !== "local_agent";
}
/** The tools whose async tasks the client never sees. */
const HIDDEN_TASK_TOOLS = new Set(["Monitor"]);
function isMonitorTask(message: AsyncTaskStarted): boolean {
return field(message, "taskType", "task_type") === "local_monitor";
}
function isSubagentTask(message: AsyncTaskStarted): boolean {
return (
field(message, "subagentType", "subagent_type") !== undefined ||

131
src/background-output.ts Normal file
View File

@@ -0,0 +1,131 @@
/**
* The output of a command that went to the background, read from the file
* that Claude Code writes it to. Claude Code streams no output of such a
* command, so its display terminal would show only the placeholder of the
* Bash result ("Command running in background with ID …") until the command
* exits. ACP v2 asks an Agent to keep a listed terminal's output current
* whenever it can read it (the v2 prompt lifecycle, "Background work").
*
* The file is polled: watching a file is not reliable on every platform, and
* a missing file only means that nothing was written yet.
*/
import { open } from "node:fs/promises";
import { StringDecoder } from "node:string_decoder";
/** How often a running command's output file is read. */
export const BACKGROUND_OUTPUT_POLL_MS = 500;
/** The most bytes one read takes, so a burst of output does not block the loop. */
export const BACKGROUND_OUTPUT_READ_BYTES = 256 * 1024;
/**
* The most unread bytes that are still sent. A command that writes faster
* than the polls read falls behind. Past this, the reads skip to the file's
* last bytes, as a snapshot: a terminal shows the latest output, and a
* client keeps a bounded amount of it anyway.
*/
export const BACKGROUND_OUTPUT_MAX_BACKLOG_BYTES = 1024 * 1024;
/**
* Text that the file adds. A snapshot replaces the terminal's output so far:
* the first one replaces the placeholder of the Bash result, and a later one
* follows a skip or a file that shrank. Other text appends to it.
*/
export type BackgroundOutput = { text: string; replaces: boolean };
/**
* Follows the output file `path` of one background command and hands each
* piece of new text to `publish`, in order. Reads never overlap. A read that
* fails, for example because the file does not exist yet, is tried again at
* the next poll. `publish` must not reject: the next read waits for it.
*/
export class BackgroundOutputTail {
private offset = 0;
private decoder = new StringDecoder("utf8");
/** Whether the next text replaces the terminal's output. */
private replaces = true;
private stopped = false;
private reading: Promise<void> = Promise.resolve();
private readonly timer: ReturnType<typeof setInterval>;
constructor(
private readonly path: string,
private readonly publish: (output: BackgroundOutput) => Promise<void>,
) {
this.timer = setInterval(() => void this.read(false), BACKGROUND_OUTPUT_POLL_MS);
// A command that runs on must not keep the agent process alive.
this.timer.unref();
}
/**
* Reads the rest of the file and stops. The command ended, so what the file
* holds is its whole output, and it goes out before the terminal's exit.
*/
async finish(): Promise<void> {
clearInterval(this.timer);
await this.read(true);
this.stopped = true;
}
/** Stops without reading more: the session ended. */
stop(): void {
clearInterval(this.timer);
this.stopped = true;
}
/**
* Reads what the file adds, one read of at most
* {@link BACKGROUND_OUTPUT_READ_BYTES} at a time, and publishes each read's
* text before the next. `toEnd` reads until the end of the file, and ends a
* character that the command cut off with a replacement character.
*/
private read(toEnd: boolean): Promise<void> {
this.reading = this.reading.then(async () => {
if (this.stopped) return;
let file;
try {
file = await open(this.path, "r");
} catch {
// Nothing written yet, or the file is gone: try again at the next
// poll. At the end, the terminal keeps the output it has.
return;
}
try {
const { size } = await file.stat();
// A file that shrank is a new one: its output starts over. Past the
// backlog, the reads skip to the file's last bytes.
const start = size < this.offset ? 0 : this.offset;
const skipTo = Math.max(start, size - BACKGROUND_OUTPUT_MAX_BACKLOG_BYTES);
if (skipTo !== this.offset) {
this.offset = skipTo;
this.decoder = new StringDecoder("utf8");
this.replaces = true;
}
const buffer = Buffer.alloc(BACKGROUND_OUTPUT_READ_BYTES);
let bytesRead: number;
do {
({ bytesRead } = await file.read(buffer, 0, buffer.length, this.offset));
this.offset += bytesRead;
const text = this.decoder.write(buffer.subarray(0, bytesRead));
if (text !== "") await this.send(text);
} while (toEnd && bytesRead > 0);
if (toEnd) {
const rest = this.decoder.end();
// A command that wrote nothing still replaces the placeholder.
if (rest !== "" || this.replaces) await this.send(rest);
}
} catch {
// The read failed: try again at the next poll.
} finally {
await file.close();
}
});
return this.reading;
}
private async send(text: string): Promise<void> {
const replaces = this.replaces;
this.replaces = false;
await this.publish({ text, replaces });
}
}

627
src/background-work.ts Normal file
View File

@@ -0,0 +1,627 @@
/**
* The background work of a session: the work that Claude Code keeps running
* independently of the session's foreground work, which ACP v2 lists on every
* `state_update` of the session as `background` (the v2 prompt lifecycle,
* "Background work").
*
* The lists are built from Claude Code's live background task set
* (`background_tasks_changed`), without the tasks it marks `ambient`, which
* are housekeeping that it started on its own. The root session and each
* announced subagent session (the subagents RFD form) has its own list. Each
* task is listed once, in the list of the session that runs it:
*
* - A command that went to the background: its display terminal, from the
* Bash tool result until its task ends (`Session.backgroundTerminals`).
* - A detached subagent: its child session, in its parent's list, while it
* works, and while it is idle with background work of its own.
* - A task that a tool call started, such as a `Monitor`, a `Workflow` or a
* backgrounded MCP call: that tool call, kept `in_progress` (or `paused`)
* from its tool result until its task ends (`Session.backgroundToolCalls`).
* - Any other task, once its `task_started` came and the tool call that
* started it, if any, returned: a `_claude/task` item with the task's id
* and description. For example, a task whose tool call ended before the
* task joined the level.
*
* Only a session whose interrupt spares background tasks reports its lists
* (`Session.reportsBackground`): `session/cancel` must leave listed work
* running.
*/
import { RequestError } from "@agentclientprotocol/sdk/unstable";
import type { AcpSessionNotification } from "./acp-subagents.js";
import type { NativeSubagent } from "./native-subagents.js";
import { type AcpToolCallRenderer, V2_CANCELLED, V2_PAUSED } from "./tool-calls/renderer.js";
import { type BackgroundOutput, BackgroundOutputTail } from "./background-output.js";
/** An item of a `background` list. */
export type BackgroundItem =
| { type: "terminal"; terminalId: string }
| { type: "tool_call"; toolCallId: string }
| { type: "subagent"; sessionId: string }
| { type: "_claude/task"; taskId: string; description: string };
/** The display terminal of a command that went to the background (see `Session.backgroundTerminals`). */
export interface BackgroundTerminal {
/** The Bash tool call, whose id names the terminal. */
toolCallId: string;
/** The subagent tool call that ran the command, if any. */
parentToolUseId?: string;
/** The file that Claude Code writes the command's output to, from the Bash result. */
outputFilePath?: string;
/** Reads that file into the terminal while the command runs. */
output?: BackgroundOutputTail;
}
/** A tool call whose task runs on in the background (see `Session.backgroundToolCalls`). */
export interface BackgroundToolCall {
toolCallId: string;
/** The tool use of the call, which Claude Code no longer caches after its result. */
toolUse: { name: string; input: unknown };
/** The subagent tool call that made the call, if any. */
parentToolUseId?: string;
paused?: boolean;
}
/** How a background task ended, as Claude Code reports it. Undefined when it did not say. */
export type BackgroundTaskEnd = "completed" | "failed" | "stopped" | undefined;
/** The live background task set of `background_tasks_changed`, by task id. */
export type BackgroundLevel = Map<
string,
{ ambient: boolean; taskType: string; description: string; parentTaskId?: string }
>;
/** The work state of a session, as its `state_update` names it. */
export type WorkStateName = "idle" | "running" | "requires_action" | "unknown";
/** A new list of the session `sessionId`, with what its repeated state needs. */
export interface BackgroundReport {
sessionId: string;
items: BackgroundItem[];
/** The session's work state now, or undefined when it has none yet. */
state: WorkStateName | undefined;
/** The parent of a subagent session, which mirrors the child's state. */
parentSessionId?: string;
}
/** Where the agent reports the lists (see the agent's `background` option). */
export interface BackgroundSink {
/** The list of a session changed, or is reported for the first time. */
changed(report: BackgroundReport): void;
/** The session ended: its list is no longer reported. */
forget(sessionId: string): void;
}
/**
* The state of a session that other modules own, and that the background
* work reads: Claude Code's tasks as the consumer tracks them, the subagent
* sessions, and the query that stops a task.
*/
export interface BackgroundOwner {
liveBackgroundTasks: Map<string, { parentToolUseId?: string; isSubagent: boolean }>;
nativeSubagentsByTaskId?: Map<string, NativeSubagent>;
query: { stopTask(taskId: string): Promise<void> };
}
/** A task of a `background_tasks_changed` payload. */
export type LevelTask = {
task_id: string;
task_type: string;
description: string;
parent_task_id?: string;
ambient?: boolean;
};
/** The most returned tool calls that {@link SessionBackgroundWork} remembers. */
const MAX_RETURNED_CALLS = 1024;
/** The tools whose structured result names the task that the call started. */
const TASK_TOOLS = new Set(["Monitor", "Workflow"]);
/**
* The background work of one root session, and of its subagent sessions: the
* terminals and tool calls of the tasks that run in the background, and what
* the client is told of them.
*
* Terminals are kept for every client that renders them, as before the
* background lists. The rest is kept only when `reported`.
*/
export class SessionBackgroundWork {
/** Claude Code's live background task set, from its last `background_tasks_changed`. */
private level: BackgroundLevel = new Map();
/** The terminals of the commands that went to the background, by task id. */
private readonly terminals = new Map<string, BackgroundTerminal>();
/** The tool calls whose tasks went on in the background, by task id. */
private readonly toolCalls = new Map<string, BackgroundToolCall>();
/**
* The tool calls that returned, oldest first. A task that a call started
* is part of that call until it returns, and the foreground work waits on
* it. The task's `task_started` can come after the call returned. Bounded
* by {@link MAX_RETURNED_CALLS}: a task joins the level around the time its
* call returns.
*/
private readonly returned = new Set<string>();
/**
* `reported`: whether the client gets the lists. Only when Claude Code's
* interrupt spares background tasks, as `perTaskStopAffordance` makes it.
*/
constructor(
private readonly sessionId: string,
readonly reported: boolean,
) {}
/**
* Keeps the display terminal of the command `taskId` running after its Bash
* result, until its task ends (see `withRunningTerminal`).
*/
terminalStarted(taskId: string, terminal: BackgroundTerminal): void {
this.terminals.set(taskId, terminal);
}
/**
* Reads what a tool result message tells of the background tasks:
*
* - The returned calls no longer hold the tasks that they started.
* - Each call whose task goes on in the background is registered: a
* `Monitor` or `Workflow` call, whose structured result names its task,
* or a call that a live task names as its tool use, such as a
* backgrounded MCP call. The task must be in the level: one that ended
* already, or that Claude Code marks `ambient`, leaves its call as it is.
*
* Returns the ids of the registered calls, whose result then reports
* `in_progress` (see `withRunningToolCall`).
*/
toolResults(
owner: BackgroundOwner,
content: unknown,
toolUseResult: unknown,
toolUses: Record<string, { name: string; input: unknown }>,
parentToolUseId: string | null,
): Set<string> {
const registered = new Set<string>();
if (!this.reported || !Array.isArray(content)) return registered;
const results = content.filter(
(block): block is { type: "tool_result"; tool_use_id: string; is_error?: boolean } =>
typeof block === "object" &&
block !== null &&
block.type === "tool_result" &&
typeof block.tool_use_id === "string",
);
for (const { tool_use_id: toolCallId } of results) {
this.returned.add(toolCallId);
if (this.returned.size > MAX_RETURNED_CALLS) {
this.returned.delete(this.returned.values().next().value!);
}
}
const succeeded = results.filter((block) => block.is_error !== true);
const structuredTaskId =
succeeded.length === 1 &&
typeof toolUseResult === "object" &&
toolUseResult !== null &&
typeof (toolUseResult as { taskId?: unknown }).taskId === "string"
? (toolUseResult as { taskId: string }).taskId
: undefined;
for (const { tool_use_id: toolCallId } of succeeded) {
const toolUse = toolUses[toolCallId];
if (!toolUse) continue;
const taskId =
(TASK_TOOLS.has(toolUse.name) ? structuredTaskId : undefined) ??
[...owner.liveBackgroundTasks].find(
([, task]) => task.parentToolUseId === toolCallId && !task.isSubagent,
)?.[0];
const task = taskId === undefined ? undefined : this.level.get(taskId);
if (
!task ||
task.ambient ||
task.taskType === "local_bash" ||
task.taskType === "local_agent"
) {
continue;
}
this.toolCalls.set(taskId!, {
toolCallId,
toolUse,
...(parentToolUseId ? { parentToolUseId } : {}),
});
registered.add(toolCallId);
}
return registered;
}
/**
* Starts reading the output of the background command `taskId` into its
* terminal, when the client gets the lists and the Bash result named the
* command's output file. `send` sends an update of the terminal's tool
* call, routed as the call's other updates. Called once the Bash result's
* own updates are sent: the first output replaces the placeholder that they
* gave the terminal.
*/
followOutput(taskId: string, send: Send): void {
const terminal = this.terminals.get(taskId);
if (!this.reported || !terminal?.outputFilePath || terminal.output) return;
terminal.output = new BackgroundOutputTail(terminal.outputFilePath, (output) =>
send(terminalOutputUpdate(this.sessionId, terminal, output)),
);
}
/**
* The task `taskId` ended. Reads the rest of its command's output, then
* exits its terminal, and gives a tool call whose task it was its final
* status: `cancelled` for a task that Claude Code stopped, and `completed`
* for one that ended without saying how, such as one that the level
* dropped: it no longer runs, and reported no failure. Claude Code reports
* no exit code for a command, and `completed` does not mean 0: Claude Code
* accepts some non-zero codes as a success, such as grep's 1.
*/
async taskEnded(taskId: string, end: BackgroundTaskEnd, send: Send): Promise<void> {
const terminal = this.terminals.get(taskId);
if (terminal) {
await terminal.output?.finish();
this.terminals.delete(taskId);
await send(terminalExit(this.sessionId, terminal));
}
const toolCall = this.toolCalls.get(taskId);
if (toolCall) {
this.toolCalls.delete(taskId);
await send(
toolCallStatusUpdate(
this.sessionId,
toolCall,
end === "stopped" ? V2_CANCELLED : end === "failed" ? "failed" : "completed",
),
);
}
}
/**
* The update that reports the background tool call of the task `taskId` as
* `paused`, or as `in_progress` again, when `paused` changes it. Claude Code
* pauses a workflow this way.
*/
taskPaused(taskId: string, paused: boolean): AcpSessionNotification | undefined {
const toolCall = this.toolCalls.get(taskId);
if (!toolCall || (toolCall.paused ?? false) === paused) return undefined;
toolCall.paused = paused;
return toolCallStatusUpdate(this.sessionId, toolCall, paused ? V2_PAUSED : "in_progress");
}
/**
* Takes Claude Code's new live background task set. The terminals and tool
* calls whose tasks it no longer holds end first, before a list drops them,
* as v2 requires. A terminal or tool call is registered at its tool result,
* after its task joined the level, so a later level without the task means
* that it ended. Claude Code usually sends a task's end before that level,
* which ends them first, but the SDK does not promise that order.
*/
async levelChanged(tasks: LevelTask[], send: Send): Promise<void> {
if (!this.reported) return;
const level: BackgroundLevel = new Map(
tasks.map((task) => [
task.task_id,
{
ambient: task.ambient === true,
taskType: task.task_type,
description: task.description,
...(task.parent_task_id !== undefined ? { parentTaskId: task.parent_task_id } : {}),
},
]),
);
for (const taskId of [...this.terminals.keys(), ...this.toolCalls.keys()]) {
if (!level.has(taskId)) await this.taskEnded(taskId, undefined, send);
}
this.level = level;
}
/**
* The query closed: Claude Code's background tasks end with its process.
* Stops reading output, and returns the updates that exit the root
* session's terminals and cancel its tool calls. The level empties, so the
* next lists are empty. A subagent's command or tool call is left as it
* was: it is on the child's stream, which the closed query's routing owned.
*/
queryClosed(): AcpSessionNotification[] {
for (const terminal of this.terminals.values()) terminal.output?.stop();
if (!this.reported) return [];
const updates: AcpSessionNotification[] = [];
for (const [taskId, terminal] of this.terminals) {
if (terminal.parentToolUseId) continue;
this.terminals.delete(taskId);
updates.push(terminalExit(this.sessionId, terminal));
}
for (const [taskId, toolCall] of this.toolCalls) {
if (toolCall.parentToolUseId) continue;
this.toolCalls.delete(taskId);
updates.push(toolCallStatusUpdate(this.sessionId, toolCall, V2_CANCELLED));
}
this.level = new Map();
this.returned.clear();
return updates;
}
/**
* The `background` list of the root session and of each of its announced
* subagent sessions, with what their repeated states need, deeper sessions
* first and the root last: a child's own list goes out before its parent's
* list drops it. Each list is empty while the level has not been reported:
* Claude Code sends none before the first background task starts.
*/
reports(owner: BackgroundOwner, rootState: WorkStateName): BackgroundReport[] {
const children = [...(owner.nativeSubagentsByTaskId ?? [])].filter(
([, child]) => child.announced,
);
const childByTask = new Map(children);
const childByToolUse = new Map(
children.flatMap(([, child]) =>
child.parentToolUseId ? [[child.parentToolUseId, child] as const] : [],
),
);
const lists = new Map<string, BackgroundItem[]>([
[this.sessionId, []],
...children.map(([, child]): [string, BackgroundItem[]] => [child.sessionId, []]),
]);
// A subagent's command or tool call runs on the child's stream.
const ownerOf = (parentToolUseId: string | undefined) =>
parentToolUseId ? childByToolUse.get(parentToolUseId)?.sessionId : this.sessionId;
for (const [taskId, task] of this.level) {
if (task.ambient) continue;
const terminal = this.terminals.get(taskId);
if (terminal) {
const session = ownerOf(terminal.parentToolUseId);
if (session)
lists.get(session)!.push({ type: "terminal", terminalId: terminal.toolCallId });
continue;
}
const toolCall = this.toolCalls.get(taskId);
if (toolCall) {
const session = ownerOf(toolCall.parentToolUseId);
if (session)
lists.get(session)!.push({ type: "tool_call", toolCallId: toolCall.toolCallId });
continue;
}
// A command without a terminal yet is one that foreground work waits on.
// A subagent is listed below, as its session.
if (task.taskType === "local_bash" || task.taskType === "local_agent") continue;
// A task joins the level before its `task_started`, and before the tool
// call that starts it returns. Until both, the task is unknown or part
// of that tool call.
const live = owner.liveBackgroundTasks.get(taskId);
if (!live || (live.parentToolUseId && !this.returned.has(live.parentToolUseId))) continue;
const parent = task.parentTaskId ? childByTask.get(task.parentTaskId) : undefined;
lists
.get(parent?.sessionId ?? this.sessionId)!
.push({ type: "_claude/task", taskId, description: task.description });
}
// A child is listed in its parent's list while it works in the background,
// and while it is idle with background work of its own. Deeper children
// first, so a child's list is whole before its parent's reads it.
const sessions = new Map(children.map(([, child]) => [child.sessionId, child]));
const depth = (child: NativeSubagent): number => {
const parent = sessions.get(child.parentSessionId);
return parent ? depth(parent) + 1 : 0;
};
const ordered = children.sort(([, a], [, b]) => depth(b) - depth(a));
for (const [taskId, child] of ordered) {
const task = this.level.get(taskId);
const working =
child.workState !== undefined &&
child.workState !== "idle" &&
task !== undefined &&
!task.ambient;
if (working || lists.get(child.sessionId)!.length > 0) {
lists.get(child.parentSessionId)?.push({ type: "subagent", sessionId: child.sessionId });
}
}
return [
...ordered.map(([, child]) => ({
sessionId: child.sessionId,
items: lists.get(child.sessionId)!,
state: workStateName(child.workState),
parentSessionId: child.parentSessionId,
})),
{ sessionId: this.sessionId, items: lists.get(this.sessionId)!, state: rootState },
];
}
/**
* The current state of each background tool call in `toolCallIds`: the
* call as `renderer` shows its tool use, with its status now (see
* `ClaudeAcpAgent.publishBackgroundToolCalls`).
*/
toolCallStates(
toolCallIds: string[],
renderer: AcpToolCallRenderer,
cwd: string,
): AcpSessionNotification[] {
return [...this.toolCalls.values()]
.filter((toolCall) => toolCallIds.includes(toolCall.toolCallId))
.map((toolCall) => {
const update = renderer.toolCall({ id: toolCall.toolCallId, ...toolCall.toolUse }, { cwd });
const status = toolCall.paused ? V2_PAUSED : "in_progress";
return {
sessionId: this.sessionId,
update: { ...update, status } as AcpSessionNotification["update"],
};
});
}
/**
* The task of the terminal or tool call `target` that the session
* `sessionId` lists, or undefined when it lists no such item.
*/
listedTask(
owner: BackgroundOwner,
sessionId: string,
target: BackgroundTarget,
): string | undefined {
if (!this.reported) return undefined;
const listed = this.reports(owner, "idle")
.find((report) => report.sessionId === sessionId)
?.items.some((item) =>
"terminalId" in target
? item.type === "terminal" && item.terminalId === target.terminalId
: item.type === "tool_call" && item.toolCallId === target.toolCallId,
);
if (!listed) return undefined;
const id = "terminalId" in target ? target.terminalId : target.toolCallId;
const entries = "terminalId" in target ? this.terminals : this.toolCalls;
return [...entries].find(([, entry]) => entry.toolCallId === id)?.[0];
}
}
type Send = (notification: AcpSessionNotification) => Promise<void>;
/** A terminal or tool call that `terminal/cancel` or `tool_call/cancel` names. */
export type BackgroundTarget = { terminalId: string } | { toolCallId: string };
/**
* Stops the task of a terminal or tool call that the session `sessionId`
* lists (v2 `terminal/cancel` and `tool_call/cancel`). `sessions` are the
* root sessions: a subagent session's list is built with its root's. The
* terminal exits, or the tool call gets its final status, when Claude Code
* reports the task stopped.
*/
export async function cancelBackgroundWork(
sessions: Array<BackgroundOwner & { background?: SessionBackgroundWork }>,
sessionId: string,
target: BackgroundTarget,
): Promise<void> {
const notCancellable = () =>
"terminalId" in target
? RequestError.invalidParams(
{ sessionId, terminalId: target.terminalId },
`Terminal ${target.terminalId} is not a command running in the background, so it cannot be cancelled.`,
)
: RequestError.invalidParams(
{ sessionId, toolCallId: target.toolCallId },
`Tool call ${target.toolCallId} is not running in the background, so it cannot be cancelled.`,
);
for (const session of sessions) {
const taskId = session.background?.listedTask(session, sessionId, target);
if (taskId === undefined) continue;
try {
await session.query.stopTask(taskId);
} catch (error) {
// The task ended meanwhile: it has ended on the client too, or is about to.
if (session.background?.listedTask(session, sessionId, target) === undefined) {
throw notCancellable();
}
throw error;
}
return;
}
throw notCancellable();
}
/** The update that gives the terminal of `terminal` the text `output`. */
function terminalOutputUpdate(
sessionId: string,
terminal: BackgroundTerminal,
output: BackgroundOutput,
): AcpSessionNotification {
const data = { terminal_id: terminal.toolCallId, data: output.text };
return {
sessionId,
update: {
sessionUpdate: "tool_call_update",
toolCallId: terminal.toolCallId,
_meta: {
...(terminal.parentToolUseId
? { claudeCode: { parentToolUseId: terminal.parentToolUseId } }
: {}),
...(output.replaces ? { terminal_output: data } : { terminal_output_delta: data }),
},
},
};
}
/** The update that exits the terminal of `terminal`, with no exit code. */
function terminalExit(sessionId: string, terminal: BackgroundTerminal): AcpSessionNotification {
return {
sessionId,
update: {
sessionUpdate: "tool_call_update",
toolCallId: terminal.toolCallId,
_meta: {
...(terminal.parentToolUseId
? { claudeCode: { parentToolUseId: terminal.parentToolUseId } }
: {}),
terminal_exit: { terminal_id: terminal.toolCallId, exit_code: null, signal: null },
},
},
};
}
function toolCallStatusUpdate(
sessionId: string,
toolCall: BackgroundToolCall,
status: NonNullable<
Extract<AcpSessionNotification["update"], { sessionUpdate: "tool_call_update" }>["status"]
>,
): AcpSessionNotification {
return {
sessionId,
update: {
sessionUpdate: "tool_call_update",
toolCallId: toolCall.toolCallId,
status,
...(toolCall.parentToolUseId
? { _meta: { claudeCode: { parentToolUseId: toolCall.parentToolUseId } } }
: {}),
},
};
}
/**
* Publishes the lists of each session that reports them, each time one
* changes. The sink sends them to the client synchronously, so a list follows
* the updates sent before it.
*/
export class BackgroundWorkReporter {
/** The list last published for each session, as JSON, and the root session it is built with. */
private readonly published = new Map<string, { json: string; root: string }>();
constructor(private readonly sink?: BackgroundSink) {}
/** Whether the lists are reported at all. */
get enabled(): boolean {
return this.sink !== undefined;
}
/**
* Publishes each list of the root session `rootSessionId` that changed. The
* first call for a session publishes its list, also when it is empty.
* `rootState` is the root session's state now.
*/
sync(
rootSessionId: string,
session: BackgroundOwner & { background?: SessionBackgroundWork },
rootState: WorkStateName,
): void {
if (!this.sink || !session.background?.reported) return;
for (const report of session.background.reports(session, rootState)) {
const json = JSON.stringify(report.items);
if (this.published.get(report.sessionId)?.json === json) continue;
this.published.set(report.sessionId, { json, root: rootSessionId });
this.sink.changed(report);
}
}
/** The root session `rootSessionId` ended, and its subagent sessions with it. */
forget(rootSessionId: string): void {
for (const [sessionId, { root }] of this.published) {
if (root !== rootSessionId) continue;
this.published.delete(sessionId);
this.sink?.forget(sessionId);
}
}
}
/** A child's work state, when it is one that v2 names. */
function workStateName(state: string | undefined): WorkStateName | undefined {
return state === "idle" ||
state === "running" ||
state === "requires_action" ||
state === "unknown"
? state
: undefined;
}

125
src/blob-resources.ts Normal file
View File

@@ -0,0 +1,125 @@
/**
* Embedded `resource` prompt blocks whose resource is a base64 `blob`, and the
* Claude content each supported MIME type becomes.
*/
import type { BlobResourceContents } from "@agentclientprotocol/sdk/unstable";
/** Image types the Messages API accepts as a base64 `image` block. */
const IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"] as const;
type ImageMimeType = (typeof IMAGE_MIME_TYPES)[number];
/** Non-`text/*` types whose bytes are text, e.g. a `.json` or `.ts` file. */
const TEXT_APPLICATION_MIME_TYPES = new Set([
"application/json",
"application/xml",
"application/javascript",
"application/typescript",
"application/x-yaml",
"application/yaml",
"application/toml",
"application/x-sh",
"application/sql",
"application/graphql",
]);
export type BlobResourceContent =
| {
kind: "image";
block: {
type: "image";
source: { type: "base64"; media_type: ImageMimeType; data: string };
};
}
| {
kind: "document";
block: {
type: "document";
source: { type: "base64"; media_type: "application/pdf"; data: string };
};
}
/** The decoded text, to send like a text resource. */
| { kind: "text"; text: string };
/**
* The MIME type without parameters or case, e.g. `text/plain; charset=utf-8` ->
* `text/plain`. `image/jpg`, which some clients send, becomes `image/jpeg`.
*/
function baseMimeType(mimeType: string | null | undefined): string | undefined {
const base = mimeType?.split(";")[0].trim().toLowerCase();
if (base === "image/jpg") return "image/jpeg";
return base || undefined;
}
/**
* Decodes a text blob with the `charset` parameter of its MIME type, e.g.
* `text/csv; charset=iso-8859-1`, and UTF-8 when there is none or the label is
* unknown. A leading byte order mark is dropped.
*/
function decodeText(blob: string, mimeType: string | null | undefined): string {
const charset = /;\s*charset\s*=\s*"?([^";\s]+)/i.exec(mimeType ?? "")?.[1];
let decoder: TextDecoder;
try {
decoder = new TextDecoder(charset ?? "utf-8");
} catch {
decoder = new TextDecoder("utf-8");
}
return decoder.decode(Buffer.from(blob, "base64"));
}
function isImageMimeType(mimeType: string): mimeType is ImageMimeType {
return (IMAGE_MIME_TYPES as readonly string[]).includes(mimeType);
}
function isTextMimeType(mimeType: string): boolean {
return (
mimeType.startsWith("text/") ||
TEXT_APPLICATION_MIME_TYPES.has(mimeType) ||
mimeType.endsWith("+json") ||
mimeType.endsWith("+xml")
);
}
/**
* Whether {@link blobResourceToClaude} can send a blob of this MIME type. A blob
* without a MIME type is not supported: its bytes could be anything.
*/
export function isSupportedBlobMimeType(mimeType: string | null | undefined): boolean {
const base = baseMimeType(mimeType);
if (!base) return false;
return isImageMimeType(base) || base === "application/pdf" || isTextMimeType(base);
}
/**
* The Claude content for a blob resource: an `image` block for PNG, JPEG, GIF
* and WebP, a `document` block for PDF, and the decoded text for text
* types such as `text/markdown` or `application/json`. Returns `undefined` for
* any other type, e.g. `application/zip`.
*/
export function blobResourceToClaude(
resource: BlobResourceContents,
): BlobResourceContent | undefined {
const mimeType = baseMimeType(resource.mimeType);
if (!mimeType) return undefined;
if (isImageMimeType(mimeType)) {
return {
kind: "image",
block: {
type: "image",
source: { type: "base64", media_type: mimeType, data: resource.blob },
},
};
}
if (mimeType === "application/pdf") {
return {
kind: "document",
block: {
type: "document",
source: { type: "base64", media_type: "application/pdf", data: resource.blob },
},
};
}
if (isTextMimeType(mimeType)) {
return { kind: "text", text: decodeText(resource.blob, resource.mimeType) };
}
return undefined;
}

View File

@@ -39,11 +39,13 @@ export type ClearContextSession<Turn extends ClearContextTurn = ClearContextTurn
configOptions: SessionConfigOption[];
currentAgent: string;
fastModeEnabled: boolean;
ultracodeEnabled: boolean;
effortPinnedLevel?: string;
activeTurn?: Turn | null;
turnQueue?: Turn[];
pendingExitPlanContextReset?: ClearContextReset;
contextUsedTokens?: number;
contextWindowSize: number;
sentUserUuids?: Set<string>;
input: { push(message: SDKUserMessage): unknown };
};
@@ -59,10 +61,14 @@ export type ClearContextCoordinatorHost<
options: { publicSessionId: string; permissionMode: PermissionMode },
): Promise<Session>;
applyFastMode(session: Session, enabled: boolean): Promise<void>;
applyUltracode(session: Session, enabled: boolean): Promise<void>;
/** Publishes the fresh context's mode, config options, and usage: the
* context starts empty in a window of `contextWindowSize` tokens. */
publishSessionState(
sessionId: string,
mode: PermissionMode,
configOptions: SessionConfigOption[],
contextWindowSize: number,
): Promise<void>;
continuationMessage(sessionId: string, plan: string, promptUuid: string): SDKUserMessage;
ensureConsumer(session: Session, sessionId: string): void;
@@ -146,6 +152,16 @@ export async function continuePlanInFreshContext<
}
assertRestartActive(freshSession);
}
// The fresh query seeds ultracode from settings only, so a toggle made
// during the old session would otherwise be lost.
if (oldSession.ultracodeEnabled !== freshSession.ultracodeEnabled) {
try {
await host.applyUltracode(freshSession, oldSession.ultracodeEnabled);
} catch (error) {
host.logError("Failed to restore ultracode after clearing context:", error);
}
assertRestartActive(freshSession);
}
oldSession.activeTurn = null;
oldSession.turnQueue = [];
@@ -153,7 +169,12 @@ export async function continuePlanInFreshContext<
freshSession.contextUsedTokens = 0;
try {
await host.publishSessionState(sessionId, reset.mode, freshSession.configOptions);
await host.publishSessionState(
sessionId,
reset.mode,
freshSession.configOptions,
freshSession.contextWindowSize,
);
} catch (error) {
host.logError("Failed to publish clear-context session state:", error);
}

View File

@@ -89,7 +89,7 @@ export type PatchForm = "air" | "v2";
/**
* How a git patch names its file. `git` is git's own form: the leading slash
* dropped and the `a/` and `b/` prefixes added. `absolute` is the form of an
* ACP v2 `git_patch`, whose paths must be absolute: the path itself.
* ACP v2 `git_patch`, whose paths must be absolute: the native path itself.
*/
type GitPatchPaths = "git" | "absolute";
@@ -438,11 +438,11 @@ export function gitPatchText(
}
function gitPatchHeader(filePath: string, change: FileChange, paths: GitPatchPaths): string[] {
// A Windows path gets forward slashes so the header names the same file on
// every platform.
const slashed = filePath.replaceAll("\\", "/");
// git drops one leading slash of an absolute path.
const name = paths === "git" ? slashed.replace(/^\/+/u, "") : slashed;
// git's form uses forward slashes, so the header names the same file on
// every platform, and drops one leading slash of an absolute path. An ACP v2
// patch keeps the native path so that, once unquoted, it equals the `changes`
// entry: `C:\work\App.ts` is written `"C:\\work\\App.ts"`.
const name = paths === "git" ? filePath.replaceAll("\\", "/").replace(/^\/+/u, "") : filePath;
const oldName = quoteGitPath(paths === "git" ? "a/" : "", name);
const newName = quoteGitPath(paths === "git" ? "b/" : "", name);
// git ends a ---/+++ name that contains a space with a tab, for GNU patch.

View File

@@ -1,8 +1,7 @@
import { randomUUID } from "node:crypto";
import { CreateElicitationResponse } from "@agentclientprotocol/sdk/unstable";
import type {
CreateElicitationRequest,
ElicitationAcceptAction,
CreateElicitationResponse,
ElicitationPropertySchema,
ElicitationSchema,
EnumOption,
@@ -75,17 +74,22 @@ export function mcpElicitationToCreateRequest(
}
/**
* Content of an accepted elicitation response.
* Content of an accepted elicitation response, or `{}` when the response is
* no accept or its `content` is no JSON object.
*
* Uses the SDK's validating guard rather than an `action === "accept"` check:
* the guard both narrows past the union's custom/future variant and validates
* the payload, so a malformed accept (right tag, ill-typed content) yields
* empty content — the same classification the SDK's wire validators apply.
* The values are not checked. v1 allows a string, number, boolean, or string
* array; v2 also allows any other JSON value, such as an object. An MCP
* server checks the content against its own `requestedSchema`, so it gets the
* content as the client sent it: before, an accept whose content held an
* object reached the server as `{ action: "accept", content: {} }`. Callers
* that read a field must check its type.
*/
function acceptedElicitationContent(
response: CreateElicitationResponse,
): NonNullable<ElicitationAcceptAction["content"]> {
return CreateElicitationResponse.isAccept(response) ? (response.content ?? {}) : {};
function acceptedElicitationContent(response: CreateElicitationResponse): Record<string, unknown> {
if (response.action !== "accept") return {};
const content: unknown = (response as { content?: unknown }).content;
return content !== null && typeof content === "object" && !Array.isArray(content)
? (content as Record<string, unknown>)
: {};
}
/**
@@ -97,7 +101,11 @@ export function createElicitationResponseToElicitResult(
): ElicitationResult {
switch (response.action) {
case "accept":
return { action: "accept", content: acceptedElicitationContent(response) };
// `ElicitResult` types the values as v1 does. The MCP server checks them.
return {
action: "accept",
content: acceptedElicitationContent(response) as ElicitationResult["content"],
};
case "decline":
return { action: "decline" };
case "cancel":
@@ -280,14 +288,37 @@ function joinMultiSelectAnswer(items: string[]): string {
* cannot make the selection disappear. Decline yields empty answers (the model
* is told the user skipped rather than the turn aborting); cancel — and any
* custom/future action we don't understand — aborts the tool call.
*
* With `reportUnanswered` set (the session's `system/init` listed
* `ask_user_question_unanswered_v1`), each question left without an answer
* also gets `annotations[question].unanswered`: `"dismissed"` for every
* question when the user declined the form, `"skipped"` for a question left
* blank in an accepted form. The model does not see this field; the tool
* result carries it for clients and transcripts. A CLI without the capability
* can refuse the whole answer over it, so it is left out there.
*/
export function applyAskElicitationResponse(
response: CreateElicitationResponse,
toolInput: Record<string, unknown>,
questions: AskUserQuestion[],
options: { reportUnanswered?: boolean } = {},
): AskUserQuestionOutcome {
// Typed against the output schema: as of SDK 0.3.316 only it lists
// `unanswered`, though the tool reads the same annotations from its input.
const annotations: NonNullable<AskUserQuestionOutput["annotations"]> = {};
const withAnnotations = (answers: AskUserQuestionOutput["answers"]) => ({
...toolInput,
answers,
...(Object.keys(annotations).length > 0 ? { annotations } : {}),
});
if (response.action === "decline") {
return { action: "answered", updatedInput: { ...toolInput, answers: {} } };
if (options.reportUnanswered) {
for (const question of questions) {
annotations[question.question] = { unanswered: "dismissed" };
}
}
return { action: "answered", updatedInput: withAnnotations({}) };
}
if (response.action !== "accept") {
@@ -298,18 +329,17 @@ export function applyAskElicitationResponse(
// Typed against the tool's own output schema so the answer/response shapes
// stay in sync with what the built-in tool's call() expects to read back.
const answers: AskUserQuestionOutput["answers"] = {};
const annotations: NonNullable<AskUserQuestionInput["annotations"]> = {};
questions.forEach((question, index) => {
const custom = content[questionCustomFieldKey(index)];
const customText = typeof custom === "string" ? custom.trim() : "";
// A pick is a string, number, or boolean. Other values, such as an object
// that a v2 client may send, are dropped instead of read as "[object Object]".
const value = content[questionFieldKey(index)];
const picks: string[] =
value === undefined || value === null
? []
: Array.isArray(value)
? value.filter((item) => item !== undefined && item !== null && item !== "").map(String)
: [String(value)];
const picks: string[] = (Array.isArray(value) ? value : [value])
.filter(isElicitationScalar)
.map(String)
.filter((item) => item !== "");
// A multi-select is additive, and the form offers the selection and the
// custom box as independent fields — a user who fills both means both, so
@@ -341,20 +371,27 @@ export function applyAskElicitationResponse(
}
});
return {
action: "answered",
updatedInput: {
...toolInput,
answers,
...(Object.keys(annotations).length > 0 ? { annotations } : {}),
},
};
if (options.reportUnanswered) {
for (const question of questions) {
if (answers[question.question] === undefined) {
annotations[question.question] = { unanswered: "skipped" };
}
}
}
return { action: "answered", updatedInput: withAnnotations(answers) };
}
function isElicitationScalar(value: unknown): value is string | number | boolean {
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
}
/**
* Coerce an arbitrary MCP `requestedSchema` into an ACP `ElicitationSchema`.
* The two are structurally compatible JSON Schemas; we just guarantee the
* `type: "object"` discriminator is present.
* `type: "object"` discriminator and a `properties` map are present. The v2
* schema requires both, so a client on SDK 2.2.0 or later rejects an MCP
* schema such as `{ type: "object" }` that has no `properties`.
*/
function normalizeElicitationSchema(
schema: Record<string, unknown> | undefined,
@@ -362,7 +399,8 @@ function normalizeElicitationSchema(
if (!schema || typeof schema !== "object") {
return { type: "object", properties: {} };
}
return { ...(schema as ElicitationSchema), type: "object" };
const { properties } = schema as Partial<ElicitationSchema>;
return { ...(schema as ElicitationSchema), type: "object", properties: properties ?? {} };
}
/**

View File

@@ -126,10 +126,9 @@ export function isExitPlanInterruptionResult(
);
}
/** The id of the plan that an approved ExitPlanMode publishes. Claude Code
* keeps one plan file per session, which a later plan mode rewrites, so each
* approval replaces the plan before it. The TodoWrite checklist is a
* separate plan. */
/** The base ID of an approved plan. AIR appends the review ID to keep each
* approval in the transcript. Other clients replace one plan per session.
* The TodoWrite checklist is a separate plan. */
export const PLAN_MODE_PLAN_ID = "plan-mode";
/** The plan, trimmed, that a successful ExitPlanMode result approved: the plan
@@ -150,11 +149,19 @@ export function approvedPlan(input: unknown, output: unknown): string | undefine
}
/** The update that shows an approved plan to a client that supports plan
* operations (the `plan` client capability, which every v2 client has). */
export function planModeUpdate(plan: string): SessionNotification["update"] {
* operations (the `plan` client capability, which every v2 client has).
* For AIR, pass the review ID to join the plan to its approval card. */
export function planModeUpdate(
plan: string,
reviewToolCallId?: string,
): SessionNotification["update"] {
return {
sessionUpdate: "plan_update",
plan: { type: "markdown", planId: PLAN_MODE_PLAN_ID, content: plan },
plan: {
type: "markdown",
planId: reviewToolCallId ? `${PLAN_MODE_PLAN_ID}:${reviewToolCallId}` : PLAN_MODE_PLAN_ID,
content: plan,
},
};
}
@@ -178,6 +185,7 @@ export type ExitPlanRestartHost<
sessionUpdate(notification: SessionNotification): Promise<void>;
/** Whether the client takes plan operations (see planModeUpdate). */
supportsPlanOperations(): boolean;
isAirClient(): boolean;
destroyReplacement(sessionId: string, session: Session): void;
settleCancelledTurn(oldSession: Session, turnSession: Session, turn: Turn): void;
settleFailedTurn(turnSession: Session, turn: Turn, error: unknown): void;
@@ -205,7 +213,7 @@ export class ExitPlanCoordinator<
try {
const clearContextHost: ClearContextCoordinatorHost<Session, Turn> = {
...this.host,
publishSessionState: async (id, mode, configOptions) => {
publishSessionState: async (id, mode, configOptions, contextWindowSize) => {
await this.host.sessionUpdate({
sessionId: id,
update: { sessionUpdate: "current_mode_update", currentModeId: mode },
@@ -214,11 +222,23 @@ export class ExitPlanCoordinator<
sessionId: id,
update: { sessionUpdate: "config_option_update", configOptions },
});
// Otherwise the client shows the old context's usage until the fresh
// context first reports its own.
await this.host.sessionUpdate({
sessionId: id,
update: { sessionUpdate: "usage_update", used: 0, size: contextWindowSize },
});
// The approved plan becomes the session's plan once the fresh
// context that carries it out exists. Its ExitPlanMode ended with
// an error result, so toAcpNotifications did not publish it.
if (this.host.supportsPlanOperations()) {
await this.host.sessionUpdate({ sessionId: id, update: planModeUpdate(reset.plan) });
await this.host.sessionUpdate({
sessionId: id,
update: planModeUpdate(
reset.plan,
this.host.isAirClient() ? reset.toolUseId : undefined,
),
});
}
},
continuationMessage: (id, plan, promptUuid) => ({

View File

@@ -69,10 +69,7 @@ function forkPointMessageIdCandidates(messageId: string): string[] {
return protocolMessageId === messageId ? [messageId] : [messageId, protocolMessageId];
}
async function loadFullSessionHistory(
sessionId: string,
cwd: string,
): Promise<SessionStoreEntry[]> {
async function loadFullSessionHistory(sessionId: string): Promise<SessionStoreEntry[]> {
// getSessionMessages returns only the active parentUuid chain. The import keeps inactive branches that AIR can still reference.
const entries: SessionStoreEntry[] = [];
const store: SessionStore = {
@@ -81,7 +78,7 @@ async function loadFullSessionHistory(
},
load: async () => null,
};
await importSessionToStore(sessionId, store, { dir: cwd, includeSubagents: false });
await importSessionToStore(sessionId, store, { includeSubagents: false });
return entries;
}
@@ -179,14 +176,44 @@ function resolveFromFullHistory(
return occurrenceMatches.length === 1 ? occurrenceMatches[0]?.uuid : undefined;
}
/**
* Whether `error` is the Claude SDK's report that `sessionId` has no
* transcript: `forkSession` and `importSessionToStore` throw
* `Error("Session <id> not found")`, and both throw
* `"Invalid sessionId: <id>"` for an id that is not a UUID.
*/
function isUnknownSessionError(error: unknown, sessionId: string): boolean {
if (!(error instanceof Error)) return false;
return (
error.message === `Session ${sessionId} not found` ||
error.message === `Invalid sessionId: ${sessionId}`
);
}
export async function forkSession(
params: ForkSessionRequest,
dependencies: ForkSessionDependencies,
): Promise<ForkSessionResponse> {
try {
return await forkTranscript(params, dependencies);
} catch (error) {
if (isUnknownSessionError(error, params.sessionId)) {
throw RequestError.sessionNotFound(params.sessionId);
}
throw error;
}
}
async function forkTranscript(
params: ForkSessionRequest,
dependencies: ForkSessionDependencies,
): Promise<ForkSessionResponse> {
const timing = new SessionTiming(dependencies.logger, "fork", params.sessionId);
const point = forkPoint(params._meta);
// SDK `dir` scopes source lookup, not the fork's working directory. Search
// all projects: params.cwd can name a different workspace for the new session.
if (!point) {
const forked = await forkClaudeSession(params.sessionId, { dir: params.cwd });
const forked = await forkClaudeSession(params.sessionId);
timing.phase("sdk-fork", " resolution=latest");
return { sessionId: forked.sessionId };
}
@@ -195,9 +222,7 @@ export async function forkSession(
const liveUuid = candidateIds
.map((candidateId) => dependencies.liveMessageIdToUuid?.get(candidateId))
.find(Boolean);
const history = liveUuid
? undefined
: await getSessionMessages(params.sessionId, { dir: params.cwd });
const history = liveUuid ? undefined : await getSessionMessages(params.sessionId);
timing.phase("active-history", ` resolution=${liveUuid ? "live" : "pending"}`);
const messageUuid =
liveUuid ??
@@ -213,7 +238,7 @@ export async function forkSession(
const fullHistoryUuid = messageUuid
? undefined
: resolveFromFullHistory(
await loadFullSessionHistory(params.sessionId, params.cwd),
await loadFullSessionHistory(params.sessionId),
candidateIds,
point,
dependencies.messageIdForGrouping,
@@ -231,7 +256,6 @@ export async function forkSession(
}
const forked = await forkClaudeSession(params.sessionId, {
dir: params.cwd,
upToMessageId: messageUuid ?? fullHistoryUuid,
});
timing.phase("sdk-fork");

View File

@@ -1,6 +1,13 @@
import { RequestError, type SessionNotification } from "@agentclientprotocol/sdk/unstable";
import {
RequestError,
type SessionConfigOption,
type SessionNotification,
} from "@agentclientprotocol/sdk/unstable";
import { realpath } from "node:fs/promises";
import path from "node:path";
import type { AcpSessionNotification, SubagentForm, SubagentState } from "./acp-subagents.js";
import { AIR_SUBAGENT_KEY, airExtensionMeta } from "./air-extension.js";
import { MODEL_CONFIG_ID } from "./session-model.js";
import { isFinalToolCallStatus } from "./tool-calls/renderer.js";
/** The work state of a child, as the RFD's `subagent_update.state` carries it. */
@@ -20,11 +27,14 @@ export type NativeSubagent = {
* form. It is absent when the adapter has no prompt.
*/
prompt?: string;
/** The RFD form: the parent's label for the child, `subagent_update.title`. */
/**
* The RFD form: the parent's label for the child, `subagent_update.title`:
* its name, else its task.
*/
title?: string;
/** The RFD form: the child's role, its `subagent_type`, for the title. */
/** The RFD form: the child's role, its `subagent_type`. */
role?: string;
/** The RFD form: the child's purpose, `subagent_update.description`. */
/** The RFD form: `subagent_update.description`, the child's role. */
description?: string;
/**
* The RFD form: the work state the client holds. The child is reusable, so
@@ -48,14 +58,23 @@ export type NativeSubagent = {
/** The RFD form: the calls that delegated to the child, whose runs handed back a report. */
handedBack?: Set<string>;
announced?: boolean;
/**
* The RFD form: the `cwd` that the client holds for the child. Undefined or
* null while the child inherits its parent's working directory.
*/
reportedCwd?: string | null;
terminalState?: SubagentState;
/** Connection-local single-flight state; never serialized on the wire. */
announcePromise?: Promise<void>;
/** Connection-local: serializes the child's `cwd` reports; never serialized on the wire. */
cwdPublishing?: Promise<void>;
/** Connection-local single-flight state; never serialized on the wire. */
terminalPromise?: Promise<void>;
};
export type NativeSubagentSession = {
/** The working directory of the root session, which a child inherits unless it reports its own. */
cwd?: string;
nativeSubagentsByTaskId?: Map<string, NativeSubagent>;
nativeSubagentTaskIdByToolUseId?: Map<string, string>;
nativeSubagentParentByToolUseId?: Map<string, string>;
@@ -64,6 +83,32 @@ export type NativeSubagentSession = {
type Publish = (notification: AcpSessionNotification) => Promise<void>;
type Logger = { log(message: string): void };
/** An entry of a session's `model` select option. */
export type ModelEntry = { value: string; name: string };
/**
* The models of the root session. A child's model is an entry of the root
* session's `model` option when one matches, so that a client can compare the
* two sessions' models.
*/
export type SubagentModels = {
/**
* The entry of the root session's `model` option for `model`, a model id
* that a response reported. Undefined when no entry matches.
*/
entry(model: string): ModelEntry | undefined;
/** The display name of the model id `model` in the session's model list, if the list has it. */
name(model: string): string | undefined;
};
type ChildConfiguration = {
options: Map<string, SessionConfigOption>;
published?: string;
/** The `model` option value and name that the client last got. */
publishedModel?: ModelEntry;
publishing: Promise<void>;
};
/** What the SDK says about the end of a task, beyond its status. */
export type TaskEnd = {
/** The SDK's text for a failure. */
@@ -99,6 +144,8 @@ export type HistoricalSubagent = {
launchId: string;
/** The agent that made that call. None means the root session. */
parentAgentId?: string;
/** Its working directory, when Claude Code placed it elsewhere, e.g. in a worktree. */
cwd?: string;
name?: string;
agentType?: string;
description?: string;
@@ -137,6 +184,12 @@ export class NativeSubagentRuntime {
private readonly parentByToolUse: Map<string, string>;
private readonly identityByToolUse = new Map<string, SubagentIdentity>();
private readonly controlByToolUse = new Map<string, AcpSessionNotification>();
private readonly configBySession = new Map<string, ChildConfiguration>();
/**
* The RFD form: the working directory of each child, by session id, as its
* hooks last reported it. A hook can come before the child is announced.
*/
private readonly cwdBySession = new Map<string, string>();
private readonly childByParentToolUse = new Map<string, NativeSubagent>();
/**
* The child session of each tool call that went to a child session. A later
@@ -153,6 +206,15 @@ export class NativeSubagentRuntime {
* closes an unfinished call has no `toolName` to recognize it by.
*/
private readonly hiddenLaunches = new Map<string, { background: boolean }>();
/**
* The RFD form: the SendMessage calls that the client is not shown, until
* their final update, with their first report and the session that made
* them. A message that reaches an announced child replaces its call.
*/
private readonly hiddenSendMessages = new Map<
string,
{ initial: AcpSessionNotification; sender: string; delivered: boolean }
>();
/**
* The RFD form: the SubagentHandback calls of announced children that the
* client is not shown yet, with their first report, the report from their
@@ -184,6 +246,8 @@ export class NativeSubagentRuntime {
* `form` is undefined for a client without native subagent sessions. The RFD
* form restores an agent from before a load or resume with `lineage`. The AIR
* form does too, and `launchedAgent` finds the agent that a launch call made.
* `models` maps the model of a child response to the root session's entry
* (see {@link reportModel}).
*/
constructor(
form: SubagentForm | undefined,
@@ -193,6 +257,7 @@ export class NativeSubagentRuntime {
private readonly logger: Logger,
private readonly lineage?: SubagentLineage,
private readonly launchedAgent?: LaunchedAgent,
private readonly models?: SubagentModels,
) {
this.form = form;
this.enabled = form !== undefined;
@@ -212,8 +277,246 @@ export class NativeSubagentRuntime {
forcedSessionId?: string,
): Promise<AcpSessionNotification | null> {
const routed = await this.routeUpdate(notification, deliver, forcedSessionId);
if (routed) this.rememberSendMessage(routed);
return routed;
if (!routed) return routed;
this.rememberSendMessage(routed);
return this.form === "rfd" ? this.routeRfdSendMessage(routed, forcedSessionId) : routed;
}
/**
* The RFD form: the work state that the client holds for the announced child
* `sessionId` of this live session, or undefined when no such child runs here.
*/
liveWorkState(sessionId: string): WorkState["state"] | undefined {
if (this.form !== "rfd") return undefined;
const child = this.children.get(sessionId);
return child?.announced ? child.workState : undefined;
}
/**
* The RFD form: the `subagent_update` that tells a client that holds nothing
* of the announced child `sessionId` of this live session what it is now:
* its labels, its directory, its `cancel` capability, and its work state.
* Undefined when no such child runs here.
*/
currentUpdate(sessionId: string): AcpSessionNotification | undefined {
if (this.form !== "rfd") return undefined;
const child = this.childOfSession(sessionId);
if (!child?.announced || !child.workState) return undefined;
return {
sessionId: child.parentSessionId,
update: {
sessionUpdate: "subagent_update",
sessionId: child.sessionId,
...(child.title ? { title: child.title } : {}),
...(child.description ? { description: child.description } : {}),
...(child.reportedCwd ? { cwd: child.reportedCwd } : {}),
capabilities: { cancel: {} },
state: { state: child.workState },
},
};
}
/**
* Reports the model of a child response, an exact model id, as the entry of
* the root session's `model` option that matches it, else as the id (see
* {@link responseModelEntry}). The child gets no model before its first
* response: the adapter cannot tell the model that Claude Code picks for it.
* The client gets a new configuration only when the entry changes.
*/
async reportModel(
parentToolUseId: string | null,
model: string | undefined,
deliver: Publish,
): Promise<void> {
const entry = this.form && parentToolUseId ? responseModelEntry(model, this.models) : undefined;
if (!entry) return;
// Every response of a child reports its model: most repeat the model that
// the client already has, and need no routing.
// A publication that failed sent nothing, so only the published model counts.
const child = this.childByParentToolUse.get(parentToolUseId!);
if (
child?.announced &&
sameModelEntry(this.configBySession.get(child.sessionId)?.publishedModel, entry)
) {
return;
}
await deliver({
sessionId: this.rootSessionId,
update: {
sessionUpdate: "config_option_update",
configOptions: [childModelOption(entry)],
_meta: { claudeCode: { parentToolUseId } },
},
});
}
/** The configuration that the client last got for the announced child `sessionId`, if any. */
configurationOf(sessionId: string): SessionConfigOption[] | undefined {
const published = this.configBySession.get(sessionId)?.published;
if (!this.childOfSession(sessionId)?.announced || !published) return undefined;
return JSON.parse(published) as SessionConfigOption[];
}
/**
* Takes configuration options of the child `agentId`: an SDK agent id, as a
* hook names it, or a child session id. Hook observations can precede the
* task event that establishes a child's parent.
*/
async reportConfiguration(
agentId: string,
options: SessionConfigOption[],
meta?: SessionNotification["update"]["_meta"],
): Promise<void> {
if (!this.form || agentId === this.rootSessionId || options.length === 0) return;
// In AIR's draft, the session id of a resumed or restored agent is
// `<agent id>:generation:<n>`, so the agent id names its current generation.
const known = this.children.get(agentId) ?? this.childOfSession(agentId);
const sessionId = known?.sessionId ?? agentId;
// AIR's draft takes no configuration of an ended child (see `finishTask`).
if (
this.form === "air" &&
known &&
(known.terminalState !== undefined || known.terminalPromise)
) {
return;
}
let config = this.configBySession.get(sessionId);
if (!config) {
const pending = [...this.configBySession.keys()].filter(
(id) => !this.childOfSession(id),
).length;
if (!known && pending >= MAX_PENDING_PARENTS) {
this.logger.log(
`Session ${this.rootSessionId}: dropping configuration for unannounced subagent ${agentId}; pending buffer limit reached`,
);
return;
}
config = { options: new Map(), publishing: Promise.resolve() };
this.configBySession.set(sessionId, config);
}
for (const option of options) config.options.set(option.id, option);
if (known?.announced) await this.publishConfiguration(known, meta);
}
private async publishConfiguration(
child: NativeSubagent,
meta?: SessionNotification["update"]["_meta"],
): Promise<void> {
const config = this.configBySession.get(child.sessionId);
// AIR's draft ends a child for good, so it gets no update after its end,
// also when the end comes while this publication waits for an earlier one.
const ended = () =>
this.form === "air" && (child.terminalState !== undefined || !!child.terminalPromise);
if (!config || ended()) return;
// SDK hooks can run concurrently with streamed responses. Serialize snapshots
// so a delayed publication cannot replace newer settings with older ones.
const publishing = config.publishing.then(async () => {
if (this.configBySession.get(child.sessionId) !== config || ended()) return;
const priority = ["mode", "model", "thought_level", "model_config"];
const rank = (option: SessionConfigOption) => {
const index = priority.indexOf(option.category ?? "");
return index < 0 ? priority.length : index;
};
const configOptions = [...config.options.values()].sort(
(left, right) => rank(left) - rank(right),
);
const snapshot = JSON.stringify(configOptions);
if (config.published === snapshot) return;
await this.publish({
sessionId: child.sessionId,
update: {
sessionUpdate: "config_option_update",
configOptions,
...(meta ? { _meta: meta } : {}),
},
});
config.published = snapshot;
const model = configOptions.find((option) => option.id === MODEL_CONFIG_ID);
const shown = model?.type === "select" ? model.options[0] : undefined;
if (model && shown && "name" in shown && typeof model.currentValue === "string") {
config.publishedModel = { value: model.currentValue, name: shown.name };
}
});
// The caller receives the error; later observations must still be able to retry.
config.publishing = publishing.catch(() => {});
await publishing;
}
/**
* The RFD form: takes the working directory of the child `agentId`, the
* `cwd` of one of its hooks. A child that Claude Code runs in its own
* worktree (Agent `isolation: "worktree"`) reports the worktree. The client
* gets the `cwd` only when it differs from the parent's, in the
* announcement or in a `subagent_update` right after it. Claude Code runs a
* child's SubagentStart hook before its first model request, so the `cwd`
* reaches the client before the child's first tool call.
*/
async reportCwd(agentId: string, cwd: string): Promise<void> {
if (this.form !== "rfd" || agentId === this.rootSessionId || !path.isAbsolute(cwd)) return;
const known = this.children.get(agentId) ?? this.childOfSession(agentId);
const sessionId = known?.sessionId ?? agentId;
if (!known && !this.cwdBySession.has(sessionId)) {
const pending = [...this.cwdBySession.keys()].filter((id) => !this.childOfSession(id)).length;
if (pending >= MAX_PENDING_PARENTS) {
this.logger.log(
`Session ${this.rootSessionId}: dropping cwd for unannounced subagent ${agentId}; pending buffer limit reached`,
);
return;
}
}
this.cwdBySession.set(sessionId, cwd);
if (!known) return;
// An announcement in flight may have been built without this `cwd`.
if (known.announcePromise) await known.announcePromise.catch(() => {});
if (known.announced) await this.publishCwd(known);
}
/**
* The `cwd` of `child` that the client does not hold yet: its working
* directory when that differs from its parent's, `null` when it is back in
* the parent's after it reported another, else undefined.
*/
private async unreportedCwd(child: NativeSubagent): Promise<string | null | undefined> {
const cwd = this.cwdBySession.get(child.sessionId);
if (cwd === undefined) return undefined;
const inherited = this.effectiveCwd(child.parentSessionId);
if (inherited !== undefined && (await sameDirectory(cwd, inherited))) {
return child.reportedCwd ? null : undefined;
}
return cwd === child.reportedCwd ? undefined : cwd;
}
/**
* The working directory of the session `sessionId` as the client holds it:
* the root's from session setup, a child's reported `cwd`, else its
* parent's.
*/
private effectiveCwd(sessionId: string): string | undefined {
const seen = new Set<string>();
let child = this.childOfSession(sessionId);
while (child && !seen.has(child.sessionId)) {
if (child.reportedCwd) return child.reportedCwd;
seen.add(child.sessionId);
child = this.childOfSession(child.parentSessionId);
}
return this.session.cwd;
}
/** The RFD form: sends the `cwd` of the announced `child` that the client does not hold. */
private async publishCwd(child: NativeSubagent): Promise<void> {
if (this.form !== "rfd") return;
// Concurrent hooks of one child report the same `cwd` once.
const publishing = (child.cwdPublishing ?? Promise.resolve()).then(async () => {
const cwd = await this.unreportedCwd(child);
if (cwd === undefined) return;
await this.publish({
sessionId: child.parentSessionId,
update: { sessionUpdate: "subagent_update", sessionId: child.sessionId, cwd },
});
child.reportedCwd = cwd;
});
child.cwdPublishing = publishing.catch(() => {});
await publishing;
}
private async routeUpdate(
@@ -315,6 +618,10 @@ export class NativeSubagentRuntime {
(claudeMeta?.parentToolUseId
? this.childByParentToolUse.get(claudeMeta.parentToolUseId)
: undefined);
if (announcedChild?.announced && update.sessionUpdate === "config_option_update") {
await this.reportConfiguration(announcedChild.sessionId, update.configOptions, update._meta);
return null;
}
if (this.form === "rfd" && announcedChild?.announced) {
const handback = await this.routeRfdHandback(notification, announcedChild);
if (handback !== undefined) return handback;
@@ -557,6 +864,8 @@ export class NativeSubagentRuntime {
if (child.parentToolUseId) (child.endedRuns ??= new Set()).add(child.parentToolUseId);
} else {
await finishNativeSubagent(this.session, taskId, state, this.publish);
// AIR's draft takes no configuration of an ended child.
this.configBySession.delete(child.sessionId);
}
} finally {
if (child.parentToolUseId) {
@@ -595,6 +904,7 @@ export class NativeSubagentRuntime {
this.pendingCount = 0;
this.identityByToolUse.clear();
this.controlByToolUse.clear();
this.configBySession.clear();
this.parentByToolUse.clear();
this.forgetRestores();
}
@@ -732,9 +1042,14 @@ export class NativeSubagentRuntime {
this.childByParentToolUse.set(entry.launchId, child);
}
if (child.announced) continue;
// A hook of the resumed agent may have reported a newer `cwd`.
if (entry.cwd && !this.cwdBySession.has(child.sessionId)) {
this.cwdBySession.set(child.sessionId, entry.cwd);
}
if (entry.agentId === agentId) {
await this.announce(child);
} else {
const cwd = await this.unreportedCwd(child);
await this.publish({
sessionId: child.parentSessionId,
update: {
@@ -742,8 +1057,10 @@ export class NativeSubagentRuntime {
sessionId: child.sessionId,
...(child.title ? { title: child.title } : {}),
...(child.description ? { description: child.description } : {}),
...(cwd ? { cwd } : {}),
},
});
if (cwd) child.reportedCwd = cwd;
child.announced = true;
}
for (const pending of this.takePending(entry.launchId)) await deliver(pending);
@@ -888,13 +1205,16 @@ export class NativeSubagentRuntime {
});
}
if (prompt && toolUseId) {
const sendMessage = this.hiddenSendMessages.get(toolUseId);
await publishPrompt(
child,
prompt,
toolUseId,
this.senderByToolUse.get(toolUseId),
sendMessage?.sender ?? this.senderByToolUse.get(toolUseId),
this.publish,
{ replacesCall: sendMessage !== undefined },
);
if (sendMessage) sendMessage.delivered = true;
}
for (const pending of toolUseId ? this.takePending(toolUseId) : []) await deliver(pending);
}
@@ -908,6 +1228,8 @@ export class NativeSubagentRuntime {
*/
private withdraw(taskId: string, child: NativeSubagent): void {
this.children.delete(taskId);
this.configBySession.delete(child.sessionId);
this.cwdBySession.delete(child.sessionId);
if (child.parentToolUseId) {
this.takePending(child.parentToolUseId);
this.cleanupControl(child.parentToolUseId);
@@ -929,9 +1251,18 @@ export class NativeSubagentRuntime {
await this.reportState(child, rfdEndState("failed", { error }));
}
/** Announces `child` in the client's form. A runtime without one never does. */
/**
* Announces `child` in the client's form, with its `cwd` in the RFD form,
* then its configuration. A runtime without a form never does.
*/
private async announce(child: NativeSubagent): Promise<void> {
if (this.form) await announceNativeSubagent(child, this.publish, this.form);
if (!this.form) return;
const cwd =
this.form === "rfd" && !child.announced ? await this.unreportedCwd(child) : undefined;
await announceNativeSubagent(child, this.publish, this.form, cwd ?? undefined);
// A hook that reported a `cwd` while the announcement was being built.
await this.publishCwd(child);
await this.publishConfiguration(child);
}
/** The RFD form: publishes the work state of `child` on its parent. */
@@ -1100,6 +1431,44 @@ export class NativeSubagentRuntime {
return unexposedControlFallback(handback.initial, notification, child.sessionId);
}
/**
* The RFD form: an update of a SendMessage call, which its message replaces
* once it reaches an announced child: the sender's outgoing view and the
* child's incoming one, see {@link linkedSessionMessages}. The SDK reports
* that delivery before the call's final update. A call whose message reached
* no announced child, such as one to the parent, is shown after all, with its
* final status. Any other update is returned as is.
*/
private routeRfdSendMessage(
notification: AcpSessionNotification,
forcedSessionId: string | undefined,
): AcpSessionNotification | null {
const { update } = notification;
if (update.sessionUpdate !== "tool_call" && update.sessionUpdate !== "tool_call_update") {
return notification;
}
const toolCallId = update.toolCallId;
// A permission request already showed the call to the client, so it keeps
// getting its updates, and its message goes only to the child.
if (forcedSessionId) {
this.hiddenSendMessages.delete(toolCallId);
return notification;
}
let sendMessage = this.hiddenSendMessages.get(toolCallId);
if (!sendMessage) {
const claudeMeta = update._meta?.claudeCode as { toolName?: string } | undefined;
if (update.sessionUpdate !== "tool_call" || claudeMeta?.toolName !== "SendMessage") {
return notification;
}
sendMessage = { initial: notification, sender: notification.sessionId, delivered: false };
this.hiddenSendMessages.set(toolCallId, sendMessage);
}
if (!isFinalToolCallStatus(update.status)) return null;
this.hiddenSendMessages.delete(toolCallId);
if (sendMessage.delivered) return null;
return unexposedControlFallback(sendMessage.initial, notification, sendMessage.sender);
}
/** Whether `update` is of an Agent/Task call, by its tool name or, in the
* RFD form, by the id of a call the client is not shown. */
private isControlUpdate(
@@ -1157,10 +1526,13 @@ export class NativeSubagentRuntime {
this.parentByToolUse.clear();
this.identityByToolUse.clear();
this.controlByToolUse.clear();
this.configBySession.clear();
this.cwdBySession.clear();
this.childByParentToolUse.clear();
this.taskFinishPromises.clear();
this.senderByToolUse.clear();
this.hiddenLaunches.clear();
this.hiddenSendMessages.clear();
this.handbacks.clear();
this.generationByTaskId.clear();
this.pending.clear();
@@ -1188,6 +1560,20 @@ export class NativeSubagentRuntime {
private buffer(parentToolUseId: string, notification: AcpSessionNotification): void {
const updates = this.pending.get(parentToolUseId);
// A child reports its model with each response before its announcement
// (see `reportModel`). Only the latest such report waits, so they do not
// fill the buffer. Any other configuration update waits as it is.
const modelOnly = ({ update }: AcpSessionNotification) =>
update.sessionUpdate === "config_option_update" &&
update.configOptions.length === 1 &&
update.configOptions[0]?.id === MODEL_CONFIG_ID;
if (updates && modelOnly(notification)) {
const index = updates.findIndex(modelOnly);
if (index >= 0) {
updates[index] = notification;
return;
}
}
if (
this.pendingCount >= MAX_PENDING_UPDATES ||
(updates === undefined && this.pending.size >= MAX_PENDING_PARENTS) ||
@@ -1301,6 +1687,40 @@ export class NativeSubagentRuntime {
}
}
/**
* The `model` option of a child session. The client cannot change a child's
* configuration, so the select offers only the current model.
*/
export function childModelOption(entry: ModelEntry): SessionConfigOption {
return {
id: MODEL_CONFIG_ID,
name: "Model",
category: "model",
type: "select",
currentValue: entry.value,
options: [{ value: entry.value, name: entry.name }],
};
}
/** Whether two model entries have the same value and name. */
export function sameModelEntry(left: ModelEntry | undefined, right: ModelEntry): boolean {
return left?.value === right.value && left.name === right.name;
}
/**
* The model entry for `model`, the model id of a child's response: the entry
* of the root session's `model` option that matches it, else the id, with its
* display name when the session's model list has one. Undefined for no model,
* and for `<synthetic>`, which marks a message that no model wrote.
*/
export function responseModelEntry(
model: unknown,
models: SubagentModels | undefined,
): ModelEntry | undefined {
if (typeof model !== "string" || !model || model === "<synthetic>") return undefined;
return models?.entry(model) ?? { value: model, name: models?.name(model) ?? model };
}
/**
* Announces the child on its parent, once, before any update of the child.
* The RFD form announces it as running and then sends the prompt as the
@@ -1313,6 +1733,7 @@ export async function announceNativeSubagent(
child: NativeSubagent,
publish: Publish,
form: SubagentForm,
cwd?: string,
): Promise<void> {
if (child.announced) return;
if (child.announcePromise) return child.announcePromise;
@@ -1326,10 +1747,12 @@ export async function announceNativeSubagent(
sessionId: child.sessionId,
...(child.title ? { title: child.title } : {}),
...(child.description ? { description: child.description } : {}),
...(cwd ? { cwd } : {}),
capabilities: { cancel: {} },
state: { state: "running" },
},
});
if (cwd) child.reportedCwd = cwd;
child.announced = true;
// The immediate parent made the Agent/Task call that launched the child.
if (child.prompt) {
@@ -1339,7 +1762,7 @@ export async function announceNativeSubagent(
child.parentToolUseId,
child.parentSessionId,
publish,
{ replacesLaunch: true },
{ replacesCall: true },
);
}
return;
@@ -1401,9 +1824,9 @@ export async function finishNativeSubagent(
* transcript is the same delegation and is not sent again. `sender` is the
* session that made that call, when known.
*
* A launch's prompt (`replacesLaunch`) also goes to the parent, see
* {@link linkedSessionMessages}. A SendMessage call stays a tool call of the
* sender, so its message has only the incoming view.
* When the message replaces its call (`replacesCall`), a launch's or a
* SendMessage call the client is not shown, it also goes to the sender, see
* {@link linkedSessionMessages}. Otherwise it has only the incoming view.
*/
async function publishPrompt(
child: NativeSubagent,
@@ -1411,13 +1834,13 @@ async function publishPrompt(
toolUseId: string | undefined,
sender: string | undefined,
publish: Publish,
{ replacesLaunch = false }: { replacesLaunch?: boolean } = {},
{ replacesCall = false }: { replacesCall?: boolean } = {},
): Promise<void> {
child.promptIds ??= new Set();
const messageId = toolUseId ?? `${child.sessionId}:prompt:${child.promptIds.size + 1}`;
if (child.promptIds.has(messageId)) return;
child.promptIds.add(messageId);
if (replacesLaunch && sender !== undefined && toolUseId !== undefined) {
if (replacesCall && sender !== undefined && toolUseId !== undefined) {
for (const view of linkedSessionMessages(toolUseId, sender, child.sessionId, prompt)) {
await publish(view);
}
@@ -1439,8 +1862,9 @@ async function publishPrompt(
* The RFD form: a message that a tool call sent, which the client is not
* shown, as two session messages: the sender's outgoing view, then the
* recipient's incoming one. That call is a launch's Agent/Task call, whose
* prompt goes from the parent to the child, or a child's SubagentHandback
* call, whose report goes from the child to the parent. Both views use the
* prompt goes from the parent to the child, a SendMessage call, whose message
* goes from its sender to a child, or a child's SubagentHandback call, whose
* report goes from the child to the parent. Both views use the
* call's tool use id, and each names the other with the draft RFD's
* `recipientMessageId` and `senderMessageId`.
*/
@@ -1460,11 +1884,11 @@ export function linkedSessionMessages(
return [
{
sessionId: senderSessionId,
update: { ...message, recipientMessageId: toolUseId } as AcpSessionNotification["update"],
update: { ...message, recipientMessageId: toolUseId },
},
{
sessionId: recipientSessionId,
update: { ...message, senderMessageId: toolUseId } as AcpSessionNotification["update"],
update: { ...message, senderMessageId: toolUseId },
},
];
}
@@ -1582,26 +2006,22 @@ export function rfdEndState(state: SubagentState, end: TaskEnd | undefined): Wor
}
/**
* The RFD form: the parent's labels for a child. The title is the agent's
* name, with its role after it when both are known, else the role. The
* description is the task's short description, never the prompt.
* The RFD form: the parent's labels for a child. The title is what tells
* siblings apart: the agent's name when it has one, else the task's short
* description (never the prompt), else its role. The description is the
* role, its `subagent_type`, unless the role is already the title.
*/
export function rfdLabels(
name: unknown,
role: unknown,
description: unknown,
): { title?: string; role?: string; description?: string } {
const agentName = nonBlankString(name);
const agentRole = nonBlankString(role);
const title =
agentName && agentRole && agentName !== agentRole
? `${agentName} (${agentRole})`
: (agentName ?? agentRole);
const text = nonBlankString(description);
const title = nonBlankString(name) ?? nonBlankString(description) ?? agentRole;
return {
...(title ? { title } : {}),
...(agentRole ? { role: agentRole } : {}),
...(text ? { description: text } : {}),
...(agentRole && agentRole !== title ? { description: agentRole } : {}),
};
}
@@ -1710,7 +2130,8 @@ function isFailedToolCallUpdate(update: AcpSessionNotification["update"]): boole
* A tool call that the client was not shown, as one `tool_call` with its
* final status, shown after all: an Agent/Task call whose child was never
* exposed (AIR's draft shows only a failed one, the RFD form also a completed
* one), or a failed SubagentHandback call.
* one), a SendMessage call whose message reached no announced child, or a
* failed SubagentHandback call.
*/
function unexposedControlFallback(
initial: AcpSessionNotification | undefined,
@@ -1869,3 +2290,15 @@ function promptField(prompt: string | undefined): { prompt?: string } {
function nonBlankString(value: unknown): string | undefined {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
}
/**
* Whether `left` and `right` are the same directory, also when one is
* spelled through a symlink, e.g. `/tmp/project` and `/private/tmp/project`
* on macOS. A path that does not exist is compared as written.
*/
export async function sameDirectory(left: string, right: string): Promise<boolean> {
if (path.resolve(left) === path.resolve(right)) return true;
const real = (dir: string) => realpath(dir).catch(() => path.resolve(dir));
const [realLeft, realRight] = await Promise.all([real(left), real(right)]);
return realLeft === realRight;
}

View File

@@ -2,3 +2,4 @@
export const DEFAULT_MODEL_ID = "default";
export const DEFAULT_AGENT_ID = "default";
export const EFFORT_CONFIG_ID = "effort";
export const ULTRACODE_CONFIG_ID = "ultracode";

View File

@@ -14,14 +14,15 @@ export function toSdkEffortLevel(value: string | undefined): EffortLevel | null
// Since CLI 2.1.284 an `effortLevel` that changes the level, sent without an
// `ultracode` key, turns ultracode off. The CLI's own effort control keeps
// ultracode on at any level, so carry a settings-requested ultracode along
// with every effort apply.
// ultracode on at any level, so send `ultracode: true` with every effort apply
// while the session has ultracode on. When it is off, the key is omitted: an
// effort change then leaves ultracode off, as it already was.
export function effortFlagSettings(
value: string | undefined,
settings: Settings,
ultracode: boolean,
): { effortLevel: EffortLevel | null; ultracode?: true } {
const effortLevel = toSdkEffortLevel(value);
return settings.ultracode === true ? { effortLevel, ultracode: true } : { effortLevel };
return ultracode ? { effortLevel, ultracode: true } : { effortLevel };
}
function canonicalizeModelSettingsKey(value: string): string {
@@ -79,6 +80,13 @@ export function mergeEffortSettings(base: Settings, override: Settings | undefin
};
}
export function effortLevelName(level: string): string {
return level
.split(/[_-]/)
.map((part) => (part ? part.charAt(0).toUpperCase() + part.slice(1) : part))
.join(" ");
}
export function buildEffortConfigOption(
modelInfos: ModelInfo[],
currentModelId: string,
@@ -98,10 +106,7 @@ export function buildEffortConfigOption(
...(useRecommendedValue ? [] : [{ value: "default", name: "Default" }]),
...supportedLevels.map((level) => ({
value: level,
name: level
.split(/[_-]/)
.map((part) => (part ? part.charAt(0).toUpperCase() + part.slice(1) : part))
.join(" "),
name: effortLevelName(level),
})),
];
const includes = (level: string) =>

View File

@@ -20,6 +20,7 @@ import type { Query } from "@anthropic-ai/claude-agent-sdk";
import type { AcpClient, Logger, Session } from "../acp-agent.js";
import {
AIR_SESSION_ARCHIVE_CAPABILITY,
AIR_SESSION_BATCH_GET_CAPABILITY,
AIR_SESSION_INDEX_CAPABILITY,
AIR_SESSION_LIST_SUBSCRIBE_CAPABILITY,
AIR_SESSION_RENAME_CAPABILITY,
@@ -46,6 +47,8 @@ import {
renameTo,
parseRenameSessionRequest,
parseSessionIdRequest,
parseBatchGetSessionsRequest,
SESSION_BATCH_GET_METHOD,
SESSION_ARCHIVE_METHOD,
SESSION_RENAME_METHOD,
SESSION_UNARCHIVE_METHOD,
@@ -54,6 +57,8 @@ import {
type RetitleOptions,
type SessionIdRequest,
type TitleChange,
type BatchGetSessionsRequest,
type BatchGetSessionsResponse,
} from "./service.js";
/** What the session index keeps on each `Session`. */
@@ -144,12 +149,13 @@ export class SessionIndexConnection {
}
/** The AIR capabilities the agent advertises for the index: the index
* itself, archive, rename, the list subscription and the `agentWorktree`
* itself, batch get, archive, rename, the list subscription and the `agentWorktree`
* row field, all only to a client that declared `sessionIndex`. */
capabilities(): string[] {
return this.service
? [
AIR_SESSION_INDEX_CAPABILITY,
AIR_SESSION_BATCH_GET_CAPABILITY,
AIR_SESSION_ARCHIVE_CAPABILITY,
AIR_SESSION_RENAME_CAPABILITY,
AIR_SESSION_LIST_SUBSCRIBE_CAPABILITY,
@@ -230,6 +236,12 @@ export class SessionIndexConnection {
return this.service?.list(params, (sessionId) => this.ownSessionState(sessionId));
}
batchGet(params: BatchGetSessionsRequest): Promise<BatchGetSessionsResponse> {
return this.requireService(SESSION_BATCH_GET_METHOD).batchGet(params, (sessionId) =>
this.ownSessionState(sessionId),
);
}
/** An AIR client archives with session/delete (see {@link deleteSession}):
* its archived sessions (an archived custom title) stay hidden from the
* old list, as when the delete removed them. */
@@ -448,6 +460,7 @@ export class SessionIndexConnection {
/** The agent methods behind the session index extension methods. */
type SessionIndexMethods = {
batchGetSessions(params: BatchGetSessionsRequest): Promise<BatchGetSessionsResponse>;
subscribeSessionList(params: ListSubscribeRequest): Promise<ListSubscribeResponse>;
unsubscribeSessionList(params: ListUnsubscribeRequest): Promise<EmptyResponse>;
renameSessionTitle(params: RenameSessionRequest): Promise<EmptyResponse>;
@@ -455,10 +468,15 @@ type SessionIndexMethods = {
unarchiveSession(params: SessionIdRequest): Promise<EmptyResponse>;
};
/** Routes `_session/list/subscribe`, `_session/list/unsubscribe`,
/** Routes `_session/batch_get`, `_session/list/subscribe`, `_session/list/unsubscribe`,
* `_session/rename`, `_session/archive` and `_session/unarchive`. */
export function onSessionIndexRequests(app: AgentApp, agent: () => SessionIndexMethods): AgentApp {
return app
.onRequest<BatchGetSessionsRequest, BatchGetSessionsResponse>(
SESSION_BATCH_GET_METHOD,
{ parse: parseBatchGetSessionsRequest },
(ctx) => agent().batchGetSessions(ctx.params),
)
.onRequest<ListSubscribeRequest, ListSubscribeResponse>(
LIST_SUBSCRIBE_METHOD,
{ parse: parseListSubscribeRequest },

View File

@@ -104,6 +104,17 @@ export async function canonicalPath(cwd: string): Promise<string> {
}
}
/** Whether the path exists, including a symlink whose target is missing. */
export async function exists(target: string): Promise<boolean> {
try {
await fs.lstat(target);
return true;
} catch (error) {
if (errorCode(error) === "ENOENT" || errorCode(error) === "ENOTDIR") return false;
throw error;
}
}
/** The names of the project directories of `cwd` among `rootEntries`. */
export function projectDirsOf(cwd: string, rootEntries: readonly string[]): string[] {
const exact = encodeProjectPath(cwd);

View File

@@ -15,6 +15,7 @@ import {
RequestError,
type ListSessionsRequest,
type ListSessionsResponse,
type SessionInfo,
} from "@agentclientprotocol/sdk/unstable";
import { deleteSession as sdkDeleteSession } from "@anthropic-ai/claude-agent-sdk";
import { airExtensionMeta } from "../air-extension.js";
@@ -37,7 +38,7 @@ import {
import { LiveSessionRegistry } from "./live-registry.js";
import {
canonicalPath,
errorCode,
exists,
isExactProjectDir,
isSessionId,
projectDirMatches,
@@ -65,6 +66,7 @@ import { sessionInfoOf } from "./session-info.js";
export const SESSION_RENAME_METHOD = "_session/rename";
export const SESSION_ARCHIVE_METHOD = "_session/archive";
export const SESSION_UNARCHIVE_METHOD = "_session/unarchive";
export const SESSION_BATCH_GET_METHOD = "_session/batch_get";
/** The JSON-RPC code of an unknown session (ACP `ResourceNotFound`). */
const RESOURCE_NOT_FOUND = -32002;
@@ -73,6 +75,27 @@ const CURSOR_VERSION = 5;
export type SessionIdRequest = { sessionId: string };
export type RenameSessionRequest = { sessionId: string; title: string };
export type BatchGetSessionsRequest = { sessionIds: string[] };
export type BatchGetSessionsResponse = {
sessions: SessionInfo[];
};
export function parseBatchGetSessionsRequest(value: unknown): BatchGetSessionsRequest {
const record = asRecord(value);
const ids = record.sessionIds;
if (
Object.keys(record).length !== 1 ||
!Array.isArray(ids) ||
ids.some((id) => typeof id !== "string" || !id.trim()) ||
new Set(ids).size !== ids.length
) {
throw RequestError.invalidParams(
undefined,
"params require only sessionIds: distinct, non-blank strings",
);
}
return { sessionIds: ids };
}
export function sessionNotFound(sessionId: string): RequestError {
return new RequestError(RESOURCE_NOT_FOUND, `Session not found: ${sessionId}`, { sessionId });
@@ -364,26 +387,6 @@ function isSdkNotFound(error: unknown): boolean {
return error instanceof Error && /^Session \S+ not found in /.test(error.message);
}
async function exists(target: string): Promise<boolean> {
try {
await fs.lstat(target);
return true;
} catch (error) {
if (errorCode(error) === "ENOENT" || errorCode(error) === "ENOTDIR") return false;
throw error;
}
}
/** The error the SDK `deleteSession` gives for a session id it rejects or
* does not find, which a client without `sessionIndex` got before. */
function sdkDeleteError(sessionId: string): Error {
return new Error(
isSessionId(sessionId)
? `Session ${sessionId} not found in any project directory`
: `Invalid sessionId: ${sessionId}`,
);
}
/**
* `session/delete` of a client that is neither AIR nor a `sessionIndex` client:
* the SDK delete of the session's transcript. A session without one succeeds,
@@ -409,16 +412,18 @@ export async function deleteSessionIfStored(
/**
* `session/delete` of an AIR client without `sessionIndex`, which uses delete
* to mark a session done: archives it instead, so the transcript survives. A
* session without a transcript fails as the SDK delete did.
* session without a transcript has nothing to archive and succeeds, as ACP
* asks of a delete of a session that never existed (see
* {@link deleteSessionIfStored}).
*/
export async function archiveInsteadOfDelete(
sessionId: string,
service: SessionIndexService,
): Promise<void> {
if (!isSessionId(sessionId)) throw sdkDeleteError(sessionId);
if (!isSessionId(sessionId)) return;
// The SDK delete skips empty transcripts, so they do not count.
const found = await service.index.findTranscripts(sessionId);
if (found.length === 0) throw sdkDeleteError(sessionId);
if (found.length === 0) return;
await service.retitle(sessionId, archiveTo(true), { sidecar: "existing" });
}
@@ -520,6 +525,22 @@ export class SessionIndexService {
return hasMore && last ? { sessions, nextCursor: encodeListCursor(last, scope) } : { sessions };
}
/** Metadata for known ids, independent of list pages and project filters. */
async batchGet(
{ sessionIds }: BatchGetSessionsRequest,
own: (sessionId: string) => OwnSessionState | undefined,
): Promise<BatchGetSessionsResponse> {
if (sessionIds.length === 0) return { sessions: [] };
const live = await this.registry.snapshot();
const now = this.now();
const rows = await this.index.getSessions(sessionIds);
return {
sessions: rows.map((row) =>
sessionInfoOf(row, own(row.sessionId), live.get(row.sessionId), now),
),
};
}
/** Runs `mutation` after the previous mutation of the session ended. */
private exclusive<T>(sessionId: string, mutation: () => Promise<T>): Promise<T> {
const previous = this.mutations.get(sessionId) ?? Promise.resolve();

View File

@@ -26,6 +26,8 @@ import { effectiveTitle, visibleTitle } from "./archive-title.js";
import {
canonicalPath,
encodeProjectPath,
errorCode,
exists,
isExactProjectDir,
isSessionId,
normalizePath,
@@ -58,11 +60,15 @@ import { isInLinkedWorktree, worktreeCounterparts } from "./worktrees.js";
export const DEFAULT_LIST_LIMIT = 50;
export const MAX_LIST_LIMIT = 200;
/** Discovery could not establish which sessions the store contains. */
export class SessionStoreUnavailableError extends Error {}
/** Entries of the metadata cache: a few hundred bytes each, so the
* transcripts of a large history (tens of thousands) stay cached and deep
* pages do not read them again. */
const METADATA_CACHE_SIZE = 50_000;
const READ_BATCH_SIZE = 16;
/** Session ids looked up together by {@link SessionIndex.getSessions}. */
const LOOKUP_CHUNK_SIZE = 100;
/** Transcripts read at most per directory to recover a sibling's cwd. */
const MAX_CWD_PROBES_PER_DIR = 64;
/** How long a directory that gave no cwd is not probed again. */
@@ -97,6 +103,24 @@ export type TranscriptCandidate = {
ino: number;
};
type TranscriptFile = Pick<
TranscriptCandidate,
"sessionId" | "filePath" | "dirName" | "projectPath"
>;
/** Non-empty transcript files, with bounded stats shared by lists and id lookups. */
async function transcriptCandidates(
files: readonly TranscriptFile[],
): Promise<TranscriptCandidate[]> {
const stats = await statFiles(files.map(({ filePath }) => filePath));
return files.flatMap((file, i) => {
const current = stats[i];
return current?.isFile() && current.size > 0
? [{ ...file, mtimeMs: current.mtimeMs, size: current.size, ino: current.ino }]
: [];
});
}
/** The cached metadata of one transcript. */
type TranscriptMetadata = {
/** The visible title: without the archive prefix. */
@@ -526,25 +550,16 @@ export class SessionIndex {
const listed = await this.listings.namesOfAll(dirPaths);
const files = dirs.flatMap(({ dirName, projectPath }, i) =>
listed[i]!.filter((name) => name.endsWith(".jsonl") && isSessionId(name.slice(0, -6))).map(
(name) => ({ name, filePath: path.join(root, dirName, name), dirName, projectPath }),
(name) => ({
sessionId: name.slice(0, -6),
filePath: path.join(root, dirName, name),
dirName,
projectPath,
}),
),
);
const stats = await statFiles(files.map(({ filePath }) => filePath));
const all: TranscriptCandidate[] = [];
files.forEach(({ name, filePath, dirName, projectPath }, i) => {
const fileStats = stats[i];
if (!fileStats?.isFile() || fileStats.size === 0) return;
all.push({
sessionId: name.slice(0, -6),
filePath,
dirName,
projectPath,
mtimeMs: fileStats.mtimeMs,
size: fileStats.size,
ino: fileStats.ino,
});
});
return { dirs: [root, ...dirPaths], names: [rootNames, ...listed], candidates: all };
const candidates = await transcriptCandidates(files);
return { dirs: [root, ...dirPaths], names: [rootNames, ...listed], candidates };
}
/**
@@ -580,6 +595,90 @@ export class SessionIndex {
};
}
/** Known ids in request order, using the same rows as the list.
* Addresses exact filenames; only rows without a cwd need sibling discovery. */
async getSessions(sessionIds: readonly string[]): Promise<IndexRow[]> {
if (sessionIds.length === 0) return [];
const root = projectsRoot();
let projectEntries: string[];
try {
projectEntries = await fs.readdir(root);
} catch (cause) {
if (errorCode(cause) === "ENOENT" && !(await exists(root))) {
return [];
}
throw new SessionStoreUnavailableError("Session store could not be read", { cause });
}
const ids = sessionIds.filter(isSessionId);
const byId = new Map<string, IndexRow>();
// Paths are project directories × ids: bound them per chunk, not per request.
for (let start = 0; start < ids.length; start += LOOKUP_CHUNK_SIZE) {
const chunk = ids.slice(start, start + LOOKUP_CHUNK_SIZE);
for (const row of await this.lookupRows(root, projectEntries, chunk)) {
byId.set(row.sessionId, row);
}
}
return sessionIds.flatMap((id) => {
const row = byId.get(id);
return row ? [row] : [];
});
}
/** The rows of `ids` found under `projectEntries`. All copies of an id are
* in the same call, so copy selection is unchanged by chunking. */
private async lookupRows(
root: string,
projectEntries: readonly string[],
ids: readonly string[],
): Promise<IndexRow[]> {
let candidates = await transcriptCandidates(
projectEntries.flatMap((dirName) =>
ids.map((sessionId) => ({
sessionId,
filePath: path.join(root, dirName, `${sessionId}.jsonl`),
dirName,
})),
),
);
if (CASE_INSENSITIVE_PATHS) {
// On a case-insensitive file system, stat of `ABC.jsonl` also finds
// `abc.jsonl`. Keep a file only when its real name is the requested one,
// or differs by more than case (a symlink, as the list would read it).
// A failed realpath means the file is unreadable, so the id is omitted.
const exact = await Promise.all(
candidates.map(async ({ filePath }) => {
try {
const actual = path.basename(await fs.realpath(filePath));
const expected = path.basename(filePath);
return actual === expected || actual.toLowerCase() !== expected.toLowerCase();
} catch {
return false;
}
}),
);
candidates = candidates.filter((_, i) => exact[i]);
}
return this.collectRows(candidates.sort(byMtimeDescending), {
inScope: scopeOf(undefined),
archived: "all",
wanted: Infinity,
unread: async (read, dirNames) => {
const dirs = [...dirNames];
const listed = await this.listings.namesOfAll(dirs.map((dir) => path.join(root, dir)));
const siblings = dirs.flatMap((dirName, i) =>
listed[i]!.filter((name) => name.endsWith(".jsonl") && isSessionId(name.slice(0, -6)))
.map((name) => ({
sessionId: name.slice(0, -6),
filePath: path.join(root, dirName, name),
dirName,
}))
.filter(({ filePath }) => !read.has(filePath)),
);
return (await transcriptCandidates(siblings)).sort(byMtimeDescending);
},
});
}
/** Every transcript file of `sessionId` (see {@link scanSession}). Empty
* files only with `includeEmpty`. */
async findTranscripts(
@@ -778,9 +877,12 @@ export class SessionIndex {
archived: ArchivedFilter;
after?: ListCursor;
wanted: number;
/** The transcripts not read (by path), which may supply a sibling's
* cwd. */
unread: (read: ReadonlySet<string>) => readonly TranscriptCandidate[];
/** The unread transcripts of the directories still eligible for cwd
* recovery. Called only after checking the negative cache. */
unread?: (
read: ReadonlySet<string>,
dirNames: ReadonlySet<string>,
) => readonly TranscriptCandidate[] | Promise<readonly TranscriptCandidate[]>;
restat?: boolean;
},
): Promise<IndexRow[]> {
@@ -852,14 +954,15 @@ export class SessionIndex {
}
await settlePending();
}
if (pending.length > 0) {
const unread = options.unread;
if (pending.length > 0 && unread) {
// A row recovered after the scan stopped still sorts into the page: it
// was read before the stop, and more rows only raise the bound. A
// sibling that the archive filter or the cursor leaves out may still
// supply the cwd.
await this.learnDirCwds(
new Set(pending.map(({ candidate }) => candidate.dirName)),
options.unread(read),
(dirNames) => unread(read, dirNames),
options.restat ?? false,
);
await settlePending();
@@ -893,7 +996,9 @@ export class SessionIndex {
* changes. `restat` as in {@link collectRows}. */
private async learnDirCwds(
dirNames: ReadonlySet<string>,
unread: readonly TranscriptCandidate[],
unread: (
dirNames: ReadonlySet<string>,
) => readonly TranscriptCandidate[] | Promise<readonly TranscriptCandidate[]>,
restatFirst: boolean,
): Promise<void> {
const root = projectsRoot();
@@ -909,8 +1014,9 @@ export class SessionIndex {
if (known && known.mtimeMs === mtimeMs && now - known.at < NO_CWD_RETRY_MS) continue;
mtimes.set(dirName, mtimeMs);
}
if (mtimes.size === 0) return;
const probes = new Map<string, number>();
let remaining = unread.filter((candidate) => {
let remaining = (await unread(new Set(mtimes.keys()))).filter((candidate) => {
if (!mtimes.has(candidate.dirName)) return false;
const count = probes.get(candidate.dirName) ?? 0;
if (count >= MAX_CWD_PROBES_PER_DIR) return false;

View File

@@ -79,8 +79,9 @@ type InitializeSessionModeParams = {
* The permission mode a new Claude query starts in.
*
* An explicit mode wins. A resume or a load continues in the mode of the last
* user record of the transcript. If that mode is unknown or not available now,
* for example bypass, the session starts in `defaultMode` of the settings.
* user record of the transcript. If that mode is unknown or absent from the
* session's mode catalog, use an available `defaultMode` from settings, or
* `default` when that setting is unavailable too.
*/
export function resolveInitialPermissionMode(
sources: { explicit?: PermissionMode; resumed?: string; defaultMode?: unknown },
@@ -97,13 +98,23 @@ export function resolveInitialPermissionMode(
{ error: () => (rejected = true) },
allowBypass,
);
if (!rejected) return mode;
if (!rejected && buildAvailableModes(allowBypass).some((available) => available.id === mode)) {
return mode;
}
logger.error(
`Ignoring permission mode "${sources.resumed}" of the resumed transcript: ` +
`not available in this session.`,
);
}
return resolvePermissionMode(sources.defaultMode, logger, allowBypass);
const configuredMode = resolvePermissionMode(sources.defaultMode, logger, allowBypass);
// A rejected transcript mode falls back only to a mode this session can offer.
if (
sources.resumed !== undefined &&
!buildAvailableModes(allowBypass).some((available) => available.id === configuredMode)
) {
return "default";
}
return configuredMode;
}
/** Owns session-mode policy and the ACP/SDK synchronization it requires. */
@@ -120,7 +131,7 @@ export class SessionModeManager<S extends SessionMode> {
modes: SessionModeState;
autoModeFallbackWarningPending: boolean;
}> {
const availableModes = this.buildAvailableModes(allowBypass);
const availableModes = buildAvailableModes(allowBypass, this.options.airClient?.() ?? false);
let effectiveMode = requestedMode;
let autoModeFallbackWarningPending = false;
@@ -247,7 +258,10 @@ export class SessionModeManager<S extends SessionMode> {
const session = this.requireOpenSession(sessionId);
const requestedMode = this.parseMode(modeId);
if (!this.isAvailable(session.modes.availableModes, requestedMode)) {
throw new Error(`Mode ${modeId} is not available in this session`);
throw RequestError.invalidParams(
undefined,
`Mode ${modeId} is not available in this session`,
);
}
const effectiveMode = this.effectiveMode(session, requestedMode);
@@ -314,7 +328,7 @@ export class SessionModeManager<S extends SessionMode> {
private requireOpenSession(sessionId: string): S {
const session = this.options.getSession(sessionId);
if (!session) throw new Error("Session not found");
if (!session) throw RequestError.sessionNotFound(sessionId);
if (session.queryClosed) {
throw RequestError.internalError(undefined, this.options.sessionEndedMessage);
}
@@ -343,54 +357,10 @@ export class SessionModeManager<S extends SessionMode> {
case "plan":
return modeId;
default:
throw new Error("Invalid Mode");
throw RequestError.invalidParams(undefined, `Invalid Mode: ${modeId}`);
}
}
private buildAvailableModes(allowBypass: boolean): SessionModeState["availableModes"] {
// Only AIR gets the mode kind.
const air = this.options.airClient?.() ?? false;
const kind = (value: string) => {
const meta = airOnlyMeta(air, AIR_KIND_KEY, value);
return meta ? { _meta: meta } : {};
};
const modes: SessionModeState["availableModes"] = [
{
id: "default",
name: "Manual",
description: "Always ask before making changes",
...kind("standard"),
},
{
id: "acceptEdits",
name: "Accept edits",
description: "Automatically accept all file edits",
...kind("standard"),
},
{
id: "plan",
name: "Plan",
description: "Create a plan before making changes",
...kind("plan"),
},
{
id: "auto",
name: "Auto",
description: "Claude handles permission decisions",
...kind("auto_review"),
},
];
if (allowBypass) {
modes.push({
id: "bypassPermissions",
name: "Bypass permissions",
description: "Accepts all permissions",
...kind("full_access"),
});
}
return modes;
}
private async trySyncMode(
query: Pick<Query, "setPermissionMode">,
mode: PermissionMode,
@@ -403,3 +373,49 @@ export class SessionModeManager<S extends SessionMode> {
}
}
}
function buildAvailableModes(
allowBypass: boolean,
air = false,
): SessionModeState["availableModes"] {
// Only AIR gets the mode kind.
const kind = (value: string) => {
const meta = airOnlyMeta(air, AIR_KIND_KEY, value);
return meta ? { _meta: meta } : {};
};
const modes: SessionModeState["availableModes"] = [
{
id: "default",
name: "Manual",
description: "Always ask before making changes",
...kind("standard"),
},
{
id: "acceptEdits",
name: "Accept edits",
description: "Automatically accept all file edits",
...kind("standard"),
},
{
id: "plan",
name: "Plan",
description: "Create a plan before making changes",
...kind("plan"),
},
{
id: "auto",
name: "Auto",
description: "Claude handles permission decisions",
...kind("auto_review"),
},
];
if (allowBypass) {
modes.push({
id: "bypassPermissions",
name: "Bypass permissions",
description: "Accepts all permissions",
...kind("full_access"),
});
}
return modes;
}

View File

@@ -210,22 +210,12 @@ export function buildModelConfigOption(
? models.availableModels.filter((model) => model.modelId !== "default")
: models.availableModels;
return {
id: MODEL_CONFIG_ID,
name: "Model",
description: "AI model to use",
category: "model",
type: "select",
currentValue:
concreteRecommendation && models.currentModelId === "default"
? recommended.value
: models.currentModelId,
...(concreteRecommendation
? {
_meta: withAirMeta(undefined, AIR_RECOMMENDED_CONFIG_VALUE_CAPABILITY, recommended.value),
}
: {}),
options: available.map((model) => {
const currentValue =
concreteRecommendation && models.currentModelId === "default"
? recommended.value
: models.currentModelId;
const options: Array<{ value: string; name: string; description?: string }> = available.map(
(model) => {
if (model.modelId === "default" && defaultInfo?.resolvedModel) {
const named = modelInfos.find(
(info) => info.value !== "default" && info.resolvedModel === defaultInfo.resolvedModel,
@@ -241,7 +231,35 @@ export function buildModelConfigOption(
name: model.name,
description: model.description ?? undefined,
};
}),
},
);
// ACP requires `currentValue` to be one of `options`. A session can run a
// model the picker does not list: resumed onto a model excluded by the
// `availableModels` allowlist, or switched by a refusal fallback. List that
// model as its own entry. It is not added to `models.availableModels`, so the
// next rebuild after switching to a listed model drops it.
if (!options.some((option) => option.value === currentValue)) {
const info = modelInfos.find((model) => model.value === currentValue);
options.push({
value: currentValue,
name: info?.displayName || currentValue,
description: info?.description || undefined,
});
}
return {
id: MODEL_CONFIG_ID,
name: "Model",
description: "AI model to use",
category: "model",
type: "select",
currentValue,
...(concreteRecommendation
? {
_meta: withAirMeta(undefined, AIR_RECOMMENDED_CONFIG_VALUE_CAPABILITY, recommended.value),
}
: {}),
options,
};
}
@@ -595,14 +613,107 @@ export async function getAvailableModels(
for (const name of displayNames) {
displayNameCounts.set(name, (displayNameCounts.get(name) ?? 0) + 1);
}
const names = models.map((model, index) =>
displayNameCounts.get(displayNames[index]) === 1 ? displayNames[index] : model.displayName,
);
const nameCounts = new Map<string, number>();
for (const name of names) {
nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1);
}
return {
availableModels: models.map((model, index) => ({
modelId: model.value,
name:
displayNameCounts.get(displayNames[index]) === 1 ? displayNames[index] : model.displayName,
// Allowlisted aliases can inherit the same SDK label. Keep every ID
// selectable, but distinguish the rows for clients that show only names.
name: nameCounts.get(names[index]) === 1 ? names[index] : `${names[index]} (${model.value})`,
description: model.description,
})),
currentModelId: currentModel.value,
};
}
/** The display name of the model id `model` in `modelInfos`, if it is there. */
export function modelDisplayName(modelInfos: ModelInfo[], model: string): string | undefined {
let byModel = displayNameCache.get(modelInfos);
if (!byModel) displayNameCache.set(modelInfos, (byModel = new Map()));
if (!byModel.has(model)) {
const info = modelInfos.find((info) => info.value === model || info.resolvedModel === model);
byModel.set(model, info?.displayName);
}
return byModel.get(model);
}
/** {@link modelDisplayName} per model list, which a session replaces and never edits. */
const displayNameCache = new WeakMap<ModelInfo[], Map<string, string | undefined>>();
/** The flat entries of a session's `model` select option. */
function modelOptionEntries(option: SessionConfigOption): Array<{ value: string; name: string }> {
if (option.type !== "select") return [];
return option.options.flatMap((entry) =>
"value" in entry ? [{ value: entry.value, name: entry.name }] : entry.options,
);
}
/**
* The entry of the `model` option among `configOptions` for `model`, a model
* id that a response reported, so that a client can compare it with the
* session's own model. Best first:
*
* 1. the current entry, when it runs that model up to a context hint: a
* session on `opus[1m]` or `default` that answers as `claude-opus-4-6`;
* 2. the entry whose value or resolved model is that id;
* 3. an entry that runs that model up to a context hint.
*
* Among several entries for one model, a concrete entry comes before
* `default`, as in {@link resolveModelPreference}. Undefined when no entry
* matches. A model's resolved id comes from `modelInfos`
* (`ModelInfo.resolvedModel`), the wire id that a response reports, as for
* {@link matchResumedModel}. It is compared as it is: a dated id such as
* `claude-haiku-4-5-20251001` matches only the same dated id.
*/
export function modelEntryFor(
configOptions: readonly SessionConfigOption[],
modelInfos: ModelInfo[],
model: string,
): { value: string; name: string } | undefined {
// Each response of a subagent reports its model, so the result is kept per
// list. A session replaces its lists on a change and never edits them.
let byInfos = modelEntryCache.get(configOptions);
if (!byInfos) modelEntryCache.set(configOptions, (byInfos = new WeakMap()));
let byModel = byInfos.get(modelInfos);
if (!byModel) byInfos.set(modelInfos, (byModel = new Map()));
if (byModel.has(model)) return byModel.get(model);
const entry = findModelEntry(configOptions, modelInfos, model);
byModel.set(model, entry);
return entry;
}
const modelEntryCache = new WeakMap<
readonly SessionConfigOption[],
WeakMap<ModelInfo[], Map<string, { value: string; name: string } | undefined>>
>();
function findModelEntry(
configOptions: readonly SessionConfigOption[],
modelInfos: ModelInfo[],
model: string,
): { value: string; name: string } | undefined {
const option = configOptions.find((candidate) => candidate.id === MODEL_CONFIG_ID);
if (option?.type !== "select") return undefined;
// Concrete entries first; `sort` keeps the list order otherwise.
const entries = modelOptionEntries(option).sort(
(left, right) => Number(left.value === "default") - Number(right.value === "default"),
);
const observed = canonicalizeModelId(model);
const resolved = (value: string) =>
canonicalizeModelId(modelInfos.find((info) => info.value === value)?.resolvedModel ?? value);
const sameModel = (value: string) =>
stripContextHints(resolved(value)) === stripContextHints(observed);
const current = entries.find((entry) => entry.value === option.currentValue);
if (current && sameModel(current.value)) return current;
return (
entries.find((entry) => entry.value === model || resolved(entry.value) === observed) ??
entries.find((entry) => sameModel(entry.value))
);
}

View File

@@ -296,6 +296,32 @@ export class SessionTitles {
});
}
/** Sends the session's current title and `updatedAt`, even when the client
* was already sent that title: a client that resumes the session may not
* have it. Both come from the session file, as `session/list` reports
* them; a session with no file yet sends the last title published, if any,
* without `updatedAt`. */
async publishCurrent(session: Session): Promise<void> {
const info = await this.readSessionInfo(session);
// A title published but not yet stored (a generated title still being
// written) wins over `summary`, which is then only the raw first prompt.
const stored =
info?.customTitle || (this.settled && this.lastTitle ? undefined : info?.summary);
const title = stored
? sanitizeTitle(this.agent.clientTitle?.(stored) ?? stored)
: this.lastTitle;
if (!title) return;
this.lastTitle = title;
await this.agent.client.sessionUpdate({
sessionId: this.sessionId,
update: {
sessionUpdate: "session_info_update",
title,
...(info ? { updatedAt: new Date(info.lastModified).toISOString() } : {}),
},
});
}
/** Records a title the CLI took, as soon as it took it. */
rememberStoredTitle(title: string): void {
this.persistedTitle = title;

140
src/session-ultracode.ts Normal file
View File

@@ -0,0 +1,140 @@
import { RequestError } from "@agentclientprotocol/sdk/unstable";
import type {
SessionConfigOption,
SetSessionConfigOptionRequest,
} from "@agentclientprotocol/sdk/unstable";
import type { ModelInfo, Query } from "@anthropic-ai/claude-agent-sdk";
import { ULTRACODE_CONFIG_ID } from "./session-config-ids.js";
export { ULTRACODE_CONFIG_ID } from "./session-config-ids.js";
/** Select-fallback values for clients without boolean config options. */
export const ULTRACODE_ON = "on";
export const ULTRACODE_OFF = "off";
const ULTRACODE_DESCRIPTION = "Dynamic workflows on every task, at any effort level";
/** Ultracode state from the CLI's `get_settings` control response
* (`applied.ultracodeAvailable` / `applied.ultracodeRequested`). Each field is
* undefined when the CLI does not report it. */
export type UltracodeSettingsReport = {
/** Workflows are enabled and the current model supports ultracode. */
available?: boolean;
/** Ultracode is turned on for the session, whether or not it can take
* effect on the current model. */
requested?: boolean;
};
// `Query.getSettings()` exists at runtime (sdk.mjs) but is missing from the
// SDK's type declarations as of 0.3.316.
type GetSettingsQuery = Query & {
getSettings?: () => Promise<{
applied?: { ultracodeAvailable?: unknown; ultracodeRequested?: unknown };
}>;
};
/** Ask the CLI whether ultracode is available and requested for the session.
* Returns undefined when the SDK has no `getSettings` or the request fails;
* callers then fall back to {@link modelSupportsUltracode}. */
export async function readUltracodeSettings(
query: Query,
): Promise<UltracodeSettingsReport | undefined> {
const getSettings = (query as GetSettingsQuery).getSettings;
if (typeof getSettings !== "function") return undefined;
try {
const applied = (await getSettings.call(query))?.applied;
return {
available:
typeof applied?.ultracodeAvailable === "boolean" ? applied.ultracodeAvailable : undefined,
requested:
typeof applied?.ultracodeRequested === "boolean" ? applied.ultracodeRequested : undefined,
};
} catch {
return undefined;
}
}
/** The CLI supports ultracode on exactly the models that support `xhigh`
* effort (both use the same capability check), and `supportedEffortLevels`
* lists `xhigh` only for those models. E.g. Opus 4.7+, Sonnet 5+ and Haiku
* 5.5 qualify; Opus 4.6 and Haiku 4.5 do not. */
export function modelSupportsUltracode(modelInfo: ModelInfo | undefined): boolean {
return (
modelInfo?.supportsEffort === true &&
modelInfo.supportedEffortLevels?.includes("xhigh") === true
);
}
/** Whether dynamic workflows are enabled for the session, inferred from the
* CLI's `ultracodeAvailable` for the model the session started on. This is
* the part of availability that does not change with the model: the plan
* default (off on Pro), `enableWorkflows`/`disableWorkflows` and org policy.
* Undefined when the report can't tell, e.g. `ultracodeAvailable: false` on a
* model that lacks ultracode support anyway. */
export function ultracodeWorkflowsEnabled(
report: UltracodeSettingsReport | undefined,
modelInfo: ModelInfo | undefined,
): boolean | undefined {
if (report?.available === true) return true;
if (report?.available === false && modelSupportsUltracode(modelInfo)) return false;
return undefined;
}
/** Whether to offer the ultracode toggle for `modelInfo`. An unknown workflows
* state shows the toggle: the CLI keeps an unavailable request without
* error, so the toggle is merely inert there. */
export function ultracodeSupported(
workflowsEnabled: boolean | undefined,
modelInfo: ModelInfo | undefined,
): boolean {
return workflowsEnabled !== false && modelSupportsUltracode(modelInfo);
}
/** Ultracode state threaded into `buildConfigOptions`. The option is only
* surfaced while `supported`. */
export type UltracodeOptionState = {
supported: boolean;
enabled: boolean;
/** Whether the client opted into boolean config options. */
useBooleanOption: boolean;
};
/** Build the ultracode toggle: a native boolean for clients that opted into
* boolean config options, otherwise an "on"/"off" select. */
export function createUltracodeConfigOption(
enabled: boolean,
useBooleanOption: boolean,
): SessionConfigOption {
const base = {
id: ULTRACODE_CONFIG_ID,
name: "Ultracode",
description: ULTRACODE_DESCRIPTION,
category: "model_config",
} as const;
if (useBooleanOption) {
return { ...base, type: "boolean", currentValue: enabled };
}
return {
...base,
type: "select",
currentValue: enabled ? ULTRACODE_ON : ULTRACODE_OFF,
options: [
{ value: ULTRACODE_ON, name: "On" },
{ value: ULTRACODE_OFF, name: "Off" },
],
};
}
/** Read the requested ultracode value from a `session/set_config_option`
* request: a boolean, or the "on"/"off" select-fallback strings. */
export function resolveUltracodeEnabled(params: SetSessionConfigOptionRequest): boolean {
const value = params.value;
if (typeof value === "boolean") return value;
if (value === ULTRACODE_ON) return true;
if (value === ULTRACODE_OFF) return false;
throw RequestError.invalidParams(
undefined,
`Invalid value for config option ${ULTRACODE_CONFIG_ID}: ${value}`,
);
}

42
src/startup-failure.ts Normal file
View File

@@ -0,0 +1,42 @@
import { RequestError } from "@agentclientprotocol/sdk/unstable";
import type { SDKStartupFailureReason } from "@anthropic-ai/claude-agent-sdk";
/** Makes the CLI write an `error_during_execution` result with a
* `startup_failure_reason` before it exits on a known startup failure. Without
* it, some failures (e.g. `proxy_invalid`) end with stderr and an exit code
* alone. The SDK then rejects `initializationResult()` with an error that
* carries the reason as `startupFailureReason`. */
export const STARTUP_FAILURE_RESULTS_ENV = { CLAUDE_CODE_STARTUP_FAILURE_RESULTS: "1" };
/** The SDK's prefix on the message of an error built from an error result. */
const ERROR_RESULT_PREFIX = "Claude Code returned an error result: ";
/** Reasons that signing in again, or with another account, can fix. */
const AUTH_REQUIRED_REASONS: ReadonlySet<SDKStartupFailureReason> = new Set([
"gateway_signin_required",
"org_pin_mismatch",
"org_config_refused",
]);
/** The ACP error for a CLI that refused to start, or `undefined` when `error`
* carries no `startupFailureReason` (older CLIs, failures without a known
* cause). The message is the CLI's own, which says what to fix, e.g.
* `Invalid proxy URL in HTTPS_PROXY: … Fix or unset HTTPS_PROXY and restart
* Claude Code.`; `data.startupFailureReason` carries the reason. */
export function startupFailureError(error: unknown): RequestError | undefined {
if (!(error instanceof Error)) return undefined;
const reason = (error as { startupFailureReason?: unknown }).startupFailureReason;
if (typeof reason !== "string" || reason === "") return undefined;
const message = error.message.startsWith(ERROR_RESULT_PREFIX)
? error.message.slice(ERROR_RESULT_PREFIX.length)
: error.message;
const data = { startupFailureReason: reason };
if (AUTH_REQUIRED_REASONS.has(reason as SDKStartupFailureReason)) {
return RequestError.authRequired(data, message);
}
if (reason === "cwd_unavailable") {
return RequestError.invalidParams(data, message);
}
return RequestError.internalError(data, message);
}

View File

@@ -24,6 +24,11 @@ export interface SubagentRecord {
background: boolean;
/** Whether the user stopped it. */
stoppedByUser: boolean;
/**
* Its working directory, when Claude Code placed it outside its parent's:
* in a worktree of its own or of an ancestor, or in a `cwd` of its own.
*/
cwd?: string;
}
/** Where the subagent transcripts of a session are. */
@@ -43,7 +48,9 @@ export interface SubagentHistory {
* Claude keeps the history of a subagent in its own transcript,
* `<session>/subagents/agent-<id>.jsonl`, next to the session transcript.
* The `agent-<id>.meta.json` file beside it names the launching tool use.
* The ids are empty for a session without a local transcript.
* For a child that Claude Code placed elsewhere, it has `worktreePath`,
* `inheritedWorktreePath` or `cwd`; then the child's working directory is the
* `cwd` of its transcript's records. The ids are empty for a session without a local transcript.
*/
export async function subagentHistory(sessionId: string): Promise<SubagentHistory> {
const agents = new Map<string, SubagentRecord>();
@@ -67,6 +74,12 @@ export async function subagentHistory(sessionId: string): Promise<SubagentHistor
> | null;
if (typeof meta?.toolUseId !== "string") return;
const text = (value: unknown) => (typeof value === "string" ? value : undefined);
const placement = [meta.cwd, meta.worktreePath, meta.inheritedWorktreePath]
.map(text)
.find((value) => value && path.isAbsolute(value));
const cwd = placement
? ((await recordedCwd(path.join(directory, `agent-${match[1]!}.jsonl`))) ?? placement)
: undefined;
agents.set(meta.toolUseId, {
agentId: match[1]!,
name: text(meta.name),
@@ -74,6 +87,7 @@ export async function subagentHistory(sessionId: string): Promise<SubagentHistor
description: text(meta.description),
background: meta.requestShape === "background",
stoppedByUser: meta.stoppedByUser === true,
...(cwd ? { cwd } : {}),
});
} catch {
// A damaged meta file leaves its subagent without history.
@@ -87,6 +101,31 @@ export async function subagentHistory(sessionId: string): Promise<SubagentHistor
return dir ? { agents, dir } : { agents };
}
/**
* The `cwd` of the first record of the transcript `file` that has one, or
* undefined when the file cannot be read. Claude Code writes the working
* directory of the session or subagent on each record.
*/
async function recordedCwd(file: string): Promise<string | undefined> {
const stream = createReadStream(file, { encoding: "utf8" });
try {
for await (const line of readline.createInterface({ input: stream, crlfDelay: Infinity })) {
if (!line.includes('"cwd"')) continue;
try {
const cwd = (JSON.parse(line) as { cwd?: unknown } | null)?.cwd;
if (typeof cwd === "string" && path.isAbsolute(cwd)) return cwd;
} catch {
// A damaged record; a later one can have the `cwd`.
}
}
} catch {
// An unreadable transcript; the caller falls back to the metadata's path.
} finally {
stream.destroy();
}
return undefined;
}
/** The folder of the subagent transcripts, next to the session transcript `transcript`. */
function subagentsDirectory(transcript: string, sessionId: string): string {
return path.join(path.dirname(transcript), sessionId, "subagents");
@@ -162,6 +201,7 @@ export async function subagentLineage(
agentId: current,
launchId: launch.launchId,
...(launcher !== null ? { parentAgentId: launcher } : {}),
...(launch.record.cwd ? { cwd: launch.record.cwd } : {}),
...(launch.record.name ? { name: launch.record.name } : {}),
...(launch.record.agentType ? { agentType: launch.record.agentType } : {}),
...(launch.record.description ? { description: launch.record.description } : {}),

View File

@@ -6,7 +6,8 @@
* `subagentHistory`). The replay announces each child right after that call,
* under the session id it had live, the SDK agent id, and sends the child's
* history to the child session. Every child's end goes out before the replay
* ends, since no subagent from before the reload still runs.
* ends. These states are history to the client, so each child's current
* state goes out again after the load response (`currentStates`).
*
* As live, the parent is not shown the Agent or Task call of a child that the
* replay announces: the launch's prompt is the parent's outgoing message, and
@@ -15,21 +16,40 @@
* Unlike live, the replay does not tell runs apart, so a child that handed
* back any report gets no reply from its launch's result.
*
* A SendMessage call whose message the replay delivered to a child before it
* reached the call is that message too: the sender's outgoing view, at the
* call, and the child's incoming one, in the child's history. A child's
* history follows its launch, so a call to a child from its parent is always
* delivered first. Any other SendMessage call stays a tool call.
*
* Right after its announcement, each child gets its `model` option: the model
* of its first response, in its transcript. A later response with another
* model sends it again. A child without responses gets none. After the load
* response, a model goes again only when the session's model list, which the
* replay can lack, maps it to another entry.
*
* A launch without metadata has no known id, so it is not announced: the RFD
* forbids inventing one. Its Agent call stays an ordinary tool call of the
* parent, and a notice says that subagent history is missing.
*/
import type { SessionConfigOption } from "@agentclientprotocol/sdk/unstable";
import type { SessionMessage } from "@anthropic-ai/claude-agent-sdk";
import { statSync } from "node:fs";
import type { AcpSessionNotification, SubagentState } from "./acp-subagents.js";
import {
childModelOption,
HANDBACK_TOOL,
handbackMessage,
isNativeSubagentControlTool,
launchReply,
linkedSessionMessages,
type ModelEntry,
nativeSubagentState,
responseModelEntry,
rfdEndState,
rfdLabels,
sameDirectory,
type SubagentModels,
type WorkState,
} from "./native-subagents.js";
import { noticeUpdate } from "./session-notices.js";
@@ -53,9 +73,15 @@ export type ReplayChild = {
/** The state of the child's last persisted task notification. */
notified?: SubagentState;
announced: boolean;
/** The `cwd` that the replay reported for it; undefined while it inherits its parent's. */
cwd?: string;
promptSent: boolean;
/** Whether it handed a report to its parent with a SubagentHandback call. */
handedBack?: boolean;
/** The `model` option entry that the replay sent. */
model?: ModelEntry;
/** The model id of the latest response in its transcript. */
responseModel?: string;
};
type SendMessageCall = {
@@ -63,7 +89,12 @@ type SendMessageCall = {
to: string;
message: string;
senderSessionId: string;
delivered: boolean;
/** The child the replay delivered the message to. */
recipientSessionId?: string;
/** The replay reached the call before the delivery, so it stays a tool call. */
shownAsToolCall: boolean;
/** The sender's view of the message went out, at the call. */
outgoingSent: boolean;
};
export class RfdSubagentReplay {
@@ -84,6 +115,15 @@ export class RfdSubagentReplay {
constructor(
private readonly history: SubagentHistory,
private readonly publish: Publish,
private readonly models?: SubagentModels,
/**
* Whether the replay maps a model to the session's model list as it goes.
* A load lacks the list until the CLI started, so it maps only in
* {@link currentStates}, after the load response.
*/
private readonly mapsDuringReplay = true,
/** The working directory of the root session, which a child inherits unless it reports its own. */
private readonly rootCwd?: string,
) {}
/**
@@ -107,7 +147,8 @@ export class RfdSubagentReplay {
to: input.to,
message: input.message,
senderSessionId: ownerSessionId,
delivered: false,
shownAsToolCall: false,
outgoingSent: false,
});
}
}
@@ -129,14 +170,25 @@ export class RfdSubagentReplay {
return this.children.get(toolUseId);
}
/** Announces `child` on its parent, as live: running, with the parent's labels. */
async announce(child: ReplayChild): Promise<void> {
/**
* Announces `child` on its parent, as live: running, with the parent's
* labels, and with its recorded `cwd` when that differs from its parent's.
* Then it sends `firstModel`, the model of the child's first response, when
* the transcript has one.
*/
async announce(child: ReplayChild, firstModel?: string): Promise<void> {
if (child.announced) return;
const labels = rfdLabels(
child.record.name ?? child.input.name,
child.record.agentType ?? child.input.subagent_type,
child.record.description ?? child.input.description,
);
const inherited = this.effectiveCwd(child.parentSessionId);
const cwd =
child.record.cwd &&
(inherited === undefined || !(await sameDirectory(child.record.cwd, inherited)))
? child.record.cwd
: undefined;
await this.publish({
sessionId: child.parentSessionId,
update: {
@@ -144,10 +196,51 @@ export class RfdSubagentReplay {
sessionId: child.sessionId,
...(labels.title ? { title: labels.title } : {}),
...(labels.description ? { description: labels.description } : {}),
...(cwd ? { cwd } : {}),
state: { state: "running" },
},
});
child.cwd = cwd;
child.announced = true;
await this.responseModel(child, firstModel);
}
/**
* The working directory of the session `sessionId` as the client holds it:
* a child's reported `cwd`, else its parent's, up to the root's.
*/
private effectiveCwd(sessionId: string): string | undefined {
const seen = new Set<string>();
let child = this.byAgentId.get(sessionId);
while (child && !seen.has(child.sessionId)) {
if (child.cwd) return child.cwd;
seen.add(child.sessionId);
child = this.byAgentId.get(child.parentSessionId);
}
return this.rootCwd;
}
/**
* Takes the model of a replayed response of the child `owner`. A child
* whose model changes gets its `model` option again.
*/
async responseModel(owner: ReplayChild, model: string | undefined): Promise<void> {
const entry = responseModelEntry(model, this.mapsDuringReplay ? this.models : undefined);
if (!entry || !owner.announced) return;
owner.responseModel = model;
await this.sendModel(owner, entry);
}
private async sendModel(child: ReplayChild, model: ModelEntry): Promise<void> {
if (child.model?.value === model.value && child.model.name === model.name) return;
child.model = model;
await this.publish({
sessionId: child.sessionId,
update: {
sessionUpdate: "config_option_update",
configOptions: [childModelOption(model)],
},
});
}
/**
@@ -193,10 +286,11 @@ export class RfdSubagentReplay {
/**
* Takes a replayed update of a tool call, from the transcript of `owner`
* when it is a child's. Returns whether the client is not shown it: an
* update of the launch of a child that the replay shows, or of a successful
* update of the launch of a child that the replay shows, of a SendMessage
* call whose message the replay already delivered, or of a successful
* SubagentHandback call of `owner`. A foreground launch's completed result
* goes out as the child's reply, and a hand-back as the child's message to
* its parent.
* goes out as the child's reply, a delivered SendMessage as the sender's
* outgoing message, and a hand-back as the child's message to its parent.
*/
async toolCallUpdate(
update: AcpSessionNotification["update"],
@@ -205,6 +299,24 @@ export class RfdSubagentReplay {
if (update.sessionUpdate !== "tool_call" && update.sessionUpdate !== "tool_call_update") {
return false;
}
const sendMessage = this.sendMessages.find((call) => call.toolUseId === update.toolCallId);
if (sendMessage) {
if (sendMessage.recipientSessionId === undefined) {
sendMessage.shownAsToolCall = true;
return false;
}
if (!sendMessage.outgoingSent) {
sendMessage.outgoingSent = true;
const [outgoing] = linkedSessionMessages(
sendMessage.toolUseId,
sendMessage.senderSessionId,
sendMessage.recipientSessionId,
sendMessage.message,
);
if (outgoing) await this.publish(outgoing);
}
return true;
}
const handback = owner ? this.handbacks.get(update.toolCallId) : undefined;
if (owner && handback && !handback.failed) {
if (!handback.sent) {
@@ -247,19 +359,19 @@ export class RfdSubagentReplay {
}
/**
* Sends the end of every announced child, and a notice when subagent history
* is missing. The end is the state of the child's last task notification,
* else `cancelled` for a child the user stopped, else the result of its
* foreground launch, else `idle` with no stop reason.
* Sends the end of every announced child (see `endState`), and a notice when
* subagent history is missing.
*/
async finish(supportsNotices: boolean, rootSessionId: string): Promise<void> {
for (const child of [...this.children.values()].reverse()) {
if (!child.announced) continue;
const end = child.notified ?? (child.record.stoppedByUser ? "cancelled" : child.launchEnd);
const state: WorkState = end ? rfdEndState(end, undefined) : { state: "idle" };
await this.publish({
sessionId: child.parentSessionId,
update: { sessionUpdate: "subagent_update", sessionId: child.sessionId, state },
update: {
sessionUpdate: "subagent_update",
sessionId: child.sessionId,
state: endState(child),
},
});
}
const missing = this.omitted.size + this.withoutHistory;
@@ -278,6 +390,61 @@ export class RfdSubagentReplay {
}
}
/**
* The current state of each announced child, to send after the load
* response. The RFD makes that response the freshness boundary: a client
* treats the replayed states before it as history, so they show no current
* activity and allow no cancel.
*
* `live` gives the work state of a child that still runs in this process, as
* when a live session is loaded again. Such a child reports that state and
* can be cancelled, as live. Any other child cannot be running, because the
* process that ran it is gone, so its replayed end is its current state.
*
* A replayed `cwd` whose directory is gone, such as a worktree that Claude
* Code removed when the child finished, is cleared with `cwd: null`, as the
* RFD requires after a reconnection.
*/
currentStates(
live: (sessionId: string) => WorkState["state"] | undefined,
liveConfiguration?: (sessionId: string) => SessionConfigOption[] | undefined,
): AcpSessionNotification[] {
return [...this.children.values()]
.filter((child) => child.announced)
.flatMap((child): AcpSessionNotification[] => {
const liveState = live(child.sessionId);
const state: WorkState =
liveState === undefined || liveState === "idle" ? endState(child) : { state: liveState };
// The session's model list may be known only now, after the load response.
const entry = responseModelEntry(child.responseModel, this.models);
const replayed = child.model ? JSON.stringify([childModelOption(child.model)]) : undefined;
const current =
liveConfiguration?.(child.sessionId) ?? (entry ? [childModelOption(entry)] : undefined);
// A configuration that the replay already sent does not go again.
const configOptions = current && JSON.stringify(current) !== replayed ? current : undefined;
return [
{
sessionId: child.parentSessionId,
update: {
sessionUpdate: "subagent_update",
sessionId: child.sessionId,
...(liveState !== undefined ? { capabilities: { cancel: {} } } : {}),
...(child.cwd && !isDirectory(child.cwd) ? { cwd: null } : {}),
state,
},
},
...(configOptions
? [
{
sessionId: child.sessionId,
update: { sessionUpdate: "config_option_update" as const, configOptions },
},
]
: []),
];
});
}
private registerLaunch(
toolUseId: string,
input: Record<string, unknown>,
@@ -322,6 +489,9 @@ export class RfdSubagentReplay {
* first undelivered SendMessage to the child whose message the delivered
* text contains. With that call, the message has the call's id and text and
* its sender, as live; without it, its transcript id and the delivered text.
* Unless the replay already showed the call as a tool call, the message
* replaces it, so this view names the sender's view, which goes out at the
* call (see {@link toolCallUpdate}).
*/
private async sendDelivery(
child: ReplayChild,
@@ -331,13 +501,15 @@ export class RfdSubagentReplay {
const names = [child.sessionId, child.record.name, child.input.name];
const call = this.sendMessages.find(
(candidate) =>
!candidate.delivered &&
candidate.recipientSessionId === undefined &&
names.includes(candidate.to) &&
text.includes(candidate.message.trim()),
);
if (call) {
call.delivered = true;
await this.sendMessage(child, call.toolUseId, call.message, call.senderSessionId);
call.recipientSessionId = child.sessionId;
await this.sendMessage(child, call.toolUseId, call.message, call.senderSessionId, {
linked: !call.shownAsToolCall,
});
} else {
await this.sendMessage(child, message.uuid, text, undefined);
}
@@ -348,6 +520,7 @@ export class RfdSubagentReplay {
messageId: string,
text: string,
sender: string | undefined,
{ linked = false }: { linked?: boolean } = {},
): Promise<void> {
await this.publish({
sessionId: child.sessionId,
@@ -356,12 +529,23 @@ export class RfdSubagentReplay {
messageId,
...(sender ? { senderSessionId: sender } : {}),
recipientSessionId: child.sessionId,
...(linked ? { senderMessageId: messageId } : {}),
content: [{ type: "text", text }],
},
});
}
}
/** The model of the first response in `messages`, a subagent's transcript. */
export function firstResponseModel(messages: readonly SessionMessage[]): string | undefined {
for (const message of messages) {
if (message.type !== "assistant") continue;
const model = (message.message as { model?: unknown } | undefined)?.model;
if (typeof model === "string" && model && model !== "<synthetic>") return model;
}
return undefined;
}
/**
* The end of a foreground launch from its tool result: an error result that
* names a stop is a cancellation, any other error a failure.
@@ -376,6 +560,16 @@ export function replaySubagentTerminalState(
: "failed";
}
/**
* How `child`'s work ended: the state of its last task notification, else
* `cancelled` for a child the user stopped, else the result of its foreground
* launch, else `idle` with no stop reason.
*/
function endState(child: ReplayChild): WorkState {
const end = child.notified ?? (child.record.stoppedByUser ? "cancelled" : child.launchEnd);
return end ? rfdEndState(end, undefined) : { state: "idle" };
}
function replayContentText(value: unknown, seen = new Set<unknown>()): string {
if (typeof value === "string") return value;
if (typeof value !== "object" || value === null || seen.has(value)) return "";
@@ -415,6 +609,15 @@ function textOf(content: unknown): string {
.join("\n");
}
/** Whether `dir` is an existing directory. */
function isDirectory(dir: string): boolean {
try {
return statSync(dir).isDirectory();
} catch {
return false;
}
}
/** A message that a SendMessage call delivered to the child. */
function isDelivery(message: SessionMessage): boolean {
return (message as { origin?: { kind?: unknown } | null }).origin?.kind === "coordinator";

View File

@@ -464,6 +464,16 @@ describe("ClaudeAcpAgent settings", () => {
expect(response.modes.currentModeId).toBe("plan");
});
it("uses the available configured mode when the restored mode is not in the catalog", async () => {
const { permissionMode, response } = await resume([user("dontAsk")]);
expect(permissionMode).toBe("plan");
expect(response.modes.currentModeId).toBe("plan");
expect(response.configOptions.find((option: any) => option.id === "mode").currentValue).toBe(
"plan",
);
});
it("uses permissions.defaultMode when bypass is not available now", async () => {
const { permissionMode, response } = await resume([user("bypassPermissions")], {
claudeCode: { options: { allowDangerouslySkipPermissions: false } },
@@ -521,15 +531,16 @@ describe("ClaudeAcpAgent settings", () => {
(agent as any).createSession = async (params: object, options: { resume?: string }) => {
creations.push(options);
// The CLI never wrote the conversation, so the resume fails.
if (options.resume) throw RequestError.resourceNotFound(sessionId);
if (options.resume) throw RequestError.sessionNotFound(sessionId);
return createSession(params, options);
};
await (agent as any).recreateSignedOutQuery(sessionId, (agent as any).sessions[sessionId]);
const husk = (agent as any).sessions[sessionId];
await (agent as any).recreateSignedOutQuery(sessionId, husk);
expect(creations).toEqual([
{ resume: sessionId, permissionMode: "default" },
{ reuseSessionId: sessionId, permissionMode: "default" },
{ resume: sessionId, permissionMode: "default", replacingSession: husk },
{ reuseSessionId: sessionId, permissionMode: "default", replacingSession: husk },
]);
expect(getCapturedOptions().permissionMode).toBe("default");
});

File diff suppressed because it is too large Load Diff

View File

@@ -37,6 +37,7 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async (importOriginal) => {
...actual,
query: harness.mockedQuery,
getSessionMessages: harness.mockedSessionMessages,
getSessionInfo: harness.mockedSessionInfo,
};
});

View File

@@ -1,4 +1,5 @@
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]"},"name":"mcp"}],"sessionUpdate":"available_commands_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"SandboxNetworkAccess"}},"content":[],"kind":"other","name":"SandboxNetworkAccess","rawInput":{"host":"example.com"},"sessionUpdate":"tool_call","status":"pending","title":"SandboxNetworkAccess","toolCallId":"toolu_net"}}}
{"kind":"requestPermission","payload":{"_meta":{"jetbrains":{"air":{"permission":{"description":"Reason: Sandbox blocks network","title":"example.com","version":1},"version":1}}},"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","toolCall":{"rawInput":{"host":"example.com"},"title":"example.com","toolCallId":"toolu_net"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_net"}}}
{"kind":"promptResponse","payload":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}

View File

@@ -8,8 +8,10 @@
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"terminal_output_delta":{"data":"app.ts","terminal_id":"toolu_r_bash"}},"sessionUpdate":"tool_call_update","toolCallId":"toolu_r_bash"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"terminal_exit":{"exit_code":0,"signal":null,"terminal_id":"toolu_r_bash"}},"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_r_bash"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"rawOutput":"The file <cwd>/src/app.ts has been updated successfully.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_r_edit"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>:replay-subagent:toolu_r_task","update":{"configOptions":[{"category":"model","currentValue":"claude-sonnet-4-6","id":"model","name":"Model","options":[{"name":"claude-sonnet-4-6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>:replay-subagent:toolu_r_task","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_r_task"}},"content":{"text":"Subagent text.","type":"text"},"messageId":"msg_r_sub","sessionUpdate":"agent_message_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"content":{"text":"Fixed.","type":"text"},"messageId":"msg_r2","sessionUpdate":"agent_message_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"sessionUpdate":"subagent_state_update","state":"completed","subagentSessionId":"<session>:replay-subagent:toolu_r_task"}}}
{"kind":"loadSession","payload":{"configOptions":[{"category":"mode","currentValue":"default","description":"Session permission mode","id":"mode","name":"Mode","options":[{"_meta":{"jetbrains":{"air":{"kind":"standard","version":1}}},"description":"Always ask before making changes","name":"Manual","value":"default"},{"_meta":{"jetbrains":{"air":{"kind":"standard","version":1}}},"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"_meta":{"jetbrains":{"air":{"kind":"plan","version":1}}},"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"_meta":{"jetbrains":{"air":{"kind":"auto_review","version":1}}},"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"_meta":{"jetbrains":{"air":{"kind":"full_access","version":1}}},"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","id":"model","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"modes":{"availableModes":[{"_meta":{"jetbrains":{"air":{"kind":"standard","version":1}}},"description":"Always ask before making changes","id":"default","name":"Manual"},{"_meta":{"jetbrains":{"air":{"kind":"standard","version":1}}},"description":"Automatically accept all file edits","id":"acceptEdits","name":"Accept edits"},{"_meta":{"jetbrains":{"air":{"kind":"plan","version":1}}},"description":"Create a plan before making changes","id":"plan","name":"Plan"},{"_meta":{"jetbrains":{"air":{"kind":"auto_review","version":1}}},"description":"Claude handles permission decisions","id":"auto","name":"Auto"},{"_meta":{"jetbrains":{"air":{"kind":"full_access","version":1}}},"description":"Accepts all permissions","id":"bypassPermissions","name":"Bypass permissions"}],"currentModeId":"default"},"sessionId":"<session>"}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>:replay-subagent:toolu_r_task","update":{"configOptions":[{"category":"model","currentValue":"claude-sonnet-4-6","id":"model","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]"},"name":"mcp"}],"sessionUpdate":"available_commands_update"}}}

View File

@@ -2,6 +2,7 @@
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"capabilities":{},"name":"Background review","prompt":"Review the diff","sessionUpdate":"subagent_spawned","subagentSessionId":"a2","task":"Review the diff"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"a2","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_agent"}},"configOptions":[{"category":"model","currentValue":"claude-sonnet-4-6","id":"model","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"a2","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_agent"}},"content":{"text":"Reviewi","type":"text"},"messageId":"msg_async_sub","sessionUpdate":"agent_message_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"a2","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_agent"}},"content":{"text":"ng now.","type":"text"},"messageId":"msg_async_sub","sessionUpdate":"agent_message_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"sessionUpdate":"subagent_state_update","state":"completed","subagentSessionId":"a2"}}}

View File

@@ -2,6 +2,7 @@
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"capabilities":{},"name":"Check the logs","prompt":"Check logs","sessionUpdate":"subagent_spawned","subagentSessionId":"agent_late","task":"Check logs"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_late","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_late"}},"configOptions":[{"category":"model","currentValue":"claude-sonnet-4-6","id":"model","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_late","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_late","toolName":"Read"}},"content":[],"kind":"read","locations":[],"name":"Read","sessionUpdate":"tool_call","status":"pending","title":"Read File","toolCallId":"toolu_late_read"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_late","update":{"locations":[{"line":1,"path":"/var/log/app.log"}],"rawInput":{"file_path":"/var/log/app.log"},"sessionUpdate":"tool_call_update","title":"Read /var/log/app.log","toolCallId":"toolu_late_read"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"sessionUpdate":"subagent_state_update","state":"completed","subagentSessionId":"agent_late"}}}

View File

@@ -2,6 +2,7 @@
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"capabilities":{},"name":"Fix the build","prompt":"Make the build pass","sessionUpdate":"subagent_spawned","subagentSessionId":"agent_n","task":"Make the build pass"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_n","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"configOptions":[{"category":"model","currentValue":"claude-sonnet-4-6","id":"model","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_n","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"content":{"text":"Running t","type":"text"},"messageId":"msg_native_sub_1","sessionUpdate":"agent_message_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_n","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"content":{"text":"he build.","type":"text"},"messageId":"msg_native_sub_1","sessionUpdate":"agent_message_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_n","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Grep"}},"content":[],"kind":"search","name":"Grep","sessionUpdate":"tool_call","status":"pending","title":"grep","toolCallId":"toolu_native_grep"}}}

View File

@@ -2,7 +2,9 @@
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"capabilities":{},"name":"Plan the work","prompt":"Plan it","sessionUpdate":"subagent_spawned","subagentSessionId":"agent_outer","task":"Plan it"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_outer","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_outer"}},"configOptions":[{"category":"model","currentValue":"claude-sonnet-4-6","id":"model","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_outer","update":{"capabilities":{},"name":"Read the spec","prompt":"Read spec.md","sessionUpdate":"subagent_spawned","subagentSessionId":"agent_inner","task":"Read spec.md"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_inner","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_inner"}},"configOptions":[{"category":"model","currentValue":"claude-sonnet-4-6","id":"model","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_inner","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_inner","toolName":"Read"}},"content":[],"kind":"read","locations":[],"name":"Read","sessionUpdate":"tool_call","status":"pending","title":"Read File","toolCallId":"toolu_grandchild_read"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_inner","update":{"locations":[{"line":1,"path":"/spec.md"}],"rawInput":{"file_path":"/spec.md"},"sessionUpdate":"tool_call_update","title":"Read /spec.md","toolCallId":"toolu_grandchild_read"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_inner","update":{"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_grandchild_read"}}}

View File

@@ -2,6 +2,7 @@
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"capabilities":{},"name":"Explore code","prompt":"Find the parser","sessionUpdate":"subagent_spawned","subagentSessionId":"agent_1","task":"Find the parser"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"configOptions":[{"category":"model","currentValue":"claude-sonnet-4-6","id":"model","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":{"text":"Looking for","type":"text"},"messageId":"msg_sub_1","sessionUpdate":"agent_message_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":{"text":" the parser.","type":"text"},"messageId":"msg_sub_1","sessionUpdate":"agent_message_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Read"}},"content":[],"kind":"read","locations":[],"name":"Read","sessionUpdate":"tool_call","status":"pending","title":"Read File","toolCallId":"toolu_sub_read"}}}

View File

@@ -2,6 +2,7 @@
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"capabilities":{},"name":"Summarize","prompt":"Summarize the repo","sessionUpdate":"subagent_spawned","subagentSessionId":"agent_t","task":"Summarize the repo"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_t","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_transcript"}},"configOptions":[{"category":"model","currentValue":"claude-sonnet-4-6","id":"model","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_t","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_transcript"}},"content":{"text":"Look at t","type":"text"},"messageId":"msg_transcript_sub","sessionUpdate":"agent_thought_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_t","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_transcript"}},"content":{"text":"he layout.","type":"text"},"messageId":"msg_transcript_sub","sessionUpdate":"agent_thought_chunk"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"agent_t","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_transcript"}},"content":{"text":"The repo has ","type":"text"},"messageId":"msg_transcript_sub","sessionUpdate":"agent_message_chunk"}}}

View File

@@ -1,15 +1,15 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"other","name":"AskUserQuestion","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Asking for your input","toolCallId":"toolu_ask"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"Which database?","type":"text"},"type":"content"}],"rawInput":{"questions":[{"header":"Database","multiSelect":false,"options":[{"description":"Relational","label":"Postgres","preview":"CREATE TABLE t ();"},{"description":"Key-value","label":"Redis"}],"question":"Which database?"}]},"sessionUpdate":"tool_call_update","title":"Which database?","toolCallId":"toolu_ask"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"elicitation/create","payload":{"message":"Which database?","mode":"form","requestedSchema":{"properties":{"question_0":{"oneOf":[{"_meta":{"_claude/askUserQuestionOption":{"preview":"CREATE TABLE t ();"}},"const":"Postgres","description":"Relational","title":"Postgres"},{"const":"Redis","description":"Key-value","title":"Redis"}],"title":"Database","type":"string"},"question_0_custom":{"description":"Type your own answer, or add a note to the option you chose above (optional).","title":"Other","type":"string"}},"type":"object"},"sessionId":"<session>","toolCallId":"toolu_ask"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"User has answered your questions: \"Which database?\"=\"Postgres\".","type":"text"},"type":"content"}],"rawOutput":"User has answered your questions: \"Which database?\"=\"Postgres\".","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_ask"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,6 +1,6 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,6 +1,6 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,6 +1,6 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"terminal_update","terminalId":"toolu_bg"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"terminalId":"toolu_bg","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"toolu_bg"}}}
@@ -15,4 +15,4 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"exitStatus":{},"sessionUpdate":"terminal_update","terminalId":"toolu_bg"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_bg"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"terminal_update","terminalId":"toolu_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"terminalId":"toolu_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"toolu_bash"}}}
@@ -14,4 +14,4 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"exitStatus":{"exitCode":1},"sessionUpdate":"terminal_update","terminalId":"toolu_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"tool_call_update","status":"failed","toolCallId":"toolu_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"terminal_update","terminalId":"toolu_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"terminalId":"toolu_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"toolu_bash"}}}
@@ -10,9 +10,9 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"command":"ls -la"},"sessionUpdate":"tool_call_update","title":"ls -la","toolCallId":"toolu_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"command":"ls -la","description":"List files"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"terminalId":"toolu_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{"command":"ls -la","description":"List files"},"status":"pending","title":"ls -la","toolCallId":"toolu_bash"},"type":"tool_call"},"title":"ls -la"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"terminalId":"toolu_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{"command":"ls -la","description":"List files"},"title":"ls -la","toolCallId":"toolu_bash"},"type":"tool_call"},"title":"ls -la"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"output":{"data":"YS50cwpiLnRz"},"sessionUpdate":"terminal_update","terminalId":"toolu_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"exitStatus":{"exitCode":0},"sessionUpdate":"terminal_update","terminalId":"toolu_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_bash"}}}
@@ -20,4 +20,4 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"ne.","type":"text"},"messageId":"msg_done","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"compactionId":"00000000-0000-4000-8000-000000000001","sessionUpdate":"compaction_update","status":"in_progress"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"compactionId":"00000000-0000-4000-8000-000000000001","error":"Context too small","sessionUpdate":"compaction_update","status":"failed"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,11 +1,11 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"compactionId":"00000000-0000-4000-8000-000000000001","sessionUpdate":"compaction_update","status":"in_progress"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"compactionId":"00000000-0000-4000-8000-000000000001","content":{"text":"The summary.","type":"text"},"sessionUpdate":"compaction_summary_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"compactionId":"00000000-0000-4000-8000-000000000001","sessionUpdate":"compaction_update","status":"completed"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"usage_update","size":200000,"used":100}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":100}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,11 +1,11 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"compactionId":"00000000-0000-4000-8000-000000000001","sessionUpdate":"compaction_update","status":"in_progress"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"compactionId":"00000000-0000-4000-8000-000000000001","content":{"text":"The summary.","type":"text"},"sessionUpdate":"compaction_summary_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"compactionId":"00000000-0000-4000-8000-000000000001","sessionUpdate":"compaction_update","status":"completed"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"usage_update","size":200000,"used":100}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":100}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"edit","locations":[],"name":"Edit","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Edit","toolCallId":"toolu_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"locations":[{"path":"<cwd>/src/app.ts"}],"rawInput":{"file_path":"<cwd>/src/app.ts"},"sessionUpdate":"tool_call_update","title":"Edit src/app.ts","toolCallId":"toolu_edit"}}}
@@ -10,9 +10,9 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"file_path":"<cwd>/src/app.ts","new_string":"value = 2","old_string":"value = 1"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"changes":[{"fileType":"text","operation":"modify","path":"<cwd>/src/app.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/src/app.ts <cwd>/src/app.ts\n--- <cwd>/src/app.ts\n+++ <cwd>/src/app.ts\n@@ -1 +1 @@\n-const value = 1;\n+const value = 2;\n"},"type":"diff"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"changes":[{"fileType":"text","operation":"modify","path":"<cwd>/src/app.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/src/app.ts <cwd>/src/app.ts\n--- <cwd>/src/app.ts\n+++ <cwd>/src/app.ts\n@@ -1 +1 @@\n-const value = 1;\n+const value = 2;\n"},"type":"diff"}],"kind":"edit","locations":[{"path":"<cwd>/src/app.ts"}],"name":"Edit","rawInput":{"file_path":"<cwd>/src/app.ts","new_string":"value = 2","old_string":"value = 1"},"status":"pending","title":"Edit src/app.ts","toolCallId":"toolu_edit"},"type":"tool_call"},"title":"Edit src/app.ts"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"changes":[{"fileType":"text","operation":"modify","path":"<cwd>/src/app.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/src/app.ts <cwd>/src/app.ts\n--- <cwd>/src/app.ts\n+++ <cwd>/src/app.ts\n@@ -1 +1 @@\n-const value = 1;\n+const value = 2;\n"},"type":"diff"}],"kind":"edit","locations":[{"path":"<cwd>/src/app.ts"}],"name":"Edit","rawInput":{"file_path":"<cwd>/src/app.ts","new_string":"value = 2","old_string":"value = 1"},"title":"Edit src/app.ts","toolCallId":"toolu_edit"},"type":"tool_call"},"title":"Edit src/app.ts"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"```\nThe user doesn't want to proceed with this tool use. The tool use was rejected.\n```","type":"text"},"type":"content"}],"rawOutput":"The user doesn't want to proceed with this tool use. The tool use was rejected.","sessionUpdate":"tool_call_update","status":"failed","toolCallId":"toolu_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"edit","locations":[],"name":"Edit","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Edit","toolCallId":"toolu_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"locations":[{"path":"<cwd>/src/app.ts"}],"rawInput":{"file_path":"<cwd>/src/app.ts"},"sessionUpdate":"tool_call_update","title":"Edit src/app.ts","toolCallId":"toolu_edit"}}}
@@ -10,10 +10,10 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"file_path":"<cwd>/src/app.ts","new_string":"const value = 2;","old_string":"const value = 1;"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"changes":[{"fileType":"text","operation":"modify","path":"<cwd>/src/app.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/src/app.ts <cwd>/src/app.ts\n--- <cwd>/src/app.ts\n+++ <cwd>/src/app.ts\n@@ -1,3 +1,3 @@\n line 1\n-const value = 1;\n+const value = 2;\n line 3\n"},"type":"diff"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"changes":[{"fileType":"text","operation":"modify","path":"<cwd>/src/app.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/src/app.ts <cwd>/src/app.ts\n--- <cwd>/src/app.ts\n+++ <cwd>/src/app.ts\n@@ -1,3 +1,3 @@\n line 1\n-const value = 1;\n+const value = 2;\n line 3\n"},"type":"diff"}],"kind":"edit","locations":[{"path":"<cwd>/src/app.ts"}],"name":"Edit","rawInput":{"file_path":"<cwd>/src/app.ts","new_string":"const value = 2;","old_string":"const value = 1;"},"status":"pending","title":"Edit src/app.ts","toolCallId":"toolu_edit"},"type":"tool_call"},"title":"Edit src/app.ts"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"changes":[{"fileType":"text","operation":"modify","path":"<cwd>/src/app.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/src/app.ts <cwd>/src/app.ts\n--- <cwd>/src/app.ts\n+++ <cwd>/src/app.ts\n@@ -1,3 +1,3 @@\n line 1\n-const value = 1;\n+const value = 2;\n line 3\n"},"type":"diff"}],"kind":"edit","locations":[{"path":"<cwd>/src/app.ts"}],"name":"Edit","rawInput":{"file_path":"<cwd>/src/app.ts","new_string":"const value = 2;","old_string":"const value = 1;"},"title":"Edit src/app.ts","toolCallId":"toolu_edit"},"type":"tool_call"},"title":"Edit src/app.ts"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawOutput":"The file <cwd>/src/app.ts has been updated successfully.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"locations":[{"line":1,"path":"<cwd>/src/app.ts"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,17 +1,17 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"switch_mode","name":"ExitPlanMode","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Approve Plan","toolCallId":"toolu_plan"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"# Plan\n1. Do it","type":"text"},"type":"content"}],"rawInput":{"plan":"# Plan\n1. Do it"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_plan"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes, manually approve edits","optionId":"exit-plan-default"},{"kind":"allow_always","name":"Yes, clear context (0% used) and use auto mode","optionId":"exit-plan-clear-auto"},{"kind":"allow_always","name":"Yes, and use auto mode","optionId":"exit-plan-auto"},{"kind":"allow_always","name":"Yes, and bypass permissions","optionId":"exit-plan-bypass"},{"kind":"reject_once","name":"No, keep planning","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"content":{"text":"# Plan\n1. Do it","type":"text"},"type":"content"}],"kind":"switch_mode","name":"ExitPlanMode","rawInput":{"plan":"# Plan\n1. Do it"},"status":"pending","title":"Approve Plan","toolCallId":"toolu_plan"},"type":"tool_call"},"title":"Ready to code?"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes, manually approve edits","optionId":"exit-plan-default"},{"kind":"allow_always","name":"Yes, clear context (0% used) and use auto mode","optionId":"exit-plan-clear-auto"},{"kind":"allow_always","name":"Yes, and use auto mode","optionId":"exit-plan-auto"},{"kind":"allow_always","name":"Yes, and bypass permissions","optionId":"exit-plan-bypass"},{"kind":"reject_once","name":"No, keep planning","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"content":{"text":"# Plan\n1. Do it","type":"text"},"type":"content"}],"kind":"switch_mode","name":"ExitPlanMode","rawInput":{"plan":"# Plan\n1. Do it"},"title":"Approve Plan","toolCallId":"toolu_plan"},"type":"tool_call"},"title":"Ready to code?"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawOutput":"User has approved your plan. You can now start coding.\n\n## Approved Plan:\n# Plan\n1. Do it","sessionUpdate":"tool_call_update","status":"completed","title":"Exited Plan Mode","toolCallId":"toolu_plan"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"plan":{"content":"# Plan\n1. Do it","planId":"plan-mode","type":"markdown"},"sessionUpdate":"plan_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,15 +1,15 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"switch_mode","name":"ExitPlanMode","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Approve Plan","toolCallId":"toolu_plan"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"# Plan\n1. Do it","type":"text"},"type":"content"}],"rawInput":{"plan":"# Plan\n1. Do it"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_plan"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes, manually approve edits","optionId":"exit-plan-default"},{"kind":"allow_always","name":"Yes, clear context (0% used) and use auto mode","optionId":"exit-plan-clear-auto"},{"kind":"allow_always","name":"Yes, and use auto mode","optionId":"exit-plan-auto"},{"kind":"allow_always","name":"Yes, and bypass permissions","optionId":"exit-plan-bypass"},{"kind":"reject_once","name":"No, keep planning","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"content":{"text":"# Plan\n1. Do it","type":"text"},"type":"content"}],"kind":"switch_mode","name":"ExitPlanMode","rawInput":{"plan":"# Plan\n1. Do it"},"status":"pending","title":"Approve Plan","toolCallId":"toolu_plan"},"type":"tool_call"},"title":"Ready to code?"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes, manually approve edits","optionId":"exit-plan-default"},{"kind":"allow_always","name":"Yes, clear context (0% used) and use auto mode","optionId":"exit-plan-clear-auto"},{"kind":"allow_always","name":"Yes, and use auto mode","optionId":"exit-plan-auto"},{"kind":"allow_always","name":"Yes, and bypass permissions","optionId":"exit-plan-bypass"},{"kind":"reject_once","name":"No, keep planning","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"content":{"text":"# Plan\n1. Do it","type":"text"},"type":"content"}],"kind":"switch_mode","name":"ExitPlanMode","rawInput":{"plan":"# Plan\n1. Do it"},"title":"Approve Plan","toolCallId":"toolu_plan"},"type":"tool_call"},"title":"Ready to code?"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"```\n```\nThe user doesn't want to proceed with this tool use.\n```\n```","type":"text"},"type":"content"}],"rawOutput":"The user doesn't want to proceed with this tool use.","sessionUpdate":"tool_call_update","status":"failed","toolCallId":"toolu_plan"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,6 +1,6 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"/goal Ship the feature","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"search","name":"Grep","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"grep","toolCallId":"toolu_grep"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"pattern":"value"},"sessionUpdate":"tool_call_update","title":"grep \"value\"","toolCallId":"toolu_grep"}}}
@@ -16,4 +16,4 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"pattern":"**/*.ts"},"sessionUpdate":"tool_call_update","title":"Find `**/*.ts`","toolCallId":"toolu_glob"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"src/app.ts\nsrc/b.ts","type":"text"},"type":"content"}],"rawOutput":"src/app.ts\nsrc/b.ts","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_glob"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,15 +1,15 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"other","name":"mcp__docs__search","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"mcp__docs__search","toolCallId":"toolu_mcp"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"q":"acp"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_mcp"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"_meta":{"claudeCode":{"mcpServer":{"name":"docs","source":"project"}}},"content":[],"kind":"other","name":"mcp__docs__search","rawInput":{"q":"acp"},"status":"pending","title":"mcp__docs__search","toolCallId":"toolu_mcp"},"type":"tool_call"},"title":"mcp__docs__search"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"_meta":{"claudeCode":{"mcpServer":{"name":"docs","source":"project"}}},"content":[],"kind":"other","name":"mcp__docs__search","rawInput":{"q":"acp"},"title":"mcp__docs__search","toolCallId":"toolu_mcp"},"type":"tool_call"},"title":"mcp__docs__search"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"Found 2 documents.","type":"text"},"type":"content"}],"rawOutput":[{"text":"Found 2 documents.","type":"text"}],"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_mcp"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolResponse":{"mode":"select"}}},"kind":"read","locations":[{"path":"/memory/a.md"},{"path":"/memory/b.md"}],"name":"memory_recall","sessionUpdate":"tool_call_update","status":"completed","title":"Recalled 2 memories","toolCallId":"00000000-0000-4000-8000-000000000001"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolResponse":{"mode":"synthesize"}}},"content":[{"content":{"text":"The user prefers tabs.","type":"text"},"type":"content"}],"kind":"read","name":"memory_recall","sessionUpdate":"tool_call_update","status":"completed","title":"Recalled synthesized memory","toolCallId":"00000000-0000-4000-8000-000000000002"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,10 +1,11 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"other","name":"SandboxNetworkAccess","rawInput":{"host":"example.com"},"sessionUpdate":"tool_call_update","status":"pending","title":"SandboxNetworkAccess","toolCallId":"toolu_net"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"description":"Reason: Sandbox blocks network","options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"content":{"text":"```json\n{\n \"host\": \"example.com\"\n}\n```","type":"text"},"type":"content"}],"kind":"other","name":"SandboxNetworkAccess","rawInput":{"host":"example.com"},"status":"pending","title":"example.com","toolCallId":"toolu_net"},"type":"tool_call"},"title":"example.com"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"description":"Reason: Sandbox blocks network","options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"content":{"text":"```json\n{\n \"host\": \"example.com\"\n}\n```","type":"text"},"type":"content"}],"kind":"other","name":"SandboxNetworkAccess","rawInput":{"host":"example.com"},"title":"example.com","toolCallId":"toolu_net"},"type":"tool_call"},"title":"example.com"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_net"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"other","name":"NotebookEdit","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"NotebookEdit","toolCallId":"toolu_nb"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"notebook_path":"<cwd>/n.ipynb"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_nb"}}}
@@ -10,9 +10,9 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"cell_id":"cell-1","new_source":"print('hi')","notebook_path":"<cwd>/n.ipynb"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_nb"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"cell_id":"cell-1","edit_mode":"replace","new_source":"print('hi')","notebook_path":"<cwd>/n.ipynb"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_nb"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[],"kind":"other","name":"NotebookEdit","rawInput":{"cell_id":"cell-1","edit_mode":"replace","new_source":"print('hi')","notebook_path":"<cwd>/n.ipynb"},"status":"pending","title":"NotebookEdit","toolCallId":"toolu_nb"},"type":"tool_call"},"title":"NotebookEdit"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[],"kind":"other","name":"NotebookEdit","rawInput":{"cell_id":"cell-1","edit_mode":"replace","new_source":"print('hi')","notebook_path":"<cwd>/n.ipynb"},"title":"NotebookEdit","toolCallId":"toolu_nb"},"type":"tool_call"},"title":"NotebookEdit"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"Updated cell cell-1 with print('hi')","type":"text"},"type":"content"}],"rawOutput":"Updated cell cell-1 with print('hi')","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_nb"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"terminal_update","terminalId":"toolu_denied"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"terminalId":"toolu_denied","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"toolu_denied"}}}
@@ -14,4 +14,4 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"exitStatus":{},"sessionUpdate":"terminal_update","terminalId":"toolu_denied"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"terminalId":"toolu_denied","type":"terminal"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_denied"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,12 +1,12 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"Work","type":"text"},"messageId":"msg_rate","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"ing.","type":"text"},"messageId":"msg_rate","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6","_claude/rateLimit":{"resetsAt":1700000000,"status":"allowed_warning","utilization":0.9}},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6","_claude/origin":{"kind":"human"}},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,12 +1,12 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"read","locations":[],"name":"Read","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Read File","toolCallId":"toolu_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"locations":[{"line":1,"path":"<cwd>/a.ts"}],"rawInput":{"file_path":"<cwd>/a.ts"},"sessionUpdate":"tool_call_update","title":"Read a.ts","toolCallId":"toolu_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"```\n1\tconst a = 1;\n```","type":"text"},"type":"content"}],"rawOutput":"1\tconst a = 1;\n<system-reminder>x</system-reminder>","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -6,9 +6,9 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"messageId":"msg_r1","sessionUpdate":"agent_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"I will edit it.","type":"text"},"messageId":"msg_r1","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"command":"ls","sessionUpdate":"terminal_update","terminalId":"toolu_r_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"terminalId":"toolu_r_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{"command":"ls","description":"List"},"sessionUpdate":"tool_call_update","status":"pending","title":"ls","toolCallId":"toolu_r_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"edit","locations":[{"path":"<cwd>/src/app.ts"}],"name":"Edit","rawInput":{"file_path":"<cwd>/src/app.ts","new_string":"b","old_string":"a"},"sessionUpdate":"tool_call_update","status":"pending","title":"Edit src/app.ts","toolCallId":"toolu_r_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"Look around","type":"text"},"type":"content"}],"kind":"think","name":"Task","rawInput":{"description":"Explore","prompt":"Look around"},"sessionUpdate":"tool_call_update","status":"pending","title":"Explore","toolCallId":"toolu_r_task"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":null,"content":[{"terminalId":"toolu_r_bash","type":"terminal"}],"kind":"execute","locations":null,"name":"Bash","rawInput":{"command":"ls","description":"List"},"rawOutput":null,"sessionUpdate":"tool_call_update","status":"pending","title":"ls","toolCallId":"toolu_r_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":null,"content":[],"kind":"edit","locations":[{"path":"<cwd>/src/app.ts"}],"name":"Edit","rawInput":{"file_path":"<cwd>/src/app.ts","new_string":"b","old_string":"a"},"rawOutput":null,"sessionUpdate":"tool_call_update","status":"pending","title":"Edit src/app.ts","toolCallId":"toolu_r_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":null,"content":[{"content":{"text":"Look around","type":"text"},"type":"content"}],"kind":"think","locations":null,"name":"Task","rawInput":{"description":"Explore","prompt":"Look around"},"rawOutput":null,"sessionUpdate":"tool_call_update","status":"pending","title":"Explore","toolCallId":"toolu_r_task"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"output":{"data":"YXBwLnRz"},"sessionUpdate":"terminal_update","terminalId":"toolu_r_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"exitStatus":{"exitCode":0},"sessionUpdate":"terminal_update","terminalId":"toolu_r_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"terminalId":"toolu_r_bash","type":"terminal"}],"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_r_bash"}}}
@@ -17,5 +17,6 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"messageId":"msg_r2","sessionUpdate":"agent_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"Fixed.","type":"text"},"messageId":"msg_r2","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"description":"The history of 1 subagent of this session could not be restored.","sessionUpdate":"notice","severity":"info","title":"Some subagent history is not available"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"idle"}}}
{"kind":"response","method":"session/resume","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}]}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"session_info_update","title":"Fix the app","updatedAt":"2026-01-01T00:00:00.000Z"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"idle"}}}
{"kind":"response","method":"session/resume","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}]}}

View File

@@ -1,2 +1,2 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}

View File

@@ -1,12 +1,12 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"other","name":"Skill","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Load skill","toolCallId":"toolu_skill"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"skill":"commits"},"sessionUpdate":"tool_call_update","title":"Load skill: commits","toolCallId":"toolu_skill"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawOutput":"Launching skill: commits","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_skill"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,17 +1,18 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"Background review","sessionId":"a2","sessionUpdate":"subagent_update","state":{"state":"running"},"title":"general-purpose"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"a2","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"general-purpose","sessionId":"a2","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"},"title":"Background review"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"a2","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"Review the diff","type":"text"}],"messageId":"toolu_agent","recipientMessageId":"toolu_agent","recipientSessionId":"a2","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"a2","update":{"content":[{"text":"Review the diff","type":"text"}],"messageId":"toolu_agent","recipientSessionId":"a2","senderMessageId":"toolu_agent","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"a2","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_agent"}},"configOptions":[{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"a2","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_agent"}},"content":{"text":"Reviewi","type":"text"},"messageId":"msg_async_sub","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"a2","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_agent"}},"content":{"text":"ng now.","type":"text"},"messageId":"msg_async_sub","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"a2","sessionUpdate":"subagent_update","state":{"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"a2","update":{"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"a2","sessionUpdate":"subagent_update","state":{"background":[],"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"a2","update":{"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,20 +1,21 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"Check the logs","sessionId":"agent_late","sessionUpdate":"subagent_update","state":{"state":"running"},"title":"general-purpose"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"general-purpose","sessionId":"agent_late","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"},"title":"Check the logs"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"Check logs","type":"text"}],"messageId":"toolu_late","recipientMessageId":"toolu_late","recipientSessionId":"agent_late","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"content":[{"text":"Check logs","type":"text"}],"messageId":"toolu_late","recipientSessionId":"agent_late","senderMessageId":"toolu_late","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_late"}},"configOptions":[{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_late"}},"content":[],"kind":"read","locations":[],"name":"Read","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Read File","toolCallId":"toolu_late_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"locations":[{"line":1,"path":"/var/log/app.log"}],"rawInput":{"file_path":"/var/log/app.log"},"sessionUpdate":"tool_call_update","title":"Read /var/log/app.log","toolCallId":"toolu_late_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"The logs are clean.","type":"text"}],"messageId":"toolu_late:reply","recipientSessionId":"<session>","senderSessionId":"agent_late","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_late","sessionUpdate":"subagent_update","state":{"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_late","sessionUpdate":"subagent_update","state":{"background":[],"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_late","toolResponse":{"elapsedTimeSeconds":4}}},"sessionUpdate":"tool_call_update","status":"in_progress","toolCallId":"toolu_late_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_late","update":{"content":[{"content":{"text":"```\n1\tok\n```","type":"text"},"type":"content"}],"rawOutput":"1\tok","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_late_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,14 +1,15 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"Fix the build","sessionId":"agent_n","sessionUpdate":"subagent_update","state":{"state":"running"},"title":"general-purpose"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"general-purpose","sessionId":"agent_n","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"},"title":"Fix the build"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"Make the build pass","type":"text"}],"messageId":"toolu_native","recipientMessageId":"toolu_native","recipientSessionId":"agent_n","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"content":[{"text":"Make the build pass","type":"text"}],"messageId":"toolu_native","recipientSessionId":"agent_n","senderMessageId":"toolu_native","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"configOptions":[{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"content":{"text":"Running t","type":"text"},"messageId":"msg_native_sub_1","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"content":{"text":"he build.","type":"text"},"messageId":"msg_native_sub_1","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"content":[],"kind":"search","name":"Grep","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"grep","toolCallId":"toolu_native_grep"}}}
@@ -20,17 +21,17 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"command":"make","sessionUpdate":"terminal_update","terminalId":"toolu_native_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"rawInput":{"command":"make"},"sessionUpdate":"tool_call_update","title":"make","toolCallId":"toolu_native_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"rawInput":{"command":"make","description":"Build"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_native_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_n","sessionUpdate":"subagent_update","state":{"state":"requires_action"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"agent_n","subject":{"toolCall":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"content":[{"terminalId":"toolu_native_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{"command":"make","description":"Build"},"status":"pending","title":"make","toolCallId":"toolu_native_bash"},"type":"tool_call"},"title":"make"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_n","sessionUpdate":"subagent_update","state":{"state":"running"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_n","sessionUpdate":"subagent_update","state":{"background":[],"state":"requires_action"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"agent_n","subject":{"toolCall":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"content":[{"terminalId":"toolu_native_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{"command":"make","description":"Build"},"title":"make","toolCallId":"toolu_native_bash"},"type":"tool_call"},"title":"make"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_n","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"output":{"data":"bWFrZTogKioqIEVycm9yIDI="},"sessionUpdate":"terminal_update","terminalId":"toolu_native_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"exitStatus":{},"sessionUpdate":"terminal_update","terminalId":"toolu_native_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"sessionUpdate":"tool_call_update","status":"failed","toolCallId":"toolu_native_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_n","sessionUpdate":"subagent_update","state":{"state":"idle","stopReason":"error"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"sessionUpdate":"state_update","state":"idle","stopReason":"error"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_n","sessionUpdate":"subagent_update","state":{"background":[],"state":"idle","stopReason":"error"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"error"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,28 +1,30 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"Plan the work","sessionId":"agent_outer","sessionUpdate":"subagent_update","state":{"state":"running"},"title":"Plan"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"Plan","sessionId":"agent_outer","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"},"title":"Plan the work"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"Plan it","type":"text"}],"messageId":"toolu_outer","recipientMessageId":"toolu_outer","recipientSessionId":"agent_outer","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"content":[{"text":"Plan it","type":"text"}],"messageId":"toolu_outer","recipientSessionId":"agent_outer","senderMessageId":"toolu_outer","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"capabilities":{"cancel":{}},"description":"Read the spec","sessionId":"agent_inner","sessionUpdate":"subagent_update","state":{"state":"running"},"title":"Explore"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_inner","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_outer"}},"configOptions":[{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"capabilities":{"cancel":{}},"description":"Explore","sessionId":"agent_inner","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"},"title":"Read the spec"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_inner","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"content":[{"text":"Read spec.md","type":"text"}],"messageId":"toolu_inner","recipientMessageId":"toolu_inner","recipientSessionId":"agent_inner","senderSessionId":"agent_outer","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_inner","update":{"content":[{"text":"Read spec.md","type":"text"}],"messageId":"toolu_inner","recipientSessionId":"agent_inner","senderMessageId":"toolu_inner","senderSessionId":"agent_outer","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_inner","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_inner"}},"configOptions":[{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_inner","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_inner"}},"content":[],"kind":"read","locations":[],"name":"Read","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Read File","toolCallId":"toolu_grandchild_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_inner","update":{"locations":[{"line":1,"path":"/spec.md"}],"rawInput":{"file_path":"/spec.md"},"sessionUpdate":"tool_call_update","title":"Read /spec.md","toolCallId":"toolu_grandchild_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_inner","update":{"content":[{"content":{"text":"```\n1\tspec\n```","type":"text"},"type":"content"}],"rawOutput":"1\tspec","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_grandchild_read"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"content":[{"text":"The spec says X.","type":"text"}],"messageId":"toolu_inner:reply","recipientSessionId":"agent_outer","senderSessionId":"agent_inner","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"sessionId":"agent_inner","sessionUpdate":"subagent_update","state":{"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_inner","update":{"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"sessionId":"agent_inner","sessionUpdate":"subagent_update","state":{"background":[],"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_inner","update":{"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_outer"}},"content":{"text":"Plan:","type":"text"},"messageId":"msg_outer_text","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_outer"}},"content":{"text":" do X.","type":"text"},"messageId":"msg_outer_text","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"Plan: do X.","type":"text"}],"messageId":"toolu_outer:reply","recipientSessionId":"<session>","senderSessionId":"agent_outer","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_outer","sessionUpdate":"subagent_update","state":{"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_outer","sessionUpdate":"subagent_update","state":{"background":[],"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_outer","update":{"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,14 +1,15 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"Explore code","sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"state":"running"},"title":"Explore"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"Explore","sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"},"title":"Explore code"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"Find the parser","type":"text"}],"messageId":"toolu_task","recipientMessageId":"toolu_task","recipientSessionId":"agent_1","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"content":[{"text":"Find the parser","type":"text"}],"messageId":"toolu_task","recipientSessionId":"agent_1","senderMessageId":"toolu_task","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"configOptions":[{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":{"text":"Looking for","type":"text"},"messageId":"msg_sub_1","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":{"text":" the parser.","type":"text"},"messageId":"msg_sub_1","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":[],"kind":"read","locations":[],"name":"Read","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Read File","toolCallId":"toolu_sub_read"}}}
@@ -19,13 +20,13 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"command":"rg parser","sessionUpdate":"terminal_update","terminalId":"toolu_sub_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"rawInput":{"command":"rg parser"},"sessionUpdate":"tool_call_update","title":"rg parser","toolCallId":"toolu_sub_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"rawInput":{"command":"rg parser","description":"Search"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_sub_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"state":"requires_action"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"agent_1","subject":{"toolCall":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":[{"terminalId":"toolu_sub_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{"command":"rg parser","description":"Search"},"status":"pending","title":"rg parser","toolCallId":"toolu_sub_bash"},"type":"tool_call"},"title":"rg parser"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"state":"running"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"background":[],"state":"requires_action"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"agent_1","subject":{"toolCall":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":[{"terminalId":"toolu_sub_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{"command":"rg parser","description":"Search"},"title":"rg parser","toolCallId":"toolu_sub_bash"},"type":"tool_call"},"title":"rg parser"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"output":{"data":"eC50cw=="},"sessionUpdate":"terminal_update","terminalId":"toolu_sub_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"exitStatus":{"exitCode":0},"sessionUpdate":"terminal_update","terminalId":"toolu_sub_bash"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_sub_bash"}}}
@@ -33,19 +34,19 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"locations":[{"path":"<cwd>/x.ts"}],"rawInput":{"file_path":"<cwd>/x.ts"},"sessionUpdate":"tool_call_update","title":"Edit x.ts","toolCallId":"toolu_sub_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"rawInput":{"file_path":"<cwd>/x.ts","old_string":"a"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_sub_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"rawInput":{"file_path":"<cwd>/x.ts","new_string":"b","old_string":"a"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_sub_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"state":"requires_action"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"agent_1","subject":{"toolCall":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":[],"kind":"edit","locations":[{"path":"<cwd>/x.ts"}],"name":"Edit","rawInput":{"file_path":"<cwd>/x.ts","new_string":"b","old_string":"a"},"status":"pending","title":"Edit x.ts","toolCallId":"toolu_sub_edit"},"type":"tool_call"},"title":"Edit x.ts"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"state":"running"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"background":[],"state":"requires_action"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"agent_1","subject":{"toolCall":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":[],"kind":"edit","locations":[{"path":"<cwd>/x.ts"}],"name":"Edit","rawInput":{"file_path":"<cwd>/x.ts","new_string":"b","old_string":"a"},"title":"Edit x.ts","toolCallId":"toolu_sub_edit"},"type":"tool_call"},"title":"Edit x.ts"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"rawOutput":"The file has been updated.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_sub_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"content":[{"changes":[{"fileType":"text","operation":"modify","path":"<cwd>/x.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/x.ts <cwd>/x.ts\n--- <cwd>/x.ts\n+++ <cwd>/x.ts\n@@ -1 +1 @@\n-a\n+b\n"},"type":"diff"}],"locations":[{"line":1,"path":"<cwd>/x.ts"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_sub_edit"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":{"text":"Foun","type":"text"},"messageId":"msg_sub_2","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":{"text":"d it.","type":"text"},"messageId":"msg_sub_2","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"The parser is in x.ts.","type":"text"}],"messageId":"toolu_task:reply","recipientSessionId":"<session>","senderSessionId":"agent_1","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_1","sessionUpdate":"subagent_update","state":{"background":[],"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,14 +1,15 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"Summarize","sessionId":"agent_t","sessionUpdate":"subagent_update","state":{"state":"running"},"title":"Explore"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"capabilities":{"cancel":{}},"description":"Explore","sessionId":"agent_t","sessionUpdate":"subagent_update","state":{"background":[],"state":"running"},"title":"Summarize"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"Summarize the repo","type":"text"}],"messageId":"toolu_transcript","recipientMessageId":"toolu_transcript","recipientSessionId":"agent_t","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"content":[{"text":"Summarize the repo","type":"text"}],"messageId":"toolu_transcript","recipientSessionId":"agent_t","senderMessageId":"toolu_transcript","senderSessionId":"<session>","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_transcript"}},"configOptions":[{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","name":"Model","options":[{"name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionUpdate":"config_option_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_transcript"}},"content":{"text":"Look at t","type":"text"},"messageId":"msg_transcript_sub","sessionUpdate":"agent_thought_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_transcript"}},"content":{"text":"he layout.","type":"text"},"messageId":"msg_transcript_sub","sessionUpdate":"agent_thought_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_transcript"}},"content":{"text":"The repo has ","type":"text"},"messageId":"msg_transcript_sub","sessionUpdate":"agent_message_chunk"}}}
@@ -17,7 +18,7 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"rawInput":{"pattern":"*"},"sessionUpdate":"tool_call_update","title":"Find `*`","toolCallId":"toolu_transcript_glob"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"content":[{"content":{"text":"a\nb","type":"text"},"type":"content"}],"rawOutput":"a\nb","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_transcript_glob"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"Two packages.","type":"text"}],"messageId":"toolu_transcript:reply","recipientSessionId":"<session>","senderSessionId":"agent_t","sessionUpdate":"session_message"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_t","sessionUpdate":"subagent_update","state":{"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionId":"agent_t","sessionUpdate":"subagent_update","state":{"background":[],"state":"idle","stopReason":"end_turn"}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_t","update":{"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"plan":{"entries":[{"content":"Write tests","priority":"medium","status":"pending"}],"planId":"plan","type":"items"},"sessionUpdate":"plan_update"}}}
@@ -13,13 +13,13 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"plan":{"entries":[{"content":"Write tests","priority":"medium","status":"completed"}],"planId":"plan","type":"items"},"sessionUpdate":"plan_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"plan":{"entries":[{"content":"Write tests","priority":"medium","status":"completed"}],"planId":"plan","type":"items"},"sessionUpdate":"plan_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 2","type":"text"}],"messageId":"<id-2>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-2>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"All ","type":"text"},"messageId":"msg_follow","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"done.","type":"text"},"messageId":"msg_follow","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"other","name":"TaskOutput","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"TaskOutput","toolCallId":"toolu_out"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"task_id":"bash_1"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_out"}}}
@@ -14,4 +14,4 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"task_id":"bash_1"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_stop"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"Successfully stopped task: bash_1","type":"text"},"type":"content"}],"rawOutput":"Successfully stopped task: bash_1","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_stop"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"Let me thin","type":"text"},"messageId":"msg_text","sessionUpdate":"agent_thought_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"k about it.","type":"text"},"messageId":"msg_text","sessionUpdate":"agent_thought_chunk"}}}
@@ -10,4 +10,4 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"he answer.","type":"text"},"messageId":"msg_text","sessionUpdate":"agent_message_chunk"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,10 +1,10 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"plan":{"entries":[{"content":"Writing tests","priority":"medium","status":"in_progress"},{"content":"Ship","priority":"medium","status":"pending"}],"planId":"plan","type":"items"},"sessionUpdate":"plan_update"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"terminal_update","terminalId":"toolu_slow"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"terminalId":"toolu_slow","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"toolu_slow"}}}
@@ -16,4 +16,4 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"exitStatus":{"exitCode":0},"sessionUpdate":"terminal_update","terminalId":"toolu_slow"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_slow"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,20 +1,20 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"fetch","name":"WebFetch","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Fetch","toolCallId":"toolu_fetch"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"url":"https://example.com"},"sessionUpdate":"tool_call_update","title":"Fetch https://example.com","toolCallId":"toolu_fetch"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"Summarize the page","type":"text"},"type":"content"}],"rawInput":{"prompt":"Summarize the page","url":"https://example.com"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_fetch"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"allow_always","name":"Yes, and don't ask again for example.com","optionId":"allow-with-updates"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"content":{"text":"Summarize the page","type":"text"},"type":"content"}],"kind":"fetch","name":"WebFetch","rawInput":{"prompt":"Summarize the page","url":"https://example.com"},"status":"pending","title":"Fetch https://example.com","toolCallId":"toolu_fetch"},"type":"tool_call"},"title":"Fetch https://example.com"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"allow_always","name":"Yes, and don't ask again for example.com","optionId":"allow-with-updates"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"content":{"text":"Summarize the page","type":"text"},"type":"content"}],"kind":"fetch","name":"WebFetch","rawInput":{"prompt":"Summarize the page","url":"https://example.com"},"title":"Fetch https://example.com","toolCallId":"toolu_fetch"},"type":"tool_call"},"title":"Fetch https://example.com"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"The page is an example.","type":"text"},"type":"content"}],"rawOutput":"The page is an example.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_fetch"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"fetch","name":"WebSearch","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Web search","toolCallId":"toolu_search"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"query":"acp protocol"},"sessionUpdate":"tool_call_update","title":"Search \"acp protocol\"","toolCallId":"toolu_search"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"ACP (https://agentclientprotocol.com)\nA summary.","type":"text"},"type":"content"}],"rawOutput":"Web search results for query: \"acp protocol\"\n\nLinks: [...]","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_search"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,8 +1,8 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"edit","locations":[],"name":"Write","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Preparing file…","toolCallId":"toolu_write"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"locations":[{"path":"<cwd>/old.ts"}],"rawInput":{"file_path":"<cwd>/old.ts"},"sessionUpdate":"tool_call_update","title":"Write old.ts","toolCallId":"toolu_write"}}}
@@ -12,4 +12,4 @@
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawOutput":"The file <cwd>/old.ts has been updated successfully.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_write"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"locations":[{"line":1,"path":"<cwd>/old.ts"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_write"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -1,17 +1,17 @@
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}},"systemPrompt":{"mode":{"override":{}}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":2}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"response","method":"session/new","payload":{"availableCommands":[{"description":"Show the MCP servers and their status, or reconnect, enable, or disable a server","input":{"hint":"[reconnect|enable|disable [<server>|all]]","type":"text"},"name":"mcp","type":"command"}],"configOptions":[{"category":"mode","configId":"mode","currentValue":"default","description":"Session permission mode","name":"Mode","options":[{"description":"Always ask before making changes","name":"Manual","value":"default"},{"description":"Automatically accept all file edits","name":"Accept edits","value":"acceptEdits"},{"description":"Create a plan before making changes","name":"Plan","value":"plan"},{"description":"Claude handles permission decisions","name":"Auto","value":"auto"},{"description":"Accepts all permissions","name":"Bypass permissions","value":"bypassPermissions"}],"type":"select"},{"category":"model","configId":"model","currentValue":"claude-sonnet-4-6","description":"AI model to use","name":"Model","options":[{"description":"Fast","name":"Claude Sonnet 4.6","value":"claude-sonnet-4-6"}],"type":"select"}],"sessionId":"<session>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"edit","locations":[],"name":"Write","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Preparing file…","toolCallId":"toolu_write"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"locations":[{"path":"<cwd>/new.ts"}],"rawInput":{"file_path":"<cwd>/new.ts"},"sessionUpdate":"tool_call_update","title":"Write new.ts","toolCallId":"toolu_write"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"content":"export const x = 1;\n","file_path":"<cwd>/new.ts"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_write"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"changes":[{"fileType":"text","operation":"add","path":"<cwd>/new.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/new.ts <cwd>/new.ts\nnew file mode 100644\n--- /dev/null\n+++ <cwd>/new.ts\n@@ -0,0 +1 @@\n+export const x = 1;\n"},"type":"diff"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_write"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"changes":[{"fileType":"text","operation":"add","path":"<cwd>/new.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/new.ts <cwd>/new.ts\nnew file mode 100644\n--- /dev/null\n+++ <cwd>/new.ts\n@@ -0,0 +1 @@\n+export const x = 1;\n"},"type":"diff"}],"kind":"edit","locations":[{"path":"<cwd>/new.ts"}],"name":"Write","rawInput":{"content":"export const x = 1;\n","file_path":"<cwd>/new.ts"},"status":"pending","title":"Write new.ts","toolCallId":"toolu_write"},"type":"tool_call"},"title":"Write new.ts"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"requires_action"}}}
{"kind":"request","method":"session/request_permission","payload":{"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","subject":{"toolCall":{"content":[{"changes":[{"fileType":"text","operation":"add","path":"<cwd>/new.ts"}],"patch":{"format":"git_patch","text":"diff --git <cwd>/new.ts <cwd>/new.ts\nnew file mode 100644\n--- /dev/null\n+++ <cwd>/new.ts\n@@ -0,0 +1 @@\n+export const x = 1;\n"},"type":"diff"}],"kind":"edit","locations":[{"path":"<cwd>/new.ts"}],"name":"Write","rawInput":{"content":"export const x = 1;\n","file_path":"<cwd>/new.ts"},"title":"Write new.ts","toolCallId":"toolu_write"},"type":"tool_call"},"title":"Write new.ts"}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"background":[],"sessionUpdate":"state_update","state":"running"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawOutput":"File created successfully at: <cwd>/new.ts","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_write"}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"background":[],"sessionUpdate":"state_update","state":"idle","stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}}

View File

@@ -25,6 +25,10 @@
* - The `initialize` response also advertises
* `agentCapabilities.sessionCapabilities.systemPrompt`, which origin/main
* did not: the agent now applies the `systemPrompt` of session requests.
* - A SandboxNetworkAccess tool call is settled by a `tool_call_update` with
* only its final `status` once its permission request is answered. The
* callback runs nothing, so origin/main never settled the call (see
* {@link withoutSandboxNetworkSettlements}).
* - A `terminal_exit` has a `null` exit code where origin/main sent a code
* that the tool result does not say (see `CommandOutput.exitCode`): 1 for a
* failure that names no code or an interrupted command, 0 for a
@@ -126,6 +130,23 @@ function flatten(update: Json): Map<string, string> {
return flat;
}
/** `traffic` without the updates that settle a SandboxNetworkAccess tool call. */
function withoutSandboxNetworkSettlements(traffic: Recorded[]): Recorded[] {
const sandboxCalls = new Set<unknown>();
return traffic.filter((record) => {
const update = updateOf(record);
if (update?.sessionUpdate === "tool_call" && update.name === "SandboxNetworkAccess") {
sandboxCalls.add(update.toolCallId);
}
const settles =
update?.sessionUpdate === "tool_call_update" &&
sandboxCalls.has(update.toolCallId) &&
Object.keys(update).sort().join() === "sessionUpdate,status,toolCallId" &&
(update.status === "completed" || update.status === "failed");
return !settles;
});
}
/** A copy of `value` without `key`. */
function without(value: Json, key: string): Json {
const copy = { ...value };
@@ -232,7 +253,9 @@ export function compareWithBaseline(baseline: Recorded[], traffic: Recorded[]):
const update = updateOf(record);
return !(update?.sessionUpdate === "session_info_update" && Object.keys(update).length === 1);
});
const current = traffic.map((record) => withoutSessionCapability(record, "systemPrompt"));
const current = withoutSandboxNetworkSettlements(
traffic.map((record) => withoutSessionCapability(record, "systemPrompt")),
);
const violations: string[] = [];
const state = new Map<string, Map<string, string>>();
const summaryChunks = new Map<string, string>();

View File

@@ -284,6 +284,16 @@ export async function mockedSessionMessages(): Promise<Record<string, unknown>[]
return activeScript?.transcript ?? [];
}
/** The `getSessionInfo` of the SDK mock: a stored title and modification
* time for a scenario that replays a transcript, and none for a new
* session, whose transcript the mock never writes. */
export async function mockedSessionInfo(
sessionId: string,
): Promise<{ sessionId: string; summary: string; lastModified: number } | undefined> {
if (!activeScript?.transcript.length) return undefined;
return { sessionId, summary: "Fix the app", lastModified: Date.UTC(2026, 0, 1) };
}
type AgentClass = new (client: any, logger?: any) => any;
/** The outbound messages of one scenario run. */
@@ -490,21 +500,17 @@ export async function runScenarioV2(
const idles: unknown[] = [];
const failures: string[] = [];
const each = (message: v2.AnyWireMessage) =>
(Array.isArray(message) ? message : [message]) as Record<string, any>[];
const toAgent = new TransformStream<v2.AnyWireMessage, v2.AnyWireMessage>({
const toAgent = new TransformStream<v2.AnyMessage, v2.AnyMessage>({
transform(message, controller) {
for (const m of each(message)) {
if ("method" in m && "id" in m) methodsOfClientRequests.set(m.id, m.method);
if ("method" in message && "id" in message && message.id !== null) {
methodsOfClientRequests.set(message.id, message.method);
}
controller.enqueue(message);
},
});
const toClient = new TransformStream<v2.AnyWireMessage, v2.AnyWireMessage>({
const toClient = new TransformStream<v2.AnyMessage, v2.AnyMessage>({
transform(message, controller) {
for (const m of each(message)) {
if (recording) raw.push(wireRecord(m, methodsOfClientRequests));
}
if (recording) raw.push(wireRecord(message, methodsOfClientRequests));
controller.enqueue(message);
},
});

File diff suppressed because it is too large Load Diff

View File

@@ -1221,10 +1221,28 @@ describe("AsyncTaskRuntime", () => {
return { runtime, published };
};
it("never announces a task that a Monitor tool call started", async () => {
const updateKinds = (published: AcpSessionNotification[]) =>
published.map(({ update }) => update.sessionUpdate);
const MONITOR_SPAWN = {
sessionUpdate: "async_task_spawned",
taskType: "monitor",
showInTranscript: false,
canStop: false,
};
/**
* Scenario:
* 1. The SDK starts a `local_bash` task for a Monitor tool call.
* 2. The task reports progress, then its notification completes it.
* 3. The client asks to stop the task.
* Expected:
* - The spawn is a monitor with no transcript card and no Stop.
* - Progress and the completed state follow.
* - `canStop` and `claimStop` are false.
*/
it("publishes a task that a Monitor tool call started as a monitor", async () => {
const { runtime, published } = monitorRuntime();
// The SDK reports a Monitor task as a background shell.
await runtime.taskStarted({
task_id: "monitor",
task_type: "local_bash",
@@ -1233,22 +1251,38 @@ describe("AsyncTaskRuntime", () => {
tool_use_id: "monitor-tool",
});
await runtime.taskProgress({ task_id: "monitor", summary: "First line" });
await runtime.taskUpdated("monitor", { status: "paused" });
await runtime.backgroundTasksChanged([{ task_id: "monitor", task_type: "local_bash" }]);
expect(runtime.canStop("monitor")).toBe(false);
expect(runtime.claimStop("monitor")).toBe(false);
await runtime.taskStopped("monitor");
const stoppable = runtime.canStop("monitor");
const claimed = runtime.claimStop("monitor");
await runtime.taskNotification({
task_id: "monitor",
status: "completed",
tool_use_id: "monitor-tool",
});
await runtime.finishAll("stopped");
expect(published).toEqual([]);
expect(published[0]?.update).toMatchObject({
...MONITOR_SPAWN,
asyncTaskId: "monitor",
toolCallId: "monitor-tool",
});
expect(updateKinds(published)).toEqual([
"async_task_spawned",
"async_task_progress",
"async_task_state_update",
]);
expect(published[2]?.update).toMatchObject({ state: "completed" });
expect(stoppable).toBe(false);
expect(claimed).toBe(false);
});
it("never announces a held task whose tool call id names a Monitor", async () => {
/**
* Scenario:
* 1. A task starts without a tool call id and is held.
* 2. A later progress carries the Monitor tool call id.
* Expected:
* - The task is spawned as a monitor once the id arrives, not before.
* - It is not stoppable.
*/
it("spawns a held task as a monitor when its tool call id names a Monitor", async () => {
const { runtime, published } = monitorRuntime();
await runtime.taskStarted({
@@ -1257,18 +1291,26 @@ describe("AsyncTaskRuntime", () => {
description: "Watch the logs",
});
await runtime.taskProgress({ task_id: "monitor", summary: "First line" });
const spawnedBeforeId = published.length;
await runtime.taskProgress({
task_id: "monitor",
summary: "Second line",
tool_use_id: "monitor-tool",
});
await runtime.releaseHeld();
await runtime.taskNotification({ task_id: "monitor", status: "stopped" });
expect(published).toEqual([]);
expect(spawnedBeforeId).toBe(0);
expect(published[0]?.update).toMatchObject({ ...MONITOR_SPAWN, toolCallId: "monitor-tool" });
expect(runtime.canStop("monitor")).toBe(false);
});
it("never announces a held task that a Monitor ends", async () => {
/**
* Scenario:
* 1. A task starts without a tool call id and is held.
* 2. A notification with the Monitor tool call id completes it.
* Expected:
* - The task is spawned as a monitor, then reaches the completed state.
*/
it("spawns a held task that a Monitor ends as a monitor", async () => {
const { runtime, published } = monitorRuntime();
await runtime.taskStarted({ task_id: "monitor", task_type: "local_workflow" });
@@ -1278,23 +1320,47 @@ describe("AsyncTaskRuntime", () => {
tool_use_id: "monitor-tool",
});
expect(published).toEqual([]);
expect(updateKinds(published)).toEqual(["async_task_spawned", "async_task_state_update"]);
expect(published[0]?.update).toMatchObject(MONITOR_SPAWN);
expect(published[1]?.update).toMatchObject({ state: "completed" });
});
it("ignores a local_monitor task without a tool call", async () => {
/**
* Scenario:
* 1. The SDK starts a `local_monitor` task for a Bash tool call.
* 2. The SDK reports another `local_monitor` task only in the task level.
* 3. The turn ends (`releaseHeld`), which spawns the level-only task without a tool call.
* Expected:
* - Both tasks are spawned as monitors and neither is stoppable.
*/
it("spawns a local_monitor task as a monitor", async () => {
const { runtime, published } = monitorRuntime();
await runtime.backgroundTasksChanged([{ task_id: "level", task_type: "local_monitor" }]);
await runtime.taskStarted({ task_id: "level", task_type: "local_monitor" });
await runtime.taskStarted({ task_id: "started", task_type: "local_monitor" });
await runtime.taskProgress({ task_id: "started", summary: "line" });
await runtime.taskStarted({
task_id: "started",
task_type: "local_monitor",
tool_use_id: "bash-tool",
});
await runtime.backgroundTasksChanged([
{ task_id: "started", task_type: "local_monitor" },
{ task_id: "level", task_type: "local_monitor" },
]);
await runtime.releaseHeld();
await runtime.taskNotification({ task_id: "started", status: "completed" });
expect(published).toEqual([]);
const spawns = published.map(({ update }) => update);
expect(spawns).toHaveLength(2);
expect(spawns[0]).toMatchObject({ ...MONITOR_SPAWN, asyncTaskId: "started" });
expect(spawns[1]).toMatchObject({ ...MONITOR_SPAWN, asyncTaskId: "level" });
expect(runtime.canStop("started")).toBe(false);
expect(runtime.canStop("level")).toBe(false);
});
/**
* Scenario:
* 1. The SDK starts a background `local_bash` task for a Bash tool call.
* Expected:
* - The spawn is a shell that stays in the transcript and can be stopped.
*/
it("still announces a task of another tool", async () => {
const { runtime, published } = monitorRuntime();
@@ -1307,6 +1373,11 @@ describe("AsyncTaskRuntime", () => {
});
expect(published.map(({ update }) => update.sessionUpdate)).toEqual(["async_task_spawned"]);
expect(published[0]?.update).toMatchObject({
taskType: "shell",
showInTranscript: true,
canStop: true,
});
expect(runtime.canStop("shell")).toBe(true);
});
});

View File

@@ -22,6 +22,11 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async () => ({
/** Shared spy for both `claude auth status --json` and `claude auth logout`.
* Tests set `execFileResults` per invoked subcommand. */
const execFileSpy = vi.hoisted(() => vi.fn());
/** `auth status` probes, wherever global options such as `--settings` put the subcommand. */
function isStatusProbe(args: string[]): boolean {
const auth = args.indexOf("auth");
return auth >= 0 && args[auth + 1] === "status";
}
vi.mock("node:child_process", async () => {
const actual = await vi.importActual<typeof import("node:child_process")>("node:child_process");
@@ -409,7 +414,7 @@ describe("auth status over ACP", () => {
execFileSpy.mockImplementation((...invocation: unknown[]) => {
const args = invocation[1] as string[];
const cb = invocation[invocation.length - 1] as (...a: unknown[]) => void;
if (args[1] === "status") {
if (isStatusProbe(args)) {
const stdout = statusStdout;
const fails = statusFails;
const answer = () => {
@@ -451,7 +456,7 @@ describe("auth status over ACP", () => {
});
function statusCalls() {
return execFileSpy.mock.calls.filter((call) => (call[1] as string[])[1] === "status");
return execFileSpy.mock.calls.filter((call) => isStatusProbe(call[1] as string[]));
}
function updates() {
@@ -542,11 +547,11 @@ describe("auth status over ACP", () => {
env: { ANTHROPIC_BASE_URL: "https://example.invalid/override" },
});
expect(statusCalls().at(-1)?.[1]).toEqual([
"--settings",
JSON.stringify({ apiKeyHelper: "central proxy start" }),
"auth",
"status",
"--json",
"--settings",
JSON.stringify({ apiKeyHelper: "central proxy start" }),
]);
await promptOnce(sessionId);
await flushStatus();

View File

@@ -1076,6 +1076,62 @@ describe("authorization", () => {
expect([...respawned.sessionFailureState.active.values()]).toHaveLength(0);
});
it("restores the user's choices on the respawned query, then a choice sent meanwhile", async () => {
const [agent, updates] = createRecordingAgent(true);
hideClaudeAuth();
const effortModels = ["sonnet", "opus"].map((value) => ({
value,
displayName: value,
description: "",
supportsAutoMode: true,
supportsEffort: true,
supportedEffortLevels: ["low", "medium", "high"],
}));
const scripts = [[[signOutResult()]], [[successResult()]]];
let releaseRespawn!: () => void;
const respawnGate = new Promise<void>((resolve) => (releaseRespawn = resolve));
const queries: any[] = [];
mockQuery.mockImplementation((args: any) => {
const query = scriptedQuery(args, SIGNED_IN_WITH_KEY, scripts[queries.length] ?? []);
const gate = queries.length > 0 ? respawnGate : undefined;
query.initializationResult = async () => {
await gate;
return { models: effortModels, account: SIGNED_IN_WITH_KEY };
};
query.setModel = vi.fn(async () => {});
query.applyFlagSettings = vi.fn(async () => {});
queries.push(query);
return query;
});
const { sessionId } = await agent.newSession(newSessionParams());
await agent.setSessionConfigOption({ sessionId, configId: "model", value: "opus" });
await agent.setSessionConfigOption({ sessionId, configId: "effort", value: "high" });
await expect(agent.prompt(promptParams(sessionId))).rejects.toMatchObject({
code: AUTH_REQUIRED_CODE,
});
const turn = agent.prompt(promptParams(sessionId));
await vi.waitFor(() => expect(queries).toHaveLength(2));
const choice = agent.setSessionConfigOption({
sessionId,
configId: "effort",
value: "low",
});
releaseRespawn();
await expect(turn).resolves.toMatchObject({ stopReason: "end_turn" });
await choice;
expect(queries[1].setModel).toHaveBeenCalledWith("opus");
expect(queries[1].applyFlagSettings.mock.calls).toEqual([
[{ effortLevel: "high" }],
[{ effortLevel: "low" }],
]);
expect(updates).toContainEqual({
sessionId,
update: expect.objectContaining({ sessionUpdate: "config_option_update" }),
});
});
it("refuses with the plain error and no second row when still signed out", async () => {
const [agent, updates] = createRecordingAgent(true);
hideClaudeAuth();

View File

@@ -0,0 +1,129 @@
import { appendFile, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
BACKGROUND_OUTPUT_MAX_BACKLOG_BYTES,
BACKGROUND_OUTPUT_READ_BYTES,
type BackgroundOutput,
BackgroundOutputTail,
} from "../background-output.js";
describe("BackgroundOutputTail", () => {
let dir: string;
let file: string;
let published: BackgroundOutput[];
let tail: BackgroundOutputTail | undefined;
const follow = () =>
(tail = new BackgroundOutputTail(file, async (output) => void published.push(output)));
beforeEach(async () => {
dir = await mkdtemp(path.join(tmpdir(), "background-output-"));
file = path.join(dir, "bg1.output");
published = [];
});
afterEach(async () => {
tail?.stop();
await rm(dir, { recursive: true, force: true });
});
it("replaces the terminal's output with the file's first text, then appends what the file adds", async () => {
await writeFile(file, "building\n");
follow();
await vi.waitFor(() => expect(published).toHaveLength(1), { timeout: 2000 });
await appendFile(file, "done\n");
await vi.waitFor(() => expect(published).toHaveLength(2), { timeout: 2000 });
expect(published).toEqual([
{ text: "building\n", replaces: true },
{ text: "done\n", replaces: false },
]);
});
it("waits for a file that does not exist yet", async () => {
follow();
await new Promise((resolve) => setTimeout(resolve, 700));
expect(published).toEqual([]);
await writeFile(file, "started\n");
await vi.waitFor(() => expect(published).toHaveLength(1), { timeout: 2000 });
expect(published[0]).toEqual({ text: "started\n", replaces: true });
});
it("holds a character that a read splits until its last byte is written", async () => {
const check = Buffer.from("✓", "utf8");
await writeFile(file, check.subarray(0, 1));
follow();
await new Promise((resolve) => setTimeout(resolve, 700));
expect(published).toEqual([]);
await appendFile(file, check.subarray(1));
await tail!.finish();
expect(published).toEqual([{ text: "✓", replaces: true }]);
});
it("reads the rest of the file on finish, and nothing after", async () => {
await writeFile(file, "a");
follow();
await appendFile(file, "b");
await tail!.finish();
await appendFile(file, "c");
await new Promise((resolve) => setTimeout(resolve, 700));
expect(published.map((output) => output.text).join("")).toBe("ab");
});
it("sends at most the backlog, read by read, from the file's last bytes as a snapshot", async () => {
// More than the backlog, written faster than the polls read it.
const skipped = "x".repeat(BACKGROUND_OUTPUT_MAX_BACKLOG_BYTES);
const kept = "y".repeat(BACKGROUND_OUTPUT_MAX_BACKLOG_BYTES - 4) + "end\n";
await writeFile(file, skipped + kept);
follow();
await tail!.finish();
expect(published.map((output) => output.text).join("")).toBe(kept);
expect(published[0].replaces).toBe(true);
expect(published.slice(1).every((output) => !output.replaces)).toBe(true);
expect(
published.every((output) => Buffer.byteLength(output.text) <= BACKGROUND_OUTPUT_READ_BYTES),
).toBe(true);
});
it("starts over with a snapshot when the file shrinks", async () => {
await writeFile(file, "old output\n");
follow();
await vi.waitFor(() => expect(published).toHaveLength(1), { timeout: 2000 });
await writeFile(file, "new\n");
await tail!.finish();
expect(published).toEqual([
{ text: "old output\n", replaces: true },
{ text: "new\n", replaces: true },
]);
});
it("replaces the placeholder with an empty output when the command wrote nothing", async () => {
await writeFile(file, "");
follow();
await tail!.finish();
expect(published).toEqual([{ text: "", replaces: true }]);
});
it("publishes nothing on finish when the file never appeared", async () => {
follow();
await tail!.finish();
expect(published).toEqual([]);
});
it("reads nothing after stop", async () => {
follow();
tail!.stop();
await writeFile(file, "late\n");
await new Promise((resolve) => setTimeout(resolve, 700));
expect(published).toEqual([]);
});
});

View File

@@ -28,9 +28,11 @@ function testSession(overrides: Partial<TestSession> = {}): TestSession {
},
},
models: { currentModelId: "default" },
contextWindowSize: 200_000,
configOptions: [],
currentAgent: "default",
fastModeEnabled: false,
ultracodeEnabled: false,
input: { push: vi.fn() },
...overrides,
};
@@ -49,6 +51,7 @@ function testHost(
return freshSession;
}),
applyFastMode: vi.fn(async () => {}),
applyUltracode: vi.fn(async () => {}),
publishSessionState: vi.fn(async () => {}),
continuationMessage: vi.fn((sessionId, plan, promptUuid) => ({
type: "user" as const,
@@ -252,6 +255,25 @@ describe("continuePlanInFreshContext", () => {
expect(host.applyFastMode).toHaveBeenCalledWith(freshSession, false);
});
it("restores ultracode turned on before clearing context", async () => {
const turn: ClearContextTurn = {
settled: false,
promptUuid: "00000000-0000-4000-8000-000000000000",
};
const oldSession = testSession({ activeTurn: turn, ultracodeEnabled: true });
const freshSession = testSession({ ultracodeEnabled: false });
const host = testHost(oldSession, freshSession);
await continuePlanInFreshContext(
"public-session",
oldSession,
{ toolUseId: "tool-plan", plan: "Ship it", mode: "auto" },
host,
);
expect(host.applyUltracode).toHaveBeenCalledWith(freshSession, true);
});
it("does not publish or continue a restart cancelled while creating the fresh session", async () => {
const turn: ClearContextTurn = {
settled: false,
@@ -336,6 +358,7 @@ describe("ExitPlanCoordinator", () => {
...baseHost,
sessionUpdate: vi.fn(async () => {}),
supportsPlanOperations: () => true,
isAirClient: () => false,
destroyReplacement,
settleCancelledTurn,
settleFailedTurn: vi.fn(),
@@ -381,6 +404,7 @@ describe("ExitPlanCoordinator", () => {
...baseHost,
sessionUpdate: vi.fn(async () => {}),
supportsPlanOperations: () => true,
isAirClient: () => false,
destroyReplacement: vi.fn(),
settleCancelledTurn: vi.fn(),
settleFailedTurn,
@@ -400,9 +424,14 @@ describe("ExitPlanCoordinator", () => {
});
it.each([
["publishes the approved plan after the session state, before the fresh context runs", true],
["publishes no plan to a client without plan operations", false],
])("%s", async (_, supportsPlanOperations) => {
[
"publishes the approved plan after the session state, before the fresh context runs",
true,
false,
],
["links the approved AIR plan after a context reset", true, true],
["publishes no plan to a client without plan operations", false, false],
])("%s", async (_, supportsPlanOperations, airClient) => {
const turn: ClearContextTurn = {
settled: false,
promptUuid: "00000000-0000-4000-8000-000000000000",
@@ -421,6 +450,7 @@ describe("ExitPlanCoordinator", () => {
);
}),
supportsPlanOperations: () => supportsPlanOperations,
isAirClient: () => airClient,
destroyReplacement: vi.fn(),
settleCancelledTurn: vi.fn(),
settleFailedTurn: vi.fn(),
@@ -435,14 +465,32 @@ describe("ExitPlanCoordinator", () => {
expect(order).toEqual([
"current_mode_update",
"config_option_update",
"usage_update",
...(supportsPlanOperations
? [
"plan_update " +
JSON.stringify({ type: "markdown", planId: "plan-mode", content: "Ship it" }),
JSON.stringify({
type: "markdown",
planId: airClient ? "plan-mode:tool-plan" : "plan-mode",
content: "Ship it",
}),
]
: []),
"continuation",
]);
expect(host.sessionUpdate).toHaveBeenCalledWith({
sessionId: "public-session",
update: { sessionUpdate: "usage_update", used: 0, size: 200_000 },
});
if (airClient) {
expect(host.sessionUpdate).toHaveBeenCalledWith({
sessionId: "public-session",
update: {
sessionUpdate: "plan_update",
plan: { type: "markdown", planId: "plan-mode:tool-plan", content: "Ship it" },
},
});
}
});
});

View File

@@ -7,7 +7,10 @@ import {
import type { ClientCapabilities } from "@agentclientprotocol/sdk/unstable";
import { getSessionMessages, type Options } from "@anthropic-ai/claude-agent-sdk";
import type { AcpClient, ClaudeAcpAgent as ClaudeAcpAgentType } from "../acp-agent.js";
import type { AcpSessionNotification } from "../acp-subagents.js";
import { ALLOW_BYPASS } from "../permissions/modes.js";
import { NativeSubagentRuntime } from "../native-subagents.js";
import { startupFailure } from "./helpers.js";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
@@ -185,6 +188,266 @@ describe("createSession options merging", () => {
]);
});
it.each([false, true])("reports child settings through registered hooks (v2: %s)", async (v2) => {
const updates: AcpSessionNotification[] = [];
const client = createMockClient();
client.sessionUpdate = async (notification) => {
updates.push(notification);
};
const observedAgent = new ClaudeAcpAgent(client, undefined, { v2 });
await observedAgent.initialize({ protocolVersion: 1, clientCapabilities: { subagents: {} } });
const { sessionId } = await observedAgent.newSession({ cwd: process.cwd(), mcpServers: [] });
const session = observedAgent.sessions[sessionId]!;
const parentOptions = structuredClone(session.configOptions);
const runtime = (session.nativeSubagentRuntime = new NativeSubagentRuntime(
"rfd",
sessionId,
session,
(notification) => client.sessionUpdate(notification),
{ log: () => {} },
));
const invoke = async (
event: "PreToolUse" | "PostToolUse" | "PostModelSwitch" | "SubagentStart" | "SubagentStop",
fields: Record<string, unknown>,
) => {
const matcher = capturedOptions!.hooks![event]!.find((matcher) => !matcher.matcher);
expect(matcher).toBeDefined();
for (const hook of matcher!.hooks) {
await expect(
hook(
{
hook_event_name: event,
session_id: sessionId,
cwd: process.cwd(),
transcript_path: "/tmp/transcript",
...fields,
} as any,
undefined,
{ signal: new AbortController().signal },
),
).resolves.toMatchObject({ continue: true });
}
};
// A main-thread --agent persona has agent_type too, but no agent_id.
await invoke("PreToolUse", {
agent_type: "reviewer",
permission_mode: "plan",
effort: { level: "max" },
});
expect(updates).toEqual([]);
await invoke("SubagentStart", {
agent_id: "child",
agent_type: "Explore",
permission_mode: "acceptEdits",
});
expect(updates).toEqual([]);
await runtime.taskStarted({ taskId: "child", subagentType: "Explore" }, async () => {});
const configs = () =>
updates.flatMap(({ update }) =>
update.sessionUpdate === "config_option_update" ? [update.configOptions] : [],
);
expect(
configs()
.at(-1)
?.map((option) => [option.id, option.currentValue]),
).toEqual([
["mode", "acceptEdits"],
["agent", "Explore"],
]);
await invoke("PreToolUse", {
agent_id: "child",
agent_type: "Explore",
effort: { level: "high" },
});
expect(
configs()
.at(-1)
?.find((option) => option.id === "effort"),
).toMatchObject({
category: "thought_level",
currentValue: "high",
options: [{ value: "high", name: "High" }],
});
await invoke("PostToolUse", {
agent_id: "child",
tool_name: "EnterPlanMode",
permission_mode: "plan",
});
await invoke("PostModelSwitch", {
agent_id: "child",
to_model: "child-model",
source: "auto",
effort: { level: "low" },
});
await invoke("SubagentStop", { agent_id: "child", agent_type: "Explore" });
expect(
configs()
.at(-1)
?.map((option) => [option.id, option.currentValue]),
).toEqual([
["mode", "plan"],
["model", "child-model"],
["effort", "low"],
["agent", "Explore"],
]);
expect(
configs()
.at(-1)
?.every((option) => option.type === "select" && option.options.length === 1),
).toBe(true);
// A model of the root session's list goes as that list's entry, with its name.
const rootModel = session.configOptions.find((option) => option.id === "model");
const listed =
rootModel?.type === "select"
? (rootModel.options as Array<{ value: string; name: string }>).find(
(entry) => entry.value !== "default",
)
: undefined;
expect(listed).toBeDefined();
await invoke("PostModelSwitch", { agent_id: "child", to_model: listed!.value, source: "auto" });
expect(
configs()
.at(-1)
?.find((option) => option.id === "model"),
).toMatchObject({
currentValue: listed!.value,
options: [{ value: listed!.value, name: listed!.name }],
});
await new Promise((resolve) => setImmediate(resolve));
expect(session.configOptions).toEqual(parentOptions);
expect(
updates
.filter(({ update }) => update.sessionUpdate === "config_option_update")
.every(({ sessionId: target }) => target === "child"),
).toBe(true);
});
it("reports a worktree child's cwd from its SubagentStart hook, and none for a child in the session's cwd", async () => {
const updates: AcpSessionNotification[] = [];
const client = createMockClient();
client.sessionUpdate = async (notification) => {
updates.push(notification);
};
const observedAgent = new ClaudeAcpAgent(client);
await observedAgent.initialize({ protocolVersion: 1, clientCapabilities: { subagents: {} } });
const { sessionId } = await observedAgent.newSession({ cwd: process.cwd(), mcpServers: [] });
const session = observedAgent.sessions[sessionId]!;
const runtime = (session.nativeSubagentRuntime = new NativeSubagentRuntime(
"rfd",
sessionId,
session,
(notification) => client.sessionUpdate(notification),
{ log: () => {} },
));
const subagentStart = async (agentId: string, cwd: string) => {
const matcher = capturedOptions!.hooks!.SubagentStart!.find((matcher) => !matcher.matcher);
for (const hook of matcher!.hooks) {
await hook(
{
hook_event_name: "SubagentStart",
session_id: sessionId,
cwd,
transcript_path: "/tmp/transcript",
agent_id: agentId,
agent_type: "general-purpose",
},
undefined,
{ signal: new AbortController().signal },
);
}
};
const worktree = path.join(process.cwd(), ".claude", "worktrees", "agent-isolated");
await subagentStart("isolated", worktree);
await subagentStart("shared", process.cwd());
await runtime.taskStarted(
{ taskId: "isolated", subagentType: "general-purpose" },
async () => {},
);
await runtime.taskStarted(
{ taskId: "shared", subagentType: "general-purpose" },
async () => {},
);
const announcements = updates.flatMap(({ update }) =>
update.sessionUpdate === "subagent_update" ? [update] : [],
);
expect(announcements).toMatchObject([
{ sessionId: "isolated", cwd: worktree },
{ sessionId: "shared" },
]);
expect(announcements[1]).not.toHaveProperty("cwd");
});
it("reports an AIR child's model switch under its current generation", async () => {
const updates: AcpSessionNotification[] = [];
const client = createMockClient();
client.sessionUpdate = async (notification) => {
updates.push(notification);
};
const observedAgent = new ClaudeAcpAgent(client);
await observedAgent.initialize({
protocolVersion: 1,
clientCapabilities: {
_meta: { jetbrains: { air: { version: 1, capabilities: ["nativeSubagentSessions"] } } },
} as ClientCapabilities,
});
const { sessionId } = await observedAgent.newSession({ cwd: process.cwd(), mcpServers: [] });
const session = observedAgent.sessions[sessionId]!;
const runtime = (session.nativeSubagentRuntime = new NativeSubagentRuntime(
"air",
sessionId,
session,
(notification) => client.sessionUpdate(notification),
{ log: () => {} },
));
const matcher = capturedOptions!.hooks!.PostModelSwitch!.find((matcher) => !matcher.matcher);
const switchModel = async (fields: Record<string, unknown>) => {
for (const hook of matcher!.hooks) {
await hook(
{
hook_event_name: "PostModelSwitch",
session_id: sessionId,
cwd: process.cwd(),
transcript_path: "/tmp/transcript",
source: "auto",
...fields,
} as any,
undefined,
{ signal: new AbortController().signal },
);
}
};
const deliver = async () => {};
await runtime.taskStarted({ taskId: "child", subagentType: "Explore" }, deliver);
await runtime.finishTask("child", "completed", deliver);
await runtime.taskResumed("child", deliver, "Continue");
// The hook names the SDK agent id; AIR's draft runs the resumed agent in a new session.
await switchModel({ agent_id: "child", to_model: "claude-sonnet-4-6" });
const rootModel = session.configOptions.find((option) => option.id === "model");
const rootEntryName =
rootModel?.type === "select"
? (rootModel.options as Array<{ value: string; name: string }>).find(
(entry) => entry.value === "claude-sonnet-4-6",
)?.name
: undefined;
expect(rootEntryName).toBeDefined();
const models = updates.flatMap(({ sessionId: target, update }) =>
update.sessionUpdate === "config_option_update"
? update.configOptions.map((option) => [target, option.currentValue, option])
: [],
);
expect(models).toEqual([
[
"child:generation:2",
"claude-sonnet-4-6",
expect.objectContaining({
id: "model",
// The name of the root session's entry for the model.
options: [{ value: "claude-sonnet-4-6", name: rootEntryName }],
}),
],
]);
});
it("merges user-provided disallowedTools with ACP internal list", async () => {
await agent.newSession({
cwd: process.cwd(),
@@ -1047,6 +1310,66 @@ describe("createSession options merging", () => {
});
});
describe("thinking display from CLAUDE_CODE_THINKING_DISPLAY", () => {
let originalMaxThinking: string | undefined;
let originalDisplay: string | undefined;
beforeEach(() => {
originalMaxThinking = process.env.MAX_THINKING_TOKENS;
originalDisplay = process.env.CLAUDE_CODE_THINKING_DISPLAY;
delete process.env.MAX_THINKING_TOKENS;
delete process.env.CLAUDE_CODE_THINKING_DISPLAY;
});
afterEach(() => {
if (originalMaxThinking !== undefined) {
process.env.MAX_THINKING_TOKENS = originalMaxThinking;
} else {
delete process.env.MAX_THINKING_TOKENS;
}
if (originalDisplay !== undefined) {
process.env.CLAUDE_CODE_THINKING_DISPLAY = originalDisplay;
} else {
delete process.env.CLAUDE_CODE_THINKING_DISPLAY;
}
});
it("becomes explicit adaptive thinking when only display is set", async () => {
process.env.CLAUDE_CODE_THINKING_DISPLAY = "omitted";
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(capturedOptions!.thinking).toEqual({ type: "adaptive", display: "omitted" });
});
it("attaches display to a fixed thinking budget", async () => {
process.env.MAX_THINKING_TOKENS = "12000";
process.env.CLAUDE_CODE_THINKING_DISPLAY = "summarized";
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(capturedOptions!.thinking).toEqual({
type: "enabled",
budgetTokens: 12000,
display: "summarized",
});
});
it("has no effect when thinking is disabled", async () => {
process.env.MAX_THINKING_TOKENS = "0";
process.env.CLAUDE_CODE_THINKING_DISPLAY = "summarized";
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(capturedOptions!.thinking).toEqual({ type: "disabled" });
});
it("ignores an invalid value", async () => {
process.env.CLAUDE_CODE_THINKING_DISPLAY = "verbose";
const errorSpy = vi.fn();
(agent as any).logger = { log: () => {}, error: errorSpy };
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(capturedOptions!.thinking).toBeUndefined();
expect(errorSpy).toHaveBeenCalledWith(
expect.stringContaining("CLAUDE_CODE_THINKING_DISPLAY"),
);
});
});
describe("cwd validation", () => {
it("rejects a relative cwd with invalidParams", async () => {
await expect(
@@ -1079,6 +1402,36 @@ describe("createSession options merging", () => {
});
});
describe("startup failures", () => {
it("asks Claude Code to report why it could not start", async () => {
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(capturedOptions?.env?.CLAUDE_CODE_STARTUP_FAILURE_RESULTS).toBe("1");
});
it.each([
["proxy_invalid", RequestError.internalError().code, "Internal error"],
["gateway_signin_required", RequestError.authRequired().code, "Authentication required"],
["org_pin_mismatch", RequestError.authRequired().code, "Authentication required"],
["org_config_refused", RequestError.authRequired().code, "Authentication required"],
["cwd_unavailable", RequestError.invalidParams().code, "Invalid params"],
])("rejects with %s as code %i", async (reason, code, prefix) => {
initError = startupFailure(reason, "Fix the setting and restart Claude Code.");
await expect(agent.newSession({ cwd: process.cwd(), mcpServers: [] })).rejects.toMatchObject({
code,
message: `${prefix}: Fix the setting and restart Claude Code.`,
data: { startupFailureReason: reason },
});
expect(closeSpy).toHaveBeenCalled();
});
it("passes on an exit error that names no reason", async () => {
initError = new Error("Claude Code process exited with code 1. stderr: boom");
await expect(agent.newSession({ cwd: process.cwd(), mcpServers: [] })).rejects.toBe(
initError,
);
});
});
describe("elicitation", () => {
it("keeps AskUserQuestion disabled and omits callbacks without elicitation capability", async () => {
await agent.initialize({ protocolVersion: 1, clientCapabilities: {} });
@@ -1793,7 +2146,7 @@ describe("createSession options merging", () => {
await expect(
loadAgent.loadSession({ sessionId: "replay-probe", cwd: process.cwd(), mcpServers: [] }),
).rejects.toMatchObject({ code: RequestError.resourceNotFound().code });
).rejects.toMatchObject({ code: -32002, data: { sessionId: "replay-probe" } });
expect(updates).toEqual([]);
expect((loadAgent as any).sessions["replay-probe"]).toBeUndefined();
});
@@ -1815,7 +2168,8 @@ describe("createSession options merging", () => {
mcpServers: [],
});
const failed = expect(loading).rejects.toMatchObject({
code: RequestError.resourceNotFound().code,
code: -32002,
data: { sessionId: "replay-probe" },
});
failInit();
@@ -1951,6 +2305,66 @@ describe("createSession options merging", () => {
await loading;
});
it.each([
["without subagents", [] as Record<string, unknown>[]],
[
"with a subagent",
[
{
type: "assistant",
uuid: "child-message",
session_id: "replay-probe",
parent_tool_use_id: "agent-call",
message: {
id: "api-child-message",
model: "claude-sonnet-4-6",
role: "assistant",
content: [{ type: "text", text: "child report" }],
},
},
],
],
])(
"sends the end event before the CLI start finishes for native subagent sessions (%s)",
async (_label, history) => {
let releaseInit!: () => void;
initGate = new Promise((resolve) => (releaseInit = resolve));
sessionMessages = [userMessage("u1", "first"), ...history];
const updates: SessionNotification[] = [];
const loading = (
await endAgent(updates, undefined, {
_meta: {
jetbrains: {
air: {
version: 1,
capabilities: ["loadReplayEnd", "nativeSubagentSessions"],
},
},
},
} as ClientCapabilities)
).loadSession({ sessionId: "replay-probe", cwd: process.cwd(), mcpServers: [] });
// The child's model goes as the replay reaches it, with no model list yet.
await vi.waitFor(() =>
expect(kinds(updates)).toEqual(
history.length === 0
? ["user_message_chunk", "end"]
: [
"user_message_chunk",
"subagent_spawned",
"config_option_update",
"agent_message_chunk",
"subagent_state_update",
"end",
],
),
);
releaseInit();
await loading;
},
);
it("sends the end event for an empty history", async () => {
sessionMessages = [];
const updates: SessionNotification[] = [];

View File

@@ -466,6 +466,12 @@ describe("ACP v2 diffs", () => {
);
});
it("keeps a Windows path native in a git patch, so it matches the changes entry", () => {
expect(gitPatchText("C:\\work\\App.ts", "update", [hunk], "absolute")).toContain(
'diff --git "C:\\\\work\\\\App.ts" "C:\\\\work\\\\App.ts"\n--- "C:\\\\work\\\\App.ts"\n+++ "C:\\\\work\\\\App.ts"\n',
);
});
it("shows no diff at tool use, where the input holds a snippet or no old text", () => {
const renderer = new AcpToolCallRenderer(v2);
const edit = { file_path: "/work/a.ts", old_string: "old", new_string: "new" };

View File

@@ -60,6 +60,19 @@ describe("mcpElicitationToCreateRequest", () => {
});
});
it("adds an empty properties map when the MCP schema has none", () => {
const request: ElicitationRequest = {
serverName: "server",
message: "Confirm?",
mode: "form",
requestedSchema: { type: "object" },
};
const result = mcpElicitationToCreateRequest(request, SESSION_ID);
expect(result).toMatchObject({ requestedSchema: { type: "object", properties: {} } });
});
it("maps a url-mode request and preserves the elicitation id", () => {
const request: ElicitationRequest = {
serverName: "server",
@@ -119,6 +132,31 @@ describe("createElicitationResponseToElicitResult", () => {
});
});
it("forwards content values that only v2 allows, such as an object", () => {
// ACP v2 lets an elicitation value be any JSON value. The MCP server checks
// the content against its own schema, so none of it is dropped here.
const content = {
name: "Ada",
address: { city: "London" },
tags: ["a", 1, null],
};
const response = { action: "accept", content } as unknown as CreateElicitationResponse;
expect(createElicitationResponseToElicitResult(response)).toEqual({
action: "accept",
content,
});
});
it("maps accept content that is no JSON object to an empty object", () => {
for (const content of [null, "Ada", ["Ada"]]) {
const response = { action: "accept", content } as unknown as CreateElicitationResponse;
expect(createElicitationResponseToElicitResult(response)).toEqual({
action: "accept",
content: {},
});
}
});
it("maps decline and cancel", () => {
expect(
createElicitationResponseToElicitResult({ action: "decline" } as CreateElicitationResponse),
@@ -343,6 +381,28 @@ describe("applyAskElicitationResponse", () => {
});
});
it("reads only string, number, and boolean picks from v2 content", () => {
// A v2 client may send an object or a mixed array. Such values are no
// option label, so they count as no pick instead of "[object Object]".
const response = {
action: "accept",
content: {
question_0: { label: "A" },
question_0_custom: { text: "ignored" },
question_1: ["X", { label: "Y" }, null, 2],
},
} as unknown as CreateElicitationResponse;
expect(applyAskElicitationResponse(response, toolInput, questions)).toEqual({
action: "answered",
updatedInput: {
questions,
metadata: { source: "test" },
answers: { "Multi?": "X, 2" },
},
});
});
it("uses a multi-select question's custom answer alone when nothing is selected", () => {
const response = {
action: "accept",
@@ -481,6 +541,45 @@ describe("applyAskElicitationResponse", () => {
});
});
it("marks every question dismissed on decline when reportUnanswered is set", () => {
const response = { action: "decline" } as CreateElicitationResponse;
expect(
applyAskElicitationResponse(response, toolInput, questions, { reportUnanswered: true }),
).toEqual({
action: "answered",
updatedInput: {
questions,
metadata: { source: "test" },
answers: {},
annotations: {
"Single?": { unanswered: "dismissed" },
"Multi?": { unanswered: "dismissed" },
},
},
});
});
it("marks blank questions skipped on accept when reportUnanswered is set", () => {
const response = {
action: "accept",
content: { question_0: "A", question_0_custom: "note", question_1: [] },
} as CreateElicitationResponse;
expect(
applyAskElicitationResponse(response, toolInput, questions, { reportUnanswered: true }),
).toEqual({
action: "answered",
updatedInput: {
questions,
metadata: { source: "test" },
answers: { "Single?": "A" },
annotations: {
"Single?": { notes: "note" },
"Multi?": { unanswered: "skipped" },
},
},
});
});
it("returns cancel on cancel", () => {
const response = { action: "cancel" } as CreateElicitationResponse;
expect(applyAskElicitationResponse(response, toolInput, questions)).toEqual({
@@ -600,10 +699,12 @@ describe("refusalFallbackResultFromResponse", () => {
{ action: "accept", content: { choice: "cancelled" } },
{ action: "accept", content: {} },
{ action: "accept", content: { choice: "something-new" } },
{ action: "accept", content: { choice: { value: "retry_fallback" } } },
{ action: "accept", content: { choice: ["retry_fallback"] } },
{ action: "accept" },
{ action: "decline" },
{ action: "cancel" },
] as CreateElicitationResponse[];
] as unknown as CreateElicitationResponse[];
for (const response of cases) {
expect(refusalFallbackResultFromResponse(response)).toBe("cancelled");
}

View File

@@ -114,7 +114,10 @@ describe("resolveFastModeEnabled", () => {
it("rejects any other value", () => {
expect(() => resolveFastModeEnabled({ ...base, value: "maybe" })).toThrow(
/Invalid value for config option fast/,
expect.objectContaining({
code: -32602,
message: expect.stringContaining("Invalid value for config option fast"),
}),
);
});
});
@@ -253,6 +256,31 @@ describe("setSessionConfigOption Fast mode toggle", () => {
expect(response.configOptions).toContainEqual(createFastModeConfigOption(true, false));
});
it("rejects a value whose type differs from the option's advertised type", async () => {
// Advertised as `type: "boolean"`: "on" is not a boolean.
const booleanClient = setup({ useBooleanOption: true });
await expect(
booleanClient.agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: FAST_MODE_CONFIG_ID,
value: FAST_MODE_ON,
}),
).rejects.toMatchObject({ code: -32602 });
expect(booleanClient.applyFlagSettings).not.toHaveBeenCalled();
// Advertised as an on/off select: `true` is not a string.
const selectClient = setup({ useBooleanOption: false });
await expect(
selectClient.agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: FAST_MODE_CONFIG_ID,
type: "boolean",
value: true,
}),
).rejects.toMatchObject({ code: -32602 });
expect(selectClient.applyFlagSettings).not.toHaveBeenCalled();
});
it("does not change session state when the SDK rejects the flag", async () => {
const { agent, applyFlagSettings } = setup({ useBooleanOption: true });
applyFlagSettings.mockRejectedValueOnce(new Error("nope"));

View File

@@ -0,0 +1,298 @@
import { describe, expect, it, vi } from "vitest";
import { randomUUID } from "node:crypto";
import { ClaudeAcpAgent, type AcpClient } from "../acp-agent.js";
import { Pushable } from "../utils.js";
import {
mockSessionState,
successfulResultMessage,
userEcho,
wrapQuery,
} from "./session-doubles.js";
vi.mock("@anthropic-ai/claude-agent-sdk", async (importOriginal) => {
const actual = await importOriginal<typeof import("@anthropic-ai/claude-agent-sdk")>();
const refused = () => {
throw new Error("Unexpected real SDK call");
};
return {
...actual,
query: vi.fn(refused),
getSessionMessages: vi.fn(async () => []),
getSubagentMessages: vi.fn(async () => []),
listSessions: vi.fn(async () => []),
getSessionInfo: vi.fn(async () => undefined),
deleteSession: vi.fn(refused),
forkSession: vi.fn(refused),
importSessionToStore: vi.fn(refused),
};
});
async function untilDone<T>(promise: Promise<T>): Promise<T> {
let timer: ReturnType<typeof setTimeout> | undefined;
try {
return await Promise.race([
promise,
new Promise<never>((_, reject) => {
timer = setTimeout(() => reject(new Error("Prompt did not settle")), 3000);
}),
]);
} finally {
if (timer !== undefined) clearTimeout(timer);
}
}
function createAgent() {
const client = { sessionUpdate: async () => {} } as unknown as AcpClient;
return new ClaudeAcpAgent(client, { log: () => {}, error: () => {} });
}
function usage(inputTokens: number, outputTokens: number) {
return {
input_tokens: inputTokens,
output_tokens: outputTokens,
cache_read_input_tokens: 0,
cache_creation_input_tokens: 0,
};
}
function modelUsage(inputTokens: number, outputTokens: number) {
return {
"claude-sonnet-4-6": {
inputTokens,
outputTokens,
cacheReadInputTokens: 0,
cacheCreationInputTokens: 0,
webSearchRequests: 0,
costUSD: 0,
contextWindow: 200000,
},
};
}
function stamp(uuid: string, fields: string) {
return {
...(fields !== "plural" ? { user_message_uuid: uuid } : {}),
...(fields !== "singular" ? { user_message_uuids: [uuid] } : {}),
};
}
const idle = { type: "system", subtype: "session_state_changed", state: "idle" };
describe("a steer folded into an autonomous cycle", () => {
it.each([
["singular", true],
["plural", true],
["both", true],
["singular", false],
["plural", false],
["both", false],
] as const)("settles from its own %s UUID stamp (echo: %s)", async (fields, echo) => {
const agent = createAgent();
const input = new Pushable<any>();
const echoed = Promise.withResolvers<void>();
const interrupted = Promise.withResolvers<void>();
const resume = Promise.withResolvers<void>();
const processed = Promise.withResolvers<void>();
const finish = Promise.withResolvers<void>();
async function* messages() {
const iter = input[Symbol.asyncIterator]();
const { value: original } = await iter.next();
yield userEcho(original);
echoed.resolve();
const { value: steered } = await iter.next();
yield successfulResultMessage({
origin: { kind: "human" },
...stamp(original.uuid, "both"),
queued_turn_count: 1,
stop_reason: "tool_use",
usage: usage(10, 5),
modelUsage: modelUsage(10, 5),
});
interrupted.resolve();
await resume.promise;
if (echo) yield userEcho(steered);
// Only the steer's UUID is named; it has no queued Turn of its own.
yield successfulResultMessage({
origin: { kind: "task-notification" },
...stamp(steered.uuid, fields),
queued_turn_count: 0,
stop_reason: "max_tokens",
usage: usage(20, 7),
modelUsage: modelUsage(30, 12),
});
yield idle;
processed.resolve();
// Stream shutdown must not supply the prompt's response.
await finish.promise;
}
const session = mockSessionState({ input, query: wrapQuery(messages()) });
agent.sessions["test-session"] = session;
let response: unknown;
const prompt = agent
.prompt({ sessionId: "test-session", prompt: [{ type: "text", text: "original" }] })
.then((value) => (response = value));
try {
await untilDone(echoed.promise);
await expect(
agent.steer({
sessionId: "test-session",
prompt: [{ type: "text", text: "also handle this" }],
}),
).resolves.toEqual({ outcome: "injected" });
await untilDone(interrupted.promise);
expect(response).toBeUndefined();
resume.resolve();
await untilDone(processed.promise);
await untilDone(prompt);
const tokenCount = {
totalTokens: 42,
inputTokens: 30,
cachedInputTokens: 0,
cachedWriteTokens: 0,
outputTokens: 12,
reasoningOutputTokens: 0,
};
expect(response).toEqual({
stopReason: "max_tokens",
usage: {
inputTokens: 30,
outputTokens: 12,
cachedReadTokens: 0,
cachedWriteTokens: 0,
totalTokens: 42,
},
_meta: {
quota: {
token_count: tokenCount,
model_usage: [{ model: "claude-sonnet-4-6", token_count: tokenCount }],
},
},
});
expect(session.activeTurn).toBeNull();
expect(session.turnQueue).toHaveLength(0);
expect(session.owedTrailingIdles).toBe(0);
} finally {
resume.resolve();
finish.resolve();
await untilDone(session.consumer);
await untilDone(prompt);
}
});
it.each(["unrelated", "held", "settled", "plural-unrelated", "plural-empty"] as const)(
"does not attribute a result to a steer in the %s state",
async (state) => {
const agent = createAgent();
const input = new Pushable<any>();
const echoed = Promise.withResolvers<void>();
const processed = Promise.withResolvers<void>();
const resume = Promise.withResolvers<void>();
const finish = Promise.withResolvers<void>();
let response: unknown;
let owner: any;
let steerUuid = "";
const heldOutcome = { stopReason: "end_turn" };
async function* messages() {
const iter = input[Symbol.asyncIterator]();
const { value: original } = await iter.next();
yield userEcho(original);
echoed.resolve();
const { value: steered } = await iter.next();
steerUuid = steered.uuid;
const session = agent.sessions["test-session"];
owner = session.activeTurn;
// Constructed defensive states: these do not model normal settlement.
if (state === "held") {
owner.deferredSettle = heldOutcome;
owner.spawnedTaskIds = new Set(["still-running"]);
session.liveBackgroundTasks.set("still-running", { isSubagent: true });
} else if (state === "settled") {
owner.settled = true;
}
const unrelated = randomUUID();
yield successfulResultMessage({
origin: { kind: "task-notification" },
user_message_uuid: state === "unrelated" ? unrelated : steerUuid,
user_message_uuids:
state === "plural-empty" ? [] : [state.includes("unrelated") ? unrelated : steerUuid],
stop_reason: "refusal",
usage: usage(100, 50),
modelUsage: modelUsage(100, 50),
});
processed.resolve();
await resume.promise;
owner.settled = false;
owner.deferredSettle = undefined;
owner.spawnedTaskIds = undefined;
session.liveBackgroundTasks.clear();
yield successfulResultMessage({
origin: { kind: "human" },
...stamp(steerUuid, "both"),
usage: usage(10, 5),
});
yield idle; // the excluded autonomous result's trailer
yield idle; // the human result's trailer
await finish.promise;
}
const session = mockSessionState({ input, query: wrapQuery(messages()) });
agent.sessions["test-session"] = session;
const prompt = agent
.prompt({ sessionId: "test-session", prompt: [{ type: "text", text: "original" }] })
.then((value) => (response = value));
try {
await untilDone(echoed.promise);
await expect(
agent.steer({
sessionId: "test-session",
prompt: [{ type: "text", text: "also handle this" }],
}),
).resolves.toEqual({ outcome: "injected" });
await untilDone(processed.promise);
expect(response).toBeUndefined();
expect(session.activeTurn).toBe(owner);
expect(session.accumulatedUsage).toEqual({
inputTokens: 0,
outputTokens: 0,
cachedReadTokens: 0,
cachedWriteTokens: 0,
});
expect(session.accumulatedModelUsage).toEqual({});
expect(owner.steeredUuids).toEqual(new Set([steerUuid]));
expect(owner.steeredEchoes).toEqual(new Set([steerUuid]));
expect(owner.steeredSettle).toBeUndefined();
if (state === "held") expect(owner.deferredSettle).toBe(heldOutcome);
if (state === "settled") expect(owner.settled).toBe(true);
resume.resolve();
await untilDone(prompt);
expect(response).toEqual({
stopReason: "end_turn",
usage: {
inputTokens: 10,
outputTokens: 5,
cachedReadTokens: 0,
cachedWriteTokens: 0,
totalTokens: 15,
},
_meta: {
quota: {
token_count: {
totalTokens: 15,
inputTokens: 10,
cachedInputTokens: 0,
cachedWriteTokens: 0,
outputTokens: 5,
reasoningOutputTokens: 0,
},
model_usage: [],
},
},
});
} finally {
resume.resolve();
finish.resolve();
await untilDone(session.consumer);
await untilDone(prompt);
}
},
);
});

View File

@@ -0,0 +1,188 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { getSessionMessages } from "@anthropic-ai/claude-agent-sdk";
import { createHash, randomUUID } from "node:crypto";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import * as os from "node:os";
import { messageIdForGrouping } from "../acp-agent.js";
import { forkSession } from "../fork-session.js";
import { encodeProjectPath } from "../session-index/project-dirs.js";
// Use the real SDK against temporary transcripts, without starting Claude Code.
let root: string;
let sourceCwd: string;
let destinationCwd: string;
let sessionId: string;
let sourceFile: string;
let transcript: string;
let activeUuid: string;
beforeEach(async () => {
root = await fs.realpath(await fs.mkdtemp(path.join(os.tmpdir(), "fork-session-")));
sourceCwd = path.join(root, "source");
destinationCwd = path.join(root, "destination");
const configDir = path.join(root, "config");
vi.stubEnv("CLAUDE_CONFIG_DIR", configDir);
await fs.mkdir(sourceCwd);
await fs.mkdir(destinationCwd);
sessionId = randomUUID();
const userUuid = randomUUID();
const firstUuid = randomUUID();
const followupUuid = randomUUID();
activeUuid = randomUUID();
const common = { sessionId, cwd: sourceCwd, isSidechain: false };
const entries = [
{
...common,
type: "user",
uuid: userUuid,
parentUuid: null,
message: { role: "user", content: "First prompt" },
},
{
...common,
type: "assistant",
uuid: firstUuid,
parentUuid: userUuid,
message: {
id: "msg_first",
role: "assistant",
content: [{ type: "text", text: "First answer" }],
},
},
{
...common,
type: "user",
uuid: followupUuid,
parentUuid: firstUuid,
message: { role: "user", content: "Follow-up prompt" },
},
{
...common,
type: "assistant",
uuid: randomUUID(),
parentUuid: followupUuid,
message: {
id: "msg_inactive",
role: "assistant",
content: [{ type: "text", text: "Inactive answer" }],
},
},
{
...common,
type: "assistant",
uuid: activeUuid,
parentUuid: followupUuid,
message: {
id: "msg_active",
role: "assistant",
content: [{ type: "text", text: "Active answer" }],
},
},
];
transcript =
entries
.map((entry, index) =>
JSON.stringify({ ...entry, timestamp: new Date(1_700_000_000_000 + index).toISOString() }),
)
.join("\n") + "\n";
const projectDir = path.join(configDir, "projects", encodeProjectPath(sourceCwd));
await fs.mkdir(projectDir, { recursive: true });
sourceFile = path.join(projectDir, `${sessionId}.jsonl`);
await fs.writeFile(sourceFile, transcript);
});
afterEach(async () => {
vi.unstubAllEnvs();
await fs.rm(root, { recursive: true, force: true });
});
describe.each(["same directory", "different directory"])("fork into %s", (directory) => {
it.each([
{ resolution: "latest", messageId: undefined, expectedAnswer: "Active answer", count: 4 },
{
resolution: "active history",
messageId: "msg_first",
expectedAnswer: "First answer",
count: 2,
},
{
resolution: "inactive branch",
messageId: "msg_inactive",
expectedAnswer: "Inactive answer",
count: 4,
},
{
resolution: "fingerprint",
messageId: "stale-message-id",
expectedAnswer: "Inactive answer",
count: 4,
},
{ resolution: "live map", messageId: "msg_active", expectedAnswer: "Active answer", count: 4 },
])("resolves the fork point through $resolution", async (scenario) => {
const originalMessages = await getSessionMessages(sessionId);
expect(originalMessages.map(messageIdForGrouping)).not.toContain("msg_inactive");
const cwd = directory === "same directory" ? sourceCwd : destinationCwd;
const result = await forkSession(
{
sessionId,
cwd,
mcpServers: [],
...(scenario.messageId && {
_meta: {
jetbrains: {
air: {
fork: {
version: 1,
messageId: scenario.messageId,
...(scenario.resolution === "fingerprint" && {
messageFingerprint: `sha256:${createHash("sha256").update("Inactive answer").digest("hex")}`,
messageOccurrence: 1,
}),
},
},
},
},
}),
},
{
messageIdForGrouping,
...(scenario.resolution === "live map" && {
liveMessageIdToUuid: new Map([["msg_active", activeUuid]]),
}),
},
);
expect(result.sessionId).not.toBe(sessionId);
const messages = await getSessionMessages(result.sessionId);
expect(messages).toHaveLength(scenario.count);
expect(messages.at(-1)?.message).toMatchObject({
content: [{ type: "text", text: scenario.expectedAnswer }],
});
const originalUuids = originalMessages.map((message) => message.uuid);
for (const message of messages) {
expect(originalUuids).not.toContain(message.uuid);
}
expect(await fs.readFile(sourceFile, "utf8")).toBe(transcript);
});
it("rejects a fork point that is not in the source session", async () => {
await expect(
forkSession(
{
sessionId,
cwd: directory === "same directory" ? sourceCwd : destinationCwd,
mcpServers: [],
_meta: { jetbrains: { air: { fork: { version: 1, messageId: "missing" } } } },
},
{ messageIdForGrouping },
),
).rejects.toMatchObject({
code: -32602,
message: expect.stringContaining(
`Fork point message missing was not found in session ${sessionId}`,
),
});
expect(await fs.readFile(sourceFile, "utf8")).toBe(transcript);
});
});

View File

@@ -7,7 +7,11 @@ import {
listGatewayModels,
selectBedrockRegion,
} from "../gateway-models.js";
import { getAvailableModels, narrowToGatewayModels } from "../session-model.js";
import {
buildModelConfigOption,
getAvailableModels,
narrowToGatewayModels,
} from "../session-model.js";
const logger = { log: vi.fn(), error: vi.fn() };
@@ -391,6 +395,13 @@ describe("getAvailableModels under a gateway listing", () => {
expect(state.currentModelId).toBe("claude-opus-5");
expect(setModel).not.toHaveBeenCalled();
// The model option lists the resumed model so its currentValue is selectable.
const option = buildModelConfigOption(state, LISTED, false);
expect(option.currentValue).toBe("claude-opus-5");
expect(
option.type === "select" &&
option.options.map((entry) => ("value" in entry ? entry.value : entry.group)),
).toEqual([...state.availableModels.map((model) => model.modelId), "claude-opus-5"]);
});
it("reports the SDK default when a PreModelSwitch hook vetoes the switch", async () => {

View File

@@ -4,7 +4,11 @@
* their own vi.fn spies via `overrides`.
*/
import type { ClientCapabilities, InitializeRequest } from "@agentclientprotocol/sdk/unstable";
import {
RequestError,
type ClientCapabilities,
type InitializeRequest,
} from "@agentclientprotocol/sdk/unstable";
/** The context-usage report the base mock query returns. `rawMaxTokens`
* matches the agent's DEFAULT_CONTEXT_WINDOW so window-related assertions
@@ -31,6 +35,9 @@ export function makeMockQuery(overrides: Record<string, unknown> = {}) {
supportedCommands: async () => [],
mcpServerStatus: async () => [],
getContextUsage: async () => DEFAULT_CONTEXT_USAGE,
// No ultracode fields: session creation falls back to settings and the
// model's effort levels, as with a CLI that does not report them.
getSettings: async () => ({ applied: {} }),
close: () => {},
interrupt: async () => undefined,
[Symbol.asyncIterator]: async function* () {},
@@ -38,6 +45,19 @@ export function makeMockQuery(overrides: Record<string, unknown> = {}) {
};
}
/**
* The error `initializationResult()` rejects with when Claude Code refuses to
* start with `CLAUDE_CODE_STARTUP_FAILURE_RESULTS` set, as SDK 0.3.316 builds
* it from the CLI's error result (same message prefix and fields).
*/
export function startupFailure(reason: string, cliMessage: string): Error {
return Object.assign(new Error(`Claude Code returned an error result: ${cliMessage}`), {
telemetryMessage: "Claude Code returned an error result",
errorClass: "error_result",
startupFailureReason: reason,
});
}
/**
* Declares the client capabilities through `initialize`, as a real client
* does. The agent reads its capability choices only there.
@@ -48,3 +68,16 @@ export async function initializeClient(
): Promise<void> {
await agent.initialize({ protocolVersion: 1, clientCapabilities });
}
/**
* A request to a client that never answers it, as the SDK connection returns
* it with `stopWaitingOnCancel`: it rejects with `-32800` once `signal`
* aborts. The real connection also sends `$/cancel_request` then.
*/
export function heldRequest<T>(_params?: unknown, signal?: AbortSignal): Promise<T> {
return new Promise<T>((_resolve, reject) => {
const cancel = () => reject(RequestError.requestCancelled(signal?.reason));
if (signal?.aborted) cancel();
else signal?.addEventListener("abort", cancel, { once: true });
});
}

Some files were not shown because too many files have changed in this diff Show More