@pwrdrvr/agent-acp 0.10.3 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -212,6 +212,22 @@ declare function normalizeAcpRuntimeCapabilities(params: {
212
212
  initialize?: AcpRuntimeCapabilities;
213
213
  }): AcpRuntimeCapabilities | undefined;
214
214
  declare function acpRuntimeSupportsSessionLoad(capabilities: AcpRuntimeCapabilities | undefined): boolean;
215
+ /** The configOption an agent uses to expose model choice (ACP `category:
216
+ * "model"`, falling back to a literal `model` id). Some agents (e.g. Kimi)
217
+ * advertise the active model HERE rather than via top-level
218
+ * `models.currentModelId`. */
219
+ declare function modelConfigOption(capabilities: AcpRuntimeCapabilities | undefined): AcpRuntimeConfigOption | undefined;
220
+ /** The effective model id the agent will run with, agnostic of HOW it advertises
221
+ * it. Prefers a protocol-confirmed `models.currentModelId`; falls back to the
222
+ * `model` configOption's `currentValue` for agents that expose model choice as a
223
+ * config option (Kimi). Returns undefined when the agent advertises no model at
224
+ * all — callers report that as "no model" rather than inventing one. */
225
+ declare function modelIdFromCapabilities(capabilities: AcpRuntimeCapabilities | undefined): string | undefined;
226
+ /** The full model list a host can render/cache, agnostic of advertisement style.
227
+ * Returns `models.availableModels` when present; otherwise derives the list from
228
+ * the `model` configOption's values (Kimi), preserving labels/descriptions and
229
+ * flagging the current value as default. `[]` when no model is advertised. */
230
+ declare function modelsFromCapabilities(capabilities: AcpRuntimeCapabilities | undefined): AcpRuntimeModel[];
215
231
  declare function acpSessionRuntimeStateFromCapabilities(capabilities: AcpRuntimeCapabilities | undefined, now: number): AcpSessionRuntimeState | undefined;
216
232
  /** Runtime-state change carried inside a session/update (mode/model/config). */
217
233
  declare function acpSessionRuntimeStateFromUpdate(update: Record<string, unknown>, now: number): AcpSessionRuntimeState | undefined;
