@wrongstack/core 0.89.1 → 0.89.3

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 (81) hide show
  1. package/dist/{agent-bridge-B8F2ORwg.d.ts → agent-bridge-DbVe1dHT.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-CQaLFNnA.d.ts → agent-subagent-runner-C6eqID1t.d.ts} +433 -433
  3. package/dist/{events-DzyLtja6.d.ts → brain-s9DyD_Vf.d.ts} +179 -92
  4. package/dist/{compactor-CJq7LQev.d.ts → compactor-DXLxLcmU.d.ts} +2 -2
  5. package/dist/{config-_DZ7dN-T.d.ts → config-84VaZpH6.d.ts} +45 -38
  6. package/dist/{context-ToHAp4-U.d.ts → context-CNRYfhUv.d.ts} +1 -1
  7. package/dist/coordination/index.d.ts +16 -17
  8. package/dist/coordination/index.js +25 -37
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +31 -33
  11. package/dist/defaults/index.js +476 -206
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/{director-state-CgIc30qi.d.ts → director-state-BfeCUbmk.d.ts} +1 -1
  14. package/dist/execution/index.d.ts +18 -19
  15. package/dist/execution/index.js +23 -45
  16. package/dist/execution/index.js.map +1 -1
  17. package/dist/extension/index.d.ts +9 -10
  18. package/dist/extension/index.js +8 -6
  19. package/dist/extension/index.js.map +1 -1
  20. package/dist/{index-Ca_lOmKl.d.ts → index-CqrroYcU.d.ts} +13 -15
  21. package/dist/{index-Dx-CnrDN.d.ts → index-D3Nax3YU.d.ts} +9 -10
  22. package/dist/index.d.ts +184 -174
  23. package/dist/index.js +973 -349
  24. package/dist/index.js.map +1 -1
  25. package/dist/infrastructure/index.d.ts +9 -9
  26. package/dist/infrastructure/index.js +4 -2
  27. package/dist/infrastructure/index.js.map +1 -1
  28. package/dist/kernel/index.d.ts +14 -16
  29. package/dist/kernel/index.js.map +1 -1
  30. package/dist/{logger-B72yyPc6.d.ts → logger-B63L5bTg.d.ts} +1 -1
  31. package/dist/{logger-C_27pj9i.d.ts → logger-B9J5puGM.d.ts} +1 -1
  32. package/dist/{mcp-servers-Dck3T85_.d.ts → mcp-servers-D3E5ixAR.d.ts} +3 -3
  33. package/dist/{mode-CHo2XtHs.d.ts → mode-ARA3HrkY.d.ts} +1 -1
  34. package/dist/models/index.d.ts +5 -5
  35. package/dist/models/index.js +8 -6
  36. package/dist/models/index.js.map +1 -1
  37. package/dist/{models-registry-Be3osGt5.d.ts → models-registry-B6_KfS65.d.ts} +1 -1
  38. package/dist/{models-registry-Boz639EI.d.ts → models-registry-DU64QxQa.d.ts} +1 -1
  39. package/dist/{multi-agent-coordinator-Da9nd8Ic.d.ts → multi-agent-coordinator-DHNrjPaA.d.ts} +2 -2
  40. package/dist/{null-fleet-bus-CHJOXv3t.d.ts → null-fleet-bus-BzzciAc4.d.ts} +32 -31
  41. package/dist/observability/index.d.ts +3 -3
  42. package/dist/{observability-CoSNZdhX.d.ts → observability-D-HZN_mF.d.ts} +1 -1
  43. package/dist/{parallel-eternal-engine-DyMCj2Pw.d.ts → parallel-eternal-engine-CTpcrb9O.d.ts} +33 -33
  44. package/dist/{path-resolver-TjBlRKyH.d.ts → path-resolver-B7hgMAVe.d.ts} +3 -3
  45. package/dist/{permission-14CChMmO.d.ts → permission-BDv7z0mk.d.ts} +7 -2
  46. package/dist/{permission-policy-gW5htOo1.d.ts → permission-policy-dF74EpDp.d.ts} +2 -2
  47. package/dist/{system-prompt-C0rLCeyn.d.ts → pipeline-C1Ad9OjI.d.ts} +73 -73
  48. package/dist/{plan-templates-D9UhwoGh.d.ts → plan-templates-CNphsz0n.d.ts} +79 -11
  49. package/dist/{provider-runner-BEPWwkxH.d.ts → provider-runner-cqvlB_oS.d.ts} +5 -5
  50. package/dist/{retry-policy-DSu6O6rD.d.ts → retry-policy-BcmuT_V0.d.ts} +2 -2
  51. package/dist/sdd/index.d.ts +12 -33
  52. package/dist/sdd/index.js +21 -31
  53. package/dist/sdd/index.js.map +1 -1
  54. package/dist/{secret-scrubber-yGBFQYju.d.ts → secret-vault-DrOhc2i5.d.ts} +7 -7
  55. package/dist/security/index.d.ts +4 -5
  56. package/dist/security/index.js +4 -2
  57. package/dist/security/index.js.map +1 -1
  58. package/dist/{selector-11-fm95U.d.ts → selector-C7wcdqMA.d.ts} +1 -1
  59. package/dist/{session-event-bridge-D0u-x576.d.ts → session-event-bridge-IVzs2GpE.d.ts} +1 -1
  60. package/dist/{session-reader-BQU-toaN.d.ts → session-reader-DDz1Ek4V.d.ts} +2 -2
  61. package/dist/{skill-BJeF2DwY.d.ts → skill-BJxzIyyN.d.ts} +1 -1
  62. package/dist/skills/index.d.ts +1 -1
  63. package/dist/skills/index.js +3 -9
  64. package/dist/skills/index.js.map +1 -1
  65. package/dist/storage/index.d.ts +132 -12
  66. package/dist/storage/index.js +834 -159
  67. package/dist/storage/index.js.map +1 -1
  68. package/dist/{task-graph-CikNdRTG.d.ts → task-graph-DJBqO0EY.d.ts} +1 -1
  69. package/dist/types/index.d.ts +25 -27
  70. package/dist/types/index.js +19 -31
  71. package/dist/types/index.js.map +1 -1
  72. package/dist/utils/expect-defined.d.ts +7 -0
  73. package/dist/utils/expect-defined.js +11 -0
  74. package/dist/utils/expect-defined.js.map +1 -0
  75. package/dist/utils/index.d.ts +6 -20
  76. package/dist/utils/index.js +11 -41
  77. package/dist/utils/index.js.map +1 -1
  78. package/dist/{wstack-paths-BQMvEllz.d.ts → wstack-paths-_lqjzErq.d.ts} +1 -1
  79. package/package.json +5 -1
  80. package/dist/memory-CEXuo7sz.d.ts +0 -16
  81. package/dist/secret-scrubber-3MHDDAtm.d.ts +0 -6
