@wrongstack/core 0.260.0 → 0.264.0

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 (80) hide show
  1. package/dist/{agent-bridge-BbskZ7HH.d.ts → agent-bridge-D8sa1vtv.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-BNIGZx18.d.ts → agent-subagent-runner-c9DLkaas.d.ts} +11 -9
  3. package/dist/{brain-C2yDd7Lw.d.ts → brain-O1IdKPaK.d.ts} +2 -2
  4. package/dist/{compactor-t0R_AIt_.d.ts → compactor-BBy0rCtB.d.ts} +1 -1
  5. package/dist/{config-FG6As4H5.d.ts → config-Dz2F3H2K.d.ts} +7 -1
  6. package/dist/{context-JFOVvu6z.d.ts → context-BGSpZNSE.d.ts} +11 -0
  7. package/dist/coordination/index.d.ts +1681 -15
  8. package/dist/coordination/index.js +2648 -388
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +25 -25
  11. package/dist/defaults/index.js +1414 -1387
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/dispatcher-types.d-BBeXBQgS.d.ts +66 -0
  14. package/dist/execution/index.d.ts +15 -15
  15. package/dist/execution/index.js +410 -388
  16. package/dist/execution/index.js.map +1 -1
  17. package/dist/execution/prompt-enhancer.d.ts +2 -2
  18. package/dist/execution/prompt-enhancer.js +7 -1
  19. package/dist/execution/prompt-enhancer.js.map +1 -1
  20. package/dist/extension/index.d.ts +6 -6
  21. package/dist/extension/index.js.map +1 -1
  22. package/dist/{goal-preamble-B1IXJtLX.d.ts → goal-preamble-DzjFuN3p.d.ts} +21 -9
  23. package/dist/{goal-store-CPXz6Mml.d.ts → goal-store-CxWmCGbH.d.ts} +1 -1
  24. package/dist/{index-CebbJB94.d.ts → index-CYIQrXVF.d.ts} +8 -8
  25. package/dist/{index-BPcg4N3M.d.ts → index-CbLSI66_.d.ts} +5 -5
  26. package/dist/index.d.ts +45 -91
  27. package/dist/index.js +14976 -12551
  28. package/dist/index.js.map +1 -1
  29. package/dist/infrastructure/index.d.ts +6 -6
  30. package/dist/kernel/index.d.ts +9 -9
  31. package/dist/kernel/index.js +6 -1
  32. package/dist/kernel/index.js.map +1 -1
  33. package/dist/{llm-selector-DXxI2tlu.d.ts → llm-selector-DzxuZnNz.d.ts} +2 -2
  34. package/dist/{mcp-servers-OwNHo43-.d.ts → mcp-servers-DC4QRPUI.d.ts} +3 -3
  35. package/dist/models/index.d.ts +5 -5
  36. package/dist/models/index.js +6 -1
  37. package/dist/models/index.js.map +1 -1
  38. package/dist/{models-registry-Djlmq4uB.d.ts → models-registry-B_siPxqN.d.ts} +1 -1
  39. package/dist/{multi-agent-coordinator-CEmrSCMJ.d.ts → multi-agent-coordinator-CK5Jdj9K.d.ts} +2 -2
  40. package/dist/{null-fleet-bus-DT92xqgJ.d.ts → null-fleet-bus-DgvD4SCO.d.ts} +6 -6
  41. package/dist/observability/index.d.ts +2 -2
  42. package/dist/observability/index.js +8 -3
  43. package/dist/observability/index.js.map +1 -1
  44. package/dist/{parallel-eternal-engine-0SItuq5r.d.ts → parallel-eternal-engine-bK0JQBR_.d.ts} +9 -9
  45. package/dist/{path-resolver-DKBh6Jlo.d.ts → path-resolver-BPEDlN38.d.ts} +3 -3
  46. package/dist/{permission-BJ7eO9Vl.d.ts → permission-4yvGmMRB.d.ts} +1 -1
  47. package/dist/{permission-policy-DEXOfnpm.d.ts → permission-policy-C6XpsBOy.d.ts} +2 -2
  48. package/dist/{pipeline-zflkI2dp.d.ts → pipeline-CXCeMz8J.d.ts} +58 -3
  49. package/dist/{plan-templates-BFXyRkEK.d.ts → plan-templates-BvzRBkJc.d.ts} +5 -5
  50. package/dist/{provider-runner-BC-uywtT.d.ts → provider-runner-C5aQpDWE.d.ts} +3 -3
  51. package/dist/{retry-policy-Cavrzmtk.d.ts → retry-policy-CFhdtRzz.d.ts} +1 -1
  52. package/dist/sdd/index.d.ts +8 -8
  53. package/dist/sdd/index.js +39 -29
  54. package/dist/sdd/index.js.map +1 -1
  55. package/dist/{secret-vault-CDvDYXWX.d.ts → secret-vault-CxiVLbt1.d.ts} +1 -1
  56. package/dist/security/index.d.ts +4 -4
  57. package/dist/security/index.js +208 -203
  58. package/dist/security/index.js.map +1 -1
  59. package/dist/{selector-B7AivHsu.d.ts → selector-gIuhRTkN.d.ts} +1 -1
  60. package/dist/{session-event-bridge-BmIDxdJd.d.ts → session-event-bridge-DkvvrpDt.d.ts} +8 -2
  61. package/dist/{session-reader-DtofsB-2.d.ts → session-reader-KdfVwkKP.d.ts} +1 -1
  62. package/dist/skills/index.js +67 -64
  63. package/dist/skills/index.js.map +1 -1
  64. package/dist/storage/index.d.ts +31 -12
  65. package/dist/storage/index.js +441 -360
  66. package/dist/storage/index.js.map +1 -1
  67. package/dist/tools/index.d.ts +57 -0
  68. package/dist/tools/index.js +411 -0
  69. package/dist/tools/index.js.map +1 -0
  70. package/dist/types/index.d.ts +19 -19
  71. package/dist/types/index.js +703 -694
  72. package/dist/types/index.js.map +1 -1
  73. package/dist/utils/error.d.ts +7 -0
  74. package/dist/utils/error.js +8 -0
  75. package/dist/utils/error.js.map +1 -0
  76. package/dist/utils/index.d.ts +7 -67
  77. package/dist/utils/index.js +17 -5
  78. package/dist/utils/index.js.map +1 -1
  79. package/package.json +5 -1
  80. package/dist/package-outdated-watcher-C70ag2G9.d.ts +0 -581
@@ -1,7 +1,7 @@
1
1
  import * as crypto2 from 'crypto';
2
2
  import { randomBytes, createCipheriv, createDecipheriv, randomUUID, createHash } from 'crypto';
3
- import * as fsp from 'fs/promises';
4
- import * as path11 from 'path';
3
+ import * as fsp2 from 'fs/promises';
4
+ import * as path3 from 'path';
5
5
  import { isAbsolute, resolve } from 'path';
6
6
  import * as fs from 'fs';
7
7
  import * as os from 'os';
@@ -33,17 +33,17 @@ __export(atomic_write_exports, {
33
33
  withFileLock: () => withFileLock
34
34
  });
