@pwrdrvr/agent-acp 0.15.0 → 0.16.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 +37 -5
- package/dist/index.js +42 -6
- 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;
|
|
@@ -389,8 +401,9 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
389
401
|
* Public `AgentBackend.startThread`: accepts NEUTRAL `AgentStartThreadOptions`
|
|
390
402
|
* and maps them onto an ACP `session/new`. ACP supports:
|
|
391
403
|
* • `cwd` → `session/new.cwd`.
|
|
392
|
-
* • `model` → applied
|
|
393
|
-
* the
|
|
404
|
+
* • `model` → applied after the session opens, by whichever route the agent
|
|
405
|
+
* advertises: the `model` config option, or legacy `session/set_model`
|
|
406
|
+
* (best-effort; debug-logged if not). See `setModel`.
|
|
394
407
|
* • `instructions` is NOT injected here — ACP `session/new` has no base-
|
|
395
408
|
* instructions slot, matching the adapter's existing behavior. A host that
|
|
396
409
|
* wants system framing sends it as leading turn text.
|
|
@@ -480,6 +493,25 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
480
493
|
* releasing here never blocks resume. */
|
|
481
494
|
archiveThread(threadId: string): Promise<void>;
|
|
482
495
|
setMode(threadId: string, modeId: string): Promise<void>;
|
|
496
|
+
/** Apply a model selection by whichever route the agent actually advertises.
|
|
497
|
+
*
|
|
498
|
+
* ACP 1.3 has NO `session/set_model` — it is absent from `AGENT_METHODS`.
|
|
499
|
+
* An agent that offers model choice does it through a config option with
|
|
500
|
+
* `category: "model"` (Kimi's `session/new` returns exactly that, and nothing
|
|
501
|
+
* under `models`). Some agents still accept `session/set_model` as a vendor
|
|
502
|
+
* extension, and the `models.availableModels` shape is still honoured for
|
|
503
|
+
* them, but it is not what the protocol defines any more.
|
|
504
|
+
*
|
|
505
|
+
* Two reasons to prefer the config option when the agent offers it. The READ
|
|
506
|
+
* path already draws this distinction — `modelIdFromCapabilities` and
|
|
507
|
+
* `modelsFromCapabilities` both fall back to the config option — and the two
|
|
508
|
+
* disagreeing lets a host list models it cannot then select. And the config
|
|
509
|
+
* option's REPLY carries the agent's full refreshed config set, where
|
|
510
|
+
* `session/set_model` answers `{}` and refreshes only via an async
|
|
511
|
+
* notification. On Kimi the model DETERMINES which thought levels exist —
|
|
512
|
+
* `K2.7 Coding` offers only `on`, `K3` offers `low`/`high`/`max` — and
|
|
513
|
+
* `applyReasoning` picks from `runtimeCapabilities` on the following
|
|
514
|
+
* `startTurn`, so the refresh this write leaves behind is what it reads. */
|
|
483
515
|
setModel(threadId: string, modelId: string): Promise<void>;
|
|
484
516
|
setConfigOption(threadId: string, optionId: string, value: string): Promise<void>;
|
|
485
517
|
close(): Promise<void>;
|
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 {};
|
|
@@ -1061,8 +1062,9 @@ var AcpAgentClient = class {
|
|
|
1061
1062
|
* Public `AgentBackend.startThread`: accepts NEUTRAL `AgentStartThreadOptions`
|
|
1062
1063
|
* and maps them onto an ACP `session/new`. ACP supports:
|
|
1063
1064
|
* • `cwd` → `session/new.cwd`.
|
|
1064
|
-
* • `model` → applied
|
|
1065
|
-
* the
|
|
1065
|
+
* • `model` → applied after the session opens, by whichever route the agent
|
|
1066
|
+
* advertises: the `model` config option, or legacy `session/set_model`
|
|
1067
|
+
* (best-effort; debug-logged if not). See `setModel`.
|
|
1066
1068
|
* • `instructions` is NOT injected here — ACP `session/new` has no base-
|
|
1067
1069
|
* instructions slot, matching the adapter's existing behavior. A host that
|
|
1068
1070
|
* wants system framing sends it as leading turn text.
|
|
@@ -1440,7 +1442,32 @@ var AcpAgentClient = class {
|
|
|
1440
1442
|
async setMode(threadId, modeId) {
|
|
1441
1443
|
await this.setRuntimeOption(threadId, "mode", modeId, modeId);
|
|
1442
1444
|
}
|
|
1445
|
+
/** Apply a model selection by whichever route the agent actually advertises.
|
|
1446
|
+
*
|
|
1447
|
+
* ACP 1.3 has NO `session/set_model` — it is absent from `AGENT_METHODS`.
|
|
1448
|
+
* An agent that offers model choice does it through a config option with
|
|
1449
|
+
* `category: "model"` (Kimi's `session/new` returns exactly that, and nothing
|
|
1450
|
+
* under `models`). Some agents still accept `session/set_model` as a vendor
|
|
1451
|
+
* extension, and the `models.availableModels` shape is still honoured for
|
|
1452
|
+
* them, but it is not what the protocol defines any more.
|
|
1453
|
+
*
|
|
1454
|
+
* Two reasons to prefer the config option when the agent offers it. The READ
|
|
1455
|
+
* path already draws this distinction — `modelIdFromCapabilities` and
|
|
1456
|
+
* `modelsFromCapabilities` both fall back to the config option — and the two
|
|
1457
|
+
* disagreeing lets a host list models it cannot then select. And the config
|
|
1458
|
+
* option's REPLY carries the agent's full refreshed config set, where
|
|
1459
|
+
* `session/set_model` answers `{}` and refreshes only via an async
|
|
1460
|
+
* notification. On Kimi the model DETERMINES which thought levels exist —
|
|
1461
|
+
* `K2.7 Coding` offers only `on`, `K3` offers `low`/`high`/`max` — and
|
|
1462
|
+
* `applyReasoning` picks from `runtimeCapabilities` on the following
|
|
1463
|
+
* `startTurn`, so the refresh this write leaves behind is what it reads. */
|
|
1443
1464
|
async setModel(threadId, modelId) {
|
|
1465
|
+
const advertisesModelsCapability = (this.runtimeCapabilities?.models?.availableModels?.length ?? 0) > 0;
|
|
1466
|
+
const option = modelConfigOption(this.runtimeCapabilities);
|
|
1467
|
+
if (!advertisesModelsCapability && option !== void 0) {
|
|
1468
|
+
await this.setRuntimeOption(threadId, "modelConfigOption", option.id, modelId);
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1444
1471
|
await this.setRuntimeOption(threadId, "model", modelId, modelId);
|
|
1445
1472
|
}
|
|
1446
1473
|
async setConfigOption(threadId, optionId, value) {
|
|
@@ -1597,7 +1624,16 @@ var AcpAgentClient = class {
|
|
|
1597
1624
|
value
|
|
1598
1625
|
);
|
|
1599
1626
|
const runtimeCapabilities = this.captureRuntimeCapabilities("session-load", result);
|
|
1600
|
-
const requested = source === "configOption" ? { configValues: { [optionId]: value }, updatedAt: this.now() } : source === "mode" ? { currentModeId: value, updatedAt: this.now() } :
|
|
1627
|
+
const requested = source === "configOption" ? { configValues: { [optionId]: value }, updatedAt: this.now() } : source === "mode" ? { currentModeId: value, updatedAt: this.now() } : source === "modelConfigOption" ? (
|
|
1628
|
+
// Record BOTH: it IS the config option's value, and it IS the
|
|
1629
|
+
// session's model. A consumer reading `currentModelId` must not
|
|
1630
|
+
// have to know which route the agent happened to expose.
|
|
1631
|
+
{
|
|
1632
|
+
currentModelId: value,
|
|
1633
|
+
configValues: { [optionId]: value },
|
|
1634
|
+
updatedAt: this.now()
|
|
1635
|
+
}
|
|
1636
|
+
) : { currentModelId: value, updatedAt: this.now() };
|
|
1601
1637
|
session.runtimeState = mergeAcpRuntimeState(session.runtimeState, requested);
|
|
1602
1638
|
const effectiveCapabilities = runtimeCapabilities ?? this.runtimeCapabilities;
|
|
1603
1639
|
if (effectiveCapabilities) {
|
|
@@ -1610,7 +1646,7 @@ var AcpAgentClient = class {
|
|
|
1610
1646
|
this.emitThreadSettings(session, effectiveCapabilities, session.runtimeState);
|
|
1611
1647
|
}
|
|
1612
1648
|
async setRuntimeOptionOnTransport(protocolSessionId, source, optionId, value) {
|
|
1613
|
-
if (source === "configOption") {
|
|
1649
|
+
if (source === "configOption" || source === "modelConfigOption") {
|
|
1614
1650
|
return await this.transport.request("session/set_config_option", {
|
|
1615
1651
|
sessionId: protocolSessionId,
|
|
1616
1652
|
configId: optionId,
|