@moxt-ai/mobius 0.0.9 → 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.
- package/dist/acp/session-history.d.ts.map +1 -1
- package/dist/acp/session-history.js +1 -0
- package/dist/acp/session-history.js.map +1 -1
- package/dist/acp/session-manager.d.ts +4 -1
- package/dist/acp/session-manager.d.ts.map +1 -1
- package/dist/acp/session-manager.js +109 -25
- package/dist/acp/session-manager.js.map +1 -1
- package/dist/agent-catalog/acp-readiness-probe.d.ts +4 -0
- package/dist/agent-catalog/acp-readiness-probe.d.ts.map +1 -0
- package/dist/agent-catalog/acp-readiness-probe.js +153 -0
- package/dist/agent-catalog/acp-readiness-probe.js.map +1 -0
- package/dist/agent-catalog/agent-diagnostics.d.ts +1 -1
- package/dist/agent-catalog/agent-diagnostics.d.ts.map +1 -1
- package/dist/agent-catalog/agent-diagnostics.js +7 -3
- package/dist/agent-catalog/agent-diagnostics.js.map +1 -1
- package/dist/agent-catalog/agent-discovery-context.d.ts +8 -0
- package/dist/agent-catalog/agent-discovery-context.d.ts.map +1 -0
- package/dist/agent-catalog/agent-discovery-context.js +2 -0
- package/dist/agent-catalog/agent-discovery-context.js.map +1 -0
- package/dist/agent-catalog/agent-discovery.d.ts +55 -0
- package/dist/agent-catalog/agent-discovery.d.ts.map +1 -0
- package/dist/agent-catalog/agent-discovery.js +89 -0
- package/dist/agent-catalog/agent-discovery.js.map +1 -0
- package/dist/agent-catalog/agent-probe-error.d.ts +5 -0
- package/dist/agent-catalog/agent-probe-error.d.ts.map +1 -0
- package/dist/agent-catalog/agent-probe-error.js +9 -0
- package/dist/agent-catalog/agent-probe-error.js.map +1 -0
- package/dist/agent-catalog/agent-specification.d.ts +45 -0
- package/dist/agent-catalog/agent-specification.d.ts.map +1 -0
- package/dist/agent-catalog/agent-specification.js +59 -0
- package/dist/agent-catalog/agent-specification.js.map +1 -0
- package/dist/agent-catalog/agents/claude-code-agent.d.ts +14 -0
- package/dist/agent-catalog/agents/claude-code-agent.d.ts.map +1 -0
- package/dist/agent-catalog/agents/claude-code-agent.js +30 -0
- package/dist/agent-catalog/agents/claude-code-agent.js.map +1 -0
- package/dist/agent-catalog/agents/codex-agent.d.ts +14 -0
- package/dist/agent-catalog/agents/codex-agent.d.ts.map +1 -0
- package/dist/agent-catalog/agents/codex-agent.js +23 -0
- package/dist/agent-catalog/agents/codex-agent.js.map +1 -0
- package/dist/agent-catalog/agents/deepseek-harness-agent.d.ts +16 -0
- package/dist/agent-catalog/agents/deepseek-harness-agent.d.ts.map +1 -0
- package/dist/agent-catalog/agents/deepseek-harness-agent.js +94 -0
- package/dist/agent-catalog/agents/deepseek-harness-agent.js.map +1 -0
- package/dist/agent-catalog/agents/opencode-agent.d.ts +17 -0
- package/dist/agent-catalog/agents/opencode-agent.d.ts.map +1 -0
- package/dist/agent-catalog/agents/opencode-agent.js +36 -0
- package/dist/agent-catalog/agents/opencode-agent.js.map +1 -0
- package/dist/agent-catalog/agents/pi-agent.d.ts +21 -0
- package/dist/agent-catalog/agents/pi-agent.d.ts.map +1 -0
- package/dist/agent-catalog/agents/pi-agent.js +69 -0
- package/dist/agent-catalog/agents/pi-agent.js.map +1 -0
- package/dist/agent-catalog/agents/supported-agent-specifications.d.ts +3 -0
- package/dist/agent-catalog/agents/supported-agent-specifications.d.ts.map +1 -0
- package/dist/agent-catalog/agents/supported-agent-specifications.js +15 -0
- package/dist/agent-catalog/agents/supported-agent-specifications.js.map +1 -0
- package/dist/agent-catalog/discover-agent.d.ts +5 -0
- package/dist/agent-catalog/discover-agent.d.ts.map +1 -0
- package/dist/agent-catalog/discover-agent.js +100 -0
- package/dist/agent-catalog/discover-agent.js.map +1 -0
- package/dist/agent-catalog/discover-local-agents.d.ts +12 -73
- package/dist/agent-catalog/discover-local-agents.d.ts.map +1 -1
- package/dist/agent-catalog/discover-local-agents.js +11 -453
- package/dist/agent-catalog/discover-local-agents.js.map +1 -1
- package/dist/agent-catalog/executable-discovery.d.ts +16 -0
- package/dist/agent-catalog/executable-discovery.d.ts.map +1 -0
- package/dist/agent-catalog/executable-discovery.js +105 -0
- package/dist/agent-catalog/executable-discovery.js.map +1 -0
- package/dist/agent-catalog/local-agent.d.ts +2 -1
- package/dist/agent-catalog/local-agent.d.ts.map +1 -1
- package/dist/agent-catalog/local-agent.js +7 -1
- package/dist/agent-catalog/local-agent.js.map +1 -1
- package/dist/agent-installation/agent-installation-manager.d.ts +1 -1
- package/dist/agent-installation/agent-installation-manager.d.ts.map +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.d.ts +2 -1
- package/dist/agent-installation/automatic-adapter-provisioner.d.ts.map +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.js +11 -2
- package/dist/agent-installation/automatic-adapter-provisioner.js.map +1 -1
- package/dist/agent-installation/deepseek-managed-package.d.ts +9 -0
- package/dist/agent-installation/deepseek-managed-package.d.ts.map +1 -0
- package/dist/agent-installation/deepseek-managed-package.js +10 -0
- package/dist/agent-installation/deepseek-managed-package.js.map +1 -0
- package/dist/agent-installation/fixed-agent-install-recipes.d.ts +19 -0
- package/dist/agent-installation/fixed-agent-install-recipes.d.ts.map +1 -1
- package/dist/agent-installation/fixed-agent-install-recipes.js +145 -0
- package/dist/agent-installation/fixed-agent-install-recipes.js.map +1 -1
- package/dist/agent-installation/fixed-installation-executor.d.ts +1 -1
- package/dist/agent-installation/fixed-installation-executor.d.ts.map +1 -1
- package/dist/agent-installation/fixed-installation-executor.js +19 -3
- package/dist/agent-installation/fixed-installation-executor.js.map +1 -1
- package/dist/agent-installation/managed-adapter-installer.d.ts +7 -6
- package/dist/agent-installation/managed-adapter-installer.d.ts.map +1 -1
- package/dist/agent-installation/managed-adapter-installer.js +25 -126
- package/dist/agent-installation/managed-adapter-installer.js.map +1 -1
- package/dist/agent-installation/managed-agent-archive.d.ts +40 -0
- package/dist/agent-installation/managed-agent-archive.d.ts.map +1 -0
- package/dist/agent-installation/managed-agent-archive.js +274 -0
- package/dist/agent-installation/managed-agent-archive.js.map +1 -0
- package/dist/agent-installation/managed-npm-package.d.ts +27 -0
- package/dist/agent-installation/managed-npm-package.d.ts.map +1 -0
- package/dist/agent-installation/managed-npm-package.js +258 -0
- package/dist/agent-installation/managed-npm-package.js.map +1 -0
- package/dist/agent-installation/opencode-managed-release.d.ts +4 -0
- package/dist/agent-installation/opencode-managed-release.d.ts.map +1 -0
- package/dist/agent-installation/opencode-managed-release.js +25 -0
- package/dist/agent-installation/opencode-managed-release.js.map +1 -0
- package/dist/agent-installation/pi-managed-packages.d.ts +4 -0
- package/dist/agent-installation/pi-managed-packages.d.ts.map +1 -0
- package/dist/agent-installation/pi-managed-packages.js +4 -0
- package/dist/agent-installation/pi-managed-packages.js.map +1 -0
- package/dist/cli.js +2419 -950
- package/dist/cli.js.map +4 -4
- package/dist/index.js +2138 -669
- package/dist/index.js.map +4 -4
- package/dist/native-sessions/codex-history.d.ts.map +1 -1
- package/dist/native-sessions/codex-history.js +1 -0
- package/dist/native-sessions/codex-history.js.map +1 -1
- package/dist/process/executable.d.ts +6 -1
- package/dist/process/executable.d.ts.map +1 -1
- package/dist/process/executable.js +150 -7
- package/dist/process/executable.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,96 +6,24 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
|
-
import { mkdir as
|
|
9
|
+
import { mkdir as mkdir5 } from "node:fs/promises";
|
|
10
10
|
|
|
11
|
-
// src/agent-catalog/
|
|
12
|
-
import {
|
|
13
|
-
import { isAbsolute, join as join5 } from "node:path";
|
|
14
|
-
import {
|
|
15
|
-
methods as methods2,
|
|
16
|
-
ndJsonStream,
|
|
17
|
-
PROTOCOL_VERSION
|
|
18
|
-
} from "@agentclientprotocol/sdk";
|
|
19
|
-
|
|
20
|
-
// src/acp/create-client.ts
|
|
21
|
-
import {
|
|
22
|
-
client,
|
|
23
|
-
methods
|
|
24
|
-
} from "@agentclientprotocol/sdk";
|
|
25
|
-
var createAcpClient = (handler) => client({ name: "mobius" }).onRequest(
|
|
26
|
-
methods.client.session.requestPermission,
|
|
27
|
-
async (context) => await handler.onPermission(context.params, context.signal)
|
|
28
|
-
).onRequest(
|
|
29
|
-
methods.client.elicitation.create,
|
|
30
|
-
async (context) => await handler.onElicitation(context.params, context.signal)
|
|
31
|
-
).onNotification(methods.client.session.update, async (context) => {
|
|
32
|
-
await handler.onUpdate(context.params);
|
|
33
|
-
}).onNotification(methods.client.elicitation.complete, async (context) => {
|
|
34
|
-
await handler.onElicitationComplete(context.params);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// src/agent-installation/installation-grants.ts
|
|
38
|
-
import { open } from "node:fs/promises";
|
|
39
|
-
import { join } from "node:path";
|
|
40
|
-
var MAX_GRANT_FILE_LENGTH = 16 * 1024;
|
|
41
|
-
var AGENT_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
|
|
42
|
-
function isRecord(value) {
|
|
43
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
44
|
-
}
|
|
45
|
-
async function readGrantedAgents(stateDirectory) {
|
|
46
|
-
const path2 = join(stateDirectory, "installation-grants.json");
|
|
47
|
-
let handle;
|
|
48
|
-
try {
|
|
49
|
-
handle = await open(path2, "r");
|
|
50
|
-
} catch (error61) {
|
|
51
|
-
if (error61 instanceof Error && "code" in error61 && error61.code === "ENOENT") {
|
|
52
|
-
return /* @__PURE__ */ new Set();
|
|
53
|
-
}
|
|
54
|
-
throw error61;
|
|
55
|
-
}
|
|
56
|
-
try {
|
|
57
|
-
const metadata = await handle.stat();
|
|
58
|
-
if (!metadata.isFile() || metadata.size > MAX_GRANT_FILE_LENGTH || typeof process.getuid === "function" && metadata.uid !== process.getuid() || process.platform !== "win32" && (metadata.mode & 63) !== 0) {
|
|
59
|
-
throw new Error("The local installation grant file is not private");
|
|
60
|
-
}
|
|
61
|
-
const encoded = await handle.readFile({ encoding: "utf8" });
|
|
62
|
-
const value = JSON.parse(encoded);
|
|
63
|
-
if (!isRecord(value) || Object.keys(value).length !== 1 || !Array.isArray(value["agents"])) {
|
|
64
|
-
throw new Error("The local installation grant file is invalid");
|
|
65
|
-
}
|
|
66
|
-
const agents = /* @__PURE__ */ new Set();
|
|
67
|
-
for (const candidate of value["agents"]) {
|
|
68
|
-
if (typeof candidate !== "string" || !AGENT_ID_PATTERN.test(candidate) || agents.has(candidate)) {
|
|
69
|
-
throw new Error("The local installation grant file is invalid");
|
|
70
|
-
}
|
|
71
|
-
agents.add(candidate);
|
|
72
|
-
}
|
|
73
|
-
return agents;
|
|
74
|
-
} finally {
|
|
75
|
-
await handle.close();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// src/agent-installation/managed-adapter-installer.ts
|
|
80
|
-
import {
|
|
81
|
-
mkdir,
|
|
82
|
-
mkdtemp,
|
|
83
|
-
open as open2,
|
|
84
|
-
realpath as realpath2,
|
|
85
|
-
rename,
|
|
86
|
-
rm,
|
|
87
|
-
stat as stat2
|
|
88
|
-
} from "node:fs/promises";
|
|
89
|
-
import { delimiter, dirname, join as join3, relative } from "node:path";
|
|
11
|
+
// src/agent-catalog/local-agent.ts
|
|
12
|
+
import { realpathSync, statSync } from "node:fs";
|
|
90
13
|
|
|
91
14
|
// src/process/executable.ts
|
|
92
|
-
import { spawn } from "node:child_process";
|
|
15
|
+
import { execFile, spawn } from "node:child_process";
|
|
93
16
|
import { constants } from "node:fs";
|
|
94
17
|
import { access, realpath, stat } from "node:fs/promises";
|
|
95
|
-
import { join
|
|
18
|
+
import { join, resolve } from "node:path";
|
|
96
19
|
var DEFAULT_OUTPUT_LIMIT_BYTES = 16 * 1024;
|
|
97
20
|
var PROCESS_GRACEFUL_STOP_TIMEOUT_MILLISECONDS = 1e3;
|
|
98
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;
|
|
99
27
|
var ExecutableIdentity = class {
|
|
100
28
|
changedAtMilliseconds;
|
|
101
29
|
device;
|
|
@@ -189,7 +117,7 @@ async function findExecutableOnPath(executableName, environment, platform = proc
|
|
|
189
117
|
}
|
|
190
118
|
for (const extension of extensions) {
|
|
191
119
|
const requestedPath = resolve(
|
|
192
|
-
|
|
120
|
+
join(entry, `${executableName}${extension}`)
|
|
193
121
|
);
|
|
194
122
|
const executable = await resolveExecutablePath(requestedPath, platform);
|
|
195
123
|
if (executable !== null) {
|
|
@@ -214,6 +142,119 @@ function appendBounded(chunks, chunk, currentBytes, limitBytes) {
|
|
|
214
142
|
chunks.push(chunk);
|
|
215
143
|
return nextBytes;
|
|
216
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
|
+
}
|
|
217
258
|
function terminateProcess(child) {
|
|
218
259
|
if (child.exitCode !== null || child.signalCode !== null || child.pid === void 0) {
|
|
219
260
|
return;
|
|
@@ -240,24 +281,35 @@ function forceTerminateProcess(child) {
|
|
|
240
281
|
}
|
|
241
282
|
child.kill("SIGKILL");
|
|
242
283
|
}
|
|
243
|
-
async function stopProcessTree(child) {
|
|
244
|
-
|
|
284
|
+
async function stopProcessTree(child, capturedProcesses = []) {
|
|
285
|
+
const childProcessId = child.pid;
|
|
286
|
+
if (childProcessId === void 0) {
|
|
245
287
|
return;
|
|
246
288
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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)
|
|
251
296
|
);
|
|
252
|
-
|
|
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) {
|
|
253
304
|
return;
|
|
254
305
|
}
|
|
306
|
+
signalDetachedProcessGroups(processes, childProcessId, FORCED_STOP_SIGNAL);
|
|
255
307
|
forceTerminateProcess(child);
|
|
256
|
-
const exitedAfterForce = await
|
|
257
|
-
child,
|
|
258
|
-
|
|
259
|
-
);
|
|
260
|
-
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) {
|
|
261
313
|
throw new ProcessExecutionError(
|
|
262
314
|
"process_termination_timeout",
|
|
263
315
|
"The executable process could not be stopped"
|
|
@@ -424,209 +476,7 @@ async function runBoundedProcess(options) {
|
|
|
424
476
|
}
|
|
425
477
|
}
|
|
426
478
|
|
|
427
|
-
// src/agent-installation/managed-adapter-installer.ts
|
|
428
|
-
var ADAPTER_INSTALL_TIMEOUT_MILLISECONDS = 2 * 60 * 1e3;
|
|
429
|
-
var PACKAGE_METADATA_LIMIT_BYTES = 64 * 1024;
|
|
430
|
-
var ManagedAdapterInstallError = class extends Error {
|
|
431
|
-
code;
|
|
432
|
-
constructor(code, message, options = {}) {
|
|
433
|
-
super(message, options);
|
|
434
|
-
this.code = code;
|
|
435
|
-
this.name = "ManagedAdapterInstallError";
|
|
436
|
-
}
|
|
437
|
-
};
|
|
438
|
-
function isRecord2(value) {
|
|
439
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
440
|
-
}
|
|
441
|
-
function packagePath(root, packageName) {
|
|
442
|
-
return join3(root, "node_modules", ...packageName.split("/"));
|
|
443
|
-
}
|
|
444
|
-
async function readPackageJson(path2) {
|
|
445
|
-
const handle = await open2(path2, "r");
|
|
446
|
-
try {
|
|
447
|
-
const metadata = await handle.stat();
|
|
448
|
-
if (!metadata.isFile() || metadata.size > PACKAGE_METADATA_LIMIT_BYTES) {
|
|
449
|
-
throw new ManagedAdapterInstallError(
|
|
450
|
-
"adapter_verification_failed",
|
|
451
|
-
"The managed adapter package metadata is invalid"
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
const value = JSON.parse(
|
|
455
|
-
await handle.readFile({ encoding: "utf8" })
|
|
456
|
-
);
|
|
457
|
-
if (!isRecord2(value)) {
|
|
458
|
-
throw new ManagedAdapterInstallError(
|
|
459
|
-
"adapter_verification_failed",
|
|
460
|
-
"The managed adapter package metadata is invalid"
|
|
461
|
-
);
|
|
462
|
-
}
|
|
463
|
-
return value;
|
|
464
|
-
} finally {
|
|
465
|
-
await handle.close();
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
function readBinPath(packageMetadata, executableName) {
|
|
469
|
-
const bin = packageMetadata["bin"];
|
|
470
|
-
if (typeof bin === "string") {
|
|
471
|
-
return bin;
|
|
472
|
-
}
|
|
473
|
-
if (isRecord2(bin) && typeof bin[executableName] === "string") {
|
|
474
|
-
return bin[executableName];
|
|
475
|
-
}
|
|
476
|
-
throw new ManagedAdapterInstallError(
|
|
477
|
-
"adapter_verification_failed",
|
|
478
|
-
"The managed adapter executable declaration is invalid"
|
|
479
|
-
);
|
|
480
|
-
}
|
|
481
|
-
async function validateManagedAdapter(root, specification) {
|
|
482
|
-
const rootPath = await realpath2(root);
|
|
483
|
-
const packageRoot = await realpath2(
|
|
484
|
-
packagePath(rootPath, specification.adapterPackage)
|
|
485
|
-
);
|
|
486
|
-
const packageDifference = relative(rootPath, packageRoot);
|
|
487
|
-
if (packageDifference.startsWith("..") || packageDifference.length === 0) {
|
|
488
|
-
throw new ManagedAdapterInstallError(
|
|
489
|
-
"adapter_verification_failed",
|
|
490
|
-
"The managed adapter package escaped its private cache"
|
|
491
|
-
);
|
|
492
|
-
}
|
|
493
|
-
const metadata = await readPackageJson(join3(packageRoot, "package.json"));
|
|
494
|
-
if (metadata["name"] !== specification.adapterPackage || metadata["version"] !== specification.adapterVersion) {
|
|
495
|
-
throw new ManagedAdapterInstallError(
|
|
496
|
-
"adapter_verification_failed",
|
|
497
|
-
"The managed adapter package identity is invalid"
|
|
498
|
-
);
|
|
499
|
-
}
|
|
500
|
-
const declaredBin = readBinPath(metadata, specification.adapterExecutable);
|
|
501
|
-
const executable = await resolveExecutablePath(
|
|
502
|
-
join3(packageRoot, declaredBin)
|
|
503
|
-
);
|
|
504
|
-
if (executable === null) {
|
|
505
|
-
throw new ManagedAdapterInstallError(
|
|
506
|
-
"adapter_verification_failed",
|
|
507
|
-
"The managed adapter executable is invalid"
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
const executableDifference = relative(rootPath, executable.path);
|
|
511
|
-
if (executableDifference.startsWith("..") || executableDifference.length === 0) {
|
|
512
|
-
throw new ManagedAdapterInstallError(
|
|
513
|
-
"adapter_verification_failed",
|
|
514
|
-
"The managed adapter executable escaped its private cache"
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
return executable;
|
|
518
|
-
}
|
|
519
|
-
var ManagedAdapterInstaller = class {
|
|
520
|
-
#environment;
|
|
521
|
-
#stateDirectory;
|
|
522
|
-
constructor(options) {
|
|
523
|
-
this.#environment = options.environment;
|
|
524
|
-
this.#stateDirectory = options.stateDirectory;
|
|
525
|
-
}
|
|
526
|
-
install = async (specification, signal) => {
|
|
527
|
-
signal.throwIfAborted();
|
|
528
|
-
const agentCache = join3(this.#stateDirectory, "adapters", specification.id);
|
|
529
|
-
const published = join3(agentCache, specification.adapterVersion);
|
|
530
|
-
try {
|
|
531
|
-
const metadata = await stat2(published);
|
|
532
|
-
if (metadata.isDirectory()) {
|
|
533
|
-
return await validateManagedAdapter(published, specification);
|
|
534
|
-
}
|
|
535
|
-
} catch (error61) {
|
|
536
|
-
if (!(error61 instanceof Error) || !("code" in error61) || error61.code !== "ENOENT") {
|
|
537
|
-
throw error61;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
await mkdir(agentCache, { mode: 448, recursive: true });
|
|
541
|
-
const staging = await mkdtemp(
|
|
542
|
-
join3(agentCache, `.${specification.adapterVersion}.staging-`)
|
|
543
|
-
);
|
|
544
|
-
try {
|
|
545
|
-
const npm = await findExecutableOnPath(
|
|
546
|
-
process.platform === "win32" ? "npm.cmd" : "npm",
|
|
547
|
-
this.#environment
|
|
548
|
-
);
|
|
549
|
-
if (npm === null) {
|
|
550
|
-
throw new ManagedAdapterInstallError(
|
|
551
|
-
"package_manager_not_found",
|
|
552
|
-
"A supported npm executable is required to install the ACP adapter"
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
await runBoundedProcess({
|
|
556
|
-
arguments: [
|
|
557
|
-
"install",
|
|
558
|
-
"--prefix",
|
|
559
|
-
staging,
|
|
560
|
-
"--omit=optional",
|
|
561
|
-
"--ignore-scripts",
|
|
562
|
-
"--no-audit",
|
|
563
|
-
"--no-fund",
|
|
564
|
-
"--package-lock=false",
|
|
565
|
-
"--save=false",
|
|
566
|
-
"--registry=https://registry.npmjs.org/",
|
|
567
|
-
`${specification.adapterPackage}@${specification.adapterVersion}`
|
|
568
|
-
],
|
|
569
|
-
command: npm.path,
|
|
570
|
-
cwd: staging,
|
|
571
|
-
environment: {
|
|
572
|
-
HOME: join3(staging, "home"),
|
|
573
|
-
NPM_CONFIG_AUDIT: "false",
|
|
574
|
-
NPM_CONFIG_FUND: "false",
|
|
575
|
-
NPM_CONFIG_IGNORE_SCRIPTS: "true",
|
|
576
|
-
NPM_CONFIG_OMIT: "optional",
|
|
577
|
-
NPM_CONFIG_REGISTRY: "https://registry.npmjs.org/",
|
|
578
|
-
PATH: `${dirname(process.execPath)}${delimiter}/usr/bin${delimiter}/bin`
|
|
579
|
-
},
|
|
580
|
-
outputLimitBytes: 64 * 1024,
|
|
581
|
-
signal,
|
|
582
|
-
timeoutMilliseconds: ADAPTER_INSTALL_TIMEOUT_MILLISECONDS
|
|
583
|
-
});
|
|
584
|
-
await validateManagedAdapter(staging, specification);
|
|
585
|
-
try {
|
|
586
|
-
await rename(staging, published);
|
|
587
|
-
} catch (error61) {
|
|
588
|
-
try {
|
|
589
|
-
return await validateManagedAdapter(published, specification);
|
|
590
|
-
} catch {
|
|
591
|
-
throw new ManagedAdapterInstallError(
|
|
592
|
-
"adapter_publish_failed",
|
|
593
|
-
"The managed adapter could not be published",
|
|
594
|
-
{ cause: error61 }
|
|
595
|
-
);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
return await validateManagedAdapter(published, specification);
|
|
599
|
-
} catch (error61) {
|
|
600
|
-
if (signal.aborted) {
|
|
601
|
-
signal.throwIfAborted();
|
|
602
|
-
}
|
|
603
|
-
if (error61 instanceof ManagedAdapterInstallError) {
|
|
604
|
-
throw error61;
|
|
605
|
-
}
|
|
606
|
-
throw new ManagedAdapterInstallError(
|
|
607
|
-
"adapter_install_failed",
|
|
608
|
-
"The pinned ACP adapter could not be installed",
|
|
609
|
-
{ cause: error61 }
|
|
610
|
-
);
|
|
611
|
-
} finally {
|
|
612
|
-
await rm(staging, { force: true, recursive: true });
|
|
613
|
-
}
|
|
614
|
-
};
|
|
615
|
-
};
|
|
616
|
-
|
|
617
|
-
// src/storage/state-directory.ts
|
|
618
|
-
import { homedir } from "node:os";
|
|
619
|
-
import { join as join4, resolve as resolve2 } from "node:path";
|
|
620
|
-
function readStateDirectory(environment = process.env) {
|
|
621
|
-
const configured = environment["MOBIUS_STATE_DIRECTORY"]?.trim();
|
|
622
|
-
if (configured !== void 0 && configured.length > 0) {
|
|
623
|
-
return resolve2(configured);
|
|
624
|
-
}
|
|
625
|
-
return join4(homedir(), ".mobius");
|
|
626
|
-
}
|
|
627
|
-
|
|
628
479
|
// src/agent-catalog/local-agent.ts
|
|
629
|
-
import { realpathSync, statSync } from "node:fs";
|
|
630
480
|
function readTrustedIdentity(path2) {
|
|
631
481
|
const resolvedPath = realpathSync(path2);
|
|
632
482
|
const metadata = statSync(resolvedPath);
|
|
@@ -650,7 +500,11 @@ var LocalAgentDefinition = class {
|
|
|
650
500
|
environment;
|
|
651
501
|
id;
|
|
652
502
|
label;
|
|
653
|
-
|
|
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
|
+
}
|
|
654
508
|
this.adapterIdentity = adapterIdentity;
|
|
655
509
|
this.adapterPath = adapterIdentity.realPath;
|
|
656
510
|
this.adapterVersion = adapterVersion;
|
|
@@ -658,6 +512,7 @@ var LocalAgentDefinition = class {
|
|
|
658
512
|
this.agentVersion = agentVersion;
|
|
659
513
|
this.id = id;
|
|
660
514
|
this.label = label;
|
|
515
|
+
this.promptTimeoutMilliseconds = promptTimeoutMilliseconds;
|
|
661
516
|
this.command = adapterIdentity.realPath;
|
|
662
517
|
this.arguments = Object.freeze([...arguments_]);
|
|
663
518
|
this.environment = Object.freeze({ ...environment });
|
|
@@ -725,71 +580,7 @@ var LocalAgentCatalog = class {
|
|
|
725
580
|
};
|
|
726
581
|
};
|
|
727
582
|
|
|
728
|
-
// src/agent-catalog/
|
|
729
|
-
var VERSION_PROBE_TIMEOUT_MILLISECONDS = 3e3;
|
|
730
|
-
var ACP_PROBE_TIMEOUT_MILLISECONDS = 5e3;
|
|
731
|
-
var ACP_OUTPUT_LIMIT_BYTES = 1024 * 1024;
|
|
732
|
-
var LocalAgentSpecification = class {
|
|
733
|
-
recoveryCommand = () => `npm install -g --omit=optional ${this.adapterPackage}@${this.adapterVersion}`;
|
|
734
|
-
supportsInstallation = (platform, architecture) => (platform === "darwin" || platform === "linux") && (architecture === "arm64" || architecture === "x64");
|
|
735
|
-
};
|
|
736
|
-
var CodexAgentSpecification = class extends LocalAgentSpecification {
|
|
737
|
-
adapterEnvironmentVariable = "CODEX_PATH";
|
|
738
|
-
adapterExecutable = "codex-acp";
|
|
739
|
-
adapterOverrideVariable = "MOBIUS_CODEX_ACP_PATH";
|
|
740
|
-
adapterPackage = "@agentclientprotocol/codex-acp";
|
|
741
|
-
adapterVersion = "1.8.0";
|
|
742
|
-
agentExecutable = "codex";
|
|
743
|
-
agentOverrideVariable = "MOBIUS_CODEX_PATH";
|
|
744
|
-
id = "codex";
|
|
745
|
-
label = "Codex";
|
|
746
|
-
managedAdapterExecutablePath = [
|
|
747
|
-
"adapters",
|
|
748
|
-
"codex",
|
|
749
|
-
"1.8.0",
|
|
750
|
-
"node_modules",
|
|
751
|
-
"@agentclientprotocol",
|
|
752
|
-
"codex-acp",
|
|
753
|
-
"dist",
|
|
754
|
-
"index.js"
|
|
755
|
-
];
|
|
756
|
-
managedAgentExecutablePath = ["agents", "codex", "bin", "codex"];
|
|
757
|
-
};
|
|
758
|
-
var ClaudeCodeAgentSpecification = class extends LocalAgentSpecification {
|
|
759
|
-
adapterEnvironmentVariable = "CLAUDE_CODE_EXECUTABLE";
|
|
760
|
-
adapterExecutable = "claude-agent-acp";
|
|
761
|
-
adapterOverrideVariable = "MOBIUS_CLAUDE_ACP_PATH";
|
|
762
|
-
adapterPackage = "@agentclientprotocol/claude-agent-acp";
|
|
763
|
-
adapterVersion = "0.73.0";
|
|
764
|
-
agentExecutable = "claude";
|
|
765
|
-
agentOverrideVariable = "MOBIUS_CLAUDE_PATH";
|
|
766
|
-
id = "claude";
|
|
767
|
-
label = "Claude Code";
|
|
768
|
-
managedAdapterExecutablePath = [
|
|
769
|
-
"adapters",
|
|
770
|
-
"claude",
|
|
771
|
-
"0.73.0",
|
|
772
|
-
"node_modules",
|
|
773
|
-
"@agentclientprotocol",
|
|
774
|
-
"claude-agent-acp",
|
|
775
|
-
"dist",
|
|
776
|
-
"index.js"
|
|
777
|
-
];
|
|
778
|
-
managedAgentExecutablePath = [
|
|
779
|
-
"agents",
|
|
780
|
-
"claude",
|
|
781
|
-
"home",
|
|
782
|
-
".local",
|
|
783
|
-
"bin",
|
|
784
|
-
"claude"
|
|
785
|
-
];
|
|
786
|
-
};
|
|
787
|
-
function supportedLocalAgentSpecifications() {
|
|
788
|
-
return Object.freeze([
|
|
789
|
-
new CodexAgentSpecification(),
|
|
790
|
-
new ClaudeCodeAgentSpecification()
|
|
791
|
-
]);
|
|
792
|
-
}
|
|
583
|
+
// src/agent-catalog/agent-discovery.ts
|
|
793
584
|
var AgentDiscovery = class {
|
|
794
585
|
specification;
|
|
795
586
|
constructor(specification) {
|
|
@@ -840,6 +631,14 @@ var IncompatibleAdapter = class extends UnavailableAgent {
|
|
|
840
631
|
super(specification);
|
|
841
632
|
}
|
|
842
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
|
+
};
|
|
843
642
|
var LocalAgentInventory = class {
|
|
844
643
|
available;
|
|
845
644
|
discoveries;
|
|
@@ -857,49 +656,1576 @@ var LocalAgentInventory = class {
|
|
|
857
656
|
throw new Error("Unsupported Agent discovery outcome");
|
|
858
657
|
}
|
|
859
658
|
}
|
|
860
|
-
this.available = Object.freeze(available);
|
|
861
|
-
this.discoveries = Object.freeze([...discoveries]);
|
|
862
|
-
this.installable = Object.freeze(
|
|
863
|
-
unavailable.filter(
|
|
864
|
-
(discovery) => discovery.specification.supportsInstallation(
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
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();
|
|
885
|
+
}
|
|
886
|
+
} catch (error61) {
|
|
887
|
+
if (error61 instanceof ManagedNpmPackageError) {
|
|
888
|
+
throw error61;
|
|
889
|
+
}
|
|
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
|
|
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;
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
var BridgedAcpAgentDefinition = class extends LocalAgentSpecification {
|
|
1171
|
+
launchArguments = Object.freeze([]);
|
|
1172
|
+
get adapterExecutable() {
|
|
1173
|
+
return this.adapterPackageDefinition.executableName;
|
|
1174
|
+
}
|
|
1175
|
+
get adapterPackage() {
|
|
1176
|
+
return this.adapterPackageDefinition.packageName;
|
|
1177
|
+
}
|
|
1178
|
+
get adapterVersion() {
|
|
1179
|
+
return this.adapterPackageDefinition.version;
|
|
1180
|
+
}
|
|
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";
|
|
1271
|
+
}
|
|
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"
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
try {
|
|
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;
|
|
1289
|
+
} catch (error61) {
|
|
1290
|
+
if (error61 instanceof AgentProbeError) {
|
|
1291
|
+
throw error61;
|
|
1292
|
+
}
|
|
1293
|
+
throw new AgentProbeError(
|
|
1294
|
+
"agent_environment_invalid",
|
|
1295
|
+
"The configured Agent home is invalid",
|
|
1296
|
+
{ cause: error61 }
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
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
|
+
]
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
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
|
|
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";
|
|
1410
|
+
}
|
|
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;
|
|
1420
|
+
}
|
|
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"
|
|
1439
|
+
);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
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;
|
|
1459
|
+
try {
|
|
1460
|
+
redirected = new URL(location, url2);
|
|
1461
|
+
} catch (error61) {
|
|
1462
|
+
throw new InstallerDownloadError(
|
|
1463
|
+
"installer_redirect_invalid",
|
|
1464
|
+
"The installer returned an invalid redirect",
|
|
1465
|
+
{ cause: error61 }
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
try {
|
|
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 }
|
|
1475
|
+
);
|
|
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"
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
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"
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
digest.update(chunk.value);
|
|
1545
|
+
await writeCompleteChunk(handle, chunk.value);
|
|
1546
|
+
}
|
|
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",
|
|
1573
|
+
{ cause: error61 }
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
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
|
+
}
|
|
1588
|
+
|
|
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 {
|
|
1626
|
+
code;
|
|
1627
|
+
constructor(code, message, options = {}) {
|
|
1628
|
+
super(message, options);
|
|
1629
|
+
this.code = code;
|
|
1630
|
+
this.name = "ManagedAgentArchiveError";
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
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);
|
|
1657
|
+
}
|
|
1658
|
+
return digest.digest("hex");
|
|
1659
|
+
} catch (error61) {
|
|
1660
|
+
if (signal.aborted) {
|
|
1661
|
+
signal.throwIfAborted();
|
|
1662
|
+
}
|
|
1663
|
+
if (error61 instanceof ManagedAgentArchiveError) {
|
|
1664
|
+
throw error61;
|
|
1665
|
+
}
|
|
1666
|
+
throw new ManagedAgentArchiveError(
|
|
1667
|
+
"managed_archive_verification_failed",
|
|
1668
|
+
"The managed Agent artifact could not be verified",
|
|
1669
|
+
{ cause: error61 }
|
|
1670
|
+
);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
async function readManifest(path2) {
|
|
1674
|
+
let handle;
|
|
1675
|
+
try {
|
|
1676
|
+
handle = await open4(path2, "r");
|
|
1677
|
+
} catch (error61) {
|
|
1678
|
+
throw new ManagedAgentArchiveError(
|
|
1679
|
+
"managed_archive_verification_failed",
|
|
1680
|
+
"The managed Agent archive manifest is invalid",
|
|
1681
|
+
{ cause: error61 }
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
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" })
|
|
1694
|
+
);
|
|
1695
|
+
if (!isRecord3(parsed)) {
|
|
1696
|
+
throw new ManagedAgentArchiveError(
|
|
1697
|
+
"managed_archive_verification_failed",
|
|
1698
|
+
"The managed Agent archive manifest is invalid"
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
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();
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
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");
|
|
1721
|
+
}
|
|
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
|
+
);
|
|
1745
|
+
}
|
|
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
|
+
);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
var ManagedAgentArchiveInstaller = class {
|
|
1757
|
+
#downloader;
|
|
1758
|
+
constructor(downloader = new ManagedAgentArchiveDownloader()) {
|
|
1759
|
+
this.#downloader = downloader;
|
|
1760
|
+
}
|
|
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);
|
|
1767
|
+
}
|
|
1768
|
+
} catch (error61) {
|
|
1769
|
+
if (error61 instanceof ManagedAgentArchiveError || !(error61 instanceof Error) || !("code" in error61) || error61.code !== "ENOENT") {
|
|
1770
|
+
throw error61;
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
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();
|
|
1868
|
+
}
|
|
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
|
+
]);
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1884
|
+
};
|
|
1885
|
+
|
|
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
|
+
);
|
|
1903
|
+
}
|
|
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
|
+
);
|
|
1913
|
+
}
|
|
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
|
+
);
|
|
1923
|
+
}
|
|
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"
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
throw new Error(
|
|
1935
|
+
"OpenCode does not publish an approved archive for this platform"
|
|
1936
|
+
);
|
|
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
|
|
1965
|
+
);
|
|
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
|
+
};
|
|
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;
|
|
2004
|
+
var PiAgentSpecification = class extends BridgedAcpAgentDefinition {
|
|
2005
|
+
acpProbeTimeoutMilliseconds = ACP_PROBE_TIMEOUT_MILLISECONDS2;
|
|
2006
|
+
adapterEnvironmentVariable = "PI_ACP_PI_COMMAND";
|
|
2007
|
+
adapterOverrideVariable = "MOBIUS_PI_ACP_PATH";
|
|
2008
|
+
adapterPackageDefinition = PI_ACP_PACKAGE;
|
|
2009
|
+
adapterVersionArguments = [];
|
|
2010
|
+
authenticationCommand = () => "pi-acp --terminal-login";
|
|
2011
|
+
agentExecutable = "pi";
|
|
2012
|
+
agentOverrideVariable = "MOBIUS_PI_PATH";
|
|
2013
|
+
id = "pi";
|
|
2014
|
+
label = "Pi";
|
|
2015
|
+
managedAdapterExecutablePath = [
|
|
2016
|
+
"adapters",
|
|
2017
|
+
"pi",
|
|
2018
|
+
PI_ACP_PACKAGE.version,
|
|
2019
|
+
"node_modules",
|
|
2020
|
+
"pi-acp",
|
|
2021
|
+
"dist",
|
|
2022
|
+
"index.js"
|
|
2023
|
+
];
|
|
2024
|
+
managedAgentExecutablePath = [
|
|
2025
|
+
"agents",
|
|
2026
|
+
"pi",
|
|
2027
|
+
PI_AGENT_PACKAGE.version,
|
|
2028
|
+
"node_modules",
|
|
2029
|
+
"@earendil-works",
|
|
2030
|
+
"pi-coding-agent",
|
|
2031
|
+
"dist",
|
|
2032
|
+
"bundle",
|
|
2033
|
+
"cli.js"
|
|
2034
|
+
];
|
|
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
|
+
};
|
|
2045
|
+
verifyAcp = async (connection, initialized, defaultWorkspace, signal) => {
|
|
2046
|
+
const capabilities = initialized.agentCapabilities ?? {};
|
|
2047
|
+
if (capabilities.loadSession !== true) {
|
|
2048
|
+
throw new AgentProbeError(
|
|
2049
|
+
"acp_incompatible",
|
|
2050
|
+
"Pi ACP adapter does not support session recovery"
|
|
2051
|
+
);
|
|
2052
|
+
}
|
|
2053
|
+
const session = await connection.agent.request(
|
|
2054
|
+
methods.agent.session.new,
|
|
2055
|
+
{
|
|
2056
|
+
additionalDirectories: [],
|
|
2057
|
+
cwd: defaultWorkspace,
|
|
2058
|
+
mcpServers: []
|
|
2059
|
+
},
|
|
2060
|
+
{ cancellationSignal: signal }
|
|
872
2061
|
);
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
2062
|
+
if (session.sessionId.length === 0) {
|
|
2063
|
+
throw new AgentProbeError(
|
|
2064
|
+
"acp_incompatible",
|
|
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 }
|
|
2075
|
+
);
|
|
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
|
+
};
|
|
876
2083
|
};
|
|
877
|
-
|
|
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 {
|
|
878
2128
|
code;
|
|
879
2129
|
constructor(code, message, options = {}) {
|
|
880
2130
|
super(message, options);
|
|
881
2131
|
this.code = code;
|
|
882
|
-
this.name = "
|
|
2132
|
+
this.name = "ManagedAdapterInstallError";
|
|
883
2133
|
}
|
|
884
2134
|
};
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
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 }
|
|
895
2166
|
);
|
|
896
2167
|
}
|
|
897
|
-
async function
|
|
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
|
+
};
|
|
2208
|
+
};
|
|
2209
|
+
|
|
2210
|
+
// src/agent-catalog/executable-discovery.ts
|
|
2211
|
+
async function resolveManagedExecutable(specification, context) {
|
|
2212
|
+
try {
|
|
2213
|
+
return await specification.resolveManagedAgent(
|
|
2214
|
+
readStateDirectory(context.environment),
|
|
2215
|
+
context.platform ?? process.platform,
|
|
2216
|
+
context.signal
|
|
2217
|
+
);
|
|
2218
|
+
} catch {
|
|
2219
|
+
context.signal.throwIfAborted();
|
|
2220
|
+
return null;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
async function resolveManagedAdapterExecutable(specification, context) {
|
|
898
2224
|
let managed;
|
|
899
2225
|
try {
|
|
900
2226
|
managed = await validateManagedAdapter(
|
|
901
|
-
|
|
902
|
-
readStateDirectory(
|
|
2227
|
+
join11(
|
|
2228
|
+
readStateDirectory(context.environment),
|
|
903
2229
|
"adapters",
|
|
904
2230
|
specification.id,
|
|
905
2231
|
specification.adapterVersion
|
|
@@ -911,19 +2237,19 @@ async function resolveManagedAdapterExecutable(specification, options) {
|
|
|
911
2237
|
}
|
|
912
2238
|
return managed;
|
|
913
2239
|
}
|
|
914
|
-
async function resolveSpecificationExecutables(executableName, overrideVariable, resolveManaged,
|
|
915
|
-
const override =
|
|
2240
|
+
async function resolveSpecificationExecutables(executableName, overrideVariable, resolveManaged, context) {
|
|
2241
|
+
const override = context.environment[overrideVariable];
|
|
916
2242
|
if (override !== void 0) {
|
|
917
|
-
if (override.length === 0 || !
|
|
2243
|
+
if (override.length === 0 || !isAbsolute4(override)) {
|
|
918
2244
|
return [];
|
|
919
2245
|
}
|
|
920
|
-
const executable = await resolveExecutablePath(override,
|
|
2246
|
+
const executable = await resolveExecutablePath(override, context.platform);
|
|
921
2247
|
return executable === null ? [] : [executable];
|
|
922
2248
|
}
|
|
923
2249
|
const external = await findExecutableOnPath(
|
|
924
2250
|
executableName,
|
|
925
|
-
|
|
926
|
-
|
|
2251
|
+
context.environment,
|
|
2252
|
+
context.platform
|
|
927
2253
|
);
|
|
928
2254
|
const managed = await resolveManaged();
|
|
929
2255
|
if (external === null) {
|
|
@@ -934,44 +2260,47 @@ async function resolveSpecificationExecutables(executableName, overrideVariable,
|
|
|
934
2260
|
}
|
|
935
2261
|
return [external, managed];
|
|
936
2262
|
}
|
|
937
|
-
async function resolveAgentExecutables(specification,
|
|
2263
|
+
async function resolveAgentExecutables(specification, context) {
|
|
938
2264
|
return await resolveSpecificationExecutables(
|
|
939
2265
|
specification.agentExecutable,
|
|
940
2266
|
specification.agentOverrideVariable,
|
|
941
|
-
async () => await resolveManagedExecutable(
|
|
942
|
-
|
|
943
|
-
options
|
|
944
|
-
),
|
|
945
|
-
options
|
|
2267
|
+
async () => await resolveManagedExecutable(specification, context),
|
|
2268
|
+
context
|
|
946
2269
|
);
|
|
947
2270
|
}
|
|
948
|
-
async function resolveAdapterExecutables(specification,
|
|
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
|
+
}
|
|
949
2278
|
return await resolveSpecificationExecutables(
|
|
950
2279
|
specification.adapterExecutable,
|
|
951
2280
|
specification.adapterOverrideVariable,
|
|
952
|
-
async () => await resolveManagedAdapterExecutable(specification,
|
|
953
|
-
|
|
2281
|
+
async () => await resolveManagedAdapterExecutable(specification, context),
|
|
2282
|
+
context
|
|
954
2283
|
);
|
|
955
2284
|
}
|
|
956
|
-
async function resolveAgentExecutable(specification,
|
|
957
|
-
const executables = await resolveAgentExecutables(specification,
|
|
2285
|
+
async function resolveAgentExecutable(specification, context) {
|
|
2286
|
+
const executables = await resolveAgentExecutables(specification, context);
|
|
958
2287
|
return executables[0] ?? null;
|
|
959
2288
|
}
|
|
960
|
-
async function resolveAdapterExecutable(specification,
|
|
961
|
-
const executables = await resolveAdapterExecutables(specification,
|
|
2289
|
+
async function resolveAdapterExecutable(specification, context) {
|
|
2290
|
+
const executables = await resolveAdapterExecutables(specification, context);
|
|
962
2291
|
return executables[0] ?? null;
|
|
963
2292
|
}
|
|
964
2293
|
function normalizedVersion(output2) {
|
|
965
2294
|
return output2.trim().split(/\r?\n/, 1)[0]?.trim().slice(0, 128) ?? "";
|
|
966
2295
|
}
|
|
967
|
-
async function probeVersion(executable, environment, defaultWorkspace, signal) {
|
|
2296
|
+
async function probeVersion(executable, arguments_, environment, defaultWorkspace, signal, timeoutMilliseconds) {
|
|
968
2297
|
const result = await runBoundedProcess({
|
|
969
|
-
arguments:
|
|
2298
|
+
arguments: arguments_,
|
|
970
2299
|
command: executable.path,
|
|
971
2300
|
cwd: defaultWorkspace,
|
|
972
2301
|
environment,
|
|
973
2302
|
signal,
|
|
974
|
-
timeoutMilliseconds
|
|
2303
|
+
timeoutMilliseconds
|
|
975
2304
|
});
|
|
976
2305
|
const version2 = normalizedVersion(result.stdout || result.stderr);
|
|
977
2306
|
if (version2.length === 0) {
|
|
@@ -990,62 +2319,73 @@ var VerifiedAgentExecutable = class {
|
|
|
990
2319
|
this.version = version2;
|
|
991
2320
|
}
|
|
992
2321
|
};
|
|
993
|
-
async function findVerifiedAgentExecutable(specification,
|
|
994
|
-
const executables = await resolveAgentExecutables(specification,
|
|
2322
|
+
async function findVerifiedAgentExecutable(specification, context) {
|
|
2323
|
+
const executables = await resolveAgentExecutables(specification, context);
|
|
995
2324
|
for (const executable of executables) {
|
|
996
2325
|
try {
|
|
997
2326
|
const version2 = await probeVersion(
|
|
998
2327
|
executable,
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
2328
|
+
specification.agentVersionArguments,
|
|
2329
|
+
context.environment,
|
|
2330
|
+
context.defaultWorkspace,
|
|
2331
|
+
context.signal,
|
|
2332
|
+
specification.versionProbeTimeoutMilliseconds
|
|
1002
2333
|
);
|
|
1003
2334
|
return new VerifiedAgentExecutable(executable, version2);
|
|
1004
2335
|
} catch {
|
|
1005
|
-
if (
|
|
1006
|
-
|
|
2336
|
+
if (context.signal.aborted) {
|
|
2337
|
+
context.signal.throwIfAborted();
|
|
1007
2338
|
}
|
|
1008
2339
|
}
|
|
1009
2340
|
}
|
|
1010
2341
|
return null;
|
|
1011
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
|
+
};
|
|
1012
2352
|
function readableAcpStream(input2) {
|
|
1013
2353
|
let byteLength = 0;
|
|
1014
|
-
let
|
|
2354
|
+
let open7 = true;
|
|
1015
2355
|
return new ReadableStream({
|
|
1016
2356
|
start(controller) {
|
|
1017
2357
|
input2.on("data", (chunk) => {
|
|
1018
|
-
if (!
|
|
2358
|
+
if (!open7) {
|
|
1019
2359
|
return;
|
|
1020
2360
|
}
|
|
1021
2361
|
if (!(chunk instanceof Uint8Array)) {
|
|
1022
|
-
|
|
2362
|
+
open7 = false;
|
|
1023
2363
|
controller.error(new Error("ACP adapter produced invalid output"));
|
|
1024
2364
|
return;
|
|
1025
2365
|
}
|
|
1026
2366
|
byteLength += chunk.byteLength;
|
|
1027
2367
|
if (byteLength > ACP_OUTPUT_LIMIT_BYTES) {
|
|
1028
|
-
|
|
2368
|
+
open7 = false;
|
|
1029
2369
|
controller.error(new Error("ACP adapter output limit exceeded"));
|
|
1030
2370
|
return;
|
|
1031
2371
|
}
|
|
1032
2372
|
controller.enqueue(new Uint8Array(chunk));
|
|
1033
2373
|
});
|
|
1034
2374
|
input2.once("end", () => {
|
|
1035
|
-
if (
|
|
1036
|
-
|
|
2375
|
+
if (open7) {
|
|
2376
|
+
open7 = false;
|
|
1037
2377
|
controller.close();
|
|
1038
2378
|
}
|
|
1039
2379
|
});
|
|
1040
2380
|
input2.once("error", (error61) => {
|
|
1041
|
-
if (
|
|
1042
|
-
|
|
2381
|
+
if (open7) {
|
|
2382
|
+
open7 = false;
|
|
1043
2383
|
controller.error(error61);
|
|
1044
2384
|
}
|
|
1045
2385
|
});
|
|
1046
2386
|
},
|
|
1047
2387
|
cancel() {
|
|
1048
|
-
|
|
2388
|
+
open7 = false;
|
|
1049
2389
|
}
|
|
1050
2390
|
});
|
|
1051
2391
|
}
|
|
@@ -1064,14 +2404,14 @@ function writableAcpStream(output2) {
|
|
|
1064
2404
|
}
|
|
1065
2405
|
});
|
|
1066
2406
|
}
|
|
1067
|
-
async function
|
|
2407
|
+
async function probeAcpReadiness(specification, agent, environment, defaultWorkspace, signal) {
|
|
1068
2408
|
signal.throwIfAborted();
|
|
1069
2409
|
const controller = new AbortController();
|
|
1070
2410
|
let timedOut = false;
|
|
1071
2411
|
const timeout = setTimeout(() => {
|
|
1072
2412
|
timedOut = true;
|
|
1073
2413
|
controller.abort(new Error("ACP probe timed out"));
|
|
1074
|
-
},
|
|
2414
|
+
}, specification.acpProbeTimeoutMilliseconds);
|
|
1075
2415
|
const abort = () => controller.abort(signal.reason);
|
|
1076
2416
|
signal.addEventListener("abort", abort, { once: true });
|
|
1077
2417
|
if (signal.aborted) {
|
|
@@ -1108,13 +2448,13 @@ async function probeAcp(agent, environment, defaultWorkspace, signal) {
|
|
|
1108
2448
|
}
|
|
1109
2449
|
};
|
|
1110
2450
|
stderr.on("data", consumeStderr);
|
|
1111
|
-
const connection = createAcpClient(
|
|
1112
|
-
|
|
1113
|
-
);
|
|
2451
|
+
const connection = createAcpClient(
|
|
2452
|
+
new DiscoveryAcpHandler()
|
|
2453
|
+
).connect(ndJsonStream(writableAcpStream(stdin), readableAcpStream(stdout)));
|
|
1114
2454
|
try {
|
|
1115
2455
|
const initialized = await Promise.race([
|
|
1116
2456
|
connection.agent.request(
|
|
1117
|
-
|
|
2457
|
+
methods3.agent.initialize,
|
|
1118
2458
|
{
|
|
1119
2459
|
clientCapabilities: {},
|
|
1120
2460
|
clientInfo: { name: "mobius", version: "0.0.3" },
|
|
@@ -1130,6 +2470,13 @@ async function probeAcp(agent, environment, defaultWorkspace, signal) {
|
|
|
1130
2470
|
"ACP adapter selected an unsupported protocol version"
|
|
1131
2471
|
);
|
|
1132
2472
|
}
|
|
2473
|
+
await specification.verifyAcp(
|
|
2474
|
+
connection,
|
|
2475
|
+
initialized,
|
|
2476
|
+
defaultWorkspace,
|
|
2477
|
+
controller.signal
|
|
2478
|
+
);
|
|
2479
|
+
return normalizedVersion(initialized.agentInfo?.version ?? "");
|
|
1133
2480
|
} catch (error61) {
|
|
1134
2481
|
if (signal.aborted) {
|
|
1135
2482
|
signal.throwIfAborted();
|
|
@@ -1144,6 +2491,13 @@ async function probeAcp(agent, environment, defaultWorkspace, signal) {
|
|
|
1144
2491
|
if (error61 instanceof AgentProbeError) {
|
|
1145
2492
|
throw error61;
|
|
1146
2493
|
}
|
|
2494
|
+
if (error61 instanceof RequestError && error61.code === -32e3 && specification.authenticationCommand().length > 0) {
|
|
2495
|
+
throw new AgentProbeError(
|
|
2496
|
+
"authentication_required",
|
|
2497
|
+
"Agent authentication is required",
|
|
2498
|
+
{ cause: error61 }
|
|
2499
|
+
);
|
|
2500
|
+
}
|
|
1147
2501
|
throw new AgentProbeError(
|
|
1148
2502
|
"acp_incompatible",
|
|
1149
2503
|
"ACP adapter initialization failed",
|
|
@@ -1158,8 +2512,13 @@ async function probeAcp(agent, environment, defaultWorkspace, signal) {
|
|
|
1158
2512
|
await stopProcessTree(child);
|
|
1159
2513
|
}
|
|
1160
2514
|
}
|
|
2515
|
+
|
|
2516
|
+
// src/agent-catalog/discover-agent.ts
|
|
1161
2517
|
function probeFailure(specification, executableKind, error61) {
|
|
1162
2518
|
if (error61 instanceof AgentProbeError) {
|
|
2519
|
+
if (error61.code === "authentication_required") {
|
|
2520
|
+
return new AuthenticationRequiredAgent(specification);
|
|
2521
|
+
}
|
|
1163
2522
|
return error61.code === "acp_incompatible" ? new IncompatibleAdapter(specification) : new ProbeFailed(specification, error61.code, error61.message);
|
|
1164
2523
|
}
|
|
1165
2524
|
if (error61 instanceof ProcessExecutionError) {
|
|
@@ -1176,12 +2535,15 @@ function probeFailure(specification, executableKind, error61) {
|
|
|
1176
2535
|
`${executableKind === "agent" ? "Agent" : "Adapter"} probe failed`
|
|
1177
2536
|
);
|
|
1178
2537
|
}
|
|
1179
|
-
async function
|
|
1180
|
-
|
|
2538
|
+
async function discoverAgent(specification, context) {
|
|
2539
|
+
context.signal.throwIfAborted();
|
|
1181
2540
|
const [adapters, agents] = await Promise.all([
|
|
1182
|
-
resolveAdapterExecutables(specification,
|
|
1183
|
-
resolveAgentExecutables(specification,
|
|
2541
|
+
resolveAdapterExecutables(specification, context),
|
|
2542
|
+
resolveAgentExecutables(specification, context)
|
|
1184
2543
|
]);
|
|
2544
|
+
if (specification instanceof DirectAcpAgentDefinition && agents.length === 0) {
|
|
2545
|
+
return new MissingAgent(specification);
|
|
2546
|
+
}
|
|
1185
2547
|
if (adapters.length === 0) {
|
|
1186
2548
|
return new MissingAdapter(specification);
|
|
1187
2549
|
}
|
|
@@ -1194,32 +2556,46 @@ async function discoverSpecification(specification, options) {
|
|
|
1194
2556
|
try {
|
|
1195
2557
|
agentVersion = await probeVersion(
|
|
1196
2558
|
agent,
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
2559
|
+
specification.agentVersionArguments,
|
|
2560
|
+
context.environment,
|
|
2561
|
+
context.defaultWorkspace,
|
|
2562
|
+
context.signal,
|
|
2563
|
+
specification.versionProbeTimeoutMilliseconds
|
|
1200
2564
|
);
|
|
1201
2565
|
} catch (error61) {
|
|
1202
|
-
if (
|
|
1203
|
-
|
|
2566
|
+
if (context.signal.aborted) {
|
|
2567
|
+
context.signal.throwIfAborted();
|
|
1204
2568
|
}
|
|
1205
2569
|
lastFailure = probeFailure(specification, "agent", error61);
|
|
1206
2570
|
continue;
|
|
1207
2571
|
}
|
|
1208
2572
|
for (const adapter of adapters) {
|
|
2573
|
+
if (specification instanceof DirectAcpAgentDefinition && adapter.path !== agent.path) {
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
1209
2576
|
let adapterVersion = "";
|
|
2577
|
+
let launchEnvironment;
|
|
1210
2578
|
try {
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
...options.environment,
|
|
1215
|
-
[specification.adapterEnvironmentVariable]: agent.path
|
|
1216
|
-
},
|
|
1217
|
-
options.defaultWorkspace,
|
|
1218
|
-
options.signal
|
|
2579
|
+
launchEnvironment = await specification.createLaunchEnvironment(
|
|
2580
|
+
agent,
|
|
2581
|
+
context
|
|
1219
2582
|
);
|
|
2583
|
+
if (specification.adapterVersionArguments.length > 0) {
|
|
2584
|
+
adapterVersion = await probeVersion(
|
|
2585
|
+
adapter,
|
|
2586
|
+
specification.adapterVersionArguments,
|
|
2587
|
+
{
|
|
2588
|
+
...context.environment,
|
|
2589
|
+
...launchEnvironment
|
|
2590
|
+
},
|
|
2591
|
+
context.defaultWorkspace,
|
|
2592
|
+
context.signal,
|
|
2593
|
+
specification.versionProbeTimeoutMilliseconds
|
|
2594
|
+
);
|
|
2595
|
+
}
|
|
1220
2596
|
} catch (error61) {
|
|
1221
|
-
if (
|
|
1222
|
-
|
|
2597
|
+
if (context.signal.aborted) {
|
|
2598
|
+
context.signal.throwIfAborted();
|
|
1223
2599
|
}
|
|
1224
2600
|
lastFailure = probeFailure(specification, "adapter", error61);
|
|
1225
2601
|
continue;
|
|
@@ -1228,134 +2604,90 @@ async function discoverSpecification(specification, options) {
|
|
|
1228
2604
|
specification.id,
|
|
1229
2605
|
specification.label,
|
|
1230
2606
|
adapter.path,
|
|
1231
|
-
|
|
1232
|
-
|
|
2607
|
+
specification.launchArguments,
|
|
2608
|
+
launchEnvironment,
|
|
1233
2609
|
adapterVersion,
|
|
1234
2610
|
agentVersion,
|
|
1235
2611
|
adapter.identity,
|
|
1236
|
-
agent.identity
|
|
2612
|
+
agent.identity,
|
|
2613
|
+
specification.promptTimeoutMilliseconds
|
|
1237
2614
|
);
|
|
1238
2615
|
try {
|
|
1239
|
-
await
|
|
2616
|
+
const acpVersion = await probeAcpReadiness(
|
|
2617
|
+
specification,
|
|
1240
2618
|
definition,
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
2619
|
+
context.environment,
|
|
2620
|
+
context.defaultWorkspace,
|
|
2621
|
+
context.signal
|
|
1244
2622
|
);
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
2623
|
+
const verifiedAdapterVersion = adapterVersion.length > 0 ? adapterVersion : acpVersion;
|
|
2624
|
+
if (verifiedAdapterVersion.length === 0) {
|
|
2625
|
+
throw new AgentProbeError(
|
|
2626
|
+
"acp_incompatible",
|
|
2627
|
+
"ACP adapter did not report a version"
|
|
2628
|
+
);
|
|
1249
2629
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
// src/agent-catalog/agent-diagnostics.ts
|
|
1279
|
-
var AvailableAgentDiagnostic = class {
|
|
1280
|
-
adapterVersion;
|
|
1281
|
-
agentVersion;
|
|
1282
|
-
id;
|
|
1283
|
-
label;
|
|
1284
|
-
status = "ready";
|
|
1285
|
-
constructor(discovery) {
|
|
1286
|
-
this.adapterVersion = discovery.agent.adapterVersion;
|
|
1287
|
-
this.agentVersion = discovery.agent.agentVersion;
|
|
1288
|
-
this.id = discovery.specification.id;
|
|
1289
|
-
this.label = discovery.specification.label;
|
|
1290
|
-
}
|
|
1291
|
-
};
|
|
1292
|
-
var UnavailableAgentDiagnostic = class {
|
|
1293
|
-
code;
|
|
1294
|
-
id;
|
|
1295
|
-
label;
|
|
1296
|
-
message;
|
|
1297
|
-
recoveryCommand;
|
|
1298
|
-
status;
|
|
1299
|
-
constructor(discovery) {
|
|
1300
|
-
this.code = discovery.code;
|
|
1301
|
-
this.id = discovery.specification.id;
|
|
1302
|
-
this.label = discovery.specification.label;
|
|
1303
|
-
this.message = discovery.message;
|
|
1304
|
-
this.recoveryCommand = discovery instanceof MissingAdapter ? discovery.specification.recoveryCommand() : "";
|
|
1305
|
-
this.status = discovery.status;
|
|
1306
|
-
}
|
|
1307
|
-
};
|
|
1308
|
-
var AgentDiagnostics = class {
|
|
1309
|
-
available;
|
|
1310
|
-
unavailable;
|
|
1311
|
-
constructor(inventory) {
|
|
1312
|
-
this.available = inventory.available.map(
|
|
1313
|
-
(discovery) => new AvailableAgentDiagnostic(discovery)
|
|
1314
|
-
);
|
|
1315
|
-
this.unavailable = inventory.unavailable.map(
|
|
1316
|
-
(discovery) => new UnavailableAgentDiagnostic(discovery)
|
|
1317
|
-
);
|
|
1318
|
-
}
|
|
1319
|
-
};
|
|
1320
|
-
function humanLine(label, status, detail) {
|
|
1321
|
-
return `${label.padEnd(13)} ${status.padEnd(17)} ${detail}`.trimEnd();
|
|
1322
|
-
}
|
|
1323
|
-
function formatAgentDiagnostics(inventory, json2) {
|
|
1324
|
-
const diagnostics = new AgentDiagnostics(inventory);
|
|
1325
|
-
if (json2) {
|
|
1326
|
-
return `${JSON.stringify(diagnostics, null, 2)}
|
|
1327
|
-
`;
|
|
1328
|
-
}
|
|
1329
|
-
const lines = [];
|
|
1330
|
-
for (const available of diagnostics.available) {
|
|
1331
|
-
lines.push(
|
|
1332
|
-
humanLine(
|
|
1333
|
-
available.label,
|
|
1334
|
-
available.status,
|
|
1335
|
-
`${available.agentVersion} ${available.adapterVersion}`
|
|
1336
|
-
)
|
|
1337
|
-
);
|
|
2630
|
+
return new AvailableAgent(
|
|
2631
|
+
specification,
|
|
2632
|
+
new LocalAgentDefinition(
|
|
2633
|
+
specification.id,
|
|
2634
|
+
specification.label,
|
|
2635
|
+
adapter.path,
|
|
2636
|
+
specification.launchArguments,
|
|
2637
|
+
launchEnvironment,
|
|
2638
|
+
verifiedAdapterVersion,
|
|
2639
|
+
agentVersion,
|
|
2640
|
+
adapter.identity,
|
|
2641
|
+
agent.identity,
|
|
2642
|
+
specification.promptTimeoutMilliseconds
|
|
2643
|
+
)
|
|
2644
|
+
);
|
|
2645
|
+
} catch (error61) {
|
|
2646
|
+
if (context.signal.aborted) {
|
|
2647
|
+
context.signal.throwIfAborted();
|
|
2648
|
+
}
|
|
2649
|
+
const failure = probeFailure(specification, "adapter", error61);
|
|
2650
|
+
if (failure instanceof AuthenticationRequiredAgent) {
|
|
2651
|
+
return failure;
|
|
2652
|
+
}
|
|
2653
|
+
lastFailure = failure;
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
1338
2656
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
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)
|
|
1342
2672
|
);
|
|
1343
|
-
|
|
1344
|
-
lines.push(` Install adapter: ${unavailable.recoveryCommand}`);
|
|
1345
|
-
}
|
|
2673
|
+
} catch {
|
|
1346
2674
|
}
|
|
1347
|
-
return
|
|
1348
|
-
|
|
2675
|
+
return new LocalAgentInventory(
|
|
2676
|
+
discoveries,
|
|
2677
|
+
grantedAgents,
|
|
2678
|
+
options.platform,
|
|
2679
|
+
options.architecture
|
|
2680
|
+
);
|
|
1349
2681
|
}
|
|
1350
2682
|
|
|
1351
2683
|
// src/audit/audit-store.ts
|
|
1352
2684
|
import { mkdirSync } from "node:fs";
|
|
1353
|
-
import { dirname as
|
|
2685
|
+
import { dirname as dirname3 } from "node:path";
|
|
1354
2686
|
import { DatabaseSync } from "node:sqlite";
|
|
1355
2687
|
var AuditStore = class {
|
|
1356
2688
|
database;
|
|
1357
2689
|
constructor(path2) {
|
|
1358
|
-
mkdirSync(
|
|
2690
|
+
mkdirSync(dirname3(path2), { recursive: true });
|
|
1359
2691
|
this.database = new DatabaseSync(path2);
|
|
1360
2692
|
this.database.exec(`
|
|
1361
2693
|
CREATE TABLE IF NOT EXISTS audit_events (
|
|
@@ -1399,10 +2731,10 @@ var AuditStore = class {
|
|
|
1399
2731
|
};
|
|
1400
2732
|
|
|
1401
2733
|
// src/config.ts
|
|
1402
|
-
import { join as
|
|
2734
|
+
import { join as join12 } from "node:path";
|
|
1403
2735
|
function loadDefaultWorkspace() {
|
|
1404
2736
|
const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = process.env;
|
|
1405
|
-
return configuredWorkspace ??
|
|
2737
|
+
return configuredWorkspace ?? join12(readStateDirectory(), "workspace");
|
|
1406
2738
|
}
|
|
1407
2739
|
function createDaemonConfig(connection, agents = [], installableAgents = []) {
|
|
1408
2740
|
return {
|
|
@@ -1420,22 +2752,16 @@ function createDaemonConfig(connection, agents = [], installableAgents = []) {
|
|
|
1420
2752
|
// src/pairing/pair-local-machine.ts
|
|
1421
2753
|
import { randomUUID } from "node:crypto";
|
|
1422
2754
|
import {
|
|
1423
|
-
mkdir as
|
|
1424
|
-
open as
|
|
1425
|
-
realpath as
|
|
1426
|
-
rename as
|
|
1427
|
-
stat as
|
|
1428
|
-
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
|
|
1429
2761
|
} from "node:fs/promises";
|
|
1430
2762
|
import { hostname } from "node:os";
|
|
1431
|
-
import { basename, join as
|
|
1432
|
-
import { fetch } from "undici";
|
|
1433
|
-
|
|
1434
|
-
// src/network/environment-proxy.ts
|
|
1435
|
-
import { EnvHttpProxyAgent } from "undici";
|
|
1436
|
-
var environmentProxyDispatcher = new EnvHttpProxyAgent();
|
|
1437
|
-
|
|
1438
|
-
// 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";
|
|
1439
2765
|
var PAIRING_TIMEOUT_MILLISECONDS = 2e4;
|
|
1440
2766
|
var PAIRING_CODE_PATTERN = /^[A-HJ-NP-Z2-9]{4}(?:-[A-HJ-NP-Z2-9]{4}){4}$/;
|
|
1441
2767
|
var IDENTIFIER_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
|
|
@@ -1460,7 +2786,7 @@ var PairingClientError = class extends Error {
|
|
|
1460
2786
|
this.name = "PairingClientError";
|
|
1461
2787
|
}
|
|
1462
2788
|
};
|
|
1463
|
-
function
|
|
2789
|
+
function isRecord4(value) {
|
|
1464
2790
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1465
2791
|
}
|
|
1466
2792
|
function hasExactKeys(value, expectedKeys) {
|
|
@@ -1573,9 +2899,9 @@ function parseChannelArgument(arguments_) {
|
|
|
1573
2899
|
async function resolveDefaultWorkspace(path2) {
|
|
1574
2900
|
const requestedWorkspace = resolve3(path2);
|
|
1575
2901
|
try {
|
|
1576
|
-
await
|
|
1577
|
-
const defaultWorkspace = await
|
|
1578
|
-
const metadata = await
|
|
2902
|
+
await mkdir3(requestedWorkspace, { recursive: true, mode: 448 });
|
|
2903
|
+
const defaultWorkspace = await realpath5(requestedWorkspace);
|
|
2904
|
+
const metadata = await stat5(defaultWorkspace);
|
|
1579
2905
|
if (!metadata.isDirectory()) {
|
|
1580
2906
|
throw new PairingClientError(
|
|
1581
2907
|
"invalid_workspace",
|
|
@@ -1613,9 +2939,9 @@ async function readJsonResponse(response) {
|
|
|
1613
2939
|
}
|
|
1614
2940
|
}
|
|
1615
2941
|
function readRelayError(value) {
|
|
1616
|
-
if (
|
|
2942
|
+
if (isRecord4(value)) {
|
|
1617
2943
|
const error61 = value["error"];
|
|
1618
|
-
if (
|
|
2944
|
+
if (isRecord4(error61)) {
|
|
1619
2945
|
const code = error61["code"];
|
|
1620
2946
|
const message = error61["message"];
|
|
1621
2947
|
if (typeof code === "string" && typeof message === "string") {
|
|
@@ -1629,7 +2955,7 @@ function readRelayError(value) {
|
|
|
1629
2955
|
);
|
|
1630
2956
|
}
|
|
1631
2957
|
function decodeClaimResponse(value) {
|
|
1632
|
-
if (!
|
|
2958
|
+
if (!isRecord4(value) || !hasExactKeys(value, CLAIM_RESPONSE_KEYS)) {
|
|
1633
2959
|
throw new PairingClientError(
|
|
1634
2960
|
"invalid_relay_response",
|
|
1635
2961
|
"The relay returned an invalid pairing response"
|
|
@@ -1654,7 +2980,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
|
|
|
1654
2980
|
const endpoint = new URL("/api/pairings/claim", options.relayOrigin);
|
|
1655
2981
|
let response;
|
|
1656
2982
|
try {
|
|
1657
|
-
response = await
|
|
2983
|
+
response = await fetch2(endpoint, {
|
|
1658
2984
|
body: JSON.stringify({
|
|
1659
2985
|
agents,
|
|
1660
2986
|
installableAgents: installableAgents.map((agent) => ({
|
|
@@ -1688,14 +3014,14 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
|
|
|
1688
3014
|
return decodeClaimResponse(value);
|
|
1689
3015
|
}
|
|
1690
3016
|
async function persistConnection(channelId, relayOrigin, daemonToken, defaultWorkspace) {
|
|
1691
|
-
const connectionsDirectory =
|
|
1692
|
-
await
|
|
1693
|
-
const connectionPath =
|
|
1694
|
-
const temporaryPath =
|
|
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(
|
|
1695
3021
|
connectionsDirectory,
|
|
1696
3022
|
`.${channelId}.${randomUUID()}.tmp`
|
|
1697
3023
|
);
|
|
1698
|
-
await
|
|
3024
|
+
await writeFile2(
|
|
1699
3025
|
temporaryPath,
|
|
1700
3026
|
`${JSON.stringify(
|
|
1701
3027
|
{
|
|
@@ -1710,11 +3036,11 @@ async function persistConnection(channelId, relayOrigin, daemonToken, defaultWor
|
|
|
1710
3036
|
`,
|
|
1711
3037
|
{ flag: "wx", mode: 384 }
|
|
1712
3038
|
);
|
|
1713
|
-
await
|
|
3039
|
+
await rename3(temporaryPath, connectionPath);
|
|
1714
3040
|
return connectionPath;
|
|
1715
3041
|
}
|
|
1716
3042
|
function decodeSavedConnection(value, expectedChannelId) {
|
|
1717
|
-
if (!
|
|
3043
|
+
if (!isRecord4(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS)) {
|
|
1718
3044
|
throw new PairingClientError(
|
|
1719
3045
|
"invalid_connection",
|
|
1720
3046
|
"The saved connection is invalid"
|
|
@@ -1738,13 +3064,13 @@ function decodeSavedConnection(value, expectedChannelId) {
|
|
|
1738
3064
|
};
|
|
1739
3065
|
}
|
|
1740
3066
|
async function readSavedConnection(channelId) {
|
|
1741
|
-
const connectionPath =
|
|
3067
|
+
const connectionPath = join13(
|
|
1742
3068
|
readStateDirectory(),
|
|
1743
3069
|
"connections",
|
|
1744
3070
|
`${channelId}.json`
|
|
1745
3071
|
);
|
|
1746
3072
|
let encoded = "";
|
|
1747
|
-
const handle = await
|
|
3073
|
+
const handle = await open5(connectionPath, "r");
|
|
1748
3074
|
try {
|
|
1749
3075
|
const metadata = await handle.stat();
|
|
1750
3076
|
if (!metadata.isFile() || metadata.size > MAX_CONNECTION_FILE_LENGTH) {
|
|
@@ -1793,7 +3119,7 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
|
|
|
1793
3119
|
);
|
|
1794
3120
|
relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
1795
3121
|
relayUrl.searchParams.set("token", connection.daemonToken);
|
|
1796
|
-
const auditDatabasePath =
|
|
3122
|
+
const auditDatabasePath = join13(
|
|
1797
3123
|
readStateDirectory(),
|
|
1798
3124
|
"audit",
|
|
1799
3125
|
`${connection.channelId}.sqlite`
|
|
@@ -1852,7 +3178,7 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
|
|
|
1852
3178
|
}
|
|
1853
3179
|
async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
|
|
1854
3180
|
const channelId = parseChannelArgument(arguments_);
|
|
1855
|
-
const connectionPath =
|
|
3181
|
+
const connectionPath = join13(
|
|
1856
3182
|
readStateDirectory(),
|
|
1857
3183
|
"connections",
|
|
1858
3184
|
`${channelId}.json`
|
|
@@ -6887,7 +8213,7 @@ function bucketFor(state, inst) {
|
|
|
6887
8213
|
return bucket;
|
|
6888
8214
|
}
|
|
6889
8215
|
var handoff;
|
|
6890
|
-
var
|
|
8216
|
+
var open6 = [];
|
|
6891
8217
|
var memo = {
|
|
6892
8218
|
alloc(_inst, payload, empty) {
|
|
6893
8219
|
const bucket = handoff;
|
|
@@ -6896,7 +8222,7 @@ var memo = {
|
|
|
6896
8222
|
handoff = void 0;
|
|
6897
8223
|
const entry = { value: empty, issues: null };
|
|
6898
8224
|
bucket.set(payload.value, entry);
|
|
6899
|
-
|
|
8225
|
+
open6.push(entry);
|
|
6900
8226
|
return empty;
|
|
6901
8227
|
},
|
|
6902
8228
|
guard(inst) {
|
|
@@ -6962,10 +8288,10 @@ var memo = {
|
|
|
6962
8288
|
return payload;
|
|
6963
8289
|
}
|
|
6964
8290
|
handoff = bucket;
|
|
6965
|
-
const depth =
|
|
8291
|
+
const depth = open6.length;
|
|
6966
8292
|
const result = base(payload, ctx);
|
|
6967
8293
|
handoff = void 0;
|
|
6968
|
-
const entry =
|
|
8294
|
+
const entry = open6.length > depth ? open6.pop() : void 0;
|
|
6969
8295
|
if (result instanceof Promise) {
|
|
6970
8296
|
return result.then((r) => {
|
|
6971
8297
|
if (entry)
|
|
@@ -21254,7 +22580,7 @@ var ProtocolMessageError = class extends Error {
|
|
|
21254
22580
|
this.name = "ProtocolMessageError";
|
|
21255
22581
|
}
|
|
21256
22582
|
};
|
|
21257
|
-
function
|
|
22583
|
+
function isRecord5(value) {
|
|
21258
22584
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21259
22585
|
}
|
|
21260
22586
|
function parseJson2(encoded) {
|
|
@@ -21266,7 +22592,7 @@ function parseJson2(encoded) {
|
|
|
21266
22592
|
cause: error61
|
|
21267
22593
|
});
|
|
21268
22594
|
}
|
|
21269
|
-
if (!
|
|
22595
|
+
if (!isRecord5(value)) {
|
|
21270
22596
|
throw new ProtocolMessageError("Message must be a JSON object");
|
|
21271
22597
|
}
|
|
21272
22598
|
return value;
|
|
@@ -21350,7 +22676,7 @@ function readActivityContent(value) {
|
|
|
21350
22676
|
}
|
|
21351
22677
|
function readActivityDetail(value) {
|
|
21352
22678
|
const field = value["detail"];
|
|
21353
|
-
if (!
|
|
22679
|
+
if (!isRecord5(field)) {
|
|
21354
22680
|
throw new ProtocolMessageError("Invalid activity detail field");
|
|
21355
22681
|
}
|
|
21356
22682
|
const kind = readString(field, "kind", 32);
|
|
@@ -21641,7 +22967,10 @@ var sessionHistorySchema = external_exports.discriminatedUnion("status", [
|
|
|
21641
22967
|
status: external_exports.literal("available"),
|
|
21642
22968
|
messages: external_exports.array(historyMessage).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
|
|
21643
22969
|
truncated: external_exports.boolean(),
|
|
21644
|
-
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([])
|
|
21645
22974
|
}).readonly(),
|
|
21646
22975
|
external_exports.strictObject({ status: external_exports.literal("unavailable") }).readonly()
|
|
21647
22976
|
]).refine(
|
|
@@ -22315,14 +23644,15 @@ import { WebSocket as WebSocket2 } from "undici";
|
|
|
22315
23644
|
// src/acp/session-manager.ts
|
|
22316
23645
|
import { spawn as spawn3 } from "node:child_process";
|
|
22317
23646
|
import {
|
|
22318
|
-
methods as
|
|
23647
|
+
methods as methods4,
|
|
22319
23648
|
ndJsonStream as ndJsonStream2,
|
|
22320
|
-
PROTOCOL_VERSION as PROTOCOL_VERSION2
|
|
23649
|
+
PROTOCOL_VERSION as PROTOCOL_VERSION2,
|
|
23650
|
+
RequestError as RequestError2
|
|
22321
23651
|
} from "@agentclientprotocol/sdk";
|
|
22322
23652
|
|
|
22323
23653
|
// src/acp/activity.ts
|
|
22324
23654
|
var TRUNCATION_MARKER = "\n\u2026 output truncated by Mobius";
|
|
22325
|
-
function
|
|
23655
|
+
function isRecord6(value) {
|
|
22326
23656
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22327
23657
|
}
|
|
22328
23658
|
var AcpActivity = class _AcpActivity {
|
|
@@ -22596,7 +23926,7 @@ function contentPhase(content) {
|
|
|
22596
23926
|
return "";
|
|
22597
23927
|
}
|
|
22598
23928
|
const codex = metadata["codex"];
|
|
22599
|
-
if (!
|
|
23929
|
+
if (!isRecord6(codex)) {
|
|
22600
23930
|
return "";
|
|
22601
23931
|
}
|
|
22602
23932
|
const phase = codex["phase"];
|
|
@@ -22891,13 +24221,14 @@ var AcpHistoryReplay = class {
|
|
|
22891
24221
|
status: "available",
|
|
22892
24222
|
messages: [...this.#messages],
|
|
22893
24223
|
truncated: this.#truncated,
|
|
22894
|
-
coveredCommandIds: []
|
|
24224
|
+
coveredCommandIds: [],
|
|
24225
|
+
coveredMessageIds: []
|
|
22895
24226
|
});
|
|
22896
24227
|
};
|
|
22897
24228
|
|
|
22898
24229
|
// src/acp/session-store.ts
|
|
22899
24230
|
import { mkdirSync as mkdirSync2 } from "node:fs";
|
|
22900
|
-
import { dirname as
|
|
24231
|
+
import { dirname as dirname4 } from "node:path";
|
|
22901
24232
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
22902
24233
|
var AcpStoredSession = class {
|
|
22903
24234
|
additionalDirectories;
|
|
@@ -23000,7 +24331,7 @@ var MemoryAcpSessionStore = class {
|
|
|
23000
24331
|
var SqliteAcpSessionStore = class {
|
|
23001
24332
|
#database;
|
|
23002
24333
|
constructor(path2) {
|
|
23003
|
-
mkdirSync2(
|
|
24334
|
+
mkdirSync2(dirname4(path2), { recursive: true });
|
|
23004
24335
|
this.#database = new DatabaseSync2(path2);
|
|
23005
24336
|
this.#database.exec(`
|
|
23006
24337
|
CREATE TABLE IF NOT EXISTS acp_deleted_sessions (session_id TEXT PRIMARY KEY) STRICT;
|
|
@@ -23107,6 +24438,8 @@ var SqliteAcpSessionStore = class {
|
|
|
23107
24438
|
|
|
23108
24439
|
// src/acp/session-manager.ts
|
|
23109
24440
|
var PROCESS_STOP_TIMEOUT_MILLISECONDS = 1e3;
|
|
24441
|
+
var MAX_PRE_SESSION_UPDATES = 100;
|
|
24442
|
+
var MAX_PRE_SESSION_UPDATE_BYTES = 1024 * 1024;
|
|
23110
24443
|
var SESSION_CONTROL_COMMAND_ID = "session-control";
|
|
23111
24444
|
var NullAcpSessionObserver = class {
|
|
23112
24445
|
onCommands = () => Promise.resolve();
|
|
@@ -23227,7 +24560,7 @@ function createWritableAcpStream(output2) {
|
|
|
23227
24560
|
function truncate2(value, maximumLength) {
|
|
23228
24561
|
return value.length <= maximumLength ? value : `${value.slice(0, Math.max(0, maximumLength - 1))}\u2026`;
|
|
23229
24562
|
}
|
|
23230
|
-
function
|
|
24563
|
+
function isRecord7(value) {
|
|
23231
24564
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23232
24565
|
}
|
|
23233
24566
|
function readString3(value, fallback = "") {
|
|
@@ -23257,7 +24590,7 @@ function normalizeConfigValues(value) {
|
|
|
23257
24590
|
}
|
|
23258
24591
|
const normalized = [];
|
|
23259
24592
|
for (const entry of value) {
|
|
23260
|
-
if (!
|
|
24593
|
+
if (!isRecord7(entry)) {
|
|
23261
24594
|
continue;
|
|
23262
24595
|
}
|
|
23263
24596
|
const groupedOptions = entry["options"];
|
|
@@ -23265,7 +24598,7 @@ function normalizeConfigValues(value) {
|
|
|
23265
24598
|
const groupId = readString3(entry["group"]);
|
|
23266
24599
|
const groupName = readString3(entry["name"]);
|
|
23267
24600
|
for (const groupedOption of groupedOptions) {
|
|
23268
|
-
if (
|
|
24601
|
+
if (isRecord7(groupedOption)) {
|
|
23269
24602
|
normalized.push(
|
|
23270
24603
|
normalizeConfigValue(groupedOption, groupId, groupName)
|
|
23271
24604
|
);
|
|
@@ -23281,7 +24614,7 @@ function normalizeConfigOptions(value, modes) {
|
|
|
23281
24614
|
const normalized = [];
|
|
23282
24615
|
if (Array.isArray(value)) {
|
|
23283
24616
|
for (const option of value) {
|
|
23284
|
-
if (!
|
|
24617
|
+
if (!isRecord7(option)) {
|
|
23285
24618
|
continue;
|
|
23286
24619
|
}
|
|
23287
24620
|
const id = truncate2(readString3(option["id"]), 128);
|
|
@@ -23320,12 +24653,12 @@ function normalizeConfigOptions(value, modes) {
|
|
|
23320
24653
|
}
|
|
23321
24654
|
}
|
|
23322
24655
|
}
|
|
23323
|
-
if (
|
|
24656
|
+
if (isRecord7(modes)) {
|
|
23324
24657
|
const values = [];
|
|
23325
24658
|
const availableModes = modes["availableModes"];
|
|
23326
24659
|
if (Array.isArray(availableModes)) {
|
|
23327
24660
|
for (const mode of availableModes) {
|
|
23328
|
-
if (!
|
|
24661
|
+
if (!isRecord7(mode)) {
|
|
23329
24662
|
continue;
|
|
23330
24663
|
}
|
|
23331
24664
|
const id = truncate2(readString3(mode["id"]), 500);
|
|
@@ -23403,7 +24736,7 @@ function normalizeElicitationOptions(schema) {
|
|
|
23403
24736
|
const oneOf = schema["oneOf"];
|
|
23404
24737
|
if (Array.isArray(oneOf)) {
|
|
23405
24738
|
for (const option of oneOf) {
|
|
23406
|
-
if (
|
|
24739
|
+
if (isRecord7(option) && typeof option["const"] === "string") {
|
|
23407
24740
|
normalized.push({
|
|
23408
24741
|
description: truncate2(readString3(option["description"]), 500),
|
|
23409
24742
|
label: truncate2(readString3(option["title"], option["const"]), 255),
|
|
@@ -23423,7 +24756,7 @@ function normalizeElicitationOptions(schema) {
|
|
|
23423
24756
|
return normalized.slice(0, 200);
|
|
23424
24757
|
}
|
|
23425
24758
|
const items = schema["items"];
|
|
23426
|
-
if (!
|
|
24759
|
+
if (!isRecord7(items)) {
|
|
23427
24760
|
return normalized;
|
|
23428
24761
|
}
|
|
23429
24762
|
const anyOf = items["anyOf"];
|
|
@@ -23436,7 +24769,7 @@ function numericString(value) {
|
|
|
23436
24769
|
return typeof value === "number" && Number.isFinite(value) ? value.toString() : "";
|
|
23437
24770
|
}
|
|
23438
24771
|
function normalizeElicitationFields(requestedSchema) {
|
|
23439
|
-
if (!
|
|
24772
|
+
if (!isRecord7(requestedSchema) || !isRecord7(requestedSchema["properties"])) {
|
|
23440
24773
|
return [];
|
|
23441
24774
|
}
|
|
23442
24775
|
const properties = requestedSchema["properties"];
|
|
@@ -23446,7 +24779,7 @@ function normalizeElicitationFields(requestedSchema) {
|
|
|
23446
24779
|
);
|
|
23447
24780
|
const normalized = [];
|
|
23448
24781
|
for (const [name, property] of Object.entries(properties)) {
|
|
23449
|
-
if (!
|
|
24782
|
+
if (!isRecord7(property)) {
|
|
23450
24783
|
continue;
|
|
23451
24784
|
}
|
|
23452
24785
|
const type = elicitationFieldType(property["type"]);
|
|
@@ -23541,7 +24874,7 @@ function validateElicitationContent(encoded, requestedSchema) {
|
|
|
23541
24874
|
"invalid_elicitation_response"
|
|
23542
24875
|
);
|
|
23543
24876
|
}
|
|
23544
|
-
if (!
|
|
24877
|
+
if (!isRecord7(decoded) || !isRecord7(requestedSchema) || !isRecord7(requestedSchema["properties"])) {
|
|
23545
24878
|
throw new AcpExecutionError(
|
|
23546
24879
|
"Elicitation response does not match the requested form",
|
|
23547
24880
|
{},
|
|
@@ -23565,7 +24898,7 @@ function validateElicitationContent(encoded, requestedSchema) {
|
|
|
23565
24898
|
const validated = {};
|
|
23566
24899
|
for (const [name, value] of Object.entries(decoded)) {
|
|
23567
24900
|
const property = properties[name];
|
|
23568
|
-
if (!
|
|
24901
|
+
if (!isRecord7(property)) {
|
|
23569
24902
|
throw new AcpExecutionError(
|
|
23570
24903
|
`Elicitation field ${name} was not requested`,
|
|
23571
24904
|
{},
|
|
@@ -23715,8 +25048,11 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
23715
25048
|
#mobiusSessionId;
|
|
23716
25049
|
#pendingElicitations = /* @__PURE__ */ new Map();
|
|
23717
25050
|
#pendingPermissions = /* @__PURE__ */ new Map();
|
|
25051
|
+
#preSessionUpdates = [];
|
|
23718
25052
|
#processExit;
|
|
23719
25053
|
#store;
|
|
25054
|
+
#verifiedAgentVersion;
|
|
25055
|
+
#preSessionUpdateBytes = 0;
|
|
23720
25056
|
#agentName = "";
|
|
23721
25057
|
#agentVersion = "";
|
|
23722
25058
|
#capabilities = {
|
|
@@ -23750,6 +25086,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
23750
25086
|
this.#getObserver = getObserver;
|
|
23751
25087
|
this.#mobiusSessionId = mobiusSessionId;
|
|
23752
25088
|
this.#store = store;
|
|
25089
|
+
this.#verifiedAgentVersion = agent.agentVersion;
|
|
23753
25090
|
this.#child = spawn3(agent.adapterPath, [...agent.arguments], {
|
|
23754
25091
|
cwd,
|
|
23755
25092
|
detached: process.platform !== "win32",
|
|
@@ -23852,7 +25189,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
23852
25189
|
try {
|
|
23853
25190
|
await this.race(
|
|
23854
25191
|
this.#connection.agent.request(
|
|
23855
|
-
|
|
25192
|
+
methods4.agent.session.load,
|
|
23856
25193
|
{
|
|
23857
25194
|
additionalDirectories: [...this.#additionalDirectories],
|
|
23858
25195
|
cwd: this.#cwd,
|
|
@@ -23874,7 +25211,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
23874
25211
|
async #initializeAgent(signal) {
|
|
23875
25212
|
const initialized = await this.race(
|
|
23876
25213
|
this.#connection.agent.request(
|
|
23877
|
-
|
|
25214
|
+
methods4.agent.initialize,
|
|
23878
25215
|
{
|
|
23879
25216
|
clientCapabilities: {
|
|
23880
25217
|
elicitation: { form: {}, url: {} },
|
|
@@ -23905,7 +25242,10 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
23905
25242
|
initialized.agentInfo?.title ?? initialized.agentInfo?.name ?? this.#agentId,
|
|
23906
25243
|
255
|
|
23907
25244
|
);
|
|
23908
|
-
this.#agentVersion = truncate2(
|
|
25245
|
+
this.#agentVersion = truncate2(
|
|
25246
|
+
this.#verifiedAgentVersion.length > 0 ? this.#verifiedAgentVersion : initialized.agentInfo?.version ?? "",
|
|
25247
|
+
128
|
|
25248
|
+
);
|
|
23909
25249
|
}
|
|
23910
25250
|
initialize = async (signal, origin) => {
|
|
23911
25251
|
await this.#initializeAgent(signal);
|
|
@@ -23939,7 +25279,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
23939
25279
|
);
|
|
23940
25280
|
const forked = await this.race(
|
|
23941
25281
|
this.#connection.agent.request(
|
|
23942
|
-
|
|
25282
|
+
methods4.agent.session.fork,
|
|
23943
25283
|
{
|
|
23944
25284
|
sessionId: nativeSessionIdSchema.parse(origin.nativeSessionId),
|
|
23945
25285
|
cwd: this.#cwd,
|
|
@@ -23951,7 +25291,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
23951
25291
|
"ACP agent exited while forking a session",
|
|
23952
25292
|
signal
|
|
23953
25293
|
);
|
|
23954
|
-
this
|
|
25294
|
+
this.selectNativeSession(forked.sessionId);
|
|
23955
25295
|
if (this.#nativeSessionId === origin.nativeSessionId || this.#store.findNative(this.#agentId, this.#nativeSessionId).length > 0) {
|
|
23956
25296
|
throw new AcpExecutionError(
|
|
23957
25297
|
"The Agent did not create an independent fork",
|
|
@@ -23966,13 +25306,13 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
23966
25306
|
stored.session.additionalDirectories,
|
|
23967
25307
|
this.#additionalDirectories
|
|
23968
25308
|
)) {
|
|
23969
|
-
this
|
|
25309
|
+
this.selectNativeSession(stored.session.nativeSessionId);
|
|
23970
25310
|
this.#prompted = stored.session.prompted;
|
|
23971
25311
|
this.#restored = true;
|
|
23972
25312
|
if (this.#capabilities.resumeSession) {
|
|
23973
25313
|
lifecycleResponse = await this.race(
|
|
23974
25314
|
this.#connection.agent.request(
|
|
23975
|
-
|
|
25315
|
+
methods4.agent.session.resume,
|
|
23976
25316
|
{
|
|
23977
25317
|
additionalDirectories: [...this.#additionalDirectories],
|
|
23978
25318
|
cwd: this.#cwd,
|
|
@@ -23989,7 +25329,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
23989
25329
|
try {
|
|
23990
25330
|
lifecycleResponse = await this.race(
|
|
23991
25331
|
this.#connection.agent.request(
|
|
23992
|
-
|
|
25332
|
+
methods4.agent.session.load,
|
|
23993
25333
|
{
|
|
23994
25334
|
additionalDirectories: [...this.#additionalDirectories],
|
|
23995
25335
|
cwd: this.#cwd,
|
|
@@ -24025,7 +25365,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24025
25365
|
}
|
|
24026
25366
|
const created = await this.race(
|
|
24027
25367
|
this.#connection.agent.request(
|
|
24028
|
-
|
|
25368
|
+
methods4.agent.session.new,
|
|
24029
25369
|
{
|
|
24030
25370
|
additionalDirectories: [...this.#additionalDirectories],
|
|
24031
25371
|
cwd: this.#cwd,
|
|
@@ -24036,10 +25376,10 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24036
25376
|
"ACP agent exited while starting a session",
|
|
24037
25377
|
signal
|
|
24038
25378
|
);
|
|
24039
|
-
this
|
|
25379
|
+
this.selectNativeSession(created.sessionId);
|
|
24040
25380
|
lifecycleResponse = created;
|
|
24041
25381
|
}
|
|
24042
|
-
const lifecycleRecord =
|
|
25382
|
+
const lifecycleRecord = isRecord7(lifecycleResponse) ? lifecycleResponse : {};
|
|
24043
25383
|
this.#configOptions = normalizeConfigOptions(
|
|
24044
25384
|
lifecycleRecord["configOptions"],
|
|
24045
25385
|
lifecycleRecord["modes"]
|
|
@@ -24068,7 +25408,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24068
25408
|
);
|
|
24069
25409
|
const response = await this.race(
|
|
24070
25410
|
this.#connection.agent.request(
|
|
24071
|
-
|
|
25411
|
+
methods4.agent.session.list,
|
|
24072
25412
|
cursor === null ? {} : { cursor },
|
|
24073
25413
|
{ cancellationSignal: signal }
|
|
24074
25414
|
),
|
|
@@ -24093,7 +25433,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24093
25433
|
);
|
|
24094
25434
|
await this.race(
|
|
24095
25435
|
this.#connection.agent.request(
|
|
24096
|
-
|
|
25436
|
+
methods4.agent.session.delete,
|
|
24097
25437
|
{ sessionId: nativeSessionIdSchema.parse(nativeSessionId) },
|
|
24098
25438
|
{ cancellationSignal: signal }
|
|
24099
25439
|
),
|
|
@@ -24124,7 +25464,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24124
25464
|
if (existing.category === "mode") {
|
|
24125
25465
|
await this.race(
|
|
24126
25466
|
this.#connection.agent.request(
|
|
24127
|
-
|
|
25467
|
+
methods4.agent.session.setMode,
|
|
24128
25468
|
{ modeId: options.value, sessionId: this.#nativeSessionId },
|
|
24129
25469
|
{ cancellationSignal: options.signal }
|
|
24130
25470
|
),
|
|
@@ -24137,7 +25477,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24137
25477
|
} else {
|
|
24138
25478
|
const response = await this.race(
|
|
24139
25479
|
this.#connection.agent.request(
|
|
24140
|
-
|
|
25480
|
+
methods4.agent.session.setConfigOption,
|
|
24141
25481
|
{
|
|
24142
25482
|
configId: existing.id,
|
|
24143
25483
|
sessionId: this.#nativeSessionId,
|
|
@@ -24156,7 +25496,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24156
25496
|
} else {
|
|
24157
25497
|
const response = await this.race(
|
|
24158
25498
|
this.#connection.agent.request(
|
|
24159
|
-
|
|
25499
|
+
methods4.agent.session.setConfigOption,
|
|
24160
25500
|
{
|
|
24161
25501
|
configId: existing.id,
|
|
24162
25502
|
sessionId: this.#nativeSessionId,
|
|
@@ -24255,7 +25595,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24255
25595
|
try {
|
|
24256
25596
|
const response = await this.race(
|
|
24257
25597
|
this.#connection.agent.request(
|
|
24258
|
-
|
|
25598
|
+
methods4.agent.session.prompt,
|
|
24259
25599
|
{ prompt, sessionId: this.#nativeSessionId },
|
|
24260
25600
|
{ cancellationSignal: options.signal }
|
|
24261
25601
|
),
|
|
@@ -24298,13 +25638,17 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24298
25638
|
}
|
|
24299
25639
|
};
|
|
24300
25640
|
cancel = async () => {
|
|
24301
|
-
await this
|
|
24302
|
-
|
|
24303
|
-
|
|
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);
|
|
24304
25651
|
}
|
|
24305
|
-
await this.#connection.agent.notify(methods3.agent.session.cancel, {
|
|
24306
|
-
sessionId: this.#nativeSessionId
|
|
24307
|
-
});
|
|
24308
25652
|
};
|
|
24309
25653
|
respondPermission = async (options) => {
|
|
24310
25654
|
const pending = this.#pendingPermissions.get(options.interactionId);
|
|
@@ -24491,11 +25835,45 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24491
25835
|
});
|
|
24492
25836
|
};
|
|
24493
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) => {
|
|
24494
25856
|
this.#updateTail = this.#updateTail.then(
|
|
24495
25857
|
async () => await this.processUpdate(notification)
|
|
24496
25858
|
);
|
|
24497
25859
|
return this.#updateTail;
|
|
24498
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
|
+
};
|
|
24499
25877
|
processUpdate = async (notification) => {
|
|
24500
25878
|
this.assertNativeSession(notification.sessionId);
|
|
24501
25879
|
const update = notification.update;
|
|
@@ -24620,7 +25998,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24620
25998
|
if (this.#capabilities.closeSession && this.#nativeSessionId.length > 0) {
|
|
24621
25999
|
try {
|
|
24622
26000
|
await this.#connection.agent.request(
|
|
24623
|
-
|
|
26001
|
+
methods4.agent.session.close,
|
|
24624
26002
|
{ sessionId: this.#nativeSessionId },
|
|
24625
26003
|
{
|
|
24626
26004
|
cancellationSignal: AbortSignal.timeout(
|
|
@@ -24633,9 +26011,12 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24633
26011
|
}
|
|
24634
26012
|
await this.closeProcess();
|
|
24635
26013
|
};
|
|
24636
|
-
closeProcess = async () => {
|
|
24637
|
-
|
|
24638
|
-
|
|
26014
|
+
closeProcess = async (capturedProcesses = []) => {
|
|
26015
|
+
try {
|
|
26016
|
+
await stopProcessTree(this.#child, capturedProcesses);
|
|
26017
|
+
} finally {
|
|
26018
|
+
this.#connection.close();
|
|
26019
|
+
}
|
|
24639
26020
|
};
|
|
24640
26021
|
race = async (operation, exitMessage, signal) => {
|
|
24641
26022
|
signal.throwIfAborted();
|
|
@@ -24651,13 +26032,25 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
24651
26032
|
};
|
|
24652
26033
|
});
|
|
24653
26034
|
try {
|
|
24654
|
-
|
|
24655
|
-
|
|
24656
|
-
|
|
24657
|
-
|
|
24658
|
-
|
|
24659
|
-
|
|
24660
|
-
|
|
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
|
+
}
|
|
24661
26054
|
} finally {
|
|
24662
26055
|
removeAbortListener();
|
|
24663
26056
|
}
|
|
@@ -24916,7 +26309,13 @@ var AcpSessionManager = class {
|
|
|
24916
26309
|
this.#cancellationRequests.add(sessionId);
|
|
24917
26310
|
const runtime = this.#sessions.get(sessionId);
|
|
24918
26311
|
if (runtime !== void 0) {
|
|
24919
|
-
|
|
26312
|
+
try {
|
|
26313
|
+
await runtime.cancel();
|
|
26314
|
+
} finally {
|
|
26315
|
+
if (this.#sessions.get(sessionId) === runtime) {
|
|
26316
|
+
this.#sessions.delete(sessionId);
|
|
26317
|
+
}
|
|
26318
|
+
}
|
|
24920
26319
|
}
|
|
24921
26320
|
};
|
|
24922
26321
|
runPrompt = async (options) => {
|
|
@@ -24937,12 +26336,27 @@ var AcpSessionManager = class {
|
|
|
24937
26336
|
"session_busy"
|
|
24938
26337
|
);
|
|
24939
26338
|
}
|
|
26339
|
+
const deadlineController = new AbortController();
|
|
26340
|
+
const deadlineError = new AcpExecutionError(
|
|
26341
|
+
"The local Agent prompt exceeded its deadline",
|
|
26342
|
+
{},
|
|
26343
|
+
"prompt_timeout"
|
|
26344
|
+
);
|
|
26345
|
+
const deadline = setTimeout(
|
|
26346
|
+
() => deadlineController.abort(deadlineError),
|
|
26347
|
+
options.agent.promptTimeoutMilliseconds
|
|
26348
|
+
);
|
|
26349
|
+
deadline.unref();
|
|
26350
|
+
const boundedOptions = {
|
|
26351
|
+
...options,
|
|
26352
|
+
signal: AbortSignal.any([options.signal, deadlineController.signal])
|
|
26353
|
+
};
|
|
24940
26354
|
this.#activeSessionIds.add(options.sessionId);
|
|
24941
26355
|
try {
|
|
24942
26356
|
if (this.#cancellationRequests.has(options.sessionId)) {
|
|
24943
26357
|
return { stopReason: "cancelled" };
|
|
24944
26358
|
}
|
|
24945
|
-
const runtime = await this.ensureSession(
|
|
26359
|
+
const runtime = await this.ensureSession(boundedOptions);
|
|
24946
26360
|
if (this.#nativeOperations.has(
|
|
24947
26361
|
`${options.agent.id}:${runtime.nativeSessionId()}`
|
|
24948
26362
|
))
|
|
@@ -24954,7 +26368,19 @@ var AcpSessionManager = class {
|
|
|
24954
26368
|
if (this.#cancellationRequests.has(options.sessionId)) {
|
|
24955
26369
|
return { stopReason: "cancelled" };
|
|
24956
26370
|
}
|
|
24957
|
-
|
|
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
|
+
}
|
|
24958
26384
|
} catch (error61) {
|
|
24959
26385
|
const failedRuntime = this.#sessions.get(options.sessionId);
|
|
24960
26386
|
if (failedRuntime !== void 0) {
|
|
@@ -24968,6 +26394,7 @@ var AcpSessionManager = class {
|
|
|
24968
26394
|
cause: error61
|
|
24969
26395
|
});
|
|
24970
26396
|
} finally {
|
|
26397
|
+
clearTimeout(deadline);
|
|
24971
26398
|
this.#activeSessionIds.delete(options.sessionId);
|
|
24972
26399
|
this.#cancellationRequests.delete(options.sessionId);
|
|
24973
26400
|
}
|
|
@@ -25173,7 +26600,7 @@ var AcpSessionManager = class {
|
|
|
25173
26600
|
};
|
|
25174
26601
|
|
|
25175
26602
|
// src/agent-catalog/publish-agent-catalog.ts
|
|
25176
|
-
import { fetch as
|
|
26603
|
+
import { fetch as fetch3 } from "undici";
|
|
25177
26604
|
var CATALOG_UPDATE_TIMEOUT_MILLISECONDS = 2e4;
|
|
25178
26605
|
var AgentCatalogPublishError = class extends Error {
|
|
25179
26606
|
code = "agent_catalog_update_failed";
|
|
@@ -25200,7 +26627,7 @@ async function publishAgentCatalog(config2, signal) {
|
|
|
25200
26627
|
let response;
|
|
25201
26628
|
const catalog = config2.catalog.read();
|
|
25202
26629
|
try {
|
|
25203
|
-
response = await
|
|
26630
|
+
response = await fetch3(catalogEndpoint(config2.relayUrl), {
|
|
25204
26631
|
body: JSON.stringify({
|
|
25205
26632
|
agents: catalog.agents.map((agent) => ({
|
|
25206
26633
|
id: agent.id,
|
|
@@ -25606,7 +27033,7 @@ var InstalledAutomaticAdapter = class {
|
|
|
25606
27033
|
}
|
|
25607
27034
|
};
|
|
25608
27035
|
function shouldProvisionAdapter(unavailable, options) {
|
|
25609
|
-
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(
|
|
25610
27037
|
options.platform ?? process.platform,
|
|
25611
27038
|
options.architecture ?? process.arch
|
|
25612
27039
|
);
|
|
@@ -25650,6 +27077,10 @@ var AutomaticAdapterProvisioner = class {
|
|
|
25650
27077
|
for (const unavailable of candidates) {
|
|
25651
27078
|
signal.throwIfAborted();
|
|
25652
27079
|
const specification = unavailable.specification;
|
|
27080
|
+
if (!(specification instanceof BridgedAcpAgentDefinition)) {
|
|
27081
|
+
attempts.push(null);
|
|
27082
|
+
continue;
|
|
27083
|
+
}
|
|
25653
27084
|
const verifiedAgent = await findVerifiedAgentExecutable(
|
|
25654
27085
|
specification,
|
|
25655
27086
|
discoveryOptions(
|
|
@@ -25729,252 +27160,59 @@ var AutomaticAdapterProvisioner = class {
|
|
|
25729
27160
|
this.#options,
|
|
25730
27161
|
verificationEnvironment,
|
|
25731
27162
|
signal,
|
|
25732
|
-
this.#specifications
|
|
25733
|
-
)
|
|
25734
|
-
);
|
|
25735
|
-
}
|
|
25736
|
-
this.#options.catalog.transition(
|
|
25737
|
-
inventory.readyAgents(),
|
|
25738
|
-
inventory.installable
|
|
25739
|
-
);
|
|
25740
|
-
for (const attempt of installed) {
|
|
25741
|
-
const available = inventory.available.find(
|
|
25742
|
-
(candidate) => candidate instanceof AvailableAgent && candidate.specification.id === attempt.specification.id
|
|
25743
|
-
);
|
|
25744
|
-
if (available === void 0) {
|
|
25745
|
-
const unavailable = inventory.unavailable.find(
|
|
25746
|
-
(candidate) => candidate.specification.id === attempt.specification.id
|
|
25747
|
-
);
|
|
25748
|
-
this.#options.audit.record(
|
|
25749
|
-
"agent_adapter_auto_install_verification_failed",
|
|
25750
|
-
attempt.requestId,
|
|
25751
|
-
attempt.specification.id,
|
|
25752
|
-
{ code: unavailable?.code ?? "agent_verification_failed" }
|
|
25753
|
-
);
|
|
25754
|
-
continue;
|
|
25755
|
-
}
|
|
25756
|
-
this.#options.audit.record(
|
|
25757
|
-
"agent_adapter_auto_install_verified",
|
|
25758
|
-
attempt.requestId,
|
|
25759
|
-
attempt.specification.id,
|
|
25760
|
-
{
|
|
25761
|
-
adapterVersion: available.agent.adapterVersion,
|
|
25762
|
-
agentVersion: available.agent.agentVersion
|
|
25763
|
-
}
|
|
25764
|
-
);
|
|
25765
|
-
process.stdout.write(
|
|
25766
|
-
`${attempt.specification.label} ACP integration is ready.
|
|
25767
|
-
`
|
|
25768
|
-
);
|
|
25769
|
-
}
|
|
25770
|
-
return inventory;
|
|
25771
|
-
};
|
|
25772
|
-
};
|
|
25773
|
-
|
|
25774
|
-
// src/agent-installation/fixed-installation-executor.ts
|
|
25775
|
-
import { realpathSync as realpathSync2, statSync as statSync2 } from "node:fs";
|
|
25776
|
-
import { resolve as resolve4 } from "node:path";
|
|
25777
|
-
|
|
25778
|
-
// src/agent-installation/fixed-agent-install-recipes.ts
|
|
25779
|
-
import { mkdir as mkdir3, mkdtemp as mkdtemp2, rm as rm2 } from "node:fs/promises";
|
|
25780
|
-
import { join as join8 } from "node:path";
|
|
25781
|
-
|
|
25782
|
-
// src/agent-installation/verified-download.ts
|
|
25783
|
-
import { createHash } from "node:crypto";
|
|
25784
|
-
import { open as open5, unlink } from "node:fs/promises";
|
|
25785
|
-
import { fetch as fetch3 } from "undici";
|
|
25786
|
-
var MAX_REDIRECTS = 4;
|
|
25787
|
-
var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
25788
|
-
var InstallerDownloadError = class extends Error {
|
|
25789
|
-
code;
|
|
25790
|
-
constructor(code, message, options = {}) {
|
|
25791
|
-
super(message, options);
|
|
25792
|
-
this.code = code;
|
|
25793
|
-
this.name = "InstallerDownloadError";
|
|
25794
|
-
}
|
|
25795
|
-
};
|
|
25796
|
-
var VerifiedDownload = class {
|
|
25797
|
-
bytes;
|
|
25798
|
-
sha256;
|
|
25799
|
-
url;
|
|
25800
|
-
constructor(url2, bytes, sha256) {
|
|
25801
|
-
this.bytes = bytes;
|
|
25802
|
-
this.sha256 = sha256;
|
|
25803
|
-
this.url = url2;
|
|
25804
|
-
}
|
|
25805
|
-
};
|
|
25806
|
-
var UndiciInstallerDownloadTransport = class {
|
|
25807
|
-
get = async (url2, signal) => await fetch3(url2, {
|
|
25808
|
-
dispatcher: environmentProxyDispatcher,
|
|
25809
|
-
headers: {
|
|
25810
|
-
accept: "text/plain, application/octet-stream;q=0.9",
|
|
25811
|
-
"user-agent": "Mobius-Agent-Installer/0.0.3"
|
|
25812
|
-
},
|
|
25813
|
-
method: "GET",
|
|
25814
|
-
redirect: "manual",
|
|
25815
|
-
signal
|
|
25816
|
-
});
|
|
25817
|
-
};
|
|
25818
|
-
function validateUrl(url2, allowedHosts) {
|
|
25819
|
-
if (url2.protocol !== "https:" || url2.username.length > 0 || url2.password.length > 0 || url2.port.length > 0 || !allowedHosts.has(url2.hostname)) {
|
|
25820
|
-
throw new InstallerDownloadError(
|
|
25821
|
-
"installer_host_not_allowed",
|
|
25822
|
-
"The installer download target is not allowed"
|
|
25823
|
-
);
|
|
25824
|
-
}
|
|
25825
|
-
}
|
|
25826
|
-
async function fetchInstaller(options, signal, transport) {
|
|
25827
|
-
let url2 = new URL(options.url);
|
|
25828
|
-
for (let redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount += 1) {
|
|
25829
|
-
validateUrl(url2, options.allowedHosts);
|
|
25830
|
-
const response = await transport.get(url2, signal);
|
|
25831
|
-
if (!REDIRECT_STATUSES.has(response.status)) {
|
|
25832
|
-
return { response, url: url2 };
|
|
25833
|
-
}
|
|
25834
|
-
await response.body?.cancel();
|
|
25835
|
-
const location = response.headers.get("location");
|
|
25836
|
-
if (location === null || redirectCount === MAX_REDIRECTS) {
|
|
25837
|
-
throw new InstallerDownloadError(
|
|
25838
|
-
"installer_redirect_invalid",
|
|
25839
|
-
"The installer returned an invalid redirect"
|
|
25840
|
-
);
|
|
25841
|
-
}
|
|
25842
|
-
let redirected;
|
|
25843
|
-
try {
|
|
25844
|
-
redirected = new URL(location, url2);
|
|
25845
|
-
} catch (error61) {
|
|
25846
|
-
throw new InstallerDownloadError(
|
|
25847
|
-
"installer_redirect_invalid",
|
|
25848
|
-
"The installer returned an invalid redirect",
|
|
25849
|
-
{ cause: error61 }
|
|
25850
|
-
);
|
|
25851
|
-
}
|
|
25852
|
-
try {
|
|
25853
|
-
validateUrl(redirected, options.allowedHosts);
|
|
25854
|
-
} catch (error61) {
|
|
25855
|
-
throw new InstallerDownloadError(
|
|
25856
|
-
"installer_redirect_not_allowed",
|
|
25857
|
-
"The installer redirect left the approved host allowlist",
|
|
25858
|
-
{ cause: error61 }
|
|
25859
|
-
);
|
|
25860
|
-
}
|
|
25861
|
-
url2 = redirected;
|
|
25862
|
-
}
|
|
25863
|
-
throw new InstallerDownloadError(
|
|
25864
|
-
"installer_redirect_invalid",
|
|
25865
|
-
"The installer returned too many redirects"
|
|
25866
|
-
);
|
|
25867
|
-
}
|
|
25868
|
-
async function writeCompleteChunk(handle, chunk) {
|
|
25869
|
-
let offset = 0;
|
|
25870
|
-
while (offset < chunk.byteLength) {
|
|
25871
|
-
const written = await handle.write(
|
|
25872
|
-
chunk,
|
|
25873
|
-
offset,
|
|
25874
|
-
chunk.byteLength - offset
|
|
25875
|
-
);
|
|
25876
|
-
if (written.bytesWritten < 1) {
|
|
25877
|
-
throw new InstallerDownloadError(
|
|
25878
|
-
"installer_write_failed",
|
|
25879
|
-
"The verified installer could not be stored"
|
|
27163
|
+
this.#specifications
|
|
27164
|
+
)
|
|
25880
27165
|
);
|
|
25881
27166
|
}
|
|
25882
|
-
|
|
25883
|
-
|
|
25884
|
-
|
|
25885
|
-
|
|
25886
|
-
|
|
25887
|
-
|
|
25888
|
-
|
|
25889
|
-
let handle = null;
|
|
25890
|
-
let responseBody = null;
|
|
25891
|
-
try {
|
|
25892
|
-
const fetched = await fetchInstaller(options, signal, transport);
|
|
25893
|
-
responseBody = fetched.response.body;
|
|
25894
|
-
if (!fetched.response.ok || fetched.response.body === null) {
|
|
25895
|
-
throw new InstallerDownloadError(
|
|
25896
|
-
"installer_download_failed",
|
|
25897
|
-
"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
|
|
25898
27174
|
);
|
|
25899
|
-
|
|
25900
|
-
|
|
25901
|
-
|
|
25902
|
-
|
|
25903
|
-
|
|
25904
|
-
|
|
25905
|
-
|
|
25906
|
-
|
|
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" }
|
|
25907
27184
|
);
|
|
27185
|
+
continue;
|
|
25908
27186
|
}
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
25916
|
-
signal.throwIfAborted();
|
|
25917
|
-
const chunk = await reader.read();
|
|
25918
|
-
if (chunk.done) {
|
|
25919
|
-
break;
|
|
25920
|
-
}
|
|
25921
|
-
bytes += chunk.value.byteLength;
|
|
25922
|
-
if (bytes > options.maximumBytes) {
|
|
25923
|
-
throw new InstallerDownloadError(
|
|
25924
|
-
"installer_size_invalid",
|
|
25925
|
-
"The installer exceeded the approved size limit"
|
|
25926
|
-
);
|
|
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
|
|
25927
27194
|
}
|
|
25928
|
-
digest.update(chunk.value);
|
|
25929
|
-
await writeCompleteChunk(handle, chunk.value);
|
|
25930
|
-
}
|
|
25931
|
-
} finally {
|
|
25932
|
-
await reader.cancel().catch(() => void 0);
|
|
25933
|
-
reader.releaseLock();
|
|
25934
|
-
}
|
|
25935
|
-
if (bytes === 0) {
|
|
25936
|
-
throw new InstallerDownloadError(
|
|
25937
|
-
"installer_verification_failed",
|
|
25938
|
-
"The downloaded installer is empty"
|
|
25939
27195
|
);
|
|
25940
|
-
|
|
25941
|
-
|
|
25942
|
-
|
|
25943
|
-
handle = null;
|
|
25944
|
-
return new VerifiedDownload(fetched.url, bytes, digest.digest("hex"));
|
|
25945
|
-
} catch (error61) {
|
|
25946
|
-
if (handle !== null) {
|
|
25947
|
-
await handle.close().catch(() => void 0);
|
|
25948
|
-
}
|
|
25949
|
-
await unlink(options.destination).catch(() => void 0);
|
|
25950
|
-
if (options.signal.aborted) {
|
|
25951
|
-
options.signal.throwIfAborted();
|
|
25952
|
-
}
|
|
25953
|
-
if (timeoutSignal.aborted) {
|
|
25954
|
-
throw new InstallerDownloadError(
|
|
25955
|
-
"installer_download_timeout",
|
|
25956
|
-
"The installer download timed out",
|
|
25957
|
-
{ cause: error61 }
|
|
27196
|
+
process.stdout.write(
|
|
27197
|
+
`${attempt.specification.label} ACP integration is ready.
|
|
27198
|
+
`
|
|
25958
27199
|
);
|
|
25959
27200
|
}
|
|
25960
|
-
|
|
25961
|
-
|
|
25962
|
-
|
|
25963
|
-
|
|
25964
|
-
|
|
25965
|
-
|
|
25966
|
-
|
|
25967
|
-
);
|
|
25968
|
-
} finally {
|
|
25969
|
-
await responseBody?.cancel().catch(() => void 0);
|
|
25970
|
-
}
|
|
25971
|
-
}
|
|
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";
|
|
25972
27208
|
|
|
25973
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";
|
|
25974
27212
|
var INSTALLER_DOWNLOAD_LIMIT_BYTES = 1024 * 1024;
|
|
25975
27213
|
var INSTALLER_DOWNLOAD_TIMEOUT_MILLISECONDS = 6e4;
|
|
25976
27214
|
var AGENT_INSTALL_TIMEOUT_MILLISECONDS = 5 * 60 * 1e3;
|
|
25977
|
-
var
|
|
27215
|
+
var MINIMAL_PATH2 = "/usr/bin:/bin";
|
|
25978
27216
|
var FixedAgentInstallRecipe = class {
|
|
25979
27217
|
assertSupportedPlatform = () => {
|
|
25980
27218
|
if (process.platform !== "darwin" && process.platform !== "linux" || process.arch !== "arm64" && process.arch !== "x64") {
|
|
@@ -26001,22 +27239,22 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
26001
27239
|
releaseChannel = "latest";
|
|
26002
27240
|
install = async (options) => {
|
|
26003
27241
|
this.assertSupportedPlatform();
|
|
26004
|
-
const root =
|
|
26005
|
-
const temporaryRoot =
|
|
27242
|
+
const root = join14(options.stateDirectory, "agents", this.id);
|
|
27243
|
+
const temporaryRoot = join14(options.stateDirectory, "temporary");
|
|
26006
27244
|
await Promise.all([
|
|
26007
|
-
|
|
26008
|
-
|
|
27245
|
+
mkdir4(root, { mode: 448, recursive: true }),
|
|
27246
|
+
mkdir4(temporaryRoot, { mode: 448, recursive: true })
|
|
26009
27247
|
]);
|
|
26010
|
-
const temporaryDirectory = await
|
|
26011
|
-
|
|
27248
|
+
const temporaryDirectory = await mkdtemp3(
|
|
27249
|
+
join14(temporaryRoot, ".codex-install-")
|
|
26012
27250
|
);
|
|
26013
|
-
const installerPath =
|
|
26014
|
-
const installDirectory =
|
|
26015
|
-
const codexHome =
|
|
27251
|
+
const installerPath = join14(temporaryDirectory, "install.sh");
|
|
27252
|
+
const installDirectory = join14(root, "bin");
|
|
27253
|
+
const codexHome = join14(root, "home");
|
|
26016
27254
|
try {
|
|
26017
27255
|
await Promise.all([
|
|
26018
|
-
|
|
26019
|
-
|
|
27256
|
+
mkdir4(installDirectory, { mode: 448, recursive: true }),
|
|
27257
|
+
mkdir4(codexHome, { mode: 448, recursive: true })
|
|
26020
27258
|
]);
|
|
26021
27259
|
await options.report("downloading");
|
|
26022
27260
|
const download = await downloadVerifiedFile({
|
|
@@ -26049,14 +27287,14 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
26049
27287
|
CODEX_NON_INTERACTIVE: "1",
|
|
26050
27288
|
CODEX_RELEASE: this.releaseChannel,
|
|
26051
27289
|
HOME: codexHome,
|
|
26052
|
-
PATH:
|
|
27290
|
+
PATH: MINIMAL_PATH2
|
|
26053
27291
|
},
|
|
26054
27292
|
outputLimitBytes: 64 * 1024,
|
|
26055
27293
|
signal: options.signal,
|
|
26056
27294
|
timeoutMilliseconds: AGENT_INSTALL_TIMEOUT_MILLISECONDS
|
|
26057
27295
|
});
|
|
26058
27296
|
return await this.requireInstalledExecutable(
|
|
26059
|
-
|
|
27297
|
+
join14(installDirectory, "codex")
|
|
26060
27298
|
);
|
|
26061
27299
|
} catch (error61) {
|
|
26062
27300
|
if (options.signal.aborted) {
|
|
@@ -26070,7 +27308,7 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
26070
27308
|
"The fixed Codex installation recipe failed"
|
|
26071
27309
|
);
|
|
26072
27310
|
} finally {
|
|
26073
|
-
await
|
|
27311
|
+
await rm3(temporaryDirectory, { force: true, recursive: true });
|
|
26074
27312
|
}
|
|
26075
27313
|
};
|
|
26076
27314
|
};
|
|
@@ -26080,17 +27318,17 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
26080
27318
|
releaseChannel = "stable";
|
|
26081
27319
|
install = async (options) => {
|
|
26082
27320
|
this.assertSupportedPlatform();
|
|
26083
|
-
const root =
|
|
26084
|
-
const managedHome =
|
|
26085
|
-
const temporaryRoot =
|
|
27321
|
+
const root = join14(options.stateDirectory, "agents", this.id);
|
|
27322
|
+
const managedHome = join14(root, "home");
|
|
27323
|
+
const temporaryRoot = join14(options.stateDirectory, "temporary");
|
|
26086
27324
|
await Promise.all([
|
|
26087
|
-
|
|
26088
|
-
|
|
27325
|
+
mkdir4(managedHome, { mode: 448, recursive: true }),
|
|
27326
|
+
mkdir4(temporaryRoot, { mode: 448, recursive: true })
|
|
26089
27327
|
]);
|
|
26090
|
-
const temporaryDirectory = await
|
|
26091
|
-
|
|
27328
|
+
const temporaryDirectory = await mkdtemp3(
|
|
27329
|
+
join14(temporaryRoot, ".claude-install-")
|
|
26092
27330
|
);
|
|
26093
|
-
const installerPath =
|
|
27331
|
+
const installerPath = join14(temporaryDirectory, "install.sh");
|
|
26094
27332
|
try {
|
|
26095
27333
|
await options.report("downloading");
|
|
26096
27334
|
const download = await downloadVerifiedFile({
|
|
@@ -26119,7 +27357,7 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
26119
27357
|
cwd: temporaryDirectory,
|
|
26120
27358
|
environment: {
|
|
26121
27359
|
HOME: managedHome,
|
|
26122
|
-
PATH:
|
|
27360
|
+
PATH: MINIMAL_PATH2,
|
|
26123
27361
|
SHELL: "/bin/bash"
|
|
26124
27362
|
},
|
|
26125
27363
|
outputLimitBytes: 64 * 1024,
|
|
@@ -26127,7 +27365,7 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
26127
27365
|
timeoutMilliseconds: AGENT_INSTALL_TIMEOUT_MILLISECONDS
|
|
26128
27366
|
});
|
|
26129
27367
|
return await this.requireInstalledExecutable(
|
|
26130
|
-
|
|
27368
|
+
join14(managedHome, ".local", "bin", "claude")
|
|
26131
27369
|
);
|
|
26132
27370
|
} catch (error61) {
|
|
26133
27371
|
if (options.signal.aborted) {
|
|
@@ -26141,14 +27379,236 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
26141
27379
|
"The fixed Claude Code installation recipe failed"
|
|
26142
27380
|
);
|
|
26143
27381
|
} finally {
|
|
26144
|
-
await
|
|
27382
|
+
await rm3(temporaryDirectory, { force: true, recursive: true });
|
|
27383
|
+
}
|
|
27384
|
+
};
|
|
27385
|
+
};
|
|
27386
|
+
var PiInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
27387
|
+
#npmInstaller;
|
|
27388
|
+
id = "pi";
|
|
27389
|
+
label = "Pi";
|
|
27390
|
+
releaseChannel = PI_AGENT_PACKAGE.version;
|
|
27391
|
+
constructor(npmInstaller = new ManagedNpmPackageInstaller()) {
|
|
27392
|
+
super();
|
|
27393
|
+
this.#npmInstaller = npmInstaller;
|
|
27394
|
+
}
|
|
27395
|
+
install = async (options) => {
|
|
27396
|
+
this.assertSupportedPlatform();
|
|
27397
|
+
try {
|
|
27398
|
+
await options.report("downloading");
|
|
27399
|
+
await options.report("verifying");
|
|
27400
|
+
await options.report("installing_agent");
|
|
27401
|
+
const executable = await this.#npmInstaller.install(
|
|
27402
|
+
join14(
|
|
27403
|
+
options.stateDirectory,
|
|
27404
|
+
"agents",
|
|
27405
|
+
this.id,
|
|
27406
|
+
PI_AGENT_PACKAGE.version
|
|
27407
|
+
),
|
|
27408
|
+
PI_AGENT_PACKAGE,
|
|
27409
|
+
options.environment,
|
|
27410
|
+
options.signal
|
|
27411
|
+
);
|
|
27412
|
+
options.audit.record(
|
|
27413
|
+
"agent_npm_package_verified",
|
|
27414
|
+
options.requestId,
|
|
27415
|
+
this.id,
|
|
27416
|
+
{
|
|
27417
|
+
integrity: PI_AGENT_PACKAGE.integrity,
|
|
27418
|
+
package: PI_AGENT_PACKAGE.packageName,
|
|
27419
|
+
version: PI_AGENT_PACKAGE.version
|
|
27420
|
+
}
|
|
27421
|
+
);
|
|
27422
|
+
return executable;
|
|
27423
|
+
} catch (error61) {
|
|
27424
|
+
if (options.signal.aborted) {
|
|
27425
|
+
options.signal.throwIfAborted();
|
|
27426
|
+
}
|
|
27427
|
+
if (error61 instanceof ManagedNpmPackageError) {
|
|
27428
|
+
if (error61.code === "managed_package_verification_failed") {
|
|
27429
|
+
throw new InstallationManagerError(
|
|
27430
|
+
"agent_verification_failed",
|
|
27431
|
+
"The managed Pi package did not match its pinned artifact",
|
|
27432
|
+
{ cause: error61 }
|
|
27433
|
+
);
|
|
27434
|
+
}
|
|
27435
|
+
if (error61.code === "managed_package_publish_failed") {
|
|
27436
|
+
throw new InstallationManagerError(
|
|
27437
|
+
"agent_publish_failed",
|
|
27438
|
+
"The managed Pi package could not be published",
|
|
27439
|
+
{ cause: error61 }
|
|
27440
|
+
);
|
|
27441
|
+
}
|
|
27442
|
+
throw new InstallationManagerError(error61.code, error61.message, {
|
|
27443
|
+
cause: error61
|
|
27444
|
+
});
|
|
27445
|
+
}
|
|
27446
|
+
if (error61 instanceof InstallationManagerError) {
|
|
27447
|
+
throw error61;
|
|
27448
|
+
}
|
|
27449
|
+
throw new InstallationManagerError(
|
|
27450
|
+
"agent_install_failed",
|
|
27451
|
+
"The fixed Pi installation recipe failed",
|
|
27452
|
+
{ cause: error61 }
|
|
27453
|
+
);
|
|
27454
|
+
}
|
|
27455
|
+
};
|
|
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
|
+
);
|
|
26145
27602
|
}
|
|
26146
27603
|
};
|
|
26147
27604
|
};
|
|
26148
27605
|
function fixedAgentInstallRecipes() {
|
|
26149
27606
|
return Object.freeze([
|
|
26150
27607
|
new CodexInstallRecipe(),
|
|
26151
|
-
new ClaudeCodeInstallRecipe()
|
|
27608
|
+
new ClaudeCodeInstallRecipe(),
|
|
27609
|
+
new PiInstallRecipe(),
|
|
27610
|
+
new DeepSeekHarnessInstallRecipe(),
|
|
27611
|
+
new OpenCodeInstallRecipe()
|
|
26152
27612
|
]);
|
|
26153
27613
|
}
|
|
26154
27614
|
|
|
@@ -26278,6 +27738,7 @@ var FixedInstallationExecutor = class {
|
|
|
26278
27738
|
if (agent === null) {
|
|
26279
27739
|
agent = await recipe.install({
|
|
26280
27740
|
audit: this.#audit,
|
|
27741
|
+
environment: this.#environment,
|
|
26281
27742
|
report,
|
|
26282
27743
|
requestId,
|
|
26283
27744
|
signal,
|
|
@@ -26285,12 +27746,15 @@ var FixedInstallationExecutor = class {
|
|
|
26285
27746
|
});
|
|
26286
27747
|
installedAgent = true;
|
|
26287
27748
|
}
|
|
26288
|
-
let adapter = await resolveAdapterExecutable(
|
|
26289
|
-
specification,
|
|
26290
|
-
discoveryOptions2
|
|
26291
|
-
);
|
|
27749
|
+
let adapter = specification instanceof DirectAcpAgentDefinition ? agent : await resolveAdapterExecutable(specification, discoveryOptions2);
|
|
26292
27750
|
let installedAdapter = false;
|
|
26293
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
|
+
}
|
|
26294
27758
|
await report("installing_adapter");
|
|
26295
27759
|
adapter = await this.#adapterInstaller.install(specification, signal);
|
|
26296
27760
|
installedAdapter = true;
|
|
@@ -26347,7 +27811,7 @@ var FixedInstallationExecutor = class {
|
|
|
26347
27811
|
"The complete Agent ACP path did not pass verification"
|
|
26348
27812
|
);
|
|
26349
27813
|
}
|
|
26350
|
-
if (!installedAdapter && requiresAdapterReplacement(unavailable.code)) {
|
|
27814
|
+
if (!installedAdapter && specification instanceof BridgedAcpAgentDefinition && requiresAdapterReplacement(unavailable.code)) {
|
|
26351
27815
|
await report("installing_adapter");
|
|
26352
27816
|
adapter = await this.#adapterInstaller.install(specification, signal);
|
|
26353
27817
|
installedAdapter = true;
|
|
@@ -26359,12 +27823,16 @@ var FixedInstallationExecutor = class {
|
|
|
26359
27823
|
if (!installedAgent && (requiresAgentReplacement(unavailable.code) || unavailable.code === "acp_incompatible")) {
|
|
26360
27824
|
agent = await recipe.install({
|
|
26361
27825
|
audit: this.#audit,
|
|
27826
|
+
environment: this.#environment,
|
|
26362
27827
|
report,
|
|
26363
27828
|
requestId,
|
|
26364
27829
|
signal,
|
|
26365
27830
|
stateDirectory: this.#stateDirectory
|
|
26366
27831
|
});
|
|
26367
27832
|
installedAgent = true;
|
|
27833
|
+
if (specification instanceof DirectAcpAgentDefinition) {
|
|
27834
|
+
adapter = agent;
|
|
27835
|
+
}
|
|
26368
27836
|
continue;
|
|
26369
27837
|
}
|
|
26370
27838
|
throw new InstallationManagerError(
|
|
@@ -26426,15 +27894,15 @@ var LocalAuthorizationError = class extends Error {
|
|
|
26426
27894
|
};
|
|
26427
27895
|
|
|
26428
27896
|
// src/authorization/workspace-directory.ts
|
|
26429
|
-
import { readdir, realpath as
|
|
27897
|
+
import { readdir, realpath as realpath6, stat as stat6 } from "node:fs/promises";
|
|
26430
27898
|
import { homedir as homedir2 } from "node:os";
|
|
26431
27899
|
import {
|
|
26432
27900
|
basename as basename2,
|
|
26433
|
-
dirname as
|
|
26434
|
-
isAbsolute as
|
|
26435
|
-
join as
|
|
26436
|
-
normalize,
|
|
26437
|
-
relative as
|
|
27901
|
+
dirname as dirname5,
|
|
27902
|
+
isAbsolute as isAbsolute5,
|
|
27903
|
+
join as join15,
|
|
27904
|
+
normalize as normalize2,
|
|
27905
|
+
relative as relative3
|
|
26438
27906
|
} from "node:path";
|
|
26439
27907
|
var WorkspaceDirectoryListing = class {
|
|
26440
27908
|
directoryId;
|
|
@@ -26462,7 +27930,7 @@ function decodePath(value, prefix) {
|
|
|
26462
27930
|
if (encoded.length === 0 || encoded.length > 2e3 || !/^[a-zA-Z0-9_-]+$/.test(encoded))
|
|
26463
27931
|
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
26464
27932
|
const decoded = Buffer.from(encoded, "base64url").toString("utf8");
|
|
26465
|
-
if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded || decoded.includes("\0") ||
|
|
27933
|
+
if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded || decoded.includes("\0") || normalize2(decoded) !== decoded)
|
|
26466
27934
|
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
26467
27935
|
return decoded;
|
|
26468
27936
|
}
|
|
@@ -26484,20 +27952,20 @@ var WorkspaceDirectoryService = class {
|
|
|
26484
27952
|
if (id === "home") return homedir2();
|
|
26485
27953
|
if (id.startsWith("path-")) {
|
|
26486
27954
|
const path2 = decodePath(id, "path-");
|
|
26487
|
-
if (
|
|
27955
|
+
if (isAbsolute5(path2)) return path2;
|
|
26488
27956
|
}
|
|
26489
27957
|
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
26490
27958
|
};
|
|
26491
27959
|
resolvePath = async (requested, signal) => {
|
|
26492
27960
|
signal.throwIfAborted();
|
|
26493
|
-
if (!
|
|
27961
|
+
if (!isAbsolute5(requested) || requested.includes("\0") || normalize2(requested) !== requested)
|
|
26494
27962
|
throw new LocalAuthorizationError("The local directory path is invalid");
|
|
26495
27963
|
try {
|
|
26496
|
-
const target = await
|
|
27964
|
+
const target = await realpath6(requested);
|
|
26497
27965
|
signal.throwIfAborted();
|
|
26498
|
-
const metadata = await
|
|
27966
|
+
const metadata = await stat6(target);
|
|
26499
27967
|
signal.throwIfAborted();
|
|
26500
|
-
if (!
|
|
27968
|
+
if (!isAbsolute5(target) || normalize2(target) !== target || !metadata.isDirectory())
|
|
26501
27969
|
throw new LocalAuthorizationError(
|
|
26502
27970
|
"The selected location is not a directory"
|
|
26503
27971
|
);
|
|
@@ -26512,8 +27980,8 @@ var WorkspaceDirectoryService = class {
|
|
|
26512
27980
|
}
|
|
26513
27981
|
};
|
|
26514
27982
|
label = (path2) => {
|
|
26515
|
-
const suffix =
|
|
26516
|
-
return suffix === "" ? "~" : !
|
|
27983
|
+
const suffix = relative3(homedir2(), path2);
|
|
27984
|
+
return suffix === "" ? "~" : !isAbsolute5(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
|
|
26517
27985
|
};
|
|
26518
27986
|
identify = async (bindingId, requestedPath, signal) => {
|
|
26519
27987
|
this.authorizeMachine(bindingId);
|
|
@@ -26541,7 +28009,7 @@ var WorkspaceDirectoryService = class {
|
|
|
26541
28009
|
continue;
|
|
26542
28010
|
}
|
|
26543
28011
|
try {
|
|
26544
|
-
const path2 = await this.resolvePath(
|
|
28012
|
+
const path2 = await this.resolvePath(join15(target, child.name), signal);
|
|
26545
28013
|
entries.push({ directoryId: encodePath(path2), name: child.name });
|
|
26546
28014
|
} catch (cause) {
|
|
26547
28015
|
signal.throwIfAborted();
|
|
@@ -26549,7 +28017,7 @@ var WorkspaceDirectoryService = class {
|
|
|
26549
28017
|
}
|
|
26550
28018
|
}
|
|
26551
28019
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
26552
|
-
const parent =
|
|
28020
|
+
const parent = dirname5(target);
|
|
26553
28021
|
return new WorkspaceDirectoryListing(
|
|
26554
28022
|
id,
|
|
26555
28023
|
this.label(target),
|
|
@@ -26664,7 +28132,8 @@ function decodeHistory(thread, value) {
|
|
|
26664
28132
|
status: "available",
|
|
26665
28133
|
messages,
|
|
26666
28134
|
truncated,
|
|
26667
|
-
coveredCommandIds: []
|
|
28135
|
+
coveredCommandIds: [],
|
|
28136
|
+
coveredMessageIds: []
|
|
26668
28137
|
}
|
|
26669
28138
|
};
|
|
26670
28139
|
}
|
|
@@ -27341,7 +28810,7 @@ var RelayConnectionLiveness = class {
|
|
|
27341
28810
|
|
|
27342
28811
|
// src/relay-connection/session-event-outbox.ts
|
|
27343
28812
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
27344
|
-
import { dirname as
|
|
28813
|
+
import { dirname as dirname6 } from "node:path";
|
|
27345
28814
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
27346
28815
|
function readPendingEnvelope(row) {
|
|
27347
28816
|
if (typeof row !== "object" || row === null || Array.isArray(row)) {
|
|
@@ -27356,7 +28825,7 @@ function readPendingEnvelope(row) {
|
|
|
27356
28825
|
var SessionEventOutbox = class {
|
|
27357
28826
|
#database;
|
|
27358
28827
|
constructor(path2) {
|
|
27359
|
-
mkdirSync3(
|
|
28828
|
+
mkdirSync3(dirname6(path2), { recursive: true });
|
|
27360
28829
|
this.#database = new DatabaseSync3(path2);
|
|
27361
28830
|
this.#database.exec(`
|
|
27362
28831
|
CREATE TABLE IF NOT EXISTS relay_session_event_outbox (
|
|
@@ -28613,7 +30082,7 @@ async function main() {
|
|
|
28613
30082
|
if (arguments_.length > 1 || arguments_.length === 1 && arguments_[0] !== "--json") {
|
|
28614
30083
|
throw new Error("Usage: mobius agents [--json]");
|
|
28615
30084
|
}
|
|
28616
|
-
await
|
|
30085
|
+
await mkdir5(loadDefaultWorkspace(), { recursive: true, mode: 448 });
|
|
28617
30086
|
const inventory = await discoverLocalAgentInventory({
|
|
28618
30087
|
defaultWorkspace: loadDefaultWorkspace(),
|
|
28619
30088
|
environment: process.env,
|