@wrongstack/core 0.286.0 → 0.287.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 (251) hide show
  1. package/dist/boot.d.ts.map +1 -1
  2. package/dist/coordination/brain-monitor.d.ts +15 -0
  3. package/dist/coordination/brain-monitor.d.ts.map +1 -1
  4. package/dist/coordination/delegate-tool.d.ts +8 -14
  5. package/dist/coordination/delegate-tool.d.ts.map +1 -1
  6. package/dist/coordination/director/director-btw-notes.d.ts +20 -0
  7. package/dist/coordination/director/director-btw-notes.d.ts.map +1 -0
  8. package/dist/coordination/director/director-collab.d.ts +40 -0
  9. package/dist/coordination/director/director-collab.d.ts.map +1 -0
  10. package/dist/coordination/director.d.ts +27 -8
  11. package/dist/coordination/director.d.ts.map +1 -1
  12. package/dist/coordination/fleet-manager.d.ts +21 -0
  13. package/dist/coordination/fleet-manager.d.ts.map +1 -1
  14. package/dist/coordination/fleet.d.ts +2 -3
  15. package/dist/coordination/fleet.d.ts.map +1 -1
  16. package/dist/coordination/global-mailbox.d.ts +36 -22
  17. package/dist/coordination/global-mailbox.d.ts.map +1 -1
  18. package/dist/coordination/icoordinator.d.ts +1 -3
  19. package/dist/coordination/icoordinator.d.ts.map +1 -1
  20. package/dist/coordination/index.js +708 -442
  21. package/dist/coordination/index.js.map +4 -4
  22. package/dist/coordination/mail-tools.d.ts.map +1 -1
  23. package/dist/coordination/mailbox-message-codec.d.ts +21 -1
  24. package/dist/coordination/mailbox-message-codec.d.ts.map +1 -1
  25. package/dist/coordination/mailbox-types.d.ts +42 -1
  26. package/dist/coordination/mailbox-types.d.ts.map +1 -1
  27. package/dist/core/agent-loop.d.ts.map +1 -1
  28. package/dist/core/agent-response.d.ts +10 -1
  29. package/dist/core/agent-response.d.ts.map +1 -1
  30. package/dist/core/agent-tools.d.ts.map +1 -1
  31. package/dist/core/agent.d.ts +8 -0
  32. package/dist/core/agent.d.ts.map +1 -1
  33. package/dist/core/context.d.ts +25 -0
  34. package/dist/core/context.d.ts.map +1 -1
  35. package/dist/core/conversation-state.d.ts +6 -0
  36. package/dist/core/conversation-state.d.ts.map +1 -1
  37. package/dist/core/fallback-model.d.ts.map +1 -1
  38. package/dist/core/mailbox-loop.d.ts +12 -5
  39. package/dist/core/mailbox-loop.d.ts.map +1 -1
  40. package/dist/core/system-prompt-builder.d.ts +14 -6
  41. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  42. package/dist/defaults/index.d.ts +0 -1
  43. package/dist/defaults/index.d.ts.map +1 -1
  44. package/dist/defaults/index.js +834 -1465
  45. package/dist/defaults/index.js.map +4 -4
  46. package/dist/execution/auto-compaction-middleware.d.ts +8 -1
  47. package/dist/execution/auto-compaction-middleware.d.ts.map +1 -1
  48. package/dist/execution/compaction-core.d.ts.map +1 -1
  49. package/dist/execution/council-brain.d.ts.map +1 -1
  50. package/dist/execution/council-orchestrator.d.ts +32 -0
  51. package/dist/execution/council-orchestrator.d.ts.map +1 -0
  52. package/dist/execution/council-personas.d.ts +19 -0
  53. package/dist/execution/council-personas.d.ts.map +1 -0
  54. package/dist/execution/council-profiles.d.ts +34 -0
  55. package/dist/execution/council-profiles.d.ts.map +1 -0
  56. package/dist/execution/council-prompts.d.ts +21 -0
  57. package/dist/execution/council-prompts.d.ts.map +1 -0
  58. package/dist/execution/council-resolution.d.ts +57 -0
  59. package/dist/execution/council-resolution.d.ts.map +1 -0
  60. package/dist/execution/design-kit-loader.d.ts.map +1 -1
  61. package/dist/execution/enhance-recovery.d.ts +35 -0
  62. package/dist/execution/enhance-recovery.d.ts.map +1 -0
  63. package/dist/execution/error-handler.d.ts.map +1 -1
  64. package/dist/execution/index.d.ts +6 -0
  65. package/dist/execution/index.d.ts.map +1 -1
  66. package/dist/execution/index.js +1738 -161
  67. package/dist/execution/index.js.map +4 -4
  68. package/dist/execution/intelligent-compactor.d.ts +18 -7
  69. package/dist/execution/intelligent-compactor.d.ts.map +1 -1
  70. package/dist/execution/one-shot-llm.d.ts +52 -0
  71. package/dist/execution/one-shot-llm.d.ts.map +1 -0
  72. package/dist/execution/prompt-enhancer.d.ts +23 -4
  73. package/dist/execution/prompt-enhancer.d.ts.map +1 -1
  74. package/dist/execution/prompt-enhancer.js +24 -6
  75. package/dist/execution/prompt-enhancer.js.map +2 -2
  76. package/dist/execution/selective-compactor.d.ts +4 -4
  77. package/dist/execution/strategy-compactor.d.ts +7 -0
  78. package/dist/execution/strategy-compactor.d.ts.map +1 -1
  79. package/dist/execution/tool-executor.d.ts +19 -5
  80. package/dist/execution/tool-executor.d.ts.map +1 -1
  81. package/dist/hooks/registry.d.ts.map +1 -1
  82. package/dist/hooks/runner.d.ts.map +1 -1
  83. package/dist/hooks/shell-executor.d.ts.map +1 -1
  84. package/dist/hq/alerts.d.ts +5 -0
  85. package/dist/hq/alerts.d.ts.map +1 -1
  86. package/dist/hq/auth-store.d.ts +17 -7
  87. package/dist/hq/auth-store.d.ts.map +1 -1
  88. package/dist/hq/commands.d.ts +8 -4
  89. package/dist/hq/commands.d.ts.map +1 -1
  90. package/dist/hq/cost-bridge.d.ts +5 -3
  91. package/dist/hq/cost-bridge.d.ts.map +1 -1
  92. package/dist/hq/fleet-bridge.d.ts.map +1 -1
  93. package/dist/hq/index.d.ts +0 -1
  94. package/dist/hq/index.d.ts.map +1 -1
  95. package/dist/hq/index.js +471 -294
  96. package/dist/hq/index.js.map +4 -4
  97. package/dist/hq/persistence.d.ts +42 -0
  98. package/dist/hq/persistence.d.ts.map +1 -1
  99. package/dist/hq/protocol/brain.d.ts +24 -0
  100. package/dist/hq/protocol/brain.d.ts.map +1 -0
  101. package/dist/hq/protocol/browser.d.ts +12 -0
  102. package/dist/hq/protocol/browser.d.ts.map +1 -0
  103. package/dist/hq/protocol/client.d.ts +69 -0
  104. package/dist/hq/protocol/client.d.ts.map +1 -0
  105. package/dist/hq/protocol/core.d.ts +130 -0
  106. package/dist/hq/protocol/core.d.ts.map +1 -0
  107. package/dist/hq/protocol/fleet.d.ts +73 -0
  108. package/dist/hq/protocol/fleet.d.ts.map +1 -0
  109. package/dist/hq/protocol/mailbox.d.ts +76 -0
  110. package/dist/hq/protocol/mailbox.d.ts.map +1 -0
  111. package/dist/hq/protocol/mcp.d.ts +51 -0
  112. package/dist/hq/protocol/mcp.d.ts.map +1 -0
  113. package/dist/hq/protocol/project.d.ts +26 -0
  114. package/dist/hq/protocol/project.d.ts.map +1 -0
  115. package/dist/hq/protocol/session.d.ts +153 -0
  116. package/dist/hq/protocol/session.d.ts.map +1 -0
  117. package/dist/hq/protocol/tool.d.ts +31 -0
  118. package/dist/hq/protocol/tool.d.ts.map +1 -0
  119. package/dist/hq/protocol.d.ts +10 -611
  120. package/dist/hq/protocol.d.ts.map +1 -1
  121. package/dist/index.d.ts +12 -2
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +4540 -2283
  124. package/dist/index.js.map +4 -4
  125. package/dist/infrastructure/index.js +13 -11
  126. package/dist/infrastructure/index.js.map +2 -2
  127. package/dist/infrastructure/token-counter.d.ts.map +1 -1
  128. package/dist/kernel/events/agent-events.d.ts +283 -0
  129. package/dist/kernel/events/agent-events.d.ts.map +1 -0
  130. package/dist/kernel/events/brain-events.d.ts +176 -0
  131. package/dist/kernel/events/brain-events.d.ts.map +1 -0
  132. package/dist/kernel/events/fleet-events.d.ts +39 -0
  133. package/dist/kernel/events/fleet-events.d.ts.map +1 -0
  134. package/dist/kernel/events/memory-events.d.ts +165 -0
  135. package/dist/kernel/events/memory-events.d.ts.map +1 -0
  136. package/dist/kernel/events/provider-events.d.ts +98 -0
  137. package/dist/kernel/events/provider-events.d.ts.map +1 -0
  138. package/dist/kernel/events/sdd-events.d.ts +118 -0
  139. package/dist/kernel/events/sdd-events.d.ts.map +1 -0
  140. package/dist/kernel/events/session-events.d.ts +227 -0
  141. package/dist/kernel/events/session-events.d.ts.map +1 -0
  142. package/dist/kernel/events/tool-events.d.ts +185 -0
  143. package/dist/kernel/events/tool-events.d.ts.map +1 -0
  144. package/dist/kernel/events/worktree-events.d.ts +58 -0
  145. package/dist/kernel/events/worktree-events.d.ts.map +1 -0
  146. package/dist/kernel/events.d.ts +10 -1298
  147. package/dist/kernel/events.d.ts.map +1 -1
  148. package/dist/kernel/index.js +22 -2
  149. package/dist/kernel/index.js.map +2 -2
  150. package/dist/kernel/run-controller.d.ts +15 -0
  151. package/dist/kernel/run-controller.d.ts.map +1 -1
  152. package/dist/mailbox-attach.d.ts.map +1 -1
  153. package/dist/models/index.js +19 -6
  154. package/dist/models/index.js.map +2 -2
  155. package/dist/models/llm-selector.d.ts +7 -0
  156. package/dist/models/llm-selector.d.ts.map +1 -1
  157. package/dist/plugins/auto-review-plugin.d.ts +36 -0
  158. package/dist/plugins/auto-review-plugin.d.ts.map +1 -0
  159. package/dist/plugins/chimera-plugin.d.ts +8 -0
  160. package/dist/plugins/chimera-plugin.d.ts.map +1 -1
  161. package/dist/security/index.js +7 -2
  162. package/dist/security/index.js.map +2 -2
  163. package/dist/security/secret-scrubber.d.ts.map +1 -1
  164. package/dist/skills/index.js.map +2 -2
  165. package/dist/storage/config-loader.d.ts.map +1 -1
  166. package/dist/storage/goal-kanban.d.ts +69 -0
  167. package/dist/storage/goal-kanban.d.ts.map +1 -0
  168. package/dist/storage/index.d.ts +1 -1
  169. package/dist/storage/index.d.ts.map +1 -1
  170. package/dist/storage/index.js +425 -717
  171. package/dist/storage/index.js.map +4 -4
  172. package/dist/storage/memory-consolidator.d.ts +9 -1
  173. package/dist/storage/memory-consolidator.d.ts.map +1 -1
  174. package/dist/tools/council-tool.d.ts +25 -0
  175. package/dist/tools/council-tool.d.ts.map +1 -0
  176. package/dist/tools/index.d.ts +2 -0
  177. package/dist/tools/index.d.ts.map +1 -1
  178. package/dist/tools/index.js +1884 -12
  179. package/dist/tools/index.js.map +4 -4
  180. package/dist/tools/one-shot-llm-tool.d.ts +51 -0
  181. package/dist/tools/one-shot-llm-tool.d.ts.map +1 -0
  182. package/dist/types/config.d.ts +27 -3
  183. package/dist/types/config.d.ts.map +1 -1
  184. package/dist/types/context-window.d.ts.map +1 -1
  185. package/dist/types/council.d.ts +134 -0
  186. package/dist/types/council.d.ts.map +1 -0
  187. package/dist/types/default-config.d.ts +3 -3
  188. package/dist/types/hooks.d.ts +14 -0
  189. package/dist/types/hooks.d.ts.map +1 -1
  190. package/dist/types/index.d.ts +2 -0
  191. package/dist/types/index.d.ts.map +1 -1
  192. package/dist/types/index.js +29 -8
  193. package/dist/types/index.js.map +3 -3
  194. package/dist/types/one-shot-llm.d.ts +128 -0
  195. package/dist/types/one-shot-llm.d.ts.map +1 -0
  196. package/dist/types/plugin.d.ts +6 -0
  197. package/dist/types/plugin.d.ts.map +1 -1
  198. package/dist/types/provider.d.ts +17 -0
  199. package/dist/types/provider.d.ts.map +1 -1
  200. package/dist/types/system-prompt.d.ts +2 -1
  201. package/dist/types/system-prompt.d.ts.map +1 -1
  202. package/dist/types/tool-executor.d.ts +10 -0
  203. package/dist/types/tool-executor.d.ts.map +1 -1
  204. package/dist/utils/atomic-write.d.ts.map +1 -1
  205. package/dist/utils/index.js +75 -8
  206. package/dist/utils/index.js.map +3 -3
  207. package/dist/utils/tool-wire-compact.d.ts +1 -0
  208. package/dist/utils/tool-wire-compact.d.ts.map +1 -1
  209. package/dist/utils/wstack-paths.d.ts +20 -4
  210. package/dist/utils/wstack-paths.d.ts.map +1 -1
  211. package/instructions/cli/next-task-predictor.md +5 -3
  212. package/instructions/coordination/subagent-baseline.md +8 -7
  213. package/instructions/leader-after-task.md +18 -9
  214. package/instructions/llm/council-judge.md +19 -0
  215. package/instructions/llm/council-voter.md +22 -0
  216. package/instructions/modes/architect.md +14 -9
  217. package/instructions/modes/audit-lite.md +9 -7
  218. package/instructions/modes/brief-system.md +12 -12
  219. package/instructions/modes/brief.md +7 -5
  220. package/instructions/modes/code-auditor.md +13 -8
  221. package/instructions/modes/code-reviewer.md +13 -8
  222. package/instructions/modes/debug-lite.md +7 -6
  223. package/instructions/modes/debugger.md +13 -9
  224. package/instructions/modes/devops.md +13 -9
  225. package/instructions/modes/plan-lite.md +7 -6
  226. package/instructions/modes/refactor-lite.md +7 -6
  227. package/instructions/modes/refactorer.md +14 -9
  228. package/instructions/modes/research-lite.md +8 -6
  229. package/instructions/modes/research-web.md +18 -55
  230. package/instructions/modes/review-lite.md +7 -6
  231. package/instructions/modes/teach-system.md +15 -15
  232. package/instructions/modes/teach.md +13 -12
  233. package/instructions/modes/test-lite.md +6 -5
  234. package/instructions/modes/tester.md +13 -9
  235. package/instructions/modes/ui-design.md +14 -8
  236. package/instructions/sections/tool/commit-hygiene.md +1 -7
  237. package/instructions/sections/tool/common-patterns.md +5 -5
  238. package/instructions/sections/tool/delegation-full.md +3 -31
  239. package/instructions/sections/tool/mailbox-compact.md +1 -1
  240. package/instructions/sections/tool/mailbox-full.md +6 -22
  241. package/instructions/system.md +278 -39
  242. package/package.json +2 -2
  243. package/skills/mailbox-bridge/SKILL.md +16 -7
  244. package/skills/output-standards/SKILL.md +16 -16
  245. package/skills/wrongstack-mailbox/SKILL.md +6 -0
  246. package/dist/coordination/director-construction.d.ts +0 -90
  247. package/dist/coordination/director-construction.d.ts.map +0 -1
  248. package/dist/hq/agent-bridge.d.ts +0 -27
  249. package/dist/hq/agent-bridge.d.ts.map +0 -1
  250. package/dist/storage/memory-store.d.ts +0 -119
  251. package/dist/storage/memory-store.d.ts.map +0 -1
