@mono-agent/agent-runtime 0.20.11 → 0.21.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.
Files changed (148) hide show
  1. package/ARCHITECTURE.md +50 -11
  2. package/MIGRATION.md +288 -26
  3. package/README.md +352 -477
  4. package/package.json +13 -44
  5. package/src/agent/tool-bloat.js +145 -9
  6. package/src/agent/tools/agent-tool.js +108 -9
  7. package/src/agent/tools/bash.js +11 -26
  8. package/src/agent/tools/codex-subscription-search.js +123 -29
  9. package/src/agent/tools/exec.js +10 -2
  10. package/src/agent/tools/index.js +7 -0
  11. package/src/agent/tools/monitor.js +149 -0
  12. package/src/agent/tools/pi-bridge.js +123 -19
  13. package/src/agent/tools/shared/bash-environment.js +31 -0
  14. package/src/agent/tools/shared/monitors.js +293 -0
  15. package/src/agent/tools/shared/path-resolver.js +25 -6
  16. package/src/agent/tools/shared/process-jobs.js +6 -1
  17. package/src/agent/tools/shared/process-runner.js +26 -6
  18. package/src/agent/tools/shared/tool-context.js +8 -0
  19. package/src/agent/tools/web-access-interstitial.js +70 -0
  20. package/src/agent/tools/web-browser-render.js +83 -58
  21. package/src/agent/tools/web-controller.js +112 -21
  22. package/src/agent/tools/web-document-extractor.js +379 -0
  23. package/src/agent/tools/web-fetch.js +271 -243
  24. package/src/agent/tools/web-request.js +65 -0
  25. package/src/agent/tools/web-search-output.js +165 -0
  26. package/src/agent/tools/web-search-state.js +75 -0
  27. package/src/agent/tools/web-search.js +532 -71
  28. package/src/ai/cost.js +13 -68
  29. package/src/ai/failure.js +3 -3
  30. package/src/ai/index.js +5 -17
  31. package/src/ai/observer.js +8 -0
  32. package/src/ai/pi-interop.js +221 -1
  33. package/src/ai/pi-oauth-compat.js +1 -1
  34. package/src/ai/provider-check.js +131 -0
  35. package/src/ai/providers/codex/app-server-client.js +592 -0
  36. package/src/ai/providers/pi-models.js +18 -10
  37. package/src/ai/providers/pi-native/compaction-driver.js +94 -42
  38. package/src/ai/providers/pi-native/compaction-summary.js +140 -0
  39. package/src/ai/providers/pi-native/harness-adapter.js +376 -0
  40. package/src/ai/providers/pi-native/prompt-cache-diagnostics.js +103 -0
  41. package/src/ai/providers/pi-native/provider-attribution.js +102 -0
  42. package/src/ai/providers/pi-native/result-builder.js +38 -14
  43. package/src/ai/providers/pi-native/session-lifecycle.js +253 -55
  44. package/src/ai/providers/pi-native/stream-subscriber.js +52 -6
  45. package/src/ai/providers/pi-native/terminal-recovery.js +40 -0
  46. package/src/ai/providers/pi-native/turn-runner.js +279 -28
  47. package/src/ai/providers/pi-native.js +206 -61
  48. package/src/ai/runtime/capabilities.js +11 -56
  49. package/src/ai/runtime/live-input-events.js +250 -54
  50. package/src/ai/runtime/model-refs.js +118 -153
  51. package/src/ai/runtime/registry.js +22 -56
  52. package/src/ai/runtime/router.js +76 -417
  53. package/src/ai/runtime/session-liveness.js +3 -4
  54. package/src/ai/runtime/sessions.js +4 -5
  55. package/src/ai/runtime/tool-policy.js +0 -2
  56. package/src/ai/tool-lifecycle.js +32 -18
  57. package/src/ai/types.js +37 -112
  58. package/src/index.js +0 -6
  59. package/src/runtime.js +29 -16
  60. package/types/agent/tool-bloat.d.ts +1 -1
  61. package/types/agent/tools/agent-tool.d.ts +4 -2
  62. package/types/agent/tools/bash.d.ts +5 -3
  63. package/types/agent/tools/codex-subscription-search.d.ts +7 -3
  64. package/types/agent/tools/exec.d.ts +5 -3
  65. package/types/agent/tools/index.d.ts +1 -0
  66. package/types/agent/tools/monitor.d.ts +47 -0
  67. package/types/agent/tools/pi-bridge.d.ts +7 -4
  68. package/types/agent/tools/shared/bash-environment.d.ts +4 -0
  69. package/types/agent/tools/shared/monitors.d.ts +98 -0
  70. package/types/agent/tools/shared/process-jobs.d.ts +5 -1
  71. package/types/agent/tools/shared/process-runner.d.ts +14 -4
  72. package/types/agent/tools/shared/tool-context.d.ts +2 -0
  73. package/types/agent/tools/web-access-interstitial.d.ts +23 -0
  74. package/types/agent/tools/web-browser-render.d.ts +4 -1
  75. package/types/agent/tools/web-controller.d.ts +4 -2
  76. package/types/agent/tools/web-document-extractor.d.ts +27 -0
  77. package/types/agent/tools/web-fetch.d.ts +19 -24
  78. package/types/agent/tools/web-request.d.ts +20 -0
  79. package/types/agent/tools/web-search-output.d.ts +31 -0
  80. package/types/agent/tools/web-search-state.d.ts +21 -0
  81. package/types/agent/tools/web-search.d.ts +10 -45
  82. package/types/ai/cost.d.ts +1 -2
  83. package/types/ai/index.d.ts +2 -4
  84. package/types/ai/observer.d.ts +6 -0
  85. package/types/ai/pi-interop.d.ts +81 -0
  86. package/types/ai/provider-check.d.ts +53 -0
  87. package/types/ai/providers/codex/app-server-client.d.ts +37 -0
  88. package/types/ai/providers/pi-native/compaction-driver.d.ts +2 -1
  89. package/types/ai/providers/pi-native/compaction-summary.d.ts +19 -0
  90. package/types/ai/providers/pi-native/harness-adapter.d.ts +58 -0
  91. package/types/ai/providers/pi-native/prompt-cache-diagnostics.d.ts +3 -0
  92. package/types/ai/providers/pi-native/provider-attribution.d.ts +26 -0
  93. package/types/ai/providers/pi-native/result-builder.d.ts +14 -4
  94. package/types/ai/providers/pi-native/session-lifecycle.d.ts +25 -6
  95. package/types/ai/providers/pi-native/stream-subscriber.d.ts +2 -2
  96. package/types/ai/providers/pi-native/terminal-recovery.d.ts +2 -0
  97. package/types/ai/providers/pi-native/turn-runner.d.ts +68 -10
  98. package/types/ai/providers/pi-native.d.ts +21 -4
  99. package/types/ai/runtime/capabilities.d.ts +21 -70
  100. package/types/ai/runtime/live-input-events.d.ts +32 -8
  101. package/types/ai/runtime/model-refs.d.ts +0 -24
  102. package/types/ai/runtime/router.d.ts +3 -10
  103. package/types/ai/runtime/tool-policy.d.ts +0 -2
  104. package/types/ai/tool-lifecycle.d.ts +4 -3
  105. package/types/ai/types.d.ts +162 -256
  106. package/types/index.d.ts +0 -1
  107. package/src/ai/providers/acp-client.js +0 -1149
  108. package/src/ai/providers/acp-privacy.js +0 -124
  109. package/src/ai/providers/acp-public.js +0 -21
  110. package/src/ai/providers/acp-session-tokens.js +0 -282
  111. package/src/ai/providers/acp-transport.js +0 -356
  112. package/src/ai/providers/acp.js +0 -543
  113. package/src/ai/providers/claude-cli.js +0 -883
  114. package/src/ai/providers/claude-sandbox.js +0 -71
  115. package/src/ai/providers/claude-sdk-discovery-worker.js +0 -53
  116. package/src/ai/providers/claude-sdk-discovery.js +0 -352
  117. package/src/ai/providers/claude-sdk.js +0 -1127
  118. package/src/ai/providers/claude-subagent-activity.js +0 -719
  119. package/src/ai/providers/claude-subagents.js +0 -88
  120. package/src/ai/providers/codex-app.js +0 -2946
  121. package/src/ai/providers/opencode-app.js +0 -1109
  122. package/src/ai/providers/opencode-discovery.js +0 -39
  123. package/src/ai/providers/opencode-server.js +0 -508
  124. package/src/ai/runtime/context-windows.js +0 -46
  125. package/src/ai/runtime/fast-mode.js +0 -8
  126. package/src/ai/streaming/codex-events.js +0 -146
  127. package/src/ai/streaming/opencode-events.js +0 -59
  128. package/types/ai/providers/acp-client.d.ts +0 -227
  129. package/types/ai/providers/acp-privacy.d.ts +0 -25
  130. package/types/ai/providers/acp-public.d.ts +0 -7
  131. package/types/ai/providers/acp-session-tokens.d.ts +0 -41
  132. package/types/ai/providers/acp-transport.d.ts +0 -45
  133. package/types/ai/providers/acp.d.ts +0 -93
  134. package/types/ai/providers/claude-cli.d.ts +0 -305
  135. package/types/ai/providers/claude-sandbox.d.ts +0 -79
  136. package/types/ai/providers/claude-sdk-discovery-worker.d.ts +0 -1
  137. package/types/ai/providers/claude-sdk-discovery.d.ts +0 -97
  138. package/types/ai/providers/claude-sdk.d.ts +0 -138
  139. package/types/ai/providers/claude-subagent-activity.d.ts +0 -53
  140. package/types/ai/providers/claude-subagents.d.ts +0 -18
  141. package/types/ai/providers/codex-app.d.ts +0 -151
  142. package/types/ai/providers/opencode-app.d.ts +0 -96
  143. package/types/ai/providers/opencode-discovery.d.ts +0 -4
  144. package/types/ai/providers/opencode-server.d.ts +0 -20
  145. package/types/ai/runtime/context-windows.d.ts +0 -9
  146. package/types/ai/runtime/fast-mode.d.ts +0 -2
  147. package/types/ai/streaming/codex-events.d.ts +0 -40
  148. package/types/ai/streaming/opencode-events.d.ts +0 -42