35
35
  async function atomicWrite(targetPath, content, opts = {}) {
36
- const dir = path11.dirname(targetPath);
37
- await fsp.mkdir(dir, { recursive: true });
38
- const tmp = path11.join(dir, `.${path11.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
36
+ const dir = path3.dirname(targetPath);
37
+ await fsp2.mkdir(dir, { recursive: true });
38
+ const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
39
39
  try {
40
40
  if (typeof content === "string") {
41
- await fsp.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
41
+ await fsp2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
42
42
  } else {
43
- await fsp.writeFile(tmp, content, { flag: "wx" });
43
+ await fsp2.writeFile(tmp, content, { flag: "wx" });
44
44
  }
45
45
  try {
46
- const fh = await fsp.open(tmp, "r+");
46
+ const fh = await fsp2.open(tmp, "r+");
47
47
  try {
48
48
  await fh.sync();
49
49
  } finally {
@@ -53,45 +53,50 @@ async function atomicWrite(targetPath, content, opts = {}) {
53
53
  }
54
54
  let mode;
55
55
  try {
56
- const stat6 = await fsp.stat(targetPath);
56
+ const stat6 = await fsp2.stat(targetPath);
57
57
  mode = stat6.mode & 511;
58
58
  } catch {
59
59
  mode = opts.mode;
60
60
  }
61
61
  if (mode !== void 0) {
62
- await fsp.chmod(tmp, mode);
62
+ await fsp2.chmod(tmp, mode);
63
63
  }
64
64
  await renameWithRetry(tmp, targetPath);
65
65
  } catch (err) {
66
66
  try {
67
- await fsp.unlink(tmp);
67
+ await fsp2.unlink(tmp);
68
68
  } catch {
69
69
  }
70
70
  throw err;
71
71
  }
72
72
  }
73
73
  async function ensureDir(dir) {
74
- await fsp.mkdir(dir, { recursive: true });
74
+ await fsp2.mkdir(dir, { recursive: true });
75
75
  }
76
76
  async function withFileLock(targetPath, fn, opts = {}) {
77
- const dir = path11.dirname(targetPath);
78
- await fsp.mkdir(dir, { recursive: true });
79
- const lockPath = path11.join(dir, `.${path11.basename(targetPath)}.lock`);
77
+ const dir = path3.dirname(targetPath);
78
+ await fsp2.mkdir(dir, { recursive: true });
79
+ const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
80
80
  const timeoutMs = opts.timeoutMs ?? 5e3;
81
81
  const staleMs = opts.staleMs ?? 3e4;
82
82
  const started = Date.now();
83
83
  let handle;
84
84
  for (; ; ) {
85
85
  try {
86
- handle = await fsp.open(lockPath, "wx");
86
+ handle = await fsp2.open(lockPath, "wx");
87
87
  await handle.writeFile(`${process.pid}:${Date.now()}`);
88
88
  break;
89
89
  } catch (err) {
90
- if (err.code !== "EEXIST") throw err;
90
+ const code = err.code;
91
+ if (code === "ENOENT") {
92
+ await fsp2.mkdir(dir, { recursive: true });
93
+ continue;
94
+ }
95
+ if (code !== "EEXIST") throw err;
91
96
  try {
92
- const stat6 = await fsp.stat(lockPath);
97
+ const stat6 = await fsp2.stat(lockPath);
93
98
  if (Date.now() - stat6.mtimeMs > staleMs) {
94
- await fsp.unlink(lockPath);
99
+ await fsp2.unlink(lockPath);
95
100
  continue;
96
101
  }
97
102
  } catch {
@@ -111,21 +116,21 @@ async function withFileLock(targetPath, fn, opts = {}) {
111
116
  } catch {
112
117
  }
113
118
  try {
114
- await fsp.unlink(lockPath);
119
+ await fsp2.unlink(lockPath);
115
120
  } catch {
116
121
  }
117
122
  }
118
123
  }
119
124
  async function renameWithRetry(from, to) {
120
125
  if (process.platform !== "win32") {
121
- await fsp.rename(from, to);
126
+ await fsp2.rename(from, to);
122
127
  return;
123
128
  }
124
129
  const delays = [10, 25, 60, 120, 250];
125
130
  let lastErr;
126
131
  for (let i = 0; i <= delays.length; i++) {
127
132
  try {
128
- await fsp.rename(from, to);
133
+ await fsp2.rename(from, to);
129
134
  return;
130
135
  } catch (err) {
131
136
  lastErr = err;
@@ -229,7 +234,7 @@ var DefaultLogger = class _DefaultLogger {
229
234
  this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
230
235
  if (this.file) {
231
236
  try {
232
- fs.mkdirSync(path11.dirname(this.file), { recursive: true });
237
+ fs.mkdirSync(path3.dirname(this.file), { recursive: true });
233
238
  } catch {
234
239
  }
235
240
  }
@@ -431,203 +436,1029 @@ function isEmptyMessage(msg) {
431
436
  return msg.content.length === 0;
432
437
  }
433
438
 
434
- // src/storage/session-store.ts
435
- function sanitizeModel(model) {
436
- return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
437
- }
438
- function generateSessionId(startedAt, model) {
439
- const date = startedAt.slice(0, 10);
440
- const time = startedAt.slice(11, 19).replace(/:/g, "-");
441
- const suffix = randomBytes(2).toString("hex");
442
- const modelPart = model ? `_${sanitizeModel(model)}` : "";
443
- return `${date}/${time}Z${modelPart}_${suffix}`;
439
+ // src/utils/index.ts
440
+ init_atomic_write();
441
+
442
+ // src/utils/error.ts
443
+ function toErrorMessage(err) {
444
+ return err instanceof Error ? err.message : String(err);
444
445
  }
445
- var DefaultSessionStore = class _DefaultSessionStore {
446
- dir;
447
- events;
448
- secretScrubber;
449
- constructor(opts) {
450
- this.dir = opts.dir;
451
- this.events = opts.events;
452
- this.secretScrubber = opts.secretScrubber;
453
- }
454
- // ── Storage event helpers ───────────────────────────────────────────────────
455
- emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
456
- this.events?.emit("storage.read", {
457
- sessionId,
458
- store: "session",
459
- filePath,
460
- operation,
461
- outcome,
462
- durationMs,
463
- ...error !== void 0 ? { error } : {}
464
- });
465
- }
466
- emitWrite(sessionId, filePath, operation, outcome, durationMs, eventCount, error) {
467
- this.events?.emit("storage.write", {
468
- sessionId,
469
- store: "session",
470
- filePath,
471
- operation,
472
- outcome,
473
- durationMs,
474
- ...eventCount !== void 0 ? { eventCount } : {},
475
- ...error !== void 0 ? { error } : {}
476
- });
477
- }
478
- emitError(sessionId, filePath, operation, error, recoverable) {
479
- this.events?.emit("storage.error", {
480
- sessionId,
481
- store: "session",
482
- filePath,
483
- operation,
484
- error,
485
- recoverable
486
- });
487
- }
488
- /** Absolute path to the session index file. */
489
- get indexFile() {
490
- return path11.join(this.dir, "_index.jsonl");
491
- }
492
- /** Join session ID to its absolute path within the store directory. */
493
- sessionPath(id, ext) {
494
- return path11.join(this.dir, `${id}${ext}`);
446
+
447
+ // src/utils/safe-json.ts
448
+ function safeParse(input, maxBytes = 5e6) {
449
+ if (input.length > maxBytes) {
450
+ return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
495
451
  }
496
- /**
497
- * Ensure the directory implied by the session ID exists. When the ID
498
- * contains a date prefix like `2026-06-06/...`, this creates the date
499
- * subdirectory so sessions group naturally by day.
500
- */
501
- async ensureShardDir(id) {
502
- const dirPath = path11.dirname(path11.join(this.dir, id));
503
- await ensureDir(dirPath);
504
- return dirPath;
452
+ try {
453
+ return { ok: true, value: JSON.parse(input) };
454
+ } catch (err) {
455
+ return {
456
+ ok: false,
457
+ error: toErrorMessage(err)
458
+ };
505
459
  }
506
- async create(meta) {
507
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
508
- const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
509
- const shardDir = await this.ensureShardDir(id);
510
- const file = path11.join(shardDir, `${path11.basename(id)}.jsonl`);
511
- const t0 = Date.now();
512
- let handle;
513
- try {
514
- handle = await fsp.open(file, "a", 384);
515
- } catch (err) {
516
- this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), false);
517
- throw new Error(
518
- `Failed to open session file: ${err instanceof Error ? err.message : String(err)}`,
519
- { cause: err }
520
- );
521
- }
522
- try {
523
- const writer = new FileSessionWriter(id, handle, startedAt, meta, this.events, {
524
- dir: shardDir,
525
- filePath: file,
526
- secretScrubber: this.secretScrubber,
527
- onClose: (s) => this.appendToIndex(s)
528
- });
529
- this.emitWrite(id, file, "create", "success", Date.now() - t0);
530
- return writer;
531
- } catch (err) {
532
- await handle.close().catch((e) => console.warn(JSON.stringify({
533
- level: "warn",
534
- event: "session_store.handle_close_failed",
535
- message: e instanceof Error ? e.message : String(e),
536
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
537
- })));
538
- this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), true);
539
- throw err;
460
+ }
461
+
462
+ // src/utils/string.ts
463
+ function truncate(s, max) {
464
+ return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
465
+ }
466
+
467
+ // src/utils/glob-match.ts
468
+ function escapeRegex(s) {
469
+ return s.replace(/[.+^${}()|\\]/g, "\\$&");
470
+ }
471
+ var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
472
+ var CACHE_MAX_SIZE = 2e3;
473
+ function getCachedGlob(pattern) {
474
+ const cached = COMPILED_GLOB_CACHE.get(pattern);
475
+ if (cached) return cached;
476
+ if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
477
+ const keys = [...COMPILED_GLOB_CACHE.keys()];
478
+ for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
479
+ COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
540
480
  }
541
481
  }
542
- async resume(id) {
543
- const file = this.sessionPath(id, ".jsonl");
544
- const t0 = Date.now();
545
- const data = await this.load(id);
546
- let handle;
547
- try {
548
- handle = await fsp.open(file, "a", 384);
549
- } catch (err) {
550
- this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), false);
551
- throw new Error(
552
- `Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
553
- { cause: err }
554
- );
555
- }
556
- try {
557
- const writer = new FileSessionWriter(
558
- id,
559
- handle,
560
- (/* @__PURE__ */ new Date()).toISOString(),
561
- {
562
- id,
563
- model: data.metadata.model,
564
- provider: data.metadata.provider
565
- },
566
- this.events,
567
- {
568
- resumed: true,
569
- // Shard directory (sessions/<date>/) — must match create() so the
570
- // .summary.json sidecar lands next to the JSONL instead of the
571
- // sessions root (where summaryFor() would never find it).
572
- dir: path11.dirname(file),
573
- filePath: file,
574
- secretScrubber: this.secretScrubber,
575
- onClose: (s) => this.appendToIndex(s)
576
- }
577
- );
578
- this.emitWrite(id, file, "resume", "success", Date.now() - t0);
579
- return { writer, data };
580
- } catch (err) {
581
- await handle.close().catch((e) => console.warn(JSON.stringify({
582
- level: "warn",
583
- event: "session_store.handle_close_failed",
584
- message: e instanceof Error ? e.message : String(e),
585
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
586
- })));
587
- this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), true);
588
- throw err;
589
- }
482
+ const re = compileGlob(pattern);
483
+ COMPILED_GLOB_CACHE.set(pattern, re);
484
+ return re;
485
+ }
486
+ var MAX_GLOB_PATTERN_LEN = 1024;
487
+ function compileGlob(pattern) {
488
+ if (pattern.length > MAX_GLOB_PATTERN_LEN) {
489
+ throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
590
490
  }
591
- async load(id) {
592
- const file = this.sessionPath(id, ".jsonl");
593
- const t0 = Date.now();
594
- let outcome = "success";
595
- let errorMsg;
596
- try {
597
- const raw = await fsp.readFile(file, "utf8");
598
- const lines = raw.split("\n").filter((l) => l.trim());
599
- const events = [];
600
- for (const line of lines) {
601
- try {
602
- const parsed = JSON.parse(line);
603
- if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
604
- events.push(parsed);
605
- }
606
- } catch {
491
+ let i = 0;
492
+ let re = "^";
493
+ while (i < pattern.length) {
494
+ const c = pattern[i];
495
+ if (c === "*") {
496
+ if (pattern[i + 1] === "*") {
497
+ re += ".*";
498
+ i += 2;
499
+ if (pattern[i] === "/") i++;
500
+ } else {
501
+ re += "[^/]*";
502
+ i++;
503
+ }
504
+ } else if (c === "?") {
505
+ re += "[^/]";
506
+ i++;
507
+ } else if (c === "[") {
508
+ let cls = "[";
509
+ i++;
510
+ if (pattern[i] === "!" || pattern[i] === "^") {
511
+ cls += "^";
512
+ i++;
513
+ }
514
+ while (i < pattern.length && pattern[i] !== "]") {
515
+ const ch = pattern[i] ?? "";
516
+ if (ch === "\\") {
517
+ cls += "\\\\";
518
+ } else if (ch === "]" || ch === "^") {
519
+ cls += `\\${ch}`;
520
+ } else {
521
+ cls += ch;
607
522
  }
523
+ i++;
608
524
  }
609
- const meta = this.metaFromEvents(id, events);
610
- const { messages, usage } = this.replay(events, id);
611
- const toolCallEnds = extractToolCallEnds(events);
612
- return { metadata: meta, events, messages, usage, toolCallEnds };
613
- } catch (err) {
614
- outcome = "failure";
615
- errorMsg = err instanceof Error ? err.message : String(err);
616
- throw err;
617
- } finally {
618
- this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
525
+ cls += "]";
526
+ re += cls;
527
+ i++;
528
+ } else {
529
+ re += escapeRegex(c ?? "");
530
+ i++;
619
531
  }
620
532
  }
621
- async list(limit = 20) {
622
- try {
623
- await ensureDir(this.dir);
624
- const indexed = await this.readIndex();
625
- if (indexed.length > 0) {
626
- indexed.sort((a, b) => {
627
- if (a.startedAt < b.startedAt) return 1;
628
- if (a.startedAt > b.startedAt) return -1;
629
- return a.id.localeCompare(b.id);
630
- });
533
+ re += "$";
534
+ return new RegExp(re);
535
+ }
536
+ function matchGlob(pattern, input) {
537
+ return getCachedGlob(pattern).test(input);
538
+ }
539
+ function matchAny(patterns, input) {
540
+ return patterns.some((p) => matchGlob(p, input));
541
+ }
542
+ function projectHash(absRoot) {
543
+ return createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 12);
544
+ }
545
+ function projectSlug(absRoot) {
546
+ const base = slugify(path3.basename(absRoot));
547
+ const hash = createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 6);
548
+ return `${base}-${hash}`;
549
+ }
550
+ function slugify(name) {
551
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
552
+ }
553
+ function wstackGlobalRoot() {
554
+ const fromEnv = process.env["WRONGSTACK_HOME"];
555
+ if (fromEnv && fromEnv.trim().length > 0) return path3.resolve(fromEnv);
556
+ return path3.join(os.homedir(), ".wrongstack");
557
+ }
558
+ function resolveWstackPaths(opts) {
559
+ const globalRoot = opts.globalRoot ?? (opts.userHome ? path3.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
560
+ const hash = projectHash(opts.projectRoot);
561
+ const slug = projectSlug(opts.projectRoot);
562
+ const projectDir = path3.join(globalRoot, "projects", slug);
563
+ return {
564
+ globalRoot,
565
+ configDir: globalRoot,
566
+ globalConfig: path3.join(globalRoot, "config.json"),
567
+ secretsKey: path3.join(globalRoot, ".key"),
568
+ globalMemory: path3.join(globalRoot, "memory.md"),
569
+ globalSkills: path3.join(globalRoot, "skills"),
570
+ globalPrompts: path3.join(globalRoot, "prompts"),
571
+ cacheDir: path3.join(globalRoot, "cache"),
572
+ modelsCache: path3.join(globalRoot, "cache", "models.dev.json"),
573
+ modelsOverlayCache: path3.join(globalRoot, "cache", "models-overlay.json"),
574
+ historyFile: path3.join(globalRoot, "history"),
575
+ logFile: path3.join(globalRoot, "logs", "wrongstack.log"),
576
+ projectDir,
577
+ projectCodebaseIndex: path3.join(projectDir, "codebase-index"),
578
+ projectMemory: path3.join(projectDir, "memory.md"),
579
+ projectSessions: path3.join(projectDir, "sessions"),
580
+ projectTrust: path3.join(projectDir, "trust.json"),
581
+ projectMeta: path3.join(projectDir, "meta.json"),
582
+ projectLocalConfig: path3.join(projectDir, "config.local.json"),
583
+ inProjectConfig: path3.join(opts.projectRoot, ".wrongstack", "config.json"),
584
+ inProjectAgentsFile: path3.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
585
+ inProjectSkills: path3.join(opts.projectRoot, ".wrongstack", "skills"),
586
+ inProjectWorktrees: path3.join(opts.projectRoot, ".wrongstack", "worktrees"),
587
+ projectHash: hash,
588
+ projectSlug: slug,
589
+ projectGoal: path3.join(projectDir, "goal.json"),
590
+ projectSpecs: path3.join(projectDir, "specs"),
591
+ projectTaskGraphs: path3.join(projectDir, "task-graphs"),
592
+ projectSddSession: path3.join(projectDir, "sdd-session.json"),
593
+ projectPlan: path3.join(projectDir, "plan.json"),
594
+ projectAutophase: path3.join(projectDir, "autophase"),
595
+ syncConfig: path3.join(globalRoot, "sync.json")
596
+ };
597
+ }
598
+
599
+ // src/utils/sleep.ts
600
+ function sleep(ms) {
601
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
602
+ }
603
+
604
+ // src/utils/assert-never.ts
605
+ function assertNever(x, message) {
606
+ const err = new Error(
607
+ `Unhandled case: ${JSON.stringify(x)}`
608
+ );
609
+ err.name = "AssertNeverError";
610
+ throw err;
611
+ }
612
+
613
+ // src/utils/deep-merge.ts
614
+ var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
615
+ "__proto__",
616
+ "constructor",
617
+ "prototype",
618
+ "__defineGetter__",
619
+ "__defineSetter__",
620
+ "__lookupGetter__",
621
+ "__lookupSetter__"
622
+ ]);
623
+ function isPrimitiveArray(a) {
624
+ return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
625
+ }
626
+ function deepMerge(base, patch, options = {}) {
627
+ const {
628
+ conflictResolution = "prefer-patch",
629
+ arrayMode = "replace",
630
+ protectProto = true,
631
+ onNonPrimitiveArrayReplace
632
+ } = options;
633
+ if (typeof base !== "object" || base === null) {
634
+ return conflictResolution === "prefer-patch" ? patch : base;
635
+ }
636
+ if (typeof patch !== "object" || patch === null) {
637
+ return conflictResolution === "prefer-patch" ? patch : base;
638
+ }
639
+ if (Array.isArray(base) && Array.isArray(patch)) {
640
+ if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
641
+ return [.../* @__PURE__ */ new Set([...base, ...patch])];
642
+ }
643
+ return conflictResolution === "prefer-patch" ? patch : base;
644
+ }
645
+ if (Array.isArray(base) || Array.isArray(patch)) {
646
+ return conflictResolution === "prefer-patch" ? patch : base;
647
+ }
648
+ const baseObj = base;
649
+ const patchObj = patch;
650
+ const out = { ...baseObj };
651
+ for (const [k, v] of Object.entries(patchObj)) {
652
+ if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
653
+ const existing = out[k];
654
+ if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
655
+ out[k] = deepMerge(existing, v, options);
656
+ } else if (Array.isArray(v) && Array.isArray(existing)) {
657
+ if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
658
+ onNonPrimitiveArrayReplace(k, existing.length, v.length);
659
+ }
660
+ out[k] = deepMerge(existing, v, options);
661
+ } else if (v !== void 0) {
662
+ if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
663
+ const existingLen = Array.isArray(existing) ? existing.length : 0;
664
+ onNonPrimitiveArrayReplace(k, existingLen, v.length);
665
+ }
666
+ out[k] = v;
667
+ }
668
+ }
669
+ return out;
670
+ }
671
+
672
+ // src/utils/tool-output-serializer.ts
673
+ function createToolOutputSerializer(opts = {}) {
674
+ const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
675
+ function serialize(value) {
676
+ if (typeof value === "string") return value;
677
+ if (value === null || value === void 0) return "";
678
+ if (typeof value === "object") {
679
+ if (Array.isArray(value)) return value.map(serialize).join("\n");
680
+ if ("text" in value) {
681
+ const t = value.text;
682
+ return typeof t === "string" ? t : JSON.stringify(value, null, 2);
683
+ }
684
+ try {
685
+ return JSON.stringify(value, null, 2);
686
+ } catch {
687
+ return String(value);
688
+ }
689
+ }
690
+ return String(value);
691
+ }
692
+ function enforceCap(text, remainingBudget) {
693
+ if (remainingBudget <= 0) {
694
+ return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
695
+ }
696
+ const textBytes = Buffer.byteLength(text, "utf8");
697
+ if (textBytes <= remainingBudget) {
698
+ return { text, newBudget: remainingBudget - textBytes };
699
+ }
700
+ const marker = `
701
+ \u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
702
+ `;
703
+ const markerBytes = Buffer.byteLength(marker, "utf8");
704
+ const available = remainingBudget - markerBytes;
705
+ if (available <= 0) {
706
+ return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
707
+ }
708
+ const half = Math.floor(available / 2);
709
+ const first = text.slice(0, half);
710
+ const second = text.slice(text.length - half);
711
+ return { text: `${first}${marker}${second}`, newBudget: 0 };
712
+ }
713
+ return { serialize, enforceCap, capBytes };
714
+ }
715
+
716
+ // src/utils/token-estimate.ts
717
+ var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
718
+ var CALIBRATION_GLOBAL_KEY = "__global__";
719
+ var _cals = /* @__PURE__ */ new Map();
720
+ function calState(key) {
721
+ let state = _cals.get(key);
722
+ if (!state) {
723
+ state = { ratio: 1, count: 0, prevEst: 0 };
724
+ _cals.set(key, state);
725
+ }
726
+ return state;
727
+ }
728
+ var MIN_SAMPLES_FOR_CALIBRATION = 3;
729
+ var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
730
+ var ESTIMATE_CACHE_MAX_SIZE = 1e4;
731
+ function getCachedEstimate(key, compute) {
732
+ const existing = ESTIMATE_CACHE.get(key);
733
+ if (existing !== void 0) return existing;
734
+ if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
735
+ let evicted = 0;
736
+ const maxEvict = Math.floor(ESTIMATE_CACHE_MAX_SIZE / 4);
737
+ for (const k of ESTIMATE_CACHE.keys()) {
738
+ if (evicted >= maxEvict) break;
739
+ ESTIMATE_CACHE.delete(k);
740
+ evicted++;
741
+ }
742
+ }
743
+ const estimate = compute(key);
744
+ ESTIMATE_CACHE.set(key, estimate);
745
+ return estimate;
746
+ }
747
+ function estimateToolInputTokens(input) {
748
+ if (typeof input === "string") return RoughTokenEstimate(input);
749
+ if (input === null || typeof input !== "object") {
750
+ return RoughTokenEstimate(String(input));
751
+ }
752
+ return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
753
+ }
754
+ function estimateToolResultTokens(content) {
755
+ if (typeof content === "string") return RoughTokenEstimate(content);
756
+ return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
757
+ }
758
+ function estimateTextTokens(text) {
759
+ return RoughTokenEstimate(text);
760
+ }
761
+ function computeMessageTokens(msg) {
762
+ if (typeof msg.content === "string") return estimateTextTokens(msg.content);
763
+ let total = 0;
764
+ for (const b of msg.content) {
765
+ if (b.type === "text") total += estimateTextTokens(b.text);
766
+ else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
767
+ else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
768
+ else total += RoughTokenEstimate(JSON.stringify(b));
769
+ }
770
+ return total;
771
+ }
772
+ function estimateMessageTokens(messages) {
773
+ let total = 0;
774
+ for (const m of messages) {
775
+ if (typeof m._estTokens === "number" && m._estTokens > 0) {
776
+ total += m._estTokens;
777
+ continue;
778
+ }
779
+ total += computeMessageTokens(m);
780
+ }
781
+ return total;
782
+ }
783
+ function estimateToolDefTokens(tool) {
784
+ const cached = tool._estDefTokens;
785
+ if (typeof cached === "number" && cached > 0) return cached;
786
+ return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
787
+ }
788
+ function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
789
+ let messagesTokens = 0;
790
+ if (typeof messages === "string") {
791
+ messagesTokens = RoughTokenEstimate(messages);
792
+ } else if (Array.isArray(messages)) {
793
+ for (const m of messages) {
794
+ if (typeof m === "object" && m !== null && "content" in m) {
795
+ const cached = m._estTokens;
796
+ if (typeof cached === "number" && cached > 0) {
797
+ messagesTokens += cached;
798
+ continue;
799
+ }
800
+ const content = m.content;
801
+ if (typeof content === "string") {
802
+ messagesTokens += RoughTokenEstimate(content);
803
+ } else if (Array.isArray(content)) {
804
+ for (const b of content) {
805
+ if (typeof b === "object" && b !== null) {
806
+ if (b.type === "text") {
807
+ messagesTokens += RoughTokenEstimate(b.text);
808
+ } else {
809
+ messagesTokens += RoughTokenEstimate(JSON.stringify(b));
810
+ }
811
+ }
812
+ }
813
+ }
814
+ }
815
+ }
816
+ }
817
+ let systemTokens = 0;
818
+ if (typeof systemPrompt === "string") {
819
+ systemTokens = RoughTokenEstimate(systemPrompt);
820
+ } else if (Array.isArray(systemPrompt)) {
821
+ for (const b of systemPrompt) {
822
+ if (typeof b === "object" && b !== null && b.type === "text") {
823
+ systemTokens += RoughTokenEstimate(b.text);
824
+ }
825
+ }
826
+ }
827
+ let toolsTokens = 0;
828
+ for (const t of tools) {
829
+ toolsTokens += estimateToolDefTokens(t);
830
+ }
831
+ const total = messagesTokens + systemTokens + toolsTokens;
832
+ calState(calibrationKey).prevEst = total;
833
+ return {
834
+ messages: messagesTokens,
835
+ systemPrompt: systemTokens,
836
+ tools: toolsTokens,
837
+ total
838
+ };
839
+ }
840
+ function getCalibrationState(calibrationKey = CALIBRATION_GLOBAL_KEY) {
841
+ const cal = calState(calibrationKey);
842
+ return {
843
+ ratio: cal.ratio,
844
+ count: cal.count,
845
+ calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION
846
+ };
847
+ }
848
+ function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
849
+ const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);
850
+ const cal = calState(calibrationKey);
851
+ if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
852
+ const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));
853
+ return {
854
+ messages: Math.round(result.messages * safeRatio),
855
+ systemPrompt: Math.round(result.systemPrompt * safeRatio),
856
+ tools: Math.round(result.tools * safeRatio),
857
+ total: Math.round(result.total * safeRatio)
858
+ };
859
+ }
860
+ return result;
861
+ }
862
+
863
+ // src/utils/json-schema-validate.ts
864
+ function validateAgainstSchema(value, schema) {
865
+ const errors = [];
866
+ walk(value, schema, "", errors);
867
+ return { ok: errors.length === 0, errors };
868
+ }
869
+ function walk(value, schema, path19, errors) {
870
+ if (schema.enum !== void 0) {
871
+ if (!schema.enum.some((e) => deepEqual(e, value))) {
872
+ errors.push({
873
+ path: path19 || "<root>",
874
+ message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
875
+ });
876
+ return;
877
+ }
878
+ }
879
+ if (typeof schema.type === "string") {
880
+ if (!checkType(value, schema.type)) {
881
+ errors.push({
882
+ path: path19 || "<root>",
883
+ message: `expected ${schema.type}, got ${describeType(value)}`
884
+ });
885
+ return;
886
+ }
887
+ }
888
+ if (schema.type === "object" && isPlainObject(value)) {
889
+ const obj = value;
890
+ for (const req of schema.required ?? []) {
891
+ if (!(req in obj)) {
892
+ errors.push({ path: joinPath(path19, req), message: "required property missing" });
893
+ }
894
+ }
895
+ if (schema.properties) {
896
+ for (const [key, subSchema] of Object.entries(schema.properties)) {
897
+ if (key in obj) {
898
+ walk(obj[key], subSchema, joinPath(path19, key), errors);
899
+ }
900
+ }
901
+ }
902
+ }
903
+ if (schema.type === "array" && Array.isArray(value) && schema.items) {
904
+ for (let i = 0; i < value.length; i++) {
905
+ walk(value[i], schema.items, `${path19}[${i}]`, errors);
906
+ }
907
+ }
908
+ }
909
+ function checkType(value, type) {
910
+ switch (type) {
911
+ case "string":
912
+ return typeof value === "string";
913
+ case "number":
914
+ return typeof value === "number" && !Number.isNaN(value);
915
+ case "integer":
916
+ return typeof value === "number" && Number.isInteger(value);
917
+ case "boolean":
918
+ return typeof value === "boolean";
919
+ case "null":
920
+ return value === null;
921
+ case "array":
922
+ return Array.isArray(value);
923
+ case "object":
924
+ return isPlainObject(value);
925
+ default:
926
+ return true;
927
+ }
928
+ }
929
+ function isPlainObject(v) {
930
+ return typeof v === "object" && v !== null && !Array.isArray(v);
931
+ }
932
+ function describeType(v) {
933
+ if (v === null) return "null";
934
+ if (Array.isArray(v)) return "array";
935
+ return typeof v;
936
+ }
937
+ function joinPath(parent, key) {
938
+ if (!parent) return key;
939
+ return `${parent}.${key}`;
940
+ }
941
+ function deepEqual(a, b) {
942
+ if (a === b) return true;
943
+ if (typeof a !== typeof b) return false;
944
+ if (a === null || b === null) return a === b;
945
+ if (Array.isArray(a) && Array.isArray(b)) {
946
+ return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
947
+ }
948
+ if (typeof a === "object" && typeof b === "object") {
949
+ const ak = Object.keys(a);
950
+ const bk = Object.keys(b);
951
+ if (ak.length !== bk.length) return false;
952
+ return ak.every(
953
+ (k) => deepEqual(a[k], b[k])
954
+ );
955
+ }
956
+ return false;
957
+ }
958
+
959
+ // src/utils/regex-guard.ts
960
+ var MAX_PATTERN_LEN = 512;
961
+ var DANGEROUS_PATTERNS = [
962
+ /(\([^)]*[+*][^)]*\))[+*]/,
963
+ // (a+)+, (.*)+, etc
964
+ /(\(\?:[^)]*[+*][^)]*\))[+*]/
965
+ // same, with non-capturing group
966
+ ];
967
+ function compileUserRegex(pattern, flags) {
968
+ if (typeof pattern !== "string") {
969
+ return { ok: false, reason: "pattern must be a string" };
970
+ }
971
+ if (pattern.length === 0) {
972
+ return { ok: false, reason: "pattern is empty" };
973
+ }
974
+ if (pattern.length > MAX_PATTERN_LEN) {
975
+ return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
976
+ }
977
+ for (const rx of DANGEROUS_PATTERNS) {
978
+ if (rx.test(pattern)) {
979
+ return {
980
+ ok: false,
981
+ reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
982
+ };
983
+ }
984
+ }
985
+ try {
986
+ return { ok: true, regex: new RegExp(pattern, flags) };
987
+ } catch (err) {
988
+ return {
989
+ ok: false,
990
+ reason: err instanceof Error ? err.message : "invalid regex"
991
+ };
992
+ }
993
+ }
994
+ var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
995
+ var IS_WINDOWS = process.platform === "win32";
996
+ var SEP = IS_WINDOWS ? "\\" : "/";
997
+ function isGlob(p) {
998
+ for (const c of p) {
999
+ if (GLOB_CHARS.has(c)) return true;
1000
+ }
1001
+ return false;
1002
+ }
1003
+ function globToRegex(pat) {
1004
+ let i = 0;
1005
+ let re = "^";
1006
+ while (i < pat.length) {
1007
+ const c = expectDefined(pat[i]);
1008
+ if (c === "*") {
1009
+ if (pat[i + 1] === "*") {
1010
+ re += ".*";
1011
+ i += 2;
1012
+ if (pat[i] === "/") i++;
1013
+ } else {
1014
+ re += "[^/\\\\]*";
1015
+ i++;
1016
+ }
1017
+ } else if (c === "?") {
1018
+ re += "[^/\\\\]";
1019
+ i++;
1020
+ } else if (c === "[") {
1021
+ let cls = "[";
1022
+ i++;
1023
+ if (pat[i] === "!" || pat[i] === "^") {
1024
+ cls += "^";
1025
+ i++;
1026
+ }
1027
+ while (i < pat.length && pat[i] !== "]") {
1028
+ const ch = pat[i] ?? "";
1029
+ if (ch === "\\") cls += "\\\\";
1030
+ else if (ch === "]" || ch === "^") cls += `\\${ch}`;
1031
+ else cls += ch;
1032
+ i++;
1033
+ }
1034
+ cls += "]";
1035
+ re += cls;
1036
+ i++;
1037
+ } else {
1038
+ re += c.replace(/[.+^${}()|\\]/g, "\\$&");
1039
+ i++;
1040
+ }
1041
+ }
1042
+ return new RegExp(re + "$");
1043
+ }
1044
+ function baseDir(pat) {
1045
+ let i = pat.length - 1;
1046
+ while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
1047
+ const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
1048
+ return cut < 0 ? "." : pat.slice(0, cut);
1049
+ }
1050
+ async function expandGlob(pattern) {
1051
+ if (!isGlob(pattern)) return [pattern];
1052
+ const results = /* @__PURE__ */ new Set();
1053
+ const abs = isAbsolute(pattern);
1054
+ const base = abs ? baseDir(pattern) : baseDir(pattern);
1055
+ const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
1056
+ async function walk3(dir, pat) {
1057
+ let entries;
1058
+ try {
1059
+ entries = await fsp2.readdir(dir);
1060
+ } catch {
1061
+ return;
1062
+ }
1063
+ const firstGlob = pat.search(/[*?[[]/);
1064
+ if (firstGlob < 0) {
1065
+ const re = globToRegex(pat);
1066
+ for (const e of entries) {
1067
+ if (re.test(e)) {
1068
+ const full = `${dir}${SEP}${e}`;
1069
+ results.add(abs ? resolve(full) : full);
1070
+ }
1071
+ }
1072
+ return;
1073
+ }
1074
+ const before = pat.slice(0, firstGlob);
1075
+ const rest = pat.slice(firstGlob);
1076
+ if (before.endsWith("**")) {
1077
+ await walk3(dir, rest);
1078
+ for (const e of entries) {
1079
+ const full = `${dir}${SEP}${e}`;
1080
+ try {
1081
+ const stat6 = await fsp2.stat(full);
1082
+ if (stat6.isDirectory()) await walk3(full, rest);
1083
+ } catch {
1084
+ }
1085
+ }
1086
+ } else if (before === "") {
1087
+ const re = globToRegex(rest);
1088
+ for (const e of entries) {
1089
+ if (re.test(e)) {
1090
+ const full = `${dir}${SEP}${e}`;
1091
+ results.add(abs ? resolve(full) : full);
1092
+ }
1093
+ }
1094
+ } else {
1095
+ const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
1096
+ if (entries.includes(seg)) {
1097
+ const full = `${dir}${SEP}${seg}`;
1098
+ try {
1099
+ const stat6 = await fsp2.stat(full);
1100
+ if (stat6.isDirectory()) await walk3(full, rest);
1101
+ } catch {
1102
+ }
1103
+ }
1104
+ }
1105
+ }
1106
+ await walk3(base === "." ? "." : base, relPat);
1107
+ return [...results];
1108
+ }
1109
+
1110
+ // src/utils/json-repair.ts
1111
+ function completePartialObject(s) {
1112
+ if (!s.trim().startsWith("{")) return s;
1113
+ if (tryParse(s).ok) return s;
1114
+ return repairTruncated(s);
1115
+ }
1116
+ function repairTruncated(s) {
1117
+ const stack = [];
1118
+ let inString = false;
1119
+ let escaped = false;
1120
+ let sawKey = false;
1121
+ let prevSig = "";
1122
+ let contentEnd = 0;
1123
+ let stringBraceDepth = 0;
1124
+ for (let i = 0; i < s.length; i++) {
1125
+ const ch = expectDefined(s[i]);
1126
+ if (inString) {
1127
+ contentEnd = i + 1;
1128
+ if (escaped) {
1129
+ escaped = false;
1130
+ continue;
1131
+ }
1132
+ if (ch === "\\") {
1133
+ escaped = true;
1134
+ continue;
1135
+ }
1136
+ if (ch === '"') {
1137
+ inString = false;
1138
+ prevSig = '"';
1139
+ stringBraceDepth = 0;
1140
+ continue;
1141
+ }
1142
+ if (ch === "{") stringBraceDepth++;
1143
+ else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
1144
+ continue;
1145
+ }
1146
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
1147
+ contentEnd = i + 1;
1148
+ if (ch === '"') {
1149
+ inString = true;
1150
+ sawKey = true;
1151
+ stringBraceDepth = 0;
1152
+ prevSig = '"';
1153
+ } else if (ch === "{" || ch === "[") {
1154
+ stack.push(ch);
1155
+ prevSig = ch;
1156
+ } else if (ch === "}" || ch === "]") {
1157
+ stack.pop();
1158
+ prevSig = ch;
1159
+ } else {
1160
+ prevSig = ch;
1161
+ }
1162
+ }
1163
+ if (!sawKey && !inString) return s;
1164
+ let result = s.slice(0, contentEnd);
1165
+ if (inString) {
1166
+ if (escaped) {
1167
+ result = result.slice(0, -1);
1168
+ } else if (endsWithInvalidEscape(result)) {
1169
+ result = result.slice(0, -2);
1170
+ }
1171
+ if (stringBraceDepth > 0) result += "}".repeat(stringBraceDepth);
1172
+ result += '"';
1173
+ } else if (prevSig === ":") {
1174
+ result += "null";
1175
+ }
1176
+ for (let k = stack.length - 1; k >= 0; k--) {
1177
+ result += stack[k] === "{" ? "}" : "]";
1178
+ }
1179
+ if (!tryParse(result).ok) {
1180
+ const patched = result.replace(/:(\s*)([}\]])/g, ":null$2");
1181
+ if (tryParse(patched).ok) result = patched;
1182
+ }
1183
+ return result;
1184
+ }
1185
+ var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
1186
+ function endsWithInvalidEscape(str) {
1187
+ const last = str[str.length - 1];
1188
+ if (str[str.length - 2] !== "\\" || last === void 0) return false;
1189
+ if (VALID_ESCAPE.has(last)) return false;
1190
+ let backslashes = 0;
1191
+ for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
1192
+ return backslashes % 2 === 1;
1193
+ }
1194
+ function tryParse(s) {
1195
+ try {
1196
+ return { ok: true, value: JSON.parse(s) };
1197
+ } catch {
1198
+ return { ok: false };
1199
+ }
1200
+ }
1201
+
1202
+ // src/utils/merge-models-payload.ts
1203
+ function mergeModelsPayload(base, overlay) {
1204
+ const out = {};
1205
+ for (const [id, provider] of Object.entries(base)) {
1206
+ out[id] = cloneProvider(provider);
1207
+ }
1208
+ for (const [id, ovProvider] of Object.entries(overlay)) {
1209
+ const existing = out[id];
1210
+ out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
1211
+ }
1212
+ return out;
1213
+ }
1214
+ function mergeProvider(base, overlay) {
1215
+ const models = {};
1216
+ for (const [mid, m] of Object.entries(base.models ?? {})) {
1217
+ models[mid] = { ...m };
1218
+ }
1219
+ for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
1220
+ const existing = models[mid];
1221
+ models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
1222
+ }
1223
+ return {
1224
+ ...base,
1225
+ // Overlay scalar fields win when explicitly provided; otherwise keep base.
1226
+ ...stripUndefined({
1227
+ id: overlay.id,
1228
+ name: overlay.name,
1229
+ npm: overlay.npm,
1230
+ api: overlay.api,
1231
+ env: overlay.env,
1232
+ doc: overlay.doc
1233
+ }),
1234
+ models
1235
+ };
1236
+ }
1237
+ function mergeModel(base, overlay) {
1238
+ const merged = { ...base, ...overlay };
1239
+ if (base.limit || overlay.limit) {
1240
+ merged.limit = { ...base.limit, ...overlay.limit };
1241
+ }
1242
+ if (base.cost || overlay.cost) {
1243
+ merged.cost = { ...base.cost, ...overlay.cost };
1244
+ }
1245
+ if (base.modalities || overlay.modalities) {
1246
+ merged.modalities = { ...base.modalities, ...overlay.modalities };
1247
+ }
1248
+ return merged;
1249
+ }
1250
+ function cloneProvider(p) {
1251
+ const models = {};
1252
+ for (const [mid, m] of Object.entries(p.models ?? {})) {
1253
+ models[mid] = { ...m };
1254
+ }
1255
+ return { ...p, models };
1256
+ }
1257
+ function stripUndefined(obj) {
1258
+ const out = {};
1259
+ for (const [k, v] of Object.entries(obj)) {
1260
+ if (v !== void 0) out[k] = v;
1261
+ }
1262
+ return out;
1263
+ }
1264
+
1265
+ // src/storage/session-store.ts
1266
+ function sanitizeModel(model) {
1267
+ return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
1268
+ }
1269
+ function generateSessionId(startedAt, model) {
1270
+ const date = startedAt.slice(0, 10);
1271
+ const time = startedAt.slice(11, 19).replace(/:/g, "-");
1272
+ const suffix = randomBytes(2).toString("hex");
1273
+ const modelPart = model ? `_${sanitizeModel(model)}` : "";
1274
+ return `${date}/${time}Z${modelPart}_${suffix}`;
1275
+ }
1276
+ var DefaultSessionStore = class _DefaultSessionStore {
1277
+ dir;
1278
+ events;
1279
+ secretScrubber;
1280
+ constructor(opts) {
1281
+ this.dir = opts.dir;
1282
+ this.events = opts.events;
1283
+ this.secretScrubber = opts.secretScrubber;
1284
+ }
1285
+ // ── Storage event helpers ───────────────────────────────────────────────────
1286
+ emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
1287
+ this.events?.emit("storage.read", {
1288
+ sessionId,
1289
+ store: "session",
1290
+ filePath,
1291
+ operation,
1292
+ outcome,
1293
+ durationMs,
1294
+ ...error !== void 0 ? { error } : {}
1295
+ });
1296
+ }
1297
+ emitWrite(sessionId, filePath, operation, outcome, durationMs, eventCount, error) {
1298
+ this.events?.emit("storage.write", {
1299
+ sessionId,
1300
+ store: "session",
1301
+ filePath,
1302
+ operation,
1303
+ outcome,
1304
+ durationMs,
1305
+ ...eventCount !== void 0 ? { eventCount } : {},
1306
+ ...error !== void 0 ? { error } : {}
1307
+ });
1308
+ }
1309
+ emitError(sessionId, filePath, operation, error, recoverable) {
1310
+ this.events?.emit("storage.error", {
1311
+ sessionId,
1312
+ store: "session",
1313
+ filePath,
1314
+ operation,
1315
+ error,
1316
+ recoverable
1317
+ });
1318
+ }
1319
+ /** Absolute path to the session index file. */
1320
+ get indexFile() {
1321
+ return path3.join(this.dir, "_index.jsonl");
1322
+ }
1323
+ /** Join session ID to its absolute path within the store directory. */
1324
+ sessionPath(id, ext) {
1325
+ return path3.join(this.dir, `${id}${ext}`);
1326
+ }
1327
+ /**
1328
+ * Ensure the directory implied by the session ID exists. When the ID
1329
+ * contains a date prefix like `2026-06-06/...`, this creates the date
1330
+ * subdirectory so sessions group naturally by day.
1331
+ */
1332
+ async ensureShardDir(id) {
1333
+ const dirPath = path3.dirname(path3.join(this.dir, id));
1334
+ await ensureDir(dirPath);
1335
+ return dirPath;
1336
+ }
1337
+ async create(meta) {
1338
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1339
+ const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
1340
+ const shardDir = await this.ensureShardDir(id);
1341
+ const file = path3.join(shardDir, `${path3.basename(id)}.jsonl`);
1342
+ const t0 = Date.now();
1343
+ let handle;
1344
+ try {
1345
+ handle = await fsp2.open(file, "a", 384);
1346
+ } catch (err) {
1347
+ this.emitError(id, file, "create", toErrorMessage(err), false);
1348
+ throw new Error(
1349
+ `Failed to open session file: ${toErrorMessage(err)}`,
1350
+ { cause: err }
1351
+ );
1352
+ }
1353
+ try {
1354
+ const writer = new FileSessionWriter(id, handle, startedAt, meta, this.events, {
1355
+ dir: shardDir,
1356
+ filePath: file,
1357
+ secretScrubber: this.secretScrubber,
1358
+ onClose: (s) => this.appendToIndex(s)
1359
+ });
1360
+ this.emitWrite(id, file, "create", "success", Date.now() - t0);
1361
+ return writer;
1362
+ } catch (err) {
1363
+ await handle.close().catch((e) => console.warn(JSON.stringify({
1364
+ level: "warn",
1365
+ event: "session_store.handle_close_failed",
1366
+ message: e instanceof Error ? e.message : String(e),
1367
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1368
+ })));
1369
+ this.emitError(id, file, "create", toErrorMessage(err), true);
1370
+ throw err;
1371
+ }
1372
+ }
1373
+ async resume(id) {
1374
+ const file = this.sessionPath(id, ".jsonl");
1375
+ const t0 = Date.now();
1376
+ const data = await this.load(id);
1377
+ let handle;
1378
+ try {
1379
+ handle = await fsp2.open(file, "a", 384);
1380
+ } catch (err) {
1381
+ this.emitError(id, file, "resume", toErrorMessage(err), false);
1382
+ throw new Error(
1383
+ `Failed to open session "${id}" for append: ${toErrorMessage(err)}`,
1384
+ { cause: err }
1385
+ );
1386
+ }
1387
+ try {
1388
+ const writer = new FileSessionWriter(
1389
+ id,
1390
+ handle,
1391
+ (/* @__PURE__ */ new Date()).toISOString(),
1392
+ {
1393
+ id,
1394
+ model: data.metadata.model,
1395
+ provider: data.metadata.provider
1396
+ },
1397
+ this.events,
1398
+ {
1399
+ resumed: true,
1400
+ // Shard directory (sessions/<date>/) — must match create() so the
1401
+ // .summary.json sidecar lands next to the JSONL instead of the
1402
+ // sessions root (where summaryFor() would never find it).
1403
+ dir: path3.dirname(file),
1404
+ filePath: file,
1405
+ secretScrubber: this.secretScrubber,
1406
+ onClose: (s) => this.appendToIndex(s)
1407
+ }
1408
+ );
1409
+ this.emitWrite(id, file, "resume", "success", Date.now() - t0);
1410
+ return { writer, data };
1411
+ } catch (err) {
1412
+ await handle.close().catch((e) => console.warn(JSON.stringify({
1413
+ level: "warn",
1414
+ event: "session_store.handle_close_failed",
1415
+ message: e instanceof Error ? e.message : String(e),
1416
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1417
+ })));
1418
+ this.emitError(id, file, "resume", toErrorMessage(err), true);
1419
+ throw err;
1420
+ }
1421
+ }
1422
+ async load(id) {
1423
+ const file = this.sessionPath(id, ".jsonl");
1424
+ const t0 = Date.now();
1425
+ let outcome = "success";
1426
+ let errorMsg;
1427
+ try {
1428
+ const raw = await fsp2.readFile(file, "utf8");
1429
+ const lines = raw.split("\n").filter((l) => l.trim());
1430
+ const events = [];
1431
+ for (const line of lines) {
1432
+ try {
1433
+ const parsed = JSON.parse(line);
1434
+ if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
1435
+ events.push(parsed);
1436
+ }
1437
+ } catch {
1438
+ }
1439
+ }
1440
+ const meta = this.metaFromEvents(id, events);
1441
+ const { messages, usage } = this.replay(events, id);
1442
+ const toolCallEnds = extractToolCallEnds(events);
1443
+ return { metadata: meta, events, messages, usage, toolCallEnds };
1444
+ } catch (err) {
1445
+ outcome = "failure";
1446
+ errorMsg = toErrorMessage(err);
1447
+ throw err;
1448
+ } finally {
1449
+ this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
1450
+ }
1451
+ }
1452
+ async list(limit = 20) {
1453
+ try {
1454
+ await ensureDir(this.dir);
1455
+ const indexed = await this.readIndex();
1456
+ if (indexed.length > 0) {
1457
+ indexed.sort((a, b) => {
1458
+ if (a.startedAt < b.startedAt) return 1;
1459
+ if (a.startedAt > b.startedAt) return -1;
1460
+ return a.id.localeCompare(b.id);
1461
+ });
631
1462
  return indexed.slice(0, limit);
632
1463
  }
633
1464
  const ids = await this.collectSessionIds(this.dir);
@@ -659,7 +1490,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
659
1490
  try {
660
1491
  await ensureDir(this.dir);
661
1492
  const line = JSON.stringify(summary) + "\n";
662
- await fsp.appendFile(this.indexFile, line, "utf8");
1493
+ await fsp2.appendFile(this.indexFile, line, "utf8");
663
1494
  this.indexAppendCount++;
664
1495
  if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
665
1496
  await this.compactIndex();
@@ -673,7 +1504,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
673
1504
  try {
674
1505
  await ensureDir(this.dir);
675
1506
  const line = JSON.stringify({ action: "delete", id }) + "\n";
676
- await fsp.appendFile(this.indexFile, line, "utf8");
1507
+ await fsp2.appendFile(this.indexFile, line, "utf8");
677
1508
  this.indexAppendCount++;
678
1509
  } catch {
679
1510
  }
@@ -691,11 +1522,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
691
1522
  if (entries.length === 0) return;
692
1523
  const tmp = `${this.indexFile}.compact.tmp`;
693
1524
  const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
694
- await fsp.writeFile(tmp, lines, "utf8");
695
- await fsp.rename(tmp, this.indexFile);
1525
+ await fsp2.writeFile(tmp, lines, "utf8");
1526
+ await fsp2.rename(tmp, this.indexFile);
696
1527
  } catch (err) {
697
1528
  outcome = "failure";
698
- errorMsg = err instanceof Error ? err.message : String(err);
1529
+ errorMsg = toErrorMessage(err);
699
1530
  } finally {
700
1531
  this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
701
1532
  }
@@ -708,7 +1539,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
708
1539
  async readIndex() {
709
1540
  let raw;
710
1541
  try {
711
- raw = await fsp.readFile(this.indexFile, "utf8");
1542
+ raw = await fsp2.readFile(this.indexFile, "utf8");
712
1543
  } catch {
713
1544
  return [];
714
1545
  }
@@ -741,8 +1572,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
741
1572
  const valid = summaries.filter((s) => s !== null);
742
1573
  const tmp = `${this.indexFile}.tmp`;
743
1574
  const lines = valid.map((s) => JSON.stringify(s)).join("\n") + "\n";
744
- await fsp.writeFile(tmp, lines, "utf8");
745
- await fsp.rename(tmp, this.indexFile);
1575
+ await fsp2.writeFile(tmp, lines, "utf8");
1576
+ await fsp2.rename(tmp, this.indexFile);
746
1577
  return valid.length;
747
1578
  }
748
1579
  /** Recursively collect session IDs from date-shard subdirectories.
@@ -753,7 +1584,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
753
1584
  const ids = [];
754
1585
  let entries;
755
1586
  try {
756
- entries = await fsp.readdir(dir, { withFileTypes: true });
1587
+ entries = await fsp2.readdir(dir, { withFileTypes: true });
757
1588
  } catch {
758
1589
  return ids;
759
1590
  }
@@ -763,7 +1594,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
763
1594
  continue;
764
1595
  if (entry.isDirectory()) {
765
1596
  const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
766
- ids.push(...await this.collectSessionIds(path11.join(dir, entry.name), childPrefix, depth + 1));
1597
+ ids.push(...await this.collectSessionIds(path3.join(dir, entry.name), childPrefix, depth + 1));
767
1598
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
768
1599
  if (entry.name === "_index.jsonl") continue;
769
1600
  const base = entry.name.replace(/\.jsonl$/, "");
@@ -778,15 +1609,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
778
1609
  let outcome = "success";
779
1610
  let errorMsg;
780
1611
  try {
781
- const raw = await fsp.readFile(manifest, "utf8");
1612
+ const raw = await fsp2.readFile(manifest, "utf8");
782
1613
  this.emitRead(id, manifest, "summary", "success", Date.now() - t0);
783
1614
  return JSON.parse(raw);
784
1615
  } catch {
785
1616
  const full = this.sessionPath(id, ".jsonl");
786
- const stat6 = await fsp.stat(full);
1617
+ const stat6 = await fsp2.stat(full);
787
1618
  const summary = await this.summarize(id, stat6.mtime.toISOString());
788
1619
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
789
- const msg = err instanceof Error ? err.message : String(err);
1620
+ const msg = toErrorMessage(err);
790
1621
  this.emitError(id, manifest, "summary_fallback", msg, true);
791
1622
  console.warn(JSON.stringify({
792
1623
  level: "warn",
@@ -814,14 +1645,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
814
1645
  async deleteSession(id) {
815
1646
  const jsonlPath = this.sessionPath(id, ".jsonl");
816
1647
  const summaryPath = this.sessionPath(id, ".summary.json");
817
- const shardDir = path11.dirname(path11.join(this.dir, id));
818
- const base = path11.basename(id);
819
- const sessDir = path11.join(shardDir, base);
1648
+ const shardDir = path3.dirname(path3.join(this.dir, id));
1649
+ const base = path3.basename(id);
1650
+ const sessDir = path3.join(shardDir, base);
820
1651
  const deletions = [
821
- fsp.unlink(jsonlPath),
822
- fsp.unlink(summaryPath),
823
- fsp.unlink(path11.join(shardDir, `${base}.plan.json`)),
824
- fsp.unlink(path11.join(shardDir, `${base}.todos.json`))
1652
+ fsp2.unlink(jsonlPath),
1653
+ fsp2.unlink(summaryPath),
1654
+ fsp2.unlink(path3.join(shardDir, `${base}.plan.json`)),
1655
+ fsp2.unlink(path3.join(shardDir, `${base}.todos.json`))
825
1656
  ];
826
1657
  const results = await Promise.allSettled(deletions);
827
1658
  for (const r of results) {
@@ -838,12 +1669,12 @@ var DefaultSessionStore = class _DefaultSessionStore {
838
1669
  }
839
1670
  }
840
1671
  }
841
- await fsp.rm(sessDir, { recursive: true, force: true }).catch((err) => {
1672
+ await fsp2.rm(sessDir, { recursive: true, force: true }).catch((err) => {
842
1673
  console.warn(JSON.stringify({
843
1674
  level: "warn",
844
1675
  event: "session_store.rmdir_failed",
845
1676
  sessionId: id,
846
- message: err instanceof Error ? err.message : String(err),
1677
+ message: toErrorMessage(err),
847
1678
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
848
1679
  }));
849
1680
  });
@@ -857,16 +1688,16 @@ var DefaultSessionStore = class _DefaultSessionStore {
857
1688
  let deleted = 0;
858
1689
  let activeSessionId = null;
859
1690
  try {
860
- const raw = await fsp.readFile(path11.join(this.dir, "active.json"), "utf8");
1691
+ const raw = await fsp2.readFile(path3.join(this.dir, "active.json"), "utf8");
861
1692
  const active = JSON.parse(raw);
862
1693
  activeSessionId = active.sessionId ?? null;
863
1694
  } catch {
864
1695
  }
865
1696
  const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
866
1697
  const pruneFile = async (dir, name, prefix) => {
867
- const jsonlPath = path11.join(dir, name);
1698
+ const jsonlPath = path3.join(dir, name);
868
1699
  try {
869
- const stat6 = await fsp.stat(jsonlPath);
1700
+ const stat6 = await fsp2.stat(jsonlPath);
870
1701
  if (stat6.mtimeMs >= cutoff) return;
871
1702
  } catch {
872
1703
  return;
@@ -877,15 +1708,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
877
1708
  await this.deleteSession(id);
878
1709
  deleted++;
879
1710
  };
880
- const entries = await fsp.readdir(this.dir, { withFileTypes: true }).catch(() => []);
1711
+ const entries = await fsp2.readdir(this.dir, { withFileTypes: true }).catch(() => []);
881
1712
  for (const entry of entries) {
882
1713
  if (entry.isFile()) {
883
1714
  if (isPrunableJsonl(entry.name)) await pruneFile(this.dir, entry.name, "");
884
1715
  continue;
885
1716
  }
886
1717
  if (!entry.isDirectory()) continue;
887
- const dateDir = path11.join(this.dir, entry.name);
888
- const files = await fsp.readdir(dateDir, { withFileTypes: true }).catch(() => []);
1718
+ const dateDir = path3.join(this.dir, entry.name);
1719
+ const files = await fsp2.readdir(dateDir, { withFileTypes: true }).catch(() => []);
889
1720
  for (const file of files) {
890
1721
  if (!file.isFile() || !isPrunableJsonl(file.name)) continue;
891
1722
  await pruneFile(dateDir, file.name, entry.name);
@@ -896,11 +1727,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
896
1727
  }
897
1728
  for (const entry of entries) {
898
1729
  if (!entry.isDirectory()) continue;
899
- const dateDir = path11.join(this.dir, entry.name);
1730
+ const dateDir = path3.join(this.dir, entry.name);
900
1731
  try {
901
- const remaining = await fsp.readdir(dateDir);
1732
+ const remaining = await fsp2.readdir(dateDir);
902
1733
  if (remaining.length === 0) {
903
- await fsp.rmdir(dateDir).catch(() => void 0);
1734
+ await fsp2.rmdir(dateDir).catch(() => void 0);
904
1735
  }
905
1736
  } catch {
906
1737
  }
@@ -919,8 +1750,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
919
1750
  provider: "unknown"
920
1751
  })}
921
1752
  `;
922
- await fsp.writeFile(file, record, "utf8");
923
- await fsp.unlink(meta).catch(() => void 0);
1753
+ await fsp2.writeFile(file, record, "utf8");
1754
+ await fsp2.unlink(meta).catch(() => void 0);
924
1755
  }
925
1756
  async summarize(id, mtime) {
926
1757
  try {
@@ -1071,7 +1902,7 @@ var FileSessionWriter = class _FileSessionWriter {
1071
1902
  this.meta = meta;
1072
1903
  this.events = events;
1073
1904
  this.resumed = opts.resumed ?? false;
1074
- this.manifestFile = opts.dir ? path11.join(opts.dir, `${path11.basename(id)}.summary.json`) : "";
1905
+ this.manifestFile = opts.dir ? path3.join(opts.dir, `${path3.basename(id)}.summary.json`) : "";
1075
1906
  this.filePath = opts.filePath ?? "";
1076
1907
  this.secretScrubber = opts.secretScrubber;
1077
1908
  this.onCloseCb = opts.onClose;
@@ -1278,7 +2109,7 @@ var FileSessionWriter = class _FileSessionWriter {
1278
2109
  await this.enqueueWrite(batch);
1279
2110
  } catch (err) {
1280
2111
  outcome = "failure";
1281
- errorMsg = err instanceof Error ? err.message : String(err);
2112
+ errorMsg = toErrorMessage(err);
1282
2113
  this.appendFailCount += eventCount;
1283
2114
  const now = Date.now();
1284
2115
  if (now - this.lastAppendWarnAt > 5e3) {
@@ -1286,7 +2117,7 @@ var FileSessionWriter = class _FileSessionWriter {
1286
2117
  const tail = suppressed > 0 ? ` (+${suppressed} suppressed)` : "";
1287
2118
  console.warn(
1288
2119
  "[session] flush failed:",
1289
- err instanceof Error ? err.message : String(err),
2120
+ toErrorMessage(err),
1290
2121
  tail
1291
2122
  );
1292
2123
  this.lastAppendWarnAt = now;
@@ -1376,7 +2207,7 @@ var FileSessionWriter = class _FileSessionWriter {
1376
2207
  await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
1377
2208
  } catch (err) {
1378
2209
  outcome = "failure";
1379
- errorMsg = err instanceof Error ? err.message : String(err);
2210
+ errorMsg = toErrorMessage(err);
1380
2211
  } finally {
1381
2212
  this.events?.emit("storage.write", {
1382
2213
  sessionId: this.id,
@@ -1397,7 +2228,7 @@ var FileSessionWriter = class _FileSessionWriter {
1397
2228
  await this.onCloseCb?.(this.summary);
1398
2229
  } catch (err) {
1399
2230
  idxOutcome = "failure";
1400
- idxError = err instanceof Error ? err.message : String(err);
2231
+ idxError = toErrorMessage(err);
1401
2232
  } finally {
1402
2233
  this.events?.emit("storage.write", {
1403
2234
  sessionId: this.summary.id,
@@ -1450,7 +2281,7 @@ var FileSessionWriter = class _FileSessionWriter {
1450
2281
  }
1451
2282
  await this.flushBuffer();
1452
2283
  await this.writeChain;
1453
- const raw = await fsp.readFile(this.filePath, "utf8");
2284
+ const raw = await fsp2.readFile(this.filePath, "utf8");
1454
2285
  const lines = raw.split("\n");
1455
2286
  const kept = [];
1456
2287
  let removedCount = 0;
@@ -1488,13 +2319,13 @@ var FileSessionWriter = class _FileSessionWriter {
1488
2319
  }
1489
2320
  const truncated = kept.join("\n");
1490
2321
  const tmpPath = `${this.filePath}.rewind.tmp`;
1491
- await fsp.writeFile(tmpPath, truncated + "\n", "utf8");
2322
+ await fsp2.writeFile(tmpPath, truncated + "\n", "utf8");
1492
2323
  try {
1493
2324
  await this.handle.close();
1494
- await fsp.rename(tmpPath, this.filePath);
1495
- this.handle = await fsp.open(this.filePath, "a", 384);
2325
+ await fsp2.rename(tmpPath, this.filePath);
2326
+ this.handle = await fsp2.open(this.filePath, "a", 384);
1496
2327
  } catch (err) {
1497
- await fsp.unlink(tmpPath).catch(() => void 0);
2328
+ await fsp2.unlink(tmpPath).catch(() => void 0);
1498
2329
  throw err;
1499
2330
  }
1500
2331
  await this.append({
@@ -1526,7 +2357,7 @@ var FileSessionWriter = class _FileSessionWriter {
1526
2357
  provider: this.meta.provider ?? "unknown"
1527
2358
  })}
1528
2359
  `;
1529
- await fsp.writeFile(this.filePath, record, "utf8");
2360
+ await fsp2.writeFile(this.filePath, record, "utf8");
1530
2361
  }
1531
2362
  /**
1532
2363
  * Idea #1 — write an in-flight marker. The agent loop should call
@@ -1575,7 +2406,7 @@ var QueueStore = class {
1575
2406
  events;
1576
2407
  traceId;
1577
2408
  constructor(opts) {
1578
- this.file = path11.join(opts.dir, "queue.json");
2409
+ this.file = path3.join(opts.dir, "queue.json");
1579
2410
  this.events = opts.events;
1580
2411
  this.traceId = opts.traceId;
1581
2412
  }
@@ -1603,7 +2434,7 @@ var QueueStore = class {
1603
2434
  filePath: this.file,
1604
2435
  operation: "write",
1605
2436
  outcome: "failure",
1606
- error: err instanceof Error ? err.message : String(err),
2437
+ error: toErrorMessage(err),
1607
2438
  recoverable: false,
1608
2439
  ...this.traceId !== void 0 && { traceId: this.traceId }
1609
2440
  });
@@ -1611,7 +2442,7 @@ var QueueStore = class {
1611
2442
  level: "warn",
1612
2443
  event: "queue_store.write_failed",
1613
2444
  path: this.file,
1614
- message: err instanceof Error ? err.message : String(err),
2445
+ message: toErrorMessage(err),
1615
2446
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1616
2447
  }));
1617
2448
  }
@@ -1620,7 +2451,7 @@ var QueueStore = class {
1620
2451
  const t0 = Date.now();
1621
2452
  let raw;
1622
2453
  try {
1623
- raw = await fsp.readFile(this.file, "utf8");
2454
+ raw = await fsp2.readFile(this.file, "utf8");
1624
2455
  } catch (err) {
1625
2456
  const code = err.code;
1626
2457
  if (code === "ENOENT") {
@@ -1641,7 +2472,7 @@ var QueueStore = class {
1641
2472
  filePath: this.file,
1642
2473
  operation: "read",
1643
2474
  outcome: "failure",
1644
- error: err instanceof Error ? err.message : String(err),
2475
+ error: toErrorMessage(err),
1645
2476
  recoverable: true,
1646
2477
  ...this.traceId !== void 0 && { traceId: this.traceId }
1647
2478
  });
@@ -1649,7 +2480,7 @@ var QueueStore = class {
1649
2480
  level: "warn",
1650
2481
  event: "queue_store.read_failed",
1651
2482
  path: this.file,
1652
- message: err instanceof Error ? err.message : String(err),
2483
+ message: toErrorMessage(err),
1653
2484
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1654
2485
  }));
1655
2486
  return [];
@@ -1701,7 +2532,7 @@ var QueueStore = class {
1701
2532
  async clear() {
1702
2533
  const t0 = Date.now();
1703
2534
  try {
1704
- await fsp.unlink(this.file);
2535
+ await fsp2.unlink(this.file);
1705
2536
  this.events?.emit("storage.write", {
1706
2537
  sessionId: this.traceId ?? "~boot~",
1707
2538
  store: "queue",
@@ -1720,7 +2551,7 @@ var QueueStore = class {
1720
2551
  filePath: this.file,
1721
2552
  operation: "clear",
1722
2553
  outcome: "failure",
1723
- error: err instanceof Error ? err.message : String(err),
2554
+ error: toErrorMessage(err),
1724
2555
  recoverable: true,
1725
2556
  ...this.traceId !== void 0 && { traceId: this.traceId }
1726
2557
  });
@@ -1761,8 +2592,8 @@ var DefaultAttachmentStore = class {
1761
2592
  let spooledPath;
1762
2593
  let data = input.data;
1763
2594
  if (this.spoolDir && bytes >= this.spoolThreshold) {
1764
- await fsp.mkdir(this.spoolDir, { recursive: true });
1765
- spooledPath = path11.join(this.spoolDir, `${id}.bin`);
2595
+ await fsp2.mkdir(this.spoolDir, { recursive: true });
2596
+ spooledPath = path3.join(this.spoolDir, `${id}.bin`);
1766
2597
  await atomicWrite(spooledPath, input.data, {
1767
2598
  encoding: input.kind === "image" ? "base64" : "utf8"
1768
2599
  });
@@ -1824,7 +2655,7 @@ var DefaultAttachmentStore = class {
1824
2655
  for (const att of this.items.values()) {
1825
2656
  if (att.path) toDelete.push(att.path);
1826
2657
  }
1827
- await Promise.all(toDelete.map((p) => fsp.unlink(p).catch(() => void 0)));
2658
+ await Promise.all(toDelete.map((p) => fsp2.unlink(p).catch(() => void 0)));
1828
2659
  }
1829
2660
  this.items.clear();
1830
2661
  this.refs.length = 0;
@@ -1832,7 +2663,7 @@ var DefaultAttachmentStore = class {
1832
2663
  }
1833
2664
  async toBlock(att) {
1834
2665
  if (att.kind === "image") {
1835
- const data = att.data ?? (att.path ? await fsp.readFile(att.path, { encoding: "base64" }) : "");
2666
+ const data = att.data ?? (att.path ? await fsp2.readFile(att.path, { encoding: "base64" }) : "");
1836
2667
  return {
1837
2668
  type: "image",
1838
2669
  source: {
@@ -1842,7 +2673,7 @@ var DefaultAttachmentStore = class {
1842
2673
  }
1843
2674
  };
1844
2675
  }
1845
- const raw = att.data ?? (att.path ? await fsp.readFile(att.path, "utf8") : "");
2676
+ const raw = att.data ?? (att.path ? await fsp2.readFile(att.path, "utf8") : "");
1846
2677
  const label = att.meta.filename ? `<file path="${att.meta.filename}">` : "<pasted>";
1847
2678
  const close = att.meta.filename ? "</file>" : "</pasted>";
1848
2679
  return { type: "text", text: `${label}
@@ -1974,10 +2805,10 @@ var FileMemoryBackend = class {
1974
2805
  }
1975
2806
  async remember(scope, entry, filePath) {
1976
2807
  const file = this.resolveFile(filePath, scope);
1977
- await ensureDir(path11.dirname(file));
2808
+ await ensureDir(path3.dirname(file));
1978
2809
  let existing = "";
1979
2810
  try {
1980
- existing = await fsp.readFile(file, "utf8");
2811
+ existing = await fsp2.readFile(file, "utf8");
1981
2812
  } catch {
1982
2813
  }
1983
2814
  const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
@@ -1994,7 +2825,7 @@ ${line}`;
1994
2825
  return withFileLock(file, async () => {
1995
2826
  let existing;
1996
2827
  try {
1997
- existing = await fsp.readFile(file, "utf8");
2828
+ existing = await fsp2.readFile(file, "utf8");
1998
2829
  } catch {
1999
2830
  return 0;
2000
2831
  }
@@ -2030,7 +2861,7 @@ ${line}`;
2030
2861
  async readAll(scope, filePath) {
2031
2862
  const file = this.resolveFile(filePath, scope);
2032
2863
  try {
2033
- return await fsp.readFile(file, "utf8");
2864
+ return await fsp2.readFile(file, "utf8");
2034
2865
  } catch {
2035
2866
  return "";
2036
2867
  }
@@ -2065,7 +2896,7 @@ ${line}`;
2065
2896
  const file = this.resolveFile(filePath, scope);
2066
2897
  let existing;
2067
2898
  try {
2068
- existing = await fsp.readFile(file, "utf8");
2899
+ existing = await fsp2.readFile(file, "utf8");
2069
2900
  } catch {
2070
2901
  return 0;
2071
2902
  }
@@ -2085,7 +2916,7 @@ ${line}`;
2085
2916
  const next = lines.join("\n");
2086
2917
  const backup = `${file}.bak.${Date.now()}`;
2087
2918
  try {
2088
- await fsp.copyFile(file, backup);
2919
+ await fsp2.copyFile(file, backup);
2089
2920
  } catch {
2090
2921
  }
2091
2922
  try {
@@ -2202,7 +3033,7 @@ ${body.trim()}`);
2202
3033
  operation: "readAll",
2203
3034
  outcome: "failure",
2204
3035
  durationMs: dur,
2205
- error: err instanceof Error ? err.message : String(err),
3036
+ error: toErrorMessage(err),
2206
3037
  ...this.traceId !== void 0 && { traceId: this.traceId }
2207
3038
  });
2208
3039
  throw err;
@@ -2235,7 +3066,7 @@ ${body.trim()}`);
2235
3066
  operation: "read",
2236
3067
  outcome: "failure",
2237
3068
  durationMs: dur,
2238
- error: err instanceof Error ? err.message : String(err),
3069
+ error: toErrorMessage(err),
2239
3070
  ...this.traceId !== void 0 && { traceId: this.traceId }
2240
3071
  });
2241
3072
  throw err;
@@ -2288,7 +3119,7 @@ ${body.trim()}`);
2288
3119
  operation: "remember",
2289
3120
  outcome: "failure",
2290
3121
  durationMs: dur,
2291
- error: err instanceof Error ? err.message : String(err),
3122
+ error: toErrorMessage(err),
2292
3123
  ...this.traceId !== void 0 && { traceId: this.traceId }
2293
3124
  });
2294
3125
  throw err;
@@ -2444,7 +3275,7 @@ ${body.trim()}`);
2444
3275
  operation: "forget",
2445
3276
  outcome: "failure",
2446
3277
  durationMs: dur,
2447
- error: err instanceof Error ? err.message : String(err),
3278
+ error: toErrorMessage(err),
2448
3279
  ...this.traceId !== void 0 && { traceId: this.traceId }
2449
3280
  });
2450
3281
  throw err;
@@ -2486,7 +3317,7 @@ ${body.trim()}`);
2486
3317
  operation: "consolidate",
2487
3318
  outcome: "failure",
2488
3319
  durationMs: dur,
2489
- error: err instanceof Error ? err.message : String(err),
3320
+ error: toErrorMessage(err),
2490
3321
  ...this.traceId !== void 0 && { traceId: this.traceId }
2491
3322
  });
2492
3323
  throw err;
@@ -2526,7 +3357,7 @@ ${body.trim()}`);
2526
3357
  operation: "clear",
2527
3358
  outcome: "failure",
2528
3359
  durationMs: dur,
2529
- error: err instanceof Error ? err.message : String(err),
3360
+ error: toErrorMessage(err),
2530
3361
  ...this.traceId !== void 0 && { traceId: this.traceId }
2531
3362
  });
2532
3363
  throw err;
@@ -2562,7 +3393,7 @@ ${body.trim()}`);
2562
3393
  operation: "clear",
2563
3394
  outcome: "failure",
2564
3395
  durationMs: dur,
2565
- error: err instanceof Error ? err.message : String(err),
3396
+ error: toErrorMessage(err),
2566
3397
  ...this.traceId !== void 0 && { traceId: this.traceId }
2567
3398
  });
2568
3399
  throw err;
@@ -2722,7 +3553,7 @@ var AgentError = class extends WrongStackError {
2722
3553
  };
2723
3554
  function toWrongStackError(err, code = ERROR_CODES.AGENT_RUN_FAILED) {
2724
3555
  if (err instanceof WrongStackError) return err;
2725
- const message = err instanceof Error ? err.message : String(err);
3556
+ const message = toErrorMessage(err);
2726
3557
  return new AgentError({
2727
3558
  message,
2728
3559
  code: code === "UNKNOWN" ? ERROR_CODES.AGENT_RUN_FAILED : code,
@@ -2798,105 +3629,44 @@ var DefaultConfigStore = class {
2798
3629
  });
2799
3630
  }
2800
3631
  const prev = this.current;
2801
- this.current = next;
2802
- for (const w of this.watchers) {
2803
- try {
2804
- w(next, prev);
2805
- } catch (err) {
2806
- console.error(JSON.stringify({
2807
- level: "error",
2808
- event: "config_store.watcher_threw",
2809
- message: err instanceof Error ? err.message : String(err),
2810
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2811
- }));
2812
- }
2813
- }
2814
- return next;
2815
- }
2816
- watch(cb) {
2817
- this.watchers.add(cb);
2818
- return () => this.watchers.delete(cb);
2819
- }
2820
- };
2821
- var FROZEN_EMPTY = Object.freeze({});
2822
- function deepFreeze(obj) {
2823
- if (obj === null || typeof obj !== "object") return obj;
2824
- if (Object.isFrozen(obj)) return obj;
2825
- for (const key of Object.keys(obj)) {
2826
- const v = obj[key];
2827
- if (v !== null && typeof v === "object" && !Object.isFrozen(v)) {
2828
- deepFreeze(v);
2829
- }
2830
- }
2831
- return Object.freeze(obj);
2832
- }
2833
-
2834
- // src/types/secret-vault.ts
2835
- var ENCRYPTED_PREFIX = "enc:v1:";
2836
-
2837
- // src/security/secret-vault.ts
2838
- init_atomic_write();
2839
-
2840
- // src/utils/deep-merge.ts
2841
- var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
2842
- "__proto__",
2843
- "constructor",
2844
- "prototype",
2845
- "__defineGetter__",
2846
- "__defineSetter__",
2847
- "__lookupGetter__",
2848
- "__lookupSetter__"
2849
- ]);
2850
- function isPrimitiveArray(a) {
2851
- return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
2852
- }
2853
- function deepMerge(base, patch, options = {}) {
2854
- const {
2855
- conflictResolution = "prefer-patch",
2856
- arrayMode = "replace",
2857
- protectProto = true,
2858
- onNonPrimitiveArrayReplace
2859
- } = options;
2860
- if (typeof base !== "object" || base === null) {
2861
- return conflictResolution === "prefer-patch" ? patch : base;
2862
- }
2863
- if (typeof patch !== "object" || patch === null) {
2864
- return conflictResolution === "prefer-patch" ? patch : base;
2865
- }
2866
- if (Array.isArray(base) && Array.isArray(patch)) {
2867
- if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
2868
- return [.../* @__PURE__ */ new Set([...base, ...patch])];
2869
- }
2870
- return conflictResolution === "prefer-patch" ? patch : base;
2871
- }
2872
- if (Array.isArray(base) || Array.isArray(patch)) {
2873
- return conflictResolution === "prefer-patch" ? patch : base;
2874
- }
2875
- const baseObj = base;
2876
- const patchObj = patch;
2877
- const out = { ...baseObj };
2878
- for (const [k, v] of Object.entries(patchObj)) {
2879
- if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
2880
- const existing = out[k];
2881
- if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
2882
- out[k] = deepMerge(existing, v, options);
2883
- } else if (Array.isArray(v) && Array.isArray(existing)) {
2884
- if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
2885
- onNonPrimitiveArrayReplace(k, existing.length, v.length);
2886
- }
2887
- out[k] = deepMerge(existing, v, options);
2888
- } else if (v !== void 0) {
2889
- if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
2890
- const existingLen = Array.isArray(existing) ? existing.length : 0;
2891
- onNonPrimitiveArrayReplace(k, existingLen, v.length);
3632
+ this.current = next;
3633
+ for (const w of this.watchers) {
3634
+ try {
3635
+ w(next, prev);
3636
+ } catch (err) {
3637
+ console.error(JSON.stringify({
3638
+ level: "error",
3639
+ event: "config_store.watcher_threw",
3640
+ message: toErrorMessage(err),
3641
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3642
+ }));
2892
3643
  }
2893
- out[k] = v;
2894
3644
  }
3645
+ return next;
2895
3646
  }
