@agentclientprotocol/claude-agent-acp 0.90.1-preview.9 → 0.91.1-preview.1

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 (44) hide show
  1. package/README.md +14 -5
  2. package/dist/acp-agent.d.ts +94 -11
  3. package/dist/acp-agent.d.ts.map +1 -1
  4. package/dist/acp-agent.js +246 -76
  5. package/dist/air-extension.d.ts +6 -0
  6. package/dist/air-extension.d.ts.map +1 -1
  7. package/dist/air-extension.js +6 -0
  8. package/dist/file-change-audit.d.ts +17 -0
  9. package/dist/file-change-audit.d.ts.map +1 -1
  10. package/dist/file-change-audit.js +62 -20
  11. package/dist/gateway-models.d.ts +62 -0
  12. package/dist/gateway-models.d.ts.map +1 -0
  13. package/dist/gateway-models.js +180 -0
  14. package/dist/native-subagents.d.ts +34 -9
  15. package/dist/native-subagents.d.ts.map +1 -1
  16. package/dist/native-subagents.js +55 -11
  17. package/dist/session-index/connection.d.ts +4 -3
  18. package/dist/session-index/connection.d.ts.map +1 -1
  19. package/dist/session-index/connection.js +8 -7
  20. package/dist/session-index/list-subscriptions.d.ts.map +1 -1
  21. package/dist/session-index/list-subscriptions.js +4 -1
  22. package/dist/session-index/service.d.ts +10 -0
  23. package/dist/session-index/service.d.ts.map +1 -1
  24. package/dist/session-index/service.js +21 -0
  25. package/dist/session-index/session-index.d.ts +10 -2
  26. package/dist/session-index/session-index.d.ts.map +1 -1
  27. package/dist/session-index/session-index.js +21 -5
  28. package/dist/session-index/session-info.d.ts.map +1 -1
  29. package/dist/session-index/session-info.js +1 -0
  30. package/dist/session-index/transcript-scan.d.ts +25 -1
  31. package/dist/session-index/transcript-scan.d.ts.map +1 -1
  32. package/dist/session-index/transcript-scan.js +57 -1
  33. package/dist/session-index/worktrees.d.ts +6 -0
  34. package/dist/session-index/worktrees.d.ts.map +1 -1
  35. package/dist/session-index/worktrees.js +12 -0
  36. package/dist/session-model.d.ts +21 -1
  37. package/dist/session-model.d.ts.map +1 -1
  38. package/dist/session-model.js +89 -11
  39. package/dist/turn-events.d.ts +5 -1
  40. package/dist/turn-events.d.ts.map +1 -1
  41. package/dist/v2/prompt.d.ts +2 -1
  42. package/dist/v2/prompt.d.ts.map +1 -1
  43. package/dist/v2/prompt.js +2 -1
  44. package/package.json +3 -3