@@ -1,543 +0,0 @@
1
- // @ts-check
2
-
3
- import { isAbsolute } from "node:path";
4
- import { runtimeCapabilities } from "../runtime/capabilities.js";
5
- import { resolveSandboxPolicy } from "../../agent/tools/shared/tool-context.js";
6
- import {
7
- AcpClientError,
8
- connectAcpProfile,
9
- } from "./acp-client.js";
10
- import {
11
- decodeAcpProviderSessionId,
12
- encodeAcpProviderSessionId,
13
- validateAcpProviderSessionId,
14
- validateAcpSessionTokenKey,
15
- } from "./acp-session-tokens.js";
16
- import {
17
- ownAcpSessionUpdateKind,
18
- sanitizeAcpHostValueWithStatus,
19
- } from "./acp-privacy.js";
20
- import { toolLifecycleMetadata } from "../tool-lifecycle.js";
21
-
22
- /** @param {any} callback @param {any} event */
23
- function emit(callback, event) {
24
- try { callback?.(event); } catch { /* observers cannot break a provider turn */ }
25
- }
26
-
27
- /** @param {any} value */
28
- function jsonSafe(value) {
29
- try { return JSON.parse(JSON.stringify(value)); } catch { return null; }
30
- }
31
-
32
- /** @param {any} content @returns {string} */
33
- function textFromContent(content) {
34
- if (typeof content === "string") return content;
35
- if (!Array.isArray(content)) return content == null ? "" : String(content);
36
- return content
37
- .filter((block) => block?.type === "text" && typeof block.text === "string")
38
- .map((block) => block.text)
39
- .join("");
40
- }
41
-
42
- /** @param {any} block @param {any} promptCapabilities */
43
- function normalizePromptBlock(block, promptCapabilities) {
44
- if (!block || typeof block !== "object" || Array.isArray(block)) {
45
- throw new AcpClientError("invalid_request", "ACP prompt content blocks must be objects.");
46
- }
47
- if (block.type === "text" && typeof block.text === "string") {
48
- return { type: "text", text: block.text };
49
- }
50
- // Resource links are ACP baseline content and must survive normalization.
51
- if (block.type === "resource_link"
52
- && typeof block.uri === "string"
53
- && typeof block.name === "string") {
54
- return jsonSafe(block);
55
- }
56
- if (block.type === "image" && promptCapabilities?.image === true) return jsonSafe(block);
57
- if (block.type === "audio" && promptCapabilities?.audio === true) return jsonSafe(block);
58
- if (block.type === "resource" && promptCapabilities?.embeddedContext === true) return jsonSafe(block);
59
- throw new AcpClientError("capability_missing", `ACP agent cannot accept prompt content type '${block.type || "unknown"}'.`);
60
- }
61
-
62
- /**
63
- * Convert runtime history to one ACP user prompt. A fresh ACP session has no
64
- * protocol method for importing arbitrary prior assistant turns, so the
65
- * client-owned path sends them as a labelled transcript while preserving
66
- * baseline resource_link blocks as real blocks. A resumed session sends only
67
- * the latest user content because the agent already owns prior history.
68
- * @param {string} systemPrompt
69
- * @param {any[]} messages
70
- * @param {{includeHistory: boolean, includeSystem: boolean, promptCapabilities: any}} options
71
- */
72
- function runtimePrompt(systemPrompt, messages, options) {
73
- const source = Array.isArray(messages) ? messages : [];
74
- const selected = options.includeHistory
75
- ? source
76
- : (() => {
77
- for (let index = source.length - 1; index >= 0; index -= 1) {
78
- if (source[index]?.role === "user") return [source[index]];
79
- }
80
- return source.length > 0 ? [source[source.length - 1]] : [];
81
- })();
82
- /** @type {any[]} */
83
- const blocks = [];
84
- if (options.includeSystem && systemPrompt.trim()) {
85
- blocks.push({ type: "text", text: `[System]\n${systemPrompt}` });
86
- }
87
-
88
- selected.forEach((message, messageIndex) => {
89
- const role = typeof message?.role === "string" ? message.role : "user";
90
- const content = message?.content;
91
- if (Array.isArray(content)) {
92
- let labelled = false;
93
- for (const block of content) {
94
- const normalized = normalizePromptBlock(block, options.promptCapabilities);
95
- if (normalized.type === "text") {
96
- blocks.push({
97
- ...normalized,
98
- text: `${labelled ? "" : `[${role}]\n`}${normalized.text}`,
99
- });
100
- labelled = true;
101
- } else {
102
- blocks.push(normalized);
103
- }
104
- }
105
- if (content.length === 0) blocks.push({ type: "text", text: `[${role}]\n` });
106
- return;
107
- }
108
- const text = textFromContent(content);
109
- if (text || messageIndex === selected.length - 1) {
110
- blocks.push({ type: "text", text: `[${role}]\n${text}` });
111
- }
112
- });
113
- if (blocks.length === 0) blocks.push({ type: "text", text: "" });
114
- return blocks;
115
- }
116
-
117
- /** @param {unknown} value @param {string} key */
118
- function ownValue(value, key) {
119
- if (!value || typeof value !== "object" || Array.isArray(value) || !Object.hasOwn(value, key)) {
120
- return undefined;
121
- }
122
- return /** @type {Record<string, unknown>} */ (value)[key];
123
- }
124
-
125
- /** @param {any} update @param {any} state */
126
- function normalizeUpdate(update, state) {
127
- const protocolBody = update.update;
128
- const updateKind = ownAcpSessionUpdateKind(protocolBody);
129
- if (updateKind === null) {
130
- state.protocolError ||= new AcpClientError("protocol", "ACP session update has no valid own discriminator.");
131
- return [{ type: "acp_session_update_rejected", reason: "invalid_discriminator" }];
132
- }
133
- const sanitized = sanitizeAcpHostValueWithStatus(protocolBody, [update.sessionId]);
134
- if (sanitized.truncated) {
135
- state.protocolError ||= new AcpClientError(
136
- "protocol",
137
- "ACP session update exceeded safe host normalization limits.",
138
- );
139
- return [{ type: "acp_session_update_rejected", reason: "normalization_limit" }];
140
- }
141
- const publicBody = sanitized.value;
142
- /** @param {unknown} value */
143
- const publicValue = (value) => sanitizeAcpHostValueWithStatus(value, [update.sessionId]).value;
144
- const raw = {
145
- type: "acp_session_update",
146
- update: jsonSafe({ ...publicBody, sessionUpdate: updateKind }),
147
- };
148
- /** @type {any[]} */
149
- const events = [raw];
150
- switch (updateKind) {
151
- case "agent_message_chunk": {
152
- const content = publicValue(ownValue(protocolBody, "content"));
153
- if (content?.type === "text" && typeof content.text === "string") state.text.push(content.text);
154
- events.push({ type: "assistant", message: { content: [jsonSafe(content)] } });
155
- break;
156
- }
157
- case "agent_thought_chunk": {
158
- const content = publicValue(ownValue(protocolBody, "content"));
159
- if (content?.type === "text" && typeof content.text === "string") state.thinking.push(content.text);
160
- events.push({
161
- type: "assistant",
162
- message: { content: [{ type: "thinking", text: content?.type === "text" ? content.text : "" }] },
163
- });
164
- break;
165
- }
166
- case "tool_call":
167
- events.push({
168
- type: "assistant",
169
- message: {
170
- content: [{
171
- type: "tool_use",
172
- id: publicValue(ownValue(protocolBody, "toolCallId")),
173
- name: publicValue(ownValue(protocolBody, "name"))
174
- || publicValue(ownValue(protocolBody, "title"))
175
- || "acp_tool",
176
- input: jsonSafe(publicValue(ownValue(protocolBody, "rawInput") ?? {})),
177
- }],
178
- },
179
- });
180
- break;
181
- case "tool_call_update":
182
- if (["completed", "failed"].includes(/** @type {any} */ (ownValue(protocolBody, "status")))) {
183
- const rawOutput = ownValue(protocolBody, "rawOutput") ?? ownValue(protocolBody, "content") ?? "";
184
- const output = publicValue(rawOutput);
185
- events.push({
186
- type: "user",
187
- message: {
188
- content: [{
189
- type: "tool_result",
190
- tool_use_id: publicValue(ownValue(protocolBody, "toolCallId")),
191
- content: typeof output === "string" ? output : JSON.stringify(jsonSafe(output)),
192
- is_error: ownValue(protocolBody, "status") === "failed",
193
- tool_lifecycle: toolLifecycleMetadata(ownValue(protocolBody, "status") === "failed"
194
- ? { state: "error", failure_kind: "runtime_error", detail_code: "acp_tool_failed" }
195
- : { state: "success" }),
196
- }],
197
- },
198
- });
199
- }
200
- break;
201
- case "usage_update":
202
- state.usage = {
203
- totalTokens: Number(ownValue(protocolBody, "used")) || 0,
204
- contextWindow: Number(ownValue(protocolBody, "size")) || 0,
205
- cost: publicValue(ownValue(protocolBody, "cost")) || null,
206
- };
207
- events.push({
208
- type: "context_usage",
209
- model: state.model,
210
- source: "acp",
211
- context: {
212
- used: Number(ownValue(protocolBody, "used")) || 0,
213
- window: Number(ownValue(protocolBody, "size")) || 0,
214
- },
215
- cost: publicValue(ownValue(protocolBody, "cost")) || null,
216
- });
217
- break;
218
- case "plan":
219
- case "plan_update":
220
- case "plan_removed":
221
- events.push({
222
- type: "plan",
223
- source: "acp",
224
- update: jsonSafe({ ...publicBody, sessionUpdate: updateKind }),
225
- });
226
- break;
227
- default:
228
- break;
229
- }
230
- return events;
231
- }
232
-
233
- /** @param {any} descriptor @param {any} req */
234
- function workspaceFor(descriptor, req) {
235
- if (descriptor.workspaceOwner === "agent") return descriptor.workspacePath;
236
- const cwd = req.cwd || descriptor.workspacePath || descriptor.cwd || process.cwd();
237
- if (typeof cwd !== "string" || !isAbsolute(cwd)) {
238
- throw new AcpClientError("invalid_request", "ACP runtime cwd must be absolute.");
239
- }
240
- return cwd;
241
- }
242
-
243
- /** @param {any} descriptor */
244
- function sessionConfig(descriptor) {
245
- const config = descriptor.sessionConfig || {};
246
- return {
247
- additionalDirectories: [...(config.additionalDirectories || [])],
248
- mcpServers: descriptor.mcpOwner === "agent" ? [] : [...(config.mcpServers || [])],
249
- };
250
- }
251
-
252
- /** @param {any} connection @param {string} sessionId @param {any} setupResponse @param {any} descriptor */
253
- async function applyClientConfiguration(connection, sessionId, setupResponse, descriptor) {
254
- if (descriptor.configurationOwner === "agent") return { modeApplied: false, configOptionsApplied: [] };
255
- const config = descriptor.sessionConfig || {};
256
- let modes = setupResponse?.modes || null;
257
- let configOptions = Array.isArray(setupResponse?.configOptions) ? setupResponse.configOptions : [];
258
- let modeApplied = false;
259
- const applied = [];
260
- if (config.modeId !== undefined) {
261
- const available = modes?.availableModes?.some((mode) => mode?.id === config.modeId) === true;
262
- if (!available) throw new AcpClientError("capability_missing", "Configured ACP session mode was not advertised.");
263
- await connection.setSessionMode(sessionId, config.modeId);
264
- modeApplied = true;
265
- }
266
- for (const [configId, value] of Object.entries(config.configOptions || {})) {
267
- const option = configOptions.find((candidate) => candidate?.id === configId);
268
- if (!option) throw new AcpClientError("capability_missing", "Configured ACP session option was not advertised.");
269
- if (option.type === "boolean") {
270
- if (typeof value !== "boolean") {
271
- throw new AcpClientError("invalid_profile", "ACP boolean config option requires a boolean value.");
272
- }
273
- } else {
274
- if (typeof value !== "string" || !selectValues(option.options).includes(value)) {
275
- throw new AcpClientError("invalid_profile", "ACP select config option value was not advertised.");
276
- }
277
- }
278
- const response = await connection.setSessionConfigOption(sessionId, configId, value);
279
- configOptions = Array.isArray(response?.configOptions) ? response.configOptions : configOptions;
280
- applied.push(configId);
281
- }
282
- return { modeApplied, configOptionsApplied: applied };
283
- }
284
-
285
- /** @param {any[]} options */
286
- function selectValues(options) {
287
- if (!Array.isArray(options)) return [];
288
- const values = [];
289
- for (const item of options) {
290
- if (typeof item?.value === "string") values.push(item.value);
291
- if (Array.isArray(item?.options)) values.push(...selectValues(item.options));
292
- }
293
- return values;
294
- }
295
-
296
- /** @param {any} connection @param {any} descriptor @param {any} req @param {string} profileId @param {Uint8Array} key @param {(notification:any)=>void} onUpdate */
297
- async function openSession(connection, descriptor, req, profileId, key, onUpdate) {
298
- const cwd = workspaceFor(descriptor, req);
299
- const config = sessionConfig(descriptor);
300
- const baseRequest = { cwd, ...config };
301
- if (!req.providerSessionId) {
302
- const response = await connection.newSession(baseRequest);
303
- return { sessionId: response.sessionId, response, resumed: false, resumeMethod: null };
304
- }
305
- const decoded = decodeAcpProviderSessionId(req.providerSessionId, key);
306
- if (decoded.profileId !== profileId) {
307
- throw new AcpClientError("invalid_session_id", "ACP provider session belongs to a different profile.");
308
- }
309
- const remove = connection.onSessionUpdate(decoded.sessionId, onUpdate);
310
- try {
311
- const strategy = descriptor.sessionConfig?.resumeStrategy || "auto";
312
- if (strategy === "resume" || (strategy === "auto" && connection.hasCapability("resume"))) {
313
- if (!connection.hasCapability("resume")) {
314
- throw new AcpClientError("capability_missing", "ACP agent did not advertise session/resume.");
315
- }
316
- const response = await connection.resumeSession({ sessionId: decoded.sessionId, ...baseRequest });
317
- return { sessionId: decoded.sessionId, response, resumed: true, resumeMethod: "resume" };
318
- }
319
- if (strategy === "load" || (strategy === "auto" && connection.hasCapability("load"))) {
320
- if (!connection.hasCapability("load")) {
321
- throw new AcpClientError("capability_missing", "ACP agent did not advertise session/load.");
322
- }
323
- const response = await connection.loadSession({ sessionId: decoded.sessionId, ...baseRequest });
324
- return { sessionId: decoded.sessionId, response, resumed: true, resumeMethod: "load" };
325
- }
326
- if (strategy === "auto") {
327
- const response = await connection.newSession(baseRequest);
328
- return {
329
- sessionId: response.sessionId,
330
- response,
331
- resumed: false,
332
- resumeMethod: "new_fallback",
333
- };
334
- }
335
- throw new AcpClientError("invalid_profile", "Invalid ACP resume strategy.");
336
- } finally {
337
- remove();
338
- }
339
- }
340
-
341
- /** @param {unknown} error @param {boolean} aborted */
342
- function failureFor(error, aborted) {
343
- const coded = /** @type {any} */ (error);
344
- if (aborted || coded?.code === "cancelled") return { cancelled: true, failureKind: null, message: null };
345
- if (coded?.code === "timeout") return { cancelled: false, failureKind: "timeout", message: coded.message };
346
- if (coded?.code === "spawn") return { cancelled: false, failureKind: "spawn", message: coded.message };
347
- return {
348
- cancelled: false,
349
- failureKind: "provider_protocol",
350
- message: error instanceof Error ? error.message : "ACP provider protocol failed.",
351
- };
352
- }
353
-
354
- /**
355
- * Runtime bridge for one ACP v1 profile turn.
356
- * @param {string} systemPrompt
357
- * @param {any} req
358
- */
359
- export async function generateAcpResponse(systemPrompt, req) {
360
- const start = Date.now();
361
- const profileId = req?.model?.model;
362
- const reference = req?.model?.reference || `acp:${profileId || "unknown"}`;
363
- const events = [];
364
- const state = { text: [], thinking: [], model: reference, usage: null, protocolError: null };
365
- const onEvent = req?.onEvent;
366
- const capture = (event) => {
367
- events.push(event);
368
- emit(onEvent, event);
369
- };
370
- capture({ type: "provider_request_started", sdk: "acp", model: reference, runtime: "acp-stdio", timestamp: start });
371
- let connection;
372
- let sessionId = null;
373
- let providerSessionId = null;
374
- let setup = null;
375
- let configuration = { modeApplied: false, configOptionsApplied: [] };
376
- try {
377
- const sessionTokenKey = validateAcpSessionTokenKey(req?.acpSessionTokenKey);
378
- if (req?.providerSessionId != null) {
379
- providerSessionId = validateAcpProviderSessionId(
380
- req.providerSessionId,
381
- profileId,
382
- sessionTokenKey,
383
- );
384
- }
385
- connection = await connectAcpProfile(profileId, {
386
- resolveAcpProfile: req.resolveAcpProfile,
387
- onAcpInteractionRequest: req.onAcpInteractionRequest,
388
- sandbox: req.toolContext?.sandbox || req.sandbox,
389
- sandboxPolicy: resolveSandboxPolicy(req.toolContext, req.sandboxPolicy),
390
- sandboxEngine: req.sandboxEngine || req.toolContext?.sandboxEngine,
391
- cwd: req.cwd,
392
- signal: req.abortSignal,
393
- context: { operation: "run", model: reference },
394
- operation: "run",
395
- acpSessionTokenKey: sessionTokenKey,
396
- });
397
- capture({
398
- type: "capabilities_resolved",
399
- sdk: "acp",
400
- model: reference,
401
- capabilitiesUsed: {
402
- protocol_version: connection.initializeResult.protocolVersion,
403
- agent_capabilities: jsonSafe(connection.initializeResult.agentCapabilities || {}),
404
- },
405
- });
406
- const onUpdate = (notification) => {
407
- for (const event of normalizeUpdate(notification, state)) capture(event);
408
- };
409
- setup = await openSession(
410
- connection,
411
- connection.descriptor,
412
- req,
413
- profileId,
414
- sessionTokenKey,
415
- onUpdate,
416
- );
417
- sessionId = setup.sessionId;
418
- providerSessionId = setup.resumed && providerSessionId
419
- ? providerSessionId
420
- : encodeAcpProviderSessionId(profileId, sessionId, sessionTokenKey);
421
- configuration = await applyClientConfiguration(
422
- connection,
423
- sessionId,
424
- setup.response,
425
- connection.descriptor,
426
- );
427
- const remove = connection.onSessionUpdate(sessionId, onUpdate);
428
- let promptResponse;
429
- try {
430
- promptResponse = await connection.prompt(
431
- sessionId,
432
- runtimePrompt(systemPrompt, req.messages || [], {
433
- includeHistory: !setup.resumed,
434
- includeSystem: !setup.resumed && connection.descriptor.configurationOwner === "client",
435
- promptCapabilities: connection.initializeResult.agentCapabilities?.promptCapabilities || {},
436
- }),
437
- { signal: req.abortSignal },
438
- );
439
- } finally {
440
- remove();
441
- }
442
- if (state.protocolError) throw state.protocolError;
443
- // PromptResponse.usage is explicitly unstable in ACP 1.3.0. The stable
444
- // cumulative source is the latest top-level usage_update notification.
445
- const usage = state.usage;
446
- capture({
447
- type: "provider_request_completed",
448
- sdk: "acp",
449
- model: reference,
450
- runtime: "acp-stdio",
451
- timestamp: Date.now(),
452
- durationMs: Date.now() - start,
453
- cancelled: promptResponse.stopReason === "cancelled",
454
- });
455
- const limited = promptResponse.stopReason === "max_tokens" || promptResponse.stopReason === "max_turn_requests";
456
- return {
457
- text: state.text.join("") || null,
458
- thinking: state.thinking.join(""),
459
- events,
460
- usage: usage ? {
461
- total_tokens: usage.totalTokens || null,
462
- context_window: usage.contextWindow || null,
463
- cost: usage.cost,
464
- } : {},
465
- durationMs: Date.now() - start,
466
- numTurns: 1,
467
- model: reference,
468
- effort: req.effort || null,
469
- sdk: "acp",
470
- cancelled: promptResponse.stopReason === "cancelled",
471
- error: limited ? `ACP agent stopped with ${promptResponse.stopReason}.` : null,
472
- failureKind: limited ? "usage_limit" : null,
473
- providerSessionId,
474
- runtimeWarnings: [],
475
- diagnostics: {
476
- acp_protocol_version: connection.initializeResult.protocolVersion,
477
- acp_profile_id: profileId,
478
- acp_session_id_encoded: true,
479
- acp_stop_reason: promptResponse.stopReason,
480
- acp_resume_method: setup.resumeMethod,
481
- acp_mode_applied: configuration.modeApplied,
482
- acp_config_options_applied: configuration.configOptionsApplied,
483
- },
484
- capabilitiesUsed: {
485
- session_resume: setup.resumed,
486
- session_load: setup.resumeMethod === "load",
487
- session_config: configuration.modeApplied || configuration.configOptionsApplied.length > 0,
488
- mcp: connection.descriptor.mcpOwner === "client"
489
- && (connection.descriptor.sessionConfig?.mcpServers || []).length > 0,
490
- },
491
- structuredResult: undefined,
492
- structuredResultSource: null,
493
- };
494
- } catch (error) {
495
- const failure = failureFor(error, req?.abortSignal?.aborted === true);
496
- capture({
497
- type: "provider_request_completed",
498
- sdk: "acp",
499
- model: reference,
500
- runtime: "acp-stdio",
501
- timestamp: Date.now(),
502
- durationMs: Date.now() - start,
503
- cancelled: failure.cancelled,
504
- });
505
- return {
506
- text: state.text.join("") || null,
507
- thinking: state.thinking.join(""),
508
- events,
509
- usage: {},
510
- durationMs: Date.now() - start,
511
- numTurns: 0,
512
- model: reference,
513
- effort: req?.effort || null,
514
- sdk: "acp",
515
- cancelled: failure.cancelled,
516
- error: failure.message,
517
- errorDetails: failure.message ? {
518
- acp_error_code: typeof error?.code === "string" ? error.code : "protocol",
519
- } : null,
520
- failureKind: failure.failureKind,
521
- providerSessionId,
522
- runtimeWarnings: [],
523
- diagnostics: {
524
- acp_profile_id: typeof profileId === "string" ? profileId : null,
525
- acp_session_id_encoded: providerSessionId != null,
526
- acp_stop_reason: failure.cancelled ? "cancelled" : "error",
527
- },
528
- capabilitiesUsed: {},
529
- structuredResult: undefined,
530
- structuredResultSource: null,
531
- };
532
- } finally {
533
- await connection?.close().catch(() => {});
534
- }
535
- }
536
-
537
- export const acpRuntimeBridge = {
538
- id: "acp-stdio",
539
- kind: "acp",
540
- capabilities: runtimeCapabilities("acp"),
541
- supports: (ref, options) => ref?.sdk === "acp" && options?.executionMode === "acp",
542
- execute: generateAcpResponse,
543
- };