poe-code 3.0.201 → 3.0.203

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 (49) hide show
  1. package/dist/cli/commands/experiment.js +11 -4
  2. package/dist/cli/commands/experiment.js.map +1 -1
  3. package/dist/cli/commands/ralph.js +12 -5
  4. package/dist/cli/commands/ralph.js.map +1 -1
  5. package/dist/cli/commands/runtime-options.d.ts +10 -0
  6. package/dist/cli/commands/runtime-options.js +23 -0
  7. package/dist/cli/commands/runtime-options.js.map +1 -0
  8. package/dist/cli/commands/spawn.js +9 -3
  9. package/dist/cli/commands/spawn.js.map +1 -1
  10. package/dist/index.js +22975 -20513
  11. package/dist/index.js.map +4 -4
  12. package/dist/providers/claude-code.js +2741 -1706
  13. package/dist/providers/claude-code.js.map +4 -4
  14. package/dist/providers/codex.js +2770 -1735
  15. package/dist/providers/codex.js.map +4 -4
  16. package/dist/providers/goose.js +2640 -1605
  17. package/dist/providers/goose.js.map +4 -4
  18. package/dist/providers/kimi.js +2740 -1705
  19. package/dist/providers/kimi.js.map +4 -4
  20. package/dist/providers/opencode.js +2741 -1706
  21. package/dist/providers/opencode.js.map +4 -4
  22. package/dist/providers/poe-agent.js +19779 -17296
  23. package/dist/providers/poe-agent.js.map +4 -4
  24. package/dist/providers/spawn-options.d.ts +6 -0
  25. package/dist/sdk/experiment.js +5 -0
  26. package/dist/sdk/experiment.js.map +1 -1
  27. package/dist/sdk/ralph.js +5 -0
  28. package/dist/sdk/ralph.js.map +1 -1
  29. package/dist/sdk/spawn.js +17 -1
  30. package/dist/sdk/spawn.js.map +1 -1
  31. package/dist/sdk/types.d.ts +11 -0
  32. package/package.json +1 -1
  33. package/packages/memory/dist/index.js +2642 -444
  34. package/packages/memory/dist/index.js.map +4 -4
  35. package/packages/superintendent/dist/commands/run.d.ts +35 -0
  36. package/packages/superintendent/dist/commands/run.js +49 -1
  37. package/packages/superintendent/dist/commands/superintendent-group.d.ts +30 -0
  38. package/packages/superintendent/dist/runtime/agent-runner.d.ts +30 -0
  39. package/packages/superintendent/dist/runtime/agent-runner.js +119 -0
  40. package/packages/superintendent/dist/runtime/loop.d.ts +6 -1
  41. package/packages/superintendent/dist/runtime/loop.js +3 -11
  42. package/packages/superintendent/dist/runtime/run-builder.d.ts +1 -0
  43. package/packages/superintendent/dist/runtime/run-builder.js +3 -25
  44. package/packages/superintendent/dist/runtime/run-inspector.d.ts +1 -0
  45. package/packages/superintendent/dist/runtime/run-inspector.js +3 -25
  46. package/packages/superintendent/dist/runtime/run-owner-review.d.ts +1 -0
  47. package/packages/superintendent/dist/runtime/run-owner-review.js +3 -25
  48. package/packages/superintendent/dist/runtime/run-superintendent.d.ts +1 -0
  49. package/packages/superintendent/dist/runtime/run-superintendent.js +3 -25
@@ -35,7 +35,405 @@ function assertSafeRelPath(input) {
35
35
  }
36
36
 
37
37
  // packages/memory/src/resolve-root.ts
38
- import path8 from "node:path";
38
+ import path11 from "node:path";
39
+
40
+ // packages/poe-code-config/src/runtime.ts
41
+ import { existsSync } from "node:fs";
42
+ import path2 from "node:path";
43
+ var defaultWorkspaceExclude = [
44
+ ".git",
45
+ "node_modules",
46
+ "dist",
47
+ ".turbo",
48
+ ".next",
49
+ ".poe-code/state.json"
50
+ ];
51
+ var runtimeConfigScope = {
52
+ scope: "runtime",
53
+ schema: {
54
+ type: {
55
+ type: "string",
56
+ default: "host",
57
+ doc: "Runtime backend: host, docker, or e2b"
58
+ },
59
+ build_args: {
60
+ type: "json",
61
+ default: {},
62
+ parse: parseBuildArgs,
63
+ doc: "Build arguments passed to the runtime image build"
64
+ },
65
+ mounts: {
66
+ type: "json",
67
+ default: [],
68
+ parse: parseMounts,
69
+ doc: "Additional runtime mounts"
70
+ },
71
+ runner: {
72
+ type: "json",
73
+ default: createDefaultRunnerScope(),
74
+ parse: parseRunner,
75
+ doc: "Runner process and workspace transfer settings"
76
+ },
77
+ link: {
78
+ type: "string",
79
+ default: "",
80
+ doc: "Informational link for the runtime definition"
81
+ },
82
+ image: {
83
+ type: "string",
84
+ default: "",
85
+ doc: "Prebuilt Docker image"
86
+ },
87
+ dockerfile: {
88
+ type: "string",
89
+ default: "",
90
+ doc: "Path to the Dockerfile used for docker or e2b builds"
91
+ },
92
+ build_context: {
93
+ type: "string",
94
+ default: "",
95
+ doc: "Path to the Docker build context"
96
+ },
97
+ engine: {
98
+ type: "string",
99
+ default: "",
100
+ doc: "Container engine for Docker runtime"
101
+ },
102
+ network: {
103
+ type: "string",
104
+ default: "",
105
+ doc: "Docker network"
106
+ },
107
+ extra_args: {
108
+ type: "json",
109
+ default: void 0,
110
+ parse: parseOptionalStringArray,
111
+ doc: "Extra Docker runtime arguments"
112
+ },
113
+ template_id: {
114
+ type: "string",
115
+ default: "",
116
+ doc: "Prebuilt E2B template id"
117
+ },
118
+ cpu: {
119
+ type: "json",
120
+ default: void 0,
121
+ parse: parseOptionalNumber,
122
+ doc: "E2B CPU count"
123
+ },
124
+ memory_mb: {
125
+ type: "json",
126
+ default: void 0,
127
+ parse: parseOptionalNumber,
128
+ doc: "E2B memory in megabytes"
129
+ },
130
+ timeout_minutes: {
131
+ type: "json",
132
+ default: void 0,
133
+ parse: parseOptionalNumber,
134
+ doc: "E2B timeout in minutes"
135
+ },
136
+ preserve_after_exit_hours: {
137
+ type: "json",
138
+ default: void 0,
139
+ parse: parseOptionalNumber,
140
+ doc: "Hours to keep an E2B sandbox alive after job exit"
141
+ },
142
+ api_key_env: {
143
+ type: "string",
144
+ default: "",
145
+ doc: "Environment variable name containing the E2B API key"
146
+ }
147
+ }
148
+ };
149
+ function parseRunner(raw) {
150
+ if (raw === void 0) {
151
+ return createDefaultRunnerScope();
152
+ }
153
+ const record = asRecord(raw);
154
+ if (record === void 0) {
155
+ throw new Error("runner: expected an object.");
156
+ }
157
+ const uploadMaxFileMb = parseOptionalNumber(record.upload_max_file_mb, "runner.upload_max_file_mb") ?? 100;
158
+ if (uploadMaxFileMb <= 0) {
159
+ throw new Error("runner.upload_max_file_mb: expected a positive finite number.");
160
+ }
161
+ return omitUndefined({
162
+ detach: parseOptionalBoolean(record.detach, "runner.detach") ?? false,
163
+ upload_max_file_mb: uploadMaxFileMb,
164
+ download_conflict: parseDownloadConflict(record.download_conflict),
165
+ workspace: parseRunnerWorkspace(record.workspace)
166
+ });
167
+ }
168
+ function parseRuntime(raw) {
169
+ if (raw === void 0) {
170
+ return {
171
+ type: "host",
172
+ build_args: {},
173
+ mounts: []
174
+ };
175
+ }
176
+ const record = asRecord(raw);
177
+ if (record === void 0) {
178
+ throw new Error("runtime: expected an object.");
179
+ }
180
+ const type = parseRuntimeType(record.type);
181
+ const shared = parseSharedRuntimeFields(record);
182
+ if (type === "docker") {
183
+ return omitUndefined({
184
+ ...shared,
185
+ type,
186
+ image: parseOptionalString(record.image),
187
+ dockerfile: parseOptionalString(record.dockerfile),
188
+ build_context: parseOptionalString(record.build_context),
189
+ engine: parseEngine(record.engine),
190
+ network: parseOptionalString(record.network),
191
+ extra_args: parseOptionalStringArray(record.extra_args)
192
+ });
193
+ }
194
+ if (type === "e2b") {
195
+ const preserveAfterExitHours = parseOptionalNumber(record.preserve_after_exit_hours) ?? 24;
196
+ if (preserveAfterExitHours < 0 || preserveAfterExitHours > 168) {
197
+ throw new Error("preserve_after_exit_hours: expected a number from 0 to 168.");
198
+ }
199
+ return omitUndefined({
200
+ ...shared,
201
+ type,
202
+ template_id: parseOptionalString(record.template_id),
203
+ dockerfile: parseOptionalString(record.dockerfile),
204
+ build_context: parseOptionalString(record.build_context),
205
+ cpu: parseOptionalNumber(record.cpu),
206
+ memory_mb: parseOptionalNumber(record.memory_mb),
207
+ timeout_minutes: parseOptionalNumber(record.timeout_minutes),
208
+ preserve_after_exit_hours: preserveAfterExitHours,
209
+ api_key_env: parseOptionalString(record.api_key_env) ?? "E2B_API_KEY"
210
+ });
211
+ }
212
+ return {
213
+ ...shared,
214
+ type
215
+ };
216
+ }
217
+ function resolveRuntime({
218
+ cwd,
219
+ config
220
+ }) {
221
+ const runtime = config.runtime;
222
+ return runtimeResolvers[runtime.type]({ cwd, runtime });
223
+ }
224
+ var runtimeResolvers = {
225
+ host({ runtime }) {
226
+ return {
227
+ runtime,
228
+ runner: "host",
229
+ dockerfilePath: null,
230
+ buildContext: null
231
+ };
232
+ },
233
+ docker({ cwd, runtime }) {
234
+ const dockerRuntime = runtime;
235
+ const { dockerfilePath, buildContext } = resolveRuntimeBuildPaths(cwd, dockerRuntime);
236
+ if (dockerRuntime.image !== void 0) {
237
+ return {
238
+ runtime: dockerRuntime,
239
+ runner: "docker",
240
+ dockerfilePath: null,
241
+ buildContext: null
242
+ };
243
+ }
244
+ if (!existsSync(dockerfilePath)) {
245
+ throw new Error(`Docker runtime requires image or a Dockerfile at ${dockerfilePath}.`);
246
+ }
247
+ return {
248
+ runtime: dockerRuntime,
249
+ runner: "docker",
250
+ dockerfilePath,
251
+ buildContext
252
+ };
253
+ },
254
+ e2b({ cwd, runtime }) {
255
+ const e2bRuntime = runtime;
256
+ const { dockerfilePath, buildContext } = resolveRuntimeBuildPaths(cwd, e2bRuntime);
257
+ if (e2bRuntime.template_id !== void 0) {
258
+ return {
259
+ runtime: e2bRuntime,
260
+ runner: "e2b",
261
+ dockerfilePath: null,
262
+ buildContext: null
263
+ };
264
+ }
265
+ if (!existsSync(dockerfilePath)) {
266
+ throw new Error(`E2B runtime requires template_id or a Dockerfile at ${dockerfilePath}.`);
267
+ }
268
+ return {
269
+ runtime: e2bRuntime,
270
+ runner: "e2b",
271
+ dockerfilePath,
272
+ buildContext
273
+ };
274
+ }
275
+ };
276
+ function resolveRuntimeBuildPaths(cwd, runtime) {
277
+ return {
278
+ dockerfilePath: path2.resolve(cwd, runtime.dockerfile ?? path2.join(".poe-code", "Dockerfile")),
279
+ buildContext: path2.resolve(cwd, runtime.build_context ?? ".")
280
+ };
281
+ }
282
+ function parseSharedRuntimeFields(record) {
283
+ return omitUndefined({
284
+ build_args: parseBuildArgs(record.build_args),
285
+ mounts: parseMounts(record.mounts),
286
+ link: parseOptionalString(record.link)
287
+ });
288
+ }
289
+ function parseRuntimeType(value) {
290
+ if (value === void 0) {
291
+ return "host";
292
+ }
293
+ if (value === "host" || value === "docker" || value === "e2b") {
294
+ return value;
295
+ }
296
+ throw new Error('type: expected "host", "docker", or "e2b".');
297
+ }
298
+ function createDefaultRunnerScope() {
299
+ return {
300
+ detach: false,
301
+ upload_max_file_mb: 100,
302
+ download_conflict: "refuse",
303
+ workspace: {
304
+ exclude: [...defaultWorkspaceExclude]
305
+ }
306
+ };
307
+ }
308
+ function parseRunnerWorkspace(value) {
309
+ if (value === void 0) {
310
+ return {
311
+ exclude: [...defaultWorkspaceExclude]
312
+ };
313
+ }
314
+ const record = asRecord(value);
315
+ if (record === void 0) {
316
+ throw new Error("runner.workspace: expected an object.");
317
+ }
318
+ return {
319
+ exclude: parseOptionalStringArray(record.exclude, "runner.workspace.exclude") ?? [
320
+ ...defaultWorkspaceExclude
321
+ ]
322
+ };
323
+ }
324
+ function parseDownloadConflict(value) {
325
+ if (value === void 0) {
326
+ return "refuse";
327
+ }
328
+ if (value === "refuse" || value === "overwrite") {
329
+ return value;
330
+ }
331
+ throw new Error('runner.download_conflict: expected "refuse" or "overwrite".');
332
+ }
333
+ function parseBuildArgs(value) {
334
+ if (value === void 0) {
335
+ return {};
336
+ }
337
+ const record = asRecord(value);
338
+ if (record === void 0) {
339
+ throw new Error("build_args: expected an object.");
340
+ }
341
+ const parsed = {};
342
+ for (const [key, entry] of Object.entries(record)) {
343
+ if (typeof entry !== "string") {
344
+ throw new Error(`build_args.${key}: expected a string.`);
345
+ }
346
+ parsed[key] = entry;
347
+ }
348
+ return parsed;
349
+ }
350
+ function parseMounts(value) {
351
+ if (value === void 0) {
352
+ return [];
353
+ }
354
+ if (!Array.isArray(value)) {
355
+ throw new Error("mounts: expected an array.");
356
+ }
357
+ return value.map((entry, index) => {
358
+ const record = asRecord(entry);
359
+ if (record === void 0) {
360
+ throw new Error(`mounts[${index}]: expected an object.`);
361
+ }
362
+ const source = record.source;
363
+ const target = record.target;
364
+ if (typeof source !== "string") {
365
+ throw new Error(`mounts[${index}].source: expected a string.`);
366
+ }
367
+ if (typeof target !== "string") {
368
+ throw new Error(`mounts[${index}].target: expected a string.`);
369
+ }
370
+ return omitUndefined({
371
+ source,
372
+ target,
373
+ readonly: parseOptionalBoolean(record.readonly, `mounts[${index}].readonly`)
374
+ });
375
+ });
376
+ }
377
+ function parseOptionalString(value) {
378
+ if (value === void 0) {
379
+ return void 0;
380
+ }
381
+ if (typeof value !== "string") {
382
+ throw new Error("expected a string.");
383
+ }
384
+ if (value.length === 0) {
385
+ return void 0;
386
+ }
387
+ return value;
388
+ }
389
+ function parseOptionalStringArray(value, key = "") {
390
+ if (value === void 0) {
391
+ return void 0;
392
+ }
393
+ if (!Array.isArray(value)) {
394
+ throw new Error(`${key ? `${key}: ` : ""}expected an array.`);
395
+ }
396
+ return value.map((entry, index) => {
397
+ if (typeof entry !== "string") {
398
+ throw new Error(`${key}[${index}]: expected a string.`);
399
+ }
400
+ return entry;
401
+ });
402
+ }
403
+ function parseEngine(value) {
404
+ const engine = parseOptionalString(value);
405
+ if (engine === void 0 || engine === "docker" || engine === "podman") {
406
+ return engine;
407
+ }
408
+ throw new Error('engine: expected "docker" or "podman".');
409
+ }
410
+ function parseOptionalNumber(value, key = "") {
411
+ if (value === void 0) {
412
+ return void 0;
413
+ }
414
+ if (typeof value !== "number" || !Number.isFinite(value)) {
415
+ throw new Error(`${key ? `${key}: ` : ""}expected a finite number.`);
416
+ }
417
+ return value;
418
+ }
419
+ function parseOptionalBoolean(value, key) {
420
+ if (value === void 0) {
421
+ return void 0;
422
+ }
423
+ if (typeof value !== "boolean") {
424
+ throw new Error(`${key}: expected a boolean.`);
425
+ }
426
+ return value;
427
+ }
428
+ function asRecord(value) {
429
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
430
+ return void 0;
431
+ }
432
+ return value;
433
+ }
434
+ function omitUndefined(value) {
435
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
436
+ }
39
437
 
40
438
  // packages/poe-code-config/src/schema.ts
