@moxt-ai/mobius 0.0.23 → 0.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp/session-capabilities.d.ts +2 -2
- package/dist/acp/session-capabilities.d.ts.map +1 -1
- package/dist/acp/session-capabilities.js +16 -1
- package/dist/acp/session-capabilities.js.map +1 -1
- package/dist/acp/session-manager.d.ts +47 -6
- package/dist/acp/session-manager.d.ts.map +1 -1
- package/dist/acp/session-manager.js +250 -8
- package/dist/acp/session-manager.js.map +1 -1
- package/dist/agent-catalog/acp-readiness-probe.js +1 -1
- package/dist/agent-catalog/acp-readiness-probe.js.map +1 -1
- package/dist/agent-catalog/publish-agent-catalog.d.ts.map +1 -1
- package/dist/agent-catalog/publish-agent-catalog.js +12 -7
- package/dist/agent-catalog/publish-agent-catalog.js.map +1 -1
- package/dist/bots/local-bot-grant-store.d.ts +2 -1
- package/dist/bots/local-bot-grant-store.d.ts.map +1 -1
- package/dist/bots/local-bot-grant-store.js +8 -5
- package/dist/bots/local-bot-grant-store.js.map +1 -1
- package/dist/cli.js +949 -260
- package/dist/cli.js.map +4 -4
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1002 -216
- package/dist/index.js.map +4 -4
- package/dist/native-sessions/service.d.ts +2 -1
- package/dist/native-sessions/service.d.ts.map +1 -1
- package/dist/native-sessions/service.js +65 -0
- package/dist/native-sessions/service.js.map +1 -1
- package/dist/pairing/pair-local-machine.d.ts +8 -0
- package/dist/pairing/pair-local-machine.d.ts.map +1 -1
- package/dist/pairing/pair-local-machine.js +64 -37
- package/dist/pairing/pair-local-machine.js.map +1 -1
- package/dist/prompt-artifact-resolver.d.ts.map +1 -1
- package/dist/prompt-artifact-resolver.js +11 -3
- package/dist/prompt-artifact-resolver.js.map +1 -1
- package/dist/relay-connection/connect.d.ts.map +1 -1
- package/dist/relay-connection/connect.js +9 -1
- package/dist/relay-connection/connect.js.map +1 -1
- package/dist/relay-connection/session-event-outbox.d.ts +1 -0
- package/dist/relay-connection/session-event-outbox.d.ts.map +1 -1
- package/dist/relay-connection/session-event-outbox.js +7 -0
- package/dist/relay-connection/session-event-outbox.js.map +1 -1
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +165 -54
- package/dist/run-daemon.js.map +1 -1
- package/dist/runtime.d.ts +23 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +31046 -0
- package/dist/runtime.js.map +7 -0
- package/package.json +6 -2
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import { mkdir as mkdir5 } from "node:fs/promises";
|
|
|
11
11
|
// package.json
|
|
12
12
|
var package_default = {
|
|
13
13
|
name: "@moxt-ai/mobius",
|
|
14
|
-
version: "0.0.
|
|
14
|
+
version: "0.0.25",
|
|
15
15
|
description: "Mobius CLI for pairing and running local AI agent services.",
|
|
16
16
|
type: "module",
|
|
17
17
|
bin: {
|
|
@@ -21,6 +21,10 @@ var package_default = {
|
|
|
21
21
|
".": {
|
|
22
22
|
types: "./dist/index.d.ts",
|
|
23
23
|
import: "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./runtime": {
|
|
26
|
+
types: "./dist/runtime.d.ts",
|
|
27
|
+
import: "./dist/runtime.js"
|
|
24
28
|
}
|
|
25
29
|
},
|
|
26
30
|
files: [
|
|
@@ -38,7 +42,7 @@ var package_default = {
|
|
|
38
42
|
directory: "packages/cli"
|
|
39
43
|
},
|
|
40
44
|
scripts: {
|
|
41
|
-
build: "tsc --project tsconfig.build.json && esbuild src/cli.ts src/index.ts --bundle --platform=node --format=esm --sourcemap --outdir=dist --external:@agentclientprotocol/* --external:undici",
|
|
45
|
+
build: "tsc --project tsconfig.build.json && esbuild src/cli.ts src/index.ts src/runtime.ts --bundle --platform=node --format=esm --sourcemap --outdir=dist --external:@agentclientprotocol/* --external:undici",
|
|
42
46
|
dev: "node --watch src/cli.ts",
|
|
43
47
|
prepack: "pnpm build",
|
|
44
48
|
start: "node dist/cli.js",
|
|
@@ -71,6 +75,16 @@ var package_default = {
|
|
|
71
75
|
import { realpathSync, statSync } from "node:fs";
|
|
72
76
|
|
|
73
77
|
// src/acp/session-capabilities.ts
|
|
78
|
+
function isRecord(value) {
|
|
79
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
80
|
+
}
|
|
81
|
+
function hasTrueChildField(value, childKey, fieldKey) {
|
|
82
|
+
if (!isRecord(value)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const child = Reflect.get(value, childKey);
|
|
86
|
+
return isRecord(child) && Reflect.get(child, fieldKey) === true;
|
|
87
|
+
}
|
|
74
88
|
function unsupportedSessionCapabilities() {
|
|
75
89
|
return Object.freeze({
|
|
76
90
|
additionalDirectories: false,
|
|
@@ -81,11 +95,14 @@ function unsupportedSessionCapabilities() {
|
|
|
81
95
|
imagePrompt: false,
|
|
82
96
|
listSessions: false,
|
|
83
97
|
loadSession: false,
|
|
98
|
+
promptQueueing: false,
|
|
84
99
|
resumeSession: false,
|
|
100
|
+
steering: false,
|
|
85
101
|
unstableForkSession: false
|
|
86
102
|
});
|
|
87
103
|
}
|
|
88
|
-
function normalizeAcpCapabilities(
|
|
104
|
+
function normalizeAcpCapabilities(initialized) {
|
|
105
|
+
const capabilities = initialized.agentCapabilities ?? {};
|
|
89
106
|
const session = capabilities.sessionCapabilities;
|
|
90
107
|
const prompt = capabilities.promptCapabilities;
|
|
91
108
|
return Object.freeze({
|
|
@@ -97,7 +114,9 @@ function normalizeAcpCapabilities(capabilities) {
|
|
|
97
114
|
imagePrompt: prompt?.image === true,
|
|
98
115
|
listSessions: session?.list != null,
|
|
99
116
|
loadSession: capabilities.loadSession === true,
|
|
117
|
+
promptQueueing: hasTrueChildField(capabilities._meta, "claudeCode", "promptQueueing"),
|
|
100
118
|
resumeSession: session?.resume != null,
|
|
119
|
+
steering: hasTrueChildField(initialized._meta, "steering", "supported"),
|
|
101
120
|
unstableForkSession: session?.fork != null
|
|
102
121
|
});
|
|
103
122
|
}
|
|
@@ -865,7 +884,7 @@ var ManagedNpmPackageError = class extends Error {
|
|
|
865
884
|
this.name = "ManagedNpmPackageError";
|
|
866
885
|
}
|
|
867
886
|
};
|
|
868
|
-
function
|
|
887
|
+
function isRecord2(value) {
|
|
869
888
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
870
889
|
}
|
|
871
890
|
function packagePath(root, packageName) {
|
|
@@ -887,7 +906,7 @@ async function readJsonRecord(path2, maximumBytes) {
|
|
|
887
906
|
);
|
|
888
907
|
}
|
|
889
908
|
const value = JSON.parse(await handle.readFile({ encoding: "utf8" }));
|
|
890
|
-
if (!
|
|
909
|
+
if (!isRecord2(value)) {
|
|
891
910
|
throw new ManagedNpmPackageError(
|
|
892
911
|
"managed_package_verification_failed",
|
|
893
912
|
"The managed npm package metadata is invalid"
|
|
@@ -913,7 +932,7 @@ function readBinPath(packageMetadata, executableName) {
|
|
|
913
932
|
if (typeof bin === "string") {
|
|
914
933
|
return bin;
|
|
915
934
|
}
|
|
916
|
-
if (
|
|
935
|
+
if (isRecord2(bin) && typeof bin[executableName] === "string") {
|
|
917
936
|
return bin[executableName];
|
|
918
937
|
}
|
|
919
938
|
throw new ManagedNpmPackageError(
|
|
@@ -929,7 +948,7 @@ function verifyPackageLockEntry(lock, definition, verifyDependencyClosure) {
|
|
|
929
948
|
);
|
|
930
949
|
}
|
|
931
950
|
const packages = lock["packages"];
|
|
932
|
-
if (!
|
|
951
|
+
if (!isRecord2(packages)) {
|
|
933
952
|
throw new ManagedNpmPackageError(
|
|
934
953
|
"managed_package_verification_failed",
|
|
935
954
|
"The managed npm package lockfile is invalid"
|
|
@@ -943,7 +962,7 @@ function verifyPackageLockEntry(lock, definition, verifyDependencyClosure) {
|
|
|
943
962
|
if (verifyDependencyClosure && normalizedKey.length > 0) {
|
|
944
963
|
let resolved;
|
|
945
964
|
try {
|
|
946
|
-
if (!normalizedKey.split("/").includes("node_modules") || !
|
|
965
|
+
if (!normalizedKey.split("/").includes("node_modules") || !isRecord2(candidate) || typeof candidate["version"] !== "string" || candidate["version"].length === 0 || typeof candidate["integrity"] !== "string" || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(candidate["integrity"]) || typeof candidate["resolved"] !== "string") {
|
|
947
966
|
throw new Error("Invalid locked dependency");
|
|
948
967
|
}
|
|
949
968
|
resolved = new URL(candidate["resolved"]);
|
|
@@ -966,7 +985,7 @@ function verifyPackageLockEntry(lock, definition, verifyDependencyClosure) {
|
|
|
966
985
|
matches += 1;
|
|
967
986
|
}
|
|
968
987
|
}
|
|
969
|
-
if (matches !== 1 || !
|
|
988
|
+
if (matches !== 1 || !isRecord2(entry) || entry["integrity"] !== definition.integrity || entry["resolved"] !== definition.tarballUrl || entry["version"] !== definition.version) {
|
|
970
989
|
throw new ManagedNpmPackageError(
|
|
971
990
|
"managed_package_verification_failed",
|
|
972
991
|
"The managed npm package lockfile does not match the pinned artifact"
|
|
@@ -1607,7 +1626,7 @@ var ManagedAgentArchiveError = class extends Error {
|
|
|
1607
1626
|
this.name = "ManagedAgentArchiveError";
|
|
1608
1627
|
}
|
|
1609
1628
|
};
|
|
1610
|
-
function
|
|
1629
|
+
function isRecord3(value) {
|
|
1611
1630
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1612
1631
|
}
|
|
1613
1632
|
function isContainedPath3(root, candidate) {
|
|
@@ -1664,7 +1683,7 @@ async function readManifest(path2) {
|
|
|
1664
1683
|
);
|
|
1665
1684
|
}
|
|
1666
1685
|
const parsed = JSON.parse(await handle.readFile({ encoding: "utf8" }));
|
|
1667
|
-
if (!
|
|
1686
|
+
if (!isRecord3(parsed)) {
|
|
1668
1687
|
throw new ManagedAgentArchiveError(
|
|
1669
1688
|
"managed_archive_verification_failed",
|
|
1670
1689
|
"The managed Agent archive manifest is invalid"
|
|
@@ -2384,7 +2403,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
|
|
|
2384
2403
|
await specification.verifyAcp(connection, initialized, defaultWorkspace, controller.signal);
|
|
2385
2404
|
return new VerifiedAcpReadiness(
|
|
2386
2405
|
normalizedVersion(initialized.agentInfo?.version ?? ""),
|
|
2387
|
-
normalizeAcpCapabilities(initialized
|
|
2406
|
+
normalizeAcpCapabilities(initialized)
|
|
2388
2407
|
);
|
|
2389
2408
|
} catch (error61) {
|
|
2390
2409
|
if (signal.aborted) {
|
|
@@ -2563,54 +2582,6 @@ async function discoverLocalAgentInventory(options) {
|
|
|
2563
2582
|
return new LocalAgentInventory(discoveries);
|
|
2564
2583
|
}
|
|
2565
2584
|
|
|
2566
|
-
// src/audit/audit-store.ts
|
|
2567
|
-
import { mkdirSync } from "node:fs";
|
|
2568
|
-
import { dirname as dirname3 } from "node:path";
|
|
2569
|
-
import { DatabaseSync } from "node:sqlite";
|
|
2570
|
-
var AuditStore = class {
|
|
2571
|
-
database;
|
|
2572
|
-
constructor(path2) {
|
|
2573
|
-
mkdirSync(dirname3(path2), { recursive: true });
|
|
2574
|
-
this.database = new DatabaseSync(path2);
|
|
2575
|
-
this.database.exec(`
|
|
2576
|
-
CREATE TABLE IF NOT EXISTS audit_events (
|
|
2577
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
2578
|
-
recorded_at TEXT NOT NULL,
|
|
2579
|
-
event TEXT NOT NULL,
|
|
2580
|
-
command_id TEXT NOT NULL,
|
|
2581
|
-
session_id TEXT NOT NULL,
|
|
2582
|
-
details_json TEXT NOT NULL DEFAULT '{}'
|
|
2583
|
-
) STRICT
|
|
2584
|
-
`);
|
|
2585
|
-
const columns = this.database.prepare("PRAGMA table_info(audit_events)").all();
|
|
2586
|
-
if (!columns.some((column) => Reflect.get(column, "name") === "details_json")) {
|
|
2587
|
-
this.database.exec("ALTER TABLE audit_events ADD COLUMN details_json TEXT NOT NULL DEFAULT '{}'");
|
|
2588
|
-
}
|
|
2589
|
-
}
|
|
2590
|
-
record(record2) {
|
|
2591
|
-
this.database.prepare(
|
|
2592
|
-
`
|
|
2593
|
-
INSERT INTO audit_events (
|
|
2594
|
-
recorded_at,
|
|
2595
|
-
event,
|
|
2596
|
-
command_id,
|
|
2597
|
-
session_id,
|
|
2598
|
-
details_json
|
|
2599
|
-
) VALUES (?, ?, ?, ?, ?)
|
|
2600
|
-
`
|
|
2601
|
-
).run(
|
|
2602
|
-
(/* @__PURE__ */ new Date()).toISOString(),
|
|
2603
|
-
record2.event,
|
|
2604
|
-
record2.commandId,
|
|
2605
|
-
record2.sessionId,
|
|
2606
|
-
JSON.stringify(record2.details ?? {})
|
|
2607
|
-
);
|
|
2608
|
-
}
|
|
2609
|
-
close() {
|
|
2610
|
-
this.database.close();
|
|
2611
|
-
}
|
|
2612
|
-
};
|
|
2613
|
-
|
|
2614
2585
|
// src/bots/bot-message-command.ts
|
|
2615
2586
|
import { randomUUID } from "node:crypto";
|
|
2616
2587
|
import { createConnection } from "node:net";
|
|
@@ -2624,7 +2595,7 @@ var BotMessageCommandError = class extends Error {
|
|
|
2624
2595
|
this.name = "BotMessageCommandError";
|
|
2625
2596
|
}
|
|
2626
2597
|
};
|
|
2627
|
-
function
|
|
2598
|
+
function isRecord4(value) {
|
|
2628
2599
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2629
2600
|
}
|
|
2630
2601
|
function requiredEnvironment(environment, name) {
|
|
@@ -2675,14 +2646,14 @@ function decodeResponse(encoded) {
|
|
|
2675
2646
|
cause
|
|
2676
2647
|
});
|
|
2677
2648
|
}
|
|
2678
|
-
if (!
|
|
2649
|
+
if (!isRecord4(value) || typeof value["ok"] !== "boolean") {
|
|
2679
2650
|
throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data");
|
|
2680
2651
|
}
|
|
2681
2652
|
if (value["ok"] === true && typeof value["outboundMessageId"] === "string") {
|
|
2682
2653
|
return value["outboundMessageId"];
|
|
2683
2654
|
}
|
|
2684
2655
|
const error61 = value["error"];
|
|
2685
|
-
if (value["ok"] === false &&
|
|
2656
|
+
if (value["ok"] === false && isRecord4(error61) && typeof error61["code"] === "string" && typeof error61["message"] === "string") {
|
|
2686
2657
|
throw new BotMessageCommandError(error61["code"], error61["message"]);
|
|
2687
2658
|
}
|
|
2688
2659
|
throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data");
|
|
@@ -21743,7 +21714,9 @@ var sessionCapabilitiesSchema = external_exports.strictObject({
|
|
|
21743
21714
|
imagePrompt: external_exports.boolean(),
|
|
21744
21715
|
listSessions: external_exports.boolean(),
|
|
21745
21716
|
loadSession: external_exports.boolean(),
|
|
21717
|
+
promptQueueing: external_exports.boolean().default(false),
|
|
21746
21718
|
resumeSession: external_exports.boolean(),
|
|
21719
|
+
steering: external_exports.boolean().default(false),
|
|
21747
21720
|
unstableForkSession: external_exports.boolean().default(false)
|
|
21748
21721
|
}).readonly();
|
|
21749
21722
|
var sessionCommandSchema = external_exports.strictObject({
|
|
@@ -22085,6 +22058,11 @@ var AGENT_ACTIVITY_LOCATION_LIMIT = 12;
|
|
|
22085
22058
|
var AGENT_ACTIVITY_LOCATION_LENGTH_LIMIT = 1024;
|
|
22086
22059
|
var SESSION_COMPLETED_KIND = "session.completed";
|
|
22087
22060
|
var SESSION_FAILED_KIND = "session.failed";
|
|
22061
|
+
var SessionPromptDisposition = Object.freeze({
|
|
22062
|
+
Queue: "queue",
|
|
22063
|
+
Reject: "reject",
|
|
22064
|
+
Steer: "steer"
|
|
22065
|
+
});
|
|
22088
22066
|
|
|
22089
22067
|
// ../protocol/src/codecs/messages.ts
|
|
22090
22068
|
var IDENTIFIER_PATTERN2 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
|
|
@@ -22114,7 +22092,7 @@ var ProtocolMessageError = class extends Error {
|
|
|
22114
22092
|
this.name = "ProtocolMessageError";
|
|
22115
22093
|
}
|
|
22116
22094
|
};
|
|
22117
|
-
function
|
|
22095
|
+
function isRecord5(value) {
|
|
22118
22096
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22119
22097
|
}
|
|
22120
22098
|
function parseJson2(encoded) {
|
|
@@ -22126,7 +22104,7 @@ function parseJson2(encoded) {
|
|
|
22126
22104
|
cause: error61
|
|
22127
22105
|
});
|
|
22128
22106
|
}
|
|
22129
|
-
if (!
|
|
22107
|
+
if (!isRecord5(value)) {
|
|
22130
22108
|
throw new ProtocolMessageError("Message must be a JSON object");
|
|
22131
22109
|
}
|
|
22132
22110
|
return value;
|
|
@@ -22159,6 +22137,19 @@ function readAllowedString(value, key, allowedValues) {
|
|
|
22159
22137
|
}
|
|
22160
22138
|
return field;
|
|
22161
22139
|
}
|
|
22140
|
+
function readPromptDisposition(value) {
|
|
22141
|
+
const field = value["disposition"];
|
|
22142
|
+
if (field === void 0 || field === SessionPromptDisposition.Reject) {
|
|
22143
|
+
return SessionPromptDisposition.Reject;
|
|
22144
|
+
}
|
|
22145
|
+
if (field === SessionPromptDisposition.Queue) {
|
|
22146
|
+
return SessionPromptDisposition.Queue;
|
|
22147
|
+
}
|
|
22148
|
+
if (field === SessionPromptDisposition.Steer) {
|
|
22149
|
+
return SessionPromptDisposition.Steer;
|
|
22150
|
+
}
|
|
22151
|
+
throw new ProtocolMessageError("Invalid value field: disposition");
|
|
22152
|
+
}
|
|
22162
22153
|
function readSequence(value, key) {
|
|
22163
22154
|
const field = value[key];
|
|
22164
22155
|
if (typeof field !== "number" || !Number.isSafeInteger(field) || field < 0) {
|
|
@@ -22210,7 +22201,7 @@ function readActivityContent(value) {
|
|
|
22210
22201
|
}
|
|
22211
22202
|
function readActivityDetail(value) {
|
|
22212
22203
|
const field = value["detail"];
|
|
22213
|
-
if (!
|
|
22204
|
+
if (!isRecord5(field)) {
|
|
22214
22205
|
throw new ProtocolMessageError("Invalid activity detail field");
|
|
22215
22206
|
}
|
|
22216
22207
|
const kind = readString(field, "kind", 32);
|
|
@@ -22283,11 +22274,13 @@ function decodeSessionPromptCommand(encoded) {
|
|
|
22283
22274
|
"bindingId",
|
|
22284
22275
|
"commandId",
|
|
22285
22276
|
"directoryId",
|
|
22277
|
+
"disposition",
|
|
22286
22278
|
"images",
|
|
22287
22279
|
"kind",
|
|
22288
22280
|
"prompt",
|
|
22289
22281
|
"resources",
|
|
22290
|
-
"sessionId"
|
|
22282
|
+
"sessionId",
|
|
22283
|
+
"targetCommandId"
|
|
22291
22284
|
]);
|
|
22292
22285
|
assertKind(value, SESSION_PROMPT_KIND);
|
|
22293
22286
|
const prompt = readText(value, "prompt", MAX_PROMPT_LENGTH);
|
|
@@ -22300,17 +22293,25 @@ function decodeSessionPromptCommand(encoded) {
|
|
|
22300
22293
|
if (prompt.trim().length === 0 && images.length === 0 && resources.length === 0) {
|
|
22301
22294
|
throw new ProtocolMessageError("Prompt must contain content");
|
|
22302
22295
|
}
|
|
22296
|
+
const disposition = readPromptDisposition(value);
|
|
22297
|
+
const rawTargetCommandId = value["targetCommandId"];
|
|
22298
|
+
const targetCommandId = rawTargetCommandId === void 0 ? "" : readText(value, "targetCommandId", 128);
|
|
22299
|
+
if (disposition === SessionPromptDisposition.Reject && targetCommandId.length > 0 || disposition !== SessionPromptDisposition.Reject && !IDENTIFIER_PATTERN2.test(targetCommandId)) {
|
|
22300
|
+
throw new ProtocolMessageError("Prompt disposition has an invalid target command");
|
|
22301
|
+
}
|
|
22303
22302
|
return {
|
|
22304
22303
|
additionalDirectoryIds: readDirectoryIdentifiers(value, "additionalDirectoryIds", 16),
|
|
22305
22304
|
agentId: readIdentifier(value, "agentId"),
|
|
22306
22305
|
bindingId: readIdentifier(value, "bindingId"),
|
|
22307
22306
|
commandId: readIdentifier(value, "commandId"),
|
|
22308
22307
|
directoryId: readDirectoryIdentifier(value, "directoryId"),
|
|
22308
|
+
disposition,
|
|
22309
22309
|
images,
|
|
22310
22310
|
kind: SESSION_PROMPT_KIND,
|
|
22311
22311
|
prompt,
|
|
22312
22312
|
resources,
|
|
22313
|
-
sessionId: readIdentifier(value, "sessionId")
|
|
22313
|
+
sessionId: readIdentifier(value, "sessionId"),
|
|
22314
|
+
targetCommandId
|
|
22314
22315
|
};
|
|
22315
22316
|
}
|
|
22316
22317
|
function decodeSessionCancelCommand(encoded) {
|
|
@@ -22360,11 +22361,12 @@ function decodeSessionStartedEvent(encoded) {
|
|
|
22360
22361
|
}
|
|
22361
22362
|
function decodeAgentMessageDeltaEvent(encoded) {
|
|
22362
22363
|
const value = parseJson2(encoded);
|
|
22363
|
-
assertExactKeys(value, ["commandId", "kind", "sessionId", "text"]);
|
|
22364
|
+
assertExactKeys(value, ["commandId", "kind", "messageId", "sessionId", "text"]);
|
|
22364
22365
|
assertKind(value, AGENT_MESSAGE_DELTA_KIND);
|
|
22365
22366
|
return {
|
|
22366
22367
|
commandId: readIdentifier(value, "commandId"),
|
|
22367
22368
|
kind: AGENT_MESSAGE_DELTA_KIND,
|
|
22369
|
+
messageId: value["messageId"] === void 0 ? "" : readText(value, "messageId", 255),
|
|
22368
22370
|
sessionId: readIdentifier(value, "sessionId"),
|
|
22369
22371
|
text: readString(value, "text", MAX_EVENT_TEXT_LENGTH)
|
|
22370
22372
|
};
|
|
@@ -22629,7 +22631,7 @@ var BotSessionPromptCommand = class {
|
|
|
22629
22631
|
this.replyRoute = replyRoute;
|
|
22630
22632
|
}
|
|
22631
22633
|
};
|
|
22632
|
-
function
|
|
22634
|
+
function isRecord6(value) {
|
|
22633
22635
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22634
22636
|
}
|
|
22635
22637
|
function parseJson3(encoded) {
|
|
@@ -22639,7 +22641,7 @@ function parseJson3(encoded) {
|
|
|
22639
22641
|
} catch (cause) {
|
|
22640
22642
|
throw new ProtocolMessageError("Message is not valid JSON", { cause });
|
|
22641
22643
|
}
|
|
22642
|
-
if (!
|
|
22644
|
+
if (!isRecord6(value)) {
|
|
22643
22645
|
throw new ProtocolMessageError("Message must be a JSON object");
|
|
22644
22646
|
}
|
|
22645
22647
|
return value;
|
|
@@ -22724,7 +22726,7 @@ function readTargets(value, key) {
|
|
|
22724
22726
|
const identifiers = /* @__PURE__ */ new Set();
|
|
22725
22727
|
const aliases = /* @__PURE__ */ new Set();
|
|
22726
22728
|
for (const candidate of field) {
|
|
22727
|
-
if (!
|
|
22729
|
+
if (!isRecord6(candidate)) {
|
|
22728
22730
|
throw new ProtocolMessageError(`Invalid Bot delivery targets: ${key}`);
|
|
22729
22731
|
}
|
|
22730
22732
|
assertExactKeys2(candidate, ["alias", "deliveryTargetId"]);
|
|
@@ -22740,7 +22742,7 @@ function readTargets(value, key) {
|
|
|
22740
22742
|
return Object.freeze(targets);
|
|
22741
22743
|
}
|
|
22742
22744
|
function readCompletionPolicy(value) {
|
|
22743
|
-
if (!
|
|
22745
|
+
if (!isRecord6(value)) {
|
|
22744
22746
|
throw new ProtocolMessageError("Invalid Bot completion policy");
|
|
22745
22747
|
}
|
|
22746
22748
|
const kind = readText2(value, "kind", 32, false);
|
|
@@ -22758,7 +22760,7 @@ function readCompletionPolicy(value) {
|
|
|
22758
22760
|
throw new ProtocolMessageError("Invalid Bot completion policy");
|
|
22759
22761
|
}
|
|
22760
22762
|
function readReplyRoute(value) {
|
|
22761
|
-
if (!
|
|
22763
|
+
if (!isRecord6(value)) {
|
|
22762
22764
|
throw new ProtocolMessageError("Invalid Bot reply route");
|
|
22763
22765
|
}
|
|
22764
22766
|
const kind = readText2(value, "kind", 32, false);
|
|
@@ -23010,6 +23012,8 @@ var sessionHistorySchema = external_exports.discriminatedUnion("status", [
|
|
|
23010
23012
|
var NATIVE_SESSION_REQUEST_KIND = "native.session.request";
|
|
23011
23013
|
var NATIVE_SESSION_RESULT_KIND = "native.session.result";
|
|
23012
23014
|
var NATIVE_SESSION_CANCEL_KIND = "native.session.cancel";
|
|
23015
|
+
var NATIVE_SESSION_TURN_FORK_REQUEST_KIND = "native.session.turn-fork.request";
|
|
23016
|
+
var NATIVE_SESSION_TURN_FORK_KIND = "native.session.turn-fork";
|
|
23013
23017
|
var identifier = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/);
|
|
23014
23018
|
var directoryId = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,2047}$/);
|
|
23015
23019
|
var envelope = {
|
|
@@ -23024,6 +23028,28 @@ var identity = {
|
|
|
23024
23028
|
bindingId: identifier,
|
|
23025
23029
|
directoryId
|
|
23026
23030
|
};
|
|
23031
|
+
var turnForkIdentity = {
|
|
23032
|
+
additionalDirectoryIds: external_exports.array(directoryId).max(16).readonly(),
|
|
23033
|
+
agentId: identifier,
|
|
23034
|
+
bindingId: identifier,
|
|
23035
|
+
directoryId,
|
|
23036
|
+
requestId: identifier,
|
|
23037
|
+
sessionId: identifier,
|
|
23038
|
+
sourceCommandId: identifier,
|
|
23039
|
+
targetSessionId: identifier
|
|
23040
|
+
};
|
|
23041
|
+
var nativeSessionTurnForkRequestSchema = external_exports.strictObject({
|
|
23042
|
+
...turnForkIdentity,
|
|
23043
|
+
kind: external_exports.literal(NATIVE_SESSION_TURN_FORK_REQUEST_KIND)
|
|
23044
|
+
}).readonly();
|
|
23045
|
+
var nativeSessionTurnForkCommandSchema = external_exports.strictObject({
|
|
23046
|
+
...turnForkIdentity,
|
|
23047
|
+
kind: external_exports.literal(NATIVE_SESSION_TURN_FORK_KIND),
|
|
23048
|
+
messageFingerprint: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
23049
|
+
messageId: external_exports.string().min(1).max(255),
|
|
23050
|
+
messageOccurrence: external_exports.number().int().positive(),
|
|
23051
|
+
title: external_exports.string().max(500)
|
|
23052
|
+
}).readonly();
|
|
23027
23053
|
var nativeSessionCommandSchema = external_exports.discriminatedUnion("action", [
|
|
23028
23054
|
external_exports.strictObject({
|
|
23029
23055
|
...identity,
|
|
@@ -23098,6 +23124,7 @@ function decode4(schema, encoded) {
|
|
|
23098
23124
|
var decodeNativeSessionCommand = (encoded) => decode4(nativeSessionCommandSchema, encoded);
|
|
23099
23125
|
var encodeNativeSessionResult = (value) => JSON.stringify(nativeSessionResultSchema.parse(value));
|
|
23100
23126
|
var decodeNativeSessionCancel = (encoded) => decode4(nativeSessionCancelSchema, encoded);
|
|
23127
|
+
var decodeNativeSessionTurnForkCommand = (encoded) => decode4(nativeSessionTurnForkCommandSchema, encoded);
|
|
23101
23128
|
|
|
23102
23129
|
// ../protocol/src/session-events.ts
|
|
23103
23130
|
var SESSION_EVENT_KIND = "session.event";
|
|
@@ -23183,9 +23210,9 @@ var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAckno
|
|
|
23183
23210
|
var decodeSessionEventRejection = (encoded) => decode5(encoded, eventRejectionSchema);
|
|
23184
23211
|
|
|
23185
23212
|
// src/config.ts
|
|
23186
|
-
function loadDefaultWorkspace() {
|
|
23187
|
-
const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } =
|
|
23188
|
-
return configuredWorkspace ?? join13(readStateDirectory(), "workspace");
|
|
23213
|
+
function loadDefaultWorkspace(environment = process.env) {
|
|
23214
|
+
const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = environment;
|
|
23215
|
+
return configuredWorkspace ?? join13(readStateDirectory(environment), "workspace");
|
|
23189
23216
|
}
|
|
23190
23217
|
function createDaemonConfig(connection, agents = [], installableAgents = []) {
|
|
23191
23218
|
return {
|
|
@@ -23212,7 +23239,9 @@ var IDENTIFIER_PATTERN5 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
|
|
|
23212
23239
|
var TOKEN_PATTERN2 = /^[a-zA-Z0-9_-]{32,256}$/;
|
|
23213
23240
|
var MAX_CONNECTION_FILE_LENGTH = 16 * 1024;
|
|
23214
23241
|
var CLAIM_RESPONSE_KEYS = ["channelId", "daemonToken", "runtimeId"];
|
|
23215
|
-
var
|
|
23242
|
+
var REDEEM_RESPONSE_KEYS = ["channelId", "runtimeCredential", "runtimeId"];
|
|
23243
|
+
var LEGACY_SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "daemonToken", "relayOrigin"];
|
|
23244
|
+
var SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "relayOrigin", "runtimeCredential"];
|
|
23216
23245
|
var PairingClientError = class extends Error {
|
|
23217
23246
|
code;
|
|
23218
23247
|
constructor(code, message, options = {}) {
|
|
@@ -23221,7 +23250,7 @@ var PairingClientError = class extends Error {
|
|
|
23221
23250
|
this.name = "PairingClientError";
|
|
23222
23251
|
}
|
|
23223
23252
|
};
|
|
23224
|
-
function
|
|
23253
|
+
function isRecord7(value) {
|
|
23225
23254
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23226
23255
|
}
|
|
23227
23256
|
function hasExactKeys(value, expectedKeys) {
|
|
@@ -23249,7 +23278,7 @@ function readSingleOption(arguments_, option2) {
|
|
|
23249
23278
|
function assertKnownArguments(arguments_) {
|
|
23250
23279
|
for (let index = 0; index < arguments_.length; index += 2) {
|
|
23251
23280
|
const option2 = arguments_[index];
|
|
23252
|
-
if (option2 !== "--url" && option2 !== "--code" && option2 !== "--workspace") {
|
|
23281
|
+
if (option2 !== "--url" && option2 !== "--code" && option2 !== "--pairing" && option2 !== "--workspace") {
|
|
23253
23282
|
throw new PairingClientError("invalid_arguments", `Unknown pairing option: ${option2 ?? "missing option"}`);
|
|
23254
23283
|
}
|
|
23255
23284
|
}
|
|
@@ -23289,10 +23318,17 @@ function parsePairCommand(arguments_) {
|
|
|
23289
23318
|
assertKnownArguments(arguments_);
|
|
23290
23319
|
return {
|
|
23291
23320
|
code: parsePairingCode(readSingleOption(arguments_, "--code")),
|
|
23321
|
+
pairingId: arguments_.includes("--pairing") ? validatePairingId(readSingleOption(arguments_, "--pairing")) : "",
|
|
23292
23322
|
relayOrigin: parseRelayOrigin(readSingleOption(arguments_, "--url")),
|
|
23293
23323
|
workspace: arguments_.includes("--workspace") ? readSingleOption(arguments_, "--workspace") : loadDefaultWorkspace()
|
|
23294
23324
|
};
|
|
23295
23325
|
}
|
|
23326
|
+
function validatePairingId(value) {
|
|
23327
|
+
if (!IDENTIFIER_PATTERN5.test(value)) {
|
|
23328
|
+
throw new PairingClientError("invalid_pairing_id", "The Runtime pairing ID is invalid");
|
|
23329
|
+
}
|
|
23330
|
+
return value;
|
|
23331
|
+
}
|
|
23296
23332
|
function parseChannelArgument(arguments_) {
|
|
23297
23333
|
if (arguments_.length !== 2 || arguments_[0] !== "--channel") {
|
|
23298
23334
|
throw new PairingClientError("invalid_arguments", "Usage: mobius start --channel <channel-id>");
|
|
@@ -23337,9 +23373,9 @@ async function readJsonResponse(response) {
|
|
|
23337
23373
|
}
|
|
23338
23374
|
}
|
|
23339
23375
|
function readRelayError(value) {
|
|
23340
|
-
if (
|
|
23376
|
+
if (isRecord7(value)) {
|
|
23341
23377
|
const error61 = value["error"];
|
|
23342
|
-
if (
|
|
23378
|
+
if (isRecord7(error61)) {
|
|
23343
23379
|
const code = error61["code"];
|
|
23344
23380
|
const message = error61["message"];
|
|
23345
23381
|
if (typeof code === "string" && typeof message === "string") {
|
|
@@ -23349,17 +23385,17 @@ function readRelayError(value) {
|
|
|
23349
23385
|
}
|
|
23350
23386
|
return new PairingClientError("pairing_failed", "The relay could not pair this machine");
|
|
23351
23387
|
}
|
|
23352
|
-
function
|
|
23353
|
-
if (!
|
|
23388
|
+
function decodePairingResponse(value, credentialField, expectedKeys) {
|
|
23389
|
+
if (!isRecord7(value) || !hasExactKeys(value, expectedKeys)) {
|
|
23354
23390
|
throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
|
|
23355
23391
|
}
|
|
23356
23392
|
const channelId = value["channelId"];
|
|
23357
|
-
const
|
|
23393
|
+
const runtimeCredential = value[credentialField];
|
|
23358
23394
|
const runtimeId = value["runtimeId"];
|
|
23359
|
-
if (typeof channelId !== "string" || !IDENTIFIER_PATTERN5.test(channelId) || typeof
|
|
23395
|
+
if (typeof channelId !== "string" || !IDENTIFIER_PATTERN5.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof runtimeId !== "string" || !IDENTIFIER_PATTERN5.test(runtimeId)) {
|
|
23360
23396
|
throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
|
|
23361
23397
|
}
|
|
23362
|
-
return { channelId,
|
|
23398
|
+
return { channelId, runtimeCredential, runtimeId };
|
|
23363
23399
|
}
|
|
23364
23400
|
async function claimPairing(options, defaultWorkspace, localAgents, installableAgents, signal) {
|
|
23365
23401
|
const agents = localAgents.map((agent) => ({
|
|
@@ -23369,7 +23405,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
|
|
|
23369
23405
|
id: agent.id,
|
|
23370
23406
|
label: agent.label
|
|
23371
23407
|
}));
|
|
23372
|
-
const endpoint = mobiusApiUrl(options.relayOrigin, "/pairings/claim");
|
|
23408
|
+
const endpoint = options.pairingId.length > 0 ? mobiusApiUrl(options.relayOrigin, `/v1/runtime-pairings/${encodeURIComponent(options.pairingId)}/redeem`) : mobiusApiUrl(options.relayOrigin, "/pairings/claim");
|
|
23373
23409
|
let response;
|
|
23374
23410
|
try {
|
|
23375
23411
|
response = await fetch2(endpoint, {
|
|
@@ -23392,10 +23428,10 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
|
|
|
23392
23428
|
if (!response.ok) {
|
|
23393
23429
|
throw readRelayError(value);
|
|
23394
23430
|
}
|
|
23395
|
-
return
|
|
23431
|
+
return options.pairingId.length > 0 ? decodePairingResponse(value, "runtimeCredential", REDEEM_RESPONSE_KEYS) : decodePairingResponse(value, "daemonToken", CLAIM_RESPONSE_KEYS);
|
|
23396
23432
|
}
|
|
23397
|
-
async function persistConnection(channelId, relayOrigin,
|
|
23398
|
-
const connectionsDirectory = join14(readStateDirectory(), "connections");
|
|
23433
|
+
async function persistConnection(channelId, relayOrigin, runtimeCredential, defaultWorkspace, environment) {
|
|
23434
|
+
const connectionsDirectory = join14(readStateDirectory(environment), "connections");
|
|
23399
23435
|
await mkdir3(connectionsDirectory, { mode: 448, recursive: true });
|
|
23400
23436
|
const connectionPath = join14(connectionsDirectory, `${channelId}.json`);
|
|
23401
23437
|
const temporaryPath = join14(connectionsDirectory, `.${channelId}.${randomUUID2()}.tmp`);
|
|
@@ -23405,8 +23441,8 @@ async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWo
|
|
|
23405
23441
|
{
|
|
23406
23442
|
defaultWorkspace,
|
|
23407
23443
|
channelId,
|
|
23408
|
-
|
|
23409
|
-
|
|
23444
|
+
relayOrigin: relayOrigin.toString(),
|
|
23445
|
+
runtimeCredential
|
|
23410
23446
|
},
|
|
23411
23447
|
null,
|
|
23412
23448
|
2
|
|
@@ -23418,25 +23454,25 @@ async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWo
|
|
|
23418
23454
|
return connectionPath;
|
|
23419
23455
|
}
|
|
23420
23456
|
function decodeSavedConnection(value, expectedChannelId) {
|
|
23421
|
-
if (!
|
|
23457
|
+
if (!isRecord7(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS) && !hasExactKeys(value, LEGACY_SAVED_CONNECTION_KEYS)) {
|
|
23422
23458
|
throw new PairingClientError("invalid_connection", "The saved connection is invalid");
|
|
23423
23459
|
}
|
|
23424
23460
|
const defaultWorkspace = value["defaultWorkspace"];
|
|
23425
23461
|
const channelId = value["channelId"];
|
|
23426
|
-
const
|
|
23462
|
+
const runtimeCredential = value["runtimeCredential"] ?? value["daemonToken"];
|
|
23427
23463
|
const relayOrigin = value["relayOrigin"];
|
|
23428
|
-
if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN5.test(channelId) || typeof
|
|
23464
|
+
if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN5.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof relayOrigin !== "string") {
|
|
23429
23465
|
throw new PairingClientError("invalid_connection", "The saved connection is invalid");
|
|
23430
23466
|
}
|
|
23431
23467
|
return {
|
|
23432
23468
|
defaultWorkspace,
|
|
23433
23469
|
channelId,
|
|
23434
|
-
|
|
23435
|
-
|
|
23470
|
+
relayOrigin: parseRelayOrigin(relayOrigin),
|
|
23471
|
+
runtimeCredential
|
|
23436
23472
|
};
|
|
23437
23473
|
}
|
|
23438
|
-
async function readSavedConnection(channelId) {
|
|
23439
|
-
const connectionPath = join14(readStateDirectory(), "connections", `${channelId}.json`);
|
|
23474
|
+
async function readSavedConnection(channelId, environment) {
|
|
23475
|
+
const connectionPath = join14(readStateDirectory(environment), "connections", `${channelId}.json`);
|
|
23440
23476
|
let encoded = "";
|
|
23441
23477
|
const handle = await open4(connectionPath, "r");
|
|
23442
23478
|
try {
|
|
@@ -23458,8 +23494,8 @@ async function readSavedConnection(channelId) {
|
|
|
23458
23494
|
return {
|
|
23459
23495
|
defaultWorkspace: await resolveDefaultWorkspace(connection.defaultWorkspace),
|
|
23460
23496
|
channelId: connection.channelId,
|
|
23461
|
-
|
|
23462
|
-
|
|
23497
|
+
relayOrigin: connection.relayOrigin,
|
|
23498
|
+
runtimeCredential: connection.runtimeCredential
|
|
23463
23499
|
};
|
|
23464
23500
|
}
|
|
23465
23501
|
function assertInventoryUsable(inventory) {
|
|
@@ -23467,15 +23503,12 @@ function assertInventoryUsable(inventory) {
|
|
|
23467
23503
|
throw new PairingClientError("no_supported_agent", "No supported Agent is available for local discovery");
|
|
23468
23504
|
}
|
|
23469
23505
|
}
|
|
23470
|
-
function buildPairedDaemon(connection, connectionPath, inventory) {
|
|
23506
|
+
function buildPairedDaemon(connection, connectionPath, inventory, environment) {
|
|
23471
23507
|
assertInventoryUsable(inventory);
|
|
23472
|
-
const relayUrl = mobiusApiUrl(
|
|
23473
|
-
connection.relayOrigin,
|
|
23474
|
-
`/channels/${encodeURIComponent(connection.channelId)}/daemon`
|
|
23475
|
-
);
|
|
23508
|
+
const relayUrl = mobiusApiUrl(connection.relayOrigin, "/v1/runtimes/connect");
|
|
23476
23509
|
relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
23477
|
-
relayUrl.searchParams.set("token", connection.
|
|
23478
|
-
const auditDatabasePath = join14(readStateDirectory(), "audit", `${connection.channelId}.sqlite`);
|
|
23510
|
+
relayUrl.searchParams.set("token", connection.runtimeCredential);
|
|
23511
|
+
const auditDatabasePath = join14(readStateDirectory(environment), "audit", `${connection.channelId}.sqlite`);
|
|
23479
23512
|
return {
|
|
23480
23513
|
channelUrl: new URL(`/channels/${encodeURIComponent(connection.channelId)}`, connection.relayOrigin).toString(),
|
|
23481
23514
|
config: createDaemonConfig(
|
|
@@ -23491,12 +23524,11 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
|
|
|
23491
23524
|
connectionPath
|
|
23492
23525
|
};
|
|
23493
23526
|
}
|
|
23494
|
-
async function
|
|
23495
|
-
const options = parsePairCommand(arguments_);
|
|
23527
|
+
async function pairRuntime(options, signal, environment) {
|
|
23496
23528
|
const defaultWorkspace = await resolveDefaultWorkspace(options.workspace);
|
|
23497
23529
|
const inventory = await discoverLocalAgentInventory({
|
|
23498
23530
|
defaultWorkspace,
|
|
23499
|
-
environment
|
|
23531
|
+
environment,
|
|
23500
23532
|
signal
|
|
23501
23533
|
});
|
|
23502
23534
|
assertInventoryUsable(inventory);
|
|
@@ -23505,35 +23537,91 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
|
|
|
23505
23537
|
const connectionPath = await persistConnection(
|
|
23506
23538
|
claim2.channelId,
|
|
23507
23539
|
options.relayOrigin,
|
|
23508
|
-
claim2.
|
|
23509
|
-
defaultWorkspace
|
|
23540
|
+
claim2.runtimeCredential,
|
|
23541
|
+
defaultWorkspace,
|
|
23542
|
+
environment
|
|
23510
23543
|
);
|
|
23511
23544
|
return buildPairedDaemon(
|
|
23512
23545
|
{
|
|
23513
23546
|
defaultWorkspace,
|
|
23514
23547
|
channelId: claim2.channelId,
|
|
23515
|
-
|
|
23516
|
-
|
|
23548
|
+
relayOrigin: options.relayOrigin,
|
|
23549
|
+
runtimeCredential: claim2.runtimeCredential
|
|
23517
23550
|
},
|
|
23518
23551
|
connectionPath,
|
|
23519
|
-
inventory
|
|
23552
|
+
inventory,
|
|
23553
|
+
environment
|
|
23520
23554
|
);
|
|
23521
23555
|
}
|
|
23522
|
-
async function
|
|
23523
|
-
|
|
23524
|
-
|
|
23525
|
-
|
|
23556
|
+
async function pairLocalMachine(arguments_, signal = new AbortController().signal) {
|
|
23557
|
+
return await pairRuntime(parsePairCommand(arguments_), signal, process.env);
|
|
23558
|
+
}
|
|
23559
|
+
async function resumeMobiusRuntime(channelId, signal, environment = process.env) {
|
|
23560
|
+
const normalizedChannelId = validatePairingId(channelId);
|
|
23561
|
+
const connectionPath = join14(readStateDirectory(environment), "connections", `${normalizedChannelId}.json`);
|
|
23562
|
+
const connection = await readSavedConnection(normalizedChannelId, environment);
|
|
23526
23563
|
const inventory = await discoverLocalAgentInventory({
|
|
23527
23564
|
defaultWorkspace: connection.defaultWorkspace,
|
|
23528
|
-
environment
|
|
23565
|
+
environment,
|
|
23529
23566
|
signal
|
|
23530
23567
|
});
|
|
23531
|
-
return buildPairedDaemon(connection, connectionPath, inventory);
|
|
23568
|
+
return buildPairedDaemon(connection, connectionPath, inventory, environment);
|
|
23569
|
+
}
|
|
23570
|
+
async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
|
|
23571
|
+
return await resumeMobiusRuntime(parseChannelArgument(arguments_), signal, process.env);
|
|
23532
23572
|
}
|
|
23533
23573
|
|
|
23574
|
+
// src/audit/audit-store.ts
|
|
23575
|
+
import { mkdirSync } from "node:fs";
|
|
23576
|
+
import { dirname as dirname3 } from "node:path";
|
|
23577
|
+
import { DatabaseSync } from "node:sqlite";
|
|
23578
|
+
var AuditStore = class {
|
|
23579
|
+
database;
|
|
23580
|
+
constructor(path2) {
|
|
23581
|
+
mkdirSync(dirname3(path2), { recursive: true });
|
|
23582
|
+
this.database = new DatabaseSync(path2);
|
|
23583
|
+
this.database.exec(`
|
|
23584
|
+
CREATE TABLE IF NOT EXISTS audit_events (
|
|
23585
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
23586
|
+
recorded_at TEXT NOT NULL,
|
|
23587
|
+
event TEXT NOT NULL,
|
|
23588
|
+
command_id TEXT NOT NULL,
|
|
23589
|
+
session_id TEXT NOT NULL,
|
|
23590
|
+
details_json TEXT NOT NULL DEFAULT '{}'
|
|
23591
|
+
) STRICT
|
|
23592
|
+
`);
|
|
23593
|
+
const columns = this.database.prepare("PRAGMA table_info(audit_events)").all();
|
|
23594
|
+
if (!columns.some((column) => Reflect.get(column, "name") === "details_json")) {
|
|
23595
|
+
this.database.exec("ALTER TABLE audit_events ADD COLUMN details_json TEXT NOT NULL DEFAULT '{}'");
|
|
23596
|
+
}
|
|
23597
|
+
}
|
|
23598
|
+
record(record2) {
|
|
23599
|
+
this.database.prepare(
|
|
23600
|
+
`
|
|
23601
|
+
INSERT INTO audit_events (
|
|
23602
|
+
recorded_at,
|
|
23603
|
+
event,
|
|
23604
|
+
command_id,
|
|
23605
|
+
session_id,
|
|
23606
|
+
details_json
|
|
23607
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
23608
|
+
`
|
|
23609
|
+
).run(
|
|
23610
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
23611
|
+
record2.event,
|
|
23612
|
+
record2.commandId,
|
|
23613
|
+
record2.sessionId,
|
|
23614
|
+
JSON.stringify(record2.details ?? {})
|
|
23615
|
+
);
|
|
23616
|
+
}
|
|
23617
|
+
close() {
|
|
23618
|
+
this.database.close();
|
|
23619
|
+
}
|
|
23620
|
+
};
|
|
23621
|
+
|
|
23534
23622
|
// src/run-daemon.ts
|
|
23535
23623
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
23536
|
-
import { basename as
|
|
23624
|
+
import { basename as basename4, join as join16 } from "node:path";
|
|
23537
23625
|
import { styleText } from "node:util";
|
|
23538
23626
|
import { fetch as fetch6, WebSocket as WebSocket2 } from "undici";
|
|
23539
23627
|
|
|
@@ -23548,7 +23636,7 @@ import {
|
|
|
23548
23636
|
|
|
23549
23637
|
// src/acp/activity.ts
|
|
23550
23638
|
var TRUNCATION_MARKER = "\n\u2026 output truncated by Mobius";
|
|
23551
|
-
function
|
|
23639
|
+
function isRecord8(value) {
|
|
23552
23640
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23553
23641
|
}
|
|
23554
23642
|
var AcpActivity = class _AcpActivity {
|
|
@@ -23815,7 +23903,7 @@ function contentPhase(content) {
|
|
|
23815
23903
|
return "";
|
|
23816
23904
|
}
|
|
23817
23905
|
const codex = metadata["codex"];
|
|
23818
|
-
if (!
|
|
23906
|
+
if (!isRecord8(codex)) {
|
|
23819
23907
|
return "";
|
|
23820
23908
|
}
|
|
23821
23909
|
const phase = codex["phase"];
|
|
@@ -24053,7 +24141,7 @@ var SessionConfigProtocolError = class extends Error {
|
|
|
24053
24141
|
this.name = "SessionConfigProtocolError";
|
|
24054
24142
|
}
|
|
24055
24143
|
};
|
|
24056
|
-
function
|
|
24144
|
+
function isRecord9(value) {
|
|
24057
24145
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24058
24146
|
}
|
|
24059
24147
|
function readString2(value, fallback = "") {
|
|
@@ -24080,7 +24168,7 @@ function normalizeConfigValues(value) {
|
|
|
24080
24168
|
}
|
|
24081
24169
|
const normalized = [];
|
|
24082
24170
|
for (const entry of value) {
|
|
24083
|
-
if (!
|
|
24171
|
+
if (!isRecord9(entry)) {
|
|
24084
24172
|
continue;
|
|
24085
24173
|
}
|
|
24086
24174
|
const groupedOptions = entry["options"];
|
|
@@ -24088,7 +24176,7 @@ function normalizeConfigValues(value) {
|
|
|
24088
24176
|
const groupId = readString2(entry["group"]);
|
|
24089
24177
|
const groupName = readString2(entry["name"]);
|
|
24090
24178
|
for (const groupedOption of groupedOptions) {
|
|
24091
|
-
if (
|
|
24179
|
+
if (isRecord9(groupedOption)) {
|
|
24092
24180
|
normalized.push(normalizeConfigValue(groupedOption, groupId, groupName));
|
|
24093
24181
|
}
|
|
24094
24182
|
}
|
|
@@ -24102,7 +24190,7 @@ function normalizeConfigOptions(value, modes) {
|
|
|
24102
24190
|
const normalized = [];
|
|
24103
24191
|
if (Array.isArray(value)) {
|
|
24104
24192
|
for (const option2 of value) {
|
|
24105
|
-
if (!
|
|
24193
|
+
if (!isRecord9(option2)) {
|
|
24106
24194
|
continue;
|
|
24107
24195
|
}
|
|
24108
24196
|
const id = truncate2(readString2(option2["id"]), 128);
|
|
@@ -24141,12 +24229,12 @@ function normalizeConfigOptions(value, modes) {
|
|
|
24141
24229
|
}
|
|
24142
24230
|
}
|
|
24143
24231
|
}
|
|
24144
|
-
if (
|
|
24232
|
+
if (isRecord9(modes)) {
|
|
24145
24233
|
const values = [];
|
|
24146
24234
|
const availableModes = modes["availableModes"];
|
|
24147
24235
|
if (Array.isArray(availableModes)) {
|
|
24148
24236
|
for (const mode of availableModes) {
|
|
24149
|
-
if (!
|
|
24237
|
+
if (!isRecord9(mode)) {
|
|
24150
24238
|
continue;
|
|
24151
24239
|
}
|
|
24152
24240
|
const id = truncate2(readString2(mode["id"]), 500);
|
|
@@ -24224,7 +24312,7 @@ function ensureReasoningEffortOption(current) {
|
|
|
24224
24312
|
return [...current.slice(0, MAXIMUM_CONFIG_OPTIONS - 2), model, unavailable];
|
|
24225
24313
|
}
|
|
24226
24314
|
function normalizeStandardLifecycle(response) {
|
|
24227
|
-
const lifecycle =
|
|
24315
|
+
const lifecycle = isRecord9(response) ? response : {};
|
|
24228
24316
|
return ensureReasoningEffortOption(normalizeConfigOptions(lifecycle["configOptions"], lifecycle["modes"]));
|
|
24229
24317
|
}
|
|
24230
24318
|
function replaceStandardConfigOptions(current, value) {
|
|
@@ -24266,7 +24354,7 @@ var StandardSessionConfigProtocol = class {
|
|
|
24266
24354
|
"ACP agent exited while changing session configuration",
|
|
24267
24355
|
change.signal
|
|
24268
24356
|
);
|
|
24269
|
-
if (!
|
|
24357
|
+
if (!isRecord9(response) || !Array.isArray(response["configOptions"])) {
|
|
24270
24358
|
throw new SessionConfigProtocolError("The Agent returned an invalid session configuration response");
|
|
24271
24359
|
}
|
|
24272
24360
|
return replaceStandardConfigOptions(current, response["configOptions"]);
|
|
@@ -24280,18 +24368,18 @@ var standardSessionConfigProtocol = new StandardSessionConfigProtocol();
|
|
|
24280
24368
|
var LEGACY_MODEL_CONFIG_ID = "grok_build_legacy_model";
|
|
24281
24369
|
var LEGACY_REASONING_EFFORT_CONFIG_ID = "grok_build_legacy_reasoning_effort";
|
|
24282
24370
|
function legacyOptions(response) {
|
|
24283
|
-
if (!
|
|
24371
|
+
if (!isRecord9(response)) {
|
|
24284
24372
|
return [];
|
|
24285
24373
|
}
|
|
24286
24374
|
const metadata = response["_meta"];
|
|
24287
|
-
if (!
|
|
24375
|
+
if (!isRecord9(metadata)) {
|
|
24288
24376
|
return [];
|
|
24289
24377
|
}
|
|
24290
24378
|
const sessionConfig = metadata["x.ai/sessionConfig"];
|
|
24291
|
-
if (!
|
|
24379
|
+
if (!isRecord9(sessionConfig) || !Array.isArray(sessionConfig["options"])) {
|
|
24292
24380
|
return [];
|
|
24293
24381
|
}
|
|
24294
|
-
return sessionConfig["options"].slice(0, 500).filter(
|
|
24382
|
+
return sessionConfig["options"].slice(0, 500).filter(isRecord9);
|
|
24295
24383
|
}
|
|
24296
24384
|
function legacyValue(option2) {
|
|
24297
24385
|
const value = readString2(option2["id"]);
|
|
@@ -24304,7 +24392,7 @@ function legacyValue(option2) {
|
|
|
24304
24392
|
};
|
|
24305
24393
|
}
|
|
24306
24394
|
function modelsRecord(response) {
|
|
24307
|
-
if (!
|
|
24395
|
+
if (!isRecord9(response) || !isRecord9(response["models"])) {
|
|
24308
24396
|
return {};
|
|
24309
24397
|
}
|
|
24310
24398
|
return response["models"];
|
|
@@ -24323,7 +24411,7 @@ function legacyModelOptions(response) {
|
|
|
24323
24411
|
}
|
|
24324
24412
|
if (values.length === 0 && Array.isArray(models["availableModels"])) {
|
|
24325
24413
|
for (const model of models["availableModels"]) {
|
|
24326
|
-
if (!
|
|
24414
|
+
if (!isRecord9(model)) {
|
|
24327
24415
|
continue;
|
|
24328
24416
|
}
|
|
24329
24417
|
const value = readString2(model["modelId"]).slice(0, 500);
|
|
@@ -24403,7 +24491,7 @@ function legacyReasoningEffortOptions(response) {
|
|
|
24403
24491
|
return [result];
|
|
24404
24492
|
}
|
|
24405
24493
|
function normalizeGrokLifecycle(response) {
|
|
24406
|
-
const lifecycle =
|
|
24494
|
+
const lifecycle = isRecord9(response) ? response : {};
|
|
24407
24495
|
const standard = normalizeConfigOptions(lifecycle["configOptions"], lifecycle["modes"]);
|
|
24408
24496
|
const normalized = [...standard];
|
|
24409
24497
|
if (!normalized.some((option2) => option2.category === "model")) {
|
|
@@ -24422,11 +24510,11 @@ function updateCurrentValue2(current, category, value) {
|
|
|
24422
24510
|
return current.map((option2) => option2.category === category ? { ...option2, currentValue: value } : option2);
|
|
24423
24511
|
}
|
|
24424
24512
|
function validateGrokResponse(response, modelId) {
|
|
24425
|
-
if (!
|
|
24513
|
+
if (!isRecord9(response) || !isRecord9(response["_meta"])) {
|
|
24426
24514
|
throw new SessionConfigProtocolError("Grok Build returned an invalid session configuration response");
|
|
24427
24515
|
}
|
|
24428
24516
|
const model = response["_meta"]["model"];
|
|
24429
|
-
const appliedModel =
|
|
24517
|
+
const appliedModel = isRecord9(model) ? model["Ok"] : model;
|
|
24430
24518
|
if (appliedModel !== modelId) {
|
|
24431
24519
|
throw new SessionConfigProtocolError("Grok Build returned an invalid session configuration response");
|
|
24432
24520
|
}
|
|
@@ -24869,7 +24957,7 @@ function createWritableAcpStream(output2) {
|
|
|
24869
24957
|
function truncate3(value, maximumLength) {
|
|
24870
24958
|
return value.length <= maximumLength ? value : `${value.slice(0, Math.max(0, maximumLength - 1))}\u2026`;
|
|
24871
24959
|
}
|
|
24872
|
-
function
|
|
24960
|
+
function isRecord10(value) {
|
|
24873
24961
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24874
24962
|
}
|
|
24875
24963
|
function readString4(value, fallback = "") {
|
|
@@ -24878,6 +24966,11 @@ function readString4(value, fallback = "") {
|
|
|
24878
24966
|
function arraysEqual(left, right) {
|
|
24879
24967
|
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
24880
24968
|
}
|
|
24969
|
+
function recordsEqual(left, right) {
|
|
24970
|
+
const leftEntries = Object.entries(left);
|
|
24971
|
+
const rightEntries = Object.entries(right);
|
|
24972
|
+
return leftEntries.length === rightEntries.length && leftEntries.every(([name, value]) => right[name] === value);
|
|
24973
|
+
}
|
|
24881
24974
|
function elicitationFieldType(value) {
|
|
24882
24975
|
if (value === "boolean") {
|
|
24883
24976
|
return "boolean";
|
|
@@ -24898,7 +24991,7 @@ function normalizeElicitationOptions(schema) {
|
|
|
24898
24991
|
const oneOf = schema["oneOf"];
|
|
24899
24992
|
if (Array.isArray(oneOf)) {
|
|
24900
24993
|
for (const option2 of oneOf) {
|
|
24901
|
-
if (
|
|
24994
|
+
if (isRecord10(option2) && typeof option2["const"] === "string") {
|
|
24902
24995
|
normalized.push({
|
|
24903
24996
|
description: truncate3(readString4(option2["description"]), 500),
|
|
24904
24997
|
label: truncate3(readString4(option2["title"], option2["const"]), 255),
|
|
@@ -24918,7 +25011,7 @@ function normalizeElicitationOptions(schema) {
|
|
|
24918
25011
|
return normalized.slice(0, 200);
|
|
24919
25012
|
}
|
|
24920
25013
|
const items = schema["items"];
|
|
24921
|
-
if (!
|
|
25014
|
+
if (!isRecord10(items)) {
|
|
24922
25015
|
return normalized;
|
|
24923
25016
|
}
|
|
24924
25017
|
const anyOf = items["anyOf"];
|
|
@@ -24931,7 +25024,7 @@ function numericString(value) {
|
|
|
24931
25024
|
return typeof value === "number" && Number.isFinite(value) ? value.toString() : "";
|
|
24932
25025
|
}
|
|
24933
25026
|
function normalizeElicitationFields(requestedSchema) {
|
|
24934
|
-
if (!
|
|
25027
|
+
if (!isRecord10(requestedSchema) || !isRecord10(requestedSchema["properties"])) {
|
|
24935
25028
|
return [];
|
|
24936
25029
|
}
|
|
24937
25030
|
const properties = requestedSchema["properties"];
|
|
@@ -24941,7 +25034,7 @@ function normalizeElicitationFields(requestedSchema) {
|
|
|
24941
25034
|
);
|
|
24942
25035
|
const normalized = [];
|
|
24943
25036
|
for (const [name, property] of Object.entries(properties)) {
|
|
24944
|
-
if (!
|
|
25037
|
+
if (!isRecord10(property)) {
|
|
24945
25038
|
continue;
|
|
24946
25039
|
}
|
|
24947
25040
|
const type = elicitationFieldType(property["type"]);
|
|
@@ -25032,7 +25125,7 @@ function validateElicitationContent(encoded, requestedSchema) {
|
|
|
25032
25125
|
"invalid_elicitation_response"
|
|
25033
25126
|
);
|
|
25034
25127
|
}
|
|
25035
|
-
if (!
|
|
25128
|
+
if (!isRecord10(decoded) || !isRecord10(requestedSchema) || !isRecord10(requestedSchema["properties"])) {
|
|
25036
25129
|
throw new AcpExecutionError(
|
|
25037
25130
|
"Elicitation response does not match the requested form",
|
|
25038
25131
|
{},
|
|
@@ -25052,7 +25145,7 @@ function validateElicitationContent(encoded, requestedSchema) {
|
|
|
25052
25145
|
const validated = {};
|
|
25053
25146
|
for (const [name, value] of Object.entries(decoded)) {
|
|
25054
25147
|
const property = properties[name];
|
|
25055
|
-
if (!
|
|
25148
|
+
if (!isRecord10(property)) {
|
|
25056
25149
|
throw new AcpExecutionError(
|
|
25057
25150
|
`Elicitation field ${name} was not requested`,
|
|
25058
25151
|
{},
|
|
@@ -25194,6 +25287,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25194
25287
|
#child;
|
|
25195
25288
|
#connection;
|
|
25196
25289
|
#cwd;
|
|
25290
|
+
#environment;
|
|
25197
25291
|
#getObserver;
|
|
25198
25292
|
#mobiusSessionId;
|
|
25199
25293
|
#pendingElicitations = /* @__PURE__ */ new Map();
|
|
@@ -25215,7 +25309,9 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25215
25309
|
imagePrompt: false,
|
|
25216
25310
|
listSessions: false,
|
|
25217
25311
|
loadSession: false,
|
|
25312
|
+
promptQueueing: false,
|
|
25218
25313
|
resumeSession: false,
|
|
25314
|
+
steering: false,
|
|
25219
25315
|
unstableForkSession: false
|
|
25220
25316
|
};
|
|
25221
25317
|
#commands = [];
|
|
@@ -25235,6 +25331,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25235
25331
|
this.#additionalDirectories = [...additionalDirectories];
|
|
25236
25332
|
this.#agentId = agent.id;
|
|
25237
25333
|
this.#cwd = cwd;
|
|
25334
|
+
this.#environment = agent.environment;
|
|
25238
25335
|
this.#getObserver = getObserver;
|
|
25239
25336
|
this.#mobiusSessionId = mobiusSessionId;
|
|
25240
25337
|
this.#sessionConfigProtocol = sessionConfigProtocolForAgent(agent.id);
|
|
@@ -25320,7 +25417,18 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25320
25417
|
store,
|
|
25321
25418
|
async (runtime) => await runtime.#initializeAgent(options.signal)
|
|
25322
25419
|
);
|
|
25420
|
+
static startSessionForkAtTurn = async (options, sourceNativeSessionId, forkPoint, getObserver, store) => await _AcpProcessSession.#start(
|
|
25421
|
+
options,
|
|
25422
|
+
getObserver,
|
|
25423
|
+
store,
|
|
25424
|
+
async (runtime) => await runtime.initialize(
|
|
25425
|
+
options.signal,
|
|
25426
|
+
{ kind: "fork", nativeSessionId: sourceNativeSessionId },
|
|
25427
|
+
forkPoint
|
|
25428
|
+
)
|
|
25429
|
+
);
|
|
25323
25430
|
belongsTo = (agentId, cwd, additionalDirectories) => this.#agentId === agentId && this.#cwd === cwd && arraysEqual(this.#additionalDirectories, additionalDirectories);
|
|
25431
|
+
usesEnvironment = (environment) => recordsEqual(this.#environment, environment);
|
|
25324
25432
|
canRebind = () => !this.#prompted && !this.#turn.running;
|
|
25325
25433
|
prepared = () => new AcpPreparedSession(
|
|
25326
25434
|
this.#agentName,
|
|
@@ -25386,15 +25494,14 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25386
25494
|
"unsupported_acp_protocol"
|
|
25387
25495
|
);
|
|
25388
25496
|
}
|
|
25389
|
-
|
|
25390
|
-
this.#capabilities = normalizeAcpCapabilities(capabilities);
|
|
25497
|
+
this.#capabilities = normalizeAcpCapabilities(initialized);
|
|
25391
25498
|
this.#agentName = truncate3(initialized.agentInfo?.title ?? initialized.agentInfo?.name ?? this.#agentId, 255);
|
|
25392
25499
|
this.#agentVersion = truncate3(
|
|
25393
25500
|
this.#verifiedAgentVersion.length > 0 ? this.#verifiedAgentVersion : initialized.agentInfo?.version ?? "",
|
|
25394
25501
|
128
|
|
25395
25502
|
);
|
|
25396
25503
|
}
|
|
25397
|
-
initialize = async (signal, origin) => {
|
|
25504
|
+
initialize = async (signal, origin, forkPoint) => {
|
|
25398
25505
|
await this.#initializeAgent(signal);
|
|
25399
25506
|
let stored = origin?.kind === "import" ? new FoundAcpStoredSession(
|
|
25400
25507
|
new AcpStoredSession(
|
|
@@ -25426,6 +25533,13 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25426
25533
|
"native_session_unsupported"
|
|
25427
25534
|
);
|
|
25428
25535
|
}
|
|
25536
|
+
if (this.#additionalDirectories.length > 0 && !this.#capabilities.additionalDirectories) {
|
|
25537
|
+
throw new AcpExecutionError(
|
|
25538
|
+
"This Agent cannot restore additional working directories",
|
|
25539
|
+
{},
|
|
25540
|
+
"native_session_unsupported"
|
|
25541
|
+
);
|
|
25542
|
+
}
|
|
25429
25543
|
const forked = await this.race(
|
|
25430
25544
|
this.#connection.agent.request(
|
|
25431
25545
|
methods6.agent.session.fork,
|
|
@@ -25433,7 +25547,21 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25433
25547
|
sessionId: nativeSessionIdSchema.parse(origin.nativeSessionId),
|
|
25434
25548
|
cwd: this.#cwd,
|
|
25435
25549
|
additionalDirectories: [...this.#additionalDirectories],
|
|
25436
|
-
mcpServers: []
|
|
25550
|
+
mcpServers: [],
|
|
25551
|
+
...forkPoint === void 0 ? {} : {
|
|
25552
|
+
_meta: {
|
|
25553
|
+
jetbrains: {
|
|
25554
|
+
air: {
|
|
25555
|
+
fork: {
|
|
25556
|
+
version: 1,
|
|
25557
|
+
messageFingerprint: forkPoint.messageFingerprint,
|
|
25558
|
+
messageId: forkPoint.messageId,
|
|
25559
|
+
messageOccurrence: forkPoint.messageOccurrence
|
|
25560
|
+
}
|
|
25561
|
+
}
|
|
25562
|
+
}
|
|
25563
|
+
}
|
|
25564
|
+
}
|
|
25437
25565
|
},
|
|
25438
25566
|
{ cancellationSignal: signal }
|
|
25439
25567
|
),
|
|
@@ -25638,10 +25766,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25638
25766
|
}
|
|
25639
25767
|
return this.#configOptions;
|
|
25640
25768
|
};
|
|
25641
|
-
|
|
25642
|
-
if (this.#turn.running) {
|
|
25643
|
-
throw new AcpExecutionError("Another prompt is already running in this conversation", {}, "session_busy");
|
|
25644
|
-
}
|
|
25769
|
+
buildPrompt = (options) => {
|
|
25645
25770
|
if (options.images.length > 0 && !this.#capabilities.imagePrompt) {
|
|
25646
25771
|
throw new AcpExecutionError(
|
|
25647
25772
|
"The selected agent does not accept image prompts",
|
|
@@ -25690,6 +25815,13 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25690
25815
|
if (prompt.length === 0) {
|
|
25691
25816
|
throw new AcpExecutionError("A prompt must contain text or an attachment", {}, "empty_prompt");
|
|
25692
25817
|
}
|
|
25818
|
+
return prompt;
|
|
25819
|
+
};
|
|
25820
|
+
prompt = async (options) => {
|
|
25821
|
+
if (this.#turn.running) {
|
|
25822
|
+
throw new AcpExecutionError("Another prompt is already running in this conversation", {}, "session_busy");
|
|
25823
|
+
}
|
|
25824
|
+
const prompt = this.buildPrompt(options);
|
|
25693
25825
|
this.#store.write(
|
|
25694
25826
|
new AcpStoredSession(
|
|
25695
25827
|
this.#mobiusSessionId,
|
|
@@ -25748,6 +25880,54 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25748
25880
|
this.#turn = new IdleAcpTurn();
|
|
25749
25881
|
}
|
|
25750
25882
|
};
|
|
25883
|
+
steer = async (options) => {
|
|
25884
|
+
if (!this.#capabilities.steering) {
|
|
25885
|
+
throw new AcpExecutionError(
|
|
25886
|
+
"The selected agent does not support mid-turn steering",
|
|
25887
|
+
{},
|
|
25888
|
+
"steering_unsupported"
|
|
25889
|
+
);
|
|
25890
|
+
}
|
|
25891
|
+
if (!this.#turn.running || this.#turn.commandId !== options.targetCommandId) {
|
|
25892
|
+
throw new AcpExecutionError("The target turn is no longer active", {}, "steering_target_unavailable");
|
|
25893
|
+
}
|
|
25894
|
+
const prompt = this.buildPrompt(options);
|
|
25895
|
+
const response = await this.race(
|
|
25896
|
+
this.#connection.agent.request("_session/steering", {
|
|
25897
|
+
_meta: { steering: { idleBehavior: "promptRequired" } },
|
|
25898
|
+
prompt,
|
|
25899
|
+
sessionId: this.#nativeSessionId
|
|
25900
|
+
}),
|
|
25901
|
+
"ACP agent exited during steering",
|
|
25902
|
+
options.signal
|
|
25903
|
+
);
|
|
25904
|
+
if (!isRecord10(response)) {
|
|
25905
|
+
throw new AcpExecutionError(
|
|
25906
|
+
"ACP agent returned an invalid steering response",
|
|
25907
|
+
{},
|
|
25908
|
+
"acp_invalid_steering_response"
|
|
25909
|
+
);
|
|
25910
|
+
}
|
|
25911
|
+
const outcome = response["outcome"];
|
|
25912
|
+
if (outcome === "injected" || outcome === "startedNewTurn") {
|
|
25913
|
+
return outcome;
|
|
25914
|
+
}
|
|
25915
|
+
if (outcome === "promptRequired") {
|
|
25916
|
+
throw new AcpExecutionError(
|
|
25917
|
+
"The target turn finished before steering could be injected",
|
|
25918
|
+
{},
|
|
25919
|
+
"steering_target_unavailable"
|
|
25920
|
+
);
|
|
25921
|
+
}
|
|
25922
|
+
if (outcome === "failed") {
|
|
25923
|
+
throw new AcpExecutionError("The Agent could not inject steering", {}, "steering_failed");
|
|
25924
|
+
}
|
|
25925
|
+
throw new AcpExecutionError(
|
|
25926
|
+
"ACP agent returned an invalid steering response",
|
|
25927
|
+
{},
|
|
25928
|
+
"acp_invalid_steering_response"
|
|
25929
|
+
);
|
|
25930
|
+
};
|
|
25751
25931
|
cancel = async () => {
|
|
25752
25932
|
const capturedProcesses = await captureProcessTree(this.#child);
|
|
25753
25933
|
try {
|
|
@@ -25988,7 +26168,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
25988
26168
|
);
|
|
25989
26169
|
} else {
|
|
25990
26170
|
await this.#turn.activities.finishNarrative();
|
|
25991
|
-
await this.#turn.onText(update.content.text);
|
|
26171
|
+
await this.#turn.onText(update.content.text, truncate3(update.messageId ?? "", 255));
|
|
25992
26172
|
}
|
|
25993
26173
|
} else {
|
|
25994
26174
|
await this.#turn.activities.finishNarrative();
|
|
@@ -26214,6 +26394,7 @@ var AcpProcessSession = class _AcpProcessSession {
|
|
|
26214
26394
|
};
|
|
26215
26395
|
};
|
|
26216
26396
|
var AcpSessionManager = class {
|
|
26397
|
+
#activeCommandIds = /* @__PURE__ */ new Map();
|
|
26217
26398
|
#nativeOperations = /* @__PURE__ */ new Set();
|
|
26218
26399
|
#nativeBrowserObserver = new NullAcpSessionObserver();
|
|
26219
26400
|
#nativeBrowserPreparing = /* @__PURE__ */ new Map();
|
|
@@ -26282,6 +26463,25 @@ var AcpSessionManager = class {
|
|
|
26282
26463
|
this.#preparing.set(options.sessionId, operation);
|
|
26283
26464
|
return await operation;
|
|
26284
26465
|
}
|
|
26466
|
+
async #startSessionForkAtTurnPreparation(options, sourceNativeSessionId, forkPoint) {
|
|
26467
|
+
const operation = (async () => {
|
|
26468
|
+
try {
|
|
26469
|
+
const runtime = await AcpProcessSession.startSessionForkAtTurn(
|
|
26470
|
+
options,
|
|
26471
|
+
sourceNativeSessionId,
|
|
26472
|
+
forkPoint,
|
|
26473
|
+
() => this.#observer,
|
|
26474
|
+
this.#store
|
|
26475
|
+
);
|
|
26476
|
+
this.#sessions.set(options.sessionId, runtime);
|
|
26477
|
+
return runtime;
|
|
26478
|
+
} finally {
|
|
26479
|
+
this.#preparing.delete(options.sessionId);
|
|
26480
|
+
}
|
|
26481
|
+
})();
|
|
26482
|
+
this.#preparing.set(options.sessionId, operation);
|
|
26483
|
+
return await operation;
|
|
26484
|
+
}
|
|
26285
26485
|
async #replacePreparation(existing, options) {
|
|
26286
26486
|
const operation = (async () => {
|
|
26287
26487
|
try {
|
|
@@ -26298,6 +26498,58 @@ var AcpSessionManager = class {
|
|
|
26298
26498
|
this.#preparing.set(options.sessionId, operation);
|
|
26299
26499
|
return await operation;
|
|
26300
26500
|
}
|
|
26501
|
+
async #replaceExecutionPreparation(existing, options) {
|
|
26502
|
+
const desiredConfigOptions = existing.prepared().configOptions;
|
|
26503
|
+
const operation = (async () => {
|
|
26504
|
+
try {
|
|
26505
|
+
this.#sessions.delete(options.sessionId);
|
|
26506
|
+
await existing.close();
|
|
26507
|
+
this.#store.remove(options.sessionId);
|
|
26508
|
+
const runtime = await AcpProcessSession.start(options, () => this.#observer, this.#store);
|
|
26509
|
+
try {
|
|
26510
|
+
for (const desired of desiredConfigOptions) {
|
|
26511
|
+
const current = runtime.prepared().configOptions.find((option2) => option2.id === desired.id);
|
|
26512
|
+
if (current === void 0 || current.type !== desired.type) {
|
|
26513
|
+
continue;
|
|
26514
|
+
}
|
|
26515
|
+
if (desired.type === "boolean") {
|
|
26516
|
+
if (current.booleanValue !== desired.booleanValue) {
|
|
26517
|
+
await runtime.configure({
|
|
26518
|
+
...options,
|
|
26519
|
+
booleanValue: desired.booleanValue,
|
|
26520
|
+
configId: desired.id,
|
|
26521
|
+
requestId: `execution-prepare-${desired.id}`,
|
|
26522
|
+
value: "",
|
|
26523
|
+
valueType: "boolean"
|
|
26524
|
+
});
|
|
26525
|
+
}
|
|
26526
|
+
continue;
|
|
26527
|
+
}
|
|
26528
|
+
if (current.currentValue !== desired.currentValue && current.values.some((value) => value.value === desired.currentValue)) {
|
|
26529
|
+
await runtime.configure({
|
|
26530
|
+
...options,
|
|
26531
|
+
booleanValue: false,
|
|
26532
|
+
configId: desired.id,
|
|
26533
|
+
requestId: `execution-prepare-${desired.id}`,
|
|
26534
|
+
value: desired.currentValue,
|
|
26535
|
+
valueType: "select"
|
|
26536
|
+
});
|
|
26537
|
+
}
|
|
26538
|
+
}
|
|
26539
|
+
} catch (error61) {
|
|
26540
|
+
await runtime.close();
|
|
26541
|
+
this.#store.remove(options.sessionId);
|
|
26542
|
+
throw error61;
|
|
26543
|
+
}
|
|
26544
|
+
this.#sessions.set(options.sessionId, runtime);
|
|
26545
|
+
return runtime;
|
|
26546
|
+
} finally {
|
|
26547
|
+
this.#preparing.delete(options.sessionId);
|
|
26548
|
+
}
|
|
26549
|
+
})();
|
|
26550
|
+
this.#preparing.set(options.sessionId, operation);
|
|
26551
|
+
return await operation;
|
|
26552
|
+
}
|
|
26301
26553
|
prepareSession = async (options) => {
|
|
26302
26554
|
const stored = this.#store.read(options.sessionId);
|
|
26303
26555
|
if (stored instanceof DeletedAcpStoredSession) {
|
|
@@ -26336,16 +26588,45 @@ var AcpSessionManager = class {
|
|
|
26336
26588
|
const runtime = await this.#startPreparation(options);
|
|
26337
26589
|
return runtime.prepared();
|
|
26338
26590
|
};
|
|
26339
|
-
|
|
26340
|
-
const pending = this.#
|
|
26591
|
+
prepareExecutionSession = async (options) => {
|
|
26592
|
+
const pending = this.#preparing.get(options.sessionId);
|
|
26341
26593
|
if (pending !== void 0) {
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
return runtime2.prepared();
|
|
26345
|
-
}
|
|
26346
|
-
return await this.prepareNativeSessionBrowser(options);
|
|
26594
|
+
await pending;
|
|
26595
|
+
return await this.prepareExecutionSession(options);
|
|
26347
26596
|
}
|
|
26348
|
-
const existing = this.#
|
|
26597
|
+
const existing = this.#sessions.get(options.sessionId);
|
|
26598
|
+
if (existing === void 0) {
|
|
26599
|
+
return (await this.#startPreparation(options)).prepared();
|
|
26600
|
+
}
|
|
26601
|
+
if (!existing.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
|
|
26602
|
+
throw new AcpExecutionError(
|
|
26603
|
+
"The agent or workspace cannot change after a conversation has started",
|
|
26604
|
+
{},
|
|
26605
|
+
"session_identity_mismatch"
|
|
26606
|
+
);
|
|
26607
|
+
}
|
|
26608
|
+
if (existing.usesEnvironment(options.agent.environment)) {
|
|
26609
|
+
return existing.prepared();
|
|
26610
|
+
}
|
|
26611
|
+
if (!existing.canRebind()) {
|
|
26612
|
+
throw new AcpExecutionError(
|
|
26613
|
+
"The execution environment cannot change after a conversation has started",
|
|
26614
|
+
{},
|
|
26615
|
+
"session_identity_mismatch"
|
|
26616
|
+
);
|
|
26617
|
+
}
|
|
26618
|
+
return (await this.#replaceExecutionPreparation(existing, options)).prepared();
|
|
26619
|
+
};
|
|
26620
|
+
prepareNativeSessionBrowser = async (options) => {
|
|
26621
|
+
const pending = this.#nativeBrowserPreparing.get(options.sessionId);
|
|
26622
|
+
if (pending !== void 0) {
|
|
26623
|
+
const runtime2 = await pending;
|
|
26624
|
+
if (runtime2.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
|
|
26625
|
+
return runtime2.prepared();
|
|
26626
|
+
}
|
|
26627
|
+
return await this.prepareNativeSessionBrowser(options);
|
|
26628
|
+
}
|
|
26629
|
+
const existing = this.#nativeBrowserSessions.get(options.sessionId);
|
|
26349
26630
|
if (existing !== void 0) {
|
|
26350
26631
|
if (existing.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
|
|
26351
26632
|
return existing.prepared();
|
|
@@ -26380,6 +26661,47 @@ var AcpSessionManager = class {
|
|
|
26380
26661
|
}
|
|
26381
26662
|
}
|
|
26382
26663
|
};
|
|
26664
|
+
steerPrompt = async (options) => {
|
|
26665
|
+
if (this.#activeCommandIds.get(options.sessionId) !== options.targetCommandId) {
|
|
26666
|
+
throw new AcpExecutionError("The target turn is no longer active", {}, "steering_target_unavailable");
|
|
26667
|
+
}
|
|
26668
|
+
const pending = this.#preparing.get(options.sessionId);
|
|
26669
|
+
if (pending !== void 0) {
|
|
26670
|
+
await pending;
|
|
26671
|
+
}
|
|
26672
|
+
if (this.#activeCommandIds.get(options.sessionId) !== options.targetCommandId) {
|
|
26673
|
+
throw new AcpExecutionError("The target turn is no longer active", {}, "steering_target_unavailable");
|
|
26674
|
+
}
|
|
26675
|
+
const runtime = this.#sessions.get(options.sessionId);
|
|
26676
|
+
if (runtime === void 0 || !runtime.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
|
|
26677
|
+
throw new AcpExecutionError(
|
|
26678
|
+
"The target turn execution context is unavailable",
|
|
26679
|
+
{},
|
|
26680
|
+
"steering_target_unavailable"
|
|
26681
|
+
);
|
|
26682
|
+
}
|
|
26683
|
+
const deadlineController = new AbortController();
|
|
26684
|
+
const deadline = setTimeout(
|
|
26685
|
+
() => deadlineController.abort(
|
|
26686
|
+
new AcpExecutionError("The steering request exceeded its deadline", {}, "steering_timeout")
|
|
26687
|
+
),
|
|
26688
|
+
options.agent.promptTimeoutMilliseconds
|
|
26689
|
+
);
|
|
26690
|
+
deadline.unref();
|
|
26691
|
+
try {
|
|
26692
|
+
return await runtime.steer({
|
|
26693
|
+
...options,
|
|
26694
|
+
signal: AbortSignal.any([options.signal, deadlineController.signal])
|
|
26695
|
+
});
|
|
26696
|
+
} catch (error61) {
|
|
26697
|
+
if (error61 instanceof AcpExecutionError) {
|
|
26698
|
+
throw error61;
|
|
26699
|
+
}
|
|
26700
|
+
throw new AcpExecutionError("ACP agent steering failed", { cause: error61 }, "steering_failed");
|
|
26701
|
+
} finally {
|
|
26702
|
+
clearTimeout(deadline);
|
|
26703
|
+
}
|
|
26704
|
+
};
|
|
26383
26705
|
runPrompt = async (options) => {
|
|
26384
26706
|
const stored = this.#store.read(options.sessionId);
|
|
26385
26707
|
if (stored instanceof FoundAcpStoredSession && this.#nativeOperations.has(`${stored.session.agentId}:${stored.session.nativeSessionId}`)) {
|
|
@@ -26404,6 +26726,7 @@ var AcpSessionManager = class {
|
|
|
26404
26726
|
signal: AbortSignal.any([options.signal, deadlineController.signal])
|
|
26405
26727
|
};
|
|
26406
26728
|
this.#activeSessionIds.add(options.sessionId);
|
|
26729
|
+
this.#activeCommandIds.set(options.sessionId, options.commandId);
|
|
26407
26730
|
try {
|
|
26408
26731
|
if (this.#cancellationRequests.has(options.sessionId)) {
|
|
26409
26732
|
return { stopReason: "cancelled" };
|
|
@@ -26443,6 +26766,9 @@ var AcpSessionManager = class {
|
|
|
26443
26766
|
} finally {
|
|
26444
26767
|
clearTimeout(deadline);
|
|
26445
26768
|
this.#activeSessionIds.delete(options.sessionId);
|
|
26769
|
+
if (this.#activeCommandIds.get(options.sessionId) === options.commandId) {
|
|
26770
|
+
this.#activeCommandIds.delete(options.sessionId);
|
|
26771
|
+
}
|
|
26446
26772
|
this.#cancellationRequests.delete(options.sessionId);
|
|
26447
26773
|
}
|
|
26448
26774
|
};
|
|
@@ -26467,6 +26793,7 @@ var AcpSessionManager = class {
|
|
|
26467
26793
|
this.#nativeBrowserSessions.clear();
|
|
26468
26794
|
await Promise.all([...sessions, ...nativeBrowserSessions].map(async (session) => await session.close()));
|
|
26469
26795
|
};
|
|
26796
|
+
hasPreparedSession = (sessionId, agentId, cwd, additionalDirectories) => this.#sessions.get(sessionId)?.belongsTo(agentId, cwd, additionalDirectories) === true;
|
|
26470
26797
|
ensureSession = async (options) => {
|
|
26471
26798
|
const existing = this.#sessions.get(options.sessionId);
|
|
26472
26799
|
if (existing !== void 0) {
|
|
@@ -26575,6 +26902,68 @@ var AcpSessionManager = class {
|
|
|
26575
26902
|
this.#nativeOperations.delete(key);
|
|
26576
26903
|
}
|
|
26577
26904
|
};
|
|
26905
|
+
forkSessionAtTurn = async (options, sourceSessionId, forkPoint) => {
|
|
26906
|
+
if (sourceSessionId === options.sessionId) {
|
|
26907
|
+
throw new AcpExecutionError(
|
|
26908
|
+
"The fork destination must be a new conversation",
|
|
26909
|
+
{},
|
|
26910
|
+
"session_identity_mismatch"
|
|
26911
|
+
);
|
|
26912
|
+
}
|
|
26913
|
+
const source = this.#store.read(sourceSessionId);
|
|
26914
|
+
if (!(source instanceof FoundAcpStoredSession) || !source.session.prompted) {
|
|
26915
|
+
throw new AcpExecutionError(
|
|
26916
|
+
"The source conversation has no resumable native Agent session",
|
|
26917
|
+
{},
|
|
26918
|
+
"native_session_not_found"
|
|
26919
|
+
);
|
|
26920
|
+
}
|
|
26921
|
+
if (source.session.agentId !== options.agent.id || source.session.cwd !== options.cwd || !arraysEqual(source.session.additionalDirectories, options.additionalDirectories)) {
|
|
26922
|
+
throw new AcpExecutionError(
|
|
26923
|
+
"The source conversation execution target does not match the fork request",
|
|
26924
|
+
{},
|
|
26925
|
+
"native_session_target_changed"
|
|
26926
|
+
);
|
|
26927
|
+
}
|
|
26928
|
+
const key = `${options.agent.id}:${source.session.nativeSessionId}`;
|
|
26929
|
+
const linked = this.#store.findNative(options.agent.id, source.session.nativeSessionId);
|
|
26930
|
+
if (this.#nativeOperations.has(key) || linked.some((entry) => this.#activeSessionIds.has(entry.sessionId) || this.#preparing.has(entry.sessionId))) {
|
|
26931
|
+
throw new AcpExecutionError(
|
|
26932
|
+
"Wait for the source conversation's running operation to finish",
|
|
26933
|
+
{},
|
|
26934
|
+
"session_busy"
|
|
26935
|
+
);
|
|
26936
|
+
}
|
|
26937
|
+
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) {
|
|
26938
|
+
throw new AcpExecutionError("The destination conversation already exists", {}, "session_identity_mismatch");
|
|
26939
|
+
}
|
|
26940
|
+
this.#nativeOperations.add(key);
|
|
26941
|
+
try {
|
|
26942
|
+
const runtime = await this.#startSessionForkAtTurnPreparation(
|
|
26943
|
+
options,
|
|
26944
|
+
source.session.nativeSessionId,
|
|
26945
|
+
forkPoint
|
|
26946
|
+
);
|
|
26947
|
+
const openedKey = `${options.agent.id}:${runtime.nativeSessionId()}`;
|
|
26948
|
+
this.#nativeOperations.add(openedKey);
|
|
26949
|
+
try {
|
|
26950
|
+
return {
|
|
26951
|
+
history: await runtime.loadHistory(options.signal),
|
|
26952
|
+
prepared: runtime.prepared(),
|
|
26953
|
+
sessionId: options.sessionId
|
|
26954
|
+
};
|
|
26955
|
+
} catch (error61) {
|
|
26956
|
+
this.#sessions.delete(options.sessionId);
|
|
26957
|
+
this.#store.remove(options.sessionId);
|
|
26958
|
+
await runtime.closeProcess();
|
|
26959
|
+
throw error61;
|
|
26960
|
+
} finally {
|
|
26961
|
+
this.#nativeOperations.delete(openedKey);
|
|
26962
|
+
}
|
|
26963
|
+
} finally {
|
|
26964
|
+
this.#nativeOperations.delete(key);
|
|
26965
|
+
}
|
|
26966
|
+
};
|
|
26578
26967
|
deleteNativeSession = async (options, nativeSessionId) => {
|
|
26579
26968
|
const runtime = await this.#ensureNativeSessionBrowser(options);
|
|
26580
26969
|
const key = `${options.agent.id}:${nativeSessionId}`;
|
|
@@ -26620,14 +27009,19 @@ var AgentCatalogPublishError = class extends Error {
|
|
|
26620
27009
|
this.name = "AgentCatalogPublishError";
|
|
26621
27010
|
}
|
|
26622
27011
|
};
|
|
26623
|
-
function catalogEndpoint(
|
|
26624
|
-
const endpoint = new URL(relayUrl);
|
|
27012
|
+
function catalogEndpoint(config2) {
|
|
27013
|
+
const endpoint = new URL(config2.relayUrl);
|
|
26625
27014
|
endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
|
|
26626
|
-
if (
|
|
26627
|
-
|
|
27015
|
+
if (endpoint.pathname.endsWith("/daemon")) {
|
|
27016
|
+
endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
|
|
27017
|
+
return endpoint;
|
|
26628
27018
|
}
|
|
26629
|
-
|
|
26630
|
-
|
|
27019
|
+
const runtimePath = "/v1/runtimes/connect";
|
|
27020
|
+
if (endpoint.pathname.endsWith(runtimePath)) {
|
|
27021
|
+
endpoint.pathname = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}/catalog`;
|
|
27022
|
+
return endpoint;
|
|
27023
|
+
}
|
|
27024
|
+
throw new AgentCatalogPublishError("The Relay URL is invalid");
|
|
26631
27025
|
}
|
|
26632
27026
|
async function publishAgentCatalog(config2, signal) {
|
|
26633
27027
|
signal.throwIfAborted();
|
|
@@ -26636,7 +27030,7 @@ async function publishAgentCatalog(config2, signal) {
|
|
|
26636
27030
|
let response;
|
|
26637
27031
|
const catalog = config2.catalog.read();
|
|
26638
27032
|
try {
|
|
26639
|
-
response = await fetch3(catalogEndpoint(config2
|
|
27033
|
+
response = await fetch3(catalogEndpoint(config2), {
|
|
26640
27034
|
body: JSON.stringify({
|
|
26641
27035
|
agents: catalog.agents.map((agent) => ({
|
|
26642
27036
|
agentPath: agent.agentIdentity.requestedPath,
|
|
@@ -27317,7 +27711,8 @@ var LocalBotGrant = class {
|
|
|
27317
27711
|
revocationEpoch;
|
|
27318
27712
|
workspaceIdentityDigest;
|
|
27319
27713
|
workspacePath;
|
|
27320
|
-
|
|
27714
|
+
workspaceRef;
|
|
27715
|
+
constructor(localGrantId, botId, behaviorRevisionId, behaviorDigest, bindingId, bindingRevision, agent, agentIdentityDigest, workspaceRef, workspacePath, workspaceIdentityDigest, allowedDeliveryTargetIds, expiresAt, revocationEpoch) {
|
|
27321
27716
|
this.localGrantId = localGrantId;
|
|
27322
27717
|
this.botId = botId;
|
|
27323
27718
|
this.behaviorRevisionId = behaviorRevisionId;
|
|
@@ -27326,6 +27721,7 @@ var LocalBotGrant = class {
|
|
|
27326
27721
|
this.bindingRevision = bindingRevision;
|
|
27327
27722
|
this.agent = agent;
|
|
27328
27723
|
this.agentIdentityDigest = agentIdentityDigest;
|
|
27724
|
+
this.workspaceRef = workspaceRef;
|
|
27329
27725
|
this.workspacePath = workspacePath;
|
|
27330
27726
|
this.workspaceIdentityDigest = workspaceIdentityDigest;
|
|
27331
27727
|
this.allowedDeliveryTargetIds = Object.freeze([...allowedDeliveryTargetIds]);
|
|
@@ -27510,7 +27906,7 @@ var LocalBotGrantStore = class {
|
|
|
27510
27906
|
WHERE local_grant_id = ? AND bot_id = ?
|
|
27511
27907
|
AND behavior_revision_id = ? AND behavior_digest = ?
|
|
27512
27908
|
AND binding_id = ? AND binding_revision = ?
|
|
27513
|
-
AND agent_ref = ?
|
|
27909
|
+
AND agent_ref = ?
|
|
27514
27910
|
AND revoked_at IS NULL AND expires_at > ?`
|
|
27515
27911
|
).get(
|
|
27516
27912
|
command.localGrantId,
|
|
@@ -27520,7 +27916,6 @@ var LocalBotGrantStore = class {
|
|
|
27520
27916
|
command.bindingId,
|
|
27521
27917
|
command.bindingRevision,
|
|
27522
27918
|
command.agentId,
|
|
27523
|
-
command.directoryId,
|
|
27524
27919
|
now
|
|
27525
27920
|
);
|
|
27526
27921
|
if (row === void 0) {
|
|
@@ -27543,7 +27938,8 @@ var LocalBotGrantStore = class {
|
|
|
27543
27938
|
revalidateExecutable(agent.agentIdentity)
|
|
27544
27939
|
]);
|
|
27545
27940
|
signal.throwIfAborted();
|
|
27546
|
-
const
|
|
27941
|
+
const workspaceRef = rowString(row, "workspace_ref");
|
|
27942
|
+
const workspacePath = await directories.resolve(bindingAuthorityId, workspaceRef, signal);
|
|
27547
27943
|
const currentAgentDigest = agentDigest(agent);
|
|
27548
27944
|
const currentWorkspaceDigest = await workspaceDigest(workspacePath);
|
|
27549
27945
|
signal.throwIfAborted();
|
|
@@ -27569,6 +27965,7 @@ var LocalBotGrantStore = class {
|
|
|
27569
27965
|
command.bindingRevision,
|
|
27570
27966
|
agent,
|
|
27571
27967
|
currentAgentDigest,
|
|
27968
|
+
workspaceRef,
|
|
27572
27969
|
workspacePath,
|
|
27573
27970
|
currentWorkspaceDigest,
|
|
27574
27971
|
approvedTargets,
|
|
@@ -27783,7 +28180,7 @@ var BotOutboundIpcRequest = class {
|
|
|
27783
28180
|
this.text = text;
|
|
27784
28181
|
}
|
|
27785
28182
|
};
|
|
27786
|
-
function
|
|
28183
|
+
function isRecord11(value) {
|
|
27787
28184
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27788
28185
|
}
|
|
27789
28186
|
function decodeRequest(encoded) {
|
|
@@ -27793,7 +28190,7 @@ function decodeRequest(encoded) {
|
|
|
27793
28190
|
} catch (cause) {
|
|
27794
28191
|
throw new LocalBotAuthorizationError("invalid_request", "The local Bot request is invalid", { cause });
|
|
27795
28192
|
}
|
|
27796
|
-
if (!
|
|
28193
|
+
if (!isRecord11(value)) {
|
|
27797
28194
|
throw new LocalBotAuthorizationError("invalid_request", "The local Bot request is invalid");
|
|
27798
28195
|
}
|
|
27799
28196
|
const keys = Object.keys(value);
|
|
@@ -27843,10 +28240,10 @@ async function decodeRelayResponse(response) {
|
|
|
27843
28240
|
cause
|
|
27844
28241
|
});
|
|
27845
28242
|
}
|
|
27846
|
-
if (response.status === 202 &&
|
|
28243
|
+
if (response.status === 202 && isRecord11(value) && typeof value["outboundMessageId"] === "string") {
|
|
27847
28244
|
return value["outboundMessageId"];
|
|
27848
28245
|
}
|
|
27849
|
-
if (
|
|
28246
|
+
if (isRecord11(value) && isRecord11(value["error"])) {
|
|
27850
28247
|
const error61 = value["error"];
|
|
27851
28248
|
if (typeof error61["code"] === "string" && typeof error61["message"] === "string") {
|
|
27852
28249
|
throw new LocalBotAuthorizationError(error61["code"], error61["message"]);
|
|
@@ -27879,7 +28276,7 @@ var BotOutboundIpcServer = class {
|
|
|
27879
28276
|
if (this.#usesFilesystemSocket) {
|
|
27880
28277
|
await mkdir4(dirname6(this.#socketPath), { mode: 448, recursive: true });
|
|
27881
28278
|
await unlink(this.#socketPath).catch((error61) => {
|
|
27882
|
-
if (!
|
|
28279
|
+
if (!isRecord11(error61) || error61["code"] !== "ENOENT") {
|
|
27883
28280
|
throw error61;
|
|
27884
28281
|
}
|
|
27885
28282
|
});
|
|
@@ -27914,7 +28311,7 @@ var BotOutboundIpcServer = class {
|
|
|
27914
28311
|
}
|
|
27915
28312
|
if (this.#usesFilesystemSocket) {
|
|
27916
28313
|
await unlink(this.#socketPath).catch((error61) => {
|
|
27917
|
-
if (!
|
|
28314
|
+
if (!isRecord11(error61) || error61["code"] !== "ENOENT") {
|
|
27918
28315
|
throw error61;
|
|
27919
28316
|
}
|
|
27920
28317
|
});
|
|
@@ -28002,6 +28399,7 @@ var BotOutboundIpcServer = class {
|
|
|
28002
28399
|
|
|
28003
28400
|
// src/native-sessions/service.ts
|
|
28004
28401
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
28402
|
+
import { basename as basename3 } from "node:path";
|
|
28005
28403
|
|
|
28006
28404
|
// src/native-sessions/codex-history.ts
|
|
28007
28405
|
import { spawn as spawn4 } from "node:child_process";
|
|
@@ -28479,6 +28877,74 @@ var NativeSessionService = class {
|
|
|
28479
28877
|
history: opened.history
|
|
28480
28878
|
};
|
|
28481
28879
|
};
|
|
28880
|
+
executeTurnFork = async (command, signal) => {
|
|
28881
|
+
signal.throwIfAborted();
|
|
28882
|
+
const cwd = await this.#directories.resolve(this.#config.bindingId, command.directoryId, signal);
|
|
28883
|
+
const additionalDirectories = [];
|
|
28884
|
+
const additionalEntries = [];
|
|
28885
|
+
const resolvedPaths = /* @__PURE__ */ new Set([cwd]);
|
|
28886
|
+
for (const directoryId2 of command.additionalDirectoryIds) {
|
|
28887
|
+
const path2 = await this.#directories.resolve(this.#config.bindingId, directoryId2, signal);
|
|
28888
|
+
if (resolvedPaths.has(path2)) {
|
|
28889
|
+
throw new LocalAuthorizationError("Workspace directories must resolve to distinct locations");
|
|
28890
|
+
}
|
|
28891
|
+
resolvedPaths.add(path2);
|
|
28892
|
+
additionalDirectories.push(path2);
|
|
28893
|
+
additionalEntries.push({ directoryId: directoryId2, name: basename3(path2) });
|
|
28894
|
+
}
|
|
28895
|
+
const listing = await this.#directories.list(this.#config.bindingId, command.directoryId, signal);
|
|
28896
|
+
const opened = await this.#sessions.forkSessionAtTurn(
|
|
28897
|
+
{
|
|
28898
|
+
additionalDirectories,
|
|
28899
|
+
agent: this.#config.catalog.find(command.agentId),
|
|
28900
|
+
cwd,
|
|
28901
|
+
sessionId: command.targetSessionId,
|
|
28902
|
+
signal
|
|
28903
|
+
},
|
|
28904
|
+
command.sessionId,
|
|
28905
|
+
{
|
|
28906
|
+
messageFingerprint: command.messageFingerprint,
|
|
28907
|
+
messageId: command.messageId,
|
|
28908
|
+
messageOccurrence: command.messageOccurrence
|
|
28909
|
+
}
|
|
28910
|
+
);
|
|
28911
|
+
signal.throwIfAborted();
|
|
28912
|
+
const ready = {
|
|
28913
|
+
additionalDirectories: additionalEntries,
|
|
28914
|
+
agentId: command.agentId,
|
|
28915
|
+
agentName: opened.prepared.agentName,
|
|
28916
|
+
agentVersion: opened.prepared.agentVersion,
|
|
28917
|
+
capabilities: opened.prepared.capabilities,
|
|
28918
|
+
commands: opened.prepared.commands,
|
|
28919
|
+
configOptions: opened.prepared.configOptions,
|
|
28920
|
+
directoryId: command.directoryId,
|
|
28921
|
+
directoryLabel: listing.directoryLabel,
|
|
28922
|
+
kind: "session.ready",
|
|
28923
|
+
requestId: command.requestId,
|
|
28924
|
+
restored: true,
|
|
28925
|
+
sessionId: opened.sessionId
|
|
28926
|
+
};
|
|
28927
|
+
this.#audit.record({
|
|
28928
|
+
commandId: command.requestId,
|
|
28929
|
+
details: {
|
|
28930
|
+
agentId: command.agentId,
|
|
28931
|
+
messageId: command.messageId,
|
|
28932
|
+
sourceCommandId: command.sourceCommandId
|
|
28933
|
+
},
|
|
28934
|
+
event: "native_session_turn_forked",
|
|
28935
|
+
sessionId: command.sessionId
|
|
28936
|
+
});
|
|
28937
|
+
return {
|
|
28938
|
+
action: "fork",
|
|
28939
|
+
history: opened.history,
|
|
28940
|
+
kind: "native.session.result",
|
|
28941
|
+
ready,
|
|
28942
|
+
requestId: command.requestId,
|
|
28943
|
+
sessionId: command.sessionId,
|
|
28944
|
+
targetSessionId: command.targetSessionId,
|
|
28945
|
+
title: command.title
|
|
28946
|
+
};
|
|
28947
|
+
};
|
|
28482
28948
|
authorizeEntry = async (command, options, info) => {
|
|
28483
28949
|
try {
|
|
28484
28950
|
const primary = await this.#directories.identify(command.bindingId, info.cwd, options.signal);
|
|
@@ -28620,10 +29086,17 @@ function parseReference(resource) {
|
|
|
28620
29086
|
function downloadUrl(config2, command, reference) {
|
|
28621
29087
|
const url2 = new URL(config2.relayUrl);
|
|
28622
29088
|
url2.protocol = url2.protocol === "wss:" ? "https:" : "http:";
|
|
28623
|
-
|
|
28624
|
-
|
|
29089
|
+
let channelPath = "";
|
|
29090
|
+
if (url2.pathname.endsWith("/daemon")) {
|
|
29091
|
+
channelPath = url2.pathname.slice(0, -"/daemon".length);
|
|
29092
|
+
} else {
|
|
29093
|
+
const runtimePath = "/v1/runtimes/connect";
|
|
29094
|
+
if (!url2.pathname.endsWith(runtimePath)) {
|
|
29095
|
+
throw new PromptArtifactError("artifact_endpoint_invalid", "The Relay artifact endpoint is invalid");
|
|
29096
|
+
}
|
|
29097
|
+
channelPath = `${url2.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
|
|
28625
29098
|
}
|
|
28626
|
-
url2.pathname = `${
|
|
29099
|
+
url2.pathname = `${channelPath}/sessions/${encodeURIComponent(command.sessionId)}/commands/${encodeURIComponent(command.commandId)}/artifacts/${encodeURIComponent(reference.artifactId)}/content`;
|
|
28627
29100
|
url2.searchParams.delete("liveness");
|
|
28628
29101
|
return url2;
|
|
28629
29102
|
}
|
|
@@ -28782,8 +29255,16 @@ var RelayConnectionError = class extends Error {
|
|
|
28782
29255
|
function connectRelay(url2, signal) {
|
|
28783
29256
|
signal.throwIfAborted();
|
|
28784
29257
|
return new Promise((resolve5, reject) => {
|
|
28785
|
-
const
|
|
28786
|
-
|
|
29258
|
+
const endpoint = new URL(url2);
|
|
29259
|
+
const headers = {};
|
|
29260
|
+
if (endpoint.pathname.endsWith("/v1/runtimes/connect")) {
|
|
29261
|
+
const runtimeCredential = endpoint.searchParams.get("token") ?? "";
|
|
29262
|
+
endpoint.searchParams.delete("token");
|
|
29263
|
+
headers["authorization"] = `Bearer ${runtimeCredential}`;
|
|
29264
|
+
}
|
|
29265
|
+
const socket = new WebSocket(endpoint, {
|
|
29266
|
+
dispatcher: environmentProxyDispatcher,
|
|
29267
|
+
headers
|
|
28787
29268
|
});
|
|
28788
29269
|
let finished = false;
|
|
28789
29270
|
const timeout = setTimeout(onTimeout, CONNECTION_TIMEOUT_MILLISECONDS);
|
|
@@ -29043,6 +29524,11 @@ var SessionEventOutbox = class {
|
|
|
29043
29524
|
}
|
|
29044
29525
|
this.#database.prepare(`UPDATE relay_turn_receipts SET state = ? WHERE command_id = ?`).run(state, commandId);
|
|
29045
29526
|
};
|
|
29527
|
+
isTerminalExecution = (commandId, sessionId) => {
|
|
29528
|
+
const row = this.#database.prepare(`SELECT state FROM relay_turn_receipts WHERE command_id = ? AND session_id = ?`).get(commandId, sessionId);
|
|
29529
|
+
const state = typeof row === "object" && row !== null ? Reflect.get(row, "state") : void 0;
|
|
29530
|
+
return state === "succeeded" || state === "failed" || state === "cancelled";
|
|
29531
|
+
};
|
|
29046
29532
|
acknowledge = (acknowledgement) => {
|
|
29047
29533
|
this.#database.prepare(
|
|
29048
29534
|
`DELETE FROM relay_session_event_outbox
|
|
@@ -29399,10 +29885,17 @@ var ActiveTurnReconciliationError = class extends Error {
|
|
|
29399
29885
|
function activeTurnReconciliationEndpoint(config2, turn) {
|
|
29400
29886
|
const endpoint = new URL(config2.relayUrl);
|
|
29401
29887
|
endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
|
|
29402
|
-
|
|
29403
|
-
|
|
29888
|
+
let channelPath = "";
|
|
29889
|
+
if (endpoint.pathname.endsWith("/daemon")) {
|
|
29890
|
+
channelPath = endpoint.pathname.slice(0, -"/daemon".length);
|
|
29891
|
+
} else {
|
|
29892
|
+
const runtimePath = "/v1/runtimes/connect";
|
|
29893
|
+
if (!endpoint.pathname.endsWith(runtimePath)) {
|
|
29894
|
+
throw new ActiveTurnReconciliationError("The Relay URL is invalid");
|
|
29895
|
+
}
|
|
29896
|
+
channelPath = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
|
|
29404
29897
|
}
|
|
29405
|
-
endpoint.pathname = `${
|
|
29898
|
+
endpoint.pathname = `${channelPath}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
|
|
29406
29899
|
endpoint.searchParams.delete("liveness");
|
|
29407
29900
|
return endpoint;
|
|
29408
29901
|
}
|
|
@@ -29555,7 +30048,7 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
|
|
|
29555
30048
|
additionalDirectories.push(directory);
|
|
29556
30049
|
additionalDirectoryEntries.push({
|
|
29557
30050
|
directoryId: additionalDirectoryId,
|
|
29558
|
-
name:
|
|
30051
|
+
name: basename4(directory)
|
|
29559
30052
|
});
|
|
29560
30053
|
}
|
|
29561
30054
|
return new ResolvedSessionDirectories(cwd, additionalDirectories, additionalDirectoryEntries);
|
|
@@ -29704,68 +30197,96 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
|
|
|
29704
30197
|
attemptId
|
|
29705
30198
|
);
|
|
29706
30199
|
const attachments = await resolvePromptArtifacts(config2, command, signal);
|
|
29707
|
-
|
|
29708
|
-
|
|
29709
|
-
|
|
29710
|
-
|
|
29711
|
-
|
|
29712
|
-
|
|
29713
|
-
|
|
29714
|
-
|
|
29715
|
-
|
|
29716
|
-
|
|
29717
|
-
|
|
29718
|
-
|
|
29719
|
-
|
|
29720
|
-
|
|
29721
|
-
|
|
29722
|
-
|
|
29723
|
-
|
|
29724
|
-
|
|
29725
|
-
|
|
29726
|
-
|
|
29727
|
-
|
|
29728
|
-
sessionId: command.sessionId,
|
|
29729
|
-
status: activity.status,
|
|
29730
|
-
title: activity.title,
|
|
29731
|
-
toolKind: activity.toolKind
|
|
29732
|
-
}),
|
|
29733
|
-
attemptId
|
|
29734
|
-
);
|
|
29735
|
-
return Promise.resolve();
|
|
29736
|
-
},
|
|
29737
|
-
onText: (text) => {
|
|
29738
|
-
publisher.recordAttempt(
|
|
29739
|
-
command.sessionId,
|
|
29740
|
-
encodeAgentMessageDeltaEvent({
|
|
29741
|
-
commandId: command.commandId,
|
|
29742
|
-
kind: AGENT_MESSAGE_DELTA_KIND,
|
|
29743
|
-
sessionId: command.sessionId,
|
|
29744
|
-
text
|
|
29745
|
-
}),
|
|
29746
|
-
attemptId
|
|
30200
|
+
let stopReason;
|
|
30201
|
+
if (command.disposition === SessionPromptDisposition.Steer) {
|
|
30202
|
+
await sessions.steerPrompt({
|
|
30203
|
+
additionalDirectories: resolved.additionalDirectories,
|
|
30204
|
+
agent,
|
|
30205
|
+
commandId: command.commandId,
|
|
30206
|
+
cwd: resolved.cwd,
|
|
30207
|
+
images: attachments.images,
|
|
30208
|
+
prompt: command.prompt,
|
|
30209
|
+
resources: attachments.resources,
|
|
30210
|
+
sessionId: command.sessionId,
|
|
30211
|
+
signal,
|
|
30212
|
+
targetCommandId: command.targetCommandId
|
|
30213
|
+
});
|
|
30214
|
+
stopReason = "steered";
|
|
30215
|
+
} else {
|
|
30216
|
+
if (command.disposition === SessionPromptDisposition.Queue && !outbox.isTerminalExecution(command.targetCommandId, command.sessionId)) {
|
|
30217
|
+
throw new AcpExecutionError(
|
|
30218
|
+
"The queued prompt target is unavailable",
|
|
30219
|
+
{},
|
|
30220
|
+
"prompt_queue_target_unavailable"
|
|
29747
30221
|
);
|
|
29748
|
-
|
|
29749
|
-
|
|
29750
|
-
|
|
29751
|
-
|
|
29752
|
-
|
|
29753
|
-
|
|
29754
|
-
|
|
30222
|
+
}
|
|
30223
|
+
const result = await sessions.runPrompt({
|
|
30224
|
+
additionalDirectories: resolved.additionalDirectories,
|
|
30225
|
+
agent,
|
|
30226
|
+
commandId: command.commandId,
|
|
30227
|
+
cwd: resolved.cwd,
|
|
30228
|
+
disposition: command.disposition,
|
|
30229
|
+
images: attachments.images,
|
|
30230
|
+
onActivity: (activity) => {
|
|
30231
|
+
publisher.recordAttempt(
|
|
30232
|
+
command.sessionId,
|
|
30233
|
+
encodeAgentActivityEvent({
|
|
30234
|
+
activityId: activity.activityId,
|
|
30235
|
+
activityType: activity.activityType,
|
|
30236
|
+
commandId: command.commandId,
|
|
30237
|
+
content: activity.content,
|
|
30238
|
+
detail: activity.detail,
|
|
30239
|
+
input: activity.input,
|
|
30240
|
+
kind: AGENT_ACTIVITY_KIND,
|
|
30241
|
+
locations: activity.locations,
|
|
30242
|
+
name: activity.name,
|
|
30243
|
+
output: activity.output,
|
|
30244
|
+
sequence: activity.sequence,
|
|
30245
|
+
sessionId: command.sessionId,
|
|
30246
|
+
status: activity.status,
|
|
30247
|
+
title: activity.title,
|
|
30248
|
+
toolKind: activity.toolKind
|
|
30249
|
+
}),
|
|
30250
|
+
attemptId
|
|
30251
|
+
);
|
|
30252
|
+
return Promise.resolve();
|
|
30253
|
+
},
|
|
30254
|
+
onText: (text, messageId) => {
|
|
30255
|
+
publisher.recordAttempt(
|
|
30256
|
+
command.sessionId,
|
|
30257
|
+
encodeAgentMessageDeltaEvent({
|
|
30258
|
+
commandId: command.commandId,
|
|
30259
|
+
kind: AGENT_MESSAGE_DELTA_KIND,
|
|
30260
|
+
messageId,
|
|
30261
|
+
sessionId: command.sessionId,
|
|
30262
|
+
text
|
|
30263
|
+
}),
|
|
30264
|
+
attemptId
|
|
30265
|
+
);
|
|
30266
|
+
return Promise.resolve();
|
|
30267
|
+
},
|
|
30268
|
+
prompt: command.prompt,
|
|
30269
|
+
resources: attachments.resources,
|
|
30270
|
+
sessionId: command.sessionId,
|
|
30271
|
+
signal,
|
|
30272
|
+
targetCommandId: command.targetCommandId
|
|
30273
|
+
});
|
|
30274
|
+
stopReason = result.stopReason;
|
|
30275
|
+
}
|
|
29755
30276
|
publisher.recordAttempt(
|
|
29756
30277
|
command.sessionId,
|
|
29757
30278
|
encodeSessionCompletedEvent({
|
|
29758
30279
|
commandId: command.commandId,
|
|
29759
30280
|
kind: SESSION_COMPLETED_KIND,
|
|
29760
30281
|
sessionId: command.sessionId,
|
|
29761
|
-
stopReason
|
|
30282
|
+
stopReason
|
|
29762
30283
|
}),
|
|
29763
30284
|
attemptId
|
|
29764
30285
|
);
|
|
29765
|
-
outbox.transitionExecution(command.commandId,
|
|
30286
|
+
outbox.transitionExecution(command.commandId, stopReason === "cancelled" ? "cancelled" : "succeeded");
|
|
29766
30287
|
audit.record({
|
|
29767
30288
|
commandId: command.commandId,
|
|
29768
|
-
event:
|
|
30289
|
+
event: stopReason === "cancelled" ? "session_cancelled" : "session_completed",
|
|
29769
30290
|
sessionId: command.sessionId
|
|
29770
30291
|
});
|
|
29771
30292
|
} catch (error61) {
|
|
@@ -29809,12 +30330,6 @@ function botExecutionAgent(agent, contextHandle, socketPath) {
|
|
|
29809
30330
|
}
|
|
29810
30331
|
async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, signal) {
|
|
29811
30332
|
try {
|
|
29812
|
-
if (command.additionalDirectoryIds.length > 0) {
|
|
29813
|
-
throw new LocalBotAuthorizationError(
|
|
29814
|
-
"binding_not_approved",
|
|
29815
|
-
"The Bot execution requested unapproved additional directories"
|
|
29816
|
-
);
|
|
29817
|
-
}
|
|
29818
30333
|
const deadline = Date.parse(command.executionDeadlineAt);
|
|
29819
30334
|
const remaining = deadline - Date.now();
|
|
29820
30335
|
if (!Number.isSafeInteger(deadline) || remaining <= 0) {
|
|
@@ -29828,6 +30343,32 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
29828
30343
|
Date.now(),
|
|
29829
30344
|
signal
|
|
29830
30345
|
);
|
|
30346
|
+
const requestedExecutionTargetOverridden = command.directoryId !== grant.workspaceRef || command.additionalDirectoryIds.length > 0;
|
|
30347
|
+
if (requestedExecutionTargetOverridden && !(command.replyRoute instanceof OwnerBotReplyRoute)) {
|
|
30348
|
+
throw new LocalBotAuthorizationError(
|
|
30349
|
+
"binding_not_approved",
|
|
30350
|
+
"Only a paired owner conversation can override the approved Bot workspace"
|
|
30351
|
+
);
|
|
30352
|
+
}
|
|
30353
|
+
const resolved = await resolveSessionDirectories(
|
|
30354
|
+
directories,
|
|
30355
|
+
config2.bindingId,
|
|
30356
|
+
command.directoryId,
|
|
30357
|
+
command.additionalDirectoryIds,
|
|
30358
|
+
signal
|
|
30359
|
+
);
|
|
30360
|
+
const executionTargetOverridden = resolved.cwd !== grant.workspacePath || resolved.additionalDirectories.length > 0;
|
|
30361
|
+
if (executionTargetOverridden && !sessions.hasPreparedSession(
|
|
30362
|
+
command.sessionId,
|
|
30363
|
+
grant.agent.id,
|
|
30364
|
+
resolved.cwd,
|
|
30365
|
+
resolved.additionalDirectories
|
|
30366
|
+
)) {
|
|
30367
|
+
throw new LocalBotAuthorizationError(
|
|
30368
|
+
"local_approval_required",
|
|
30369
|
+
"Open this Bot conversation in the paired local Web app before using the selected workspace"
|
|
30370
|
+
);
|
|
30371
|
+
}
|
|
29831
30372
|
const attachments = await resolvePromptArtifacts(config2, command, signal);
|
|
29832
30373
|
const applied = await applicator.apply(command, grant, attachments.resources, signal);
|
|
29833
30374
|
const lease = contexts.activate(command);
|
|
@@ -29835,6 +30376,13 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
29835
30376
|
const deadlineSignal = AbortSignal.timeout(remaining);
|
|
29836
30377
|
const executionSignal = AbortSignal.any([signal, deadlineSignal]);
|
|
29837
30378
|
const agent = botExecutionAgent(grant.agent, lease.context.contextHandle, ipc.socketPath);
|
|
30379
|
+
await sessions.prepareExecutionSession({
|
|
30380
|
+
additionalDirectories: resolved.additionalDirectories,
|
|
30381
|
+
agent,
|
|
30382
|
+
cwd: resolved.cwd,
|
|
30383
|
+
sessionId: command.sessionId,
|
|
30384
|
+
signal: executionSignal
|
|
30385
|
+
});
|
|
29838
30386
|
audit.record({
|
|
29839
30387
|
commandId: command.commandId,
|
|
29840
30388
|
details: {
|
|
@@ -29859,10 +30407,10 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
29859
30407
|
attemptId
|
|
29860
30408
|
);
|
|
29861
30409
|
const result = await sessions.runPrompt({
|
|
29862
|
-
additionalDirectories:
|
|
30410
|
+
additionalDirectories: resolved.additionalDirectories,
|
|
29863
30411
|
agent,
|
|
29864
30412
|
commandId: command.commandId,
|
|
29865
|
-
cwd:
|
|
30413
|
+
cwd: resolved.cwd,
|
|
29866
30414
|
images: attachments.images,
|
|
29867
30415
|
onActivity: (activity) => {
|
|
29868
30416
|
publisher.recordAttempt(
|
|
@@ -29888,12 +30436,13 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
29888
30436
|
);
|
|
29889
30437
|
return Promise.resolve();
|
|
29890
30438
|
},
|
|
29891
|
-
onText: (text) => {
|
|
30439
|
+
onText: (text, messageId) => {
|
|
29892
30440
|
publisher.recordAttempt(
|
|
29893
30441
|
command.sessionId,
|
|
29894
30442
|
encodeAgentMessageDeltaEvent({
|
|
29895
30443
|
commandId: command.commandId,
|
|
29896
30444
|
kind: AGENT_MESSAGE_DELTA_KIND,
|
|
30445
|
+
messageId,
|
|
29897
30446
|
sessionId: command.sessionId,
|
|
29898
30447
|
text
|
|
29899
30448
|
}),
|
|
@@ -30248,6 +30797,86 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
30248
30797
|
});
|
|
30249
30798
|
return;
|
|
30250
30799
|
}
|
|
30800
|
+
if (kind === NATIVE_SESSION_TURN_FORK_KIND) {
|
|
30801
|
+
const command2 = decodeNativeSessionTurnForkCommand(encoded);
|
|
30802
|
+
const previous = [...nativeRequests.values()].filter(
|
|
30803
|
+
(request) => request.agentId === command2.agentId
|
|
30804
|
+
);
|
|
30805
|
+
if (nativeRequests.has(command2.requestId) || nativeRequests.size >= 4 || previous.some((request) => !request.controller.signal.aborted)) {
|
|
30806
|
+
reportOperationFailure(
|
|
30807
|
+
socket,
|
|
30808
|
+
command2.requestId,
|
|
30809
|
+
command2.sessionId,
|
|
30810
|
+
new AcpExecutionError(
|
|
30811
|
+
"Another native session operation is running",
|
|
30812
|
+
{},
|
|
30813
|
+
"native_session_busy"
|
|
30814
|
+
)
|
|
30815
|
+
);
|
|
30816
|
+
return;
|
|
30817
|
+
}
|
|
30818
|
+
const controller = new AbortController();
|
|
30819
|
+
const operationSignal = AbortSignal.any([connectionController.signal, controller.signal]);
|
|
30820
|
+
const timeout = timer.schedule(
|
|
30821
|
+
() => controller.abort(
|
|
30822
|
+
new AcpExecutionError(
|
|
30823
|
+
"Native session operation timed out",
|
|
30824
|
+
{},
|
|
30825
|
+
"native_session_timeout"
|
|
30826
|
+
)
|
|
30827
|
+
),
|
|
30828
|
+
9e4
|
|
30829
|
+
);
|
|
30830
|
+
const completion = (async () => {
|
|
30831
|
+
try {
|
|
30832
|
+
operationSignal.throwIfAborted();
|
|
30833
|
+
if (previous.length > 0) {
|
|
30834
|
+
await new Promise((resolve6, reject2) => {
|
|
30835
|
+
const cancel = () => reject2(operationSignal.reason);
|
|
30836
|
+
operationSignal.addEventListener("abort", cancel, { once: true });
|
|
30837
|
+
void Promise.all(previous.map((request) => request.completion)).then(
|
|
30838
|
+
() => {
|
|
30839
|
+
operationSignal.removeEventListener("abort", cancel);
|
|
30840
|
+
resolve6();
|
|
30841
|
+
},
|
|
30842
|
+
(error61) => {
|
|
30843
|
+
operationSignal.removeEventListener("abort", cancel);
|
|
30844
|
+
reject2(error61);
|
|
30845
|
+
}
|
|
30846
|
+
);
|
|
30847
|
+
});
|
|
30848
|
+
}
|
|
30849
|
+
operationSignal.throwIfAborted();
|
|
30850
|
+
const result = await nativeSessions.executeTurnFork(command2, operationSignal);
|
|
30851
|
+
if (socket.readyState === WebSocket2.OPEN) {
|
|
30852
|
+
send(socket, encodeNativeSessionResult(result));
|
|
30853
|
+
}
|
|
30854
|
+
} catch (error61) {
|
|
30855
|
+
const failure = operationSignal.aborted ? controller.signal.reason ?? new AcpExecutionError(
|
|
30856
|
+
"Native session operation cancelled",
|
|
30857
|
+
{},
|
|
30858
|
+
"native_session_cancelled"
|
|
30859
|
+
) : error61;
|
|
30860
|
+
audit.record({
|
|
30861
|
+
commandId: command2.requestId,
|
|
30862
|
+
details: { code: operationErrorCode(failure) },
|
|
30863
|
+
event: "native_session_turn_fork_failed",
|
|
30864
|
+
sessionId: command2.sessionId
|
|
30865
|
+
});
|
|
30866
|
+
reportOperationFailure(socket, command2.requestId, command2.sessionId, failure);
|
|
30867
|
+
} finally {
|
|
30868
|
+
timeout.cancel();
|
|
30869
|
+
nativeRequests.delete(command2.requestId);
|
|
30870
|
+
}
|
|
30871
|
+
})();
|
|
30872
|
+
nativeRequests.set(command2.requestId, {
|
|
30873
|
+
agentId: command2.agentId,
|
|
30874
|
+
completion,
|
|
30875
|
+
controller,
|
|
30876
|
+
sessionId: command2.sessionId
|
|
30877
|
+
});
|
|
30878
|
+
return;
|
|
30879
|
+
}
|
|
30251
30880
|
if (kind === RUNTIME_LIVENESS_READY_KIND) {
|
|
30252
30881
|
decodeRuntimeLivenessReadyEvent(encoded);
|
|
30253
30882
|
connectionLiveness.start();
|
|
@@ -30401,7 +31030,9 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
30401
31030
|
}
|
|
30402
31031
|
const turn = new ActiveDaemonTurn(command, receipt.attemptId);
|
|
30403
31032
|
activeTurns.set(command.commandId, turn);
|
|
30404
|
-
|
|
31033
|
+
if (command.disposition !== SessionPromptDisposition.Steer) {
|
|
31034
|
+
activeAttemptIds.set(command.sessionId, receipt.attemptId);
|
|
31035
|
+
}
|
|
30405
31036
|
void executePrompt(
|
|
30406
31037
|
outbox,
|
|
30407
31038
|
publisher,
|
|
@@ -30589,6 +31220,69 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
30589
31220
|
}
|
|
30590
31221
|
}
|
|
30591
31222
|
|
|
31223
|
+
// src/runtime.ts
|
|
31224
|
+
var APP_CREDENTIAL_PATTERN = /^mapp_[a-zA-Z0-9_-]{22}_[a-zA-Z0-9_-]{43}$/;
|
|
31225
|
+
var CONVERSATION_CREDENTIAL_PATTERN = /^mconv_[a-zA-Z0-9_-]{22}_[a-zA-Z0-9_-]{43}$/;
|
|
31226
|
+
var RUNTIME_CREDENTIAL_PATTERN = /^[a-zA-Z0-9_-]{32,256}$/;
|
|
31227
|
+
var MobiusRuntimeConfigurationError = class extends Error {
|
|
31228
|
+
code = "runtime_configuration_invalid";
|
|
31229
|
+
constructor(message, options = {}) {
|
|
31230
|
+
super(message, options);
|
|
31231
|
+
this.name = "MobiusRuntimeConfigurationError";
|
|
31232
|
+
}
|
|
31233
|
+
};
|
|
31234
|
+
var MobiusRuntimeState = class {
|
|
31235
|
+
};
|
|
31236
|
+
var IdleMobiusRuntimeState = class extends MobiusRuntimeState {
|
|
31237
|
+
};
|
|
31238
|
+
var RunningMobiusRuntimeState = class extends MobiusRuntimeState {
|
|
31239
|
+
};
|
|
31240
|
+
var FinishedMobiusRuntimeState = class extends MobiusRuntimeState {
|
|
31241
|
+
};
|
|
31242
|
+
function validateRuntimeRelay(relayUrl) {
|
|
31243
|
+
if (relayUrl.protocol !== "ws:" && relayUrl.protocol !== "wss:") {
|
|
31244
|
+
throw new MobiusRuntimeConfigurationError("The Runtime relay URL must use WebSocket transport");
|
|
31245
|
+
}
|
|
31246
|
+
const credential = relayUrl.searchParams.get("token") ?? "";
|
|
31247
|
+
if (credential.length === 0) {
|
|
31248
|
+
throw new MobiusRuntimeConfigurationError("The Runtime relay credential is required");
|
|
31249
|
+
}
|
|
31250
|
+
if (APP_CREDENTIAL_PATTERN.test(credential)) {
|
|
31251
|
+
throw new MobiusRuntimeConfigurationError("An AppCredential cannot be held by a local Runtime");
|
|
31252
|
+
}
|
|
31253
|
+
if (CONVERSATION_CREDENTIAL_PATTERN.test(credential)) {
|
|
31254
|
+
throw new MobiusRuntimeConfigurationError("A ConversationAccessToken cannot be held by a local Runtime");
|
|
31255
|
+
}
|
|
31256
|
+
if (!RUNTIME_CREDENTIAL_PATTERN.test(credential)) {
|
|
31257
|
+
throw new MobiusRuntimeConfigurationError("The Runtime relay credential is invalid");
|
|
31258
|
+
}
|
|
31259
|
+
}
|
|
31260
|
+
function validateRuntimeConfiguration(config2) {
|
|
31261
|
+
validateRuntimeRelay(config2.relayUrl);
|
|
31262
|
+
}
|
|
31263
|
+
var MobiusRuntimeEngine = class {
|
|
31264
|
+
#config;
|
|
31265
|
+
#state = new IdleMobiusRuntimeState();
|
|
31266
|
+
constructor(config2) {
|
|
31267
|
+
validateRuntimeConfiguration(config2);
|
|
31268
|
+
this.#config = config2;
|
|
31269
|
+
}
|
|
31270
|
+
run = async (signal) => {
|
|
31271
|
+
if (!(this.#state instanceof IdleMobiusRuntimeState)) {
|
|
31272
|
+
throw new MobiusRuntimeConfigurationError("A Mobius Runtime engine can only be started once");
|
|
31273
|
+
}
|
|
31274
|
+
signal.throwIfAborted();
|
|
31275
|
+
this.#state = new RunningMobiusRuntimeState();
|
|
31276
|
+
const audit = new AuditStore(this.#config.auditDatabasePath);
|
|
31277
|
+
try {
|
|
31278
|
+
await runDaemon(this.#config, audit, signal);
|
|
31279
|
+
} finally {
|
|
31280
|
+
audit.close();
|
|
31281
|
+
this.#state = new FinishedMobiusRuntimeState();
|
|
31282
|
+
}
|
|
31283
|
+
};
|
|
31284
|
+
};
|
|
31285
|
+
|
|
30592
31286
|
// src/cli.ts
|
|
30593
31287
|
var shutdown = new AbortController();
|
|
30594
31288
|
process.once("SIGINT", () => shutdown.abort());
|
|
@@ -30596,7 +31290,7 @@ process.once("SIGTERM", () => shutdown.abort());
|
|
|
30596
31290
|
var USAGE = `Mobius CLI
|
|
30597
31291
|
|
|
30598
31292
|
Usage:
|
|
30599
|
-
mobius pair --url <relay-url> --code <pairing-code> [--workspace <directory>]
|
|
31293
|
+
mobius pair --url <relay-url> --pairing <runtime-pairing-id> --code <pairing-code> [--workspace <directory>]
|
|
30600
31294
|
mobius start --channel <channel-id>
|
|
30601
31295
|
mobius agents [--json]
|
|
30602
31296
|
mobius bot message send --target <current|alias> --text <message>
|
|
@@ -30669,12 +31363,7 @@ async function main() {
|
|
|
30669
31363
|
process.stdout.write(`Default workspace: ${config2.defaultWorkspace}
|
|
30670
31364
|
|
|
30671
31365
|
`);
|
|
30672
|
-
|
|
30673
|
-
try {
|
|
30674
|
-
await runDaemon(config2, audit, shutdown.signal);
|
|
30675
|
-
} finally {
|
|
30676
|
-
audit.close();
|
|
30677
|
-
}
|
|
31366
|
+
await new MobiusRuntimeEngine(config2).run(shutdown.signal);
|
|
30678
31367
|
}
|
|
30679
31368
|
try {
|
|
30680
31369
|
await main();
|