@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
package/dist/index.js CHANGED
@@ -7,6 +7,8 @@ import { join, extname, relative, isAbsolute, resolve, sep } from 'path';
7
7
  import * as fs2 from 'fs';
8
8
  import * as os6 from 'os';
9
9
  import { hostname } from 'os';
10
+ import * as dns from 'dns/promises';
11
+ import * as net from 'net';
10
12
  import { execFile, spawn } from 'child_process';
11
13
  import { promisify } from 'util';
12
14
  import { EventEmitter } from 'events';
@@ -4277,6 +4279,13 @@ function hasDangerousCapabilityForSubagents(toolOrCaps) {
4277
4279
  const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
4278
4280
  return caps.some((c) => DANGEROUS_FOR_SUBAGENTS.includes(c));
4279
4281
  }
4282
+ function hasCapability(toolOrCaps, capability) {
4283
+ if (!toolOrCaps) return false;
4284
+ const input = toolOrCaps;
4285
+ const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
4286
+ const toCheck = Array.isArray(capability) ? capability : [capability];
4287
+ return toCheck.some((c) => caps.includes(c));
4288
+ }
4280
4289
  function getDangerousCapabilities(toolOrCaps) {
4281
4290
  if (!toolOrCaps) return [];
4282
4291
  const input = toolOrCaps;
@@ -5050,6 +5059,12 @@ var Context = class {
5050
5059
  agentId;
5051
5060
  /** Human-readable agent name. */
5052
5061
  agentName;
5062
+ /**
5063
+ * Session-level trace ID for correlating storage events with agent
5064
+ * iterations. Stored here and also propagated to `session.traceId`
5065
+ * so storage operations can include it in `storage.*` events.
5066
+ */
5067
+ traceId;
5053
5068
  /** Callbacks fired when `setWorkingDir()` changes the working directory. */
5054
5069
  _onWorkingDirChanged = [];
5055
5070
  /**
@@ -5085,6 +5100,8 @@ var Context = class {
5085
5100
  this.tools = init.tools ?? [];
5086
5101
  this.agentId = init.agentId ?? "unknown";
5087
5102
  this.agentName = init.agentName ?? "Unknown Agent";
5103
+ this.traceId = init.traceId;
5104
+ this.session.traceId = init.traceId;
5088
5105
  }
5089
5106
  /**
5090
5107
  * Observable wrapper over the mutable conversation state. Lazy so
@@ -6374,6 +6391,95 @@ function mergeCustomModelDefs(providerCustomModels, configModels) {
6374
6391
  if (Object.keys(out).length === 0) return void 0;
6375
6392
  return out;
6376
6393
  }
6394
+ function isPrivateIPv4(addr) {
6395
+ const parts = addr.split(".").map((p) => Number.parseInt(p, 10));
6396
+ if (parts.length !== 4 || parts.some((n) => Number.isNaN(n) || n < 0 || n > 255)) {
6397
+ return true;
6398
+ }
6399
+ const [a, b, c] = parts;
6400
+ if (a === 0) return true;
6401
+ if (a === 10) return true;
6402
+ if (a === 127) return true;
6403
+ if (a === 169 && b === 254) return true;
6404
+ if (a === 172 && b >= 16 && b <= 31) return true;
6405
+ if (a === 192 && b === 168) return true;
6406
+ if (a === 192 && b === 0 && c === 0) return true;
6407
+ if (a === 100 && b >= 64 && b <= 127) return true;
6408
+ if (a >= 224) return true;
6409
+ return false;
6410
+ }
6411
+ function isPrivateIPv6(raw) {
6412
+ const lower = raw.toLowerCase();
6413
+ if (lower === "::" || lower === "::1") return true;
6414
+ const groups = expandIPv6(lower);
6415
+ if (!groups) return true;
6416
+ if (groups[0] === 0 && groups[1] === 0 && groups[2] === 0 && groups[3] === 0 && groups[4] === 0 && groups[5] === 65535) {
6417
+ const a = (groups[6] ?? 0) >> 8;
6418
+ const b = (groups[6] ?? 0) & 255;
6419
+ const c = (groups[7] ?? 0) >> 8;
6420
+ const d = (groups[7] ?? 0) & 255;
6421
+ return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
6422
+ }
6423
+ const high = groups[0] ?? 0;
6424
+ if ((high & 65024) === 64512) return true;
6425
+ if ((high & 65472) === 65152) return true;
6426
+ if ((high & 65280) === 65280) return true;
6427
+ return false;
6428
+ }
6429
+ function expandIPv6(addr) {
6430
+ const parts = addr.split("::");
6431
+ if (parts.length > 2) return null;
6432
+ const parseGroups = (s) => {
6433
+ if (s === "") return [];
6434
+ const out = [];
6435
+ for (const g of s.split(":")) {
6436
+ if (g.length === 0 || g.length > 4) return null;
6437
+ const n = Number.parseInt(g, 16);
6438
+ if (Number.isNaN(n) || n < 0 || n > 65535) return null;
6439
+ out.push(n);
6440
+ }
6441
+ return out;
6442
+ };
6443
+ if (parts.length === 1) {
6444
+ const groups = parseGroups(parts[0] ?? "");
6445
+ if (!groups || groups.length !== 8) return null;
6446
+ return groups;
6447
+ }
6448
+ const head = parseGroups(parts[0] ?? "");
6449
+ const tail = parseGroups(parts[1] ?? "");
6450
+ if (!head || !tail) return null;
6451
+ const fill = 8 - head.length - tail.length;
6452
+ if (fill < 0) return null;
6453
+ return [...head, ...new Array(fill).fill(0), ...tail];
6454
+ }
6455
+ async function assertNotPrivateHost(hostname3) {
6456
+ const host = hostname3.startsWith("[") && hostname3.endsWith("]") ? hostname3.slice(1, -1) : hostname3;
6457
+ if (host === "localhost" || host.endsWith(".localhost")) {
6458
+ throw new Error("fetch: blocked localhost target");
6459
+ }
6460
+ const ipVersion = net.isIP(host);
6461
+ if (ipVersion === 4) {
6462
+ if (isPrivateIPv4(host)) {
6463
+ throw new Error(`fetch: blocked private/loopback address "${host}"`);
6464
+ }
6465
+ } else if (ipVersion === 6) {
6466
+ if (isPrivateIPv6(host)) {
6467
+ throw new Error(`fetch: blocked private/loopback address "${host}"`);
6468
+ }
6469
+ } else {
6470
+ try {
6471
+ const records = await dns.lookup(host, { all: true });
6472
+ for (const r of records) {
6473
+ const bad = r.family === 4 ? isPrivateIPv4(r.address) : isPrivateIPv6(r.address);
6474
+ if (bad) {
6475
+ throw new Error(`fetch: resolved to private address ${r.address}`);
6476
+ }
6477
+ }
6478
+ } catch (err) {
6479
+ if (err instanceof Error && err.message.startsWith("fetch:")) throw err;
6480
+ }
6481
+ }
6482
+ }
6377
6483
 
6378
6484
  // src/storage/session-store.ts
6379
6485
  init_atomic_write();
@@ -6396,6 +6502,40 @@ var DefaultSessionStore = class _DefaultSessionStore {
6396
6502
  this.events = opts.events;
6397
6503
  this.secretScrubber = opts.secretScrubber;
6398
6504
  }
6505
+ // ── Storage event helpers ───────────────────────────────────────────────────
6506
+ emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
6507
+ this.events?.emit("storage.read", {
6508
+ sessionId,
6509
+ store: "session",
6510
+ filePath,
6511
+ operation,
6512
+ outcome,
6513
+ durationMs,
6514
+ ...error !== void 0 ? { error } : {}
6515
+ });
6516
+ }
6517
+ emitWrite(sessionId, filePath, operation, outcome, durationMs, eventCount, error) {
6518
+ this.events?.emit("storage.write", {
6519
+ sessionId,
6520
+ store: "session",
6521
+ filePath,
6522
+ operation,
6523
+ outcome,
6524
+ durationMs,
6525
+ ...eventCount !== void 0 ? { eventCount } : {},
6526
+ ...error !== void 0 ? { error } : {}
6527
+ });
6528
+ }
6529
+ emitError(sessionId, filePath, operation, error, recoverable) {
6530
+ this.events?.emit("storage.error", {
6531
+ sessionId,
6532
+ store: "session",
6533
+ filePath,
6534
+ operation,
6535
+ error,
6536
+ recoverable
6537
+ });
6538
+ }
6399
6539
  /** Absolute path to the session index file. */
6400
6540
  get indexFile() {
6401
6541
  return path7.join(this.dir, "_index.jsonl");
@@ -6419,22 +6559,26 @@ var DefaultSessionStore = class _DefaultSessionStore {
6419
6559
  const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
6420
6560
  const shardDir = await this.ensureShardDir(id);
6421
6561
  const file = path7.join(shardDir, `${path7.basename(id)}.jsonl`);
6562
+ const t0 = Date.now();
6422
6563
  let handle;
6423
6564
  try {
6424
6565
  handle = await fsp3.open(file, "a", 384);
6425
6566
  } catch (err) {
6567
+ this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), false);
6426
6568
  throw new Error(
6427
6569
  `Failed to open session file: ${err instanceof Error ? err.message : String(err)}`,
6428
6570
  { cause: err }
6429
6571
  );
6430
6572
  }
6431
6573
  try {
6432
- return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
6574
+ const writer = new FileSessionWriter(id, handle, startedAt, meta, this.events, {
6433
6575
  dir: shardDir,
6434
6576
  filePath: file,
6435
6577
  secretScrubber: this.secretScrubber,
6436
6578
  onClose: (s) => this.appendToIndex(s)
6437
6579
  });
6580
+ this.emitWrite(id, file, "create", "success", Date.now() - t0);
6581
+ return writer;
6438
6582
  } catch (err) {
6439
6583
  await handle.close().catch((e) => console.warn(JSON.stringify({
6440
6584
  level: "warn",
@@ -6442,16 +6586,19 @@ var DefaultSessionStore = class _DefaultSessionStore {
6442
6586
  message: e instanceof Error ? e.message : String(e),
6443
6587
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6444
6588
  })));
6589
+ this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), true);
6445
6590
  throw err;
6446
6591
  }
6447
6592
  }
