@wrongstack/core 0.87.0 → 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 (83) hide show
  1. package/dist/{agent-bridge-C9P_HPez.d.ts → agent-bridge-DbVe1dHT.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-2Aq0jOSj.d.ts → agent-subagent-runner-C6eqID1t.d.ts} +477 -476
  3. package/dist/{events-DnRqXaZ3.d.ts → brain-s9DyD_Vf.d.ts} +185 -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 +208 -93
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/{default-config-DEXI4jsl.d.ts → default-config-CXsDvOmP.d.ts} +3 -1
  11. package/dist/defaults/index.d.ts +32 -34
  12. package/dist/defaults/index.js +744 -274
  13. package/dist/defaults/index.js.map +1 -1
  14. package/dist/{director-state-CgIc30qi.d.ts → director-state-BfeCUbmk.d.ts} +1 -1
  15. package/dist/execution/index.d.ts +18 -19
  16. package/dist/execution/index.js +79 -64
  17. package/dist/execution/index.js.map +1 -1
  18. package/dist/extension/index.d.ts +9 -10
  19. package/dist/extension/index.js +8 -6
  20. package/dist/extension/index.js.map +1 -1
  21. package/dist/{index-BNOLadHw.d.ts → index-CqrroYcU.d.ts} +13 -15
  22. package/dist/{index-N0_c4bHQ.d.ts → index-D3Nax3YU.d.ts} +11 -10
  23. package/dist/index.d.ts +191 -178
  24. package/dist/index.js +1419 -526
  25. package/dist/index.js.map +1 -1
  26. package/dist/infrastructure/index.d.ts +9 -9
  27. package/dist/infrastructure/index.js +16 -5
  28. package/dist/infrastructure/index.js.map +1 -1
  29. package/dist/kernel/index.d.ts +14 -16
  30. package/dist/kernel/index.js +17 -0
  31. package/dist/kernel/index.js.map +1 -1
  32. package/dist/{logger-B72yyPc6.d.ts → logger-B63L5bTg.d.ts} +1 -1
  33. package/dist/{logger-C_27pj9i.d.ts → logger-B9J5puGM.d.ts} +1 -1
  34. package/dist/{mcp-servers-Dck3T85_.d.ts → mcp-servers-D3E5ixAR.d.ts} +3 -3
  35. package/dist/{mode-CHo2XtHs.d.ts → mode-ARA3HrkY.d.ts} +1 -1
  36. package/dist/models/index.d.ts +5 -5
  37. package/dist/models/index.js +8 -6
  38. package/dist/models/index.js.map +1 -1
  39. package/dist/{models-registry-Be3osGt5.d.ts → models-registry-B6_KfS65.d.ts} +1 -1
  40. package/dist/{models-registry-Boz639EI.d.ts → models-registry-DU64QxQa.d.ts} +1 -1
  41. package/dist/{multi-agent-coordinator-DllpCVkF.d.ts → multi-agent-coordinator-DHNrjPaA.d.ts} +2 -2
  42. package/dist/{null-fleet-bus-BY0AN-sr.d.ts → null-fleet-bus-BzzciAc4.d.ts} +39 -32
  43. package/dist/observability/index.d.ts +3 -3
  44. package/dist/{observability-CoSNZdhX.d.ts → observability-D-HZN_mF.d.ts} +1 -1
  45. package/dist/{parallel-eternal-engine-D402RASp.d.ts → parallel-eternal-engine-CTpcrb9O.d.ts} +34 -33
  46. package/dist/{path-resolver-UPFTsDyD.d.ts → path-resolver-B7hgMAVe.d.ts} +3 -3
  47. package/dist/{permission-14CChMmO.d.ts → permission-BDv7z0mk.d.ts} +7 -2
  48. package/dist/{permission-policy-gW5htOo1.d.ts → permission-policy-dF74EpDp.d.ts} +2 -2
  49. package/dist/{system-prompt-C0rLCeyn.d.ts → pipeline-C1Ad9OjI.d.ts} +73 -73
  50. package/dist/{plan-templates-DRvPgkfZ.d.ts → plan-templates-CNphsz0n.d.ts} +79 -11
  51. package/dist/{provider-runner-COAJM8tC.d.ts → provider-runner-cqvlB_oS.d.ts} +5 -5
  52. package/dist/{retry-policy-DSu6O6rD.d.ts → retry-policy-BcmuT_V0.d.ts} +2 -2
  53. package/dist/sdd/index.d.ts +12 -33
  54. package/dist/sdd/index.js +110 -50
  55. package/dist/sdd/index.js.map +1 -1
  56. package/dist/{secret-scrubber-yGBFQYju.d.ts → secret-vault-DrOhc2i5.d.ts} +7 -7
  57. package/dist/security/index.d.ts +4 -5
  58. package/dist/security/index.js +22 -4
  59. package/dist/security/index.js.map +1 -1
  60. package/dist/{selector-11-fm95U.d.ts → selector-C7wcdqMA.d.ts} +1 -1
  61. package/dist/{session-event-bridge-D0u-x576.d.ts → session-event-bridge-IVzs2GpE.d.ts} +1 -1
  62. package/dist/{session-reader-BQU-toaN.d.ts → session-reader-DDz1Ek4V.d.ts} +2 -2
  63. package/dist/{skill-BJeF2DwY.d.ts → skill-BJxzIyyN.d.ts} +1 -1
  64. package/dist/skills/index.d.ts +1 -1
  65. package/dist/skills/index.js +3 -9
  66. package/dist/skills/index.js.map +1 -1
  67. package/dist/storage/index.d.ts +134 -14
  68. package/dist/storage/index.js +982 -248
  69. package/dist/storage/index.js.map +1 -1
  70. package/dist/{task-graph-CikNdRTG.d.ts → task-graph-DJBqO0EY.d.ts} +1 -1
  71. package/dist/types/index.d.ts +26 -28
  72. package/dist/types/index.js +62 -37
  73. package/dist/types/index.js.map +1 -1
  74. package/dist/utils/expect-defined.d.ts +7 -0
  75. package/dist/utils/expect-defined.js +11 -0
  76. package/dist/utils/expect-defined.js.map +1 -0
  77. package/dist/utils/index.d.ts +11 -20
  78. package/dist/utils/index.js +61 -43
  79. package/dist/utils/index.js.map +1 -1
  80. package/dist/{wstack-paths-BQMvEllz.d.ts → wstack-paths-_lqjzErq.d.ts} +1 -1
  81. package/package.json +5 -1
  82. package/dist/memory-CEXuo7sz.d.ts +0 -16
  83. package/dist/secret-scrubber-3MHDDAtm.d.ts +0 -6
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import * as path6 from 'path';
6
6
  import { join, extname, relative, isAbsolute, resolve, sep } from 'path';
7
7
  import * as fs2 from 'fs';
8
8
  import * as os7 from 'os';
9
+ import { hostname } from 'os';
9
10
  import { execFile, spawn } from 'child_process';
10
11
  import { promisify } from 'util';
11
12
  import { EventEmitter } from 'events';
@@ -15,12 +16,6 @@ import { pipeline } from 'stream/promises';
15
16
 
16
17
  var __defProp = Object.defineProperty;
17
18
  var __getOwnPropNames = Object.getOwnPropertyNames;
18
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
19
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
20
- }) : x)(function(x) {
21
- if (typeof require !== "undefined") return require.apply(this, arguments);
22
- throw Error('Dynamic require of "' + x + '" is not supported');
23
- });
24
19
  var __esm = (fn, res) => function __init() {
25
20
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
26
21
  };
@@ -33,7 +28,8 @@ var __export = (target, all) => {
33
28
  var atomic_write_exports = {};
34
29
  __export(atomic_write_exports, {
35
30
  atomicWrite: () => atomicWrite,
36
- ensureDir: () => ensureDir
31
+ ensureDir: () => ensureDir,
32
+ withFileLock: () => withFileLock
37
33
  });
38
34
  async function atomicWrite(targetPath, content, opts = {}) {
39
35
  const dir = path6.dirname(targetPath);
@@ -56,8 +52,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
56
52
  }
57
53
  let mode;
58
54
  try {
59
- const stat10 = await fsp3.stat(targetPath);
60
- mode = stat10.mode & 511;
55
+ const stat11 = await fsp3.stat(targetPath);
56
+ mode = stat11.mode & 511;
61
57
  } catch {
62
58
  mode = opts.mode;
63
59
  }
@@ -76,6 +72,49 @@ async function atomicWrite(targetPath, content, opts = {}) {
76
72
  async function ensureDir(dir) {
77
73
  await fsp3.mkdir(dir, { recursive: true });
78
74
  }
75
+ async function withFileLock(targetPath, fn, opts = {}) {
76
+ const dir = path6.dirname(targetPath);
77
+ await fsp3.mkdir(dir, { recursive: true });
78
+ const lockPath = path6.join(dir, `.${path6.basename(targetPath)}.lock`);
79
+ const timeoutMs = opts.timeoutMs ?? 5e3;
80
+ const staleMs = opts.staleMs ?? 3e4;
81
+ const started = Date.now();
82
+ let handle;
83
+ for (; ; ) {
84
+ try {
85
+ handle = await fsp3.open(lockPath, "wx");
86
+ await handle.writeFile(`${process.pid}:${Date.now()}`);
87
+ break;
88
+ } catch (err) {
89
+ if (err.code !== "EEXIST") throw err;
90
+ try {
91
+ const stat11 = await fsp3.stat(lockPath);
92
+ if (Date.now() - stat11.mtimeMs > staleMs) {
93
+ await fsp3.unlink(lockPath);
94
+ continue;
95
+ }
96
+ } catch {
97
+ continue;
98
+ }
99
+ if (Date.now() - started >= timeoutMs) {
100
+ throw new Error(`Timed out waiting for file lock: ${targetPath}`);
101
+ }
102
+ await new Promise((resolve13) => setTimeout(resolve13, 25));
103
+ }
104
+ }
105
+ try {
106
+ return await fn();
107
+ } finally {
108
+ try {
109
+ await handle?.close();
110
+ } catch {
111
+ }
112
+ try {
113
+ await fsp3.unlink(lockPath);
114
+ } catch {
115
+ }
116
+ }
117
+ }
79
118
  async function renameWithRetry(from, to) {
80
119
  if (process.platform !== "win32") {
81
120
  await fsp3.rename(from, to);
@@ -703,6 +742,23 @@ var EventBus = class {
703
742
  }
704
743
  }
705
744
  }
745
+ /**
746
+ * Emit a plugin-defined event that is intentionally outside EventMap.
747
+ * Custom events are delivered to wildcard/pattern listeners only; typed
748
+ * listeners remain reserved for core EventMap keys.
749
+ */
750
+ emitCustom(event, payload) {
751
+ if (this.wildcards.length === 0) return;
752
+ const snapshot = this.wildcards.slice();
753
+ for (const { match, fn } of snapshot) {
754
+ if (!match(event)) continue;
755
+ try {
756
+ fn(event, payload);
757
+ } catch (err) {
758
+ this.logger?.error(`EventBus wildcard listener for "${event}" threw`, err);
759
+ }
760
+ }
761
+ }
706
762
  clear() {
707
763
  this.listeners.clear();
708
764
  this.wildcards.length = 0;
@@ -1093,6 +1149,7 @@ var DEFAULT_SESSION_LOGGING_CONFIG = Object.freeze({
1093
1149
  }
1094
1150
  }
1095
1151
  });
1152
+ var DEFAULT_SESSION_PRUNE_DAYS = 30;
1096
1153
 
1097
1154
  // src/types/secret-vault.ts
1098
1155
  var ENCRYPTED_PREFIX = "enc:v1:";
@@ -1254,9 +1311,18 @@ async function restrictFilePermissions(filePath, opts) {
1254
1311
  const { execFile: execFile2 } = await import('child_process');
1255
1312
  const { promisify: promisify2 } = await import('util');
1256
1313
  const execFileAsync = promisify2(execFile2);
1257
- await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${process.env.USERNAME}:(F)`]);
1314
+ const user = windowsAccountName();
1315
+ if (!user) {
1316
+ warn(
1317
+ `[secret-vault] Could not determine the current Windows user for ${filePath}; skipping icacls hardening.`
1318
+ );
1319
+ return;
1320
+ }
1321
+ await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${user}:(F)`]);
1258
1322
  } catch {
1259
- warn(`[secret-vault] Could not restrict permissions on ${filePath} \u2014 config file may be readable by other users on this system.`);
1323
+ warn(
1324
+ `[secret-vault] Could not restrict permissions on ${filePath} \u2014 config file may be readable by other users on this system.`
1325
+ );
1260
1326
  }
1261
1327
  } else {
1262
1328
  try {
@@ -1265,6 +1331,13 @@ async function restrictFilePermissions(filePath, opts) {
1265
1331
  }
1266
1332
  }
1267
1333
  }
