@wrongstack/core 0.257.0 → 0.260.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 (65) hide show
  1. package/dist/{agent-bridge-BrxWHEOm.d.ts → agent-bridge-BbskZ7HH.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-US741uBH.d.ts → agent-subagent-runner-BNIGZx18.d.ts} +28 -8
  3. package/dist/{brain-TjEEwSpw.d.ts → brain-C2yDd7Lw.d.ts} +58 -1
  4. package/dist/{compactor-C5sT4U7I.d.ts → compactor-t0R_AIt_.d.ts} +1 -1
  5. package/dist/{config-DuAu23zm.d.ts → config-FG6As4H5.d.ts} +1 -1
  6. package/dist/{context-CGdgA0q6.d.ts → context-JFOVvu6z.d.ts} +22 -0
  7. package/dist/coordination/index.d.ts +14 -14
  8. package/dist/coordination/index.js +195 -33
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +25 -25
  11. package/dist/defaults/index.js +908 -92
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +15 -15
  14. package/dist/execution/index.js +134 -35
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +1 -1
  17. package/dist/extension/index.d.ts +6 -6
  18. package/dist/{goal-preamble-UiEkbNmW.d.ts → goal-preamble-B1IXJtLX.d.ts} +11 -9
  19. package/dist/{goal-store-CV9Yz2X_.d.ts → goal-store-CPXz6Mml.d.ts} +4 -2
  20. package/dist/{index-CitPrI3a.d.ts → index-BPcg4N3M.d.ts} +5 -5
  21. package/dist/{index-CC0Mcm05.d.ts → index-CebbJB94.d.ts} +8 -8
  22. package/dist/index.d.ts +47 -43
  23. package/dist/index.js +1571 -284
  24. package/dist/index.js.map +1 -1
  25. package/dist/infrastructure/index.d.ts +6 -6
  26. package/dist/kernel/index.d.ts +9 -9
  27. package/dist/kernel/index.js.map +1 -1
  28. package/dist/{llm-selector-CJ4SyAFE.d.ts → llm-selector-DXxI2tlu.d.ts} +2 -2
  29. package/dist/{mcp-servers-D8YnLaEp.d.ts → mcp-servers-OwNHo43-.d.ts} +3 -3
  30. package/dist/models/index.d.ts +5 -5
  31. package/dist/{models-registry-ByZCdFuQ.d.ts → models-registry-Djlmq4uB.d.ts} +1 -1
  32. package/dist/{multi-agent-coordinator-DqTUEAeC.d.ts → multi-agent-coordinator-CEmrSCMJ.d.ts} +1 -1
  33. package/dist/{null-fleet-bus-B5mfTJXT.d.ts → null-fleet-bus-DT92xqgJ.d.ts} +13 -8
  34. package/dist/observability/index.d.ts +2 -2
  35. package/dist/{package-outdated-watcher-BSgR_kK-.d.ts → package-outdated-watcher-C70ag2G9.d.ts} +3 -3
  36. package/dist/{parallel-eternal-engine-C0juOszP.d.ts → parallel-eternal-engine-0SItuq5r.d.ts} +13 -9
  37. package/dist/{path-resolver-CbkT-RMU.d.ts → path-resolver-DKBh6Jlo.d.ts} +3 -3
  38. package/dist/{permission-CwBBpCoF.d.ts → permission-BJ7eO9Vl.d.ts} +1 -1
  39. package/dist/{permission-policy-B8rSu908.d.ts → permission-policy-DEXOfnpm.d.ts} +3 -2
  40. package/dist/{pipeline-JG8XoudC.d.ts → pipeline-zflkI2dp.d.ts} +2 -2
  41. package/dist/{plan-templates-DPiQMkBz.d.ts → plan-templates-BFXyRkEK.d.ts} +32 -11
  42. package/dist/{provider-runner-hM7EXlLI.d.ts → provider-runner-BC-uywtT.d.ts} +3 -3
  43. package/dist/{retry-policy-Tg7LXkoK.d.ts → retry-policy-Cavrzmtk.d.ts} +1 -1
  44. package/dist/sdd/index.d.ts +8 -8
  45. package/dist/sdd/index.js +20 -2
  46. package/dist/sdd/index.js.map +1 -1
  47. package/dist/{secret-vault-gxtFZYBt.d.ts → secret-vault-CDvDYXWX.d.ts} +1 -1
  48. package/dist/security/index.d.ts +4 -4
  49. package/dist/security/index.js +30 -1
  50. package/dist/security/index.js.map +1 -1
  51. package/dist/{selector-DWsqVjGf.d.ts → selector-B7AivHsu.d.ts} +1 -1
  52. package/dist/{session-event-bridge-BAFWdgQ3.d.ts → session-event-bridge-BmIDxdJd.d.ts} +1 -1
  53. package/dist/{session-reader-CqRvaL5v.d.ts → session-reader-DtofsB-2.d.ts} +1 -1
  54. package/dist/storage/index.d.ts +30 -21
  55. package/dist/storage/index.js +1264 -216
  56. package/dist/storage/index.js.map +1 -1
  57. package/dist/types/index.d.ts +19 -19
  58. package/dist/types/index.js +8 -0
  59. package/dist/types/index.js.map +1 -1
  60. package/dist/utils/index.d.ts +101 -3
  61. package/dist/utils/index.js +92 -1
  62. package/dist/utils/index.js.map +1 -1
  63. package/package.json +1 -1
  64. package/skills/output-standards/SKILL.md +14 -9
  65. package/skills/output-standards/SKILL.save.md +3 -2
@@ -451,6 +451,40 @@ var DefaultSessionStore = class _DefaultSessionStore {
451
451
  this.events = opts.events;
452
452
  this.secretScrubber = opts.secretScrubber;
453
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
+ }
454
488
  /** Absolute path to the session index file. */
455
489
  get indexFile() {
456
490
  return path11.join(this.dir, "_index.jsonl");
@@ -474,22 +508,26 @@ var DefaultSessionStore = class _DefaultSessionStore {
474
508
  const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
475
509
  const shardDir = await this.ensureShardDir(id);
476
510
  const file = path11.join(shardDir, `${path11.basename(id)}.jsonl`);
511
+ const t0 = Date.now();
477
512
  let handle;
478
513
  try {
479
514
  handle = await fsp.open(file, "a", 384);
480
515
  } catch (err) {
516
+ this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), false);
481
517
  throw new Error(
482
518
  `Failed to open session file: ${err instanceof Error ? err.message : String(err)}`,
483
519
  { cause: err }
484
520
  );
485
521
  }
486
522
  try {
487
- return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
523
+ const writer = new FileSessionWriter(id, handle, startedAt, meta, this.events, {
488
524
  dir: shardDir,
489
525
  filePath: file,
490
526
  secretScrubber: this.secretScrubber,
491
527
  onClose: (s) => this.appendToIndex(s)
492
528
  });
529
+ this.emitWrite(id, file, "create", "success", Date.now() - t0);
530
+ return writer;
493
531
  } catch (err) {
494
532
  await handle.close().catch((e) => console.warn(JSON.stringify({
495
533
  level: "warn",
@@ -497,16 +535,19 @@ var DefaultSessionStore = class _DefaultSessionStore {
497
535
  message: e instanceof Error ? e.message : String(e),
498
536
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
499
537
  })));
538
+ this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), true);
500
539
  throw err;
501
540
  }
502
541
  }