6448
6593
  async resume(id) {
6449
6594
  const file = this.sessionPath(id, ".jsonl");
6595
+ const t0 = Date.now();
6450
6596
  const data = await this.load(id);
6451
6597
  let handle;
6452
6598
  try {
6453
6599
  handle = await fsp3.open(file, "a", 384);
6454
6600
  } catch (err) {
6601
+ this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), false);
6455
6602
  throw new Error(
6456
6603
  `Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
6457
6604
  { cause: err }
@@ -6479,6 +6626,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
6479
6626
  onClose: (s) => this.appendToIndex(s)
6480
6627
  }
6481
6628
  );
6629
+ this.emitWrite(id, file, "resume", "success", Date.now() - t0);
6482
6630
  return { writer, data };
6483
6631
  } catch (err) {
6484
6632
  await handle.close().catch((e) => console.warn(JSON.stringify({
@@ -6487,27 +6635,39 @@ var DefaultSessionStore = class _DefaultSessionStore {
6487
6635
  message: e instanceof Error ? e.message : String(e),
6488
6636
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6489
6637
  })));
6638
+ this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), true);
6490
6639
  throw err;
6491
6640
  }
6492
6641
  }
6493
6642
  async load(id) {
6494
6643
  const file = this.sessionPath(id, ".jsonl");
6495
- const raw = await fsp3.readFile(file, "utf8");
6496
- const lines = raw.split("\n").filter((l) => l.trim());
6497
- const events = [];
6498
- for (const line of lines) {
6499
- try {
6500
- const parsed = JSON.parse(line);
6501
- if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
6502
- events.push(parsed);
6644
+ const t0 = Date.now();
6645
+ let outcome = "success";
6646
+ let errorMsg;
6647
+ try {
6648
+ const raw = await fsp3.readFile(file, "utf8");
6649
+ const lines = raw.split("\n").filter((l) => l.trim());
6650
+ const events = [];
6651
+ for (const line of lines) {
6652
+ try {
6653
+ const parsed = JSON.parse(line);
6654
+ if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
6655
+ events.push(parsed);
6656
+ }
6657
+ } catch {
6503
6658
  }
6504
- } catch {
6505
6659
  }
6660
+ const meta = this.metaFromEvents(id, events);
6661
+ const { messages, usage } = this.replay(events, id);
6662
+ const toolCallEnds = extractToolCallEnds(events);
6663
+ return { metadata: meta, events, messages, usage, toolCallEnds };
6664
+ } catch (err) {
6665
+ outcome = "failure";
6666
+ errorMsg = err instanceof Error ? err.message : String(err);
6667
+ throw err;
6668
+ } finally {
6669
+ this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
6506
6670
  }
6507
- const meta = this.metaFromEvents(id, events);
6508
- const { messages, usage } = this.replay(events, id);
6509
- const toolCallEnds = extractToolCallEnds(events);
6510
- return { metadata: meta, events, messages, usage, toolCallEnds };
6511
6671
  }
6512
6672
  async list(limit = 20) {
6513
6673
  try {
@@ -6574,12 +6734,22 @@ var DefaultSessionStore = class _DefaultSessionStore {
6574
6734
  * (keep latest per session), and rewrite. Atomic via temp+rename.
6575
6735
  */
6576
6736
  async compactIndex() {
6577
- const entries = await this.readIndex();
6578
- if (entries.length === 0) return;
6579
- const tmp = `${this.indexFile}.compact.tmp`;
6580
- const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
6581
- await fsp3.writeFile(tmp, lines, "utf8");
6582
- await fsp3.rename(tmp, this.indexFile);
6737
+ const t0 = Date.now();
6738
+ let outcome = "success";
6739
+ let errorMsg;
6740
+ try {
6741
+ const entries = await this.readIndex();
6742
+ if (entries.length === 0) return;
6743
+ const tmp = `${this.indexFile}.compact.tmp`;
6744
+ const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
6745
+ await fsp3.writeFile(tmp, lines, "utf8");
6746
+ await fsp3.rename(tmp, this.indexFile);
6747
+ } catch (err) {
6748
+ outcome = "failure";
6749
+ errorMsg = err instanceof Error ? err.message : String(err);
6750
+ } finally {
6751
+ this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
6752
+ }
6583
6753
  }
6584
6754
  /**
6585
6755
  * Read the index file and return deduplicated session summaries.
@@ -6655,22 +6825,31 @@ var DefaultSessionStore = class _DefaultSessionStore {
6655
6825
  }
6656
6826
  async summaryFor(id) {
6657
6827
  const manifest = this.sessionPath(id, ".summary.json");
6828
+ const t0 = Date.now();
6829
+ let outcome = "success";
6830
+ let errorMsg;
6658
6831
  try {
6659
6832
  const raw = await fsp3.readFile(manifest, "utf8");
6833
+ this.emitRead(id, manifest, "summary", "success", Date.now() - t0);
6660
6834
  return JSON.parse(raw);
6661
6835
  } catch {
6662
6836
  const full = this.sessionPath(id, ".jsonl");
6663
6837
  const stat11 = await fsp3.stat(full);
6664
6838
  const summary = await this.summarize(id, stat11.mtime.toISOString());
6665
6839
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
6840
+ const msg = err instanceof Error ? err.message : String(err);
6841
+ this.emitError(id, manifest, "summary_fallback", msg, true);
6666
6842
  console.warn(JSON.stringify({
6667
6843
  level: "warn",
6668
6844
  event: "session_store.manifest_write_failed",
6669
6845
  sessionId: id,
6670
- message: err instanceof Error ? err.message : String(err),
6846
+ message: msg,
6671
6847
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6672
6848
  }));
6673
6849
  });
6850
+ outcome = "failure";
6851
+ errorMsg = "summary fallback \u2014 manifest rebuilt";
6852
+ this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
6674
6853
  return summary;
6675
6854
  }
6676
6855
  }
@@ -6936,7 +7115,7 @@ function extractToolCallEnds(events) {
6936
7115
  return result;
6937
7116
  }
6938
7117
  var FileSessionWriter = class _FileSessionWriter {
6939
- constructor(id, handle, startedAt, meta, events, opts = {}) {
7118
+ constructor(id, handle, startedAt, meta, events, opts = {}, traceId) {
6940
7119
  this.id = id;
6941
7120
  this.handle = handle;
6942
7121
  this.startedAt = startedAt;
@@ -6955,6 +7134,7 @@ var FileSessionWriter = class _FileSessionWriter {
6955
7134
  provider: meta.provider ?? "unknown",
6956
7135
  tokenTotal: 0
6957
7136
  };
7137
+ this.traceId = traceId;
6958
7138
  }
6959
7139
  id;
6960
7140
  handle;
@@ -6987,6 +7167,8 @@ var FileSessionWriter = class _FileSessionWriter {
6987
7167
  lastAppendWarnAt = 0;
6988
7168
  secretScrubber;
6989
7169
  onCloseCb;
7170
+ /** Implements SessionWriter.traceId — propagated from ContextInit.traceId. */
7171
+ traceId;
6990
7172
  // ── Write buffer — batches events to reduce per-event disk I/O ─────────
6991
7173
  //
6992
7174
  // Every append() pushes the scrubbed event into an in-memory buffer instead
@@ -7140,9 +7322,14 @@ var FileSessionWriter = class _FileSessionWriter {
7140
7322
  const eventCount = this.writeBuffer.length;
7141
7323
  const batch = this.writeBuffer.map((e) => JSON.stringify(e)).join("\n") + "\n";
7142
7324
  this.writeBuffer = [];
7325
+ const t0 = Date.now();
7326
+ let outcome = "success";
7327
+ let errorMsg;
7143
7328
  try {
7144
7329
  await this.enqueueWrite(batch);
7145
7330
  } catch (err) {
7331
+ outcome = "failure";
7332
+ errorMsg = err instanceof Error ? err.message : String(err);
7146
7333
  this.appendFailCount += eventCount;
7147
7334
  const now = Date.now();
7148
7335
  if (now - this.lastAppendWarnAt > 5e3) {
@@ -7156,6 +7343,18 @@ var FileSessionWriter = class _FileSessionWriter {
7156
7343
  this.lastAppendWarnAt = now;
7157
7344
  this.appendFailCount = 0;
7158
7345
  }
7346
+ } finally {
7347
+ this.events?.emit("storage.write", {
7348
+ sessionId: this.id,
7349
+ store: "session",
7350
+ filePath: this.filePath,
7351
+ operation: "flush",
7352
+ outcome,
7353
+ durationMs: Date.now() - t0,
7354
+ ...errorMsg !== void 0 ? { error: errorMsg } : {},
7355
+ ...eventCount !== void 0 ? { eventCount } : {},
7356
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
7357
+ });
7159
7358
  }
7160
7359
  }
7161
7360
  observeForSummary(event) {
@@ -7221,14 +7420,46 @@ var FileSessionWriter = class _FileSessionWriter {
7221
7420
  outcome: this.outcome ?? "completed"
7222
7421
  };
7223
7422
  if (this.manifestFile) {
7423
+ const t0 = Date.now();
7424
+ let outcome = "success";
7425
+ let errorMsg;
7224
7426
  try {
7225
7427
  await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
7226
- } catch {
7428
+ } catch (err) {
7429
+ outcome = "failure";
7430
+ errorMsg = err instanceof Error ? err.message : String(err);
7431
+ } finally {
7432
+ this.events?.emit("storage.write", {
7433
+ sessionId: this.id,
7434
+ store: "session",
7435
+ filePath: this.manifestFile,
7436
+ operation: "close",
7437
+ outcome,
7438
+ durationMs: Date.now() - t0,
7439
+ ...errorMsg !== void 0 ? { error: errorMsg } : {},
7440
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
7441
+ });
7227
7442
  }
7228
7443
  }
7444
+ const idxT0 = Date.now();
7445
+ let idxOutcome = "success";
7446
+ let idxError;
7229
7447
  try {
7230
7448
  await this.onCloseCb?.(this.summary);
7231
- } catch {
7449
+ } catch (err) {
7450
+ idxOutcome = "failure";
7451
+ idxError = err instanceof Error ? err.message : String(err);
7452
+ } finally {
7453
+ this.events?.emit("storage.write", {
7454
+ sessionId: this.summary.id,
7455
+ store: "session",
7456
+ filePath: this.filePath,
7457
+ operation: "index_append",
7458
+ outcome: idxOutcome,
7459
+ durationMs: Date.now() - idxT0,
7460
+ ...idxError !== void 0 ? { error: idxError } : {},
7461
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
7462
+ });
7232
7463
  }
7233
7464
  try {
7234
7465
  await this.handle.close();
@@ -7390,23 +7621,81 @@ function userInputTitle(content) {
7390
7621
  init_atomic_write();
7391
7622
  var QueueStore = class {
7392
7623
  file;
7624
+ // Use `| undefined` (not `?`) so exactOptionalPropertyTypes doesn't
7625
+ // reject assigning an optional constructor parameter to these fields.
7626
+ events;
7627
+ traceId;
7393
7628
  constructor(opts) {
7394
7629
  this.file = path7.join(opts.dir, "queue.json");
7630
+ this.events = opts.events;
7631
+ this.traceId = opts.traceId;
7395
7632
  }
7396
7633
  async write(items) {
7634
+ const t0 = Date.now();
7397
7635
  if (items.length === 0) {
7398
7636
  await this.clear();
7399
7637
  return;
7400
7638
  }
7401
- await atomicWrite(this.file, JSON.stringify(items), { mode: 384 });
7639
+ try {
7640
+ await atomicWrite(this.file, JSON.stringify(items), { mode: 384 });
7641
+ this.events?.emit("storage.write", {
7642
+ sessionId: this.traceId ?? "~boot~",
7643
+ store: "queue",
7644
+ filePath: this.file,
7645
+ operation: "write",
7646
+ outcome: "success",
7647
+ durationMs: Date.now() - t0,
7648
+ ...this.traceId !== void 0 && { traceId: this.traceId }
7649
+ });
7650
+ } catch (err) {
7651
+ this.events?.emit("storage.error", {
7652
+ sessionId: this.traceId ?? "~boot~",
7653
+ store: "queue",
7654
+ filePath: this.file,
7655
+ operation: "write",
7656
+ outcome: "failure",
7657
+ error: err instanceof Error ? err.message : String(err),
7658
+ recoverable: false,
7659
+ ...this.traceId !== void 0 && { traceId: this.traceId }
7660
+ });
7661
+ console.warn(JSON.stringify({
7662
+ level: "warn",
7663
+ event: "queue_store.write_failed",
7664
+ path: this.file,
7665
+ message: err instanceof Error ? err.message : String(err),
7666
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
7667
+ }));
7668
+ }
7402
7669
  }
7403
7670
  async read() {
7671
+ const t0 = Date.now();
7404
7672
  let raw;
7405
7673
  try {
7406
7674
  raw = await fsp3.readFile(this.file, "utf8");
7407
7675
  } catch (err) {
7408
7676
  const code = err.code;
7409
- if (code === "ENOENT") return [];
7677
+ if (code === "ENOENT") {
7678
+ this.events?.emit("storage.read", {
7679
+ sessionId: this.traceId ?? "~boot~",
7680
+ store: "queue",
7681
+ filePath: this.file,
7682
+ operation: "read",
7683
+ outcome: "success",
7684
+ durationMs: Date.now() - t0,
7685
+ ...this.traceId !== void 0 && { traceId: this.traceId }
7686
+ });
7687
+ return [];
7688
+ }
7689
+ this.events?.emit("storage.error", {
7690
+ sessionId: this.traceId ?? "~boot~",
7691
+ store: "queue",
7692
+ filePath: this.file,
7693
+ operation: "read",
7694
+ outcome: "failure",
7695
+ error: err instanceof Error ? err.message : String(err),
7696
+ recoverable: true,
7697
+ ...this.traceId !== void 0 && { traceId: this.traceId }
7698
+ });
7410
7699
  console.warn(JSON.stringify({
7411
7700
  level: "warn",
7412
7701
  event: "queue_store.read_failed",
@@ -7420,9 +7709,40 @@ var QueueStore = class {
7420
7709
  try {
7421
7710
  parsed = JSON.parse(raw);
7422
7711
  } catch {
7712
+ this.events?.emit("storage.read", {
7713
+ sessionId: this.traceId ?? "~boot~",
7714
+ store: "queue",
7715
+ filePath: this.file,
7716
+ operation: "read",
7717
+ outcome: "failure",
7718
+ durationMs: Date.now() - t0,
7719
+ error: "parse_failed",
7720
+ ...this.traceId !== void 0 && { traceId: this.traceId }
7721
+ });
7722
+ return [];
7723
+ }
7724
+ if (!Array.isArray(parsed)) {
7725
+ this.events?.emit("storage.read", {
7726
+ sessionId: this.traceId ?? "~boot~",
7727
+ store: "queue",
7728
+ filePath: this.file,
7729
+ operation: "read",
7730
+ outcome: "failure",
7731
+ durationMs: Date.now() - t0,
7732
+ error: "invalid_schema",
7733
+ ...this.traceId !== void 0 && { traceId: this.traceId }
7734
+ });
7423
7735
  return [];
7424
7736
  }
7425
- if (!Array.isArray(parsed)) return [];
7737
+ this.events?.emit("storage.read", {
7738
+ sessionId: this.traceId ?? "~boot~",
7739
+ store: "queue",
7740
+ filePath: this.file,
7741
+ operation: "read",
7742
+ outcome: "success",
7743
+ durationMs: Date.now() - t0,
7744
+ ...this.traceId !== void 0 && { traceId: this.traceId }
7745
+ });
7426
7746
  const out = [];
7427
7747
  for (const v of parsed) {
7428
7748
  if (isPersistedQueueItem(v)) out.push(v);
@@ -7430,11 +7750,31 @@ var QueueStore = class {
7430
7750
  return out;
7431
7751
  }
7432
7752
  async clear() {
7753
+ const t0 = Date.now();
7433
7754
  try {
7434
7755
  await fsp3.unlink(this.file);
7756
+ this.events?.emit("storage.write", {
7757
+ sessionId: this.traceId ?? "~boot~",
7758
+ store: "queue",
7759
+ filePath: this.file,
7760
+ operation: "clear",
7761
+ outcome: "success",
7762
+ durationMs: Date.now() - t0,
7763
+ ...this.traceId !== void 0 && { traceId: this.traceId }
7764
+ });
7435
7765
  } catch (err) {
7436
7766
  const code = err.code;
7437
7767
  if (code === "ENOENT") return;
7768
+ this.events?.emit("storage.error", {
7769
+ sessionId: this.traceId ?? "~boot~",
7770
+ store: "queue",
7771
+ filePath: this.file,
7772
+ operation: "clear",
7773
+ outcome: "failure",
7774
+ error: err instanceof Error ? err.message : String(err),
7775
+ recoverable: true,
7776
+ ...this.traceId !== void 0 && { traceId: this.traceId }
7777
+ });
7438
7778
  console.warn(JSON.stringify({
7439
7779
  level: "warn",
7440
7780
  event: "queue_store.clear_failed",
@@ -7809,6 +8149,7 @@ var MAX_BYTES_TOTAL = 32e3;
7809
8149
  var DefaultMemoryStore = class {
7810
8150
  files;
7811
8151
  events;
8152
+ traceId;
7812
8153
  backend;
7813
8154
  /**
7814
8155
  * Per-scope serialization queue. `remember` / `forget` / `consolidate` /
@@ -7874,15 +8215,70 @@ var DefaultMemoryStore = class {
7874
8215
  if (writeErr2) {
7875
8216
  parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
7876
8217
  }
7877
- const body = await this.backend.readAll(scope, this.files[scope]);
7878
- if (body.trim()) parts.push(`## ${labelOf(scope)}
8218
+ const t0 = Date.now();
8219
+ const filePath = this.files[scope];
8220
+ try {
8221
+ const body = await this.backend.readAll(scope, filePath);
8222
+ const dur = Date.now() - t0;
8223
+ this.events?.emit("storage.read", {
8224
+ sessionId: "~memory~",
8225
+ store: "memory",
8226
+ filePath,
8227
+ operation: "readAll",
8228
+ outcome: "success",
8229
+ durationMs: dur,
8230
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8231
+ });
8232
+ if (body.trim()) parts.push(`## ${labelOf(scope)}
7879
8233
 
7880
8234
  ${body.trim()}`);
8235
+ } catch (err) {
8236
+ const dur = Date.now() - t0;
8237
+ this.events?.emit("storage.read", {
8238
+ sessionId: "~memory~",
8239
+ store: "memory",
8240
+ filePath,
8241
+ operation: "readAll",
8242
+ outcome: "failure",
8243
+ durationMs: dur,
8244
+ error: err instanceof Error ? err.message : String(err),
8245
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8246
+ });
8247
+ throw err;
8248
+ }
7881
8249
  }
7882
8250
  return parts.join("\n\n");
7883
8251
  }
7884
8252
  async read(scope) {
7885
- return this.backend.readAll(scope, this.files[scope]);
8253
+ const t0 = Date.now();
8254
+ const filePath = this.files[scope];
8255
+ try {
8256
+ const body = await this.backend.readAll(scope, filePath);
8257
+ const dur = Date.now() - t0;
8258
+ this.events?.emit("storage.read", {
8259
+ sessionId: "~memory~",
8260
+ store: "memory",
8261
+ filePath,
8262
+ operation: "read",
8263
+ outcome: "success",
8264
+ durationMs: dur,
8265
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8266
+ });
8267
+ return body;
8268
+ } catch (err) {
8269
+ const dur = Date.now() - t0;
8270
+ this.events?.emit("storage.read", {
8271
+ sessionId: "~memory~",
8272
+ store: "memory",
8273
+ filePath,
8274
+ operation: "read",
8275
+ outcome: "failure",
8276
+ durationMs: dur,
8277
+ error: err instanceof Error ? err.message : String(err),
8278
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8279
+ });
8280
+ throw err;
8281
+ }
7886
8282
  }
7887
8283
  /**
7888
8284
  * List entries from a scope, newest first. Delegates to the backend
@@ -7908,7 +8304,34 @@ ${body.trim()}`);
7908
8304
  const ts = (/* @__PURE__ */ new Date()).toISOString();
