@moxt-ai/mobius 0.0.5 → 0.0.6
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/native-session.d.ts +30 -0
- package/dist/acp/native-session.d.ts.map +1 -0
- package/dist/acp/native-session.js +25 -0
- package/dist/acp/native-session.js.map +1 -0
- package/dist/acp/session-history.d.ts +9 -0
- package/dist/acp/session-history.d.ts.map +1 -0
- package/dist/acp/session-history.js +69 -0
- package/dist/acp/session-history.js.map +1 -0
- package/dist/acp/session-manager.d.ts +16 -5
- package/dist/acp/session-manager.d.ts.map +1 -1
- package/dist/acp/session-manager.js +211 -12
- package/dist/acp/session-manager.js.map +1 -1
- package/dist/acp/session-store.d.ts +9 -0
- package/dist/acp/session-store.d.ts.map +1 -1
- package/dist/acp/session-store.js +43 -0
- package/dist/acp/session-store.js.map +1 -1
- package/dist/agent-catalog/discover-local-agents.d.ts +1 -1
- package/dist/agent-catalog/discover-local-agents.d.ts.map +1 -1
- package/dist/agent-catalog/discover-local-agents.js +19 -30
- package/dist/agent-catalog/discover-local-agents.js.map +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.d.ts +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.d.ts.map +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.js +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.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 +9 -18
- package/dist/agent-installation/fixed-installation-executor.js.map +1 -1
- package/dist/agent-installation/managed-adapter-installer.d.ts +0 -1
- package/dist/agent-installation/managed-adapter-installer.d.ts.map +1 -1
- package/dist/agent-installation/managed-adapter-installer.js +1 -3
- package/dist/agent-installation/managed-adapter-installer.js.map +1 -1
- package/dist/authorization/local-authorization-error.d.ts +5 -0
- package/dist/authorization/local-authorization-error.d.ts.map +1 -0
- package/dist/authorization/local-authorization-error.js +8 -0
- package/dist/authorization/local-authorization-error.js.map +1 -0
- package/dist/authorization/workspace-directory.d.ts +16 -5
- package/dist/authorization/workspace-directory.d.ts.map +1 -1
- package/dist/authorization/workspace-directory.js +109 -119
- package/dist/authorization/workspace-directory.js.map +1 -1
- package/dist/cli.js +1851 -543
- package/dist/cli.js.map +4 -4
- package/dist/config.d.ts +3 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -5
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1866 -593
- package/dist/index.js.map +4 -4
- package/dist/native-sessions/codex-history.d.ts +11 -0
- package/dist/native-sessions/codex-history.d.ts.map +1 -0
- package/dist/native-sessions/codex-history.js +223 -0
- package/dist/native-sessions/codex-history.js.map +1 -0
- package/dist/native-sessions/service.d.ts +14 -0
- package/dist/native-sessions/service.d.ts.map +1 -0
- package/dist/native-sessions/service.js +249 -0
- package/dist/native-sessions/service.js.map +1 -0
- package/dist/pairing/pair-local-machine.d.ts.map +1 -1
- package/dist/pairing/pair-local-machine.js +29 -26
- package/dist/pairing/pair-local-machine.js.map +1 -1
- package/dist/process/executable.d.ts +1 -1
- package/dist/process/executable.d.ts.map +1 -1
- package/dist/process/executable.js +3 -11
- package/dist/process/executable.js.map +1 -1
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +274 -193
- package/dist/run-daemon.js.map +1 -1
- package/package.json +3 -2
- package/dist/authorization/authorize-binding.d.ts +0 -6
- package/dist/authorization/authorize-binding.d.ts.map +0 -1
- package/dist/authorization/authorize-binding.js +0 -30
- package/dist/authorization/authorize-binding.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ var createAcpClient = (handler) => client({ name: "mobius" }).onRequest(
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
// src/config.ts
|
|
25
|
+
import { join as join3 } from "node:path";
|
|
25
26
|
import { fileURLToPath } from "node:url";
|
|
26
27
|
|
|
27
28
|
// src/agent-catalog/local-agent.ts
|
|
@@ -31,7 +32,7 @@ import { realpathSync, statSync } from "node:fs";
|
|
|
31
32
|
import { spawn } from "node:child_process";
|
|
32
33
|
import { constants } from "node:fs";
|
|
33
34
|
import { access, realpath, stat } from "node:fs/promises";
|
|
34
|
-
import {
|
|
35
|
+
import { join, resolve } from "node:path";
|
|
35
36
|
var DEFAULT_OUTPUT_LIMIT_BYTES = 16 * 1024;
|
|
36
37
|
var PROCESS_GRACEFUL_STOP_TIMEOUT_MILLISECONDS = 1e3;
|
|
37
38
|
var PROCESS_FORCED_STOP_TIMEOUT_MILLISECONDS = 5e3;
|
|
@@ -55,9 +56,9 @@ var ExecutableIdentity = class {
|
|
|
55
56
|
var ResolvedExecutable = class {
|
|
56
57
|
identity;
|
|
57
58
|
path;
|
|
58
|
-
constructor(
|
|
59
|
-
this.identity =
|
|
60
|
-
this.path =
|
|
59
|
+
constructor(identity2) {
|
|
60
|
+
this.identity = identity2;
|
|
61
|
+
this.path = identity2.realPath;
|
|
61
62
|
}
|
|
62
63
|
};
|
|
63
64
|
var ProcessExecutionError = class extends Error {
|
|
@@ -76,10 +77,6 @@ var ProcessResult = class {
|
|
|
76
77
|
this.stdout = stdout;
|
|
77
78
|
}
|
|
78
79
|
};
|
|
79
|
-
function isInside(root, candidate) {
|
|
80
|
-
const difference = relative(root, candidate);
|
|
81
|
-
return difference.length === 0 || !difference.startsWith("..") && !isAbsolute(difference);
|
|
82
|
-
}
|
|
83
80
|
function executableExtensions(executableName, environment, platform) {
|
|
84
81
|
if (platform !== "win32") {
|
|
85
82
|
return [""];
|
|
@@ -118,10 +115,9 @@ async function resolveExecutablePath(requestedPath, platform = process.platform)
|
|
|
118
115
|
return null;
|
|
119
116
|
}
|
|
120
117
|
}
|
|
121
|
-
async function findExecutableOnPath(executableName, environment,
|
|
118
|
+
async function findExecutableOnPath(executableName, environment, platform = process.platform) {
|
|
122
119
|
const encodedPath = environment["PATH"] ?? "";
|
|
123
120
|
const separator = platform === "win32" ? ";" : ":";
|
|
124
|
-
const approvedRealPath = await realpath(resolve(approvedRoot));
|
|
125
121
|
const extensions = executableExtensions(
|
|
126
122
|
executableName,
|
|
127
123
|
environment,
|
|
@@ -136,16 +132,16 @@ async function findExecutableOnPath(executableName, environment, approvedRoot, p
|
|
|
136
132
|
join(entry, `${executableName}${extension}`)
|
|
137
133
|
);
|
|
138
134
|
const executable = await resolveExecutablePath(requestedPath, platform);
|
|
139
|
-
if (executable !== null
|
|
135
|
+
if (executable !== null) {
|
|
140
136
|
return executable;
|
|
141
137
|
}
|
|
142
138
|
}
|
|
143
139
|
}
|
|
144
140
|
return null;
|
|
145
141
|
}
|
|
146
|
-
async function revalidateExecutable(
|
|
147
|
-
const current = await resolveExecutablePath(
|
|
148
|
-
return current !== null &&
|
|
142
|
+
async function revalidateExecutable(identity2, platform = process.platform) {
|
|
143
|
+
const current = await resolveExecutablePath(identity2.requestedPath, platform);
|
|
144
|
+
return current !== null && identity2.matches(current.identity);
|
|
149
145
|
}
|
|
150
146
|
function appendBounded(chunks, chunk, currentBytes, limitBytes) {
|
|
151
147
|
const nextBytes = currentBytes + chunk.byteLength;
|
|
@@ -369,8 +365,8 @@ async function runBoundedProcess(options) {
|
|
|
369
365
|
}
|
|
370
366
|
|
|
371
367
|
// src/agent-catalog/local-agent.ts
|
|
372
|
-
function readTrustedIdentity(
|
|
373
|
-
const resolvedPath = realpathSync(
|
|
368
|
+
function readTrustedIdentity(path2) {
|
|
369
|
+
const resolvedPath = realpathSync(path2);
|
|
374
370
|
const metadata = statSync(resolvedPath);
|
|
375
371
|
return new ExecutableIdentity(
|
|
376
372
|
resolvedPath,
|
|
@@ -467,14 +463,26 @@ var LocalAgentCatalog = class {
|
|
|
467
463
|
};
|
|
468
464
|
};
|
|
469
465
|
|
|
466
|
+
// src/storage/state-directory.ts
|
|
467
|
+
import { homedir } from "node:os";
|
|
468
|
+
import { join as join2, resolve as resolve2 } from "node:path";
|
|
469
|
+
function readStateDirectory(environment = process.env) {
|
|
470
|
+
const configured = environment["MOBIUS_STATE_DIRECTORY"]?.trim();
|
|
471
|
+
if (configured !== void 0 && configured.length > 0) {
|
|
472
|
+
return resolve2(configured);
|
|
473
|
+
}
|
|
474
|
+
return join2(homedir(), ".mobius");
|
|
475
|
+
}
|
|
476
|
+
|
|
470
477
|
// src/config.ts
|
|
471
|
-
function
|
|
472
|
-
|
|
478
|
+
function loadDefaultWorkspace() {
|
|
479
|
+
const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = process.env;
|
|
480
|
+
return configuredWorkspace ?? join3(readStateDirectory(), "workspace");
|
|
473
481
|
}
|
|
474
482
|
function createDaemonConfig(connection, agents = [], installableAgents = []) {
|
|
475
483
|
return {
|
|
476
484
|
agents,
|
|
477
|
-
|
|
485
|
+
defaultWorkspace: connection.defaultWorkspace,
|
|
478
486
|
auditDatabasePath: connection.auditDatabasePath,
|
|
479
487
|
bindingId: "workspace",
|
|
480
488
|
catalog: new LocalAgentCatalog(agents, installableAgents),
|
|
@@ -498,7 +506,7 @@ function loadDaemonConfig() {
|
|
|
498
506
|
}
|
|
499
507
|
return createDaemonConfig(
|
|
500
508
|
{
|
|
501
|
-
|
|
509
|
+
defaultWorkspace: loadDefaultWorkspace(),
|
|
502
510
|
auditDatabasePath: (
|
|
503
511
|
// biome-ignore lint/complexity/useLiteralKeys: Environment variables use an index signature.
|
|
504
512
|
process.env["MOBIUS_AUDIT_DATABASE"] ?? fileURLToPath(
|
|
@@ -1266,10 +1274,10 @@ function mergeDefs(...defs) {
|
|
|
1266
1274
|
function cloneDef(schema) {
|
|
1267
1275
|
return mergeDefs(schema._zod.def);
|
|
1268
1276
|
}
|
|
1269
|
-
function getElementAtPath(obj,
|
|
1270
|
-
if (!
|
|
1277
|
+
function getElementAtPath(obj, path2) {
|
|
1278
|
+
if (!path2)
|
|
1271
1279
|
return obj;
|
|
1272
|
-
return
|
|
1280
|
+
return path2.reduce((acc, key) => acc?.[key], obj);
|
|
1273
1281
|
}
|
|
1274
1282
|
function promiseAllObject(promisesObj) {
|
|
1275
1283
|
const keys = Object.keys(promisesObj);
|
|
@@ -1681,11 +1689,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1681
1689
|
}
|
|
1682
1690
|
return false;
|
|
1683
1691
|
}
|
|
1684
|
-
function prefixIssues(
|
|
1692
|
+
function prefixIssues(path2, issues) {
|
|
1685
1693
|
return issues.map((iss) => {
|
|
1686
1694
|
var _a3;
|
|
1687
1695
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1688
|
-
iss.path.unshift(
|
|
1696
|
+
iss.path.unshift(path2);
|
|
1689
1697
|
return iss;
|
|
1690
1698
|
});
|
|
1691
1699
|
}
|
|
@@ -2118,16 +2126,16 @@ function flattenError(error61, mapper = (issue2) => issue2.message) {
|
|
|
2118
2126
|
}
|
|
2119
2127
|
function formatError(error61, mapper = (issue2) => issue2.message) {
|
|
2120
2128
|
const fieldErrors = { _errors: [] };
|
|
2121
|
-
const processError = (error62,
|
|
2129
|
+
const processError = (error62, path2 = []) => {
|
|
2122
2130
|
for (const issue2 of error62.issues) {
|
|
2123
2131
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2124
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2132
|
+
issue2.errors.map((issues) => processError({ issues }, [...path2, ...issue2.path]));
|
|
2125
2133
|
} else if (issue2.code === "invalid_key") {
|
|
2126
|
-
processError({ issues: issue2.issues }, [...
|
|
2134
|
+
processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
|
|
2127
2135
|
} else if (issue2.code === "invalid_element") {
|
|
2128
|
-
processError({ issues: issue2.issues }, [...
|
|
2136
|
+
processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
|
|
2129
2137
|
} else {
|
|
2130
|
-
const fullpath = [...
|
|
2138
|
+
const fullpath = [...path2, ...issue2.path];
|
|
2131
2139
|
if (fullpath.length === 0) {
|
|
2132
2140
|
fieldErrors._errors.push(mapper(issue2));
|
|
2133
2141
|
} else {
|
|
@@ -2166,17 +2174,17 @@ function formatError(error61, mapper = (issue2) => issue2.message) {
|
|
|
2166
2174
|
}
|
|
2167
2175
|
function treeifyError(error61, mapper = (issue2) => issue2.message) {
|
|
2168
2176
|
const result = { errors: [] };
|
|
2169
|
-
const processError = (error62,
|
|
2177
|
+
const processError = (error62, path2 = []) => {
|
|
2170
2178
|
var _a3;
|
|
2171
2179
|
for (const issue2 of error62.issues) {
|
|
2172
2180
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2173
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2181
|
+
issue2.errors.map((issues) => processError({ issues }, [...path2, ...issue2.path]));
|
|
2174
2182
|
} else if (issue2.code === "invalid_key") {
|
|
2175
|
-
processError({ issues: issue2.issues }, [...
|
|
2183
|
+
processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
|
|
2176
2184
|
} else if (issue2.code === "invalid_element") {
|
|
2177
|
-
processError({ issues: issue2.issues }, [...
|
|
2185
|
+
processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
|
|
2178
2186
|
} else {
|
|
2179
|
-
const fullpath = [...
|
|
2187
|
+
const fullpath = [...path2, ...issue2.path];
|
|
2180
2188
|
if (fullpath.length === 0) {
|
|
2181
2189
|
result.errors.push(mapper(issue2));
|
|
2182
2190
|
continue;
|
|
@@ -2215,8 +2223,8 @@ function treeifyError(error61, mapper = (issue2) => issue2.message) {
|
|
|
2215
2223
|
}
|
|
2216
2224
|
function toDotPath(_path) {
|
|
2217
2225
|
const segs = [];
|
|
2218
|
-
const
|
|
2219
|
-
for (const seg of
|
|
2226
|
+
const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2227
|
+
for (const seg of path2) {
|
|
2220
2228
|
if (typeof seg === "number")
|
|
2221
2229
|
segs.push(`[${seg}]`);
|
|
2222
2230
|
else if (typeof seg === "symbol")
|
|
@@ -18726,13 +18734,13 @@ function resolveRef(ref, ctx) {
|
|
|
18726
18734
|
if (!ref.startsWith("#")) {
|
|
18727
18735
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
18728
18736
|
}
|
|
18729
|
-
const
|
|
18730
|
-
if (
|
|
18737
|
+
const path2 = ref.slice(1).split("/").filter(Boolean);
|
|
18738
|
+
if (path2.length === 0) {
|
|
18731
18739
|
return ctx.rootSchema;
|
|
18732
18740
|
}
|
|
18733
18741
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
18734
|
-
if (
|
|
18735
|
-
const key =
|
|
18742
|
+
if (path2[0] === defsKey) {
|
|
18743
|
+
const key = path2[1] === void 0 ? void 0 : decodeJSONPointerSegment(path2[1]);
|
|
18736
18744
|
if (!key || !ctx.defs[key]) {
|
|
18737
18745
|
throw new Error(`Reference not found: ${ref}`);
|
|
18738
18746
|
}
|
|
@@ -19388,6 +19396,56 @@ function date4(params) {
|
|
|
19388
19396
|
return _coercedDate(ZodDate, params);
|
|
19389
19397
|
}
|
|
19390
19398
|
|
|
19399
|
+
// ../protocol/src/prompt-usage.ts
|
|
19400
|
+
var TOKEN_COUNT = external_exports.number().int().nonnegative();
|
|
19401
|
+
var promptUsageSchema = external_exports.object({
|
|
19402
|
+
cachedReadTokens: TOKEN_COUNT.nullish(),
|
|
19403
|
+
cachedWriteTokens: TOKEN_COUNT.nullish(),
|
|
19404
|
+
inputTokens: TOKEN_COUNT,
|
|
19405
|
+
outputTokens: TOKEN_COUNT,
|
|
19406
|
+
thoughtTokens: TOKEN_COUNT.nullish(),
|
|
19407
|
+
totalTokens: TOKEN_COUNT
|
|
19408
|
+
}).readonly();
|
|
19409
|
+
var quotaTokenCountSchema = external_exports.object({
|
|
19410
|
+
cachedInputTokens: TOKEN_COUNT,
|
|
19411
|
+
cachedWriteTokens: TOKEN_COUNT.optional(),
|
|
19412
|
+
inputTokens: TOKEN_COUNT,
|
|
19413
|
+
outputTokens: TOKEN_COUNT,
|
|
19414
|
+
reasoningOutputTokens: TOKEN_COUNT,
|
|
19415
|
+
totalTokens: TOKEN_COUNT
|
|
19416
|
+
}).readonly();
|
|
19417
|
+
var promptMetadataSchema = external_exports.object({
|
|
19418
|
+
quota: external_exports.object({
|
|
19419
|
+
model_usage: external_exports.array(
|
|
19420
|
+
external_exports.object({
|
|
19421
|
+
model: external_exports.string().min(1).max(255),
|
|
19422
|
+
token_count: quotaTokenCountSchema
|
|
19423
|
+
}).readonly()
|
|
19424
|
+
).max(100).readonly(),
|
|
19425
|
+
token_count: quotaTokenCountSchema.nullable()
|
|
19426
|
+
}).readonly().optional()
|
|
19427
|
+
}).readonly();
|
|
19428
|
+
var agentPromptResponseSchema = external_exports.object({
|
|
19429
|
+
_meta: promptMetadataSchema.nullish(),
|
|
19430
|
+
stopReason: external_exports.enum([
|
|
19431
|
+
"end_turn",
|
|
19432
|
+
"max_tokens",
|
|
19433
|
+
"max_turn_requests",
|
|
19434
|
+
"refusal",
|
|
19435
|
+
"cancelled"
|
|
19436
|
+
]),
|
|
19437
|
+
usage: promptUsageSchema.nullish()
|
|
19438
|
+
}).readonly();
|
|
19439
|
+
function decodeAgentPromptResponse(value) {
|
|
19440
|
+
const result = agentPromptResponseSchema.safeParse(value);
|
|
19441
|
+
if (!result.success) {
|
|
19442
|
+
throw new ProtocolMessageError("Invalid agent prompt response", {
|
|
19443
|
+
cause: result.error
|
|
19444
|
+
});
|
|
19445
|
+
}
|
|
19446
|
+
return result.data;
|
|
19447
|
+
}
|
|
19448
|
+
|
|
19391
19449
|
// ../protocol/src/version.ts
|
|
19392
19450
|
var MOBIUS_PROTOCOL_VERSION = 1;
|
|
19393
19451
|
|
|
@@ -19454,7 +19512,8 @@ var sessionCapabilitiesSchema = external_exports.strictObject({
|
|
|
19454
19512
|
imagePrompt: external_exports.boolean(),
|
|
19455
19513
|
listSessions: external_exports.boolean(),
|
|
19456
19514
|
loadSession: external_exports.boolean(),
|
|
19457
|
-
resumeSession: external_exports.boolean()
|
|
19515
|
+
resumeSession: external_exports.boolean(),
|
|
19516
|
+
unstableForkSession: external_exports.boolean().default(false)
|
|
19458
19517
|
}).readonly();
|
|
19459
19518
|
var sessionCommandSchema = external_exports.strictObject({
|
|
19460
19519
|
description: SHORT_TEXT,
|
|
@@ -19595,7 +19654,7 @@ var sessionConfigUpdatedEventSchema = external_exports.strictObject({
|
|
|
19595
19654
|
requestId: IDENTIFIER,
|
|
19596
19655
|
sessionId: IDENTIFIER
|
|
19597
19656
|
}).readonly();
|
|
19598
|
-
var
|
|
19657
|
+
var sessionContextUsageUpdatedEventSchema = external_exports.strictObject({
|
|
19599
19658
|
costAmount: external_exports.number().finite().nonnegative(),
|
|
19600
19659
|
costCurrency: external_exports.string().max(16),
|
|
19601
19660
|
hasCost: external_exports.boolean(),
|
|
@@ -19603,8 +19662,22 @@ var sessionUsageUpdatedEventSchema = external_exports.strictObject({
|
|
|
19603
19662
|
protocolVersion: PROTOCOL_VERSION,
|
|
19604
19663
|
sessionId: IDENTIFIER,
|
|
19605
19664
|
size: external_exports.number().int().nonnegative(),
|
|
19665
|
+
source: external_exports.literal("context").default("context"),
|
|
19606
19666
|
used: external_exports.number().int().nonnegative()
|
|
19607
19667
|
}).readonly();
|
|
19668
|
+
var sessionPromptUsageUpdatedEventSchema = external_exports.strictObject({
|
|
19669
|
+
_meta: promptMetadataSchema.nullable(),
|
|
19670
|
+
commandId: IDENTIFIER,
|
|
19671
|
+
kind: external_exports.literal(SESSION_USAGE_UPDATED_KIND),
|
|
19672
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
19673
|
+
sessionId: IDENTIFIER,
|
|
19674
|
+
source: external_exports.literal("prompt"),
|
|
19675
|
+
usage: promptUsageSchema.nullable()
|
|
19676
|
+
}).refine((event) => event.usage !== null || event._meta?.quota !== void 0).readonly();
|
|
19677
|
+
var sessionUsageUpdatedEventSchema = external_exports.union([
|
|
19678
|
+
sessionContextUsageUpdatedEventSchema,
|
|
19679
|
+
sessionPromptUsageUpdatedEventSchema
|
|
19680
|
+
]);
|
|
19608
19681
|
var sessionCommandsUpdatedEventSchema = external_exports.strictObject({
|
|
19609
19682
|
commands: external_exports.array(sessionCommandSchema).max(500).readonly(),
|
|
19610
19683
|
kind: external_exports.literal(SESSION_COMMANDS_UPDATED_KIND),
|
|
@@ -19615,7 +19688,7 @@ var sessionInfoUpdatedEventSchema = external_exports.strictObject({
|
|
|
19615
19688
|
kind: external_exports.literal(SESSION_INFO_UPDATED_KIND),
|
|
19616
19689
|
protocolVersion: PROTOCOL_VERSION,
|
|
19617
19690
|
sessionId: IDENTIFIER,
|
|
19618
|
-
title: external_exports.string().max(500),
|
|
19691
|
+
title: external_exports.string().max(500).nullable(),
|
|
19619
19692
|
updatedAt: external_exports.string().max(128)
|
|
19620
19693
|
}).readonly();
|
|
19621
19694
|
var sessionPermissionRequestedEventSchema = external_exports.strictObject({
|
|
@@ -20192,6 +20265,160 @@ function encodeSessionFailedEvent(message) {
|
|
|
20192
20265
|
return JSON.stringify(decodeSessionFailedEvent(JSON.stringify(message)));
|
|
20193
20266
|
}
|
|
20194
20267
|
|
|
20268
|
+
// ../protocol/src/session-history.ts
|
|
20269
|
+
var SESSION_HISTORY_CHARACTER_LIMIT = 256e3;
|
|
20270
|
+
var SESSION_HISTORY_MESSAGE_LIMIT = 1e3;
|
|
20271
|
+
var SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT = 10;
|
|
20272
|
+
var SESSION_HISTORY_IMAGE_CHARACTER_LIMIT = 4e6;
|
|
20273
|
+
var historyImage = external_exports.strictObject({
|
|
20274
|
+
data: external_exports.string().min(4).max(SESSION_HISTORY_IMAGE_CHARACTER_LIMIT).refine(
|
|
20275
|
+
(data) => data.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(data)
|
|
20276
|
+
),
|
|
20277
|
+
mimeType: external_exports.enum(["image/png", "image/jpeg", "image/gif", "image/webp"]),
|
|
20278
|
+
name: external_exports.string().min(1).max(500)
|
|
20279
|
+
}).readonly();
|
|
20280
|
+
function parseNativeHistoryText(text) {
|
|
20281
|
+
const images = [];
|
|
20282
|
+
let imageCharacters = 0;
|
|
20283
|
+
const plainText = text.replace(
|
|
20284
|
+
/\[@image\]\(([^)\r\n]*)(?:\)|$)/g,
|
|
20285
|
+
(_marker, uri) => {
|
|
20286
|
+
const header = /^data:(image\/[a-z+]+);base64,/.exec(uri);
|
|
20287
|
+
if (header === null) return "[Image unavailable]";
|
|
20288
|
+
const parsed = historyImage.safeParse({
|
|
20289
|
+
data: uri.slice(header[0].length),
|
|
20290
|
+
mimeType: header[1],
|
|
20291
|
+
name: `Image ${(images.length + 1).toString()}`
|
|
20292
|
+
});
|
|
20293
|
+
if (!parsed.success || images.length >= 8 || imageCharacters + parsed.data.data.length > SESSION_HISTORY_IMAGE_CHARACTER_LIMIT)
|
|
20294
|
+
return "[Image unavailable]";
|
|
20295
|
+
images.push(parsed.data);
|
|
20296
|
+
imageCharacters += parsed.data.data.length;
|
|
20297
|
+
return "";
|
|
20298
|
+
}
|
|
20299
|
+
);
|
|
20300
|
+
return { text: plainText, images };
|
|
20301
|
+
}
|
|
20302
|
+
var historyMessage = external_exports.strictObject({
|
|
20303
|
+
role: external_exports.enum(["user", "assistant"]),
|
|
20304
|
+
text: external_exports.string().max(SESSION_HISTORY_CHARACTER_LIMIT),
|
|
20305
|
+
images: external_exports.array(historyImage).max(8).readonly().optional()
|
|
20306
|
+
}).readonly();
|
|
20307
|
+
var sessionHistorySchema = external_exports.discriminatedUnion("status", [
|
|
20308
|
+
external_exports.strictObject({
|
|
20309
|
+
status: external_exports.literal("available"),
|
|
20310
|
+
messages: external_exports.array(historyMessage).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
|
|
20311
|
+
truncated: external_exports.boolean(),
|
|
20312
|
+
coveredCommandIds: external_exports.array(external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/)).max(1e4).readonly()
|
|
20313
|
+
}).readonly(),
|
|
20314
|
+
external_exports.strictObject({ status: external_exports.literal("unavailable") }).readonly()
|
|
20315
|
+
]).refine(
|
|
20316
|
+
(history) => history.status === "unavailable" || history.messages.reduce(
|
|
20317
|
+
(total, message) => total + message.text.length,
|
|
20318
|
+
0
|
|
20319
|
+
) <= SESSION_HISTORY_CHARACTER_LIMIT && history.messages.reduce(
|
|
20320
|
+
(total, message) => total + (message.images ?? []).reduce(
|
|
20321
|
+
(size, image) => size + image.data.length,
|
|
20322
|
+
0
|
|
20323
|
+
),
|
|
20324
|
+
0
|
|
20325
|
+
) <= SESSION_HISTORY_IMAGE_CHARACTER_LIMIT
|
|
20326
|
+
);
|
|
20327
|
+
|
|
20328
|
+
// ../protocol/src/native-sessions.ts
|
|
20329
|
+
var NATIVE_SESSION_REQUEST_KIND = "native.session.request";
|
|
20330
|
+
var NATIVE_SESSION_RESULT_KIND = "native.session.result";
|
|
20331
|
+
var NATIVE_SESSION_CANCEL_KIND = "native.session.cancel";
|
|
20332
|
+
var identifier = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/);
|
|
20333
|
+
var directoryId = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,2047}$/);
|
|
20334
|
+
var envelope = {
|
|
20335
|
+
protocolVersion: external_exports.literal(MOBIUS_PROTOCOL_VERSION),
|
|
20336
|
+
requestId: identifier,
|
|
20337
|
+
sessionId: identifier
|
|
20338
|
+
};
|
|
20339
|
+
var identity = {
|
|
20340
|
+
...envelope,
|
|
20341
|
+
kind: external_exports.literal(NATIVE_SESSION_REQUEST_KIND),
|
|
20342
|
+
additionalDirectoryIds: external_exports.array(directoryId).max(16).readonly(),
|
|
20343
|
+
agentId: identifier,
|
|
20344
|
+
bindingId: identifier,
|
|
20345
|
+
directoryId
|
|
20346
|
+
};
|
|
20347
|
+
var nativeSessionCommandSchema = external_exports.discriminatedUnion("action", [
|
|
20348
|
+
external_exports.strictObject({
|
|
20349
|
+
...identity,
|
|
20350
|
+
action: external_exports.literal("list"),
|
|
20351
|
+
cursor: identifier.nullable()
|
|
20352
|
+
}).readonly(),
|
|
20353
|
+
external_exports.strictObject({
|
|
20354
|
+
...identity,
|
|
20355
|
+
action: external_exports.enum(["import", "fork"]),
|
|
20356
|
+
entryId: identifier,
|
|
20357
|
+
targetSessionId: identifier
|
|
20358
|
+
}).readonly(),
|
|
20359
|
+
external_exports.strictObject({
|
|
20360
|
+
...identity,
|
|
20361
|
+
action: external_exports.literal("delete"),
|
|
20362
|
+
entryId: identifier
|
|
20363
|
+
}).readonly()
|
|
20364
|
+
]);
|
|
20365
|
+
var nativeSessionEntrySchema = external_exports.strictObject({
|
|
20366
|
+
entryId: identifier,
|
|
20367
|
+
title: external_exports.string().max(500),
|
|
20368
|
+
directoryId,
|
|
20369
|
+
directoryLabel: external_exports.string().min(1).max(2048),
|
|
20370
|
+
updatedAt: external_exports.iso.datetime({ offset: true }).nullable(),
|
|
20371
|
+
additionalDirectories: external_exports.array(
|
|
20372
|
+
external_exports.strictObject({ directoryId, name: external_exports.string().min(1).max(255) }).readonly()
|
|
20373
|
+
).max(16).readonly()
|
|
20374
|
+
}).readonly();
|
|
20375
|
+
var resultEnvelope = {
|
|
20376
|
+
...envelope,
|
|
20377
|
+
kind: external_exports.literal(NATIVE_SESSION_RESULT_KIND)
|
|
20378
|
+
};
|
|
20379
|
+
var nativeSessionResultSchema = external_exports.discriminatedUnion("action", [
|
|
20380
|
+
external_exports.strictObject({
|
|
20381
|
+
...resultEnvelope,
|
|
20382
|
+
action: external_exports.literal("list"),
|
|
20383
|
+
capabilities: sessionCapabilitiesSchema,
|
|
20384
|
+
entries: external_exports.array(nativeSessionEntrySchema).max(500).readonly(),
|
|
20385
|
+
nextCursor: identifier.nullable()
|
|
20386
|
+
}).readonly(),
|
|
20387
|
+
external_exports.strictObject({
|
|
20388
|
+
...resultEnvelope,
|
|
20389
|
+
action: external_exports.enum(["import", "fork"]),
|
|
20390
|
+
ready: sessionReadyEventSchema,
|
|
20391
|
+
history: sessionHistorySchema,
|
|
20392
|
+
title: external_exports.string().max(500)
|
|
20393
|
+
}).readonly(),
|
|
20394
|
+
external_exports.strictObject({
|
|
20395
|
+
...resultEnvelope,
|
|
20396
|
+
action: external_exports.literal("preview"),
|
|
20397
|
+
entryId: identifier,
|
|
20398
|
+
reason: external_exports.literal("continuation_unavailable"),
|
|
20399
|
+
history: sessionHistorySchema,
|
|
20400
|
+
title: external_exports.string().max(500)
|
|
20401
|
+
}).readonly(),
|
|
20402
|
+
external_exports.strictObject({
|
|
20403
|
+
...resultEnvelope,
|
|
20404
|
+
action: external_exports.literal("delete"),
|
|
20405
|
+
entryId: identifier,
|
|
20406
|
+
deletedSessionIds: external_exports.array(identifier).max(500).readonly()
|
|
20407
|
+
}).readonly()
|
|
20408
|
+
]);
|
|
20409
|
+
var nativeSessionCancelSchema = external_exports.strictObject({ ...envelope, kind: external_exports.literal(NATIVE_SESSION_CANCEL_KIND) }).readonly();
|
|
20410
|
+
function decode4(schema, encoded) {
|
|
20411
|
+
try {
|
|
20412
|
+
const value = JSON.parse(encoded);
|
|
20413
|
+
return schema.parse(value);
|
|
20414
|
+
} catch (cause) {
|
|
20415
|
+
throw new ProtocolMessageError("Invalid native session message", { cause });
|
|
20416
|
+
}
|
|
20417
|
+
}
|
|
20418
|
+
var decodeNativeSessionCommand = (encoded) => decode4(nativeSessionCommandSchema, encoded);
|
|
20419
|
+
var encodeNativeSessionResult = (value) => JSON.stringify(nativeSessionResultSchema.parse(value));
|
|
20420
|
+
var decodeNativeSessionCancel = (encoded) => decode4(nativeSessionCancelSchema, encoded);
|
|
20421
|
+
|
|
20195
20422
|
// src/acp/activity.ts
|
|
20196
20423
|
var TRUNCATION_MARKER = "\n\u2026 output truncated by Mobius";
|
|
20197
20424
|
function isRecord2(value) {
|
|
@@ -20430,15 +20657,15 @@ var AcpActivityTracker = class {
|
|
|
20430
20657
|
await this.finishNarrative();
|
|
20431
20658
|
}
|
|
20432
20659
|
const previous = this.#activities.get(key);
|
|
20433
|
-
const
|
|
20660
|
+
const identity2 = this.#identity(key);
|
|
20434
20661
|
const activity = new AcpActivity(
|
|
20435
|
-
|
|
20662
|
+
identity2.id,
|
|
20436
20663
|
activityType,
|
|
20437
20664
|
"",
|
|
20438
20665
|
[],
|
|
20439
20666
|
"",
|
|
20440
20667
|
truncate(`${previous?.output ?? ""}${text}`, AGENT_ACTIVITY_DETAIL_LIMIT),
|
|
20441
|
-
|
|
20668
|
+
identity2.sequence,
|
|
20442
20669
|
"running",
|
|
20443
20670
|
activityType === "reasoning" ? "Reasoning" : "Progress update",
|
|
20444
20671
|
activityType === "reasoning" ? "think" : ""
|
|
@@ -20458,18 +20685,18 @@ var AcpActivityTracker = class {
|
|
|
20458
20685
|
await this.#recordPlanEntries(key, plan.entries);
|
|
20459
20686
|
return;
|
|
20460
20687
|
}
|
|
20461
|
-
const
|
|
20688
|
+
const identity2 = this.#identity(key);
|
|
20462
20689
|
const output2 = plan.type === "markdown" ? plan.content : `Plan file: ${plan.uri}`;
|
|
20463
20690
|
await this.#record(
|
|
20464
20691
|
key,
|
|
20465
20692
|
new AcpActivity(
|
|
20466
|
-
|
|
20693
|
+
identity2.id,
|
|
20467
20694
|
"plan",
|
|
20468
20695
|
"",
|
|
20469
20696
|
[],
|
|
20470
20697
|
"",
|
|
20471
20698
|
truncate(output2, AGENT_ACTIVITY_DETAIL_LIMIT),
|
|
20472
|
-
|
|
20699
|
+
identity2.sequence,
|
|
20473
20700
|
"running",
|
|
20474
20701
|
"Plan",
|
|
20475
20702
|
""
|
|
@@ -20502,18 +20729,18 @@ var AcpActivityTracker = class {
|
|
|
20502
20729
|
recordToolCall = async (update) => {
|
|
20503
20730
|
await this.finishNarrative();
|
|
20504
20731
|
const key = `tool:${update.toolCallId}`;
|
|
20505
|
-
const
|
|
20732
|
+
const identity2 = this.#identity(key);
|
|
20506
20733
|
const output2 = update.rawOutput === void 0 ? readContentOutput(update.content) : formatUnknown(update.rawOutput);
|
|
20507
20734
|
await this.#record(
|
|
20508
20735
|
key,
|
|
20509
20736
|
new AcpActivity(
|
|
20510
|
-
|
|
20737
|
+
identity2.id,
|
|
20511
20738
|
"tool",
|
|
20512
20739
|
update.rawInput === void 0 ? "" : formatUnknown(update.rawInput),
|
|
20513
20740
|
normalizeLocations(update.locations ?? []),
|
|
20514
20741
|
truncate(update.name ?? "", 128),
|
|
20515
20742
|
output2,
|
|
20516
|
-
|
|
20743
|
+
identity2.sequence,
|
|
20517
20744
|
normalizeToolStatus(update.status ?? "pending", "pending"),
|
|
20518
20745
|
normalizeTitle(update.title, "Tool call"),
|
|
20519
20746
|
update.kind ?? "other",
|
|
@@ -20525,19 +20752,19 @@ var AcpActivityTracker = class {
|
|
|
20525
20752
|
await this.finishNarrative();
|
|
20526
20753
|
const key = `tool:${update.toolCallId}`;
|
|
20527
20754
|
const previous = this.#activities.get(key);
|
|
20528
|
-
const
|
|
20755
|
+
const identity2 = this.#identity(key);
|
|
20529
20756
|
const output2 = update.rawOutput !== void 0 ? formatUnknown(update.rawOutput) : update.content !== void 0 && update.content !== null ? readContentOutput(update.content) : previous?.output ?? "";
|
|
20530
20757
|
const content = update.content === void 0 || update.content === null ? previous?.content ?? [] : normalizeToolContent(update.content);
|
|
20531
20758
|
await this.#record(
|
|
20532
20759
|
key,
|
|
20533
20760
|
new AcpActivity(
|
|
20534
|
-
|
|
20761
|
+
identity2.id,
|
|
20535
20762
|
"tool",
|
|
20536
20763
|
update.rawInput === void 0 ? previous?.input ?? "" : formatUnknown(update.rawInput),
|
|
20537
20764
|
update.locations === void 0 || update.locations === null ? previous?.locations ?? [] : normalizeLocations(update.locations),
|
|
20538
20765
|
update.name === void 0 || update.name === null ? previous?.name ?? "" : truncate(update.name, 128),
|
|
20539
20766
|
output2,
|
|
20540
|
-
|
|
20767
|
+
identity2.sequence,
|
|
20541
20768
|
update.status === void 0 || update.status === null ? previous?.status ?? "pending" : normalizeToolStatus(update.status, previous?.status ?? "pending"),
|
|
20542
20769
|
update.title === void 0 || update.title === null ? previous?.title ?? "Tool call" : normalizeTitle(update.title, "Tool call"),
|
|
20543
20770
|
update.kind === void 0 || update.kind === null ? previous?.toolKind ?? "other" : update.kind,
|
|
@@ -20577,30 +20804,30 @@ var AcpActivityTracker = class {
|
|
|
20577
20804
|
if (existing !== void 0) {
|
|
20578
20805
|
return existing;
|
|
20579
20806
|
}
|
|
20580
|
-
const
|
|
20807
|
+
const identity2 = new ActivityIdentity(
|
|
20581
20808
|
`activity-${crypto.randomUUID()}`,
|
|
20582
20809
|
this.#nextSequence
|
|
20583
20810
|
);
|
|
20584
20811
|
this.#nextSequence += 1;
|
|
20585
|
-
this.#identities.set(key,
|
|
20586
|
-
return
|
|
20812
|
+
this.#identities.set(key, identity2);
|
|
20813
|
+
return identity2;
|
|
20587
20814
|
};
|
|
20588
20815
|
#record = async (key, activity) => {
|
|
20589
20816
|
this.#activities.set(key, activity);
|
|
20590
20817
|
await this.#onActivity(activity);
|
|
20591
20818
|
};
|
|
20592
20819
|
#recordPlanEntries = async (key, entries) => {
|
|
20593
|
-
const
|
|
20820
|
+
const identity2 = this.#identity(key);
|
|
20594
20821
|
await this.#record(
|
|
20595
20822
|
key,
|
|
20596
20823
|
new AcpActivity(
|
|
20597
|
-
|
|
20824
|
+
identity2.id,
|
|
20598
20825
|
"plan",
|
|
20599
20826
|
"",
|
|
20600
20827
|
[],
|
|
20601
20828
|
"",
|
|
20602
20829
|
formatPlanEntries(entries),
|
|
20603
|
-
|
|
20830
|
+
identity2.sequence,
|
|
20604
20831
|
planStatus(entries),
|
|
20605
20832
|
"Plan",
|
|
20606
20833
|
""
|
|
@@ -20609,6 +20836,88 @@ var AcpActivityTracker = class {
|
|
|
20609
20836
|
};
|
|
20610
20837
|
};
|
|
20611
20838
|
|
|
20839
|
+
// src/acp/native-session.ts
|
|
20840
|
+
var path = external_exports.string().min(1).max(2048).refine((value) => !value.includes("\0"));
|
|
20841
|
+
var nativeSessionIdSchema = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,255}$/);
|
|
20842
|
+
var nativeSessionInfoSchema = external_exports.object({
|
|
20843
|
+
sessionId: nativeSessionIdSchema,
|
|
20844
|
+
cwd: path,
|
|
20845
|
+
additionalDirectories: external_exports.array(path).max(16).default([]).readonly(),
|
|
20846
|
+
title: external_exports.string().max(500).nullish(),
|
|
20847
|
+
updatedAt: external_exports.iso.datetime({ offset: true }).nullish()
|
|
20848
|
+
}).readonly();
|
|
20849
|
+
var nativeSessionPageSchema = external_exports.object({
|
|
20850
|
+
sessions: external_exports.array(nativeSessionInfoSchema).max(500).readonly(),
|
|
20851
|
+
nextCursor: external_exports.string().min(1).max(4096).nullish()
|
|
20852
|
+
}).readonly();
|
|
20853
|
+
|
|
20854
|
+
// src/acp/session-history.ts
|
|
20855
|
+
var AcpHistoryReplay = class {
|
|
20856
|
+
#messages = [];
|
|
20857
|
+
#lastMessageId = null;
|
|
20858
|
+
#characters = 0;
|
|
20859
|
+
#imageCharacters = 0;
|
|
20860
|
+
#truncated = false;
|
|
20861
|
+
record = (update) => {
|
|
20862
|
+
if (update.sessionUpdate !== "user_message_chunk" && update.sessionUpdate !== "agent_message_chunk")
|
|
20863
|
+
return;
|
|
20864
|
+
if (update.sessionUpdate === "agent_message_chunk" && isProgressMessage(update))
|
|
20865
|
+
return;
|
|
20866
|
+
const role = update.sessionUpdate === "user_message_chunk" ? "user" : "assistant";
|
|
20867
|
+
const content = update.content;
|
|
20868
|
+
const parsed = parseNativeHistoryText(
|
|
20869
|
+
content.type === "text" ? content.text : content.type === "image" ? `[@image](data:${content.mimeType};base64,${content.data})` : `[${content.type} attachment]`
|
|
20870
|
+
);
|
|
20871
|
+
const text = parsed.text;
|
|
20872
|
+
if (text.length === 0 && parsed.images.length === 0) return;
|
|
20873
|
+
const previous = this.#messages.at(-1);
|
|
20874
|
+
const id = update.messageId ?? null;
|
|
20875
|
+
const continuation = previous?.role === role && id === this.#lastMessageId;
|
|
20876
|
+
const combined = continuation ? `${previous.text}${text}` : text;
|
|
20877
|
+
const allImages = continuation ? [...previous.images ?? [], ...parsed.images] : parsed.images;
|
|
20878
|
+
const images = allImages.slice(0, 8);
|
|
20879
|
+
this.#truncated ||= images.length < allImages.length;
|
|
20880
|
+
if (continuation) {
|
|
20881
|
+
this.#messages.pop();
|
|
20882
|
+
this.#characters -= previous.text.length;
|
|
20883
|
+
this.#imageCharacters -= (previous.images ?? []).reduce(
|
|
20884
|
+
(size, image) => size + image.data.length,
|
|
20885
|
+
0
|
|
20886
|
+
);
|
|
20887
|
+
}
|
|
20888
|
+
const bounded = combined.slice(-SESSION_HISTORY_CHARACTER_LIMIT);
|
|
20889
|
+
this.#truncated ||= bounded.length < combined.length;
|
|
20890
|
+
this.#messages.push({
|
|
20891
|
+
role,
|
|
20892
|
+
text: bounded,
|
|
20893
|
+
...images.length > 0 ? { images } : {}
|
|
20894
|
+
});
|
|
20895
|
+
this.#characters += bounded.length;
|
|
20896
|
+
this.#imageCharacters += images.reduce(
|
|
20897
|
+
(size, image) => size + image.data.length,
|
|
20898
|
+
0
|
|
20899
|
+
);
|
|
20900
|
+
this.#lastMessageId = id;
|
|
20901
|
+
while (this.#messages.length > SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT || this.#characters > SESSION_HISTORY_CHARACTER_LIMIT || this.#imageCharacters > SESSION_HISTORY_IMAGE_CHARACTER_LIMIT) {
|
|
20902
|
+
const removed = this.#messages.shift();
|
|
20903
|
+
if (removed !== void 0) {
|
|
20904
|
+
this.#characters -= removed.text.length;
|
|
20905
|
+
this.#imageCharacters -= (removed.images ?? []).reduce(
|
|
20906
|
+
(size, image) => size + image.data.length,
|
|
20907
|
+
0
|
|
20908
|
+
);
|
|
20909
|
+
}
|
|
20910
|
+
this.#truncated = true;
|
|
20911
|
+
}
|
|
20912
|
+
};
|
|
20913
|
+
snapshot = () => ({
|
|
20914
|
+
status: "available",
|
|
20915
|
+
messages: [...this.#messages],
|
|
20916
|
+
truncated: this.#truncated,
|
|
20917
|
+
coveredCommandIds: []
|
|
20918
|
+
});
|
|
20919
|
+
};
|
|
20920
|
+
|
|
20612
20921
|
// src/acp/session-store.ts
|
|
20613
20922
|
import { mkdirSync } from "node:fs";
|
|
20614
20923
|
import { dirname } from "node:path";
|
|
@@ -20642,6 +20951,9 @@ var FoundAcpStoredSession = class extends AcpStoredSessionLookup {
|
|
|
20642
20951
|
this.session = session;
|
|
20643
20952
|
}
|
|
20644
20953
|
};
|
|
20954
|
+
var DeletedAcpStoredSession = class extends AcpStoredSessionLookup {
|
|
20955
|
+
found = false;
|
|
20956
|
+
};
|
|
20645
20957
|
function readString2(value, field) {
|
|
20646
20958
|
if (typeof value !== "string" || value.length === 0) {
|
|
20647
20959
|
throw new Error(`Stored ACP session has an invalid ${field}`);
|
|
@@ -20682,7 +20994,22 @@ function decodeSessionRow(row) {
|
|
|
20682
20994
|
}
|
|
20683
20995
|
var MemoryAcpSessionStore = class {
|
|
20684
20996
|
#sessions = /* @__PURE__ */ new Map();
|
|
20997
|
+
#deleted = /* @__PURE__ */ new Set();
|
|
20998
|
+
findNative = (agentId, nativeSessionId) => [...this.#sessions.values()].filter(
|
|
20999
|
+
(session) => session.agentId === agentId && session.nativeSessionId === nativeSessionId
|
|
21000
|
+
);
|
|
21001
|
+
markDeleted = (agentId, nativeSessionId) => {
|
|
21002
|
+
const ids = this.findNative(agentId, nativeSessionId).map(
|
|
21003
|
+
(session) => session.sessionId
|
|
21004
|
+
);
|
|
21005
|
+
for (const id of ids) {
|
|
21006
|
+
this.#sessions.delete(id);
|
|
21007
|
+
this.#deleted.add(id);
|
|
21008
|
+
}
|
|
21009
|
+
return ids;
|
|
21010
|
+
};
|
|
20685
21011
|
read = (sessionId) => {
|
|
21012
|
+
if (this.#deleted.has(sessionId)) return new DeletedAcpStoredSession();
|
|
20686
21013
|
const session = this.#sessions.get(sessionId);
|
|
20687
21014
|
return session === void 0 ? new MissingAcpStoredSession() : new FoundAcpStoredSession(session);
|
|
20688
21015
|
};
|
|
@@ -20695,10 +21022,11 @@ var MemoryAcpSessionStore = class {
|
|
|
20695
21022
|
};
|
|
20696
21023
|
var SqliteAcpSessionStore = class {
|
|
20697
21024
|
#database;
|
|
20698
|
-
constructor(
|
|
20699
|
-
mkdirSync(dirname(
|
|
20700
|
-
this.#database = new DatabaseSync(
|
|
21025
|
+
constructor(path2) {
|
|
21026
|
+
mkdirSync(dirname(path2), { recursive: true });
|
|
21027
|
+
this.#database = new DatabaseSync(path2);
|
|
20701
21028
|
this.#database.exec(`
|
|
21029
|
+
CREATE TABLE IF NOT EXISTS acp_deleted_sessions (session_id TEXT PRIMARY KEY) STRICT;
|
|
20702
21030
|
CREATE TABLE IF NOT EXISTS acp_sessions (
|
|
20703
21031
|
session_id TEXT PRIMARY KEY,
|
|
20704
21032
|
agent_id TEXT NOT NULL,
|
|
@@ -20721,6 +21049,10 @@ var SqliteAcpSessionStore = class {
|
|
|
20721
21049
|
}
|
|
20722
21050
|
}
|
|
20723
21051
|
read = (sessionId) => {
|
|
21052
|
+
if (this.#database.prepare(
|
|
21053
|
+
"SELECT session_id FROM acp_deleted_sessions WHERE session_id = ?"
|
|
21054
|
+
).get(sessionId) !== void 0)
|
|
21055
|
+
return new DeletedAcpStoredSession();
|
|
20724
21056
|
const row = this.#database.prepare(
|
|
20725
21057
|
`
|
|
20726
21058
|
SELECT
|
|
@@ -20739,6 +21071,28 @@ var SqliteAcpSessionStore = class {
|
|
|
20739
21071
|
remove = (sessionId) => {
|
|
20740
21072
|
this.#database.prepare("DELETE FROM acp_sessions WHERE session_id = ?").run(sessionId);
|
|
20741
21073
|
};
|
|
21074
|
+
findNative = (agentId, nativeSessionId) => this.#database.prepare(
|
|
21075
|
+
"SELECT * FROM acp_sessions WHERE agent_id = ? AND native_session_id = ?"
|
|
21076
|
+
).all(agentId, nativeSessionId).map(decodeSessionRow);
|
|
21077
|
+
markDeleted = (agentId, nativeSessionId) => {
|
|
21078
|
+
const ids = this.findNative(agentId, nativeSessionId).map(
|
|
21079
|
+
(session) => session.sessionId
|
|
21080
|
+
);
|
|
21081
|
+
this.#database.exec("BEGIN");
|
|
21082
|
+
try {
|
|
21083
|
+
for (const id of ids) {
|
|
21084
|
+
this.#database.prepare(
|
|
21085
|
+
"INSERT OR IGNORE INTO acp_deleted_sessions (session_id) VALUES (?)"
|
|
21086
|
+
).run(id);
|
|
21087
|
+
this.remove(id);
|
|
21088
|
+
}
|
|
21089
|
+
this.#database.exec("COMMIT");
|
|
21090
|
+
return ids;
|
|
21091
|
+
} catch (error61) {
|
|
21092
|
+
this.#database.exec("ROLLBACK");
|
|
21093
|
+
throw error61;
|
|
21094
|
+
}
|
|
21095
|
+
};
|
|
20742
21096
|
write = (session) => {
|
|
20743
21097
|
this.#database.prepare(
|
|
20744
21098
|
`
|
|
@@ -20868,7 +21222,7 @@ function createWritableAcpStream(output2) {
|
|
|
20868
21222
|
if (outputFailure !== void 0) {
|
|
20869
21223
|
return Promise.reject(outputFailure);
|
|
20870
21224
|
}
|
|
20871
|
-
return new Promise((
|
|
21225
|
+
return new Promise((resolve5, reject) => {
|
|
20872
21226
|
pendingWriteFailures.add(reject);
|
|
20873
21227
|
output2.write(chunk, (error61) => {
|
|
20874
21228
|
pendingWriteFailures.delete(reject);
|
|
@@ -20881,7 +21235,7 @@ function createWritableAcpStream(output2) {
|
|
|
20881
21235
|
reject(outputFailure);
|
|
20882
21236
|
return;
|
|
20883
21237
|
}
|
|
20884
|
-
|
|
21238
|
+
resolve5();
|
|
20885
21239
|
});
|
|
20886
21240
|
});
|
|
20887
21241
|
}
|
|
@@ -21042,7 +21396,8 @@ function normalizeCapabilities(capabilities) {
|
|
|
21042
21396
|
imagePrompt: prompt?.image === true,
|
|
21043
21397
|
listSessions: session?.list != null,
|
|
21044
21398
|
loadSession: capabilities.loadSession === true,
|
|
21045
|
-
resumeSession: session?.resume != null
|
|
21399
|
+
resumeSession: session?.resume != null,
|
|
21400
|
+
unstableForkSession: session?.fork != null
|
|
21046
21401
|
};
|
|
21047
21402
|
}
|
|
21048
21403
|
function elicitationFieldType(value) {
|
|
@@ -21390,15 +21745,18 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21390
21745
|
imagePrompt: false,
|
|
21391
21746
|
listSessions: false,
|
|
21392
21747
|
loadSession: false,
|
|
21393
|
-
resumeSession: false
|
|
21748
|
+
resumeSession: false,
|
|
21749
|
+
unstableForkSession: false
|
|
21394
21750
|
};
|
|
21395
21751
|
#commands = [];
|
|
21396
21752
|
#configOptions = [];
|
|
21397
21753
|
#nativeSessionId = "";
|
|
21398
21754
|
#prompted = false;
|
|
21399
|
-
#
|
|
21755
|
+
#replay = {
|
|
21756
|
+
kind: "idle"
|
|
21757
|
+
};
|
|
21400
21758
|
#restored = false;
|
|
21401
|
-
#sessionTitle =
|
|
21759
|
+
#sessionTitle = null;
|
|
21402
21760
|
#sessionUpdatedAt = "";
|
|
21403
21761
|
#turn = new IdleAcpTurn();
|
|
21404
21762
|
#updateTail = Promise.resolve();
|
|
@@ -21427,9 +21785,9 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21427
21785
|
stderr.on("data", (chunk) => {
|
|
21428
21786
|
process.stderr.write(`[${agent.id}-acp] ${chunk}`);
|
|
21429
21787
|
});
|
|
21430
|
-
this.#processExit = new Promise((
|
|
21431
|
-
this.#child.once("error",
|
|
21432
|
-
this.#child.once("exit",
|
|
21788
|
+
this.#processExit = new Promise((resolve5) => {
|
|
21789
|
+
this.#child.once("error", resolve5);
|
|
21790
|
+
this.#child.once("exit", resolve5);
|
|
21433
21791
|
});
|
|
21434
21792
|
const stream = ndJsonStream(
|
|
21435
21793
|
createWritableAcpStream(stdin),
|
|
@@ -21460,10 +21818,11 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21460
21818
|
store
|
|
21461
21819
|
);
|
|
21462
21820
|
try {
|
|
21463
|
-
await runtime.initialize(options.signal);
|
|
21821
|
+
await runtime.initialize(options.signal, options.origin);
|
|
21464
21822
|
return runtime;
|
|
21465
21823
|
} catch (error61) {
|
|
21466
21824
|
await runtime.closeProcess();
|
|
21825
|
+
options.signal.throwIfAborted();
|
|
21467
21826
|
if (error61 instanceof AcpExecutionError) {
|
|
21468
21827
|
throw error61;
|
|
21469
21828
|
}
|
|
@@ -21482,7 +21841,33 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21482
21841
|
this.#configOptions,
|
|
21483
21842
|
this.#restored
|
|
21484
21843
|
);
|
|
21485
|
-
|
|
21844
|
+
loadHistory = async (signal) => {
|
|
21845
|
+
if (!this.#capabilities.loadSession) return { status: "unavailable" };
|
|
21846
|
+
const history = new AcpHistoryReplay();
|
|
21847
|
+
this.#replay = { kind: "capture", history };
|
|
21848
|
+
try {
|
|
21849
|
+
await this.race(
|
|
21850
|
+
this.#connection.agent.request(
|
|
21851
|
+
methods2.agent.session.load,
|
|
21852
|
+
{
|
|
21853
|
+
additionalDirectories: [...this.#additionalDirectories],
|
|
21854
|
+
cwd: this.#cwd,
|
|
21855
|
+
mcpServers: [],
|
|
21856
|
+
sessionId: this.#nativeSessionId
|
|
21857
|
+
},
|
|
21858
|
+
{ cancellationSignal: signal }
|
|
21859
|
+
),
|
|
21860
|
+
"ACP agent exited while loading history",
|
|
21861
|
+
signal
|
|
21862
|
+
);
|
|
21863
|
+
await this.#updateTail;
|
|
21864
|
+
signal.throwIfAborted();
|
|
21865
|
+
return history.snapshot();
|
|
21866
|
+
} finally {
|
|
21867
|
+
this.#replay = { kind: "idle" };
|
|
21868
|
+
}
|
|
21869
|
+
};
|
|
21870
|
+
initialize = async (signal, origin) => {
|
|
21486
21871
|
const initialized = await this.race(
|
|
21487
21872
|
this.#connection.agent.request(
|
|
21488
21873
|
methods2.agent.initialize,
|
|
@@ -21514,9 +21899,56 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21514
21899
|
255
|
|
21515
21900
|
);
|
|
21516
21901
|
this.#agentVersion = truncate2(initialized.agentInfo?.version ?? "", 128);
|
|
21517
|
-
const stored =
|
|
21902
|
+
const stored = origin?.kind === "import" ? new FoundAcpStoredSession(
|
|
21903
|
+
new AcpStoredSession(
|
|
21904
|
+
this.#mobiusSessionId,
|
|
21905
|
+
this.#agentId,
|
|
21906
|
+
nativeSessionIdSchema.parse(origin.nativeSessionId),
|
|
21907
|
+
this.#cwd,
|
|
21908
|
+
this.#additionalDirectories,
|
|
21909
|
+
true
|
|
21910
|
+
)
|
|
21911
|
+
) : this.#store.read(this.#mobiusSessionId);
|
|
21912
|
+
if (stored instanceof DeletedAcpStoredSession)
|
|
21913
|
+
throw new AcpExecutionError(
|
|
21914
|
+
"The native Agent session was deleted. Start a new chat.",
|
|
21915
|
+
{},
|
|
21916
|
+
"native_session_deleted"
|
|
21917
|
+
);
|
|
21518
21918
|
let lifecycleResponse;
|
|
21519
|
-
if (
|
|
21919
|
+
if (origin?.kind === "fork") {
|
|
21920
|
+
if (!this.#capabilities.unstableForkSession)
|
|
21921
|
+
throw new AcpExecutionError(
|
|
21922
|
+
"This Agent does not support session forking",
|
|
21923
|
+
{},
|
|
21924
|
+
"native_session_unsupported"
|
|
21925
|
+
);
|
|
21926
|
+
const forked = await this.race(
|
|
21927
|
+
this.#connection.agent.request(
|
|
21928
|
+
methods2.agent.session.fork,
|
|
21929
|
+
{
|
|
21930
|
+
sessionId: nativeSessionIdSchema.parse(origin.nativeSessionId),
|
|
21931
|
+
cwd: this.#cwd,
|
|
21932
|
+
additionalDirectories: [...this.#additionalDirectories],
|
|
21933
|
+
mcpServers: []
|
|
21934
|
+
},
|
|
21935
|
+
{ cancellationSignal: signal }
|
|
21936
|
+
),
|
|
21937
|
+
"ACP agent exited while forking a session",
|
|
21938
|
+
signal
|
|
21939
|
+
);
|
|
21940
|
+
this.#nativeSessionId = nativeSessionIdSchema.parse(forked.sessionId);
|
|
21941
|
+
if (this.#nativeSessionId === origin.nativeSessionId || this.#store.findNative(this.#agentId, this.#nativeSessionId).length > 0) {
|
|
21942
|
+
throw new AcpExecutionError(
|
|
21943
|
+
"The Agent did not create an independent fork",
|
|
21944
|
+
{},
|
|
21945
|
+
"invalid_native_session_response"
|
|
21946
|
+
);
|
|
21947
|
+
}
|
|
21948
|
+
this.#prompted = true;
|
|
21949
|
+
this.#restored = true;
|
|
21950
|
+
lifecycleResponse = forked;
|
|
21951
|
+
} else if (stored instanceof FoundAcpStoredSession && stored.session.agentId === this.#agentId && stored.session.cwd === this.#cwd && arraysEqual(
|
|
21520
21952
|
stored.session.additionalDirectories,
|
|
21521
21953
|
this.#additionalDirectories
|
|
21522
21954
|
)) {
|
|
@@ -21539,7 +21971,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21539
21971
|
signal
|
|
21540
21972
|
);
|
|
21541
21973
|
} else if (this.#capabilities.loadSession) {
|
|
21542
|
-
this.#
|
|
21974
|
+
this.#replay = { kind: "discard" };
|
|
21543
21975
|
try {
|
|
21544
21976
|
lifecycleResponse = await this.race(
|
|
21545
21977
|
this.#connection.agent.request(
|
|
@@ -21556,7 +21988,8 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21556
21988
|
signal
|
|
21557
21989
|
);
|
|
21558
21990
|
} finally {
|
|
21559
|
-
this.#
|
|
21991
|
+
await this.#updateTail;
|
|
21992
|
+
this.#replay = { kind: "idle" };
|
|
21560
21993
|
}
|
|
21561
21994
|
} else {
|
|
21562
21995
|
throw new AcpExecutionError(
|
|
@@ -21609,7 +22042,60 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21609
22042
|
);
|
|
21610
22043
|
await this.#updateTail;
|
|
21611
22044
|
signal.throwIfAborted();
|
|
22045
|
+
this.#store.write(
|
|
22046
|
+
new AcpStoredSession(
|
|
22047
|
+
this.#mobiusSessionId,
|
|
22048
|
+
this.#agentId,
|
|
22049
|
+
this.#nativeSessionId,
|
|
22050
|
+
this.#cwd,
|
|
22051
|
+
this.#additionalDirectories,
|
|
22052
|
+
this.#prompted
|
|
22053
|
+
)
|
|
22054
|
+
);
|
|
22055
|
+
};
|
|
22056
|
+
listNativeSessions = async (cursor, signal) => {
|
|
22057
|
+
if (!this.#capabilities.listSessions)
|
|
22058
|
+
throw new AcpExecutionError(
|
|
22059
|
+
"This Agent does not support native session listing",
|
|
22060
|
+
{},
|
|
22061
|
+
"native_session_unsupported"
|
|
22062
|
+
);
|
|
22063
|
+
const response = await this.race(
|
|
22064
|
+
this.#connection.agent.request(
|
|
22065
|
+
methods2.agent.session.list,
|
|
22066
|
+
cursor === null ? {} : { cursor },
|
|
22067
|
+
{ cancellationSignal: signal }
|
|
22068
|
+
),
|
|
22069
|
+
"ACP agent exited while listing sessions",
|
|
22070
|
+
signal
|
|
22071
|
+
);
|
|
22072
|
+
const decoded = nativeSessionPageSchema.safeParse(response);
|
|
22073
|
+
if (!decoded.success)
|
|
22074
|
+
throw new AcpExecutionError(
|
|
22075
|
+
"The Agent returned an invalid native session list",
|
|
22076
|
+
{ cause: decoded.error },
|
|
22077
|
+
"invalid_native_session_response"
|
|
22078
|
+
);
|
|
22079
|
+
return decoded.data;
|
|
21612
22080
|
};
|
|
22081
|
+
deleteNativeSession = async (nativeSessionId, signal) => {
|
|
22082
|
+
if (!this.#capabilities.deleteSession || !this.#capabilities.listSessions)
|
|
22083
|
+
throw new AcpExecutionError(
|
|
22084
|
+
"This Agent does not support native session deletion",
|
|
22085
|
+
{},
|
|
22086
|
+
"native_session_unsupported"
|
|
22087
|
+
);
|
|
22088
|
+
await this.race(
|
|
22089
|
+
this.#connection.agent.request(
|
|
22090
|
+
methods2.agent.session.delete,
|
|
22091
|
+
{ sessionId: nativeSessionIdSchema.parse(nativeSessionId) },
|
|
22092
|
+
{ cancellationSignal: signal }
|
|
22093
|
+
),
|
|
22094
|
+
"ACP agent exited while deleting a session",
|
|
22095
|
+
signal
|
|
22096
|
+
);
|
|
22097
|
+
};
|
|
22098
|
+
nativeSessionId = () => this.#nativeSessionId;
|
|
21613
22099
|
configure = async (options) => {
|
|
21614
22100
|
const existing = this.#configOptions.find(
|
|
21615
22101
|
(option) => option.id === options.configId
|
|
@@ -21772,7 +22258,28 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21772
22258
|
);
|
|
21773
22259
|
await this.#updateTail;
|
|
21774
22260
|
await running.activities.finish();
|
|
21775
|
-
|
|
22261
|
+
let result;
|
|
22262
|
+
try {
|
|
22263
|
+
result = decodeAgentPromptResponse(response);
|
|
22264
|
+
} catch (cause) {
|
|
22265
|
+
throw new AcpExecutionError(
|
|
22266
|
+
"ACP agent returned an invalid prompt response",
|
|
22267
|
+
{ cause },
|
|
22268
|
+
"acp_invalid_prompt_response"
|
|
22269
|
+
);
|
|
22270
|
+
}
|
|
22271
|
+
if (result.usage != null || result._meta?.quota !== void 0) {
|
|
22272
|
+
await this.#getObserver().onUsage({
|
|
22273
|
+
_meta: result._meta ?? null,
|
|
22274
|
+
commandId: options.commandId,
|
|
22275
|
+
kind: SESSION_USAGE_UPDATED_KIND,
|
|
22276
|
+
protocolVersion: MOBIUS_PROTOCOL_VERSION,
|
|
22277
|
+
sessionId: this.#mobiusSessionId,
|
|
22278
|
+
source: "prompt",
|
|
22279
|
+
usage: result.usage ?? null
|
|
22280
|
+
});
|
|
22281
|
+
}
|
|
22282
|
+
return result;
|
|
21776
22283
|
} catch (error61) {
|
|
21777
22284
|
await running.activities.finish();
|
|
21778
22285
|
if (error61 instanceof AcpExecutionError) {
|
|
@@ -21992,7 +22499,8 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
21992
22499
|
processUpdate = async (notification) => {
|
|
21993
22500
|
this.assertNativeSession(notification.sessionId);
|
|
21994
22501
|
const update = notification.update;
|
|
21995
|
-
if (this.#
|
|
22502
|
+
if (this.#replay.kind === "capture") this.#replay.history.record(update);
|
|
22503
|
+
if (this.#replay.kind !== "idle" && (update.sessionUpdate === "user_message_chunk" || update.sessionUpdate === "agent_message_chunk" || update.sessionUpdate === "agent_thought_chunk" || update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update" || update.sessionUpdate === "plan" || update.sessionUpdate === "plan_update" || update.sessionUpdate === "plan_removed")) {
|
|
21996
22504
|
return;
|
|
21997
22505
|
}
|
|
21998
22506
|
if (update.sessionUpdate === "agent_message_chunk") {
|
|
@@ -22106,6 +22614,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
22106
22614
|
protocolVersion: MOBIUS_PROTOCOL_VERSION,
|
|
22107
22615
|
sessionId: this.#mobiusSessionId,
|
|
22108
22616
|
size: Math.max(0, Math.trunc(update.size)),
|
|
22617
|
+
source: "context",
|
|
22109
22618
|
used: Math.max(0, Math.trunc(update.used))
|
|
22110
22619
|
});
|
|
22111
22620
|
}
|
|
@@ -22227,6 +22736,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
22227
22736
|
};
|
|
22228
22737
|
};
|
|
22229
22738
|
var AcpSessionManager = class {
|
|
22739
|
+
#nativeOperations = /* @__PURE__ */ new Set();
|
|
22230
22740
|
#activeSessionIds = /* @__PURE__ */ new Set();
|
|
22231
22741
|
#cancellationRequests = /* @__PURE__ */ new Set();
|
|
22232
22742
|
#preparing = /* @__PURE__ */ new Map();
|
|
@@ -22278,6 +22788,21 @@ var AcpSessionManager = class {
|
|
|
22278
22788
|
return await operation;
|
|
22279
22789
|
}
|
|
22280
22790
|
prepareSession = async (options) => {
|
|
22791
|
+
const stored = this.#store.read(options.sessionId);
|
|
22792
|
+
if (stored instanceof DeletedAcpStoredSession)
|
|
22793
|
+
throw new AcpExecutionError(
|
|
22794
|
+
"The native Agent session was deleted. Start a new chat.",
|
|
22795
|
+
{},
|
|
22796
|
+
"native_session_deleted"
|
|
22797
|
+
);
|
|
22798
|
+
if (stored instanceof FoundAcpStoredSession && this.#nativeOperations.has(
|
|
22799
|
+
`${stored.session.agentId}:${stored.session.nativeSessionId}`
|
|
22800
|
+
))
|
|
22801
|
+
throw new AcpExecutionError(
|
|
22802
|
+
"The native session is being managed",
|
|
22803
|
+
{},
|
|
22804
|
+
"session_busy"
|
|
22805
|
+
);
|
|
22281
22806
|
const pending = this.#preparing.get(options.sessionId);
|
|
22282
22807
|
if (pending !== void 0) {
|
|
22283
22808
|
const runtime2 = await pending;
|
|
@@ -22324,6 +22849,16 @@ var AcpSessionManager = class {
|
|
|
22324
22849
|
}
|
|
22325
22850
|
};
|
|
22326
22851
|
runPrompt = async (options) => {
|
|
22852
|
+
const stored = this.#store.read(options.sessionId);
|
|
22853
|
+
if (stored instanceof FoundAcpStoredSession && this.#nativeOperations.has(
|
|
22854
|
+
`${stored.session.agentId}:${stored.session.nativeSessionId}`
|
|
22855
|
+
)) {
|
|
22856
|
+
throw new AcpExecutionError(
|
|
22857
|
+
"The native session is being managed",
|
|
22858
|
+
{},
|
|
22859
|
+
"session_busy"
|
|
22860
|
+
);
|
|
22861
|
+
}
|
|
22327
22862
|
if (this.#activeSessionIds.has(options.sessionId)) {
|
|
22328
22863
|
throw new AcpExecutionError(
|
|
22329
22864
|
"Another prompt is already running in this conversation",
|
|
@@ -22337,6 +22872,14 @@ var AcpSessionManager = class {
|
|
|
22337
22872
|
return { stopReason: "cancelled" };
|
|
22338
22873
|
}
|
|
22339
22874
|
const runtime = await this.ensureSession(options);
|
|
22875
|
+
if (this.#nativeOperations.has(
|
|
22876
|
+
`${options.agent.id}:${runtime.nativeSessionId()}`
|
|
22877
|
+
))
|
|
22878
|
+
throw new AcpExecutionError(
|
|
22879
|
+
"The native session is being managed",
|
|
22880
|
+
{},
|
|
22881
|
+
"session_busy"
|
|
22882
|
+
);
|
|
22340
22883
|
if (this.#cancellationRequests.has(options.sessionId)) {
|
|
22341
22884
|
return { stopReason: "cancelled" };
|
|
22342
22885
|
}
|
|
@@ -22408,6 +22951,124 @@ var AcpSessionManager = class {
|
|
|
22408
22951
|
}
|
|
22409
22952
|
return started;
|
|
22410
22953
|
};
|
|
22954
|
+
listNativeSessions = async (options, cursor) => await (await this.ensureSession(options)).listNativeSessions(
|
|
22955
|
+
cursor,
|
|
22956
|
+
options.signal
|
|
22957
|
+
);
|
|
22958
|
+
openNativeSession = async (options, origin) => {
|
|
22959
|
+
const key = `${options.agent.id}:${origin.nativeSessionId}`;
|
|
22960
|
+
if (this.#nativeOperations.has(key))
|
|
22961
|
+
throw new AcpExecutionError(
|
|
22962
|
+
"The native session is being managed",
|
|
22963
|
+
{},
|
|
22964
|
+
"session_busy"
|
|
22965
|
+
);
|
|
22966
|
+
const linked = this.#store.findNative(
|
|
22967
|
+
options.agent.id,
|
|
22968
|
+
origin.nativeSessionId
|
|
22969
|
+
);
|
|
22970
|
+
if (linked.some(
|
|
22971
|
+
(entry) => this.#activeSessionIds.has(entry.sessionId) || this.#preparing.has(entry.sessionId)
|
|
22972
|
+
))
|
|
22973
|
+
throw new AcpExecutionError(
|
|
22974
|
+
"Wait for the native session's running operation to finish",
|
|
22975
|
+
{},
|
|
22976
|
+
"session_busy"
|
|
22977
|
+
);
|
|
22978
|
+
const existing = linked[0];
|
|
22979
|
+
if (origin.kind === "import" && existing !== void 0) {
|
|
22980
|
+
const runtime = await this.ensureSession({
|
|
22981
|
+
...options,
|
|
22982
|
+
sessionId: existing.sessionId
|
|
22983
|
+
});
|
|
22984
|
+
if (this.#activeSessionIds.has(existing.sessionId) || this.#nativeOperations.has(key))
|
|
22985
|
+
throw new AcpExecutionError(
|
|
22986
|
+
"Wait for the native session's running operation to finish",
|
|
22987
|
+
{},
|
|
22988
|
+
"session_busy"
|
|
22989
|
+
);
|
|
22990
|
+
this.#nativeOperations.add(key);
|
|
22991
|
+
try {
|
|
22992
|
+
const history = await runtime.loadHistory(options.signal);
|
|
22993
|
+
return {
|
|
22994
|
+
prepared: runtime.prepared(),
|
|
22995
|
+
sessionId: existing.sessionId,
|
|
22996
|
+
history
|
|
22997
|
+
};
|
|
22998
|
+
} catch (error61) {
|
|
22999
|
+
this.#sessions.delete(existing.sessionId);
|
|
23000
|
+
await runtime.closeProcess();
|
|
23001
|
+
throw error61;
|
|
23002
|
+
} finally {
|
|
23003
|
+
this.#nativeOperations.delete(key);
|
|
23004
|
+
}
|
|
23005
|
+
}
|
|
23006
|
+
if (this.#sessions.has(options.sessionId) || this.#preparing.has(options.sessionId) || this.#store.read(options.sessionId) instanceof FoundAcpStoredSession || this.#store.read(options.sessionId) instanceof DeletedAcpStoredSession)
|
|
23007
|
+
throw new AcpExecutionError(
|
|
23008
|
+
"The destination conversation already exists",
|
|
23009
|
+
{},
|
|
23010
|
+
"session_identity_mismatch"
|
|
23011
|
+
);
|
|
23012
|
+
this.#nativeOperations.add(key);
|
|
23013
|
+
try {
|
|
23014
|
+
const runtime = await this.#startPreparation({ ...options, origin });
|
|
23015
|
+
const openedKey = `${options.agent.id}:${runtime.nativeSessionId()}`;
|
|
23016
|
+
this.#nativeOperations.add(openedKey);
|
|
23017
|
+
try {
|
|
23018
|
+
const history = await runtime.loadHistory(options.signal);
|
|
23019
|
+
return {
|
|
23020
|
+
sessionId: options.sessionId,
|
|
23021
|
+
prepared: runtime.prepared(),
|
|
23022
|
+
history
|
|
23023
|
+
};
|
|
23024
|
+
} catch (error61) {
|
|
23025
|
+
this.#sessions.delete(options.sessionId);
|
|
23026
|
+
this.#store.remove(options.sessionId);
|
|
23027
|
+
await runtime.closeProcess();
|
|
23028
|
+
throw error61;
|
|
23029
|
+
} finally {
|
|
23030
|
+
this.#nativeOperations.delete(openedKey);
|
|
23031
|
+
}
|
|
23032
|
+
} finally {
|
|
23033
|
+
this.#nativeOperations.delete(key);
|
|
23034
|
+
}
|
|
23035
|
+
};
|
|
23036
|
+
deleteNativeSession = async (options, nativeSessionId) => {
|
|
23037
|
+
const runtime = await this.ensureSession(options);
|
|
23038
|
+
const key = `${options.agent.id}:${nativeSessionId}`;
|
|
23039
|
+
const linked = this.#store.findNative(options.agent.id, nativeSessionId);
|
|
23040
|
+
if (this.#nativeOperations.has(key) || linked.some(
|
|
23041
|
+
(entry) => this.#activeSessionIds.has(entry.sessionId) || this.#preparing.has(entry.sessionId)
|
|
23042
|
+
))
|
|
23043
|
+
throw new AcpExecutionError(
|
|
23044
|
+
"Wait for the native session's running operation to finish",
|
|
23045
|
+
{},
|
|
23046
|
+
"session_busy"
|
|
23047
|
+
);
|
|
23048
|
+
this.#nativeOperations.add(key);
|
|
23049
|
+
try {
|
|
23050
|
+
for (const entry of linked) {
|
|
23051
|
+
const active = this.#sessions.get(entry.sessionId);
|
|
23052
|
+
if (active !== void 0 && active !== runtime) {
|
|
23053
|
+
this.#sessions.delete(entry.sessionId);
|
|
23054
|
+
await active.close();
|
|
23055
|
+
}
|
|
23056
|
+
}
|
|
23057
|
+
options.signal.throwIfAborted();
|
|
23058
|
+
await runtime.deleteNativeSession(nativeSessionId, options.signal);
|
|
23059
|
+
const deleted = this.#store.markDeleted(
|
|
23060
|
+
options.agent.id,
|
|
23061
|
+
nativeSessionId
|
|
23062
|
+
);
|
|
23063
|
+
if (runtime.nativeSessionId() === nativeSessionId) {
|
|
23064
|
+
this.#sessions.delete(options.sessionId);
|
|
23065
|
+
await runtime.close();
|
|
23066
|
+
}
|
|
23067
|
+
return deleted;
|
|
23068
|
+
} finally {
|
|
23069
|
+
this.#nativeOperations.delete(key);
|
|
23070
|
+
}
|
|
23071
|
+
};
|
|
22411
23072
|
};
|
|
22412
23073
|
|
|
22413
23074
|
// src/acp/run-prompt.ts
|
|
@@ -22443,9 +23104,9 @@ import { dirname as dirname2 } from "node:path";
|
|
|
22443
23104
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
22444
23105
|
var AuditStore = class {
|
|
22445
23106
|
database;
|
|
22446
|
-
constructor(
|
|
22447
|
-
mkdirSync2(dirname2(
|
|
22448
|
-
this.database = new DatabaseSync2(
|
|
23107
|
+
constructor(path2) {
|
|
23108
|
+
mkdirSync2(dirname2(path2), { recursive: true });
|
|
23109
|
+
this.database = new DatabaseSync2(path2);
|
|
22449
23110
|
this.database.exec(`
|
|
22450
23111
|
CREATE TABLE IF NOT EXISTS audit_events (
|
|
22451
23112
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -22487,8 +23148,7 @@ var AuditStore = class {
|
|
|
22487
23148
|
}
|
|
22488
23149
|
};
|
|
22489
23150
|
|
|
22490
|
-
// src/authorization/
|
|
22491
|
-
import { realpath as realpath2, stat as stat2 } from "node:fs/promises";
|
|
23151
|
+
// src/authorization/local-authorization-error.ts
|
|
22492
23152
|
var LocalAuthorizationError = class extends Error {
|
|
22493
23153
|
code = "local_authorization_failed";
|
|
22494
23154
|
constructor(message, options = {}) {
|
|
@@ -22496,45 +23156,24 @@ var LocalAuthorizationError = class extends Error {
|
|
|
22496
23156
|
this.name = "LocalAuthorizationError";
|
|
22497
23157
|
}
|
|
22498
23158
|
};
|
|
22499
|
-
async function authorizeBinding(requestedBindingId, approvedBindingId, approvedRoot) {
|
|
22500
|
-
if (requestedBindingId !== approvedBindingId) {
|
|
22501
|
-
throw new LocalAuthorizationError("The runtime binding is not authorized");
|
|
22502
|
-
}
|
|
22503
|
-
try {
|
|
22504
|
-
const resolvedRoot = await realpath2(approvedRoot);
|
|
22505
|
-
const metadata = await stat2(resolvedRoot);
|
|
22506
|
-
if (!metadata.isDirectory()) {
|
|
22507
|
-
throw new LocalAuthorizationError("The approved root is not a directory");
|
|
22508
|
-
}
|
|
22509
|
-
return resolvedRoot;
|
|
22510
|
-
} catch (error61) {
|
|
22511
|
-
if (error61 instanceof LocalAuthorizationError) {
|
|
22512
|
-
throw error61;
|
|
22513
|
-
}
|
|
22514
|
-
throw new LocalAuthorizationError("The approved root cannot be resolved", {
|
|
22515
|
-
cause: error61
|
|
22516
|
-
});
|
|
22517
|
-
}
|
|
22518
|
-
}
|
|
22519
23159
|
|
|
22520
23160
|
// src/pairing/pair-local-machine.ts
|
|
22521
23161
|
import { randomUUID } from "node:crypto";
|
|
22522
23162
|
import {
|
|
22523
23163
|
mkdir as mkdir2,
|
|
22524
23164
|
open as open4,
|
|
22525
|
-
realpath as
|
|
23165
|
+
realpath as realpath3,
|
|
22526
23166
|
rename as rename2,
|
|
22527
|
-
stat as
|
|
23167
|
+
stat as stat3,
|
|
22528
23168
|
writeFile
|
|
22529
23169
|
} from "node:fs/promises";
|
|
22530
23170
|
import { hostname as hostname3 } from "node:os";
|
|
22531
|
-
import { basename, join as
|
|
23171
|
+
import { basename, join as join7, resolve as resolve3 } from "node:path";
|
|
22532
23172
|
import { fetch } from "undici";
|
|
22533
23173
|
|
|
22534
23174
|
// src/agent-catalog/discover-local-agents.ts
|
|
22535
23175
|
import { spawn as spawn3 } from "node:child_process";
|
|
22536
|
-
import {
|
|
22537
|
-
import { isAbsolute as isAbsolute2, join as join5, relative as relative3, resolve as resolve3 } from "node:path";
|
|
23176
|
+
import { isAbsolute, join as join6 } from "node:path";
|
|
22538
23177
|
import {
|
|
22539
23178
|
methods as methods3,
|
|
22540
23179
|
ndJsonStream as ndJsonStream2,
|
|
@@ -22543,17 +23182,17 @@ import {
|
|
|
22543
23182
|
|
|
22544
23183
|
// src/agent-installation/installation-grants.ts
|
|
22545
23184
|
import { open as open2 } from "node:fs/promises";
|
|
22546
|
-
import { join as
|
|
23185
|
+
import { join as join4 } from "node:path";
|
|
22547
23186
|
var MAX_GRANT_FILE_LENGTH = 16 * 1024;
|
|
22548
23187
|
var AGENT_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
|
|
22549
23188
|
function isRecord4(value) {
|
|
22550
23189
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22551
23190
|
}
|
|
22552
23191
|
async function readGrantedAgents(stateDirectory) {
|
|
22553
|
-
const
|
|
23192
|
+
const path2 = join4(stateDirectory, "installation-grants.json");
|
|
22554
23193
|
let handle;
|
|
22555
23194
|
try {
|
|
22556
|
-
handle = await open2(
|
|
23195
|
+
handle = await open2(path2, "r");
|
|
22557
23196
|
} catch (error61) {
|
|
22558
23197
|
if (error61 instanceof Error && "code" in error61 && error61.code === "ENOENT") {
|
|
22559
23198
|
return /* @__PURE__ */ new Set();
|
|
@@ -22588,12 +23227,12 @@ import {
|
|
|
22588
23227
|
mkdir,
|
|
22589
23228
|
mkdtemp,
|
|
22590
23229
|
open as open3,
|
|
22591
|
-
realpath as
|
|
23230
|
+
realpath as realpath2,
|
|
22592
23231
|
rename,
|
|
22593
23232
|
rm,
|
|
22594
|
-
stat as
|
|
23233
|
+
stat as stat2
|
|
22595
23234
|
} from "node:fs/promises";
|
|
22596
|
-
import { delimiter, dirname as dirname3, join as
|
|
23235
|
+
import { delimiter, dirname as dirname3, join as join5, relative } from "node:path";
|
|
22597
23236
|
var ADAPTER_INSTALL_TIMEOUT_MILLISECONDS = 2 * 60 * 1e3;
|
|
22598
23237
|
var PACKAGE_METADATA_LIMIT_BYTES = 64 * 1024;
|
|
22599
23238
|
var ManagedAdapterInstallError = class extends Error {
|
|
@@ -22608,10 +23247,10 @@ function isRecord5(value) {
|
|
|
22608
23247
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22609
23248
|
}
|
|
22610
23249
|
function packagePath(root, packageName) {
|
|
22611
|
-
return
|
|
23250
|
+
return join5(root, "node_modules", ...packageName.split("/"));
|
|
22612
23251
|
}
|
|
22613
|
-
async function readPackageJson(
|
|
22614
|
-
const handle = await open3(
|
|
23252
|
+
async function readPackageJson(path2) {
|
|
23253
|
+
const handle = await open3(path2, "r");
|
|
22615
23254
|
try {
|
|
22616
23255
|
const metadata = await handle.stat();
|
|
22617
23256
|
if (!metadata.isFile() || metadata.size > PACKAGE_METADATA_LIMIT_BYTES) {
|
|
@@ -22648,18 +23287,18 @@ function readBinPath(packageMetadata, executableName) {
|
|
|
22648
23287
|
);
|
|
22649
23288
|
}
|
|
22650
23289
|
async function validateManagedAdapter(root, specification) {
|
|
22651
|
-
const rootPath = await
|
|
22652
|
-
const packageRoot = await
|
|
23290
|
+
const rootPath = await realpath2(root);
|
|
23291
|
+
const packageRoot = await realpath2(
|
|
22653
23292
|
packagePath(rootPath, specification.adapterPackage)
|
|
22654
23293
|
);
|
|
22655
|
-
const packageDifference =
|
|
23294
|
+
const packageDifference = relative(rootPath, packageRoot);
|
|
22656
23295
|
if (packageDifference.startsWith("..") || packageDifference.length === 0) {
|
|
22657
23296
|
throw new ManagedAdapterInstallError(
|
|
22658
23297
|
"adapter_verification_failed",
|
|
22659
23298
|
"The managed adapter package escaped its private cache"
|
|
22660
23299
|
);
|
|
22661
23300
|
}
|
|
22662
|
-
const metadata = await readPackageJson(
|
|
23301
|
+
const metadata = await readPackageJson(join5(packageRoot, "package.json"));
|
|
22663
23302
|
if (metadata["name"] !== specification.adapterPackage || metadata["version"] !== specification.adapterVersion) {
|
|
22664
23303
|
throw new ManagedAdapterInstallError(
|
|
22665
23304
|
"adapter_verification_failed",
|
|
@@ -22668,7 +23307,7 @@ async function validateManagedAdapter(root, specification) {
|
|
|
22668
23307
|
}
|
|
22669
23308
|
const declaredBin = readBinPath(metadata, specification.adapterExecutable);
|
|
22670
23309
|
const executable = await resolveExecutablePath(
|
|
22671
|
-
|
|
23310
|
+
join5(packageRoot, declaredBin)
|
|
22672
23311
|
);
|
|
22673
23312
|
if (executable === null) {
|
|
22674
23313
|
throw new ManagedAdapterInstallError(
|
|
@@ -22676,7 +23315,7 @@ async function validateManagedAdapter(root, specification) {
|
|
|
22676
23315
|
"The managed adapter executable is invalid"
|
|
22677
23316
|
);
|
|
22678
23317
|
}
|
|
22679
|
-
const executableDifference =
|
|
23318
|
+
const executableDifference = relative(rootPath, executable.path);
|
|
22680
23319
|
if (executableDifference.startsWith("..") || executableDifference.length === 0) {
|
|
22681
23320
|
throw new ManagedAdapterInstallError(
|
|
22682
23321
|
"adapter_verification_failed",
|
|
@@ -22686,20 +23325,18 @@ async function validateManagedAdapter(root, specification) {
|
|
|
22686
23325
|
return executable;
|
|
22687
23326
|
}
|
|
22688
23327
|
var ManagedAdapterInstaller = class {
|
|
22689
|
-
#approvedRoot;
|
|
22690
23328
|
#environment;
|
|
22691
23329
|
#stateDirectory;
|
|
22692
23330
|
constructor(options) {
|
|
22693
|
-
this.#approvedRoot = options.approvedRoot;
|
|
22694
23331
|
this.#environment = options.environment;
|
|
22695
23332
|
this.#stateDirectory = options.stateDirectory;
|
|
22696
23333
|
}
|
|
22697
23334
|
install = async (specification, signal) => {
|
|
22698
23335
|
signal.throwIfAborted();
|
|
22699
|
-
const agentCache =
|
|
22700
|
-
const published =
|
|
23336
|
+
const agentCache = join5(this.#stateDirectory, "adapters", specification.id);
|
|
23337
|
+
const published = join5(agentCache, specification.adapterVersion);
|
|
22701
23338
|
try {
|
|
22702
|
-
const metadata = await
|
|
23339
|
+
const metadata = await stat2(published);
|
|
22703
23340
|
if (metadata.isDirectory()) {
|
|
22704
23341
|
return await validateManagedAdapter(published, specification);
|
|
22705
23342
|
}
|
|
@@ -22710,13 +23347,12 @@ var ManagedAdapterInstaller = class {
|
|
|
22710
23347
|
}
|
|
22711
23348
|
await mkdir(agentCache, { mode: 448, recursive: true });
|
|
22712
23349
|
const staging = await mkdtemp(
|
|
22713
|
-
|
|
23350
|
+
join5(agentCache, `.${specification.adapterVersion}.staging-`)
|
|
22714
23351
|
);
|
|
22715
23352
|
try {
|
|
22716
23353
|
const npm = await findExecutableOnPath(
|
|
22717
23354
|
process.platform === "win32" ? "npm.cmd" : "npm",
|
|
22718
|
-
this.#environment
|
|
22719
|
-
this.#approvedRoot
|
|
23355
|
+
this.#environment
|
|
22720
23356
|
);
|
|
22721
23357
|
if (npm === null) {
|
|
22722
23358
|
throw new ManagedAdapterInstallError(
|
|
@@ -22741,7 +23377,7 @@ var ManagedAdapterInstaller = class {
|
|
|
22741
23377
|
command: npm.path,
|
|
22742
23378
|
cwd: staging,
|
|
22743
23379
|
environment: {
|
|
22744
|
-
HOME:
|
|
23380
|
+
HOME: join5(staging, "home"),
|
|
22745
23381
|
NPM_CONFIG_AUDIT: "false",
|
|
22746
23382
|
NPM_CONFIG_FUND: "false",
|
|
22747
23383
|
NPM_CONFIG_IGNORE_SCRIPTS: "true",
|
|
@@ -22786,17 +23422,6 @@ var ManagedAdapterInstaller = class {
|
|
|
22786
23422
|
};
|
|
22787
23423
|
};
|
|
22788
23424
|
|
|
22789
|
-
// src/storage/state-directory.ts
|
|
22790
|
-
import { homedir } from "node:os";
|
|
22791
|
-
import { join as join4, resolve as resolve2 } from "node:path";
|
|
22792
|
-
function readStateDirectory(environment = process.env) {
|
|
22793
|
-
const configured = environment["MOBIUS_STATE_DIRECTORY"]?.trim();
|
|
22794
|
-
if (configured !== void 0 && configured.length > 0) {
|
|
22795
|
-
return resolve2(configured);
|
|
22796
|
-
}
|
|
22797
|
-
return join4(homedir(), ".mobius");
|
|
22798
|
-
}
|
|
22799
|
-
|
|
22800
23425
|
// src/agent-catalog/discover-local-agents.ts
|
|
22801
23426
|
var VERSION_PROBE_TIMEOUT_MILLISECONDS = 3e3;
|
|
22802
23427
|
var ACP_PROBE_TIMEOUT_MILLISECONDS = 5e3;
|
|
@@ -22960,29 +23585,17 @@ var DiscoveryAcpHandler = class {
|
|
|
22960
23585
|
onPermission = (_request, _signal) => Promise.reject(new Error("ACP probe does not grant permissions"));
|
|
22961
23586
|
onUpdate = (_notification) => Promise.resolve();
|
|
22962
23587
|
};
|
|
22963
|
-
async function authorizeManagedExecutable(managed, options) {
|
|
22964
|
-
const approvedRoot = await realpath4(resolve3(options.approvedRoot));
|
|
22965
|
-
const requestedDifference = relative3(
|
|
22966
|
-
approvedRoot,
|
|
22967
|
-
managed.identity.requestedPath
|
|
22968
|
-
);
|
|
22969
|
-
const resolvedDifference = relative3(approvedRoot, managed.path);
|
|
22970
|
-
const requestedInside = requestedDifference.length === 0 || !requestedDifference.startsWith("..") && !isAbsolute2(requestedDifference);
|
|
22971
|
-
const resolvedInside = resolvedDifference.length === 0 || !resolvedDifference.startsWith("..") && !isAbsolute2(resolvedDifference);
|
|
22972
|
-
return requestedInside || resolvedInside ? null : managed;
|
|
22973
|
-
}
|
|
22974
23588
|
async function resolveManagedExecutable(managedExecutablePath, options) {
|
|
22975
|
-
|
|
22976
|
-
|
|
23589
|
+
return await resolveExecutablePath(
|
|
23590
|
+
join6(readStateDirectory(options.environment), ...managedExecutablePath),
|
|
22977
23591
|
options.platform
|
|
22978
23592
|
);
|
|
22979
|
-
return managed === null ? null : await authorizeManagedExecutable(managed, options);
|
|
22980
23593
|
}
|
|
22981
23594
|
async function resolveManagedAdapterExecutable(specification, options) {
|
|
22982
23595
|
let managed;
|
|
22983
23596
|
try {
|
|
22984
23597
|
managed = await validateManagedAdapter(
|
|
22985
|
-
|
|
23598
|
+
join6(
|
|
22986
23599
|
readStateDirectory(options.environment),
|
|
22987
23600
|
"adapters",
|
|
22988
23601
|
specification.id,
|
|
@@ -22993,12 +23606,12 @@ async function resolveManagedAdapterExecutable(specification, options) {
|
|
|
22993
23606
|
} catch {
|
|
22994
23607
|
return null;
|
|
22995
23608
|
}
|
|
22996
|
-
return
|
|
23609
|
+
return managed;
|
|
22997
23610
|
}
|
|
22998
23611
|
async function resolveSpecificationExecutables(executableName, overrideVariable, resolveManaged, options) {
|
|
22999
23612
|
const override = options.environment[overrideVariable];
|
|
23000
23613
|
if (override !== void 0) {
|
|
23001
|
-
if (override.length === 0 || !
|
|
23614
|
+
if (override.length === 0 || !isAbsolute(override)) {
|
|
23002
23615
|
return [];
|
|
23003
23616
|
}
|
|
23004
23617
|
const executable = await resolveExecutablePath(override, options.platform);
|
|
@@ -23007,7 +23620,6 @@ async function resolveSpecificationExecutables(executableName, overrideVariable,
|
|
|
23007
23620
|
const external = await findExecutableOnPath(
|
|
23008
23621
|
executableName,
|
|
23009
23622
|
options.environment,
|
|
23010
|
-
options.approvedRoot,
|
|
23011
23623
|
options.platform
|
|
23012
23624
|
);
|
|
23013
23625
|
const managed = await resolveManaged();
|
|
@@ -23049,11 +23661,11 @@ async function resolveAdapterExecutable(specification, options) {
|
|
|
23049
23661
|
function normalizedVersion(output2) {
|
|
23050
23662
|
return output2.trim().split(/\r?\n/, 1)[0]?.trim().slice(0, 128) ?? "";
|
|
23051
23663
|
}
|
|
23052
|
-
async function probeVersion(executable, environment,
|
|
23664
|
+
async function probeVersion(executable, environment, defaultWorkspace, signal) {
|
|
23053
23665
|
const result = await runBoundedProcess({
|
|
23054
23666
|
arguments: ["--version"],
|
|
23055
23667
|
command: executable.path,
|
|
23056
|
-
cwd:
|
|
23668
|
+
cwd: defaultWorkspace,
|
|
23057
23669
|
environment,
|
|
23058
23670
|
signal,
|
|
23059
23671
|
timeoutMilliseconds: VERSION_PROBE_TIMEOUT_MILLISECONDS
|
|
@@ -23082,7 +23694,7 @@ async function findVerifiedAgentExecutable(specification, options) {
|
|
|
23082
23694
|
const version2 = await probeVersion(
|
|
23083
23695
|
executable,
|
|
23084
23696
|
options.environment,
|
|
23085
|
-
options.
|
|
23697
|
+
options.defaultWorkspace,
|
|
23086
23698
|
options.signal
|
|
23087
23699
|
);
|
|
23088
23700
|
return new VerifiedAgentExecutable(executable, version2);
|
|
@@ -23149,7 +23761,7 @@ function writableAcpStream(output2) {
|
|
|
23149
23761
|
}
|
|
23150
23762
|
});
|
|
23151
23763
|
}
|
|
23152
|
-
async function probeAcp(agent, environment,
|
|
23764
|
+
async function probeAcp(agent, environment, defaultWorkspace, signal) {
|
|
23153
23765
|
signal.throwIfAborted();
|
|
23154
23766
|
const controller = new AbortController();
|
|
23155
23767
|
let timedOut = false;
|
|
@@ -23163,13 +23775,19 @@ async function probeAcp(agent, environment, approvedRoot, signal) {
|
|
|
23163
23775
|
abort();
|
|
23164
23776
|
}
|
|
23165
23777
|
const child = spawn3(agent.adapterPath, [...agent.arguments], {
|
|
23166
|
-
cwd:
|
|
23778
|
+
cwd: defaultWorkspace,
|
|
23167
23779
|
detached: process.platform !== "win32",
|
|
23168
23780
|
env: { ...environment, ...agent.environment },
|
|
23169
23781
|
shell: false,
|
|
23170
23782
|
stdio: ["pipe", "pipe", "pipe"]
|
|
23171
23783
|
});
|
|
23172
|
-
const
|
|
23784
|
+
const processExit = new Promise((_resolve, reject) => {
|
|
23785
|
+
const rejectExit = () => reject(new Error("ACP adapter exited during initialization"));
|
|
23786
|
+
child.once("error", reject);
|
|
23787
|
+
child.once("exit", rejectExit);
|
|
23788
|
+
if (child.exitCode !== null || child.signalCode !== null) rejectExit();
|
|
23789
|
+
});
|
|
23790
|
+
const stdin = child.stdin;
|
|
23173
23791
|
const stdout = child.stdout;
|
|
23174
23792
|
const stderr = child.stderr;
|
|
23175
23793
|
if (stdin === null || stdout === null || stderr === null) {
|
|
@@ -23190,13 +23808,6 @@ async function probeAcp(agent, environment, approvedRoot, signal) {
|
|
|
23190
23808
|
const connection = createAcpClient(new DiscoveryAcpHandler()).connect(
|
|
23191
23809
|
ndJsonStream2(writableAcpStream(stdin), readableAcpStream(stdout))
|
|
23192
23810
|
);
|
|
23193
|
-
const processExit = new Promise((_resolve, reject) => {
|
|
23194
|
-
child.once("error", (error61) => reject(error61));
|
|
23195
|
-
child.once(
|
|
23196
|
-
"exit",
|
|
23197
|
-
() => reject(new Error("ACP adapter exited during initialization"))
|
|
23198
|
-
);
|
|
23199
|
-
});
|
|
23200
23811
|
try {
|
|
23201
23812
|
const initialized = await Promise.race([
|
|
23202
23813
|
connection.agent.request(
|
|
@@ -23281,7 +23892,7 @@ async function discoverSpecification(specification, options) {
|
|
|
23281
23892
|
agentVersion = await probeVersion(
|
|
23282
23893
|
agent,
|
|
23283
23894
|
options.environment,
|
|
23284
|
-
options.
|
|
23895
|
+
options.defaultWorkspace,
|
|
23285
23896
|
options.signal
|
|
23286
23897
|
);
|
|
23287
23898
|
} catch (error61) {
|
|
@@ -23300,7 +23911,7 @@ async function discoverSpecification(specification, options) {
|
|
|
23300
23911
|
...options.environment,
|
|
23301
23912
|
[specification.adapterEnvironmentVariable]: agent.path
|
|
23302
23913
|
},
|
|
23303
|
-
options.
|
|
23914
|
+
options.defaultWorkspace,
|
|
23304
23915
|
options.signal
|
|
23305
23916
|
);
|
|
23306
23917
|
} catch (error61) {
|
|
@@ -23325,7 +23936,7 @@ async function discoverSpecification(specification, options) {
|
|
|
23325
23936
|
await probeAcp(
|
|
23326
23937
|
definition,
|
|
23327
23938
|
options.environment,
|
|
23328
|
-
options.
|
|
23939
|
+
options.defaultWorkspace,
|
|
23329
23940
|
options.signal
|
|
23330
23941
|
);
|
|
23331
23942
|
return new AvailableAgent(specification, definition);
|
|
@@ -23373,7 +23984,7 @@ var TOKEN_PATTERN = /^[a-zA-Z0-9_-]{32,256}$/;
|
|
|
23373
23984
|
var MAX_CONNECTION_FILE_LENGTH = 16 * 1024;
|
|
23374
23985
|
var CLAIM_RESPONSE_KEYS = ["channelId", "daemonToken"];
|
|
23375
23986
|
var SAVED_CONNECTION_KEYS = [
|
|
23376
|
-
"
|
|
23987
|
+
"defaultWorkspace",
|
|
23377
23988
|
"channelId",
|
|
23378
23989
|
"daemonToken",
|
|
23379
23990
|
"relayOrigin"
|
|
@@ -23420,7 +24031,7 @@ function readSingleOption(arguments_, option) {
|
|
|
23420
24031
|
function assertKnownArguments(arguments_) {
|
|
23421
24032
|
for (let index = 0; index < arguments_.length; index += 2) {
|
|
23422
24033
|
const option = arguments_[index];
|
|
23423
|
-
if (option !== "--url" && option !== "--code" && option !== "--
|
|
24034
|
+
if (option !== "--url" && option !== "--code" && option !== "--workspace") {
|
|
23424
24035
|
throw new PairingClientError(
|
|
23425
24036
|
"invalid_arguments",
|
|
23426
24037
|
`Unknown pairing option: ${option ?? "missing option"}`
|
|
@@ -23477,7 +24088,7 @@ function parsePairCommand(arguments_) {
|
|
|
23477
24088
|
return {
|
|
23478
24089
|
code: parsePairingCode(readSingleOption(arguments_, "--code")),
|
|
23479
24090
|
relayOrigin: parseRelayOrigin(readSingleOption(arguments_, "--url")),
|
|
23480
|
-
|
|
24091
|
+
workspace: arguments_.includes("--workspace") ? readSingleOption(arguments_, "--workspace") : loadDefaultWorkspace()
|
|
23481
24092
|
};
|
|
23482
24093
|
}
|
|
23483
24094
|
function parseChannelArgument(arguments_) {
|
|
@@ -23496,25 +24107,26 @@ function parseChannelArgument(arguments_) {
|
|
|
23496
24107
|
}
|
|
23497
24108
|
return channelId;
|
|
23498
24109
|
}
|
|
23499
|
-
async function
|
|
23500
|
-
const
|
|
24110
|
+
async function resolveDefaultWorkspace(path2) {
|
|
24111
|
+
const requestedWorkspace = resolve3(path2);
|
|
23501
24112
|
try {
|
|
23502
|
-
|
|
23503
|
-
const
|
|
24113
|
+
await mkdir2(requestedWorkspace, { recursive: true, mode: 448 });
|
|
24114
|
+
const defaultWorkspace = await realpath3(requestedWorkspace);
|
|
24115
|
+
const metadata = await stat3(defaultWorkspace);
|
|
23504
24116
|
if (!metadata.isDirectory()) {
|
|
23505
24117
|
throw new PairingClientError(
|
|
23506
24118
|
"invalid_workspace",
|
|
23507
|
-
"The
|
|
24119
|
+
"The default workspace is not a directory"
|
|
23508
24120
|
);
|
|
23509
24121
|
}
|
|
23510
|
-
return
|
|
24122
|
+
return defaultWorkspace;
|
|
23511
24123
|
} catch (error61) {
|
|
23512
24124
|
if (error61 instanceof PairingClientError) {
|
|
23513
24125
|
throw error61;
|
|
23514
24126
|
}
|
|
23515
24127
|
throw new PairingClientError(
|
|
23516
24128
|
"invalid_workspace",
|
|
23517
|
-
"The
|
|
24129
|
+
"The default workspace cannot be resolved",
|
|
23518
24130
|
{ cause: error61 }
|
|
23519
24131
|
);
|
|
23520
24132
|
}
|
|
@@ -23570,7 +24182,7 @@ function decodeClaimResponse(value) {
|
|
|
23570
24182
|
}
|
|
23571
24183
|
return { channelId, daemonToken };
|
|
23572
24184
|
}
|
|
23573
|
-
async function claimPairing(options,
|
|
24185
|
+
async function claimPairing(options, defaultWorkspace, localAgents, installableAgents, signal) {
|
|
23574
24186
|
const agents = localAgents.map((agent) => ({
|
|
23575
24187
|
id: agent.id,
|
|
23576
24188
|
label: agent.label
|
|
@@ -23588,7 +24200,7 @@ async function claimPairing(options, approvedRoot, localAgents, installableAgent
|
|
|
23588
24200
|
})),
|
|
23589
24201
|
machineName: hostname3(),
|
|
23590
24202
|
pairingCode: options.code,
|
|
23591
|
-
workspaceName: basename(
|
|
24203
|
+
workspaceName: basename(defaultWorkspace) || "Local workspace"
|
|
23592
24204
|
}),
|
|
23593
24205
|
dispatcher: environmentProxyDispatcher,
|
|
23594
24206
|
headers: { "content-type": "application/json" },
|
|
@@ -23611,11 +24223,11 @@ async function claimPairing(options, approvedRoot, localAgents, installableAgent
|
|
|
23611
24223
|
}
|
|
23612
24224
|
return decodeClaimResponse(value);
|
|
23613
24225
|
}
|
|
23614
|
-
async function persistConnection(channelId, relayOrigin, daemonToken,
|
|
23615
|
-
const connectionsDirectory =
|
|
24226
|
+
async function persistConnection(channelId, relayOrigin, daemonToken, defaultWorkspace) {
|
|
24227
|
+
const connectionsDirectory = join7(readStateDirectory(), "connections");
|
|
23616
24228
|
await mkdir2(connectionsDirectory, { mode: 448, recursive: true });
|
|
23617
|
-
const connectionPath =
|
|
23618
|
-
const temporaryPath =
|
|
24229
|
+
const connectionPath = join7(connectionsDirectory, `${channelId}.json`);
|
|
24230
|
+
const temporaryPath = join7(
|
|
23619
24231
|
connectionsDirectory,
|
|
23620
24232
|
`.${channelId}.${randomUUID()}.tmp`
|
|
23621
24233
|
);
|
|
@@ -23623,7 +24235,7 @@ async function persistConnection(channelId, relayOrigin, daemonToken, approvedRo
|
|
|
23623
24235
|
temporaryPath,
|
|
23624
24236
|
`${JSON.stringify(
|
|
23625
24237
|
{
|
|
23626
|
-
|
|
24238
|
+
defaultWorkspace,
|
|
23627
24239
|
channelId,
|
|
23628
24240
|
daemonToken,
|
|
23629
24241
|
relayOrigin: relayOrigin.toString()
|
|
@@ -23644,25 +24256,25 @@ function decodeSavedConnection(value, expectedChannelId) {
|
|
|
23644
24256
|
"The saved connection is invalid"
|
|
23645
24257
|
);
|
|
23646
24258
|
}
|
|
23647
|
-
const
|
|
24259
|
+
const defaultWorkspace = value["defaultWorkspace"];
|
|
23648
24260
|
const channelId = value["channelId"];
|
|
23649
24261
|
const daemonToken = value["daemonToken"];
|
|
23650
24262
|
const relayOrigin = value["relayOrigin"];
|
|
23651
|
-
if (typeof
|
|
24263
|
+
if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN3.test(channelId) || typeof daemonToken !== "string" || !TOKEN_PATTERN.test(daemonToken) || typeof relayOrigin !== "string") {
|
|
23652
24264
|
throw new PairingClientError(
|
|
23653
24265
|
"invalid_connection",
|
|
23654
24266
|
"The saved connection is invalid"
|
|
23655
24267
|
);
|
|
23656
24268
|
}
|
|
23657
24269
|
return {
|
|
23658
|
-
|
|
24270
|
+
defaultWorkspace,
|
|
23659
24271
|
channelId,
|
|
23660
24272
|
daemonToken,
|
|
23661
24273
|
relayOrigin: parseRelayOrigin(relayOrigin)
|
|
23662
24274
|
};
|
|
23663
24275
|
}
|
|
23664
24276
|
async function readSavedConnection(channelId) {
|
|
23665
|
-
const connectionPath =
|
|
24277
|
+
const connectionPath = join7(
|
|
23666
24278
|
readStateDirectory(),
|
|
23667
24279
|
"connections",
|
|
23668
24280
|
`${channelId}.json`
|
|
@@ -23693,7 +24305,9 @@ async function readSavedConnection(channelId) {
|
|
|
23693
24305
|
}
|
|
23694
24306
|
const connection = decodeSavedConnection(value, channelId);
|
|
23695
24307
|
return {
|
|
23696
|
-
|
|
24308
|
+
defaultWorkspace: await resolveDefaultWorkspace(
|
|
24309
|
+
connection.defaultWorkspace
|
|
24310
|
+
),
|
|
23697
24311
|
channelId: connection.channelId,
|
|
23698
24312
|
daemonToken: connection.daemonToken,
|
|
23699
24313
|
relayOrigin: connection.relayOrigin
|
|
@@ -23715,7 +24329,7 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
|
|
|
23715
24329
|
);
|
|
23716
24330
|
relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
23717
24331
|
relayUrl.searchParams.set("token", connection.daemonToken);
|
|
23718
|
-
const auditDatabasePath =
|
|
24332
|
+
const auditDatabasePath = join7(
|
|
23719
24333
|
readStateDirectory(),
|
|
23720
24334
|
"audit",
|
|
23721
24335
|
`${connection.channelId}.sqlite`
|
|
@@ -23727,7 +24341,7 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
|
|
|
23727
24341
|
).toString(),
|
|
23728
24342
|
config: createDaemonConfig(
|
|
23729
24343
|
{
|
|
23730
|
-
|
|
24344
|
+
defaultWorkspace: connection.defaultWorkspace,
|
|
23731
24345
|
auditDatabasePath,
|
|
23732
24346
|
channelId: connection.channelId,
|
|
23733
24347
|
relayUrl
|
|
@@ -23740,9 +24354,9 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
|
|
|
23740
24354
|
}
|
|
23741
24355
|
async function pairLocalMachine(arguments_, signal = new AbortController().signal) {
|
|
23742
24356
|
const options = parsePairCommand(arguments_);
|
|
23743
|
-
const
|
|
24357
|
+
const defaultWorkspace = await resolveDefaultWorkspace(options.workspace);
|
|
23744
24358
|
const inventory = await discoverLocalAgentInventory({
|
|
23745
|
-
|
|
24359
|
+
defaultWorkspace,
|
|
23746
24360
|
environment: process.env,
|
|
23747
24361
|
signal
|
|
23748
24362
|
});
|
|
@@ -23750,7 +24364,7 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
|
|
|
23750
24364
|
const agents = inventory.readyAgents();
|
|
23751
24365
|
const claim2 = await claimPairing(
|
|
23752
24366
|
options,
|
|
23753
|
-
|
|
24367
|
+
defaultWorkspace,
|
|
23754
24368
|
agents,
|
|
23755
24369
|
inventory.installable,
|
|
23756
24370
|
signal
|
|
@@ -23759,11 +24373,11 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
|
|
|
23759
24373
|
claim2.channelId,
|
|
23760
24374
|
options.relayOrigin,
|
|
23761
24375
|
claim2.daemonToken,
|
|
23762
|
-
|
|
24376
|
+
defaultWorkspace
|
|
23763
24377
|
);
|
|
23764
24378
|
return buildPairedDaemon(
|
|
23765
24379
|
{
|
|
23766
|
-
|
|
24380
|
+
defaultWorkspace,
|
|
23767
24381
|
channelId: claim2.channelId,
|
|
23768
24382
|
daemonToken: claim2.daemonToken,
|
|
23769
24383
|
relayOrigin: options.relayOrigin
|
|
@@ -23774,14 +24388,14 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
|
|
|
23774
24388
|
}
|
|
23775
24389
|
async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
|
|
23776
24390
|
const channelId = parseChannelArgument(arguments_);
|
|
23777
|
-
const connectionPath =
|
|
24391
|
+
const connectionPath = join7(
|
|
23778
24392
|
readStateDirectory(),
|
|
23779
24393
|
"connections",
|
|
23780
24394
|
`${channelId}.json`
|
|
23781
24395
|
);
|
|
23782
24396
|
const connection = await readSavedConnection(channelId);
|
|
23783
24397
|
const inventory = await discoverLocalAgentInventory({
|
|
23784
|
-
|
|
24398
|
+
defaultWorkspace: connection.defaultWorkspace,
|
|
23785
24399
|
environment: process.env,
|
|
23786
24400
|
signal
|
|
23787
24401
|
});
|
|
@@ -23789,7 +24403,7 @@ async function resumeLocalMachine(arguments_, signal = new AbortController().sig
|
|
|
23789
24403
|
}
|
|
23790
24404
|
|
|
23791
24405
|
// src/run-daemon.ts
|
|
23792
|
-
import { randomUUID as
|
|
24406
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
23793
24407
|
import { basename as basename3 } from "node:path";
|
|
23794
24408
|
import { WebSocket as WebSocket2 } from "undici";
|
|
23795
24409
|
|
|
@@ -24238,7 +24852,7 @@ function stableFailureCode(error61) {
|
|
|
24238
24852
|
function discoveryOptions(options, environment, signal, specifications) {
|
|
24239
24853
|
return {
|
|
24240
24854
|
...options.architecture === void 0 ? {} : { architecture: options.architecture },
|
|
24241
|
-
|
|
24855
|
+
defaultWorkspace: options.defaultWorkspace,
|
|
24242
24856
|
environment,
|
|
24243
24857
|
...options.platform === void 0 ? {} : { platform: options.platform },
|
|
24244
24858
|
signal,
|
|
@@ -24394,11 +25008,11 @@ var AutomaticAdapterProvisioner = class {
|
|
|
24394
25008
|
|
|
24395
25009
|
// src/agent-installation/fixed-installation-executor.ts
|
|
24396
25010
|
import { realpathSync as realpathSync2, statSync as statSync2 } from "node:fs";
|
|
24397
|
-
import {
|
|
25011
|
+
import { resolve as resolve4 } from "node:path";
|
|
24398
25012
|
|
|
24399
25013
|
// src/agent-installation/fixed-agent-install-recipes.ts
|
|
24400
25014
|
import { mkdir as mkdir3, mkdtemp as mkdtemp2, rm as rm2 } from "node:fs/promises";
|
|
24401
|
-
import { join as
|
|
25015
|
+
import { join as join8 } from "node:path";
|
|
24402
25016
|
|
|
24403
25017
|
// src/agent-installation/verified-download.ts
|
|
24404
25018
|
import { createHash } from "node:crypto";
|
|
@@ -24605,8 +25219,8 @@ var FixedAgentInstallRecipe = class {
|
|
|
24605
25219
|
);
|
|
24606
25220
|
}
|
|
24607
25221
|
};
|
|
24608
|
-
requireInstalledExecutable = async (
|
|
24609
|
-
const executable = await resolveExecutablePath(
|
|
25222
|
+
requireInstalledExecutable = async (path2) => {
|
|
25223
|
+
const executable = await resolveExecutablePath(path2);
|
|
24610
25224
|
if (executable === null) {
|
|
24611
25225
|
throw new InstallationManagerError(
|
|
24612
25226
|
"agent_verification_failed",
|
|
@@ -24622,18 +25236,18 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
24622
25236
|
releaseChannel = "latest";
|
|
24623
25237
|
install = async (options) => {
|
|
24624
25238
|
this.assertSupportedPlatform();
|
|
24625
|
-
const root =
|
|
24626
|
-
const temporaryRoot =
|
|
25239
|
+
const root = join8(options.stateDirectory, "agents", this.id);
|
|
25240
|
+
const temporaryRoot = join8(options.stateDirectory, "temporary");
|
|
24627
25241
|
await Promise.all([
|
|
24628
25242
|
mkdir3(root, { mode: 448, recursive: true }),
|
|
24629
25243
|
mkdir3(temporaryRoot, { mode: 448, recursive: true })
|
|
24630
25244
|
]);
|
|
24631
25245
|
const temporaryDirectory = await mkdtemp2(
|
|
24632
|
-
|
|
25246
|
+
join8(temporaryRoot, ".codex-install-")
|
|
24633
25247
|
);
|
|
24634
|
-
const installerPath =
|
|
24635
|
-
const installDirectory =
|
|
24636
|
-
const codexHome =
|
|
25248
|
+
const installerPath = join8(temporaryDirectory, "install.sh");
|
|
25249
|
+
const installDirectory = join8(root, "bin");
|
|
25250
|
+
const codexHome = join8(root, "home");
|
|
24637
25251
|
try {
|
|
24638
25252
|
await Promise.all([
|
|
24639
25253
|
mkdir3(installDirectory, { mode: 448, recursive: true }),
|
|
@@ -24677,7 +25291,7 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
24677
25291
|
timeoutMilliseconds: AGENT_INSTALL_TIMEOUT_MILLISECONDS
|
|
24678
25292
|
});
|
|
24679
25293
|
return await this.requireInstalledExecutable(
|
|
24680
|
-
|
|
25294
|
+
join8(installDirectory, "codex")
|
|
24681
25295
|
);
|
|
24682
25296
|
} catch (error61) {
|
|
24683
25297
|
if (options.signal.aborted) {
|
|
@@ -24701,17 +25315,17 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
24701
25315
|
releaseChannel = "stable";
|
|
24702
25316
|
install = async (options) => {
|
|
24703
25317
|
this.assertSupportedPlatform();
|
|
24704
|
-
const root =
|
|
24705
|
-
const managedHome =
|
|
24706
|
-
const temporaryRoot =
|
|
25318
|
+
const root = join8(options.stateDirectory, "agents", this.id);
|
|
25319
|
+
const managedHome = join8(root, "home");
|
|
25320
|
+
const temporaryRoot = join8(options.stateDirectory, "temporary");
|
|
24707
25321
|
await Promise.all([
|
|
24708
25322
|
mkdir3(managedHome, { mode: 448, recursive: true }),
|
|
24709
25323
|
mkdir3(temporaryRoot, { mode: 448, recursive: true })
|
|
24710
25324
|
]);
|
|
24711
25325
|
const temporaryDirectory = await mkdtemp2(
|
|
24712
|
-
|
|
25326
|
+
join8(temporaryRoot, ".claude-install-")
|
|
24713
25327
|
);
|
|
24714
|
-
const installerPath =
|
|
25328
|
+
const installerPath = join8(temporaryDirectory, "install.sh");
|
|
24715
25329
|
try {
|
|
24716
25330
|
await options.report("downloading");
|
|
24717
25331
|
const download = await downloadVerifiedFile({
|
|
@@ -24748,7 +25362,7 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
|
|
|
24748
25362
|
timeoutMilliseconds: AGENT_INSTALL_TIMEOUT_MILLISECONDS
|
|
24749
25363
|
});
|
|
24750
25364
|
return await this.requireInstalledExecutable(
|
|
24751
|
-
|
|
25365
|
+
join8(managedHome, ".local", "bin", "claude")
|
|
24752
25366
|
);
|
|
24753
25367
|
} catch (error61) {
|
|
24754
25368
|
if (options.signal.aborted) {
|
|
@@ -24796,28 +25410,24 @@ function findRecipe(recipes, agentId) {
|
|
|
24796
25410
|
"The requested Agent does not have an approved installation recipe"
|
|
24797
25411
|
);
|
|
24798
25412
|
}
|
|
24799
|
-
function
|
|
24800
|
-
const difference = relative4(resolve5(root), resolve5(candidate));
|
|
24801
|
-
return difference.length === 0 || !difference.startsWith("..") && !isAbsolute3(difference);
|
|
24802
|
-
}
|
|
24803
|
-
function resolveApprovedRoot2(path) {
|
|
25413
|
+
function resolveDefaultWorkspace2(path2) {
|
|
24804
25414
|
try {
|
|
24805
|
-
const directory = realpathSync2(
|
|
25415
|
+
const directory = realpathSync2(resolve4(path2));
|
|
24806
25416
|
if (!statSync2(directory).isDirectory()) {
|
|
24807
|
-
throw new Error("
|
|
25417
|
+
throw new Error("Default workspace is not a directory");
|
|
24808
25418
|
}
|
|
24809
25419
|
return directory;
|
|
24810
25420
|
} catch (error61) {
|
|
24811
25421
|
throw new InstallationManagerError(
|
|
24812
25422
|
"unsafe_installation_root",
|
|
24813
|
-
"The
|
|
25423
|
+
"The default workspace is unavailable or unsafe",
|
|
24814
25424
|
{ cause: error61 }
|
|
24815
25425
|
);
|
|
24816
25426
|
}
|
|
24817
25427
|
}
|
|
24818
|
-
function resolveUserInstallationDirectory(
|
|
25428
|
+
function resolveUserInstallationDirectory(path2) {
|
|
24819
25429
|
try {
|
|
24820
|
-
const directory = realpathSync2(
|
|
25430
|
+
const directory = realpathSync2(resolve4(path2));
|
|
24821
25431
|
const metadata = statSync2(directory);
|
|
24822
25432
|
if (!metadata.isDirectory() || typeof process.getuid === "function" && metadata.uid !== process.getuid()) {
|
|
24823
25433
|
throw new Error("Installation directory is not user-owned");
|
|
@@ -24854,7 +25464,7 @@ function requiresAdapterReplacement(code) {
|
|
|
24854
25464
|
}
|
|
24855
25465
|
var FixedInstallationExecutor = class {
|
|
24856
25466
|
#adapterInstaller;
|
|
24857
|
-
#
|
|
25467
|
+
#defaultWorkspace;
|
|
24858
25468
|
#audit;
|
|
24859
25469
|
#catalog;
|
|
24860
25470
|
#environment;
|
|
@@ -24863,17 +25473,11 @@ var FixedInstallationExecutor = class {
|
|
|
24863
25473
|
#specifications;
|
|
24864
25474
|
#stateDirectory;
|
|
24865
25475
|
constructor(options, specifications = supportedLocalAgentSpecifications(), recipes = fixedAgentInstallRecipes(), adapterInstaller) {
|
|
24866
|
-
const
|
|
25476
|
+
const defaultWorkspace = resolveDefaultWorkspace2(options.defaultWorkspace);
|
|
24867
25477
|
const stateDirectory = resolveUserInstallationDirectory(
|
|
24868
25478
|
options.stateDirectory
|
|
24869
25479
|
);
|
|
24870
|
-
|
|
24871
|
-
throw new InstallationManagerError(
|
|
24872
|
-
"unsafe_installation_root",
|
|
24873
|
-
"The Mobius state directory cannot be inside the approved workspace"
|
|
24874
|
-
);
|
|
24875
|
-
}
|
|
24876
|
-
this.#approvedRoot = approvedRoot;
|
|
25480
|
+
this.#defaultWorkspace = defaultWorkspace;
|
|
24877
25481
|
this.#audit = options.audit;
|
|
24878
25482
|
this.#catalog = options.catalog;
|
|
24879
25483
|
this.#environment = Object.freeze({ ...options.environment });
|
|
@@ -24882,7 +25486,6 @@ var FixedInstallationExecutor = class {
|
|
|
24882
25486
|
this.#specifications = Object.freeze([...specifications]);
|
|
24883
25487
|
this.#stateDirectory = stateDirectory;
|
|
24884
25488
|
this.#adapterInstaller = adapterInstaller ?? new ManagedAdapterInstaller({
|
|
24885
|
-
approvedRoot,
|
|
24886
25489
|
environment: this.#environment,
|
|
24887
25490
|
stateDirectory
|
|
24888
25491
|
});
|
|
@@ -24901,7 +25504,7 @@ var FixedInstallationExecutor = class {
|
|
|
24901
25504
|
}
|
|
24902
25505
|
);
|
|
24903
25506
|
const discoveryOptions2 = {
|
|
24904
|
-
|
|
25507
|
+
defaultWorkspace: this.#defaultWorkspace,
|
|
24905
25508
|
environment: this.#environment,
|
|
24906
25509
|
signal
|
|
24907
25510
|
};
|
|
@@ -24936,7 +25539,7 @@ var FixedInstallationExecutor = class {
|
|
|
24936
25539
|
environment[specification.agentOverrideVariable] = agent.path;
|
|
24937
25540
|
environment[specification.adapterOverrideVariable] = adapter.path;
|
|
24938
25541
|
const inventory = await discoverLocalAgentInventory({
|
|
24939
|
-
|
|
25542
|
+
defaultWorkspace: this.#defaultWorkspace,
|
|
24940
25543
|
environment,
|
|
24941
25544
|
signal,
|
|
24942
25545
|
specifications: this.#specifications
|
|
@@ -25049,18 +25652,16 @@ Mobius Web requested installation of ${label} (${requestId}). Allow this fixed u
|
|
|
25049
25652
|
};
|
|
25050
25653
|
|
|
25051
25654
|
// src/authorization/workspace-directory.ts
|
|
25052
|
-
import { readdir, realpath as
|
|
25655
|
+
import { readdir, realpath as realpath4, stat as stat4 } from "node:fs/promises";
|
|
25656
|
+
import { homedir as homedir2 } from "node:os";
|
|
25053
25657
|
import {
|
|
25054
25658
|
basename as basename2,
|
|
25055
25659
|
dirname as dirname4,
|
|
25056
|
-
isAbsolute as
|
|
25057
|
-
join as
|
|
25660
|
+
isAbsolute as isAbsolute2,
|
|
25661
|
+
join as join9,
|
|
25058
25662
|
normalize,
|
|
25059
|
-
relative as
|
|
25663
|
+
relative as relative2
|
|
25060
25664
|
} from "node:path";
|
|
25061
|
-
var ROOT_DIRECTORY_ID = "root";
|
|
25062
|
-
var DIRECTORY_ID_PREFIX = "dir-";
|
|
25063
|
-
var DIRECTORY_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
25064
25665
|
var WorkspaceDirectoryListing = class {
|
|
25065
25666
|
directoryId;
|
|
25066
25667
|
directoryLabel;
|
|
@@ -25073,151 +25674,691 @@ var WorkspaceDirectoryListing = class {
|
|
|
25073
25674
|
this.parentDirectoryId = parentDirectoryId;
|
|
25074
25675
|
}
|
|
25075
25676
|
};
|
|
25076
|
-
function
|
|
25077
|
-
|
|
25078
|
-
|
|
25677
|
+
function encodePath(path2) {
|
|
25678
|
+
const encoded = Buffer.from(path2, "utf8").toString("base64url");
|
|
25679
|
+
if (encoded.length === 0 || encoded.length > 2e3) {
|
|
25680
|
+
throw new LocalAuthorizationError(
|
|
25681
|
+
"The local directory path is too long to select"
|
|
25682
|
+
);
|
|
25079
25683
|
}
|
|
25080
|
-
return
|
|
25684
|
+
return `path-${encoded}`;
|
|
25081
25685
|
}
|
|
25082
|
-
function
|
|
25083
|
-
|
|
25084
|
-
|
|
25085
|
-
|
|
25086
|
-
if (!value.startsWith(DIRECTORY_ID_PREFIX)) {
|
|
25087
|
-
throw new LocalAuthorizationError("The workspace directory is invalid");
|
|
25088
|
-
}
|
|
25089
|
-
const encoded = value.slice(DIRECTORY_ID_PREFIX.length);
|
|
25090
|
-
if (encoded.length === 0 || encoded.length > 2e3 || !DIRECTORY_ID_PATTERN.test(encoded)) {
|
|
25091
|
-
throw new LocalAuthorizationError("The workspace directory is invalid");
|
|
25092
|
-
}
|
|
25686
|
+
function decodePath(value, prefix) {
|
|
25687
|
+
const encoded = value.slice(prefix.length);
|
|
25688
|
+
if (encoded.length === 0 || encoded.length > 2e3 || !/^[a-zA-Z0-9_-]+$/.test(encoded))
|
|
25689
|
+
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
25093
25690
|
const decoded = Buffer.from(encoded, "base64url").toString("utf8");
|
|
25094
|
-
if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded)
|
|
25095
|
-
throw new LocalAuthorizationError("The
|
|
25096
|
-
|
|
25097
|
-
const normalized = normalize(decoded);
|
|
25098
|
-
if (decoded.includes("\0") || isAbsolute4(decoded) || normalized === ".." || normalized.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || normalized !== decoded) {
|
|
25099
|
-
throw new LocalAuthorizationError("The workspace directory is invalid");
|
|
25100
|
-
}
|
|
25101
|
-
return normalized;
|
|
25102
|
-
}
|
|
25103
|
-
function isContained(root, target) {
|
|
25104
|
-
const remainder = relative5(root, target);
|
|
25105
|
-
return remainder.length === 0 || !isAbsolute4(remainder) && remainder !== ".." && !remainder.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
|
|
25691
|
+
if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded || decoded.includes("\0") || normalize(decoded) !== decoded)
|
|
25692
|
+
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
25693
|
+
return decoded;
|
|
25106
25694
|
}
|
|
25107
25695
|
var WorkspaceDirectoryService = class {
|
|
25108
|
-
#
|
|
25109
|
-
#
|
|
25110
|
-
constructor(
|
|
25111
|
-
this.#
|
|
25112
|
-
this.#
|
|
25113
|
-
}
|
|
25114
|
-
|
|
25115
|
-
if (
|
|
25696
|
+
#bindingId;
|
|
25697
|
+
#defaultWorkspace;
|
|
25698
|
+
constructor(bindingId, defaultWorkspace) {
|
|
25699
|
+
this.#bindingId = bindingId;
|
|
25700
|
+
this.#defaultWorkspace = defaultWorkspace;
|
|
25701
|
+
}
|
|
25702
|
+
authorizeMachine = (bindingId) => {
|
|
25703
|
+
if (bindingId !== this.#bindingId)
|
|
25116
25704
|
throw new LocalAuthorizationError(
|
|
25117
25705
|
"The runtime binding is not authorized"
|
|
25118
25706
|
);
|
|
25707
|
+
};
|
|
25708
|
+
requestedPath = (id) => {
|
|
25709
|
+
if (id === "default") return this.#defaultWorkspace;
|
|
25710
|
+
if (id === "home") return homedir2();
|
|
25711
|
+
if (id.startsWith("path-")) {
|
|
25712
|
+
const path2 = decodePath(id, "path-");
|
|
25713
|
+
if (isAbsolute2(path2)) return path2;
|
|
25119
25714
|
}
|
|
25120
|
-
|
|
25121
|
-
|
|
25715
|
+
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
25716
|
+
};
|
|
25717
|
+
resolvePath = async (requested, signal) => {
|
|
25718
|
+
signal.throwIfAborted();
|
|
25719
|
+
if (!isAbsolute2(requested) || requested.includes("\0") || normalize(requested) !== requested)
|
|
25720
|
+
throw new LocalAuthorizationError("The local directory path is invalid");
|
|
25122
25721
|
try {
|
|
25123
|
-
const target = await
|
|
25124
|
-
|
|
25125
|
-
|
|
25722
|
+
const target = await realpath4(requested);
|
|
25723
|
+
signal.throwIfAborted();
|
|
25724
|
+
const metadata = await stat4(target);
|
|
25725
|
+
signal.throwIfAborted();
|
|
25726
|
+
if (!isAbsolute2(target) || normalize(target) !== target || !metadata.isDirectory())
|
|
25126
25727
|
throw new LocalAuthorizationError(
|
|
25127
|
-
"The
|
|
25728
|
+
"The selected location is not a directory"
|
|
25128
25729
|
);
|
|
25129
|
-
}
|
|
25130
25730
|
return target;
|
|
25131
|
-
} catch (
|
|
25132
|
-
|
|
25133
|
-
|
|
25134
|
-
}
|
|
25731
|
+
} catch (cause) {
|
|
25732
|
+
signal.throwIfAborted();
|
|
25733
|
+
if (cause instanceof LocalAuthorizationError) throw cause;
|
|
25135
25734
|
throw new LocalAuthorizationError(
|
|
25136
|
-
"
|
|
25137
|
-
{ cause
|
|
25735
|
+
"This directory is missing or unavailable to your user account",
|
|
25736
|
+
{ cause }
|
|
25138
25737
|
);
|
|
25139
25738
|
}
|
|
25140
25739
|
};
|
|
25141
|
-
|
|
25142
|
-
const
|
|
25143
|
-
|
|
25144
|
-
|
|
25740
|
+
label = (path2) => {
|
|
25741
|
+
const suffix = relative2(homedir2(), path2);
|
|
25742
|
+
return suffix === "" ? "~" : !isAbsolute2(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
|
|
25743
|
+
};
|
|
25744
|
+
identify = async (bindingId, requestedPath, signal) => {
|
|
25745
|
+
this.authorizeMachine(bindingId);
|
|
25746
|
+
const path2 = await this.resolvePath(requestedPath, signal);
|
|
25747
|
+
return {
|
|
25748
|
+
path: path2,
|
|
25749
|
+
directoryId: encodePath(path2),
|
|
25750
|
+
name: basename2(path2) || path2,
|
|
25751
|
+
label: this.label(path2)
|
|
25752
|
+
};
|
|
25753
|
+
};
|
|
25754
|
+
resolve = async (bindingId, id, signal) => {
|
|
25755
|
+
this.authorizeMachine(bindingId);
|
|
25756
|
+
return await this.resolvePath(this.requestedPath(id), signal);
|
|
25757
|
+
};
|
|
25758
|
+
list = async (bindingId, id, signal) => {
|
|
25759
|
+
const target = await this.resolve(bindingId, id, signal);
|
|
25145
25760
|
try {
|
|
25146
|
-
const entries = [];
|
|
25147
25761
|
const children = await readdir(target, { withFileTypes: true });
|
|
25762
|
+
signal.throwIfAborted();
|
|
25763
|
+
const entries = [];
|
|
25148
25764
|
for (const child of children) {
|
|
25149
|
-
if (entries.length === 500)
|
|
25150
|
-
|
|
25765
|
+
if (entries.length === 500) break;
|
|
25766
|
+
if (child.isFile() || child.isBlockDevice() || child.isCharacterDevice() || child.isFIFO() || child.isSocket()) {
|
|
25767
|
+
continue;
|
|
25151
25768
|
}
|
|
25152
|
-
const logicalChild = logicalRelativePath.length === 0 ? child.name : join8(logicalRelativePath, child.name);
|
|
25153
25769
|
try {
|
|
25154
|
-
const
|
|
25155
|
-
|
|
25156
|
-
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
name: child.name
|
|
25160
|
-
});
|
|
25161
|
-
}
|
|
25162
|
-
} catch {
|
|
25770
|
+
const path2 = await this.resolvePath(join9(target, child.name), signal);
|
|
25771
|
+
entries.push({ directoryId: encodePath(path2), name: child.name });
|
|
25772
|
+
} catch (cause) {
|
|
25773
|
+
signal.throwIfAborted();
|
|
25774
|
+
if (!(cause instanceof LocalAuthorizationError)) throw cause;
|
|
25163
25775
|
}
|
|
25164
25776
|
}
|
|
25165
|
-
entries.sort((
|
|
25166
|
-
const
|
|
25167
|
-
const parentRelative = logicalRelativePath.length === 0 || dirname4(logicalRelativePath) === "." ? "" : dirname4(logicalRelativePath);
|
|
25777
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
25778
|
+
const parent = dirname4(target);
|
|
25168
25779
|
return new WorkspaceDirectoryListing(
|
|
25169
|
-
|
|
25170
|
-
|
|
25780
|
+
id,
|
|
25781
|
+
this.label(target),
|
|
25171
25782
|
entries,
|
|
25172
|
-
|
|
25783
|
+
parent === target ? "" : encodePath(parent)
|
|
25173
25784
|
);
|
|
25174
|
-
} catch (
|
|
25175
|
-
|
|
25176
|
-
|
|
25177
|
-
}
|
|
25785
|
+
} catch (cause) {
|
|
25786
|
+
signal.throwIfAborted();
|
|
25787
|
+
if (cause instanceof LocalAuthorizationError) throw cause;
|
|
25178
25788
|
throw new LocalAuthorizationError(
|
|
25179
|
-
"
|
|
25180
|
-
{ cause
|
|
25789
|
+
"This directory cannot be browsed by your user account",
|
|
25790
|
+
{ cause }
|
|
25181
25791
|
);
|
|
25182
25792
|
}
|
|
25183
25793
|
};
|
|
25184
|
-
resolveMany = async (
|
|
25185
|
-
const
|
|
25186
|
-
const
|
|
25187
|
-
|
|
25188
|
-
|
|
25189
|
-
|
|
25190
|
-
|
|
25794
|
+
resolveMany = async (bindingId, ids, signal) => {
|
|
25795
|
+
const paths = /* @__PURE__ */ new Set();
|
|
25796
|
+
for (const id of ids) paths.add(await this.resolve(bindingId, id, signal));
|
|
25797
|
+
return [...paths];
|
|
25798
|
+
};
|
|
25799
|
+
};
|
|
25800
|
+
|
|
25801
|
+
// src/native-sessions/service.ts
|
|
25802
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
25803
|
+
|
|
25804
|
+
// src/native-sessions/codex-history.ts
|
|
25805
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
25806
|
+
var responseSchema = external_exports.union([
|
|
25807
|
+
external_exports.object({
|
|
25808
|
+
id: external_exports.number(),
|
|
25809
|
+
error: external_exports.object({ code: external_exports.number(), message: external_exports.string() })
|
|
25810
|
+
}),
|
|
25811
|
+
external_exports.object({ id: external_exports.number(), result: external_exports.unknown() }),
|
|
25812
|
+
external_exports.object({ method: external_exports.string() })
|
|
25813
|
+
]);
|
|
25814
|
+
var threadSchema = external_exports.object({
|
|
25815
|
+
thread: external_exports.object({
|
|
25816
|
+
id: nativeSessionIdSchema,
|
|
25817
|
+
cwd: external_exports.string().min(1).max(2048).refine((path2) => !path2.includes("\0"))
|
|
25818
|
+
})
|
|
25819
|
+
});
|
|
25820
|
+
var turnsSchema = external_exports.object({
|
|
25821
|
+
data: external_exports.array(external_exports.object({ items: external_exports.array(external_exports.unknown()).max(1e5) })).max(SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT),
|
|
25822
|
+
nextCursor: external_exports.string().nullish()
|
|
25823
|
+
});
|
|
25824
|
+
var itemTypeSchema = external_exports.object({ type: external_exports.string().min(1).max(80) });
|
|
25825
|
+
var userMessageSchema = external_exports.object({
|
|
25826
|
+
content: external_exports.array(external_exports.unknown()).max(1e4)
|
|
25827
|
+
});
|
|
25828
|
+
var textSchema = external_exports.object({ text: external_exports.string() });
|
|
25829
|
+
var imageSchema = external_exports.object({ url: external_exports.string() });
|
|
25830
|
+
var agentMessageSchema = textSchema.extend({ phase: external_exports.string().nullish() });
|
|
25831
|
+
function decodeHistory(thread, value) {
|
|
25832
|
+
const page = turnsSchema.parse(value);
|
|
25833
|
+
const messages = [];
|
|
25834
|
+
let characters = 0;
|
|
25835
|
+
let imageCharacters = 0;
|
|
25836
|
+
let truncated = page.nextCursor != null;
|
|
25837
|
+
for (const turn of page.data.toReversed()) {
|
|
25838
|
+
for (const item of turn.items) {
|
|
25839
|
+
const { type } = itemTypeSchema.parse(item);
|
|
25840
|
+
let message;
|
|
25841
|
+
if (type === "userMessage") {
|
|
25842
|
+
const { content } = userMessageSchema.parse(item);
|
|
25843
|
+
const parsed = parseNativeHistoryText(
|
|
25844
|
+
content.map((block) => {
|
|
25845
|
+
const { type: type2 } = itemTypeSchema.parse(block);
|
|
25846
|
+
if (type2 === "text") return textSchema.parse(block).text;
|
|
25847
|
+
if (type2 === "image")
|
|
25848
|
+
return `[@image](${imageSchema.parse(block).url})`;
|
|
25849
|
+
return "[Attachment]";
|
|
25850
|
+
}).join("")
|
|
25851
|
+
);
|
|
25852
|
+
message = {
|
|
25853
|
+
role: "user",
|
|
25854
|
+
text: parsed.text,
|
|
25855
|
+
...parsed.images.length > 0 ? { images: parsed.images } : {}
|
|
25856
|
+
};
|
|
25857
|
+
} else if (type === "agentMessage") {
|
|
25858
|
+
const agent = agentMessageSchema.parse(item);
|
|
25859
|
+
if (agent.phase === "commentary") continue;
|
|
25860
|
+
message = { role: "assistant", text: agent.text };
|
|
25861
|
+
} else {
|
|
25862
|
+
continue;
|
|
25863
|
+
}
|
|
25864
|
+
const text = message.text.slice(-SESSION_HISTORY_CHARACTER_LIMIT);
|
|
25865
|
+
if (text.length === 0 && (message.images?.length ?? 0) === 0) continue;
|
|
25866
|
+
truncated ||= text.length < message.text.length;
|
|
25867
|
+
messages.push({ ...message, text });
|
|
25868
|
+
characters += text.length;
|
|
25869
|
+
imageCharacters += (message.images ?? []).reduce(
|
|
25870
|
+
(size, image) => size + image.data.length,
|
|
25871
|
+
0
|
|
25191
25872
|
);
|
|
25192
|
-
|
|
25193
|
-
|
|
25194
|
-
|
|
25873
|
+
while (messages.length > SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT || characters > SESSION_HISTORY_CHARACTER_LIMIT || imageCharacters > SESSION_HISTORY_IMAGE_CHARACTER_LIMIT) {
|
|
25874
|
+
const removed = messages.shift();
|
|
25875
|
+
if (removed !== void 0) {
|
|
25876
|
+
characters -= removed.text.length;
|
|
25877
|
+
imageCharacters -= (removed.images ?? []).reduce(
|
|
25878
|
+
(size, image) => size + image.data.length,
|
|
25879
|
+
0
|
|
25880
|
+
);
|
|
25881
|
+
}
|
|
25882
|
+
truncated = true;
|
|
25195
25883
|
}
|
|
25196
25884
|
}
|
|
25197
|
-
|
|
25885
|
+
}
|
|
25886
|
+
return {
|
|
25887
|
+
nativeSessionId: thread.id,
|
|
25888
|
+
cwd: thread.cwd,
|
|
25889
|
+
history: {
|
|
25890
|
+
status: "available",
|
|
25891
|
+
messages,
|
|
25892
|
+
truncated,
|
|
25893
|
+
coveredCommandIds: []
|
|
25894
|
+
}
|
|
25895
|
+
};
|
|
25896
|
+
}
|
|
25897
|
+
async function readCodexHistory(agent, cwd, nativeSessionId, signal) {
|
|
25898
|
+
signal.throwIfAborted();
|
|
25899
|
+
nativeSessionIdSchema.parse(nativeSessionId);
|
|
25900
|
+
if (agent.id !== "codex" || !await revalidateExecutable(agent.agentIdentity))
|
|
25901
|
+
throw new AcpExecutionError(
|
|
25902
|
+
"The local Codex executable changed. Restart Mobius to discover it again.",
|
|
25903
|
+
{},
|
|
25904
|
+
"agent_executable_changed"
|
|
25905
|
+
);
|
|
25906
|
+
signal.throwIfAborted();
|
|
25907
|
+
const child = spawn4(agent.agentIdentity.realPath, ["app-server"], {
|
|
25908
|
+
cwd,
|
|
25909
|
+
env: { ...process.env, ...agent.environment },
|
|
25910
|
+
detached: process.platform !== "win32",
|
|
25911
|
+
shell: false,
|
|
25912
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
25913
|
+
});
|
|
25914
|
+
const response = Promise.withResolvers();
|
|
25915
|
+
let state = { kind: "initializing", requestId: 1 };
|
|
25916
|
+
let pending = "";
|
|
25917
|
+
let receivedBytes = 0;
|
|
25918
|
+
const fail = (cause) => response.reject(
|
|
25919
|
+
cause instanceof AcpExecutionError ? cause : new AcpExecutionError(
|
|
25920
|
+
"Saved Codex history could not be read.",
|
|
25921
|
+
{ cause },
|
|
25922
|
+
"native_history_unavailable"
|
|
25923
|
+
)
|
|
25924
|
+
);
|
|
25925
|
+
const send2 = (message) => {
|
|
25926
|
+
child.stdin.write(`${JSON.stringify(message)}
|
|
25927
|
+
`);
|
|
25198
25928
|
};
|
|
25199
|
-
|
|
25929
|
+
const onData = (chunk) => {
|
|
25200
25930
|
try {
|
|
25201
|
-
|
|
25202
|
-
|
|
25203
|
-
|
|
25204
|
-
|
|
25205
|
-
|
|
25931
|
+
receivedBytes += Buffer.byteLength(chunk);
|
|
25932
|
+
if (receivedBytes > 32 * 1024 * 1024)
|
|
25933
|
+
throw new AcpExecutionError(
|
|
25934
|
+
"This saved conversation is too large to preview.",
|
|
25935
|
+
{},
|
|
25936
|
+
"native_history_too_large"
|
|
25206
25937
|
);
|
|
25938
|
+
pending += chunk;
|
|
25939
|
+
let newline = pending.indexOf("\n");
|
|
25940
|
+
while (newline >= 0) {
|
|
25941
|
+
const line = pending.slice(0, newline);
|
|
25942
|
+
pending = pending.slice(newline + 1);
|
|
25943
|
+
newline = pending.indexOf("\n");
|
|
25944
|
+
if (line.trim().length === 0) continue;
|
|
25945
|
+
const value = JSON.parse(line);
|
|
25946
|
+
const message = responseSchema.parse(value);
|
|
25947
|
+
if (!("id" in message)) continue;
|
|
25948
|
+
if (message.id !== state.requestId)
|
|
25949
|
+
throw new AcpExecutionError(
|
|
25950
|
+
"Codex returned an invalid history response.",
|
|
25951
|
+
{},
|
|
25952
|
+
"invalid_native_session_response"
|
|
25953
|
+
);
|
|
25954
|
+
if ("error" in message) {
|
|
25955
|
+
fail(
|
|
25956
|
+
new AcpExecutionError(
|
|
25957
|
+
"Saved Codex history could not be read.",
|
|
25958
|
+
{ cause: message.error },
|
|
25959
|
+
"native_history_unavailable"
|
|
25960
|
+
)
|
|
25961
|
+
);
|
|
25962
|
+
return;
|
|
25963
|
+
}
|
|
25964
|
+
switch (state.kind) {
|
|
25965
|
+
case "initializing":
|
|
25966
|
+
state = { kind: "metadata", requestId: 2 };
|
|
25967
|
+
send2({ method: "initialized", params: {} });
|
|
25968
|
+
send2({
|
|
25969
|
+
id: state.requestId,
|
|
25970
|
+
method: "thread/read",
|
|
25971
|
+
params: { threadId: nativeSessionId, includeTurns: false }
|
|
25972
|
+
});
|
|
25973
|
+
break;
|
|
25974
|
+
case "metadata": {
|
|
25975
|
+
const { thread } = threadSchema.parse(message.result);
|
|
25976
|
+
state = { kind: "turns", requestId: 3, thread };
|
|
25977
|
+
send2({
|
|
25978
|
+
id: state.requestId,
|
|
25979
|
+
method: "thread/turns/list",
|
|
25980
|
+
params: {
|
|
25981
|
+
threadId: nativeSessionId,
|
|
25982
|
+
limit: SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT,
|
|
25983
|
+
sortDirection: "desc",
|
|
25984
|
+
itemsView: "summary"
|
|
25985
|
+
}
|
|
25986
|
+
});
|
|
25987
|
+
break;
|
|
25988
|
+
}
|
|
25989
|
+
case "turns":
|
|
25990
|
+
response.resolve(decodeHistory(state.thread, message.result));
|
|
25991
|
+
return;
|
|
25992
|
+
default: {
|
|
25993
|
+
const unhandled = state;
|
|
25994
|
+
throw new Error(`Unknown history read state: ${String(unhandled)}`);
|
|
25995
|
+
}
|
|
25996
|
+
}
|
|
25207
25997
|
}
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
|
|
25998
|
+
} catch (cause) {
|
|
25999
|
+
fail(cause);
|
|
26000
|
+
}
|
|
26001
|
+
};
|
|
26002
|
+
const abort = () => response.reject(signal.reason);
|
|
26003
|
+
const onExit = () => fail(
|
|
26004
|
+
new AcpExecutionError(
|
|
26005
|
+
"Codex stopped before its saved history was available.",
|
|
26006
|
+
{},
|
|
26007
|
+
"native_history_unavailable"
|
|
26008
|
+
)
|
|
26009
|
+
);
|
|
26010
|
+
const timeout = setTimeout(
|
|
26011
|
+
() => fail(
|
|
26012
|
+
new AcpExecutionError(
|
|
26013
|
+
"Reading saved Codex history timed out.",
|
|
26014
|
+
{},
|
|
26015
|
+
"native_history_timeout"
|
|
26016
|
+
)
|
|
26017
|
+
),
|
|
26018
|
+
15e3
|
|
26019
|
+
);
|
|
26020
|
+
child.stdout.setEncoding("utf8");
|
|
26021
|
+
child.stdout.on("data", onData);
|
|
26022
|
+
child.stdin.on("error", fail);
|
|
26023
|
+
child.on("error", fail);
|
|
26024
|
+
child.on("exit", onExit);
|
|
26025
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
26026
|
+
try {
|
|
26027
|
+
if (signal.aborted) abort();
|
|
26028
|
+
else
|
|
26029
|
+
send2({
|
|
26030
|
+
id: 1,
|
|
26031
|
+
method: "initialize",
|
|
26032
|
+
params: { clientInfo: { name: "mobius_history", version: "1.0.0" } }
|
|
26033
|
+
});
|
|
26034
|
+
return await response.promise;
|
|
26035
|
+
} finally {
|
|
26036
|
+
clearTimeout(timeout);
|
|
26037
|
+
signal.removeEventListener("abort", abort);
|
|
26038
|
+
child.stdout.removeListener("data", onData);
|
|
26039
|
+
if (child.pid !== void 0) await stopProcessTree(child);
|
|
26040
|
+
child.stdin.removeListener("error", fail);
|
|
26041
|
+
child.removeListener("error", fail);
|
|
26042
|
+
child.removeListener("exit", onExit);
|
|
26043
|
+
}
|
|
26044
|
+
}
|
|
26045
|
+
|
|
26046
|
+
// src/native-sessions/service.ts
|
|
26047
|
+
var NativeSessionService = class {
|
|
26048
|
+
#entries = /* @__PURE__ */ new Map();
|
|
26049
|
+
#cursors = /* @__PURE__ */ new Map();
|
|
26050
|
+
#config;
|
|
26051
|
+
#directories;
|
|
26052
|
+
#sessions;
|
|
26053
|
+
#audit;
|
|
26054
|
+
constructor(config2, directories, sessions, audit) {
|
|
26055
|
+
this.#config = config2;
|
|
26056
|
+
this.#directories = directories;
|
|
26057
|
+
this.#sessions = sessions;
|
|
26058
|
+
this.#audit = audit;
|
|
26059
|
+
}
|
|
26060
|
+
execute = async (command, signal) => {
|
|
26061
|
+
signal.throwIfAborted();
|
|
26062
|
+
const cwd = await this.#directories.resolve(
|
|
26063
|
+
command.bindingId,
|
|
26064
|
+
command.directoryId,
|
|
26065
|
+
signal
|
|
26066
|
+
);
|
|
26067
|
+
const additionalDirectories = await this.#directories.resolveMany(
|
|
26068
|
+
command.bindingId,
|
|
26069
|
+
command.additionalDirectoryIds,
|
|
26070
|
+
signal
|
|
26071
|
+
);
|
|
26072
|
+
signal.throwIfAborted();
|
|
26073
|
+
const options = {
|
|
26074
|
+
additionalDirectories,
|
|
26075
|
+
agent: this.#config.catalog.find(command.agentId),
|
|
26076
|
+
cwd,
|
|
26077
|
+
sessionId: command.sessionId,
|
|
26078
|
+
signal
|
|
26079
|
+
};
|
|
26080
|
+
const prepared = await this.#sessions.prepareSession(options);
|
|
26081
|
+
if (!prepared.capabilities.listSessions)
|
|
26082
|
+
throw new AcpExecutionError(
|
|
26083
|
+
"This Agent does not support native session listing",
|
|
26084
|
+
{},
|
|
26085
|
+
"native_session_unsupported"
|
|
26086
|
+
);
|
|
26087
|
+
const scope = JSON.stringify([
|
|
26088
|
+
command.agentId,
|
|
26089
|
+
command.bindingId,
|
|
26090
|
+
command.sessionId,
|
|
26091
|
+
cwd
|
|
26092
|
+
]);
|
|
26093
|
+
const envelope2 = {
|
|
26094
|
+
kind: "native.session.result",
|
|
26095
|
+
protocolVersion: 1,
|
|
26096
|
+
requestId: command.requestId,
|
|
26097
|
+
sessionId: command.sessionId
|
|
26098
|
+
};
|
|
26099
|
+
if (command.action === "list") {
|
|
26100
|
+
let cursor = null;
|
|
26101
|
+
if (command.cursor !== null) {
|
|
26102
|
+
const known = this.#cursors.get(command.cursor);
|
|
26103
|
+
if (known?.scope !== scope)
|
|
26104
|
+
throw new AcpExecutionError(
|
|
26105
|
+
"Refresh the native session list before continuing",
|
|
26106
|
+
{},
|
|
26107
|
+
"native_session_cursor_expired"
|
|
26108
|
+
);
|
|
26109
|
+
cursor = known.cursor;
|
|
25212
26110
|
}
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
|
|
25216
|
-
|
|
25217
|
-
|
|
26111
|
+
const page = await this.#sessions.listNativeSessions(options, cursor);
|
|
26112
|
+
const entries = [];
|
|
26113
|
+
for (const info of page.sessions) {
|
|
26114
|
+
const authorized = await this.authorizeEntry(command, options, info);
|
|
26115
|
+
if (authorized === null) continue;
|
|
26116
|
+
const entryId = randomUUID3();
|
|
26117
|
+
if (this.#entries.size >= 5e3) this.#entries.clear();
|
|
26118
|
+
this.#entries.set(entryId, {
|
|
26119
|
+
scope,
|
|
26120
|
+
nativeSessionId: info.sessionId,
|
|
26121
|
+
cwd: authorized.cwd,
|
|
26122
|
+
additionalDirectories: authorized.additionalDirectories
|
|
26123
|
+
});
|
|
26124
|
+
entries.push({
|
|
26125
|
+
entryId,
|
|
26126
|
+
title: info.title ?? "Untitled session",
|
|
26127
|
+
directoryId: authorized.directoryId,
|
|
26128
|
+
directoryLabel: authorized.directoryLabel,
|
|
26129
|
+
updatedAt: info.updatedAt ?? null,
|
|
26130
|
+
additionalDirectories: authorized.additionalEntries
|
|
26131
|
+
});
|
|
26132
|
+
}
|
|
26133
|
+
let nextCursor = null;
|
|
26134
|
+
if (page.nextCursor != null) {
|
|
26135
|
+
if (page.nextCursor === cursor)
|
|
26136
|
+
throw new AcpExecutionError(
|
|
26137
|
+
"The Agent returned a repeated session cursor",
|
|
26138
|
+
{},
|
|
26139
|
+
"invalid_native_session_response"
|
|
26140
|
+
);
|
|
26141
|
+
nextCursor = randomUUID3();
|
|
26142
|
+
if (this.#cursors.size >= 500) this.#cursors.clear();
|
|
26143
|
+
this.#cursors.set(nextCursor, { scope, cursor: page.nextCursor });
|
|
26144
|
+
}
|
|
26145
|
+
this.record(command, "native_sessions_listed");
|
|
26146
|
+
return {
|
|
26147
|
+
...envelope2,
|
|
26148
|
+
action: "list",
|
|
26149
|
+
capabilities: prepared.capabilities,
|
|
26150
|
+
entries,
|
|
26151
|
+
nextCursor
|
|
26152
|
+
};
|
|
26153
|
+
}
|
|
26154
|
+
const selected = this.#entries.get(command.entryId);
|
|
26155
|
+
if (selected?.scope !== scope)
|
|
26156
|
+
throw new AcpExecutionError(
|
|
26157
|
+
"Select a session from a fresh native session list",
|
|
26158
|
+
{},
|
|
26159
|
+
"native_session_not_found"
|
|
26160
|
+
);
|
|
26161
|
+
const target = await this.resolveTarget(command, options, selected);
|
|
26162
|
+
if (command.action === "delete") {
|
|
26163
|
+
if (!prepared.capabilities.deleteSession)
|
|
26164
|
+
throw new AcpExecutionError(
|
|
26165
|
+
"This Agent does not support native session deletion",
|
|
26166
|
+
{},
|
|
26167
|
+
"native_session_unsupported"
|
|
26168
|
+
);
|
|
26169
|
+
signal.throwIfAborted();
|
|
26170
|
+
this.record(command, "native_session_delete_authorized");
|
|
26171
|
+
const deletedSessionIds = await this.#sessions.deleteNativeSession(
|
|
26172
|
+
options,
|
|
26173
|
+
target.info.sessionId
|
|
25218
26174
|
);
|
|
26175
|
+
this.record(command, "native_session_deleted");
|
|
26176
|
+
return {
|
|
26177
|
+
...envelope2,
|
|
26178
|
+
action: "delete",
|
|
26179
|
+
entryId: command.entryId,
|
|
26180
|
+
deletedSessionIds
|
|
26181
|
+
};
|
|
26182
|
+
}
|
|
26183
|
+
if (command.action === "import" && !prepared.capabilities.loadSession && !prepared.capabilities.resumeSession)
|
|
26184
|
+
throw new AcpExecutionError(
|
|
26185
|
+
"This Agent cannot import native sessions",
|
|
26186
|
+
{},
|
|
26187
|
+
"native_session_unsupported"
|
|
26188
|
+
);
|
|
26189
|
+
if (command.action === "fork" && !prepared.capabilities.unstableForkSession)
|
|
26190
|
+
throw new AcpExecutionError(
|
|
26191
|
+
"This Agent does not support session forking",
|
|
26192
|
+
{},
|
|
26193
|
+
"native_session_unsupported"
|
|
26194
|
+
);
|
|
26195
|
+
if (target.additionalDirectories.length > 0 && !prepared.capabilities.additionalDirectories)
|
|
26196
|
+
throw new AcpExecutionError(
|
|
26197
|
+
"This Agent cannot restore additional working directories",
|
|
26198
|
+
{},
|
|
26199
|
+
"native_session_unsupported"
|
|
26200
|
+
);
|
|
26201
|
+
const opening = this.#sessions.openNativeSession(
|
|
26202
|
+
{
|
|
26203
|
+
...options,
|
|
26204
|
+
cwd: target.cwd,
|
|
26205
|
+
sessionId: command.targetSessionId,
|
|
26206
|
+
additionalDirectories: target.additionalDirectories
|
|
26207
|
+
},
|
|
26208
|
+
{ kind: command.action, nativeSessionId: target.info.sessionId }
|
|
26209
|
+
);
|
|
26210
|
+
const opened = await opening.catch(async (cause) => {
|
|
26211
|
+
signal.throwIfAborted();
|
|
26212
|
+
if (command.action !== "import" || options.agent.id !== "codex" || !(cause instanceof AcpExecutionError) || cause.code !== "acp_execution_failed")
|
|
26213
|
+
throw cause;
|
|
26214
|
+
const snapshot = await readCodexHistory(
|
|
26215
|
+
options.agent,
|
|
26216
|
+
target.cwd,
|
|
26217
|
+
target.info.sessionId,
|
|
26218
|
+
signal
|
|
26219
|
+
);
|
|
26220
|
+
const resolved = await this.#directories.identify(
|
|
26221
|
+
command.bindingId,
|
|
26222
|
+
snapshot.cwd,
|
|
26223
|
+
signal
|
|
26224
|
+
);
|
|
26225
|
+
if (snapshot.nativeSessionId !== target.info.sessionId || resolved.path !== target.cwd)
|
|
26226
|
+
throw new AcpExecutionError(
|
|
26227
|
+
"Codex returned history for a different session or workspace.",
|
|
26228
|
+
{ cause },
|
|
26229
|
+
"invalid_native_session_response"
|
|
26230
|
+
);
|
|
26231
|
+
signal.throwIfAborted();
|
|
26232
|
+
this.record(command, "native_session_previewed");
|
|
26233
|
+
return { kind: "preview", history: snapshot.history };
|
|
26234
|
+
});
|
|
26235
|
+
if ("kind" in opened)
|
|
26236
|
+
return {
|
|
26237
|
+
...envelope2,
|
|
26238
|
+
action: "preview",
|
|
26239
|
+
entryId: command.entryId,
|
|
26240
|
+
reason: "continuation_unavailable",
|
|
26241
|
+
title: target.info.title ?? "Untitled session",
|
|
26242
|
+
history: opened.history
|
|
26243
|
+
};
|
|
26244
|
+
signal.throwIfAborted();
|
|
26245
|
+
const ready = {
|
|
26246
|
+
additionalDirectories: target.additionalEntries,
|
|
26247
|
+
agentId: command.agentId,
|
|
26248
|
+
agentName: opened.prepared.agentName,
|
|
26249
|
+
agentVersion: opened.prepared.agentVersion,
|
|
26250
|
+
capabilities: opened.prepared.capabilities,
|
|
26251
|
+
commands: opened.prepared.commands,
|
|
26252
|
+
configOptions: opened.prepared.configOptions,
|
|
26253
|
+
directoryId: target.directoryId,
|
|
26254
|
+
directoryLabel: target.directoryLabel,
|
|
26255
|
+
kind: "session.ready",
|
|
26256
|
+
protocolVersion: 1,
|
|
26257
|
+
requestId: command.requestId,
|
|
26258
|
+
restored: true,
|
|
26259
|
+
sessionId: opened.sessionId
|
|
26260
|
+
};
|
|
26261
|
+
this.record(
|
|
26262
|
+
command,
|
|
26263
|
+
command.action === "fork" ? "native_session_forked" : "native_session_imported"
|
|
26264
|
+
);
|
|
26265
|
+
return {
|
|
26266
|
+
...envelope2,
|
|
26267
|
+
action: command.action,
|
|
26268
|
+
ready,
|
|
26269
|
+
title: target.info.title ?? "Untitled session",
|
|
26270
|
+
history: opened.history
|
|
26271
|
+
};
|
|
26272
|
+
};
|
|
26273
|
+
authorizeEntry = async (command, options, info) => {
|
|
26274
|
+
try {
|
|
26275
|
+
const primary = await this.#directories.identify(
|
|
26276
|
+
command.bindingId,
|
|
26277
|
+
info.cwd,
|
|
26278
|
+
options.signal
|
|
26279
|
+
);
|
|
26280
|
+
const additionalDirectories = [];
|
|
26281
|
+
const additionalEntries = [];
|
|
26282
|
+
const paths = /* @__PURE__ */ new Set([primary.path]);
|
|
26283
|
+
for (const path2 of info.additionalDirectories) {
|
|
26284
|
+
const directory = await this.#directories.identify(
|
|
26285
|
+
command.bindingId,
|
|
26286
|
+
path2,
|
|
26287
|
+
options.signal
|
|
26288
|
+
);
|
|
26289
|
+
if (paths.has(directory.path))
|
|
26290
|
+
throw new LocalAuthorizationError(
|
|
26291
|
+
"Native session roots must be distinct"
|
|
26292
|
+
);
|
|
26293
|
+
paths.add(directory.path);
|
|
26294
|
+
additionalDirectories.push(directory.path);
|
|
26295
|
+
additionalEntries.push({
|
|
26296
|
+
directoryId: directory.directoryId,
|
|
26297
|
+
name: directory.name
|
|
26298
|
+
});
|
|
26299
|
+
}
|
|
26300
|
+
return {
|
|
26301
|
+
info,
|
|
26302
|
+
cwd: primary.path,
|
|
26303
|
+
directoryId: primary.directoryId,
|
|
26304
|
+
directoryLabel: primary.label,
|
|
26305
|
+
additionalDirectories,
|
|
26306
|
+
additionalEntries
|
|
26307
|
+
};
|
|
26308
|
+
} catch (error61) {
|
|
26309
|
+
options.signal.throwIfAborted();
|
|
26310
|
+
if (error61 instanceof LocalAuthorizationError) return null;
|
|
26311
|
+
throw error61;
|
|
25219
26312
|
}
|
|
25220
26313
|
};
|
|
26314
|
+
resolveTarget = async (command, options, selected) => {
|
|
26315
|
+
let cursor = null;
|
|
26316
|
+
const visited = /* @__PURE__ */ new Set();
|
|
26317
|
+
for (let index = 0; index < 100; index += 1) {
|
|
26318
|
+
const page = await this.#sessions.listNativeSessions(options, cursor);
|
|
26319
|
+
const info = page.sessions.find(
|
|
26320
|
+
(entry) => entry.sessionId === selected.nativeSessionId
|
|
26321
|
+
);
|
|
26322
|
+
if (info !== void 0) {
|
|
26323
|
+
const target = await this.authorizeEntry(command, options, info);
|
|
26324
|
+
if (target === null)
|
|
26325
|
+
throw new LocalAuthorizationError(
|
|
26326
|
+
"The selected native session is not authorized"
|
|
26327
|
+
);
|
|
26328
|
+
if (target.cwd !== selected.cwd || target.additionalDirectories.length !== selected.additionalDirectories.length || target.additionalDirectories.some(
|
|
26329
|
+
(path2, index2) => path2 !== selected.additionalDirectories[index2]
|
|
26330
|
+
))
|
|
26331
|
+
throw new AcpExecutionError(
|
|
26332
|
+
"This session's directory changed. Refresh the list before continuing.",
|
|
26333
|
+
{},
|
|
26334
|
+
"native_session_target_changed"
|
|
26335
|
+
);
|
|
26336
|
+
return target;
|
|
26337
|
+
}
|
|
26338
|
+
cursor = page.nextCursor ?? null;
|
|
26339
|
+
if (cursor === null) break;
|
|
26340
|
+
if (visited.has(cursor))
|
|
26341
|
+
throw new AcpExecutionError(
|
|
26342
|
+
"The Agent returned a repeated session cursor",
|
|
26343
|
+
{},
|
|
26344
|
+
"invalid_native_session_response"
|
|
26345
|
+
);
|
|
26346
|
+
visited.add(cursor);
|
|
26347
|
+
}
|
|
26348
|
+
throw new AcpExecutionError(
|
|
26349
|
+
"The selected native session no longer exists. Refresh the list.",
|
|
26350
|
+
{},
|
|
26351
|
+
"native_session_not_found"
|
|
26352
|
+
);
|
|
26353
|
+
};
|
|
26354
|
+
record = (command, event) => {
|
|
26355
|
+
this.#audit.record({
|
|
26356
|
+
commandId: command.requestId,
|
|
26357
|
+
sessionId: command.sessionId,
|
|
26358
|
+
event,
|
|
26359
|
+
details: { agentId: command.agentId }
|
|
26360
|
+
});
|
|
26361
|
+
};
|
|
25221
26362
|
};
|
|
25222
26363
|
|
|
25223
26364
|
// src/relay-connection/connect.ts
|
|
@@ -25233,7 +26374,7 @@ var RelayConnectionError = class extends Error {
|
|
|
25233
26374
|
};
|
|
25234
26375
|
function connectRelay(url2, signal) {
|
|
25235
26376
|
signal.throwIfAborted();
|
|
25236
|
-
return new Promise((
|
|
26377
|
+
return new Promise((resolve5, reject) => {
|
|
25237
26378
|
const socket = new WebSocket(url2, {
|
|
25238
26379
|
dispatcher: environmentProxyDispatcher
|
|
25239
26380
|
});
|
|
@@ -25286,7 +26427,7 @@ function connectRelay(url2, signal) {
|
|
|
25286
26427
|
}
|
|
25287
26428
|
finished = true;
|
|
25288
26429
|
cleanup();
|
|
25289
|
-
|
|
26430
|
+
resolve5(socket);
|
|
25290
26431
|
};
|
|
25291
26432
|
function onTimeout() {
|
|
25292
26433
|
fail(
|
|
@@ -25307,7 +26448,7 @@ function connectRelay(url2, signal) {
|
|
|
25307
26448
|
}
|
|
25308
26449
|
|
|
25309
26450
|
// src/relay-connection/liveness.ts
|
|
25310
|
-
import { randomUUID as
|
|
26451
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
25311
26452
|
var HEARTBEAT_ACK_TIMEOUT_MILLISECONDS = 1e4;
|
|
25312
26453
|
var HEARTBEAT_INTERVAL_MILLISECONDS = 2e4;
|
|
25313
26454
|
var RelayLivenessState = class {
|
|
@@ -25398,7 +26539,7 @@ var RelayConnectionLiveness = class {
|
|
|
25398
26539
|
}
|
|
25399
26540
|
throw new Error("Heartbeat was scheduled in an invalid state");
|
|
25400
26541
|
}
|
|
25401
|
-
const heartbeatId =
|
|
26542
|
+
const heartbeatId = randomUUID4();
|
|
25402
26543
|
this.#state = new AwaitingHeartbeatAckState(
|
|
25403
26544
|
heartbeatId,
|
|
25404
26545
|
this.#timer.schedule(
|
|
@@ -25725,15 +26866,16 @@ function reportOperationFailure(socket, requestId, sessionId, error61) {
|
|
|
25725
26866
|
})
|
|
25726
26867
|
);
|
|
25727
26868
|
}
|
|
25728
|
-
async function resolveSessionDirectories(directories, bindingId, directoryId2, additionalDirectoryIds) {
|
|
25729
|
-
const cwd = await directories.resolve(bindingId, directoryId2);
|
|
26869
|
+
async function resolveSessionDirectories(directories, bindingId, directoryId2, additionalDirectoryIds, signal) {
|
|
26870
|
+
const cwd = await directories.resolve(bindingId, directoryId2, signal);
|
|
25730
26871
|
const additionalDirectories = [];
|
|
25731
26872
|
const additionalDirectoryEntries = [];
|
|
25732
26873
|
const unique = /* @__PURE__ */ new Set([cwd]);
|
|
25733
26874
|
for (const additionalDirectoryId of additionalDirectoryIds) {
|
|
25734
26875
|
const directory = await directories.resolve(
|
|
25735
26876
|
bindingId,
|
|
25736
|
-
additionalDirectoryId
|
|
26877
|
+
additionalDirectoryId,
|
|
26878
|
+
signal
|
|
25737
26879
|
);
|
|
25738
26880
|
if (unique.has(directory)) {
|
|
25739
26881
|
throw new LocalAuthorizationError(
|
|
@@ -25753,11 +26895,12 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
|
|
|
25753
26895
|
additionalDirectoryEntries
|
|
25754
26896
|
);
|
|
25755
26897
|
}
|
|
25756
|
-
async function executeWorkspaceList(socket, command, directories, audit) {
|
|
26898
|
+
async function executeWorkspaceList(socket, command, directories, audit, signal) {
|
|
25757
26899
|
try {
|
|
25758
26900
|
const listing = await directories.list(
|
|
25759
26901
|
command.bindingId,
|
|
25760
|
-
command.directoryId
|
|
26902
|
+
command.directoryId,
|
|
26903
|
+
signal
|
|
25761
26904
|
);
|
|
25762
26905
|
audit.record({
|
|
25763
26906
|
commandId: command.requestId,
|
|
@@ -25787,7 +26930,8 @@ async function executeSessionPrepare(socket, command, config2, directories, audi
|
|
|
25787
26930
|
directories,
|
|
25788
26931
|
command.bindingId,
|
|
25789
26932
|
command.directoryId,
|
|
25790
|
-
command.additionalDirectoryIds
|
|
26933
|
+
command.additionalDirectoryIds,
|
|
26934
|
+
signal
|
|
25791
26935
|
);
|
|
25792
26936
|
const agent = config2.catalog.find(command.agentId);
|
|
25793
26937
|
const prepared = await sessions.prepareSession({
|
|
@@ -25799,7 +26943,8 @@ async function executeSessionPrepare(socket, command, config2, directories, audi
|
|
|
25799
26943
|
});
|
|
25800
26944
|
const listing = await directories.list(
|
|
25801
26945
|
command.bindingId,
|
|
25802
|
-
command.directoryId
|
|
26946
|
+
command.directoryId,
|
|
26947
|
+
signal
|
|
25803
26948
|
);
|
|
25804
26949
|
audit.record({
|
|
25805
26950
|
commandId: command.requestId,
|
|
@@ -25835,7 +26980,8 @@ async function executeSessionConfigure(socket, command, config2, directories, au
|
|
|
25835
26980
|
directories,
|
|
25836
26981
|
command.bindingId,
|
|
25837
26982
|
command.directoryId,
|
|
25838
|
-
command.additionalDirectoryIds
|
|
26983
|
+
command.additionalDirectoryIds,
|
|
26984
|
+
signal
|
|
25839
26985
|
);
|
|
25840
26986
|
const configOptions = await sessions.configureSession({
|
|
25841
26987
|
additionalDirectories: resolved.additionalDirectories,
|
|
@@ -25874,10 +27020,11 @@ async function executePrompt(socket, command, config2, directories, audit, sessi
|
|
|
25874
27020
|
directories,
|
|
25875
27021
|
command.bindingId,
|
|
25876
27022
|
command.directoryId,
|
|
25877
|
-
command.additionalDirectoryIds
|
|
27023
|
+
command.additionalDirectoryIds,
|
|
27024
|
+
signal
|
|
25878
27025
|
);
|
|
25879
27026
|
const agent = config2.catalog.find(command.agentId);
|
|
25880
|
-
const processAttemptId =
|
|
27027
|
+
const processAttemptId = randomUUID5();
|
|
25881
27028
|
audit.record({
|
|
25882
27029
|
commandId: command.commandId,
|
|
25883
27030
|
event: "session_authorized",
|
|
@@ -25975,262 +27122,383 @@ function waitForRelayRetry(signal, timer) {
|
|
|
25975
27122
|
if (signal.aborted) {
|
|
25976
27123
|
return Promise.resolve();
|
|
25977
27124
|
}
|
|
25978
|
-
return new Promise((
|
|
27125
|
+
return new Promise((resolve5) => {
|
|
25979
27126
|
const retry = timer.schedule(finish, RELAY_RECONNECT_DELAY_MILLISECONDS);
|
|
25980
27127
|
function finish() {
|
|
25981
27128
|
retry.cancel();
|
|
25982
27129
|
signal.removeEventListener("abort", finish);
|
|
25983
|
-
|
|
27130
|
+
resolve5();
|
|
25984
27131
|
}
|
|
25985
27132
|
signal.addEventListener("abort", finish, { once: true });
|
|
25986
27133
|
});
|
|
25987
27134
|
}
|
|
25988
|
-
async function runRelayConnection(config2, directories, audit, sessions, installations, installationObserver, signal, timer) {
|
|
27135
|
+
async function runRelayConnection(config2, directories, audit, sessions, installations, installationObserver, nativeSessions, signal, timer) {
|
|
25989
27136
|
const relayUrl = new URL(config2.relayUrl);
|
|
25990
27137
|
relayUrl.searchParams.set("liveness", "1");
|
|
25991
27138
|
const socket = await connectRelay(relayUrl, signal);
|
|
25992
27139
|
const activeTurns = /* @__PURE__ */ new Map();
|
|
25993
27140
|
const connectionController = new AbortController();
|
|
27141
|
+
const nativeRequests = /* @__PURE__ */ new Map();
|
|
25994
27142
|
sessions.setObserver(new DaemonAcpObserver(socket));
|
|
25995
27143
|
installationObserver.connect(socket);
|
|
25996
|
-
|
|
25997
|
-
|
|
25998
|
-
|
|
25999
|
-
|
|
26000
|
-
|
|
26001
|
-
|
|
26002
|
-
|
|
26003
|
-
|
|
26004
|
-
|
|
26005
|
-
|
|
26006
|
-
|
|
26007
|
-
|
|
26008
|
-
turn.
|
|
26009
|
-
|
|
26010
|
-
|
|
26011
|
-
|
|
26012
|
-
|
|
26013
|
-
|
|
26014
|
-
|
|
26015
|
-
|
|
26016
|
-
|
|
26017
|
-
|
|
26018
|
-
|
|
26019
|
-
|
|
26020
|
-
|
|
26021
|
-
|
|
26022
|
-
|
|
26023
|
-
|
|
26024
|
-
|
|
26025
|
-
|
|
26026
|
-
|
|
26027
|
-
|
|
26028
|
-
|
|
26029
|
-
|
|
26030
|
-
|
|
26031
|
-
};
|
|
26032
|
-
const refreshConnection = () => {
|
|
26033
|
-
socket.close(1e3, "Refreshing idle relay connection");
|
|
26034
|
-
resolveConnection();
|
|
26035
|
-
};
|
|
26036
|
-
const connectionLiveness = new RelayConnectionLiveness(timer, {
|
|
26037
|
-
send: (message) => send(socket, message),
|
|
26038
|
-
unresponsive: () => {
|
|
26039
|
-
socket.close(4001, "Relay liveness check timed out");
|
|
27144
|
+
try {
|
|
27145
|
+
await new Promise((resolve5, reject) => {
|
|
27146
|
+
let finished = false;
|
|
27147
|
+
const cleanup = () => {
|
|
27148
|
+
connectionRefresh.close();
|
|
27149
|
+
connectionLiveness.close();
|
|
27150
|
+
signal.removeEventListener("abort", stop);
|
|
27151
|
+
socket.removeEventListener("close", handleClose);
|
|
27152
|
+
socket.removeEventListener("error", handleError);
|
|
27153
|
+
socket.removeEventListener("message", handleMessage);
|
|
27154
|
+
installationObserver.disconnect(socket);
|
|
27155
|
+
connectionController.abort();
|
|
27156
|
+
for (const turn of activeTurns.values()) {
|
|
27157
|
+
turn.abortConnection();
|
|
27158
|
+
}
|
|
27159
|
+
activeTurns.clear();
|
|
27160
|
+
};
|
|
27161
|
+
const resolveConnection = () => {
|
|
27162
|
+
if (finished) {
|
|
27163
|
+
return;
|
|
27164
|
+
}
|
|
27165
|
+
finished = true;
|
|
27166
|
+
cleanup();
|
|
27167
|
+
resolve5();
|
|
27168
|
+
};
|
|
27169
|
+
const rejectConnection = () => {
|
|
27170
|
+
if (finished) {
|
|
27171
|
+
return;
|
|
27172
|
+
}
|
|
27173
|
+
finished = true;
|
|
27174
|
+
cleanup();
|
|
27175
|
+
reject(new Error("Relay WebSocket failed"));
|
|
27176
|
+
};
|
|
27177
|
+
const stop = () => {
|
|
27178
|
+
socket.close(1e3, "Daemon stopped");
|
|
26040
27179
|
resolveConnection();
|
|
26041
|
-
}
|
|
26042
|
-
|
|
26043
|
-
|
|
26044
|
-
timer,
|
|
26045
|
-
() => activeTurns.size === 0,
|
|
26046
|
-
refreshConnection
|
|
26047
|
-
);
|
|
26048
|
-
signal.addEventListener("abort", stop, { once: true });
|
|
26049
|
-
const handleClose = () => {
|
|
26050
|
-
resolveConnection();
|
|
26051
|
-
};
|
|
26052
|
-
const handleError = () => {
|
|
26053
|
-
if (signal.aborted) {
|
|
27180
|
+
};
|
|
27181
|
+
const refreshConnection = () => {
|
|
27182
|
+
socket.close(1e3, "Refreshing idle relay connection");
|
|
26054
27183
|
resolveConnection();
|
|
26055
|
-
|
|
26056
|
-
|
|
26057
|
-
|
|
26058
|
-
|
|
26059
|
-
|
|
26060
|
-
|
|
26061
|
-
|
|
26062
|
-
|
|
26063
|
-
|
|
26064
|
-
|
|
26065
|
-
|
|
26066
|
-
|
|
26067
|
-
|
|
26068
|
-
|
|
26069
|
-
|
|
26070
|
-
|
|
27184
|
+
};
|
|
27185
|
+
const connectionLiveness = new RelayConnectionLiveness(timer, {
|
|
27186
|
+
send: (message) => send(socket, message),
|
|
27187
|
+
unresponsive: () => {
|
|
27188
|
+
socket.close(4001, "Relay liveness check timed out");
|
|
27189
|
+
resolveConnection();
|
|
27190
|
+
}
|
|
27191
|
+
});
|
|
27192
|
+
const connectionRefresh = new RelayConnectionRefresh(
|
|
27193
|
+
timer,
|
|
27194
|
+
() => activeTurns.size === 0,
|
|
27195
|
+
refreshConnection
|
|
27196
|
+
);
|
|
27197
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
27198
|
+
const handleClose = () => {
|
|
27199
|
+
resolveConnection();
|
|
27200
|
+
};
|
|
27201
|
+
const handleError = () => {
|
|
27202
|
+
if (signal.aborted) {
|
|
27203
|
+
resolveConnection();
|
|
26071
27204
|
return;
|
|
26072
27205
|
}
|
|
26073
|
-
|
|
26074
|
-
|
|
26075
|
-
|
|
26076
|
-
|
|
27206
|
+
rejectConnection();
|
|
27207
|
+
};
|
|
27208
|
+
const handleMessage = (event) => {
|
|
27209
|
+
const encoded = event.data;
|
|
27210
|
+
if (typeof encoded !== "string") {
|
|
27211
|
+
socket.close(4003, "Text message required");
|
|
27212
|
+
rejectConnection();
|
|
26077
27213
|
return;
|
|
26078
27214
|
}
|
|
26079
|
-
|
|
26080
|
-
const
|
|
26081
|
-
|
|
26082
|
-
|
|
26083
|
-
|
|
26084
|
-
|
|
27215
|
+
try {
|
|
27216
|
+
const kind = decodeMessageKind(encoded);
|
|
27217
|
+
if (kind === NATIVE_SESSION_CANCEL_KIND) {
|
|
27218
|
+
const command2 = decodeNativeSessionCancel(encoded);
|
|
27219
|
+
const pending = nativeRequests.get(command2.requestId);
|
|
27220
|
+
if (pending?.sessionId === command2.sessionId)
|
|
27221
|
+
pending.controller.abort(
|
|
27222
|
+
new AcpExecutionError(
|
|
27223
|
+
"Native session operation cancelled",
|
|
27224
|
+
{},
|
|
27225
|
+
"native_session_cancelled"
|
|
27226
|
+
)
|
|
27227
|
+
);
|
|
27228
|
+
return;
|
|
27229
|
+
}
|
|
27230
|
+
if (kind === NATIVE_SESSION_REQUEST_KIND) {
|
|
27231
|
+
const command2 = decodeNativeSessionCommand(encoded);
|
|
27232
|
+
const previous = [...nativeRequests.values()].filter(
|
|
27233
|
+
(request) => request.agentId === command2.agentId
|
|
26085
27234
|
);
|
|
27235
|
+
if (nativeRequests.has(command2.requestId) || nativeRequests.size >= 4 || previous.some((request) => !request.controller.signal.aborted)) {
|
|
27236
|
+
reportOperationFailure(
|
|
27237
|
+
socket,
|
|
27238
|
+
command2.requestId,
|
|
27239
|
+
command2.sessionId,
|
|
27240
|
+
new AcpExecutionError(
|
|
27241
|
+
"Another native session operation is running",
|
|
27242
|
+
{},
|
|
27243
|
+
"native_session_busy"
|
|
27244
|
+
)
|
|
27245
|
+
);
|
|
27246
|
+
return;
|
|
27247
|
+
}
|
|
27248
|
+
const controller = new AbortController();
|
|
27249
|
+
const operationSignal = AbortSignal.any([
|
|
27250
|
+
connectionController.signal,
|
|
27251
|
+
controller.signal
|
|
27252
|
+
]);
|
|
27253
|
+
const timeout = timer.schedule(
|
|
27254
|
+
() => controller.abort(
|
|
27255
|
+
new AcpExecutionError(
|
|
27256
|
+
"Native session operation timed out",
|
|
27257
|
+
{},
|
|
27258
|
+
"native_session_timeout"
|
|
27259
|
+
)
|
|
27260
|
+
),
|
|
27261
|
+
9e4
|
|
27262
|
+
);
|
|
27263
|
+
const completion = (async () => {
|
|
27264
|
+
try {
|
|
27265
|
+
operationSignal.throwIfAborted();
|
|
27266
|
+
if (previous.length > 0) {
|
|
27267
|
+
await new Promise((resolve6, reject2) => {
|
|
27268
|
+
const cancel = () => reject2(operationSignal.reason);
|
|
27269
|
+
operationSignal.addEventListener("abort", cancel, {
|
|
27270
|
+
once: true
|
|
27271
|
+
});
|
|
27272
|
+
void Promise.all(
|
|
27273
|
+
previous.map((request) => request.completion)
|
|
27274
|
+
).then(
|
|
27275
|
+
() => {
|
|
27276
|
+
operationSignal.removeEventListener("abort", cancel);
|
|
27277
|
+
resolve6();
|
|
27278
|
+
},
|
|
27279
|
+
(error61) => {
|
|
27280
|
+
operationSignal.removeEventListener("abort", cancel);
|
|
27281
|
+
reject2(error61);
|
|
27282
|
+
}
|
|
27283
|
+
);
|
|
27284
|
+
});
|
|
27285
|
+
}
|
|
27286
|
+
operationSignal.throwIfAborted();
|
|
27287
|
+
const result = await nativeSessions.execute(
|
|
27288
|
+
command2,
|
|
27289
|
+
operationSignal
|
|
27290
|
+
);
|
|
27291
|
+
if (socket.readyState === WebSocket2.OPEN)
|
|
27292
|
+
send(socket, encodeNativeSessionResult(result));
|
|
27293
|
+
} catch (error61) {
|
|
27294
|
+
const failure = operationSignal.aborted ? controller.signal.reason ?? new AcpExecutionError(
|
|
27295
|
+
"Native session operation cancelled",
|
|
27296
|
+
{},
|
|
27297
|
+
"native_session_cancelled"
|
|
27298
|
+
) : error61;
|
|
27299
|
+
audit.record({
|
|
27300
|
+
commandId: command2.requestId,
|
|
27301
|
+
sessionId: command2.sessionId,
|
|
27302
|
+
event: "native_session_operation_failed",
|
|
27303
|
+
details: { code: operationErrorCode(failure) }
|
|
27304
|
+
});
|
|
27305
|
+
reportOperationFailure(
|
|
27306
|
+
socket,
|
|
27307
|
+
command2.requestId,
|
|
27308
|
+
command2.sessionId,
|
|
27309
|
+
failure
|
|
27310
|
+
);
|
|
27311
|
+
} finally {
|
|
27312
|
+
timeout.cancel();
|
|
27313
|
+
nativeRequests.delete(command2.requestId);
|
|
27314
|
+
}
|
|
27315
|
+
})();
|
|
27316
|
+
nativeRequests.set(command2.requestId, {
|
|
27317
|
+
agentId: command2.agentId,
|
|
27318
|
+
sessionId: command2.sessionId,
|
|
27319
|
+
controller,
|
|
27320
|
+
completion
|
|
27321
|
+
});
|
|
26086
27322
|
return;
|
|
26087
27323
|
}
|
|
26088
|
-
if (
|
|
27324
|
+
if (kind === RUNTIME_LIVENESS_READY_KIND) {
|
|
27325
|
+
decodeRuntimeLivenessReadyEvent(encoded);
|
|
27326
|
+
connectionLiveness.start();
|
|
26089
27327
|
return;
|
|
26090
27328
|
}
|
|
26091
|
-
|
|
26092
|
-
|
|
26093
|
-
|
|
26094
|
-
sessionId: cancellation.sessionId
|
|
26095
|
-
});
|
|
26096
|
-
void sessions.cancelPrompt(cancellation.sessionId).catch((error61) => {
|
|
26097
|
-
console.error(
|
|
26098
|
-
"Mobius daemon could not cancel the agent turn",
|
|
26099
|
-
error61
|
|
27329
|
+
if (kind === RUNTIME_HEARTBEAT_ACK_KIND) {
|
|
27330
|
+
connectionLiveness.acknowledge(
|
|
27331
|
+
decodeRuntimeHeartbeatAckEvent(encoded)
|
|
26100
27332
|
);
|
|
26101
|
-
|
|
26102
|
-
|
|
26103
|
-
|
|
26104
|
-
|
|
26105
|
-
|
|
26106
|
-
|
|
26107
|
-
|
|
26108
|
-
|
|
26109
|
-
|
|
26110
|
-
|
|
26111
|
-
|
|
26112
|
-
|
|
26113
|
-
|
|
26114
|
-
|
|
26115
|
-
directories,
|
|
26116
|
-
audit,
|
|
26117
|
-
sessions,
|
|
26118
|
-
connectionController.signal
|
|
26119
|
-
);
|
|
26120
|
-
return;
|
|
26121
|
-
}
|
|
26122
|
-
if (kind === SESSION_CONFIGURE_KIND) {
|
|
26123
|
-
const command2 = decodeSessionConfigureCommand(encoded);
|
|
26124
|
-
void executeSessionConfigure(
|
|
26125
|
-
socket,
|
|
26126
|
-
command2,
|
|
26127
|
-
config2,
|
|
26128
|
-
directories,
|
|
26129
|
-
audit,
|
|
26130
|
-
sessions,
|
|
26131
|
-
connectionController.signal
|
|
26132
|
-
);
|
|
26133
|
-
return;
|
|
26134
|
-
}
|
|
26135
|
-
if (kind === SESSION_PERMISSION_RESPOND_KIND) {
|
|
26136
|
-
const command2 = decodeSessionPermissionRespondCommand(encoded);
|
|
26137
|
-
void sessions.respondPermission(command2).then(() => {
|
|
27333
|
+
return;
|
|
27334
|
+
}
|
|
27335
|
+
if (kind === SESSION_CANCEL_KIND) {
|
|
27336
|
+
const cancellation = decodeSessionCancelCommand(encoded);
|
|
27337
|
+
const turn2 = activeTurns.get(cancellation.commandId);
|
|
27338
|
+
if (turn2 === void 0 || !turn2.matches(cancellation)) {
|
|
27339
|
+
console.warn(
|
|
27340
|
+
"Mobius daemon ignored cancellation for an inactive turn"
|
|
27341
|
+
);
|
|
27342
|
+
return;
|
|
27343
|
+
}
|
|
27344
|
+
if (!turn2.requestCancellation()) {
|
|
27345
|
+
return;
|
|
27346
|
+
}
|
|
26138
27347
|
audit.record({
|
|
26139
|
-
commandId:
|
|
26140
|
-
event: "
|
|
26141
|
-
sessionId:
|
|
27348
|
+
commandId: cancellation.commandId,
|
|
27349
|
+
event: "session_cancellation_requested",
|
|
27350
|
+
sessionId: cancellation.sessionId
|
|
27351
|
+
});
|
|
27352
|
+
void sessions.cancelPrompt(cancellation.sessionId).catch((error61) => {
|
|
27353
|
+
console.error(
|
|
27354
|
+
"Mobius daemon could not cancel the agent turn",
|
|
27355
|
+
error61
|
|
27356
|
+
);
|
|
26142
27357
|
});
|
|
26143
|
-
|
|
26144
|
-
|
|
27358
|
+
return;
|
|
27359
|
+
}
|
|
27360
|
+
if (kind === WORKSPACE_LIST_KIND) {
|
|
27361
|
+
const command2 = decodeWorkspaceListCommand(encoded);
|
|
27362
|
+
void executeWorkspaceList(
|
|
26145
27363
|
socket,
|
|
26146
|
-
command2
|
|
26147
|
-
|
|
26148
|
-
|
|
27364
|
+
command2,
|
|
27365
|
+
directories,
|
|
27366
|
+
audit,
|
|
27367
|
+
connectionController.signal
|
|
26149
27368
|
);
|
|
26150
|
-
|
|
26151
|
-
|
|
26152
|
-
|
|
26153
|
-
|
|
26154
|
-
|
|
26155
|
-
|
|
26156
|
-
|
|
26157
|
-
|
|
26158
|
-
|
|
26159
|
-
|
|
27369
|
+
return;
|
|
27370
|
+
}
|
|
27371
|
+
if (kind === SESSION_PREPARE_KIND) {
|
|
27372
|
+
const command2 = decodeSessionPrepareCommand(encoded);
|
|
27373
|
+
void executeSessionPrepare(
|
|
27374
|
+
socket,
|
|
27375
|
+
command2,
|
|
27376
|
+
config2,
|
|
27377
|
+
directories,
|
|
27378
|
+
audit,
|
|
27379
|
+
sessions,
|
|
27380
|
+
connectionController.signal
|
|
27381
|
+
);
|
|
27382
|
+
return;
|
|
27383
|
+
}
|
|
27384
|
+
if (kind === SESSION_CONFIGURE_KIND) {
|
|
27385
|
+
const command2 = decodeSessionConfigureCommand(encoded);
|
|
27386
|
+
void executeSessionConfigure(
|
|
27387
|
+
socket,
|
|
27388
|
+
command2,
|
|
27389
|
+
config2,
|
|
27390
|
+
directories,
|
|
27391
|
+
audit,
|
|
27392
|
+
sessions,
|
|
27393
|
+
connectionController.signal
|
|
27394
|
+
);
|
|
27395
|
+
return;
|
|
27396
|
+
}
|
|
27397
|
+
if (kind === SESSION_PERMISSION_RESPOND_KIND) {
|
|
27398
|
+
const command2 = decodeSessionPermissionRespondCommand(encoded);
|
|
27399
|
+
void sessions.respondPermission(command2).then(() => {
|
|
27400
|
+
audit.record({
|
|
27401
|
+
commandId: command2.interactionId,
|
|
27402
|
+
event: "session_permission_resolved",
|
|
27403
|
+
sessionId: command2.sessionId
|
|
27404
|
+
});
|
|
27405
|
+
}).catch((error61) => {
|
|
27406
|
+
reportOperationFailure(
|
|
27407
|
+
socket,
|
|
27408
|
+
command2.interactionId,
|
|
27409
|
+
command2.sessionId,
|
|
27410
|
+
error61
|
|
27411
|
+
);
|
|
27412
|
+
});
|
|
27413
|
+
return;
|
|
27414
|
+
}
|
|
27415
|
+
if (kind === SESSION_ELICITATION_RESPOND_KIND) {
|
|
27416
|
+
const command2 = decodeSessionElicitationRespondCommand(encoded);
|
|
27417
|
+
void sessions.respondElicitation(command2).then(() => {
|
|
27418
|
+
audit.record({
|
|
27419
|
+
commandId: command2.interactionId,
|
|
27420
|
+
event: "session_elicitation_resolved",
|
|
27421
|
+
sessionId: command2.sessionId
|
|
27422
|
+
});
|
|
27423
|
+
}).catch((error61) => {
|
|
27424
|
+
reportOperationFailure(
|
|
27425
|
+
socket,
|
|
27426
|
+
command2.interactionId,
|
|
27427
|
+
command2.sessionId,
|
|
27428
|
+
error61
|
|
27429
|
+
);
|
|
26160
27430
|
});
|
|
26161
|
-
|
|
26162
|
-
|
|
27431
|
+
return;
|
|
27432
|
+
}
|
|
27433
|
+
if (kind === AGENT_INSTALL_REQUEST_KIND) {
|
|
27434
|
+
const command2 = decodeAgentInstallRequestCommand(encoded);
|
|
27435
|
+
void installations.request(command2, signal).catch((error61) => {
|
|
27436
|
+
console.error("Mobius Agent installation failed", error61);
|
|
27437
|
+
});
|
|
27438
|
+
return;
|
|
27439
|
+
}
|
|
27440
|
+
if (kind === AGENT_INSTALL_CANCEL_KIND) {
|
|
27441
|
+
const command2 = decodeAgentInstallCancelCommand(encoded);
|
|
27442
|
+
void installations.cancel(command2.requestId).catch((error61) => {
|
|
27443
|
+
reportOperationFailure(socket, command2.requestId, "", error61);
|
|
27444
|
+
});
|
|
27445
|
+
return;
|
|
27446
|
+
}
|
|
27447
|
+
if (kind !== SESSION_PROMPT_KIND) {
|
|
27448
|
+
throw new Error("Unsupported protocol message");
|
|
27449
|
+
}
|
|
27450
|
+
const command = decodeSessionPromptCommand(encoded);
|
|
27451
|
+
if (activeTurns.has(command.commandId)) {
|
|
27452
|
+
reportFailure(
|
|
26163
27453
|
socket,
|
|
26164
|
-
|
|
26165
|
-
|
|
26166
|
-
|
|
27454
|
+
command,
|
|
27455
|
+
"duplicate_command",
|
|
27456
|
+
"The command is already running"
|
|
26167
27457
|
);
|
|
26168
|
-
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
void installations.request(command2, signal).catch((error61) => {
|
|
26174
|
-
console.error("Mobius Agent installation failed", error61);
|
|
26175
|
-
});
|
|
26176
|
-
return;
|
|
26177
|
-
}
|
|
26178
|
-
if (kind === AGENT_INSTALL_CANCEL_KIND) {
|
|
26179
|
-
const command2 = decodeAgentInstallCancelCommand(encoded);
|
|
26180
|
-
void installations.cancel(command2.requestId).catch((error61) => {
|
|
26181
|
-
reportOperationFailure(socket, command2.requestId, "", error61);
|
|
26182
|
-
});
|
|
26183
|
-
return;
|
|
26184
|
-
}
|
|
26185
|
-
if (kind !== SESSION_PROMPT_KIND) {
|
|
26186
|
-
throw new Error("Unsupported protocol message");
|
|
26187
|
-
}
|
|
26188
|
-
const command = decodeSessionPromptCommand(encoded);
|
|
26189
|
-
if (activeTurns.has(command.commandId)) {
|
|
26190
|
-
reportFailure(
|
|
27458
|
+
return;
|
|
27459
|
+
}
|
|
27460
|
+
const turn = new ActiveDaemonTurn(command, new AbortController());
|
|
27461
|
+
activeTurns.set(command.commandId, turn);
|
|
27462
|
+
void executePrompt(
|
|
26191
27463
|
socket,
|
|
26192
27464
|
command,
|
|
26193
|
-
|
|
26194
|
-
|
|
26195
|
-
|
|
26196
|
-
|
|
27465
|
+
config2,
|
|
27466
|
+
directories,
|
|
27467
|
+
audit,
|
|
27468
|
+
sessions,
|
|
27469
|
+
turn.signal
|
|
27470
|
+
).catch((error61) => {
|
|
27471
|
+
console.error("Mobius daemon turn failed", error61);
|
|
27472
|
+
}).finally(() => {
|
|
27473
|
+
activeTurns.delete(command.commandId);
|
|
27474
|
+
connectionRefresh.afterTurnFinished();
|
|
27475
|
+
});
|
|
27476
|
+
} catch (error61) {
|
|
27477
|
+
console.error("Mobius daemon rejected a message", error61);
|
|
27478
|
+
socket.close(4008, "Invalid protocol message");
|
|
27479
|
+
rejectConnection();
|
|
26197
27480
|
}
|
|
26198
|
-
|
|
26199
|
-
|
|
26200
|
-
|
|
26201
|
-
|
|
26202
|
-
|
|
26203
|
-
|
|
26204
|
-
directories,
|
|
26205
|
-
audit,
|
|
26206
|
-
sessions,
|
|
26207
|
-
turn.signal
|
|
26208
|
-
).catch((error61) => {
|
|
26209
|
-
console.error("Mobius daemon turn failed", error61);
|
|
26210
|
-
}).finally(() => {
|
|
26211
|
-
activeTurns.delete(command.commandId);
|
|
26212
|
-
connectionRefresh.afterTurnFinished();
|
|
26213
|
-
});
|
|
26214
|
-
} catch (error61) {
|
|
26215
|
-
console.error("Mobius daemon rejected a message", error61);
|
|
26216
|
-
socket.close(4008, "Invalid protocol message");
|
|
26217
|
-
rejectConnection();
|
|
27481
|
+
};
|
|
27482
|
+
socket.addEventListener("close", handleClose);
|
|
27483
|
+
socket.addEventListener("error", handleError);
|
|
27484
|
+
socket.addEventListener("message", handleMessage);
|
|
27485
|
+
if (signal.aborted) {
|
|
27486
|
+
stop();
|
|
26218
27487
|
}
|
|
26219
|
-
};
|
|
26220
|
-
|
|
26221
|
-
|
|
26222
|
-
|
|
26223
|
-
|
|
26224
|
-
|
|
26225
|
-
|
|
26226
|
-
});
|
|
27488
|
+
});
|
|
27489
|
+
} finally {
|
|
27490
|
+
connectionController.abort();
|
|
27491
|
+
await Promise.all(
|
|
27492
|
+
[...nativeRequests.values()].map((request) => request.completion)
|
|
27493
|
+
);
|
|
27494
|
+
}
|
|
26227
27495
|
}
|
|
26228
27496
|
async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
26229
27497
|
const installationAudit = new AuditInstallationEvents(audit);
|
|
26230
27498
|
const stateDirectory = readStateDirectory();
|
|
26231
27499
|
const specifications = supportedLocalAgentSpecifications();
|
|
26232
27500
|
const installationExecutor = new FixedInstallationExecutor({
|
|
26233
|
-
|
|
27501
|
+
defaultWorkspace: config2.defaultWorkspace,
|
|
26234
27502
|
audit: installationAudit,
|
|
26235
27503
|
catalog: config2.catalog,
|
|
26236
27504
|
environment: process.env,
|
|
@@ -26239,13 +27507,12 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
26239
27507
|
});
|
|
26240
27508
|
await new AutomaticAdapterProvisioner(
|
|
26241
27509
|
{
|
|
26242
|
-
|
|
27510
|
+
defaultWorkspace: config2.defaultWorkspace,
|
|
26243
27511
|
audit: installationAudit,
|
|
26244
27512
|
catalog: config2.catalog,
|
|
26245
27513
|
environment: process.env
|
|
26246
27514
|
},
|
|
26247
27515
|
new ManagedAdapterInstaller({
|
|
26248
|
-
approvedRoot: config2.approvedRoot,
|
|
26249
27516
|
environment: process.env,
|
|
26250
27517
|
stateDirectory
|
|
26251
27518
|
}),
|
|
@@ -26258,9 +27525,15 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
26258
27525
|
);
|
|
26259
27526
|
const directories = new WorkspaceDirectoryService(
|
|
26260
27527
|
config2.bindingId,
|
|
26261
|
-
config2.
|
|
27528
|
+
config2.defaultWorkspace
|
|
26262
27529
|
);
|
|
26263
27530
|
const installationObserver = new DaemonInstallationObserver();
|
|
27531
|
+
const nativeSessions = new NativeSessionService(
|
|
27532
|
+
config2,
|
|
27533
|
+
directories,
|
|
27534
|
+
sessions,
|
|
27535
|
+
audit
|
|
27536
|
+
);
|
|
26264
27537
|
const installations = new AgentInstallationManager(
|
|
26265
27538
|
specifications,
|
|
26266
27539
|
new ConsoleInstallationAuthorizer(stateDirectory),
|
|
@@ -26280,6 +27553,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
26280
27553
|
sessions,
|
|
26281
27554
|
installations,
|
|
26282
27555
|
installationObserver,
|
|
27556
|
+
nativeSessions,
|
|
26283
27557
|
signal,
|
|
26284
27558
|
timer
|
|
26285
27559
|
);
|
|
@@ -26312,7 +27586,6 @@ export {
|
|
|
26312
27586
|
AuditStore,
|
|
26313
27587
|
LocalAuthorizationError,
|
|
26314
27588
|
PairingClientError,
|
|
26315
|
-
authorizeBinding,
|
|
26316
27589
|
createAcpClient,
|
|
26317
27590
|
loadDaemonConfig,
|
|
26318
27591
|
pairLocalMachine,
|