@noopolis/daimon 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +54 -10
  2. package/dist/core/siblingBuildFreshness.d.ts +21 -0
  3. package/dist/core/siblingBuildFreshness.d.ts.map +1 -0
  4. package/dist/core/siblingBuildFreshness.js +25 -0
  5. package/dist/core/types.d.ts +10 -1
  6. package/dist/core/types.d.ts.map +1 -1
  7. package/dist/examples/exampleCausalId.d.ts +3 -0
  8. package/dist/examples/exampleCausalId.d.ts.map +1 -0
  9. package/dist/examples/exampleCausalId.js +8 -0
  10. package/dist/examples/jungian-play-org.js +3 -2
  11. package/dist/examples/jungian-triad-org.js +3 -2
  12. package/dist/examples/jungianPlayAgent.d.ts +1 -1
  13. package/dist/examples/jungianPlayAgent.d.ts.map +1 -1
  14. package/dist/examples/jungianPlayAgent.js +1 -1
  15. package/dist/examples/jungianProfiles.d.ts +1 -1
  16. package/dist/examples/jungianProfiles.d.ts.map +1 -1
  17. package/dist/examples/jungianTriadProfiles.d.ts +1 -1
  18. package/dist/examples/jungianTriadProfiles.d.ts.map +1 -1
  19. package/dist/examples/pi-agent.js +3 -2
  20. package/dist/examples/pi-memory-org.js +6 -5
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -0
  24. package/dist/mcp/toolServer.d.ts +14 -0
  25. package/dist/mcp/toolServer.d.ts.map +1 -0
  26. package/dist/mcp/toolServer.js +105 -0
  27. package/dist/observability/causalEvents.d.ts +118 -0
  28. package/dist/observability/causalEvents.d.ts.map +1 -0
  29. package/dist/observability/causalEvents.js +135 -0
  30. package/dist/observability/controlCausal.d.ts +117 -0
  31. package/dist/observability/controlCausal.d.ts.map +1 -0
  32. package/dist/observability/controlCausal.js +107 -0
  33. package/dist/observability/emitCausalFixture.d.ts +25 -0
  34. package/dist/observability/emitCausalFixture.d.ts.map +1 -0
  35. package/dist/observability/emitCausalFixture.js +98 -0
  36. package/dist/observability/index.d.ts +2 -0
  37. package/dist/observability/index.d.ts.map +1 -1
  38. package/dist/observability/index.js +2 -0
  39. package/dist/pi/cliSession.d.ts +46 -0
  40. package/dist/pi/cliSession.d.ts.map +1 -0
  41. package/dist/pi/cliSession.js +224 -0
  42. package/dist/pi/index.d.ts +5 -0
  43. package/dist/pi/index.d.ts.map +1 -1
  44. package/dist/pi/index.js +5 -0
  45. package/dist/pi/memoryTools.d.ts +55 -1
  46. package/dist/pi/memoryTools.d.ts.map +1 -1
  47. package/dist/pi/memoryTools.js +144 -24
  48. package/dist/pi/modelRegistry.d.ts +13 -0
  49. package/dist/pi/modelRegistry.d.ts.map +1 -0
  50. package/dist/pi/modelRegistry.js +19 -0
  51. package/dist/pi/piAgentHandle.d.ts +45 -0
  52. package/dist/pi/piAgentHandle.d.ts.map +1 -0
  53. package/dist/pi/piAgentHandle.js +324 -0
  54. package/dist/pi/piAgentWakeSupport.d.ts +68 -0
  55. package/dist/pi/piAgentWakeSupport.d.ts.map +1 -0
  56. package/dist/pi/piAgentWakeSupport.js +147 -0
  57. package/dist/pi/piHarness.d.ts +30 -4
  58. package/dist/pi/piHarness.d.ts.map +1 -1
  59. package/dist/pi/piHarness.js +97 -255
  60. package/dist/pi/prompts.d.ts +9 -0
  61. package/dist/pi/prompts.d.ts.map +1 -0
  62. package/dist/pi/prompts.js +39 -0
  63. package/dist/pi/rawTrainingCapture.d.ts +59 -0
  64. package/dist/pi/rawTrainingCapture.d.ts.map +1 -0
  65. package/dist/pi/rawTrainingCapture.js +175 -0
  66. package/dist/pi/turnCausal.d.ts +73 -0
  67. package/dist/pi/turnCausal.d.ts.map +1 -0
  68. package/dist/pi/turnCausal.js +75 -0
  69. package/dist/pi/turnTrace.d.ts +128 -0
  70. package/dist/pi/turnTrace.d.ts.map +1 -0
  71. package/dist/pi/turnTrace.js +141 -0
  72. package/dist/pi/wakeAcceptance.d.ts +49 -0
  73. package/dist/pi/wakeAcceptance.d.ts.map +1 -0
  74. package/dist/pi/wakeAcceptance.js +281 -0
  75. package/dist/pi/wakeAcceptanceFs.d.ts +44 -0
  76. package/dist/pi/wakeAcceptanceFs.d.ts.map +1 -0
  77. package/dist/pi/wakeAcceptanceFs.js +318 -0
  78. package/dist/pi/wakeAcceptanceSchema.d.ts +75 -0
  79. package/dist/pi/wakeAcceptanceSchema.d.ts.map +1 -0
  80. package/dist/pi/wakeAcceptanceSchema.js +265 -0
  81. package/dist/pi/wakeModes.d.ts +9 -0
  82. package/dist/pi/wakeModes.d.ts.map +1 -0
  83. package/dist/pi/wakeModes.js +19 -0
  84. package/dist/pi/worldNudge.d.ts +22 -0
  85. package/dist/pi/worldNudge.d.ts.map +1 -0
  86. package/dist/pi/worldNudge.js +62 -0
  87. package/dist/pi/worldToolProtocol.d.ts +13 -0
  88. package/dist/pi/worldToolProtocol.d.ts.map +1 -0
  89. package/dist/pi/worldToolProtocol.js +49 -0
  90. package/dist/pi/worldTools.d.ts +32 -0
  91. package/dist/pi/worldTools.d.ts.map +1 -0
  92. package/dist/pi/worldTools.js +373 -0
  93. package/dist/pi/worldTrajectory.d.ts +52 -0
  94. package/dist/pi/worldTrajectory.d.ts.map +1 -0
  95. package/dist/pi/worldTrajectory.js +131 -0
  96. package/package.json +10 -13
  97. package/dist/examples/mixed-engine-org.d.ts +0 -2
  98. package/dist/examples/mixed-engine-org.d.ts.map +0 -1
  99. package/dist/examples/mixed-engine-org.js +0 -351
  100. package/dist/examples/mixedEngineCli.d.ts +0 -15
  101. package/dist/examples/mixedEngineCli.d.ts.map +0 -1
  102. package/dist/examples/mixedEngineCli.js +0 -174