7909
8305
  return this.runSerialized(scope, async () => {
7910
8306
  const entry = { scope, text, ts, ...metadata };
7911
- await this.backend.remember(scope, entry, this.files[scope]);
8307
+ const filePath = this.files[scope];
8308
+ const t0 = Date.now();
8309
+ try {
8310
+ await this.backend.remember(scope, entry, filePath);
8311
+ const dur = Date.now() - t0;
8312
+ this.events?.emit("storage.write", {
8313
+ sessionId: "~memory~",
8314
+ store: "memory",
8315
+ filePath,
8316
+ operation: "remember",
8317
+ outcome: "success",
8318
+ durationMs: dur,
8319
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8320
+ });
8321
+ } catch (err) {
8322
+ const dur = Date.now() - t0;
8323
+ this.events?.emit("storage.write", {
8324
+ sessionId: "~memory~",
8325
+ store: "memory",
8326
+ filePath,
8327
+ operation: "remember",
8328
+ outcome: "failure",
8329
+ durationMs: dur,
8330
+ error: err instanceof Error ? err.message : String(err),
8331
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8332
+ });
8333
+ throw err;
8334
+ }
7912
8335
  const raw = await this.backend.readAll(scope, this.files[scope]);
7913
8336
  if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
7914
8337
  const removed = await this.backend.consolidate(scope, this.files[scope]);
@@ -8036,7 +8459,35 @@ ${body.trim()}`);
8036
8459
  }
8037
8460
  async forget(query, scope = "project-memory") {
8038
8461
  return this.runSerialized(scope, async () => {
8039
- const removed = await this.backend.forget(scope, query, this.files[scope]);
8462
+ const filePath = this.files[scope];
8463
+ const t0 = Date.now();
8464
+ let removed = 0;
8465
+ try {
8466
+ removed = await this.backend.forget(scope, query, filePath);
8467
+ const dur = Date.now() - t0;
8468
+ this.events?.emit("storage.write", {
8469
+ sessionId: "~memory~",
8470
+ store: "memory",
8471
+ filePath,
8472
+ operation: "forget",
8473
+ outcome: "success",
8474
+ durationMs: dur,
8475
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8476
+ });
8477
+ } catch (err) {
8478
+ const dur = Date.now() - t0;
8479
+ this.events?.emit("storage.write", {
8480
+ sessionId: "~memory~",
8481
+ store: "memory",
8482
+ filePath,
8483
+ operation: "forget",
8484
+ outcome: "failure",
8485
+ durationMs: dur,
8486
+ error: err instanceof Error ? err.message : String(err),
8487
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8488
+ });
8489
+ throw err;
8490
+ }
8040
8491
  if (removed > 0) {
8041
8492
  this.events?.emit("memory.forgotten", {
8042
8493
  scope,
@@ -8050,7 +8501,35 @@ ${body.trim()}`);
8050
8501
  }
