@pwrdrvr/agent-acp 0.16.0 → 0.16.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.
- package/dist/index.d.ts +20 -4
- package/dist/index.js +94 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -384,6 +384,11 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
384
384
|
private unsubscribeRequest;
|
|
385
385
|
private initialized;
|
|
386
386
|
private runtimeCapabilities?;
|
|
387
|
+
/** What `initialize` reported: agent-level, and never any one session's. The
|
|
388
|
+
* base a session's own menus are built on — the client-wide snapshot above is
|
|
389
|
+
* whichever session wrote last, and a fresh session must not inherit its
|
|
390
|
+
* menus or their current values. */
|
|
391
|
+
private initializeCapabilities?;
|
|
387
392
|
constructor(options: AcpAgentClientOptions);
|
|
388
393
|
private readonly clientName;
|
|
389
394
|
private readonly clientTitle;
|
|
@@ -510,7 +515,7 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
510
515
|
* `session/set_model` answers `{}` and refreshes only via an async
|
|
511
516
|
* notification. On Kimi the model DETERMINES which thought levels exist —
|
|
512
517
|
* `K2.7 Coding` offers only `on`, `K3` offers `low`/`high`/`max` — and
|
|
513
|
-
* `applyReasoning` picks from
|
|
518
|
+
* `applyReasoning` picks from THIS session's menus on the following
|
|
514
519
|
* `startTurn`, so the refresh this write leaves behind is what it reads. */
|
|
515
520
|
setModel(threadId: string, modelId: string): Promise<void>;
|
|
516
521
|
setConfigOption(threadId: string, optionId: string, value: string): Promise<void>;
|
|
@@ -537,7 +542,17 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
537
542
|
/** Read an image file and build an ACP `image` content block (base64 + inferred
|
|
538
543
|
* mimeType). Throws on read failure; the caller catches + skips. */
|
|
539
544
|
private imageBlockFromPath;
|
|
545
|
+
/** Normalize `result` into a capability snapshot, filling what it omits from
|
|
546
|
+
* `base`. Required, not defaulted: an explicit `undefined` would trigger a
|
|
547
|
+
* default parameter, and the only default on hand is the client-wide
|
|
548
|
+
* snapshot — which may be another session's. */
|
|
540
549
|
private captureRuntimeCapabilities;
|
|
550
|
+
/** The option menus to consult when choosing a value for `threadId`. A
|
|
551
|
+
* session with none of its own gets the agent-level `initialize` menus, never
|
|
552
|
+
* another session's. */
|
|
553
|
+
private sessionCapabilities;
|
|
554
|
+
/** What a session's menus are built on: its own, or `initialize`'s. */
|
|
555
|
+
private sessionBase;
|
|
541
556
|
private notifyRuntimeCapabilities;
|
|
542
557
|
private emitThreadSettings;
|
|
543
558
|
private requireSession;
|
|
@@ -550,9 +565,10 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
550
565
|
supportsSseMcp(): boolean;
|
|
551
566
|
}
|
|
552
567
|
/** Pick the value to set on a "thinking" config option for a neutral reasoning
|
|
553
|
-
* token:
|
|
554
|
-
*
|
|
555
|
-
*
|
|
568
|
+
* token: high-effort → the ON-like value; low-effort → the OFF-like value, or,
|
|
569
|
+
* when the agent offers only graded levels, the LOW one — the closest thing to
|
|
570
|
+
* off it has. Returns undefined when the token isn't an effort signal we
|
|
571
|
+
* recognize, or the option has no value to match (caller leaves it alone).
|
|
556
572
|
* Exported for testing. */
|
|
557
573
|
declare function reasoningValueForThoughtLevel(reasoning: string, values: ReadonlyArray<AcpRuntimeConfigOptionValue>): string | undefined;
|
|
558
574
|
|
package/dist/index.js
CHANGED
|
@@ -702,9 +702,10 @@ function acpRuntimeSupportsSseMcp(capabilities) {
|
|
|
702
702
|
return capabilities?.agentCapabilities?.mcp?.sse === true;
|
|
703
703
|
}
|
|
704
704
|
function modelConfigOption(capabilities) {
|
|
705
|
-
return capabilities?.configOptions?.find(
|
|
706
|
-
|
|
707
|
-
|
|
705
|
+
return capabilities?.configOptions?.find(isModelConfigOption);
|
|
706
|
+
}
|
|
707
|
+
function isModelConfigOption(option) {
|
|
708
|
+
return option.category === "model" || option.id === "model";
|
|
708
709
|
}
|
|
709
710
|
function modelIdFromCapabilities(capabilities) {
|
|
710
711
|
return capabilities?.models?.currentModelId ?? modelConfigOption(capabilities)?.currentValue;
|
|
@@ -751,6 +752,8 @@ function acpSessionRuntimeStateFromUpdate(update, now) {
|
|
|
751
752
|
return currentModeId ? { currentModeId, updatedAt: now } : void 0;
|
|
752
753
|
}
|
|
753
754
|
if (kind === "config_option_update") {
|
|
755
|
+
const fullSet = configOptionUpdateSet(update);
|
|
756
|
+
if (fullSet !== void 0) return runtimeStateFromConfigOptionSet(fullSet, now);
|
|
754
757
|
const configOption = asRecord(update.configOption ?? update.config_option) ?? update;
|
|
755
758
|
const id = readString(configOption, "id") ?? readString(configOption, "configOptionId") ?? readString(configOption, "configId");
|
|
756
759
|
const value = readString(configOption, "currentValue") ?? readString(configOption, "value");
|
|
@@ -768,6 +771,24 @@ function mergeAcpRuntimeState(existing, update) {
|
|
|
768
771
|
}
|
|
769
772
|
};
|
|
770
773
|
}
|
|
774
|
+
function configOptionUpdateSet(update) {
|
|
775
|
+
if (readKind(update) !== "config_option_update") return void 0;
|
|
776
|
+
const set = update.configOptions ?? update.config_options;
|
|
777
|
+
return Array.isArray(set) ? set : void 0;
|
|
778
|
+
}
|
|
779
|
+
function runtimeStateFromConfigOptionSet(set, now) {
|
|
780
|
+
const options = readConfigOptions(set);
|
|
781
|
+
const configValues = Object.fromEntries(
|
|
782
|
+
options.flatMap(
|
|
783
|
+
(option) => option.currentValue !== void 0 ? [[option.id, option.currentValue]] : []
|
|
784
|
+
)
|
|
785
|
+
);
|
|
786
|
+
if (Object.keys(configValues).length === 0) return void 0;
|
|
787
|
+
const state = { configValues, updatedAt: now };
|
|
788
|
+
const model = options.find(isModelConfigOption)?.currentValue;
|
|
789
|
+
if (model !== void 0) state.currentModelId = model;
|
|
790
|
+
return state;
|
|
791
|
+
}
|
|
771
792
|
function modeLabelFor(capabilities, modeId) {
|
|
772
793
|
return capabilities?.modes?.availableModes.find((mode) => mode.id === modeId)?.label;
|
|
773
794
|
}
|
|
@@ -1006,6 +1027,11 @@ var AcpAgentClient = class {
|
|
|
1006
1027
|
unsubscribeRequest = void 0;
|
|
1007
1028
|
initialized = false;
|
|
1008
1029
|
runtimeCapabilities;
|
|
1030
|
+
/** What `initialize` reported: agent-level, and never any one session's. The
|
|
1031
|
+
* base a session's own menus are built on — the client-wide snapshot above is
|
|
1032
|
+
* whichever session wrote last, and a fresh session must not inherit its
|
|
1033
|
+
* menus or their current values. */
|
|
1034
|
+
initializeCapabilities;
|
|
1009
1035
|
constructor(options) {
|
|
1010
1036
|
this.transport = options.transport;
|
|
1011
1037
|
this.strategy = options.strategy;
|
|
@@ -1219,6 +1245,7 @@ var AcpAgentClient = class {
|
|
|
1219
1245
|
normalizer: new AcpSessionNormalizer({ quirks: this.strategy.quirks }),
|
|
1220
1246
|
turnId: void 0,
|
|
1221
1247
|
runtimeState: void 0,
|
|
1248
|
+
runtimeCapabilities: void 0,
|
|
1222
1249
|
pendingTurn: void 0,
|
|
1223
1250
|
pendingInstructions: options.instructions,
|
|
1224
1251
|
mcpServerNames: configuredMcpServers.map((server) => server.name)
|
|
@@ -1260,6 +1287,7 @@ var AcpAgentClient = class {
|
|
|
1260
1287
|
normalizer: new AcpSessionNormalizer({ quirks: this.strategy.quirks }),
|
|
1261
1288
|
turnId: void 0,
|
|
1262
1289
|
runtimeState: void 0,
|
|
1290
|
+
runtimeCapabilities: void 0,
|
|
1263
1291
|
pendingTurn: void 0,
|
|
1264
1292
|
pendingInstructions: options.instructions,
|
|
1265
1293
|
// Remember ONLY the MCP servers attached to THIS session so the host's
|
|
@@ -1272,9 +1300,11 @@ var AcpAgentClient = class {
|
|
|
1272
1300
|
}
|
|
1273
1301
|
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1274
1302
|
options.lifecycle === "load" ? "session-load" : "session-new",
|
|
1275
|
-
result
|
|
1303
|
+
result,
|
|
1304
|
+
this.sessionBase(session)
|
|
1276
1305
|
);
|
|
1277
1306
|
if (runtimeCapabilities) {
|
|
1307
|
+
session.runtimeCapabilities = runtimeCapabilities;
|
|
1278
1308
|
const runtimeState = acpSessionRuntimeStateFromCapabilities(
|
|
1279
1309
|
runtimeCapabilities,
|
|
1280
1310
|
this.now()
|
|
@@ -1459,11 +1489,12 @@ var AcpAgentClient = class {
|
|
|
1459
1489
|
* `session/set_model` answers `{}` and refreshes only via an async
|
|
1460
1490
|
* notification. On Kimi the model DETERMINES which thought levels exist —
|
|
1461
1491
|
* `K2.7 Coding` offers only `on`, `K3` offers `low`/`high`/`max` — and
|
|
1462
|
-
* `applyReasoning` picks from
|
|
1492
|
+
* `applyReasoning` picks from THIS session's menus on the following
|
|
1463
1493
|
* `startTurn`, so the refresh this write leaves behind is what it reads. */
|
|
1464
1494
|
async setModel(threadId, modelId) {
|
|
1465
|
-
const
|
|
1466
|
-
const
|
|
1495
|
+
const capabilities = this.sessionCapabilities(threadId);
|
|
1496
|
+
const advertisesModelsCapability = (capabilities?.models?.availableModels?.length ?? 0) > 0;
|
|
1497
|
+
const option = modelConfigOption(capabilities);
|
|
1467
1498
|
if (!advertisesModelsCapability && option !== void 0) {
|
|
1468
1499
|
await this.setRuntimeOption(threadId, "modelConfigOption", option.id, modelId);
|
|
1469
1500
|
return;
|
|
@@ -1516,8 +1547,13 @@ var AcpAgentClient = class {
|
|
|
1516
1547
|
},
|
|
1517
1548
|
ACP_REQUEST_TIMEOUT_MS
|
|
1518
1549
|
);
|
|
1519
|
-
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1550
|
+
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1551
|
+
"initialize",
|
|
1552
|
+
result,
|
|
1553
|
+
this.initializeCapabilities
|
|
1554
|
+
);
|
|
1520
1555
|
if (runtimeCapabilities) {
|
|
1556
|
+
this.initializeCapabilities = runtimeCapabilities;
|
|
1521
1557
|
this.notifyRuntimeCapabilities({ runtimeCapabilities });
|
|
1522
1558
|
}
|
|
1523
1559
|
this.initialized = true;
|
|
@@ -1543,17 +1579,28 @@ var AcpAgentClient = class {
|
|
|
1543
1579
|
this.logger.debug("acp session/update for unknown session", { protocolSessionId });
|
|
1544
1580
|
return;
|
|
1545
1581
|
}
|
|
1582
|
+
if (configOptionUpdateSet(update) !== void 0) {
|
|
1583
|
+
const refreshed = this.captureRuntimeCapabilities(
|
|
1584
|
+
// A live refresh of this session's menus, not a load: keep the source
|
|
1585
|
+
// they were opened with.
|
|
1586
|
+
session.runtimeCapabilities?.source ?? "session-new",
|
|
1587
|
+
update,
|
|
1588
|
+
this.sessionBase(session)
|
|
1589
|
+
);
|
|
1590
|
+
if (refreshed) session.runtimeCapabilities = refreshed;
|
|
1591
|
+
}
|
|
1546
1592
|
const runtimeState = acpSessionRuntimeStateFromUpdate(update, this.now());
|
|
1547
1593
|
if (runtimeState) {
|
|
1548
1594
|
session.runtimeState = mergeAcpRuntimeState(session.runtimeState, runtimeState);
|
|
1549
|
-
|
|
1595
|
+
const capabilities = this.sessionCapabilities(session.threadId);
|
|
1596
|
+
if (capabilities) {
|
|
1550
1597
|
this.notifyRuntimeCapabilities({
|
|
1551
1598
|
threadId: session.threadId,
|
|
1552
|
-
runtimeCapabilities:
|
|
1599
|
+
runtimeCapabilities: capabilities,
|
|
1553
1600
|
runtimeState: session.runtimeState
|
|
1554
1601
|
});
|
|
1555
1602
|
}
|
|
1556
|
-
this.emitThreadSettings(session,
|
|
1603
|
+
this.emitThreadSettings(session, capabilities, session.runtimeState);
|
|
1557
1604
|
return;
|
|
1558
1605
|
}
|
|
1559
1606
|
const updateKind = readString2(update, "sessionUpdate");
|
|
@@ -1623,7 +1670,12 @@ var AcpAgentClient = class {
|
|
|
1623
1670
|
optionId,
|
|
1624
1671
|
value
|
|
1625
1672
|
);
|
|
1626
|
-
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1673
|
+
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1674
|
+
"session-load",
|
|
1675
|
+
result,
|
|
1676
|
+
this.sessionBase(session)
|
|
1677
|
+
);
|
|
1678
|
+
if (runtimeCapabilities) session.runtimeCapabilities = runtimeCapabilities;
|
|
1627
1679
|
const requested = source === "configOption" ? { configValues: { [optionId]: value }, updatedAt: this.now() } : source === "mode" ? { currentModeId: value, updatedAt: this.now() } : source === "modelConfigOption" ? (
|
|
1628
1680
|
// Record BOTH: it IS the config option's value, and it IS the
|
|
1629
1681
|
// session's model. A consumer reading `currentModelId` must not
|
|
@@ -1635,7 +1687,13 @@ var AcpAgentClient = class {
|
|
|
1635
1687
|
}
|
|
1636
1688
|
) : { currentModelId: value, updatedAt: this.now() };
|
|
1637
1689
|
session.runtimeState = mergeAcpRuntimeState(session.runtimeState, requested);
|
|
1638
|
-
const
|
|
1690
|
+
const reply = asRecord2(result);
|
|
1691
|
+
const reported = runtimeStateFromConfigOptionSet(
|
|
1692
|
+
reply?.configOptions ?? reply?.config_options,
|
|
1693
|
+
this.now()
|
|
1694
|
+
);
|
|
1695
|
+
if (reported) session.runtimeState = mergeAcpRuntimeState(session.runtimeState, reported);
|
|
1696
|
+
const effectiveCapabilities = this.sessionCapabilities(threadId);
|
|
1639
1697
|
if (effectiveCapabilities) {
|
|
1640
1698
|
this.notifyRuntimeCapabilities({
|
|
1641
1699
|
threadId,
|
|
@@ -1676,7 +1734,8 @@ var AcpAgentClient = class {
|
|
|
1676
1734
|
* If nothing matches we leave it alone — the caller's `.catch` debug-logs. */
|
|
1677
1735
|
async applyReasoning(threadId, reasoning) {
|
|
1678
1736
|
const target = reasoning.toLowerCase();
|
|
1679
|
-
const
|
|
1737
|
+
const capabilities = this.sessionCapabilities(threadId);
|
|
1738
|
+
const modes = capabilities?.modes?.availableModes ?? [];
|
|
1680
1739
|
const modeMatch = modes.find(
|
|
1681
1740
|
(mode) => mode.id.toLowerCase() === target || typeof mode.label === "string" && mode.label.toLowerCase() === target
|
|
1682
1741
|
);
|
|
@@ -1684,7 +1743,7 @@ var AcpAgentClient = class {
|
|
|
1684
1743
|
await this.setMode(threadId, modeMatch.id);
|
|
1685
1744
|
return;
|
|
1686
1745
|
}
|
|
1687
|
-
const thinking =
|
|
1746
|
+
const thinking = capabilities?.configOptions?.find(
|
|
1688
1747
|
(option) => option.category === "thought_level" || option.id === "thinking"
|
|
1689
1748
|
);
|
|
1690
1749
|
if (thinking !== void 0) {
|
|
@@ -1714,18 +1773,32 @@ var AcpAgentClient = class {
|
|
|
1714
1773
|
data: data.toString("base64")
|
|
1715
1774
|
};
|
|
1716
1775
|
}
|
|
1717
|
-
|
|
1776
|
+
/** Normalize `result` into a capability snapshot, filling what it omits from
|
|
1777
|
+
* `base`. Required, not defaulted: an explicit `undefined` would trigger a
|
|
1778
|
+
* default parameter, and the only default on hand is the client-wide
|
|
1779
|
+
* snapshot — which may be another session's. */
|
|
1780
|
+
captureRuntimeCapabilities(source, result, base) {
|
|
1718
1781
|
const runtimeCapabilities = normalizeAcpRuntimeCapabilities({
|
|
1719
1782
|
value: result,
|
|
1720
1783
|
now: this.now(),
|
|
1721
1784
|
source,
|
|
1722
|
-
...
|
|
1785
|
+
...base !== void 0 ? { initialize: base } : {}
|
|
1723
1786
|
});
|
|
1724
1787
|
if (runtimeCapabilities) {
|
|
1725
1788
|
this.runtimeCapabilities = runtimeCapabilities;
|
|
1726
1789
|
}
|
|
1727
1790
|
return runtimeCapabilities;
|
|
1728
1791
|
}
|
|
1792
|
+
/** The option menus to consult when choosing a value for `threadId`. A
|
|
1793
|
+
* session with none of its own gets the agent-level `initialize` menus, never
|
|
1794
|
+
* another session's. */
|
|
1795
|
+
sessionCapabilities(threadId) {
|
|
1796
|
+
return this.sessionBase(this.sessions.get(threadId));
|
|
1797
|
+
}
|
|
1798
|
+
/** What a session's menus are built on: its own, or `initialize`'s. */
|
|
1799
|
+
sessionBase(session) {
|
|
1800
|
+
return session?.runtimeCapabilities ?? this.initializeCapabilities;
|
|
1801
|
+
}
|
|
1729
1802
|
notifyRuntimeCapabilities(event) {
|
|
1730
1803
|
this.runtimeCapabilitiesHandler?.({
|
|
1731
1804
|
...event.threadId !== void 0 ? { threadId: event.threadId } : {},
|
|
@@ -1803,6 +1876,7 @@ function thoughtLevelPolarity(value) {
|
|
|
1803
1876
|
const haystack = `${value.value} ${value.label ?? ""}`.toLowerCase();
|
|
1804
1877
|
if (/\b(off|none|disabled?|no)\b/.test(haystack)) return "off";
|
|
1805
1878
|
if (/\b(on|enabled?|high|full|yes)\b/.test(haystack)) return "on";
|
|
1879
|
+
if (/\b(low|minimal|min|minimum)\b/.test(haystack)) return "low";
|
|
1806
1880
|
return "other";
|
|
1807
1881
|
}
|
|
1808
1882
|
function reasoningValueForThoughtLevel(reasoning, values) {
|
|
@@ -1810,8 +1884,9 @@ function reasoningValueForThoughtLevel(reasoning, values) {
|
|
|
1810
1884
|
const wantOff = LOW_EFFORT_TOKENS.has(token);
|
|
1811
1885
|
const wantOn = HIGH_EFFORT_TOKENS.has(token);
|
|
1812
1886
|
if (!wantOff && !wantOn) return void 0;
|
|
1813
|
-
const
|
|
1814
|
-
|
|
1887
|
+
const withPolarity = (polarity) => values.find((value) => thoughtLevelPolarity(value) === polarity);
|
|
1888
|
+
if (wantOn) return withPolarity("on")?.value;
|
|
1889
|
+
return (withPolarity("off") ?? withPolarity("low"))?.value;
|
|
1815
1890
|
}
|
|
1816
1891
|
function buildApprovalRequest(args) {
|
|
1817
1892
|
const toolCall = asRecord2(args.params.toolCall) ?? {};
|