@@ -7,7 +7,8 @@ import { createInterface } from "node:readline";
7
7
 
8
8
  // src/utils/atomic-write.ts
9
9
  import { randomBytes as randomBytes2 } from "node:crypto";
10
- import * as fs from "node:fs/promises";
10
+ import * as fs2 from "node:fs/promises";
11
+ import { watch as watchDir } from "node:fs";
11
12
  import * as path3 from "node:path";
12
13
 
13
14
  // src/utils/child-env.ts
@@ -453,14 +454,34 @@ function ulid(seedTime = Date.now()) {
453
454
 
454
455
  // src/utils/wstack-paths.ts
455
456
  import { createHash } from "node:crypto";
457
+ import * as fs from "node:fs";
456
458
  import * as os from "node:os";
457
459
  import * as path2 from "node:path";
460
+ function canonicalProjectRoot(absRoot) {
461
+ const checkoutRoot = path2.resolve(absRoot);
462
+ const dotGit = path2.join(checkoutRoot, ".git");
463
+ try {
464
+ if (!fs.statSync(dotGit).isFile()) return checkoutRoot;
465
+ const gitDirLine = fs.readFileSync(dotGit, "utf8").trim();
466
+ const match = /^gitdir:\s*(.+)$/i.exec(gitDirLine);
467
+ if (!match?.[1]) return checkoutRoot;
468
+ const gitDir = path2.resolve(checkoutRoot, match[1].trim());
469
+ const commonDirFile = path2.join(gitDir, "commondir");
470
+ if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;
471
+ const commonDir = path2.resolve(gitDir, fs.readFileSync(commonDirFile, "utf8").trim());
472
+ if (path2.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
473
+ return path2.dirname(commonDir);
474
+ } catch {
475
+ return checkoutRoot;
476
+ }
477
+ }
458
478
  function projectHash(absRoot) {
459
- return createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 12);
479
+ return createHash("sha256").update(canonicalProjectRoot(absRoot)).digest("hex").slice(0, 12);
460
480
  }
461
481
  function projectSlug(absRoot) {
462
- const base = slugify2(path2.basename(absRoot));
463
- const hash = createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 6);
482
+ const identityRoot = canonicalProjectRoot(absRoot);
483
+ const base = slugify2(path2.basename(identityRoot));
484
+ const hash = createHash("sha256").update(identityRoot).digest("hex").slice(0, 6);
464
485
  return `${base}-${hash}`;
465
486
  }
