@wrongstack/core 0.89.1 → 0.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/{agent-bridge-B8F2ORwg.d.ts → agent-bridge-mOxbpFcg.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-CQaLFNnA.d.ts → agent-subagent-runner-DukQLUcS.d.ts} +433 -433
  3. package/dist/{events-DzyLtja6.d.ts → brain-Dfv4Y82E.d.ts} +179 -92
  4. package/dist/{compactor-CJq7LQev.d.ts → compactor-DXLxLcmU.d.ts} +2 -2
  5. package/dist/{config-_DZ7dN-T.d.ts → config-BSU-6vah.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 +128 -37
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +34 -35
  11. package/dist/defaults/index.js +752 -218
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/{director-state-CgIc30qi.d.ts → director-state-BfeCUbmk.d.ts} +1 -1
  14. package/dist/execution/index.d.ts +65 -370
  15. package/dist/execution/index.js +446 -53
  16. package/dist/execution/index.js.map +1 -1
  17. package/dist/extension/index.d.ts +9 -10
  18. package/dist/extension/index.js +8 -6
  19. package/dist/extension/index.js.map +1 -1
  20. package/dist/goal-preamble-CI8lxeY1.d.ts +378 -0
  21. package/dist/{goal-store-DvWLNu52.d.ts → goal-store-ht0VmR1A.d.ts} +63 -21
  22. package/dist/{index-Ca_lOmKl.d.ts → index-BWRN6wOb.d.ts} +13 -15
  23. package/dist/{index-Dx-CnrDN.d.ts → index-DIKEcfgC.d.ts} +9 -10
  24. package/dist/index.d.ts +188 -176
  25. package/dist/index.js +1508 -407
  26. package/dist/index.js.map +1 -1
  27. package/dist/infrastructure/index.d.ts +9 -9
  28. package/dist/infrastructure/index.js +4 -2
  29. package/dist/infrastructure/index.js.map +1 -1
  30. package/dist/kernel/index.d.ts +14 -16
  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-CXCsANdY.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-Da9nd8Ic.d.ts → multi-agent-coordinator-51LvnXkD.d.ts} +2 -2
  42. package/dist/{null-fleet-bus-CHJOXv3t.d.ts → null-fleet-bus-D09hMzFQ.d.ts} +32 -31
  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-DyMCj2Pw.d.ts → parallel-eternal-engine-CUtmM_0V.d.ts} +54 -35
  46. package/dist/{path-resolver-TjBlRKyH.d.ts → path-resolver-DDJiMAtX.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-BqiA_UMr.d.ts} +73 -73
  50. package/dist/{plan-templates-D9UhwoGh.d.ts → plan-templates-BdDxl9cI.d.ts} +79 -11
  51. package/dist/{provider-runner-BEPWwkxH.d.ts → provider-runner-BUunikwY.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 +17 -33
  54. package/dist/sdd/index.js +145 -36
  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 +4 -2
  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-BpJ5trO9.d.ts} +2 -2
  62. package/dist/{session-reader-BQU-toaN.d.ts → session-reader-DDz1Ek4V.d.ts} +2 -2
  63. package/dist/{skill-BJeF2DwY.d.ts → skill-Bj6Ezqb8.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/spec-TBi3Jr6T.d.ts +78 -0
  68. package/dist/storage/index.d.ts +145 -13
  69. package/dist/storage/index.js +990 -172
  70. package/dist/storage/index.js.map +1 -1
  71. package/dist/task-format-vGOIftmK.d.ts +23 -0
  72. package/dist/task-graph-u1q9Jkyk.d.ts +84 -0
  73. package/dist/types/index.d.ts +27 -28
  74. package/dist/types/index.js +19 -31
  75. package/dist/types/index.js.map +1 -1
  76. package/dist/utils/expect-defined.d.ts +7 -0
  77. package/dist/utils/expect-defined.js +11 -0
  78. package/dist/utils/expect-defined.js.map +1 -0
  79. package/dist/utils/index.d.ts +8 -20
  80. package/dist/utils/index.js +120 -41
  81. package/dist/utils/index.js.map +1 -1
  82. package/dist/{wstack-paths-BQMvEllz.d.ts → wstack-paths-_lqjzErq.d.ts} +1 -1
  83. package/package.json +5 -1
  84. package/skills/tech-stack/SKILL.md +177 -0
  85. package/dist/memory-CEXuo7sz.d.ts +0 -16
  86. package/dist/secret-scrubber-3MHDDAtm.d.ts +0 -6
  87. package/dist/task-graph-CikNdRTG.d.ts +0 -161
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
  };
@@ -1713,13 +1708,25 @@ function round4(n) {
1713
1708
  return Math.round(n * 1e4) / 1e4;
1714
1709
  }
1715
1710
 
1716
- // src/utils/token-estimate.ts
1717
- 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) {
1718
1723
  if (value === null || value === void 0) {
1719
- throw new Error("Expected value to be defined");
1724
+ throw new Error(label ? `Expected ${label} to be defined` : "Expected value to be defined");
1720
1725
  }
1721
1726
  return value;
1722
1727
  }
1728
+
1729
+ // src/utils/token-estimate.ts
1723
1730
  var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