@@ -5,18 +5,13 @@ import * as path3 from 'path';
5
5
  import { isAbsolute, resolve } from 'path';
6
6
  import * as fs5 from 'fs';
7
7
  import * as os from 'os';
8
+ import { hostname } from 'os';
8
9
  import { execFile } from 'child_process';
9
10
  import { promisify } from 'util';
10
11
  import { EventEmitter } from 'events';
11
12
 
12
13
  var __defProp = Object.defineProperty;
13
14
  var __getOwnPropNames = Object.getOwnPropertyNames;
14
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
15
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
16
- }) : x)(function(x) {
17
- if (typeof require !== "undefined") return require.apply(this, arguments);
18
- throw Error('Dynamic require of "' + x + '" is not supported');
19
- });
20
15
  var __esm = (fn, res) => function __init() {
21
16
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
22
17
  };
@@ -294,16 +289,18 @@ function formatCtx(ctx) {
294
289
  }
295
290
  }
296
291
 
297
- // src/storage/session-store.ts
298
- init_atomic_write();
299
-
300
- // src/utils/message-invariants.ts
301
- function expectDefined(value) {
292
+ // src/utils/expect-defined.ts
293
+ function expectDefined(value, label) {
302
294
  if (value === null || value === void 0) {
303
295
  throw new Error("Expected value to be defined");
304
296
  }
305
297
  return value;
306
298
  }
299
+
300
+ // src/storage/session-store.ts
301
+ init_atomic_write();
302
+
303
+ // src/utils/message-invariants.ts
307
304
  function repairToolUseAdjacency(messages) {
308
305
  const removedToolUses = [];
309
306
  const removedToolResults = [];
@@ -396,12 +393,6 @@ function isEmptyMessage(msg) {
396
393
  }
397
394
 
398
395
  // src/storage/session-store.ts
399
- function expectDefined2(value) {
400
- if (value === null || value === void 0) {
401
- throw new Error("Expected value to be defined");
402
- }
403
- return value;
404
- }
405
396
  function sanitizeModel(model) {
406
397
  return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
407
398
  }
@@ -1112,7 +1103,7 @@ var FileSessionWriter = class {
1112
1103
  let targetCheckpointLine = -1;
1113
1104
  let afterTarget = false;
1114
1105
  for (let i = 0; i < lines.length; i++) {
1115
- const line = expectDefined2(lines[i]);
1106
+ const line = expectDefined(lines[i]);
1116
1107
  if (!line.trim()) continue;
1117
1108
  let event;
1118
1109
  try {
@@ -1408,11 +1399,235 @@ function mergeAdjacentText(blocks) {
1408
1399
  return out;
1409
1400
  }
1410
1401
 
1411
- // src/storage/memory-store.ts
1402
+ // src/types/memory.ts
1403
+ var MEMORY_TYPE_LABELS = {
1404
+ fact: "Fact",
1405
+ decision: "Decision",
1406
+ convention: "Convention",
1407
+ preference: "Preference",
1408
+ reference: "Reference",
1409
+ anti_pattern: "Anti-pattern"
1410
+ };
1411
+
1412
+ // src/storage/memory-backend.ts
1412
1413
  init_atomic_write();
1414
+ var TYPE_PRIORITY_RE = /^\[(\w+)\|(\w+)\]\s+/;
1415
+ var TAG_RE = /#([\w-]+)/g;
1416
+ function formatMetadata(entry) {
1417
+ const parts = [];
1418
+ if (entry.type && entry.priority) {
1419
+ parts.push(`[${entry.type}|${entry.priority}]`);
1420
+ } else if (entry.type) {
1421
+ parts.push(`[${entry.type}]`);
1422
+ } else if (entry.priority) {
1423
+ parts.push(`[${entry.priority}]`);
1424
+ }
1425
+ if (entry.tags && entry.tags.length > 0) {
1426
+ parts.push(entry.tags.map((t) => `#${t}`).join(" "));
1427
+ }
1428
+ return parts.length > 0 ? ` ${parts.join(" ")}` : "";
1429
+ }
1430
+ function lineToEntry(line, scope) {
1431
+ const trimmed = line.trim();
1432
+ if (!trimmed.startsWith("- [")) return null;
1433
+ const tsMatch = trimmed.match(/^-\s*\[([^\]]+)\]/);
1434
+ if (!tsMatch) return null;
1435
+ const ts = tsMatch[1] ?? "";
1436
+ let rest = trimmed.slice(tsMatch[0].length).trim();
1437
+ let type;
1438
+ let priority;
1439
+ const tpMatch = rest.match(TYPE_PRIORITY_RE);
1440
+ if (tpMatch) {
1441
+ const a = tpMatch[1] ?? "";
1442
+ const b = tpMatch[2] ?? "";
1443
+ if (isMemoryType(a)) {
1444
+ type = a;
1445
+ priority = isPriority(b) ? b : void 0;
1446
+ } else if (isPriority(a)) {
1447
+ priority = a;
1448
+ }
1449
+ rest = rest.slice(tpMatch[0].length).trim();
1450
+ }
1451
+ const idMatch = rest.match(/^mem_\d+_\w+\s+/);
1452
+ let text;
1453
+ if (idMatch) {
1454
+ text = rest.slice(idMatch[0].length).trim();
1455
+ } else {
1456
+ text = rest.trim();
1457
+ }
1458
+ const tags = [];
1459
+ let tagMatch;
1460
+ TAG_RE.lastIndex = 0;
1461
+ while ((tagMatch = TAG_RE.exec(text)) !== null) {
1462
+ tags.push(tagMatch[1] ?? "");
1463
+ }
1464
+ const cleanText = text.replace(TAG_RE, "").replace(/\s{2,}/g, " ").trim();
1465
+ if (!cleanText) return null;
1466
+ return {
1467
+ scope,
1468
+ text: cleanText,
1469
+ ts,
1470
+ type,
1471
+ priority,
1472
+ tags: tags.length > 0 ? tags : void 0
1473
+ };
1474
+ }
1475
+ function isMemoryType(s) {
1476
+ return s in MEMORY_TYPE_LABELS;
1477
+ }
1478
+ function isPriority(s) {
1479
+ return s === "critical" || s === "high" || s === "medium" || s === "low";
1480
+ }
1481
+ var FileMemoryBackend = class {
1482
+ kind = "file";
1483
+ files;
1484
+ constructor(opts) {
1485
+ this.files = {
1486
+ "project-agents": opts.paths.inProjectAgentsFile,
1487
+ "project-memory": opts.paths.projectMemory,
1488
+ "user-memory": opts.paths.globalMemory
1489
+ };
1490
+ }
1491
+ resolveFile(filePath, scope) {
1492
+ return filePath || this.files[scope];
1493
+ }
1494
+ async remember(scope, entry, filePath) {
1495
+ const file = this.resolveFile(filePath, scope);
1496
+ await ensureDir(path3.dirname(file));
1497
+ let existing = "";
1498
+ try {
1499
+ existing = await fsp.readFile(file, "utf8");
1500
+ } catch {
1501
+ }
1502
+ const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
1503
+ const meta = formatMetadata(entry);
1504
+ const line = `
1505
+ - [${entry.ts}] ${id}${meta} ${entry.text.replace(/\n/g, " ")}
1506
+ `;
1507
+ const next = existing.trim() ? existing.replace(/\n+$/, "") + line : `# WrongStack Memory
1508
+ ${line}`;
1509
+ await atomicWrite(file, next);
1510
+ }
1511
+ async forget(scope, query, filePath) {
1512
+ const file = this.resolveFile(filePath, scope);
1513
+ let existing;
1514
+ try {
1515
+ existing = await fsp.readFile(file, "utf8");
1516
+ } catch {
1517
+ return 0;
1518
+ }
1519
+ const needle = query.toLowerCase();
1520
+ const idMatcher = /mem_\d+_\w+/;
1521
+ let removed = 0;
1522
+ const lines = existing.split("\n").filter((line) => {
1523
+ const trimmed = line.trim();
1524
+ if (!trimmed.startsWith("- ")) return true;
1525
+ if (idMatcher.test(query)) {
1526
+ const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
1527
+ if (entryIdMatch && entryIdMatch[0] === query) {
1528
+ removed++;
1529
+ return false;
1530
+ }
1531
+ }
1532
+ if (trimmed.toLowerCase().includes(needle)) {
1533
+ removed++;
1534
+ return false;
1535
+ }
1536
+ return true;
1537
+ });
1538
+ if (removed > 0) {
1539
+ if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
1540
+ await atomicWrite(file, "");
1541
+ } else {
1542
+ await atomicWrite(file, lines.join("\n"));
1543
+ }
1544
+ }
1545
+ return removed;
1546
+ }
1547
+ async readAll(scope, filePath) {
1548
+ const file = this.resolveFile(filePath, scope);
1549
+ try {
1550
+ return await fsp.readFile(file, "utf8");
1551
+ } catch {
1552
+ return "";
1553
+ }
1554
+ }
1555
+ async list(scope, filePath, limit) {
1556
+ const raw = await this.readAll(scope, filePath);
1557
+ if (!raw.trim()) return [];
1558
+ const entries = parseEntries(raw, scope);
1559
+ return limit ? entries.slice(0, limit) : entries;
1560
+ }
1561
+ async search(scope, query, filePath, limit) {
1562
+ const entries = await this.list(scope, filePath);
1563
+ const needle = query.toLowerCase().split(/\s+/);
1564
+ const scored = entries.map((e) => {
1565
+ const words = e.text.toLowerCase().split(/\s+/);
1566
+ let score = 0;
1567
+ for (const n of needle) {
1568
+ if (words.some((w) => w.includes(n))) score += 1;
1569
+ if (e.tags?.some((t) => t.toLowerCase().includes(n))) score += 2;
1570
+ }
1571
+ return { entry: e, score };
1572
+ });
1573
+ scored.sort((a, b) => b.score - a.score);
1574
+ const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
1575
+ return limit ? matched.slice(0, limit) : matched;
1576
+ }
1577
+ async clear(scope, filePath) {
1578
+ const file = this.resolveFile(filePath, scope);
1579
+ await atomicWrite(file, "");
1580
+ }
1581
+ async consolidate(scope, filePath) {
1582
+ const file = this.resolveFile(filePath, scope);
1583
+ let existing;
1584
+ try {
1585
+ existing = await fsp.readFile(file, "utf8");
1586
+ } catch {
1587
+ return 0;
1588
+ }
1589
+ const seen = /* @__PURE__ */ new Set();
1590
+ let removed = 0;
1591
+ const lines = existing.split("\n").filter((line) => {
1592
+ const trimmed = line.trim();
1593
+ if (!trimmed.startsWith("- ")) return true;
1594
+ const norm = trimmed.replace(/\[[^\]]+\]/g, "").replace(/\bmem_\d+_\w+\s*/, "").replace(/#[\w-]+/g, "").replace(/\s+/g, " ").trim().toLowerCase();
1595
+ if (seen.has(norm)) {
1596
+ removed++;
1597
+ return false;
1598
+ }
1599
+ seen.add(norm);
1600
+ return true;
1601
+ });
1602
+ const next = lines.join("\n");
1603
+ const backup = `${file}.bak.${Date.now()}`;
1604
+ try {
1605
+ await fsp.copyFile(file, backup);
1606
+ } catch {
1607
+ }
1608
+ try {
1609
+ await atomicWrite(file, next);
1610
+ } catch {
1611
+ return 0;
1612
+ }
1613
+ return removed;
1614
+ }
1615
+ };
1616
+ function parseEntries(raw, scope = "project-memory") {
1617
+ const entries = [];
1618
+ for (const line of raw.split("\n")) {
1619
+ const entry = lineToEntry(line, scope);
1620
+ if (entry) entries.push(entry);
1621
+ }
1622
+ return entries.reverse();
1623
+ }
1624
+
1625
+ // src/storage/memory-store.ts
1413
1626
  var MAX_BYTES_TOTAL = 32e3;
1414
1627
  var DefaultMemoryStore = class {
1415
1628
  files;
1629
+ events;
1630
+ backend;
1416
1631
  /**
1417
1632
  * Per-scope serialization queue. `remember` / `forget` / `consolidate` /
1418
1633
  * `clear` are read-modify-write against a single file; without a lock,
@@ -1429,12 +1644,29 @@ var DefaultMemoryStore = class {
1429
1644
  writeChain = /* @__PURE__ */ new Map();
1430
1645
  /** Last write error per scope — surfaced as warnings on the next readAll(). */
1431
1646
  writeErrors = /* @__PURE__ */ new Map();
1647
+ /**
1648
+ * When the global root is a temporary directory (opencode, CI sandboxes),
1649
+ * memory files are also mirrored to the project tree so they survive
1650
+ * session cleanup. The primary path stays in the temp root for isolation;
1651
+ * this backup ensures memory is never lost.
1652
+ */
1653
+ persistBackup;
1654
+ backupDir;
1432
1655
  constructor(opts) {
1433
1656
  this.files = {
1434
1657
  "project-agents": opts.paths.inProjectAgentsFile,
1435
1658
  "project-memory": opts.paths.projectMemory,
1436
1659
  "user-memory": opts.paths.globalMemory
1437
1660
  };
1661
+ this.events = opts.events;
1662
+ this.backend = opts.backend ?? new FileMemoryBackend({ paths: opts.paths });
1663
+ const root = opts.paths.globalRoot.toLowerCase();
1664
+ this.persistBackup = /[/\\](tmp|temp|cache)[/\\]/.test(root);
1665
+ this.backupDir = this.persistBackup ? opts.paths.inProjectAgentsFile.replace(/AGENTS\.md$/, "memory-persist") : "";
1666
+ }
1667
+ /** Expose the backend for plugin introspection and advanced queries. */
1668
+ getBackend() {
1669
+ return this.backend;
1438
1670
  }
1439
1671
  async runSerialized(scope, work) {
1440
1672
  const prior = this.writeChain.get(scope) ?? Promise.resolve();
@@ -1461,7 +1693,7 @@ var DefaultMemoryStore = class {
1461
1693
  if (writeErr2) {
1462
1694
  parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
1463
1695
  }
1464
- const body = await this.read(scope);
1696
+ const body = await this.backend.readAll(scope, this.files[scope]);
1465
1697
  if (body.trim()) parts.push(`## ${labelOf(scope)}
1466
1698
 
1467
1699
  ${body.trim()}`);
@@ -1469,117 +1701,214 @@ ${body.trim()}`);
1469
1701
  return parts.join("\n\n");
1470
1702
  }
1471
1703
  async read(scope) {
1472
- try {
1473
- return await fsp.readFile(this.files[scope], "utf8");
1474
- } catch {
1475
- return "";
1704
+ return this.backend.readAll(scope, this.files[scope]);
1705
+ }
1706
+ /**
1707
+ * List entries from a scope, newest first. Delegates to the backend
1708
+ * so graph/semantic backends can return enriched or filtered results.
1709
+ */
1710
+ async list(scope = "project-memory", limit) {
1711
+ return this.backend.list(scope, this.files[scope], limit);
1712
+ }
1713
+ /**
1714
+ * Find memories related to the given text via graph traversal.
1715
+ * Falls back to content search when no graph backend is available.
1716
+ */
1717
+ async findRelated(text, scope = "project-memory", limit = 5) {
1718
+ if (this.backend.findRelated) {
1719
+ return this.backend.findRelated(scope, this.files[scope], text, limit);
1476
1720
  }
1721
+ return this.search(text, scope, limit);
1477
1722
  }
1478
- async remember(text, scope = "project-memory") {
1723
+ async search(query, scope = "project-memory", limit) {
1724
+ return this.backend.search(scope, query, this.files[scope], limit);
1725
+ }
1726
+ async remember(text, scope = "project-memory", metadata) {
1727
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
1479
1728
  return this.runSerialized(scope, async () => {
1480
- const file = this.files[scope];
1481
- await ensureDir(path3.dirname(file));
1482
- let existing = "";
1483
- try {
1484
- existing = await fsp.readFile(file, "utf8");
1485
- } catch {
1486
- }
1487
- const ts = (/* @__PURE__ */ new Date()).toISOString();
1488
- const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
1489
- const entry = `
1490
- - [${ts}] ${id} ${text.replace(/\n/g, " ")}
1491
- `;
1492
- const next = existing.trim() ? existing.replace(/\n+$/, "") + entry : `# WrongStack Memory
1493
- ${entry}`;
1494
- await atomicWrite(file, next);
1495
- const buf = Buffer.byteLength(next, "utf8");
1496
- if (buf > MAX_BYTES_TOTAL) {
1497
- await this.consolidateUnsafe(scope);
1729
+ const entry = { scope, text, ts, ...metadata };
1730
+ await this.backend.remember(scope, entry, this.files[scope]);
1731
+ const raw = await this.backend.readAll(scope, this.files[scope]);
1732
+ if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
1733
+ const removed = await this.backend.consolidate(scope, this.files[scope]);
1734
+ if (removed > 0) {
1735
+ this.events?.emit("memory.consolidated", {
1736
+ scope,
1737
+ removed
1738
+ });
1739
+ }
1498
1740
  }
1741
+ await this.mirrorBackup(scope);
1742
+ this.events?.emit("memory.remembered", {
1743
+ scope,
1744
+ text,
1745
+ ts,
1746
+ type: entry.type,
1747
+ tags: entry.tags,
1748
+ priority: entry.priority
1749
+ });
1499
1750
  });
1500
1751
  }
1501
- async forget(query, scope = "project-memory") {
1502
- return this.runSerialized(scope, async () => this.forgetUnsafe(query, scope));
1503
- }
1504
- async forgetUnsafe(query, scope) {
1505
- const file = this.files[scope];
1506
- let existing;
1507
- try {
1508
- existing = await fsp.readFile(file, "utf8");
1509
- } catch {
1510
- return 0;
1511
- }
1512
- const needle = query.toLowerCase();
1513
- const idMatcher = /mem_\d+_\w+/;
1514
- let removed = 0;
1515
- const lines = existing.split("\n").filter((line) => {
1516
- const trimmed = line.trim();
1517
- if (!trimmed.startsWith("- ")) return true;
1518
- if (idMatcher.test(query)) {
1519
- const afterBracket = trimmed.indexOf("] ");
1520
- if (afterBracket !== -1) {
1521
- const afterTs = trimmed.slice(afterBracket + 2);
1522
- const entryIdMatch = /^mem_\d+_\w+/.exec(afterTs);
1523
- if (entryIdMatch && entryIdMatch[0] === query) {
1524
- removed++;
1525
- return false;
1526
- }
1752
+ /**
1753
+ * Score and rank memories by relevance to the current context.
1754
+ * Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
1755
+ */
1756
+ async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
1757
+ const all = await this.list(scope);
1758
+ if (all.length === 0) return [];
1759
+ const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
1760
+ const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
1761
+ const toolWords = (ctx.toolNames ?? []).flatMap((t) => t.toLowerCase().split("_"));
1762
+ const now = Date.now();
1763
+ const scored = [];
1764
+ for (const entry of all) {
1765
+ let score = 0;
1766
+ const reasons = [];
1767
+ const textLower = entry.text.toLowerCase();
1768
+ const tagsLower = (entry.tags ?? []).map((t) => t.toLowerCase());
1769
+ let taskHits = 0;
1770
+ for (const w of taskWords) {
1771
+ if (textLower.includes(w)) {
1772
+ taskHits++;
1773
+ score += 2;
1774
+ }
1775
+ if (tagsLower.some((t) => t.includes(w))) {
1776
+ taskHits++;
1777
+ score += 3;
1527
1778
  }
1528
1779
  }
1529
- if (trimmed.toLowerCase().includes(needle)) {
1530
- removed++;
1531
- return false;
1780
+ if (taskHits > 0) reasons.push(`task match (${taskHits})`);
1781
+ let skillHits = 0;
1782
+ for (const w of skillWords) {
1783
+ if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
1784
+ skillHits++;
1785
+ }
1786
+ }
1787
+ score += skillHits;
1788
+ if (skillHits > 0) reasons.push(`skill match (${skillHits})`);
1789
+ for (const w of toolWords) {
1790
+ if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
1791
+ score += 1;
1792
+ reasons.push(`tool mention: ${w}`);
1793
+ }
1794
+ }
1795
+ switch (entry.priority) {
1796
+ case "critical":
1797
+ score += 5;
1798
+ reasons.push("critical");
1799
+ break;
1800
+ case "high":
1801
+ score += 3;
1802
+ reasons.push("high priority");
1803
+ break;
1804
+ case "medium":
1805
+ score += 1;
1806
+ break;
1807
+ case "low":
1808
+ score -= 2;
1809
+ reasons.push("low priority");
1810
+ break;
1811
+ }
1812
+ switch (entry.type) {
1813
+ case "decision":
1814
+ score += 2;
1815
+ reasons.push("decision");
1816
+ break;
1817
+ case "convention":
1818
+ score += 2;
1819
+ reasons.push("convention");
1820
+ break;
1821
+ case "anti_pattern":
1822
+ score += 3;
1823
+ reasons.push("anti-pattern");
1824
+ break;
1825
+ case "preference":
1826
+ score += 1;
1827
+ reasons.push("preference");
1828
+ break;
1829
+ }
1830
+ const ageDays = (now - new Date(entry.ts).getTime()) / (1e3 * 60 * 60 * 24);
1831
+ if (ageDays < 1) score += 1;
1832
+ else if (ageDays > 30) score -= 1;
1833
+ if (entry.confidence !== void 0 && entry.confidence < 0.5) {
1834
+ score -= 2;
1835
+ reasons.push("low confidence");
1836
+ }
1837
+ if (entry.lastAccessed) {
1838
+ const hoursSinceAccess = (now - new Date(entry.lastAccessed).getTime()) / (1e3 * 60 * 60);
1839
+ if (hoursSinceAccess < 1) score -= 1;
1840
+ }
1841
+ if (score > 0) {
1842
+ scored.push({
1843
+ ...entry,
1844
+ score,
1845
+ matchReason: reasons.join(", ") || "keyword match"
1846
+ });
1532
1847
  }
1533
- return true;
1534
- });
1535
- if (removed > 0) {
1536
- await atomicWrite(file, lines.join("\n"));
1537
1848
  }
1538
- return removed;
1849
+ scored.sort((a, b) => b.score - a.score);
1850
+ const threshold = 2;
1851
+ const relevant = scored.filter(
1852
+ (s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
1853
+ );
1854
+ return relevant.slice(0, Math.min(limit, 15));
1539
1855
  }
1540
- async consolidate(scope) {
1541
- return this.runSerialized(scope, async () => this.consolidateUnsafe(scope));
1856
+ async forget(query, scope = "project-memory") {
1857
+ return this.runSerialized(scope, async () => {
1858
+ const removed = await this.backend.forget(scope, query, this.files[scope]);
1859
+ if (removed > 0) {
1860
+ this.events?.emit("memory.forgotten", {
1861
+ scope,
1862
+ query,
1863
+ removed
1864
+ });
1865
+ await this.mirrorBackup(scope);
1866
+ }
1867
+ return removed;
1868
+ });
1542
1869
  }
1543
- async consolidateUnsafe(scope) {
1544
- const file = this.files[scope];
1545
- let existing;
1546
- try {
1547
- existing = await fsp.readFile(file, "utf8");
1548
- } catch {
1549
- return;
1550
- }
1551
- const seen = /* @__PURE__ */ new Set();
1552
- const lines = existing.split("\n").filter((line) => {
1553
- const trimmed = line.trim();
1554
- if (!trimmed.startsWith("- ")) return true;
1555
- const norm = trimmed.replace(/\[[^\]]+\]/, "").replace(/\bmem_\d+_\w+\s*/, "").trim().toLowerCase();
1556
- if (seen.has(norm)) return false;
1557
- seen.add(norm);
1558
- return true;
1870
+ async consolidate(scope) {
1871
+ return this.runSerialized(scope, async () => {
1872
+ const removed = await this.backend.consolidate(scope, this.files[scope]);
1873
+ if (removed > 0) {
1874
+ this.events?.emit("memory.consolidated", {
1875
+ scope,
1876
+ removed
1877
+ });
1878
+ await this.mirrorBackup(scope);
1879
+ }
1559
1880
  });
1560
- const next = lines.join("\n");
1561
- const backup = `${file}.bak.${Date.now()}`;
1562
- try {
1563
- await fsp.copyFile(file, backup);
1564
- } catch {
1565
- }
1566
- try {
1567
- await atomicWrite(file, next);
1568
- } catch {
1569
- return;
1570
- }
1571
1881
  }
1572
1882
  async clear(scope) {
1573
1883
  if (scope) {
1574
- await this.runSerialized(scope, async () => atomicWrite(this.files[scope], ""));
1884
+ await this.runSerialized(scope, async () => {
1885
+ await this.backend.clear(scope, this.files[scope]);
1886
+ this.events?.emit("memory.cleared", { scope });
1887
+ await this.mirrorBackup(scope);
1888
+ });
1575
1889
  return;
1576
1890
  }
1577
1891
  await Promise.all(
1578
1892
  ["project-agents", "project-memory", "user-memory"].map(
1579
- (s) => this.runSerialized(s, async () => atomicWrite(this.files[s], ""))
1893
+ (s) => this.runSerialized(s, async () => {
1894
+ await this.backend.clear(s, this.files[s]);
1895
+ this.events?.emit("memory.cleared", { scope: s });
1896
+ await this.mirrorBackup(s);
1897
+ })
1580
1898
  )
1581
1899
  );
1582
1900
  }
1901
+ /** Mirror current memory content to the persistent backup directory. */
1902
+ async mirrorBackup(scope) {
1903
+ if (!this.persistBackup || scope === "project-agents") return;
1904
+ try {
1905
+ const content = await this.backend.readAll(scope, this.files[scope]);
1906
+ const { writeFile: writeFile4, mkdir: mkdir6 } = await import('fs/promises');
1907
+ await mkdir6(this.backupDir, { recursive: true });
1908
+ await writeFile4(`${this.backupDir}/${scope}.md`, content, "utf8");
1909
+ } catch {
1910
+ }
1911
+ }
1583
1912
  };
1584
1913
  function labelOf(scope) {
1585
1914
  switch (scope) {
@@ -2295,12 +2624,6 @@ function compileUserRegex(pattern, flags) {
2295
2624
  }
2296
2625
 
2297
2626
  // src/storage/session-reader.ts
2298
- function expectDefined3(value) {
2299
- if (value === null || value === void 0) {
2300
- throw new Error("Expected value to be defined");
2301
- }
2302
- return value;
2303
- }
2304
2627
  var DefaultSessionReader = class {
2305
2628
  store;
2306
2629
  constructor(opts) {
@@ -2362,7 +2685,7 @@ var DefaultSessionReader = class {
2362
2685
  continue;
2363
2686
  }
2364
2687
  for (let i = 0; i < data.events.length; i++) {
2365
- const ev = expectDefined3(data.events[i]);
2688
+ const ev = expectDefined(data.events[i]);
2366
2689
  if (allowedTypes && !allowedTypes.has(ev.type)) continue;
2367
2690
  const text = eventText(ev);
2368
2691
  if (text === null) continue;
@@ -2811,7 +3134,10 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
2811
3134
  let pending = null;
2812
3135
  let writeChain = Promise.resolve();
2813
3136
  const enqueueWrite = (todos) => {
2814
- writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos));
3137
+ writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos)).catch((err) => {
3138
+ const msg = err instanceof Error ? err.message : String(err);
3139
+ console.error(`[TodosCheckpoint] save failed for session ${sessionId}: ${msg}`);
3140
+ });
2815
3141
  return writeChain;
2816
3142
  };
2817
3143
  const flush = () => {
@@ -3115,7 +3441,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
3115
3441
  }
3116
3442
  const lock = {
3117
3443
  pid: processId,
3118
- hostname: __require("os").hostname(),
3444
+ hostname: hostname(),
3119
3445
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
3120
3446
  };
3121
3447
  await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
@@ -3641,12 +3967,6 @@ function getDangerousCapabilities(toolOrCaps) {
3641
3967
  init_atomic_write();
3642
3968
 
3643
3969
  // src/utils/glob-match.ts
3644
- function expectDefined4(value) {
3645
- if (value === null || value === void 0) {
3646
- throw new Error("Expected value to be defined");
3647
- }
3648
- return value;
3649
- }
3650
3970
  function escapeRegex(s) {
3651
3971
  return s.replace(/[.+^${}()|\\]/g, "\\$&");
3652
3972
  }
@@ -3658,7 +3978,7 @@ function getCachedGlob(pattern) {
3658
3978
  if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
3659
3979
  const keys = [...COMPILED_GLOB_CACHE.keys()];
3660
3980
  for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
3661
- COMPILED_GLOB_CACHE.delete(expectDefined4(keys[i]));
3981
+ COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
3662
3982
  }
3663
3983
  }
3664
3984
  const re = compileGlob(pattern);
@@ -4523,12 +4843,6 @@ function parseDescription(raw) {
4523
4843
  }
4524
4844
 
4525
4845
  // src/utils/json-repair.ts
4526
- function expectDefined5(value) {
4527
- if (value === null || value === void 0) {
4528
- throw new Error("Expected value to be defined");
4529
- }
4530
- return value;
4531
- }
4532
4846
  function completePartialObject(s) {
4533
4847
  if (!s.trim().startsWith("{")) return s;
4534
4848
  if (tryParse(s).ok) return s;
@@ -4540,7 +4854,7 @@ function completePartialObject(s) {
4540
4854
  let contentEnd = 0;
4541
4855
  let stringBraceDepth = 0;
4542
4856
  for (let i = 0; i < s.length; i++) {
4543
- const ch = expectDefined5(s[i]);
4857
+ const ch = expectDefined(s[i]);
4544
4858
  if (inString) {
4545
4859
  contentEnd = i + 1;
4546
4860
  if (escaped) {
@@ -4926,12 +5240,6 @@ var DefaultProviderRunner = class {
4926
5240
  };
4927
5241
 
4928
5242
  // src/utils/token-estimate.ts
4929
- function expectDefined6(value) {
4930
- if (value === null || value === void 0) {
4931
- throw new Error("Expected value to be defined");
4932
- }
4933
- return value;
4934
- }
4935
5243
  var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
4936
5244
  var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
4937
5245
  var ESTIMATE_CACHE_MAX_SIZE = 1e4;
@@ -4941,7 +5249,7 @@ function getCachedEstimate(key, compute) {
4941
5249
  if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
4942
5250
  const keys = [...ESTIMATE_CACHE.keys()];
4943
5251
  for (let i = 0; i < Math.floor(ESTIMATE_CACHE_MAX_SIZE / 4); i++) {
4944
- ESTIMATE_CACHE.delete(expectDefined6(keys[i]));
5252
+ ESTIMATE_CACHE.delete(expectDefined(keys[i]));
4945
5253
  }
4946
5254
  }
4947
5255
  const estimate = compute();
@@ -5434,12 +5742,6 @@ var IntelligentCompactor = class {
5434
5742
  };
5435
5743
 
5436
5744
  // src/models/llm-selector.ts
5437
- function expectDefined7(value) {
5438
- if (value === null || value === void 0) {
5439
- throw new Error("Expected value to be defined");
5440
- }
5441
- return value;
5442
- }
5443
5745
  var DEFAULT_SYSTEM_PROMPT = `You are a context pruning assistant. Given a conversation history and a token budget, decide which message ranges are worth keeping verbatim and which should be collapsed into summaries.
5444
5746
 
5445
5747
  Output a JSON object with this structure:
@@ -5480,7 +5782,7 @@ function formatMessages(messages, maxChars = 8e3) {
5480
5782
  const lines = [];
5481
5783
  let used = 0;
5482
5784
  for (let i = 0; i < messages.length; i++) {
5483
- const m = expectDefined7(messages[i]);
5785
+ const m = expectDefined(messages[i]);
5484
5786
  const role = m.role.padEnd(10, " ");
5485
5787
  let text;
5486
5788
  if (typeof m.content === "string") {
@@ -5545,7 +5847,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
5545
5847
  let tokenCount = 0;
5546
5848
  let startIdx = 0;
5547
5849
  for (let i = messages.length - 1; i >= 0; i--) {
5548
- const m = expectDefined7(messages[i]);
5850
+ const m = expectDefined(messages[i]);
5549
5851
  const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
5550
5852
  (acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
5551
5853
  0
@@ -6152,12 +6454,6 @@ function createToolOutputSerializer(opts = {}) {
6152
6454
  }
6153
6455
 
6154
6456
  // src/execution/tool-executor.ts
6155
- function expectDefined8(value) {
6156
- if (value === null || value === void 0) {
6157
- throw new Error("Expected value to be defined");
6158
- }
6159
- return value;
6160
- }
6161
6457
  var ToolExecutor = class {
6162
6458
  constructor(registry, opts) {
6163
6459
  this.registry = registry;
@@ -6564,7 +6860,7 @@ function hasMalformedArguments(input) {
6564
6860
  function extractMalformedRaw(input) {
6565
6861
  if (!hasMalformedArguments(input)) return void 0;
6566
6862
  const obj = input;
6567
- const value = obj[expectDefined8(Object.keys(obj)[0])];
6863
+ const value = obj[expectDefined(Object.keys(obj)[0])];
6568
6864
  if (value === void 0 || value === null) return void 0;
6569
6865
  if (typeof value === "string") return value;
6570
6866
  try {
@@ -7722,15 +8018,7 @@ function makeAgentSubagentRunner(opts) {
7722
8018
  let budgetError = null;
7723
8019
  const onBudgetError = (err) => {
7724
8020
  if (err instanceof BudgetThresholdSignal) {
7725
- err.decision.then((decision) => {
7726
- if (decision === "stop") {
7727
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
7728
- aborter.abort();
7729
- }
7730
- }).catch(() => {
7731
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
7732
- aborter.abort();
7733
- });
8021
+ budgetError = err;
7734
8022
  return;
7735
8023
  }
7736
8024
  aborter.abort();
@@ -7838,15 +8126,23 @@ function makeAgentSubagentRunner(opts) {
7838
8126
  }
7839
8127
  if (budgetError) {
7840
8128
  if ("decision" in budgetError) {
7841
- const decision = await budgetError.decision;
7842
- if (decision === "stop") {
8129
+ try {
8130
+ const decision = await budgetError.decision;
8131
+ if (decision === "stop") {
8132
+ budgetError = new BudgetExceededError(
8133
+ budgetError.kind,
8134
+ budgetError.limit,
8135
+ budgetError.used
8136
+ );
8137
+ } else {
8138
+ budgetError = null;
8139
+ }
8140
+ } catch {
7843
8141
  budgetError = new BudgetExceededError(
7844
8142
  budgetError.kind,
7845
8143
  budgetError.limit,
7846
8144
  budgetError.used
7847
8145
  );
7848
- } else {
7849
- budgetError = null;
7850
8146
  }
7851
8147
  }
7852
8148
  if (budgetError) throw budgetError;
@@ -11342,12 +11638,6 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
11342
11638
  };
11343
11639
 
11344
11640
  // src/execution/parallel-eternal-engine.ts
11345
- function expectDefined9(value) {
11346
- if (value === null || value === void 0) {
11347
- throw new Error("Expected value to be defined");
11348
- }
11349
- return value;
11350
- }
11351
11641
  var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
11352
11642
  var ParallelEternalEngine = class {
11353
11643
  constructor(opts) {
@@ -11503,7 +11793,7 @@ var ParallelEternalEngine = class {
11503
11793
  // Fan-out
11504
11794
  // -------------------------------------------------------------------------
11505
11795
  async fanOut(goal, tasks) {
11506
- const coordinator = expectDefined9(this.coordinator);
11796
+ const coordinator = expectDefined(this.coordinator);
11507
11797
  const slotCount = Math.min(this.slots, tasks.length);
11508
11798
  const routes = this.dispatchEnabled ? await Promise.all(
11509
11799
  tasks.slice(0, slotCount).map(
@@ -11535,7 +11825,7 @@ ${recentJournal}` : "No prior iterations.",
11535
11825
  const routeInfo = [];
11536
11826
  const spawnPromises = [];
11537
11827
  for (let i = 0; i < slotCount; i++) {
11538
- const task = expectDefined9(tasks[i]);
11828
+ const task = expectDefined(tasks[i]);
11539
11829
  const route = routes[i] ?? null;
11540
11830
  const subagentId = `parallel-${this.iterations}-${i}`;
11541
11831
  const taskId = randomUUID();
@@ -12026,12 +12316,6 @@ function createMessage(type, from, payload, to) {
12026
12316
  priority: "normal"
12027
12317
  };
12028
12318
  }
12029
- function expectDefined10(value) {
12030
- if (value === null || value === void 0) {
12031
- throw new Error("Expected value to be defined");
12032
- }
12033
- return value;
12034
- }
12035
12319
  var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
12036
12320
  var IS_WINDOWS = process.platform === "win32";
12037
12321
  var SEP = IS_WINDOWS ? "\\" : "/";
@@ -12045,7 +12329,7 @@ function globToRegex(pat) {
12045
12329
  let i = 0;
12046
12330
  let re = "^";
12047
12331
  while (i < pat.length) {
12048
- const c = expectDefined10(pat[i]);
12332
+ const c = expectDefined(pat[i]);
12049
12333
  if (c === "*") {
12050
12334
  if (pat[i + 1] === "*") {
12051
12335
  re += ".*";
@@ -12084,7 +12368,7 @@ function globToRegex(pat) {
12084
12368
  }
12085
12369
  function baseDir(pat) {
12086
12370
  let i = pat.length - 1;
12087
- while (i >= 0 && !GLOB_CHARS.has(expectDefined10(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
12371
+ while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
12088
12372
  const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
12089
12373
  return cut < 0 ? "." : pat.slice(0, cut);
12090
12374
  }
@@ -12165,6 +12449,7 @@ var CollabSession = class extends EventEmitter {
12165
12449
  settled = false;
12166
12450
  timeoutMs;
12167
12451
  cancelled = false;
12452
+ _raceResolved = false;
12168
12453
  alerts = [];
12169
12454
  snapshotWarnings = [];
12170
12455
  /** Tracks tool call counts per subagent for progress-based timeout decisions. */
@@ -12261,7 +12546,7 @@ var CollabSession = class extends EventEmitter {
12261
12546
  * Safe to call multiple times (idempotent after first call).
12262
12547
  */
12263
12548
  cancel(reason = "Director cancelled collab session") {
12264
- if (this.settled) return;
12549
+ if (this.cancelled) return;
12265
12550
  this.cancelled = true;
12266
12551
  if (this._timeoutTimer) {
12267
12552
  clearTimeout(this._timeoutTimer);
@@ -12299,6 +12584,7 @@ var CollabSession = class extends EventEmitter {
12299
12584
  this.subagentIds.set("critic", criticId);
12300
12585
  const timeout = new Promise((_, reject) => {
12301
12586
  this._timeoutTimer = setTimeout(() => {
12587
+ if (this._raceResolved) return;
12302
12588
  this.cancel("Session-level timeout reached");
12303
12589
  reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
12304
12590
  }, this.timeoutMs);
@@ -12314,6 +12600,7 @@ var CollabSession = class extends EventEmitter {
12314
12600
  timeout
12315
12601
  ]);
12316
12602
  } catch (err) {
12603
+ this._raceResolved = true;
12317
12604
  if (this._timeoutTimer) {
12318
12605
  clearTimeout(this._timeoutTimer);
12319
12606
  this._timeoutTimer = void 0;
@@ -12328,6 +12615,7 @@ var CollabSession = class extends EventEmitter {
12328
12615
  }
12329
12616
  this.snapshotWarnings = await this.checkSnapshotFreshness();
12330
12617
  const report = this.assembleReport();
12618
+ this._raceResolved = true;
12331
12619
  this.cleanup();
12332
12620
  this.emit("session.done", report);
12333
12621
  return report;
@@ -17035,12 +17323,6 @@ var TaskGraphStore = class {
17035
17323
  };
17036
17324
 
17037
17325
  // src/sdd/spec-builder.ts
17038
- function expectDefined11(value) {
17039
- if (value === null || value === void 0) {
17040
- throw new Error("Expected value to be defined");
17041
- }
17042
- return value;
17043
- }
17044
17326
  function buildQuestioningPrompt(session, min, max) {
17045
17327
  const answered = session.answers.length;
17046
17328
  const remaining = Math.max(0, min - answered);
@@ -17086,7 +17368,7 @@ function buildQuestioningPrompt(session, min, max) {
17086
17368
  if (answered > 0) {
17087
17369
  lines.push("", "**Conversation so far:**");
17088
17370
  for (let i = 0; i < answered; i++) {
17089
- const a = expectDefined11(session.answers[i]);
17371
+ const a = expectDefined(session.answers[i]);
17090
17372
  lines.push(``, `Q${i + 1}: ${a.question}`, `A${i + 1}: ${a.answer}`);
17091
17373
  }
17092
17374
  }
@@ -17828,12 +18110,6 @@ function truncate2(str, maxLen) {
17828
18110
  }
17829
18111
 
17830
18112
  // src/sdd/critical-path.ts
17831
- function expectDefined12(value) {
17832
- if (value === null || value === void 0) {
17833
- throw new Error("Expected value to be defined");
17834
- }
17835
- return value;
17836
- }
17837
18113
  function analyzeCriticalPath(graph) {
17838
18114
  const nodes = Array.from(graph.nodes.values());
17839
18115
  const topoOrder = topologicalSort(graph);
@@ -17909,7 +18185,7 @@ function getTransitiveBlocked(_graph, taskId, blocksMap) {
17909
18185
  const visited = /* @__PURE__ */ new Set();
17910
18186
  const queue = [taskId];
17911
18187
  while (queue.length > 0) {
17912
- const current = expectDefined12(queue.shift());
18188
+ const current = expectDefined(queue.shift());
17913
18189
  const blocked = blocksMap.get(current);
17914
18190
  if (!blocked) continue;
17915
18191
  for (const id of blocked) {
@@ -17955,7 +18231,7 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
17955
18231
  if (!changed) break;
17956
18232
  }
17957
18233
  let maxDist = 0;
17958
- let maxId = expectDefined12(allIds[0]);
18234
+ let maxId = expectDefined(allIds[0]);
17959
18235
  for (const id of allIds) {
17960
18236
  const d = dist.get(id) ?? 0;
17961
18237
  if (d > maxDist) {
@@ -18424,12 +18700,6 @@ var SddTaskDecomposer = class {
18424
18700
  return nodes.some((n) => n.status === "blocked");
18425
18701
  }
18426
18702
  };
18427
- function expectDefined13(value) {
18428
- if (value === null || value === void 0) {
18429
- throw new Error("Expected value to be defined");
18430
- }
18431
- return value;
18432
- }
18433
18703
  var SddParallelRun = class {
18434
18704
  constructor(opts) {
18435
18705
  this.opts = opts;
@@ -18578,8 +18848,8 @@ var SddParallelRun = class {
18578
18848
  const successCount = results.filter((r) => r.status === "success").length;
18579
18849
  const failCount = results.length - successCount;
18580
18850
  for (let i = 0; i < results.length; i++) {
18581
- const result = expectDefined13(results[i]);
18582
- const taskId = expectDefined13(taskIds[i]);
18851
+ const result = expectDefined(results[i]);
18852
+ const taskId = expectDefined(taskIds[i]);
18583
18853
  if (result.status === "success") {
18584
18854
  this.opts.tracker.updateNodeStatus(taskId, "completed");
18585
18855
  } else {