@pwrdrvr/agent-acp 0.8.0 → 0.9.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 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, JsonRpcObserver, JsonRpcTransport } from '@pwrdrvr/agent-transport';
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
- type AcpStdioJsonRpcTransportOptions = {
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
- /** Override the underlying JSON-RPC transport (tests inject a fake). */
20
- transport?: JsonRpcTransport;
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 AcpStdioJsonRpcTransport implements AcpJsonRpcTransport {
23
- private readonly connection;
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: AcpStdioJsonRpcTransportOptions);
27
- connect(): Promise<void>;
28
- request(method: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
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;
@@ -757,4 +787,4 @@ declare class AcpRegistryService {
757
787
  declare function evaluateAcpDistributionPolicy(distribution: AcpRegistryDistribution, allowlist: AcpAllowlistDecision): AcpDistributionPolicy;
758
788
  declare function normalizeRegistry(raw: unknown): AcpRegistryAgent[];
759
789
 
760
- 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, AcpStdioJsonRpcTransport, type AcpStdioJsonRpcTransportOptions, 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 };
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,200 @@
1
- // src/acp-stdio-transport.ts
2
- import { noopLogger } from "@pwrdrvr/agent-core";
1
+ // src/acp-connection.ts
2
+ import { spawn } from "child_process";
3
+ import { Readable, Writable } from "stream";
3
4
  import {
4
- JsonRpcConnection,
5
- StdioJsonRpcTransport
6
- } from "@pwrdrvr/agent-transport";
7
- var DEFAULT_REQUEST_TIMEOUT_MS = 10 * 6e4;
8
- var AcpStdioJsonRpcTransport = class {
5
+ ClientSideConnection,
6
+ ndJsonStream
7
+ } from "@zed-industries/agent-client-protocol";
8
+ import { noopLogger } from "@pwrdrvr/agent-core";
9
+ function isPlainObject(value) {
10
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11
+ }
12
+ function sanitizeAcpNotificationLine(line) {
13
+ if (line.trim().length === 0) return line;
14
+ let message;
15
+ try {
16
+ message = JSON.parse(line);
17
+ } catch {
18
+ return line;
19
+ }
20
+ if (!isPlainObject(message) || message.method !== "session/update") return line;
21
+ const params = message.params;
22
+ if (!isPlainObject(params)) return line;
23
+ const update = params.update;
24
+ if (!isPlainObject(update)) return line;
25
+ let changed = false;
26
+ for (const key of ["rawInput", "rawOutput"]) {
27
+ if (!(key in update)) continue;
28
+ const value = update[key];
29
+ if (value === void 0 || isPlainObject(value)) continue;
30
+ if (value === null) {
31
+ delete update[key];
32
+ } else {
33
+ update[key] = { value };
34
+ }
35
+ changed = true;
36
+ }
37
+ return changed ? JSON.stringify(message) : line;
38
+ }
39
+ function sanitizeInboundStream(source) {
40
+ const decoder = new TextDecoder();
41
+ const encoder = new TextEncoder();
42
+ let buffer = "";
43
+ return source.pipeThrough(
44
+ new TransformStream({
45
+ transform(chunk, controller) {
46
+ buffer += decoder.decode(chunk, { stream: true });
47
+ let nl = buffer.indexOf("\n");
48
+ while (nl !== -1) {
49
+ const line = buffer.slice(0, nl);
50
+ buffer = buffer.slice(nl + 1);
51
+ controller.enqueue(encoder.encode(sanitizeAcpNotificationLine(line) + "\n"));
52
+ nl = buffer.indexOf("\n");
53
+ }
54
+ },
55
+ flush(controller) {
56
+ if (buffer.length > 0) {
57
+ controller.enqueue(encoder.encode(sanitizeAcpNotificationLine(buffer)));
58
+ buffer = "";
59
+ }
60
+ }
61
+ })
62
+ );
63
+ }
64
+ var AcpConnection = class {
65
+ constructor(options) {
66
+ this.options = options;
67
+ this.logger = options.logger ?? noopLogger;
68
+ }
69
+ options;
70
+ logger;
9
71
  connection;
72
+ dispose;
73
+ connecting;
10
74
  notificationListeners = /* @__PURE__ */ new Set();
11
75
  requestHandler;
12
- constructor(options) {
13
- const logger = options.logger ?? noopLogger;
14
- const transport = options.transport ?? new StdioJsonRpcTransport({
15
- command: options.command,
16
- args: options.args,
17
- ...options.env !== void 0 ? { env: options.env } : {},
18
- logger
19
- });
20
- this.connection = new JsonRpcConnection(
21
- transport,
22
- options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
23
- options.observer,
24
- { logger, logContext: { owner: "acp-stdio-transport" } }
25
- );
26
- this.connection.setNotificationHandler((method, params) => {
27
- const normalized = asRecord(params) ?? {};
28
- for (const listener of this.notificationListeners) {
29
- listener(method, normalized);
30
- }
31
- });
32
- this.connection.setRequestHandler(async (method, params, id) => {
33
- if (!this.requestHandler) {
34
- throw new Error(`ACP request handler unavailable for ${method}`);
76
+ /** The `Client` handler the library invokes for agent→client traffic. */
77
+ buildClient() {
78
+ const emit = (method, params) => {
79
+ for (const listener of this.notificationListeners) listener(method, params);
80
+ };
81
+ return {
82
+ requestPermission: async (params) => {
83
+ if (!this.requestHandler) {
84
+ throw new Error("ACP request handler unavailable for session/request_permission");
85
+ }
86
+ return await this.requestHandler(
87
+ "session/request_permission",
88
+ params
89
+ );
90
+ },
91
+ sessionUpdate: async (params) => {
92
+ emit("session/update", params);
93
+ },
94
+ extNotification: async (method, params) => {
95
+ emit(method, params);
96
+ },
97
+ extMethod: async (method, params) => {
98
+ if (!this.requestHandler) {
99
+ throw new Error(`ACP request handler unavailable for ${method}`);
100
+ }
101
+ return await this.requestHandler(method, params) ?? {};
35
102
  }
36
- return await this.requestHandler(method, asRecord(params) ?? {}, id);
37
- });
103
+ // No fs/terminal handlers: we don't advertise those client capabilities at
104
+ // `initialize`, so a conformant agent won't call them.
105
+ };
106
+ }
107
+ async ensureConnected() {
108
+ if (this.connection) return;
109
+ this.connecting ??= this.connect();
110
+ await this.connecting;
38
111
  }
39
112
  async connect() {
40
- await this.connection.connect();
113
+ const create = this.options.createConnection ?? this.defaultCreateConnection();
114
+ const handle = create(this.buildClient());
115
+ this.connection = handle.connection;
116
+ this.dispose = handle.dispose;
117
+ }
118
+ defaultCreateConnection() {
119
+ const { command, args, env } = this.options;
120
+ const logger = this.logger;
121
+ return (client) => {
122
+ const child = spawn(command, args, {
123
+ stdio: ["pipe", "pipe", "pipe"],
124
+ ...env !== void 0 ? { env } : {}
125
+ });
126
+ child.on("error", (error) => {
127
+ logger.error("acp agent process error", { command, message: error.message });
128
+ });
129
+ child.stderr?.on("data", (chunk) => {
130
+ const line = chunk.toString("utf8").trimEnd();
131
+ if (line.length > 0) logger.debug("acp agent stderr", { line });
132
+ });
133
+ const toAgentStdin = Writable.toWeb(child.stdin);
134
+ const fromAgentStdout = sanitizeInboundStream(
135
+ Readable.toWeb(child.stdout)
136
+ );
137
+ const stream = ndJsonStream(toAgentStdin, fromAgentStdout);
138
+ const connection = new ClientSideConnection(
139
+ (_agent) => client,
140
+ stream
141
+ );
142
+ return {
143
+ connection,
144
+ dispose: () => {
145
+ try {
146
+ child.stdin?.end();
147
+ } catch {
148
+ }
149
+ child.kill();
150
+ }
151
+ };
152
+ };
41
153
  }
42
- async request(method, params, timeoutMs) {
43
- await this.connection.connect();
44
- return await this.connection.request(method, params, timeoutMs);
154
+ async request(method, params = {}, _timeoutMs) {
155
+ await this.ensureConnected();
156
+ const conn = this.connection;
157
+ if (!conn) throw new Error("acp connection unavailable");
158
+ switch (method) {
159
+ case "initialize":
160
+ return await conn.initialize(params);
161
+ case "session/new":
162
+ return await conn.newSession(params);
163
+ case "session/load":
164
+ return await conn.loadSession(params);
165
+ case "session/prompt":
166
+ return await conn.prompt(params);
167
+ case "session/set_mode":
168
+ return await conn.setSessionMode(params);
169
+ case "session/set_model":
170
+ return await conn.setSessionModel(params);
171
+ case "session/cancel":
172
+ await conn.cancel(params);
173
+ return {};
174
+ case "authenticate":
175
+ return await conn.authenticate(params);
176
+ default:
177
+ return await conn.extMethod(method, params);
178
+ }
45
179
  }
46
- async notify(method, params) {
47
- await this.connection.connect();
48
- await this.connection.notify(method, params);
180
+ async notify(method, params = {}) {
181
+ await this.ensureConnected();
182
+ const conn = this.connection;
183
+ if (!conn) throw new Error("acp connection unavailable");
184
+ if (method === "session/cancel") {
185
+ await conn.cancel(params);
186
+ return;
187
+ }
188
+ await conn.extNotification(method, params);
49
189
  }
50
190
  async close() {
51
- await this.connection.close();
191
+ try {
192
+ this.dispose?.();
193
+ } finally {
194
+ this.connection = void 0;
195
+ this.dispose = void 0;
196
+ this.connecting = void 0;
197
+ }
52
198
  }
53
199
  onNotification(listener) {
54
200
  this.notificationListeners.add(listener);
@@ -65,9 +211,6 @@ var AcpStdioJsonRpcTransport = class {
65
211
  };
66
212
  }
67
213
  };
68
- function asRecord(value) {
69
- return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
70
- }
71
214
 
72
215
  // src/acp-client.ts
73
216
  import {
@@ -85,7 +228,7 @@ import {
85
228
  } from "@pwrdrvr/agent-core";
86
229
 
87
230
  // src/normalizer/content.ts
88
- function asRecord2(value) {
231
+ function asRecord(value) {
89
232
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
90
233
  }
91
234
  function readString(record, key) {
@@ -122,7 +265,7 @@ function readAcpContentText(value) {
122
265
  const parts = value.map((item) => readAcpContentText(item)).filter((item) => Boolean(item));
123
266
  return parts.length > 0 ? parts.join("\n") : void 0;
124
267
  }
125
- const content = asRecord2(value);
268
+ const content = asRecord(value);
126
269
  if (!content) {
127
270
  return void 0;
128
271
  }
@@ -143,7 +286,7 @@ function readFirstLocationPath(record) {
143
286
  return void 0;
144
287
  }
145
288
  for (const location of locations) {
146
- const path5 = readString(asRecord2(location), "path");
289
+ const path5 = readString(asRecord(location), "path");
147
290
  if (path5 && path5.trim()) {
148
291
  return path5;
149
292
  }
@@ -244,7 +387,7 @@ function toolCallFromUpdate(update, kind, sessionId) {
244
387
  }
245
388
  function readToolArgs(update) {
246
389
  const raw = update.rawInput ?? update.raw_input ?? update.input ?? update.arguments ?? update.args;
247
- return asRecord2(raw) ?? (raw === void 0 ? void 0 : raw);
390
+ return asRecord(raw) ?? (raw === void 0 ? void 0 : raw);
248
391
  }
249
392
  function readToolContentText(value) {
250
393
  return readContentText({ content: value }, "content");
@@ -484,7 +627,7 @@ function readPlanSteps(record) {
484
627
  if (typeof step === "string") {
485
628
  return [{ step, status: "pending" }];
486
629
  }
487
- const stepRecord = asRecord2(step);
630
+ const stepRecord = asRecord(step);
488
631
  if (!stepRecord) {
489
632
  return [];
490
633
  }
@@ -507,7 +650,7 @@ function readPromptText(content) {
507
650
 
508
651
  // src/normalizer/runtime-capabilities.ts
509
652
  function normalizeAcpRuntimeCapabilities(params) {
510
- const record = asRecord2(params.value);
653
+ const record = asRecord(params.value);
511
654
  if (!record) {
512
655
  return params.initialize;
513
656
  }
@@ -589,7 +732,7 @@ function acpSessionRuntimeStateFromUpdate(update, now) {
589
732
  return currentModeId ? { currentModeId, updatedAt: now } : void 0;
590
733
  }
591
734
  if (kind === "config_option_update") {
592
- const configOption = asRecord2(update.configOption ?? update.config_option) ?? update;
735
+ const configOption = asRecord(update.configOption ?? update.config_option) ?? update;
593
736
  const id = readString(configOption, "id") ?? readString(configOption, "configOptionId") ?? readString(configOption, "configId");
594
737
  const value = readString(configOption, "currentValue") ?? readString(configOption, "value");
595
738
  return id && value ? { configValues: { [id]: value }, updatedAt: now } : void 0;
@@ -619,7 +762,7 @@ function readConfigOptions(value) {
619
762
  return [];
620
763
  }
621
764
  return value.flatMap((item) => {
622
- const record = asRecord2(item);
765
+ const record = asRecord(item);
623
766
  const id = readString(record, "id") ?? readString(record, "configOptionId") ?? readString(record, "configId");
624
767
  if (!record || !id) {
625
768
  return [];
@@ -648,7 +791,7 @@ function readConfigOptionValues(value) {
648
791
  return [];
649
792
  }
650
793
  return value.flatMap((item) => {
651
- const record = asRecord2(item);
794
+ const record = asRecord(item);
652
795
  const optionValue = readString(record, "value") ?? readString(record, "id") ?? readString(record, "optionId");
653
796
  if (!record || !optionValue) {
654
797
  return [];
@@ -662,7 +805,7 @@ function readConfigOptionValues(value) {
662
805
  });
663
806
  }
664
807
  function readModes(value) {
665
- const record = asRecord2(value);
808
+ const record = asRecord(value);
666
809
  const modes = Array.isArray(record?.availableModes) ? record.availableModes.flatMap(readMode) : [];
667
810
  if (modes.length === 0) {
668
811
  return void 0;
@@ -673,7 +816,7 @@ function readModes(value) {
673
816
  return result;
674
817
  }
675
818
  function readMode(value) {
676
- const record = asRecord2(value);
819
+ const record = asRecord(value);
677
820
  const id = readString(record, "id") ?? readString(record, "modeId");
678
821
  if (!record || !id) {
679
822
  return [];
@@ -687,7 +830,7 @@ function readMode(value) {
687
830
  return [mode];
688
831
  }
689
832
  function readModels(value) {
690
- const record = asRecord2(value);
833
+ const record = asRecord(value);
691
834
  const models = Array.isArray(record?.availableModels) ? record.availableModels.flatMap(readModel) : [];
692
835
  if (models.length === 0) {
693
836
  return void 0;
@@ -698,7 +841,7 @@ function readModels(value) {
698
841
  return result;
699
842
  }
700
843
  function readModel(value) {
701
- const record = asRecord2(value);
844
+ const record = asRecord(value);
702
845
  const id = readString(record, "modelId") ?? readString(record, "id");
703
846
  if (!record || !id) {
704
847
  return [];
@@ -711,16 +854,16 @@ function readModel(value) {
711
854
  return [model];
712
855
  }
713
856
  function readAgentCapabilities(value, sessionCapabilitiesValue) {
714
- const record = asRecord2(value);
715
- const sessionCapabilities = asRecord2(sessionCapabilitiesValue);
716
- const sessionMeta = asRecord2(sessionCapabilities?._meta);
717
- const kimiMeta = asRecord2(sessionMeta?.kimi);
857
+ const record = asRecord(value);
858
+ const sessionCapabilities = asRecord(sessionCapabilitiesValue);
859
+ const sessionMeta = asRecord(sessionCapabilities?._meta);
860
+ const kimiMeta = asRecord(sessionMeta?.kimi);
718
861
  if (!record && !sessionCapabilities) {
719
862
  return void 0;
720
863
  }
721
- const loadSession = readBoolean(record, "loadSession") ?? readBoolean(record, "load_session") ?? readBoolean(asRecord2(record?.session), "load");
722
- const close = readBoolean(asRecord2(record?.session), "close");
723
- const cancel = readBoolean(asRecord2(record?.session), "cancel");
864
+ const loadSession = readBoolean(record, "loadSession") ?? readBoolean(record, "load_session") ?? readBoolean(asRecord(record?.session), "load");
865
+ const close = readBoolean(asRecord(record?.session), "close");
866
+ const cancel = readBoolean(asRecord(record?.session), "cancel");
724
867
  const sessionHistoryReplay = readBoolean(kimiMeta, "sessionHistoryReplay") ?? readBoolean(kimiMeta, "session_history_replay");
725
868
  const capabilities = {
726
869
  raw: record ?? sessionCapabilities
@@ -738,7 +881,7 @@ function readAgentCapabilities(value, sessionCapabilitiesValue) {
738
881
  return capabilities;
739
882
  }
740
883
  function readAgentInfo(value) {
741
- const record = asRecord2(value);
884
+ const record = asRecord(value);
742
885
  if (!record) {
743
886
  return void 0;
744
887
  }
@@ -960,7 +1103,7 @@ var AcpAgentClient = class {
960
1103
  cwd,
961
1104
  mcpServers
962
1105
  });
963
- const record = asRecord3(result);
1106
+ const record = asRecord2(result);
964
1107
  const protocolSessionId = readString2(record, "sessionId") ?? readString2(record, "session_id");
965
1108
  if (!protocolSessionId) {
966
1109
  throw new Error("ACP session/new did not return a session id");
@@ -1201,13 +1344,13 @@ var AcpAgentClient = class {
1201
1344
  if (method !== "session/update" && !vendorMethods.includes(method)) {
1202
1345
  return;
1203
1346
  }
1204
- const record = asRecord3(params);
1347
+ const record = asRecord2(params);
1205
1348
  if (!record) return;
1206
1349
  this.applySessionUpdate(record);
1207
1350
  }
1208
1351
  applySessionUpdate(params) {
1209
1352
  const protocolSessionId = readString2(params, "sessionId") ?? readString2(params, "session_id");
1210
- const update = asRecord3(params.update);
1353
+ const update = asRecord2(params.update);
1211
1354
  if (!protocolSessionId || !update) {
1212
1355
  return;
1213
1356
  }
@@ -1400,7 +1543,7 @@ var AcpAgentClient = class {
1400
1543
  }
1401
1544
  };
1402
1545
  function buildApprovalRequest(args) {
1403
- const toolCall = asRecord3(args.params.toolCall) ?? {};
1546
+ const toolCall = asRecord2(args.params.toolCall) ?? {};
1404
1547
  const title = typeof toolCall.title === "string" && toolCall.title.trim() ? toolCall.title.trim() : "ACP tool call";
1405
1548
  const toolCallId = readString2(toolCall, "toolCallId") ?? readString2(toolCall, "tool_call_id");
1406
1549
  const requestId = args.id == null ? toolCallId ?? `acp:${args.now()}` : String(args.id);
@@ -1436,7 +1579,7 @@ function readPermissionOptions(value) {
1436
1579
  return [];
1437
1580
  }
1438
1581
  return value.flatMap((option) => {
1439
- const record = asRecord3(option);
1582
+ const record = asRecord2(option);
1440
1583
  const optionId = readString2(record, "optionId");
1441
1584
  if (!record || !optionId) {
1442
1585
  return [];
@@ -1511,12 +1654,12 @@ function readString2(record, key) {
1511
1654
  const value = record?.[key];
1512
1655
  return typeof value === "string" ? value : void 0;
1513
1656
  }
1514
- function asRecord3(value) {
1657
+ function asRecord2(value) {
1515
1658
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1516
1659
  }
1517
1660
  function readAcpPromptUsage(result) {
1518
- const meta = asRecord3(asRecord3(result)?._meta);
1519
- const tokenCount = asRecord3(asRecord3(meta?.quota)?.token_count);
1661
+ const meta = asRecord2(asRecord2(result)?._meta);
1662
+ const tokenCount = asRecord2(asRecord2(meta?.quota)?.token_count);
1520
1663
  if (!tokenCount) return void 0;
1521
1664
  const num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
1522
1665
  const input = num(tokenCount.input_tokens);
@@ -2284,7 +2427,7 @@ function evaluateAcpDistributionPolicy(distribution, allowlist) {
2284
2427
  };
2285
2428
  }
2286
2429
  function normalizeRegistry(raw) {
2287
- const record = asRecord4(raw);
2430
+ const record = asRecord3(raw);
2288
2431
  const rawAgents = Array.isArray(record?.agents) ? record.agents : [];
2289
2432
  return rawAgents.flatMap((item) => {
2290
2433
  const agent = normalizeAgent(item);
@@ -2292,7 +2435,7 @@ function normalizeRegistry(raw) {
2292
2435
  });
2293
2436
  }
2294
2437
  function normalizeAgent(raw) {
2295
- const record = asRecord4(raw);
2438
+ const record = asRecord3(raw);
2296
2439
  if (!record || typeof record.id !== "string" || typeof record.name !== "string") {
2297
2440
  return void 0;
2298
2441
  }
@@ -2325,7 +2468,7 @@ function normalizeAgent(raw) {
2325
2468
  return agent;
2326
2469
  }
2327
2470
  function normalizeDistributions(raw) {
2328
- const record = asRecord4(raw);
2471
+ const record = asRecord3(raw);
2329
2472
  if (!record) {
2330
2473
  return [];
2331
2474
  }
@@ -2334,7 +2477,7 @@ function normalizeDistributions(raw) {
2334
2477
  if (npx) distributions.push(npx);
2335
2478
  const uvx = normalizePackageDistribution("uvx", record.uvx);
2336
2479
  if (uvx) distributions.push(uvx);
2337
- const binaryRecord = asRecord4(record.binary);
2480
+ const binaryRecord = asRecord3(record.binary);
2338
2481
  if (binaryRecord) {
2339
2482
  for (const [platform, value] of Object.entries(binaryRecord)) {
2340
2483
  const binary = normalizeBinaryDistribution(platform, value);
@@ -2344,7 +2487,7 @@ function normalizeDistributions(raw) {
2344
2487
  return distributions;
2345
2488
  }
2346
2489
  function normalizePackageDistribution(kind, raw) {
2347
- const record = asRecord4(raw);
2490
+ const record = asRecord3(raw);
2348
2491
  if (!record || typeof record.package !== "string") {
2349
2492
  return void 0;
2350
2493
  }
@@ -2356,7 +2499,7 @@ function normalizePackageDistribution(kind, raw) {
2356
2499
  };
2357
2500
  }
2358
2501
  function normalizeBinaryDistribution(platform, raw) {
2359
- const record = asRecord4(raw);
2502
+ const record = asRecord3(raw);
2360
2503
  if (!record || typeof record.archive !== "string" || typeof record.cmd !== "string") {
2361
2504
  return void 0;
2362
2505
  }
@@ -2378,7 +2521,7 @@ function normalizeAuth(raw) {
2378
2521
  if (!raw) {
2379
2522
  return { required: false, methods: [] };
2380
2523
  }
2381
- const record = asRecord4(raw);
2524
+ const record = asRecord3(raw);
2382
2525
  if (!record) {
2383
2526
  return { required: true, methods: ["unknown"], raw };
2384
2527
  }
@@ -2391,7 +2534,7 @@ function normalizeAuth(raw) {
2391
2534
  raw
2392
2535
  };
2393
2536
  }
2394
- function asRecord4(value) {
2537
+ function asRecord3(value) {
2395
2538
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
2396
2539
  }
2397
2540
  function stringValue(value) {
@@ -2401,7 +2544,7 @@ function stringArray(value) {
2401
2544
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
2402
2545
  }
2403
2546
  function stringRecord(value) {
2404
- const record = asRecord4(value);
2547
+ const record = asRecord3(value);
2405
2548
  if (!record) {
2406
2549
  return {};
2407
2550
  }
@@ -2416,17 +2559,17 @@ export {
2416
2559
  AcpAgentAllowlist,
2417
2560
  AcpAgentClient,
2418
2561
  AcpAgentClientPool,
2562
+ AcpConnection,
2419
2563
  AcpOneShotClient,
2420
2564
  AcpRegistryService,
2421
2565
  AcpSessionNormalizer,
2422
- AcpStdioJsonRpcTransport,
2423
2566
  BANNED_ACP_REGISTRY_IDS,
2424
2567
  BUILT_IN_ACP_STRATEGIES,
2425
2568
  DEFAULT_ACP_AGENT_ALLOWLIST,
2426
2569
  acpRuntimeSupportsSessionLoad,
2427
2570
  acpSessionRuntimeStateFromCapabilities,
2428
2571
  acpSessionRuntimeStateFromUpdate,
2429
- asRecord2 as asRecord,
2572
+ asRecord,
2430
2573
  buildAcpBackendId,
2431
2574
  buildStrategyTable,
2432
2575
  defaultAcpAgentAllowlist,