8051
8502
  async consolidate(scope) {
8052
8503
  return this.runSerialized(scope, async () => {
8053
- const removed = await this.backend.consolidate(scope, this.files[scope]);
8504
+ const filePath = this.files[scope];
8505
+ const t0 = Date.now();
8506
+ let removed = 0;
8507
+ try {
8508
+ removed = await this.backend.consolidate(scope, filePath);
8509
+ const dur = Date.now() - t0;
8510
+ this.events?.emit("storage.write", {
8511
+ sessionId: "~memory~",
8512
+ store: "memory",
8513
+ filePath,
8514
+ operation: "consolidate",
8515
+ outcome: "success",
8516
+ durationMs: dur,
8517
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8518
+ });
8519
+ } catch (err) {
8520
+ const dur = Date.now() - t0;
8521
+ this.events?.emit("storage.write", {
8522
+ sessionId: "~memory~",
8523
+ store: "memory",
8524
+ filePath,
8525
+ operation: "consolidate",
8526
+ outcome: "failure",
8527
+ durationMs: dur,
8528
+ error: err instanceof Error ? err.message : String(err),
8529
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8530
+ });
8531
+ throw err;
8532
+ }
8054
8533
  if (removed > 0) {
8055
8534
  this.events?.emit("memory.consolidated", {
8056
8535
  scope,
@@ -8063,7 +8542,34 @@ ${body.trim()}`);
8063
8542
  async clear(scope) {
8064
8543
  if (scope) {
8065
8544
  await this.runSerialized(scope, async () => {
8066
- await this.backend.clear(scope, this.files[scope]);
8545
+ const filePath = this.files[scope];
8546
+ const t0 = Date.now();
8547
+ try {
8548
+ await this.backend.clear(scope, filePath);
8549
+ const dur = Date.now() - t0;
8550
+ this.events?.emit("storage.write", {
8551
+ sessionId: "~memory~",
8552
+ store: "memory",
8553
+ filePath,
8554
+ operation: "clear",
8555
+ outcome: "success",
8556
+ durationMs: dur,
8557
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8558
+ });
8559
+ } catch (err) {
8560
+ const dur = Date.now() - t0;
8561
+ this.events?.emit("storage.write", {
8562
+ sessionId: "~memory~",
8563
+ store: "memory",
8564
+ filePath,
8565
+ operation: "clear",
8566
+ outcome: "failure",
8567
+ durationMs: dur,
8568
+ error: err instanceof Error ? err.message : String(err),
8569
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8570
+ });
8571
+ throw err;
8572
+ }
8067
8573
  this.events?.emit("memory.cleared", { scope });
8068
8574
  await this.mirrorBackup(scope);
8069
8575
  });
@@ -8071,15 +8577,54 @@ ${body.trim()}`);
8071
8577
  }
8072
8578
  await Promise.all(
8073
8579
  ["project-agents", "project-memory", "user-memory"].map(
8074
- (s) => this.runSerialized(s, async () => {
8075
- await this.backend.clear(s, this.files[s]);
8580
+ async (s) => this.runSerialized(s, async () => {
8581
+ const filePath = this.files[s];
8582
+ const t0 = Date.now();
8583
+ try {
8584
+ await this.backend.clear(s, filePath);
8585
+ const dur = Date.now() - t0;
8586
+ this.events?.emit("storage.write", {
8587
+ sessionId: "~memory~",
8588
+ store: "memory",
8589
+ filePath,
8590
+ operation: "clear",
8591
+ outcome: "success",
8592
+ durationMs: dur,
8593
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8594
+ });
8595
+ } catch (err) {
8596
+ const dur = Date.now() - t0;
8597
+ this.events?.emit("storage.write", {
8598
+ sessionId: "~memory~",
8599
+ store: "memory",
8600
+ filePath,
8601
+ operation: "clear",
8602
+ outcome: "failure",
8603
+ durationMs: dur,
8604
+ error: err instanceof Error ? err.message : String(err),
8605
+ ...this.traceId !== void 0 && { traceId: this.traceId }
8606
+ });
8607
+ throw err;
8608
+ }
8076
8609
  this.events?.emit("memory.cleared", { scope: s });
8077
8610
  await this.mirrorBackup(s);
8078
8611
  })
8079
8612
  )
8080
8613
  );
8081
8614
  }
8082
- /** Mirror current memory content to the persistent backup directory. */
8615
+ /**
8616
+ * Return a new MemoryStore proxy that carries `traceId` on every storage
8617
+ * event. The original store is left unchanged — callers that need a
8618
+ * trace-decorated view (e.g. session-run tools) receive the proxy while
8619
+ * the singleton remains trace-free for boot-time use.
8620
+ *
8621
+ * The proxy implements the full `MemoryStore` interface; all other
8622
+ * properties (backend, etc.) are delegated to the original store.
8623
+ */
8624
+ withTraceId(traceId) {
8625
+ this.traceId = traceId;
8626
+ return this;
8627
+ }
8083
8628
  async mirrorBackup(scope) {
8084
8629
  if (!this.persistBackup || scope === "project-agents") return;
8085
8630
  try {
@@ -8173,6 +8718,13 @@ function deepFreeze(obj) {
8173
8718
  return Object.freeze(obj);
8174
8719
  }
8175
8720
  init_atomic_write();
8721
+ function storageErrorString(err) {
8722
+ if (err instanceof Error) {
8723
+ const code = err.code;
8724
+ return code ? `${code}: ${err.message}` : err.message;
8725
+ }
8726
+ return String(err);
8727
+ }
8176
8728
  var BEHAVIOR_DEFAULTS = {
8177
8729
  version: 1,
8178
8730
  context: {
@@ -8275,11 +8827,15 @@ var DefaultConfigLoader = class {
8275
8827
  strict;
8276
8828
  vault;
8277
8829
  extraSources;
8830
+ events;
8831
+ traceId;
8278
8832
  constructor(opts) {
8279
8833
  this.paths = opts.paths;
8280
8834
  this.strict = opts.strict ?? false;
8281
8835
  this.vault = opts.vault;
8282
8836
  this.extraSources = opts.sources ?? [];
8837
+ this.events = opts.events;
8838
+ this.traceId = opts.traceId;
8283
8839
  }
8284
8840
  async load(opts = {}) {
8285
8841
  let cfg = { ...BEHAVIOR_DEFAULTS };
@@ -8356,7 +8912,33 @@ var DefaultConfigLoader = class {
8356
8912
  if (this.vault && toWrite.githubToken && !toWrite.githubToken.startsWith("enc:")) {
8357
8913
  toWrite = { ...toWrite, githubToken: this.vault.encrypt(toWrite.githubToken) };
8358
8914
  }
8359
- await atomicWrite(this.paths.syncConfig, JSON.stringify(toWrite, null, 2), { mode: 384 });
8915
+ const fp = this.paths.syncConfig;
8916
+ const t0 = Date.now();
8917
+ try {
8918
+ await atomicWrite(fp, JSON.stringify(toWrite, null, 2), { mode: 384 });
8919
+ this.events?.emit("storage.write", {
8920
+ sessionId: "~config~",
8921
+ store: "config",
8922
+ filePath: fp,
8923
+ operation: "persist_sync",
8924
+ outcome: "success",
8925
+ durationMs: Date.now() - t0,
8926
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
8927
+ });
8928
+ } catch (err) {
8929
+ this.events?.emit("storage.error", {
8930
+ sessionId: "~config~",
8931
+ store: "config",
8932
+ filePath: fp,
8933
+ operation: "persist_sync",
8934
+ outcome: "failure",
8935
+ error: storageErrorString(err),
8936
+ recoverable: false,
8937
+ durationMs: Date.now() - t0,
8938
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
8939
+ });
8940
+ throw err;
8941
+ }
8360
8942
  }
8361
8943
  /**
8362
8944
  * Read ~/.wrongstack/sync.json (encrypted GitHub token storage) and decrypt
@@ -8365,17 +8947,60 @@ var DefaultConfigLoader = class {
8365
8947
  * isolated — it should never be part of project-local or env-driven config.
8366
8948
  */
8367
8949
  async loadSyncConfig() {
8950
+ const fp = this.paths.syncConfig;
8951
+ const t0 = Date.now();
8368
8952
  try {
8369
- const raw = await fsp3.readFile(this.paths.syncConfig, "utf8");
8953
+ const raw = await fsp3.readFile(fp, "utf8");
8370
8954
  const parsed = safeParse(raw);
8371
- if (!parsed.ok || !parsed.value) return null;
8955
+ if (!parsed.ok || !parsed.value) {
8956
+ this.events?.emit("storage.read", {
8957
+ sessionId: "~config~",
8958
+ store: "config",
8959
+ filePath: fp,
8960
+ operation: "load_sync",
8961
+ outcome: "failure",
8962
+ durationMs: Date.now() - t0,
8963
+ error: "parse error or empty file",
8964
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
8965
+ });
8966
+ return null;
8967
+ }
8372
8968
  if (this.vault) {
8373
8969
  const decrypted = decryptConfigSecrets({ sync: parsed.value }, this.vault);
8374
- return decrypted.sync ?? null;
8970
+ const result = decrypted.sync ?? null;
8971
+ this.events?.emit("storage.read", {
8972
+ sessionId: "~config~",
8973
+ store: "config",
8974
+ filePath: fp,
8975
+ operation: "load_sync",
8976
+ outcome: "success",
8977
+ durationMs: Date.now() - t0,
8978
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
8979
+ });
8980
+ return result;
8375
8981
  }
8982
+ this.events?.emit("storage.read", {
8983
+ sessionId: "~config~",
8984
+ store: "config",
8985
+ filePath: fp,
8986
+ operation: "load_sync",
8987
+ outcome: "success",
8988
+ durationMs: Date.now() - t0,
8989
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
8990
+ });
8376
8991
  return parsed.value;
8377
8992
  } catch (err) {
8378
8993
  if (err.code === "ENOENT") return null;
8994
+ this.events?.emit("storage.read", {
8995
+ sessionId: "~config~",
8996
+ store: "config",
8997
+ filePath: fp,
8998
+ operation: "load_sync",
8999
+ outcome: "failure",
9000
+ durationMs: Date.now() - t0,
9001
+ error: storageErrorString(err),
9002
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
9003
+ });
8379
9004
  console.warn(JSON.stringify({
8380
9005
  level: "warn",
8381
9006
  event: "config.sync_load_failed",
@@ -8387,10 +9012,21 @@ var DefaultConfigLoader = class {
8387
9012
  }
8388
9013
  async readJson(file) {
8389
9014
  let raw;
9015
+ const t0 = Date.now();
8390
9016
  try {
8391
9017
  raw = await fsp3.readFile(file, "utf8");
8392
9018
  } catch (err) {
8393
9019
  if (err.code !== "ENOENT") {
9020
+ this.events?.emit("storage.read", {
9021
+ sessionId: "~config~",
9022
+ store: "config",
9023
+ filePath: file,
9024
+ operation: "read_json",
9025
+ outcome: "failure",
9026
+ durationMs: Date.now() - t0,
9027
+ error: storageErrorString(err),
9028
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
9029
+ });
8394
9030
  console.warn(JSON.stringify({
8395
9031
  level: "warn",
8396
9032
  event: "config.read_failed",
@@ -8403,6 +9039,16 @@ var DefaultConfigLoader = class {
8403
9039
  }
8404
9040
  const parsed = safeParse(raw);
8405
9041
  if (!parsed.ok || !parsed.value) {
9042
+ this.events?.emit("storage.read", {
9043
+ sessionId: "~config~",
9044
+ store: "config",
9045
+ filePath: file,
9046
+ operation: "read_json",
9047
+ outcome: "failure",
9048
+ durationMs: Date.now() - t0,
9049
+ error: "parse error or empty file",
9050
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
9051
+ });
8406
9052
  console.warn(JSON.stringify({
8407
9053
  level: "warn",
8408
9054
  event: "config.parse_failed",
@@ -8878,24 +9524,66 @@ function resolveSessionLoggingConfig(cfg) {
8878
9524
 
8879
9525
  // src/storage/todos-checkpoint.ts
8880
9526
  init_atomic_write();
8881
- async function loadTodosCheckpoint(filePath) {
9527
+ async function loadTodosCheckpoint(filePath, events, traceId) {
9528
+ const t0 = Date.now();
8882
9529
  let raw;
8883
9530
  try {
8884
9531
  raw = await fsp3.readFile(filePath, "utf8");
8885
- } catch {
9532
+ } catch (err) {
9533
+ events?.emit("storage.error", {
9534
+ sessionId: traceId ?? "~boot~",
9535
+ store: "todos",
9536
+ filePath,
9537
+ operation: "load",
9538
+ outcome: "failure",
9539
+ error: err instanceof Error ? err.message : String(err),
9540
+ recoverable: true
9541
+ });
8886
9542
  return null;
8887
9543
  }
8888
9544
  try {
8889
9545
  const parsed = JSON.parse(raw);
8890
- if (parsed?.version !== 1 || !Array.isArray(parsed.todos)) return null;
9546
+ if (parsed?.version !== 1 || !Array.isArray(parsed.todos)) {
9547
+ events?.emit("storage.read", {
9548
+ sessionId: traceId ?? "~boot~",
9549
+ store: "todos",
9550
+ filePath,
9551
+ operation: "load",
9552
+ outcome: "failure",
9553
+ durationMs: Date.now() - t0,
9554
+ error: "invalid_schema",
9555
+ ...traceId !== void 0 && { traceId }
9556
+ });
9557
+ return null;
9558
+ }
9559
+ events?.emit("storage.read", {
9560
+ sessionId: traceId ?? "~boot~",
9561
+ store: "todos",
9562
+ filePath,
9563
+ operation: "load",
9564
+ outcome: "success",
9565
+ durationMs: Date.now() - t0,
9566
+ ...traceId !== void 0 && { traceId }
9567
+ });
8891
9568
  return parsed.todos.filter(
8892
9569
  (t2) => !!t2 && typeof t2.id === "string" && typeof t2.content === "string" && typeof t2.status === "string" && (t2.activeForm === void 0 || typeof t2.activeForm === "string")
8893
9570
  );
8894
9571
  } catch {
9572
+ events?.emit("storage.read", {
9573
+ sessionId: traceId ?? "~boot~",
9574
+ store: "todos",
9575
+ filePath,
9576
+ operation: "load",
9577
+ outcome: "failure",
9578
+ durationMs: Date.now() - t0,
9579
+ error: "parse_failed",
9580
+ ...traceId !== void 0 && { traceId }
9581
+ });
8895
9582
  return null;
8896
9583
  }
8897
9584
  }
8898
- async function saveTodosCheckpoint(filePath, sessionId, todos) {
9585
+ async function saveTodosCheckpoint(filePath, sessionId, todos, events, traceId) {
9586
+ const t0 = Date.now();
8899
9587
  const payload = {
8900
9588
  version: 1,
8901
9589
  sessionId,
@@ -8904,7 +9592,25 @@ async function saveTodosCheckpoint(filePath, sessionId, todos) {
8904
9592
  };
8905
9593
  try {
8906
9594
  await atomicWrite(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
9595
+ events?.emit("storage.write", {
9596
+ sessionId: traceId ?? sessionId,
9597
+ store: "todos",
9598
+ filePath,
9599
+ operation: "save",
9600
+ outcome: "success",
9601
+ durationMs: Date.now() - t0,
9602
+ ...traceId !== void 0 && { traceId }
9603
+ });
8907
9604
  } catch (err) {
9605
+ events?.emit("storage.error", {
9606
+ sessionId: traceId ?? sessionId,
9607
+ store: "todos",
9608
+ filePath,
9609
+ operation: "save",
9610
+ outcome: "failure",
9611
+ error: err instanceof Error ? err.message : String(err),
9612
+ recoverable: false
9613
+ });
8908
9614
  console.warn(JSON.stringify({
8909
9615
  level: "warn",
8910
9616
  event: "todos_checkpoint.save_failed",
@@ -8913,12 +9619,12 @@ async function saveTodosCheckpoint(filePath, sessionId, todos) {
8913
9619
  }));
8914
9620
  }
8915
9621
  }
8916
- function attachTodosCheckpoint(state, filePath, sessionId) {
9622
+ function attachTodosCheckpoint(state, filePath, sessionId, events, traceId) {
8917
9623
  let timer = null;
8918
9624
  let pending = null;
8919
9625
  let writeChain = Promise.resolve();
8920
9626
  const enqueueWrite = (todos) => {
8921
- writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos)).catch((err) => {
9627
+ writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos, events, traceId)).catch((err) => {
8922
9628
  const msg = err instanceof Error ? err.message : String(err);
8923
9629
  console.error(JSON.stringify({
8924
9630
  level: "error",
@@ -8960,25 +9666,79 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
8960
9666
 
8961
9667
  // src/storage/plan-store.ts
8962
9668
  init_atomic_write();
8963
- async function loadPlan(filePath) {
9669
+ async function loadPlan(filePath, events) {
9670
+ const t0 = Date.now();
8964
9671
  let raw;
8965
9672
  try {
8966
9673
  raw = await fsp3.readFile(filePath, "utf8");
8967
- } catch {
9674
+ } catch (err) {
9675
+ events?.emit("storage.error", {
9676
+ sessionId: "~boot~",
9677
+ store: "plan",
9678
+ filePath,
9679
+ operation: "load",
9680
+ error: err instanceof Error ? err.message : String(err),
9681
+ recoverable: true
9682
+ });
8968
9683
  return null;
8969
9684
  }
8970
9685
  try {
8971
9686
  const parsed = JSON.parse(raw);
8972
- if (parsed?.version !== 1 || !Array.isArray(parsed.items)) return null;
9687
+ if (parsed?.version !== 1 || !Array.isArray(parsed.items)) {
9688
+ events?.emit("storage.read", {
9689
+ sessionId: "~boot~",
9690
+ store: "plan",
9691
+ filePath,
9692
+ operation: "load",
9693
+ outcome: "failure",
9694
+ durationMs: Date.now() - t0,
9695
+ error: "invalid_schema"
9696
+ });
9697
+ return null;
9698
+ }
9699
+ events?.emit("storage.read", {
9700
+ sessionId: "~boot~",
9701
+ store: "plan",
9702
+ filePath,
9703
+ operation: "load",
9704
+ outcome: "success",
9705
+ durationMs: Date.now() - t0
9706
+ });
8973
9707
  return parsed;
8974
9708
  } catch {
9709
+ events?.emit("storage.read", {
9710
+ sessionId: "~boot~",
9711
+ store: "plan",
9712
+ filePath,
9713
+ operation: "load",
9714
+ outcome: "failure",
9715
+ durationMs: Date.now() - t0,
9716
+ error: "parse_failed"
9717
+ });
8975
9718
  return null;
8976
9719
  }
8977
9720
  }
8978
- async function savePlan(filePath, plan) {
9721
+ async function savePlan(filePath, plan, events) {
9722
+ const t0 = Date.now();
8979
9723
  try {
8980
9724
  await atomicWrite(filePath, JSON.stringify(plan, null, 2), { mode: 384 });
9725
+ events?.emit("storage.write", {
9726
+ sessionId: "~boot~",
9727
+ store: "plan",
9728
+ filePath,
9729
+ operation: "save",
9730
+ outcome: "success",
9731
+ durationMs: Date.now() - t0
9732
+ });
8981
9733
  } catch (err) {
9734
+ events?.emit("storage.error", {
9735
+ sessionId: "~boot~",
9736
+ store: "plan",
9737
+ filePath,
9738
+ operation: "save",
9739
+ error: err instanceof Error ? err.message : String(err),
9740
+ recoverable: false
9741
+ });
8982
9742
  console.warn(
8983
9743
  "[plan-store] save failed:",
8984
9744
  err instanceof Error ? err.message : String(err)
@@ -9662,7 +10422,16 @@ var DefaultPermissionPolicy = class {
9662
10422
  };
9663
10423
  }
9664
10424
  }
9665
- if (tool.permission === "auto" && !tool.mutating) {
10425
+ const hasWriteCap = hasCapability(tool, ToolCapabilities.FS_WRITE);
10426
+ const hasShellCap = hasCapability(tool, [
10427
+ ToolCapabilities.SHELL_ARBITRARY,
10428
+ ToolCapabilities.SHELL_RESTRICTED
10429
+ ]);
10430
+ const hasInstallCap = hasCapability(tool, ToolCapabilities.PACKAGE_INSTALL);
10431
+ const hasConfigCap = hasCapability(tool, ToolCapabilities.CONFIG_MUTATE);
10432
+ const hasSubagentCap = hasCapability(tool, ToolCapabilities.SUBAGENT_SPAWN);
10433
+ const isMutating = tool.mutating || hasWriteCap || hasShellCap || hasInstallCap || hasConfigCap || hasSubagentCap;
10434
+ if (tool.permission === "auto" && !isMutating) {
9666
10435
  const decision = { permission: "auto", source: "default" };
9667
10436
  this._evalCache.set(cacheKey, decision);
9668
10437
  return decision;
@@ -9681,7 +10450,27 @@ var DefaultPermissionPolicy = class {
9681
10450
  }
9682
10451
  return { permission: "confirm", source: "default" };
9683
10452
  }
10453
+ // Capability-based destructive check (preferred over name-based)
10454
+ isDestructiveByCapability(tool) {
10455
+ const caps = tool.capabilities ?? [];
10456
+ if (caps.includes("shell.arbitrary")) return true;
10457
+ if (caps.includes("fs.write")) return true;
10458
+ if (caps.includes("fs.write.outside-project")) return true;
10459
+ return false;
10460
+ }
9684
10461
  isDestructiveYoloCall(tool, input, ctx) {
10462
+ if (this.isDestructiveByCapability(tool)) {
10463
+ if (tool.name === "bash") {
10464
+ const command = getInputString(input, "command");
10465
+ return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) : true;
10466
+ }
10467
+ if (tool.name === "write" || tool.name === "edit" || tool.name === "replace" || tool.name === "patch") {
10468
+ const targetPath = getInputString(input, "path") ?? getInputString(input, "file");
10469
+ if (!targetPath || !ctx.projectRoot) return false;
10470
+ return !pathLooksInsideProject(targetPath, ctx.projectRoot);
10471
+ }
10472
+ return true;
10473
+ }
9685
10474
  if (tool.name === "bash") {
9686
10475
  const command = getInputString(input, "command");
9687
10476
  return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) : true;
@@ -9840,6 +10629,8 @@ function compactSkillBody(body) {
9840
10629
  var DefaultSkillLoader = class {
9841
10630
  dirs;
9842
10631
  cache;
10632
+ entriesCache;
10633
+ bodyCache = /* @__PURE__ */ new Map();
9843
10634
  constructor(opts) {
9844
10635
  this.dirs = [
9845
10636
  { dir: opts.paths.inProjectSkills, source: "project" },
@@ -9898,6 +10689,7 @@ var DefaultSkillLoader = class {
9898
10689
  return lines.join("\n");
9899
10690
  }
9900
10691
  async listEntries() {
10692
+ if (this.entriesCache) return this.entriesCache;
9901
10693
  const skills = await this.list();
9902
10694
  const entries = [];
9903
10695
  for (const s of skills) {
@@ -9908,33 +10700,47 @@ var DefaultSkillLoader = class {
9908
10700
  } catch {
9909
10701
  }
9910
10702
  }
10703
+ this.entriesCache = entries;
9911
10704
  return entries;
9912
10705
  }
9913
10706
  invalidateCache() {
9914
10707
  this.cache = void 0;
10708
+ this.entriesCache = void 0;
10709
+ this.bodyCache.clear();
9915
10710
  }
9916
10711
  async readBody(name) {
10712
+ const cached = this.bodyCache.get(name);
10713
+ if (cached !== void 0) return cached;
9917
10714
  const m = await this.find(name);
9918
10715
  if (!m) throw new Error(`Skill "${name}" not found`);
9919
- return fsp3.readFile(m.path, "utf8");
10716
+ const body = await fsp3.readFile(m.path, "utf8");
10717
+ this.bodyCache.set(name, body);
10718
+ return body;
9920
10719
  }
9921
10720
  async readSaveBody(name) {
10721
+ const key = `save:${name}`;
10722
+ const cached = this.bodyCache.get(key);
10723
+ if (cached !== void 0) return cached;
9922
10724
  const m = await this.find(name);
9923
10725
  if (!m) throw new Error(`Skill "${name}" not found`);
9924
10726
  const savePath = path7.join(path7.dirname(m.path), "SKILL.save.md");
10727
+ let result;
9925
10728
  try {
9926
- return await fsp3.readFile(savePath, "utf8");
10729
+ result = await fsp3.readFile(savePath, "utf8");
9927
10730
  } catch {
9928
10731
  const full = await fsp3.readFile(m.path, "utf8");
9929
10732
  const body = stripFrontmatter(full);
9930
10733
  const compact = compactSkillBody(body);
9931
10734
  if (compact) {
9932
- return `## Overview
10735
+ result = `## Overview
9933
10736
 
9934
10737
  ${compact}`;
10738
+ } else {
10739
+ result = body.trim().slice(0, 300);
9935
10740
  }
9936
- return body.trim().slice(0, 300);
9937
10741
  }
10742
+ this.bodyCache.set(key, result);
10743
+ return result;
9938
10744
  }
9939
10745
  };
9940
10746
  function parseFrontmatter(raw) {
@@ -11333,13 +12139,32 @@ var MAX_JOURNAL_ENTRIES = 500;
11333
12139
  function goalFilePath(projectRoot) {
11334
12140
  return resolveWstackPaths({ projectRoot }).projectGoal;
11335
12141
  }
11336
- async function loadGoal(filePath) {
12142
+ async function loadGoal(filePath, events) {
12143
+ const t0 = Date.now();
11337
12144
  let raw;
11338
12145
  try {
11339
12146
  raw = await fsp3.readFile(filePath, "utf8");
11340
12147
  } catch (err) {
11341
12148
  const code = err.code;
11342
- if (code === "ENOENT") return null;
12149
+ if (code === "ENOENT") {
12150
+ events?.emit("storage.read", {
12151
+ sessionId: "~boot~",
12152
+ store: "goal",
12153
+ filePath,
12154
+ operation: "load",
12155
+ outcome: "success",
12156
+ durationMs: Date.now() - t0
12157
+ });
12158
+ return null;
12159
+ }
12160
+ events?.emit("storage.error", {
12161
+ sessionId: "~boot~",
12162
+ store: "goal",
12163
+ filePath,
12164
+ operation: "load",
12165
+ error: err instanceof Error ? err.message : String(err),
12166
+ recoverable: false
12167
+ });
11343
12168
  throw err;
11344
12169
  }
11345
12170
  try {
@@ -11352,8 +12177,25 @@ async function loadGoal(filePath) {
11352
12177
  message: "invalid schema \u2014 consider deleting and re-creating",
11353
12178
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
11354
12179
  }));
12180
+ events?.emit("storage.read", {
12181
+ sessionId: "~boot~",
12182
+ store: "goal",
12183
+ filePath,
12184
+ operation: "load",
12185
+ outcome: "failure",
12186
+ durationMs: Date.now() - t0,
12187
+ error: "invalid_schema"
12188
+ });
11355
12189
  return null;
11356
12190
  }
12191
+ events?.emit("storage.read", {
12192
+ sessionId: "~boot~",
12193
+ store: "goal",
12194
+ filePath,
12195
+ operation: "load",
12196
+ outcome: "success",
12197
+ durationMs: Date.now() - t0
12198
+ });
11357
12199
  return parsed;
11358
12200
  } catch {
11359
12201
  console.warn(JSON.stringify({
@@ -11363,13 +12205,39 @@ async function loadGoal(filePath) {
11363
12205
  message: "JSON parse failed \u2014 consider deleting and re-creating",
11364
12206
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
11365
12207
  }));
12208
+ events?.emit("storage.read", {
12209
+ sessionId: "~boot~",
12210
+ store: "goal",
12211
+ filePath,
12212
+ operation: "load",
12213
+ outcome: "failure",
12214
+ durationMs: Date.now() - t0,
12215
+ error: "parse_failed"
12216
+ });
11366
12217
  return null;
11367
12218
  }
11368
12219
  }
11369
- async function saveGoal(filePath, goal) {
12220
+ async function saveGoal(filePath, goal, events) {
12221
+ const t0 = Date.now();
11370
12222
  try {
11371
12223
  await atomicWrite(filePath, JSON.stringify(goal, null, 2), { mode: 384 });
12224
+ events?.emit("storage.write", {
12225
+ sessionId: "~boot~",
12226
+ store: "goal",
12227
+ filePath,
12228
+ operation: "save",
12229
+ outcome: "success",
12230
+ durationMs: Date.now() - t0
12231
+ });
11372
12232
  } catch (err) {
12233
+ events?.emit("storage.error", {
12234
+ sessionId: "~boot~",
12235
+ store: "goal",
12236
+ filePath,
12237
+ operation: "save",
12238
+ error: err instanceof Error ? err.message : String(err),
12239
+ recoverable: false
12240
+ });
11373
12241
  throw new FsError({
11374
12242
  message: err instanceof Error ? err.message : String(err),
11375
12243
  code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
@@ -11815,7 +12683,7 @@ var EternalAutonomyEngine = class {
11815
12683
  const emit = (stage) => {
11816
12684
  this.opts.onStage?.(stage);
11817
12685
  };
11818
- const goal = await loadGoal(this.goalPath);
12686
+ const goal = await loadGoal(this.goalPath, this.opts.events);
11819
12687
  if (!goal) {
11820
12688
  emit({ phase: "stopped" });
11821
12689
  this.stopRequested = true;
@@ -11919,7 +12787,7 @@ var EternalAutonomyEngine = class {
11919
12787
  emit({ phase: "reflect", status, note });
11920
12788
  let iterationIndex = 0;
11921
12789
  try {
11922
- const reloaded = await loadGoal(this.goalPath);
12790
+ const reloaded = await loadGoal(this.goalPath, this.opts.events);
11923
12791
  iterationIndex = reloaded?.iterations ?? 0;
11924
12792
  } catch {
11925
12793
  }
@@ -12239,12 +13107,12 @@ ${recentJournal}` : "No prior iterations.",
12239
13107
  }
12240
13108
  }
12241
13109
  async appendIterationEntry(entry) {
12242
- const current = await loadGoal(this.goalPath);
13110
+ const current = await loadGoal(this.goalPath, this.opts.events);
12243
13111
  if (!current) {
12244
13112
  return;
12245
13113
  }
12246
13114
  const updated = appendJournal(current, entry);
12247
- await saveGoal(this.goalPath, updated);
13115
+ await saveGoal(this.goalPath, updated, this.opts.events);
12248
13116
  }
12249
13117
  /**
12250
13118
  * Persistent per-todo failure counter. Skipped silently when the goal
@@ -12253,11 +13121,11 @@ ${recentJournal}` : "No prior iterations.",
12253
13121
  * the counter to rotate past stuck todos once they cross `todoMaxAttempts`.
12254
13122
  */
12255
13123
  async bumpTodoAttempt(todoId) {
12256
- const current = await loadGoal(this.goalPath);
13124
+ const current = await loadGoal(this.goalPath, this.opts.events);
12257
13125
  if (!current) return;
12258
13126
  const attempts = { ...current.todoAttempts ?? {} };
12259
13127
  attempts[todoId] = (attempts[todoId] ?? 0) + 1;
12260
- await saveGoal(this.goalPath, { ...current, todoAttempts: attempts });
13128
+ await saveGoal(this.goalPath, { ...current, todoAttempts: attempts }, this.opts.events);
12261
13129
  }
12262
13130
  /**
12263
13131
  * Flip the mission to `completed` and journal it. Called from two
@@ -12267,7 +13135,7 @@ ${recentJournal}` : "No prior iterations.",
12267
13135
  * goal is already `completed`.
12268
13136
  */
12269
13137
  async markGoalCompleted(action, note) {
12270
- const current = await loadGoal(this.goalPath);
13138
+ const current = await loadGoal(this.goalPath, this.opts.events);
12271
13139
  if (!current) return;
12272
13140
  if (current.goalState === "completed") return;
12273
13141
  const withFlag = { ...current, goalState: "completed" };
@@ -12277,7 +13145,7 @@ ${recentJournal}` : "No prior iterations.",
12277
13145
  status: "success",
12278
13146
  note: note.slice(0, 240)
12279
13147
  });
12280
- await saveGoal(this.goalPath, withEntry);
13148
+ await saveGoal(this.goalPath, withEntry, this.opts.events);
12281
13149
  this.opts.onEternalStop?.();
12282
13150
  }
12283
13151
  /**
@@ -12286,10 +13154,10 @@ ${recentJournal}` : "No prior iterations.",
12286
13154
  * `onEternalStop` so the REPL returns to normal mode.
12287
13155
  */
12288
13156
  async clearGoalManually(note) {
12289
- const current = await loadGoal(this.goalPath);
13157
+ const current = await loadGoal(this.goalPath, this.opts.events);
12290
13158
  if (current) {
12291
13159
  const abandoned = { ...current, goalState: "abandoned" };
12292
- await saveGoal(this.goalPath, abandoned);
13160
+ await saveGoal(this.goalPath, abandoned, this.opts.events);
12293
13161
  }
12294
13162
  try {
12295
13163
  const { unlink: unlink16 } = await import('fs/promises');
@@ -12365,16 +13233,16 @@ ${recentJournal}` : ""
12365
13233
  * Persist a progress update from the agent's [PROGRESS: N%] output.
12366
13234
  */
12367
13235
  async updateProgress(progress, note) {
12368
- const current = await loadGoal(this.goalPath);
13236
+ const current = await loadGoal(this.goalPath, this.opts.events);
12369
13237
  if (!current) return;
12370
13238
  const updated = recordProgress(current, progress, note);
12371
- await saveGoal(this.goalPath, updated);
13239
+ await saveGoal(this.goalPath, updated, this.opts.events);
12372
13240
  }
12373
13241
  async persistEngineState(state) {
12374
- const current = await loadGoal(this.goalPath);
13242
+ const current = await loadGoal(this.goalPath, this.opts.events);
12375
13243
  if (!current) return;
12376
13244
  if (current.engineState === state) return;
12377
- await saveGoal(this.goalPath, { ...current, engineState: state });
13245
+ await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
12378
13246
  }
12379
13247
  };
12380
13248
 
@@ -12723,6 +13591,20 @@ var SubagentBudget = class _SubagentBudget {
12723
13591
  };
12724
13592
 
12725
13593
  // src/coordination/agent-subagent-runner.ts
13594
+ function withDisabledToolFiltering(factory) {
13595
+ return async (config) => {
13596
+ const result = await factory(config);
13597
+ const disabled = config.disabledTools ?? [];
13598
+ if (disabled.length === 0) return result;
13599
+ const registry = result.agent.tools;
13600
+ if (registry && typeof registry.unregister === "function") {
13601
+ for (const toolName of disabled) {
13602
+ registry.unregister(toolName);
13603
+ }
13604
+ }
13605
+ return result;
13606
+ };
13607
+ }
12726
13608
  function makeAgentSubagentRunner(opts) {
12727
13609
  const format = opts.formatTaskInput ?? defaultFormatTaskInput;
12728
13610
  return async (task, ctx) => {
@@ -15546,17 +16428,17 @@ function normalize2(text) {
15546
16428
  return ` ${text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim()} `;
15547
16429
  }
15548
16430
  function scoreAgents(task, catalog = AGENT_CATALOG) {
15549
- const hay = normalize2(task);
16431
+ const haySet = new Set(normalize2(task).split(/\s+/).filter(Boolean));
15550
16432
  const out = [];
15551
16433
  for (const def of Object.values(catalog)) {
15552
16434
  if (!def?.config?.role) continue;
15553
16435
  let score = 0;
15554
16436
  const matched = [];
15555
16437
  for (const kw of def.capability.keywords) {
15556
- const needle = normalize2(kw);
15557
- if (hay.includes(needle.trimEnd() + " ") || hay.includes(" " + needle.trimStart())) {
15558
- const words = kw.trim().split(/\s+/).length;
15559
- score += words;
16438
+ const needleWords = normalize2(kw).split(/\s+/).filter(Boolean);
16439
+ const allPresent = needleWords.every((w) => haySet.has(w));
16440
+ if (allPresent) {
16441
+ score += needleWords.length;
15560
16442
  matched.push(kw);
15561
16443
  }
15562
16444
  }
@@ -16889,7 +17771,8 @@ var ParallelEternalEngine = class {
16889
17771
  doneCondition: { type: "all_tasks_done" }
16890
17772
  };
16891
17773
  this.coordinator = new DefaultMultiAgentCoordinator(config);
16892
- const runner = makeAgentSubagentRunner({ factory: this.agentFactory });
17774
+ const filteredFactory = withDisabledToolFiltering(this.agentFactory);
17775
+ const runner = makeAgentSubagentRunner({ factory: filteredFactory });
16893
17776
  this.coordinator.setRunner?.(runner);
16894
17777
  try {
16895
17778
  while (!this.stopRequested) {
@@ -16924,7 +17807,7 @@ var ParallelEternalEngine = class {
16924
17807
  this.opts.onStage?.(stage);
16925
17808
  };
16926
17809
  this.iterations++;
16927
- const goal = await loadGoal(this.goalPath);
17810
+ const goal = await loadGoal(this.goalPath, this.opts.events);
16928
17811
  if (!goal) {
16929
17812
  this.stopRequested = true;
16930
17813
  emit({ phase: "stopped" });
@@ -16942,7 +17825,8 @@ var ParallelEternalEngine = class {
16942
17825
  doneCondition: { type: "all_tasks_done" }
16943
17826
  };
16944
17827
  this.coordinator = new DefaultMultiAgentCoordinator(config);
16945
- const runner = makeAgentSubagentRunner({ factory: this.agentFactory });
17828
+ const filteredFactory = withDisabledToolFiltering(this.agentFactory);
17829
+ const runner = makeAgentSubagentRunner({ factory: filteredFactory });
16946
17830
  this.coordinator.setRunner?.(runner);
16947
17831
  }
16948
17832
  emit({ phase: "decompose" });
@@ -17051,13 +17935,17 @@ ${personaLine}Task: ${task}
17051
17935
  role: route.role,
17052
17936
  tools: route.definition.config.tools,
17053
17937
  systemPromptOverride: route.definition.config.prompt,
17054
- timeoutMs: this.timeoutMs
17938
+ timeoutMs: this.timeoutMs,
17939
+ // Disable delegation — subagents are leaf workers, not orchestrators
17940
+ disabledTools: ["delegate"]
17055
17941
  } : {
17056
17942
  id: subagentId,
17057
17943
  name: `slot-${subagentId.slice(-6)}`,
17058
17944
  // Let the coordinator apply its default budget (roster or generic).
17059
17945
  // Hardcoding low limits here defeats the x10 budget improvement.
17060
- timeoutMs: this.timeoutMs
17946
+ timeoutMs: this.timeoutMs,
17947
+ // Disable delegation — subagents are leaf workers, not orchestrators
17948
+ disabledTools: ["delegate"]
17061
17949
  }
17062
17950
  );
17063
17951
  subagentIds.push(subagentId);
@@ -17203,10 +18091,10 @@ ${lastFew}` : "No prior iterations.",
17203
18091
  // Helpers
17204
18092
  // -------------------------------------------------------------------------
17205
18093
  async appendIterationEntry(entry) {
17206
- const current = await loadGoal(this.goalPath);
18094
+ const current = await loadGoal(this.goalPath, this.opts.events);
17207
18095
  if (!current) return;
17208
18096
  const updated = appendJournal(current, entry);
17209
- await saveGoal(this.goalPath, updated);
18097
+ await saveGoal(this.goalPath, updated, this.opts.events);
17210
18098
  const entryWithMeta = {
17211
18099
  at: (this.opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
17212
18100
  iteration: updated.iterations,
@@ -17218,10 +18106,10 @@ ${lastFew}` : "No prior iterations.",
17218
18106
  await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
17219
18107
  }
17220
18108
  async persistState(state) {
17221
- const current = await loadGoal(this.goalPath);
18109
+ const current = await loadGoal(this.goalPath, this.opts.events);
17222
18110
  if (!current) return;
17223
18111
  if (current.engineState === state) return;
17224
- await saveGoal(this.goalPath, { ...current, engineState: state });
18112
+ await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
17225
18113
  }
17226
18114
  };
17227
18115
 
@@ -17967,6 +18855,7 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
17967
18855
  }
17968
18856
  for (const dispose of this.disposers) dispose();
17969
18857
  this.disposers.length = 0;
18858
+ this.snapshot.files.length = 0;
17970
18859
  }
17971
18860
  };
17972
18861
 
@@ -18000,7 +18889,12 @@ Working rules:
18000
18889
  6. Never claim a subagent's work as your own without verifying it. If a
18001
18890
  result looks wrong, ask_subagent for clarification before passing it
18002
18891
  to the user.
18003
- 7. Wind down when satisfied. When the results are good enough, call
18892
+ 7. **Act on subagent mail immediately**. Subagent messages (result, ask,
18893
+ assign, note) are injected inline before every step \u2014 even mid-task.
18894
+ When you see one, address it before continuing: reply to asks, factor
18895
+ in results, act on assignments. Use \`mailbox action=ack\` to mark
18896
+ completed messages.
18897
+ 8. Wind down when satisfied. When the results are good enough, call
18004
18898
  work_complete \u2014 no new subagents will spawn and queued tasks complete
18005
18899
  as aborted. Running subagents finish naturally. Call terminate_subagent
18006
18900
  only for ones you need to stop immediately.`;
@@ -18017,6 +18911,13 @@ Bridge contract:
18017
18911
  structured, and self-contained \u2014 assume the Director will paste your
18018
18912
  output into its own context.
18019
18913
 
18914
+ CRITICAL CONSTRAINT \u2014 NO FURTHER DELEGATION:
18915
+ - You MUST NOT call the \`delegate\` tool or attempt to spawn subagents.
18916
+ - You MUST NOT use \`spawn_subagent\`, \`assign_task\`, or any equivalent.
18917
+ - Your role is to execute the assigned task yourself, not to orchestrate.
18918
+ - If a subtask is too complex, report back to the Director with what you
18919
+ found and let the Director decide how to decompose.
18920
+
18020
18921
  Inter-agent mailbox (if you have the \`mail_send\`/\`mail_inbox\`/\`mailbox\` tools):
18021
18922
  - You are part of a project-wide fleet that may span other terminals and
18022
18923
  WebUIs. Your mailbox identity is \`<your-name>@<session-tag>\` (unique
@@ -18031,7 +18932,12 @@ Inter-agent mailbox (if you have the \`mail_send\`/\`mail_inbox\`/\`mailbox\` to
18031
18932
  their exact id instead of doing everything yourself. Discover ids with
18032
18933
  \`mailbox action=online\`.
18033
18934
  - Answer your mail: reply to the sender's exact \`from\` id. When done with
18034
- an assigned task, post a \`result\` back to whoever assigned it.`;
18935
+ an assigned task, post a \`result\` back to whoever assigned it.
18936
+ - **Mail to the leader is always seen**: when you send \`ask\`, \`result\`,
18937
+ or \`assign\` to the director/leader, the message is injected inline into
18938
+ the leader's conversation before their next step \u2014 even if the leader is
18939
+ mid-task. Use \`mail_send\` to reliably reach the leader instead of
18940
+ waiting for them to check in.`;
18035
18941
  function composeDirectorPrompt(parts = {}) {
18036
18942
  const sections = [];
18037
18943
  const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
@@ -20440,6 +21346,7 @@ async function readSubagentPartial(opts, subagentId) {
20440
21346
  }
20441
21347
  function makeDirectorSessionFactory(opts) {
20442
21348
  const runId = opts.directorRunId ?? `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-director`;
21349
+ const { traceId } = opts;
20443
21350
  let store;
20444
21351
  let dir;
20445
21352
  if (opts.store) {
@@ -20455,12 +21362,16 @@ function makeDirectorSessionFactory(opts) {
20455
21362
  dir,
20456
21363
  directorRunId: runId,
20457
21364
  async createSubagentSession({ subagentId, provider, model, title }) {
20458
- return store.create({
21365
+ const writer = await store.create({
20459
21366
  id: subagentId,
20460
21367
  title: title ?? subagentId,
20461
21368
  provider: provider ?? "unknown",
20462
21369
  model: model ?? "unknown"
20463
21370
  });
21371
+ if (traceId !== void 0) {
21372
+ writer.traceId = traceId;
21373
+ }
21374
+ return writer;
20464
21375
  }
20465
21376
  };
20466
21377
  }
@@ -23179,7 +24090,9 @@ var SddParallelRun = class {
23179
24090
  doneCondition: { type: "all_tasks_done" }
23180
24091
  };
23181
24092
  this.coordinator = new DefaultMultiAgentCoordinator(config);
23182
- const runner = makeAgentSubagentRunner({ factory: this.opts.subagentFactory ?? this.defaultFactory() });
24093
+ const baseFactory = this.opts.subagentFactory ?? this.defaultFactory();
24094
+ const filteredFactory = withDisabledToolFiltering(baseFactory);
24095
+ const runner = makeAgentSubagentRunner({ factory: filteredFactory });
23183
24096
  this.coordinator.setRunner?.(runner);
23184
24097
  }
23185
24098
  defaultFactory() {
@@ -23221,7 +24134,9 @@ var SddParallelRun = class {
23221
24134
  id: subagentId,
23222
24135
  name: subagentId,
23223
24136
  role: "executor",
23224
- timeoutMs: this.timeoutMs
24137
+ timeoutMs: this.timeoutMs,
24138
+ // Disable delegation — subagents are leaf workers, not orchestrators
24139
+ disabledTools: ["delegate"]
23225
24140
  })
23226
24141
  );
23227
24142
  const spawnResults = await Promise.all(spawns);
@@ -25308,20 +26223,53 @@ var MAX_TEXT_LENGTH = 2e3;
25308
26223
  var MAX_ANNOTATIONS = 1e3;
25309
26224
  var AnnotationsStore = class {
25310
26225
  dir;
26226
+ events;
26227
+ traceId;
25311
26228
  /** Per-session write queue. Created lazily on first add. */
25312
26229
  writeChains = /* @__PURE__ */ new Map();
25313
26230
  constructor(opts) {
25314
26231
  this.dir = opts.dir;
26232
+ this.events = opts.events;
26233
+ this.traceId = opts.traceId;
25315
26234
  }
25316
26235
  // ── Reads ──────────────────────────────────────────────────────────────
25317
26236
  /**
25318
26237
  * Return all annotations for `sessionId` in insertion order
25319
26238
  * (oldest first). Returns an empty array when no file exists
25320
- * yet (the normal case for a fresh session).
26239
+ * yet (the normal case for a fresh session) and also degrades
26240
+ * gracefully to `[]` on a read error (permissions, corruption) —
26241
+ * the failure is still surfaced via a `storage.read` event so it
26242
+ * never silently hides I/O problems from observers.
25321
26243
  */
25322
26244
  async list(sessionId) {
25323
- const file = await this.readFile(sessionId);
25324
- return file ? file.annotations : [];
26245
+ const t0 = Date.now();
26246
+ const fp = this.filePath(sessionId);
26247
+ try {
26248
+ const file = await this.readFile(sessionId);
26249
+ const durationMs = Date.now() - t0;
26250
+ this.events?.emit("storage.read", {
26251
+ sessionId,
26252
+ store: "annotations",
26253
+ filePath: fp,
26254
+ operation: "list",
26255
+ outcome: "success",
26256
+ durationMs,
26257
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26258
+ });
26259
+ return file ? file.annotations : [];
26260
+ } catch (err) {
26261
+ this.events?.emit("storage.read", {
26262
+ sessionId,
26263
+ store: "annotations",
26264
+ filePath: fp,
26265
+ operation: "list",
26266
+ outcome: "failure",
26267
+ durationMs: Date.now() - t0,
26268
+ error: err instanceof Error ? err.message : String(err),
26269
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26270
+ });
26271
+ return [];
26272
+ }
25325
26273
  }
25326
26274
  /**
25327
26275
  * Convenience: only unresolved annotations, newest first — the
@@ -25373,25 +26321,62 @@ var AnnotationsStore = class {
25373
26321
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
25374
26322
  resolved: false
25375
26323
  };
25376
- await this.enqueue(input.sessionId, async () => {
25377
- await withFileLock(this.filePath(input.sessionId), async () => {
25378
- const all = await this.list(input.sessionId);
25379
- all.push(annotation);
25380
- if (all.length > MAX_ANNOTATIONS) {
25381
- const sorted = all.map((a, i) => ({ a, i })).sort((x, y) => {
25382
- if (x.a.resolved !== y.a.resolved) return x.a.resolved ? 1 : -1;
25383
- return x.a.createdAt.localeCompare(y.a.createdAt);
25384
- });
25385
- const evictCount = all.length - MAX_ANNOTATIONS;
25386
- const toEvict = new Set(sorted.slice(0, evictCount).map((s) => s.a.id));
25387
- const kept = all.filter((a) => !toEvict.has(a.id));
25388
- await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: kept });
25389
- } else {
25390
- await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
25391
- }
26324
+ const fp = this.filePath(input.sessionId);
26325
+ const t0 = Date.now();
26326
+ try {
26327
+ await this.enqueue(input.sessionId, async () => {
26328
+ await withFileLock(fp, async () => {
26329
+ const all = await this.list(input.sessionId);
26330
+ all.push(annotation);
26331
+ if (all.length > MAX_ANNOTATIONS) {
26332
+ const sorted = all.map((a, i) => ({ a, i })).sort((x, y) => {
26333
+ if (x.a.resolved !== y.a.resolved) return x.a.resolved ? 1 : -1;
26334
+ return x.a.createdAt.localeCompare(y.a.createdAt);
26335
+ });
26336
+ const evictCount = all.length - MAX_ANNOTATIONS;
26337
+ const toEvict = new Set(sorted.slice(0, evictCount).map((s) => s.a.id));
26338
+ const kept = all.filter((a) => !toEvict.has(a.id));
26339
+ await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: kept });
26340
+ const durationMs = Date.now() - t0;
26341
+ this.events?.emit("storage.write", {
26342
+ sessionId: input.sessionId,
26343
+ store: "annotations",
26344
+ filePath: fp,
26345
+ operation: "evict",
26346
+ outcome: "success",
26347
+ durationMs,
26348
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26349
+ });
26350
+ } else {
26351
+ await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
26352
+ const durationMs = Date.now() - t0;
26353
+ this.events?.emit("storage.write", {
26354
+ sessionId: input.sessionId,
26355
+ store: "annotations",
26356
+ filePath: fp,
26357
+ operation: "add",
26358
+ outcome: "success",
26359
+ durationMs,
26360
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26361
+ });
26362
+ }
26363
+ });
25392
26364
  });
25393
- });
25394
- return annotation;
26365
+ return annotation;
26366
+ } catch (err) {
26367
+ this.events?.emit("storage.error", {
26368
+ sessionId: input.sessionId,
26369
+ store: "annotations",
26370
+ filePath: fp,
26371
+ operation: "add",
26372
+ outcome: "failure",
26373
+ error: err instanceof Error ? err.message : String(err),
26374
+ recoverable: false,
26375
+ durationMs: Date.now() - t0,
26376
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26377
+ });
26378
+ throw err;
26379
+ }
25395
26380
  }
25396
26381
  /**
25397
26382
  * Mark an annotation as resolved. Returns the updated record, or
@@ -25401,26 +26386,53 @@ var AnnotationsStore = class {
25401
26386
  */
25402
26387
  async resolve(input) {
25403
26388
  let updated = null;
25404
- await this.enqueue(input.sessionId, async () => {
25405
- await withFileLock(this.filePath(input.sessionId), async () => {
25406
- const all = await this.list(input.sessionId);
25407
- const idx = all.findIndex((a) => a.id === input.annotationId);
25408
- if (idx === -1) {
25409
- updated = null;
25410
- return;
25411
- }
25412
- const next = {
25413
- ...expectDefined(all[idx]),
25414
- resolved: true,
25415
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
25416
- resolvedBy: input.resolvedBy
25417
- };
25418
- all[idx] = next;
25419
- await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
25420
- updated = next;
26389
+ const fp = this.filePath(input.sessionId);
26390
+ const t0 = Date.now();
26391
+ try {
26392
+ await this.enqueue(input.sessionId, async () => {
26393
+ await withFileLock(fp, async () => {
26394
+ const all = await this.list(input.sessionId);
26395
+ const idx = all.findIndex((a) => a.id === input.annotationId);
26396
+ if (idx === -1) {
26397
+ updated = null;
26398
+ return;
26399
+ }
26400
+ const next = {
26401
+ ...expectDefined(all[idx]),
26402
+ resolved: true,
26403
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
26404
+ resolvedBy: input.resolvedBy
26405
+ };
26406
+ all[idx] = next;
26407
+ await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
26408
+ updated = next;
26409
+ const durationMs = Date.now() - t0;
26410
+ this.events?.emit("storage.write", {
26411
+ sessionId: input.sessionId,
26412
+ store: "annotations",
26413
+ filePath: fp,
26414
+ operation: "resolve",
26415
+ outcome: "success",
26416
+ durationMs,
26417
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26418
+ });
26419
+ });
25421
26420
  });
25422
- });
25423
- return updated;
26421
+ return updated;
26422
+ } catch (err) {
26423
+ this.events?.emit("storage.error", {
26424
+ sessionId: input.sessionId,
26425
+ store: "annotations",
26426
+ filePath: fp,
26427
+ operation: "resolve",
26428
+ outcome: "failure",
26429
+ error: err instanceof Error ? err.message : String(err),
26430
+ recoverable: false,
26431
+ durationMs: Date.now() - t0,
26432
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26433
+ });
26434
+ throw err;
26435
+ }
25424
26436
  }
25425
26437
  // ── Internals ──────────────────────────────────────────────────────────
25426
26438
  filePath(sessionId) {
@@ -25428,16 +26440,21 @@ var AnnotationsStore = class {
25428
26440
  }
25429
26441
  async readFile(sessionId) {
25430
26442
  const fp = this.filePath(sessionId);
26443
+ let raw;
26444
+ try {
26445
+ raw = await fsp3.readFile(fp, "utf8");
26446
+ } catch (err) {
26447
+ if (err.code === "ENOENT") return null;
26448
+ throw err;
26449
+ }
25431
26450
  try {
25432
- const raw = await fsp3.readFile(fp, "utf8");
25433
26451
  const parsed = JSON.parse(raw);
25434
26452
  if (parsed.version !== FILE_VERSION) {
25435
26453
  return { version: FILE_VERSION, annotations: [] };
25436
26454
  }
25437
26455
  return parsed;
25438
- } catch (err) {
25439
- if (err.code === "ENOENT") return null;
25440
- return { version: FILE_VERSION, annotations: [] };
26456
+ } catch {
26457
+ return null;
25441
26458
  }
25442
26459
  }
25443
26460
  async writeFile(sessionId, file) {
@@ -25494,9 +26511,20 @@ function hashRequest(request) {
25494
26511
  const digest = createHash("sha256").update(json, "utf8").digest("hex");
25495
26512
  return `sha256:${digest}`;
25496
26513
  }
26514
+
26515
+ // src/storage/replay-log-store.ts
26516
+ function storageErrorString2(err) {
26517
+ if (err instanceof Error) {
26518
+ const code = err.code;
26519
+ return code ? `${code}: ${err.message}` : err.message;
26520
+ }
26521
+ return String(err);
26522
+ }
25497
26523
  var DEFAULT_MAX_ENTRIES = 1e3;
25498
26524
  var ReplayLogStore = class {
25499
26525
  dir;
26526
+ events;
26527
+ traceId;
25500
26528
  writeChains = /* @__PURE__ */ new Map();
25501
26529
  /** Per-session hash → entry index, kept in memory after the first load. */
25502
26530
  cache = /* @__PURE__ */ new Map();
@@ -25505,6 +26533,8 @@ var ReplayLogStore = class {
25505
26533
  maxEntries;
25506
26534
  constructor(opts) {
25507
26535
  this.dir = opts.dir;
26536
+ this.events = opts.events;
26537
+ this.traceId = opts.traceId;
25508
26538
  this.maxEntries = opts.maxEntries ?? DEFAULT_MAX_ENTRIES;
25509
26539
  }
25510
26540
  // ── Writes ──────────────────────────────────────────────────────────────
@@ -25515,38 +26545,43 @@ var ReplayLogStore = class {
25515
26545
  */
25516
26546
  async record(input) {
25517
26547
  const hash = hashRequest(input.request);
25518
- await this.enqueue(input.sessionId, async () => {
25519
- await withFileLock(this.filePath(input.sessionId), async () => {
25520
- const entries = await this.readAll(input.sessionId);
25521
- if (entries.some((entry2) => entry2.hash === hash)) return;
25522
- const entry = {
25523
- hash,
25524
- ts: (/* @__PURE__ */ new Date()).toISOString(),
25525
- request: input.request,
25526
- response: input.response
25527
- };
25528
- entries.push(entry);
25529
- const keep = entries.slice(-this.maxEntries);
25530
- const cache = /* @__PURE__ */ new Map();
25531
- for (const e of keep) cache.set(e.hash, e);
25532
- this.cache.set(input.sessionId, cache);
25533
- await this.rewriteCache(input.sessionId, cache);
26548
+ const fp = this.filePath(input.sessionId);
26549
+ const t0 = Date.now();
26550
+ try {
26551
+ await this.enqueue(input.sessionId, async () => {
26552
+ await withFileLock(this.filePath(input.sessionId), async () => {
26553
+ const entries = await this.readAll(input.sessionId);
26554
+ if (entries.some((entry2) => entry2.hash === hash)) return;
26555
+ const entry = {
26556
+ hash,
26557
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
26558
+ request: input.request,
26559
+ response: input.response
26560
+ };
26561
+ entries.push(entry);
26562
+ const keep = entries.slice(-this.maxEntries);
26563
+ const didEvict = keep.length < entries.length;
26564
+ const cache = /* @__PURE__ */ new Map();
26565
+ for (const e of keep) cache.set(e.hash, e);
26566
+ this.cache.set(input.sessionId, cache);
26567
+ await this.writeAll(input.sessionId, keep, didEvict ? "compact" : "record");
26568
+ });
25534
26569
  });
25535
- });
25536
- return hash;
25537
- }
25538
- /**
25539
- * Compact the replay log to keep only the most recent maxEntries.
25540
- * Called when entry count exceeds the cap. Rewrites the entire file
25541
- * but only happens O(n / maxEntries) times per session.
25542
- */
25543
- async rewriteCache(sessionId, cache) {
25544
- const all = [...cache.values()];
25545
- const keep = all.slice(-this.maxEntries);
25546
- await this.writeAll(sessionId, keep);
25547
- cache.clear();
25548
- for (const e of keep) cache.set(e.hash, e);
25549
- this.diskCount.set(sessionId, keep.length);
26570
+ return hash;
26571
+ } catch (err) {
26572
+ this.events?.emit("storage.error", {
26573
+ sessionId: input.sessionId,
26574
+ store: "replay",
26575
+ filePath: fp,
26576
+ operation: "record",
26577
+ outcome: "failure",
26578
+ error: storageErrorString2(err),
26579
+ recoverable: false,
26580
+ durationMs: Date.now() - t0,
26581
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26582
+ });
26583
+ throw err;
26584
+ }
25550
26585
  }
25551
26586
  // ── Reads ───────────────────────────────────────────────────────────────
25552
26587
  /**
@@ -25555,13 +26590,65 @@ var ReplayLogStore = class {
25555
26590
  * per session (in-memory cache).
25556
26591
  */
25557
26592
  async lookup(sessionId, hash) {
25558
- const cache = await this.ensureCache(sessionId);
25559
- return cache.get(hash) ?? null;
26593
+ const fp = this.filePath(sessionId);
26594
+ const t0 = Date.now();
26595
+ try {
26596
+ const cache = await this.ensureCache(sessionId);
26597
+ this.events?.emit("storage.read", {
26598
+ sessionId,
26599
+ store: "replay",
26600
+ filePath: fp,
26601
+ operation: "lookup",
26602
+ outcome: "success",
26603
+ durationMs: Date.now() - t0,
26604
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26605
+ });
26606
+ return cache.get(hash) ?? null;
26607
+ } catch (err) {
26608
+ this.events?.emit("storage.read", {
26609
+ sessionId,
26610
+ store: "replay",
26611
+ filePath: fp,
26612
+ operation: "lookup",
26613
+ outcome: "failure",
26614
+ durationMs: Date.now() - t0,
26615
+ error: storageErrorString2(err),
26616
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26617
+ });
26618
+ throw err;
26619
+ }
25560
26620
  }
25561
26621
  /** All recorded entries for a session, in insertion order. */
25562
26622
  async load(sessionId) {
25563
- const cache = await this.ensureCache(sessionId);
25564
- return [...cache.values()];
26623
+ const fp = this.filePath(sessionId);
26624
+ const t0 = Date.now();
26625
+ try {
26626
+ const cache = await this.ensureCache(sessionId);
26627
+ const durationMs = Date.now() - t0;
26628
+ this.events?.emit("storage.read", {
26629
+ sessionId,
26630
+ store: "replay",
26631
+ filePath: fp,
26632
+ operation: "load",
26633
+ outcome: "success",
26634
+ durationMs,
26635
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26636
+ });
26637
+ return [...cache.values()];
26638
+ } catch (err) {
26639
+ const durationMs = Date.now() - t0;
26640
+ this.events?.emit("storage.read", {
26641
+ sessionId,
26642
+ store: "replay",
26643
+ filePath: fp,
26644
+ operation: "load",
26645
+ outcome: "failure",
26646
+ durationMs,
26647
+ error: storageErrorString2(err),
26648
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26649
+ });
26650
+ throw err;
26651
+ }
25565
26652
  }
25566
26653
  /**
25567
26654
  * List every session id that has a replay log in the store dir.
@@ -25631,13 +26718,24 @@ var ReplayLogStore = class {
25631
26718
  return out;
25632
26719
  } catch (err) {
25633
26720
  if (err.code === "ENOENT") return [];
25634
- return [];
26721
+ throw err;
25635
26722
  }
25636
26723
  }
25637
- async writeAll(sessionId, entries) {
26724
+ async writeAll(sessionId, entries, operation = "record") {
25638
26725
  const fp = this.filePath(sessionId);
26726
+ const t0 = Date.now();
25639
26727
  const body = entries.map((e) => JSON.stringify(e)).join("\n") + (entries.length ? "\n" : "");
25640
26728
  await atomicWrite(fp, body);
26729
+ const durationMs = Date.now() - t0;
26730
+ this.events?.emit("storage.write", {
26731
+ sessionId,
26732
+ store: "replay",
26733
+ filePath: fp,
26734
+ operation,
26735
+ outcome: "success",
26736
+ durationMs,
26737
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26738
+ });
25641
26739
  }
25642
26740
  async ensureCache(sessionId) {
25643
26741
  let cache = this.cache.get(sessionId);
@@ -25818,6 +26916,8 @@ var GENESIS_PREV = "0".repeat(64);
25818
26916
  var DEFAULT_FSYNC_EVERY = 100;
25819
26917
  var ToolAuditLog = class {
25820
26918
  dir;
26919
+ events;
26920
+ traceId;
25821
26921
  /** In-memory cache of the last entry's hash (per session), to compute chains efficiently. */
25822
26922
  tailHash = /* @__PURE__ */ new Map();
25823
26923
  /** In-memory counter for entry indices — avoids re-reading the file on every write. */
@@ -25828,6 +26928,8 @@ var ToolAuditLog = class {
25828
26928
  fsyncEvery;
25829
26929
  constructor(opts) {
25830
26930
  this.dir = opts.dir;
26931
+ this.events = opts.events;
26932
+ this.traceId = opts.traceId;
25831
26933
  this.fsyncEvery = opts.fsyncEvery ?? DEFAULT_FSYNC_EVERY;
25832
26934
  }
25833
26935
  /**
@@ -25838,96 +26940,180 @@ var ToolAuditLog = class {
25838
26940
  */
25839
26941
  async record(input) {
25840
26942
  let entry;
25841
- await this.enqueue(input.sessionId, async () => {
25842
- await withFileLock(this.filePath(input.sessionId), async () => {
25843
- const entries = await this.readAll(input.sessionId);
25844
- const prev = entries.at(-1);
25845
- const prevHash = prev?.hash ?? GENESIS_PREV;
25846
- const index = prev ? prev.index + 1 : 0;
25847
- const id = randomUUID();
25848
- const ts = (/* @__PURE__ */ new Date()).toISOString();
25849
- const content = {
25850
- id,
25851
- ts,
25852
- prevHash,
25853
- toolName: input.toolName,
25854
- toolUseId: input.toolUseId,
25855
- input: input.input,
25856
- output: input.output,
25857
- isError: input.isError,
25858
- index
25859
- };
25860
- const hash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
25861
- entry = {
25862
- id,
25863
- ts,
25864
- prevHash,
25865
- hash,
25866
- toolName: input.toolName,
25867
- toolUseId: input.toolUseId,
25868
- input: input.input,
25869
- output: input.output,
25870
- isError: input.isError,
25871
- index
25872
- };
25873
- entries.push(entry);
25874
- await this.writeAll(input.sessionId, entries);
25875
- this.tailHash.set(input.sessionId, hash);
25876
- this.tailIndex.set(input.sessionId, index + 1);
26943
+ const fp = this.filePath(input.sessionId);
26944
+ const t0 = Date.now();
26945
+ try {
26946
+ await this.enqueue(input.sessionId, async () => {
26947
+ await withFileLock(fp, async () => {
26948
+ const entries = await this.readAll(input.sessionId);
26949
+ const prev = entries.at(-1);
26950
+ const prevHash = prev?.hash ?? GENESIS_PREV;
26951
+ const index = prev ? prev.index + 1 : 0;
26952
+ const id = randomUUID();
26953
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
26954
+ const content = {
26955
+ id,
26956
+ ts,
26957
+ prevHash,
26958
+ toolName: input.toolName,
26959
+ toolUseId: input.toolUseId,
26960
+ input: input.input,
26961
+ output: input.output,
26962
+ isError: input.isError,
26963
+ index
26964
+ };
26965
+ const hash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
26966
+ entry = {
26967
+ id,
26968
+ ts,
26969
+ prevHash,
26970
+ hash,
26971
+ toolName: input.toolName,
26972
+ toolUseId: input.toolUseId,
26973
+ input: input.input,
26974
+ output: input.output,
26975
+ isError: input.isError,
26976
+ index
26977
+ };
26978
+ entries.push(entry);
26979
+ await this.writeAll(input.sessionId, entries);
26980
+ this.tailHash.set(input.sessionId, hash);
26981
+ this.tailIndex.set(input.sessionId, index + 1);
26982
+ const durationMs = Date.now() - t0;
26983
+ this.events?.emit("storage.write", {
26984
+ sessionId: input.sessionId,
26985
+ store: "audit",
26986
+ filePath: fp,
26987
+ operation: "record",
26988
+ outcome: "success",
26989
+ durationMs,
26990
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
26991
+ });
26992
+ });
25877
26993
  });
25878
- });
25879
- return entry;
26994
+ return entry;
26995
+ } catch (err) {
26996
+ this.events?.emit("storage.error", {
26997
+ sessionId: input.sessionId,
26998
+ store: "audit",
26999
+ filePath: fp,
27000
+ operation: "record",
27001
+ outcome: "failure",
27002
+ error: err instanceof Error ? err.message : String(err),
27003
+ recoverable: false,
27004
+ durationMs: Date.now() - t0,
27005
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
27006
+ });
27007
+ throw err;
27008
+ }
25880
27009
  }
25881
27010
  /**
25882
27011
  * Walk the chain and verify every entry's hash and prevHash.
25883
27012
  * Returns a structured verdict — never throws.
25884
27013
  */
25885
27014
  async verify(sessionId) {
25886
- const entries = await this.readAll(sessionId);
25887
- if (entries.length === 0) return { ok: true, entries: 0 };
25888
- if (entries[0]?.prevHash !== GENESIS_PREV) {
25889
- return {
25890
- ok: false,
25891
- brokenAt: 0,
25892
- reason: "first entry is not the genesis (prevHash != 0\u20260)"
25893
- };
27015
+ const fp = this.filePath(sessionId);
27016
+ const t0 = Date.now();
27017
+ let entries;
27018
+ try {
27019
+ entries = await this.readAll(sessionId);
27020
+ } catch (err) {
27021
+ this.events?.emit("storage.read", {
27022
+ sessionId,
27023
+ store: "audit",
27024
+ filePath: fp,
27025
+ operation: "verify",
27026
+ outcome: "failure",
27027
+ durationMs: Date.now() - t0,
27028
+ error: err instanceof Error ? err.message : String(err),
27029
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
27030
+ });
27031
+ return { ok: true, entries: 0 };
25894
27032
  }
25895
- let prevHash = GENESIS_PREV;
25896
- for (let i = 0; i < entries.length; i++) {
25897
- const e = expectDefined(entries[i]);
25898
- if (e.prevHash !== prevHash) {
27033
+ const verdict = (() => {
27034
+ if (entries.length === 0) return { ok: true, entries: 0 };
27035
+ if (entries[0]?.prevHash !== GENESIS_PREV) {
25899
27036
  return {
25900
27037
  ok: false,
25901
- brokenAt: i,
25902
- reason: `prevHash mismatch at entry ${i} (expected ${prevHash.slice(0, 8)}\u2026, got ${e.prevHash.slice(0, 8)}\u2026)`
27038
+ brokenAt: 0,
27039
+ reason: "first entry is not the genesis (prevHash != 0\u20260)"
25903
27040
  };
25904
27041
  }
25905
- const content = {
25906
- id: e.id,
25907
- ts: e.ts,
25908
- prevHash: e.prevHash,
25909
- toolName: e.toolName,
25910
- toolUseId: e.toolUseId,
25911
- input: e.input,
25912
- output: e.output,
25913
- isError: e.isError,
25914
- index: e.index
25915
- };
25916
- const expectedHash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
25917
- if (expectedHash !== e.hash) {
25918
- return {
25919
- ok: false,
25920
- brokenAt: i,
25921
- reason: `hash mismatch at entry ${i} (entry content was modified)`
27042
+ let prevHash = GENESIS_PREV;
27043
+ for (let i = 0; i < entries.length; i++) {
27044
+ const e = expectDefined(entries[i]);
27045
+ if (e.prevHash !== prevHash) {
27046
+ return {
27047
+ ok: false,
27048
+ brokenAt: i,
27049
+ reason: `prevHash mismatch at entry ${i} (expected ${prevHash.slice(0, 8)}\u2026, got ${e.prevHash.slice(0, 8)}\u2026)`
27050
+ };
27051
+ }
27052
+ const content = {
27053
+ id: e.id,
27054
+ ts: e.ts,
27055
+ prevHash: e.prevHash,
27056
+ toolName: e.toolName,
27057
+ toolUseId: e.toolUseId,
27058
+ input: e.input,
27059
+ output: e.output,
27060
+ isError: e.isError,
27061
+ index: e.index
25922
27062
  };
27063
+ const expectedHash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
27064
+ if (expectedHash !== e.hash) {
27065
+ return {
27066
+ ok: false,
27067
+ brokenAt: i,
27068
+ reason: `hash mismatch at entry ${i} (entry content was modified)`
27069
+ };
27070
+ }
27071
+ prevHash = e.hash;
25923
27072
  }
25924
- prevHash = e.hash;
25925
- }
25926
- return { ok: true, entries: entries.length };
27073
+ return { ok: true, entries: entries.length };
27074
+ })();
27075
+ this.events?.emit("storage.read", {
27076
+ sessionId,
27077
+ store: "audit",
27078
+ filePath: fp,
27079
+ operation: "verify",
27080
+ outcome: verdict.ok ? "success" : "failure",
27081
+ durationMs: Date.now() - t0,
27082
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
27083
+ });
27084
+ return verdict;
25927
27085
  }
25928
27086
  /** All entries for a session, in insertion order. */
25929
27087
  async load(sessionId) {
25930
- return this.readAll(sessionId);
27088
+ const fp = this.filePath(sessionId);
27089
+ const t0 = Date.now();
27090
+ try {
27091
+ const entries = await this.readAll(sessionId);
27092
+ const durationMs = Date.now() - t0;
27093
+ this.events?.emit("storage.read", {
27094
+ sessionId,
27095
+ store: "audit",
27096
+ filePath: fp,
27097
+ operation: "load",
27098
+ outcome: "success",
27099
+ durationMs,
27100
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
27101
+ });
27102
+ return entries;
27103
+ } catch (err) {
27104
+ const durationMs = Date.now() - t0;
27105
+ this.events?.emit("storage.read", {
27106
+ sessionId,
27107
+ store: "audit",
27108
+ filePath: fp,
27109
+ operation: "load",
27110
+ outcome: "failure",
27111
+ durationMs,
27112
+ error: err instanceof Error ? err.message : String(err),
27113
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
27114
+ });
27115
+ throw err;
27116
+ }
25931
27117
  }
25932
27118
  // ── Internals ────────────────────────────────────────────────────────────
25933
27119
  filePath(sessionId) {
@@ -25949,7 +27135,7 @@ var ToolAuditLog = class {
25949
27135
  return out;
25950
27136
  } catch (err) {
25951
27137
  if (err.code === "ENOENT") return [];
25952
- return [];
27138
+ throw err;
25953
27139
  }
25954
27140
  }
25955
27141
  async writeAll(sessionId, entries) {
@@ -26349,37 +27535,98 @@ function emptyTaskFile(sessionId) {
26349
27535
  tasks: []
26350
27536
  };
26351
27537
  }
26352
- async function loadTasks(filePath) {
27538
+ async function loadTasks(filePath, events, traceId) {
27539
+ const t0 = Date.now();
26353
27540
  let raw;
26354
27541
  try {
26355
27542
  raw = await fsp3.readFile(filePath, "utf8");
26356
- } catch {
27543
+ } catch (err) {
27544
+ events?.emit("storage.error", {
27545
+ sessionId: traceId ?? "~boot~",
27546
+ store: "tasks",
27547
+ filePath,
27548
+ operation: "load",
27549
+ outcome: "failure",
27550
+ error: err instanceof Error ? err.message : String(err),
27551
+ recoverable: true
27552
+ });
26357
27553
  return null;
26358
27554
  }
26359
27555
  try {
26360
27556
  const parsed = JSON.parse(raw);
26361
- if (parsed?.version !== 1 || !Array.isArray(parsed.tasks)) return null;
27557
+ if (parsed?.version !== 1 || !Array.isArray(parsed.tasks)) {
27558
+ events?.emit("storage.read", {
27559
+ sessionId: traceId ?? "~boot~",
27560
+ store: "tasks",
27561
+ filePath,
27562
+ operation: "load",
27563
+ outcome: "failure",
27564
+ durationMs: Date.now() - t0,
27565
+ error: "invalid_schema",
27566
+ ...traceId !== void 0 && { traceId }
27567
+ });
27568
+ return null;
27569
+ }
27570
+ events?.emit("storage.read", {
27571
+ sessionId: traceId ?? "~boot~",
27572
+ store: "tasks",
27573
+ filePath,
27574
+ operation: "load",
27575
+ outcome: "success",
27576
+ durationMs: Date.now() - t0,
27577
+ ...traceId !== void 0 && { traceId }
27578
+ });
26362
27579
  return parsed;
26363
27580
  } catch {
27581
+ events?.emit("storage.read", {
27582
+ sessionId: traceId ?? "~boot~",
27583
+ store: "tasks",
27584
+ filePath,
27585
+ operation: "load",
27586
+ outcome: "failure",
27587
+ durationMs: Date.now() - t0,
27588
+ error: "parse_failed",
27589
+ ...traceId !== void 0 && { traceId }
27590
+ });
26364
27591
  return null;
26365
27592
  }
26366
27593
  }
26367
- async function saveTasks(filePath, tasks) {
27594
+ async function saveTasks(filePath, tasks, events, traceId) {
27595
+ const t0 = Date.now();
26368
27596
  try {
26369
27597
  tasks.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
26370
27598
  await atomicWrite(filePath, JSON.stringify(tasks, null, 2), { mode: 384 });
27599
+ events?.emit("storage.write", {
27600
+ sessionId: traceId ?? "~boot~",
27601
+ store: "tasks",
27602
+ filePath,
27603
+ operation: "save",
27604
+ outcome: "success",
27605
+ durationMs: Date.now() - t0,
27606
+ ...traceId !== void 0 && { traceId }
27607
+ });
26371
27608
  } catch (err) {
27609
+ events?.emit("storage.error", {
27610
+ sessionId: traceId ?? "~boot~",
27611
+ store: "tasks",
27612
+ filePath,
27613
+ operation: "save",
27614
+ outcome: "failure",
27615
+ error: err instanceof Error ? err.message : String(err),
27616
+ recoverable: false,
27617
+ ...traceId !== void 0 && { traceId }
27618
+ });
26372
27619
  console.warn(
26373
27620
  "[task-store] save failed:",
26374
27621
  err instanceof Error ? err.message : String(err)
26375
27622
  );
26376
27623
  }
26377
27624
  }
26378
- async function mutateTasks(filePath, sessionId, fn) {
27625
+ async function mutateTasks(filePath, sessionId, fn, events, traceId) {
26379
27626
  return withFileLock(filePath, async () => {
26380
- const file = await loadTasks(filePath) ?? emptyTaskFile(sessionId);
27627
+ const file = await loadTasks(filePath, events, traceId) ?? emptyTaskFile(sessionId);
26381
27628
  const updated = await fn(file);
26382
- await saveTasks(filePath, updated);
27629
+ await saveTasks(filePath, updated, events, traceId);
26383
27630
  return updated;
26384
27631
  });
26385
27632
  }
@@ -30686,8 +31933,9 @@ function buildMailboxBlock(messages) {
30686
31933
  const parts = [];
30687
31934
  parts.push("[MAILBOX] New message(s) from other agents:");
30688
31935
  parts.push("");
31936
+ const hasActionable = messages.some((m) => m.type === "ask" || m.type === "assign" || m.type === "result");
30689
31937
  for (const m of messages) {
30690
- const typeLabel = m.type === "steer" ? "\u{1F504} STEER" : m.type === "btw" ? "\u{1F4AC} BTW" : `\u{1F4E8} ${m.type.toUpperCase()}`;
31938
+ const typeLabel = m.type === "steer" ? "\u{1F504} STEER" : m.type === "btw" ? "\u{1F4AC} BTW" : m.type === "ask" ? "\u2753 ASK" : m.type === "assign" ? "\u{1F4CB} ASSIGN" : m.type === "result" ? "\u2705 RESULT" : `\u{1F4E8} ${m.type.toUpperCase()}`;
30691
31939
  parts.push(`--- ${typeLabel} from ${m.from} ---`);
30692
31940
  parts.push(`Subject: ${m.subject}`);
30693
31941
  parts.push("");
@@ -30697,6 +31945,22 @@ function buildMailboxBlock(messages) {
30697
31945
  parts.push("After your current operation reaches a stopping point, adjust your approach per the instruction above.");
30698
31946
  parts.push("");
30699
31947
  }
31948
+ if (m.type === "ask") {
31949
+ parts.push("\u21B3 This agent is waiting for your answer. Reply directly or use mailbox action=send to respond.");
31950
+ parts.push("");
31951
+ }
31952
+ if (m.type === "assign") {
31953
+ parts.push("\u21B3 This is a task assignment. Act on it when your current operation allows.");
31954
+ parts.push("");
31955
+ }
31956
+ if (m.type === "result") {
31957
+ parts.push("\u21B3 A subagent has completed its work. Factor this result into your next decision.");
31958
+ parts.push("");
31959
+ }
31960
+ }
31961
+ if (hasActionable) {
31962
+ parts.push("Action required: address the items above. When done, use `mailbox action=ack messageId=<id> completed=true` to mark them complete.");
31963
+ parts.push("");
30700
31964
  }
30701
31965
  parts.push("[END MAILBOX]");
30702
31966
  return { type: "text", text: parts.join("\n") };
@@ -30717,26 +31981,12 @@ async function injectPendingMailboxMessages(checkMailbox, foldFn, a) {
30717
31981
  });
30718
31982
  }
30719
31983
  if (messages.length === 0) return;
30720
- const injectable = messages.filter((m) => m.type === "steer" || m.type === "btw");
30721
- const others = messages.filter((m) => m.type !== "steer" && m.type !== "btw");
30722
- if (injectable.length > 0) {
30723
- try {
30724
- foldFn(buildMailboxBlock(injectable));
30725
- } catch (err) {
30726
- (a.logger.debug ?? console.debug)?.(
30727
- `mailbox: failed to fold messages: ${err instanceof Error ? err.message : String(err)}`
30728
- );
30729
- }
30730
- }
30731
- if (others.length > 0) {
30732
- const otherSubjects = others.map((m) => ` - [${m.type}] ${m.from}: ${m.subject}`).join("\n");
30733
- const note = `[MAILBOX] You have ${others.length} other unread message(s). Use \`mailbox action=check\` to read them:
30734
- ${otherSubjects}
30735
- [END MAILBOX]`;
30736
- try {
30737
- foldFn({ type: "text", text: note });
30738
- } catch {
30739
- }
31984
+ try {
31985
+ foldFn(buildMailboxBlock(messages));
31986
+ } catch (err) {
31987
+ (a.logger.debug ?? console.debug)?.(
31988
+ `mailbox: failed to fold messages: ${err instanceof Error ? err.message : String(err)}`
31989
+ );
30740
31990
  }
30741
31991
  }
30742
31992
 
@@ -31751,9 +33001,26 @@ async function writeProjectMeta(paths, projectRoot) {
31751
33001
  } catch {
31752
33002
  }
31753
33003
  }
31754
- async function registerProjectInManifest(paths, projectRoot) {
33004
+ async function registerProjectInManifest(paths, projectRoot, events) {
33005
+ const manifestPath = path7.join(paths.globalRoot, "projects.json");
33006
+ try {
33007
+ const t0 = Date.now();
33008
+ const raw = await fsp3.readFile(manifestPath, "utf8");
33009
+ events?.emit("storage.read", {
33010
+ sessionId: "~boot~",
33011
+ store: "project",
33012
+ filePath: manifestPath,
33013
+ operation: "manifest_read",
33014
+ outcome: "success",
33015
+ durationMs: Date.now() - t0
33016
+ });
33017
+ try {
33018
+ JSON.parse(raw);
33019
+ } catch {
33020
+ }
33021
+ } catch (err) {
33022
+ }
31755
33023
  try {
31756
- const manifestPath = path7.join(paths.globalRoot, "projects.json");
31757
33024
  let manifest;
31758
33025
  try {
31759
33026
  const raw = await fsp3.readFile(manifestPath, "utf8");
@@ -31770,8 +33037,17 @@ async function registerProjectInManifest(paths, projectRoot) {
31770
33037
  const name = path7.basename(projectRoot);
31771
33038
  manifest.projects.push({ name, root: projectRoot, slug, lastSeen: now, createdAt: now });
31772
33039
  }
33040
+ const writeT0 = Date.now();
31773
33041
  await fsp3.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
31774
- } catch {
33042
+ events?.emit("storage.write", {
33043
+ sessionId: "~boot~",
33044
+ store: "project",
33045
+ filePath: manifestPath,
33046
+ operation: "manifest_write",
33047
+ outcome: "success",
33048
+ durationMs: Date.now() - writeT0
33049
+ });
33050
+ } catch (err) {
31775
33051
  }
31776
33052
  }
31777
33053
  var GITIGNORE_ENTRY = ".wrongstack/\n";
@@ -32036,9 +33312,10 @@ Never silently skip a failure \u2014 always report it, even when you choose not
32036
33312
 
32037
33313
  ## After-task suggestions
32038
33314
 
32039
- After completing a significant task, end your response with 2\u20134 suggested next
32040
- actions in a \`<next_steps>\` block. Use this exact format so the user can
32041
- select them with \`/next 1\`, \`/next 2\`, or \`/next 1 2 3\`:
33315
+ **You are the leader agent.** After completing a significant task, end your
33316
+ response with 2\u20134 suggested next actions in a \`<next_steps>\` block. Use this
33317
+ exact format so the user can select them with \`/next 1\`, \`/next 2\`, or
33318
+ \`/next 1 2 3\`:
32042
33319
 
32043
33320
  \`\`\`
32044
33321
  <next_steps>
@@ -32051,6 +33328,7 @@ select them with \`/next 1\`, \`/next 2\`, or \`/next 1 2 3\`:
32051
33328
  Rules:
32052
33329
  - Each line is a single imperative sentence the user can act on immediately.
32053
33330
  - Be specific: mention file names, tool names, or commands.
33331
+ - **Concrete actions only** \u2014 never write declarations of intent ("we should fix X", "consider refactoring Y") or manual suggestions ("manually check Z"). Write exactly what should be done: "Fix null deref in auth/session.ts:42", "Run pnpm typecheck".
32054
33332
  - Order by priority. Keep each suggestion to one line.
32055
33333
  - Skip during multi-step operations \u2014 only show after completion.
32056
33334
  - If nothing is pending, say "No pending actions."
@@ -32380,8 +33658,17 @@ sender's exact \`from\` id.
32380
33658
  ### Receiving
32381
33659
 
32382
33660
  Unread mail (direct, base-name, and \`*\` broadcasts) is injected into
32383
- your conversation automatically before each step \u2014 urgent steer/btw
32384
- inline, the rest as a summary. To catch up explicitly:
33661
+ your conversation automatically before each step \u2014 ALL message types
33662
+ (steer, btw, ask, assign, result, note) appear inline with a call to
33663
+ action. You do NOT need to manually check the mailbox; subagent results
33664
+ and questions reach you even while you are mid-task.
33665
+
33666
+ When a message includes a call to action:
33667
+ - **ask**: reply to the agent directly or use \`mail_send\` to respond
33668
+ - **assign**: act on the task when your current operation allows
33669
+ - **result**: factor the outcome into your next decision
33670
+
33671
+ To catch up explicitly:
32385
33672
 
32386
33673
  - \`mail_inbox\` \u2014 read your unread mail and mark it read
32387
33674
  - \`mailbox action=query from=<agent> type=result\` \u2014 find specific results
@@ -38351,6 +39638,6 @@ function createChimeraPlugin() {
38351
39638
  };
38352
39639
  }
38353
39640
 
38354
- export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AgentStatusTracker, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BrainMonitor, BudgetExceededError, CHIMERA_REVIEW_PROMPT, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DEPENDENCY_FILE_PATTERNS, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMailbox, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FORBIDDEN_PROTO_KEYS, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GlobalMailbox, GraphMemoryBackend, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, MEMORY_TYPE_LABELS, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddError, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, StreamHangError, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertNever, assertSafePath, atomicWrite, attachAutoExtend, attachDepWatcherBridge, attachMailboxChecker, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildLosslessDigest, buildMailboxBlock, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildQueuedMessagesBlock, buildRecoveryStrategies, buildSmartDigest, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compactLog, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeMessageTokens, computeTaskItemProgress, computeTaskProgress, consumeBtwNotes, consumeQueuedMessagesUpdate, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createAutonomyBrain, createChimeraPlugin, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMailboxChecker, createMcpControlTool, createMcpUseTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createStrategyCompactor, createSyncPlugin, createTieredBrainArbiter, createToolOutputSerializer, decryptConfigSecrets, deepMerge, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, detectEcosystem as detectPackageEcosystem, dispatchAgent, downloadGitHubTarball, eliseOldToolResults, emptyGoal, emptyPlan, emptyTaskFile, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateMessageTokens, estimateMessages, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expectDefined, extractRunEnv, extractText, filesystemServer, findCriticalPath, findPreserveStart, flagsToConfigPatch, formatContextWindowModeList, formatDecisionSummary, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTaskList, formatTaskProgress, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getFileHistory, getFilesByAgent, getFullLog, getFullPackageLog, getLastAuthor, getManifestPackages, getPackageAuthor, getPackagesByAgent, getPlanTemplate, getSessionRegistry, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hasSessionRegistry, hasTextContent, hashRequest, hookMatcherMatches, injectPendingMailboxMessages, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isPrimitiveArray, isSddError, isSecretField, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTasks, loadTodosCheckpoint, loadUserModes, mailboxSessionTag, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, mutatePlan, mutateTasks, noOpLogger, noOpVault, normalizeRecipient, normalizeToLf, normalizedEqual, onResize, parseContinueDirective, parseEntries, parseProgressFromText, parseSkillRef, peekQueuedMessages, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, recordFileAction, recordPackageAction, recordProgress, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveChimeraConfig, resolveContextWindowPolicy, resolveMailboxIdentity, resolveModelMatrix, resolveProjectDir, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, scoreAgents, scoreMessage, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setProgress, setQueuedMessagesSnapshot, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, startPackageOutdatedWatcher, startTechStackConsumer, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, truncate, unifiedDiff, unloadPlugins, updatePackageOutdatedStatus, validateAgainstSchema, wireMetricsToEvents, withFileLock, wrapAsState, writeErr, writeOut, wstackGlobalRoot, zaiVisionServer };
39641
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AgentStatusTracker, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BrainMonitor, BudgetExceededError, CHIMERA_REVIEW_PROMPT, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DEPENDENCY_FILE_PATTERNS, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMailbox, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FORBIDDEN_PROTO_KEYS, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GlobalMailbox, GraphMemoryBackend, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, MEMORY_TYPE_LABELS, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddError, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, StreamHangError, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertNever, assertNotPrivateHost, assertSafePath, atomicWrite, attachAutoExtend, attachDepWatcherBridge, attachMailboxChecker, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildLosslessDigest, buildMailboxBlock, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildQueuedMessagesBlock, buildRecoveryStrategies, buildSmartDigest, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compactLog, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeMessageTokens, computeTaskItemProgress, computeTaskProgress, consumeBtwNotes, consumeQueuedMessagesUpdate, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createAutonomyBrain, createChimeraPlugin, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMailboxChecker, createMcpControlTool, createMcpUseTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createStrategyCompactor, createSyncPlugin, createTieredBrainArbiter, createToolOutputSerializer, decryptConfigSecrets, deepMerge, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, detectEcosystem as detectPackageEcosystem, dispatchAgent, downloadGitHubTarball, eliseOldToolResults, emptyGoal, emptyPlan, emptyTaskFile, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateMessageTokens, estimateMessages, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expandIPv6, expectDefined, extractRunEnv, extractText, filesystemServer, findCriticalPath, findPreserveStart, flagsToConfigPatch, formatContextWindowModeList, formatDecisionSummary, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTaskList, formatTaskProgress, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getFileHistory, getFilesByAgent, getFullLog, getFullPackageLog, getLastAuthor, getManifestPackages, getPackageAuthor, getPackagesByAgent, getPlanTemplate, getSessionRegistry, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hasSessionRegistry, hasTextContent, hashRequest, hookMatcherMatches, injectPendingMailboxMessages, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isPrimitiveArray, isPrivateIPv4, isPrivateIPv6, isSddError, isSecretField, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTasks, loadTodosCheckpoint, loadUserModes, mailboxSessionTag, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, mutatePlan, mutateTasks, noOpLogger, noOpVault, normalizeRecipient, normalizeToLf, normalizedEqual, onResize, parseContinueDirective, parseEntries, parseProgressFromText, parseSkillRef, peekQueuedMessages, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, recordFileAction, recordPackageAction, recordProgress, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveChimeraConfig, resolveContextWindowPolicy, resolveMailboxIdentity, resolveModelMatrix, resolveProjectDir, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, scoreAgents, scoreMessage, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setProgress, setQueuedMessagesSnapshot, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, startPackageOutdatedWatcher, startTechStackConsumer, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, truncate, unifiedDiff, unloadPlugins, updatePackageOutdatedStatus, validateAgainstSchema, wireMetricsToEvents, withFileLock, wrapAsState, writeErr, writeOut, wstackGlobalRoot, zaiVisionServer };
38355
39642
  //# sourceMappingURL=index.js.map
38356
39643
  //# sourceMappingURL=index.js.map