@@ -414,10 +430,16 @@ declare class AcpAgentClient implements AgentBackend {
414
430
  private handlePermissionRequest;
415
431
  private setRuntimeOption;
416
432
  private setRuntimeOptionOnTransport;
417
- /** Map a neutral `reasoning` token onto an ACP runtime option. We try to match
418
- * it to an available MODE (by id or label, case-insensitively) and switch via
419
- * `session/set_mode`. ACP has no first-class "reasoning effort" concept, so if
420
- * no mode matches we leave it alone — the caller's `.catch` debug-logs. */
433
+ /** Map a neutral `reasoning` token onto an ACP runtime option. ACP has no
434
+ * first-class "reasoning effort" concept, so we map it to whatever the agent
435
+ * exposes, in order:
436
+ * 1. An available MODE whose id/label matches the token (set_mode).
437
+ * 2. A "thinking" config option (`category: "thought_level"`, or id
438
+ * `thinking`) — map low-effort tokens to its OFF-like value and
439
+ * high-effort tokens to its ON-like value (set_config_option). This is
440
+ * what lets a one-shot enrichment turn ("low") disable a reasoning
441
+ * model's thinking pass, which is dramatically faster.
442
+ * If nothing matches we leave it alone — the caller's `.catch` debug-logs. */
421
443
  private applyReasoning;
422
444
  /** Read an image file and build an ACP `image` content block (base64 + inferred
423
445
  * mimeType). Throws on read failure; the caller catches + skips. */
@@ -429,6 +451,12 @@ declare class AcpAgentClient implements AgentBackend {
429
451
  /** Whether the agent advertises session/load support (for hosts that resume). */
430
452
  supportsSessionLoad(): boolean;
431
453
  }
454
+ /** Pick the value to set on a "thinking" config option for a neutral reasoning
455
+ * token: low-effort → the OFF-like value, high-effort → the ON-like value.
456
+ * Returns undefined when the token isn't an effort signal we recognize, or the
457
+ * option has no value of the needed polarity (caller leaves the option alone).
458
+ * Exported for testing. */
459
+ declare function reasoningValueForThoughtLevel(reasoning: string, values: ReadonlyArray<AcpRuntimeConfigOptionValue>): string | undefined;
432
460
 
433
461
  type AcpAgentClientFactory = () => AcpAgentClient;
434
462
  type AcpAgentClientPoolOptions = {
@@ -792,4 +820,4 @@ declare class AcpRegistryService {
792
820
  declare function evaluateAcpDistributionPolicy(distribution: AcpRegistryDistribution, allowlist: AcpAllowlistDecision): AcpDistributionPolicy;
793
821
  declare function normalizeRegistry(raw: unknown): AcpRegistryAgent[];
794
822
 
795
- export { ACP_REGISTRY_URL, AcpAgentAllowlist, type AcpAgentAllowlistRule, AcpAgentClient, type AcpAgentClientFactory, type AcpAgentClientOptions, AcpAgentClientPool, type AcpAgentClientPoolOptions, type AcpAgentConnection, type AcpAgentConnectionHandle, type AcpAgentInstanceSource, type AcpAgentQuirks, type AcpAgentStrategy, type AcpAllowlistDecision, type AcpApplyContext, type AcpBinaryPlatformDistribution, AcpConnection, type AcpConnectionOptions, type AcpDiscoveryProbe, type AcpDistributionKind, type AcpDistributionPolicy, type AcpJsonRpcTransport, type AcpMcpServerConfig, type AcpNormalizeResult, type AcpNormalizerOptions, AcpOneShotClient, type AcpOneShotClientOptions, type AcpOneShotRequest, type AcpOneShotResponse, type AcpPackageDistribution, type AcpPathExecutableLister, type AcpPromptContentBlock, type AcpRegistryAgent, type AcpRegistryAgentWithPolicy, type AcpRegistryAuthDescriptor, type AcpRegistryAuthMethod, type AcpRegistryDistribution, type AcpRegistryFetch, AcpRegistryService, type AcpRegistryServiceOptions, type AcpRegistrySnapshot, type AcpRuntimeAgentCapabilities, type AcpRuntimeAgentInfo, type AcpRuntimeCapabilities, type AcpRuntimeCapabilitiesHandler, type AcpRuntimeCapabilitiesSource, type AcpRuntimeConfigOption, type AcpRuntimeConfigOptionValue, type AcpRuntimeMode, type AcpRuntimeModel, type AcpRuntimeModels, type AcpRuntimeModes, type AcpRuntimeOptionSource, AcpSessionNormalizer, type AcpSessionRuntimeState, type AcpSpawnSpec, type AcpStartThreadOptions, type AcpStartTurnOptions, type AcpTitleHandler, type AcpVerificationStatus, BANNED_ACP_REGISTRY_IDS, BUILT_IN_ACP_STRATEGIES, DEFAULT_ACP_AGENT_ALLOWLIST, type DiscoveredAcpAgent, type DiscoveredAcpAgentGroup, type DiscoveredAcpAgentInstance, type LocalAcpAgentProbe, type LocalAcpDiscoveryOptions, type LocalAcpProbeResult, acpRuntimeSupportsSessionLoad, acpSessionRuntimeStateFromCapabilities, acpSessionRuntimeStateFromUpdate, asRecord, buildAcpBackendId, buildStrategyTable, defaultAcpAgentAllowlist, defaultQuirks, discoverLocalAcpAgentInstances, discoverLocalAcpAgents, evaluateAcpDistributionPolicy, geminiStrategy, grokStrategy, isBannedAcpRegistryId, kimiStrategy, mergeAcpRuntimeState, modeLabelFor, normalizeAcpRuntimeCapabilities, normalizeRegistry, qwenStrategy, readAcpContentText, readBoolean, readContentText, readFirstLocationPath, readFirstString, readKind, readNonEmptyString, readNumber, readPromptText, readString, readToolCallId, readToolContentText, readToolOutput, readUpdateText, strategyByBackendId, strategyById, toolCallFromUpdate, wellKnownAgentBinDirs };
823
+ export { ACP_REGISTRY_URL, AcpAgentAllowlist, type AcpAgentAllowlistRule, AcpAgentClient, type AcpAgentClientFactory, type AcpAgentClientOptions, AcpAgentClientPool, type AcpAgentClientPoolOptions, type AcpAgentConnection, type AcpAgentConnectionHandle, type AcpAgentInstanceSource, type AcpAgentQuirks, type AcpAgentStrategy, type AcpAllowlistDecision, type AcpApplyContext, type AcpBinaryPlatformDistribution, AcpConnection, type AcpConnectionOptions, type AcpDiscoveryProbe, type AcpDistributionKind, type AcpDistributionPolicy, type AcpJsonRpcTransport, type AcpMcpServerConfig, type AcpNormalizeResult, type AcpNormalizerOptions, AcpOneShotClient, type AcpOneShotClientOptions, type AcpOneShotRequest, type AcpOneShotResponse, type AcpPackageDistribution, type AcpPathExecutableLister, type AcpPromptContentBlock, type AcpRegistryAgent, type AcpRegistryAgentWithPolicy, type AcpRegistryAuthDescriptor, type AcpRegistryAuthMethod, type AcpRegistryDistribution, type AcpRegistryFetch, AcpRegistryService, type AcpRegistryServiceOptions, type AcpRegistrySnapshot, type AcpRuntimeAgentCapabilities, type AcpRuntimeAgentInfo, type AcpRuntimeCapabilities, type AcpRuntimeCapabilitiesHandler, type AcpRuntimeCapabilitiesSource, type AcpRuntimeConfigOption, type AcpRuntimeConfigOptionValue, type AcpRuntimeMode, type AcpRuntimeModel, type AcpRuntimeModels, type AcpRuntimeModes, type AcpRuntimeOptionSource, AcpSessionNormalizer, type AcpSessionRuntimeState, type AcpSpawnSpec, type AcpStartThreadOptions, type AcpStartTurnOptions, type AcpTitleHandler, type AcpVerificationStatus, BANNED_ACP_REGISTRY_IDS, BUILT_IN_ACP_STRATEGIES, DEFAULT_ACP_AGENT_ALLOWLIST, type DiscoveredAcpAgent, type DiscoveredAcpAgentGroup, type DiscoveredAcpAgentInstance, type LocalAcpAgentProbe, type LocalAcpDiscoveryOptions, type LocalAcpProbeResult, acpRuntimeSupportsSessionLoad, acpSessionRuntimeStateFromCapabilities, acpSessionRuntimeStateFromUpdate, asRecord, buildAcpBackendId, buildStrategyTable, defaultAcpAgentAllowlist, defaultQuirks, discoverLocalAcpAgentInstances, discoverLocalAcpAgents, evaluateAcpDistributionPolicy, geminiStrategy, grokStrategy, isBannedAcpRegistryId, kimiStrategy, mergeAcpRuntimeState, modeLabelFor, modelConfigOption, modelIdFromCapabilities, modelsFromCapabilities, normalizeAcpRuntimeCapabilities, normalizeRegistry, qwenStrategy, readAcpContentText, readBoolean, readContentText, readFirstLocationPath, readFirstString, readKind, readNonEmptyString, readNumber, readPromptText, readString, readToolCallId, readToolContentText, readToolOutput, readUpdateText, reasoningValueForThoughtLevel, strategyByBackendId, strategyById, toolCallFromUpdate, wellKnownAgentBinDirs };
package/dist/index.js CHANGED
@@ -368,8 +368,12 @@ function toolCallFromUpdate(update, kind, sessionId) {
368
368
  if (output !== void 0) {
369
369
  call.result = output;
370
370
  }
371
+ const locations = readToolLocations(update);
372
+ if (locations.length > 0) {
373
+ call.locations = locations;
374
+ }
371
375
  const explicitDisplay = command ?? readString(update, "title");
372
- if (command || output !== void 0 || exitCode !== void 0) {
376
+ if (command !== void 0 || exitCode !== void 0) {
373
377
  const detail = {
374
378
  displayCommand: explicitDisplay ?? label
375
379
  };
@@ -378,13 +382,23 @@ function toolCallFromUpdate(update, kind, sessionId) {
378
382
  if (exitCode !== void 0) detail.exitCode = exitCode;
379
383
  call.command = detail;
380
384
  }
381
- if ((toolKind === "write" || toolKind === "read") && path5) {
382
- if (!call.command) {
383
- call.command = { displayCommand: label };
384
- }
385
- }
386
385
  return call;
387
386
  }
387
+ function readToolLocations(update) {
388
+ const raw = update.locations;
389
+ if (!Array.isArray(raw)) return [];
390
+ const out = [];
391
+ for (const item of raw) {
392
+ const record = asRecord(item);
393
+ const path5 = record ? readString(record, "path") : void 0;
394
+ if (path5 === void 0) continue;
395
+ const location = { path: path5 };
396
+ const line = record ? readNumber(record, "line") : void 0;
397
+ if (line !== void 0) location.line = line;
398
+ out.push(location);
399
+ }
400
+ return out;
401
+ }
388
402
  function readToolArgs(update) {
389
403
  const raw = update.rawInput ?? update.raw_input ?? update.input ?? update.arguments ?? update.args;
390
404
  return asRecord(raw) ?? (raw === void 0 ? void 0 : raw);
@@ -702,6 +716,26 @@ function normalizeAcpRuntimeCapabilities(params) {
702
716
  function acpRuntimeSupportsSessionLoad(capabilities) {
703
717
  return capabilities?.agentCapabilities?.loadSession !== false;
704
718
  }
719
+ function modelConfigOption(capabilities) {
720
+ return capabilities?.configOptions?.find(
721
+ (option) => option.category === "model" || option.id === "model"
722
+ );
723
+ }
724
+ function modelIdFromCapabilities(capabilities) {
725
+ return capabilities?.models?.currentModelId ?? modelConfigOption(capabilities)?.currentValue;
726
+ }
727
+ function modelsFromCapabilities(capabilities) {
728
+ const advertised = capabilities?.models?.availableModels;
729
+ if (advertised !== void 0 && advertised.length > 0) return advertised;
730
+ const option = modelConfigOption(capabilities);
731
+ if (option === void 0) return [];
732
+ return option.values.map((value) => ({
733
+ id: value.value,
734
+ ...value.label !== void 0 ? { label: value.label } : {},
735
+ ...value.description !== void 0 ? { description: value.description } : {},
736
+ ...option.currentValue === value.value ? { isDefault: true } : {}
737
+ }));
738
+ }
705
739
  function acpSessionRuntimeStateFromCapabilities(capabilities, now) {
706
740
  if (!capabilities) {
707
741
  return void 0;
@@ -1146,7 +1180,7 @@ var AcpAgentClient = class {
1146
1180
  resumed: options.bindThreadId !== void 0
1147
1181
  });
1148
1182
  const out = { threadId };
1149
- const model = runtimeCapabilities?.models?.currentModelId;
1183
+ const model = modelIdFromCapabilities(runtimeCapabilities);
1150
1184
  if (model !== void 0) out.model = model;
1151
1185
  out.modelProvider = this.strategy.id;
1152
1186
  return out;
@@ -1494,21 +1528,45 @@ var AcpAgentClient = class {
1494
1528
  modelId: value
1495
1529
  });
1496
1530
  }
1497
- /** Map a neutral `reasoning` token onto an ACP runtime option. We try to match
1498
- * it to an available MODE (by id or label, case-insensitively) and switch via
1499
- * `session/set_mode`. ACP has no first-class "reasoning effort" concept, so if
1500
- * no mode matches we leave it alone — the caller's `.catch` debug-logs. */
1531
+ /** Map a neutral `reasoning` token onto an ACP runtime option. ACP has no
1532
+ * first-class "reasoning effort" concept, so we map it to whatever the agent
1533
+ * exposes, in order:
1534
+ * 1. An available MODE whose id/label matches the token (set_mode).
1535
+ * 2. A "thinking" config option (`category: "thought_level"`, or id
1536
+ * `thinking`) — map low-effort tokens to its OFF-like value and
1537
+ * high-effort tokens to its ON-like value (set_config_option). This is
1538
+ * what lets a one-shot enrichment turn ("low") disable a reasoning
1539
+ * model's thinking pass, which is dramatically faster.
1540
+ * If nothing matches we leave it alone — the caller's `.catch` debug-logs. */
1501
1541
  async applyReasoning(threadId, reasoning) {
1502
- const modes = this.runtimeCapabilities?.modes?.availableModes ?? [];
1503
1542
  const target = reasoning.toLowerCase();
1504
- const match = modes.find(
1543
+ const modes = this.runtimeCapabilities?.modes?.availableModes ?? [];
1544
+ const modeMatch = modes.find(
1505
1545
  (mode) => mode.id.toLowerCase() === target || typeof mode.label === "string" && mode.label.toLowerCase() === target
1506
1546
  );
1507
- if (match === void 0) {
1508
- this.logger.debug("acp reasoning has no matching mode \u2014 ignored", { reasoning });
1547
+ if (modeMatch !== void 0) {
1548
+ await this.setMode(threadId, modeMatch.id);
1549
+ return;
1550
+ }
1551
+ const thinking = this.runtimeCapabilities?.configOptions?.find(
1552
+ (option) => option.category === "thought_level" || option.id === "thinking"
1553
+ );
1554
+ if (thinking !== void 0) {
1555
+ const value = reasoningValueForThoughtLevel(target, thinking.values);
1556
+ if (value === void 0) {
1557
+ this.logger.debug("acp reasoning has no matching thought-level value \u2014 ignored", {
1558
+ reasoning
1559
+ });
1560
+ return;
1561
+ }
1562
+ if (value !== thinking.currentValue) {
1563
+ await this.setConfigOption(threadId, thinking.id, value);
1564
+ }
1509
1565
  return;
1510
1566
  }
1511
- await this.setMode(threadId, match.id);
1567
+ this.logger.debug("acp reasoning has no matching mode or thought-level option \u2014 ignored", {
1568
+ reasoning
1569
+ });
1512
1570
  }
1513
1571
  /** Read an image file and build an ACP `image` content block (base64 + inferred
1514
1572
  * mimeType). Throws on read failure; the caller catches + skips. */
@@ -1541,7 +1599,7 @@ var AcpAgentClient = class {
1541
1599
  }
1542
1600
  emitThreadSettings(session, capabilities, runtimeState) {
1543
1601
  const settings = { threadId: session.threadId };
1544
- const model = runtimeState?.currentModelId ?? capabilities?.models?.currentModelId;
1602
+ const model = runtimeState?.currentModelId ?? modelIdFromCapabilities(capabilities);
1545
1603
  if (model !== void 0) settings.model = model;
1546
1604
  settings.modelProvider = this.strategy.id;
1547
1605
  const modeId = runtimeState?.currentModeId ?? capabilities?.modes?.currentModeId;
@@ -1564,6 +1622,38 @@ var AcpAgentClient = class {
1564
1622
  return acpRuntimeSupportsSessionLoad(this.runtimeCapabilities);
1565
1623
  }
1566
1624
  };
1625
+ var LOW_EFFORT_TOKENS = /* @__PURE__ */ new Set([
1626
+ "off",
1627
+ "none",
1628
+ "minimal",
1629
+ "min",
1630
+ "minimum",
1631
+ "low",
1632
+ "fast"
1633
+ ]);
1634
+ var HIGH_EFFORT_TOKENS = /* @__PURE__ */ new Set([
1635
+ "on",
1636
+ "high",
1637
+ "medium",
1638
+ "max",
1639
+ "maximum",
1640
+ "full",
1641
+ "think"
1642
+ ]);
1643
+ function thoughtLevelPolarity(value) {
1644
+ const haystack = `${value.value} ${value.label ?? ""}`.toLowerCase();
1645
+ if (/\b(off|none|disabled?|no)\b/.test(haystack)) return "off";
1646
+ if (/\b(on|enabled?|high|full|yes)\b/.test(haystack)) return "on";
1647
+ return "other";
1648
+ }
1649
+ function reasoningValueForThoughtLevel(reasoning, values) {
1650
+ const token = reasoning.toLowerCase();
1651
+ const wantOff = LOW_EFFORT_TOKENS.has(token);
1652
+ const wantOn = HIGH_EFFORT_TOKENS.has(token);
1653
+ if (!wantOff && !wantOn) return void 0;
1654
+ const desired = wantOff ? "off" : "on";
1655
+ return values.find((value) => thoughtLevelPolarity(value) === desired)?.value;
1656
+ }
1567
1657
  function buildApprovalRequest(args) {
1568
1658
  const toolCall = asRecord2(args.params.toolCall) ?? {};
1569
1659
  const title = typeof toolCall.title === "string" && toolCall.title.trim() ? toolCall.title.trim() : "ACP tool call";
@@ -1953,8 +2043,8 @@ var AcpOneShotClient = class {
1953
2043
  async listModelsInner() {
1954
2044
  let models = [];
1955
2045
  const unsubscribe = this.client.onRuntimeCapabilities((event) => {
1956
- const observed = event.runtimeCapabilities.models?.availableModels;
1957
- if (observed !== void 0 && observed.length > 0) models = observed;
2046
+ const observed = modelsFromCapabilities(event.runtimeCapabilities);
2047
+ if (observed.length > 0) models = observed;
1958
2048
  });
1959
2049
  try {
1960
2050
  await this.client.startThread();
@@ -2678,6 +2768,9 @@ export {
2678
2768
  kimiStrategy,
2679
2769
  mergeAcpRuntimeState,
2680
2770
  modeLabelFor,
2771
+ modelConfigOption,
2772
+ modelIdFromCapabilities,
2773
+ modelsFromCapabilities,
2681
2774
  normalizeAcpRuntimeCapabilities,
2682
2775
  normalizeRegistry,
2683
2776
  qwenStrategy,
@@ -2695,6 +2788,7 @@ export {
2695
2788
  readToolContentText,
2696
2789
  readToolOutput,
2697
2790
  readUpdateText,
2791
+ reasoningValueForThoughtLevel,
2698
2792
  strategyByBackendId,
2699
2793
  strategyById,
2700
2794
  toolCallFromUpdate,