mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-11-04 08:22:03 +00:00
Compare commits
73 Commits
v0.89.0
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98e7fe3231 | ||
|
|
59e292e29f | ||
|
|
50cd76e78d | ||
|
|
aa62f44886 | ||
|
|
9b3a5c891c | ||
|
|
ecaa2c935b | ||
|
|
5b11f470b1 | ||
|
|
e9f986b8a9 | ||
|
|
4d378f93f3 | ||
|
|
a1d0eeeaad | ||
|
|
7bcbfc7a5c | ||
|
|
2dab922644 | ||
|
|
ff01e06579 | ||
|
|
e155ecb419 | ||
|
|
bcf74c2fe8 | ||
|
|
1420994ced | ||
|
|
424db87535 | ||
|
|
dbf386d8d3 | ||
|
|
db9f50f021 | ||
|
|
f5967fd6b4 | ||
|
|
af3d7f9335 | ||
|
|
68bf74f58e | ||
|
|
26f144a2a5 | ||
|
|
872e93f11f | ||
|
|
78a29f58ba | ||
|
|
910d83ceea | ||
|
|
c0af592d25 | ||
|
|
761b4c8359 | ||
|
|
1a1f1a0e80 | ||
|
|
0c8de1380f | ||
|
|
fadbe1881c | ||
|
|
ba3c7be05e | ||
|
|
aeb69e7ed1 | ||
|
|
e8eb9d340f | ||
|
|
d528647162 | ||
|
|
594aa8c955 | ||
|
|
bcc024d2c9 | ||
|
|
b4788d61ea | ||
|
|
c1e83b46b7 | ||
|
|
c7425e4e04 | ||
|
|
1fd217f3e7 | ||
|
|
9a1b90fb9d | ||
|
|
764f339c3e | ||
|
|
ebf8b49fd6 | ||
|
|
df21b3fb56 | ||
|
|
7f7a1404a1 | ||
|
|
d6f60f512a | ||
|
|
32c23db06d | ||
|
|
c6510c27c1 | ||
|
|
21eef57655 | ||
|
|
53df1c7b22 | ||
|
|
c7393d27ff | ||
|
|
4615a9141c | ||
|
|
252126f616 | ||
|
|
bd06804b67 | ||
|
|
5758ac84a4 | ||
|
|
d6c371704f | ||
|
|
30ad5f7372 | ||
|
|
383bc7dcf0 | ||
|
|
9db8a28b64 | ||
|
|
1137df33aa | ||
|
|
d074558e4a | ||
|
|
14b2233192 | ||
|
|
c22ec1d189 | ||
|
|
b007ac7c45 | ||
|
|
412da2789a | ||
|
|
2a8417ab89 | ||
|
|
fe8b69edd4 | ||
|
|
b3bed2f763 | ||
|
|
77910dea74 | ||
|
|
231bbd7dd6 | ||
|
|
4e1fc1e8df | ||
|
|
92abb8fd4d |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -24,9 +24,10 @@ jobs:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
|
||||
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
check-latest: true
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
|
||||
6
.github/workflows/publish.yml
vendored
6
.github/workflows/publish.yml
vendored
@@ -74,9 +74,10 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }}
|
||||
# Setup .npmrc file to publish to npm
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
|
||||
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
check-latest: true
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
@@ -129,9 +130,10 @@ jobs:
|
||||
# few megabytes packed, so a full fetch is cheap and sidesteps every
|
||||
# shallow-clone tag caveat.
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
|
||||
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
check-latest: true
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
# Ahead of the version bump, so this still validates the committed lockfile.
|
||||
- run: npm ci
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "0.89.0"
|
||||
".": "0.94.0"
|
||||
}
|
||||
|
||||
101
CHANGELOG.md
101
CHANGELOG.md
@@ -1,5 +1,106 @@
|
||||
# Changelog
|
||||
|
||||
## [0.94.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.93.0...v0.94.0) (2026-10-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* send one end event after the session/load history replay for AIR ([#1361](https://github.com/agentclientprotocol/claude-agent-acp/issues/1361)) ([1420994](https://github.com/agentclientprotocol/claude-agent-acp/commit/1420994ced20919aad127a8bd0294de022449e35))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* match OpenRouter model ids in the gateway model listing ([#1365](https://github.com/agentclientprotocol/claude-agent-acp/issues/1365)) ([bcf74c2](https://github.com/agentclientprotocol/claude-agent-acp/commit/bcf74c2fe8e32bd24714d45122529dcf2f99a279))
|
||||
* resolve a skill's SKILL.md by its declared name, not by its directory name ([#1372](https://github.com/agentclientprotocol/claude-agent-acp/issues/1372)) ([424db87](https://github.com/agentclientprotocol/claude-agent-acp/commit/424db8753581bfaf4c5bf278f8df8c089eec35da))
|
||||
* update to ACP SDK 2.0.0 ([#1386](https://github.com/agentclientprotocol/claude-agent-acp/issues/1386)) ([ff01e06](https://github.com/agentclientprotocol/claude-agent-acp/commit/ff01e06579225e00643914692994cd60da4d6225))
|
||||
* update to Claude Code 2.1.303 ([#1384](https://github.com/agentclientprotocol/claude-agent-acp/issues/1384)) ([2dab922](https://github.com/agentclientprotocol/claude-agent-acp/commit/2dab92264414d133fd6774f7a90241ea5192ba32))
|
||||
|
||||
## [0.93.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.92.0...v0.93.0) (2026-10-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add /remote-control command to start Remote Control for a session ([#1197](https://github.com/agentclientprotocol/claude-agent-acp/issues/1197)) ([db9f50f](https://github.com/agentclientprotocol/claude-agent-acp/commit/db9f50f02145d088c0f6080672969a0673ba6359))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* hold a fork back from the session list until its session/fork response is out ([#1368](https://github.com/agentclientprotocol/claude-agent-acp/issues/1368)) ([f5967fd](https://github.com/agentclientprotocol/claude-agent-acp/commit/f5967fd6b49ce4821cc829c26902ccf46f671992))
|
||||
|
||||
## [0.92.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.91.0...v0.92.0) (2026-10-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add the MCP server and tool to AIR tool calls ([#1359](https://github.com/agentclientprotocol/claude-agent-acp/issues/1359)) ([872e93f](https://github.com/agentclientprotocol/claude-agent-acp/commit/872e93f11fde8abc528822d29cebcc542b05e9e8))
|
||||
* publish the predicted follow-up prompt as _meta.promptSuggestions ([#1356](https://github.com/agentclientprotocol/claude-agent-acp/issues/1356)) ([78a29f5](https://github.com/agentclientprotocol/claude-agent-acp/commit/78a29f58ba1eb1b54896d8d73f49402ee50aa087))
|
||||
* update ACP SDK to 1.9.0 and adopt the v2 spec clarifications ([#1366](https://github.com/agentclientprotocol/claude-agent-acp/issues/1366)) ([68bf74f](https://github.com/agentclientprotocol/claude-agent-acp/commit/68bf74f58ee378f21502ac93c44878b712905af2))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* include subagent file edits in the file-change report ([#1357](https://github.com/agentclientprotocol/claude-agent-acp/issues/1357)) ([910d83c](https://github.com/agentclientprotocol/claude-agent-acp/commit/910d83ceea78a03b39002cf2dcc275442838e2db))
|
||||
* restore resumed subagents after an adapter restart ([#1364](https://github.com/agentclientprotocol/claude-agent-acp/issues/1364)) ([26f144a](https://github.com/agentclientprotocol/claude-agent-acp/commit/26f144a2a5c60452805d11c87433963318ef4e65))
|
||||
|
||||
## [0.91.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.90.0...v0.91.0) (2026-10-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* keep background subagents running after a parent cancel, for clients that can cancel them ([#1348](https://github.com/agentclientprotocol/claude-agent-acp/issues/1348)) ([ba3c7be](https://github.com/agentclientprotocol/claude-agent-acp/commit/ba3c7be05e9af297fe95b0ac647da2340dd9e15b))
|
||||
* let a client cancel one subagent with session/cancel on its child session ([#1347](https://github.com/agentclientprotocol/claude-agent-acp/issues/1347)) ([aeb69e7](https://github.com/agentclientprotocol/claude-agent-acp/commit/aeb69e7ed1170e8bce236378432ef8b83cdf2554))
|
||||
* mark session list rows of worktrees Claude Code made (agentWorktree) ([#1352](https://github.com/agentclientprotocol/claude-agent-acp/issues/1352)) ([1a1f1a0](https://github.com/agentclientprotocol/claude-agent-acp/commit/1a1f1a0e802cc2ee097d9c78c1f7bafccef7c3df))
|
||||
* narrow models to what the providers/set gateway lists ([#1340](https://github.com/agentclientprotocol/claude-agent-acp/issues/1340)) ([e8eb9d3](https://github.com/agentclientprotocol/claude-agent-acp/commit/e8eb9d340fd4f6dfd9826cf58461edf5ee165f44))
|
||||
* restore a subagent from before a reload when SendMessage resumes it, for subagents RFD clients ([#1332](https://github.com/agentclientprotocol/claude-agent-acp/issues/1332)) ([df21b3f](https://github.com/agentclientprotocol/claude-agent-acp/commit/df21b3fb56c3449b6f86c9c9df558be351815ed3))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* close the tool calls of a background subagent that the SDK reports stopped after a cancel ([#1336](https://github.com/agentclientprotocol/claude-agent-acp/issues/1336)) ([9a1b90f](https://github.com/agentclientprotocol/claude-agent-acp/commit/9a1b90fb9db8b04ef6756cbcdc8df22afc82a2ba))
|
||||
* close the tool calls of a cycle that Claude Code started and a cancel stopped ([#1339](https://github.com/agentclientprotocol/claude-agent-acp/issues/1339)) ([1fd217f](https://github.com/agentclientprotocol/claude-agent-acp/commit/1fd217f3e77d0b7883ebd220bc19c7899d3af77f))
|
||||
* close the tool calls that a cycle Claude Code started abandoned, at its end ([#1344](https://github.com/agentclientprotocol/claude-agent-acp/issues/1344)) ([594aa8c](https://github.com/agentclientprotocol/claude-agent-acp/commit/594aa8c9556609c16946c889e9c8f1f76337b203))
|
||||
* deliver the output of a background subagent that a cancel did not stop ([#1345](https://github.com/agentclientprotocol/claude-agent-acp/issues/1345)) ([bcc024d](https://github.com/agentclientprotocol/claude-agent-acp/commit/bcc024d2c9a34d62e19999bc6cfc393ad56fbcde))
|
||||
* end the terminal of a command whose tool call is closed without a result ([#1335](https://github.com/agentclientprotocol/claude-agent-acp/issues/1335)) ([764f339](https://github.com/agentclientprotocol/claude-agent-acp/commit/764f339c3e83eac8ca34f4ddc8ed65f8199b651d))
|
||||
* end the tool calls of a cycle that Claude Code started and a cancel stopped, on v1 ([#1342](https://github.com/agentclientprotocol/claude-agent-acp/issues/1342)) ([b4788d6](https://github.com/agentclientprotocol/claude-agent-acp/commit/b4788d61eaf9372b376d56c0f6fb7c6156822ae6))
|
||||
* keep a live session that was never prompted when a resume or load changes its settings ([#1353](https://github.com/agentclientprotocol/claude-agent-acp/issues/1353)) ([0c8de13](https://github.com/agentclientprotocol/claude-agent-acp/commit/0c8de1380fd990291fc51cf17c6742402efb5c94))
|
||||
* let session/delete succeed for a session without a transcript ([#1351](https://github.com/agentclientprotocol/claude-agent-acp/issues/1351)) ([fadbe18](https://github.com/agentclientprotocol/claude-agent-acp/commit/fadbe1881cd441609a1fde8c43286d9a404dc425))
|
||||
* report no running for the session's own request that a cancel withdraws, on ACP v2 ([#1346](https://github.com/agentclientprotocol/claude-agent-acp/issues/1346)) ([d528647](https://github.com/agentclientprotocol/claude-agent-acp/commit/d528647162ba6747654125cf6ebc2297ced02ed0))
|
||||
* show nothing of a tool that cancelled work asks to run after the cancel was reported ([#1334](https://github.com/agentclientprotocol/claude-agent-acp/issues/1334)) ([ebf8b49](https://github.com/agentclientprotocol/claude-agent-acp/commit/ebf8b49fd68924f2c50680e7e73241f625801980))
|
||||
* stop waiting for a question the client leaves open after Claude Code cancels it ([#1338](https://github.com/agentclientprotocol/claude-agent-acp/issues/1338)) ([c1e83b4](https://github.com/agentclientprotocol/claude-agent-acp/commit/c1e83b46b717230a5d623c3ac591d915a776e0ae))
|
||||
* update Claude Agent SDK to 0.3.300 ([#1355](https://github.com/agentclientprotocol/claude-agent-acp/issues/1355)) ([761b4c8](https://github.com/agentclientprotocol/claude-agent-acp/commit/761b4c835985ff265d35338df35eee6e6861d8f3))
|
||||
|
||||
## [0.90.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.89.1...v0.90.0) (2026-10-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* accept IDE session hooks without dropping model settings ([#1265](https://github.com/agentclientprotocol/claude-agent-acp/issues/1265)) ([fe8b69e](https://github.com/agentclientprotocol/claude-agent-acp/commit/fe8b69edd438ee1bc4206752b029dd45e71975fe))
|
||||
* **deps:** Bump actions/setup-node from 7.0.0 to 7.1.0 ([#1329](https://github.com/agentclientprotocol/claude-agent-acp/issues/1329)) ([c6510c2](https://github.com/agentclientprotocol/claude-agent-acp/commit/c6510c27c11bb59ad10090c8c18ba60c0fa1c3fa))
|
||||
* keep a subagent's session across SendMessage resumes for subagents RFD clients ([#1321](https://github.com/agentclientprotocol/claude-agent-acp/issues/1321)) ([bd06804](https://github.com/agentclientprotocol/claude-agent-acp/commit/bd06804b67d7b4efa8180db2f36203a5d34c3ac4))
|
||||
* replay each subagent under its live session id for subagents RFD clients ([#1328](https://github.com/agentclientprotocol/claude-agent-acp/issues/1328)) ([21eef57](https://github.com/agentclientprotocol/claude-agent-acp/commit/21eef576557c845bfe9acc66499dab347f813a98))
|
||||
* report a subagent as requires_action while its request is open for subagents RFD clients ([#1322](https://github.com/agentclientprotocol/claude-agent-acp/issues/1322)) ([252126f](https://github.com/agentclientprotocol/claude-agent-acp/commit/252126f61654cf7ac2cad74be53b4c6add569e1f))
|
||||
* report subagents as the subagents RFD defines, for clients that are not AIR ([#1320](https://github.com/agentclientprotocol/claude-agent-acp/issues/1320)) ([5758ac8](https://github.com/agentclientprotocol/claude-agent-acp/commit/5758ac84a4a3b95ace7542a7e71fd58d93a581b3))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* close the open tool calls of a held or settling turn that a cancel ends ([#1221](https://github.com/agentclientprotocol/claude-agent-acp/issues/1221)) ([32c23db](https://github.com/agentclientprotocol/claude-agent-acp/commit/32c23db06d53d7d4e1295d78fc0129f8278d75df))
|
||||
* display active Central helpers as JetBrains Air Gateway ([#1266](https://github.com/agentclientprotocol/claude-agent-acp/issues/1266)) ([d6c3717](https://github.com/agentclientprotocol/claude-agent-acp/commit/d6c371704fb5fd74c3b329a3d756486975e3f180))
|
||||
* drop streamed output of prompts a cancel abandoned ([#1313](https://github.com/agentclientprotocol/claude-agent-acp/issues/1313)) ([1137df3](https://github.com/agentclientprotocol/claude-agent-acp/commit/1137df33aa4fe387916596ff489c02ee48ccbba3))
|
||||
* end a cancelled turn at its interrupted result and deliver Claude Code's follow-up after a cancel ([#1309](https://github.com/agentclientprotocol/claude-agent-acp/issues/1309)) ([b007ac7](https://github.com/agentclientprotocol/claude-agent-acp/commit/b007ac7c45b85755877fee802c2c901a0e3057c6))
|
||||
* end the tool calls of a cancelled turn before it reports the cancel ([#1327](https://github.com/agentclientprotocol/claude-agent-acp/issues/1327)) ([53df1c7](https://github.com/agentclientprotocol/claude-agent-acp/commit/53df1c7b22bf08f9a2866389be812be80f34c8a3))
|
||||
* keep the terminal of a command in the background running until its task ends ([#1312](https://github.com/agentclientprotocol/claude-agent-acp/issues/1312)) ([d074558](https://github.com/agentclientprotocol/claude-agent-acp/commit/d074558e4a2ef4ee6f4b39aecb5ebf36d8c0f84b))
|
||||
* report a command whose result says interrupted as cancelled on the experimental ACP v2 surface ([#1301](https://github.com/agentclientprotocol/claude-agent-acp/issues/1301)) ([b3bed2f](https://github.com/agentclientprotocol/claude-agent-acp/commit/b3bed2f7630f4ddb97978246255ba89ca6e4613e))
|
||||
* route a subagent's PostToolUse hook update to its child session ([#1324](https://github.com/agentclientprotocol/claude-agent-acp/issues/1324)) ([4615a91](https://github.com/agentclientprotocol/claude-agent-acp/commit/4615a9141c12e8cd9093a5bbc0e62a7d778cf0cf))
|
||||
* start a forked session so the client can prompt it at once ([#1317](https://github.com/agentclientprotocol/claude-agent-acp/issues/1317)) ([383bc7d](https://github.com/agentclientprotocol/claude-agent-acp/commit/383bc7dcf0d4d624d2d74405eb3cee6453eb9b11))
|
||||
|
||||
## [0.89.1](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.89.0...v0.89.1) (2026-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* push renamed sessions to list subscribers and reuse one listing across pages ([#1293](https://github.com/agentclientprotocol/claude-agent-acp/issues/1293)) ([92abb8f](https://github.com/agentclientprotocol/claude-agent-acp/commit/92abb8fd4db862c465c4b3a49ea32dac0720de2c))
|
||||
|
||||
## [0.89.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.88.0...v0.89.0) (2026-10-09)
|
||||
|
||||
|
||||
|
||||
88
README.md
88
README.md
@@ -37,14 +37,86 @@ npm install @agentclientprotocol/claude-agent-acp@preview
|
||||
|
||||
### Subagent sessions
|
||||
|
||||
Subagents are exposed only after bilateral capability negotiation. Until the released ACP SDKs
|
||||
preserve the draft `clientCapabilities.subagents` field, a supporting client may advertise
|
||||
`nativeSubagentSessions` in `_meta.jetbrains.air.capabilities`; the adapter mirrors the capability
|
||||
in its initialize response. The canonical field remains supported and takes precedence once it is
|
||||
available. Without either client signal, Agent/Task lifecycle keeps its legacy ordinary ACP
|
||||
tool-call representation and child interactions stay on the root session. Clients that use the
|
||||
historical `_meta["subagent-transcript"]` capability or `forwardSubagentText` session option retain
|
||||
the flattened child transcript behavior. See [AIR extensions](docs/air-extensions.md#native-subagent-sessions).
|
||||
A client that declares `clientCapabilities.subagents`, other than JetBrains AIR, gets each Agent or
|
||||
Task subagent as its own child session, following the [subagents RFD](https://github.com/agentclientprotocol/agent-client-protocol/blob/main/docs/rfds/subagents.mdx):
|
||||
|
||||
- `subagent_update` on the parent announces the child before any of its traffic, with the
|
||||
parent's `title` and `description` for it, and reports its work state: `running`,
|
||||
`requires_action` while one of its requests is open (and `running` again when the last one ends,
|
||||
unless a cancel withdrew it: the child is stopping then), and `idle` when its work ends, with
|
||||
`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.
|
||||
- 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
|
||||
the child's incoming one. Both use the launch's tool use id, and each names the other with the
|
||||
draft RFD's `recipientMessageId` and `senderMessageId`.
|
||||
- A foreground child's answer, from the launch's result, is the child's `session_message` to
|
||||
the parent. In Claude Code's hand-back mode, the child's `SubagentHandback` call is its
|
||||
message to the parent instead, in both transcripts and linked the same way.
|
||||
- A launch whose child is never exposed is shown as a tool call after all, with its final
|
||||
status.
|
||||
- The message of a SendMessage call that resumes the child is a `session_message` in the child's
|
||||
transcript. The SendMessage call stays a tool call of the session that made it.
|
||||
- `session/cancel` on the parent interrupts Claude Code, which stops the foreground children that
|
||||
the turn waits on. Background children keep running, as the RFD prefers; a client stops one with
|
||||
its own `cancel`. Each stopped child reports `idle` with `cancelled` from Claude Code's own
|
||||
events, and its open requests are withdrawn. The adapter ends no child at a cancel itself, so a
|
||||
child that keeps running keeps its open requests and its open tool calls. To spare background
|
||||
children, the adapter declares the SDK option `perTaskStopAffordance`, which a client can turn
|
||||
off in `_meta.claudeCode.options`. That option spares background workflows too, which a client
|
||||
cannot stop, so the adapter stops them after the interrupt, as Claude Code did without it.
|
||||
Background commands keep running after a cancel either way. A tool call of a stopped child that Claude Code reports no
|
||||
result for, such as the command it was running, ends in the child's session before the cancelled
|
||||
prompt resolves. When Claude Code stops a background child after that, or while no prompt runs,
|
||||
the tool call ends right after the child reports `idle`. A child that still runs when Claude Code
|
||||
exits is `idle` with no stop reason.
|
||||
- A child whose parent is not yet known when it ends is not exposed, since the RFD forbids
|
||||
guessing its parent.
|
||||
- A replay restores each child under its live session id, from its own transcript, with the same
|
||||
session messages as live, and its end is an `idle` before the response.
|
||||
A launch without Claude Code's metadata file for the child stays an ordinary tool call of the
|
||||
parent, and a notice, for a client that takes notices, says that subagent history is missing.
|
||||
- After a load or resume, with or without replay, a SendMessage that resumes an agent from before
|
||||
it runs that agent in its session from before. Before the agent's traffic, each of its ancestors
|
||||
that the client was not told about in this connection is announced, parent before child and
|
||||
with no work state, as the RFD requires. An agent whose launch the session's history does not
|
||||
record stays unexposed.
|
||||
- Each running child is announced with the `cancel` capability. `session/cancel` with the child's
|
||||
session id stops that child's current work, through the SDK's `Query.stopTask`, and not its
|
||||
parent's: Claude Code also stops the foreground children it waits on, and the parent's turn goes
|
||||
on. Each stopped child reports `idle` with `cancelled` from Claude Code's own events, and its
|
||||
requests that the stop withdraws report no `running` first. A replayed child, which no longer
|
||||
runs, gets no capabilities. A client cannot prompt or otherwise change a child.
|
||||
|
||||
An [ACP v2](#experimental-acp-v2) client gets the same without declaring anything, since subagents
|
||||
are part of v2's baseline. Each child also reports its work state as its own `state_update`, right
|
||||
after the parent's copy.
|
||||
|
||||
JetBrains AIR keeps the earlier draft of the RFD (`subagent_spawned`, `subagent_state_update`),
|
||||
which it enables with `nativeSubagentSessions` in `_meta.jetbrains.air.capabilities`. See
|
||||
[AIR extensions](docs/air-extensions.md#native-subagent-sessions).
|
||||
|
||||
Without either signal, Agent and Task stay ordinary tool calls and child interactions stay on the
|
||||
root session. Clients that use the historical `_meta["subagent-transcript"]` capability or the
|
||||
`forwardSubagentText` session option retain the flattened child transcript.
|
||||
|
||||
### Experimental ACP v2
|
||||
|
||||
The adapter also serves the [draft ACP v2](https://agentclientprotocol.com/protocol/v2/overview):
|
||||
a client that asks for protocol version 2 or later in `initialize` gets v2, and every other client
|
||||
gets v1. The draft can still change, and this surface changes with it.
|
||||
|
||||
`CLAUDE_AGENT_ACP_PROTOCOL` limits the adapter to one version. With `v1`, a client that asks
|
||||
for v2 is negotiated down to v1. With `v2`, the adapter rejects the `initialize` of a v1 client.
|
||||
`auto`, the default, serves both.
|
||||
|
||||
On v2 the adapter serves the session baseline (`session/new`, `session/list`, `session/resume`
|
||||
with replay, `session/close`, `session/prompt`, `session/cancel`), `session/delete`,
|
||||
`session/fork`, additional directories, config options (the mode is one), plans, notices,
|
||||
compaction, display terminals for commands, permission requests, elicitation, providers, auth, and
|
||||
[subagents](#subagent-sessions). Steering and the AIR extensions are v1 only for now.
|
||||
|
||||
## Contribution Policy
|
||||
|
||||
|
||||
@@ -17,16 +17,20 @@ It describes only this adapter.
|
||||
- [Claude tools and ACP fields](#claude-tools-and-acp-fields)
|
||||
- [Diff patch](#diff-patch)
|
||||
- [Plan file](#plan-file)
|
||||
- [Session hooks](#session-hooks)
|
||||
- [Permission presentation](#permission-presentation)
|
||||
- [Goal](#goal)
|
||||
- [Predicted follow-up prompt](#predicted-follow-up-prompt)
|
||||
- [Recommended config values](#recommended-config-values)
|
||||
- [Async tasks](#async-tasks)
|
||||
- [Agent file-change report](#agent-file-change-report)
|
||||
- [Session failure](#session-failure)
|
||||
- [Native subagent sessions](#native-subagent-sessions)
|
||||
- [Session index](#session-index)
|
||||
- [Background subagents](#background-subagents)
|
||||
- [Context compaction](#context-compaction)
|
||||
- [Question custom answers](#question-custom-answers)
|
||||
- [Load replay end](#load-replay-end)
|
||||
- [Session fork point](#session-fork-point)
|
||||
- [Presentation hints](#presentation-hints)
|
||||
- [Removed keys](#removed-keys)
|
||||
@@ -122,7 +126,8 @@ The `initialize` response of an AIR client carries the agent side of the extensi
|
||||
"asyncTasks",
|
||||
"recommendedValue",
|
||||
"diffPatch",
|
||||
"planFile"
|
||||
"planFile",
|
||||
"backgroundSubagents"
|
||||
],
|
||||
"goal": {
|
||||
"version": 1,
|
||||
@@ -136,26 +141,29 @@ 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` and `sessionListSubscribe`, 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` and `sessionListSubscribe`.
|
||||
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 response to a client that is not AIR has no `_meta.jetbrains` key.
|
||||
|
||||
### Capabilities
|
||||
|
||||
| Capability | Advertised | What the adapter does when the client declares it | Section |
|
||||
| ------------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------- |
|
||||
| `diffPatch` | yes | Sends an exact Git patch in the diff block of an Edit or a Write. | [Diff patch](#diff-patch) |
|
||||
| `sessionFailure` | yes | Sends warnings and errors as typed transcript records. | [Session failure](#session-failure) |
|
||||
| `recommendedValue` | yes | Replaces the `default` model and effort rows with concrete values and a recommendation. | [Recommended config values](#recommended-config-values) |
|
||||
| `asyncTasks` | yes | Publishes background work that is not a subagent as async tasks. | [Async tasks](#async-tasks) |
|
||||
| `agentFileChangeReport` | yes | Accepts a report request on `session/prompt` and sends the changed file list. | [Agent file-change report](#agent-file-change-report) |
|
||||
| `nativeSubagentSessions` | yes | Reports an Agent or Task subagent as a native ACP child session. | [Native subagent sessions](#native-subagent-sessions) |
|
||||
| `planFile` | yes | Sends the path of the plan file in place of the plan text of an ExitPlanMode. | [Plan file](#plan-file) |
|
||||
| `sessionIndex` | on request | Pages, orders and annotates `session/list`; adds rename, archive and a list subscription. | [Session index](#session-index) |
|
||||
| `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) |
|
||||
| `rawInputRendering` | no | Sends no display copy of readable input in `content`. The client renders `rawInput`. | [Tool call contract](#tool-call-contract) |
|
||||
| Capability | Advertised | What the adapter does when the client declares it | Section |
|
||||
| ------------------------ | ------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
||||
| `diffPatch` | yes | Sends an exact Git patch in the diff block of an Edit or a Write. | [Diff patch](#diff-patch) |
|
||||
| `sessionFailure` | yes | Sends warnings and errors as typed transcript records. | [Session failure](#session-failure) |
|
||||
| `recommendedValue` | yes | Replaces the `default` model and effort rows with concrete values and a recommendation. | [Recommended config values](#recommended-config-values) |
|
||||
| `asyncTasks` | yes | Publishes background work that is not a subagent as async tasks. | [Async tasks](#async-tasks) |
|
||||
| `agentFileChangeReport` | yes | Accepts a report request on `session/prompt` and sends the changed file list. | [Agent file-change report](#agent-file-change-report) |
|
||||
| `nativeSubagentSessions` | yes | Reports an Agent or Task subagent as a native ACP child session. | [Native subagent sessions](#native-subagent-sessions) |
|
||||
| `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) |
|
||||
| `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) |
|
||||
| `sessionWorktrees` | with `sessionIndex` | Nothing on its own: the agent lists it to tell that rows carry `agentWorktree` for a worktree Claude Code made. | [List response](#list-response) |
|
||||
| `loadReplayEnd` | no | Sends one `session_info_update` after the whole history replay of a loaded session. | [Load replay end](#load-replay-end) |
|
||||
| `rawInputRendering` | no | Sends no display copy of readable input in `content`. The client renders `rawInput`. | [Tool call contract](#tool-call-contract) |
|
||||
|
||||
The adapter ignores `planContentDelta`.
|
||||
Claude does not stream a plan, so the adapter never sends `contentDelta` (see [Plan file](#plan-file)).
|
||||
@@ -184,11 +192,13 @@ The adapter sends these keys only to an AIR client. "AIR" in the Gate column mea
|
||||
| `commandTitle` | Bash and PowerShell tool call `_meta.jetbrains.air` | the `description` input string | AIR |
|
||||
| `subagent` | Agent and Task tool call `_meta.jetbrains.air` | `true` | AIR |
|
||||
| `skill` | Skill tool call `_meta.jetbrains.air` | `{name, path?}` | AIR |
|
||||
| `mcp` | MCP tool call `_meta.jetbrains.air` | `{server, tool}` | AIR |
|
||||
| `contextCompaction` | compaction tool call or `compaction_update`, `_meta.jetbrains.air` | `{version: 1, trigger?, preTokens?, postTokens?, durationMs?, error?}` | AIR |
|
||||
| `customAnswer` | elicitation schema property `_meta.jetbrains.air` | `{questionId, isCustomAnswer: true}` | AIR, when the client supports form elicitation |
|
||||
| `kind` | session mode `_meta.jetbrains.air` and mode config option value `_meta.jetbrains.air` | `standard`, `plan`, `auto_review`, or `full_access` | AIR |
|
||||
| `kind` | available command `_meta.jetbrains.air` of a skill | `skill` | AIR, when the skill's `SKILL.md` is found; not for a built-in command or an MCP prompt |
|
||||
| `skillPath` | available command `_meta.jetbrains.air` of a skill | absolute path of the skill's `SKILL.md` | AIR, together with `kind: "skill"` |
|
||||
| `loadReplayEnd` | `session_info_update._meta.jetbrains.air` | `true` | `loadReplayEnd` |
|
||||
| `fork` | `session/fork` request `_meta.jetbrains.air` (client to agent) | `{version: 1, messageId, messageFingerprint?, messageOccurrence?}` | none |
|
||||
|
||||
## JetBrains shared keys
|
||||
@@ -204,6 +214,21 @@ The adapter keeps them where they are.
|
||||
| `steering` | `initialize` response `_meta.steering = {supported: true}` | The agent accepts `_session/steering` for a running turn. |
|
||||
| `quota` | `PromptResponse._meta.quota` | Token usage of the turn, also split by model. |
|
||||
| `authStatus` | `initialize` response `agentCapabilities._meta.authStatus` | The agent pushes `_auth/status_update`. The object carries no payload. |
|
||||
| `promptSuggestions` | `session_info_update._meta.promptSuggestions = {suggestions: [{text}]}`, AIR only | The prompt the agent predicts for the next turn. An empty list clears it. |
|
||||
|
||||
## Auth source display
|
||||
|
||||
The connection-scoped `_auth/status_update` continues to report agent-owned authentication, not client-owned `providers/set` routing. When the active key source is `apiKeyHelper`, the adapter reads the effective helper command through SDK `resolveSettings` / the session's `SettingsManager`, including programmatic settings overrides.
|
||||
|
||||
A command containing the whole word `central` and the word sequence `proxy start` is displayed as:
|
||||
|
||||
```json
|
||||
{ "authStatus": { "kind": "gateway", "label": "JetBrains Air Gateway" } }
|
||||
```
|
||||
|
||||
Matching is case-insensitive and accepts multiple whitespace characters between `proxy` and `start`. The command's flags, URL, host and route are not checked. This is a weak display heuristic, not proof of configuration provenance, account ownership or entitlement. Inactive helpers and external cloud backends retain their existing classification. No helper command, key, headers, private route or inferred account is included in this payload.
|
||||
|
||||
Other active helpers use `label: "Anthropic API key"` and `detail: "API key helper"`; unknown key-source identifiers are not exposed as display text. CLI and SDK reads share this presentation, retain stale-probe protection, and suppress unchanged notifications.
|
||||
|
||||
## Zed conventions
|
||||
|
||||
@@ -214,6 +239,7 @@ An AIR client that declares `terminal_output_delta` gets appends instead of `ter
|
||||
A command tool call then has `content: [{type: "terminal", terminalId}]` and `_meta.terminal_info = {terminal_id}`.
|
||||
The output goes in `_meta.terminal_output = {terminal_id, data}`.
|
||||
The end sends `_meta.terminal_exit = {terminal_id, exit_code, signal: null}`.
|
||||
A command that ends without a result (a cancel stopped its subagent, say) gets the exit on the report that closes its tool call, with no exit code (`null`; AIR gets `1`).
|
||||
- A client that declares neither `terminal_output` nor `terminal_output_delta` gets no terminal.
|
||||
The command output is a `console` code block in `content`.
|
||||
- A client that declares `clientCapabilities._meta["terminal-auth"]: true` gets `_meta["terminal-auth"] = {command, args, label}` on the terminal login methods.
|
||||
@@ -306,10 +332,14 @@ For every tool, a client that is not AIR also gets these upstream fields:
|
||||
| `Skill` | `kind: other`, `title: "Load skill: <name>"`. | `_meta.jetbrains.air.skill`. | none |
|
||||
| `TodoWrite`, `TaskCreate`, `TaskUpdate`, `TaskList`, `TaskGet` | The stream reports them as a standard `plan` with entries. Only a permission request shows them as a tool call. | A plan that repeats the previous plan is not sent. | Every plan goes out. |
|
||||
| `ReportFindings` | `kind: think`, `title: "Report <n> findings"`. | With `rawInputRendering`, no copy of the findings in `content`. | none |
|
||||
| MCP tools (`mcp__*`) and other tools | `kind: other`, `title` is the tool name. The result text goes to `content`. | `_meta.is_mcp_tool_call` for MCP. | none |
|
||||
| MCP tools (`mcp__*`) and other tools | `kind: other`, `title` is the tool name. The result text goes to `content`. | `_meta.is_mcp_tool_call` for MCP, and `_meta.jetbrains.air.mcp = {server, tool}`. | none |
|
||||
| Context compaction | `compaction_update` when the client declares `session.compaction`. Otherwise a synthetic tool call. | Both forms carry `_meta.jetbrains.air.contextCompaction`. | See [Context compaction](#context-compaction). |
|
||||
| Background work that is not a subagent | none | With `asyncTasks`, async task updates. | none |
|
||||
|
||||
`_meta.jetbrains.air.mcp` comes from the tool name `mcp__<server>__<tool>`, on `tool_call` and on a `tool_call_update` that changes it.
|
||||
`server` is the part up to the next `__`. `tool` is the whole rest and can contain `__`, as in Claude Code.
|
||||
A name that does not split (`mcp__x`) gets no `mcp` key, and `_meta.is_mcp_tool_call` still marks it.
|
||||
|
||||
## Diff patch
|
||||
|
||||
The diff patch extension lets the adapter send one compact Git patch instead of file text snapshots.
|
||||
@@ -496,6 +526,21 @@ The plan text goes out as before in these cases:
|
||||
AIR then shows the in-memory `plan` text.
|
||||
A client that is not AIR always gets the whole SDK input, like upstream.
|
||||
|
||||
## Session hooks
|
||||
|
||||
The adapter accepts session hooks from any client, without a capability.
|
||||
AIR puts the hooks into `_meta.claudeCode.options.settings.hooks` of `session/new` and `session/load`:
|
||||
|
||||
```json
|
||||
{ "claudeCode": { "options": { "settings": { "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "..." }] }] } } } }
|
||||
```
|
||||
|
||||
- The settings go to the flag settings tier of that session only. Other sessions and the settings files do not change.
|
||||
- Claude Code runs the hooks of every settings tier, so the user, project and IDE hooks of one event all run. `src/tests/session-hooks.test.ts` checks this with the Claude Code binary.
|
||||
- `CLAUDE_MODEL_CONFIG` stays in effect (see [Model configuration](model-configuration.md#precedence)).
|
||||
- A `session/load` with different settings recreates the query, so it applies the new hooks.
|
||||
- Claude asks for no hook trust step.
|
||||
|
||||
## Permission presentation
|
||||
|
||||
The adapter uses standard ACP permission requests and responses.
|
||||
@@ -569,6 +614,7 @@ The client already has the rest of the tool call, because the adapter sends the
|
||||
- `content` is present only with an exact preview patch for a `diffPatch` client.
|
||||
- `locations` is present only when a valid `blockedPath` is not a location of the tool call yet.
|
||||
- `_meta.claudeCode.mcpServer` names the MCP server of an `mcp__*` tool.
|
||||
It is the server name as authored in the config. `_meta.jetbrains.air.mcp.server` on the tool call is the normalized name (characters outside `[a-zA-Z0-9_-]` replaced by `_`), so the two can differ.
|
||||
|
||||
A client that is not AIR gets the upstream request `toolCall`: `toolCallId`, `name`, `status: pending`, the whole `rawInput`, `title`, `kind`, `content`, and `locations`.
|
||||
Its `_meta.claudeCode` carries `toolName`, and `parentToolUseId` for a subagent tool call, next to `mcpServer`.
|
||||
@@ -784,6 +830,37 @@ While that command is pending, the adapter ignores late updates for the previous
|
||||
A matching runtime `active_goal` stays authoritative and adds the iteration, reason, and timestamp.
|
||||
A failed command restores the previous state.
|
||||
|
||||
## Predicted follow-up prompt
|
||||
|
||||
After a turn, the Claude Code CLI can predict the prompt the user is likely to send next.
|
||||
The adapter asks for it with the SDK option `promptSuggestions: true` and forwards the one prediction of the turn.
|
||||
|
||||
ACP has no field for a composer prefill, so the prediction travels outside the AIR namespace:
|
||||
|
||||
```json
|
||||
{
|
||||
"sessionUpdate": "session_info_update",
|
||||
"_meta": { "promptSuggestions": { "suggestions": [{ "text": "Run the tests" }] } }
|
||||
}
|
||||
```
|
||||
|
||||
The key names no vendor, so another client can read it.
|
||||
The adapter sends it only to an AIR client; see the [compatibility rule](#compatibility-rule).
|
||||
|
||||
AIR shows the first non-blank `suggestions[].text` as gray text in an empty composer.
|
||||
Tab fills the composer with it and sends nothing.
|
||||
|
||||
`suggestions: []` clears the prediction the client holds. The adapter clears it:
|
||||
|
||||
- when the next `session/prompt` arrives, because that turn's prediction is spent;
|
||||
- on a `conversation_reset`, because the prediction was drawn from the transcript the reset dropped.
|
||||
|
||||
A prediction the next prompt outran — the user sent it before the adapter read the prediction off the SDK stream — is dropped instead of published.
|
||||
|
||||
The CLI predicts nothing on the first turn of a session, after an API error, in plan mode, and at the account's usage limit, so many turns end with no prediction.
|
||||
A client must treat a turn without one as normal.
|
||||
A caller can turn the whole feature off with `_meta.claudeCode.options.promptSuggestions: false` on `session/new`; a user can turn it off in Claude Code settings or with `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=false`.
|
||||
|
||||
## Recommended config values
|
||||
|
||||
The `recommendedValue` extension lets a client show concrete model and effort choices without an ambiguous `default` row.
|
||||
@@ -920,6 +997,20 @@ When an announced task leaves that list, the adapter reports `stopped`.
|
||||
A later terminal event can correct that state to `completed` or `failed`.
|
||||
At shutdown, the adapter finishes each open task as `failed` or `stopped`.
|
||||
|
||||
### Autonomous turn
|
||||
|
||||
Claude Code can start a turn of its own after the prompt ended, for example to answer the notification of a finished background task.
|
||||
ACP v1 has no session state, so the adapter publishes such a turn as an async task.
|
||||
The task has the id `autonomous-turn-<uuid>`, the name `Claude is working`, `taskType: "turn"`, and `showInTranscript: false`.
|
||||
Its description is the summary of the last task notification, when there is one.
|
||||
The task starts at the SDK `running` state while no prompt is active or queued.
|
||||
When the state stays `running`, as it does while background agents run, it starts at the first root output with no prompt open.
|
||||
It ends `completed` at the result of the turn or at the SDK `idle` state, and `failed` at an error result.
|
||||
A coalesced placeholder result (`num_turns: 0`) does not end it.
|
||||
A prompt that the turn takes in ends the task, because that prompt owns the turn now.
|
||||
A stop request interrupts the turn instead of stopping an SDK task.
|
||||
It returns `{ "stopped": false }` while a prompt waits for its turn, because the interrupt would drop that prompt.
|
||||
|
||||
### Stop request
|
||||
|
||||
The client sends `_session/async_task/stop`:
|
||||
@@ -1170,8 +1261,9 @@ This section covers only the AIR bridge.
|
||||
- The canonical client field is `clientCapabilities.subagents: {}`.
|
||||
- Released ACP SDKs can strip that draft field.
|
||||
AIR can instead declare `nativeSubagentSessions` in `_meta.jetbrains.air.capabilities`.
|
||||
- Either signal enables native subagent sessions. The canonical field takes precedence when it is available.
|
||||
- The agent advertises `nativeSubagentSessions` to AIR, and `agentCapabilities.sessionCapabilities.subagents` to every client.
|
||||
- Either signal enables native subagent sessions, and AIR gets this earlier draft of the RFD whichever it declares.
|
||||
- The agent advertises `nativeSubagentSessions` and `agentCapabilities.sessionCapabilities.subagents` to AIR.
|
||||
- A client that is not AIR and declares `clientCapabilities.subagents` gets the revised RFD instead (`subagent_update`, `session_message`), see the README.
|
||||
- With native sessions, the adapter sends `subagent_spawned` and `subagent_state_update`.
|
||||
The child output goes to the child session. The Agent or Task tool call is not the subagent card.
|
||||
- `subagent_spawned` can carry an optional `prompt`: the exact prompt of the child session.
|
||||
@@ -1190,8 +1282,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`) and `sessionListSubscribe` (it supports `_session/list/subscribe` and `_session/list/unsubscribe`).
|
||||
The client does not declare these three; declaring them without `sessionIndex` enables nothing.
|
||||
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.
|
||||
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.
|
||||
@@ -1259,6 +1351,7 @@ Each row carries `_meta.jetbrains.air` with flat row fields of the RFDs. `archiv
|
||||
"lastPromptAt": "2026-10-06T09:57:40.000Z",
|
||||
"model": "claude-opus-5-5",
|
||||
"forkedFrom": "8f0c1d2e-0000-4000-8000-000000000000",
|
||||
"agentWorktree": true,
|
||||
"state": "idle",
|
||||
"lastTurnEndedAt": "2026-10-06T09:58:10.000Z",
|
||||
"cost": { "amount": 1.23, "currency": "USD" }
|
||||
@@ -1268,10 +1361,19 @@ Each row carries `_meta.jetbrains.air` with flat row fields of the RFDs. `archiv
|
||||
- `lastPromptAt` is the time of the last real user prompt in the transcript tail: prompt text (the predicate of the SDK title extractor) or an image or a document, but not a tool result, a meta or compact summary record, a slash command or an interrupt. When a long answer or tool output follows the last prompt, the tail window grows (256 KB, 1 MB, 4 MB) to find it; it is omitted only when the last 4 MB hold no prompt.
|
||||
- `model` is the model of the last assistant message in the tail; messages the CLI makes up (`<synthetic>`) do not count.
|
||||
- `forkedFrom` is the parent session id that the SDK `forkSession` and the CLI fork write on every copied record (`forkedFrom.sessionId`). Sessions started any other way have none.
|
||||
- `agentWorktree` is `true` when the session runs in a linked git worktree that Claude Code made for it (`claude --worktree`, or the worktree tools), and omitted otherwise, never `false`. The transcript rule is the one of AIR's native Claude provider; the adapter adds the git check of the row's `cwd`:
|
||||
- the transcript holds `worktree-state` records, `{"type":"worktree-state","worktreeSession":{originalCwd, worktreePath, worktreeName, worktreeBranch, originalBranch, originalHeadCommit, sessionId, hookBased?, enteredExisting?}}`, which the CLI writes when the session enters or leaves a worktree;
|
||||
- the last line that parses as a `worktree-state` record decides, whatever it holds: the session is marked only when its `worktreeSession` is an object without `enteredExisting: true` (`enteredExisting` means a worktree that existed before, so not made for the session). Any other `worktreeSession` (`null`, which the CLI writes when the session leaves the worktree, a missing key, or a value that is no object) means not marked, and an older record does not count then. Only lines that are not JSON, such as the cut first line of a window, are skipped;
|
||||
- the row's `cwd` must lie in a linked worktree: the adapter reads the git files, without running git. It takes the nearest `.git` above `cwd`, follows a `.git` file to its git dir, reads `commondir`, and requires the git dir to be `<git-common-dir>/worktrees/<name>`. This is checked on every `session/list` and on every row a subscription reads, not cached. The main checkout, a submodule and a directory outside a repository never get the field, and neither does a worktree the user made (it has no record). The adapter does not use `environment.isWorktree` or `git worktree list`;
|
||||
- `hookBased` is not read: a worktree a `WorktreeCreate` hook made is marked only when its directory is a linked git worktree as above, so a hook that makes a plain directory or a checkout of another VCS gives no field. The repository is the nearest one to `cwd`: a session in a repository nested in a linked worktree is not marked, and one in a linked worktree of a nested repository is;
|
||||
- the record is read from the 64 KB tail, else from the 64 KB head, and from the wider tail when the window grows to find the last prompt (see `lastPromptAt`). `claude --worktree` writes it at the start of the session, and the CLI writes it again with the other session metadata at the end of the transcript (on exit and as the transcript grows), so one of these windows almost always holds the latest one. A session that entered or left a worktree in the middle of a long transcript, with no metadata written since, keeps its old value until the CLI writes the metadata again;
|
||||
- a session that made a worktree mid-session (EnterWorktree) is marked too: the CLI moves its transcript to the worktree (a `relocated` record), so the row's `cwd` is the worktree path, not the directory the session started in. The row's `cwd` is always the session's own directory, so the worktree path;
|
||||
- a subscription does not send a row again when only git metadata changes, such as a manual `git worktree remove` or `git worktree move`: the field is checked again only when the transcript changes, or when the rescan finds that the worktrees of the scope changed and resolves the rows again. `session/list` checks it on every read.
|
||||
- `state` is `running`, `idle`, `requires_action`, or `error`, never `unknown`. `error` means the session is not running and its last turn ended with an error; a user cancel or interrupt is no error, and a new turn clears it:
|
||||
- a session this connection runs: the SDK `session_state_changed` state; `error` instead of `idle` when its last turn failed (an error result, `is_error` or an `error_*` subtype, that was not a cancel). A session whose query failed here (not a cancel) is reported as any other session, but `error` until its transcript shows a prompt or a turn end after the failure, unless a busy or waiting interactive CLI holds it;
|
||||
- a session no live Claude Code process holds: `idle`, or `error` when its last turn ended with an API error (an assistant record with `isApiErrorMessage: true`, also when the CLI's turn-end records follow it, with no prompt after it);
|
||||
- a session that an interactive CLI holds (not an `sdk-*` entrypoint) whose registry status is newer than the transcript: `busy` and `shell` are `running`, `waiting` is `requires_action`, `idle` is `idle`, or `error` after such an API error;
|
||||
- any other held session whose last turn ended with an open background task: `running`, also after 10 minutes, until the registry status is `idle`. Claude Code wakes the session again when the task ends, so the session gets one `idle` at the last turn end, not one for each task. A background shell command (`toolUseResult.backgroundTaskId`) or an async sub-agent (`isAsync` with an `agentId`) opens a task. Its `<task-notification>` with a `<status>`, as a user record or a `queue-operation` `remove`, or the result of a `TaskStop` call closes it. A notification without a `<status>`, such as the check-in of a quiet shell or a `Monitor` event, keeps it open. Sidechain records open no task. The tail gives the tasks on top of those of an earlier scan when the file only grew within the tail; else the whole transcript is read, and only for such a held session. A background task that a sub-agent starts is not seen: it is only in the transcript of the sub-agent;
|
||||
- any other held session: `idle` after a finished turn (an assistant `end_turn`, a user interrupt, or the CLI turn-end records), `error` after a turn that ended with an API error, `running` for an unfinished turn written in the last 10 minutes, and no state otherwise.
|
||||
- The live processes come from `<config>/sessions/<pid>.json`, with the liveness rules of Claude Desktop: `kill(pid, 0)` (or `EPERM`), a matching `procStart`, no foreign `pidDomain`, and records older than 24 hours only with a matching `procStart`.
|
||||
The adapter never reads the `.key` files there and never changes the directory.
|
||||
@@ -1363,7 +1465,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.
|
||||
- `sessions` holds the full current row of each session in scope that appeared, or whose `title`, `lastPromptAt`, `state`, `lastTurnEndedAt`, `cost`, `model`, `forkedFrom` 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.
|
||||
- `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.
|
||||
- Archive and unarchive are row changes (`archived`): their title records are a transcript change. The agent applies no archive filter: the client filters.
|
||||
@@ -1391,7 +1493,8 @@ It is idempotent, and an unknown id also returns `{}`.
|
||||
- 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.
|
||||
A `sessionIndex` client sees such a session as archived (`archived: true`), as AIR's native Claude path does.
|
||||
- any other client: the SDK delete, as before.
|
||||
- any other client: the SDK delete. A session without a transcript (unknown, deleted before, or
|
||||
never prompted) succeeds, as ACP asks.
|
||||
|
||||
`session/close` of a session that is not loaded returns `{}` for every client.
|
||||
|
||||
@@ -1409,21 +1512,22 @@ It is idempotent, and an unknown id also returns `{}`.
|
||||
The session index is the `_meta` form of two ACP RFDs: "Session list extensions: limit, order, row metadata and change hints" and "Session Archive and Unarchive" (#2161).
|
||||
Names and semantics follow them; the transport differs, and so do the `archived` list filter and the archive of a running session (see [Relation to ACP RFD #2161](#relation-to-acp-rfd-2161)):
|
||||
|
||||
| Extension | RFD |
|
||||
| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
||||
| capability `sessionIndex` (`_meta.jetbrains.air`) | `sessionCapabilities.list.limit` |
|
||||
| capability `sessionArchive` (`_meta.jetbrains.air`) | `sessionCapabilities.archive` (#2161) |
|
||||
| capability `sessionRename` (`_meta.jetbrains.air`) | `sessionCapabilities.setTitle` (#1987) |
|
||||
| `_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 |
|
||||
| row `_meta.jetbrains.air.lastPromptAt`, `model`, `forkedFrom`, `state`, `lastTurnEndedAt`, `cost` | `SessionInfo` fields of the same names; the RFD `state` has no `error` |
|
||||
| row `_meta.jetbrains.air.archived` | `SessionInfo.archived` (#2161) |
|
||||
| `session_info_update` `_meta.jetbrains.air.archived` | `SessionInfoUpdate.archived` (#2161) |
|
||||
| capability `sessionListSubscribe`, `_session/list/subscribe`, `_session/list/unsubscribe` | none |
|
||||
| `_session/list/changes { subscriptionId, sessions, removed }` | none |
|
||||
| `_session/archive`, `_session/unarchive` | `session/archive`, `session/unarchive` (#2161) |
|
||||
| `_session/rename` | `session/set_title` (#1987) |
|
||||
| Extension | RFD |
|
||||
| ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| capability `sessionIndex` (`_meta.jetbrains.air`) | `sessionCapabilities.list.limit` |
|
||||
| capability `sessionArchive` (`_meta.jetbrains.air`) | `sessionCapabilities.archive` (#2161) |
|
||||
| capability `sessionRename` (`_meta.jetbrains.air`) | `sessionCapabilities.setTitle` (#1987) |
|
||||
| `_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 |
|
||||
| row `_meta.jetbrains.air.lastPromptAt`, `model`, `forkedFrom`, `state`, `lastTurnEndedAt`, `cost` | `SessionInfo` fields of the same names; the RFD `state` has no `error` |
|
||||
| row `_meta.jetbrains.air.archived` | `SessionInfo.archived` (#2161) |
|
||||
| `session_info_update` `_meta.jetbrains.air.archived` | `SessionInfoUpdate.archived` (#2161) |
|
||||
| capability `sessionWorktrees`, row `_meta.jetbrains.air.agentWorktree` | none yet: proposed in the session list extensions RFD (agentclientprotocol/agent-client-protocol#2323) as `SessionInfo.agentWorktree`, with the capability named `agentWorktrees` there, not `sessionWorktrees` |
|
||||
| capability `sessionListSubscribe`, `_session/list/subscribe`, `_session/list/unsubscribe` | none |
|
||||
| `_session/list/changes { subscriptionId, sessions, removed }` | none |
|
||||
| `_session/archive`, `_session/unarchive` | `session/archive`, `session/unarchive` (#2161) |
|
||||
| `_session/rename` | `session/set_title` (#1987) |
|
||||
|
||||
### Relation to ACP RFD #2161
|
||||
|
||||
@@ -1438,6 +1542,45 @@ It differs in transport, until the RFD lands in the SDK, in the list filter, and
|
||||
- the list parameter is `_meta.jetbrains.air.list.archived`, and the state is `_meta.jetbrains.air.archived` on rows and in `session_info_update`, not the `archived` fields;
|
||||
- an AIR client without `sessionIndex` still archives with `session/delete` (see [Delete and close](#delete-and-close)), which the RFD asks clients not to do.
|
||||
|
||||
## Background subagents
|
||||
|
||||
A prompt turn can start a background Agent or Task subagent.
|
||||
A SendMessage tool call can also resume a subagent that ended.
|
||||
Claude Code sends the result of the turn when the main agent stops, also while such a subagent still runs.
|
||||
|
||||
### Hold without the capability
|
||||
|
||||
A client that does not declare `backgroundSubagents` gets the hold of issues #864 and #866.
|
||||
The adapter keeps the `session/prompt` request open while a subagent that the turn started or resumed runs.
|
||||
The subagent output, its permission requests and the followup summary of the model then go out inside the turn.
|
||||
The turn ends at the followup result, at an `idle` with no live subagent, at `session/cancel`, or when the next prompt starts.
|
||||
The hold protects a client that stops listening at the prompt response.
|
||||
|
||||
### Behavior with the capability
|
||||
|
||||
AIR declares `backgroundSubagents` in `initialize.clientCapabilities._meta.jetbrains.air.capabilities`.
|
||||
The capability applies to ACP v1 only. ACP v2 keeps the hold, and the agent does not advertise the capability to ACP v2.
|
||||
|
||||
- The turn ends at its result with the stop reason of the result, usually `end_turn`.
|
||||
- The subagent updates, permission requests and elicitations go out after the prompt response.
|
||||
- The routing does not change: the native child session, else the root session with the parent tool call in `_meta.claudeCode.parentToolUseId`.
|
||||
- The followup cycle after a subagent ends is an autonomous cycle. Its updates go out of a turn. Its result does not end a turn.
|
||||
- A steer with no turn in flight starts a new turn, or returns `promptRequired` when the client asks for it.
|
||||
- `session/cancel` with no turn works as before: the adapter interrupts the SDK query and reports each native subagent session as cancelled.
|
||||
- The tool call of a running subagent does not fail the incomplete tool call check of the turn.
|
||||
- The [agent file-change report](#agent-file-change-report) of the turn does not include an edit that a subagent makes after the result.
|
||||
- An open permission request of a subagent counts as an open request for the next turn and for the steer priority.
|
||||
|
||||
### Plan approval out of a turn
|
||||
|
||||
The followup cycle can call ExitPlanMode. AIR then gets the permission request out of a turn.
|
||||
The request has no clear-context option, because the fresh query must continue an ACP turn.
|
||||
"No, keep planning" stops only the followup cycle. There is no turn to end.
|
||||
|
||||
A prompt turn owns the request when the turn is active, or when Claude Code asks before the echo of its prompt.
|
||||
Then the request has the clear-context option.
|
||||
A turn that took over a followup cycle also owns the request of that cycle, and the accepted plan continues in that turn.
|
||||
|
||||
## Context compaction
|
||||
|
||||
The adapter implements the ACP session compaction RFD for a client that declares `clientCapabilities.session.compaction`.
|
||||
@@ -1493,6 +1636,32 @@ For AIR, the companion field also carries the same value under the root key `_me
|
||||
Released AIR versions read only that key.
|
||||
A client that is not AIR gets neither key.
|
||||
|
||||
## Load replay end
|
||||
|
||||
ACP has no signal that the history replay of `session/load` is over, other than the response.
|
||||
The adapter replays the transcript while the Claude Code CLI starts, so the history can arrive long before the response.
|
||||
|
||||
The client declares `loadReplayEnd` in its capabilities. The agent does not advertise it.
|
||||
After the history replay of the loaded session, the agent then sends one more update for that session:
|
||||
|
||||
```json
|
||||
{
|
||||
"sessionId": "<loaded session>",
|
||||
"update": {
|
||||
"sessionUpdate": "session_info_update",
|
||||
"_meta": { "jetbrains": { "air": { "version": 1, "loadReplayEnd": true } } }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- The update has no `title` and no `updatedAt`. A `null` there would clear the value.
|
||||
- It comes after every replayed update, the subagent history that the replay sends to the child sessions included, and before the `session/load` response.
|
||||
- It comes also for an empty history.
|
||||
- It comes for `session/load` of a new or an already live session, and for the ACP v2 `session/resume` with `replayFrom: {type: "start"}`.
|
||||
- It does not come when the replay stops or fails. The replay stops when the CLI start fails during the replay.
|
||||
- It does not come for a live update, and it does not come to a client without the capability.
|
||||
- The `session/load` can still fail after it, for example when the CLI start fails after the replay. The event means that the recorded history arrived whole. The client decides what to do with the failed load.
|
||||
|
||||
## Session fork point
|
||||
|
||||
AIR can fork a session at one agent message.
|
||||
@@ -1525,6 +1694,8 @@ It adds this object to the `session/fork` request:
|
||||
When only one stored message has the fingerprint, the adapter uses that message.
|
||||
- When no message matches, the request fails with `invalidParams`.
|
||||
- The fork keeps the history up to that message.
|
||||
- With or without this object, the forked session runs once the response arrives, so the client can prompt it without `session/load` or `session/resume`.
|
||||
A later load replays its history into it, and a later resume returns it. A load or resume with other settings (`cwd`, MCP servers, additional directories, or the session options in `_meta`) restarts it, as for any loaded session.
|
||||
|
||||
## Presentation hints
|
||||
|
||||
|
||||
47
docs/background-work-experiment.md
Normal file
47
docs/background-work-experiment.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Background work RFD experiment
|
||||
|
||||
This branch prototypes [RFD 2452](https://github.com/agentclientprotocol/agent-client-protocol/pull/2452)
|
||||
at revision `33c473ba0723a0f6ffcfcbb54fd780c05df7b208`. It is an opt-in local
|
||||
experiment, not a released protocol implementation.
|
||||
|
||||
Build with `npm ci --ignore-scripts` and `npm run build`. Launch with
|
||||
`ACP_BACKGROUND_WORK_EXPERIMENT=1 node dist/index.js`. Omit the environment
|
||||
variable to restore normal behavior. No package publication is needed.
|
||||
|
||||
The message-stream bridge works on ACP v1 and v2. It reuses the installed
|
||||
native asynchronous-task adapter and its private negotiation internally. Known
|
||||
background shells keep the existing tool call `in_progress`; state snapshots
|
||||
reference that tool. `tool_call/cancel` is translated to the existing native
|
||||
task stop request. A successful response is acceptance only. A native terminal
|
||||
event finalizes the tool. ACP v1 represents cancellation as `failed` because its
|
||||
tool status enum has no `cancelled`; ACP v2 uses `cancelled`.
|
||||
|
||||
Native replace-semantics task levels establish whether the list is complete.
|
||||
Before a native level arrives, after a malformed level, or while any native task
|
||||
cannot be mapped to an announced tool, `background` is `null`. This includes
|
||||
unmapped monitors, scheduled tasks and subagents. The prototype does not claim
|
||||
their full RFD support. Forks start with `background: []`. Reconnects remain
|
||||
unreported until native current state and referenced entities are available.
|
||||
|
||||
This bridge does not upgrade the SDK schema. A client must accept the draft
|
||||
fields and raw `tool_call/cancel`, and must treat repeated `idle` snapshots
|
||||
without a stop reason as list changes rather than new completions. The private
|
||||
negotiation can also select the adapter's existing client-specific tool rendering;
|
||||
this is a bounded experiment, not general client capability negotiation.
|
||||
|
||||
Suggested manual evaluation: start a bounded background shell, finish the
|
||||
foreground turn, cancel one shell, and separately allow a shell to finish while
|
||||
idle. Verify that the tool remains in progress and listed until native evidence
|
||||
ends it. Send foreground `session/cancel` while background work is listed, then
|
||||
close the session. Reconnect and fork must not invent live tools from history.
|
||||
Exercise pending permissions separately; deterministic protocol tests are not
|
||||
evidence of live native permission or session-close behavior.
|
||||
|
||||
A tool already reported final before native background discovery is never revived.
|
||||
That task remains unreported by this bridge. Native subagents also remain unreported
|
||||
while associated, including an idle child whose own background state is unknown.
|
||||
|
||||
Codex list disappearance proves the process ended but supplies no exit outcome.
|
||||
The existing native adapter reports this as stopped, represented here as cancelled
|
||||
(v2) or failed (v1); only an explicit native command completion supplies a reliable
|
||||
completed/failed outcome. This is a known limit of the experiment.
|
||||
@@ -42,15 +42,26 @@ node dist/index.js
|
||||
|
||||
## Precedence
|
||||
|
||||
When an ACP caller provides `settings` via `_meta.claudeCode.options.settings` in the `sessions/create` request, `CLAUDE_MODEL_CONFIG` is ignored entirely. The env var is a deployment-level fallback for cases where the caller does not configure model settings itself.
|
||||
An ACP caller can provide `settings` via `_meta.claudeCode.options.settings` in `session/new` or `session/load`, as an object or a path to a settings file. The adapter merges `CLAUDE_MODEL_CONFIG` into those settings, so a caller that only adds hooks keeps the model settings. When both set the same key, the caller wins; the merge is shallow, so a caller's `modelOverrides` replaces the env var's map.
|
||||
|
||||
| Source | Priority |
|
||||
| -------------------------------------------- | ----------------------------------------------------- |
|
||||
| `_meta.claudeCode.options.settings` (caller) | Highest — used if present |
|
||||
| `CLAUDE_MODEL_CONFIG` (env var) | Fallback — used only when caller provides no settings |
|
||||
| Source | Priority |
|
||||
| -------------------------------------------- | ---------------------------------------------- |
|
||||
| `_meta.claudeCode.options.settings` (caller) | Highest — wins for each key it sets |
|
||||
| `CLAUDE_MODEL_CONFIG` (env var) | Used for the keys that the caller does not set |
|
||||
|
||||
## Format details
|
||||
|
||||
- The value must be valid JSON. Invalid JSON will cause session creation to fail with a parse error.
|
||||
- Only `modelOverrides` and `availableModels` keys are read; other keys in the JSON are ignored.
|
||||
- Both fields map directly to the Claude Agent SDK's `Settings` type.
|
||||
|
||||
## Models of a `providers/set` gateway
|
||||
|
||||
When an ACP client routes the agent through a gateway with `providers/set`, the adapter lists the gateway's models before `providers/set` returns, with the provider headers:
|
||||
|
||||
- `anthropic`: `GET {baseUrl}/v1/models`, paginated by `has_more`/`last_id`.
|
||||
- `bedrock`: `GET {baseUrl}/foundation-models` (Bedrock `ListFoundationModels`, `modelSummaries[].modelId`). Only `anthropic.` ids count. The cross-region prefix `global` when listed, else that of the first listed id with one (`eu` for `eu.anthropic.claude-sonnet-5`), becomes the session's `ANTHROPIC_BEDROCK_REGION_PREFIX`, and ids with other prefixes, or without one, are dropped.
|
||||
|
||||
The model picker then offers only the SDK models whose resolved model the gateway lists, matched without dates, version suffixes (`-v1:0`), `-fast`, `@` suffixes, context hints (`[1m]`) and provider prefixes, after the `availableModels` allowlist above. On Bedrock, a model the CLI resolved to another region prefix is dropped as well. When the gateway does not list the model a session runs, that is the default model or a resumed session's last model, the session switches to the first remaining picker row.
|
||||
|
||||
A 4xx answer means the gateway has no listing: `providers/set` succeeds and nothing is narrowed. A failed listing (network error, a status other than 2xx or 4xx, unexpected body, the 10 s timeout, an invalid header, or a base URL with credentials) fails `providers/set` with a JSON-RPC internal error, and the previous provider stays in effect. A listing that matches no SDK model leaves the session with no model to offer: there is no model config option. Gateways set through `authenticate`, `vertex` providers, and native routing are not listed. Setting the same gateway again (the same protocol, base URL and headers) reuses its listing, unless it answered 4xx; `providers/set` and `providers/disable` apply in the order they arrive, and logout clears the routing after `claude auth logout`, including a `providers/set` still listing; `providers/disable` and logout drop it.
|
||||
|
||||
@@ -82,10 +82,10 @@ import {
|
||||
client as acpClient,
|
||||
methods,
|
||||
ndJsonStream,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
} from "@agentclientprotocol/sdk/unstable";
|
||||
// Type-only: Node's type stripping does not elide value imports, and these two
|
||||
// exist only in the .d.ts — importing them normally is a runtime SyntaxError.
|
||||
import type { AnyMessage, Stream } from "@agentclientprotocol/sdk";
|
||||
import type { AnyMessage, Stream } from "@agentclientprotocol/sdk/unstable";
|
||||
|
||||
// ---------------------------------------------------------------- steering
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
ndJsonStream,
|
||||
type PromptRequest,
|
||||
type PromptResponse,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
} from "@agentclientprotocol/sdk/unstable";
|
||||
|
||||
/** The steering extension method, per the ACP steering wire protocol. */
|
||||
const STEERING_METHOD = "_session/steering";
|
||||
|
||||
518
package-lock.json
generated
518
package-lock.json
generated
@@ -1,69 +1,69 @@
|
||||
{
|
||||
"name": "@agentclientprotocol/claude-agent-acp",
|
||||
"version": "0.89.0",
|
||||
"version": "0.94.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@agentclientprotocol/claude-agent-acp",
|
||||
"version": "0.89.0",
|
||||
"version": "0.94.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "1.8.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.293",
|
||||
"@agentclientprotocol/sdk": "2.0.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.303",
|
||||
"diff": "9.0.0",
|
||||
"zod": "4.6.5"
|
||||
"zod": "4.6.7"
|
||||
},
|
||||
"bin": {
|
||||
"claude-agent-acp": "dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "0.132.1",
|
||||
"@anthropic-ai/sdk": "0.135.0",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@tsconfig/node22": "22.0.6",
|
||||
"@types/node": "26.6.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.71.1",
|
||||
"@typescript-eslint/parser": "8.71.1",
|
||||
"@types/node": "26.6.5",
|
||||
"@typescript-eslint/eslint-plugin": "8.71.2",
|
||||
"@typescript-eslint/parser": "8.71.2",
|
||||
"ajv": "8.20.0",
|
||||
"ajv-formats": "3.0.1",
|
||||
"eslint": "10.12.0",
|
||||
"eslint": "10.13.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"globals": "17.13.0",
|
||||
"prettier": "3.9.9",
|
||||
"prettier": "3.9.10",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "5.0.3"
|
||||
"vitest": "5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@agentclientprotocol/sdk": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.8.0.tgz",
|
||||
"integrity": "sha512-fbP7qHZsFyTJVT0/RPrcWJ8An26qLXHEuGCHHEKDyxD1xpXV+Ejyh2LAE6vBAFePkpVjZeeohnbgvuEhZ7wLMg==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-2.0.0.tgz",
|
||||
"integrity": "sha512-Za5gWOPQ44aBOdegDJZ+OjCAPn7POc0cw8BlFJQ1mmUY1OoUWxRi3+QZT4gdZnyEWgKXyfeGRb0Oo1ag9YkQ1w==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
"zod": "^3.25.76 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk": {
|
||||
"version": "0.3.293",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.293.tgz",
|
||||
"integrity": "sha512-DVfn46SmpfB7xaKuUHsAC+qwY27hOLHNbvF7FoNOAXLZPRTeCr68SjhBjlFOtk+jz5Nf7pfUwnIUDFcdVih6Dg==",
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.303.tgz",
|
||||
"integrity": "sha512-XBql04V40xr+fmGJh8AmIpqRZKFNrs99BzXzzSVlx+uE5NjFCzKmkNKTdMD0DQtDvFJlijOLoh1siaH+au9Psw==",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.293",
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.293",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.293",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.293",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.293",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.293",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.293",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.293"
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.303"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@anthropic-ai/sdk": ">=0.93.0",
|
||||
@@ -72,9 +72,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": {
|
||||
"version": "0.3.293",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.293.tgz",
|
||||
"integrity": "sha512-E2rwiiHr3LwYzBuTROVDpJkts4QmYCrW39ZIN1qixVSNQhSEiz5LjaKxzrwq0xgPgQDU0vd2fLMG1k72BgD24w==",
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.303.tgz",
|
||||
"integrity": "sha512-QAWV9Zd7i7FQDaUSwZNVXJirqCS9CSF4aPwbkGwLvLH83qcfFOmnyxWUg1H7dU6tJFLOCyDLQzc3xzUA4dakoA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -85,9 +85,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": {
|
||||
"version": "0.3.293",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.293.tgz",
|
||||
"integrity": "sha512-UJD8kX1XDhMqs/0LLxD012h+S9U3Q8XPbRSNcnPQEMNAoEF+lh898xPbVnkUgPKl3jUOUyqYRwFRwDiUJD+GJg==",
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.303.tgz",
|
||||
"integrity": "sha512-o931musaryuq04fMaEjceCEmgsBrLh39WvwsQWmv070h9jXS8EmWRFpdSILv1xrUFXiukRSq8yUDRVzg+FvTug==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -98,9 +98,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": {
|
||||
"version": "0.3.293",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.293.tgz",
|
||||
"integrity": "sha512-B3M5gw6pPH/ckMtrLtGg4ZY1adLVnPoXkEV+Ruu0j/gdFzwuLWR64Spa0RCsbo7BXVN6w5NTqkuZk+HBfsPUZA==",
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.303.tgz",
|
||||
"integrity": "sha512-c04i9i1BU3j9G3D7XdVzXTdM3/sDpQH+1o8jNcMPnztOTYbNFmH/cacgeX9x3N112r+5VhCEHXGILoe5GxVxeg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -111,9 +111,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": {
|
||||
"version": "0.3.293",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.293.tgz",
|
||||
"integrity": "sha512-l5Nfwox69bBMEccSQfPQFOq9hbSqQIMsi4H/UXaMSsqgnxunXuKEBXb5klXZJwk4vUt/NzeqAjB/UFbKqW29HQ==",
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.303.tgz",
|
||||
"integrity": "sha512-a/OsLTEx9ly9hC26cXidg9mtTMA00SQnEiTN9P4IPNj64uSp/7uBnlWl0VeUoVFbgKYYq6XrGRcD6F1ilQsYxg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -124,9 +124,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": {
|
||||
"version": "0.3.293",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.293.tgz",
|
||||
"integrity": "sha512-MtqXROSQiuqOCNXfC4k3/3DVfgTNLEdaT5jp/omaZeiPtnRtfCz2KTtBTVG4IwbEDFaKsqfzPsPFzN7X6avwkg==",
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.303.tgz",
|
||||
"integrity": "sha512-lsm1sX2+9Wos8glBbn86M8leFuK60LKB1cJdS0b2HiTSwiDCqpUKYRGJNUlW1JdUTwJB5nDv6S8/FOJExm1/tw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -137,9 +137,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": {
|
||||
"version": "0.3.293",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.293.tgz",
|
||||
"integrity": "sha512-5N30l2Y17ZV9q0TGCMXtrkTleX+RVtUMoOL+48qQ7ZrKWGYYBtVDS59cIXDv+TAGqoyYVmklw0W2Sy4dA40BwQ==",
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.303.tgz",
|
||||
"integrity": "sha512-yixAa3ZOZd1hiI4mpoukzT9TnuDCzSEHyh7pHcbYTRmdRE2y8bAOmI0eiMFJPivnG4iyGhS2C4XNAe6hAjl30w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -150,9 +150,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": {
|
||||
"version": "0.3.293",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.293.tgz",
|
||||
"integrity": "sha512-WvPf8cXna446bTx2U4WVQc5+iUgDn94ve8aauvWwcG8n9oa+1wNCKNGFxk8HvS7DQJhF8dfrCbcF068uNFqYNw==",
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.303.tgz",
|
||||
"integrity": "sha512-AnEseiw5E1JF3XRaGIZN880V/i2OxCaTOQJF9F1+oELleSdvLnCUxCE6/baGvaZjrgvffO9YbYChBpgUKj9j7A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -163,9 +163,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": {
|
||||
"version": "0.3.293",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.293.tgz",
|
||||
"integrity": "sha512-SVaIDHtpCwM0r0m9jzue1U7yktIZ1eJhR1O3e0UCHYTJluaIXty5m6DzqhG4rjnEoNlEAl9rQLqPigS/R5+IiQ==",
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.303.tgz",
|
||||
"integrity": "sha512-j5kABFEQ6x6T5AB/glmn0r9VwZRbzcH+nSEOWglB69ODNfkZA2btjcz9r794+2BLqJ9QAyswiYWcOX4sDz/RmQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -176,11 +176,12 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/sdk": {
|
||||
"version": "0.132.1",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.132.1.tgz",
|
||||
"integrity": "sha512-Dg+Rd0effi+4FyNwgEx6JXl9COefsZ3C7BOPSKYIWboQOEIi9HXtu1WBdND0DIgMF7Qt1vuCSjsbs7sjUkzJuQ==",
|
||||
"version": "0.135.0",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.135.0.tgz",
|
||||
"integrity": "sha512-WWrdL57JhU5KoynpTjt9GXK6k7ud9nPw2uw/Trn+oBXh6s0KpjhAccWZm2Qxw08ARYG1UDQj5iC+JO42j1rt/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
"json-schema-to-ts": "^3.1.1",
|
||||
"standardwebhooks": "^1.0.0"
|
||||
},
|
||||
@@ -358,9 +359,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@hono/node-server": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.4.tgz",
|
||||
"integrity": "sha512-2g1qeHl4BduwDn6L8nkAvDrPUWPMlkQ//a5NhP/zIyaXKMjZNRV+cG9Csu+BiJFf6+68pqzjf4Hi5sJGKHuS1g==",
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.5.tgz",
|
||||
"integrity": "sha512-MgP/I06GdvsilDspMk/qmRzpk8LOUStP6jFAbSRi40W09DhoyUn+Mj7eyKICPqeYKybOjVFLVIAQzvu1bfIa7Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
@@ -529,10 +530,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/api": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
|
||||
"integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-project/types": {
|
||||
"version": "0.153.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz",
|
||||
"integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==",
|
||||
"version": "0.154.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.154.0.tgz",
|
||||
"integrity": "sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@@ -541,9 +551,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm-eabi": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.13.tgz",
|
||||
"integrity": "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.14.tgz",
|
||||
"integrity": "sha512-aWR+XKH9+rW7m5fWq/5360J3mHycrx/o6xZM2DQFdJnTMdyP7/WzxPjhtLbVWdKo2y612/4U1bWqb3HQhiF7vg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -559,9 +569,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm64": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.13.tgz",
|
||||
"integrity": "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.14.tgz",
|
||||
"integrity": "sha512-QiVxHofl6A8lQU7WAtOUY4QMVZXohxwz7WJJoeh8NBEUyKfIkMRKhYyq8az/1RypNiIXzNTtiS6KwHz0erxYRQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -577,9 +587,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.13.tgz",
|
||||
"integrity": "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.14.tgz",
|
||||
"integrity": "sha512-lTQOzHuBIooh3jIr5XINQkjPGEuGy+Dsy0meXDr6Sjty0b99kYgGh4uBC/8mJX1T0kC8d6S0aPP1Y+bHkh6aBw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -595,9 +605,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-x64": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.13.tgz",
|
||||
"integrity": "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.14.tgz",
|
||||
"integrity": "sha512-hyMKEGatjmBYCs/Q4sbIcL/GqpFEqsX3wc+HYVQ/85A4rxc1o7rPm4DUI4GV4k/h0ITddwFSGpqQunI14GXjTA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -613,9 +623,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.13.tgz",
|
||||
"integrity": "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.14.tgz",
|
||||
"integrity": "sha512-u371GbXoVXrEgP20PwzoGNOdAOqAMhzo19JzzY+V4YmmnC+DTOoqdbMu+O+54emM59Mik/UdrtWgcuEDKWRPKA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -631,9 +641,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.13.tgz",
|
||||
"integrity": "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.14.tgz",
|
||||
"integrity": "sha512-BqwKsdZQf6EqVWr8fv2MrDYzAvOZ7+NYFSJESRGs87fkseEhs0TU9TXgRJTkZoRRkxTizyEg1LKUXfU3/78uqg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -649,9 +659,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.13.tgz",
|
||||
"integrity": "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.14.tgz",
|
||||
"integrity": "sha512-SD9kY8M009x/7s57Z1dgUVlP5+uYBgKQK0Tkr1WIWngrtUJmRCW1RJFMY7QDla2khXhmg2i5Io7MUjcmZNZ/KQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -667,9 +677,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.13.tgz",
|
||||
"integrity": "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.14.tgz",
|
||||
"integrity": "sha512-BJ2CL19M6uuWesMgnqJzgxoI+N+eV7b0A9O9vLV42y+gitC41fNa8KElau1tHVzuLWX5GzEuxMPhAO0tQXQMkg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -685,9 +695,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.13.tgz",
|
||||
"integrity": "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.14.tgz",
|
||||
"integrity": "sha512-4+Kl8daPV8NDHaD7mfCcc1pRM+Qg7ab8DyMm5FG5WoZgv4h2i3KFN1CybYBNbQqmuYm7ftJLvmMI1eHYJ22S/Q==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -703,9 +713,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.13.tgz",
|
||||
"integrity": "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.14.tgz",
|
||||
"integrity": "sha512-V/h8ePq1fKDrmtFMz6xcCN3DKvuvMRrwwD1q0PkBeeo4DpK64biaKvqWGVxqCeYKqeRk7gE7/iKPxLCn08O97A==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -721,9 +731,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.13.tgz",
|
||||
"integrity": "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.14.tgz",
|
||||
"integrity": "sha512-WxalXzgrLVpKC93xfpaJ3gCXUWzeCr+FRCbDVD2mUAy4L6Vo3lNVz0epUEUpxPWJkLNYUwM4OSIikv36OJ9mLg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -739,9 +749,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.13.tgz",
|
||||
"integrity": "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.14.tgz",
|
||||
"integrity": "sha512-3G3tnAmTpdk9jO3GQtyHXqeFD2SlecfTws/Y3M2mmj9nMBwwuLT4kur8sZKeu7ENUHSpVO3cIsm5A52LEWWwlg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -757,9 +767,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.13.tgz",
|
||||
"integrity": "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.14.tgz",
|
||||
"integrity": "sha512-qyr5XiNGxm2ytx17vvtcJ9nXn6v1gENETi503bJx3OGwZ1tSonj7BosxbMa/xeAfSl5iS9X6KjEWYXLu6v1L7Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -775,9 +785,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.13.tgz",
|
||||
"integrity": "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.14.tgz",
|
||||
"integrity": "sha512-oA4o5o0S2yB199YyYXDzKUv1Z56Rc+mgOjwm7Z/vJ9Z8igiDPYvoOuDyo6g7AK/ZaF09nPhDKVnGBcYHO11GXA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -793,9 +803,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.13.tgz",
|
||||
"integrity": "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.14.tgz",
|
||||
"integrity": "sha512-9mE94bf2GZUaR/fUSXCMpBoWkctu421Mi0ORpm4zqfbkjKep7RxzPFa5etcGs5h+2KApoWBOA/hAJa/7saGgEQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -899,9 +909,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.4.tgz",
|
||||
"integrity": "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==",
|
||||
"version": "26.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz",
|
||||
"integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -909,17 +919,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.1.tgz",
|
||||
"integrity": "sha512-nNlBf6HuotqMgZvSkWD5hNdemiBIxQ2NmrlgfcUIeZRs+BbakjscCQRq/Hb74EFVnFiSWWdL12JruqwBy3ERDw==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.2.tgz",
|
||||
"integrity": "sha512-K72/pvT7qpm8q3k8G5u93NCSN1RcLxX5h7Gw0bQpbMXviBHeY6Nx1WEwmk9wP+eKTSQ8ecxvcgt/ZX/rB938xQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.71.1",
|
||||
"@typescript-eslint/type-utils": "8.71.1",
|
||||
"@typescript-eslint/utils": "8.71.1",
|
||||
"@typescript-eslint/visitor-keys": "8.71.1",
|
||||
"@typescript-eslint/scope-manager": "8.71.2",
|
||||
"@typescript-eslint/type-utils": "8.71.2",
|
||||
"@typescript-eslint/utils": "8.71.2",
|
||||
"@typescript-eslint/visitor-keys": "8.71.2",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
@@ -932,22 +942,22 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.71.1",
|
||||
"@typescript-eslint/parser": "^8.71.2",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.1.tgz",
|
||||
"integrity": "sha512-oM/5sAqz/l1v/zYK1qTOPrn2+JIrBGC1V6uNYucRO5gh6eT9vWJ/RhPbGE59byvZEUQ4VP6XkLBaS5Jti5jpvw==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.2.tgz",
|
||||
"integrity": "sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.71.1",
|
||||
"@typescript-eslint/types": "8.71.1",
|
||||
"@typescript-eslint/typescript-estree": "8.71.1",
|
||||
"@typescript-eslint/visitor-keys": "8.71.1",
|
||||
"@typescript-eslint/scope-manager": "8.71.2",
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/typescript-estree": "8.71.2",
|
||||
"@typescript-eslint/visitor-keys": "8.71.2",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -963,14 +973,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.1.tgz",
|
||||
"integrity": "sha512-Oijc9RUsohHjncg6iGi7gCtjnEjYt9FWv9u6ygK8uKcHK5eLjLAkqERQ9BaytbpceOFdk6kifPCS/zAZE+A99g==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.2.tgz",
|
||||
"integrity": "sha512-Pd68GsLVh5ZXlcEccFPdhSOvtHZ/2WU0XsA8zmXOS3F158lVlcTLhfQ6qvtL1RzbMie0Kp8xtbVwAg4nejKPHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.71.1",
|
||||
"@typescript-eslint/types": "^8.71.1",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.71.2",
|
||||
"@typescript-eslint/types": "^8.71.2",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -985,14 +995,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.1.tgz",
|
||||
"integrity": "sha512-1YUDZXdTnXLlob03SFHBipdyMBsDVm7zoSz0ytTJ39dk5J1TgsTK03VlR/dLXG/RLLUrPcYbD4rp23BmHxHP0w==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.2.tgz",
|
||||
"integrity": "sha512-YF4wgg5iUX5Obywoac27B56FhozMNFhdJSbh11H5GBAwmBtfyRhmgTQqW9tRNNmHDvsZ2CX6UGzoz+YfYn2Leg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.71.1",
|
||||
"@typescript-eslint/visitor-keys": "8.71.1"
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/visitor-keys": "8.71.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1003,9 +1013,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.1.tgz",
|
||||
"integrity": "sha512-oTvkml5SxXhgg+WWKhod5qWQICDXtOB+/Hi9VdPKFAANJ2b7YOgMffgvzXuqog7R6A/JtrzRf128dsxummyWhg==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.2.tgz",
|
||||
"integrity": "sha512-jz49TAWokm/7yWD9/o837nYax9DyALxIPOImdImvVzo6canBCZYzin8zJyqNR5k1j9K1DE0bVvr0XYB+99uAdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1020,15 +1030,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.1.tgz",
|
||||
"integrity": "sha512-kH3t3jZCYv2PZSBEWPKTariLrCaoF1CLoPitYFBJOhAGEwWnSRAtGEifrfrpBa4K9JlwWNZotgA5w3/wLSk/FQ==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.2.tgz",
|
||||
"integrity": "sha512-cC8UaalCViJkb3rH/xLIvidEwoFRtMZMUqDjjJMGEKaTFwYRHYBcoczlEGHuvkHEO8svRDrgZ0yQgOArFmwang==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.71.1",
|
||||
"@typescript-eslint/typescript-estree": "8.71.1",
|
||||
"@typescript-eslint/utils": "8.71.1",
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/typescript-estree": "8.71.2",
|
||||
"@typescript-eslint/utils": "8.71.2",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
@@ -1045,9 +1055,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.1.tgz",
|
||||
"integrity": "sha512-ybYnPTUwg3VQQOFEiBPLs8Jxl1ry2vXdn1kVKguomBbvxpetdu3MKTNDqFNUrlG8ylP9MpZosmXMFYlGQHk1oQ==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.2.tgz",
|
||||
"integrity": "sha512-y5Ukl3oVp7e/VGxqIoEXoeb/Roj71PAJ4WG/N/RGj+eeDvKRLM15kGr9MijLbN0i03S52zxu31Z98E5gWChJKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1059,16 +1069,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.1.tgz",
|
||||
"integrity": "sha512-pSKSEK1JJpHs6KiKVtABCE4iJK5d7FLFzDXQNBQ2oRY7LEd3UwOt3TZemTgu9syTIf56dJWlTn/pdmrO9oKUXQ==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.2.tgz",
|
||||
"integrity": "sha512-DgNOEsl4pdYzwSL+N/Cl33tX1Hlw3FvId73LnKXfq7jnrF2viOEn7X+P7RBkkSWCVm92gnWd28GIC5vJuLREeA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.71.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.71.1",
|
||||
"@typescript-eslint/types": "8.71.1",
|
||||
"@typescript-eslint/visitor-keys": "8.71.1",
|
||||
"@typescript-eslint/project-service": "8.71.2",
|
||||
"@typescript-eslint/tsconfig-utils": "8.71.2",
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/visitor-keys": "8.71.2",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -1087,16 +1097,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.1.tgz",
|
||||
"integrity": "sha512-CBjT6gfAz3DW2j9Q4moFnqt208Aq+506sVGXfpRcyAeqn0EpayL0mjdWoCO2t3RC7GewNrFxdeehuEaPg6DMzQ==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.2.tgz",
|
||||
"integrity": "sha512-RyKa1G1M9rKHDhrk+HJtiF1MvLD5Ux2JW3TSkzw+02pRALdpJb+QzG1Y5em+LvCgzGwgg8H7mBRFKljNz7ImPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.71.1",
|
||||
"@typescript-eslint/types": "8.71.1",
|
||||
"@typescript-eslint/typescript-estree": "8.71.1"
|
||||
"@typescript-eslint/scope-manager": "8.71.2",
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/typescript-estree": "8.71.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1111,13 +1121,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.1.tgz",
|
||||
"integrity": "sha512-0GxiUGqMU0qXJt58cBsiG3DP8lUmBaFo2vChtUX+CT7PnR3v/VGLLrzVP4K9r1lNiZXjHYefMBbPunplr09v6w==",
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.2.tgz",
|
||||
"integrity": "sha512-zozlN0UA32bSG2zkKAgQyRYSbEISVoyU6vvkKYo2aSny74D8Jh4e+POkJ6Lk0dlezZi5x7TtD+88Xb06IB8nhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.71.1",
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1142,22 +1152,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.3.tgz",
|
||||
"integrity": "sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.5.tgz",
|
||||
"integrity": "sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.31",
|
||||
"@vitest/spy": "5.0.3",
|
||||
"@vitest/spy": "5.0.5",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^1.2.3"
|
||||
"magic-string": "^1.4.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"msw": "^2.4.9",
|
||||
"msw": "^2.4.9 || ^3.0.0",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
@@ -1181,9 +1191,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.3.tgz",
|
||||
"integrity": "sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.5.tgz",
|
||||
"integrity": "sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -1418,9 +1428,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
|
||||
"integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz",
|
||||
"integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
@@ -1647,9 +1657,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "10.12.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.12.0.tgz",
|
||||
"integrity": "sha512-npHjrHb2o4ehH3mE+YuxABR+0gyb0aVWCIolgJDArwCiHEIsonBm3ZKjel5b0U5+a+MbEvIyRCGCzD3eKKK2NA==",
|
||||
"version": "10.13.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.13.0.tgz",
|
||||
"integrity": "sha512-UohDjD4zSzEwhf58M3O7vc/ycc8O1I7dNlhtD0WxE8Px8EHNJKtUwzBqrzeGhisRuv2ag4sAYswszCJRgKXYUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
@@ -1931,16 +1941,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.3.0.tgz",
|
||||
"integrity": "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.1",
|
||||
"content-disposition": "^1.0.0",
|
||||
"content-type": "^1.0.5",
|
||||
"body-parser": "^2.3.0",
|
||||
"content-disposition": "^2.0.1",
|
||||
"content-type": "^2.0.0",
|
||||
"cookie": "^0.7.1",
|
||||
"cookie-signature": "^1.2.1",
|
||||
"debug": "^4.4.0",
|
||||
@@ -1956,14 +1966,14 @@
|
||||
"on-finished": "^2.4.1",
|
||||
"once": "^1.4.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"qs": "^6.14.0",
|
||||
"proxy-addr": "^2.0.8",
|
||||
"qs": "^6.16.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"router": "^2.2.0",
|
||||
"send": "^1.1.0",
|
||||
"serve-static": "^2.2.0",
|
||||
"statuses": "^2.0.1",
|
||||
"type-is": "^2.0.1",
|
||||
"type-is": "^2.1.0",
|
||||
"vary": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1994,6 +2004,20 @@
|
||||
"express": ">= 4.11"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/content-type": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
|
||||
"integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -2368,9 +2392,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ip-address": {
|
||||
"version": "10.7.3",
|
||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.3.tgz",
|
||||
"integrity": "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==",
|
||||
"version": "10.7.4",
|
||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz",
|
||||
"integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
@@ -2964,9 +2988,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/obug": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz",
|
||||
"integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz",
|
||||
"integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/sxzz",
|
||||
@@ -3122,9 +3146,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.29",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz",
|
||||
"integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==",
|
||||
"version": "8.5.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz",
|
||||
"integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3143,7 +3167,7 @@
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.19",
|
||||
"nanoid": "^3.3.20",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.2"
|
||||
},
|
||||
@@ -3162,9 +3186,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.9.9",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.9.tgz",
|
||||
"integrity": "sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==",
|
||||
"version": "3.9.10",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.10.tgz",
|
||||
"integrity": "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -3226,9 +3250,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",
|
||||
"integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz",
|
||||
"integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -3282,14 +3306,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.13.tgz",
|
||||
"integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.14.tgz",
|
||||
"integrity": "sha512-eTBTTqMR6uHKLgcUqpJP8ntzDI6V4LYA8negAMBEI7gkgVvOdrx6VsvS2Ym+83KhOPdLCHWJu0w7UQFJZhHKNw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@oxc-project/types": "=0.153.0",
|
||||
"@oxc-project/types": "=0.154.0",
|
||||
"@rolldown/pluginutils": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -3299,21 +3323,21 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rolldown/binding-android-arm-eabi": "1.2.13",
|
||||
"@rolldown/binding-android-arm64": "1.2.13",
|
||||
"@rolldown/binding-darwin-arm64": "1.2.13",
|
||||
"@rolldown/binding-darwin-x64": "1.2.13",
|
||||
"@rolldown/binding-freebsd-x64": "1.2.13",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.2.13",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.2.13",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.2.13",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.2.13",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.2.13",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.2.13",
|
||||
"@rolldown/binding-linux-x64-musl": "1.2.13",
|
||||
"@rolldown/binding-openharmony-arm64": "1.2.13",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.2.13",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.2.13"
|
||||
"@rolldown/binding-android-arm-eabi": "1.2.14",
|
||||
"@rolldown/binding-android-arm64": "1.2.14",
|
||||
"@rolldown/binding-darwin-arm64": "1.2.14",
|
||||
"@rolldown/binding-darwin-x64": "1.2.14",
|
||||
"@rolldown/binding-freebsd-x64": "1.2.14",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.2.14",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.2.14",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.2.14",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.2.14",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.2.14",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.2.14",
|
||||
"@rolldown/binding-linux-x64-musl": "1.2.14",
|
||||
"@rolldown/binding-openharmony-arm64": "1.2.14",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.2.14",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.2.14"
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
@@ -3543,9 +3567,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinybench": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.1.tgz",
|
||||
"integrity": "sha512-IAbQaPJIIdhVxi0mqy9lez1wEwje0UQf9F1vwtbelEG5Nbgn0nPOxCvU+mNeNS9gyJTAhrHp74CbUaSynQpZcQ==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz",
|
||||
"integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -3767,17 +3791,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.3.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.3.tgz",
|
||||
"integrity": "sha512-cTAldKPImjg6c+gk48U19POPn3GCBzZwpdsN8ZMEEcbpes+6/wvfqUd0C2y3qYY4wsj8PwgFwrZ/1jBPVttMSg==",
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz",
|
||||
"integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.33.0",
|
||||
"picomatch": "^4.0.7",
|
||||
"postcss": "^8.5.28",
|
||||
"rolldown": "~1.2.11",
|
||||
"postcss": "^8.5.29",
|
||||
"rolldown": "~1.2.12",
|
||||
"tinyglobby": "^0.2.17"
|
||||
},
|
||||
"bin": {
|
||||
@@ -3846,23 +3870,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.3.tgz",
|
||||
"integrity": "sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.5.tgz",
|
||||
"integrity": "sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/mocker": "5.0.3",
|
||||
"chai": "^6.2.2",
|
||||
"@vitest/mocker": "5.0.5",
|
||||
"chai": "^6.3.0",
|
||||
"es-module-lexer": "^2.3.2",
|
||||
"expect-type": "^1.4.0",
|
||||
"magic-string": "^1.2.3",
|
||||
"obug": "^2.1.4",
|
||||
"magic-string": "^1.4.3",
|
||||
"obug": "^3.0.0",
|
||||
"picomatch": "^4.0.7",
|
||||
"std-env": "^4.2.0",
|
||||
"tinybench": "^6.1.4",
|
||||
"tinyexec": "^1.3.0",
|
||||
"std-env": "^4.3.0",
|
||||
"tinybench": "^6.2.2",
|
||||
"tinyexec": "^1.3.1",
|
||||
"tinyglobby": "^0.2.17",
|
||||
"why-is-node-running": "3.2.1"
|
||||
},
|
||||
@@ -3879,12 +3903,12 @@
|
||||
"@edge-runtime/vm": "*",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@types/node": "^22.0.0 || >=24.0.0",
|
||||
"@vitest/browser-playwright": "5.0.3",
|
||||
"@vitest/browser-preview": "5.0.3",
|
||||
"@vitest/browser-playwright": "5.0.5",
|
||||
"@vitest/browser-preview": "5.0.5",
|
||||
"@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0",
|
||||
"@vitest/coverage-istanbul": "5.0.3",
|
||||
"@vitest/coverage-v8": "5.0.3",
|
||||
"@vitest/ui": "5.0.3",
|
||||
"@vitest/coverage-istanbul": "5.0.5",
|
||||
"@vitest/coverage-v8": "5.0.5",
|
||||
"@vitest/ui": "5.0.5",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*",
|
||||
"vite": "^6.4.0 || ^7.0.0 || ^8.0.0"
|
||||
@@ -3997,9 +4021,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.6.5",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz",
|
||||
"integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==",
|
||||
"version": "4.6.7",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz",
|
||||
"integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
|
||||
22
package.json
22
package.json
@@ -3,7 +3,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "0.89.0",
|
||||
"version": "0.94.0",
|
||||
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
|
||||
"main": "dist/lib.js",
|
||||
"types": "dist/lib.d.ts",
|
||||
@@ -65,26 +65,26 @@
|
||||
"node": ">=22"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "1.8.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.293",
|
||||
"@agentclientprotocol/sdk": "2.0.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.303",
|
||||
"diff": "9.0.0",
|
||||
"zod": "4.6.5"
|
||||
"zod": "4.6.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "0.132.1",
|
||||
"@anthropic-ai/sdk": "0.135.0",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@tsconfig/node22": "22.0.6",
|
||||
"@types/node": "26.6.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.71.1",
|
||||
"@typescript-eslint/parser": "8.71.1",
|
||||
"@types/node": "26.6.5",
|
||||
"@typescript-eslint/eslint-plugin": "8.71.2",
|
||||
"@typescript-eslint/parser": "8.71.2",
|
||||
"ajv": "8.20.0",
|
||||
"ajv-formats": "3.0.1",
|
||||
"eslint": "10.12.0",
|
||||
"eslint": "10.13.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"globals": "17.13.0",
|
||||
"prettier": "3.9.9",
|
||||
"prettier": "3.9.10",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "5.0.3"
|
||||
"vitest": "5.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
2933
src/acp-agent.ts
2933
src/acp-agent.ts
File diff suppressed because it is too large
Load Diff
@@ -2,21 +2,38 @@ import type {
|
||||
ClientCapabilities,
|
||||
SessionCapabilities,
|
||||
SessionNotification,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
} from "@agentclientprotocol/sdk/unstable";
|
||||
import {
|
||||
AIR_NATIVE_SUBAGENT_SESSIONS_CAPABILITY,
|
||||
clientSupportsAirCapability,
|
||||
isAirClient,
|
||||
} from "./air-extension.js";
|
||||
|
||||
export { AIR_NATIVE_SUBAGENT_SESSIONS_CAPABILITY } from "./air-extension.js";
|
||||
|
||||
/**
|
||||
* Temporary typed surface for agentclientprotocol/agent-client-protocol#1992.
|
||||
* How a client gets native subagent sessions.
|
||||
*
|
||||
* The wire contract is already defined by the ACP draft, but the published
|
||||
* TypeScript SDK does not contain it yet. Keep the compatibility boundary in
|
||||
* this file so it can be replaced by SDK exports without changing lifecycle
|
||||
* code when the draft ships.
|
||||
* - `rfd`: the subagents RFD (agentclientprotocol/agent-client-protocol#1992,
|
||||
* as revised): `subagent_update` and `session_message`, which the SDK types.
|
||||
* A client that declares `clientCapabilities.subagents` and is not AIR gets
|
||||
* it. (An ACP v2 client gets it without declaring anything, since subagents
|
||||
* are part of v2's baseline.)
|
||||
* - `air`: the earlier draft of that RFD, which AIR implements:
|
||||
* `subagent_spawned` and `subagent_state_update`, typed below. AIR gets it,
|
||||
* whichever capability it declares (`docs/air-extensions.md`).
|
||||
*/
|
||||
export type SubagentForm = "rfd" | "air";
|
||||
|
||||
/** The form of native subagent sessions that the client gets, or none. */
|
||||
export function subagentForm(capabilities?: ClientCapabilities | null): SubagentForm | undefined {
|
||||
if (!clientSupportsSubagents(capabilities)) return undefined;
|
||||
return isAirClient(capabilities) ? "air" : "rfd";
|
||||
}
|
||||
|
||||
/**
|
||||
* The earlier draft of the subagents RFD, which AIR implements and the SDK
|
||||
* does not type.
|
||||
*/
|
||||
export type SubagentSessionCapabilities = {
|
||||
cancel?: boolean;
|
||||
@@ -116,7 +133,7 @@ export function clientSupportsSubagents(capabilities?: ClientCapabilities | null
|
||||
return clientSupportsAirCapability(capabilities, AIR_NATIVE_SUBAGENT_SESSIONS_CAPABILITY);
|
||||
}
|
||||
|
||||
/** The only cast needed until the TypeScript SDK publishes PR #1992. */
|
||||
/** The cast that AIR's draft updates and the async task updates need. */
|
||||
export function asSdkSessionNotification(
|
||||
notification: AcpSessionNotification,
|
||||
): SessionNotification {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
export const AIR_NATIVE_SUBAGENT_SESSIONS_CAPABILITY = "nativeSubagentSessions";
|
||||
export const AIR_ASYNC_TASKS_CAPABILITY = "asyncTasks";
|
||||
/**
|
||||
* A prompt turn ends at its result also while its background subagents run.
|
||||
* The subagent updates and requests then go out after the prompt response.
|
||||
*/
|
||||
export const AIR_BACKGROUND_SUBAGENTS_CAPABILITY = "backgroundSubagents";
|
||||
export const AIR_SESSION_FAILURE_CAPABILITY = "sessionFailure";
|
||||
export const AIR_RECOMMENDED_CONFIG_VALUE_CAPABILITY = "recommendedValue";
|
||||
export const AIR_CUSTOM_INSTRUCTIONS_CAPABILITY = "customInstructions";
|
||||
@@ -34,11 +39,26 @@ export const AIR_SESSION_RENAME_CAPABILITY = "sessionRename";
|
||||
* {@link AIR_SESSION_INDEX_CAPABILITY} is; the client does not declare it.
|
||||
*/
|
||||
export const AIR_SESSION_LIST_SUBSCRIBE_CAPABILITY = "sessionListSubscribe";
|
||||
/**
|
||||
* Session index rows carry `agentWorktree: true` when the session runs in a
|
||||
* linked git worktree that Claude Code made for it. Advertised exactly when
|
||||
* {@link AIR_SESSION_INDEX_CAPABILITY} is; the client does not declare it.
|
||||
*/
|
||||
export const AIR_SESSION_WORKTREES_CAPABILITY = "sessionWorktrees";
|
||||
/**
|
||||
* The client wants to know when the history replay of a loaded session is
|
||||
* whole. After the last replayed update, the agent sends one
|
||||
* `session_info_update` with `_meta.jetbrains.air.loadReplayEnd: true` (see
|
||||
* {@link AIR_LOAD_REPLAY_END_KEY}). The client declares it; the agent does not
|
||||
* advertise it.
|
||||
*/
|
||||
export const AIR_LOAD_REPLAY_END_CAPABILITY = "loadReplayEnd";
|
||||
|
||||
/** The `_meta.jetbrains.air` keys that the ACP tool call contract defines. */
|
||||
export const AIR_COMMAND_TITLE_KEY = "commandTitle";
|
||||
export const AIR_SUBAGENT_KEY = "subagent";
|
||||
export const AIR_SKILL_KEY = "skill";
|
||||
export const AIR_MCP_KEY = "mcp";
|
||||
export const AIR_CONTEXT_COMPACTION_KEY = "contextCompaction";
|
||||
export const AIR_GOAL_KEY = "goal";
|
||||
export const AIR_KIND_KEY = "kind";
|
||||
@@ -46,6 +66,8 @@ export const AIR_KIND_KEY = "kind";
|
||||
export const AIR_SKILL_PATH_KEY = "skillPath";
|
||||
export const AIR_PERMISSION_KEY = "permission";
|
||||
export const AIR_CUSTOM_ANSWER_KEY = "customAnswer";
|
||||
/** The `_meta.jetbrains.air` key of the update that ends a whole history replay. */
|
||||
export const AIR_LOAD_REPLAY_END_KEY = "loadReplayEnd";
|
||||
/**
|
||||
* The root `_meta` spelling of {@link AIR_CUSTOM_ANSWER_KEY} that released
|
||||
* AIR versions read. Remove it when the ACP registry no longer serves AIR
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ClientCapabilities } from "@agentclientprotocol/sdk";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { ClientCapabilities } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { AcpSessionNotification, AsyncTaskState } from "./acp-subagents.js";
|
||||
import { AIR_ASYNC_TASKS_CAPABILITY, clientSupportsAirCapability } from "./air-extension.js";
|
||||
import { noticeOrTranscriptUpdate } from "./session-notices.js";
|
||||
@@ -39,6 +40,12 @@ type AsyncTask = {
|
||||
terminalSource?: TerminalSource;
|
||||
/** The optional fields that the client holds now, as JSON. */
|
||||
published: Map<string, string>;
|
||||
/**
|
||||
* Whether the task stands for a model turn that Claude Code started on its
|
||||
* own, see {@link AsyncTaskRuntime.autonomousTurnStarted}. The SDK knows no
|
||||
* such task, so its background task list never ends it.
|
||||
*/
|
||||
autonomousTurn: boolean;
|
||||
};
|
||||
|
||||
type TaskIdentity = { taskId?: unknown; task_id?: unknown };
|
||||
@@ -121,6 +128,10 @@ export class AsyncTaskRuntime {
|
||||
* The oldest entries are dropped.
|
||||
*/
|
||||
private readonly outputPathsByToolCall = new Map<string, string[]>();
|
||||
/** The task of the autonomous turn that runs now. */
|
||||
private autonomousTurn?: AsyncTask;
|
||||
/** The summary of the last task notification, which usually starts the next autonomous turn. */
|
||||
private lastNotificationSummary?: string;
|
||||
|
||||
constructor(
|
||||
readonly enabled: boolean,
|
||||
@@ -300,6 +311,7 @@ export class AsyncTaskRuntime {
|
||||
const state = taskState(message.status);
|
||||
if (!taskId || !state || state === "running" || state === "paused") return;
|
||||
|
||||
this.lastNotificationSummary = nonBlankString(message.summary) ?? this.lastNotificationSummary;
|
||||
const task = this.task(taskId);
|
||||
const wasTerminal = isTerminal(task.state);
|
||||
const correctsLevelState = wasTerminal && task.terminalSource === "level";
|
||||
@@ -363,6 +375,7 @@ export class AsyncTaskRuntime {
|
||||
if (
|
||||
(task.announced || task.held) &&
|
||||
!task.ignored &&
|
||||
!task.autonomousTurn &&
|
||||
!isTerminal(task.state) &&
|
||||
!live.has(task.id)
|
||||
) {
|
||||
@@ -378,6 +391,7 @@ export class AsyncTaskRuntime {
|
||||
}
|
||||
|
||||
async finishAll(state: Extract<AsyncTaskState, "failed" | "stopped">): Promise<void> {
|
||||
this.autonomousTurn = undefined;
|
||||
const errors: unknown[] = [];
|
||||
for (const task of this.tasks.values()) {
|
||||
if ((task.announced || task.held) && !task.ignored && !isTerminal(task.state)) {
|
||||
@@ -412,6 +426,7 @@ export class AsyncTaskRuntime {
|
||||
const task = this.tasks.get(taskId);
|
||||
if (!task || !task.announced || task.ignored || task.stopAnnounced) return;
|
||||
task.stopAnnounced = true;
|
||||
if (this.autonomousTurn === task) this.autonomousTurn = undefined;
|
||||
// No terminal summary: a stopped task leaves the Async Tasks panel at once,
|
||||
// so anything said there is said to nobody.
|
||||
await this.finish(task, "stopped", undefined, "event");
|
||||
@@ -438,6 +453,47 @@ export class AsyncTaskRuntime {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes a model turn that Claude Code started with no prompt open, for
|
||||
* example to answer a task notification. ACP v1 has no session state, and
|
||||
* the client already got the stop reason of its last prompt, so this task is
|
||||
* the only sign that the agent works. The tool calls of the turn are in the
|
||||
* transcript already, so the task is not shown there. Does nothing while
|
||||
* such a task is open.
|
||||
*/
|
||||
async autonomousTurnStarted(): Promise<void> {
|
||||
if (!this.enabled || this.autonomousTurn) return;
|
||||
const task = this.task(`autonomous-turn-${randomUUID()}`);
|
||||
task.autonomousTurn = true;
|
||||
task.startedObserved = true;
|
||||
task.name = "Claude is working";
|
||||
task.taskType = "turn";
|
||||
task.description =
|
||||
this.lastNotificationSummary ?? "Claude Code started a turn without a prompt.";
|
||||
task.showInTranscript = false;
|
||||
this.lastNotificationSummary = undefined;
|
||||
this.autonomousTurn = task;
|
||||
await this.announce(task, { withoutToolCall: true });
|
||||
}
|
||||
|
||||
/** Whether the task of an autonomous turn is open. */
|
||||
get autonomousTurnOpen(): boolean {
|
||||
return this.autonomousTurn !== undefined;
|
||||
}
|
||||
|
||||
/** Ends the task of the autonomous turn that runs now, if there is one. */
|
||||
async autonomousTurnEnded(state: "completed" | "failed"): Promise<void> {
|
||||
const task = this.autonomousTurn;
|
||||
if (!task) return;
|
||||
this.autonomousTurn = undefined;
|
||||
await this.finish(task, state, undefined, "event");
|
||||
}
|
||||
|
||||
/** Whether the task stands for an autonomous turn, which a stop interrupts. */
|
||||
isAutonomousTurn(taskId: string): boolean {
|
||||
return this.tasks.get(taskId)?.autonomousTurn === true;
|
||||
}
|
||||
|
||||
/** Whether a task of the tool call went to the background, so the client gets it. */
|
||||
isBackgroundedToolCall(toolCallId: string): boolean {
|
||||
for (const task of this.tasks.values()) {
|
||||
@@ -488,6 +544,8 @@ export class AsyncTaskRuntime {
|
||||
clear(): void {
|
||||
this.tasks.clear();
|
||||
this.outputPathsByToolCall.clear();
|
||||
this.autonomousTurn = undefined;
|
||||
this.lastNotificationSummary = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -520,6 +578,7 @@ export class AsyncTaskRuntime {
|
||||
stopAnnounced: false,
|
||||
state: "running",
|
||||
published: new Map(),
|
||||
autonomousTurn: false,
|
||||
};
|
||||
this.tasks.set(taskId, task);
|
||||
return task;
|
||||
@@ -786,7 +845,7 @@ export function backgroundBashTaskFromToolResult(
|
||||
content: unknown,
|
||||
toolUseResult: unknown,
|
||||
toolUses: Record<string, { name: string; input: unknown }>,
|
||||
): AsyncTaskStarted | undefined {
|
||||
): (AsyncTaskStarted & { taskId: string; toolCallId: string }) | undefined {
|
||||
if (!Array.isArray(content)) return undefined;
|
||||
const toolResults = content.flatMap(toolResultBlock);
|
||||
const bashResults = toolResults.filter((result) => toolUses[result.toolUseId]?.name === "Bash");
|
||||
|
||||
@@ -103,6 +103,13 @@ const NOT_LOGGED_IN: AuthStatus = {
|
||||
label: "Not logged in",
|
||||
};
|
||||
|
||||
const API_KEY_SOURCE_DETAILS = new Map([
|
||||
["apiKeyHelper", "API key helper"],
|
||||
["env", "env"],
|
||||
["ANTHROPIC_API_KEY", "ANTHROPIC_API_KEY"],
|
||||
["ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_AUTH_TOKEN"],
|
||||
]);
|
||||
|
||||
/** Display names for the non-firstParty backends, where auth lives outside the
|
||||
* agent (AWS credentials, gcloud ADC, …). */
|
||||
const EXTERNAL_PROVIDER_LABELS: Record<string, string> = {
|
||||
@@ -162,7 +169,10 @@ export function accountInfoHasIdentitySignal(account: AccountInfo | undefined):
|
||||
/** Session-time mapping from the SDK's `AccountInfo`, the richest source when
|
||||
* it is populated. Returns `undefined` when the account carries no identity
|
||||
* signal, so callers keep the status they already know. */
|
||||
export function fromAccountInfo(account: AccountInfo | undefined): AuthStatus | undefined {
|
||||
export function fromAccountInfo(
|
||||
account: AccountInfo | undefined,
|
||||
apiKeyHelper?: string,
|
||||
): AuthStatus | undefined {
|
||||
if (!account || !accountInfoHasIdentitySignal(account)) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -172,13 +182,14 @@ export function fromAccountInfo(account: AccountInfo | undefined): AuthStatus |
|
||||
apiKeySource: account.apiKeySource,
|
||||
email: account.email,
|
||||
organization: account.organization,
|
||||
apiKeyHelper,
|
||||
});
|
||||
}
|
||||
|
||||
/** Pre-session mapping from `claude auth status --json` stdout. Returns
|
||||
* `undefined` when the output is not the expected JSON object, so callers can
|
||||
* report "not known" instead of guessing. */
|
||||
export function fromCliStatus(stdout: string): AuthStatus | undefined {
|
||||
export function fromCliStatus(stdout: string, apiKeyHelper?: string): AuthStatus | undefined {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(stdout);
|
||||
@@ -205,6 +216,7 @@ export function fromCliStatus(stdout: string): AuthStatus | undefined {
|
||||
apiKeySource: status.apiKeySource,
|
||||
email: status.email,
|
||||
organization: status.orgName,
|
||||
apiKeyHelper,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -224,6 +236,7 @@ function mapAuthFields(fields: {
|
||||
apiKeySource?: string;
|
||||
email?: string;
|
||||
organization?: string;
|
||||
apiKeyHelper?: string;
|
||||
}): AuthStatus {
|
||||
const { apiProvider, subscriptionType, apiKeySource, email, organization } = fields;
|
||||
|
||||
@@ -237,11 +250,19 @@ function mapAuthFields(fields: {
|
||||
};
|
||||
}
|
||||
if (apiKeySource) {
|
||||
if (
|
||||
apiKeySource === "apiKeyHelper" &&
|
||||
typeof fields.apiKeyHelper === "string" &&
|
||||
/\bcentral\b/i.test(fields.apiKeyHelper) &&
|
||||
/\bproxy\s+start\b/i.test(fields.apiKeyHelper)
|
||||
) {
|
||||
return { kind: "gateway", label: "JetBrains Air Gateway" };
|
||||
}
|
||||
const detail = API_KEY_SOURCE_DETAILS.get(apiKeySource);
|
||||
return {
|
||||
kind: "api_key",
|
||||
label: "Anthropic API key",
|
||||
// The source ("apiKeyHelper", "env", …) is the specifics line.
|
||||
detail: apiKeySource,
|
||||
...(detail ? { detail } : {}),
|
||||
};
|
||||
}
|
||||
if (subscriptionType) {
|
||||
|
||||
362
src/background-work-experiment.ts
Normal file
362
src/background-work-experiment.ts
Normal file
@@ -0,0 +1,362 @@
|
||||
/** Opt-in wire prototype for RFD 2452 over the installed native task adapters. */
|
||||
type Json = Record<string, unknown>;
|
||||
type Task = {
|
||||
id: string;
|
||||
toolCallId?: string;
|
||||
canStop: boolean;
|
||||
final: boolean;
|
||||
supported: boolean;
|
||||
};
|
||||
type Session = {
|
||||
state: string;
|
||||
known: boolean;
|
||||
level?: Set<string>;
|
||||
tasks: Map<string, Task>;
|
||||
tools: Set<string>;
|
||||
finalTools: Set<string>;
|
||||
children: Set<string>;
|
||||
};
|
||||
function object(value: unknown): Json {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Json)
|
||||
: {};
|
||||
}
|
||||
|
||||
export class BackgroundWorkExperiment {
|
||||
private sessions = new Map<string, Session>();
|
||||
private requests = new Map<unknown, { method: string; sessionId?: string }>();
|
||||
private cancellations = new Set<unknown>();
|
||||
private protocol = 1;
|
||||
private session(id: string): Session {
|
||||
let session = this.sessions.get(id);
|
||||
if (!session) {
|
||||
session = {
|
||||
state: "idle",
|
||||
known: false,
|
||||
tasks: new Map(),
|
||||
tools: new Set(),
|
||||
finalTools: new Set(),
|
||||
children: new Set(),
|
||||
};
|
||||
this.sessions.set(id, session);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
private notification(sessionId: string, update: Json): Json {
|
||||
return {
|
||||
jsonrpc: "2.0",
|
||||
method: "session/update",
|
||||
params: { sessionId, update },
|
||||
};
|
||||
}
|
||||
snapshot(sessionId: string): Json {
|
||||
const session = this.session(sessionId);
|
||||
const live = [...session.tasks.values()].filter(
|
||||
(task) => !task.final && session.level?.has(task.id),
|
||||
);
|
||||
const complete =
|
||||
session.known &&
|
||||
session.children.size === 0 &&
|
||||
[...(session.level ?? [])].every((id) => {
|
||||
const task = session.tasks.get(id);
|
||||
return (
|
||||
task &&
|
||||
task.supported &&
|
||||
!task.final &&
|
||||
task.toolCallId !== undefined &&
|
||||
session.tools.has(task.toolCallId) &&
|
||||
!session.finalTools.has(task.toolCallId)
|
||||
);
|
||||
});
|
||||
return this.notification(sessionId, {
|
||||
sessionUpdate: "state_update",
|
||||
state: session.state,
|
||||
background: complete
|
||||
? live.map((task) => ({
|
||||
type: "tool_call",
|
||||
toolCallId: task.toolCallId,
|
||||
}))
|
||||
: null,
|
||||
});
|
||||
}
|
||||
inbound(message: Json): { forward?: Json; send: Json[] } {
|
||||
const params = object(message["params"]);
|
||||
const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : undefined;
|
||||
const send: Json[] = [];
|
||||
if (message["method"] === "initialize") {
|
||||
this.protocol = typeof params["protocolVersion"] === "number" ? params["protocolVersion"] : 1;
|
||||
const capabilities = object(params["clientCapabilities"]),
|
||||
meta = object(capabilities["_meta"]);
|
||||
const jetbrains = object(meta["jetbrains"]),
|
||||
air = object(jetbrains["air"]);
|
||||
const old = Array.isArray(air["capabilities"]) ? air["capabilities"] : [];
|
||||
// The native task adapters already implement these private capabilities.
|
||||
message = {
|
||||
...message,
|
||||
params: {
|
||||
...params,
|
||||
clientCapabilities: {
|
||||
...capabilities,
|
||||
_meta: {
|
||||
...meta,
|
||||
jetbrains: {
|
||||
...jetbrains,
|
||||
air: {
|
||||
...air,
|
||||
version: 1,
|
||||
capabilities: [...new Set([...old, "asyncTasks", "backgroundSubagents"])],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
if (["session/resume", "session/load"].includes(String(message["method"])) && sessionId) {
|
||||
const session = this.session(sessionId);
|
||||
session.known = false;
|
||||
delete session.level;
|
||||
session.tools.clear();
|
||||
}
|
||||
if (message["method"] === "tool_call/cancel") {
|
||||
const session = sessionId ? this.session(sessionId) : undefined;
|
||||
const task =
|
||||
session &&
|
||||
[...session.tasks.values()].find(
|
||||
(task) =>
|
||||
!task.final &&
|
||||
task.supported &&
|
||||
task.canStop &&
|
||||
task.toolCallId === params["toolCallId"] &&
|
||||
task.toolCallId !== undefined &&
|
||||
session.tools.has(task.toolCallId) &&
|
||||
!session.finalTools.has(task.toolCallId),
|
||||
);
|
||||
if (!task)
|
||||
return {
|
||||
send: [
|
||||
{
|
||||
jsonrpc: "2.0",
|
||||
id: message["id"],
|
||||
error: {
|
||||
code: -32602,
|
||||
message: "Tool call is not cancellable background work",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
this.cancellations.add(message["id"]);
|
||||
return {
|
||||
forward: {
|
||||
...message,
|
||||
method: "_session/async_task/stop",
|
||||
params: { sessionId, asyncTaskId: task.id },
|
||||
},
|
||||
send,
|
||||
};
|
||||
}
|
||||
if (message["id"] !== undefined && typeof message["method"] === "string")
|
||||
this.requests.set(message["id"], {
|
||||
method: message["method"],
|
||||
sessionId,
|
||||
});
|
||||
if (message["method"] === "session/prompt" && sessionId && this.protocol < 2) {
|
||||
this.session(sessionId).state = "running";
|
||||
send.push(this.snapshot(sessionId));
|
||||
}
|
||||
return { forward: message, send };
|
||||
}
|
||||
outbound(message: Json): Json[] {
|
||||
if (!message["method"] && this.cancellations.delete(message["id"])) {
|
||||
if (message["error"]) return [message];
|
||||
return object(message["result"])["stopped"] === true
|
||||
? [{ ...message, result: {} }]
|
||||
: [
|
||||
{
|
||||
jsonrpc: "2.0",
|
||||
id: message["id"],
|
||||
error: {
|
||||
code: -32602,
|
||||
message: "Native cancellation was not accepted",
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
const request = !message["method"] ? this.requests.get(message["id"]) : undefined;
|
||||
if (request) {
|
||||
this.requests.delete(message["id"]);
|
||||
const result = object(message["result"]);
|
||||
const id = typeof result["sessionId"] === "string" ? result["sessionId"] : request.sessionId;
|
||||
if (id && !message["error"]) {
|
||||
if (request.method === "session/new" || request.method === "session/fork") {
|
||||
if (request.method === "session/fork") {
|
||||
this.session(id).known = true;
|
||||
this.session(id).level = new Set();
|
||||
}
|
||||
return [message, this.snapshot(id)];
|
||||
}
|
||||
if (request.method === "session/resume" || request.method === "session/load")
|
||||
return [this.snapshot(id), message];
|
||||
if (request.method === "session/close") this.sessions.delete(id);
|
||||
}
|
||||
if (id && request.method === "session/prompt" && this.protocol < 2) {
|
||||
this.session(id).state = "idle";
|
||||
return [this.snapshot(id), message];
|
||||
}
|
||||
}
|
||||
const params = object(message["params"]),
|
||||
update = object(params["update"]);
|
||||
const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : undefined;
|
||||
if (message["method"] !== "session/update" || !sessionId) return [message];
|
||||
const session = this.session(sessionId);
|
||||
const marker = object(object(update["_meta"])["backgroundWorkExperiment"]);
|
||||
if (update["sessionUpdate"] === "session_info_update" && "taskIds" in marker) {
|
||||
if (
|
||||
!Array.isArray(marker["taskIds"]) ||
|
||||
!marker["taskIds"].every((id) => typeof id === "string" && id.length > 0)
|
||||
) {
|
||||
session.known = false;
|
||||
} else {
|
||||
session.level = new Set(marker["taskIds"] as string[]);
|
||||
session.known = true;
|
||||
}
|
||||
return [this.snapshot(sessionId)];
|
||||
}
|
||||
if (update["sessionUpdate"] === "state_update") {
|
||||
if (typeof update["state"] === "string") session.state = update["state"];
|
||||
return [
|
||||
{
|
||||
...message,
|
||||
params: {
|
||||
...params,
|
||||
update: {
|
||||
...update,
|
||||
background: object(object(this.snapshot(sessionId)["params"])["update"])[
|
||||
"background"
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
if (
|
||||
["subagent_update", "_subagent_update", "subagent_spawned", "subagent_state_update"].includes(
|
||||
String(update["sessionUpdate"]),
|
||||
)
|
||||
) {
|
||||
const id = String(update["sessionId"] ?? update["subagentSessionId"] ?? "unknown");
|
||||
const state =
|
||||
typeof update["state"] === "string" ? update["state"] : object(update["state"])["state"];
|
||||
if (["completed", "failed", "cancelled"].includes(String(state))) session.children.delete(id);
|
||||
else session.children.add(id);
|
||||
return [message, this.snapshot(sessionId)];
|
||||
}
|
||||
if (update["sessionUpdate"] === "tool_call" || update["sessionUpdate"] === "tool_call_update") {
|
||||
const id = typeof update["toolCallId"] === "string" ? update["toolCallId"] : "";
|
||||
if (id && (this.protocol >= 2 || update["sessionUpdate"] === "tool_call"))
|
||||
session.tools.add(id);
|
||||
const task = [...session.tasks.values()].find(
|
||||
(task) =>
|
||||
!task.final && task.supported && task.toolCallId === id && !session.finalTools.has(id),
|
||||
);
|
||||
if (!task && ["completed", "failed", "cancelled"].includes(String(update["status"])))
|
||||
session.finalTools.add(id);
|
||||
return task
|
||||
? [
|
||||
{
|
||||
...message,
|
||||
params: {
|
||||
...params,
|
||||
update: {
|
||||
...update,
|
||||
status: "in_progress",
|
||||
capabilities: task.canStop ? { cancel: {} } : {},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
: [message];
|
||||
}
|
||||
if (
|
||||
typeof update["sessionUpdate"] !== "string" ||
|
||||
!update["sessionUpdate"].replace(/^_/, "").startsWith("async_task_")
|
||||
)
|
||||
return [message];
|
||||
const id = typeof update["asyncTaskId"] === "string" ? update["asyncTaskId"] : undefined;
|
||||
if (!id) {
|
||||
session.known = false;
|
||||
return [message, this.snapshot(sessionId)];
|
||||
}
|
||||
let task = session.tasks.get(id);
|
||||
if (!task) {
|
||||
task = { id, canStop: false, final: false, supported: false };
|
||||
session.tasks.set(id, task);
|
||||
}
|
||||
if (String(update["sessionUpdate"]).replace(/^_/, "") === "async_task_spawned")
|
||||
task.supported = update["taskType"] === "shell";
|
||||
if (typeof update["toolCallId"] === "string") task.toolCallId = update["toolCallId"];
|
||||
if (update["canStop"] === true) task.canStop = true;
|
||||
const terminal = ["completed", "failed", "stopped"].includes(String(update["state"]));
|
||||
if (!terminal && session.level && !session.level.has(id)) session.known = false;
|
||||
const wasFinal = task.final;
|
||||
if (terminal) {
|
||||
task.final = true;
|
||||
session.level?.delete(id);
|
||||
}
|
||||
if (wasFinal) return [this.snapshot(sessionId)];
|
||||
if (
|
||||
!task.supported ||
|
||||
!task.toolCallId ||
|
||||
!session.tools.has(task.toolCallId) ||
|
||||
session.finalTools.has(task.toolCallId)
|
||||
)
|
||||
return [message, this.snapshot(sessionId)];
|
||||
const updates: Json[] = [];
|
||||
if (!wasFinal)
|
||||
updates.push(
|
||||
this.notification(sessionId, {
|
||||
sessionUpdate: "tool_call_update",
|
||||
toolCallId: task.toolCallId,
|
||||
status: terminal
|
||||
? update["state"] === "stopped"
|
||||
? this.protocol < 2
|
||||
? "failed"
|
||||
: "cancelled"
|
||||
: update["state"]
|
||||
: "in_progress",
|
||||
capabilities: terminal ? null : task.canStop ? { cancel: {} } : {},
|
||||
}),
|
||||
);
|
||||
if (terminal) session.finalTools.add(task.toolCallId);
|
||||
return [...updates, this.snapshot(sessionId)];
|
||||
}
|
||||
}
|
||||
|
||||
/** Draft messages are added at the stream boundary, outside the installed SDK schema. */
|
||||
export function backgroundWorkStream<T>(stream: {
|
||||
readable: ReadableStream<T>;
|
||||
writable: WritableStream<T>;
|
||||
}): typeof stream {
|
||||
if (process.env["ACP_BACKGROUND_WORK_EXPERIMENT"] !== "1") return stream;
|
||||
const experiment = new BackgroundWorkExperiment(),
|
||||
writer = stream.writable.getWriter();
|
||||
const send = async (messages: Json[]) => {
|
||||
for (const message of messages) await writer.write(message as T);
|
||||
};
|
||||
return {
|
||||
readable: stream.readable.pipeThrough(
|
||||
new TransformStream<T, T>({
|
||||
async transform(message, controller) {
|
||||
const result = experiment.inbound(object(message));
|
||||
await send(result["send"]);
|
||||
if (result["forward"]) controller.enqueue(result["forward"] as T);
|
||||
},
|
||||
}),
|
||||
),
|
||||
writable: new WritableStream<T>({
|
||||
write: (message) => send(experiment.outbound(object(message))),
|
||||
close: () => writer.close(),
|
||||
abort: (reason) => writer.abort(reason),
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { NewSessionRequest, SessionConfigOption } from "@agentclientprotocol/sdk";
|
||||
import type { NewSessionRequest, SessionConfigOption } from "@agentclientprotocol/sdk/unstable";
|
||||
import type {
|
||||
EffortLevel,
|
||||
Options,
|
||||
@@ -44,6 +44,7 @@ export type ClearContextSession<Turn extends ClearContextTurn = ClearContextTurn
|
||||
turnQueue?: Turn[];
|
||||
pendingExitPlanContextReset?: ClearContextReset;
|
||||
contextUsedTokens?: number;
|
||||
sentUserUuids?: Set<string>;
|
||||
input: { push(message: SDKUserMessage): unknown };
|
||||
};
|
||||
|
||||
@@ -158,6 +159,7 @@ export async function continuePlanInFreshContext<
|
||||
}
|
||||
assertRestartActive(freshSession);
|
||||
|
||||
(freshSession.sentUserUuids ??= new Set()).add(turn.promptUuid);
|
||||
freshSession.input.push(host.continuationMessage(sessionId, reset.plan, turn.promptUuid));
|
||||
assertRestartActive(freshSession);
|
||||
host.ensureConsumer(freshSession, sessionId);
|
||||
|
||||
74
src/client-system-prompt.ts
Normal file
74
src/client-system-prompt.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* The client-provided system prompt of the unstable `systemPrompt` field on
|
||||
* `session/new`, `session/load`, `session/resume`, and `session/fork`.
|
||||
*
|
||||
* - `append` keeps the Claude Code preset and adds the text after it, the
|
||||
* SDK's `{ type: "preset", preset: "claude_code", append }`.
|
||||
* - `override` replaces the Claude Code preset with the text, the SDK's custom
|
||||
* string prompt. Tools, MCP servers, permissions, and settings (including the
|
||||
* CLAUDE.md files that `settingSources` loads) are unchanged.
|
||||
*
|
||||
* The CLI records the system prompt of a conversation on its first request
|
||||
* and reuses it on resume, so a different `systemPrompt` on a later
|
||||
* `session/resume` takes effect at the next compaction. The RFD allows this
|
||||
* deferral.
|
||||
*/
|
||||
import type { Options } from "@anthropic-ai/claude-agent-sdk";
|
||||
import {
|
||||
RequestError,
|
||||
type SessionCapabilities,
|
||||
type SystemPrompt,
|
||||
} from "@agentclientprotocol/sdk/unstable";
|
||||
|
||||
/** The `sessionCapabilities.systemPrompt` the agent advertises: both modes. */
|
||||
export const SYSTEM_PROMPT_CAPABILITY = {
|
||||
mode: { override: {} },
|
||||
} satisfies NonNullable<SessionCapabilities["systemPrompt"]>;
|
||||
|
||||
export type ClientSystemPrompt = { mode: "append" | "override"; text: string };
|
||||
|
||||
/**
|
||||
* The mode and text of a `systemPrompt`, or `undefined` when the request has
|
||||
* none. Throws `invalid_params` for a mode other than `append` and `override`
|
||||
* and for a content block other than text: the RFD requires an error, and no
|
||||
* session, for a prompt the agent cannot apply.
|
||||
*/
|
||||
export function clientSystemPrompt(
|
||||
systemPrompt: SystemPrompt | null | undefined,
|
||||
): ClientSystemPrompt | undefined {
|
||||
if (systemPrompt == null) return undefined;
|
||||
const { mode, content } = systemPrompt;
|
||||
if (mode !== "append" && mode !== "override") {
|
||||
throw RequestError.invalidParams(
|
||||
{ systemPrompt: { mode } },
|
||||
`Unsupported systemPrompt mode: ${mode}`,
|
||||
);
|
||||
}
|
||||
const text: string[] = [];
|
||||
for (const block of content) {
|
||||
if (block.type !== "text") {
|
||||
throw RequestError.invalidParams(
|
||||
{ systemPrompt: { content: { type: block.type } } },
|
||||
`systemPrompt content must be text, but received a ${block.type} block`,
|
||||
);
|
||||
}
|
||||
text.push(block.text);
|
||||
}
|
||||
return { mode, text: text.join("\n\n") };
|
||||
}
|
||||
|
||||
/**
|
||||
* The SDK `systemPrompt` option for a client system prompt. `customInstructions`
|
||||
* (AIR's `customInstructions` on `session/new` metadata) still follows the
|
||||
* client text in both modes.
|
||||
*/
|
||||
export function sdkSystemPrompt(
|
||||
prompt: ClientSystemPrompt,
|
||||
customInstructions: string | undefined,
|
||||
): NonNullable<Options["systemPrompt"]> {
|
||||
const text = [prompt.text, customInstructions].filter((part) => part).join("\n\n");
|
||||
if (prompt.mode === "override") return text;
|
||||
return text
|
||||
? { type: "preset", preset: "claude_code", append: text }
|
||||
: { type: "preset", preset: "claude_code" };
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk";
|
||||
import { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk/unstable";
|
||||
import {
|
||||
ContextCompactionMetadata,
|
||||
createContextCompactionMeta,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ToolCallContent, ToolCallLocation } from "@agentclientprotocol/sdk";
|
||||
import type * as v2 from "@agentclientprotocol/sdk/experimental/v2";
|
||||
import { ToolCallContent, ToolCallLocation } from "@agentclientprotocol/sdk/unstable";
|
||||
import type * as v2 from "@agentclientprotocol/sdk/experimental/v2/unstable";
|
||||
import { structuredPatch } from "diff";
|
||||
import { constants } from "node:fs";
|
||||
import { type FileHandle, open } from "node:fs/promises";
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { CreateElicitationResponse } from "@agentclientprotocol/sdk";
|
||||
import { CreateElicitationResponse } from "@agentclientprotocol/sdk/unstable";
|
||||
import type {
|
||||
CreateElicitationRequest,
|
||||
ElicitationAcceptAction,
|
||||
ElicitationPropertySchema,
|
||||
ElicitationSchema,
|
||||
EnumOption,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
} from "@agentclientprotocol/sdk/unstable";
|
||||
import type { ElicitationRequest, ElicitationResult } from "@anthropic-ai/claude-agent-sdk";
|
||||
import type {
|
||||
AskUserQuestionInput,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SessionNotification } from "@agentclientprotocol/sdk";
|
||||
import type { SessionNotification } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { SDKResultMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||
import {
|
||||
type ClearContextCoordinatorHost,
|
||||
@@ -126,6 +126,38 @@ 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. */
|
||||
export const PLAN_MODE_PLAN_ID = "plan-mode";
|
||||
|
||||
/** The plan, trimmed, that a successful ExitPlanMode result approved: the plan
|
||||
* of the structured `output`, which includes the user's edits, else the plan
|
||||
* that Claude Code adds to the tool's `input`. Replay has only the input: the
|
||||
* SDK's `getSessionMessages` drops the structured output that the transcript
|
||||
* keeps. Undefined for a subagent's plan, one that waits for its team lead
|
||||
* (both only known from the output), and an empty plan. */
|
||||
export function approvedPlan(input: unknown, output: unknown): string | undefined {
|
||||
const result = output as
|
||||
{ plan?: unknown; isAgent?: unknown; awaitingLeaderApproval?: unknown } | undefined;
|
||||
if (result?.isAgent === true || result?.awaitingLeaderApproval === true) return undefined;
|
||||
const plan =
|
||||
typeof result?.plan === "string"
|
||||
? result.plan
|
||||
: (input as { plan?: unknown } | undefined)?.plan;
|
||||
return typeof plan === "string" && plan.trim() !== "" ? plan.trim() : undefined;
|
||||
}
|
||||
|
||||
/** 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"] {
|
||||
return {
|
||||
sessionUpdate: "plan_update",
|
||||
plan: { type: "markdown", planId: PLAN_MODE_PLAN_ID, content: plan },
|
||||
};
|
||||
}
|
||||
|
||||
/** Claude wraps a rejected ExitPlanMode explanation in a Markdown code fence.
|
||||
* Strip exactly one complete outer fence for that tool only. */
|
||||
export function exitPlanModeRawOutput(toolName: string, content: unknown): unknown {
|
||||
@@ -144,6 +176,8 @@ export type ExitPlanRestartHost<
|
||||
"publishSessionState" | "continuationMessage"
|
||||
> & {
|
||||
sessionUpdate(notification: SessionNotification): Promise<void>;
|
||||
/** Whether the client takes plan operations (see planModeUpdate). */
|
||||
supportsPlanOperations(): boolean;
|
||||
destroyReplacement(sessionId: string, session: Session): void;
|
||||
settleCancelledTurn(oldSession: Session, turnSession: Session, turn: Turn): void;
|
||||
settleFailedTurn(turnSession: Session, turn: Turn, error: unknown): void;
|
||||
@@ -180,6 +214,12 @@ export class ExitPlanCoordinator<
|
||||
sessionId: id,
|
||||
update: { sessionUpdate: "config_option_update", configOptions },
|
||||
});
|
||||
// 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) });
|
||||
}
|
||||
},
|
||||
continuationMessage: (id, plan, promptUuid) => ({
|
||||
type: "user",
|
||||
|
||||
@@ -19,6 +19,10 @@ export type FileChangeReportTurnState = {
|
||||
export type NativeFileChangeReportTurn = {
|
||||
promptUuid: string;
|
||||
fileChangeReport?: FileChangeReportTurnState;
|
||||
/** Raw `file_path`/`notebook_path` values of the file-tool edits that
|
||||
* subagents of this turn completed. Checkpoints do not track files that
|
||||
* only a subagent edited. */
|
||||
editedPaths?: Set<string>;
|
||||
};
|
||||
|
||||
export type FileChangeReportWorkspace = {
|
||||
@@ -94,6 +98,41 @@ export function agentFileChangeReportMeta(
|
||||
return withAirMeta(undefined, AGENT_FILE_CHANGE_REPORT_CAPABILITY, result);
|
||||
}
|
||||
|
||||
const FILE_EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
|
||||
|
||||
/**
|
||||
* Adds to `turn.editedPaths` the `file_path` (`notebook_path` for NotebookEdit)
|
||||
* of each successful file-tool edit among the tool results of a subagent
|
||||
* message. Root-agent messages (`parentToolUseId === null`) are skipped: the
|
||||
* checkpoint already tracks their files. Must run before `toAcpNotifications`
|
||||
* drops the tool use cache entries.
|
||||
*/
|
||||
export function recordSubagentFileEdits(
|
||||
turn: (NativeFileChangeReportTurn & { settled: boolean }) | null | undefined,
|
||||
parentToolUseId: string | null,
|
||||
content: unknown,
|
||||
toolUses: Record<string, { name: string; input: unknown } | undefined>,
|
||||
): void {
|
||||
if (parentToolUseId === null || !turn || turn.settled || !turn.fileChangeReport) return;
|
||||
if (!Array.isArray(content)) return;
|
||||
for (const block of content) {
|
||||
if (
|
||||
block?.type !== "tool_result" ||
|
||||
block.is_error === true ||
|
||||
typeof block.tool_use_id !== "string"
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const toolUse = toolUses[block.tool_use_id];
|
||||
if (!toolUse || !FILE_EDIT_TOOLS.has(toolUse.name)) continue;
|
||||
const input = toolUse.input as Record<string, unknown> | undefined;
|
||||
const editedPath = toolUse.name === "NotebookEdit" ? input?.notebook_path : input?.file_path;
|
||||
if (typeof editedPath === "string" && editedPath.length > 0) {
|
||||
(turn.editedPaths ??= new Set()).add(editedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createNativeFileChangeReporter(
|
||||
options: NativeFileChangeReporterOptions,
|
||||
): NativeFileChangeReporter {
|
||||
@@ -101,7 +140,7 @@ export function createNativeFileChangeReporter(
|
||||
const timeoutMs = options.timeoutMs ?? AGENT_FILE_CHANGE_REPORT_TIMEOUT_MS;
|
||||
const requestIds = new Set<string>();
|
||||
|
||||
const publish = async (state: FileChangeReportTurnState, result: AgentFileChangeReportResult) => {
|
||||
async function publish(state: FileChangeReportTurnState, result: AgentFileChangeReportResult) {
|
||||
if (state.phase === "finished") return;
|
||||
state.phase = "finished";
|
||||
try {
|
||||
@@ -109,24 +148,55 @@ export function createNativeFileChangeReporter(
|
||||
} catch (error) {
|
||||
options.logError(`Failed to publish file-change report ${state.requestId}: ${error}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const finishUnavailable = async (
|
||||
async function finishUnavailable(
|
||||
state: FileChangeReportTurnState,
|
||||
reason: FileChangeReportUnavailableReason,
|
||||
) => {
|
||||
) {
|
||||
await publish(state, {
|
||||
version: 1,
|
||||
requestId: state.requestId,
|
||||
status: "unavailable",
|
||||
reason,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const report = async (
|
||||
async function publishReported(state: FileChangeReportTurnState, reportedPaths: string[]) {
|
||||
const normalized = normalizeReportedPaths(reportedPaths, workspace);
|
||||
await publish(
|
||||
state,
|
||||
fitReportedAgentFileChangeReport({
|
||||
version: 1,
|
||||
requestId: state.requestId,
|
||||
status: "reported",
|
||||
paths: normalized.paths,
|
||||
// Checkpoints miss Bash mutations. Subagent file-tool edits are added
|
||||
// from the message stream, but the list is still not exhaustive.
|
||||
declaredComplete: false,
|
||||
truncated: normalized.truncated,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Used when the checkpoint preview fails: the subagent edits seen in the
|
||||
// stream are still worth reporting.
|
||||
async function finishWithoutPreview(
|
||||
state: FileChangeReportTurnState,
|
||||
editedPaths: Set<string> | undefined,
|
||||
reason: FileChangeReportUnavailableReason,
|
||||
) {
|
||||
if (editedPaths && editedPaths.size > 0) {
|
||||
await publishReported(state, [...editedPaths]);
|
||||
} else {
|
||||
await finishUnavailable(state, reason);
|
||||
}
|
||||
}
|
||||
|
||||
async function report(
|
||||
turn: NativeFileChangeReportTurn | null | undefined,
|
||||
query: Pick<Query, "rewindFiles">,
|
||||
) => {
|
||||
) {
|
||||
const state = turn?.fileChangeReport;
|
||||
if (!state || state.phase !== "requested") return;
|
||||
state.phase = "collecting";
|
||||
@@ -145,35 +215,22 @@ export function createNativeFileChangeReporter(
|
||||
}),
|
||||
]);
|
||||
if (preview === null) {
|
||||
await finishUnavailable(state, "timeout");
|
||||
await finishWithoutPreview(state, turn.editedPaths, "timeout");
|
||||
return;
|
||||
}
|
||||
if (!preview.canRewind || !Array.isArray(preview.filesChanged)) {
|
||||
await finishUnavailable(state, "invalidOutput");
|
||||
await finishWithoutPreview(state, turn.editedPaths, "invalidOutput");
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = normalizeReportedPaths(preview.filesChanged, workspace);
|
||||
await publish(
|
||||
state,
|
||||
fitReportedAgentFileChangeReport({
|
||||
version: 1,
|
||||
requestId: state.requestId,
|
||||
status: "reported",
|
||||
paths: normalized.paths,
|
||||
// Checkpoints cover Claude file tools, but not every mutation source
|
||||
// (notably Bash and most subagents), so this list is never exhaustive.
|
||||
declaredComplete: false,
|
||||
truncated: normalized.truncated,
|
||||
}),
|
||||
);
|
||||
await publishReported(state, [...preview.filesChanged, ...(turn.editedPaths ?? [])]);
|
||||
} catch (error) {
|
||||
options.logError(`Failed to inspect Claude file checkpoint ${turn.promptUuid}: ${error}`);
|
||||
await finishUnavailable(state, "providerError");
|
||||
await finishWithoutPreview(state, turn.editedPaths, "providerError");
|
||||
} finally {
|
||||
if (timer) clearTimeout(timer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
request(meta) {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { ForkSessionRequest, ForkSessionResponse, RequestError } from "@agentclientprotocol/sdk";
|
||||
import {
|
||||
ForkSessionRequest,
|
||||
ForkSessionResponse,
|
||||
RequestError,
|
||||
} from "@agentclientprotocol/sdk/unstable";
|
||||
import {
|
||||
forkSession as forkClaudeSession,
|
||||
getSessionMessages,
|
||||
|
||||
223
src/gateway-models.ts
Normal file
223
src/gateway-models.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Model discovery for a gateway set via `providers/set`.
|
||||
*
|
||||
* An organization can restrict which models its gateway serves, and the
|
||||
* gateway's listing returns only those: `GET /v1/models` for an `anthropic`
|
||||
* gateway, `GET /foundation-models` (Bedrock `ListFoundationModels`) for a
|
||||
* `bedrock` one. The adapter lists them once when the provider is set, and the
|
||||
* model picker then offers only the SDK models the gateway listed (see
|
||||
* `narrowToGatewayModels` in `session-model.ts`). A listing that fails fails
|
||||
* `providers/set` with a {@link GatewayListingError}.
|
||||
*/
|
||||
|
||||
const LISTING_TIMEOUT_MS = 10_000;
|
||||
const ANTHROPIC_PAGE_LIMIT = "1000";
|
||||
const MAX_LISTING_PAGES = 20;
|
||||
const MAX_LOGGED_BODY_CHARS = 500;
|
||||
const ANTHROPIC_VERSION = "2023-06-01";
|
||||
|
||||
/**
|
||||
* The bearer token `createEnvForProvider` sets as `ANTHROPIC_AUTH_TOKEN` and
|
||||
* `AWS_BEARER_TOKEN_BEDROCK`, so the CLI sends `Authorization: Bearer acp-proxy`
|
||||
* unless the provider headers replace it. The listing sends the same, so the
|
||||
* gateway sees the same auth as on chat requests.
|
||||
*/
|
||||
export const PROVIDER_AUTH_TOKEN_PLACEHOLDER = "acp-proxy";
|
||||
|
||||
/**
|
||||
* The cross-region inference profile prefixes the CLI knows:
|
||||
* `eu.anthropic.claude-sonnet-5`. `ANTHROPIC_BEDROCK_REGION_PREFIX` accepts all
|
||||
* of them but `us-gov`, which the CLI takes only from a `us-gov-*` AWS region;
|
||||
* a gateway listing only `us-gov.*` ids would then get an empty picker. That
|
||||
* case is theoretical, so it is left as is.
|
||||
*/
|
||||
const BEDROCK_REGION_PREFIXES = ["us", "eu", "apac", "jp", "au", "us-gov", "global"];
|
||||
|
||||
type ListingLogger = { log: (...args: any[]) => void; error: (...args: any[]) => void };
|
||||
|
||||
type ListingPage = { ids: string[]; nextQuery?: Record<string, string> };
|
||||
|
||||
/**
|
||||
* A gateway listing that failed: a network error, a status other than 2xx or
|
||||
* 4xx, a body that is not the expected listing, or the 10 s timeout. The
|
||||
* message names the request path and the reason, never a header value.
|
||||
*/
|
||||
export class GatewayListingError extends Error {
|
||||
override name = "GatewayListingError";
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the model ids an `anthropic` gateway at `baseUrl` serves, following
|
||||
* `has_more` / `last_id` pagination. Returns `undefined` when the gateway
|
||||
* answers 4xx: it has no listing, and nothing is narrowed. Throws a
|
||||
* {@link GatewayListingError} when the listing fails. Header values are never
|
||||
* logged.
|
||||
*/
|
||||
export async function listGatewayModels(
|
||||
baseUrl: string,
|
||||
providerHeaders: Record<string, string>,
|
||||
logger: ListingLogger,
|
||||
): Promise<string[] | undefined> {
|
||||
return listPages(
|
||||
`${baseUrl.replace(/\/+$/, "")}/v1/models`,
|
||||
{ limit: ANTHROPIC_PAGE_LIMIT },
|
||||
listingHeaders(providerHeaders, { "anthropic-version": ANTHROPIC_VERSION }),
|
||||
logger,
|
||||
(body) => {
|
||||
const page = body as { data?: unknown; has_more?: unknown; last_id?: unknown };
|
||||
if (!Array.isArray(page.data)) return undefined;
|
||||
return {
|
||||
ids: stringField(page.data, "id"),
|
||||
nextQuery:
|
||||
page.has_more === true && typeof page.last_id === "string"
|
||||
? { limit: ANTHROPIC_PAGE_LIMIT, after_id: page.last_id }
|
||||
: undefined,
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the Anthropic models a `bedrock` gateway at `baseUrl` serves
|
||||
* (`GET /foundation-models`, Bedrock `ListFoundationModels`, one page), the
|
||||
* listing AI Router answers on its Bedrock route. Returns the listed ids, such
|
||||
* as `global.anthropic.claude-sonnet-5-5`; `undefined` and the errors are
|
||||
* those of {@link listGatewayModels}.
|
||||
*/
|
||||
export async function listBedrockModels(
|
||||
baseUrl: string,
|
||||
providerHeaders: Record<string, string>,
|
||||
logger: ListingLogger,
|
||||
): Promise<string[] | undefined> {
|
||||
const ids = await listPages(
|
||||
`${baseUrl.replace(/\/+$/, "")}/foundation-models`,
|
||||
{},
|
||||
listingHeaders(providerHeaders, {}),
|
||||
logger,
|
||||
(body) => {
|
||||
const page = body as { modelSummaries?: unknown };
|
||||
if (!Array.isArray(page.modelSummaries)) return undefined;
|
||||
return { ids: stringField(page.modelSummaries, "modelId") };
|
||||
},
|
||||
);
|
||||
return ids?.filter((id) => id.includes("anthropic."));
|
||||
}
|
||||
|
||||
/** The cross-region prefix of a Bedrock model id or ARN, if it has one. */
|
||||
export function bedrockRegionPrefix(modelId: string): string | undefined {
|
||||
const id = modelId.slice(modelId.lastIndexOf("/") + 1).toLowerCase();
|
||||
return BEDROCK_REGION_PREFIXES.find((prefix) => id.startsWith(`${prefix}.anthropic.`));
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks the region of the listed Bedrock models: `global` when the gateway
|
||||
* lists it, else the prefix of the first id that has one. A gateway that lists
|
||||
* `eu.*` and `us.*` ids gets the prefix that comes first, and the ids of the
|
||||
* other prefixes are dropped, so the picker offers only models the CLI can
|
||||
* call under that prefix. Ids without a prefix (`anthropic.claude-…`) are
|
||||
* dropped too once a prefix is picked; without any prefix every id is kept.
|
||||
*/
|
||||
export function selectBedrockRegion(modelIds: readonly string[]): {
|
||||
regionPrefix?: string;
|
||||
modelIds: string[];
|
||||
} {
|
||||
const prefixes = modelIds.map(bedrockRegionPrefix);
|
||||
const regionPrefix = prefixes.includes("global")
|
||||
? "global"
|
||||
: prefixes.find((prefix) => prefix !== undefined);
|
||||
if (!regionPrefix) return { modelIds: [...modelIds] };
|
||||
return {
|
||||
regionPrefix,
|
||||
modelIds: modelIds.filter((id) => bedrockRegionPrefix(id) === regionPrefix),
|
||||
};
|
||||
}
|
||||
|
||||
// Provider headers win over the defaults, matched case-insensitively, the way
|
||||
// `ANTHROPIC_CUSTOM_HEADERS` overrides the CLI's own headers.
|
||||
function listingHeaders(
|
||||
providerHeaders: Record<string, string>,
|
||||
protocolHeaders: Record<string, string>,
|
||||
): Headers {
|
||||
const headers = new Headers({
|
||||
authorization: `Bearer ${PROVIDER_AUTH_TOKEN_PLACEHOLDER}`,
|
||||
accept: "application/json",
|
||||
...protocolHeaders,
|
||||
});
|
||||
for (const [name, value] of Object.entries(providerHeaders)) {
|
||||
// `Headers.set` puts an invalid value into its error message, so name only the header.
|
||||
try {
|
||||
headers.set(name, value);
|
||||
} catch {
|
||||
throw new GatewayListingError(`Model listing cannot send the invalid header "${name}"`);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
function stringField(entries: unknown[], field: string): string[] {
|
||||
return entries.flatMap((entry) => {
|
||||
const value = (entry as Record<string, unknown> | null)?.[field];
|
||||
return typeof value === "string" && value.trim() !== "" ? [value] : [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads `endpoint` page by page; `parse` returns `undefined` for a body it does
|
||||
* not understand. A 4xx answer on any page means the gateway has no listing:
|
||||
* the result is `undefined`, and nothing is narrowed.
|
||||
*/
|
||||
async function listPages(
|
||||
endpoint: string,
|
||||
firstQuery: Record<string, string>,
|
||||
headers: Headers,
|
||||
logger: ListingLogger,
|
||||
parse: (body: unknown) => ListingPage | undefined,
|
||||
): Promise<string[] | undefined> {
|
||||
const { pathname: path, username, password } = new URL(endpoint);
|
||||
const failure = (reason: string): GatewayListingError => {
|
||||
const error = new GatewayListingError(`Model listing GET ${path} ${reason}`);
|
||||
logger.error(`[providers/set] ${error.message}`);
|
||||
return error;
|
||||
};
|
||||
// fetch refuses such a URL and repeats it, credentials included, in its error.
|
||||
if (username || password) throw failure("cannot be sent: the base URL carries credentials");
|
||||
const signal = AbortSignal.timeout(LISTING_TIMEOUT_MS);
|
||||
const ids: string[] = [];
|
||||
let query: Record<string, string> | undefined = firstQuery;
|
||||
for (let page = 0; page < MAX_LISTING_PAGES && query; page++) {
|
||||
const url = new URL(endpoint);
|
||||
for (const [name, value] of Object.entries(query)) url.searchParams.set(name, value);
|
||||
let response: Response;
|
||||
let body: string;
|
||||
try {
|
||||
response = await fetch(url, { headers, signal });
|
||||
body = await response.text();
|
||||
} catch (error) {
|
||||
throw failure(`failed: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
if (response.status >= 400 && response.status < 500) {
|
||||
logger.log(
|
||||
`[providers/set] Model listing GET ${path} returned ${response.status}: the gateway has no listing, models are not narrowed`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw failure(`returned ${response.status}: ${body.slice(0, MAX_LOGGED_BODY_CHARS)}`);
|
||||
}
|
||||
let parsed: ListingPage | undefined;
|
||||
try {
|
||||
parsed = parse(JSON.parse(body));
|
||||
} catch {
|
||||
parsed = undefined;
|
||||
}
|
||||
if (!parsed) throw failure("returned an unexpected body");
|
||||
ids.push(...parsed.ids);
|
||||
query = parsed.nextQuery;
|
||||
}
|
||||
if (query) {
|
||||
logger.error(
|
||||
`[providers/set] Model listing GET ${path} still had more pages after ${MAX_LISTING_PAGES}; using what was read`,
|
||||
);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequestError } from "@agentclientprotocol/sdk";
|
||||
import { RequestError } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { SDKActiveGoalMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
export const GOAL_EXTENSION_VERSION = 1 as const;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
* until the next prompt. Awaiting it would add a CLI run to every turn.
|
||||
*/
|
||||
|
||||
import { RequestError } from "@agentclientprotocol/sdk";
|
||||
import { RequestError } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { AccountInfo, Query } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
export function shouldHideClaudeAuth(): boolean {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { claudeCliPath } from "./acp-agent.js";
|
||||
import { applyManagedPolicyEnv } from "./managed-policy.js";
|
||||
import { serveAcp } from "./serve.js";
|
||||
import { acpProtocolsFromEnv, serveAcp } from "./serve.js";
|
||||
import packageJson from "../package.json" with { type: "json" };
|
||||
import { appendFileSync, mkdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
@@ -83,9 +83,8 @@ if (process.argv.includes("--cli")) {
|
||||
: undefined;
|
||||
logger?.log("Claude ACP started");
|
||||
|
||||
// The draft ACP v2 is opt-in while it is a draft.
|
||||
const acp = serveAcp(logger, {
|
||||
experimentalV2: process.env.CLAUDE_AGENT_ACP_EXPERIMENTAL_V2 === "1",
|
||||
protocols: acpProtocolsFromEnv(process.env.CLAUDE_AGENT_ACP_PROTOCOL),
|
||||
});
|
||||
|
||||
async function shutdown() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AvailableCommand } from "@agentclientprotocol/sdk";
|
||||
import type { AvailableCommand } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { McpServerStatus, Query } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { escapeMarkdown } from "./usage-markdown.js";
|
||||
import { raceTimeoutAndAbort } from "./utils.js";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
import type { RequestPermissionResponse } from "@agentclientprotocol/sdk";
|
||||
import type { RequestPermissionResponse } from "@agentclientprotocol/sdk/unstable";
|
||||
import type {
|
||||
PermissionMode,
|
||||
PermissionResult,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk";
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk/unstable";
|
||||
import {
|
||||
buildEditPermissionOptions,
|
||||
buildGlobPermissionOptions,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk";
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk/unstable";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DurablePermissionChangeSet } from "../normalization.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk";
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { DurablePermissionChangeSet } from "../normalization.js";
|
||||
|
||||
export const PERMISSION_OPTION_ID = {
|
||||
@@ -30,6 +30,9 @@ export interface PermissionOptionContext {
|
||||
/** The mode the session left when it entered plan mode. */
|
||||
prePlanMode?: string;
|
||||
contextUsedPercent?: number;
|
||||
/** False when no ACP turn can continue an accepted plan in a fresh
|
||||
* context. Then an ExitPlanMode request offers no clear-context option. */
|
||||
allowContextReset?: boolean;
|
||||
}
|
||||
|
||||
export function allowOnce(name = "Yes"): PermissionOption {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk";
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk/unstable";
|
||||
import path from "node:path";
|
||||
import type { DurablePermissionChangeSet } from "../normalization.js";
|
||||
import { plainString, type PermissionOptionContext, withOptionalUpdate } from "./shared.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk";
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk/unstable";
|
||||
import {
|
||||
allowOnce,
|
||||
exactLocalAllowRule,
|
||||
@@ -70,7 +70,7 @@ export function buildExitPlanModePermissionOptions(
|
||||
: (["auto", "bypassPermissions"] as const)
|
||||
).filter((mode) => modes.has(mode));
|
||||
const preferredMode = elevatedModes[0] ?? "acceptEdits";
|
||||
if (plainString(context.input.plan)) {
|
||||
if (context.allowContextReset !== false && plainString(context.input.plan)) {
|
||||
const usage =
|
||||
context.contextUsedPercent === undefined ? "" : ` (${context.contextUsedPercent}% used)`;
|
||||
options.push(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RequestPermissionRequest, ToolCallContent } from "@agentclientprotocol/sdk";
|
||||
import type { RequestPermissionRequest, ToolCallContent } from "@agentclientprotocol/sdk/unstable";
|
||||
import { AIR_PERMISSION_KEY, withAirMeta } from "../air-extension.js";
|
||||
import { ClientCapabilities } from "../tool-calls/client-capabilities.js";
|
||||
import { AcpToolCallRenderer } from "../tool-calls/renderer.js";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { RequestPermissionResponse } from "@agentclientprotocol/sdk";
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk";
|
||||
import type { RequestPermissionResponse } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { PermissionOption } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { PermissionMode, PermissionResult } from "@anthropic-ai/claude-agent-sdk";
|
||||
import type { DurablePermissionChangeSet } from "./normalization.js";
|
||||
import { applyClaudePermissionSelection, parseClaudePermissionSelection } from "./effects.js";
|
||||
|
||||
44
src/prompt-suggestions.ts
Normal file
44
src/prompt-suggestions.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* The follow-up prompt the SDK predicts for the next turn.
|
||||
*
|
||||
* With `promptSuggestions` on, the CLI emits one `prompt_suggestion` message
|
||||
* after a turn's `result` holding the prompt it expects the user to send next.
|
||||
* ACP has no field for it, so it travels as
|
||||
* `session_info_update._meta.promptSuggestions`: a client that does not read
|
||||
* the key sees a session info update that says nothing and ignores it.
|
||||
*
|
||||
* AIR renders the first non-blank `suggestions[].text` as gray text in an empty
|
||||
* composer, where Tab fills the composer without sending.
|
||||
*
|
||||
* The CLI sends no suggestion on the first turn of a session, after an API
|
||||
* error, in plan mode, and at the account's usage limit, so most sessions have
|
||||
* turns that end with no prediction at all.
|
||||
*/
|
||||
|
||||
/** The `_meta` key the suggestion travels under. It names no vendor. */
|
||||
export const PROMPT_SUGGESTIONS_META_KEY = "promptSuggestions";
|
||||
|
||||
/** One predicted prompt, as the `_meta` value carries it. */
|
||||
export type PromptSuggestionsMetaValue = {
|
||||
suggestions: { text: string }[];
|
||||
};
|
||||
|
||||
/**
|
||||
* The suggestion as it goes on the wire, or `null` for a suggestion that says
|
||||
* nothing: the CLI can predict whitespace, and an empty text would otherwise
|
||||
* reach the composer as a hint with no content.
|
||||
*/
|
||||
export function promptSuggestionText(raw: string | null | undefined): string | null {
|
||||
const text = raw?.trim();
|
||||
return text ? text : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `_meta` of the session info update that publishes `text`. `null` sends an
|
||||
* empty `suggestions` list, which clears the prompt the client holds — the turn
|
||||
* predicted nothing, so the composer must not keep the previous prediction.
|
||||
*/
|
||||
export function promptSuggestionsMeta(text: string | null): Record<string, unknown> {
|
||||
const value: PromptSuggestionsMetaValue = { suggestions: text === null ? [] : [{ text }] };
|
||||
return { [PROMPT_SUGGESTIONS_META_KEY]: value };
|
||||
}
|
||||
51
src/serve.ts
51
src/serve.ts
@@ -1,19 +1,33 @@
|
||||
import { ndJsonStream } from "@agentclientprotocol/sdk";
|
||||
import { backgroundWorkStream } from "./background-work-experiment.js";
|
||||
import * as v2 from "@agentclientprotocol/sdk/experimental/v2";
|
||||
import { v1AgentApp, type ClaudeAcpAgent, type Logger } from "./acp-agent.js";
|
||||
import { nodeToWebReadable, nodeToWebWritable } from "./utils.js";
|
||||
import { v2AgentApp } from "./v2/agent.js";
|
||||
|
||||
/**
|
||||
* Serves ACP on stdio.
|
||||
*
|
||||
* With `experimentalV2`, the connection goes through {@link acpProtocolRouter}.
|
||||
* Without it, the adapter serves ACP v1 alone and never routes by protocol
|
||||
* version, exactly as before.
|
||||
* The ACP versions that the adapter serves: `v1` or `v2` alone, or both with
|
||||
* `auto`, where the client's `initialize` picks the version.
|
||||
*/
|
||||
export type AcpProtocols = "auto" | "v1" | "v2";
|
||||
|
||||
/**
|
||||
* Reads `CLAUDE_AGENT_ACP_PROTOCOL`: unset, empty, or `auto` serves both
|
||||
* versions, and `v1` or `v2` serves that version alone. Throws on any other
|
||||
* value, so that a typo such as `CLAUDE_AGENT_ACP_PROTOCOL=1` fails at startup
|
||||
* instead of silently serving both.
|
||||
*/
|
||||
export function acpProtocolsFromEnv(value: string | undefined): AcpProtocols {
|
||||
if (value === undefined || value === "") return "auto";
|
||||
if (value === "auto" || value === "v1" || value === "v2") return value;
|
||||
throw new Error(
|
||||
`CLAUDE_AGENT_ACP_PROTOCOL must be "auto", "v1", or "v2", not ${JSON.stringify(value)}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** Serves ACP on stdio through {@link acpProtocolRouter}. */
|
||||
export function serveAcp(
|
||||
logger: Logger | undefined,
|
||||
options: { experimentalV2: boolean },
|
||||
options: { protocols: AcpProtocols },
|
||||
): { closed: Promise<void>; dispose(): Promise<void> } {
|
||||
const output = nodeToWebWritable(process.stdout);
|
||||
const input = nodeToWebReadable(process.stdin);
|
||||
@@ -23,9 +37,9 @@ export function serveAcp(
|
||||
const onAgent = (created: ClaudeAcpAgent) => {
|
||||
agent = created;
|
||||
};
|
||||
const { closed } = options.experimentalV2
|
||||
? acpProtocolRouter(logger, onAgent).connect(v2.ndJsonStream(output, input))
|
||||
: v1AgentApp(logger, onAgent).connect(ndJsonStream(output, input));
|
||||
const { closed } = acpProtocolRouter(logger, onAgent, options.protocols).connect(
|
||||
backgroundWorkStream(v2.ndJsonStream(output, input)),
|
||||
);
|
||||
return {
|
||||
closed,
|
||||
dispose: async () => {
|
||||
@@ -36,16 +50,19 @@ export function serveAcp(
|
||||
|
||||
/**
|
||||
* Routes a connection to the protocol version that the client negotiates in
|
||||
* `initialize`: the experimental draft ACP v2 for a client that requests
|
||||
* version 2 or later, and ACP v1 otherwise. `onAgent` receives the agent of
|
||||
* the connection once the router has opened it.
|
||||
* `initialize`. With `auto`, a client that requests version 2 or later gets
|
||||
* the draft ACP v2, and every other client gets ACP v1. With `v1`, a v2
|
||||
* client is negotiated down to v1. With `v2`, the router rejects the
|
||||
* `initialize` of a v1 client as an unsupported protocol version. `onAgent`
|
||||
* receives the agent of the connection once the router has opened it.
|
||||
*/
|
||||
export function acpProtocolRouter(
|
||||
logger: Logger | undefined,
|
||||
onAgent: (agent: ClaudeAcpAgent) => void,
|
||||
protocols: AcpProtocols = "auto",
|
||||
): v2.AgentProtocolRouter {
|
||||
return v2
|
||||
.agentProtocolRouter()
|
||||
.withV1(v1AgentApp(logger, onAgent))
|
||||
.withV2(v2AgentApp(logger, onAgent));
|
||||
const router = v2.agentProtocolRouter();
|
||||
if (protocols !== "v2") router.withV1(v1AgentApp(logger, onAgent));
|
||||
if (protocols !== "v1") router.withV2(v2AgentApp(logger, onAgent));
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SessionConfigOption } from "@agentclientprotocol/sdk";
|
||||
import type { SessionConfigOption } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { EffortLevel, ModelInfo, Settings } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { AIR_RECOMMENDED_CONFIG_VALUE_CAPABILITY, withAirMeta } from "./air-extension.js";
|
||||
import { EFFORT_CONFIG_ID } from "./session-config-ids.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk";
|
||||
import type { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk/unstable";
|
||||
import {
|
||||
getSessionMessages,
|
||||
type SDKAssistantMessageError,
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
* - an interactive CLI that is not driven by an SDK holds it, and its registry
|
||||
* status is newer than the transcript: the registry status (an idle one is
|
||||
* `error` after such a turn);
|
||||
* - a turn that ended with an open background task is `running`, also after
|
||||
* 10 minutes: Claude Code wakes the session again when the task ends. The
|
||||
* wait ends when the registry status is `idle`, because a task can end
|
||||
* without a transcript record (a stop in the task UI);
|
||||
* - otherwise the transcript tail: a finished turn is `idle` (`error` after
|
||||
* an API error), an unfinished turn written in the last 10 minutes is
|
||||
* `running`, and anything else has no known state.
|
||||
@@ -72,6 +76,14 @@ function usesRegistryStatus(record: LiveRecord, transcriptMtimeMs: number): bool
|
||||
);
|
||||
}
|
||||
|
||||
/** Whether the last turn ended while a background task of the session is
|
||||
* open. */
|
||||
function waitsForBackgroundTasks(facts: TranscriptFacts): boolean {
|
||||
if (facts.turnState !== "finished") return false;
|
||||
const openTasks = facts.backgroundTasks?.open ?? [];
|
||||
return openTasks.length > 0;
|
||||
}
|
||||
|
||||
function iso(ms: number | undefined): string | undefined {
|
||||
return ms === undefined || !Number.isFinite(ms) ? undefined : new Date(ms).toISOString();
|
||||
}
|
||||
@@ -103,6 +115,9 @@ export function deriveActivity(input: {
|
||||
// A busy or waiting CLI wins; an idle one shows how its turn ended.
|
||||
state = registryState(live.status);
|
||||
if (state === "idle" && failed) state = "error";
|
||||
} else if (waitsForBackgroundTasks(facts) && live.status !== "idle") {
|
||||
// Claude Code wakes the session again when an open task ends.
|
||||
state = "running";
|
||||
} else if (failed) {
|
||||
state = "error";
|
||||
} else if (facts.turnState === "finished") {
|
||||
|
||||
223
src/session-index/background-tasks.ts
Normal file
223
src/session-index/background-tasks.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* The background tasks of a session that did not end yet, from its transcript.
|
||||
*
|
||||
* A Claude Code session can start a background shell command or a background
|
||||
* sub-agent and end its turn at once. When the task ends, Claude Code wakes
|
||||
* the session with a `<task-notification>` input for a short turn. So a turn
|
||||
* end with an open task is no end of the work: the session list keeps such a
|
||||
* session `running` (see `activity.ts`).
|
||||
*
|
||||
* The records, as Claude Code 2.1.299 writes them, in the rules of AIR's
|
||||
* native Claude provider:
|
||||
*
|
||||
* - a tool result opens a task when its `toolUseResult` has a
|
||||
* `backgroundTaskId` (a shell command), or `isAsync: true` with an
|
||||
* `agentId` (a sub-agent);
|
||||
* - a task closes when its notification reaches the session: as a user
|
||||
* record, or as a `queue-operation` `remove` when a running turn takes it
|
||||
* in. A notification that waits in the queue (`enqueue`) keeps the task
|
||||
* open, because the session still runs a turn for it;
|
||||
* - only a notification with a `<status>` closes a task. A notification
|
||||
* without one comes while the task still runs: the check-in of a quiet
|
||||
* shell ("is still running after …", since Claude Code 2.1.303) or a
|
||||
* `Monitor` event;
|
||||
* - a `TaskStop` result closes the task of its `toolUseResult.task_id`:
|
||||
* Claude Code writes no notification for it. The result does not name its
|
||||
* tool, so its `tool_use_id` must match a `TaskStop` call. A `task_id` in
|
||||
* the result of another tool keeps the task open;
|
||||
* - a resumed CLI writes a `stopped` notification for each task of the
|
||||
* previous process;
|
||||
* - a sidechain record belongs to a sub-agent and opens or closes no task of
|
||||
* the session.
|
||||
*
|
||||
* A background shell that does not end, for example a dev server, keeps its
|
||||
* task open, so its session stays `running` while the CLI holds it.
|
||||
*
|
||||
* A task that a sub-agent starts is not seen. Its start record is only in the
|
||||
* transcript of the sub-agent, so the session can be `idle` while the
|
||||
* sub-agent waits for it.
|
||||
*/
|
||||
|
||||
import { createReadStream } from "node:fs";
|
||||
import { createInterface } from "node:readline";
|
||||
import { TASK_NOTIFICATION_TAG, taskNotificationsOf } from "../task-notifications.js";
|
||||
|
||||
export type BackgroundTasks = {
|
||||
/** The ids of the tasks that started and did not end, in start order. */
|
||||
open: string[];
|
||||
/** The `tool_use` ids of the `TaskStop` calls without a result. */
|
||||
stopCalls: string[];
|
||||
/** Whether the sets hold the records of the whole file. A set from the
|
||||
* tail window only does not know a task that started before it. */
|
||||
complete: boolean;
|
||||
};
|
||||
|
||||
/** The fields of a transcript record that bear on a task. */
|
||||
type TranscriptRecord = {
|
||||
type?: string;
|
||||
isSidechain?: boolean;
|
||||
/** Of a `queue-operation` record: `enqueue`, `remove`, ... */
|
||||
operation?: string;
|
||||
/** Of a `queue-operation` record: the queued input. */
|
||||
content?: string;
|
||||
message?: { content?: string | ContentBlock[] };
|
||||
toolUseResult?: ToolUseResult;
|
||||
};
|
||||
|
||||
type ContentBlock = {
|
||||
type?: string;
|
||||
text?: string;
|
||||
/** Of a `tool_use` block. */
|
||||
id?: string;
|
||||
name?: string;
|
||||
/** Of a `tool_result` block. */
|
||||
tool_use_id?: string;
|
||||
};
|
||||
|
||||
type ToolUseResult = {
|
||||
/** The task of a background shell command. */
|
||||
backgroundTaskId?: string;
|
||||
/** With `agentId`: an async sub-agent. */
|
||||
isAsync?: boolean;
|
||||
agentId?: string;
|
||||
/** The task that a `TaskStop` call stopped. Another tool can give one. */
|
||||
task_id?: string;
|
||||
};
|
||||
|
||||
const NOTIFICATION_TAG = `<${TASK_NOTIFICATION_TAG}>`;
|
||||
|
||||
/** Text that a line must hold to bear on a task, so that the other lines
|
||||
* are not parsed. */
|
||||
const TASK_MARKERS = ["backgroundTaskId", '"isAsync"', NOTIFICATION_TAG, "TaskStop", "task_id"];
|
||||
|
||||
function parseRecord(line: string): TranscriptRecord | undefined {
|
||||
try {
|
||||
return JSON.parse(line) as TranscriptRecord;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function blocksOf(record: TranscriptRecord): ContentBlock[] {
|
||||
const content = record.message?.content;
|
||||
return Array.isArray(content) ? content : [];
|
||||
}
|
||||
|
||||
/** The input of a user record: its string content, else its first text
|
||||
* block. */
|
||||
function inputTextOf(record: TranscriptRecord): string | undefined {
|
||||
const content = record.message?.content;
|
||||
if (typeof content === "string") return content;
|
||||
return blocksOf(record).find((block) => block.type === "text")?.text;
|
||||
}
|
||||
|
||||
/** The task that a `<task-notification>` input ends: only an input that
|
||||
* starts with the tag, after whitespace, as AIR's `TaskNotification` reads
|
||||
* it. Only a notification with a `<status>` ends a task (see
|
||||
* `taskNotificationsOf`). */
|
||||
export function endedTaskId(input: string | undefined): string | undefined {
|
||||
if (!input?.trimStart().startsWith(NOTIFICATION_TAG)) return undefined;
|
||||
return taskNotificationsOf(input)[0]?.task_id;
|
||||
}
|
||||
|
||||
/** The task that a tool result started: a background shell command, or an
|
||||
* async sub-agent. */
|
||||
function startedTaskId(record: TranscriptRecord): string | undefined {
|
||||
if (record.type !== "user") return undefined;
|
||||
const result = record.toolUseResult;
|
||||
if (result?.backgroundTaskId) return result.backgroundTaskId;
|
||||
if (result?.isAsync) return result.agentId;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** The task whose notification reached the session: as a user record, or
|
||||
* as a queue `remove` when a running turn takes it in. */
|
||||
function deliveredTaskId(record: TranscriptRecord): string | undefined {
|
||||
if (record.type === "user") return endedTaskId(inputTextOf(record));
|
||||
if (record.type === "queue-operation" && record.operation === "remove") {
|
||||
return endedTaskId(record.content);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Collects the tasks of the lines it is given, in file order. */
|
||||
class TaskTracker {
|
||||
private readonly open: Set<string>;
|
||||
private readonly stopCalls: Set<string>;
|
||||
|
||||
constructor(after?: BackgroundTasks) {
|
||||
this.open = new Set(after?.open);
|
||||
this.stopCalls = new Set(after?.stopCalls);
|
||||
}
|
||||
|
||||
add(line: string): void {
|
||||
const mayBearOnTask = TASK_MARKERS.some((marker) => line.includes(marker));
|
||||
if (!mayBearOnTask) return;
|
||||
const record = parseRecord(line);
|
||||
// A sidechain record belongs to a sub-agent, which receives the
|
||||
// notifications of its own tasks.
|
||||
if (!record || record.isSidechain) return;
|
||||
this.apply(record);
|
||||
}
|
||||
|
||||
result({ complete }: { complete: boolean }): BackgroundTasks {
|
||||
return { open: [...this.open], stopCalls: [...this.stopCalls], complete };
|
||||
}
|
||||
|
||||
private apply(record: TranscriptRecord): void {
|
||||
const started = startedTaskId(record);
|
||||
if (started) this.open.add(started);
|
||||
const delivered = deliveredTaskId(record);
|
||||
if (delivered) this.open.delete(delivered);
|
||||
this.applyTaskStop(record);
|
||||
}
|
||||
|
||||
/** A `task_id` stops its task only in the result of a `TaskStop` call:
|
||||
* another tool can give a `task_id` too. */
|
||||
private applyTaskStop(record: TranscriptRecord): void {
|
||||
let isTaskStopResult = false;
|
||||
for (const block of blocksOf(record)) {
|
||||
if (block.type === "tool_use" && block.name === "TaskStop" && block.id) {
|
||||
this.stopCalls.add(block.id);
|
||||
}
|
||||
const resultId = block.tool_use_id;
|
||||
if (resultId && this.stopCalls.has(resultId)) {
|
||||
this.stopCalls.delete(resultId);
|
||||
isTaskStopResult = true;
|
||||
}
|
||||
}
|
||||
const stopped = record.toolUseResult?.task_id;
|
||||
if (isTaskStopResult && stopped) this.open.delete(stopped);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The tasks of the lines of `text`, applied on the tasks `after`.
|
||||
*
|
||||
* `after` can be what an earlier scan found for a shorter copy of the file,
|
||||
* and `text` can repeat records of that copy. The result is still the same:
|
||||
* the last record that opens or closes a task decides, and `text` holds the
|
||||
* last records.
|
||||
*/
|
||||
export function backgroundTasksOf(
|
||||
text: string,
|
||||
options: { complete: boolean; after?: BackgroundTasks },
|
||||
): BackgroundTasks {
|
||||
const tracker = new TaskTracker(options.after);
|
||||
for (const line of text.split("\n")) tracker.add(line);
|
||||
return tracker.result({ complete: options.complete });
|
||||
}
|
||||
|
||||
/** The tasks of the first `size` bytes of `filePath`, from all its records.
|
||||
* Reads the whole file, so only for a session that a live process holds. */
|
||||
export async function searchBackgroundTasks(
|
||||
filePath: string,
|
||||
size: number,
|
||||
): Promise<BackgroundTasks> {
|
||||
const tracker = new TaskTracker();
|
||||
if (size === 0) return tracker.result({ complete: true });
|
||||
const stream = createReadStream(filePath, { start: 0, end: size - 1, encoding: "utf8" });
|
||||
const lines = createInterface({ input: stream, crlfDelay: Infinity });
|
||||
for await (const line of lines) tracker.add(line);
|
||||
return tracker.result({ complete: true });
|
||||
}
|
||||
@@ -15,8 +15,7 @@ import {
|
||||
type InitializeRequest,
|
||||
type ListSessionsRequest,
|
||||
type ListSessionsResponse,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
import { deleteSession as sdkDeleteSession } from "@anthropic-ai/claude-agent-sdk";
|
||||
} from "@agentclientprotocol/sdk/unstable";
|
||||
import type { Query } from "@anthropic-ai/claude-agent-sdk";
|
||||
import type { AcpClient, Logger, Session } from "../acp-agent.js";
|
||||
import {
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
AIR_SESSION_INDEX_CAPABILITY,
|
||||
AIR_SESSION_LIST_SUBSCRIBE_CAPABILITY,
|
||||
AIR_SESSION_RENAME_CAPABILITY,
|
||||
AIR_SESSION_WORKTREES_CAPABILITY,
|
||||
clientSupportsAirCapability,
|
||||
withAirMeta,
|
||||
} from "../air-extension.js";
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
} from "./list-subscriptions.js";
|
||||
import {
|
||||
archiveInsteadOfDelete,
|
||||
deleteSessionIfStored,
|
||||
archiveTo,
|
||||
renameTo,
|
||||
parseRenameSessionRequest,
|
||||
@@ -143,8 +144,8 @@ export class SessionIndexConnection {
|
||||
}
|
||||
|
||||
/** The AIR capabilities the agent advertises for the index: the index
|
||||
* itself, archive, rename and the list subscription, all only to a client
|
||||
* that declared `sessionIndex`. */
|
||||
* itself, archive, rename, the list subscription and the `agentWorktree`
|
||||
* row field, all only to a client that declared `sessionIndex`. */
|
||||
capabilities(): string[] {
|
||||
return this.service
|
||||
? [
|
||||
@@ -152,6 +153,7 @@ export class SessionIndexConnection {
|
||||
AIR_SESSION_ARCHIVE_CAPABILITY,
|
||||
AIR_SESSION_RENAME_CAPABILITY,
|
||||
AIR_SESSION_LIST_SUBSCRIBE_CAPABILITY,
|
||||
AIR_SESSION_WORKTREES_CAPABILITY,
|
||||
]
|
||||
: [];
|
||||
}
|
||||
@@ -187,6 +189,31 @@ export class SessionIndexConnection {
|
||||
this.service?.ownSessionChanged(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs `fork`, the handler of a `session/fork` request of `sourceId`. The
|
||||
* session list holds back the forks of the source until the response is
|
||||
* out. The client then knows a fork from the response before a list shows
|
||||
* it.
|
||||
*/
|
||||
async forking<T>(sourceId: string, fork: () => Promise<T>): Promise<T> {
|
||||
// The forks of this connection: the request handler reads the service
|
||||
// once, so a later `initialize` does not split the start and the answer.
|
||||
const service = this.service;
|
||||
service?.forkStarted(sourceId);
|
||||
try {
|
||||
return await fork();
|
||||
} finally {
|
||||
// The SDK writes the response in the microtasks after the handler
|
||||
// resolves, so a `setTimeout(0)` callback runs after it.
|
||||
setTimeout(() => service?.forkAnswered(sourceId), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** The `session/fork` of `sourceId` copied the transcript into `forkId`. */
|
||||
onForkCreated(sourceId: string, forkId: string): void {
|
||||
this.service?.forkCreated(sourceId, forkId);
|
||||
}
|
||||
|
||||
/** `_session/list/subscribe`. */
|
||||
subscribeList(params: ListSubscribeRequest): Promise<ListSubscribeResponse> {
|
||||
return this.requireService(LIST_SUBSCRIBE_METHOD).subscribeList(params.cwd);
|
||||
@@ -336,7 +363,8 @@ export class SessionIndexConnection {
|
||||
* - Another AIR client uses delete to mark a session done, and may reopen
|
||||
* it later: the adapter archives it instead (as `_session/archive` does,
|
||||
* after the CLI closed), so the transcript survives.
|
||||
* - Every other client: the SDK delete, as before.
|
||||
* - Every other client: the SDK delete. A session without a transcript
|
||||
* succeeds (see {@link deleteSessionIfStored}).
|
||||
*/
|
||||
async deleteSession(params: DeleteSessionRequest): Promise<DeleteSessionResponse> {
|
||||
const loaded = this.host.agent.sessions[params.sessionId] !== undefined;
|
||||
@@ -350,7 +378,7 @@ export class SessionIndexConnection {
|
||||
} else if (this.host.isAirClient()) {
|
||||
await archiveInsteadOfDelete(params.sessionId, this.airArchiver());
|
||||
} else {
|
||||
await sdkDeleteSession(params.sessionId);
|
||||
await deleteSessionIfStored(params.sessionId);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* The first prompt of a transcript, extracted the way the SDK does for a
|
||||
* session title (its `rf`, `ti` and `jP` in `@anthropic-ai/claude-agent-sdk`).
|
||||
* Used when the SDK `getSessionInfo` read another copy of the session.
|
||||
* Part of the SDK's title rule that the session index applies itself (see
|
||||
* `sdkTitles`).
|
||||
*
|
||||
* - Only main-chain user records count: no tool result, meta or compact
|
||||
* summary record.
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
* - A registry event names the record: only that record is read, and only
|
||||
* the sessions whose record changed get their `state` recomputed.
|
||||
* - A session this connection runs reports its SDK state at once.
|
||||
* - A session whose transcripts this connection wrote (a rename, archive,
|
||||
* unarchive or delete) is stat'ed and read again at once, without waiting
|
||||
* for its transcript event.
|
||||
*
|
||||
* Events are coalesced: 150 ms after the last one, at most 1 s after the
|
||||
* first. Each subscription compares a recomputed row with the last row it
|
||||
@@ -37,7 +40,7 @@ import { randomUUID } from "node:crypto";
|
||||
import * as fs from "node:fs";
|
||||
import * as fsp from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import { RequestError, type SessionInfo } from "@agentclientprotocol/sdk";
|
||||
import { RequestError, type SessionInfo } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { OwnSessionState } from "./activity.js";
|
||||
import {
|
||||
bySession,
|
||||
@@ -108,6 +111,8 @@ export type ListSubscriptionDeps = {
|
||||
/** What this connection knows of a session it runs. */
|
||||
own: (sessionId: string) => OwnSessionState | undefined;
|
||||
notify: (changes: ListChanges) => Promise<void>;
|
||||
/** The forks whose rows wait for their fork response. */
|
||||
forkHolds?: ForkHolds;
|
||||
logError: (message: string, error: unknown) => void;
|
||||
now?: () => number;
|
||||
debounceMs?: number;
|
||||
@@ -239,6 +244,54 @@ function transcriptId(filename: string): string | undefined {
|
||||
return isSessionId(id) ? id : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `session/fork` requests in flight. A client must get the id of a fork
|
||||
* from the fork response before a list shows the fork. Otherwise the client
|
||||
* sees an unknown session and lists it as a session of its own.
|
||||
*/
|
||||
export class ForkHolds {
|
||||
/** Source session id → the number of its forks in flight, and the forks
|
||||
* they copied the transcript into. */
|
||||
private readonly sources = new Map<string, { count: number; forks: Set<string> }>();
|
||||
|
||||
started(sourceId: string): void {
|
||||
const source = this.sources.get(sourceId);
|
||||
if (source) source.count++;
|
||||
else this.sources.set(sourceId, { count: 1, forks: new Set() });
|
||||
}
|
||||
|
||||
/** A fork of `sourceId` copied the transcript into `forkId`. */
|
||||
created(sourceId: string, forkId: string): void {
|
||||
this.sources.get(sourceId)?.forks.add(forkId);
|
||||
}
|
||||
|
||||
/** The response of a fork of `sourceId` is out. Returns whether that
|
||||
* freed rows: the last fork of the source answered. */
|
||||
answered(sourceId: string): boolean {
|
||||
const source = this.sources.get(sourceId);
|
||||
if (!source) return false;
|
||||
if (--source.count > 0) return false;
|
||||
this.sources.delete(sourceId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Whether `id` is a fork whose fork response is not out yet. */
|
||||
isHeld(id: string): boolean {
|
||||
for (const { forks } of this.sources.values()) if (forks.has(id)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the row of `id` waits. A fork that a fork in flight created
|
||||
* waits. Before the copy returns its id, an unsent row of a fork of a
|
||||
* source in flight waits too, by its `forkedFrom`.
|
||||
*/
|
||||
holds(id: string, sent: boolean, forkedFrom: string | undefined): boolean {
|
||||
if (this.isHeld(id)) return true;
|
||||
return !sent && forkedFrom !== undefined && this.sources.has(forkedFrom);
|
||||
}
|
||||
}
|
||||
|
||||
export class ListSubscriptions {
|
||||
private readonly subscriptions = new Map<string, Subscription>();
|
||||
/** Sessions whose row shows this connection's SDK state: the rescan
|
||||
@@ -345,6 +398,34 @@ export class ListSubscriptions {
|
||||
}
|
||||
}
|
||||
|
||||
/** Sends the pending sessions now: {@link ForkHolds} held some back. */
|
||||
deliverPending(): void {
|
||||
for (const subscription of this.subscriptions.values()) {
|
||||
if (subscription.pending.size > 0) this.deliver(subscription);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This connection wrote the transcripts of a session: a rename, archive,
|
||||
* unarchive or delete. Its transcripts are stat'ed and its row read again
|
||||
* at once, as a transcript event would after its quiet period; the event
|
||||
* that follows finds the row unchanged and sends nothing more. A copy the
|
||||
* watch does not know yet is left to its event.
|
||||
*/
|
||||
sessionWritten(id: string): void {
|
||||
for (const watch of this.watches.values()) {
|
||||
if (!watch.ready) {
|
||||
// Read when the watch is ready, as the events of its start.
|
||||
watch.dirtyRows.add(id);
|
||||
continue;
|
||||
}
|
||||
const files = watch.filesById.get(id);
|
||||
if (!files) continue;
|
||||
for (const filePath of files) watch.dirtyFiles.add(filePath);
|
||||
this.schedule(watch, "now");
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposed = true;
|
||||
for (const id of [...this.subscriptions.keys()]) this.unsubscribe(id);
|
||||
@@ -1175,7 +1256,10 @@ export class ListSubscriptions {
|
||||
if (!watch.rows.has(id)) candidates.push(...(priorFiles.get(id) ?? []));
|
||||
}
|
||||
if (candidates.length === 0) return new Set();
|
||||
const { rows, unknown } = await this.deps.index.cachedRowsOf(paths, candidates);
|
||||
// Only the ids count: no worktree check.
|
||||
const { rows, unknown } = await this.deps.index.cachedRowsOf(paths, candidates, {
|
||||
mark: false,
|
||||
});
|
||||
for (const row of rows) unknown.add(row.sessionId);
|
||||
return unknown;
|
||||
}
|
||||
@@ -1200,6 +1284,12 @@ export class ListSubscriptions {
|
||||
subscription.pending.delete(id);
|
||||
continue;
|
||||
}
|
||||
// A fork whose fork response is not out yet stays pending.
|
||||
if (
|
||||
this.deps.forkHolds?.holds(id, subscription.sent.has(id), current?.row.facts.forkedFrom)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const dueAt = (subscription.sentAt.get(id) ?? -Infinity) + this.minIntervalMs;
|
||||
if (dueAt > now) {
|
||||
nextAt = Math.min(nextAt, dueAt);
|
||||
|
||||
@@ -15,11 +15,8 @@ import {
|
||||
RequestError,
|
||||
type ListSessionsRequest,
|
||||
type ListSessionsResponse,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
import {
|
||||
deleteSession as sdkDeleteSession,
|
||||
getSessionInfo as sdkGetSessionInfo,
|
||||
} from "@anthropic-ai/claude-agent-sdk";
|
||||
} from "@agentclientprotocol/sdk/unstable";
|
||||
import { deleteSession as sdkDeleteSession } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { airExtensionMeta } from "../air-extension.js";
|
||||
import { sanitizeTitle } from "../session-titles.js";
|
||||
import type { OwnSessionState } from "./activity.js";
|
||||
@@ -31,9 +28,11 @@ import {
|
||||
visibleTitle,
|
||||
} from "./archive-title.js";
|
||||
import {
|
||||
ForkHolds,
|
||||
ListSubscriptions,
|
||||
type ListChanges,
|
||||
type ListSubscribeResponse,
|
||||
type ListSubscriptionDeps,
|
||||
} from "./list-subscriptions.js";
|
||||
import { LiveSessionRegistry } from "./live-registry.js";
|
||||
import {
|
||||
@@ -59,7 +58,6 @@ import {
|
||||
readSidecarTitle,
|
||||
SessionIndex,
|
||||
type ArchivedFilter,
|
||||
type GetSessionInfo,
|
||||
type ListCursor,
|
||||
} from "./session-index.js";
|
||||
import { sessionInfoOf } from "./session-info.js";
|
||||
@@ -294,7 +292,7 @@ async function copyTitle(filePath: string, sessionId: string): Promise<string |
|
||||
const { size } = await fs.stat(filePath);
|
||||
const headTail = await readHeadTail(filePath, size);
|
||||
const sidecar =
|
||||
tailCustomTitle(headTail.tail) === undefined
|
||||
tailCustomTitle(headTail) === undefined
|
||||
? await readSidecarTitle(filePath, sessionId)
|
||||
: undefined;
|
||||
const { title, archived } = effectiveTitle(
|
||||
@@ -386,6 +384,28 @@ function sdkDeleteError(sessionId: string): Error {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* `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,
|
||||
* as ACP asks ("Deleting an already-deleted session, or a session that never
|
||||
* existed, SHOULD succeed silently"): an id that is not a session id, a
|
||||
* session deleted before, or a session this connection ran but never prompted,
|
||||
* since Claude Code writes the transcript at the first prompt. Other failures
|
||||
* still fail the request.
|
||||
*/
|
||||
export async function deleteSessionIfStored(
|
||||
sessionId: string,
|
||||
deleteSession: (sessionId: string) => Promise<void> = sdkDeleteSession,
|
||||
): Promise<void> {
|
||||
if (!isSessionId(sessionId)) return;
|
||||
try {
|
||||
await deleteSession(sessionId);
|
||||
} catch (error) {
|
||||
if (isSdkNotFound(error)) return;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `session/delete` of an AIR client without `sessionIndex`, which uses delete
|
||||
* to mark a session done: archives it instead, so the transcript survives. A
|
||||
@@ -426,7 +446,6 @@ export type RetitleOptions = {
|
||||
};
|
||||
|
||||
export type SessionIndexDeps = {
|
||||
getSessionInfo?: GetSessionInfo;
|
||||
deleteSession?: (sessionId: string) => Promise<void>;
|
||||
registry?: LiveSessionRegistry;
|
||||
now?: () => number;
|
||||
@@ -434,6 +453,11 @@ export type SessionIndexDeps = {
|
||||
ownSessionState?: (sessionId: string) => OwnSessionState | undefined;
|
||||
/** Sends `_session/list/changes`. */
|
||||
notifyListChanges?: (changes: ListChanges) => Promise<void>;
|
||||
/** The timing of the list subscriptions, for tests. */
|
||||
listSubscriptionTiming?: Pick<
|
||||
ListSubscriptionDeps,
|
||||
"debounceMs" | "maxWaitMs" | "rescanMs" | "minSessionIntervalMs"
|
||||
>;
|
||||
logError: (message: string, error: unknown) => void;
|
||||
};
|
||||
|
||||
@@ -441,6 +465,9 @@ export class SessionIndexService {
|
||||
readonly index: SessionIndex;
|
||||
readonly registry: LiveSessionRegistry;
|
||||
private subscriptions?: ListSubscriptions;
|
||||
/** Here and not in the subscriptions, which start with the first subscribe:
|
||||
* a subscribe during a fork holds that fork back too. */
|
||||
private readonly forkHolds = new ForkHolds();
|
||||
/** Set for good by {@link dispose}: no subscription is made after it. */
|
||||
private disposed = false;
|
||||
/** The mutation in flight per session, which the next one waits for. */
|
||||
@@ -449,12 +476,18 @@ export class SessionIndexService {
|
||||
private readonly deleteSession: (sessionId: string) => Promise<void>;
|
||||
|
||||
constructor(private readonly deps: SessionIndexDeps) {
|
||||
this.index = new SessionIndex(deps.getSessionInfo ?? sdkGetSessionInfo);
|
||||
this.registry = deps.registry ?? new LiveSessionRegistry();
|
||||
this.index = new SessionIndex({ isLive: (sessionId) => this.isLive(sessionId) });
|
||||
this.now = deps.now ?? Date.now;
|
||||
this.deleteSession = deps.deleteSession ?? ((id) => sdkDeleteSession(id));
|
||||
}
|
||||
|
||||
/** Whether a live process holds the session, by the live registry. */
|
||||
private async isLive(sessionId: string): Promise<boolean> {
|
||||
const live = await this.registry.snapshot();
|
||||
return live.has(sessionId);
|
||||
}
|
||||
|
||||
/** `session/list` of a `sessionIndex` client. `own` reports the sessions
|
||||
* that this connection runs. */
|
||||
async list(
|
||||
@@ -479,9 +512,10 @@ export class SessionIndexService {
|
||||
});
|
||||
const live = await livePromise;
|
||||
const now = this.now();
|
||||
const sessions = rows.map((row) =>
|
||||
sessionInfoOf(row, own(row.sessionId), live.get(row.sessionId), now),
|
||||
);
|
||||
const sessions = rows
|
||||
// A fork whose fork response is not out yet is left out, as in the list subscriptions.
|
||||
.filter((row) => !this.forkHolds.isHeld(row.sessionId))
|
||||
.map((row) => sessionInfoOf(row, own(row.sessionId), live.get(row.sessionId), now));
|
||||
const last = rows[rows.length - 1];
|
||||
return hasMore && last ? { sessions, nextCursor: encodeListCursor(last, scope) } : { sessions };
|
||||
}
|
||||
@@ -588,6 +622,9 @@ export class SessionIndexService {
|
||||
stored = await this.titleCopies(sessionId, transcripts, change, options.sidecar);
|
||||
}
|
||||
return stored;
|
||||
}).finally(() => {
|
||||
// Whatever was written, also by a change that failed part way.
|
||||
this.subscriptions?.sessionWritten(sessionId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -666,6 +703,7 @@ export class SessionIndexService {
|
||||
}
|
||||
} finally {
|
||||
this.index.invalidate(all);
|
||||
this.subscriptions?.sessionWritten(sessionId);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -681,7 +719,9 @@ export class SessionIndexService {
|
||||
registry: this.registry,
|
||||
own: (sessionId) => this.deps.ownSessionState?.(sessionId),
|
||||
notify: (changes) => this.deps.notifyListChanges?.(changes) ?? Promise.resolve(),
|
||||
forkHolds: this.forkHolds,
|
||||
logError: this.deps.logError,
|
||||
...this.deps.listSubscriptionTiming,
|
||||
});
|
||||
return this.subscriptions.subscribe(cwd);
|
||||
}
|
||||
@@ -696,6 +736,22 @@ export class SessionIndexService {
|
||||
this.subscriptions?.ownSessionChanged(sessionId);
|
||||
}
|
||||
|
||||
/** A `session/fork` of `sourceId` started (see {@link ForkHolds}). */
|
||||
forkStarted(sourceId: string): void {
|
||||
this.forkHolds.started(sourceId);
|
||||
}
|
||||
|
||||
/** The `session/fork` of `sourceId` copied the transcript into `forkId`. */
|
||||
forkCreated(sourceId: string, forkId: string): void {
|
||||
this.forkHolds.created(sourceId, forkId);
|
||||
}
|
||||
|
||||
/** The response of a `session/fork` of `sourceId` is out: the list
|
||||
* subscriptions send the rows they held back. */
|
||||
forkAnswered(sourceId: string): void {
|
||||
if (this.forkHolds.answered(sourceId)) this.subscriptions?.deliverPending();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposed = true;
|
||||
this.subscriptions?.dispose();
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/**
|
||||
* The session list of a `sessionIndex` client.
|
||||
*
|
||||
* Each list enumerates the transcripts of the requested cwd and of every
|
||||
* A list enumerates the transcripts of the requested cwd and of every
|
||||
* worktree of its repository: one `readdir` per project directory and one
|
||||
* `stat` per transcript, no long-lived cache. The metadata of a transcript is
|
||||
* cached by `(path, mtime, size)` in an LRU and read on a miss with the SDK
|
||||
* `getSessionInfo` (titles, sidecar) plus one head and tail read for
|
||||
* what the SDK does not report (see {@link scanTranscript}). Misses are read in
|
||||
* parallel batches.
|
||||
* `stat` per transcript. The pages after a cursor reuse that enumeration for
|
||||
* a short while (see {@link SessionIndex.reusableListing}). The metadata of a transcript is
|
||||
* cached by `(path, mtime, size)` in an LRU and read on a miss from one head
|
||||
* and tail read (the titles by the SDK's rule, see {@link sdkTitles}; the
|
||||
* facts, see {@link scanTranscriptFile}) and the title sidecar. Misses are
|
||||
* read in parallel batches.
|
||||
*
|
||||
* Order is the last user activity descending, then session id: the time of
|
||||
* the last real user prompt (`lastPromptAt`), else `updatedAt`. `updatedAt`
|
||||
@@ -20,7 +21,6 @@
|
||||
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import type { SDKSessionInfo } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { sanitizeTitle } from "../session-titles.js";
|
||||
import { effectiveTitle, visibleTitle } from "./archive-title.js";
|
||||
import {
|
||||
@@ -52,8 +52,9 @@ import {
|
||||
type SdkTitles,
|
||||
type TranscriptFacts,
|
||||
} from "./transcript-scan.js";
|
||||
import { searchBackgroundTasks } from "./background-tasks.js";
|
||||
import { DirListings, statFiles } from "./dir-listing.js";
|
||||
import { worktreeCounterparts } from "./worktrees.js";
|
||||
import { isInLinkedWorktree, worktreeCounterparts } from "./worktrees.js";
|
||||
|
||||
export const DEFAULT_LIST_LIMIT = 50;
|
||||
export const MAX_LIST_LIMIT = 200;
|
||||
@@ -66,6 +67,10 @@ const READ_BATCH_SIZE = 16;
|
||||
const MAX_CWD_PROBES_PER_DIR = 64;
|
||||
/** How long a directory that gave no cwd is not probed again. */
|
||||
const NO_CWD_RETRY_MS = 60_000;
|
||||
/** How long the pages after a list reuse its enumeration. */
|
||||
const LISTING_REUSE_MS = 30_000;
|
||||
/** Enumerations kept for the next page of their list. */
|
||||
const MAX_REUSABLE_LISTINGS = 8;
|
||||
|
||||
/** Which sessions a list holds by archive state: `unarchived` the
|
||||
* unarchived ones only, `archived` the archived ones only, `all` both in one
|
||||
@@ -121,6 +126,10 @@ export type IndexRow = {
|
||||
facts: TranscriptFacts;
|
||||
mtimeMs: number;
|
||||
archived: boolean;
|
||||
/** The session runs in a linked git worktree that Claude Code made for
|
||||
* it: its transcript says so (`facts.agentWorktree`) and `cwd` lies in
|
||||
* a linked worktree. See {@link markAgentWorktrees}. */
|
||||
agentWorktree?: true;
|
||||
};
|
||||
|
||||
/** A keyset position: the order key and the session id of the last row. */
|
||||
@@ -135,10 +144,60 @@ export type ListQuery = {
|
||||
after?: ListCursor;
|
||||
};
|
||||
|
||||
export type GetSessionInfo = (
|
||||
sessionId: string,
|
||||
options: { dir?: string },
|
||||
) => Promise<SDKSessionInfo | undefined>;
|
||||
/** The transcripts of a list scope as one enumeration found them. */
|
||||
type Listing = {
|
||||
/** The scope paths; undefined for every project. */
|
||||
paths: string[] | undefined;
|
||||
/** The directories read: the projects root, then each project directory. */
|
||||
dirs: string[];
|
||||
/** The names each of `dirs` had, as {@link DirListings} returned them. */
|
||||
names: string[][];
|
||||
/** Newest mtime first. */
|
||||
candidates: TranscriptCandidate[];
|
||||
/** When it was enumerated. */
|
||||
at: number;
|
||||
};
|
||||
|
||||
/** The key of the page of `query` that follows `cursor`. */
|
||||
function pageKey(query: ListQuery, cursor: ListCursor): string {
|
||||
return JSON.stringify([
|
||||
query.cwd ?? null,
|
||||
query.includeWorktrees ?? false,
|
||||
cursor.orderAtMs,
|
||||
cursor.sessionId,
|
||||
]);
|
||||
}
|
||||
|
||||
/** The candidates of each session. */
|
||||
function copiesBySession(
|
||||
candidates: readonly TranscriptCandidate[],
|
||||
): Map<string, TranscriptCandidate[]> {
|
||||
const copies = new Map<string, TranscriptCandidate[]>();
|
||||
for (const candidate of candidates) {
|
||||
const known = copies.get(candidate.sessionId);
|
||||
if (known) known.push(candidate);
|
||||
else copies.set(candidate.sessionId, [candidate]);
|
||||
}
|
||||
return copies;
|
||||
}
|
||||
|
||||
/** `candidates` as they are on disk now; one that is gone or empty is left
|
||||
* out. */
|
||||
async function restat(candidates: readonly TranscriptCandidate[]): Promise<TranscriptCandidate[]> {
|
||||
const stats = await statFiles(candidates.map(({ filePath }) => filePath));
|
||||
return candidates.flatMap((candidate, i) => {
|
||||
const now = stats[i];
|
||||
if (!now?.isFile() || now.size === 0) return [];
|
||||
if (
|
||||
now.mtimeMs === candidate.mtimeMs &&
|
||||
now.size === candidate.size &&
|
||||
now.ino === candidate.ino
|
||||
) {
|
||||
return [candidate];
|
||||
}
|
||||
return [{ ...candidate, mtimeMs: now.mtimeMs, size: now.size, ino: now.ino }];
|
||||
});
|
||||
}
|
||||
|
||||
type CacheEntry = {
|
||||
mtimeMs: number;
|
||||
@@ -195,6 +254,22 @@ function toRow({ candidate, metadata }: Resolved, cwd: string): IndexRow {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets `agentWorktree` on the rows whose transcript says Claude Code made
|
||||
* their worktree and whose cwd lies in a linked worktree now. Only those rows
|
||||
* are checked; the check is not cached, so a removed worktree shows at once.
|
||||
*/
|
||||
async function markAgentWorktrees(rows: IndexRow[]): Promise<IndexRow[]> {
|
||||
await Promise.all(
|
||||
rows.map(async (row) => {
|
||||
if (row.facts.agentWorktree === true && (await isInLinkedWorktree(row.cwd))) {
|
||||
row.agentWorktree = true;
|
||||
}
|
||||
}),
|
||||
);
|
||||
return rows;
|
||||
}
|
||||
|
||||
function isAfter(row: { orderAtMs: number; sessionId: string }, cursor: ListCursor): boolean {
|
||||
return compareRows(row, cursor) > 0;
|
||||
}
|
||||
@@ -326,7 +401,15 @@ function scopeOf(
|
||||
};
|
||||
}
|
||||
|
||||
export type SessionIndexOptions = {
|
||||
/** Whether a live process holds the session. Only for such a session the
|
||||
* whole transcript is searched for its background tasks. */
|
||||
isLive?: (sessionId: string) => Promise<boolean>;
|
||||
};
|
||||
|
||||
export class SessionIndex {
|
||||
constructor(private readonly options: SessionIndexOptions = {}) {}
|
||||
|
||||
private readonly listings = new DirListings();
|
||||
private readonly metadata = new Lru<string, CacheEntry>(METADATA_CACHE_SIZE);
|
||||
/** The metadata reads in flight, by path: a list and a subscription that
|
||||
@@ -346,8 +429,16 @@ export class SessionIndex {
|
||||
/** Directories whose transcripts gave no cwd, by the directory mtime and
|
||||
* when that was learned. */
|
||||
private readonly dirsWithoutCwd = new Map<string, { mtimeMs: number; at: number }>();
|
||||
/** The enumeration of each list with a next page, by {@link pageKey}. */
|
||||
private readonly nextPages = new Lru<string, Listing>(MAX_REUSABLE_LISTINGS);
|
||||
|
||||
constructor(private readonly getSessionInfo: GetSessionInfo) {}
|
||||
/** Transcripts read so far (metadata cache misses). */
|
||||
private reads = 0;
|
||||
|
||||
/** How many transcripts the index has read, for tests and diagnostics. */
|
||||
get transcriptsRead(): number {
|
||||
return this.reads;
|
||||
}
|
||||
|
||||
/** The paths whose sessions a list of `cwd` shows: `cwd`, and with
|
||||
* `includeWorktrees` the same subdirectory of every other existing
|
||||
@@ -418,14 +509,21 @@ export class SessionIndex {
|
||||
* every copy of a session: a list shows the newest one that has a row
|
||||
* (see {@link collectRows}), as the SDK does. */
|
||||
async enumerateFiles(paths?: readonly string[]): Promise<TranscriptCandidate[]> {
|
||||
return (await this.enumerate(paths)).candidates;
|
||||
}
|
||||
|
||||
/** {@link enumerateFiles}, with the directory listings it was made from. */
|
||||
private async enumerate(
|
||||
paths?: readonly string[],
|
||||
): Promise<{ dirs: string[]; names: string[][]; candidates: TranscriptCandidate[] }> {
|
||||
const root = projectsRoot();
|
||||
const rootNames = await this.listings.names(root);
|
||||
const dirs: { dirName: string; projectPath?: string }[] = paths
|
||||
? await this.projectDirs(paths)
|
||||
: (await this.listings.names(root)).map((dirName) => ({ dirName }));
|
||||
: rootNames.map((dirName) => ({ dirName }));
|
||||
// All directories' names first, then one bounded run of stats.
|
||||
const listed = await this.listings.namesOfAll(
|
||||
dirs.map(({ dirName }) => path.join(root, dirName)),
|
||||
);
|
||||
const dirPaths = dirs.map(({ dirName }) => path.join(root, dirName));
|
||||
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 }),
|
||||
@@ -446,7 +544,7 @@ export class SessionIndex {
|
||||
ino: fileStats.ino,
|
||||
});
|
||||
});
|
||||
return all;
|
||||
return { dirs: [root, ...dirPaths], names: [rootNames, ...listed], candidates: all };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,11 +622,10 @@ export class SessionIndex {
|
||||
|
||||
/** One page of rows, plus whether more rows follow the page. */
|
||||
async list(query: ListQuery): Promise<{ rows: IndexRow[]; hasMore: boolean }> {
|
||||
const paths = query.cwd
|
||||
? await this.listedPaths(query.cwd, query.includeWorktrees ?? false)
|
||||
: undefined;
|
||||
const after = query.after;
|
||||
const enumerated = (await this.enumerateFiles(paths)).sort(byMtimeDescending);
|
||||
const reused = after ? await this.reusableListing(query, after) : undefined;
|
||||
const listing = reused ?? (await this.freshListing(query));
|
||||
const { paths, candidates: enumerated } = listing;
|
||||
// A page after a cursor skips, without reading them, the sessions whose
|
||||
// newest transcript has a cached order key before the cursor. The
|
||||
// archive state comes from the title, so the archive filter applies once
|
||||
@@ -548,9 +645,47 @@ export class SessionIndex {
|
||||
archived: query.archived,
|
||||
after,
|
||||
wanted: query.limit + 1,
|
||||
unread: (read) => enumerated.filter((candidate) => !read.has(candidate)),
|
||||
unread: (read) => enumerated.filter((candidate) => !read.has(candidate.filePath)),
|
||||
restat: reused !== undefined,
|
||||
});
|
||||
return { rows: rows.slice(0, query.limit), hasMore: rows.length > query.limit };
|
||||
const page = rows.slice(0, query.limit);
|
||||
const hasMore = rows.length > query.limit;
|
||||
const last = page.at(-1);
|
||||
if (hasMore && last) this.nextPages.set(pageKey(query, last), listing);
|
||||
return { rows: page, hasMore };
|
||||
}
|
||||
|
||||
/** Enumerates the scope of `query`, newest mtime first. */
|
||||
private async freshListing(query: ListQuery): Promise<Listing> {
|
||||
const at = Date.now();
|
||||
const paths = query.cwd
|
||||
? await this.listedPaths(query.cwd, query.includeWorktrees ?? false)
|
||||
: undefined;
|
||||
const { dirs, names, candidates } = await this.enumerate(paths);
|
||||
return { paths, dirs, names, candidates: candidates.sort(byMtimeDescending), at };
|
||||
}
|
||||
|
||||
/**
|
||||
* The enumeration of the earlier page of `query` that ended at `cursor`,
|
||||
* while it is recent and no directory of it has changed its entries: a
|
||||
* transcript was neither added nor removed (see {@link DirListings}, which
|
||||
* returns the same names until then). Which transcripts the page reads is
|
||||
* stat'ed again (see {@link collectRows}), so a page made from it is as
|
||||
* fresh as one from a new enumeration: a transcript that grew since moves
|
||||
* up, out of the pages after the cursor, or keeps its place. A page of
|
||||
* thousands of transcripts then stats some hundreds, not all of them.
|
||||
*/
|
||||
private async reusableListing(
|
||||
query: ListQuery,
|
||||
cursor: ListCursor,
|
||||
): Promise<Listing | undefined> {
|
||||
const key = pageKey(query, cursor);
|
||||
const listing = this.nextPages.peek(key);
|
||||
if (!listing) return undefined;
|
||||
this.nextPages.delete(key);
|
||||
if (Date.now() - listing.at > LISTING_REUSE_MS) return undefined;
|
||||
const names = await this.listings.namesOfAll(listing.dirs);
|
||||
return names.every((current, i) => current === listing.names[i]) ? listing : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,7 +706,7 @@ export class SessionIndex {
|
||||
inScope: scopeOf(paths),
|
||||
archived: "all",
|
||||
wanted: Infinity,
|
||||
unread: (read) => siblings().filter((candidate) => !read.has(candidate)),
|
||||
unread: (read) => siblings().filter((candidate) => !read.has(candidate.filePath)),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -580,11 +715,14 @@ export class SessionIndex {
|
||||
* alone gives, read from it at once, and the ids of the sessions it gives
|
||||
* no answer for: a transcript of it has nothing cached at its stat, no cwd
|
||||
* known, or is continued in another session (whether the successor hides
|
||||
* it depends on the successor now). Reads no transcript.
|
||||
* it depends on the successor now). Reads no transcript. The rows carry
|
||||
* `agentWorktree` ({@link markAgentWorktrees}, which reads git files)
|
||||
* unless `mark` is false, for a caller that uses only their ids.
|
||||
*/
|
||||
async cachedRowsOf(
|
||||
paths: readonly string[],
|
||||
candidates: readonly TranscriptCandidate[],
|
||||
{ mark = true }: { mark?: boolean } = {},
|
||||
): Promise<{ rows: IndexRow[]; unknown: Set<string> }> {
|
||||
const unknown = new Set<string>();
|
||||
const resolved: { item: Resolved; cwd: string }[] = [];
|
||||
@@ -615,7 +753,8 @@ export class SessionIndex {
|
||||
if (previous && previous.mtimeMs >= item.candidate.mtimeMs) continue;
|
||||
newest.set(id, { row: toRow(item, cwd), mtimeMs: item.candidate.mtimeMs });
|
||||
}
|
||||
return { rows: [...newest.values()].map(({ row }) => row), unknown };
|
||||
const rows = [...newest.values()].map(({ row }) => row);
|
||||
return { rows: mark ? await markAgentWorktrees(rows) : rows, unknown };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -624,6 +763,12 @@ export class SessionIndex {
|
||||
* row, as the SDK list does. With a finite `wanted`, stops once `wanted`
|
||||
* rows are certain to come first (a candidate's order key is at most its
|
||||
* mtime, so `candidates` must be in mtime order then).
|
||||
*
|
||||
* With `restat`, `candidates` are an earlier enumeration: each is stat'ed
|
||||
* again just before it is read, together with the other copies of its
|
||||
* session, any of which may be its newest one now. Their mtimes still
|
||||
* bound the order keys: a transcript that changed since either kept its
|
||||
* order key (no new prompt) or got one newer than any listed before.
|
||||
*/
|
||||
private async collectRows(
|
||||
candidates: readonly TranscriptCandidate[],
|
||||
@@ -633,8 +778,10 @@ export class SessionIndex {
|
||||
archived: ArchivedFilter;
|
||||
after?: ListCursor;
|
||||
wanted: number;
|
||||
/** The transcripts not read, which may supply a sibling's cwd. */
|
||||
unread: (read: ReadonlySet<TranscriptCandidate>) => readonly TranscriptCandidate[];
|
||||
/** The transcripts not read (by path), which may supply a sibling's
|
||||
* cwd. */
|
||||
unread: (read: ReadonlySet<string>) => readonly TranscriptCandidate[];
|
||||
restat?: boolean;
|
||||
},
|
||||
): Promise<IndexRow[]> {
|
||||
const { inScope, archived, after, wanted } = options;
|
||||
@@ -668,7 +815,9 @@ export class SessionIndex {
|
||||
}
|
||||
pending = left;
|
||||
};
|
||||
const read = new Set<TranscriptCandidate>();
|
||||
const read = new Set<string>();
|
||||
/** With `restat`: the copies of each session. */
|
||||
const copies = options.restat ? copiesBySession(candidates) : undefined;
|
||||
let index = 0;
|
||||
while (index < candidates.length) {
|
||||
if (bySession.size >= wanted) {
|
||||
@@ -678,11 +827,18 @@ export class SessionIndex {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const batch = candidates.slice(index, index + READ_BATCH_SIZE);
|
||||
index += batch.length;
|
||||
const next = candidates.slice(index, index + READ_BATCH_SIZE);
|
||||
index += next.length;
|
||||
const batch = copies
|
||||
? await restat(
|
||||
[...new Set(next.flatMap(({ sessionId }) => copies.get(sessionId)!))].filter(
|
||||
({ filePath }) => !read.has(filePath),
|
||||
),
|
||||
)
|
||||
: next;
|
||||
const resolved = await Promise.all(
|
||||
batch.map(async (candidate) => {
|
||||
read.add(candidate);
|
||||
read.add(candidate.filePath);
|
||||
const metadata = await this.metadataOf(candidate);
|
||||
if (!metadata || (await this.continuedElsewhere(candidate, metadata))) return undefined;
|
||||
return { candidate, metadata };
|
||||
@@ -704,10 +860,11 @@ export class SessionIndex {
|
||||
await this.learnDirCwds(
|
||||
new Set(pending.map(({ candidate }) => candidate.dirName)),
|
||||
options.unread(read),
|
||||
options.restat ?? false,
|
||||
);
|
||||
await settlePending();
|
||||
}
|
||||
return rows();
|
||||
return markAgentWorktrees(rows());
|
||||
}
|
||||
|
||||
/** Whether the cached metadata of `candidate`, still current, places it
|
||||
@@ -733,10 +890,11 @@ export class SessionIndex {
|
||||
/** Reads the unread transcripts of `dirNames` until each directory has a
|
||||
* known cwd, at most {@link MAX_CWD_PROBES_PER_DIR} per directory. A
|
||||
* directory that gives none is not read again for a while, unless it
|
||||
* changes. */
|
||||
* changes. `restat` as in {@link collectRows}. */
|
||||
private async learnDirCwds(
|
||||
dirNames: ReadonlySet<string>,
|
||||
unread: readonly TranscriptCandidate[],
|
||||
restatFirst: boolean,
|
||||
): Promise<void> {
|
||||
const root = projectsRoot();
|
||||
const now = Date.now();
|
||||
@@ -761,8 +919,9 @@ export class SessionIndex {
|
||||
});
|
||||
while (remaining.length > 0) {
|
||||
remaining = remaining.filter((candidate) => !this.dirCwds.has(candidate.dirName));
|
||||
const batch = remaining.slice(0, READ_BATCH_SIZE);
|
||||
remaining = remaining.slice(batch.length);
|
||||
const next = remaining.slice(0, READ_BATCH_SIZE);
|
||||
remaining = remaining.slice(next.length);
|
||||
const batch = restatFirst ? await restat(next) : next;
|
||||
await Promise.all(batch.map((candidate) => this.metadataOf(candidate)));
|
||||
}
|
||||
for (const [dirName, mtimeMs] of mtimes) {
|
||||
@@ -839,16 +998,18 @@ export class SessionIndex {
|
||||
candidate: TranscriptCandidate,
|
||||
previous?: PreviousScan,
|
||||
): Promise<TranscriptMetadata | null> {
|
||||
this.reads++;
|
||||
const headTail = await readHeadTail(candidate.filePath, candidate.size);
|
||||
if (isSidechainTranscript(headTail.head)) return null;
|
||||
const facts = await scanTranscriptFile(
|
||||
const scanned = await scanTranscriptFile(
|
||||
candidate.filePath,
|
||||
candidate.size,
|
||||
headTail,
|
||||
previous,
|
||||
candidate.ino,
|
||||
);
|
||||
if (!facts.hasMessages) return null;
|
||||
if (!scanned.hasMessages) return null;
|
||||
const facts = await this.withAllBackgroundTasks(candidate, scanned);
|
||||
// The last relocation names the session's cwd, as the SDK reads it; the
|
||||
// messages before it keep the old one.
|
||||
const fileCwd = await this.recoverCwd(candidate.dirName, [
|
||||
@@ -857,7 +1018,7 @@ export class SessionIndex {
|
||||
...(facts.tailCwd ? pathAndAncestors(facts.tailCwd) : []),
|
||||
]);
|
||||
if (fileCwd) this.dirCwds.set(candidate.dirName, fileCwd);
|
||||
const sdk = await this.titlesOf(candidate, headTail, fileCwd);
|
||||
const sdk = await this.titlesOf(candidate, headTail);
|
||||
// No title at all: the SDK does not list it either.
|
||||
if (!sdk.summary) return null;
|
||||
// Archived by its name alone: the agent name, else the custom title.
|
||||
@@ -882,32 +1043,35 @@ export class SessionIndex {
|
||||
}
|
||||
|
||||
/**
|
||||
* The titles the SDK reports for the listed transcript (`customTitle` and
|
||||
* `summary` of `getSessionInfo`). The SDK reads the first copy of the
|
||||
* session that its search finds; its answer is used only when that copy is
|
||||
* the listed file (same size and mtime), else the same titles are taken
|
||||
* from the listed file itself (see {@link sdkTitles}).
|
||||
* `facts` with the background tasks of the whole transcript, when the
|
||||
* tasks of its tail are not complete: a task can start before the tail.
|
||||
* Only a finished turn of a live session waits for the tasks, so only
|
||||
* then the whole transcript is read.
|
||||
*/
|
||||
private async titlesOf(
|
||||
private async withAllBackgroundTasks(
|
||||
candidate: TranscriptCandidate,
|
||||
headTail: HeadTail,
|
||||
fileCwd: string | undefined,
|
||||
): Promise<SdkTitles> {
|
||||
const dir = [fileCwd, candidate.projectPath].find(
|
||||
(cwd) => cwd !== undefined && isExactProjectDir(candidate.dirName, cwd),
|
||||
);
|
||||
const info = await this.getSessionInfo(candidate.sessionId, dir ? { dir } : {}).catch(
|
||||
() => undefined,
|
||||
);
|
||||
if (
|
||||
info &&
|
||||
info.fileSize === candidate.size &&
|
||||
info.lastModified === Math.trunc(candidate.mtimeMs)
|
||||
) {
|
||||
return { customTitle: info.customTitle, summary: info.summary };
|
||||
}
|
||||
facts: TranscriptFacts,
|
||||
): Promise<TranscriptFacts> {
|
||||
if (facts.turnState !== "finished") return facts;
|
||||
if (facts.backgroundTasks?.complete) return facts;
|
||||
const isLive = await this.options.isLive?.(candidate.sessionId);
|
||||
if (!isLive) return facts;
|
||||
const backgroundTasks = await searchBackgroundTasks(candidate.filePath, candidate.size);
|
||||
return { ...facts, backgroundTasks };
|
||||
}
|
||||
|
||||
/**
|
||||
* The titles the SDK gives the listed transcript (`customTitle` and
|
||||
* `summary` of its `getSessionInfo`), by the SDK's rule from the head and
|
||||
* tail read here and the sidecar (see {@link sdkTitles}). Not through the
|
||||
* SDK itself: its lookup of the transcript reads the whole project
|
||||
* directory on every call, which made each page of a large project cost a
|
||||
* listing of every transcript per row, and it may read another copy of the
|
||||
* session than the listed one.
|
||||
*/
|
||||
private async titlesOf(candidate: TranscriptCandidate, headTail: HeadTail): Promise<SdkTitles> {
|
||||
const sidecar =
|
||||
tailCustomTitle(headTail.tail) === undefined
|
||||
tailCustomTitle(headTail) === undefined
|
||||
? await readSidecarTitle(candidate.filePath, candidate.sessionId)
|
||||
: undefined;
|
||||
return sdkTitles(headTail, sidecar);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* the RFDs in `_meta.jetbrains.air`.
|
||||
*/
|
||||
|
||||
import type { SessionInfo } from "@agentclientprotocol/sdk";
|
||||
import type { SessionInfo } from "@agentclientprotocol/sdk/unstable";
|
||||
import { withAirMeta } from "../air-extension.js";
|
||||
import { deriveActivity, selectCost, type OwnSessionState } from "./activity.js";
|
||||
import type { LiveRecord } from "./live-registry.js";
|
||||
@@ -38,6 +38,7 @@ export function sessionInfoOf(
|
||||
lastPromptAt: iso(facts.lastPromptAt),
|
||||
model: facts.model,
|
||||
forkedFrom: facts.forkedFrom,
|
||||
agentWorktree: row.agentWorktree,
|
||||
state: activity?.state,
|
||||
lastTurnEndedAt: activity?.lastTurnEndedAt,
|
||||
cost: cost === undefined ? undefined : { amount: cost, currency: "USD" },
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
*
|
||||
* A last message longer than the tail window leaves no message in it: then
|
||||
* the tail window grows, up to {@link MAX_TAIL_SIZE}, until it holds one.
|
||||
*
|
||||
* The open background tasks come from the tail, on top of the tasks of an
|
||||
* earlier scan (see `background-tasks.ts`).
|
||||
*/
|
||||
|
||||
import * as fs from "node:fs/promises";
|
||||
import { backgroundTasksOf, type BackgroundTasks } from "./background-tasks.js";
|
||||
import { firstPrompt, isUserPrompt, mediaPrompt, promptOf } from "./first-prompt.js";
|
||||
|
||||
const CHUNK_SIZE = 64 * 1024;
|
||||
@@ -51,32 +55,52 @@ export type TranscriptFacts = {
|
||||
/** The session this one was forked from: the `forkedFrom.sessionId` that
|
||||
* the SDK and CLI fork write on every copied record. */
|
||||
forkedFrom?: string;
|
||||
/** What the last `worktree-state` record says: true when its
|
||||
* `worktreeSession` is set and not `enteredExisting` (Claude Code made the
|
||||
* worktree and the session is in it), false when it says otherwise.
|
||||
* Undefined when no such record was read. See {@link agentWorktreeOf}. */
|
||||
agentWorktree?: boolean;
|
||||
/** The background tasks that did not end. From the tail, on top of what
|
||||
* an earlier scan found when the file only grew within the tail. */
|
||||
backgroundTasks?: BackgroundTasks;
|
||||
/** Whether the head or the tail holds a user or an assistant message. A
|
||||
* transcript without one is a metadata-only stub. */
|
||||
hasMessages: boolean;
|
||||
};
|
||||
|
||||
export type HeadTail = { head: string; tail: string };
|
||||
export type HeadTail = {
|
||||
head: string;
|
||||
/** The last 64 KB from their first whole line on; the whole file when it
|
||||
* fits in the head. */
|
||||
tail: string;
|
||||
/** The last 64 KB as the SDK searches them, with the cut line before
|
||||
* `tail`; absent when that is `tail`. */
|
||||
sdkTail?: string;
|
||||
};
|
||||
|
||||
/** Facts that an earlier scan may have found before the tail window. */
|
||||
const INHERITED_FACTS = ["model", "tailCwd", "lastTurnEndedAt"] as const;
|
||||
const INHERITED_FACTS = ["model", "tailCwd", "lastTurnEndedAt", "agentWorktree"] as const;
|
||||
|
||||
/** What an earlier scan of the same file found, at `size`. */
|
||||
export type PreviousScan = { size: number } & Pick<
|
||||
TranscriptFacts,
|
||||
"lastPromptAt" | "promptSearched" | (typeof INHERITED_FACTS)[number]
|
||||
"lastPromptAt" | "promptSearched" | "backgroundTasks" | (typeof INHERITED_FACTS)[number]
|
||||
>;
|
||||
|
||||
/** The last `window` bytes of a file of `size` bytes, without the first,
|
||||
* cut line. The whole file when it fits. */
|
||||
async function readTail(handle: fs.FileHandle, size: number, window: number): Promise<string> {
|
||||
async function readTail(
|
||||
handle: fs.FileHandle,
|
||||
size: number,
|
||||
window: number,
|
||||
): Promise<{ raw: string; tail: string }> {
|
||||
const length = Math.min(window, size);
|
||||
const buffer = Buffer.allocUnsafe(length);
|
||||
const { bytesRead } = await handle.read(buffer, 0, length, size - length);
|
||||
const raw = buffer.toString("utf8", 0, bytesRead);
|
||||
if (length === size) return raw;
|
||||
if (length === size) return { raw, tail: raw };
|
||||
const newline = raw.indexOf("\n");
|
||||
return newline >= 0 ? raw.slice(newline + 1) : "";
|
||||
return { raw, tail: newline >= 0 ? raw.slice(newline + 1) : "" };
|
||||
}
|
||||
|
||||
/** The first and the last 64 KB of the first `size` bytes of `filePath`:
|
||||
@@ -98,7 +122,8 @@ export async function readHeadTailOf(handle: fs.FileHandle, size: number): Promi
|
||||
const first = await handle.read(buffer, 0, Math.min(CHUNK_SIZE, size), 0);
|
||||
const head = buffer.toString("utf8", 0, first.bytesRead);
|
||||
if (size <= CHUNK_SIZE) return { head, tail: head };
|
||||
return { head, tail: await readTail(handle, size, CHUNK_SIZE) };
|
||||
const { raw, tail } = await readTail(handle, size, CHUNK_SIZE);
|
||||
return raw === tail ? { head, tail } : { head, tail, sdkTail: raw };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,6 +164,14 @@ export async function scanTranscriptFile(
|
||||
}
|
||||
}
|
||||
}
|
||||
const previousTasks = grownWithinTail ? previous.backgroundTasks : undefined;
|
||||
facts.backgroundTasks = tailBackgroundTasks(read.tail, size, previousTasks);
|
||||
// No worktree record in the tail (or before it, by an earlier scan): the
|
||||
// one `claude --worktree` writes at the start may still be in the head.
|
||||
if (facts.agentWorktree === undefined) {
|
||||
const agentWorktree = agentWorktreeOf(read.head);
|
||||
if (agentWorktree !== undefined) facts.agentWorktree = agentWorktree;
|
||||
}
|
||||
if (facts.lastPromptAt !== undefined || size <= CHUNK_SIZE) {
|
||||
facts.promptSearched = true;
|
||||
} else if (grownWithinTail && (previous.lastPromptAt !== undefined || previous.promptSearched)) {
|
||||
@@ -150,7 +183,8 @@ export async function scanTranscriptFile(
|
||||
if (!facts.hasMessages || complete(facts) || size <= CHUNK_SIZE) return facts;
|
||||
const key = `${filePath}\0${size}\0${identity ?? ""}`;
|
||||
const searched = fullySearched.get(key);
|
||||
if (searched) return searched;
|
||||
// The tasks can differ by the earlier scan.
|
||||
if (searched) return { ...searched, backgroundTasks: facts.backgroundTasks };
|
||||
let result = facts;
|
||||
const handle = await fs.open(filePath, "r");
|
||||
try {
|
||||
@@ -186,9 +220,15 @@ export async function scanTranscriptFile(
|
||||
...wider,
|
||||
...(facts.model !== undefined && { model: facts.model }),
|
||||
...(facts.tailCwd !== undefined && { tailCwd: facts.tailCwd }),
|
||||
// The wider tail holds the narrow one, so a record it has is the
|
||||
// latest; else the narrow scan's (inherited or from the head) stands.
|
||||
...((wider.agentWorktree ?? facts.agentWorktree) !== undefined && {
|
||||
agentWorktree: wider.agentWorktree ?? facts.agentWorktree,
|
||||
}),
|
||||
// A turn end the wider tail lacks may lie before it: the
|
||||
// narrow scan inherited it from an earlier scan of this file.
|
||||
lastTurnEndedAt: wider.lastTurnEndedAt ?? facts.lastTurnEndedAt,
|
||||
backgroundTasks: facts.backgroundTasks,
|
||||
hasMessages: facts.hasMessages,
|
||||
lastPromptAt,
|
||||
// Searched to the start of the file, or found.
|
||||
@@ -208,6 +248,22 @@ export async function scanTranscriptFile(
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The background tasks of the tail of a file of `size` bytes. `previous`:
|
||||
* the tasks of an earlier scan, when the file only grew within the tail
|
||||
* since. The set is complete when the tail is the whole file, or when it
|
||||
* holds all records after a complete earlier set.
|
||||
*/
|
||||
function tailBackgroundTasks(
|
||||
tail: string,
|
||||
size: number,
|
||||
previous: BackgroundTasks | undefined,
|
||||
): BackgroundTasks {
|
||||
if (size <= CHUNK_SIZE) return backgroundTasksOf(tail, { complete: true });
|
||||
if (previous?.complete) return backgroundTasksOf(tail, { complete: true, after: previous });
|
||||
return backgroundTasksOf(tail, { complete: false });
|
||||
}
|
||||
|
||||
const RELOCATED_MARKER = '"relocated"';
|
||||
|
||||
/** The cwd of the last `relocated` record of the tail: where the session
|
||||
@@ -225,6 +281,43 @@ export function relocatedCwd(tail: string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const WORKTREE_STATE_MARKER = '"worktree-state"';
|
||||
|
||||
/**
|
||||
* Whether the last `worktree-state` record of `text` says the session is in
|
||||
* a worktree that Claude Code made for it, as AIR's native Claude provider
|
||||
* reads it: a `worktreeSession` that is an object without
|
||||
* `enteredExisting: true`. The last line that parses as a `worktree-state`
|
||||
* record decides; any other `worktreeSession` (`null`, which the CLI writes
|
||||
* when the session leaves the worktree, missing, or no object) means false.
|
||||
* Only lines that are not JSON (such as the cut first line of a window) are
|
||||
* skipped. Undefined when `text` holds no such record.
|
||||
*
|
||||
* The CLI writes the record when it enters or leaves a worktree (for
|
||||
* `claude --worktree`, at the start of the session, so in the head), and
|
||||
* writes it again with the other session metadata at the end of the
|
||||
* transcript (on exit and as the transcript grows), so the tail usually
|
||||
* holds the latest one. Only the head and the tail are read: a session that
|
||||
* entered a worktree midway through a long transcript, with no metadata
|
||||
* written after it yet, is not seen until the CLI writes the metadata again.
|
||||
*/
|
||||
export function agentWorktreeOf(text: string): boolean | undefined {
|
||||
if (!text.includes(WORKTREE_STATE_MARKER)) return undefined;
|
||||
const lines = text.split("\n");
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
const line = lines[i]!;
|
||||
if (!line.includes(WORKTREE_STATE_MARKER)) continue;
|
||||
const entry = parseLine(line);
|
||||
// Only a line that is no JSON record is skipped: the latest record that
|
||||
// parses decides, even when its `worktreeSession` is missing or no object.
|
||||
if (entry?.type !== "worktree-state") continue;
|
||||
const session = entry.worktreeSession;
|
||||
if (!session || typeof session !== "object" || Array.isArray(session)) return false;
|
||||
return (session as { enteredExisting?: unknown }).enteredExisting !== true;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** The cwd a transcript belongs to, as the SDK reads it: the last
|
||||
* `relocated` record of the tail, else the first cwd of the head. */
|
||||
export function transcriptProjectCwd({ head, tail }: HeadTail): string | undefined {
|
||||
@@ -331,6 +424,10 @@ export function scanTranscript(
|
||||
if (headCwd !== undefined) facts.headCwd = decodeJsonString(headCwd);
|
||||
const forkedFrom = FORKED_FROM_PATTERN.exec(head)?.[1];
|
||||
if (forkedFrom !== undefined) facts.forkedFrom = forkedFrom;
|
||||
// The tail only: scanTranscriptFile falls back to the head after it
|
||||
// inherits what an earlier scan found before the tail.
|
||||
const agentWorktree = agentWorktreeOf(tail);
|
||||
if (agentWorktree !== undefined) facts.agentWorktree = agentWorktree;
|
||||
|
||||
const lines = tail.split("\n");
|
||||
let turnEndFound = false;
|
||||
@@ -464,8 +561,8 @@ export function isSidechainTranscript(head: string): boolean {
|
||||
}
|
||||
|
||||
/** The last `customTitle` of the tail, as the SDK reads it. */
|
||||
export function tailCustomTitle(tail: string): string | undefined {
|
||||
return lastField(tail, "customTitle");
|
||||
export function tailCustomTitle({ tail, sdkTail }: HeadTail): string | undefined {
|
||||
return lastField(sdkTail ?? tail, "customTitle");
|
||||
}
|
||||
|
||||
/** The last non-blank top-level `agentName` of the records of `text`, the
|
||||
@@ -500,11 +597,15 @@ export type SdkTitles = {
|
||||
/**
|
||||
* The titles the SDK gives a transcript, from its own head and tail, in the
|
||||
* SDK's order: custom title (tail, the sidecar, head), AI title, then
|
||||
* {@link generatedTitle}. For a transcript that the SDK `getSessionInfo`
|
||||
* does not read (another copy of the session comes first in its search).
|
||||
* The sidecar title counts only when the tail has no custom title.
|
||||
* {@link generatedTitle}: what the SDK `getSessionInfo` reports for this
|
||||
* file, without its lookup of the file. The sidecar title counts only when
|
||||
* the tail has no custom title.
|
||||
*/
|
||||
export function sdkTitles({ head, tail }: HeadTail, sidecarTitle?: string): SdkTitles {
|
||||
export function sdkTitles(
|
||||
{ head, tail: wholeLines, sdkTail }: HeadTail,
|
||||
sidecarTitle?: string,
|
||||
): SdkTitles {
|
||||
const tail = sdkTail ?? wholeLines;
|
||||
const customTitle =
|
||||
lastField(tail, "customTitle") ??
|
||||
sidecarTitle ??
|
||||
|
||||
@@ -115,6 +115,18 @@ export async function worktreeCounterparts(cwd: string): Promise<string[]> {
|
||||
return [...new Set(existing.filter((value): value is string => value !== undefined))];
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether `cwd` lies in a linked worktree: its git dir is
|
||||
* `<common-dir>/worktrees/<name>`. False in the main worktree, in a
|
||||
* submodule and outside a repository.
|
||||
*/
|
||||
export async function isInLinkedWorktree(cwd: string): Promise<boolean> {
|
||||
const gitDir = await findGitDir(cwd);
|
||||
if (!gitDir) return false;
|
||||
const commonDir = await commonDirOf(gitDir);
|
||||
return path.resolve(path.dirname(gitDir)) === path.resolve(commonDir, "worktrees");
|
||||
}
|
||||
|
||||
/**
|
||||
* Every existing worktree of the repository that contains `cwd`: the main
|
||||
* worktree and the linked ones. Empty outside a repository.
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type SessionNotification,
|
||||
type SetSessionModeRequest,
|
||||
type SetSessionModeResponse,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
} from "@agentclientprotocol/sdk/unstable";
|
||||
import type {
|
||||
ModelInfo,
|
||||
PermissionMode,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { SessionConfigOption } from "@agentclientprotocol/sdk";
|
||||
import type { SessionConfigOption } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { ModelInfo, Query, Settings } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { AIR_RECOMMENDED_CONFIG_VALUE_CAPABILITY, withAirMeta } from "./air-extension.js";
|
||||
import { bedrockRegionPrefix } from "./gateway-models.js";
|
||||
|
||||
export const MODEL_CONFIG_ID = "model";
|
||||
|
||||
@@ -262,31 +263,47 @@ function resolveSettingsModel(
|
||||
|
||||
const MODEL_FAMILY_ALIASES = new Set(["opus", "sonnet", "haiku", "fable"]);
|
||||
|
||||
/** A model id without the spellings `deniedModels` ignores: provider
|
||||
* prefixes, dates, version suffixes, `-fast` and context hints. */
|
||||
function normalizeDeniedModelId(value: string): string {
|
||||
/** A model id without the spellings that name the same model: provider
|
||||
* prefixes, dates, version suffixes, `:variant` suffixes, dotted versions,
|
||||
* `-fast` and context hints. `deniedModels` and gateway listings both match on
|
||||
* it. OpenRouter lists `anthropic/claude-opus-5.5:batch[1m]`, which normalizes
|
||||
* to `claude-opus-5-5`; `anthropic/openai/gpt-5` keeps its `openai/` prefix and
|
||||
* matches no Claude id. */
|
||||
function normalizeModelId(value: string): string {
|
||||
let id = stripContextHints(value.trim().toLowerCase());
|
||||
const providerPrefix = id.lastIndexOf("anthropic.");
|
||||
if (providerPrefix !== -1) id = id.slice(providerPrefix + "anthropic.".length);
|
||||
return id
|
||||
.replace(/@.*$/, "")
|
||||
.replace(/-v\d+(?::\d+)?$/, "")
|
||||
.replace(/-fast$/, "")
|
||||
.replace(/-\d{8}$/, "");
|
||||
return (
|
||||
id
|
||||
// Only a leading `anthropic/`: OpenRouter's `anthropic/openai/gpt-5` must
|
||||
// keep `openai/`, which `lastIndexOf` would strip along with it.
|
||||
.replace(/^anthropic\//, "")
|
||||
// `:batch`, `:free`. A letter must follow the colon, so Bedrock's `-v1:0`
|
||||
// is left for the `-vN:M` step below.
|
||||
.replace(/:[a-z][a-z0-9-]*$/, "")
|
||||
// `claude-opus-5.5` -> `claude-opus-5-5`; only dots between two digits.
|
||||
.replace(/(?<=\d)\.(?=\d)/g, "-")
|
||||
.replace(/@.*$/, "")
|
||||
.replace(/-v\d+(?::\d+)?$/, "")
|
||||
.replace(/-fast$/, "")
|
||||
.replace(/-\d{8}$/, "")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether managed `deniedModels` blocks a model id, following the CLI's rules
|
||||
* closely enough for the picker: a family alias blocks the whole family, and a
|
||||
* model id blocks that version in every spelling plus the later minor versions
|
||||
* it prefixes (`claude-opus-5` also blocks `claude-opus-5-5`). The CLI stays
|
||||
* the enforcer; this only keeps the picker from offering a refused row.
|
||||
* it prefixes (`claude-opus-5` also blocks `claude-opus-5-5`). Entries share
|
||||
* `normalizeModelId`, so `claude-opus-4.5` or `anthropic/claude-opus-4.5` also
|
||||
* blocks `claude-opus-4-5`, which the CLI may still allow. The CLI stays the
|
||||
* enforcer; this only keeps the picker from offering a refused row.
|
||||
*/
|
||||
export function isDeniedModel(modelId: string, deniedModels: readonly string[]): boolean {
|
||||
const id = normalizeDeniedModelId(modelId);
|
||||
const id = normalizeModelId(modelId);
|
||||
if (!id) return false;
|
||||
return deniedModels.some((entry) => {
|
||||
const denied = normalizeDeniedModelId(entry);
|
||||
const denied = normalizeModelId(entry);
|
||||
if (!denied) return false;
|
||||
if (MODEL_FAMILY_ALIASES.has(denied)) return id === denied || id.includes(`-${denied}`);
|
||||
return id === denied || id.startsWith(`${denied}-`);
|
||||
@@ -379,6 +396,48 @@ export function applyAvailableModelsAllowlist(
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the SDK models the `providers/set` gateway listed (`listGatewayModels`,
|
||||
* `listBedrockModels`). A row matches when its `resolvedModel` (or
|
||||
* its `value` when the SDK gives none) names a listed model in any spelling:
|
||||
* `claude-sonnet-5[1m]` matches a listed `claude-sonnet-5-20260101`, and
|
||||
* `eu.anthropic.claude-sonnet-5` matches a listed `eu.anthropic.claude-sonnet-5-v1:0`.
|
||||
*
|
||||
* With a Bedrock `regionPrefix`, a row resolved to another cross-region prefix
|
||||
* (`us.anthropic.claude-sonnet-5` under `eu`) is dropped even when the same
|
||||
* model is listed under `eu`: the CLI would call it in the other region.
|
||||
*
|
||||
* A row the SDK gives no `resolvedModel` names its model only by an alias, so
|
||||
* it is checked as far as the alias allows: `default` is kept, and a family
|
||||
* alias such as `opus` is kept when the gateway lists a model of that family.
|
||||
*
|
||||
* When no row matches by its model (a `default` row kept only for lacking a
|
||||
* `resolvedModel` does not count), the gateway serves none of the SDK's
|
||||
* models, and nothing is left: the session offers no model.
|
||||
*/
|
||||
export function narrowToGatewayModels(
|
||||
models: ModelInfo[],
|
||||
listedModelIds: readonly string[],
|
||||
regionPrefix?: string,
|
||||
): ModelInfo[] {
|
||||
const listed = new Set(listedModelIds.map(normalizeModelId).filter(Boolean));
|
||||
const narrowed = models.filter((model) => {
|
||||
const modelId = model.resolvedModel ?? model.value;
|
||||
const prefix = bedrockRegionPrefix(modelId);
|
||||
if (regionPrefix !== undefined && prefix !== undefined && prefix !== regionPrefix) {
|
||||
return false;
|
||||
}
|
||||
const id = normalizeModelId(modelId);
|
||||
if (listed.has(id)) return true;
|
||||
if (model.resolvedModel !== undefined) return false;
|
||||
if (model.value === "default") return true;
|
||||
return MODEL_FAMILY_ALIASES.has(id) && [...listed].some((entry) => entry.includes(`-${id}`));
|
||||
});
|
||||
return narrowed.some((model) => listed.has(normalizeModelId(model.resolvedModel ?? model.value)))
|
||||
? narrowed
|
||||
: [];
|
||||
}
|
||||
|
||||
/** Whether a rejected `setModel` was vetoed by a user-configured
|
||||
* PreModelSwitch hook. The CLI's rejection message is the stable,
|
||||
* distinguishable marker ("Model switch blocked by a PreModelSwitch hook:
|
||||
@@ -397,7 +456,18 @@ export async function getAvailableModels(
|
||||
isResumedSession: boolean,
|
||||
sessionId: string,
|
||||
resumedModelHint?: string,
|
||||
narrowedByGateway = false,
|
||||
): Promise<SessionModelState> {
|
||||
// A gateway that lists none of the SDK's models leaves none to offer
|
||||
// (`narrowToGatewayModels`). The session stays on the model the SDK runs,
|
||||
// and the gateway refuses its requests.
|
||||
if (models.length === 0) {
|
||||
return {
|
||||
availableModels: [],
|
||||
currentModelId: (isResumedSession && resumedModelHint) || sdkModels[0]?.value || "default",
|
||||
};
|
||||
}
|
||||
|
||||
const settings = settingsManager.getSettings();
|
||||
|
||||
let currentModel = models[0];
|
||||
@@ -444,9 +514,34 @@ export async function getAvailableModels(
|
||||
// be re-asserted to keep the reported model truthful.
|
||||
// Anything else (fuzzy match, allowlist-synthesized value, alias) gets a
|
||||
// setModel call so we don't drift from the user's intended pin.
|
||||
// Case (a) does not hold when `narrowToGatewayModels` left the SDK on a
|
||||
// model the gateway does not list: the dropped `default` row of a fresh or
|
||||
// transcript-less session, or a resumed transcript model outside the list.
|
||||
// Such a session switches to models[0].
|
||||
const sdkSawSameValue = sdkModels.some((m) => m.value === currentModel.value);
|
||||
const sdkDefault = sdkModels.find((m) => m.value === "default");
|
||||
const sdkDefaultDropped = sdkDefault !== undefined && !models.some((m) => m.value === "default");
|
||||
let switchToListed = false;
|
||||
if (resolvedFromInput === undefined) {
|
||||
if (!isResumedSession || !resumedModelHint) {
|
||||
switchToListed = sdkDefaultDropped;
|
||||
} else if (
|
||||
narrowedByGateway &&
|
||||
(!models.some((m) => m.value === currentModel.value) ||
|
||||
// A loose match can map the transcript's model onto a listed row of
|
||||
// another version, which the CLI does not resume on.
|
||||
(currentModel.resolvedModel !== undefined &&
|
||||
normalizeModelId(currentModel.resolvedModel) !== normalizeModelId(resumedModelHint)))
|
||||
) {
|
||||
currentModel = models[0];
|
||||
switchToListed = true;
|
||||
}
|
||||
}
|
||||
// When the switch fails, the SDK stays on the model it ran before it.
|
||||
const modelBeforeSwitch = (): ModelInfo =>
|
||||
switchToListed && sdkDefault !== undefined ? sdkDefault : models[0];
|
||||
const skipSetModel =
|
||||
resolvedFromInput === undefined ||
|
||||
(resolvedFromInput === undefined && !switchToListed) ||
|
||||
(!isResumedSession && currentModel.value === resolvedFromInput && sdkSawSameValue);
|
||||
if (!skipSetModel) {
|
||||
const setModelStartedAt = performance.now();
|
||||
@@ -478,15 +573,19 @@ export async function getAvailableModels(
|
||||
// live model back on this path: getContextUsage isn't serviced on a
|
||||
// fresh session until the first prompt turn (issues #886/#880).
|
||||
if (!isResumedSession) {
|
||||
if (!isPreModelSwitchHookBlock(error)) throw error;
|
||||
// A switch onto a listed model is advisory too: the session stays on
|
||||
// the model the SDK runs and reports it.
|
||||
if (!switchToListed && !isPreModelSwitchHookBlock(error)) throw error;
|
||||
logger.error(
|
||||
`Model pin "${currentModel.value}" was vetoed by a PreModelSwitch hook; staying on the default model:`,
|
||||
`Model switch to "${currentModel.value}" failed or was vetoed by a PreModelSwitch hook; staying on the default model:`,
|
||||
error,
|
||||
);
|
||||
currentModel = models[0];
|
||||
currentModel = modelBeforeSwitch();
|
||||
} else {
|
||||
logger.error(`Failed to re-assert model "${currentModel.value}" on resume:`, error);
|
||||
currentModel = resumedModelHint ? matchResumedModel(models, resumedModelHint) : models[0];
|
||||
currentModel = resumedModelHint
|
||||
? matchResumedModel(models, resumedModelHint)
|
||||
: modelBeforeSwitch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk";
|
||||
import type { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk/unstable";
|
||||
|
||||
/** Fire-and-forget advisory text for the user: live events rather than
|
||||
* conversation history. See the ACP Session Notices RFD
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
/**
|
||||
* AI-generated session titles.
|
||||
*
|
||||
* Claude Code's own auto-title generation never runs under the Agent SDK — the
|
||||
* latch that arms it is pre-set in the headless path — so `SDKSessionInfo.summary`
|
||||
* degrades to the raw first prompt. This module asks the CLI for a real title
|
||||
* instead, via the `generate_session_title` control request, and publishes it
|
||||
* over ACP as a `session_info_update`.
|
||||
* Claude Code titles a new SDK session itself: when it reads the first prompt,
|
||||
* it asks a small model for a title and stores it in the transcript as an
|
||||
* `ai-title` entry, a few seconds in. It sends no message when it does. This
|
||||
* module asks for the title with the `generate_session_title` control request,
|
||||
* which returns the stored title or waits for the one being generated, and
|
||||
* publishes it over ACP as a `session_info_update`. Where the CLI does not
|
||||
* title the session itself (a resumed session, or one with
|
||||
* `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` or
|
||||
* `CLAUDE_CODE_DISABLE_TERMINAL_TITLE` set), the same request generates it.
|
||||
*
|
||||
* {@link SessionTitles} holds the per-session state and is owned by `Session`.
|
||||
* `acp-agent.ts` drives it from four places: {@link SessionTitles.onPrompt} and
|
||||
* `acp-agent.ts` drives it from five places: {@link SessionTitles.onPrompt} and
|
||||
* {@link SessionTitles.onAssistantText} to collect text,
|
||||
* {@link SessionTitles.onPromptQueued} once a prompt is sent to the CLI,
|
||||
* {@link SessionTitles.onTurnEnd} at `session_state_changed: idle`, and
|
||||
* {@link SessionTitles.reset} on `conversation_reset`.
|
||||
*/
|
||||
|
||||
import type { ContentBlock, PromptRequest } from "@agentclientprotocol/sdk";
|
||||
import type { ContentBlock, PromptRequest } from "@agentclientprotocol/sdk/unstable";
|
||||
import { getSessionInfo, type Query, type SDKSessionInfo } from "@anthropic-ai/claude-agent-sdk";
|
||||
import type { ClaudeAcpAgent, Session } from "./acp-agent.js";
|
||||
import { ExplicitTitle } from "./session-index/explicit-title.js";
|
||||
@@ -77,6 +82,11 @@ export function appendTitleContext(previous: string | undefined, text: string):
|
||||
return next.length > MAX_TITLE_CONTEXT_LENGTH ? next.slice(-MAX_TITLE_CONTEXT_LENGTH) : next;
|
||||
}
|
||||
|
||||
/** The text chunks of a prompt, one per line. */
|
||||
function promptText(prompt: PromptRequest["prompt"]): string {
|
||||
return prompt.flatMap((chunk) => (chunk.type === "text" ? [chunk.text] : [])).join("\n");
|
||||
}
|
||||
|
||||
/** Whether this SDK still offers on-demand title generation. */
|
||||
function supportsTitleGeneration(query: Query): boolean {
|
||||
return typeof (query as TitleCapableQuery).generateSessionTitle === "function";
|
||||
@@ -100,6 +110,11 @@ export class SessionTitles {
|
||||
* one. Released by {@link reset}, and when generation yields nothing. */
|
||||
private settled = false;
|
||||
|
||||
/** Whether {@link onPromptQueued} asked for a title from the latest prompt.
|
||||
* A turn end then does not ask again: like the CLI, a prompt that yields
|
||||
* no title gets one try, and the next prompt tries again. */
|
||||
private askedAtPrompt = false;
|
||||
|
||||
/** The title a client set, which no generated title replaces. */
|
||||
private readonly explicit = new ExplicitTitle();
|
||||
|
||||
@@ -132,11 +147,33 @@ export class SessionTitles {
|
||||
if (this.settled) {
|
||||
return;
|
||||
}
|
||||
const promptText = prompt
|
||||
.flatMap((chunk) => (chunk.type === "text" ? [chunk.text] : []))
|
||||
.join("\n");
|
||||
this.context = appendTitleContext(this.context, `\n${promptText(prompt)}\n`);
|
||||
}
|
||||
|
||||
this.context = appendTitleContext(this.context, `\n${promptText}\n`);
|
||||
/** Ask for the title as soon as a prompt is sent to the CLI, instead of
|
||||
* waiting for `idle`: `idle` comes only after the whole turn and its
|
||||
* background tasks, often minutes after the CLI stored its title. The CLI
|
||||
* answers `generate_session_title` while the turn runs, and with the title
|
||||
* it stored or is generating, so this makes no second model call. The
|
||||
* prompt alone is the description, because the CLI titles from the prompt
|
||||
* alone. When this yields no title, the stored summary is published and
|
||||
* the next prompt tries again. */
|
||||
onPromptQueued(session: Session, prompt: PromptRequest["prompt"]): void {
|
||||
this.askedAtPrompt = false;
|
||||
// `session.cancelled` is not checked: after a cancelled turn it stays set
|
||||
// until the next turn starts, which is after this prompt is queued.
|
||||
if (this.settled || this.explicit.active || session.queryClosed) {
|
||||
return;
|
||||
}
|
||||
if (!supportsTitleGeneration(session.query)) {
|
||||
return;
|
||||
}
|
||||
const description = promptText(prompt).trim();
|
||||
if (description.length < MIN_TITLE_CONTEXT_LENGTH) {
|
||||
return;
|
||||
}
|
||||
this.askedAtPrompt = true;
|
||||
this.startGeneration(() => this.titleFromPrompt(session, description));
|
||||
}
|
||||
|
||||
/** Collect the assistant's own answer for the title. Chunks are appended
|
||||
@@ -152,6 +189,7 @@ export class SessionTitles {
|
||||
* `conversation_reset`, which mounts a fresh transcript. */
|
||||
reset(): void {
|
||||
this.settled = false;
|
||||
this.askedAtPrompt = false;
|
||||
this.explicit.reset();
|
||||
this.context = undefined;
|
||||
this.lastTitle = undefined;
|
||||
@@ -165,10 +203,12 @@ export class SessionTitles {
|
||||
* title — a user `/rename` or one generated earlier; the SDK folds both into
|
||||
* that one field. Adopt it and latch, so neither is ever titled over.
|
||||
*
|
||||
* With no title yet, ask the SDK to generate one in the background: it is a
|
||||
* ~2s small-model call and turn-end must not wait on it. Only when that is
|
||||
* not possible do we fall back to `summary`, which for an SDK-driven session
|
||||
* is just the raw first prompt. */
|
||||
* With no title yet, and none asked for at the latest prompt (see
|
||||
* {@link onPromptQueued}), ask the SDK to generate one in the background
|
||||
* from the prompts and replies so far: it is a ~2s small-model call and
|
||||
* turn-end must not wait on it. That covers a prompt too short to title
|
||||
* from, such as "hi". Only when that is not possible do we fall back to
|
||||
* `summary`, which for an SDK-driven session is just the raw first prompt. */
|
||||
async onTurnEnd(session: Session): Promise<void> {
|
||||
if (this.explicit.settledByClient) {
|
||||
// No title is generated over the one the client set, but a custom title
|
||||
@@ -200,17 +240,9 @@ export class SessionTitles {
|
||||
? { title: info.summary, lastModified: info.lastModified }
|
||||
: undefined;
|
||||
|
||||
if (this.canRequest(session)) {
|
||||
this.settled = true;
|
||||
|
||||
this.explicit.track(
|
||||
this.requestGenerateTitle(session, fallback).catch((error) => {
|
||||
this.agent.logger.error(
|
||||
`Session ${this.sessionId}: session title update failed: ${error}`,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
if (!this.askedAtPrompt && this.canRequest(session)) {
|
||||
const description = this.context?.trim() ?? "";
|
||||
this.startGeneration(() => this.requestGenerateTitle(session, description, fallback));
|
||||
return;
|
||||
}
|
||||
// Only while this session has no title of its own: `summary` degrades to the
|
||||
@@ -309,17 +341,48 @@ export class SessionTitles {
|
||||
return (this.context?.trim().length ?? 0) >= MIN_TITLE_CONTEXT_LENGTH;
|
||||
}
|
||||
|
||||
/** Generate a title from the session's own text and publish it. `persist`
|
||||
* writes it to the session file, which is what carries it into
|
||||
* `session/list` and `session/load`. A null or failed generation releases the
|
||||
* latch so a later turn retries, and publishes `fallback` (the stored
|
||||
* summary) so a backend that can't generate titles is no worse off than
|
||||
* before. */
|
||||
/** Latch the title and run `generation` in the background, so neither the
|
||||
* prompt nor turn-end waits on a model call. A client title change waits
|
||||
* for it to finish (see {@link ExplicitTitle.track}). */
|
||||
private startGeneration(generation: () => Promise<void>): void {
|
||||
this.settled = true;
|
||||
this.explicit.track(
|
||||
generation().catch((error) => {
|
||||
this.agent.logger.error(`Session ${this.sessionId}: session title update failed: ${error}`);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/** The prompt-time title: the one the session file already holds (a resumed
|
||||
* session's title, or a `/rename`), else one generated from `description`.
|
||||
* The file is read first because the CLI checks only the titles it holds in
|
||||
* memory before generating one. */
|
||||
private async titleFromPrompt(session: Session, description: string): Promise<void> {
|
||||
const info = await this.readSessionInfo(session);
|
||||
if (this.agent.sessions[this.sessionId] !== session || this.explicit.active) {
|
||||
return;
|
||||
}
|
||||
if (info?.customTitle) {
|
||||
this.context = undefined;
|
||||
await this.publish(info.customTitle, info.lastModified);
|
||||
return;
|
||||
}
|
||||
const fallback = info?.summary
|
||||
? { title: info.summary, lastModified: info.lastModified }
|
||||
: undefined;
|
||||
await this.requestGenerateTitle(session, description, fallback);
|
||||
}
|
||||
|
||||
/** Generate a title from `description` and publish it. `persist` writes it
|
||||
* to the session file, which is what carries it into `session/list` and
|
||||
* `session/load`. A null or failed generation releases the latch so a later
|
||||
* prompt or turn end retries, and publishes `fallback` (the stored summary)
|
||||
* so a backend that can't generate titles is no worse off than before. */
|
||||
private async requestGenerateTitle(
|
||||
session: Session,
|
||||
description: string,
|
||||
fallback: TitleFallback | undefined,
|
||||
): Promise<void> {
|
||||
const description = this.context?.trim() ?? "";
|
||||
let title: string | null = null;
|
||||
|
||||
try {
|
||||
|
||||
@@ -50,6 +50,7 @@ export class SettingsManager {
|
||||
private cwd: string;
|
||||
private effective: Settings = {};
|
||||
private managedDeniedModels: string[] = [];
|
||||
private managedApiKeyHelper?: string;
|
||||
private watchers: fs.FSWatcher[] = [];
|
||||
private onChange?: () => void;
|
||||
private logger: { log: (...args: any[]) => void; error: (...args: any[]) => void };
|
||||
@@ -111,10 +112,15 @@ export class SettingsManager {
|
||||
this.managedDeniedModels = resolved.sources
|
||||
.filter((entry) => entry.source === "managed")
|
||||
.flatMap((entry) => entry.settings.deniedModels ?? []);
|
||||
this.managedApiKeyHelper =
|
||||
resolved.provenance.apiKeyHelper?.source === "managed"
|
||||
? resolved.effective.apiKeyHelper
|
||||
: undefined;
|
||||
} catch (error) {
|
||||
this.logger.error("Failed to resolve settings:", error);
|
||||
this.effective = {};
|
||||
this.managedDeniedModels = [];
|
||||
this.managedApiKeyHelper = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +183,16 @@ export class SettingsManager {
|
||||
return this.effective;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `apiKeyHelper` the CLI runs: managed settings outrank the `--settings`
|
||||
* override, which outranks the user/project/local files.
|
||||
*/
|
||||
getApiKeyHelper(settingsOverride?: Settings): string | undefined {
|
||||
return (
|
||||
this.managedApiKeyHelper ?? settingsOverride?.apiKeyHelper ?? this.effective.apiKeyHelper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* `deniedModels` entries from the managed tier, the only tier the CLI
|
||||
* honors them from.
|
||||
|
||||
@@ -1,12 +1,35 @@
|
||||
import { getSessionInfo } from "@anthropic-ai/claude-agent-sdk";
|
||||
import {
|
||||
getSessionInfo,
|
||||
getSubagentMessages,
|
||||
type SessionMessage,
|
||||
} from "@anthropic-ai/claude-agent-sdk";
|
||||
import { createReadStream } from "node:fs";
|
||||
import { readdir, readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import readline from "node:readline";
|
||||
import { isNativeSubagentControlTool, type HistoricalSubagent } from "./native-subagents.js";
|
||||
import { findTranscript } from "./resumed-session.js";
|
||||
|
||||
/** What the metadata file of a subagent says about it. */
|
||||
export interface SubagentRecord {
|
||||
/** The SDK agent id, the name of its transcript. */
|
||||
agentId: string;
|
||||
/** The `name` that the Agent call gave it. */
|
||||
name?: string;
|
||||
/** Its role, the `subagent_type` of the Agent call. */
|
||||
agentType?: string;
|
||||
/** The short `description` of the Agent call. */
|
||||
description?: string;
|
||||
/** Whether the Agent call ran it in the background. */
|
||||
background: boolean;
|
||||
/** Whether the user stopped it. */
|
||||
stoppedByUser: boolean;
|
||||
}
|
||||
|
||||
/** Where the subagent transcripts of a session are. */
|
||||
export interface SubagentHistory {
|
||||
/** The subagent id of each Agent or Task tool use that launched a subagent. */
|
||||
ids: Map<string, string>;
|
||||
/** The subagent of each Agent or Task tool use that launched one. */
|
||||
agents: Map<string, SubagentRecord>;
|
||||
/**
|
||||
* The project directory of the session, as the SDK takes it in `dir`. With
|
||||
* it, the SDK reads a subagent transcript without a search of every project.
|
||||
@@ -23,32 +46,278 @@ export interface SubagentHistory {
|
||||
* The ids are empty for a session without a local transcript.
|
||||
*/
|
||||
export async function subagentHistory(sessionId: string): Promise<SubagentHistory> {
|
||||
const ids = new Map<string, string>();
|
||||
const agents = new Map<string, SubagentRecord>();
|
||||
const transcript = await findTranscript(sessionId);
|
||||
if (!transcript) return { ids };
|
||||
const directory = path.join(path.dirname(transcript), sessionId, "subagents");
|
||||
if (!transcript) return { agents };
|
||||
const directory = subagentsDirectory(transcript, sessionId);
|
||||
let names: string[];
|
||||
try {
|
||||
names = await readdir(directory);
|
||||
} catch {
|
||||
return { ids };
|
||||
return { agents };
|
||||
}
|
||||
await Promise.all(
|
||||
names.map(async (name) => {
|
||||
const match = /^agent-(.+)\.meta\.json$/.exec(name);
|
||||
if (!match) return;
|
||||
try {
|
||||
const meta: unknown = JSON.parse(await readFile(path.join(directory, name), "utf8"));
|
||||
const toolUseId = (meta as { toolUseId?: unknown } | null)?.toolUseId;
|
||||
if (typeof toolUseId === "string") ids.set(toolUseId, match[1]!);
|
||||
const meta = JSON.parse(await readFile(path.join(directory, name), "utf8")) as Record<
|
||||
string,
|
||||
unknown
|
||||
> | null;
|
||||
if (typeof meta?.toolUseId !== "string") return;
|
||||
const text = (value: unknown) => (typeof value === "string" ? value : undefined);
|
||||
agents.set(meta.toolUseId, {
|
||||
agentId: match[1]!,
|
||||
name: text(meta.name),
|
||||
agentType: text(meta.agentType),
|
||||
description: text(meta.description),
|
||||
background: meta.requestShape === "background",
|
||||
stoppedByUser: meta.stoppedByUser === true,
|
||||
});
|
||||
} catch {
|
||||
// A damaged meta file leaves its subagent without history.
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (ids.size === 0) return { ids };
|
||||
if (agents.size === 0) return { agents };
|
||||
// The folder name encodes the project directory, and the encoding loses
|
||||
// characters. The session info reads it back from the transcript.
|
||||
const dir = (await getSessionInfo(sessionId).catch(() => undefined))?.cwd;
|
||||
return dir ? { ids, dir } : { ids };
|
||||
return dir ? { agents, dir } : { agents };
|
||||
}
|
||||
|
||||
/** 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* The agent that the Agent or Task call `launchId` launched, from the subagent
|
||||
* history of the session `sessionId`.
|
||||
*/
|
||||
export async function subagentLaunchedBy(
|
||||
sessionId: string,
|
||||
launchId: string,
|
||||
): Promise<string | undefined> {
|
||||
return (await subagentHistory(sessionId)).agents.get(launchId)?.agentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* The agent `agentId` and its ancestors, root first, from the subagent history
|
||||
* of the session `sessionId`. The runtime restores an agent from it when Claude
|
||||
* Code resumes one that ran before the session was loaded or resumed.
|
||||
*
|
||||
* The metadata file of an agent names the call that launched it, but not the
|
||||
* agent that made the call. So the parent of a launch is the session whose
|
||||
* transcript has the call, as in the replay: the session's own transcript, or
|
||||
* an agent's. The list is empty when the agent or one of its ancestors has no
|
||||
* metadata, or no transcript has its launch.
|
||||
*/
|
||||
export async function subagentLineage(
|
||||
sessionId: string,
|
||||
agentId: string,
|
||||
logger: { error(...args: unknown[]): void },
|
||||
): Promise<HistoricalSubagent[]> {
|
||||
const history = await subagentHistory(sessionId);
|
||||
const launchOf = new Map<string, { launchId: string; record: SubagentRecord }>();
|
||||
for (const [launchId, record] of history.agents) {
|
||||
launchOf.set(record.agentId, { launchId, record });
|
||||
}
|
||||
if (!launchOf.has(agentId)) return [];
|
||||
|
||||
// The SDK's readers return only the active chain, which starts after the
|
||||
// last compaction, so the launch calls come from the transcript files.
|
||||
const transcript = await findTranscript(sessionId);
|
||||
if (!transcript) return [];
|
||||
const rootLaunches = await readLaunchIdsFromTranscript(transcript, null, logger);
|
||||
const agentLaunches = new Map<string, Set<string>>();
|
||||
/** The agent whose transcript has the launch `launchId`, `null` for the root's. */
|
||||
const launcherOf = async (launchId: string): Promise<string | null | undefined> => {
|
||||
if (rootLaunches.has(launchId)) return null;
|
||||
for (const candidate of launchOf.keys()) {
|
||||
let launches = agentLaunches.get(candidate);
|
||||
if (!launches) {
|
||||
launches = await readLaunchIdsFromTranscript(
|
||||
path.join(subagentsDirectory(transcript, sessionId), `agent-${candidate}.jsonl`),
|
||||
candidate,
|
||||
logger,
|
||||
);
|
||||
agentLaunches.set(candidate, launches);
|
||||
}
|
||||
if (launches.has(launchId)) return candidate;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const lineage: HistoricalSubagent[] = [];
|
||||
let current: string | null = agentId;
|
||||
while (current !== null) {
|
||||
const launch = launchOf.get(current);
|
||||
// A cycle, or an ancestor without metadata.
|
||||
if (!launch || lineage.some((entry) => entry.agentId === current)) return [];
|
||||
const launcher = await launcherOf(launch.launchId);
|
||||
if (launcher === undefined) return [];
|
||||
lineage.unshift({
|
||||
agentId: current,
|
||||
launchId: launch.launchId,
|
||||
...(launcher !== null ? { parentAgentId: launcher } : {}),
|
||||
...(launch.record.name ? { name: launch.record.name } : {}),
|
||||
...(launch.record.agentType ? { agentType: launch.record.agentType } : {}),
|
||||
...(launch.record.description ? { description: launch.record.description } : {}),
|
||||
});
|
||||
current = launcher;
|
||||
}
|
||||
return lineage;
|
||||
}
|
||||
|
||||
/** The ids of the Agent and Task calls that launched subagents in `content`. */
|
||||
export function subagentLaunchIds(content: unknown): string[] {
|
||||
if (!Array.isArray(content)) return [];
|
||||
return content.flatMap((block) =>
|
||||
typeof block === "object" &&
|
||||
block !== null &&
|
||||
block.type === "tool_use" &&
|
||||
typeof block.id === "string" &&
|
||||
isNativeSubagentControlTool(block.name)
|
||||
? [block.id as string]
|
||||
: [],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The ids of the launch calls in the transcript `file`, every record of it,
|
||||
* including those before a compaction. `owner` is the agent the transcript
|
||||
* belongs to, null for the session's. The session's transcript can hold
|
||||
* sidechain records, which are an agent's, not the root's; an agent's
|
||||
* transcript keeps only the records of that agent. A file that cannot be read
|
||||
* has no launches, so the agent is not placed under the root.
|
||||
*/
|
||||
async function readLaunchIdsFromTranscript(
|
||||
file: string,
|
||||
owner: string | null,
|
||||
logger: { error(...args: unknown[]): void },
|
||||
): Promise<Set<string>> {
|
||||
const launches = new Set<string>();
|
||||
const stream = createReadStream(file, { encoding: "utf8" });
|
||||
try {
|
||||
for await (const line of readline.createInterface({ input: stream, crlfDelay: Infinity })) {
|
||||
const forkDirective = owner !== null && line.includes(FORK_BOILERPLATE_OPEN);
|
||||
if (!line.includes('"tool_use"') && !forkDirective) continue;
|
||||
let record: {
|
||||
type?: unknown;
|
||||
isSidechain?: unknown;
|
||||
agentId?: unknown;
|
||||
message?: { content?: unknown };
|
||||
} | null;
|
||||
try {
|
||||
record = JSON.parse(line);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
// Every record of a fork child's transcript before its directive message
|
||||
// is inherited context: a copy of the launching message, whose launch
|
||||
// calls are the parent's, not the fork's.
|
||||
if (forkDirective && record !== null && isForkDirectiveMessage(record)) {
|
||||
launches.clear();
|
||||
continue;
|
||||
}
|
||||
if (record?.type !== "assistant") continue;
|
||||
if (
|
||||
owner === null
|
||||
? record.isSidechain === true
|
||||
: record.agentId !== undefined && record.agentId !== owner
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
for (const id of subagentLaunchIds(record.message?.content)) launches.add(id);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to read the transcript ${file}:`, error);
|
||||
return new Set();
|
||||
} finally {
|
||||
stream.destroy();
|
||||
}
|
||||
return launches;
|
||||
}
|
||||
|
||||
/**
|
||||
* The transcript of the subagent `agentId` of a session, or an empty list
|
||||
* when it cannot be read. With the project directory, the SDK skips its
|
||||
* search of every project; a session opened from another directory falls
|
||||
* back to that search.
|
||||
*/
|
||||
export async function readSubagentMessages(
|
||||
sessionId: string,
|
||||
agentId: string,
|
||||
history: SubagentHistory,
|
||||
logger: { error(...args: unknown[]): void },
|
||||
): Promise<SessionMessage[]> {
|
||||
try {
|
||||
if (history.dir) {
|
||||
const messages = await getSubagentMessages(sessionId, agentId, { dir: history.dir });
|
||||
if (messages.length > 0) return stripForkPrefix(messages);
|
||||
}
|
||||
return stripForkPrefix(await getSubagentMessages(sessionId, agentId));
|
||||
} catch (error) {
|
||||
logger.error(`Failed to read the history of subagent ${agentId}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/** How Claude Code opens the directive message of a `subagent_type: "fork"` child. */
|
||||
const FORK_BOILERPLATE_OPEN = "<fork-boilerplate>";
|
||||
/** What follows the boilerplate, right before the prompt of the Agent call. */
|
||||
const FORK_DIRECTIVE_MARKER = "</fork-boilerplate>\nYour directive: ";
|
||||
|
||||
/** Whether `block` is the text block of a fork child's directive message. */
|
||||
function isForkDirectiveText(block: unknown): block is { type: "text"; text: string } {
|
||||
return (
|
||||
typeof block === "object" &&
|
||||
block !== null &&
|
||||
(block as { type?: unknown }).type === "text" &&
|
||||
typeof (block as { text?: unknown }).text === "string" &&
|
||||
(block as { text: string }).text.startsWith(FORK_BOILERPLATE_OPEN)
|
||||
);
|
||||
}
|
||||
|
||||
/** Whether the transcript record `message` is the directive message of a fork child. */
|
||||
function isForkDirectiveMessage(message: { type?: unknown; message?: unknown }): boolean {
|
||||
if (message.type !== "user") return false;
|
||||
const content = (message.message as { content?: unknown } | null | undefined)?.content;
|
||||
return Array.isArray(content) && content.some(isForkDirectiveText);
|
||||
}
|
||||
|
||||
/**
|
||||
* The transcript of a `subagent_type: "fork"` child without the context it
|
||||
* inherited. A fork transcript starts with a copy of the parent's launching
|
||||
* assistant message (its text, thinking and every tool_use, including the
|
||||
* Agent call itself), then a user message with a "Fork started — processing
|
||||
* in background" tool_result for each of those tool_uses and the text
|
||||
* `<fork-boilerplate>…</fork-boilerplate>\nYour directive: <prompt>`. A fork
|
||||
* launched from inside a subagent also gets a copy of that subagent's history
|
||||
* before them. None of it is the child's own work: replayed as is, the
|
||||
* parent's text and tool calls would show again in the child.
|
||||
*
|
||||
* This drops every record before the directive message, and reduces that
|
||||
* message to the prompt. Other transcripts are returned unchanged.
|
||||
*/
|
||||
export function stripForkPrefix(messages: SessionMessage[]): SessionMessage[] {
|
||||
const index = messages.findIndex(isForkDirectiveMessage);
|
||||
if (index === -1) return messages;
|
||||
const directive = messages[index];
|
||||
const content = (directive.message as { content: unknown[] }).content;
|
||||
const prompt = content.flatMap((block) => {
|
||||
if (!isForkDirectiveText(block)) return [];
|
||||
const start = block.text.indexOf(FORK_DIRECTIVE_MARKER);
|
||||
return start === -1
|
||||
? []
|
||||
: [{ type: "text", text: block.text.slice(start + FORK_DIRECTIVE_MARKER.length) }];
|
||||
});
|
||||
const rest = messages.slice(index + 1);
|
||||
if (prompt.length === 0) return rest;
|
||||
return [
|
||||
{ ...directive, message: { ...(directive.message as object), content: prompt } },
|
||||
...rest,
|
||||
];
|
||||
}
|
||||
|
||||
421
src/subagent-replay.ts
Normal file
421
src/subagent-replay.ts
Normal file
@@ -0,0 +1,421 @@
|
||||
/**
|
||||
* The replay of subagents to a client on the subagents RFD form.
|
||||
*
|
||||
* Claude Code keeps each subagent's history in its own transcript, and the
|
||||
* subagent's metadata file names the Agent or Task call that launched it (see
|
||||
* `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.
|
||||
*
|
||||
* 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
|
||||
* a foreground child's answer is its reply to the parent. A child's
|
||||
* successful SubagentHandback call is its message to the parent instead.
|
||||
* 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 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 { SessionMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||
import type { AcpSessionNotification, SubagentState } from "./acp-subagents.js";
|
||||
import {
|
||||
HANDBACK_TOOL,
|
||||
handbackMessage,
|
||||
isNativeSubagentControlTool,
|
||||
launchReply,
|
||||
linkedSessionMessages,
|
||||
nativeSubagentState,
|
||||
rfdEndState,
|
||||
rfdLabels,
|
||||
type WorkState,
|
||||
} from "./native-subagents.js";
|
||||
import { noticeUpdate } from "./session-notices.js";
|
||||
import type { SubagentHistory, SubagentRecord } from "./subagent-history.js";
|
||||
|
||||
type Publish = (notification: AcpSessionNotification) => Promise<void>;
|
||||
|
||||
/** A child that the replay shows: its launch has metadata. */
|
||||
export type ReplayChild = {
|
||||
/** The Agent or Task tool use that launched it. */
|
||||
launchId: string;
|
||||
/** Its session id: the SDK agent id, as live. */
|
||||
sessionId: string;
|
||||
record: SubagentRecord;
|
||||
/** The session that made the launch. */
|
||||
parentSessionId: string;
|
||||
/** The input of the launch call. */
|
||||
input: Record<string, unknown>;
|
||||
/** How the launch's tool result ended a foreground child's run. */
|
||||
launchEnd?: SubagentState;
|
||||
/** The state of the child's last persisted task notification. */
|
||||
notified?: SubagentState;
|
||||
announced: boolean;
|
||||
promptSent: boolean;
|
||||
/** Whether it handed a report to its parent with a SubagentHandback call. */
|
||||
handedBack?: boolean;
|
||||
};
|
||||
|
||||
type SendMessageCall = {
|
||||
toolUseId: string;
|
||||
to: string;
|
||||
message: string;
|
||||
senderSessionId: string;
|
||||
delivered: boolean;
|
||||
};
|
||||
|
||||
export class RfdSubagentReplay {
|
||||
/** The children by the id of the tool use that launched each. */
|
||||
private readonly children = new Map<string, ReplayChild>();
|
||||
private readonly byAgentId = new Map<string, ReplayChild>();
|
||||
private readonly sendMessages: SendMessageCall[] = [];
|
||||
/** Launches without metadata, which the replay leaves out. */
|
||||
private readonly omitted = new Set<string>();
|
||||
/** Children whose transcript could not be read: only their association is replayed. */
|
||||
private withoutHistory = 0;
|
||||
/** The SubagentHandback calls, with their report and whether their result failed. */
|
||||
private readonly handbacks = new Map<
|
||||
string,
|
||||
{ message: string; failed: boolean; sent: boolean }
|
||||
>();
|
||||
|
||||
constructor(
|
||||
private readonly history: SubagentHistory,
|
||||
private readonly publish: Publish,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Registers the launches, the launch results, and the SendMessage calls in
|
||||
* `messages`, the transcript of the session `ownerSessionId`.
|
||||
*/
|
||||
scan(messages: readonly SessionMessage[], ownerSessionId: string): void {
|
||||
for (const message of messages) {
|
||||
for (const block of contentBlocks(message)) {
|
||||
if (block.type === "tool_use" && typeof block.id === "string") {
|
||||
if (isNativeSubagentControlTool(block.name)) {
|
||||
this.registerLaunch(block.id, inputOf(block), ownerSessionId);
|
||||
} else if (block.name === HANDBACK_TOOL) {
|
||||
const message = handbackMessage(block.input);
|
||||
if (message) this.handbacks.set(block.id, { message, failed: false, sent: false });
|
||||
} else if (block.name === "SendMessage") {
|
||||
const input = inputOf(block);
|
||||
if (typeof input.to === "string" && typeof input.message === "string") {
|
||||
this.sendMessages.push({
|
||||
toolUseId: block.id,
|
||||
to: input.to,
|
||||
message: input.message,
|
||||
senderSessionId: ownerSessionId,
|
||||
delivered: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
|
||||
const handback = this.handbacks.get(block.tool_use_id);
|
||||
if (handback && block.is_error === true) handback.failed = true;
|
||||
const child = this.children.get(block.tool_use_id);
|
||||
// A background launch answers at once; its result is not the end.
|
||||
if (child && !child.record.background) {
|
||||
child.launchEnd = replaySubagentTerminalState(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The child that the tool use `toolUseId` launched, when the replay shows it. */
|
||||
childOfLaunch(toolUseId: string): ReplayChild | undefined {
|
||||
return this.children.get(toolUseId);
|
||||
}
|
||||
|
||||
/** Announces `child` on its parent, as live: running, with the parent's labels. */
|
||||
async announce(child: ReplayChild): 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,
|
||||
);
|
||||
await this.publish({
|
||||
sessionId: child.parentSessionId,
|
||||
update: {
|
||||
sessionUpdate: "subagent_update",
|
||||
sessionId: child.sessionId,
|
||||
...(labels.title ? { title: labels.title } : {}),
|
||||
...(labels.description ? { description: labels.description } : {}),
|
||||
state: { state: "running" },
|
||||
},
|
||||
});
|
||||
child.announced = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rest of a child's user message to replay, or `null` when nothing is
|
||||
* left, after its text went out as the child's incoming `session_message`.
|
||||
*
|
||||
* A child session has no user, so the text of its user messages is never a
|
||||
* user message. The first text is the prompt of the launch. A message that
|
||||
* a SendMessage call delivered (`origin.kind: "coordinator"`) is another
|
||||
* incoming message. Any other text is harness text, such as "[Request
|
||||
* interrupted by user for tool use]", which the live child session does not
|
||||
* show. Tool results stay.
|
||||
*/
|
||||
async childContent(
|
||||
child: ReplayChild,
|
||||
message: SessionMessage,
|
||||
content: unknown,
|
||||
): Promise<unknown> {
|
||||
const text = textOf(content);
|
||||
if (text.trim()) {
|
||||
if (isDelivery(message)) await this.sendDelivery(child, message, text);
|
||||
else if (!child.promptSent) await this.sendPrompt(child, text);
|
||||
}
|
||||
if (!Array.isArray(content)) return null;
|
||||
const rest = content.filter(
|
||||
(block) => !(typeof block === "object" && block !== null && block.type === "text"),
|
||||
);
|
||||
return rest.length > 0 ? rest : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the replay of `child`'s history. `messages` is its transcript: when
|
||||
* it is empty, the replay has only the child's association, and the prompt
|
||||
* comes from the launch call.
|
||||
*/
|
||||
async childReplayed(child: ReplayChild, messages: readonly SessionMessage[]): Promise<void> {
|
||||
if (messages.length === 0) this.withoutHistory++;
|
||||
if (!child.promptSent && typeof child.input.prompt === "string") {
|
||||
await this.sendPrompt(child, child.input.prompt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
async toolCallUpdate(
|
||||
update: AcpSessionNotification["update"],
|
||||
owner: ReplayChild | undefined,
|
||||
): Promise<boolean> {
|
||||
if (update.sessionUpdate !== "tool_call" && update.sessionUpdate !== "tool_call_update") {
|
||||
return false;
|
||||
}
|
||||
const handback = owner ? this.handbacks.get(update.toolCallId) : undefined;
|
||||
if (owner && handback && !handback.failed) {
|
||||
if (!handback.sent) {
|
||||
handback.sent = true;
|
||||
owner.handedBack = true;
|
||||
for (const view of linkedSessionMessages(
|
||||
update.toolCallId,
|
||||
owner.sessionId,
|
||||
owner.parentSessionId,
|
||||
handback.message,
|
||||
)) {
|
||||
await this.publish(view);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const child = this.children.get(update.toolCallId);
|
||||
if (!child) return false;
|
||||
if (
|
||||
update.status === "completed" &&
|
||||
child.announced &&
|
||||
!child.record.background &&
|
||||
!child.handedBack
|
||||
) {
|
||||
const reply = launchReply(child.launchId, child.sessionId, child.parentSessionId, update);
|
||||
if (reply) await this.publish(reply);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a persisted task notification. Returns whether it was a child's:
|
||||
* its task id is the agent id.
|
||||
*/
|
||||
taskNotification(taskId: string, status: string): boolean {
|
||||
const child = this.byAgentId.get(taskId);
|
||||
if (!child) return false;
|
||||
child.notified = nativeSubagentState(status) ?? child.notified;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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 },
|
||||
});
|
||||
}
|
||||
const missing = this.omitted.size + this.withoutHistory;
|
||||
if (missing > 0 && supportsNotices) {
|
||||
await this.publish({
|
||||
sessionId: rootSessionId,
|
||||
update: noticeUpdate({
|
||||
severity: "info",
|
||||
title: "Some subagent history is not available",
|
||||
description:
|
||||
missing === 1
|
||||
? "The history of 1 subagent of this session could not be restored."
|
||||
: `The history of ${missing} subagents of this session could not be restored.`,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private registerLaunch(
|
||||
toolUseId: string,
|
||||
input: Record<string, unknown>,
|
||||
parentSessionId: string,
|
||||
): void {
|
||||
if (this.children.has(toolUseId)) return;
|
||||
const record = this.history.agents.get(toolUseId);
|
||||
if (!record) {
|
||||
this.omitted.add(toolUseId);
|
||||
return;
|
||||
}
|
||||
const child: ReplayChild = {
|
||||
launchId: toolUseId,
|
||||
sessionId: record.agentId,
|
||||
record,
|
||||
parentSessionId,
|
||||
input,
|
||||
announced: false,
|
||||
promptSent: false,
|
||||
};
|
||||
this.children.set(toolUseId, child);
|
||||
this.byAgentId.set(record.agentId, child);
|
||||
}
|
||||
|
||||
/** The prompt of the launch, with the launch's id, to the parent and the child, as live. */
|
||||
private async sendPrompt(child: ReplayChild, text: string): Promise<void> {
|
||||
child.promptSent = true;
|
||||
for (const view of linkedSessionMessages(
|
||||
child.launchId,
|
||||
child.parentSessionId,
|
||||
child.sessionId,
|
||||
text,
|
||||
)) {
|
||||
await this.publish(view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A message that a SendMessage call delivered. Claude Code frames it for the
|
||||
* child's model ("The coordinator sent a message while you were working:",
|
||||
* then the message, then an instruction), so the call it came from is the
|
||||
* 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.
|
||||
*/
|
||||
private async sendDelivery(
|
||||
child: ReplayChild,
|
||||
message: SessionMessage,
|
||||
text: string,
|
||||
): Promise<void> {
|
||||
const names = [child.sessionId, child.record.name, child.input.name];
|
||||
const call = this.sendMessages.find(
|
||||
(candidate) =>
|
||||
!candidate.delivered &&
|
||||
names.includes(candidate.to) &&
|
||||
text.includes(candidate.message.trim()),
|
||||
);
|
||||
if (call) {
|
||||
call.delivered = true;
|
||||
await this.sendMessage(child, call.toolUseId, call.message, call.senderSessionId);
|
||||
} else {
|
||||
await this.sendMessage(child, message.uuid, text, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendMessage(
|
||||
child: ReplayChild,
|
||||
messageId: string,
|
||||
text: string,
|
||||
sender: string | undefined,
|
||||
): Promise<void> {
|
||||
await this.publish({
|
||||
sessionId: child.sessionId,
|
||||
update: {
|
||||
sessionUpdate: "session_message",
|
||||
messageId,
|
||||
...(sender ? { senderSessionId: sender } : {}),
|
||||
recipientSessionId: child.sessionId,
|
||||
content: [{ type: "text", text }],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export function replaySubagentTerminalState(
|
||||
block: Record<string, unknown>,
|
||||
): "completed" | "failed" | "cancelled" {
|
||||
if (block.is_error !== true) return "completed";
|
||||
const text = replayContentText(block.content).toLowerCase();
|
||||
return /\b(?:cancelled|canceled|interrupted|stopped|killed)\b/.test(text)
|
||||
? "cancelled"
|
||||
: "failed";
|
||||
}
|
||||
|
||||
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 "";
|
||||
seen.add(value);
|
||||
if (Array.isArray(value)) return value.map((item) => replayContentText(item, seen)).join(" ");
|
||||
const record = value as Record<string, unknown>;
|
||||
return [record.text, record.content, record.message]
|
||||
.map((item) => replayContentText(item, seen))
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function contentBlocks(message: SessionMessage): Record<string, unknown>[] {
|
||||
const content = (message as { message?: { content?: unknown } }).message?.content;
|
||||
return Array.isArray(content)
|
||||
? content.filter(
|
||||
(block): block is Record<string, unknown> => typeof block === "object" && block !== null,
|
||||
)
|
||||
: [];
|
||||
}
|
||||
|
||||
function inputOf(block: Record<string, unknown>): Record<string, unknown> {
|
||||
return typeof block.input === "object" && block.input !== null
|
||||
? (block.input as Record<string, unknown>)
|
||||
: {};
|
||||
}
|
||||
|
||||
/** The text of user message content: a string, or its text blocks. */
|
||||
function textOf(content: unknown): string {
|
||||
if (typeof content === "string") return content;
|
||||
if (!Array.isArray(content)) return "";
|
||||
return content
|
||||
.flatMap((block) =>
|
||||
typeof block === "object" && block !== null && block.type === "text" ? [block.text] : [],
|
||||
)
|
||||
.filter((text): text is string => typeof text === "string")
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
/** 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";
|
||||
}
|
||||
75
src/task-notifications.ts
Normal file
75
src/task-notifications.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* The `<task-notification>` inputs that Claude Code writes to a transcript
|
||||
* when a background task stops. The replay of a session (`acp-agent.ts`) and
|
||||
* the session index (`session-index/background-tasks.ts`) read them here, so
|
||||
* both read the same fields.
|
||||
*/
|
||||
|
||||
export const TASK_NOTIFICATION_TAG = "task-notification";
|
||||
|
||||
/** The text between the first `<tag>` and the next `</tag>` after it. */
|
||||
export function markerTagText(text: string, tag: string): string | undefined {
|
||||
const open = `<${tag}>`;
|
||||
const close = `</${tag}>`;
|
||||
const start = text.indexOf(open);
|
||||
if (start === -1) return undefined;
|
||||
const end = text.indexOf(close, start + open.length);
|
||||
if (end === -1) return undefined;
|
||||
return text.slice(start + open.length, end);
|
||||
}
|
||||
|
||||
/** The fields of a persisted `<task-notification>`, in the shape of the SDK
|
||||
* `task_notification` frame that reported the same stop live. */
|
||||
export type PersistedTaskNotification = {
|
||||
task_id: string;
|
||||
status: string;
|
||||
tool_use_id?: string;
|
||||
summary?: string;
|
||||
output_file?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns each `<task-notification>` in user-message content (a string or its
|
||||
* text blocks). The `<result>` body is model context that the live frame does
|
||||
* not carry, so the scan stops at it and a tag quoted in it cannot match.
|
||||
*
|
||||
* Only a notification with a `<task-id>` and a `<status>` is returned. A
|
||||
* notification without a status comes while its task still runs: the
|
||||
* check-in of a quiet shell ("is still running after …") or a `Monitor`
|
||||
* event.
|
||||
*/
|
||||
export function taskNotificationsOf(content: unknown): PersistedTaskNotification[] {
|
||||
const texts =
|
||||
typeof content === "string"
|
||||
? [content]
|
||||
: Array.isArray(content)
|
||||
? content.flatMap((block) =>
|
||||
block?.type === "text" && typeof block.text === "string" ? [block.text as string] : [],
|
||||
)
|
||||
: [];
|
||||
const open = `<${TASK_NOTIFICATION_TAG}>`;
|
||||
const close = `</${TASK_NOTIFICATION_TAG}>`;
|
||||
const notifications: PersistedTaskNotification[] = [];
|
||||
for (const text of texts) {
|
||||
let start = text.indexOf(open);
|
||||
while (start !== -1) {
|
||||
const end = text.indexOf(close, start + open.length);
|
||||
if (end === -1) break;
|
||||
const body = text.slice(start + open.length, end).split("<result>", 1)[0];
|
||||
const field = (tag: string) => markerTagText(body, tag)?.trim() || undefined;
|
||||
const taskId = field("task-id");
|
||||
const status = field("status");
|
||||
if (taskId && status) {
|
||||
notifications.push({
|
||||
task_id: taskId,
|
||||
status,
|
||||
tool_use_id: field("tool-use-id"),
|
||||
summary: field("summary"),
|
||||
output_file: field("output-file"),
|
||||
});
|
||||
}
|
||||
start = text.indexOf(open, end + close.length);
|
||||
}
|
||||
}
|
||||
return notifications;
|
||||
}
|
||||
@@ -515,7 +515,7 @@ describe("ClaudeAcpAgent settings", () => {
|
||||
_meta: { disableBuiltInTools: true },
|
||||
});
|
||||
await agent.setSessionMode({ sessionId, modeId: "default" });
|
||||
const { RequestError } = await import("@agentclientprotocol/sdk");
|
||||
const { RequestError } = await import("@agentclientprotocol/sdk/unstable");
|
||||
const createSession = (agent as any).createSession.bind(agent);
|
||||
const creations: object[] = [];
|
||||
(agent as any).createSession = async (params: object, options: { resume?: string }) => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"mcp__docs__search"},"is_mcp_tool_call":true},"content":[],"kind":"other","name":"mcp__docs__search","sessionUpdate":"tool_call","status":"pending","title":"mcp__docs__search","toolCallId":"toolu_mcp"}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"mcp__docs__search"},"is_mcp_tool_call":true,"jetbrains":{"air":{"mcp":{"server":"docs","tool":"search"},"version":1}}},"content":[],"kind":"other","name":"mcp__docs__search","sessionUpdate":"tool_call","status":"pending","title":"mcp__docs__search","toolCallId":"toolu_mcp"}}}
|
||||
{"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":{"rawInput":{"q":"acp"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_mcp"}}}
|
||||
{"kind":"requestPermission","payload":{"_meta":{"jetbrains":{"air":{"permission":{"title":"mcp__docs__search","version":1},"version":1}}},"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"<session>","toolCall":{"_meta":{"claudeCode":{"mcpServer":{"name":"docs","source":"project"}}},"rawInput":{"q":"acp"},"title":"mcp__docs__search","toolCallId":"toolu_mcp"}}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{"kind":"initialize","payload":{"_meta":{"jetbrains":{"air":{"capabilities":["sessionFailure","agentFileChangeReport","nativeSubagentSessions","asyncTasks","customInstructions","recommendedValue","diffPatch","planFile"],"goal":{"actions":["set","clear"],"controlMethod":"_session/goal","version":1},"version":1}},"steering":{"supported":true}},"agentCapabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"auth":{"logout":{}},"loadSession":true,"mcpCapabilities":{"http":true,"sse":true},"promptCapabilities":{"embeddedContext":true,"image":true},"providers":{},"sessionCapabilities":{"additionalDirectories":{},"close":{},"delete":{},"fork":{},"list":{},"resume":{},"subagents":{}}},"agentInfo":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"authMethods":[],"protocolVersion":1}}
|
||||
{"kind":"initialize","payload":{"_meta":{"jetbrains":{"air":{"capabilities":["sessionFailure","agentFileChangeReport","nativeSubagentSessions","asyncTasks","customInstructions","recommendedValue","diffPatch","planFile","backgroundSubagents"],"goal":{"actions":["set","clear"],"controlMethod":"_session/goal","version":1},"version":1}},"steering":{"supported":true}},"agentCapabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"auth":{"logout":{}},"loadSession":true,"mcpCapabilities":{"http":true,"sse":true},"promptCapabilities":{"embeddedContext":true,"image":true},"providers":{},"sessionCapabilities":{"additionalDirectories":{},"close":{},"delete":{},"fork":{},"list":{},"resume":{},"subagents":{},"systemPrompt":{"mode":{"override":{}}}}},"agentInfo":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"authMethods":[],"protocolVersion":1}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"content":{"text":"Fix the bug","type":"text"},"messageId":"00000000-0000-4000-8000-00000000c001","sessionUpdate":"user_message_chunk"}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"content":{"text":"Check the file.","type":"text"},"messageId":"msg_r1","sessionUpdate":"agent_thought_chunk"}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"content":{"text":"I will edit it.","type":"text"},"messageId":"msg_r1","sessionUpdate":"agent_message_chunk"}}}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{"kind":"initialize","payload":{"_meta":{"jetbrains":{"air":{"capabilities":["sessionFailure","agentFileChangeReport","nativeSubagentSessions","asyncTasks","customInstructions","recommendedValue","diffPatch","planFile"],"goal":{"actions":["set","clear"],"controlMethod":"_session/goal","version":1},"version":1}},"steering":{"supported":true}},"agentCapabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"auth":{"logout":{}},"loadSession":true,"mcpCapabilities":{"http":true,"sse":true},"promptCapabilities":{"embeddedContext":true,"image":true},"providers":{},"sessionCapabilities":{"additionalDirectories":{},"close":{},"delete":{},"fork":{},"list":{},"resume":{},"subagents":{}}},"agentInfo":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"authMethods":[],"protocolVersion":1}}
|
||||
{"kind":"initialize","payload":{"_meta":{"jetbrains":{"air":{"capabilities":["sessionFailure","agentFileChangeReport","nativeSubagentSessions","asyncTasks","customInstructions","recommendedValue","diffPatch","planFile","backgroundSubagents"],"goal":{"actions":["set","clear"],"controlMethod":"_session/goal","version":1},"version":1}},"steering":{"supported":true}},"agentCapabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"auth":{"logout":{}},"loadSession":true,"mcpCapabilities":{"http":true,"sse":true},"promptCapabilities":{"embeddedContext":true,"image":true},"providers":{},"sessionCapabilities":{"additionalDirectories":{},"close":{},"delete":{},"fork":{},"list":{},"resume":{},"subagents":{},"systemPrompt":{"mode":{"override":{}}}}},"agentInfo":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"authMethods":[],"protocolVersion":1}}
|
||||
{"kind":"newSession","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>","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"}}}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{"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":{"_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":{"_meta":{"claudeCode":{"toolName":"Agent","toolResponse":{"isAsync":true,"status":"async_launched"}}},"sessionUpdate":"tool_call_update","toolCallId":"toolu_agent"}}}
|
||||
{"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"}},"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"}}}
|
||||
|
||||
@@ -18,10 +18,9 @@
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"agent_1","update":{"content":[{"newText":"b","oldText":"a","path":"<cwd>/x.ts","type":"diff"}],"rawInput":{"file_path":"<cwd>/x.ts"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_sub_edit"}}}
|
||||
{"kind":"requestPermission","payload":{"_meta":{"jetbrains":{"air":{"permission":{"title":"Edit x.ts","version":1},"version":1}}},"options":[{"kind":"allow_once","name":"Yes","optionId":"allow-once"},{"kind":"reject_once","name":"No","optionId":"reject"}],"sessionId":"agent_1","toolCall":{"rawInput":{"file_path":"<cwd>/x.ts"},"title":"Edit x.ts","toolCallId":"toolu_sub_edit"}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"agent_1","update":{"rawOutput":"The file has been updated.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_sub_edit"}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"locations":[{"line":1,"path":"<cwd>/x.ts"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_sub_edit"}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"agent_1","update":{"locations":[{"line":1,"path":"<cwd>/x.ts"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_sub_edit"}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":{"text":"Foun","type":"text"},"messageId":"msg_sub_2","sessionUpdate":"agent_message_chunk"}}}
|
||||
{"kind":"sessionUpdate","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":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Task","toolResponse":{"status":"completed"}}},"sessionUpdate":"tool_call_update","toolCallId":"toolu_task"}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"sessionUpdate":"subagent_state_update","state":"completed","subagentSessionId":"agent_1"}}}
|
||||
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"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}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"AskUserQuestion"}},"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":{"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":{"_meta":{"claudeCode":{"toolName":"AskUserQuestion"}},"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":{"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":"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":{"_meta":{"claudeCode":{"toolName":"AskUserQuestion"}},"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":{"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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}}}}
|
||||
|
||||
@@ -1,18 +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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"content":[{"terminalId":"toolu_bg","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"command":"sleep 100","sessionUpdate":"terminal_update","terminalId":"toolu_bg"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Bash"}},"rawInput":{"command":"sleep 100"},"sessionUpdate":"tool_call_update","title":"sleep 100","toolCallId":"toolu_bg"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Bash"}},"rawInput":{"command":"sleep 100","description":"Wait"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_bg"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"command":"sleep 100"},"sessionUpdate":"tool_call_update","title":"sleep 100","toolCallId":"toolu_bg"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"command":"sleep 100","description":"Wait"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_bg"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Bash"}},"rawInput":{"command":"sleep 100","description":"Wait","run_in_background":true},"sessionUpdate":"tool_call_update","toolCallId":"toolu_bg"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"command":"sleep 100","description":"Wait","run_in_background":true},"sessionUpdate":"tool_call_update","toolCallId":"toolu_bg"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"output":{"data":"Q29tbWFuZCBydW5uaW5nIGluIGJhY2tncm91bmQgd2l0aCBJRDogYmFzaF8xLiBPdXRwdXQgaXMgYmVpbmcgd3JpdHRlbiB0bzogL3RtcC90YXNrcy9iYXNoXzEub3V0cHV0LiBZb3Ugd2lsbCBiZSBub3RpZmllZCB3aGVuIGl0IGNvbXBsZXRlcy4="},"sessionUpdate":"terminal_update","terminalId":"toolu_bg"}}}
|
||||
{"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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"content":[{"terminalId":"toolu_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"command":"false","sessionUpdate":"terminal_update","terminalId":"toolu_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Bash"}},"rawInput":{"command":"false"},"sessionUpdate":"tool_call_update","title":"false","toolCallId":"toolu_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"command":"false"},"sessionUpdate":"tool_call_update","title":"false","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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"rawInput":{"command":"false","description":"Fail"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"command":"false","description":"Fail"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"output":{"data":"RXhpdCBjb2RlIDEKYm9vbQ=="},"sessionUpdate":"terminal_update","terminalId":"toolu_bash"}}}
|
||||
{"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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"sessionUpdate":"tool_call_update","status":"failed","toolCallId":"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}}}}
|
||||
|
||||
@@ -1,22 +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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"content":[{"terminalId":"toolu_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"command":"ls -la","sessionUpdate":"terminal_update","terminalId":"toolu_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Bash"}},"rawInput":{"command":"ls -la"},"sessionUpdate":"tool_call_update","title":"ls -la","toolCallId":"toolu_bash"}}}
|
||||
{"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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"rawInput":{"command":"ls -la","description":"List files"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_bash"}}}
|
||||
{"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":{"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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Bash","toolResponse":{"interrupted":false,"isImage":false,"stderr":"","stdout":"a.ts\nb.ts"}}},"sessionUpdate":"tool_call_update","toolCallId":"toolu_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"Do","type":"text"},"messageId":"msg_done","sessionUpdate":"agent_message_chunk"}}}
|
||||
{"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}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"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"}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"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"}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Edit"}},"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":{"_meta":{"claudeCode":{"toolName":"Edit"}},"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Edit"}},"rawInput":{"file_path":"<cwd>/src/app.ts","old_string":"value = 1"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
|
||||
{"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"file_path":"<cwd>/src/app.ts","old_string":"value = 1"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Edit"}},"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":{"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":{"_meta":{"claudeCode":{"toolName":"Edit"}},"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":{"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}}}}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Edit"}},"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":{"_meta":{"claudeCode":{"toolName":"Edit"}},"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Edit"}},"rawInput":{"file_path":"<cwd>/src/app.ts","old_string":"const value = 1;"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
|
||||
{"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"file_path":"<cwd>/src/app.ts","old_string":"const value = 1;"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Edit"}},"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":{"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":{"_meta":{"claudeCode":{"toolName":"Edit"}},"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":{"_meta":{"claudeCode":{"toolName":"Edit","toolResponse":{"filePath":"<cwd>/src/app.ts","newString":"const value = 2;","oldString":"const value = 1;","originalFile":"line 1\nconst value = 1;\nline 3\n","replaceAll":false,"structuredPatch":[{"lines":[" line 1","-const value = 1;","+const value = 2;"," line 3"],"newLines":3,"newStart":1,"oldLines":3,"oldStart":1}],"userModified":false}}},"locations":[{"line":1,"path":"<cwd>/src/app.ts"}],"sessionUpdate":"tool_call_update","toolCallId":"toolu_edit"}}}
|
||||
{"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}}}}
|
||||
|
||||
@@ -1,16 +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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"ExitPlanMode"}},"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":{"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":{"_meta":{"claudeCode":{"toolName":"ExitPlanMode"}},"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":{"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":{"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":{"_meta":{"claudeCode":{"toolName":"ExitPlanMode"}},"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":{"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"ExitPlanMode"}},"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":{"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":{"_meta":{"claudeCode":{"toolName":"ExitPlanMode"}},"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":{"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":{"_meta":{"claudeCode":{"toolName":"ExitPlanMode"}},"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":{"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"/goal Ship the feature","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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}}}}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Grep"}},"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":{"_meta":{"claudeCode":{"toolName":"Grep"}},"rawInput":{"pattern":"value"},"sessionUpdate":"tool_call_update","title":"grep \"value\"","toolCallId":"toolu_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Grep"}},"rawInput":{"path":"src","pattern":"value"},"sessionUpdate":"tool_call_update","title":"grep \"value\" src","toolCallId":"toolu_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Grep"}},"rawInput":{"output_mode":"content","path":"src","pattern":"value"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_grep"}}}
|
||||
{"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"path":"src","pattern":"value"},"sessionUpdate":"tool_call_update","title":"grep \"value\" src","toolCallId":"toolu_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"output_mode":"content","path":"src","pattern":"value"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_grep"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Grep"}},"rawInput":{"-n":true,"output_mode":"content","path":"src","pattern":"value"},"sessionUpdate":"tool_call_update","title":"grep -n \"value\" src","toolCallId":"toolu_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Grep"}},"content":[{"content":{"text":"src/app.ts:2:const value = 1;","type":"text"},"type":"content"}],"rawOutput":"src/app.ts:2:const value = 1;","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Glob"}},"content":[],"kind":"search","locations":[],"name":"Glob","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Find","toolCallId":"toolu_glob"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"-n":true,"output_mode":"content","path":"src","pattern":"value"},"sessionUpdate":"tool_call_update","title":"grep -n \"value\" src","toolCallId":"toolu_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"src/app.ts:2:const value = 1;","type":"text"},"type":"content"}],"rawOutput":"src/app.ts:2:const value = 1;","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"kind":"search","locations":[],"name":"Glob","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Find","toolCallId":"toolu_glob"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Glob"}},"rawInput":{"pattern":"**/*.ts"},"sessionUpdate":"tool_call_update","title":"Find `**/*.ts`","toolCallId":"toolu_glob"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Glob"}},"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":{"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}}}}
|
||||
|
||||
@@ -1,16 +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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
|
||||
{"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":{"claudeCode":{"toolName":"mcp__docs__search"}},"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":{"_meta":{"claudeCode":{"toolName":"mcp__docs__search"}},"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"},"toolName":"mcp__docs__search"}},"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":{"_meta":{"claudeCode":{"toolName":"mcp__docs__search"}},"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":{"claudeCode":{"toolName":"mcp__docs__search","toolResponse":[{"text":"Found 2 documents.","type":"text"}]}},"sessionUpdate":"tool_call_update","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":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":{"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"memory_recall","toolResponse":{"mode":"select"}}},"kind":"read","locations":[{"path":"/memory/a.md"},{"path":"/memory/b.md"}],"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":{"toolName":"memory_recall","toolResponse":{"mode":"synthesize"}}},"content":[{"content":{"text":"The user prefers tabs.","type":"text"},"type":"content"}],"kind":"read","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":{"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}}}}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"SandboxNetworkAccess"}},"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":"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"}}}
|
||||
|
||||
@@ -1,18 +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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"NotebookEdit"}},"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":{"_meta":{"claudeCode":{"toolName":"NotebookEdit"}},"rawInput":{"notebook_path":"<cwd>/n.ipynb"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_nb"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"NotebookEdit"}},"rawInput":{"cell_id":"cell-1","notebook_path":"<cwd>/n.ipynb"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_nb"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"NotebookEdit"}},"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":{"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"cell_id":"cell-1","notebook_path":"<cwd>/n.ipynb"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_nb"}}}
|
||||
{"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":{"_meta":{"claudeCode":{"toolName":"NotebookEdit"}},"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":{"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":{"_meta":{"claudeCode":{"toolName":"NotebookEdit"}},"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":{"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"content":[{"terminalId":"toolu_denied","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"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"}}}
|
||||
{"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":{"command":"rm -rf /","sessionUpdate":"terminal_update","terminalId":"toolu_denied"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Bash"}},"rawInput":{"command":"rm -rf /"},"sessionUpdate":"tool_call_update","title":"rm -rf /","toolCallId":"toolu_denied"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Bash","toolResponse":{"decisionReason":"Denied by rule Bash(rm:*)","decisionReasonType":"rule","message":"Permission to use Bash has been denied."}}},"content":[{"content":{"text":"Permission denied: Denied by rule Bash(rm:*)","type":"text"},"type":"content"}],"sessionUpdate":"tool_call_update","status":"failed","toolCallId":"toolu_denied"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"rawInput":{"command":"rm -rf /"},"sessionUpdate":"tool_call_update","title":"rm -rf /","toolCallId":"toolu_denied"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolResponse":{"decisionReasonType":"rule","message":"Permission to use Bash has been denied."}}},"content":[{"content":{"text":"Permission denied: Denied by rule Bash(rm:*)","type":"text"},"type":"content"}],"sessionUpdate":"tool_call_update","status":"failed","toolCallId":"toolu_denied"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"output":{"data":"UGVybWlzc2lvbiB0byB1c2UgQmFzaCBoYXMgYmVlbiBkZW5pZWQu"},"sessionUpdate":"terminal_update","terminalId":"toolu_denied"}}}
|
||||
{"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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"content":[{"terminalId":"toolu_denied","type":"terminal"}],"sessionUpdate":"tool_call_update","toolCallId":"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}}}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_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"}}}
|
||||
|
||||
11
src/tests/acp-scenarios/__snapshots__/v2/read.jsonl
generated
11
src/tests/acp-scenarios/__snapshots__/v2/read.jsonl
generated
@@ -1,13 +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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Read"}},"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":{"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":{"_meta":{"claudeCode":{"toolName":"Read"}},"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":{"_meta":{"claudeCode":{"toolName":"Read"}},"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":{"claudeCode":{"toolName":"Read","toolResponse":{"file":{"content":"const a = 1;\n","filePath":"<cwd>/a.ts","numLines":1,"startLine":1,"totalLines":1},"type":"text"}}},"sessionUpdate":"tool_call_update","toolCallId":"toolu_read"}}}
|
||||
{"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}}}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"messageId":"00000000-0000-4000-8000-00000000c001","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":{"text":"Fix the bug","type":"text"},"messageId":"00000000-0000-4000-8000-00000000c001","sessionUpdate":"user_message_chunk"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[],"messageId":"msg_r1","sessionUpdate":"agent_thought"}}}
|
||||
@@ -6,14 +6,16 @@
|
||||
{"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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"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":{"_meta":{"claudeCode":{"toolName":"Edit"}},"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":{"_meta":{"claudeCode":{"toolName":"Task"}},"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":{"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":{"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":{"_meta":{"claudeCode":{"toolName":"Bash"}},"content":[{"terminalId":"toolu_r_bash","type":"terminal"}],"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_r_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Edit"}},"rawOutput":"The file <cwd>/src/app.ts has been updated successfully.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_r_edit"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Task"}},"content":[{"content":{"text":"Report.","type":"text"},"type":"content"}],"rawOutput":"Report.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_r_task"}}}
|
||||
{"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"}}}
|
||||
{"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_r_edit"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"Report.","type":"text"},"type":"content"}],"rawOutput":"Report.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_r_task"}}}
|
||||
{"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"}]}}
|
||||
|
||||
@@ -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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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>"}}
|
||||
|
||||
11
src/tests/acp-scenarios/__snapshots__/v2/skill.jsonl
generated
11
src/tests/acp-scenarios/__snapshots__/v2/skill.jsonl
generated
@@ -1,13 +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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Skill"}},"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":{"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":{"_meta":{"claudeCode":{"toolName":"Skill"}},"rawInput":{"skill":"commits"},"sessionUpdate":"tool_call_update","title":"Load skill: commits","toolCallId":"toolu_skill"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Skill"}},"rawOutput":"Launching skill: commits","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_skill"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Skill","toolResponse":{"commandName":"commits","success":true}}},"sessionUpdate":"tool_call_update","toolCallId":"toolu_skill"}}}
|
||||
{"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}}}}
|
||||
|
||||
@@ -1,18 +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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"content":[],"kind":"think","name":"Agent","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Task","toolCallId":"toolu_agent"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"rawInput":{"description":"Background review"},"sessionUpdate":"tool_call_update","title":"Background review","toolCallId":"toolu_agent"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"rawInput":{"description":"Background review","prompt":"Review the diff"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_agent"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"rawInput":{"description":"Background review","prompt":"Review the diff","subagent_type":"general-purpose"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_agent"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Agent"}},"content":[{"content":{"text":"Review the diff","type":"text"},"type":"content"}],"rawInput":{"description":"Background review","prompt":"Review the diff","run_in_background":true,"subagent_type":"general-purpose"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_agent"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"content":[{"content":{"text":"Async agent launched successfully.","type":"text"},"type":"content"}],"rawOutput":"Async agent launched successfully.\nagentId: a2 (use SendMessage to continue)","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_agent"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent","toolResponse":{"agentId":"a2","description":"Background review","isAsync":true,"outputFile":"/tmp/tasks/a2.output","prompt":"Review the diff","status":"async_launched"}}},"sessionUpdate":"tool_call_update","toolCallId":"toolu_agent"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","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":"<session>","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":{"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":{"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"}},"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":{"_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}}}}
|
||||
|
||||
@@ -1,18 +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":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"content":[],"kind":"think","name":"Agent","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Task","toolCallId":"toolu_late"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"rawInput":{"description":"Check the logs"},"sessionUpdate":"tool_call_update","title":"Check the logs","toolCallId":"toolu_late"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"rawInput":{"description":"Check the logs","prompt":"Check logs"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_late"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Agent"}},"content":[{"content":{"text":"Check logs","type":"text"},"type":"content"}],"rawInput":{"description":"Check the logs","prompt":"Check logs","subagent_type":"general-purpose"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_late"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_late","toolName":"Read"}},"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":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_late","toolName":"Read"}},"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":{"_meta":{"claudeCode":{"toolName":"Agent"}},"content":[{"content":{"text":"The logs are clean.","type":"text"},"type":"content"}],"rawOutput":[{"text":"The logs are clean.","type":"text"}],"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_late"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Read","toolResponse":{"elapsedTimeSeconds":4}}},"sessionUpdate":"tool_call_update","status":"in_progress","toolCallId":"toolu_late_read"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_late","toolName":"Read"}},"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":{"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":{"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"}},"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":"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}}}}
|
||||
|
||||
@@ -1,31 +1,36 @@
|
||||
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"kind":"response","method":"session/prompt","payload":{"messageId":"<id-1>"}}
|
||||
{"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":{"claudeCode":{"toolName":"Agent"}},"content":[],"kind":"think","name":"Agent","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Task","toolCallId":"toolu_native"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"rawInput":{"description":"Fix the build"},"sessionUpdate":"tool_call_update","title":"Fix the build","toolCallId":"toolu_native"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"rawInput":{"description":"Fix the build","prompt":"Make the build pass"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_native"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Agent"}},"content":[{"content":{"text":"Make the build pass","type":"text"},"type":"content"}],"rawInput":{"description":"Fix the build","prompt":"Make the build pass","subagent_type":"general-purpose"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_native"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","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":"<session>","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":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Grep"}},"content":[],"kind":"search","name":"Grep","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"grep","toolCallId":"toolu_native_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Grep"}},"rawInput":{"pattern":"TODO"},"sessionUpdate":"tool_call_update","title":"grep \"TODO\"","toolCallId":"toolu_native_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Grep"}},"rawInput":{"path":"/src","pattern":"TODO"},"sessionUpdate":"tool_call_update","title":"grep \"TODO\" /src","toolCallId":"toolu_native_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Grep"}},"content":[{"content":{"text":"src/a.ts","type":"text"},"type":"content"}],"rawOutput":"src/a.ts","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_native_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"terminal_update","terminalId":"toolu_native_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Bash"}},"content":[{"terminalId":"toolu_native_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"toolu_native_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"command":"make","sessionUpdate":"terminal_update","terminalId":"toolu_native_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Bash"}},"rawInput":{"command":"make"},"sessionUpdate":"tool_call_update","title":"make","toolCallId":"toolu_native_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Bash"}},"rawInput":{"command":"make","description":"Build"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_native_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":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Bash"}},"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":{"output":{"data":"bWFrZTogKioqIEVycm9yIDI="},"sessionUpdate":"terminal_update","terminalId":"toolu_native_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"exitStatus":{},"sessionUpdate":"terminal_update","terminalId":"toolu_native_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native","toolName":"Bash"}},"sessionUpdate":"tool_call_update","status":"failed","toolCallId":"toolu_native_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"content":[{"content":{"text":"```\nThe build still fails.\n```","type":"text"},"type":"content"}],"rawOutput":[{"text":"The build still fails.","type":"text"}],"sessionUpdate":"tool_call_update","status":"failed","toolCallId":"toolu_native"}}}
|
||||
{"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":{"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"}},"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"rawInput":{"pattern":"TODO"},"sessionUpdate":"tool_call_update","title":"grep \"TODO\"","toolCallId":"toolu_native_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"rawInput":{"path":"/src","pattern":"TODO"},"sessionUpdate":"tool_call_update","title":"grep \"TODO\" /src","toolCallId":"toolu_native_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"content":[{"content":{"text":"src/a.ts","type":"text"},"type":"content"}],"rawOutput":"src/a.ts","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_native_grep"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"sessionUpdate":"terminal_update","terminalId":"toolu_native_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_n","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_native"}},"content":[{"terminalId":"toolu_native_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"toolu_native_bash"}}}
|
||||
{"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":"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":{"_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}}}}
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"content":[],"kind":"think","name":"Agent","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Task","toolCallId":"toolu_outer"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"rawInput":{"description":"Plan the work"},"sessionUpdate":"tool_call_update","title":"Plan the work","toolCallId":"toolu_outer"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Agent"}},"rawInput":{"description":"Plan the work","prompt":"Plan it"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_outer"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Agent"}},"content":[{"content":{"text":"Plan it","type":"text"},"type":"content"}],"rawInput":{"description":"Plan the work","prompt":"Plan it","subagent_type":"Plan"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_outer"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_outer","toolName":"Agent"}},"content":[],"kind":"think","name":"Agent","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Task","toolCallId":"toolu_inner"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_outer","toolName":"Agent"}},"rawInput":{"description":"Read the spec"},"sessionUpdate":"tool_call_update","title":"Read the spec","toolCallId":"toolu_inner"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_outer","toolName":"Agent"}},"rawInput":{"description":"Read the spec","prompt":"Read spec.md"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_inner"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_outer","toolName":"Agent"}},"content":[{"content":{"text":"Read spec.md","type":"text"},"type":"content"}],"rawInput":{"description":"Read the spec","prompt":"Read spec.md","subagent_type":"Explore"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_inner"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_inner","toolName":"Read"}},"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":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_inner","toolName":"Read"}},"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":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_inner","toolName":"Read"}},"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":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_outer","toolName":"Agent"}},"content":[{"content":{"text":"The spec says X.","type":"text"},"type":"content"}],"rawOutput":[{"text":"The spec says X.","type":"text"}],"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_inner"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","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":"<session>","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":{"_meta":{"claudeCode":{"toolName":"Agent"}},"content":[{"content":{"text":"Plan: do X.","type":"text"},"type":"content"}],"rawOutput":[{"text":"Plan: do X.","type":"text"}],"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_outer"}}}
|
||||
{"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":{"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":{"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"}},"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":{"_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":{"_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}}}}
|
||||
|
||||
@@ -1,43 +1,51 @@
|
||||
{"kind":"response","method":"initialize","payload":{"authMethods":[],"capabilities":{"_meta":{"authStatus":{},"claudeCode":{"promptQueueing":true}},"providers":{},"session":{"additionalDirectories":{},"delete":{},"fork":{},"mcp":{"http":{},"stdio":{}},"prompt":{"embeddedContext":{},"image":{}}}},"info":{"name":"@agentclientprotocol/claude-agent-acp","title":"Claude Agent","version":"<version>"},"protocolVersion":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":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"content":[{"text":"prompt 1","type":"text"}],"messageId":"<id-1>","sessionUpdate":"user_message"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"state_update","state":"running"}}}
|
||||
{"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":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Task"}},"content":[],"kind":"think","name":"Task","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Task","toolCallId":"toolu_task"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Task"}},"rawInput":{"description":"Explore code"},"sessionUpdate":"tool_call_update","title":"Explore code","toolCallId":"toolu_task"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Task"}},"rawInput":{"description":"Explore code","prompt":"Find the parser"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_task"}}}
|
||||
{"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":{"claudeCode":{"toolName":"Task"}},"content":[{"content":{"text":"Find the parser","type":"text"},"type":"content"}],"rawInput":{"description":"Explore code","prompt":"Find the parser","subagent_type":"Explore"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_task"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","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":"<session>","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":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Read"}},"content":[],"kind":"read","locations":[],"name":"Read","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Read File","toolCallId":"toolu_sub_read"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Read"}},"locations":[{"line":1,"path":"/x.ts"}],"rawInput":{"file_path":"/x.ts"},"sessionUpdate":"tool_call_update","title":"Read /x.ts","toolCallId":"toolu_sub_read"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Task","toolResponse":{"elapsedTimeSeconds":3,"subagentType":"Explore"}}},"sessionUpdate":"tool_call_update","status":"in_progress","toolCallId":"toolu_task"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Read"}},"content":[{"content":{"text":"```\n1\tparser\n```","type":"text"},"type":"content"}],"rawOutput":"1\tparser","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_sub_read"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"sessionUpdate":"terminal_update","terminalId":"toolu_sub_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Bash"}},"content":[{"terminalId":"toolu_sub_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"toolu_sub_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"command":"rg parser","sessionUpdate":"terminal_update","terminalId":"toolu_sub_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Bash"}},"rawInput":{"command":"rg parser"},"sessionUpdate":"tool_call_update","title":"rg parser","toolCallId":"toolu_sub_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Bash"}},"rawInput":{"command":"rg parser","description":"Search"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_sub_bash"}}}
|
||||
{"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":{"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"}},"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"locations":[{"line":1,"path":"/x.ts"}],"rawInput":{"file_path":"/x.ts"},"sessionUpdate":"tool_call_update","title":"Read /x.ts","toolCallId":"toolu_sub_read"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"content":[{"content":{"text":"```\n1\tparser\n```","type":"text"},"type":"content"}],"rawOutput":"1\tparser","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_sub_read"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"sessionUpdate":"terminal_update","terminalId":"toolu_sub_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":[{"terminalId":"toolu_sub_bash","type":"terminal"}],"kind":"execute","name":"Bash","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Terminal","toolCallId":"toolu_sub_bash"}}}
|
||||
{"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":"<session>","subject":{"toolCall":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Bash"}},"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":"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":{"output":{"data":"eC50cw=="},"sessionUpdate":"terminal_update","terminalId":"toolu_sub_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"exitStatus":{"exitCode":0},"sessionUpdate":"terminal_update","terminalId":"toolu_sub_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Bash"}},"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_sub_bash"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Edit"}},"content":[],"kind":"edit","locations":[],"name":"Edit","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Edit","toolCallId":"toolu_sub_edit"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Edit"}},"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":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Edit"}},"rawInput":{"file_path":"<cwd>/x.ts","old_string":"a"},"sessionUpdate":"tool_call_update","toolCallId":"toolu_sub_edit"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Edit"}},"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":"running"}}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"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"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"agent_1","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task"}},"content":[],"kind":"edit","locations":[],"name":"Edit","rawInput":{},"sessionUpdate":"tool_call_update","status":"pending","title":"Edit","toolCallId":"toolu_sub_edit"}}}
|
||||
{"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":"<session>","subject":{"toolCall":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Edit"}},"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":"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":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Edit"}},"rawOutput":"The file has been updated.","sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_sub_edit"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"parentToolUseId":"toolu_task","toolName":"Edit","toolResponse":{"filePath":"<cwd>/x.ts","newString":"b","oldString":"a","originalFile":"a\n","replaceAll":false,"structuredPatch":[{"lines":["-a","+b"],"newLines":1,"newStart":1,"oldLines":1,"oldStart":1}],"userModified":false}}},"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":"<session>","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":"<session>","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":{"_meta":{"claudeCode":{"toolName":"Task"}},"content":[{"content":{"text":"The parser is in x.ts.","type":"text"},"type":"content"}],"rawOutput":[{"text":"The parser is in x.ts.\nagentId: a1 (use SendMessage to continue)","type":"text"}],"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_task"}}}
|
||||
{"kind":"notification","method":"session/update","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"Task","toolResponse":{"agentId":"a1","content":[{"text":"The parser is in x.ts.","type":"text"}],"prompt":"Find the parser","status":"completed","totalDurationMs":10,"totalTokens":5,"totalToolUseCount":1}}},"sessionUpdate":"tool_call_update","toolCallId":"toolu_task"}}}
|
||||
{"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":"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":{"_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}}}}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user