@agentclientprotocol/claude-agent-acp 0.89.2-preview.2 → 0.89.2-preview.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -8
- package/dist/acp-agent.d.ts +131 -23
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +879 -239
- package/dist/acp-subagents.d.ts +16 -6
- package/dist/acp-subagents.d.ts.map +1 -1
- package/dist/acp-subagents.js +8 -2
- package/dist/async-tasks.d.ts +4 -1
- package/dist/async-tasks.d.ts.map +1 -1
- package/dist/auth-status.d.ts +2 -2
- package/dist/auth-status.d.ts.map +1 -1
- package/dist/auth-status.js +18 -4
- package/dist/exit-plan.d.ts +17 -0
- package/dist/exit-plan.d.ts.map +1 -1
- package/dist/exit-plan.js +34 -0
- package/dist/native-subagents.d.ts +190 -11
- package/dist/native-subagents.d.ts.map +1 -1
- package/dist/native-subagents.js +419 -40
- package/dist/settings.d.ts +6 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +13 -0
- package/dist/subagent-history.d.ts +27 -2
- package/dist/subagent-history.d.ts.map +1 -1
- package/dist/subagent-history.js +38 -10
- package/dist/subagent-replay.d.ts +108 -0
- package/dist/subagent-replay.d.ts.map +1 -0
- package/dist/subagent-replay.js +261 -0
- package/dist/tool-calls/background.d.ts +9 -0
- package/dist/tool-calls/background.d.ts.map +1 -1
- package/dist/tool-calls/background.js +25 -0
- package/dist/tool-calls/renderer.d.ts +32 -7
- package/dist/tool-calls/renderer.d.ts.map +1 -1
- package/dist/tool-calls/renderer.js +54 -21
- package/dist/turn-events.d.ts +39 -17
- package/dist/turn-events.d.ts.map +1 -1
- package/dist/v2/agent.d.ts.map +1 -1
- package/dist/v2/agent.js +52 -14
- package/dist/v2/initialize.d.ts.map +1 -1
- package/dist/v2/initialize.js +10 -1
- package/dist/v2/prompt.d.ts +29 -18
- package/dist/v2/prompt.d.ts.map +1 -1
- package/dist/v2/prompt.js +54 -43
- package/dist/v2/session-update.d.ts +11 -0
- package/dist/v2/session-update.d.ts.map +1 -1
- package/dist/v2/session-update.js +67 -0
- package/dist/v2/session.d.ts +3 -12
- package/dist/v2/session.d.ts.map +1 -1
- package/dist/v2/session.js +5 -18
- package/dist/v2/tool-call-meta.d.ts +44 -0
- package/dist/v2/tool-call-meta.d.ts.map +1 -0
- package/dist/v2/tool-call-meta.js +72 -0
- package/package.json +1 -1
package/dist/v2/prompt.js
CHANGED
|
@@ -8,27 +8,15 @@ import { RequestError, } from "@agentclientprotocol/sdk";
|
|
|
8
8
|
import * as v2 from "@agentclientprotocol/sdk/experimental/v2";
|
|
9
9
|
/**
|
|
10
10
|
* Starts a turn for `params`, and answers when Claude Code takes the prompt
|
|
11
|
-
* in
|
|
12
|
-
*
|
|
13
|
-
* the agent's own updates.
|
|
11
|
+
* in, with the id of the user message it became. `send` delivers a session
|
|
12
|
+
* update of the prompt's session; it must not throw, and must deliver updates
|
|
13
|
+
* in the order it is called, also relative to the agent's own updates.
|
|
14
14
|
*
|
|
15
|
-
* - Taken in: the prompt's `user_message`, then
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* - Ended: `idle` with the stop reason, usage, and `_meta` of the turn.
|
|
19
|
-
* - Failed after it was taken in: `idle` with the `error` stop reason and the
|
|
20
|
-
* JSON-RPC error that v1 answers the prompt with, so a client can show its
|
|
21
|
-
* message and handle its code as for an error response (for example,
|
|
22
|
-
* `-32000` starts the client's sign-in).
|
|
15
|
+
* - Taken in: the prompt's `user_message`, then the answer. From then on, the
|
|
16
|
+
* turn is the session's foreground work, which reports its states (see
|
|
17
|
+
* {@link v2Foreground}).
|
|
23
18
|
* - Ended or failed before it was taken in: the answer is a JSON-RPC error,
|
|
24
19
|
* `-32800` for a prompt that a cancel ended in the queue.
|
|
25
|
-
*
|
|
26
|
-
* Deferred decision: a turn whose background subagents still run ends only
|
|
27
|
-
* when they finish (`Turn.deferredSettle`), so `idle` waits for them too. The
|
|
28
|
-
* hold exists for v1, whose clients stop listening at the prompt response.
|
|
29
|
-
* v2 allows background updates while idle, so it could report `idle` at the
|
|
30
|
-
* turn's result instead, with a turn event for the hold. To be decided with
|
|
31
|
-
* async tasks (step 6 in `docs/acp-v2.md`).
|
|
32
20
|
*/
|
|
33
21
|
export function v2Prompt(agent, params, send) {
|
|
34
22
|
return new Promise((resolve, reject) => {
|
|
@@ -37,37 +25,18 @@ export function v2Prompt(agent, params, send) {
|
|
|
37
25
|
inserted(messageId) {
|
|
38
26
|
inserted = true;
|
|
39
27
|
send({ sessionUpdate: "user_message", messageId, content: params.prompt });
|
|
40
|
-
send({ sessionUpdate: "state_update", state: "running" });
|
|
41
28
|
resolve({ messageId });
|
|
42
29
|
},
|
|
43
|
-
awaitingUser() {
|
|
44
|
-
send({ sessionUpdate: "state_update", state: "requires_action" });
|
|
45
|
-
},
|
|
46
|
-
resumed() {
|
|
47
|
-
send({ sessionUpdate: "state_update", state: "running" });
|
|
48
|
-
},
|
|
49
30
|
ended(outcome) {
|
|
50
|
-
if (inserted)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
reject(RequestError.internalError({ stopReason: outcome.stopReason }, "The turn ended before Claude Code took the prompt in"));
|
|
58
|
-
}
|
|
31
|
+
if (inserted)
|
|
32
|
+
return;
|
|
33
|
+
reject(outcome.stopReason === "cancelled"
|
|
34
|
+
? RequestError.requestCancelled()
|
|
35
|
+
: RequestError.internalError({ stopReason: outcome.stopReason }, "The turn ended before Claude Code took the prompt in"));
|
|
59
36
|
},
|
|
60
37
|
failed(error) {
|
|
61
|
-
if (!inserted)
|
|
38
|
+
if (!inserted)
|
|
62
39
|
reject(error);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
send({
|
|
66
|
-
sessionUpdate: "state_update",
|
|
67
|
-
state: "idle",
|
|
68
|
-
stopReason: "error",
|
|
69
|
-
error: jsonRpcError(error),
|
|
70
|
-
});
|
|
71
40
|
},
|
|
72
41
|
};
|
|
73
42
|
let request;
|
|
@@ -81,6 +50,48 @@ export function v2Prompt(agent, params, send) {
|
|
|
81
50
|
agent.startTurn(request, events).catch(reject);
|
|
82
51
|
});
|
|
83
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Reports the foreground work of a session (see {@link ForegroundEvents}) as
|
|
55
|
+
* its `state_update`s. `send` delivers a session update of that session, as
|
|
56
|
+
* for {@link v2Prompt}.
|
|
57
|
+
*
|
|
58
|
+
* - Started: `running`. For a prompt's turn, right after its `user_message`.
|
|
59
|
+
* - Waiting on a permission request or a question: `requires_action`, and
|
|
60
|
+
* `running` once none is open.
|
|
61
|
+
* - Ended: `idle` with the stop reason, usage, and `_meta` of the work.
|
|
62
|
+
* - Failed: `idle` with the `error` stop reason and the JSON-RPC error that v1
|
|
63
|
+
* answers the prompt with, so a client can show its message and handle its
|
|
64
|
+
* code as for an error response (for example, `-32000` starts the client's
|
|
65
|
+
* sign-in).
|
|
66
|
+
*
|
|
67
|
+
* The work is a prompt's turn or a model cycle that Claude Code started on its
|
|
68
|
+
* own, such as its follow-up after a background subagent finished. A turn's
|
|
69
|
+
* work ends at its result, also when v1 holds the turn's answer for the
|
|
70
|
+
* background subagents it spawned (`Turn.deferredSettle`): v2 allows
|
|
71
|
+
* background updates while idle, so the subagents' output and requests arrive
|
|
72
|
+
* after `idle`, and their follow-up is a cycle of its own.
|
|
73
|
+
*/
|
|
74
|
+
export function v2Foreground(send) {
|
|
75
|
+
return {
|
|
76
|
+
running() {
|
|
77
|
+
send({ sessionUpdate: "state_update", state: "running" });
|
|
78
|
+
},
|
|
79
|
+
awaitingUser() {
|
|
80
|
+
send({ sessionUpdate: "state_update", state: "requires_action" });
|
|
81
|
+
},
|
|
82
|
+
ended(outcome) {
|
|
83
|
+
send(idle(outcome));
|
|
84
|
+
},
|
|
85
|
+
failed(error) {
|
|
86
|
+
send({
|
|
87
|
+
sessionUpdate: "state_update",
|
|
88
|
+
state: "idle",
|
|
89
|
+
stopReason: "error",
|
|
90
|
+
error: jsonRpcError(error),
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
84
95
|
/** The JSON-RPC error that v1 answers a failed prompt with. */
|
|
85
96
|
function jsonRpcError(error) {
|
|
86
97
|
if (error instanceof RequestError) {
|
|
@@ -18,4 +18,15 @@ export declare const SESSION_PLAN_ID = "plan";
|
|
|
18
18
|
* an error rather than a silently missing update.
|
|
19
19
|
*/
|
|
20
20
|
export declare function v2SessionUpdate(update: AcpSessionNotification["update"]): v2.SessionUpdate | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* The child's own `state_update` for a v1 `subagent_update` that reports the
|
|
23
|
+
* child's work state, or `undefined` when it reports none.
|
|
24
|
+
*
|
|
25
|
+
* v1 reports a child's work state only on its parent, as the `state` of the
|
|
26
|
+
* `subagent_update`. v2 reports it on the child's own stream, as for any
|
|
27
|
+
* session, and mirrors it on the parent (the subagents RFD). The child's copy
|
|
28
|
+
* goes right after the parent's: never before the announcement, and both
|
|
29
|
+
* copies of a state before any later state.
|
|
30
|
+
*/
|
|
31
|
+
export declare function v2ChildStateUpdate(update: AcpSessionNotification["update"]): v2.UpdateSessionNotification | undefined;
|
|
21
32
|
//# sourceMappingURL=session-update.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-update.d.ts","sourceRoot":"","sources":["../../src/v2/session-update.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAC/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAIlE;;;;GAIG;AACH,eAAO,MAAM,eAAe,SAAS,CAAC;AAEtC;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GACvC,EAAE,CAAC,aAAa,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"session-update.d.ts","sourceRoot":"","sources":["../../src/v2/session-update.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAC/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAIlE;;;;GAIG;AACH,eAAO,MAAM,eAAe,SAAS,CAAC;AAEtC;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GACvC,EAAE,CAAC,aAAa,GAAG,SAAS,CAyD9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GACvC,EAAE,CAAC,yBAAyB,GAAG,SAAS,CAM1C"}
|
|
@@ -45,6 +45,21 @@ export function v2SessionUpdate(update) {
|
|
|
45
45
|
plan: { type: "items", planId: SESSION_PLAN_ID, entries: update.entries },
|
|
46
46
|
...(update._meta != null ? { _meta: update._meta } : {}),
|
|
47
47
|
};
|
|
48
|
+
case "plan_update":
|
|
49
|
+
case "plan_removed":
|
|
50
|
+
// v1's draft plan operations have v2's shape.
|
|
51
|
+
return update;
|
|
52
|
+
case "subagent_update": {
|
|
53
|
+
// The subagents RFD gives it the same shape in v1 and v2. One that
|
|
54
|
+
// reports a state also has a copy on the child's stream (see
|
|
55
|
+
// `v2ChildStateUpdate`).
|
|
56
|
+
const { state, ...association } = update;
|
|
57
|
+
return { ...association, ...(state != null ? { state: v2SubagentState(state) } : {}) };
|
|
58
|
+
}
|
|
59
|
+
case "session_message":
|
|
60
|
+
case "session_message_chunk":
|
|
61
|
+
// The subagents RFD gives these the same shape in v1 and v2.
|
|
62
|
+
return update;
|
|
48
63
|
case "current_mode_update":
|
|
49
64
|
// v2 has no modes: the mode is the `mode` config option. Every mode
|
|
50
65
|
// change also reaches the client as a `config_option_update` or in the
|
|
@@ -54,3 +69,55 @@ export function v2SessionUpdate(update) {
|
|
|
54
69
|
throw new Error(`The ACP v2 surface does not translate ${update.sessionUpdate} session updates yet`);
|
|
55
70
|
}
|
|
56
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* The child's own `state_update` for a v1 `subagent_update` that reports the
|
|
74
|
+
* child's work state, or `undefined` when it reports none.
|
|
75
|
+
*
|
|
76
|
+
* v1 reports a child's work state only on its parent, as the `state` of the
|
|
77
|
+
* `subagent_update`. v2 reports it on the child's own stream, as for any
|
|
78
|
+
* session, and mirrors it on the parent (the subagents RFD). The child's copy
|
|
79
|
+
* goes right after the parent's: never before the announcement, and both
|
|
80
|
+
* copies of a state before any later state.
|
|
81
|
+
*/
|
|
82
|
+
export function v2ChildStateUpdate(update) {
|
|
83
|
+
if (update.sessionUpdate !== "subagent_update" || update.state == null)
|
|
84
|
+
return undefined;
|
|
85
|
+
return {
|
|
86
|
+
sessionId: update.sessionId,
|
|
87
|
+
update: { sessionUpdate: "state_update", ...v2SubagentState(update.state) },
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** The work states of the subagents RFD, and the stop reasons of its `idle`. */
|
|
91
|
+
const SUBAGENT_STATES = new Set([
|
|
92
|
+
"running",
|
|
93
|
+
"requires_action",
|
|
94
|
+
"unknown",
|
|
95
|
+
"idle",
|
|
96
|
+
]);
|
|
97
|
+
const IDLE_STOP_REASONS = new Set([
|
|
98
|
+
undefined,
|
|
99
|
+
null,
|
|
100
|
+
"end_turn",
|
|
101
|
+
"max_tokens",
|
|
102
|
+
"max_turn_requests",
|
|
103
|
+
"refusal",
|
|
104
|
+
"cancelled",
|
|
105
|
+
"error",
|
|
106
|
+
]);
|
|
107
|
+
/**
|
|
108
|
+
* The v2 form of a subagent's v1 work state.
|
|
109
|
+
*
|
|
110
|
+
* The RFD's states have the same shape in v1 and v2. Each version also types
|
|
111
|
+
* an open member for states and stop reasons it does not know, and those two
|
|
112
|
+
* members are typed apart. So the state is checked to be one of the RFD's, and
|
|
113
|
+
* any other throws, as an update kind that this surface does not translate
|
|
114
|
+
* does.
|
|
115
|
+
*/
|
|
116
|
+
function v2SubagentState(state) {
|
|
117
|
+
const stopReason = state.stopReason;
|
|
118
|
+
if (!SUBAGENT_STATES.has(state.state) ||
|
|
119
|
+
(state.state === "idle" && !IDLE_STOP_REASONS.has(stopReason))) {
|
|
120
|
+
throw new Error(`The ACP v2 surface does not translate the subagent state ${state.state}${state.state === "idle" ? ` with the stop reason ${String(stopReason)}` : ""}`);
|
|
121
|
+
}
|
|
122
|
+
return state;
|
|
123
|
+
}
|
package/dist/v2/session.d.ts
CHANGED
|
@@ -23,19 +23,10 @@ export declare function v1RestoreSessionRequest(params: v2.ResumeSessionRequest)
|
|
|
23
23
|
request: LoadSessionRequest;
|
|
24
24
|
};
|
|
25
25
|
/**
|
|
26
|
-
* The v1
|
|
27
|
-
*
|
|
28
|
-
* The agent's v1 fork copies the transcript into a new session and returns
|
|
29
|
-
* only its id; a v1 client then resumes or loads it. A v2 fork returns the new
|
|
30
|
-
* session with its config options, like `session/new`, so the client can
|
|
31
|
-
* prompt it. The v2 surface therefore forks, then resumes the fork without
|
|
32
|
-
* replay, with the request's `cwd`, `additionalDirectories`, and
|
|
33
|
-
* `mcpServers`. The request's `_meta` goes to the fork alone.
|
|
26
|
+
* The v1 request of a v2 `session/fork`. In both versions the agent's fork
|
|
27
|
+
* returns a session that the client can prompt, with its config options.
|
|
34
28
|
*/
|
|
35
|
-
export declare function
|
|
36
|
-
fork: ForkSessionRequest;
|
|
37
|
-
resume: (sessionId: string) => ResumeSessionRequest;
|
|
38
|
-
};
|
|
29
|
+
export declare function v1ForkSessionRequest(params: v2.ForkSessionRequest): ForkSessionRequest;
|
|
39
30
|
/** v2 drops `modes`: the session mode is the `mode` config option, which the agent always lists. */
|
|
40
31
|
export declare function v2NewSessionResponse(response: NewSessionResponse): v2.NewSessionResponse;
|
|
41
32
|
export declare function v2ResumeSessionResponse(response: ResumeSessionResponse): v2.ResumeSessionResponse;
|
package/dist/v2/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/v2/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EAEvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EAGxB,KAAK,6BAA6B,EACnC,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAE/D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,EAAE,CAAC,iBAAiB,GAAG,iBAAiB,CAInF;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,EAAE,CAAC,oBAAoB,GAE7B;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,GACnD;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAkBlD;AAED
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/v2/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EAEvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EAGxB,KAAK,6BAA6B,EACnC,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAE/D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,EAAE,CAAC,iBAAiB,GAAG,iBAAiB,CAInF;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,EAAE,CAAC,oBAAoB,GAE7B;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,GACnD;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAkBlD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,EAAE,CAAC,kBAAkB,GAAG,kBAAkB,CAGtF;AAED,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,EAAE,CAAC,kBAAkB,CAExF;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,EAAE,CAAC,qBAAqB,CAKjG;AAED,uEAAuE;AACvE,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,EAAE,CAAC,6BAA6B,GACvC,6BAA6B,CAgB/B;AAED,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAQxF;AAWD,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC,gBAAgB,EAAE,CAKvF"}
|
package/dist/v2/session.js
CHANGED
|
@@ -36,25 +36,12 @@ export function v1RestoreSessionRequest(params) {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* The v1
|
|
40
|
-
*
|
|
41
|
-
* The agent's v1 fork copies the transcript into a new session and returns
|
|
42
|
-
* only its id; a v1 client then resumes or loads it. A v2 fork returns the new
|
|
43
|
-
* session with its config options, like `session/new`, so the client can
|
|
44
|
-
* prompt it. The v2 surface therefore forks, then resumes the fork without
|
|
45
|
-
* replay, with the request's `cwd`, `additionalDirectories`, and
|
|
46
|
-
* `mcpServers`. The request's `_meta` goes to the fork alone.
|
|
39
|
+
* The v1 request of a v2 `session/fork`. In both versions the agent's fork
|
|
40
|
+
* returns a session that the client can prompt, with its config options.
|
|
47
41
|
*/
|
|
48
|
-
export function
|
|
49
|
-
const { mcpServers,
|
|
50
|
-
|
|
51
|
-
...request,
|
|
52
|
-
...(mcpServers ? { mcpServers: mcpServers.map(v1McpServer) } : {}),
|
|
53
|
-
};
|
|
54
|
-
return {
|
|
55
|
-
fork: { ...session, ...(_meta != null ? { _meta } : {}) },
|
|
56
|
-
resume: (sessionId) => ({ ...session, sessionId }),
|
|
57
|
-
};
|
|
42
|
+
export function v1ForkSessionRequest(params) {
|
|
43
|
+
const { mcpServers, ...request } = params;
|
|
44
|
+
return { ...request, ...(mcpServers ? { mcpServers: mcpServers.map(v1McpServer) } : {}) };
|
|
58
45
|
}
|
|
59
46
|
/** v2 drops `modes`: the session mode is the `mode` config option, which the agent always lists. */
|
|
60
47
|
export function v2NewSessionResponse(response) {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `_meta` of the tool calls of one ACP v2 connection, as the client stores
|
|
3
|
+
* it.
|
|
4
|
+
*
|
|
5
|
+
* The agent gives each v1 tool call report the `_meta` keys of that report.
|
|
6
|
+
* An AIR client merges them by key (see `ToolCallFieldTracker`), and another
|
|
7
|
+
* v1 client reads them per report. A v2 client stores a tool call's `_meta`,
|
|
8
|
+
* and a report that carries `_meta` replaces it. So the earlier keys must
|
|
9
|
+
* travel again, or the `{ toolName }` of a tool's result would replace, for
|
|
10
|
+
* example, the reason of the denial before it.
|
|
11
|
+
*
|
|
12
|
+
* {@link V2ToolCallMeta} therefore keeps the merged `_meta` of each tool call,
|
|
13
|
+
* with the `claudeCode` keys merged one by one and every other key replaced
|
|
14
|
+
* whole, as `ToolCallFieldTracker` models it. A report that changes it carries
|
|
15
|
+
* all of it; a report that changes nothing carries no `_meta`.
|
|
16
|
+
*
|
|
17
|
+
* `claudeCode.toolName` repeats the name of the tool, which v2 has a field
|
|
18
|
+
* for. It becomes the `name` of a tool call that has none yet, and is left out
|
|
19
|
+
* of the stored `_meta`. Subagent routing reads it before this, on the agent's
|
|
20
|
+
* v1 reports.
|
|
21
|
+
*/
|
|
22
|
+
import type { ToolCall, ToolCallUpdate } from "@agentclientprotocol/sdk";
|
|
23
|
+
export declare class V2ToolCallMeta {
|
|
24
|
+
/** The stored `_meta` of each tool call, by session and tool call, oldest first. */
|
|
25
|
+
private readonly calls;
|
|
26
|
+
/**
|
|
27
|
+
* `report` with the tool call's whole `_meta` when the report changes it,
|
|
28
|
+
* without `_meta` when it does not, and with `name` when it names the tool
|
|
29
|
+
* call for the first time. `starts` says that the report starts the tool
|
|
30
|
+
* call (a v1 `tool_call`), which forgets what an earlier one stored.
|
|
31
|
+
*/
|
|
32
|
+
apply<Report extends ToolCall | ToolCallUpdate>(sessionId: string, report: Report, starts: boolean): Report;
|
|
33
|
+
/**
|
|
34
|
+
* The tool call of a permission request's subject: like {@link apply}, but
|
|
35
|
+
* with the tool call's whole `_meta` even when the subject changes nothing,
|
|
36
|
+
* since a client can show the request from the request alone.
|
|
37
|
+
*/
|
|
38
|
+
subject<Report extends ToolCall | ToolCallUpdate>(sessionId: string, toolCall: Report): Report;
|
|
39
|
+
/** Forgets the tool calls of a session, when it closes. */
|
|
40
|
+
forget(sessionId: string): void;
|
|
41
|
+
/** Stores the entry as the most recent one, and forgets the oldest one past the limit. */
|
|
42
|
+
private remember;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=tool-call-meta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-call-meta.d.ts","sourceRoot":"","sources":["../../src/v2/tool-call-meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAOzE,qBAAa,cAAc;IACzB,oFAAoF;IACpF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqD;IAE3E;;;;;OAKG;IACH,KAAK,CAAC,MAAM,SAAS,QAAQ,GAAG,cAAc,EAC5C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,GACd,MAAM;IA6BT;;;;OAIG;IACH,OAAO,CAAC,MAAM,SAAS,QAAQ,GAAG,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAO9F,2DAA2D;IAC3D,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAM/B,0FAA0F;IAC1F,OAAO,CAAC,QAAQ;CAQjB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** The number of tool calls whose `_meta` a connection remembers. */
|
|
2
|
+
const TOOL_CALL_LIMIT = 2048;
|
|
3
|
+
export class V2ToolCallMeta {
|
|
4
|
+
/** The stored `_meta` of each tool call, by session and tool call, oldest first. */
|
|
5
|
+
calls = new Map();
|
|
6
|
+
/**
|
|
7
|
+
* `report` with the tool call's whole `_meta` when the report changes it,
|
|
8
|
+
* without `_meta` when it does not, and with `name` when it names the tool
|
|
9
|
+
* call for the first time. `starts` says that the report starts the tool
|
|
10
|
+
* call (a v1 `tool_call`), which forgets what an earlier one stored.
|
|
11
|
+
*/
|
|
12
|
+
apply(sessionId, report, starts) {
|
|
13
|
+
const key = `${sessionId} ${report.toolCallId}`;
|
|
14
|
+
const call = (!starts && this.calls.get(key)) || { meta: {}, named: false };
|
|
15
|
+
this.remember(key, call);
|
|
16
|
+
const { _meta, ...fields } = report;
|
|
17
|
+
const result = fields;
|
|
18
|
+
const { toolName, ...claudeCode } = asRecord(_meta?.claudeCode) ?? {};
|
|
19
|
+
if (report.name != null) {
|
|
20
|
+
call.named = true;
|
|
21
|
+
}
|
|
22
|
+
else if (!call.named && typeof toolName === "string") {
|
|
23
|
+
result.name = toolName;
|
|
24
|
+
call.named = true;
|
|
25
|
+
}
|
|
26
|
+
const meta = { ...call.meta };
|
|
27
|
+
for (const [name, value] of Object.entries(_meta ?? {})) {
|
|
28
|
+
if (name !== "claudeCode")
|
|
29
|
+
meta[name] = value;
|
|
30
|
+
}
|
|
31
|
+
if (Object.keys(claudeCode).length > 0) {
|
|
32
|
+
meta.claudeCode = { ...asRecord(call.meta.claudeCode), ...claudeCode };
|
|
33
|
+
}
|
|
34
|
+
if (JSON.stringify(meta) !== JSON.stringify(call.meta)) {
|
|
35
|
+
call.meta = meta;
|
|
36
|
+
result._meta = meta;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The tool call of a permission request's subject: like {@link apply}, but
|
|
42
|
+
* with the tool call's whole `_meta` even when the subject changes nothing,
|
|
43
|
+
* since a client can show the request from the request alone.
|
|
44
|
+
*/
|
|
45
|
+
subject(sessionId, toolCall) {
|
|
46
|
+
const result = this.apply(sessionId, toolCall, false);
|
|
47
|
+
const stored = this.calls.get(`${sessionId} ${toolCall.toolCallId}`)?.meta ?? {};
|
|
48
|
+
if (result._meta === undefined && Object.keys(stored).length > 0)
|
|
49
|
+
result._meta = stored;
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/** Forgets the tool calls of a session, when it closes. */
|
|
53
|
+
forget(sessionId) {
|
|
54
|
+
for (const key of this.calls.keys()) {
|
|
55
|
+
if (key.startsWith(`${sessionId} `))
|
|
56
|
+
this.calls.delete(key);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Stores the entry as the most recent one, and forgets the oldest one past the limit. */
|
|
60
|
+
remember(key, call) {
|
|
61
|
+
this.calls.delete(key);
|
|
62
|
+
this.calls.set(key, call);
|
|
63
|
+
if (this.calls.size > TOOL_CALL_LIMIT) {
|
|
64
|
+
const oldest = this.calls.keys().next().value;
|
|
65
|
+
if (oldest !== undefined)
|
|
66
|
+
this.calls.delete(oldest);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function asRecord(value) {
|
|
71
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
72
|
+
}
|
package/package.json
CHANGED