@agimon-ai/doompi 0.0.1-alpha.91 → 0.0.1-alpha.93
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/src/builders/server/logSink.cjs +57 -0
- package/dist/src/builders/server/logSink.cjs.map +1 -0
- package/dist/src/builders/server/logSink.mjs +57 -0
- package/dist/src/builders/server/logSink.mjs.map +1 -0
- package/dist/src/builders/server/runtime.cjs +109 -32
- package/dist/src/builders/server/runtime.cjs.map +1 -1
- package/dist/src/builders/server/runtime.mjs +111 -34
- package/dist/src/builders/server/runtime.mjs.map +1 -1
- package/dist/src/composition/layerPackageInstaller/index.cjs +1 -1
- package/dist/src/composition/layerPackageInstaller/index.cjs.map +1 -1
- package/dist/src/composition/layerPackageInstaller/index.mjs +1 -1
- package/dist/src/composition/layerPackageInstaller/index.mjs.map +1 -1
- package/package.json +19 -18
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
let node_child_process = require("node:child_process");
|
|
2
|
+
let node_timers_promises = require("node:timers/promises");
|
|
3
|
+
let node_url = require("node:url");
|
|
4
|
+
let node_util = require("node:util");
|
|
5
|
+
let _agimon_ai_log_sink_mcp = require("@agimon-ai/log-sink-mcp");
|
|
6
|
+
//#region src/builders/server/logSink.ts
|
|
7
|
+
const START_TIMEOUT_MS = 5e3;
|
|
8
|
+
const DISCOVERY_TIMEOUT_MS = 4e3;
|
|
9
|
+
const HEALTH_TIMEOUT_MS = 750;
|
|
10
|
+
const DISCOVERY_INTERVAL_MS = 1100;
|
|
11
|
+
const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
12
|
+
async function healthy(env) {
|
|
13
|
+
let timer;
|
|
14
|
+
try {
|
|
15
|
+
return await Promise.race([(0, _agimon_ai_log_sink_mcp.resolveLogSinkPort)({
|
|
16
|
+
env,
|
|
17
|
+
healthCheck: true
|
|
18
|
+
}), new Promise((resolve) => {
|
|
19
|
+
timer = setTimeout(() => resolve(void 0), HEALTH_TIMEOUT_MS);
|
|
20
|
+
})]) !== void 0;
|
|
21
|
+
} finally {
|
|
22
|
+
if (timer) clearTimeout(timer);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Attach to the shared daemon or start it through its published CLI. Never own its shutdown. */
|
|
26
|
+
async function ensureGlobalLogSink(options) {
|
|
27
|
+
const { cwd, env, notice } = options;
|
|
28
|
+
let failure;
|
|
29
|
+
try {
|
|
30
|
+
if (await healthy(env)) return;
|
|
31
|
+
const cli = (0, node_url.fileURLToPath)({}.resolve("@agimon-ai/log-sink-mcp/cli"));
|
|
32
|
+
await execFileAsync(process.execPath, [
|
|
33
|
+
cli,
|
|
34
|
+
"start",
|
|
35
|
+
"--global",
|
|
36
|
+
"--http-only"
|
|
37
|
+
], {
|
|
38
|
+
cwd,
|
|
39
|
+
env,
|
|
40
|
+
timeout: START_TIMEOUT_MS,
|
|
41
|
+
maxBuffer: 65536
|
|
42
|
+
});
|
|
43
|
+
} catch (error) {
|
|
44
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
45
|
+
}
|
|
46
|
+
const deadline = Date.now() + DISCOVERY_TIMEOUT_MS;
|
|
47
|
+
do {
|
|
48
|
+
if (await healthy(env)) return;
|
|
49
|
+
if (Date.now() + DISCOVERY_INTERVAL_MS > deadline) break;
|
|
50
|
+
await (0, node_timers_promises.setTimeout)(DISCOVERY_INTERVAL_MS);
|
|
51
|
+
} while (Date.now() < deadline);
|
|
52
|
+
notice(`Global log sink unavailable: ${failure ?? "healthy discovery timed out"}. Telemetry remains nonfatal.`);
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
exports.ensureGlobalLogSink = ensureGlobalLogSink;
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=logSink.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logSink.cjs","names":["promisify","execFile","resolveLogSinkPort","fileURLToPath","sleep"],"sources":["../../../../src/builders/server/logSink.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { setTimeout as sleep } from 'node:timers/promises';\nimport { fileURLToPath } from 'node:url';\nimport { promisify } from 'node:util';\n\nimport { resolveLogSinkPort } from '@agimon-ai/log-sink-mcp';\n\nconst START_TIMEOUT_MS = 5_000;\nconst DISCOVERY_TIMEOUT_MS = 4_000;\nconst HEALTH_TIMEOUT_MS = 750;\nconst DISCOVERY_INTERVAL_MS = 1_100;\n\nconst execFileAsync = promisify(execFile);\n\nasync function healthy(env: NodeJS.ProcessEnv): Promise<boolean> {\n // Registry discovery may wait on a broken daemon's health endpoint. The\n // server must still start, without allowing that endpoint to select a local\n // fallback or hold the startup path indefinitely.\n let timer: NodeJS.Timeout | undefined;\n try {\n const result = await Promise.race([\n resolveLogSinkPort({ env, healthCheck: true }),\n new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), HEALTH_TIMEOUT_MS);\n }),\n ]);\n return result !== undefined;\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/** Attach to the shared daemon or start it through its published CLI. Never own its shutdown. */\nexport async function ensureGlobalLogSink(options: {\n cwd: string;\n env: NodeJS.ProcessEnv;\n notice: (message: string) => void;\n}): Promise<void> {\n const { cwd, env, notice } = options;\n let failure: string | undefined;\n try {\n if (await healthy(env)) return;\n const cli = fileURLToPath(import.meta.resolve('@agimon-ai/log-sink-mcp/cli'));\n await execFileAsync(process.execPath, [cli, 'start', '--global', '--http-only'], {\n cwd,\n env,\n timeout: START_TIMEOUT_MS,\n maxBuffer: 64 * 1024,\n });\n } catch (error) {\n failure = error instanceof Error ? error.message : String(error);\n }\n\n const deadline = Date.now() + DISCOVERY_TIMEOUT_MS;\n do {\n if (await healthy(env)) return;\n if (Date.now() + DISCOVERY_INTERVAL_MS > deadline) break;\n await sleep(DISCOVERY_INTERVAL_MS);\n } while (Date.now() < deadline);\n\n notice(`Global log sink unavailable: ${failure ?? 'healthy discovery timed out'}. Telemetry remains nonfatal.`);\n}\n"],"mappings":";;;;;;AAOA,MAAM,mBAAmB;AACzB,MAAM,uBAAuB;AAC7B,MAAM,oBAAoB;AAC1B,MAAM,wBAAwB;AAE9B,MAAM,iBAAA,GAAgBA,UAAAA,UAAAA,CAAUC,mBAAAA,QAAQ;AAExC,eAAe,QAAQ,KAA0C;CAI/D,IAAI;CACJ,IAAI;EAOF,OAAO,MANc,QAAQ,KAAK,EAAA,GAChCC,wBAAAA,mBAAAA,CAAmB;GAAE;GAAK,aAAa;EAAK,CAAC,GAC7C,IAAI,SAAoB,YAAY;GAClC,QAAQ,iBAAiB,QAAQ,KAAA,CAAS,GAAG,iBAAiB;EAChE,CAAC,CACH,CAAC,MACiB,KAAA;CACpB,UAAU;EACR,IAAI,OAAO,aAAa,KAAK;CAC/B;AACF;;AAGA,eAAsB,oBAAoB,SAIxB;CAChB,MAAM,EAAE,KAAK,KAAK,WAAW;CAC7B,IAAI;CACJ,IAAI;EACF,IAAI,MAAM,QAAQ,GAAG,GAAG;EACxB,MAAM,OAAA,GAAMC,SAAAA,cAAAA,CAAAA,CAAAA,EAA0B,QAAQ,6BAA6B,CAAC;EAC5E,MAAM,cAAc,QAAQ,UAAU;GAAC;GAAK;GAAS;GAAY;EAAa,GAAG;GAC/E;GACA;GACA,SAAS;GACT,WAAW;EACb,CAAC;CACH,SAAS,OAAO;EACd,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;CACjE;CAEA,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,GAAG;EACD,IAAI,MAAM,QAAQ,GAAG,GAAG;EACxB,IAAI,KAAK,IAAI,IAAI,wBAAwB,UAAU;EACnD,OAAA,GAAMC,qBAAAA,WAAAA,CAAM,qBAAqB;CACnC,SAAS,KAAK,IAAI,IAAI;CAEtB,OAAO,gCAAgC,WAAW,8BAA8B,8BAA8B;AAChH"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { resolveLogSinkPort } from "@agimon-ai/log-sink-mcp";
|
|
6
|
+
//#region src/builders/server/logSink.ts
|
|
7
|
+
const START_TIMEOUT_MS = 5e3;
|
|
8
|
+
const DISCOVERY_TIMEOUT_MS = 4e3;
|
|
9
|
+
const HEALTH_TIMEOUT_MS = 750;
|
|
10
|
+
const DISCOVERY_INTERVAL_MS = 1100;
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
async function healthy(env) {
|
|
13
|
+
let timer;
|
|
14
|
+
try {
|
|
15
|
+
return await Promise.race([resolveLogSinkPort({
|
|
16
|
+
env,
|
|
17
|
+
healthCheck: true
|
|
18
|
+
}), new Promise((resolve) => {
|
|
19
|
+
timer = setTimeout(() => resolve(void 0), HEALTH_TIMEOUT_MS);
|
|
20
|
+
})]) !== void 0;
|
|
21
|
+
} finally {
|
|
22
|
+
if (timer) clearTimeout(timer);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Attach to the shared daemon or start it through its published CLI. Never own its shutdown. */
|
|
26
|
+
async function ensureGlobalLogSink(options) {
|
|
27
|
+
const { cwd, env, notice } = options;
|
|
28
|
+
let failure;
|
|
29
|
+
try {
|
|
30
|
+
if (await healthy(env)) return;
|
|
31
|
+
const cli = fileURLToPath(import.meta.resolve("@agimon-ai/log-sink-mcp/cli"));
|
|
32
|
+
await execFileAsync(process.execPath, [
|
|
33
|
+
cli,
|
|
34
|
+
"start",
|
|
35
|
+
"--global",
|
|
36
|
+
"--http-only"
|
|
37
|
+
], {
|
|
38
|
+
cwd,
|
|
39
|
+
env,
|
|
40
|
+
timeout: START_TIMEOUT_MS,
|
|
41
|
+
maxBuffer: 65536
|
|
42
|
+
});
|
|
43
|
+
} catch (error) {
|
|
44
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
45
|
+
}
|
|
46
|
+
const deadline = Date.now() + DISCOVERY_TIMEOUT_MS;
|
|
47
|
+
do {
|
|
48
|
+
if (await healthy(env)) return;
|
|
49
|
+
if (Date.now() + DISCOVERY_INTERVAL_MS > deadline) break;
|
|
50
|
+
await setTimeout$1(DISCOVERY_INTERVAL_MS);
|
|
51
|
+
} while (Date.now() < deadline);
|
|
52
|
+
notice(`Global log sink unavailable: ${failure ?? "healthy discovery timed out"}. Telemetry remains nonfatal.`);
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { ensureGlobalLogSink };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=logSink.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logSink.mjs","names":["sleep"],"sources":["../../../../src/builders/server/logSink.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { setTimeout as sleep } from 'node:timers/promises';\nimport { fileURLToPath } from 'node:url';\nimport { promisify } from 'node:util';\n\nimport { resolveLogSinkPort } from '@agimon-ai/log-sink-mcp';\n\nconst START_TIMEOUT_MS = 5_000;\nconst DISCOVERY_TIMEOUT_MS = 4_000;\nconst HEALTH_TIMEOUT_MS = 750;\nconst DISCOVERY_INTERVAL_MS = 1_100;\n\nconst execFileAsync = promisify(execFile);\n\nasync function healthy(env: NodeJS.ProcessEnv): Promise<boolean> {\n // Registry discovery may wait on a broken daemon's health endpoint. The\n // server must still start, without allowing that endpoint to select a local\n // fallback or hold the startup path indefinitely.\n let timer: NodeJS.Timeout | undefined;\n try {\n const result = await Promise.race([\n resolveLogSinkPort({ env, healthCheck: true }),\n new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), HEALTH_TIMEOUT_MS);\n }),\n ]);\n return result !== undefined;\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/** Attach to the shared daemon or start it through its published CLI. Never own its shutdown. */\nexport async function ensureGlobalLogSink(options: {\n cwd: string;\n env: NodeJS.ProcessEnv;\n notice: (message: string) => void;\n}): Promise<void> {\n const { cwd, env, notice } = options;\n let failure: string | undefined;\n try {\n if (await healthy(env)) return;\n const cli = fileURLToPath(import.meta.resolve('@agimon-ai/log-sink-mcp/cli'));\n await execFileAsync(process.execPath, [cli, 'start', '--global', '--http-only'], {\n cwd,\n env,\n timeout: START_TIMEOUT_MS,\n maxBuffer: 64 * 1024,\n });\n } catch (error) {\n failure = error instanceof Error ? error.message : String(error);\n }\n\n const deadline = Date.now() + DISCOVERY_TIMEOUT_MS;\n do {\n if (await healthy(env)) return;\n if (Date.now() + DISCOVERY_INTERVAL_MS > deadline) break;\n await sleep(DISCOVERY_INTERVAL_MS);\n } while (Date.now() < deadline);\n\n notice(`Global log sink unavailable: ${failure ?? 'healthy discovery timed out'}. Telemetry remains nonfatal.`);\n}\n"],"mappings":";;;;;;AAOA,MAAM,mBAAmB;AACzB,MAAM,uBAAuB;AAC7B,MAAM,oBAAoB;AAC1B,MAAM,wBAAwB;AAE9B,MAAM,gBAAgB,UAAU,QAAQ;AAExC,eAAe,QAAQ,KAA0C;CAI/D,IAAI;CACJ,IAAI;EAOF,OAAO,MANc,QAAQ,KAAK,CAChC,mBAAmB;GAAE;GAAK,aAAa;EAAK,CAAC,GAC7C,IAAI,SAAoB,YAAY;GAClC,QAAQ,iBAAiB,QAAQ,KAAA,CAAS,GAAG,iBAAiB;EAChE,CAAC,CACH,CAAC,MACiB,KAAA;CACpB,UAAU;EACR,IAAI,OAAO,aAAa,KAAK;CAC/B;AACF;;AAGA,eAAsB,oBAAoB,SAIxB;CAChB,MAAM,EAAE,KAAK,KAAK,WAAW;CAC7B,IAAI;CACJ,IAAI;EACF,IAAI,MAAM,QAAQ,GAAG,GAAG;EACxB,MAAM,MAAM,cAAc,YAAY,QAAQ,6BAA6B,CAAC;EAC5E,MAAM,cAAc,QAAQ,UAAU;GAAC;GAAK;GAAS;GAAY;EAAa,GAAG;GAC/E;GACA;GACA,SAAS;GACT,WAAW;EACb,CAAC;CACH,SAAS,OAAO;EACd,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;CACjE;CAEA,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,GAAG;EACD,IAAI,MAAM,QAAQ,GAAG,GAAG;EACxB,IAAI,KAAK,IAAI,IAAI,wBAAwB,UAAU;EACnD,MAAMA,aAAM,qBAAqB;CACnC,SAAS,KAAK,IAAI,IAAI;CAEtB,OAAO,gCAAgC,WAAW,8BAA8B,8BAA8B;AAChH"}
|
|
@@ -6,6 +6,7 @@ const require_index$3 = require("../cli/bootstrapLocator/index.cjs");
|
|
|
6
6
|
const require_index$4 = require("../../composition/syncDrift/index.cjs");
|
|
7
7
|
const require_harnessContext = require("../cli/harnessContext.cjs");
|
|
8
8
|
const require_computerUseBinding = require("./computerUseBinding.cjs");
|
|
9
|
+
const require_logSink = require("./logSink.cjs");
|
|
9
10
|
const require_selectionStatus = require("./selectionStatus.cjs");
|
|
10
11
|
const require_sessionArguments = require("./sessionArguments.cjs");
|
|
11
12
|
const require_sessionArtifact = require("./sessionArtifact.cjs");
|
|
@@ -53,7 +54,16 @@ async function bounded(operation, label, notice) {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
async function runServerRuntime(options, runtime) {
|
|
56
|
-
const { cwd: baseCwd, environment:
|
|
57
|
+
const { cwd: baseCwd, environment: incomingEnvironment, notice, resolveHarnessOptions, signal, syncWorkspace } = runtime;
|
|
58
|
+
const baseEnvironment = {
|
|
59
|
+
...incomingEnvironment,
|
|
60
|
+
LOG_SINK_INSTANCE: "global"
|
|
61
|
+
};
|
|
62
|
+
await require_logSink.ensureGlobalLogSink({
|
|
63
|
+
cwd: baseCwd,
|
|
64
|
+
env: baseEnvironment,
|
|
65
|
+
notice
|
|
66
|
+
}).catch((error) => notice(`Global log sink unavailable: ${error instanceof Error ? error.message : String(error)}`));
|
|
57
67
|
const telemetry = (0, _agimon_ai_doompi_core_serverTelemetry.createServerTelemetry)({
|
|
58
68
|
cwd: baseCwd,
|
|
59
69
|
env: baseEnvironment,
|
|
@@ -61,6 +71,34 @@ async function runServerRuntime(options, runtime) {
|
|
|
61
71
|
});
|
|
62
72
|
const homeDirectory = baseEnvironment.HOME ?? node_os.default.homedir();
|
|
63
73
|
const serverDirectory = node_path.default.join((0, _agimon_ai_doompi_core_piSettings.piAgentDirectory)(baseEnvironment), "server");
|
|
74
|
+
const requestReceipts = (0, _agimon_ai_doompi_core_history.createRequestReceipts)({ directory: node_path.default.join(serverDirectory, "request-receipts") });
|
|
75
|
+
const activeMedia = /* @__PURE__ */ new Set();
|
|
76
|
+
const mediaArbitration = {
|
|
77
|
+
register(busy) {
|
|
78
|
+
activeMedia.add(busy);
|
|
79
|
+
return () => {
|
|
80
|
+
activeMedia.delete(busy);
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
available(busy) {
|
|
84
|
+
return activeMedia.has(busy) && [...activeMedia].every((other) => other === busy || !other());
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const activePeerAgents = /* @__PURE__ */ new Map();
|
|
88
|
+
const peerAgents = {
|
|
89
|
+
register(sessionId, agent, hubToken) {
|
|
90
|
+
if (activePeerAgents.has(sessionId)) throw new Error(`Voice peer agent '${sessionId}' is already registered.`);
|
|
91
|
+
const entry = {
|
|
92
|
+
fetch: (request) => agent.fetch(request),
|
|
93
|
+
hubToken
|
|
94
|
+
};
|
|
95
|
+
activePeerAgents.set(sessionId, entry);
|
|
96
|
+
return () => {
|
|
97
|
+
if (activePeerAgents.get(sessionId) === entry) activePeerAgents.delete(sessionId);
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
get: (sessionId) => activePeerAgents.get(sessionId)
|
|
101
|
+
};
|
|
64
102
|
const workspaces = (0, _agimon_ai_doompi_core_history.createWorkspaceRegistry)({
|
|
65
103
|
directory: serverDirectory,
|
|
66
104
|
onNotice: notice
|
|
@@ -265,7 +303,10 @@ async function runServerRuntime(options, runtime) {
|
|
|
265
303
|
environment: baseEnvironment,
|
|
266
304
|
hubToken: token,
|
|
267
305
|
sessionService: hub.sessionService,
|
|
306
|
+
mediaArbitration,
|
|
307
|
+
peerAgents,
|
|
268
308
|
directEvents: hub.directEvents,
|
|
309
|
+
requestApi: (mount, basePath, request) => hub.requestApi(mount, basePath, request),
|
|
269
310
|
repositories: () => hub.workspaces().map((workspace) => ({
|
|
270
311
|
id: workspace.id,
|
|
271
312
|
path: workspace.root,
|
|
@@ -316,6 +357,7 @@ async function runServerRuntime(options, runtime) {
|
|
|
316
357
|
await hub.mountFacets(globalBundle.facets, {
|
|
317
358
|
...sharedApiContext,
|
|
318
359
|
scope: "global",
|
|
360
|
+
requestReceipts: requestReceipts.receipts,
|
|
319
361
|
remoteControl: { fetch: (request) => remoteRuntime.fetchLocal(request) }
|
|
320
362
|
});
|
|
321
363
|
webCompositions.publish({ scope: "global" }, (0, _agimon_ai_doompi_core_syncRegistration.readSyncRegistration)(globalRoot, homeDirectory), hub.channelTypes());
|
|
@@ -382,9 +424,11 @@ async function runServerRuntime(options, runtime) {
|
|
|
382
424
|
for (const record of openSessions.list()) {
|
|
383
425
|
if (workspaces.list().some((workspace) => workspace.id === record.workspaceId)) continue;
|
|
384
426
|
try {
|
|
385
|
-
const
|
|
427
|
+
const candidate = record.groupingRoot ?? (record.sessionProvenance === "worktree" ? record.artifact?.root : record.cwd);
|
|
428
|
+
if (!candidate) throw new Error("Parent workspace location is unavailable.");
|
|
429
|
+
const root = node_fs.default.realpathSync(require_index$1.findRepositoryRoot(candidate));
|
|
386
430
|
const id = (0, _agimon_ai_doompi_core_syncLocation.resolveSyncLocation)(root, homeDirectory).identity.worktreeId;
|
|
387
|
-
if (id === record.workspaceId) workspaces.add({
|
|
431
|
+
if ((record.groupingRoot === void 0 || root === candidate) && id === record.workspaceId) workspaces.add({
|
|
388
432
|
id,
|
|
389
433
|
root
|
|
390
434
|
});
|
|
@@ -427,6 +471,8 @@ async function runServerRuntime(options, runtime) {
|
|
|
427
471
|
repoRoot: policyOptions.repoRoot,
|
|
428
472
|
sessionId: identity.sessionId,
|
|
429
473
|
workspaceId: workspaceId ?? (0, _agimon_ai_doompi_core_syncLocation.resolveSyncLocation)(policyOptions.repoRoot, homeDirectory).identity.worktreeId,
|
|
474
|
+
groupingRoot: workspaceId === void 0 ? policyOptions.repoRoot : hub.workspaces().find((workspace) => workspace.id === workspaceId)?.root,
|
|
475
|
+
...identity.sessionProvenance === "worktree" ? { inheritedArtifact: registration } : {},
|
|
430
476
|
sessionName: identity.sessionName,
|
|
431
477
|
webComposition: webCompositions?.publish({
|
|
432
478
|
scope: "session",
|
|
@@ -477,6 +523,7 @@ async function runServerRuntime(options, runtime) {
|
|
|
477
523
|
cwd: sessionOptions.cwd,
|
|
478
524
|
environment: sessionOptions.environment,
|
|
479
525
|
directEvents: hub.directEvents,
|
|
526
|
+
requestApi: (mount, basePath, request) => hub.requestApi(mount, basePath, request),
|
|
480
527
|
...computerUse === void 0 ? {} : { computerUse: {
|
|
481
528
|
get available() {
|
|
482
529
|
return computerUse.available;
|
|
@@ -490,6 +537,8 @@ async function runServerRuntime(options, runtime) {
|
|
|
490
537
|
} },
|
|
491
538
|
hubToken: token,
|
|
492
539
|
sessionService: hub.sessionService,
|
|
540
|
+
mediaArbitration,
|
|
541
|
+
peerAgents,
|
|
493
542
|
pluginRegistry: hub.pluginRegistry,
|
|
494
543
|
apis: [],
|
|
495
544
|
facets: pendingSessions.get(sessionOptions.sessionId)?.bundle.facets ?? [],
|
|
@@ -560,7 +609,7 @@ async function runServerRuntime(options, runtime) {
|
|
|
560
609
|
await bounded(harnessTelemetry.flush(), "initial composition telemetry flush", notice);
|
|
561
610
|
}
|
|
562
611
|
const reservedOpens = /* @__PURE__ */ new Map();
|
|
563
|
-
openSession = async (request, sessionId, pinnedArtifact) => {
|
|
612
|
+
openSession = async (request, sessionId, pinnedArtifact, groupingWorkspaceId) => {
|
|
564
613
|
request.signal?.throwIfAborted();
|
|
565
614
|
if (request.reservationId !== void 0) {
|
|
566
615
|
if (!request.parentSessionId || !cockpit) throw new Error("A reserved session requires its owning parent.");
|
|
@@ -591,7 +640,7 @@ async function runServerRuntime(options, runtime) {
|
|
|
591
640
|
sessionProvenance: request.sessionProvenance
|
|
592
641
|
};
|
|
593
642
|
const isWorktree = request.sessionProvenance === "worktree";
|
|
594
|
-
const inheritedWorkspaceId = isWorktree && request.parentSessionId ? hub.session(request.parentSessionId)?.workspaceId ?? openSessions.list().find((record) => record.sessionId === request.parentSessionId)?.workspaceId : void 0;
|
|
643
|
+
const inheritedWorkspaceId = isWorktree && request.parentSessionId ? groupingWorkspaceId ?? hub.session(request.parentSessionId)?.workspaceId ?? openSessions.list().find((record) => record.sessionId === request.parentSessionId)?.workspaceId : void 0;
|
|
595
644
|
if (isWorktree && inheritedWorkspaceId === void 0) throw new Error("Worktree session requires its parent workspace.");
|
|
596
645
|
const parentArtifact = request.parentSessionId === void 0 ? void 0 : sessionArtifacts.get(request.parentSessionId)?.registration ?? pendingSessions.get(request.parentSessionId)?.registration ?? openSessions.list().find((record) => record.sessionId === request.parentSessionId)?.artifact;
|
|
597
646
|
const start = (async () => {
|
|
@@ -645,6 +694,8 @@ async function runServerRuntime(options, runtime) {
|
|
|
645
694
|
sessionId: created.id,
|
|
646
695
|
workspaceId: created.workspaceId,
|
|
647
696
|
cwd: created.cwd,
|
|
697
|
+
repoRoot: childContext.options.repoRoot,
|
|
698
|
+
groupingRoot: hub.workspaces().find((workspace) => workspace.id === created.workspaceId)?.root,
|
|
648
699
|
name: created.name,
|
|
649
700
|
createdAt: created.createdAt,
|
|
650
701
|
...created.parentSessionId === void 0 ? {} : { parentSessionId: created.parentSessionId },
|
|
@@ -675,6 +726,22 @@ async function runServerRuntime(options, runtime) {
|
|
|
675
726
|
};
|
|
676
727
|
});
|
|
677
728
|
const workspaceResumes = /* @__PURE__ */ new Map();
|
|
729
|
+
const savedHistory = async (workspaceId) => {
|
|
730
|
+
const root = hub.workspaces().find((workspace) => workspace.id === workspaceId && workspace.available !== false)?.root;
|
|
731
|
+
if (!root) throw new Error("Workspace not found.");
|
|
732
|
+
return (0, _agimon_ai_doompi_core_history.listSavedSessionRecords)(node_path.default.join(serverDirectory, "sessions"), root, new Set(hub.snapshot().map((active) => active.id)), workspaceId);
|
|
733
|
+
};
|
|
734
|
+
const validatedExecution = (target, workspaceId) => {
|
|
735
|
+
const root = hub.workspaces().find((workspace) => workspace.id === workspaceId && workspace.available !== false)?.root;
|
|
736
|
+
if (!root || target.groupingRoot !== root || target.workspaceId && target.workspaceId !== workspaceId) throw new Error("Saved session does not belong to this workspace.");
|
|
737
|
+
if (node_fs.default.realpathSync(require_index$1.findRepositoryRoot(target.cwd)) !== target.repoRoot) throw new Error("Saved session checkout is unavailable.");
|
|
738
|
+
if (target.sessionProvenance !== "worktree") return void 0;
|
|
739
|
+
if (!target.parentSessionId || target.inheritedArtifact === void 0) throw new Error("Saved worktree generation is unavailable.");
|
|
740
|
+
const artifact = (0, _agimon_ai_doompi_core_syncRegistration.parseSyncRegistration)(target.inheritedArtifact, "saved worktree generation");
|
|
741
|
+
(0, _agimon_ai_doompi_core_syncRegistration.validateSyncRegistration)(artifact, (0, _agimon_ai_doompi_core_syncLocation.resolveSyncLocation)(artifact.root, homeDirectory));
|
|
742
|
+
if (artifact.root !== root) throw new Error("Saved worktree generation belongs to another workspace.");
|
|
743
|
+
return artifact;
|
|
744
|
+
};
|
|
678
745
|
cockpit = await (0, _agimon_ai_doompi_core_headlessServer.serveHeadlessServer)({
|
|
679
746
|
port: options.webPort,
|
|
680
747
|
headlessHub: hub,
|
|
@@ -683,25 +750,22 @@ async function runServerRuntime(options, runtime) {
|
|
|
683
750
|
sessionMcpStateDir: serverDirectory,
|
|
684
751
|
sessionMcpPublicOriginRevision: () => remoteRuntime?.remote.publicOriginRevision() ?? 0,
|
|
685
752
|
isSessionPersisted: (sessionId, cwd) => openSessions.list().some((record) => record.sessionId === sessionId && record.cwd === cwd),
|
|
686
|
-
workspaceHistory: (workspaceId) =>
|
|
687
|
-
const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === workspaceId && workspace.available !== false)?.root;
|
|
688
|
-
if (!workspaceRoot) throw new Error("Workspace not found.");
|
|
689
|
-
return (0, _agimon_ai_doompi_core_history.listSavedSessions)(node_path.default.join(serverDirectory, "sessions"), workspaceRoot, new Set(hub.snapshot().map((active) => active.id)));
|
|
690
|
-
},
|
|
753
|
+
workspaceHistory: async (workspaceId) => (await savedHistory(workspaceId)).map((record) => record.summary),
|
|
691
754
|
resumeWorkspaceSession: async (workspaceId, targetSessionId) => {
|
|
692
755
|
const key = `${workspaceId}\0${targetSessionId}`;
|
|
693
756
|
const pending = workspaceResumes.get(key);
|
|
694
757
|
if (pending) return pending;
|
|
695
758
|
const resume = (async () => {
|
|
696
|
-
const
|
|
697
|
-
if (!workspaceRoot) throw new Error("Workspace not found.");
|
|
698
|
-
const target = (await (0, _agimon_ai_doompi_core_history.listSavedSessions)(node_path.default.join(serverDirectory, "sessions"), workspaceRoot, new Set(hub.snapshot().map((active) => active.id)))).find((item) => item.id === targetSessionId);
|
|
759
|
+
const target = (await savedHistory(workspaceId)).find((item) => item.summary.id === targetSessionId);
|
|
699
760
|
if (!target) throw new Error("Saved Pi thread not found in this workspace.");
|
|
761
|
+
const artifact = validatedExecution(target.execution, workspaceId);
|
|
700
762
|
await openSession({
|
|
701
|
-
cwd:
|
|
702
|
-
name: target.name ?? "untitled"
|
|
703
|
-
|
|
704
|
-
|
|
763
|
+
cwd: target.execution.cwd,
|
|
764
|
+
name: target.summary.name ?? "untitled",
|
|
765
|
+
...target.execution.parentSessionId ? { parentSessionId: target.execution.parentSessionId } : {},
|
|
766
|
+
...target.execution.sessionProvenance ? { sessionProvenance: target.execution.sessionProvenance } : {}
|
|
767
|
+
}, target.summary.id, artifact, target.execution.sessionProvenance === "worktree" ? workspaceId : void 0);
|
|
768
|
+
return target.summary.id;
|
|
705
769
|
})();
|
|
706
770
|
workspaceResumes.set(key, resume);
|
|
707
771
|
try {
|
|
@@ -713,19 +777,24 @@ async function runServerRuntime(options, runtime) {
|
|
|
713
777
|
sessionHistory: (session) => {
|
|
714
778
|
const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === session.workspaceId)?.root;
|
|
715
779
|
if (!workspaceRoot) throw new Error("Session workspace not found.");
|
|
716
|
-
return (0, _agimon_ai_doompi_core_history.listSavedSessions)(node_path.default.join((0, _agimon_ai_doompi_core_piSettings.piAgentDirectory)(baseEnvironment), "server", "sessions"), workspaceRoot, new Set(hub.snapshot().map((active) => active.id)));
|
|
780
|
+
return (0, _agimon_ai_doompi_core_history.listSavedSessions)(node_path.default.join((0, _agimon_ai_doompi_core_piSettings.piAgentDirectory)(baseEnvironment), "server", "sessions"), workspaceRoot, new Set(hub.snapshot().map((active) => active.id)), session.workspaceId);
|
|
717
781
|
},
|
|
718
782
|
readDormantTranscript: (record, request, context) => {
|
|
719
783
|
const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === record.workspaceId)?.root;
|
|
720
|
-
if (!workspaceRoot) throw new Error("Saved transcript unavailable.");
|
|
784
|
+
if (!workspaceRoot || record.groupingRoot !== void 0 && record.groupingRoot !== workspaceRoot) throw new Error("Saved transcript unavailable.");
|
|
785
|
+
const owner = node_fs.default.realpathSync(require_index$1.findRepositoryRoot(record.cwd));
|
|
786
|
+
if (record.repoRoot !== void 0 && record.repoRoot !== owner) throw new Error("Saved transcript checkout has changed.");
|
|
721
787
|
return (0, _agimon_ai_doompi_core_history.readSqliteTranscript)(node_path.default.join((0, _agimon_ai_doompi_core_piSettings.piAgentDirectory)(baseEnvironment), "server", "sessions", `${record.sessionId}.sqlite`), request, context, {
|
|
722
788
|
sessionId: record.sessionId,
|
|
723
|
-
workspaceRoot
|
|
789
|
+
workspaceRoot: owner,
|
|
790
|
+
workspaceId: record.workspaceId,
|
|
791
|
+
groupingRoot: workspaceRoot
|
|
724
792
|
});
|
|
725
793
|
},
|
|
726
794
|
restartSession: async (session) => {
|
|
727
795
|
const worktree = session.sessionProvenance === "worktree";
|
|
728
796
|
const artifact = sessionArtifacts.get(session.id)?.registration ?? openSessions.list().find((record) => record.sessionId === session.id)?.artifact;
|
|
797
|
+
if (worktree && !artifact) throw new Error("Worktree generation is unavailable; the session is still running.");
|
|
729
798
|
if (!worktree) {
|
|
730
799
|
const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === session.workspaceId)?.root;
|
|
731
800
|
if (!workspaceRoot) throw new Error("Session workspace not found.");
|
|
@@ -750,30 +819,33 @@ async function runServerRuntime(options, runtime) {
|
|
|
750
819
|
}, session.id, worktree ? artifact : void 0);
|
|
751
820
|
},
|
|
752
821
|
resumeSession: async (session, targetSessionId) => {
|
|
753
|
-
const
|
|
754
|
-
if (!
|
|
755
|
-
const target = (await (
|
|
822
|
+
const workspaceId = session.workspaceId;
|
|
823
|
+
if (!workspaceId || !hub.workspaces().some((workspace) => workspace.id === workspaceId)) throw new Error("Session workspace not found.");
|
|
824
|
+
const target = (await savedHistory(workspaceId)).find((item) => item.summary.id === targetSessionId);
|
|
756
825
|
if (!target) throw new Error("Saved Pi thread not found in this workspace.");
|
|
757
|
-
const
|
|
758
|
-
const artifact = sessionArtifacts.get(session.id)?.registration ?? openSessions.list().find((record) => record.sessionId === session.id)?.artifact;
|
|
826
|
+
const artifact = validatedExecution(target.execution, workspaceId);
|
|
759
827
|
await hub.closeSession(session.id);
|
|
760
828
|
await openSession({
|
|
761
|
-
cwd:
|
|
762
|
-
name: target.name ?? "untitled",
|
|
763
|
-
...
|
|
764
|
-
...
|
|
765
|
-
}, target.id, worktree ?
|
|
766
|
-
return target.id;
|
|
829
|
+
cwd: target.execution.cwd,
|
|
830
|
+
name: target.summary.name ?? "untitled",
|
|
831
|
+
...target.execution.parentSessionId ? { parentSessionId: target.execution.parentSessionId } : {},
|
|
832
|
+
...target.execution.sessionProvenance ? { sessionProvenance: target.execution.sessionProvenance } : {}
|
|
833
|
+
}, target.summary.id, artifact, target.execution.sessionProvenance === "worktree" ? workspaceId : void 0);
|
|
834
|
+
return target.summary.id;
|
|
767
835
|
},
|
|
768
836
|
dormantSessions: () => openSessions.list(),
|
|
769
837
|
removeDormantSession: (record) => openSessions.remove(record.sessionId),
|
|
770
838
|
reviveSession: async (record) => {
|
|
839
|
+
const groupingRoot = hub.workspaces().find((workspace) => workspace.id === record.workspaceId && workspace.available !== false)?.root;
|
|
840
|
+
if (!groupingRoot || record.groupingRoot !== void 0 && groupingRoot !== record.groupingRoot) throw new Error("The session workspace is unavailable.");
|
|
841
|
+
if (record.repoRoot !== void 0 && node_fs.default.realpathSync(require_index$1.findRepositoryRoot(record.cwd)) !== record.repoRoot) throw new Error("The session checkout has changed.");
|
|
842
|
+
if (record.sessionProvenance === "worktree" && record.artifact?.root !== groupingRoot) throw new Error("The inherited worktree generation is unavailable.");
|
|
771
843
|
await openSession({
|
|
772
844
|
cwd: record.cwd,
|
|
773
845
|
name: record.name,
|
|
774
846
|
...record.parentSessionId === void 0 ? {} : { parentSessionId: record.parentSessionId },
|
|
775
847
|
...record.sessionProvenance === void 0 ? {} : { sessionProvenance: record.sessionProvenance }
|
|
776
|
-
}, record.sessionId, record.sessionProvenance === "worktree" ? record.artifact : void 0);
|
|
848
|
+
}, record.sessionId, record.sessionProvenance === "worktree" ? record.artifact : void 0, record.sessionProvenance === "worktree" ? record.workspaceId : void 0);
|
|
777
849
|
},
|
|
778
850
|
requestAsset: (request) => webCompositions?.request(request) ?? Promise.resolve(void 0),
|
|
779
851
|
compositions: () => ({
|
|
@@ -816,6 +888,11 @@ async function runServerRuntime(options, runtime) {
|
|
|
816
888
|
} catch (error) {
|
|
817
889
|
notice(error instanceof Error ? error.message : String(error));
|
|
818
890
|
}
|
|
891
|
+
try {
|
|
892
|
+
await requestReceipts.close();
|
|
893
|
+
} catch (error) {
|
|
894
|
+
notice(error instanceof Error ? error.message : String(error));
|
|
895
|
+
}
|
|
819
896
|
try {
|
|
820
897
|
await sessionManager.close();
|
|
821
898
|
} catch (error) {
|