41
439
  function defineScope(scope, schema) {
@@ -56,15 +454,15 @@ var planConfigScope = defineScope("plan", {
56
454
  });
57
455
 
58
456
  // packages/poe-code-config/src/store.ts
59
- import path6 from "node:path";
457
+ import path7 from "node:path";
60
458
 
61
459
  // packages/config-extends/src/discover.ts
62
- import path2 from "node:path";
460
+ import path3 from "node:path";
63
461
  async function findBase(name, bases, fs14) {
64
462
  const checkedPaths = [];
65
463
  for (const basePath of bases) {
66
464
  for (const extension of [".md", ".yaml", ".yml", ".json"]) {
67
- const filePath = path2.join(basePath, `${name}${extension}`);
465
+ const filePath = path3.join(basePath, `${name}${extension}`);
68
466
  checkedPaths.push(filePath);
69
467
  try {
70
468
  return {
@@ -88,7 +486,7 @@ function hasCode(error2, code) {
88
486
  }
89
487
 
90
488
  // packages/config-extends/src/parse.ts
91
- import path3 from "node:path";
489
+ import path4 from "node:path";
92
490
  import matter from "gray-matter";
93
491
  import { parse as parseYaml } from "yaml";
94
492
  function parseDocument(content, filePath) {
@@ -106,7 +504,7 @@ function parseDocument(content, filePath) {
106
504
  };
107
505
  }
108
506
  function detectFormat(content, filePath) {
109
- const extension = path3.extname(filePath).toLowerCase();
507
+ const extension = path4.extname(filePath).toLowerCase();
110
508
  if (extension === ".md") {
111
509
  return "markdown";
112
510
  }
@@ -148,11 +546,11 @@ function stripBom(content) {
148
546
  function mergeLayers(layers) {
149
547
  return mergeObjectLayers(layers, []);
150
548
  }
151
- function mergeObjectLayers(layers, path30) {
549
+ function mergeObjectLayers(layers, path38) {
152
550
  const data = {};
153
551
  const sources = {};
154
552
  for (const key of collectKeys(layers)) {
155
- const resolved = resolveKey(layers, key, path30);
553
+ const resolved = resolveKey(layers, key, path38);
156
554
  if (resolved === void 0) {
157
555
  continue;
158
556
  }
@@ -170,7 +568,7 @@ function collectKeys(layers) {
170
568
  }
171
569
  return [...keys];
172
570
  }
173
- function resolveKey(layers, key, path30) {
571
+ function resolveKey(layers, key, path38) {
174
572
  let winningSource;
175
573
  let winningValue;
176
574
  const objectLayers = [];
@@ -200,9 +598,9 @@ function resolveKey(layers, key, path30) {
200
598
  if (winningSource === void 0) {
201
599
  return void 0;
202
600
  }
203
- const fullPath = buildPath(path30, key);
601
+ const fullPath = buildPath(path38, key);
204
602
  if (isPlainObject(winningValue)) {
205
- const merged = mergeObjectLayers(objectLayers, [...path30, key]);
603
+ const merged = mergeObjectLayers(objectLayers, [...path38, key]);
206
604
  return {
207
605
  value: merged.data,
208
606
  sources: {
@@ -227,8 +625,8 @@ function isWinningCandidate(key, value) {
227
625
  }
228
626
  return true;
229
627
  }
230
- function buildPath(path30, key) {
231
- return [...path30, key].join(".");
628
+ function buildPath(path38, key) {
629
+ return [...path38, key].join(".");
232
630
  }
233
631
  function isPlainObject(value) {
234
632
  if (value === null || Array.isArray(value) || typeof value !== "object") {
@@ -252,7 +650,7 @@ function cloneValue(value) {
252
650
  }
253
651
 
254
652
  // packages/config-extends/src/resolve.ts
255
- import path4 from "node:path";
653
+ import path5 from "node:path";
256
654
  var MAX_EXTENDS_DEPTH = 5;
257
655
  var YIELD_TOKEN = "{{yield}}";
258
656
  async function resolve(chain, options) {
@@ -350,7 +748,7 @@ Visited files:
350
748
  );
351
749
  }
352
750
  const matchedBaseIndex = baseLayers.findIndex(
353
- (layer) => layer.path === path4.dirname(discoveredBase.filePath)
751
+ (layer) => layer.path === path5.dirname(discoveredBase.filePath)
354
752
  );
355
753
  if (matchedBaseIndex === -1) {
356
754
  throw new Error(`Resolved base is outside configured base paths: ${discoveredBase.filePath}`);
@@ -464,7 +862,7 @@ function stripResolvedBasePrompts(layers, consumedBaseIndexes) {
464
862
  });
465
863
  }
466
864
  function getBaseName(filePath) {
467
- return path4.basename(filePath, path4.extname(filePath));
865
+ return path5.basename(filePath, path5.extname(filePath));
468
866
  }
469
867
  function shouldResolveBase(parsedDocument, autoExtend) {
470
868
  return parsedDocument.extends || autoExtend === true && !parsedDocument.hasExtendsField;
@@ -862,20 +1260,20 @@ function getConfigFormat(pathOrFormat) {
862
1260
  }
863
1261
  return formatRegistry[formatName];
864
1262
  }
865
- function detectFormat2(path30) {
866
- const ext = getExtension(path30);
1263
+ function detectFormat2(path38) {
1264
+ const ext = getExtension(path38);
867
1265
  return extensionMap[ext];
868
1266
  }
869
- function getExtension(path30) {
870
- const lastDot = path30.lastIndexOf(".");
1267
+ function getExtension(path38) {
1268
+ const lastDot = path38.lastIndexOf(".");
871
1269
  if (lastDot === -1) {
872
1270
  return "";
873
1271
  }
874
- return path30.slice(lastDot).toLowerCase();
1272
+ return path38.slice(lastDot).toLowerCase();
875
1273
  }
876
1274
 
877
1275
  // packages/config-mutations/src/execution/path-utils.ts
878
- import path5 from "node:path";
1276
+ import path6 from "node:path";
879
1277
  function expandHome(targetPath, homeDir) {
880
1278
  if (!targetPath?.startsWith("~")) {
881
1279
  return targetPath;
@@ -892,7 +1290,7 @@ function expandHome(targetPath, homeDir) {
892
1290
  remainder = remainder.slice(1);
893
1291
  }
894
1292
  }
895
- return remainder.length === 0 ? homeDir : path5.join(homeDir, remainder);
1293
+ return remainder.length === 0 ? homeDir : path6.join(homeDir, remainder);
896
1294
  }
897
1295
  function validateHomePath(targetPath) {
898
1296
  if (typeof targetPath !== "string" || targetPath.length === 0) {
@@ -910,12 +1308,12 @@ function resolvePath(rawPath, homeDir, pathMapper) {
910
1308
  if (!pathMapper) {
911
1309
  return expanded;
912
1310
  }
913
- const rawDirectory = path5.dirname(expanded);
1311
+ const rawDirectory = path6.dirname(expanded);
914
1312
  const mappedDirectory = pathMapper.mapTargetDirectory({
915
1313
  targetDirectory: rawDirectory
916
1314
  });
917
- const filename = path5.basename(expanded);
918
- return filename.length === 0 ? mappedDirectory : path5.join(mappedDirectory, filename);
1315
+ const filename = path6.basename(expanded);
1316
+ return filename.length === 0 ? mappedDirectory : path6.join(mappedDirectory, filename);
919
1317
  }
920
1318
 
921
1319
  // packages/config-mutations/src/fs-utils.ts
@@ -1173,8 +1571,8 @@ async function applyChmod(mutation, context, options) {
1173
1571
  };
1174
1572
  }
1175
1573
  try {
1176
- const stat6 = await context.fs.stat(targetPath);
1177
- const currentMode = typeof stat6.mode === "number" ? stat6.mode & 511 : null;
1574
+ const stat7 = await context.fs.stat(targetPath);
1575
+ const currentMode = typeof stat7.mode === "number" ? stat7.mode & 511 : null;
1178
1576
  if (currentMode === mutation.mode) {
1179
1577
  return {
1180
1578
  outcome: { changed: false, effect: "none", detail: "noop" },
@@ -1537,7 +1935,7 @@ async function readMergedDocument(fs14, globalPath, projectPath) {
1537
1935
  },
1538
1936
  {
1539
1937
  source: "base",
1540
- path: path6.dirname(globalPath)
1938
+ path: path7.dirname(globalPath)
1541
1939
  }
1542
1940
  ],
1543
1941
  {
@@ -1614,22 +2012,164 @@ function createResolvedConfigFs(fs14, globalPath, globalContent) {
1614
2012
  };
1615
2013
  }
1616
2014
  async function recoverInvalidDocument(fs14, filePath, content) {
1617
- await fs14.mkdir(path6.dirname(filePath), { recursive: true });
2015
+ await fs14.mkdir(path7.dirname(filePath), { recursive: true });
1618
2016
  const backupPath = createInvalidBackupPath(filePath);
1619
2017
  await fs14.writeFile(backupPath, content, { encoding: "utf8" });
1620
2018
  await fs14.writeFile(filePath, EMPTY_DOCUMENT, { encoding: "utf8" });
1621
2019
  }
1622
2020
  function createInvalidBackupPath(filePath) {
1623
- const directory = path6.dirname(filePath);
1624
- const baseName = path6.basename(filePath);
1625
- return path6.join(directory, `${baseName}.invalid-${createTimestamp()}.json`);
2021
+ const directory = path7.dirname(filePath);
2022
+ const baseName = path7.basename(filePath);
2023
+ return path7.join(directory, `${baseName}.invalid-${createTimestamp()}.json`);
1626
2024
  }
1627
2025
  function isRecord(value) {
1628
2026
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1629
2027
  }
2028
+ function resolveConfigPath(homeDir) {
2029
+ return path7.join(homeDir, ".poe-code", "config.json");
2030
+ }
2031
+ function resolveProjectConfigPath(cwd) {
2032
+ return path7.join(cwd, ".poe-code", "config.json");
2033
+ }
1630
2034
  var EMPTY_DOCUMENT = `${JSON.stringify({}, null, 2)}
1631
2035
  `;
1632
2036
 
2037
+ // packages/poe-code-config/src/resolve.ts
2038
+ function resolveScope(schema, fileValues, env = {}) {
2039
+ const resolved = {};
2040
+ for (const key of Object.keys(schema)) {
2041
+ const field = schema[key];
2042
+ const envValue = resolveEnvValue(field, env, key);
2043
+ const fileValue = resolveFileValue(field, fileValues?.[key], key);
2044
+ resolved[key] = envValue ?? fileValue ?? field.default;
2045
+ }
2046
+ return resolved;
2047
+ }
2048
+ function resolveEnvValue(field, env, key) {
2049
+ if (!field.env) {
2050
+ return void 0;
2051
+ }
2052
+ const raw = env[field.env];
2053
+ if (raw === void 0) {
2054
+ return void 0;
2055
+ }
2056
+ return coerceValue(field, raw, key);
2057
+ }
2058
+ function resolveFileValue(field, value, key) {
2059
+ return coerceValue(field, value, key);
2060
+ }
2061
+ function coerceValue(field, value, key) {
2062
+ switch (field.type) {
2063
+ case "string":
2064
+ return typeof value === "string" ? value : void 0;
2065
+ case "number":
2066
+ return coerceNumber(value);
2067
+ case "boolean":
2068
+ return coerceBoolean(value);
2069
+ case "json":
2070
+ return coerceJson(field, value, key);
2071
+ }
2072
+ }
2073
+ function coerceNumber(value) {
2074
+ if (typeof value === "number" && Number.isFinite(value)) {
2075
+ return value;
2076
+ }
2077
+ if (typeof value !== "string" || value.length === 0) {
2078
+ return void 0;
2079
+ }
2080
+ const parsed = Number(value);
2081
+ return Number.isNaN(parsed) ? void 0 : parsed;
2082
+ }
2083
+ function coerceBoolean(value) {
2084
+ if (typeof value === "boolean") {
2085
+ return value;
2086
+ }
2087
+ if (value === "true" || value === "1") {
2088
+ return true;
2089
+ }
2090
+ if (value === "false" || value === "0") {
2091
+ return false;
2092
+ }
2093
+ return void 0;
2094
+ }
2095
+ function coerceJson(field, value, key) {
2096
+ if (value === void 0) {
2097
+ return void 0;
2098
+ }
2099
+ const parsedValue = parseJsonValue(value, key);
2100
+ try {
2101
+ return field.parse(parsedValue);
2102
+ } catch (error2) {
2103
+ const message2 = error2 instanceof Error ? error2.message : "Invalid JSON value.";
2104
+ throw new Error(`Invalid config value for "${key}": ${message2}`);
2105
+ }
2106
+ }
2107
+ function parseJsonValue(value, key) {
2108
+ if (typeof value !== "string") {
2109
+ return value;
2110
+ }
2111
+ try {
2112
+ return JSON.parse(value);
2113
+ } catch {
2114
+ throw new Error(`Invalid config value for "${key}": expected valid JSON.`);
2115
+ }
2116
+ }
2117
+
2118
+ // packages/poe-code-config/src/merge.ts
2119
+ function deepMergeDocuments(base, override) {
2120
+ const merged = {};
2121
+ const scopes = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(override)]);
2122
+ for (const scope of scopes) {
2123
+ const baseScope = base[scope] ?? {};
2124
+ const overrideScope = override[scope] ?? {};
2125
+ const nextScope = mergeScope(scope, baseScope, overrideScope);
2126
+ if (Object.keys(nextScope).length > 0) {
2127
+ merged[scope] = nextScope;
2128
+ }
2129
+ }
2130
+ return merged;
2131
+ }
2132
+ function mergeScope(scope, baseScope, overrideScope) {
2133
+ if (scope === "runtime") {
2134
+ return mergeRuntimeScope(baseScope, overrideScope);
2135
+ }
2136
+ const scopeEntries = Object.entries(overrideScope).filter(([, value]) => value !== void 0);
2137
+ return {
2138
+ ...baseScope,
2139
+ ...Object.fromEntries(scopeEntries)
2140
+ };
2141
+ }
2142
+ function mergeRuntimeScope(baseScope, overrideScope, path38 = []) {
2143
+ const merged = {};
2144
+ const keys = /* @__PURE__ */ new Set([...Object.keys(baseScope), ...Object.keys(overrideScope)]);
2145
+ for (const key of keys) {
2146
+ const baseValue = baseScope[key];
2147
+ const overrideValue = overrideScope[key];
2148
+ if (overrideValue === void 0) {
2149
+ if (baseValue !== void 0) {
2150
+ merged[key] = baseValue;
2151
+ }
2152
+ continue;
2153
+ }
2154
+ if (isRuntimeConcatenativeArray([...path38, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
2155
+ merged[key] = [...baseValue, ...overrideValue];
2156
+ continue;
2157
+ }
2158
+ if (isRecord2(baseValue) && isRecord2(overrideValue)) {
2159
+ merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path38, key]);
2160
+ continue;
2161
+ }
2162
+ merged[key] = overrideValue;
2163
+ }
2164
+ return merged;
2165
+ }
2166
+ function isRuntimeConcatenativeArray(path38) {
2167
+ return path38.join(".") === "mounts" || path38.join(".") === "runner.workspace.exclude";
2168
+ }
2169
+ function isRecord2(value) {
2170
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2171
+ }
2172
+
1633
2173
  // packages/poe-code-config/src/memory.ts
1634
2174
  async function configuredMemoryRoot(options) {
1635
2175
  return (await resolveMemoryConfig(options)).root;
@@ -1646,10 +2186,10 @@ async function cacheEnabled(options) {
1646
2186
  }
1647
2187
  async function resolveMemoryConfig(options) {
1648
2188
  const document = await readMergedDocument(options.fs, options.filePath, options.projectFilePath);
1649
- const memory = asRecord(document.memory);
1650
- const cache = asRecord(memory?.cache);
1651
- const mcp = asRecord(memory?.mcp);
1652
- const query = asRecord(memory?.query);
2189
+ const memory = asRecord2(document.memory);
2190
+ const cache = asRecord2(memory?.cache);
2191
+ const mcp = asRecord2(memory?.mcp);
2192
+ const query = asRecord2(memory?.query);
1653
2193
  return {
1654
2194
  root: readString(memory?.root),
1655
2195
  ingestAgent: readString(memory?.ingestAgent),
@@ -1659,7 +2199,7 @@ async function resolveMemoryConfig(options) {
1659
2199
  defaultQueryBudget: readNumber(query?.defaultBudgetTokens) ?? 4096
1660
2200
  };
1661
2201
  }
1662
- function asRecord(value) {
2202
+ function asRecord2(value) {
1663
2203
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1664
2204
  return void 0;
1665
2205
  }
@@ -1676,55 +2216,523 @@ function readBoolean(value) {
1676
2216
  }
1677
2217
 
1678
2218
  // packages/poe-code-config/src/inspect.ts
1679
- import path7 from "node:path";
2219
+ import path8 from "node:path";
1680
2220
  var EMPTY_DOCUMENT2 = `${JSON.stringify({}, null, 2)}
1681
2221
  `;
1682
2222
 
1683
- // packages/memory/src/resolve-root.ts
1684
- var MEMORY_ROOT_ENV_VAR = "POE_CODE_MEMORY_ROOT";
1685
- async function resolveConfiguredMemoryRoot(options) {
1686
- const envOverride = options.env[MEMORY_ROOT_ENV_VAR]?.trim();
1687
- if (envOverride && envOverride.length > 0) {
1688
- return resolveAgainstCwd(options.cwd, envOverride);
2223
+ // packages/poe-code-config/src/state/index.ts
2224
+ import os2 from "node:os";
2225
+
2226
+ // packages/poe-code-config/src/state/jobs.ts
2227
+ import path9 from "node:path";
2228
+
2229
+ // packages/file-lock/src/lock.ts
2230
+ import * as fsPromises from "node:fs/promises";
2231
+ import * as os from "node:os";
2232
+ var LockTimeoutError = class extends Error {
2233
+ constructor(message2) {
2234
+ super(message2);
2235
+ this.name = "LockTimeoutError";
1689
2236
  }
1690
- const configOverride = (await configuredMemoryRoot({
1691
- fs: options.fs,
1692
- filePath: options.configPath,
1693
- projectFilePath: options.projectConfigPath
1694
- }))?.trim();
1695
- if (configOverride && configOverride.length > 0) {
1696
- return resolveAgainstCwd(options.cwd, configOverride);
2237
+ };
2238
+ function createAbortError() {
2239
+ const error2 = new Error("The operation was aborted.");
2240
+ error2.name = "AbortError";
2241
+ return error2;
2242
+ }
2243
+ function throwIfAborted(signal) {
2244
+ if (signal?.aborted) {
2245
+ throw createAbortError();
1697
2246
  }
1698
- return resolveMemoryRoot(options.cwd);
1699
2247
  }
1700
- function resolveAgainstCwd(cwd, value) {
1701
- return path8.isAbsolute(value) ? value : path8.resolve(cwd, value);
2248
+ function sleep(ms, signal) {
2249
+ if (!signal) {
2250
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
2251
+ }
2252
+ if (signal.aborted) {
2253
+ return Promise.reject(createAbortError());
2254
+ }
2255
+ return new Promise((resolve2, reject) => {
2256
+ const timeoutId = setTimeout(() => {
2257
+ signal.removeEventListener("abort", onAbort);
2258
+ resolve2();
2259
+ }, ms);
2260
+ const onAbort = () => {
2261
+ clearTimeout(timeoutId);
2262
+ signal.removeEventListener("abort", onAbort);
2263
+ reject(createAbortError());
2264
+ };
2265
+ signal.addEventListener("abort", onAbort, { once: true });
2266
+ });
1702
2267
  }
1703
-
1704
- // packages/memory/src/init.ts
1705
- import * as fs from "node:fs/promises";
1706
- import path9 from "node:path";
1707
- async function initMemory(root) {
1708
- await fs.mkdir(path9.join(root, MEMORY_PAGES_DIR_RELPATH), { recursive: true });
1709
- await writeFileIfMissing(path9.join(root, MEMORY_INDEX_RELPATH), "# Memory index\n");
1710
- await writeFileIfMissing(path9.join(root, MEMORY_LOG_RELPATH), "");
2268
+ function backoff(attempt, minTimeout, maxTimeout) {
2269
+ const delay = Math.min(maxTimeout, minTimeout * 2 ** attempt);
2270
+ return delay + Math.random() * delay * 0.1;
1711
2271
  }
1712
- async function writeFileIfMissing(filePath, content) {
2272
+ function hasErrorCode(error2, code) {
2273
+ return !!error2 && typeof error2 === "object" && "code" in error2 && error2.code === code;
2274
+ }
2275
+ function hasAnyErrorCode(error2, codes) {
2276
+ return codes.some((code) => hasErrorCode(error2, code));
2277
+ }
2278
+ function isPidRunning(pid) {
2279
+ try {
2280
+ process.kill(pid, 0);
2281
+ return true;
2282
+ } catch (error2) {
2283
+ return !hasErrorCode(error2, "ESRCH");
2284
+ }
2285
+ }
2286
+ function createDefaultFs() {
2287
+ return {
2288
+ open: (path38, flags) => fsPromises.open(path38, flags),
2289
+ readFile: (path38, encoding) => fsPromises.readFile(path38, encoding),
2290
+ stat: fsPromises.stat,
2291
+ unlink: fsPromises.unlink
2292
+ };
2293
+ }
2294
+ async function removeLockFile(fs14, lockPath, signal) {
2295
+ for (let attempt = 0; attempt <= 4; attempt += 1) {
2296
+ throwIfAborted(signal);
2297
+ try {
2298
+ await fs14.unlink(lockPath);
2299
+ return;
2300
+ } catch (error2) {
2301
+ if (hasErrorCode(error2, "ENOENT")) {
2302
+ return;
2303
+ }
2304
+ if (!hasAnyErrorCode(error2, ["EPERM", "EBUSY"]) || attempt === 4) {
2305
+ throw error2;
2306
+ }
2307
+ }
2308
+ await sleep(25 * 2 ** attempt, signal);
2309
+ }
2310
+ }
2311
+ function parseLockMetadata(content) {
2312
+ try {
2313
+ const parsed = JSON.parse(content);
2314
+ if (!parsed || typeof parsed !== "object" || !("host" in parsed) || !("pid" in parsed)) {
2315
+ return void 0;
2316
+ }
2317
+ const { host, pid } = parsed;
2318
+ if (typeof host === "string" && typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0) {
2319
+ return {
2320
+ host,
2321
+ pid
2322
+ };
2323
+ }
2324
+ } catch (ignoredError) {
2325
+ void ignoredError;
2326
+ }
2327
+ return void 0;
2328
+ }
2329
+ async function readLockMetadata(fs14, lockPath) {
2330
+ if (!fs14.readFile) {
2331
+ return void 0;
2332
+ }
2333
+ try {
2334
+ return parseLockMetadata(await fs14.readFile(lockPath, "utf8"));
2335
+ } catch (error2) {
2336
+ if (hasErrorCode(error2, "ENOENT")) {
2337
+ return null;
2338
+ }
2339
+ return void 0;
2340
+ }
2341
+ }
2342
+ async function shouldReclaimLock(options) {
2343
+ const metadata = await readLockMetadata(options.fs, options.lockPath);
2344
+ if (metadata === null) {
2345
+ return "missing";
2346
+ }
2347
+ if (metadata?.host === os.hostname()) {
2348
+ return !options.isPidRunning(metadata.pid);
2349
+ }
2350
+ return Date.now() - options.stat.mtimeMs > options.staleMs;
2351
+ }
2352
+ async function writeLockMetadata(handle) {
2353
+ try {
2354
+ await handle.writeFile(
2355
+ JSON.stringify({ pid: process.pid, host: os.hostname(), acquiredAt: (/* @__PURE__ */ new Date()).toISOString() }),
2356
+ { encoding: "utf8" }
2357
+ );
2358
+ } catch (ignoredError) {
2359
+ void ignoredError;
2360
+ }
2361
+ try {
2362
+ await handle.close();
2363
+ } catch (ignoredError) {
2364
+ void ignoredError;
2365
+ }
2366
+ }
2367
+ async function acquireFileLock(filePath, options = {}) {
2368
+ const fs14 = options.fs ?? createDefaultFs();
2369
+ const retries = options.retries ?? 20;
2370
+ const minTimeout = options.minTimeout ?? 25;
2371
+ const maxTimeout = options.maxTimeout ?? 250;
2372
+ const staleMs = options.staleMs ?? 1e3;
2373
+ const pidIsRunning = options.isPidRunning ?? isPidRunning;
2374
+ const lockPath = `${filePath}.lock`;
2375
+ let attempt = 0;
2376
+ while (attempt <= retries) {
2377
+ throwIfAborted(options.signal);
2378
+ try {
2379
+ const handle = await fs14.open(lockPath, "wx");
2380
+ await writeLockMetadata(handle);
2381
+ let released = false;
2382
+ return async () => {
2383
+ if (released) {
2384
+ return;
2385
+ }
2386
+ released = true;
2387
+ await removeLockFile(fs14, lockPath, options.signal);
2388
+ };
2389
+ } catch (error2) {
2390
+ if (!hasErrorCode(error2, "EEXIST")) {
2391
+ throw error2;
2392
+ }
2393
+ }
2394
+ let stat7;
2395
+ try {
2396
+ stat7 = await fs14.stat(lockPath);
2397
+ } catch (statError) {
2398
+ if (hasErrorCode(statError, "ENOENT")) {
2399
+ continue;
2400
+ }
2401
+ throw statError;
2402
+ }
2403
+ const reclaimLock = await shouldReclaimLock({
2404
+ fs: fs14,
2405
+ isPidRunning: pidIsRunning,
2406
+ lockPath,
2407
+ staleMs,
2408
+ stat: stat7
2409
+ });
2410
+ if (reclaimLock === "missing") {
2411
+ continue;
2412
+ }
2413
+ if (reclaimLock) {
2414
+ await removeLockFile(fs14, lockPath, options.signal);
2415
+ continue;
2416
+ }
2417
+ if (attempt >= retries) {
2418
+ break;
2419
+ }
2420
+ await sleep(backoff(attempt, minTimeout, maxTimeout), options.signal);
2421
+ attempt += 1;
2422
+ }
2423
+ throw new LockTimeoutError(`Failed to acquire lock on "${filePath}".`);
2424
+ }
2425
+
2426
+ // packages/poe-code-config/src/state/fs.ts
2427
+ import * as nodeFs from "node:fs/promises";
2428
+ var defaultStateFs = nodeFs;
2429
+ function isNotFoundError(error2) {
2430
+ return error2 instanceof Error && "code" in error2 && error2.code === "ENOENT";
2431
+ }
2432
+
2433
+ // packages/poe-code-config/src/state/jobs.ts
2434
+ function createJobRegistry(homeDir, fs14 = defaultStateFs) {
2435
+ const jobsDir = path9.join(homeDir, ".poe-code", "state", "jobs");
2436
+ function jobPath(id) {
2437
+ assertSafeJobId(id);
2438
+ return path9.join(jobsDir, `${id}.json`);
2439
+ }
2440
+ async function get(id) {
2441
+ try {
2442
+ return parseJobEntry(await fs14.readFile(jobPath(id), "utf8"));
2443
+ } catch (error2) {
2444
+ if (isNotFoundError(error2)) {
2445
+ return null;
2446
+ }
2447
+ throw error2;
2448
+ }
2449
+ }
2450
+ async function put(entry) {
2451
+ assertJobEntry(entry);
2452
+ const filePath = jobPath(entry.id);
2453
+ await fs14.mkdir(jobsDir, { recursive: true });
2454
+ const release = await acquireFileLock(filePath, { fs: fs14 });
2455
+ try {
2456
+ await writeJobAtomically(filePath, entry);
2457
+ } finally {
2458
+ await release();
2459
+ }
2460
+ }
2461
+ async function update(id, patch) {
2462
+ const filePath = jobPath(id);
2463
+ await fs14.mkdir(jobsDir, { recursive: true });
2464
+ const release = await acquireFileLock(filePath, { fs: fs14 });
2465
+ try {
2466
+ const current = await get(id);
2467
+ if (current === null) {
2468
+ return null;
2469
+ }
2470
+ const updated = {
2471
+ ...current,
2472
+ ...patch,
2473
+ id: current.id
2474
+ };
2475
+ assertJobEntry(updated);
2476
+ await writeJobAtomically(filePath, updated);
2477
+ return updated;
2478
+ } finally {
2479
+ await release();
2480
+ }
2481
+ }
2482
+ async function list(filter = {}) {
2483
+ let entries;
2484
+ try {
2485
+ entries = await fs14.readdir(jobsDir);
2486
+ } catch (error2) {
2487
+ if (isNotFoundError(error2)) {
2488
+ return [];
2489
+ }
2490
+ throw error2;
2491
+ }
2492
+ const jobs = [];
2493
+ for (const entry of entries.sort()) {
2494
+ if (!entry.endsWith(".json")) {
2495
+ continue;
2496
+ }
2497
+ const filePath = path9.join(jobsDir, entry);
2498
+ const stat7 = await fs14.stat(filePath);
2499
+ if (!stat7.isFile()) {
2500
+ continue;
2501
+ }
2502
+ const job = parseJobEntry(await fs14.readFile(filePath, "utf8"));
2503
+ if (matchesFilter(job, filter)) {
2504
+ jobs.push(job);
2505
+ }
2506
+ }
2507
+ return jobs;
2508
+ }
2509
+ async function remove2(id) {
2510
+ const filePath = jobPath(id);
2511
+ try {
2512
+ await fs14.stat(jobsDir);
2513
+ } catch (error2) {
2514
+ if (isNotFoundError(error2)) {
2515
+ return;
2516
+ }
2517
+ throw error2;
2518
+ }
2519
+ const release = await acquireFileLock(filePath, { fs: fs14 });
2520
+ try {
2521
+ await fs14.unlink(filePath);
2522
+ } catch (error2) {
2523
+ if (!isNotFoundError(error2)) {
2524
+ throw error2;
2525
+ }
2526
+ } finally {
2527
+ await release();
2528
+ }
2529
+ }
2530
+ async function writeJobAtomically(filePath, entry) {
2531
+ await fs14.mkdir(path9.dirname(filePath), { recursive: true });
2532
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
2533
+ try {
2534
+ await fs14.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
2535
+ `, {
2536
+ encoding: "utf8"
2537
+ });
2538
+ await fs14.rename(tempPath, filePath);
2539
+ } catch (error2) {
2540
+ await removeTempFile(tempPath);
2541
+ throw error2;
2542
+ }
2543
+ }
2544
+ async function removeTempFile(tempPath) {
2545
+ try {
2546
+ await fs14.unlink(tempPath);
2547
+ } catch (error2) {
2548
+ if (!isNotFoundError(error2)) {
2549
+ throw error2;
2550
+ }
2551
+ }
2552
+ }
2553
+ return {
2554
+ get,
2555
+ put,
2556
+ update,
2557
+ list,
2558
+ remove: remove2
2559
+ };
2560
+ }
2561
+ function assertSafeJobId(id) {
2562
+ if (id.length === 0 || id === "." || id === ".." || path9.isAbsolute(id) || id.includes("/") || id.includes("\\") || id.includes("\0")) {
2563
+ throw new Error("Invalid job id.");
2564
+ }
2565
+ }
2566
+ function assertJobEntry(entry) {
2567
+ if (!isJobEntry(entry)) {
2568
+ throw new Error("Invalid job entry.");
2569
+ }
2570
+ }
2571
+ function matchesFilter(job, filter) {
2572
+ return (filter.env_id === void 0 || job.env_id === filter.env_id) && (filter.env_kind === void 0 || job.env_kind === filter.env_kind) && (filter.tool === void 0 || job.tool === filter.tool) && (filter.status === void 0 || job.status === filter.status);
2573
+ }
2574
+ function parseJobEntry(content) {
2575
+ const parsed = JSON.parse(content);
2576
+ if (!isJobEntry(parsed)) {
2577
+ throw new Error("Invalid job state file.");
2578
+ }
2579
+ return parsed;
2580
+ }
2581
+ function isJobEntry(value) {
2582
+ return isRecord3(value) && typeof value.id === "string" && typeof value.env_id === "string" && typeof value.env_kind === "string" && typeof value.tool === "string" && Array.isArray(value.argv) && value.argv.every((arg) => typeof arg === "string") && typeof value.cwd === "string" && typeof value.started_at === "string" && isJobStatus(value.status) && (value.exit_code === void 0 || typeof value.exit_code === "number") && (value.exited_at === void 0 || typeof value.exited_at === "string") && (value.log_file === void 0 || typeof value.log_file === "string");
2583
+ }
2584
+ function isJobStatus(value) {
2585
+ return value === "pending" || value === "running" || value === "exited" || value === "killed" || value === "lost";
2586
+ }
2587
+ function isRecord3(value) {
2588
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2589
+ }
2590
+
2591
+ // packages/poe-code-config/src/state/templates.ts
2592
+ import path10 from "node:path";
2593
+ function createTemplateRegistry(homeDir, fs14 = defaultStateFs) {
2594
+ const filePath = path10.join(homeDir, ".poe-code", "state", "templates.json");
2595
+ async function readState() {
2596
+ try {
2597
+ const raw = await fs14.readFile(filePath, "utf8");
2598
+ return normalizeTemplateState(JSON.parse(raw));
2599
+ } catch (error2) {
2600
+ if (isNotFoundError(error2)) {
2601
+ return createEmptyState();
2602
+ }
2603
+ throw error2;
2604
+ }
2605
+ }
2606
+ async function writeState(state) {
2607
+ await fs14.writeFile(filePath, `${JSON.stringify(state, null, 2)}
2608
+ `, {
2609
+ encoding: "utf8"
2610
+ });
2611
+ }
2612
+ async function updateState(mutator) {
2613
+ await fs14.mkdir(path10.dirname(filePath), { recursive: true });
2614
+ const release = await acquireFileLock(filePath, { fs: fs14 });
2615
+ try {
2616
+ const state = await readState();
2617
+ mutator(state);
2618
+ await writeState(state);
2619
+ } finally {
2620
+ await release();
2621
+ }
2622
+ }
2623
+ async function get(backend, hash) {
2624
+ const state = await readState();
2625
+ return state[backend][hash] ?? null;
2626
+ }
2627
+ async function put(backend, entry) {
2628
+ await updateState((state) => {
2629
+ state[backend][entry.hash] = entry;
2630
+ });
2631
+ }
2632
+ async function remove2(backend, hash) {
2633
+ await updateState((state) => {
2634
+ delete state[backend][hash];
2635
+ });
2636
+ }
2637
+ async function list(backend) {
2638
+ const state = await readState();
2639
+ const entries = backend === void 0 ? [...Object.values(state.docker), ...Object.values(state.e2b)] : Object.values(state[backend]);
2640
+ return entries.sort((left, right) => left.hash.localeCompare(right.hash));
2641
+ }
2642
+ return {
2643
+ get,
2644
+ put,
2645
+ remove: remove2,
2646
+ list
2647
+ };
2648
+ }
2649
+ function createEmptyState() {
2650
+ return {
2651
+ docker: {},
2652
+ e2b: {}
2653
+ };
2654
+ }
2655
+ function normalizeTemplateState(value) {
2656
+ if (!isRecord4(value)) {
2657
+ return createEmptyState();
2658
+ }
2659
+ return {
2660
+ docker: normalizeTemplateEntries(value.docker),
2661
+ e2b: normalizeTemplateEntries(value.e2b)
2662
+ };
2663
+ }
2664
+ function normalizeTemplateEntries(value) {
2665
+ if (!isRecord4(value)) {
2666
+ return {};
2667
+ }
2668
+ const entries = {};
2669
+ for (const [hash, entry] of Object.entries(value)) {
2670
+ if (isTemplateEntry(entry) && entry.hash === hash) {
2671
+ entries[hash] = entry;
2672
+ }
2673
+ }
2674
+ return entries;
2675
+ }
2676
+ function isTemplateEntry(value) {
2677
+ return isRecord4(value) && typeof value.hash === "string" && typeof value.runtime_type === "string" && typeof value.dockerfile_path === "string" && typeof value.built_at === "string" && (value.template_id === void 0 || typeof value.template_id === "string") && (value.image === void 0 || typeof value.image === "string");
2678
+ }
2679
+ function isRecord4(value) {
2680
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2681
+ }
2682
+
2683
+ // packages/poe-code-config/src/state/index.ts
2684
+ function createStateManager(homeDir, fs14) {
2685
+ return {
2686
+ templates: createTemplateRegistry(homeDir, fs14),
2687
+ jobs: createJobRegistry(homeDir, fs14)
2688
+ };
2689
+ }
2690
+
2691
+ // packages/memory/src/resolve-root.ts
2692
+ var MEMORY_ROOT_ENV_VAR = "POE_CODE_MEMORY_ROOT";
2693
+ async function resolveConfiguredMemoryRoot(options) {
2694
+ const envOverride = options.env[MEMORY_ROOT_ENV_VAR]?.trim();
2695
+ if (envOverride && envOverride.length > 0) {
2696
+ return resolveAgainstCwd(options.cwd, envOverride);
2697
+ }
2698
+ const configOverride = (await configuredMemoryRoot({
2699
+ fs: options.fs,
2700
+ filePath: options.configPath,
2701
+ projectFilePath: options.projectConfigPath
2702
+ }))?.trim();
2703
+ if (configOverride && configOverride.length > 0) {
2704
+ return resolveAgainstCwd(options.cwd, configOverride);
2705
+ }
2706
+ return resolveMemoryRoot(options.cwd);
2707
+ }
2708
+ function resolveAgainstCwd(cwd, value) {
2709
+ return path11.isAbsolute(value) ? value : path11.resolve(cwd, value);
2710
+ }
2711
+
2712
+ // packages/memory/src/init.ts
2713
+ import * as fs from "node:fs/promises";
2714
+ import path12 from "node:path";
2715
+ async function initMemory(root) {
2716
+ await fs.mkdir(path12.join(root, MEMORY_PAGES_DIR_RELPATH), { recursive: true });
2717
+ await writeFileIfMissing(path12.join(root, MEMORY_INDEX_RELPATH), "# Memory index\n");
2718
+ await writeFileIfMissing(path12.join(root, MEMORY_LOG_RELPATH), "");
2719
+ }
2720
+ async function writeFileIfMissing(filePath, content) {
1713
2721
  try {
1714
2722
  await fs.writeFile(filePath, content, { encoding: "utf8", flag: "wx" });
1715
2723
  } catch (error2) {
1716
- if (!hasErrorCode(error2, "EEXIST")) {
2724
+ if (!hasErrorCode2(error2, "EEXIST")) {
1717
2725
  throw error2;
1718
2726
  }
1719
2727
  }
1720
2728
  }
1721
- function hasErrorCode(error2, code) {
2729
+ function hasErrorCode2(error2, code) {
1722
2730
  return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === code;
1723
2731
  }
1724
2732
 
1725
2733
  // packages/memory/src/pages.ts
1726
2734
  import * as fs2 from "node:fs/promises";
1727
- import path10 from "node:path";
2735
+ import path13 from "node:path";
1728
2736
 
1729
2737
  // packages/memory/src/frontmatter.ts
1730
2738
  import { parse as parse5, stringify } from "yaml";
@@ -1870,7 +2878,7 @@ function parseYamlFrontmatter(yamlBlock) {
1870
2878
  if (parsed === null) {
1871
2879
  return {};
1872
2880
  }
1873
- if (!isRecord2(parsed)) {
2881
+ if (!isRecord5(parsed)) {
1874
2882
  throw new Error("YAML frontmatter must parse to an object.");
1875
2883
  }
1876
2884
  return parsed;
@@ -1901,13 +2909,13 @@ function parseSources(value) {
1901
2909
  if (typeof item === "string") {
1902
2910
  return parseSourceRef(item);
1903
2911
  }
1904
- if (!isRecord2(item)) {
2912
+ if (!isRecord5(item)) {
1905
2913
  throw new Error('Invalid "sources" frontmatter. Expected each source to be a string or object.');
1906
2914
  }
1907
- const path30 = readRequiredString(item.path, "sources[].path");
2915
+ const path38 = readRequiredString(item.path, "sources[].path");
1908
2916
  const startLine = readOptionalPositiveInteger(item.startLine, "sources[].startLine");
1909
2917
  const endLine = readOptionalPositiveInteger(item.endLine, "sources[].endLine");
1910
- return parseSourceRef(serializeSourceRef({ path: path30, ...startLine === void 0 ? {} : { startLine }, ...endLine === void 0 ? {} : { endLine } }));
2918
+ return parseSourceRef(serializeSourceRef({ path: path38, ...startLine === void 0 ? {} : { startLine }, ...endLine === void 0 ? {} : { endLine } }));
1911
2919
  });
1912
2920
  }
1913
2921
  function readOptionalString(value, field) {
@@ -1940,7 +2948,7 @@ function assertValidLineNumber(line, value) {
1940
2948
  throw new Error(`Invalid source ref "${value}": line numbers must be positive integers.`);
1941
2949
  }
1942
2950
  }
1943
- function isRecord2(value) {
2951
+ function isRecord5(value) {
1944
2952
  return typeof value === "object" && value !== null && !Array.isArray(value);
1945
2953
  }
1946
2954
 
@@ -1952,8 +2960,8 @@ async function listPages(root) {
1952
2960
  }
1953
2961
  async function readPage(root, relPath) {
1954
2962
  const normalizedRelPath = assertMarkdownRelPath(relPath);
1955
- const absPath = path10.join(root, normalizedRelPath);
1956
- const [content, stat6] = await Promise.all([fs2.readFile(absPath, "utf8"), fs2.stat(absPath)]);
2963
+ const absPath = path13.join(root, normalizedRelPath);
2964
+ const [content, stat7] = await Promise.all([fs2.readFile(absPath, "utf8"), fs2.stat(absPath)]);
1957
2965
  try {
1958
2966
  const parsed = parseFrontmatter(content);
1959
2967
  return {
@@ -1961,7 +2969,7 @@ async function readPage(root, relPath) {
1961
2969
  frontmatter: parsed.frontmatter,
1962
2970
  body: parsed.body,
1963
2971
  bytes: Buffer.byteLength(content),
1964
- mtimeMs: stat6.mtimeMs
2972
+ mtimeMs: stat7.mtimeMs
1965
2973
  };
1966
2974
  } catch (error2) {
1967
2975
  const message2 = error2 instanceof Error ? error2.message : String(error2);
@@ -1971,7 +2979,7 @@ async function readPage(root, relPath) {
1971
2979
  frontmatter: {},
1972
2980
  body: content,
1973
2981
  bytes: Buffer.byteLength(content),
1974
- mtimeMs: stat6.mtimeMs
2982
+ mtimeMs: stat7.mtimeMs
1975
2983
  };
1976
2984
  }
1977
2985
  }
@@ -1981,7 +2989,7 @@ async function collectMarkdownRelPaths(root, startRelPath = "") {
1981
2989
  return relPaths.sort((left, right) => left.localeCompare(right));
1982
2990
  }
1983
2991
  async function collectMarkdownRelPathsInto(root, currentRelPath, relPaths) {
1984
- const absPath = path10.join(root, currentRelPath);
2992
+ const absPath = path13.join(root, currentRelPath);
1985
2993
  let entryNames;
1986
2994
  try {
1987
2995
  entryNames = await fs2.readdir(absPath);
@@ -1992,8 +3000,8 @@ async function collectMarkdownRelPathsInto(root, currentRelPath, relPaths) {
1992
3000
  throw error2;
1993
3001
  }
1994
3002
  for (const entryName of entryNames.sort((left, right) => left.localeCompare(right))) {
1995
- const entryRelPath = currentRelPath.length === 0 ? entryName : path10.posix.join(currentRelPath, entryName);
1996
- const entryAbsPath = path10.join(root, entryRelPath);
3003
+ const entryRelPath = currentRelPath.length === 0 ? entryName : path13.posix.join(currentRelPath, entryName);
3004
+ const entryAbsPath = path13.join(root, entryRelPath);
1997
3005
  const entryStat = await fs2.stat(entryAbsPath);
1998
3006
  if (entryStat.isDirectory()) {
1999
3007
  if (entryName === MEMORY_CACHE_DIR_RELPATH) {
@@ -2023,7 +3031,7 @@ function assertMarkdownRelPath(relPath) {
2023
3031
  return normalizedRelPath;
2024
3032
  }
2025
3033
  function isMarkdownPath(relPath) {
2026
- return path10.posix.extname(relPath).toLowerCase() === ".md";
3034
+ return path13.posix.extname(relPath).toLowerCase() === ".md";
2027
3035
  }
2028
3036
  function isMissing(error2) {
2029
3037
  return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
@@ -2031,7 +3039,7 @@ function isMissing(error2) {
2031
3039
 
2032
3040
  // packages/memory/src/search.ts
2033
3041
  import * as fs3 from "node:fs/promises";
2034
- import path11 from "node:path";
3042
+ import path14 from "node:path";
2035
3043
  async function searchMemory(root, query) {
2036
3044
  const normalizedQuery = query.trim();
2037
3045
  if (normalizedQuery.length === 0) {
@@ -2040,7 +3048,7 @@ async function searchMemory(root, query) {
2040
3048
  const relPaths = await collectMarkdownRelPaths(root);
2041
3049
  const hits = [];
2042
3050
  for (const relPath of relPaths) {
2043
- const content = await fs3.readFile(path11.join(root, relPath), "utf8");
3051
+ const content = await fs3.readFile(path14.join(root, relPath), "utf8");
2044
3052
  if (content.length === 0) {
2045
3053
  continue;
2046
3054
  }
@@ -2061,7 +3069,7 @@ async function searchMemory(root, query) {
2061
3069
 
2062
3070
  // packages/memory/src/status.ts
2063
3071
  import * as fs4 from "node:fs/promises";
2064
- import path12 from "node:path";
3072
+ import path15 from "node:path";
2065
3073
  async function statusOf(root) {
2066
3074
  if (!await pathExists2(root)) {
2067
3075
  return {
@@ -2078,9 +3086,9 @@ async function statusOf(root) {
2078
3086
  let totalBytes = 0;
2079
3087
  let lastWriteAtMs = Number.NEGATIVE_INFINITY;
2080
3088
  for (const relPath of markdownRelPaths) {
2081
- const stat6 = await fs4.stat(path12.join(root, relPath));
2082
- totalBytes += stat6.size;
2083
- lastWriteAtMs = Math.max(lastWriteAtMs, stat6.mtimeMs);
3089
+ const stat7 = await fs4.stat(path15.join(root, relPath));
3090
+ totalBytes += stat7.size;
3091
+ lastWriteAtMs = Math.max(lastWriteAtMs, stat7.mtimeMs);
2084
3092
  }
2085
3093
  return {
2086
3094
  pageCount: pageRelPaths.length,
@@ -2103,26 +3111,26 @@ async function pathExists2(targetPath) {
2103
3111
 
2104
3112
  // packages/memory/src/edit.ts
2105
3113
  import * as fs7 from "node:fs/promises";
2106
- import path16 from "node:path";
3114
+ import path19 from "node:path";
2107
3115
 
2108
3116
  // packages/memory/src/write.ts
2109
3117
  import * as fs6 from "node:fs/promises";
2110
- import path15 from "node:path";
3118
+ import path18 from "node:path";
2111
3119
 
2112
3120
  // packages/memory/src/lock.ts
2113
- import * as fsPromises from "node:fs/promises";
2114
- import path13 from "node:path";
2115
- function createDefaultFs() {
3121
+ import * as fsPromises2 from "node:fs/promises";
3122
+ import path16 from "node:path";
3123
+ function createDefaultFs2() {
2116
3124
  return {
2117
- readFile: (filePath, encoding) => fsPromises.readFile(filePath, encoding),
2118
- unlink: fsPromises.unlink,
2119
- writeFile: (filePath, data, options) => fsPromises.writeFile(filePath, data, options)
3125
+ readFile: (filePath, encoding) => fsPromises2.readFile(filePath, encoding),
3126
+ unlink: fsPromises2.unlink,
3127
+ writeFile: (filePath, data, options) => fsPromises2.writeFile(filePath, data, options)
2120
3128
  };
2121
3129
  }
2122
- function sleep(ms) {
3130
+ function sleep2(ms) {
2123
3131
  return new Promise((resolve2) => setTimeout(resolve2, ms));
2124
3132
  }
2125
- function hasErrorCode2(error2, code) {
3133
+ function hasErrorCode3(error2, code) {
2126
3134
  return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === code;
2127
3135
  }
2128
3136
  function lockDelay(attempt, minTimeoutMs, maxTimeoutMs) {
@@ -2141,19 +3149,19 @@ function parsePid(input) {
2141
3149
  const pid = Number.parseInt(value, 10);
2142
3150
  return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
2143
3151
  }
2144
- function isPidRunning(pid) {
3152
+ function isPidRunning2(pid) {
2145
3153
  try {
2146
3154
  process.kill(pid, 0);
2147
3155
  return true;
2148
3156
  } catch (error2) {
2149
- return !hasErrorCode2(error2, "ESRCH");
3157
+ return !hasErrorCode3(error2, "ESRCH");
2150
3158
  }
2151
3159
  }
2152
- async function removeLockFile(fs14, lockPath) {
3160
+ async function removeLockFile2(fs14, lockPath) {
2153
3161
  try {
2154
3162
  await fs14.unlink(lockPath);
2155
3163
  } catch (error2) {
2156
- if (!hasErrorCode2(error2, "ENOENT")) {
3164
+ if (!hasErrorCode3(error2, "ENOENT")) {
2157
3165
  throw error2;
2158
3166
  }
2159
3167
  }
@@ -2162,20 +3170,20 @@ async function readLockPid(fs14, lockPath) {
2162
3170
  try {
2163
3171
  return parsePid(await fs14.readFile(lockPath, "utf8"));
2164
3172
  } catch (error2) {
2165
- if (hasErrorCode2(error2, "ENOENT")) {
3173
+ if (hasErrorCode3(error2, "ENOENT")) {
2166
3174
  return null;
2167
3175
  }
2168
3176
  throw error2;
2169
3177
  }
2170
3178
  }
2171
3179
  async function withLock(root, run, options = {}) {
2172
- const fs14 = options.fs ?? createDefaultFs();
2173
- const lockPath = path13.join(root, MEMORY_LOCK_RELPATH);
3180
+ const fs14 = options.fs ?? createDefaultFs2();
3181
+ const lockPath = path16.join(root, MEMORY_LOCK_RELPATH);
2174
3182
  const pid = options.pid ?? process.pid;
2175
3183
  const retries = options.retries ?? 20;
2176
3184
  const minTimeoutMs = options.minTimeoutMs ?? 25;
2177
3185
  const maxTimeoutMs = options.maxTimeoutMs ?? 250;
2178
- const pidIsRunning = options.isPidRunning ?? isPidRunning;
3186
+ const pidIsRunning = options.isPidRunning ?? isPidRunning2;
2179
3187
  for (let attempt = 0; attempt <= retries; attempt += 1) {
2180
3188
  try {
2181
3189
  await fs14.writeFile(lockPath, `${pid}
@@ -2183,10 +3191,10 @@ async function withLock(root, run, options = {}) {
2183
3191
  try {
2184
3192
  return await run();
2185
3193
  } finally {
2186
- await removeLockFile(fs14, lockPath);
3194
+ await removeLockFile2(fs14, lockPath);
2187
3195
  }
2188
3196
  } catch (error2) {
2189
- if (!hasErrorCode2(error2, "EEXIST")) {
3197
+ if (!hasErrorCode3(error2, "EEXIST")) {
2190
3198
  throw error2;
2191
3199
  }
2192
3200
  }
@@ -2195,11 +3203,11 @@ async function withLock(root, run, options = {}) {
2195
3203
  continue;
2196
3204
  }
2197
3205
  if (existingPid === void 0 || !pidIsRunning(existingPid)) {
2198
- await removeLockFile(fs14, lockPath);
3206
+ await removeLockFile2(fs14, lockPath);
2199
3207
  continue;
2200
3208
  }
2201
3209
  if (attempt < retries) {
2202
- await sleep(lockDelay(attempt, minTimeoutMs, maxTimeoutMs));
3210
+ await sleep2(lockDelay(attempt, minTimeoutMs, maxTimeoutMs));
2203
3211
  }
2204
3212
  }
2205
3213
  throw new Error(`Failed to acquire memory lock at "${lockPath}".`);
@@ -2208,7 +3216,7 @@ async function withLock(root, run, options = {}) {
2208
3216
  // packages/memory/src/reconcile.ts
2209
3217
  import { createHash } from "node:crypto";
2210
3218
  import * as fs5 from "node:fs/promises";
2211
- import path14 from "node:path";
3219
+ import path17 from "node:path";
2212
3220
 
2213
3221
  // packages/memory/src/confidence.ts
2214
3222
  var TAG_RE = /^<!--\s*memory:(?<verb>extracted|inferred|ambiguous)(?<rest>[^>]*?)-->\s*$/;
@@ -2420,7 +3428,7 @@ async function snapshot(root) {
2420
3428
  await Promise.all(
2421
3429
  (await collectMarkdownRelPaths(root, MEMORY_PAGES_DIR_RELPATH)).map(async (relPath) => [
2422
3430
  relPath,
2423
- hashContent(await fs5.readFile(path14.join(root, relPath), "utf8"))
3431
+ hashContent(await fs5.readFile(path17.join(root, relPath), "utf8"))
2424
3432
  ])
2425
3433
  )
2426
3434
  );
@@ -2431,7 +3439,7 @@ async function reconcile(root, before, _verb, detail) {
2431
3439
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2432
3440
  const currentPages = await Promise.all(
2433
3441
  (await collectMarkdownRelPaths(root, MEMORY_PAGES_DIR_RELPATH)).map(async (relPath) => {
2434
- const absPath = path14.join(root, relPath);
3442
+ const absPath = path17.join(root, relPath);
2435
3443
  const markdown = await fs5.readFile(absPath, "utf8");
2436
3444
  const parsed = parsePageMarkdown(relPath, markdown);
2437
3445
  const normalizedFrontmatter = withDenormalizedSources(parsed.frontmatter, parsed.body);
@@ -2453,7 +3461,7 @@ async function reconcile(root, before, _verb, detail) {
2453
3461
  })
2454
3462
  );
2455
3463
  await Promise.all(
2456
- currentPages.filter((page) => page.currentMarkdown !== page.nextMarkdown).map((page) => fs5.writeFile(path14.join(root, page.relPath), page.nextMarkdown, "utf8"))
3464
+ currentPages.filter((page) => page.currentMarkdown !== page.nextMarkdown).map((page) => fs5.writeFile(path17.join(root, page.relPath), page.nextMarkdown, "utf8"))
2457
3465
  );
2458
3466
  const diff2 = diffSnapshots(before, await snapshot(root));
2459
3467
  await writeIndex(root);
@@ -2483,7 +3491,7 @@ async function appendLogEntries(root, diff2, detail, timestamp = (/* @__PURE__ *
2483
3491
  if (entries.length === 0) {
2484
3492
  return;
2485
3493
  }
2486
- const logPath = path14.join(root, MEMORY_LOG_RELPATH);
3494
+ const logPath = path17.join(root, MEMORY_LOG_RELPATH);
2487
3495
  const existing = await fs5.readFile(logPath, "utf8");
2488
3496
  const separator = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
2489
3497
  await fs5.writeFile(logPath, `${existing}${separator}${entries.join("\n")}
@@ -2507,7 +3515,7 @@ async function writeIndex(root) {
2507
3515
  description: page.frontmatter.description ?? ""
2508
3516
  }))
2509
3517
  );
2510
- await fs5.writeFile(path14.join(root, MEMORY_INDEX_RELPATH), index, "utf8");
3518
+ await fs5.writeFile(path17.join(root, MEMORY_INDEX_RELPATH), index, "utf8");
2511
3519
  }
2512
3520
  function parsePageMarkdown(relPath, markdown) {
2513
3521
  try {
@@ -2552,9 +3560,9 @@ async function writePage(root, relPath, body, opts) {
2552
3560
  const pageRelPath = assertPageRelPath(relPath);
2553
3561
  return withLock(root, async () => {
2554
3562
  const before = await snapshot(root);
2555
- await fs6.mkdir(path15.dirname(path15.join(root, pageRelPath)), { recursive: true });
3563
+ await fs6.mkdir(path18.dirname(path18.join(root, pageRelPath)), { recursive: true });
2556
3564
  await fs6.writeFile(
2557
- path15.join(root, pageRelPath),
3565
+ path18.join(root, pageRelPath),
2558
3566
  serializeFrontmatter(opts.frontmatter ?? {}, body),
2559
3567
  "utf8"
2560
3568
  );
@@ -2565,8 +3573,8 @@ async function appendToPage(root, relPath, content, opts) {
2565
3573
  const pageRelPath = assertPageRelPath(relPath);
2566
3574
  return withLock(root, async () => {
2567
3575
  const before = await snapshot(root);
2568
- const pagePath = path15.join(root, pageRelPath);
2569
- await fs6.mkdir(path15.dirname(pagePath), { recursive: true });
3576
+ const pagePath = path18.join(root, pageRelPath);
3577
+ await fs6.mkdir(path18.dirname(pagePath), { recursive: true });
2570
3578
  const existing = await readMarkdownIfPresent(pagePath);
2571
3579
  const parsed = existing === void 0 ? { frontmatter: {}, body: "" } : parseFrontmatter(existing);
2572
3580
  await fs6.writeFile(
@@ -2588,13 +3596,13 @@ async function removeChildren(directoryPath) {
2588
3596
  if (entryName === MEMORY_LOCK_RELPATH) {
2589
3597
  continue;
2590
3598
  }
2591
- const entryPath = path15.join(directoryPath, entryName);
2592
- const stat6 = await fs6.stat(entryPath);
2593
- if (stat6.isDirectory()) {
3599
+ const entryPath = path18.join(directoryPath, entryName);
3600
+ const stat7 = await fs6.stat(entryPath);
3601
+ if (stat7.isDirectory()) {
2594
3602
  await removeDirectory2(entryPath);
2595
3603
  continue;
2596
3604
  }
2597
- if (stat6.isFile()) {
3605
+ if (stat7.isFile()) {
2598
3606
  await fs6.unlink(entryPath);
2599
3607
  }
2600
3608
  }
@@ -2605,7 +3613,7 @@ async function removeDirectory2(directoryPath) {
2605
3613
  }
2606
3614
  function assertPageRelPath(relPath) {
2607
3615
  const normalizedRelPath = assertSafeRelPath(relPath);
2608
- if (!normalizedRelPath.startsWith(`${MEMORY_PAGES_DIR_RELPATH}/`) || path15.posix.extname(normalizedRelPath).toLowerCase() !== ".md") {
3616
+ if (!normalizedRelPath.startsWith(`${MEMORY_PAGES_DIR_RELPATH}/`) || path18.posix.extname(normalizedRelPath).toLowerCase() !== ".md") {
2609
3617
  throw new Error(`Expected a markdown page path under "${MEMORY_PAGES_DIR_RELPATH}/".`);
2610
3618
  }
2611
3619
  return normalizedRelPath;
@@ -2623,12 +3631,12 @@ async function readMarkdownIfPresent(filePath) {
2623
3631
 
2624
3632
  // packages/memory/src/edit.ts
2625
3633
  async function editPage(root, relPath, opts) {
2626
- const pagePath = path16.join(root, relPath);
3634
+ const pagePath = path19.join(root, relPath);
2627
3635
  const original = await readIfPresent(pagePath);
2628
- const tempRoot = path16.join(root, ".tmp");
3636
+ const tempRoot = path19.join(root, ".tmp");
2629
3637
  await fs7.mkdir(tempRoot, { recursive: true });
2630
- const tempDir = await fs7.mkdtemp(path16.join(tempRoot, "poe-code-memory-edit-"));
2631
- const tempPath = path16.join(tempDir, path16.basename(relPath));
3638
+ const tempDir = await fs7.mkdtemp(path19.join(tempRoot, "poe-code-memory-edit-"));
3639
+ const tempPath = path19.join(tempDir, path19.basename(relPath));
2632
3640
  try {
2633
3641
  await fs7.writeFile(tempPath, original ?? "", "utf8");
2634
3642
  await opts.launchEditor(tempPath);
@@ -2662,7 +3670,7 @@ async function readIfPresent(filePath) {
2662
3670
 
2663
3671
  // packages/memory/src/audit.ts
2664
3672
  import * as fs8 from "node:fs/promises";
2665
- import path17 from "node:path";
3673
+ import path20 from "node:path";
2666
3674
  var DEFAULT_MIN_INFERRED_CONFIDENCE = 0.3;
2667
3675
  var DEFAULT_REJECT_UNTAGGED = false;
2668
3676
  var DEFAULT_UNTAGGED_BODY_THRESHOLD_CHARS = 200;
@@ -2717,10 +3725,10 @@ async function auditSourceRef(source, claimLineNumber, repoRoot, sourceCache) {
2717
3725
  if (isUrlLike(source.path)) {
2718
3726
  return void 0;
2719
3727
  }
2720
- if (path17.isAbsolute(source.path)) {
3728
+ if (path20.isAbsolute(source.path)) {
2721
3729
  return `Claim on line ${claimLineNumber} cites "${serializeSourceRef(source)}", but source paths must be repo-relative or URLs.`;
2722
3730
  }
2723
- const absPath = path17.resolve(repoRoot, source.path);
3731
+ const absPath = path20.resolve(repoRoot, source.path);
2724
3732
  if (!isWithinRoot(repoRoot, absPath)) {
2725
3733
  return `Claim on line ${claimLineNumber} cites "${serializeSourceRef(source)}", which resolves outside the repo root.`;
2726
3734
  }
@@ -2786,14 +3794,14 @@ function isUrlLike(value) {
2786
3794
  return /^[a-z][a-z\d+.-]*:\/\//i.test(value);
2787
3795
  }
2788
3796
  function isWithinRoot(root, absPath) {
2789
- const relative = path17.relative(root, absPath);
2790
- return relative === "" || !relative.startsWith("..") && !path17.isAbsolute(relative);
3797
+ const relative = path20.relative(root, absPath);
3798
+ return relative === "" || !relative.startsWith("..") && !path20.isAbsolute(relative);
2791
3799
  }
2792
3800
 
2793
3801
  // packages/memory/src/cache.ts
2794
3802
  import { createHash as createHash2 } from "node:crypto";
2795
3803
  import * as fs9 from "node:fs/promises";
2796
- import path18 from "node:path";
3804
+ import path21 from "node:path";
2797
3805
  function computeIngestKey(input) {
2798
3806
  const hash = createHash2("sha256");
2799
3807
  hash.update(input.sourceBytes);
@@ -2806,7 +3814,7 @@ function computeIngestKey(input) {
2806
3814
  return hash.digest("hex");
2807
3815
  }
2808
3816
  async function readCacheEntry(root, key) {
2809
- const cachePath = path18.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH, `${key}.json`);
3817
+ const cachePath = path21.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH, `${key}.json`);
2810
3818
  let raw;
2811
3819
  try {
2812
3820
  raw = await fs9.readFile(cachePath, "utf8");
@@ -2825,17 +3833,17 @@ async function readCacheEntry(root, key) {
2825
3833
  }
2826
3834
  }
2827
3835
  async function writeCacheEntry(root, entry) {
2828
- await fs9.mkdir(path18.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH), { recursive: true });
3836
+ await fs9.mkdir(path21.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH), { recursive: true });
2829
3837
  await fs9.writeFile(
2830
- path18.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH, `${entry.key}.json`),
3838
+ path21.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH, `${entry.key}.json`),
2831
3839
  `${JSON.stringify(entry)}
2832
3840
  `,
2833
3841
  "utf8"
2834
3842
  );
2835
3843
  }
2836
3844
  async function clearCache(root, opts = {}) {
2837
- const ingestDir = path18.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH);
2838
- const cacheDir = path18.join(root, MEMORY_CACHE_DIR_RELPATH);
3845
+ const ingestDir = path21.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH);
3846
+ const cacheDir = path21.join(root, MEMORY_CACHE_DIR_RELPATH);
2839
3847
  const fileNames = await readCacheFileNames(ingestDir);
2840
3848
  if (fileNames.length === 0) {
2841
3849
  if (opts.olderThanMs === void 0) {
@@ -2855,7 +3863,7 @@ async function clearCache(root, opts = {}) {
2855
3863
  if (entry === null || Date.parse(entry.ingestedAt) > cutoff) {
2856
3864
  continue;
2857
3865
  }
2858
- await fs9.rm(path18.join(ingestDir, fileName), { force: true });
3866
+ await fs9.rm(path21.join(ingestDir, fileName), { force: true });
2859
3867
  removed += 1;
2860
3868
  }
2861
3869
  await removeEmptyDirectory(ingestDir);
@@ -2911,7 +3919,7 @@ function expectStringArray(value, field) {
2911
3919
  }
2912
3920
  async function readCacheFileNames(ingestDir) {
2913
3921
  try {
2914
- return (await fs9.readdir(ingestDir)).filter((fileName) => path18.posix.extname(fileName).toLowerCase() === ".json").sort((left, right) => left.localeCompare(right));
3922
+ return (await fs9.readdir(ingestDir)).filter((fileName) => path21.posix.extname(fileName).toLowerCase() === ".json").sort((left, right) => left.localeCompare(right));
2915
3923
  } catch (error2) {
2916
3924
  if (isMissing3(error2)) {
2917
3925
  return [];
@@ -2931,42 +3939,1406 @@ async function removeEmptyDirectory(directoryPath) {
2931
3939
  }
2932
3940
  throw error2;
2933
3941
  }
2934
- }
2935
- function isMissing3(error2) {
2936
- return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
2937
- }
3942
+ }
3943
+ function isMissing3(error2) {
3944
+ return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
3945
+ }
3946
+
3947
+ // packages/memory/src/cache.cli.ts
3948
+ import parseDuration from "parse-duration";
3949
+ async function runMemoryCacheStatus() {
3950
+ console.log("cache status not implemented yet");
3951
+ }
3952
+ async function runMemoryCacheClear(input) {
3953
+ if (!input.yes) {
3954
+ throw new Error("Refusing to clear cache without --yes.");
3955
+ }
3956
+ const olderThanMs = parseOlderThan(input.olderThan);
3957
+ const result = await clearCache(input.root, olderThanMs === void 0 ? {} : { olderThanMs });
3958
+ console.log(`removed ${result.removed} cache ${result.removed === 1 ? "entry" : "entries"}`);
3959
+ return result;
3960
+ }
3961
+ function parseOlderThan(value) {
3962
+ if (value === void 0) {
3963
+ return void 0;
3964
+ }
3965
+ const duration = parseDuration(value);
3966
+ if (duration === null || Number.isNaN(duration) || duration < 0) {
3967
+ throw new Error(`Invalid duration for --older-than: "${value}".`);
3968
+ }
3969
+ return duration;
3970
+ }
3971
+
3972
+ // packages/memory/src/ingest.ts
3973
+ import * as fs11 from "node:fs/promises";
3974
+ import path31 from "node:path";
3975
+
3976
+ // packages/agent-spawn/src/register-factories.ts
3977
+ import { spawn as spawnChildProcess2 } from "node:child_process";
3978
+
3979
+ // packages/agent-harness-tools/src/paths.ts
3980
+ import path22 from "node:path";
3981
+
3982
+ // packages/agent-defs/src/agents/claude-code.ts
3983
+ var claudeCodeAgent = {
3984
+ id: "claude-code",
3985
+ name: "claude-code",
3986
+ label: "Claude Code",
3987
+ summary: "Configure Claude Code to route through Poe.",
3988
+ aliases: ["claude"],
3989
+ binaryName: "claude",
3990
+ configPath: "~/.claude/settings.json",
3991
+ branding: {
3992
+ colors: {
3993
+ dark: "#C15F3C",
3994
+ light: "#C15F3C"
3995
+ }
3996
+ }
3997
+ };
3998
+
3999
+ // packages/agent-defs/src/agents/claude-desktop.ts
4000
+ var claudeDesktopAgent = {
4001
+ id: "claude-desktop",
4002
+ name: "claude-desktop",
4003
+ label: "Claude Desktop",
4004
+ summary: "Anthropic's official desktop application for Claude",
4005
+ configPath: "~/.claude/settings.json",
4006
+ branding: {
4007
+ colors: {
4008
+ dark: "#D97757",
4009
+ light: "#D97757"
4010
+ }
4011
+ }
4012
+ };
4013
+
4014
+ // packages/agent-defs/src/agents/codex.ts
4015
+ var codexAgent = {
4016
+ id: "codex",
4017
+ name: "codex",
4018
+ label: "Codex",
4019
+ summary: "Configure Codex to use Poe as the model provider.",
4020
+ binaryName: "codex",
4021
+ configPath: "~/.codex/config.toml",
4022
+ branding: {
4023
+ colors: {
4024
+ dark: "#D5D9DF",
4025
+ light: "#7A7F86"
4026
+ }
4027
+ }
4028
+ };
4029
+
4030
+ // packages/agent-defs/src/agents/opencode.ts
4031
+ var openCodeAgent = {
4032
+ id: "opencode",
4033
+ name: "opencode",
4034
+ label: "OpenCode CLI",
4035
+ summary: "Configure OpenCode CLI to use the Poe API.",
4036
+ binaryName: "opencode",
4037
+ configPath: "~/.config/opencode/config.json",
4038
+ branding: {
4039
+ colors: {
4040
+ dark: "#4A4F55",
4041
+ light: "#2F3338"
4042
+ }
4043
+ }
4044
+ };
4045
+
4046
+ // packages/agent-defs/src/agents/kimi.ts
4047
+ var kimiAgent = {
4048
+ id: "kimi",
4049
+ name: "kimi",
4050
+ label: "Kimi",
4051
+ summary: "Configure Kimi CLI to use Poe API",
4052
+ aliases: ["kimi-cli"],
4053
+ binaryName: "kimi",
4054
+ configPath: "~/.kimi/config.toml",
4055
+ branding: {
4056
+ colors: {
4057
+ dark: "#7B68EE",
4058
+ light: "#6A5ACD"
4059
+ }
4060
+ }
4061
+ };
4062
+
4063
+ // packages/agent-defs/src/agents/goose.ts
4064
+ var gooseAgent = {
4065
+ id: "goose",
4066
+ name: "goose",
4067
+ label: "Goose",
4068
+ summary: "Block's open-source AI agent with ACP support.",
4069
+ binaryName: "goose",
4070
+ configPath: "~/.config/goose/config.yaml",
4071
+ branding: {
4072
+ colors: {
4073
+ dark: "#FF6B35",
4074
+ light: "#E85D26"
4075
+ }
4076
+ }
4077
+ };
4078
+
4079
+ // packages/agent-defs/src/agents/poe-agent.ts
4080
+ var poeAgentAgent = {
4081
+ id: "poe-agent",
4082
+ name: "poe-agent",
4083
+ label: "Poe Agent",
4084
+ summary: "Run one-shot prompts with the built-in Poe agent runtime.",
4085
+ configPath: "~/.poe-code/config.json",
4086
+ branding: {
4087
+ colors: {
4088
+ dark: "#A465F7",
4089
+ light: "#7A3FD3"
4090
+ }
4091
+ }
4092
+ };
4093
+
4094
+ // packages/agent-defs/src/registry.ts
4095
+ var allAgents = [
4096
+ claudeCodeAgent,
4097
+ claudeDesktopAgent,
4098
+ codexAgent,
4099
+ openCodeAgent,
4100
+ kimiAgent,
4101
+ gooseAgent,
4102
+ poeAgentAgent
4103
+ ];
4104
+ var lookup = /* @__PURE__ */ new Map();
4105
+ for (const agent of allAgents) {
4106
+ const values = [agent.id, agent.name, ...agent.aliases ?? []];
4107
+ for (const value of values) {
4108
+ const normalized = value.toLowerCase();
4109
+ if (!lookup.has(normalized)) {
4110
+ lookup.set(normalized, agent.id);
4111
+ }
4112
+ }
4113
+ }
4114
+ function resolveAgentId(input) {
4115
+ if (!input) {
4116
+ return void 0;
4117
+ }
4118
+ return lookup.get(input.toLowerCase());
4119
+ }
4120
+
4121
+ // packages/agent-harness-tools/src/select-agent.ts
4122
+ var loopAgents = allAgents.filter(
4123
+ (agent) => agent.binaryName !== void 0 || agent.id === "poe-agent"
4124
+ );
4125
+ var supportedAgents = loopAgents.map((agent) => agent.id).join(", ");
4126
+
4127
+ // packages/agent-harness-tools/src/run-logs.ts
4128
+ import path23 from "node:path";
4129
+
4130
+ // packages/agent-harness-tools/src/log-stream.ts
4131
+ import nodeFs2 from "node:fs";
4132
+ var JOB_DIR = "/tmp/poe-jobs";
4133
+ var POLL_INTERVAL_MS = 250;
4134
+ function wrapForLogTee(argv, jobId) {
4135
+ if (argv.length === 0) {
4136
+ throw new Error("wrapForLogTee requires argv to contain at least one argument");
4137
+ }
4138
+ const command = argv.map(shellQuote).join(" ");
4139
+ const logFile = shellQuote(jobLogPath(jobId));
4140
+ const exitFile = shellQuote(jobExitPath(jobId));
4141
+ const exitTmpFile = shellQuote(`${jobExitPath(jobId)}.tmp`);
4142
+ const script = [
4143
+ `mkdir -p ${shellQuote(JOB_DIR)}`,
4144
+ `({ (${command}); echo $? > ${exitTmpFile}; } 2>&1 | tee ${logFile}; mv ${exitTmpFile} ${exitFile})`
4145
+ ].join(" && ");
4146
+ return ["sh", "-c", script];
4147
+ }
4148
+ async function waitForExit(env, jobId, opts = {}) {
4149
+ const fs14 = env.fs ?? nodeFs2;
4150
+ const file = jobExitPath(jobId);
4151
+ while (true) {
4152
+ throwIfAborted2(opts.signal);
4153
+ const contents = await readTextFileIfExists(fs14, file);
4154
+ if (contents !== null) {
4155
+ const text4 = contents.trim();
4156
+ const exitCode = Number(text4);
4157
+ if (text4.length === 0 || !Number.isInteger(exitCode)) {
4158
+ throw new Error(`Invalid exit code in ${file}: ${contents}`);
4159
+ }
4160
+ return { exitCode };
4161
+ }
4162
+ await sleep3(POLL_INTERVAL_MS, opts.signal);
4163
+ }
4164
+ }
4165
+ function jobLogPath(jobId) {
4166
+ return `${JOB_DIR}/${jobId}.log`;
4167
+ }
4168
+ function jobExitPath(jobId) {
4169
+ return `${JOB_DIR}/${jobId}.exit`;
4170
+ }
4171
+ async function readTextFileIfExists(fs14, file) {
4172
+ const contents = await readFileIfExists2(fs14, file);
4173
+ return contents?.toString("utf8") ?? null;
4174
+ }
4175
+ async function readFileIfExists2(fs14, file) {
4176
+ try {
4177
+ const contents = await fs14.promises.readFile(file);
4178
+ return Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
4179
+ } catch (error2) {
4180
+ if (isNodeError(error2) && error2.code === "ENOENT") {
4181
+ return null;
4182
+ }
4183
+ throw error2;
4184
+ }
4185
+ }
4186
+ function sleep3(ms, signal) {
4187
+ return new Promise((resolve2, reject) => {
4188
+ let timer = null;
4189
+ const abort = () => {
4190
+ if (timer !== null) {
4191
+ clearTimeout(timer);
4192
+ }
4193
+ reject(new Error("waitForExit aborted."));
4194
+ };
4195
+ if (signal?.aborted) {
4196
+ abort();
4197
+ return;
4198
+ }
4199
+ timer = setTimeout(() => {
4200
+ signal?.removeEventListener("abort", abort);
4201
+ resolve2();
4202
+ }, ms);
4203
+ signal?.addEventListener("abort", abort, { once: true });
4204
+ });
4205
+ }
4206
+ function throwIfAborted2(signal) {
4207
+ if (signal?.aborted) {
4208
+ throw new Error("waitForExit aborted.");
4209
+ }
4210
+ }
4211
+ function shellQuote(value) {
4212
+ return `'${value.replaceAll("'", "'\\''")}'`;
4213
+ }
4214
+ function isNodeError(error2) {
4215
+ return error2 instanceof Error && "code" in error2;
4216
+ }
4217
+
4218
+ // packages/agent-harness-tools/src/run-poe-command.ts
4219
+ import { randomBytes } from "node:crypto";
4220
+ var ULID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
4221
+ async function runPoeCommand(opts) {
4222
+ const jobId = createUlid();
4223
+ const execution = opts.openSpec.execution;
4224
+ const wrapCommand = execution?.wrapForLogTee !== false;
4225
+ const pendingJob = opts.state.jobs.put({
4226
+ id: jobId,
4227
+ env_id: "",
4228
+ env_kind: opts.factory.type,
4229
+ tool: opts.openSpec.jobLabel.tool,
4230
+ argv: opts.openSpec.jobLabel.argv,
4231
+ cwd: opts.openSpec.cwd,
4232
+ started_at: "",
4233
+ status: "pending"
4234
+ });
4235
+ const opened = opts.factory.open(opts.openSpec);
4236
+ const env = isPromiseLike(opened) ? await opened : opened;
4237
+ let shouldClose = true;
4238
+ try {
4239
+ const upload = env.uploadWorkspace();
4240
+ const argv = wrapCommand ? wrapForLogTee(opts.openSpec.jobLabel.argv, jobId) : opts.openSpec.jobLabel.argv;
4241
+ const handle = execution?.tty ? env.shell() : env.exec({
4242
+ command: argv[0],
4243
+ args: argv.slice(1),
4244
+ cwd: opts.openSpec.cwd,
4245
+ env: execution && "env" in execution ? execution.env : opts.openSpec.env,
4246
+ stdin: execution?.stdin ?? "inherit",
4247
+ stdout: execution?.stdout ?? "pipe",
4248
+ stderr: execution?.stderr ?? "pipe",
4249
+ signal: opts.signal
4250
+ });
4251
+ if (execution?.input !== void 0) {
4252
+ handle.stdin?.setDefaultEncoding("utf8");
4253
+ handle.stdin?.end(execution.input);
4254
+ }
4255
+ const runningJob = Promise.all([pendingJob, upload]).then(
4256
+ () => opts.state.jobs.update(jobId, {
4257
+ status: "running",
4258
+ env_id: env.id,
4259
+ started_at: (/* @__PURE__ */ new Date()).toISOString()
4260
+ })
4261
+ );
4262
+ if (opts.detach) {
4263
+ await runningJob;
4264
+ shouldClose = false;
4265
+ return { kind: "detached", jobId, envId: env.id };
4266
+ }
4267
+ const result = await runSync({
4268
+ env,
4269
+ handle,
4270
+ jobId,
4271
+ openSpec: opts.openSpec,
4272
+ signal: opts.signal,
4273
+ wrapCommand
4274
+ });
4275
+ await runningJob;
4276
+ shouldClose = false;
4277
+ await opts.state.jobs.update(jobId, {
4278
+ status: "exited",
4279
+ exit_code: result.exitCode,
4280
+ exited_at: (/* @__PURE__ */ new Date()).toISOString()
4281
+ });
4282
+ return {
4283
+ kind: "sync",
4284
+ exitCode: result.exitCode,
4285
+ download: result.download,
4286
+ ...result.stdout !== void 0 ? { stdout: result.stdout } : {},
4287
+ ...result.stderr !== void 0 ? { stderr: result.stderr } : {}
4288
+ };
4289
+ } finally {
4290
+ if (shouldClose) {
4291
+ await env.close();
4292
+ }
4293
+ }
4294
+ }
4295
+ async function runSync(opts) {
4296
+ const execution = opts.openSpec.execution;
4297
+ const capture = execution?.captureOutput === true;
4298
+ const abort = createAbortSync(opts.signal, opts.handle, execution?.activityTimeoutMs);
4299
+ const streamState = capture ? captureRunStreams(opts.handle, execution, abort.resetActivityTimer) : pipeRunStreams(opts.handle);
4300
+ abort.resetActivityTimer();
4301
+ try {
4302
+ const { exitCode } = opts.wrapCommand ? await abort.waitForExit(opts.env, opts.jobId) : await abort.waitForHandle();
4303
+ const download = await opts.env.downloadWorkspace({
4304
+ conflictPolicy: opts.openSpec.runner?.download_conflict ?? "refuse"
4305
+ });
4306
+ await opts.env.close();
4307
+ return {
4308
+ exitCode,
4309
+ download,
4310
+ ...capture ? { stdout: streamState.stdout(), stderr: streamState.stderr() } : {}
4311
+ };
4312
+ } finally {
4313
+ abort.dispose();
4314
+ streamState.dispose();
4315
+ }
4316
+ }
4317
+ function pipeRunStreams(handle) {
4318
+ handle.stdout?.pipe(process.stdout, { end: false });
4319
+ handle.stderr?.pipe(process.stderr, { end: false });
4320
+ return {
4321
+ stdout: () => "",
4322
+ stderr: () => "",
4323
+ dispose() {
4324
+ handle.stdout?.unpipe(process.stdout);
4325
+ handle.stderr?.unpipe(process.stderr);
4326
+ }
4327
+ };
4328
+ }
4329
+ function captureRunStreams(handle, execution, onActivity) {
4330
+ let stdout = "";
4331
+ let stderr = "";
4332
+ const listeners = [];
4333
+ const bind = (stream, onChunk) => {
4334
+ if (!stream) return;
4335
+ stream.setEncoding("utf8");
4336
+ const listener = (chunk) => {
4337
+ onActivity();
4338
+ onChunk(chunk.toString());
4339
+ };
4340
+ stream.on("data", listener);
4341
+ listeners.push(() => {
4342
+ stream.off("data", listener);
4343
+ });
4344
+ };
4345
+ bind(handle.stdout, (chunk) => {
4346
+ stdout += chunk;
4347
+ execution?.onStdout?.(chunk);
4348
+ });
4349
+ bind(handle.stderr, (chunk) => {
4350
+ stderr += chunk;
4351
+ execution?.onStderr?.(chunk);
4352
+ });
4353
+ return {
4354
+ stdout: () => stdout,
4355
+ stderr: () => stderr,
4356
+ dispose() {
4357
+ for (const remove2 of listeners) {
4358
+ remove2();
4359
+ }
4360
+ }
4361
+ };
4362
+ }
4363
+ function createAbortSync(signal, handle, activityTimeoutMs) {
4364
+ let activityTimer;
4365
+ let timedOut = false;
4366
+ const resetActivityTimer = activityTimeoutMs ? () => {
4367
+ if (activityTimer) clearTimeout(activityTimer);
4368
+ activityTimer = setTimeout(() => {
4369
+ timedOut = true;
4370
+ handle.kill("SIGTERM");
4371
+ notifyAbort?.();
4372
+ }, activityTimeoutMs);
4373
+ } : () => {
4374
+ };
4375
+ let notifyAbort;
4376
+ if (signal === void 0) {
4377
+ return {
4378
+ waitForExit: (env, jobId) => waitForExit(toLogStreamEnv(env), jobId),
4379
+ waitForHandle: async () => {
4380
+ const result = await handle.result;
4381
+ if (timedOut) {
4382
+ throw createActivityTimeoutError(activityTimeoutMs);
4383
+ }
4384
+ return result;
4385
+ },
4386
+ resetActivityTimer,
4387
+ dispose() {
4388
+ if (activityTimer) clearTimeout(activityTimer);
4389
+ }
4390
+ };
4391
+ }
4392
+ const exitWaitController = new AbortController();
4393
+ let aborted = signal.aborted;
4394
+ const abortedPromise = new Promise((resolve2) => {
4395
+ notifyAbort = resolve2;
4396
+ });
4397
+ const kill = () => {
4398
+ aborted = true;
4399
+ handle.kill("SIGTERM");
4400
+ notifyAbort?.();
4401
+ };
4402
+ if (signal.aborted) {
4403
+ kill();
4404
+ } else {
4405
+ signal.addEventListener("abort", kill, { once: true });
4406
+ }
4407
+ return {
4408
+ async waitForExit(env, jobId) {
4409
+ if (aborted) {
4410
+ return handle.result;
4411
+ }
4412
+ const exit = waitForExit(toLogStreamEnv(env), jobId, {
4413
+ signal: exitWaitController.signal
4414
+ }).then(
4415
+ (value) => ({ kind: "exit", value }),
4416
+ (error2) => ({ kind: "error", error: error2 })
4417
+ );
4418
+ const result = await Promise.race([
4419
+ exit,
4420
+ abortedPromise.then(() => ({ kind: "abort" }))
4421
+ ]);
4422
+ if (result.kind === "exit") {
4423
+ return result.value;
4424
+ }
4425
+ if (result.kind === "error") {
4426
+ throw result.error;
4427
+ }
4428
+ exitWaitController.abort();
4429
+ return handle.result;
4430
+ },
4431
+ async waitForHandle() {
4432
+ const result = await Promise.race([
4433
+ handle.result.then((value) => ({ kind: "exit", value })),
4434
+ abortedPromise.then(() => ({ kind: "abort" }))
4435
+ ]);
4436
+ if (result.kind === "exit") {
4437
+ if (aborted) {
4438
+ throw createAbortError2();
4439
+ }
4440
+ if (timedOut) {
4441
+ throw createActivityTimeoutError(activityTimeoutMs);
4442
+ }
4443
+ return result.value;
4444
+ }
4445
+ if (timedOut) {
4446
+ throw createActivityTimeoutError(activityTimeoutMs);
4447
+ }
4448
+ throw createAbortError2();
4449
+ },
4450
+ resetActivityTimer,
4451
+ dispose() {
4452
+ if (activityTimer) clearTimeout(activityTimer);
4453
+ exitWaitController.abort();
4454
+ signal.removeEventListener("abort", kill);
4455
+ }
4456
+ };
4457
+ }
4458
+ function toLogStreamEnv(env) {
4459
+ const candidate = env;
4460
+ return candidate.fs === void 0 ? {} : { fs: candidate.fs };
4461
+ }
4462
+ function isPromiseLike(value) {
4463
+ return typeof value.then === "function";
4464
+ }
4465
+ function createAbortError2() {
4466
+ const error2 = new Error("Agent spawn aborted");
4467
+ error2.name = "AbortError";
4468
+ return error2;
4469
+ }
4470
+ function createActivityTimeoutError(timeoutMs) {
4471
+ const error2 = new Error(`Agent spawn timed out after ${timeoutMs / 1e3}s of inactivity`);
4472
+ error2.name = "ActivityTimeoutError";
4473
+ return error2;
4474
+ }
4475
+ function createUlid() {
4476
+ const time = BigInt(Date.now());
4477
+ const random = randomBytes(10);
4478
+ let randomValue = 0n;
4479
+ for (const byte of random) {
4480
+ randomValue = randomValue << 8n | BigInt(byte);
4481
+ }
4482
+ return encodeBase32(time, 10) + encodeBase32(randomValue, 16);
4483
+ }
4484
+ function encodeBase32(value, length) {
4485
+ const chars = Array.from({ length }, () => "0");
4486
+ let remaining = value;
4487
+ for (let index = length - 1; index >= 0; index -= 1) {
4488
+ chars[index] = ULID_ALPHABET[Number(remaining & 31n)];
4489
+ remaining >>= 5n;
4490
+ }
4491
+ return chars.join("");
4492
+ }
4493
+
4494
+ // packages/agent-harness-tools/src/poe-command-execution.ts
4495
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
4496
+ import os3 from "node:os";
4497
+
4498
+ // packages/agent-harness-tools/src/execution-env.ts
4499
+ var executionEnvFactories = /* @__PURE__ */ new Map();
4500
+ function registerExecutionEnvFactory(factory) {
4501
+ executionEnvFactories.set(factory.type, factory);
4502
+ }
4503
+ function selectExecutionEnv(runtime) {
4504
+ const factory = executionEnvFactories.get(runtime.type);
4505
+ if (factory === void 0) {
4506
+ throw new Error(
4507
+ `No execution environment factory registered for runtime type "${runtime.type}".`
4508
+ );
4509
+ }
4510
+ return factory;
4511
+ }
4512
+
4513
+ // packages/agent-harness-tools/src/poe-command-execution.ts
4514
+ function resolvePoeCommandExecution(input) {
4515
+ const homeDir = input.context?.homeDir ?? os3.homedir();
4516
+ const config = applyRuntimeOverrides(loadRuntimeConfig(input.cwd, homeDir), input.runtime, input.cwd);
4517
+ const resolved = resolveRuntime({ cwd: input.cwd, config });
4518
+ const factory = selectExecutionEnv(resolved.runtime);
4519
+ const state = input.context?.state ?? loadState(homeDir);
4520
+ return {
4521
+ factory,
4522
+ detach: factory.supportsDetach === true && config.runner.detach,
4523
+ state,
4524
+ openSpec: {
4525
+ cwd: input.cwd,
4526
+ runtime: resolved.runtime,
4527
+ runner: config.runner,
4528
+ state,
4529
+ env: input.env,
4530
+ uploadIgnoreFiles: config.runner.workspace?.exclude ?? [],
4531
+ jobLabel: {
4532
+ tool: input.tool,
4533
+ argv: input.argv
4534
+ },
4535
+ ...input.openSpec
4536
+ }
4537
+ };
4538
+ }
4539
+ function applyRuntimeOverrides(config, overrides, cwd = process.cwd()) {
4540
+ if (!overrides) {
4541
+ return config;
4542
+ }
4543
+ const runtime = parseRuntime({
4544
+ ...config.runtime,
4545
+ ...overrides.runtime !== void 0 ? { type: overrides.runtime } : {},
4546
+ ...overrides.runtimeImage !== void 0 ? { image: overrides.runtimeImage } : {},
4547
+ ...overrides.runtimeTemplate !== void 0 ? { template_id: overrides.runtimeTemplate } : {},
4548
+ ...overrides.mountPoeCode === true ? { mounts: [...config.runtime.mounts, createPoeCodeMount(cwd)] } : {}
4549
+ });
4550
+ return {
4551
+ runtime,
4552
+ runner: {
4553
+ ...config.runner,
4554
+ ...overrides.detach === true ? { detach: true } : {}
4555
+ }
4556
+ };
4557
+ }
4558
+ function createPoeCodeMount(cwd) {
4559
+ return {
4560
+ source: cwd,
4561
+ target: "/usr/local/lib/poe-code",
4562
+ readonly: true
4563
+ };
4564
+ }
4565
+ function loadRuntimeConfig(cwd, homeDir) {
4566
+ const document = deepMergeDocuments(
4567
+ readConfigDocument(resolveConfigPath(homeDir)),
4568
+ readConfigDocument(resolveProjectConfigPath(cwd))
4569
+ );
4570
+ const runtimeScope = resolveScope(runtimeConfigScope.schema, document.runtime, process.env);
4571
+ return {
4572
+ runtime: parseRuntime(runtimeScope),
4573
+ runner: runtimeScope.runner
4574
+ };
4575
+ }
4576
+ function readConfigDocument(filePath) {
4577
+ if (!existsSync2(filePath)) {
4578
+ return {};
4579
+ }
4580
+ return JSON.parse(readFileSync(filePath, "utf8"));
4581
+ }
4582
+ function loadState(homeDir) {
4583
+ if (process.env.VITEST === "true") {
4584
+ return createMemoryStateManager();
4585
+ }
4586
+ return createStateManager(homeDir);
4587
+ }
4588
+ function createMemoryStateManager() {
4589
+ const jobs = /* @__PURE__ */ new Map();
4590
+ return {
4591
+ templates: {
4592
+ async get() {
4593
+ return null;
4594
+ },
4595
+ async put() {
4596
+ },
4597
+ async remove() {
4598
+ },
4599
+ async list() {
4600
+ return [];
4601
+ }
4602
+ },
4603
+ jobs: {
4604
+ async get(id) {
4605
+ return jobs.get(id) ?? null;
4606
+ },
4607
+ async put(entry) {
4608
+ jobs.set(entry.id, entry);
4609
+ },
4610
+ async update(id, patch) {
4611
+ const current = jobs.get(id);
4612
+ if (!current) {
4613
+ return null;
4614
+ }
4615
+ const updated = { ...current, ...patch, id };
4616
+ jobs.set(id, updated);
4617
+ return updated;
4618
+ },
4619
+ async list(filter) {
4620
+ const entries = Array.from(jobs.values());
4621
+ if (!filter) {
4622
+ return entries;
4623
+ }
4624
+ return entries.filter(
4625
+ (entry) => Object.entries(filter).every(([key, value]) => entry[key] === value)
4626
+ );
4627
+ },
4628
+ async remove(id) {
4629
+ jobs.delete(id);
4630
+ }
4631
+ }
4632
+ };
4633
+ }
4634
+
4635
+ // packages/agent-harness-tools/src/workspace-transfer.ts
4636
+ import { createHash as createHash3 } from "node:crypto";
4637
+ import { promises as nodeFs3 } from "node:fs";
4638
+ import path24 from "node:path";
4639
+
4640
+ // packages/process-runner/src/docker/context.ts
4641
+ import { execSync } from "node:child_process";
4642
+ function detectContext() {
4643
+ try {
4644
+ const output = execSync("colima list --json", {
4645
+ encoding: "utf-8",
4646
+ stdio: ["pipe", "pipe", "ignore"]
4647
+ });
4648
+ const lines = output.trim().split("\n").filter(Boolean);
4649
+ for (const line of lines) {
4650
+ const profile = JSON.parse(line);
4651
+ if (profile.status === "Running" && profile.runtime === "docker") {
4652
+ const name = profile.name ?? profile.profile;
4653
+ if (!name) {
4654
+ continue;
4655
+ }
4656
+ return name === "default" ? "colima" : `colima-${name}`;
4657
+ }
4658
+ }
4659
+ } catch {
4660
+ return null;
4661
+ }
4662
+ return null;
4663
+ }
4664
+ function buildContextArgs(engine, context) {
4665
+ if (engine === "docker" && context) {
4666
+ return ["--context", context];
4667
+ }
4668
+ return [];
4669
+ }
4670
+
4671
+ // packages/process-runner/src/docker/engine.ts
4672
+ import { execSync as execSync2 } from "node:child_process";
4673
+ function detectEngine() {
4674
+ if (isEngineAvailable("docker")) {
4675
+ return "docker";
4676
+ }
4677
+ if (isEngineAvailable("podman")) {
4678
+ return "podman";
4679
+ }
4680
+ throw new Error(
4681
+ "No container engine found. Please install Docker or Podman:\n - Docker Desktop: https://www.docker.com/products/docker-desktop\n - Colima (macOS): brew install colima && colima start\n - Podman: https://podman.io/docs/installation"
4682
+ );
4683
+ }
4684
+ function isEngineAvailable(engine) {
4685
+ try {
4686
+ execSync2(`${engine} --version`, {
4687
+ stdio: "ignore"
4688
+ });
4689
+ return true;
4690
+ } catch {
4691
+ return false;
4692
+ }
4693
+ }
4694
+
4695
+ // packages/process-runner/src/docker/docker-runner.ts
4696
+ import * as childProcess from "node:child_process";
4697
+ import { randomBytes as randomBytes2 } from "node:crypto";
4698
+
4699
+ // packages/process-runner/src/docker/args.ts
4700
+ import path25 from "node:path";
4701
+ function buildDockerRunArgs(input) {
4702
+ const args = [input.engine];
4703
+ if (input.engine === "docker" && input.context) {
4704
+ args.push("--context", input.context);
4705
+ }
4706
+ args.push("run");
4707
+ if (input.rm) {
4708
+ args.push("--rm");
4709
+ }
4710
+ if (input.detached) {
4711
+ args.push("-d");
4712
+ }
4713
+ if (input.interactive) {
4714
+ args.push("-i");
4715
+ }
4716
+ if (input.tty) {
4717
+ args.push("-t");
4718
+ }
4719
+ args.push("--name", input.containerName);
4720
+ if (input.cwd !== void 0) {
4721
+ args.push("-w", input.cwd);
4722
+ }
4723
+ for (const [key, value] of Object.entries(input.env ?? {})) {
4724
+ args.push("-e", `${key}=${value}`);
4725
+ }
4726
+ for (const mount of input.mounts) {
4727
+ const volume = `${path25.resolve(mount.source)}:${mount.target}${mount.readonly ? ":ro" : ""}`;
4728
+ args.push("-v", volume);
4729
+ }
4730
+ for (const port of input.ports) {
4731
+ const mapping = `${port.host}:${port.container}${port.protocol === void 0 || port.protocol === "tcp" ? "" : `/${port.protocol}`}`;
4732
+ args.push("-p", mapping);
4733
+ }
4734
+ if (input.network !== void 0) {
4735
+ args.push("--network", input.network);
4736
+ }
4737
+ args.push(...input.extraArgs, input.image, input.command, ...input.args);
4738
+ return args;
4739
+ }
4740
+
4741
+ // packages/process-runner/src/docker/docker-execution-env.ts
4742
+ import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
4743
+ import { mkdtempSync, rmSync } from "node:fs";
4744
+ import { readFile as readFile10 } from "node:fs/promises";
4745
+ import { tmpdir } from "node:os";
4746
+ import path26 from "node:path";
4747
+
4748
+ // packages/process-runner/src/host/host-runner.ts
4749
+ import { spawn as spawnChildProcess } from "node:child_process";
4750
+ function createHostRunner(options = {}) {
4751
+ const detached = options.detached === true;
4752
+ return {
4753
+ name: "host",
4754
+ exec(spec) {
4755
+ const stdinMode = spec.stdin ?? "ignore";
4756
+ const stdoutMode = spec.stdout ?? "pipe";
4757
+ const stderrMode = spec.stderr ?? "pipe";
4758
+ const stdio = stdinMode === "inherit" && stdoutMode === "inherit" && stderrMode === "inherit" ? "inherit" : [stdinMode, stdoutMode, stderrMode];
4759
+ const child = spawnChildProcess(spec.command, spec.args ?? [], {
4760
+ cwd: spec.cwd,
4761
+ env: spec.env,
4762
+ stdio,
4763
+ ...detached ? { detached: true } : {}
4764
+ });
4765
+ if (detached) {
4766
+ child.unref();
4767
+ }
4768
+ const kill = (signal) => {
4769
+ if (detached && process.platform !== "win32" && child.pid !== void 0) {
4770
+ process.kill(-child.pid, signal);
4771
+ return;
4772
+ }
4773
+ child.kill(signal);
4774
+ };
4775
+ let settled = false;
4776
+ let resolveResult = null;
4777
+ const result = new Promise((resolve2) => {
4778
+ resolveResult = resolve2;
4779
+ });
4780
+ const cleanupAbort = bindAbortSignal(spec.signal, () => {
4781
+ kill("SIGTERM");
4782
+ });
4783
+ child.once("close", (code) => {
4784
+ if (settled) return;
4785
+ settled = true;
4786
+ cleanupAbort();
4787
+ resolveResult?.({ exitCode: code ?? 1 });
4788
+ });
4789
+ child.once("error", () => {
4790
+ if (settled) return;
4791
+ settled = true;
4792
+ cleanupAbort();
4793
+ resolveResult?.({ exitCode: 1 });
4794
+ });
4795
+ return {
4796
+ pid: child.pid ?? null,
4797
+ stdin: child.stdin,
4798
+ stdout: child.stdout,
4799
+ stderr: child.stderr,
4800
+ result,
4801
+ kill
4802
+ };
4803
+ }
4804
+ };
4805
+ }
4806
+ function bindAbortSignal(signal, onAbort) {
4807
+ if (signal === void 0) {
4808
+ return () => {
4809
+ };
4810
+ }
4811
+ if (signal.aborted) {
4812
+ onAbort();
4813
+ return () => {
4814
+ };
4815
+ }
4816
+ signal.addEventListener("abort", onAbort, { once: true });
4817
+ return () => {
4818
+ signal.removeEventListener("abort", onAbort);
4819
+ };
4820
+ }
4821
+
4822
+ // packages/process-runner/src/docker/docker-execution-env.ts
4823
+ var containerCommand = ["sh", "-c", "while :; do sleep 3600; done"];
4824
+ var dockerExecutionEnvFactory = {
4825
+ type: "docker",
4826
+ supportsDetach: true,
4827
+ async open(spec) {
4828
+ const runtime = parseDockerRuntime(spec.runtime);
4829
+ const runner = spec.hostRunner ?? createHostRunner();
4830
+ const engine = runtime.engine ?? detectEngine();
4831
+ const context = detectContext();
4832
+ const image = await resolveImage({
4833
+ spec,
4834
+ runtime,
4835
+ runner,
4836
+ engine,
4837
+ context
4838
+ });
4839
+ const containerName = createContainerName();
4840
+ const runArgs = buildDockerRunArgs({
4841
+ engine,
4842
+ context,
4843
+ image,
4844
+ command: containerCommand[0],
4845
+ args: containerCommand.slice(1),
4846
+ cwd: void 0,
4847
+ env: void 0,
4848
+ mounts: runtime.mounts ?? [],
4849
+ ports: [],
4850
+ network: runtime.network,
4851
+ containerName,
4852
+ detached: true,
4853
+ interactive: true,
4854
+ tty: false,
4855
+ rm: false,
4856
+ extraArgs: runtime.extra_args ?? []
4857
+ });
4858
+ const [command, ...args] = runArgs;
4859
+ const id = (await runAndRead(runner, { command, args, stdout: "pipe", stderr: "pipe" })).trim();
4860
+ return createDockerEnv({
4861
+ id,
4862
+ spec,
4863
+ runner,
4864
+ engine,
4865
+ context
4866
+ });
4867
+ },
4868
+ async attach(envId) {
4869
+ const engine = detectEngine();
4870
+ return createDockerEnv({
4871
+ id: envId,
4872
+ spec: createAttachedSpec(),
4873
+ runner: createHostRunner(),
4874
+ engine,
4875
+ context: detectContext()
4876
+ });
4877
+ }
4878
+ };
4879
+ function createDockerEnv(input) {
4880
+ const containerRef = input.id;
4881
+ return {
4882
+ id: containerRef,
4883
+ job: null,
4884
+ async uploadWorkspace() {
4885
+ const tempDir = mkdtempSync(path26.join(tmpdir(), "poe-docker-upload-"));
4886
+ const archivePath = path26.join(tempDir, "workspace.tar");
4887
+ try {
4888
+ const excludeArgs = input.spec.uploadIgnoreFiles.flatMap((ignored) => [
4889
+ "--exclude",
4890
+ ignored
4891
+ ]);
4892
+ const tarArgs = [...excludeArgs, "-cf", archivePath, "-C", input.spec.cwd, "."];
4893
+ await runOrThrow(input.runner, {
4894
+ command: "tar",
4895
+ args: tarArgs,
4896
+ stdout: "pipe",
4897
+ stderr: "pipe"
4898
+ });
4899
+ await runOrThrow(input.runner, {
4900
+ command: input.engine,
4901
+ args: [
4902
+ ...buildContextArgs(input.engine, input.context),
4903
+ "cp",
4904
+ archivePath,
4905
+ `${containerRef}:/tmp/poe-workspace-upload.tar`
4906
+ ],
4907
+ stdout: "pipe",
4908
+ stderr: "pipe"
4909
+ });
4910
+ await runOrThrow(input.runner, {
4911
+ command: input.engine,
4912
+ args: [
4913
+ ...buildContextArgs(input.engine, input.context),
4914
+ "exec",
4915
+ containerRef,
4916
+ "sh",
4917
+ "-c",
4918
+ `mkdir -p ${shellQuote2(input.spec.cwd)} && tar -xf /tmp/poe-workspace-upload.tar -C ${shellQuote2(input.spec.cwd)}`
4919
+ ],
4920
+ stdout: "pipe",
4921
+ stderr: "pipe"
4922
+ });
4923
+ return { files: 0, bytes: 0, skipped: [] };
4924
+ } finally {
4925
+ rmSync(tempDir, { recursive: true, force: true });
4926
+ }
4927
+ },
4928
+ async downloadWorkspace(opts) {
4929
+ const tempDir = mkdtempSync(path26.join(tmpdir(), "poe-docker-download-"));
4930
+ const archivePath = path26.join(tempDir, "workspace.tar");
4931
+ try {
4932
+ await runOrThrow(input.runner, {
4933
+ command: input.engine,
4934
+ args: [
4935
+ ...buildContextArgs(input.engine, input.context),
4936
+ "exec",
4937
+ containerRef,
4938
+ "sh",
4939
+ "-c",
4940
+ `tar -cf /tmp/poe-workspace-download.tar -C ${shellQuote2(input.spec.cwd)} .`
4941
+ ],
4942
+ stdout: "pipe",
4943
+ stderr: "pipe"
4944
+ });
4945
+ await runOrThrow(input.runner, {
4946
+ command: input.engine,
4947
+ args: [
4948
+ ...buildContextArgs(input.engine, input.context),
4949
+ "cp",
4950
+ `${containerRef}:/tmp/poe-workspace-download.tar`,
4951
+ archivePath
4952
+ ],
4953
+ stdout: "pipe",
4954
+ stderr: "pipe"
4955
+ });
4956
+ const extractMode = opts.conflictPolicy === "refuse" ? "-xkf" : "-xf";
4957
+ await runOrThrow(input.runner, {
4958
+ command: "tar",
4959
+ args: [extractMode, archivePath, "-C", input.spec.cwd],
4960
+ stdout: "pipe",
4961
+ stderr: "pipe"
4962
+ });
4963
+ return { files: 0, bytes: 0, conflicts: [] };
4964
+ } finally {
4965
+ rmSync(tempDir, { recursive: true, force: true });
4966
+ }
4967
+ },
4968
+ exec(spec) {
4969
+ return input.runner.exec({
4970
+ command: input.engine,
4971
+ args: [
4972
+ ...buildContextArgs(input.engine, input.context),
4973
+ "exec",
4974
+ ...spec.stdin === "pipe" || spec.stdin === "inherit" ? ["-i"] : [],
4975
+ ...spec.tty === true ? ["-t"] : [],
4976
+ ...spec.cwd !== void 0 ? ["-w", spec.cwd] : [],
4977
+ ...buildEnvArgs(spec.env),
4978
+ containerRef,
4979
+ spec.command,
4980
+ ...spec.args ?? []
4981
+ ],
4982
+ stdin: spec.stdin,
4983
+ stdout: spec.stdout,
4984
+ stderr: spec.stderr,
4985
+ tty: spec.tty
4986
+ });
4987
+ },
4988
+ async detach() {
4989
+ return createContainerJob(containerRef, input.runner, input.engine, input.context);
4990
+ },
4991
+ shell() {
4992
+ const shellSpec = input.spec.shellSpec;
4993
+ return this.exec({
4994
+ command: shellSpec?.command ?? input.spec.env.SHELL ?? "sh",
4995
+ ...shellSpec?.args ? { args: shellSpec.args } : {},
4996
+ cwd: input.spec.cwd,
4997
+ env: shellSpec && "env" in shellSpec ? shellSpec.env : input.spec.env,
4998
+ stdin: "inherit",
4999
+ stdout: "inherit",
5000
+ stderr: "inherit",
5001
+ tty: true
5002
+ });
5003
+ },
5004
+ async close() {
5005
+ await runOrThrow(input.runner, {
5006
+ command: input.engine,
5007
+ args: [...buildContextArgs(input.engine, input.context), "rm", "-f", containerRef],
5008
+ stdout: "pipe",
5009
+ stderr: "pipe"
5010
+ });
5011
+ }
5012
+ };
5013
+ }
5014
+ async function resolveImage(input) {
5015
+ if (input.runtime.image !== void 0) {
5016
+ return input.runtime.image;
5017
+ }
5018
+ const dockerfilePath = path26.resolve(
5019
+ input.spec.cwd,
5020
+ input.runtime.dockerfile ?? path26.join(".poe-code", "Dockerfile")
5021
+ );
5022
+ const buildContext = path26.resolve(input.spec.cwd, input.runtime.build_context ?? ".");
5023
+ const dockerfileBytes = await readFile10(dockerfilePath);
5024
+ const hash = hashDockerTemplate(dockerfileBytes, input.runtime.build_args ?? {});
5025
+ const cached2 = await input.spec.state?.templates.get("docker", hash);
5026
+ if (cached2?.image !== void 0) {
5027
+ return cached2.image;
5028
+ }
5029
+ const image = `poe-code/local:${hash}`;
5030
+ await buildImage({
5031
+ runner: input.runner,
5032
+ engine: input.engine,
5033
+ context: input.context,
5034
+ image,
5035
+ dockerfilePath,
5036
+ buildContext,
5037
+ buildArgs: input.runtime.build_args ?? {}
5038
+ });
5039
+ await input.spec.state?.templates.put("docker", {
5040
+ hash,
5041
+ image,
5042
+ runtime_type: "docker",
5043
+ dockerfile_path: dockerfilePath,
5044
+ built_at: (/* @__PURE__ */ new Date()).toISOString()
5045
+ });
5046
+ return image;
5047
+ }
5048
+ function hashDockerTemplate(dockerfileBytes, buildArgs) {
5049
+ const hash = createHash4("sha256");
5050
+ hash.update(dockerfileBytes);
5051
+ hash.update("\0");
5052
+ for (const [key, value] of sortedBuildArgs(buildArgs)) {
5053
+ hash.update(key);
5054
+ hash.update("=");
5055
+ hash.update(value);
5056
+ hash.update("\0");
5057
+ }
5058
+ return hash.digest("hex");
5059
+ }
5060
+ async function buildImage(input) {
5061
+ await runOrThrow(input.runner, {
5062
+ command: input.engine,
5063
+ args: [
5064
+ ...buildContextArgs(input.engine, input.context),
5065
+ "build",
5066
+ "--tag",
5067
+ input.image,
5068
+ "-f",
5069
+ input.dockerfilePath,
5070
+ ...sortedBuildArgs(input.buildArgs).flatMap(([key, value]) => [
5071
+ "--build-arg",
5072
+ `${key}=${value}`
5073
+ ]),
5074
+ input.buildContext
5075
+ ],
5076
+ stdout: "pipe",
5077
+ stderr: "pipe"
5078
+ });
5079
+ }
5080
+ function parseDockerRuntime(runtime) {
5081
+ if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
5082
+ throw new Error("docker runtime must be an object");
5083
+ }
5084
+ const record = runtime;
5085
+ if (record.type !== "docker") {
5086
+ throw new Error('docker runtime type must be "docker"');
5087
+ }
5088
+ return record;
5089
+ }
5090
+ async function runAndRead(runner, spec) {
5091
+ const handle = runner.exec(spec);
5092
+ const stdout = readStream(handle.stdout);
5093
+ const stderr = readStream(handle.stderr);
5094
+ const result = await handle.result;
5095
+ const output = await stdout;
5096
+ if (result.exitCode !== 0) {
5097
+ const errorOutput = await stderr;
5098
+ throw new Error(
5099
+ `Command failed with exit code ${result.exitCode}: ${spec.command} ${(spec.args ?? []).join(" ")}${errorOutput ? `
5100
+ ${errorOutput}` : ""}`
5101
+ );
5102
+ }
5103
+ return output;
5104
+ }
5105
+ async function runOrThrow(runner, spec) {
5106
+ await runAndRead(runner, spec);
5107
+ }
5108
+ async function readStream(stream) {
5109
+ if (stream === null) {
5110
+ return "";
5111
+ }
5112
+ stream.setEncoding("utf8");
5113
+ const chunks = [];
5114
+ for await (const chunk of stream) {
5115
+ chunks.push(String(chunk));
5116
+ }
5117
+ return chunks.join("");
5118
+ }
5119
+ function sortedBuildArgs(buildArgs) {
5120
+ return Object.entries(buildArgs).sort(([left], [right]) => left.localeCompare(right));
5121
+ }
5122
+ function buildEnvArgs(env) {
5123
+ if (env === void 0) {
5124
+ return [];
5125
+ }
5126
+ return Object.entries(env).flatMap(([key, value]) => ["-e", `${key}=${value}`]);
5127
+ }
5128
+ function createContainerName() {
5129
+ return `poe-env-${randomBytes3(6).toString("hex")}`;
5130
+ }
5131
+ async function createContainerJob(containerId, runner, engine, context) {
5132
+ return {
5133
+ id: containerId,
5134
+ envId: containerId,
5135
+ tool: "docker",
5136
+ argv: ["attach", containerId],
5137
+ async status() {
5138
+ const handle = runner.exec({
5139
+ command: engine,
5140
+ args: [
5141
+ ...buildContextArgs(engine, context),
5142
+ "inspect",
5143
+ "-f",
5144
+ "{{.State.Status}}",
5145
+ containerId
5146
+ ],
5147
+ stdout: "pipe",
5148
+ stderr: "pipe"
5149
+ });
5150
+ const stdout = await readStream(handle.stdout);
5151
+ const result = await handle.result;
5152
+ if (result.exitCode !== 0) {
5153
+ return "lost";
5154
+ }
5155
+ return stdout.trim() === "running" ? "running" : "exited";
5156
+ },
5157
+ async *stream() {
5158
+ },
5159
+ async wait() {
5160
+ const handle = runner.exec({
5161
+ command: engine,
5162
+ args: [...buildContextArgs(engine, context), "wait", containerId],
5163
+ stdout: "pipe",
5164
+ stderr: "pipe"
5165
+ });
5166
+ const stdout = await readStream(handle.stdout);
5167
+ const result = await handle.result;
5168
+ return { exitCode: Number.parseInt(stdout.trim(), 10) || result.exitCode };
5169
+ },
5170
+ async kill(signal) {
5171
+ const args = signal === void 0 || signal === "SIGTERM" ? ["stop", containerId] : ["kill", ...signal === "SIGKILL" ? [] : [`--signal=${signal}`], containerId];
5172
+ await runOrThrow(runner, {
5173
+ command: engine,
5174
+ args: [...buildContextArgs(engine, context), ...args],
5175
+ stdout: "pipe",
5176
+ stderr: "pipe"
5177
+ });
5178
+ }
5179
+ };
5180
+ }
5181
+ function createAttachedSpec() {
5182
+ return {
5183
+ cwd: "/workspace",
5184
+ runtime: {
5185
+ type: "docker",
5186
+ image: "attached",
5187
+ build_args: {},
5188
+ mounts: []
5189
+ },
5190
+ env: {},
5191
+ uploadIgnoreFiles: [],
5192
+ jobLabel: {
5193
+ tool: "docker",
5194
+ argv: []
5195
+ }
5196
+ };
5197
+ }
5198
+ function shellQuote2(value) {
5199
+ return `'${value.replaceAll("'", "'\\''")}'`;
5200
+ }
5201
+
5202
+ // packages/process-runner/src/host/host-execution-env.ts
5203
+ var hostExecutionEnvFactory = {
5204
+ type: "host",
5205
+ supportsDetach: false,
5206
+ async open(openSpec) {
5207
+ return {
5208
+ id: "host",
5209
+ job: null,
5210
+ async uploadWorkspace() {
5211
+ return {
5212
+ files: 0,
5213
+ bytes: 0,
5214
+ skipped: []
5215
+ };
5216
+ },
5217
+ async downloadWorkspace() {
5218
+ return {
5219
+ files: 0,
5220
+ bytes: 0,
5221
+ conflicts: []
5222
+ };
5223
+ },
5224
+ exec(spec) {
5225
+ return createHostRunner().exec(spec);
5226
+ },
5227
+ async detach() {
5228
+ throw new Error("host runtime does not support detach because host has no addressable env");
5229
+ },
5230
+ shell() {
5231
+ const shellSpec = openSpec.shellSpec;
5232
+ return createHostRunner().exec({
5233
+ command: shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
5234
+ ...shellSpec?.args ? { args: shellSpec.args } : {},
5235
+ cwd: openSpec.cwd,
5236
+ env: shellSpec && "env" in shellSpec ? shellSpec.env : openSpec.env,
5237
+ stdin: "inherit",
5238
+ stdout: "inherit",
5239
+ stderr: "inherit",
5240
+ tty: true
5241
+ });
5242
+ },
5243
+ async close() {
5244
+ }
5245
+ };
5246
+ },
5247
+ async attach() {
5248
+ throw new Error("host runtime does not support reattach");
5249
+ }
5250
+ };
2938
5251
 
2939
- // packages/memory/src/cache.cli.ts
2940
- import parseDuration from "parse-duration";
2941
- async function runMemoryCacheStatus() {
2942
- console.log("cache status not implemented yet");
5252
+ // packages/process-runner/src/testing/mock-runner.ts
5253
+ import { Readable, Writable } from "node:stream";
5254
+
5255
+ // packages/agent-spawn/src/register-factories.ts
5256
+ registerExecutionEnvFactory(hostExecutionEnvFactory);
5257
+ registerExecutionEnvFactory(dockerExecutionEnvFactory);
5258
+ if (process.env.VITEST === "true") {
5259
+ registerExecutionEnvFactory(createTestHostExecutionEnvFactory());
2943
5260
  }
2944
- async function runMemoryCacheClear(input) {
2945
- if (!input.yes) {
2946
- throw new Error("Refusing to clear cache without --yes.");
2947
- }
2948
- const olderThanMs = parseOlderThan(input.olderThan);
2949
- const result = await clearCache(input.root, olderThanMs === void 0 ? {} : { olderThanMs });
2950
- console.log(`removed ${result.removed} cache ${result.removed === 1 ? "entry" : "entries"}`);
2951
- return result;
5261
+ function createTestHostExecutionEnvFactory() {
5262
+ return {
5263
+ type: "host",
5264
+ supportsDetach: false,
5265
+ open: ((openSpec) => {
5266
+ return {
5267
+ id: "host",
5268
+ job: null,
5269
+ async uploadWorkspace() {
5270
+ return { files: 0, bytes: 0, skipped: [] };
5271
+ },
5272
+ async downloadWorkspace() {
5273
+ return { files: 0, bytes: 0, conflicts: [] };
5274
+ },
5275
+ exec(spec) {
5276
+ return runHost(spawnChildProcess2, spec);
5277
+ },
5278
+ async detach() {
5279
+ throw new Error(
5280
+ "host runtime does not support detach because host has no addressable env"
5281
+ );
5282
+ },
5283
+ shell() {
5284
+ return runHost(spawnChildProcess2, {
5285
+ command: openSpec.shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
5286
+ args: openSpec.shellSpec?.args,
5287
+ cwd: openSpec.cwd,
5288
+ env: openSpec.shellSpec && "env" in openSpec.shellSpec ? openSpec.shellSpec.env : openSpec.env,
5289
+ stdin: "inherit",
5290
+ stdout: "inherit",
5291
+ stderr: "inherit",
5292
+ tty: true
5293
+ });
5294
+ },
5295
+ async close() {
5296
+ }
5297
+ };
5298
+ }),
5299
+ async attach() {
5300
+ throw new Error("host runtime does not support reattach");
5301
+ }
5302
+ };
2952
5303
  }
2953
- function parseOlderThan(value) {
2954
- if (value === void 0) {
2955
- return void 0;
2956
- }
2957
- const duration = parseDuration(value);
2958
- if (duration === null || Number.isNaN(duration) || duration < 0) {
2959
- throw new Error(`Invalid duration for --older-than: "${value}".`);
5304
+ function runHost(spawnProcess, spec) {
5305
+ const stdin = spec.stdin ?? "ignore";
5306
+ const stdout = spec.stdout ?? "pipe";
5307
+ const stderr = spec.stderr ?? "pipe";
5308
+ const stdio = stdin === "inherit" && stdout === "inherit" && stderr === "inherit" ? "inherit" : [stdin, stdout, stderr];
5309
+ const child = spawnProcess(spec.command, spec.args ?? [], {
5310
+ cwd: spec.cwd,
5311
+ env: spec.env,
5312
+ stdio
5313
+ });
5314
+ const result = new Promise((resolve2) => {
5315
+ child.once("close", (code) => {
5316
+ resolve2({ exitCode: code ?? 1 });
5317
+ });
5318
+ child.once("error", () => {
5319
+ resolve2({ exitCode: 1 });
5320
+ });
5321
+ });
5322
+ const kill = (signal) => {
5323
+ child.kill(signal);
5324
+ };
5325
+ if (spec.signal?.aborted) {
5326
+ kill("SIGTERM");
5327
+ } else {
5328
+ spec.signal?.addEventListener("abort", () => kill("SIGTERM"), { once: true });
2960
5329
  }
2961
- return duration;
5330
+ return {
5331
+ pid: child.pid ?? null,
5332
+ stdin: child.stdin,
5333
+ stdout: child.stdout,
5334
+ stderr: child.stderr,
5335
+ result,
5336
+ kill
5337
+ };
2962
5338
  }
2963
5339
 
2964
- // packages/memory/src/ingest.ts
2965
- import * as fs11 from "node:fs/promises";
2966
- import path23 from "node:path";
2967
-
2968
5340
  // packages/agent-spawn/src/run-command.ts
2969
- import { spawn } from "node:child_process";
5341
+ import { spawn as spawn2 } from "node:child_process";
2970
5342
 
2971
5343
  // packages/agent-spawn/src/types.ts
2972
5344
  function resolveModeConfig(modeConfig) {
@@ -2979,145 +5351,6 @@ function resolveModeConfig(modeConfig) {
2979
5351
  };
2980
5352
  }
2981
5353
 
2982
- // packages/agent-defs/src/agents/claude-code.ts
2983
- var claudeCodeAgent = {
2984
- id: "claude-code",
2985
- name: "claude-code",
2986
- label: "Claude Code",
2987
- summary: "Configure Claude Code to route through Poe.",
2988
- aliases: ["claude"],
2989
- binaryName: "claude",
2990
- configPath: "~/.claude/settings.json",
2991
- branding: {
2992
- colors: {
2993
- dark: "#C15F3C",
2994
- light: "#C15F3C"
2995
- }
2996
- }
2997
- };
2998
-
2999
- // packages/agent-defs/src/agents/claude-desktop.ts
3000
- var claudeDesktopAgent = {
3001
- id: "claude-desktop",
3002
- name: "claude-desktop",
3003
- label: "Claude Desktop",
3004
- summary: "Anthropic's official desktop application for Claude",
3005
- configPath: "~/.claude/settings.json",
3006
- branding: {
3007
- colors: {
3008
- dark: "#D97757",
3009
- light: "#D97757"
3010
- }
3011
- }
3012
- };
3013
-
3014
- // packages/agent-defs/src/agents/codex.ts
3015
- var codexAgent = {
3016
- id: "codex",
3017
- name: "codex",
3018
- label: "Codex",
3019
- summary: "Configure Codex to use Poe as the model provider.",
3020
- binaryName: "codex",
3021
- configPath: "~/.codex/config.toml",
3022
- branding: {
3023
- colors: {
3024
- dark: "#D5D9DF",
3025
- light: "#7A7F86"
3026
- }
3027
- }
3028
- };
3029
-
3030
- // packages/agent-defs/src/agents/opencode.ts
3031
- var openCodeAgent = {
3032
- id: "opencode",
3033
- name: "opencode",
3034
- label: "OpenCode CLI",
3035
- summary: "Configure OpenCode CLI to use the Poe API.",
3036
- binaryName: "opencode",
3037
- configPath: "~/.config/opencode/config.json",
3038
- branding: {
3039
- colors: {
3040
- dark: "#4A4F55",
3041
- light: "#2F3338"
3042
- }
3043
- }
3044
- };
3045
-
3046
- // packages/agent-defs/src/agents/kimi.ts
3047
- var kimiAgent = {
3048
- id: "kimi",
3049
- name: "kimi",
3050
- label: "Kimi",
3051
- summary: "Configure Kimi CLI to use Poe API",
3052
- aliases: ["kimi-cli"],
3053
- binaryName: "kimi",
3054
- configPath: "~/.kimi/config.toml",
3055
- branding: {
3056
- colors: {
3057
- dark: "#7B68EE",
3058
- light: "#6A5ACD"
3059
- }
3060
- }
3061
- };
3062
-
3063
- // packages/agent-defs/src/agents/goose.ts
3064
- var gooseAgent = {
3065
- id: "goose",
3066
- name: "goose",
3067
- label: "Goose",
3068
- summary: "Block's open-source AI agent with ACP support.",
3069
- binaryName: "goose",
3070
- configPath: "~/.config/goose/config.yaml",
3071
- branding: {
3072
- colors: {
3073
- dark: "#FF6B35",
3074
- light: "#E85D26"
3075
- }
3076
- }
3077
- };
3078
-
3079
- // packages/agent-defs/src/agents/poe-agent.ts
3080
- var poeAgentAgent = {
3081
- id: "poe-agent",
3082
- name: "poe-agent",
3083
- label: "Poe Agent",
3084
- summary: "Run one-shot prompts with the built-in Poe agent runtime.",
3085
- configPath: "~/.poe-code/config.json",
3086
- branding: {
3087
- colors: {
3088
- dark: "#A465F7",
3089
- light: "#7A3FD3"
3090
- }
3091
- }
3092
- };
3093
-
3094
- // packages/agent-defs/src/registry.ts
3095
- var allAgents = [
3096
- claudeCodeAgent,
3097
- claudeDesktopAgent,
3098
- codexAgent,
3099
- openCodeAgent,
3100
- kimiAgent,
3101
- gooseAgent,
3102
- poeAgentAgent
3103
- ];
3104
- var lookup = /* @__PURE__ */ new Map();
3105
- for (const agent of allAgents) {
3106
- const values = [agent.id, agent.name, ...agent.aliases ?? []];
3107
- for (const value of values) {
3108
- const normalized = value.toLowerCase();
3109
- if (!lookup.has(normalized)) {
3110
- lookup.set(normalized, agent.id);
3111
- }
3112
- }
3113
- }
3114
- function resolveAgentId(input) {
3115
- if (!input) {
3116
- return void 0;
3117
- }
3118
- return lookup.get(input.toLowerCase());
3119
- }
3120
-
3121
5354
  // packages/agent-spawn/src/configs/mcp.ts
3122
5355
  function toJsonMcpServers(servers) {
3123
5356
  const out = {};
@@ -3385,9 +5618,8 @@ function listMcpSupportedAgents() {
3385
5618
  }
3386
5619
 
3387
5620
  // packages/agent-spawn/src/spawn.ts
3388
- import { spawn as spawnChildProcess } from "node:child_process";
3389
5621
  import { mkdirSync, openSync, writeSync, closeSync } from "node:fs";
3390
- import path19 from "node:path";
5622
+ import path27 from "node:path";
3391
5623
 
3392
5624
  // packages/agent-spawn/src/configs/resolve-config.ts
3393
5625
  function resolveConfig(agentId) {
@@ -3437,18 +5669,11 @@ function stripModelNamespace(model) {
3437
5669
  }
3438
5670
 
3439
5671
  // packages/agent-spawn/src/spawn.ts
3440
- function createAbortError() {
5672
+ function createAbortError3() {
3441
5673
  const error2 = new Error("Agent spawn aborted");
3442
5674
  error2.name = "AbortError";
3443
5675
  return error2;
3444
5676
  }
3445
- function createActivityTimeoutError(timeoutMs) {
3446
- const error2 = new Error(
3447
- `Agent spawn timed out after ${timeoutMs / 1e3}s of inactivity`
3448
- );
3449
- error2.name = "ActivityTimeoutError";
3450
- return error2;
3451
- }
3452
5677
  function resolveCliConfig(agentId) {
3453
5678
  const resolved = resolveConfig(agentId);
3454
5679
  if (!resolved.spawnConfig) {
@@ -3516,9 +5741,9 @@ function buildCliArgs(config, options, stdinMode) {
3516
5741
  }
3517
5742
  return { args, env: mode.env };
3518
5743
  }
3519
- async function spawn2(agentId, options, context) {
5744
+ async function spawn3(agentId, options, context) {
3520
5745
  if (options.signal?.aborted) {
3521
- throw createAbortError();
5746
+ throw createAbortError3();
3522
5747
  }
3523
5748
  const { agentId: resolvedId, binaryName, spawnConfig } = resolveCliConfig(agentId);
3524
5749
  const stdinMode = options.useStdin && spawnConfig.stdinMode ? spawnConfig.stdinMode : void 0;
@@ -3530,89 +5755,68 @@ async function spawn2(agentId, options, context) {
3530
5755
  }
3531
5756
  const logFilePath = resolveSpawnLogPath(options);
3532
5757
  const logFd = logFilePath ? openSpawnLog(logFilePath) : void 0;
3533
- const child = spawnChildProcess(binaryName, spawnArgs, {
3534
- cwd: options.cwd,
3535
- stdio: [stdinMode ? "pipe" : "inherit", "pipe", "pipe"],
3536
- ...modeEnv ? { env: { ...process.env, ...modeEnv } } : {}
3537
- });
3538
- if (!child.stdout || !child.stderr) {
3539
- throw new Error(`Failed to spawn "${resolvedId}": missing stdio pipes.`);
3540
- }
3541
- const stdoutStream = child.stdout;
3542
- const stderrStream = child.stderr;
3543
- if (stdinMode) {
3544
- if (!child.stdin) {
3545
- throw new Error(`Failed to spawn "${resolvedId}": missing stdin pipe.`);
3546
- }
3547
- child.stdin.setDefaultEncoding("utf8");
3548
- child.stdin.write(options.prompt);
3549
- child.stdin.end();
3550
- }
3551
- return new Promise((resolve2, reject) => {
3552
- let stdout = "";
3553
- let stderr = "";
3554
- let aborted = false;
3555
- let timedOut = false;
3556
- const onAbort = () => {
3557
- aborted = true;
3558
- child.kill("SIGTERM");
3559
- };
3560
- options.signal?.addEventListener("abort", onAbort, { once: true });
3561
- let activityTimer;
3562
- const resetActivityTimer = options.activityTimeoutMs ? () => {
3563
- if (activityTimer) clearTimeout(activityTimer);
3564
- activityTimer = setTimeout(() => {
3565
- timedOut = true;
3566
- child.kill("SIGTERM");
3567
- }, options.activityTimeoutMs);
3568
- } : void 0;
3569
- resetActivityTimer?.();
3570
- const cleanup = () => {
3571
- options.signal?.removeEventListener("abort", onAbort);
3572
- if (activityTimer) clearTimeout(activityTimer);
3573
- };
3574
- stdoutStream.setEncoding("utf8");
3575
- stdoutStream.on("data", (chunk) => {
3576
- stdout += chunk;
3577
- resetActivityTimer?.();
3578
- if (options.tee?.stdout) options.tee.stdout.write(chunk);
3579
- appendSpawnLog(logFd, chunk);
3580
- });
3581
- stderrStream.setEncoding("utf8");
3582
- stderrStream.on("data", (chunk) => {
3583
- stderr += chunk;
3584
- resetActivityTimer?.();
3585
- if (options.tee?.stderr) options.tee.stderr.write(chunk);
3586
- appendSpawnLog(logFd, chunk);
3587
- });
3588
- child.on("error", (error2) => {
3589
- cleanup();
3590
- closeSpawnLog(logFd);
3591
- if (aborted) {
3592
- reject(createAbortError());
3593
- return;
5758
+ const processEnv = modeEnv ? { ...process.env, ...modeEnv } : void 0;
5759
+ const argv = [binaryName, ...spawnArgs];
5760
+ const execution = resolvePoeCommandExecution({
5761
+ cwd: options.cwd ?? process.cwd(),
5762
+ env: processEnv ?? process.env,
5763
+ argv,
5764
+ tool: resolvedId,
5765
+ runtime: {
5766
+ runtime: options.runtime,
5767
+ runtimeImage: options.runtimeImage,
5768
+ runtimeTemplate: options.runtimeTemplate,
5769
+ detach: options.detach,
5770
+ mountPoeCode: options.mountPoeCode
5771
+ },
5772
+ context,
5773
+ openSpec: {
5774
+ execution: {
5775
+ wrapForLogTee: false,
5776
+ stdin: stdinMode ? "pipe" : "inherit",
5777
+ stdout: "pipe",
5778
+ stderr: "pipe",
5779
+ env: processEnv,
5780
+ input: stdinMode ? options.prompt : void 0,
5781
+ captureOutput: true,
5782
+ activityTimeoutMs: options.activityTimeoutMs,
5783
+ onStdout(chunk) {
5784
+ options.tee?.stdout?.write(chunk);
5785
+ appendSpawnLog(logFd, chunk);
5786
+ },
5787
+ onStderr(chunk) {
5788
+ options.tee?.stderr?.write(chunk);
5789
+ appendSpawnLog(logFd, chunk);
5790
+ }
3594
5791
  }
3595
- reject(error2);
5792
+ }
5793
+ });
5794
+ try {
5795
+ const result = await runPoeCommand({
5796
+ factory: execution.factory,
5797
+ openSpec: execution.openSpec,
5798
+ detach: execution.detach,
5799
+ state: execution.state,
5800
+ signal: options.signal
3596
5801
  });
3597
- child.on("close", (code) => {
3598
- cleanup();
3599
- closeSpawnLog(logFd);
3600
- if (aborted) {
3601
- reject(createAbortError());
3602
- return;
3603
- }
3604
- if (timedOut) {
3605
- reject(createActivityTimeoutError(options.activityTimeoutMs));
3606
- return;
3607
- }
3608
- resolve2({
3609
- stdout,
3610
- stderr,
3611
- exitCode: code ?? 1,
5802
+ if (result.kind === "detached") {
5803
+ return {
5804
+ stdout: "",
5805
+ stderr: "",
5806
+ exitCode: 0,
3612
5807
  ...logFilePath ? { logFile: logFilePath } : {}
3613
- });
3614
- });
3615
- });
5808
+ };
5809
+ }
5810
+ const captured = result;
5811
+ return {
5812
+ stdout: captured.stdout ?? "",
5813
+ stderr: captured.stderr ?? "",
5814
+ exitCode: result.exitCode,
5815
+ ...logFilePath ? { logFile: logFilePath } : {}
5816
+ };
5817
+ } finally {
5818
+ closeSpawnLog(logFd);
5819
+ }
3616
5820
  }
3617
5821
  function resolveSpawnLogPath(options) {
3618
5822
  if (options.logPath) {
@@ -3621,11 +5825,11 @@ function resolveSpawnLogPath(options) {
3621
5825
  if (!options.logDir || !options.logFileName) {
3622
5826
  return void 0;
3623
5827
  }
3624
- return path19.join(options.logDir, options.logFileName);
5828
+ return path27.join(options.logDir, options.logFileName);
3625
5829
  }
3626
5830
  function openSpawnLog(filePath) {
3627
5831
  try {
3628
- mkdirSync(path19.dirname(filePath), { recursive: true });
5832
+ mkdirSync(path27.dirname(filePath), { recursive: true });
3629
5833
  return openSync(filePath, "a");
3630
5834
  } catch {
3631
5835
  return void 0;
@@ -3646,9 +5850,6 @@ function closeSpawnLog(fd) {
3646
5850
  }
3647
5851
  }
3648
5852
 
3649
- // packages/agent-spawn/src/spawn-interactive.ts
3650
- import { spawn as spawnChildProcess2 } from "node:child_process";
3651
-
3652
5853
  // packages/design-system/src/tokens/colors.ts
3653
5854
  import chalk from "chalk";
3654
5855
  var dark = {
@@ -4056,9 +6257,9 @@ import chalk16 from "chalk";
4056
6257
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
4057
6258
 
4058
6259
  // packages/agent-spawn/src/acp/replay.ts
4059
- import path20 from "node:path";
6260
+ import path28 from "node:path";
4060
6261
  import { homedir as homedir2 } from "node:os";
4061
- import { open, readdir as readdir4 } from "node:fs/promises";
6262
+ import { open as open2, readdir as readdir4 } from "node:fs/promises";
4062
6263
  import { createInterface } from "node:readline";
4063
6264
 
4064
6265
  // packages/poe-acp-client/src/acp-client.ts
@@ -4070,21 +6271,18 @@ import {
4070
6271
  } from "node:child_process";
4071
6272
 
4072
6273
  // packages/poe-acp-client/src/run-report.ts
4073
- import * as fsPromises2 from "node:fs/promises";
6274
+ import * as fsPromises3 from "node:fs/promises";
4074
6275
  import { homedir } from "node:os";
4075
6276
  import { join } from "node:path";
4076
6277
 
4077
- // packages/agent-spawn/src/acp/spawn.ts
4078
- import { spawn as spawnChildProcess4 } from "node:child_process";
4079
-
4080
6278
  // packages/agent-spawn/src/acp/middlewares/spawn-log.ts
4081
- import path21 from "node:path";
6279
+ import path29 from "node:path";
4082
6280
  import { homedir as homedir3 } from "node:os";
4083
- import { mkdir as mkdir5, open as open2 } from "node:fs/promises";
6281
+ import { mkdir as mkdir5, open as open3 } from "node:fs/promises";
4084
6282
 
4085
6283
  // packages/memory/src/tokens.ts
4086
6284
  import * as fs10 from "node:fs/promises";
4087
- import path22 from "node:path";
6285
+ import path30 from "node:path";
4088
6286
 
4089
6287
  // packages/tokenfill/dist/tokenizer.js
4090
6288
  import { get_encoding } from "tiktoken";
@@ -4125,7 +6323,7 @@ function countTokens(text4) {
4125
6323
  }
4126
6324
 
4127
6325
  // packages/tokenfill/dist/corpus.js
4128
- import { readdirSync, readFileSync } from "node:fs";
6326
+ import { readdirSync, readFileSync as readFileSync2 } from "node:fs";
4129
6327
  import { dirname, join as join2 } from "node:path";
4130
6328
  import { fileURLToPath } from "node:url";
4131
6329
  var CORPUS_ARTICLE_SEPARATOR = "\n\n";
@@ -4138,7 +6336,7 @@ function loadBuiltInCorpusArticles() {
4138
6336
  if (corpusFileNames.length === 0) {
4139
6337
  throw new Error(`No built-in corpus markdown files found in ${corpusDirectoryPath}`);
4140
6338
  }
4141
- return corpusFileNames.map((fileName) => readFileSync(join2(corpusDirectoryPath, fileName), "utf8").trim());
6339
+ return corpusFileNames.map((fileName) => readFileSync2(join2(corpusDirectoryPath, fileName), "utf8").trim());
4142
6340
  }
4143
6341
  var BUILT_IN_CORPUS_ARTICLES = loadBuiltInCorpusArticles();
4144
6342
 
@@ -4168,11 +6366,11 @@ async function computeTokenStats(root) {
4168
6366
  }
4169
6367
  }
4170
6368
  }
4171
- const repoRoot = path22.resolve(root, "..", "..");
6369
+ const repoRoot = path30.resolve(root, "..", "..");
4172
6370
  let sourceTokens = 0;
4173
6371
  const missingSources = [];
4174
6372
  for (const sourcePath of sourcePaths) {
4175
- const absPath = path22.isAbsolute(sourcePath) ? sourcePath : path22.resolve(repoRoot, sourcePath);
6373
+ const absPath = path30.isAbsolute(sourcePath) ? sourcePath : path30.resolve(repoRoot, sourcePath);
4176
6374
  try {
4177
6375
  const content = await fs10.readFile(absPath, "utf8");
4178
6376
  sourceTokens += countTokens(content);
@@ -4223,10 +6421,10 @@ function resolveRunners(overrides) {
4223
6421
  async function ingest(root, opts, runners) {
4224
6422
  const resolved = resolveRunners(runners);
4225
6423
  const source = await materializeSource(opts.source);
4226
- const indexMdBytes = await fs11.readFile(path23.join(root, MEMORY_INDEX_RELPATH));
6424
+ const indexMdBytes = await fs11.readFile(path31.join(root, MEMORY_INDEX_RELPATH));
4227
6425
  const configOptions = {
4228
6426
  fs: fs11,
4229
- filePath: path23.join(inferRepoRoot(root), "poe-code.json")
6427
+ filePath: path31.join(inferRepoRoot(root), "poe-code.json")
4230
6428
  };
4231
6429
  const agentId = await resolveAgent(configOptions, opts.agent ?? null) ?? opts.agent ?? "claude-code";
4232
6430
  const key = resolved.computeIngestKey({
@@ -4264,7 +6462,7 @@ async function ingest(root, opts, runners) {
4264
6462
  let timeoutError;
4265
6463
  try {
4266
6464
  const result = await runWithTimeout(
4267
- spawn2(agentId, { prompt }),
6465
+ spawn3(agentId, { prompt }),
4268
6466
  opts.timeoutMs ?? await configuredTimeout(configOptions)
4269
6467
  );
4270
6468
  exitCode = result.exitCode;
@@ -4316,7 +6514,7 @@ async function materializeSource(source) {
4316
6514
  throw new Error("URL ingest not implemented yet.");
4317
6515
  }
4318
6516
  function inferRepoRoot(root) {
4319
- return path23.resolve(root, "..", "..");
6517
+ return path31.resolve(root, "..", "..");
4320
6518
  }
4321
6519
  async function runWithTimeout(promise, timeoutMs) {
4322
6520
  return await new Promise((resolve2, reject) => {
@@ -5074,8 +7272,8 @@ function printMcpConfig() {
5074
7272
  }
5075
7273
 
5076
7274
  // packages/agent-skill-config/src/configs.ts
5077
- import os from "node:os";
5078
- import path24 from "node:path";
7275
+ import os4 from "node:os";
7276
+ import path32 from "node:path";
5079
7277
  var agentSkillConfigs = {
5080
7278
  "claude-code": {
5081
7279
  globalSkillDir: "~/.claude/skills",
@@ -5094,7 +7292,7 @@ var agentSkillConfigs = {
5094
7292
  localSkillDir: ".agents/skills"
5095
7293
  }
5096
7294
  };
5097
- var supportedAgents = Object.keys(agentSkillConfigs);
7295
+ var supportedAgents2 = Object.keys(agentSkillConfigs);
5098
7296
  function resolveAgentSupport(input, registry = agentSkillConfigs) {
5099
7297
  const resolvedId = resolveAgentId(input);
5100
7298
  if (!resolvedId) {
@@ -5108,8 +7306,8 @@ function resolveAgentSupport(input, registry = agentSkillConfigs) {
5108
7306
  }
5109
7307
 
5110
7308
  // packages/agent-skill-config/src/templates.ts
5111
- import { readFile as readFile11, stat as stat5 } from "node:fs/promises";
5112
- import path25 from "node:path";
7309
+ import { readFile as readFile13, stat as stat6 } from "node:fs/promises";
7310
+ import path33 from "node:path";
5113
7311
  import { fileURLToPath as fileURLToPath2 } from "node:url";
5114
7312
 
5115
7313
  // packages/agent-skill-config/src/apply.ts
@@ -5215,7 +7413,7 @@ var agentMcpConfigs = {
5215
7413
  shape: "goose"
5216
7414
  }
5217
7415
  };
5218
- var supportedAgents2 = Object.keys(agentMcpConfigs);
7416
+ var supportedAgents3 = Object.keys(agentMcpConfigs);
5219
7417
  function resolveAgentSupport2(input, registry = agentMcpConfigs) {
5220
7418
  const resolvedId = resolveAgentId(input);
5221
7419
  if (!resolvedId) {
@@ -5242,7 +7440,7 @@ function resolveConfigPath2(config, platform) {
5242
7440
  }
5243
7441
 
5244
7442
  // packages/agent-mcp-config/src/apply.ts
5245
- import path26 from "node:path";
7443
+ import path34 from "node:path";
5246
7444
  import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
5247
7445
 
5248
7446
  // packages/agent-mcp-config/src/shapes.ts
@@ -5334,7 +7532,7 @@ function getShapeTransformer(shape) {
5334
7532
 
5335
7533
  // packages/agent-mcp-config/src/apply.ts
5336
7534
  function getConfigDirectory(configPath) {
5337
- return path26.dirname(configPath);
7535
+ return path34.dirname(configPath);
5338
7536
  }
5339
7537
  var UnsupportedAgentError2 = class extends Error {
5340
7538
  constructor(agentId) {
@@ -5360,9 +7558,9 @@ function expandHomePath(configPath, homeDir) {
5360
7558
  return homeDir;
5361
7559
  }
5362
7560
  if (configPath.startsWith("~/")) {
5363
- return path26.join(homeDir, configPath.slice(2));
7561
+ return path34.join(homeDir, configPath.slice(2));
5364
7562
  }
5365
- return path26.join(homeDir, configPath.slice(1));
7563
+ return path34.join(homeDir, configPath.slice(1));
5366
7564
  }
5367
7565
  function parseYamlDocument(content) {
5368
7566
  if (content.trim() === "") {
@@ -5395,7 +7593,7 @@ async function writeYamlConfig(configPath, document, options) {
5395
7593
  return;
5396
7594
  }
5397
7595
  const absolutePath = expandHomePath(configPath, options.homeDir);
5398
- const configDir = path26.dirname(absolutePath);
7596
+ const configDir = path34.dirname(absolutePath);
5399
7597
  await options.fs.mkdir(configDir, { recursive: true });
5400
7598
  await options.fs.writeFile(absolutePath, serializeYamlDocument(document), {
5401
7599
  encoding: "utf8"
@@ -5573,7 +7771,7 @@ async function installMemory(options) {
5573
7771
 
5574
7772
  // packages/memory/src/query.ts
5575
7773
  import * as fs12 from "node:fs/promises";
5576
- import path27 from "node:path";
7774
+ import path35 from "node:path";
5577
7775
  async function queryMemory(root, options) {
5578
7776
  const pages = await listPages(root);
5579
7777
  if (pages.length === 0) {
@@ -5587,11 +7785,11 @@ async function queryMemory(root, options) {
5587
7785
  }
5588
7786
  const configOptions = {
5589
7787
  fs: fs12,
5590
- filePath: path27.join(inferRepoRoot2(root), "poe-code.json")
7788
+ filePath: path35.join(inferRepoRoot2(root), "poe-code.json")
5591
7789
  };
5592
7790
  const agentId = await resolveAgent(configOptions, options.agent ?? null) ?? options.agent ?? "claude-code";
5593
7791
  const context = await selectQueryContext(root, options.question, options.budget);
5594
- const result = await spawn2(agentId, { prompt: context.prompt });
7792
+ const result = await spawn3(agentId, { prompt: context.prompt });
5595
7793
  return {
5596
7794
  answer: result.answer,
5597
7795
  citations: result.citations,
@@ -5602,7 +7800,7 @@ async function queryMemory(root, options) {
5602
7800
  }
5603
7801
  async function selectQueryContext(root, question, budget) {
5604
7802
  const [indexText, pages] = await Promise.all([
5605
- fs12.readFile(path27.join(root, MEMORY_INDEX_RELPATH), "utf8"),
7803
+ fs12.readFile(path35.join(root, MEMORY_INDEX_RELPATH), "utf8"),
5606
7804
  listPages(root)
5607
7805
  ]);
5608
7806
  const indexTokens = countTokens(indexText);
@@ -5681,12 +7879,12 @@ function tokenize(text4) {
5681
7879
  return text4.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length > 0);
5682
7880
  }
5683
7881
  function inferRepoRoot2(root) {
5684
- return path27.resolve(root, "..", "..");
7882
+ return path35.resolve(root, "..", "..");
5685
7883
  }
5686
7884
 
5687
7885
  // packages/memory/src/explain.ts
5688
7886
  import * as fs13 from "node:fs/promises";
5689
- import path28 from "node:path";
7887
+ import path36 from "node:path";
5690
7888
  async function explainPage(root, options) {
5691
7889
  const targetPage = await readPageIfPresent(root, options.relPath);
5692
7890
  if (targetPage === void 0) {
@@ -5709,10 +7907,10 @@ async function explainPage(root, options) {
5709
7907
  }
5710
7908
  const configOptions = {
5711
7909
  fs: fs13,
5712
- filePath: path28.join(inferRepoRoot3(root), "poe-code.json")
7910
+ filePath: path36.join(inferRepoRoot3(root), "poe-code.json")
5713
7911
  };
5714
7912
  const agentId = await resolveAgent(configOptions, options.agent ?? null) ?? options.agent ?? "claude-code";
5715
- const response = await spawn2(agentId, { prompt });
7913
+ const response = await spawn3(agentId, { prompt });
5716
7914
  return {
5717
7915
  answer: response.answer,
5718
7916
  citations: response.citations,
@@ -5758,7 +7956,7 @@ async function readPageIfPresent(root, relPath) {
5758
7956
  }
5759
7957
  }
5760
7958
  function inferRepoRoot3(root) {
5761
- return path28.resolve(root, "..", "..");
7959
+ return path36.resolve(root, "..", "..");
5762
7960
  }
5763
7961
 
5764
7962
  // packages/memory/src/explain.cli.ts
@@ -5771,9 +7969,9 @@ async function runMemoryExplain(input) {
5771
7969
  }
5772
7970
 
5773
7971
  // packages/memory/src/handle.ts
5774
- import path29 from "node:path";
7972
+ import path37 from "node:path";
5775
7973
  function openMemory(opts) {
5776
- if (!path29.isAbsolute(opts.root)) {
7974
+ if (!path37.isAbsolute(opts.root)) {
5777
7975
  throw new Error(`openMemory: root must be absolute, got ${opts.root}`);
5778
7976
  }
5779
7977
  const root = opts.root;