@turmind/halo 2.2.3 → 2.2.5

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 (44) hide show
  1. package/README.md +2 -1
  2. package/admin-out/404.html +1 -1
  3. package/admin-out/_next/static/chunks/1989-a9d3b40139c1e9a7.js +1 -0
  4. package/admin-out/_next/static/chunks/4320-0fb65ed376b666a8.js +1 -0
  5. package/admin-out/_next/static/chunks/4362.115405260299210d.js +1 -0
  6. package/admin-out/_next/static/chunks/{7613.fe24922a6a839656.js → 7073.4081ed209e6748bd.js} +1 -1
  7. package/admin-out/_next/static/chunks/{7073.9d5f9dd7a77827b7.js → 7613.9c1657bb3b52d1b5.js} +1 -1
  8. package/admin-out/_next/static/chunks/app/{page-10a4afac9cbc27a2.js → page-7c5bdfc234c8ac16.js} +1 -1
  9. package/admin-out/_next/static/chunks/{webpack-9532ec7f1fa9a757.js → webpack-1970c5140e4019b7.js} +1 -1
  10. package/admin-out/_next/static/css/7eb37d975d4027da.css +1 -0
  11. package/admin-out/index.html +1 -1
  12. package/admin-out/index.txt +5 -5
  13. package/bundled-docs/design/a2a.md +3 -1
  14. package/bundled-docs/design/architecture.md +2 -2
  15. package/bundled-docs/design/canvas-extensions.md +2 -0
  16. package/bundled-docs/design/node.md +18 -39
  17. package/bundled-docs/design/observability.md +2 -2
  18. package/bundled-docs/design/relay.md +1 -1
  19. package/bundled-docs/design/session.md +2 -2
  20. package/bundled-docs/design/storage.md +8 -3
  21. package/bundled-docs/design/ws.md +3 -3
  22. package/bundled-docs/dev/add-model-provider.md +18 -0
  23. package/bundled-docs/dev/api.md +10 -3
  24. package/bundled-docs/dev/deploy.md +1 -1
  25. package/bundled-docs/dev/env.md +3 -2
  26. package/bundled-docs/dev/tools.md +10 -6
  27. package/bundled-docs/guide/README.md +1 -1
  28. package/bundled-docs/guide/agents.md +2 -2
  29. package/bundled-docs/guide/cli.md +2 -31
  30. package/bundled-docs/guide/node.md +57 -36
  31. package/bundled-docs/guide/skills.md +1 -1
  32. package/bundled-docs/requirements/settings.md +62 -2
  33. package/dist/evo-wrapper.js +323 -247
  34. package/dist/index.js +1860 -1965
  35. package/package.json +2 -1
  36. package/templates/agents/default/agent.yaml +1 -1
  37. package/templates/models/aws-bedrock-claude-invoke.yaml +62 -1
  38. package/templates/models/aws-bedrock-openai.yaml +81 -7
  39. package/admin-out/_next/static/chunks/1607-bf227fc5e4db45b7.js +0 -1
  40. package/admin-out/_next/static/chunks/1989-3b2faa2731d973c4.js +0 -1
  41. package/admin-out/_next/static/chunks/4362.9d2ee4f9b6facc49.js +0 -1
  42. package/admin-out/_next/static/css/87d4fc049f865389.css +0 -1
  43. /package/admin-out/_next/static/{bbPEaP5uh2KvLzSHGU2CO → 1969BAiACyey4NBiI4j44}/_buildManifest.js +0 -0
  44. /package/admin-out/_next/static/{bbPEaP5uh2KvLzSHGU2CO → 1969BAiACyey4NBiI4j44}/_ssgManifest.js +0 -0
@@ -1,8 +1,8 @@
1
1
  // ../server/dist/evolution/evo-wrapper.js
2
- import fs11 from "node:fs";
3
- import path13 from "node:path";
2
+ import fs12 from "node:fs";
3
+ import path14 from "node:path";
4
4
  import { spawn as spawn2 } from "node:child_process";
5
- import { homedir as homedir6 } from "node:os";
5
+ import { homedir as homedir7 } from "node:os";
6
6
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7
7
  import { parseArgs } from "node:util";
8
8
  import { eq as eq3, and as and3, inArray as inArray2 } from "drizzle-orm";
@@ -237,41 +237,133 @@ function evoApplyLogFile(applyId) {
237
237
  }
238
238
 
239
239
  // ../server/dist/config.js
240
+ import fs9 from "node:fs";
241
+ import path12 from "node:path";
242
+ import { homedir as homedir6 } from "node:os";
243
+
244
+ // ../server/dist/settings-schema.js
240
245
  import fs8 from "node:fs";
241
246
  import path11 from "node:path";
242
247
  import { homedir as homedir5 } from "node:os";
243
248
  import YAML5 from "yaml";
244
249
 
245
- // ../server/dist/settings-schema.js
246
- import fs7 from "node:fs";
247
- import path10 from "node:path";
248
- import { homedir as homedir4 } from "node:os";
249
- import YAML4 from "yaml";
250
-
251
250
  // ../server/dist/tools/sandbox.js
252
251
  import { exec, execFile, spawn } from "node:child_process";
253
252
  import { promisify } from "node:util";
253
+ import path4 from "node:path";
254
+ import { homedir as homedir3 } from "node:os";
255
+
256
+ // ../server/dist/settings-file.js
257
+ import fs2 from "node:fs";
254
258
  import path3 from "node:path";
255
259
  import { homedir as homedir2 } from "node:os";
260
+ import YAML from "yaml";
261
+ function loadYamlFile(filePath) {
262
+ try {
263
+ const raw = fs2.readFileSync(filePath, "utf-8");
264
+ return YAML.parse(raw) ?? {};
265
+ } catch {
266
+ return {};
267
+ }
268
+ }
269
+ var SETTINGS_PATH = path3.join(homedir2(), ".halo", "secrets", "settings.yaml");
270
+ var _settingsCache = null;
271
+ function getSettings() {
272
+ let mtimeMs = 0;
273
+ try {
274
+ mtimeMs = fs2.statSync(SETTINGS_PATH).mtimeMs;
275
+ } catch {
276
+ }
277
+ if (!_settingsCache || _settingsCache.mtimeMs !== mtimeMs) {
278
+ _settingsCache = { mtimeMs, data: loadYamlFile(SETTINGS_PATH) };
279
+ }
280
+ return _settingsCache.data;
281
+ }
282
+ var ENV_PATTERN = /<<([A-Z_][A-Z0-9_]*)>>/g;
283
+ function expandEnv(raw) {
284
+ if (!raw)
285
+ return "";
286
+ return raw.replace(ENV_PATTERN, (m, name) => process.env[name] ?? m);
287
+ }
288
+
289
+ // ../server/dist/outbound-proxy.js
290
+ var DEFAULT_NO_PROXY = ["localhost", "127.0.0.1", "::1"];
291
+ function checkProxyUrl(raw) {
292
+ let url;
293
+ try {
294
+ url = new URL(raw);
295
+ } catch {
296
+ return "not a valid URL";
297
+ }
298
+ if (url.protocol !== "http:" && url.protocol !== "https:")
299
+ return "scheme must be http:// or https://";
300
+ if (url.username || url.password)
301
+ return "credentials are not allowed in the URL \u2014 put them in Token (general.outbound_proxy.token)";
302
+ return null;
303
+ }
304
+ var _cache = null;
305
+ function outboundProxy() {
306
+ const settings = getSettings();
307
+ if (_cache?.settings !== settings)
308
+ _cache = { settings, proxy: resolve(settings) };
309
+ return _cache.proxy;
310
+ }
311
+ function resolve(settings) {
312
+ const general = settings.general;
313
+ const cfg = general?.outbound_proxy;
314
+ const enabled = ["true", "1"].includes(String(cfg?.enabled ?? "").trim().toLowerCase());
315
+ const raw = typeof cfg?.url === "string" ? cfg.url.trim() : "";
316
+ if (!enabled || !raw)
317
+ return null;
318
+ const bad = checkProxyUrl(raw);
319
+ if (bad) {
320
+ console.warn(`[OutboundProxy] general.outbound_proxy.url ignored (${bad}) \u2014 proxy off`);
321
+ return null;
322
+ }
323
+ const url = new URL(raw).origin;
324
+ const cred = cfg?.token == null ? "" : expandEnv(String(cfg.token)).trim();
325
+ const colon = cred.indexOf(":");
326
+ const user = colon < 0 ? cred : cred.slice(0, colon);
327
+ const pass = colon < 0 ? "" : cred.slice(colon + 1);
328
+ const userinfo = `${encodeURIComponent(user)}:${encodeURIComponent(pass)}`;
329
+ const basic = Buffer.from(`${user}:${pass}`).toString("base64");
330
+ const inherited = [process.env.NO_PROXY, process.env.no_proxy].flatMap((v) => (v ?? "").split(","));
331
+ const merged = [.../* @__PURE__ */ new Set([...DEFAULT_NO_PROXY, ...inherited.map((s) => s.trim()).filter(Boolean)])];
332
+ const noProxy = merged.includes("*") ? ["*"] : merged;
333
+ console.log(`[OutboundProxy] on: ${url}${cred ? " (with token)" : ""}`);
334
+ return {
335
+ url,
336
+ envUrl: cred ? url.replace("://", `://${userinfo}@`) : url,
337
+ authorization: cred ? `Basic ${basic}` : "",
338
+ noProxy,
339
+ credential: cred ? { raw: cred, plain: `${user}:${pass}`, userinfo, basic } : null
340
+ };
341
+ }
256
342
 
257
343
  // ../server/dist/child-env.js
258
344
  var settingsOtelEnvKeys = /* @__PURE__ */ new Set();
