@agentclientprotocol/claude-agent-acp 0.85.2-preview.1 → 0.85.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 +1 -0
- package/dist/acp-agent.d.ts +113 -27
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +477 -175
- package/dist/air-extension.d.ts +2 -0
- package/dist/air-extension.d.ts.map +1 -1
- package/dist/air-extension.js +2 -0
- package/dist/diff.d.ts +42 -5
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +103 -26
- package/dist/mcp-command.d.ts +51 -0
- package/dist/mcp-command.d.ts.map +1 -0
- package/dist/mcp-command.js +321 -0
- package/dist/resumed-session.d.ts +16 -6
- package/dist/resumed-session.d.ts.map +1 -1
- package/dist/resumed-session.js +91 -25
- package/dist/session-mode.d.ts +14 -0
- package/dist/session-mode.d.ts.map +1 -1
- package/dist/session-mode.js +22 -0
- package/dist/tool-calls/client-capabilities.d.ts +16 -2
- package/dist/tool-calls/client-capabilities.d.ts.map +1 -1
- package/dist/tool-calls/client-capabilities.js +11 -3
- package/dist/tool-calls/reporters/file-edit.d.ts +1 -1
- package/dist/tool-calls/reporters/file-edit.d.ts.map +1 -1
- package/dist/tool-calls/reporters/file-edit.js +18 -14
- package/dist/turn-events.d.ts +71 -0
- package/dist/turn-events.d.ts.map +1 -0
- package/dist/turn-events.js +1 -0
- package/dist/usage-markdown.d.ts +2 -0
- package/dist/usage-markdown.d.ts.map +1 -1
- package/dist/usage-markdown.js +2 -1
- package/dist/utils.d.ts +12 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +28 -0
- package/dist/v2/agent.d.ts.map +1 -1
- package/dist/v2/agent.js +27 -5
- package/dist/v2/prompt.d.ts +47 -0
- package/dist/v2/prompt.d.ts.map +1 -0
- package/dist/v2/prompt.js +153 -0
- package/dist/v2/session-update.d.ts.map +1 -1
- package/dist/v2/session-update.js +14 -0
- package/dist/v2/tool-call.d.ts +32 -0
- package/dist/v2/tool-call.d.ts.map +1 -0
- package/dist/v2/tool-call.js +42 -0
- package/package.json +2 -2
|
@@ -12,6 +12,7 @@ export class ClientCapabilities {
|
|
|
12
12
|
terminalOutputDelta;
|
|
13
13
|
diffPatch;
|
|
14
14
|
air;
|
|
15
|
+
v2;
|
|
15
16
|
constructor(
|
|
16
17
|
/** The client renders a terminal from `_meta.terminal_info`, `terminal_output`, and `terminal_exit`. */
|
|
17
18
|
terminalOutput = false,
|
|
@@ -20,20 +21,27 @@ export class ClientCapabilities {
|
|
|
20
21
|
/** The client accepts an exact git patch in a diff (`jetbrains.air` `diffPatch`). */
|
|
21
22
|
diffPatch = false,
|
|
22
23
|
/** The capabilities that only JetBrains AIR declares. */
|
|
23
|
-
air = NO_AIR
|
|
24
|
+
air = NO_AIR,
|
|
25
|
+
/**
|
|
26
|
+
* The client speaks ACP v2. Its diffs carry structured `changes` and an
|
|
27
|
+
* optional git patch, which a v1 diff cannot express. Only the v2 surface
|
|
28
|
+
* sets this, never the capabilities that a client sends.
|
|
29
|
+
*/
|
|
30
|
+
v2 = false) {
|
|
24
31
|
this.terminalOutput = terminalOutput;
|
|
25
32
|
this.terminalOutputDelta = terminalOutputDelta;
|
|
26
33
|
this.diffPatch = diffPatch;
|
|
27
34
|
this.air = air;
|
|
35
|
+
this.v2 = v2;
|
|
28
36
|
}
|
|
29
|
-
static from(capabilities) {
|
|
37
|
+
static from(capabilities, { v2 = false } = {}) {
|
|
30
38
|
const meta = capabilities?._meta;
|
|
31
39
|
const terminalOutputDelta = meta?.["terminal_output_delta"] === true;
|
|
32
40
|
return new ClientCapabilities(terminalOutputDelta || meta?.["terminal_output"] === true, terminalOutputDelta, clientSupportsAirCapability(capabilities, AIR_DIFF_PATCH_CAPABILITY), {
|
|
33
41
|
client: isAirClient(capabilities),
|
|
34
42
|
rawInputRendering: clientSupportsAirCapability(capabilities, AIR_RAW_INPUT_RENDERING_CAPABILITY),
|
|
35
43
|
planFile: clientSupportsAirCapability(capabilities, AIR_PLAN_FILE_CAPABILITY),
|
|
36
|
-
});
|
|
44
|
+
}, v2);
|
|
37
45
|
}
|
|
38
46
|
}
|
|
39
47
|
const NO_AIR = {
|
|
@@ -22,7 +22,7 @@ type NormalizedWriteInput = Partial<FileWriteInput> & {
|
|
|
22
22
|
export declare function normalizeWriteInput(input: unknown): NormalizedWriteInput | undefined;
|
|
23
23
|
/** Edit: the diff holds the old and the new text. */
|
|
24
24
|
export declare class EditReporter implements ToolReporter {
|
|
25
|
-
toolUse(input: unknown, { cwd }: ToolUseContext): ToolUseFacts;
|
|
25
|
+
toolUse(input: unknown, { cwd, capabilities }: ToolUseContext): ToolUseFacts;
|
|
26
26
|
toolResult(): ToolResultFacts;
|
|
27
27
|
hookResult(toolResponse: unknown, context: ToolUseContext): Promise<ToolResultFacts>;
|
|
28
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-edit.d.ts","sourceRoot":"","sources":["../../../src/tool-calls/reporters/file-edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,cAAc,EAEf,MAAM,6CAA6C,CAAC;
|
|
1
|
+
{"version":3,"file":"file-edit.d.ts","sourceRoot":"","sources":["../../../src/tool-calls/reporters/file-edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,cAAc,EAEf,MAAM,6CAA6C,CAAC;AAQrD,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,YAAY,EACb,MAAM,aAAa,CAAC;AAErB;;;GAGG;AACH,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,cAAc,GAAG,YAAY;IAkCpF,UAAU,IAAI,eAAe;IAI7B,UAAU,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAGrF;AAED,yFAAyF;AACzF,KAAK,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAI9E;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,GAAG,SAAS,CAYpF;AAED,qDAAqD;AACrD,qBAAa,YAAa,YAAW,YAAY;IAC/C,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,cAAc,GAAG,YAAY;IA0B5E,UAAU,IAAI,eAAe;IAI7B,UAAU,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAGrF;AAyCD;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,cAAc,GAAG,YAAY;IAsB5E;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,eAAe;CAMxD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { patchUpdateFromDiffToolResponse, toolUpdateFromDiffToolResponse } from "../../diff.js";
|
|
1
|
+
import { patchUpdateFromDiffToolResponse, toolUpdateFromDiffToolResponse, v2UpdateFromDiffToolResponse, } from "../../diff.js";
|
|
2
2
|
import { markdownEscape, resultText, textContent, toDisplayPath } from "../content.js";
|
|
3
3
|
/**
|
|
4
4
|
* Write: the diff holds the file text. The PostToolUse hook sends the final
|
|
@@ -14,12 +14,13 @@ export class WriteReporter {
|
|
|
14
14
|
locations: write?.file_path ? [{ path: write.file_path }] : [],
|
|
15
15
|
};
|
|
16
16
|
if (write?.file_path) {
|
|
17
|
-
// A negotiated client
|
|
18
|
-
// from the PostToolUse hook. The input does not tell
|
|
19
|
-
// exists, so the live tool call shows no diff: a diff
|
|
20
|
-
// text would claim a creation. The adapter reads the
|
|
21
|
-
// preview. A replay has no preview and no hook, so it
|
|
22
|
-
|
|
17
|
+
// A negotiated client and a v2 client get the exact patch from the
|
|
18
|
+
// approval preview or from the PostToolUse hook. The input does not tell
|
|
19
|
+
// whether the file exists, so the live tool call shows no diff: a diff
|
|
20
|
+
// without the old text would claim a creation. The adapter reads the
|
|
21
|
+
// file only for the preview. A replay has no preview and no hook, so it
|
|
22
|
+
// keeps the diff.
|
|
23
|
+
if (!(capabilities.diffPatch || capabilities.v2) || replay) {
|
|
23
24
|
facts.change = [
|
|
24
25
|
{
|
|
25
26
|
type: "diff",
|
|
@@ -68,7 +69,7 @@ export function normalizeWriteInput(input) {
|
|
|
68
69
|
}
|
|
69
70
|
/** Edit: the diff holds the old and the new text. */
|
|
70
71
|
export class EditReporter {
|
|
71
|
-
toolUse(input, { cwd }) {
|
|
72
|
+
toolUse(input, { cwd, capabilities }) {
|
|
72
73
|
const edit = input;
|
|
73
74
|
const displayPath = edit?.file_path ? toDisplayPath(edit.file_path, cwd) : undefined;
|
|
74
75
|
const facts = {
|
|
@@ -76,10 +77,11 @@ export class EditReporter {
|
|
|
76
77
|
kind: "edit",
|
|
77
78
|
locations: edit?.file_path ? [{ path: edit.file_path }] : [],
|
|
78
79
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
// The standard diff, also for a client that negotiated patches: the input
|
|
81
|
+
// holds a snippet, not the file, so a patch would need line numbers that
|
|
82
|
+
// the adapter does not know here. A v2 diff cannot hold a snippet, so a v2
|
|
83
|
+
// client gets the patch from the PostToolUse hook alone.
|
|
84
|
+
if (edit?.file_path && (edit.old_string || edit.new_string) && !capabilities.v2) {
|
|
83
85
|
facts.change = [
|
|
84
86
|
{
|
|
85
87
|
type: "diff",
|
|
@@ -104,12 +106,14 @@ export class EditReporter {
|
|
|
104
106
|
* PostToolUse `tool_response`. For Write it replaces the optimistic creation
|
|
105
107
|
* diff with the real diff of an updated file. A negotiated client gets an
|
|
106
108
|
* exact git patch built from the written file, or the standard diff when none
|
|
107
|
-
* can be built.
|
|
109
|
+
* can be built. A v2 client gets a v2 diff.
|
|
108
110
|
*/
|
|
109
111
|
async function finalChange(toolResponse, { capabilities }) {
|
|
112
|
+
if (capabilities.v2)
|
|
113
|
+
return v2UpdateFromDiffToolResponse(toolResponse);
|
|
110
114
|
if (!capabilities.diffPatch)
|
|
111
115
|
return toolUpdateFromDiffToolResponse(toolResponse);
|
|
112
|
-
const patch = await patchUpdateFromDiffToolResponse(toolResponse);
|
|
116
|
+
const patch = await patchUpdateFromDiffToolResponse(toolResponse, "air");
|
|
113
117
|
if (patch)
|
|
114
118
|
return patch;
|
|
115
119
|
const created = createdFileDiff(toolResponse);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A prompt turn as the adapter understands it, independent of the ACP version.
|
|
3
|
+
*
|
|
4
|
+
* The agent reports each turn through {@link TurnEvents}, and each ACP version
|
|
5
|
+
* maps the events to its own messages. ACP v1 answers `session/prompt` when the
|
|
6
|
+
* turn ends or fails. ACP v2 answers it when the prompt is inserted, and
|
|
7
|
+
* reports the rest as session state.
|
|
8
|
+
*
|
|
9
|
+
* The agent calls these synchronously while it updates its own state, so a
|
|
10
|
+
* handler must not throw, nor call back into the agent before it returns.
|
|
11
|
+
*/
|
|
12
|
+
export interface TurnEvents {
|
|
13
|
+
/**
|
|
14
|
+
* Claude Code took the prompt in as the user message `messageId` of the
|
|
15
|
+
* conversation. Reported at most once per turn, and never after
|
|
16
|
+
* {@link ended} or {@link failed}.
|
|
17
|
+
*
|
|
18
|
+
* When Claude Code reports command lifecycles (`msg_lifecycle_v1`), that is
|
|
19
|
+
* when the prompt starts a turn, before the turn's output. Otherwise, and
|
|
20
|
+
* while another turn is still active (held for its background work,
|
|
21
|
+
* steered, or cancelled but not yet ended), it is the prompt's echo, after
|
|
22
|
+
* that turn ended; for a command Claude Code does not echo (such as
|
|
23
|
+
* `/compact`), it is the command's result, after any output it streamed.
|
|
24
|
+
*/
|
|
25
|
+
inserted(messageId: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* The turn waits for the user: a permission request or a question is open in
|
|
28
|
+
* the session. Requests do not say which prompt they are for, so this
|
|
29
|
+
* includes one opened before the turn was inserted. Only reported after
|
|
30
|
+
* {@link inserted}.
|
|
31
|
+
*/
|
|
32
|
+
awaitingUser(): void;
|
|
33
|
+
/**
|
|
34
|
+
* The turn works again: no request is open any more. A turn that ends or
|
|
35
|
+
* fails while it waits reports that instead.
|
|
36
|
+
*/
|
|
37
|
+
resumed(): void;
|
|
38
|
+
/**
|
|
39
|
+
* The turn ended. A queued turn that is cancelled before it is inserted
|
|
40
|
+
* ends too, without {@link inserted}; Claude Code may still run its prompt.
|
|
41
|
+
*/
|
|
42
|
+
ended(outcome: TurnOutcome): void;
|
|
43
|
+
/**
|
|
44
|
+
* The turn failed. Without {@link inserted} before, Claude Code was not seen
|
|
45
|
+
* taking the prompt in. `title`, when the agent has one, describes the
|
|
46
|
+
* failure for the user in plain text, such as "API Error: 529 Overloaded".
|
|
47
|
+
*/
|
|
48
|
+
failed(error: unknown, title?: string): void;
|
|
49
|
+
}
|
|
50
|
+
/** Why a turn ended. Every ACP version has these reasons. */
|
|
51
|
+
export type StopReason = "end_turn" | "max_tokens" | "max_turn_requests" | "refusal" | "cancelled";
|
|
52
|
+
/** How a turn ended. */
|
|
53
|
+
export type TurnOutcome = {
|
|
54
|
+
stopReason: StopReason;
|
|
55
|
+
/** The tokens that the main agent loop of the turn used. */
|
|
56
|
+
usage?: TurnUsage;
|
|
57
|
+
/**
|
|
58
|
+
* Extension data for the client, such as the quota breakdown of the turn
|
|
59
|
+
* and a terminal session failure.
|
|
60
|
+
*/
|
|
61
|
+
_meta?: Record<string, unknown>;
|
|
62
|
+
};
|
|
63
|
+
export type TurnUsage = {
|
|
64
|
+
totalTokens: number;
|
|
65
|
+
inputTokens: number;
|
|
66
|
+
outputTokens: number;
|
|
67
|
+
thoughtTokens?: number;
|
|
68
|
+
cachedReadTokens?: number;
|
|
69
|
+
cachedWriteTokens?: number;
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=turn-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-events.d.ts","sourceRoot":"","sources":["../src/turn-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;;OAKG;IACH,YAAY,IAAI,IAAI,CAAC;IACrB;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9C;AAED,6DAA6D;AAC7D,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,mBAAmB,GAAG,SAAS,GAAG,WAAW,CAAC;AAEnG,wBAAwB;AACxB,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,UAAU,CAAC;IACvB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/usage-markdown.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { SDKControlGetUsageResponse } from "@anthropic-ai/claude-agent-sdk"
|
|
|
2
2
|
/** Validate the experimental SDK response at the runtime boundary. */
|
|
3
3
|
export declare function parseUsageResponse(value: unknown): SDKControlGetUsageResponse | null;
|
|
4
4
|
export declare function isUsageCommandText(text: string): boolean;
|
|
5
|
+
/** Escape the Markdown syntax characters of `value` and fold its line breaks. */
|
|
6
|
+
export declare function escapeMarkdown(value: string): string;
|
|
5
7
|
/** Render the SDK's structured `/usage` response as Markdown. */
|
|
6
8
|
export declare function formatUsageResponse(usage: SDKControlGetUsageResponse): string;
|
|
7
9
|
//# sourceMappingURL=usage-markdown.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage-markdown.d.ts","sourceRoot":"","sources":["../src/usage-markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AA8DjF,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,GAAG,IAAI,CAMpF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;
|
|
1
|
+
{"version":3,"file":"usage-markdown.d.ts","sourceRoot":"","sources":["../src/usage-markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AA8DjF,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,GAAG,IAAI,CAMpF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AASD,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AA+DD,iEAAiE;AACjE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,CAiE7E"}
|
package/dist/usage-markdown.js
CHANGED
|
@@ -71,7 +71,8 @@ function usageBar(percent) {
|
|
|
71
71
|
const filled = clamped === 0 ? 0 : Math.max(1, Math.round((clamped / 100) * cells));
|
|
72
72
|
return `${"█".repeat(filled)}${"░".repeat(cells - filled)}`;
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
/** Escape the Markdown syntax characters of `value` and fold its line breaks. */
|
|
75
|
+
export function escapeMarkdown(value) {
|
|
75
76
|
return value.replace(/([\\`*_[\]<>|])/g, "\\$1").replace(/[\r\n]+/g, " ");
|
|
76
77
|
}
|
|
77
78
|
function formatDuration(milliseconds) {
|
package/dist/utils.d.ts
CHANGED
|
@@ -13,4 +13,16 @@ export declare function nodeToWebWritable(nodeStream: Writable): WritableStream<
|
|
|
13
13
|
export declare function nodeToWebReadable(nodeStream: Readable): ReadableStream<Uint8Array>;
|
|
14
14
|
export declare function unreachable(value: never, logger?: Logger): void;
|
|
15
15
|
export declare function sleep(time: number): Promise<void>;
|
|
16
|
+
/** The outcome of {@link raceTimeoutAndAbort}. */
|
|
17
|
+
export type RaceOutcome<T> = {
|
|
18
|
+
type: "done";
|
|
19
|
+
value: T;
|
|
20
|
+
} | {
|
|
21
|
+
type: "timeout";
|
|
22
|
+
} | {
|
|
23
|
+
type: "aborted";
|
|
24
|
+
};
|
|
25
|
+
/** Wait for `promise` until `timeoutMs` passes or `signal` aborts. A rejection
|
|
26
|
+
* of `promise` rejects the result. The timer does not keep the process alive. */
|
|
27
|
+
export declare function raceTimeoutAndAbort<T>(promise: Promise<T>, timeoutMs: number, signal: AbortSignal): Promise<RaceOutcome<T>>;
|
|
16
28
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,qBAAa,QAAQ,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,IAAI,CAAS;IAErB,IAAI,CAAC,IAAI,EAAE,CAAC;IASZ,GAAG;IAQH,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;CAgB3C;AAGD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAclF;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAUlF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,MAAgB,QAQjE;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjD"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,qBAAa,QAAQ,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,IAAI,CAAS;IAErB,IAAI,CAAC,IAAI,EAAE,CAAC;IASZ,GAAG;IAQH,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;CAgB3C;AAGD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAclF;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAUlF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,MAAgB,QAQjE;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjD;AAED,kDAAkD;AAClD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEpG;kFACkF;AAClF,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAoBzB"}
|
package/dist/utils.js
CHANGED
|
@@ -79,3 +79,31 @@ export function unreachable(value, logger = console) {
|
|
|
79
79
|
export function sleep(time) {
|
|
80
80
|
return new Promise((resolve) => setTimeout(resolve, time));
|
|
81
81
|
}
|
|
82
|
+
/** Wait for `promise` until `timeoutMs` passes or `signal` aborts. A rejection
|
|
83
|
+
* of `promise` rejects the result. The timer does not keep the process alive. */
|
|
84
|
+
export async function raceTimeoutAndAbort(promise, timeoutMs, signal) {
|
|
85
|
+
let timeout;
|
|
86
|
+
let onAbort;
|
|
87
|
+
try {
|
|
88
|
+
return await Promise.race([
|
|
89
|
+
promise.then((value) => ({ type: "done", value })),
|
|
90
|
+
new Promise((resolve) => {
|
|
91
|
+
timeout = setTimeout(() => resolve({ type: "timeout" }), timeoutMs);
|
|
92
|
+
timeout.unref?.();
|
|
93
|
+
}),
|
|
94
|
+
new Promise((resolve) => {
|
|
95
|
+
onAbort = () => resolve({ type: "aborted" });
|
|
96
|
+
if (signal.aborted)
|
|
97
|
+
onAbort();
|
|
98
|
+
else
|
|
99
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
100
|
+
}),
|
|
101
|
+
]);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
if (timeout)
|
|
105
|
+
clearTimeout(timeout);
|
|
106
|
+
if (onAbort)
|
|
107
|
+
signal.removeEventListener("abort", onAbort);
|
|
108
|
+
}
|
|
109
|
+
}
|
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":"AAcA,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAkB,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAc9E;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GACvC,EAAE,CAAC,QAAQ,CAoCb"}
|
package/dist/v2/agent.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as v2 from "@agentclientprotocol/sdk/experimental/v2";
|
|
2
2
|
import { ClaudeAcpAgent } from "../acp-agent.js";
|
|
3
3
|
import { v1InitializeRequest, v2InitializeResponse } from "./initialize.js";
|
|
4
|
+
import { v2Prompt } from "./prompt.js";
|
|
4
5
|
import { v1NewSessionRequest, v1ResumeSessionRequest, v1SetSessionConfigOptionRequest, v2ConfigOptions, v2NewSessionResponse, v2ResumeSessionResponse, } from "./session.js";
|
|
5
6
|
import { v2SessionUpdate } from "./session-update.js";
|
|
6
7
|
/**
|
|
@@ -12,10 +13,12 @@ import { v2SessionUpdate } from "./session-update.js";
|
|
|
12
13
|
*/
|
|
13
14
|
export function v2AgentApp(logger, onAgent) {
|
|
14
15
|
let agent;
|
|
16
|
+
let client;
|
|
15
17
|
return v2
|
|
16
18
|
.agent({ name: "claude-code-acp" })
|
|
17
19
|
.onConnect((connection) => {
|
|
18
|
-
|
|
20
|
+
client = new V2ClientConnection(connection.client, logger ?? console);
|
|
21
|
+
agent = new ClaudeAcpAgent(client, logger, { v2: true });
|
|
19
22
|
onAgent(agent);
|
|
20
23
|
})
|
|
21
24
|
.onRequest(v2.methods.agent.initialize, async ({ params }) => v2InitializeResponse(await agent.initialize(v1InitializeRequest(params))))
|
|
@@ -29,19 +32,38 @@ export function v2AgentApp(logger, onAgent) {
|
|
|
29
32
|
.onRequest(v2.methods.agent.session.setConfigOption, async ({ params }) => {
|
|
30
33
|
const { configOptions, ...response } = await agent.setSessionConfigOption(v1SetSessionConfigOptionRequest(params));
|
|
31
34
|
return { ...response, configOptions: v2ConfigOptions(configOptions) };
|
|
32
|
-
})
|
|
35
|
+
})
|
|
36
|
+
.onRequest(v2.methods.agent.session.prompt, ({ params }) => v2Prompt(agent, params, (update) => {
|
|
37
|
+
void client.send({ sessionId: params.sessionId, update });
|
|
38
|
+
}))
|
|
39
|
+
.onNotification(v2.methods.agent.session.cancel, ({ params }) => agent.cancel(params));
|
|
33
40
|
}
|
|
34
41
|
/**
|
|
35
42
|
* The {@link AcpClient} of an ACP v2 connection: it sends the v1 messages of
|
|
36
43
|
* the agent as v2 messages.
|
|
37
44
|
*
|
|
38
|
-
*
|
|
39
|
-
* only a prompt turn makes, still reject.
|
|
45
|
+
* Permission requests still reject until the v2 surface translates them.
|
|
40
46
|
*/
|
|
41
47
|
class V2ClientConnection {
|
|
42
48
|
ctx;
|
|
43
|
-
|
|
49
|
+
logger;
|
|
50
|
+
constructor(ctx, logger) {
|
|
44
51
|
this.ctx = ctx;
|
|
52
|
+
this.logger = logger;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Sends a v2 session update, and logs rather than rejects when that fails.
|
|
56
|
+
* It and {@link sessionUpdate} hand updates to the connection synchronously,
|
|
57
|
+
* so they go out in the order of the calls. The agent's consumer awaits each
|
|
58
|
+
* of its updates before it reports the next turn event, so a turn's state
|
|
59
|
+
* follows the output that came before it. (An update the consumer is still
|
|
60
|
+
* routing can be overtaken by an event from elsewhere, such as a permission
|
|
61
|
+
* request opening.)
|
|
62
|
+
*/
|
|
63
|
+
send(notification) {
|
|
64
|
+
return this.ctx.notify(v2.methods.client.session.update, notification).catch((error) => {
|
|
65
|
+
this.logger.error(`Failed to send a ${notification.update.sessionUpdate} update:`, error);
|
|
66
|
+
});
|
|
45
67
|
}
|
|
46
68
|
extNotification(method, params) {
|
|
47
69
|
if (!isExtensionMethod(method)) {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACP v2 `session/prompt`, served through the turn events of `ClaudeAcpAgent`.
|
|
3
|
+
*
|
|
4
|
+
* v2 answers the prompt once Claude Code takes it in, with the id of the user
|
|
5
|
+
* message it became, and reports the rest of the turn as `state_update`s.
|
|
6
|
+
*/
|
|
7
|
+
import { type PromptRequest } from "@agentclientprotocol/sdk";
|
|
8
|
+
import * as v2 from "@agentclientprotocol/sdk/experimental/v2";
|
|
9
|
+
import type { ClaudeAcpAgent } from "../acp-agent.js";
|
|
10
|
+
/**
|
|
11
|
+
* Starts a turn for `params`, and answers when Claude Code takes the prompt
|
|
12
|
+
* in. `send` delivers a session update of the prompt's session; it must not
|
|
13
|
+
* throw, and must deliver updates in the order it is called, also relative to
|
|
14
|
+
* the agent's own updates.
|
|
15
|
+
*
|
|
16
|
+
* - Taken in: the prompt's `user_message`, then `running`, then the answer.
|
|
17
|
+
* - Waiting on a permission request or a question: `requires_action`, and
|
|
18
|
+
* `running` once none is open.
|
|
19
|
+
* - Ended: `idle` with the stop reason, usage, and `_meta` of the turn.
|
|
20
|
+
* - Failed after it was taken in: `idle` with {@link FAILED_STOP_REASON} and
|
|
21
|
+
* the JSON-RPC error that v1 answers with in `_meta.claudeCode.error`, and
|
|
22
|
+
* an error `notice` before it that shows the failure. v2 has no error on
|
|
23
|
+
* `idle`, and a notice must not be the only report of a failure.
|
|
24
|
+
* - Ended or failed before it was taken in: the answer is a JSON-RPC error,
|
|
25
|
+
* `-32800` for a prompt that a cancel ended in the queue.
|
|
26
|
+
*
|
|
27
|
+
* Deferred decision: a turn whose background subagents still run ends only
|
|
28
|
+
* when they finish (`Turn.deferredSettle`), so `idle` waits for them too. The
|
|
29
|
+
* hold exists for v1, whose clients stop listening at the prompt response.
|
|
30
|
+
* v2 allows background updates while idle, so it could report `idle` at the
|
|
31
|
+
* turn's result instead, with a turn event for the hold. To be decided with
|
|
32
|
+
* async tasks (step 6 in `docs/acp-v2.md`).
|
|
33
|
+
*/
|
|
34
|
+
export declare function v2Prompt(agent: ClaudeAcpAgent, params: v2.PromptRequest, send: (update: v2.SessionUpdate) => void): Promise<v2.PromptResponse>;
|
|
35
|
+
/**
|
|
36
|
+
* The stop reason of a turn that failed after it was taken in. v2's stop
|
|
37
|
+
* reasons all describe work that ended without an error, and a custom one
|
|
38
|
+
* starts with `_`; clients show it as a generic stop.
|
|
39
|
+
*/
|
|
40
|
+
export declare const FAILED_STOP_REASON = "_error";
|
|
41
|
+
/**
|
|
42
|
+
* A v2 prompt may hold content blocks that v1 cannot express: a custom one,
|
|
43
|
+
* or one from a newer ACP version. Clients send only the kinds the agent
|
|
44
|
+
* advertised, so such a block is rejected rather than silently dropped.
|
|
45
|
+
*/
|
|
46
|
+
export declare function v1PromptRequest({ prompt, ...request }: v2.PromptRequest): PromptRequest;
|
|
47
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/v2/prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAIL,KAAK,aAAa,EAEnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAItD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,EAAE,CAAC,aAAa,EACxB,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,KAAK,IAAI,GACvC,OAAO,CAAC,EAAE,CAAC,cAAc,CAAC,CA0D5B;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,WAAW,CAAC;AA2B3C;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE,CAAC,aAAa,GAAG,aAAa,CAEvF"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACP v2 `session/prompt`, served through the turn events of `ClaudeAcpAgent`.
|
|
3
|
+
*
|
|
4
|
+
* v2 answers the prompt once Claude Code takes it in, with the id of the user
|
|
5
|
+
* message it became, and reports the rest of the turn as `state_update`s.
|
|
6
|
+
*/
|
|
7
|
+
import { RequestError, } from "@agentclientprotocol/sdk";
|
|
8
|
+
import * as v2 from "@agentclientprotocol/sdk/experimental/v2";
|
|
9
|
+
import { splitNoticeText } from "../session-notices.js";
|
|
10
|
+
/**
|
|
11
|
+
* Starts a turn for `params`, and answers when Claude Code takes the prompt
|
|
12
|
+
* in. `send` delivers a session update of the prompt's session; it must not
|
|
13
|
+
* throw, and must deliver updates in the order it is called, also relative to
|
|
14
|
+
* the agent's own updates.
|
|
15
|
+
*
|
|
16
|
+
* - Taken in: the prompt's `user_message`, then `running`, then the answer.
|
|
17
|
+
* - Waiting on a permission request or a question: `requires_action`, and
|
|
18
|
+
* `running` once none is open.
|
|
19
|
+
* - Ended: `idle` with the stop reason, usage, and `_meta` of the turn.
|
|
20
|
+
* - Failed after it was taken in: `idle` with {@link FAILED_STOP_REASON} and
|
|
21
|
+
* the JSON-RPC error that v1 answers with in `_meta.claudeCode.error`, and
|
|
22
|
+
* an error `notice` before it that shows the failure. v2 has no error on
|
|
23
|
+
* `idle`, and a notice must not be the only report of a failure.
|
|
24
|
+
* - Ended or failed before it was taken in: the answer is a JSON-RPC error,
|
|
25
|
+
* `-32800` for a prompt that a cancel ended in the queue.
|
|
26
|
+
*
|
|
27
|
+
* Deferred decision: a turn whose background subagents still run ends only
|
|
28
|
+
* when they finish (`Turn.deferredSettle`), so `idle` waits for them too. The
|
|
29
|
+
* hold exists for v1, whose clients stop listening at the prompt response.
|
|
30
|
+
* v2 allows background updates while idle, so it could report `idle` at the
|
|
31
|
+
* turn's result instead, with a turn event for the hold. To be decided with
|
|
32
|
+
* async tasks (step 6 in `docs/acp-v2.md`).
|
|
33
|
+
*/
|
|
34
|
+
export function v2Prompt(agent, params, send) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
let inserted = false;
|
|
37
|
+
const events = {
|
|
38
|
+
inserted(messageId) {
|
|
39
|
+
inserted = true;
|
|
40
|
+
send({ sessionUpdate: "user_message", messageId, content: params.prompt });
|
|
41
|
+
send({ sessionUpdate: "state_update", state: "running" });
|
|
42
|
+
resolve({ messageId });
|
|
43
|
+
},
|
|
44
|
+
awaitingUser() {
|
|
45
|
+
send({ sessionUpdate: "state_update", state: "requires_action" });
|
|
46
|
+
},
|
|
47
|
+
resumed() {
|
|
48
|
+
send({ sessionUpdate: "state_update", state: "running" });
|
|
49
|
+
},
|
|
50
|
+
ended(outcome) {
|
|
51
|
+
if (inserted) {
|
|
52
|
+
send(idle(outcome));
|
|
53
|
+
}
|
|
54
|
+
else if (outcome.stopReason === "cancelled") {
|
|
55
|
+
reject(RequestError.requestCancelled());
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
reject(RequestError.internalError({ stopReason: outcome.stopReason }, "The turn ended before Claude Code took the prompt in"));
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
failed(error, title) {
|
|
62
|
+
if (!inserted) {
|
|
63
|
+
reject(error);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const failure = jsonRpcError(error);
|
|
67
|
+
send({
|
|
68
|
+
sessionUpdate: "notice",
|
|
69
|
+
severity: "error",
|
|
70
|
+
...splitNoticeText(title ?? failure.message, "The turn failed"),
|
|
71
|
+
});
|
|
72
|
+
send({
|
|
73
|
+
sessionUpdate: "state_update",
|
|
74
|
+
state: "idle",
|
|
75
|
+
stopReason: FAILED_STOP_REASON,
|
|
76
|
+
_meta: { claudeCode: { error: failure } },
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
let request;
|
|
81
|
+
try {
|
|
82
|
+
request = v1PromptRequest(params);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
reject(error);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
agent.startTurn(request, events).catch(reject);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The stop reason of a turn that failed after it was taken in. v2's stop
|
|
93
|
+
* reasons all describe work that ended without an error, and a custom one
|
|
94
|
+
* starts with `_`; clients show it as a generic stop.
|
|
95
|
+
*/
|
|
96
|
+
export const FAILED_STOP_REASON = "_error";
|
|
97
|
+
/** The JSON-RPC error that v1 answers a failed prompt with. */
|
|
98
|
+
function jsonRpcError(error) {
|
|
99
|
+
if (error instanceof RequestError) {
|
|
100
|
+
return {
|
|
101
|
+
code: error.code,
|
|
102
|
+
message: error.message,
|
|
103
|
+
...(error.data !== undefined ? { data: error.data } : {}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
code: RequestError.internalError().code,
|
|
108
|
+
message: error instanceof Error ? error.message : String(error),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function idle({ stopReason, usage, _meta }) {
|
|
112
|
+
return {
|
|
113
|
+
sessionUpdate: "state_update",
|
|
114
|
+
state: "idle",
|
|
115
|
+
stopReason,
|
|
116
|
+
...(usage ? { usage } : {}),
|
|
117
|
+
...(_meta ? { _meta } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A v2 prompt may hold content blocks that v1 cannot express: a custom one,
|
|
122
|
+
* or one from a newer ACP version. Clients send only the kinds the agent
|
|
123
|
+
* advertised, so such a block is rejected rather than silently dropped.
|
|
124
|
+
*/
|
|
125
|
+
export function v1PromptRequest({ prompt, ...request }) {
|
|
126
|
+
return { ...request, prompt: prompt.map(v1ContentBlock) };
|
|
127
|
+
}
|
|
128
|
+
function v1ContentBlock(block) {
|
|
129
|
+
if (v2.ContentBlock.isText(block) ||
|
|
130
|
+
v2.ContentBlock.isImage(block) ||
|
|
131
|
+
v2.ContentBlock.isAudio(block) ||
|
|
132
|
+
v2.ContentBlock.isResourceLink(block) ||
|
|
133
|
+
v2.ContentBlock.isResource(block)) {
|
|
134
|
+
const { annotations, ...rest } = block;
|
|
135
|
+
return annotations != null ? { ...rest, annotations: v1Annotations(annotations) } : rest;
|
|
136
|
+
}
|
|
137
|
+
throw RequestError.invalidParams({ type: block.type }, `Content blocks of type ${block.type} are not supported`);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* v2 also accepts audiences of a newer ACP version; v1 knows two. An audience
|
|
141
|
+
* of only unknown roles is dropped rather than narrowed to nobody.
|
|
142
|
+
*/
|
|
143
|
+
function v1Annotations({ audience, ...annotations }) {
|
|
144
|
+
if (audience == null)
|
|
145
|
+
return annotations;
|
|
146
|
+
const known = audience.filter(isV1Role);
|
|
147
|
+
return known.length > 0 || audience.length === 0
|
|
148
|
+
? { ...annotations, audience: known }
|
|
149
|
+
: annotations;
|
|
150
|
+
}
|
|
151
|
+
function isV1Role(role) {
|
|
152
|
+
return role === "assistant" || role === "user";
|
|
153
|
+
}
|
|
@@ -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;
|
|
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;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GACvC,EAAE,CAAC,aAAa,GAAG,SAAS,CAiC9B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { v2AvailableCommands, v2ConfigOptions } from "./session.js";
|
|
2
|
+
import { v2ToolCallUpdate } from "./tool-call.js";
|
|
2
3
|
/**
|
|
3
4
|
* The v2 form of a v1 session update, or `undefined` when v2 has no
|
|
4
5
|
* counterpart and the update tells a v2 client nothing new.
|
|
@@ -16,6 +17,19 @@ export function v2SessionUpdate(update) {
|
|
|
16
17
|
case "usage_update":
|
|
17
18
|
case "notice":
|
|
18
19
|
return update;
|
|
20
|
+
case "agent_message_chunk":
|
|
21
|
+
case "agent_thought_chunk":
|
|
22
|
+
case "user_message_chunk": {
|
|
23
|
+
// v2 requires the id of the message a chunk belongs to; v1 allows none.
|
|
24
|
+
const { messageId, ...chunk } = update;
|
|
25
|
+
if (!messageId) {
|
|
26
|
+
throw new Error(`An ACP v2 ${update.sessionUpdate} needs a messageId`);
|
|
27
|
+
}
|
|
28
|
+
return { ...chunk, messageId };
|
|
29
|
+
}
|
|
30
|
+
case "tool_call":
|
|
31
|
+
case "tool_call_update":
|
|
32
|
+
return v2ToolCallUpdate(update);
|
|
19
33
|
case "current_mode_update":
|
|
20
34
|
// v2 has no modes: the mode is the `mode` config option. Every mode
|
|
21
35
|
// change also reaches the client as a `config_option_update` or in the
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The v2 form of the tool call reports that `ClaudeAcpAgent` sends as v1
|
|
3
|
+
* `tool_call` and `tool_call_update` updates.
|
|
4
|
+
*/
|
|
5
|
+
import type { ToolCall, ToolCallUpdate } from "@agentclientprotocol/sdk";
|
|
6
|
+
import type * as v2 from "@agentclientprotocol/sdk/experimental/v2";
|
|
7
|
+
type V1ToolCallReport = (ToolCall & {
|
|
8
|
+
sessionUpdate: "tool_call";
|
|
9
|
+
}) | (ToolCallUpdate & {
|
|
10
|
+
sessionUpdate: "tool_call_update";
|
|
11
|
+
});
|
|
12
|
+
type V2ToolCallUpdate = v2.ToolCallUpdate & {
|
|
13
|
+
sessionUpdate: "tool_call_update";
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The v2 form of a v1 tool call report.
|
|
17
|
+
*
|
|
18
|
+
* v1 reports a new tool call with `tool_call` and changes it with
|
|
19
|
+
* `tool_call_update`; v2 has one `tool_call_update` that upserts by
|
|
20
|
+
* `toolCallId`. Both leave a field that a report omits unchanged, and both
|
|
21
|
+
* replace `content` and `locations` as whole arrays. They differ on `null`:
|
|
22
|
+
* in v1 it leaves the field unchanged too, in v2 it clears the field, so `null`
|
|
23
|
+
* fields are left out.
|
|
24
|
+
*
|
|
25
|
+
* Diffs need what a v1 diff does not say, so the agent builds them in v2 form
|
|
26
|
+
* for a v2 client (`v2DiffContent`), and they pass as they are. Terminal
|
|
27
|
+
* content throws until the v2 surface translates it (step 4 of
|
|
28
|
+
* `docs/acp-v2.md`).
|
|
29
|
+
*/
|
|
30
|
+
export declare function v2ToolCallUpdate(report: V1ToolCallReport): V2ToolCallUpdate;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=tool-call.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-call.d.ts","sourceRoot":"","sources":["../../src/v2/tool-call.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAmB,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,KAAK,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAEpE,KAAK,gBAAgB,GACjB,CAAC,QAAQ,GAAG;IAAE,aAAa,EAAE,WAAW,CAAA;CAAE,CAAC,GAC3C,CAAC,cAAc,GAAG;IAAE,aAAa,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAAC;AAE7D,KAAK,gBAAgB,GAAG,EAAE,CAAC,cAAc,GAAG;IAAE,aAAa,EAAE,kBAAkB,CAAA;CAAE,CAAC;AAElF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAW3E"}
|