@agentclientprotocol/claude-agent-acp 0.85.1 → 0.85.2-preview.10

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.
Files changed (45) hide show
  1. package/README.md +1 -0
  2. package/dist/acp-agent.d.ts +101 -22
  3. package/dist/acp-agent.d.ts.map +1 -1
  4. package/dist/acp-agent.js +450 -171
  5. package/dist/air-extension.d.ts +2 -0
  6. package/dist/air-extension.d.ts.map +1 -1
  7. package/dist/air-extension.js +2 -0
  8. package/dist/mcp-command.d.ts +51 -0
  9. package/dist/mcp-command.d.ts.map +1 -0
  10. package/dist/mcp-command.js +321 -0
  11. package/dist/resumed-session.d.ts +16 -6
  12. package/dist/resumed-session.d.ts.map +1 -1
  13. package/dist/resumed-session.js +91 -25
  14. package/dist/serve.d.ts.map +1 -1
  15. package/dist/serve.js +1 -2
  16. package/dist/session-mode.d.ts +14 -0
  17. package/dist/session-mode.d.ts.map +1 -1
  18. package/dist/session-mode.js +22 -0
  19. package/dist/turn-events.d.ts +71 -0
  20. package/dist/turn-events.d.ts.map +1 -0
  21. package/dist/turn-events.js +1 -0
  22. package/dist/usage-markdown.d.ts +2 -0
  23. package/dist/usage-markdown.d.ts.map +1 -1
  24. package/dist/usage-markdown.js +2 -1
  25. package/dist/utils.d.ts +12 -0
  26. package/dist/utils.d.ts.map +1 -1
  27. package/dist/utils.js +28 -0
  28. package/dist/v2/agent.d.ts +0 -7
  29. package/dist/v2/agent.d.ts.map +1 -1
  30. package/dist/v2/agent.js +56 -23
  31. package/dist/v2/initialize.d.ts.map +1 -1
  32. package/dist/v2/initialize.js +4 -2
  33. package/dist/v2/prompt.d.ts +47 -0
  34. package/dist/v2/prompt.d.ts.map +1 -0
  35. package/dist/v2/prompt.js +153 -0
  36. package/dist/v2/session-update.d.ts +15 -0
  37. package/dist/v2/session-update.d.ts.map +1 -0
  38. package/dist/v2/session-update.js +41 -0
  39. package/dist/v2/session.d.ts +23 -0
  40. package/dist/v2/session.d.ts.map +1 -0
  41. package/dist/v2/session.js +96 -0
  42. package/dist/v2/tool-call.d.ts +30 -0
  43. package/dist/v2/tool-call.d.ts.map +1 -0
  44. package/dist/v2/tool-call.js +30 -0
  45. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"session-mode.d.ts","sourceRoot":"","sources":["../src/session-mode.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC5B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,KAAK,EACN,MAAM,gCAAgC,CAAC;AASxC,eAAO,MAAM,cAAc,SAAS,CAAC;AACrC,eAAO,MAAM,kBAAkB,EAAE,cAA8B,CAAC;AAYhE,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,8EAA8E;IAC9E,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,gFAAgF;IAChF,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,yBAAyB,CAAC,CAAC,SAAS,WAAW,IAAI;IAC7D,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;IAC7C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtF,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D;;oCAEgC;IAChC,eAAe,CAAC,IAAI,OAAO,CAAC;IAC5B,QAAQ,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACnC,2FAA2F;IAC3F,SAAS,CAAC,IAAI,OAAO,CAAC;CACvB,CAAC;AAEF,KAAK,iBAAiB,GAAG,WAAW,GAAG;IACrC,aAAa,EAAE,mBAAmB,EAAE,CAAC;CACtC,CAAC;AAEF,KAAK,2BAA2B,GAAG;IACjC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;IACxC,aAAa,EAAE,cAAc,CAAC;IAC9B,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,4EAA4E;AAC5E,qBAAa,kBAAkB,CAAC,CAAC,SAAS,WAAW;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAE5D,UAAU,CAAC,EACf,KAAK,EACL,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,WAAW,GACZ,EAAE,2BAA2B,GAAG,OAAO,CAAC;QACvC,KAAK,EAAE,gBAAgB,CAAC;QACxB,8BAA8B,EAAE,OAAO,CAAC;KACzC,CAAC;IAgCF,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,mBAAmB;IAiBjE,UAAU,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAe1D,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,EAAE;IAInD,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,GAAG,cAAc;IAMlF,uBAAuB,CACrB,OAAO,EAAE,WAAW,EACpB,gBAAgB,EAAE,gBAAgB,GACjC;QAAE,gBAAgB,EAAE,gBAAgB,CAAC;QAAC,eAAe,EAAE,OAAO,CAAA;KAAE;IAwBnE;sDACkD;IAC5C,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS;IAqBpE,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAyBtE,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAS9E,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9D,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9E,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlF,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,mBAAmB;YA4Cb,WAAW;CAW1B"}
