@moxt-ai/mobius 0.0.10 → 0.0.11

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 (112) hide show
  1. package/dist/acp/session-history.d.ts.map +1 -1
  2. package/dist/acp/session-history.js +1 -0
  3. package/dist/acp/session-history.js.map +1 -1
  4. package/dist/acp/session-manager.d.ts +4 -1
  5. package/dist/acp/session-manager.d.ts.map +1 -1
  6. package/dist/acp/session-manager.js +99 -24
  7. package/dist/acp/session-manager.js.map +1 -1
  8. package/dist/agent-catalog/acp-readiness-probe.d.ts +4 -0
  9. package/dist/agent-catalog/acp-readiness-probe.d.ts.map +1 -0
  10. package/dist/agent-catalog/acp-readiness-probe.js +153 -0
  11. package/dist/agent-catalog/acp-readiness-probe.js.map +1 -0
  12. package/dist/agent-catalog/agent-diagnostics.d.ts +1 -1
  13. package/dist/agent-catalog/agent-diagnostics.d.ts.map +1 -1
  14. package/dist/agent-catalog/agent-diagnostics.js +1 -1
  15. package/dist/agent-catalog/agent-diagnostics.js.map +1 -1
  16. package/dist/agent-catalog/agent-discovery-context.d.ts +8 -0
  17. package/dist/agent-catalog/agent-discovery-context.d.ts.map +1 -0
  18. package/dist/agent-catalog/agent-discovery-context.js +2 -0
  19. package/dist/agent-catalog/agent-discovery-context.js.map +1 -0
  20. package/dist/agent-catalog/agent-discovery.d.ts +55 -0
  21. package/dist/agent-catalog/agent-discovery.d.ts.map +1 -0
  22. package/dist/agent-catalog/agent-discovery.js +89 -0
  23. package/dist/agent-catalog/agent-discovery.js.map +1 -0
  24. package/dist/agent-catalog/agent-probe-error.d.ts +5 -0
  25. package/dist/agent-catalog/agent-probe-error.d.ts.map +1 -0
  26. package/dist/agent-catalog/agent-probe-error.js +9 -0
  27. package/dist/agent-catalog/agent-probe-error.js.map +1 -0
  28. package/dist/agent-catalog/agent-specification.d.ts +45 -0
  29. package/dist/agent-catalog/agent-specification.d.ts.map +1 -0
  30. package/dist/agent-catalog/agent-specification.js +59 -0
  31. package/dist/agent-catalog/agent-specification.js.map +1 -0
  32. package/dist/agent-catalog/agents/claude-code-agent.d.ts +14 -0
  33. package/dist/agent-catalog/agents/claude-code-agent.d.ts.map +1 -0
  34. package/dist/agent-catalog/agents/claude-code-agent.js +30 -0
  35. package/dist/agent-catalog/agents/claude-code-agent.js.map +1 -0
  36. package/dist/agent-catalog/agents/codex-agent.d.ts +14 -0
  37. package/dist/agent-catalog/agents/codex-agent.d.ts.map +1 -0
  38. package/dist/agent-catalog/agents/codex-agent.js +23 -0
  39. package/dist/agent-catalog/agents/codex-agent.js.map +1 -0
  40. package/dist/agent-catalog/agents/deepseek-harness-agent.d.ts +16 -0
  41. package/dist/agent-catalog/agents/deepseek-harness-agent.d.ts.map +1 -0
  42. package/dist/agent-catalog/agents/deepseek-harness-agent.js +94 -0
  43. package/dist/agent-catalog/agents/deepseek-harness-agent.js.map +1 -0
  44. package/dist/agent-catalog/agents/opencode-agent.d.ts +17 -0
  45. package/dist/agent-catalog/agents/opencode-agent.d.ts.map +1 -0
  46. package/dist/agent-catalog/agents/opencode-agent.js +36 -0
  47. package/dist/agent-catalog/agents/opencode-agent.js.map +1 -0
  48. package/dist/agent-catalog/agents/pi-agent.d.ts +21 -0
  49. package/dist/agent-catalog/agents/pi-agent.d.ts.map +1 -0
  50. package/dist/agent-catalog/agents/pi-agent.js +69 -0
  51. package/dist/agent-catalog/agents/pi-agent.js.map +1 -0
  52. package/dist/agent-catalog/agents/supported-agent-specifications.d.ts +3 -0
  53. package/dist/agent-catalog/agents/supported-agent-specifications.d.ts.map +1 -0
  54. package/dist/agent-catalog/agents/supported-agent-specifications.js +15 -0
  55. package/dist/agent-catalog/agents/supported-agent-specifications.js.map +1 -0
  56. package/dist/agent-catalog/discover-agent.d.ts +5 -0
  57. package/dist/agent-catalog/discover-agent.d.ts.map +1 -0
  58. package/dist/agent-catalog/discover-agent.js +100 -0
  59. package/dist/agent-catalog/discover-agent.js.map +1 -0
  60. package/dist/agent-catalog/discover-local-agents.d.ts +12 -101
  61. package/dist/agent-catalog/discover-local-agents.d.ts.map +1 -1
  62. package/dist/agent-catalog/discover-local-agents.js +11 -564
  63. package/dist/agent-catalog/discover-local-agents.js.map +1 -1
  64. package/dist/agent-catalog/executable-discovery.d.ts +16 -0
  65. package/dist/agent-catalog/executable-discovery.d.ts.map +1 -0
  66. package/dist/agent-catalog/executable-discovery.js +105 -0
  67. package/dist/agent-catalog/executable-discovery.js.map +1 -0
  68. package/dist/agent-installation/agent-installation-manager.d.ts +1 -1
  69. package/dist/agent-installation/agent-installation-manager.d.ts.map +1 -1
  70. package/dist/agent-installation/automatic-adapter-provisioner.d.ts +2 -1
  71. package/dist/agent-installation/automatic-adapter-provisioner.d.ts.map +1 -1
  72. package/dist/agent-installation/automatic-adapter-provisioner.js +11 -2
  73. package/dist/agent-installation/automatic-adapter-provisioner.js.map +1 -1
  74. package/dist/agent-installation/deepseek-managed-package.d.ts +9 -0
  75. package/dist/agent-installation/deepseek-managed-package.d.ts.map +1 -0
  76. package/dist/agent-installation/deepseek-managed-package.js +10 -0
  77. package/dist/agent-installation/deepseek-managed-package.js.map +1 -0
  78. package/dist/agent-installation/fixed-agent-install-recipes.d.ts +18 -0
  79. package/dist/agent-installation/fixed-agent-install-recipes.d.ts.map +1 -1
  80. package/dist/agent-installation/fixed-agent-install-recipes.js +97 -0
  81. package/dist/agent-installation/fixed-agent-install-recipes.js.map +1 -1
  82. package/dist/agent-installation/fixed-installation-executor.d.ts +1 -1
  83. package/dist/agent-installation/fixed-installation-executor.d.ts.map +1 -1
  84. package/dist/agent-installation/fixed-installation-executor.js +17 -3
  85. package/dist/agent-installation/fixed-installation-executor.js.map +1 -1
  86. package/dist/agent-installation/managed-adapter-installer.d.ts +4 -4
  87. package/dist/agent-installation/managed-adapter-installer.d.ts.map +1 -1
  88. package/dist/agent-installation/managed-adapter-installer.js.map +1 -1
  89. package/dist/agent-installation/managed-agent-archive.d.ts +40 -0
  90. package/dist/agent-installation/managed-agent-archive.d.ts.map +1 -0
  91. package/dist/agent-installation/managed-agent-archive.js +274 -0
  92. package/dist/agent-installation/managed-agent-archive.js.map +1 -0
  93. package/dist/agent-installation/managed-npm-package.d.ts +3 -0
  94. package/dist/agent-installation/managed-npm-package.d.ts.map +1 -1
  95. package/dist/agent-installation/managed-npm-package.js +55 -24
  96. package/dist/agent-installation/managed-npm-package.js.map +1 -1
  97. package/dist/agent-installation/opencode-managed-release.d.ts +4 -0
  98. package/dist/agent-installation/opencode-managed-release.d.ts.map +1 -0
  99. package/dist/agent-installation/opencode-managed-release.js +25 -0
  100. package/dist/agent-installation/opencode-managed-release.js.map +1 -0
  101. package/dist/cli.js +2316 -1298
  102. package/dist/cli.js.map +4 -4
  103. package/dist/index.js +1763 -743
  104. package/dist/index.js.map +4 -4
  105. package/dist/native-sessions/codex-history.d.ts.map +1 -1
  106. package/dist/native-sessions/codex-history.js +1 -0
  107. package/dist/native-sessions/codex-history.js.map +1 -1
  108. package/dist/process/executable.d.ts +6 -1
  109. package/dist/process/executable.d.ts.map +1 -1
  110. package/dist/process/executable.js +150 -7
  111. package/dist/process/executable.js.map +1 -1
  112. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -6,100 +6,24 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/cli.ts
9
- import { mkdir as mkdir4 } from "node:fs/promises";
9
+ import { mkdir as mkdir5 } from "node:fs/promises";
10
10
 
11
- // src/agent-catalog/discover-local-agents.ts
12
- import { spawn as spawn2 } from "node:child_process";
13
- import { isAbsolute as isAbsolute2, join as join6 } from "node:path";
14
- import {
15
- methods as methods2,
16
- ndJsonStream,
17
- PROTOCOL_VERSION,
18
- RequestError
19
- } from "@agentclientprotocol/sdk";
20
-
21
- // src/acp/create-client.ts
22
- import {
23
- client,
24
- methods
25
- } from "@agentclientprotocol/sdk";
26
- var createAcpClient = (handler) => client({ name: "mobius" }).onRequest(
27
- methods.client.session.requestPermission,
28
- async (context) => await handler.onPermission(context.params, context.signal)
29
- ).onRequest(
30
- methods.client.elicitation.create,
31
- async (context) => await handler.onElicitation(context.params, context.signal)
32
- ).onNotification(methods.client.session.update, async (context) => {
33
- await handler.onUpdate(context.params);
34
- }).onNotification(methods.client.elicitation.complete, async (context) => {
35
- await handler.onElicitationComplete(context.params);
36
- });
37
-
38
- // src/agent-installation/installation-grants.ts
39
- import { open } from "node:fs/promises";
40
- import { join } from "node:path";
41
- var MAX_GRANT_FILE_LENGTH = 16 * 1024;
42
- var AGENT_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
43
- function isRecord(value) {
44
- return typeof value === "object" && value !== null && !Array.isArray(value);
45
- }
46
- async function readGrantedAgents(stateDirectory) {
47
- const path2 = join(stateDirectory, "installation-grants.json");
48
- let handle;
49
- try {
50
- handle = await open(path2, "r");
51
- } catch (error61) {
52
- if (error61 instanceof Error && "code" in error61 && error61.code === "ENOENT") {
53
- return /* @__PURE__ */ new Set();
54
- }
55
- throw error61;
56
- }
57
- try {
58
- const metadata = await handle.stat();
59
- if (!metadata.isFile() || metadata.size > MAX_GRANT_FILE_LENGTH || typeof process.getuid === "function" && metadata.uid !== process.getuid() || process.platform !== "win32" && (metadata.mode & 63) !== 0) {
60
- throw new Error("The local installation grant file is not private");
61
- }
62
- const encoded = await handle.readFile({ encoding: "utf8" });
63
- const value = JSON.parse(encoded);
64
- if (!isRecord(value) || Object.keys(value).length !== 1 || !Array.isArray(value["agents"])) {
65
- throw new Error("The local installation grant file is invalid");
66
- }
67
- const agents = /* @__PURE__ */ new Set();
68
- for (const candidate of value["agents"]) {
69
- if (typeof candidate !== "string" || !AGENT_ID_PATTERN.test(candidate) || agents.has(candidate)) {
70
- throw new Error("The local installation grant file is invalid");
71
- }
72
- agents.add(candidate);
73
- }
74
- return agents;
75
- } finally {
76
- await handle.close();
77
- }
78
- }
79
-
80
- // src/agent-installation/managed-adapter-installer.ts
81
- import { join as join4 } from "node:path";
82
-
83
- // src/agent-installation/managed-npm-package.ts
84
- import {
85
- mkdir,
86
- mkdtemp,
87
- open as open2,
88
- realpath as realpath2,
89
- rename,
90
- rm,
91
- stat as stat2
92
- } from "node:fs/promises";
93
- import { delimiter, dirname, isAbsolute, join as join3, relative, sep } from "node:path";
11
+ // src/agent-catalog/local-agent.ts
12
+ import { realpathSync, statSync } from "node:fs";
94
13
 
95
14
  // src/process/executable.ts
96
- import { spawn } from "node:child_process";
15
+ import { execFile, spawn } from "node:child_process";
97
16
  import { constants } from "node:fs";
98
17
  import { access, realpath, stat } from "node:fs/promises";
99
- import { join as join2, resolve } from "node:path";
18
+ import { join, resolve } from "node:path";
100
19
  var DEFAULT_OUTPUT_LIMIT_BYTES = 16 * 1024;
101
20
  var PROCESS_GRACEFUL_STOP_TIMEOUT_MILLISECONDS = 1e3;
102
21
  var PROCESS_FORCED_STOP_TIMEOUT_MILLISECONDS = 5e3;
