@pwrdrvr/agent-acp 0.7.0 → 0.9.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 +57 -13
- package/dist/index.js +203 -83
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { Client } from '@zed-industries/agent-client-protocol';
|
|
1
2
|
import { Logger, AgentBackend, NormalizedThreadEvent, Unsubscribe, AgentBackendToolCallHandler, AgentBackendApprovalHandler, AgentStartThreadOptions, AgentBackendStartThreadResult, AgentStartTurnOptions, NormalizedTokenUsage, NormalizedToolCall } from '@pwrdrvr/agent-core';
|
|
2
|
-
import { JsonRpcId
|
|
3
|
+
import { JsonRpcId } from '@pwrdrvr/agent-transport';
|
|
3
4
|
|
|
4
|
-
/** The bidirectional transport shape the ACP client drives. */
|
|
5
5
|
interface AcpJsonRpcTransport {
|
|
6
6
|
request(method: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
7
7
|
notify?(method: string, params?: Record<string, unknown>): Promise<void>;
|
|
@@ -9,23 +9,53 @@ interface AcpJsonRpcTransport {
|
|
|
9
9
|
onNotification(listener: (method: string, params: Record<string, unknown>) => void): () => void;
|
|
10
10
|
onRequest?(listener: (method: string, params: Record<string, unknown>, id?: JsonRpcId) => Promise<unknown> | unknown): () => void;
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
/** The subset of the library's client→agent surface that `AcpConnection`
|
|
14
|
+
* drives. Injectable (via `createConnection`) so the adapter can be unit-tested
|
|
15
|
+
* without spawning a real agent. The library's `ClientSideConnection` satisfies
|
|
16
|
+
* this structurally (its methods take typed schema params; we pass records that
|
|
17
|
+
* match at runtime). */
|
|
18
|
+
interface AcpAgentConnection {
|
|
19
|
+
initialize(params: Record<string, unknown>): Promise<unknown>;
|
|
20
|
+
newSession(params: Record<string, unknown>): Promise<unknown>;
|
|
21
|
+
loadSession(params: Record<string, unknown>): Promise<unknown>;
|
|
22
|
+
prompt(params: Record<string, unknown>): Promise<unknown>;
|
|
23
|
+
cancel(params: Record<string, unknown>): Promise<void>;
|
|
24
|
+
setSessionMode(params: Record<string, unknown>): Promise<unknown>;
|
|
25
|
+
setSessionModel(params: Record<string, unknown>): Promise<unknown>;
|
|
26
|
+
authenticate(params: Record<string, unknown>): Promise<unknown>;
|
|
27
|
+
extMethod(method: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
28
|
+
extNotification(method: string, params: Record<string, unknown>): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/** Built connection + a teardown handle (kills the child / closes streams). */
|
|
31
|
+
type AcpAgentConnectionHandle = {
|
|
32
|
+
connection: AcpAgentConnection;
|
|
33
|
+
dispose: () => void;
|
|
34
|
+
};
|
|
35
|
+
type AcpConnectionOptions = {
|
|
13
36
|
command: string;
|
|
14
37
|
args: string[];
|
|
15
38
|
env?: NodeJS.ProcessEnv;
|
|
16
|
-
requestTimeoutMs?: number;
|
|
17
|
-
observer?: JsonRpcObserver;
|
|
18
39
|
logger?: Logger;
|
|
19
|
-
/**
|
|
20
|
-
|
|
40
|
+
/** Test seam: build the underlying connection. Defaults to spawning the agent
|
|
41
|
+
* and wiring its stdio through the library's `ClientSideConnection`. */
|
|
42
|
+
createConnection?: (client: Client) => AcpAgentConnectionHandle;
|
|
21
43
|
};
|
|
22
|
-
declare class
|
|
23
|
-
private readonly
|
|
44
|
+
declare class AcpConnection implements AcpJsonRpcTransport {
|
|
45
|
+
private readonly options;
|
|
46
|
+
private readonly logger;
|
|
47
|
+
private connection;
|
|
48
|
+
private dispose;
|
|
49
|
+
private connecting;
|
|
24
50
|
private readonly notificationListeners;
|
|
25
51
|
private requestHandler;
|
|
26
|
-
constructor(options:
|
|
27
|
-
|
|
28
|
-
|
|
52
|
+
constructor(options: AcpConnectionOptions);
|
|
53
|
+
/** The `Client` handler the library invokes for agent→client traffic. */
|
|
54
|
+
private buildClient;
|
|
55
|
+
private ensureConnected;
|
|
56
|
+
private connect;
|
|
57
|
+
private defaultCreateConnection;
|
|
58
|
+
request(method: string, params?: Record<string, unknown>, _timeoutMs?: number): Promise<unknown>;
|
|
29
59
|
notify(method: string, params?: Record<string, unknown>): Promise<void>;
|
|
30
60
|
close(): Promise<void>;
|
|
31
61
|
onNotification(listener: (method: string, params: Record<string, unknown>) => void): () => void;
|
|
@@ -349,6 +379,20 @@ declare class AcpAgentClient implements AgentBackend {
|
|
|
349
379
|
turnId: string;
|
|
350
380
|
}>;
|
|
351
381
|
interruptTurn(threadId: string): Promise<void>;
|
|
382
|
+
/** Release a thread the host has archived/closed.
|
|
383
|
+
*
|
|
384
|
+
* ACP has NO protocol-level session delete/archive — sessions are
|
|
385
|
+
* connection-scoped and the spec offers only `session/cancel` (interrupt).
|
|
386
|
+
* So unlike the Codex backend (which sends `thread/archive`), there's no
|
|
387
|
+
* remote call to make here. What we MUST do is drop the live session
|
|
388
|
+
* LOCALLY: a long-lived pooled client (one process shared across surfaces,
|
|
389
|
+
* warmed at startup) would otherwise accumulate a dead `AcpSessionState` for
|
|
390
|
+
* every closed chat for the life of the app. Best-effort cancels an
|
|
391
|
+
* in-flight turn first so the agent stops working on a thread the user
|
|
392
|
+
* closed. Idempotent — no-ops if the session is already gone. A later
|
|
393
|
+
* `reopenThread` re-establishes a fresh session under the same threadId, so
|
|
394
|
+
* releasing here never blocks resume. */
|
|
395
|
+
archiveThread(threadId: string): Promise<void>;
|
|
352
396
|
setMode(threadId: string, modeId: string): Promise<void>;
|
|
353
397
|
setModel(threadId: string, modelId: string): Promise<void>;
|
|
354
398
|
setConfigOption(threadId: string, optionId: string, value: string): Promise<void>;
|
|
@@ -743,4 +787,4 @@ declare class AcpRegistryService {
|
|
|
743
787
|
declare function evaluateAcpDistributionPolicy(distribution: AcpRegistryDistribution, allowlist: AcpAllowlistDecision): AcpDistributionPolicy;
|
|
744
788
|
declare function normalizeRegistry(raw: unknown): AcpRegistryAgent[];
|
|
745
789
|
|
|
746
|
-
export { ACP_REGISTRY_URL, AcpAgentAllowlist, type AcpAgentAllowlistRule, AcpAgentClient, type AcpAgentClientFactory, type AcpAgentClientOptions, AcpAgentClientPool, type AcpAgentClientPoolOptions, type AcpAgentInstanceSource, type AcpAgentQuirks, type AcpAgentStrategy, type AcpAllowlistDecision, type AcpApplyContext, type AcpBinaryPlatformDistribution, 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,
|
|
790
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,54 +1,143 @@
|
|
|
1
|
-
// src/acp-
|
|
2
|
-
import {
|
|
1
|
+
// src/acp-connection.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { Readable, Writable } from "stream";
|
|
3
4
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "@
|
|
7
|
-
|
|
8
|
-
var
|
|
5
|
+
ClientSideConnection,
|
|
6
|
+
ndJsonStream
|
|
7
|
+
} from "@zed-industries/agent-client-protocol";
|
|
8
|
+
import { noopLogger } from "@pwrdrvr/agent-core";
|
|
9
|
+
var AcpConnection = class {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.logger = options.logger ?? noopLogger;
|
|
13
|
+
}
|
|
14
|
+
options;
|
|
15
|
+
logger;
|
|
9
16
|
connection;
|
|
17
|
+
dispose;
|
|
18
|
+
connecting;
|
|
10
19
|
notificationListeners = /* @__PURE__ */ new Set();
|
|
11
20
|
requestHandler;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
/** The `Client` handler the library invokes for agent→client traffic. */
|
|
22
|
+
buildClient() {
|
|
23
|
+
const emit = (method, params) => {
|
|
24
|
+
for (const listener of this.notificationListeners) listener(method, params);
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
requestPermission: async (params) => {
|
|
28
|
+
if (!this.requestHandler) {
|
|
29
|
+
throw new Error("ACP request handler unavailable for session/request_permission");
|
|
30
|
+
}
|
|
31
|
+
return await this.requestHandler(
|
|
32
|
+
"session/request_permission",
|
|
33
|
+
params
|
|
34
|
+
);
|
|
35
|
+
},
|
|
36
|
+
sessionUpdate: async (params) => {
|
|
37
|
+
emit("session/update", params);
|
|
38
|
+
},
|
|
39
|
+
extNotification: async (method, params) => {
|
|
40
|
+
emit(method, params);
|
|
41
|
+
},
|
|
42
|
+
extMethod: async (method, params) => {
|
|
43
|
+
if (!this.requestHandler) {
|
|
44
|
+
throw new Error(`ACP request handler unavailable for ${method}`);
|
|
45
|
+
}
|
|
46
|
+
return await this.requestHandler(method, params) ?? {};
|
|
35
47
|
}
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
// No fs/terminal handlers: we don't advertise those client capabilities at
|
|
49
|
+
// `initialize`, so a conformant agent won't call them.
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async ensureConnected() {
|
|
53
|
+
if (this.connection) return;
|
|
54
|
+
this.connecting ??= this.connect();
|
|
55
|
+
await this.connecting;
|
|
38
56
|
}
|
|
39
57
|
async connect() {
|
|
40
|
-
|
|
58
|
+
const create = this.options.createConnection ?? this.defaultCreateConnection();
|
|
59
|
+
const handle = create(this.buildClient());
|
|
60
|
+
this.connection = handle.connection;
|
|
61
|
+
this.dispose = handle.dispose;
|
|
62
|
+
}
|
|
63
|
+
defaultCreateConnection() {
|
|
64
|
+
const { command, args, env } = this.options;
|
|
65
|
+
const logger = this.logger;
|
|
66
|
+
return (client) => {
|
|
67
|
+
const child = spawn(command, args, {
|
|
68
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
69
|
+
...env !== void 0 ? { env } : {}
|
|
70
|
+
});
|
|
71
|
+
child.on("error", (error) => {
|
|
72
|
+
logger.error("acp agent process error", { command, message: error.message });
|
|
73
|
+
});
|
|
74
|
+
child.stderr?.on("data", (chunk) => {
|
|
75
|
+
const line = chunk.toString("utf8").trimEnd();
|
|
76
|
+
if (line.length > 0) logger.debug("acp agent stderr", { line });
|
|
77
|
+
});
|
|
78
|
+
const toAgentStdin = Writable.toWeb(child.stdin);
|
|
79
|
+
const fromAgentStdout = Readable.toWeb(child.stdout);
|
|
80
|
+
const stream = ndJsonStream(toAgentStdin, fromAgentStdout);
|
|
81
|
+
const connection = new ClientSideConnection(
|
|
82
|
+
(_agent) => client,
|
|
83
|
+
stream
|
|
84
|
+
);
|
|
85
|
+
return {
|
|
86
|
+
connection,
|
|
87
|
+
dispose: () => {
|
|
88
|
+
try {
|
|
89
|
+
child.stdin?.end();
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
child.kill();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
};
|
|
41
96
|
}
|
|
42
|
-
async request(method, params,
|
|
43
|
-
await this.
|
|
44
|
-
|
|
97
|
+
async request(method, params = {}, _timeoutMs) {
|
|
98
|
+
await this.ensureConnected();
|
|
99
|
+
const conn = this.connection;
|
|
100
|
+
if (!conn) throw new Error("acp connection unavailable");
|
|
101
|
+
switch (method) {
|
|
102
|
+
case "initialize":
|
|
103
|
+
return await conn.initialize(params);
|
|
104
|
+
case "session/new":
|
|
105
|
+
return await conn.newSession(params);
|
|
106
|
+
case "session/load":
|
|
107
|
+
return await conn.loadSession(params);
|
|
108
|
+
case "session/prompt":
|
|
109
|
+
return await conn.prompt(params);
|
|
110
|
+
case "session/set_mode":
|
|
111
|
+
return await conn.setSessionMode(params);
|
|
112
|
+
case "session/set_model":
|
|
113
|
+
return await conn.setSessionModel(params);
|
|
114
|
+
case "session/cancel":
|
|
115
|
+
await conn.cancel(params);
|
|
116
|
+
return {};
|
|
117
|
+
case "authenticate":
|
|
118
|
+
return await conn.authenticate(params);
|
|
119
|
+
default:
|
|
120
|
+
return await conn.extMethod(method, params);
|
|
121
|
+
}
|
|
45
122
|
}
|
|
46
|
-
async notify(method, params) {
|
|
47
|
-
await this.
|
|
48
|
-
|
|
123
|
+
async notify(method, params = {}) {
|
|
124
|
+
await this.ensureConnected();
|
|
125
|
+
const conn = this.connection;
|
|
126
|
+
if (!conn) throw new Error("acp connection unavailable");
|
|
127
|
+
if (method === "session/cancel") {
|
|
128
|
+
await conn.cancel(params);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
await conn.extNotification(method, params);
|
|
49
132
|
}
|
|
50
133
|
async close() {
|
|
51
|
-
|
|
134
|
+
try {
|
|
135
|
+
this.dispose?.();
|
|
136
|
+
} finally {
|
|
137
|
+
this.connection = void 0;
|
|
138
|
+
this.dispose = void 0;
|
|
139
|
+
this.connecting = void 0;
|
|
140
|
+
}
|
|
52
141
|
}
|
|
53
142
|
onNotification(listener) {
|
|
54
143
|
this.notificationListeners.add(listener);
|
|
@@ -65,9 +154,6 @@ var AcpStdioJsonRpcTransport = class {
|
|
|
65
154
|
};
|
|
66
155
|
}
|
|
67
156
|
};
|
|
68
|
-
function asRecord(value) {
|
|
69
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
70
|
-
}
|
|
71
157
|
|
|
72
158
|
// src/acp-client.ts
|
|
73
159
|
import {
|
|
@@ -85,7 +171,7 @@ import {
|
|
|
85
171
|
} from "@pwrdrvr/agent-core";
|
|
86
172
|
|
|
87
173
|
// src/normalizer/content.ts
|
|
88
|
-
function
|
|
174
|
+
function asRecord(value) {
|
|
89
175
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
90
176
|
}
|
|
91
177
|
function readString(record, key) {
|
|
@@ -122,7 +208,7 @@ function readAcpContentText(value) {
|
|
|
122
208
|
const parts = value.map((item) => readAcpContentText(item)).filter((item) => Boolean(item));
|
|
123
209
|
return parts.length > 0 ? parts.join("\n") : void 0;
|
|
124
210
|
}
|
|
125
|
-
const content =
|
|
211
|
+
const content = asRecord(value);
|
|
126
212
|
if (!content) {
|
|
127
213
|
return void 0;
|
|
128
214
|
}
|
|
@@ -143,7 +229,7 @@ function readFirstLocationPath(record) {
|
|
|
143
229
|
return void 0;
|
|
144
230
|
}
|
|
145
231
|
for (const location of locations) {
|
|
146
|
-
const path5 = readString(
|
|
232
|
+
const path5 = readString(asRecord(location), "path");
|
|
147
233
|
if (path5 && path5.trim()) {
|
|
148
234
|
return path5;
|
|
149
235
|
}
|
|
@@ -244,7 +330,7 @@ function toolCallFromUpdate(update, kind, sessionId) {
|
|
|
244
330
|
}
|
|
245
331
|
function readToolArgs(update) {
|
|
246
332
|
const raw = update.rawInput ?? update.raw_input ?? update.input ?? update.arguments ?? update.args;
|
|
247
|
-
return
|
|
333
|
+
return asRecord(raw) ?? (raw === void 0 ? void 0 : raw);
|
|
248
334
|
}
|
|
249
335
|
function readToolContentText(value) {
|
|
250
336
|
return readContentText({ content: value }, "content");
|
|
@@ -484,7 +570,7 @@ function readPlanSteps(record) {
|
|
|
484
570
|
if (typeof step === "string") {
|
|
485
571
|
return [{ step, status: "pending" }];
|
|
486
572
|
}
|
|
487
|
-
const stepRecord =
|
|
573
|
+
const stepRecord = asRecord(step);
|
|
488
574
|
if (!stepRecord) {
|
|
489
575
|
return [];
|
|
490
576
|
}
|
|
@@ -507,7 +593,7 @@ function readPromptText(content) {
|
|
|
507
593
|
|
|
508
594
|
// src/normalizer/runtime-capabilities.ts
|
|
509
595
|
function normalizeAcpRuntimeCapabilities(params) {
|
|
510
|
-
const record =
|
|
596
|
+
const record = asRecord(params.value);
|
|
511
597
|
if (!record) {
|
|
512
598
|
return params.initialize;
|
|
513
599
|
}
|
|
@@ -589,7 +675,7 @@ function acpSessionRuntimeStateFromUpdate(update, now) {
|
|
|
589
675
|
return currentModeId ? { currentModeId, updatedAt: now } : void 0;
|
|
590
676
|
}
|
|
591
677
|
if (kind === "config_option_update") {
|
|
592
|
-
const configOption =
|
|
678
|
+
const configOption = asRecord(update.configOption ?? update.config_option) ?? update;
|
|
593
679
|
const id = readString(configOption, "id") ?? readString(configOption, "configOptionId") ?? readString(configOption, "configId");
|
|
594
680
|
const value = readString(configOption, "currentValue") ?? readString(configOption, "value");
|
|
595
681
|
return id && value ? { configValues: { [id]: value }, updatedAt: now } : void 0;
|
|
@@ -619,7 +705,7 @@ function readConfigOptions(value) {
|
|
|
619
705
|
return [];
|
|
620
706
|
}
|
|
621
707
|
return value.flatMap((item) => {
|
|
622
|
-
const record =
|
|
708
|
+
const record = asRecord(item);
|
|
623
709
|
const id = readString(record, "id") ?? readString(record, "configOptionId") ?? readString(record, "configId");
|
|
624
710
|
if (!record || !id) {
|
|
625
711
|
return [];
|
|
@@ -648,7 +734,7 @@ function readConfigOptionValues(value) {
|
|
|
648
734
|
return [];
|
|
649
735
|
}
|
|
650
736
|
return value.flatMap((item) => {
|
|
651
|
-
const record =
|
|
737
|
+
const record = asRecord(item);
|
|
652
738
|
const optionValue = readString(record, "value") ?? readString(record, "id") ?? readString(record, "optionId");
|
|
653
739
|
if (!record || !optionValue) {
|
|
654
740
|
return [];
|
|
@@ -662,7 +748,7 @@ function readConfigOptionValues(value) {
|
|
|
662
748
|
});
|
|
663
749
|
}
|
|
664
750
|
function readModes(value) {
|
|
665
|
-
const record =
|
|
751
|
+
const record = asRecord(value);
|
|
666
752
|
const modes = Array.isArray(record?.availableModes) ? record.availableModes.flatMap(readMode) : [];
|
|
667
753
|
if (modes.length === 0) {
|
|
668
754
|
return void 0;
|
|
@@ -673,7 +759,7 @@ function readModes(value) {
|
|
|
673
759
|
return result;
|
|
674
760
|
}
|
|
675
761
|
function readMode(value) {
|
|
676
|
-
const record =
|
|
762
|
+
const record = asRecord(value);
|
|
677
763
|
const id = readString(record, "id") ?? readString(record, "modeId");
|
|
678
764
|
if (!record || !id) {
|
|
679
765
|
return [];
|
|
@@ -687,7 +773,7 @@ function readMode(value) {
|
|
|
687
773
|
return [mode];
|
|
688
774
|
}
|
|
689
775
|
function readModels(value) {
|
|
690
|
-
const record =
|
|
776
|
+
const record = asRecord(value);
|
|
691
777
|
const models = Array.isArray(record?.availableModels) ? record.availableModels.flatMap(readModel) : [];
|
|
692
778
|
if (models.length === 0) {
|
|
693
779
|
return void 0;
|
|
@@ -698,7 +784,7 @@ function readModels(value) {
|
|
|
698
784
|
return result;
|
|
699
785
|
}
|
|
700
786
|
function readModel(value) {
|
|
701
|
-
const record =
|
|
787
|
+
const record = asRecord(value);
|
|
702
788
|
const id = readString(record, "modelId") ?? readString(record, "id");
|
|
703
789
|
if (!record || !id) {
|
|
704
790
|
return [];
|
|
@@ -711,16 +797,16 @@ function readModel(value) {
|
|
|
711
797
|
return [model];
|
|
712
798
|
}
|
|
713
799
|
function readAgentCapabilities(value, sessionCapabilitiesValue) {
|
|
714
|
-
const record =
|
|
715
|
-
const sessionCapabilities =
|
|
716
|
-
const sessionMeta =
|
|
717
|
-
const kimiMeta =
|
|
800
|
+
const record = asRecord(value);
|
|
801
|
+
const sessionCapabilities = asRecord(sessionCapabilitiesValue);
|
|
802
|
+
const sessionMeta = asRecord(sessionCapabilities?._meta);
|
|
803
|
+
const kimiMeta = asRecord(sessionMeta?.kimi);
|
|
718
804
|
if (!record && !sessionCapabilities) {
|
|
719
805
|
return void 0;
|
|
720
806
|
}
|
|
721
|
-
const loadSession = readBoolean(record, "loadSession") ?? readBoolean(record, "load_session") ?? readBoolean(
|
|
722
|
-
const close = readBoolean(
|
|
723
|
-
const cancel = readBoolean(
|
|
807
|
+
const loadSession = readBoolean(record, "loadSession") ?? readBoolean(record, "load_session") ?? readBoolean(asRecord(record?.session), "load");
|
|
808
|
+
const close = readBoolean(asRecord(record?.session), "close");
|
|
809
|
+
const cancel = readBoolean(asRecord(record?.session), "cancel");
|
|
724
810
|
const sessionHistoryReplay = readBoolean(kimiMeta, "sessionHistoryReplay") ?? readBoolean(kimiMeta, "session_history_replay");
|
|
725
811
|
const capabilities = {
|
|
726
812
|
raw: record ?? sessionCapabilities
|
|
@@ -738,7 +824,7 @@ function readAgentCapabilities(value, sessionCapabilitiesValue) {
|
|
|
738
824
|
return capabilities;
|
|
739
825
|
}
|
|
740
826
|
function readAgentInfo(value) {
|
|
741
|
-
const record =
|
|
827
|
+
const record = asRecord(value);
|
|
742
828
|
if (!record) {
|
|
743
829
|
return void 0;
|
|
744
830
|
}
|
|
@@ -960,7 +1046,7 @@ var AcpAgentClient = class {
|
|
|
960
1046
|
cwd,
|
|
961
1047
|
mcpServers
|
|
962
1048
|
});
|
|
963
|
-
const record =
|
|
1049
|
+
const record = asRecord2(result);
|
|
964
1050
|
const protocolSessionId = readString2(record, "sessionId") ?? readString2(record, "session_id");
|
|
965
1051
|
if (!protocolSessionId) {
|
|
966
1052
|
throw new Error("ACP session/new did not return a session id");
|
|
@@ -1106,6 +1192,40 @@ var AcpAgentClient = class {
|
|
|
1106
1192
|
});
|
|
1107
1193
|
}
|
|
1108
1194
|
}
|
|
1195
|
+
/** Release a thread the host has archived/closed.
|
|
1196
|
+
*
|
|
1197
|
+
* ACP has NO protocol-level session delete/archive — sessions are
|
|
1198
|
+
* connection-scoped and the spec offers only `session/cancel` (interrupt).
|
|
1199
|
+
* So unlike the Codex backend (which sends `thread/archive`), there's no
|
|
1200
|
+
* remote call to make here. What we MUST do is drop the live session
|
|
1201
|
+
* LOCALLY: a long-lived pooled client (one process shared across surfaces,
|
|
1202
|
+
* warmed at startup) would otherwise accumulate a dead `AcpSessionState` for
|
|
1203
|
+
* every closed chat for the life of the app. Best-effort cancels an
|
|
1204
|
+
* in-flight turn first so the agent stops working on a thread the user
|
|
1205
|
+
* closed. Idempotent — no-ops if the session is already gone. A later
|
|
1206
|
+
* `reopenThread` re-establishes a fresh session under the same threadId, so
|
|
1207
|
+
* releasing here never blocks resume. */
|
|
1208
|
+
async archiveThread(threadId) {
|
|
1209
|
+
const session = this.sessions.get(threadId);
|
|
1210
|
+
if (session === void 0) return;
|
|
1211
|
+
if (session.turnId !== void 0) {
|
|
1212
|
+
try {
|
|
1213
|
+
if (this.transport.notify) {
|
|
1214
|
+
await this.transport.notify("session/cancel", {
|
|
1215
|
+
sessionId: session.protocolSessionId
|
|
1216
|
+
});
|
|
1217
|
+
} else {
|
|
1218
|
+
await this.transport.request("session/cancel", {
|
|
1219
|
+
sessionId: session.protocolSessionId
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
} catch {
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
this.sessions.delete(threadId);
|
|
1226
|
+
this.threadIdByProtocolId.delete(session.protocolSessionId);
|
|
1227
|
+
this.logger.debug("acp thread archived (session released locally)", { threadId });
|
|
1228
|
+
}
|
|
1109
1229
|
async setMode(threadId, modeId) {
|
|
1110
1230
|
await this.setRuntimeOption(threadId, "mode", modeId, modeId);
|
|
1111
1231
|
}
|
|
@@ -1167,13 +1287,13 @@ var AcpAgentClient = class {
|
|
|
1167
1287
|
if (method !== "session/update" && !vendorMethods.includes(method)) {
|
|
1168
1288
|
return;
|
|
1169
1289
|
}
|
|
1170
|
-
const record =
|
|
1290
|
+
const record = asRecord2(params);
|
|
1171
1291
|
if (!record) return;
|
|
1172
1292
|
this.applySessionUpdate(record);
|
|
1173
1293
|
}
|
|
1174
1294
|
applySessionUpdate(params) {
|
|
1175
1295
|
const protocolSessionId = readString2(params, "sessionId") ?? readString2(params, "session_id");
|
|
1176
|
-
const update =
|
|
1296
|
+
const update = asRecord2(params.update);
|
|
1177
1297
|
if (!protocolSessionId || !update) {
|
|
1178
1298
|
return;
|
|
1179
1299
|
}
|
|
@@ -1366,7 +1486,7 @@ var AcpAgentClient = class {
|
|
|
1366
1486
|
}
|
|
1367
1487
|
};
|
|
1368
1488
|
function buildApprovalRequest(args) {
|
|
1369
|
-
const toolCall =
|
|
1489
|
+
const toolCall = asRecord2(args.params.toolCall) ?? {};
|
|
1370
1490
|
const title = typeof toolCall.title === "string" && toolCall.title.trim() ? toolCall.title.trim() : "ACP tool call";
|
|
1371
1491
|
const toolCallId = readString2(toolCall, "toolCallId") ?? readString2(toolCall, "tool_call_id");
|
|
1372
1492
|
const requestId = args.id == null ? toolCallId ?? `acp:${args.now()}` : String(args.id);
|
|
@@ -1402,7 +1522,7 @@ function readPermissionOptions(value) {
|
|
|
1402
1522
|
return [];
|
|
1403
1523
|
}
|
|
1404
1524
|
return value.flatMap((option) => {
|
|
1405
|
-
const record =
|
|
1525
|
+
const record = asRecord2(option);
|
|
1406
1526
|
const optionId = readString2(record, "optionId");
|
|
1407
1527
|
if (!record || !optionId) {
|
|
1408
1528
|
return [];
|
|
@@ -1477,12 +1597,12 @@ function readString2(record, key) {
|
|
|
1477
1597
|
const value = record?.[key];
|
|
1478
1598
|
return typeof value === "string" ? value : void 0;
|
|
1479
1599
|
}
|
|
1480
|
-
function
|
|
1600
|
+
function asRecord2(value) {
|
|
1481
1601
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1482
1602
|
}
|
|
1483
1603
|
function readAcpPromptUsage(result) {
|
|
1484
|
-
const meta =
|
|
1485
|
-
const tokenCount =
|
|
1604
|
+
const meta = asRecord2(asRecord2(result)?._meta);
|
|
1605
|
+
const tokenCount = asRecord2(asRecord2(meta?.quota)?.token_count);
|
|
1486
1606
|
if (!tokenCount) return void 0;
|
|
1487
1607
|
const num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1488
1608
|
const input = num(tokenCount.input_tokens);
|
|
@@ -2250,7 +2370,7 @@ function evaluateAcpDistributionPolicy(distribution, allowlist) {
|
|
|
2250
2370
|
};
|
|
2251
2371
|
}
|
|
2252
2372
|
function normalizeRegistry(raw) {
|
|
2253
|
-
const record =
|
|
2373
|
+
const record = asRecord3(raw);
|
|
2254
2374
|
const rawAgents = Array.isArray(record?.agents) ? record.agents : [];
|
|
2255
2375
|
return rawAgents.flatMap((item) => {
|
|
2256
2376
|
const agent = normalizeAgent(item);
|
|
@@ -2258,7 +2378,7 @@ function normalizeRegistry(raw) {
|
|
|
2258
2378
|
});
|
|
2259
2379
|
}
|
|
2260
2380
|
function normalizeAgent(raw) {
|
|
2261
|
-
const record =
|
|
2381
|
+
const record = asRecord3(raw);
|
|
2262
2382
|
if (!record || typeof record.id !== "string" || typeof record.name !== "string") {
|
|
2263
2383
|
return void 0;
|
|
2264
2384
|
}
|
|
@@ -2291,7 +2411,7 @@ function normalizeAgent(raw) {
|
|
|
2291
2411
|
return agent;
|
|
2292
2412
|
}
|
|
2293
2413
|
function normalizeDistributions(raw) {
|
|
2294
|
-
const record =
|
|
2414
|
+
const record = asRecord3(raw);
|
|
2295
2415
|
if (!record) {
|
|
2296
2416
|
return [];
|
|
2297
2417
|
}
|
|
@@ -2300,7 +2420,7 @@ function normalizeDistributions(raw) {
|
|
|
2300
2420
|
if (npx) distributions.push(npx);
|
|
2301
2421
|
const uvx = normalizePackageDistribution("uvx", record.uvx);
|
|
2302
2422
|
if (uvx) distributions.push(uvx);
|
|
2303
|
-
const binaryRecord =
|
|
2423
|
+
const binaryRecord = asRecord3(record.binary);
|
|
2304
2424
|
if (binaryRecord) {
|
|
2305
2425
|
for (const [platform, value] of Object.entries(binaryRecord)) {
|
|
2306
2426
|
const binary = normalizeBinaryDistribution(platform, value);
|
|
@@ -2310,7 +2430,7 @@ function normalizeDistributions(raw) {
|
|
|
2310
2430
|
return distributions;
|
|
2311
2431
|
}
|
|
2312
2432
|
function normalizePackageDistribution(kind, raw) {
|
|
2313
|
-
const record =
|
|
2433
|
+
const record = asRecord3(raw);
|
|
2314
2434
|
if (!record || typeof record.package !== "string") {
|
|
2315
2435
|
return void 0;
|
|
2316
2436
|
}
|
|
@@ -2322,7 +2442,7 @@ function normalizePackageDistribution(kind, raw) {
|
|
|
2322
2442
|
};
|
|
2323
2443
|
}
|
|
2324
2444
|
function normalizeBinaryDistribution(platform, raw) {
|
|
2325
|
-
const record =
|
|
2445
|
+
const record = asRecord3(raw);
|
|
2326
2446
|
if (!record || typeof record.archive !== "string" || typeof record.cmd !== "string") {
|
|
2327
2447
|
return void 0;
|
|
2328
2448
|
}
|
|
@@ -2344,7 +2464,7 @@ function normalizeAuth(raw) {
|
|
|
2344
2464
|
if (!raw) {
|
|
2345
2465
|
return { required: false, methods: [] };
|
|
2346
2466
|
}
|
|
2347
|
-
const record =
|
|
2467
|
+
const record = asRecord3(raw);
|
|
2348
2468
|
if (!record) {
|
|
2349
2469
|
return { required: true, methods: ["unknown"], raw };
|
|
2350
2470
|
}
|
|
@@ -2357,7 +2477,7 @@ function normalizeAuth(raw) {
|
|
|
2357
2477
|
raw
|
|
2358
2478
|
};
|
|
2359
2479
|
}
|
|
2360
|
-
function
|
|
2480
|
+
function asRecord3(value) {
|
|
2361
2481
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2362
2482
|
}
|
|
2363
2483
|
function stringValue(value) {
|
|
@@ -2367,7 +2487,7 @@ function stringArray(value) {
|
|
|
2367
2487
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
2368
2488
|
}
|
|
2369
2489
|
function stringRecord(value) {
|
|
2370
|
-
const record =
|
|
2490
|
+
const record = asRecord3(value);
|
|
2371
2491
|
if (!record) {
|
|
2372
2492
|
return {};
|
|
2373
2493
|
}
|
|
@@ -2382,17 +2502,17 @@ export {
|
|
|
2382
2502
|
AcpAgentAllowlist,
|
|
2383
2503
|
AcpAgentClient,
|
|
2384
2504
|
AcpAgentClientPool,
|
|
2505
|
+
AcpConnection,
|
|
2385
2506
|
AcpOneShotClient,
|
|
2386
2507
|
AcpRegistryService,
|
|
2387
2508
|
AcpSessionNormalizer,
|
|
2388
|
-
AcpStdioJsonRpcTransport,
|
|
2389
2509
|
BANNED_ACP_REGISTRY_IDS,
|
|
2390
2510
|
BUILT_IN_ACP_STRATEGIES,
|
|
2391
2511
|
DEFAULT_ACP_AGENT_ALLOWLIST,
|
|
2392
2512
|
acpRuntimeSupportsSessionLoad,
|
|
2393
2513
|
acpSessionRuntimeStateFromCapabilities,
|
|
2394
2514
|
acpSessionRuntimeStateFromUpdate,
|
|
2395
|
-
|
|
2515
|
+
asRecord,
|
|
2396
2516
|
buildAcpBackendId,
|
|
2397
2517
|
buildStrategyTable,
|
|
2398
2518
|
defaultAcpAgentAllowlist,
|