2896
- return out;
3647
+ watch(cb) {
3648
+ this.watchers.add(cb);
3649
+ return () => this.watchers.delete(cb);
3650
+ }
3651
+ };
3652
+ var FROZEN_EMPTY = Object.freeze({});
3653
+ function deepFreeze(obj) {
3654
+ if (obj === null || typeof obj !== "object") return obj;
3655
+ if (Object.isFrozen(obj)) return obj;
3656
+ for (const key of Object.keys(obj)) {
3657
+ const v = obj[key];
3658
+ if (v !== null && typeof v === "object" && !Object.isFrozen(v)) {
3659
+ deepFreeze(v);
3660
+ }
3661
+ }
3662
+ return Object.freeze(obj);
2897
3663
  }
2898
3664
 
3665
+ // src/types/secret-vault.ts
3666
+ var ENCRYPTED_PREFIX = "enc:v1:";
3667
+
2899
3668
  // src/security/secret-vault.ts
3669
+ init_atomic_write();
2900
3670
  var KEY_BYTES = 32;
2901
3671
  var IV_BYTES = 12;
2902
3672
  var TAG_BYTES = 16;
@@ -2987,7 +3757,7 @@ var DefaultSecretVault = class {
2987
3757
  } catch (err) {
2988
3758
  if (err.code !== "ENOENT") throw err;
2989
3759
  }