1334
+ function windowsAccountName() {
1335
+ const username = process.env.USERNAME || process.env.USER;
1336
+ if (!username || username.includes("\0")) return void 0;
1337
+ const domain = process.env.USERDOMAIN;
1338
+ if (domain && !domain.includes("\0")) return `${domain}\\${username}`;
1339
+ return username;
1340
+ }
1268
1341
  function walkCount(node, vault, counter) {
1269
1342
  if (node === null || node === void 0) return node;
1270
1343
  if (typeof node !== "object") return node;
@@ -1371,10 +1444,15 @@ function writeErr(s, stream = process.stderr) {
1371
1444
 
1372
1445
  // src/utils/color.ts
1373
1446
  var isColorTty = () => {
1374
- if (process.env.NO_COLOR) return false;
1375
- if (process.env.FORCE_COLOR) return true;
1447
+ if (envFlag(process.env.NO_COLOR)) return false;
1448
+ if (envFlag(process.env.FORCE_COLOR)) return true;
1376
1449
  return isStdoutTTY();
1377
1450
  };
1451
+ function envFlag(value) {
1452
+ if (value === void 0) return false;
1453
+ if (value.trim() === "") return false;
1454
+ return !/^(0|false|no|off)$/i.test(value.trim());
1455
+ }
1378
1456
  var COLOR = isColorTty();
1379
1457
  var wrap = (open5, close) => (s) => COLOR ? `\x1B[${open5}m${s}\x1B[${close}m` : s;
1380
1458
  var color = {
@@ -1418,6 +1496,7 @@ var COLORS = {
1418
1496
  debug: color.gray,
1419
1497
  trace: color.dim
1420
1498
  };
1499
+ var LOG_LEVELS = /* @__PURE__ */ new Set(["error", "warn", "info", "debug", "trace"]);
1421
1500
  var DefaultLogger = class _DefaultLogger {
1422
1501
  level;
1423
1502
  file;
@@ -1425,7 +1504,7 @@ var DefaultLogger = class _DefaultLogger {
1425
1504
  pretty;
1426
1505
  stderr;
1427
1506
  constructor(opts = {}) {
1428
- this.level = opts.level ?? process.env.WRONGSTACK_LOG_LEVEL ?? "info";
1507
+ this.level = opts.level ?? parseLogLevel(process.env.WRONGSTACK_LOG_LEVEL);
1429
1508
  this.file = opts.file;
1430
1509
  this.bindings = opts.bindings ?? {};
1431
1510
  this.pretty = opts.pretty ?? true;
@@ -1490,6 +1569,9 @@ var DefaultLogger = class _DefaultLogger {
1490
1569
  }
1491
1570
  }
1492
1571
  };
1572
+ function parseLogLevel(raw) {
1573
+ return raw && LOG_LEVELS.has(raw) ? raw : "info";
1574
+ }
1493
1575
  function formatCtx(ctx) {
1494
1576
  if (ctx instanceof Error) return color.dim(ctx.message);
1495
1577
  if (typeof ctx === "string") return color.dim(ctx);
@@ -1626,13 +1708,25 @@ function round4(n) {
1626
1708
  return Math.round(n * 1e4) / 1e4;
1627
1709
  }
1628
1710
 
1629
- // src/utils/token-estimate.ts
1630
- function expectDefined(value) {
1711
+ // src/types/memory.ts
1712
+ var MEMORY_TYPE_LABELS = {
1713
+ fact: "Fact",
1714
+ decision: "Decision",
1715
+ convention: "Convention",
1716
+ preference: "Preference",
1717
+ reference: "Reference",
1718
+ anti_pattern: "Anti-pattern"
1719
+ };
1720
+
1721
+ // src/utils/expect-defined.ts
1722
+ function expectDefined(value, label) {
1631
1723
  if (value === null || value === void 0) {
1632
- throw new Error("Expected value to be defined");
1724
+ throw new Error(label ? `Expected ${label} to be defined` : "Expected value to be defined");
1633
1725
  }
1634
1726
  return value;
1635
1727
  }
1728
+
1729
+ // src/utils/token-estimate.ts
1636
1730
  var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
1637
1731
  var _cal = {
1638
1732
  ratio: 1,
@@ -1766,12 +1860,6 @@ function resetCalibration() {
1766
1860
  }
1767
1861
 
1768
1862
  // src/utils/message-invariants.ts
1769
- function expectDefined2(value) {
1770
- if (value === null || value === void 0) {
1771
- throw new Error("Expected value to be defined");
1772
- }
1773
- return value;
1774
- }
1775
1863
  function repairToolUseAdjacency(messages) {
1776
1864
  const removedToolUses = [];
1777
1865
  const removedToolResults = [];
@@ -1779,7 +1867,7 @@ function repairToolUseAdjacency(messages) {
1779
1867
  let changed = false;
1780
1868
  const out = [];
1781
1869
  for (let i = 0; i < messages.length; i++) {
1782
- const original = expectDefined2(messages[i]);
1870
+ const original = expectDefined(messages[i]);
1783
1871
  let msg = original;
1784
1872
  if (hasToolUse(msg)) {
1785
1873
  const nextIds = toolResultIds(messages[i + 1]);
@@ -2937,12 +3025,6 @@ Remember: your job is to make the user a better developer, not just to complete
2937
3025
  ];
2938
3026
 
2939
3027
  // src/types/context-window.ts
2940
- function expectDefined3(value) {
2941
- if (value === null || value === void 0) {
2942
- throw new Error("Expected value to be defined");
2943
- }
2944
- return value;
2945
- }
2946
3028
  var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
2947
3029
  var CONTEXT_WINDOW_MODES = Object.freeze([
2948
3030
  {
@@ -2999,7 +3081,7 @@ function isContextWindowModeId(id) {
2999
3081
  }
3000
3082
  function resolveContextWindowPolicy(config = {}, overrideMode) {
3001
3083
  const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
3002
- const mode = getContextWindowMode(requested) ?? expectDefined3(getContextWindowMode(DEFAULT_CONTEXT_WINDOW_MODE_ID));
3084
+ const mode = getContextWindowMode(requested) ?? expectDefined(getContextWindowMode(DEFAULT_CONTEXT_WINDOW_MODE_ID));
3003
3085
  if (mode.id !== DEFAULT_CONTEXT_WINDOW_MODE_ID) {
3004
3086
  return mode;
3005
3087
  }
@@ -3479,17 +3561,12 @@ function sizeSignals(toolName, content) {
3479
3561
  }
3480
3562
 
3481
3563
  // src/execution/tool-executor.ts
3482
- function expectDefined4(value) {
3483
- if (value === null || value === void 0) {
3484
- throw new Error("Expected value to be defined");
3485
- }
3486
- return value;
3487
- }
3488
3564
  var ToolExecutor = class {
3489
3565
  constructor(registry, opts) {
3490
3566
  this.registry = registry;
3491
3567
  this.opts = opts;
3492
3568
  this.iterationTimeoutMs = opts.iterationTimeoutMs ?? 3e5;
3569
+ this.maxToolTimeoutMs = opts.maxToolTimeoutMs ?? 3e5;
3493
3570
  this.serializer = createToolOutputSerializer({
3494
3571
  perIterationOutputCapBytes: opts.perIterationOutputCapBytes ?? 1e5
3495
3572
  });
@@ -3498,6 +3575,7 @@ var ToolExecutor = class {
3498
3575
  opts;
3499
3576
  serializer;
3500
3577
  iterationTimeoutMs;
3578
+ maxToolTimeoutMs;
3501
3579
  /**
3502
3580
  * Clear the interactive confirm awaiter so the executor returns
3503
3581
  * `ToolConfirmPendingResult` instead of blocking on stdin. Used by
@@ -3735,7 +3813,10 @@ ${post.additionalContext}` };
3735
3813
  if (parentSignal.reason instanceof Error) throw parentSignal.reason;
3736
3814
  throw new Error(typeof parentSignal.reason === "string" ? parentSignal.reason : "aborted");
3737
3815
  }
3738
- const timeoutMs = tool.timeoutMs ?? this.iterationTimeoutMs;
3816
+ const timeoutMs = clampTimeoutMs(
3817
+ tool.timeoutMs ?? this.iterationTimeoutMs,
3818
+ this.maxToolTimeoutMs
3819
+ );
3739
3820
  const ctrl = new AbortController();
3740
3821
  const timer = setTimeout(() => ctrl.abort(new Error("tool timeout")), timeoutMs);
3741
3822
  const combined = AbortSignal.any([parentSignal, ctrl.signal]);
@@ -3870,6 +3951,12 @@ ${excerpt}`;
3870
3951
  return void 0;
3871
3952
  }
3872
3953
  };
3954
+ function clampTimeoutMs(timeoutMs, maxTimeoutMs) {
3955
+ const fallback = 3e5;
3956
+ const finiteTimeout = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : fallback;
3957
+ const finiteMax = Number.isFinite(maxTimeoutMs) && maxTimeoutMs > 0 ? maxTimeoutMs : fallback;
3958
+ return Math.max(1, Math.min(finiteTimeout, finiteMax));
3959
+ }
3873
3960
  var MALFORMED_ARG_MARKERS = ["__raw", "__raw_arguments", "_raw"];
3874
3961
  function hasMalformedArguments(input) {
3875
3962
  if (!input || typeof input !== "object" || Array.isArray(input)) return false;
@@ -3880,7 +3967,7 @@ function hasMalformedArguments(input) {
3880
3967
  function extractMalformedRaw(input) {
3881
3968
  if (!hasMalformedArguments(input)) return void 0;
3882
3969
  const obj = input;
3883
- const value = obj[expectDefined4(Object.keys(obj)[0])];
3970
+ const value = obj[expectDefined(Object.keys(obj)[0])];
3884
3971
  if (value === void 0 || value === null) return void 0;
3885
3972
  if (typeof value === "string") return value;
3886
3973
  try {
@@ -3927,12 +4014,6 @@ function compileUserRegex(pattern, flags) {
3927
4014
  }
3928
4015
 
3929
4016
  // src/storage/session-reader.ts
3930
- function expectDefined5(value) {
3931
- if (value === null || value === void 0) {
3932
- throw new Error("Expected value to be defined");
3933
- }
3934
- return value;
3935
- }
3936
4017
  var DefaultSessionReader = class {
3937
4018
  store;
3938
4019
  constructor(opts) {
@@ -3994,7 +4075,7 @@ var DefaultSessionReader = class {
3994
4075
  continue;
3995
4076
  }
3996
4077
  for (let i = 0; i < data.events.length; i++) {
3997
- const ev = expectDefined5(data.events[i]);
4078
+ const ev = expectDefined(data.events[i]);
3998
4079
  if (allowedTypes && !allowedTypes.has(ev.type)) continue;
3999
4080
  const text = eventText(ev);
4000
4081
  if (text === null) continue;
@@ -4362,12 +4443,6 @@ function formatTodosList(todos) {
4362
4443
  }
4363
4444
 
4364
4445
  // src/utils/glob-match.ts
4365
- function expectDefined6(value) {
4366
- if (value === null || value === void 0) {
4367
- throw new Error("Expected value to be defined");
4368
- }
4369
- return value;
4370
- }
4371
4446
  function escapeRegex(s) {
4372
4447
  return s.replace(/[.+^${}()|\\]/g, "\\$&");
4373
4448
  }
@@ -4379,7 +4454,7 @@ function getCachedGlob(pattern) {
4379
4454
  if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
4380
4455
  const keys = [...COMPILED_GLOB_CACHE.keys()];
4381
4456
  for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
4382
- COMPILED_GLOB_CACHE.delete(expectDefined6(keys[i]));
4457
+ COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
4383
4458
  }
4384
4459
  }
4385
4460
  const re = compileGlob(pattern);
@@ -4739,20 +4814,6 @@ function buildChildEnv(optsOrSessionId) {
4739
4814
  function sleep(ms) {
4740
4815
  return new Promise((resolve13) => setTimeout(resolve13, ms));
4741
4816
  }
4742
-
4743
- // src/utils/expect-defined.ts
4744
- function expectDefined7(value) {
4745
- if (value === null || value === void 0) {
4746
- throw new Error("Expected value to be defined");
4747
- }
4748
- return value;
4749
- }
4750
- function expectDefined8(value) {
4751
- if (value === null || value === void 0) {
4752
- throw new Error("Expected value to be defined");
4753
- }
4754
- return value;
4755
- }
4756
4817
  var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
4757
4818
  var IS_WINDOWS = process.platform === "win32";
4758
4819
  var SEP = IS_WINDOWS ? "\\" : "/";
@@ -4766,7 +4827,7 @@ function globToRegex(pat) {
4766
4827
  let i = 0;
4767
4828
  let re = "^";
4768
4829
  while (i < pat.length) {
4769
- const c = expectDefined8(pat[i]);
4830
+ const c = expectDefined(pat[i]);
4770
4831
  if (c === "*") {
4771
4832
  if (pat[i + 1] === "*") {
4772
4833
  re += ".*";
@@ -4805,7 +4866,7 @@ function globToRegex(pat) {
4805
4866
  }
4806
4867
  function baseDir(pat) {
4807
4868
  let i = pat.length - 1;
4808
- while (i >= 0 && !GLOB_CHARS.has(expectDefined8(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
4869
+ while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
4809
4870
  const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
4810
4871
  return cut < 0 ? "." : pat.slice(0, cut);
4811
4872
  }
@@ -4840,8 +4901,8 @@ async function expandGlob(pattern) {
4840
4901
  for (const e of entries) {
4841
4902
  const full = `${dir}${SEP}${e}`;
4842
4903
  try {
4843
- const stat10 = await fsp3.stat(full);
4844
- if (stat10.isDirectory()) await walk4(full, rest);
4904
+ const stat11 = await fsp3.stat(full);
4905
+ if (stat11.isDirectory()) await walk4(full, rest);
4845
4906
  } catch {
4846
4907
  }
4847
4908
  }
@@ -4858,8 +4919,8 @@ async function expandGlob(pattern) {
4858
4919
  if (entries.includes(seg)) {
4859
4920
  const full = `${dir}${SEP}${seg}`;
4860
4921
  try {
4861
- const stat10 = await fsp3.stat(full);
4862
- if (stat10.isDirectory()) await walk4(full, rest);
4922
+ const stat11 = await fsp3.stat(full);
4923
+ if (stat11.isDirectory()) await walk4(full, rest);
4863
4924
  } catch {
4864
4925
  }
4865
4926
  }
@@ -4870,12 +4931,6 @@ async function expandGlob(pattern) {
4870
4931
  }
4871
4932
 
4872
4933
  // src/utils/json-repair.ts
4873
- function expectDefined9(value) {
4874
- if (value === null || value === void 0) {
4875
- throw new Error("Expected value to be defined");
4876
- }
4877
- return value;
4878
- }
4879
4934
  function completePartialObject(s) {
4880
4935
  if (!s.trim().startsWith("{")) return s;
4881
4936
  if (tryParse(s).ok) return s;
@@ -4887,7 +4942,7 @@ function completePartialObject(s) {
4887
4942
  let contentEnd = 0;
4888
4943
  let stringBraceDepth = 0;
4889
4944
  for (let i = 0; i < s.length; i++) {
4890
- const ch = expectDefined9(s[i]);
4945
+ const ch = expectDefined(s[i]);
4891
4946
  if (inString) {
4892
4947
  contentEnd = i + 1;
4893
4948
  if (escaped) {
@@ -4983,12 +5038,6 @@ function mergeCustomModelDefs(providerCustomModels, configModels) {
4983
5038
 
4984
5039
  // src/storage/session-store.ts
4985
5040
  init_atomic_write();
4986
- function expectDefined10(value) {
4987
- if (value === null || value === void 0) {
4988
- throw new Error("Expected value to be defined");
4989
- }
4990
- return value;
4991
- }
4992
5041
  function sanitizeModel(model) {
4993
5042
  return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
4994
5043
  }
@@ -5254,8 +5303,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
5254
5303
  return JSON.parse(raw);
5255
5304
  } catch {
5256
5305
  const full = this.sessionPath(id, ".jsonl");
5257
- const stat10 = await fsp3.stat(full);
5258
- const summary = await this.summarize(id, stat10.mtime.toISOString());
5306
+ const stat11 = await fsp3.stat(full);
5307
+ const summary = await this.summarize(id, stat11.mtime.toISOString());
5259
5308
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
5260
5309
  console.warn(
5261
5310
  `[session-store] Failed to write manifest for "${id}":`,
@@ -5303,8 +5352,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
5303
5352
  if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
5304
5353
  const jsonlPath = path6.join(dateDir, file.name);
5305
5354
  try {
5306
- const stat10 = await fsp3.stat(jsonlPath);
5307
- if (stat10.mtimeMs >= cutoff) continue;
5355
+ const stat11 = await fsp3.stat(jsonlPath);
5356
+ if (stat11.mtimeMs >= cutoff) continue;
5308
5357
  } catch {
5309
5358
  continue;
5310
5359
  }
@@ -5699,7 +5748,7 @@ var FileSessionWriter = class {
5699
5748
  let targetCheckpointLine = -1;
5700
5749
  let afterTarget = false;
5701
5750
  for (let i = 0; i < lines.length; i++) {
5702
- const line = expectDefined10(lines[i]);
5751
+ const line = expectDefined(lines[i]);
5703
5752
  if (!line.trim()) continue;
5704
5753
  let event;
5705
5754
  try {
@@ -5994,12 +6043,224 @@ function mergeAdjacentText(blocks) {
5994
6043
  }
5995
6044
  return out;
5996
6045
  }
6046
+ init_atomic_write();
6047
+ var TYPE_PRIORITY_RE = /^\[(\w+)\|(\w+)\]\s+/;
6048
+ var TAG_RE = /#([\w-]+)/g;
6049
+ function formatMetadata(entry) {
6050
+ const parts = [];
6051
+ if (entry.type && entry.priority) {
6052
+ parts.push(`[${entry.type}|${entry.priority}]`);
6053
+ } else if (entry.type) {
6054
+ parts.push(`[${entry.type}]`);
6055
+ } else if (entry.priority) {
6056
+ parts.push(`[${entry.priority}]`);
6057
+ }
6058
+ if (entry.tags && entry.tags.length > 0) {
6059
+ parts.push(entry.tags.map((t2) => `#${t2}`).join(" "));
6060
+ }
6061
+ return parts.length > 0 ? ` ${parts.join(" ")}` : "";
6062
+ }
6063
+ function lineToEntry(line, scope) {
6064
+ const trimmed = line.trim();
6065
+ if (!trimmed.startsWith("- [")) return null;
6066
+ const tsMatch = trimmed.match(/^-\s*\[([^\]]+)\]/);
6067
+ if (!tsMatch) return null;
6068
+ const ts = tsMatch[1] ?? "";
6069
+ let rest = trimmed.slice(tsMatch[0].length).trim();
6070
+ let type;
6071
+ let priority;
6072
+ const tpMatch = rest.match(TYPE_PRIORITY_RE);
6073
+ if (tpMatch) {
6074
+ const a = tpMatch[1] ?? "";
6075
+ const b = tpMatch[2] ?? "";
6076
+ if (isMemoryType(a)) {
6077
+ type = a;
6078
+ priority = isPriority(b) ? b : void 0;
6079
+ } else if (isPriority(a)) {
6080
+ priority = a;
6081
+ }
6082
+ rest = rest.slice(tpMatch[0].length).trim();
6083
+ }
6084
+ const idMatch = rest.match(/^mem_\d+_\w+\s+/);
6085
+ let text;
6086
+ if (idMatch) {
6087
+ text = rest.slice(idMatch[0].length).trim();
6088
+ } else {
6089
+ text = rest.trim();
6090
+ }
6091
+ const tags = [];
6092
+ let tagMatch;
6093
+ TAG_RE.lastIndex = 0;
6094
+ while ((tagMatch = TAG_RE.exec(text)) !== null) {
6095
+ tags.push(tagMatch[1] ?? "");
6096
+ }
6097
+ const cleanText = text.replace(TAG_RE, "").replace(/\s{2,}/g, " ").trim();
6098
+ if (!cleanText) return null;
6099
+ return {
6100
+ scope,
6101
+ text: cleanText,
6102
+ ts,
6103
+ type,
6104
+ priority,
6105
+ tags: tags.length > 0 ? tags : void 0
6106
+ };
6107
+ }
6108
+ function isMemoryType(s) {
6109
+ return s in MEMORY_TYPE_LABELS;
6110
+ }
6111
+ function isPriority(s) {
6112
+ return s === "critical" || s === "high" || s === "medium" || s === "low";
6113
+ }
6114
+ var FileMemoryBackend = class {
6115
+ kind = "file";
6116
+ files;
6117
+ constructor(opts) {
6118
+ this.files = {
6119
+ "project-agents": opts.paths.inProjectAgentsFile,
6120
+ "project-memory": opts.paths.projectMemory,
6121
+ "user-memory": opts.paths.globalMemory
6122
+ };
6123
+ }
6124
+ resolveFile(filePath, scope) {
6125
+ return filePath || this.files[scope];
6126
+ }
6127
+ async remember(scope, entry, filePath) {
6128
+ const file = this.resolveFile(filePath, scope);
6129
+ await ensureDir(path6.dirname(file));
6130
+ let existing = "";
6131
+ try {
6132
+ existing = await fsp3.readFile(file, "utf8");
6133
+ } catch {
6134
+ }
6135
+ const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
6136
+ const meta = formatMetadata(entry);
6137
+ const line = `
6138
+ - [${entry.ts}] ${id}${meta} ${entry.text.replace(/\n/g, " ")}
6139
+ `;
6140
+ const next = existing.trim() ? existing.replace(/\n+$/, "") + line : `# WrongStack Memory
6141
+ ${line}`;
6142
+ await atomicWrite(file, next);
6143
+ }
6144
+ async forget(scope, query, filePath) {
6145
+ const file = this.resolveFile(filePath, scope);
6146
+ let existing;
6147
+ try {
6148
+ existing = await fsp3.readFile(file, "utf8");
6149
+ } catch {
6150
+ return 0;
6151
+ }
6152
+ const needle = query.toLowerCase();
6153
+ const idMatcher = /mem_\d+_\w+/;
6154
+ let removed = 0;
6155
+ const lines = existing.split("\n").filter((line) => {
6156
+ const trimmed = line.trim();
6157
+ if (!trimmed.startsWith("- ")) return true;
6158
+ if (idMatcher.test(query)) {
6159
+ const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
6160
+ if (entryIdMatch && entryIdMatch[0] === query) {
6161
+ removed++;
6162
+ return false;
6163
+ }
6164
+ }
6165
+ if (trimmed.toLowerCase().includes(needle)) {
6166
+ removed++;
6167
+ return false;
6168
+ }
6169
+ return true;
6170
+ });
6171
+ if (removed > 0) {
6172
+ if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
6173
+ await atomicWrite(file, "");
6174
+ } else {
6175
+ await atomicWrite(file, lines.join("\n"));
6176
+ }
6177
+ }
6178
+ return removed;
6179
+ }
6180
+ async readAll(scope, filePath) {
6181
+ const file = this.resolveFile(filePath, scope);
6182
+ try {
6183
+ return await fsp3.readFile(file, "utf8");
6184
+ } catch {
6185
+ return "";
6186
+ }
6187
+ }
6188
+ async list(scope, filePath, limit) {
6189
+ const raw = await this.readAll(scope, filePath);
6190
+ if (!raw.trim()) return [];
6191
+ const entries = parseEntries(raw, scope);
6192
+ return limit ? entries.slice(0, limit) : entries;
6193
+ }
6194
+ async search(scope, query, filePath, limit) {
6195
+ const entries = await this.list(scope, filePath);
6196
+ const needle = query.toLowerCase().split(/\s+/);
6197
+ const scored = entries.map((e) => {
6198
+ const words = e.text.toLowerCase().split(/\s+/);
6199
+ let score = 0;
6200
+ for (const n of needle) {
6201
+ if (words.some((w) => w.includes(n))) score += 1;
6202
+ if (e.tags?.some((t2) => t2.toLowerCase().includes(n))) score += 2;
6203
+ }
6204
+ return { entry: e, score };
6205
+ });
6206
+ scored.sort((a, b) => b.score - a.score);
6207
+ const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
6208
+ return limit ? matched.slice(0, limit) : matched;
6209
+ }
6210
+ async clear(scope, filePath) {
6211
+ const file = this.resolveFile(filePath, scope);
6212
+ await atomicWrite(file, "");
6213
+ }
6214
+ async consolidate(scope, filePath) {
6215
+ const file = this.resolveFile(filePath, scope);
6216
+ let existing;
6217
+ try {
6218
+ existing = await fsp3.readFile(file, "utf8");
6219
+ } catch {
6220
+ return 0;
6221
+ }
6222
+ const seen = /* @__PURE__ */ new Set();
6223
+ let removed = 0;
6224
+ const lines = existing.split("\n").filter((line) => {
6225
+ const trimmed = line.trim();
6226
+ if (!trimmed.startsWith("- ")) return true;
6227
+ const norm = trimmed.replace(/\[[^\]]+\]/g, "").replace(/\bmem_\d+_\w+\s*/, "").replace(/#[\w-]+/g, "").replace(/\s+/g, " ").trim().toLowerCase();
6228
+ if (seen.has(norm)) {
6229
+ removed++;
6230
+ return false;
6231
+ }
6232
+ seen.add(norm);
6233
+ return true;
6234
+ });
6235
+ const next = lines.join("\n");
6236
+ const backup = `${file}.bak.${Date.now()}`;
6237
+ try {
6238
+ await fsp3.copyFile(file, backup);
6239
+ } catch {
6240
+ }
6241
+ try {
6242
+ await atomicWrite(file, next);
6243
+ } catch {
6244
+ return 0;
6245
+ }
6246
+ return removed;
6247
+ }
6248
+ };
6249
+ function parseEntries(raw, scope = "project-memory") {
6250
+ const entries = [];
6251
+ for (const line of raw.split("\n")) {
6252
+ const entry = lineToEntry(line, scope);
6253
+ if (entry) entries.push(entry);
6254
+ }
6255
+ return entries.reverse();
6256
+ }
5997
6257
 
5998
6258
  // src/storage/memory-store.ts
5999
- init_atomic_write();
6000
6259
  var MAX_BYTES_TOTAL = 32e3;
6001
6260
  var DefaultMemoryStore = class {
6002
6261
  files;
6262
+ events;
6263
+ backend;
6003
6264
  /**
6004
6265
  * Per-scope serialization queue. `remember` / `forget` / `consolidate` /
6005
6266
  * `clear` are read-modify-write against a single file; without a lock,
@@ -6016,12 +6277,29 @@ var DefaultMemoryStore = class {
6016
6277
  writeChain = /* @__PURE__ */ new Map();
6017
6278
  /** Last write error per scope — surfaced as warnings on the next readAll(). */
6018
6279
  writeErrors = /* @__PURE__ */ new Map();
6280
+ /**
6281
+ * When the global root is a temporary directory (opencode, CI sandboxes),
6282
+ * memory files are also mirrored to the project tree so they survive
6283
+ * session cleanup. The primary path stays in the temp root for isolation;
6284
+ * this backup ensures memory is never lost.
6285
+ */
6286
+ persistBackup;
6287
+ backupDir;
6019
6288
  constructor(opts) {
6020
6289
  this.files = {
6021
6290
  "project-agents": opts.paths.inProjectAgentsFile,
6022
6291
  "project-memory": opts.paths.projectMemory,
6023
6292
  "user-memory": opts.paths.globalMemory
6024
6293
  };
6294
+ this.events = opts.events;
6295
+ this.backend = opts.backend ?? new FileMemoryBackend({ paths: opts.paths });
6296
+ const root = opts.paths.globalRoot.toLowerCase();
6297
+ this.persistBackup = /[/\\](tmp|temp|cache)[/\\]/.test(root);
6298
+ this.backupDir = this.persistBackup ? opts.paths.inProjectAgentsFile.replace(/AGENTS\.md$/, "memory-persist") : "";
6299
+ }
6300
+ /** Expose the backend for plugin introspection and advanced queries. */
6301
+ getBackend() {
6302
+ return this.backend;
6025
6303
  }
6026
6304
  async runSerialized(scope, work) {
6027
6305
  const prior = this.writeChain.get(scope) ?? Promise.resolve();
@@ -6048,7 +6326,7 @@ var DefaultMemoryStore = class {
6048
6326
  if (writeErr2) {
6049
6327
  parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
6050
6328
  }
6051
- const body = await this.read(scope);
6329
+ const body = await this.backend.readAll(scope, this.files[scope]);
6052
6330
  if (body.trim()) parts.push(`## ${labelOf(scope)}
6053
6331
 
6054
6332
  ${body.trim()}`);
@@ -6056,117 +6334,214 @@ ${body.trim()}`);
6056
6334
  return parts.join("\n\n");
6057
6335
  }
6058
6336
  async read(scope) {
6059
- try {
6060
- return await fsp3.readFile(this.files[scope], "utf8");
6061
- } catch {
6062
- return "";
6337
+ return this.backend.readAll(scope, this.files[scope]);
6338
+ }
6339
+ /**
6340
+ * List entries from a scope, newest first. Delegates to the backend
6341
+ * so graph/semantic backends can return enriched or filtered results.
6342
+ */
6343
+ async list(scope = "project-memory", limit) {
6344
+ return this.backend.list(scope, this.files[scope], limit);
6345
+ }
6346
+ /**
6347
+ * Find memories related to the given text via graph traversal.
6348
+ * Falls back to content search when no graph backend is available.
6349
+ */
6350
+ async findRelated(text, scope = "project-memory", limit = 5) {
6351
+ if (this.backend.findRelated) {
6352
+ return this.backend.findRelated(scope, this.files[scope], text, limit);
6063
6353
  }
6354
+ return this.search(text, scope, limit);
6355
+ }
6356
+ async search(query, scope = "project-memory", limit) {
6357
+ return this.backend.search(scope, query, this.files[scope], limit);
6064
6358
  }
6065
- async remember(text, scope = "project-memory") {
6359
+ async remember(text, scope = "project-memory", metadata) {
6360
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
6066
6361
  return this.runSerialized(scope, async () => {
6067
- const file = this.files[scope];
6068
- await ensureDir(path6.dirname(file));
6069
- let existing = "";
6070
- try {
6071
- existing = await fsp3.readFile(file, "utf8");
6072
- } catch {
6073
- }
6074
- const ts = (/* @__PURE__ */ new Date()).toISOString();
6075
- const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
6076
- const entry = `
6077
- - [${ts}] ${id} ${text.replace(/\n/g, " ")}
6078
- `;
6079
- const next = existing.trim() ? existing.replace(/\n+$/, "") + entry : `# WrongStack Memory
6080
- ${entry}`;
6081
- await atomicWrite(file, next);
6082
- const buf = Buffer.byteLength(next, "utf8");
6083
- if (buf > MAX_BYTES_TOTAL) {
6084
- await this.consolidateUnsafe(scope);
6362
+ const entry = { scope, text, ts, ...metadata };
6363
+ await this.backend.remember(scope, entry, this.files[scope]);
6364
+ const raw = await this.backend.readAll(scope, this.files[scope]);
6365
+ if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
6366
+ const removed = await this.backend.consolidate(scope, this.files[scope]);
6367
+ if (removed > 0) {
6368
+ this.events?.emit("memory.consolidated", {
6369
+ scope,
6370
+ removed
6371
+ });
6372
+ }
6085
6373
  }
6374
+ await this.mirrorBackup(scope);
6375
+ this.events?.emit("memory.remembered", {
6376
+ scope,
6377
+ text,
6378
+ ts,
6379
+ type: entry.type,
6380
+ tags: entry.tags,
6381
+ priority: entry.priority
6382
+ });
6086
6383
  });
6087
6384
  }
6088
- async forget(query, scope = "project-memory") {
6089
- return this.runSerialized(scope, async () => this.forgetUnsafe(query, scope));
6090
- }
6091
- async forgetUnsafe(query, scope) {
6092
- const file = this.files[scope];
6093
- let existing;
6094
- try {
6095
- existing = await fsp3.readFile(file, "utf8");
6096
- } catch {
6097
- return 0;
6098
- }
6099
- const needle = query.toLowerCase();
6100
- const idMatcher = /mem_\d+_\w+/;
6101
- let removed = 0;
6102
- const lines = existing.split("\n").filter((line) => {
6103
- const trimmed = line.trim();
6104
- if (!trimmed.startsWith("- ")) return true;
6105
- if (idMatcher.test(query)) {
6106
- const afterBracket = trimmed.indexOf("] ");
6107
- if (afterBracket !== -1) {
6108
- const afterTs = trimmed.slice(afterBracket + 2);
6109
- const entryIdMatch = /^mem_\d+_\w+/.exec(afterTs);
6110
- if (entryIdMatch && entryIdMatch[0] === query) {
6111
- removed++;
6112
- return false;
6113
- }
6385
+ /**
6386
+ * Score and rank memories by relevance to the current context.
6387
+ * Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
6388
+ */
6389
+ async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
6390
+ const all = await this.list(scope);
6391
+ if (all.length === 0) return [];
6392
+ const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
6393
+ const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
6394
+ const toolWords = (ctx.toolNames ?? []).flatMap((t2) => t2.toLowerCase().split("_"));
6395
+ const now = Date.now();
6396
+ const scored = [];
6397
+ for (const entry of all) {
6398
+ let score = 0;
6399
+ const reasons = [];
6400
+ const textLower = entry.text.toLowerCase();
6401
+ const tagsLower = (entry.tags ?? []).map((t2) => t2.toLowerCase());
6402
+ let taskHits = 0;
6403
+ for (const w of taskWords) {
6404
+ if (textLower.includes(w)) {
6405
+ taskHits++;
6406
+ score += 2;
6407
+ }
6408
+ if (tagsLower.some((t2) => t2.includes(w))) {
6409
+ taskHits++;
6410
+ score += 3;
6114
6411
  }
6115
6412
  }
6116
- if (trimmed.toLowerCase().includes(needle)) {
6117
- removed++;
6118
- return false;
6413
+ if (taskHits > 0) reasons.push(`task match (${taskHits})`);
6414
+ let skillHits = 0;
6415
+ for (const w of skillWords) {
6416
+ if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t2) => t2.includes(w)))) {
6417
+ skillHits++;
6418
+ }
6419
+ }
6420
+ score += skillHits;
6421
+ if (skillHits > 0) reasons.push(`skill match (${skillHits})`);
6422
+ for (const w of toolWords) {
6423
+ if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t2) => t2.includes(w)))) {
6424
+ score += 1;
6425
+ reasons.push(`tool mention: ${w}`);
6426
+ }
6427
+ }
6428
+ switch (entry.priority) {
6429
+ case "critical":
6430
+ score += 5;
6431
+ reasons.push("critical");
6432
+ break;
6433
+ case "high":
6434
+ score += 3;
6435
+ reasons.push("high priority");
6436
+ break;
6437
+ case "medium":
6438
+ score += 1;
6439
+ break;
6440
+ case "low":
6441
+ score -= 2;
6442
+ reasons.push("low priority");
6443
+ break;
6444
+ }
6445
+ switch (entry.type) {
6446
+ case "decision":
6447
+ score += 2;
6448
+ reasons.push("decision");
6449
+ break;
6450
+ case "convention":
6451
+ score += 2;
6452
+ reasons.push("convention");
6453
+ break;
6454
+ case "anti_pattern":
6455
+ score += 3;
6456
+ reasons.push("anti-pattern");
6457
+ break;
6458
+ case "preference":
6459
+ score += 1;
6460
+ reasons.push("preference");
6461
+ break;
6462
+ }
6463
+ const ageDays = (now - new Date(entry.ts).getTime()) / (1e3 * 60 * 60 * 24);
6464
+ if (ageDays < 1) score += 1;
6465
+ else if (ageDays > 30) score -= 1;
6466
+ if (entry.confidence !== void 0 && entry.confidence < 0.5) {
6467
+ score -= 2;
6468
+ reasons.push("low confidence");
6469
+ }
6470
+ if (entry.lastAccessed) {
6471
+ const hoursSinceAccess = (now - new Date(entry.lastAccessed).getTime()) / (1e3 * 60 * 60);
6472
+ if (hoursSinceAccess < 1) score -= 1;
6473
+ }
6474
+ if (score > 0) {
6475
+ scored.push({
6476
+ ...entry,
6477
+ score,
6478
+ matchReason: reasons.join(", ") || "keyword match"
6479
+ });
6119
6480
  }
6120
- return true;
6121
- });
6122
- if (removed > 0) {
6123
- await atomicWrite(file, lines.join("\n"));
6124
6481
  }
6125
- return removed;
6482
+ scored.sort((a, b) => b.score - a.score);
6483
+ const threshold = 2;
6484
+ const relevant = scored.filter(
6485
+ (s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
6486
+ );
6487
+ return relevant.slice(0, Math.min(limit, 15));
6126
6488
  }
6127
- async consolidate(scope) {
6128
- return this.runSerialized(scope, async () => this.consolidateUnsafe(scope));
6489
+ async forget(query, scope = "project-memory") {
6490
+ return this.runSerialized(scope, async () => {
6491
+ const removed = await this.backend.forget(scope, query, this.files[scope]);
6492
+ if (removed > 0) {
6493
+ this.events?.emit("memory.forgotten", {
6494
+ scope,
6495
+ query,
6496
+ removed
6497
+ });
6498
+ await this.mirrorBackup(scope);
6499
+ }
6500
+ return removed;
6501
+ });
6129
6502
  }
6130
- async consolidateUnsafe(scope) {
6131
- const file = this.files[scope];
6132
- let existing;
6133
- try {
6134
- existing = await fsp3.readFile(file, "utf8");
6135
- } catch {
6136
- return;
6137
- }
6138
- const seen = /* @__PURE__ */ new Set();
6139
- const lines = existing.split("\n").filter((line) => {
6140
- const trimmed = line.trim();
6141
- if (!trimmed.startsWith("- ")) return true;
6142
- const norm = trimmed.replace(/\[[^\]]+\]/, "").replace(/\bmem_\d+_\w+\s*/, "").trim().toLowerCase();
6143
- if (seen.has(norm)) return false;
6144
- seen.add(norm);
6145
- return true;
6503
+ async consolidate(scope) {
6504
+ return this.runSerialized(scope, async () => {
6505
+ const removed = await this.backend.consolidate(scope, this.files[scope]);
6506
+ if (removed > 0) {
6507
+ this.events?.emit("memory.consolidated", {
6508
+ scope,
6509
+ removed
6510
+ });
6511
+ await this.mirrorBackup(scope);
6512
+ }
6146
6513
  });
6147
- const next = lines.join("\n");
6148
- const backup = `${file}.bak.${Date.now()}`;
6149
- try {
6150
- await fsp3.copyFile(file, backup);
6151
- } catch {
6152
- }
6153
- try {
6154
- await atomicWrite(file, next);
6155
- } catch {
6156
- return;
6157
- }
6158
6514
  }
6159
6515
  async clear(scope) {
6160
6516
  if (scope) {
6161
- await this.runSerialized(scope, async () => atomicWrite(this.files[scope], ""));
6517
+ await this.runSerialized(scope, async () => {
6518
+ await this.backend.clear(scope, this.files[scope]);
6519
+ this.events?.emit("memory.cleared", { scope });
6520
+ await this.mirrorBackup(scope);
6521
+ });
6162
6522
  return;
6163
6523
  }
6164
6524
  await Promise.all(
6165
6525
  ["project-agents", "project-memory", "user-memory"].map(
6166
- (s) => this.runSerialized(s, async () => atomicWrite(this.files[s], ""))
6526
+ (s) => this.runSerialized(s, async () => {
6527
+ await this.backend.clear(s, this.files[s]);
6528
+ this.events?.emit("memory.cleared", { scope: s });
6529
+ await this.mirrorBackup(s);
6530
+ })
6167
6531
  )
6168
6532
  );
6169
6533
  }
6534
+ /** Mirror current memory content to the persistent backup directory. */
6535
+ async mirrorBackup(scope) {
6536
+ if (!this.persistBackup || scope === "project-agents") return;
6537
+ try {
6538
+ const content = await this.backend.readAll(scope, this.files[scope]);
6539
+ const { writeFile: writeFile11, mkdir: mkdir17 } = await import('fs/promises');
6540
+ await mkdir17(this.backupDir, { recursive: true });
6541
+ await writeFile11(`${this.backupDir}/${scope}.md`, content, "utf8");
6542
+ } catch {
6543
+ }
6544
+ }
6170
6545
  };
6171
6546
  function labelOf(scope) {
6172
6547
  switch (scope) {
@@ -6321,6 +6696,13 @@ var BEHAVIOR_DEFAULTS = {
6321
6696
  function envBool(v) {
6322
6697
  return !/^(0|false|no|off)$/i.test(v.trim());
6323
6698
  }
6699
+ function envBoolOptional(v) {
6700
+ return v !== void 0 && envBool(v);
6701
+ }
6702
+ var LOG_LEVELS2 = /* @__PURE__ */ new Set(["error", "warn", "info", "debug", "trace"]);
6703
+ function envLogLevel(v) {
6704
+ return LOG_LEVELS2.has(v) ? v : "info";
6705
+ }
6324
6706
  var ENV_MAP = {
6325
6707
  WRONGSTACK_PROVIDER: (c, v) => {
6326
6708
  c.provider = v;
@@ -6344,7 +6726,7 @@ var ENV_MAP = {
6344
6726
  },
6345
6727
  WRONGSTACK_LOG_LEVEL: (c, v) => {
6346
6728
  if (!c.log) c.log = { level: "info" };
6347
- c.log.level = v;
6729
+ c.log.level = envLogLevel(v);
6348
6730
  },
6349
6731
  WRONGSTACK_INDEX_ON_START: (c, v) => {
6350
6732
  c.indexing = { ...defaultIndexing, ...c.indexing, onSessionStart: envBool(v) };
@@ -6386,7 +6768,7 @@ function deepMerge2(base, patch) {
6386
6768
  out[k] = [.../* @__PURE__ */ new Set([...existing, ...v])];
6387
6769
  } else {
6388
6770
  out[k] = v;
6389
- if (process.env.WRONGSTACK_DEBUG_CONFIG) {
6771
+ if (envBoolOptional(process.env.WRONGSTACK_DEBUG_CONFIG)) {
6390
6772
  console.warn(
6391
6773
  `[config] Non-primitive array for "${k}" replaced (global + local config merge). Global entries: ${existing?.length ?? 0}, local entries: ${v.length}.`
6392
6774
  );
@@ -6980,7 +7362,10 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
6980
7362
  let pending = null;
6981
7363
  let writeChain = Promise.resolve();
6982
7364
  const enqueueWrite = (todos) => {
6983
- writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos));
7365
+ writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos)).catch((err) => {
7366
+ const msg = err instanceof Error ? err.message : String(err);
7367
+ console.error(`[TodosCheckpoint] save failed for session ${sessionId}: ${msg}`);
7368
+ });
6984
7369
  return writeChain;
6985
7370
  };
6986
7371
  const flush = () => {
@@ -7284,7 +7669,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
7284
7669
  }
7285
7670
  const lock = {
7286
7671
  pid: processId,
7287
- hostname: __require("os").hostname(),
7672
+ hostname: hostname(),
7288
7673
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
7289
7674
  };
7290
7675
  await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
@@ -7954,6 +8339,7 @@ function parseDescription(raw) {
7954
8339
  }
7955
8340
  return { trigger, scope };
7956
8341
  }
8342
+ var STREAM_DRAIN_TIMEOUT_MS = 500;
7957
8343
  function buildResponse(state) {
7958
8344
  const content = [];
7959
8345
  for (const b of state.blockOrder) {
@@ -8166,7 +8552,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
8166
8552
  await Promise.race([
8167
8553
  Promise.resolve(iter.return?.()),
8168
8554
  new Promise((resolve13) => {
8169
- drainTimer = setTimeout(resolve13, 500);
8555
+ drainTimer = setTimeout(resolve13, STREAM_DRAIN_TIMEOUT_MS);
8170
8556
  })
8171
8557
  ]);
8172
8558
  } finally {
@@ -8505,12 +8891,6 @@ var IntelligentCompactor = class {
8505
8891
  };
8506
8892
 
8507
8893
  // src/models/llm-selector.ts
8508
- function expectDefined11(value) {
8509
- if (value === null || value === void 0) {
8510
- throw new Error("Expected value to be defined");
8511
- }
8512
- return value;
8513
- }
8514
8894
  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.
8515
8895
 
8516
8896
  Output a JSON object with this structure:
@@ -8551,7 +8931,7 @@ function formatMessages(messages, maxChars = 8e3) {
8551
8931
  const lines = [];
8552
8932
  let used = 0;
8553
8933
  for (let i = 0; i < messages.length; i++) {
8554
- const m = expectDefined11(messages[i]);
8934
+ const m = expectDefined(messages[i]);
8555
8935
  const role = m.role.padEnd(10, " ");
8556
8936
  let text;
8557
8937
  if (typeof m.content === "string") {
@@ -8616,7 +8996,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
8616
8996
  let tokenCount = 0;
8617
8997
  let startIdx = 0;
8618
8998
  for (let i = messages.length - 1; i >= 0; i--) {
8619
- const m = expectDefined11(messages[i]);
8999
+ const m = expectDefined(messages[i]);
8620
9000
  const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
8621
9001
  (acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
8622
9002
  0
@@ -9904,8 +10284,8 @@ ${recentJournal}` : "No prior iterations.",
9904
10284
  await saveGoal(this.goalPath, abandoned);
9905
10285
  }
9906
10286
  try {
9907
- const { unlink: unlink13 } = await import('fs/promises');
9908
- await unlink13(this.goalPath);
10287
+ const { unlink: unlink14 } = await import('fs/promises');
10288
+ await unlink14(this.goalPath);
9909
10289
  } catch {
9910
10290
  }
9911
10291
  this.opts.onEternalStop?.();
@@ -9928,6 +10308,7 @@ ${recentJournal}` : "No prior iterations.",
9928
10308
  };
9929
10309
 
9930
10310
  // src/coordination/subagent-budget.ts
10311
+ var TIMEOUT_PREEMPT_FRACTION = 0.85;
9931
10312
  var BudgetExceededError = class extends Error {
9932
10313
  kind;
9933
10314
  limit;
@@ -10283,15 +10664,7 @@ function makeAgentSubagentRunner(opts) {
10283
10664
  let budgetError = null;
10284
10665
  const onBudgetError = (err) => {
10285
10666
  if (err instanceof BudgetThresholdSignal) {
10286
- err.decision.then((decision) => {
10287
- if (decision === "stop") {
10288
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
10289
- aborter.abort();
10290
- }
10291
- }).catch(() => {
10292
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
10293
- aborter.abort();
10294
- });
10667
+ budgetError = err;
10295
10668
  return;
10296
10669
  }
10297
10670
  aborter.abort();
@@ -10399,15 +10772,23 @@ function makeAgentSubagentRunner(opts) {
10399
10772
  }
10400
10773
  if (budgetError) {
10401
10774
  if ("decision" in budgetError) {
10402
- const decision = await budgetError.decision;
10403
- if (decision === "stop") {
10775
+ try {
10776
+ const decision = await budgetError.decision;
10777
+ if (decision === "stop") {
10778
+ budgetError = new BudgetExceededError(
10779
+ budgetError.kind,
10780
+ budgetError.limit,
10781
+ budgetError.used
10782
+ );
10783
+ } else {
10784
+ budgetError = null;
10785
+ }
10786
+ } catch {
10404
10787
  budgetError = new BudgetExceededError(
10405
10788
  budgetError.kind,
10406
10789
  budgetError.limit,
10407
10790
  budgetError.used
10408
10791
  );
10409
- } else {
10410
- budgetError = null;
10411
10792
  }
10412
10793
  }
10413
10794
  if (budgetError) throw budgetError;
@@ -12729,16 +13110,17 @@ Do not add prose, markdown, or code fences.`;
12729
13110
  const raw = (await complete(prompt)).trim();
12730
13111
  const match = raw.match(/\{[\s\S]*\}/);
12731
13112
  if (!match) return null;
12732
- try {
12733
- const parsed = JSON.parse(match[0]);
12734
- if (typeof parsed.role !== "string") return null;
12735
- const role = parsed.role.trim();
12736
- const valid = candidates.some((c) => c.role === role);
12737
- if (!valid) return null;
12738
- return { role, reason: typeof parsed.reason === "string" ? parsed.reason : void 0 };
12739
- } catch {
12740
- return null;
12741
- }
13113
+ const parsed = safeParse(
13114
+ match[0]
13115
+ );
13116
+ if (!parsed.ok || !parsed.value || typeof parsed.value.role !== "string") return null;
13117
+ const role = parsed.value.role.trim();
13118
+ const valid = candidates.some((c) => c.role === role);
13119
+ if (!valid) return null;
13120
+ return {
13121
+ role,
13122
+ reason: typeof parsed.value.reason === "string" ? parsed.value.reason : void 0
13123
+ };
12742
13124
  };
12743
13125
  }
12744
13126
 
@@ -13693,15 +14075,38 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
13693
14075
  }
13694
14076
  const start = Date.now();
13695
14077
  let timer = null;
14078
+ let preemptedForLimit = null;
13696
14079
  const timeoutPromise = new Promise((_, reject) => {
13697
14080
  const armFor = (ms) => {
13698
14081
  if (timer) clearTimeout(timer);
13699
14082
  timer = setTimeout(onTick, Math.max(0, ms));
13700
14083
  };
13701
14084
  const scheduleNext = () => {
13702
- const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.timeoutMs ?? initialTimeoutMs) - (Date.now() - start);
14085
+ const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
14086
+ const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
13703
14087
  const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
13704
- armFor(Math.max(25, Math.min(wallRemaining, idleRemaining)));
14088
+ const preemptRemaining = initialTimeoutMs === void 0 || preemptedForLimit === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * TIMEOUT_PREEMPT_FRACTION - (Date.now() - start);
14089
+ armFor(Math.max(25, Math.min(wallRemaining, idleRemaining, preemptRemaining)));
14090
+ };
14091
+ const negotiateTimeout = async (used, limit) => {
14092
+ const handler = budget.onThreshold;
14093
+ if (!handler) return "stop";
14094
+ const result = handler({
14095
+ kind: "timeout",
14096
+ used,
14097
+ limit,
14098
+ requestDecision: () => new Promise((resolveDecision) => {
14099
+ budget._events?.emit("budget.threshold_reached", {
14100
+ kind: "timeout",
14101
+ used,
14102
+ limit,
14103
+ timeoutMs: 6e4,
14104
+ extend: (extra) => resolveDecision({ extend: extra }),
14105
+ deny: () => resolveDecision("stop")
14106
+ });
14107
+ })
14108
+ });
14109
+ return typeof result === "string" ? result : await result;
13705
14110
  };
13706
14111
  const onTick = async () => {
13707
14112
  const elapsed = Date.now() - start;
@@ -13714,6 +14119,21 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
13714
14119
  reject(new BudgetExceededError("timeout", idleLimit ?? 0, budget.idleMs()));
13715
14120
  return;
13716
14121
  }
14122
+ if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptedForLimit !== wallLimit && elapsed >= wallLimit * TIMEOUT_PREEMPT_FRACTION) {
14123
+ try {
14124
+ const decision = await negotiateTimeout(elapsed, wallLimit);
14125
+ if (typeof decision !== "string" && decision.extend.timeoutMs !== void 0) {
14126
+ budget.limits.timeoutMs = decision.extend.timeoutMs;
14127
+ preemptedForLimit = null;
14128
+ } else {
14129
+ preemptedForLimit = wallLimit;
14130
+ }
14131
+ } catch {
14132
+ preemptedForLimit = wallLimit;
14133
+ }
14134
+ scheduleNext();
14135
+ return;
14136
+ }
13717
14137
  if (!wallExceeded) {
13718
14138
  scheduleNext();
13719
14139
  return;
@@ -13725,28 +14145,15 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
13725
14145
  return;
13726
14146
  }
13727
14147
  try {
13728
- const result = budget.onThreshold({
13729
- kind: "timeout",
13730
- used: elapsed,
13731
- limit,
13732
- requestDecision: () => new Promise((resolveDecision) => {
13733
- budget._events?.emit("budget.threshold_reached", {
13734
- kind: "timeout",
13735
- used: elapsed,
13736
- limit,
13737
- timeoutMs: 6e4,
13738
- extend: (extra) => resolveDecision({ extend: extra }),
13739
- deny: () => resolveDecision("stop")
13740
- });
13741
- })
13742
- });
13743
- const decision = typeof result === "string" ? result : await result;
14148
+ const decision = await negotiateTimeout(elapsed, limit);
13744
14149
  if (decision === "continue" || decision === "throw" || decision === "stop") {
14150
+ preemptedForLimit = null;
13745
14151
  armFor(Math.max(1e3, limit));
13746
14152
  return;
13747
14153
  }
13748
14154
  if (decision.extend.timeoutMs !== void 0) {
13749
14155
  budget.limits.timeoutMs = decision.extend.timeoutMs;
14156
+ preemptedForLimit = null;
13750
14157
  scheduleNext();
13751
14158
  return;
13752
14159
  }
@@ -13877,12 +14284,6 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
13877
14284
  };
13878
14285
 
13879
14286
  // src/execution/parallel-eternal-engine.ts
13880
- function expectDefined12(value) {
13881
- if (value === null || value === void 0) {
13882
- throw new Error("Expected value to be defined");
13883
- }
13884
- return value;
13885
- }
13886
14287
  var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
13887
14288
  var ParallelEternalEngine = class {
13888
14289
  constructor(opts) {
@@ -14038,7 +14439,7 @@ var ParallelEternalEngine = class {
14038
14439
  // Fan-out
14039
14440
  // -------------------------------------------------------------------------
14040
14441
  async fanOut(goal, tasks) {
14041
- const coordinator = expectDefined12(this.coordinator);
14442
+ const coordinator = expectDefined(this.coordinator);
14042
14443
  const slotCount = Math.min(this.slots, tasks.length);
14043
14444
  const routes = this.dispatchEnabled ? await Promise.all(
14044
14445
  tasks.slice(0, slotCount).map(
@@ -14070,7 +14471,7 @@ ${recentJournal}` : "No prior iterations.",
14070
14471
  const routeInfo = [];
14071
14472
  const spawnPromises = [];
14072
14473
  for (let i = 0; i < slotCount; i++) {
14073
- const task = expectDefined12(tasks[i]);
14474
+ const task = expectDefined(tasks[i]);
14074
14475
  const route = routes[i] ?? null;
14075
14476
  const subagentId = `parallel-${this.iterations}-${i}`;
14076
14477
  const taskId = randomUUID();
@@ -14429,7 +14830,9 @@ var CollabSession = class extends EventEmitter {
14429
14830
  settled = false;
14430
14831
  timeoutMs;
14431
14832
  cancelled = false;
14833
+ _raceResolved = false;
14432
14834
  alerts = [];
14835
+ snapshotWarnings = [];
14433
14836
  /** Tracks tool call counts per subagent for progress-based timeout decisions. */
14434
14837
  progressBySubagent = /* @__PURE__ */ new Map();
14435
14838
  /** Last tool call count when a timeout warning was handled. */
@@ -14499,10 +14902,19 @@ var CollabSession = class extends EventEmitter {
14499
14902
  }
14500
14903
  for (const filePath of allFiles) {
14501
14904
  try {
14502
- const content = await fsp3.readFile(filePath, "utf8");
14905
+ const [content, stat11] = await Promise.all([
14906
+ fsp3.readFile(filePath, "utf8"),
14907
+ fsp3.stat(filePath)
14908
+ ]);
14503
14909
  const ext = filePath.split(".").pop() ?? "";
14504
14910
  const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
14505
- this.snapshot.files.push({ path: filePath, content, language });
14911
+ this.snapshot.files.push({
14912
+ path: filePath,
14913
+ content,
14914
+ language,
14915
+ snapshotMtimeMs: stat11.mtimeMs,
14916
+ snapshotSizeBytes: stat11.size
14917
+ });
14506
14918
  } catch {
14507
14919
  this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
14508
14920
  }
@@ -14515,7 +14927,7 @@ var CollabSession = class extends EventEmitter {
14515
14927
  * Safe to call multiple times (idempotent after first call).
14516
14928
  */
14517
14929
  cancel(reason = "Director cancelled collab session") {
14518
- if (this.settled) return;
14930
+ if (this.cancelled) return;
14519
14931
  this.cancelled = true;
14520
14932
  if (this._timeoutTimer) {
14521
14933
  clearTimeout(this._timeoutTimer);
@@ -14525,7 +14937,11 @@ var CollabSession = class extends EventEmitter {
14525
14937
  subagentId: this.director.id,
14526
14938
  ts: Date.now(),
14527
14939
  type: "director.cancel_collab",
14528
- payload: { sessionId: this.sessionId, reason, cancelledAt: (/* @__PURE__ */ new Date()).toISOString() }
14940
+ payload: {
14941
+ sessionId: this.sessionId,
14942
+ reason,
14943
+ cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
14944
+ }
14529
14945
  });
14530
14946
  this.fleetBus.emit({
14531
14947
  subagentId: this.director.id,
@@ -14549,6 +14965,7 @@ var CollabSession = class extends EventEmitter {
14549
14965
  this.subagentIds.set("critic", criticId);
14550
14966
  const timeout = new Promise((_, reject) => {
14551
14967
  this._timeoutTimer = setTimeout(() => {
14968
+ if (this._raceResolved) return;
14552
14969
  this.cancel("Session-level timeout reached");
14553
14970
  reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
14554
14971
  }, this.timeoutMs);
@@ -14564,6 +14981,7 @@ var CollabSession = class extends EventEmitter {
14564
14981
  timeout
14565
14982
  ]);
14566
14983
  } catch (err) {
14984
+ this._raceResolved = true;
14567
14985
  if (this._timeoutTimer) {
14568
14986
  clearTimeout(this._timeoutTimer);
14569
14987
  this._timeoutTimer = void 0;
@@ -14576,7 +14994,9 @@ var CollabSession = class extends EventEmitter {
14576
14994
  for (const result of results?.flat() ?? []) {
14577
14995
  await this.parseAndEmit(result);
14578
14996
  }
14997
+ this.snapshotWarnings = await this.checkSnapshotFreshness();
14579
14998
  const report = this.assembleReport();
14999
+ this._raceResolved = true;
14580
15000
  this.cleanup();
14581
15001
  this.emit("session.done", report);
14582
15002
  return report;
@@ -14639,7 +15059,7 @@ var CollabSession = class extends EventEmitter {
14639
15059
  const defaults = {
14640
15060
  "bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
14641
15061
  "refactor-planner": { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 },
14642
- "critic": { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
15062
+ critic: { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
14643
15063
  };
14644
15064
  return defaults[role] ?? { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 };
14645
15065
  }
@@ -14719,7 +15139,10 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
14719
15139
  }
14720
15140
  wireFleetBus() {
14721
15141
  const dTool = this.fleetBus.filter("tool.executed", (e) => {
14722
- this.progressBySubagent.set(e.subagentId, (this.progressBySubagent.get(e.subagentId) ?? 0) + 1);
15142
+ this.progressBySubagent.set(
15143
+ e.subagentId,
15144
+ (this.progressBySubagent.get(e.subagentId) ?? 0) + 1
15145
+ );
14723
15146
  });
14724
15147
  this.disposers.push(dTool);
14725
15148
  const dBudget = this.fleetBus.filter("budget.threshold_reached", (e) => {
@@ -14758,7 +15181,10 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
14758
15181
  return;
14759
15182
  }
14760
15183
  this.lastTimeoutProgress.set(e.subagentId, progress);
14761
- const newLimit = Math.min(Math.ceil((payload.timeoutMs ?? payload.limit) * 2), 24 * 60 * 6e4);
15184
+ const newLimit = Math.min(
15185
+ Math.ceil((payload.timeoutMs ?? payload.limit) * 2),
15186
+ 24 * 60 * 6e4
15187
+ );
14762
15188
  setImmediate(() => {
14763
15189
  payload.extend({ timeoutMs: newLimit });
14764
15190
  });
@@ -14868,15 +15294,18 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
14868
15294
  needs_revision: 1,
14869
15295
  reject: 2
14870
15296
  };
14871
- const overallVerdict = evalList.reduce(
14872
- (worst, eval_) => {
14873
- const w = verdictOrder[worst];
14874
- const c = verdictOrder[eval_.verdict];
14875
- return c > w ? eval_.verdict : worst;
14876
- },
14877
- "approve"
15297
+ const overallVerdict = evalList.reduce((worst, eval_) => {
15298
+ const w = verdictOrder[worst];
15299
+ const c = verdictOrder[eval_.verdict];
15300
+ return c > w ? eval_.verdict : worst;
15301
+ }, "approve");
15302
+ const summary = this.buildMarkdownSummary(
15303
+ bugList,
15304
+ planList,
15305
+ evalList,
15306
+ overallVerdict,
15307
+ disposition
14878
15308
  );
14879
- const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict, disposition);
14880
15309
  return {
14881
15310
  sessionId: this.sessionId,
14882
15311
  startedAt: this.snapshot.createdAt,
@@ -14887,10 +15316,28 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
14887
15316
  refactorPlans: planList,
14888
15317
  evaluations: evalList,
14889
15318
  alerts: [...this.alerts],
15319
+ ...this.snapshotWarnings.length > 0 ? { snapshotWarnings: this.snapshotWarnings } : {},
14890
15320
  overallVerdict,
14891
15321
  summary
14892
15322
  };
14893
15323
  }
15324
+ async checkSnapshotFreshness() {
15325
+ const warnings = [];
15326
+ for (const file of this.snapshot.files) {
15327
+ if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
15328
+ try {
15329
+ const stat11 = await fsp3.stat(file.path);
15330
+ const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat11.mtimeMs > file.snapshotMtimeMs + 1;
15331
+ const sizeChanged = file.snapshotSizeBytes !== void 0 && stat11.size !== file.snapshotSizeBytes;
15332
+ if (mtimeChanged || sizeChanged) {
15333
+ warnings.push(`${file.path} changed after the collab snapshot was captured.`);
15334
+ }
15335
+ } catch {
15336
+ warnings.push(`${file.path} could not be checked after the collab snapshot was captured.`);
15337
+ }
15338
+ }
15339
+ return warnings;
15340
+ }
14894
15341
  buildMarkdownSummary(bugs, plans, evals, overallVerdict, disposition) {
14895
15342
  const lines = [
14896
15343
  `## Collaborative Debugging Report \u2014 ${this.sessionId}`,
@@ -14907,10 +15354,19 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
14907
15354
  }
14908
15355
  lines.push("");
14909
15356
  }
15357
+ if (this.snapshotWarnings.length > 0) {
15358
+ lines.push("### Snapshot Warnings", "");
15359
+ for (const warning of this.snapshotWarnings) {
15360
+ lines.push(`- ${warning}`);
15361
+ }
15362
+ lines.push("");
15363
+ }
14910
15364
  if (bugs.length > 0) {
14911
15365
  lines.push("### Bugs Found", "");
14912
15366
  for (const b of bugs) {
14913
- lines.push(`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`);
15367
+ lines.push(
15368
+ `- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`
15369
+ );
14914
15370
  }
14915
15371
  lines.push("");
14916
15372
  }
@@ -15513,6 +15969,7 @@ var FleetBus = class {
15513
15969
  set.add(handler);
15514
15970
  return () => {
15515
15971
  set.delete(handler);
15972
+ if (set.size === 0) this.byId.delete(subagentId);
15516
15973
  };
15517
15974
  }
15518
15975
  /** Subscribe to one event type across all subagents. */
@@ -15525,6 +15982,7 @@ var FleetBus = class {
15525
15982
  set.add(handler);
15526
15983
  return () => {
15527
15984
  set.delete(handler);
15985
+ if (set.size === 0) this.byType.delete(type);
15528
15986
  };
15529
15987
  }
15530
15988
  /** Subscribe to literally everything. The fleet roll-up uses this. */
@@ -15558,6 +16016,11 @@ var FleetBus = class {
15558
16016
  }
15559
16017
  }
15560
16018
  }
16019
+ clear() {
16020
+ this.byId.clear();
16021
+ this.byType.clear();
16022
+ this.any.clear();
16023
+ }
15561
16024
  };
15562
16025
  var FleetUsageAggregator = class {
15563
16026
  constructor(bus, priceLookup, metaLookup) {
@@ -16802,7 +17265,9 @@ var Director = class _Director {
16802
17265
  let toolUses = 0;
16803
17266
  for (const line of targetLines) {
16804
17267
  try {
16805
- const ev = JSON.parse(line);
17268
+ const parsed = safeParse(line);
17269
+ if (!parsed.ok || !parsed.value) continue;
17270
+ const ev = parsed.value;
16806
17271
  if (ev.type === "assistant" && typeof ev.text === "string") {
16807
17272
  lastAssistantText = ev.text;
16808
17273
  } else if (ev.type === "stop" && ev.stopReason) {
@@ -17116,7 +17581,7 @@ function createDelegateTool(opts) {
17116
17581
  const offIter = dir.fleet.filter("iteration.started", bump);
17117
17582
  const offProgress = dir.fleet.filter("tool.progress", bump);
17118
17583
  arm();
17119
- dir.awaitTasks([taskId]).then((r) => finish(r[0] ?? { __timeout: true })).catch(() => finish({ __timeout: true }));
17584
+ dir.awaitTasks([taskId]).then((r) => finish(r[0] ?? { __emptyResult: true })).catch(() => finish({ __timeout: true }));
17120
17585
  });
17121
17586
  if ("__timeout" in result) {
17122
17587
  const partial2 = await readSubagentPartial(opts, subagentId);
@@ -17141,6 +17606,29 @@ function createDelegateTool(opts) {
17141
17606
  partial: partial2
17142
17607
  };
17143
17608
  }
17609
+ if ("__emptyResult" in result) {
17610
+ const partial2 = await readSubagentPartial(opts, subagentId);
17611
+ opts.events?.emit("delegate.completed", {
17612
+ target,
17613
+ task: i.task,
17614
+ ok: false,
17615
+ status: "empty_result",
17616
+ summary: `[${target}] completed without a task result`,
17617
+ durationMs: 0,
17618
+ iterations: partial2?.events ?? 0,
17619
+ toolCalls: partial2?.toolUsesObserved ?? 0,
17620
+ subagentId
17621
+ });
17622
+ return {
17623
+ ok: false,
17624
+ stopReason: "error",
17625
+ error: "Director returned no task result for the delegated task.",
17626
+ hint: "Check fleet state with /fleet status, then retry or reassign the task.",
17627
+ subagentId,
17628
+ taskId,
17629
+ partial: partial2
17630
+ };
17631
+ }
17144
17632
  const baseStopReason = result.status === "success" ? "end_turn" : result.status === "timeout" ? "subagent_timeout" : result.status === "stopped" ? "aborted" : "budget_exhausted";
17145
17633
  const partial = result.status === "success" ? void 0 : await readSubagentPartial(opts, subagentId);
17146
17634
  const errorKind = result.error?.kind;
@@ -17313,7 +17801,9 @@ async function readSubagentPartial(opts, subagentId) {
17313
17801
  let toolUses = 0;
17314
17802
  for (const line of lines) {
17315
17803
  try {
17316
- const ev = JSON.parse(line);
17804
+ const parsed = safeParse(line);
17805
+ if (!parsed.ok || !parsed.value) continue;
17806
+ const ev = parsed.value;
17317
17807
  if (ev.type === "tool_use") toolUses += 1;
17318
17808
  if (ev.type === "llm_response") {
17319
17809
  if (typeof ev.stopReason === "string") lastStopReason = ev.stopReason;
@@ -17501,8 +17991,8 @@ async function loadProjectModes(modesDir) {
17501
17991
  for (const entry of entries) {
17502
17992
  if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
17503
17993
  const filePath = path6.join(modesDir, entry);
17504
- const stat10 = await fsp3.stat(filePath);
17505
- if (!stat10.isFile()) continue;
17994
+ const stat11 = await fsp3.stat(filePath);
17995
+ if (!stat11.isFile()) continue;
17506
17996
  const content = await fsp3.readFile(filePath, "utf8");
17507
17997
  const id = path6.basename(entry, path6.extname(entry));
17508
17998
  modes.push({
@@ -18572,12 +19062,6 @@ var TaskGraphStore = class {
18572
19062
  };
18573
19063
 
18574
19064
  // src/sdd/spec-builder.ts
18575
- function expectDefined13(value) {
18576
- if (value === null || value === void 0) {
18577
- throw new Error("Expected value to be defined");
18578
- }
18579
- return value;
18580
- }
18581
19065
  function buildQuestioningPrompt(session, min, max) {
18582
19066
  const answered = session.answers.length;
18583
19067
  const remaining = Math.max(0, min - answered);
@@ -18623,7 +19107,7 @@ function buildQuestioningPrompt(session, min, max) {
18623
19107
  if (answered > 0) {
18624
19108
  lines.push("", "**Conversation so far:**");
18625
19109
  for (let i = 0; i < answered; i++) {
18626
- const a = expectDefined13(session.answers[i]);
19110
+ const a = expectDefined(session.answers[i]);
18627
19111
  lines.push(``, `Q${i + 1}: ${a.question}`, `A${i + 1}: ${a.answer}`);
18628
19112
  }
18629
19113
  }
@@ -19365,12 +19849,6 @@ function truncate2(str, maxLen) {
19365
19849
  }
19366
19850
 
19367
19851
  // src/sdd/critical-path.ts
19368
- function expectDefined14(value) {
19369
- if (value === null || value === void 0) {
19370
- throw new Error("Expected value to be defined");
19371
- }
19372
- return value;
19373
- }
19374
19852
  function analyzeCriticalPath(graph) {
19375
19853
  const nodes = Array.from(graph.nodes.values());
19376
19854
  const topoOrder = topologicalSort(graph);
@@ -19446,7 +19924,7 @@ function getTransitiveBlocked(_graph, taskId, blocksMap) {
19446
19924
  const visited = /* @__PURE__ */ new Set();
19447
19925
  const queue = [taskId];
19448
19926
  while (queue.length > 0) {
19449
- const current = expectDefined14(queue.shift());
19927
+ const current = expectDefined(queue.shift());
19450
19928
  const blocked = blocksMap.get(current);
19451
19929
  if (!blocked) continue;
19452
19930
  for (const id of blocked) {
@@ -19492,7 +19970,7 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
19492
19970
  if (!changed) break;
19493
19971
  }
19494
19972
  let maxDist = 0;
19495
- let maxId = expectDefined14(allIds[0]);
19973
+ let maxId = expectDefined(allIds[0]);
19496
19974
  for (const id of allIds) {
19497
19975
  const d = dist.get(id) ?? 0;
19498
19976
  if (d > maxDist) {
@@ -19961,12 +20439,6 @@ var SddTaskDecomposer = class {
19961
20439
  return nodes.some((n) => n.status === "blocked");
19962
20440
  }
19963
20441
  };
19964
- function expectDefined15(value) {
19965
- if (value === null || value === void 0) {
19966
- throw new Error("Expected value to be defined");
19967
- }
19968
- return value;
19969
- }
19970
20442
  var SddParallelRun = class {
19971
20443
  constructor(opts) {
19972
20444
  this.opts = opts;
@@ -20115,8 +20587,8 @@ var SddParallelRun = class {
20115
20587
  const successCount = results.filter((r) => r.status === "success").length;
20116
20588
  const failCount = results.length - successCount;
20117
20589
  for (let i = 0; i < results.length; i++) {
20118
- const result = expectDefined15(results[i]);
20119
- const taskId = expectDefined15(taskIds[i]);
20590
+ const result = expectDefined(results[i]);
20591
+ const taskId = expectDefined(taskIds[i]);
20120
20592
  if (result.status === "success") {
20121
20593
  this.opts.tracker.updateNodeStatus(taskId, "completed");
20122
20594
  } else {
@@ -21223,12 +21695,6 @@ var allServers = () => ({
21223
21695
  "zai-vision": { ...zaiVisionServer(), enabled: false },
21224
21696
  "minimax-vision": { ...miniMaxVisionServer(), enabled: false }
21225
21697
  });
21226
- function expectDefined16(value) {
21227
- if (value === null || value === void 0) {
21228
- throw new Error("Expected value to be defined");
21229
- }
21230
- return value;
21231
- }
21232
21698
  function parseSkillRef(input) {
21233
21699
  const trimmed = input.trim().replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
21234
21700
  const atIdx = trimmed.indexOf("@");
@@ -21245,7 +21711,7 @@ function parseSkillRef(input) {
21245
21711
  if (parts.length < 2) {
21246
21712
  throw new Error(`Invalid skill reference "${input}". Expected format: user/repo or user/repo@ref`);
21247
21713
  }
21248
- return { owner: expectDefined16(parts[0]), repo: expectDefined16(parts[1]), ref };
21714
+ return { owner: expectDefined(parts[0]), repo: expectDefined(parts[1]), ref };
21249
21715
  }
21250
21716
  var MAX_TARBALL_SIZE = 50 * 1024 * 1024;
21251
21717
  async function downloadGitHubTarball(parsed) {
@@ -21414,12 +21880,6 @@ var SkillManifestStore = class {
21414
21880
  };
21415
21881
 
21416
21882
  // src/skills/skill-installer.ts
21417
- function expectDefined17(value) {
21418
- if (value === null || value === void 0) {
21419
- throw new Error("Expected value to be defined");
21420
- }
21421
- return value;
21422
- }
21423
21883
  var MAX_SKILL_FILE_SIZE = 100 * 1024;
21424
21884
  var SkillInstaller = class {
21425
21885
  opts;
@@ -21464,10 +21924,10 @@ var SkillInstaller = class {
21464
21924
  if (!resolved.startsWith(path6.resolve(destDir))) {
21465
21925
  throw new Error(`Path traversal detected in skill file: ${file}`);
21466
21926
  }
21467
- const stat10 = await fsp3.stat(srcPath);
21468
- if (stat10.size > MAX_SKILL_FILE_SIZE) {
21927
+ const stat11 = await fsp3.stat(srcPath);
21928
+ if (stat11.size > MAX_SKILL_FILE_SIZE) {
21469
21929
  throw new Error(
21470
- `Skill file "${file}" is too large (${(stat10.size / 1024).toFixed(1)}KB). Max: ${MAX_SKILL_FILE_SIZE / 1024}KB`
21930
+ `Skill file "${file}" is too large (${(stat11.size / 1024).toFixed(1)}KB). Max: ${MAX_SKILL_FILE_SIZE / 1024}KB`
21471
21931
  );
21472
21932
  }
21473
21933
  await fsp3.mkdir(path6.dirname(destPath), { recursive: true });
@@ -21544,7 +22004,7 @@ var SkillInstaller = class {
21544
22004
  bySource.get(key)?.push(entry);
21545
22005
  }
21546
22006
  for (const [, entries] of bySource) {
21547
- const first = expectDefined17(entries[0]);
22007
+ const first = expectDefined(entries[0]);
21548
22008
  const scope = first.scope;
21549
22009
  const isGlobal = scope === "user";
21550
22010
  try {
@@ -21711,15 +22171,387 @@ async function collectFiles(dir, baseDir2) {
21711
22171
  }
21712
22172
  return results;
21713
22173
  }
22174
+ var GraphMemoryBackend = class {
22175
+ kind = "graph";
22176
+ file;
22177
+ graphFile;
22178
+ // In-memory graph state — lazily loaded, saved on mutation.
22179
+ nodes = /* @__PURE__ */ new Map();
22180
+ edges = [];
22181
+ loadedScope = null;
22182
+ loaded = false;
22183
+ constructor(opts) {
22184
+ this.file = new FileMemoryBackend({ paths: opts.paths });
22185
+ this.graphFile = opts.graphPath ?? `${opts.paths.projectDir}/memory-graph.json`;
22186
+ }
22187
+ // ── Backend interface ──────────────────────────────────────────────
22188
+ async remember(scope, entry, filePath) {
22189
+ await this.file.remember(scope, entry, filePath);
22190
+ await this.loadGraph(scope);
22191
+ const nodeId = this.nodeId(entry);
22192
+ const existing = this.nodes.get(nodeId);
22193
+ if (existing) {
22194
+ existing.count++;
22195
+ existing.entry = entry;
22196
+ existing.type = entry.type;
22197
+ existing.tags = entry.tags;
22198
+ existing.priority = entry.priority;
22199
+ } else {
22200
+ this.nodes.set(nodeId, {
22201
+ id: nodeId,
22202
+ entry,
22203
+ firstSeen: entry.ts,
22204
+ count: 1,
22205
+ type: entry.type,
22206
+ tags: entry.tags,
22207
+ priority: entry.priority
22208
+ });
22209
+ for (const [, other] of this.nodes) {
22210
+ if (other.id === nodeId) continue;
22211
+ const sim = wordOverlap(entry.text, other.entry.text);
22212
+ const tagSim = sharedTags(entry.tags ?? [], other.tags ?? []);
22213
+ const weight = Math.max(sim, tagSim * 0.5);
22214
+ if (weight > 0.15) {
22215
+ this.edges.push({
22216
+ from: nodeId,
22217
+ to: other.id,
22218
+ relation: sim >= tagSim ? "similar" : "same_turn",
22219
+ weight,
22220
+ ts: entry.ts
22221
+ });
22222
+ }
22223
+ }
22224
+ }
22225
+ await this.saveGraph(scope);
22226
+ }
22227
+ async forget(scope, query, filePath) {
22228
+ const removed = await this.file.forget(scope, query, filePath);
22229
+ if (removed > 0) {
22230
+ await this.loadGraph(scope);
22231
+ const n = query.toLowerCase();
22232
+ const toRemove = [];
22233
+ for (const [id, node] of this.nodes) {
22234
+ if (node.entry.text.toLowerCase().includes(n)) {
22235
+ toRemove.push(id);
22236
+ }
22237
+ }
22238
+ for (const id of toRemove) this.nodes.delete(id);
22239
+ this.edges = this.edges.filter((e) => !toRemove.includes(e.from) && !toRemove.includes(e.to));
22240
+ await this.saveGraph(scope);
22241
+ }
22242
+ return removed;
22243
+ }
22244
+ async readAll(scope, filePath) {
22245
+ return this.file.readAll(scope, filePath);
22246
+ }
22247
+ async list(scope, filePath, limit) {
22248
+ await this.loadGraph(scope);
22249
+ const fileEntries = await this.file.list(scope, filePath);
22250
+ const nodeMap = new Map(this.nodes.entries());
22251
+ const enriched = fileEntries.map((fe) => {
22252
+ const nodeId = this.nodeId(fe);
22253
+ const node = nodeMap.get(nodeId);
22254
+ if (node) {
22255
+ return {
22256
+ ...fe,
22257
+ type: node.type ?? fe.type,
22258
+ tags: node.tags ?? fe.tags,
22259
+ priority: node.priority ?? fe.priority
22260
+ };
22261
+ }
22262
+ return fe;
22263
+ });
22264
+ const fileIds = new Set(fileEntries.map((e) => this.nodeId(e)));
22265
+ for (const [id, node] of this.nodes) {
22266
+ if (!fileIds.has(id)) {
22267
+ enriched.push(node.entry);
22268
+ }
22269
+ }
22270
+ enriched.sort((a, b) => b.ts.localeCompare(a.ts));
22271
+ return limit ? enriched.slice(0, limit) : enriched;
22272
+ }
22273
+ async search(scope, query, filePath, limit) {
22274
+ await this.loadGraph(scope);
22275
+ const needle = query.toLowerCase().split(/\s+/);
22276
+ const all = await this.list(scope, filePath);
22277
+ const scored = all.map((entry) => {
22278
+ const words = entry.text.toLowerCase().split(/\s+/);
22279
+ let score = 0;
22280
+ for (const n of needle) {
22281
+ if (words.some((w) => w.includes(n))) score += 1;
22282
+ if (entry.tags?.some((t2) => t2.toLowerCase().includes(n))) score += 2;
22283
+ }
22284
+ const node = this.nodes.get(this.nodeId(entry));
22285
+ if (node) {
22286
+ if (node.priority === "critical") score += 3;
22287
+ else if (node.priority === "high") score += 2;
22288
+ score += node.count * 0.5;
22289
+ }
22290
+ return { entry, score };
22291
+ });
22292
+ scored.sort((a, b) => b.score - a.score);
22293
+ const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
22294
+ return limit ? matched.slice(0, limit) : matched;
22295
+ }
22296
+ async clear(scope, filePath) {
22297
+ await this.file.clear(scope, filePath);
22298
+ this.nodes.clear();
22299
+ this.edges = [];
22300
+ this.loadedScope = scope;
22301
+ this.loaded = true;
22302
+ try {
22303
+ await fsp3.unlink(this.graphFile);
22304
+ } catch {
22305
+ }
22306
+ }
22307
+ async consolidate(scope, filePath) {
22308
+ return this.file.consolidate(scope, filePath);
22309
+ }
22310
+ // ── Graph-specific queries ─────────────────────────────────────────
22311
+ /**
22312
+ * Find memories related to the given entry, ordered by edge weight.
22313
+ */
22314
+ async findRelated(scope, _filePath, entryText, limit = 5) {
22315
+ await this.loadGraph(scope);
22316
+ const targetId = this.nodeId({ scope, text: entryText, ts: "" });
22317
+ const related = this.edges.filter((e) => e.from === targetId || e.to === targetId).sort((a, b) => b.weight - a.weight).slice(0, limit);
22318
+ const result = [];
22319
+ for (const edge of related) {
22320
+ const otherId = edge.from === targetId ? edge.to : edge.from;
22321
+ const node = this.nodes.get(otherId);
22322
+ if (node) result.push(node.entry);
22323
+ }
22324
+ return result;
22325
+ }
22326
+ /**
22327
+ * Get all edges for visualization or traversal.
22328
+ */
22329
+ getGraph() {
22330
+ return {
22331
+ nodes: [...this.nodes.values()],
22332
+ edges: [...this.edges]
22333
+ };
22334
+ }
22335
+ // ── Persistence ────────────────────────────────────────────────────
22336
+ nodeId(entry) {
22337
+ const norm = entry.text.toLowerCase().trim().replace(/\s+/g, " ");
22338
+ return `${entry.scope ?? "mem"}::${simpleHash(norm)}`;
22339
+ }
22340
+ async loadGraph(scope) {
22341
+ if (this.loaded && this.loadedScope === scope) return;
22342
+ try {
22343
+ const raw = await fsp3.readFile(this.graphFile, "utf8");
22344
+ const data = JSON.parse(raw);
22345
+ this.nodes = new Map(data.nodes);
22346
+ this.edges = data.edges;
22347
+ } catch {
22348
+ this.nodes = /* @__PURE__ */ new Map();
22349
+ this.edges = [];
22350
+ }
22351
+ this.loadedScope = scope;
22352
+ this.loaded = true;
22353
+ }
22354
+ async saveGraph(scope) {
22355
+ this.loadedScope = scope;
22356
+ this.loaded = true;
22357
+ try {
22358
+ const data = {
22359
+ nodes: [...this.nodes.entries()],
22360
+ edges: this.edges
22361
+ };
22362
+ await fsp3.mkdir(
22363
+ this.graphFile.substring(0, this.graphFile.lastIndexOf("/")),
22364
+ { recursive: true }
22365
+ );
22366
+ const tmp = `${this.graphFile}.tmp`;
22367
+ await fsp3.writeFile(tmp, JSON.stringify(data));
22368
+ await fsp3.rename(tmp, this.graphFile);
22369
+ } catch {
22370
+ }
22371
+ }
22372
+ };
22373
+ function wordOverlap(a, b) {
22374
+ const wordsA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
22375
+ const wordsB = new Set(b.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
22376
+ if (wordsA.size === 0 || wordsB.size === 0) return 0;
22377
+ let intersection = 0;
22378
+ for (const w of wordsA) {
22379
+ if (wordsB.has(w)) intersection++;
22380
+ }
22381
+ return intersection / Math.max(wordsA.size, wordsB.size);
22382
+ }
22383
+ function sharedTags(a, b) {
22384
+ if (a.length === 0 || b.length === 0) return 0;
22385
+ const setB = new Set(b);
22386
+ let shared = 0;
22387
+ for (const t2 of a) if (setB.has(t2)) shared++;
22388
+ return shared / Math.max(a.length, b.length);
22389
+ }
22390
+ function simpleHash(s) {
22391
+ let h = 0;
22392
+ for (let i = 0; i < s.length; i++) {
22393
+ h = (h << 5) - h + s.charCodeAt(i) | 0;
22394
+ }
22395
+ return Math.abs(h).toString(36);
22396
+ }
22397
+
22398
+ // src/storage/memory-consolidator.ts
22399
+ function buildConsolidationPrompt(finalText, iterations, existingEntries) {
22400
+ const existingBlock = existingEntries.length > 0 ? `
22401
+
22402
+ Existing memory entries:
22403
+ ${existingEntries.map((e) => `- [${e.ts.slice(0, 10)}] ${e.text}`).join("\n")}` : "";
22404
+ return `You are a memory consolidator. Review the following session summary and decide what key facts, conventions, decisions, or learnings should be persisted to long-term memory.
22405
+
22406
+ Session summary (${iterations} iterations):
22407
+ ${finalText.slice(0, 3e3)}${existingBlock}
22408
+
22409
+ Return a JSON object with an "operations" array. Each operation must have an "action" field:
22410
+ - "add": create a new memory entry. Include "text", and optionally "type", "tags", "priority".
22411
+ - "edit": replace an existing entry. Include "query" (to match) and "text" (replacement).
22412
+ - "delete": remove an entry. Include "query" (to match).
22413
+
22414
+ Memory types:
22415
+ - "fact": Objective truth about the project (e.g. "uses pnpm workspaces")
22416
+ - "decision": A choice that was made (e.g. "decided to use biome over eslint")
22417
+ - "convention": A recurring pattern or standard (e.g. "commit messages use conventional format")
22418
+ - "preference": User or team preference (e.g. "prefers short variable names")
22419
+ - "reference": Pointer to a file or location (e.g. "auth logic in packages/core/src/auth/")
22420
+ - "anti_pattern": Something to avoid (e.g. "never use any in TypeScript")
22421
+
22422
+ Priority levels:
22423
+ - "critical": Must always be known (e.g. security constraints)
22424
+ - "high": Important for most tasks
22425
+ - "medium": Useful context
22426
+ - "low": Nice to know
22427
+
22428
+ Rules:
22429
+ - Only persist facts likely useful across multiple future sessions.
22430
+ - Do NOT persist task progress, temporary state, or one-off observations.
22431
+ - Prefer "add" over "edit" unless the existing entry is clearly outdated.
22432
+ - Assign a type and priority to every "add" operation.
22433
+ - Use 1-3 hashtag tags for each entry (e.g. #typescript #build).
22434
+ - Be concise \u2014 each memory entry should be one clear sentence.
22435
+
22436
+ Return ONLY valid JSON, no markdown, no explanation:
22437
+ {
22438
+ "operations": [
22439
+ {
22440
+ "action": "add",
22441
+ "text": "Project uses pnpm workspaces with TypeScript strict mode",
22442
+ "type": "convention",
22443
+ "priority": "high",
22444
+ "tags": ["pnpm", "typescript", "build"]
22445
+ },
22446
+ {
22447
+ "action": "edit",
22448
+ "query": "pnpm",
22449
+ "text": "Project uses pnpm v9+ with ESM-only modules",
22450
+ "type": "fact",
22451
+ "priority": "medium"
22452
+ },
22453
+ { "action": "delete", "query": "outdated convention" }
22454
+ ]
22455
+ }`;
22456
+ }
22457
+ var SessionMemoryConsolidator = class {
22458
+ name = "session-memory-consolidator";
22459
+ owner = "core";
22460
+ memoryStore;
22461
+ provider;
22462
+ model;
22463
+ minIterations;
22464
+ maxExistingEntries;
22465
+ constructor(opts) {
22466
+ this.memoryStore = opts.memoryStore;
22467
+ this.provider = opts.provider;
22468
+ this.model = opts.model;
22469
+ this.minIterations = opts.minIterations ?? 2;
22470
+ this.maxExistingEntries = opts.maxExistingEntries ?? 15;
22471
+ }
22472
+ afterRun = async (ctx, result) => {
22473
+ if (result.status !== "done") return;
22474
+ if (!result.finalText || result.finalText.trim().length < 20) return;
22475
+ if (result.iterations < this.minIterations) return;
22476
+ const provider = this.provider ?? ctx.provider;
22477
+ if (!provider || !provider.complete) return;
22478
+ try {
22479
+ const existingEntries = await this.memoryStore.list("project-memory", this.maxExistingEntries);
22480
+ const prompt = buildConsolidationPrompt(
22481
+ result.finalText,
22482
+ result.iterations,
22483
+ existingEntries
22484
+ );
22485
+ const signal = AbortSignal.timeout(15e3);
22486
+ const response = await provider.complete(
22487
+ {
22488
+ model: this.model ?? ctx.model,
22489
+ system: [{ type: "text", text: prompt }],
22490
+ messages: [
22491
+ { role: "user", content: "Review the session and return memory operations as JSON." }
22492
+ ],
22493
+ maxTokens: 500
22494
+ },
22495
+ { signal }
22496
+ );
22497
+ const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
22498
+ if (!text) return;
22499
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
22500
+ if (!jsonMatch) return;
22501
+ const parsed = JSON.parse(jsonMatch[0]);
22502
+ if (!Array.isArray(parsed.operations) || parsed.operations.length === 0) return;
22503
+ let added = 0;
22504
+ let edited = 0;
22505
+ let deleted = 0;
22506
+ for (const op of parsed.operations) {
22507
+ switch (op.action) {
22508
+ case "add": {
22509
+ if (op.text && op.text.trim()) {
22510
+ await this.memoryStore.remember(op.text.trim(), void 0, {
22511
+ type: op.type,
22512
+ tags: op.tags,
22513
+ priority: op.priority
22514
+ });
22515
+ added++;
22516
+ }
22517
+ break;
22518
+ }
22519
+ case "edit": {
22520
+ if (op.query && op.text && op.text.trim()) {
22521
+ await this.memoryStore.forget(op.query);
22522
+ await this.memoryStore.remember(op.text.trim(), void 0, {
22523
+ type: op.type,
22524
+ tags: op.tags,
22525
+ priority: op.priority
22526
+ });
22527
+ edited++;
22528
+ }
22529
+ break;
22530
+ }
22531
+ case "delete": {
22532
+ if (op.query) {
22533
+ const n = await this.memoryStore.forget(op.query);
22534
+ deleted += n;
22535
+ }
22536
+ break;
22537
+ }
22538
+ }
22539
+ }
22540
+ if (added > 0 || edited > 0 || deleted > 0) {
22541
+ const parts = [];
22542
+ if (added) parts.push(`${added} added`);
22543
+ if (edited) parts.push(`${edited} edited`);
22544
+ if (deleted) parts.push(`${deleted} deleted`);
22545
+ process.stderr.write(`[memory] Session consolidation: ${parts.join(", ")}
22546
+ `);
22547
+ }
22548
+ } catch {
22549
+ }
22550
+ };
22551
+ };
21714
22552
 
21715
22553
  // src/storage/annotations-store.ts
21716
22554
  init_atomic_write();
21717
- function expectDefined18(value) {
21718
- if (value === null || value === void 0) {
21719
- throw new Error("Expected value to be defined");
21720
- }
21721
- return value;
21722
- }
21723
22555
  var FILE_VERSION = 1;
21724
22556
  var MAX_TEXT_LENGTH = 2e3;
21725
22557
  var MAX_ANNOTATIONS = 1e3;
@@ -21760,9 +22592,7 @@ var AnnotationsStore = class {
21760
22592
  throw new Error("Annotation text must be non-empty");
21761
22593
  }
21762
22594
  if (text.length > MAX_TEXT_LENGTH) {
21763
- throw new Error(
21764
- `Annotation text exceeds ${MAX_TEXT_LENGTH} chars (got ${text.length})`
21765
- );
22595
+ throw new Error(`Annotation text exceeds ${MAX_TEXT_LENGTH} chars (got ${text.length})`);
21766
22596
  }
21767
22597
  if (!Number.isInteger(input.atEventIndex) || input.atEventIndex < 0) {
21768
22598
  throw new Error("atEventIndex must be a non-negative integer");
@@ -21778,20 +22608,22 @@ var AnnotationsStore = class {
21778
22608
  resolved: false
21779
22609
  };
21780
22610
  await this.enqueue(input.sessionId, async () => {
21781
- const all = await this.list(input.sessionId);
21782
- all.push(annotation);
21783
- if (all.length > MAX_ANNOTATIONS) {
21784
- const sorted = all.map((a, i) => ({ a, i })).sort((x, y) => {
21785
- if (x.a.resolved !== y.a.resolved) return x.a.resolved ? 1 : -1;
21786
- return x.a.createdAt.localeCompare(y.a.createdAt);
21787
- });
21788
- const evictCount = all.length - MAX_ANNOTATIONS;
21789
- const toEvict = new Set(sorted.slice(0, evictCount).map((s) => s.a.id));
21790
- const kept = all.filter((a) => !toEvict.has(a.id));
21791
- await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: kept });
21792
- } else {
21793
- await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
21794
- }
22611
+ await withFileLock(this.filePath(input.sessionId), async () => {
22612
+ const all = await this.list(input.sessionId);
22613
+ all.push(annotation);
22614
+ if (all.length > MAX_ANNOTATIONS) {
22615
+ const sorted = all.map((a, i) => ({ a, i })).sort((x, y) => {
22616
+ if (x.a.resolved !== y.a.resolved) return x.a.resolved ? 1 : -1;
22617
+ return x.a.createdAt.localeCompare(y.a.createdAt);
22618
+ });
22619
+ const evictCount = all.length - MAX_ANNOTATIONS;
22620
+ const toEvict = new Set(sorted.slice(0, evictCount).map((s) => s.a.id));
22621
+ const kept = all.filter((a) => !toEvict.has(a.id));
22622
+ await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: kept });
22623
+ } else {
22624
+ await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
22625
+ }
22626
+ });
21795
22627
  });
21796
22628
  return annotation;
21797
22629
  }
@@ -21804,21 +22636,23 @@ var AnnotationsStore = class {
21804
22636
  async resolve(input) {
21805
22637
  let updated = null;
21806
22638
  await this.enqueue(input.sessionId, async () => {
21807
- const all = await this.list(input.sessionId);
21808
- const idx = all.findIndex((a) => a.id === input.annotationId);
21809
- if (idx === -1) {
21810
- updated = null;
21811
- return;
21812
- }
21813
- const next = {
21814
- ...expectDefined18(all[idx]),
21815
- resolved: true,
21816
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
21817
- resolvedBy: input.resolvedBy
21818
- };
21819
- all[idx] = next;
21820
- await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
21821
- updated = next;
22639
+ await withFileLock(this.filePath(input.sessionId), async () => {
22640
+ const all = await this.list(input.sessionId);
22641
+ const idx = all.findIndex((a) => a.id === input.annotationId);
22642
+ if (idx === -1) {
22643
+ updated = null;
22644
+ return;
22645
+ }
22646
+ const next = {
22647
+ ...expectDefined(all[idx]),
22648
+ resolved: true,
22649
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
22650
+ resolvedBy: input.resolvedBy
22651
+ };
22652
+ all[idx] = next;
22653
+ await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
22654
+ updated = next;
22655
+ });
21822
22656
  });
21823
22657
  return updated;
21824
22658
  }
@@ -21919,22 +22753,22 @@ var ReplayLogStore = class {
21919
22753
  async record(input) {
21920
22754
  const hash = hashRequest(input.request);
21921
22755
  await this.enqueue(input.sessionId, async () => {
21922
- const cache = await this.ensureCache(input.sessionId);
21923
- if (cache.has(hash)) return;
21924
- const entry = {
21925
- hash,
21926
- ts: (/* @__PURE__ */ new Date()).toISOString(),
21927
- request: input.request,
21928
- response: input.response
21929
- };
21930
- cache.set(hash, entry);
21931
- const count = (this.diskCount.get(input.sessionId) ?? 0) + 1;
21932
- if (count > this.maxEntries) {
21933
- await this.compact(input.sessionId, cache);
21934
- } else {
21935
- await fsp3.appendFile(this.filePath(input.sessionId), JSON.stringify(entry) + "\n", "utf8");
21936
- this.diskCount.set(input.sessionId, count);
21937
- }
22756
+ await withFileLock(this.filePath(input.sessionId), async () => {
22757
+ const entries = await this.readAll(input.sessionId);
22758
+ if (entries.some((entry2) => entry2.hash === hash)) return;
22759
+ const entry = {
22760
+ hash,
22761
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
22762
+ request: input.request,
22763
+ response: input.response
22764
+ };
22765
+ entries.push(entry);
22766
+ const keep = entries.slice(-this.maxEntries);
22767
+ const cache = /* @__PURE__ */ new Map();
22768
+ for (const e of keep) cache.set(e.hash, e);
22769
+ this.cache.set(input.sessionId, cache);
22770
+ await this.rewriteCache(input.sessionId, cache);
22771
+ });
21938
22772
  });
21939
22773
  return hash;
21940
22774
  }
@@ -21943,7 +22777,7 @@ var ReplayLogStore = class {
21943
22777
  * Called when entry count exceeds the cap. Rewrites the entire file
21944
22778
  * but only happens O(n / maxEntries) times per session.
21945
22779
  */
21946
- async compact(sessionId, cache) {
22780
+ async rewriteCache(sessionId, cache) {
21947
22781
  const all = [...cache.values()];
21948
22782
  const keep = all.slice(-this.maxEntries);
21949
22783
  await this.writeAll(sessionId, keep);
@@ -22007,11 +22841,12 @@ var ReplayLogStore = class {
22007
22841
  for (const line of raw.split("\n")) {
22008
22842
  if (!line.trim()) continue;
22009
22843
  try {
22010
- const parsed = JSON.parse(line);
22011
- if ("entry" in parsed && parsed.entry) {
22012
- out.push(parsed.entry);
22844
+ const parsed = safeParse(line);
22845
+ if (!parsed.ok || !parsed.value) continue;
22846
+ if ("entry" in parsed.value && parsed.value.entry) {
22847
+ out.push(parsed.value.entry);
22013
22848
  } else {
22014
- out.push(parsed);
22849
+ out.push(parsed.value);
22015
22850
  }
22016
22851
  } catch {
22017
22852
  }
@@ -22047,12 +22882,6 @@ var ReplayLogStore = class {
22047
22882
  return next;
22048
22883
  }
22049
22884
  };
22050
- function expectDefined19(value) {
22051
- if (value === null || value === void 0) {
22052
- throw new Error("Expected value to be defined");
22053
- }
22054
- return value;
22055
- }
22056
22885
  var SessionRecovery = class {
22057
22886
  constructor(dir) {
22058
22887
  this.dir = dir;
@@ -22071,15 +22900,15 @@ var SessionRecovery = class {
22071
22900
  async detectStale(sessionId) {
22072
22901
  const fp = this.filePath(sessionId);
22073
22902
  const TAIL_SIZE = 8192;
22074
- let stat10;
22903
+ let stat11;
22075
22904
  try {
22076
- stat10 = await fsp3.stat(fp);
22905
+ stat11 = await fsp3.stat(fp);
22077
22906
  } catch (err) {
22078
22907
  if (err.code === "ENOENT") return null;
22079
22908
  return null;
22080
22909
  }
22081
- if (stat10.size === 0) return null;
22082
- const position = Math.max(0, stat10.size - TAIL_SIZE);
22910
+ if (stat11.size === 0) return null;
22911
+ const position = Math.max(0, stat11.size - TAIL_SIZE);
22083
22912
  const buf = Buffer.alloc(TAIL_SIZE);
22084
22913
  let fh;
22085
22914
  try {
@@ -22093,7 +22922,7 @@ var SessionRecovery = class {
22093
22922
  const lines = raw.split("\n").filter((l) => l.trim());
22094
22923
  for (let i = lines.length - 1; i >= 0; i--) {
22095
22924
  try {
22096
- const ev = JSON.parse(expectDefined19(lines[i]));
22925
+ const ev = JSON.parse(expectDefined(lines[i]));
22097
22926
  if (ev.type === "in_flight_start") {
22098
22927
  return {
22099
22928
  sessionId,
@@ -22146,12 +22975,12 @@ var SessionRecovery = class {
22146
22975
  let lastCheckpointIdx = -1;
22147
22976
  for (let i = 0; i < events.length; i++) {
22148
22977
  if (events[i]?.type === "checkpoint") {
22149
- lastCheckpoint = expectDefined19(events[i]);
22978
+ lastCheckpoint = expectDefined(events[i]);
22150
22979
  lastCheckpointIdx = i;
22151
22980
  }
22152
22981
  }
22153
22982
  const pendingEvents = lastCheckpointIdx >= 0 ? events.slice(lastCheckpointIdx + 1) : events;
22154
- const lastEv = expectDefined19(events[events.length - 1]);
22983
+ const lastEv = expectDefined(events[events.length - 1]);
22155
22984
  const inFlightStart = lastEv.type === "in_flight_start" ? lastEv : null;
22156
22985
  const context = inFlightStart && inFlightStart.type === "in_flight_start" ? inFlightStart.context : null;
22157
22986
  return {
@@ -22194,12 +23023,9 @@ var SessionRecovery = class {
22194
23023
  return path6.join(this.dir, `${sessionId}.jsonl`);
22195
23024
  }
22196
23025
  };
22197
- function expectDefined20(value) {
22198
- if (value === null || value === void 0) {
22199
- throw new Error("Expected value to be defined");
22200
- }
22201
- return value;
22202
- }
23026
+
23027
+ // src/storage/tool-audit-log.ts
23028
+ init_atomic_write();
22203
23029
  var GENESIS_PREV = "0".repeat(64);
22204
23030
  var DEFAULT_FSYNC_EVERY = 100;
22205
23031
  var ToolAuditLog = class {
@@ -22225,37 +23051,42 @@ var ToolAuditLog = class {
22225
23051
  async record(input) {
22226
23052
  let entry = null;
22227
23053
  await this.enqueue(input.sessionId, async () => {
22228
- const prevHash = this.tailHash.get(input.sessionId) ?? GENESIS_PREV;
22229
- const index = this.tailIndex.get(input.sessionId) ?? 0;
22230
- const id = randomUUID();
22231
- const ts = (/* @__PURE__ */ new Date()).toISOString();
22232
- const content = {
22233
- id,
22234
- ts,
22235
- prevHash,
22236
- toolName: input.toolName,
22237
- toolUseId: input.toolUseId,
22238
- input: input.input,
22239
- output: input.output,
22240
- isError: input.isError,
22241
- index
22242
- };
22243
- const hash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
22244
- entry = {
22245
- id,
22246
- ts,
22247
- prevHash,
22248
- hash,
22249
- toolName: input.toolName,
22250
- toolUseId: input.toolUseId,
22251
- input: input.input,
22252
- output: input.output,
22253
- isError: input.isError,
22254
- index
22255
- };
22256
- await this.appendLine(input.sessionId, entry);
22257
- this.tailHash.set(input.sessionId, hash);
22258
- this.tailIndex.set(input.sessionId, index + 1);
23054
+ await withFileLock(this.filePath(input.sessionId), async () => {
23055
+ const entries = await this.readAll(input.sessionId);
23056
+ const prev = entries.at(-1);
23057
+ const prevHash = prev?.hash ?? GENESIS_PREV;
23058
+ const index = prev ? prev.index + 1 : 0;
23059
+ const id = randomUUID();
23060
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
23061
+ const content = {
23062
+ id,
23063
+ ts,
23064
+ prevHash,
23065
+ toolName: input.toolName,
23066
+ toolUseId: input.toolUseId,
23067
+ input: input.input,
23068
+ output: input.output,
23069
+ isError: input.isError,
23070
+ index
23071
+ };
23072
+ const hash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
23073
+ entry = {
23074
+ id,
23075
+ ts,
23076
+ prevHash,
23077
+ hash,
23078
+ toolName: input.toolName,
23079
+ toolUseId: input.toolUseId,
23080
+ input: input.input,
23081
+ output: input.output,
23082
+ isError: input.isError,
23083
+ index
23084
+ };
23085
+ entries.push(entry);
23086
+ await this.writeAll(input.sessionId, entries);
23087
+ this.tailHash.set(input.sessionId, hash);
23088
+ this.tailIndex.set(input.sessionId, index + 1);
23089
+ });
22259
23090
  });
22260
23091
  return entry;
22261
23092
  }
@@ -22275,7 +23106,7 @@ var ToolAuditLog = class {
22275
23106
  }
22276
23107
  let prevHash = GENESIS_PREV;
22277
23108
  for (let i = 0; i < entries.length; i++) {
22278
- const e = expectDefined20(entries[i]);
23109
+ const e = expectDefined(entries[i]);
22279
23110
  if (e.prevHash !== prevHash) {
22280
23111
  return {
22281
23112
  ok: false,
@@ -22325,7 +23156,8 @@ var ToolAuditLog = class {
22325
23156
  for (const line of raw.split("\n")) {
22326
23157
  if (!line.trim()) continue;
22327
23158
  try {
22328
- out.push(JSON.parse(line));
23159
+ const parsed = safeParse(line);
23160
+ if (parsed.ok && parsed.value) out.push(parsed.value);
22329
23161
  } catch {
22330
23162
  }
22331
23163
  }
@@ -22335,10 +23167,10 @@ var ToolAuditLog = class {
22335
23167
  return [];
22336
23168
  }
22337
23169
  }
22338
- async appendLine(sessionId, entry) {
23170
+ async writeAll(sessionId, entries) {
22339
23171
  const fp = this.filePath(sessionId);
22340
- const line = JSON.stringify(entry) + "\n";
22341
- await fsp3.appendFile(fp, line, "utf8");
23172
+ const line = entries.map((e) => JSON.stringify(e)).join("\n") + (entries.length ? "\n" : "");
23173
+ await atomicWrite(fp, line, { mode: 384 });
22342
23174
  const count = (this.unSyncedWrites.get(sessionId) ?? 0) + 1;
22343
23175
  this.unSyncedWrites.set(sessionId, count);
22344
23176
  if (this.fsyncEvery !== Number.POSITIVE_INFINITY && count % this.fsyncEvery === 0) {
@@ -22394,12 +23226,6 @@ function sortKeys2(value) {
22394
23226
 
22395
23227
  // src/storage/session-rewinder.ts
22396
23228
  init_atomic_write();
22397
- function expectDefined21(value) {
22398
- if (value === null || value === void 0) {
22399
- throw new Error("Expected value to be defined");
22400
- }
22401
- return value;
22402
- }
22403
23229
  var DefaultSessionRewinder = class {
22404
23230
  constructor(sessionsDir, projectRoot) {
22405
23231
  this.sessionsDir = sessionsDir;
@@ -22438,7 +23264,7 @@ var DefaultSessionRewinder = class {
22438
23264
  const events = parseEvents(raw);
22439
23265
  let targetIdx = -1;
22440
23266
  for (let i = 0; i < events.length; i++) {
22441
- const event = expectDefined21(events[i]);
23267
+ const event = expectDefined(events[i]);
22442
23268
  if (event.type === "checkpoint") {
22443
23269
  const checkpointEvent = event;
22444
23270
  if (checkpointEvent.promptIndex === checkpointIndex) {
@@ -22452,7 +23278,7 @@ var DefaultSessionRewinder = class {
22452
23278
  }
22453
23279
  const snapshotsToRevert = [];
22454
23280
  for (let i = targetIdx + 1; i < events.length; i++) {
22455
- const event = expectDefined21(events[i]);
23281
+ const event = expectDefined(events[i]);
22456
23282
  if (event.type === "checkpoint") {
22457
23283
  break;
22458
23284
  }
@@ -22634,12 +23460,6 @@ var DefaultPromptStore = class {
22634
23460
  };
22635
23461
  }
22636
23462
  };
22637
- function expectDefined22(value) {
22638
- if (value === null || value === void 0) {
22639
- throw new Error("Expected value to be defined");
22640
- }
22641
- return value;
22642
- }
22643
23463
  var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
22644
23464
  var CloudSync = class {
22645
23465
  constructor(paths, getConfig, setConfig) {
@@ -22682,8 +23502,8 @@ var CloudSync = class {
22682
23502
  const cfg = this.getConfig();
22683
23503
  if (!cfg?.enabled) return { ok: false, action: "push", categories: [], message: "Not enabled." };
22684
23504
  const parts = cfg.repo.split("/");
22685
- const owner = expectDefined22(parts[0]);
22686
- const repoName = expectDefined22(parts[1]);
23505
+ const owner = expectDefined(parts[0]);
23506
+ const repoName = expectDefined(parts[1]);
22687
23507
  const branch = "main";
22688
23508
  const baseTreeSha = this.state?.sha;
22689
23509
  const { treeEntries, rev } = await this.buildLocalTree(cfg.categories);
@@ -22735,8 +23555,8 @@ var CloudSync = class {
22735
23555
  const cfg = this.getConfig();
22736
23556
  if (!cfg?.enabled) return { ok: false, action: "pull", categories: [], message: "Not enabled." };
22737
23557
  const pullParts = cfg.repo.split("/");
22738
- const owner = expectDefined22(pullParts[0]);
22739
- const repoName = expectDefined22(pullParts[1]);
23558
+ const owner = expectDefined(pullParts[0]);
23559
+ const repoName = expectDefined(pullParts[1]);
22740
23560
  const branchData = await this.getRef(token, owner, repoName, "main");
22741
23561
  const currentSha = branchData.object.sha;
22742
23562
  const commitData = await this.getCommit(token, owner, repoName, currentSha);
@@ -22855,8 +23675,8 @@ var CloudSync = class {
22855
23675
  const localPath = this.categoryToPath(cat);
22856
23676
  if (!localPath) continue;
22857
23677
  try {
22858
- const stat10 = await fsp3.stat(localPath);
22859
- if (stat10.isDirectory()) {
23678
+ const stat11 = await fsp3.stat(localPath);
23679
+ if (stat11.isDirectory()) {
22860
23680
  const files = await this.walkDir(localPath, localPath);
22861
23681
  for (const file of files) {
22862
23682
  const content = await fsp3.readFile(file, "utf8");
@@ -22881,8 +23701,8 @@ var CloudSync = class {
22881
23701
  const localPath = this.categoryToPath(cat);
22882
23702
  if (!localPath) continue;
22883
23703
  try {
22884
- const stat10 = await fsp3.stat(localPath);
22885
- if (stat10.isDirectory()) {
23704
+ const stat11 = await fsp3.stat(localPath);
23705
+ if (stat11.isDirectory()) {
22886
23706
  const files = await this.walkDir(localPath, localPath);
22887
23707
  for (const file of files) {
22888
23708
  const content = await fsp3.readFile(file);
@@ -23834,8 +24654,8 @@ var ReportGenerator = class {
23834
24654
  try {
23835
24655
  await stat(this.options.outputDir);
23836
24656
  } catch {
23837
- const { mkdir: mkdir16 } = await import('fs/promises');
23838
- await mkdir16(this.options.outputDir, { recursive: true });
24657
+ const { mkdir: mkdir17 } = await import('fs/promises');
24658
+ await mkdir17(this.options.outputDir, { recursive: true });
23839
24659
  }
23840
24660
  }
23841
24661
  generateMarkdown(result) {
@@ -24082,12 +24902,6 @@ var defaultGitignoreUpdater = new GitignoreUpdater();
24082
24902
 
24083
24903
  // src/security-scanner/orchestrator.ts
24084
24904
  init_atomic_write();
24085
- function expectDefined23(value) {
24086
- if (value === null || value === void 0) {
24087
- throw new Error("Expected value to be defined");
24088
- }
24089
- return value;
24090
- }
24091
24905
  var SecurityScannerOrchestrator = class {
24092
24906
  constructor(retryPolicy, errorHandler) {
24093
24907
  this.retryPolicy = retryPolicy;
@@ -24137,7 +24951,7 @@ var SecurityScannerOrchestrator = class {
24137
24951
  if (detectionResult.detectedStacks.length === 0) {
24138
24952
  throw new Error(`No supported tech stack detected in ${projectRoot}`);
24139
24953
  }
24140
- const techStack = expectDefined23(detectionResult.detectedStacks[0]);
24954
+ const techStack = expectDefined(detectionResult.detectedStacks[0]);
24141
24955
  const generatedSkill = await this.generateSkillLLM(provider, model, projectRoot, techStack);
24142
24956
  const scanResult = await this.scanWithLLM(provider, model, projectRoot, generatedSkill, techStack, options);
24143
24957
  const synthesizedReport = await this.synthesizeReportLLM(provider, model, projectRoot, techStack, scanResult);
@@ -24218,7 +25032,7 @@ Return ONLY the JSON object, no markdown, no explanation.`;
24218
25032
  const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
24219
25033
  const jsonMatch = text.match(/\{[\s\S]*\}/);
24220
25034
  if (jsonMatch) {
24221
- const sanitized = sanitizeJsonString(expectDefined23(jsonMatch[0])) || expectDefined23(jsonMatch[0]);
25035
+ const sanitized = sanitizeJsonString(expectDefined(jsonMatch[0])) || expectDefined(jsonMatch[0]);
24222
25036
  const skillData = JSON.parse(sanitized);
24223
25037
  return {
24224
25038
  name: skillData.name || `security-scanner-${techStack.stack}`,
@@ -24338,7 +25152,7 @@ Return ONLY the JSON array. If no issues found, return [].`;
24338
25152
  const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
24339
25153
  const jsonMatch = text.match(/\[[\s\S]*\]/);
24340
25154
  if (jsonMatch) {
24341
- const sanitized = sanitizeJsonString(expectDefined23(jsonMatch[0])) || expectDefined23(jsonMatch[0]);
25155
+ const sanitized = sanitizeJsonString(expectDefined(jsonMatch[0])) || expectDefined(jsonMatch[0]);
24342
25156
  const parsed = JSON.parse(sanitized);
24343
25157
  return parsed.map((item, idx) => ({
24344
25158
  id: `llm-finding-${idx}-${Date.now()}`,
@@ -24578,7 +25392,7 @@ ${dirs.join(", ")}`);
24578
25392
  if (detectionResult.detectedStacks.length === 0) {
24579
25393
  throw new Error(`No supported tech stack detected in ${projectRoot}`);
24580
25394
  }
24581
- const techStack = expectDefined23(detectionResult.detectedStacks[0]);
25395
+ const techStack = expectDefined(detectionResult.detectedStacks[0]);
24582
25396
  return {
24583
25397
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
24584
25398
  projectRoot,
@@ -24767,16 +25581,16 @@ Use \`/security report <number>\` to view a specific report.` };
24767
25581
  }
24768
25582
  const index = Number.parseInt(reportId, 10) - 1;
24769
25583
  if (!Number.isNaN(index) && reports[index]) {
24770
- const { readFile: readFile38 } = await import('fs/promises');
24771
- const content = await readFile38(join(reportsDir, reports[index]), "utf-8");
25584
+ const { readFile: readFile39 } = await import('fs/promises');
25585
+ const content = await readFile39(join(reportsDir, reports[index]), "utf-8");
24772
25586
  return { message: `# Security Report
24773
25587
 
24774
25588
  ${content}` };
24775
25589
  }
24776
25590
  const match = reports.find((r) => r.includes(reportId));
24777
25591
  if (match) {
24778
- const { readFile: readFile38 } = await import('fs/promises');
24779
- const content = await readFile38(join(reportsDir, match), "utf-8");
25592
+ const { readFile: readFile39 } = await import('fs/promises');
25593
+ const content = await readFile39(join(reportsDir, match), "utf-8");
24780
25594
  return { message: `# Security Report
24781
25595
 
24782
25596
  ${content}` };
@@ -25186,12 +26000,6 @@ var FleetManager = class {
25186
26000
  this.usage.dispose();
25187
26001
  }
25188
26002
  };
25189
- function expectDefined24(value) {
25190
- if (value === null || value === void 0) {
25191
- throw new Error("Expected value to be defined");
25192
- }
25193
- return value;
25194
- }
25195
26003
  function createMcpControlTool(opts) {
25196
26004
  const { getConfig, configPath, registry } = opts;
25197
26005
  const inputSchema = {
@@ -25343,7 +26151,7 @@ async function runDisable(name, deps) {
25343
26151
  const mcpServers = {
25344
26152
  ...full.mcpServers ?? {}
25345
26153
  };
25346
- const existing = expectDefined24(mcpServers[name]);
26154
+ const existing = expectDefined(mcpServers[name]);
25347
26155
  mcpServers[name] = { ...existing, enabled: false };
25348
26156
  full.mcpServers = mcpServers;
25349
26157
  await writeConfig(deps.configPath, full);
@@ -25414,12 +26222,6 @@ function badge(state) {
25414
26222
  }
25415
26223
 
25416
26224
  // src/extension/registry.ts
25417
- function expectDefined25(value) {
25418
- if (value === null || value === void 0) {
25419
- throw new Error("Expected value to be defined");
25420
- }
25421
- return value;
25422
- }
25423
26225
  var ExtensionRegistry = class {
25424
26226
  extensions = [];
25425
26227
  promptContributors = [];
@@ -25585,7 +26387,7 @@ var ExtensionRegistry = class {
25585
26387
  * default runner, each subsequent wrapper wraps the previous.
25586
26388
  */
25587
26389
  wrapProviderRunner(inner) {
25588
- const wrappers = this.extensions.filter((e) => e.wrapProviderRunner).map((e) => ({ name: e.name, wrap: expectDefined25(e.wrapProviderRunner) }));
26390
+ const wrappers = this.extensions.filter((e) => e.wrapProviderRunner).map((e) => ({ name: e.name, wrap: expectDefined(e.wrapProviderRunner) }));
25589
26391
  if (wrappers.length === 0) return inner;
25590
26392
  let composed = inner;
25591
26393
  for (let i = wrappers.length - 1; i >= 0; i--) {
@@ -26602,6 +27404,7 @@ async function bootConfig(options = {}) {
26602
27404
  await fsp3.mkdir(wpaths.projectDir, { recursive: true });
26603
27405
  await fsp3.mkdir(wpaths.projectSessions, { recursive: true });
26604
27406
  await writeProjectMeta(wpaths, projectRoot);
27407
+ await ensureGitignore(projectRoot);
26605
27408
  cleanupStaleProjects(wpaths).catch(() => {
26606
27409
  });
26607
27410
  const vault = new DefaultSecretVault({ keyFile: wpaths.secretsKey });
@@ -26677,6 +27480,22 @@ async function writeProjectMeta(paths, projectRoot) {
26677
27480
  } catch {
26678
27481
  }
26679
27482
  }
27483
+ var GITIGNORE_ENTRY = ".wrongstack/\n";
27484
+ async function ensureGitignore(projectRoot) {
27485
+ const gitignorePath = path6.join(projectRoot, ".gitignore");
27486
+ try {
27487
+ let content = "";
27488
+ try {
27489
+ content = await fsp3.readFile(gitignorePath, "utf8");
27490
+ } catch {
27491
+ }
27492
+ if (!content.includes(".wrongstack")) {
27493
+ const updated = content ? content.endsWith("\n") ? content + GITIGNORE_ENTRY : content + "\n" + GITIGNORE_ENTRY : GITIGNORE_ENTRY;
27494
+ await fsp3.writeFile(gitignorePath, updated, "utf8");
27495
+ }
27496
+ } catch {
27497
+ }
27498
+ }
26680
27499
  async function cleanupStaleProjects(wpaths) {
26681
27500
  const projectsRoot = path6.dirname(wpaths.projectDir);
26682
27501
  let entries;
@@ -27114,7 +27933,10 @@ var DefaultSystemPromptBuilder = class {
27114
27933
  skillCache;
27115
27934
  /** Cached full skill bodies (after frontmatter), built once per session. */
27116
27935
  skillBodyCache;
27936
+ /** Tools from last build — used for memory relevance scoring. */
27937
+ _lastBuildTools;
27117
27938
  async build(ctx) {
27939
+ this._lastBuildTools = ctx.tools;
27118
27940
  if (this.opts.skillLoader && !this.skillCache) {
27119
27941
  try {
27120
27942
  const entries = await this.opts.skillLoader.listEntries();
@@ -27418,10 +28240,31 @@ summarize it, and let the tool result hold only the summary.`);
27418
28240
  const parts = [];
27419
28241
  if (this.opts.memoryStore) {
27420
28242
  try {
27421
- const mem = await this.opts.memoryStore.readAll();
27422
- if (mem.trim()) parts.push(`# Project Memory
28243
+ if (this.opts.memoryStore.scoreRelevant) {
28244
+ const toolNames = this._lastBuildTools?.map((t2) => t2.name) ?? [];
28245
+ const scored = await this.opts.memoryStore.scoreRelevant(
28246
+ {
28247
+ currentTask: "",
28248
+ toolNames
28249
+ },
28250
+ "project-memory",
28251
+ 8
28252
+ );
28253
+ if (scored.length > 0) {
28254
+ const lines = ["# Relevant Memory"];
28255
+ for (const e of scored) {
28256
+ const badge2 = e.type ? `[\`${e.type.replace("_", "-")}\`] ` : "";
28257
+ const priorityMark = e.priority === "critical" ? "\u26A1" : e.priority === "high" ? "\u25B2" : "";
28258
+ lines.push(`- ${priorityMark}${badge2}${e.text}${e.tags ? ` \`#${e.tags.join(" #")}\`` : ""}`);
28259
+ }
28260
+ parts.push(lines.join("\n"));
28261
+ }
28262
+ } else {
28263
+ const mem = await this.opts.memoryStore.readAll();
28264
+ if (mem.trim()) parts.push(`# Project Memory
27423
28265
 
27424
28266
  ${mem}`);
28267
+ }
27425
28268
  } catch {
27426
28269
  }
27427
28270
  }
@@ -27470,8 +28313,8 @@ ${this.skillBodyCache}`);
27470
28313
  }
27471
28314
  async dirExists(p) {
27472
28315
  try {
27473
- const stat10 = await fsp3.stat(p);
27474
- return stat10.isDirectory();
28316
+ const stat11 = await fsp3.stat(p);
28317
+ return stat11.isDirectory();
27475
28318
  } catch {
27476
28319
  return false;
27477
28320
  }
@@ -27967,7 +28810,7 @@ var DefaultPluginAPI = class {
27967
28810
  return off;
27968
28811
  }
27969
28812
  emitCustom(event, payload) {
27970
- this.events.emit(event, payload);
28813
+ this.events.emitCustom(event, payload);
27971
28814
  }
27972
28815
  onConfigChange(handler) {
27973
28816
  if (!this.configStore) return () => {
@@ -28308,20 +29151,28 @@ function wrapApiForCapabilityCheck(plugin, api, log, enforce = false) {
28308
29151
  }
28309
29152
 
28310
29153
  // src/execution/prompt-enhancer.ts
28311
- var ENHANCER_SYSTEM_PROMPT = `You are a request refiner embedded in a coding agent. Your ONLY job is to rewrite the user's message into a single, clearer, unambiguous instruction that the coding agent can act on confidently.
29154
+ var ENHANCER_SYSTEM_PROMPT = `You are a request refiner embedded in a coding agent. Your ONLY job is to rewrite the user's message into clearer, unambiguous instructions that the coding agent can act on confidently.
28312
29155
 
28313
29156
  Rules:
28314
29157
  - Preserve the user's intent and scope EXACTLY. Do not add new requirements, features, constraints, or steps the user did not ask for. Do not remove anything they did ask for.
28315
29158
  - Do NOT answer, solve, or perform the request. Only restate it more clearly.
28316
29159
  - Keep all concrete details verbatim: file paths, identifiers, code, error text, numbers, names, URLs.
28317
29160
  - Resolve obvious ambiguity by making the implied subject explicit, not by inventing specifics. If something is genuinely unspecified, leave it general rather than guessing.
28318
- - Be concise: one tight instruction (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.
29161
+ - Be concise: one tight instruction per version (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.
28319
29162
  - If the message is already clear and complete, return it essentially unchanged.
28320
- - Preserve the user's language (if they wrote in Turkish, refine in Turkish).
29163
+
29164
+ You MUST output TWO versions of the refined request, separated by a line containing only "---".
29165
+ - First version: refined in the SAME LANGUAGE the user wrote in (if Turkish \u2192 Turkish, if Spanish \u2192 Spanish, etc.).
29166
+ - Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).
29167
+
29168
+ Output format:
29169
+ <refined in user's language>
29170
+ ---
29171
+ <refined in English>
28321
29172
 
28322
29173
  When earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message \u2014 "it", "that", "the same", "the other one", "this file", "again" \u2014 so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation.
28323
29174
 
28324
- Output ONLY the refined request text \u2014 nothing else.`;
29175
+ Output ONLY the two versions separated by "---" \u2014 nothing else.`;
28325
29176
  var AFFIRMATION_RE = /^(y|n|yes|no|yep|nope|ok|okay|sure|go|go ahead|continue|proceed|stop|cancel|done|next|skip|retry|again|please do|do it)\b[.! ]*$/i;
28326
29177
  function shouldEnhance(text) {
28327
29178
  const t2 = text.trim();
@@ -28368,12 +29219,23 @@ async function enhanceUserPrompt(opts) {
28368
29219
  const signal = opts.signal ? AbortSignal.any([opts.signal, timer.signal]) : timer.signal;
28369
29220
  try {
28370
29221
  const res = await provider.complete(req, { signal });
28371
- const refined = res.content.filter(isTextBlock).map((b) => b.text).join("\n").trim();
28372
- if (!refined) {
29222
+ const raw = res.content.filter(isTextBlock).map((b) => b.text).join("\n").trim();
29223
+ if (!raw) {
28373
29224
  opts.onError?.("model returned no text");
28374
29225
  return null;
28375
29226
  }
28376
- return refined;
29227
+ const sepIdx = raw.indexOf("\n---\n");
29228
+ if (sepIdx === -1) {
29229
+ opts.onError?.("model did not produce two versions");
29230
+ return { refined: raw, english: raw };
29231
+ }
29232
+ const refined = raw.slice(0, sepIdx).trim();
29233
+ const english = raw.slice(sepIdx + 5).trim();
29234
+ if (!refined || !english) {
29235
+ opts.onError?.("one or both versions empty");
29236
+ return null;
29237
+ }
29238
+ return { refined, english };
28377
29239
  } catch (err) {
28378
29240
  if (opts.signal?.aborted) return null;
28379
29241
  if (timer.signal.aborted) {
@@ -28476,8 +29338,8 @@ var PhaseGraphBuilder = class _PhaseGraphBuilder {
28476
29338
  return graph;
28477
29339
  }
28478
29340
  /**
28479
- * Var olan bir TaskGraph'tan fazlar oluştur.
28480
- * Task'ları priority/type göre gruplayarak fazlara ayırır.
29341
+ * Create phases from an existing TaskGraph.
29342
+ * Groups tasks into phases by priority and type.
28481
29343
  */
28482
29344
  static fromTaskGraph(taskGraph, options) {
28483
29345
  const tasksPerPhase = options.tasksPerPhase ?? 5;
@@ -28555,8 +29417,8 @@ var PhaseOrchestrator = class {
28555
29417
  }
28556
29418
  // ─── Lifecycle ────────────────────────────────────────────────────────────
28557
29419
  /**
28558
- * Tüm faz akışını başlat.
28559
- * Autonomous mode'da: kök faz(lar)ı başlatır, bitince sonrakini otomatik başlatır.
29420
+ * Start the full phase flow.
29421
+ * In autonomous mode, starts root phases and automatically starts the next phase when they finish.
28560
29422
  */
28561
29423
  async start() {
28562
29424
  this.stopped = false;
@@ -28583,17 +29445,17 @@ var PhaseOrchestrator = class {
28583
29445
  this.tickInterval = setInterval(() => this.tick(), 1e3);
28584
29446
  }
28585
29447
  }
28586
- /** Bekleyen tüm faz merge'lerini (dep-sıralı + global seri) bekle. */
29448
+ /** Wait for all pending phase merges, dependency-ordered and globally serialized. */
28587
29449
  async drainMerges() {
28588
29450
  await Promise.allSettled([...this.phaseMergePromise.values()]);
28589
29451
  await this.mergeQueue.catch(() => {
28590
29452
  });
28591
29453
  }
28592
- /** Duraklat aktif fazlar çalışmaya devam eder ama yeni faz başlamaz */
29454
+ /** Pause: active phases continue, but no new phase starts. */
28593
29455
  pause() {
28594
29456
  this.paused = true;
28595
29457
  }
28596
- /** Devam et yeni fazlar başlayabilir */
29458
+ /** Resume: new phases may start again. */
28597
29459
  resume() {
28598
29460
  this.paused = false;
28599
29461
  this.tick().catch((err) => {
@@ -28603,7 +29465,7 @@ var PhaseOrchestrator = class {
28603
29465
  );
28604
29466
  });
28605
29467
  }
28606
- /** Tamamen durdur aktif fazlar da durdurulur */
29468
+ /** Stop completely, including active phases. */
28607
29469
  stop() {
28608
29470
  this.stopped = true;
28609
29471
  if (this.tickInterval) {
@@ -28801,7 +29663,10 @@ var PhaseOrchestrator = class {
28801
29663
  const depPromises = phase.dependsOn.map((d) => this.phaseMergePromise.get(d)).filter((p) => Boolean(p));
28802
29664
  const merged = (async () => {
28803
29665
  await Promise.allSettled(depPromises);
28804
- this.mergeQueue = this.mergeQueue.then(() => this.mergeOne(phase, handle));
29666
+ this.mergeQueue = this.mergeQueue.then(() => this.mergeOne(phase, handle)).catch((err) => {
29667
+ const msg = err instanceof Error ? err.message : String(err);
29668
+ console.error(`[PhaseOrchestrator] mergeOne failed for phase ${phase.id}: ${msg}`);
29669
+ });
28805
29670
  await this.mergeQueue;
28806
29671
  })();
28807
29672
  this.phaseMergePromise.set(phase.id, merged);
@@ -29189,6 +30054,7 @@ var AutoPhaseRunner = class {
29189
30054
  orchestrator = null;
29190
30055
  opts;
29191
30056
  progressInterval = null;
30057
+ maxRunTimer = null;
29192
30058
  graphCompletedHandler = (payload) => {
29193
30059
  const p = payload;
29194
30060
  if (this.graph && p.graphId === this.graph.id) {
@@ -29256,6 +30122,11 @@ var AutoPhaseRunner = class {
29256
30122
  if (progress) this.opts.onProgress?.(progress);
29257
30123
  }, 2e3);
29258
30124
  }
30125
+ this.maxRunTimer = setTimeout(
30126
+ () => this.stop(),
30127
+ this.opts.maxRunDurationMs ?? 24 * 60 * 6e4
30128
+ );
30129
+ this.maxRunTimer.unref?.();
29259
30130
  if (this.opts.events) {
29260
30131
  const events = this.opts.events;
29261
30132
  const onUntyped = events.on;
@@ -29298,6 +30169,10 @@ var AutoPhaseRunner = class {
29298
30169
  clearInterval(this.progressInterval);
29299
30170
  this.progressInterval = null;
29300
30171
  }
30172
+ if (this.maxRunTimer) {
30173
+ clearTimeout(this.maxRunTimer);
30174
+ this.maxRunTimer = null;
30175
+ }
29301
30176
  this.unsubscribeCompleted?.();
29302
30177
  this.unsubscribeCompleted = null;
29303
30178
  this.unsubscribeFailed?.();
@@ -29343,14 +30218,14 @@ var AutoPhasePlanner = class {
29343
30218
  this.opts = opts;
29344
30219
  }
29345
30220
  opts;
29346
- /** Hedefi faz+todo planına dönüştür. */
30221
+ /** Convert the goal into a phase-and-todo plan. */
29347
30222
  async plan() {
29348
30223
  const prompt = this.buildPrompt();
29349
30224
  const raw = await this.opts.runOnce(prompt);
29350
30225
  const phases = this.parse(raw);
29351
30226
  return { phases, raw, parseFailed: phases.length === 0 };
29352
30227
  }
29353
- /** Modelin üreteceği plan için talimat prompt'u. */
30228
+ /** Instruction prompt for the plan the model should produce. */
29354
30229
  buildPrompt() {
29355
30230
  const minP = this.opts.minPhases ?? 3;
29356
30231
  const maxP = this.opts.maxPhases ?? 8;
@@ -29398,7 +30273,7 @@ ${ctx}
29398
30273
  "```"
29399
30274
  ].filter((l) => l !== "").join("\n");
29400
30275
  }
29401
- /** Ham çıktıdan JSON'u çıkar, doğrula ve PhaseTemplate[]'e dönüştür. */
30276
+ /** Extract JSON from raw output, validate it, and convert it to PhaseTemplate[]. */
29402
30277
  parse(raw) {
29403
30278
  const json = extractJSONArray(raw);
29404
30279
  if (!json) return [];
@@ -30138,7 +31013,8 @@ ${check.stderr}`);
30138
31013
  const child = spawn(this.gitBin, args, {
30139
31014
  cwd,
30140
31015
  env: buildChildEnv(),
30141
- stdio: ["ignore", "pipe", "pipe"]
31016
+ stdio: ["ignore", "pipe", "pipe"],
31017
+ signal: AbortSignal.timeout(3e4)
30142
31018
  });
30143
31019
  child.stdout?.on("data", (c) => {
30144
31020
  stdout += c.toString();
@@ -30506,12 +31382,6 @@ var ReplayProviderRunner = class {
30506
31382
  };
30507
31383
 
30508
31384
  // src/plugins/prompts-plugin.ts
30509
- function expectDefined26(value) {
30510
- if (value === null || value === void 0) {
30511
- throw new Error("Expected value to be defined");
30512
- }
30513
- return value;
30514
- }
30515
31385
  function createPromptsPlugin(opts) {
30516
31386
  let store = null;
30517
31387
  return {
@@ -30565,7 +31435,7 @@ ${lines.join("\n")}
30565
31435
  if (!restJoined) return { message: "Usage: /prompts view <title>" };
30566
31436
  const matches = await store.find(restJoined);
30567
31437
  if (matches.length === 0) return { message: `No prompt matching "${restJoined}".` };
30568
- const entry = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined26(matches[0]);
31438
+ const entry = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined(matches[0]);
30569
31439
  const tags = entry.tags.length > 0 ? ` [${entry.tags.join(", ")}]` : "";
30570
31440
  return {
30571
31441
  message: `# ${entry.title}${tags}
@@ -30588,7 +31458,7 @@ ${dim2(`id: ${entry.id} | created: ${entry.createdAt}`)}`
30588
31458
  if (!restJoined) return { message: "Usage: /prompts delete <title>" };
30589
31459
  const matches = await store.find(restJoined);
30590
31460
  if (matches.length === 0) return { message: `No prompt matching "${restJoined}".` };
30591
- const exact = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined26(matches[0]);
31461
+ const exact = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined(matches[0]);
30592
31462
  const deleted = await store.delete(exact.id);
30593
31463
  return { message: deleted ? `Deleted "${exact.title}".` : "Delete failed." };
30594
31464
  }
@@ -30598,7 +31468,7 @@ ${dim2(`id: ${entry.id} | created: ${entry.createdAt}`)}`
30598
31468
  if (!parsed.title) return { message: 'Usage: /prompts edit "title" "new content"' };
30599
31469
  const matches = await store.find(parsed.title);
30600
31470
  if (matches.length === 0) return { message: `No prompt matching "${parsed.title}".` };
30601
- const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined26(matches[0]);
31471
+ const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined(matches[0]);
30602
31472
  exact.content = parsed.content;
30603
31473
  exact.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
30604
31474
  await store.save(exact);
@@ -30610,7 +31480,7 @@ ${dim2(`id: ${entry.id} | created: ${entry.createdAt}`)}`
30610
31480
  if (!parsed.title) return { message: 'Usage: /prompts extend "title" <instructions>' };
30611
31481
  const matches = await store.find(parsed.title);
30612
31482
  if (matches.length === 0) return { message: `No prompt matching "${parsed.title}".` };
30613
- const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined26(matches[0]);
31483
+ const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined(matches[0]);
30614
31484
  const prov = ctx.provider;
30615
31485
  if (!prov?.complete) return { message: "LLM not available. Configure a provider first." };
30616
31486
  const enhanced = await prov.complete(ctx.model, [
@@ -30645,9 +31515,9 @@ function parseTitleContent(args) {
30645
31515
  const trimmed = args.trim();
30646
31516
  if (!trimmed) return { title: "", content: "" };
30647
31517
  const doubleMatch = /^"([^"]+)"\s+"([^"]+)"$/.exec(trimmed) || /^'([^']+)'\s+'([^']+)'$/.exec(trimmed);
30648
- if (doubleMatch) return { title: expectDefined26(doubleMatch[1]), content: expectDefined26(doubleMatch[2]) };
31518
+ if (doubleMatch) return { title: expectDefined(doubleMatch[1]), content: expectDefined(doubleMatch[2]) };
30649
31519
  const singleMatch = /^'([^']+)'\s+(.+)$/.exec(trimmed);
30650
- if (singleMatch) return { title: expectDefined26(singleMatch[1]), content: expectDefined26(singleMatch[2]) };
31520
+ if (singleMatch) return { title: expectDefined(singleMatch[1]), content: expectDefined(singleMatch[2]) };
30651
31521
  const firstSpace = trimmed.indexOf(" ");
30652
31522
  if (firstSpace === -1) return { title: trimmed, content: "" };
30653
31523
  return { title: trimmed.slice(0, firstSpace), content: trimmed.slice(firstSpace + 1) };
@@ -30658,12 +31528,6 @@ function dim2(s) {
30658
31528
 
30659
31529
  // src/plugins/sync-plugin.ts
30660
31530
  init_atomic_write();
30661
- function expectDefined27(value) {
30662
- if (value === null || value === void 0) {
30663
- throw new Error("Expected value to be defined");
30664
- }
30665
- return value;
30666
- }
30667
31531
  function createSyncPlugin(opts) {
30668
31532
  let cloud = null;
30669
31533
  let configStore;
@@ -30737,7 +31601,7 @@ function buildSyncCommand(cloud, configStore, vault, syncConfigPath) {
30737
31601
  if (!repo || !repo.includes("/")) {
30738
31602
  return { message: 'Invalid repo format. Expected "owner/repo".' };
30739
31603
  }
30740
- const storedToken = vault ? vault.encrypt(expectDefined27(token)) : expectDefined27(token);
31604
+ const storedToken = vault ? vault.encrypt(expectDefined(token)) : expectDefined(token);
30741
31605
  const syncConfig = {
30742
31606
  enabled: true,
30743
31607
  repo,
@@ -30878,7 +31742,7 @@ function createGitPlugin() {
30878
31742
  async function runGit(args, cwd) {
30879
31743
  try {
30880
31744
  return await new Promise((resolve13, reject) => {
30881
- const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
31745
+ const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], signal: AbortSignal.timeout(3e4) });
30882
31746
  let stdout = "";
30883
31747
  let stderr = "";
30884
31748
  child.stdout?.on("data", (d) => {
@@ -31003,6 +31867,21 @@ function buildCommitCommand() {
31003
31867
  }
31004
31868
  const dryRun = args.includes("--dry-run") || args.includes("-n");
31005
31869
  const noLlm = args.includes("--no-llm");
31870
+ let worktreeWarning = "";
31871
+ try {
31872
+ const wtResult = await runGit(["worktree", "list", "--porcelain"], cwd);
31873
+ const worktrees = wtResult.stdout.split("\n\n").filter((b) => b.trim().startsWith("worktree "));
31874
+ if (worktrees.length > 1) {
31875
+ const branches = worktrees.map((b) => {
31876
+ const branchLine = b.split("\n").find((l) => l.startsWith("branch "));
31877
+ return branchLine ? branchLine.slice(7).replace("refs/heads/", "") : "?";
31878
+ }).filter(Boolean);
31879
+ worktreeWarning = `
31880
+ ${color.yellow("\u26A0")} ${color.dim("Simultaneous edits:")} ${worktrees.length} worktrees active (${branches.join(", ")}). ${color.dim("Changes from other agents may be captured. Review the diff below.")}
31881
+ `;
31882
+ }
31883
+ } catch {
31884
+ }
31006
31885
  let message = null;
31007
31886
  const provider = noLlm ? null : asLLMProvider(ctx?.provider);
31008
31887
  if (provider && ctx?.model) {
@@ -31011,26 +31890,40 @@ function buildCommitCommand() {
31011
31890
  }
31012
31891
  if (!message) message = await generateCommitMessageHeuristics(cwd);
31013
31892
  if (dryRun) {
31893
+ const diffStat2 = (await runGit(["diff", "--stat"], cwd)).stdout || "(no changes)";
31014
31894
  return {
31015
- message: `Would commit:
31016
-
31017
- ${color.green(message)}
31018
-
31019
- ${color.dim("(dry-run \u2014 no actual commit)")}`
31895
+ message: [
31896
+ worktreeWarning,
31897
+ `${color.dim("Would commit:")}`,
31898
+ ` ${color.green(message)}`,
31899
+ "",
31900
+ `${color.dim(diffStat2)}`,
31901
+ `${color.dim("(dry-run \u2014 no actual commit)")}`
31902
+ ].filter(Boolean).join("\n")
31020
31903
  };
31021
31904
  }
31022
31905
  const stageResult = await runGit(["add", "."], cwd);
31023
31906
  if (stageResult.code !== 0) return { message: `Stage failed: ${stageResult.stderr}` };
31907
+ const diffStat = (await runGit(["diff", "--cached", "--stat"], cwd)).stdout || "";
31908
+ const fullDiff = (await runGit(["diff", "--cached"], cwd)).stdout || "";
31909
+ const diffPreview = fullDiff.length > 8e3 ? fullDiff.slice(0, 8e3) + "\n\n... (diff truncated, showing first 8KB)" : fullDiff;
31024
31910
  const commitResult = await runGit(["commit", "-m", message], cwd);
31025
31911
  if (commitResult.code !== 0) return { message: `Commit failed: ${commitResult.stderr}` };
31026
31912
  const hash = (await runGit(["rev-parse", "--short", "HEAD"], cwd)).stdout.trim();
31027
31913
  const hasRemote = (await runGit(["remote"], cwd)).stdout.trim().length > 0;
31028
31914
  const pushMsg = hasRemote ? `
31029
-
31030
31915
  ${color.dim("Tip: Run /push to push to remote")}` : "";
31031
31916
  return {
31032
- message: `${color.green("\u2713")} Committed: ${color.bold(message)}
31033
- ${color.dim(hash)}${pushMsg}`
31917
+ message: [
31918
+ worktreeWarning,
31919
+ `${color.green("\u2713")} Committed: ${color.bold(message)}`,
31920
+ ` ${color.dim(hash)}`,
31921
+ "",
31922
+ color.dim("\u2500\u2500\u2500 Staged diff \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
31923
+ color.dim(diffStat),
31924
+ diffPreview ? color.dim(diffPreview) : "",
31925
+ pushMsg
31926
+ ].filter(Boolean).join("\n")
31034
31927
  };
31035
31928
  }
31036
31929
  };
@@ -31574,6 +32467,6 @@ ${formatPlan(updated)}`
31574
32467
  };
31575
32468
  }
31576
32469
 
31577
- export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BudgetExceededError, 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_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, 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, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, 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, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expectDefined7 as expectDefined, extractRunEnv, filesystemServer, findCriticalPath, flagsToConfigPatch, formatContextWindowModeList, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hashRequest, hookMatcherMatches, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, normalizedEqual, onResize, parseContinueDirective, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, writeErr, writeOut, zaiVisionServer };
32470
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BudgetExceededError, 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, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, 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, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GraphMemoryBackend, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, 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, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expectDefined, extractRunEnv, filesystemServer, findCriticalPath, flagsToConfigPatch, formatContextWindowModeList, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hashRequest, hookMatcherMatches, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, normalizedEqual, onResize, parseContinueDirective, parseEntries, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, withFileLock, wrapAsState, writeErr, writeOut, zaiVisionServer };
31578
32471
  //# sourceMappingURL=index.js.map
31579
32472
  //# sourceMappingURL=index.js.map