@wrongstack/core 0.289.0 → 0.291.1

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 (177) hide show
  1. package/dist/boot.d.ts.map +1 -1
  2. package/dist/chronicle/domain-adapter.d.ts.map +1 -1
  3. package/dist/chronicle/index.d.ts +1 -1
  4. package/dist/chronicle/index.d.ts.map +1 -1
  5. package/dist/chronicle/index.js +964 -245
  6. package/dist/chronicle/index.js.map +3 -3
  7. package/dist/chronicle/journal.d.ts +35 -3
  8. package/dist/chronicle/journal.d.ts.map +1 -1
  9. package/dist/chronicle/query.d.ts +8 -5
  10. package/dist/chronicle/query.d.ts.map +1 -1
  11. package/dist/chronicle/tool-adapter.d.ts.map +1 -1
  12. package/dist/coordination/agent-subagent-runner.d.ts +6 -0
  13. package/dist/coordination/agent-subagent-runner.d.ts.map +1 -1
  14. package/dist/coordination/agents/phase6-domain.d.ts.map +1 -1
  15. package/dist/coordination/brain.d.ts +2 -1
  16. package/dist/coordination/brain.d.ts.map +1 -1
  17. package/dist/coordination/collab-debug.d.ts +0 -9
  18. package/dist/coordination/collab-debug.d.ts.map +1 -1
  19. package/dist/coordination/coordinator/error-classifier.d.ts.map +1 -1
  20. package/dist/coordination/dep-watcher.d.ts +2 -1
  21. package/dist/coordination/dep-watcher.d.ts.map +1 -1
  22. package/dist/coordination/director/director-collab.d.ts +1 -1
  23. package/dist/coordination/director/director-collab.d.ts.map +1 -1
  24. package/dist/coordination/director-tools.d.ts.map +1 -1
  25. package/dist/coordination/director.d.ts +19 -1
  26. package/dist/coordination/director.d.ts.map +1 -1
  27. package/dist/coordination/fleet-event-validation.d.ts +0 -7
  28. package/dist/coordination/fleet-event-validation.d.ts.map +1 -1
  29. package/dist/coordination/fleet-spawn.d.ts.map +1 -1
  30. package/dist/coordination/fleet-supervisor.d.ts +1 -1
  31. package/dist/coordination/fleet-supervisor.d.ts.map +1 -1
  32. package/dist/coordination/fleet.d.ts +0 -44
  33. package/dist/coordination/fleet.d.ts.map +1 -1
  34. package/dist/coordination/index.d.ts +2 -2
  35. package/dist/coordination/index.d.ts.map +1 -1
  36. package/dist/coordination/index.js +733 -111
  37. package/dist/coordination/index.js.map +4 -4
  38. package/dist/coordination/knowledge-graph.d.ts +2 -1
  39. package/dist/coordination/knowledge-graph.d.ts.map +1 -1
  40. package/dist/coordination/large-answer-store.d.ts +0 -6
  41. package/dist/coordination/large-answer-store.d.ts.map +1 -1
  42. package/dist/coordination/mail-tools.d.ts.map +1 -1
  43. package/dist/coordination/mailbox-http-router.d.ts.map +1 -1
  44. package/dist/coordination/mailbox-message-codec.d.ts +35 -0
  45. package/dist/coordination/mailbox-message-codec.d.ts.map +1 -1
  46. package/dist/coordination/mailbox-tool.d.ts +1 -1
  47. package/dist/coordination/mailbox-tool.d.ts.map +1 -1
  48. package/dist/coordination/mailbox-types.d.ts +151 -0
  49. package/dist/coordination/mailbox-types.d.ts.map +1 -1
  50. package/dist/coordination/provider-status-tracker.d.ts +27 -2
  51. package/dist/coordination/provider-status-tracker.d.ts.map +1 -1
  52. package/dist/coordination/subagent-budget.d.ts +2 -1
  53. package/dist/coordination/subagent-budget.d.ts.map +1 -1
  54. package/dist/core/agent-response.d.ts +2 -1
  55. package/dist/core/agent-response.d.ts.map +1 -1
  56. package/dist/core/agent-tools.d.ts.map +1 -1
  57. package/dist/core/context.d.ts +51 -0
  58. package/dist/core/context.d.ts.map +1 -1
  59. package/dist/core/fallback-model.d.ts +1 -1
  60. package/dist/core/fallback-model.d.ts.map +1 -1
  61. package/dist/core/fallback-profile-manager.d.ts +1 -1
  62. package/dist/core/fallback-profile-manager.d.ts.map +1 -1
  63. package/dist/core/fleet-pulse.d.ts +2 -1
  64. package/dist/core/fleet-pulse.d.ts.map +1 -1
  65. package/dist/core/iteration-limit.d.ts +2 -1
  66. package/dist/core/iteration-limit.d.ts.map +1 -1
  67. package/dist/core/mailbox-loop.d.ts +11 -1
  68. package/dist/core/mailbox-loop.d.ts.map +1 -1
  69. package/dist/core/model-availability-calendar.d.ts +27 -0
  70. package/dist/core/model-availability-calendar.d.ts.map +1 -0
  71. package/dist/core/system-prompt-builder.d.ts +2 -1
  72. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  73. package/dist/defaults/index.js +1459 -560
  74. package/dist/defaults/index.js.map +4 -4
  75. package/dist/execution/auto-compaction-middleware.d.ts +3 -2
  76. package/dist/execution/auto-compaction-middleware.d.ts.map +1 -1
  77. package/dist/execution/enhance-recovery.d.ts +8 -0
  78. package/dist/execution/enhance-recovery.d.ts.map +1 -1
  79. package/dist/execution/error-handler.d.ts +2 -2
  80. package/dist/execution/error-handler.d.ts.map +1 -1
  81. package/dist/execution/index.js +643 -134
  82. package/dist/execution/index.js.map +4 -4
  83. package/dist/execution/intelligent-compactor.d.ts +0 -16
  84. package/dist/execution/intelligent-compactor.d.ts.map +1 -1
  85. package/dist/execution/one-shot-llm.d.ts.map +1 -1
  86. package/dist/execution/retry-policy.d.ts +0 -13
  87. package/dist/execution/retry-policy.d.ts.map +1 -1
  88. package/dist/execution/skill-loader.d.ts.map +1 -1
  89. package/dist/execution/tool-executor.d.ts.map +1 -1
  90. package/dist/hq/auth-store.d.ts.map +1 -1
  91. package/dist/hq/index.js +39 -1
  92. package/dist/hq/index.js.map +3 -3
  93. package/dist/index.d.ts +32 -30
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +22263 -19999
  96. package/dist/index.js.map +4 -4
  97. package/dist/infrastructure/index.js +59 -5
  98. package/dist/infrastructure/index.js.map +2 -2
  99. package/dist/kernel/events/agent-events.d.ts +2 -0
  100. package/dist/kernel/events/agent-events.d.ts.map +1 -1
  101. package/dist/kernel/events/file-events.d.ts +46 -0
  102. package/dist/kernel/events/file-events.d.ts.map +1 -1
  103. package/dist/kernel/events/memory-events.d.ts +9 -0
  104. package/dist/kernel/events/memory-events.d.ts.map +1 -1
  105. package/dist/kernel/events/provider-events.d.ts +2 -1
  106. package/dist/kernel/events/provider-events.d.ts.map +1 -1
  107. package/dist/kernel/events/tool-events.d.ts +25 -0
  108. package/dist/kernel/events/tool-events.d.ts.map +1 -1
  109. package/dist/mailbox-attach.d.ts.map +1 -1
  110. package/dist/plugins/auto-review-plugin.d.ts +10 -0
  111. package/dist/plugins/auto-review-plugin.d.ts.map +1 -1
  112. package/dist/plugins/chimera-plugin.d.ts +5 -0
  113. package/dist/plugins/chimera-plugin.d.ts.map +1 -1
  114. package/dist/security/index.d.ts +1 -0
  115. package/dist/security/index.d.ts.map +1 -1
  116. package/dist/security/index.js +317 -9
  117. package/dist/security/index.js.map +4 -4
  118. package/dist/security/kanban-boundary.d.ts +10 -0
  119. package/dist/security/kanban-boundary.d.ts.map +1 -0
  120. package/dist/security/permission-policy.d.ts +8 -1
  121. package/dist/security/permission-policy.d.ts.map +1 -1
  122. package/dist/storage/config-loader.d.ts +15 -0
  123. package/dist/storage/config-loader.d.ts.map +1 -1
  124. package/dist/storage/index.d.ts +2 -2
  125. package/dist/storage/index.d.ts.map +1 -1
  126. package/dist/storage/index.js +627 -428
  127. package/dist/storage/index.js.map +4 -4
  128. package/dist/storage/memory-consolidator.d.ts +49 -4
  129. package/dist/storage/memory-consolidator.d.ts.map +1 -1
  130. package/dist/storage/memory-graph-backend.d.ts +11 -0
  131. package/dist/storage/memory-graph-backend.d.ts.map +1 -1
  132. package/dist/storage/provider-config-watcher.d.ts +17 -0
  133. package/dist/storage/provider-config-watcher.d.ts.map +1 -1
  134. package/dist/storage/session-event-bridge.d.ts +2 -2
  135. package/dist/storage/session-event-bridge.d.ts.map +1 -1
  136. package/dist/tools/fallback-manage-tools.d.ts.map +1 -1
  137. package/dist/tools/index.js +166 -26
  138. package/dist/tools/index.js.map +3 -3
  139. package/dist/types/config.d.ts +12 -4
  140. package/dist/types/config.d.ts.map +1 -1
  141. package/dist/types/file-event-record.d.ts +68 -0
  142. package/dist/types/file-event-record.d.ts.map +1 -0
  143. package/dist/types/index.d.ts +1 -0
  144. package/dist/types/index.d.ts.map +1 -1
  145. package/dist/types/index.js +13 -8
  146. package/dist/types/index.js.map +3 -3
  147. package/dist/types/permission.d.ts +38 -0
  148. package/dist/types/permission.d.ts.map +1 -1
  149. package/dist/types/provider.d.ts +1 -1
  150. package/dist/types/provider.d.ts.map +1 -1
  151. package/dist/types/session.d.ts +27 -0
  152. package/dist/types/session.d.ts.map +1 -1
  153. package/dist/types/tool-executor.d.ts +2 -0
  154. package/dist/types/tool-executor.d.ts.map +1 -1
  155. package/dist/utils/config-backup.d.ts +20 -0
  156. package/dist/utils/config-backup.d.ts.map +1 -0
  157. package/dist/utils/index.d.ts +12 -11
  158. package/dist/utils/index.d.ts.map +1 -1
  159. package/dist/utils/index.js +436 -290
  160. package/dist/utils/index.js.map +4 -4
  161. package/dist/utils/message-invariants.d.ts +12 -0
  162. package/dist/utils/message-invariants.d.ts.map +1 -1
  163. package/dist/utils/safe-json.d.ts +1 -1
  164. package/dist/utils/safe-json.d.ts.map +1 -1
  165. package/dist/utils/term.d.ts +6 -0
  166. package/dist/utils/term.d.ts.map +1 -1
  167. package/dist/utils/tool-wire-compact.d.ts +9 -0
  168. package/dist/utils/tool-wire-compact.d.ts.map +1 -1
  169. package/dist/utils/wstack-paths.d.ts +21 -1
  170. package/dist/utils/wstack-paths.d.ts.map +1 -1
  171. package/instructions/agents/ios.md +113 -0
  172. package/instructions/llm/memory-consolidator.md +4 -12
  173. package/instructions/system.md +4 -1
  174. package/package.json +3 -3
  175. package/skills/auto-review/SKILL.md +176 -0
  176. package/skills/mnemosyne/SKILL.md +156 -0
  177. package/skills/mnemosyne/instructions/agent-prompt.md +200 -0
@@ -2,14 +2,14 @@
2
2
  import { createHash as createHash4 } from "node:crypto";
3
3
  import { createReadStream } from "node:fs";
4
4
  import * as fsp4 from "node:fs/promises";
5
- import * as path7 from "node:path";
5
+ import * as path8 from "node:path";
6
6
  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 fs2 from "node:fs/promises";
10
+ import * as fs3 from "node:fs/promises";
11
11
  import { watch as watchDir } from "node:fs";
12
- import * as path3 from "node:path";
12
+ import * as path4 from "node:path";
13
13
 
14
14
  // src/utils/child-env.ts