2990
- fs.mkdirSync(path11.dirname(this.keyFile), { recursive: true });
3760
+ fs.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
2991
3761
  const key = randomBytes(KEY_BYTES);
2992
3762
  try {
2993
3763
  fs.writeFileSync(this.keyFile, key, { mode: 384, flag: "wx" });
@@ -3011,7 +3781,7 @@ var DefaultSecretVault = class {
3011
3781
  };
3012
3782
  function decryptConfigSecrets(cfg, vault, opts) {
3013
3783
  const warn = opts?.warn ?? ((msg) => console.warn(msg));
3014
- return walk(cfg, vault, (v, key) => {
3784
+ return walk2(cfg, vault, (v, key) => {
3015
3785
  try {
3016
3786
  return vault.decrypt(v);
3017
3787
  } catch (err) {
@@ -3023,20 +3793,20 @@ function decryptConfigSecrets(cfg, vault, opts) {
3023
3793
  });
3024
3794
  }
3025
3795
  function encryptConfigSecrets(cfg, vault, _opts) {
3026
- return walk(cfg, vault, (v) => vault.encrypt(v));
3796
+ return walk2(cfg, vault, (v) => vault.encrypt(v));
3027
3797
  }
3028
- function walk(node, vault, transform) {
3798
+ function walk2(node, vault, transform) {
3029
3799
  if (node === null || node === void 0) return node;
3030
3800
  if (typeof node !== "object") return node;
3031
3801
  if (Array.isArray(node)) {
3032
- return node.map((item) => walk(item, vault, transform));
3802
+ return node.map((item) => walk2(item, vault, transform));
3033
3803
  }
3034
3804
  const out = /* @__PURE__ */ Object.create(null);
3035
3805
  for (const [k, v] of Object.entries(node)) {
3036
3806
  if (typeof v === "string" && isSecretField(k)) {
3037
3807
  out[k] = transform(v, k);
3038
3808
  } else if (typeof v === "object" && v !== null) {
3039
- out[k] = walk(v, vault, transform);
3809
+ out[k] = walk2(v, vault, transform);
3040
3810
  } else {
3041
3811
  out[k] = v;
3042
3812
  }
@@ -3053,20 +3823,20 @@ function isSecretField(name) {
3053
3823
  async function rewriteConfigEncrypted(configPath, vault, patch) {
3054
3824
  let current = {};
3055
3825
  try {
3056
- const raw = await fsp.readFile(configPath, "utf8");
3826
+ const raw = await fsp2.readFile(configPath, "utf8");
3057
3827
  current = JSON.parse(raw);
3058
3828
  } catch {
3059
3829
  }
3060
3830
  const merged = deepMerge(current, patch ?? {});
3061
3831
  const encrypted = encryptConfigSecrets(merged, vault);
3062
- await fsp.mkdir(path11.dirname(configPath), { recursive: true });
3832
+ await fsp2.mkdir(path3.dirname(configPath), { recursive: true });
3063
3833
  await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3064
3834
  await restrictFilePermissions(configPath);
3065
3835
  }
3066
3836
  async function migratePlaintextSecrets(configPath, vault, logger) {
3067
3837
  let raw;
3068
3838
  try {
3069
- raw = await fsp.readFile(configPath, "utf8");
3839
+ raw = await fsp2.readFile(configPath, "utf8");
3070
3840
  } catch {
3071
3841
  return { migrated: 0, file: configPath };
3072
3842
  }
@@ -3108,7 +3878,7 @@ async function restrictFilePermissions(filePath, opts) {
3108
3878
  }
3109
3879
  } else {
3110
3880
  try {
3111
- await fsp.chmod(filePath, 384);
3881
+ await fsp2.chmod(filePath, 384);
3112
3882
  } catch {
3113
3883
  }
3114
3884
  }
@@ -3194,21 +3964,6 @@ function isContextWindowModeId(id) {
3194
3964
  return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
3195
3965
  }
3196
3966
 
3197
- // src/utils/safe-json.ts
3198
- function safeParse(input, maxBytes = 5e6) {
3199
- if (input.length > maxBytes) {
3200
- return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
3201
- }
3202
- try {
3203
- return { ok: true, value: JSON.parse(input) };
3204
- } catch (err) {
3205
- return {
3206
- ok: false,
3207
- error: err instanceof Error ? err.message : String(err)
3208
- };
3209
- }
3210
- }
3211
-
3212
3967
  // src/types/default-config.ts
3213
3968
  var DEFAULT_TOOLS_CONFIG = Object.freeze({
3214
3969
  defaultExecutionStrategy: "smart",
@@ -3384,7 +4139,7 @@ var DefaultConfigLoader = class {
3384
4139
  level: "warn",
3385
4140
  event: "config.source_load_failed",
3386
4141
  source: src.name,
3387
- message: err instanceof Error ? err.message : String(err),
4142
+ message: toErrorMessage(err),
3388
4143
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3389
4144
  }));
3390
4145
  }
@@ -3467,7 +4222,7 @@ var DefaultConfigLoader = class {
3467
4222
  const fp = this.paths.syncConfig;
3468
4223
  const t0 = Date.now();
3469
4224
  try {
3470
- const raw = await fsp.readFile(fp, "utf8");
4225
+ const raw = await fsp2.readFile(fp, "utf8");
3471
4226
  const parsed = safeParse(raw);
3472
4227
  if (!parsed.ok || !parsed.value) {
3473
4228
  this.events?.emit("storage.read", {
@@ -3521,7 +4276,7 @@ var DefaultConfigLoader = class {
3521
4276
  console.warn(JSON.stringify({
3522
4277
  level: "warn",
3523
4278
  event: "config.sync_load_failed",
3524
- message: err instanceof Error ? err.message : String(err),
4279
+ message: toErrorMessage(err),
3525
4280
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3526
4281
  }));
3527
4282
  return null;
@@ -3531,7 +4286,7 @@ var DefaultConfigLoader = class {
3531
4286
  let raw;
3532
4287
  const t0 = Date.now();
3533
4288
  try {
3534
- raw = await fsp.readFile(file, "utf8");
4289
+ raw = await fsp2.readFile(file, "utf8");
3535
4290
  } catch (err) {
3536
4291
  if (err.code !== "ENOENT") {
3537
4292
  this.events?.emit("storage.read", {
@@ -3548,7 +4303,7 @@ var DefaultConfigLoader = class {
3548
4303
  level: "warn",
3549
4304
  event: "config.read_failed",
3550
4305
  path: file,
3551
- message: err instanceof Error ? err.message : String(err),
4306
+ message: toErrorMessage(err),
3552
4307
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3553
4308
  }));
3554
4309
  }
@@ -3702,7 +4457,7 @@ var RecoveryLock = class {
3702
4457
  sessionStore;
3703
4458
  probe;
3704
4459
  constructor(opts) {
3705
- this.file = path11.join(opts.dir, LOCK_FILE);
4460
+ this.file = path3.join(opts.dir, LOCK_FILE);
3706
4461
  this.pid = opts.pid ?? process.pid;
3707
4462
  this.hostname = opts.hostname ?? os.hostname();
3708
4463
  this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
@@ -3763,7 +4518,7 @@ var RecoveryLock = class {
3763
4518
  * null return before calling this.
3764
4519
  */
3765
4520
  async write(sessionId) {
3766
- await ensureDir(path11.dirname(this.file));
4521
+ await ensureDir(path3.dirname(this.file));
3767
4522
  const lock = {
3768
4523
  v: 1,
3769
4524
  sessionId,
@@ -3772,7 +4527,7 @@ var RecoveryLock = class {
3772
4527
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
3773
4528
  };
3774
4529
  try {
3775
- await fsp.writeFile(this.file, JSON.stringify(lock), { flag: "wx", mode: 384 });
4530
+ await fsp2.writeFile(this.file, JSON.stringify(lock), { flag: "wx", mode: 384 });
3776
4531
  } catch (err) {
3777
4532
  const code = err.code;
3778
4533
  if (code === "EEXIST") {
@@ -3788,7 +4543,7 @@ var RecoveryLock = class {
3788
4543
  */
3789
4544
  async clear() {
3790
4545
  try {
3791
- await fsp.unlink(this.file);
4546
+ await fsp2.unlink(this.file);
3792
4547
  } catch (err) {
3793
4548
  const code = err.code;
3794
4549
  if (code === "ENOENT") return;
@@ -3798,7 +4553,7 @@ var RecoveryLock = class {
3798
4553
  async readLock() {
3799
4554
  let raw;
3800
4555
  try {
3801
- raw = await fsp.readFile(this.file, "utf8");
4556
+ raw = await fsp2.readFile(this.file, "utf8");
3802
4557
  } catch (err) {
3803
4558
  const code = err.code;
3804
4559
  if (code === "ENOENT") return null;
@@ -3830,42 +4585,6 @@ function defaultIsPidAlive(pid) {
3830
4585
  }
3831
4586
  }
3832
4587
 
3833
- // src/utils/regex-guard.ts
3834
- var MAX_PATTERN_LEN = 512;
3835
- var DANGEROUS_PATTERNS = [
3836
- /(\([^)]*[+*][^)]*\))[+*]/,
3837
- // (a+)+, (.*)+, etc
3838
- /(\(\?:[^)]*[+*][^)]*\))[+*]/
3839
- // same, with non-capturing group
3840
- ];
3841
- function compileUserRegex(pattern, flags) {
3842
- if (typeof pattern !== "string") {
3843
- return { ok: false, reason: "pattern must be a string" };
3844
- }
3845
- if (pattern.length === 0) {
3846
- return { ok: false, reason: "pattern is empty" };
3847
- }
3848
- if (pattern.length > MAX_PATTERN_LEN) {
3849
- return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
3850
- }
3851
- for (const rx of DANGEROUS_PATTERNS) {
3852
- if (rx.test(pattern)) {
3853
- return {
3854
- ok: false,
3855
- reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
3856
- };
3857
- }
3858
- }
3859
- try {
3860
- return { ok: true, regex: new RegExp(pattern, flags) };
3861
- } catch (err) {
3862
- return {
3863
- ok: false,
3864
- reason: err instanceof Error ? err.message : "invalid regex"
3865
- };
3866
- }
3867
- }
3868
-
3869
4588
  // src/storage/session-reader.ts
3870
4589
  var DefaultSessionReader = class {
3871
4590
  store;
@@ -4281,7 +5000,7 @@ function isAllowed(type, level) {
4281
5000
  return true;
4282
5001
  }
4283
5002
  function createSessionEventBridge(writer, level = "standard", options = {}) {
4284
- const normalizedLevel = level ?? "standard";
5003
+ let currentLevel = level ?? "standard";
4285
5004
  const resolveWriter = typeof writer === "function" ? writer : () => writer;
4286
5005
  const progressCounters = /* @__PURE__ */ new Map();
4287
5006
  const toolProgressConfig = options.sampling?.toolProgress ?? {};
@@ -4302,14 +5021,19 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
4302
5021
  return true;
4303
5022
  }
4304
5023
  return {
4305
- level: normalizedLevel,
5024
+ get level() {
5025
+ return currentLevel;
5026
+ },
5027
+ setAuditLevel(next) {
5028
+ currentLevel = next ?? "standard";
5029
+ },
4306
5030
  allows(type) {
4307
- return isAllowed(type, normalizedLevel);
5031
+ return isAllowed(type, currentLevel);
4308
5032
  },
4309
5033
  async append(event) {
4310
5034
  const target = resolveWriter();
4311
5035
  if (!target) return;
4312
- if (!isAllowed(event.type, normalizedLevel)) return;
5036
+ if (!isAllowed(event.type, currentLevel)) return;
4313
5037
  if (!shouldSample(event)) return;
4314
5038
  try {
4315
5039
  await target.append(event);
@@ -4320,7 +5044,7 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
4320
5044
  const target = resolveWriter();
4321
5045
  if (!target || events.length === 0) return;
4322
5046
  const allowed = events.filter(
4323
- (e) => isAllowed(e.type, normalizedLevel) && shouldSample(e)
5047
+ (e) => isAllowed(e.type, currentLevel) && shouldSample(e)
4324
5048
  );
4325
5049
  if (allowed.length === 0) return;
4326
5050
  try {
@@ -4357,7 +5081,7 @@ async function loadTodosCheckpoint(filePath, events, traceId) {
4357
5081
  const t0 = Date.now();
4358
5082
  let raw;
4359
5083
  try {
4360
- raw = await fsp.readFile(filePath, "utf8");
5084
+ raw = await fsp2.readFile(filePath, "utf8");
4361
5085
  } catch (err) {
4362
5086
  events?.emit("storage.error", {
4363
5087
  sessionId: traceId ?? "~boot~",
@@ -4365,7 +5089,7 @@ async function loadTodosCheckpoint(filePath, events, traceId) {
4365
5089
  filePath,
4366
5090
  operation: "load",
4367
5091
  outcome: "failure",
4368
- error: err instanceof Error ? err.message : String(err),
5092
+ error: toErrorMessage(err),
4369
5093
  recoverable: true
4370
5094
  });
4371
5095
  return null;
@@ -4437,13 +5161,13 @@ async function saveTodosCheckpoint(filePath, sessionId, todos, events, traceId)
4437
5161
  filePath,
4438
5162
  operation: "save",
4439
5163
  outcome: "failure",
4440
- error: err instanceof Error ? err.message : String(err),
5164
+ error: toErrorMessage(err),
4441
5165
  recoverable: false
4442
5166
  });
4443
5167
  console.warn(JSON.stringify({
4444
5168
  level: "warn",
4445
5169
  event: "todos_checkpoint.save_failed",
4446
- message: err instanceof Error ? err.message : String(err),
5170
+ message: toErrorMessage(err),
4447
5171
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4448
5172
  }));
4449
5173
  }
@@ -4454,7 +5178,7 @@ function attachTodosCheckpoint(state, filePath, sessionId, events, traceId) {
4454
5178
  let writeChain = Promise.resolve();
4455
5179
  const enqueueWrite = (todos) => {
4456
5180
  writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos, events, traceId)).catch((err) => {
4457
- const msg = err instanceof Error ? err.message : String(err);
5181
+ const msg = toErrorMessage(err);
4458
5182
  console.error(JSON.stringify({
4459
5183
  level: "error",
4460
5184
  event: "todos_checkpoint.write_chain_failed",
@@ -4499,14 +5223,14 @@ async function loadPlan(filePath, events) {
4499
5223
  const t0 = Date.now();
4500
5224
  let raw;
4501
5225
  try {
4502
- raw = await fsp.readFile(filePath, "utf8");
5226
+ raw = await fsp2.readFile(filePath, "utf8");
4503
5227
  } catch (err) {
4504
5228
  events?.emit("storage.error", {
4505
5229
  sessionId: "~boot~",
4506
5230
  store: "plan",
4507
5231
  filePath,
4508
5232
  operation: "load",
4509
- error: err instanceof Error ? err.message : String(err),
5233
+ error: toErrorMessage(err),
4510
5234
  recoverable: true
4511
5235
  });
4512
5236
  return null;
@@ -4565,12 +5289,12 @@ async function savePlan(filePath, plan, events) {
4565
5289
  store: "plan",
4566
5290
  filePath,
4567
5291
  operation: "save",
4568
- error: err instanceof Error ? err.message : String(err),
5292
+ error: toErrorMessage(err),
4569
5293
  recoverable: false
4570
5294
  });
4571
5295
  console.warn(
4572
5296
  "[plan-store] save failed:",
4573
- err instanceof Error ? err.message : String(err)
5297
+ toErrorMessage(err)
4574
5298
  );
4575
5299
  }
4576
5300
  }
@@ -4791,7 +5515,7 @@ init_atomic_write();
4791
5515
  async function loadDirectorState(filePath) {
4792
5516
  let raw;
4793
5517
  try {
4794
- raw = await fsp.readFile(filePath, "utf8");
5518
+ raw = await fsp2.readFile(filePath, "utf8");
4795
5519
  } catch {
4796
5520
  return null;
4797
5521
  }
@@ -4806,7 +5530,7 @@ async function loadDirectorState(filePath) {
4806
5530
  async function acquireDirectorStateLock(lockPath, processId = process.pid) {
4807
5531
  let existing;
4808
5532
  try {
4809
- existing = await fsp.readFile(lockPath, "utf8");
5533
+ existing = await fsp2.readFile(lockPath, "utf8");
4810
5534
  } catch {
4811
5535
  }
4812
5536
  if (existing) {
@@ -4830,7 +5554,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
4830
5554
  }
4831
5555
  async function releaseDirectorStateLock(lockPath) {
4832
5556
  try {
4833
- await fsp.unlink(lockPath);
5557
+ await fsp2.unlink(lockPath);
4834
5558
  } catch {
4835
5559
  }
4836
5560
  }
@@ -4955,7 +5679,7 @@ var DirectorStateCheckpoint = class {
4955
5679
  } catch (err) {
4956
5680
  console.warn(
4957
5681
  "[director-state] checkpoint write failed:",
4958
- err instanceof Error ? err.message : String(err)
5682
+ toErrorMessage(err)
4959
5683
  );
4960
5684
  } finally {
4961
5685
  this.writing = false;
@@ -5165,82 +5889,6 @@ function getDangerousCapabilities(toolOrCaps) {
5165
5889
 
5166
5890
  // src/security/permission-policy.ts
5167
5891
  init_atomic_write();
5168
-
5169
- // src/utils/glob-match.ts
5170
- function escapeRegex(s) {
5171
- return s.replace(/[.+^${}()|\\]/g, "\\$&");
5172
- }
5173
- var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
5174
- var CACHE_MAX_SIZE = 2e3;
5175
- function getCachedGlob(pattern) {
5176
- const cached = COMPILED_GLOB_CACHE.get(pattern);
5177
- if (cached) return cached;
5178
- if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
5179
- const keys = [...COMPILED_GLOB_CACHE.keys()];
5180
- for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
5181
- COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
5182
- }
5183
- }
5184
- const re = compileGlob(pattern);
5185
- COMPILED_GLOB_CACHE.set(pattern, re);
5186
- return re;
5187
- }
5188
- var MAX_GLOB_PATTERN_LEN = 1024;
5189
- function compileGlob(pattern) {
5190
- if (pattern.length > MAX_GLOB_PATTERN_LEN) {
5191
- throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
5192
- }
5193
- let i = 0;
5194
- let re = "^";
5195
- while (i < pattern.length) {
5196
- const c = pattern[i];
5197
- if (c === "*") {
5198
- if (pattern[i + 1] === "*") {
5199
- re += ".*";
5200
- i += 2;
5201
- if (pattern[i] === "/") i++;
5202
- } else {
5203
- re += "[^/]*";
5204
- i++;
5205
- }
5206
- } else if (c === "?") {
5207
- re += "[^/]";
5208
- i++;
5209
- } else if (c === "[") {
5210
- let cls = "[";
5211
- i++;
5212
- if (pattern[i] === "!" || pattern[i] === "^") {
5213
- cls += "^";
5214
- i++;
5215
- }
5216
- while (i < pattern.length && pattern[i] !== "]") {
5217
- const ch = pattern[i] ?? "";
5218
- if (ch === "\\") {
5219
- cls += "\\\\";
5220
- } else if (ch === "]" || ch === "^") {
5221
- cls += `\\${ch}`;
5222
- } else {
5223
- cls += ch;
5224
- }
5225
- i++;
5226
- }
5227
- cls += "]";
5228
- re += cls;
5229
- i++;
5230
- } else {
5231
- re += escapeRegex(c ?? "");
5232
- i++;
5233
- }
5234
- }
5235
- re += "$";
5236
- return new RegExp(re);
5237
- }
5238
- function matchGlob(pattern, input) {
5239
- return getCachedGlob(pattern).test(input);
5240
- }
5241
- function matchAny(patterns, input) {
5242
- return patterns.some((p) => matchGlob(p, input));
5243
- }
5244
5892
  var DESTRUCTIVE_BASH_PATTERNS = [
5245
5893
  /\bgit\s+(?:clean\s+-[^\s]*[xdf]|reset\s+--hard)\b/i,
5246
5894
  /\b(?:drop|truncate)\s+(?:table|database|schema)\b/i,
@@ -5263,9 +5911,9 @@ function getInputString(input, key) {
5263
5911
  function pathLooksInsideProject(rawPath, projectRoot) {
5264
5912
  if (!projectRoot) return false;
5265
5913
  if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
5266
- const resolved = path11.resolve(projectRoot, rawPath);
5267
- const relative2 = path11.relative(projectRoot, resolved);
5268
- return !!relative2 && !relative2.startsWith("..") && !path11.isAbsolute(relative2);
5914
+ const resolved = path3.resolve(projectRoot, rawPath);
5915
+ const relative2 = path3.relative(projectRoot, resolved);
5916
+ return !!relative2 && !relative2.startsWith("..") && !path3.isAbsolute(relative2);
5269
5917
  }
5270
5918
  function tokenizeShell(command) {
5271
5919
  return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
@@ -5275,7 +5923,7 @@ function pathTokenIsOutsideProject(token, projectRoot) {
5275
5923
  if (token === "/" || token === "~" || token === "." || token === "..") return token !== ".";
5276
5924
  if (token.includes("*")) return true;
5277
5925
  if (token.startsWith("..") || token.includes("../") || token.includes("..\\")) return true;
5278
- if (path11.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
5926
+ if (path3.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
5279
5927
  return false;
5280
5928
  }
5281
5929
  function hasDangerousDeleteTarget(tokens, start, projectRoot) {
@@ -5421,7 +6069,7 @@ var DefaultPermissionPolicy = class {
5421
6069
  }
5422
6070
  async reload() {
5423
6071
  try {
5424
- const raw = await fsp.readFile(this.trustFile, "utf8");
6072
+ const raw = await fsp2.readFile(this.trustFile, "utf8");
5425
6073
  const parsed = safeParse(raw);
5426
6074
  if (parsed.ok && parsed.value) this.policy = parsed.value;
5427
6075
  } catch {
@@ -5693,11 +6341,6 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
5693
6341
  }
5694
6342
  };
5695
6343
 
5696
- // src/utils/string.ts
5697
- function truncate(s, max) {
5698
- return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
5699
- }
5700
-
5701
6344
  // src/types/provider.ts
5702
6345
  var ProviderError = class extends WrongStackError {
5703
6346
  status;
@@ -5934,12 +6577,12 @@ var DefaultSkillLoader = class {
5934
6577
  const seen = /* @__PURE__ */ new Set();
5935
6578
  for (const { dir, source } of this.dirs) {
5936
6579
  try {
5937
- const entries = await fsp.readdir(dir, { withFileTypes: true });
6580
+ const entries = await fsp2.readdir(dir, { withFileTypes: true });
5938
6581
  for (const e of entries) {
5939
6582
  if (!e.isDirectory()) continue;
5940
- const skillFile = path11.join(dir, e.name, "SKILL.md");
6583
+ const skillFile = path3.join(dir, e.name, "SKILL.md");
5941
6584
  try {
5942
- const raw = await fsp.readFile(skillFile, "utf8");
6585
+ const raw = await fsp2.readFile(skillFile, "utf8");
5943
6586
  const meta = parseFrontmatter(raw);
5944
6587
  if (!meta.name || !meta.description) continue;
5945
6588
  if (seen.has(meta.name)) continue;
@@ -5982,7 +6625,7 @@ var DefaultSkillLoader = class {
5982
6625
  const entries = [];
5983
6626
  for (const s of skills) {
5984
6627
  try {
5985
- const raw = await fsp.readFile(s.path, "utf8");
6628
+ const raw = await fsp2.readFile(s.path, "utf8");
5986
6629
  const { trigger, scope } = parseDescription(raw);
5987
6630
  entries.push({ name: s.name, trigger, scope, source: s.source, path: s.path });
5988
6631
  } catch {
@@ -6001,7 +6644,7 @@ var DefaultSkillLoader = class {
6001
6644
  if (cached !== void 0) return cached;
6002
6645
  const m = await this.find(name);
6003
6646
  if (!m) throw new Error(`Skill "${name}" not found`);
6004
- const body = await fsp.readFile(m.path, "utf8");
6647
+ const body = await fsp2.readFile(m.path, "utf8");
6005
6648
  this.bodyCache.set(name, body);
6006
6649
  return body;
6007
6650
  }
@@ -6011,12 +6654,12 @@ var DefaultSkillLoader = class {
6011
6654
  if (cached !== void 0) return cached;
6012
6655
  const m = await this.find(name);
6013
6656
  if (!m) throw new Error(`Skill "${name}" not found`);
6014
- const savePath = path11.join(path11.dirname(m.path), "SKILL.save.md");
6657
+ const savePath = path3.join(path3.dirname(m.path), "SKILL.save.md");
6015
6658
  let result;
6016
6659
  try {
6017
- result = await fsp.readFile(savePath, "utf8");
6660
+ result = await fsp2.readFile(savePath, "utf8");
6018
6661
  } catch {
6019
- const full = await fsp.readFile(m.path, "utf8");
6662
+ const full = await fsp2.readFile(m.path, "utf8");
6020
6663
  const body = stripFrontmatter(full);
6021
6664
  const compact = compactSkillBody(body);
6022
6665
  if (compact) {
@@ -6041,139 +6684,45 @@ function parseFrontmatter(raw) {
6041
6684
  let value = [];
6042
6685
  const flush = () => {
6043
6686
  if (key) {
6044
- out[key] = value.join("\n").trim();
6045
- }
6046
- key = null;
6047
- value = [];
6048
- };
6049
- for (const line of block.split("\n")) {
6050
- const m = /^([a-zA-Z_]+):\s*(\|?)\s*(.*)$/.exec(line);
6051
- if (m) {
6052
- flush();
6053
- key = m[1] ?? "";
6054
- const pipe = m[2];
6055
- const rest = m[3] ?? "";
6056
- if (pipe === "|") {
6057
- value = [];
6058
- } else if (rest) {
6059
- value = [rest];
6060
- } else {
6061
- value = [];
6062
- }
6063
- } else if (key) {
6064
- value.push(line.replace(/^\s+/, ""));
6065
- }
6066
- }
6067
- flush();
6068
- return out;
6069
- }
6070
- function parseDescription(raw) {
6071
- const fm = parseFrontmatter(raw);
6072
- const desc = fm.description ?? "";
6073
- const firstSentenceEnd = desc.indexOf(". ");
6074
- const trigger = firstSentenceEnd !== -1 ? desc.slice(0, firstSentenceEnd + 1).trim() : desc.trim().split("\n")[0] ?? "";
6075
- const scope = [];
6076
- const coversMatch = /(?:covers|for|including)\s+([^.]+)/i.exec(desc);
6077
- if (coversMatch) {
6078
- const items = coversMatch[1] ?? "".replace(/[·•]/g, ",").split(",").map((s) => s.trim()).filter(Boolean);
6079
- scope.push(...items);
6080
- }
6081
- return { trigger, scope };
6082
- }
6083
-
6084
- // src/utils/json-repair.ts
6085
- function completePartialObject(s) {
6086
- if (!s.trim().startsWith("{")) return s;
6087
- if (tryParse(s).ok) return s;
6088
- return repairTruncated(s);
6089
- }
6090
- function repairTruncated(s) {
6091
- const stack = [];
6092
- let inString = false;
6093
- let escaped = false;
6094
- let sawKey = false;
6095
- let prevSig = "";
6096
- let contentEnd = 0;
6097
- let stringBraceDepth = 0;
6098
- for (let i = 0; i < s.length; i++) {
6099
- const ch = expectDefined(s[i]);
6100
- if (inString) {
6101
- contentEnd = i + 1;
6102
- if (escaped) {
6103
- escaped = false;
6104
- continue;
6105
- }
6106
- if (ch === "\\") {
6107
- escaped = true;
6108
- continue;
6109
- }
6110
- if (ch === '"') {
6111
- inString = false;
6112
- prevSig = '"';
6113
- stringBraceDepth = 0;
6114
- continue;
6115
- }
6116
- if (ch === "{") stringBraceDepth++;
6117
- else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
6118
- continue;
6119
- }
6120
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
6121
- contentEnd = i + 1;
6122
- if (ch === '"') {
6123
- inString = true;
6124
- sawKey = true;
6125
- stringBraceDepth = 0;
6126
- prevSig = '"';
6127
- } else if (ch === "{" || ch === "[") {
6128
- stack.push(ch);
6129
- prevSig = ch;
6130
- } else if (ch === "}" || ch === "]") {
6131
- stack.pop();
6132
- prevSig = ch;
6133
- } else {
6134
- prevSig = ch;
6687
+ out[key] = value.join("\n").trim();
6135
6688
  }
6136
- }
6137
- if (!sawKey && !inString) return s;
6138
- let result = s.slice(0, contentEnd);
6139
- if (inString) {
6140
- if (escaped) {
6141
- result = result.slice(0, -1);
6142
- } else if (endsWithInvalidEscape(result)) {
6143
- result = result.slice(0, -2);
6689
+ key = null;
6690
+ value = [];
6691
+ };
6692
+ for (const line of block.split("\n")) {
6693
+ const m = /^([a-zA-Z_]+):\s*(\|?)\s*(.*)$/.exec(line);
6694
+ if (m) {
6695
+ flush();
6696
+ key = m[1] ?? "";
6697
+ const pipe = m[2];
6698
+ const rest = m[3] ?? "";
6699
+ if (pipe === "|") {
6700
+ value = [];
6701
+ } else if (rest) {
6702
+ value = [rest];
6703
+ } else {
6704
+ value = [];
6705
+ }
6706
+ } else if (key) {
6707
+ value.push(line.replace(/^\s+/, ""));
6144
6708
  }
6145
- if (stringBraceDepth > 0) result += "}".repeat(stringBraceDepth);
6146
- result += '"';
6147
- } else if (prevSig === ":") {
6148
- result += "null";
6149
6709
  }
6150
- for (let k = stack.length - 1; k >= 0; k--) {
6151
- result += stack[k] === "{" ? "}" : "]";
6152
- }
6153
- if (!tryParse(result).ok) {
6154
- const patched = result.replace(/:(\s*)([}\]])/g, ":null$2");
6155
- if (tryParse(patched).ok) result = patched;
6156
- }
6157
- return result;
6158
- }
6159
- var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
6160
- function endsWithInvalidEscape(str) {
6161
- const last = str[str.length - 1];
6162
- if (str[str.length - 2] !== "\\" || last === void 0) return false;
6163
- if (VALID_ESCAPE.has(last)) return false;
6164
- let backslashes = 0;
6165
- for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
6166
- return backslashes % 2 === 1;
6710
+ flush();
6711
+ return out;
6167
6712
  }
6168
- function tryParse(s) {
6169
- try {
6170
- return { ok: true, value: JSON.parse(s) };
6171
- } catch {
6172
- return { ok: false };
6713
+ function parseDescription(raw) {
6714
+ const fm = parseFrontmatter(raw);
6715
+ const desc = fm.description ?? "";
6716
+ const firstSentenceEnd = desc.indexOf(". ");
6717
+ const trigger = firstSentenceEnd !== -1 ? desc.slice(0, firstSentenceEnd + 1).trim() : desc.trim().split("\n")[0] ?? "";
6718
+ const scope = [];
6719
+ const coversMatch = /(?:covers|for|including)\s+([^.]+)/i.exec(desc);
6720
+ if (coversMatch) {
6721
+ const items = (coversMatch[1] ?? "").replace(/[·•]/g, ",").split(",").map((s) => s.trim()).filter(Boolean);
6722
+ scope.push(...items);
6173
6723
  }
6724
+ return { trigger, scope };
6174
6725
  }
6175
-
6176
- // src/core/streaming-response-builder.ts
6177
6726
  var STREAM_DRAIN_TIMEOUT_MS = 500;
6178
6727
  function buildResponse(state) {
6179
6728
  const content = [];
@@ -6469,244 +7018,97 @@ async function runProviderWithRetry(opts) {
6469
7018
  const res = provider.capabilities.streaming ? await streamProviderToResponse(provider, request, signal, ctx, events, logger) : await provider.complete(request, { signal });
6470
7019
  span?.setAttribute("provider.stopReason", res.stopReason);
6471
7020
  span?.setAttribute("provider.usage_in", res.usage.input);
6472
- span?.setAttribute("provider.usage_out", res.usage.output);
6473
- span?.end();
6474
- logger.debug("Provider call succeeded", {
6475
- ...providerLogCtx(provider, request),
6476
- stopReason: res.stopReason,
6477
- usageInput: res.usage.input,
6478
- usageOutput: res.usage.output,
6479
- cacheRead: res.usage.cacheRead,
6480
- cacheWrite: res.usage.cacheWrite,
6481
- attempts: attempt + 1
6482
- });
6483
- return res;
6484
- } catch (err) {
6485
- if (err instanceof Error) span?.recordError(err);
6486
- span?.end();
6487
- if (signal.aborted) throw err;
6488
- const isProviderErr = err instanceof ProviderError;
6489
- const errAsErr = err instanceof Error ? err : new Error(String(err));
6490
- const canRetry = retry.shouldRetry(isProviderErr ? err : errAsErr, attempt);
6491
- const description = isProviderErr ? err.describe() : errAsErr.message;
6492
- if (!canRetry) {
6493
- if (isProviderErr) {
6494
- events.emit("provider.error", {
6495
- providerId: err.providerId,
6496
- status: err.status,
6497
- description,
6498
- retryable: false
6499
- });
6500
- }
6501
- logger.error(`Provider call failed after ${attempt + 1} attempt(s) \u2014 ${description}`, {
6502
- ...providerLogCtx(provider, request),
6503
- attempts: attempt + 1,
6504
- errorDescription: description,
6505
- status: isProviderErr ? err.status : void 0,
6506
- errorName: err instanceof Error ? err.name : void 0,
6507
- errorStack: err instanceof Error ? err.stack?.split("\n").slice(0, 3).join("\n") : void 0
6508
- });
6509
- throw toWrongStackError(err);
6510
- }
6511
- const delay = Math.round(retry.delayMs(attempt));
6512
- const attemptNum = attempt + 1;
6513
- const maxAttempts = retry.maxAttempts(isProviderErr ? err : errAsErr);
6514
- logger.warn(`Provider retry ${attemptNum}/${maxAttempts} in ${delay}ms \u2014 ${description}`, {
6515
- ...providerLogCtx(provider, request),
6516
- attempt: attemptNum,
6517
- maxAttempts,
6518
- delayMs: delay,
6519
- errorDescription: description,
6520
- status: isProviderErr ? err.status : void 0
6521
- });
6522
- if (isProviderErr) {
6523
- events.emit("provider.retry", {
6524
- providerId: err.providerId,
6525
- attempt: attemptNum,
6526
- delayMs: delay,
6527
- status: err.status,
6528
- description
6529
- });
6530
- }
6531
- await new Promise((resolve5, reject) => {
6532
- let settled = false;
6533
- const onAbort = () => {
6534
- if (settled) return;
6535
- settled = true;
6536
- clearTimeout(t);
6537
- reject(new Error("aborted"));
6538
- };
6539
- const t = setTimeout(() => {
6540
- if (settled) return;
6541
- settled = true;
6542
- clearTimeout(t);
6543
- signal.removeEventListener("abort", onAbort);
6544
- resolve5();
6545
- }, delay);
6546
- if (signal.aborted) {
6547
- onAbort();
6548
- return;
6549
- }
6550
- signal.addEventListener("abort", onAbort, { once: true });
6551
- });
6552
- attempt++;
6553
- }
6554
- }
6555
- }
6556
-
6557
- // src/execution/provider-runner-impl.ts
6558
- var DefaultProviderRunner = class {
6559
- async run(opts) {
6560
- return runProviderWithRetry(opts);
6561
- }
6562
- };
6563
-
6564
- // src/utils/token-estimate.ts
6565
- var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
6566
- var CALIBRATION_GLOBAL_KEY = "__global__";
6567
- var _cals = /* @__PURE__ */ new Map();
6568
- function calState(key) {
6569
- let state = _cals.get(key);
6570
- if (!state) {
6571
- state = { ratio: 1, count: 0, prevEst: 0 };
6572
- _cals.set(key, state);
6573
- }
6574
- return state;
6575
- }
6576
- var MIN_SAMPLES_FOR_CALIBRATION = 3;
6577
- var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
6578
- var ESTIMATE_CACHE_MAX_SIZE = 1e4;
6579
- function getCachedEstimate(key, compute) {
6580
- const existing = ESTIMATE_CACHE.get(key);
6581
- if (existing !== void 0) return existing;
6582
- if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
6583
- let evicted = 0;
6584
- const maxEvict = Math.floor(ESTIMATE_CACHE_MAX_SIZE / 4);
6585
- for (const k of ESTIMATE_CACHE.keys()) {
6586
- if (evicted >= maxEvict) break;
6587
- ESTIMATE_CACHE.delete(k);
6588
- evicted++;
6589
- }
6590
- }
6591
- const estimate = compute(key);
6592
- ESTIMATE_CACHE.set(key, estimate);
6593
- return estimate;
6594
- }
6595
- function estimateToolInputTokens(input) {
6596
- if (typeof input === "string") return RoughTokenEstimate(input);
6597
- if (input === null || typeof input !== "object") {
6598
- return RoughTokenEstimate(String(input));
6599
- }
6600
- return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
6601
- }
6602
- function estimateToolResultTokens(content) {
6603
- if (typeof content === "string") return RoughTokenEstimate(content);
6604
- return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
6605
- }
6606
- function estimateTextTokens(text) {
6607
- return RoughTokenEstimate(text);
6608
- }
6609
- function computeMessageTokens(msg) {
6610
- if (typeof msg.content === "string") return estimateTextTokens(msg.content);
6611
- let total = 0;
6612
- for (const b of msg.content) {
6613
- if (b.type === "text") total += estimateTextTokens(b.text);
6614
- else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
6615
- else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
6616
- else total += RoughTokenEstimate(JSON.stringify(b));
6617
- }
6618
- return total;
6619
- }
6620
- function estimateMessageTokens(messages) {
6621
- let total = 0;
6622
- for (const m of messages) {
6623
- if (typeof m._estTokens === "number" && m._estTokens > 0) {
6624
- total += m._estTokens;
6625
- continue;
6626
- }
6627
- total += computeMessageTokens(m);
6628
- }
6629
- return total;
6630
- }
6631
- function estimateToolDefTokens(tool) {
6632
- const cached = tool._estDefTokens;
6633
- if (typeof cached === "number" && cached > 0) return cached;
6634
- return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
6635
- }
6636
- function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
6637
- let messagesTokens = 0;
6638
- if (typeof messages === "string") {
6639
- messagesTokens = RoughTokenEstimate(messages);
6640
- } else if (Array.isArray(messages)) {
6641
- for (const m of messages) {
6642
- if (typeof m === "object" && m !== null && "content" in m) {
6643
- const cached = m._estTokens;
6644
- if (typeof cached === "number" && cached > 0) {
6645
- messagesTokens += cached;
6646
- continue;
6647
- }
6648
- const content = m.content;
6649
- if (typeof content === "string") {
6650
- messagesTokens += RoughTokenEstimate(content);
6651
- } else if (Array.isArray(content)) {
6652
- for (const b of content) {
6653
- if (typeof b === "object" && b !== null) {
6654
- if (b.type === "text") {
6655
- messagesTokens += RoughTokenEstimate(b.text);
6656
- } else {
6657
- messagesTokens += RoughTokenEstimate(JSON.stringify(b));
6658
- }
6659
- }
6660
- }
7021
+ span?.setAttribute("provider.usage_out", res.usage.output);
7022
+ span?.end();
7023
+ logger.debug("Provider call succeeded", {
7024
+ ...providerLogCtx(provider, request),
7025
+ stopReason: res.stopReason,
7026
+ usageInput: res.usage.input,
7027
+ usageOutput: res.usage.output,
7028
+ cacheRead: res.usage.cacheRead,
7029
+ cacheWrite: res.usage.cacheWrite,
7030
+ attempts: attempt + 1
7031
+ });
7032
+ return res;
7033
+ } catch (err) {
7034
+ if (err instanceof Error) span?.recordError(err);
7035
+ span?.end();
7036
+ if (signal.aborted) throw err;
7037
+ const isProviderErr = err instanceof ProviderError;
7038
+ const errAsErr = err instanceof Error ? err : new Error(String(err));
7039
+ const canRetry = retry.shouldRetry(isProviderErr ? err : errAsErr, attempt);
7040
+ const description = isProviderErr ? err.describe() : errAsErr.message;
7041
+ if (!canRetry) {
7042
+ if (isProviderErr) {
7043
+ events.emit("provider.error", {
7044
+ providerId: err.providerId,
7045
+ status: err.status,
7046
+ description,
7047
+ retryable: false
7048
+ });
6661
7049
  }
7050
+ logger.error(`Provider call failed after ${attempt + 1} attempt(s) \u2014 ${description}`, {
7051
+ ...providerLogCtx(provider, request),
7052
+ attempts: attempt + 1,
7053
+ errorDescription: description,
7054
+ status: isProviderErr ? err.status : void 0,
7055
+ errorName: err instanceof Error ? err.name : void 0,
7056
+ errorStack: err instanceof Error ? err.stack?.split("\n").slice(0, 3).join("\n") : void 0
7057
+ });
7058
+ throw toWrongStackError(err);
6662
7059
  }
6663
- }
6664
- }
6665
- let systemTokens = 0;
6666
- if (typeof systemPrompt === "string") {
6667
- systemTokens = RoughTokenEstimate(systemPrompt);
6668
- } else if (Array.isArray(systemPrompt)) {
6669
- for (const b of systemPrompt) {
6670
- if (typeof b === "object" && b !== null && b.type === "text") {
6671
- systemTokens += RoughTokenEstimate(b.text);
7060
+ const delay = Math.round(retry.delayMs(attempt));
7061
+ const attemptNum = attempt + 1;
7062
+ const maxAttempts = retry.maxAttempts(isProviderErr ? err : errAsErr);
7063
+ logger.warn(`Provider retry ${attemptNum}/${maxAttempts} in ${delay}ms \u2014 ${description}`, {
7064
+ ...providerLogCtx(provider, request),
7065
+ attempt: attemptNum,
7066
+ maxAttempts,
7067
+ delayMs: delay,
7068
+ errorDescription: description,
7069
+ status: isProviderErr ? err.status : void 0
7070
+ });
7071
+ if (isProviderErr) {
7072
+ events.emit("provider.retry", {
7073
+ providerId: err.providerId,
7074
+ attempt: attemptNum,
7075
+ delayMs: delay,
7076
+ status: err.status,
7077
+ description
7078
+ });
6672
7079
  }
7080
+ await new Promise((resolve5, reject) => {
7081
+ let settled = false;
7082
+ const onAbort = () => {
7083
+ if (settled) return;
7084
+ settled = true;
7085
+ clearTimeout(t);
7086
+ reject(new Error("aborted"));
7087
+ };
7088
+ const t = setTimeout(() => {
7089
+ if (settled) return;
7090
+ settled = true;
7091
+ clearTimeout(t);
7092
+ signal.removeEventListener("abort", onAbort);
7093
+ resolve5();
7094
+ }, delay);
7095
+ if (signal.aborted) {
7096
+ onAbort();
7097
+ return;
7098
+ }
7099
+ signal.addEventListener("abort", onAbort, { once: true });
7100
+ });
7101
+ attempt++;
6673
7102
  }
6674
7103
  }
6675
- let toolsTokens = 0;
6676
- for (const t of tools) {
6677
- toolsTokens += estimateToolDefTokens(t);
6678
- }
6679
- const total = messagesTokens + systemTokens + toolsTokens;
6680
- calState(calibrationKey).prevEst = total;
6681
- return {
6682
- messages: messagesTokens,
6683
- systemPrompt: systemTokens,
6684
- tools: toolsTokens,
6685
- total
6686
- };
6687
- }
6688
- function getCalibrationState(calibrationKey = CALIBRATION_GLOBAL_KEY) {
6689
- const cal = calState(calibrationKey);
6690
- return {
6691
- ratio: cal.ratio,
6692
- count: cal.count,
6693
- calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION
6694
- };
6695
7104
  }
6696
- function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
6697
- const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);
6698
- const cal = calState(calibrationKey);
6699
- if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
6700
- const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));
6701
- return {
6702
- messages: Math.round(result.messages * safeRatio),
6703
- systemPrompt: Math.round(result.systemPrompt * safeRatio),
6704
- tools: Math.round(result.tools * safeRatio),
6705
- total: Math.round(result.total * safeRatio)
6706
- };
7105
+
7106
+ // src/execution/provider-runner-impl.ts
7107
+ var DefaultProviderRunner = class {
7108
+ async run(opts) {
7109
+ return runProviderWithRetry(opts);
6707
7110
  }
6708
- return result;
6709
- }
7111
+ };
6710
7112
 
6711
7113
  // src/types/blocks.ts
6712
7114
  function isTextBlock(b) {
@@ -7721,6 +8123,12 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
7721
8123
  hardThreshold;
7722
8124
  /** Writable so model-switch can update the denominator without re-registering the middleware. */
7723
8125
  _maxContext;
8126
+ /**
8127
+ * Runtime on/off gate. The middleware is always installed in the pipeline so
8128
+ * auto-compaction can be toggled live from the TUI `/settings` picker; when
8129
+ * disabled the handler is a pass-through. Defaults to enabled.
8130
+ */
8131
+ _enabled = true;
7724
8132
  aggressiveOn;
7725
8133
  events;
7726
8134
  failureMode;
@@ -7778,8 +8186,19 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
7778
8186
  setMaxContext(maxContext) {
7779
8187
  this._maxContext = maxContext;
7780
8188
  }
8189
+ /** Whether auto-compaction is currently active. */
8190
+ get enabled() {
8191
+ return this._enabled;
8192
+ }
8193
+ /** Toggle auto-compaction on a live session (TUI `/settings`). When disabled
8194
+ * the middleware passes every iteration straight through without estimating
8195
+ * tokens or compacting. */
8196
+ setEnabled(enabled) {
8197
+ this._enabled = enabled;
8198
+ }
7781
8199
  handler() {
7782
8200
  return async (ctx, next) => {
8201
+ if (!this._enabled) return next(ctx);
7783
8202
  const msgCount = ctx.messages.length;
7784
8203
  const toolCount = (ctx.tools ?? []).length;
7785
8204
  let tokens;
@@ -7901,163 +8320,25 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
7901
8320
  level: pressure.level,
7902
8321
  tokens: pressure.tokens,
7903
8322
  maxContext: this._maxContext,
7904
- load: pressure.load,
7905
- fatal
7906
- });
7907
- if (fatal) {
7908
- throw new AgentError({
7909
- message: `Auto-compaction failed at ${pressure.level} threshold`,
7910
- code: ERROR_CODES.AGENT_CONTEXT_OVERFLOW,
7911
- recoverable: true,
7912
- context: {
7913
- level: pressure.level,
7914
- tokens: pressure.tokens,
7915
- maxContext: this._maxContext
7916
- },
7917
- cause: err
7918
- });
7919
- }
7920
- }
7921
- }
7922
- };
7923
-
7924
- // src/utils/json-schema-validate.ts
7925
- function validateAgainstSchema(value, schema) {
7926
- const errors = [];
7927
- walk2(value, schema, "", errors);
7928
- return { ok: errors.length === 0, errors };
7929
- }
7930
- function walk2(value, schema, path19, errors) {
7931
- if (schema.enum !== void 0) {
7932
- if (!schema.enum.some((e) => deepEqual(e, value))) {
7933
- errors.push({
7934
- path: path19 || "<root>",
7935
- message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
7936
- });
7937
- return;
7938
- }
7939
- }
7940
- if (typeof schema.type === "string") {
7941
- if (!checkType(value, schema.type)) {
7942
- errors.push({
7943
- path: path19 || "<root>",
7944
- message: `expected ${schema.type}, got ${describeType(value)}`
7945
- });
7946
- return;
7947
- }
7948
- }
7949
- if (schema.type === "object" && isPlainObject(value)) {
7950
- const obj = value;
7951
- for (const req of schema.required ?? []) {
7952
- if (!(req in obj)) {
7953
- errors.push({ path: joinPath(path19, req), message: "required property missing" });
7954
- }
7955
- }
7956
- if (schema.properties) {
7957
- for (const [key, subSchema] of Object.entries(schema.properties)) {
7958
- if (key in obj) {
7959
- walk2(obj[key], subSchema, joinPath(path19, key), errors);
7960
- }
7961
- }
7962
- }
7963
- }
7964
- if (schema.type === "array" && Array.isArray(value) && schema.items) {
7965
- value.forEach((item, i) => walk2(item, schema.items, `${path19}[${i}]`, errors));
7966
- }
7967
- }
7968
- function checkType(value, type) {
7969
- switch (type) {
7970
- case "string":
7971
- return typeof value === "string";
7972
- case "number":
7973
- return typeof value === "number" && !Number.isNaN(value);
7974
- case "integer":
7975
- return typeof value === "number" && Number.isInteger(value);
7976
- case "boolean":
7977
- return typeof value === "boolean";
7978
- case "null":
7979
- return value === null;
7980
- case "array":
7981
- return Array.isArray(value);
7982
- case "object":
7983
- return isPlainObject(value);
7984
- default:
7985
- return true;
7986
- }
7987
- }
7988
- function isPlainObject(v) {
7989
- return typeof v === "object" && v !== null && !Array.isArray(v);
7990
- }
7991
- function describeType(v) {
7992
- if (v === null) return "null";
7993
- if (Array.isArray(v)) return "array";
7994
- return typeof v;
7995
- }
7996
- function joinPath(parent, key) {
7997
- if (!parent) return key;
7998
- return `${parent}.${key}`;
7999
- }
8000
- function deepEqual(a, b) {
8001
- if (a === b) return true;
8002
- if (typeof a !== typeof b) return false;
8003
- if (a === null || b === null) return a === b;
8004
- if (Array.isArray(a) && Array.isArray(b)) {
8005
- return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
8006
- }
8007
- if (typeof a === "object" && typeof b === "object") {
8008
- const ak = Object.keys(a);
8009
- const bk = Object.keys(b);
8010
- if (ak.length !== bk.length) return false;
8011
- return ak.every(
8012
- (k) => deepEqual(a[k], b[k])
8013
- );
8014
- }
8015
- return false;
8016
- }
8017
-
8018
- // src/utils/tool-output-serializer.ts
8019
- function createToolOutputSerializer(opts = {}) {
8020
- const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
8021
- function serialize(value) {
8022
- if (typeof value === "string") return value;
8023
- if (value === null || value === void 0) return "";
8024
- if (typeof value === "object") {
8025
- if (Array.isArray(value)) return value.map(serialize).join("\n");
8026
- if ("text" in value) {
8027
- const t = value.text;
8028
- return typeof t === "string" ? t : JSON.stringify(value, null, 2);
8029
- }
8030
- try {
8031
- return JSON.stringify(value, null, 2);
8032
- } catch {
8033
- return String(value);
8323
+ load: pressure.load,
8324
+ fatal
8325
+ });
8326
+ if (fatal) {
8327
+ throw new AgentError({
8328
+ message: `Auto-compaction failed at ${pressure.level} threshold`,
8329
+ code: ERROR_CODES.AGENT_CONTEXT_OVERFLOW,
8330
+ recoverable: true,
8331
+ context: {
8332
+ level: pressure.level,
8333
+ tokens: pressure.tokens,
8334
+ maxContext: this._maxContext
8335
+ },
8336
+ cause: err
8337
+ });
8034
8338
  }
8035
8339
  }
8036
- return String(value);
8037
- }
8038
- function enforceCap(text, remainingBudget) {
8039
- if (remainingBudget <= 0) {
8040
- return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
8041
- }
8042
- const textBytes = Buffer.byteLength(text, "utf8");
8043
- if (textBytes <= remainingBudget) {
8044
- return { text, newBudget: remainingBudget - textBytes };
8045
- }
8046
- const marker = `
8047
- \u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
8048
- `;
8049
- const markerBytes = Buffer.byteLength(marker, "utf8");
8050
- const available = remainingBudget - markerBytes;
8051
- if (available <= 0) {
8052
- return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
8053
- }
8054
- const half = Math.floor(available / 2);
8055
- const first = text.slice(0, half);
8056
- const second = text.slice(text.length - half);
8057
- return { text: `${first}${marker}${second}`, newBudget: 0 };
8058
8340
  }
8059
- return { serialize, enforceCap, capBytes };
8060
- }
8341
+ };
8061
8342
 
8062
8343
  // src/execution/tool-executor.ts
8063
8344
  var ToolExecutor = class _ToolExecutor {
@@ -8222,7 +8503,7 @@ ${post.additionalContext}`;
8222
8503
  );
8223
8504
  return { result, tool, durationMs: Date.now() - start };
8224
8505
  } catch (err) {
8225
- const msg = err instanceof Error ? err.message : String(err);
8506
+ const msg = toErrorMessage(err);
8226
8507
  const scrubbed = this.opts.secretScrubber.scrub(msg);
8227
8508
  this.opts.renderer?.writeToolResult(tool.name, scrubbed, true);
8228
8509
  const result = {
@@ -8243,7 +8524,7 @@ ${post.additionalContext}`;
8243
8524
  try {
8244
8525
  return await runOne(use);
8245
8526
  } catch (err) {
8246
- const msg = err instanceof Error ? err.message : String(err);
8527
+ const msg = toErrorMessage(err);
8247
8528
  const scrubbed = this.opts.secretScrubber.scrub(msg);
8248
8529
  const result = {
8249
8530
  type: "tool_result",
@@ -8526,15 +8807,6 @@ function extractMalformedRaw(input) {
8526
8807
  }
8527
8808
  }
8528
8809
 
8529
- // src/utils/assert-never.ts
8530
- function assertNever(x, message) {
8531
- const err = new Error(
8532
- `Unhandled case: ${JSON.stringify(x)}`
8533
- );
8534
- err.name = "AssertNeverError";
8535
- throw err;
8536
- }
8537
-
8538
8810
  // src/execution/autonomous-runner.ts
8539
8811
  var DoneConditionChecker = class {
8540
8812
  constructor(condition) {
@@ -8714,64 +8986,6 @@ var AutonomousRunner = class {
8714
8986
 
8715
8987
  // src/storage/goal-store.ts
8716
8988
  init_atomic_write();
8717
- function projectHash(absRoot) {
8718
- return createHash("sha256").update(path11.resolve(absRoot)).digest("hex").slice(0, 12);
8719
- }
8720
- function projectSlug(absRoot) {
8721
- const base = slugify(path11.basename(absRoot));
8722
- const hash = createHash("sha256").update(path11.resolve(absRoot)).digest("hex").slice(0, 6);
8723
- return `${base}-${hash}`;
8724
- }
8725
- function slugify(name) {
8726
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
8727
- }
8728
- function wstackGlobalRoot() {
8729
- const fromEnv = process.env["WRONGSTACK_HOME"];
8730
- if (fromEnv && fromEnv.trim().length > 0) return path11.resolve(fromEnv);
8731
- return path11.join(os.homedir(), ".wrongstack");
8732
- }
8733
- function resolveWstackPaths(opts) {
8734
- const globalRoot = opts.globalRoot ?? (opts.userHome ? path11.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
8735
- const hash = projectHash(opts.projectRoot);
8736
- const slug = projectSlug(opts.projectRoot);
8737
- const projectDir = path11.join(globalRoot, "projects", slug);
8738
- return {
8739
- globalRoot,
8740
- configDir: globalRoot,
8741
- globalConfig: path11.join(globalRoot, "config.json"),
8742
- secretsKey: path11.join(globalRoot, ".key"),
8743
- globalMemory: path11.join(globalRoot, "memory.md"),
8744
- globalSkills: path11.join(globalRoot, "skills"),
8745
- globalPrompts: path11.join(globalRoot, "prompts"),
8746
- cacheDir: path11.join(globalRoot, "cache"),
8747
- modelsCache: path11.join(globalRoot, "cache", "models.dev.json"),
8748
- modelsOverlayCache: path11.join(globalRoot, "cache", "models-overlay.json"),
8749
- historyFile: path11.join(globalRoot, "history"),
8750
- logFile: path11.join(globalRoot, "logs", "wrongstack.log"),
8751
- projectDir,
8752
- projectCodebaseIndex: path11.join(projectDir, "codebase-index"),
8753
- projectMemory: path11.join(projectDir, "memory.md"),
8754
- projectSessions: path11.join(projectDir, "sessions"),
8755
- projectTrust: path11.join(projectDir, "trust.json"),
8756
- projectMeta: path11.join(projectDir, "meta.json"),
8757
- projectLocalConfig: path11.join(projectDir, "config.local.json"),
8758
- inProjectConfig: path11.join(opts.projectRoot, ".wrongstack", "config.json"),
8759
- inProjectAgentsFile: path11.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
8760
- inProjectSkills: path11.join(opts.projectRoot, ".wrongstack", "skills"),
8761
- inProjectWorktrees: path11.join(opts.projectRoot, ".wrongstack", "worktrees"),
8762
- projectHash: hash,
8763
- projectSlug: slug,
8764
- projectGoal: path11.join(projectDir, "goal.json"),
8765
- projectSpecs: path11.join(projectDir, "specs"),
8766
- projectTaskGraphs: path11.join(projectDir, "task-graphs"),
8767
- projectSddSession: path11.join(projectDir, "sdd-session.json"),
8768
- projectPlan: path11.join(projectDir, "plan.json"),
8769
- projectAutophase: path11.join(projectDir, "autophase"),
8770
- syncConfig: path11.join(globalRoot, "sync.json")
8771
- };
8772
- }
8773
-
8774
- // src/storage/goal-store.ts
8775
8989
  var MAX_JOURNAL_ENTRIES = 500;
8776
8990
  function goalFilePath(projectRoot) {
8777
8991
  return resolveWstackPaths({ projectRoot }).projectGoal;
@@ -8780,7 +8994,7 @@ async function loadGoal(filePath, events) {
8780
8994
  const t0 = Date.now();
8781
8995
  let raw;
8782
8996
  try {
8783
- raw = await fsp.readFile(filePath, "utf8");
8997
+ raw = await fsp2.readFile(filePath, "utf8");
8784
8998
  } catch (err) {
8785
8999
  const code = err.code;
8786
9000
  if (code === "ENOENT") {
@@ -8799,7 +9013,7 @@ async function loadGoal(filePath, events) {
8799
9013
  store: "goal",
8800
9014
  filePath,
8801
9015
  operation: "load",
8802
- error: err instanceof Error ? err.message : String(err),
9016
+ error: toErrorMessage(err),
8803
9017
  recoverable: false
8804
9018
  });
8805
9019
  throw err;
@@ -8872,11 +9086,11 @@ async function saveGoal(filePath, goal, events) {
8872
9086
  store: "goal",
8873
9087
  filePath,
8874
9088
  operation: "save",
8875
- error: err instanceof Error ? err.message : String(err),
9089
+ error: toErrorMessage(err),
8876
9090
  recoverable: false
8877
9091
  });
8878
9092
  throw new FsError({
8879
- message: err instanceof Error ? err.message : String(err),
9093
+ message: toErrorMessage(err),
8880
9094
  code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
8881
9095
  path: filePath,
8882
9096
  cause: err
@@ -8930,11 +9144,6 @@ function computeTrend(history) {
8930
9144
  return "steady";
8931
9145
  }
8932
9146
 
8933
- // src/utils/sleep.ts
8934
- function sleep(ms) {
8935
- return new Promise((resolve5) => setTimeout(resolve5, ms));
8936
- }
8937
-
8938
9147
  // src/execution/autonomy-brain.ts
8939
9148
  function formatDecisionSummary(decision, request) {
8940
9149
  const question = request.question.length > 80 ? request.question.slice(0, 77) + "\u2026" : request.question;
@@ -8985,7 +9194,7 @@ var EternalAutonomyEngine = class {
8985
9194
  console.error(JSON.stringify({
8986
9195
  level: "error",
8987
9196
  event: "engine.persist_state_failed",
8988
- message: err instanceof Error ? err.message : String(err),
9197
+ message: toErrorMessage(err),
8989
9198
  context: { expectedState: "stopped" },
8990
9199
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
8991
9200
  }));
@@ -9018,7 +9227,7 @@ var EternalAutonomyEngine = class {
9018
9227
  } catch (err) {
9019
9228
  this.consecutiveFailures++;
9020
9229
  this.opts.onError?.(err instanceof Error ? err : new Error(String(err)), this.consecutiveFailures);
9021
- await this.appendFailure("engine error", err instanceof Error ? err.message : String(err));
9230
+ await this.appendFailure("engine error", toErrorMessage(err));
9022
9231
  }
9023
9232
  if (iterationOk) {
9024
9233
  this.consecutiveFailures = 0;
@@ -9119,7 +9328,7 @@ var EternalAutonomyEngine = class {
9119
9328
  } catch (err) {
9120
9329
  const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("abort"));
9121
9330
  status = isAbort ? "aborted" : "failure";
9122
- note = err instanceof Error ? err.message : String(err);
9331
+ note = toErrorMessage(err);
9123
9332
  if (!isAbort && typeof err?.recoverable === "boolean") {
9124
9333
  isTransientFailure = err.recoverable;
9125
9334
  }
@@ -12916,7 +13125,7 @@ function classifySubagentError(err, hints = {}) {
12916
13125
  const baseMessage2 = err.describe();
12917
13126
  return providerErrorToSubagentError(err, baseMessage2, cause);
12918
13127
  }
12919
- const baseMessage = err instanceof Error ? err.message : String(err);
13128
+ const baseMessage = toErrorMessage(err);
12920
13129
  if (err instanceof BudgetExceededError) {
12921
13130
  const map = {
12922
13131
  iterations: "budget_iterations",
@@ -14111,7 +14320,7 @@ var ParallelEternalEngine = class {
14111
14320
  console.error(JSON.stringify({
14112
14321
  level: "error",
14113
14322
  event: "engine.persist_state_failed",
14114
- message: err instanceof Error ? err.message : String(err),
14323
+ message: toErrorMessage(err),
14115
14324
  context: { expectedState: "stopped" },
14116
14325
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
14117
14326
  }));
@@ -14147,7 +14356,7 @@ var ParallelEternalEngine = class {
14147
14356
  );
14148
14357
  await this.appendFailure(
14149
14358
  "engine error",
14150
- err instanceof Error ? err.message : String(err)
14359
+ toErrorMessage(err)
14151
14360
  );
14152
14361
  }
14153
14362
  if (this.stopRequested) break;
@@ -14794,123 +15003,6 @@ function createMessage(type, from, payload, to) {
14794
15003
  priority: "normal"
14795
15004
  };
14796
15005
  }
14797
- var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
14798
- var IS_WINDOWS = process.platform === "win32";
14799
- var SEP = IS_WINDOWS ? "\\" : "/";
14800
- function isGlob(p) {
14801
- for (const c of p) {
14802
- if (GLOB_CHARS.has(c)) return true;
14803
- }
14804
- return false;
14805
- }
14806
- function globToRegex(pat) {
14807
- let i = 0;
14808
- let re = "^";
14809
- while (i < pat.length) {
14810
- const c = expectDefined(pat[i]);
14811
- if (c === "*") {
14812
- if (pat[i + 1] === "*") {
14813
- re += ".*";
14814
- i += 2;
14815
- if (pat[i] === "/") i++;
14816
- } else {
14817
- re += "[^/\\\\]*";
14818
- i++;
14819
- }
14820
- } else if (c === "?") {
14821
- re += "[^/\\\\]";
14822
- i++;
14823
- } else if (c === "[") {
14824
- let cls = "[";
14825
- i++;
14826
- if (pat[i] === "!" || pat[i] === "^") {
14827
- cls += "^";
14828
- i++;
14829
- }
14830
- while (i < pat.length && pat[i] !== "]") {
14831
- const ch = pat[i] ?? "";
14832
- if (ch === "\\") cls += "\\\\";
14833
- else if (ch === "]" || ch === "^") cls += `\\${ch}`;
14834
- else cls += ch;
14835
- i++;
14836
- }
14837
- cls += "]";
14838
- re += cls;
14839
- i++;
14840
- } else {
14841
- re += c.replace(/[.+^${}()|\\]/g, "\\$&");
14842
- i++;
14843
- }
14844
- }
14845
- return new RegExp(re + "$");
14846
- }
14847
- function baseDir(pat) {
14848
- let i = pat.length - 1;
14849
- while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
14850
- const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
14851
- return cut < 0 ? "." : pat.slice(0, cut);
14852
- }
14853
- async function expandGlob(pattern) {
14854
- if (!isGlob(pattern)) return [pattern];
14855
- const results = /* @__PURE__ */ new Set();
14856
- const abs = isAbsolute(pattern);
14857
- const base = abs ? baseDir(pattern) : baseDir(pattern);
14858
- const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
14859
- async function walk3(dir, pat) {
14860
- let entries;
14861
- try {
14862
- entries = await fsp.readdir(dir);
14863
- } catch {
14864
- return;
14865
- }
14866
- const firstGlob = pat.search(/[*?[[]/);
14867
- if (firstGlob < 0) {
14868
- const re = globToRegex(pat);
14869
- for (const e of entries) {
14870
- if (re.test(e)) {
14871
- const full = `${dir}${SEP}${e}`;
14872
- results.add(abs ? resolve(full) : full);
14873
- }
14874
- }
14875
- return;
14876
- }
14877
- const before = pat.slice(0, firstGlob);
14878
- const rest = pat.slice(firstGlob);
14879
- if (before.endsWith("**")) {
14880
- await walk3(dir, rest);
14881
- for (const e of entries) {
14882
- const full = `${dir}${SEP}${e}`;
14883
- try {
14884
- const stat6 = await fsp.stat(full);
14885
- if (stat6.isDirectory()) await walk3(full, rest);
14886
- } catch {
14887
- }
14888
- }
14889
- } else if (before === "") {
14890
- const re = globToRegex(rest);
14891
- for (const e of entries) {
14892
- if (re.test(e)) {
14893
- const full = `${dir}${SEP}${e}`;
14894
- results.add(abs ? resolve(full) : full);
14895
- }
14896
- }
14897
- } else {
14898
- const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
14899
- if (entries.includes(seg)) {
14900
- const full = `${dir}${SEP}${seg}`;
14901
- try {
14902
- const stat6 = await fsp.stat(full);
14903
- if (stat6.isDirectory()) await walk3(full, rest);
14904
- } catch {
14905
- }
14906
- }
14907
- }
14908
- }
14909
- await walk3(base === "." ? "." : base, relPat);
14910
- return [...results];
14911
- }
14912
-
14913
- // src/coordination/collab-debug.ts
14914
15006
  var DEFAULT_MAX_TARGET_FILES = 30;
14915
15007
  var CollabSession = class extends EventEmitter {
14916
15008
  sessionId;
@@ -15000,8 +15092,8 @@ var CollabSession = class extends EventEmitter {
15000
15092
  for (const filePath of allFiles) {
15001
15093
  try {
15002
15094
  const [content, stat6] = await Promise.all([
15003
- fsp.readFile(filePath, "utf8"),
15004
- fsp.stat(filePath)
15095
+ fsp2.readFile(filePath, "utf8"),
15096
+ fsp2.stat(filePath)
15005
15097
  ]);
15006
15098
  const ext = filePath.split(".").pop() ?? "";
15007
15099
  const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
@@ -15423,7 +15515,7 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
15423
15515
  for (const file of this.snapshot.files) {
15424
15516
  if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
15425
15517
  try {
15426
- const stat6 = await fsp.stat(file.path);
15518
+ const stat6 = await fsp2.stat(file.path);
15427
15519
  const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat6.mtimeMs > file.snapshotMtimeMs + 1;
15428
15520
  const sizeChanged = file.snapshotSizeBytes !== void 0 && stat6.size !== file.snapshotSizeBytes;
15429
15521
  if (mtimeChanged || sizeChanged) {
@@ -15706,7 +15798,7 @@ function makeSpawnTool(director, roster) {
15706
15798
  if (err instanceof FleetCostCapError) {
15707
15799
  return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
15708
15800
  }
15709
- return { error: err instanceof Error ? err.message : String(err) };
15801
+ return { error: toErrorMessage(err) };
15710
15802
  }
15711
15803
  }
15712
15804
  };
@@ -15788,7 +15880,7 @@ function makeAskTool(director) {
15788
15880
  _hint: "Response was large and stored. Use ask_result with the key to retrieve it."
15789
15881
  };
15790
15882
  } catch (err) {
15791
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
15883
+ return { ok: false, error: toErrorMessage(err) };
15792
15884
  }
15793
15885
  }
15794
15886
  };
@@ -16014,7 +16106,7 @@ function makeCollabDebugTool(director) {
16014
16106
  evaluations: report.evaluations
16015
16107
  };
16016
16108
  } catch (err) {
16017
- const msg = err instanceof Error ? err.message : String(err);
16109
+ const msg = toErrorMessage(err);
16018
16110
  return { error: "collab_debug failed: " + msg };
16019
16111
  }
16020
16112
  }
@@ -16588,7 +16680,7 @@ var Director = class _Director {
16588
16680
  ) : null;
16589
16681
  this.fleetManager = opts.fleetManager;
16590
16682
  if (this.sharedScratchpadPath) {
16591
- void fsp.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
16683
+ void fsp2.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
16592
16684
  }
16593
16685
  this.transport = new InMemoryBridgeTransport();
16594
16686
  this.bridge = new InMemoryAgentBridge(
@@ -17180,7 +17272,7 @@ var Director = class _Director {
17180
17272
  })),
17181
17273
  usage: this.usage.snapshot()
17182
17274
  };
17183
- await fsp.mkdir(path11.dirname(this.manifestPath), { recursive: true });
17275
+ await fsp2.mkdir(path3.dirname(this.manifestPath), { recursive: true });
17184
17276
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
17185
17277
  return this.manifestPath;
17186
17278
  }
@@ -17230,7 +17322,7 @@ var Director = class _Director {
17230
17322
  * listener for structured collection, and never affects exit code.
17231
17323
  */
17232
17324
  logShutdownError(phase, err) {
17233
- const detail = err instanceof Error ? err.message : String(err);
17325
+ const detail = toErrorMessage(err);
17234
17326
  process.emitWarning(
17235
17327
  `Director shutdown phase "${phase}" failed: ${detail}`,
17236
17328
  "DirectorShutdownWarning"
@@ -17394,10 +17486,10 @@ var Director = class _Director {
17394
17486
  */
17395
17487
  async readSession(subagentId, tail) {
17396
17488
  if (!this.sessionsRoot) return null;
17397
- const filePath = path11.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
17489
+ const filePath = path3.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
17398
17490
  let raw;
17399
17491
  try {
17400
- raw = await fsp.readFile(filePath, "utf8");
17492
+ raw = await fsp2.readFile(filePath, "utf8");
17401
17493
  } catch {
17402
17494
  return null;
17403
17495
  }
@@ -17823,7 +17915,7 @@ function createDelegateTool(opts) {
17823
17915
  summary
17824
17916
  };
17825
17917
  } catch (err) {
17826
- const message = err instanceof Error ? err.message : String(err);
17918
+ const message = toErrorMessage(err);
17827
17919
  opts.events?.emit("delegate.completed", {
17828
17920
  target,
17829
17921
  task: i.task,
@@ -17924,13 +18016,13 @@ async function readSubagentPartial(opts, subagentId) {
17924
18016
  if (!opts.sessionsRoot) return void 0;
17925
18017
  const candidates = [];
17926
18018
  if (opts.directorRunId) {
17927
- candidates.push(path11.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
18019
+ candidates.push(path3.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
17928
18020
  } else {
17929
18021
  try {
17930
- const entries = await fsp.readdir(opts.sessionsRoot, { withFileTypes: true });
18022
+ const entries = await fsp2.readdir(opts.sessionsRoot, { withFileTypes: true });
17931
18023
  for (const entry of entries) {
17932
18024
  if (entry.isDirectory()) {
17933
- candidates.push(path11.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
18025
+ candidates.push(path3.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
17934
18026
  }
17935
18027
  }
17936
18028
  } catch {
@@ -17940,7 +18032,7 @@ async function readSubagentPartial(opts, subagentId) {
17940
18032
  for (const file of candidates) {
17941
18033
  let raw;
17942
18034
  try {
17943
- raw = await fsp.readFile(file, "utf8");
18035
+ raw = await fsp2.readFile(file, "utf8");
17944
18036
  } catch {
17945
18037
  continue;
17946
18038
  }
@@ -17980,9 +18072,9 @@ function makeDirectorSessionFactory(opts) {
17980
18072
  let dir;
17981
18073
  if (opts.store) {
17982
18074
  store = opts.store;
17983
- dir = opts.sessionsRoot ? path11.join(opts.sessionsRoot, runId) : "(caller-managed)";
18075
+ dir = opts.sessionsRoot ? path3.join(opts.sessionsRoot, runId) : "(caller-managed)";
17984
18076
  } else if (opts.sessionsRoot) {
17985
- dir = path11.join(opts.sessionsRoot, runId);
18077
+ dir = path3.join(opts.sessionsRoot, runId);
17986
18078
  store = new DefaultSessionStore({ dir });
17987
18079
  } else {
17988
18080
  throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
@@ -18072,71 +18164,6 @@ var NULL_FLEET_BUS = new FleetBus();
18072
18164
 
18073
18165
  // src/models/models-registry.ts
18074
18166
  init_atomic_write();
18075
-
18076
- // src/utils/merge-models-payload.ts
18077
- function mergeModelsPayload(base, overlay) {
18078
- const out = {};
18079
- for (const [id, provider] of Object.entries(base)) {
18080
- out[id] = cloneProvider(provider);
18081
- }
18082
- for (const [id, ovProvider] of Object.entries(overlay)) {
18083
- const existing = out[id];
18084
- out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
18085
- }
18086
- return out;
18087
- }
18088
- function mergeProvider(base, overlay) {
18089
- const models = {};
18090
- for (const [mid, m] of Object.entries(base.models ?? {})) {
18091
- models[mid] = { ...m };
18092
- }
18093
- for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
18094
- const existing = models[mid];
18095
- models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
18096
- }
18097
- return {
18098
- ...base,
18099
- // Overlay scalar fields win when explicitly provided; otherwise keep base.
18100
- ...stripUndefined({
18101
- id: overlay.id,
18102
- name: overlay.name,
18103
- npm: overlay.npm,
18104
- api: overlay.api,
18105
- env: overlay.env,
18106
- doc: overlay.doc
18107
- }),
18108
- models
18109
- };
18110
- }
18111
- function mergeModel(base, overlay) {
18112
- const merged = { ...base, ...overlay };
18113
- if (base.limit || overlay.limit) {
18114
- merged.limit = { ...base.limit, ...overlay.limit };
18115
- }
18116
- if (base.cost || overlay.cost) {
18117
- merged.cost = { ...base.cost, ...overlay.cost };
18118
- }
18119
- if (base.modalities || overlay.modalities) {
18120
- merged.modalities = { ...base.modalities, ...overlay.modalities };
18121
- }
18122
- return merged;
18123
- }
18124
- function cloneProvider(p) {
18125
- const models = {};
18126
- for (const [mid, m] of Object.entries(p.models ?? {})) {
18127
- models[mid] = { ...m };
18128
- }
18129
- return { ...p, models };
18130
- }
18131
- function stripUndefined(obj) {
18132
- const out = {};
18133
- for (const [k, v] of Object.entries(obj)) {
18134
- if (v !== void 0) out[k] = v;
18135
- }
18136
- return out;
18137
- }
18138
-
18139
- // src/models/models-registry.ts
18140
18167
  var DEFAULT_URL = "https://models.dev/api.json";
18141
18168
  var DEFAULT_TTL_SECONDS = 24 * 3600;
18142
18169
  var DEFAULT_REFRESH_TIMEOUT_MS = 15e3;
@@ -18193,7 +18220,7 @@ var DefaultModelsRegistry = class {
18193
18220
  this.overlay = opts.overlay;
18194
18221
  this.overlayUrl = opts.overlayUrl;
18195
18222
  this.overlayFile = opts.overlayFile;
18196
- this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path11.join(path11.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
18223
+ this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path3.join(path3.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
18197
18224
  }
18198
18225
  async load(opts = {}) {
18199
18226
  if (this.payload && !opts.force) return this.payload;
@@ -18232,7 +18259,7 @@ var DefaultModelsRegistry = class {
18232
18259
  }
18233
18260
  if (overlayAvailable) {
18234
18261
  console.warn(
18235
- `ModelsRegistry: models.dev unavailable (${err instanceof Error ? err.message : String(err)}); serving curated overlay only.`
18262
+ `ModelsRegistry: models.dev unavailable (${toErrorMessage(err)}); serving curated overlay only.`
18236
18263
  );
18237
18264
  return {};
18238
18265
  }
@@ -18320,7 +18347,7 @@ var DefaultModelsRegistry = class {
18320
18347
  async readOverlayFile() {
18321
18348
  if (!this.overlayFile) return void 0;
18322
18349
  try {
18323
- const raw = await fsp.readFile(this.overlayFile, "utf8");
18350
+ const raw = await fsp2.readFile(this.overlayFile, "utf8");
18324
18351
  return JSON.parse(raw);
18325
18352
  } catch {
18326
18353
  return void 0;
@@ -18398,7 +18425,7 @@ var DefaultModelsRegistry = class {
18398
18425
  }
18399
18426
  async readCacheAt(file) {
18400
18427
  try {
18401
- const raw = await fsp.readFile(file, "utf8");
18428
+ const raw = await fsp2.readFile(file, "utf8");
18402
18429
  return JSON.parse(raw);
18403
18430
  } catch {
18404
18431
  return void 0;
@@ -18406,7 +18433,7 @@ var DefaultModelsRegistry = class {
18406
18433
  }
18407
18434
  /** Used by `wstack models refresh` to expose where the cache lives. */
18408
18435
  cacheLocation() {
18409
- return path11.resolve(this.cacheFile);
18436
+ return path3.resolve(this.cacheFile);
18410
18437
  }
18411
18438
  };
18412
18439
  function hasEntries(payload) {
@@ -18773,8 +18800,8 @@ var DefaultModeStore = class {
18773
18800
  }
18774
18801
  async loadActiveMode() {
18775
18802
  try {
18776
- const configPath = path11.join(this.configDir, "mode.json");
18777
- const content = await fsp.readFile(configPath, "utf8");
18803
+ const configPath = path3.join(this.configDir, "mode.json");
18804
+ const content = await fsp2.readFile(configPath, "utf8");
18778
18805
  const data = JSON.parse(content);
18779
18806
  this.activeModeId = data.activeMode ?? null;
18780
18807
  } catch {
@@ -18783,8 +18810,8 @@ var DefaultModeStore = class {
18783
18810
  }
18784
18811
  async saveActiveMode() {
18785
18812
  try {
18786
- await fsp.mkdir(this.configDir, { recursive: true });
18787
- const configPath = path11.join(this.configDir, "mode.json");
18813
+ await fsp2.mkdir(this.configDir, { recursive: true });
18814
+ const configPath = path3.join(this.configDir, "mode.json");
18788
18815
  await atomicWrite(
18789
18816
  configPath,
18790
18817
  JSON.stringify({ activeMode: this.activeModeId }, null, 2)
@@ -18796,14 +18823,14 @@ var DefaultModeStore = class {
18796
18823
  async function loadProjectModes(modesDir) {
18797
18824
  const modes = [];
18798
18825
  try {
18799
- const entries = await fsp.readdir(modesDir);
18826
+ const entries = await fsp2.readdir(modesDir);
18800
18827
  for (const entry of entries) {
18801
18828
  if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
18802
- const filePath = path11.join(modesDir, entry);
18803
- const stat6 = await fsp.stat(filePath);
18829
+ const filePath = path3.join(modesDir, entry);
18830
+ const stat6 = await fsp2.stat(filePath);
18804
18831
  if (!stat6.isFile()) continue;
18805
- const content = await fsp.readFile(filePath, "utf8");
18806
- const id = path11.basename(entry, path11.extname(entry));
18832
+ const content = await fsp2.readFile(filePath, "utf8");
18833
+ const id = path3.basename(entry, path3.extname(entry));
18807
18834
  modes.push({
18808
18835
  id,
18809
18836
  name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
@@ -18819,8 +18846,8 @@ async function loadProjectModes(modesDir) {
18819
18846
  async function loadUserModes(modesDir) {
18820
18847
  const modes = [];
18821
18848
  try {
18822
- const manifestPath = path11.join(modesDir, "modes.json");
18823
- const content = await fsp.readFile(manifestPath, "utf8");
18849
+ const manifestPath = path3.join(modesDir, "modes.json");
18850
+ const content = await fsp2.readFile(manifestPath, "utf8");
18824
18851
  const manifest = JSON.parse(content);
18825
18852
  for (const mode of manifest.modes) {
18826
18853
  modes.push(mode);
@@ -19546,7 +19573,7 @@ var TaskTracker = class {
19546
19573
  this.opts.onPersistError ? this.opts.onPersistError(err) : console.warn(JSON.stringify({
19547
19574
  level: "warn",
19548
19575
  event: "task_tracker.save_graph_failed",
19549
- message: err instanceof Error ? err.message : String(err),
19576
+ message: toErrorMessage(err),
19550
19577
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
19551
19578
  }));
19552
19579
  });
@@ -19771,7 +19798,7 @@ var SpecStore = class {
19771
19798
  indexPath;
19772
19799
  constructor(opts) {
19773
19800
  this.baseDir = opts.baseDir;
19774
- this.indexPath = path11.join(this.baseDir, "_index.json");
19801
+ this.indexPath = path3.join(this.baseDir, "_index.json");
19775
19802
  }
19776
19803
  async save(spec) {
19777
19804
  await ensureDir(this.baseDir);
@@ -19781,7 +19808,7 @@ var SpecStore = class {
19781
19808
  }
19782
19809
  async load(id) {
19783
19810
  try {
19784
- const raw = await fsp.readFile(this.filePath(id), "utf8");
19811
+ const raw = await fsp2.readFile(this.filePath(id), "utf8");
19785
19812
  return JSON.parse(raw);
19786
19813
  } catch {
19787
19814
  return null;
@@ -19793,7 +19820,7 @@ var SpecStore = class {
19793
19820
  }
19794
19821
  async delete(id) {
19795
19822
  try {
19796
- await fsp.unlink(this.filePath(id));
19823
+ await fsp2.unlink(this.filePath(id));
19797
19824
  await this.removeFromIndex(id);
19798
19825
  return true;
19799
19826
  } catch {
@@ -19802,7 +19829,7 @@ var SpecStore = class {
19802
19829
  }
19803
19830
  async exists(id) {
19804
19831
  try {
19805
- await fsp.access(this.filePath(id));
19832
+ await fsp2.access(this.filePath(id));
19806
19833
  return true;
19807
19834
  } catch {
19808
19835
  return false;
@@ -19840,11 +19867,11 @@ var SpecStore = class {
19840
19867
  return updated;
19841
19868
  }
19842
19869
  filePath(id) {
19843
- return path11.join(this.baseDir, `${id}.json`);
19870
+ return path3.join(this.baseDir, `${id}.json`);
19844
19871
  }
19845
19872
  async readIndex() {
19846
19873
  try {
19847
- const raw = await fsp.readFile(this.indexPath, "utf8");
19874
+ const raw = await fsp2.readFile(this.indexPath, "utf8");
19848
19875
  const parsed = JSON.parse(raw);
19849
19876
  if (parsed?.version === 1) return parsed;
19850
19877
  } catch {
@@ -19897,7 +19924,7 @@ var TaskGraphStore = class {
19897
19924
  indexPath;
19898
19925
  constructor(opts) {
19899
19926
  this.baseDir = opts.baseDir;
19900
- this.indexPath = path11.join(this.baseDir, "_index.json");
19927
+ this.indexPath = path3.join(this.baseDir, "_index.json");
19901
19928
  }
19902
19929
  async save(graph) {
19903
19930
  await ensureDir(this.baseDir);
@@ -19907,7 +19934,7 @@ var TaskGraphStore = class {
19907
19934
  }
19908
19935
  async load(id) {
19909
19936
  try {
19910
- const raw = await fsp.readFile(this.filePath(id), "utf8");
19937
+ const raw = await fsp2.readFile(this.filePath(id), "utf8");
19911
19938
  return graphFromJSON(raw);
19912
19939
  } catch {
19913
19940
  return null;
@@ -19919,7 +19946,7 @@ var TaskGraphStore = class {
19919
19946
  }
19920
19947
  async delete(id) {
19921
19948
  try {
19922
- await fsp.unlink(this.filePath(id));
19949
+ await fsp2.unlink(this.filePath(id));
19923
19950
  await this.removeFromIndex(id);
19924
19951
  return true;
19925
19952
  } catch {
@@ -19928,18 +19955,18 @@ var TaskGraphStore = class {
19928
19955
  }
19929
19956
  async exists(id) {
19930
19957
  try {
19931
- await fsp.access(this.filePath(id));
19958
+ await fsp2.access(this.filePath(id));
19932
19959
  return true;
19933
19960
  } catch {
19934
19961
  return false;
19935
19962
  }
19936
19963
  }
19937
19964
  filePath(id) {
19938
- return path11.join(this.baseDir, `${id}.json`);
19965
+ return path3.join(this.baseDir, `${id}.json`);
19939
19966
  }
19940
19967
  async readIndex() {
19941
19968
  try {
19942
- const raw = await fsp.readFile(this.indexPath, "utf8");
19969
+ const raw = await fsp2.readFile(this.indexPath, "utf8");
19943
19970
  const parsed = JSON.parse(raw);
19944
19971
  if (parsed?.version === 1) return parsed;
19945
19972
  } catch {
@@ -20216,7 +20243,7 @@ var AISpecBuilder = class {
20216
20243
  * ENOSPC / EACCES doesn't silently strand session edits in memory. */
20217
20244
  autoSave() {
20218
20245
  this.saveSession().catch((err) => {
20219
- const detail = err instanceof Error ? err.message : String(err);
20246
+ const detail = toErrorMessage(err);
20220
20247
  process.emitWarning(
20221
20248
  `SpecBuilder autoSave failed: ${detail}`,
20222
20249
  "SpecBuilderWarning"
@@ -21535,7 +21562,7 @@ var SddParallelRun = class {
21535
21562
  const failCount = results.length - successCount;
21536
21563
  for (let i = 0; i < results.length; i++) {
21537
21564
  const result = expectDefined(results[i]);
21538
- const taskId = expectDefined(taskIds[i]);
21565
+ const taskId = expectDefined(tasks[i]).id;
21539
21566
  if (result.status === "success") {
21540
21567
  this.opts.tracker.updateNodeStatus(taskId, "completed");
21541
21568
  this.retryMap.delete(taskId);
@@ -21749,7 +21776,7 @@ var DefaultHealthRegistry = class {
21749
21776
  try {
21750
21777
  return await Promise.race([check.check(), timeout]);
21751
21778
  } catch (err) {
21752
- return { status: "unhealthy", detail: err instanceof Error ? err.message : String(err) };
21779
+ return { status: "unhealthy", detail: toErrorMessage(err) };
21753
21780
  } finally {
21754
21781
  if (timer) clearTimeout(timer);
21755
21782
  }
@@ -21942,7 +21969,7 @@ async function startMetricsServer(opts) {
21942
21969
  } catch (err) {
21943
21970
  res.statusCode = 500;
21944
21971
  res.setHeader("content-type", "text/plain; charset=utf-8");
21945
- res.end(`metrics render failed: ${err instanceof Error ? err.message : String(err)}`);
21972
+ res.end(`metrics render failed: ${toErrorMessage(err)}`);
21946
21973
  return;
21947
21974
  }
21948
21975
  res.statusCode = 200;
@@ -21960,7 +21987,7 @@ async function startMetricsServer(opts) {
21960
21987
  (err) => {
21961
21988
  res.statusCode = 500;
21962
21989
  res.setHeader("content-type", "text/plain; charset=utf-8");
21963
- res.end(`health run failed: ${err instanceof Error ? err.message : String(err)}`);
21990
+ res.end(`health run failed: ${toErrorMessage(err)}`);
21964
21991
  }
21965
21992
  );
21966
21993
  return;