@@ -1,351 +0,0 @@
1
- import { mkdir, rm, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { createMemoryRuntime } from "@noopolis/mneme";
5
- import { JsonlMemoryStore } from "@noopolis/mneme";
6
- import { OrgObserver } from "../observability/index.js";
7
- import { runEngineDetailed } from "./mixedEngineCli.js";
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
- const daimonRoot = path.resolve(__dirname, "../..");
10
- const runtimeRoot = path.join(daimonRoot, ".runtime", "mixed-engine-org");
11
- const observer = new OrgObserver({
12
- orgId: "mixed-engine-org",
13
- runId: `run-${Date.now().toString(36)}`
14
- });
15
- const maxMemoryChars = 1800;
16
- const maxTranscriptLines = 3;
17
- class MixedEngineAgent {
18
- config;
19
- runtimeHomePath;
20
- workspacePath;
21
- memory;
22
- constructor(config) {
23
- this.config = config;
24
- this.workspacePath = path.join(runtimeRoot, "agents", config.id, "workspace");
25
- this.runtimeHomePath = path.join(runtimeRoot, "agents", config.id, "runtime");
26
- this.memory = createMemoryRuntime({
27
- agentId: config.id,
28
- runtimeHomePath: this.runtimeHomePath,
29
- source: `daimon/mixed-engine/${config.engine}`,
30
- tokenBudget: 2200
31
- });
32
- }
33
- async prepare() {
34
- await mkdir(this.workspacePath, { recursive: true });
35
- await mkdir(this.runtimeHomePath, { recursive: true });
36
- await writeFile(path.join(this.workspacePath, "AGENTS.md"), [
37
- `# ${this.config.name}`,
38
- "",
39
- `Engine: ${this.config.engine}`,
40
- "This workspace belongs to the mixed-engine Daimon org E2E.",
41
- "The test uses real CLI engines and Daimon's persisted memory."
42
- ].join("\n"));
43
- }
44
- async wake(event) {
45
- const startedAt = Date.now();
46
- const prepareStartedAt = Date.now();
47
- const prepared = await this.memory.prepareTurn({
48
- eventId: event.id,
49
- kind: event.kind,
50
- text: event.text,
51
- from: event.from,
52
- context: event.context ?? {}
53
- });
54
- const memoryPrepareMs = Date.now() - prepareStartedAt;
55
- const rawMemoryText = prepared.packet.sections.length === 0
56
- ? "(no recalled memories)"
57
- : prepared.packet.sections
58
- .map((section) => `- ${section.heading}: ${section.text}`)
59
- .join("\n");
60
- const memoryText = rawMemoryText.length <= maxMemoryChars
61
- ? rawMemoryText
62
- : `${rawMemoryText.slice(0, maxMemoryChars).trim()}\n[truncated memory]`;
63
- const prompt = [
64
- `${this.config.name} (${this.config.id}) running on ${this.config.engine}.`,
65
- "Use recalled Daimon memory as authoritative context.",
66
- "Answer only the requested final line.",
67
- "Memory:",
68
- memoryText,
69
- "",
70
- "Task:",
71
- event.text
72
- ].join("\n");
73
- const engineResult = await runEngineDetailed(this.config.engine, prompt, {
74
- runtimeHomePath: this.runtimeHomePath,
75
- workspacePath: this.workspacePath
76
- });
77
- const recordStartedAt = Date.now();
78
- await this.memory.recordTurn({
79
- principal: prepared.principal,
80
- prompt: prepared.packet,
81
- request: {
82
- eventId: event.id,
83
- kind: event.kind,
84
- text: event.text,
85
- from: event.from,
86
- context: event.context ?? {}
87
- },
88
- recall: prepared.recall,
89
- result: "completed",
90
- outputText: engineResult.text
91
- });
92
- const memoryRecordMs = Date.now() - recordStartedAt;
93
- observer.recordTurn({
94
- agent: this.config.id,
95
- engine: this.config.engine,
96
- event: event.id,
97
- eventText: event.text,
98
- totalMs: Date.now() - startedAt,
99
- memoryPrepareMs,
100
- engineMs: engineResult.durationMs,
101
- memoryRecordMs,
102
- promptChars: engineResult.promptChars,
103
- outputChars: engineResult.outputChars,
104
- outputText: engineResult.text,
105
- recall: prepared.recall
106
- });
107
- return {
108
- agentId: this.config.id,
109
- durationMs: Date.now() - startedAt,
110
- text: engineResult.text
111
- };
112
- }
113
- }
114
- const agents = [
115
- new MixedEngineAgent({
116
- id: "navigator",
117
- name: "Navigator",
118
- engine: "codex",
119
- signalPrefix: "COD"
120
- }),
121
- new MixedEngineAgent({
122
- id: "cartographer",
123
- name: "Cartographer",
124
- engine: "grok",
125
- signalPrefix: "GRK"
126
- }),
127
- new MixedEngineAgent({
128
- id: "sentinel",
129
- name: "Sentinel",
130
- engine: "agy",
131
- signalPrefix: "AGY"
132
- })
133
- ];
134
- const roomContext = {
135
- networkId: "mixed-engine-lab",
136
- roomId: "workbench",
137
- teamId: "mixed-engine-org",
138
- participants: agents.map((agent) => agent.config.id)
139
- };
140
- const assertIncludes = (label, actual, expected, event) => {
141
- const passed = actual.includes(expected);
142
- if (event) {
143
- observer.recordAssertion({
144
- detail: `${label} should include ${expected}`,
145
- event,
146
- kind: "recall",
147
- passed
148
- });
149
- }
150
- if (!passed) {
151
- throw new Error(`${label} did not include ${expected}.\nActual:\n${actual}`);
152
- }
153
- };
154
- const assertNoSignalLeak = (event, signals) => {
155
- let passed = true;
156
- for (const signal of signals) {
157
- if (event.text.includes(signal)) {
158
- passed = false;
159
- observer.recordAssertion({
160
- detail: `wake text must not contain ${signal}`,
161
- event: event.id,
162
- kind: "no-leak",
163
- passed
164
- });
165
- throw new Error(`wake ${event.id} leaked ${signal} in the current prompt`);
166
- }
167
- }
168
- observer.recordAssertion({
169
- detail: `wake text does not contain ${signals.length} known signal(s)`,
170
- event: event.id,
171
- kind: "no-leak",
172
- passed
173
- });
174
- };
175
- const transcriptTail = (transcript) => {
176
- if (transcript.length === 0) {
177
- return "(empty)";
178
- }
179
- const tail = transcript.slice(-maxTranscriptLines);
180
- const prefix = transcript.length > tail.length
181
- ? `(${transcript.length - tail.length} earlier room line(s) omitted)\n`
182
- : "";
183
- return `${prefix}${tail.join("\n")}`;
184
- };
185
- const extractSignal = (agent, text) => {
186
- const match = text.match(/SIGNAL\s*[:=]\s*`?([A-Z0-9_-]{6,80})`?/i);
187
- if (!match) {
188
- throw new Error(`Could not extract signal from ${agent.config.id} output:\n${text}`);
189
- }
190
- const signal = match[1].toUpperCase();
191
- if (!signal.startsWith(`${agent.config.signalPrefix}-`)) {
192
- throw new Error(`${agent.config.id} signal ${signal} does not start with ${agent.config.signalPrefix}-`);
193
- }
194
- return signal;
195
- };
196
- const seedSignals = async () => {
197
- console.log("\n== Live seed phase ==");
198
- const signals = new Map();
199
- const results = await Promise.all(agents.map(async (agent) => {
200
- const result = await agent.wake({
201
- id: `seed-${agent.config.id}`,
202
- kind: "manual",
203
- text: [
204
- "Invent a private signal token for yourself.",
205
- `The token must start with ${agent.config.signalPrefix}- and use only uppercase letters, numbers, and hyphens.`,
206
- "Do not use spaces inside the token.",
207
- "Do not copy examples. Do not mention any other agent.",
208
- "Reply in one line only: SIGNAL=<token> NOTE=<six words or fewer>"
209
- ].join("\n")
210
- });
211
- const signal = extractSignal(agent, result.text);
212
- return { agent, result, signal };
213
- }));
214
- for (const { agent, result, signal } of results) {
215
- signals.set(agent.config.id, signal);
216
- observer.recordSignal({
217
- agent: agent.config.id,
218
- engine: agent.config.engine,
219
- signal
220
- });
221
- console.log(`${agent.config.id} (${agent.config.engine}) -> ${result.text}`);
222
- }
223
- return signals;
224
- };
225
- const runRoom = async (signals) => {
226
- console.log("\n== Mixed-engine room ==");
227
- const transcript = [];
228
- const navigatorSignal = signals.get("navigator");
229
- const cartographerSignal = signals.get("cartographer");
230
- const sentinelSignal = signals.get("sentinel");
231
- const navigatorEvent = {
232
- id: "room-navigator-1",
233
- kind: "manual",
234
- context: roomContext,
235
- text: [
236
- "Room transcript so far:",
237
- transcriptTail(transcript),
238
- "Recall your own private SIGNAL from Daimon memory.",
239
- "Reply in one line: @cartographer navigator=<your signal> asks cartographer to answer."
240
- ].join("\n")
241
- };
242
- assertNoSignalLeak(navigatorEvent, [...signals.values()]);
243
- const navigator = await agents[0].wake(navigatorEvent);
244
- assertIncludes("navigator reply", navigator.text, navigatorSignal, navigatorEvent.id);
245
- observer.recordConsultation({
246
- event: navigatorEvent.id,
247
- from: "navigator",
248
- outputText: navigator.text,
249
- to: "cartographer"
250
- });
251
- transcript.push(`navigator: ${navigator.text}`);
252
- console.log(transcript.at(-1));
253
- const cartographerEvent = {
254
- id: "room-cartographer-1",
255
- kind: "manual",
256
- context: roomContext,
257
- text: [
258
- "Room transcript so far:",
259
- transcriptTail(transcript),
260
- "Recall your own private SIGNAL from Daimon memory.",
261
- "Reply in one line: @sentinel cartographer=<your signal> observed navigator=<navigator signal>."
262
- ].join("\n")
263
- };
264
- assertNoSignalLeak(cartographerEvent, [cartographerSignal, sentinelSignal]);
265
- const cartographer = await agents[1].wake(cartographerEvent);
266
- assertIncludes("cartographer reply", cartographer.text, navigatorSignal, cartographerEvent.id);
267
- assertIncludes("cartographer reply", cartographer.text, cartographerSignal, cartographerEvent.id);
268
- observer.recordConsultation({
269
- event: cartographerEvent.id,
270
- from: "cartographer",
271
- outputText: cartographer.text,
272
- to: "sentinel"
273
- });
274
- transcript.push(`cartographer: ${cartographer.text}`);
275
- console.log(transcript.at(-1));
276
- const sentinelEvent = {
277
- id: "room-sentinel-1",
278
- kind: "manual",
279
- context: roomContext,
280
- text: [
281
- "Room transcript so far:",
282
- transcriptTail(transcript),
283
- "Recall your own private SIGNAL from Daimon memory.",
284
- "Reply in one line: sentinel=<your signal> observed navigator=<navigator signal> cartographer=<cartographer signal>."
285
- ].join("\n")
286
- };
287
- assertNoSignalLeak(sentinelEvent, [sentinelSignal]);
288
- const sentinel = await agents[2].wake(sentinelEvent);
289
- assertIncludes("sentinel reply", sentinel.text, navigatorSignal, sentinelEvent.id);
290
- assertIncludes("sentinel reply", sentinel.text, cartographerSignal, sentinelEvent.id);
291
- assertIncludes("sentinel reply", sentinel.text, sentinelSignal, sentinelEvent.id);
292
- transcript.push(`sentinel: ${sentinel.text}`);
293
- console.log(transcript.at(-1));
294
- return transcript;
295
- };
296
- const runFinalRecall = async (signals) => {
297
- console.log("\n== Fresh CLI final recall ==");
298
- const event = {
299
- id: "room-sentinel-2",
300
- kind: "manual",
301
- context: roomContext,
302
- text: [
303
- "There is no room transcript in this wake.",
304
- "Use only Daimon memory recalled into this fresh CLI turn.",
305
- "Report all three remembered signals in one line:",
306
- "final navigator=<token> cartographer=<token> sentinel=<token>"
307
- ].join("\n")
308
- };
309
- assertNoSignalLeak(event, [...signals.values()]);
310
- const result = await agents[2].wake(event);
311
- for (const signal of signals.values()) {
312
- assertIncludes("final recall", result.text, signal, event.id);
313
- }
314
- console.log(`sentinel (${agents[2].config.engine}) -> ${result.text}`);
315
- };
316
- const printMemoryCounts = async () => {
317
- console.log("\nMemory event counts:");
318
- for (const agent of agents) {
319
- const events = await new JsonlMemoryStore(agent.runtimeHomePath).read();
320
- const counts = events.reduce((memo, event) => {
321
- memo[event.type] = (memo[event.type] ?? 0) + 1;
322
- return memo;
323
- }, {});
324
- console.log(`${agent.config.id}: ${JSON.stringify(counts)}`);
325
- }
326
- };
327
- const printBench = async () => {
328
- console.log("\nBench rows:");
329
- console.table(observer.benchRows());
330
- const summary = observer.summary();
331
- console.log("Bench summary:");
332
- for (const [engine, row] of Object.entries(summary)) {
333
- console.log(`${engine}: avg_engine_ms=${Math.round(row.engineMs / row.count)} avg_total_ms=${Math.round(row.totalMs / row.count)} avg_prompt_chars=${Math.round(row.promptChars / row.count)}`);
334
- }
335
- await observer.write(runtimeRoot);
336
- };
337
- const run = async () => {
338
- await rm(runtimeRoot, { recursive: true, force: true });
339
- await Promise.all(agents.map((agent) => agent.prepare()));
340
- await writeFile(path.join(runtimeRoot, "org.json"), JSON.stringify(agents.map((agent) => agent.config), null, 2));
341
- const signals = await seedSignals();
342
- await runRoom(signals);
343
- await runFinalRecall(signals);
344
- await printMemoryCounts();
345
- await printBench();
346
- console.log("\ne2e:mixed-engine-org ok");
347
- };
348
- run().catch((error) => {
349
- console.error(error instanceof Error ? error.stack ?? error.message : String(error));
350
- process.exitCode = 1;
351
- });
@@ -1,15 +0,0 @@
1
- export type EngineKind = "agy" | "codex" | "grok";
2
- export interface EngineRunResult {
3
- durationMs: number;
4
- outputChars: number;
5
- promptChars: number;
6
- text: string;
7
- }
8
- interface EnginePaths {
9
- runtimeHomePath: string;
10
- workspacePath: string;
11
- }
12
- export declare const runEngine: (engine: EngineKind, prompt: string, paths: EnginePaths) => Promise<string>;
13
- export declare const runEngineDetailed: (engine: EngineKind, prompt: string, paths: EnginePaths) => Promise<EngineRunResult>;
14
- export {};
15
- //# sourceMappingURL=mixedEngineCli.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mixedEngineCli.d.ts","sourceRoot":"","sources":["../../src/examples/mixedEngineCli.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,WAAW;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACvB;AAuKD,eAAO,MAAM,SAAS,GACpB,QAAQ,UAAU,EAClB,QAAQ,MAAM,EACd,OAAO,WAAW,KACjB,OAAO,CAAC,MAAM,CAGhB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,UAAU,EAClB,QAAQ,MAAM,EACd,OAAO,WAAW,KACjB,OAAO,CAAC,eAAe,CAgBzB,CAAC"}
@@ -1,174 +0,0 @@
1
- import { execFile, spawn } from "node:child_process";
2
- import { closeSync, openSync } from "node:fs";
3
- import { readFile, stat, unlink } from "node:fs/promises";
4
- import path from "node:path";
5
- import { promisify } from "node:util";
6
- const execFileAsync = promisify(execFile);
7
- const maxCapturedOutputBytes = 1024 * 256;
8
- const outputOptions = {
9
- maxBuffer: 1024 * 1024 * 8,
10
- timeout: 180_000
11
- };
12
- const stripAnsi = (value) => value.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, "").trim();
13
- const pushCapped = (chunks, chunk, state) => {
14
- if (state.bytes >= maxCapturedOutputBytes) {
15
- return;
16
- }
17
- const remaining = maxCapturedOutputBytes - state.bytes;
18
- const next = chunk.length > remaining ? chunk.subarray(0, remaining) : chunk;
19
- chunks.push(next);
20
- state.bytes += next.length;
21
- };
22
- const readBounded = async (filePath) => {
23
- const stats = await stat(filePath);
24
- if (stats.size <= maxCapturedOutputBytes) {
25
- return readFile(filePath, "utf8");
26
- }
27
- const content = await readFile(filePath);
28
- const head = content.subarray(0, maxCapturedOutputBytes).toString("utf8");
29
- return `${head}\n[truncated ${stats.size - maxCapturedOutputBytes} bytes]`;
30
- };
31
- const spawnWithInput = (command, args, input, cwd) => new Promise((resolve, reject) => {
32
- const child = spawn(command, args, { cwd, stdio: ["pipe", "pipe", "pipe"] });
33
- const timer = setTimeout(() => {
34
- child.kill("SIGTERM");
35
- reject(new Error(`${command} timed out after ${outputOptions.timeout}ms`));
36
- }, outputOptions.timeout);
37
- const stdout = [];
38
- const stderr = [];
39
- const stdoutState = { bytes: 0 };
40
- const stderrState = { bytes: 0 };
41
- child.stdout.on("data", (chunk) => pushCapped(stdout, chunk, stdoutState));
42
- child.stderr.on("data", (chunk) => pushCapped(stderr, chunk, stderrState));
43
- child.on("error", (error) => {
44
- clearTimeout(timer);
45
- reject(error);
46
- });
47
- child.on("close", (code, signal) => {
48
- clearTimeout(timer);
49
- const output = {
50
- stdout: Buffer.concat(stdout).toString("utf8"),
51
- stderr: Buffer.concat(stderr).toString("utf8")
52
- };
53
- if (code === 0) {
54
- resolve(output);
55
- return;
56
- }
57
- reject(new Error(`${command} exited ${code ?? signal}: ${output.stderr || output.stdout}`));
58
- });
59
- child.stdin.end(input);
60
- });
61
- const spawnToFiles = (command, args, input) => new Promise((resolve, reject) => {
62
- const stdoutFd = openSync(input.stdoutPath, "w");
63
- const stderrFd = openSync(input.stderrPath, "w");
64
- const closeFiles = () => {
65
- closeSync(stdoutFd);
66
- closeSync(stderrFd);
67
- };
68
- const child = spawn(command, args, { cwd: input.cwd, stdio: ["ignore", stdoutFd, stderrFd] });
69
- const timer = setTimeout(() => {
70
- child.kill("SIGTERM");
71
- closeFiles();
72
- reject(new Error(`${command} timed out after ${outputOptions.timeout}ms; stderr=${input.stderrPath}`));
73
- }, outputOptions.timeout);
74
- child.on("error", (error) => {
75
- clearTimeout(timer);
76
- closeFiles();
77
- reject(error);
78
- });
79
- child.on("close", (code, signal) => {
80
- clearTimeout(timer);
81
- closeFiles();
82
- if (code === 0) {
83
- resolve();
84
- return;
85
- }
86
- reject(new Error(`${command} exited ${code ?? signal}; stderr=${input.stderrPath}; stdout=${input.stdoutPath}`));
87
- });
88
- });
89
- const runCodex = async (prompt, paths) => {
90
- const outputPath = `${paths.runtimeHomePath}/codex-${Date.now()}.txt`;
91
- const args = [
92
- "exec",
93
- "--sandbox",
94
- "read-only",
95
- "--ephemeral",
96
- "--skip-git-repo-check",
97
- "--ignore-rules",
98
- "--color",
99
- "never",
100
- "-C",
101
- paths.workspacePath,
102
- "--output-last-message",
103
- outputPath
104
- ];
105
- args.push("-m", process.env.DAIMON_CODEX_MODEL ?? "gpt-5.4-mini");
106
- args.push("-");
107
- const { stdout, stderr } = await spawnWithInput("codex", args, prompt, paths.workspacePath);
108
- try {
109
- return stripAnsi(await readFile(outputPath, "utf8"));
110
- }
111
- catch {
112
- return stripAnsi([stdout, stderr].filter(Boolean).join("\n"));
113
- }
114
- };
115
- const runGrok = async (prompt, paths) => {
116
- const { stdout } = await execFileAsync("grok", [
117
- "--single",
118
- prompt,
119
- "--max-turns",
120
- process.env.DAIMON_GROK_MAX_TURNS ?? "2",
121
- "--no-memory",
122
- "--disable-web-search",
123
- "--cwd",
124
- paths.workspacePath,
125
- "--output-format",
126
- "plain"
127
- ], { ...outputOptions, cwd: paths.workspacePath });
128
- return stripAnsi(stdout);
129
- };
130
- const runAgy = async (prompt, paths) => {
131
- const outputPath = path.resolve(paths.runtimeHomePath, `agy-output-${Date.now()}.txt`);
132
- const errorPath = path.resolve(paths.runtimeHomePath, `agy-error-${Date.now()}.txt`);
133
- await spawnToFiles("agy", [
134
- "--print",
135
- prompt,
136
- "--print-timeout",
137
- process.env.DAIMON_AGY_TIMEOUT ?? "300s",
138
- "--model",
139
- process.env.DAIMON_AGY_MODEL ?? "Gemini 3.5 Flash (Low)",
140
- "--new-project",
141
- "--add-dir",
142
- paths.workspacePath
143
- ], {
144
- cwd: paths.workspacePath,
145
- stderrPath: errorPath,
146
- stdoutPath: outputPath
147
- });
148
- const text = stripAnsi(await readBounded(outputPath));
149
- await Promise.all([unlink(outputPath), unlink(errorPath)].map((promise) => promise.catch(() => undefined)));
150
- return text;
151
- };
152
- export const runEngine = async (engine, prompt, paths) => {
153
- const result = await runEngineDetailed(engine, prompt, paths);
154
- return result.text;
155
- };
156
- export const runEngineDetailed = async (engine, prompt, paths) => {
157
- const startedAt = Date.now();
158
- let text;
159
- if (engine === "codex") {
160
- text = await runCodex(prompt, paths);
161
- }
162
- else if (engine === "grok") {
163
- text = await runGrok(prompt, paths);
164
- }
165
- else {
166
- text = await runAgy(prompt, paths);
167
- }
168
- return {
169
- durationMs: Date.now() - startedAt,
170
- outputChars: text.length,
171
- promptChars: prompt.length,
172
- text
173
- };
174
- };