503
542
  async resume(id) {
504
543
  const file = this.sessionPath(id, ".jsonl");
544
+ const t0 = Date.now();
505
545
  const data = await this.load(id);
506
546
  let handle;
507
547
  try {
508
548
  handle = await fsp.open(file, "a", 384);
509
549
  } catch (err) {
550
+ this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), false);
510
551
  throw new Error(
511
552
  `Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
512
553
  { cause: err }
@@ -534,6 +575,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
534
575
  onClose: (s) => this.appendToIndex(s)
535
576
  }
536
577
  );
578
+ this.emitWrite(id, file, "resume", "success", Date.now() - t0);
537
579
  return { writer, data };
538
580
  } catch (err) {
539
581
  await handle.close().catch((e) => console.warn(JSON.stringify({
@@ -542,27 +584,39 @@ var DefaultSessionStore = class _DefaultSessionStore {
542
584
  message: e instanceof Error ? e.message : String(e),
543
585
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
544
586
  })));
587
+ this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), true);
545
588
  throw err;
546
589
  }
547
590
  }
548
591
  async load(id) {
549
592
  const file = this.sessionPath(id, ".jsonl");
550
- const raw = await fsp.readFile(file, "utf8");
551
- const lines = raw.split("\n").filter((l) => l.trim());
552
- const events = [];
553
- for (const line of lines) {
554
- try {
555
- const parsed = JSON.parse(line);
556
- if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
557
- events.push(parsed);
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 {
558
607
  }
559
- } catch {
560
608
  }
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);
561
619
  }
562
- const meta = this.metaFromEvents(id, events);
563
- const { messages, usage } = this.replay(events, id);
564
- const toolCallEnds = extractToolCallEnds(events);
565
- return { metadata: meta, events, messages, usage, toolCallEnds };
566
620
  }
567
621
  async list(limit = 20) {
568
622
  try {
@@ -629,12 +683,22 @@ var DefaultSessionStore = class _DefaultSessionStore {
629
683
  * (keep latest per session), and rewrite. Atomic via temp+rename.
630
684
  */
631
685
  async compactIndex() {
632
- const entries = await this.readIndex();
633
- if (entries.length === 0) return;
634
- const tmp = `${this.indexFile}.compact.tmp`;
635
- const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
636
- await fsp.writeFile(tmp, lines, "utf8");
637
- await fsp.rename(tmp, this.indexFile);
686
+ const t0 = Date.now();
687
+ let outcome = "success";
688
+ let errorMsg;
689
+ try {
690
+ const entries = await this.readIndex();
691
+ if (entries.length === 0) return;
692
+ const tmp = `${this.indexFile}.compact.tmp`;
693
+ 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);
696
+ } catch (err) {
697
+ outcome = "failure";
698
+ errorMsg = err instanceof Error ? err.message : String(err);
699
+ } finally {
700
+ this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
701
+ }
638
702
  }
639
703
  /**
640
704
  * Read the index file and return deduplicated session summaries.
@@ -710,22 +774,31 @@ var DefaultSessionStore = class _DefaultSessionStore {
710
774
  }
711
775
  async summaryFor(id) {
712
776
  const manifest = this.sessionPath(id, ".summary.json");
777
+ const t0 = Date.now();
778
+ let outcome = "success";
779
+ let errorMsg;
713
780
  try {
714
781
  const raw = await fsp.readFile(manifest, "utf8");
782
+ this.emitRead(id, manifest, "summary", "success", Date.now() - t0);
715
783
  return JSON.parse(raw);
716
784
  } catch {
717
785
  const full = this.sessionPath(id, ".jsonl");
718
786
  const stat6 = await fsp.stat(full);
719
787
  const summary = await this.summarize(id, stat6.mtime.toISOString());
720
788
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
789
+ const msg = err instanceof Error ? err.message : String(err);
790
+ this.emitError(id, manifest, "summary_fallback", msg, true);
721
791
  console.warn(JSON.stringify({
722
792
  level: "warn",
723
793
  event: "session_store.manifest_write_failed",
724
794
  sessionId: id,
725
- message: err instanceof Error ? err.message : String(err),
795
+ message: msg,
726
796
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
727
797
  }));
728
798
  });
799
+ outcome = "failure";
800
+ errorMsg = "summary fallback \u2014 manifest rebuilt";
801
+ this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
729
802
  return summary;
730
803
  }
731
804
  }
@@ -991,7 +1064,7 @@ function extractToolCallEnds(events) {
991
1064
  return result;
992
1065
  }
993
1066
  var FileSessionWriter = class _FileSessionWriter {
994
- constructor(id, handle, startedAt, meta, events, opts = {}) {
1067
+ constructor(id, handle, startedAt, meta, events, opts = {}, traceId) {
995
1068
  this.id = id;
996
1069
  this.handle = handle;
997
1070
  this.startedAt = startedAt;
@@ -1010,6 +1083,7 @@ var FileSessionWriter = class _FileSessionWriter {
1010
1083
  provider: meta.provider ?? "unknown",
1011
1084
  tokenTotal: 0
1012
1085
  };
1086
+ this.traceId = traceId;
1013
1087
  }
1014
1088
  id;
1015
1089
  handle;
@@ -1042,6 +1116,8 @@ var FileSessionWriter = class _FileSessionWriter {
1042
1116
  lastAppendWarnAt = 0;
1043
1117
  secretScrubber;
1044
1118
  onCloseCb;
1119
+ /** Implements SessionWriter.traceId — propagated from ContextInit.traceId. */
1120
+ traceId;
1045
1121
  // ── Write buffer — batches events to reduce per-event disk I/O ─────────
1046
1122
  //
1047
1123
  // Every append() pushes the scrubbed event into an in-memory buffer instead
@@ -1195,9 +1271,14 @@ var FileSessionWriter = class _FileSessionWriter {
1195
1271
  const eventCount = this.writeBuffer.length;
1196
1272
  const batch = this.writeBuffer.map((e) => JSON.stringify(e)).join("\n") + "\n";
1197
1273
  this.writeBuffer = [];
1274
+ const t0 = Date.now();
1275
+ let outcome = "success";
1276
+ let errorMsg;
1198
1277
  try {
1199
1278
  await this.enqueueWrite(batch);
1200
1279
  } catch (err) {
1280
+ outcome = "failure";
1281
+ errorMsg = err instanceof Error ? err.message : String(err);
1201
1282
  this.appendFailCount += eventCount;
1202
1283
  const now = Date.now();
1203
1284
  if (now - this.lastAppendWarnAt > 5e3) {
@@ -1211,6 +1292,18 @@ var FileSessionWriter = class _FileSessionWriter {
1211
1292
  this.lastAppendWarnAt = now;
1212
1293
  this.appendFailCount = 0;
1213
1294
  }
1295
+ } finally {
1296
+ this.events?.emit("storage.write", {
1297
+ sessionId: this.id,
1298
+ store: "session",
1299
+ filePath: this.filePath,
1300
+ operation: "flush",
1301
+ outcome,
1302
+ durationMs: Date.now() - t0,
1303
+ ...errorMsg !== void 0 ? { error: errorMsg } : {},
1304
+ ...eventCount !== void 0 ? { eventCount } : {},
1305
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
1306
+ });
1214
1307
  }
1215
1308
  }
1216
1309
  observeForSummary(event) {
@@ -1276,14 +1369,46 @@ var FileSessionWriter = class _FileSessionWriter {
1276
1369
  outcome: this.outcome ?? "completed"
1277
1370
  };
1278
1371
  if (this.manifestFile) {
1372
+ const t0 = Date.now();
1373
+ let outcome = "success";
1374
+ let errorMsg;
1279
1375
  try {
1280
1376
  await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
1281
- } catch {
1377
+ } catch (err) {
1378
+ outcome = "failure";
1379
+ errorMsg = err instanceof Error ? err.message : String(err);
1380
+ } finally {
1381
+ this.events?.emit("storage.write", {
1382
+ sessionId: this.id,
1383
+ store: "session",
1384
+ filePath: this.manifestFile,
1385
+ operation: "close",
1386
+ outcome,
1387
+ durationMs: Date.now() - t0,
1388
+ ...errorMsg !== void 0 ? { error: errorMsg } : {},
1389
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
1390
+ });
1282
1391
  }
1283
1392
  }
1393
+ const idxT0 = Date.now();
1394
+ let idxOutcome = "success";
1395
+ let idxError;
1284
1396
  try {
1285
1397
  await this.onCloseCb?.(this.summary);
1286
- } catch {
1398
+ } catch (err) {
1399
+ idxOutcome = "failure";
1400
+ idxError = err instanceof Error ? err.message : String(err);
1401
+ } finally {
1402
+ this.events?.emit("storage.write", {
1403
+ sessionId: this.summary.id,
1404
+ store: "session",
1405
+ filePath: this.filePath,
1406
+ operation: "index_append",
1407
+ outcome: idxOutcome,
1408
+ durationMs: Date.now() - idxT0,
1409
+ ...idxError !== void 0 ? { error: idxError } : {},
1410
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
1411
+ });
1287
1412
  }
1288
1413
  try {
1289
1414
  await this.handle.close();
@@ -1445,23 +1570,81 @@ function userInputTitle(content) {
1445
1570
  init_atomic_write();
1446
1571
  var QueueStore = class {
1447
1572
  file;
1573
+ // Use `| undefined` (not `?`) so exactOptionalPropertyTypes doesn't
1574
+ // reject assigning an optional constructor parameter to these fields.
1575
+ events;
1576
+ traceId;
1448
1577
  constructor(opts) {
1449
1578
  this.file = path11.join(opts.dir, "queue.json");
1579
+ this.events = opts.events;
1580
+ this.traceId = opts.traceId;
1450
1581
  }
1451
1582
  async write(items) {
1583
+ const t0 = Date.now();
1452
1584
  if (items.length === 0) {
1453
1585
  await this.clear();
1454
1586
  return;
1455
1587
  }
1456
- await atomicWrite(this.file, JSON.stringify(items), { mode: 384 });
1588
+ try {
1589
+ await atomicWrite(this.file, JSON.stringify(items), { mode: 384 });
1590
+ this.events?.emit("storage.write", {
1591
+ sessionId: this.traceId ?? "~boot~",
1592
+ store: "queue",
1593
+ filePath: this.file,
1594
+ operation: "write",
1595
+ outcome: "success",
1596
+ durationMs: Date.now() - t0,
1597
+ ...this.traceId !== void 0 && { traceId: this.traceId }
1598
+ });
1599
+ } catch (err) {
1600
+ this.events?.emit("storage.error", {
1601
+ sessionId: this.traceId ?? "~boot~",
1602
+ store: "queue",
1603
+ filePath: this.file,
1604
+ operation: "write",
1605
+ outcome: "failure",
1606
+ error: err instanceof Error ? err.message : String(err),
1607
+ recoverable: false,
1608
+ ...this.traceId !== void 0 && { traceId: this.traceId }
1609
+ });
1610
+ console.warn(JSON.stringify({
1611
+ level: "warn",
1612
+ event: "queue_store.write_failed",
1613
+ path: this.file,
1614
+ message: err instanceof Error ? err.message : String(err),
1615
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1616
+ }));
1617
+ }
1457
1618
  }
1458
1619
  async read() {
1620
+ const t0 = Date.now();
1459
1621
  let raw;
1460
1622
  try {
1461
1623
  raw = await fsp.readFile(this.file, "utf8");
1462
1624
  } catch (err) {
1463
1625
  const code = err.code;
1464
- if (code === "ENOENT") return [];
1626
+ if (code === "ENOENT") {
1627
+ this.events?.emit("storage.read", {
1628
+ sessionId: this.traceId ?? "~boot~",
1629
+ store: "queue",
1630
+ filePath: this.file,
1631
+ operation: "read",
1632
+ outcome: "success",
1633
+ durationMs: Date.now() - t0,
1634
+ ...this.traceId !== void 0 && { traceId: this.traceId }
1635
+ });
1636
+ return [];
1637
+ }
1638
+ this.events?.emit("storage.error", {
1639
+ sessionId: this.traceId ?? "~boot~",
1640
+ store: "queue",
1641
+ filePath: this.file,
1642
+ operation: "read",
1643
+ outcome: "failure",
1644
+ error: err instanceof Error ? err.message : String(err),
1645
+ recoverable: true,
1646
+ ...this.traceId !== void 0 && { traceId: this.traceId }
1647
+ });
1465
1648
  console.warn(JSON.stringify({
1466
1649
  level: "warn",
1467
1650
  event: "queue_store.read_failed",
@@ -1475,9 +1658,40 @@ var QueueStore = class {
1475
1658
  try {
1476
1659
  parsed = JSON.parse(raw);
1477
1660
  } catch {
1661
+ this.events?.emit("storage.read", {
1662
+ sessionId: this.traceId ?? "~boot~",
1663
+ store: "queue",
1664
+ filePath: this.file,
1665
+ operation: "read",
1666
+ outcome: "failure",
1667
+ durationMs: Date.now() - t0,
1668
+ error: "parse_failed",
1669
+ ...this.traceId !== void 0 && { traceId: this.traceId }
1670
+ });
1478
1671
  return [];
1479
1672
  }
1480
- if (!Array.isArray(parsed)) return [];
1673
+ if (!Array.isArray(parsed)) {
1674
+ this.events?.emit("storage.read", {
1675
+ sessionId: this.traceId ?? "~boot~",
1676
+ store: "queue",
1677
+ filePath: this.file,
1678
+ operation: "read",
1679
+ outcome: "failure",
1680
+ durationMs: Date.now() - t0,
1681
+ error: "invalid_schema",
1682
+ ...this.traceId !== void 0 && { traceId: this.traceId }
1683
+ });
1684
+ return [];
1685
+ }
1686
+ this.events?.emit("storage.read", {
1687
+ sessionId: this.traceId ?? "~boot~",
1688
+ store: "queue",
1689
+ filePath: this.file,
1690
+ operation: "read",
1691
+ outcome: "success",
1692
+ durationMs: Date.now() - t0,
1693
+ ...this.traceId !== void 0 && { traceId: this.traceId }
1694
+ });
1481
1695
  const out = [];
1482
1696
  for (const v of parsed) {
1483
1697
  if (isPersistedQueueItem(v)) out.push(v);
@@ -1485,11 +1699,31 @@ var QueueStore = class {
1485
1699
  return out;
1486
1700
  }
1487
1701
  async clear() {
1702
+ const t0 = Date.now();
1488
1703
  try {
1489
1704
  await fsp.unlink(this.file);
1705
+ this.events?.emit("storage.write", {
1706
+ sessionId: this.traceId ?? "~boot~",
1707
+ store: "queue",
1708
+ filePath: this.file,
1709
+ operation: "clear",
1710
+ outcome: "success",
1711
+ durationMs: Date.now() - t0,
1712
+ ...this.traceId !== void 0 && { traceId: this.traceId }
1713
+ });
1490
1714
  } catch (err) {
1491
1715
  const code = err.code;
1492
1716
  if (code === "ENOENT") return;
1717
+ this.events?.emit("storage.error", {
1718
+ sessionId: this.traceId ?? "~boot~",
1719
+ store: "queue",
1720
+ filePath: this.file,
1721
+ operation: "clear",
1722
+ outcome: "failure",
1723
+ error: err instanceof Error ? err.message : String(err),
1724
+ recoverable: true,
1725
+ ...this.traceId !== void 0 && { traceId: this.traceId }
1726
+ });
1493
1727
  console.warn(JSON.stringify({
1494
1728
  level: "warn",
1495
1729
  event: "queue_store.clear_failed",
@@ -1876,6 +2110,7 @@ var MAX_BYTES_TOTAL = 32e3;
1876
2110
  var DefaultMemoryStore = class {
1877
2111
  files;
1878
2112
  events;
2113
+ traceId;
1879
2114
  backend;
1880
2115
  /**
1881
2116
  * Per-scope serialization queue. `remember` / `forget` / `consolidate` /
@@ -1941,15 +2176,70 @@ var DefaultMemoryStore = class {
1941
2176
  if (writeErr2) {
1942
2177
  parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
1943
2178
  }
1944
- const body = await this.backend.readAll(scope, this.files[scope]);
1945
- if (body.trim()) parts.push(`## ${labelOf(scope)}
2179
+ const t0 = Date.now();
2180
+ const filePath = this.files[scope];
2181
+ try {
2182
+ const body = await this.backend.readAll(scope, filePath);
2183
+ const dur = Date.now() - t0;
2184
+ this.events?.emit("storage.read", {
2185
+ sessionId: "~memory~",
2186
+ store: "memory",
2187
+ filePath,
2188
+ operation: "readAll",
2189
+ outcome: "success",
2190
+ durationMs: dur,
2191
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2192
+ });
2193
+ if (body.trim()) parts.push(`## ${labelOf(scope)}
1946
2194
 
1947
2195
  ${body.trim()}`);
2196
+ } catch (err) {
2197
+ const dur = Date.now() - t0;
2198
+ this.events?.emit("storage.read", {
2199
+ sessionId: "~memory~",
2200
+ store: "memory",
2201
+ filePath,
2202
+ operation: "readAll",
2203
+ outcome: "failure",
2204
+ durationMs: dur,
2205
+ error: err instanceof Error ? err.message : String(err),
2206
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2207
+ });
2208
+ throw err;
2209
+ }
1948
2210
  }