466
487
  function slugify2(name) {
@@ -661,16 +682,16 @@ var FsError = class extends WrongStackError {
661
682
  // src/utils/atomic-write.ts
662
683
  async function atomicWrite(targetPath, content, opts = {}) {
663
684
  const dir = path3.dirname(targetPath);
664
- await fs.mkdir(dir, { recursive: true });
685
+ await fs2.mkdir(dir, { recursive: true });
665
686
  const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes2(6).toString("hex")}.tmp`);
666
687
  try {
667
688
  if (typeof content === "string") {
668
- await fs.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
689
+ await fs2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
669
690
  } else {
670
- await fs.writeFile(tmp, content, { flag: "wx" });
691
+ await fs2.writeFile(tmp, content, { flag: "wx" });
671
692
  }
672
693
  try {
673
- const fh = await fs.open(tmp, "r+");
694
+ const fh = await fs2.open(tmp, "r+");
674
695
  try {
675
696
  await fh.sync();
676
697
  } finally {
@@ -680,35 +701,35 @@ async function atomicWrite(targetPath, content, opts = {}) {
680
701
  }
681
702
  let mode;
682
703
  try {
683
- const stat13 = await fs.stat(targetPath);
704
+ const stat13 = await fs2.stat(targetPath);
684
705
  mode = stat13.mode & 511;
685
706
  } catch {
686
707
  mode = opts.mode;
687
708
  }
688
709
  if (mode !== void 0) {
689
- await fs.chmod(tmp, mode);
710
+ await fs2.chmod(tmp, mode);
690
711
  }
691
712
  await renameWithRetry(tmp, targetPath);
692
713
  if (mode !== void 0 && process.platform === "win32") {
693
714
  try {
694
- await fs.chmod(targetPath, mode);
715
+ await fs2.chmod(targetPath, mode);
695
716
  } catch {
696
717
  }
697
718
  }
698
719
  } catch (err) {
699
720
  try {
700
- await fs.unlink(tmp);
721
+ await fs2.unlink(tmp);
701
722
  } catch {
702
723
  }
703
724
  throw err;
704
725
  }
705
726
  }
706
727
  async function ensureDir(dir) {
707
- await fs.mkdir(dir, { recursive: true });
728
+ await fs2.mkdir(dir, { recursive: true });
708
729
  }
709
730
  async function withFileLock(targetPath, fn, opts = {}) {
710
731
  const dir = path3.dirname(targetPath);
711
- await fs.mkdir(dir, { recursive: true });
732
+ await fs2.mkdir(dir, { recursive: true });
712
733
  const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
713
734
  const timeoutMs = opts.timeoutMs ?? 5e3;
714
735
  const staleMs = opts.staleMs ?? 3e4;
@@ -716,26 +737,27 @@ async function withFileLock(targetPath, fn, opts = {}) {
716
737
  let handle;
717
738
  for (; ; ) {
718
739
  try {
719
- handle = await fs.open(lockPath, "wx");
740
+ handle = await fs2.open(lockPath, "wx");
720
741
  await handle.writeFile(`${process.pid}:${Date.now()}`);
721
742
  break;
722
743
  } catch (err) {
723
744
  const code = err.code;
724
745
  if (code === "ENOENT") {
725
- await fs.mkdir(dir, { recursive: true });
746
+ await fs2.mkdir(dir, { recursive: true });
726
747
  continue;
727
748
  }
728
- if (code !== "EEXIST") throw err;
749
+ if (code !== "EEXIST" && code !== "EPERM") throw err;
729
750
  try {
730
- const stat13 = await fs.stat(lockPath);
751
+ const stat13 = await fs2.stat(lockPath);
731
752
  if (Date.now() - stat13.mtimeMs > staleMs) {
732
- await fs.unlink(lockPath);
753
+ await fs2.unlink(lockPath);
733
754
  continue;
734
755
  }
735
756
  } catch {
736
757
  continue;
737
758
  }
738
- if (Date.now() - started >= timeoutMs) {
759
+ const elapsed = Date.now() - started;
760
+ if (elapsed >= timeoutMs) {
739
761
  throw new FsError({
740
762
  message: `Timed out waiting for file lock: ${targetPath}`,
741
763
  code: "FS_ATOMIC_WRITE_FAILED",
@@ -743,7 +765,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
743
765
  context: { timeoutMs }
744
766
  });
745
767
  }
746
- await new Promise((resolve11) => setTimeout(resolve11, 25));
768
+ await waitForLockRelease(lockPath, timeoutMs - elapsed);
747
769
  }
748
770
  }
749
771
  try {
@@ -754,22 +776,66 @@ async function withFileLock(targetPath, fn, opts = {}) {
754
776
  } catch {
755
777
  }
756
778
  try {
757
- await fs.unlink(lockPath);
779
+ await fs2.unlink(lockPath);
758
780
  } catch {
759
781
  }
760
782
  }
761
783
  }
784
+ async function waitForLockRelease(lockPath, remainingMs) {
785
+ const parentDir = path3.dirname(lockPath);
786
+ const lockName = path3.basename(lockPath);
787
+ const intervalMs = Math.min(remainingMs, 100);
788
+ return new Promise((resolve11) => {
789
+ let settled = false;
790
+ let watcher = null;
791
+ const timer = setTimeout(() => {
792
+ settled = true;
793
+ watcher?.close();
794
+ resolve11();
795
+ }, intervalMs);
796
+ try {
797
+ watcher = watchDir(parentDir, (eventType, filename) => {
798
+ if (settled) return;
799
+ if (filename === lockName && (eventType === "rename" || eventType === "change")) {
800
+ settled = true;
801
+ clearTimeout(timer);
802
+ watcher?.close();
803
+ resolve11();
804
+ }
805
+ });
806
+ } catch {
807
+ clearTimeout(timer);
808
+ if (!settled) {
809
+ settled = true;
810
+ setTimeout(resolve11, Math.min(remainingMs, 25));
811
+ }
812
+ return;
813
+ }
814
+ fs2.access(lockPath).then(
815
+ () => {
816
+ },
817
+ () => {
818
+ if (!settled) {
819
+ settled = true;
820
+ clearTimeout(timer);
821
+ watcher?.close();
822
+ resolve11();
823
+ }
824
+ }
825
+ );
826
+ });
827
+ }
762
828
  var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
763
829
  async function renameWithRetry(from, to) {
764
830
  if (process.platform !== "win32") {
765
- await fs.rename(from, to);
831
+ await fs2.rename(from, to);
766
832
  return;
767
833
  }
768
834
  const delays = [10, 25, 60, 120, 250];
769
835
  let lastErr;
770
836
  for (let i = 0; i <= delays.length; i++) {
771
837
  try {
772
- await fs.rename(from, to);
838
+ await fs2.rename(from, to);
773
839
  return;
774
840
  } catch (err) {
775
841
  lastErr = err;
@@ -3700,7 +3766,7 @@ function mergeAdjacentText(blocks) {
3700
3766
 
3701
3767
  // src/storage/memory-backend.ts
3702
3768
  import { randomUUID as randomUUID2 } from "node:crypto";
3703
- import * as fs2 from "node:fs/promises";
3769
+ import * as fs3 from "node:fs/promises";
3704
3770
  import * as path10 from "node:path";
3705
3771
 
3706
3772
  // src/types/memory.ts
@@ -3868,7 +3934,7 @@ var FileMemoryBackend = class {
3868
3934
  }
3869
3935
  async getMtime(file) {
3870
3936
  try {
3871
- const stat13 = await fs2.stat(file);
3937
+ const stat13 = await fs3.stat(file);
3872
3938
  return stat13.mtimeMs;
3873
3939
  } catch {
3874
3940
  return 0;
@@ -3918,7 +3984,7 @@ var FileMemoryBackend = class {
3918
3984
  await ensureDir(path10.dirname(file));
3919
3985
  let existing = "";
3920
3986
  try {
3921
- existing = await fs2.readFile(file, "utf8");
3987
+ existing = await fs3.readFile(file, "utf8");
3922
3988
  } catch {
3923
3989
  }
3924
3990
  const id = `mem_${Date.now()}_${randomUUID2().slice(0, 8)}`;
@@ -3936,7 +4002,7 @@ ${line}`;
3936
4002
  return withFileLock(file, async () => {
3937
4003
  let existing;
3938
4004
  try {
3939
- existing = await fs2.readFile(file, "utf8");
4005
+ existing = await fs3.readFile(file, "utf8");
3940
4006
  } catch {
3941
4007
  return 0;
3942
4008
  }
@@ -3973,7 +4039,7 @@ ${line}`;
3973
4039
  async readAll(scope, filePath) {
3974
4040
  const file = this.resolveFile(filePath, scope);
3975
4041
  try {
3976
- return await fs2.readFile(file, "utf8");
4042
+ return await fs3.readFile(file, "utf8");
3977
4043
  } catch {
3978
4044
  return "";
3979
4045
  }
@@ -3997,7 +4063,7 @@ ${line}`;
3997
4063
  const file = this.resolveFile(filePath, scope);
3998
4064
  let existing;
3999
4065
  try {
4000
- existing = await fs2.readFile(file, "utf8");
4066
+ existing = await fs3.readFile(file, "utf8");
4001
4067
  } catch {
4002
4068
  return 0;
4003
4069
  }
@@ -4017,7 +4083,7 @@ ${line}`;
4017
4083
  const next = lines.join("\n");
4018
4084
  const backup = `${file}.bak.${Date.now()}`;
4019
4085
  try {
4020
- await fs2.copyFile(file, backup);
4086
+ await fs3.copyFile(file, backup);
4021
4087
  await pruneConsolidateBackups(file);
4022
4088
  } catch {
4023
4089
  }
@@ -4034,11 +4100,11 @@ async function pruneConsolidateBackups(file) {
4034
4100
  const dir = path10.dirname(file);
4035
4101
  const base = path10.basename(file);
4036
4102
  const prefix = `${base}.bak.`;
4037
- const backups = (await fs2.readdir(dir)).filter((name) => name.startsWith(prefix)).sort().reverse();
4103
+ const backups = (await fs3.readdir(dir)).filter((name) => name.startsWith(prefix)).sort().reverse();
4038
4104
  await Promise.all(
4039
4105
  backups.slice(MAX_MEMORY_CONSOLIDATE_BACKUPS).map(async (name) => {
4040
4106
  try {
4041
- await fs2.unlink(path10.join(dir, name));
4107
+ await fs3.unlink(path10.join(dir, name));
4042
4108
  } catch {
4043
4109
  }
4044
4110
  })
@@ -4053,582 +4119,8 @@ function parseEntries(raw, scope = "project-memory") {
4053
4119
  return entries.reverse();
4054
4120
  }
4055
4121
 
4056
- // src/storage/memory-store.ts
4057
- var MAX_BYTES_TOTAL = 32e3;
4058
- var DefaultMemoryStore = class {
4059
- files;
4060
- events;
4061
- traceId;
4062
- backend;
4063
- /**
4064
- * Per-scope serialization queue. `remember` / `forget` / `consolidate` /
4065
- * `clear` are read-modify-write against a single file; without a lock,
4066
- * two concurrent calls on the same scope can read the same baseline and
4067
- * the later write silently drops the earlier entry. We chain each
4068
- * mutation onto the prior promise for the same scope so they run in
4069
- * issue order. Different scopes still proceed in parallel.
4070
- *
4071
- * The chain tracks only the last pending write. If a write fails, its
4072
- * error is caught and swallowed so the chain stays alive for subsequent
4073
- * calls. The error is stored in `writeErrors` so callers can learn about
4074
- * it on the next read operation.
4075
- */
4076
- writeChain = /* @__PURE__ */ new Map();
4077
- /** Last write error per scope — surfaced as warnings on the next readAll(). */
4078
- writeErrors = /* @__PURE__ */ new Map();
4079
- /**
4080
- * When the global root is a temporary directory (opencode, CI sandboxes),
4081
- * memory files are also mirrored to the project tree so they survive
4082
- * session cleanup. The primary path stays in the temp root for isolation;
4083
- * this backup ensures memory is never lost.
4084
- */
4085
- persistBackup;
4086
- backupDir;
4087
- /**
4088
- * Per-scope tracked byte sizes — incremented on `remember()`, decremented on
4089
- * `forget()`, recalculated after `consolidate()`. Eliminates the redundant
4090
- * readAll() call that previously checked the file size after every write.
4091
- */
4092
- _trackedByteSizes = {};
4093
- /** Result cache for scoreRelevant() — keyed by scope + context hash, TTL 30s. */
4094
- _scoreCache = /* @__PURE__ */ new Map();
4095
- /**
4096
- * Per-entry cached lowercase strings — lazily allocated once and reused
4097
- * across scoreRelevant() calls so repeated scoring of the same entries skips
4098
- * re-lowercasing. Keyed by entry object identity; cleared (set to null) on
4099
- * every mutation (remember/forget/consolidate/clear) via invalidateScoreCaches().
4100
- */
4101
- _cachedLower = null;
4102
- constructor(opts) {
4103
- this.files = {
4104
- "project-agents": opts.paths.inProjectAgentsFile,
4105
- "project-memory": opts.paths.projectMemory,
4106
- "user-memory": opts.paths.globalMemory
4107
- };
4108
- this.events = opts.events;
4109
- this.backend = opts.backend ?? new FileMemoryBackend({ paths: opts.paths });
4110
- const root = opts.paths.globalRoot.toLowerCase();
4111
- this.persistBackup = /[/\\](tmp|temp|cache)[/\\]/.test(root);
4112
- this.backupDir = this.persistBackup ? opts.paths.inProjectAgentsFile.replace(/AGENTS\.md$/, "memory-persist") : "";
4113
- }
4114
- /** Expose the backend for plugin introspection and advanced queries. */
4115
- getBackend() {
4116
- return this.backend;
4117
- }
4118
- async runSerialized(scope, work) {
4119
- const prior = this.writeChain.get(scope) ?? Promise.resolve();
4120
- const next = prior.catch((err) => {
4121
- this.writeErrors.set(scope, err);
4122
- }).then(() => work());
4123
- this.writeChain.set(scope, next);
4124
- try {
4125
- return await next;
4126
- } catch (err) {
4127
- this.writeErrors.set(scope, err);
4128
- throw err;
4129
- } finally {
4130
- if (this.writeChain.get(scope) === next) {
4131
- this.writeChain.delete(scope);
4132
- }
4133
- }
4134
- }
4135
- /**
4136
- * Recalculate the tracked byte size for a scope by re-reading the file and
4137
- * summing the serialized byte length of each line. Called after consolidate()
4138
- * (which modifies the file) to keep the tracker accurate.
4139
- */
4140
- async _recalcTrackedByteSize(scope) {
4141
- const raw = await this.backend.readAll(scope, this.files[scope]);
4142
- let total = 0;
4143
- for (const line of raw.split("\n")) {
4144
- if (line.trim()) total += Buffer.byteLength(line, "utf8");
4145
- }
4146
- this._trackedByteSizes[scope] = total;
4147
- return total;
4148
- }
4149
- async readAll() {
4150
- const parts = [];
4151
- for (const scope of ["project-agents", "project-memory", "user-memory"]) {
4152
- const writeErr = this.writeErrors.get(scope);
4153
- if (writeErr) {
4154
- parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr.message}`);
4155
- }
4156
- const t0 = Date.now();
4157
- const filePath = this.files[scope];
4158
- try {
4159
- const body = await this.backend.readAll(scope, filePath);
4160
- const dur = Date.now() - t0;
4161
- this.events?.emit("storage.read", {
4162
- sessionId: "~memory~",
4163
- store: "memory",
4164
- filePath,
4165
- operation: "readAll",
4166
- outcome: "success",
4167
- durationMs: dur,
4168
- ...this.traceId !== void 0 && { traceId: this.traceId }
4169
- });
4170
- if (body.trim()) parts.push(`## ${labelOf(scope)}
4171
-
4172
- ${body.trim()}`);
4173
- } catch (err) {
4174
- const dur = Date.now() - t0;
4175
- this.events?.emit("storage.read", {
4176
- sessionId: "~memory~",
4177
- store: "memory",
4178
- filePath,
4179
- operation: "readAll",
4180
- outcome: "failure",
4181
- durationMs: dur,
4182
- error: toErrorMessage(err),
4183
- ...this.traceId !== void 0 && { traceId: this.traceId }
4184
- });
4185
- throw err;
4186
- }
4187
- }
4188
- return parts.join("\n\n");
4189
- }
4190
- async read(scope) {
4191
- const t0 = Date.now();
4192
- const filePath = this.files[scope];
4193
- try {
4194
- const body = await this.backend.readAll(scope, filePath);
4195
- const dur = Date.now() - t0;
4196
- this.events?.emit("storage.read", {
4197
- sessionId: "~memory~",
4198
- store: "memory",
4199
- filePath,
4200
- operation: "read",
4201
- outcome: "success",
4202
- durationMs: dur,
4203
- ...this.traceId !== void 0 && { traceId: this.traceId }
4204
- });
4205
- return body;
4206
- } catch (err) {
4207
- const dur = Date.now() - t0;
4208
- this.events?.emit("storage.read", {
4209
- sessionId: "~memory~",
4210
- store: "memory",
4211
- filePath,
4212
- operation: "read",
4213
- outcome: "failure",
4214
- durationMs: dur,
4215
- error: toErrorMessage(err),
4216
- ...this.traceId !== void 0 && { traceId: this.traceId }
4217
- });
4218
- throw err;
4219
- }
4220
- }
4221
- /**
4222
- * List entries from a scope, newest first. Delegates to the backend
4223
- * so graph/semantic backends can return enriched or filtered results.
4224
- */
4225
- async list(scope = "project-memory", limit) {
4226
- return this.backend.list(scope, this.files[scope], limit);
4227
- }
4228
- /**
4229
- * Find memories related to the given text via graph traversal.
4230
- * Falls back to content search when no graph backend is available.
4231
- */
4232
- async findRelated(text, scope = "project-memory", limit = 5) {
4233
- if (this.backend.findRelated) {
4234
- return this.backend.findRelated(scope, this.files[scope], text, limit);
4235
- }
4236
- return this.search(text, scope, limit);
4237
- }
4238
- async search(query, scope = "project-memory", limit) {
4239
- return this.backend.search(scope, query, this.files[scope], limit);
4240
- }
4241
- async remember(text, scope = "project-memory", metadata) {
4242
- const ts = (/* @__PURE__ */ new Date()).toISOString();
4243
- return this.runSerialized(scope, async () => {
4244
- const entry = { scope, text, ts, ...metadata };
4245
- const filePath = this.files[scope];
4246
- const t0 = Date.now();
4247
- try {
4248
- await this.backend.remember(scope, entry, filePath);
4249
- this.invalidateScoreCaches();
4250
- const dur = Date.now() - t0;
4251
- this.events?.emit("storage.write", {
4252
- sessionId: "~memory~",
4253
- store: "memory",
4254
- filePath,
4255
- operation: "remember",
4256
- outcome: "success",
4257
- durationMs: dur,
4258
- ...this.traceId !== void 0 && { traceId: this.traceId }
4259
- });
4260
- } catch (err) {
4261
- const dur = Date.now() - t0;
4262
- this.events?.emit("storage.write", {
4263
- sessionId: "~memory~",
4264
- store: "memory",
4265
- filePath,
4266
- operation: "remember",
4267
- outcome: "failure",
4268
- durationMs: dur,
4269
- error: toErrorMessage(err),
4270
- ...this.traceId !== void 0 && { traceId: this.traceId }
4271
- });
4272
- throw err;
4273
- }
4274
- let trackedSize = this._trackedByteSizes[scope];
4275
- if (trackedSize === void 0) {
4276
- trackedSize = await this._recalcTrackedByteSize(scope);
4277
- }
4278
- if (trackedSize > MAX_BYTES_TOTAL) {
4279
- const removed = await this.backend.consolidate(scope, this.files[scope]);
4280
- if (removed > 0) {
4281
- this.events?.emit("memory.consolidated", {
4282
- scope,
4283
- removed
4284
- });
4285
- await this._recalcTrackedByteSize(scope);
4286
- }
4287
- }
4288
- this._trackedByteSizes[scope] = (this._trackedByteSizes[scope] ?? 0) + Buffer.byteLength(JSON.stringify(entry), "utf8");
4289
- await this.mirrorBackup(scope);
4290
- this.events?.emit("memory.remembered", {
4291
- scope,
4292
- text,
4293
- ts,
4294
- type: entry.type,
4295
- tags: entry.tags,
4296
- priority: entry.priority
4297
- });
4298
- });
4299
- }
4300
- /**
4301
- * Drop the relevance caches after a mutation. Both the per-context score
4302
- * cache and the per-entry lowercase cache are keyed on entry objects that a
4303
- * mutation invalidates, so they must be cleared together when entries change.
4304
- */
4305
- invalidateScoreCaches() {
4306
- this._scoreCache.clear();
4307
- this._cachedLower = null;
4308
- }
4309
- /**
4310
- * Score and rank memories by relevance to the current context.
4311
- * Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
4312
- */
4313
- async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
4314
- const all = await this.list(scope);
4315
- if (all.length === 0) return [];
4316
- const ctxHash = `${scope}|${ctx.currentTask}|${(ctx.activeSkills ?? []).join(",")}|${(ctx.toolNames ?? []).join(",")}`;
4317
- const now = Date.now();
4318
- const TTL_MS = 3e4;
4319
- const cached = this._scoreCache.get(ctxHash);
4320
- if (cached && cached.expiresAt > now && cached.entries === all) {
4321
- return cached.scored.slice(0, Math.min(limit, 15));
4322
- }
4323
- const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
4324
- const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
4325
- const toolWords = (ctx.toolNames ?? []).flatMap((t) => t.toLowerCase().split("_"));
4326
- this._cachedLower ??= /* @__PURE__ */ new WeakMap();
4327
- const lowerCache = this._cachedLower;
4328
- const scored = [];
4329
- for (const entry of all) {
4330
- let score = 0;
4331
- const reasons = [];
4332
- let cachedLower = lowerCache.get(entry);
4333
- if (!cachedLower) {
4334
- cachedLower = {
4335
- textLower: entry.text.toLowerCase(),
4336
- tagsLower: (entry.tags ?? []).map((t) => t.toLowerCase())
4337
- };
4338
- lowerCache.set(entry, cachedLower);
4339
- }
4340
- const { textLower, tagsLower } = cachedLower;
4341
- let taskHits = 0;
4342
- for (const w of taskWords) {
4343
- if (textLower.includes(w)) {
4344
- taskHits++;
4345
- score += 2;
4346
- }
4347
- if (tagsLower.some((t) => t.includes(w))) {
4348
- taskHits++;
4349
- score += 3;
4350
- }
4351
- }
4352
- if (taskHits > 0) reasons.push(`task match (${taskHits})`);
4353
- let skillHits = 0;
4354
- for (const w of skillWords) {
4355
- if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
4356
- skillHits++;
4357
- }
4358
- }
4359
- score += skillHits;
4360
- if (skillHits > 0) reasons.push(`skill match (${skillHits})`);
4361
- for (const w of toolWords) {
4362
- if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
4363
- score += 1;
4364
- reasons.push(`tool mention: ${w}`);
4365
- }
4366
- }
4367
- switch (entry.priority) {
4368
- case "critical":
4369
- score += 5;
4370
- reasons.push("critical");
4371
- break;
4372
- case "high":
4373
- score += 3;
4374
- reasons.push("high priority");
4375
- break;
4376
- case "medium":
4377
- score += 1;
4378
- break;
4379
- case "low":
4380
- score -= 2;
4381
- reasons.push("low priority");
4382
- break;
4383
- }
4384
- switch (entry.type) {
4385
- case "decision":
4386
- score += 2;
4387
- reasons.push("decision");
4388
- break;
4389
- case "convention":
4390
- score += 2;
4391
- reasons.push("convention");
4392
- break;
4393
- case "anti_pattern":
4394
- score += 3;
4395
- reasons.push("anti-pattern");
4396
- break;
4397
- case "preference":
4398
- score += 1;
4399
- reasons.push("preference");
4400
- break;
4401
- case "reference":
4402
- break;
4403
- case "fact":
4404
- break;
4405
- }
4406
- const ageDays = (now - new Date(entry.ts).getTime()) / (1e3 * 60 * 60 * 24);
4407
- if (ageDays < 1) score += 1;
4408
- else if (ageDays > 30) score -= 1;
4409
- if (entry.confidence !== void 0 && entry.confidence < 0.5) {
4410
- score -= 2;
4411
- reasons.push("low confidence");
4412
- }
4413
- if (entry.lastAccessed) {
4414
- const hoursSinceAccess = (now - new Date(entry.lastAccessed).getTime()) / (1e3 * 60 * 60);
4415
- if (hoursSinceAccess < 1) score -= 1;
4416
- }
4417
- if (score > 0) {
4418
- scored.push({
4419
- ...entry,
4420
- score,
4421
- matchReason: reasons.join(", ") || "keyword match"
4422
- });
4423
- }
4424
- }
4425
- scored.sort((a, b) => b.score - a.score);
4426
- const threshold = 2;
4427
- const relevant = scored.filter(
4428
- (s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
4429
- );
4430
- this._scoreCache.set(ctxHash, { entries: all, scored: relevant, expiresAt: now + TTL_MS });
4431
- return relevant.slice(0, Math.min(limit, 15));
4432
- }
4433
- async forget(query, scope = "project-memory") {
4434
- return this.runSerialized(scope, async () => {
4435
- const filePath = this.files[scope];
4436
- const t0 = Date.now();
4437
- let removed = 0;
4438
- try {
4439
- removed = await this.backend.forget(scope, query, filePath);
4440
- this.invalidateScoreCaches();
4441
- const dur = Date.now() - t0;
4442
- this.events?.emit("storage.write", {
4443
- sessionId: "~memory~",
4444
- store: "memory",
4445
- filePath,
4446
- operation: "forget",
4447
- outcome: "success",
4448
- durationMs: dur,
4449
- ...this.traceId !== void 0 && { traceId: this.traceId }
4450
- });
4451
- } catch (err) {
4452
- const dur = Date.now() - t0;
4453
- this.events?.emit("storage.write", {
4454
- sessionId: "~memory~",
4455
- store: "memory",
4456
- filePath,
4457
- operation: "forget",
4458
- outcome: "failure",
4459
- durationMs: dur,
4460
- error: toErrorMessage(err),
4461
- ...this.traceId !== void 0 && { traceId: this.traceId }
4462
- });
4463
- throw err;
4464
- }
4465
- if (removed > 0) {
4466
- this.events?.emit("memory.forgotten", {
4467
- scope,
4468
- query,
4469
- removed
4470
- });
4471
- await this.mirrorBackup(scope);
4472
- await this._recalcTrackedByteSize(scope);
4473
- }
4474
- return removed;
4475
- });
4476
- }
4477
- async consolidate(scope) {
4478
- return this.runSerialized(scope, async () => {
4479
- const filePath = this.files[scope];
4480
- const t0 = Date.now();
4481
- let removed = 0;
4482
- try {
4483
- removed = await this.backend.consolidate(scope, filePath);
4484
- this.invalidateScoreCaches();
4485
- const dur = Date.now() - t0;
4486
- this.events?.emit("storage.write", {
4487
- sessionId: "~memory~",
4488
- store: "memory",
4489
- filePath,
4490
- operation: "consolidate",
4491
- outcome: "success",
4492
- durationMs: dur,
4493
- ...this.traceId !== void 0 && { traceId: this.traceId }
4494
- });
4495
- } catch (err) {
4496
- const dur = Date.now() - t0;
4497
- this.events?.emit("storage.write", {
4498
- sessionId: "~memory~",
4499
- store: "memory",
4500
- filePath,
4501
- operation: "consolidate",
4502
- outcome: "failure",
4503
- durationMs: dur,
4504
- error: toErrorMessage(err),
4505
- ...this.traceId !== void 0 && { traceId: this.traceId }
4506
- });
4507
- throw err;
4508
- }
4509
- if (removed > 0) {
4510
- this.events?.emit("memory.consolidated", {
4511
- scope,
4512
- removed
4513
- });
4514
- await this.mirrorBackup(scope);
4515
- await this._recalcTrackedByteSize(scope);
4516
- }
4517
- });
4518
- }
4519
- async clear(scope) {
4520
- if (scope) {
4521
- await this.runSerialized(scope, async () => {
4522
- const filePath = this.files[scope];
4523
- const t0 = Date.now();
4524
- try {
4525
- await this.backend.clear(scope, filePath);
4526
- this.invalidateScoreCaches();
4527
- const dur = Date.now() - t0;
4528
- this.events?.emit("storage.write", {
4529
- sessionId: "~memory~",
4530
- store: "memory",
4531
- filePath,
4532
- operation: "clear",
4533
- outcome: "success",
4534
- durationMs: dur,
4535
- ...this.traceId !== void 0 && { traceId: this.traceId }
4536
- });
4537
- } catch (err) {
4538
- const dur = Date.now() - t0;
4539
- this.events?.emit("storage.write", {
4540
- sessionId: "~memory~",
4541
- store: "memory",
4542
- filePath,
4543
- operation: "clear",
4544
- outcome: "failure",
4545
- durationMs: dur,
4546
- error: toErrorMessage(err),
4547
- ...this.traceId !== void 0 && { traceId: this.traceId }
4548
- });
4549
- throw err;
4550
- }
4551
- this.events?.emit("memory.cleared", { scope });
4552
- await this.mirrorBackup(scope);
4553
- this._trackedByteSizes[scope] = 0;
4554
- });
4555
- return;
4556
- }
4557
- await Promise.all(
4558
- ["project-agents", "project-memory", "user-memory"].map(
4559
- async (s) => this.runSerialized(s, async () => {
4560
- const filePath = this.files[s];
4561
- const t0 = Date.now();
4562
- try {
4563
- await this.backend.clear(s, filePath);
4564
- const dur = Date.now() - t0;
4565
- this.events?.emit("storage.write", {
4566
- sessionId: "~memory~",
4567
- store: "memory",
4568
- filePath,
4569
- operation: "clear",
4570
- outcome: "success",
4571
- durationMs: dur,
4572
- ...this.traceId !== void 0 && { traceId: this.traceId }
4573
- });
4574
- } catch (err) {
4575
- const dur = Date.now() - t0;
4576
- this.events?.emit("storage.write", {
4577
- sessionId: "~memory~",
4578
- store: "memory",
4579
- filePath,
4580
- operation: "clear",
4581
- outcome: "failure",
4582
- durationMs: dur,
4583
- error: toErrorMessage(err),
4584
- ...this.traceId !== void 0 && { traceId: this.traceId }
4585
- });
4586
- throw err;
4587
- }
4588
- this.events?.emit("memory.cleared", { scope: s });
4589
- await this.mirrorBackup(s);
4590
- })
4591
- )
4592
- );
4593
- this.invalidateScoreCaches();
4594
- }
4595
- /**
4596
- * Return a new MemoryStore proxy that carries `traceId` on every storage
4597
- * event. The original store is left unchanged — callers that need a
4598
- * trace-decorated view (e.g. session-run tools) receive the proxy while
4599
- * the singleton remains trace-free for boot-time use.
4600
- *
4601
- * The proxy implements the full `MemoryStore` interface; all other
4602
- * properties (backend, etc.) are delegated to the original store.
4603
- */
4604
- withTraceId(traceId) {
4605
- this.traceId = traceId;
4606
- return this;
4607
- }
4608
- async mirrorBackup(scope) {
4609
- if (!this.persistBackup || scope === "project-agents") return;
4610
- try {
4611
- const content = await this.backend.readAll(scope, this.files[scope]);
4612
- const { writeFile: writeFile7, mkdir: mkdir7 } = await import("node:fs/promises");
4613
- await mkdir7(this.backupDir, { recursive: true });
4614
- await writeFile7(`${this.backupDir}/${scope}.md`, content, "utf8");
4615
- } catch {
4616
- }
4617
- }
4618
- };
4619
- function labelOf(scope) {
4620
- switch (scope) {
4621
- case "project-agents":
4622
- return "Project AGENTS.md";
4623
- case "project-memory":
4624
- return "Project memory";
4625
- case "user-memory":
4626
- return "User memory";
4627
- }
4628
- }
4629
-
4630
4122
  // src/storage/memory-graph-backend.ts
