@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
@@ -1,4 +1,5 @@
1
1
  import { AIR_RECOMMENDED_CONFIG_VALUE_CAPABILITY, withAirMeta } from "./air-extension.js";
2
+ import { bedrockRegionPrefix } from "./gateway-models.js";
2
3
  export const MODEL_CONFIG_ID = "model";
3
4
  const MODEL_CONTEXT_HINT_PATTERN = /\[(\d+m)\]$/i;
4
5
  const CONTEXT_HINT_SUFFIX_PATTERN = /-(\d+m)$/i;
@@ -218,9 +219,10 @@ function resolveSettingsModel(models, settingsModel, logger) {
218
219
  return resolveModelPreference(models, settingsModel);
219
220
  }
220
221
  const MODEL_FAMILY_ALIASES = new Set(["opus", "sonnet", "haiku", "fable"]);
221
- /** A model id without the spellings `deniedModels` ignores: provider
222
- * prefixes, dates, version suffixes, `-fast` and context hints. */
223
- function normalizeDeniedModelId(value) {
222
+ /** A model id without the spellings that name the same model: provider
223
+ * prefixes, dates, version suffixes, `-fast` and context hints. `deniedModels`
224
+ * and gateway listings both match on it. */
225
+ function normalizeModelId(value) {
224
226
  let id = stripContextHints(value.trim().toLowerCase());
225
227
  const providerPrefix = id.lastIndexOf("anthropic.");
226
228
  if (providerPrefix !== -1)
@@ -239,11 +241,11 @@ function normalizeDeniedModelId(value) {
239
241
  * the enforcer; this only keeps the picker from offering a refused row.
240
242
  */
241
243
  export function isDeniedModel(modelId, deniedModels) {
242
- const id = normalizeDeniedModelId(modelId);
244
+ const id = normalizeModelId(modelId);
243
245
  if (!id)
244
246
  return false;
245
247
  return deniedModels.some((entry) => {
246
- const denied = normalizeDeniedModelId(entry);
248
+ const denied = normalizeModelId(entry);
247
249
  if (!denied)
248
250
  return false;
249
251
  if (MODEL_FAMILY_ALIASES.has(denied))
@@ -324,6 +326,46 @@ export function applyAvailableModelsAllowlist(sdkModels, allowlist, settingsMode
324
326
  }
325
327
  return result;
326
328
  }
329
+ /**
330
+ * Keep the SDK models the `providers/set` gateway listed (`listGatewayModels`,
331
+ * `listBedrockModels`). A row matches when its `resolvedModel` (or
332
+ * its `value` when the SDK gives none) names a listed model in any spelling:
333
+ * `claude-sonnet-5[1m]` matches a listed `claude-sonnet-5-20260101`, and
334
+ * `eu.anthropic.claude-sonnet-5` matches a listed `eu.anthropic.claude-sonnet-5-v1:0`.
335
+ *
336
+ * With a Bedrock `regionPrefix`, a row resolved to another cross-region prefix
337
+ * (`us.anthropic.claude-sonnet-5` under `eu`) is dropped even when the same
338
+ * model is listed under `eu`: the CLI would call it in the other region.
339
+ *
340
+ * A row the SDK gives no `resolvedModel` names its model only by an alias, so
341
+ * it is checked as far as the alias allows: `default` is kept, and a family
342
+ * alias such as `opus` is kept when the gateway lists a model of that family.
343
+ *
344
+ * When no row matches by its model (a `default` row kept only for lacking a
345
+ * `resolvedModel` does not count), the gateway serves none of the SDK's
346
+ * models, and nothing is left: the session offers no model.
347
+ */
348
+ export function narrowToGatewayModels(models, listedModelIds, regionPrefix) {
349
+ const listed = new Set(listedModelIds.map(normalizeModelId).filter(Boolean));
350
+ const narrowed = models.filter((model) => {
351
+ const modelId = model.resolvedModel ?? model.value;
352
+ const prefix = bedrockRegionPrefix(modelId);
353
+ if (regionPrefix !== undefined && prefix !== undefined && prefix !== regionPrefix) {
354
+ return false;
355
+ }
356
+ const id = normalizeModelId(modelId);
357
+ if (listed.has(id))
358
+ return true;
359
+ if (model.resolvedModel !== undefined)
360
+ return false;
361
+ if (model.value === "default")
362
+ return true;
363
+ return MODEL_FAMILY_ALIASES.has(id) && [...listed].some((entry) => entry.includes(`-${id}`));
364
+ });
365
+ return narrowed.some((model) => listed.has(normalizeModelId(model.resolvedModel ?? model.value)))
366
+ ? narrowed
367
+ : [];
368
+ }
327
369
  /** Whether a rejected `setModel` was vetoed by a user-configured
328
370
  * PreModelSwitch hook. The CLI's rejection message is the stable,
329
371
  * distinguishable marker ("Model switch blocked by a PreModelSwitch hook:
@@ -332,7 +374,16 @@ export function applyAvailableModelsAllowlist(sdkModels, allowlist, settingsMode
332
374
  function isPreModelSwitchHookBlock(error) {
333
375
  return error instanceof Error && error.message.includes("blocked by a PreModelSwitch hook");
334
376
  }
335
- export async function getAvailableModels(query, models, sdkModels, settingsManager, logger, isResumedSession, sessionId, resumedModelHint) {
377
+ export async function getAvailableModels(query, models, sdkModels, settingsManager, logger, isResumedSession, sessionId, resumedModelHint, narrowedByGateway = false) {
378
+ // A gateway that lists none of the SDK's models leaves none to offer
379
+ // (`narrowToGatewayModels`). The session stays on the model the SDK runs,
380
+ // and the gateway refuses its requests.
381
+ if (models.length === 0) {
382
+ return {
383
+ availableModels: [],
384
+ currentModelId: (isResumedSession && resumedModelHint) || sdkModels[0]?.value || "default",
385
+ };
386
+ }
336
387
  const settings = settingsManager.getSettings();
337
388
  let currentModel = models[0];
338
389
  let resolvedFromInput;
@@ -377,8 +428,31 @@ export async function getAvailableModels(query, models, sdkModels, settingsManag
377
428
  // be re-asserted to keep the reported model truthful.
378
429
  // Anything else (fuzzy match, allowlist-synthesized value, alias) gets a
379
430
  // setModel call so we don't drift from the user's intended pin.
431
+ // Case (a) does not hold when `narrowToGatewayModels` left the SDK on a
432
+ // model the gateway does not list: the dropped `default` row of a fresh or
433
+ // transcript-less session, or a resumed transcript model outside the list.
434
+ // Such a session switches to models[0].
380
435
  const sdkSawSameValue = sdkModels.some((m) => m.value === currentModel.value);
381
- const skipSetModel = resolvedFromInput === undefined ||
436
+ const sdkDefault = sdkModels.find((m) => m.value === "default");
437
+ const sdkDefaultDropped = sdkDefault !== undefined && !models.some((m) => m.value === "default");
438
+ let switchToListed = false;
439
+ if (resolvedFromInput === undefined) {
440
+ if (!isResumedSession || !resumedModelHint) {
441
+ switchToListed = sdkDefaultDropped;
442
+ }
443
+ else if (narrowedByGateway &&
444
+ (!models.some((m) => m.value === currentModel.value) ||
445
+ // A loose match can map the transcript's model onto a listed row of
446
+ // another version, which the CLI does not resume on.
447
+ (currentModel.resolvedModel !== undefined &&
448
+ normalizeModelId(currentModel.resolvedModel) !== normalizeModelId(resumedModelHint)))) {
449
+ currentModel = models[0];
450
+ switchToListed = true;
451
+ }
452
+ }
453
+ // When the switch fails, the SDK stays on the model it ran before it.
454
+ const modelBeforeSwitch = () => switchToListed && sdkDefault !== undefined ? sdkDefault : models[0];
455
+ const skipSetModel = (resolvedFromInput === undefined && !switchToListed) ||
382
456
  (!isResumedSession && currentModel.value === resolvedFromInput && sdkSawSameValue);
383
457
  if (!skipSetModel) {
384
458
  const setModelStartedAt = performance.now();
@@ -407,14 +481,18 @@ export async function getAvailableModels(query, models, sdkModels, settingsManag
407
481
  // live model back on this path: getContextUsage isn't serviced on a
408
482
  // fresh session until the first prompt turn (issues #886/#880).
409
483
  if (!isResumedSession) {
410
- if (!isPreModelSwitchHookBlock(error))
484
+ // A switch onto a listed model is advisory too: the session stays on
485
+ // the model the SDK runs and reports it.
486
+ if (!switchToListed && !isPreModelSwitchHookBlock(error))
411
487
  throw error;
412
- logger.error(`Model pin "${currentModel.value}" was vetoed by a PreModelSwitch hook; staying on the default model:`, error);
413
- currentModel = models[0];
488
+ logger.error(`Model switch to "${currentModel.value}" failed or was vetoed by a PreModelSwitch hook; staying on the default model:`, error);
489
+ currentModel = modelBeforeSwitch();
414
490
  }
415
491
  else {
416
492
  logger.error(`Failed to re-assert model "${currentModel.value}" on resume:`, error);
417
- currentModel = resumedModelHint ? matchResumedModel(models, resumedModelHint) : models[0];
493
+ currentModel = resumedModelHint
494
+ ? matchResumedModel(models, resumedModelHint)
495
+ : modelBeforeSwitch();
418
496
  }
419
497
  }
420
498
  }
@@ -53,7 +53,11 @@ export interface TurnEvents {
53
53
  * the cycle reports nothing more, and the turn reports from there.
54
54
  */
55
55
  export interface ForegroundEvents {
56
- /** The work started, or works again after it waited for the user. */
56
+ /**
57
+ * The work started, or works again after it waited for the user. Not after
58
+ * a wait that a cancel ended: Claude Code's interrupt withdrew the last
59
+ * request, so the work is being stopped, and {@link ended} follows at once.
60
+ */
57
61
  running(): void;
58
62
  /**
59
63
  * The work waits for the user: a permission request or a question is open in
@@ -1 +1 @@
1
- {"version":3,"file":"turn-events.d.ts","sourceRoot":"","sources":["../src/turn-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,OAAO,IAAI,IAAI,CAAC;IAChB;;;;OAIG;IACH,YAAY,IAAI,IAAI,CAAC;IACrB,sBAAsB;IACtB,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAClC;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;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"}
1
+ {"version":3,"file":"turn-events.d.ts","sourceRoot":"","sources":["../src/turn-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,OAAO,IAAI,IAAI,CAAC;IAChB;;;;OAIG;IACH,YAAY,IAAI,IAAI,CAAC;IACrB,sBAAsB;IACtB,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAClC;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;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"}
@@ -28,7 +28,8 @@ export declare function v2Prompt(agent: ClaudeAcpAgent, params: v2.PromptRequest
28
28
  *
29
29
  * - Started: `running`. For a prompt's turn, right after its `user_message`.
30
30
  * - Waiting on a permission request or a question: `requires_action`, and
31
- * `running` once none is open.
31
+ * `running` once none is open, unless a cancel withdrew the last one: the
32
+ * work is being stopped then, and its `idle` follows.
32
33
  * - Ended: `idle` with the stop reason, usage, and `_meta` of the work.
33
34
  * - Failed: `idle` with the `error` stop reason and the JSON-RPC error that v1
34
35
  * answers the prompt with, so a client can show its message and handle its
@@ -1 +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;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAuC,MAAM,mBAAmB,CAAC;AAE/F;;;;;;;;;;;GAWG;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,CAiC5B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,KAAK,IAAI,GAAG,gBAAgB,CAoBvF;AAgCD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE,CAAC,aAAa,GAAG,aAAa,CAEvF"}
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;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAuC,MAAM,mBAAmB,CAAC;AAE/F;;;;;;;;;;;GAWG;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,CAiC5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,KAAK,IAAI,GAAG,gBAAgB,CAoBvF;AAgCD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE,CAAC,aAAa,GAAG,aAAa,CAEvF"}
package/dist/v2/prompt.js CHANGED
@@ -57,7 +57,8 @@ export function v2Prompt(agent, params, send) {
57
57
  *
58
58
  * - Started: `running`. For a prompt's turn, right after its `user_message`.
59
59
  * - Waiting on a permission request or a question: `requires_action`, and
60
- * `running` once none is open.
60
+ * `running` once none is open, unless a cancel withdrew the last one: the
61
+ * work is being stopped then, and its `idle` follows.
61
62
  * - Ended: `idle` with the stop reason, usage, and `_meta` of the work.
62
63
  * - Failed: `idle` with the `error` stop reason and the JSON-RPC error that v1
63
64
  * answers the prompt with, so a client can show its message and handle its
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.90.1-preview.9",
6
+ "version": "0.91.1-preview.1",
7
7
  "description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
8
8
  "main": "dist/lib.js",
9
9
  "types": "dist/lib.d.ts",
@@ -66,12 +66,12 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@agentclientprotocol/sdk": "1.8.0",
69
- "@anthropic-ai/claude-agent-sdk": "0.3.296",
69
+ "@anthropic-ai/claude-agent-sdk": "0.3.300",
70
70
  "diff": "9.0.0",
71
71
  "zod": "4.6.7"
72
72
  },
73
73
  "devDependencies": {
74
- "@anthropic-ai/sdk": "0.133.0",
74
+ "@anthropic-ai/sdk": "0.134.0",
75
75
  "@eslint/js": "10.0.1",
76
76
  "@tsconfig/node22": "22.0.6",
77
77
  "@types/node": "26.6.5",