1949
2211
  return parts.join("\n\n");
1950
2212
  }
1951
2213
  async read(scope) {
1952
- return this.backend.readAll(scope, this.files[scope]);
2214
+ const t0 = Date.now();
2215
+ const filePath = this.files[scope];
2216
+ try {
2217
+ const body = await this.backend.readAll(scope, filePath);
2218
+ const dur = Date.now() - t0;
2219
+ this.events?.emit("storage.read", {
2220
+ sessionId: "~memory~",
2221
+ store: "memory",
2222
+ filePath,
2223
+ operation: "read",
2224
+ outcome: "success",
2225
+ durationMs: dur,
2226
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2227
+ });
2228
+ return body;
2229
+ } catch (err) {
2230
+ const dur = Date.now() - t0;
2231
+ this.events?.emit("storage.read", {
2232
+ sessionId: "~memory~",
2233
+ store: "memory",
2234
+ filePath,
2235
+ operation: "read",
2236
+ outcome: "failure",
2237
+ durationMs: dur,
2238
+ error: err instanceof Error ? err.message : String(err),
2239
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2240
+ });
2241
+ throw err;
2242
+ }
1953
2243
  }
1954
2244
  /**
1955
2245
  * List entries from a scope, newest first. Delegates to the backend
@@ -1975,7 +2265,34 @@ ${body.trim()}`);
1975
2265
  const ts = (/* @__PURE__ */ new Date()).toISOString();
1976
2266
  return this.runSerialized(scope, async () => {
1977
2267
  const entry = { scope, text, ts, ...metadata };
1978
- await this.backend.remember(scope, entry, this.files[scope]);
2268
+ const filePath = this.files[scope];
2269
+ const t0 = Date.now();
2270
+ try {
2271
+ await this.backend.remember(scope, entry, filePath);
2272
+ const dur = Date.now() - t0;
2273
+ this.events?.emit("storage.write", {
2274
+ sessionId: "~memory~",
2275
+ store: "memory",
2276
+ filePath,
2277
+ operation: "remember",
2278
+ outcome: "success",
2279
+ durationMs: dur,
2280
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2281
+ });
2282
+ } catch (err) {
2283
+ const dur = Date.now() - t0;
2284
+ this.events?.emit("storage.write", {
2285
+ sessionId: "~memory~",
2286
+ store: "memory",
2287
+ filePath,
2288
+ operation: "remember",
2289
+ outcome: "failure",
2290
+ durationMs: dur,
2291
+ error: err instanceof Error ? err.message : String(err),
2292
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2293
+ });
2294
+ throw err;
2295
+ }
1979
2296
  const raw = await this.backend.readAll(scope, this.files[scope]);
1980
2297
  if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
1981
2298
  const removed = await this.backend.consolidate(scope, this.files[scope]);
@@ -2103,7 +2420,35 @@ ${body.trim()}`);
2103
2420
  }
2104
2421
  async forget(query, scope = "project-memory") {
2105
2422
  return this.runSerialized(scope, async () => {
2106
- const removed = await this.backend.forget(scope, query, this.files[scope]);
2423
+ const filePath = this.files[scope];
2424
+ const t0 = Date.now();
2425
+ let removed = 0;
2426
+ try {
2427
+ removed = await this.backend.forget(scope, query, filePath);
2428
+ const dur = Date.now() - t0;
2429
+ this.events?.emit("storage.write", {
2430
+ sessionId: "~memory~",
2431
+ store: "memory",
2432
+ filePath,
2433
+ operation: "forget",
2434
+ outcome: "success",
2435
+ durationMs: dur,
2436
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2437
+ });
2438
+ } catch (err) {
2439
+ const dur = Date.now() - t0;
2440
+ this.events?.emit("storage.write", {
2441
+ sessionId: "~memory~",
2442
+ store: "memory",
2443
+ filePath,
2444
+ operation: "forget",
2445
+ outcome: "failure",
2446
+ durationMs: dur,
2447
+ error: err instanceof Error ? err.message : String(err),
2448
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2449
+ });
2450
+ throw err;
2451
+ }
2107
2452
  if (removed > 0) {
2108
2453
  this.events?.emit("memory.forgotten", {
2109
2454
  scope,
@@ -2117,7 +2462,35 @@ ${body.trim()}`);
2117
2462
  }
