@pwrdrvr/agent-acp 0.15.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 +56 -8
- package/dist/index.js +133 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,16 @@ interface AcpJsonRpcTransport {
|
|
|
14
14
|
* drives. Injectable (via `createConnection`) so the adapter can be unit-tested
|
|
15
15
|
* without spawning a real agent. The library's `ClientSideConnection` satisfies
|
|
16
16
|
* this structurally (its methods take typed schema params; we pass records that
|
|
17
|
-
* match at runtime).
|
|
17
|
+
* match at runtime).
|
|
18
|
+
*
|
|
19
|
+
* Every entry here MUST exist on the real `ClientSideConnection`. It is
|
|
20
|
+
* attached with an `as unknown as` cast (see `createConnection`), so a name
|
|
21
|
+
* that is merely plausible type-checks and then throws
|
|
22
|
+
* "conn.<name> is not a function" at runtime, on the wire, against a user's
|
|
23
|
+
* agent. `setSessionModel` was such an entry: ACP 1.3 has no `session/set_model`
|
|
24
|
+
* at all, so every model selection died before it was sent. Anything the SDK
|
|
25
|
+
* does not expose as a typed method belongs in `dispatch`'s default arm, which
|
|
26
|
+
* sends it verbatim through `request()`. */
|
|
18
27
|
interface AcpAgentConnection {
|
|
19
28
|
initialize(params: Record<string, unknown>): Promise<unknown>;
|
|
20
29
|
newSession(params: Record<string, unknown>): Promise<unknown>;
|
|
@@ -22,7 +31,6 @@ interface AcpAgentConnection {
|
|
|
22
31
|
prompt(params: Record<string, unknown>): Promise<unknown>;
|
|
23
32
|
cancel(params: Record<string, unknown>): Promise<void>;
|
|
24
33
|
setSessionMode(params: Record<string, unknown>): Promise<unknown>;
|
|
25
|
-
setSessionModel(params: Record<string, unknown>): Promise<unknown>;
|
|
26
34
|
authenticate(params: Record<string, unknown>): Promise<unknown>;
|
|
27
35
|
request(method: string, params: Record<string, unknown>): Promise<unknown>;
|
|
28
36
|
notify(method: string, params: Record<string, unknown>): Promise<void>;
|
|
@@ -301,7 +309,11 @@ type AcpPromptContentBlock = {
|
|
|
301
309
|
mimeType: string;
|
|
302
310
|
data: string;
|
|
303
311
|
};
|
|
304
|
-
type AcpRuntimeOptionSource = "mode" | "model" | "configOption"
|
|
312
|
+
type AcpRuntimeOptionSource = "mode" | "model" | "configOption"
|
|
313
|
+
/** A model write that travels as `session/set_config_option` because the
|
|
314
|
+
* agent exposes model choice as a config option rather than via the legacy
|
|
315
|
+
* top-level `models` capability. See `setModel`. */
|
|
316
|
+
| "modelConfigOption";
|
|
305
317
|
type AcpAgentClientOptions = {
|
|
306
318
|
/** The transport (real stdio or a fake). */
|
|
307
319
|
transport: AcpJsonRpcTransport;
|
|
@@ -372,6 +384,11 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
372
384
|
private unsubscribeRequest;
|
|
373
385
|
private initialized;
|
|
374
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?;
|
|
375
392
|
constructor(options: AcpAgentClientOptions);
|
|
376
393
|
private readonly clientName;
|
|
377
394
|
private readonly clientTitle;
|
|
@@ -389,8 +406,9 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
389
406
|
* Public `AgentBackend.startThread`: accepts NEUTRAL `AgentStartThreadOptions`
|
|
390
407
|
* and maps them onto an ACP `session/new`. ACP supports:
|
|
391
408
|
* • `cwd` → `session/new.cwd`.
|
|
392
|
-
* • `model` → applied
|
|
393
|
-
* the
|
|
409
|
+
* • `model` → applied after the session opens, by whichever route the agent
|
|
410
|
+
* advertises: the `model` config option, or legacy `session/set_model`
|
|
411
|
+
* (best-effort; debug-logged if not). See `setModel`.
|
|
394
412
|
* • `instructions` is NOT injected here — ACP `session/new` has no base-
|
|
395
413
|
* instructions slot, matching the adapter's existing behavior. A host that
|
|
396
414
|
* wants system framing sends it as leading turn text.
|
|
@@ -480,6 +498,25 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
480
498
|
* releasing here never blocks resume. */
|
|
481
499
|
archiveThread(threadId: string): Promise<void>;
|
|
482
500
|
setMode(threadId: string, modeId: string): Promise<void>;
|
|
501
|
+
/** Apply a model selection by whichever route the agent actually advertises.
|
|
502
|
+
*
|
|
503
|
+
* ACP 1.3 has NO `session/set_model` — it is absent from `AGENT_METHODS`.
|
|
504
|
+
* An agent that offers model choice does it through a config option with
|
|
505
|
+
* `category: "model"` (Kimi's `session/new` returns exactly that, and nothing
|
|
506
|
+
* under `models`). Some agents still accept `session/set_model` as a vendor
|
|
507
|
+
* extension, and the `models.availableModels` shape is still honoured for
|
|
508
|
+
* them, but it is not what the protocol defines any more.
|
|
509
|
+
*
|
|
510
|
+
* Two reasons to prefer the config option when the agent offers it. The READ
|
|
511
|
+
* path already draws this distinction — `modelIdFromCapabilities` and
|
|
512
|
+
* `modelsFromCapabilities` both fall back to the config option — and the two
|
|
513
|
+
* disagreeing lets a host list models it cannot then select. And the config
|
|
514
|
+
* option's REPLY carries the agent's full refreshed config set, where
|
|
515
|
+
* `session/set_model` answers `{}` and refreshes only via an async
|
|
516
|
+
* notification. On Kimi the model DETERMINES which thought levels exist —
|
|
517
|
+
* `K2.7 Coding` offers only `on`, `K3` offers `low`/`high`/`max` — and
|
|
518
|
+
* `applyReasoning` picks from THIS session's menus on the following
|
|
519
|
+
* `startTurn`, so the refresh this write leaves behind is what it reads. */
|
|
483
520
|
setModel(threadId: string, modelId: string): Promise<void>;
|
|
484
521
|
setConfigOption(threadId: string, optionId: string, value: string): Promise<void>;
|
|
485
522
|
close(): Promise<void>;
|
|
@@ -505,7 +542,17 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
505
542
|
/** Read an image file and build an ACP `image` content block (base64 + inferred
|
|
506
543
|
* mimeType). Throws on read failure; the caller catches + skips. */
|
|
507
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. */
|
|
508
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;
|
|
509
556
|
private notifyRuntimeCapabilities;
|
|
510
557
|
private emitThreadSettings;
|
|
511
558
|
private requireSession;
|
|
@@ -518,9 +565,10 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
518
565
|
supportsSseMcp(): boolean;
|
|
519
566
|
}
|
|
520
567
|
/** Pick the value to set on a "thinking" config option for a neutral reasoning
|
|
521
|
-
* token:
|
|
522
|
-
*
|
|
523
|
-
*
|
|
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).
|
|
524
572
|
* Exported for testing. */
|
|
525
573
|
declare function reasoningValueForThoughtLevel(reasoning: string, values: ReadonlyArray<AcpRuntimeConfigOptionValue>): string | undefined;
|
|
526
574
|
|
package/dist/index.js
CHANGED
|
@@ -144,8 +144,9 @@ var AcpConnection = class {
|
|
|
144
144
|
return await conn.prompt(params);
|
|
145
145
|
case "session/set_mode":
|
|
146
146
|
return await conn.setSessionMode(params);
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
// `session/set_model` is deliberately NOT cased here: SDK 1.3 has no
|
|
148
|
+
// typed `setSessionModel`, so it goes out through the default arm's
|
|
149
|
+
// generic `request()` for agents still speaking the older method.
|
|
149
150
|
case "session/cancel":
|
|
150
151
|
await conn.cancel(params);
|
|
151
152
|
return {};
|
|
@@ -701,9 +702,10 @@ function acpRuntimeSupportsSseMcp(capabilities) {
|
|
|
701
702
|
return capabilities?.agentCapabilities?.mcp?.sse === true;
|
|
702
703
|
}
|
|
703
704
|
function modelConfigOption(capabilities) {
|
|
704
|
-
return capabilities?.configOptions?.find(
|
|
705
|
-
|
|
706
|
-
|
|
705
|
+
return capabilities?.configOptions?.find(isModelConfigOption);
|
|
706
|
+
}
|
|
707
|
+
function isModelConfigOption(option) {
|
|
708
|
+
return option.category === "model" || option.id === "model";
|
|
707
709
|
}
|
|
708
710
|
function modelIdFromCapabilities(capabilities) {
|
|
709
711
|
return capabilities?.models?.currentModelId ?? modelConfigOption(capabilities)?.currentValue;
|
|
@@ -750,6 +752,8 @@ function acpSessionRuntimeStateFromUpdate(update, now) {
|
|
|
750
752
|
return currentModeId ? { currentModeId, updatedAt: now } : void 0;
|
|
751
753
|
}
|
|
752
754
|
if (kind === "config_option_update") {
|
|
755
|
+
const fullSet = configOptionUpdateSet(update);
|
|
756
|
+
if (fullSet !== void 0) return runtimeStateFromConfigOptionSet(fullSet, now);
|
|
753
757
|
const configOption = asRecord(update.configOption ?? update.config_option) ?? update;
|
|
754
758
|
const id = readString(configOption, "id") ?? readString(configOption, "configOptionId") ?? readString(configOption, "configId");
|
|
755
759
|
const value = readString(configOption, "currentValue") ?? readString(configOption, "value");
|
|
@@ -767,6 +771,24 @@ function mergeAcpRuntimeState(existing, update) {
|
|
|
767
771
|
}
|
|
768
772
|
};
|
|
769
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
|
+
}
|
|
770
792
|
function modeLabelFor(capabilities, modeId) {
|
|
771
793
|
return capabilities?.modes?.availableModes.find((mode) => mode.id === modeId)?.label;
|
|
772
794
|
}
|
|
@@ -1005,6 +1027,11 @@ var AcpAgentClient = class {
|
|
|
1005
1027
|
unsubscribeRequest = void 0;
|
|
1006
1028
|
initialized = false;
|
|
1007
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;
|
|
1008
1035
|
constructor(options) {
|
|
1009
1036
|
this.transport = options.transport;
|
|
1010
1037
|
this.strategy = options.strategy;
|
|
@@ -1061,8 +1088,9 @@ var AcpAgentClient = class {
|
|
|
1061
1088
|
* Public `AgentBackend.startThread`: accepts NEUTRAL `AgentStartThreadOptions`
|
|
1062
1089
|
* and maps them onto an ACP `session/new`. ACP supports:
|
|
1063
1090
|
* • `cwd` → `session/new.cwd`.
|
|
1064
|
-
* • `model` → applied
|
|
1065
|
-
* the
|
|
1091
|
+
* • `model` → applied after the session opens, by whichever route the agent
|
|
1092
|
+
* advertises: the `model` config option, or legacy `session/set_model`
|
|
1093
|
+
* (best-effort; debug-logged if not). See `setModel`.
|
|
1066
1094
|
* • `instructions` is NOT injected here — ACP `session/new` has no base-
|
|
1067
1095
|
* instructions slot, matching the adapter's existing behavior. A host that
|
|
1068
1096
|
* wants system framing sends it as leading turn text.
|
|
@@ -1217,6 +1245,7 @@ var AcpAgentClient = class {
|
|
|
1217
1245
|
normalizer: new AcpSessionNormalizer({ quirks: this.strategy.quirks }),
|
|
1218
1246
|
turnId: void 0,
|
|
1219
1247
|
runtimeState: void 0,
|
|
1248
|
+
runtimeCapabilities: void 0,
|
|
1220
1249
|
pendingTurn: void 0,
|
|
1221
1250
|
pendingInstructions: options.instructions,
|
|
1222
1251
|
mcpServerNames: configuredMcpServers.map((server) => server.name)
|
|
@@ -1258,6 +1287,7 @@ var AcpAgentClient = class {
|
|
|
1258
1287
|
normalizer: new AcpSessionNormalizer({ quirks: this.strategy.quirks }),
|
|
1259
1288
|
turnId: void 0,
|
|
1260
1289
|
runtimeState: void 0,
|
|
1290
|
+
runtimeCapabilities: void 0,
|
|
1261
1291
|
pendingTurn: void 0,
|
|
1262
1292
|
pendingInstructions: options.instructions,
|
|
1263
1293
|
// Remember ONLY the MCP servers attached to THIS session so the host's
|
|
@@ -1270,9 +1300,11 @@ var AcpAgentClient = class {
|
|
|
1270
1300
|
}
|
|
1271
1301
|
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1272
1302
|
options.lifecycle === "load" ? "session-load" : "session-new",
|
|
1273
|
-
result
|
|
1303
|
+
result,
|
|
1304
|
+
this.sessionBase(session)
|
|
1274
1305
|
);
|
|
1275
1306
|
if (runtimeCapabilities) {
|
|
1307
|
+
session.runtimeCapabilities = runtimeCapabilities;
|
|
1276
1308
|
const runtimeState = acpSessionRuntimeStateFromCapabilities(
|
|
1277
1309
|
runtimeCapabilities,
|
|
1278
1310
|
this.now()
|
|
@@ -1440,7 +1472,33 @@ var AcpAgentClient = class {
|
|
|
1440
1472
|
async setMode(threadId, modeId) {
|
|
1441
1473
|
await this.setRuntimeOption(threadId, "mode", modeId, modeId);
|
|
1442
1474
|
}
|
|
1475
|
+
/** Apply a model selection by whichever route the agent actually advertises.
|
|
1476
|
+
*
|
|
1477
|
+
* ACP 1.3 has NO `session/set_model` — it is absent from `AGENT_METHODS`.
|
|
1478
|
+
* An agent that offers model choice does it through a config option with
|
|
1479
|
+
* `category: "model"` (Kimi's `session/new` returns exactly that, and nothing
|
|
1480
|
+
* under `models`). Some agents still accept `session/set_model` as a vendor
|
|
1481
|
+
* extension, and the `models.availableModels` shape is still honoured for
|
|
1482
|
+
* them, but it is not what the protocol defines any more.
|
|
1483
|
+
*
|
|
1484
|
+
* Two reasons to prefer the config option when the agent offers it. The READ
|
|
1485
|
+
* path already draws this distinction — `modelIdFromCapabilities` and
|
|
1486
|
+
* `modelsFromCapabilities` both fall back to the config option — and the two
|
|
1487
|
+
* disagreeing lets a host list models it cannot then select. And the config
|
|
1488
|
+
* option's REPLY carries the agent's full refreshed config set, where
|
|
1489
|
+
* `session/set_model` answers `{}` and refreshes only via an async
|
|
1490
|
+
* notification. On Kimi the model DETERMINES which thought levels exist —
|
|
1491
|
+
* `K2.7 Coding` offers only `on`, `K3` offers `low`/`high`/`max` — and
|
|
1492
|
+
* `applyReasoning` picks from THIS session's menus on the following
|
|
1493
|
+
* `startTurn`, so the refresh this write leaves behind is what it reads. */
|
|
1443
1494
|
async setModel(threadId, modelId) {
|
|
1495
|
+
const capabilities = this.sessionCapabilities(threadId);
|
|
1496
|
+
const advertisesModelsCapability = (capabilities?.models?.availableModels?.length ?? 0) > 0;
|
|
1497
|
+
const option = modelConfigOption(capabilities);
|
|
1498
|
+
if (!advertisesModelsCapability && option !== void 0) {
|
|
1499
|
+
await this.setRuntimeOption(threadId, "modelConfigOption", option.id, modelId);
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1444
1502
|
await this.setRuntimeOption(threadId, "model", modelId, modelId);
|
|
1445
1503
|
}
|
|
1446
1504
|
async setConfigOption(threadId, optionId, value) {
|
|
@@ -1489,8 +1547,13 @@ var AcpAgentClient = class {
|
|
|
1489
1547
|
},
|
|
1490
1548
|
ACP_REQUEST_TIMEOUT_MS
|
|
1491
1549
|
);
|
|
1492
|
-
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1550
|
+
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1551
|
+
"initialize",
|
|
1552
|
+
result,
|
|
1553
|
+
this.initializeCapabilities
|
|
1554
|
+
);
|
|
1493
1555
|
if (runtimeCapabilities) {
|
|
1556
|
+
this.initializeCapabilities = runtimeCapabilities;
|
|
1494
1557
|
this.notifyRuntimeCapabilities({ runtimeCapabilities });
|
|
1495
1558
|
}
|
|
1496
1559
|
this.initialized = true;
|
|
@@ -1516,17 +1579,28 @@ var AcpAgentClient = class {
|
|
|
1516
1579
|
this.logger.debug("acp session/update for unknown session", { protocolSessionId });
|
|
1517
1580
|
return;
|
|
1518
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
|
+
}
|
|
1519
1592
|
const runtimeState = acpSessionRuntimeStateFromUpdate(update, this.now());
|
|
1520
1593
|
if (runtimeState) {
|
|
1521
1594
|
session.runtimeState = mergeAcpRuntimeState(session.runtimeState, runtimeState);
|
|
1522
|
-
|
|
1595
|
+
const capabilities = this.sessionCapabilities(session.threadId);
|
|
1596
|
+
if (capabilities) {
|
|
1523
1597
|
this.notifyRuntimeCapabilities({
|
|
1524
1598
|
threadId: session.threadId,
|
|
1525
|
-
runtimeCapabilities:
|
|
1599
|
+
runtimeCapabilities: capabilities,
|
|
1526
1600
|
runtimeState: session.runtimeState
|
|
1527
1601
|
});
|
|
1528
1602
|
}
|
|
1529
|
-
this.emitThreadSettings(session,
|
|
1603
|
+
this.emitThreadSettings(session, capabilities, session.runtimeState);
|
|
1530
1604
|
return;
|
|
1531
1605
|
}
|
|
1532
1606
|
const updateKind = readString2(update, "sessionUpdate");
|
|
@@ -1596,10 +1670,30 @@ var AcpAgentClient = class {
|
|
|
1596
1670
|
optionId,
|
|
1597
1671
|
value
|
|
1598
1672
|
);
|
|
1599
|
-
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1600
|
-
|
|
1673
|
+
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1674
|
+
"session-load",
|
|
1675
|
+
result,
|
|
1676
|
+
this.sessionBase(session)
|
|
1677
|
+
);
|
|
1678
|
+
if (runtimeCapabilities) session.runtimeCapabilities = runtimeCapabilities;
|
|
1679
|
+
const requested = source === "configOption" ? { configValues: { [optionId]: value }, updatedAt: this.now() } : source === "mode" ? { currentModeId: value, updatedAt: this.now() } : source === "modelConfigOption" ? (
|
|
1680
|
+
// Record BOTH: it IS the config option's value, and it IS the
|
|
1681
|
+
// session's model. A consumer reading `currentModelId` must not
|
|
1682
|
+
// have to know which route the agent happened to expose.
|
|
1683
|
+
{
|
|
1684
|
+
currentModelId: value,
|
|
1685
|
+
configValues: { [optionId]: value },
|
|
1686
|
+
updatedAt: this.now()
|
|
1687
|
+
}
|
|
1688
|
+
) : { currentModelId: value, updatedAt: this.now() };
|
|
1601
1689
|
session.runtimeState = mergeAcpRuntimeState(session.runtimeState, requested);
|
|
1602
|
-
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);
|
|
1603
1697
|
if (effectiveCapabilities) {
|
|
1604
1698
|
this.notifyRuntimeCapabilities({
|
|
1605
1699
|
threadId,
|
|
@@ -1610,7 +1704,7 @@ var AcpAgentClient = class {
|
|
|
1610
1704
|
this.emitThreadSettings(session, effectiveCapabilities, session.runtimeState);
|
|
1611
1705
|
}
|
|
1612
1706
|
async setRuntimeOptionOnTransport(protocolSessionId, source, optionId, value) {
|
|
1613
|
-
if (source === "configOption") {
|
|
1707
|
+
if (source === "configOption" || source === "modelConfigOption") {
|
|
1614
1708
|
return await this.transport.request("session/set_config_option", {
|
|
1615
1709
|
sessionId: protocolSessionId,
|
|
1616
1710
|
configId: optionId,
|
|
@@ -1640,7 +1734,8 @@ var AcpAgentClient = class {
|
|
|
1640
1734
|
* If nothing matches we leave it alone — the caller's `.catch` debug-logs. */
|
|
1641
1735
|
async applyReasoning(threadId, reasoning) {
|
|
1642
1736
|
const target = reasoning.toLowerCase();
|
|
1643
|
-
const
|
|
1737
|
+
const capabilities = this.sessionCapabilities(threadId);
|
|
1738
|
+
const modes = capabilities?.modes?.availableModes ?? [];
|
|
1644
1739
|
const modeMatch = modes.find(
|
|
1645
1740
|
(mode) => mode.id.toLowerCase() === target || typeof mode.label === "string" && mode.label.toLowerCase() === target
|
|
1646
1741
|
);
|
|
@@ -1648,7 +1743,7 @@ var AcpAgentClient = class {
|
|
|
1648
1743
|
await this.setMode(threadId, modeMatch.id);
|
|
1649
1744
|
return;
|
|
1650
1745
|
}
|
|
1651
|
-
const thinking =
|
|
1746
|
+
const thinking = capabilities?.configOptions?.find(
|
|
1652
1747
|
(option) => option.category === "thought_level" || option.id === "thinking"
|
|
1653
1748
|
);
|
|
1654
1749
|
if (thinking !== void 0) {
|
|
@@ -1678,18 +1773,32 @@ var AcpAgentClient = class {
|
|
|
1678
1773
|
data: data.toString("base64")
|
|
1679
1774
|
};
|
|
1680
1775
|
}
|
|
1681
|
-
|
|
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) {
|
|
1682
1781
|
const runtimeCapabilities = normalizeAcpRuntimeCapabilities({
|
|
1683
1782
|
value: result,
|
|
1684
1783
|
now: this.now(),
|
|
1685
1784
|
source,
|
|
1686
|
-
...
|
|
1785
|
+
...base !== void 0 ? { initialize: base } : {}
|
|
1687
1786
|
});
|
|
1688
1787
|
if (runtimeCapabilities) {
|
|
1689
1788
|
this.runtimeCapabilities = runtimeCapabilities;
|
|
1690
1789
|
}
|
|
1691
1790
|
return runtimeCapabilities;
|
|
1692
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
|
+
}
|
|
1693
1802
|
notifyRuntimeCapabilities(event) {
|
|
1694
1803
|
this.runtimeCapabilitiesHandler?.({
|
|
1695
1804
|
...event.threadId !== void 0 ? { threadId: event.threadId } : {},
|
|
@@ -1767,6 +1876,7 @@ function thoughtLevelPolarity(value) {
|
|
|
1767
1876
|
const haystack = `${value.value} ${value.label ?? ""}`.toLowerCase();
|
|
1768
1877
|
if (/\b(off|none|disabled?|no)\b/.test(haystack)) return "off";
|
|
1769
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";
|
|
1770
1880
|
return "other";
|
|
1771
1881
|
}
|
|
1772
1882
|
function reasoningValueForThoughtLevel(reasoning, values) {
|
|
@@ -1774,8 +1884,9 @@ function reasoningValueForThoughtLevel(reasoning, values) {
|
|
|
1774
1884
|
const wantOff = LOW_EFFORT_TOKENS.has(token);
|
|
1775
1885
|
const wantOn = HIGH_EFFORT_TOKENS.has(token);
|
|
1776
1886
|
if (!wantOff && !wantOn) return void 0;
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1887
|
+
const withPolarity = (polarity) => values.find((value) => thoughtLevelPolarity(value) === polarity);
|
|
1888
|
+
if (wantOn) return withPolarity("on")?.value;
|
|
1889
|
+
return (withPolarity("off") ?? withPolarity("low"))?.value;
|
|
1779
1890
|
}
|
|
1780
1891
|
function buildApprovalRequest(args) {
|
|
1781
1892
|
const toolCall = asRecord2(args.params.toolCall) ?? {};
|