15
15
  var ALLOWED_KEYS = /* @__PURE__ */ new Set([
@@ -186,6 +186,37 @@ var color = {
186
186
  bgGreen: wrap("42", "49")
187
187
  };
188
188
 
189
+ // src/utils/config-backup.ts
190
+ import * as fs from "node:fs/promises";
191
+ import * as path from "node:path";
192
+ function configHistoryDir(globalRoot) {
193
+ return path.join(globalRoot, "config-history");
194
+ }
195
+ function configSlug(absolutePath, globalRoot) {
196
+ const rel = path.relative(globalRoot, absolutePath);
197
+ const normalized = rel.replace(/\\/g, "/").replace(/\.json$/i, "");
198
+ return normalized.replace(/\//g, "-");
199
+ }
200
+ async function backupConfigFile(filePath, paths) {
201
+ let currentContent;
202
+ try {
203
+ currentContent = await fs.readFile(filePath, "utf8");
204
+ if (!currentContent.trim()) return;
205
+ } catch {
206
+ return;
207
+ }
208
+ const now = /* @__PURE__ */ new Date();
209
+ const ts = now.toISOString().replace(/[:.]/g, "-").replace(/Z$/, "");
210
+ const slug = configSlug(filePath, paths.globalRoot);
211
+ const backupDir = configHistoryDir(paths.globalRoot);
212
+ const backupFile = path.join(backupDir, `${slug}-${ts}.json`);
213
+ try {
214
+ await fs.mkdir(backupDir, { recursive: true });
215
+ await fs.writeFile(backupFile, currentContent, { mode: 384, encoding: "utf8" });
216
+ } catch {
217
+ }
218
+ }
219
+
189
220
  // src/utils/deep-merge.ts
190
221
  var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
191
222
  "__proto__",
@@ -347,9 +378,23 @@ function mapContent(msg, fn) {
347
378
  }
348
379
  return { ...msg, content: next };
349
380
  }
381
+ function hasMeaningfulContent(content) {
382
+ if (typeof content === "string") return content.trim().length > 0;
383
+ for (const block of content) {
384
+ if (block.type === "text") {
385
+ if (block.text.trim().length > 0) return true;
386
+ continue;
387
+ }
388
+ if (block.type === "thinking") {
389
+ if (block.thinking.trim().length > 0 || block.signature) return true;
390
+ continue;
391
+ }
392
+ return true;
393
+ }
394
+ return false;
395
+ }
350
396
  function isEmptyMessage(msg) {
351
- if (typeof msg.content === "string") return msg.content.trim().length === 0;
352
- return msg.content.length === 0;
397
+ return !hasMeaningfulContent(msg.content);
353
398
  }
354
399
 
355
400
  // src/utils/regex-guard.ts
@@ -390,7 +435,7 @@ function compileUserRegex(pattern, flags) {
390
435
 
391
436
  // src/utils/safe-json.ts
392
437
  function safeParse(input, maxBytes = 5e6) {
393
- if (input.length > maxBytes) {
438
+ if (Buffer.byteLength(input, "utf8") > maxBytes) {
394
439
  return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
395
440
  }
396
441
  try {
@@ -404,14 +449,14 @@ function safeParse(input, maxBytes = 5e6) {
404
449
  }
405
450
 
406
451
  // src/utils/session-scoped-path.ts
407
- import * as path from "node:path";
452
+ import * as path2 from "node:path";
408
453
  function sessionScopedPath(dir, sessionId, suffix) {
409
454
  if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
410
455
  throw invalid(sessionId);
411
456
  }
412
- const resolved = path.resolve(dir, `${sessionId}${suffix}`);
413
- const rel = path.relative(path.resolve(dir), resolved);
414
- if (rel.startsWith("..") || path.isAbsolute(rel)) {
457
+ const resolved = path2.resolve(dir, `${sessionId}${suffix}`);
458
+ const rel = path2.relative(path2.resolve(dir), resolved);
459
+ if (rel.startsWith("..") || path2.isAbsolute(rel)) {
415
460
  throw invalid(sessionId);
416
461
  }
417
462
  return resolved;
@@ -460,23 +505,23 @@ function ulid(seedTime = Date.now()) {
460
505
 
461
506
  // src/utils/wstack-paths.ts
462
507
  import { createHash } from "node:crypto";
463
- import * as fs from "node:fs";
508
+ import * as fs2 from "node:fs";
464
509
  import * as os from "node:os";
465
- import * as path2 from "node:path";
510
+ import * as path3 from "node:path";
466
511
  function canonicalProjectRoot(absRoot) {
467
- const checkoutRoot = path2.resolve(absRoot);
468
- const dotGit = path2.join(checkoutRoot, ".git");
512
+ const checkoutRoot = path3.resolve(absRoot);
513
+ const dotGit = path3.join(checkoutRoot, ".git");
469
514
  try {
470
- if (!fs.statSync(dotGit).isFile()) return checkoutRoot;
471
- const gitDirLine = fs.readFileSync(dotGit, "utf8").trim();
515
+ if (!fs2.statSync(dotGit).isFile()) return checkoutRoot;
516
+ const gitDirLine = fs2.readFileSync(dotGit, "utf8").trim();
472
517
  const match = /^gitdir:\s*(.+)$/i.exec(gitDirLine);
473
518
  if (!match?.[1]) return checkoutRoot;
474
- const gitDir = path2.resolve(checkoutRoot, match[1].trim());
475
- const commonDirFile = path2.join(gitDir, "commondir");
476
- if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;
477
- const commonDir = path2.resolve(gitDir, fs.readFileSync(commonDirFile, "utf8").trim());
478
- if (path2.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
479
- return path2.dirname(commonDir);
519
+ const gitDir = path3.resolve(checkoutRoot, match[1].trim());
520
+ const commonDirFile = path3.join(gitDir, "commondir");
521
+ if (!fs2.statSync(commonDirFile).isFile()) return checkoutRoot;
522
+ const commonDir = path3.resolve(gitDir, fs2.readFileSync(commonDirFile, "utf8").trim());
523
+ if (path3.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
524
+ return path3.dirname(commonDir);
480
525
  } catch {
481
526
  return checkoutRoot;
482
527
  }
@@ -486,7 +531,7 @@ function projectHash(absRoot) {
486
531
  }
487
532
  function projectSlug(absRoot) {
488
533
  const identityRoot = canonicalProjectRoot(absRoot);
489
- const base = slugify2(path2.basename(identityRoot));
534
+ const base = slugify2(path3.basename(identityRoot));
490
535
  const hash = createHash("sha256").update(identityRoot).digest("hex").slice(0, 6);
491
536
  return `${base}-${hash}`;
492
537
  }
@@ -495,61 +540,83 @@ function slugify2(name) {
495
540
  }
496
541
  function wstackGlobalRoot() {
497
542
  const fromEnv = process.env["WRONGSTACK_HOME"];
498
- if (fromEnv && fromEnv.trim().length > 0) return path2.resolve(fromEnv);
499
- return path2.join(os.homedir(), ".wrongstack");
543
+ if (fromEnv && fromEnv.trim().length > 0) return path3.resolve(fromEnv);
544
+ return path3.join(os.homedir(), ".wrongstack");
500
545
  }
501
546
  function resolveWstackPaths(opts) {
502
- const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
547
+ const globalRoot = opts.globalRoot ?? (opts.userHome ? path3.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
503
548
  const homeDir = opts.userHome ?? os.homedir();
504
549
  const hash = projectHash(opts.projectRoot);
505
550
  const slug = projectSlug(opts.projectRoot);
506
- const projectDir = path2.join(globalRoot, "projects", slug);
551
+ const projectDir = path3.join(globalRoot, "projects", slug);
507
552
  return {
508
553
  globalRoot,
509
554
  projectRoot: opts.projectRoot,
510
555
  homeDir,
511
556
  configDir: globalRoot,
512
- globalConfig: path2.join(globalRoot, "config.json"),
513
- secretsKey: path2.join(globalRoot, ".key"),
514
- globalMemory: path2.join(globalRoot, "memory.md"),
515
- globalSkills: path2.join(globalRoot, "skills"),
516
- globalClaudeSkills: path2.join(homeDir, ".claude", "skills"),
517
- globalDesignKits: path2.join(globalRoot, "design-kits"),
518
- globalPrompts: path2.join(globalRoot, "prompts"),
519
- globalInstructions: path2.join(globalRoot, "instructions"),
520
- promptUsage: path2.join(globalRoot, "prompt-usage.json"),
521
- cacheDir: path2.join(globalRoot, "cache"),
522
- modelsCache: path2.join(globalRoot, "cache", "models.dev.json"),
523
- modelsOverlayCache: path2.join(globalRoot, "cache", "models-overlay.json"),
524
- historyFile: path2.join(globalRoot, "history"),
525
- logFile: path2.join(globalRoot, "logs", "wrongstack.log"),
557
+ globalConfig: path3.join(globalRoot, "config.json"),
558
+ profilesDir: path3.join(globalRoot, "profiles"),
559
+ profileConfig: (name) => {
560
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
561
+ return path3.join(globalRoot, "profiles", safe || "default", "config.json");
562
+ },
563
+ profileStatuslineConfig: (name) => {
564
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
565
+ return path3.join(globalRoot, "profiles", safe || "default", "statusline.json");
566
+ },
567
+ profileModeConfig: (name) => {
568
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
569
+ return path3.join(globalRoot, "profiles", safe || "default", "mode.json");
570
+ },
571
+ profileProviderStatus: (name) => {
572
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
573
+ return path3.join(globalRoot, "profiles", safe || "default", "provider-status.json");
574
+ },
575
+ profileUpdateCache: (name) => {
576
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
577
+ return path3.join(globalRoot, "profiles", safe || "default", "update-cache.json");
578
+ },
579
+ secretsKey: path3.join(globalRoot, ".key"),
580
+ globalMemory: path3.join(globalRoot, "memory.md"),
581
+ globalSkills: path3.join(globalRoot, "skills"),
582
+ globalClaudeSkills: path3.join(homeDir, ".claude", "skills"),
583
+ globalDesignKits: path3.join(globalRoot, "design-kits"),
584
+ globalPrompts: path3.join(globalRoot, "prompts"),
585
+ globalInstructions: path3.join(globalRoot, "instructions"),
586
+ promptUsage: path3.join(globalRoot, "prompt-usage.json"),
587
+ cacheDir: path3.join(globalRoot, "cache"),
588
+ modelsCache: path3.join(globalRoot, "cache", "models.dev.json"),
589
+ modelsOverlayCache: path3.join(globalRoot, "cache", "models-overlay.json"),
590
+ historyFile: path3.join(globalRoot, "history"),
591
+ logFile: path3.join(globalRoot, "logs", "wrongstack.log"),
526
592
  projectDir,
527
- projectCodebaseIndex: path2.join(projectDir, "codebase-index"),
528
- projectMemory: path2.join(projectDir, "memory.md"),
529
- projectSessions: path2.join(projectDir, "sessions"),
530
- projectTrust: path2.join(projectDir, "trust.json"),
531
- projectMeta: path2.join(projectDir, "meta.json"),
532
- projectLocalConfig: path2.join(projectDir, "config.local.json"),
533
- inProjectConfig: path2.join(opts.projectRoot, ".wrongstack", "config.json"),
534
- inProjectAgentsFile: path2.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
535
- inProjectSkills: path2.join(opts.projectRoot, ".wrongstack", "skills"),
536
- inProjectClaudeSkills: path2.join(opts.projectRoot, ".claude", "skills"),
537
- inProjectPrompts: path2.join(opts.projectRoot, ".wrongstack", "prompts"),
538
- inProjectInstructions: path2.join(opts.projectRoot, ".wrongstack", "instructions"),
539
- inProjectDesignKits: path2.join(opts.projectRoot, ".wrongstack", "design-kits"),
540
- inProjectWorktrees: path2.join(opts.projectRoot, ".wrongstack", "worktrees"),
593
+ projectCodebaseIndex: path3.join(projectDir, "codebase-index"),
594
+ projectMemory: path3.join(projectDir, "memory.md"),
595
+ projectSessions: path3.join(projectDir, "sessions"),
596
+ projectTrust: path3.join(projectDir, "trust.json"),
597
+ projectMeta: path3.join(projectDir, "meta.json"),
598
+ projectLocalConfig: path3.join(projectDir, "config.local.json"),
599
+ inProjectConfig: path3.join(opts.projectRoot, ".wrongstack", "config.json"),
600
+ inProjectAgentsFile: path3.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
601
+ inProjectSkills: path3.join(opts.projectRoot, ".wrongstack", "skills"),
602
+ inProjectClaudeSkills: path3.join(opts.projectRoot, ".claude", "skills"),
603
+ inProjectPrompts: path3.join(opts.projectRoot, ".wrongstack", "prompts"),
604
+ inProjectInstructions: path3.join(opts.projectRoot, ".wrongstack", "instructions"),
605
+ inProjectDesignKits: path3.join(opts.projectRoot, ".wrongstack", "design-kits"),
606
+ inProjectWorktrees: path3.join(opts.projectRoot, ".wrongstack", "worktrees"),
541
607
  projectHash: hash,
542
608
  projectSlug: slug,
543
- projectGoal: path2.join(projectDir, "goal.json"),
544
- projectInputHistory: path2.join(projectDir, "input-history.json"),
545
- projectSpecs: path2.join(projectDir, "specs"),
546
- projectTaskGraphs: path2.join(projectDir, "task-graphs"),
547
- projectSddSession: path2.join(projectDir, "sdd-session.json"),
548
- projectPlan: path2.join(projectDir, "plan.json"),
549
- projectAutophase: path2.join(projectDir, "autophase"),
550
- projectSddBoards: path2.join(projectDir, "sdd-boards"),
551
- syncConfig: path2.join(globalRoot, "sync.json"),
552
- projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
609
+ projectGoal: path3.join(projectDir, "goal.json"),
610
+ projectInputHistory: path3.join(projectDir, "input-history.json"),
611
+ projectSpecs: path3.join(projectDir, "specs"),
612
+ projectTaskGraphs: path3.join(projectDir, "task-graphs"),
613
+ projectSddSession: path3.join(projectDir, "sdd-session.json"),
614
+ projectPlan: path3.join(projectDir, "plan.json"),
615
+ projectAutophase: path3.join(projectDir, "autophase"),
616
+ projectSddBoards: path3.join(projectDir, "sdd-boards"),
617
+ syncConfig: path3.join(globalRoot, "sync.json"),
618
+ configHistoryDir: path3.join(globalRoot, "config-history"),
619
+ projectStatus: (projectHash2) => path3.join(globalRoot, "projects", projectHash2, "status.json")
553
620
  };
554
621
  }
555
622
 
@@ -687,17 +754,17 @@ var FsError = class extends WrongStackError {
687
754
 
688
755
  // src/utils/atomic-write.ts
689
756
  async function atomicWrite(targetPath, content, opts = {}) {
690
- const dir = path3.dirname(targetPath);
691
- await fs2.mkdir(dir, { recursive: true });
692
- const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes2(6).toString("hex")}.tmp`);
757
+ const dir = path4.dirname(targetPath);
758
+ await fs3.mkdir(dir, { recursive: true });
759
+ const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes2(6).toString("hex")}.tmp`);
693
760
  try {
694
761
  if (typeof content === "string") {
695
- await fs2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
762
+ await fs3.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
696
763
  } else {
697
- await fs2.writeFile(tmp, content, { flag: "wx" });
764
+ await fs3.writeFile(tmp, content, { flag: "wx" });
698
765
  }
699
766
  try {
700
- const fh = await fs2.open(tmp, "r+");
767
+ const fh = await fs3.open(tmp, "r+");
701
768
  try {
702
769
  await fh.sync();
703
770
  } finally {
@@ -707,63 +774,63 @@ async function atomicWrite(targetPath, content, opts = {}) {
707
774
  }
708
775
  let mode;
709
776
  try {
710
- const stat13 = await fs2.stat(targetPath);
777
+ const stat13 = await fs3.stat(targetPath);
711
778
  mode = stat13.mode & 511;
712
779
  } catch {
713
780
  mode = opts.mode;
714
781
  }
715
782
  if (mode !== void 0) {
716
- await fs2.chmod(tmp, mode);
783
+ await fs3.chmod(tmp, mode);
717
784
  }
718
785
  await renameWithRetry(tmp, targetPath);
719
786
  if (mode !== void 0 && process.platform === "win32") {
720
787
  try {
721
- await fs2.chmod(targetPath, mode);
788
+ await fs3.chmod(targetPath, mode);
722
789
  } catch {
723
790
  }
724
791
  }
725
792
  } catch (err) {
726
793
  try {
727
- await fs2.unlink(tmp);
794
+ await fs3.unlink(tmp);
728
795
  } catch {
729
796
  }
730
797
  throw err;
731
798
  }
732
799
  }
733
800
  async function ensureDir(dir) {
734
- await fs2.mkdir(dir, { recursive: true });
801
+ await fs3.mkdir(dir, { recursive: true });
735
802
  }
736
803
  async function withFileLock(targetPath, fn, opts = {}) {
737
- const dir = path3.dirname(targetPath);
738
- await fs2.mkdir(dir, { recursive: true });
739
- const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
804
+ const dir = path4.dirname(targetPath);
805
+ await fs3.mkdir(dir, { recursive: true });
806
+ const lockPath = path4.join(dir, `.${path4.basename(targetPath)}.lock`);
740
807
  const timeoutMs = opts.timeoutMs ?? 15e3;
741
808
  const staleMs = opts.staleMs ?? 3e4;
742
809
  const started = Date.now();
743
810
  let handle;
744
811
  for (; ; ) {
745
812
  try {
746
- handle = await fs2.open(lockPath, "wx");
813
+ handle = await fs3.open(lockPath, "wx");
747
814
  await handle.writeFile(`${process.pid}:${Date.now()}`);
748
815
  break;
749
816
  } catch (err) {
750
817
  if (handle) {
751
818
  await handle.close().catch(() => {
752
819
  });
753
- await fs2.unlink(lockPath).catch(() => {
820
+ await fs3.unlink(lockPath).catch(() => {
754
821
  });
755
822
  handle = void 0;
756
823
  }
757
824
  const code = err.code;
758
825
  if (code === "ENOENT") {
759
- await fs2.mkdir(dir, { recursive: true });
826
+ await fs3.mkdir(dir, { recursive: true });
760
827
  continue;
761
828
  }
762
829
  if (code !== "EEXIST" && code !== "EPERM") throw err;
763
830
  try {
764
- const stat13 = await fs2.stat(lockPath);
831
+ const stat13 = await fs3.stat(lockPath);
765
832
  if (Date.now() - stat13.mtimeMs > staleMs) {
766
- await fs2.unlink(lockPath);
833
+ await fs3.unlink(lockPath);
767
834
  continue;
768
835
  }
769
836
  } catch {
@@ -789,14 +856,14 @@ async function withFileLock(targetPath, fn, opts = {}) {
789
856
  } catch {
790
857
  }
791
858
  try {
792
- await fs2.unlink(lockPath);
859
+ await fs3.unlink(lockPath);
793
860
  } catch {
794
861
  }
795
862
  }
796
863
  }
797
864
  async function waitForLockRelease(lockPath, remainingMs) {
798
- const parentDir = path3.dirname(lockPath);
799
- const lockName = path3.basename(lockPath);
865
+ const parentDir = path4.dirname(lockPath);
866
+ const lockName = path4.basename(lockPath);
800
867
  const intervalMs = Math.min(remainingMs, 100);
801
868
  return new Promise((resolve11) => {
802
869
  let settled = false;
@@ -824,7 +891,7 @@ async function waitForLockRelease(lockPath, remainingMs) {
824
891
  }
825
892
  return;
826
893
  }
827
- fs2.access(lockPath).then(
894
+ fs3.access(lockPath).then(
828
895
  () => {
829
896
  },
830
897
  () => {
@@ -841,14 +908,14 @@ async function waitForLockRelease(lockPath, remainingMs) {
841
908
  var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
842
909
  async function renameWithRetry(from, to) {
843
910
  if (process.platform !== "win32") {
844
- await fs2.rename(from, to);
911
+ await fs3.rename(from, to);
845
912
  return;
846
913
  }
847
914
  const delays = [10, 25, 60, 120, 250];
848
915
  let lastErr;
849
916
  for (let i = 0; i <= delays.length; i++) {
850
917
  try {
851
- await fs2.rename(from, to);
918
+ await fs3.rename(from, to);
852
919
  return;
853
920
  } catch (err) {
854
921
  lastErr = err;
@@ -865,7 +932,7 @@ async function renameWithRetry(from, to) {
865
932
  // src/storage/file-session-writer.ts
866
933
  import { closeSync, fsyncSync, openSync, writeSync } from "node:fs";
867
934
  import * as fsp from "node:fs/promises";
868
- import * as path4 from "node:path";
935
+ import * as path5 from "node:path";
869
936
 
870
937
  // src/storage/session-helpers.ts
871
938
  function userInputTitle(content) {
@@ -882,7 +949,7 @@ var FileSessionWriter = class _FileSessionWriter {
882
949
  this.meta = meta;
883
950
  this.events = events;
884
951
  this.resumed = opts.resumed ?? false;
885
- this.manifestFile = opts.dir ? path4.join(opts.dir, `${path4.basename(id)}.summary.json`) : "";
952
+ this.manifestFile = opts.dir ? path5.join(opts.dir, `${path5.basename(id)}.summary.json`) : "";
886
953
  this.filePath = opts.filePath ?? "";
887
954
  this.secretScrubber = opts.secretScrubber;
888
955
  this.checkpointCas = opts.checkpointCas;
@@ -1664,7 +1731,7 @@ var FileSessionWriter = class _FileSessionWriter {
1664
1731
  import { spawn } from "node:child_process";
1665
1732
  import { createHash as createHash2, randomUUID } from "node:crypto";
1666
1733
  import * as fsp2 from "node:fs/promises";
1667
- import * as path5 from "node:path";
1734
+ import * as path6 from "node:path";
1668
1735
 
1669
1736
  // src/storage/storage-concurrency.ts
1670
1737
  async function mapWithConcurrency(items, concurrency, mapper) {
@@ -1695,29 +1762,29 @@ function sha256(content) {
1695
1762
  return createHash2("sha256").update(content).digest("hex");
1696
1763
  }
1697
1764
  function isInside(root, target) {
1698
- const relative6 = path5.relative(root, target);
1699
- return relative6 === "" || !relative6.startsWith("..") && !path5.isAbsolute(relative6);
1765
+ const relative7 = path6.relative(root, target);
1766
+ return relative7 === "" || !relative7.startsWith("..") && !path6.isAbsolute(relative7);
1700
1767
  }
1701
1768
  function normalizeRelative(input) {
1702
- if (!input || path5.isAbsolute(input)) return null;
1769
+ if (!input || path6.isAbsolute(input)) return null;
1703
1770
  const normalized = input.replace(/\\/g, "/").replace(/^\.\//, "");
1704
- const resolved = path5.posix.normalize(normalized);
1771
+ const resolved = path6.posix.normalize(normalized);
1705
1772
  if (!resolved || resolved === "." || resolved === ".." || resolved.startsWith("../")) return null;
1706
1773
  return resolved;
1707
1774
  }
1708
1775
  function parseNulPaths(output) {
1709
1776
  return output.split("\0").map(normalizeRelative).filter((value) => value !== null);
1710
1777
  }
1711
- function isWrongStackWorktreePath(relative6) {
1712
- return relative6 === ".wrongstack/worktrees" || relative6.startsWith(".wrongstack/worktrees/");
1778
+ function isWrongStackWorktreePath(relative7) {
1779
+ return relative7 === ".wrongstack/worktrees" || relative7.startsWith(".wrongstack/worktrees/");
1713
1780
  }
1714
1781
  var SessionCheckpointCas = class {
1715
1782
  rootDir;
1716
1783
  projectRoot;
1717
1784
  runGit;
1718
1785
  constructor(opts) {
1719
- this.rootDir = path5.resolve(opts.rootDir);
1720
- this.projectRoot = path5.resolve(opts.projectRoot);
1786
+ this.rootDir = path6.resolve(opts.rootDir);
1787
+ this.projectRoot = path6.resolve(opts.projectRoot);
1721
1788
  this.runGit = opts.runGit ?? defaultRunGit;
1722
1789
  }
1723
1790
  async capture(_sessionId, _promptIndex) {
@@ -1735,46 +1802,46 @@ var SessionCheckpointCas = class {
1735
1802
  }
1736
1803
  const relativePaths = [
1737
1804
  .../* @__PURE__ */ new Set([...parseNulPaths(tracked.stdout), ...parseNulPaths(untracked.stdout)])
1738
- ].filter((relative6) => !isWrongStackWorktreePath(relative6)).sort();
1805
+ ].filter((relative7) => !isWrongStackWorktreePath(relative7)).sort();
1739
1806
  const unresolved = [];
1740
1807
  let scheduledBytes = 0;
1741
1808
  const captured = await mapWithConcurrency(
1742
1809
  relativePaths,
1743
1810
  CAPTURE_CONCURRENCY,
1744
- async (relative6) => {
1745
- const absolute = path5.resolve(this.projectRoot, ...relative6.split("/"));
1811
+ async (relative7) => {
1812
+ const absolute = path6.resolve(this.projectRoot, ...relative7.split("/"));
1746
1813
  if (!isInside(this.projectRoot, absolute)) {
1747
- unresolved.push({ path: relative6, reason: "path escapes project root" });
1814
+ unresolved.push({ path: relative7, reason: "path escapes project root" });
1748
1815
  return null;
1749
1816
  }
1750
1817
  try {
1751
1818
  const stat13 = await fsp2.lstat(absolute);
1752
1819
  if (stat13.isSymbolicLink()) {
1753
1820
  const linkTarget = await fsp2.readlink(absolute);
1754
- const resolvedLink = path5.resolve(path5.dirname(absolute), linkTarget);
1755
- if (path5.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
1821
+ const resolvedLink = path6.resolve(path6.dirname(absolute), linkTarget);
1822
+ if (path6.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
1756
1823
  unresolved.push({
1757
- path: relative6,
1824
+ path: relative7,
1758
1825
  reason: "symlink target escapes project root"
1759
1826
  });
1760
1827
  return null;
1761
1828
  }
1762
- return { path: relative6, state: "symlink", linkTarget };
1829
+ return { path: relative7, state: "symlink", linkTarget };
1763
1830
  }
1764
1831
  if (!stat13.isFile()) {
1765
- unresolved.push({ path: relative6, reason: "changed path is not a regular file" });
1832
+ unresolved.push({ path: relative7, reason: "changed path is not a regular file" });
1766
1833
  return null;
1767
1834
  }
1768
1835
  if (stat13.size > MAX_BLOB_BYTES) {
1769
1836
  unresolved.push({
1770
- path: relative6,
1837
+ path: relative7,
1771
1838
  reason: `file exceeds ${MAX_BLOB_BYTES}-byte checkpoint blob limit`
1772
1839
  });
1773
1840
  return null;
1774
1841
  }
1775
1842
  if (scheduledBytes + stat13.size > MAX_CHECKPOINT_BYTES) {
1776
1843
  unresolved.push({
1777
- path: relative6,
1844
+ path: relative7,
1778
1845
  reason: `checkpoint exceeds ${MAX_CHECKPOINT_BYTES}-byte aggregate blob limit`
1779
1846
  });
1780
1847
  return null;
@@ -1783,12 +1850,12 @@ var SessionCheckpointCas = class {
1783
1850
  const content = await fsp2.readFile(absolute);
1784
1851
  const blobHash = sha256(content);
1785
1852
  await this.putBlob(blobHash, content);
1786
- return { path: relative6, state: "file", blobHash, mode: stat13.mode & 511 };
1853
+ return { path: relative7, state: "file", blobHash, mode: stat13.mode & 511 };
1787
1854
  } catch (err) {
1788
1855
  if (err.code === "ENOENT") {
1789
- return { path: relative6, state: "absent" };
1856
+ return { path: relative7, state: "absent" };
1790
1857
  }
1791
- unresolved.push({ path: relative6, reason: toErrorMessage(err) });
1858
+ unresolved.push({ path: relative7, reason: toErrorMessage(err) });
1792
1859
  return null;
1793
1860
  }
1794
1861
  }
@@ -1814,7 +1881,7 @@ var SessionCheckpointCas = class {
1814
1881
  };
1815
1882
  }
1816
1883
  async materialize(checkpoint, targetRoot) {
1817
- const target = path5.resolve(targetRoot);
1884
+ const target = path6.resolve(targetRoot);
1818
1885
  if (target === this.projectRoot) {
1819
1886
  throw new Error("Refusing to materialize a workspace checkpoint over the parent project root");
1820
1887
  }
@@ -1854,10 +1921,10 @@ var SessionCheckpointCas = class {
1854
1921
  try {
1855
1922
  const output = await this.safeOutputPath(target, realTarget, entry.path);
1856
1923
  if (entry.state === "symlink") {
1857
- if (path5.isAbsolute(entry.linkTarget)) {
1924
+ if (path6.isAbsolute(entry.linkTarget)) {
1858
1925
  throw new Error("absolute symlink target refused");
1859
1926
  }
1860
- const resolvedLink = path5.resolve(path5.dirname(output), entry.linkTarget);
1927
+ const resolvedLink = path6.resolve(path6.dirname(output), entry.linkTarget);
1861
1928
  if (!isInside(target, resolvedLink)) throw new Error("symlink target escapes checkpoint root");
1862
1929
  }
1863
1930
  prepared.push({
@@ -1885,7 +1952,7 @@ var SessionCheckpointCas = class {
1885
1952
  await fsp2.unlink(output).catch((err) => {
1886
1953
  if (err.code !== "ENOENT") throw err;
1887
1954
  });
1888
- await fsp2.mkdir(path5.dirname(output), { recursive: true });
1955
+ await fsp2.mkdir(path6.dirname(output), { recursive: true });
1889
1956
  await fsp2.symlink(entry.linkTarget, output);
1890
1957
  writtenFiles.push(entry.path);
1891
1958
  continue;
@@ -1902,15 +1969,15 @@ var SessionCheckpointCas = class {
1902
1969
  }
1903
1970
  objectPath(hash) {
1904
1971
  if (!HASH_RE.test(hash)) throw new Error(`Invalid CAS object hash: ${hash}`);
1905
- return path5.join(this.rootDir, "objects", hash.slice(0, 2), hash.slice(2));
1972
+ return path6.join(this.rootDir, "objects", hash.slice(0, 2), hash.slice(2));
1906
1973
  }
1907
1974
  manifestPath(hash) {
1908
1975
  if (!HASH_RE.test(hash)) throw new Error(`Invalid checkpoint manifest hash: ${hash}`);
1909
- return path5.join(this.rootDir, "manifests", `${hash}.json`);
1976
+ return path6.join(this.rootDir, "manifests", `${hash}.json`);
1910
1977
  }
1911
1978
  async putBlob(hash, content) {
1912
1979
  const target = this.objectPath(hash);
1913
- await fsp2.mkdir(path5.dirname(target), { recursive: true });
1980
+ await fsp2.mkdir(path6.dirname(target), { recursive: true });
1914
1981
  try {
1915
1982
  const existing = await fsp2.readFile(target);
1916
1983
  if (sha256(existing) !== hash) throw new Error(`Corrupt CAS object collision: ${hash}`);
@@ -1918,9 +1985,9 @@ var SessionCheckpointCas = class {
1918
1985
  } catch (err) {
1919
1986
  if (err.code !== "ENOENT") throw err;
1920
1987
  }
1921
- const temp = path5.join(
1922
- path5.dirname(target),
1923
- `.${path5.basename(target)}.${process.pid}.${randomUUID()}.tmp`
1988
+ const temp = path6.join(
1989
+ path6.dirname(target),
1990
+ `.${path6.basename(target)}.${process.pid}.${randomUUID()}.tmp`
1924
1991
  );
1925
1992
  let handle;
1926
1993
  try {
@@ -1978,10 +2045,10 @@ var SessionCheckpointCas = class {
1978
2045
  }
1979
2046
  return parsed;
1980
2047
  }
1981
- async safeOutputPath(target, realTarget, relative6) {
1982
- const normalized = normalizeRelative(relative6);
2048
+ async safeOutputPath(target, realTarget, relative7) {
2049
+ const normalized = normalizeRelative(relative7);
1983
2050
  if (!normalized) throw new Error("invalid relative path");
1984
- const output = path5.resolve(target, ...normalized.split("/"));
2051
+ const output = path6.resolve(target, ...normalized.split("/"));
1985
2052
  if (!isInside(target, output)) throw new Error("path escapes checkpoint target");
1986
2053
  let probe = output;
1987
2054
  for (; ; ) {
@@ -1991,7 +2058,7 @@ var SessionCheckpointCas = class {
1991
2058
  return output;
1992
2059
  } catch (err) {
1993
2060
  if (err.code !== "ENOENT") throw err;
1994
- const parent = path5.dirname(probe);
2061
+ const parent = path6.dirname(probe);
1995
2062
  if (parent === probe) throw err;
1996
2063
  probe = parent;
1997
2064
  }
@@ -2060,13 +2127,13 @@ function generateSessionId(startedAt, _model) {
2060
2127
  // src/storage/session-resume-validation.ts
2061
2128
  import { createHash as createHash3 } from "node:crypto";
2062
2129
  import * as fsp3 from "node:fs/promises";
2063
- import * as path6 from "node:path";
2130
+ import * as path7 from "node:path";
2064
2131
  var MAX_REVALIDATE_BYTES = 5 * 1024 * 1024;
2065
2132
  var VALIDATION_CONCURRENCY = 8;
2066
2133
  var NOTICE_PATH_LIMIT = 20;
2067
2134
  function isInside2(root, target) {
2068
- const relative6 = path6.relative(root, target);
2069
- return relative6 === "" || !relative6.startsWith("..") && !path6.isAbsolute(relative6);
2135
+ const relative7 = path7.relative(root, target);
2136
+ return relative7 === "" || !relative7.startsWith("..") && !path7.isAbsolute(relative7);
2070
2137
  }
2071
2138
  function errno(err) {
2072
2139
  return err && typeof err === "object" && "code" in err ? String(err.code) : void 0;
@@ -2077,7 +2144,7 @@ function latestObservations(events, projectRoot) {
2077
2144
  if (event.type !== "file_observation" || typeof event.path !== "string" || event.path.length === 0 || typeof event.hash !== "string" || !/^[a-f\d]{64}$/i.test(event.hash)) {
2078
2145
  continue;
2079
2146
  }
2080
- const normalized = path6.resolve(projectRoot, event.path);
2147
+ const normalized = path7.resolve(projectRoot, event.path);
2081
2148
  latest.set(normalized, {
2082
2149
  path: normalized,
2083
2150
  hash: event.hash.toLowerCase(),
@@ -2135,7 +2202,7 @@ async function validateOne(observation, lexicalRoot, realRoot) {
2135
2202
  }
2136
2203
  }
2137
2204
  async function validateResumeFileObservations(events, projectRoot) {
2138
- const lexicalRoot = path6.resolve(projectRoot);
2205
+ const lexicalRoot = path7.resolve(projectRoot);
2139
2206
  const realRoot = await fsp3.realpath(lexicalRoot).catch(() => lexicalRoot);
2140
2207
  const observations = latestObservations(events, lexicalRoot);
2141
2208
  const results = await mapWithConcurrency(
@@ -2151,10 +2218,10 @@ async function validateResumeFileObservations(events, projectRoot) {
2151
2218
  }
2152
2219
  function formatResumeValidationNotice(validation, projectRoot) {
2153
2220
  if (validation.staleFiles.length === 0) return null;
2154
- const root = path6.resolve(projectRoot);
2221
+ const root = path7.resolve(projectRoot);
2155
2222
  const shown = validation.staleFiles.slice(0, NOTICE_PATH_LIMIT).map((entry) => {
2156
- const relative6 = path6.relative(root, entry.path);
2157
- const display = isInside2(root, entry.path) ? relative6 || "." : entry.path;
2223
+ const relative7 = path7.relative(root, entry.path);
2224
+ const display = isInside2(root, entry.path) ? relative7 || "." : entry.path;
2158
2225
  return `- ${JSON.stringify(display)} [${entry.status}]`;
2159
2226
  });
2160
2227
  const omitted = validation.staleFiles.length - shown.length;
@@ -2287,9 +2354,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
2287
2354
  static LIST_SCAN_CONCURRENCY = 32;
2288
2355
  constructor(opts) {
2289
2356
  this.dir = opts.dir;
2290
- this.projectRoot = opts.projectRoot ? path7.resolve(opts.projectRoot) : void 0;
2357
+ this.projectRoot = opts.projectRoot ? path8.resolve(opts.projectRoot) : void 0;
2291
2358
  this.checkpointCas = this.projectRoot ? new SessionCheckpointCas({
2292
- rootDir: path7.join(this.dir, "_cas"),
2359
+ rootDir: path8.join(this.dir, "_cas"),
2293
2360
  projectRoot: this.projectRoot
2294
2361
  }) : void 0;
2295
2362
  this.events = opts.events;
@@ -2355,17 +2422,17 @@ var DefaultSessionStore = class _DefaultSessionStore {
2355
2422
  }
2356
2423
  /** Absolute path to the session index file. */
2357
2424
  get indexFile() {
2358
- return path7.join(this.dir, "_index.jsonl");
2425
+ return path8.join(this.dir, "_index.jsonl");
2359
2426
  }
2360
2427
  /** Join session ID to its absolute path within the store directory. */
2361
2428
  sessionPath(id, ext) {
2362
2429
  return sessionScopedPath(this.dir, id, ext);
2363
2430
  }
2364
2431
  shardManifestPath(shardKey) {
2365
- return shardKey ? path7.join(this.dir, shardKey, "_manifest.json") : path7.join(this.dir, "_manifest.json");
2432
+ return shardKey ? path8.join(this.dir, shardKey, "_manifest.json") : path8.join(this.dir, "_manifest.json");
2366
2433
  }
2367
2434
  shardKeyForSessionId(id) {
2368
- const dirName = path7.dirname(id);
2435
+ const dirName = path8.dirname(id);
2369
2436
  return dirName === "." ? "" : dirName;
2370
2437
  }
2371
2438
  invalidateShardManifestBySessionId(id) {
@@ -2377,7 +2444,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2377
2444
  * subdirectory so sessions group naturally by day.
2378
2445
  */
2379
2446
  async ensureShardDir(id) {
2380
- const dirPath = path7.dirname(sessionScopedPath(this.dir, id, ""));
2447
+ const dirPath = path8.dirname(sessionScopedPath(this.dir, id, ""));
2381
2448
  await ensureDir(dirPath);
2382
2449
  return dirPath;
2383
2450
  }
@@ -2541,7 +2608,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2541
2608
  // Shard directory (sessions/<date>/) — must match create() so the
2542
2609
  // .summary.json sidecar lands next to the JSONL instead of the
2543
2610
  // sessions root (where summaryFor() would never find it).
2544
- dir: path7.dirname(file),
2611
+ dir: path8.dirname(file),
2545
2612
  filePath: file,
2546
2613
  secretScrubber: this.secretScrubber,
2547
2614
  checkpointCas: this.checkpointCas,
@@ -3162,7 +3229,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
3162
3229
  return entry;
3163
3230
  }
3164
3231
  async collectSessionFilesInShard(shardKey) {
3165
- const dir = shardKey ? path7.join(this.dir, shardKey) : this.dir;
3232
+ const dir = shardKey ? path8.join(this.dir, shardKey) : this.dir;
3166
3233
  const entries = await this.collectSessionFiles(dir, shardKey);
3167
3234
  return shardKey ? entries.filter((entry) => entry.id.startsWith(`${shardKey}/`)) : entries.filter((entry) => !entry.id.includes("/"));
3168
3235
  }
@@ -3185,13 +3252,13 @@ var DefaultSessionStore = class _DefaultSessionStore {
3185
3252
  if (entry.name === "_index.jsonl") continue;
3186
3253
  const base = entry.name.replace(/\.jsonl$/, "");
3187
3254
  const id = prefix ? `${prefix}/${base}` : base;
3188
- files.push({ id, filePath: path7.join(dir, entry.name) });
3255
+ files.push({ id, filePath: path8.join(dir, entry.name) });
3189
3256
  }
3190
3257
  }
3191
3258
  const childFileArrays = await Promise.all(
3192
3259
  dirEntries.map((entry) => {
3193
3260
  const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
3194
- return this.collectSessionFiles(path7.join(dir, entry.name), childPrefix, depth + 1);
3261
+ return this.collectSessionFiles(path8.join(dir, entry.name), childPrefix, depth + 1);
3195
3262
  })
3196
3263
  );
3197
3264
  return [...childFileArrays.flat(), ...files];
@@ -3224,7 +3291,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
3224
3291
  const childIdArrays = await Promise.all(
3225
3292
  dirEntries.map((entry) => {
3226
3293
  const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
3227
- return this.collectSessionIds(path7.join(dir, entry.name), childPrefix, depth + 1);
3294
+ return this.collectSessionIds(path8.join(dir, entry.name), childPrefix, depth + 1);
3228
3295
  })
3229
3296
  );
3230
3297
  return [...childIdArrays.flat(), ...fileIds];
@@ -3339,9 +3406,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
3339
3406
  async deleteSession(id) {
3340
3407
  const jsonlPath = this.sessionPath(id, ".jsonl");
3341
3408
  const summaryPath = this.sessionPath(id, ".summary.json");
3342
- const shardDir = path7.dirname(jsonlPath);
3343
- const base = path7.basename(id);
3344
- const sessDir = path7.join(shardDir, base);
3409
+ const shardDir = path8.dirname(jsonlPath);
3410
+ const base = path8.basename(id);
3411
+ const sessDir = path8.join(shardDir, base);
3345
3412
  const deletions = [
3346
3413
  fsp4.unlink(jsonlPath),
3347
3414
  fsp4.unlink(summaryPath),
@@ -3386,7 +3453,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
3386
3453
  */
3387
3454
  async readActiveSessionId() {
3388
3455
  try {
3389
- const raw = await fsp4.readFile(path7.join(this.dir, "active.json"), "utf8");
3456
+ const raw = await fsp4.readFile(path8.join(this.dir, "active.json"), "utf8");
3390
3457
  const active = JSON.parse(raw);
3391
3458
  return active.sessionId ?? null;
3392
3459
  } catch {
@@ -3449,7 +3516,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
3449
3516
  const activeSessionId = await this.readActiveSessionId();
3450
3517
  const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
3451
3518
  const pruneFile = async (dir, name, prefix) => {
3452
- const jsonlPath = path7.join(dir, name);
3519
+ const jsonlPath = path8.join(dir, name);
3453
3520
  try {
3454
3521
  const stat13 = await fsp4.stat(jsonlPath);
3455
3522
  if (stat13.mtimeMs >= cutoff) return;
@@ -3469,7 +3536,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
3469
3536
  continue;
3470
3537
  }
3471
3538
  if (!entry.isDirectory()) continue;
3472
- const dateDir = path7.join(this.dir, entry.name);
3539
+ const dateDir = path8.join(this.dir, entry.name);
3473
3540
  const files = await fsp4.readdir(dateDir, { withFileTypes: true }).catch(() => []);
3474
3541
  for (const file of files) {
3475
3542
  if (!file.isFile() || !isPrunableJsonl(file.name)) continue;
@@ -3481,7 +3548,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
3481
3548
  }
3482
3549
  for (const entry of entries) {
3483
3550
  if (!entry.isDirectory()) continue;
3484
- const dateDir = path7.join(this.dir, entry.name);
3551
+ const dateDir = path8.join(this.dir, entry.name);
3485
3552
  try {
3486
3553
  const remaining = await fsp4.readdir(dateDir);
3487
3554
  if (remaining.length === 0) {
@@ -3606,7 +3673,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
3606
3673
 
3607
3674
  // src/storage/queue-store.ts
3608
3675
  import * as fsp5 from "node:fs/promises";
3609
- import * as path8 from "node:path";
3676
+ import * as path9 from "node:path";
3610
3677
  var QueueStore = class {
3611
3678
  file;
3612
3679
  // Use `| undefined` (not `?`) so exactOptionalPropertyTypes doesn't
@@ -3615,7 +3682,7 @@ var QueueStore = class {
3615
3682
  traceId;
3616
3683
  logger;
3617
3684
  constructor(opts) {
3618
- this.file = path8.join(opts.dir, "queue.json");
3685
+ this.file = path9.join(opts.dir, "queue.json");
3619
3686
  this.events = opts.events;
3620
3687
  this.traceId = opts.traceId;
3621
3688
  this.logger = opts.logger;
@@ -3773,7 +3840,7 @@ function isPersistedQueueItem(v) {
3773
3840
  // src/storage/attachment-store.ts
3774
3841
  import { randomBytes as randomBytes3 } from "node:crypto";
3775
3842
  import * as fsp6 from "node:fs/promises";
3776
- import * as path9 from "node:path";
3843
+ import * as path10 from "node:path";
3777
3844
  var DEFAULT_SPOOL_THRESHOLD = 256 * 1024;
3778
3845
  var PLACEHOLDER_RE = /\[(pasted|image|file) #(\d+)[^\]]*\]|\[file:([^\]]+)\]/g;
3779
3846
  var DefaultAttachmentStore = class {
@@ -3794,7 +3861,7 @@ var DefaultAttachmentStore = class {
3794
3861
  let data = input.data;
3795
3862
  if (this.spoolDir && bytes >= this.spoolThreshold) {
3796
3863
  await fsp6.mkdir(this.spoolDir, { recursive: true });
3797
- spooledPath = path9.join(this.spoolDir, `${id}.bin`);
3864
+ spooledPath = path10.join(this.spoolDir, `${id}.bin`);
3798
3865
  await atomicWrite(spooledPath, input.data, {
3799
3866
  encoding: input.kind === "image" ? "base64" : "utf8"
3800
3867
  });
@@ -3914,8 +3981,8 @@ function mergeAdjacentText(blocks) {
3914
3981
 
3915
3982
  // src/storage/memory-backend.ts
3916
3983
  import { randomUUID as randomUUID2 } from "node:crypto";
3917
- import * as fs3 from "node:fs/promises";
3918
- import * as path10 from "node:path";
3984
+ import * as fs4 from "node:fs/promises";
3985
+ import * as path11 from "node:path";
3919
3986
 
3920
3987
  // src/types/memory.ts
3921
3988
  var MEMORY_TYPE_LABELS = {
@@ -4082,7 +4149,7 @@ var FileMemoryBackend = class {
4082
4149
  }
4083
4150
  async getMtime(file) {
4084
4151
  try {
4085
- const stat13 = await fs3.stat(file);
4152
+ const stat13 = await fs4.stat(file);
4086
4153
  return stat13.mtimeMs;
4087
4154
  } catch {
4088
4155
  return 0;
@@ -4129,10 +4196,10 @@ var FileMemoryBackend = class {
4129
4196
  }
4130
4197
  async remember(scope, entry, filePath) {
4131
4198
  const file = this.resolveFile(filePath, scope);
4132
- await ensureDir(path10.dirname(file));
4199
+ await ensureDir(path11.dirname(file));
4133
4200
  let existing = "";
4134
4201
  try {
4135
- existing = await fs3.readFile(file, "utf8");
4202
+ existing = await fs4.readFile(file, "utf8");
4136
4203
  } catch {
4137
4204
  }
4138
4205
  const id = `mem_${Date.now()}_${randomUUID2().slice(0, 8)}`;
@@ -4150,7 +4217,7 @@ ${line}`;
4150
4217
  return withFileLock(file, async () => {
4151
4218
  let existing;
4152
4219
  try {
4153
- existing = await fs3.readFile(file, "utf8");
4220
+ existing = await fs4.readFile(file, "utf8");
4154
4221
  } catch {
4155
4222
  return 0;
4156
4223
  }
@@ -4187,7 +4254,7 @@ ${line}`;
4187
4254
  async readAll(scope, filePath) {
4188
4255
  const file = this.resolveFile(filePath, scope);
4189
4256
  try {
4190
- return await fs3.readFile(file, "utf8");
4257
+ return await fs4.readFile(file, "utf8");
4191
4258
  } catch {
4192
4259
  return "";
4193
4260
  }
@@ -4211,7 +4278,7 @@ ${line}`;
4211
4278
  const file = this.resolveFile(filePath, scope);
4212
4279
  let existing;
4213
4280
  try {
4214
- existing = await fs3.readFile(file, "utf8");
4281
+ existing = await fs4.readFile(file, "utf8");
4215
4282
  } catch {
4216
4283
  return 0;
4217
4284
  }
@@ -4231,7 +4298,7 @@ ${line}`;
4231
4298
  const next = lines.join("\n");
4232
4299
  const backup = `${file}.bak.${Date.now()}`;
4233
4300
  try {
4234
- await fs3.copyFile(file, backup);
4301
+ await fs4.copyFile(file, backup);
4235
4302
  await pruneConsolidateBackups(file);
4236
4303
  } catch {
4237
4304
  }
@@ -4245,14 +4312,14 @@ ${line}`;
4245
4312
  }
4246
4313
  };
4247
4314
  async function pruneConsolidateBackups(file) {
4248
- const dir = path10.dirname(file);
4249
- const base = path10.basename(file);
4315
+ const dir = path11.dirname(file);
4316
+ const base = path11.basename(file);
4250
4317
  const prefix = `${base}.bak.`;
4251
- const backups = (await fs3.readdir(dir)).filter((name) => name.startsWith(prefix)).sort().reverse();
4318
+ const backups = (await fs4.readdir(dir)).filter((name) => name.startsWith(prefix)).sort().reverse();
4252
4319
  await Promise.all(
4253
4320
  backups.slice(MAX_MEMORY_CONSOLIDATE_BACKUPS).map(async (name) => {
4254
4321
  try {
4255
- await fs3.unlink(path10.join(dir, name));
4322
+ await fs4.unlink(path11.join(dir, name));
4256
4323
  } catch {
4257
4324
  }
4258
4325
  })
@@ -4268,7 +4335,7 @@ function parseEntries(raw, scope = "project-memory") {
4268
4335
  }
4269
4336
 
4270
4337
  // src/storage/memory-graph-backend.ts
4271
- import * as fs4 from "node:fs/promises";
4338
+ import * as fs5 from "node:fs/promises";
4272
4339
  var GraphMemoryBackend = class _GraphMemoryBackend {
4273
4340
  kind = "graph";
4274
4341
  file;
@@ -4436,7 +4503,7 @@ var GraphMemoryBackend = class _GraphMemoryBackend {
4436
4503
  this.loadedScope = scope;
4437
4504
  this.loaded = true;
4438
4505
  try {
4439
- await fs4.unlink(this.graphFile);
4506
+ await fs5.unlink(this.graphFile);
4440
4507
  } catch {
4441
4508
  }
4442
4509
  }
@@ -4476,7 +4543,7 @@ var GraphMemoryBackend = class _GraphMemoryBackend {
4476
4543
  async loadGraph(scope) {
4477
4544
  if (this.loaded && this.loadedScope === scope) return;
4478
4545
  try {
4479
- const raw = await fs4.readFile(this.graphFile, "utf8");
4546
+ const raw = await fs5.readFile(this.graphFile, "utf8");
4480
4547
  const data = JSON.parse(raw);
4481
4548
  this.nodes = new Map(data.nodes);
4482
4549
  this.edges = data.edges;
@@ -4498,10 +4565,10 @@ var GraphMemoryBackend = class _GraphMemoryBackend {
4498
4565
  edges: this.edges
4499
4566
  };
4500
4567
  const dir = this.graphFile.substring(0, this.graphFile.lastIndexOf("/"));
4501
- await fs4.mkdir(dir, { recursive: true });
4568
+ await fs5.mkdir(dir, { recursive: true });
4502
4569
  const tmp = `${this.graphFile}.tmp`;
4503
- await fs4.writeFile(tmp, JSON.stringify(data));
4504
- await fs4.rename(tmp, this.graphFile);
4570
+ await fs5.writeFile(tmp, JSON.stringify(data));
4571
+ await fs5.rename(tmp, this.graphFile);
4505
4572
  } catch {
4506
4573
  }
4507
4574
  }
@@ -4620,7 +4687,7 @@ function simpleHash(s) {
4620
4687
 
4621
4688
  // src/utils/instruction-file.ts
4622
4689
  import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
4623
- import * as path11 from "node:path";
4690
+ import * as path12 from "node:path";
4624
4691
  import { fileURLToPath } from "node:url";
4625
4692
  var textCache = /* @__PURE__ */ new Map();
4626
4693
  var rootCandidates;
@@ -4630,7 +4697,7 @@ function readBundledInstructionText(relativePath) {
4630
4697
  let resolved = "";
4631
4698
  for (const root of instructionRootCandidates()) {
4632
4699
  try {
4633
- resolved = readFileSync2(path11.join(root, relativePath), "utf8").trimEnd();
4700
+ resolved = readFileSync2(path12.join(root, relativePath), "utf8").trimEnd();
4634
4701
  break;
4635
4702
  } catch {
4636
4703
  }
@@ -4646,11 +4713,11 @@ function renderInstructionTemplate(template, values) {
4646
4713
  }
4647
4714
  function instructionRootCandidates() {
4648
4715
  if (rootCandidates !== void 0) return rootCandidates;
4649
- const here = path11.dirname(fileURLToPath(import.meta.url));
4716
+ const here = path12.dirname(fileURLToPath(import.meta.url));
4650
4717
  const candidates = [
4651
- path11.resolve(here, "../../instructions"),
4652
- path11.resolve(here, "../instructions"),
4653
- path11.resolve(here, "instructions")
4718
+ path12.resolve(here, "../../instructions"),
4719
+ path12.resolve(here, "../instructions"),
4720
+ path12.resolve(here, "instructions")
4654
4721
  ];
4655
4722
  rootCandidates = candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
4656
4723
  return rootCandidates;
@@ -4664,6 +4731,43 @@ function isDirectory(candidate) {
4664
4731
  }
4665
4732
 
4666
4733
  // src/storage/memory-consolidator.ts
4734
+ function isSuperMemoryRecord(value) {
4735
+ return typeof value === "object" && value !== null && typeof value.text === "string";
4736
+ }
4737
+ function toPromptEntries(records, limit) {
4738
+ return records.filter(isSuperMemoryRecord).filter((record) => record.scope === "project").slice(0, limit).map((record) => ({
4739
+ scope: "project-memory",
4740
+ text: record.text,
4741
+ ts: record.updatedAt ?? record.createdAt ?? ""
4742
+ }));
4743
+ }
4744
+ function toSuperMemoryKind(type) {
4745
+ switch (type) {
4746
+ case "decision":
4747
+ case "convention":
4748
+ case "preference":
4749
+ case "anti_pattern":
4750
+ return type;
4751
+ case "reference":
4752
+ return "file_note";
4753
+ default:
4754
+ return "fact";
4755
+ }
4756
+ }
4757
+ function importanceFromPriority(priority) {
4758
+ switch (priority) {
4759
+ case "critical":
4760
+ return 0.95;
4761
+ case "high":
4762
+ return 0.8;
4763
+ case "medium":
4764
+ return 0.55;
4765
+ case "low":
4766
+ return 0.25;
4767
+ default:
4768
+ return 0.6;
4769
+ }
4770
+ }
4667
4771
  function buildConsolidationPrompt(finalText, iterations, existingEntries) {
4668
4772
  const existingBlock = existingEntries.length > 0 ? `
4669
4773
 
@@ -4675,36 +4779,11 @@ ${existingEntries.map((e) => `- [${e.ts.slice(0, 10)}] ${e.text}`).join("\n")}`
4675
4779
  existingEntries: existingBlock
4676
4780
  });
4677
4781
  }
4678
- function resolveDeletionBackend(store) {
4679
- const backend = store.getBackend?.();
4680
- if (!backend || typeof backend !== "object") return void 0;
4681
- const candidate = backend;
4682
- if (typeof candidate.deleteSuperMemory !== "function") return void 0;
4683
- if (typeof candidate.listSuper !== "function") return void 0;
4684
- return candidate;
4685
- }
4686
- async function deleteByQueryViaBackend(backend, query, reason) {
4687
- const memories = await backend.listSuper(["active", "stale"]);
4688
- const needle = query.toLowerCase();
4689
- const matches = memories.filter((m) => {
4690
- const legacyScope = m.legacyScope ?? m.scope;
4691
- if (legacyScope !== "project" && legacyScope !== "project-memory") return false;
4692
- return m.id === query || m.text.toLowerCase().includes(needle) || (m.tags?.some((t) => t.toLowerCase() === needle) ?? false) || (m.anchors?.some((a) => a.path?.toLowerCase().includes(needle)) ?? false);
4693
- });
4694
- let deleted = 0;
4695
- for (const m of matches) {
4696
- try {
4697
- await backend.deleteSuperMemory(m.id, reason);
4698
- deleted++;
4699
- } catch {
4700
- }
4701
- }
4702
- return deleted;
4703
- }
4704
4782
  var SessionMemoryConsolidator = class {
4705
4783
  name = "session-memory-consolidator";
4706
4784
  owner = "core";
4707
4785
  memoryStore;
4786
+ superMemory;
4708
4787
  provider;
4709
4788
  model;
4710
4789
  minIterations;
@@ -4712,6 +4791,7 @@ var SessionMemoryConsolidator = class {
4712
4791
  oneShotOrchestrator;
4713
4792
  constructor(opts) {
4714
4793
  this.memoryStore = opts.memoryStore;
4794
+ this.superMemory = opts.superMemory;
4715
4795
  this.provider = opts.provider;
4716
4796
  this.model = opts.model;
4717
4797
  this.minIterations = opts.minIterations ?? 2;
@@ -4727,9 +4807,20 @@ var SessionMemoryConsolidator = class {
4727
4807
  const _finalText = result.finalText;
4728
4808
  const _iterations = result.iterations;
4729
4809
  const _model = this.model ?? ctx.model;
4810
+ const _sessionId = ctx.session.id;
4730
4811
  void (async () => {
4731
4812
  try {
4732
- const existingEntries = await this.memoryStore.list("project-memory", this.maxExistingEntries);
4813
+ let existingEntries;
4814
+ if (this.superMemory) {
4815
+ const records = this.superMemory.listSuper ? await this.superMemory.listSuper(["active"]) : await this.superMemory.searchSuper?.("", {
4816
+ limit: this.maxExistingEntries,
4817
+ scope: "project",
4818
+ includeStatuses: ["active"]
4819
+ }) ?? [];
4820
+ existingEntries = toPromptEntries(records, this.maxExistingEntries);
4821
+ } else {
4822
+ existingEntries = await this.memoryStore.list("project-memory", this.maxExistingEntries);
4823
+ }
4733
4824
  const prompt = buildConsolidationPrompt(
4734
4825
  _finalText,
4735
4826
  _iterations,
@@ -4766,59 +4857,33 @@ var SessionMemoryConsolidator = class {
4766
4857
  const parsed = JSON.parse(jsonMatch[0]);
4767
4858
  if (!Array.isArray(parsed.operations) || parsed.operations.length === 0) return;
4768
4859
  let added = 0;
4769
- let edited = 0;
4770
- let deleted = 0;
4771
4860
  for (const op of parsed.operations) {
4772
- switch (op.action) {
4773
- case "add": {
4774
- if (op.text?.trim()) {
4775
- await this.memoryStore.remember(op.text.trim(), void 0, {
4776
- type: op.type,
4777
- tags: op.tags,
4778
- priority: op.priority
4779
- });
4780
- added++;
4781
- }
4782
- break;
4783
- }
4784
- case "edit": {
4785
- if (op.query && op.text?.trim()) {
4786
- const reason = `memory-consolidator edit: "${op.query}"`;
4787
- const backend = resolveDeletionBackend(this.memoryStore);
4788
- if (backend) {
4789
- await deleteByQueryViaBackend(backend, op.query, reason);
4790
- } else {
4791
- await this.memoryStore.forget(op.query);
4792
- }
4793
- await this.memoryStore.remember(op.text.trim(), void 0, {
4794
- type: op.type,
4795
- tags: op.tags,
4796
- priority: op.priority
4797
- });
4798
- edited++;
4799
- }
4800
- break;
4801
- }
4802
- case "delete": {
4803
- if (op.query) {
4804
- const reason = `memory-consolidator delete: "${op.query}"`;
4805
- const backend = resolveDeletionBackend(this.memoryStore);
4806
- if (backend) {
4807
- deleted += await deleteByQueryViaBackend(backend, op.query, reason);
4808
- } else {
4809
- deleted += await this.memoryStore.forget(op.query);
4810
- }
4811
- }
4812
- break;
4861
+ try {
4862
+ if (!op || typeof op !== "object" || op.action !== "add") continue;
4863
+ if (typeof op.text !== "string" || !op.text.trim()) continue;
4864
+ const memoryText = op.text.trim();
4865
+ if (this.superMemory) {
4866
+ await this.superMemory.rememberSuper({
4867
+ text: memoryText,
4868
+ scope: "project",
4869
+ kind: toSuperMemoryKind(op.type),
4870
+ tags: op.tags,
4871
+ importance: importanceFromPriority(op.priority),
4872
+ sources: [{ type: "session", sessionId: _sessionId }]
4873
+ });
4874
+ } else {
4875
+ await this.memoryStore.remember(memoryText, void 0, {
4876
+ type: op.type,
4877
+ tags: op.tags,
4878
+ priority: op.priority
4879
+ });
4813
4880
  }
4881
+ added++;
4882
+ } catch {
4814
4883
  }
4815
4884
  }
4816
- if (added > 0 || edited > 0 || deleted > 0) {
4817
- const parts = [];
4818
- if (added) parts.push(`${added} added`);
4819
- if (edited) parts.push(`${edited} edited`);
4820
- if (deleted) parts.push(`${deleted} deleted`);
4821
- process.stderr.write(`[memory] Session consolidation: ${parts.join(", ")}
4885
+ if (added > 0) {
4886
+ process.stderr.write(`[memory] Session consolidation: ${added} added
4822
4887
  `);
4823
4888
  }
4824
4889
  } catch {
@@ -4900,8 +4965,8 @@ function deepFreeze(obj) {
4900
4965
 
4901
4966
  // src/storage/provider-config-watcher.ts
4902
4967
  import * as syncFs from "node:fs";
4903
- import * as fs5 from "node:fs/promises";
4904
- import * as path12 from "node:path";
4968
+ import * as fs6 from "node:fs/promises";
4969
+ import * as path13 from "node:path";
4905
4970
 
4906
4971
  // src/security/config-secrets.ts
4907
4972
  function decryptConfigSecrets(cfg, vault, opts) {
@@ -4947,7 +5012,7 @@ function isSecretField(name) {
4947
5012
  async function readProviderSnapshot(configPath, vault, warn) {
4948
5013
  let raw;
4949
5014
  try {
4950
- raw = await fs5.readFile(configPath, "utf8");
5015
+ raw = await fs6.readFile(configPath, "utf8");
4951
5016
  } catch (err) {
4952
5017
  if (err.code !== "ENOENT") {
4953
5018
  warn?.(`Could not read ${configPath}: ${err.message}`);
@@ -4963,7 +5028,8 @@ async function readProviderSnapshot(configPath, vault, warn) {
4963
5028
  }
4964
5029
  const decrypted = decryptConfigSecrets(parsed, vault, warn ? { warn } : {});
4965
5030
  const snapshot = {
4966
- providers: decrypted.providers ?? {}
5031
+ providers: decrypted.providers ?? {},
5032
+ snapshotHasProviders: decrypted.providers !== void 0
4967
5033
  };
4968
5034
  if (typeof decrypted.apiKey === "string") snapshot.apiKey = decrypted.apiKey;
4969
5035
  if (typeof decrypted.baseUrl === "string") snapshot.baseUrl = decrypted.baseUrl;
@@ -4972,9 +5038,12 @@ async function readProviderSnapshot(configPath, vault, warn) {
4972
5038
  if (Array.isArray(decrypted.fallbackModels)) snapshot.fallbackModels = decrypted.fallbackModels;
4973
5039
  if (decrypted.fallbackProfiles) snapshot.fallbackProfiles = decrypted.fallbackProfiles;
4974
5040
  if (Array.isArray(decrypted.favoriteModels)) snapshot.favoriteModels = decrypted.favoriteModels;
4975
- if (typeof decrypted.favoriteModelsOnly === "boolean") snapshot.favoriteModelsOnly = decrypted.favoriteModelsOnly;
5041
+ if (typeof decrypted.favoriteModelsOnly === "boolean")
5042
+ snapshot.favoriteModelsOnly = decrypted.favoriteModelsOnly;
4976
5043
  if (decrypted.modelMatrix) snapshot.modelMatrix = decrypted.modelMatrix;
4977
5044
  if (typeof decrypted.fallbackAuto === "boolean") snapshot.fallbackAuto = decrypted.fallbackAuto;
5045
+ if (Array.isArray(decrypted.modelAvailabilitySchedule))
5046
+ snapshot.modelAvailabilitySchedule = decrypted.modelAvailabilitySchedule;
4978
5047
  return snapshot;
4979
5048
  }
4980
5049
  function serializeSnapshot(s) {
@@ -4988,13 +5057,14 @@ function serializeSnapshot(s) {
4988
5057
  favoriteModels: s.favoriteModels ?? null,
4989
5058
  favoriteModelsOnly: s.favoriteModelsOnly ?? null,
4990
5059
  modelMatrix: s.modelMatrix ?? null,
4991
- fallbackAuto: s.fallbackAuto ?? null
5060
+ fallbackAuto: s.fallbackAuto ?? null,
5061
+ modelAvailabilitySchedule: s.modelAvailabilitySchedule ?? null
4992
5062
  });
4993
5063
  }
4994
5064
  function watchProviderConfig(configPath, vault, onChange, opts = {}) {
4995
5065
  const debounceMs = opts.debounceMs ?? 200;
4996
5066
  const warn = opts.warn;
4997
- const base = path12.basename(configPath);
5067
+ const base = path13.basename(configPath);
4998
5068
  let timer;
4999
5069
  let closed = false;
5000
5070
  let lastSerialized;
@@ -5005,7 +5075,7 @@ function watchProviderConfig(configPath, vault, onChange, opts = {}) {
5005
5075
  });
5006
5076
  let watcher;
5007
5077
  try {
5008
- watcher = syncFs.watch(path12.dirname(configPath), { recursive: false });
5078
+ watcher = syncFs.watch(path13.dirname(configPath), { recursive: false });
5009
5079
  } catch (err) {
5010
5080
  warn?.(`Provider config watcher could not start: ${err.message}`);
5011
5081
  return { close: () => {
@@ -5048,8 +5118,8 @@ function watchProviderConfig(configPath, vault, onChange, opts = {}) {
5048
5118
  }
5049
5119
 
5050
5120
  // src/storage/config-loader.ts
5051
- import * as fs6 from "node:fs/promises";
5052
- import * as path13 from "node:path";
5121
+ import * as fs7 from "node:fs/promises";
5122
+ import * as path14 from "node:path";
5053
5123
 
5054
5124
  // src/types/config.ts
5055
5125
  var DEFAULT_TUI_THINKING_WORD = "thinking";
@@ -5349,6 +5419,7 @@ var defaultIndexing = {
5349
5419
  };
5350
5420
  var IN_PROJECT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
5351
5421
  "version",
5422
+ "activeProfile",
5352
5423
  "model",
5353
5424
  "cwd",
5354
5425
  "context",
@@ -5371,6 +5442,7 @@ var IN_PROJECT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
5371
5442
  "fallbackProfiles",
5372
5443
  "favoriteModels",
5373
5444
  "favoriteModelsOnly",
5445
+ "modelAvailabilitySchedule",
5374
5446
  "fallbackAuto",
5375
5447
  "models",
5376
5448
  "modelMatrix",
@@ -5406,6 +5478,7 @@ var KNOWN_DENIED_IN_PROJECT = [
5406
5478
  ];
5407
5479
  var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
5408
5480
  "version",
5481
+ "activeProfile",
5409
5482
  "provider",
5410
5483
  "model",
5411
5484
  "apiKey",
@@ -5422,6 +5495,7 @@ var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
5422
5495
  "fallbackProfiles",
5423
5496
  "favoriteModels",
5424
5497
  "favoriteModelsOnly",
5498
+ "modelAvailabilitySchedule",
5425
5499
  "fallbackAuto",
5426
5500
  "hooks",
5427
5501
  "plugins",
@@ -5569,8 +5643,8 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
5569
5643
  return out;
5570
5644
  }
5571
5645
  function samePath(a, b) {
5572
- let ra = path13.resolve(a);
5573
- let rb = path13.resolve(b);
5646
+ let ra = path14.resolve(a);
5647
+ let rb = path14.resolve(b);
5574
5648
  if (process.platform === "win32" || process.platform === "darwin") {
5575
5649
  ra = ra.toLowerCase();
5576
5650
  rb = rb.toLowerCase();
@@ -5592,7 +5666,7 @@ function deepMerge2(base, patch) {
5592
5666
  opts
5593
5667
  );
5594
5668
  }
5595
- var DefaultConfigLoader = class {
5669
+ var DefaultConfigLoader = class _DefaultConfigLoader {
5596
5670
  paths;
5597
5671
  strict;
5598
5672
  vault;
@@ -5626,12 +5700,17 @@ var DefaultConfigLoader = class {
5626
5700
  let cfg = { ...BEHAVIOR_DEFAULTS };
5627
5701
  await this.ensureGlobalDefaults();
5628
5702
  const inProjectCollides = samePath(this.paths.inProjectConfig, this.paths.globalConfig) || samePath(this.paths.inProjectConfig, this.paths.projectLocalConfig);
5629
- const [global, local, inProject] = await Promise.all([
5703
+ const [bootstrap, local, inProject] = await Promise.all([
5630
5704
  this.readJson(this.paths.globalConfig),
5631
5705
  this.readJson(this.paths.projectLocalConfig),
5632
5706
  inProjectCollides ? Promise.resolve({}) : this.readJson(this.paths.inProjectConfig)
5633
5707
  ]);
5634
- cfg = deepMerge2(cfg, global);
5708
+ cfg = deepMerge2(cfg, bootstrap);
5709
+ const profileName = bootstrap.activeProfile ?? "default";
5710
+ const profileCfg = await this.readJson(this.paths.profileConfig(profileName));
5711
+ if (Object.keys(profileCfg).length > 0) {
5712
+ cfg = deepMerge2(cfg, profileCfg);
5713
+ }
5635
5714
  cfg = deepMerge2(cfg, local);
5636
5715
  cfg = deepMerge2(
5637
5716
  cfg,
@@ -5657,10 +5736,11 @@ var DefaultConfigLoader = class {
5657
5736
  cfg = deepMerge2(cfg, patch);
5658
5737
  }
5659
5738
  } catch (err) {
5660
- this.logWarn(
5661
- "Config source load failed",
5662
- { event: "config.source_load_failed", source: src.name, message: toErrorMessage(err) }
5663
- );
5739
+ this.logWarn("Config source load failed", {
5740
+ event: "config.source_load_failed",
5741
+ source: src.name,
5742
+ message: toErrorMessage(err)
5743
+ });
5664
5744
  }
5665
5745
  }
5666
5746
  if (opts.cliFlags) {
@@ -5693,14 +5773,20 @@ var DefaultConfigLoader = class {
5693
5773
  }
5694
5774
  return Object.freeze(cfg);
5695
5775
  }
5776
+ /** Check whether a config object contains only the two bootstrap keys. */
5777
+ static isBootstrapOnly(config) {
5778
+ const BOOTSTRAP_KEYS = /* @__PURE__ */ new Set(["version", "activeProfile"]);
5779
+ return Object.keys(config).every((k) => BOOTSTRAP_KEYS.has(k));
5780
+ }
5696
5781
  async ensureGlobalDefaults() {
5697
5782
  const fp = this.paths.globalConfig;
5698
5783
  const t0 = Date.now();
5699
5784
  try {
5700
5785
  await withFileLock(fp, async () => {
5701
5786
  let parsed;
5787
+ let fileExisted = true;
5702
5788
  try {
5703
- const raw = await fs6.readFile(fp, "utf8");
5789
+ const raw = await fs7.readFile(fp, "utf8");
5704
5790
  const result = safeParse(raw);
5705
5791
  if (!result.ok || !isPlainRecord(result.value)) {
5706
5792
  return;
@@ -5719,29 +5805,46 @@ var DefaultConfigLoader = class {
5719
5805
  durationMs: Date.now() - t0,
5720
5806
  ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5721
5807
  });
5722
- this.logWarn(
5723
- "Config defaults read failed",
5724
- { event: "config.defaults_read_failed", path: fp, message: toErrorMessage(err) }
5725
- );
5808
+ this.logWarn("Config defaults read failed", {
5809
+ event: "config.defaults_read_failed",
5810
+ path: fp,
5811
+ message: toErrorMessage(err)
5812
+ });
5726
5813
  return;
5727
5814
  }
5815
+ fileExisted = false;
5728
5816
  parsed = {};
5729
5817
  }
5730
- const { value, changed } = fillMissingDefaults(
5731
- parsed,
5732
- BEHAVIOR_DEFAULTS
5733
- );
5734
- if (!changed) return;
5735
- await atomicWrite(fp, JSON.stringify(value, null, 2), { mode: 384 });
5736
- this.events?.emit("storage.write", {
5737
- sessionId: "~config~",
5738
- store: "config",
5739
- filePath: fp,
5740
- operation: "ensure_defaults",
5741
- outcome: "success",
5742
- durationMs: Date.now() - t0,
5743
- ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5744
- });
5818
+ const profileName = parsed.activeProfile ?? "default";
5819
+ const profileFp = this.paths.profileConfig(profileName);
5820
+ await this.ensureProfileConfig(profileFp, parsed, fileExisted);
5821
+ const bootstrap = {
5822
+ version: 1,
5823
+ activeProfile: profileName
5824
+ };
5825
+ let needsBootstrapWrite = false;
5826
+ if (parsed.version !== 1) {
5827
+ needsBootstrapWrite = true;
5828
+ }
5829
+ if (parsed.activeProfile === void 0) {
5830
+ needsBootstrapWrite = true;
5831
+ }
5832
+ if (!_DefaultConfigLoader.isBootstrapOnly(parsed)) {
5833
+ needsBootstrapWrite = true;
5834
+ }
5835
+ if (needsBootstrapWrite) {
5836
+ await backupConfigFile(fp, this.paths);
5837
+ await atomicWrite(fp, JSON.stringify(bootstrap, null, 2), { mode: 384 });
5838
+ this.events?.emit("storage.write", {
5839
+ sessionId: "~config~",
5840
+ store: "config",
5841
+ filePath: fp,
5842
+ operation: "ensure_defaults",
5843
+ outcome: "success",
5844
+ durationMs: Date.now() - t0,
5845
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5846
+ });
5847
+ }
5745
5848
  });
5746
5849
  } catch (err) {
5747
5850
  this.events?.emit("storage.error", {
@@ -5755,11 +5858,104 @@ var DefaultConfigLoader = class {
5755
5858
  durationMs: Date.now() - t0,
5756
5859
  ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5757
5860
  });
5758
- this.logWarn(
5759
- "Config defaults write failed",
5760
- { event: "config.defaults_write_failed", path: fp, message: toErrorMessage(err) }
5761
- );
5861
+ this.logWarn("Config defaults write failed", {
5862
+ event: "config.defaults_write_failed",
5863
+ path: fp,
5864
+ message: toErrorMessage(err)
5865
+ });
5866
+ }
5867
+ }
5868
+ /**
5869
+ * Ensure a profile config file exists at the given path.
5870
+ * On first boot: migrate content from the old flat global config, or seed
5871
+ * with behavior defaults. Subsequent boots: fill any missing keys from
5872
+ * BEHAVIOR_DEFAULTS (keeping user settings intact).
5873
+ *
5874
+ * CRITICAL SAFETY NET: when the profile already exists but the old global
5875
+ * config has extra non-bootstrap keys (because WebUI/CLI persistence functions
5876
+ * mistakenly wrote settings to the root config), those keys are merged into
5877
+ * the profile so they are NOT silently destroyed by the subsequent bootstrap
5878
+ * trim. This prevents the "config keeps getting emptied" bug.
5879
+ */
5880
+ async ensureProfileConfig(profileFp, oldGlobalParsed, oldFileExisted) {
5881
+ const t0 = Date.now();
5882
+ let parsed;
5883
+ let existed = true;
5884
+ try {
5885
+ const raw = await fs7.readFile(profileFp, "utf8");
5886
+ const result = safeParse(raw);
5887
+ if (!result.ok || !isPlainRecord(result.value)) {
5888
+ this.logWarn("Profile config parse failed \u2014 falling back to defaults", {
5889
+ event: "config.profile_parse_failed",
5890
+ path: profileFp
5891
+ });
5892
+ parsed = {};
5893
+ } else {
5894
+ parsed = result.value;
5895
+ }
5896
+ } catch (err) {
5897
+ if (err.code !== "ENOENT") {
5898
+ this.logWarn("Profile config read failed", {
5899
+ event: "config.profile_read_failed",
5900
+ path: profileFp,
5901
+ message: toErrorMessage(err)
5902
+ });
5903
+ return;
5904
+ }
5905
+ existed = false;
5906
+ parsed = {};
5907
+ }
5908
+ const profileHasContent = existed && Object.keys(parsed).some((k) => k !== "version" && k !== "activeProfile");
5909
+ let seed;
5910
+ if (!profileHasContent && oldFileExisted && Object.keys(oldGlobalParsed).length >= 1) {
5911
+ seed = { ...oldGlobalParsed };
5912
+ delete seed["activeProfile"];
5913
+ const filled = fillMissingDefaults(seed, BEHAVIOR_DEFAULTS);
5914
+ seed = filled.value;
5915
+ } else {
5916
+ if (existed && !_DefaultConfigLoader.isBootstrapOnly(oldGlobalParsed)) {
5917
+ const BOOTSTRAP_KEYS = /* @__PURE__ */ new Set(["version", "activeProfile"]);
5918
+ let merged = false;
5919
+ for (const [k, v] of Object.entries(oldGlobalParsed)) {
5920
+ if (!BOOTSTRAP_KEYS.has(k) && !(k in parsed)) {
5921
+ parsed[k] = v;
5922
+ merged = true;
5923
+ }
5924
+ }
5925
+ if (merged) {
5926
+ const filled2 = fillMissingDefaults(parsed, BEHAVIOR_DEFAULTS);
5927
+ if (filled2.changed) {
5928
+ seed = filled2.value;
5929
+ await backupConfigFile(profileFp, this.paths);
5930
+ await atomicWrite(profileFp, JSON.stringify(seed, null, 2), { mode: 384 });
5931
+ this.events?.emit("storage.write", {
5932
+ sessionId: "~config~",
5933
+ store: "config",
5934
+ filePath: profileFp,
5935
+ operation: "ensure_profile_defaults",
5936
+ outcome: "success",
5937
+ durationMs: Date.now() - t0,
5938
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5939
+ });
5940
+ return;
5941
+ }
5942
+ }
5943
+ }
5944
+ const filled = fillMissingDefaults(parsed, BEHAVIOR_DEFAULTS);
5945
+ if (!filled.changed) return;
5946
+ seed = filled.value;
5762
5947
  }
5948
+ await backupConfigFile(profileFp, this.paths);
5949
+ await atomicWrite(profileFp, JSON.stringify(seed, null, 2), { mode: 384 });
5950
+ this.events?.emit("storage.write", {
5951
+ sessionId: "~config~",
5952
+ store: "config",
5953
+ filePath: profileFp,
5954
+ operation: "ensure_profile_defaults",
5955
+ outcome: "success",
5956
+ durationMs: Date.now() - t0,
5957
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5958
+ });
5763
5959
  }
5764
5960
  /**
5765
5961
  * Persist a sync config to ~/.wrongstack/sync.json, with the token encrypted
@@ -5809,7 +6005,7 @@ var DefaultConfigLoader = class {
5809
6005
  const fp = this.paths.syncConfig;
5810
6006
  const t0 = Date.now();
5811
6007
  try {
5812
- const raw = await fs6.readFile(fp, "utf8");
6008
+ const raw = await fs7.readFile(fp, "utf8");
5813
6009
  const parsed = safeParse(raw);
5814
6010
  if (!parsed.ok || !parsed.value) {
5815
6011
  this.events?.emit("storage.read", {
@@ -5860,10 +6056,10 @@ var DefaultConfigLoader = class {
5860
6056
  error: storageErrorString(err),
5861
6057
  ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5862
6058
  });
5863
- this.logWarn(
5864
- "Config sync load failed",
5865
- { event: "config.sync_load_failed", message: toErrorMessage(err) }
5866
- );
6059
+ this.logWarn("Config sync load failed", {
6060
+ event: "config.sync_load_failed",
6061
+ message: toErrorMessage(err)
6062
+ });
5867
6063
  return null;
5868
6064
  }
5869
6065
  }
@@ -5871,7 +6067,7 @@ var DefaultConfigLoader = class {
5871
6067
  const t0 = Date.now();
5872
6068
  let mtimeMs = null;
5873
6069
  try {
5874
- const stat13 = await fs6.stat(file);
6070
+ const stat13 = await fs7.stat(file);
5875
6071
  mtimeMs = stat13.mtimeMs;
5876
6072
  const cached = this.jsonCache.get(file);
5877
6073
  if (cached && cached.mtimeMs === mtimeMs) {
@@ -5892,15 +6088,16 @@ var DefaultConfigLoader = class {
5892
6088
  error: storageErrorString(err),
5893
6089
  ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5894
6090
  });
5895
- this.logWarn(
5896
- "Config read failed (stat)",
5897
- { event: "config.read_failed", path: file, message: toErrorMessage(err) }
5898
- );
6091
+ this.logWarn("Config read failed (stat)", {
6092
+ event: "config.read_failed",
6093
+ path: file,
6094
+ message: toErrorMessage(err)
6095
+ });
5899
6096
  return {};
5900
6097
  }
5901
6098
  let raw;
5902
6099
  try {
5903
- raw = await fs6.readFile(file, "utf8");
6100
+ raw = await fs7.readFile(file, "utf8");
5904
6101
  } catch (err) {
5905
6102
  if (err.code !== "ENOENT") {
5906
6103
  this.events?.emit("storage.read", {
@@ -5913,10 +6110,11 @@ var DefaultConfigLoader = class {
5913
6110
  error: storageErrorString(err),
5914
6111
  ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5915
6112
  });
5916
- this.logWarn(
5917
- "Config read failed (read)",
5918
- { event: "config.read_failed", path: file, message: toErrorMessage(err) }
5919
- );
6113
+ this.logWarn("Config read failed (read)", {
6114
+ event: "config.read_failed",
6115
+ path: file,
6116
+ message: toErrorMessage(err)
6117
+ });
5920
6118
  }
5921
6119
  this.jsonCache.set(file, { mtimeMs: null, value: {} });
5922
6120
  return {};
@@ -5933,10 +6131,10 @@ var DefaultConfigLoader = class {
5933
6131
  error: "parse error or empty file",
5934
6132
  ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
5935
6133
  });
5936
- this.logWarn(
5937
- "Config parse failed \u2014 falling back to defaults",
5938
- { event: "config.parse_failed", path: file }
5939
- );
6134
+ this.logWarn("Config parse failed \u2014 falling back to defaults", {
6135
+ event: "config.parse_failed",
6136
+ path: file
6137
+ });
5940
6138
  return {};
5941
6139
  }
5942
6140
  this.jsonCache.set(file, { mtimeMs, value: structuredClone(parsed.value) });
@@ -6062,7 +6260,7 @@ var DEFAULT_CONFIG_MIGRATIONS = [];
6062
6260
  // src/storage/recovery-lock.ts
6063
6261
  import * as fsp7 from "node:fs/promises";
6064
6262
  import * as os2 from "node:os";
6065
- import * as path14 from "node:path";
6263
+ import * as path15 from "node:path";
6066
6264
  var LOCK_FILE = "active.json";
6067
6265
  var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
6068
6266
  var RecoveryLock = class {
@@ -6073,7 +6271,7 @@ var RecoveryLock = class {
6073
6271
  sessionStore;
6074
6272
  probe;
6075
6273
  constructor(opts) {
6076
- this.file = path14.join(opts.dir, LOCK_FILE);
6274
+ this.file = path15.join(opts.dir, LOCK_FILE);
6077
6275
  this.pid = opts.pid ?? process.pid;
6078
6276
  this.hostname = opts.hostname ?? os2.hostname();
6079
6277
  this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
@@ -6134,7 +6332,7 @@ var RecoveryLock = class {
6134
6332
  * null return before calling this.
6135
6333
  */
6136
6334
  async write(sessionId) {
6137
- await ensureDir(path14.dirname(this.file));
6335
+ await ensureDir(path15.dirname(this.file));
6138
6336
  const lock = {
6139
6337
  v: 1,
6140
6338
  sessionId,
@@ -6202,7 +6400,7 @@ function defaultIsPidAlive(pid) {
6202
6400
  }
6203
6401
 
6204
6402
  // src/storage/session-reader.ts
6205
- import * as fs7 from "node:fs/promises";
6403
+ import * as fs8 from "node:fs/promises";
6206
6404
  var DefaultSessionReader = class _DefaultSessionReader {
6207
6405
  store;
6208
6406
  eventCache = /* @__PURE__ */ new Map();
@@ -6220,7 +6418,7 @@ var DefaultSessionReader = class _DefaultSessionReader {
6220
6418
  const sessionPath = sessionScopedPath(rootDir, sessionId, ".jsonl");
6221
6419
  let mtimeMs = null;
6222
6420
  try {
6223
- const stat13 = await fs7.stat(sessionPath);
6421
+ const stat13 = await fs8.stat(sessionPath);
6224
6422
  mtimeMs = stat13.mtimeMs;
6225
6423
  } catch {
6226
6424
  this.eventCache.delete(sessionId);
@@ -6584,7 +6782,7 @@ function renderPlainText(meta, events) {
6584
6782
 
6585
6783
  // src/storage/annotations-store.ts
6586
6784
  import { randomUUID as randomUUID3 } from "node:crypto";
6587
- import * as fs8 from "node:fs/promises";
6785
+ import * as fs9 from "node:fs/promises";
6588
6786
  var FILE_VERSION = 1;
6589
6787
  var MAX_TEXT_LENGTH = 2e3;
6590
6788
  var MAX_ANNOTATIONS = 1e3;
@@ -6811,7 +7009,7 @@ var AnnotationsStore = class {
6811
7009
  const fp = this.filePath(sessionId);
6812
7010
  let raw;
6813
7011
  try {
6814
- raw = await fs8.readFile(fp, "utf8");
7012
+ raw = await fs9.readFile(fp, "utf8");
6815
7013
  } catch (err) {
6816
7014
  if (err.code === "ENOENT") return null;
6817
7015
  throw err;
@@ -6848,8 +7046,8 @@ var AnnotationsStore = class {
6848
7046
  };
6849
7047
 
6850
7048
  // src/storage/replay-log-store.ts
6851
- import * as fs9 from "node:fs/promises";
6852
- import * as path15 from "node:path";
7049
+ import * as fs10 from "node:fs/promises";
7050
+ import * as path16 from "node:path";
6853
7051
 
6854
7052
  // src/replay/hash.ts
6855
7053
  import { createHash as createHash5 } from "node:crypto";
@@ -6938,12 +7136,12 @@ var ReplayLogStore = class {
6938
7136
  const line = JSON.stringify(entry) + "\n";
6939
7137
  let offset2 = 0;
6940
7138
  try {
6941
- const stat13 = await fs9.stat(fp);
7139
+ const stat13 = await fs10.stat(fp);
6942
7140
  offset2 = stat13.size;
6943
7141
  } catch (err) {
6944
7142
  if (err.code !== "ENOENT") throw err;
6945
7143
  }
6946
- await fs9.appendFile(fp, line, "utf8");
7144
+ await fs10.appendFile(fp, line, "utf8");
6947
7145
  cache.set(hash, { entry, offset: offset2, length: Buffer.byteLength(line, "utf8") });
6948
7146
  this.diskCount.set(input.sessionId, currentCount + 1);
6949
7147
  this.events?.emit("storage.write", {
@@ -7071,7 +7269,7 @@ var ReplayLogStore = class {
7071
7269
  const scan = async (dir, prefix, depth) => {
7072
7270
  let entries;
7073
7271
  try {
7074
- entries = await fs9.readdir(dir, { withFileTypes: true });
7272
+ entries = await fs10.readdir(dir, { withFileTypes: true });
7075
7273
  } catch (err) {
7076
7274
  if (depth === 0 && err.code !== "ENOENT") {
7077
7275
  console.warn(JSON.stringify({
@@ -7087,13 +7285,13 @@ var ReplayLogStore = class {
7087
7285
  for (const entry of entries) {
7088
7286
  if (entry.name.startsWith(".")) continue;
7089
7287
  if (entry.isDirectory()) {
7090
- if (depth === 0) await scan(path15.join(dir, entry.name), entry.name, depth + 1);
7288
+ if (depth === 0) await scan(path16.join(dir, entry.name), entry.name, depth + 1);
7091
7289
  continue;
7092
7290
  }
7093
7291
  if (!entry.isFile() || !entry.name.endsWith(".replay.jsonl")) continue;
7094
7292
  const base = entry.name.slice(0, -".replay.jsonl".length);
7095
7293
  const sessionId = prefix ? `${prefix}/${base}` : base;
7096
- const fp = path15.join(dir, entry.name);
7294
+ const fp = path16.join(dir, entry.name);
7097
7295
  out.push({
7098
7296
  sessionId,
7099
7297
  entryCount: await this.countEntries(fp),
@@ -7109,7 +7307,7 @@ var ReplayLogStore = class {
7109
7307
  return sessionScopedPath(this.dir, sessionId, ".replay.jsonl");
7110
7308
  }
7111
7309
  async countEntries(filePath) {
7112
- const handle = await fs9.open(filePath, "r");
7310
+ const handle = await fs10.open(filePath, "r");
7113
7311
  const buffer = Buffer.allocUnsafe(64 * 1024);
7114
7312
  let count = 0;
7115
7313
  let hasNonWhitespace2 = false;
@@ -7150,7 +7348,7 @@ var ReplayLogStore = class {
7150
7348
  async readAll(sessionId) {
7151
7349
  const fp = this.filePath(sessionId);
7152
7350
  try {
7153
- const raw = await fs9.readFile(fp, "utf8");
7351
+ const raw = await fs10.readFile(fp, "utf8");
7154
7352
  const out = [];
7155
7353
  for (const line of raw.split("\n")) {
7156
7354
  if (!line.trim()) continue;
@@ -7186,7 +7384,7 @@ var ReplayLogStore = class {
7186
7384
  const fp = this.filePath(sessionId);
7187
7385
  cache = /* @__PURE__ */ new Map();
7188
7386
  try {
7189
- const handle = await fs9.open(fp, "r");
7387
+ const handle = await fs10.open(fp, "r");
7190
7388
  const CHUNK = 64 * 1024;
7191
7389
  const buffer = Buffer.alloc(CHUNK);
7192
7390
  let leftover = "";
@@ -7237,7 +7435,7 @@ var ReplayLogStore = class {
7237
7435
  async hydrateEntry(sessionId, hash, location) {
7238
7436
  if (location.entry) return location.entry;
7239
7437
  const fp = this.filePath(sessionId);
7240
- const handle = await fs9.open(fp, "r");
7438
+ const handle = await fs10.open(fp, "r");
7241
7439
  try {
7242
7440
  const buffer = Buffer.alloc(location.length);
7243
7441
  const { bytesRead } = await handle.read(buffer, 0, location.length, location.offset);
@@ -7264,8 +7462,8 @@ var ReplayLogStore = class {
7264
7462
  };
7265
7463
 
7266
7464
  // src/storage/session-recovery.ts
7267
- import * as fs10 from "node:fs/promises";
7268
- import * as path16 from "node:path";
7465
+ import * as fs11 from "node:fs/promises";
7466
+ import * as path17 from "node:path";
7269
7467
  var SessionRecovery = class {
7270
7468
  constructor(dir) {
7271
7469
  this.dir = dir;
@@ -7291,7 +7489,7 @@ var SessionRecovery = class {
7291
7489
  const fp = this.filePath(sessionId);
7292
7490
  let stat13;
7293
7491
  try {
7294
- stat13 = await fs10.stat(fp);
7492
+ stat13 = await fs11.stat(fp);
7295
7493
  } catch (err) {
7296
7494
  if (err.code === "ENOENT") return null;
7297
7495
  return null;
@@ -7325,7 +7523,7 @@ var SessionRecovery = class {
7325
7523
  const fp = this.filePath(sessionId);
7326
7524
  let raw;
7327
7525
  try {
7328
- raw = await fs10.readFile(fp, "utf8");
7526
+ raw = await fs11.readFile(fp, "utf8");
7329
7527
  } catch (err) {
7330
7528
  if (err.code === "ENOENT") return null;
7331
7529
  return null;
@@ -7370,7 +7568,7 @@ var SessionRecovery = class {
7370
7568
  const collect = async (dir, prefix, depth) => {
7371
7569
  let entries;
7372
7570
  try {
7373
- entries = await fs10.readdir(dir, { withFileTypes: true });
7571
+ entries = await fs11.readdir(dir, { withFileTypes: true });
7374
7572
  } catch {
7375
7573
  return;
7376
7574
  }
@@ -7380,7 +7578,7 @@ var SessionRecovery = class {
7380
7578
  continue;
7381
7579
  if (entry.isDirectory()) {
7382
7580
  if (depth === 0) {
7383
- await collect(path16.join(dir, entry.name), entry.name, depth + 1);
7581
+ await collect(path17.join(dir, entry.name), entry.name, depth + 1);
7384
7582
  }
7385
7583
  continue;
7386
7584
  }
@@ -7421,7 +7619,7 @@ function hasNonWhitespace(line) {
7421
7619
  }
7422
7620
  async function scanLatestLifecycleBoundary(filePath, size) {
7423
7621
  const CHUNK_SIZE = 64 * 1024;
7424
- const handle = await fs10.open(filePath, "r");
7622
+ const handle = await fs11.open(filePath, "r");
7425
7623
  let position = size;
7426
7624
  let laterLineFragment = Buffer.alloc(0);
7427
7625
  let latestBoundary = null;
@@ -7460,7 +7658,7 @@ async function scanLatestLifecycleBoundary(filePath, size) {
7460
7658
 
7461
7659
  // src/storage/tool-audit-log.ts
7462
7660
  import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
7463
- import * as fs11 from "node:fs/promises";
7661
+ import * as fs12 from "node:fs/promises";
7464
7662
  var GENESIS_PREV = "0".repeat(64);
7465
7663
  var DEFAULT_FSYNC_EVERY = 100;
7466
7664
  var ToolAuditLog = class {
@@ -7530,9 +7728,9 @@ var ToolAuditLog = class {
7530
7728
  isError: input.isError,
7531
7729
  index
7532
7730
  };
7533
- await fs11.appendFile(fp, JSON.stringify(entry) + "\n", "utf8");
7731
+ await fs12.appendFile(fp, JSON.stringify(entry) + "\n", "utf8");
7534
7732
  try {
7535
- const st = await fs11.stat(fp);
7733
+ const st = await fs12.stat(fp);
7536
7734
  this.tailStat.set(input.sessionId, { mtimeMs: st.mtimeMs, size: st.size });
7537
7735
  } catch {
7538
7736
  }
@@ -7579,7 +7777,7 @@ var ToolAuditLog = class {
7579
7777
  const cachedStat = this.tailStat.get(sessionId);
7580
7778
  if (cachedHash !== void 0 && cachedIndex !== void 0 && cachedStat) {
7581
7779
  try {
7582
- const st = await fs11.stat(fp);
7780
+ const st = await fs12.stat(fp);
7583
7781
  if (st.mtimeMs === cachedStat.mtimeMs && st.size === cachedStat.size) {
7584
7782
  return { prevHash: cachedHash, nextIndex: cachedIndex };
7585
7783
  }
@@ -7600,7 +7798,7 @@ var ToolAuditLog = class {
7600
7798
  this.tailHash.set(sessionId, prevHash);
7601
7799
  this.tailIndex.set(sessionId, nextIndex);
7602
7800
  try {
7603
- const st = await fs11.stat(fp);
7801
+ const st = await fs12.stat(fp);
7604
7802
  this.tailStat.set(sessionId, { mtimeMs: st.mtimeMs, size: st.size });
7605
7803
  } catch {
7606
7804
  }
@@ -7721,7 +7919,7 @@ var ToolAuditLog = class {
7721
7919
  async readAll(sessionId) {
7722
7920
  const fp = this.filePath(sessionId);
7723
7921
  try {
7724
- const raw = await fs11.readFile(fp, "utf8");
7922
+ const raw = await fs12.readFile(fp, "utf8");
7725
7923
  const out = [];
7726
7924
  for (const line of raw.split("\n")) {
7727
7925
  if (!line.trim()) continue;
@@ -7759,7 +7957,7 @@ var ToolAuditLog = class {
7759
7957
  }
7760
7958
  async sync(sessionId, fp) {
7761
7959
  try {
7762
- const fh = await fs11.open(fp, "r+");
7960
+ const fh = await fs12.open(fp, "r+");
7763
7961
  try {
7764
7962
  await fh.sync();
7765
7963
  } finally {
@@ -7898,8 +8096,8 @@ var SessionAnalyzer = class {
7898
8096
 
7899
8097
  // src/session-registry.ts
7900
8098
  import { randomUUID as randomUUID5 } from "node:crypto";
7901
- import * as fs12 from "node:fs/promises";
7902
- import * as path17 from "node:path";
8099
+ import * as fs13 from "node:fs/promises";
8100
+ import * as path18 from "node:path";
7903
8101
  var REGISTRY_FILE = "session-registry.json";
7904
8102
  var HEARTBEAT_INTERVAL_MS = 5e3;
7905
8103
  var STALE_TIMEOUT_MS = 3e4;
@@ -7938,7 +8136,7 @@ var SessionRegistry = class {
7938
8136
  */
7939
8137
  lastEntry = null;
7940
8138
  constructor(globalRoot) {
7941
- this.filePath = path17.join(globalRoot, REGISTRY_FILE);
8139
+ this.filePath = path18.join(globalRoot, REGISTRY_FILE);
7942
8140
  }
7943
8141
  // ── Public API ──────────────────────────────────────────────────────────
7944
8142
  /**
@@ -8094,7 +8292,7 @@ var SessionRegistry = class {
8094
8292
  }
8095
8293
  async readAndPrune() {
8096
8294
  try {
8097
- const raw = await fs12.readFile(this.filePath, "utf8");
8295
+ const raw = await fs13.readFile(this.filePath, "utf8");
8098
8296
  const registry = parseRegistry(raw);
8099
8297
  const now = Date.now();
8100
8298
  let pruned = false;
@@ -8147,11 +8345,11 @@ var SessionRegistry = class {
8147
8345
  const retryDelayMs = 20;
8148
8346
  for (let attempt = 0; attempt < maxRetries; attempt++) {
8149
8347
  try {
8150
- await fs12.mkdir(path17.dirname(this.filePath), { recursive: true });
8151
- let lockHandle = await fs12.open(lockPath, "wx").catch(() => null);
8348
+ await fs13.mkdir(path18.dirname(this.filePath), { recursive: true });
8349
+ let lockHandle = await fs13.open(lockPath, "wx").catch(() => null);
8152
8350
  if (!lockHandle) {
8153
8351
  if (await this.breakStaleLock(lockPath)) {
8154
- lockHandle = await fs12.open(lockPath, "wx").catch(() => null);
8352
+ lockHandle = await fs13.open(lockPath, "wx").catch(() => null);
8155
8353
  }
8156
8354
  if (!lockHandle) {
8157
8355
  await new Promise((r) => setTimeout(r, retryDelayMs * (attempt + 1)));
@@ -8160,14 +8358,14 @@ var SessionRegistry = class {
8160
8358
  }
8161
8359
  try {
8162
8360
  await lockHandle.writeFile(String(process.pid)).catch(() => void 0);
8163
- const raw = await fs12.readFile(this.filePath, "utf8").catch(() => "{}");
8361
+ const raw = await fs13.readFile(this.filePath, "utf8").catch(() => "{}");
8164
8362
  const registry = parseRegistry(raw);
8165
8363
  fn(registry);
8166
8364
  await this.writeAtomicLocked(registry);
8167
8365
  return;
8168
8366
  } finally {
8169
8367
  await lockHandle.close();
8170
- await fs12.unlink(lockPath).catch(() => void 0);
8368
+ await fs13.unlink(lockPath).catch(() => void 0);
8171
8369
  }
8172
8370
  } catch {
8173
8371
  return;
@@ -8184,16 +8382,16 @@ var SessionRegistry = class {
8184
8382
  async breakStaleLock(lockPath) {
8185
8383
  try {
8186
8384
  const [stat13, content] = await Promise.all([
8187
- fs12.stat(lockPath),
8385
+ fs13.stat(lockPath),
8188
8386
  /* v8 ignore start -- best-effort lock-content read; .catch only fires if the lock vanished */
8189
- fs12.readFile(lockPath, "utf8").catch(() => "")
8387
+ fs13.readFile(lockPath, "utf8").catch(() => "")
8190
8388
  /* v8 ignore stop */
8191
8389
  ]);
8192
8390
  const ageMs = Date.now() - stat13.mtimeMs;
8193
8391
  const ownerPid = Number.parseInt(content.trim(), 10);
8194
8392
  const ownerDead = Number.isInteger(ownerPid) && ownerPid > 0 && ownerPid !== process.pid && !pidAlive(ownerPid);
8195
8393
  if (ownerDead || ageMs > STALE_LOCK_MS) {
8196
- await fs12.unlink(lockPath).catch(() => void 0);
8394
+ await fs13.unlink(lockPath).catch(() => void 0);
8197
8395
  return true;
8198
8396
  }
8199
8397
  return false;
@@ -8211,41 +8409,41 @@ var SessionRegistry = class {
8211
8409
  await this.writeAtomicFile(registry);
8212
8410
  }
8213
8411
  async writeAtomicFile(registry) {
8214
- const tmp = path17.join(
8215
- path17.dirname(this.filePath),
8216
- `.${path17.basename(this.filePath)}.${randomUUID5().slice(0, 8)}.tmp`
8412
+ const tmp = path18.join(
8413
+ path18.dirname(this.filePath),
8414
+ `.${path18.basename(this.filePath)}.${randomUUID5().slice(0, 8)}.tmp`
8217
8415
  );
8218
8416
  try {
8219
- const handle = await fs12.open(tmp, "w");
8417
+ const handle = await fs13.open(tmp, "w");
8220
8418
  try {
8221
8419
  await handle.writeFile(JSON.stringify(registry, null, 2), "utf8");
8222
8420
  await handle.sync().catch(() => void 0);
8223
8421
  } finally {
8224
8422
  await handle.close();
8225
8423
  }
8226
- await fs12.rename(tmp, this.filePath);
8424
+ await fs13.rename(tmp, this.filePath);
8227
8425
  } catch (err) {
8228
- await fs12.unlink(tmp).catch(() => void 0);
8426
+ await fs13.unlink(tmp).catch(() => void 0);
8229
8427
  throw err;
8230
8428
  }
8231
8429
  }
8232
8430
  async pruneStaleTempFiles() {
8233
8431
  try {
8234
- const dir = path17.dirname(this.filePath);
8235
- const base = path17.basename(this.filePath);
8432
+ const dir = path18.dirname(this.filePath);
8433
+ const base = path18.basename(this.filePath);
8236
8434
  const now = Date.now();
8237
8435
  const stale = [];
8238
- for (const name of await fs12.readdir(dir)) {
8436
+ for (const name of await fs13.readdir(dir)) {
8239
8437
  const isTemp = (name.startsWith(`${base}.`) || name.startsWith(`.${base}.`)) && name.endsWith(".tmp");
8240
8438
  if (!isTemp) continue;
8241
- const stat13 = await fs12.stat(path17.join(dir, name)).catch(() => null);
8439
+ const stat13 = await fs13.stat(path18.join(dir, name)).catch(() => null);
8242
8440
  if (!stat13) continue;
8243
8441
  if (now - stat13.mtimeMs > STALE_TMP_MS) stale.push({ name, mtimeMs: stat13.mtimeMs });
8244
8442
  }
8245
8443
  stale.sort((a, b) => b.mtimeMs - a.mtimeMs);
8246
8444
  await Promise.all(
8247
8445
  stale.slice(MAX_STALE_TMP_FILES).map(async ({ name }) => {
8248
- await fs12.unlink(path17.join(dir, name)).catch(() => void 0);
8446
+ await fs13.unlink(path18.join(dir, name)).catch(() => void 0);
8249
8447
  })
8250
8448
  );
8251
8449
  } catch {
@@ -8980,8 +9178,8 @@ var AgentStatusTracker = class {
8980
9178
  };
8981
9179
 
8982
9180
  // src/fleet-notifier.ts
8983
- import * as fs13 from "node:fs/promises";
8984
- import * as path18 from "node:path";
9181
+ import * as fs14 from "node:fs/promises";
9182
+ import * as path19 from "node:path";
8985
9183
  var INSTANCES_FILE = "webui-instances.json";
8986
9184
  var DISCOVERY_TTL_MS = 2500;
8987
9185
  var COALESCE_MS = 50;
@@ -8996,7 +9194,7 @@ function pidAlive2(pid) {
8996
9194
  }
8997
9195
  }
8998
9196
  function normRoot(root) {
8999
- const resolved = path18.resolve(root);
9197
+ const resolved = path19.resolve(root);
9000
9198
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
9001
9199
  }
9002
9200
  var FleetNotifier = class {
@@ -9043,7 +9241,7 @@ var FleetNotifier = class {
9043
9241
  }
9044
9242
  async discover() {
9045
9243
  try {
9046
- const raw = await fs13.readFile(path18.join(this.baseDir, INSTANCES_FILE), "utf8");
9244
+ const raw = await fs14.readFile(path19.join(this.baseDir, INSTANCES_FILE), "utf8");
9047
9245
  const data = JSON.parse(raw);
9048
9246
  const list = Array.isArray(data?.instances) ? data.instances : [];
9049
9247
  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) => {
@@ -9064,7 +9262,7 @@ async function defaultPost(url) {
9064
9262
 
9065
9263
  // src/storage/session-rewinder.ts
9066
9264
  import * as fsp8 from "node:fs/promises";
9067
- import * as path19 from "node:path";
9265
+ import * as path20 from "node:path";
9068
9266
  var DefaultSessionRewinder = class {
9069
9267
  constructor(sessionsDir, projectRoot) {
9070
9268
  this.sessionsDir = sessionsDir;
@@ -9202,10 +9400,10 @@ async function revertSnapshots(snapshots, projectRoot) {
9202
9400
  for (const snapshot of [...snapshots].reverse()) {
9203
9401
  for (const file of [...snapshot.files].reverse()) {
9204
9402
  try {
9205
- const absPath = path19.resolve(file.path);
9206
- const root = path19.resolve(projectRoot);
9207
- const rel = path19.relative(root, absPath);
9208
- if (rel.startsWith("..") || path19.isAbsolute(rel)) {
9403
+ const absPath = path20.resolve(file.path);
9404
+ const root = path20.resolve(projectRoot);
9405
+ const rel = path20.relative(root, absPath);
9406
+ if (rel.startsWith("..") || path20.isAbsolute(rel)) {
9209
9407
  errors.push(`${file.path}: path resolves outside project root \u2014 skipping`);
9210
9408
  continue;
9211
9409
  }
@@ -10730,8 +10928,8 @@ function normalizeTitle(value) {
10730
10928
 
10731
10929
  // src/storage/prompt-store.ts
10732
10930
  import { createHash as createHash7 } from "node:crypto";
10733
- import * as fs14 from "node:fs/promises";
10734
- import * as path20 from "node:path";
10931
+ import * as fs15 from "node:fs/promises";
10932
+ import * as path21 from "node:path";
10735
10933
  var SCHEMA_VERSION = 2;
10736
10934
  function promptChecksum(content) {
10737
10935
  return createHash7("sha256").update(content, "utf8").digest("hex");
@@ -10795,12 +10993,12 @@ var DefaultPromptStore = class {
10795
10993
  await ensureDir(this.dir);
10796
10994
  const entries = [];
10797
10995
  try {
10798
- const files = await fs14.readdir(this.dir);
10996
+ const files = await fs15.readdir(this.dir);
10799
10997
  for (const file of files) {
10800
10998
  if (!file.endsWith(".json")) continue;
10801
10999
  try {
10802
11000
  const raw = JSON.parse(
10803
- await fs14.readFile(path20.join(this.dir, file), "utf8")
11001
+ await fs15.readFile(path21.join(this.dir, file), "utf8")
10804
11002
  );
10805
11003
  const migrated = migratePromptEntry(raw.entry);
10806
11004
  if (migrated) entries.push(migrated);
@@ -10814,9 +11012,9 @@ var DefaultPromptStore = class {
10814
11012
  );
10815
11013
  }
10816
11014
  async get(id) {
10817
- const file = path20.join(this.dir, `${id}.json`);
11015
+ const file = path21.join(this.dir, `${id}.json`);
10818
11016
  try {
10819
- const raw = JSON.parse(await fs14.readFile(file, "utf8"));
11017
+ const raw = JSON.parse(await fs15.readFile(file, "utf8"));
10820
11018
  return migratePromptEntry(raw.entry);
10821
11019
  } catch {
10822
11020
  return null;
@@ -10824,14 +11022,14 @@ var DefaultPromptStore = class {
10824
11022
  }
10825
11023
  async save(entry) {
10826
11024
  await ensureDir(this.dir);
10827
- const file = path20.join(this.dir, `${entry.id}.json`);
11025
+ const file = path21.join(this.dir, `${entry.id}.json`);
10828
11026
  const raw = { version: SCHEMA_VERSION, entry };
10829
11027
  await atomicWrite(file, JSON.stringify(raw, null, 2));
10830
11028
  }
10831
11029
  async delete(id) {
10832
- const file = path20.join(this.dir, `${id}.json`);
11030
+ const file = path21.join(this.dir, `${id}.json`);
10833
11031
  try {
10834
- await fs14.unlink(file);
11032
+ await fs15.unlink(file);
10835
11033
  return true;
10836
11034
  } catch {
10837
11035
  return false;
@@ -10870,8 +11068,8 @@ var DefaultPromptStore = class {
10870
11068
  };
10871
11069
 
10872
11070
  // src/storage/prompt-usage-store.ts
10873
- import * as fs15 from "node:fs/promises";
10874
- import * as path21 from "node:path";
11071
+ import * as fs16 from "node:fs/promises";
11072
+ import * as path22 from "node:path";
10875
11073
  var PromptUsageStore = class {
10876
11074
  constructor(file) {
10877
11075
  this.file = file;
@@ -10879,7 +11077,7 @@ var PromptUsageStore = class {
10879
11077
  file;
10880
11078
  async load() {
10881
11079
  try {
10882
- const raw = JSON.parse(await fs15.readFile(this.file, "utf8"));
11080
+ const raw = JSON.parse(await fs16.readFile(this.file, "utf8"));
10883
11081
  if (raw && typeof raw === "object" && raw.usage && typeof raw.usage === "object") {
10884
11082
  return raw.usage;
10885
11083
  }
@@ -10892,7 +11090,7 @@ var PromptUsageStore = class {
10892
11090
  const prev = usage[slug];
10893
11091
  const next = { count: (prev?.count ?? 0) + 1, lastUsedAt: at };
10894
11092
  usage[slug] = next;
10895
- await ensureDir(path21.dirname(this.file));
11093
+ await ensureDir(path22.dirname(this.file));
10896
11094
  await atomicWrite(this.file, JSON.stringify({ version: 1, usage }, null, 2));
10897
11095
  return next;
10898
11096
  }
@@ -10916,8 +11114,8 @@ var PromptUsageStore = class {
10916
11114
  };
10917
11115
 
10918
11116
  // src/storage/input-history-store.ts
10919
- import * as fs16 from "node:fs/promises";
10920
- import * as path22 from "node:path";
11117
+ import * as fs17 from "node:fs/promises";
11118
+ import * as path23 from "node:path";
10921
11119
  var INPUT_HISTORY_DEFAULT_MAX = 100;
10922
11120
  var InputHistoryStore = class {
10923
11121
  /**
@@ -10939,7 +11137,7 @@ var InputHistoryStore = class {
10939
11137
  */
10940
11138
  async load() {
10941
11139
  try {
10942
- const raw = JSON.parse(await fs16.readFile(this.file, "utf8"));
11140
+ const raw = JSON.parse(await fs17.readFile(this.file, "utf8"));
10943
11141
  if (raw && typeof raw === "object" && Array.isArray(raw.entries) && raw.entries.every((e) => typeof e === "string")) {
10944
11142
  return raw.entries.slice(0, this.maxEntries);
10945
11143
  }
@@ -10956,7 +11154,7 @@ var InputHistoryStore = class {
10956
11154
  */
10957
11155
  async save(entries) {
10958
11156
  const cleaned = this.scrubAndFilter(entries);
10959
- await ensureDir(path22.dirname(this.file));
11157
+ await ensureDir(path23.dirname(this.file));
10960
11158
  const payload = {
10961
11159
  version: 1,
10962
11160
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -10966,7 +11164,7 @@ var InputHistoryStore = class {
10966
11164
  }
10967
11165
  /** Truncate the file to an empty entry list (used by /clear). */
10968
11166
  async clear() {
10969
- await ensureDir(path22.dirname(this.file));
11167
+ await ensureDir(path23.dirname(this.file));
10970
11168
  const payload = { version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: [] };
10971
11169
  await atomicWrite(this.file, JSON.stringify(payload, null, 2));
10972
11170
  }
@@ -10996,8 +11194,8 @@ var InputHistoryStore = class {
10996
11194
  };
10997
11195
 
10998
11196
  // src/storage/cloud-sync.ts
10999
- import * as fs17 from "node:fs/promises";
11000
- import * as path23 from "node:path";
11197
+ import * as fs18 from "node:fs/promises";
11198
+ import * as path24 from "node:path";
11001
11199
  import { createHash as createHash8 } from "node:crypto";
11002
11200
  var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
11003
11201
  var CloudSync = class {
@@ -11005,7 +11203,7 @@ var CloudSync = class {
11005
11203
  this.paths = paths;
11006
11204
  this.getConfig = getConfig;
11007
11205
  this.setConfig = setConfig;
11008
- this.statePath = path23.join(paths.globalRoot, "sync-state.json");
11206
+ this.statePath = path24.join(paths.globalRoot, "sync-state.json");
11009
11207
  }
11010
11208
  paths;
11011
11209
  getConfig;
@@ -11080,7 +11278,7 @@ var CloudSync = class {
11080
11278
  lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString(),
11081
11279
  localRev: rev
11082
11280
  };
11083
- await fs17.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
11281
+ await fs18.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
11084
11282
  this.state = syncState;
11085
11283
  return {
11086
11284
  ok: true,
@@ -11112,8 +11310,8 @@ var CloudSync = class {
11112
11310
  const rel = segments.slice(2).join("/");
11113
11311
  const destPath = resolvePulledCategoryPath(cat, localPath, rel, entry.path);
11114
11312
  const blobData = await this.getBlob(token, owner, repoName, entry.sha);
11115
- await fs17.mkdir(path23.dirname(destPath), { recursive: true });
11116
- await fs17.writeFile(destPath, Buffer.from(blobData, "base64"));
11313
+ await fs18.mkdir(path24.dirname(destPath), { recursive: true });
11314
+ await fs18.writeFile(destPath, Buffer.from(blobData, "base64"));
11117
11315
  }
11118
11316
  const localRev = await this.hashLocalCategories(cfg.categories);
11119
11317
  const syncState = {
@@ -11122,7 +11320,7 @@ var CloudSync = class {
11122
11320
  lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString(),
11123
11321
  localRev
11124
11322
  };
11125
- await fs17.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
11323
+ await fs18.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
11126
11324
  this.state = syncState;
11127
11325
  return {
11128
11326
  ok: true,
@@ -11141,7 +11339,7 @@ var CloudSync = class {
11141
11339
  }
11142
11340
  async loadState() {
11143
11341
  try {
11144
- const raw = await fs17.readFile(this.statePath, "utf8");
11342
+ const raw = await fs18.readFile(this.statePath, "utf8");
11145
11343
  this.state = JSON.parse(raw);
11146
11344
  } catch {
11147
11345
  this.state = null;
@@ -11219,17 +11417,17 @@ var CloudSync = class {
11219
11417
  const localPath = this.categoryToPath(cat);
11220
11418
  if (!localPath) continue;
11221
11419
  try {
11222
- const stat13 = await fs17.stat(localPath);
11420
+ const stat13 = await fs18.stat(localPath);
11223
11421
  if (stat13.isDirectory()) {
11224
11422
  const files = await this.walkDir(localPath, localPath);
11225
11423
  for (const file of files) {
11226
- const content = await fs17.readFile(file, "utf8");
11227
- const rel = path23.relative(localPath, file).replace(/\\/g, "/");
11424
+ const content = await fs18.readFile(file, "utf8");
11425
+ const rel = path24.relative(localPath, file).replace(/\\/g, "/");
11228
11426
  entries.push({ path: `data/${cat}/${rel}`, content, mode: "100644" });
11229
11427
  hashes.push(content);
11230
11428
  }
11231
11429
  } else {
11232
- const content = await fs17.readFile(localPath, "utf8");
11430
+ const content = await fs18.readFile(localPath, "utf8");
11233
11431
  entries.push({ path: `data/${cat}`, content, mode: "100644" });
11234
11432
  hashes.push(content);
11235
11433
  }
@@ -11245,15 +11443,15 @@ var CloudSync = class {
11245
11443
  const localPath = this.categoryToPath(cat);
11246
11444
  if (!localPath) continue;
11247
11445
  try {
11248
- const stat13 = await fs17.stat(localPath);
11446
+ const stat13 = await fs18.stat(localPath);
11249
11447
  if (stat13.isDirectory()) {
11250
11448
  const files = await this.walkDir(localPath, localPath);
11251
11449
  for (const file of files) {
11252
- const content = await fs17.readFile(file);
11450
+ const content = await fs18.readFile(file);
11253
11451
  hashes.push(content.toString("base64") + file);
11254
11452
  }
11255
11453
  } else {
11256
- const content = await fs17.readFile(localPath);
11454
+ const content = await fs18.readFile(localPath);
11257
11455
  hashes.push(content.toString("base64") + localPath);
11258
11456
  }
11259
11457
  } catch {
@@ -11280,9 +11478,9 @@ var CloudSync = class {
11280
11478
  }
11281
11479
  async walkDir(dir, base) {
11282
11480
  const results = [];
11283
- const entries = await fs17.readdir(dir, { withFileTypes: true });
11481
+ const entries = await fs18.readdir(dir, { withFileTypes: true });
11284
11482
  for (const entry of entries) {
11285
- const full = path23.join(dir, entry.name);
11483
+ const full = path24.join(dir, entry.name);
11286
11484
  if (entry.isDirectory()) {
11287
11485
  results.push(...await this.walkDir(full, base));
11288
11486
  } else {
@@ -11304,9 +11502,9 @@ function resolvePulledCategoryPath(cat, localPath, rel, remotePath) {
11304
11502
  return localPath;
11305
11503
  }
11306
11504
  if (!rel) return localPath;
11307
- const normalizedRel = path23.normalize(rel);
11308
- const traversesUp = normalizedRel === ".." || normalizedRel.startsWith(`..${path23.sep}`);
11309
- if (path23.isAbsolute(normalizedRel) || traversesUp) {
11505
+ const normalizedRel = path24.normalize(rel);
11506
+ const traversesUp = normalizedRel === ".." || normalizedRel.startsWith(`..${path24.sep}`);
11507
+ if (path24.isAbsolute(normalizedRel) || traversesUp) {
11310
11508
  throw new FsError({
11311
11509
  message: `Refusing CloudSync path traversal: ${remotePath}`,
11312
11510
  code: ERROR_CODES.FS_DELETE_FAILED,
@@ -11314,10 +11512,10 @@ function resolvePulledCategoryPath(cat, localPath, rel, remotePath) {
11314
11512
  context: { reason: "path_traversal", normalizedRel }
11315
11513
  });
11316
11514
  }
11317
- const dest = path23.resolve(localPath, normalizedRel);
11318
- const root = path23.resolve(localPath);
11319
- const relative6 = path23.relative(root, dest);
11320
- if (relative6.startsWith("..") || path23.isAbsolute(relative6)) {
11515
+ const dest = path24.resolve(localPath, normalizedRel);
11516
+ const root = path24.resolve(localPath);
11517
+ const relative7 = path24.relative(root, dest);
11518
+ if (relative7.startsWith("..") || path24.isAbsolute(relative7)) {
11321
11519
  throw new FsError({
11322
11520
  message: `Refusing CloudSync path outside category root: ${remotePath}`,
11323
11521
  code: ERROR_CODES.FS_DELETE_FAILED,
@@ -11546,6 +11744,7 @@ export {
11546
11744
  parseEntries,
11547
11745
  parseProgressFromText,
11548
11746
  promptChecksum,
11747
+ readProviderSnapshot,
11549
11748
  recomputeGoalProgress,
11550
11749
  recordProgress,
11551
11750
  removePlanItem,