2118
2463
  async consolidate(scope) {
2119
2464
  return this.runSerialized(scope, async () => {
2120
- const removed = await this.backend.consolidate(scope, this.files[scope]);
2465
+ const filePath = this.files[scope];
2466
+ const t0 = Date.now();
2467
+ let removed = 0;
2468
+ try {
2469
+ removed = await this.backend.consolidate(scope, filePath);
2470
+ const dur = Date.now() - t0;
2471
+ this.events?.emit("storage.write", {
2472
+ sessionId: "~memory~",
2473
+ store: "memory",
2474
+ filePath,
2475
+ operation: "consolidate",
2476
+ outcome: "success",
2477
+ durationMs: dur,
2478
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2479
+ });
2480
+ } catch (err) {
2481
+ const dur = Date.now() - t0;
2482
+ this.events?.emit("storage.write", {
2483
+ sessionId: "~memory~",
2484
+ store: "memory",
2485
+ filePath,
2486
+ operation: "consolidate",
2487
+ outcome: "failure",
2488
+ durationMs: dur,
2489
+ error: err instanceof Error ? err.message : String(err),
2490
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2491
+ });
2492
+ throw err;
2493
+ }
2121
2494
  if (removed > 0) {
2122
2495
  this.events?.emit("memory.consolidated", {
2123
2496
  scope,
@@ -2130,7 +2503,34 @@ ${body.trim()}`);
2130
2503
  async clear(scope) {
2131
2504
  if (scope) {
2132
2505
  await this.runSerialized(scope, async () => {
2133
- await this.backend.clear(scope, this.files[scope]);
2506
+ const filePath = this.files[scope];
2507
+ const t0 = Date.now();
2508
+ try {
2509
+ await this.backend.clear(scope, filePath);
2510
+ const dur = Date.now() - t0;
2511
+ this.events?.emit("storage.write", {
2512
+ sessionId: "~memory~",
2513
+ store: "memory",
2514
+ filePath,
2515
+ operation: "clear",
2516
+ outcome: "success",
2517
+ durationMs: dur,
2518
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2519
+ });
2520
+ } catch (err) {
2521
+ const dur = Date.now() - t0;
2522
+ this.events?.emit("storage.write", {
2523
+ sessionId: "~memory~",
2524
+ store: "memory",
2525
+ filePath,
2526
+ operation: "clear",
2527
+ outcome: "failure",
2528
+ durationMs: dur,
2529
+ error: err instanceof Error ? err.message : String(err),
2530
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2531
+ });
2532
+ throw err;
2533
+ }
2134
2534
  this.events?.emit("memory.cleared", { scope });
2135
2535
  await this.mirrorBackup(scope);
2136
2536
  });
@@ -2138,15 +2538,54 @@ ${body.trim()}`);
2138
2538
  }
2139
2539
  await Promise.all(
2140
2540
  ["project-agents", "project-memory", "user-memory"].map(
2141
- (s) => this.runSerialized(s, async () => {
2142
- await this.backend.clear(s, this.files[s]);
2541
+ async (s) => this.runSerialized(s, async () => {
2542
+ const filePath = this.files[s];
2543
+ const t0 = Date.now();
2544
+ try {
2545
+ await this.backend.clear(s, filePath);
2546
+ const dur = Date.now() - t0;
2547
+ this.events?.emit("storage.write", {
2548
+ sessionId: "~memory~",
2549
+ store: "memory",
2550
+ filePath,
2551
+ operation: "clear",
2552
+ outcome: "success",
2553
+ durationMs: dur,
2554
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2555
+ });
2556
+ } catch (err) {
2557
+ const dur = Date.now() - t0;
2558
+ this.events?.emit("storage.write", {
2559
+ sessionId: "~memory~",
2560
+ store: "memory",
2561
+ filePath,
2562
+ operation: "clear",
2563
+ outcome: "failure",
2564
+ durationMs: dur,
2565
+ error: err instanceof Error ? err.message : String(err),
2566
+ ...this.traceId !== void 0 && { traceId: this.traceId }
2567
+ });
2568
+ throw err;
2569
+ }
2143
2570
  this.events?.emit("memory.cleared", { scope: s });
2144
2571
  await this.mirrorBackup(s);
2145
2572
  })
2146
2573
  )
2147
2574
  );
2148
2575
  }
2149
- /** Mirror current memory content to the persistent backup directory. */
2576
+ /**
2577
+ * Return a new MemoryStore proxy that carries `traceId` on every storage
2578
+ * event. The original store is left unchanged — callers that need a
2579
+ * trace-decorated view (e.g. session-run tools) receive the proxy while
2580
+ * the singleton remains trace-free for boot-time use.
2581
+ *
2582
+ * The proxy implements the full `MemoryStore` interface; all other
2583
+ * properties (backend, etc.) are delegated to the original store.
2584
+ */
2585
+ withTraceId(traceId) {
2586
+ this.traceId = traceId;
2587
+ return this;
2588
+ }
2150
2589
  async mirrorBackup(scope) {
2151
2590
  if (!this.persistBackup || scope === "project-agents") return;
2152
2591
  try {
@@ -2796,6 +3235,13 @@ var DEFAULT_SESSION_LOGGING_CONFIG = Object.freeze({
2796
3235
  });
2797
3236
 
2798
3237
  // src/storage/config-loader.ts
3238
+ function storageErrorString(err) {
3239
+ if (err instanceof Error) {
3240
+ const code = err.code;
3241
+ return code ? `${code}: ${err.message}` : err.message;
3242
+ }
3243
+ return String(err);
3244
+ }
2799
3245
  var BEHAVIOR_DEFAULTS = {
2800
3246
  version: 1,
2801
3247
  context: {
@@ -2898,11 +3344,15 @@ var DefaultConfigLoader = class {
2898
3344
  strict;
2899
3345
  vault;
2900
3346
  extraSources;
3347
+ events;
3348
+ traceId;
2901
3349
  constructor(opts) {
2902
3350
  this.paths = opts.paths;
2903
3351
  this.strict = opts.strict ?? false;
2904
3352
  this.vault = opts.vault;
2905
3353
  this.extraSources = opts.sources ?? [];
3354
+ this.events = opts.events;
3355
+ this.traceId = opts.traceId;
2906
3356
  }
2907
3357
  async load(opts = {}) {
2908
3358
  let cfg = { ...BEHAVIOR_DEFAULTS };
@@ -2979,7 +3429,33 @@ var DefaultConfigLoader = class {
2979
3429
  if (this.vault && toWrite.githubToken && !toWrite.githubToken.startsWith("enc:")) {
2980
3430
  toWrite = { ...toWrite, githubToken: this.vault.encrypt(toWrite.githubToken) };
2981
3431
  }
2982
- await atomicWrite(this.paths.syncConfig, JSON.stringify(toWrite, null, 2), { mode: 384 });
3432
+ const fp = this.paths.syncConfig;
3433
+ const t0 = Date.now();
3434
+ try {
3435
+ await atomicWrite(fp, JSON.stringify(toWrite, null, 2), { mode: 384 });
3436
+ this.events?.emit("storage.write", {
3437
+ sessionId: "~config~",
3438
+ store: "config",
3439
+ filePath: fp,
3440
+ operation: "persist_sync",
3441
+ outcome: "success",
3442
+ durationMs: Date.now() - t0,
3443
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
3444
+ });
3445
+ } catch (err) {
3446
+ this.events?.emit("storage.error", {
3447
+ sessionId: "~config~",
3448
+ store: "config",
3449
+ filePath: fp,
3450
+ operation: "persist_sync",
3451
+ outcome: "failure",
3452
+ error: storageErrorString(err),
3453
+ recoverable: false,
3454
+ durationMs: Date.now() - t0,
3455
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
3456
+ });
3457
+ throw err;
3458
+ }
2983
3459
  }
2984
3460
  /**
2985
3461
  * Read ~/.wrongstack/sync.json (encrypted GitHub token storage) and decrypt
@@ -2988,17 +3464,60 @@ var DefaultConfigLoader = class {
2988
3464
  * isolated — it should never be part of project-local or env-driven config.
2989
3465
  */
2990
3466
  async loadSyncConfig() {
3467
+ const fp = this.paths.syncConfig;
3468
+ const t0 = Date.now();
2991
3469
  try {
2992
- const raw = await fsp.readFile(this.paths.syncConfig, "utf8");
3470
+ const raw = await fsp.readFile(fp, "utf8");
2993
3471
  const parsed = safeParse(raw);
2994
- if (!parsed.ok || !parsed.value) return null;
3472
+ if (!parsed.ok || !parsed.value) {
3473
+ this.events?.emit("storage.read", {
3474
+ sessionId: "~config~",
3475
+ store: "config",
3476
+ filePath: fp,
3477
+ operation: "load_sync",
3478
+ outcome: "failure",
3479
+ durationMs: Date.now() - t0,
3480
+ error: "parse error or empty file",
3481
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
3482
+ });
3483
+ return null;
3484
+ }
2995
3485
  if (this.vault) {
2996
3486
  const decrypted = decryptConfigSecrets({ sync: parsed.value }, this.vault);
2997
- return decrypted.sync ?? null;
3487
+ const result = decrypted.sync ?? null;
3488
+ this.events?.emit("storage.read", {
3489
+ sessionId: "~config~",
3490
+ store: "config",
3491
+ filePath: fp,
3492
+ operation: "load_sync",
3493
+ outcome: "success",
3494
+ durationMs: Date.now() - t0,
3495
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
3496
+ });
3497
+ return result;
2998
3498
  }
3499
+ this.events?.emit("storage.read", {
3500
+ sessionId: "~config~",
3501
+ store: "config",
3502
+ filePath: fp,
3503
+ operation: "load_sync",
3504
+ outcome: "success",
3505
+ durationMs: Date.now() - t0,
3506
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
3507
+ });
2999
3508
  return parsed.value;
3000
3509
  } catch (err) {
3001
3510
  if (err.code === "ENOENT") return null;
3511
+ this.events?.emit("storage.read", {
3512
+ sessionId: "~config~",
3513
+ store: "config",
3514
+ filePath: fp,
3515
+ operation: "load_sync",
3516
+ outcome: "failure",
3517
+ durationMs: Date.now() - t0,
3518
+ error: storageErrorString(err),
3519
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
3520
+ });
3002
3521
  console.warn(JSON.stringify({
3003
3522
  level: "warn",
3004
3523
  event: "config.sync_load_failed",
@@ -3010,10 +3529,21 @@ var DefaultConfigLoader = class {
3010
3529
  }
3011
3530
  async readJson(file) {
3012
3531
  let raw;
3532
+ const t0 = Date.now();
3013
3533
  try {
3014
3534
  raw = await fsp.readFile(file, "utf8");
3015
3535
  } catch (err) {
3016
3536
  if (err.code !== "ENOENT") {
3537
+ this.events?.emit("storage.read", {
3538
+ sessionId: "~config~",
3539
+ store: "config",
3540
+ filePath: file,
3541
+ operation: "read_json",
3542
+ outcome: "failure",
3543
+ durationMs: Date.now() - t0,
3544
+ error: storageErrorString(err),
3545
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
3546
+ });
3017
3547
  console.warn(JSON.stringify({
3018
3548
  level: "warn",
3019
3549
  event: "config.read_failed",
@@ -3026,6 +3556,16 @@ var DefaultConfigLoader = class {
3026
3556
  }
3027
3557
  const parsed = safeParse(raw);
3028
3558
  if (!parsed.ok || !parsed.value) {
3559
+ this.events?.emit("storage.read", {
3560
+ sessionId: "~config~",
3561
+ store: "config",
3562
+ filePath: file,
3563
+ operation: "read_json",
3564
+ outcome: "failure",
3565
+ durationMs: Date.now() - t0,
3566
+ error: "parse error or empty file",
3567
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
3568
+ });
3029
3569
  console.warn(JSON.stringify({
3030
3570
  level: "warn",
3031
3571
  event: "config.parse_failed",
@@ -3813,24 +4353,66 @@ function resolveSessionLoggingConfig(cfg) {
3813
4353
 
3814
4354
  // src/storage/todos-checkpoint.ts
3815
4355
  init_atomic_write();
3816
- async function loadTodosCheckpoint(filePath) {
4356
+ async function loadTodosCheckpoint(filePath, events, traceId) {
4357
+ const t0 = Date.now();
3817
4358
  let raw;
3818
4359
  try {
3819
4360
  raw = await fsp.readFile(filePath, "utf8");
3820
- } catch {
4361
+ } catch (err) {
4362
+ events?.emit("storage.error", {
4363
+ sessionId: traceId ?? "~boot~",
4364
+ store: "todos",
4365
+ filePath,
4366
+ operation: "load",
4367
+ outcome: "failure",
4368
+ error: err instanceof Error ? err.message : String(err),
4369
+ recoverable: true
4370
+ });
3821
4371
  return null;
3822
4372
  }
3823
4373
  try {
3824
4374
  const parsed = JSON.parse(raw);
3825
- if (parsed?.version !== 1 || !Array.isArray(parsed.todos)) return null;
4375
+ if (parsed?.version !== 1 || !Array.isArray(parsed.todos)) {
4376
+ events?.emit("storage.read", {
4377
+ sessionId: traceId ?? "~boot~",
4378
+ store: "todos",
4379
+ filePath,
4380
+ operation: "load",
4381
+ outcome: "failure",
4382
+ durationMs: Date.now() - t0,
4383
+ error: "invalid_schema",
4384
+ ...traceId !== void 0 && { traceId }
4385
+ });
4386
+ return null;
4387
+ }
4388
+ events?.emit("storage.read", {
4389
+ sessionId: traceId ?? "~boot~",
4390
+ store: "todos",
4391
+ filePath,
4392
+ operation: "load",
4393
+ outcome: "success",
4394
+ durationMs: Date.now() - t0,
4395
+ ...traceId !== void 0 && { traceId }
4396
+ });
3826
4397
  return parsed.todos.filter(
3827
4398
  (t) => !!t && typeof t.id === "string" && typeof t.content === "string" && typeof t.status === "string" && (t.activeForm === void 0 || typeof t.activeForm === "string")
3828
4399
  );
3829
4400
  } catch {
4401
+ events?.emit("storage.read", {
4402
+ sessionId: traceId ?? "~boot~",
4403
+ store: "todos",
4404
+ filePath,
4405
+ operation: "load",
4406
+ outcome: "failure",
4407
+ durationMs: Date.now() - t0,
4408
+ error: "parse_failed",
4409
+ ...traceId !== void 0 && { traceId }
4410
+ });
3830
4411
  return null;
3831
4412
  }
3832
4413
  }
3833
- async function saveTodosCheckpoint(filePath, sessionId, todos) {
4414
+ async function saveTodosCheckpoint(filePath, sessionId, todos, events, traceId) {
4415
+ const t0 = Date.now();
3834
4416
  const payload = {
3835
4417
  version: 1,
3836
4418
  sessionId,
@@ -3839,7 +4421,25 @@ async function saveTodosCheckpoint(filePath, sessionId, todos) {
3839
4421
  };
3840
4422
  try {
3841
4423
  await atomicWrite(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
4424
+ events?.emit("storage.write", {
4425
+ sessionId: traceId ?? sessionId,
4426
+ store: "todos",
4427
+ filePath,
4428
+ operation: "save",
4429
+ outcome: "success",
4430
+ durationMs: Date.now() - t0,
4431
+ ...traceId !== void 0 && { traceId }
4432
+ });
3842
4433
  } catch (err) {
4434
+ events?.emit("storage.error", {
4435
+ sessionId: traceId ?? sessionId,
4436
+ store: "todos",
4437
+ filePath,
4438
+ operation: "save",
4439
+ outcome: "failure",
4440
+ error: err instanceof Error ? err.message : String(err),
4441
+ recoverable: false
4442
+ });
3843
4443
  console.warn(JSON.stringify({
3844
4444
  level: "warn",
3845
4445
  event: "todos_checkpoint.save_failed",
@@ -3848,12 +4448,12 @@ async function saveTodosCheckpoint(filePath, sessionId, todos) {
3848
4448
  }));
3849
4449
  }
3850
4450
  }
3851
- function attachTodosCheckpoint(state, filePath, sessionId) {
4451
+ function attachTodosCheckpoint(state, filePath, sessionId, events, traceId) {
3852
4452
  let timer = null;
3853
4453
  let pending = null;
3854
4454
  let writeChain = Promise.resolve();
3855
4455
  const enqueueWrite = (todos) => {
3856
- writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos)).catch((err) => {
4456
+ writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos, events, traceId)).catch((err) => {
3857
4457
  const msg = err instanceof Error ? err.message : String(err);
3858
4458
  console.error(JSON.stringify({
3859
4459
  level: "error",
@@ -3895,25 +4495,79 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
3895
4495
 
3896
4496
  // src/storage/plan-store.ts
3897
4497
  init_atomic_write();
3898
- async function loadPlan(filePath) {
4498
+ async function loadPlan(filePath, events) {
4499
+ const t0 = Date.now();
3899
4500
  let raw;
3900
4501
  try {
3901
4502
  raw = await fsp.readFile(filePath, "utf8");
3902
- } catch {
4503
+ } catch (err) {
4504
+ events?.emit("storage.error", {
4505
+ sessionId: "~boot~",
4506
+ store: "plan",
4507
+ filePath,
4508
+ operation: "load",
4509
+ error: err instanceof Error ? err.message : String(err),
4510
+ recoverable: true
4511
+ });
3903
4512
  return null;
3904
4513
  }
3905
4514
  try {
3906
4515
  const parsed = JSON.parse(raw);
3907
- if (parsed?.version !== 1 || !Array.isArray(parsed.items)) return null;
4516
+ if (parsed?.version !== 1 || !Array.isArray(parsed.items)) {
4517
+ events?.emit("storage.read", {
4518
+ sessionId: "~boot~",
4519
+ store: "plan",
4520
+ filePath,
4521
+ operation: "load",
4522
+ outcome: "failure",
4523
+ durationMs: Date.now() - t0,
4524
+ error: "invalid_schema"
4525
+ });
4526
+ return null;
4527
+ }
4528
+ events?.emit("storage.read", {
4529
+ sessionId: "~boot~",
4530
+ store: "plan",
4531
+ filePath,
4532
+ operation: "load",
4533
+ outcome: "success",
4534
+ durationMs: Date.now() - t0
4535
+ });
3908
4536
  return parsed;
3909
4537
  } catch {
4538
+ events?.emit("storage.read", {
4539
+ sessionId: "~boot~",
4540
+ store: "plan",
4541
+ filePath,
4542
+ operation: "load",
4543
+ outcome: "failure",
4544
+ durationMs: Date.now() - t0,
4545
+ error: "parse_failed"
4546
+ });
3910
4547
  return null;
3911
4548
  }
3912
4549
  }
3913
- async function savePlan(filePath, plan) {
4550
+ async function savePlan(filePath, plan, events) {
4551
+ const t0 = Date.now();
3914
4552
  try {
3915
4553
  await atomicWrite(filePath, JSON.stringify(plan, null, 2), { mode: 384 });
4554
+ events?.emit("storage.write", {
4555
+ sessionId: "~boot~",
4556
+ store: "plan",
4557
+ filePath,
4558
+ operation: "save",
4559
+ outcome: "success",
4560
+ durationMs: Date.now() - t0
4561
+ });
3916
4562
  } catch (err) {
4563
+ events?.emit("storage.error", {
4564
+ sessionId: "~boot~",
4565
+ store: "plan",
4566
+ filePath,
4567
+ operation: "save",
4568
+ error: err instanceof Error ? err.message : String(err),
4569
+ recoverable: false
4570
+ });
3917
4571
  console.warn(
3918
4572
  "[plan-store] save failed:",
3919
4573
  err instanceof Error ? err.message : String(err)
@@ -4493,6 +5147,13 @@ function hasDangerousCapabilityForSubagents(toolOrCaps) {
4493
5147
  const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
4494
5148
  return caps.some((c) => DANGEROUS_FOR_SUBAGENTS.includes(c));
4495
5149
  }
5150
+ function hasCapability(toolOrCaps, capability) {
5151
+ if (!toolOrCaps) return false;
5152
+ const input = toolOrCaps;
5153
+ const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
5154
+ const toCheck = Array.isArray(capability) ? capability : [capability];
5155
+ return toCheck.some((c) => caps.includes(c));
5156
+ }
4496
5157
  function getDangerousCapabilities(toolOrCaps) {
4497
5158
  if (!toolOrCaps) return [];
4498
5159
  const input = toolOrCaps;
@@ -4856,7 +5517,16 @@ var DefaultPermissionPolicy = class {
4856
5517
  };
4857
5518
  }
4858
5519
  }
4859
- if (tool.permission === "auto" && !tool.mutating) {
5520
+ const hasWriteCap = hasCapability(tool, ToolCapabilities.FS_WRITE);
5521
+ const hasShellCap = hasCapability(tool, [
5522
+ ToolCapabilities.SHELL_ARBITRARY,
5523
+ ToolCapabilities.SHELL_RESTRICTED
5524
+ ]);
5525
+ const hasInstallCap = hasCapability(tool, ToolCapabilities.PACKAGE_INSTALL);
5526
+ const hasConfigCap = hasCapability(tool, ToolCapabilities.CONFIG_MUTATE);
5527
+ const hasSubagentCap = hasCapability(tool, ToolCapabilities.SUBAGENT_SPAWN);
5528
+ const isMutating = tool.mutating || hasWriteCap || hasShellCap || hasInstallCap || hasConfigCap || hasSubagentCap;
5529
+ if (tool.permission === "auto" && !isMutating) {
4860
5530
  const decision = { permission: "auto", source: "default" };
4861
5531
  this._evalCache.set(cacheKey, decision);
4862
5532
  return decision;
@@ -4875,7 +5545,27 @@ var DefaultPermissionPolicy = class {
4875
5545
  }
4876
5546
  return { permission: "confirm", source: "default" };
4877
5547
  }
5548
+ // Capability-based destructive check (preferred over name-based)
5549
+ isDestructiveByCapability(tool) {
5550
+ const caps = tool.capabilities ?? [];
5551
+ if (caps.includes("shell.arbitrary")) return true;
5552
+ if (caps.includes("fs.write")) return true;
5553
+ if (caps.includes("fs.write.outside-project")) return true;
5554
+ return false;
5555
+ }
4878
5556
  isDestructiveYoloCall(tool, input, ctx) {
5557
+ if (this.isDestructiveByCapability(tool)) {
5558
+ if (tool.name === "bash") {
5559
+ const command = getInputString(input, "command");
5560
+ return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) : true;
5561
+ }
5562
+ if (tool.name === "write" || tool.name === "edit" || tool.name === "replace" || tool.name === "patch") {
5563
+ const targetPath = getInputString(input, "path") ?? getInputString(input, "file");
5564
+ if (!targetPath || !ctx.projectRoot) return false;
5565
+ return !pathLooksInsideProject(targetPath, ctx.projectRoot);
5566
+ }
5567
+ return true;
5568
+ }
4879
5569
  if (tool.name === "bash") {
4880
5570
  const command = getInputString(input, "command");
4881
5571
  return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) : true;
@@ -5227,6 +5917,8 @@ function compactSkillBody(body) {
5227
5917
  var DefaultSkillLoader = class {
5228
5918
  dirs;
5229
5919
  cache;
5920
+ entriesCache;
5921
+ bodyCache = /* @__PURE__ */ new Map();
5230
5922
  constructor(opts) {
5231
5923
  this.dirs = [
5232
5924
  { dir: opts.paths.inProjectSkills, source: "project" },
@@ -5285,6 +5977,7 @@ var DefaultSkillLoader = class {
5285
5977
  return lines.join("\n");
5286
5978
  }
5287
5979
  async listEntries() {
5980
+ if (this.entriesCache) return this.entriesCache;
5288
5981
  const skills = await this.list();
5289
5982
  const entries = [];
5290
5983
  for (const s of skills) {
@@ -5295,33 +5988,47 @@ var DefaultSkillLoader = class {
5295
5988
  } catch {
5296
5989
  }
5297
5990
  }
5991
+ this.entriesCache = entries;
5298
5992
  return entries;
5299
5993
  }
5300
5994
  invalidateCache() {
5301
5995
  this.cache = void 0;
5996
+ this.entriesCache = void 0;
5997
+ this.bodyCache.clear();
5302
5998
  }
5303
5999
  async readBody(name) {
6000
+ const cached = this.bodyCache.get(name);
6001
+ if (cached !== void 0) return cached;
5304
6002
  const m = await this.find(name);
5305
6003
  if (!m) throw new Error(`Skill "${name}" not found`);
5306
- return fsp.readFile(m.path, "utf8");
6004
+ const body = await fsp.readFile(m.path, "utf8");
6005
+ this.bodyCache.set(name, body);
6006
+ return body;
5307
6007
  }
5308
6008
  async readSaveBody(name) {
6009
+ const key = `save:${name}`;
6010
+ const cached = this.bodyCache.get(key);
6011
+ if (cached !== void 0) return cached;
5309
6012
  const m = await this.find(name);
5310
6013
  if (!m) throw new Error(`Skill "${name}" not found`);
5311
6014
  const savePath = path11.join(path11.dirname(m.path), "SKILL.save.md");
6015
+ let result;
5312
6016
  try {
5313
- return await fsp.readFile(savePath, "utf8");
6017
+ result = await fsp.readFile(savePath, "utf8");
5314
6018
  } catch {
5315
6019
  const full = await fsp.readFile(m.path, "utf8");
5316
6020
  const body = stripFrontmatter(full);
5317
6021
  const compact = compactSkillBody(body);
5318
6022
  if (compact) {
5319
- return `## Overview
6023
+ result = `## Overview
5320
6024
 
5321
6025
  ${compact}`;
6026
+ } else {
6027
+ result = body.trim().slice(0, 300);
5322
6028
  }
5323
- return body.trim().slice(0, 300);
5324
6029
  }
6030
+ this.bodyCache.set(key, result);
6031
+ return result;
5325
6032
  }
5326
6033
  };
5327
6034
  function parseFrontmatter(raw) {
@@ -8069,13 +8776,32 @@ var MAX_JOURNAL_ENTRIES = 500;
8069
8776
  function goalFilePath(projectRoot) {
8070
8777
  return resolveWstackPaths({ projectRoot }).projectGoal;
8071
8778
  }
8072
- async function loadGoal(filePath) {
8779
+ async function loadGoal(filePath, events) {
8780
+ const t0 = Date.now();
8073
8781
  let raw;
8074
8782
  try {
8075
8783
  raw = await fsp.readFile(filePath, "utf8");
8076
8784
  } catch (err) {
8077
8785
  const code = err.code;
8078
- if (code === "ENOENT") return null;
8786
+ if (code === "ENOENT") {
8787
+ events?.emit("storage.read", {
8788
+ sessionId: "~boot~",
8789
+ store: "goal",
8790
+ filePath,
8791
+ operation: "load",
8792
+ outcome: "success",
8793
+ durationMs: Date.now() - t0
8794
+ });
8795
+ return null;
8796
+ }
8797
+ events?.emit("storage.error", {
8798
+ sessionId: "~boot~",
8799
+ store: "goal",
8800
+ filePath,
8801
+ operation: "load",
8802
+ error: err instanceof Error ? err.message : String(err),
8803
+ recoverable: false
8804
+ });
8079
8805
  throw err;
8080
8806
  }
8081
8807
  try {
@@ -8088,8 +8814,25 @@ async function loadGoal(filePath) {
8088
8814
  message: "invalid schema \u2014 consider deleting and re-creating",
8089
8815
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
8090
8816
  }));
8817
+ events?.emit("storage.read", {
8818
+ sessionId: "~boot~",
8819
+ store: "goal",
8820
+ filePath,
8821
+ operation: "load",
8822
+ outcome: "failure",
8823
+ durationMs: Date.now() - t0,
8824
+ error: "invalid_schema"
8825
+ });
8091
8826
  return null;
8092
8827
  }
8828
+ events?.emit("storage.read", {
8829
+ sessionId: "~boot~",
8830
+ store: "goal",
8831
+ filePath,
8832
+ operation: "load",
8833
+ outcome: "success",
8834
+ durationMs: Date.now() - t0
8835
+ });
8093
8836
  return parsed;
8094
8837
  } catch {
8095
8838
  console.warn(JSON.stringify({
@@ -8099,13 +8842,39 @@ async function loadGoal(filePath) {
8099
8842
  message: "JSON parse failed \u2014 consider deleting and re-creating",
8100
8843
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
8101
8844
  }));
8845
+ events?.emit("storage.read", {
8846
+ sessionId: "~boot~",
8847
+ store: "goal",
8848
+ filePath,
8849
+ operation: "load",
8850
+ outcome: "failure",
8851
+ durationMs: Date.now() - t0,
8852
+ error: "parse_failed"
8853
+ });
8102
8854
  return null;
8103
8855
  }
8104
8856
  }
8105
- async function saveGoal(filePath, goal) {
8857
+ async function saveGoal(filePath, goal, events) {
8858
+ const t0 = Date.now();
8106
8859
  try {
8107
8860
  await atomicWrite(filePath, JSON.stringify(goal, null, 2), { mode: 384 });
8861
+ events?.emit("storage.write", {
8862
+ sessionId: "~boot~",
8863
+ store: "goal",
8864
+ filePath,
8865
+ operation: "save",
8866
+ outcome: "success",
8867
+ durationMs: Date.now() - t0
8868
+ });
8108
8869
  } catch (err) {
8870
+ events?.emit("storage.error", {
8871
+ sessionId: "~boot~",
8872
+ store: "goal",
8873
+ filePath,
8874
+ operation: "save",
8875
+ error: err instanceof Error ? err.message : String(err),
8876
+ recoverable: false
8877
+ });
8109
8878
  throw new FsError({
8110
8879
  message: err instanceof Error ? err.message : String(err),
8111
8880
  code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
@@ -8275,7 +9044,7 @@ var EternalAutonomyEngine = class {
8275
9044
  const emit = (stage) => {
8276
9045
  this.opts.onStage?.(stage);
8277
9046
  };
8278
- const goal = await loadGoal(this.goalPath);
9047
+ const goal = await loadGoal(this.goalPath, this.opts.events);
8279
9048
  if (!goal) {
8280
9049
  emit({ phase: "stopped" });
8281
9050
  this.stopRequested = true;
@@ -8379,7 +9148,7 @@ var EternalAutonomyEngine = class {
8379
9148
  emit({ phase: "reflect", status, note });
8380
9149
  let iterationIndex = 0;
8381
9150
  try {
8382
- const reloaded = await loadGoal(this.goalPath);
9151
+ const reloaded = await loadGoal(this.goalPath, this.opts.events);
8383
9152
  iterationIndex = reloaded?.iterations ?? 0;
8384
9153
  } catch {
8385
9154
  }
@@ -8699,12 +9468,12 @@ ${recentJournal}` : "No prior iterations.",
8699
9468
  }
8700
9469
  }
8701
9470
  async appendIterationEntry(entry) {
8702
- const current = await loadGoal(this.goalPath);
9471
+ const current = await loadGoal(this.goalPath, this.opts.events);
8703
9472
  if (!current) {
8704
9473
  return;
8705
9474
  }
8706
9475
  const updated = appendJournal(current, entry);
8707
- await saveGoal(this.goalPath, updated);
9476
+ await saveGoal(this.goalPath, updated, this.opts.events);
8708
9477
  }
8709
9478
  /**
8710
9479
  * Persistent per-todo failure counter. Skipped silently when the goal
@@ -8713,11 +9482,11 @@ ${recentJournal}` : "No prior iterations.",
8713
9482
  * the counter to rotate past stuck todos once they cross `todoMaxAttempts`.
8714
9483
  */
8715
9484
  async bumpTodoAttempt(todoId) {
8716
- const current = await loadGoal(this.goalPath);
9485
+ const current = await loadGoal(this.goalPath, this.opts.events);
8717
9486
  if (!current) return;
8718
9487
  const attempts = { ...current.todoAttempts ?? {} };
8719
9488
  attempts[todoId] = (attempts[todoId] ?? 0) + 1;
8720
- await saveGoal(this.goalPath, { ...current, todoAttempts: attempts });
9489
+ await saveGoal(this.goalPath, { ...current, todoAttempts: attempts }, this.opts.events);
8721
9490
  }
8722
9491
  /**
8723
9492
  * Flip the mission to `completed` and journal it. Called from two
@@ -8727,7 +9496,7 @@ ${recentJournal}` : "No prior iterations.",
8727
9496
  * goal is already `completed`.
8728
9497
  */
8729
9498
  async markGoalCompleted(action, note) {
8730
- const current = await loadGoal(this.goalPath);
9499
+ const current = await loadGoal(this.goalPath, this.opts.events);
8731
9500
  if (!current) return;
8732
9501
  if (current.goalState === "completed") return;
8733
9502
  const withFlag = { ...current, goalState: "completed" };
@@ -8737,7 +9506,7 @@ ${recentJournal}` : "No prior iterations.",
8737
9506
  status: "success",
8738
9507
  note: note.slice(0, 240)
8739
9508
  });
8740
- await saveGoal(this.goalPath, withEntry);
9509
+ await saveGoal(this.goalPath, withEntry, this.opts.events);
8741
9510
  this.opts.onEternalStop?.();
8742
9511
  }
8743
9512
  /**
@@ -8746,10 +9515,10 @@ ${recentJournal}` : "No prior iterations.",
8746
9515
  * `onEternalStop` so the REPL returns to normal mode.
8747
9516
  */
8748
9517
  async clearGoalManually(note) {
8749
- const current = await loadGoal(this.goalPath);
9518
+ const current = await loadGoal(this.goalPath, this.opts.events);
8750
9519
  if (current) {
8751
9520
  const abandoned = { ...current, goalState: "abandoned" };
8752
- await saveGoal(this.goalPath, abandoned);
9521
+ await saveGoal(this.goalPath, abandoned, this.opts.events);
8753
9522
  }
8754
9523
  try {
8755
9524
  const { unlink: unlink10 } = await import('fs/promises');
@@ -8825,16 +9594,16 @@ ${recentJournal}` : ""
8825
9594
  * Persist a progress update from the agent's [PROGRESS: N%] output.
8826
9595
  */
8827
9596
  async updateProgress(progress, note) {
8828
- const current = await loadGoal(this.goalPath);
9597
+ const current = await loadGoal(this.goalPath, this.opts.events);
8829
9598
  if (!current) return;
8830
9599
  const updated = recordProgress(current, progress, note);
8831
- await saveGoal(this.goalPath, updated);
9600
+ await saveGoal(this.goalPath, updated, this.opts.events);
8832
9601
  }
8833
9602
  async persistEngineState(state) {
8834
- const current = await loadGoal(this.goalPath);
9603
+ const current = await loadGoal(this.goalPath, this.opts.events);
8835
9604
  if (!current) return;
8836
9605
  if (current.engineState === state) return;
8837
- await saveGoal(this.goalPath, { ...current, engineState: state });
9606
+ await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
8838
9607
  }
8839
9608
  };
8840
9609
 
@@ -9183,6 +9952,20 @@ var SubagentBudget = class _SubagentBudget {
9183
9952
  };
9184
9953
 
9185
9954
  // src/coordination/agent-subagent-runner.ts
9955
+ function withDisabledToolFiltering(factory) {
9956
+ return async (config) => {
9957
+ const result = await factory(config);
9958
+ const disabled = config.disabledTools ?? [];
9959
+ if (disabled.length === 0) return result;
9960
+ const registry = result.agent.tools;
9961
+ if (registry && typeof registry.unregister === "function") {
9962
+ for (const toolName of disabled) {
9963
+ registry.unregister(toolName);
9964
+ }
9965
+ }
9966
+ return result;
9967
+ };
9968
+ }
9186
9969
  function makeAgentSubagentRunner(opts) {
9187
9970
  const format = opts.formatTaskInput ?? defaultFormatTaskInput;
9188
9971
  return async (task, ctx) => {
@@ -12006,17 +12789,17 @@ function normalize(text) {
12006
12789
  return ` ${text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim()} `;
12007
12790
  }
12008
12791
  function scoreAgents(task, catalog = AGENT_CATALOG) {
12009
- const hay = normalize(task);
12792
+ const haySet = new Set(normalize(task).split(/\s+/).filter(Boolean));
12010
12793
  const out = [];
12011
12794
  for (const def of Object.values(catalog)) {
12012
12795
  if (!def?.config?.role) continue;
12013
12796
  let score = 0;
12014
12797
  const matched = [];
12015
12798
  for (const kw of def.capability.keywords) {
12016
- const needle = normalize(kw);
12017
- if (hay.includes(needle.trimEnd() + " ") || hay.includes(" " + needle.trimStart())) {
12018
- const words = kw.trim().split(/\s+/).length;
12019
- score += words;
12799
+ const needleWords = normalize(kw).split(/\s+/).filter(Boolean);
12800
+ const allPresent = needleWords.every((w) => haySet.has(w));
12801
+ if (allPresent) {
12802
+ score += needleWords.length;
12020
12803
  matched.push(kw);
12021
12804
  }
12022
12805
  }
@@ -13349,7 +14132,8 @@ var ParallelEternalEngine = class {
13349
14132
  doneCondition: { type: "all_tasks_done" }
13350
14133
  };
13351
14134
  this.coordinator = new DefaultMultiAgentCoordinator(config);
13352
- const runner = makeAgentSubagentRunner({ factory: this.agentFactory });
14135
+ const filteredFactory = withDisabledToolFiltering(this.agentFactory);
14136
+ const runner = makeAgentSubagentRunner({ factory: filteredFactory });
13353
14137
  this.coordinator.setRunner?.(runner);
13354
14138
  try {
13355
14139
  while (!this.stopRequested) {
@@ -13384,7 +14168,7 @@ var ParallelEternalEngine = class {
13384
14168
  this.opts.onStage?.(stage);
13385
14169
  };
13386
14170
  this.iterations++;
13387
- const goal = await loadGoal(this.goalPath);
14171
+ const goal = await loadGoal(this.goalPath, this.opts.events);
13388
14172
  if (!goal) {
13389
14173
  this.stopRequested = true;
13390
14174
  emit({ phase: "stopped" });
@@ -13402,7 +14186,8 @@ var ParallelEternalEngine = class {
13402
14186
  doneCondition: { type: "all_tasks_done" }
13403
14187
  };
13404
14188
  this.coordinator = new DefaultMultiAgentCoordinator(config);
13405
- const runner = makeAgentSubagentRunner({ factory: this.agentFactory });
14189
+ const filteredFactory = withDisabledToolFiltering(this.agentFactory);
14190
+ const runner = makeAgentSubagentRunner({ factory: filteredFactory });
13406
14191
  this.coordinator.setRunner?.(runner);
13407
14192
  }
13408
14193
  emit({ phase: "decompose" });
@@ -13511,13 +14296,17 @@ ${personaLine}Task: ${task}
13511
14296
  role: route.role,
13512
14297
  tools: route.definition.config.tools,
13513
14298
  systemPromptOverride: route.definition.config.prompt,
13514
- timeoutMs: this.timeoutMs
14299
+ timeoutMs: this.timeoutMs,
14300
+ // Disable delegation — subagents are leaf workers, not orchestrators
14301
+ disabledTools: ["delegate"]
13515
14302
  } : {
13516
14303
  id: subagentId,
13517
14304
  name: `slot-${subagentId.slice(-6)}`,
13518
14305
  // Let the coordinator apply its default budget (roster or generic).
13519
14306
  // Hardcoding low limits here defeats the x10 budget improvement.
13520
- timeoutMs: this.timeoutMs
14307
+ timeoutMs: this.timeoutMs,
14308
+ // Disable delegation — subagents are leaf workers, not orchestrators
14309
+ disabledTools: ["delegate"]
13521
14310
  }
13522
14311
  );
13523
14312
  subagentIds.push(subagentId);
@@ -13663,10 +14452,10 @@ ${lastFew}` : "No prior iterations.",
13663
14452
  // Helpers
13664
14453
  // -------------------------------------------------------------------------
13665
14454
  async appendIterationEntry(entry) {
13666
- const current = await loadGoal(this.goalPath);
14455
+ const current = await loadGoal(this.goalPath, this.opts.events);
13667
14456
  if (!current) return;
13668
14457
  const updated = appendJournal(current, entry);
13669
- await saveGoal(this.goalPath, updated);
14458
+ await saveGoal(this.goalPath, updated, this.opts.events);
13670
14459
  const entryWithMeta = {
13671
14460
  at: (this.opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
13672
14461
  iteration: updated.iterations,
@@ -13678,10 +14467,10 @@ ${lastFew}` : "No prior iterations.",
13678
14467
  await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
13679
14468
  }
13680
14469
  async persistState(state) {
13681
- const current = await loadGoal(this.goalPath);
14470
+ const current = await loadGoal(this.goalPath, this.opts.events);
13682
14471
  if (!current) return;
13683
14472
  if (current.engineState === state) return;
13684
- await saveGoal(this.goalPath, { ...current, engineState: state });
14473
+ await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
13685
14474
  }
13686
14475
  };
13687
14476
 
@@ -14707,6 +15496,7 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
14707
15496
  }
14708
15497
  for (const dispose of this.disposers) dispose();
14709
15498
  this.disposers.length = 0;
15499
+ this.snapshot.files.length = 0;
14710
15500
  }
14711
15501
  };
14712
15502
 
@@ -14740,7 +15530,12 @@ Working rules:
14740
15530
  6. Never claim a subagent's work as your own without verifying it. If a
14741
15531
  result looks wrong, ask_subagent for clarification before passing it
14742
15532
  to the user.
14743
- 7. Wind down when satisfied. When the results are good enough, call
15533
+ 7. **Act on subagent mail immediately**. Subagent messages (result, ask,
15534
+ assign, note) are injected inline before every step \u2014 even mid-task.
15535
+ When you see one, address it before continuing: reply to asks, factor
15536
+ in results, act on assignments. Use \`mailbox action=ack\` to mark
15537
+ completed messages.
15538
+ 8. Wind down when satisfied. When the results are good enough, call
14744
15539
  work_complete \u2014 no new subagents will spawn and queued tasks complete
14745
15540
  as aborted. Running subagents finish naturally. Call terminate_subagent
14746
15541
  only for ones you need to stop immediately.`;
@@ -14757,6 +15552,13 @@ Bridge contract:
14757
15552
  structured, and self-contained \u2014 assume the Director will paste your
14758
15553
  output into its own context.
14759
15554
 
15555
+ CRITICAL CONSTRAINT \u2014 NO FURTHER DELEGATION:
15556
+ - You MUST NOT call the \`delegate\` tool or attempt to spawn subagents.
15557
+ - You MUST NOT use \`spawn_subagent\`, \`assign_task\`, or any equivalent.
15558
+ - Your role is to execute the assigned task yourself, not to orchestrate.
15559
+ - If a subtask is too complex, report back to the Director with what you
15560
+ found and let the Director decide how to decompose.
15561
+
14760
15562
  Inter-agent mailbox (if you have the \`mail_send\`/\`mail_inbox\`/\`mailbox\` tools):
14761
15563
  - You are part of a project-wide fleet that may span other terminals and
14762
15564
  WebUIs. Your mailbox identity is \`<your-name>@<session-tag>\` (unique
@@ -14771,7 +15573,12 @@ Inter-agent mailbox (if you have the \`mail_send\`/\`mail_inbox\`/\`mailbox\` to
14771
15573
  their exact id instead of doing everything yourself. Discover ids with
14772
15574
  \`mailbox action=online\`.
14773
15575
  - Answer your mail: reply to the sender's exact \`from\` id. When done with
14774
- an assigned task, post a \`result\` back to whoever assigned it.`;
15576
+ an assigned task, post a \`result\` back to whoever assigned it.
15577
+ - **Mail to the leader is always seen**: when you send \`ask\`, \`result\`,
15578
+ or \`assign\` to the director/leader, the message is injected inline into
15579
+ the leader's conversation before their next step \u2014 even if the leader is
15580
+ mid-task. Use \`mail_send\` to reliably reach the leader instead of
15581
+ waiting for them to check in.`;
14775
15582
  function composeDirectorPrompt(parts = {}) {
14776
15583
  const sections = [];
14777
15584
  const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
@@ -17168,6 +17975,7 @@ async function readSubagentPartial(opts, subagentId) {
17168
17975
  }
17169
17976
  function makeDirectorSessionFactory(opts) {
17170
17977
  const runId = opts.directorRunId ?? `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-director`;
17978
+ const { traceId } = opts;
17171
17979
  let store;
17172
17980
  let dir;
17173
17981
  if (opts.store) {
@@ -17183,12 +17991,16 @@ function makeDirectorSessionFactory(opts) {
17183
17991
  dir,
17184
17992
  directorRunId: runId,
17185
17993
  async createSubagentSession({ subagentId, provider, model, title }) {
17186
- return store.create({
17994
+ const writer = await store.create({
17187
17995
  id: subagentId,
17188
17996
  title: title ?? subagentId,
17189
17997
  provider: provider ?? "unknown",
17190
17998
  model: model ?? "unknown"
17191
17999
  });
18000
+ if (traceId !== void 0) {
18001
+ writer.traceId = traceId;
18002
+ }
18003
+ return writer;
17192
18004
  }
17193
18005
  };
17194
18006
  }
@@ -20632,7 +21444,9 @@ var SddParallelRun = class {
20632
21444
  doneCondition: { type: "all_tasks_done" }
20633
21445
  };
20634
21446
  this.coordinator = new DefaultMultiAgentCoordinator(config);
20635
- const runner = makeAgentSubagentRunner({ factory: this.opts.subagentFactory ?? this.defaultFactory() });
21447
+ const baseFactory = this.opts.subagentFactory ?? this.defaultFactory();
21448
+ const filteredFactory = withDisabledToolFiltering(baseFactory);
21449
+ const runner = makeAgentSubagentRunner({ factory: filteredFactory });
20636
21450
  this.coordinator.setRunner?.(runner);
20637
21451
  }
20638
21452
  defaultFactory() {
@@ -20674,7 +21488,9 @@ var SddParallelRun = class {
20674
21488
  id: subagentId,
20675
21489
  name: subagentId,
20676
21490
  role: "executor",
20677
- timeoutMs: this.timeoutMs
21491
+ timeoutMs: this.timeoutMs,
21492
+ // Disable delegation — subagents are leaf workers, not orchestrators
21493
+ disabledTools: ["delegate"]
20678
21494
  })
20679
21495
  );
20680
21496
  const spawnResults = await Promise.all(spawns);