1724
1731
  var _cal = {
1725
1732
  ratio: 1,
@@ -1853,12 +1860,6 @@ function resetCalibration() {
1853
1860
  }
1854
1861
 
1855
1862
  // src/utils/message-invariants.ts
1856
- function expectDefined2(value) {
1857
- if (value === null || value === void 0) {
1858
- throw new Error("Expected value to be defined");
1859
- }
1860
- return value;
1861
- }
1862
1863
  function repairToolUseAdjacency(messages) {
1863
1864
  const removedToolUses = [];
1864
1865
  const removedToolResults = [];
@@ -1866,7 +1867,7 @@ function repairToolUseAdjacency(messages) {
1866
1867
  let changed = false;
1867
1868
  const out = [];
1868
1869
  for (let i = 0; i < messages.length; i++) {
1869
- const original = expectDefined2(messages[i]);
1870
+ const original = expectDefined(messages[i]);
1870
1871
  let msg = original;
1871
1872
  if (hasToolUse(msg)) {
1872
1873
  const nextIds = toolResultIds(messages[i + 1]);
@@ -3024,12 +3025,6 @@ Remember: your job is to make the user a better developer, not just to complete
3024
3025
  ];
3025
3026
 
3026
3027
  // src/types/context-window.ts
3027
- function expectDefined3(value) {
3028
- if (value === null || value === void 0) {
3029
- throw new Error("Expected value to be defined");
3030
- }
3031
- return value;
3032
- }
3033
3028
  var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
3034
3029
  var CONTEXT_WINDOW_MODES = Object.freeze([
3035
3030
  {
@@ -3086,7 +3081,7 @@ function isContextWindowModeId(id) {
3086
3081
  }
3087
3082
  function resolveContextWindowPolicy(config = {}, overrideMode) {
3088
3083
  const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
3089
- const mode = getContextWindowMode(requested) ?? expectDefined3(getContextWindowMode(DEFAULT_CONTEXT_WINDOW_MODE_ID));
3084
+ const mode = getContextWindowMode(requested) ?? expectDefined(getContextWindowMode(DEFAULT_CONTEXT_WINDOW_MODE_ID));
3090
3085
  if (mode.id !== DEFAULT_CONTEXT_WINDOW_MODE_ID) {
3091
3086
  return mode;
3092
3087
  }
@@ -3566,12 +3561,6 @@ function sizeSignals(toolName, content) {
3566
3561
  }
3567
3562
 
3568
3563
  // src/execution/tool-executor.ts
3569
- function expectDefined4(value) {
3570
- if (value === null || value === void 0) {
3571
- throw new Error("Expected value to be defined");
3572
- }
3573
- return value;
3574
- }
3575
3564
  var ToolExecutor = class {
3576
3565
  constructor(registry, opts) {
3577
3566
  this.registry = registry;
@@ -3978,7 +3967,7 @@ function hasMalformedArguments(input) {
3978
3967
  function extractMalformedRaw(input) {
3979
3968
  if (!hasMalformedArguments(input)) return void 0;
3980
3969
  const obj = input;
3981
- const value = obj[expectDefined4(Object.keys(obj)[0])];
3970
+ const value = obj[expectDefined(Object.keys(obj)[0])];
3982
3971
  if (value === void 0 || value === null) return void 0;
3983
3972
  if (typeof value === "string") return value;
3984
3973
  try {
@@ -4025,12 +4014,6 @@ function compileUserRegex(pattern, flags) {
4025
4014
  }
4026
4015
 
4027
4016
  // src/storage/session-reader.ts
4028
- function expectDefined5(value) {
4029
- if (value === null || value === void 0) {
4030
- throw new Error("Expected value to be defined");
4031
- }
4032
- return value;
4033
- }
4034
4017
  var DefaultSessionReader = class {
4035
4018
  store;
4036
4019
  constructor(opts) {
@@ -4092,7 +4075,7 @@ var DefaultSessionReader = class {
4092
4075
  continue;
4093
4076
  }
4094
4077
  for (let i = 0; i < data.events.length; i++) {
4095
- const ev = expectDefined5(data.events[i]);
4078
+ const ev = expectDefined(data.events[i]);
4096
4079
  if (allowedTypes && !allowedTypes.has(ev.type)) continue;
4097
4080
  const text = eventText(ev);
4098
4081
  if (text === null) continue;
@@ -4459,13 +4442,116 @@ function formatTodosList(todos) {
4459
4442
  return lines.join("\n");
4460
4443
  }
4461
4444
 
4462
- // src/utils/glob-match.ts
4463
- function expectDefined6(value) {
4464
- if (value === null || value === void 0) {
4465
- throw new Error("Expected value to be defined");
4445
+ // src/utils/task-format.ts
4446
+ function computeTaskItemProgress(tasks) {
4447
+ let completed = 0;
4448
+ let pending = 0;
4449
+ let inProgress = 0;
4450
+ let blocked = 0;
4451
+ let failed = 0;
4452
+ let review = 0;
4453
+ let estimatedHours = 0;
4454
+ let actualHours = 0;
4455
+ for (const t2 of tasks) {
4456
+ switch (t2.status) {
4457
+ case "completed":
4458
+ completed++;
4459
+ break;
4460
+ case "pending":
4461
+ pending++;
4462
+ break;
4463
+ case "in_progress":
4464
+ inProgress++;
4465
+ break;
4466
+ case "blocked":
4467
+ blocked++;
4468
+ break;
4469
+ case "failed":
4470
+ failed++;
4471
+ break;
4472
+ case "review":
4473
+ review++;
4474
+ break;
4475
+ }
4476
+ estimatedHours += t2.estimateHours ?? 0;
4466
4477
  }
4467
- return value;
4478
+ return {
4479
+ total: tasks.length,
4480
+ pending,
4481
+ inProgress,
4482
+ blocked,
4483
+ failed,
4484
+ review,
4485
+ completed,
4486
+ percentComplete: tasks.length > 0 ? Math.round(completed / tasks.length * 100) : 0,
4487
+ estimatedHours,
4488
+ actualHours
4489
+ };
4490
+ }
4491
+ var STATUS_ICON = {
4492
+ pending: "\u25CB",
4493
+ in_progress: "\u25D0",
4494
+ blocked: "\u2298",
4495
+ failed: "\u2717",
4496
+ review: "\u25D1",
4497
+ completed: "\u25CF"
4498
+ };
4499
+ var PRIORITY_ICON = {
4500
+ critical: "\u{1F534}",
4501
+ high: "\u{1F7E0}",
4502
+ medium: "\u{1F7E1}",
4503
+ low: "\u{1F7E2}"
4504
+ };
4505
+ var TYPE_ICON = {
4506
+ feature: "\u26A1",
4507
+ bugfix: "\u{1F41B}",
4508
+ refactor: "\u267B\uFE0F",
4509
+ docs: "\u{1F4DD}",
4510
+ test: "\u{1F9EA}",
4511
+ chore: "\u{1F527}"
4512
+ };
4513
+ function formatTaskProgress(tasks) {
4514
+ const p = computeTaskItemProgress(tasks);
4515
+ if (p.total === 0) return "No tasks.";
4516
+ const barWidth = 24;
4517
+ const filled = Math.round(p.percentComplete / 100 * barWidth);
4518
+ const empty = barWidth - filled;
4519
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
4520
+ return [
4521
+ `${color.bold("Tasks")} [${bar}] ${p.percentComplete}%`,
4522
+ ` ${color.green("\u25CF")} ${p.completed} done \u2502 ${color.yellow("\u25D0")} ${p.inProgress} active \u2502 ${color.dim("\u25CB")} ${p.pending} pending \u2502 \u2298 ${p.blocked} blocked \u2502 \u2717 ${p.failed} failed`,
4523
+ p.estimatedHours > 0 ? ` ${color.dim(`est. ${p.estimatedHours}h`)}` : ""
4524
+ ].filter(Boolean).join("\n");
4525
+ }
4526
+ function formatTaskList(tasks) {
4527
+ if (tasks.length === 0) return "No tasks.";
4528
+ const order = ["in_progress", "blocked", "review", "pending", "failed", "completed"];
4529
+ const groups = /* @__PURE__ */ new Map();
4530
+ for (const t2 of tasks) {
4531
+ const list = groups.get(t2.status) ?? [];
4532
+ list.push(t2);
4533
+ groups.set(t2.status, list);
4534
+ }
4535
+ const lines = [];
4536
+ lines.push(color.dim(`Tasks (${tasks.length} total):`));
4537
+ for (const status of order) {
4538
+ const group = groups.get(status);
4539
+ if (!group || group.length === 0) continue;
4540
+ const icon = STATUS_ICON[status];
4541
+ lines.push(` ${icon} ${status.toUpperCase()} (${group.length})`);
4542
+ for (const t2 of group) {
4543
+ const prio = PRIORITY_ICON[t2.priority];
4544
+ const type = TYPE_ICON[t2.type];
4545
+ const deps = t2.dependsOn && t2.dependsOn.length > 0 ? ` ${color.dim("\u2190")} ${color.dim(t2.dependsOn.map((d) => d.slice(0, 8)).join(", "))}` : "";
4546
+ const who = t2.assignee ? ` ${color.dim(`@${t2.assignee}`)}` : "";
4547
+ const hrs = t2.estimateHours ? ` ${color.dim(`${t2.estimateHours}h`)}` : "";
4548
+ lines.push(` ${type} ${prio} ${t2.title}${deps}${who}${hrs}`);
4549
+ }
4550
+ }
4551
+ return lines.join("\n");
4468
4552
  }
4553
+
4554
+ // src/utils/glob-match.ts
4469
4555
  function escapeRegex(s) {
4470
4556
  return s.replace(/[.+^${}()|\\]/g, "\\$&");
4471
4557
  }
@@ -4477,7 +4563,7 @@ function getCachedGlob(pattern) {
4477
4563
  if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
4478
4564
  const keys = [...COMPILED_GLOB_CACHE.keys()];
4479
4565
  for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
4480
- COMPILED_GLOB_CACHE.delete(expectDefined6(keys[i]));
4566
+ COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
4481
4567
  }
4482
4568
  }
4483
4569
  const re = compileGlob(pattern);
@@ -4837,20 +4923,6 @@ function buildChildEnv(optsOrSessionId) {
4837
4923
  function sleep(ms) {
4838
4924
  return new Promise((resolve13) => setTimeout(resolve13, ms));
4839
4925
  }
4840
-
4841
- // src/utils/expect-defined.ts
4842
- function expectDefined7(value) {
4843
- if (value === null || value === void 0) {
4844
- throw new Error("Expected value to be defined");
4845
- }
4846
- return value;
4847
- }
4848
- function expectDefined8(value) {
4849
- if (value === null || value === void 0) {
4850
- throw new Error("Expected value to be defined");
4851
- }
4852
- return value;
4853
- }
4854
4926
  var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
4855
4927
  var IS_WINDOWS = process.platform === "win32";
4856
4928
  var SEP = IS_WINDOWS ? "\\" : "/";
@@ -4864,7 +4936,7 @@ function globToRegex(pat) {
4864
4936
  let i = 0;
4865
4937
  let re = "^";
4866
4938
  while (i < pat.length) {
4867
- const c = expectDefined8(pat[i]);
4939
+ const c = expectDefined(pat[i]);
4868
4940
  if (c === "*") {
4869
4941
  if (pat[i + 1] === "*") {
4870
4942
  re += ".*";
@@ -4903,7 +4975,7 @@ function globToRegex(pat) {
4903
4975
  }
4904
4976
  function baseDir(pat) {
4905
4977
  let i = pat.length - 1;
4906
- while (i >= 0 && !GLOB_CHARS.has(expectDefined8(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
4978
+ while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
4907
4979
  const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
4908
4980
  return cut < 0 ? "." : pat.slice(0, cut);
4909
4981
  }
@@ -4968,12 +5040,6 @@ async function expandGlob(pattern) {
4968
5040
  }
4969
5041
 
4970
5042
  // src/utils/json-repair.ts
4971
- function expectDefined9(value) {
4972
- if (value === null || value === void 0) {
4973
- throw new Error("Expected value to be defined");
4974
- }
4975
- return value;
4976
- }
4977
5043
  function completePartialObject(s) {
4978
5044
  if (!s.trim().startsWith("{")) return s;
4979
5045
  if (tryParse(s).ok) return s;
@@ -4985,7 +5051,7 @@ function completePartialObject(s) {
4985
5051
  let contentEnd = 0;
4986
5052
  let stringBraceDepth = 0;
4987
5053
  for (let i = 0; i < s.length; i++) {
4988
- const ch = expectDefined9(s[i]);
5054
+ const ch = expectDefined(s[i]);
4989
5055
  if (inString) {
4990
5056
  contentEnd = i + 1;
4991
5057
  if (escaped) {
@@ -5081,12 +5147,6 @@ function mergeCustomModelDefs(providerCustomModels, configModels) {
5081
5147
 
5082
5148
  // src/storage/session-store.ts
5083
5149
  init_atomic_write();
5084
- function expectDefined10(value) {
5085
- if (value === null || value === void 0) {
5086
- throw new Error("Expected value to be defined");
5087
- }
5088
- return value;
5089
- }
5090
5150
  function sanitizeModel(model) {
5091
5151
  return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
5092
5152
  }
@@ -5797,7 +5857,7 @@ var FileSessionWriter = class {
5797
5857
  let targetCheckpointLine = -1;
5798
5858
  let afterTarget = false;
5799
5859
  for (let i = 0; i < lines.length; i++) {
5800
- const line = expectDefined10(lines[i]);
5860
+ const line = expectDefined(lines[i]);
5801
5861
  if (!line.trim()) continue;
5802
5862
  let event;
5803
5863
  try {
@@ -6092,12 +6152,224 @@ function mergeAdjacentText(blocks) {
6092
6152
  }
6093
6153
  return out;
6094
6154
  }
6155
+ init_atomic_write();
6156
+ var TYPE_PRIORITY_RE = /^\[(\w+)\|(\w+)\]\s+/;
6157
+ var TAG_RE = /#([\w-]+)/g;
6158
+ function formatMetadata(entry) {
6159
+ const parts = [];
6160
+ if (entry.type && entry.priority) {
6161
+ parts.push(`[${entry.type}|${entry.priority}]`);
6162
+ } else if (entry.type) {
6163
+ parts.push(`[${entry.type}]`);
6164
+ } else if (entry.priority) {
6165
+ parts.push(`[${entry.priority}]`);
6166
+ }
6167
+ if (entry.tags && entry.tags.length > 0) {
6168
+ parts.push(entry.tags.map((t2) => `#${t2}`).join(" "));
6169
+ }
6170
+ return parts.length > 0 ? ` ${parts.join(" ")}` : "";
6171
+ }
6172
+ function lineToEntry(line, scope) {
6173
+ const trimmed = line.trim();
6174
+ if (!trimmed.startsWith("- [")) return null;
6175
+ const tsMatch = trimmed.match(/^-\s*\[([^\]]+)\]/);
6176
+ if (!tsMatch) return null;
6177
+ const ts = tsMatch[1] ?? "";
6178
+ let rest = trimmed.slice(tsMatch[0].length).trim();
6179
+ let type;
6180
+ let priority;
6181
+ const tpMatch = rest.match(TYPE_PRIORITY_RE);
6182
+ if (tpMatch) {
6183
+ const a = tpMatch[1] ?? "";
6184
+ const b = tpMatch[2] ?? "";
6185
+ if (isMemoryType(a)) {
6186
+ type = a;
6187
+ priority = isPriority(b) ? b : void 0;
6188
+ } else if (isPriority(a)) {
6189
+ priority = a;
6190
+ }
6191
+ rest = rest.slice(tpMatch[0].length).trim();
6192
+ }
6193
+ const idMatch = rest.match(/^mem_\d+_\w+\s+/);
6194
+ let text;
6195
+ if (idMatch) {
6196
+ text = rest.slice(idMatch[0].length).trim();
6197
+ } else {
6198
+ text = rest.trim();
6199
+ }
6200
+ const tags = [];
6201
+ let tagMatch;
6202
+ TAG_RE.lastIndex = 0;
6203
+ while ((tagMatch = TAG_RE.exec(text)) !== null) {
6204
+ tags.push(tagMatch[1] ?? "");
6205
+ }
6206
+ const cleanText = text.replace(TAG_RE, "").replace(/\s{2,}/g, " ").trim();
6207
+ if (!cleanText) return null;
6208
+ return {
6209
+ scope,
6210
+ text: cleanText,
6211
+ ts,
6212
+ type,
6213
+ priority,
6214
+ tags: tags.length > 0 ? tags : void 0
6215
+ };
6216
+ }
6217
+ function isMemoryType(s) {
6218
+ return s in MEMORY_TYPE_LABELS;
6219
+ }
6220
+ function isPriority(s) {
6221
+ return s === "critical" || s === "high" || s === "medium" || s === "low";
6222
+ }
6223
+ var FileMemoryBackend = class {
6224
+ kind = "file";
6225
+ files;
6226
+ constructor(opts) {
6227
+ this.files = {
6228
+ "project-agents": opts.paths.inProjectAgentsFile,
6229
+ "project-memory": opts.paths.projectMemory,
6230
+ "user-memory": opts.paths.globalMemory
6231
+ };
6232
+ }
6233
+ resolveFile(filePath, scope) {
6234
+ return filePath || this.files[scope];
6235
+ }
6236
+ async remember(scope, entry, filePath) {
6237
+ const file = this.resolveFile(filePath, scope);
6238
+ await ensureDir(path6.dirname(file));
6239
+ let existing = "";
6240
+ try {
6241
+ existing = await fsp3.readFile(file, "utf8");
6242
+ } catch {
6243
+ }
6244
+ const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
6245
+ const meta = formatMetadata(entry);
6246
+ const line = `
6247
+ - [${entry.ts}] ${id}${meta} ${entry.text.replace(/\n/g, " ")}
6248
+ `;
6249
+ const next = existing.trim() ? existing.replace(/\n+$/, "") + line : `# WrongStack Memory
6250
+ ${line}`;
6251
+ await atomicWrite(file, next);
6252
+ }
6253
+ async forget(scope, query, filePath) {
6254
+ const file = this.resolveFile(filePath, scope);
6255
+ let existing;
6256
+ try {
6257
+ existing = await fsp3.readFile(file, "utf8");
6258
+ } catch {
6259
+ return 0;
6260
+ }
6261
+ const needle = query.toLowerCase();
6262
+ const idMatcher = /mem_\d+_\w+/;
6263
+ let removed = 0;
6264
+ const lines = existing.split("\n").filter((line) => {
6265
+ const trimmed = line.trim();
6266
+ if (!trimmed.startsWith("- ")) return true;
6267
+ if (idMatcher.test(query)) {
6268
+ const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
6269
+ if (entryIdMatch && entryIdMatch[0] === query) {
6270
+ removed++;
6271
+ return false;
6272
+ }
6273
+ }
6274
+ if (trimmed.toLowerCase().includes(needle)) {
6275
+ removed++;
6276
+ return false;
6277
+ }
6278
+ return true;
6279
+ });
6280
+ if (removed > 0) {
6281
+ if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
6282
+ await atomicWrite(file, "");
6283
+ } else {
6284
+ await atomicWrite(file, lines.join("\n"));
6285
+ }
6286
+ }
6287
+ return removed;
6288
+ }
6289
+ async readAll(scope, filePath) {
6290
+ const file = this.resolveFile(filePath, scope);
6291
+ try {
6292
+ return await fsp3.readFile(file, "utf8");
6293
+ } catch {
6294
+ return "";
6295
+ }
6296
+ }
6297
+ async list(scope, filePath, limit) {
6298
+ const raw = await this.readAll(scope, filePath);
6299
+ if (!raw.trim()) return [];
6300
+ const entries = parseEntries(raw, scope);
6301
+ return limit ? entries.slice(0, limit) : entries;
6302
+ }
6303
+ async search(scope, query, filePath, limit) {
6304
+ const entries = await this.list(scope, filePath);
6305
+ const needle = query.toLowerCase().split(/\s+/);
6306
+ const scored = entries.map((e) => {
6307
+ const words = e.text.toLowerCase().split(/\s+/);
6308
+ let score = 0;
6309
+ for (const n of needle) {
6310
+ if (words.some((w) => w.includes(n))) score += 1;
6311
+ if (e.tags?.some((t2) => t2.toLowerCase().includes(n))) score += 2;
6312
+ }
6313
+ return { entry: e, score };
6314
+ });
6315
+ scored.sort((a, b) => b.score - a.score);
6316
+ const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
6317
+ return limit ? matched.slice(0, limit) : matched;
6318
+ }
6319
+ async clear(scope, filePath) {
6320
+ const file = this.resolveFile(filePath, scope);
6321
+ await atomicWrite(file, "");
6322
+ }
6323
+ async consolidate(scope, filePath) {
6324
+ const file = this.resolveFile(filePath, scope);
6325
+ let existing;
6326
+ try {
6327
+ existing = await fsp3.readFile(file, "utf8");
6328
+ } catch {
6329
+ return 0;
6330
+ }
6331
+ const seen = /* @__PURE__ */ new Set();
6332
+ let removed = 0;
6333
+ const lines = existing.split("\n").filter((line) => {
6334
+ const trimmed = line.trim();
6335
+ if (!trimmed.startsWith("- ")) return true;
6336
+ const norm = trimmed.replace(/\[[^\]]+\]/g, "").replace(/\bmem_\d+_\w+\s*/, "").replace(/#[\w-]+/g, "").replace(/\s+/g, " ").trim().toLowerCase();
6337
+ if (seen.has(norm)) {
6338
+ removed++;
6339
+ return false;
6340
+ }
6341
+ seen.add(norm);
6342
+ return true;
6343
+ });
6344
+ const next = lines.join("\n");
6345
+ const backup = `${file}.bak.${Date.now()}`;
6346
+ try {
6347
+ await fsp3.copyFile(file, backup);
6348
+ } catch {
6349
+ }
6350
+ try {
6351
+ await atomicWrite(file, next);
6352
+ } catch {
6353
+ return 0;
6354
+ }
6355
+ return removed;
6356
+ }
6357
+ };
6358
+ function parseEntries(raw, scope = "project-memory") {
6359
+ const entries = [];
6360
+ for (const line of raw.split("\n")) {
6361
+ const entry = lineToEntry(line, scope);
6362
+ if (entry) entries.push(entry);
6363
+ }
6364
+ return entries.reverse();
6365
+ }
6095
6366
 
6096
6367
  // src/storage/memory-store.ts
6097
- init_atomic_write();
6098
6368
  var MAX_BYTES_TOTAL = 32e3;
6099
6369
  var DefaultMemoryStore = class {
6100
6370
  files;
6371
+ events;
6372
+ backend;
6101
6373
  /**
6102
6374
  * Per-scope serialization queue. `remember` / `forget` / `consolidate` /
6103
6375
  * `clear` are read-modify-write against a single file; without a lock,
@@ -6114,12 +6386,29 @@ var DefaultMemoryStore = class {
6114
6386
  writeChain = /* @__PURE__ */ new Map();
6115
6387
  /** Last write error per scope — surfaced as warnings on the next readAll(). */
6116
6388
  writeErrors = /* @__PURE__ */ new Map();
6389
+ /**
6390
+ * When the global root is a temporary directory (opencode, CI sandboxes),
6391
+ * memory files are also mirrored to the project tree so they survive
6392
+ * session cleanup. The primary path stays in the temp root for isolation;
6393
+ * this backup ensures memory is never lost.
6394
+ */
6395
+ persistBackup;
6396
+ backupDir;
6117
6397
  constructor(opts) {
6118
6398
  this.files = {
6119
6399
  "project-agents": opts.paths.inProjectAgentsFile,
6120
6400
  "project-memory": opts.paths.projectMemory,
6121
6401
  "user-memory": opts.paths.globalMemory
6122
6402
  };
6403
+ this.events = opts.events;
6404
+ this.backend = opts.backend ?? new FileMemoryBackend({ paths: opts.paths });
6405
+ const root = opts.paths.globalRoot.toLowerCase();
6406
+ this.persistBackup = /[/\\](tmp|temp|cache)[/\\]/.test(root);
6407
+ this.backupDir = this.persistBackup ? opts.paths.inProjectAgentsFile.replace(/AGENTS\.md$/, "memory-persist") : "";
6408
+ }
6409
+ /** Expose the backend for plugin introspection and advanced queries. */
6410
+ getBackend() {
6411
+ return this.backend;
6123
6412
  }
6124
6413
  async runSerialized(scope, work) {
6125
6414
  const prior = this.writeChain.get(scope) ?? Promise.resolve();
@@ -6146,7 +6435,7 @@ var DefaultMemoryStore = class {
6146
6435
  if (writeErr2) {
6147
6436
  parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
6148
6437
  }
6149
- const body = await this.read(scope);
6438
+ const body = await this.backend.readAll(scope, this.files[scope]);
6150
6439
  if (body.trim()) parts.push(`## ${labelOf(scope)}
6151
6440
 
6152
6441
  ${body.trim()}`);
@@ -6154,117 +6443,214 @@ ${body.trim()}`);
6154
6443
  return parts.join("\n\n");
6155
6444
  }
6156
6445
  async read(scope) {
6157
- try {
6158
- return await fsp3.readFile(this.files[scope], "utf8");
6159
- } catch {
6160
- return "";
6446
+ return this.backend.readAll(scope, this.files[scope]);
6447
+ }
6448
+ /**
6449
+ * List entries from a scope, newest first. Delegates to the backend
6450
+ * so graph/semantic backends can return enriched or filtered results.
6451
+ */
6452
+ async list(scope = "project-memory", limit) {
6453
+ return this.backend.list(scope, this.files[scope], limit);
6454
+ }
6455
+ /**
6456
+ * Find memories related to the given text via graph traversal.
6457
+ * Falls back to content search when no graph backend is available.
6458
+ */
6459
+ async findRelated(text, scope = "project-memory", limit = 5) {
6460
+ if (this.backend.findRelated) {
6461
+ return this.backend.findRelated(scope, this.files[scope], text, limit);
6161
6462
  }
6463
+ return this.search(text, scope, limit);
6162
6464
  }
6163
- async remember(text, scope = "project-memory") {
6465
+ async search(query, scope = "project-memory", limit) {
6466
+ return this.backend.search(scope, query, this.files[scope], limit);
6467
+ }
6468
+ async remember(text, scope = "project-memory", metadata) {
6469
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
6164
6470
  return this.runSerialized(scope, async () => {
6165
- const file = this.files[scope];
6166
- await ensureDir(path6.dirname(file));
6167
- let existing = "";
6168
- try {
6169
- existing = await fsp3.readFile(file, "utf8");
6170
- } catch {
6171
- }
6172
- const ts = (/* @__PURE__ */ new Date()).toISOString();
6173
- const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
6174
- const entry = `
6175
- - [${ts}] ${id} ${text.replace(/\n/g, " ")}
6176
- `;
6177
- const next = existing.trim() ? existing.replace(/\n+$/, "") + entry : `# WrongStack Memory
6178
- ${entry}`;
6179
- await atomicWrite(file, next);
6180
- const buf = Buffer.byteLength(next, "utf8");
6181
- if (buf > MAX_BYTES_TOTAL) {
6182
- await this.consolidateUnsafe(scope);
6471
+ const entry = { scope, text, ts, ...metadata };
6472
+ await this.backend.remember(scope, entry, this.files[scope]);
6473
+ const raw = await this.backend.readAll(scope, this.files[scope]);
6474
+ if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
6475
+ const removed = await this.backend.consolidate(scope, this.files[scope]);
6476
+ if (removed > 0) {
6477
+ this.events?.emit("memory.consolidated", {
6478
+ scope,
6479
+ removed
6480
+ });
6481
+ }
6183
6482
  }
6483
+ await this.mirrorBackup(scope);
6484
+ this.events?.emit("memory.remembered", {
6485
+ scope,
6486
+ text,
6487
+ ts,
6488
+ type: entry.type,
6489
+ tags: entry.tags,
6490
+ priority: entry.priority
6491
+ });
6184
6492
  });
6185
6493
  }
6186
- async forget(query, scope = "project-memory") {
6187
- return this.runSerialized(scope, async () => this.forgetUnsafe(query, scope));
6188
- }
6189
- async forgetUnsafe(query, scope) {
6190
- const file = this.files[scope];
6191
- let existing;
6192
- try {
6193
- existing = await fsp3.readFile(file, "utf8");
6194
- } catch {
6195
- return 0;
6196
- }
6197
- const needle = query.toLowerCase();
6198
- const idMatcher = /mem_\d+_\w+/;
6199
- let removed = 0;
6200
- const lines = existing.split("\n").filter((line) => {
6201
- const trimmed = line.trim();
6202
- if (!trimmed.startsWith("- ")) return true;
6203
- if (idMatcher.test(query)) {
6204
- const afterBracket = trimmed.indexOf("] ");
6205
- if (afterBracket !== -1) {
6206
- const afterTs = trimmed.slice(afterBracket + 2);
6207
- const entryIdMatch = /^mem_\d+_\w+/.exec(afterTs);
6208
- if (entryIdMatch && entryIdMatch[0] === query) {
6209
- removed++;
6210
- return false;
6211
- }
6494
+ /**
6495
+ * Score and rank memories by relevance to the current context.
6496
+ * Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
6497
+ */
6498
+ async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
6499
+ const all = await this.list(scope);
6500
+ if (all.length === 0) return [];
6501
+ const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
6502
+ const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
6503
+ const toolWords = (ctx.toolNames ?? []).flatMap((t2) => t2.toLowerCase().split("_"));
6504
+ const now = Date.now();
6505
+ const scored = [];
6506
+ for (const entry of all) {
6507
+ let score = 0;
6508
+ const reasons = [];
6509
+ const textLower = entry.text.toLowerCase();
6510
+ const tagsLower = (entry.tags ?? []).map((t2) => t2.toLowerCase());
6511
+ let taskHits = 0;
6512
+ for (const w of taskWords) {
6513
+ if (textLower.includes(w)) {
6514
+ taskHits++;
6515
+ score += 2;
6516
+ }
6517
+ if (tagsLower.some((t2) => t2.includes(w))) {
6518
+ taskHits++;
6519
+ score += 3;
6212
6520
  }
6213
6521
  }
6214
- if (trimmed.toLowerCase().includes(needle)) {
6215
- removed++;
6216
- return false;
6522
+ if (taskHits > 0) reasons.push(`task match (${taskHits})`);
6523
+ let skillHits = 0;
6524
+ for (const w of skillWords) {
6525
+ if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t2) => t2.includes(w)))) {
6526
+ skillHits++;
6527
+ }
6528
+ }
6529
+ score += skillHits;
6530
+ if (skillHits > 0) reasons.push(`skill match (${skillHits})`);
6531
+ for (const w of toolWords) {
6532
+ if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t2) => t2.includes(w)))) {
6533
+ score += 1;
6534
+ reasons.push(`tool mention: ${w}`);
6535
+ }
6536
+ }
6537
+ switch (entry.priority) {
6538
+ case "critical":
6539
+ score += 5;
6540
+ reasons.push("critical");
6541
+ break;
6542
+ case "high":
6543
+ score += 3;
6544
+ reasons.push("high priority");
6545
+ break;
6546
+ case "medium":
6547
+ score += 1;
6548
+ break;
6549
+ case "low":
6550
+ score -= 2;
6551
+ reasons.push("low priority");
6552
+ break;
6553
+ }
6554
+ switch (entry.type) {
6555
+ case "decision":
6556
+ score += 2;
6557
+ reasons.push("decision");
6558
+ break;
6559
+ case "convention":
6560
+ score += 2;
6561
+ reasons.push("convention");
6562
+ break;
6563
+ case "anti_pattern":
6564
+ score += 3;
6565
+ reasons.push("anti-pattern");
6566
+ break;
6567
+ case "preference":
6568
+ score += 1;
6569
+ reasons.push("preference");
6570
+ break;
6571
+ }
6572
+ const ageDays = (now - new Date(entry.ts).getTime()) / (1e3 * 60 * 60 * 24);
6573
+ if (ageDays < 1) score += 1;
6574
+ else if (ageDays > 30) score -= 1;
6575
+ if (entry.confidence !== void 0 && entry.confidence < 0.5) {
6576
+ score -= 2;
6577
+ reasons.push("low confidence");
6578
+ }
6579
+ if (entry.lastAccessed) {
6580
+ const hoursSinceAccess = (now - new Date(entry.lastAccessed).getTime()) / (1e3 * 60 * 60);
6581
+ if (hoursSinceAccess < 1) score -= 1;
6582
+ }
6583
+ if (score > 0) {
6584
+ scored.push({
6585
+ ...entry,
6586
+ score,
6587
+ matchReason: reasons.join(", ") || "keyword match"
6588
+ });
6217
6589
  }
6218
- return true;
6219
- });
6220
- if (removed > 0) {
6221
- await atomicWrite(file, lines.join("\n"));
6222
6590
  }
6223
- return removed;
6591
+ scored.sort((a, b) => b.score - a.score);
6592
+ const threshold = 2;
6593
+ const relevant = scored.filter(
6594
+ (s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
6595
+ );
6596
+ return relevant.slice(0, Math.min(limit, 15));
6224
6597
  }
6225
- async consolidate(scope) {
6226
- return this.runSerialized(scope, async () => this.consolidateUnsafe(scope));
6598
+ async forget(query, scope = "project-memory") {
6599
+ return this.runSerialized(scope, async () => {
6600
+ const removed = await this.backend.forget(scope, query, this.files[scope]);
6601
+ if (removed > 0) {
6602
+ this.events?.emit("memory.forgotten", {
6603
+ scope,
6604
+ query,
6605
+ removed
6606
+ });
6607
+ await this.mirrorBackup(scope);
6608
+ }
6609
+ return removed;
6610
+ });
6227
6611
  }
6228
- async consolidateUnsafe(scope) {
6229
- const file = this.files[scope];
6230
- let existing;
6231
- try {
6232
- existing = await fsp3.readFile(file, "utf8");
6233
- } catch {
6234
- return;
6235
- }
6236
- const seen = /* @__PURE__ */ new Set();
6237
- const lines = existing.split("\n").filter((line) => {
6238
- const trimmed = line.trim();
6239
- if (!trimmed.startsWith("- ")) return true;
6240
- const norm = trimmed.replace(/\[[^\]]+\]/, "").replace(/\bmem_\d+_\w+\s*/, "").trim().toLowerCase();
6241
- if (seen.has(norm)) return false;
6242
- seen.add(norm);
6243
- return true;
6612
+ async consolidate(scope) {
6613
+ return this.runSerialized(scope, async () => {
6614
+ const removed = await this.backend.consolidate(scope, this.files[scope]);
6615
+ if (removed > 0) {
6616
+ this.events?.emit("memory.consolidated", {
6617
+ scope,
6618
+ removed
6619
+ });
6620
+ await this.mirrorBackup(scope);
6621
+ }
6244
6622
  });
6245
- const next = lines.join("\n");
6246
- const backup = `${file}.bak.${Date.now()}`;
6247
- try {
6248
- await fsp3.copyFile(file, backup);
6249
- } catch {
6250
- }
6251
- try {
6252
- await atomicWrite(file, next);
6253
- } catch {
6254
- return;
6255
- }
6256
6623
  }
6257
6624
  async clear(scope) {
6258
6625
  if (scope) {
6259
- await this.runSerialized(scope, async () => atomicWrite(this.files[scope], ""));
6626
+ await this.runSerialized(scope, async () => {
6627
+ await this.backend.clear(scope, this.files[scope]);
6628
+ this.events?.emit("memory.cleared", { scope });
6629
+ await this.mirrorBackup(scope);
6630
+ });
6260
6631
  return;
6261
6632
  }
6262
6633
  await Promise.all(
6263
6634
  ["project-agents", "project-memory", "user-memory"].map(
6264
- (s) => this.runSerialized(s, async () => atomicWrite(this.files[s], ""))
6635
+ (s) => this.runSerialized(s, async () => {
6636
+ await this.backend.clear(s, this.files[s]);
6637
+ this.events?.emit("memory.cleared", { scope: s });
6638
+ await this.mirrorBackup(s);
6639
+ })
6265
6640
  )
6266
6641
  );
6267
6642
  }
6643
+ /** Mirror current memory content to the persistent backup directory. */
6644
+ async mirrorBackup(scope) {
6645
+ if (!this.persistBackup || scope === "project-agents") return;
6646
+ try {
6647
+ const content = await this.backend.readAll(scope, this.files[scope]);
6648
+ const { writeFile: writeFile11, mkdir: mkdir17 } = await import('fs/promises');
6649
+ await mkdir17(this.backupDir, { recursive: true });
6650
+ await writeFile11(`${this.backupDir}/${scope}.md`, content, "utf8");
6651
+ } catch {
6652
+ }
6653
+ }
6268
6654
  };
6269
6655
  function labelOf(scope) {
6270
6656
  switch (scope) {
@@ -7085,7 +7471,10 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
7085
7471
  let pending = null;
7086
7472
  let writeChain = Promise.resolve();
7087
7473
  const enqueueWrite = (todos) => {
7088
- writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos));
7474
+ writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos)).catch((err) => {
7475
+ const msg = err instanceof Error ? err.message : String(err);
7476
+ console.error(`[TodosCheckpoint] save failed for session ${sessionId}: ${msg}`);
7477
+ });
7089
7478
  return writeChain;
7090
7479
  };
7091
7480
  const flush = () => {
@@ -7389,7 +7778,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
7389
7778
  }
7390
7779
  const lock = {
7391
7780
  pid: processId,
7392
- hostname: __require("os").hostname(),
7781
+ hostname: hostname(),
7393
7782
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
7394
7783
  };
7395
7784
  await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
@@ -8611,12 +9000,6 @@ var IntelligentCompactor = class {
8611
9000
  };
8612
9001
 
8613
9002
  // src/models/llm-selector.ts
8614
- function expectDefined11(value) {
8615
- if (value === null || value === void 0) {
8616
- throw new Error("Expected value to be defined");
8617
- }
8618
- return value;
8619
- }
8620
9003
  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.
8621
9004
 
8622
9005
  Output a JSON object with this structure:
@@ -8657,7 +9040,7 @@ function formatMessages(messages, maxChars = 8e3) {
8657
9040
  const lines = [];
8658
9041
  let used = 0;
8659
9042
  for (let i = 0; i < messages.length; i++) {
8660
- const m = expectDefined11(messages[i]);
9043
+ const m = expectDefined(messages[i]);
8661
9044
  const role = m.role.padEnd(10, " ");
8662
9045
  let text;
8663
9046
  if (typeof m.content === "string") {
@@ -8722,7 +9105,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
8722
9105
  let tokenCount = 0;
8723
9106
  let startIdx = 0;
8724
9107
  for (let i = messages.length - 1; i >= 0; i--) {
8725
- const m = expectDefined11(messages[i]);
9108
+ const m = expectDefined(messages[i]);
8726
9109
  const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
8727
9110
  (acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
8728
9111
  0
@@ -9405,6 +9788,14 @@ function emptyGoal(goal) {
9405
9788
  journal: []
9406
9789
  };
9407
9790
  }
9791
+ function setProgress(goal, progress, note) {
9792
+ const clamped = Math.min(100, Math.max(0, progress));
9793
+ return {
9794
+ ...goal,
9795
+ progress: clamped,
9796
+ progressNote: note ?? clamped + "% complete"
9797
+ };
9798
+ }
9408
9799
  function appendJournal(goal, entry) {
9409
9800
  const iteration = goal.iterations + 1;
9410
9801
  const at = (/* @__PURE__ */ new Date()).toISOString();
@@ -9433,34 +9824,110 @@ function summarizeUsage(goal) {
9433
9824
  }
9434
9825
  return { totalCostUsd, totalInputTokens, totalOutputTokens, iterationsWithUsage };
9435
9826
  }
9827
+ var DOLLAR = "$";
9436
9828
  function formatGoal(goal, journalLimit = 10) {
9437
9829
  const lines = [];
9438
- lines.push(`Goal: ${goal.goal}`);
9439
- lines.push(`Set: ${goal.setAt}`);
9440
- lines.push(`Last activity: ${goal.lastActivityAt}`);
9441
- lines.push(`Iterations: ${goal.iterations}`);
9830
+ const displayGoal = goal.refinedGoal || goal.goal;
9831
+ lines.push(color.bold("Goal") + ": " + displayGoal);
9832
+ if (goal.refinedGoal && goal.refinedGoal !== goal.goal) {
9833
+ const snippet = goal.goal.length > 60 ? goal.goal.slice(0, 60) + "\u2026" : goal.goal;
9834
+ lines.push(color.dim(' (original: "' + snippet + '")'));
9835
+ }
9836
+ if (typeof goal.progress === "number") {
9837
+ const pct = Math.min(100, Math.max(0, Math.round(goal.progress)));
9838
+ const filled = Math.round(pct / 5);
9839
+ const empty = 20 - filled;
9840
+ const bar = color.green("\u2588".repeat(filled)) + color.dim("\u2591".repeat(empty));
9841
+ lines.push("Progress: " + bar + " " + color.bold(pct + "%"));
9842
+ if (goal.progressNote) {
9843
+ lines.push(" " + color.dim(goal.progressNote));
9844
+ }
9845
+ if (goal.progressTrend) {
9846
+ const trendIcon = goal.progressTrend === "accelerating" ? "\u{1F680}" : goal.progressTrend === "stalling" ? "\u26A0\uFE0F" : "\u27A1\uFE0F";
9847
+ lines.push(" Trend: " + trendIcon + " " + goal.progressTrend);
9848
+ }
9849
+ }
9850
+ if (goal.deliverables && goal.deliverables.length > 0) {
9851
+ lines.push("");
9852
+ lines.push(color.bold("Deliverables:"));
9853
+ for (const d of goal.deliverables) {
9854
+ const done = /^\[[x✓]\]|✅|\(done\)/i.test(d);
9855
+ const marker = done ? color.green("\u2713") : color.dim("\u25CB");
9856
+ lines.push(" " + marker + " " + d);
9857
+ }
9858
+ }
9859
+ lines.push("");
9860
+ lines.push("Set: " + goal.setAt);
9861
+ lines.push("Last activity: " + goal.lastActivityAt);
9862
+ lines.push("Iterations: " + goal.iterations);
9442
9863
  const stateLabel = goal.goalState ?? "active";
9443
- lines.push(`State: ${stateLabel}${goal.iterations > 0 ? ` (iteration #${goal.iterations})` : ""}`);
9444
- lines.push(`Engine: ${goal.engineState}`);
9864
+ lines.push("State: " + stateLabel + (goal.iterations > 0 ? " (iteration #" + goal.iterations + ")" : ""));
9865
+ lines.push("Engine: " + goal.engineState);
9445
9866
  const usage = summarizeUsage(goal);
9446
9867
  if (usage.iterationsWithUsage > 0) {
9447
- lines.push(
9448
- `Spent: $${usage.totalCostUsd.toFixed(4)} (in ${usage.totalInputTokens} / out ${usage.totalOutputTokens} tokens across ${usage.iterationsWithUsage} iterations)`
9449
- );
9868
+ const spent = "Spent: " + DOLLAR + usage.totalCostUsd.toFixed(4) + " (in " + usage.totalInputTokens + " / out " + usage.totalOutputTokens + " tokens across " + usage.iterationsWithUsage + " iterations)";
9869
+ lines.push(spent);
9450
9870
  }
9451
9871
  if (goal.journal.length > 0) {
9452
9872
  lines.push("");
9453
- lines.push(`Recent journal (last ${Math.min(journalLimit, goal.journal.length)}):`);
9873
+ lines.push("Recent journal (last " + Math.min(journalLimit, goal.journal.length) + "):");
9454
9874
  const tail = goal.journal.slice(-journalLimit);
9455
9875
  for (const e of tail) {
9456
9876
  const mark = e.status === "success" ? "\u2713" : e.status === "failure" ? "\u2717" : e.status === "aborted" ? "\u2298" : "\xB7";
9457
- const note = e.note ? ` \u2014 ${e.note}` : "";
9458
- const cost = typeof e.costUsd === "number" ? ` ($${e.costUsd.toFixed(4)})` : "";
9459
- lines.push(` #${e.iteration} ${mark} [${e.source}] ${e.task}${cost}${note}`);
9877
+ const note = e.note ? " \u2014 " + e.note : "";
9878
+ const cost = typeof e.costUsd === "number" ? " (" + DOLLAR + e.costUsd.toFixed(4) + ")" : "";
9879
+ lines.push(" #" + e.iteration + " " + mark + " [" + e.source + "] " + e.task + cost + note);
9460
9880
  }
9461
9881
  }
9462
9882
  return lines.join("\n");
9463
9883
  }
9884
+ function parseProgressFromText(text) {
9885
+ const re = /\[progress:\s*(\d{1,3})%\]\s*(?:[—\-]\s*(.+))?/i;
9886
+ const m = text.match(re);
9887
+ if (!m) return null;
9888
+ const progress = Math.min(100, Math.max(0, Number.parseInt(m[1], 10)));
9889
+ const note = m[2]?.trim() || void 0;
9890
+ return { progress, note };
9891
+ }
9892
+ function recordProgress(goal, progress, note) {
9893
+ const clamped = Math.min(100, Math.max(0, progress));
9894
+ const history = [...goal.progressHistory ?? [], { at: (/* @__PURE__ */ new Date()).toISOString(), progress: clamped, note }];
9895
+ const trimmed = history.length > 200 ? history.slice(-200) : history;
9896
+ return {
9897
+ ...goal,
9898
+ progress: clamped,
9899
+ progressNote: note ?? `${clamped}% complete`,
9900
+ progressHistory: trimmed,
9901
+ progressTrend: computeTrend(trimmed)
9902
+ };
9903
+ }
9904
+ var MAX_PROGRESS_HISTORY = 200;
9905
+ function computeTrend(history) {
9906
+ if (history.length < 3) return void 0;
9907
+ const recent = history.slice(-5);
9908
+ const deltas = [];
9909
+ for (let i = 1; i < recent.length; i++) {
9910
+ deltas.push(recent[i].progress - recent[i - 1].progress);
9911
+ }
9912
+ if (deltas.length < 2) return void 0;
9913
+ const avgDelta = deltas.reduce((a, b) => a + b, 0) / deltas.length;
9914
+ if (avgDelta > 2) return "accelerating";
9915
+ if (avgDelta < -1) return "stalling";
9916
+ return "steady";
9917
+ }
9918
+
9919
+ // src/execution/autonomy-brain.ts
9920
+ function formatDecisionSummary(decision, request) {
9921
+ const question = request.question.length > 80 ? request.question.slice(0, 77) + "\u2026" : request.question;
9922
+ if (decision.type === "deny") {
9923
+ return `\u{1F9E0} Brain: DENIED \u2014 "${question}" \u2192 ${decision.reason}`;
9924
+ }
9925
+ if (decision.type === "answer") {
9926
+ const action = decision.optionId ? `chose [${decision.optionId}]` : decision.text.length > 60 ? decision.text.slice(0, 57) + "\u2026" : decision.text;
9927
+ return `\u{1F9E0} Brain: DECIDED \u2014 "${question}" \u2192 ${action}`;
9928
+ }
9929
+ return `\u{1F9E0} Brain: ASKED HUMAN \u2014 "${question}"`;
9930
+ }
9464
9931
 
9465
9932
  // src/execution/eternal-autonomy.ts
9466
9933
  var execFileP = promisify(execFile);
@@ -9531,7 +9998,7 @@ var EternalAutonomyEngine = class {
9531
9998
  this.consecutiveFailures = 0;
9532
9999
  }
9533
10000
  if (this.stopRequested) break;
9534
- await sleep(this.opts.cycleGapMs ?? 1e3);
10001
+ await sleep(this.opts.cycleGapMs ?? 0);
9535
10002
  }
9536
10003
  } finally {
9537
10004
  this.state = "stopped";
@@ -9701,6 +10168,18 @@ var EternalAutonomyEngine = class {
9701
10168
  this.stopRequested = true;
9702
10169
  return true;
9703
10170
  }
10171
+ const parsed = parseProgressFromText(finalText);
10172
+ if (parsed) {
10173
+ await this.updateProgress(parsed.progress, parsed.note);
10174
+ if (parsed.progress >= 100) {
10175
+ await this.markGoalCompleted(
10176
+ { source: action.source, task: action.task, directive: action.directive },
10177
+ `progress reached 100%: ${parsed.note ?? "goal complete"}`
10178
+ );
10179
+ this.stopRequested = true;
10180
+ return true;
10181
+ }
10182
+ }
9704
10183
  this.iterationsSinceCompact++;
9705
10184
  await this.maybeCompact().catch((err) => {
9706
10185
  this.opts.onError?.(
@@ -9787,11 +10266,16 @@ var EternalAutonomyEngine = class {
9787
10266
  this.consecutiveBrainstormDone++;
9788
10267
  const threshold = this.opts.brainstormDoneStopThreshold ?? 3;
9789
10268
  if (this.consecutiveBrainstormDone >= threshold) {
9790
- await this.markGoalCompleted(
9791
- { source: "brainstorm", task: "no further work", directive: "" },
9792
- `brainstorm returned DONE ${this.consecutiveBrainstormDone}x in a row`
9793
- );
9794
- this.stopRequested = true;
10269
+ const shouldStop = await this.consultBrainForDone(goal);
10270
+ if (shouldStop) {
10271
+ await this.markGoalCompleted(
10272
+ { source: "brainstorm", task: "no further work", directive: "" },
10273
+ `brainstorm returned DONE ${this.consecutiveBrainstormDone}x in a row${this.opts.brain ? ", brain confirmed" : ""}`
10274
+ );
10275
+ this.stopRequested = true;
10276
+ } else {
10277
+ this.consecutiveBrainstormDone = 0;
10278
+ }
9795
10279
  }
9796
10280
  return null;
9797
10281
  }
@@ -10010,8 +10494,8 @@ ${recentJournal}` : "No prior iterations.",
10010
10494
  await saveGoal(this.goalPath, abandoned);
10011
10495
  }
10012
10496
  try {
10013
- const { unlink: unlink13 } = await import('fs/promises');
10014
- await unlink13(this.goalPath);
10497
+ const { unlink: unlink14 } = await import('fs/promises');
10498
+ await unlink14(this.goalPath);
10015
10499
  } catch {
10016
10500
  }
10017
10501
  this.opts.onEternalStop?.();
@@ -10025,6 +10509,69 @@ ${recentJournal}` : "No prior iterations.",
10025
10509
  async appendFailure(task, note) {
10026
10510
  await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
10027
10511
  }
10512
+ /**
10513
+ * Consult the brain on whether the goal is truly complete.
10514
+ * Without a brain, always returns true (use heuristic: DONE threshold met = done).
10515
+ */
10516
+ async consultBrainForDone(goal) {
10517
+ if (!this.opts.brain) return true;
10518
+ const deliverablesStatus = goal.deliverables?.length ? `
10519
+ Deliverables: ${goal.deliverables.length} total, progress ${goal.progress ?? "unknown"}%` : "";
10520
+ const recentJournal = goal.journal.slice(-5).map(
10521
+ (e) => ` #${e.iteration} [${e.status}] ${e.task}`
10522
+ ).join("\n");
10523
+ try {
10524
+ const decision = await this.opts.brain.decide({
10525
+ id: `goal-done-${goal.iterations}`,
10526
+ source: "system",
10527
+ question: `Brainstorm returned DONE ${this.consecutiveBrainstormDone}x. Is the goal truly complete?`,
10528
+ context: [
10529
+ `Goal: ${goal.goal}`,
10530
+ `Iterations: ${goal.iterations}`,
10531
+ `Progress: ${goal.progress ?? "unknown"}%`,
10532
+ deliverablesStatus,
10533
+ recentJournal ? `
10534
+ Recent work:
10535
+ ${recentJournal}` : ""
10536
+ ].join("\n"),
10537
+ risk: "high",
10538
+ fallback: "continue"
10539
+ });
10540
+ const summary = formatDecisionSummary(decision, {
10541
+ id: `goal-done-${goal.iterations}`,
10542
+ source: "system",
10543
+ question: "Is the goal complete?",
10544
+ risk: "high",
10545
+ fallback: "continue"
10546
+ });
10547
+ const rationale = "rationale" in decision ? decision.rationale : void 0;
10548
+ await this.appendIterationEntry({
10549
+ source: "brainstorm",
10550
+ task: summary,
10551
+ status: "success",
10552
+ note: rationale
10553
+ });
10554
+ if (decision.type === "deny") {
10555
+ return false;
10556
+ }
10557
+ if (decision.type === "answer") {
10558
+ const text = decision.text.toLowerCase();
10559
+ return text.includes("complete") || text.includes("done") || text.includes("yes");
10560
+ }
10561
+ return true;
10562
+ } catch {
10563
+ return true;
10564
+ }
10565
+ }
10566
+ /**
10567
+ * Persist a progress update from the agent's [PROGRESS: N%] output.
10568
+ */
10569
+ async updateProgress(progress, note) {
10570
+ const current = await loadGoal(this.goalPath);
10571
+ if (!current) return;
10572
+ const updated = recordProgress(current, progress, note);
10573
+ await saveGoal(this.goalPath, updated);
10574
+ }
10028
10575
  async persistEngineState(state) {
10029
10576
  const current = await loadGoal(this.goalPath);
10030
10577
  if (!current) return;
@@ -10390,15 +10937,7 @@ function makeAgentSubagentRunner(opts) {
10390
10937
  let budgetError = null;
10391
10938
  const onBudgetError = (err) => {
10392
10939
  if (err instanceof BudgetThresholdSignal) {
10393
- err.decision.then((decision) => {
10394
- if (decision === "stop") {
10395
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
10396
- aborter.abort();
10397
- }
10398
- }).catch(() => {
10399
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
10400
- aborter.abort();
10401
- });
10940
+ budgetError = err;
10402
10941
  return;
10403
10942
  }
10404
10943
  aborter.abort();
@@ -10506,15 +11045,23 @@ function makeAgentSubagentRunner(opts) {
10506
11045
  }
10507
11046
  if (budgetError) {
10508
11047
  if ("decision" in budgetError) {
10509
- const decision = await budgetError.decision;
10510
- if (decision === "stop") {
11048
+ try {
11049
+ const decision = await budgetError.decision;
11050
+ if (decision === "stop") {
11051
+ budgetError = new BudgetExceededError(
11052
+ budgetError.kind,
11053
+ budgetError.limit,
11054
+ budgetError.used
11055
+ );
11056
+ } else {
11057
+ budgetError = null;
11058
+ }
11059
+ } catch {
10511
11060
  budgetError = new BudgetExceededError(
10512
11061
  budgetError.kind,
10513
11062
  budgetError.limit,
10514
11063
  budgetError.used
10515
11064
  );
10516
- } else {
10517
- budgetError = null;
10518
11065
  }
10519
11066
  }
10520
11067
  if (budgetError) throw budgetError;
@@ -12665,17 +13212,120 @@ Working rules:
12665
13212
  phase: "meta",
12666
13213
  summary: "Token/cloud cost optimization: finds spend waste, recommends model routing and trimming with $ estimates.",
12667
13214
  keywords: [
12668
- "cost",
12669
- "token cost",
12670
- "optimize cost",
12671
- "spend",
12672
- "cheaper",
12673
- "model routing",
12674
- "budget",
12675
- "expensive",
12676
- "reduce tokens",
12677
- "pricing",
12678
- "cloud cost"
13215
+ "cost",
13216
+ "token cost",
13217
+ "optimize cost",
13218
+ "spend",
13219
+ "cheaper",
13220
+ "model routing",
13221
+ "budget",
13222
+ "expensive",
13223
+ "reduce tokens",
13224
+ "pricing",
13225
+ "cloud cost"
13226
+ ]
13227
+ }
13228
+ },
13229
+ {
13230
+ config: {
13231
+ id: "tech-stack",
13232
+ name: "Tech Stack Validator",
13233
+ role: "tech-stack",
13234
+ tools: ["search", "fetch", "read", "grep", "glob", "outdated", "audit", "json"],
13235
+ prompt: `You are the Tech Stack Validator \u2014 a single-shot validation agent that fires
13236
+ before any package, library, or framework choice is committed.
13237
+
13238
+ Your ONLY job: verify that a technology choice is current, real, and not obsolete.
13239
+ You are the "this isn't code, this is 10-year-old technology" agent. Intervene
13240
+ hard when the LLM hallucinates a version number or suggests dead tech.
13241
+
13242
+ ## Critical rules
13243
+
13244
+ 1. **Verify existence.** Search npm registry (fetch https://registry.npmjs.org/<pkg>/latest)
13245
+ or web search. A package that doesn't exist = hallucination.
13246
+
13247
+ 2. **Check latest version.** Never trust any version number from the model. Always
13248
+ fetch the actual latest stable version from npm or the project's release page.
13249
+
13250
+ 3. **Reject dead packages.** No release in >2 years + unresolved critical issues =
13251
+ dead. Suggest a maintained replacement.
13252
+
13253
+ 4. **Reject prehistoric tech.** Any package/pattern superseded \u22655 years ago is
13254
+ REJECTED. Key blocklist:
13255
+ - axios / node-fetch / got / request \u2192 native fetch (Node 18+)
13256
+ - moment \u2192 date-fns / luxon / Temporal
13257
+ - jQuery (new projects) \u2192 vanilla DOM / React
13258
+ - Gulp / Grunt \u2192 tsup / esbuild / vite
13259
+ - CoffeeScript / Flow \u2192 TypeScript
13260
+ - Bluebird \u2192 native Promises
13261
+ - crypto-js \u2192 node:crypto / Web Crypto
13262
+ - Bower \u2192 npm/pnpm
13263
+ - underscore \u2192 lodash or native ES2020+
13264
+
13265
+ 5. **The intervention phrase.** When rejecting on age grounds, you MUST output
13266
+ exactly: "This isn't code, this is X-year-old technology." where X =
13267
+ current year \u2212 the year the technology was made obsolete. Follow with
13268
+ what replaced it and a one-step migration path.
13269
+
13270
+ 6. **Prefer built-in over third-party.** Check Node 22+ native APIs first:
13271
+ node:test, node:sqlite, fetch, WebSocket, Web Crypto \u2014 all built-in.
13272
+
13273
+ ## Workflow (single-shot \u2014 do NOT loop)
13274
+
13275
+ 1. Receive the proposed package + version
13276
+ 2. Search npm registry or web for the latest version
13277
+ 3. Check age, maintenance status, deprecation
13278
+ 4. Output verdict: APPROVED (with exact version) or REJECTED (with replacement)
13279
+
13280
+ ## Output format
13281
+
13282
+ ### Tech Stack Validation \u2014 <package>
13283
+
13284
+ **Status**: APPROVED | REJECTED
13285
+
13286
+ **Package**: <name>@<version>
13287
+ **Source**: <URL you checked \u2014 npm registry, GitHub, web search>
13288
+ **Age**: <first release> \u2014 <last release date>
13289
+ **Verdict**: 1\u20132 sentence explanation.
13290
+
13291
+ When REJECTED on age:
13292
+ **"This isn't code, this is X-year-old technology."**
13293
+ **Replaced by**: <modern alternative>
13294
+ **Migration**: <one concrete step>
13295
+
13296
+ When APPROVED:
13297
+ **Install**: pnpm add <name>@^<major>.<minor>.0`
13298
+ },
13299
+ budget: {
13300
+ timeoutMs: 6e4,
13301
+ maxIterations: 5,
13302
+ maxToolCalls: 20,
13303
+ maxTokens: 4e4,
13304
+ maxCostUsd: 0.1
13305
+ },
13306
+ capability: {
13307
+ phase: "meta",
13308
+ summary: "Single-shot tech stack validator: checks npm for latest versions, rejects dead/obsolete packages, enforces modern alternatives.",
13309
+ keywords: [
13310
+ "tech stack",
13311
+ "version",
13312
+ "package",
13313
+ "library",
13314
+ "framework",
13315
+ "dependency",
13316
+ "install",
13317
+ "upgrade",
13318
+ "latest",
13319
+ "npm",
13320
+ "pnpm add",
13321
+ "outdated",
13322
+ "obsolete",
13323
+ "deprecated",
13324
+ "what version",
13325
+ "which package",
13326
+ "check version",
13327
+ "verify version",
13328
+ "is this current"
12679
13329
  ]
12680
13330
  }
12681
13331
  }
@@ -14010,12 +14660,6 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
14010
14660
  };
14011
14661
 
14012
14662
  // src/execution/parallel-eternal-engine.ts
14013
- function expectDefined12(value) {
14014
- if (value === null || value === void 0) {
14015
- throw new Error("Expected value to be defined");
14016
- }
14017
- return value;
14018
- }
14019
14663
  var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
14020
14664
  var ParallelEternalEngine = class {
14021
14665
  constructor(opts) {
@@ -14171,7 +14815,7 @@ var ParallelEternalEngine = class {
14171
14815
  // Fan-out
14172
14816
  // -------------------------------------------------------------------------
14173
14817
  async fanOut(goal, tasks) {
14174
- const coordinator = expectDefined12(this.coordinator);
14818
+ const coordinator = expectDefined(this.coordinator);
14175
14819
  const slotCount = Math.min(this.slots, tasks.length);
14176
14820
  const routes = this.dispatchEnabled ? await Promise.all(
14177
14821
  tasks.slice(0, slotCount).map(
@@ -14203,7 +14847,7 @@ ${recentJournal}` : "No prior iterations.",
14203
14847
  const routeInfo = [];
14204
14848
  const spawnPromises = [];
14205
14849
  for (let i = 0; i < slotCount; i++) {
14206
- const task = expectDefined12(tasks[i]);
14850
+ const task = expectDefined(tasks[i]);
14207
14851
  const route = routes[i] ?? null;
14208
14852
  const subagentId = `parallel-${this.iterations}-${i}`;
14209
14853
  const taskId = randomUUID();
@@ -14476,7 +15120,17 @@ ${journalTail.join("\n")}` : "Recent journal: (none \u2014 this is the first ite
14476
15120
  }
14477
15121
 
14478
15122
  // src/execution/goal-preamble.ts
14479
- function buildGoalPreamble(goal) {
15123
+ function buildGoalPreamble(goal, deliverables) {
15124
+ const deliverableBlock = deliverables && deliverables.length > 0 ? [
15125
+ "",
15126
+ "CONCRETE DELIVERABLES (check these off as you go):",
15127
+ ...deliverables.map((d, i) => ` ${i + 1}. ${d}`),
15128
+ "",
15129
+ "After EACH iteration, estimate your completion percentage (0\u2013100)",
15130
+ "against this deliverable list. Output it as:",
15131
+ " [PROGRESS: N%] \u2014 <1-sentence status>",
15132
+ "The eternal engine reads this to update the progress bar."
15133
+ ].join("\n") : "";
14480
15134
  return [
14481
15135
  "[GOAL \u2014 LOCKED IN. You will work on this until it is verifiably done.",
14482
15136
  "The user granted you full autonomy. Read these constraints once, then act.",
@@ -14485,6 +15139,7 @@ function buildGoalPreamble(goal) {
14485
15139
  "---",
14486
15140
  goal,
14487
15141
  "---",
15142
+ deliverableBlock,
14488
15143
  "",
14489
15144
  "AUTHORITY YOU HAVE:",
14490
15145
  "- Spawn as many subagents as the work needs (delegate / spawn_subagent).",
@@ -14506,6 +15161,7 @@ function buildGoalPreamble(goal) {
14506
15161
  "- You can tell the user HOW to verify it themselves in 10 seconds.",
14507
15162
  '- You have NOT hedged. None of: "looks like it should work", "I',
14508
15163
  ' believe this fixes it", "the changes appear correct".',
15164
+ `- Progress bar shows 100%. All deliverables checked off.`,
14509
15165
  "",
14510
15166
  "WHAT IS NOT DONE \u2014 never report any of these as completion:",
14511
15167
  "- An error message you didn't recover from.",
@@ -14520,6 +15176,13 @@ function buildGoalPreamble(goal) {
14520
15176
  " respond to with a fresh attempt (different role, different model,",
14521
15177
  " tighter prompt).",
14522
15178
  "",
15179
+ "PROGRESS REPORTING \u2014 MANDATORY every iteration:",
15180
+ "- End every response with exactly: [PROGRESS: N%] \u2014 <status note>",
15181
+ " where N is your honest estimate (0-100) of how much of the goal",
15182
+ " is done. The engine tracks this and shows a live progress bar.",
15183
+ "- Example: [PROGRESS: 45%] \u2014 Auth module refactored, 3/5 tests pass,",
15184
+ " remaining: test coverage + docs update.",
15185
+ "",
14523
15186
  "PERSISTENCE PROTOCOL:",
14524
15187
  "- If blocked, try at least 3 different angles before reporting the",
14525
15188
  " problem to the user. Different tool inputs, different subagent",
@@ -14562,6 +15225,7 @@ var CollabSession = class extends EventEmitter {
14562
15225
  settled = false;
14563
15226
  timeoutMs;
14564
15227
  cancelled = false;
15228
+ _raceResolved = false;
14565
15229
  alerts = [];
14566
15230
  snapshotWarnings = [];
14567
15231
  /** Tracks tool call counts per subagent for progress-based timeout decisions. */
@@ -14658,7 +15322,7 @@ var CollabSession = class extends EventEmitter {
14658
15322
  * Safe to call multiple times (idempotent after first call).
14659
15323
  */
14660
15324
  cancel(reason = "Director cancelled collab session") {
14661
- if (this.settled) return;
15325
+ if (this.cancelled) return;
14662
15326
  this.cancelled = true;
14663
15327
  if (this._timeoutTimer) {
14664
15328
  clearTimeout(this._timeoutTimer);
@@ -14696,6 +15360,7 @@ var CollabSession = class extends EventEmitter {
14696
15360
  this.subagentIds.set("critic", criticId);
14697
15361
  const timeout = new Promise((_, reject) => {
14698
15362
  this._timeoutTimer = setTimeout(() => {
15363
+ if (this._raceResolved) return;
14699
15364
  this.cancel("Session-level timeout reached");
14700
15365
  reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
14701
15366
  }, this.timeoutMs);
@@ -14711,6 +15376,7 @@ var CollabSession = class extends EventEmitter {
14711
15376
  timeout
14712
15377
  ]);
14713
15378
  } catch (err) {
15379
+ this._raceResolved = true;
14714
15380
  if (this._timeoutTimer) {
14715
15381
  clearTimeout(this._timeoutTimer);
14716
15382
  this._timeoutTimer = void 0;
@@ -14725,6 +15391,7 @@ var CollabSession = class extends EventEmitter {
14725
15391
  }
14726
15392
  this.snapshotWarnings = await this.checkSnapshotFreshness();
14727
15393
  const report = this.assembleReport();
15394
+ this._raceResolved = true;
14728
15395
  this.cleanup();
14729
15396
  this.emit("session.done", report);
14730
15397
  return report;
@@ -18307,7 +18974,7 @@ var TaskTracker = class {
18307
18974
  const blockers = this.getBlockers(taskId);
18308
18975
  return blockers.every((id) => {
18309
18976
  const node = this.graph?.nodes.get(id);
18310
- return node?.status === "completed";
18977
+ return node?.status === "completed" || node?.status === "failed";
18311
18978
  });
18312
18979
  }
18313
18980
  getProgress() {
@@ -18339,7 +19006,7 @@ var TaskTracker = class {
18339
19006
  const remainingBlockers = this.getBlockers(depId);
18340
19007
  const allUnblocked = remainingBlockers.every((id) => {
18341
19008
  const blocker = this.graph?.nodes.get(id);
18342
- return blocker?.status === "completed";
19009
+ return blocker?.status === "completed" || blocker?.status === "failed";
18343
19010
  });
18344
19011
  if (allUnblocked) {
18345
19012
  dep.status = "pending";
@@ -18353,7 +19020,7 @@ var TaskTracker = class {
18353
19020
  const blockers = this.getBlockers(taskId);
18354
19021
  const someBlocked = blockers.some((id) => {
18355
19022
  const blocker = this.graph?.nodes.get(id);
18356
- return blocker?.status !== "completed";
19023
+ return blocker?.status !== "completed" && blocker?.status !== "failed";
18357
19024
  });
18358
19025
  if (someBlocked) {
18359
19026
  const node = this.graph.nodes.get(taskId);
@@ -18790,12 +19457,6 @@ var TaskGraphStore = class {
18790
19457
  };
18791
19458
 
18792
19459
  // src/sdd/spec-builder.ts
18793
- function expectDefined13(value) {
18794
- if (value === null || value === void 0) {
18795
- throw new Error("Expected value to be defined");
18796
- }
18797
- return value;
18798
- }
18799
19460
  function buildQuestioningPrompt(session, min, max) {
18800
19461
  const answered = session.answers.length;
18801
19462
  const remaining = Math.max(0, min - answered);
@@ -18841,7 +19502,7 @@ function buildQuestioningPrompt(session, min, max) {
18841
19502
  if (answered > 0) {
18842
19503
  lines.push("", "**Conversation so far:**");
18843
19504
  for (let i = 0; i < answered; i++) {
18844
- const a = expectDefined13(session.answers[i]);
19505
+ const a = expectDefined(session.answers[i]);
18845
19506
  lines.push(``, `Q${i + 1}: ${a.question}`, `A${i + 1}: ${a.answer}`);
18846
19507
  }
18847
19508
  }
@@ -18999,10 +19660,10 @@ var AISpecBuilder = class {
18999
19660
  async saveSession() {
19000
19661
  if (!this.sessionPath) return;
19001
19662
  try {
19002
- const fsp20 = await import('fs/promises');
19663
+ const fsp21 = await import('fs/promises');
19003
19664
  const path37 = await import('path');
19004
19665
  const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
19005
- await fsp20.mkdir(path37.dirname(this.sessionPath), { recursive: true });
19666
+ await fsp21.mkdir(path37.dirname(this.sessionPath), { recursive: true });
19006
19667
  await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
19007
19668
  } catch {
19008
19669
  }
@@ -19011,8 +19672,8 @@ var AISpecBuilder = class {
19011
19672
  async loadSession() {
19012
19673
  if (!this.sessionPath) return false;
19013
19674
  try {
19014
- const fsp20 = await import('fs/promises');
19015
- const raw = await fsp20.readFile(this.sessionPath, "utf8");
19675
+ const fsp21 = await import('fs/promises');
19676
+ const raw = await fsp21.readFile(this.sessionPath, "utf8");
19016
19677
  const loaded = JSON.parse(raw);
19017
19678
  if (loaded?.id && loaded?.phase && loaded?.title) {
19018
19679
  this.session = loaded;
@@ -19026,8 +19687,8 @@ var AISpecBuilder = class {
19026
19687
  async deleteSession() {
19027
19688
  if (!this.sessionPath) return;
19028
19689
  try {
19029
- const fsp20 = await import('fs/promises');
19030
- await fsp20.unlink(this.sessionPath);
19690
+ const fsp21 = await import('fs/promises');
19691
+ await fsp21.unlink(this.sessionPath);
19031
19692
  } catch {
19032
19693
  }
19033
19694
  }
@@ -19428,7 +20089,7 @@ function templateToMarkdown(template, title) {
19428
20089
  }
19429
20090
 
19430
20091
  // src/sdd/task-visualizer.ts
19431
- var STATUS_ICON = {
20092
+ var STATUS_ICON2 = {
19432
20093
  pending: "\u25CB",
19433
20094
  in_progress: "\u25D0",
19434
20095
  blocked: "\u2298",
@@ -19436,13 +20097,13 @@ var STATUS_ICON = {
19436
20097
  review: "\u25D1",
19437
20098
  completed: "\u25CF"
19438
20099
  };
19439
- var PRIORITY_ICON = {
20100
+ var PRIORITY_ICON2 = {
19440
20101
  critical: "\u{1F534}",
19441
20102
  high: "\u{1F7E0}",
19442
20103
  medium: "\u{1F7E1}",
19443
20104
  low: "\u{1F7E2}"
19444
20105
  };
19445
- var TYPE_ICON = {
20106
+ var TYPE_ICON2 = {
19446
20107
  feature: "\u26A1",
19447
20108
  bugfix: "\u{1F41B}",
19448
20109
  refactor: "\u267B\uFE0F",
@@ -19491,9 +20152,9 @@ function renderNode(graph, nodeId, lines, rendered, childrenMap, compact, prefix
19491
20152
  rendered.add(nodeId);
19492
20153
  const node = graph.nodes.get(nodeId);
19493
20154
  if (!node) return;
19494
- const icon = STATUS_ICON[node.status];
19495
- const prioIcon = PRIORITY_ICON[node.priority];
19496
- const typeIcon = TYPE_ICON[node.type];
20155
+ const icon = STATUS_ICON2[node.status];
20156
+ const prioIcon = PRIORITY_ICON2[node.priority];
20157
+ const typeIcon = TYPE_ICON2[node.type];
19497
20158
  const title = compact ? truncate2(node.title, 40) : node.title;
19498
20159
  const blockedBy = childrenMap.get(nodeId) ?? [];
19499
20160
  const depsStr = blockedBy.length > 0 ? ` \u2190 [${blockedBy.map((d) => graph.nodes.get(d)?.title?.slice(0, 12) ?? "?").join(", ")}]` : "";
@@ -19535,11 +20196,11 @@ function renderTaskList(graph) {
19535
20196
  }
19536
20197
  for (const [status, group] of Object.entries(groups)) {
19537
20198
  if (group.length === 0) continue;
19538
- const icon = STATUS_ICON[status];
20199
+ const icon = STATUS_ICON2[status];
19539
20200
  lines.push(`${icon} ${status.toUpperCase()} (${group.length})`);
19540
20201
  for (const node of group) {
19541
- const prio = PRIORITY_ICON[node.priority];
19542
- const type = TYPE_ICON[node.type];
20202
+ const prio = PRIORITY_ICON2[node.priority];
20203
+ const type = TYPE_ICON2[node.type];
19543
20204
  lines.push(` ${type} ${prio} ${node.title}`);
19544
20205
  }
19545
20206
  lines.push("");
@@ -19583,12 +20244,6 @@ function truncate2(str, maxLen) {
19583
20244
  }
19584
20245
 
19585
20246
  // src/sdd/critical-path.ts
19586
- function expectDefined14(value) {
19587
- if (value === null || value === void 0) {
19588
- throw new Error("Expected value to be defined");
19589
- }
19590
- return value;
19591
- }
19592
20247
  function analyzeCriticalPath(graph) {
19593
20248
  const nodes = Array.from(graph.nodes.values());
19594
20249
  const topoOrder = topologicalSort(graph);
@@ -19664,7 +20319,7 @@ function getTransitiveBlocked(_graph, taskId, blocksMap) {
19664
20319
  const visited = /* @__PURE__ */ new Set();
19665
20320
  const queue = [taskId];
19666
20321
  while (queue.length > 0) {
19667
- const current = expectDefined14(queue.shift());
20322
+ const current = expectDefined(queue.shift());
19668
20323
  const blocked = blocksMap.get(current);
19669
20324
  if (!blocked) continue;
19670
20325
  for (const id of blocked) {
@@ -19710,7 +20365,7 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
19710
20365
  if (!changed) break;
19711
20366
  }
19712
20367
  let maxDist = 0;
19713
- let maxId = expectDefined14(allIds[0]);
20368
+ let maxId = expectDefined(allIds[0]);
19714
20369
  for (const id of allIds) {
19715
20370
  const d = dist.get(id) ?? 0;
19716
20371
  if (d > maxDist) {
@@ -20179,25 +20834,22 @@ var SddTaskDecomposer = class {
20179
20834
  return nodes.some((n) => n.status === "blocked");
20180
20835
  }
20181
20836
  };
20182
- function expectDefined15(value) {
20183
- if (value === null || value === void 0) {
20184
- throw new Error("Expected value to be defined");
20185
- }
20186
- return value;
20187
- }
20188
20837
  var SddParallelRun = class {
20189
20838
  constructor(opts) {
20190
20839
  this.opts = opts;
20191
20840
  this.slots = Math.min(16, Math.max(1, opts.parallelSlots ?? 4));
20192
20841
  this.timeoutMs = opts.taskTimeoutMs ?? 3e5;
20842
+ this.maxRetries = Math.max(0, opts.maxRetries ?? 2);
20193
20843
  this.decomposer = new SddTaskDecomposer(opts.tracker, opts.graph, { parallelSlots: this.slots });
20194
20844
  }
20195
20845
  opts;
20196
20846
  slots;
20197
20847
  timeoutMs;
20848
+ maxRetries;
20198
20849
  decomposer;
20199
20850
  coordinator = null;
20200
20851
  stopRequested = false;
20852
+ retryMap = /* @__PURE__ */ new Map();
20201
20853
  // -------------------------------------------------------------------
20202
20854
  // Public API
20203
20855
  // -------------------------------------------------------------------
@@ -20209,6 +20861,7 @@ var SddParallelRun = class {
20209
20861
  /** Execute all waves until completion or deadlock. Returns final summary. */
20210
20862
  async run() {
20211
20863
  this.stopRequested = false;
20864
+ this.retryMap.clear();
20212
20865
  const startTime = Date.now();
20213
20866
  let totalCompleted = 0;
20214
20867
  let totalFailed = 0;
@@ -20333,13 +20986,24 @@ var SddParallelRun = class {
20333
20986
  const successCount = results.filter((r) => r.status === "success").length;
20334
20987
  const failCount = results.length - successCount;
20335
20988
  for (let i = 0; i < results.length; i++) {
20336
- const result = expectDefined15(results[i]);
20337
- const taskId = expectDefined15(taskIds[i]);
20989
+ const result = expectDefined(results[i]);
20990
+ const taskId = expectDefined(taskIds[i]);
20338
20991
  if (result.status === "success") {
20339
20992
  this.opts.tracker.updateNodeStatus(taskId, "completed");
20993
+ this.retryMap.delete(taskId);
20340
20994
  } else {
20341
20995
  const errMsg = result.error?.kind ? `${result.error.kind}: ${result.error.message}` : result.error?.message ?? "unknown error";
20342
- this.opts.tracker.updateNodeStatus(taskId, "failed", errMsg);
20996
+ const currentRetries = this.retryMap.get(taskId) ?? 0;
20997
+ if (currentRetries < this.maxRetries) {
20998
+ this.retryMap.set(taskId, currentRetries + 1);
20999
+ this.opts.tracker.updateNodeStatus(
21000
+ taskId,
21001
+ "pending",
21002
+ `Retry ${currentRetries + 1}/${this.maxRetries}: ${errMsg}`
21003
+ );
21004
+ } else {
21005
+ this.opts.tracker.updateNodeStatus(taskId, "failed", errMsg);
21006
+ }
20343
21007
  }
20344
21008
  }
20345
21009
  return {
@@ -20354,6 +21018,7 @@ var SddParallelRun = class {
20354
21018
  }
20355
21019
  buildProgress() {
20356
21020
  const gp = this.opts.tracker.getProgress();
21021
+ const isDeadlocked = !this.decomposer.isDone() && this.decomposer.nextBatch().deadlocked;
20357
21022
  return {
20358
21023
  wave: this.decomposer.getWaveCount(),
20359
21024
  total: gp.total,
@@ -20363,7 +21028,7 @@ var SddParallelRun = class {
20363
21028
  blocked: gp.blocked,
20364
21029
  pending: gp.pending,
20365
21030
  percent: gp.percentComplete,
20366
- deadlocked: false
21031
+ deadlocked: isDeadlocked
20367
21032
  };
20368
21033
  }
20369
21034
  };
@@ -21441,12 +22106,6 @@ var allServers = () => ({
21441
22106
  "zai-vision": { ...zaiVisionServer(), enabled: false },
21442
22107
  "minimax-vision": { ...miniMaxVisionServer(), enabled: false }
21443
22108
  });
21444
- function expectDefined16(value) {
21445
- if (value === null || value === void 0) {
21446
- throw new Error("Expected value to be defined");
21447
- }
21448
- return value;
21449
- }
21450
22109
  function parseSkillRef(input) {
21451
22110
  const trimmed = input.trim().replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
21452
22111
  const atIdx = trimmed.indexOf("@");
@@ -21463,7 +22122,7 @@ function parseSkillRef(input) {
21463
22122
  if (parts.length < 2) {
21464
22123
  throw new Error(`Invalid skill reference "${input}". Expected format: user/repo or user/repo@ref`);
21465
22124
  }
21466
- return { owner: expectDefined16(parts[0]), repo: expectDefined16(parts[1]), ref };
22125
+ return { owner: expectDefined(parts[0]), repo: expectDefined(parts[1]), ref };
21467
22126
  }
21468
22127
  var MAX_TARBALL_SIZE = 50 * 1024 * 1024;
21469
22128
  async function downloadGitHubTarball(parsed) {
@@ -21632,12 +22291,6 @@ var SkillManifestStore = class {
21632
22291
  };
21633
22292
 
21634
22293
  // src/skills/skill-installer.ts
21635
- function expectDefined17(value) {
21636
- if (value === null || value === void 0) {
21637
- throw new Error("Expected value to be defined");
21638
- }
21639
- return value;
21640
- }
21641
22294
  var MAX_SKILL_FILE_SIZE = 100 * 1024;
21642
22295
  var SkillInstaller = class {
21643
22296
  opts;
@@ -21762,7 +22415,7 @@ var SkillInstaller = class {
21762
22415
  bySource.get(key)?.push(entry);
21763
22416
  }
21764
22417
  for (const [, entries] of bySource) {
21765
- const first = expectDefined17(entries[0]);
22418
+ const first = expectDefined(entries[0]);
21766
22419
  const scope = first.scope;
21767
22420
  const isGlobal = scope === "user";
21768
22421
  try {
@@ -21929,15 +22582,387 @@ async function collectFiles(dir, baseDir2) {
21929
22582
  }
21930
22583
  return results;
21931
22584
  }
22585
+ var GraphMemoryBackend = class {
22586
+ kind = "graph";
22587
+ file;
22588
+ graphFile;
22589
+ // In-memory graph state — lazily loaded, saved on mutation.
22590
+ nodes = /* @__PURE__ */ new Map();
22591
+ edges = [];
22592
+ loadedScope = null;
22593
+ loaded = false;
22594
+ constructor(opts) {
22595
+ this.file = new FileMemoryBackend({ paths: opts.paths });
22596
+ this.graphFile = opts.graphPath ?? `${opts.paths.projectDir}/memory-graph.json`;
22597
+ }
22598
+ // ── Backend interface ──────────────────────────────────────────────
22599
+ async remember(scope, entry, filePath) {
22600
+ await this.file.remember(scope, entry, filePath);
22601
+ await this.loadGraph(scope);
22602
+ const nodeId = this.nodeId(entry);
22603
+ const existing = this.nodes.get(nodeId);
22604
+ if (existing) {
22605
+ existing.count++;
22606
+ existing.entry = entry;
22607
+ existing.type = entry.type;
22608
+ existing.tags = entry.tags;
22609
+ existing.priority = entry.priority;
22610
+ } else {
22611
+ this.nodes.set(nodeId, {
22612
+ id: nodeId,
22613
+ entry,
22614
+ firstSeen: entry.ts,
22615
+ count: 1,
22616
+ type: entry.type,
22617
+ tags: entry.tags,
22618
+ priority: entry.priority
22619
+ });
22620
+ for (const [, other] of this.nodes) {
22621
+ if (other.id === nodeId) continue;
22622
+ const sim = wordOverlap(entry.text, other.entry.text);
22623
+ const tagSim = sharedTags(entry.tags ?? [], other.tags ?? []);
22624
+ const weight = Math.max(sim, tagSim * 0.5);
22625
+ if (weight > 0.15) {
22626
+ this.edges.push({
22627
+ from: nodeId,
22628
+ to: other.id,
22629
+ relation: sim >= tagSim ? "similar" : "same_turn",
22630
+ weight,
22631
+ ts: entry.ts
22632
+ });
22633
+ }
22634
+ }
22635
+ }
22636
+ await this.saveGraph(scope);
22637
+ }
22638
+ async forget(scope, query, filePath) {
22639
+ const removed = await this.file.forget(scope, query, filePath);
22640
+ if (removed > 0) {
22641
+ await this.loadGraph(scope);
22642
+ const n = query.toLowerCase();
22643
+ const toRemove = [];
22644
+ for (const [id, node] of this.nodes) {
22645
+ if (node.entry.text.toLowerCase().includes(n)) {
22646
+ toRemove.push(id);
22647
+ }
22648
+ }
22649
+ for (const id of toRemove) this.nodes.delete(id);
22650
+ this.edges = this.edges.filter((e) => !toRemove.includes(e.from) && !toRemove.includes(e.to));
22651
+ await this.saveGraph(scope);
22652
+ }
22653
+ return removed;
22654
+ }
22655
+ async readAll(scope, filePath) {
22656
+ return this.file.readAll(scope, filePath);
22657
+ }
22658
+ async list(scope, filePath, limit) {
22659
+ await this.loadGraph(scope);
22660
+ const fileEntries = await this.file.list(scope, filePath);
22661
+ const nodeMap = new Map(this.nodes.entries());
22662
+ const enriched = fileEntries.map((fe) => {
22663
+ const nodeId = this.nodeId(fe);
22664
+ const node = nodeMap.get(nodeId);
22665
+ if (node) {
22666
+ return {
22667
+ ...fe,
22668
+ type: node.type ?? fe.type,
22669
+ tags: node.tags ?? fe.tags,
22670
+ priority: node.priority ?? fe.priority
22671
+ };
22672
+ }
22673
+ return fe;
22674
+ });
22675
+ const fileIds = new Set(fileEntries.map((e) => this.nodeId(e)));
22676
+ for (const [id, node] of this.nodes) {
22677
+ if (!fileIds.has(id)) {
22678
+ enriched.push(node.entry);
22679
+ }
22680
+ }
22681
+ enriched.sort((a, b) => b.ts.localeCompare(a.ts));
22682
+ return limit ? enriched.slice(0, limit) : enriched;
22683
+ }
22684
+ async search(scope, query, filePath, limit) {
22685
+ await this.loadGraph(scope);
22686
+ const needle = query.toLowerCase().split(/\s+/);
22687
+ const all = await this.list(scope, filePath);
22688
+ const scored = all.map((entry) => {
22689
+ const words = entry.text.toLowerCase().split(/\s+/);
22690
+ let score = 0;
22691
+ for (const n of needle) {
22692
+ if (words.some((w) => w.includes(n))) score += 1;
22693
+ if (entry.tags?.some((t2) => t2.toLowerCase().includes(n))) score += 2;
22694
+ }
22695
+ const node = this.nodes.get(this.nodeId(entry));
22696
+ if (node) {
22697
+ if (node.priority === "critical") score += 3;
22698
+ else if (node.priority === "high") score += 2;
22699
+ score += node.count * 0.5;
22700
+ }
22701
+ return { entry, score };
22702
+ });
22703
+ scored.sort((a, b) => b.score - a.score);
22704
+ const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
22705
+ return limit ? matched.slice(0, limit) : matched;
22706
+ }
22707
+ async clear(scope, filePath) {
22708
+ await this.file.clear(scope, filePath);
22709
+ this.nodes.clear();
22710
+ this.edges = [];
22711
+ this.loadedScope = scope;
22712
+ this.loaded = true;
22713
+ try {
22714
+ await fsp3.unlink(this.graphFile);
22715
+ } catch {
22716
+ }
22717
+ }
22718
+ async consolidate(scope, filePath) {
22719
+ return this.file.consolidate(scope, filePath);
22720
+ }
22721
+ // ── Graph-specific queries ─────────────────────────────────────────
22722
+ /**
22723
+ * Find memories related to the given entry, ordered by edge weight.
22724
+ */
22725
+ async findRelated(scope, _filePath, entryText, limit = 5) {
22726
+ await this.loadGraph(scope);
22727
+ const targetId = this.nodeId({ scope, text: entryText, ts: "" });
22728
+ const related = this.edges.filter((e) => e.from === targetId || e.to === targetId).sort((a, b) => b.weight - a.weight).slice(0, limit);
22729
+ const result = [];
22730
+ for (const edge of related) {
22731
+ const otherId = edge.from === targetId ? edge.to : edge.from;
22732
+ const node = this.nodes.get(otherId);
22733
+ if (node) result.push(node.entry);
22734
+ }
22735
+ return result;
22736
+ }
22737
+ /**
22738
+ * Get all edges for visualization or traversal.
22739
+ */
22740
+ getGraph() {
22741
+ return {
22742
+ nodes: [...this.nodes.values()],
22743
+ edges: [...this.edges]
22744
+ };
22745
+ }
22746
+ // ── Persistence ────────────────────────────────────────────────────
22747
+ nodeId(entry) {
22748
+ const norm = entry.text.toLowerCase().trim().replace(/\s+/g, " ");
22749
+ return `${entry.scope ?? "mem"}::${simpleHash(norm)}`;
22750
+ }
22751
+ async loadGraph(scope) {
22752
+ if (this.loaded && this.loadedScope === scope) return;
22753
+ try {
22754
+ const raw = await fsp3.readFile(this.graphFile, "utf8");
22755
+ const data = JSON.parse(raw);
22756
+ this.nodes = new Map(data.nodes);
22757
+ this.edges = data.edges;
22758
+ } catch {
22759
+ this.nodes = /* @__PURE__ */ new Map();
22760
+ this.edges = [];
22761
+ }
22762
+ this.loadedScope = scope;
22763
+ this.loaded = true;
22764
+ }
22765
+ async saveGraph(scope) {
22766
+ this.loadedScope = scope;
22767
+ this.loaded = true;
22768
+ try {
22769
+ const data = {
22770
+ nodes: [...this.nodes.entries()],
22771
+ edges: this.edges
22772
+ };
22773
+ await fsp3.mkdir(
22774
+ this.graphFile.substring(0, this.graphFile.lastIndexOf("/")),
22775
+ { recursive: true }
22776
+ );
22777
+ const tmp = `${this.graphFile}.tmp`;
22778
+ await fsp3.writeFile(tmp, JSON.stringify(data));
22779
+ await fsp3.rename(tmp, this.graphFile);
22780
+ } catch {
22781
+ }
22782
+ }
22783
+ };
22784
+ function wordOverlap(a, b) {
22785
+ const wordsA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
22786
+ const wordsB = new Set(b.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
22787
+ if (wordsA.size === 0 || wordsB.size === 0) return 0;
22788
+ let intersection = 0;
22789
+ for (const w of wordsA) {
22790
+ if (wordsB.has(w)) intersection++;
22791
+ }
22792
+ return intersection / Math.max(wordsA.size, wordsB.size);
22793
+ }
22794
+ function sharedTags(a, b) {
22795
+ if (a.length === 0 || b.length === 0) return 0;
22796
+ const setB = new Set(b);
22797
+ let shared = 0;
22798
+ for (const t2 of a) if (setB.has(t2)) shared++;
22799
+ return shared / Math.max(a.length, b.length);
22800
+ }
22801
+ function simpleHash(s) {
22802
+ let h = 0;
22803
+ for (let i = 0; i < s.length; i++) {
22804
+ h = (h << 5) - h + s.charCodeAt(i) | 0;
22805
+ }
22806
+ return Math.abs(h).toString(36);
22807
+ }
22808
+
22809
+ // src/storage/memory-consolidator.ts
22810
+ function buildConsolidationPrompt(finalText, iterations, existingEntries) {
22811
+ const existingBlock = existingEntries.length > 0 ? `
22812
+
22813
+ Existing memory entries:
22814
+ ${existingEntries.map((e) => `- [${e.ts.slice(0, 10)}] ${e.text}`).join("\n")}` : "";
22815
+ 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.
22816
+
22817
+ Session summary (${iterations} iterations):
22818
+ ${finalText.slice(0, 3e3)}${existingBlock}
22819
+
22820
+ Return a JSON object with an "operations" array. Each operation must have an "action" field:
22821
+ - "add": create a new memory entry. Include "text", and optionally "type", "tags", "priority".
22822
+ - "edit": replace an existing entry. Include "query" (to match) and "text" (replacement).
22823
+ - "delete": remove an entry. Include "query" (to match).
22824
+
22825
+ Memory types:
22826
+ - "fact": Objective truth about the project (e.g. "uses pnpm workspaces")
22827
+ - "decision": A choice that was made (e.g. "decided to use biome over eslint")
22828
+ - "convention": A recurring pattern or standard (e.g. "commit messages use conventional format")
22829
+ - "preference": User or team preference (e.g. "prefers short variable names")
22830
+ - "reference": Pointer to a file or location (e.g. "auth logic in packages/core/src/auth/")
22831
+ - "anti_pattern": Something to avoid (e.g. "never use any in TypeScript")
22832
+
22833
+ Priority levels:
22834
+ - "critical": Must always be known (e.g. security constraints)
22835
+ - "high": Important for most tasks
22836
+ - "medium": Useful context
22837
+ - "low": Nice to know
22838
+
22839
+ Rules:
22840
+ - Only persist facts likely useful across multiple future sessions.
22841
+ - Do NOT persist task progress, temporary state, or one-off observations.
22842
+ - Prefer "add" over "edit" unless the existing entry is clearly outdated.
22843
+ - Assign a type and priority to every "add" operation.
22844
+ - Use 1-3 hashtag tags for each entry (e.g. #typescript #build).
22845
+ - Be concise \u2014 each memory entry should be one clear sentence.
22846
+
22847
+ Return ONLY valid JSON, no markdown, no explanation:
22848
+ {
22849
+ "operations": [
22850
+ {
22851
+ "action": "add",
22852
+ "text": "Project uses pnpm workspaces with TypeScript strict mode",
22853
+ "type": "convention",
22854
+ "priority": "high",
22855
+ "tags": ["pnpm", "typescript", "build"]
22856
+ },
22857
+ {
22858
+ "action": "edit",
22859
+ "query": "pnpm",
22860
+ "text": "Project uses pnpm v9+ with ESM-only modules",
22861
+ "type": "fact",
22862
+ "priority": "medium"
22863
+ },
22864
+ { "action": "delete", "query": "outdated convention" }
22865
+ ]
22866
+ }`;
22867
+ }
22868
+ var SessionMemoryConsolidator = class {
22869
+ name = "session-memory-consolidator";
22870
+ owner = "core";
22871
+ memoryStore;
22872
+ provider;
22873
+ model;
22874
+ minIterations;
22875
+ maxExistingEntries;
22876
+ constructor(opts) {
22877
+ this.memoryStore = opts.memoryStore;
22878
+ this.provider = opts.provider;
22879
+ this.model = opts.model;
22880
+ this.minIterations = opts.minIterations ?? 2;
22881
+ this.maxExistingEntries = opts.maxExistingEntries ?? 15;
22882
+ }
22883
+ afterRun = async (ctx, result) => {
22884
+ if (result.status !== "done") return;
22885
+ if (!result.finalText || result.finalText.trim().length < 20) return;
22886
+ if (result.iterations < this.minIterations) return;
22887
+ const provider = this.provider ?? ctx.provider;
22888
+ if (!provider || !provider.complete) return;
22889
+ try {
22890
+ const existingEntries = await this.memoryStore.list("project-memory", this.maxExistingEntries);
22891
+ const prompt = buildConsolidationPrompt(
22892
+ result.finalText,
22893
+ result.iterations,
22894
+ existingEntries
22895
+ );
22896
+ const signal = AbortSignal.timeout(15e3);
22897
+ const response = await provider.complete(
22898
+ {
22899
+ model: this.model ?? ctx.model,
22900
+ system: [{ type: "text", text: prompt }],
22901
+ messages: [
22902
+ { role: "user", content: "Review the session and return memory operations as JSON." }
22903
+ ],
22904
+ maxTokens: 500
22905
+ },
22906
+ { signal }
22907
+ );
22908
+ const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
22909
+ if (!text) return;
22910
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
22911
+ if (!jsonMatch) return;
22912
+ const parsed = JSON.parse(jsonMatch[0]);
22913
+ if (!Array.isArray(parsed.operations) || parsed.operations.length === 0) return;
22914
+ let added = 0;
22915
+ let edited = 0;
22916
+ let deleted = 0;
22917
+ for (const op of parsed.operations) {
22918
+ switch (op.action) {
22919
+ case "add": {
22920
+ if (op.text && op.text.trim()) {
22921
+ await this.memoryStore.remember(op.text.trim(), void 0, {
22922
+ type: op.type,
22923
+ tags: op.tags,
22924
+ priority: op.priority
22925
+ });
22926
+ added++;
22927
+ }
22928
+ break;
22929
+ }
22930
+ case "edit": {
22931
+ if (op.query && op.text && op.text.trim()) {
22932
+ await this.memoryStore.forget(op.query);
22933
+ await this.memoryStore.remember(op.text.trim(), void 0, {
22934
+ type: op.type,
22935
+ tags: op.tags,
22936
+ priority: op.priority
22937
+ });
22938
+ edited++;
22939
+ }
22940
+ break;
22941
+ }
22942
+ case "delete": {
22943
+ if (op.query) {
22944
+ const n = await this.memoryStore.forget(op.query);
22945
+ deleted += n;
22946
+ }
22947
+ break;
22948
+ }
22949
+ }
22950
+ }
22951
+ if (added > 0 || edited > 0 || deleted > 0) {
22952
+ const parts = [];
22953
+ if (added) parts.push(`${added} added`);
22954
+ if (edited) parts.push(`${edited} edited`);
22955
+ if (deleted) parts.push(`${deleted} deleted`);
22956
+ process.stderr.write(`[memory] Session consolidation: ${parts.join(", ")}
22957
+ `);
22958
+ }
22959
+ } catch {
22960
+ }
22961
+ };
22962
+ };
21932
22963
 
21933
22964
  // src/storage/annotations-store.ts
21934
22965
  init_atomic_write();
21935
- function expectDefined18(value) {
21936
- if (value === null || value === void 0) {
21937
- throw new Error("Expected value to be defined");
21938
- }
21939
- return value;
21940
- }
21941
22966
  var FILE_VERSION = 1;
21942
22967
  var MAX_TEXT_LENGTH = 2e3;
21943
22968
  var MAX_ANNOTATIONS = 1e3;
@@ -22030,7 +23055,7 @@ var AnnotationsStore = class {
22030
23055
  return;
22031
23056
  }
22032
23057
  const next = {
22033
- ...expectDefined18(all[idx]),
23058
+ ...expectDefined(all[idx]),
22034
23059
  resolved: true,
22035
23060
  resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
22036
23061
  resolvedBy: input.resolvedBy
@@ -22268,12 +23293,6 @@ var ReplayLogStore = class {
22268
23293
  return next;
22269
23294
  }
22270
23295
  };
22271
- function expectDefined19(value) {
22272
- if (value === null || value === void 0) {
22273
- throw new Error("Expected value to be defined");
22274
- }
22275
- return value;
22276
- }
22277
23296
  var SessionRecovery = class {
22278
23297
  constructor(dir) {
22279
23298
  this.dir = dir;
@@ -22314,7 +23333,7 @@ var SessionRecovery = class {
22314
23333
  const lines = raw.split("\n").filter((l) => l.trim());
22315
23334
  for (let i = lines.length - 1; i >= 0; i--) {
22316
23335
  try {
22317
- const ev = JSON.parse(expectDefined19(lines[i]));
23336
+ const ev = JSON.parse(expectDefined(lines[i]));
22318
23337
  if (ev.type === "in_flight_start") {
22319
23338
  return {
22320
23339
  sessionId,
@@ -22367,12 +23386,12 @@ var SessionRecovery = class {
22367
23386
  let lastCheckpointIdx = -1;
22368
23387
  for (let i = 0; i < events.length; i++) {
22369
23388
  if (events[i]?.type === "checkpoint") {
22370
- lastCheckpoint = expectDefined19(events[i]);
23389
+ lastCheckpoint = expectDefined(events[i]);
22371
23390
  lastCheckpointIdx = i;
22372
23391
  }
22373
23392
  }
22374
23393
  const pendingEvents = lastCheckpointIdx >= 0 ? events.slice(lastCheckpointIdx + 1) : events;
22375
- const lastEv = expectDefined19(events[events.length - 1]);
23394
+ const lastEv = expectDefined(events[events.length - 1]);
22376
23395
  const inFlightStart = lastEv.type === "in_flight_start" ? lastEv : null;
22377
23396
  const context = inFlightStart && inFlightStart.type === "in_flight_start" ? inFlightStart.context : null;
22378
23397
  return {
@@ -22418,12 +23437,6 @@ var SessionRecovery = class {
22418
23437
 
22419
23438
  // src/storage/tool-audit-log.ts
22420
23439
  init_atomic_write();
22421
- function expectDefined20(value) {
22422
- if (value === null || value === void 0) {
22423
- throw new Error("Expected value to be defined");
22424
- }
22425
- return value;
22426
- }
22427
23440
  var GENESIS_PREV = "0".repeat(64);
22428
23441
  var DEFAULT_FSYNC_EVERY = 100;
22429
23442
  var ToolAuditLog = class {
@@ -22504,7 +23517,7 @@ var ToolAuditLog = class {
22504
23517
  }
22505
23518
  let prevHash = GENESIS_PREV;
22506
23519
  for (let i = 0; i < entries.length; i++) {
22507
- const e = expectDefined20(entries[i]);
23520
+ const e = expectDefined(entries[i]);
22508
23521
  if (e.prevHash !== prevHash) {
22509
23522
  return {
22510
23523
  ok: false,
@@ -22624,12 +23637,6 @@ function sortKeys2(value) {
22624
23637
 
22625
23638
  // src/storage/session-rewinder.ts
22626
23639
  init_atomic_write();
22627
- function expectDefined21(value) {
22628
- if (value === null || value === void 0) {
22629
- throw new Error("Expected value to be defined");
22630
- }
22631
- return value;
22632
- }
22633
23640
  var DefaultSessionRewinder = class {
22634
23641
  constructor(sessionsDir, projectRoot) {
22635
23642
  this.sessionsDir = sessionsDir;
@@ -22668,7 +23675,7 @@ var DefaultSessionRewinder = class {
22668
23675
  const events = parseEvents(raw);
22669
23676
  let targetIdx = -1;
22670
23677
  for (let i = 0; i < events.length; i++) {
22671
- const event = expectDefined21(events[i]);
23678
+ const event = expectDefined(events[i]);
22672
23679
  if (event.type === "checkpoint") {
22673
23680
  const checkpointEvent = event;
22674
23681
  if (checkpointEvent.promptIndex === checkpointIndex) {
@@ -22682,7 +23689,7 @@ var DefaultSessionRewinder = class {
22682
23689
  }
22683
23690
  const snapshotsToRevert = [];
22684
23691
  for (let i = targetIdx + 1; i < events.length; i++) {
22685
- const event = expectDefined21(events[i]);
23692
+ const event = expectDefined(events[i]);
22686
23693
  if (event.type === "checkpoint") {
22687
23694
  break;
22688
23695
  }
@@ -22792,6 +23799,43 @@ async function revertSnapshots(snapshots, projectRoot) {
22792
23799
  return { revertedFiles, errors };
22793
23800
  }
22794
23801
 
23802
+ // src/storage/task-store.ts
23803
+ init_atomic_write();
23804
+ function emptyTaskFile(sessionId) {
23805
+ return {
23806
+ version: 1,
23807
+ sessionId,
23808
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
23809
+ tasks: []
23810
+ };
23811
+ }
23812
+ async function loadTasks(filePath) {
23813
+ let raw;
23814
+ try {
23815
+ raw = await fsp3.readFile(filePath, "utf8");
23816
+ } catch {
23817
+ return null;
23818
+ }
23819
+ try {
23820
+ const parsed = JSON.parse(raw);
23821
+ if (parsed?.version !== 1 || !Array.isArray(parsed.tasks)) return null;
23822
+ return parsed;
23823
+ } catch {
23824
+ return null;
23825
+ }
23826
+ }
23827
+ async function saveTasks(filePath, tasks) {
23828
+ try {
23829
+ tasks.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
23830
+ await atomicWrite(filePath, JSON.stringify(tasks, null, 2), { mode: 384 });
23831
+ } catch (err) {
23832
+ console.warn(
23833
+ "[task-store] save failed:",
23834
+ err instanceof Error ? err.message : String(err)
23835
+ );
23836
+ }
23837
+ }
23838
+
22795
23839
  // src/storage/prompt-store.ts
22796
23840
  init_atomic_write();
22797
23841
  var DefaultPromptStore = class {
@@ -22864,12 +23908,6 @@ var DefaultPromptStore = class {
22864
23908
  };
22865
23909
  }
22866
23910
  };
22867
- function expectDefined22(value) {
22868
- if (value === null || value === void 0) {
22869
- throw new Error("Expected value to be defined");
22870
- }
22871
- return value;
22872
- }
22873
23911
  var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
22874
23912
  var CloudSync = class {
22875
23913
  constructor(paths, getConfig, setConfig) {
@@ -22912,8 +23950,8 @@ var CloudSync = class {
22912
23950
  const cfg = this.getConfig();
22913
23951
  if (!cfg?.enabled) return { ok: false, action: "push", categories: [], message: "Not enabled." };
22914
23952
  const parts = cfg.repo.split("/");
22915
- const owner = expectDefined22(parts[0]);
22916
- const repoName = expectDefined22(parts[1]);
23953
+ const owner = expectDefined(parts[0]);
23954
+ const repoName = expectDefined(parts[1]);
22917
23955
  const branch = "main";
22918
23956
  const baseTreeSha = this.state?.sha;
22919
23957
  const { treeEntries, rev } = await this.buildLocalTree(cfg.categories);
@@ -22965,8 +24003,8 @@ var CloudSync = class {
22965
24003
  const cfg = this.getConfig();
22966
24004
  if (!cfg?.enabled) return { ok: false, action: "pull", categories: [], message: "Not enabled." };
22967
24005
  const pullParts = cfg.repo.split("/");
22968
- const owner = expectDefined22(pullParts[0]);
22969
- const repoName = expectDefined22(pullParts[1]);
24006
+ const owner = expectDefined(pullParts[0]);
24007
+ const repoName = expectDefined(pullParts[1]);
22970
24008
  const branchData = await this.getRef(token, owner, repoName, "main");
22971
24009
  const currentSha = branchData.object.sha;
22972
24010
  const commitData = await this.getCommit(token, owner, repoName, currentSha);
@@ -24064,8 +25102,8 @@ var ReportGenerator = class {
24064
25102
  try {
24065
25103
  await stat(this.options.outputDir);
24066
25104
  } catch {
24067
- const { mkdir: mkdir16 } = await import('fs/promises');
24068
- await mkdir16(this.options.outputDir, { recursive: true });
25105
+ const { mkdir: mkdir17 } = await import('fs/promises');
25106
+ await mkdir17(this.options.outputDir, { recursive: true });
24069
25107
  }
24070
25108
  }
24071
25109
  generateMarkdown(result) {
@@ -24312,12 +25350,6 @@ var defaultGitignoreUpdater = new GitignoreUpdater();
24312
25350
 
24313
25351
  // src/security-scanner/orchestrator.ts
24314
25352
  init_atomic_write();
24315
- function expectDefined23(value) {
24316
- if (value === null || value === void 0) {
24317
- throw new Error("Expected value to be defined");
24318
- }
24319
- return value;
24320
- }
24321
25353
  var SecurityScannerOrchestrator = class {
24322
25354
  constructor(retryPolicy, errorHandler) {
24323
25355
  this.retryPolicy = retryPolicy;
@@ -24367,7 +25399,7 @@ var SecurityScannerOrchestrator = class {
24367
25399
  if (detectionResult.detectedStacks.length === 0) {
24368
25400
  throw new Error(`No supported tech stack detected in ${projectRoot}`);
24369
25401
  }
24370
- const techStack = expectDefined23(detectionResult.detectedStacks[0]);
25402
+ const techStack = expectDefined(detectionResult.detectedStacks[0]);
24371
25403
  const generatedSkill = await this.generateSkillLLM(provider, model, projectRoot, techStack);
24372
25404
  const scanResult = await this.scanWithLLM(provider, model, projectRoot, generatedSkill, techStack, options);
24373
25405
  const synthesizedReport = await this.synthesizeReportLLM(provider, model, projectRoot, techStack, scanResult);
@@ -24448,7 +25480,7 @@ Return ONLY the JSON object, no markdown, no explanation.`;
24448
25480
  const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
24449
25481
  const jsonMatch = text.match(/\{[\s\S]*\}/);
24450
25482
  if (jsonMatch) {
24451
- const sanitized = sanitizeJsonString(expectDefined23(jsonMatch[0])) || expectDefined23(jsonMatch[0]);
25483
+ const sanitized = sanitizeJsonString(expectDefined(jsonMatch[0])) || expectDefined(jsonMatch[0]);
24452
25484
  const skillData = JSON.parse(sanitized);
24453
25485
  return {
24454
25486
  name: skillData.name || `security-scanner-${techStack.stack}`,
@@ -24568,7 +25600,7 @@ Return ONLY the JSON array. If no issues found, return [].`;
24568
25600
  const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
24569
25601
  const jsonMatch = text.match(/\[[\s\S]*\]/);
24570
25602
  if (jsonMatch) {
24571
- const sanitized = sanitizeJsonString(expectDefined23(jsonMatch[0])) || expectDefined23(jsonMatch[0]);
25603
+ const sanitized = sanitizeJsonString(expectDefined(jsonMatch[0])) || expectDefined(jsonMatch[0]);
24572
25604
  const parsed = JSON.parse(sanitized);
24573
25605
  return parsed.map((item, idx) => ({
24574
25606
  id: `llm-finding-${idx}-${Date.now()}`,
@@ -24808,7 +25840,7 @@ ${dirs.join(", ")}`);
24808
25840
  if (detectionResult.detectedStacks.length === 0) {
24809
25841
  throw new Error(`No supported tech stack detected in ${projectRoot}`);
24810
25842
  }
24811
- const techStack = expectDefined23(detectionResult.detectedStacks[0]);
25843
+ const techStack = expectDefined(detectionResult.detectedStacks[0]);
24812
25844
  return {
24813
25845
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
24814
25846
  projectRoot,
@@ -24997,16 +26029,16 @@ Use \`/security report <number>\` to view a specific report.` };
24997
26029
  }
24998
26030
  const index = Number.parseInt(reportId, 10) - 1;
24999
26031
  if (!Number.isNaN(index) && reports[index]) {
25000
- const { readFile: readFile38 } = await import('fs/promises');
25001
- const content = await readFile38(join(reportsDir, reports[index]), "utf-8");
26032
+ const { readFile: readFile40 } = await import('fs/promises');
26033
+ const content = await readFile40(join(reportsDir, reports[index]), "utf-8");
25002
26034
  return { message: `# Security Report
25003
26035
 
25004
26036
  ${content}` };
25005
26037
  }
25006
26038
  const match = reports.find((r) => r.includes(reportId));
25007
26039
  if (match) {
25008
- const { readFile: readFile38 } = await import('fs/promises');
25009
- const content = await readFile38(join(reportsDir, match), "utf-8");
26040
+ const { readFile: readFile40 } = await import('fs/promises');
26041
+ const content = await readFile40(join(reportsDir, match), "utf-8");
25010
26042
  return { message: `# Security Report
25011
26043
 
25012
26044
  ${content}` };
@@ -25416,12 +26448,6 @@ var FleetManager = class {
25416
26448
  this.usage.dispose();
25417
26449
  }
25418
26450
  };
25419
- function expectDefined24(value) {
25420
- if (value === null || value === void 0) {
25421
- throw new Error("Expected value to be defined");
25422
- }
25423
- return value;
25424
- }
25425
26451
  function createMcpControlTool(opts) {
25426
26452
  const { getConfig, configPath, registry } = opts;
25427
26453
  const inputSchema = {
@@ -25573,7 +26599,7 @@ async function runDisable(name, deps) {
25573
26599
  const mcpServers = {
25574
26600
  ...full.mcpServers ?? {}
25575
26601
  };
25576
- const existing = expectDefined24(mcpServers[name]);
26602
+ const existing = expectDefined(mcpServers[name]);
25577
26603
  mcpServers[name] = { ...existing, enabled: false };
25578
26604
  full.mcpServers = mcpServers;
25579
26605
  await writeConfig(deps.configPath, full);
@@ -25644,12 +26670,6 @@ function badge(state) {
25644
26670
  }
25645
26671
 
25646
26672
  // src/extension/registry.ts
25647
- function expectDefined25(value) {
25648
- if (value === null || value === void 0) {
25649
- throw new Error("Expected value to be defined");
25650
- }
25651
- return value;
25652
- }
25653
26673
  var ExtensionRegistry = class {
25654
26674
  extensions = [];
25655
26675
  promptContributors = [];
@@ -25815,7 +26835,7 @@ var ExtensionRegistry = class {
25815
26835
  * default runner, each subsequent wrapper wraps the previous.
25816
26836
  */
25817
26837
  wrapProviderRunner(inner) {
25818
- const wrappers = this.extensions.filter((e) => e.wrapProviderRunner).map((e) => ({ name: e.name, wrap: expectDefined25(e.wrapProviderRunner) }));
26838
+ const wrappers = this.extensions.filter((e) => e.wrapProviderRunner).map((e) => ({ name: e.name, wrap: expectDefined(e.wrapProviderRunner) }));
25819
26839
  if (wrappers.length === 0) return inner;
25820
26840
  let composed = inner;
25821
26841
  for (let i = wrappers.length - 1; i >= 0; i--) {
@@ -26832,6 +27852,7 @@ async function bootConfig(options = {}) {
26832
27852
  await fsp3.mkdir(wpaths.projectDir, { recursive: true });
26833
27853
  await fsp3.mkdir(wpaths.projectSessions, { recursive: true });
26834
27854
  await writeProjectMeta(wpaths, projectRoot);
27855
+ await ensureGitignore(projectRoot);
26835
27856
  cleanupStaleProjects(wpaths).catch(() => {
26836
27857
  });
26837
27858
  const vault = new DefaultSecretVault({ keyFile: wpaths.secretsKey });
@@ -26907,6 +27928,22 @@ async function writeProjectMeta(paths, projectRoot) {
26907
27928
  } catch {
26908
27929
  }
26909
27930
  }
27931
+ var GITIGNORE_ENTRY = ".wrongstack/\n";
27932
+ async function ensureGitignore(projectRoot) {
27933
+ const gitignorePath = path6.join(projectRoot, ".gitignore");
27934
+ try {
27935
+ let content = "";
27936
+ try {
27937
+ content = await fsp3.readFile(gitignorePath, "utf8");
27938
+ } catch {
27939
+ }
27940
+ if (!content.includes(".wrongstack")) {
27941
+ const updated = content ? content.endsWith("\n") ? content + GITIGNORE_ENTRY : content + "\n" + GITIGNORE_ENTRY : GITIGNORE_ENTRY;
27942
+ await fsp3.writeFile(gitignorePath, updated, "utf8");
27943
+ }
27944
+ } catch {
27945
+ }
27946
+ }
26910
27947
  async function cleanupStaleProjects(wpaths) {
26911
27948
  const projectsRoot = path6.dirname(wpaths.projectDir);
26912
27949
  let entries;
@@ -27344,7 +28381,10 @@ var DefaultSystemPromptBuilder = class {
27344
28381
  skillCache;
27345
28382
  /** Cached full skill bodies (after frontmatter), built once per session. */
27346
28383
  skillBodyCache;
28384
+ /** Tools from last build — used for memory relevance scoring. */
28385
+ _lastBuildTools;
27347
28386
  async build(ctx) {
28387
+ this._lastBuildTools = ctx.tools;
27348
28388
  if (this.opts.skillLoader && !this.skillCache) {
27349
28389
  try {
27350
28390
  const entries = await this.opts.skillLoader.listEntries();
@@ -27648,10 +28688,31 @@ summarize it, and let the tool result hold only the summary.`);
27648
28688
  const parts = [];
27649
28689
  if (this.opts.memoryStore) {
27650
28690
  try {
27651
- const mem = await this.opts.memoryStore.readAll();
27652
- if (mem.trim()) parts.push(`# Project Memory
28691
+ if (this.opts.memoryStore.scoreRelevant) {
28692
+ const toolNames = this._lastBuildTools?.map((t2) => t2.name) ?? [];
28693
+ const scored = await this.opts.memoryStore.scoreRelevant(
28694
+ {
28695
+ currentTask: "",
28696
+ toolNames
28697
+ },
28698
+ "project-memory",
28699
+ 8
28700
+ );
28701
+ if (scored.length > 0) {
28702
+ const lines = ["# Relevant Memory"];
28703
+ for (const e of scored) {
28704
+ const badge2 = e.type ? `[\`${e.type.replace("_", "-")}\`] ` : "";
28705
+ const priorityMark = e.priority === "critical" ? "\u26A1" : e.priority === "high" ? "\u25B2" : "";
28706
+ lines.push(`- ${priorityMark}${badge2}${e.text}${e.tags ? ` \`#${e.tags.join(" #")}\`` : ""}`);
28707
+ }
28708
+ parts.push(lines.join("\n"));
28709
+ }
28710
+ } else {
28711
+ const mem = await this.opts.memoryStore.readAll();
28712
+ if (mem.trim()) parts.push(`# Project Memory
27653
28713
 
27654
28714
  ${mem}`);
28715
+ }
27655
28716
  } catch {
27656
28717
  }
27657
28718
  }
@@ -28103,8 +29164,13 @@ var SlashCommandRegistry = class {
28103
29164
  if (!entry) {
28104
29165
  return { message: `Unknown command "/${name}". Type /help for a list.` };
28105
29166
  }
28106
- const res = await entry.cmd.run(args, ctx);
28107
- return res ?? {};
29167
+ try {
29168
+ const res = await entry.cmd.run(args, ctx);
29169
+ return res ?? {};
29170
+ } catch (err) {
29171
+ const msg = err instanceof Error ? err.message : String(err);
29172
+ return { message: `Command "/${name}" failed: ${msg}` };
29173
+ }
28108
29174
  }
28109
29175
  };
28110
29176
 
@@ -28538,20 +29604,28 @@ function wrapApiForCapabilityCheck(plugin, api, log, enforce = false) {
28538
29604
  }
28539
29605
 
28540
29606
  // src/execution/prompt-enhancer.ts
28541
- 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.
29607
+ 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.
28542
29608
 
28543
29609
  Rules:
28544
29610
  - 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.
28545
29611
  - Do NOT answer, solve, or perform the request. Only restate it more clearly.
28546
29612
  - Keep all concrete details verbatim: file paths, identifiers, code, error text, numbers, names, URLs.
28547
29613
  - Resolve obvious ambiguity by making the implied subject explicit, not by inventing specifics. If something is genuinely unspecified, leave it general rather than guessing.
28548
- - Be concise: one tight instruction (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.
29614
+ - Be concise: one tight instruction per version (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.
28549
29615
  - If the message is already clear and complete, return it essentially unchanged.
28550
- - Preserve the user's language (if they wrote in Turkish, refine in Turkish).
29616
+
29617
+ You MUST output TWO versions of the refined request, separated by a line containing only "---".
29618
+ - First version: refined in the SAME LANGUAGE the user wrote in (if Turkish \u2192 Turkish, if Spanish \u2192 Spanish, etc.).
29619
+ - Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).
29620
+
29621
+ Output format:
29622
+ <refined in user's language>
29623
+ ---
29624
+ <refined in English>
28551
29625
 
28552
29626
  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.
28553
29627
 
28554
- Output ONLY the refined request text \u2014 nothing else.`;
29628
+ Output ONLY the two versions separated by "---" \u2014 nothing else.`;
28555
29629
  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;
28556
29630
  function shouldEnhance(text) {
28557
29631
  const t2 = text.trim();
@@ -28598,12 +29672,23 @@ async function enhanceUserPrompt(opts) {
28598
29672
  const signal = opts.signal ? AbortSignal.any([opts.signal, timer.signal]) : timer.signal;
28599
29673
  try {
28600
29674
  const res = await provider.complete(req, { signal });
28601
- const refined = res.content.filter(isTextBlock).map((b) => b.text).join("\n").trim();
28602
- if (!refined) {
29675
+ const raw = res.content.filter(isTextBlock).map((b) => b.text).join("\n").trim();
29676
+ if (!raw) {
28603
29677
  opts.onError?.("model returned no text");
28604
29678
  return null;
28605
29679
  }
28606
- return refined;
29680
+ const sepIdx = raw.indexOf("\n---\n");
29681
+ if (sepIdx === -1) {
29682
+ opts.onError?.("model did not produce two versions");
29683
+ return { refined: raw, english: raw };
29684
+ }
29685
+ const refined = raw.slice(0, sepIdx).trim();
29686
+ const english = raw.slice(sepIdx + 5).trim();
29687
+ if (!refined || !english) {
29688
+ opts.onError?.("one or both versions empty");
29689
+ return null;
29690
+ }
29691
+ return { refined, english };
28607
29692
  } catch (err) {
28608
29693
  if (opts.signal?.aborted) return null;
28609
29694
  if (timer.signal.aborted) {
@@ -28706,8 +29791,8 @@ var PhaseGraphBuilder = class _PhaseGraphBuilder {
28706
29791
  return graph;
28707
29792
  }
28708
29793
  /**
28709
- * Var olan bir TaskGraph'tan fazlar oluştur.
28710
- * Task'ları priority/type göre gruplayarak fazlara ayırır.
29794
+ * Create phases from an existing TaskGraph.
29795
+ * Groups tasks into phases by priority and type.
28711
29796
  */
28712
29797
  static fromTaskGraph(taskGraph, options) {
28713
29798
  const tasksPerPhase = options.tasksPerPhase ?? 5;
@@ -28779,14 +29864,14 @@ var PhaseOrchestrator = class {
28779
29864
  maxVerifyAttempts: opts.maxVerifyAttempts ?? 2,
28780
29865
  autonomous: opts.autonomous ?? true,
28781
29866
  phaseDelayMs: opts.phaseDelayMs ?? 0,
28782
- stopOnFailure: opts.stopOnFailure ?? true,
29867
+ stopOnFailure: opts.stopOnFailure ?? false,
28783
29868
  events: this.events
28784
29869
  };
28785
29870
  }
28786
29871
  // ─── Lifecycle ────────────────────────────────────────────────────────────
28787
29872
  /**
28788
- * Tüm faz akışını başlat.
28789
- * Autonomous mode'da: kök faz(lar)ı başlatır, bitince sonrakini otomatik başlatır.
29873
+ * Start the full phase flow.
29874
+ * In autonomous mode, starts root phases and automatically starts the next phase when they finish.
28790
29875
  */
28791
29876
  async start() {
28792
29877
  this.stopped = false;
@@ -28813,17 +29898,17 @@ var PhaseOrchestrator = class {
28813
29898
  this.tickInterval = setInterval(() => this.tick(), 1e3);
28814
29899
  }
28815
29900
  }
28816
- /** Bekleyen tüm faz merge'lerini (dep-sıralı + global seri) bekle. */
29901
+ /** Wait for all pending phase merges, dependency-ordered and globally serialized. */
28817
29902
  async drainMerges() {
28818
29903
  await Promise.allSettled([...this.phaseMergePromise.values()]);
28819
29904
  await this.mergeQueue.catch(() => {
28820
29905
  });
28821
29906
  }
28822
- /** Duraklat aktif fazlar çalışmaya devam eder ama yeni faz başlamaz */
29907
+ /** Pause: active phases continue, but no new phase starts. */
28823
29908
  pause() {
28824
29909
  this.paused = true;
28825
29910
  }
28826
- /** Devam et yeni fazlar başlayabilir */
29911
+ /** Resume: new phases may start again. */
28827
29912
  resume() {
28828
29913
  this.paused = false;
28829
29914
  this.tick().catch((err) => {
@@ -28833,7 +29918,7 @@ var PhaseOrchestrator = class {
28833
29918
  );
28834
29919
  });
28835
29920
  }
28836
- /** Tamamen durdur aktif fazlar da durdurulur */
29921
+ /** Stop completely, including active phases. */
28837
29922
  stop() {
28838
29923
  this.stopped = true;
28839
29924
  if (this.tickInterval) {
@@ -29031,7 +30116,10 @@ var PhaseOrchestrator = class {
29031
30116
  const depPromises = phase.dependsOn.map((d) => this.phaseMergePromise.get(d)).filter((p) => Boolean(p));
29032
30117
  const merged = (async () => {
29033
30118
  await Promise.allSettled(depPromises);
29034
- this.mergeQueue = this.mergeQueue.then(() => this.mergeOne(phase, handle));
30119
+ this.mergeQueue = this.mergeQueue.then(() => this.mergeOne(phase, handle)).catch((err) => {
30120
+ const msg = err instanceof Error ? err.message : String(err);
30121
+ console.error(`[PhaseOrchestrator] mergeOne failed for phase ${phase.id}: ${msg}`);
30122
+ });
29035
30123
  await this.mergeQueue;
29036
30124
  })();
29037
30125
  this.phaseMergePromise.set(phase.id, merged);
@@ -29223,7 +30311,7 @@ var PhaseOrchestrator = class {
29223
30311
  if (phase.status !== "pending") continue;
29224
30312
  const depsDone = phase.dependsOn.every((depId) => {
29225
30313
  const dep = this.graph.phases.get(depId);
29226
- return dep?.status === "completed" || dep?.status === "skipped";
30314
+ return dep?.status === "completed" || dep?.status === "skipped" || dep?.status === "failed";
29227
30315
  });
29228
30316
  if (depsDone || phase.parallelizable) {
29229
30317
  ready.push(phase);
@@ -29434,7 +30522,9 @@ var AutoPhaseRunner = class {
29434
30522
  if (failedPhase) {
29435
30523
  this.opts.onFail?.(this.graph, failedPhase, new Error(p.error));
29436
30524
  }
29437
- this.cleanup();
30525
+ if (this.opts.stopOnFailure) {
30526
+ this.cleanup();
30527
+ }
29438
30528
  }
29439
30529
  };
29440
30530
  /** Stores the unsubscribe function returned by EventBus.on() */
@@ -29488,10 +30578,32 @@ var AutoPhaseRunner = class {
29488
30578
  }, 2e3);
29489
30579
  }
29490
30580
  this.maxRunTimer = setTimeout(
29491
- () => this.stop(),
29492
- this.opts.maxRunDurationMs ?? 24 * 60 * 6e4
30581
+ () => {
30582
+ this.opts.onProgress?.({
30583
+ totalPhases: 0,
30584
+ pending: 0,
30585
+ ready: 0,
30586
+ running: 0,
30587
+ paused: 0,
30588
+ completed: 0,
30589
+ failed: 0,
30590
+ skipped: 0,
30591
+ percentComplete: 0,
30592
+ totalTasks: 0,
30593
+ completedTasks: 0,
30594
+ failedTasks: 0,
30595
+ estimatedHours: 0,
30596
+ actualHours: 0
30597
+ });
30598
+ this.stop();
30599
+ },
30600
+ this.opts.maxRunDurationMs ?? 7 * 24 * 60 * 6e4
29493
30601
  );
29494
- this.maxRunTimer.unref?.();
30602
+ if (this.opts.maxRunDurationMs !== void 0 && this.opts.maxRunDurationMs <= 0) {
30603
+ clearTimeout(this.maxRunTimer);
30604
+ this.maxRunTimer = null;
30605
+ }
30606
+ this.maxRunTimer?.unref?.();
29495
30607
  if (this.opts.events) {
29496
30608
  const events = this.opts.events;
29497
30609
  const onUntyped = events.on;
@@ -29583,14 +30695,14 @@ var AutoPhasePlanner = class {
29583
30695
  this.opts = opts;
29584
30696
  }
29585
30697
  opts;
29586
- /** Hedefi faz+todo planına dönüştür. */
30698
+ /** Convert the goal into a phase-and-todo plan. */
29587
30699
  async plan() {
29588
30700
  const prompt = this.buildPrompt();
29589
30701
  const raw = await this.opts.runOnce(prompt);
29590
30702
  const phases = this.parse(raw);
29591
30703
  return { phases, raw, parseFailed: phases.length === 0 };
29592
30704
  }
29593
- /** Modelin üreteceği plan için talimat prompt'u. */
30705
+ /** Instruction prompt for the plan the model should produce. */
29594
30706
  buildPrompt() {
29595
30707
  const minP = this.opts.minPhases ?? 3;
29596
30708
  const maxP = this.opts.maxPhases ?? 8;
@@ -29638,7 +30750,7 @@ ${ctx}
29638
30750
  "```"
29639
30751
  ].filter((l) => l !== "").join("\n");
29640
30752
  }
29641
- /** Ham çıktıdan JSON'u çıkar, doğrula ve PhaseTemplate[]'e dönüştür. */
30753
+ /** Extract JSON from raw output, validate it, and convert it to PhaseTemplate[]. */
29642
30754
  parse(raw) {
29643
30755
  const json = extractJSONArray(raw);
29644
30756
  if (!json) return [];
@@ -30378,7 +31490,8 @@ ${check.stderr}`);
30378
31490
  const child = spawn(this.gitBin, args, {
30379
31491
  cwd,
30380
31492
  env: buildChildEnv(),
30381
- stdio: ["ignore", "pipe", "pipe"]
31493
+ stdio: ["ignore", "pipe", "pipe"],
31494
+ signal: AbortSignal.timeout(3e4)
30382
31495
  });
30383
31496
  child.stdout?.on("data", (c) => {
30384
31497
  stdout += c.toString();
@@ -30746,12 +31859,6 @@ var ReplayProviderRunner = class {
30746
31859
  };
30747
31860
 
30748
31861
  // src/plugins/prompts-plugin.ts
30749
- function expectDefined26(value) {
30750
- if (value === null || value === void 0) {
30751
- throw new Error("Expected value to be defined");
30752
- }
30753
- return value;
30754
- }
30755
31862
  function createPromptsPlugin(opts) {
30756
31863
  let store = null;
30757
31864
  return {
@@ -30805,7 +31912,7 @@ ${lines.join("\n")}
30805
31912
  if (!restJoined) return { message: "Usage: /prompts view <title>" };
30806
31913
  const matches = await store.find(restJoined);
30807
31914
  if (matches.length === 0) return { message: `No prompt matching "${restJoined}".` };
30808
- const entry = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined26(matches[0]);
31915
+ const entry = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined(matches[0]);
30809
31916
  const tags = entry.tags.length > 0 ? ` [${entry.tags.join(", ")}]` : "";
30810
31917
  return {
30811
31918
  message: `# ${entry.title}${tags}
@@ -30828,7 +31935,7 @@ ${dim2(`id: ${entry.id} | created: ${entry.createdAt}`)}`
30828
31935
  if (!restJoined) return { message: "Usage: /prompts delete <title>" };
30829
31936
  const matches = await store.find(restJoined);
30830
31937
  if (matches.length === 0) return { message: `No prompt matching "${restJoined}".` };
30831
- const exact = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined26(matches[0]);
31938
+ const exact = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined(matches[0]);
30832
31939
  const deleted = await store.delete(exact.id);
30833
31940
  return { message: deleted ? `Deleted "${exact.title}".` : "Delete failed." };
30834
31941
  }
@@ -30838,7 +31945,7 @@ ${dim2(`id: ${entry.id} | created: ${entry.createdAt}`)}`
30838
31945
  if (!parsed.title) return { message: 'Usage: /prompts edit "title" "new content"' };
30839
31946
  const matches = await store.find(parsed.title);
30840
31947
  if (matches.length === 0) return { message: `No prompt matching "${parsed.title}".` };
30841
- const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined26(matches[0]);
31948
+ const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined(matches[0]);
30842
31949
  exact.content = parsed.content;
30843
31950
  exact.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
30844
31951
  await store.save(exact);
@@ -30850,7 +31957,7 @@ ${dim2(`id: ${entry.id} | created: ${entry.createdAt}`)}`
30850
31957
  if (!parsed.title) return { message: 'Usage: /prompts extend "title" <instructions>' };
30851
31958
  const matches = await store.find(parsed.title);
30852
31959
  if (matches.length === 0) return { message: `No prompt matching "${parsed.title}".` };
30853
- const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined26(matches[0]);
31960
+ const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined(matches[0]);
30854
31961
  const prov = ctx.provider;
30855
31962
  if (!prov?.complete) return { message: "LLM not available. Configure a provider first." };
30856
31963
  const enhanced = await prov.complete(ctx.model, [
@@ -30885,9 +31992,9 @@ function parseTitleContent(args) {
30885
31992
  const trimmed = args.trim();
30886
31993
  if (!trimmed) return { title: "", content: "" };
30887
31994
  const doubleMatch = /^"([^"]+)"\s+"([^"]+)"$/.exec(trimmed) || /^'([^']+)'\s+'([^']+)'$/.exec(trimmed);
30888
- if (doubleMatch) return { title: expectDefined26(doubleMatch[1]), content: expectDefined26(doubleMatch[2]) };
31995
+ if (doubleMatch) return { title: expectDefined(doubleMatch[1]), content: expectDefined(doubleMatch[2]) };
30889
31996
  const singleMatch = /^'([^']+)'\s+(.+)$/.exec(trimmed);
30890
- if (singleMatch) return { title: expectDefined26(singleMatch[1]), content: expectDefined26(singleMatch[2]) };
31997
+ if (singleMatch) return { title: expectDefined(singleMatch[1]), content: expectDefined(singleMatch[2]) };
30891
31998
  const firstSpace = trimmed.indexOf(" ");
30892
31999
  if (firstSpace === -1) return { title: trimmed, content: "" };
30893
32000
  return { title: trimmed.slice(0, firstSpace), content: trimmed.slice(firstSpace + 1) };
@@ -30898,12 +32005,6 @@ function dim2(s) {
30898
32005
 
30899
32006
  // src/plugins/sync-plugin.ts
30900
32007
  init_atomic_write();
30901
- function expectDefined27(value) {
30902
- if (value === null || value === void 0) {
30903
- throw new Error("Expected value to be defined");
30904
- }
30905
- return value;
30906
- }
30907
32008
  function createSyncPlugin(opts) {
30908
32009
  let cloud = null;
30909
32010
  let configStore;
@@ -30977,7 +32078,7 @@ function buildSyncCommand(cloud, configStore, vault, syncConfigPath) {
30977
32078
  if (!repo || !repo.includes("/")) {
30978
32079
  return { message: 'Invalid repo format. Expected "owner/repo".' };
30979
32080
  }
30980
- const storedToken = vault ? vault.encrypt(expectDefined27(token)) : expectDefined27(token);
32081
+ const storedToken = vault ? vault.encrypt(expectDefined(token)) : expectDefined(token);
30981
32082
  const syncConfig = {
30982
32083
  enabled: true,
30983
32084
  repo,
@@ -31118,7 +32219,7 @@ function createGitPlugin() {
31118
32219
  async function runGit(args, cwd) {
31119
32220
  try {
31120
32221
  return await new Promise((resolve13, reject) => {
31121
- const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
32222
+ const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], signal: AbortSignal.timeout(3e4) });
31122
32223
  let stdout = "";
31123
32224
  let stderr = "";
31124
32225
  child.stdout?.on("data", (d) => {
@@ -31843,6 +32944,6 @@ ${formatPlan(updated)}`
31843
32944
  };
31844
32945
  }
31845
32946
 
31846
- 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, 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, withFileLock, wrapAsState, writeErr, writeOut, zaiVisionServer };
32947
+ 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, MAX_PROGRESS_HISTORY, MEMORY_TYPE_LABELS, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, 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, computeTaskItemProgress, 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, emptyTaskFile, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expectDefined, extractRunEnv, filesystemServer, findCriticalPath, flagsToConfigPatch, formatContextWindowModeList, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTaskList, formatTaskProgress, 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, loadTasks, 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, parseProgressFromText, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, recordProgress, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setProgress, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, withFileLock, wrapAsState, writeErr, writeOut, zaiVisionServer };
31847
32948
  //# sourceMappingURL=index.js.map
31848
32949
  //# sourceMappingURL=index.js.map