@agentclientprotocol/claude-agent-acp 0.99.2-preview.1 → 0.99.2-preview.11
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 +8 -0
- package/dist/acp-agent.d.ts +60 -18
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +325 -100
- package/dist/diff.d.ts +1 -1
- package/dist/diff.js +5 -5
- package/dist/elicitation.d.ts +1 -2
- package/dist/elicitation.d.ts.map +1 -1
- package/dist/elicitation.js +34 -16
- package/dist/fork-session.d.ts.map +1 -1
- package/dist/fork-session.js +23 -0
- package/dist/native-subagents.d.ts +49 -3
- package/dist/native-subagents.d.ts.map +1 -1
- package/dist/native-subagents.js +118 -4
- package/dist/session-index/service.d.ts +3 -1
- package/dist/session-index/service.d.ts.map +1 -1
- package/dist/session-index/service.js +5 -10
- package/dist/startup-failure.d.ts +16 -0
- package/dist/startup-failure.d.ts.map +1 -0
- package/dist/startup-failure.js +38 -0
- package/dist/subagent-history.d.ts +8 -1
- package/dist/subagent-history.d.ts.map +1 -1
- package/dist/subagent-history.js +40 -1
- package/dist/subagent-replay.d.ts +19 -3
- package/dist/subagent-replay.d.ts.map +1 -1
- package/dist/subagent-replay.js +46 -4
- package/dist/tool-calls/reporters/interaction.d.ts.map +1 -1
- package/dist/tool-calls/reporters/interaction.js +8 -3
- package/dist/v2/agent.d.ts.map +1 -1
- package/dist/v2/agent.js +42 -17
- package/dist/v2/initialize.d.ts +16 -1
- package/dist/v2/initialize.d.ts.map +1 -1
- package/dist/v2/initialize.js +26 -3
- package/dist/v2/permission.d.ts +5 -2
- package/dist/v2/permission.d.ts.map +1 -1
- package/dist/v2/permission.js +5 -2
- package/dist/v2/prompt.d.ts +2 -1
- package/dist/v2/prompt.d.ts.map +1 -1
- package/dist/v2/prompt.js +5 -1
- package/dist/v2/session.d.ts.map +1 -1
- package/dist/v2/session.js +21 -9
- package/package.json +6 -6
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { RequestError } from "@agentclientprotocol/sdk/unstable";
|
|
2
|
+
/** Makes the CLI write an `error_during_execution` result with a
|
|
3
|
+
* `startup_failure_reason` before it exits on a known startup failure. Without
|
|
4
|
+
* it, some failures (e.g. `proxy_invalid`) end with stderr and an exit code
|
|
5
|
+
* alone. The SDK then rejects `initializationResult()` with an error that
|
|
6
|
+
* carries the reason as `startupFailureReason`. */
|
|
7
|
+
export const STARTUP_FAILURE_RESULTS_ENV = { CLAUDE_CODE_STARTUP_FAILURE_RESULTS: "1" };
|
|
8
|
+
/** The SDK's prefix on the message of an error built from an error result. */
|
|
9
|
+
const ERROR_RESULT_PREFIX = "Claude Code returned an error result: ";
|
|
10
|
+
/** Reasons that signing in again, or with another account, can fix. */
|
|
11
|
+
const AUTH_REQUIRED_REASONS = new Set([
|
|
12
|
+
"gateway_signin_required",
|
|
13
|
+
"org_pin_mismatch",
|
|
14
|
+
"org_config_refused",
|
|
15
|
+
]);
|
|
16
|
+
/** The ACP error for a CLI that refused to start, or `undefined` when `error`
|
|
17
|
+
* carries no `startupFailureReason` (older CLIs, failures without a known
|
|
18
|
+
* cause). The message is the CLI's own, which says what to fix, e.g.
|
|
19
|
+
* `Invalid proxy URL in HTTPS_PROXY: … Fix or unset HTTPS_PROXY and restart
|
|
20
|
+
* Claude Code.`; `data.startupFailureReason` carries the reason. */
|
|
21
|
+
export function startupFailureError(error) {
|
|
22
|
+
if (!(error instanceof Error))
|
|
23
|
+
return undefined;
|
|
24
|
+
const reason = error.startupFailureReason;
|
|
25
|
+
if (typeof reason !== "string" || reason === "")
|
|
26
|
+
return undefined;
|
|
27
|
+
const message = error.message.startsWith(ERROR_RESULT_PREFIX)
|
|
28
|
+
? error.message.slice(ERROR_RESULT_PREFIX.length)
|
|
29
|
+
: error.message;
|
|
30
|
+
const data = { startupFailureReason: reason };
|
|
31
|
+
if (AUTH_REQUIRED_REASONS.has(reason)) {
|
|
32
|
+
return RequestError.authRequired(data, message);
|
|
33
|
+
}
|
|
34
|
+
if (reason === "cwd_unavailable") {
|
|
35
|
+
return RequestError.invalidParams(data, message);
|
|
36
|
+
}
|
|
37
|
+
return RequestError.internalError(data, message);
|
|
38
|
+
}
|
|
@@ -14,6 +14,11 @@ export interface SubagentRecord {
|
|
|
14
14
|
background: boolean;
|
|
15
15
|
/** Whether the user stopped it. */
|
|
16
16
|
stoppedByUser: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Its working directory, when Claude Code placed it outside its parent's:
|
|
19
|
+
* in a worktree of its own or of an ancestor, or in a `cwd` of its own.
|
|
20
|
+
*/
|
|
21
|
+
cwd?: string;
|
|
17
22
|
}
|
|
18
23
|
/** Where the subagent transcripts of a session are. */
|
|
19
24
|
export interface SubagentHistory {
|
|
@@ -31,7 +36,9 @@ export interface SubagentHistory {
|
|
|
31
36
|
* Claude keeps the history of a subagent in its own transcript,
|
|
32
37
|
* `<session>/subagents/agent-<id>.jsonl`, next to the session transcript.
|
|
33
38
|
* The `agent-<id>.meta.json` file beside it names the launching tool use.
|
|
34
|
-
*
|
|
39
|
+
* For a child that Claude Code placed elsewhere, it has `worktreePath`,
|
|
40
|
+
* `inheritedWorktreePath` or `cwd`; then the child's working directory is the
|
|
41
|
+
* `cwd` of its transcript's records. The ids are empty for a session without a local transcript.
|
|
35
42
|
*/
|
|
36
43
|
export declare function subagentHistory(sessionId: string): Promise<SubagentHistory>;
|
|
37
44
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent-history.d.ts","sourceRoot":"","sources":["../src/subagent-history.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,gCAAgC,CAAC;AAKxC,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG7F,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,UAAU,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,aAAa,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"subagent-history.d.ts","sourceRoot":"","sources":["../src/subagent-history.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,gCAAgC,CAAC;AAKxC,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG7F,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,UAAU,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,aAAa,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACpC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA+CjF;AAgCD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE7B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE;IAAE,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CAAE,GAC1C,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAoD/B;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAW5D;AA0DD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE;IAAE,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CAAE,GAC1C,OAAO,CAAC,cAAc,EAAE,CAAC,CAW3B;AAyBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAkB5E"}
|
package/dist/subagent-history.js
CHANGED
|
@@ -11,7 +11,9 @@ import { findTranscript } from "./resumed-session.js";
|
|
|
11
11
|
* Claude keeps the history of a subagent in its own transcript,
|
|
12
12
|
* `<session>/subagents/agent-<id>.jsonl`, next to the session transcript.
|
|
13
13
|
* The `agent-<id>.meta.json` file beside it names the launching tool use.
|
|
14
|
-
*
|
|
14
|
+
* For a child that Claude Code placed elsewhere, it has `worktreePath`,
|
|
15
|
+
* `inheritedWorktreePath` or `cwd`; then the child's working directory is the
|
|
16
|
+
* `cwd` of its transcript's records. The ids are empty for a session without a local transcript.
|
|
15
17
|
*/
|
|
16
18
|
export async function subagentHistory(sessionId) {
|
|
17
19
|
const agents = new Map();
|
|
@@ -35,6 +37,12 @@ export async function subagentHistory(sessionId) {
|
|
|
35
37
|
if (typeof meta?.toolUseId !== "string")
|
|
36
38
|
return;
|
|
37
39
|
const text = (value) => (typeof value === "string" ? value : undefined);
|
|
40
|
+
const placement = [meta.cwd, meta.worktreePath, meta.inheritedWorktreePath]
|
|
41
|
+
.map(text)
|
|
42
|
+
.find((value) => value && path.isAbsolute(value));
|
|
43
|
+
const cwd = placement
|
|
44
|
+
? ((await recordedCwd(path.join(directory, `agent-${match[1]}.jsonl`))) ?? placement)
|
|
45
|
+
: undefined;
|
|
38
46
|
agents.set(meta.toolUseId, {
|
|
39
47
|
agentId: match[1],
|
|
40
48
|
name: text(meta.name),
|
|
@@ -42,6 +50,7 @@ export async function subagentHistory(sessionId) {
|
|
|
42
50
|
description: text(meta.description),
|
|
43
51
|
background: meta.requestShape === "background",
|
|
44
52
|
stoppedByUser: meta.stoppedByUser === true,
|
|
53
|
+
...(cwd ? { cwd } : {}),
|
|
45
54
|
});
|
|
46
55
|
}
|
|
47
56
|
catch {
|
|
@@ -55,6 +64,35 @@ export async function subagentHistory(sessionId) {
|
|
|
55
64
|
const dir = (await getSessionInfo(sessionId).catch(() => undefined))?.cwd;
|
|
56
65
|
return dir ? { agents, dir } : { agents };
|
|
57
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* The `cwd` of the first record of the transcript `file` that has one, or
|
|
69
|
+
* undefined when the file cannot be read. Claude Code writes the working
|
|
70
|
+
* directory of the session or subagent on each record.
|
|
71
|
+
*/
|
|
72
|
+
async function recordedCwd(file) {
|
|
73
|
+
const stream = createReadStream(file, { encoding: "utf8" });
|
|
74
|
+
try {
|
|
75
|
+
for await (const line of readline.createInterface({ input: stream, crlfDelay: Infinity })) {
|
|
76
|
+
if (!line.includes('"cwd"'))
|
|
77
|
+
continue;
|
|
78
|
+
try {
|
|
79
|
+
const cwd = JSON.parse(line)?.cwd;
|
|
80
|
+
if (typeof cwd === "string" && path.isAbsolute(cwd))
|
|
81
|
+
return cwd;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// A damaged record; a later one can have the `cwd`.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// An unreadable transcript; the caller falls back to the metadata's path.
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
stream.destroy();
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
58
96
|
/** The folder of the subagent transcripts, next to the session transcript `transcript`. */
|
|
59
97
|
function subagentsDirectory(transcript, sessionId) {
|
|
60
98
|
return path.join(path.dirname(transcript), sessionId, "subagents");
|
|
@@ -121,6 +159,7 @@ export async function subagentLineage(sessionId, agentId, logger) {
|
|
|
121
159
|
agentId: current,
|
|
122
160
|
launchId: launch.launchId,
|
|
123
161
|
...(launcher !== null ? { parentAgentId: launcher } : {}),
|
|
162
|
+
...(launch.record.cwd ? { cwd: launch.record.cwd } : {}),
|
|
124
163
|
...(launch.record.name ? { name: launch.record.name } : {}),
|
|
125
164
|
...(launch.record.agentType ? { agentType: launch.record.agentType } : {}),
|
|
126
165
|
...(launch.record.description ? { description: launch.record.description } : {}),
|
|
@@ -54,6 +54,8 @@ export type ReplayChild = {
|
|
|
54
54
|
/** The state of the child's last persisted task notification. */
|
|
55
55
|
notified?: SubagentState;
|
|
56
56
|
announced: boolean;
|
|
57
|
+
/** The `cwd` that the replay reported for it; undefined while it inherits its parent's. */
|
|
58
|
+
cwd?: string;
|
|
57
59
|
promptSent: boolean;
|
|
58
60
|
/** Whether it handed a report to its parent with a SubagentHandback call. */
|
|
59
61
|
handedBack?: boolean;
|
|
@@ -72,6 +74,8 @@ export declare class RfdSubagentReplay {
|
|
|
72
74
|
* {@link currentStates}, after the load response.
|
|
73
75
|
*/
|
|
74
76
|
private readonly mapsDuringReplay;
|
|
77
|
+
/** The working directory of the root session, which a child inherits unless it reports its own. */
|
|
78
|
+
private readonly rootCwd?;
|
|
75
79
|
/** The children by the id of the tool use that launched each. */
|
|
76
80
|
private readonly children;
|
|
77
81
|
private readonly byAgentId;
|
|
@@ -88,7 +92,9 @@ export declare class RfdSubagentReplay {
|
|
|
88
92
|
* A load lacks the list until the CLI started, so it maps only in
|
|
89
93
|
* {@link currentStates}, after the load response.
|
|
90
94
|
*/
|
|
91
|
-
mapsDuringReplay?: boolean
|
|
95
|
+
mapsDuringReplay?: boolean,
|
|
96
|
+
/** The working directory of the root session, which a child inherits unless it reports its own. */
|
|
97
|
+
rootCwd?: string | undefined);
|
|
92
98
|
/**
|
|
93
99
|
* Registers the launches, the launch results, and the SendMessage calls in
|
|
94
100
|
* `messages`, the transcript of the session `ownerSessionId`.
|
|
@@ -98,10 +104,16 @@ export declare class RfdSubagentReplay {
|
|
|
98
104
|
childOfLaunch(toolUseId: string): ReplayChild | undefined;
|
|
99
105
|
/**
|
|
100
106
|
* Announces `child` on its parent, as live: running, with the parent's
|
|
101
|
-
* labels
|
|
102
|
-
*
|
|
107
|
+
* labels, and with its recorded `cwd` when that differs from its parent's.
|
|
108
|
+
* Then it sends `firstModel`, the model of the child's first response, when
|
|
109
|
+
* the transcript has one.
|
|
103
110
|
*/
|
|
104
111
|
announce(child: ReplayChild, firstModel?: string): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* The working directory of the session `sessionId` as the client holds it:
|
|
114
|
+
* a child's reported `cwd`, else its parent's, up to the root's.
|
|
115
|
+
*/
|
|
116
|
+
private effectiveCwd;
|
|
105
117
|
/**
|
|
106
118
|
* Takes the model of a replayed response of the child `owner`. A child
|
|
107
119
|
* whose model changes gets its `model` option again.
|
|
@@ -156,6 +168,10 @@ export declare class RfdSubagentReplay {
|
|
|
156
168
|
* when a live session is loaded again. Such a child reports that state and
|
|
157
169
|
* can be cancelled, as live. Any other child cannot be running, because the
|
|
158
170
|
* process that ran it is gone, so its replayed end is its current state.
|
|
171
|
+
*
|
|
172
|
+
* A replayed `cwd` whose directory is gone, such as a worktree that Claude
|
|
173
|
+
* Code removed when the child finished, is cleared with `cwd: null`, as the
|
|
174
|
+
* RFD requires after a reconnection.
|
|
159
175
|
*/
|
|
160
176
|
currentStates(live: (sessionId: string) => WorkState["state"] | undefined, liveConfiguration?: (sessionId: string) => SessionConfigOption[] | undefined): AcpSessionNotification[];
|
|
161
177
|
private registerLaunch;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent-replay.d.ts","sourceRoot":"","sources":["../src/subagent-replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"subagent-replay.d.ts","sourceRoot":"","sources":["../src/subagent-replay.ts"],"names":[],"mappings":"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"}
|
package/dist/subagent-replay.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { childModelOption, HANDBACK_TOOL, handbackMessage, isNativeSubagentControlTool, launchReply, linkedSessionMessages, nativeSubagentState, responseModelEntry, rfdEndState, rfdLabels, sameDirectory, } from "./native-subagents.js";
|
|
2
3
|
import { noticeUpdate } from "./session-notices.js";
|
|
3
4
|
export class RfdSubagentReplay {
|
|
4
5
|
history;
|
|
5
6
|
publish;
|
|
6
7
|
models;
|
|
7
8
|
mapsDuringReplay;
|
|
9
|
+
rootCwd;
|
|
8
10
|
/** The children by the id of the tool use that launched each. */
|
|
9
11
|
children = new Map();
|
|
10
12
|
byAgentId = new Map();
|
|
@@ -21,11 +23,14 @@ export class RfdSubagentReplay {
|
|
|
21
23
|
* A load lacks the list until the CLI started, so it maps only in
|
|
22
24
|
* {@link currentStates}, after the load response.
|
|
23
25
|
*/
|
|
24
|
-
mapsDuringReplay = true
|
|
26
|
+
mapsDuringReplay = true,
|
|
27
|
+
/** The working directory of the root session, which a child inherits unless it reports its own. */
|
|
28
|
+
rootCwd) {
|
|
25
29
|
this.history = history;
|
|
26
30
|
this.publish = publish;
|
|
27
31
|
this.models = models;
|
|
28
32
|
this.mapsDuringReplay = mapsDuringReplay;
|
|
33
|
+
this.rootCwd = rootCwd;
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
31
36
|
* Registers the launches, the launch results, and the SendMessage calls in
|
|
@@ -76,13 +81,19 @@ export class RfdSubagentReplay {
|
|
|
76
81
|
}
|
|
77
82
|
/**
|
|
78
83
|
* Announces `child` on its parent, as live: running, with the parent's
|
|
79
|
-
* labels
|
|
80
|
-
*
|
|
84
|
+
* labels, and with its recorded `cwd` when that differs from its parent's.
|
|
85
|
+
* Then it sends `firstModel`, the model of the child's first response, when
|
|
86
|
+
* the transcript has one.
|
|
81
87
|
*/
|
|
82
88
|
async announce(child, firstModel) {
|
|
83
89
|
if (child.announced)
|
|
84
90
|
return;
|
|
85
91
|
const labels = rfdLabels(child.record.name ?? child.input.name, child.record.agentType ?? child.input.subagent_type, child.record.description ?? child.input.description);
|
|
92
|
+
const inherited = this.effectiveCwd(child.parentSessionId);
|
|
93
|
+
const cwd = child.record.cwd &&
|
|
94
|
+
(inherited === undefined || !(await sameDirectory(child.record.cwd, inherited)))
|
|
95
|
+
? child.record.cwd
|
|
96
|
+
: undefined;
|
|
86
97
|
await this.publish({
|
|
87
98
|
sessionId: child.parentSessionId,
|
|
88
99
|
update: {
|
|
@@ -90,12 +101,29 @@ export class RfdSubagentReplay {
|
|
|
90
101
|
sessionId: child.sessionId,
|
|
91
102
|
...(labels.title ? { title: labels.title } : {}),
|
|
92
103
|
...(labels.description ? { description: labels.description } : {}),
|
|
104
|
+
...(cwd ? { cwd } : {}),
|
|
93
105
|
state: { state: "running" },
|
|
94
106
|
},
|
|
95
107
|
});
|
|
108
|
+
child.cwd = cwd;
|
|
96
109
|
child.announced = true;
|
|
97
110
|
await this.responseModel(child, firstModel);
|
|
98
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* The working directory of the session `sessionId` as the client holds it:
|
|
114
|
+
* a child's reported `cwd`, else its parent's, up to the root's.
|
|
115
|
+
*/
|
|
116
|
+
effectiveCwd(sessionId) {
|
|
117
|
+
const seen = new Set();
|
|
118
|
+
let child = this.byAgentId.get(sessionId);
|
|
119
|
+
while (child && !seen.has(child.sessionId)) {
|
|
120
|
+
if (child.cwd)
|
|
121
|
+
return child.cwd;
|
|
122
|
+
seen.add(child.sessionId);
|
|
123
|
+
child = this.byAgentId.get(child.parentSessionId);
|
|
124
|
+
}
|
|
125
|
+
return this.rootCwd;
|
|
126
|
+
}
|
|
99
127
|
/**
|
|
100
128
|
* Takes the model of a replayed response of the child `owner`. A child
|
|
101
129
|
* whose model changes gets its `model` option again.
|
|
@@ -258,6 +286,10 @@ export class RfdSubagentReplay {
|
|
|
258
286
|
* when a live session is loaded again. Such a child reports that state and
|
|
259
287
|
* can be cancelled, as live. Any other child cannot be running, because the
|
|
260
288
|
* process that ran it is gone, so its replayed end is its current state.
|
|
289
|
+
*
|
|
290
|
+
* A replayed `cwd` whose directory is gone, such as a worktree that Claude
|
|
291
|
+
* Code removed when the child finished, is cleared with `cwd: null`, as the
|
|
292
|
+
* RFD requires after a reconnection.
|
|
261
293
|
*/
|
|
262
294
|
currentStates(live, liveConfiguration) {
|
|
263
295
|
return [...this.children.values()]
|
|
@@ -278,6 +310,7 @@ export class RfdSubagentReplay {
|
|
|
278
310
|
sessionUpdate: "subagent_update",
|
|
279
311
|
sessionId: child.sessionId,
|
|
280
312
|
...(liveState !== undefined ? { capabilities: { cancel: {} } } : {}),
|
|
313
|
+
...(child.cwd && !isDirectory(child.cwd) ? { cwd: null } : {}),
|
|
281
314
|
state,
|
|
282
315
|
},
|
|
283
316
|
},
|
|
@@ -427,6 +460,15 @@ function textOf(content) {
|
|
|
427
460
|
.filter((text) => typeof text === "string")
|
|
428
461
|
.join("\n");
|
|
429
462
|
}
|
|
463
|
+
/** Whether `dir` is an existing directory. */
|
|
464
|
+
function isDirectory(dir) {
|
|
465
|
+
try {
|
|
466
|
+
return statSync(dir).isDirectory();
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
430
472
|
/** A message that a SendMessage call delivered to the child. */
|
|
431
473
|
function isDelivery(message) {
|
|
432
474
|
return message.origin?.kind === "coordinator";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interaction.d.ts","sourceRoot":"","sources":["../../../src/tool-calls/reporters/interaction.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,YAAY,EACb,MAAM,aAAa,CAAC;AAErB;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,cAAc,GAAG,YAAY;IAYvE,iEAAiE;IACjE,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,eAAe;IASvD;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG,eAAe,GAAG,SAAS;CASrE;AAED,2GAA2G;AAC3G,qBAAa,qBAAsB,YAAW,YAAY;IACxD,OAAO,IAAI,YAAY;CAGxB;AAqBD,oEAAoE;AACpE,qBAAa,uBAAwB,YAAW,YAAY;IAC1D;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,cAAc,GAAG,YAAY;CAgBxE;AAED,wEAAwE;AACxE,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY;IAKrC,UAAU,IAAI,eAAe;CAG9B;AAED;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAE7C,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY;CAyBtC;AAED,kEAAkE;AAClE,qBAAa,sBAAuB,YAAW,YAAY;IACzD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY;CAmBtC;AAED,8EAA8E;AAC9E,qBAAa,eAAgB,YAAW,YAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAE7C,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY;CAgBtC;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,eAAe;IACvD,WAAW,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,iBAAiB,GAAG,eAAe,GAAG,SAAS;CAGtF;
|
|
1
|
+
{"version":3,"file":"interaction.d.ts","sourceRoot":"","sources":["../../../src/tool-calls/reporters/interaction.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,YAAY,EACb,MAAM,aAAa,CAAC;AAErB;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,cAAc,GAAG,YAAY;IAYvE,iEAAiE;IACjE,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,eAAe;IASvD;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG,eAAe,GAAG,SAAS;CASrE;AAED,2GAA2G;AAC3G,qBAAa,qBAAsB,YAAW,YAAY;IACxD,OAAO,IAAI,YAAY;CAGxB;AAqBD,oEAAoE;AACpE,qBAAa,uBAAwB,YAAW,YAAY;IAC1D;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,cAAc,GAAG,YAAY;CAgBxE;AAED,wEAAwE;AACxE,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY;IAKrC,UAAU,IAAI,eAAe;CAG9B;AAED;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAE7C,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY;CAyBtC;AAED,kEAAkE;AAClE,qBAAa,sBAAuB,YAAW,YAAY;IACzD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY;CAmBtC;AAED,8EAA8E;AAC9E,qBAAa,eAAgB,YAAW,YAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAE7C,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY;CAgBtC;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,eAAe;IACvD,WAAW,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,iBAAiB,GAAG,eAAe,GAAG,SAAS;CAGtF;AAiID;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AA0C9D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,cAAc,GACrB,MAAM,GAAG,SAAS,CA8EpB"}
|
|
@@ -256,14 +256,15 @@ function frontmatterScalar(raw) {
|
|
|
256
256
|
return value;
|
|
257
257
|
}
|
|
258
258
|
/**
|
|
259
|
-
* The `name:` that the frontmatter of [skillMdPath] declares,
|
|
259
|
+
* The `name:` that the frontmatter of [skillMdPath] declares, `undefined` when it declares none,
|
|
260
|
+
* or `null` when the file could not be read.
|
|
260
261
|
*
|
|
261
262
|
* Only the frontmatter block is parsed, so a `name:` further down the body is not a name.
|
|
262
263
|
*/
|
|
263
264
|
function declaredSkillName(skillMdPath) {
|
|
264
265
|
const head = frontmatterHead(skillMdPath);
|
|
265
266
|
if (head === undefined) {
|
|
266
|
-
return
|
|
267
|
+
return null;
|
|
267
268
|
}
|
|
268
269
|
const lines = head.split(/\r?\n/);
|
|
269
270
|
if (lines[0]?.trim() !== "---") {
|
|
@@ -298,6 +299,10 @@ function cachedDeclaredName(skillMdPath, stats) {
|
|
|
298
299
|
return cached.declaredName;
|
|
299
300
|
}
|
|
300
301
|
const declaredName = declaredSkillName(skillMdPath);
|
|
302
|
+
// A transient read error can clear without changing the stat signature, so the next pass must retry it.
|
|
303
|
+
if (declaredName === null) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
301
306
|
if (scannedSkills.size >= MAX_CACHED_SKILLS) {
|
|
302
307
|
scannedSkills.clear();
|
|
303
308
|
}
|
|
@@ -333,7 +338,7 @@ function skillsByDeclaredName(containerDir, scans) {
|
|
|
333
338
|
continue;
|
|
334
339
|
}
|
|
335
340
|
const name = cachedDeclaredName(file, stats);
|
|
336
|
-
if (name
|
|
341
|
+
if (typeof name === "string" && !byName.has(name)) {
|
|
337
342
|
byName.set(name, file);
|
|
338
343
|
}
|
|
339
344
|
}
|
package/dist/v2/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/v2/agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/v2/agent.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,MAAM,mDAAmD,CAAC;AACxE,OAAO,EAAE,cAAc,EAAkB,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAqB9E;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GACvC,EAAE,CAAC,QAAQ,CA4Gb"}
|
package/dist/v2/agent.js
CHANGED
|
@@ -39,11 +39,11 @@ export function v2AgentApp(logger, onAgent) {
|
|
|
39
39
|
.onRequest(v2.methods.agent.providers.set, ({ params }) => agent.unstable_setProvider(params))
|
|
40
40
|
.onRequest(v2.methods.agent.providers.disable, ({ params }) => agent.unstable_disableProvider(params))
|
|
41
41
|
.onRequest(v2.methods.agent.session.new, async ({ params }) => {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
};
|
|
42
|
+
const request = v1NewSessionRequest(params);
|
|
43
|
+
const response = await agent.newSession(request);
|
|
44
|
+
const commands = await setupCommands(agent, response.sessionId, logger);
|
|
45
|
+
afterSetupResponse(agent, response.sessionId, request.mcpServers, commands, logger);
|
|
46
|
+
return { ...v2NewSessionResponse(response), ...commands };
|
|
47
47
|
})
|
|
48
48
|
.onRequest(v2.methods.agent.session.list, ({ params }) => agent.listSessions(params))
|
|
49
49
|
.onRequest(v2.methods.agent.session.resume, async ({ params }) => {
|
|
@@ -65,16 +65,19 @@ export function v2AgentApp(logger, onAgent) {
|
|
|
65
65
|
const commands = await setupCommands(agent, params.sessionId, logger);
|
|
66
66
|
// The response ends a replay. The subagents RFD treats the replayed
|
|
67
67
|
// subagent states before it as history, so their current states follow it.
|
|
68
|
-
|
|
68
|
+
afterSetupResponse(agent, params.sessionId, restore.request.mcpServers ?? [], commands, logger, () => void agent.publishCurrentSubagentStates(params.sessionId));
|
|
69
69
|
return { ...v2ResumeSessionResponse(response), ...commands };
|
|
70
70
|
})
|
|
71
71
|
.onRequest(v2.methods.agent.session.fork, ({ params }) => agent.forking(params.sessionId, async () => {
|
|
72
72
|
// The agent starts the forked session, so the client can prompt it.
|
|
73
|
-
const
|
|
73
|
+
const request = v1ForkSessionRequest(params);
|
|
74
|
+
const response = await agent.unstable_forkSession(request);
|
|
75
|
+
const commands = await setupCommands(agent, response.sessionId, logger);
|
|
76
|
+
afterSetupResponse(agent, response.sessionId, request.mcpServers ?? [], commands, logger);
|
|
74
77
|
return {
|
|
75
78
|
sessionId: response.sessionId,
|
|
76
79
|
...v2ResumeSessionResponse(response),
|
|
77
|
-
...
|
|
80
|
+
...commands,
|
|
78
81
|
};
|
|
79
82
|
}))
|
|
80
83
|
.onRequest(v2.methods.agent.session.close, async ({ params }) => {
|
|
@@ -111,12 +114,13 @@ function stateSnapshot(state) {
|
|
|
111
114
|
return { sessionUpdate: "state_update", state: "requires_action" };
|
|
112
115
|
}
|
|
113
116
|
/**
|
|
114
|
-
* The `availableCommands` of a setup response,
|
|
115
|
-
* session
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* commands
|
|
117
|
+
* The `availableCommands` of a setup response, which v2 requires when the
|
|
118
|
+
* session has commands. The agent sends a v1 client the same list as an
|
|
119
|
+
* `available_commands_update` after the response, and a v2 client none
|
|
120
|
+
* (`ClaudeAcpAgent.afterSetupResponse`). Later changes still arrive as
|
|
121
|
+
* updates. An empty list is left out, as the draft asks. A failure to read
|
|
122
|
+
* the commands does not fail the setup: the result is `undefined`, and
|
|
123
|
+
* {@link afterSetupResponse} sends the commands in an update instead.
|
|
120
124
|
*/
|
|
121
125
|
async function setupCommands(agent, sessionId, logger) {
|
|
122
126
|
try {
|
|
@@ -125,9 +129,30 @@ async function setupCommands(agent, sessionId, logger) {
|
|
|
125
129
|
}
|
|
126
130
|
catch (error) {
|
|
127
131
|
(logger ?? console).error(`Session ${sessionId}: failed to read the session's commands`, error);
|
|
128
|
-
return
|
|
132
|
+
return undefined;
|
|
129
133
|
}
|
|
130
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The work that follows a v2 setup response, run once the response is sent
|
|
137
|
+
* (`ClaudeAcpAgent.afterSetupResponse` does it on v1):
|
|
138
|
+
*
|
|
139
|
+
* - When `commands` is `undefined` (reading them for the response failed),
|
|
140
|
+
* an `available_commands_update` with the commands, read again.
|
|
141
|
+
* - `then`, such as sending the current states of replayed subagents.
|
|
142
|
+
* - OAuth for the MCP servers that need it. Its URL elicitation must not
|
|
143
|
+
* reach the client before the response, which v2 asks against.
|
|
144
|
+
*/
|
|
145
|
+
function afterSetupResponse(agent, sessionId, mcpServers, commands, logger, then) {
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
if (!commands) {
|
|
148
|
+
agent.sendAvailableCommandsUpdate(sessionId).catch((error) => {
|
|
149
|
+
(logger ?? console).error(`Session ${sessionId}: failed to send the session's commands`, error);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
then?.();
|
|
153
|
+
agent.authenticateMcpServers(sessionId, mcpServers);
|
|
154
|
+
}, 0);
|
|
155
|
+
}
|
|
131
156
|
/**
|
|
132
157
|
* The {@link AcpClient} of an ACP v2 connection: it sends the v1 messages of
|
|
133
158
|
* the agent as v2 messages.
|
|
@@ -291,7 +316,7 @@ class V2ClientConnection {
|
|
|
291
316
|
async requestPermission(params, signal) {
|
|
292
317
|
// The subject is an update of the tool call, with its stored `_meta`.
|
|
293
318
|
const toolCall = this.toolCallMeta.subject(params.sessionId, params.toolCall);
|
|
294
|
-
const response = await this.ctx.request(v2.methods.client.session.requestPermission, v2PermissionRequest({ ...params, toolCall }), { cancellationSignal: signal });
|
|
319
|
+
const response = await this.ctx.request(v2.methods.client.session.requestPermission, v2PermissionRequest({ ...params, toolCall }), { cancellationSignal: signal, stopWaitingOnCancel: true });
|
|
295
320
|
return v1PermissionResponse(response);
|
|
296
321
|
}
|
|
297
322
|
// Elicitation is the same in v1 and v2.
|
|
@@ -300,7 +325,7 @@ class V2ClientConnection {
|
|
|
300
325
|
// The v1 types accept a property schema with any tag; the v2 types accept
|
|
301
326
|
// one only in a received value. As on v1, an MCP server's schema is
|
|
302
327
|
// relayed here unchanged, so this cast does not check it.
|
|
303
|
-
params, { cancellationSignal: signal });
|
|
328
|
+
params, { cancellationSignal: signal, stopWaitingOnCancel: true });
|
|
304
329
|
}
|
|
305
330
|
completeElicitation(params) {
|
|
306
331
|
return this.ctx.notify(v2.methods.client.elicitation.complete, params);
|
package/dist/v2/initialize.d.ts
CHANGED
|
@@ -15,7 +15,22 @@ export declare function v1InitializeRequest(request: v2.InitializeRequest): Init
|
|
|
15
15
|
* The v2 `initialize` response for the v1 response of the agent.
|
|
16
16
|
*
|
|
17
17
|
* It does not advertise what the v2 surface does not serve yet: the
|
|
18
|
-
* extensions of {@link UNSERVED_EXTENSION_KEYS}
|
|
18
|
+
* extensions of {@link UNSERVED_EXTENSION_KEYS}, and the AIR extensions of
|
|
19
|
+
* {@link withoutUnservedAirExtensions}.
|
|
19
20
|
*/
|
|
20
21
|
export declare function v2InitializeResponse(response: InitializeResponse): v2.InitializeResponse;
|
|
22
|
+
/**
|
|
23
|
+
* The session capabilities of the v2 surface, which serves each through a v1
|
|
24
|
+
* method of the agent.
|
|
25
|
+
*
|
|
26
|
+
* - The session baseline: `session/new`, `list`, `resume` (with replay),
|
|
27
|
+
* `close`, `prompt`, `cancel`, and `update`.
|
|
28
|
+
* - `session/delete`, `session/fork` (which returns a session the client can
|
|
29
|
+
* prompt at once), and `additionalDirectories`.
|
|
30
|
+
* - The prompt content and MCP transports that the agent's v1 `initialize`
|
|
31
|
+
* lists: images, embedded context, and HTTP servers. Stdio servers are in the
|
|
32
|
+
* v1 baseline; v2 has no SSE transport.
|
|
33
|
+
* - `systemPrompt`, in `append` and `override` mode.
|
|
34
|
+
*/
|
|
35
|
+
export declare const V2_SESSION_CAPABILITIES: v2.SessionCapabilities;
|
|
21
36
|
//# sourceMappingURL=initialize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../../src/v2/initialize.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,mDAAmD,CAAC;
|
|
1
|
+
{"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../../src/v2/initialize.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,mDAAmD,CAAC;AAIxE,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,GAAG,iBAAiB,CAmCpF;AASD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,EAAE,CAAC,kBAAkB,CA2BxF;AA0BD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,EAAE,CAAC,mBAOxC,CAAC"}
|
package/dist/v2/initialize.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { PROTOCOL_VERSION as V1_PROTOCOL_VERSION, } from "@agentclientprotocol/sdk/unstable";
|
|
11
11
|
import * as v2 from "@agentclientprotocol/sdk/experimental/v2/unstable";
|
|
12
|
+
import { AIR_ASYNC_TASKS_CAPABILITY, AIR_GOAL_KEY } from "../air-extension.js";
|
|
12
13
|
import { SYSTEM_PROMPT_CAPABILITY } from "../client-system-prompt.js";
|
|
13
14
|
/** The v1 `initialize` request that a v2 client means. */
|
|
14
15
|
export function v1InitializeRequest(request) {
|
|
@@ -57,14 +58,15 @@ const UNSERVED_EXTENSION_KEYS = ["steering"];
|
|
|
57
58
|
* The v2 `initialize` response for the v1 response of the agent.
|
|
58
59
|
*
|
|
59
60
|
* It does not advertise what the v2 surface does not serve yet: the
|
|
60
|
-
* extensions of {@link UNSERVED_EXTENSION_KEYS}
|
|
61
|
+
* extensions of {@link UNSERVED_EXTENSION_KEYS}, and the AIR extensions of
|
|
62
|
+
* {@link withoutUnservedAirExtensions}.
|
|
61
63
|
*/
|
|
62
64
|
export function v2InitializeResponse(response) {
|
|
63
65
|
if (!response.agentInfo) {
|
|
64
66
|
throw new Error("ACP v2 requires agentInfo, and the agent reported none");
|
|
65
67
|
}
|
|
66
68
|
const meta = response.agentCapabilities?._meta;
|
|
67
|
-
const topMeta = Object.fromEntries(Object.entries(response._meta ?? {}).filter(([key]) => !UNSERVED_EXTENSION_KEYS.includes(key)));
|
|
69
|
+
const topMeta = withoutUnservedAirExtensions(Object.fromEntries(Object.entries(response._meta ?? {}).filter(([key]) => !UNSERVED_EXTENSION_KEYS.includes(key))));
|
|
68
70
|
return {
|
|
69
71
|
protocolVersion: v2.PROTOCOL_VERSION,
|
|
70
72
|
info: response.agentInfo,
|
|
@@ -81,6 +83,27 @@ export function v2InitializeResponse(response) {
|
|
|
81
83
|
...(Object.keys(topMeta).length > 0 ? { _meta: topMeta } : {}),
|
|
82
84
|
};
|
|
83
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* `meta` without the AIR extensions whose methods the v2 surface does not
|
|
88
|
+
* register: the `asyncTasks` capability (`_session/async_task/stop`) and the
|
|
89
|
+
* `goal` entry (`_session/goal`) of `_meta.jetbrains.air`.
|
|
90
|
+
*/
|
|
91
|
+
function withoutUnservedAirExtensions(meta) {
|
|
92
|
+
const jetbrains = asRecord(meta.jetbrains);
|
|
93
|
+
const air = asRecord(jetbrains.air);
|
|
94
|
+
if (Object.keys(air).length === 0)
|
|
95
|
+
return meta;
|
|
96
|
+
const served = Object.fromEntries(Object.entries(air).filter(([key]) => key !== AIR_GOAL_KEY));
|
|
97
|
+
if (Array.isArray(served.capabilities)) {
|
|
98
|
+
served.capabilities = served.capabilities.filter((capability) => capability !== AIR_ASYNC_TASKS_CAPABILITY);
|
|
99
|
+
}
|
|
100
|
+
return { ...meta, jetbrains: { ...jetbrains, air: served } };
|
|
101
|
+
}
|
|
102
|
+
function asRecord(value) {
|
|
103
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
104
|
+
? value
|
|
105
|
+
: {};
|
|
106
|
+
}
|
|
84
107
|
/**
|
|
85
108
|
* The session capabilities of the v2 surface, which serves each through a v1
|
|
86
109
|
* method of the agent.
|
|
@@ -94,7 +117,7 @@ export function v2InitializeResponse(response) {
|
|
|
94
117
|
* v1 baseline; v2 has no SSE transport.
|
|
95
118
|
* - `systemPrompt`, in `append` and `override` mode.
|
|
96
119
|
*/
|
|
97
|
-
const V2_SESSION_CAPABILITIES = {
|
|
120
|
+
export const V2_SESSION_CAPABILITIES = {
|
|
98
121
|
prompt: { image: {}, embeddedContext: {} },
|
|
99
122
|
mcp: { stdio: {}, http: {} },
|
|
100
123
|
delete: {},
|
package/dist/v2/permission.d.ts
CHANGED
|
@@ -18,8 +18,11 @@ export declare function v2PermissionRequest(request: AcpPermissionRequest): v2.R
|
|
|
18
18
|
* The v1 form of the answer to a permission request.
|
|
19
19
|
*
|
|
20
20
|
* v2 adds outcomes that v1 does not know, which the draft says are no
|
|
21
|
-
* approval. They become `cancelled
|
|
22
|
-
*
|
|
21
|
+
* approval. They become `cancelled`. In v2, `cancelled` also means that the
|
|
22
|
+
* user dismissed the prompt or the client declined it by policy. So the agent
|
|
23
|
+
* reads a `cancelled` outcome on v2 as the user refusing the tool, unless a
|
|
24
|
+
* `session/cancel` is stopping the session's work: then the tool use is
|
|
25
|
+
* aborted, as on v1.
|
|
23
26
|
*/
|
|
24
27
|
export declare function v1PermissionResponse(response: v2.RequestPermissionResponse): RequestPermissionResponse;
|
|
25
28
|
//# sourceMappingURL=permission.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission.d.ts","sourceRoot":"","sources":["../../src/v2/permission.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,KAAK,KAAK,EAAE,MAAM,mDAAmD,CAAC;AAC7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAG3E;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,EAAE,CAAC,wBAAwB,CAY9F;AAED
|
|
1
|
+
{"version":3,"file":"permission.d.ts","sourceRoot":"","sources":["../../src/v2/permission.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,KAAK,KAAK,EAAE,MAAM,mDAAmD,CAAC;AAC7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAG3E;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,EAAE,CAAC,wBAAwB,CAY9F;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,EAAE,CAAC,yBAAyB,GACrC,yBAAyB,CAS3B"}
|
package/dist/v2/permission.js
CHANGED
|
@@ -24,8 +24,11 @@ export function v2PermissionRequest(request) {
|
|
|
24
24
|
* The v1 form of the answer to a permission request.
|
|
25
25
|
*
|
|
26
26
|
* v2 adds outcomes that v1 does not know, which the draft says are no
|
|
27
|
-
* approval. They become `cancelled
|
|
28
|
-
*
|
|
27
|
+
* approval. They become `cancelled`. In v2, `cancelled` also means that the
|
|
28
|
+
* user dismissed the prompt or the client declined it by policy. So the agent
|
|
29
|
+
* reads a `cancelled` outcome on v2 as the user refusing the tool, unless a
|
|
30
|
+
* `session/cancel` is stopping the session's work: then the tool use is
|
|
31
|
+
* aborted, as on v1.
|
|
29
32
|
*/
|
|
30
33
|
export function v1PermissionResponse(response) {
|
|
31
34
|
const outcome = response.outcome;
|
package/dist/v2/prompt.d.ts
CHANGED
|
@@ -51,7 +51,8 @@ export declare function v2Foreground(send: (update: v2.SessionUpdate) => void):
|
|
|
51
51
|
* so such a block fails the request with invalid params (-32602) rather than
|
|
52
52
|
* being dropped from the prompt. The same goes for an embedded blob that
|
|
53
53
|
* Claude cannot read, e.g. an `application/zip` resource: `promptToClaude`
|
|
54
|
-
* would drop it.
|
|
54
|
+
* would drop it. An empty `prompt` fails the same way: v2 requires at least
|
|
55
|
+
* one content block.
|
|
55
56
|
*/
|
|
56
57
|
export declare function v1PromptRequest({ prompt, ...request }: v2.PromptRequest): PromptRequest;
|
|
57
58
|
export declare function v1ContentBlock(block: v2.ContentBlock): ContentBlock;
|