259
- function cleanChildEnv() {
345
+ function cleanChildEnv(proxyEnv = outboundProxyEnv()) {
260
346
  const env = { ...process.env };
261
347
  delete env.HALO_PASSWORD;
262
348
  delete env.HALO_JWT_SECRET;
263
349
  delete env.HALO_NODE_TOKEN;
264
350
  for (const key of settingsOtelEnvKeys)
265
351
  delete env[key];
266
- return env;
352
+ return Object.assign(env, proxyEnv);
353
+ }
354
+ function outboundProxyEnv(proxy = outboundProxy()) {
355
+ if (!proxy)
356
+ return {};
357
+ const noProxy = proxy.noProxy.join(",");
358
+ return { http_proxy: proxy.envUrl, https_proxy: proxy.envUrl, HTTP_PROXY: proxy.envUrl, HTTPS_PROXY: proxy.envUrl, no_proxy: noProxy, NO_PROXY: noProxy };
267
359
  }
268
360
 
269
361
  // ../server/dist/tools/sandbox.js
270
362
  var execAsync = promisify(exec);
271
363
  var execFileAsync = promisify(execFile);
272
- var HOME = homedir2();
273
- var HALO_HOME = path3.join(HOME, ".halo");
274
- var EMPTY_MASK_FILE = path3.join(HALO_HOME, ".sandbox-empty");
364
+ var HOME = homedir3();
365
+ var HALO_HOME = path4.join(HOME, ".halo");
366
+ var EMPTY_MASK_FILE = path4.join(HALO_HOME, ".sandbox-empty");
275
367
  var DEFAULT_HIDDEN_DIRS = [
276
368
  "~/.halo/secrets",
277
369
  "~/.aws",
@@ -313,9 +405,9 @@ var DEFAULT_HIDDEN_FILES = [
313
405
  ];
314
406
 
315
407
  // ../server/dist/models/registry.js
316
- import fs2 from "node:fs";
317
- import path4 from "node:path";
318
- import YAML from "yaml";
408
+ import fs3 from "node:fs";
409
+ import path5 from "node:path";
410
+ import YAML2 from "yaml";
319
411
  var MODEL_RUNTIME_NAMES = [
320
412
  "anthropic-messages",
321
413
  "openai-chat",
@@ -346,15 +438,15 @@ function logOnce(msg) {
346
438
  function readProviderDir(dir, source) {
347
439
  let names;
348
440
  try {
349
- names = fs2.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".yaml")).map((e) => e.name).sort();
441
+ names = fs3.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".yaml")).map((e) => e.name).sort();
350
442
  } catch {
351
443
  return [];
352
444
  }
353
445
  const out = [];
354
446
  for (const name of names) {
355
- const file = path4.join(dir, name);
447
+ const file = path5.join(dir, name);
356
448
  try {
357
- const parsed = YAML.parse(fs2.readFileSync(file, "utf-8"));
449
+ const parsed = YAML2.parse(fs3.readFileSync(file, "utf-8"));
358
450
  if (parsed && typeof parsed === "object" && typeof parsed.id === "string") {
359
451
  out.push({
360
452
  id: parsed.id,
@@ -402,34 +494,34 @@ function loadProviders(bundledDir = globalModelsDir(), hubDir = globalHubModelsD
402
494
  }
403
495
 
404
496
  // ../server/dist/extensions/registry.js
405
- import fs4 from "node:fs";
406
- import path7 from "node:path";
497
+ import fs5 from "node:fs";
498
+ import path8 from "node:path";
407
499
 
408
500
  // ../server/dist/extensions/state.js
409
- import fs3 from "node:fs";
410
- import path6 from "node:path";
501
+ import fs4 from "node:fs";
502
+ import path7 from "node:path";
411
503
 
412
504
  // ../server/dist/init.js
413
- import path5 from "node:path";
505
+ import path6 from "node:path";
414
506
  import { fileURLToPath } from "node:url";
415
- import YAML2 from "yaml";
416
- var __dirname = path5.dirname(fileURLToPath(import.meta.url));
417
- var TEMPLATES_DIR = path5.resolve(__dirname, "..", "templates");
507
+ import YAML3 from "yaml";
508
+ var __dirname = path6.dirname(fileURLToPath(import.meta.url));
509
+ var TEMPLATES_DIR = path6.resolve(__dirname, "..", "templates");
418
510
  var DOCS_SRC_CANDIDATES = [
419
- path5.resolve(__dirname, "..", "bundled-docs"),
420
- path5.resolve(__dirname, "..", "..", "..", ".halo", "docs")
511
+ path6.resolve(__dirname, "..", "bundled-docs"),
512
+ path6.resolve(__dirname, "..", "..", "..", ".halo", "docs")
421
513
  ];
422
514
 
423
515
  // ../server/dist/extensions/state.js
424
516
  function extensionStatePath() {
425
- return path6.join(globalExtensionsDir(), ".state.json");
517
+ return path7.join(globalExtensionsDir(), ".state.json");
426
518
  }
427
519
  var cache = null;
428
520
  function load() {
429
521
  const file = extensionStatePath();
430
522
  let st;
431
523
  try {
432
- st = fs3.statSync(file);
524
+ st = fs4.statSync(file);
433
525
  } catch {
434
526
  return { states: {}, disabled: /* @__PURE__ */ new Set() };
435
527
  }
@@ -438,7 +530,7 @@ function load() {
438
530
  return cache;
439
531
  let states = null;
440
532
  try {
441
- const raw = JSON.parse(fs3.readFileSync(file, "utf-8"));
533
+ const raw = JSON.parse(fs4.readFileSync(file, "utf-8"));
442
534
  if (typeof raw === "object" && raw !== null && !Array.isArray(raw))
443
535
  states = raw;
444
536
  } catch {
@@ -683,17 +775,17 @@ function parseManifest(raw, dirName, entryExists) {
683
775
  ...m.source !== void 0 ? { source: m.source } : {}
684
776
  };
685
777
  }
686
- function readExtensionDir(dir, id = path7.basename(dir)) {
778
+ function readExtensionDir(dir, id = path8.basename(dir)) {
687
779
  let raw;
688
780
  try {
689
- raw = JSON.parse(fs4.readFileSync(path7.join(dir, MANIFEST_FILE), "utf-8"));
781
+ raw = JSON.parse(fs5.readFileSync(path8.join(dir, MANIFEST_FILE), "utf-8"));
690
782
  } catch (err) {
691
783
  const code = err.code;
692
784
  return { id, error: code === "ENOENT" ? `${MANIFEST_FILE} missing` : `${MANIFEST_FILE} unreadable: ${err instanceof Error ? err.message : String(err)}` };
693
785
  }
694
786
  const parsed = parseManifest(raw, id, (entry) => {
695
787
  try {
696
- return fs4.statSync(path7.join(dir, entry)).isFile();
788
+ return fs5.statSync(path8.join(dir, entry)).isFile();
697
789
  } catch {
698
790
  return false;
699
791
  }
@@ -702,7 +794,7 @@ function readExtensionDir(dir, id = path7.basename(dir)) {
702
794
  return { id, error: parsed.error };
703
795
  let installedAt = 0;
704
796
  try {
705
- installedAt = Math.round(fs4.statSync(dir).mtimeMs);
797
+ installedAt = Math.round(fs5.statSync(dir).mtimeMs);
706
798
  } catch {
707
799
  }
708
800
  return { ...parsed, installedAt };
@@ -713,9 +805,9 @@ function getSnapshot() {
713
805
  }
714
806
 
715
807
  // ../server/dist/agents/skill-sources.js
716
- import fs5 from "node:fs";
717
- import path8 from "node:path";
718
- import { homedir as homedir3 } from "node:os";
808
+ import fs6 from "node:fs";
809
+ import path9 from "node:path";
810
+ import { homedir as homedir4 } from "node:os";
719
811
 
720
812
  // ../server/dist/routes/mtime-cache.js
721
813
  function createMtimeCache() {
@@ -733,11 +825,11 @@ function createMtimeCache() {
733
825
  }
734
826
 
735
827
  // ../server/dist/agents/skill-sources.js
736
- var GLOBAL_SKILLS_DIR = path8.join(homedir3(), ".halo", "global", "skills");
828
+ var GLOBAL_SKILLS_DIR = path9.join(homedir4(), ".halo", "global", "skills");
737
829
  var SKILL_ID_RE = /^[\w-]+$/;
738
830
  function isFile(p) {
739
831
  try {
740
- return fs5.statSync(p).isFile();
832
+ return fs6.statSync(p).isFile();
741
833
  } catch {
742
834
  return false;
743
835
  }
@@ -745,13 +837,13 @@ function isFile(p) {
745
837
  function childDirs(parent) {
746
838
  let names;
747
839
  try {
748
- names = fs5.readdirSync(parent);
840
+ names = fs6.readdirSync(parent);
749
841
  } catch {
750
842
  return [];
751
843
  }
752
844
  return names.filter((n) => {
753
845
  try {
754
- return fs5.statSync(path8.join(parent, n)).isDirectory();
846
+ return fs6.statSync(path9.join(parent, n)).isDirectory();
755
847
  } catch {
756
848
  return false;
757
849
  }
@@ -766,7 +858,7 @@ function extensionSkillDirs() {
766
858
  const root = extensionsRoot();
767
859
  let extIds;
768
860
  try {
769
- extIds = fs5.readdirSync(root);
861
+ extIds = fs6.readdirSync(root);
770
862
  } catch {
771
863
  return [];
772
864
  }
@@ -774,31 +866,31 @@ function extensionSkillDirs() {
774
866
  const taken = /* @__PURE__ */ new Set();
775
867
  let off;
776
868
  for (const extId of extIds.filter(isExtensionId).sort()) {
777
- const extDir = path8.join(root, extId);
778
- const ids = childDirs(path8.join(extDir, "skills"));
869
+ const extDir = path9.join(root, extId);
870
+ const ids = childDirs(path9.join(extDir, "skills"));
779
871
  if (ids.length === 0)
780
872
  continue;
781
873
  off ??= disabledExtensionIds();
782
874
  if (off.has(extId))
783
875
  continue;
784
- const manifest = path8.join(extDir, MANIFEST_FILE);
876
+ const manifest = path9.join(extDir, MANIFEST_FILE);
785
877
  let mtimeMs;
786
878
  try {
787
- mtimeMs = fs5.statSync(manifest).mtimeMs;
879
+ mtimeMs = fs6.statSync(manifest).mtimeMs;
788
880
  } catch {
789
881
  continue;
790
882
  }
791
883
  let entry = _manifestEntry.get(manifest, mtimeMs);
792
884
  if (entry === void 0) {
793
885
  const ext = readExtensionDir(extDir);
794
- entry = _manifestEntry.set(manifest, mtimeMs, "error" in ext ? null : path8.join(extDir, ext.entry));
886
+ entry = _manifestEntry.set(manifest, mtimeMs, "error" in ext ? null : path9.join(extDir, ext.entry));
795
887
  }
796
888
  if (!entry || !isFile(entry))
797
889
  continue;
798
890
  const ignored = [];
799
891
  for (const id of ids.sort()) {
800
- const dir = path8.join(extDir, "skills", id);
801
- if (!fs5.existsSync(path8.join(dir, "SKILL.md")))
892
+ const dir = path9.join(extDir, "skills", id);
893
+ if (!fs6.existsSync(path9.join(dir, "SKILL.md")))
802
894
  continue;
803
895
  if (!isExtensionSkillId(extId, id) || taken.has(id)) {
804
896
  ignored.push(id);
@@ -823,28 +915,28 @@ function listSkillDirs(workspaceRoot) {
823
915
  seen.add(s.id);
824
916
  };
825
917
  if (workspaceRoot) {
826
- const wsDir = path8.join(workspaceRoot, ".halo", "skills");
918
+ const wsDir = path9.join(workspaceRoot, ".halo", "skills");
827
919
  for (const id of childDirs(wsDir))
828
- add({ id, dir: path8.join(wsDir, id), scope: "workspace" });
920
+ add({ id, dir: path9.join(wsDir, id), scope: "workspace" });
829
921
  }
830
922
  for (const id of childDirs(GLOBAL_SKILLS_DIR))
831
- add({ id, dir: path8.join(GLOBAL_SKILLS_DIR, id), scope: "global" });
923
+ add({ id, dir: path9.join(GLOBAL_SKILLS_DIR, id), scope: "global" });
832
924
  for (const s of extensionSkillDirs())
833
925
  add(s);
834
926
  return out;
835
927
  }
836
928
 
837
929
  // ../server/dist/a2a/remotes.js
838
- import fs6 from "node:fs";
839
- import path9 from "node:path";
840
- import YAML3 from "yaml";
930
+ import fs7 from "node:fs";
931
+ import path10 from "node:path";
932
+ import YAML4 from "yaml";
841
933
  function remotesFile() {
842
- return path9.join(secretsDir(), "a2a-remotes.yaml");
934
+ return path10.join(secretsDir(), "a2a-remotes.yaml");
843
935
  }
844
936
  function loadRemotes() {
845
937
  let doc;
846
938
  try {
847
- doc = YAML3.parse(fs6.readFileSync(remotesFile(), "utf8")) ?? {};
939
+ doc = YAML4.parse(fs7.readFileSync(remotesFile(), "utf8")) ?? {};
848
940
  } catch {
849
941
  return {};
850
942
  }
@@ -858,7 +950,7 @@ function loadRemotes() {
858
950
  }
859
951
 
860
952
  // ../server/dist/settings-schema.js
861
- var GLOBAL_AGENTS_DIR = path10.join(homedir4(), ".halo", "global", "agents");
953
+ var GLOBAL_AGENTS_DIR = path11.join(homedir5(), ".halo", "global", "agents");
862
954
  var DEFAULT_A2A_URL_ALLOWLIST = "100.64.0.0/10,*.ts.net";
863
955
  var DEFAULT_FILES_SUMMARY = DEFAULT_HIDDEN_FILES.filter((f) => !/-(wal|shm)$/.test(f)).join(", ");
864
956
  function generalSection() {
@@ -920,6 +1012,11 @@ function generalSection() {
920
1012
  // a workspace must not widen; public_url names this server to peers.
921
1013
  { key: "a2a.public_url", type: "string", globalOnly: true, description: "Origin peers use to reach this server for A2A (e.g. http://myhost.tailnet.ts.net:9527): the agent card's interface URL and the webhook URL given to remotes. Empty = derived from the request Host. Env HALO_A2A_PUBLIC_URL overrides.", description_zh: "\u5BF9\u7AEF\u8BBF\u95EE\u672C\u670D\u52A1\u7684 A2A \u6E90\u5730\u5740\uFF08\u5982 http://myhost.tailnet.ts.net:9527\uFF09\uFF1A\u7528\u4E8E agent card \u7684\u63A5\u53E3 URL \u548C\u4EA4\u7ED9\u8FDC\u7AEF\u7684 webhook URL\u3002\u7559\u7A7A\u5219\u6309\u8BF7\u6C42 Host \u63A8\u5BFC\u3002\u73AF\u5883\u53D8\u91CF HALO_A2A_PUBLIC_URL \u4F18\u5148\u3002", default: "" },
922
1014
  { key: "a2a.url_allowlist", type: "string", globalOnly: true, description: "Comma-separated CIDRs / host patterns A2A may reach besides public https (push webhooks, remote cards and RPC). Private / loopback ranges are refused unless listed; plain http is allowed only to listed hosts / ranges. Link-local (169.254.0.0/16, fe80::/10) is always refused.", description_zh: "A2A \u9664\u516C\u7F51 https \u4E4B\u5916\u5141\u8BB8\u8BBF\u95EE\u7684 CIDR / \u4E3B\u673A\u6A21\u5F0F\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u7528\u4E8E push webhook\u3001\u8FDC\u7AEF card \u548C RPC\uFF09\u3002\u79C1\u7F51 / \u56DE\u73AF\u5730\u5740\u5FC5\u987B\u5217\u51FA\u624D\u5141\u8BB8\uFF1B\u660E\u6587 http \u53EA\u5141\u8BB8\u5217\u51FA\u7684\u4E3B\u673A / \u7F51\u6BB5\u3002\u94FE\u8DEF\u672C\u5730\u5730\u5740\uFF08169.254.0.0/16\u3001fe80::/10\uFF09\u59CB\u7EC8\u62D2\u7EDD\u3002", default: DEFAULT_A2A_URL_ALLOWLIST },
1015
+ // Outbound proxy (outbound-proxy.ts). Global-only: a machine's way out,
1016
+ // not a workspace's. Read per spawn / request — no restart.
1017
+ { key: "outbound_proxy.enabled", type: "boolean", globalOnly: true, description: "Route outbound traffic through the proxy below: shell_exec, newly opened Terminals, notebook kernels and evolution runs get http_proxy / https_proxy (and the upper-case forms) in their env, and web_fetch and A2A requests to remotes go through it. localhost / 127.0.0.1 / ::1 always connect directly. Off = Halo sets no proxy vars (any the server inherited still pass). Applies on the next command / request; a Terminal already open keeps its env.", description_zh: "\u8BA9\u51FA\u7AD9\u6D41\u91CF\u8D70\u4E0B\u9762\u7684\u4EE3\u7406\uFF1Ashell_exec\u3001\u65B0\u5F00\u7684 Terminal\u3001notebook \u5185\u6838\u548C evolution \u8FD0\u884C\u7684\u73AF\u5883\u53D8\u91CF\u91CC\u4F1A\u8BBE\u4E0A http_proxy / https_proxy\uFF08\u5927\u5199\u5F62\u5F0F\u4E5F\u4E00\u8D77\u8BBE\uFF09\uFF0Cweb_fetch \u548C\u53D1\u5F80 A2A \u8FDC\u7AEF\u7684\u8BF7\u6C42\u4E5F\u8D70\u4EE3\u7406\u3002localhost / 127.0.0.1 / ::1 \u59CB\u7EC8\u76F4\u8FDE\u3002\u5173\u95ED\u540E Halo \u4E0D\u8BBE\u4EFB\u4F55\u4EE3\u7406\u53D8\u91CF\uFF08\u670D\u52A1\u8FDB\u7A0B\u672C\u8EAB\u7EE7\u627F\u7684\u7167\u5E38\u4F20\u4E0B\u53BB\uFF09\u3002\u4E0B\u4E00\u6761\u547D\u4EE4 / \u8BF7\u6C42\u5373\u751F\u6548\uFF1B\u5DF2\u7ECF\u6253\u5F00\u7684 Terminal \u4FDD\u6301\u539F\u6765\u7684\u73AF\u5883\u3002", default: "false" },
1018
+ { key: "outbound_proxy.url", type: "string", globalOnly: true, description: "Proxy address, http:// or https:// (e.g. http://127.0.0.1:12334 for a local Hiddify / Clash). No user:pass@ in the URL \u2014 put credentials in Token. Empty = proxy off even when enabled.", description_zh: "\u4EE3\u7406\u5730\u5740\uFF0Chttp:// \u6216 https://\uFF08\u5982\u672C\u5730 Hiddify / Clash \u7684 http://127.0.0.1:12334\uFF09\u3002URL \u91CC\u4E0D\u80FD\u5E26 user:pass@\uFF0C\u51ED\u8BC1\u586B\u5230 Token\u3002\u7559\u7A7A\u65F6\u5373\u4F7F\u6253\u5F00\u5F00\u5173\u4E5F\u4E0D\u8D70\u4EE3\u7406\u3002", default: "", placeholder: "http://127.0.0.1:12334" },
1019
+ { key: "outbound_proxy.token", type: "string", secret: true, globalOnly: true, description: "Optional proxy credential: user:password, or a bare token (sent as the user name, empty password). Sent as Basic auth \u2014 in the proxy URL's user-info for child processes (the form curl / git / pip read), as Proxy-Authorization for web_fetch and A2A. Leave empty for a proxy without auth.", description_zh: "\u53EF\u9009\u7684\u4EE3\u7406\u51ED\u8BC1\uFF1Auser:password\uFF0C\u6216\u5355\u72EC\u4E00\u4E2A token\uFF08\u4F5C\u4E3A\u7528\u6237\u540D\uFF0C\u5BC6\u7801\u4E3A\u7A7A\uFF09\u3002\u4EE5 Basic \u8BA4\u8BC1\u53D1\u9001\u2014\u2014\u5B50\u8FDB\u7A0B\u653E\u5728\u4EE3\u7406 URL \u7684 user-info \u91CC\uFF08curl / git / pip \u90FD\u8BA4\u8FD9\u79CD\u5199\u6CD5\uFF09\uFF0Cweb_fetch \u548C A2A \u7528 Proxy-Authorization \u5934\u3002\u4EE3\u7406\u4E0D\u9700\u8981\u9274\u6743\u5C31\u7559\u7A7A\u3002", default: "" },
923
1020
  // self-evolution (see plans/self-evolution.md). The level and the
924
1021
  // auto-apply threshold are per workspace (read where a run's workspace is
925
1022
  // known: the pre-compact trigger, the wrapper's auto-apply verdict and
@@ -956,11 +1053,11 @@ var _yamlCache = createMtimeCache();
956
1053
  var _skillNameCache = createMtimeCache();
957
1054
  function readYamlFile(p) {
958
1055
  try {
959
- const { mtimeMs } = fs7.statSync(p);
1056
+ const { mtimeMs } = fs8.statSync(p);
960
1057
  const hit = _yamlCache.get(p, mtimeMs);
961
1058
  if (hit !== void 0)
962
1059
  return hit;
963
- const parsed = YAML4.parse(fs7.readFileSync(p, "utf-8"));
1060
+ const parsed = YAML5.parse(fs8.readFileSync(p, "utf-8"));
964
1061
  return _yamlCache.set(p, mtimeMs, parsed && typeof parsed === "object" ? parsed : null);
965
1062
  } catch {
966
1063
  return null;
@@ -1015,8 +1112,8 @@ function providerSections() {
1015
1112
  return sections;
1016
1113
  }
1017
1114
  function buildSkillSection(skillsDir, skillId) {
1018
- const configPath = path10.join(skillsDir, skillId, "config.yaml");
1019
- if (!fs7.existsSync(configPath))
1115
+ const configPath = path11.join(skillsDir, skillId, "config.yaml");
1116
+ if (!fs8.existsSync(configPath))
1020
1117
  return null;
1021
1118
  const parsed = readYamlFile(configPath);
1022
1119
  if (!parsed)
@@ -1027,11 +1124,11 @@ function buildSkillSection(skillsDir, skillId) {
1027
1124
  return null;
1028
1125
  let displayName = skillId;
1029
1126
  try {
1030
- const mdPath = path10.join(skillsDir, skillId, "SKILL.md");
1031
- const { mtimeMs } = fs7.statSync(mdPath);
1127
+ const mdPath = path11.join(skillsDir, skillId, "SKILL.md");
1128
+ const { mtimeMs } = fs8.statSync(mdPath);
1032
1129
  let name = _skillNameCache.get(mdPath, mtimeMs);
1033
1130
  if (name === void 0) {
1034
- const m = fs7.readFileSync(mdPath, "utf-8").match(/^---\s*\n([\s\S]*?)\n---/);
1131
+ const m = fs8.readFileSync(mdPath, "utf-8").match(/^---\s*\n([\s\S]*?)\n---/);
1035
1132
  const nameMatch = m?.[1].match(/^name:\s*(.+)$/m);
1036
1133
  name = _skillNameCache.set(mdPath, mtimeMs, nameMatch ? nameMatch[1].trim() : null);
1037
1134
  }
@@ -1052,7 +1149,7 @@ function buildSkillSection(skillsDir, skillId) {
1052
1149
  function skillSections(workspaceRoot) {
1053
1150
  const byId = /* @__PURE__ */ new Map();
1054
1151
  for (const { id, dir } of listSkillDirs(workspaceRoot).reverse()) {
1055
- const section = buildSkillSection(path10.dirname(dir), id);
1152
+ const section = buildSkillSection(path11.dirname(dir), id);
1056
1153
  if (section)
1057
1154
  byId.set(section.namespaceId, section);
1058
1155
  }
@@ -1062,7 +1159,7 @@ function agentSections() {
1062
1159
  const sections = [];
1063
1160
  let entries = [];
1064
1161
  try {
1065
- entries = fs7.readdirSync(GLOBAL_AGENTS_DIR, { withFileTypes: true });
1162
+ entries = fs8.readdirSync(GLOBAL_AGENTS_DIR, { withFileTypes: true });
1066
1163
  } catch {
1067
1164
  return sections;
1068
1165
  }
@@ -1070,8 +1167,8 @@ function agentSections() {
1070
1167
  if (!entry.isDirectory())
1071
1168
  continue;
1072
1169
  const agentId = entry.name;
1073
- const configPath = path10.join(GLOBAL_AGENTS_DIR, agentId, "agent-config.yaml");
1074
- if (!fs7.existsSync(configPath))
1170
+ const configPath = path11.join(GLOBAL_AGENTS_DIR, agentId, "agent-config.yaml");
1171
+ if (!fs8.existsSync(configPath))
1075
1172
  continue;
1076
1173
  const parsed = readYamlFile(configPath);
1077
1174
  if (!parsed)
@@ -1082,7 +1179,7 @@ function agentSections() {
1082
1179
  continue;
1083
1180
  let displayName = agentId;
1084
1181
  try {
1085
- const ay = readYamlFile(path10.join(GLOBAL_AGENTS_DIR, agentId, "agent.yaml"));
1182
+ const ay = readYamlFile(path11.join(GLOBAL_AGENTS_DIR, agentId, "agent.yaml"));
1086
1183
  if (ay && typeof ay.name === "string")
1087
1184
  displayName = ay.name;
1088
1185
  } catch {
@@ -1145,23 +1242,15 @@ function extensionSections() {
1145
1242
  }
1146
1243
 
1147
1244
  // ../server/dist/config.js
1148
- var HALO_HOME2 = path11.join(homedir5(), ".halo");
1149
- var HALO_GLOBAL_DIR = path11.join(HALO_HOME2, "global");
1150
- var HALO_SECRETS_DIR = path11.join(HALO_HOME2, "secrets");
1151
- function loadYamlFile(filePath) {
1152
- try {
1153
- const raw = fs8.readFileSync(filePath, "utf-8");
1154
- return YAML5.parse(raw) ?? {};
1155
- } catch {
1156
- return {};
1157
- }
1158
- }
1159
- var SYSTEM_CONFIG_PATH = path11.join(HALO_SECRETS_DIR, "config.yaml");
1245
+ var HALO_HOME2 = path12.join(homedir6(), ".halo");
1246
+ var HALO_GLOBAL_DIR = path12.join(HALO_HOME2, "global");
1247
+ var HALO_SECRETS_DIR = path12.join(HALO_HOME2, "secrets");
1248
+ var SYSTEM_CONFIG_PATH = path12.join(HALO_SECRETS_DIR, "config.yaml");
1160
1249
  var _systemConfigCache = null;
1161
1250
  function getSystemConfig() {
1162
1251
  let mtimeMs = 0;
1163
1252
  try {
1164
- mtimeMs = fs8.statSync(SYSTEM_CONFIG_PATH).mtimeMs;
1253
+ mtimeMs = fs9.statSync(SYSTEM_CONFIG_PATH).mtimeMs;
1165
1254
  } catch {
1166
1255
  }
1167
1256
  if (!_systemConfigCache || _systemConfigCache.mtimeMs !== mtimeMs) {
@@ -1169,19 +1258,6 @@ function getSystemConfig() {
1169
1258
  }
1170
1259
  return _systemConfigCache.data;
1171
1260
  }
1172
- var SETTINGS_PATH = path11.join(HALO_SECRETS_DIR, "settings.yaml");
1173
- var _settingsCache = null;
1174
- function getSettings() {
1175
- let mtimeMs = 0;
1176
- try {
1177
- mtimeMs = fs8.statSync(SETTINGS_PATH).mtimeMs;
1178
- } catch {
1179
- }
1180
- if (!_settingsCache || _settingsCache.mtimeMs !== mtimeMs) {
1181
- _settingsCache = { mtimeMs, data: loadYamlFile(SETTINGS_PATH) };
1182
- }
1183
- return _settingsCache.data;
1184
- }
1185
1261
  function readYamlValue(tree, dotPath) {
1186
1262
  const parts = dotPath.split(".");
1187
1263
  let cur = tree;
@@ -1310,10 +1386,10 @@ function systemString(envKey, configPath, fallback) {
1310
1386
  }
1311
1387
  var _wsSettingsCache = /* @__PURE__ */ new Map();
1312
1388
  function getWorkspaceSettings(workspaceRoot) {
1313
- const p = path11.join(workspaceRoot, ".halo", "settings.yaml");
1389
+ const p = path12.join(workspaceRoot, ".halo", "settings.yaml");
1314
1390
  let mtimeMs = 0;
1315
1391
  try {
1316
- mtimeMs = fs8.statSync(p).mtimeMs;
1392
+ mtimeMs = fs9.statSync(p).mtimeMs;
1317
1393
  } catch {
1318
1394
  }
1319
1395
  const hit = _wsSettingsCache.get(workspaceRoot);
@@ -1520,7 +1596,7 @@ var config = {
1520
1596
  a2a: {
1521
1597
  /** Base dir workspaces are exposed relative to: env HALO_A2A_ROOT, else the user's home. */
1522
1598
  get root() {
1523
- return process.env.HALO_A2A_ROOT?.trim() || homedir5();
1599
+ return process.env.HALO_A2A_ROOT?.trim() || homedir6();
1524
1600
  },
1525
1601
  /** Server origin as peers see it (no trailing slash); '' = derive from the request. */
1526
1602
  get publicUrl() {
@@ -1565,21 +1641,21 @@ var config = {
1565
1641
  };
1566
1642
 
1567
1643
  // ../server/dist/evolution/auto-apply.js
1568
- import fs9 from "node:fs";
1569
- import path12 from "node:path";
1644
+ import fs10 from "node:fs";
1645
+ import path13 from "node:path";
1570
1646
  import YAML6 from "yaml";
1571
1647
  var AUTO_APPLY_FILE = "auto-apply.json";
1572
1648
  var OVERRIDE_DIRS = /* @__PURE__ */ new Set(["agents", "skills", "prompts"]);
1573
1649
  function isDir(p) {
1574
1650
  try {
1575
- return fs9.statSync(p).isDirectory();
1651
+ return fs10.statSync(p).isDirectory();
1576
1652
  } catch {
1577
1653
  return false;
1578
1654
  }
1579
1655
  }
1580
1656
  function readOrNull(p) {
1581
1657
  try {
1582
- return fs9.readFileSync(p);
1658
+ return fs10.readFileSync(p);
1583
1659
  } catch {
1584
1660
  return null;
1585
1661
  }
@@ -1587,33 +1663,33 @@ function readOrNull(p) {
1587
1663
  function walkFiles(root, rel, out = []) {
1588
1664
  let st;
1589
1665
  try {
1590
- st = fs9.statSync(path12.join(root, rel));
1666
+ st = fs10.statSync(path13.join(root, rel));
1591
1667
  } catch {
1592
1668
  return out;
1593
1669
  }
1594
1670
  if (st.isFile())
1595
1671
  out.push(rel);
1596
1672
  else if (st.isDirectory()) {
1597
- for (const e of fs9.readdirSync(path12.join(root, rel)))
1598
- walkFiles(root, path12.join(rel, e), out);
1673
+ for (const e of fs10.readdirSync(path13.join(root, rel)))
1674
+ walkFiles(root, path13.join(rel, e), out);
1599
1675
  }
1600
1676
  return out;
1601
1677
  }
1602
- var toPosix = (rel) => rel.split(path12.sep).join("/");
1678
+ var toPosix = (rel) => rel.split(path13.sep).join("/");
1603
1679
  function isInternalAgentDir(dir) {
1604
- const raw = readOrNull(path12.join(dir, "agent.yaml"));
1680
+ const raw = readOrNull(path13.join(dir, "agent.yaml"));
1605
1681
  return raw !== null && parseYamlLoose(raw.toString("utf-8"))?.internal === true;
1606
1682
  }
1607
1683
  function liveBaseline(dirs, rel) {
1608
- const parts = rel.split(path12.sep);
1684
+ const parts = rel.split(path13.sep);
1609
1685
  if (!OVERRIDE_DIRS.has(parts[0]) || parts.length < 3)
1610
- return { file: path12.join(dirs.mainHalo, rel), newDir: false };
1611
- const dirRel = path12.join(parts[0], parts[1]);
1612
- const internal = (root) => parts[0] === "agents" && isInternalAgentDir(path12.join(root, dirRel));
1613
- if (isDir(path12.join(dirs.mainHalo, dirRel)))
1614
- return { file: path12.join(dirs.mainHalo, rel), newDir: false, internalAgent: internal(dirs.mainHalo) };
1615
- if (isDir(path12.join(dirs.globalHalo, dirRel)))
1616
- return { file: path12.join(dirs.globalHalo, rel), newDir: false, globalDir: dirRel, internalAgent: internal(dirs.globalHalo) };
1686
+ return { file: path13.join(dirs.mainHalo, rel), newDir: false };
1687
+ const dirRel = path13.join(parts[0], parts[1]);
1688
+ const internal = (root) => parts[0] === "agents" && isInternalAgentDir(path13.join(root, dirRel));
1689
+ if (isDir(path13.join(dirs.mainHalo, dirRel)))
1690
+ return { file: path13.join(dirs.mainHalo, rel), newDir: false, internalAgent: internal(dirs.mainHalo) };
1691
+ if (isDir(path13.join(dirs.globalHalo, dirRel)))
1692
+ return { file: path13.join(dirs.globalHalo, rel), newDir: false, globalDir: dirRel, internalAgent: internal(dirs.globalHalo) };
1617
1693
  return { file: null, newDir: true };
1618
1694
  }
1619
1695
  function fileChange(rel, base, after) {
@@ -1635,15 +1711,15 @@ function publishChanges(dirs, files) {
1635
1711
  const base = liveBaseline(dirs, rel);
1636
1712
  if (base.globalDir)
1637
1713
  globalBacked.add(base.globalDir);
1638
- const c = fileChange(rel, base, fs9.readFileSync(full));
1714
+ const c = fileChange(rel, base, fs10.readFileSync(full));
1639
1715
  if (c)
1640
1716
  out.push(c);
1641
1717
  }
1642
1718
  for (const dirRel of globalBacked) {
1643
1719
  for (const rel of walkFiles(dirs.globalHalo, dirRel)) {
1644
- if (fs9.existsSync(path12.join(dirs.sandboxHalo, rel)))
1720
+ if (fs10.existsSync(path13.join(dirs.sandboxHalo, rel)))
1645
1721
  continue;
1646
- out.push({ rel: toPosix(rel), before: fs9.readFileSync(path12.join(dirs.globalHalo, rel), "utf-8"), after: null, newDir: false });
1722
+ out.push({ rel: toPosix(rel), before: fs10.readFileSync(path13.join(dirs.globalHalo, rel), "utf-8"), after: null, newDir: false });
1647
1723
  }
1648
1724
  }
1649
1725
  return out;
@@ -1658,21 +1734,21 @@ function collectChanges(dirs) {
1658
1734
  const base = liveBaseline(dirs, rel);
1659
1735
  if (base.globalDir)
1660
1736
  globalBacked.add(base.globalDir);
1661
- const c = fileChange(rel, base, fs9.readFileSync(path12.join(dirs.sandboxHalo, rel)));
1737
+ const c = fileChange(rel, base, fs10.readFileSync(path13.join(dirs.sandboxHalo, rel)));
1662
1738
  if (c)
1663
1739
  out.push(c);
1664
1740
  }
1665
1741
  for (const rel of walkFiles(dirs.mainHalo, top)) {
1666
1742
  if (seen.has(rel))
1667
1743
  continue;
1668
- out.push({ rel: toPosix(rel), before: fs9.readFileSync(path12.join(dirs.mainHalo, rel), "utf-8"), after: null, newDir: false });
1744
+ out.push({ rel: toPosix(rel), before: fs10.readFileSync(path13.join(dirs.mainHalo, rel), "utf-8"), after: null, newDir: false });
1669
1745
  }
1670
1746
  }
1671
1747
  for (const dirRel of globalBacked) {
1672
1748
  for (const rel of walkFiles(dirs.globalHalo, dirRel)) {
1673
1749
  if (seen.has(rel))
1674
1750
  continue;
1675
- out.push({ rel: toPosix(rel), before: fs9.readFileSync(path12.join(dirs.globalHalo, rel), "utf-8"), after: null, newDir: false });
1751
+ out.push({ rel: toPosix(rel), before: fs10.readFileSync(path13.join(dirs.globalHalo, rel), "utf-8"), after: null, newDir: false });
1676
1752
  }
1677
1753
  }
1678
1754
  return out;
@@ -1884,7 +1960,7 @@ function parseScore(raw) {
1884
1960
  }
1885
1961
  function readScore(file) {
1886
1962
  try {
1887
- return parseScore(JSON.parse(fs9.readFileSync(file, "utf-8")));
1963
+ return parseScore(JSON.parse(fs10.readFileSync(file, "utf-8")));
1888
1964
  } catch {
1889
1965
  return null;
1890
1966
  }
@@ -1963,7 +2039,7 @@ function extractLargerObservation(md) {
1963
2039
  }
1964
2040
  function readObservation(file) {
1965
2041
  try {
1966
- return extractLargerObservation(fs9.readFileSync(file, "utf-8"));
2042
+ return extractLargerObservation(fs10.readFileSync(file, "utf-8"));
1967
2043
  } catch {
1968
2044
  return null;
1969
2045
  }
@@ -2025,11 +2101,11 @@ function failApply(db, applyId, input) {
2025
2101
  }
2026
2102
 
2027
2103
  // ../server/dist/evolution/rollback.js
2028
- import fs10 from "node:fs";
2104
+ import fs11 from "node:fs";
2029
2105
  import { createHash } from "node:crypto";
2030
2106
  import { and as and2, eq as eq2, inArray } from "drizzle-orm";
2031
2107
  function sha256File(file) {
2032
- return createHash("sha256").update(fs10.readFileSync(file)).digest("hex");
2108
+ return createHash("sha256").update(fs11.readFileSync(file)).digest("hex");
2033
2109
  }
2034
2110
 
2035
2111
  // ../server/dist/evolution/evo-wrapper.js
@@ -2091,9 +2167,9 @@ function spawnProc(bin, args, logFd, teePath, stdinInput, timeoutSec) {
2091
2167
  }
2092
2168
  writeLog(logFd, `[wrapper] $ ${bin} ${loggedArgs.join(" ")}${stdinInput === void 0 ? " <last-arg-omitted>" : " <prompt-on-stdin>"}${timeoutSec ? ` (timeout ${timeoutSec}s)` : ""}
2093
2169
  `);
2094
- const tee = teePath ? fs11.createWriteStream(teePath, { flags: "a" }) : null;
2170
+ const tee = teePath ? fs12.createWriteStream(teePath, { flags: "a" }) : null;
2095
2171
  const [spawnBin, spawnArgs] = process.platform === "win32" && bin.endsWith(".cmd") ? ["cmd.exe", ["/c", bin, ...args]] : [bin, args];
2096
- return new Promise((resolve) => {
2172
+ return new Promise((resolve2) => {
2097
2173
  const child = spawn2(spawnBin, spawnArgs, {
2098
2174
  // Pipe stdin only when we have input to write — the cli reads a prompt
2099
2175
  // from stdin when it's not a TTY, which is how long briefs are delivered
@@ -2165,7 +2241,7 @@ function spawnProc(bin, args, logFd, teePath, stdinInput, timeoutSec) {
2165
2241
  tee?.end();
2166
2242
  if (stopping)
2167
2243
  return;
2168
- resolve({ exitCode, stdout, stderr });
2244
+ resolve2({ exitCode, stdout, stderr });
2169
2245
  });
2170
2246
  child.on("error", (err) => {
2171
2247
  liveProcs.delete(killTree);
@@ -2181,17 +2257,17 @@ function spawnProc(bin, args, logFd, teePath, stdinInput, timeoutSec) {
2181
2257
  tee?.end();
2182
2258
  if (stopping)
2183
2259
  return;
2184
- resolve({ exitCode: 1, stdout, stderr: stderr + `
2260
+ resolve2({ exitCode: 1, stdout, stderr: stderr + `
2185
2261
  [spawn error] ${err.message}` });
2186
2262
  });
2187
2263
  });
2188
2264
  }
2189
2265
  function subCliLogPath(runDir) {
2190
- return path13.join(runDir, "sub-cli.log");
2266
+ return path14.join(runDir, "sub-cli.log");
2191
2267
  }
2192
2268
  function appendSubCliHeader(runDir, header) {
2193
2269
  try {
2194
- fs11.appendFileSync(subCliLogPath(runDir), `
2270
+ fs12.appendFileSync(subCliLogPath(runDir), `
2195
2271
  === ${(/* @__PURE__ */ new Date()).toISOString()} ${header} ===
2196
2272
  `);
2197
2273
  } catch {
@@ -2225,10 +2301,10 @@ function buildLanguageClause(langHint, scope) {
2225
2301
  ];
2226
2302
  }
2227
2303
  function readPatchFrontmatter(runDir) {
2228
- const patchPath = path13.join(runDir, "patch.md");
2304
+ const patchPath = path14.join(runDir, "patch.md");
2229
2305
  let raw;
2230
2306
  try {
2231
- raw = fs11.readFileSync(patchPath, "utf-8");
2307
+ raw = fs12.readFileSync(patchPath, "utf-8");
2232
2308
  } catch {
2233
2309
  return null;
2234
2310
  }
@@ -2258,14 +2334,14 @@ function extractTestScenario(fm) {
2258
2334
  return { agentId: o.agentId, testMessage, originalMessage };
2259
2335
  }
2260
2336
  function readEvoContext(runDir, logFd) {
2261
- const p = path13.join(runDir, "evo-context.json");
2262
- if (!fs11.existsSync(p)) {
2337
+ const p = path14.join(runDir, "evo-context.json");
2338
+ if (!fs12.existsSync(p)) {
2263
2339
  writeLog(logFd, `[brief] evo-context.json missing at ${p}
2264
2340
  `);
2265
2341
  return null;
2266
2342
  }
2267
2343
  try {
2268
- return JSON.parse(fs11.readFileSync(p, "utf-8"));
2344
+ return JSON.parse(fs12.readFileSync(p, "utf-8"));
2269
2345
  } catch (err) {
2270
2346
  writeLog(logFd, `[brief] evo-context.json parse failed: ${err instanceof Error ? err.message : String(err)}
2271
2347
  `);
@@ -2273,12 +2349,12 @@ function readEvoContext(runDir, logFd) {
2273
2349
  }
2274
2350
  }
2275
2351
  function dumpSnapshotImages(runDir, logFd) {
2276
- const snapshotPath = path13.join(runDir, "source-snapshot.json");
2277
- if (!fs11.existsSync(snapshotPath))
2352
+ const snapshotPath = path14.join(runDir, "source-snapshot.json");
2353
+ if (!fs12.existsSync(snapshotPath))
2278
2354
  return [];
2279
2355
  let parsed;
2280
2356
  try {
2281
- parsed = JSON.parse(fs11.readFileSync(snapshotPath, "utf-8"));
2357
+ parsed = JSON.parse(fs12.readFileSync(snapshotPath, "utf-8"));
2282
2358
  } catch {
2283
2359
  writeLog(logFd, `[dumpSnapshotImages] snapshot parse failed
2284
2360
  `);
@@ -2287,8 +2363,8 @@ function dumpSnapshotImages(runDir, logFd) {
2287
2363
  const messages = Array.isArray(parsed.rawMessages) ? parsed.rawMessages : [];
2288
2364
  if (messages.length === 0)
2289
2365
  return [];
2290
- const imagesDir = path13.join(runDir, "images");
2291
- fs11.mkdirSync(imagesDir, { recursive: true });
2366
+ const imagesDir = path14.join(runDir, "images");
2367
+ fs12.mkdirSync(imagesDir, { recursive: true });
2292
2368
  const out = [];
2293
2369
  const extByMime = {
2294
2370
  "image/png": "png",
@@ -2327,10 +2403,10 @@ function dumpSnapshotImages(runDir, logFd) {
2327
2403
  continue;
2328
2404
  const ext = extByMime[mediaType] ?? "bin";
2329
2405
  const fileName = c.subBlockIdx == null ? `${m}-${b}.${ext}` : `${m}-${b}-${c.subBlockIdx}.${ext}`;
2330
- const fullPath = path13.join(imagesDir, fileName);
2406
+ const fullPath = path14.join(imagesDir, fileName);
2331
2407
  try {
2332
2408
  const buf = Buffer.from(data, "base64");
2333
- fs11.writeFileSync(fullPath, buf);
2409
+ fs12.writeFileSync(fullPath, buf);
2334
2410
  out.push({
2335
2411
  relPath: `images/${fileName}`,
2336
2412
  msgIdx: m,
@@ -2417,15 +2493,15 @@ function renderEvoContextSections(ctx) {
2417
2493
  lines.push("");
2418
2494
  }
2419
2495
  if (ctx.skills.length > 0) {
2420
- const globalSkillsDir = path13.join(homedir6(), ".halo", "global", "skills");
2496
+ const globalSkillsDir = path14.join(homedir7(), ".halo", "global", "skills");
2421
2497
  lines.push("=== Skills in this workspace ===");
2422
2498
  for (const s of ctx.skills)
2423
2499
  lines.push(` ${s.id} [${s.scope}]`);
2424
2500
  lines.push("");
2425
2501
  lines.push("To read a skill's content, file_read its SKILL.md (you have full");
2426
2502
  lines.push("file access \u2014 absolute paths resolve):");
2427
- lines.push(` [workspace] \u2192 ${path13.join("<workspace>", ".halo", "skills", "<id>", "SKILL.md")}`);
2428
- lines.push(` [global] / [builtin] \u2192 ${path13.join(globalSkillsDir, "<id>", "SKILL.md")}`);
2503
+ lines.push(` [workspace] \u2192 ${path14.join("<workspace>", ".halo", "skills", "<id>", "SKILL.md")}`);
2504
+ lines.push(` [global] / [builtin] \u2192 ${path14.join(globalSkillsDir, "<id>", "SKILL.md")}`);
2429
2505
  lines.push("A [global]/[builtin] skill is NOT inside your sandbox \u2014 read it");
2430
2506
  lines.push(`from ${globalSkillsDir} directly; don't glob the sandbox for it.`);
2431
2507
  lines.push("Sibling resource files live next to SKILL.md in that same dir.");
@@ -2448,13 +2524,13 @@ function buildDraftBrief(args) {
2448
2524
  ];
2449
2525
  if (args.userHint)
2450
2526
  lines.push(`Reviewer hint: ${args.userHint}`);
2451
- lines.push("", "The triggering agent's full message log lives on disk:", ` ${path13.join(args.runDir, "tool-flow.md")} \u2014 clipped, fast skim`, ` ${path13.join(args.runDir, "source-snapshot.json")} \u2014 full rawMessages`, "", "Read tool-flow.md first to understand what the user wanted, what the", "agent did, and where it diverged. Each tool_result is clipped to a", "~200-char peek (with the `is_error` flag when the tool failed) \u2014", "enough to see the *shape* of the flow without re-tokenizing large", "grep / shell outputs. Fall back to source-snapshot.json only for", "messages whose full tool result actually matters to your patch.", "", ...renderEvoContextSections(args.evoContext), ...renderImageManifest(args.images), "=== Non-text media note ===", "", "Inline images in source-snapshot.json are base64 \u2014 you can't read them", "directly. The `images/` dump", "(see manifest above) decodes them to disk so you can `view_image` if", "a specific image is load-bearing. Video, audio, and binary documents", "(PDF, etc.) are even more limited: at trigger time the source agent", "worked with them via shell tooling (ffmpeg, pdftotext, etc.), and your", "view is limited to the textual results those tools produced (visible", "inside source-snapshot.json as tool_result text).", "", "When the patch you have in mind requires understanding what's INSIDE", "the media file (a video's visual content, an audio's acoustic", "content, a PDF's layout), the dry-run test scenario can't reliably", "verify it \u2014 skipping the run is the right call. When the patch is", "about the shell-tooling pattern itself (how to call ffmpeg, how to", "grep its output, how to handle credential errors), it can be", "verified at the textual level and the run proceeds normally.", "", "=== Task ===", "", "Decide what change in the prompt surface above would help the next time", "a similar conversation comes up. Three shapes, in order of preference:", "rewrite an existing rule; tighten / reorganize what's there; or, only", "when neither applies, add a new rule.", "", `Sandbox path: ${evoSandboxDir(args.runDir)}`, "", `When you have a change worth making, write ${path13.join(args.runDir, "patch.md")}`, "(frontmatter + body) and one new file at", `${evoSandboxHaloDir(args.runDir)}/<target> with the`, "full new contents. AGENT.md describes the patch.md schema.", "", "When the conversation has no signal worth a patch (rule already covers,", "too short to learn from, agent did fine, depends on media content", `you can't see), write ${path13.join(args.runDir, ".skip.md")} with a`, "one or two sentence reason instead, and skip the rest.", "", ...buildLanguageClause(args.langHint, "all natural-language content (patch.md body, prose drafted into sandbox files, .skip.md body)"));
2527
+ lines.push("", "The triggering agent's full message log lives on disk:", ` ${path14.join(args.runDir, "tool-flow.md")} \u2014 clipped, fast skim`, ` ${path14.join(args.runDir, "source-snapshot.json")} \u2014 full rawMessages`, "", "Read tool-flow.md first to understand what the user wanted, what the", "agent did, and where it diverged. Each tool_result is clipped to a", "~200-char peek (with the `is_error` flag when the tool failed) \u2014", "enough to see the *shape* of the flow without re-tokenizing large", "grep / shell outputs. Fall back to source-snapshot.json only for", "messages whose full tool result actually matters to your patch.", "", ...renderEvoContextSections(args.evoContext), ...renderImageManifest(args.images), "=== Non-text media note ===", "", "Inline images in source-snapshot.json are base64 \u2014 you can't read them", "directly. The `images/` dump", "(see manifest above) decodes them to disk so you can `view_image` if", "a specific image is load-bearing. Video, audio, and binary documents", "(PDF, etc.) are even more limited: at trigger time the source agent", "worked with them via shell tooling (ffmpeg, pdftotext, etc.), and your", "view is limited to the textual results those tools produced (visible", "inside source-snapshot.json as tool_result text).", "", "When the patch you have in mind requires understanding what's INSIDE", "the media file (a video's visual content, an audio's acoustic", "content, a PDF's layout), the dry-run test scenario can't reliably", "verify it \u2014 skipping the run is the right call. When the patch is", "about the shell-tooling pattern itself (how to call ffmpeg, how to", "grep its output, how to handle credential errors), it can be", "verified at the textual level and the run proceeds normally.", "", "=== Task ===", "", "Decide what change in the prompt surface above would help the next time", "a similar conversation comes up. Three shapes, in order of preference:", "rewrite an existing rule; tighten / reorganize what's there; or, only", "when neither applies, add a new rule.", "", `Sandbox path: ${evoSandboxDir(args.runDir)}`, "", `When you have a change worth making, write ${path14.join(args.runDir, "patch.md")}`, "(frontmatter + body) and one new file at", `${evoSandboxHaloDir(args.runDir)}/<target> with the`, "full new contents. AGENT.md describes the patch.md schema.", "", "When the conversation has no signal worth a patch (rule already covers,", "too short to learn from, agent did fine, depends on media content", `you can't see), write ${path14.join(args.runDir, ".skip.md")} with a`, "one or two sentence reason instead, and skip the rest.", "", ...buildLanguageClause(args.langHint, "all natural-language content (patch.md body, prose drafted into sandbox files, .skip.md body)"));
2452
2528
  return lines.join("\n");
2453
2529
  }
2454
2530
  function buildFixBrief(args) {
2455
2531
  let failLogText = "";
2456
2532
  try {
2457
- failLogText = fs11.readFileSync(args.failLogPath, "utf-8");
2533
+ failLogText = fs12.readFileSync(args.failLogPath, "utf-8");
2458
2534
  } catch {
2459
2535
  failLogText = `(failed to read ${args.failLogPath})`;
2460
2536
  }
@@ -2469,7 +2545,7 @@ function buildFixBrief(args) {
2469
2545
  `Fix attempt: ${args.attempt}/${args.maxAttempts}`,
2470
2546
  "",
2471
2547
  `An earlier draft pass produced patch.md + a sandbox at`,
2472
- `${path13.join(args.runDir, "patch.md")} and`,
2548
+ `${path14.join(args.runDir, "patch.md")} and`,
2473
2549
  `${evoSandboxHaloDir(args.runDir)} respectively.`,
2474
2550
  "The dry-run failed; the wrapper's failure log:",
2475
2551
  "",
@@ -2490,16 +2566,16 @@ function buildFixBrief(args) {
2490
2566
  function buildScoreBrief(args) {
2491
2567
  function readOrEmpty(p, label) {
2492
2568
  try {
2493
- return fs11.readFileSync(p, "utf-8");
2569
+ return fs12.readFileSync(p, "utf-8");
2494
2570
  } catch (err) {
2495
2571
  writeLog(args.logFd, `[buildScoreBrief] ${label} read failed: ${err instanceof Error ? err.message : String(err)}
2496
2572
  `);
2497
2573
  return `(${label} unavailable)`;
2498
2574
  }
2499
2575
  }
2500
- const patchMd = readOrEmpty(path13.join(args.runDir, "patch.md"), "patch.md");
2501
- const dryRun = readOrEmpty(path13.join(args.runDir, "dry-run-output.txt"), "dry-run-output.txt");
2502
- const meta = readOrEmpty(path13.join(args.runDir, "meta.json"), "meta.json");
2576
+ const patchMd = readOrEmpty(path14.join(args.runDir, "patch.md"), "patch.md");
2577
+ const dryRun = readOrEmpty(path14.join(args.runDir, "dry-run-output.txt"), "dry-run-output.txt");
2578
+ const meta = readOrEmpty(path14.join(args.runDir, "meta.json"), "meta.json");
2503
2579
  return [
2504
2580
  "You are running as the Evolution scorer.",
2505
2581
  "",
@@ -2510,8 +2586,8 @@ function buildScoreBrief(args) {
2510
2586
  "write score.json with the absolute path given below)",
2511
2587
  "",
2512
2588
  `The original conversation lives on disk:`,
2513
- ` ${path13.join(args.runDir, "tool-flow.md")} \u2014 clipped, fast skim`,
2514
- ` ${path13.join(args.runDir, "source-snapshot.json")} \u2014 full rawMessages, when needed`,
2589
+ ` ${path14.join(args.runDir, "tool-flow.md")} \u2014 clipped, fast skim`,
2590
+ ` ${path14.join(args.runDir, "source-snapshot.json")} \u2014 full rawMessages, when needed`,
2515
2591
  "Read tool-flow.md first to find the user turn matching",
2516
2592
  "`testScenario.originalMessage` and the assistant reply that followed",
2517
2593
  "it. Fall back to source-snapshot.json only when the full tool result",
@@ -2561,7 +2637,7 @@ function buildScoreBrief(args) {
2561
2637
  "find the assistant turn that followed it in tool-flow.md (or",
2562
2638
  "source-snapshot.json) \u2014 that's your baseline. Compare baseline to",
2563
2639
  "dry-run-output.txt, rate lint / behavior / scope, write",
2564
- `${path13.join(args.runDir, "score.json")} with the shape AGENT.md describes.`,
2640
+ `${path14.join(args.runDir, "score.json")} with the shape AGENT.md describes.`,
2565
2641
  "",
2566
2642
  ...buildLanguageClause(args.langHint, 'the score.json "notes" field'),
2567
2643
  'The numeric fields and "confidence" enum stay in their canonical form.'
@@ -2604,14 +2680,14 @@ async function phaseDraft(args) {
2604
2680
  return outcome;
2605
2681
  }
2606
2682
  function judgeDraft(runDir, workspacePath, exitCode) {
2607
- if (fs11.existsSync(path13.join(runDir, ".skip.md"))) {
2683
+ if (fs12.existsSync(path14.join(runDir, ".skip.md"))) {
2608
2684
  return { outcome: "skipped", why: ".skip.md present \u2014 evo declared skip" };
2609
2685
  }
2610
2686
  if (exitCode !== 0) {
2611
2687
  const timeout = exitCode === 124 ? " (timeout)" : "";
2612
2688
  return { outcome: "failed", why: `drafter exited ${exitCode}${timeout} \u2014 draft interrupted, not trusted` };
2613
2689
  }
2614
- if (!fs11.existsSync(path13.join(runDir, "patch.md"))) {
2690
+ if (!fs12.existsSync(path14.join(runDir, "patch.md"))) {
2615
2691
  return { outcome: "failed", why: "patch.md missing" };
2616
2692
  }
2617
2693
  const target = readPatchFrontmatter(runDir)?.target;
@@ -2619,16 +2695,16 @@ function judgeDraft(runDir, workspacePath, exitCode) {
2619
2695
  return { outcome: "drafted", why: "patch.md present, no target in frontmatter \u2014 target file not checked" };
2620
2696
  }
2621
2697
  const sandboxDir = evoSandboxDir(runDir);
2622
- const rel = path13.relative(sandboxDir, path13.resolve(sandboxDir, target));
2623
- if (!rel.startsWith(".halo" + path13.sep)) {
2698
+ const rel = path14.relative(sandboxDir, path14.resolve(sandboxDir, target));
2699
+ if (!rel.startsWith(".halo" + path14.sep)) {
2624
2700
  return { outcome: "failed", why: `target ${JSON.stringify(target)} does not resolve under sandbox/.halo/` };
2625
2701
  }
2626
- const sandboxFile = path13.join(sandboxDir, rel);
2627
- if (!fs11.statSync(sandboxFile, { throwIfNoEntry: false })?.isFile()) {
2702
+ const sandboxFile = path14.join(sandboxDir, rel);
2703
+ if (!fs12.statSync(sandboxFile, { throwIfNoEntry: false })?.isFile()) {
2628
2704
  return { outcome: "failed", why: `target not written to sandbox: ${target}` };
2629
2705
  }
2630
- const wsFile = path13.join(workspacePath, rel);
2631
- if (fs11.statSync(wsFile, { throwIfNoEntry: false })?.isFile() && fs11.readFileSync(wsFile).equals(fs11.readFileSync(sandboxFile))) {
2706
+ const wsFile = path14.join(workspacePath, rel);
2707
+ if (fs12.statSync(wsFile, { throwIfNoEntry: false })?.isFile() && fs12.readFileSync(wsFile).equals(fs12.readFileSync(sandboxFile))) {
2632
2708
  return { outcome: "failed", why: `target unchanged from workspace: ${target}` };
2633
2709
  }
2634
2710
  return { outcome: "drafted", why: `patch.md + target ${target} present` };
@@ -2655,15 +2731,15 @@ async function phaseDryRun(args) {
2655
2731
  }
2656
2732
  async function tryDryRun(args, attemptIndex) {
2657
2733
  const sandboxDir = evoSandboxDir(args.runDir);
2658
- const outputPath = path13.join(args.runDir, "dry-run-output.txt");
2659
- const failLogPath = path13.join(args.runDir, `dry-run-fail-${attemptIndex}.log`);
2734
+ const outputPath = path14.join(args.runDir, "dry-run-output.txt");
2735
+ const failLogPath = path14.join(args.runDir, `dry-run-fail-${attemptIndex}.log`);
2660
2736
  const cli = resolveHaloCli();
2661
2737
  const fm = readPatchFrontmatter(args.runDir);
2662
2738
  const scenario = extractTestScenario(fm);
2663
2739
  if (!scenario) {
2664
2740
  writeLog(args.logFd, `[tryDryRun:${attemptIndex}] patch.md frontmatter missing testScenario.{agentId, testMessage}
2665
2741
  `);
2666
- fs11.writeFileSync(failLogPath, "patch.md frontmatter missing testScenario.{agentId, testMessage} \u2014 wrapper cannot run dry-run without it.\n", "utf-8");
2742
+ fs12.writeFileSync(failLogPath, "patch.md frontmatter missing testScenario.{agentId, testMessage} \u2014 wrapper cannot run dry-run without it.\n", "utf-8");
2667
2743
  return { kind: "no-scenario", failLogPath };
2668
2744
  }
2669
2745
  writeLog(args.logFd, `[tryDryRun:${attemptIndex}] agent=${scenario.agentId} testMessage=${JSON.stringify(scenario.testMessage).slice(0, 200)}
@@ -2681,14 +2757,14 @@ async function tryDryRun(args, attemptIndex) {
2681
2757
  const timedOut = result.exitCode === 124;
2682
2758
  const ok = result.exitCode === 0 && result.stdout.trim().length > 0;
2683
2759
  if (ok) {
2684
- fs11.writeFileSync(outputPath, result.stdout, "utf-8");
2760
+ fs12.writeFileSync(outputPath, result.stdout, "utf-8");
2685
2761
  writeLog(args.logFd, `[tryDryRun:${attemptIndex}] success: wrote ${outputPath} (${result.stdout.length}B)
2686
2762
  `);
2687
2763
  return { kind: "ok" };
2688
2764
  }
2689
2765
  const stdoutTail = result.stdout.length > 4e3 ? "\u2026" + result.stdout.slice(-4e3) : result.stdout;
2690
2766
  const stderrTail = result.stderr.length > 4e3 ? "\u2026" + result.stderr.slice(-4e3) : result.stderr;
2691
- fs11.writeFileSync(failLogPath, [
2767
+ fs12.writeFileSync(failLogPath, [
2692
2768
  `Wrapper command: halo cli -a ${scenario.agentId} -n -w ${sandboxDir} <testMessage>`,
2693
2769
  `Test message: ${scenario.testMessage}`,
2694
2770
  `Exit code: ${result.exitCode}${timedOut ? " (timeout)" : ""}`,
@@ -2744,7 +2820,7 @@ async function phaseScore(args) {
2744
2820
  const sandboxWs = evoSandboxDir(args.runDir);
2745
2821
  buildEvoSandbox(args.workspacePath, args.runDir, args.logFd);
2746
2822
  const cliArgs = ["cli", "-a", "__score__", "-n", "-w", sandboxWs];
2747
- fs11.rmSync(path13.join(args.runDir, "score.json"), { force: true });
2823
+ fs12.rmSync(path14.join(args.runDir, "score.json"), { force: true });
2748
2824
  appendSubCliHeader(args.runDir, "Phase C: __score__");
2749
2825
  const result = await spawnProc(
2750
2826
  resolveHaloCli(),
@@ -2765,7 +2841,7 @@ async function phaseScore(args) {
2765
2841
  writeLog(args.logFd, `[phaseScore] stdout (last 1KB): ${result.stdout.slice(-1e3)}
2766
2842
  `);
2767
2843
  }
2768
- const hasScore = fs11.existsSync(path13.join(args.runDir, "score.json"));
2844
+ const hasScore = fs12.existsSync(path14.join(args.runDir, "score.json"));
2769
2845
  writeLog(args.logFd, `[phaseScore] score.json=${hasScore}
2770
2846
  `);
2771
2847
  return hasScore;
@@ -2779,7 +2855,7 @@ var RUN_INPUT_FILES = /* @__PURE__ */ new Set([
2779
2855
  function resetRunArtifacts(runDir, logFd) {
2780
2856
  let entries;
2781
2857
  try {
2782
- entries = fs11.readdirSync(runDir);
2858
+ entries = fs12.readdirSync(runDir);
2783
2859
  } catch {
2784
2860
  return;
2785
2861
  }
@@ -2787,7 +2863,7 @@ function resetRunArtifacts(runDir, logFd) {
2787
2863
  if (RUN_INPUT_FILES.has(entry))
2788
2864
  continue;
2789
2865
  try {
2790
- fs11.rmSync(path13.join(runDir, entry), { recursive: true, force: true });
2866
+ fs12.rmSync(path14.join(runDir, entry), { recursive: true, force: true });
2791
2867
  } catch (err) {
2792
2868
  writeLog(logFd, `[resetRunArtifacts] failed to remove ${entry}: ${err instanceof Error ? err.message : String(err)}
2793
2869
  `);
@@ -2797,7 +2873,7 @@ function resetRunArtifacts(runDir, logFd) {
2797
2873
  async function runMode(row, logFd) {
2798
2874
  const { id } = row;
2799
2875
  const runDir = wsEvoRunDir(row.workspacePath, id);
2800
- fs11.mkdirSync(runDir, { recursive: true });
2876
+ fs12.mkdirSync(runDir, { recursive: true });
2801
2877
  resetRunArtifacts(runDir, logFd);
2802
2878
  const hb = setInterval(() => heartbeat("run", id, row.attempts, logFd), 6e4);
2803
2879
  const langHint = resolveLangHint();
@@ -2841,7 +2917,7 @@ async function runMode(row, logFd) {
2841
2917
  }
2842
2918
  }
2843
2919
  function markObservation(id, attempts, runDir, outcome, logFd) {
2844
- const has = outcome !== "failed" && readObservation(path13.join(runDir, outcome === "skipped" ? ".skip.md" : "patch.md")) !== null;
2920
+ const has = outcome !== "failed" && readObservation(path14.join(runDir, outcome === "skipped" ? ".skip.md" : "patch.md")) !== null;
2845
2921
  getEvoDb().update(evolutionRuns).set({ hasObservation: has }).where(ownsRun(id, attempts)).run();
2846
2922
  if (has)
2847
2923
  writeLog(logFd, `[wrapper] draft carries a Larger observation
@@ -2861,10 +2937,10 @@ function maybeAutoApply(args) {
2861
2937
  level,
2862
2938
  threshold: autoApplyThresholdFor(args.workspacePath),
2863
2939
  sourceAccess: args.sourceAccess,
2864
- score: readScore(path13.join(args.runDir, "score.json")),
2865
- observation: readObservation(path13.join(args.runDir, "patch.md"))
2940
+ score: readScore(path14.join(args.runDir, "score.json")),
2941
+ observation: readObservation(path14.join(args.runDir, "patch.md"))
2866
2942
  });
2867
- fs11.writeFileSync(path13.join(args.runDir, AUTO_APPLY_FILE), JSON.stringify(record, null, 2), "utf-8");
2943
+ fs12.writeFileSync(path14.join(args.runDir, AUTO_APPLY_FILE), JSON.stringify(record, null, 2), "utf-8");
2868
2944
  } catch (err) {
2869
2945
  writeLog(args.logFd, `[autoApply] evaluation failed, leaving the run for review: ${err instanceof Error ? err.message : String(err)}
2870
2946
  `);
@@ -2962,14 +3038,14 @@ function loadApplyRow(id) {
2962
3038
  function buildEvoSandbox(workspacePath, sandboxParent, logFd) {
2963
3039
  const srcWs = wsHaloDir(workspacePath);
2964
3040
  const dstWs = evoSandboxHaloDir(sandboxParent);
2965
- fs11.mkdirSync(dstWs, { recursive: true });
3041
+ fs12.mkdirSync(dstWs, { recursive: true });
2966
3042
  const whitelist = SANDBOX_WHITELIST;
2967
3043
  for (const entry of whitelist) {
2968
- const srcEntry = path13.join(srcWs, entry);
2969
- if (!fs11.existsSync(srcEntry))
3044
+ const srcEntry = path14.join(srcWs, entry);
3045
+ if (!fs12.existsSync(srcEntry))
2970
3046
  continue;
2971
- const dstEntry = path13.join(dstWs, entry);
2972
- if (fs11.existsSync(dstEntry))
3047
+ const dstEntry = path14.join(dstWs, entry);
3048
+ if (fs12.existsSync(dstEntry))
2973
3049
  continue;
2974
3050
  copyDereferenced(srcEntry, dstEntry);
2975
3051
  }
@@ -2977,13 +3053,13 @@ function buildEvoSandbox(workspacePath, sandboxParent, logFd) {
2977
3053
  `);
2978
3054
  }
2979
3055
  function copyDereferenced(src, dst) {
2980
- if (fs11.statSync(src).isDirectory()) {
2981
- fs11.mkdirSync(dst, { recursive: true });
2982
- for (const entry of fs11.readdirSync(src))
2983
- copyDereferenced(path13.join(src, entry), path13.join(dst, entry));
3056
+ if (fs12.statSync(src).isDirectory()) {
3057
+ fs12.mkdirSync(dst, { recursive: true });
3058
+ for (const entry of fs12.readdirSync(src))
3059
+ copyDereferenced(path14.join(src, entry), path14.join(dst, entry));
2984
3060
  return;
2985
3061
  }
2986
- fs11.copyFileSync(src, dst);
3062
+ fs12.copyFileSync(src, dst);
2987
3063
  }
2988
3064
  function buildApplySandbox(workspacePath, applyDir, logFd) {
2989
3065
  buildEvoSandbox(workspacePath, applyDir, logFd);
@@ -3011,11 +3087,11 @@ function buildApplyMergeBrief(args) {
3011
3087
  "",
3012
3088
  "Hard rules: only edit files under sandbox/.halo/ \u2014 main workspace",
3013
3089
  "and ~/.halo/global/ are off-limits. Do NOT spawn halo cli. Do NOT",
3014
- `write score.json. Write ${path13.join(args.applyDir, "apply.log")}`,
3090
+ `write score.json. Write ${path14.join(args.applyDir, "apply.log")}`,
3015
3091
  "with a summary of what you did.",
3016
3092
  "",
3017
3093
  "If the merge cannot proceed (irreconcilable patch conflicts, malformed",
3018
- `input, missing sandbox), write ${path13.join(args.applyDir, "ABORT.md")}`,
3094
+ `input, missing sandbox), write ${path14.join(args.applyDir, "ABORT.md")}`,
3019
3095
  'with the diagnosis \u2014 the wrapper treats its presence as "abort this',
3020
3096
  'apply" and fails the apply instead of publishing. You cannot signal',
3021
3097
  "abort via exit code; ABORT.md is the only abort channel.",
@@ -3036,19 +3112,19 @@ function buildApplyScoreBrief(args) {
3036
3112
  "The apply agent has merged this patch (and possibly others) into",
3037
3113
  "an apply sandbox. The wrapper just ran the patch's testScenario",
3038
3114
  "against the merged sandbox; stdout is in",
3039
- `${path13.join(args.regressDir, "dry-run-output.txt")}.`,
3115
+ `${path14.join(args.regressDir, "dry-run-output.txt")}.`,
3040
3116
  "An empty dry-run-output.txt means the dry-run against the merged",
3041
3117
  "sandbox failed \u2014 score lint 0 (that is the regression signal the",
3042
3118
  `wrapper acts on); the failure detail is in`,
3043
- `${path13.join(args.regressDir, "dry-run-fail.log")} when present.`,
3119
+ `${path14.join(args.regressDir, "dry-run-fail.log")} when present.`,
3044
3120
  "",
3045
3121
  "Follow your AGENT.md procedure. Read:",
3046
3122
  ` patch.md \u2192 ${args.patchMdPath}`,
3047
- ` tool-flow.md \u2192 ${path13.join(path13.dirname(args.patchMdPath), "tool-flow.md")} (baseline conversation)`,
3123
+ ` tool-flow.md \u2192 ${path14.join(path14.dirname(args.patchMdPath), "tool-flow.md")} (baseline conversation)`,
3048
3124
  ` source-snapshot.json \u2192 ${args.snapshotPath}`,
3049
- ` dry-run output \u2192 ${path13.join(args.regressDir, "dry-run-output.txt")}`,
3125
+ ` dry-run output \u2192 ${path14.join(args.regressDir, "dry-run-output.txt")}`,
3050
3126
  "",
3051
- `Write ${path13.join(args.regressDir, "score.json")} \u2014 NOT the source`,
3127
+ `Write ${path14.join(args.regressDir, "score.json")} \u2014 NOT the source`,
3052
3128
  "run's score.json. Same format as run mode.",
3053
3129
  "Do not modify any other file.",
3054
3130
  ...buildLanguageClause(args.langHint, 'the score.json "notes" field'),
@@ -3060,9 +3136,9 @@ async function phaseApplyMerge(ctx) {
3060
3136
  === Phase A': merge ===
3061
3137
  `);
3062
3138
  buildApplySandbox(ctx.workspacePath, ctx.applyDir, ctx.logFd);
3063
- const abortPath = path13.join(ctx.applyDir, "ABORT.md");
3139
+ const abortPath = path14.join(ctx.applyDir, "ABORT.md");
3064
3140
  try {
3065
- fs11.rmSync(abortPath, { force: true });
3141
+ fs12.rmSync(abortPath, { force: true });
3066
3142
  } catch {
3067
3143
  }
3068
3144
  const brief = buildApplyMergeBrief(ctx);
@@ -3081,10 +3157,10 @@ async function phaseApplyMerge(ctx) {
3081
3157
  `);
3082
3158
  return { ok: false, reason: `apply cli exited ${result.exitCode}` };
3083
3159
  }
3084
- if (fs11.existsSync(abortPath)) {
3160
+ if (fs12.existsSync(abortPath)) {
3085
3161
  let abortText = "";
3086
3162
  try {
3087
- abortText = fs11.readFileSync(abortPath, "utf-8").trim();
3163
+ abortText = fs12.readFileSync(abortPath, "utf-8").trim();
3088
3164
  } catch {
3089
3165
  }
3090
3166
  writeLog(ctx.logFd, `[phaseApplyMerge] ABORT.md present \u2014 agent aborted the merge:
@@ -3093,7 +3169,7 @@ ${abortText}
3093
3169
  const firstLine = abortText.split("\n")[0]?.slice(0, 300) ?? "";
3094
3170
  return { ok: false, reason: `apply agent aborted: ${firstLine || "see ABORT.md in apply dir"}` };
3095
3171
  }
3096
- const hasLog = fs11.existsSync(path13.join(ctx.applyDir, "apply.log"));
3172
+ const hasLog = fs12.existsSync(path14.join(ctx.applyDir, "apply.log"));
3097
3173
  writeLog(ctx.logFd, `[phaseApplyMerge] apply.log=${hasLog}
3098
3174
  `);
3099
3175
  if (!hasLog)
@@ -3105,17 +3181,17 @@ async function phaseApplyRegress(ctx) {
3105
3181
  === Phase B': regress ===
3106
3182
  `);
3107
3183
  const sandboxDir = evoSandboxDir(ctx.applyDir);
3108
- fs11.mkdirSync(evoRegressDir(ctx.applyDir), { recursive: true });
3184
+ fs12.mkdirSync(evoRegressDir(ctx.applyDir), { recursive: true });
3109
3185
  const outcomes = [];
3110
3186
  for (const runId of ctx.sourceRunIds) {
3111
3187
  writeLog(ctx.logFd, `
3112
3188
  --- regress for source run ${runId} ---
3113
3189
  `);
3114
3190
  const regressDir = evoRegressRunDir(ctx.applyDir, runId);
3115
- fs11.mkdirSync(regressDir, { recursive: true });
3191
+ fs12.mkdirSync(regressDir, { recursive: true });
3116
3192
  const runDir = wsEvoRunDir(ctx.workspacePath, runId);
3117
- const patchMdPath = path13.join(runDir, "patch.md");
3118
- const snapshotPath = path13.join(runDir, "source-snapshot.json");
3193
+ const patchMdPath = path14.join(runDir, "patch.md");
3194
+ const snapshotPath = path14.join(runDir, "source-snapshot.json");
3119
3195
  const fm = readPatchFrontmatter(runDir);
3120
3196
  const scenario = extractTestScenario(fm);
3121
3197
  if (!scenario) {
@@ -3142,14 +3218,14 @@ async function phaseApplyRegress(ctx) {
3142
3218
  // stdin, not argv
3143
3219
  DRY_RUN_TIMEOUT_SEC
3144
3220
  );
3145
- const dryRunOut = path13.join(regressDir, "dry-run-output.txt");
3221
+ const dryRunOut = path14.join(regressDir, "dry-run-output.txt");
3146
3222
  if (dryRun.exitCode === 0 && dryRun.stdout.trim().length > 0) {
3147
- fs11.writeFileSync(dryRunOut, dryRun.stdout, "utf-8");
3223
+ fs12.writeFileSync(dryRunOut, dryRun.stdout, "utf-8");
3148
3224
  } else {
3149
3225
  const tail = dryRun.stdout.length > 4e3 ? "\u2026" + dryRun.stdout.slice(-4e3) : dryRun.stdout;
3150
3226
  const errTail = dryRun.stderr.length > 4e3 ? "\u2026" + dryRun.stderr.slice(-4e3) : dryRun.stderr;
3151
- fs11.writeFileSync(dryRunOut, "", "utf-8");
3152
- fs11.writeFileSync(path13.join(regressDir, "dry-run-fail.log"), [
3227
+ fs12.writeFileSync(dryRunOut, "", "utf-8");
3228
+ fs12.writeFileSync(path14.join(regressDir, "dry-run-fail.log"), [
3153
3229
  `Wrapper command: halo cli -a ${scenario.agentId} -n -w ${sandboxDir} <testMessage>`,
3154
3230
  `Test message: ${scenario.testMessage}`,
3155
3231
  `Exit code: ${dryRun.exitCode}${dryRun.exitCode === 124 ? " (timeout)" : ""}`,
@@ -3170,7 +3246,7 @@ async function phaseApplyRegress(ctx) {
3170
3246
  langHint: ctx.langHint
3171
3247
  });
3172
3248
  const scoreSandboxWs = evoSandboxDir(ctx.applyDir);
3173
- fs11.rmSync(path13.join(regressDir, "score.json"), { force: true });
3249
+ fs12.rmSync(path14.join(regressDir, "score.json"), { force: true });
3174
3250
  const scoreRun = await spawnProc(
3175
3251
  resolveHaloCli(),
3176
3252
  ["cli", "-a", "__score__", "-n", "-w", scoreSandboxWs],
@@ -3184,10 +3260,10 @@ async function phaseApplyRegress(ctx) {
3184
3260
  writeLog(ctx.logFd, `[phaseApplyRegress:${runId}] scorer cli exited ${scoreRun.exitCode}
3185
3261
  `);
3186
3262
  }
3187
- const scorePath = path13.join(regressDir, "score.json");
3263
+ const scorePath = path14.join(regressDir, "score.json");
3188
3264
  let scoreJson = null;
3189
3265
  try {
3190
- scoreJson = JSON.parse(fs11.readFileSync(scorePath, "utf-8"));
3266
+ scoreJson = JSON.parse(fs12.readFileSync(scorePath, "utf-8"));
3191
3267
  } catch {
3192
3268
  }
3193
3269
  if (!scoreJson) {
@@ -3246,8 +3322,8 @@ function autoApplyRecheck(workspacePath, applyDir, changed) {
3246
3322
  async function applyMode(id, logFd) {
3247
3323
  const row = loadApplyRow(id);
3248
3324
  const applyDir = wsEvoApplyDir(row.workspacePath, id);
3249
- fs11.mkdirSync(applyDir, { recursive: true });
3250
- fs11.writeFileSync(path13.join(applyDir, "meta.json"), JSON.stringify({
3325
+ fs12.mkdirSync(applyDir, { recursive: true });
3326
+ fs12.writeFileSync(path14.join(applyDir, "meta.json"), JSON.stringify({
3251
3327
  id,
3252
3328
  sourceRunIds: row.sourceRunIds,
3253
3329
  reviewerHint: row.reviewerHint,
@@ -3338,28 +3414,28 @@ async function phaseApplyPreflight(ctx, isResume) {
3338
3414
  `);
3339
3415
  const sandboxHalo = evoSandboxHaloDir(ctx.applyDir);
3340
3416
  const mainHalo = wsHaloDir(ctx.workspacePath);
3341
- if (!fs11.existsSync(sandboxHalo))
3417
+ if (!fs12.existsSync(sandboxHalo))
3342
3418
  return { ok: false, reason: "sandbox missing \u2014 phase 11 must run first" };
3343
3419
  const whitelistTop = new Set(PUBLISH_WHITELIST);
3344
3420
  const filesToConsider = [];
3345
- for (const top of fs11.readdirSync(sandboxHalo)) {
3421
+ for (const top of fs12.readdirSync(sandboxHalo)) {
3346
3422
  if (!whitelistTop.has(top))
3347
3423
  continue;
3348
- walkInto(path13.join(sandboxHalo, top), top, filesToConsider);
3424
+ walkInto(path14.join(sandboxHalo, top), top, filesToConsider);
3349
3425
  }
3350
3426
  const historyDir = wsEvoHistoryDir(ctx.workspacePath, ctx.applyId);
3351
3427
  const changed = [];
3352
3428
  for (const { rel, full } of filesToConsider) {
3353
- const mainPath = path13.join(mainHalo, rel);
3429
+ const mainPath = path14.join(mainHalo, rel);
3354
3430
  let existsInMain = false;
3355
3431
  try {
3356
- fs11.accessSync(mainPath);
3432
+ fs12.accessSync(mainPath);
3357
3433
  existsInMain = true;
3358
3434
  } catch {
3359
3435
  }
3360
3436
  if (existsInMain) {
3361
- const sandboxBuf = fs11.readFileSync(full);
3362
- const mainBuf = fs11.readFileSync(mainPath);
3437
+ const sandboxBuf = fs12.readFileSync(full);
3438
+ const mainBuf = fs12.readFileSync(mainPath);
3363
3439
  if (sandboxBuf.equals(mainBuf))
3364
3440
  continue;
3365
3441
  }
@@ -3368,20 +3444,20 @@ async function phaseApplyPreflight(ctx, isResume) {
3368
3444
  writeLog(ctx.logFd, `[preflight] ${changed.length} of ${filesToConsider.length} whitelisted files differ from main
3369
3445
  `);
3370
3446
  if (!isResume)
3371
- fs11.rmSync(historyDir, { recursive: true, force: true });
3447
+ fs12.rmSync(historyDir, { recursive: true, force: true });
3372
3448
  if (changed.length === 0) {
3373
3449
  return { ok: true, result: { changed, historyDir, fileCount: 0 } };
3374
3450
  }
3375
- fs11.mkdirSync(historyDir, { recursive: true });
3376
- const manifestPath = path13.join(historyDir, "MANIFEST.json");
3451
+ fs12.mkdirSync(historyDir, { recursive: true });
3452
+ const manifestPath = path14.join(historyDir, "MANIFEST.json");
3377
3453
  if (!isResume) {
3378
3454
  for (const { rel, existsInMain } of changed) {
3379
3455
  if (!existsInMain)
3380
3456
  continue;
3381
- const mainPath = path13.join(mainHalo, rel);
3382
- const histPath = path13.join(historyDir, rel);
3383
- fs11.mkdirSync(path13.dirname(histPath), { recursive: true });
3384
- fs11.copyFileSync(mainPath, histPath);
3457
+ const mainPath = path14.join(mainHalo, rel);
3458
+ const histPath = path14.join(historyDir, rel);
3459
+ fs12.mkdirSync(path14.dirname(histPath), { recursive: true });
3460
+ fs12.copyFileSync(mainPath, histPath);
3385
3461
  }
3386
3462
  const effective = new Set(publishChanges({ mainHalo, globalHalo: globalDir(), sandboxHalo }, changed).map((c) => c.rel));
3387
3463
  const manifest = {
@@ -3391,13 +3467,13 @@ async function phaseApplyPreflight(ctx, isResume) {
3391
3467
  files: changed.map((c) => ({
3392
3468
  rel: c.rel,
3393
3469
  kind: c.existsInMain ? "overwrite" : "new",
3394
- before: c.existsInMain ? sha256File(path13.join(mainHalo, c.rel)) : null,
3470
+ before: c.existsInMain ? sha256File(path14.join(mainHalo, c.rel)) : null,
3395
3471
  after: sha256File(c.full),
3396
- ...effective.has(c.rel.split(path13.sep).join("/")) ? {} : { unchanged: true }
3472
+ ...effective.has(c.rel.split(path14.sep).join("/")) ? {} : { unchanged: true }
3397
3473
  })),
3398
3474
  createdDirs: dirsCreatedBy(mainHalo, changed.filter((c) => !c.existsInMain).map((c) => c.rel))
3399
3475
  };
3400
- fs11.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
3476
+ fs12.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
3401
3477
  } else {
3402
3478
  writeLog(ctx.logFd, `[preflight] resume path, skipping history backup (the pre-apply state is already preserved from the fresh claim's preflight)
3403
3479
  `);
@@ -3412,9 +3488,9 @@ async function phaseApplyPublish(ctx, preflight) {
3412
3488
  `);
3413
3489
  const mainHalo = wsHaloDir(ctx.workspacePath);
3414
3490
  for (const { rel, full } of preflight.changed) {
3415
- const mainPath = path13.join(mainHalo, rel);
3416
- fs11.mkdirSync(path13.dirname(mainPath), { recursive: true });
3417
- fs11.copyFileSync(full, mainPath);
3491
+ const mainPath = path14.join(mainHalo, rel);
3492
+ fs12.mkdirSync(path14.dirname(mainPath), { recursive: true });
3493
+ fs12.copyFileSync(full, mainPath);
3418
3494
  }
3419
3495
  writeLog(ctx.logFd, `[publish] copied ${preflight.changed.length} files to ${mainHalo}
3420
3496
  `);
@@ -3422,10 +3498,10 @@ async function phaseApplyPublish(ctx, preflight) {
3422
3498
  function dirsCreatedBy(mainHalo, newFiles) {
3423
3499
  const dirs = /* @__PURE__ */ new Set();
3424
3500
  for (const rel of newFiles) {
3425
- for (let d = path13.dirname(rel); d !== "." && !fs11.existsSync(path13.join(mainHalo, d)); d = path13.dirname(d))
3501
+ for (let d = path14.dirname(rel); d !== "." && !fs12.existsSync(path14.join(mainHalo, d)); d = path14.dirname(d))
3426
3502
  dirs.add(d);
3427
3503
  }
3428
- return [...dirs].sort((a, b) => b.split(path13.sep).length - a.split(path13.sep).length || (a < b ? -1 : 1));
3504
+ return [...dirs].sort((a, b) => b.split(path14.sep).length - a.split(path14.sep).length || (a < b ? -1 : 1));
3429
3505
  }
3430
3506
  function ownsApply(id, status, attempts) {
3431
3507
  return and3(eq3(evolutionApplies.id, id), eq3(evolutionApplies.status, status), eq3(evolutionApplies.attempts, attempts));
@@ -3451,15 +3527,15 @@ function releaseSyncing(applyId, attempts, reason, logFd) {
3451
3527
  `);
3452
3528
  }
3453
3529
  function walkInto(absPath, relPath, out) {
3454
- const stat = fs11.statSync(absPath);
3530
+ const stat = fs12.statSync(absPath);
3455
3531
  if (stat.isFile()) {
3456
3532
  out.push({ rel: relPath, full: absPath });
3457
3533
  return;
3458
3534
  }
3459
3535
  if (!stat.isDirectory())
3460
3536
  return;
3461
- for (const entry of fs11.readdirSync(absPath)) {
3462
- walkInto(path13.join(absPath, entry), path13.join(relPath, entry), out);
3537
+ for (const entry of fs12.readdirSync(absPath)) {
3538
+ walkInto(path14.join(absPath, entry), path14.join(relPath, entry), out);
3463
3539
  }
3464
3540
  }
3465
3541
  function finalizeApplied(applyId, attempts, sourceRunIds, logFd) {
@@ -3478,19 +3554,19 @@ function finalizeApplied(applyId, attempts, sourceRunIds, logFd) {
3478
3554
  }
3479
3555
  function writeLog(fd, msg) {
3480
3556
  try {
3481
- fs11.writeSync(fd, msg);
3557
+ fs12.writeSync(fd, msg);
3482
3558
  } catch {
3483
3559
  }
3484
3560
  }
3485
3561
  function openLogFor(mode, id) {
3486
- fs11.mkdirSync(evoLogsDir(), { recursive: true });
3562
+ fs12.mkdirSync(evoLogsDir(), { recursive: true });
3487
3563
  const logPath = mode === "run" ? evoWrapperLogFile(id) : evoApplyLogFile(id);
3488
- const fd = fs11.openSync(logPath, "a");
3564
+ const fd = fs12.openSync(logPath, "a");
3489
3565
  return { fd, logPath };
3490
3566
  }
3491
3567
  async function main() {
3492
3568
  const { mode, id } = parseCli(process.argv.slice(2));
3493
- setEvoDb(createEvoDb(path13.join(homedir6(), ".halo", "global")));
3569
+ setEvoDb(createEvoDb(path14.join(homedir7(), ".halo", "global")));
3494
3570
  const { fd, logPath } = openLogFor(mode, id);
3495
3571
  writeLog(fd, `
3496
3572
  === ${(/* @__PURE__ */ new Date()).toISOString()} wrapper start mode=${mode} id=${id} pid=${process.pid} ===
@@ -3518,7 +3594,7 @@ async function main() {
3518
3594
  process.exit(1);
3519
3595
  }
3520
3596
  }
3521
- if (process.argv[1] && path13.resolve(process.argv[1]) === fileURLToPath2(import.meta.url)) {
3597
+ if (process.argv[1] && path14.resolve(process.argv[1]) === fileURLToPath2(import.meta.url)) {
3522
3598
  main().catch((err) => {
3523
3599
  process.stderr.write(`[wrapper] startup error: ${err instanceof Error ? err.message : String(err)}
3524
3600
  `);