22
+ var PROCESS_TABLE_TIMEOUT_MILLISECONDS = 1e3;
23
+ var PROCESS_TABLE_OUTPUT_LIMIT_BYTES = 1024 * 1024;
24
+ var PROCESS_EXIT_CHECK_ATTEMPTS = 8;
25
+ var GRACEFUL_STOP_SIGNAL = 15;
26
+ var FORCED_STOP_SIGNAL = 9;
103
27
  var ExecutableIdentity = class {
104
28
  changedAtMilliseconds;
105
29
  device;
@@ -193,7 +117,7 @@ async function findExecutableOnPath(executableName, environment, platform = proc
193
117
  }
194
118
  for (const extension of extensions) {
195
119
  const requestedPath = resolve(
196
- join2(entry, `${executableName}${extension}`)
120
+ join(entry, `${executableName}${extension}`)
197
121
  );
198
122
  const executable = await resolveExecutablePath(requestedPath, platform);
199
123
  if (executable !== null) {
@@ -218,6 +142,119 @@ function appendBounded(chunks, chunk, currentBytes, limitBytes) {
218
142
  chunks.push(chunk);
219
143
  return nextBytes;
220
144
  }
145
+ function parseProcessTable(output2) {
146
+ const entries = [];
147
+ for (const line of output2.split("\n")) {
148
+ const groups = /^\s*(?<pid>\d+)\s+(?<ppid>\d+)\s+(?<pgid>\d+)\s*$/.exec(
149
+ line
150
+ )?.groups;
151
+ if (groups === void 0) {
152
+ continue;
153
+ }
154
+ const processId = Number(groups["pid"]);
155
+ const parentProcessId = Number(groups["ppid"]);
156
+ const processGroupId = Number(groups["pgid"]);
157
+ if (!Number.isSafeInteger(processId) || processId <= 0 || !Number.isSafeInteger(parentProcessId) || parentProcessId < 0 || !Number.isSafeInteger(processGroupId) || processGroupId <= 0) {
158
+ continue;
159
+ }
160
+ entries.push({ parentProcessId, processGroupId, processId });
161
+ }
162
+ return entries;
163
+ }
164
+ function readProcessTable() {
165
+ if (process.platform === "win32") {
166
+ return Promise.resolve([]);
167
+ }
168
+ const executable = process.platform === "darwin" ? "/bin/ps" : "/usr/bin/ps";
169
+ return new Promise((resolveTable) => {
170
+ execFile(
171
+ executable,
172
+ ["-axo", "pid=,ppid=,pgid="],
173
+ {
174
+ encoding: "utf8",
175
+ maxBuffer: PROCESS_TABLE_OUTPUT_LIMIT_BYTES,
176
+ signal: AbortSignal.timeout(PROCESS_TABLE_TIMEOUT_MILLISECONDS),
177
+ timeout: PROCESS_TABLE_TIMEOUT_MILLISECONDS
178
+ },
179
+ (error61, stdout) => {
180
+ resolveTable(error61 === null ? parseProcessTable(stdout) : []);
181
+ }
182
+ );
183
+ });
184
+ }
185
+ function selectProcessTree(entries, rootProcessId) {
186
+ const processIds = /* @__PURE__ */ new Set([rootProcessId]);
187
+ let previousSize = 0;
188
+ while (previousSize !== processIds.size) {
189
+ previousSize = processIds.size;
190
+ for (const entry of entries) {
191
+ if (processIds.has(entry.parentProcessId)) {
192
+ processIds.add(entry.processId);
193
+ }
194
+ }
195
+ }
196
+ return entries.filter((entry) => processIds.has(entry.processId)).map((entry) => ({
197
+ processGroupId: entry.processGroupId,
198
+ processId: entry.processId
199
+ }));
200
+ }
201
+ function mergeProcessTrees(captured, current) {
202
+ const processes = /* @__PURE__ */ new Map();
203
+ for (const identity2 of captured) {
204
+ processes.set(identity2.processId, identity2);
205
+ }
206
+ for (const identity2 of current) {
207
+ processes.set(identity2.processId, identity2);
208
+ }
209
+ return [...processes.values()];
210
+ }
211
+ async function captureProcessTree(child) {
212
+ if (process.platform === "win32" || child.pid === void 0) {
213
+ return [];
214
+ }
215
+ return selectProcessTree(await readProcessTable(), child.pid);
216
+ }
217
+ function signalDetachedProcessGroups(processes, rootProcessId, signal) {
218
+ const processIds = new Set(processes.map((identity2) => identity2.processId));
219
+ const signalledGroups = /* @__PURE__ */ new Set();
220
+ for (const identity2 of processes) {
221
+ if (identity2.processGroupId === rootProcessId || !processIds.has(identity2.processGroupId) || signalledGroups.has(identity2.processGroupId)) {
222
+ continue;
223
+ }
224
+ try {
225
+ process.kill(-identity2.processGroupId, signal);
226
+ signalledGroups.add(identity2.processGroupId);
227
+ } catch {
228
+ }
229
+ }
230
+ for (const identity2 of processes) {
231
+ if (identity2.processId === rootProcessId || identity2.processGroupId === rootProcessId || signalledGroups.has(identity2.processGroupId)) {
232
+ continue;
233
+ }
234
+ try {
235
+ process.kill(identity2.processId, signal);
236
+ } catch {
237
+ }
238
+ }
239
+ }
240
+ function processExists(processId) {
241
+ try {
242
+ process.kill(processId, 0);
243
+ return true;
244
+ } catch {
245
+ return false;
246
+ }
247
+ }
248
+ async function waitForCapturedProcessesExit(processes, rootProcessId) {
249
+ const processIds = processes.map((identity2) => identity2.processId).filter((processId) => processId !== rootProcessId);
250
+ for (let attempt = 0; attempt < PROCESS_EXIT_CHECK_ATTEMPTS; attempt += 1) {
251
+ if (processIds.every((processId) => !processExists(processId))) {
252
+ return true;
253
+ }
254
+ await readProcessTable();
255
+ }
256
+ return processIds.every((processId) => !processExists(processId));
257
+ }
221
258
  function terminateProcess(child) {
222
259
  if (child.exitCode !== null || child.signalCode !== null || child.pid === void 0) {
223
260
  return;
@@ -244,24 +281,35 @@ function forceTerminateProcess(child) {
244
281
  }
245
282
  child.kill("SIGKILL");
246
283
  }
247
- async function stopProcessTree(child) {
248
- if (child.exitCode !== null || child.signalCode !== null) {
284
+ async function stopProcessTree(child, capturedProcesses = []) {
285
+ const childProcessId = child.pid;
286
+ if (childProcessId === void 0) {
249
287
  return;
250
288
  }
251
- terminateProcess(child);
252
- const exitedAfterTermination = await waitForProcessExit(
253
- child,
254
- PROCESS_GRACEFUL_STOP_TIMEOUT_MILLISECONDS
289
+ const childExited = child.exitCode !== null || child.signalCode !== null;
290
+ if (childExited && capturedProcesses.length === 0) {
291
+ return;
292
+ }
293
+ const processes = mergeProcessTrees(
294
+ capturedProcesses,
295
+ childExited ? [] : await captureProcessTree(child)
255
296
  );
256
- if (exitedAfterTermination) {
297
+ signalDetachedProcessGroups(processes, childProcessId, GRACEFUL_STOP_SIGNAL);
298
+ terminateProcess(child);
299
+ const [exitedAfterTermination, descendantsExitedAfterTermination] = await Promise.all([
300
+ waitForProcessExit(child, PROCESS_GRACEFUL_STOP_TIMEOUT_MILLISECONDS),
301
+ waitForCapturedProcessesExit(processes, childProcessId)
302
+ ]);
303
+ if (exitedAfterTermination && descendantsExitedAfterTermination) {
257
304
  return;
258
305
  }
306
+ signalDetachedProcessGroups(processes, childProcessId, FORCED_STOP_SIGNAL);
259
307
  forceTerminateProcess(child);
260
- const exitedAfterForce = await waitForProcessExit(
261
- child,
262
- PROCESS_FORCED_STOP_TIMEOUT_MILLISECONDS
263
- );
264
- if (!exitedAfterForce) {
308
+ const [exitedAfterForce, descendantsExitedAfterForce] = await Promise.all([
309
+ waitForProcessExit(child, PROCESS_FORCED_STOP_TIMEOUT_MILLISECONDS),
310
+ waitForCapturedProcessesExit(processes, childProcessId)
311
+ ]);
312
+ if (!exitedAfterForce || !descendantsExitedAfterForce) {
265
313
  throw new ProcessExecutionError(
266
314
  "process_termination_timeout",
267
315
  "The executable process could not be stopped"
@@ -428,590 +476,1533 @@ async function runBoundedProcess(options) {
428
476
  }
429
477
  }
430
478
 
431
- // src/agent-installation/managed-npm-package.ts
432
- var INSTALL_TIMEOUT_MILLISECONDS = 5 * 60 * 1e3;
433
- var PACKAGE_METADATA_LIMIT_BYTES = 64 * 1024;
434
- var PACKAGE_LOCK_LIMIT_BYTES = 8 * 1024 * 1024;
435
- var ManagedNpmPackageDefinition = class {
436
- executableName;
437
- packageName;
438
- version;
439
- constructor(packageName, version2, executableName) {
440
- this.executableName = executableName;
441
- this.packageName = packageName;
442
- this.version = version2;
479
+ // src/agent-catalog/local-agent.ts
480
+ function readTrustedIdentity(path2) {
481
+ const resolvedPath = realpathSync(path2);
482
+ const metadata = statSync(resolvedPath);
483
+ return new ExecutableIdentity(
484
+ resolvedPath,
485
+ resolvedPath,
486
+ metadata.dev,
487
+ metadata.ino,
488
+ metadata.size,
489
+ metadata.ctimeMs
490
+ );
491
+ }
492
+ var LocalAgentDefinition = class {
493
+ adapterIdentity;
494
+ adapterPath;
495
+ adapterVersion;
496
+ agentIdentity;
497
+ agentVersion;
498
+ arguments;
499
+ command;
500
+ environment;
501
+ id;
502
+ label;
503
+ promptTimeoutMilliseconds;
504
+ constructor(id, label, command, arguments_, environment = {}, adapterVersion = "", agentVersion = "", adapterIdentity = readTrustedIdentity(command), agentIdentity = adapterIdentity, promptTimeoutMilliseconds = 30 * 60 * 1e3) {
505
+ if (!Number.isSafeInteger(promptTimeoutMilliseconds) || promptTimeoutMilliseconds < 1) {
506
+ throw new Error("The local Agent prompt deadline is invalid");
507
+ }
508
+ this.adapterIdentity = adapterIdentity;
509
+ this.adapterPath = adapterIdentity.realPath;
510
+ this.adapterVersion = adapterVersion;
511
+ this.agentIdentity = agentIdentity;
512
+ this.agentVersion = agentVersion;
513
+ this.id = id;
514
+ this.label = label;
515
+ this.promptTimeoutMilliseconds = promptTimeoutMilliseconds;
516
+ this.command = adapterIdentity.realPath;
517
+ this.arguments = Object.freeze([...arguments_]);
518
+ this.environment = Object.freeze({ ...environment });
443
519
  }
444
520
  };
445
- var SriManagedNpmPackageDefinition = class extends ManagedNpmPackageDefinition {
446
- integrity;
447
- tarballUrl;
448
- constructor(packageName, version2, executableName, integrity, tarballUrl) {
449
- super(packageName, version2, executableName);
450
- this.integrity = integrity;
451
- this.tarballUrl = tarballUrl;
521
+ var InstallableAgentDefinition = class {
522
+ id;
523
+ label;
524
+ requiresLocalApproval;
525
+ constructor(id, label, requiresLocalApproval = true) {
526
+ this.id = id;
527
+ this.label = label;
528
+ this.requiresLocalApproval = requiresLocalApproval;
452
529
  }
453
530
  };
454
- var ManagedNpmPackageError = class extends Error {
455
- code;
456
- constructor(code, message, options = {}) {
457
- super(message, options);
458
- this.code = code;
459
- this.name = "ManagedNpmPackageError";
531
+ var LocalAgentCatalogSnapshot = class {
532
+ agents;
533
+ installableAgents;
534
+ constructor(agents, installableAgents) {
535
+ this.agents = Object.freeze([...agents]);
536
+ this.installableAgents = Object.freeze([...installableAgents]);
460
537
  }
461
538
  };
462
- function isRecord2(value) {
463
- return typeof value === "object" && value !== null && !Array.isArray(value);
464
- }
465
- function packagePath(root, packageName) {
466
- return join3(root, "node_modules", ...packageName.split("/"));
467
- }
468
- function isContainedPath(root, candidate) {
469
- const difference = relative(root, candidate);
470
- return difference.length > 0 && difference !== ".." && !difference.startsWith(`..${sep}`) && !isAbsolute(difference);
471
- }
472
- async function readJsonRecord(path2, maximumBytes) {
473
- try {
474
- const handle = await open2(path2, "r");
475
- try {
476
- const metadata = await handle.stat();
477
- if (!metadata.isFile() || metadata.size > maximumBytes) {
478
- throw new ManagedNpmPackageError(
479
- "managed_package_verification_failed",
480
- "The managed npm package metadata is invalid"
481
- );
482
- }
483
- const value = JSON.parse(
484
- await handle.readFile({ encoding: "utf8" })
485
- );
486
- if (!isRecord2(value)) {
487
- throw new ManagedNpmPackageError(
488
- "managed_package_verification_failed",
489
- "The managed npm package metadata is invalid"
490
- );
539
+ var LocalAgentCatalogError = class extends Error {
540
+ code = "agent_not_ready";
541
+ constructor() {
542
+ super("The requested local Agent is not ready");
543
+ this.name = "LocalAgentCatalogError";
544
+ }
545
+ };
546
+ var LocalAgentCatalog = class {
547
+ #snapshot;
548
+ constructor(agents, installableAgents) {
549
+ this.#snapshot = this.validate(agents, installableAgents);
550
+ }
551
+ find = (agentId) => {
552
+ for (const agent of this.#snapshot.agents) {
553
+ if (agent.id === agentId) {
554
+ return agent;
491
555
  }
492
- return value;
493
- } finally {
494
- await handle.close();
556
+ }
557
+ throw new LocalAgentCatalogError();
558
+ };
559
+ read = () => this.#snapshot;
560
+ transition = (agents, installableAgents) => {
561
+ const next = this.validate(agents, installableAgents);
562
+ this.#snapshot = next;
563
+ return next;
564
+ };
565
+ validate = (agents, installableAgents) => {
566
+ const identifiers = /* @__PURE__ */ new Set();
567
+ for (const agent of agents) {
568
+ if (identifiers.has(agent.id)) {
569
+ throw new Error("The local Agent catalog contains duplicate entries");
570
+ }
571
+ identifiers.add(agent.id);
572
+ }
573
+ for (const agent of installableAgents) {
574
+ if (identifiers.has(agent.id)) {
575
+ throw new Error("A local Agent cannot be ready and installable");
576
+ }
577
+ identifiers.add(agent.id);
578
+ }
579
+ return new LocalAgentCatalogSnapshot(agents, installableAgents);
580
+ };
581
+ };
582
+
583
+ // src/agent-catalog/agent-discovery.ts
584
+ var AgentDiscovery = class {
585
+ specification;
586
+ constructor(specification) {
587
+ this.specification = specification;
588
+ }
589
+ };
590
+ var AvailableAgent = class extends AgentDiscovery {
591
+ agent;
592
+ status = "ready";
593
+ constructor(specification, agent) {
594
+ super(specification);
595
+ this.agent = agent;
596
+ }
597
+ };
598
+ var UnavailableAgent = class extends AgentDiscovery {
599
+ };
600
+ var MissingAdapter = class extends UnavailableAgent {
601
+ code = "adapter_not_found";
602
+ message = "ACP adapter not found";
603
+ status = "missing_adapter";
604
+ constructor(specification) {
605
+ super(specification);
606
+ }
607
+ };
608
+ var MissingAgent = class extends UnavailableAgent {
609
+ code = "agent_not_found";
610
+ message = "Agent executable not found";
611
+ status = "missing_agent";
612
+ constructor(specification) {
613
+ super(specification);
614
+ }
615
+ };
616
+ var ProbeFailed = class extends UnavailableAgent {
617
+ code;
618
+ message;
619
+ status = "probe_failed";
620
+ constructor(specification, code, message) {
621
+ super(specification);
622
+ this.code = code;
623
+ this.message = message;
624
+ }
625
+ };
626
+ var IncompatibleAdapter = class extends UnavailableAgent {
627
+ code = "acp_incompatible";
628
+ message = "ACP adapter is incompatible";
629
+ status = "incompatible_adapter";
630
+ constructor(specification) {
631
+ super(specification);
632
+ }
633
+ };
634
+ var AuthenticationRequiredAgent = class extends UnavailableAgent {
635
+ code = "authentication_required";
636
+ message = "Agent authentication is required";
637
+ status = "authentication_required";
638
+ constructor(specification) {
639
+ super(specification);
640
+ }
641
+ };
642
+ var LocalAgentInventory = class {
643
+ available;
644
+ discoveries;
645
+ installable;
646
+ unavailable;
647
+ constructor(discoveries, grantedAgents = /* @__PURE__ */ new Set(), platform = process.platform, architecture = process.arch) {
648
+ const available = [];
649
+ const unavailable = [];
650
+ for (const discovery of discoveries) {
651
+ if (discovery instanceof AvailableAgent) {
652
+ available.push(discovery);
653
+ } else if (discovery instanceof UnavailableAgent) {
654
+ unavailable.push(discovery);
655
+ } else {
656
+ throw new Error("Unsupported Agent discovery outcome");
657
+ }
658
+ }
659
+ this.available = Object.freeze(available);
660
+ this.discoveries = Object.freeze([...discoveries]);
661
+ this.installable = Object.freeze(
662
+ unavailable.filter(
663
+ (discovery) => !(discovery instanceof AuthenticationRequiredAgent) && discovery.specification.supportsInstallation(
664
+ platform,
665
+ architecture
666
+ )
667
+ ).map(
668
+ (discovery) => new InstallableAgentDefinition(
669
+ discovery.specification.id,
670
+ discovery.specification.label,
671
+ !grantedAgents.has(discovery.specification.id)
672
+ )
673
+ )
674
+ );
675
+ this.unavailable = Object.freeze(unavailable);
676
+ }
677
+ readyAgents = () => this.available.map((discovery) => discovery.agent);
678
+ };
679
+
680
+ // src/agent-catalog/agent-diagnostics.ts
681
+ var AvailableAgentDiagnostic = class {
682
+ adapterVersion;
683
+ agentVersion;
684
+ id;
685
+ label;
686
+ status = "ready";
687
+ constructor(discovery) {
688
+ this.adapterVersion = discovery.agent.adapterVersion;
689
+ this.agentVersion = discovery.agent.agentVersion;
690
+ this.id = discovery.specification.id;
691
+ this.label = discovery.specification.label;
692
+ }
693
+ };
694
+ var UnavailableAgentDiagnostic = class {
695
+ code;
696
+ id;
697
+ label;
698
+ message;
699
+ recoveryCommand;
700
+ status;
701
+ constructor(discovery) {
702
+ this.code = discovery.code;
703
+ this.id = discovery.specification.id;
704
+ this.label = discovery.specification.label;
705
+ this.message = discovery.message;
706
+ this.recoveryCommand = discovery instanceof MissingAdapter ? discovery.specification.recoveryCommand() : discovery.code === "authentication_required" ? discovery.specification.authenticationCommand() : "";
707
+ this.status = discovery.status;
708
+ }
709
+ };
710
+ var AgentDiagnostics = class {
711
+ available;
712
+ unavailable;
713
+ constructor(inventory) {
714
+ this.available = inventory.available.map(
715
+ (discovery) => new AvailableAgentDiagnostic(discovery)
716
+ );
717
+ this.unavailable = inventory.unavailable.map(
718
+ (discovery) => new UnavailableAgentDiagnostic(discovery)
719
+ );
720
+ }
721
+ };
722
+ function humanLine(label, status, detail) {
723
+ return `${label.padEnd(13)} ${status.padEnd(17)} ${detail}`.trimEnd();
724
+ }
725
+ function formatAgentDiagnostics(inventory, json2) {
726
+ const diagnostics = new AgentDiagnostics(inventory);
727
+ if (json2) {
728
+ return `${JSON.stringify(diagnostics, null, 2)}
729
+ `;
730
+ }
731
+ const lines = [];
732
+ for (const available of diagnostics.available) {
733
+ lines.push(
734
+ humanLine(
735
+ available.label,
736
+ available.status,
737
+ `${available.agentVersion} ${available.adapterVersion}`
738
+ )
739
+ );
740
+ }
741
+ for (const unavailable of diagnostics.unavailable) {
742
+ lines.push(
743
+ humanLine(unavailable.label, unavailable.status, unavailable.message)
744
+ );
745
+ if (unavailable.recoveryCommand.length > 0) {
746
+ lines.push(
747
+ unavailable.code === "authentication_required" ? ` Authenticate locally: ${unavailable.recoveryCommand}` : ` Install adapter: ${unavailable.recoveryCommand}`
748
+ );
749
+ }
750
+ }
751
+ return `${lines.join("\n")}
752
+ `;
753
+ }
754
+
755
+ // src/agent-installation/installation-grants.ts
756
+ import { open } from "node:fs/promises";
757
+ import { join as join2 } from "node:path";
758
+ var MAX_GRANT_FILE_LENGTH = 16 * 1024;
759
+ var AGENT_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
760
+ function isRecord(value) {
761
+ return typeof value === "object" && value !== null && !Array.isArray(value);
762
+ }
763
+ async function readGrantedAgents(stateDirectory) {
764
+ const path2 = join2(stateDirectory, "installation-grants.json");
765
+ let handle;
766
+ try {
767
+ handle = await open(path2, "r");
768
+ } catch (error61) {
769
+ if (error61 instanceof Error && "code" in error61 && error61.code === "ENOENT") {
770
+ return /* @__PURE__ */ new Set();
771
+ }
772
+ throw error61;
773
+ }
774
+ try {
775
+ const metadata = await handle.stat();
776
+ if (!metadata.isFile() || metadata.size > MAX_GRANT_FILE_LENGTH || typeof process.getuid === "function" && metadata.uid !== process.getuid() || process.platform !== "win32" && (metadata.mode & 63) !== 0) {
777
+ throw new Error("The local installation grant file is not private");
778
+ }
779
+ const encoded = await handle.readFile({ encoding: "utf8" });
780
+ const value = JSON.parse(encoded);
781
+ if (!isRecord(value) || Object.keys(value).length !== 1 || !Array.isArray(value["agents"])) {
782
+ throw new Error("The local installation grant file is invalid");
783
+ }
784
+ const agents = /* @__PURE__ */ new Set();
785
+ for (const candidate of value["agents"]) {
786
+ if (typeof candidate !== "string" || !AGENT_ID_PATTERN.test(candidate) || agents.has(candidate)) {
787
+ throw new Error("The local installation grant file is invalid");
788
+ }
789
+ agents.add(candidate);
790
+ }
791
+ return agents;
792
+ } finally {
793
+ await handle.close();
794
+ }
795
+ }
796
+
797
+ // src/storage/state-directory.ts
798
+ import { homedir } from "node:os";
799
+ import { join as join3, resolve as resolve2 } from "node:path";
800
+ function readStateDirectory(environment = process.env) {
801
+ const configured = environment["MOBIUS_STATE_DIRECTORY"]?.trim();
802
+ if (configured !== void 0 && configured.length > 0) {
803
+ return resolve2(configured);
804
+ }
805
+ return join3(homedir(), ".mobius");
806
+ }
807
+
808
+ // src/agent-installation/managed-npm-package.ts
809
+ import {
810
+ mkdir,
811
+ mkdtemp,
812
+ open as open2,
813
+ realpath as realpath2,
814
+ rename,
815
+ rm,
816
+ stat as stat2
817
+ } from "node:fs/promises";
818
+ import { delimiter, dirname, isAbsolute, join as join4, relative, sep } from "node:path";
819
+ var INSTALL_TIMEOUT_MILLISECONDS = 5 * 60 * 1e3;
820
+ var PACKAGE_METADATA_LIMIT_BYTES = 64 * 1024;
821
+ var PACKAGE_LOCK_LIMIT_BYTES = 8 * 1024 * 1024;
822
+ var ManagedNpmPackageDefinition = class {
823
+ dependencyResolutionArguments = Object.freeze([]);
824
+ executableName;
825
+ includeOptionalDependencies = false;
826
+ packageName;
827
+ version;
828
+ verifyDependencyClosure = false;
829
+ constructor(packageName, version2, executableName) {
830
+ this.executableName = executableName;
831
+ this.packageName = packageName;
832
+ this.version = version2;
833
+ }
834
+ };
835
+ var SriManagedNpmPackageDefinition = class extends ManagedNpmPackageDefinition {
836
+ integrity;
837
+ tarballUrl;
838
+ constructor(packageName, version2, executableName, integrity, tarballUrl) {
839
+ super(packageName, version2, executableName);
840
+ this.integrity = integrity;
841
+ this.tarballUrl = tarballUrl;
842
+ }
843
+ };
844
+ var ManagedNpmPackageError = class extends Error {
845
+ code;
846
+ constructor(code, message, options = {}) {
847
+ super(message, options);
848
+ this.code = code;
849
+ this.name = "ManagedNpmPackageError";
850
+ }
851
+ };
852
+ function isRecord2(value) {
853
+ return typeof value === "object" && value !== null && !Array.isArray(value);
854
+ }
855
+ function packagePath(root, packageName) {
856
+ return join4(root, "node_modules", ...packageName.split("/"));
857
+ }
858
+ function isContainedPath(root, candidate) {
859
+ const difference = relative(root, candidate);
860
+ return difference.length > 0 && difference !== ".." && !difference.startsWith(`..${sep}`) && !isAbsolute(difference);
861
+ }
862
+ async function readJsonRecord(path2, maximumBytes) {
863
+ try {
864
+ const handle = await open2(path2, "r");
865
+ try {
866
+ const metadata = await handle.stat();
867
+ if (!metadata.isFile() || metadata.size > maximumBytes) {
868
+ throw new ManagedNpmPackageError(
869
+ "managed_package_verification_failed",
870
+ "The managed npm package metadata is invalid"
871
+ );
872
+ }
873
+ const value = JSON.parse(
874
+ await handle.readFile({ encoding: "utf8" })
875
+ );
876
+ if (!isRecord2(value)) {
877
+ throw new ManagedNpmPackageError(
878
+ "managed_package_verification_failed",
879
+ "The managed npm package metadata is invalid"
880
+ );
881
+ }
882
+ return value;
883
+ } finally {
884
+ await handle.close();
495
885
  }
496
886
  } catch (error61) {
497
887
  if (error61 instanceof ManagedNpmPackageError) {
498
888
  throw error61;
499
889
  }
500
- throw new ManagedNpmPackageError(
501
- "managed_package_verification_failed",
502
- "The managed npm package metadata is invalid",
503
- { cause: error61 }
890
+ throw new ManagedNpmPackageError(
891
+ "managed_package_verification_failed",
892
+ "The managed npm package metadata is invalid",
893
+ { cause: error61 }
894
+ );
895
+ }
896
+ }
897
+ function readBinPath(packageMetadata, executableName) {
898
+ const bin = packageMetadata["bin"];
899
+ if (typeof bin === "string") {
900
+ return bin;
901
+ }
902
+ if (isRecord2(bin) && typeof bin[executableName] === "string") {
903
+ return bin[executableName];
904
+ }
905
+ throw new ManagedNpmPackageError(
906
+ "managed_package_verification_failed",
907
+ "The managed npm package executable declaration is invalid"
908
+ );
909
+ }
910
+ function verifyPackageLock(lock, definition) {
911
+ if (lock["lockfileVersion"] !== 3) {
912
+ throw new ManagedNpmPackageError(
913
+ "managed_package_verification_failed",
914
+ "The managed npm package lockfile is invalid"
915
+ );
916
+ }
917
+ const packages = lock["packages"];
918
+ if (!isRecord2(packages)) {
919
+ throw new ManagedNpmPackageError(
920
+ "managed_package_verification_failed",
921
+ "The managed npm package lockfile is invalid"
922
+ );
923
+ }
924
+ const suffix = `node_modules/${definition.packageName}`;
925
+ let entry;
926
+ let matches = 0;
927
+ for (const [key, candidate] of Object.entries(packages)) {
928
+ const normalizedKey = key.replaceAll("\\", "/");
929
+ if (definition.verifyDependencyClosure && normalizedKey.length > 0) {
930
+ let resolved;
931
+ try {
932
+ if (!normalizedKey.split("/").includes("node_modules") || !isRecord2(candidate) || typeof candidate["version"] !== "string" || candidate["version"].length === 0 || typeof candidate["integrity"] !== "string" || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(candidate["integrity"]) || typeof candidate["resolved"] !== "string") {
933
+ throw new Error("Invalid locked dependency");
934
+ }
935
+ resolved = new URL(candidate["resolved"]);
936
+ } catch (error61) {
937
+ throw new ManagedNpmPackageError(
938
+ "managed_package_verification_failed",
939
+ "The managed npm dependency closure is invalid",
940
+ { cause: error61 }
941
+ );
942
+ }
943
+ if (resolved.protocol !== "https:" || resolved.hostname !== "registry.npmjs.org" || resolved.port.length > 0 || resolved.username.length > 0 || resolved.password.length > 0) {
944
+ throw new ManagedNpmPackageError(
945
+ "managed_package_verification_failed",
946
+ "The managed npm dependency closure is invalid"
947
+ );
948
+ }
949
+ }
950
+ if (normalizedKey === suffix || normalizedKey.endsWith(`/${suffix}`)) {
951
+ entry = candidate;
952
+ matches += 1;
953
+ }
954
+ }
955
+ if (matches !== 1 || !isRecord2(entry) || entry["integrity"] !== definition.integrity || entry["resolved"] !== definition.tarballUrl || entry["version"] !== definition.version) {
956
+ throw new ManagedNpmPackageError(
957
+ "managed_package_verification_failed",
958
+ "The managed npm package lockfile does not match the pinned artifact"
959
+ );
960
+ }
961
+ }
962
+ async function validateManagedNpmPackage(root, definition) {
963
+ try {
964
+ return await validateManagedNpmPackageContents(root, definition);
965
+ } catch (error61) {
966
+ if (error61 instanceof ManagedNpmPackageError) {
967
+ throw error61;
968
+ }
969
+ throw new ManagedNpmPackageError(
970
+ "managed_package_verification_failed",
971
+ "The managed npm package is invalid",
972
+ { cause: error61 }
973
+ );
974
+ }
975
+ }
976
+ async function validateManagedNpmPackageContents(root, definition) {
977
+ const rootPath = await realpath2(root);
978
+ const packageRoot = await realpath2(
979
+ packagePath(rootPath, definition.packageName)
980
+ );
981
+ if (!isContainedPath(rootPath, packageRoot)) {
982
+ throw new ManagedNpmPackageError(
983
+ "managed_package_verification_failed",
984
+ "The managed npm package escaped its private cache"
985
+ );
986
+ }
987
+ const metadata = await readJsonRecord(
988
+ join4(packageRoot, "package.json"),
989
+ PACKAGE_METADATA_LIMIT_BYTES
990
+ );
991
+ if (metadata["name"] !== definition.packageName || metadata["version"] !== definition.version) {
992
+ throw new ManagedNpmPackageError(
993
+ "managed_package_verification_failed",
994
+ "The managed npm package identity is invalid"
995
+ );
996
+ }
997
+ if (definition instanceof SriManagedNpmPackageDefinition) {
998
+ verifyPackageLock(
999
+ await readJsonRecord(
1000
+ join4(rootPath, "package-lock.json"),
1001
+ PACKAGE_LOCK_LIMIT_BYTES
1002
+ ),
1003
+ definition
1004
+ );
1005
+ }
1006
+ const declaredBin = readBinPath(metadata, definition.executableName);
1007
+ const executable = await resolveExecutablePath(
1008
+ join4(packageRoot, declaredBin)
1009
+ );
1010
+ if (executable === null || !isContainedPath(rootPath, executable.path)) {
1011
+ throw new ManagedNpmPackageError(
1012
+ "managed_package_verification_failed",
1013
+ "The managed npm package executable is invalid"
1014
+ );
1015
+ }
1016
+ return executable;
1017
+ }
1018
+ var ManagedNpmPackageInstaller = class {
1019
+ install = async (root, definition, environment, signal) => {
1020
+ signal.throwIfAborted();
1021
+ try {
1022
+ const metadata = await stat2(root);
1023
+ if (metadata.isDirectory()) {
1024
+ return await validateManagedNpmPackage(root, definition);
1025
+ }
1026
+ } catch (error61) {
1027
+ if (!(error61 instanceof Error) || !("code" in error61) || error61.code !== "ENOENT") {
1028
+ throw error61;
1029
+ }
1030
+ }
1031
+ const parent = dirname(root);
1032
+ await mkdir(parent, { mode: 448, recursive: true });
1033
+ const staging = await mkdtemp(
1034
+ join4(parent, `.${definition.version}.staging-`)
1035
+ );
1036
+ try {
1037
+ const npm = await findExecutableOnPath(
1038
+ process.platform === "win32" ? "npm.cmd" : "npm",
1039
+ environment
1040
+ );
1041
+ if (npm === null) {
1042
+ throw new ManagedNpmPackageError(
1043
+ "package_manager_not_found",
1044
+ "A supported npm executable is required to install the managed package"
1045
+ );
1046
+ }
1047
+ const installArguments = ["install", "--prefix", staging];
1048
+ installArguments.push(...definition.dependencyResolutionArguments);
1049
+ if (definition.includeOptionalDependencies) {
1050
+ installArguments.push("--include=optional");
1051
+ } else {
1052
+ installArguments.push("--omit=optional");
1053
+ }
1054
+ installArguments.push(
1055
+ "--ignore-scripts",
1056
+ "--no-audit",
1057
+ "--no-fund",
1058
+ "--package-lock=true",
1059
+ "--save-exact",
1060
+ "--registry=https://registry.npmjs.org/",
1061
+ `${definition.packageName}@${definition.version}`
1062
+ );
1063
+ const installEnvironment = {
1064
+ HOME: join4(staging, "home"),
1065
+ NPM_CONFIG_AUDIT: "false",
1066
+ NPM_CONFIG_CACHE: join4(staging, ".npm-cache"),
1067
+ NPM_CONFIG_FUND: "false",
1068
+ NPM_CONFIG_IGNORE_SCRIPTS: "true",
1069
+ NPM_CONFIG_PACKAGE_LOCK: "true",
1070
+ NPM_CONFIG_REGISTRY: "https://registry.npmjs.org/",
1071
+ PATH: `${dirname(process.execPath)}${delimiter}/usr/bin${delimiter}/bin`
1072
+ };
1073
+ if (definition.includeOptionalDependencies) {
1074
+ installEnvironment["NPM_CONFIG_INCLUDE"] = "optional";
1075
+ } else {
1076
+ installEnvironment["NPM_CONFIG_OMIT"] = "optional";
1077
+ }
1078
+ await runBoundedProcess({
1079
+ arguments: installArguments,
1080
+ command: npm.path,
1081
+ cwd: staging,
1082
+ environment: installEnvironment,
1083
+ outputLimitBytes: 64 * 1024,
1084
+ signal,
1085
+ timeoutMilliseconds: INSTALL_TIMEOUT_MILLISECONDS
1086
+ });
1087
+ await Promise.all([
1088
+ rm(join4(staging, ".npm-cache"), { force: true, recursive: true }),
1089
+ rm(join4(staging, "home"), { force: true, recursive: true })
1090
+ ]);
1091
+ await validateManagedNpmPackage(staging, definition);
1092
+ try {
1093
+ await rename(staging, root);
1094
+ } catch (error61) {
1095
+ try {
1096
+ return await validateManagedNpmPackage(root, definition);
1097
+ } catch {
1098
+ throw new ManagedNpmPackageError(
1099
+ "managed_package_publish_failed",
1100
+ "The managed npm package could not be published",
1101
+ { cause: error61 }
1102
+ );
1103
+ }
1104
+ }
1105
+ return await validateManagedNpmPackage(root, definition);
1106
+ } catch (error61) {
1107
+ if (signal.aborted) {
1108
+ signal.throwIfAborted();
1109
+ }
1110
+ if (error61 instanceof ManagedNpmPackageError) {
1111
+ throw error61;
1112
+ }
1113
+ throw new ManagedNpmPackageError(
1114
+ "managed_package_install_failed",
1115
+ "The pinned npm package could not be installed",
1116
+ { cause: error61 }
1117
+ );
1118
+ } finally {
1119
+ await rm(staging, { force: true, recursive: true });
1120
+ }
1121
+ };
1122
+ };
1123
+
1124
+ // src/agent-catalog/agent-specification.ts
1125
+ import { join as join5 } from "node:path";
1126
+ var VERSION_PROBE_TIMEOUT_MILLISECONDS = 3e3;
1127
+ var ACP_PROBE_TIMEOUT_MILLISECONDS = 5e3;
1128
+ var DEFAULT_PROMPT_TIMEOUT_MILLISECONDS = 30 * 60 * 1e3;
1129
+ var LocalAgentSpecification = class {
1130
+ acpProbeTimeoutMilliseconds = ACP_PROBE_TIMEOUT_MILLISECONDS;
1131
+ authenticationCommand = () => "";
1132
+ adapterVersionArguments = ["--version"];
1133
+ agentVersionArguments = ["--version"];
1134
+ promptTimeoutMilliseconds = DEFAULT_PROMPT_TIMEOUT_MILLISECONDS;
1135
+ versionProbeTimeoutMilliseconds = VERSION_PROBE_TIMEOUT_MILLISECONDS;
1136
+ resolveManagedAgent = async (stateDirectory, platform, signal) => {
1137
+ signal.throwIfAborted();
1138
+ const executable = await resolveExecutablePath(
1139
+ join5(stateDirectory, ...this.managedAgentExecutablePath),
1140
+ platform
504
1141
  );
1142
+ signal.throwIfAborted();
1143
+ if (executable === null) {
1144
+ throw new Error("The managed Agent executable is unavailable");
1145
+ }
1146
+ return executable;
1147
+ };
1148
+ createLaunchEnvironment = async (_agent, _context) => Object.freeze({});
1149
+ recoveryCommand = () => "";
1150
+ supportsInstallation = (platform, architecture) => (platform === "darwin" || platform === "linux") && (architecture === "arm64" || architecture === "x64");
1151
+ verifyAcp = (_connection, _initialized, _defaultWorkspace, _signal) => Promise.resolve();
1152
+ };
1153
+ var DirectAcpAgentDefinition = class extends LocalAgentSpecification {
1154
+ adapterOverrideVariable;
1155
+ agentOverrideVariable;
1156
+ launchArguments;
1157
+ managedAdapterExecutablePath;
1158
+ managedAgentExecutablePath;
1159
+ constructor(executableOverrideVariable, launchArguments, managedExecutablePath) {
1160
+ super();
1161
+ this.adapterOverrideVariable = executableOverrideVariable;
1162
+ this.agentOverrideVariable = executableOverrideVariable;
1163
+ this.launchArguments = Object.freeze([...launchArguments]);
1164
+ this.managedAdapterExecutablePath = Object.freeze([
1165
+ ...managedExecutablePath
1166
+ ]);
1167
+ this.managedAgentExecutablePath = this.managedAdapterExecutablePath;
505
1168
  }
506
- }
507
- function readBinPath(packageMetadata, executableName) {
508
- const bin = packageMetadata["bin"];
509
- if (typeof bin === "string") {
510
- return bin;
511
- }
512
- if (isRecord2(bin) && typeof bin[executableName] === "string") {
513
- return bin[executableName];
1169
+ };
1170
+ var BridgedAcpAgentDefinition = class extends LocalAgentSpecification {
1171
+ launchArguments = Object.freeze([]);
1172
+ get adapterExecutable() {
1173
+ return this.adapterPackageDefinition.executableName;
514
1174
  }
515
- throw new ManagedNpmPackageError(
516
- "managed_package_verification_failed",
517
- "The managed npm package executable declaration is invalid"
518
- );
519
- }
520
- function verifyPackageLock(lock, definition) {
521
- if (lock["lockfileVersion"] !== 3) {
522
- throw new ManagedNpmPackageError(
523
- "managed_package_verification_failed",
524
- "The managed npm package lockfile is invalid"
525
- );
1175
+ get adapterPackage() {
1176
+ return this.adapterPackageDefinition.packageName;
526
1177
  }
527
- const packages = lock["packages"];
528
- if (!isRecord2(packages)) {
529
- throw new ManagedNpmPackageError(
530
- "managed_package_verification_failed",
531
- "The managed npm package lockfile is invalid"
532
- );
1178
+ get adapterVersion() {
1179
+ return this.adapterPackageDefinition.version;
533
1180
  }
534
- const suffix = `node_modules/${definition.packageName}`;
535
- let entry;
536
- let matches = 0;
537
- for (const [key, candidate] of Object.entries(packages)) {
538
- const normalizedKey = key.replaceAll("\\", "/");
539
- if (normalizedKey === suffix || normalizedKey.endsWith(`/${suffix}`)) {
540
- entry = candidate;
541
- matches += 1;
542
- }
1181
+ createLaunchEnvironment = async (agent, _context) => Object.freeze({ [this.adapterEnvironmentVariable]: agent.path });
1182
+ recoveryCommand = () => `npm install -g --omit=optional ${this.adapterPackage}@${this.adapterVersion}`;
1183
+ };
1184
+
1185
+ // src/agent-catalog/agents/claude-code-agent.ts
1186
+ var ClaudeCodeAgentSpecification = class extends BridgedAcpAgentDefinition {
1187
+ adapterEnvironmentVariable = "CLAUDE_CODE_EXECUTABLE";
1188
+ adapterOverrideVariable = "MOBIUS_CLAUDE_ACP_PATH";
1189
+ adapterPackageDefinition = new ManagedNpmPackageDefinition(
1190
+ "@agentclientprotocol/claude-agent-acp",
1191
+ "0.73.0",
1192
+ "claude-agent-acp"
1193
+ );
1194
+ agentExecutable = "claude";
1195
+ agentOverrideVariable = "MOBIUS_CLAUDE_PATH";
1196
+ id = "claude";
1197
+ label = "Claude Code";
1198
+ managedAdapterExecutablePath = [
1199
+ "adapters",
1200
+ "claude",
1201
+ "0.73.0",
1202
+ "node_modules",
1203
+ "@agentclientprotocol",
1204
+ "claude-agent-acp",
1205
+ "dist",
1206
+ "index.js"
1207
+ ];
1208
+ managedAgentExecutablePath = [
1209
+ "agents",
1210
+ "claude",
1211
+ "home",
1212
+ ".local",
1213
+ "bin",
1214
+ "claude"
1215
+ ];
1216
+ };
1217
+
1218
+ // src/agent-catalog/agents/codex-agent.ts
1219
+ var CodexAgentSpecification = class extends BridgedAcpAgentDefinition {
1220
+ adapterEnvironmentVariable = "CODEX_PATH";
1221
+ adapterOverrideVariable = "MOBIUS_CODEX_ACP_PATH";
1222
+ adapterPackageDefinition = new ManagedNpmPackageDefinition(
1223
+ "@agentclientprotocol/codex-acp",
1224
+ "1.8.0",
1225
+ "codex-acp"
1226
+ );
1227
+ agentExecutable = "codex";
1228
+ agentOverrideVariable = "MOBIUS_CODEX_PATH";
1229
+ id = "codex";
1230
+ label = "Codex";
1231
+ managedAdapterExecutablePath = [
1232
+ "adapters",
1233
+ "codex",
1234
+ "1.8.0",
1235
+ "node_modules",
1236
+ "@agentclientprotocol",
1237
+ "codex-acp",
1238
+ "dist",
1239
+ "index.js"
1240
+ ];
1241
+ managedAgentExecutablePath = ["agents", "codex", "bin", "codex"];
1242
+ };
1243
+
1244
+ // src/agent-catalog/agents/deepseek-harness-agent.ts
1245
+ import { realpath as realpath3, stat as stat3 } from "node:fs/promises";
1246
+ import { isAbsolute as isAbsolute2, join as join6, normalize } from "node:path";
1247
+
1248
+ // src/agent-installation/deepseek-managed-package.ts
1249
+ var DeepSeekHarnessManagedPackage = class extends SriManagedNpmPackageDefinition {
1250
+ dependencyResolutionArguments = Object.freeze([
1251
+ "--before=2026-09-03T06:31:52.107Z"
1252
+ ]);
1253
+ includeOptionalDependencies = true;
1254
+ verifyDependencyClosure = true;
1255
+ };
1256
+ var DEEPSEEK_HARNESS_PACKAGE = new DeepSeekHarnessManagedPackage(
1257
+ "@deepseek-ai/dsh",
1258
+ "0.1.2-rc.1",
1259
+ "dsh",
1260
+ "sha512-RPq48TzxvwpdT9/7W1tbhZDBMmeK+bxDrX9cqQC27Wx/LqtgJF8PSa3b3xriU8oxtvhwYmk21w2cej3uMQrnVA==",
1261
+ "https://registry.npmjs.org/@deepseek-ai/dsh/-/dsh-0.1.2-rc.1.tgz"
1262
+ );
1263
+
1264
+ // src/agent-catalog/agent-probe-error.ts
1265
+ var AgentProbeError = class extends Error {
1266
+ code;
1267
+ constructor(code, message, options = {}) {
1268
+ super(message, options);
1269
+ this.code = code;
1270
+ this.name = "AgentProbeError";
543
1271
  }
544
- if (matches !== 1 || !isRecord2(entry) || entry["integrity"] !== definition.integrity || entry["resolved"] !== definition.tarballUrl || entry["version"] !== definition.version) {
545
- throw new ManagedNpmPackageError(
546
- "managed_package_verification_failed",
547
- "The managed npm package lockfile does not match the pinned artifact"
1272
+ };
1273
+
1274
+ // src/agent-catalog/agents/deepseek-harness-agent.ts
1275
+ async function validateAgentHome(path2) {
1276
+ if (!isAbsolute2(path2) || normalize(path2) !== path2) {
1277
+ throw new AgentProbeError(
1278
+ "agent_environment_invalid",
1279
+ "The configured Agent home is invalid"
548
1280
  );
549
1281
  }
550
- }
551
- async function validateManagedNpmPackage(root, definition) {
552
1282
  try {
553
- return await validateManagedNpmPackageContents(root, definition);
1283
+ const resolved = await realpath3(path2);
1284
+ const metadata = await stat3(resolved);
1285
+ if (!metadata.isDirectory() || typeof process.getuid === "function" && metadata.uid !== process.getuid()) {
1286
+ throw new Error("Agent home is not a user-owned directory");
1287
+ }
1288
+ return resolved;
554
1289
  } catch (error61) {
555
- if (error61 instanceof ManagedNpmPackageError) {
1290
+ if (error61 instanceof AgentProbeError) {
556
1291
  throw error61;
557
1292
  }
558
- throw new ManagedNpmPackageError(
559
- "managed_package_verification_failed",
560
- "The managed npm package is invalid",
1293
+ throw new AgentProbeError(
1294
+ "agent_environment_invalid",
1295
+ "The configured Agent home is invalid",
561
1296
  { cause: error61 }
562
1297
  );
563
1298
  }
564
1299
  }
565
- async function validateManagedNpmPackageContents(root, definition) {
566
- const rootPath = await realpath2(root);
567
- const packageRoot = await realpath2(
568
- packagePath(rootPath, definition.packageName)
569
- );
570
- if (!isContainedPath(rootPath, packageRoot)) {
571
- throw new ManagedNpmPackageError(
572
- "managed_package_verification_failed",
573
- "The managed npm package escaped its private cache"
1300
+ var DeepSeekHarnessAgentSpecification = class extends DirectAcpAgentDefinition {
1301
+ adapterVersion = DEEPSEEK_HARNESS_PACKAGE.version;
1302
+ authenticationCommand = () => "dsh web";
1303
+ agentExecutable = "dsh";
1304
+ id = "deepseek-harness";
1305
+ label = "DeepSeek Harness";
1306
+ constructor() {
1307
+ super(
1308
+ "MOBIUS_DEEPSEEK_HARNESS_PATH",
1309
+ ["--profile", "acp"],
1310
+ [
1311
+ "agents",
1312
+ "deepseek-harness",
1313
+ DEEPSEEK_HARNESS_PACKAGE.version,
1314
+ "node_modules",
1315
+ "@deepseek-ai",
1316
+ "dsh",
1317
+ "lib",
1318
+ "bin.js"
1319
+ ]
574
1320
  );
575
1321
  }
576
- const metadata = await readJsonRecord(
577
- join3(packageRoot, "package.json"),
578
- PACKAGE_METADATA_LIMIT_BYTES
579
- );
580
- if (metadata["name"] !== definition.packageName || metadata["version"] !== definition.version) {
581
- throw new ManagedNpmPackageError(
582
- "managed_package_verification_failed",
583
- "The managed npm package identity is invalid"
1322
+ createLaunchEnvironment = async (agent, context) => {
1323
+ let selectedHome = context.environment["DSH_HOME"];
1324
+ try {
1325
+ const managed = await this.resolveManagedAgent(
1326
+ readStateDirectory(context.environment),
1327
+ context.platform ?? process.platform,
1328
+ context.signal
1329
+ );
1330
+ if (managed.path === agent.path) {
1331
+ selectedHome = join6(
1332
+ readStateDirectory(context.environment),
1333
+ "agents",
1334
+ this.id,
1335
+ "home"
1336
+ );
1337
+ }
1338
+ } catch {
1339
+ context.signal.throwIfAborted();
1340
+ }
1341
+ if (selectedHome === void 0 || selectedHome.length === 0) {
1342
+ return Object.freeze({ DSH_PERMISSION_MODE: "workspace-write" });
1343
+ }
1344
+ return Object.freeze({
1345
+ DSH_HOME: await validateAgentHome(selectedHome),
1346
+ DSH_PERMISSION_MODE: "workspace-write"
1347
+ });
1348
+ };
1349
+ resolveManagedAgent = async (stateDirectory, _platform, signal) => {
1350
+ signal.throwIfAborted();
1351
+ const executable = await validateManagedNpmPackage(
1352
+ join6(stateDirectory, "agents", this.id, DEEPSEEK_HARNESS_PACKAGE.version),
1353
+ DEEPSEEK_HARNESS_PACKAGE
584
1354
  );
1355
+ signal.throwIfAborted();
1356
+ return executable;
1357
+ };
1358
+ verifyAcp = (_connection, initialized, _defaultWorkspace, _signal) => {
1359
+ const capabilities = initialized.agentCapabilities ?? {};
1360
+ const session = capabilities.sessionCapabilities;
1361
+ const prompt = capabilities.promptCapabilities;
1362
+ if ((initialized.authMethods ?? []).length !== 0 || capabilities.loadSession === true || capabilities.mcpCapabilities?.http !== true || session?.close == null || session.list == null || session.resume == null || session.additionalDirectories != null || session.delete != null || session.fork != null || prompt?.audio === true || prompt?.embeddedContext === true || prompt?.image === true) {
1363
+ throw new AgentProbeError(
1364
+ "acp_incompatible",
1365
+ "DeepSeek Harness reported an unsupported ACP capability set"
1366
+ );
1367
+ }
1368
+ return Promise.resolve();
1369
+ };
1370
+ };
1371
+
1372
+ // src/agent-catalog/agents/opencode-agent.ts
1373
+ import { join as join8 } from "node:path";
1374
+
1375
+ // src/agent-installation/managed-agent-archive.ts
1376
+ import { createHash as createHash2 } from "node:crypto";
1377
+ import { createReadStream } from "node:fs";
1378
+ import {
1379
+ chmod,
1380
+ copyFile,
1381
+ mkdir as mkdir2,
1382
+ mkdtemp as mkdtemp2,
1383
+ open as open4,
1384
+ realpath as realpath4,
1385
+ rename as rename2,
1386
+ rm as rm2,
1387
+ stat as stat4,
1388
+ writeFile
1389
+ } from "node:fs/promises";
1390
+ import { dirname as dirname2, isAbsolute as isAbsolute3, join as join7, relative as relative2, sep as sep2 } from "node:path";
1391
+
1392
+ // src/agent-installation/verified-download.ts
1393
+ import { createHash } from "node:crypto";
1394
+ import { open as open3, unlink } from "node:fs/promises";
1395
+ import { fetch } from "undici";
1396
+
1397
+ // src/network/environment-proxy.ts
1398
+ import { EnvHttpProxyAgent } from "undici";
1399
+ var environmentProxyDispatcher = new EnvHttpProxyAgent();
1400
+
1401
+ // src/agent-installation/verified-download.ts
1402
+ var MAX_REDIRECTS = 4;
1403
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
1404
+ var InstallerDownloadError = class extends Error {
1405
+ code;
1406
+ constructor(code, message, options = {}) {
1407
+ super(message, options);
1408
+ this.code = code;
1409
+ this.name = "InstallerDownloadError";
585
1410
  }
586
- if (definition instanceof SriManagedNpmPackageDefinition) {
587
- verifyPackageLock(
588
- await readJsonRecord(
589
- join3(rootPath, "package-lock.json"),
590
- PACKAGE_LOCK_LIMIT_BYTES
591
- ),
592
- definition
593
- );
1411
+ };
1412
+ var VerifiedDownload = class {
1413
+ bytes;
1414
+ sha256;
1415
+ url;
1416
+ constructor(url2, bytes, sha256) {
1417
+ this.bytes = bytes;
1418
+ this.sha256 = sha256;
1419
+ this.url = url2;
594
1420
  }
595
- const declaredBin = readBinPath(metadata, definition.executableName);
596
- const executable = await resolveExecutablePath(
597
- join3(packageRoot, declaredBin)
598
- );
599
- if (executable === null || !isContainedPath(rootPath, executable.path)) {
600
- throw new ManagedNpmPackageError(
601
- "managed_package_verification_failed",
602
- "The managed npm package executable is invalid"
1421
+ };
1422
+ var UndiciInstallerDownloadTransport = class {
1423
+ get = async (url2, signal) => await fetch(url2, {
1424
+ dispatcher: environmentProxyDispatcher,
1425
+ headers: {
1426
+ accept: "text/plain, application/octet-stream;q=0.9",
1427
+ "user-agent": "Mobius-Agent-Installer/0.0.3"
1428
+ },
1429
+ method: "GET",
1430
+ redirect: "manual",
1431
+ signal
1432
+ });
1433
+ };
1434
+ function validateUrl(url2, allowedHosts) {
1435
+ if (url2.protocol !== "https:" || url2.username.length > 0 || url2.password.length > 0 || url2.port.length > 0 || !allowedHosts.has(url2.hostname)) {
1436
+ throw new InstallerDownloadError(
1437
+ "installer_host_not_allowed",
1438
+ "The installer download target is not allowed"
603
1439
  );
604
1440
  }
605
- return executable;
606
1441
  }
607
- var ManagedNpmPackageInstaller = class {
608
- install = async (root, definition, environment, signal) => {
609
- signal.throwIfAborted();
1442
+ async function fetchInstaller(options, signal, transport) {
1443
+ let url2 = new URL(options.url);
1444
+ for (let redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount += 1) {
1445
+ validateUrl(url2, options.allowedHosts);
1446
+ const response = await transport.get(url2, signal);
1447
+ if (!REDIRECT_STATUSES.has(response.status)) {
1448
+ return { response, url: url2 };
1449
+ }
1450
+ await response.body?.cancel();
1451
+ const location = response.headers.get("location");
1452
+ if (location === null || redirectCount === MAX_REDIRECTS) {
1453
+ throw new InstallerDownloadError(
1454
+ "installer_redirect_invalid",
1455
+ "The installer returned an invalid redirect"
1456
+ );
1457
+ }
1458
+ let redirected;
610
1459
  try {
611
- const metadata = await stat2(root);
612
- if (metadata.isDirectory()) {
613
- return await validateManagedNpmPackage(root, definition);
614
- }
1460
+ redirected = new URL(location, url2);
615
1461
  } catch (error61) {
616
- if (!(error61 instanceof Error) || !("code" in error61) || error61.code !== "ENOENT") {
617
- throw error61;
618
- }
1462
+ throw new InstallerDownloadError(
1463
+ "installer_redirect_invalid",
1464
+ "The installer returned an invalid redirect",
1465
+ { cause: error61 }
1466
+ );
619
1467
  }
620
- const parent = dirname(root);
621
- await mkdir(parent, { mode: 448, recursive: true });
622
- const staging = await mkdtemp(
623
- join3(parent, `.${definition.version}.staging-`)
624
- );
625
1468
  try {
626
- const npm = await findExecutableOnPath(
627
- process.platform === "win32" ? "npm.cmd" : "npm",
628
- environment
1469
+ validateUrl(redirected, options.allowedHosts);
1470
+ } catch (error61) {
1471
+ throw new InstallerDownloadError(
1472
+ "installer_redirect_not_allowed",
1473
+ "The installer redirect left the approved host allowlist",
1474
+ { cause: error61 }
629
1475
  );
630
- if (npm === null) {
631
- throw new ManagedNpmPackageError(
632
- "package_manager_not_found",
633
- "A supported npm executable is required to install the managed package"
1476
+ }
1477
+ url2 = redirected;
1478
+ }
1479
+ throw new InstallerDownloadError(
1480
+ "installer_redirect_invalid",
1481
+ "The installer returned too many redirects"
1482
+ );
1483
+ }
1484
+ async function writeCompleteChunk(handle, chunk) {
1485
+ let offset = 0;
1486
+ while (offset < chunk.byteLength) {
1487
+ const written = await handle.write(
1488
+ chunk,
1489
+ offset,
1490
+ chunk.byteLength - offset
1491
+ );
1492
+ if (written.bytesWritten < 1) {
1493
+ throw new InstallerDownloadError(
1494
+ "installer_write_failed",
1495
+ "The verified installer could not be stored"
1496
+ );
1497
+ }
1498
+ offset += written.bytesWritten;
1499
+ }
1500
+ }
1501
+ async function downloadVerifiedFile(options, transport = new UndiciInstallerDownloadTransport()) {
1502
+ options.signal.throwIfAborted();
1503
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMilliseconds);
1504
+ const signal = AbortSignal.any([options.signal, timeoutSignal]);
1505
+ let handle = null;
1506
+ let responseBody = null;
1507
+ try {
1508
+ const fetched = await fetchInstaller(options, signal, transport);
1509
+ responseBody = fetched.response.body;
1510
+ if (!fetched.response.ok || fetched.response.body === null) {
1511
+ throw new InstallerDownloadError(
1512
+ "installer_download_failed",
1513
+ "The approved installer could not be downloaded"
1514
+ );
1515
+ }
1516
+ const contentLength = fetched.response.headers.get("content-length");
1517
+ if (contentLength !== null) {
1518
+ const declaredLength = Number(contentLength);
1519
+ if (!Number.isSafeInteger(declaredLength) || declaredLength < 1 || declaredLength > options.maximumBytes) {
1520
+ throw new InstallerDownloadError(
1521
+ "installer_size_invalid",
1522
+ "The installer size is outside the approved limit"
634
1523
  );
635
1524
  }
636
- await runBoundedProcess({
637
- arguments: [
638
- "install",
639
- "--prefix",
640
- staging,
641
- "--omit=optional",
642
- "--ignore-scripts",
643
- "--no-audit",
644
- "--no-fund",
645
- "--package-lock=true",
646
- "--save-exact",
647
- "--registry=https://registry.npmjs.org/",
648
- `${definition.packageName}@${definition.version}`
649
- ],
650
- command: npm.path,
651
- cwd: staging,
652
- environment: {
653
- HOME: join3(staging, "home"),
654
- NPM_CONFIG_AUDIT: "false",
655
- NPM_CONFIG_CACHE: join3(staging, ".npm-cache"),
656
- NPM_CONFIG_FUND: "false",
657
- NPM_CONFIG_IGNORE_SCRIPTS: "true",
658
- NPM_CONFIG_OMIT: "optional",
659
- NPM_CONFIG_PACKAGE_LOCK: "true",
660
- NPM_CONFIG_REGISTRY: "https://registry.npmjs.org/",
661
- PATH: `${dirname(process.execPath)}${delimiter}/usr/bin${delimiter}/bin`
662
- },
663
- outputLimitBytes: 64 * 1024,
664
- signal,
665
- timeoutMilliseconds: INSTALL_TIMEOUT_MILLISECONDS
666
- });
667
- await Promise.all([
668
- rm(join3(staging, ".npm-cache"), { force: true, recursive: true }),
669
- rm(join3(staging, "home"), { force: true, recursive: true })
670
- ]);
671
- await validateManagedNpmPackage(staging, definition);
672
- try {
673
- await rename(staging, root);
674
- } catch (error61) {
675
- try {
676
- return await validateManagedNpmPackage(root, definition);
677
- } catch {
678
- throw new ManagedNpmPackageError(
679
- "managed_package_publish_failed",
680
- "The managed npm package could not be published",
681
- { cause: error61 }
1525
+ }
1526
+ handle = await open3(options.destination, "wx", 384);
1527
+ const digest = createHash("sha256");
1528
+ const reader = fetched.response.body.getReader();
1529
+ let bytes = 0;
1530
+ try {
1531
+ while (true) {
1532
+ signal.throwIfAborted();
1533
+ const chunk = await reader.read();
1534
+ if (chunk.done) {
1535
+ break;
1536
+ }
1537
+ bytes += chunk.value.byteLength;
1538
+ if (bytes > options.maximumBytes) {
1539
+ throw new InstallerDownloadError(
1540
+ "installer_size_invalid",
1541
+ "The installer exceeded the approved size limit"
682
1542
  );
683
1543
  }
1544
+ digest.update(chunk.value);
1545
+ await writeCompleteChunk(handle, chunk.value);
684
1546
  }
685
- return await validateManagedNpmPackage(root, definition);
686
- } catch (error61) {
687
- if (signal.aborted) {
688
- signal.throwIfAborted();
689
- }
690
- if (error61 instanceof ManagedNpmPackageError) {
691
- throw error61;
692
- }
693
- throw new ManagedNpmPackageError(
694
- "managed_package_install_failed",
695
- "The pinned npm package could not be installed",
1547
+ } finally {
1548
+ await reader.cancel().catch(() => void 0);
1549
+ reader.releaseLock();
1550
+ }
1551
+ if (bytes === 0) {
1552
+ throw new InstallerDownloadError(
1553
+ "installer_verification_failed",
1554
+ "The downloaded installer is empty"
1555
+ );
1556
+ }
1557
+ await handle.sync();
1558
+ await handle.close();
1559
+ handle = null;
1560
+ return new VerifiedDownload(fetched.url, bytes, digest.digest("hex"));
1561
+ } catch (error61) {
1562
+ if (handle !== null) {
1563
+ await handle.close().catch(() => void 0);
1564
+ }
1565
+ await unlink(options.destination).catch(() => void 0);
1566
+ if (options.signal.aborted) {
1567
+ options.signal.throwIfAborted();
1568
+ }
1569
+ if (timeoutSignal.aborted) {
1570
+ throw new InstallerDownloadError(
1571
+ "installer_download_timeout",
1572
+ "The installer download timed out",
696
1573
  { cause: error61 }
697
1574
  );
698
- } finally {
699
- await rm(staging, { force: true, recursive: true });
700
1575
  }
701
- };
702
- };
1576
+ if (error61 instanceof InstallerDownloadError) {
1577
+ throw error61;
1578
+ }
1579
+ throw new InstallerDownloadError(
1580
+ "installer_download_failed",
1581
+ "The approved installer could not be downloaded",
1582
+ { cause: error61 }
1583
+ );
1584
+ } finally {
1585
+ await responseBody?.cancel().catch(() => void 0);
1586
+ }
1587
+ }
703
1588
 
704
- // src/agent-installation/managed-adapter-installer.ts
705
- var ManagedAdapterInstallError = class extends Error {
1589
+ // src/agent-installation/managed-agent-archive.ts
1590
+ var ARCHIVE_DOWNLOAD_LIMIT_BYTES = 128 * 1024 * 1024;
1591
+ var ARCHIVE_DOWNLOAD_TIMEOUT_MILLISECONDS = 2 * 60 * 1e3;
1592
+ var ARCHIVE_EXTRACTION_TIMEOUT_MILLISECONDS = 6e4;
1593
+ var MANIFEST_LIMIT_BYTES = 16 * 1024;
1594
+ var MINIMAL_PATH = "/usr/bin:/bin";
1595
+ var ManagedAgentArchiveDefinition = class {
1596
+ archiveName;
1597
+ archiveSha256;
1598
+ archiveUrl;
1599
+ executableName;
1600
+ version;
1601
+ constructor(version2, archiveName, archiveUrl, archiveSha256, executableName) {
1602
+ this.archiveName = archiveName;
1603
+ this.archiveSha256 = archiveSha256;
1604
+ this.archiveUrl = new URL(archiveUrl);
1605
+ this.executableName = executableName;
1606
+ this.version = version2;
1607
+ }
1608
+ };
1609
+ var ZipManagedAgentArchiveDefinition = class extends ManagedAgentArchiveDefinition {
1610
+ extractor = "/usr/bin/unzip";
1611
+ extractArguments = (archivePath, destination) => ["-q", archivePath, "-d", destination];
1612
+ listArguments = (archivePath) => [
1613
+ "-Z1",
1614
+ archivePath
1615
+ ];
1616
+ };
1617
+ var TarGzipManagedAgentArchiveDefinition = class extends ManagedAgentArchiveDefinition {
1618
+ extractor = "/usr/bin/tar";
1619
+ extractArguments = (archivePath, destination) => ["-xzf", archivePath, "-C", destination];
1620
+ listArguments = (archivePath) => [
1621
+ "-tzf",
1622
+ archivePath
1623
+ ];
1624
+ };
1625
+ var ManagedAgentArchiveError = class extends Error {
706
1626
  code;
707
1627
  constructor(code, message, options = {}) {
708
1628
  super(message, options);
709
1629
  this.code = code;
710
- this.name = "ManagedAdapterInstallError";
1630
+ this.name = "ManagedAgentArchiveError";
711
1631
  }
712
1632
  };
713
- function translateError(error61) {
714
- if (error61 instanceof ManagedAdapterInstallError) {
715
- return error61;
716
- }
717
- if (error61 instanceof ManagedNpmPackageError) {
718
- if (error61.code === "package_manager_not_found") {
719
- return new ManagedAdapterInstallError(
720
- error61.code,
721
- "A supported npm executable is required to install the ACP adapter",
722
- { cause: error61 }
723
- );
1633
+ var ManagedAgentArchiveDownloader = class {
1634
+ download = async (options) => await downloadVerifiedFile(options);
1635
+ };
1636
+ function isRecord3(value) {
1637
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1638
+ }
1639
+ function isContainedPath2(root, candidate) {
1640
+ const difference = relative2(root, candidate);
1641
+ return difference.length > 0 && difference !== ".." && !difference.startsWith(`..${sep2}`) && !isAbsolute3(difference);
1642
+ }
1643
+ async function hashFile(path2, signal) {
1644
+ signal.throwIfAborted();
1645
+ const digest = createHash2("sha256");
1646
+ const stream = createReadStream(path2, { signal });
1647
+ try {
1648
+ for await (const chunk of stream) {
1649
+ signal.throwIfAborted();
1650
+ if (!(chunk instanceof Uint8Array)) {
1651
+ throw new ManagedAgentArchiveError(
1652
+ "managed_archive_verification_failed",
1653
+ "The managed Agent archive produced invalid bytes"
1654
+ );
1655
+ }
1656
+ digest.update(chunk);
724
1657
  }
725
- if (error61.code === "managed_package_verification_failed") {
726
- return new ManagedAdapterInstallError(
727
- "adapter_verification_failed",
728
- "The managed adapter did not match its pinned artifact",
729
- { cause: error61 }
730
- );
1658
+ return digest.digest("hex");
1659
+ } catch (error61) {
1660
+ if (signal.aborted) {
1661
+ signal.throwIfAborted();
731
1662
  }
732
- if (error61.code === "managed_package_publish_failed") {
733
- return new ManagedAdapterInstallError(
734
- "adapter_publish_failed",
735
- "The managed adapter could not be published",
736
- { cause: error61 }
737
- );
1663
+ if (error61 instanceof ManagedAgentArchiveError) {
1664
+ throw error61;
738
1665
  }
1666
+ throw new ManagedAgentArchiveError(
1667
+ "managed_archive_verification_failed",
1668
+ "The managed Agent artifact could not be verified",
1669
+ { cause: error61 }
1670
+ );
739
1671
  }
740
- return new ManagedAdapterInstallError(
741
- "adapter_install_failed",
742
- "The pinned ACP adapter could not be installed",
743
- { cause: error61 }
744
- );
745
1672
  }
746
- async function validateManagedAdapter(root, specification) {
1673
+ async function readManifest(path2) {
1674
+ let handle;
747
1675
  try {
748
- return await validateManagedNpmPackage(
749
- root,
750
- specification.adapterPackageDefinition
751
- );
1676
+ handle = await open4(path2, "r");
752
1677
  } catch (error61) {
753
- throw translateError(error61);
754
- }
755
- }
756
- var ManagedAdapterInstaller = class {
757
- #environment;
758
- #npmInstaller;
759
- #stateDirectory;
760
- constructor(options, npmInstaller = new ManagedNpmPackageInstaller()) {
761
- this.#environment = options.environment;
762
- this.#npmInstaller = npmInstaller;
763
- this.#stateDirectory = options.stateDirectory;
1678
+ throw new ManagedAgentArchiveError(
1679
+ "managed_archive_verification_failed",
1680
+ "The managed Agent archive manifest is invalid",
1681
+ { cause: error61 }
1682
+ );
764
1683
  }
765
- install = async (specification, signal) => {
766
- const root = join4(
767
- this.#stateDirectory,
768
- "adapters",
769
- specification.id,
770
- specification.adapterVersion
1684
+ try {
1685
+ const metadata = await handle.stat();
1686
+ if (!metadata.isFile() || metadata.size > MANIFEST_LIMIT_BYTES) {
1687
+ throw new ManagedAgentArchiveError(
1688
+ "managed_archive_verification_failed",
1689
+ "The managed Agent archive manifest is invalid"
1690
+ );
1691
+ }
1692
+ const parsed = JSON.parse(
1693
+ await handle.readFile({ encoding: "utf8" })
771
1694
  );
772
- try {
773
- return await this.#npmInstaller.install(
774
- root,
775
- specification.adapterPackageDefinition,
776
- this.#environment,
777
- signal
1695
+ if (!isRecord3(parsed)) {
1696
+ throw new ManagedAgentArchiveError(
1697
+ "managed_archive_verification_failed",
1698
+ "The managed Agent archive manifest is invalid"
778
1699
  );
779
- } catch (error61) {
780
- if (signal.aborted) {
781
- signal.throwIfAborted();
782
- }
783
- throw translateError(error61);
784
1700
  }
785
- };
786
- };
787
-
788
- // src/agent-installation/pi-managed-packages.ts
789
- var PI_ACP_PACKAGE = new SriManagedNpmPackageDefinition(
790
- "pi-acp",
791
- "0.0.33",
792
- "pi-acp",
793
- "sha512-vX9kY1tK14E72G4dBAx+RGCk/k7XPjTHls6dLUxA8WSkBav6B6JHuSBv3eusp50LCR/GTRsR2kIKsG0Z5jANzw==",
794
- "https://registry.npmjs.org/pi-acp/-/pi-acp-0.0.33.tgz"
795
- );
796
- var PI_AGENT_PACKAGE = new SriManagedNpmPackageDefinition(
797
- "@earendil-works/pi-coding-agent",
798
- "0.85.0",
799
- "pi",
800
- "sha512-INxVkLAVfAMju5MojJpmyu/0bMP+r+ffZuS7UqVv32E2JwHBRbcHfELDfmFNvapEbgYfKN2r9OYO1p3TqDBR+g==",
801
- "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.85.0.tgz"
802
- );
803
-
804
- // src/storage/state-directory.ts
805
- import { homedir } from "node:os";
806
- import { join as join5, resolve as resolve2 } from "node:path";
807
- function readStateDirectory(environment = process.env) {
808
- const configured = environment["MOBIUS_STATE_DIRECTORY"]?.trim();
809
- if (configured !== void 0 && configured.length > 0) {
810
- return resolve2(configured);
1701
+ return parsed;
1702
+ } catch (error61) {
1703
+ if (error61 instanceof ManagedAgentArchiveError) {
1704
+ throw error61;
1705
+ }
1706
+ throw new ManagedAgentArchiveError(
1707
+ "managed_archive_verification_failed",
1708
+ "The managed Agent archive manifest is invalid",
1709
+ { cause: error61 }
1710
+ );
1711
+ } finally {
1712
+ await handle.close();
811
1713
  }
812
- return join5(homedir(), ".mobius");
813
- }
814
-
815
- // src/agent-catalog/local-agent.ts
816
- import { realpathSync, statSync } from "node:fs";
817
- function readTrustedIdentity(path2) {
818
- const resolvedPath = realpathSync(path2);
819
- const metadata = statSync(resolvedPath);
820
- return new ExecutableIdentity(
821
- resolvedPath,
822
- resolvedPath,
823
- metadata.dev,
824
- metadata.ino,
825
- metadata.size,
826
- metadata.ctimeMs
827
- );
828
1714
  }
829
- var LocalAgentDefinition = class {
830
- adapterIdentity;
831
- adapterPath;
832
- adapterVersion;
833
- agentIdentity;
834
- agentVersion;
835
- arguments;
836
- command;
837
- environment;
838
- id;
839
- label;
840
- promptTimeoutMilliseconds;
841
- constructor(id, label, command, arguments_, environment = {}, adapterVersion = "", agentVersion = "", adapterIdentity = readTrustedIdentity(command), agentIdentity = adapterIdentity, promptTimeoutMilliseconds = 30 * 60 * 1e3) {
842
- if (!Number.isSafeInteger(promptTimeoutMilliseconds) || promptTimeoutMilliseconds < 1) {
843
- throw new Error("The local Agent prompt deadline is invalid");
1715
+ async function validateManagedAgentArchive(root, definition, signal) {
1716
+ try {
1717
+ const rootPath = await realpath4(root);
1718
+ const rootMetadata = await stat4(rootPath);
1719
+ if (!rootMetadata.isDirectory() || typeof process.getuid === "function" && rootMetadata.uid !== process.getuid()) {
1720
+ throw new Error("Managed Agent root is not user-owned");
844
1721
  }
845
- this.adapterIdentity = adapterIdentity;
846
- this.adapterPath = adapterIdentity.realPath;
847
- this.adapterVersion = adapterVersion;
848
- this.agentIdentity = agentIdentity;
849
- this.agentVersion = agentVersion;
850
- this.id = id;
851
- this.label = label;
852
- this.promptTimeoutMilliseconds = promptTimeoutMilliseconds;
853
- this.command = adapterIdentity.realPath;
854
- this.arguments = Object.freeze([...arguments_]);
855
- this.environment = Object.freeze({ ...environment });
856
- }
857
- };
858
- var InstallableAgentDefinition = class {
859
- id;
860
- label;
861
- requiresLocalApproval;
862
- constructor(id, label, requiresLocalApproval = true) {
863
- this.id = id;
864
- this.label = label;
865
- this.requiresLocalApproval = requiresLocalApproval;
866
- }
867
- };
868
- var LocalAgentCatalogSnapshot = class {
869
- agents;
870
- installableAgents;
871
- constructor(agents, installableAgents) {
872
- this.agents = Object.freeze([...agents]);
873
- this.installableAgents = Object.freeze([...installableAgents]);
1722
+ const manifest = await readManifest(join7(rootPath, "archive.json"));
1723
+ if (manifest["archiveSha256"] !== definition.archiveSha256 || manifest["archiveUrl"] !== definition.archiveUrl.toString() || manifest["version"] !== definition.version || typeof manifest["executableSha256"] !== "string") {
1724
+ throw new Error("Managed Agent archive identity is invalid");
1725
+ }
1726
+ const executable = await resolveExecutablePath(
1727
+ join7(rootPath, "bin", definition.executableName)
1728
+ );
1729
+ if (executable === null || !isContainedPath2(rootPath, executable.path) || await hashFile(executable.path, signal) !== manifest["executableSha256"]) {
1730
+ throw new Error("Managed Agent executable is invalid");
1731
+ }
1732
+ return executable;
1733
+ } catch (error61) {
1734
+ if (signal.aborted) {
1735
+ signal.throwIfAborted();
1736
+ }
1737
+ if (error61 instanceof ManagedAgentArchiveError) {
1738
+ throw error61;
1739
+ }
1740
+ throw new ManagedAgentArchiveError(
1741
+ "managed_archive_verification_failed",
1742
+ "The managed Agent archive did not match its pinned artifact",
1743
+ { cause: error61 }
1744
+ );
874
1745
  }
875
- };
876
- var LocalAgentCatalogError = class extends Error {
877
- code = "agent_not_ready";
878
- constructor() {
879
- super("The requested local Agent is not ready");
880
- this.name = "LocalAgentCatalogError";
1746
+ }
1747
+ function validateArchiveEntries(output2, definition) {
1748
+ const entries = output2.split(/\r?\n/).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
1749
+ if (entries.length !== 1 || entries[0] !== definition.executableName && entries[0] !== `./${definition.executableName}`) {
1750
+ throw new ManagedAgentArchiveError(
1751
+ "managed_archive_verification_failed",
1752
+ "The managed Agent archive contains unexpected entries"
1753
+ );
881
1754
  }
882
- };
883
- var LocalAgentCatalog = class {
884
- #snapshot;
885
- constructor(agents, installableAgents) {
886
- this.#snapshot = this.validate(agents, installableAgents);
1755
+ }
1756
+ var ManagedAgentArchiveInstaller = class {
1757
+ #downloader;
1758
+ constructor(downloader = new ManagedAgentArchiveDownloader()) {
1759
+ this.#downloader = downloader;
887
1760
  }
888
- find = (agentId) => {
889
- for (const agent of this.#snapshot.agents) {
890
- if (agent.id === agentId) {
891
- return agent;
1761
+ install = async (root, definition, signal) => {
1762
+ signal.throwIfAborted();
1763
+ try {
1764
+ const metadata = await stat4(root);
1765
+ if (metadata.isDirectory()) {
1766
+ return await validateManagedAgentArchive(root, definition, signal);
892
1767
  }
893
- }
894
- throw new LocalAgentCatalogError();
895
- };
896
- read = () => this.#snapshot;
897
- transition = (agents, installableAgents) => {
898
- const next = this.validate(agents, installableAgents);
899
- this.#snapshot = next;
900
- return next;
901
- };
902
- validate = (agents, installableAgents) => {
903
- const identifiers = /* @__PURE__ */ new Set();
904
- for (const agent of agents) {
905
- if (identifiers.has(agent.id)) {
906
- throw new Error("The local Agent catalog contains duplicate entries");
1768
+ } catch (error61) {
1769
+ if (error61 instanceof ManagedAgentArchiveError || !(error61 instanceof Error) || !("code" in error61) || error61.code !== "ENOENT") {
1770
+ throw error61;
907
1771
  }
908
- identifiers.add(agent.id);
909
1772
  }
910
- for (const agent of installableAgents) {
911
- if (identifiers.has(agent.id)) {
912
- throw new Error("A local Agent cannot be ready and installable");
1773
+ const parent = dirname2(root);
1774
+ await mkdir2(parent, { mode: 448, recursive: true });
1775
+ const working = await mkdtemp2(
1776
+ join7(parent, `.${definition.version}.archive-`)
1777
+ );
1778
+ const staging = await mkdtemp2(
1779
+ join7(parent, `.${definition.version}.staging-`)
1780
+ );
1781
+ try {
1782
+ const archivePath = join7(working, definition.archiveName);
1783
+ const extractionRoot = join7(working, "extracted");
1784
+ await mkdir2(extractionRoot, { mode: 448 });
1785
+ const canonicalExtractionRoot = await realpath4(extractionRoot);
1786
+ const download = await this.#downloader.download({
1787
+ allowedHosts: /* @__PURE__ */ new Set([
1788
+ "github.com",
1789
+ "objects.githubusercontent.com",
1790
+ "release-assets.githubusercontent.com"
1791
+ ]),
1792
+ destination: archivePath,
1793
+ maximumBytes: ARCHIVE_DOWNLOAD_LIMIT_BYTES,
1794
+ signal,
1795
+ timeoutMilliseconds: ARCHIVE_DOWNLOAD_TIMEOUT_MILLISECONDS,
1796
+ url: definition.archiveUrl
1797
+ });
1798
+ if (download.sha256 !== definition.archiveSha256) {
1799
+ throw new ManagedAgentArchiveError(
1800
+ "managed_archive_verification_failed",
1801
+ "The managed Agent archive checksum is invalid"
1802
+ );
1803
+ }
1804
+ const list = await runBoundedProcess({
1805
+ arguments: definition.listArguments(archivePath),
1806
+ command: definition.extractor,
1807
+ cwd: working,
1808
+ environment: { PATH: MINIMAL_PATH },
1809
+ outputLimitBytes: 64 * 1024,
1810
+ signal,
1811
+ timeoutMilliseconds: ARCHIVE_EXTRACTION_TIMEOUT_MILLISECONDS
1812
+ });
1813
+ validateArchiveEntries(list.stdout, definition);
1814
+ await runBoundedProcess({
1815
+ arguments: definition.extractArguments(archivePath, extractionRoot),
1816
+ command: definition.extractor,
1817
+ cwd: working,
1818
+ environment: { PATH: MINIMAL_PATH },
1819
+ outputLimitBytes: 64 * 1024,
1820
+ signal,
1821
+ timeoutMilliseconds: ARCHIVE_EXTRACTION_TIMEOUT_MILLISECONDS
1822
+ });
1823
+ const extracted = await realpath4(
1824
+ join7(canonicalExtractionRoot, definition.executableName)
1825
+ );
1826
+ const extractedMetadata = await stat4(extracted);
1827
+ if (!isContainedPath2(canonicalExtractionRoot, extracted) || !extractedMetadata.isFile()) {
1828
+ throw new ManagedAgentArchiveError(
1829
+ "managed_archive_verification_failed",
1830
+ "The managed Agent archive executable is invalid"
1831
+ );
1832
+ }
1833
+ const binDirectory = join7(staging, "bin");
1834
+ const executablePath = join7(binDirectory, definition.executableName);
1835
+ await mkdir2(binDirectory, { mode: 448 });
1836
+ await copyFile(extracted, executablePath);
1837
+ await chmod(executablePath, 448);
1838
+ const executableSha256 = await hashFile(executablePath, signal);
1839
+ await writeFile(
1840
+ join7(staging, "archive.json"),
1841
+ `${JSON.stringify({
1842
+ archiveSha256: definition.archiveSha256,
1843
+ archiveUrl: definition.archiveUrl.toString(),
1844
+ executableSha256,
1845
+ version: definition.version
1846
+ })}
1847
+ `,
1848
+ { mode: 384 }
1849
+ );
1850
+ await validateManagedAgentArchive(staging, definition, signal);
1851
+ try {
1852
+ await rename2(staging, root);
1853
+ } catch (error61) {
1854
+ try {
1855
+ return await validateManagedAgentArchive(root, definition, signal);
1856
+ } catch {
1857
+ throw new ManagedAgentArchiveError(
1858
+ "managed_archive_publish_failed",
1859
+ "The managed Agent archive could not be published",
1860
+ { cause: error61 }
1861
+ );
1862
+ }
1863
+ }
1864
+ return await validateManagedAgentArchive(root, definition, signal);
1865
+ } catch (error61) {
1866
+ if (signal.aborted) {
1867
+ signal.throwIfAborted();
913
1868
  }
914
- identifiers.add(agent.id);
1869
+ if (error61 instanceof ManagedAgentArchiveError) {
1870
+ throw error61;
1871
+ }
1872
+ throw new ManagedAgentArchiveError(
1873
+ "managed_archive_install_failed",
1874
+ "The pinned Agent archive could not be installed",
1875
+ { cause: error61 }
1876
+ );
1877
+ } finally {
1878
+ await Promise.all([
1879
+ rm2(working, { force: true, recursive: true }),
1880
+ rm2(staging, { force: true, recursive: true })
1881
+ ]);
915
1882
  }
916
- return new LocalAgentCatalogSnapshot(agents, installableAgents);
917
1883
  };
918
1884
  };
919
1885
 
920
- // src/agent-catalog/discover-local-agents.ts
921
- var VERSION_PROBE_TIMEOUT_MILLISECONDS = 3e3;
922
- var ACP_PROBE_TIMEOUT_MILLISECONDS = 5e3;
923
- var ACP_OUTPUT_LIMIT_BYTES = 1024 * 1024;
924
- var DEFAULT_PROMPT_TIMEOUT_MILLISECONDS = 30 * 60 * 1e3;
925
- var PI_ACP_PROBE_TIMEOUT_MILLISECONDS = 3e4;
926
- var PI_PROMPT_TIMEOUT_MILLISECONDS = 5 * 60 * 1e3;
927
- var LocalAgentSpecification = class {
928
- acpProbeTimeoutMilliseconds = ACP_PROBE_TIMEOUT_MILLISECONDS;
929
- authenticationCommand = () => "";
930
- adapterVersionArguments = ["--version"];
931
- agentVersionArguments = ["--version"];
932
- promptTimeoutMilliseconds = DEFAULT_PROMPT_TIMEOUT_MILLISECONDS;
933
- get adapterExecutable() {
934
- return this.adapterPackageDefinition.executableName;
1886
+ // src/agent-installation/opencode-managed-release.ts
1887
+ var OPENCODE_MANAGED_VERSION = "1.18.29";
1888
+ function releaseUrl(asset) {
1889
+ return new URL(
1890
+ `https://github.com/anomalyco/opencode/releases/download/v${OPENCODE_MANAGED_VERSION}/${asset}`
1891
+ );
1892
+ }
1893
+ function openCodeManagedArchive(platform, architecture) {
1894
+ if (platform === "darwin" && architecture === "arm64") {
1895
+ const asset = "opencode-darwin-arm64.zip";
1896
+ return new ZipManagedAgentArchiveDefinition(
1897
+ OPENCODE_MANAGED_VERSION,
1898
+ asset,
1899
+ releaseUrl(asset),
1900
+ "fe764f7f360c584a83e18dd5f23fb1a6b2725f5ee8854b0252fe558f7798e946",
1901
+ "opencode"
1902
+ );
935
1903
  }
936
- get adapterPackage() {
937
- return this.adapterPackageDefinition.packageName;
1904
+ if (platform === "darwin" && architecture === "x64") {
1905
+ const asset = "opencode-darwin-x64.zip";
1906
+ return new ZipManagedAgentArchiveDefinition(
1907
+ OPENCODE_MANAGED_VERSION,
1908
+ asset,
1909
+ releaseUrl(asset),
1910
+ "9858853e7bacdbbd22c2d70c377e009dc4b354dd04f5588705411e7afb89fd2d",
1911
+ "opencode"
1912
+ );
938
1913
  }
939
- get adapterVersion() {
940
- return this.adapterPackageDefinition.version;
1914
+ if (platform === "linux" && architecture === "arm64") {
1915
+ const asset = "opencode-linux-arm64.tar.gz";
1916
+ return new TarGzipManagedAgentArchiveDefinition(
1917
+ OPENCODE_MANAGED_VERSION,
1918
+ asset,
1919
+ releaseUrl(asset),
1920
+ "70baf769395ca4e7a68924026530c390eace194f3b7e4919d4efcb2aa2eed3c0",
1921
+ "opencode"
1922
+ );
941
1923
  }
942
- resolveManagedAgent = async (stateDirectory, platform) => {
943
- const executable = await resolveExecutablePath(
944
- join6(stateDirectory, ...this.managedAgentExecutablePath),
945
- platform
1924
+ if (platform === "linux" && architecture === "x64") {
1925
+ const asset = "opencode-linux-x64.tar.gz";
1926
+ return new TarGzipManagedAgentArchiveDefinition(
1927
+ OPENCODE_MANAGED_VERSION,
1928
+ asset,
1929
+ releaseUrl(asset),
1930
+ "ea800b7ff56226b70952126c9fc1e2517ca4c4b5682fd9d3f9e87449697a1194",
1931
+ "opencode"
946
1932
  );
947
- if (executable === null) {
948
- throw new Error("The managed Agent executable is unavailable");
949
- }
950
- return executable;
951
- };
952
- recoveryCommand = () => `npm install -g --omit=optional ${this.adapterPackage}@${this.adapterVersion}`;
953
- supportsInstallation = (platform, architecture) => (platform === "darwin" || platform === "linux") && (architecture === "arm64" || architecture === "x64");
954
- verifyAcp = (_connection, _initialized, _defaultWorkspace, _signal) => Promise.resolve();
955
- };
956
- var BridgedAcpAgentDefinition = class extends LocalAgentSpecification {
957
- };
958
- var CodexAgentSpecification = class extends BridgedAcpAgentDefinition {
959
- adapterEnvironmentVariable = "CODEX_PATH";
960
- adapterOverrideVariable = "MOBIUS_CODEX_ACP_PATH";
961
- adapterPackageDefinition = new ManagedNpmPackageDefinition(
962
- "@agentclientprotocol/codex-acp",
963
- "1.8.0",
964
- "codex-acp"
1933
+ }
1934
+ throw new Error(
1935
+ "OpenCode does not publish an approved archive for this platform"
965
1936
  );
966
- agentExecutable = "codex";
967
- agentOverrideVariable = "MOBIUS_CODEX_PATH";
968
- id = "codex";
969
- label = "Codex";
970
- managedAdapterExecutablePath = [
971
- "adapters",
972
- "codex",
973
- "1.8.0",
974
- "node_modules",
975
- "@agentclientprotocol",
976
- "codex-acp",
977
- "dist",
978
- "index.js"
979
- ];
980
- managedAgentExecutablePath = ["agents", "codex", "bin", "codex"];
981
- };
982
- var ClaudeCodeAgentSpecification = class extends BridgedAcpAgentDefinition {
983
- adapterEnvironmentVariable = "CLAUDE_CODE_EXECUTABLE";
984
- adapterOverrideVariable = "MOBIUS_CLAUDE_ACP_PATH";
985
- adapterPackageDefinition = new ManagedNpmPackageDefinition(
986
- "@agentclientprotocol/claude-agent-acp",
987
- "0.73.0",
988
- "claude-agent-acp"
1937
+ }
1938
+
1939
+ // src/agent-catalog/agents/opencode-agent.ts
1940
+ var OpenCodeAgentSpecification = class extends DirectAcpAgentDefinition {
1941
+ versionProbeTimeoutMilliseconds = 15e3;
1942
+ adapterVersion = OPENCODE_MANAGED_VERSION;
1943
+ authenticationCommand = () => "opencode auth login";
1944
+ agentExecutable = "opencode";
1945
+ id = "opencode";
1946
+ label = "OpenCode";
1947
+ constructor() {
1948
+ super(
1949
+ "MOBIUS_OPENCODE_PATH",
1950
+ ["acp"],
1951
+ [
1952
+ "agents",
1953
+ "opencode",
1954
+ OPENCODE_MANAGED_VERSION,
1955
+ "bin",
1956
+ process.platform === "win32" ? "opencode.exe" : "opencode"
1957
+ ]
1958
+ );
1959
+ }
1960
+ createLaunchEnvironment = async (_agent, _context) => Object.freeze({ OPENCODE_DISABLE_AUTOUPDATE: "1" });
1961
+ resolveManagedAgent = async (stateDirectory, platform, signal) => await validateManagedAgentArchive(
1962
+ join8(stateDirectory, "agents", this.id, OPENCODE_MANAGED_VERSION),
1963
+ openCodeManagedArchive(platform, process.arch),
1964
+ signal
989
1965
  );
990
- agentExecutable = "claude";
991
- agentOverrideVariable = "MOBIUS_CLAUDE_PATH";
992
- id = "claude";
993
- label = "Claude Code";
994
- managedAdapterExecutablePath = [
995
- "adapters",
996
- "claude",
997
- "0.73.0",
998
- "node_modules",
999
- "@agentclientprotocol",
1000
- "claude-agent-acp",
1001
- "dist",
1002
- "index.js"
1003
- ];
1004
- managedAgentExecutablePath = [
1005
- "agents",
1006
- "claude",
1007
- "home",
1008
- ".local",
1009
- "bin",
1010
- "claude"
1011
- ];
1966
+ verifyAcp = (_connection, initialized, _defaultWorkspace, _signal) => {
1967
+ const capabilities = initialized.agentCapabilities ?? {};
1968
+ const session = capabilities.sessionCapabilities;
1969
+ if (capabilities.loadSession !== true || session?.close == null || session.list == null || session.resume == null) {
1970
+ throw new AgentProbeError(
1971
+ "acp_incompatible",
1972
+ "OpenCode does not support the required ACP session lifecycle"
1973
+ );
1974
+ }
1975
+ return Promise.resolve();
1976
+ };
1012
1977
  };
1978
+
1979
+ // src/agent-catalog/agents/pi-agent.ts
1980
+ import { join as join9 } from "node:path";
1981
+ import {
1982
+ methods
1983
+ } from "@agentclientprotocol/sdk";
1984
+
1985
+ // src/agent-installation/pi-managed-packages.ts
1986
+ var PI_ACP_PACKAGE = new SriManagedNpmPackageDefinition(
1987
+ "pi-acp",
1988
+ "0.0.33",
1989
+ "pi-acp",
1990
+ "sha512-vX9kY1tK14E72G4dBAx+RGCk/k7XPjTHls6dLUxA8WSkBav6B6JHuSBv3eusp50LCR/GTRsR2kIKsG0Z5jANzw==",
1991
+ "https://registry.npmjs.org/pi-acp/-/pi-acp-0.0.33.tgz"
1992
+ );
1993
+ var PI_AGENT_PACKAGE = new SriManagedNpmPackageDefinition(
1994
+ "@earendil-works/pi-coding-agent",
1995
+ "0.85.0",
1996
+ "pi",
1997
+ "sha512-INxVkLAVfAMju5MojJpmyu/0bMP+r+ffZuS7UqVv32E2JwHBRbcHfELDfmFNvapEbgYfKN2r9OYO1p3TqDBR+g==",
1998
+ "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.85.0.tgz"
1999
+ );
2000
+
2001
+ // src/agent-catalog/agents/pi-agent.ts
2002
+ var ACP_PROBE_TIMEOUT_MILLISECONDS2 = 3e4;
2003
+ var PROMPT_TIMEOUT_MILLISECONDS = 5 * 60 * 1e3;
1013
2004
  var PiAgentSpecification = class extends BridgedAcpAgentDefinition {
1014
- acpProbeTimeoutMilliseconds = PI_ACP_PROBE_TIMEOUT_MILLISECONDS;
2005
+ acpProbeTimeoutMilliseconds = ACP_PROBE_TIMEOUT_MILLISECONDS2;
1015
2006
  adapterEnvironmentVariable = "PI_ACP_PI_COMMAND";
1016
2007
  adapterOverrideVariable = "MOBIUS_PI_ACP_PATH";
1017
2008
  adapterPackageDefinition = PI_ACP_PACKAGE;
@@ -1041,11 +2032,16 @@ var PiAgentSpecification = class extends BridgedAcpAgentDefinition {
1041
2032
  "bundle",
1042
2033
  "cli.js"
1043
2034
  ];
1044
- promptTimeoutMilliseconds = PI_PROMPT_TIMEOUT_MILLISECONDS;
1045
- resolveManagedAgent = async (stateDirectory, _platform) => await validateManagedNpmPackage(
1046
- join6(stateDirectory, "agents", this.id, PI_AGENT_PACKAGE.version),
1047
- PI_AGENT_PACKAGE
1048
- );
2035
+ promptTimeoutMilliseconds = PROMPT_TIMEOUT_MILLISECONDS;
2036
+ resolveManagedAgent = async (stateDirectory, _platform, signal) => {
2037
+ signal.throwIfAborted();
2038
+ const executable = await validateManagedNpmPackage(
2039
+ join9(stateDirectory, "agents", this.id, PI_AGENT_PACKAGE.version),
2040
+ PI_AGENT_PACKAGE
2041
+ );
2042
+ signal.throwIfAborted();
2043
+ return executable;
2044
+ };
1049
2045
  verifyAcp = async (connection, initialized, defaultWorkspace, signal) => {
1050
2046
  const capabilities = initialized.agentCapabilities ?? {};
1051
2047
  if (capabilities.loadSession !== true) {
@@ -1055,7 +2051,7 @@ var PiAgentSpecification = class extends BridgedAcpAgentDefinition {
1055
2051
  );
1056
2052
  }
1057
2053
  const session = await connection.agent.request(
1058
- methods2.agent.session.new,
2054
+ methods.agent.session.new,
1059
2055
  {
1060
2056
  additionalDirectories: [],
1061
2057
  cwd: defaultWorkspace,
@@ -1066,157 +2062,170 @@ var PiAgentSpecification = class extends BridgedAcpAgentDefinition {
1066
2062
  if (session.sessionId.length === 0) {
1067
2063
  throw new AgentProbeError(
1068
2064
  "acp_incompatible",
1069
- "Pi ACP adapter returned an invalid session"
1070
- );
1071
- }
1072
- const prompt = await connection.agent.request(
1073
- methods2.agent.session.prompt,
1074
- {
1075
- prompt: [{ text: "/session", type: "text" }],
1076
- sessionId: session.sessionId
1077
- },
1078
- { cancellationSignal: signal }
1079
- );
1080
- if (prompt.stopReason !== "end_turn") {
1081
- throw new AgentProbeError(
1082
- "acp_incompatible",
1083
- "Pi ACP adapter did not complete its conformance prompt"
1084
- );
1085
- }
1086
- };
1087
- };
1088
- function supportedLocalAgentSpecifications() {
1089
- return Object.freeze([
1090
- new CodexAgentSpecification(),
1091
- new ClaudeCodeAgentSpecification(),
1092
- new PiAgentSpecification()
1093
- ]);
1094
- }
1095
- var AgentDiscovery = class {
1096
- specification;
1097
- constructor(specification) {
1098
- this.specification = specification;
1099
- }
1100
- };
1101
- var AvailableAgent = class extends AgentDiscovery {
1102
- agent;
1103
- status = "ready";
1104
- constructor(specification, agent) {
1105
- super(specification);
1106
- this.agent = agent;
1107
- }
1108
- };
1109
- var UnavailableAgent = class extends AgentDiscovery {
1110
- };
1111
- var MissingAdapter = class extends UnavailableAgent {
1112
- code = "adapter_not_found";
1113
- message = "ACP adapter not found";
1114
- status = "missing_adapter";
1115
- constructor(specification) {
1116
- super(specification);
1117
- }
1118
- };
1119
- var MissingAgent = class extends UnavailableAgent {
1120
- code = "agent_not_found";
1121
- message = "Agent executable not found";
1122
- status = "missing_agent";
1123
- constructor(specification) {
1124
- super(specification);
1125
- }
1126
- };
1127
- var ProbeFailed = class extends UnavailableAgent {
1128
- code;
1129
- message;
1130
- status = "probe_failed";
1131
- constructor(specification, code, message) {
1132
- super(specification);
1133
- this.code = code;
1134
- this.message = message;
1135
- }
1136
- };
1137
- var IncompatibleAdapter = class extends UnavailableAgent {
1138
- code = "acp_incompatible";
1139
- message = "ACP adapter is incompatible";
1140
- status = "incompatible_adapter";
1141
- constructor(specification) {
1142
- super(specification);
1143
- }
1144
- };
1145
- var AuthenticationRequiredAgent = class extends UnavailableAgent {
1146
- code = "authentication_required";
1147
- message = "Agent authentication is required";
1148
- status = "authentication_required";
1149
- constructor(specification) {
1150
- super(specification);
1151
- }
1152
- };
1153
- var LocalAgentInventory = class {
1154
- available;
1155
- discoveries;
1156
- installable;
1157
- unavailable;
1158
- constructor(discoveries, grantedAgents = /* @__PURE__ */ new Set(), platform = process.platform, architecture = process.arch) {
1159
- const available = [];
1160
- const unavailable = [];
1161
- for (const discovery of discoveries) {
1162
- if (discovery instanceof AvailableAgent) {
1163
- available.push(discovery);
1164
- } else if (discovery instanceof UnavailableAgent) {
1165
- unavailable.push(discovery);
1166
- } else {
1167
- throw new Error("Unsupported Agent discovery outcome");
1168
- }
1169
- }
1170
- this.available = Object.freeze(available);
1171
- this.discoveries = Object.freeze([...discoveries]);
1172
- this.installable = Object.freeze(
1173
- unavailable.filter(
1174
- (discovery) => !(discovery instanceof AuthenticationRequiredAgent) && discovery.specification.supportsInstallation(
1175
- platform,
1176
- architecture
1177
- )
1178
- ).map(
1179
- (discovery) => new InstallableAgentDefinition(
1180
- discovery.specification.id,
1181
- discovery.specification.label,
1182
- !grantedAgents.has(discovery.specification.id)
1183
- )
1184
- )
2065
+ "Pi ACP adapter returned an invalid session"
2066
+ );
2067
+ }
2068
+ const prompt = await connection.agent.request(
2069
+ methods.agent.session.prompt,
2070
+ {
2071
+ prompt: [{ text: "/session", type: "text" }],
2072
+ sessionId: session.sessionId
2073
+ },
2074
+ { cancellationSignal: signal }
1185
2075
  );
1186
- this.unavailable = Object.freeze(unavailable);
1187
- }
1188
- readyAgents = () => this.available.map((discovery) => discovery.agent);
2076
+ if (prompt.stopReason !== "end_turn") {
2077
+ throw new AgentProbeError(
2078
+ "acp_incompatible",
2079
+ "Pi ACP adapter did not complete its conformance prompt"
2080
+ );
2081
+ }
2082
+ };
1189
2083
  };
1190
- var AgentProbeError = class extends Error {
2084
+
2085
+ // src/agent-catalog/agents/supported-agent-specifications.ts
2086
+ function supportedLocalAgentSpecifications() {
2087
+ return Object.freeze([
2088
+ new CodexAgentSpecification(),
2089
+ new ClaudeCodeAgentSpecification(),
2090
+ new PiAgentSpecification(),
2091
+ new DeepSeekHarnessAgentSpecification(),
2092
+ new OpenCodeAgentSpecification()
2093
+ ]);
2094
+ }
2095
+
2096
+ // src/agent-catalog/acp-readiness-probe.ts
2097
+ import { spawn as spawn2 } from "node:child_process";
2098
+ import {
2099
+ methods as methods3,
2100
+ ndJsonStream,
2101
+ PROTOCOL_VERSION,
2102
+ RequestError
2103
+ } from "@agentclientprotocol/sdk";
2104
+
2105
+ // src/acp/create-client.ts
2106
+ import {
2107
+ client,
2108
+ methods as methods2
2109
+ } from "@agentclientprotocol/sdk";
2110
+ var createAcpClient = (handler) => client({ name: "mobius" }).onRequest(
2111
+ methods2.client.session.requestPermission,
2112
+ async (context) => await handler.onPermission(context.params, context.signal)
2113
+ ).onRequest(
2114
+ methods2.client.elicitation.create,
2115
+ async (context) => await handler.onElicitation(context.params, context.signal)
2116
+ ).onNotification(methods2.client.session.update, async (context) => {
2117
+ await handler.onUpdate(context.params);
2118
+ }).onNotification(methods2.client.elicitation.complete, async (context) => {
2119
+ await handler.onElicitationComplete(context.params);
2120
+ });
2121
+
2122
+ // src/agent-catalog/executable-discovery.ts
2123
+ import { isAbsolute as isAbsolute4, join as join11 } from "node:path";
2124
+
2125
+ // src/agent-installation/managed-adapter-installer.ts
2126
+ import { join as join10 } from "node:path";
2127
+ var ManagedAdapterInstallError = class extends Error {
1191
2128
  code;
1192
2129
  constructor(code, message, options = {}) {
1193
2130
  super(message, options);
1194
2131
  this.code = code;
1195
- this.name = "AgentProbeError";
2132
+ this.name = "ManagedAdapterInstallError";
1196
2133
  }
1197
2134
  };
1198
- var DiscoveryAcpHandler = class {
1199
- onElicitation = (_request, _signal) => Promise.reject(new Error("ACP probe does not accept elicitations"));
1200
- onElicitationComplete = (_notification) => Promise.resolve();
1201
- onPermission = (_request, _signal) => Promise.reject(new Error("ACP probe does not grant permissions"));
1202
- onUpdate = (_notification) => Promise.resolve();
2135
+ function translateError(error61) {
2136
+ if (error61 instanceof ManagedAdapterInstallError) {
2137
+ return error61;
2138
+ }
2139
+ if (error61 instanceof ManagedNpmPackageError) {
2140
+ if (error61.code === "package_manager_not_found") {
2141
+ return new ManagedAdapterInstallError(
2142
+ error61.code,
2143
+ "A supported npm executable is required to install the ACP adapter",
2144
+ { cause: error61 }
2145
+ );
2146
+ }
2147
+ if (error61.code === "managed_package_verification_failed") {
2148
+ return new ManagedAdapterInstallError(
2149
+ "adapter_verification_failed",
2150
+ "The managed adapter did not match its pinned artifact",
2151
+ { cause: error61 }
2152
+ );
2153
+ }
2154
+ if (error61.code === "managed_package_publish_failed") {
2155
+ return new ManagedAdapterInstallError(
2156
+ "adapter_publish_failed",
2157
+ "The managed adapter could not be published",
2158
+ { cause: error61 }
2159
+ );
2160
+ }
2161
+ }
2162
+ return new ManagedAdapterInstallError(
2163
+ "adapter_install_failed",
2164
+ "The pinned ACP adapter could not be installed",
2165
+ { cause: error61 }
2166
+ );
2167
+ }
2168
+ async function validateManagedAdapter(root, specification) {
2169
+ try {
2170
+ return await validateManagedNpmPackage(
2171
+ root,
2172
+ specification.adapterPackageDefinition
2173
+ );
2174
+ } catch (error61) {
2175
+ throw translateError(error61);
2176
+ }
2177
+ }
2178
+ var ManagedAdapterInstaller = class {
2179
+ #environment;
2180
+ #npmInstaller;
2181
+ #stateDirectory;
2182
+ constructor(options, npmInstaller = new ManagedNpmPackageInstaller()) {
2183
+ this.#environment = options.environment;
2184
+ this.#npmInstaller = npmInstaller;
2185
+ this.#stateDirectory = options.stateDirectory;
2186
+ }
2187
+ install = async (specification, signal) => {
2188
+ const root = join10(
2189
+ this.#stateDirectory,
2190
+ "adapters",
2191
+ specification.id,
2192
+ specification.adapterVersion
2193
+ );
2194
+ try {
2195
+ return await this.#npmInstaller.install(
2196
+ root,
2197
+ specification.adapterPackageDefinition,
2198
+ this.#environment,
2199
+ signal
2200
+ );
2201
+ } catch (error61) {
2202
+ if (signal.aborted) {
2203
+ signal.throwIfAborted();
2204
+ }
2205
+ throw translateError(error61);
2206
+ }
2207
+ };
1203
2208
  };
1204
- async function resolveManagedExecutable(specification, options) {
2209
+
2210
+ // src/agent-catalog/executable-discovery.ts
2211
+ async function resolveManagedExecutable(specification, context) {
1205
2212
  try {
1206
2213
  return await specification.resolveManagedAgent(
1207
- readStateDirectory(options.environment),
1208
- options.platform ?? process.platform
2214
+ readStateDirectory(context.environment),
2215
+ context.platform ?? process.platform,
2216
+ context.signal
1209
2217
  );
1210
2218
  } catch {
2219
+ context.signal.throwIfAborted();
1211
2220
  return null;
1212
2221
  }
1213
2222
  }
1214
- async function resolveManagedAdapterExecutable(specification, options) {
2223
+ async function resolveManagedAdapterExecutable(specification, context) {
1215
2224
  let managed;
1216
2225
  try {
1217
2226
  managed = await validateManagedAdapter(
1218
- join6(
1219
- readStateDirectory(options.environment),
2227
+ join11(
2228
+ readStateDirectory(context.environment),
1220
2229
  "adapters",
1221
2230
  specification.id,
1222
2231
  specification.adapterVersion
@@ -1228,19 +2237,19 @@ async function resolveManagedAdapterExecutable(specification, options) {
1228
2237
  }
1229
2238
  return managed;
1230
2239
  }
1231
- async function resolveSpecificationExecutables(executableName, overrideVariable, resolveManaged, options) {
1232
- const override = options.environment[overrideVariable];
2240
+ async function resolveSpecificationExecutables(executableName, overrideVariable, resolveManaged, context) {
2241
+ const override = context.environment[overrideVariable];
1233
2242
  if (override !== void 0) {
1234
- if (override.length === 0 || !isAbsolute2(override)) {
2243
+ if (override.length === 0 || !isAbsolute4(override)) {
1235
2244
  return [];
1236
2245
  }
1237
- const executable = await resolveExecutablePath(override, options.platform);
2246
+ const executable = await resolveExecutablePath(override, context.platform);
1238
2247
  return executable === null ? [] : [executable];
1239
2248
  }
1240
2249
  const external = await findExecutableOnPath(
1241
2250
  executableName,
1242
- options.environment,
1243
- options.platform
2251
+ context.environment,
2252
+ context.platform
1244
2253
  );
1245
2254
  const managed = await resolveManaged();
1246
2255
  if (external === null) {
@@ -1251,41 +2260,47 @@ async function resolveSpecificationExecutables(executableName, overrideVariable,
1251
2260
  }
1252
2261
  return [external, managed];
1253
2262
  }
1254
- async function resolveAgentExecutables(specification, options) {
2263
+ async function resolveAgentExecutables(specification, context) {
1255
2264
  return await resolveSpecificationExecutables(
1256
2265
  specification.agentExecutable,
1257
2266
  specification.agentOverrideVariable,
1258
- async () => await resolveManagedExecutable(specification, options),
1259
- options
2267
+ async () => await resolveManagedExecutable(specification, context),
2268
+ context
1260
2269
  );
1261
2270
  }
1262
- async function resolveAdapterExecutables(specification, options) {
2271
+ async function resolveAdapterExecutables(specification, context) {
2272
+ if (specification instanceof DirectAcpAgentDefinition) {
2273
+ return await resolveAgentExecutables(specification, context);
2274
+ }
2275
+ if (!(specification instanceof BridgedAcpAgentDefinition)) {
2276
+ throw new Error("Unsupported local Agent execution topology");
2277
+ }
1263
2278
  return await resolveSpecificationExecutables(
1264
2279
  specification.adapterExecutable,
1265
2280
  specification.adapterOverrideVariable,
1266
- async () => await resolveManagedAdapterExecutable(specification, options),
1267
- options
2281
+ async () => await resolveManagedAdapterExecutable(specification, context),
2282
+ context
1268
2283
  );
1269
2284
  }
1270
- async function resolveAgentExecutable(specification, options) {
1271
- const executables = await resolveAgentExecutables(specification, options);
2285
+ async function resolveAgentExecutable(specification, context) {
2286
+ const executables = await resolveAgentExecutables(specification, context);
1272
2287
  return executables[0] ?? null;
1273
2288
  }
1274
- async function resolveAdapterExecutable(specification, options) {
1275
- const executables = await resolveAdapterExecutables(specification, options);
2289
+ async function resolveAdapterExecutable(specification, context) {
2290
+ const executables = await resolveAdapterExecutables(specification, context);
1276
2291
  return executables[0] ?? null;
1277
2292
  }
1278
2293
  function normalizedVersion(output2) {
1279
2294
  return output2.trim().split(/\r?\n/, 1)[0]?.trim().slice(0, 128) ?? "";
1280
2295
  }
1281
- async function probeVersion(executable, arguments_, environment, defaultWorkspace, signal) {
2296
+ async function probeVersion(executable, arguments_, environment, defaultWorkspace, signal, timeoutMilliseconds) {
1282
2297
  const result = await runBoundedProcess({
1283
2298
  arguments: arguments_,
1284
2299
  command: executable.path,
1285
2300
  cwd: defaultWorkspace,
1286
2301
  environment,
1287
2302
  signal,
1288
- timeoutMilliseconds: VERSION_PROBE_TIMEOUT_MILLISECONDS
2303
+ timeoutMilliseconds
1289
2304
  });
1290
2305
  const version2 = normalizedVersion(result.stdout || result.stderr);
1291
2306
  if (version2.length === 0) {
@@ -1304,63 +2319,73 @@ var VerifiedAgentExecutable = class {
1304
2319
  this.version = version2;
1305
2320
  }
1306
2321
  };
1307
- async function findVerifiedAgentExecutable(specification, options) {
1308
- const executables = await resolveAgentExecutables(specification, options);
2322
+ async function findVerifiedAgentExecutable(specification, context) {
2323
+ const executables = await resolveAgentExecutables(specification, context);
1309
2324
  for (const executable of executables) {
1310
2325
  try {
1311
2326
  const version2 = await probeVersion(
1312
2327
  executable,
1313
2328
  specification.agentVersionArguments,
1314
- options.environment,
1315
- options.defaultWorkspace,
1316
- options.signal
2329
+ context.environment,
2330
+ context.defaultWorkspace,
2331
+ context.signal,
2332
+ specification.versionProbeTimeoutMilliseconds
1317
2333
  );
1318
2334
  return new VerifiedAgentExecutable(executable, version2);
1319
2335
  } catch {
1320
- if (options.signal.aborted) {
1321
- options.signal.throwIfAborted();
2336
+ if (context.signal.aborted) {
2337
+ context.signal.throwIfAborted();
1322
2338
  }
1323
2339
  }
1324
2340
  }
1325
2341
  return null;
1326
2342
  }
2343
+
2344
+ // src/agent-catalog/acp-readiness-probe.ts
2345
+ var ACP_OUTPUT_LIMIT_BYTES = 1024 * 1024;
2346
+ var DiscoveryAcpHandler = class {
2347
+ onElicitation = (_request, _signal) => Promise.reject(new Error("ACP probe does not accept elicitations"));
2348
+ onElicitationComplete = (_notification) => Promise.resolve();
2349
+ onPermission = (_request, _signal) => Promise.reject(new Error("ACP probe does not grant permissions"));
2350
+ onUpdate = (_notification) => Promise.resolve();
2351
+ };
1327
2352
  function readableAcpStream(input2) {
1328
2353
  let byteLength = 0;
1329
- let open6 = true;
2354
+ let open7 = true;
1330
2355
  return new ReadableStream({
1331
2356
  start(controller) {
1332
2357
  input2.on("data", (chunk) => {
1333
- if (!open6) {
2358
+ if (!open7) {
1334
2359
  return;
1335
2360
  }
1336
2361
  if (!(chunk instanceof Uint8Array)) {
1337
- open6 = false;
2362
+ open7 = false;
1338
2363
  controller.error(new Error("ACP adapter produced invalid output"));
1339
2364
  return;
1340
2365
  }
1341
2366
  byteLength += chunk.byteLength;
1342
2367
  if (byteLength > ACP_OUTPUT_LIMIT_BYTES) {
1343
- open6 = false;
2368
+ open7 = false;
1344
2369
  controller.error(new Error("ACP adapter output limit exceeded"));
1345
2370
  return;
1346
2371
  }
1347
2372
  controller.enqueue(new Uint8Array(chunk));
1348
2373
  });
1349
2374
  input2.once("end", () => {
1350
- if (open6) {
1351
- open6 = false;
2375
+ if (open7) {
2376
+ open7 = false;
1352
2377
  controller.close();
1353
2378
  }
1354
2379
  });
1355
2380
  input2.once("error", (error61) => {
1356
- if (open6) {
1357
- open6 = false;
2381
+ if (open7) {
2382
+ open7 = false;
1358
2383
  controller.error(error61);
1359
2384
  }
1360
2385
  });
1361
2386
  },
1362
2387
  cancel() {
1363
- open6 = false;
2388
+ open7 = false;
1364
2389
  }
1365
2390
  });
1366
2391
  }
@@ -1379,7 +2404,7 @@ function writableAcpStream(output2) {
1379
2404
  }
1380
2405
  });
1381
2406
  }
1382
- async function probeAcp(specification, agent, environment, defaultWorkspace, signal) {
2407
+ async function probeAcpReadiness(specification, agent, environment, defaultWorkspace, signal) {
1383
2408
  signal.throwIfAborted();
1384
2409
  const controller = new AbortController();
1385
2410
  let timedOut = false;
@@ -1423,13 +2448,13 @@ async function probeAcp(specification, agent, environment, defaultWorkspace, sig
1423
2448
  }
1424
2449
  };
1425
2450
  stderr.on("data", consumeStderr);
1426
- const connection = createAcpClient(new DiscoveryAcpHandler()).connect(
1427
- ndJsonStream(writableAcpStream(stdin), readableAcpStream(stdout))
1428
- );
2451
+ const connection = createAcpClient(
2452
+ new DiscoveryAcpHandler()
2453
+ ).connect(ndJsonStream(writableAcpStream(stdin), readableAcpStream(stdout)));
1429
2454
  try {
1430
2455
  const initialized = await Promise.race([
1431
2456
  connection.agent.request(
1432
- methods2.agent.initialize,
2457
+ methods3.agent.initialize,
1433
2458
  {
1434
2459
  clientCapabilities: {},
1435
2460
  clientInfo: { name: "mobius", version: "0.0.3" },
@@ -1487,6 +2512,8 @@ async function probeAcp(specification, agent, environment, defaultWorkspace, sig
1487
2512
  await stopProcessTree(child);
1488
2513
  }
1489
2514
  }
2515
+
2516
+ // src/agent-catalog/discover-agent.ts
1490
2517
  function probeFailure(specification, executableKind, error61) {
1491
2518
  if (error61 instanceof AgentProbeError) {
1492
2519
  if (error61.code === "authentication_required") {
@@ -1508,12 +2535,15 @@ function probeFailure(specification, executableKind, error61) {
1508
2535
  `${executableKind === "agent" ? "Agent" : "Adapter"} probe failed`
1509
2536
  );
1510
2537
  }
1511
- async function discoverSpecification(specification, options) {
1512
- options.signal.throwIfAborted();
2538
+ async function discoverAgent(specification, context) {
2539
+ context.signal.throwIfAborted();
1513
2540
  const [adapters, agents] = await Promise.all([
1514
- resolveAdapterExecutables(specification, options),
1515
- resolveAgentExecutables(specification, options)
2541
+ resolveAdapterExecutables(specification, context),
2542
+ resolveAgentExecutables(specification, context)
1516
2543
  ]);
2544
+ if (specification instanceof DirectAcpAgentDefinition && agents.length === 0) {
2545
+ return new MissingAgent(specification);
2546
+ }
1517
2547
  if (adapters.length === 0) {
1518
2548
  return new MissingAdapter(specification);
1519
2549
  }
@@ -1527,35 +2557,45 @@ async function discoverSpecification(specification, options) {
1527
2557
  agentVersion = await probeVersion(
1528
2558
  agent,
1529
2559
  specification.agentVersionArguments,
1530
- options.environment,
1531
- options.defaultWorkspace,
1532
- options.signal
2560
+ context.environment,
2561
+ context.defaultWorkspace,
2562
+ context.signal,
2563
+ specification.versionProbeTimeoutMilliseconds
1533
2564
  );
1534
2565
  } catch (error61) {
1535
- if (options.signal.aborted) {
1536
- options.signal.throwIfAborted();
2566
+ if (context.signal.aborted) {
2567
+ context.signal.throwIfAborted();
1537
2568
  }
1538
2569
  lastFailure = probeFailure(specification, "agent", error61);
1539
2570
  continue;
1540
2571
  }
1541
2572
  for (const adapter of adapters) {
2573
+ if (specification instanceof DirectAcpAgentDefinition && adapter.path !== agent.path) {
2574
+ continue;
2575
+ }
1542
2576
  let adapterVersion = "";
2577
+ let launchEnvironment;
1543
2578
  try {
2579
+ launchEnvironment = await specification.createLaunchEnvironment(
2580
+ agent,
2581
+ context
2582
+ );
1544
2583
  if (specification.adapterVersionArguments.length > 0) {
1545
2584
  adapterVersion = await probeVersion(
1546
2585
  adapter,
1547
2586
  specification.adapterVersionArguments,
1548
2587
  {
1549
- ...options.environment,
1550
- [specification.adapterEnvironmentVariable]: agent.path
2588
+ ...context.environment,
2589
+ ...launchEnvironment
1551
2590
  },
1552
- options.defaultWorkspace,
1553
- options.signal
2591
+ context.defaultWorkspace,
2592
+ context.signal,
2593
+ specification.versionProbeTimeoutMilliseconds
1554
2594
  );
1555
2595
  }
1556
2596
  } catch (error61) {
1557
- if (options.signal.aborted) {
1558
- options.signal.throwIfAborted();
2597
+ if (context.signal.aborted) {
2598
+ context.signal.throwIfAborted();
1559
2599
  }
1560
2600
  lastFailure = probeFailure(specification, "adapter", error61);
1561
2601
  continue;
@@ -1564,8 +2604,8 @@ async function discoverSpecification(specification, options) {
1564
2604
  specification.id,
1565
2605
  specification.label,
1566
2606
  adapter.path,
1567
- [],
1568
- { [specification.adapterEnvironmentVariable]: agent.path },
2607
+ specification.launchArguments,
2608
+ launchEnvironment,
1569
2609
  adapterVersion,
1570
2610
  agentVersion,
1571
2611
  adapter.identity,
@@ -1573,12 +2613,12 @@ async function discoverSpecification(specification, options) {
1573
2613
  specification.promptTimeoutMilliseconds
1574
2614
  );
1575
2615
  try {
1576
- const acpVersion = await probeAcp(
2616
+ const acpVersion = await probeAcpReadiness(
1577
2617
  specification,
1578
2618
  definition,
1579
- options.environment,
1580
- options.defaultWorkspace,
1581
- options.signal
2619
+ context.environment,
2620
+ context.defaultWorkspace,
2621
+ context.signal
1582
2622
  );
1583
2623
  const verifiedAdapterVersion = adapterVersion.length > 0 ? adapterVersion : acpVersion;
1584
2624
  if (verifiedAdapterVersion.length === 0) {
@@ -1593,8 +2633,8 @@ async function discoverSpecification(specification, options) {
1593
2633
  specification.id,
1594
2634
  specification.label,
1595
2635
  adapter.path,
1596
- [],
1597
- { [specification.adapterEnvironmentVariable]: agent.path },
2636
+ specification.launchArguments,
2637
+ launchEnvironment,
1598
2638
  verifiedAdapterVersion,
1599
2639
  agentVersion,
1600
2640
  adapter.identity,
@@ -1603,120 +2643,51 @@ async function discoverSpecification(specification, options) {
1603
2643
  )
1604
2644
  );
1605
2645
  } catch (error61) {
1606
- if (options.signal.aborted) {
1607
- options.signal.throwIfAborted();
2646
+ if (context.signal.aborted) {
2647
+ context.signal.throwIfAborted();
1608
2648
  }
1609
- lastFailure = probeFailure(specification, "adapter", error61);
2649
+ const failure = probeFailure(specification, "adapter", error61);
2650
+ if (failure instanceof AuthenticationRequiredAgent) {
2651
+ return failure;
2652
+ }
2653
+ lastFailure = failure;
1610
2654
  }
1611
2655
  }
1612
2656
  }
1613
- return lastFailure ?? new IncompatibleAdapter(specification);
1614
- }
1615
- async function discoverLocalAgentInventory(options) {
1616
- const specifications = options.specifications ?? supportedLocalAgentSpecifications();
1617
- const discoveries = await Promise.all(
1618
- specifications.map(
1619
- async (specification) => await discoverSpecification(specification, options)
1620
- )
1621
- );
1622
- let grantedAgents = /* @__PURE__ */ new Set();
1623
- try {
1624
- grantedAgents = await readGrantedAgents(
1625
- readStateDirectory(options.environment)
1626
- );
1627
- } catch {
1628
- }
1629
- return new LocalAgentInventory(
1630
- discoveries,
1631
- grantedAgents,
1632
- options.platform,
1633
- options.architecture
1634
- );
1635
- }
1636
-
1637
- // src/agent-catalog/agent-diagnostics.ts
1638
- var AvailableAgentDiagnostic = class {
1639
- adapterVersion;
1640
- agentVersion;
1641
- id;
1642
- label;
1643
- status = "ready";
1644
- constructor(discovery) {
1645
- this.adapterVersion = discovery.agent.adapterVersion;
1646
- this.agentVersion = discovery.agent.agentVersion;
1647
- this.id = discovery.specification.id;
1648
- this.label = discovery.specification.label;
1649
- }
1650
- };
1651
- var UnavailableAgentDiagnostic = class {
1652
- code;
1653
- id;
1654
- label;
1655
- message;
1656
- recoveryCommand;
1657
- status;
1658
- constructor(discovery) {
1659
- this.code = discovery.code;
1660
- this.id = discovery.specification.id;
1661
- this.label = discovery.specification.label;
1662
- this.message = discovery.message;
1663
- this.recoveryCommand = discovery instanceof MissingAdapter ? discovery.specification.recoveryCommand() : discovery.code === "authentication_required" ? discovery.specification.authenticationCommand() : "";
1664
- this.status = discovery.status;
1665
- }
1666
- };
1667
- var AgentDiagnostics = class {
1668
- available;
1669
- unavailable;
1670
- constructor(inventory) {
1671
- this.available = inventory.available.map(
1672
- (discovery) => new AvailableAgentDiagnostic(discovery)
1673
- );
1674
- this.unavailable = inventory.unavailable.map(
1675
- (discovery) => new UnavailableAgentDiagnostic(discovery)
1676
- );
1677
- }
1678
- };
1679
- function humanLine(label, status, detail) {
1680
- return `${label.padEnd(13)} ${status.padEnd(17)} ${detail}`.trimEnd();
1681
- }
1682
- function formatAgentDiagnostics(inventory, json2) {
1683
- const diagnostics = new AgentDiagnostics(inventory);
1684
- if (json2) {
1685
- return `${JSON.stringify(diagnostics, null, 2)}
1686
- `;
1687
- }
1688
- const lines = [];
1689
- for (const available of diagnostics.available) {
1690
- lines.push(
1691
- humanLine(
1692
- available.label,
1693
- available.status,
1694
- `${available.agentVersion} ${available.adapterVersion}`
1695
- )
1696
- );
1697
- }
1698
- for (const unavailable of diagnostics.unavailable) {
1699
- lines.push(
1700
- humanLine(unavailable.label, unavailable.status, unavailable.message)
1701
- );
1702
- if (unavailable.recoveryCommand.length > 0) {
1703
- lines.push(
1704
- unavailable.code === "authentication_required" ? ` Authenticate locally: ${unavailable.recoveryCommand}` : ` Install adapter: ${unavailable.recoveryCommand}`
1705
- );
1706
- }
1707
- }
1708
- return `${lines.join("\n")}
1709
- `;
2657
+ return lastFailure ?? new IncompatibleAdapter(specification);
2658
+ }
2659
+
2660
+ // src/agent-catalog/discover-local-agents.ts
2661
+ async function discoverLocalAgentInventory(options) {
2662
+ const specifications = options.specifications ?? supportedLocalAgentSpecifications();
2663
+ const discoveries = await Promise.all(
2664
+ specifications.map(
2665
+ async (specification) => await discoverAgent(specification, options)
2666
+ )
2667
+ );
2668
+ let grantedAgents = /* @__PURE__ */ new Set();
2669
+ try {
2670
+ grantedAgents = await readGrantedAgents(
2671
+ readStateDirectory(options.environment)
2672
+ );
2673
+ } catch {
2674
+ }
2675
+ return new LocalAgentInventory(
2676
+ discoveries,
2677
+ grantedAgents,
2678
+ options.platform,
2679
+ options.architecture
2680
+ );
1710
2681
  }
1711
2682
 
1712
2683
  // src/audit/audit-store.ts
1713
2684
  import { mkdirSync } from "node:fs";
1714
- import { dirname as dirname2 } from "node:path";
2685
+ import { dirname as dirname3 } from "node:path";
1715
2686
  import { DatabaseSync } from "node:sqlite";
1716
2687
  var AuditStore = class {
1717
2688
  database;
1718
2689
  constructor(path2) {
1719
- mkdirSync(dirname2(path2), { recursive: true });
2690
+ mkdirSync(dirname3(path2), { recursive: true });
1720
2691
  this.database = new DatabaseSync(path2);
1721
2692
  this.database.exec(`
1722
2693
  CREATE TABLE IF NOT EXISTS audit_events (
@@ -1760,10 +2731,10 @@ var AuditStore = class {
1760
2731
  };
1761
2732
 
1762
2733
  // src/config.ts
1763
- import { join as join7 } from "node:path";
2734
+ import { join as join12 } from "node:path";
1764
2735
  function loadDefaultWorkspace() {
1765
2736
  const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = process.env;
1766
- return configuredWorkspace ?? join7(readStateDirectory(), "workspace");
2737
+ return configuredWorkspace ?? join12(readStateDirectory(), "workspace");
1767
2738
  }
1768
2739
  function createDaemonConfig(connection, agents = [], installableAgents = []) {
1769
2740
  return {
@@ -1781,22 +2752,16 @@ function createDaemonConfig(connection, agents = [], installableAgents = []) {
1781
2752
  // src/pairing/pair-local-machine.ts
1782
2753
  import { randomUUID } from "node:crypto";
1783
2754
  import {
1784
- mkdir as mkdir2,
1785
- open as open3,
1786
- realpath as realpath3,
1787
- rename as rename2,
1788
- stat as stat3,
1789
- writeFile
2755
+ mkdir as mkdir3,
2756
+ open as open5,
2757
+ realpath as realpath5,
2758
+ rename as rename3,
2759
+ stat as stat5,
2760
+ writeFile as writeFile2
1790
2761
  } from "node:fs/promises";
1791
2762
  import { hostname } from "node:os";
1792
- import { basename, join as join8, resolve as resolve3 } from "node:path";
1793
- import { fetch } from "undici";
1794
-
1795
- // src/network/environment-proxy.ts
1796
- import { EnvHttpProxyAgent } from "undici";
1797
- var environmentProxyDispatcher = new EnvHttpProxyAgent();
1798
-
1799
- // src/pairing/pair-local-machine.ts
2763
+ import { basename, join as join13, resolve as resolve3 } from "node:path";
2764
+ import { fetch as fetch2 } from "undici";
1800
2765
  var PAIRING_TIMEOUT_MILLISECONDS = 2e4;
1801
2766
  var PAIRING_CODE_PATTERN = /^[A-HJ-NP-Z2-9]{4}(?:-[A-HJ-NP-Z2-9]{4}){4}$/;
1802
2767
  var IDENTIFIER_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
@@ -1821,7 +2786,7 @@ var PairingClientError = class extends Error {
1821
2786
  this.name = "PairingClientError";
1822
2787
  }
1823
2788
  };
1824
- function isRecord3(value) {
2789
+ function isRecord4(value) {
1825
2790
  return typeof value === "object" && value !== null && !Array.isArray(value);
1826
2791
  }
1827
2792
  function hasExactKeys(value, expectedKeys) {
@@ -1934,9 +2899,9 @@ function parseChannelArgument(arguments_) {
1934
2899
  async function resolveDefaultWorkspace(path2) {
1935
2900
  const requestedWorkspace = resolve3(path2);
1936
2901
  try {
1937
- await mkdir2(requestedWorkspace, { recursive: true, mode: 448 });
1938
- const defaultWorkspace = await realpath3(requestedWorkspace);
1939
- const metadata = await stat3(defaultWorkspace);
2902
+ await mkdir3(requestedWorkspace, { recursive: true, mode: 448 });
2903
+ const defaultWorkspace = await realpath5(requestedWorkspace);
2904
+ const metadata = await stat5(defaultWorkspace);
1940
2905
  if (!metadata.isDirectory()) {
1941
2906
  throw new PairingClientError(
1942
2907
  "invalid_workspace",
@@ -1974,9 +2939,9 @@ async function readJsonResponse(response) {
1974
2939
  }
1975
2940
  }
1976
2941
  function readRelayError(value) {
1977
- if (isRecord3(value)) {
2942
+ if (isRecord4(value)) {
1978
2943
  const error61 = value["error"];
1979
- if (isRecord3(error61)) {
2944
+ if (isRecord4(error61)) {
1980
2945
  const code = error61["code"];
1981
2946
  const message = error61["message"];
1982
2947
  if (typeof code === "string" && typeof message === "string") {
@@ -1990,7 +2955,7 @@ function readRelayError(value) {
1990
2955
  );
1991
2956
  }
1992
2957
  function decodeClaimResponse(value) {
1993
- if (!isRecord3(value) || !hasExactKeys(value, CLAIM_RESPONSE_KEYS)) {
2958
+ if (!isRecord4(value) || !hasExactKeys(value, CLAIM_RESPONSE_KEYS)) {
1994
2959
  throw new PairingClientError(
1995
2960
  "invalid_relay_response",
1996
2961
  "The relay returned an invalid pairing response"
@@ -2015,7 +2980,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
2015
2980
  const endpoint = new URL("/api/pairings/claim", options.relayOrigin);
2016
2981
  let response;
2017
2982
  try {
2018
- response = await fetch(endpoint, {
2983
+ response = await fetch2(endpoint, {
2019
2984
  body: JSON.stringify({
2020
2985
  agents,
2021
2986
  installableAgents: installableAgents.map((agent) => ({
@@ -2049,14 +3014,14 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
2049
3014
  return decodeClaimResponse(value);
2050
3015
  }
2051
3016
  async function persistConnection(channelId, relayOrigin, daemonToken, defaultWorkspace) {
2052
- const connectionsDirectory = join8(readStateDirectory(), "connections");
2053
- await mkdir2(connectionsDirectory, { mode: 448, recursive: true });
2054
- const connectionPath = join8(connectionsDirectory, `${channelId}.json`);
2055
- const temporaryPath = join8(
3017
+ const connectionsDirectory = join13(readStateDirectory(), "connections");
3018
+ await mkdir3(connectionsDirectory, { mode: 448, recursive: true });
3019
+ const connectionPath = join13(connectionsDirectory, `${channelId}.json`);
3020
+ const temporaryPath = join13(
2056
3021
  connectionsDirectory,
2057
3022
  `.${channelId}.${randomUUID()}.tmp`
2058
3023
  );
2059
- await writeFile(
3024
+ await writeFile2(
2060
3025
  temporaryPath,
2061
3026
  `${JSON.stringify(
2062
3027
  {
@@ -2071,11 +3036,11 @@ async function persistConnection(channelId, relayOrigin, daemonToken, defaultWor
2071
3036
  `,
2072
3037
  { flag: "wx", mode: 384 }
2073
3038
  );
2074
- await rename2(temporaryPath, connectionPath);
3039
+ await rename3(temporaryPath, connectionPath);
2075
3040
  return connectionPath;
2076
3041
  }
2077
3042
  function decodeSavedConnection(value, expectedChannelId) {
2078
- if (!isRecord3(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS)) {
3043
+ if (!isRecord4(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS)) {
2079
3044
  throw new PairingClientError(
2080
3045
  "invalid_connection",
2081
3046
  "The saved connection is invalid"
@@ -2099,13 +3064,13 @@ function decodeSavedConnection(value, expectedChannelId) {
2099
3064
  };
2100
3065
  }
2101
3066
  async function readSavedConnection(channelId) {
2102
- const connectionPath = join8(
3067
+ const connectionPath = join13(
2103
3068
  readStateDirectory(),
2104
3069
  "connections",
2105
3070
  `${channelId}.json`
2106
3071
  );
2107
3072
  let encoded = "";
2108
- const handle = await open3(connectionPath, "r");
3073
+ const handle = await open5(connectionPath, "r");
2109
3074
  try {
2110
3075
  const metadata = await handle.stat();
2111
3076
  if (!metadata.isFile() || metadata.size > MAX_CONNECTION_FILE_LENGTH) {
@@ -2154,7 +3119,7 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
2154
3119
  );
2155
3120
  relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
2156
3121
  relayUrl.searchParams.set("token", connection.daemonToken);
2157
- const auditDatabasePath = join8(
3122
+ const auditDatabasePath = join13(
2158
3123
  readStateDirectory(),
2159
3124
  "audit",
2160
3125
  `${connection.channelId}.sqlite`
@@ -2213,7 +3178,7 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
2213
3178
  }
2214
3179
  async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
2215
3180
  const channelId = parseChannelArgument(arguments_);
2216
- const connectionPath = join8(
3181
+ const connectionPath = join13(
2217
3182
  readStateDirectory(),
2218
3183
  "connections",
2219
3184
  `${channelId}.json`
@@ -7248,7 +8213,7 @@ function bucketFor(state, inst) {
7248
8213
  return bucket;
7249
8214
  }
7250
8215
  var handoff;
7251
- var open4 = [];
8216
+ var open6 = [];
7252
8217
  var memo = {
7253
8218
  alloc(_inst, payload, empty) {
7254
8219
  const bucket = handoff;
@@ -7257,7 +8222,7 @@ var memo = {
7257
8222
  handoff = void 0;
7258
8223
  const entry = { value: empty, issues: null };
7259
8224
  bucket.set(payload.value, entry);
7260
- open4.push(entry);
8225
+ open6.push(entry);
7261
8226
  return empty;
7262
8227
  },
7263
8228
  guard(inst) {
@@ -7323,10 +8288,10 @@ var memo = {
7323
8288
  return payload;
7324
8289
  }
7325
8290
  handoff = bucket;
7326
- const depth = open4.length;
8291
+ const depth = open6.length;
7327
8292
  const result = base(payload, ctx);
7328
8293
  handoff = void 0;
7329
- const entry = open4.length > depth ? open4.pop() : void 0;
8294
+ const entry = open6.length > depth ? open6.pop() : void 0;
7330
8295
  if (result instanceof Promise) {
7331
8296
  return result.then((r) => {
7332
8297
  if (entry)
@@ -21615,7 +22580,7 @@ var ProtocolMessageError = class extends Error {
21615
22580
  this.name = "ProtocolMessageError";
21616
22581
  }
21617
22582
  };
21618
- function isRecord4(value) {
22583
+ function isRecord5(value) {
21619
22584
  return typeof value === "object" && value !== null && !Array.isArray(value);
21620
22585
  }
21621
22586
  function parseJson2(encoded) {
@@ -21627,7 +22592,7 @@ function parseJson2(encoded) {
21627
22592
  cause: error61
21628
22593
  });
21629
22594
  }
21630
- if (!isRecord4(value)) {
22595
+ if (!isRecord5(value)) {
21631
22596
  throw new ProtocolMessageError("Message must be a JSON object");
21632
22597
  }
21633
22598
  return value;
@@ -21711,7 +22676,7 @@ function readActivityContent(value) {
21711
22676
  }
21712
22677
  function readActivityDetail(value) {
21713
22678
  const field = value["detail"];
21714
- if (!isRecord4(field)) {
22679
+ if (!isRecord5(field)) {
21715
22680
  throw new ProtocolMessageError("Invalid activity detail field");
21716
22681
  }
21717
22682
  const kind = readString(field, "kind", 32);
@@ -22002,7 +22967,10 @@ var sessionHistorySchema = external_exports.discriminatedUnion("status", [
22002
22967
  status: external_exports.literal("available"),
22003
22968
  messages: external_exports.array(historyMessage).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
22004
22969
  truncated: external_exports.boolean(),
22005
- coveredCommandIds: external_exports.array(external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/)).max(1e4).readonly()
22970
+ coveredCommandIds: external_exports.array(external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/)).max(1e4).readonly(),
22971
+ coveredMessageIds: external_exports.array(
22972
+ external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}:(?:user|agent)$/)
22973
+ ).max(2e4).readonly().default([])
22006
22974
  }).readonly(),
22007
22975
  external_exports.strictObject({ status: external_exports.literal("unavailable") }).readonly()
22008
22976
  ]).refine(
@@ -22676,14 +23644,15 @@ import { WebSocket as WebSocket2 } from "undici";
22676
23644
  // src/acp/session-manager.ts
22677
23645
  import { spawn as spawn3 } from "node:child_process";
22678
23646
  import {
22679
- methods as methods3,
23647
+ methods as methods4,
22680
23648
  ndJsonStream as ndJsonStream2,
22681
- PROTOCOL_VERSION as PROTOCOL_VERSION2
23649
+ PROTOCOL_VERSION as PROTOCOL_VERSION2,
23650
+ RequestError as RequestError2
22682
23651
  } from "@agentclientprotocol/sdk";
22683
23652
 
22684
23653
  // src/acp/activity.ts
22685
23654
  var TRUNCATION_MARKER = "\n\u2026 output truncated by Mobius";
22686
- function isRecord5(value) {
23655
+ function isRecord6(value) {
22687
23656
  return typeof value === "object" && value !== null && !Array.isArray(value);
22688
23657
  }
22689
23658
  var AcpActivity = class _AcpActivity {
@@ -22957,7 +23926,7 @@ function contentPhase(content) {
22957
23926
  return "";
22958
23927
  }
22959
23928
  const codex = metadata["codex"];
22960
- if (!isRecord5(codex)) {
23929
+ if (!isRecord6(codex)) {
22961
23930
  return "";
22962
23931
  }
22963
23932
  const phase = codex["phase"];
@@ -23252,13 +24221,14 @@ var AcpHistoryReplay = class {
23252
24221
  status: "available",
23253
24222
  messages: [...this.#messages],
23254
24223
  truncated: this.#truncated,
23255
- coveredCommandIds: []
24224
+ coveredCommandIds: [],
24225
+ coveredMessageIds: []
23256
24226
  });
23257
24227
  };
23258
24228
 
23259
24229
  // src/acp/session-store.ts
23260
24230
  import { mkdirSync as mkdirSync2 } from "node:fs";
23261
- import { dirname as dirname3 } from "node:path";
24231
+ import { dirname as dirname4 } from "node:path";
23262
24232
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
23263
24233
  var AcpStoredSession = class {
23264
24234
  additionalDirectories;
@@ -23361,7 +24331,7 @@ var MemoryAcpSessionStore = class {
23361
24331
  var SqliteAcpSessionStore = class {
23362
24332
  #database;
23363
24333
  constructor(path2) {
23364
- mkdirSync2(dirname3(path2), { recursive: true });
24334
+ mkdirSync2(dirname4(path2), { recursive: true });
23365
24335
  this.#database = new DatabaseSync2(path2);
23366
24336
  this.#database.exec(`
23367
24337
  CREATE TABLE IF NOT EXISTS acp_deleted_sessions (session_id TEXT PRIMARY KEY) STRICT;
@@ -23468,6 +24438,8 @@ var SqliteAcpSessionStore = class {
23468
24438
 
23469
24439
  // src/acp/session-manager.ts
23470
24440
  var PROCESS_STOP_TIMEOUT_MILLISECONDS = 1e3;
24441
+ var MAX_PRE_SESSION_UPDATES = 100;
24442
+ var MAX_PRE_SESSION_UPDATE_BYTES = 1024 * 1024;
23471
24443
  var SESSION_CONTROL_COMMAND_ID = "session-control";
23472
24444
  var NullAcpSessionObserver = class {
23473
24445
  onCommands = () => Promise.resolve();
@@ -23588,7 +24560,7 @@ function createWritableAcpStream(output2) {
23588
24560
  function truncate2(value, maximumLength) {
23589
24561
  return value.length <= maximumLength ? value : `${value.slice(0, Math.max(0, maximumLength - 1))}\u2026`;
23590
24562
  }
23591
- function isRecord6(value) {
24563
+ function isRecord7(value) {
23592
24564
  return typeof value === "object" && value !== null && !Array.isArray(value);
23593
24565
  }
23594
24566
  function readString3(value, fallback = "") {
@@ -23618,7 +24590,7 @@ function normalizeConfigValues(value) {
23618
24590
  }
23619
24591
  const normalized = [];
23620
24592
  for (const entry of value) {
23621
- if (!isRecord6(entry)) {
24593
+ if (!isRecord7(entry)) {
23622
24594
  continue;
23623
24595
  }
23624
24596
  const groupedOptions = entry["options"];
@@ -23626,7 +24598,7 @@ function normalizeConfigValues(value) {
23626
24598
  const groupId = readString3(entry["group"]);
23627
24599
  const groupName = readString3(entry["name"]);
23628
24600
  for (const groupedOption of groupedOptions) {
23629
- if (isRecord6(groupedOption)) {
24601
+ if (isRecord7(groupedOption)) {
23630
24602
  normalized.push(
23631
24603
  normalizeConfigValue(groupedOption, groupId, groupName)
23632
24604
  );
@@ -23642,7 +24614,7 @@ function normalizeConfigOptions(value, modes) {
23642
24614
  const normalized = [];
23643
24615
  if (Array.isArray(value)) {
23644
24616
  for (const option of value) {
23645
- if (!isRecord6(option)) {
24617
+ if (!isRecord7(option)) {
23646
24618
  continue;
23647
24619
  }
23648
24620
  const id = truncate2(readString3(option["id"]), 128);
@@ -23681,12 +24653,12 @@ function normalizeConfigOptions(value, modes) {
23681
24653
  }
23682
24654
  }
23683
24655
  }
23684
- if (isRecord6(modes)) {
24656
+ if (isRecord7(modes)) {
23685
24657
  const values = [];
23686
24658
  const availableModes = modes["availableModes"];
23687
24659
  if (Array.isArray(availableModes)) {
23688
24660
  for (const mode of availableModes) {
23689
- if (!isRecord6(mode)) {
24661
+ if (!isRecord7(mode)) {
23690
24662
  continue;
23691
24663
  }
23692
24664
  const id = truncate2(readString3(mode["id"]), 500);
@@ -23764,7 +24736,7 @@ function normalizeElicitationOptions(schema) {
23764
24736
  const oneOf = schema["oneOf"];
23765
24737
  if (Array.isArray(oneOf)) {
23766
24738
  for (const option of oneOf) {
23767
- if (isRecord6(option) && typeof option["const"] === "string") {
24739
+ if (isRecord7(option) && typeof option["const"] === "string") {
23768
24740
  normalized.push({
23769
24741
  description: truncate2(readString3(option["description"]), 500),
23770
24742
  label: truncate2(readString3(option["title"], option["const"]), 255),
@@ -23784,7 +24756,7 @@ function normalizeElicitationOptions(schema) {
23784
24756
  return normalized.slice(0, 200);
23785
24757
  }
23786
24758
  const items = schema["items"];
23787
- if (!isRecord6(items)) {
24759
+ if (!isRecord7(items)) {
23788
24760
  return normalized;
23789
24761
  }
23790
24762
  const anyOf = items["anyOf"];
@@ -23797,7 +24769,7 @@ function numericString(value) {
23797
24769
  return typeof value === "number" && Number.isFinite(value) ? value.toString() : "";
23798
24770
  }
23799
24771
  function normalizeElicitationFields(requestedSchema) {
23800
- if (!isRecord6(requestedSchema) || !isRecord6(requestedSchema["properties"])) {
24772
+ if (!isRecord7(requestedSchema) || !isRecord7(requestedSchema["properties"])) {
23801
24773
  return [];
23802
24774
  }
23803
24775
  const properties = requestedSchema["properties"];
@@ -23807,7 +24779,7 @@ function normalizeElicitationFields(requestedSchema) {
23807
24779
  );
23808
24780
  const normalized = [];
23809
24781
  for (const [name, property] of Object.entries(properties)) {
23810
- if (!isRecord6(property)) {
24782
+ if (!isRecord7(property)) {
23811
24783
  continue;
23812
24784
  }
23813
24785
  const type = elicitationFieldType(property["type"]);
@@ -23902,7 +24874,7 @@ function validateElicitationContent(encoded, requestedSchema) {
23902
24874
  "invalid_elicitation_response"
23903
24875
  );
23904
24876
  }
23905
- if (!isRecord6(decoded) || !isRecord6(requestedSchema) || !isRecord6(requestedSchema["properties"])) {
24877
+ if (!isRecord7(decoded) || !isRecord7(requestedSchema) || !isRecord7(requestedSchema["properties"])) {
23906
24878
  throw new AcpExecutionError(
23907
24879
  "Elicitation response does not match the requested form",
23908
24880
  {},
@@ -23926,7 +24898,7 @@ function validateElicitationContent(encoded, requestedSchema) {
23926
24898
  const validated = {};
23927
24899
  for (const [name, value] of Object.entries(decoded)) {
23928
24900
  const property = properties[name];
23929
- if (!isRecord6(property)) {
24901
+ if (!isRecord7(property)) {
23930
24902
  throw new AcpExecutionError(
23931
24903
  `Elicitation field ${name} was not requested`,
23932
24904
  {},
@@ -24076,8 +25048,11 @@ var AcpProcessSession = class _AcpProcessSession {
24076
25048
  #mobiusSessionId;
24077
25049
  #pendingElicitations = /* @__PURE__ */ new Map();
24078
25050
  #pendingPermissions = /* @__PURE__ */ new Map();
25051
+ #preSessionUpdates = [];
24079
25052
  #processExit;
24080
25053
  #store;
25054
+ #verifiedAgentVersion;
25055
+ #preSessionUpdateBytes = 0;
24081
25056
  #agentName = "";
24082
25057
  #agentVersion = "";
24083
25058
  #capabilities = {
@@ -24111,6 +25086,7 @@ var AcpProcessSession = class _AcpProcessSession {
24111
25086
  this.#getObserver = getObserver;
24112
25087
  this.#mobiusSessionId = mobiusSessionId;
24113
25088
  this.#store = store;
25089
+ this.#verifiedAgentVersion = agent.agentVersion;
24114
25090
  this.#child = spawn3(agent.adapterPath, [...agent.arguments], {
24115
25091
  cwd,
24116
25092
  detached: process.platform !== "win32",
@@ -24213,7 +25189,7 @@ var AcpProcessSession = class _AcpProcessSession {
24213
25189
  try {
24214
25190
  await this.race(
24215
25191
  this.#connection.agent.request(
24216
- methods3.agent.session.load,
25192
+ methods4.agent.session.load,
24217
25193
  {
24218
25194
  additionalDirectories: [...this.#additionalDirectories],
24219
25195
  cwd: this.#cwd,
@@ -24235,7 +25211,7 @@ var AcpProcessSession = class _AcpProcessSession {
24235
25211
  async #initializeAgent(signal) {
24236
25212
  const initialized = await this.race(
24237
25213
  this.#connection.agent.request(
24238
- methods3.agent.initialize,
25214
+ methods4.agent.initialize,
24239
25215
  {
24240
25216
  clientCapabilities: {
24241
25217
  elicitation: { form: {}, url: {} },
@@ -24266,7 +25242,10 @@ var AcpProcessSession = class _AcpProcessSession {
24266
25242
  initialized.agentInfo?.title ?? initialized.agentInfo?.name ?? this.#agentId,
24267
25243
  255
24268
25244
  );
24269
- this.#agentVersion = truncate2(initialized.agentInfo?.version ?? "", 128);
25245
+ this.#agentVersion = truncate2(
25246
+ this.#verifiedAgentVersion.length > 0 ? this.#verifiedAgentVersion : initialized.agentInfo?.version ?? "",
25247
+ 128
25248
+ );
24270
25249
  }
24271
25250
  initialize = async (signal, origin) => {
24272
25251
  await this.#initializeAgent(signal);
@@ -24300,7 +25279,7 @@ var AcpProcessSession = class _AcpProcessSession {
24300
25279
  );
24301
25280
  const forked = await this.race(
24302
25281
  this.#connection.agent.request(
24303
- methods3.agent.session.fork,
25282
+ methods4.agent.session.fork,
24304
25283
  {
24305
25284
  sessionId: nativeSessionIdSchema.parse(origin.nativeSessionId),
24306
25285
  cwd: this.#cwd,
@@ -24312,7 +25291,7 @@ var AcpProcessSession = class _AcpProcessSession {
24312
25291
  "ACP agent exited while forking a session",
24313
25292
  signal
24314
25293
  );
24315
- this.#nativeSessionId = nativeSessionIdSchema.parse(forked.sessionId);
25294
+ this.selectNativeSession(forked.sessionId);
24316
25295
  if (this.#nativeSessionId === origin.nativeSessionId || this.#store.findNative(this.#agentId, this.#nativeSessionId).length > 0) {
24317
25296
  throw new AcpExecutionError(
24318
25297
  "The Agent did not create an independent fork",
@@ -24327,13 +25306,13 @@ var AcpProcessSession = class _AcpProcessSession {
24327
25306
  stored.session.additionalDirectories,
24328
25307
  this.#additionalDirectories
24329
25308
  )) {
24330
- this.#nativeSessionId = stored.session.nativeSessionId;
25309
+ this.selectNativeSession(stored.session.nativeSessionId);
24331
25310
  this.#prompted = stored.session.prompted;
24332
25311
  this.#restored = true;
24333
25312
  if (this.#capabilities.resumeSession) {
24334
25313
  lifecycleResponse = await this.race(
24335
25314
  this.#connection.agent.request(
24336
- methods3.agent.session.resume,
25315
+ methods4.agent.session.resume,
24337
25316
  {
24338
25317
  additionalDirectories: [...this.#additionalDirectories],
24339
25318
  cwd: this.#cwd,
@@ -24350,7 +25329,7 @@ var AcpProcessSession = class _AcpProcessSession {
24350
25329
  try {
24351
25330
  lifecycleResponse = await this.race(
24352
25331
  this.#connection.agent.request(
24353
- methods3.agent.session.load,
25332
+ methods4.agent.session.load,
24354
25333
  {
24355
25334
  additionalDirectories: [...this.#additionalDirectories],
24356
25335
  cwd: this.#cwd,
@@ -24386,7 +25365,7 @@ var AcpProcessSession = class _AcpProcessSession {
24386
25365
  }
24387
25366
  const created = await this.race(
24388
25367
  this.#connection.agent.request(
24389
- methods3.agent.session.new,
25368
+ methods4.agent.session.new,
24390
25369
  {
24391
25370
  additionalDirectories: [...this.#additionalDirectories],
24392
25371
  cwd: this.#cwd,
@@ -24397,10 +25376,10 @@ var AcpProcessSession = class _AcpProcessSession {
24397
25376
  "ACP agent exited while starting a session",
24398
25377
  signal
24399
25378
  );
24400
- this.#nativeSessionId = created.sessionId;
25379
+ this.selectNativeSession(created.sessionId);
24401
25380
  lifecycleResponse = created;
24402
25381
  }
24403
- const lifecycleRecord = isRecord6(lifecycleResponse) ? lifecycleResponse : {};
25382
+ const lifecycleRecord = isRecord7(lifecycleResponse) ? lifecycleResponse : {};
24404
25383
  this.#configOptions = normalizeConfigOptions(
24405
25384
  lifecycleRecord["configOptions"],
24406
25385
  lifecycleRecord["modes"]
@@ -24429,7 +25408,7 @@ var AcpProcessSession = class _AcpProcessSession {
24429
25408
  );
24430
25409
  const response = await this.race(
24431
25410
  this.#connection.agent.request(
24432
- methods3.agent.session.list,
25411
+ methods4.agent.session.list,
24433
25412
  cursor === null ? {} : { cursor },
24434
25413
  { cancellationSignal: signal }
24435
25414
  ),
@@ -24454,7 +25433,7 @@ var AcpProcessSession = class _AcpProcessSession {
24454
25433
  );
24455
25434
  await this.race(
24456
25435
  this.#connection.agent.request(
24457
- methods3.agent.session.delete,
25436
+ methods4.agent.session.delete,
24458
25437
  { sessionId: nativeSessionIdSchema.parse(nativeSessionId) },
24459
25438
  { cancellationSignal: signal }
24460
25439
  ),
@@ -24485,7 +25464,7 @@ var AcpProcessSession = class _AcpProcessSession {
24485
25464
  if (existing.category === "mode") {
24486
25465
  await this.race(
24487
25466
  this.#connection.agent.request(
24488
- methods3.agent.session.setMode,
25467
+ methods4.agent.session.setMode,
24489
25468
  { modeId: options.value, sessionId: this.#nativeSessionId },
24490
25469
  { cancellationSignal: options.signal }
24491
25470
  ),
@@ -24498,7 +25477,7 @@ var AcpProcessSession = class _AcpProcessSession {
24498
25477
  } else {
24499
25478
  const response = await this.race(
24500
25479
  this.#connection.agent.request(
24501
- methods3.agent.session.setConfigOption,
25480
+ methods4.agent.session.setConfigOption,
24502
25481
  {
24503
25482
  configId: existing.id,
24504
25483
  sessionId: this.#nativeSessionId,
@@ -24517,7 +25496,7 @@ var AcpProcessSession = class _AcpProcessSession {
24517
25496
  } else {
24518
25497
  const response = await this.race(
24519
25498
  this.#connection.agent.request(
24520
- methods3.agent.session.setConfigOption,
25499
+ methods4.agent.session.setConfigOption,
24521
25500
  {
24522
25501
  configId: existing.id,
24523
25502
  sessionId: this.#nativeSessionId,
@@ -24616,7 +25595,7 @@ var AcpProcessSession = class _AcpProcessSession {
24616
25595
  try {
24617
25596
  const response = await this.race(
24618
25597
  this.#connection.agent.request(
24619
- methods3.agent.session.prompt,
25598
+ methods4.agent.session.prompt,
24620
25599
  { prompt, sessionId: this.#nativeSessionId },
24621
25600
  { cancellationSignal: options.signal }
24622
25601
  ),
@@ -24659,13 +25638,17 @@ var AcpProcessSession = class _AcpProcessSession {
24659
25638
  }
24660
25639
  };
24661
25640
  cancel = async () => {
24662
- await this.cancelPendingInteractions();
24663
- if (!this.#turn.running) {
24664
- return;
25641
+ const capturedProcesses = await captureProcessTree(this.#child);
25642
+ try {
25643
+ await this.cancelPendingInteractions();
25644
+ if (this.#turn.running) {
25645
+ await this.#connection.agent.notify(methods4.agent.session.cancel, {
25646
+ sessionId: this.#nativeSessionId
25647
+ });
25648
+ }
25649
+ } finally {
25650
+ await this.closeProcess(capturedProcesses);
24665
25651
  }
24666
- await this.#connection.agent.notify(methods3.agent.session.cancel, {
24667
- sessionId: this.#nativeSessionId
24668
- });
24669
25652
  };
24670
25653
  respondPermission = async (options) => {
24671
25654
  const pending = this.#pendingPermissions.get(options.interactionId);
@@ -24852,11 +25835,45 @@ var AcpProcessSession = class _AcpProcessSession {
24852
25835
  });
24853
25836
  };
24854
25837
  onUpdate = (notification) => {
25838
+ if (this.#nativeSessionId.length === 0) {
25839
+ const notificationBytes = Buffer.byteLength(JSON.stringify(notification));
25840
+ if (this.#preSessionUpdates.length >= MAX_PRE_SESSION_UPDATES || this.#preSessionUpdateBytes + notificationBytes > MAX_PRE_SESSION_UPDATE_BYTES) {
25841
+ return Promise.reject(
25842
+ new AcpExecutionError(
25843
+ "ACP agent sent too many updates before creating its session",
25844
+ {},
25845
+ "agent_update_limit"
25846
+ )
25847
+ );
25848
+ }
25849
+ this.#preSessionUpdates.push(notification);
25850
+ this.#preSessionUpdateBytes += notificationBytes;
25851
+ return Promise.resolve();
25852
+ }
25853
+ return this.enqueueUpdate(notification);
25854
+ };
25855
+ enqueueUpdate = (notification) => {
24855
25856
  this.#updateTail = this.#updateTail.then(
24856
25857
  async () => await this.processUpdate(notification)
24857
25858
  );
24858
25859
  return this.#updateTail;
24859
25860
  };
25861
+ selectNativeSession = (sessionId) => {
25862
+ const selected = nativeSessionIdSchema.parse(sessionId);
25863
+ if (this.#nativeSessionId.length > 0 && this.#nativeSessionId !== selected) {
25864
+ throw new AcpExecutionError(
25865
+ "ACP agent attempted to replace the selected session",
25866
+ {},
25867
+ "invalid_agent_session"
25868
+ );
25869
+ }
25870
+ this.#nativeSessionId = selected;
25871
+ const pending = this.#preSessionUpdates.splice(0);
25872
+ this.#preSessionUpdateBytes = 0;
25873
+ for (const notification of pending) {
25874
+ void this.enqueueUpdate(notification);
25875
+ }
25876
+ };
24860
25877
  processUpdate = async (notification) => {
24861
25878
  this.assertNativeSession(notification.sessionId);
24862
25879
  const update = notification.update;
@@ -24981,7 +25998,7 @@ var AcpProcessSession = class _AcpProcessSession {
24981
25998
  if (this.#capabilities.closeSession && this.#nativeSessionId.length > 0) {
24982
25999
  try {
24983
26000
  await this.#connection.agent.request(
24984
- methods3.agent.session.close,
26001
+ methods4.agent.session.close,
24985
26002
  { sessionId: this.#nativeSessionId },
24986
26003
  {
24987
26004
  cancellationSignal: AbortSignal.timeout(
@@ -24994,9 +26011,12 @@ var AcpProcessSession = class _AcpProcessSession {
24994
26011
  }
24995
26012
  await this.closeProcess();
24996
26013
  };
24997
- closeProcess = async () => {
24998
- this.#connection.close();
24999
- await stopProcessTree(this.#child);
26014
+ closeProcess = async (capturedProcesses = []) => {
26015
+ try {
26016
+ await stopProcessTree(this.#child, capturedProcesses);
26017
+ } finally {
26018
+ this.#connection.close();
26019
+ }
25000
26020
  };
25001
26021
  race = async (operation, exitMessage, signal) => {
25002
26022
  signal.throwIfAborted();
@@ -25012,13 +26032,25 @@ var AcpProcessSession = class _AcpProcessSession {
25012
26032
  };
25013
26033
  });
25014
26034
  try {
25015
- return await Promise.race([
25016
- operation,
25017
- this.#processExit.then(() => {
25018
- throw new AcpExecutionError(exitMessage);
25019
- }),
25020
- aborted2
25021
- ]);
26035
+ try {
26036
+ return await Promise.race([
26037
+ operation,
26038
+ this.#processExit.then(() => {
26039
+ throw new AcpExecutionError(exitMessage);
26040
+ }),
26041
+ aborted2
26042
+ ]);
26043
+ } catch (error61) {
26044
+ signal.throwIfAborted();
26045
+ if (error61 instanceof RequestError2 && error61.code === -32e3) {
26046
+ throw new AcpExecutionError(
26047
+ "Agent authentication is required",
26048
+ { cause: error61 },
26049
+ "authentication_required"
26050
+ );
26051
+ }
26052
+ throw error61;
26053
+ }
25022
26054
  } finally {
25023
26055
  removeAbortListener();
25024
26056
  }
@@ -25277,7 +26309,13 @@ var AcpSessionManager = class {
25277
26309
  this.#cancellationRequests.add(sessionId);
25278
26310
  const runtime = this.#sessions.get(sessionId);
25279
26311
  if (runtime !== void 0) {
25280
- await runtime.cancel();
26312
+ try {
26313
+ await runtime.cancel();
26314
+ } finally {
26315
+ if (this.#sessions.get(sessionId) === runtime) {
26316
+ this.#sessions.delete(sessionId);
26317
+ }
26318
+ }
25281
26319
  }
25282
26320
  };
25283
26321
  runPrompt = async (options) => {
@@ -25330,7 +26368,19 @@ var AcpSessionManager = class {
25330
26368
  if (this.#cancellationRequests.has(options.sessionId)) {
25331
26369
  return { stopReason: "cancelled" };
25332
26370
  }
25333
- return await runtime.prompt(boundedOptions);
26371
+ try {
26372
+ const result = await runtime.prompt(boundedOptions);
26373
+ return this.#cancellationRequests.has(options.sessionId) ? { stopReason: "cancelled" } : result;
26374
+ } catch (error61) {
26375
+ if (this.#cancellationRequests.has(options.sessionId)) {
26376
+ if (this.#sessions.get(options.sessionId) === runtime) {
26377
+ this.#sessions.delete(options.sessionId);
26378
+ }
26379
+ await runtime.closeProcess();
26380
+ return { stopReason: "cancelled" };
26381
+ }
26382
+ throw error61;
26383
+ }
25334
26384
  } catch (error61) {
25335
26385
  const failedRuntime = this.#sessions.get(options.sessionId);
25336
26386
  if (failedRuntime !== void 0) {
@@ -25550,7 +26600,7 @@ var AcpSessionManager = class {
25550
26600
  };
25551
26601
 
25552
26602
  // src/agent-catalog/publish-agent-catalog.ts
25553
- import { fetch as fetch2 } from "undici";
26603
+ import { fetch as fetch3 } from "undici";
25554
26604
  var CATALOG_UPDATE_TIMEOUT_MILLISECONDS = 2e4;
25555
26605
  var AgentCatalogPublishError = class extends Error {
25556
26606
  code = "agent_catalog_update_failed";
@@ -25577,7 +26627,7 @@ async function publishAgentCatalog(config2, signal) {
25577
26627
  let response;
25578
26628
  const catalog = config2.catalog.read();
25579
26629
  try {
25580
- response = await fetch2(catalogEndpoint(config2.relayUrl), {
26630
+ response = await fetch3(catalogEndpoint(config2.relayUrl), {
25581
26631
  body: JSON.stringify({
25582
26632
  agents: catalog.agents.map((agent) => ({
25583
26633
  id: agent.id,
@@ -25983,7 +27033,7 @@ var InstalledAutomaticAdapter = class {
25983
27033
  }
25984
27034
  };
25985
27035
  function shouldProvisionAdapter(unavailable, options) {
25986
- return AUTOMATIC_ADAPTER_FAILURE_CODES.has(unavailable.code) && options.environment[unavailable.specification.adapterOverrideVariable] === void 0 && unavailable.specification.supportsInstallation(
27036
+ return unavailable.specification instanceof BridgedAcpAgentDefinition && AUTOMATIC_ADAPTER_FAILURE_CODES.has(unavailable.code) && options.environment[unavailable.specification.adapterOverrideVariable] === void 0 && unavailable.specification.supportsInstallation(
25987
27037
  options.platform ?? process.platform,
25988
27038
  options.architecture ?? process.arch
25989
27039
  );
@@ -26027,6 +27077,10 @@ var AutomaticAdapterProvisioner = class {
26027
27077
  for (const unavailable of candidates) {
26028
27078
  signal.throwIfAborted();
26029
27079
  const specification = unavailable.specification;
27080
+ if (!(specification instanceof BridgedAcpAgentDefinition)) {
27081
+ attempts.push(null);
27082
+ continue;
27083
+ }
26030
27084
  const verifiedAgent = await findVerifiedAgentExecutable(
26031
27085
  specification,
26032
27086
  discoveryOptions(
@@ -26088,270 +27142,77 @@ var AutomaticAdapterProvisioner = class {
26088
27142
  `
26089
27143
  );
26090
27144
  attempts.push(null);
26091
- }
26092
- }
26093
- signal.throwIfAborted();
26094
- const installed = attempts.filter(
26095
- (attempt) => attempt !== null
26096
- );
26097
- if (installed.length > 0) {
26098
- const verificationEnvironment = {
26099
- ...this.#options.environment
26100
- };
26101
- for (const attempt of installed) {
26102
- verificationEnvironment[attempt.specification.adapterOverrideVariable] = attempt.executable.path;
26103
- }
26104
- inventory = await discoverLocalAgentInventory(
26105
- discoveryOptions(
26106
- this.#options,
26107
- verificationEnvironment,
26108
- signal,
26109
- this.#specifications
26110
- )
26111
- );
26112
- }
26113
- this.#options.catalog.transition(
26114
- inventory.readyAgents(),
26115
- inventory.installable
26116
- );
26117
- for (const attempt of installed) {
26118
- const available = inventory.available.find(
26119
- (candidate) => candidate instanceof AvailableAgent && candidate.specification.id === attempt.specification.id
26120
- );
26121
- if (available === void 0) {
26122
- const unavailable = inventory.unavailable.find(
26123
- (candidate) => candidate.specification.id === attempt.specification.id
26124
- );
26125
- this.#options.audit.record(
26126
- "agent_adapter_auto_install_verification_failed",
26127
- attempt.requestId,
26128
- attempt.specification.id,
26129
- { code: unavailable?.code ?? "agent_verification_failed" }
26130
- );
26131
- continue;
26132
- }
26133
- this.#options.audit.record(
26134
- "agent_adapter_auto_install_verified",
26135
- attempt.requestId,
26136
- attempt.specification.id,
26137
- {
26138
- adapterVersion: available.agent.adapterVersion,
26139
- agentVersion: available.agent.agentVersion
26140
- }
26141
- );
26142
- process.stdout.write(
26143
- `${attempt.specification.label} ACP integration is ready.
26144
- `
26145
- );
26146
- }
26147
- return inventory;
26148
- };
26149
- };
26150
-
26151
- // src/agent-installation/fixed-installation-executor.ts
26152
- import { realpathSync as realpathSync2, statSync as statSync2 } from "node:fs";
26153
- import { resolve as resolve4 } from "node:path";
26154
-
26155
- // src/agent-installation/fixed-agent-install-recipes.ts
26156
- import { mkdir as mkdir3, mkdtemp as mkdtemp2, rm as rm2 } from "node:fs/promises";
26157
- import { join as join9 } from "node:path";
26158
-
26159
- // src/agent-installation/verified-download.ts
26160
- import { createHash } from "node:crypto";
26161
- import { open as open5, unlink } from "node:fs/promises";
26162
- import { fetch as fetch3 } from "undici";
26163
- var MAX_REDIRECTS = 4;
26164
- var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
26165
- var InstallerDownloadError = class extends Error {
26166
- code;
26167
- constructor(code, message, options = {}) {
26168
- super(message, options);
26169
- this.code = code;
26170
- this.name = "InstallerDownloadError";
26171
- }
26172
- };
26173
- var VerifiedDownload = class {
26174
- bytes;
26175
- sha256;
26176
- url;
26177
- constructor(url2, bytes, sha256) {
26178
- this.bytes = bytes;
26179
- this.sha256 = sha256;
26180
- this.url = url2;
26181
- }
26182
- };
26183
- var UndiciInstallerDownloadTransport = class {
26184
- get = async (url2, signal) => await fetch3(url2, {
26185
- dispatcher: environmentProxyDispatcher,
26186
- headers: {
26187
- accept: "text/plain, application/octet-stream;q=0.9",
26188
- "user-agent": "Mobius-Agent-Installer/0.0.3"
26189
- },
26190
- method: "GET",
26191
- redirect: "manual",
26192
- signal
26193
- });
26194
- };
26195
- function validateUrl(url2, allowedHosts) {
26196
- if (url2.protocol !== "https:" || url2.username.length > 0 || url2.password.length > 0 || url2.port.length > 0 || !allowedHosts.has(url2.hostname)) {
26197
- throw new InstallerDownloadError(
26198
- "installer_host_not_allowed",
26199
- "The installer download target is not allowed"
26200
- );
26201
- }
26202
- }
26203
- async function fetchInstaller(options, signal, transport) {
26204
- let url2 = new URL(options.url);
26205
- for (let redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount += 1) {
26206
- validateUrl(url2, options.allowedHosts);
26207
- const response = await transport.get(url2, signal);
26208
- if (!REDIRECT_STATUSES.has(response.status)) {
26209
- return { response, url: url2 };
26210
- }
26211
- await response.body?.cancel();
26212
- const location = response.headers.get("location");
26213
- if (location === null || redirectCount === MAX_REDIRECTS) {
26214
- throw new InstallerDownloadError(
26215
- "installer_redirect_invalid",
26216
- "The installer returned an invalid redirect"
26217
- );
26218
- }
26219
- let redirected;
26220
- try {
26221
- redirected = new URL(location, url2);
26222
- } catch (error61) {
26223
- throw new InstallerDownloadError(
26224
- "installer_redirect_invalid",
26225
- "The installer returned an invalid redirect",
26226
- { cause: error61 }
26227
- );
26228
- }
26229
- try {
26230
- validateUrl(redirected, options.allowedHosts);
26231
- } catch (error61) {
26232
- throw new InstallerDownloadError(
26233
- "installer_redirect_not_allowed",
26234
- "The installer redirect left the approved host allowlist",
26235
- { cause: error61 }
26236
- );
26237
- }
26238
- url2 = redirected;
26239
- }
26240
- throw new InstallerDownloadError(
26241
- "installer_redirect_invalid",
26242
- "The installer returned too many redirects"
26243
- );
26244
- }
26245
- async function writeCompleteChunk(handle, chunk) {
26246
- let offset = 0;
26247
- while (offset < chunk.byteLength) {
26248
- const written = await handle.write(
26249
- chunk,
26250
- offset,
26251
- chunk.byteLength - offset
27145
+ }
27146
+ }
27147
+ signal.throwIfAborted();
27148
+ const installed = attempts.filter(
27149
+ (attempt) => attempt !== null
26252
27150
  );
26253
- if (written.bytesWritten < 1) {
26254
- throw new InstallerDownloadError(
26255
- "installer_write_failed",
26256
- "The verified installer could not be stored"
27151
+ if (installed.length > 0) {
27152
+ const verificationEnvironment = {
27153
+ ...this.#options.environment
27154
+ };
27155
+ for (const attempt of installed) {
27156
+ verificationEnvironment[attempt.specification.adapterOverrideVariable] = attempt.executable.path;
27157
+ }
27158
+ inventory = await discoverLocalAgentInventory(
27159
+ discoveryOptions(
27160
+ this.#options,
27161
+ verificationEnvironment,
27162
+ signal,
27163
+ this.#specifications
27164
+ )
26257
27165
  );
26258
27166
  }
26259
- offset += written.bytesWritten;
26260
- }
26261
- }
26262
- async function downloadVerifiedFile(options, transport = new UndiciInstallerDownloadTransport()) {
26263
- options.signal.throwIfAborted();
26264
- const timeoutSignal = AbortSignal.timeout(options.timeoutMilliseconds);
26265
- const signal = AbortSignal.any([options.signal, timeoutSignal]);
26266
- let handle = null;
26267
- let responseBody = null;
26268
- try {
26269
- const fetched = await fetchInstaller(options, signal, transport);
26270
- responseBody = fetched.response.body;
26271
- if (!fetched.response.ok || fetched.response.body === null) {
26272
- throw new InstallerDownloadError(
26273
- "installer_download_failed",
26274
- "The approved installer could not be downloaded"
27167
+ this.#options.catalog.transition(
27168
+ inventory.readyAgents(),
27169
+ inventory.installable
27170
+ );
27171
+ for (const attempt of installed) {
27172
+ const available = inventory.available.find(
27173
+ (candidate) => candidate instanceof AvailableAgent && candidate.specification.id === attempt.specification.id
26275
27174
  );
26276
- }
26277
- const contentLength = fetched.response.headers.get("content-length");
26278
- if (contentLength !== null) {
26279
- const declaredLength = Number(contentLength);
26280
- if (!Number.isSafeInteger(declaredLength) || declaredLength < 1 || declaredLength > options.maximumBytes) {
26281
- throw new InstallerDownloadError(
26282
- "installer_size_invalid",
26283
- "The installer size is outside the approved limit"
27175
+ if (available === void 0) {
27176
+ const unavailable = inventory.unavailable.find(
27177
+ (candidate) => candidate.specification.id === attempt.specification.id
27178
+ );
27179
+ this.#options.audit.record(
27180
+ "agent_adapter_auto_install_verification_failed",
27181
+ attempt.requestId,
27182
+ attempt.specification.id,
27183
+ { code: unavailable?.code ?? "agent_verification_failed" }
26284
27184
  );
27185
+ continue;
26285
27186
  }
26286
- }
26287
- handle = await open5(options.destination, "wx", 384);
26288
- const digest = createHash("sha256");
26289
- const reader = fetched.response.body.getReader();
26290
- let bytes = 0;
26291
- try {
26292
- while (true) {
26293
- signal.throwIfAborted();
26294
- const chunk = await reader.read();
26295
- if (chunk.done) {
26296
- break;
26297
- }
26298
- bytes += chunk.value.byteLength;
26299
- if (bytes > options.maximumBytes) {
26300
- throw new InstallerDownloadError(
26301
- "installer_size_invalid",
26302
- "The installer exceeded the approved size limit"
26303
- );
27187
+ this.#options.audit.record(
27188
+ "agent_adapter_auto_install_verified",
27189
+ attempt.requestId,
27190
+ attempt.specification.id,
27191
+ {
27192
+ adapterVersion: available.agent.adapterVersion,
27193
+ agentVersion: available.agent.agentVersion
26304
27194
  }
26305
- digest.update(chunk.value);
26306
- await writeCompleteChunk(handle, chunk.value);
26307
- }
26308
- } finally {
26309
- await reader.cancel().catch(() => void 0);
26310
- reader.releaseLock();
26311
- }
26312
- if (bytes === 0) {
26313
- throw new InstallerDownloadError(
26314
- "installer_verification_failed",
26315
- "The downloaded installer is empty"
26316
27195
  );
26317
- }
26318
- await handle.sync();
26319
- await handle.close();
26320
- handle = null;
26321
- return new VerifiedDownload(fetched.url, bytes, digest.digest("hex"));
26322
- } catch (error61) {
26323
- if (handle !== null) {
26324
- await handle.close().catch(() => void 0);
26325
- }
26326
- await unlink(options.destination).catch(() => void 0);
26327
- if (options.signal.aborted) {
26328
- options.signal.throwIfAborted();
26329
- }
26330
- if (timeoutSignal.aborted) {
26331
- throw new InstallerDownloadError(
26332
- "installer_download_timeout",
26333
- "The installer download timed out",
26334
- { cause: error61 }
27196
+ process.stdout.write(
27197
+ `${attempt.specification.label} ACP integration is ready.
27198
+ `
26335
27199
  );
26336
27200
  }
26337
- if (error61 instanceof InstallerDownloadError) {
26338
- throw error61;
26339
- }
26340
- throw new InstallerDownloadError(
26341
- "installer_download_failed",
26342
- "The approved installer could not be downloaded",
26343
- { cause: error61 }
26344
- );
26345
- } finally {
26346
- await responseBody?.cancel().catch(() => void 0);
26347
- }
26348
- }
27201
+ return inventory;
27202
+ };
27203
+ };
27204
+
27205
+ // src/agent-installation/fixed-installation-executor.ts
27206
+ import { realpathSync as realpathSync2, statSync as statSync2 } from "node:fs";
27207
+ import { resolve as resolve4 } from "node:path";
26349
27208
 
26350
27209
  // src/agent-installation/fixed-agent-install-recipes.ts
27210
+ import { mkdir as mkdir4, mkdtemp as mkdtemp3, rm as rm3 } from "node:fs/promises";
27211
+ import { join as join14 } from "node:path";
26351
27212
  var INSTALLER_DOWNLOAD_LIMIT_BYTES = 1024 * 1024;
26352
27213
  var INSTALLER_DOWNLOAD_TIMEOUT_MILLISECONDS = 6e4;
26353
27214
  var AGENT_INSTALL_TIMEOUT_MILLISECONDS = 5 * 60 * 1e3;
26354
- var MINIMAL_PATH = "/usr/bin:/bin";
27215
+ var MINIMAL_PATH2 = "/usr/bin:/bin";
26355
27216
  var FixedAgentInstallRecipe = class {
26356
27217
  assertSupportedPlatform = () => {
26357
27218
  if (process.platform !== "darwin" && process.platform !== "linux" || process.arch !== "arm64" && process.arch !== "x64") {
@@ -26378,22 +27239,22 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
26378
27239
  releaseChannel = "latest";
26379
27240
  install = async (options) => {
26380
27241
  this.assertSupportedPlatform();
26381
- const root = join9(options.stateDirectory, "agents", this.id);
26382
- const temporaryRoot = join9(options.stateDirectory, "temporary");
27242
+ const root = join14(options.stateDirectory, "agents", this.id);
27243
+ const temporaryRoot = join14(options.stateDirectory, "temporary");
26383
27244
  await Promise.all([
26384
- mkdir3(root, { mode: 448, recursive: true }),
26385
- mkdir3(temporaryRoot, { mode: 448, recursive: true })
27245
+ mkdir4(root, { mode: 448, recursive: true }),
27246
+ mkdir4(temporaryRoot, { mode: 448, recursive: true })
26386
27247
  ]);
26387
- const temporaryDirectory = await mkdtemp2(
26388
- join9(temporaryRoot, ".codex-install-")
27248
+ const temporaryDirectory = await mkdtemp3(
27249
+ join14(temporaryRoot, ".codex-install-")
26389
27250
  );
26390
- const installerPath = join9(temporaryDirectory, "install.sh");
26391
- const installDirectory = join9(root, "bin");
26392
- const codexHome = join9(root, "home");
27251
+ const installerPath = join14(temporaryDirectory, "install.sh");
27252
+ const installDirectory = join14(root, "bin");
27253
+ const codexHome = join14(root, "home");
26393
27254
  try {
26394
27255
  await Promise.all([
26395
- mkdir3(installDirectory, { mode: 448, recursive: true }),
26396
- mkdir3(codexHome, { mode: 448, recursive: true })
27256
+ mkdir4(installDirectory, { mode: 448, recursive: true }),
27257
+ mkdir4(codexHome, { mode: 448, recursive: true })
26397
27258
  ]);
26398
27259
  await options.report("downloading");
26399
27260
  const download = await downloadVerifiedFile({
@@ -26426,14 +27287,14 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
26426
27287
  CODEX_NON_INTERACTIVE: "1",
26427
27288
  CODEX_RELEASE: this.releaseChannel,
26428
27289
  HOME: codexHome,
26429
- PATH: MINIMAL_PATH
27290
+ PATH: MINIMAL_PATH2
26430
27291
  },
26431
27292
  outputLimitBytes: 64 * 1024,
26432
27293
  signal: options.signal,
26433
27294
  timeoutMilliseconds: AGENT_INSTALL_TIMEOUT_MILLISECONDS
26434
27295
  });
26435
27296
  return await this.requireInstalledExecutable(
26436
- join9(installDirectory, "codex")
27297
+ join14(installDirectory, "codex")
26437
27298
  );
26438
27299
  } catch (error61) {
26439
27300
  if (options.signal.aborted) {
@@ -26447,7 +27308,7 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
26447
27308
  "The fixed Codex installation recipe failed"
26448
27309
  );
26449
27310
  } finally {
26450
- await rm2(temporaryDirectory, { force: true, recursive: true });
27311
+ await rm3(temporaryDirectory, { force: true, recursive: true });
26451
27312
  }
26452
27313
  };
26453
27314
  };
@@ -26457,17 +27318,17 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
26457
27318
  releaseChannel = "stable";
26458
27319
  install = async (options) => {
26459
27320
  this.assertSupportedPlatform();
26460
- const root = join9(options.stateDirectory, "agents", this.id);
26461
- const managedHome = join9(root, "home");
26462
- const temporaryRoot = join9(options.stateDirectory, "temporary");
27321
+ const root = join14(options.stateDirectory, "agents", this.id);
27322
+ const managedHome = join14(root, "home");
27323
+ const temporaryRoot = join14(options.stateDirectory, "temporary");
26463
27324
  await Promise.all([
26464
- mkdir3(managedHome, { mode: 448, recursive: true }),
26465
- mkdir3(temporaryRoot, { mode: 448, recursive: true })
27325
+ mkdir4(managedHome, { mode: 448, recursive: true }),
27326
+ mkdir4(temporaryRoot, { mode: 448, recursive: true })
26466
27327
  ]);
26467
- const temporaryDirectory = await mkdtemp2(
26468
- join9(temporaryRoot, ".claude-install-")
27328
+ const temporaryDirectory = await mkdtemp3(
27329
+ join14(temporaryRoot, ".claude-install-")
26469
27330
  );
26470
- const installerPath = join9(temporaryDirectory, "install.sh");
27331
+ const installerPath = join14(temporaryDirectory, "install.sh");
26471
27332
  try {
26472
27333
  await options.report("downloading");
26473
27334
  const download = await downloadVerifiedFile({
@@ -26496,7 +27357,7 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
26496
27357
  cwd: temporaryDirectory,
26497
27358
  environment: {
26498
27359
  HOME: managedHome,
26499
- PATH: MINIMAL_PATH,
27360
+ PATH: MINIMAL_PATH2,
26500
27361
  SHELL: "/bin/bash"
26501
27362
  },
26502
27363
  outputLimitBytes: 64 * 1024,
@@ -26504,7 +27365,7 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
26504
27365
  timeoutMilliseconds: AGENT_INSTALL_TIMEOUT_MILLISECONDS
26505
27366
  });
26506
27367
  return await this.requireInstalledExecutable(
26507
- join9(managedHome, ".local", "bin", "claude")
27368
+ join14(managedHome, ".local", "bin", "claude")
26508
27369
  );
26509
27370
  } catch (error61) {
26510
27371
  if (options.signal.aborted) {
@@ -26518,7 +27379,7 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
26518
27379
  "The fixed Claude Code installation recipe failed"
26519
27380
  );
26520
27381
  } finally {
26521
- await rm2(temporaryDirectory, { force: true, recursive: true });
27382
+ await rm3(temporaryDirectory, { force: true, recursive: true });
26522
27383
  }
26523
27384
  };
26524
27385
  };
@@ -26538,7 +27399,7 @@ var PiInstallRecipe = class extends FixedAgentInstallRecipe {
26538
27399
  await options.report("verifying");
26539
27400
  await options.report("installing_agent");
26540
27401
  const executable = await this.#npmInstaller.install(
26541
- join9(
27402
+ join14(
26542
27403
  options.stateDirectory,
26543
27404
  "agents",
26544
27405
  this.id,
@@ -26593,11 +27454,161 @@ var PiInstallRecipe = class extends FixedAgentInstallRecipe {
26593
27454
  }
26594
27455
  };
26595
27456
  };
27457
+ var DeepSeekHarnessInstallRecipe = class extends FixedAgentInstallRecipe {
27458
+ #npmInstaller;
27459
+ id = "deepseek-harness";
27460
+ label = "DeepSeek Harness";
27461
+ releaseChannel = DEEPSEEK_HARNESS_PACKAGE.version;
27462
+ constructor(npmInstaller = new ManagedNpmPackageInstaller()) {
27463
+ super();
27464
+ this.#npmInstaller = npmInstaller;
27465
+ }
27466
+ install = async (options) => {
27467
+ this.assertSupportedPlatform();
27468
+ try {
27469
+ const managedHome = join14(
27470
+ options.stateDirectory,
27471
+ "agents",
27472
+ this.id,
27473
+ "home"
27474
+ );
27475
+ await mkdir4(managedHome, { mode: 448, recursive: true });
27476
+ await options.report("downloading");
27477
+ await options.report("verifying");
27478
+ await options.report("installing_agent");
27479
+ const executable = await this.#npmInstaller.install(
27480
+ join14(
27481
+ options.stateDirectory,
27482
+ "agents",
27483
+ this.id,
27484
+ DEEPSEEK_HARNESS_PACKAGE.version
27485
+ ),
27486
+ DEEPSEEK_HARNESS_PACKAGE,
27487
+ options.environment,
27488
+ options.signal
27489
+ );
27490
+ options.audit.record(
27491
+ "agent_npm_package_verified",
27492
+ options.requestId,
27493
+ this.id,
27494
+ {
27495
+ integrity: DEEPSEEK_HARNESS_PACKAGE.integrity,
27496
+ package: DEEPSEEK_HARNESS_PACKAGE.packageName,
27497
+ version: DEEPSEEK_HARNESS_PACKAGE.version
27498
+ }
27499
+ );
27500
+ return executable;
27501
+ } catch (error61) {
27502
+ if (options.signal.aborted) {
27503
+ options.signal.throwIfAborted();
27504
+ }
27505
+ if (error61 instanceof ManagedNpmPackageError) {
27506
+ if (error61.code === "managed_package_verification_failed") {
27507
+ throw new InstallationManagerError(
27508
+ "agent_verification_failed",
27509
+ "The managed DeepSeek Harness package did not match its pinned artifact",
27510
+ { cause: error61 }
27511
+ );
27512
+ }
27513
+ if (error61.code === "managed_package_publish_failed") {
27514
+ throw new InstallationManagerError(
27515
+ "agent_publish_failed",
27516
+ "The managed DeepSeek Harness package could not be published",
27517
+ { cause: error61 }
27518
+ );
27519
+ }
27520
+ throw new InstallationManagerError(error61.code, error61.message, {
27521
+ cause: error61
27522
+ });
27523
+ }
27524
+ if (error61 instanceof InstallationManagerError) {
27525
+ throw error61;
27526
+ }
27527
+ throw new InstallationManagerError(
27528
+ "agent_install_failed",
27529
+ "The fixed DeepSeek Harness installation recipe failed",
27530
+ { cause: error61 }
27531
+ );
27532
+ }
27533
+ };
27534
+ };
27535
+ var OpenCodeInstallRecipe = class extends FixedAgentInstallRecipe {
27536
+ #archiveInstaller;
27537
+ id = "opencode";
27538
+ label = "OpenCode";
27539
+ releaseChannel = OPENCODE_MANAGED_VERSION;
27540
+ constructor(archiveInstaller = new ManagedAgentArchiveInstaller()) {
27541
+ super();
27542
+ this.#archiveInstaller = archiveInstaller;
27543
+ }
27544
+ install = async (options) => {
27545
+ this.assertSupportedPlatform();
27546
+ const definition = openCodeManagedArchive(process.platform, process.arch);
27547
+ try {
27548
+ await options.report("downloading");
27549
+ await options.report("verifying");
27550
+ await options.report("installing_agent");
27551
+ const executable = await this.#archiveInstaller.install(
27552
+ join14(
27553
+ options.stateDirectory,
27554
+ "agents",
27555
+ this.id,
27556
+ OPENCODE_MANAGED_VERSION
27557
+ ),
27558
+ definition,
27559
+ options.signal
27560
+ );
27561
+ options.audit.record(
27562
+ "agent_archive_verified",
27563
+ options.requestId,
27564
+ this.id,
27565
+ {
27566
+ sha256: definition.archiveSha256,
27567
+ version: definition.version
27568
+ }
27569
+ );
27570
+ return executable;
27571
+ } catch (error61) {
27572
+ if (options.signal.aborted) {
27573
+ options.signal.throwIfAborted();
27574
+ }
27575
+ if (error61 instanceof ManagedAgentArchiveError) {
27576
+ if (error61.code === "managed_archive_verification_failed") {
27577
+ throw new InstallationManagerError(
27578
+ "agent_verification_failed",
27579
+ "The managed OpenCode archive did not match its pinned artifact",
27580
+ { cause: error61 }
27581
+ );
27582
+ }
27583
+ if (error61.code === "managed_archive_publish_failed") {
27584
+ throw new InstallationManagerError(
27585
+ "agent_publish_failed",
27586
+ "The managed OpenCode archive could not be published",
27587
+ { cause: error61 }
27588
+ );
27589
+ }
27590
+ throw new InstallationManagerError(error61.code, error61.message, {
27591
+ cause: error61
27592
+ });
27593
+ }
27594
+ if (error61 instanceof InstallationManagerError) {
27595
+ throw error61;
27596
+ }
27597
+ throw new InstallationManagerError(
27598
+ "agent_install_failed",
27599
+ "The fixed OpenCode installation recipe failed",
27600
+ { cause: error61 }
27601
+ );
27602
+ }
27603
+ };
27604
+ };
26596
27605
  function fixedAgentInstallRecipes() {
26597
27606
  return Object.freeze([
26598
27607
  new CodexInstallRecipe(),
26599
27608
  new ClaudeCodeInstallRecipe(),
26600
- new PiInstallRecipe()
27609
+ new PiInstallRecipe(),
27610
+ new DeepSeekHarnessInstallRecipe(),
27611
+ new OpenCodeInstallRecipe()
26601
27612
  ]);
26602
27613
  }
26603
27614
 
@@ -26735,12 +27746,15 @@ var FixedInstallationExecutor = class {
26735
27746
  });
26736
27747
  installedAgent = true;
26737
27748
  }
26738
- let adapter = await resolveAdapterExecutable(
26739
- specification,
26740
- discoveryOptions2
26741
- );
27749
+ let adapter = specification instanceof DirectAcpAgentDefinition ? agent : await resolveAdapterExecutable(specification, discoveryOptions2);
26742
27750
  let installedAdapter = false;
26743
27751
  if (adapter === null) {
27752
+ if (!(specification instanceof BridgedAcpAgentDefinition)) {
27753
+ throw new InstallationManagerError(
27754
+ "agent_verification_failed",
27755
+ "The direct Agent executable is unavailable"
27756
+ );
27757
+ }
26744
27758
  await report("installing_adapter");
26745
27759
  adapter = await this.#adapterInstaller.install(specification, signal);
26746
27760
  installedAdapter = true;
@@ -26797,7 +27811,7 @@ var FixedInstallationExecutor = class {
26797
27811
  "The complete Agent ACP path did not pass verification"
26798
27812
  );
26799
27813
  }
26800
- if (!installedAdapter && requiresAdapterReplacement(unavailable.code)) {
27814
+ if (!installedAdapter && specification instanceof BridgedAcpAgentDefinition && requiresAdapterReplacement(unavailable.code)) {
26801
27815
  await report("installing_adapter");
26802
27816
  adapter = await this.#adapterInstaller.install(specification, signal);
26803
27817
  installedAdapter = true;
@@ -26816,6 +27830,9 @@ var FixedInstallationExecutor = class {
26816
27830
  stateDirectory: this.#stateDirectory
26817
27831
  });
26818
27832
  installedAgent = true;
27833
+ if (specification instanceof DirectAcpAgentDefinition) {
27834
+ adapter = agent;
27835
+ }
26819
27836
  continue;
26820
27837
  }
26821
27838
  throw new InstallationManagerError(
@@ -26877,15 +27894,15 @@ var LocalAuthorizationError = class extends Error {
26877
27894
  };
26878
27895
 
26879
27896
  // src/authorization/workspace-directory.ts
26880
- import { readdir, realpath as realpath4, stat as stat4 } from "node:fs/promises";
27897
+ import { readdir, realpath as realpath6, stat as stat6 } from "node:fs/promises";
26881
27898
  import { homedir as homedir2 } from "node:os";
26882
27899
  import {
26883
27900
  basename as basename2,
26884
- dirname as dirname4,
26885
- isAbsolute as isAbsolute3,
26886
- join as join10,
26887
- normalize,
26888
- relative as relative2
27901
+ dirname as dirname5,
27902
+ isAbsolute as isAbsolute5,
27903
+ join as join15,
27904
+ normalize as normalize2,
27905
+ relative as relative3
26889
27906
  } from "node:path";
26890
27907
  var WorkspaceDirectoryListing = class {
26891
27908
  directoryId;
@@ -26913,7 +27930,7 @@ function decodePath(value, prefix) {
26913
27930
  if (encoded.length === 0 || encoded.length > 2e3 || !/^[a-zA-Z0-9_-]+$/.test(encoded))
26914
27931
  throw new LocalAuthorizationError("The directory identifier is invalid");
26915
27932
  const decoded = Buffer.from(encoded, "base64url").toString("utf8");
26916
- if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded || decoded.includes("\0") || normalize(decoded) !== decoded)
27933
+ if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded || decoded.includes("\0") || normalize2(decoded) !== decoded)
26917
27934
  throw new LocalAuthorizationError("The directory identifier is invalid");
26918
27935
  return decoded;
26919
27936
  }
@@ -26935,20 +27952,20 @@ var WorkspaceDirectoryService = class {
26935
27952
  if (id === "home") return homedir2();
26936
27953
  if (id.startsWith("path-")) {
26937
27954
  const path2 = decodePath(id, "path-");
26938
- if (isAbsolute3(path2)) return path2;
27955
+ if (isAbsolute5(path2)) return path2;
26939
27956
  }
26940
27957
  throw new LocalAuthorizationError("The directory identifier is invalid");
26941
27958
  };
26942
27959
  resolvePath = async (requested, signal) => {
26943
27960
  signal.throwIfAborted();
26944
- if (!isAbsolute3(requested) || requested.includes("\0") || normalize(requested) !== requested)
27961
+ if (!isAbsolute5(requested) || requested.includes("\0") || normalize2(requested) !== requested)
26945
27962
  throw new LocalAuthorizationError("The local directory path is invalid");
26946
27963
  try {
26947
- const target = await realpath4(requested);
27964
+ const target = await realpath6(requested);
26948
27965
  signal.throwIfAborted();
26949
- const metadata = await stat4(target);
27966
+ const metadata = await stat6(target);
26950
27967
  signal.throwIfAborted();
26951
- if (!isAbsolute3(target) || normalize(target) !== target || !metadata.isDirectory())
27968
+ if (!isAbsolute5(target) || normalize2(target) !== target || !metadata.isDirectory())
26952
27969
  throw new LocalAuthorizationError(
26953
27970
  "The selected location is not a directory"
26954
27971
  );
@@ -26963,8 +27980,8 @@ var WorkspaceDirectoryService = class {
26963
27980
  }
26964
27981
  };
26965
27982
  label = (path2) => {
26966
- const suffix = relative2(homedir2(), path2);
26967
- return suffix === "" ? "~" : !isAbsolute3(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
27983
+ const suffix = relative3(homedir2(), path2);
27984
+ return suffix === "" ? "~" : !isAbsolute5(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
26968
27985
  };
26969
27986
  identify = async (bindingId, requestedPath, signal) => {
26970
27987
  this.authorizeMachine(bindingId);
@@ -26992,7 +28009,7 @@ var WorkspaceDirectoryService = class {
26992
28009
  continue;
26993
28010
  }
26994
28011
  try {
26995
- const path2 = await this.resolvePath(join10(target, child.name), signal);
28012
+ const path2 = await this.resolvePath(join15(target, child.name), signal);
26996
28013
  entries.push({ directoryId: encodePath(path2), name: child.name });
26997
28014
  } catch (cause) {
26998
28015
  signal.throwIfAborted();
@@ -27000,7 +28017,7 @@ var WorkspaceDirectoryService = class {
27000
28017
  }
27001
28018
  }
27002
28019
  entries.sort((a, b) => a.name.localeCompare(b.name));
27003
- const parent = dirname4(target);
28020
+ const parent = dirname5(target);
27004
28021
  return new WorkspaceDirectoryListing(
27005
28022
  id,
27006
28023
  this.label(target),
@@ -27115,7 +28132,8 @@ function decodeHistory(thread, value) {
27115
28132
  status: "available",
27116
28133
  messages,
27117
28134
  truncated,
27118
- coveredCommandIds: []
28135
+ coveredCommandIds: [],
28136
+ coveredMessageIds: []
27119
28137
  }
27120
28138
  };
27121
28139
  }
@@ -27792,7 +28810,7 @@ var RelayConnectionLiveness = class {
27792
28810
 
27793
28811
  // src/relay-connection/session-event-outbox.ts
27794
28812
  import { mkdirSync as mkdirSync3 } from "node:fs";
27795
- import { dirname as dirname5 } from "node:path";
28813
+ import { dirname as dirname6 } from "node:path";
27796
28814
  import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
27797
28815
  function readPendingEnvelope(row) {
27798
28816
  if (typeof row !== "object" || row === null || Array.isArray(row)) {
@@ -27807,7 +28825,7 @@ function readPendingEnvelope(row) {
27807
28825
  var SessionEventOutbox = class {
27808
28826
  #database;
27809
28827
  constructor(path2) {
27810
- mkdirSync3(dirname5(path2), { recursive: true });
28828
+ mkdirSync3(dirname6(path2), { recursive: true });
27811
28829
  this.#database = new DatabaseSync3(path2);
27812
28830
  this.#database.exec(`
27813
28831
  CREATE TABLE IF NOT EXISTS relay_session_event_outbox (
@@ -29064,7 +30082,7 @@ async function main() {
29064
30082
  if (arguments_.length > 1 || arguments_.length === 1 && arguments_[0] !== "--json") {
29065
30083
  throw new Error("Usage: mobius agents [--json]");
29066
30084
  }
29067
- await mkdir4(loadDefaultWorkspace(), { recursive: true, mode: 448 });
30085
+ await mkdir5(loadDefaultWorkspace(), { recursive: true, mode: 448 });
29068
30086
  const inventory = await discoverLocalAgentInventory({
29069
30087
  defaultWorkspace: loadDefaultWorkspace(),
29070
30088
  environment: process.env,