1
+ {"version":3,"file":"session-mode.d.ts","sourceRoot":"","sources":["../src/session-mode.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { RequestError, } from "@agentclientprotocol/sdk";
2
2
  import { AIR_KIND_KEY, airOnlyMeta } from "./air-extension.js";
3
+ import { resolvePermissionMode } from "./permissions/modes.js";
3
4
  import { noticeOrTranscriptUpdate, noticeTranscriptText, sentenceCase, } from "./session-notices.js";
4
5
  export const MODE_CONFIG_ID = "mode";
5
6
  export const AUTO_MODE_FALLBACK = "acceptEdits";
@@ -11,6 +12,27 @@ const AUTO_MODE_FALLBACK_NOTICE = {
11
12
  title: "Auto mode unavailable",
12
13
  description: sentenceCase(AUTO_MODE_FALLBACK_REASON),
13
14
  };
15
+ /**
16
+ * The permission mode a new Claude query starts in.
17
+ *
18
+ * An explicit mode wins. A resume or a load continues in the mode of the last
19
+ * user record of the transcript. If that mode is unknown or not available now,
20
+ * for example bypass, the session starts in `defaultMode` of the settings.
21
+ */
22
+ export function resolveInitialPermissionMode(sources, logger, allowBypass) {
23
+ if (sources.explicit !== undefined) {
24
+ return resolvePermissionMode(sources.explicit, logger, allowBypass);
25
+ }
26
+ if (sources.resumed !== undefined) {
27
+ let rejected = false;
28
+ const mode = resolvePermissionMode(sources.resumed, { error: () => (rejected = true) }, allowBypass);
29
+ if (!rejected)
30
+ return mode;
31
+ logger.error(`Ignoring permission mode "${sources.resumed}" of the resumed transcript: ` +
32
+ `not available in this session.`);
33
+ }
34
+ return resolvePermissionMode(sources.defaultMode, logger, allowBypass);
35
+ }
14
36
  /** Owns session-mode policy and the ACP/SDK synchronization it requires. */
15
37
  export class SessionModeManager {
16
38
  options;
@@ -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 {};
@@ -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;AA0ED,iEAAiE;AACjE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,CAiE7E"}
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"}
@@ -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
- function escapeMarkdown(value) {
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
@@ -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
+ }
@@ -1,10 +1,3 @@
1
- /**
2
- * The experimental draft ACP v2 surface of the adapter.
3
- *
4
- * `ClaudeAcpAgent` speaks ACP v1 types. This surface translates each v2
5
- * request into the v1 request that the agent serves, and each v1 message that
6
- * the agent sends into its v2 form, so v1 and v2 share one implementation.
7
- */
8
1
  import * as v2 from "@agentclientprotocol/sdk/experimental/v2";
9
2
  import { ClaudeAcpAgent, type Logger } from "../acp-agent.js";
10
3
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/v2/agent.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAkB,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAG9E;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GACvC,EAAE,CAAC,QAAQ,CAab"}
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,13 +1,9 @@
1
- /**
2
- * The experimental draft ACP v2 surface of the adapter.
3
- *
4
- * `ClaudeAcpAgent` speaks ACP v1 types. This surface translates each v2
5
- * request into the v1 request that the agent serves, and each v1 message that
6
- * the agent sends into its v2 form, so v1 and v2 share one implementation.
7
- */
8
1
  import * as v2 from "@agentclientprotocol/sdk/experimental/v2";
9
2
  import { ClaudeAcpAgent } from "../acp-agent.js";
10
3
  import { v1InitializeRequest, v2InitializeResponse } from "./initialize.js";
4
+ import { v2Prompt } from "./prompt.js";
5
+ import { v1NewSessionRequest, v1ResumeSessionRequest, v1SetSessionConfigOptionRequest, v2ConfigOptions, v2NewSessionResponse, v2ResumeSessionResponse, } from "./session.js";
6
+ import { v2SessionUpdate } from "./session-update.js";
11
7
  /**
12
8
  * The ACP v2 surface, for one connection.
13
9
  *
@@ -17,28 +13,57 @@ import { v1InitializeRequest, v2InitializeResponse } from "./initialize.js";
17
13
  */
18
14
  export function v2AgentApp(logger, onAgent) {
19
15
  let agent;
16
+ let client;
20
17
  return v2
21
18
  .agent({ name: "claude-code-acp" })
22
19
  .onConnect((connection) => {
23
- agent = new ClaudeAcpAgent(new V2ClientConnection(connection.client), logger);
20
+ client = new V2ClientConnection(connection.client, logger ?? console);
21
+ agent = new ClaudeAcpAgent(client, logger);
24
22
  onAgent(agent);
25
23
  })
26
24
  .onRequest(v2.methods.agent.initialize, async ({ params }) => v2InitializeResponse(await agent.initialize(v1InitializeRequest(params))))
27
25
  .onRequest(v2.methods.agent.auth.login, ({ params }) => agent.authenticate(params))
28
- .onRequest(v2.methods.agent.auth.logout, ({ params }) => agent.logout(params));
26
+ .onRequest(v2.methods.agent.auth.logout, ({ params }) => agent.logout(params))
27
+ .onRequest(v2.methods.agent.session.new, async ({ params }) => v2NewSessionResponse(await agent.newSession(v1NewSessionRequest(params))))
28
+ .onRequest(v2.methods.agent.session.list, ({ params }) => agent.listSessions(params))
29
+ .onRequest(v2.methods.agent.session.resume, async ({ params }) => v2ResumeSessionResponse(await agent.resumeSession(v1ResumeSessionRequest(params))))
30
+ .onRequest(v2.methods.agent.session.close, ({ params }) => agent.closeSession(params))
31
+ .onRequest(v2.methods.agent.session.delete, ({ params }) => agent.deleteSession(params))
32
+ .onRequest(v2.methods.agent.session.setConfigOption, async ({ params }) => {
33
+ const { configOptions, ...response } = await agent.setSessionConfigOption(v1SetSessionConfigOptionRequest(params));
34
+ return { ...response, configOptions: v2ConfigOptions(configOptions) };
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));
29
40
  }
30
41
  /**
31
42
  * The {@link AcpClient} of an ACP v2 connection: it sends the v1 messages of
32
43
  * the agent as v2 messages.
33
44
  *
34
- * The v2 surface serves no sessions yet, so only connection-level messages
35
- * reach it, such as the `_auth/status_update` extension notification. Every
36
- * session-scoped method rejects until the v2 surface serves sessions.
45
+ * Permission requests still reject until the v2 surface translates them.
37
46
  */
38
47
  class V2ClientConnection {
39
48
  ctx;
40
- constructor(ctx) {
49
+ logger;
50
+ constructor(ctx, logger) {
41
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
+ });
42
67
  }
43
68
  extNotification(method, params) {
44
69
  if (!isExtensionMethod(method)) {
@@ -46,17 +71,28 @@ class V2ClientConnection {
46
71
  }
47
72
  return this.ctx.notify(method, params);
48
73
  }
49
- sessionUpdate() {
50
- return noV2Sessions("session/update");
74
+ async sessionUpdate({ update, ...notification }) {
75
+ const v2Update = v2SessionUpdate(update);
76
+ if (v2Update) {
77
+ await this.ctx.notify(v2.methods.client.session.update, {
78
+ ...notification,
79
+ update: v2Update,
80
+ });
81
+ }
51
82
  }
52
83
  requestPermission() {
53
- return noV2Sessions("session/request_permission");
84
+ return Promise.reject(new Error("The ACP v2 surface does not serve session/request_permission yet"));
54
85
  }
55
- createElicitation() {
56
- return noV2Sessions("elicitation/create");
86
+ // Elicitation is the same in v1 and v2.
87
+ createElicitation(params, signal) {
88
+ return this.ctx.request(v2.methods.client.elicitation.create,
89
+ // The v1 types accept a property schema with any tag; the v2 types accept
90
+ // one only in a received value. As on v1, an MCP server's schema is
91
+ // relayed here unchanged, so this cast does not check it.
92
+ params, { cancellationSignal: signal });
57
93
  }
58
- completeElicitation() {
59
- return noV2Sessions("elicitation/complete");
94
+ completeElicitation(params) {
95
+ return this.ctx.notify(v2.methods.client.elicitation.complete, params);
60
96
  }
61
97
  // v2 has no client file system. The agent never calls these on v2, because
62
98
  // `v1InitializeRequest` reports no `fs` capability.
@@ -70,6 +106,3 @@ class V2ClientConnection {
70
106
  function isExtensionMethod(method) {
71
107
  return method.startsWith("_");
72
108
  }
73
- function noV2Sessions(method) {
74
- return Promise.reject(new Error(`The ACP v2 surface does not serve sessions yet (${method})`));
75
- }
@@ -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,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAE/D,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,GAAG,iBAAiB,CAsBpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,EAAE,CAAC,kBAAkB,CAcxF"}
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,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAE/D,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,GAAG,iBAAiB,CAwBpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,EAAE,CAAC,kBAAkB,CAcxF"}
@@ -20,8 +20,10 @@ export function v1InitializeRequest(request) {
20
20
  // reads, writes, and runs commands itself.
21
21
  fs: { readTextFile: false, writeTextFile: false },
22
22
  terminal: false,
23
- // Every v2 client handles boolean config options; v1 asks for a marker.
24
- session: { configOptions: { boolean: {} } },
23
+ // Every v2 client handles boolean config options and notices; v1 asks
24
+ // for a marker. With notices, the agent reports advisories as notices
25
+ // rather than as transcript messages.
26
+ session: { configOptions: { boolean: {} }, notices: {} },
25
27
  // v1 marks terminal auth support with a boolean, v2 with an object.
26
28
  auth: {
27
29
  terminal: capabilities?.auth?.terminal != null,
@@ -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
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The v2 form of the session updates that `ClaudeAcpAgent` sends as v1
3
+ * updates.
4
+ */
5
+ import * as v2 from "@agentclientprotocol/sdk/experimental/v2";
6
+ import type { AcpSessionNotification } from "../acp-subagents.js";
7
+ /**
8
+ * The v2 form of a v1 session update, or `undefined` when v2 has no
9
+ * counterpart and the update tells a v2 client nothing new.
10
+ *
11
+ * Update kinds that the v2 surface does not translate yet throw, so a gap is
12
+ * an error rather than a silently missing update.
13
+ */
14
+ export declare function v2SessionUpdate(update: AcpSessionNotification["update"]): v2.SessionUpdate | undefined;
15
+ //# sourceMappingURL=session-update.d.ts.map
@@ -0,0 +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;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GACvC,EAAE,CAAC,aAAa,GAAG,SAAS,CAiC9B"}
@@ -0,0 +1,41 @@
1
+ import { v2AvailableCommands, v2ConfigOptions } from "./session.js";
2
+ import { v2ToolCallUpdate } from "./tool-call.js";
3
+ /**
4
+ * The v2 form of a v1 session update, or `undefined` when v2 has no
5
+ * counterpart and the update tells a v2 client nothing new.
6
+ *
7
+ * Update kinds that the v2 surface does not translate yet throw, so a gap is
8
+ * an error rather than a silently missing update.
9
+ */
10
+ export function v2SessionUpdate(update) {
11
+ switch (update.sessionUpdate) {
12
+ case "available_commands_update":
13
+ return { ...update, availableCommands: v2AvailableCommands(update.availableCommands) };
14
+ case "config_option_update":
15
+ return { ...update, configOptions: v2ConfigOptions(update.configOptions) };
16
+ case "session_info_update":
17
+ case "usage_update":
18
+ case "notice":
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);
33
+ case "current_mode_update":
34
+ // v2 has no modes: the mode is the `mode` config option. Every mode
35
+ // change also reaches the client as a `config_option_update` or in the
36
+ // `configOptions` of a `session/set_config_option` response.
37
+ return undefined;
38
+ default:
39
+ throw new Error(`The ACP v2 surface does not translate ${update.sessionUpdate} session updates yet`);
40
+ }
41
+ }