@@ -9,6 +9,10 @@ export type FileChangeReportTurnState = {
9
9
  export type NativeFileChangeReportTurn = {
10
10
  promptUuid: string;
11
11
  fileChangeReport?: FileChangeReportTurnState;
12
+ /** Raw `file_path`/`notebook_path` values of the file-tool edits that
13
+ * subagents of this turn completed. Checkpoints do not track files that
14
+ * only a subagent edited. */
15
+ editedPaths?: Set<string>;
12
16
  };
13
17
  export type FileChangeReportWorkspace = {
14
18
  cwd: string;
@@ -42,6 +46,19 @@ export type NativeFileChangeReporter = {
42
46
  export declare function agentFileChangeReportRequestId(meta: unknown): string | undefined;
43
47
  export declare function supportsAgentFileChangeReport(capabilities: unknown): boolean;
44
48
  export declare function agentFileChangeReportMeta(result: AgentFileChangeReportResult): Record<string, unknown>;
49
+ /**
50
+ * Adds to `turn.editedPaths` the `file_path` (`notebook_path` for NotebookEdit)
51
+ * of each successful file-tool edit among the tool results of a subagent
52
+ * message. Root-agent messages (`parentToolUseId === null`) are skipped: the
53
+ * checkpoint already tracks their files. Must run before `toAcpNotifications`
54
+ * drops the tool use cache entries.
55
+ */
56
+ export declare function recordSubagentFileEdits(turn: (NativeFileChangeReportTurn & {
57
+ settled: boolean;
58
+ }) | null | undefined, parentToolUseId: string | null, content: unknown, toolUses: Record<string, {
59
+ name: string;
60
+ input: unknown;
61
+ } | undefined>): void;
45
62
  export declare function createNativeFileChangeReporter(options: NativeFileChangeReporterOptions): NativeFileChangeReporter;
46
63
  export {};
47
64
  //# sourceMappingURL=file-change-audit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-change-audit.d.ts","sourceRoot":"","sources":["../src/file-change-audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAqB,MAAM,gCAAgC,CAAC;AAK/E,eAAO,MAAM,mCAAmC,0BAA0B,CAAC;AAI3E,eAAO,MAAM,kCAAkC,QAAa,CAAC;AAC7D,eAAO,MAAM,mCAAmC,OAAQ,CAAC;AAGzD,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,CACA;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB,GACD;IACE,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,iCAAiC,CAAC;CAC3C,CACJ,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAC3C,WAAW,GAAG,SAAS,GAAG,eAAe,GAAG,aAAa,GAAG,eAAe,CAAC;AAE9E,KAAK,+BAA+B,GAAG;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,OAAO,EAAE,CAAC,MAAM,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,yBAAyB,GAAG,SAAS,CAAC;IAC9D,MAAM,CACJ,IAAI,EAAE,0BAA0B,GAAG,IAAI,GAAG,SAAS,EACnD,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,CACJ,KAAK,EAAE,yBAAyB,GAAG,SAAS,EAC5C,MAAM,EAAE,iCAAiC,GACxC,IAAI,CAAC;CACT,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAiBhF;AAED,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAE5E;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,2BAA2B,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB;AAED,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,wBAAwB,CA4F1B"}
1
+ {"version":3,"file":"file-change-audit.d.ts","sourceRoot":"","sources":["../src/file-change-audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAqB,MAAM,gCAAgC,CAAC;AAK/E,eAAO,MAAM,mCAAmC,0BAA0B,CAAC;AAI3E,eAAO,MAAM,kCAAkC,QAAa,CAAC;AAC7D,eAAO,MAAM,mCAAmC,OAAQ,CAAC;AAGzD,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IAC7C;;kCAE8B;IAC9B,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,CACA;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB,GACD;IACE,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,iCAAiC,CAAC;CAC3C,CACJ,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAC3C,WAAW,GAAG,SAAS,GAAG,eAAe,GAAG,aAAa,GAAG,eAAe,CAAC;AAE9E,KAAK,+BAA+B,GAAG;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,OAAO,EAAE,CAAC,MAAM,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,yBAAyB,GAAG,SAAS,CAAC;IAC9D,MAAM,CACJ,IAAI,EAAE,0BAA0B,GAAG,IAAI,GAAG,SAAS,EACnD,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,CACJ,KAAK,EAAE,yBAAyB,GAAG,SAAS,EAC5C,MAAM,EAAE,iCAAiC,GACxC,IAAI,CAAC;CACT,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAiBhF;AAED,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAE5E;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,2BAA2B,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB;AAID;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,CAAC,0BAA0B,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAC5E,eAAe,EAAE,MAAM,GAAG,IAAI,EAC9B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAAC,GACrE,IAAI,CAmBN;AAED,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,wBAAwB,CA8G1B"}
@@ -30,11 +30,40 @@ export function supportsAgentFileChangeReport(capabilities) {
30
30
  export function agentFileChangeReportMeta(result) {
31
31
  return withAirMeta(undefined, AGENT_FILE_CHANGE_REPORT_CAPABILITY, result);
32
32
  }
33
+ const FILE_EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
34
+ /**
35
+ * Adds to `turn.editedPaths` the `file_path` (`notebook_path` for NotebookEdit)
36
+ * of each successful file-tool edit among the tool results of a subagent
37
+ * message. Root-agent messages (`parentToolUseId === null`) are skipped: the
38
+ * checkpoint already tracks their files. Must run before `toAcpNotifications`
39
+ * drops the tool use cache entries.
40
+ */
41
+ export function recordSubagentFileEdits(turn, parentToolUseId, content, toolUses) {
42
+ if (parentToolUseId === null || !turn || turn.settled || !turn.fileChangeReport)
43
+ return;
44
+ if (!Array.isArray(content))
45
+ return;
46
+ for (const block of content) {
47
+ if (block?.type !== "tool_result" ||
48
+ block.is_error === true ||
49
+ typeof block.tool_use_id !== "string") {
50
+ continue;
51
+ }
52
+ const toolUse = toolUses[block.tool_use_id];
53
+ if (!toolUse || !FILE_EDIT_TOOLS.has(toolUse.name))
54
+ continue;
55
+ const input = toolUse.input;
56
+ const editedPath = toolUse.name === "NotebookEdit" ? input?.notebook_path : input?.file_path;
57
+ if (typeof editedPath === "string" && editedPath.length > 0) {
58
+ (turn.editedPaths ??= new Set()).add(editedPath);
59
+ }
60
+ }
61
+ }
33
62
  export function createNativeFileChangeReporter(options) {
34
63
  const workspace = normalizeWorkspace(options.cwd, options.additionalDirectories);
35
64
  const timeoutMs = options.timeoutMs ?? AGENT_FILE_CHANGE_REPORT_TIMEOUT_MS;
36
65
  const requestIds = new Set();
37
- const publish = async (state, result) => {
66
+ async function publish(state, result) {
38
67
  if (state.phase === "finished")
39
68
  return;
40
69
  state.phase = "finished";
@@ -44,16 +73,39 @@ export function createNativeFileChangeReporter(options) {
44
73
  catch (error) {
45
74
  options.logError(`Failed to publish file-change report ${state.requestId}: ${error}`);
46
75
  }
47
- };
48
- const finishUnavailable = async (state, reason) => {
76
+ }
77
+ async function finishUnavailable(state, reason) {
49
78
  await publish(state, {
50
79
  version: 1,
51
80
  requestId: state.requestId,
52
81
  status: "unavailable",
53
82
  reason,
54
83
  });
55
- };
56
- const report = async (turn, query) => {
84
+ }
85
+ async function publishReported(state, reportedPaths) {
86
+ const normalized = normalizeReportedPaths(reportedPaths, workspace);
87
+ await publish(state, fitReportedAgentFileChangeReport({
88
+ version: 1,
89
+ requestId: state.requestId,
90
+ status: "reported",
91
+ paths: normalized.paths,
92
+ // Checkpoints miss Bash mutations. Subagent file-tool edits are added
93
+ // from the message stream, but the list is still not exhaustive.
94
+ declaredComplete: false,
95
+ truncated: normalized.truncated,
96
+ }));
97
+ }
98
+ // Used when the checkpoint preview fails: the subagent edits seen in the
99
+ // stream are still worth reporting.
100
+ async function finishWithoutPreview(state, editedPaths, reason) {
101
+ if (editedPaths && editedPaths.size > 0) {
102
+ await publishReported(state, [...editedPaths]);
103
+ }
104
+ else {
105
+ await finishUnavailable(state, reason);
106
+ }
107
+ }
108
+ async function report(turn, query) {
57
109
  const state = turn?.fileChangeReport;
58
110
  if (!state || state.phase !== "requested")
59
111
  return;
@@ -72,34 +124,24 @@ export function createNativeFileChangeReporter(options) {
72
124
  }),
73
125
  ]);
74
126
  if (preview === null) {
75
- await finishUnavailable(state, "timeout");
127
+ await finishWithoutPreview(state, turn.editedPaths, "timeout");
76
128
  return;
77
129
  }
78
130
  if (!preview.canRewind || !Array.isArray(preview.filesChanged)) {
79
- await finishUnavailable(state, "invalidOutput");
131
+ await finishWithoutPreview(state, turn.editedPaths, "invalidOutput");
80
132
  return;
81
133
  }
82
- const normalized = normalizeReportedPaths(preview.filesChanged, workspace);
83
- await publish(state, fitReportedAgentFileChangeReport({
84
- version: 1,
85
- requestId: state.requestId,
86
- status: "reported",
87
- paths: normalized.paths,
88
- // Checkpoints cover Claude file tools, but not every mutation source
89
- // (notably Bash and most subagents), so this list is never exhaustive.
90
- declaredComplete: false,
91
- truncated: normalized.truncated,
92
- }));
134
+ await publishReported(state, [...preview.filesChanged, ...(turn.editedPaths ?? [])]);
93
135
  }
94
136
  catch (error) {
95
137
  options.logError(`Failed to inspect Claude file checkpoint ${turn.promptUuid}: ${error}`);
96
- await finishUnavailable(state, "providerError");
138
+ await finishWithoutPreview(state, turn.editedPaths, "providerError");
97
139
  }
98
140
  finally {
99
141
  if (timer)
100
142
  clearTimeout(timer);
101
143
  }
102
- };
144
+ }
103
145
  return {
104
146
  request(meta) {
105
147
  const requestId = agentFileChangeReportRequestId(meta);
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Model discovery for a gateway set via `providers/set`.
3
+ *
4
+ * An organization can restrict which models its gateway serves, and the
5
+ * gateway's listing returns only those: `GET /v1/models` for an `anthropic`
6
+ * gateway, `GET /foundation-models` (Bedrock `ListFoundationModels`) for a
7
+ * `bedrock` one. The adapter lists them once when the provider is set, and the
8
+ * model picker then offers only the SDK models the gateway listed (see
9
+ * `narrowToGatewayModels` in `session-model.ts`). A listing that fails fails
10
+ * `providers/set` with a {@link GatewayListingError}.
11
+ */
12
+ /**
13
+ * The bearer token `createEnvForProvider` sets as `ANTHROPIC_AUTH_TOKEN` and
14
+ * `AWS_BEARER_TOKEN_BEDROCK`, so the CLI sends `Authorization: Bearer acp-proxy`
15
+ * unless the provider headers replace it. The listing sends the same, so the
16
+ * gateway sees the same auth as on chat requests.
17
+ */
18
+ export declare const PROVIDER_AUTH_TOKEN_PLACEHOLDER = "acp-proxy";
19
+ type ListingLogger = {
20
+ log: (...args: any[]) => void;
21
+ error: (...args: any[]) => void;
22
+ };
23
+ /**
24
+ * A gateway listing that failed: a network error, a status other than 2xx or
25
+ * 4xx, a body that is not the expected listing, or the 10 s timeout. The
26
+ * message names the request path and the reason, never a header value.
27
+ */
28
+ export declare class GatewayListingError extends Error {
29
+ name: string;
30
+ }
31
+ /**
32
+ * Lists the model ids an `anthropic` gateway at `baseUrl` serves, following
33
+ * `has_more` / `last_id` pagination. Returns `undefined` when the gateway
34
+ * answers 4xx: it has no listing, and nothing is narrowed. Throws a
35
+ * {@link GatewayListingError} when the listing fails. Header values are never
36
+ * logged.
37
+ */
38
+ export declare function listGatewayModels(baseUrl: string, providerHeaders: Record<string, string>, logger: ListingLogger): Promise<string[] | undefined>;
39
+ /**
40
+ * Lists the Anthropic models a `bedrock` gateway at `baseUrl` serves
41
+ * (`GET /foundation-models`, Bedrock `ListFoundationModels`, one page), the
42
+ * listing AI Router answers on its Bedrock route. Returns the listed ids, such
43
+ * as `global.anthropic.claude-sonnet-5-5`; `undefined` and the errors are
44
+ * those of {@link listGatewayModels}.
45
+ */
46
+ export declare function listBedrockModels(baseUrl: string, providerHeaders: Record<string, string>, logger: ListingLogger): Promise<string[] | undefined>;
47
+ /** The cross-region prefix of a Bedrock model id or ARN, if it has one. */
48
+ export declare function bedrockRegionPrefix(modelId: string): string | undefined;
49
+ /**
50
+ * Picks the region of the listed Bedrock models: `global` when the gateway
51
+ * lists it, else the prefix of the first id that has one. A gateway that lists
52
+ * `eu.*` and `us.*` ids gets the prefix that comes first, and the ids of the
53
+ * other prefixes are dropped, so the picker offers only models the CLI can
54
+ * call under that prefix. Ids without a prefix (`anthropic.claude-…`) are
55
+ * dropped too once a prefix is picked; without any prefix every id is kept.
56
+ */
57
+ export declare function selectBedrockRegion(modelIds: readonly string[]): {
58
+ regionPrefix?: string;
59
+ modelIds: string[];
60
+ };
61
+ export {};
62
+ //# sourceMappingURL=gateway-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-models.d.ts","sourceRoot":"","sources":["../src/gateway-models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,cAAc,CAAC;AAW3D,KAAK,aAAa,GAAG;IAAE,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;CAAE,CAAC;AAIxF;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IACnC,IAAI,SAAyB;CACvC;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC,CAkB/B;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC,CAa/B;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGvE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAUA"}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Model discovery for a gateway set via `providers/set`.
3
+ *
4
+ * An organization can restrict which models its gateway serves, and the
5
+ * gateway's listing returns only those: `GET /v1/models` for an `anthropic`
6
+ * gateway, `GET /foundation-models` (Bedrock `ListFoundationModels`) for a
7
+ * `bedrock` one. The adapter lists them once when the provider is set, and the
8
+ * model picker then offers only the SDK models the gateway listed (see
9
+ * `narrowToGatewayModels` in `session-model.ts`). A listing that fails fails
10
+ * `providers/set` with a {@link GatewayListingError}.
11
+ */
12
+ const LISTING_TIMEOUT_MS = 10_000;
13
+ const ANTHROPIC_PAGE_LIMIT = "1000";
14
+ const MAX_LISTING_PAGES = 20;
15
+ const MAX_LOGGED_BODY_CHARS = 500;
16
+ const ANTHROPIC_VERSION = "2023-06-01";
17
+ /**
18
+ * The bearer token `createEnvForProvider` sets as `ANTHROPIC_AUTH_TOKEN` and
19
+ * `AWS_BEARER_TOKEN_BEDROCK`, so the CLI sends `Authorization: Bearer acp-proxy`
20
+ * unless the provider headers replace it. The listing sends the same, so the
21
+ * gateway sees the same auth as on chat requests.
22
+ */
23
+ export const PROVIDER_AUTH_TOKEN_PLACEHOLDER = "acp-proxy";
24
+ /**
25
+ * The cross-region inference profile prefixes the CLI knows:
26
+ * `eu.anthropic.claude-sonnet-5`. `ANTHROPIC_BEDROCK_REGION_PREFIX` accepts all
27
+ * of them but `us-gov`, which the CLI takes only from a `us-gov-*` AWS region;
28
+ * a gateway listing only `us-gov.*` ids would then get an empty picker. That
29
+ * case is theoretical, so it is left as is.
30
+ */
31
+ const BEDROCK_REGION_PREFIXES = ["us", "eu", "apac", "jp", "au", "us-gov", "global"];
32
+ /**
33
+ * A gateway listing that failed: a network error, a status other than 2xx or
34
+ * 4xx, a body that is not the expected listing, or the 10 s timeout. The
35
+ * message names the request path and the reason, never a header value.
36
+ */
37
+ export class GatewayListingError extends Error {
38
+ name = "GatewayListingError";
39
+ }
40
+ /**
41
+ * Lists the model ids an `anthropic` gateway at `baseUrl` serves, following
42
+ * `has_more` / `last_id` pagination. Returns `undefined` when the gateway
43
+ * answers 4xx: it has no listing, and nothing is narrowed. Throws a
44
+ * {@link GatewayListingError} when the listing fails. Header values are never
45
+ * logged.
46
+ */
47
+ export async function listGatewayModels(baseUrl, providerHeaders, logger) {
48
+ return listPages(`${baseUrl.replace(/\/+$/, "")}/v1/models`, { limit: ANTHROPIC_PAGE_LIMIT }, listingHeaders(providerHeaders, { "anthropic-version": ANTHROPIC_VERSION }), logger, (body) => {
49
+ const page = body;
50
+ if (!Array.isArray(page.data))
51
+ return undefined;
52
+ return {
53
+ ids: stringField(page.data, "id"),
54
+ nextQuery: page.has_more === true && typeof page.last_id === "string"
55
+ ? { limit: ANTHROPIC_PAGE_LIMIT, after_id: page.last_id }
56
+ : undefined,
57
+ };
58
+ });
59
+ }
60
+ /**
61
+ * Lists the Anthropic models a `bedrock` gateway at `baseUrl` serves
62
+ * (`GET /foundation-models`, Bedrock `ListFoundationModels`, one page), the
63
+ * listing AI Router answers on its Bedrock route. Returns the listed ids, such
64
+ * as `global.anthropic.claude-sonnet-5-5`; `undefined` and the errors are
65
+ * those of {@link listGatewayModels}.
66
+ */
67
+ export async function listBedrockModels(baseUrl, providerHeaders, logger) {
68
+ const ids = await listPages(`${baseUrl.replace(/\/+$/, "")}/foundation-models`, {}, listingHeaders(providerHeaders, {}), logger, (body) => {
69
+ const page = body;
70
+ if (!Array.isArray(page.modelSummaries))
71
+ return undefined;
72
+ return { ids: stringField(page.modelSummaries, "modelId") };
73
+ });
74
+ return ids?.filter((id) => id.includes("anthropic."));
75
+ }
76
+ /** The cross-region prefix of a Bedrock model id or ARN, if it has one. */
77
+ export function bedrockRegionPrefix(modelId) {
78
+ const id = modelId.slice(modelId.lastIndexOf("/") + 1).toLowerCase();
79
+ return BEDROCK_REGION_PREFIXES.find((prefix) => id.startsWith(`${prefix}.anthropic.`));
80
+ }
81
+ /**
82
+ * Picks the region of the listed Bedrock models: `global` when the gateway
83
+ * lists it, else the prefix of the first id that has one. A gateway that lists
84
+ * `eu.*` and `us.*` ids gets the prefix that comes first, and the ids of the
85
+ * other prefixes are dropped, so the picker offers only models the CLI can
86
+ * call under that prefix. Ids without a prefix (`anthropic.claude-…`) are
87
+ * dropped too once a prefix is picked; without any prefix every id is kept.
88
+ */
89
+ export function selectBedrockRegion(modelIds) {
90
+ const prefixes = modelIds.map(bedrockRegionPrefix);
91
+ const regionPrefix = prefixes.includes("global")
92
+ ? "global"
93
+ : prefixes.find((prefix) => prefix !== undefined);
94
+ if (!regionPrefix)
95
+ return { modelIds: [...modelIds] };
96
+ return {
97
+ regionPrefix,
98
+ modelIds: modelIds.filter((id) => bedrockRegionPrefix(id) === regionPrefix),
99
+ };
100
+ }
101
+ // Provider headers win over the defaults, matched case-insensitively, the way
102
+ // `ANTHROPIC_CUSTOM_HEADERS` overrides the CLI's own headers.
103
+ function listingHeaders(providerHeaders, protocolHeaders) {
104
+ const headers = new Headers({
105
+ authorization: `Bearer ${PROVIDER_AUTH_TOKEN_PLACEHOLDER}`,
106
+ accept: "application/json",
107
+ ...protocolHeaders,
108
+ });
109
+ for (const [name, value] of Object.entries(providerHeaders)) {
110
+ // `Headers.set` puts an invalid value into its error message, so name only the header.
111
+ try {
112
+ headers.set(name, value);
113
+ }
114
+ catch {
115
+ throw new GatewayListingError(`Model listing cannot send the invalid header "${name}"`);
116
+ }
117
+ }
118
+ return headers;
119
+ }
120
+ function stringField(entries, field) {
121
+ return entries.flatMap((entry) => {
122
+ const value = entry?.[field];
123
+ return typeof value === "string" && value.trim() !== "" ? [value] : [];
124
+ });
125
+ }
126
+ /**
127
+ * Reads `endpoint` page by page; `parse` returns `undefined` for a body it does
128
+ * not understand. A 4xx answer on any page means the gateway has no listing:
129
+ * the result is `undefined`, and nothing is narrowed.
130
+ */
131
+ async function listPages(endpoint, firstQuery, headers, logger, parse) {
132
+ const { pathname: path, username, password } = new URL(endpoint);
133
+ const failure = (reason) => {
134
+ const error = new GatewayListingError(`Model listing GET ${path} ${reason}`);
135
+ logger.error(`[providers/set] ${error.message}`);
136
+ return error;
137
+ };
138
+ // fetch refuses such a URL and repeats it, credentials included, in its error.
139
+ if (username || password)
140
+ throw failure("cannot be sent: the base URL carries credentials");
141
+ const signal = AbortSignal.timeout(LISTING_TIMEOUT_MS);
142
+ const ids = [];
143
+ let query = firstQuery;
144
+ for (let page = 0; page < MAX_LISTING_PAGES && query; page++) {
145
+ const url = new URL(endpoint);
146
+ for (const [name, value] of Object.entries(query))
147
+ url.searchParams.set(name, value);
148
+ let response;
149
+ let body;
150
+ try {
151
+ response = await fetch(url, { headers, signal });
152
+ body = await response.text();
153
+ }
154
+ catch (error) {
155
+ throw failure(`failed: ${error instanceof Error ? error.message : String(error)}`);
156
+ }
157
+ if (response.status >= 400 && response.status < 500) {
158
+ logger.log(`[providers/set] Model listing GET ${path} returned ${response.status}: the gateway has no listing, models are not narrowed`);
159
+ return undefined;
160
+ }
161
+ if (!response.ok) {
162
+ throw failure(`returned ${response.status}: ${body.slice(0, MAX_LOGGED_BODY_CHARS)}`);
163
+ }
164
+ let parsed;
165
+ try {
166
+ parsed = parse(JSON.parse(body));
167
+ }
168
+ catch {
169
+ parsed = undefined;
170
+ }
171
+ if (!parsed)
172
+ throw failure("returned an unexpected body");
173
+ ids.push(...parsed.ids);
174
+ query = parsed.nextQuery;
175
+ }
176
+ if (query) {
177
+ logger.error(`[providers/set] Model listing GET ${path} still had more pages after ${MAX_LISTING_PAGES}; using what was read`);
178
+ }
179
+ return ids;
180
+ }
@@ -34,6 +34,9 @@ export type NativeSubagent = {
34
34
  missingError?: boolean;
35
35
  /** The RFD form: the open permission and question requests of the child. */
36
36
  openRequests?: number;
37
+ /** The RFD form: how many times the client cancelled the child (see
38
+ * `NativeSubagentRuntime.cancelChild`). */
39
+ cancels?: number;
37
40
  /** The RFD form: the ids of the prompt messages in the child's transcript. */
38
41
  promptIds?: Set<string>;
39
42
  /** The RFD form: the calls that delegated to the child, whose runs ended. */
@@ -185,19 +188,32 @@ export declare class NativeSubagentRuntime {
185
188
  * the cancel, for {@link awaitingUser}.
186
189
  */
187
190
  parentCancelled(deliver: Publish): Promise<void>;
191
+ /**
192
+ * The RFD form: the client sent `session/cancel` for the child session
193
+ * `sessionId`, which the child's `cancel` capability permits (see
194
+ * `announceNativeSubagent`). `stop` stops the child's task, as
195
+ * `Query.stopTask` does: Claude Code stops the child's current work and the
196
+ * foreground subagents that it waits on, and the parent's work goes on. The
197
+ * SDK's task events report each stopped child `idle` with `cancelled` (see
198
+ * {@link finishTask}). Accepting the stop is no evidence that the child
199
+ * stopped, so nothing is reported here, and a child whose work already ended
200
+ * keeps its outcome. Returns false when `sessionId` is not a child session
201
+ * of this runtime. AIR's draft grants no `cancel`.
202
+ */
203
+ cancelChild(sessionId: string, stop: (taskId: string) => Promise<void>): Promise<boolean>;
188
204
  /**
189
205
  * Runs `request`, a permission or question request of the session
190
206
  * `sessionId`. In the RFD form, a child waiting on it is `requires_action`
191
207
  * until its last open request ends, and then `running` again.
192
208
  *
193
209
  * `signal` aborts when Claude Code withdraws the request, which is not an
194
- * answer. If the parent was cancelled while the request was open, Claude
195
- * Code's interrupt withdrew it as it stopped the child, and the SDK's events
196
- * report the child `idle` next. The end of the request then reports no
197
- * `running`, which would show a child that is being stopped as working.
198
- * Without a cancel, something else answered the request in the user's place,
199
- * such as a `PermissionRequest` hook, and the child runs on, so the end
200
- * reports `running`.
210
+ * answer. If the root session, the child, or an ancestor of the child was
211
+ * cancelled while the request was open, Claude Code withdrew it as it
212
+ * stopped the child, and the SDK's events report the child `idle` next. The
213
+ * end of the request then reports no `running`, which would show a child
214
+ * that is being stopped as working. Without a cancel, something else
215
+ * answered the request in the user's place, such as a `PermissionRequest`
216
+ * hook, and the child runs on, so the end reports `running`.
201
217
  */
202
218
  awaitingUser<T>(sessionId: string, request: () => Promise<T>, signal?: AbortSignal): Promise<T>;
203
219
  discardPending(parentToolUseId: string): void;
@@ -269,6 +285,13 @@ export declare class NativeSubagentRuntime {
269
285
  */
270
286
  private rememberSendMessage;
271
287
  private rememberToolCallOwner;
288
+ /**
289
+ * The RFD form: how many cancels the client sent for `child`, its
290
+ * ancestors, and the root session. Each cancel stops the foreground work
291
+ * under it, and the counts only grow, so a changed sum shows that a cancel
292
+ * that reaches the child came.
293
+ */
294
+ private cancelsAbove;
272
295
  /** The child generation with the ACP session `sessionId`, if one exists. */
273
296
  private childOfSession;
274
297
  clear(): void;
@@ -299,8 +322,10 @@ export declare class NativeSubagentRuntime {
299
322
  /**
300
323
  * Announces the child on its parent, once, before any update of the child.
301
324
  * The RFD form announces it as running and then sends the prompt as the
302
- * first message of the child. The child gets no capabilities, so the client
303
- * may not cancel or otherwise change it.
325
+ * first message of the child. It grants the child `cancel`: the client may
326
+ * stop the child's current work with `session/cancel` (see
327
+ * `NativeSubagentRuntime.cancelChild`), and nothing else. AIR's draft grants
328
+ * no capabilities.
304
329
  */
305
330
  export declare function announceNativeSubagent(child: NativeSubagent, publish: Publish, form: SubagentForm): Promise<void>;
306
331
  export declare function finishNativeSubagent(session: NativeSubagentSession, taskId: string, state: SubagentState, publish: Publish): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"native-subagents.d.ts","sourceRoot":"","sources":["../src/native-subagents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"native-subagents.d.ts","sourceRoot":"","sources":["../src/native-subagents.ts"],"names":[],"mappings":"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"}
@@ -372,25 +372,49 @@ export class NativeSubagentRuntime {
372
372
  else
373
373
  this.parentCancels++;
374
374
  }
375
+ /**
376
+ * The RFD form: the client sent `session/cancel` for the child session
377
+ * `sessionId`, which the child's `cancel` capability permits (see
378
+ * `announceNativeSubagent`). `stop` stops the child's task, as
379
+ * `Query.stopTask` does: Claude Code stops the child's current work and the
380
+ * foreground subagents that it waits on, and the parent's work goes on. The
381
+ * SDK's task events report each stopped child `idle` with `cancelled` (see
382
+ * {@link finishTask}). Accepting the stop is no evidence that the child
383
+ * stopped, so nothing is reported here, and a child whose work already ended
384
+ * keeps its outcome. Returns false when `sessionId` is not a child session
385
+ * of this runtime. AIR's draft grants no `cancel`.
386
+ */
387
+ async cancelChild(sessionId, stop) {
388
+ if (this.form !== "rfd")
389
+ return false;
390
+ for (const [taskId, child] of this.children) {
391
+ if (child.sessionId !== sessionId || !child.announced)
392
+ continue;
393
+ child.cancels = (child.cancels ?? 0) + 1;
394
+ await stop(taskId);
395
+ return true;
396
+ }
397
+ return false;
398
+ }
375
399
  /**
376
400
  * Runs `request`, a permission or question request of the session
377
401
  * `sessionId`. In the RFD form, a child waiting on it is `requires_action`
378
402
  * until its last open request ends, and then `running` again.
379
403
  *
380
404
  * `signal` aborts when Claude Code withdraws the request, which is not an
381
- * answer. If the parent was cancelled while the request was open, Claude
382
- * Code's interrupt withdrew it as it stopped the child, and the SDK's events
383
- * report the child `idle` next. The end of the request then reports no
384
- * `running`, which would show a child that is being stopped as working.
385
- * Without a cancel, something else answered the request in the user's place,
386
- * such as a `PermissionRequest` hook, and the child runs on, so the end
387
- * reports `running`.
405
+ * answer. If the root session, the child, or an ancestor of the child was
406
+ * cancelled while the request was open, Claude Code withdrew it as it
407
+ * stopped the child, and the SDK's events report the child `idle` next. The
408
+ * end of the request then reports no `running`, which would show a child
409
+ * that is being stopped as working. Without a cancel, something else
410
+ * answered the request in the user's place, such as a `PermissionRequest`
411
+ * hook, and the child runs on, so the end reports `running`.
388
412
  */
389
413
  async awaitingUser(sessionId, request, signal) {
390
414
  const child = this.form === "rfd" ? this.childOfSession(sessionId) : undefined;
391
415
  if (!child?.announced)
392
416
  return request();
393
- const cancelsAtOpen = this.parentCancels;
417
+ const cancelsAtOpen = this.cancelsAbove(child);
394
418
  child.openRequests = (child.openRequests ?? 0) + 1;
395
419
  if (child.openRequests === 1 && child.workState === "running") {
396
420
  await this.reportState(child, { state: "requires_action" }).catch((error) => this.logger.log(`Failed to report that subagent ${child.sessionId} waits: ${error}`));
@@ -401,7 +425,7 @@ export class NativeSubagentRuntime {
401
425
  finally {
402
426
  child.openRequests = (child.openRequests ?? 1) - 1;
403
427
  // Withdrawn after a cancel that came while the request was open.
404
- const stopped = signal?.aborted === true && this.parentCancels !== cancelsAtOpen;
428
+ const stopped = signal?.aborted === true && this.cancelsAbove(child) !== cancelsAtOpen;
405
429
  if (child.openRequests === 0 && child.workState === "requires_action" && !stopped) {
406
430
  await this.reportState(child, { state: "running" }).catch((error) => this.logger.log(`Failed to report that subagent ${child.sessionId} runs: ${error}`));
407
431
  }
@@ -604,6 +628,23 @@ export class NativeSubagentRuntime {
604
628
  this.childByToolCall.delete(oldest);
605
629
  }
606
630
  }
631
+ /**
632
+ * The RFD form: how many cancels the client sent for `child`, its
633
+ * ancestors, and the root session. Each cancel stops the foreground work
634
+ * under it, and the counts only grow, so a changed sum shows that a cancel
635
+ * that reaches the child came.
636
+ */
637
+ cancelsAbove(child) {
638
+ let cancels = this.parentCancels;
639
+ const seen = new Set();
640
+ let next = child;
641
+ while (next && !seen.has(next)) {
642
+ seen.add(next);
643
+ cancels += next.cancels ?? 0;
644
+ next = this.childOfSession(next.parentSessionId);
645
+ }
646
+ return cancels;
647
+ }
607
648
  /** The child generation with the ACP session `sessionId`, if one exists. */
608
649
  childOfSession(sessionId) {
609
650
  if (sessionId === this.rootSessionId)
@@ -716,8 +757,10 @@ export class NativeSubagentRuntime {
716
757
  /**
717
758
  * Announces the child on its parent, once, before any update of the child.
718
759
  * The RFD form announces it as running and then sends the prompt as the
719
- * first message of the child. The child gets no capabilities, so the client
720
- * may not cancel or otherwise change it.
760
+ * first message of the child. It grants the child `cancel`: the client may
761
+ * stop the child's current work with `session/cancel` (see
762
+ * `NativeSubagentRuntime.cancelChild`), and nothing else. AIR's draft grants
763
+ * no capabilities.
721
764
  */
722
765
  export async function announceNativeSubagent(child, publish, form) {
723
766
  if (child.announced)
@@ -734,6 +777,7 @@ export async function announceNativeSubagent(child, publish, form) {
734
777
  sessionId: child.sessionId,
735
778
  ...(child.title ? { title: child.title } : {}),
736
779
  ...(child.description ? { description: child.description } : {}),
780
+ capabilities: { cancel: {} },
737
781
  state: { state: "running" },
738
782
  },
739
783
  });