4631
- import * as fs3 from "node:fs/promises";
4123
+ import * as fs4 from "node:fs/promises";
4632
4124
  var GraphMemoryBackend = class _GraphMemoryBackend {
4633
4125
  kind = "graph";
4634
4126
  file;
@@ -4796,7 +4288,7 @@ var GraphMemoryBackend = class _GraphMemoryBackend {
4796
4288
  this.loadedScope = scope;
4797
4289
  this.loaded = true;
4798
4290
  try {
4799
- await fs3.unlink(this.graphFile);
4291
+ await fs4.unlink(this.graphFile);
4800
4292
  } catch {
4801
4293
  }
4802
4294
  }
@@ -4836,7 +4328,7 @@ var GraphMemoryBackend = class _GraphMemoryBackend {
4836
4328
  async loadGraph(scope) {
4837
4329
  if (this.loaded && this.loadedScope === scope) return;
4838
4330
  try {
4839
- const raw = await fs3.readFile(this.graphFile, "utf8");
4331
+ const raw = await fs4.readFile(this.graphFile, "utf8");
4840
4332
  const data = JSON.parse(raw);
4841
4333
  this.nodes = new Map(data.nodes);
4842
4334
  this.edges = data.edges;
@@ -4858,10 +4350,10 @@ var GraphMemoryBackend = class _GraphMemoryBackend {
4858
4350
  edges: this.edges
4859
4351
  };
4860
4352
  const dir = this.graphFile.substring(0, this.graphFile.lastIndexOf("/"));
4861
- await fs3.mkdir(dir, { recursive: true });
4353
+ await fs4.mkdir(dir, { recursive: true });
4862
4354
  const tmp = `${this.graphFile}.tmp`;
4863
- await fs3.writeFile(tmp, JSON.stringify(data));
4864
- await fs3.rename(tmp, this.graphFile);
4355
+ await fs4.writeFile(tmp, JSON.stringify(data));
4356
+ await fs4.rename(tmp, this.graphFile);
4865
4357
  } catch {
4866
4358
  }
4867
4359
  }
@@ -4979,7 +4471,7 @@ function simpleHash(s) {
4979
4471
  }
4980
4472
 
4981
4473
  // src/utils/instruction-file.ts
4982
- import { readFileSync, statSync } from "node:fs";
4474
+ import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
4983
4475
  import * as path11 from "node:path";
4984
4476
  import { fileURLToPath } from "node:url";
4985
4477
  var textCache = /* @__PURE__ */ new Map();
@@ -4990,7 +4482,7 @@ function readBundledInstructionText(relativePath) {
4990
4482
  let resolved = "";
4991
4483
  for (const root of instructionRootCandidates()) {
4992
4484
  try {
4993
- resolved = readFileSync(path11.join(root, relativePath), "utf8").trimEnd();
4485
+ resolved = readFileSync2(path11.join(root, relativePath), "utf8").trimEnd();
4994
4486
  break;
4995
4487
  } catch {
4996
4488
  }
@@ -5017,7 +4509,7 @@ function instructionRootCandidates() {
5017
4509
  }
5018
4510
  function isDirectory(candidate) {
5019
4511
  try {
5020
- return statSync(candidate).isDirectory();
4512
+ return statSync2(candidate).isDirectory();
5021
4513
  } catch {
5022
4514
  return false;
5023
4515
  }
@@ -5043,12 +4535,14 @@ var SessionMemoryConsolidator = class {
5043
4535
  model;
5044
4536
  minIterations;
5045
4537
  maxExistingEntries;
4538
+ oneShotOrchestrator;
5046
4539
  constructor(opts) {
5047
4540
  this.memoryStore = opts.memoryStore;
5048
4541
  this.provider = opts.provider;
5049
4542
  this.model = opts.model;
5050
4543
  this.minIterations = opts.minIterations ?? 2;
5051
4544
  this.maxExistingEntries = opts.maxExistingEntries ?? 15;
4545
+ this.oneShotOrchestrator = opts.oneShotOrchestrator;
5052
4546
  }
5053
4547
  afterRun = (ctx, result) => {
5054
4548
  if (result.status !== "done") return;
@@ -5067,19 +4561,32 @@ var SessionMemoryConsolidator = class {
5067
4561
  _iterations,
5068
4562
  existingEntries
5069
4563
  );
5070
- const signal = AbortSignal.timeout(15e3);
5071
- const response = await provider.complete(
5072
- {
5073
- model: _model,
5074
- system: [{ type: "text", text: prompt }],
5075
- messages: [
5076
- { role: "user", content: "Review the session and return memory operations as JSON." }
5077
- ],
5078
- maxTokens: 500
5079
- },
5080
- { signal }
5081
- );
5082
- const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
4564
+ let text;
4565
+ if (this.oneShotOrchestrator) {
4566
+ const oneShotResult = await this.oneShotOrchestrator.call({
4567
+ system: prompt,
4568
+ userPrompt: "Review the session and return memory operations as JSON.",
4569
+ model: _model ?? "deepseek-chat",
4570
+ maxTokens: 500,
4571
+ timeoutMs: 15e3,
4572
+ fallbackProfile: "memory"
4573
+ });
4574
+ text = oneShotResult.text;
4575
+ } else {
4576
+ const signal = AbortSignal.timeout(15e3);
4577
+ const response = await provider.complete(
4578
+ {
4579
+ model: _model,
4580
+ system: [{ type: "text", text: prompt }],
4581
+ messages: [
4582
+ { role: "user", content: "Review the session and return memory operations as JSON." }
4583
+ ],
4584
+ maxTokens: 500
4585
+ },
4586
+ { signal }
4587
+ );
4588
+ text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
4589
+ }
5083
4590
  if (!text) return;
5084
4591
  const jsonMatch = text.match(/\{[\s\S]*\}/);
5085
4592
  if (!jsonMatch) return;
@@ -5209,7 +4716,7 @@ function deepFreeze(obj) {
5209
4716
 
5210
4717
  // src/storage/provider-config-watcher.ts
5211
4718
  import * as syncFs from "node:fs";
5212
- import * as fs4 from "node:fs/promises";
4719
+ import * as fs5 from "node:fs/promises";
5213
4720
  import * as path12 from "node:path";
5214
4721
 
5215
4722
  // src/security/config-secrets.ts
@@ -5244,7 +4751,7 @@ function walk(node, vault, transform) {
5244
4751
  }
5245
4752
  return out;
5246
4753
  }
5247
- var SECRET_KEY_PATTERN = /(?:apikey|api_key|authtoken|auth_token|bearer|secret|password|passwd|pwd|refreshtoken|refresh_token|sessionkey|session_key|access[_-]?token|private[_-]?key)/i;
4754
+ var SECRET_KEY_PATTERN = /(?:apikey|api_key|authtoken|auth_token|bearer|secret|password|passwd|pwd|refreshtoken|refresh_token|sessionkey|session_key|access[_-]?token|private[_-]?key|token\b)/i;
5248
4755
  var NON_SECRET_OVERRIDES = /* @__PURE__ */ new Set(["publickey", "public_key"]);
5249
4756
  function isSecretField(name) {
5250
4757
  const lc = name.toLowerCase();
@@ -5256,7 +4763,7 @@ function isSecretField(name) {
5256
4763
  async function readProviderSnapshot(configPath, vault, warn) {
5257
4764
  let raw;
5258
4765
  try {
5259
- raw = await fs4.readFile(configPath, "utf8");
4766
+ raw = await fs5.readFile(configPath, "utf8");
5260
4767
  } catch (err) {
5261
4768
  if (err.code !== "ENOENT") {
5262
4769
  warn?.(`Could not read ${configPath}: ${err.message}`);
@@ -5345,24 +4852,24 @@ function watchProviderConfig(configPath, vault, onChange, opts = {}) {
5345
4852
  }
5346
4853
 
5347
4854
  // src/storage/config-loader.ts
5348
- import * as fs5 from "node:fs/promises";
4855
+ import * as fs6 from "node:fs/promises";
5349
4856
  import * as path13 from "node:path";
5350
4857
 
5351
4858
  // src/types/config.ts
5352
4859
  var DEFAULT_TUI_THINKING_WORD = "thinking";
5353
4860
 
5354
4861
  // src/types/context-window.ts
5355
- var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
4862
+ var DEFAULT_CONTEXT_WINDOW_MODE_ID = "frugal";
5356
4863
  var CONTEXT_WINDOW_MODES = Object.freeze([
5357
4864
  {
5358
4865
  id: "balanced",
5359
4866
  name: "Balanced",
5360
4867
  description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
5361
- thresholds: { warn: 0.6, soft: 0.75, hard: 0.9 },
4868
+ thresholds: { warn: 0.5, soft: 0.65, hard: 0.8 },
5362
4869
  aggressiveOn: "soft",
5363
- preserveK: 10,
5364
- eliseThreshold: 2e3,
5365
- targetLoad: 0.65
4870
+ preserveK: 8,
4871
+ eliseThreshold: 1e3,
4872
+ targetLoad: 0.55
5366
4873
  },
5367
4874
  {
5368
4875
  id: "frugal",
@@ -5413,7 +4920,7 @@ var DEFAULT_TOOLS_CONFIG = Object.freeze({
5413
4920
  descriptionMode: Object.freeze({}),
5414
4921
  disabledTools: Object.freeze([]),
5415
4922
  autoExtendLimit: true,
5416
- restrictToProjectRoot: false,
4923
+ restrictToProjectRoot: true,
5417
4924
  loopDetection: Object.freeze({
5418
4925
  mode: "steer-then-cut",
5419
4926
  steerThreshold: 3,
@@ -5423,8 +4930,8 @@ var DEFAULT_TOOLS_CONFIG = Object.freeze({
5423
4930
  })
5424
4931
  });
5425
4932
  var DEFAULT_CONTEXT_CONFIG = Object.freeze({
5426
- preserveK: 10,
5427
- eliseThreshold: 2e3
4933
+ preserveK: 8,
4934
+ eliseThreshold: 1e3
5428
4935
  });
5429
4936
  var DEFAULT_AUTONOMY_CONFIG = Object.freeze({
5430
4937
  autoProceedDelayMs: 45e3
@@ -5516,12 +5023,11 @@ var BEHAVIOR_DEFAULTS = {
5516
5023
  mcpServers: {},
5517
5024
  fallbackAuto: true,
5518
5025
  maxConcurrent: 4,
5519
- // YOLO on by default: auto-approve non-denied tool calls without a per-call
5520
- // prompt. Users
5521
- // who want the per-call prompts set `yolo: false`. Existing installs whose
5522
- // config lacks this key pick it up via fillMissingDefaults; an explicit
5523
- // `false` is preserved by the deep-merge (opt-out respected).
5524
- yolo: true,
5026
+ // YOLO opt-in: users who want auto-approve behaviour set `yolo: true`.
5027
+ // Off by default so prompt injection in repository content, fetched
5028
+ // content, or mailbox messages cannot induce arbitrary shell calls or
5029
+ // write operations without human confirmation.
5030
+ yolo: false,
5525
5031
  nextPrediction: false,
5526
5032
  hints: true,
5527
5033
  debugStream: false,
@@ -5546,7 +5052,7 @@ var BEHAVIOR_DEFAULTS = {
5546
5052
  terminalTitleAnimation: true,
5547
5053
  // Mirrored from the top-level yolo default so the autonomy subsystem
5548
5054
  // (which reads autonomy.yolo) stays consistent with config.yolo.
5549
- yolo: true,
5055
+ yolo: false,
5550
5056
  streamFleet: true,
5551
5057
  chime: false,
5552
5058
  confirmExit: true,
@@ -5998,7 +5504,7 @@ var DefaultConfigLoader = class {
5998
5504
  await withFileLock(fp, async () => {
5999
5505
  let parsed;
6000
5506
  try {
6001
- const raw = await fs5.readFile(fp, "utf8");
5507
+ const raw = await fs6.readFile(fp, "utf8");
6002
5508
  const result = safeParse(raw);
6003
5509
  if (!result.ok || !isPlainRecord(result.value)) {
6004
5510
  return;
@@ -6107,7 +5613,7 @@ var DefaultConfigLoader = class {
6107
5613
  const fp = this.paths.syncConfig;
6108
5614
  const t0 = Date.now();
6109
5615
  try {
6110
- const raw = await fs5.readFile(fp, "utf8");
5616
+ const raw = await fs6.readFile(fp, "utf8");
6111
5617
  const parsed = safeParse(raw);
6112
5618
  if (!parsed.ok || !parsed.value) {
6113
5619
  this.events?.emit("storage.read", {
@@ -6169,7 +5675,7 @@ var DefaultConfigLoader = class {
6169
5675
  const t0 = Date.now();
6170
5676
  let mtimeMs = null;
6171
5677
  try {
6172
- const stat13 = await fs5.stat(file);
5678
+ const stat13 = await fs6.stat(file);
6173
5679
  mtimeMs = stat13.mtimeMs;
6174
5680
  const cached = this.jsonCache.get(file);
6175
5681
  if (cached && cached.mtimeMs === mtimeMs) {
@@ -6198,7 +5704,7 @@ var DefaultConfigLoader = class {
6198
5704
  }
6199
5705
  let raw;
6200
5706
  try {
6201
- raw = await fs5.readFile(file, "utf8");
5707
+ raw = await fs6.readFile(file, "utf8");
6202
5708
  } catch (err) {
6203
5709
  if (err.code !== "ENOENT") {
6204
5710
  this.events?.emit("storage.read", {
@@ -6500,7 +6006,7 @@ function defaultIsPidAlive(pid) {
6500
6006
  }
6501
6007
 
6502
6008
  // src/storage/session-reader.ts
6503
- import * as fs6 from "node:fs/promises";
6009
+ import * as fs7 from "node:fs/promises";
6504
6010
  var DefaultSessionReader = class _DefaultSessionReader {
6505
6011
  store;
6506
6012
  eventCache = /* @__PURE__ */ new Map();
@@ -6518,7 +6024,7 @@ var DefaultSessionReader = class _DefaultSessionReader {
6518
6024
  const sessionPath = sessionScopedPath(rootDir, sessionId, ".jsonl");
6519
6025
  let mtimeMs = null;
6520
6026
  try {
6521
- const stat13 = await fs6.stat(sessionPath);
6027
+ const stat13 = await fs7.stat(sessionPath);
6522
6028
  mtimeMs = stat13.mtimeMs;
6523
6029
  } catch {
6524
6030
  this.eventCache.delete(sessionId);
@@ -6882,7 +6388,7 @@ function renderPlainText(meta, events) {
6882
6388
 
6883
6389
  // src/storage/annotations-store.ts
6884
6390
  import { randomUUID as randomUUID3 } from "node:crypto";
6885
- import * as fs7 from "node:fs/promises";
6391
+ import * as fs8 from "node:fs/promises";
6886
6392
  var FILE_VERSION = 1;
6887
6393
  var MAX_TEXT_LENGTH = 2e3;
6888
6394
  var MAX_ANNOTATIONS = 1e3;
@@ -7109,7 +6615,7 @@ var AnnotationsStore = class {
7109
6615
  const fp = this.filePath(sessionId);
7110
6616
  let raw;
7111
6617
  try {
7112
- raw = await fs7.readFile(fp, "utf8");
6618
+ raw = await fs8.readFile(fp, "utf8");
7113
6619
  } catch (err) {
7114
6620
  if (err.code === "ENOENT") return null;
7115
6621
  throw err;
@@ -7146,7 +6652,7 @@ var AnnotationsStore = class {
7146
6652
  };
7147
6653
 
7148
6654
  // src/storage/replay-log-store.ts
7149
- import * as fs8 from "node:fs/promises";
6655
+ import * as fs9 from "node:fs/promises";
7150
6656
  import * as path15 from "node:path";
7151
6657
 
7152
6658
  // src/replay/hash.ts
@@ -7236,12 +6742,12 @@ var ReplayLogStore = class {
7236
6742
  const line = JSON.stringify(entry) + "\n";
7237
6743
  let offset2 = 0;
7238
6744
  try {
7239
- const stat13 = await fs8.stat(fp);
6745
+ const stat13 = await fs9.stat(fp);
7240
6746
  offset2 = stat13.size;
7241
6747
  } catch (err) {
7242
6748
  if (err.code !== "ENOENT") throw err;
7243
6749
  }
7244
- await fs8.appendFile(fp, line, "utf8");
6750
+ await fs9.appendFile(fp, line, "utf8");
7245
6751
  cache.set(hash, { entry, offset: offset2, length: Buffer.byteLength(line, "utf8") });
7246
6752
  this.diskCount.set(input.sessionId, currentCount + 1);
7247
6753
  this.events?.emit("storage.write", {
@@ -7369,7 +6875,7 @@ var ReplayLogStore = class {
7369
6875
  const scan = async (dir, prefix, depth) => {
7370
6876
  let entries;
7371
6877
  try {
7372
- entries = await fs8.readdir(dir, { withFileTypes: true });
6878
+ entries = await fs9.readdir(dir, { withFileTypes: true });
7373
6879
  } catch (err) {
7374
6880
  if (depth === 0 && err.code !== "ENOENT") {
7375
6881
  console.warn(JSON.stringify({
@@ -7407,7 +6913,7 @@ var ReplayLogStore = class {
7407
6913
  return sessionScopedPath(this.dir, sessionId, ".replay.jsonl");
7408
6914
  }
7409
6915
  async countEntries(filePath) {
7410
- const handle = await fs8.open(filePath, "r");
6916
+ const handle = await fs9.open(filePath, "r");
7411
6917
  const buffer = Buffer.allocUnsafe(64 * 1024);
7412
6918
  let count = 0;
7413
6919
  let hasNonWhitespace2 = false;
@@ -7448,7 +6954,7 @@ var ReplayLogStore = class {
7448
6954
  async readAll(sessionId) {
7449
6955
  const fp = this.filePath(sessionId);
7450
6956
  try {
7451
- const raw = await fs8.readFile(fp, "utf8");
6957
+ const raw = await fs9.readFile(fp, "utf8");
7452
6958
  const out = [];
7453
6959
  for (const line of raw.split("\n")) {
7454
6960
  if (!line.trim()) continue;
@@ -7484,7 +6990,7 @@ var ReplayLogStore = class {
7484
6990
  const fp = this.filePath(sessionId);
7485
6991
  cache = /* @__PURE__ */ new Map();
7486
6992
  try {
7487
- const handle = await fs8.open(fp, "r");
6993
+ const handle = await fs9.open(fp, "r");
7488
6994
  const CHUNK = 64 * 1024;
7489
6995
  const buffer = Buffer.alloc(CHUNK);
7490
6996
  let leftover = "";
@@ -7535,7 +7041,7 @@ var ReplayLogStore = class {
7535
7041
  async hydrateEntry(sessionId, hash, location) {
7536
7042
  if (location.entry) return location.entry;
7537
7043
  const fp = this.filePath(sessionId);
7538
- const handle = await fs8.open(fp, "r");
7044
+ const handle = await fs9.open(fp, "r");
7539
7045
  try {
7540
7046
  const buffer = Buffer.alloc(location.length);
7541
7047
  const { bytesRead } = await handle.read(buffer, 0, location.length, location.offset);
@@ -7562,7 +7068,7 @@ var ReplayLogStore = class {
7562
7068
  };
7563
7069
 
7564
7070
  // src/storage/session-recovery.ts
7565
- import * as fs9 from "node:fs/promises";
7071
+ import * as fs10 from "node:fs/promises";
7566
7072
  import * as path16 from "node:path";
7567
7073
  var SessionRecovery = class {
7568
7074
  constructor(dir) {
@@ -7589,7 +7095,7 @@ var SessionRecovery = class {
7589
7095
  const fp = this.filePath(sessionId);
7590
7096
  let stat13;
7591
7097
  try {
7592
- stat13 = await fs9.stat(fp);
7098
+ stat13 = await fs10.stat(fp);
7593
7099
  } catch (err) {
7594
7100
  if (err.code === "ENOENT") return null;
7595
7101
  return null;
@@ -7623,7 +7129,7 @@ var SessionRecovery = class {
7623
7129
  const fp = this.filePath(sessionId);
7624
7130
  let raw;
7625
7131
  try {
7626
- raw = await fs9.readFile(fp, "utf8");
7132
+ raw = await fs10.readFile(fp, "utf8");
7627
7133
  } catch (err) {
7628
7134
  if (err.code === "ENOENT") return null;
7629
7135
  return null;
@@ -7668,7 +7174,7 @@ var SessionRecovery = class {
7668
7174
  const collect = async (dir, prefix, depth) => {
7669
7175
  let entries;
7670
7176
  try {
7671
- entries = await fs9.readdir(dir, { withFileTypes: true });
7177
+ entries = await fs10.readdir(dir, { withFileTypes: true });
7672
7178
  } catch {
7673
7179
  return;
7674
7180
  }
@@ -7719,7 +7225,7 @@ function hasNonWhitespace(line) {
7719
7225
  }
7720
7226
  async function scanLatestLifecycleBoundary(filePath, size) {
7721
7227
  const CHUNK_SIZE = 64 * 1024;
7722
- const handle = await fs9.open(filePath, "r");
7228
+ const handle = await fs10.open(filePath, "r");
7723
7229
  let position = size;
7724
7230
  let laterLineFragment = Buffer.alloc(0);
7725
7231
  let latestBoundary = null;
@@ -7758,7 +7264,7 @@ async function scanLatestLifecycleBoundary(filePath, size) {
7758
7264
 
7759
7265
  // src/storage/tool-audit-log.ts
7760
7266
  import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
7761
- import * as fs10 from "node:fs/promises";
7267
+ import * as fs11 from "node:fs/promises";
7762
7268
  var GENESIS_PREV = "0".repeat(64);
7763
7269
  var DEFAULT_FSYNC_EVERY = 100;
7764
7270
  var ToolAuditLog = class {
@@ -7828,9 +7334,9 @@ var ToolAuditLog = class {
7828
7334
  isError: input.isError,
7829
7335
  index
7830
7336
  };
7831
- await fs10.appendFile(fp, JSON.stringify(entry) + "\n", "utf8");
7337
+ await fs11.appendFile(fp, JSON.stringify(entry) + "\n", "utf8");
7832
7338
  try {
7833
- const st = await fs10.stat(fp);
7339
+ const st = await fs11.stat(fp);
7834
7340
  this.tailStat.set(input.sessionId, { mtimeMs: st.mtimeMs, size: st.size });
7835
7341
  } catch {
7836
7342
  }
@@ -7877,7 +7383,7 @@ var ToolAuditLog = class {
7877
7383
  const cachedStat = this.tailStat.get(sessionId);
7878
7384
  if (cachedHash !== void 0 && cachedIndex !== void 0 && cachedStat) {
7879
7385
  try {
7880
- const st = await fs10.stat(fp);
7386
+ const st = await fs11.stat(fp);
7881
7387
  if (st.mtimeMs === cachedStat.mtimeMs && st.size === cachedStat.size) {
7882
7388
  return { prevHash: cachedHash, nextIndex: cachedIndex };
7883
7389
  }
@@ -7898,7 +7404,7 @@ var ToolAuditLog = class {
7898
7404
  this.tailHash.set(sessionId, prevHash);
7899
7405
  this.tailIndex.set(sessionId, nextIndex);
7900
7406
  try {
7901
- const st = await fs10.stat(fp);
7407
+ const st = await fs11.stat(fp);
7902
7408
  this.tailStat.set(sessionId, { mtimeMs: st.mtimeMs, size: st.size });
7903
7409
  } catch {
7904
7410
  }
@@ -8019,7 +7525,7 @@ var ToolAuditLog = class {
8019
7525
  async readAll(sessionId) {
8020
7526
  const fp = this.filePath(sessionId);
8021
7527
  try {
8022
- const raw = await fs10.readFile(fp, "utf8");
7528
+ const raw = await fs11.readFile(fp, "utf8");
8023
7529
  const out = [];
8024
7530
  for (const line of raw.split("\n")) {
8025
7531
  if (!line.trim()) continue;
@@ -8057,7 +7563,7 @@ var ToolAuditLog = class {
8057
7563
  }
8058
7564
  async sync(sessionId, fp) {
8059
7565
  try {
8060
- const fh = await fs10.open(fp, "r+");
7566
+ const fh = await fs11.open(fp, "r+");
8061
7567
  try {
8062
7568
  await fh.sync();
8063
7569
  } finally {
@@ -8195,7 +7701,7 @@ var SessionAnalyzer = class {
8195
7701
  };
8196
7702
 
8197
7703
  // src/session-registry.ts
8198
- import * as fs11 from "node:fs/promises";
7704
+ import * as fs12 from "node:fs/promises";
8199
7705
  import * as path17 from "node:path";
8200
7706
  import { randomUUID as randomUUID5 } from "node:crypto";
8201
7707
  var REGISTRY_FILE = "session-registry.json";
@@ -8392,7 +7898,7 @@ var SessionRegistry = class {
8392
7898
  }
8393
7899
  async readAndPrune() {
8394
7900
  try {
8395
- const raw = await fs11.readFile(this.filePath, "utf8");
7901
+ const raw = await fs12.readFile(this.filePath, "utf8");
8396
7902
  const registry = parseRegistry(raw);
8397
7903
  const now = Date.now();
8398
7904
  let pruned = false;
@@ -8445,11 +7951,11 @@ var SessionRegistry = class {
8445
7951
  const retryDelayMs = 20;
8446
7952
  for (let attempt = 0; attempt < maxRetries; attempt++) {
8447
7953
  try {
8448
- await fs11.mkdir(path17.dirname(this.filePath), { recursive: true });
8449
- let lockHandle = await fs11.open(lockPath, "wx").catch(() => null);
7954
+ await fs12.mkdir(path17.dirname(this.filePath), { recursive: true });
7955
+ let lockHandle = await fs12.open(lockPath, "wx").catch(() => null);
8450
7956
  if (!lockHandle) {
8451
7957
  if (await this.breakStaleLock(lockPath)) {
8452
- lockHandle = await fs11.open(lockPath, "wx").catch(() => null);
7958
+ lockHandle = await fs12.open(lockPath, "wx").catch(() => null);
8453
7959
  }
8454
7960
  if (!lockHandle) {
8455
7961
  await new Promise((r) => setTimeout(r, retryDelayMs * (attempt + 1)));
@@ -8458,14 +7964,14 @@ var SessionRegistry = class {
8458
7964
  }
8459
7965
  try {
8460
7966
  await lockHandle.writeFile(String(process.pid)).catch(() => void 0);
8461
- const raw = await fs11.readFile(this.filePath, "utf8").catch(() => "{}");
7967
+ const raw = await fs12.readFile(this.filePath, "utf8").catch(() => "{}");
8462
7968
  const registry = parseRegistry(raw);
8463
7969
  fn(registry);
8464
7970
  await this.writeAtomicLocked(registry);
8465
7971
  return;
8466
7972
  } finally {
8467
7973
  await lockHandle.close();
8468
- await fs11.unlink(lockPath).catch(() => void 0);
7974
+ await fs12.unlink(lockPath).catch(() => void 0);
8469
7975
  }
8470
7976
  } catch {
8471
7977
  return;
@@ -8482,16 +7988,16 @@ var SessionRegistry = class {
8482
7988
  async breakStaleLock(lockPath) {
8483
7989
  try {
8484
7990
  const [stat13, content] = await Promise.all([
8485
- fs11.stat(lockPath),
7991
+ fs12.stat(lockPath),
8486
7992
  /* v8 ignore start -- best-effort lock-content read; .catch only fires if the lock vanished */
8487
- fs11.readFile(lockPath, "utf8").catch(() => "")
7993
+ fs12.readFile(lockPath, "utf8").catch(() => "")
8488
7994
  /* v8 ignore stop */
8489
7995
  ]);
8490
7996
  const ageMs = Date.now() - stat13.mtimeMs;
8491
7997
  const ownerPid = Number.parseInt(content.trim(), 10);
8492
7998
  const ownerDead = Number.isInteger(ownerPid) && ownerPid > 0 && ownerPid !== process.pid && !pidAlive(ownerPid);
8493
7999
  if (ownerDead || ageMs > STALE_LOCK_MS) {
8494
- await fs11.unlink(lockPath).catch(() => void 0);
8000
+ await fs12.unlink(lockPath).catch(() => void 0);
8495
8001
  return true;
8496
8002
  }
8497
8003
  return false;
@@ -8514,16 +8020,16 @@ var SessionRegistry = class {
8514
8020
  `.${path17.basename(this.filePath)}.${randomUUID5().slice(0, 8)}.tmp`
8515
8021
  );
8516
8022
  try {
8517
- const handle = await fs11.open(tmp, "w");
8023
+ const handle = await fs12.open(tmp, "w");
8518
8024
  try {
8519
8025
  await handle.writeFile(JSON.stringify(registry, null, 2), "utf8");
8520
8026
  await handle.sync().catch(() => void 0);
8521
8027
  } finally {
8522
8028
  await handle.close();
8523
8029
  }
8524
- await fs11.rename(tmp, this.filePath);
8030
+ await fs12.rename(tmp, this.filePath);
8525
8031
  } catch (err) {
8526
- await fs11.unlink(tmp).catch(() => void 0);
8032
+ await fs12.unlink(tmp).catch(() => void 0);
8527
8033
  throw err;
8528
8034
  }
8529
8035
  }
@@ -8533,17 +8039,17 @@ var SessionRegistry = class {
8533
8039
  const base = path17.basename(this.filePath);
8534
8040
  const now = Date.now();
8535
8041
  const stale = [];
8536
- for (const name of await fs11.readdir(dir)) {
8042
+ for (const name of await fs12.readdir(dir)) {
8537
8043
  const isTemp = (name.startsWith(`${base}.`) || name.startsWith(`.${base}.`)) && name.endsWith(".tmp");
8538
8044
  if (!isTemp) continue;
8539
- const stat13 = await fs11.stat(path17.join(dir, name)).catch(() => null);
8045
+ const stat13 = await fs12.stat(path17.join(dir, name)).catch(() => null);
8540
8046
  if (!stat13) continue;
8541
8047
  if (now - stat13.mtimeMs > STALE_TMP_MS) stale.push({ name, mtimeMs: stat13.mtimeMs });
8542
8048
  }
8543
8049
  stale.sort((a, b) => b.mtimeMs - a.mtimeMs);
8544
8050
  await Promise.all(
8545
8051
  stale.slice(MAX_STALE_TMP_FILES).map(async ({ name }) => {
8546
- await fs11.unlink(path17.join(dir, name)).catch(() => void 0);
8052
+ await fs12.unlink(path17.join(dir, name)).catch(() => void 0);
8547
8053
  })
8548
8054
  );
8549
8055
  } catch {
@@ -8959,7 +8465,7 @@ var AgentStatusTracker = class {
8959
8465
  };
8960
8466
 
8961
8467
  // src/fleet-notifier.ts
8962
- import * as fs12 from "node:fs/promises";
8468
+ import * as fs13 from "node:fs/promises";
8963
8469
  import * as path18 from "node:path";
8964
8470
  var INSTANCES_FILE = "webui-instances.json";
8965
8471
  var DISCOVERY_TTL_MS = 2500;
@@ -9022,7 +8528,7 @@ var FleetNotifier = class {
9022
8528
  }
9023
8529
  async discover() {
9024
8530
  try {
9025
- const raw = await fs12.readFile(path18.join(this.baseDir, INSTANCES_FILE), "utf8");
8531
+ const raw = await fs13.readFile(path18.join(this.baseDir, INSTANCES_FILE), "utf8");
9026
8532
  const data = JSON.parse(raw);
9027
8533
  const list = Array.isArray(data?.instances) ? data.instances : [];
9028
8534
  return list.filter((i) => i && typeof i.httpPort === "number").filter((i) => i.pid !== this.selfPid).filter((i) => normRoot(i.projectRoot) === this.projectRoot).filter((i) => pidAlive2(i.pid)).map((i) => {
@@ -10327,9 +9833,204 @@ function computeTrend(history) {
10327
9833
  return "steady";
10328
9834
  }
10329
9835
 
9836
+ // src/storage/goal-kanban.ts
9837
+ import {
9838
+ addColumn,
9839
+ addTask,
9840
+ createBoard,
9841
+ getBoard,
9842
+ listBoards,
9843
+ removeBoard
9844
+ } from "@wrongstack/kanban";
9845
+ var GOAL_BOARD_TAG_PREFIX = "goal:";
9846
+ var DEFAULT_COLUMNS = [
9847
+ { title: "Backlog", order: 0 },
9848
+ { title: "In Progress", order: 1 },
9849
+ { title: "Done", order: 2 }
9850
+ ];
9851
+ async function createGoalKanbanBoard(projectRoot, goalFile) {
9852
+ if (!projectRoot) return null;
9853
+ const existingId = goalFile.kanbanBoardId;
9854
+ if (existingId) {
9855
+ const existing = await getBoard(projectRoot, existingId).catch(() => null);
9856
+ if (existing) return existingId;
9857
+ }
9858
+ const displayGoal = (goalFile.refinedGoal || goalFile.goal).replace(/\s+/g, " ").trim();
9859
+ const titleSuffix = displayGoal.length > 80 ? displayGoal.slice(0, 77) + "\u2026" : displayGoal;
9860
+ const board = await createBoard(projectRoot, {
9861
+ title: `\u{1F3AF} ${titleSuffix}`,
9862
+ description: `Kanban board auto-created for goal: ${displayGoal}`,
9863
+ tags: [goalTag(goalFile)]
9864
+ });
9865
+ const backlogCol = board.columns.find(
9866
+ (c) => ["backlog", "todo", "open"].includes(c.id.toLowerCase())
9867
+ );
9868
+ const existingColumnIds = new Set(board.columns.map((c) => c.id));
9869
+ for (const col of DEFAULT_COLUMNS) {
9870
+ if (!existingColumnIds.has(col.title.toLowerCase().replace(/\s+/g, "-"))) {
9871
+ const result = await addColumn(projectRoot, board.id, { title: col.title });
9872
+ if (result?.column && backlogCol) {
9873
+ }
9874
+ }
9875
+ }
9876
+ const targetColumnId = board.columns[0]?.id ?? "backlog";
9877
+ if (goalFile.deliverables && goalFile.deliverables.length > 0) {
9878
+ for (const d of goalFile.deliverables) {
9879
+ const cleaned = d.replace(/^\[[x✓]\]|✅|\(done\)\s*/i, "").trim();
9880
+ if (cleaned) {
9881
+ await addTask(projectRoot, board.id, {
9882
+ title: cleaned,
9883
+ columnId: targetColumnId,
9884
+ description: `Deliverable for goal: ${displayGoal}`,
9885
+ priority: "medium"
9886
+ }).catch(() => {
9887
+ });
9888
+ }
9889
+ }
9890
+ }
9891
+ goalFile.kanbanBoardId = board.id;
9892
+ return board.id;
9893
+ }
9894
+ async function findGoalKanbanBoard(projectRoot, boardId) {
9895
+ if (!projectRoot || !boardId) return null;
9896
+ try {
9897
+ return await getBoard(projectRoot, boardId);
9898
+ } catch {
9899
+ return null;
9900
+ }
9901
+ }
9902
+ async function deleteGoalKanbanBoard(projectRoot, boardId) {
9903
+ if (!projectRoot || !boardId) return;
9904
+ await removeBoard(projectRoot, boardId).catch(() => {
9905
+ });
9906
+ }
9907
+ async function findGoalBoardByTag(projectRoot, goalFile) {
9908
+ if (!projectRoot) return null;
9909
+ const tag = goalTag(goalFile);
9910
+ const boards = await listBoards(projectRoot);
9911
+ const matched = boards.find((b) => b.tags?.includes(tag));
9912
+ if (!matched) return null;
9913
+ return getBoard(projectRoot, matched.id).catch(() => null);
9914
+ }
9915
+ function formatGoalKanbanPreview(goalFile, boardId, taskCount) {
9916
+ const lines = [];
9917
+ const displayGoal = (goalFile.refinedGoal || goalFile.goal).replace(/\s+/g, " ").trim();
9918
+ lines.push("");
9919
+ lines.push(color.bold(color.cyan("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")));
9920
+ lines.push(color.bold(color.cyan(" \u{1F3AF} GOAL KANBAN \u2014 Mission Board")));
9921
+ lines.push(color.bold(color.cyan("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")));
9922
+ lines.push("");
9923
+ lines.push(` ${color.bold("Mission:")} ${color.bold(displayGoal)}`);
9924
+ if (boardId) {
9925
+ lines.push(` ${color.dim(`Board: ${boardId.slice(0, 12)}\u2026`)}`);
9926
+ }
9927
+ if (typeof taskCount === "number") {
9928
+ lines.push(` ${color.dim(`${taskCount} task(s)`)}`);
9929
+ }
9930
+ lines.push("");
9931
+ const deliverables = goalFile.deliverables ?? [];
9932
+ const doneDeliverables = deliverables.filter(
9933
+ (d) => /^\[[x✓]\]|✅|\(done\)/i.test(d)
9934
+ ).length;
9935
+ const totalDeliverables = deliverables.length;
9936
+ if (totalDeliverables > 0) {
9937
+ const backlog = deliverables.filter(
9938
+ (d) => !/^\[[x✓]\]|✅|\(done\)|🔄|in.progress/i.test(d)
9939
+ );
9940
+ const inProgress = deliverables.filter((d) => /🔄|in.progress/i.test(d));
9941
+ const done = deliverables.filter((d) => /^\[[x✓]\]|✅|\(done\)/i.test(d));
9942
+ const renderColumn = (title, items, icon, accent) => {
9943
+ if (items.length === 0) {
9944
+ lines.push(` ${accent(color.bold(`${icon} ${title}`))} ${color.dim("(empty)")}`);
9945
+ return;
9946
+ }
9947
+ lines.push(` ${accent(color.bold(`${icon} ${title} (${items.length})`))}`);
9948
+ for (const item of items) {
9949
+ const cleaned = item.replace(/^\[[x✓]\]|✅|\(done\)|🔄|in.progress\s*/gi, "").trim();
9950
+ lines.push(` \u25CB ${cleaned}`);
9951
+ }
9952
+ };
9953
+ renderColumn("Backlog", backlog, "\u{1F4CB}", (s) => color.dim(s));
9954
+ renderColumn("In Progress", inProgress, "\u{1F504}", (s) => color.cyan(s));
9955
+ renderColumn("Done", done, "\u2705", (s) => color.green(s));
9956
+ lines.push("");
9957
+ lines.push(
9958
+ ` ${color.dim(`Progress: ${doneDeliverables}/${totalDeliverables} deliverables complete`)}`
9959
+ );
9960
+ }
9961
+ if (typeof goalFile.progress === "number") {
9962
+ const pct = Math.min(100, Math.max(0, Math.round(goalFile.progress)));
9963
+ const filled = Math.round(pct / 5);
9964
+ const empty = 20 - filled;
9965
+ const bar = color.green("\u2588".repeat(filled)) + color.dim("\u2591".repeat(empty));
9966
+ lines.push(` ${color.dim("Overall:")} ${bar} ${color.bold(`${pct}%`)}`);
9967
+ if (goalFile.progressNote) {
9968
+ lines.push(` ${color.dim(goalFile.progressNote)}`);
9969
+ }
9970
+ }
9971
+ if (typeof goalFile.progress === "undefined" && totalDeliverables > 0) {
9972
+ const pct = Math.round(doneDeliverables / totalDeliverables * 100);
9973
+ const filled = Math.round(pct / 5);
9974
+ const empty = 20 - filled;
9975
+ const bar = (pct > 0 ? color.green("\u2588".repeat(filled)) : "") + color.dim("\u2591".repeat(empty));
9976
+ lines.push(` ${color.dim("Overall:")} ${bar} ${color.bold(`${pct}%`)}`);
9977
+ }
9978
+ lines.push("");
9979
+ lines.push(color.bold(color.cyan("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")));
9980
+ return lines.join("\n");
9981
+ }
9982
+ function formatGoalEvent(goalFile, boardId) {
9983
+ const lines = [];
9984
+ lines.push("");
9985
+ lines.push(color.bold(color.green("\u250C\u2500\u2500\u2500 Goal Event \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510")));
9986
+ lines.push(color.bold(color.green("\u2502")) + " \u{1F3AF} Mission locked and ready");
9987
+ if (boardId) {
9988
+ lines.push(
9989
+ color.bold(color.green("\u2502")) + ` \u{1F4CB} Kanban board created: ${color.cyan(boardId.slice(0, 12) + "\u2026")}`
9990
+ );
9991
+ }
9992
+ const deliverables = goalFile.deliverables ?? [];
9993
+ lines.push(
9994
+ color.bold(color.green("\u2502")) + ` \u{1F4E6} ${deliverables.length} deliverable${deliverables.length !== 1 ? "s" : ""} extracted`
9995
+ );
9996
+ if (typeof goalFile.progress === "number") {
9997
+ lines.push(
9998
+ color.bold(color.green("\u2502")) + ` \u{1F4CA} Progress: ${goalFile.progress}%`
9999
+ );
10000
+ }
10001
+ lines.push(color.bold(color.green("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518")));
10002
+ return lines.join("\n");
10003
+ }
10004
+ function formatGoalAutonomyChoice() {
10005
+ const lines = [];
10006
+ lines.push("");
10007
+ lines.push(color.bold(color.magenta("\u2550\u2550\u2550 Choose Autonomy Mode \u2550\u2550\u2550")));
10008
+ lines.push("");
10009
+ lines.push(
10010
+ ` ${color.bold("1")}. ${color.red("Autonomy Eternal")} ${color.dim("\u2014 Single-agent sense/decide/execute/reflect loop")}`
10011
+ );
10012
+ lines.push(
10013
+ ` ${color.bold("2")}. ${color.magenta("Eternal Parallel")} ${color.dim("\u2014 Multi-agent fan-out (4-8 subagents per tick)")}`
10014
+ );
10015
+ lines.push("");
10016
+ lines.push(` ${color.dim("Enter 1 or 2 (or press Enter to skip):")}`);
10017
+ return lines.join("\n");
10018
+ }
10019
+ function parseAutonomyChoice(input) {
10020
+ const trimmed = input.trim().toLowerCase();
10021
+ if (trimmed === "1" || trimmed === "eternal" || trimmed === "e") return "eternal";
10022
+ if (trimmed === "2" || trimmed === "parallel" || trimmed === "p") return "eternal-parallel";
10023
+ if (trimmed === "" || trimmed === "skip" || trimmed === "s" || trimmed === "0") return null;
10024
+ return null;
10025
+ }
10026
+ function goalTag(goalFile) {
10027
+ const goal = (goalFile.refinedGoal || goalFile.goal).replace(/\s+/g, "-").slice(0, 24).toLowerCase();
10028
+ return `${GOAL_BOARD_TAG_PREFIX}${goal}`;
10029
+ }
10030
+
10330
10031
  // src/storage/prompt-store.ts
10331
10032
  import { createHash as createHash7 } from "node:crypto";
10332
- import * as fs13 from "node:fs/promises";
10033
+ import * as fs14 from "node:fs/promises";
10333
10034
  import * as path20 from "node:path";
10334
10035
  var SCHEMA_VERSION = 2;
10335
10036
  function promptChecksum(content) {
@@ -10394,12 +10095,12 @@ var DefaultPromptStore = class {
10394
10095
  await ensureDir(this.dir);
10395
10096
  const entries = [];
10396
10097
  try {
10397
- const files = await fs13.readdir(this.dir);
10098
+ const files = await fs14.readdir(this.dir);
10398
10099
  for (const file of files) {
10399
10100
  if (!file.endsWith(".json")) continue;
10400
10101
  try {
10401
10102
  const raw = JSON.parse(
10402
- await fs13.readFile(path20.join(this.dir, file), "utf8")
10103
+ await fs14.readFile(path20.join(this.dir, file), "utf8")
10403
10104
  );
10404
10105
  const migrated = migratePromptEntry(raw.entry);
10405
10106
  if (migrated) entries.push(migrated);
@@ -10415,7 +10116,7 @@ var DefaultPromptStore = class {
10415
10116
  async get(id) {
10416
10117
  const file = path20.join(this.dir, `${id}.json`);
10417
10118
  try {
10418
- const raw = JSON.parse(await fs13.readFile(file, "utf8"));
10119
+ const raw = JSON.parse(await fs14.readFile(file, "utf8"));
10419
10120
  return migratePromptEntry(raw.entry);
10420
10121
  } catch {
10421
10122
  return null;
@@ -10430,7 +10131,7 @@ var DefaultPromptStore = class {
10430
10131
  async delete(id) {
10431
10132
  const file = path20.join(this.dir, `${id}.json`);
10432
10133
  try {
10433
- await fs13.unlink(file);
10134
+ await fs14.unlink(file);
10434
10135
  return true;
10435
10136
  } catch {
10436
10137
  return false;
@@ -10469,7 +10170,7 @@ var DefaultPromptStore = class {
10469
10170
  };
10470
10171
 
10471
10172
  // src/storage/prompt-usage-store.ts
10472
- import * as fs14 from "node:fs/promises";
10173
+ import * as fs15 from "node:fs/promises";
10473
10174
  import * as path21 from "node:path";
10474
10175
  var PromptUsageStore = class {
10475
10176
  constructor(file) {
@@ -10478,7 +10179,7 @@ var PromptUsageStore = class {
10478
10179
  file;
10479
10180
  async load() {
10480
10181
  try {
10481
- const raw = JSON.parse(await fs14.readFile(this.file, "utf8"));
10182
+ const raw = JSON.parse(await fs15.readFile(this.file, "utf8"));
10482
10183
  if (raw && typeof raw === "object" && raw.usage && typeof raw.usage === "object") {
10483
10184
  return raw.usage;
10484
10185
  }
@@ -10515,7 +10216,7 @@ var PromptUsageStore = class {
10515
10216
  };
10516
10217
 
10517
10218
  // src/storage/input-history-store.ts
10518
- import * as fs15 from "node:fs/promises";
10219
+ import * as fs16 from "node:fs/promises";
10519
10220
  import * as path22 from "node:path";
10520
10221
  var INPUT_HISTORY_DEFAULT_MAX = 100;
10521
10222
  var InputHistoryStore = class {
@@ -10538,7 +10239,7 @@ var InputHistoryStore = class {
10538
10239
  */
10539
10240
  async load() {
10540
10241
  try {
10541
- const raw = JSON.parse(await fs15.readFile(this.file, "utf8"));
10242
+ const raw = JSON.parse(await fs16.readFile(this.file, "utf8"));
10542
10243
  if (raw && typeof raw === "object" && Array.isArray(raw.entries) && raw.entries.every((e) => typeof e === "string")) {
10543
10244
  return raw.entries.slice(0, this.maxEntries);
10544
10245
  }
@@ -10595,7 +10296,7 @@ var InputHistoryStore = class {
10595
10296
  };
10596
10297
 
10597
10298
  // src/storage/cloud-sync.ts
10598
- import * as fs16 from "node:fs/promises";
10299
+ import * as fs17 from "node:fs/promises";
10599
10300
  import * as path23 from "node:path";
10600
10301
  import { createHash as createHash8 } from "node:crypto";
10601
10302
  var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
@@ -10679,7 +10380,7 @@ var CloudSync = class {
10679
10380
  lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString(),
10680
10381
  localRev: rev
10681
10382
  };
10682
- await fs16.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
10383
+ await fs17.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
10683
10384
  this.state = syncState;
10684
10385
  return {
10685
10386
  ok: true,
@@ -10711,8 +10412,8 @@ var CloudSync = class {
10711
10412
  const rel = segments.slice(2).join("/");
10712
10413
  const destPath = resolvePulledCategoryPath(cat, localPath, rel, entry.path);
10713
10414
  const blobData = await this.getBlob(token, owner, repoName, entry.sha);
10714
- await fs16.mkdir(path23.dirname(destPath), { recursive: true });
10715
- await fs16.writeFile(destPath, Buffer.from(blobData, "base64"));
10415
+ await fs17.mkdir(path23.dirname(destPath), { recursive: true });
10416
+ await fs17.writeFile(destPath, Buffer.from(blobData, "base64"));
10716
10417
  }
10717
10418
  const localRev = await this.hashLocalCategories(cfg.categories);
10718
10419
  const syncState = {
@@ -10721,7 +10422,7 @@ var CloudSync = class {
10721
10422
  lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString(),
10722
10423
  localRev
10723
10424
  };
10724
- await fs16.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
10425
+ await fs17.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
10725
10426
  this.state = syncState;
10726
10427
  return {
10727
10428
  ok: true,
@@ -10740,7 +10441,7 @@ var CloudSync = class {
10740
10441
  }
10741
10442
  async loadState() {
10742
10443
  try {
10743
- const raw = await fs16.readFile(this.statePath, "utf8");
10444
+ const raw = await fs17.readFile(this.statePath, "utf8");
10744
10445
  this.state = JSON.parse(raw);
10745
10446
  } catch {
10746
10447
  this.state = null;
@@ -10818,17 +10519,17 @@ var CloudSync = class {
10818
10519
  const localPath = this.categoryToPath(cat);
10819
10520
  if (!localPath) continue;
10820
10521
  try {
10821
- const stat13 = await fs16.stat(localPath);
10522
+ const stat13 = await fs17.stat(localPath);
10822
10523
  if (stat13.isDirectory()) {
10823
10524
  const files = await this.walkDir(localPath, localPath);
10824
10525
  for (const file of files) {
10825
- const content = await fs16.readFile(file, "utf8");
10526
+ const content = await fs17.readFile(file, "utf8");
10826
10527
  const rel = path23.relative(localPath, file).replace(/\\/g, "/");
10827
10528
  entries.push({ path: `data/${cat}/${rel}`, content, mode: "100644" });
10828
10529
  hashes.push(content);
10829
10530
  }
10830
10531
  } else {
10831
- const content = await fs16.readFile(localPath, "utf8");
10532
+ const content = await fs17.readFile(localPath, "utf8");
10832
10533
  entries.push({ path: `data/${cat}`, content, mode: "100644" });
10833
10534
  hashes.push(content);
10834
10535
  }
@@ -10844,15 +10545,15 @@ var CloudSync = class {
10844
10545
  const localPath = this.categoryToPath(cat);
10845
10546
  if (!localPath) continue;
10846
10547
  try {
10847
- const stat13 = await fs16.stat(localPath);
10548
+ const stat13 = await fs17.stat(localPath);
10848
10549
  if (stat13.isDirectory()) {
10849
10550
  const files = await this.walkDir(localPath, localPath);
10850
10551
  for (const file of files) {
10851
- const content = await fs16.readFile(file);
10552
+ const content = await fs17.readFile(file);
10852
10553
  hashes.push(content.toString("base64") + file);
10853
10554
  }
10854
10555
  } else {
10855
- const content = await fs16.readFile(localPath);
10556
+ const content = await fs17.readFile(localPath);
10856
10557
  hashes.push(content.toString("base64") + localPath);
10857
10558
  }
10858
10559
  } catch {
@@ -10879,7 +10580,7 @@ var CloudSync = class {
10879
10580
  }
10880
10581
  async walkDir(dir, base) {
10881
10582
  const results = [];
10882
- const entries = await fs16.readdir(dir, { withFileTypes: true });
10583
+ const entries = await fs17.readdir(dir, { withFileTypes: true });
10883
10584
  for (const entry of entries) {
10884
10585
  const full = path23.join(dir, entry.name);
10885
10586
  if (entry.isDirectory()) {
@@ -11066,7 +10767,6 @@ export {
11066
10767
  DefaultAttachmentStore,
11067
10768
  DefaultConfigLoader,
11068
10769
  DefaultConfigStore,
11069
- DefaultMemoryStore,
11070
10770
  DefaultPromptStore,
11071
10771
  DefaultSessionReader,
11072
10772
  DefaultSessionRewinder,
@@ -11095,12 +10795,19 @@ export {
11095
10795
  attachPlanCheckpoint,
11096
10796
  attachTodosCheckpoint,
11097
10797
  clearPlan,
10798
+ createGoalKanbanBoard,
11098
10799
  createSessionEventBridge,
10800
+ deleteGoalKanbanBoard,
11099
10801
  deriveTodosFromPlanItem,
11100
10802
  emptyGoal,
11101
10803
  emptyPlan,
11102
10804
  emptyTaskFile,
10805
+ findGoalBoardByTag,
10806
+ findGoalKanbanBoard,
11103
10807
  formatGoal,
10808
+ formatGoalAutonomyChoice,
10809
+ formatGoalEvent,
10810
+ formatGoalKanbanPreview,
11104
10811
  formatPlan,
11105
10812
  formatPlanTemplates,
11106
10813
  generateSessionId,
@@ -11118,6 +10825,7 @@ export {
11118
10825
  migratePromptEntry,
11119
10826
  mutatePlan,
11120
10827
  mutateTasks,
10828
+ parseAutonomyChoice,
11121
10829
  parseEntries,
11122
10830
  parseProgressFromText,
11123
10831
  promptChecksum,