@runfusion/fusion 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/bin.js +2254 -1349
  2. package/dist/client/assets/AgentDetailView-ZbHEbYRT.js +18 -0
  3. package/dist/client/assets/{AgentsView-BkB9FiMT.js → AgentsView-B3jYk8Kt.js} +3 -3
  4. package/dist/client/assets/ChatView-DhPkiEGs.js +1 -0
  5. package/dist/client/assets/{DevServerView-BkvtjZBa.js → DevServerView-DyGDEiBP.js} +1 -1
  6. package/dist/client/assets/{DirectoryPicker-BK-KbnhP.js → DirectoryPicker-D5UIeIl6.js} +1 -1
  7. package/dist/client/assets/{DocumentsView-BEg1CQAk.js → DocumentsView-DNHu1T8K.js} +1 -1
  8. package/dist/client/assets/{EvalsView-Berf9bQm.js → EvalsView-CpRobtDi.js} +1 -1
  9. package/dist/client/assets/{ExperimentalAgentOnboardingModal-jcInE50G.js → ExperimentalAgentOnboardingModal-DOY_oZi7.js} +1 -1
  10. package/dist/client/assets/{InsightsView-BX5bSF1J.js → InsightsView-vp0RE8Mg.js} +1 -1
  11. package/dist/client/assets/MemoryView-PSc5lGJt.js +2 -0
  12. package/dist/client/assets/MemoryView-zaXewZzi.css +1 -0
  13. package/dist/client/assets/{NodesView-DLUOBLf6.js → NodesView-DMj6HGeC.js} +1 -1
  14. package/dist/client/assets/{PiExtensionsManager-COlJf0Kx.js → PiExtensionsManager-DL_QcN56.js} +2 -2
  15. package/dist/client/assets/PluginManager-BtYKm8IT.js +1 -0
  16. package/dist/client/assets/{ResearchView-B256Lr8I.js → ResearchView-BhWqfdV0.js} +1 -1
  17. package/dist/client/assets/{SettingsModal-BeA_nQtW.js → SettingsModal-BAgB4_AR.js} +4 -4
  18. package/dist/client/assets/{SettingsModal-yRqM4DV8.js → SettingsModal-CUCyaAyE.js} +1 -1
  19. package/dist/client/assets/{SetupWizardModal-uUZk3TKT.js → SetupWizardModal-BKscasuh.js} +1 -1
  20. package/dist/client/assets/{SkillsView-CP8JX0P_.js → SkillsView-BdELqTy7.js} +1 -1
  21. package/dist/client/assets/{TodoView-DCRIkDZ-.js → TodoView-DFNGBDNV.js} +1 -1
  22. package/dist/client/assets/{folder-open-DHjELt8-.js → folder-open-k1xmUMyr.js} +1 -1
  23. package/dist/client/assets/index-Qq2JOOWx.css +1 -0
  24. package/dist/client/assets/{index-CQyVRLOb.js → index-TFYXEVpn.js} +160 -160
  25. package/dist/client/assets/{star-DYesq1AV.js → star-ne32r3Y4.js} +1 -1
  26. package/dist/client/assets/{upload-DTWF3Db5.js → upload-MS-2Gx53.js} +1 -1
  27. package/dist/client/assets/{users--syrel4l.js → users-C519GSjH.js} +1 -1
  28. package/dist/client/index.html +2 -2
  29. package/dist/client/version.json +1 -1
  30. package/dist/droid-cli/package.json +1 -1
  31. package/dist/extension.js +1370 -629
  32. package/dist/pi-claude-cli/package.json +1 -1
  33. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +9 -11
  34. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  35. package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +30 -0
  36. package/dist/plugins/fusion-plugin-dependency-graph/package.json +3 -28
  37. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +899 -895
  38. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  39. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +68 -71
  40. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  41. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +47 -50
  42. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  43. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +155 -109
  44. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  45. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  46. package/dist/plugins/fusion-plugin-reports/src/index.ts +49 -3
  47. package/dist/plugins/fusion-plugin-reports/src/report-schema.ts +38 -0
  48. package/dist/plugins/fusion-plugin-reports/src/store/__tests__/report-schema.test.ts +66 -0
  49. package/dist/plugins/fusion-plugin-reports/src/store/__tests__/report-store.test.ts +177 -0
  50. package/dist/plugins/fusion-plugin-reports/src/store/report-store.ts +341 -0
  51. package/dist/plugins/fusion-plugin-reports/src/store/report-types.ts +77 -0
  52. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  53. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  54. package/package.json +1 -1
  55. package/dist/client/assets/AgentDetailView-gy_5SUj2.js +0 -18
  56. package/dist/client/assets/ChatView-B_-B8fqu.js +0 -1
  57. package/dist/client/assets/MemoryView-CKElJY_3.js +0 -2
  58. package/dist/client/assets/MemoryView-DiajLXby.css +0 -1
  59. package/dist/client/assets/PluginManager-CfW55BF4.js +0 -1
  60. package/dist/client/assets/createLucideIcon-BazL2hk5.js +0 -21
  61. package/dist/client/assets/dashboard-view-BkTMSZYn.css +0 -1
  62. package/dist/client/assets/dashboard-view-CyWN-d02.js +0 -63
  63. package/dist/client/assets/dashboard-view-DdGlfuu-.css +0 -1
  64. package/dist/client/assets/dashboard-view-lR7YYmSC.js +0 -21
  65. package/dist/client/assets/index-CxA2Nn0_.css +0 -1
  66. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.css +0 -58
  67. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.tsx +0 -301
  68. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphHighlight.css +0 -27
  69. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.css +0 -157
  70. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.tsx +0 -126
  71. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.css +0 -35
  72. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.tsx +0 -36
  73. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.highlighting.test.tsx +0 -112
  74. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.persistence.test.tsx +0 -115
  75. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.test.tsx +0 -128
  76. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.drag.test.tsx +0 -82
  77. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.test.tsx +0 -307
  78. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphToolbar.test.tsx +0 -60
  79. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/edges.test.tsx +0 -75
  80. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filtering.test.tsx +0 -62
  81. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filters.test.ts +0 -78
  82. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/graphPositionStorage.test.ts +0 -95
  83. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/host-integration.test.ts +0 -74
  84. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/index.test.ts +0 -58
  85. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/interactions.test.tsx +0 -121
  86. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/layout.test.ts +0 -70
  87. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/persistence.test.tsx +0 -89
  88. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphData.test.ts +0 -86
  89. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphInteraction.test.ts +0 -167
  90. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphPositions.test.ts +0 -66
  91. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useNodeDrag.test.ts +0 -81
  92. package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-interop.d.ts +0 -35
  93. package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-view.tsx +0 -19
  94. package/dist/plugins/fusion-plugin-dependency-graph/src/edges.tsx +0 -70
  95. package/dist/plugins/fusion-plugin-dependency-graph/src/filters.ts +0 -8
  96. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/__tests__/useDependencyChain.test.ts +0 -53
  97. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useDependencyChain.ts +0 -60
  98. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useGraphPositions.ts +0 -45
  99. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useNodeDrag.ts +0 -114
  100. package/dist/plugins/fusion-plugin-dependency-graph/src/index.ts +0 -24
  101. package/dist/plugins/fusion-plugin-dependency-graph/src/layout.ts +0 -91
  102. package/dist/plugins/fusion-plugin-dependency-graph/src/styles/drag.css +0 -15
  103. package/dist/plugins/fusion-plugin-dependency-graph/src/types.ts +0 -21
  104. package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphData.ts +0 -17
  105. package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphInteraction.ts +0 -292
  106. package/dist/plugins/fusion-plugin-dependency-graph/src/utils/graphPositionStorage.ts +0 -65
package/dist/extension.js CHANGED
@@ -68,6 +68,8 @@ var init_settings_schema = __esm({
68
68
  "awaiting-approval",
69
69
  "awaiting-user-review",
70
70
  "planning-awaiting-input",
71
+ "message:agent-to-user",
72
+ "message:agent-to-agent",
71
73
  "gridlock",
72
74
  "fallback-used",
73
75
  "memory-dreams-processed"
@@ -274,6 +276,11 @@ var init_settings_schema = __esm({
274
276
  autoBackupSchedule: "0 2 * * *",
275
277
  autoBackupRetention: 7,
276
278
  autoBackupDir: ".fusion/backups",
279
+ memoryBackupEnabled: false,
280
+ memoryBackupSchedule: "0 3 * * *",
281
+ memoryBackupRetention: 14,
282
+ memoryBackupDir: ".fusion/backups/memory",
283
+ memoryBackupScope: "all",
277
284
  autoSummarizeTitles: false,
278
285
  useAiMergeCommitSummary: true,
279
286
  titleSummarizerProvider: void 0,
@@ -4236,6 +4243,7 @@ This means a caller passed a .fusion directory where a project root was expected
4236
4243
  `INSERT OR IGNORE INTO __meta (key, value) VALUES ('lastModified', '${Date.now()}')`
4237
4244
  );
4238
4245
  this.migrate();
4246
+ this.ensureTasksSchemaCompatibility();
4239
4247
  this.ensureRoutinesSchemaCompatibility();
4240
4248
  this.ensureInsightRunsSchemaCompatibility();
4241
4249
  this.ensureEvalTaskResultsSchemaCompatibility();
@@ -4255,6 +4263,27 @@ This means a caller passed a .fusion directory where a project root was expected
4255
4263
  * Column additions use `hasColumn()` so they are idempotent — safe to
4256
4264
  * re-run even if a previous migration partially applied.
4257
4265
  */
4266
+ /**
4267
+ * Applies idempotent compatibility fixes for legacy tasks checkout lease columns.
4268
+ *
4269
+ * FN-3879 documented a self-heal for missing checkout lease columns, but the
4270
+ * original column adds lived only in the `version < 20` migration block. Some
4271
+ * legacy/mesh-synced databases report `schemaVersion >= 20` despite never
4272
+ * receiving those columns, so task listing queries can fail with `no such
4273
+ * column: checkoutNodeId`. Running this unconditionally on init guarantees the
4274
+ * canonical lease columns exist.
4275
+ */
4276
+ ensureTasksSchemaCompatibility() {
4277
+ if (!this.hasTable("tasks")) {
4278
+ return;
4279
+ }
4280
+ this.addColumnIfMissing("tasks", "checkedOutBy", "TEXT");
4281
+ this.addColumnIfMissing("tasks", "checkedOutAt", "TEXT");
4282
+ this.addColumnIfMissing("tasks", "checkoutNodeId", "TEXT");
4283
+ this.addColumnIfMissing("tasks", "checkoutRunId", "TEXT");
4284
+ this.addColumnIfMissing("tasks", "checkoutLeaseRenewedAt", "TEXT");
4285
+ this.addColumnIfMissing("tasks", "checkoutLeaseEpoch", "INTEGER DEFAULT 0");
4286
+ }
4258
4287
  /**
4259
4288
  * Applies idempotent compatibility fixes for legacy routines table shapes.
4260
4289
  *
@@ -7301,17 +7330,17 @@ var init_agent_store = __esm({
7301
7330
  * @returns Matching agent when unambiguous; otherwise null
7302
7331
  */
7303
7332
  async resolveAgent(shortname) {
7304
- const normalize4 = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7333
+ const normalize5 = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7305
7334
  const all = await this.listAgents();
7306
7335
  const exact = all.find((agent) => agent.id === shortname);
7307
7336
  if (exact) {
7308
7337
  return exact;
7309
7338
  }
7310
- const normalizedTarget = normalize4(shortname);
7339
+ const normalizedTarget = normalize5(shortname);
7311
7340
  if (!normalizedTarget) {
7312
7341
  return null;
7313
7342
  }
7314
- const matches = all.filter((agent) => normalize4(agent.name) === normalizedTarget);
7343
+ const matches = all.filter((agent) => normalize5(agent.name) === normalizedTarget);
7315
7344
  return matches.length === 1 ? matches[0] : null;
7316
7345
  }
7317
7346
  // ─────────────────────────────────────────────────────────────────────────
@@ -8928,9 +8957,9 @@ var init_global_settings = __esm({
8928
8957
  * Serialize operations via promise chain to prevent lost-update races.
8929
8958
  */
8930
8959
  withLock(fn) {
8931
- let resolve23;
8960
+ let resolve24;
8932
8961
  const next = new Promise((r) => {
8933
- resolve23 = r;
8962
+ resolve24 = r;
8934
8963
  });
8935
8964
  const prev = this.lock;
8936
8965
  this.lock = next;
@@ -8938,7 +8967,7 @@ var init_global_settings = __esm({
8938
8967
  try {
8939
8968
  return await fn();
8940
8969
  } finally {
8941
- resolve23();
8970
+ resolve24();
8942
8971
  }
8943
8972
  });
8944
8973
  }
@@ -31410,8 +31439,8 @@ var require_CronFileParser = __commonJS({
31410
31439
  * @throws If file cannot be read
31411
31440
  */
31412
31441
  static async parseFile(filePath) {
31413
- const { readFile: readFile22 } = await Promise.resolve().then(() => __importStar(__require("fs/promises")));
31414
- const data = await readFile22(filePath, "utf8");
31442
+ const { readFile: readFile23 } = await Promise.resolve().then(() => __importStar(__require("fs/promises")));
31443
+ const data = await readFile23(filePath, "utf8");
31415
31444
  return _CronFileParser.#parseContent(data);
31416
31445
  }
31417
31446
  /**
@@ -31641,9 +31670,9 @@ var init_automation_store = __esm({
31641
31670
  */
31642
31671
  withScheduleLock(id, fn) {
31643
31672
  const prev = this.scheduleLocks.get(id) ?? Promise.resolve();
31644
- let resolve23;
31673
+ let resolve24;
31645
31674
  const next = new Promise((r) => {
31646
- resolve23 = r;
31675
+ resolve24 = r;
31647
31676
  });
31648
31677
  this.scheduleLocks.set(id, next);
31649
31678
  return prev.then(async () => {
@@ -31653,7 +31682,7 @@ var init_automation_store = __esm({
31653
31682
  if (this.scheduleLocks.get(id) === next) {
31654
31683
  this.scheduleLocks.delete(id);
31655
31684
  }
31656
- resolve23();
31685
+ resolve24();
31657
31686
  }
31658
31687
  });
31659
31688
  }
@@ -32362,8 +32391,8 @@ async function writeProjectMemoryFile(rootDir, path2, content) {
32362
32391
  await mkdir6(dirname5(absPath), { recursive: true });
32363
32392
  const tmpPath = `${absPath}.tmp`;
32364
32393
  await writeFile5(tmpPath, content, "utf-8");
32365
- const { rename: rename6 } = await import("node:fs/promises");
32366
- await rename6(tmpPath, absPath);
32394
+ const { rename: rename7 } = await import("node:fs/promises");
32395
+ await rename7(tmpPath, absPath);
32367
32396
  return { success: true, backend: "file" };
32368
32397
  }
32369
32398
  async function listAgentMemoryFiles(rootDir, agentId, date = /* @__PURE__ */ new Date()) {
@@ -32481,8 +32510,8 @@ async function writeAgentMemoryFile(rootDir, agentId, path2, content) {
32481
32510
  await mkdir6(dirname5(absPath), { recursive: true });
32482
32511
  const tmpPath = `${absPath}.tmp`;
32483
32512
  await writeFile5(tmpPath, content, "utf-8");
32484
- const { rename: rename6 } = await import("node:fs/promises");
32485
- await rename6(tmpPath, absPath);
32513
+ const { rename: rename7 } = await import("node:fs/promises");
32514
+ await rename7(tmpPath, absPath);
32486
32515
  return { success: true };
32487
32516
  }
32488
32517
  function isPathTraversal(path2) {
@@ -32984,8 +33013,8 @@ var init_memory_backend = __esm({
32984
33013
  }
32985
33014
  const tmpPath = filePath + ".tmp";
32986
33015
  await writeFile5(tmpPath, content, "utf-8");
32987
- const { rename: rename6 } = await import("node:fs/promises");
32988
- await rename6(tmpPath, filePath);
33016
+ const { rename: rename7 } = await import("node:fs/promises");
33017
+ await rename7(tmpPath, filePath);
32989
33018
  return {
32990
33019
  success: true,
32991
33020
  backend: this.type
@@ -33432,7 +33461,7 @@ var init_project_memory = __esm({
33432
33461
  // ../core/src/run-command.ts
33433
33462
  import { spawn } from "node:child_process";
33434
33463
  function runCommandAsync(command, options = {}) {
33435
- return new Promise((resolve23) => {
33464
+ return new Promise((resolve24) => {
33436
33465
  const maxBuffer = options.maxBuffer ?? DEFAULT_MAX_BUFFER;
33437
33466
  let stdout = "";
33438
33467
  let stderr = "";
@@ -33491,7 +33520,7 @@ function runCommandAsync(command, options = {}) {
33491
33520
  clearTimeout(forceKillTimer);
33492
33521
  forceKillTimer = null;
33493
33522
  }
33494
- resolve23({
33523
+ resolve24({
33495
33524
  stdout,
33496
33525
  stderr,
33497
33526
  exitCode: null,
@@ -33509,7 +33538,7 @@ function runCommandAsync(command, options = {}) {
33509
33538
  }
33510
33539
  signalProcessGroup("SIGTERM");
33511
33540
  scheduleForceKill(NORMAL_CLEANUP_FORCE_KILL_DELAY_MS);
33512
- resolve23({
33541
+ resolve24({
33513
33542
  stdout,
33514
33543
  stderr,
33515
33544
  exitCode: code,
@@ -34078,15 +34107,15 @@ function createDistributedTaskIdAllocator(db) {
34078
34107
  let opLock = Promise.resolve();
34079
34108
  const withLock = async (fn) => {
34080
34109
  const prev = opLock;
34081
- let resolve23;
34110
+ let resolve24;
34082
34111
  opLock = new Promise((r) => {
34083
- resolve23 = r;
34112
+ resolve24 = r;
34084
34113
  });
34085
34114
  await prev;
34086
34115
  try {
34087
34116
  return await fn();
34088
34117
  } finally {
34089
- resolve23();
34118
+ resolve24();
34090
34119
  }
34091
34120
  };
34092
34121
  const expireReservations = (nowIso3) => {
@@ -34361,8 +34390,8 @@ function assertSafeGitBranchName(name) {
34361
34390
  }
34362
34391
  }
34363
34392
  function assertSafeAbsolutePath(path2) {
34364
- const isAbsolute14 = path2.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path2);
34365
- if (!path2 || path2.length > 4096 || !isAbsolute14 || path2.startsWith("-") || // Reject shell metacharacters, quotes, control chars, and NULs.
34393
+ const isAbsolute15 = path2.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path2);
34394
+ if (!path2 || path2.length > 4096 || !isAbsolute15 || path2.startsWith("-") || // Reject shell metacharacters, quotes, control chars, and NULs.
34366
34395
  /["'`$\n\r\t;&|<>()*?[\]{}\\\0]/.test(
34367
34396
  path2.replace(/^[A-Za-z]:/, "")
34368
34397
  // ignore the drive-letter colon on Windows
@@ -35623,9 +35652,9 @@ ${outcome}`;
35623
35652
  * lost-update races on the nextId counter.
35624
35653
  */
35625
35654
  withConfigLock(fn) {
35626
- let resolve23;
35655
+ let resolve24;
35627
35656
  const next = new Promise((r) => {
35628
- resolve23 = r;
35657
+ resolve24 = r;
35629
35658
  });
35630
35659
  const prev = this.configLock;
35631
35660
  this.configLock = next;
@@ -35633,7 +35662,7 @@ ${outcome}`;
35633
35662
  try {
35634
35663
  return await fn();
35635
35664
  } finally {
35636
- resolve23();
35665
+ resolve24();
35637
35666
  }
35638
35667
  });
35639
35668
  }
@@ -35642,9 +35671,9 @@ ${outcome}`;
35642
35671
  * per task ID. Concurrent callers for the same ID will queue behind each other.
35643
35672
  */
35644
35673
  withWorktreeAllocationLock(fn) {
35645
- let resolve23;
35674
+ let resolve24;
35646
35675
  const next = new Promise((r) => {
35647
- resolve23 = r;
35676
+ resolve24 = r;
35648
35677
  });
35649
35678
  const prev = this.worktreeAllocationLock;
35650
35679
  this.worktreeAllocationLock = next;
@@ -35652,15 +35681,15 @@ ${outcome}`;
35652
35681
  try {
35653
35682
  return await fn();
35654
35683
  } finally {
35655
- resolve23();
35684
+ resolve24();
35656
35685
  }
35657
35686
  });
35658
35687
  }
35659
35688
  withTaskLock(id, fn) {
35660
35689
  const prev = this.taskLocks.get(id) ?? Promise.resolve();
35661
- let resolve23;
35690
+ let resolve24;
35662
35691
  const next = new Promise((r) => {
35663
- resolve23 = r;
35692
+ resolve24 = r;
35664
35693
  });
35665
35694
  this.taskLocks.set(id, next);
35666
35695
  return prev.then(async () => {
@@ -35670,7 +35699,7 @@ ${outcome}`;
35670
35699
  if (this.taskLocks.get(id) === next) {
35671
35700
  this.taskLocks.delete(id);
35672
35701
  }
35673
- resolve23();
35702
+ resolve24();
35674
35703
  }
35675
35704
  });
35676
35705
  }
@@ -37638,8 +37667,8 @@ ${newTask.description}
37638
37667
  if (this.isWatching) this.taskCache.delete(id);
37639
37668
  const dir = this.taskDir(id);
37640
37669
  if (existsSync13(dir)) {
37641
- const { rm: rm4 } = await import("node:fs/promises");
37642
- await rm4(dir, { recursive: true });
37670
+ const { rm: rm5 } = await import("node:fs/promises");
37671
+ await rm5(dir, { recursive: true });
37643
37672
  }
37644
37673
  for (const dependentTask of rewrittenDependents) {
37645
37674
  this.emit("task:updated", dependentTask);
@@ -37999,8 +38028,8 @@ ${newTask.description}
37999
38028
  this.clearLinkedAgentTaskIds(id, task.updatedAt);
38000
38029
  this.db.prepare("DELETE FROM tasks WHERE id = ?").run(id);
38001
38030
  this.db.bumpLastModified();
38002
- const { rm: rm4 } = await import("node:fs/promises");
38003
- await rm4(dir, { recursive: true, force: true });
38031
+ const { rm: rm5 } = await import("node:fs/promises");
38032
+ await rm5(dir, { recursive: true, force: true });
38004
38033
  if (this.isWatching) {
38005
38034
  this.taskCache.delete(id);
38006
38035
  }
@@ -38163,7 +38192,7 @@ ${newTask.description}
38163
38192
  }
38164
38193
  }
38165
38194
  }
38166
- await new Promise((resolve23) => setImmediate(resolve23));
38195
+ await new Promise((resolve24) => setImmediate(resolve24));
38167
38196
  const selectClause = this.getTaskSelectClause(true);
38168
38197
  const changedRows = this.lastPollTime ? this.db.prepare(`SELECT ${selectClause} FROM tasks WHERE updatedAt > ? OR columnMovedAt > ?`).all(this.lastPollTime, this.lastPollTime) : this.db.prepare(`SELECT ${selectClause} FROM tasks`).all();
38169
38198
  this.lastPollTime = (/* @__PURE__ */ new Date()).toISOString();
@@ -38183,7 +38212,7 @@ ${newTask.description}
38183
38212
  this.emit("task:updated", task);
38184
38213
  }
38185
38214
  if (i > 0 && i % 50 === 0) {
38186
- await new Promise((resolve23) => setImmediate(resolve23));
38215
+ await new Promise((resolve24) => setImmediate(resolve24));
38187
38216
  }
38188
38217
  }
38189
38218
  const elapsed = Date.now() - startTime;
@@ -39138,14 +39167,14 @@ ${newTask.description}
39138
39167
  if (rows.length === 0) {
39139
39168
  return;
39140
39169
  }
39141
- const { rm: rm4 } = await import("node:fs/promises");
39170
+ const { rm: rm5 } = await import("node:fs/promises");
39142
39171
  for (const row of rows) {
39143
39172
  const task = this.rowToTask(row);
39144
39173
  const archivedAt = task.columnMovedAt ?? task.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
39145
39174
  const entry = await this.taskToArchiveEntry(task, archivedAt);
39146
39175
  this.archiveDb.upsert(entry);
39147
39176
  this.db.prepare("DELETE FROM tasks WHERE id = ?").run(task.id);
39148
- await rm4(this.taskDir(task.id), { recursive: true, force: true });
39177
+ await rm5(this.taskDir(task.id), { recursive: true, force: true });
39149
39178
  if (this.isWatching) {
39150
39179
  this.taskCache.delete(task.id);
39151
39180
  }
@@ -39168,8 +39197,8 @@ ${newTask.description}
39168
39197
  this.archiveDb.upsert(entry);
39169
39198
  this.db.prepare("DELETE FROM tasks WHERE id = ?").run(task.id);
39170
39199
  this.db.bumpLastModified();
39171
- const { rm: rm4 } = await import("node:fs/promises");
39172
- await rm4(dir, { recursive: true, force: true });
39200
+ const { rm: rm5 } = await import("node:fs/promises");
39201
+ await rm5(dir, { recursive: true, force: true });
39173
39202
  if (this.isWatching) {
39174
39203
  this.taskCache.delete(task.id);
39175
39204
  }
@@ -40334,7 +40363,7 @@ function runGh(args, cwd) {
40334
40363
  }
40335
40364
  function runGhAsync(args, cwdOrOptions) {
40336
40365
  const { cwd, signal: externalSignal, timeoutMs = DEFAULT_GH_TIMEOUT_MS } = normalizeRunGhOptions(cwdOrOptions);
40337
- return new Promise((resolve23, reject) => {
40366
+ return new Promise((resolve24, reject) => {
40338
40367
  if (externalSignal?.aborted) {
40339
40368
  reject(makeGhError(`gh command aborted: ${describeAbortReason(externalSignal.reason)}`, "ABORT_ERR"));
40340
40369
  return;
@@ -40385,7 +40414,7 @@ function runGhAsync(args, cwdOrOptions) {
40385
40414
  ghError.stderr = stderr ?? "";
40386
40415
  reject(ghError);
40387
40416
  } else {
40388
- resolve23(stdout ?? "");
40417
+ resolve24(stdout ?? "");
40389
40418
  }
40390
40419
  }
40391
40420
  );
@@ -40486,7 +40515,7 @@ import { existsSync as existsSync15, readFileSync as readFileSync4, realpathSync
40486
40515
  import { platform as platform2, tmpdir as tmpdir2 } from "node:os";
40487
40516
  import { posix, win32 as win322 } from "node:path";
40488
40517
  function runProbe(command, args, timeoutMs) {
40489
- return new Promise((resolve23) => {
40518
+ return new Promise((resolve24) => {
40490
40519
  let stdout = "";
40491
40520
  let stderr = "";
40492
40521
  const child = spawn2(command, args, {
@@ -40508,11 +40537,11 @@ function runProbe(command, args, timeoutMs) {
40508
40537
  });
40509
40538
  child.on("error", (err) => {
40510
40539
  clearTimeout(timer);
40511
- resolve23({ exitCode: null, stdout, stderr: stderr || err.message });
40540
+ resolve24({ exitCode: null, stdout, stderr: stderr || err.message });
40512
40541
  });
40513
40542
  child.on("close", (exitCode) => {
40514
40543
  clearTimeout(timer);
40515
- resolve23({ exitCode, stdout, stderr });
40544
+ resolve24({ exitCode, stdout, stderr });
40516
40545
  });
40517
40546
  });
40518
40547
  }
@@ -40819,9 +40848,9 @@ var init_routine_store = __esm({
40819
40848
  */
40820
40849
  withRoutineLock(id, fn) {
40821
40850
  const prev = this.routineLocks.get(id) ?? Promise.resolve();
40822
- let resolve23;
40851
+ let resolve24;
40823
40852
  const next = new Promise((r) => {
40824
- resolve23 = r;
40853
+ resolve24 = r;
40825
40854
  });
40826
40855
  this.routineLocks.set(id, next);
40827
40856
  return prev.then(async () => {
@@ -40831,7 +40860,7 @@ var init_routine_store = __esm({
40831
40860
  if (this.routineLocks.get(id) === next) {
40832
40861
  this.routineLocks.delete(id);
40833
40862
  }
40834
- resolve23();
40863
+ resolve24();
40835
40864
  }
40836
40865
  });
40837
40866
  }
@@ -41567,13 +41596,13 @@ var init_plugin_loader = __esm({
41567
41596
  * Execute a promise with a timeout.
41568
41597
  */
41569
41598
  withTimeout(promise, ms, timeoutMessage) {
41570
- return new Promise((resolve23, reject) => {
41599
+ return new Promise((resolve24, reject) => {
41571
41600
  const timer = setTimeout(() => {
41572
41601
  reject(new Error(timeoutMessage));
41573
41602
  }, ms);
41574
41603
  promise.then((result) => {
41575
41604
  clearTimeout(timer);
41576
- resolve23(result);
41605
+ resolve24(result);
41577
41606
  }).catch((err) => {
41578
41607
  clearTimeout(timer);
41579
41608
  reject(err);
@@ -42363,8 +42392,329 @@ var init_backup = __esm({
42363
42392
  }
42364
42393
  });
42365
42394
 
42395
+ // ../core/src/memory-backup.ts
42396
+ import { cp as cp2, mkdir as mkdir9, readdir as readdir7, readFile as readFile10, rename as rename5, rm as rm2, stat as stat4, writeFile as writeFile7 } from "node:fs/promises";
42397
+ import { existsSync as existsSync17 } from "node:fs";
42398
+ import { dirname as dirname7, join as join20, relative as relative3, resolve as resolve10, sep as sep5 } from "node:path";
42399
+ function createMemoryBackupManager(fusionDir, settings) {
42400
+ return new MemoryBackupManager(fusionDir, {
42401
+ backupDir: sanitizeMemoryBackupDir(fusionDir, canonicalizeMemoryBackupDir(settings?.memoryBackupDir)),
42402
+ retention: settings?.memoryBackupRetention,
42403
+ scope: settings?.memoryBackupScope
42404
+ });
42405
+ }
42406
+ async function runMemoryBackupCommand(fusionDir, settings) {
42407
+ if (settings.memoryBackupSchedule && !validateMemoryBackupSchedule(settings.memoryBackupSchedule)) {
42408
+ return { success: false, output: `Invalid memory backup schedule: ${settings.memoryBackupSchedule}` };
42409
+ }
42410
+ const manager = createMemoryBackupManager(fusionDir, settings);
42411
+ try {
42412
+ const backup = await manager.createBackup();
42413
+ const deletedCount = await manager.cleanupOldBackups();
42414
+ const output = deletedCount > 0 ? `Memory backup created: ${backup.filename} (${formatBytes2(backup.size)}). Removed ${deletedCount} old backup(s).` : `Memory backup created: ${backup.filename} (${formatBytes2(backup.size)})`;
42415
+ return { success: true, output, backupPath: backup.path, deletedCount };
42416
+ } catch (error) {
42417
+ return { success: false, output: `Memory backup failed: ${error.message}` };
42418
+ }
42419
+ }
42420
+ async function syncMemoryBackupAutomation(automationStore, settings) {
42421
+ const { AutomationStore: AutomationStore2 } = await Promise.resolve().then(() => (init_automation_store(), automation_store_exports));
42422
+ const schedules = await automationStore.listSchedules();
42423
+ const existing = schedules.find((s) => s.name === MEMORY_BACKUP_SCHEDULE_NAME);
42424
+ if (!settings.memoryBackupEnabled) {
42425
+ if (existing) await automationStore.deleteSchedule(existing.id);
42426
+ return void 0;
42427
+ }
42428
+ const schedule = settings.memoryBackupSchedule || "0 3 * * *";
42429
+ if (!AutomationStore2.isValidCron(schedule)) {
42430
+ throw new Error(`Invalid backup schedule: ${schedule}`);
42431
+ }
42432
+ const command = "fn memory-backup --create";
42433
+ if (existing) {
42434
+ return await automationStore.updateSchedule(existing.id, {
42435
+ scheduleType: "custom",
42436
+ cronExpression: schedule,
42437
+ command,
42438
+ enabled: true
42439
+ });
42440
+ }
42441
+ return await automationStore.createSchedule({
42442
+ name: MEMORY_BACKUP_SCHEDULE_NAME,
42443
+ description: "Automatic memory backup based on project settings",
42444
+ scheduleType: "custom",
42445
+ cronExpression: schedule,
42446
+ command,
42447
+ enabled: true
42448
+ });
42449
+ }
42450
+ async function syncMemoryBackupRoutine(routineStore, settings) {
42451
+ const { RoutineStore: RoutineStore2 } = await Promise.resolve().then(() => (init_routine_store(), routine_store_exports));
42452
+ const routines = await routineStore.listRoutines();
42453
+ const existing = routines.find((routine) => routine.name === MEMORY_BACKUP_SCHEDULE_NAME);
42454
+ if (!settings.memoryBackupEnabled) {
42455
+ if (existing) {
42456
+ await routineStore.deleteRoutine(existing.id);
42457
+ }
42458
+ return void 0;
42459
+ }
42460
+ const schedule = settings.memoryBackupSchedule || "0 3 * * *";
42461
+ if (!RoutineStore2.isValidCron(schedule)) {
42462
+ throw new Error(`Invalid backup schedule: ${schedule}`);
42463
+ }
42464
+ const command = "fn memory-backup --create";
42465
+ if (existing) {
42466
+ return await routineStore.updateRoutine(existing.id, {
42467
+ trigger: { type: "cron", cronExpression: schedule },
42468
+ command,
42469
+ enabled: true
42470
+ });
42471
+ }
42472
+ return await routineStore.createRoutine({
42473
+ name: MEMORY_BACKUP_SCHEDULE_NAME,
42474
+ description: "Automatic memory backup based on project settings",
42475
+ agentId: "",
42476
+ trigger: { type: "cron", cronExpression: schedule },
42477
+ command,
42478
+ enabled: true,
42479
+ scope: "project"
42480
+ });
42481
+ }
42482
+ function canonicalizeMemoryBackupDir(dir) {
42483
+ if (dir === ".kb/backups/memory") return ".fusion/backups/memory";
42484
+ return dir;
42485
+ }
42486
+ function sanitizeMemoryBackupDir(fusionDir, dir) {
42487
+ if (!dir) return void 0;
42488
+ if (dir.startsWith("/") || dir.startsWith("\\") || /^[a-zA-Z]:/.test(dir) || dir.includes("..")) {
42489
+ throw new Error(`Invalid memory backup directory: ${dir}`);
42490
+ }
42491
+ const projectRoot = resolve10(fusionDir, "..");
42492
+ const resolved = resolve10(projectRoot, dir);
42493
+ const rel = relative3(projectRoot, resolved);
42494
+ if (rel.startsWith("..") || rel.includes(`..${sep5}`)) {
42495
+ throw new Error(`Invalid memory backup directory: ${dir}`);
42496
+ }
42497
+ return rel || ".";
42498
+ }
42499
+ function formatTimestamp2(date) {
42500
+ const year = date.getUTCFullYear();
42501
+ const month = String(date.getUTCMonth() + 1).padStart(2, "0");
42502
+ const day = String(date.getUTCDate()).padStart(2, "0");
42503
+ const hours = String(date.getUTCHours()).padStart(2, "0");
42504
+ const minutes = String(date.getUTCMinutes()).padStart(2, "0");
42505
+ const seconds = String(date.getUTCSeconds()).padStart(2, "0");
42506
+ return `${year}-${month}-${day}-${hours}${minutes}${seconds}`;
42507
+ }
42508
+ function formatBytes2(bytes) {
42509
+ if (bytes === 0) return "0 B";
42510
+ const k = 1024;
42511
+ const sizes = ["B", "KB", "MB", "GB"];
42512
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
42513
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
42514
+ }
42515
+ async function getDirectoryStats(dir) {
42516
+ const entries = await readdir7(dir, { withFileTypes: true });
42517
+ let size = 0;
42518
+ let entryCount = 0;
42519
+ for (const entry of entries) {
42520
+ const fullPath = join20(dir, entry.name);
42521
+ if (entry.isDirectory()) {
42522
+ const nested = await getDirectoryStats(fullPath);
42523
+ size += nested.size;
42524
+ entryCount += nested.entryCount;
42525
+ continue;
42526
+ }
42527
+ if (entry.isFile()) {
42528
+ const st = await stat4(fullPath);
42529
+ size += st.size;
42530
+ entryCount++;
42531
+ }
42532
+ }
42533
+ return { size, entryCount };
42534
+ }
42535
+ async function listFilesRecursive(root) {
42536
+ const entries = await readdir7(root, { withFileTypes: true });
42537
+ const files = [];
42538
+ for (const entry of entries) {
42539
+ const fullPath = join20(root, entry.name);
42540
+ if (entry.isDirectory()) {
42541
+ files.push(...await listFilesRecursive(fullPath));
42542
+ } else if (entry.isFile()) {
42543
+ files.push(fullPath);
42544
+ }
42545
+ }
42546
+ return files;
42547
+ }
42548
+ var MEMORY_BACKUP_SCHEDULE_NAME, AGENT_MEMORY_ROOT2, MemoryBackupManager, validateMemoryBackupSchedule;
42549
+ var init_memory_backup = __esm({
42550
+ "../core/src/memory-backup.ts"() {
42551
+ "use strict";
42552
+ init_memory_backend();
42553
+ init_backup();
42554
+ MEMORY_BACKUP_SCHEDULE_NAME = "Memory Backup";
42555
+ AGENT_MEMORY_ROOT2 = ".fusion/agent-memory";
42556
+ MemoryBackupManager = class {
42557
+ fusionDir;
42558
+ backupDir;
42559
+ retention;
42560
+ scope;
42561
+ constructor(fusionDir, options) {
42562
+ this.fusionDir = fusionDir;
42563
+ this.backupDir = options?.backupDir ?? ".fusion/backups/memory";
42564
+ this.retention = options?.retention ?? 14;
42565
+ this.scope = options?.scope ?? "all";
42566
+ }
42567
+ getProjectRoot() {
42568
+ return resolve10(this.fusionDir, "..");
42569
+ }
42570
+ getBackupDirPath() {
42571
+ return resolve10(this.getProjectRoot(), this.backupDir);
42572
+ }
42573
+ async collectSources() {
42574
+ const root = this.getProjectRoot();
42575
+ const projectPath = join20(root, MEMORY_WORKSPACE_PATH);
42576
+ const agentsPath = join20(root, AGENT_MEMORY_ROOT2);
42577
+ const includeProject = this.scope !== "agents";
42578
+ const includeAgents = this.scope !== "project";
42579
+ const sources = [];
42580
+ if (includeProject && existsSync17(projectPath)) {
42581
+ sources.push({ source: projectPath, target: "project" });
42582
+ }
42583
+ if (includeAgents && existsSync17(agentsPath)) {
42584
+ sources.push({ source: agentsPath, target: "agents" });
42585
+ }
42586
+ if (sources.length === 0) {
42587
+ throw new Error(`No memory sources found for scope '${this.scope}' (.fusion/memory or .fusion/agent-memory)`);
42588
+ }
42589
+ return sources;
42590
+ }
42591
+ async createBackup() {
42592
+ const backupDirPath = this.getBackupDirPath();
42593
+ await mkdir9(backupDirPath, { recursive: true });
42594
+ const sources = await this.collectSources();
42595
+ const baseName = `memory-${formatTimestamp2(/* @__PURE__ */ new Date())}`;
42596
+ let filename = baseName;
42597
+ let finalPath = join20(backupDirPath, filename);
42598
+ let tmpPath = `${finalPath}.tmp`;
42599
+ let counter = 1;
42600
+ while (existsSync17(finalPath) || existsSync17(tmpPath)) {
42601
+ filename = `${baseName}-${counter}`;
42602
+ finalPath = join20(backupDirPath, filename);
42603
+ tmpPath = `${finalPath}.tmp`;
42604
+ counter++;
42605
+ }
42606
+ try {
42607
+ await mkdir9(tmpPath, { recursive: true });
42608
+ for (const { source, target } of sources) {
42609
+ const targetPath = join20(tmpPath, target);
42610
+ await cp2(source, targetPath, { recursive: true, preserveTimestamps: true });
42611
+ }
42612
+ await rename5(tmpPath, finalPath);
42613
+ } catch (error) {
42614
+ await rm2(tmpPath, { recursive: true, force: true });
42615
+ throw error;
42616
+ }
42617
+ return this.buildInfo(filename, finalPath);
42618
+ }
42619
+ async listBackups() {
42620
+ const backupDirPath = this.getBackupDirPath();
42621
+ try {
42622
+ const entries = await readdir7(backupDirPath);
42623
+ const backups = [];
42624
+ for (const entry of entries) {
42625
+ if (!entry.match(/^memory-\d{4}-\d{2}-\d{2}-\d{6}(?:-\d+)?$/)) continue;
42626
+ const fullPath = join20(backupDirPath, entry);
42627
+ const st = await stat4(fullPath);
42628
+ if (!st.isDirectory()) continue;
42629
+ backups.push(await this.buildInfo(entry, fullPath));
42630
+ }
42631
+ return backups.sort((a, b) => {
42632
+ const timeCompare = b.createdAt.localeCompare(a.createdAt);
42633
+ if (timeCompare !== 0) return timeCompare;
42634
+ return b.filename.localeCompare(a.filename);
42635
+ });
42636
+ } catch {
42637
+ return [];
42638
+ }
42639
+ }
42640
+ async cleanupOldBackups() {
42641
+ const backups = await this.listBackups();
42642
+ if (backups.length <= this.retention) return 0;
42643
+ const sorted = [...backups].sort((a, b) => {
42644
+ const timeCompare = a.createdAt.localeCompare(b.createdAt);
42645
+ if (timeCompare !== 0) return timeCompare;
42646
+ return a.filename.localeCompare(b.filename);
42647
+ });
42648
+ const toDelete = sorted.slice(0, sorted.length - this.retention);
42649
+ let deleted = 0;
42650
+ for (const backup of toDelete) {
42651
+ try {
42652
+ await rm2(backup.path, { recursive: true, force: true });
42653
+ deleted++;
42654
+ } catch {
42655
+ }
42656
+ }
42657
+ return deleted;
42658
+ }
42659
+ async restoreBackup(filename, opts) {
42660
+ const backupRoot = join20(this.getBackupDirPath(), filename);
42661
+ const sourceProject = join20(backupRoot, "project");
42662
+ const sourceAgents = join20(backupRoot, "agents");
42663
+ const hasProject = existsSync17(sourceProject);
42664
+ const hasAgents = existsSync17(sourceAgents);
42665
+ if (!hasProject && !hasAgents) {
42666
+ throw new Error(`Memory backup not found or empty: ${filename}`);
42667
+ }
42668
+ if (hasProject) {
42669
+ await this.restoreTree(sourceProject, join20(this.getProjectRoot(), MEMORY_WORKSPACE_PATH), opts?.overwrite === true);
42670
+ }
42671
+ if (hasAgents) {
42672
+ await this.restoreTree(sourceAgents, join20(this.getProjectRoot(), AGENT_MEMORY_ROOT2), opts?.overwrite === true);
42673
+ }
42674
+ }
42675
+ async restoreTree(sourceDir, destinationDir, overwrite) {
42676
+ const files = await listFilesRecursive(sourceDir);
42677
+ for (const file of files) {
42678
+ const rel = relative3(sourceDir, file);
42679
+ const destFile = join20(destinationDir, rel);
42680
+ await mkdir9(dirname7(destFile), { recursive: true });
42681
+ if (existsSync17(destFile) && !overwrite) {
42682
+ const [srcBuf, dstBuf] = await Promise.all([readFile10(file), readFile10(destFile)]);
42683
+ if (!srcBuf.equals(dstBuf)) {
42684
+ throw new Error(`Restore would overwrite modified memory file: ${destFile}`);
42685
+ }
42686
+ continue;
42687
+ }
42688
+ const tmp = `${destFile}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
42689
+ const content = await readFile10(file);
42690
+ await writeFile7(tmp, content);
42691
+ await rename5(tmp, destFile);
42692
+ }
42693
+ }
42694
+ async buildInfo(filename, fullPath) {
42695
+ const stats = await stat4(fullPath);
42696
+ const sizeAndCount = await getDirectoryStats(fullPath);
42697
+ const match = filename.match(/^memory-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})(?:-\d+)?$/);
42698
+ const createdAt = match ? `${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}:${match[6]}Z` : stats.mtime.toISOString();
42699
+ const hasProject = existsSync17(join20(fullPath, "project"));
42700
+ const hasAgents = existsSync17(join20(fullPath, "agents"));
42701
+ const scope = hasProject && hasAgents ? "all" : hasProject ? "project" : "agents";
42702
+ return {
42703
+ filename,
42704
+ createdAt,
42705
+ size: sizeAndCount.size,
42706
+ path: fullPath,
42707
+ scope,
42708
+ entryCount: sizeAndCount.entryCount
42709
+ };
42710
+ }
42711
+ };
42712
+ validateMemoryBackupSchedule = validateBackupSchedule;
42713
+ }
42714
+ });
42715
+
42366
42716
  // ../core/src/settings-export.ts
42367
- import { writeFile as writeFile7, readFile as readFile10, rename as rename5 } from "node:fs/promises";
42717
+ import { writeFile as writeFile8, readFile as readFile11, rename as rename6 } from "node:fs/promises";
42368
42718
  function validateImportData(data) {
42369
42719
  const errors = [];
42370
42720
  if (data === null || typeof data !== "object") {
@@ -42486,7 +42836,7 @@ async function importSettings(store, data, options = {}) {
42486
42836
  }
42487
42837
  }
42488
42838
  async function readExportFile(filePath) {
42489
- const content = await readFile10(filePath, "utf-8");
42839
+ const content = await readFile11(filePath, "utf-8");
42490
42840
  try {
42491
42841
  const parsed = JSON.parse(content);
42492
42842
  return parsed;
@@ -42496,8 +42846,8 @@ async function readExportFile(filePath) {
42496
42846
  }
42497
42847
  async function writeExportFile(filePath, data) {
42498
42848
  const tmpPath = filePath + ".tmp";
42499
- await writeFile7(tmpPath, JSON.stringify(data, null, 2));
42500
- await rename5(tmpPath, filePath);
42849
+ await writeFile8(tmpPath, JSON.stringify(data, null, 2));
42850
+ await rename6(tmpPath, filePath);
42501
42851
  }
42502
42852
  var init_settings_export = __esm({
42503
42853
  "../core/src/settings-export.ts"() {
@@ -42756,7 +43106,7 @@ var init_mission_types = __esm({
42756
43106
  // ../core/src/docker-client.ts
42757
43107
  import Docker from "dockerode";
42758
43108
  import { exec } from "node:child_process";
42759
- import { readFile as readFile11 } from "node:fs/promises";
43109
+ import { readFile as readFile12 } from "node:fs/promises";
42760
43110
  import { promisify as promisify2 } from "node:util";
42761
43111
  function isLocalDaemonHost(host) {
42762
43112
  return !host || host.trim() === "" || host === "unix:///var/run/docker.sock";
@@ -42797,9 +43147,9 @@ var init_docker_client = __esm({
42797
43147
  const options = {
42798
43148
  host: hostConfig.host
42799
43149
  };
42800
- if (hostConfig.tlsCaPath) options.ca = await readFile11(hostConfig.tlsCaPath);
42801
- if (hostConfig.tlsCertPath) options.cert = await readFile11(hostConfig.tlsCertPath);
42802
- if (hostConfig.tlsKeyPath) options.key = await readFile11(hostConfig.tlsKeyPath);
43150
+ if (hostConfig.tlsCaPath) options.ca = await readFile12(hostConfig.tlsCaPath);
43151
+ if (hostConfig.tlsCertPath) options.cert = await readFile12(hostConfig.tlsCertPath);
43152
+ if (hostConfig.tlsKeyPath) options.key = await readFile12(hostConfig.tlsKeyPath);
42803
43153
  if (hostConfig.tlsVerify === false) options.rejectUnauthorized = false;
42804
43154
  return new Docker(options);
42805
43155
  }
@@ -43063,7 +43413,7 @@ var init_mesh_config_generator = __esm({
43063
43413
  hostConfig
43064
43414
  }
43065
43415
  );
43066
- await new Promise((resolve23) => setTimeout(resolve23, POST_RECREATE_DELAY_MS));
43416
+ await new Promise((resolve24) => setTimeout(resolve24, POST_RECREATE_DELAY_MS));
43067
43417
  await this.deps.central.updateManagedDockerNode(managedNodeId, {
43068
43418
  apiKey: config.nodeApiKey,
43069
43419
  reachableUrl: config.reachableUrl,
@@ -43171,7 +43521,7 @@ var init_mesh_config_generator = __esm({
43171
43521
  }
43172
43522
  } catch {
43173
43523
  }
43174
- await new Promise((resolve23) => setTimeout(resolve23, intervalMs));
43524
+ await new Promise((resolve24) => setTimeout(resolve24, intervalMs));
43175
43525
  }
43176
43526
  return { healthy: false };
43177
43527
  }
@@ -43215,10 +43565,10 @@ var init_docker_provisioning = __esm({
43215
43565
  }
43216
43566
  } : void 0;
43217
43567
  const stream = await docker.pull(imageRef, authOptions);
43218
- await new Promise((resolve23, reject) => {
43568
+ await new Promise((resolve24, reject) => {
43219
43569
  docker.modem.followProgress(
43220
43570
  stream,
43221
- (err) => err ? reject(err) : resolve23()
43571
+ (err) => err ? reject(err) : resolve24()
43222
43572
  );
43223
43573
  });
43224
43574
  } catch (error) {
@@ -43449,36 +43799,36 @@ var init_docker_provisioning = __esm({
43449
43799
  });
43450
43800
 
43451
43801
  // ../core/src/memory-insights.ts
43452
- import { readFile as readFile12, writeFile as writeFile8, mkdir as mkdir9, unlink as unlink5 } from "node:fs/promises";
43453
- import { existsSync as existsSync17 } from "node:fs";
43454
- import { dirname as dirname7, join as join20 } from "node:path";
43802
+ import { readFile as readFile13, writeFile as writeFile9, mkdir as mkdir10, unlink as unlink5 } from "node:fs/promises";
43803
+ import { existsSync as existsSync18 } from "node:fs";
43804
+ import { dirname as dirname8, join as join21 } from "node:path";
43455
43805
  async function readWorkingMemory(rootDir) {
43456
- const filePath = join20(rootDir, MEMORY_WORKING_PATH);
43457
- if (!existsSync17(filePath)) {
43806
+ const filePath = join21(rootDir, MEMORY_WORKING_PATH);
43807
+ if (!existsSync18(filePath)) {
43458
43808
  return "";
43459
43809
  }
43460
- return readFile12(filePath, "utf-8");
43810
+ return readFile13(filePath, "utf-8");
43461
43811
  }
43462
43812
  async function migrateLegacyArtifactIfNeeded(rootDir, canonicalPath, legacyPath) {
43463
- const canonicalFilePath = join20(rootDir, canonicalPath);
43464
- const legacyFilePath = join20(rootDir, legacyPath);
43465
- if (existsSync17(canonicalFilePath) || !existsSync17(legacyFilePath)) {
43813
+ const canonicalFilePath = join21(rootDir, canonicalPath);
43814
+ const legacyFilePath = join21(rootDir, legacyPath);
43815
+ if (existsSync18(canonicalFilePath) || !existsSync18(legacyFilePath)) {
43466
43816
  return;
43467
43817
  }
43468
- const content = await readFile12(legacyFilePath, "utf-8");
43469
- const canonicalDir = dirname7(canonicalFilePath);
43470
- if (!existsSync17(canonicalDir)) {
43471
- await mkdir9(canonicalDir, { recursive: true });
43818
+ const content = await readFile13(legacyFilePath, "utf-8");
43819
+ const canonicalDir = dirname8(canonicalFilePath);
43820
+ if (!existsSync18(canonicalDir)) {
43821
+ await mkdir10(canonicalDir, { recursive: true });
43472
43822
  }
43473
- await writeFile8(canonicalFilePath, content, "utf-8");
43823
+ await writeFile9(canonicalFilePath, content, "utf-8");
43474
43824
  try {
43475
43825
  await unlink5(legacyFilePath);
43476
43826
  } catch {
43477
43827
  }
43478
43828
  }
43479
43829
  async function removeLegacyArtifactIfPresent(rootDir, legacyPath) {
43480
- const legacyFilePath = join20(rootDir, legacyPath);
43481
- if (!existsSync17(legacyFilePath)) {
43830
+ const legacyFilePath = join21(rootDir, legacyPath);
43831
+ if (!existsSync18(legacyFilePath)) {
43482
43832
  return;
43483
43833
  }
43484
43834
  try {
@@ -43488,54 +43838,54 @@ async function removeLegacyArtifactIfPresent(rootDir, legacyPath) {
43488
43838
  }
43489
43839
  async function readInsightsMemory(rootDir) {
43490
43840
  await migrateLegacyArtifactIfNeeded(rootDir, MEMORY_INSIGHTS_PATH, LEGACY_MEMORY_INSIGHTS_PATH);
43491
- const filePath = join20(rootDir, MEMORY_INSIGHTS_PATH);
43492
- if (!existsSync17(filePath)) {
43841
+ const filePath = join21(rootDir, MEMORY_INSIGHTS_PATH);
43842
+ if (!existsSync18(filePath)) {
43493
43843
  return null;
43494
43844
  }
43495
- return readFile12(filePath, "utf-8");
43845
+ return readFile13(filePath, "utf-8");
43496
43846
  }
43497
43847
  async function writeInsightsMemory(rootDir, content) {
43498
- const filePath = join20(rootDir, MEMORY_INSIGHTS_PATH);
43499
- const dir = dirname7(filePath);
43500
- if (!existsSync17(dir)) {
43501
- await mkdir9(dir, { recursive: true });
43848
+ const filePath = join21(rootDir, MEMORY_INSIGHTS_PATH);
43849
+ const dir = dirname8(filePath);
43850
+ if (!existsSync18(dir)) {
43851
+ await mkdir10(dir, { recursive: true });
43502
43852
  }
43503
- await writeFile8(filePath, content, "utf-8");
43853
+ await writeFile9(filePath, content, "utf-8");
43504
43854
  await removeLegacyArtifactIfPresent(rootDir, LEGACY_MEMORY_INSIGHTS_PATH);
43505
43855
  }
43506
43856
  async function writeWorkingMemory(rootDir, content) {
43507
- const filePath = join20(rootDir, MEMORY_WORKING_PATH);
43508
- const dir = dirname7(filePath);
43509
- if (!existsSync17(dir)) {
43510
- await mkdir9(dir, { recursive: true });
43857
+ const filePath = join21(rootDir, MEMORY_WORKING_PATH);
43858
+ const dir = dirname8(filePath);
43859
+ if (!existsSync18(dir)) {
43860
+ await mkdir10(dir, { recursive: true });
43511
43861
  }
43512
- await writeFile8(filePath, content, "utf-8");
43862
+ await writeFile9(filePath, content, "utf-8");
43513
43863
  }
43514
43864
  async function readMemoryAudit(rootDir) {
43515
43865
  await migrateLegacyArtifactIfNeeded(rootDir, MEMORY_AUDIT_PATH, LEGACY_MEMORY_AUDIT_PATH);
43516
- const filePath = join20(rootDir, MEMORY_AUDIT_PATH);
43517
- if (!existsSync17(filePath)) {
43866
+ const filePath = join21(rootDir, MEMORY_AUDIT_PATH);
43867
+ if (!existsSync18(filePath)) {
43518
43868
  return null;
43519
43869
  }
43520
- return readFile12(filePath, "utf-8");
43870
+ return readFile13(filePath, "utf-8");
43521
43871
  }
43522
43872
  async function writeMemoryAudit(rootDir, content) {
43523
- const filePath = join20(rootDir, MEMORY_AUDIT_PATH);
43524
- const dir = dirname7(filePath);
43525
- if (!existsSync17(dir)) {
43526
- await mkdir9(dir, { recursive: true });
43873
+ const filePath = join21(rootDir, MEMORY_AUDIT_PATH);
43874
+ const dir = dirname8(filePath);
43875
+ if (!existsSync18(dir)) {
43876
+ await mkdir10(dir, { recursive: true });
43527
43877
  }
43528
- await writeFile8(filePath, content, "utf-8");
43878
+ await writeFile9(filePath, content, "utf-8");
43529
43879
  await removeLegacyArtifactIfPresent(rootDir, LEGACY_MEMORY_AUDIT_PATH);
43530
43880
  }
43531
43881
  async function readMemoryAuditState(rootDir) {
43532
43882
  await migrateLegacyArtifactIfNeeded(rootDir, MEMORY_AUDIT_STATE_PATH, LEGACY_MEMORY_AUDIT_STATE_PATH);
43533
- const filePath = join20(rootDir, MEMORY_AUDIT_STATE_PATH);
43534
- if (!existsSync17(filePath)) {
43883
+ const filePath = join21(rootDir, MEMORY_AUDIT_STATE_PATH);
43884
+ if (!existsSync18(filePath)) {
43535
43885
  return null;
43536
43886
  }
43537
43887
  try {
43538
- const raw = await readFile12(filePath, "utf-8");
43888
+ const raw = await readFile13(filePath, "utf-8");
43539
43889
  const parsed = JSON.parse(raw);
43540
43890
  const extraction = isValidExtractionMetadata(parsed.extraction) ? parsed.extraction : void 0;
43541
43891
  const pruning = isValidPruneOutcome(parsed.pruning) ? parsed.pruning : void 0;
@@ -43549,12 +43899,12 @@ async function readMemoryAuditState(rootDir) {
43549
43899
  }
43550
43900
  }
43551
43901
  async function writeMemoryAuditState(rootDir, state) {
43552
- const filePath = join20(rootDir, MEMORY_AUDIT_STATE_PATH);
43553
- const dir = dirname7(filePath);
43554
- if (!existsSync17(dir)) {
43555
- await mkdir9(dir, { recursive: true });
43902
+ const filePath = join21(rootDir, MEMORY_AUDIT_STATE_PATH);
43903
+ const dir = dirname8(filePath);
43904
+ if (!existsSync18(dir)) {
43905
+ await mkdir10(dir, { recursive: true });
43556
43906
  }
43557
- await writeFile8(filePath, JSON.stringify(state, null, 2), "utf-8");
43907
+ await writeFile9(filePath, JSON.stringify(state, null, 2), "utf-8");
43558
43908
  await removeLegacyArtifactIfPresent(rootDir, LEGACY_MEMORY_AUDIT_STATE_PATH);
43559
43909
  }
43560
43910
  function isValidExtractionMetadata(value) {
@@ -44028,14 +44378,14 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
44028
44378
  const persistedState = lastExtraction === void 0 || pruningOutcome === void 0 ? await readMemoryAuditState(rootDir) : null;
44029
44379
  const effectiveExtraction = lastExtraction ?? persistedState?.extraction;
44030
44380
  const effectivePruning = pruningOutcome ?? persistedState?.pruning;
44031
- const workingMemoryPath = join20(rootDir, MEMORY_WORKING_PATH);
44032
- const workingMemoryExists = existsSync17(workingMemoryPath);
44381
+ const workingMemoryPath = join21(rootDir, MEMORY_WORKING_PATH);
44382
+ const workingMemoryExists = existsSync18(workingMemoryPath);
44033
44383
  let workingMemorySize = 0;
44034
44384
  let workingMemorySectionCount = 0;
44035
44385
  let workingMemoryContent = "";
44036
44386
  if (workingMemoryExists) {
44037
44387
  try {
44038
- workingMemoryContent = await readFile12(workingMemoryPath, "utf-8");
44388
+ workingMemoryContent = await readFile13(workingMemoryPath, "utf-8");
44039
44389
  workingMemorySize = workingMemoryContent.length;
44040
44390
  workingMemorySectionCount = countMarkdownSections(workingMemoryContent);
44041
44391
  checks.push({
@@ -44083,8 +44433,8 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
44083
44433
  details: foundSections.length >= 2 ? `Found sections: ${foundSections.join(", ")}` : `Missing sections: ${requiredSections.filter((s) => !foundSections.includes(s)).join(", ")}`
44084
44434
  });
44085
44435
  }
44086
- const insightsMemoryPath = join20(rootDir, MEMORY_INSIGHTS_PATH);
44087
- const insightsMemoryExists = existsSync17(insightsMemoryPath);
44436
+ const insightsMemoryPath = join21(rootDir, MEMORY_INSIGHTS_PATH);
44437
+ const insightsMemoryExists = existsSync18(insightsMemoryPath);
44088
44438
  let insightsMemorySize = 0;
44089
44439
  let insightsMemoryContent = "";
44090
44440
  const categoryCounts = {
@@ -44097,7 +44447,7 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
44097
44447
  let lastUpdated;
44098
44448
  if (insightsMemoryExists) {
44099
44449
  try {
44100
- insightsMemoryContent = await readFile12(insightsMemoryPath, "utf-8");
44450
+ insightsMemoryContent = await readFile13(insightsMemoryPath, "utf-8");
44101
44451
  insightsMemorySize = insightsMemoryContent.length;
44102
44452
  for (const [category, header] of Object.entries({
44103
44453
  pattern: "## Patterns",
@@ -46232,7 +46582,7 @@ var require_get_stream = __commonJS({
46232
46582
  };
46233
46583
  const { maxBuffer } = options;
46234
46584
  let stream;
46235
- await new Promise((resolve23, reject) => {
46585
+ await new Promise((resolve24, reject) => {
46236
46586
  const rejectPromise = (error) => {
46237
46587
  if (error && stream.getBufferedLength() <= BufferConstants.MAX_LENGTH) {
46238
46588
  error.bufferedData = stream.getBufferedValue();
@@ -46244,7 +46594,7 @@ var require_get_stream = __commonJS({
46244
46594
  rejectPromise(error);
46245
46595
  return;
46246
46596
  }
46247
- resolve23();
46597
+ resolve24();
46248
46598
  });
46249
46599
  stream.on("data", () => {
46250
46600
  if (stream.getBufferedLength() > maxBuffer) {
@@ -47538,7 +47888,7 @@ var require_extract_zip = __commonJS({
47538
47888
  debug("opening", this.zipPath, "with opts", this.opts);
47539
47889
  this.zipfile = await openZip(this.zipPath, { lazyEntries: true });
47540
47890
  this.canceled = false;
47541
- return new Promise((resolve23, reject) => {
47891
+ return new Promise((resolve24, reject) => {
47542
47892
  this.zipfile.on("error", (err) => {
47543
47893
  this.canceled = true;
47544
47894
  reject(err);
@@ -47547,7 +47897,7 @@ var require_extract_zip = __commonJS({
47547
47897
  this.zipfile.on("close", () => {
47548
47898
  if (!this.canceled) {
47549
47899
  debug("zip extraction complete");
47550
- resolve23();
47900
+ resolve24();
47551
47901
  }
47552
47902
  });
47553
47903
  this.zipfile.on("entry", async (entry) => {
@@ -51609,10 +51959,10 @@ var require_resolve_block_map = __commonJS({
51609
51959
  let offset = bm.offset;
51610
51960
  let commentEnd = null;
51611
51961
  for (const collItem of bm.items) {
51612
- const { start, key, sep: sep6, value } = collItem;
51962
+ const { start, key, sep: sep7, value } = collItem;
51613
51963
  const keyProps = resolveProps.resolveProps(start, {
51614
51964
  indicator: "explicit-key-ind",
51615
- next: key ?? sep6?.[0],
51965
+ next: key ?? sep7?.[0],
51616
51966
  offset,
51617
51967
  onError,
51618
51968
  parentIndent: bm.indent,
@@ -51626,7 +51976,7 @@ var require_resolve_block_map = __commonJS({
51626
51976
  else if ("indent" in key && key.indent !== bm.indent)
51627
51977
  onError(offset, "BAD_INDENT", startColMsg);
51628
51978
  }
51629
- if (!keyProps.anchor && !keyProps.tag && !sep6) {
51979
+ if (!keyProps.anchor && !keyProps.tag && !sep7) {
51630
51980
  commentEnd = keyProps.end;
51631
51981
  if (keyProps.comment) {
51632
51982
  if (map.comment)
@@ -51650,7 +52000,7 @@ var require_resolve_block_map = __commonJS({
51650
52000
  ctx.atKey = false;
51651
52001
  if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
51652
52002
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
51653
- const valueProps = resolveProps.resolveProps(sep6 ?? [], {
52003
+ const valueProps = resolveProps.resolveProps(sep7 ?? [], {
51654
52004
  indicator: "map-value-ind",
51655
52005
  next: value,
51656
52006
  offset: keyNode.range[2],
@@ -51666,7 +52016,7 @@ var require_resolve_block_map = __commonJS({
51666
52016
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
51667
52017
  onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
51668
52018
  }
51669
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep6, null, valueProps, onError);
52019
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep7, null, valueProps, onError);
51670
52020
  if (ctx.schema.compat)
51671
52021
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
51672
52022
  offset = valueNode.range[2];
@@ -51757,7 +52107,7 @@ var require_resolve_end = __commonJS({
51757
52107
  let comment = "";
51758
52108
  if (end) {
51759
52109
  let hasSpace = false;
51760
- let sep6 = "";
52110
+ let sep7 = "";
51761
52111
  for (const token of end) {
51762
52112
  const { source, type } = token;
51763
52113
  switch (type) {
@@ -51771,13 +52121,13 @@ var require_resolve_end = __commonJS({
51771
52121
  if (!comment)
51772
52122
  comment = cb;
51773
52123
  else
51774
- comment += sep6 + cb;
51775
- sep6 = "";
52124
+ comment += sep7 + cb;
52125
+ sep7 = "";
51776
52126
  break;
51777
52127
  }
51778
52128
  case "newline":
51779
52129
  if (comment)
51780
- sep6 += source;
52130
+ sep7 += source;
51781
52131
  hasSpace = true;
51782
52132
  break;
51783
52133
  default:
@@ -51820,18 +52170,18 @@ var require_resolve_flow_collection = __commonJS({
51820
52170
  let offset = fc.offset + fc.start.source.length;
51821
52171
  for (let i = 0; i < fc.items.length; ++i) {
51822
52172
  const collItem = fc.items[i];
51823
- const { start, key, sep: sep6, value } = collItem;
52173
+ const { start, key, sep: sep7, value } = collItem;
51824
52174
  const props = resolveProps.resolveProps(start, {
51825
52175
  flow: fcName,
51826
52176
  indicator: "explicit-key-ind",
51827
- next: key ?? sep6?.[0],
52177
+ next: key ?? sep7?.[0],
51828
52178
  offset,
51829
52179
  onError,
51830
52180
  parentIndent: fc.indent,
51831
52181
  startOnNewline: false
51832
52182
  });
51833
52183
  if (!props.found) {
51834
- if (!props.anchor && !props.tag && !sep6 && !value) {
52184
+ if (!props.anchor && !props.tag && !sep7 && !value) {
51835
52185
  if (i === 0 && props.comma)
51836
52186
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
51837
52187
  else if (i < fc.items.length - 1)
@@ -51885,8 +52235,8 @@ var require_resolve_flow_collection = __commonJS({
51885
52235
  }
51886
52236
  }
51887
52237
  }
51888
- if (!isMap && !sep6 && !props.found) {
51889
- const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep6, null, props, onError);
52238
+ if (!isMap && !sep7 && !props.found) {
52239
+ const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep7, null, props, onError);
51890
52240
  coll.items.push(valueNode);
51891
52241
  offset = valueNode.range[2];
51892
52242
  if (isBlock(value))
@@ -51898,7 +52248,7 @@ var require_resolve_flow_collection = __commonJS({
51898
52248
  if (isBlock(key))
51899
52249
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
51900
52250
  ctx.atKey = false;
51901
- const valueProps = resolveProps.resolveProps(sep6 ?? [], {
52251
+ const valueProps = resolveProps.resolveProps(sep7 ?? [], {
51902
52252
  flow: fcName,
51903
52253
  indicator: "map-value-ind",
51904
52254
  next: value,
@@ -51909,8 +52259,8 @@ var require_resolve_flow_collection = __commonJS({
51909
52259
  });
51910
52260
  if (valueProps.found) {
51911
52261
  if (!isMap && !props.found && ctx.options.strict) {
51912
- if (sep6)
51913
- for (const st of sep6) {
52262
+ if (sep7)
52263
+ for (const st of sep7) {
51914
52264
  if (st === valueProps.found)
51915
52265
  break;
51916
52266
  if (st.type === "newline") {
@@ -51927,7 +52277,7 @@ var require_resolve_flow_collection = __commonJS({
51927
52277
  else
51928
52278
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
51929
52279
  }
51930
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep6, null, valueProps, onError) : null;
52280
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep7, null, valueProps, onError) : null;
51931
52281
  if (valueNode) {
51932
52282
  if (isBlock(value))
51933
52283
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -52107,7 +52457,7 @@ var require_resolve_block_scalar = __commonJS({
52107
52457
  chompStart = i + 1;
52108
52458
  }
52109
52459
  let value = "";
52110
- let sep6 = "";
52460
+ let sep7 = "";
52111
52461
  let prevMoreIndented = false;
52112
52462
  for (let i = 0; i < contentStart; ++i)
52113
52463
  value += lines[i][0].slice(trimIndent) + "\n";
@@ -52124,24 +52474,24 @@ var require_resolve_block_scalar = __commonJS({
52124
52474
  indent = "";
52125
52475
  }
52126
52476
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
52127
- value += sep6 + indent.slice(trimIndent) + content;
52128
- sep6 = "\n";
52477
+ value += sep7 + indent.slice(trimIndent) + content;
52478
+ sep7 = "\n";
52129
52479
  } else if (indent.length > trimIndent || content[0] === " ") {
52130
- if (sep6 === " ")
52131
- sep6 = "\n";
52132
- else if (!prevMoreIndented && sep6 === "\n")
52133
- sep6 = "\n\n";
52134
- value += sep6 + indent.slice(trimIndent) + content;
52135
- sep6 = "\n";
52480
+ if (sep7 === " ")
52481
+ sep7 = "\n";
52482
+ else if (!prevMoreIndented && sep7 === "\n")
52483
+ sep7 = "\n\n";
52484
+ value += sep7 + indent.slice(trimIndent) + content;
52485
+ sep7 = "\n";
52136
52486
  prevMoreIndented = true;
52137
52487
  } else if (content === "") {
52138
- if (sep6 === "\n")
52488
+ if (sep7 === "\n")
52139
52489
  value += "\n";
52140
52490
  else
52141
- sep6 = "\n";
52491
+ sep7 = "\n";
52142
52492
  } else {
52143
- value += sep6 + content;
52144
- sep6 = " ";
52493
+ value += sep7 + content;
52494
+ sep7 = " ";
52145
52495
  prevMoreIndented = false;
52146
52496
  }
52147
52497
  }
@@ -52323,25 +52673,25 @@ var require_resolve_flow_scalar = __commonJS({
52323
52673
  if (!match)
52324
52674
  return source;
52325
52675
  let res = match[1];
52326
- let sep6 = " ";
52676
+ let sep7 = " ";
52327
52677
  let pos = first.lastIndex;
52328
52678
  line.lastIndex = pos;
52329
52679
  while (match = line.exec(source)) {
52330
52680
  if (match[1] === "") {
52331
- if (sep6 === "\n")
52332
- res += sep6;
52681
+ if (sep7 === "\n")
52682
+ res += sep7;
52333
52683
  else
52334
- sep6 = "\n";
52684
+ sep7 = "\n";
52335
52685
  } else {
52336
- res += sep6 + match[1];
52337
- sep6 = " ";
52686
+ res += sep7 + match[1];
52687
+ sep7 = " ";
52338
52688
  }
52339
52689
  pos = line.lastIndex;
52340
52690
  }
52341
52691
  const last = /[ \t]*(.*)/sy;
52342
52692
  last.lastIndex = pos;
52343
52693
  match = last.exec(source);
52344
- return res + sep6 + (match?.[1] ?? "");
52694
+ return res + sep7 + (match?.[1] ?? "");
52345
52695
  }
52346
52696
  function doubleQuotedValue(source, onError) {
52347
52697
  let res = "";
@@ -53148,14 +53498,14 @@ var require_cst_stringify = __commonJS({
53148
53498
  }
53149
53499
  }
53150
53500
  }
53151
- function stringifyItem({ start, key, sep: sep6, value }) {
53501
+ function stringifyItem({ start, key, sep: sep7, value }) {
53152
53502
  let res = "";
53153
53503
  for (const st of start)
53154
53504
  res += st.source;
53155
53505
  if (key)
53156
53506
  res += stringifyToken(key);
53157
- if (sep6)
53158
- for (const st of sep6)
53507
+ if (sep7)
53508
+ for (const st of sep7)
53159
53509
  res += st.source;
53160
53510
  if (value)
53161
53511
  res += stringifyToken(value);
@@ -54305,18 +54655,18 @@ var require_parser = __commonJS({
54305
54655
  if (this.type === "map-value-ind") {
54306
54656
  const prev = getPrevProps(this.peek(2));
54307
54657
  const start = getFirstKeyStartProps(prev);
54308
- let sep6;
54658
+ let sep7;
54309
54659
  if (scalar.end) {
54310
- sep6 = scalar.end;
54311
- sep6.push(this.sourceToken);
54660
+ sep7 = scalar.end;
54661
+ sep7.push(this.sourceToken);
54312
54662
  delete scalar.end;
54313
54663
  } else
54314
- sep6 = [this.sourceToken];
54664
+ sep7 = [this.sourceToken];
54315
54665
  const map = {
54316
54666
  type: "block-map",
54317
54667
  offset: scalar.offset,
54318
54668
  indent: scalar.indent,
54319
- items: [{ start, key: scalar, sep: sep6 }]
54669
+ items: [{ start, key: scalar, sep: sep7 }]
54320
54670
  };
54321
54671
  this.onKeyLine = true;
54322
54672
  this.stack[this.stack.length - 1] = map;
@@ -54469,15 +54819,15 @@ var require_parser = __commonJS({
54469
54819
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
54470
54820
  const start2 = getFirstKeyStartProps(it.start);
54471
54821
  const key = it.key;
54472
- const sep6 = it.sep;
54473
- sep6.push(this.sourceToken);
54822
+ const sep7 = it.sep;
54823
+ sep7.push(this.sourceToken);
54474
54824
  delete it.key;
54475
54825
  delete it.sep;
54476
54826
  this.stack.push({
54477
54827
  type: "block-map",
54478
54828
  offset: this.offset,
54479
54829
  indent: this.indent,
54480
- items: [{ start: start2, key, sep: sep6 }]
54830
+ items: [{ start: start2, key, sep: sep7 }]
54481
54831
  });
54482
54832
  } else if (start.length > 0) {
54483
54833
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -54671,13 +55021,13 @@ var require_parser = __commonJS({
54671
55021
  const prev = getPrevProps(parent);
54672
55022
  const start = getFirstKeyStartProps(prev);
54673
55023
  fixFlowSeqItems(fc);
54674
- const sep6 = fc.end.splice(1, fc.end.length);
54675
- sep6.push(this.sourceToken);
55024
+ const sep7 = fc.end.splice(1, fc.end.length);
55025
+ sep7.push(this.sourceToken);
54676
55026
  const map = {
54677
55027
  type: "block-map",
54678
55028
  offset: fc.offset,
54679
55029
  indent: fc.indent,
54680
- items: [{ start, key: fc, sep: sep6 }]
55030
+ items: [{ start, key: fc, sep: sep7 }]
54681
55031
  };
54682
55032
  this.onKeyLine = true;
54683
55033
  this.stack[this.stack.length - 1] = map;
@@ -54956,9 +55306,9 @@ var require_dist3 = __commonJS({
54956
55306
  });
54957
55307
 
54958
55308
  // ../core/src/agent-companies-parser.ts
54959
- import { existsSync as existsSync18, mkdtempSync, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync, statSync as statSync4 } from "node:fs";
55309
+ import { existsSync as existsSync19, mkdtempSync, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync, statSync as statSync4 } from "node:fs";
54960
55310
  import { tmpdir as tmpdir3 } from "node:os";
54961
- import { join as join21, resolve as resolve10 } from "node:path";
55311
+ import { isAbsolute as isAbsolute7, join as join22, normalize as normalize3, resolve as resolve11 } from "node:path";
54962
55312
  function slugifyAgentReference(value) {
54963
55313
  return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
54964
55314
  }
@@ -55183,15 +55533,15 @@ function parseManifestFile(filePath, parser) {
55183
55533
  }
55184
55534
  }
55185
55535
  function parseManifestSubdirectories(rootDir, sectionDir, filename, parser) {
55186
- const sectionPath = join21(rootDir, sectionDir);
55187
- if (!existsSync18(sectionPath)) {
55536
+ const sectionPath = join22(rootDir, sectionDir);
55537
+ if (!existsSync19(sectionPath)) {
55188
55538
  return [];
55189
55539
  }
55190
55540
  const manifests = [];
55191
55541
  const entries = readdirSync2(sectionPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
55192
55542
  for (const entry of entries) {
55193
- const manifestPath = join21(sectionPath, entry.name, filename);
55194
- if (!existsSync18(manifestPath)) {
55543
+ const manifestPath = join22(sectionPath, entry.name, filename);
55544
+ if (!existsSync19(manifestPath)) {
55195
55545
  continue;
55196
55546
  }
55197
55547
  manifests.push(parseManifestFile(manifestPath, parser));
@@ -55228,18 +55578,18 @@ function walkTeamIncludes(teams) {
55228
55578
  }
55229
55579
  }
55230
55580
  function parseCompanyDirectory(dirPath) {
55231
- const resolvedPath = resolve10(dirPath);
55232
- if (!existsSync18(resolvedPath)) {
55581
+ const resolvedPath = resolve11(dirPath);
55582
+ if (!existsSync19(resolvedPath)) {
55233
55583
  throw new AgentCompaniesParseError(`Company directory does not exist: ${resolvedPath}`);
55234
55584
  }
55235
55585
  if (!statSync4(resolvedPath).isDirectory()) {
55236
55586
  throw new AgentCompaniesParseError(`Company path is not a directory: ${resolvedPath}`);
55237
55587
  }
55238
- const companyPath = join21(resolvedPath, "COMPANY.md");
55588
+ const companyPath = join22(resolvedPath, "COMPANY.md");
55239
55589
  const teams = parseManifestSubdirectories(resolvedPath, "teams", "TEAM.md", parseTeamManifest);
55240
55590
  walkTeamIncludes(teams);
55241
55591
  return {
55242
- company: existsSync18(companyPath) ? parseManifestFile(companyPath, parseCompanyManifest) : void 0,
55592
+ company: existsSync19(companyPath) ? parseManifestFile(companyPath, parseCompanyManifest) : void 0,
55243
55593
  agents: parseManifestSubdirectories(resolvedPath, "agents", "AGENTS.md", parseAgentManifest),
55244
55594
  teams,
55245
55595
  projects: parseManifestSubdirectories(
@@ -55253,20 +55603,20 @@ function parseCompanyDirectory(dirPath) {
55253
55603
  };
55254
55604
  }
55255
55605
  function resolveExtractionRoot(tempDir) {
55256
- if (existsSync18(join21(tempDir, "COMPANY.md"))) {
55606
+ if (existsSync19(join22(tempDir, "COMPANY.md"))) {
55257
55607
  return tempDir;
55258
55608
  }
55259
55609
  const directories = readdirSync2(tempDir, { withFileTypes: true }).filter(
55260
55610
  (entry) => entry.isDirectory()
55261
55611
  );
55262
55612
  for (const directory of directories) {
55263
- const candidate = join21(tempDir, directory.name);
55264
- if (existsSync18(join21(candidate, "COMPANY.md"))) {
55613
+ const candidate = join22(tempDir, directory.name);
55614
+ if (existsSync19(join22(candidate, "COMPANY.md"))) {
55265
55615
  return candidate;
55266
55616
  }
55267
55617
  }
55268
55618
  if (directories.length === 1) {
55269
- return resolveExtractionRoot(join21(tempDir, directories[0].name));
55619
+ return resolveExtractionRoot(join22(tempDir, directories[0].name));
55270
55620
  }
55271
55621
  return tempDir;
55272
55622
  }
@@ -55278,9 +55628,35 @@ async function extractTarArchive(archivePath, outputDir) {
55278
55628
  const execFileAsync6 = promisify14(execFile8);
55279
55629
  await execFileAsync6("tar", ["xzf", archivePath, "-C", outputDir]);
55280
55630
  }
55281
- async function parseCompanyArchive(archivePath) {
55282
- const resolvedArchivePath = resolve10(archivePath);
55283
- const tempDir = mkdtempSync(join21(tmpdir3(), "agent-companies-"));
55631
+ function sanitizeCompanySubPath(subPath) {
55632
+ const trimmed = subPath.trim();
55633
+ if (trimmed.length === 0) {
55634
+ throw new AgentCompaniesParseError("subPath must not be empty");
55635
+ }
55636
+ if (trimmed.includes("\\")) {
55637
+ throw new AgentCompaniesParseError(`Invalid subPath "${subPath}": backslashes are not allowed`);
55638
+ }
55639
+ if (isAbsolute7(trimmed)) {
55640
+ throw new AgentCompaniesParseError(`Invalid subPath "${subPath}": absolute paths are not allowed`);
55641
+ }
55642
+ const normalized = normalize3(trimmed).replace(/^\/+/, "");
55643
+ if (normalized === "" || normalized === "." || normalized.split("/").some((segment) => segment === "..")) {
55644
+ throw new AgentCompaniesParseError(`Invalid subPath "${subPath}": path traversal is not allowed`);
55645
+ }
55646
+ return normalized;
55647
+ }
55648
+ function findArchiveWrapperRoot(tempDir) {
55649
+ const directories = readdirSync2(tempDir, { withFileTypes: true }).filter(
55650
+ (entry) => entry.isDirectory()
55651
+ );
55652
+ if (directories.length === 1) {
55653
+ return join22(tempDir, directories[0].name);
55654
+ }
55655
+ return tempDir;
55656
+ }
55657
+ async function parseCompanyArchive(archivePath, options) {
55658
+ const resolvedArchivePath = resolve11(archivePath);
55659
+ const tempDir = mkdtempSync(join22(tmpdir3(), "agent-companies-"));
55284
55660
  try {
55285
55661
  if (resolvedArchivePath.endsWith(".tar.gz") || resolvedArchivePath.endsWith(".tgz")) {
55286
55662
  await extractTarArchive(resolvedArchivePath, tempDir);
@@ -55291,6 +55667,18 @@ async function parseCompanyArchive(archivePath) {
55291
55667
  "Unsupported archive format. Expected .tar.gz, .tgz, or .zip"
55292
55668
  );
55293
55669
  }
55670
+ if (typeof options?.subPath === "string") {
55671
+ const wrapperRoot = findArchiveWrapperRoot(tempDir);
55672
+ const sanitizedSubPath = sanitizeCompanySubPath(options.subPath);
55673
+ const candidateRoot = join22(wrapperRoot, sanitizedSubPath);
55674
+ const companyManifestPath = join22(candidateRoot, "COMPANY.md");
55675
+ if (!existsSync19(companyManifestPath)) {
55676
+ throw new AgentCompaniesParseError(
55677
+ `Company manifest not found at archive subPath "${sanitizedSubPath}" (expected ${companyManifestPath})`
55678
+ );
55679
+ }
55680
+ return parseCompanyDirectory(candidateRoot);
55681
+ }
55294
55682
  return parseCompanyDirectory(resolveExtractionRoot(tempDir));
55295
55683
  } catch (error) {
55296
55684
  if (error instanceof AgentCompaniesParseError) {
@@ -55469,8 +55857,8 @@ var init_agent_companies_parser = __esm({
55469
55857
  });
55470
55858
 
55471
55859
  // ../core/src/agent-companies-exporter.ts
55472
- import { mkdir as mkdir10, writeFile as writeFile9 } from "node:fs/promises";
55473
- import { resolve as resolve11, join as join22 } from "node:path";
55860
+ import { mkdir as mkdir11, writeFile as writeFile10 } from "node:fs/promises";
55861
+ import { resolve as resolve12, join as join23 } from "node:path";
55474
55862
  function trimToUndefined(value) {
55475
55863
  if (typeof value !== "string") {
55476
55864
  return void 0;
@@ -55578,7 +55966,7 @@ function generateSkillMd(skillName) {
55578
55966
  );
55579
55967
  }
55580
55968
  async function exportAgentsToDirectory(agents, outputDir, options) {
55581
- const resolvedOutputDir = resolve11(outputDir);
55969
+ const resolvedOutputDir = resolve12(outputDir);
55582
55970
  const includeSkills = options?.includeSkills ?? true;
55583
55971
  const result = {
55584
55972
  outputDir: resolvedOutputDir,
@@ -55587,15 +55975,15 @@ async function exportAgentsToDirectory(agents, outputDir, options) {
55587
55975
  filesWritten: [],
55588
55976
  errors: []
55589
55977
  };
55590
- await mkdir10(resolvedOutputDir, { recursive: true });
55591
- await mkdir10(join22(resolvedOutputDir, "agents"), { recursive: true });
55592
- const companyMdPath = join22(resolvedOutputDir, "COMPANY.md");
55978
+ await mkdir11(resolvedOutputDir, { recursive: true });
55979
+ await mkdir11(join23(resolvedOutputDir, "agents"), { recursive: true });
55980
+ const companyMdPath = join23(resolvedOutputDir, "COMPANY.md");
55593
55981
  const companyMd = generateCompanyMd(agents, {
55594
55982
  name: options?.companyName,
55595
55983
  description: options?.companyDescription,
55596
55984
  slug: options?.companySlug
55597
55985
  });
55598
- await writeFile9(companyMdPath, companyMd, "utf-8");
55986
+ await writeFile10(companyMdPath, companyMd, "utf-8");
55599
55987
  result.filesWritten.push(companyMdPath);
55600
55988
  const validAgents = agents.filter((agent) => {
55601
55989
  if (!trimToUndefined(agent.name)) {
@@ -55641,9 +56029,9 @@ async function exportAgentsToDirectory(agents, outputDir, options) {
55641
56029
  skills: skillRefs
55642
56030
  });
55643
56031
  try {
55644
- const agentDir = join22(resolvedOutputDir, "agents", agentSlug);
55645
- const agentMdPath = join22(agentDir, "AGENTS.md");
55646
- await mkdir10(agentDir, { recursive: true });
56032
+ const agentDir = join23(resolvedOutputDir, "agents", agentSlug);
56033
+ const agentMdPath = join23(agentDir, "AGENTS.md");
56034
+ await mkdir11(agentDir, { recursive: true });
55647
56035
  const frontmatter = {
55648
56036
  name: manifest.name,
55649
56037
  title: manifest.title,
@@ -55656,7 +56044,7 @@ async function exportAgentsToDirectory(agents, outputDir, options) {
55656
56044
  memory: manifest.memory
55657
56045
  };
55658
56046
  const content = toFrontmatterMarkdown(frontmatter, manifest.instructionBody ?? "");
55659
- await writeFile9(agentMdPath, content, "utf-8");
56047
+ await writeFile10(agentMdPath, content, "utf-8");
55660
56048
  result.agentsExported += 1;
55661
56049
  result.filesWritten.push(agentMdPath);
55662
56050
  } catch (error) {
@@ -55667,13 +56055,13 @@ async function exportAgentsToDirectory(agents, outputDir, options) {
55667
56055
  }
55668
56056
  }
55669
56057
  if (includeSkills && skillByName.size > 0) {
55670
- const skillsDir = join22(resolvedOutputDir, "skills");
55671
- await mkdir10(skillsDir, { recursive: true });
56058
+ const skillsDir = join23(resolvedOutputDir, "skills");
56059
+ await mkdir11(skillsDir, { recursive: true });
55672
56060
  for (const skill of skillByName.values()) {
55673
- const skillDir = join22(skillsDir, skill.slug);
55674
- const skillPath = join22(skillDir, "SKILL.md");
55675
- await mkdir10(skillDir, { recursive: true });
55676
- await writeFile9(skillPath, generateSkillMd(skill.name), "utf-8");
56061
+ const skillDir = join23(skillsDir, skill.slug);
56062
+ const skillPath = join23(skillDir, "SKILL.md");
56063
+ await mkdir11(skillDir, { recursive: true });
56064
+ await writeFile10(skillPath, generateSkillMd(skill.name), "utf-8");
55677
56065
  result.skillsExported += 1;
55678
56066
  result.filesWritten.push(skillPath);
55679
56067
  }
@@ -56401,19 +56789,19 @@ var init_chat_store = __esm({
56401
56789
  });
56402
56790
 
56403
56791
  // ../core/src/oauth-credential-interop.ts
56404
- import { existsSync as existsSync19, readFileSync as readFileSync6 } from "node:fs";
56792
+ import { existsSync as existsSync20, readFileSync as readFileSync6 } from "node:fs";
56405
56793
  import { homedir as homedir4 } from "node:os";
56406
- import { join as join23 } from "node:path";
56794
+ import { join as join24 } from "node:path";
56407
56795
  function getHomeDir4() {
56408
56796
  return process.env.HOME || process.env.USERPROFILE || homedir4();
56409
56797
  }
56410
56798
  function getCodexCliAuthPath(home = getHomeDir4()) {
56411
- return join23(home, ".codex", "auth.json");
56799
+ return join24(home, ".codex", "auth.json");
56412
56800
  }
56413
56801
  function getClaudeCodeCredentialPaths(home = getHomeDir4()) {
56414
56802
  return [
56415
- join23(home, ".claude", ".credentials.json"),
56416
- join23(home, ".config", "claude", ".credentials.json")
56803
+ join24(home, ".claude", ".credentials.json"),
56804
+ join24(home, ".config", "claude", ".credentials.json")
56417
56805
  ];
56418
56806
  }
56419
56807
  function parseJwtPayload(token) {
@@ -56580,7 +56968,7 @@ function extractClaudeCliStoredCredential(raw) {
56580
56968
  };
56581
56969
  }
56582
56970
  function readStoredCredentialsFromAuthFile(authPath) {
56583
- if (!existsSync19(authPath)) {
56971
+ if (!existsSync20(authPath)) {
56584
56972
  return {};
56585
56973
  }
56586
56974
  try {
@@ -56717,6 +57105,7 @@ __export(src_exports, {
56717
57105
  MAX_TITLE_LENGTH: () => MAX_TITLE_LENGTH,
56718
57106
  MEMORY_AUDIT_PATH: () => MEMORY_AUDIT_PATH,
56719
57107
  MEMORY_BACKEND_SETTINGS_KEYS: () => MEMORY_BACKEND_SETTINGS_KEYS,
57108
+ MEMORY_BACKUP_SCHEDULE_NAME: () => MEMORY_BACKUP_SCHEDULE_NAME,
56720
57109
  MEMORY_DREAMS_FILENAME: () => MEMORY_DREAMS_FILENAME,
56721
57110
  MEMORY_DREAMS_SCHEDULE_NAME: () => MEMORY_DREAMS_SCHEDULE_NAME,
56722
57111
  MEMORY_INSIGHTS_PATH: () => MEMORY_INSIGHTS_PATH,
@@ -56735,6 +57124,7 @@ __export(src_exports, {
56735
57124
  MISSION_EVENT_TYPES: () => MISSION_EVENT_TYPES,
56736
57125
  MISSION_STATUSES: () => MISSION_STATUSES,
56737
57126
  MemoryBackendError: () => MemoryBackendError,
57127
+ MemoryBackupManager: () => MemoryBackupManager,
56738
57128
  MeshConfigGenerator: () => MeshConfigGenerator,
56739
57129
  MessageStore: () => MessageStore,
56740
57130
  MigrationCoordinator: () => MigrationCoordinator,
@@ -56840,6 +57230,7 @@ __export(src_exports, {
56840
57230
  createDistributedTaskIdAllocator: () => createDistributedTaskIdAllocator,
56841
57231
  createFenceToken: () => createFenceToken,
56842
57232
  createInsightExtractionAutomation: () => createInsightExtractionAutomation,
57233
+ createMemoryBackupManager: () => createMemoryBackupManager,
56843
57234
  createMemoryDreamsAutomation: () => createMemoryDreamsAutomation,
56844
57235
  createMissionHierarchySnapshot: () => createMissionHierarchySnapshot,
56845
57236
  createProjectSettingsSnapshot: () => createProjectSettingsSnapshot,
@@ -57029,6 +57420,7 @@ __export(src_exports, {
57029
57420
  runGhAsync: () => runGhAsync,
57030
57421
  runGhJson: () => runGhJson,
57031
57422
  runGhJsonAsync: () => runGhJsonAsync,
57423
+ runMemoryBackupCommand: () => runMemoryBackupCommand,
57032
57424
  runScheduledEvalBatch: () => runScheduledEvalBatch,
57033
57425
  sanitizeCommitSubject: () => sanitizeCommitSubject,
57034
57426
  sanitizeDockerNodeConfigForResponse: () => sanitizeDockerNodeConfigForResponse,
@@ -57052,6 +57444,8 @@ __export(src_exports, {
57052
57444
  syncBackupAutomation: () => syncBackupAutomation,
57053
57445
  syncBackupRoutine: () => syncBackupRoutine,
57054
57446
  syncInsightExtractionAutomation: () => syncInsightExtractionAutomation,
57447
+ syncMemoryBackupAutomation: () => syncMemoryBackupAutomation,
57448
+ syncMemoryBackupRoutine: () => syncMemoryBackupRoutine,
57055
57449
  syncMemoryDreamsAutomation: () => syncMemoryDreamsAutomation,
57056
57450
  syncScheduledEvalBatchAutomation: () => syncScheduledEvalBatchAutomation,
57057
57451
  taskMatchesReplicatedCreate: () => taskMatchesReplicatedCreate,
@@ -57066,6 +57460,7 @@ __export(src_exports, {
57066
57460
  validateDescription: () => validateDescription,
57067
57461
  validateDockerNodeConfig: () => validateDockerNodeConfig,
57068
57462
  validateImportData: () => validateImportData,
57463
+ validateMemoryBackupSchedule: () => validateMemoryBackupSchedule,
57069
57464
  validateMessageMetadata: () => validateMessageMetadata,
57070
57465
  validateNodeOverrideChange: () => validateNodeOverrideChange,
57071
57466
  validatePluginManifest: () => validatePluginManifest,
@@ -57123,6 +57518,7 @@ var init_src = __esm({
57123
57518
  init_plugin_loader();
57124
57519
  init_plugin_security_scan();
57125
57520
  init_backup();
57521
+ init_memory_backup();
57126
57522
  init_settings_export();
57127
57523
  init_ai_summarize();
57128
57524
  init_model_resolution();
@@ -57876,12 +58272,12 @@ var init_concurrency = __esm({
57876
58272
  this._active++;
57877
58273
  return Promise.resolve();
57878
58274
  }
57879
- return new Promise((resolve23) => {
58275
+ return new Promise((resolve24) => {
57880
58276
  this._waiters.push({
57881
58277
  priority,
57882
58278
  resolve: () => {
57883
58279
  this._active++;
57884
- resolve23();
58280
+ resolve24();
57885
58281
  }
57886
58282
  });
57887
58283
  });
@@ -58689,23 +59085,23 @@ var init_github_provider = __esm({
58689
59085
  });
58690
59086
 
58691
59087
  // ../engine/src/skill-resolver.ts
58692
- import { existsSync as existsSync20, readFileSync as readFileSync7 } from "node:fs";
58693
- import { dirname as dirname8, join as join24, resolve as resolve12 } from "node:path";
59088
+ import { existsSync as existsSync21, readFileSync as readFileSync7 } from "node:fs";
59089
+ import { dirname as dirname9, join as join25, resolve as resolve13 } from "node:path";
58694
59090
  function resolveProjectRoot(cwd) {
58695
- let current = resolve12(cwd);
59091
+ let current = resolve13(cwd);
58696
59092
  while (true) {
58697
- if (existsSync20(join24(current, ".fusion"))) {
59093
+ if (existsSync21(join25(current, ".fusion"))) {
58698
59094
  return current;
58699
59095
  }
58700
- const parent = dirname8(current);
59096
+ const parent = dirname9(current);
58701
59097
  if (parent === current) {
58702
- return resolve12(cwd);
59098
+ return resolve13(cwd);
58703
59099
  }
58704
59100
  current = parent;
58705
59101
  }
58706
59102
  }
58707
59103
  function readJsonObject(path2) {
58708
- if (!existsSync20(path2)) {
59104
+ if (!existsSync21(path2)) {
58709
59105
  return {};
58710
59106
  }
58711
59107
  try {
@@ -58716,8 +59112,8 @@ function readJsonObject(path2) {
58716
59112
  }
58717
59113
  }
58718
59114
  function readProjectSettings(projectRootDir) {
58719
- const fusionSettings = join24(projectRootDir, ".fusion", "settings.json");
58720
- if (existsSync20(fusionSettings)) {
59115
+ const fusionSettings = join25(projectRootDir, ".fusion", "settings.json");
59116
+ if (existsSync21(fusionSettings)) {
58721
59117
  const parsed = readJsonObject(fusionSettings);
58722
59118
  return {
58723
59119
  skills: Array.isArray(parsed.skills) ? parsed.skills : void 0,
@@ -58963,24 +59359,24 @@ var init_context_limit_detector = __esm({
58963
59359
  });
58964
59360
 
58965
59361
  // ../engine/src/auth-storage.ts
58966
- import { existsSync as existsSync21, readFileSync as readFileSync8 } from "node:fs";
59362
+ import { existsSync as existsSync22, readFileSync as readFileSync8 } from "node:fs";
58967
59363
  import { homedir as homedir5 } from "node:os";
58968
- import { join as join25 } from "node:path";
59364
+ import { join as join26 } from "node:path";
58969
59365
  import { AuthStorage } from "@mariozechner/pi-coding-agent";
58970
59366
  import { getOAuthProvider } from "@mariozechner/pi-ai/oauth";
58971
59367
  function getHomeDir5() {
58972
59368
  return process.env.HOME || process.env.USERPROFILE || homedir5();
58973
59369
  }
58974
59370
  function getFusionAuthPath(home = getHomeDir5()) {
58975
- return join25(home, ".fusion", "agent", "auth.json");
59371
+ return join26(home, ".fusion", "agent", "auth.json");
58976
59372
  }
58977
59373
  function getFusionModelsPath(home = getHomeDir5()) {
58978
- return join25(home, ".fusion", "agent", "models.json");
59374
+ return join26(home, ".fusion", "agent", "models.json");
58979
59375
  }
58980
59376
  function getLegacyAuthPaths(home = getHomeDir5()) {
58981
59377
  return [
58982
- join25(home, ".pi", "agent", "auth.json"),
58983
- join25(home, ".pi", "auth.json")
59378
+ join26(home, ".pi", "agent", "auth.json"),
59379
+ join26(home, ".pi", "auth.json")
58984
59380
  ];
58985
59381
  }
58986
59382
  function getSupplementalAuthPaths(home = getHomeDir5()) {
@@ -58992,16 +59388,16 @@ function getSupplementalAuthPaths(home = getHomeDir5()) {
58992
59388
  }
58993
59389
  function getLegacyModelsPaths(home = getHomeDir5()) {
58994
59390
  return [
58995
- join25(home, ".pi", "agent", "models.json"),
58996
- join25(home, ".pi", "models.json")
59391
+ join26(home, ".pi", "agent", "models.json"),
59392
+ join26(home, ".pi", "models.json")
58997
59393
  ];
58998
59394
  }
58999
59395
  function getModelRegistryModelsPath(home = getHomeDir5()) {
59000
59396
  const fusionModelsPath = getFusionModelsPath(home);
59001
- if (existsSync21(fusionModelsPath)) {
59397
+ if (existsSync22(fusionModelsPath)) {
59002
59398
  return fusionModelsPath;
59003
59399
  }
59004
- return getLegacyModelsPaths(home).find((modelsPath) => existsSync21(modelsPath)) ?? fusionModelsPath;
59400
+ return getLegacyModelsPaths(home).find((modelsPath) => existsSync22(modelsPath)) ?? fusionModelsPath;
59005
59401
  }
59006
59402
  function readSupplementalCredentials(authPaths = getSupplementalAuthPaths()) {
59007
59403
  const credentials = {};
@@ -59035,7 +59431,7 @@ function resolveStoredCredentialApiKey(providerId, credential) {
59035
59431
  function readModelsJsonApiKeys(home = getHomeDir5()) {
59036
59432
  const apiKeys = /* @__PURE__ */ new Map();
59037
59433
  const modelsPath = getModelRegistryModelsPath(home);
59038
- if (!existsSync21(modelsPath)) {
59434
+ if (!existsSync22(modelsPath)) {
59039
59435
  return apiKeys;
59040
59436
  }
59041
59437
  try {
@@ -59199,10 +59595,10 @@ var init_auth_storage = __esm({
59199
59595
  // ../engine/src/custom-providers.ts
59200
59596
  import { readFileSync as readFileSync9 } from "node:fs";
59201
59597
  import { homedir as homedir6 } from "node:os";
59202
- import { join as join26 } from "node:path";
59598
+ import { join as join27 } from "node:path";
59203
59599
  function readCustomProviders(homeDir = homedir6()) {
59204
59600
  try {
59205
- const settingsPath = join26(homeDir, ".fusion", "settings.json");
59601
+ const settingsPath = join27(homeDir, ".fusion", "settings.json");
59206
59602
  const raw = readFileSync9(settingsPath, "utf-8");
59207
59603
  const parsed = JSON.parse(raw);
59208
59604
  return Array.isArray(parsed.customProviders) ? parsed.customProviders : [];
@@ -59286,11 +59682,11 @@ var init_permanent_agent_gating = __esm({
59286
59682
  });
59287
59683
 
59288
59684
  // ../engine/src/pi.ts
59289
- import { existsSync as existsSync22, readFileSync as readFileSync10 } from "node:fs";
59685
+ import { existsSync as existsSync23, readFileSync as readFileSync10 } from "node:fs";
59290
59686
  import { exec as exec2 } from "node:child_process";
59291
59687
  import { promisify as promisify3 } from "node:util";
59292
59688
  import { createRequire as createRequire2 } from "node:module";
59293
- import { basename as basename7, dirname as dirname9, join as join27, relative as relative3, isAbsolute as isAbsolute7, resolve as resolve13 } from "node:path";
59689
+ import { basename as basename7, dirname as dirname10, join as join28, relative as relative4, isAbsolute as isAbsolute8, resolve as resolve14 } from "node:path";
59294
59690
  import {
59295
59691
  createAgentSession,
59296
59692
  createBashTool,
@@ -59751,7 +60147,7 @@ function isRetryableModelSelectionError(message) {
59751
60147
  return normalized.includes("rate limit") || normalized.includes("too many requests") || normalized.includes("429") || normalized.includes("401") || normalized.includes("403") || normalized.includes("unauthorized") || normalized.includes("forbidden") || normalized.includes("authentication") || normalized.includes("invalid api key") || normalized.includes("invalid key") || normalized.includes("api key") || normalized.includes("overloaded") || normalized.includes("quota") || normalized.includes("capacity") || normalized.includes("temporarily unavailable") || normalized.includes("invalid temperature");
59752
60148
  }
59753
60149
  function readJsonObject2(path2) {
59754
- if (!existsSync22(path2)) {
60150
+ if (!existsSync23(path2)) {
59755
60151
  return {};
59756
60152
  }
59757
60153
  try {
@@ -59892,17 +60288,17 @@ function siblingAgentDir(agentDir, siblingRoot) {
59892
60288
  if (basename7(agentDir) !== "agent") {
59893
60289
  return void 0;
59894
60290
  }
59895
- return join27(dirname9(dirname9(agentDir)), siblingRoot, "agent");
60291
+ return join28(dirname10(dirname10(agentDir)), siblingRoot, "agent");
59896
60292
  }
59897
60293
  function createReadOnlyPiSettingsView(cwd, agentDir) {
59898
60294
  const projectRoot = resolvePiExtensionProjectRoot(cwd);
59899
- const fusionAgentDir = agentDir.includes(`${join27(".fusion", "agent")}`) ? agentDir : siblingAgentDir(agentDir, ".fusion");
59900
- const legacyAgentDir = agentDir.includes(`${join27(".pi", "agent")}`) ? agentDir : siblingAgentDir(agentDir, ".pi");
59901
- const legacyGlobalSettings = legacyAgentDir ? readJsonObject2(join27(legacyAgentDir, "settings.json")) : {};
59902
- const fusionGlobalSettings = fusionAgentDir ? readJsonObject2(join27(fusionAgentDir, "settings.json")) : {};
59903
- const directGlobalSettings = readJsonObject2(join27(agentDir, "settings.json"));
60295
+ const fusionAgentDir = agentDir.includes(`${join28(".fusion", "agent")}`) ? agentDir : siblingAgentDir(agentDir, ".fusion");
60296
+ const legacyAgentDir = agentDir.includes(`${join28(".pi", "agent")}`) ? agentDir : siblingAgentDir(agentDir, ".pi");
60297
+ const legacyGlobalSettings = legacyAgentDir ? readJsonObject2(join28(legacyAgentDir, "settings.json")) : {};
60298
+ const fusionGlobalSettings = fusionAgentDir ? readJsonObject2(join28(fusionAgentDir, "settings.json")) : {};
60299
+ const directGlobalSettings = readJsonObject2(join28(agentDir, "settings.json"));
59904
60300
  const globalSettings = { ...legacyGlobalSettings, ...directGlobalSettings, ...fusionGlobalSettings };
59905
- const fusionProjectSettings = readJsonObject2(join27(projectRoot, ".fusion", "settings.json"));
60301
+ const fusionProjectSettings = readJsonObject2(join28(projectRoot, ".fusion", "settings.json"));
59906
60302
  const mergedSettings = { ...globalSettings, ...fusionProjectSettings };
59907
60303
  return {
59908
60304
  getGlobalSettings: () => structuredClone(globalSettings),
@@ -59913,15 +60309,15 @@ function createReadOnlyPiSettingsView(cwd, agentDir) {
59913
60309
  function getPackageManagerAgentDir() {
59914
60310
  const fusionAgentDir = getFusionAgentDir();
59915
60311
  const legacyAgentDir = getLegacyPiAgentDir();
59916
- const fusionSettings = readJsonObject2(join27(fusionAgentDir, "settings.json"));
59917
- const legacySettings = readJsonObject2(join27(legacyAgentDir, "settings.json"));
59918
- if (hasPackageManagerSettings(fusionSettings) || !existsSync22(legacyAgentDir)) {
60312
+ const fusionSettings = readJsonObject2(join28(fusionAgentDir, "settings.json"));
60313
+ const legacySettings = readJsonObject2(join28(legacyAgentDir, "settings.json"));
60314
+ if (hasPackageManagerSettings(fusionSettings) || !existsSync23(legacyAgentDir)) {
59919
60315
  return fusionAgentDir;
59920
60316
  }
59921
60317
  if (hasPackageManagerSettings(legacySettings)) {
59922
60318
  return legacyAgentDir;
59923
60319
  }
59924
- return existsSync22(fusionAgentDir) ? fusionAgentDir : legacyAgentDir;
60320
+ return existsSync23(fusionAgentDir) ? fusionAgentDir : legacyAgentDir;
59925
60321
  }
59926
60322
  function resolveVendoredClaudeCliEntry() {
59927
60323
  try {
@@ -59932,8 +60328,8 @@ function resolveVendoredClaudeCliEntry() {
59932
60328
  if (!Array.isArray(extensions) || extensions.length === 0) return null;
59933
60329
  const entry = extensions[0];
59934
60330
  if (typeof entry !== "string" || entry.length === 0) return null;
59935
- const path2 = resolve13(dirname9(pkgJsonPath), entry);
59936
- return existsSync22(path2) ? path2 : null;
60331
+ const path2 = resolve14(dirname10(pkgJsonPath), entry);
60332
+ return existsSync23(path2) ? path2 : null;
59937
60333
  } catch {
59938
60334
  return null;
59939
60335
  }
@@ -59947,8 +60343,8 @@ function resolveVendoredDroidCliEntry() {
59947
60343
  if (!Array.isArray(extensions) || extensions.length === 0) return null;
59948
60344
  const entry = extensions[0];
59949
60345
  if (typeof entry !== "string" || entry.length === 0) return null;
59950
- const path2 = resolve13(dirname9(pkgJsonPath), entry);
59951
- return existsSync22(path2) ? path2 : null;
60346
+ const path2 = resolve14(dirname10(pkgJsonPath), entry);
60347
+ return existsSync23(path2) ? path2 : null;
59952
60348
  } catch {
59953
60349
  return null;
59954
60350
  }
@@ -59976,7 +60372,7 @@ async function registerExtensionProviders(cwd, modelRegistry) {
59976
60372
  const extensionsResult = await discoverAndLoadExtensions(
59977
60373
  doubleReconciledPaths,
59978
60374
  cwd,
59979
- join27(resolvePiExtensionProjectRoot(cwd), ".fusion", "disabled-auto-extension-discovery")
60375
+ join28(resolvePiExtensionProjectRoot(cwd), ".fusion", "disabled-auto-extension-discovery")
59980
60376
  );
59981
60377
  for (const { path: path2, error } of extensionsResult.errors) {
59982
60378
  extensionsLog.warn(`Failed to load ${path2}: ${error}`);
@@ -60011,9 +60407,9 @@ async function isRegisteredGitWorktree(projectRoot, worktreePath) {
60011
60407
  cwd: projectRoot,
60012
60408
  encoding: "utf-8"
60013
60409
  });
60014
- const resolvedWorktree = resolve13(worktreePath);
60410
+ const resolvedWorktree = resolve14(worktreePath);
60015
60411
  return stdout.split("\n").some(
60016
- (line) => line.startsWith("worktree ") && resolve13(line.slice("worktree ".length)) === resolvedWorktree
60412
+ (line) => line.startsWith("worktree ") && resolve14(line.slice("worktree ".length)) === resolvedWorktree
60017
60413
  );
60018
60414
  } catch {
60019
60415
  return false;
@@ -60025,16 +60421,16 @@ async function isCompleteGitWorktree(worktreePath) {
60025
60421
  cwd: worktreePath,
60026
60422
  encoding: "utf-8"
60027
60423
  });
60028
- return resolve13(stdout.trim()) === resolve13(worktreePath);
60424
+ return resolve14(stdout.trim()) === resolve14(worktreePath);
60029
60425
  } catch {
60030
60426
  return false;
60031
60427
  }
60032
60428
  }
60033
60429
  async function assertValidWorktreeSession(cwd, projectRoot) {
60034
- if (!existsSync22(cwd)) {
60430
+ if (!existsSync23(cwd)) {
60035
60431
  throw new Error(`Refusing to start coding agent in missing worktree: ${cwd}`);
60036
60432
  }
60037
- if (!existsSync22(join27(cwd, ".git")) || !await isCompleteGitWorktree(cwd)) {
60433
+ if (!existsSync23(join28(cwd, ".git")) || !await isCompleteGitWorktree(cwd)) {
60038
60434
  throw new Error(`Refusing to start coding agent in incomplete worktree: ${cwd}`);
60039
60435
  }
60040
60436
  if (!await isRegisteredGitWorktree(projectRoot, cwd)) {
@@ -60042,14 +60438,14 @@ async function assertValidWorktreeSession(cwd, projectRoot) {
60042
60438
  }
60043
60439
  }
60044
60440
  function isWorktreeAllowedPath(worktreePath, projectRoot, requestedPath, toolName) {
60045
- const worktreeResolved = resolve13(worktreePath);
60046
- const projectRootResolved = resolve13(projectRoot);
60047
- const requestedResolved = isAbsolute7(requestedPath) ? resolve13(requestedPath) : resolve13(worktreeResolved, requestedPath);
60048
- const relToWorktree = relative3(worktreeResolved, requestedResolved);
60049
- if (!relToWorktree.startsWith("..") && !isAbsolute7(relToWorktree)) {
60441
+ const worktreeResolved = resolve14(worktreePath);
60442
+ const projectRootResolved = resolve14(projectRoot);
60443
+ const requestedResolved = isAbsolute8(requestedPath) ? resolve14(requestedPath) : resolve14(worktreeResolved, requestedPath);
60444
+ const relToWorktree = relative4(worktreeResolved, requestedResolved);
60445
+ if (!relToWorktree.startsWith("..") && !isAbsolute8(relToWorktree)) {
60050
60446
  return true;
60051
60447
  }
60052
- const relToProjectRoot = relative3(projectRootResolved, requestedResolved).replace(/\\/g, "/");
60448
+ const relToProjectRoot = relative4(projectRootResolved, requestedResolved).replace(/\\/g, "/");
60053
60449
  if (relToProjectRoot === ".fusion/memory" || relToProjectRoot === ".fusion/memory/" || relToProjectRoot.startsWith(".fusion/memory/")) {
60054
60450
  return true;
60055
60451
  }
@@ -60103,7 +60499,7 @@ function wrapToolsWithBoundary(tools, worktreePath, projectRoot) {
60103
60499
  const _signal = args[2];
60104
60500
  const pathArg = params.path;
60105
60501
  if (pathArg && !isWorktreeAllowedPath(worktreePath, projectRoot, pathArg, tool.name)) {
60106
- const relToProject = relative3(projectRoot, pathArg);
60502
+ const relToProject = relative4(projectRoot, pathArg);
60107
60503
  return boundaryRejection(
60108
60504
  `Path "${relToProject}" is outside the worktree boundary. Coding agents can only modify files inside the current worktree. Exceptions (read-only): .fusion/memory/, .fusion/tasks/*/attachments/, and .fusion/tasks/*/{PROMPT.md,task.json} for dependency context.`
60109
60505
  );
@@ -60852,7 +61248,7 @@ ${source.content ?? ""}`;
60852
61248
 
60853
61249
  // ../engine/src/research/providers/local-docs-provider.ts
60854
61250
  import { promises as fs } from "node:fs";
60855
- import { extname as extname2, join as join28, relative as relative4, resolve as resolve14 } from "node:path";
61251
+ import { extname as extname2, join as join29, relative as relative5, resolve as resolve15 } from "node:path";
60856
61252
  function buildExcerpt(content, terms) {
60857
61253
  const lower = content.toLowerCase();
60858
61254
  const first = terms.find((term) => lower.includes(term));
@@ -60889,7 +61285,7 @@ var init_local_docs_provider = __esm({
60889
61285
  LocalDocsProvider = class {
60890
61286
  constructor(options) {
60891
61287
  this.options = options;
60892
- this.projectRoot = resolve14(options.projectRoot);
61288
+ this.projectRoot = resolve15(options.projectRoot);
60893
61289
  this.scanPaths = options.scanPaths ?? ["docs", "README.md", "AGENTS.md", ".fusion/memory"];
60894
61290
  }
60895
61291
  type = "local-docs";
@@ -60915,7 +61311,7 @@ var init_local_docs_provider = __esm({
60915
61311
  score += matches?.length ?? 0;
60916
61312
  }
60917
61313
  if (score <= 0) continue;
60918
- const relPath = relative4(this.projectRoot, file);
61314
+ const relPath = relative5(this.projectRoot, file);
60919
61315
  results.push({
60920
61316
  score,
60921
61317
  source: {
@@ -60933,7 +61329,7 @@ var init_local_docs_provider = __esm({
60933
61329
  }
60934
61330
  async fetchContent(filePath, config = {}, signal) {
60935
61331
  const timeoutMs = Number(config.timeoutMs ?? this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS3);
60936
- const resolvedPath = resolve14(this.projectRoot, filePath);
61332
+ const resolvedPath = resolve15(this.projectRoot, filePath);
60937
61333
  if (!resolvedPath.startsWith(this.projectRoot)) {
60938
61334
  throw new ResearchProviderError({
60939
61335
  providerType: "local-docs",
@@ -60941,8 +61337,8 @@ var init_local_docs_provider = __esm({
60941
61337
  message: "Path traversal is not allowed"
60942
61338
  });
60943
61339
  }
60944
- const stat8 = await this.withTimeout(fs.stat(resolvedPath), timeoutMs, signal);
60945
- if (!stat8.isFile()) {
61340
+ const stat9 = await this.withTimeout(fs.stat(resolvedPath), timeoutMs, signal);
61341
+ if (!stat9.isFile()) {
60946
61342
  throw new ResearchProviderError({ providerType: "local-docs", code: "provider-unavailable", message: "Path is not a file" });
60947
61343
  }
60948
61344
  const content = await this.withTimeout(fs.readFile(resolvedPath), timeoutMs, signal);
@@ -60954,9 +61350,9 @@ var init_local_docs_provider = __esm({
60954
61350
  return {
60955
61351
  content: text.length > MAX_FILE_SIZE_BYTES ? text.slice(0, MAX_FILE_SIZE_BYTES) : text,
60956
61352
  metadata: {
60957
- path: relative4(this.projectRoot, resolvedPath),
60958
- size: stat8.size,
60959
- modifiedAt: stat8.mtime.toISOString(),
61353
+ path: relative5(this.projectRoot, resolvedPath),
61354
+ size: stat9.size,
61355
+ modifiedAt: stat9.mtime.toISOString(),
60960
61356
  extension: extname2(resolvedPath)
60961
61357
  },
60962
61358
  mimeType: "text/plain"
@@ -60966,13 +61362,13 @@ var init_local_docs_provider = __esm({
60966
61362
  const ignorePatterns = await this.readGitignore();
60967
61363
  const files = [];
60968
61364
  for (const pathEntry of this.scanPaths) {
60969
- const target = resolve14(this.projectRoot, pathEntry);
61365
+ const target = resolve15(this.projectRoot, pathEntry);
60970
61366
  if (!target.startsWith(this.projectRoot)) continue;
60971
61367
  try {
60972
- const stat8 = await fs.stat(target);
60973
- if (stat8.isDirectory()) {
61368
+ const stat9 = await fs.stat(target);
61369
+ if (stat9.isDirectory()) {
60974
61370
  await this.walk(target, files, ignorePatterns, signal);
60975
- } else if (stat8.isFile()) {
61371
+ } else if (stat9.isFile()) {
60976
61372
  files.push(target);
60977
61373
  }
60978
61374
  } catch {
@@ -60981,7 +61377,7 @@ var init_local_docs_provider = __esm({
60981
61377
  const rootEntries = await fs.readdir(this.projectRoot, { withFileTypes: true });
60982
61378
  for (const entry of rootEntries) {
60983
61379
  if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
60984
- files.push(join28(this.projectRoot, entry.name));
61380
+ files.push(join29(this.projectRoot, entry.name));
60985
61381
  }
60986
61382
  }
60987
61383
  return [...new Set(files)];
@@ -60991,8 +61387,8 @@ var init_local_docs_provider = __esm({
60991
61387
  const entries = await fs.readdir(dir, { withFileTypes: true });
60992
61388
  for (const entry of entries) {
60993
61389
  this.throwIfAborted(signal);
60994
- const fullPath = join28(dir, entry.name);
60995
- const relPath = relative4(this.projectRoot, fullPath).replace(/\\/g, "/");
61390
+ const fullPath = join29(dir, entry.name);
61391
+ const relPath = relative5(this.projectRoot, fullPath).replace(/\\/g, "/");
60996
61392
  if (matchesGitignore(relPath, ignorePatterns)) continue;
60997
61393
  if (entry.isDirectory()) {
60998
61394
  await this.walk(fullPath, out, ignorePatterns, signal);
@@ -61003,8 +61399,8 @@ var init_local_docs_provider = __esm({
61003
61399
  }
61004
61400
  async safeReadText(filePath, signal) {
61005
61401
  try {
61006
- const stat8 = await fs.stat(filePath);
61007
- if (stat8.size > MAX_FILE_SIZE_BYTES) return void 0;
61402
+ const stat9 = await fs.stat(filePath);
61403
+ if (stat9.size > MAX_FILE_SIZE_BYTES) return void 0;
61008
61404
  const content = await fs.readFile(filePath);
61009
61405
  if (content.subarray(0, BINARY_SNIFF_BYTES).includes(0)) return void 0;
61010
61406
  this.throwIfAborted(signal);
@@ -61016,7 +61412,7 @@ var init_local_docs_provider = __esm({
61016
61412
  }
61017
61413
  async readGitignore() {
61018
61414
  try {
61019
- const content = await fs.readFile(join28(this.projectRoot, ".gitignore"), "utf-8");
61415
+ const content = await fs.readFile(join29(this.projectRoot, ".gitignore"), "utf-8");
61020
61416
  return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
61021
61417
  } catch {
61022
61418
  return [];
@@ -61349,8 +61745,8 @@ var init_page_fetch_provider = __esm({
61349
61745
 
61350
61746
  // ../engine/src/research/providers/web-search-provider.ts
61351
61747
  async function sleep(ms, signal) {
61352
- await new Promise((resolve23, reject) => {
61353
- const timer = setTimeout(resolve23, ms);
61748
+ await new Promise((resolve24, reject) => {
61749
+ const timer = setTimeout(resolve24, ms);
61354
61750
  const onAbort = () => {
61355
61751
  clearTimeout(timer);
61356
61752
  reject(new ResearchProviderError({ providerType: "web-search", code: "abort", message: "Search aborted" }));
@@ -61818,31 +62214,31 @@ var init_research_step_runner = __esm({
61818
62214
  });
61819
62215
 
61820
62216
  // ../engine/src/agent-tools.ts
61821
- import { appendFile as appendFile3, mkdir as mkdir11, readFile as readFile13, readdir as readdir7, stat as stat4, writeFile as writeFile10 } from "node:fs/promises";
61822
- import { existsSync as existsSync23 } from "node:fs";
62217
+ import { appendFile as appendFile3, mkdir as mkdir12, readFile as readFile14, readdir as readdir8, stat as stat5, writeFile as writeFile11 } from "node:fs/promises";
62218
+ import { existsSync as existsSync24 } from "node:fs";
61823
62219
  import { createHash as createHash5 } from "node:crypto";
61824
- import { join as join29, relative as relative5, resolve as resolve15 } from "node:path";
62220
+ import { join as join30, relative as relative6, resolve as resolve16 } from "node:path";
61825
62221
  import { Type } from "@mariozechner/pi-ai";
61826
62222
  function sanitizeAgentMemoryId(agentId) {
61827
62223
  return agentId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
61828
62224
  }
61829
62225
  function agentMemoryDisplayPath(agentId) {
61830
- return `${AGENT_MEMORY_ROOT2}/${sanitizeAgentMemoryId(agentId)}/${AGENT_MEMORY_FILENAME2}`;
62226
+ return `${AGENT_MEMORY_ROOT3}/${sanitizeAgentMemoryId(agentId)}/${AGENT_MEMORY_FILENAME2}`;
61831
62227
  }
61832
62228
  function agentDreamsDisplayPath(agentId) {
61833
- return `${AGENT_MEMORY_ROOT2}/${sanitizeAgentMemoryId(agentId)}/${AGENT_DREAMS_FILENAME2}`;
62229
+ return `${AGENT_MEMORY_ROOT3}/${sanitizeAgentMemoryId(agentId)}/${AGENT_DREAMS_FILENAME2}`;
61834
62230
  }
61835
62231
  function agentMemoryDirectory(rootDir, agentId) {
61836
- return join29(rootDir, AGENT_MEMORY_ROOT2, sanitizeAgentMemoryId(agentId));
62232
+ return join30(rootDir, AGENT_MEMORY_ROOT3, sanitizeAgentMemoryId(agentId));
61837
62233
  }
61838
62234
  function agentMemoryFilePath(rootDir, agentId) {
61839
- return join29(agentMemoryDirectory(rootDir, agentId), AGENT_MEMORY_FILENAME2);
62235
+ return join30(agentMemoryDirectory(rootDir, agentId), AGENT_MEMORY_FILENAME2);
61840
62236
  }
61841
62237
  function agentDreamsFilePath(rootDir, agentId) {
61842
- return join29(agentMemoryDirectory(rootDir, agentId), AGENT_DREAMS_FILENAME2);
62238
+ return join30(agentMemoryDirectory(rootDir, agentId), AGENT_DREAMS_FILENAME2);
61843
62239
  }
61844
62240
  function agentDailyFilePath(rootDir, agentId, date = /* @__PURE__ */ new Date()) {
61845
- return join29(agentMemoryDirectory(rootDir, agentId), `${date.toISOString().slice(0, 10)}.md`);
62241
+ return join30(agentMemoryDirectory(rootDir, agentId), `${date.toISOString().slice(0, 10)}.md`);
61846
62242
  }
61847
62243
  async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
61848
62244
  const safeRoot = typeof rootDir === "string" ? rootDir.trim() : "";
@@ -61852,11 +62248,11 @@ async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
61852
62248
  }
61853
62249
  const filePath = agentMemoryFilePath(safeRoot, safeAgentId);
61854
62250
  try {
61855
- const fileStat = await stat4(filePath);
62251
+ const fileStat = await stat5(filePath);
61856
62252
  if (!fileStat.isFile()) {
61857
62253
  return "";
61858
62254
  }
61859
- const content = await readFile13(filePath, "utf-8");
62255
+ const content = await readFile14(filePath, "utf-8");
61860
62256
  return typeof content === "string" ? content.trim() : "";
61861
62257
  } catch {
61862
62258
  return "";
@@ -61894,9 +62290,9 @@ async function syncAgentMemoryFile(rootDir, agentMemory) {
61894
62290
  return null;
61895
62291
  }
61896
62292
  const dir = agentMemoryDirectory(rootDir, agentMemory.agentId);
61897
- await mkdir11(dir, { recursive: true });
62293
+ await mkdir12(dir, { recursive: true });
61898
62294
  const longTermPath = agentMemoryFilePath(rootDir, agentMemory.agentId);
61899
- if (!existsSync23(longTermPath)) {
62295
+ if (!existsSync24(longTermPath)) {
61900
62296
  const title = agentMemory.agentName?.trim() ? `# Agent Memory: ${agentMemory.agentName.trim()}` : "# Agent Memory";
61901
62297
  const fileContent = `${title}
61902
62298
 
@@ -61904,15 +62300,15 @@ async function syncAgentMemoryFile(rootDir, agentMemory) {
61904
62300
 
61905
62301
  ${content || ""}
61906
62302
  `;
61907
- await writeFile10(longTermPath, fileContent, "utf-8");
62303
+ await writeFile11(longTermPath, fileContent, "utf-8");
61908
62304
  }
61909
62305
  const dreamsPath = agentDreamsFilePath(rootDir, agentMemory.agentId);
61910
- if (!existsSync23(dreamsPath)) {
61911
- await writeFile10(dreamsPath, "# Agent Memory Dreams\n\n<!-- Synthesized patterns from this agent's daily notes. -->\n", "utf-8");
62306
+ if (!existsSync24(dreamsPath)) {
62307
+ await writeFile11(dreamsPath, "# Agent Memory Dreams\n\n<!-- Synthesized patterns from this agent's daily notes. -->\n", "utf-8");
61912
62308
  }
61913
62309
  const dailyPath = agentDailyFilePath(rootDir, agentMemory.agentId);
61914
- if (!existsSync23(dailyPath)) {
61915
- await writeFile10(dailyPath, `# Agent Daily Memory ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
62310
+ if (!existsSync24(dailyPath)) {
62311
+ await writeFile11(dailyPath, `# Agent Daily Memory ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
61916
62312
 
61917
62313
  <!-- Running observations for this agent. -->
61918
62314
  `, "utf-8");
@@ -61928,19 +62324,19 @@ async function listAgentMemoryFiles2(rootDir, agentMemory) {
61928
62324
  ];
61929
62325
  let entries;
61930
62326
  try {
61931
- entries = await readdir7(dir);
62327
+ entries = await readdir8(dir);
61932
62328
  } catch (err) {
61933
62329
  log10.warn(`Failed to read agent memory directory ${dir}: ${err instanceof Error ? err.message : String(err)}`);
61934
62330
  entries = [];
61935
62331
  }
61936
62332
  for (const entry of entries) {
61937
62333
  if (!DAILY_AGENT_MEMORY_RE2.test(entry)) continue;
61938
- const absPath = join29(dir, entry);
61939
- const fileStat = await stat4(absPath);
62334
+ const absPath = join30(dir, entry);
62335
+ const fileStat = await stat5(absPath);
61940
62336
  if (fileStat.isFile()) {
61941
62337
  files.push({
61942
62338
  absPath,
61943
- displayPath: `${AGENT_MEMORY_ROOT2}/${sanitizeAgentMemoryId(agentMemory.agentId)}/${entry}`
62339
+ displayPath: `${AGENT_MEMORY_ROOT3}/${sanitizeAgentMemoryId(agentMemory.agentId)}/${entry}`
61944
62340
  });
61945
62341
  }
61946
62342
  }
@@ -61958,7 +62354,7 @@ async function searchAgentMemoryFile(rootDir, agentMemory, query, limit) {
61958
62354
  }
61959
62355
  const results = [];
61960
62356
  for (const file of await listAgentMemoryFiles2(rootDir, agentMemory)) {
61961
- const content = await readFile13(file.absPath, "utf-8");
62357
+ const content = await readFile14(file.absPath, "utf-8");
61962
62358
  const lines = content.split("\n");
61963
62359
  for (let index = 0; index < lines.length; index += 8) {
61964
62360
  const chunk = lines.slice(index, index + 12).join("\n").trim();
@@ -62036,13 +62432,13 @@ function normalizeQmdAgentMemoryResultPath(rootDir, agentId, rawPath) {
62036
62432
  candidate = candidate.split("?")[0]?.split("#")[0] ?? "";
62037
62433
  candidate = candidate.replace(/^\.\/+/, "");
62038
62434
  const normalizedAgentId = sanitizeAgentMemoryId(agentId);
62039
- const agentPrefix = `${AGENT_MEMORY_ROOT2}/${normalizedAgentId}/`;
62435
+ const agentPrefix = `${AGENT_MEMORY_ROOT3}/${normalizedAgentId}/`;
62040
62436
  if (candidate.startsWith(agentPrefix)) {
62041
62437
  return resolveAgentMemoryPath(rootDir, agentId, candidate)?.displayPath ?? fallbackPath;
62042
62438
  }
62043
- const workspacePath = resolve15(agentMemoryDirectory(rootDir, agentId)).replace(/\\/g, "/");
62044
- const candidateAbs = resolve15(rootDir, candidate).replace(/\\/g, "/");
62045
- const relToWorkspace = relative5(workspacePath, candidateAbs).replace(/\\/g, "/");
62439
+ const workspacePath = resolve16(agentMemoryDirectory(rootDir, agentId)).replace(/\\/g, "/");
62440
+ const candidateAbs = resolve16(rootDir, candidate).replace(/\\/g, "/");
62441
+ const relToWorkspace = relative6(workspacePath, candidateAbs).replace(/\\/g, "/");
62046
62442
  if (relToWorkspace && !relToWorkspace.startsWith("..") && !relToWorkspace.includes("/../")) {
62047
62443
  const maybeDisplayPath = `${agentPrefix}${relToWorkspace}`;
62048
62444
  return resolveAgentMemoryPath(rootDir, agentId, maybeDisplayPath)?.displayPath ?? fallbackPath;
@@ -62095,7 +62491,7 @@ async function searchAgentMemoryWithQmd(rootDir, agentMemory, query, limit) {
62095
62491
  }
62096
62492
  function resolveAgentMemoryPath(rootDir, agentId, path2) {
62097
62493
  const safeAgentId = sanitizeAgentMemoryId(agentId);
62098
- const prefix = `${AGENT_MEMORY_ROOT2}/${safeAgentId}/`;
62494
+ const prefix = `${AGENT_MEMORY_ROOT3}/${safeAgentId}/`;
62099
62495
  if (!path2.startsWith(prefix)) {
62100
62496
  return null;
62101
62497
  }
@@ -62104,7 +62500,7 @@ function resolveAgentMemoryPath(rootDir, agentId, path2) {
62104
62500
  return null;
62105
62501
  }
62106
62502
  return {
62107
- absPath: join29(agentMemoryDirectory(rootDir, agentId), filename),
62503
+ absPath: join30(agentMemoryDirectory(rootDir, agentId), filename),
62108
62504
  displayPath: `${prefix}${filename}`
62109
62505
  };
62110
62506
  }
@@ -62114,7 +62510,7 @@ async function getAgentMemoryWindow(rootDir, agentMemory, path2, startLine = 1,
62114
62510
  return null;
62115
62511
  }
62116
62512
  await syncAgentMemoryFile(rootDir, agentMemory);
62117
- const content = await readFile13(resolved.absPath, "utf-8");
62513
+ const content = await readFile14(resolved.absPath, "utf-8");
62118
62514
  const lines = content.split("\n");
62119
62515
  const start = Math.max(1, Math.floor(startLine));
62120
62516
  const count = Math.max(1, Math.min(Math.floor(lineCount), 200));
@@ -63123,9 +63519,9 @@ function createResearchTools(options) {
63123
63519
  const maxWaitMs = Math.max(1e3, Math.min(params.max_wait_ms ?? 9e4, resolved.limits.maxDurationMs));
63124
63520
  const completed = await Promise.race([
63125
63521
  runPromise,
63126
- new Promise((resolve23) => setTimeout(() => {
63522
+ new Promise((resolve24) => setTimeout(() => {
63127
63523
  const latest = options.store.getResearchStore().getRun(runId);
63128
- resolve23(latest ?? {
63524
+ resolve24(latest ?? {
63129
63525
  id: runId,
63130
63526
  query: params.query,
63131
63527
  status: "running",
@@ -63251,7 +63647,7 @@ ${lines.join("\n")}`
63251
63647
  }
63252
63648
  };
63253
63649
  }
63254
- var taskCreateParams, taskLogParams, taskDocumentWriteParams, taskDocumentReadParams, reflectOnPerformanceParams, readEvaluationsParams, updateIdentityParams, listAgentsParams, delegateTaskParams, getAgentConfigParams, updateAgentConfigParams, createAgentParams, deleteAgentParams, sendMessageParams, readMessagesParams, memorySearchParams, memoryGetParams, webFetchParams, researchRunParams, researchListParams, researchGetParams, researchCancelParams, memoryAppendParams, log10, MAX_INSTRUCTIONS_TEXT_LENGTH, MAX_MEMORY_LENGTH, MAX_SOUL_LENGTH, AGENT_MEMORY_ROOT2, AGENT_MEMORY_FILENAME2, AGENT_DREAMS_FILENAME2, agentQmdRefreshState, AGENT_QMD_REFRESH_INTERVAL_MS, DAILY_AGENT_MEMORY_RE2;
63650
+ var taskCreateParams, taskLogParams, taskDocumentWriteParams, taskDocumentReadParams, reflectOnPerformanceParams, readEvaluationsParams, updateIdentityParams, listAgentsParams, delegateTaskParams, getAgentConfigParams, updateAgentConfigParams, createAgentParams, deleteAgentParams, sendMessageParams, readMessagesParams, memorySearchParams, memoryGetParams, webFetchParams, researchRunParams, researchListParams, researchGetParams, researchCancelParams, memoryAppendParams, log10, MAX_INSTRUCTIONS_TEXT_LENGTH, MAX_MEMORY_LENGTH, MAX_SOUL_LENGTH, AGENT_MEMORY_ROOT3, AGENT_MEMORY_FILENAME2, AGENT_DREAMS_FILENAME2, agentQmdRefreshState, AGENT_QMD_REFRESH_INTERVAL_MS, DAILY_AGENT_MEMORY_RE2;
63255
63651
  var init_agent_tools = __esm({
63256
63652
  "../engine/src/agent-tools.ts"() {
63257
63653
  "use strict";
@@ -63422,7 +63818,7 @@ var init_agent_tools = __esm({
63422
63818
  MAX_INSTRUCTIONS_TEXT_LENGTH = 5e4;
63423
63819
  MAX_MEMORY_LENGTH = 5e4;
63424
63820
  MAX_SOUL_LENGTH = 1e4;
63425
- AGENT_MEMORY_ROOT2 = ".fusion/agent-memory";
63821
+ AGENT_MEMORY_ROOT3 = ".fusion/agent-memory";
63426
63822
  AGENT_MEMORY_FILENAME2 = "MEMORY.md";
63427
63823
  AGENT_DREAMS_FILENAME2 = "DREAMS.md";
63428
63824
  agentQmdRefreshState = /* @__PURE__ */ new Map();
@@ -63962,9 +64358,9 @@ var init_usage_limit_detector = __esm({
63962
64358
  });
63963
64359
 
63964
64360
  // ../engine/src/agent-instructions.ts
63965
- import { readFile as readFile14, writeFile as writeFile11, mkdir as mkdir12, access as access4 } from "node:fs/promises";
64361
+ import { readFile as readFile15, writeFile as writeFile12, mkdir as mkdir13, access as access4 } from "node:fs/promises";
63966
64362
  import { constants as fsConstants2 } from "node:fs";
63967
- import { isAbsolute as isAbsolute8, resolve as resolve16, relative as relative6, normalize as normalize3, sep as sep5, dirname as dirname10 } from "node:path";
64363
+ import { isAbsolute as isAbsolute9, resolve as resolve17, relative as relative7, normalize as normalize4, sep as sep6, dirname as dirname11 } from "node:path";
63968
64364
  function trimAndClamp(value, maxLength, label, agentId) {
63969
64365
  const trimmed = value.trim();
63970
64366
  if (!trimmed) {
@@ -63994,18 +64390,18 @@ function resolveValidatedMarkdownPath(rawPath, rootDir, agentId, fieldLabel) {
63994
64390
  log12.warn(`${fieldLabel} must end in .md for agent ${agentId}: ${trimmed}`);
63995
64391
  return null;
63996
64392
  }
63997
- if (isAbsolute8(trimmed)) {
64393
+ if (isAbsolute9(trimmed)) {
63998
64394
  log12.warn(`${fieldLabel} must be project-relative for agent ${agentId}: ${trimmed}`);
63999
64395
  return null;
64000
64396
  }
64001
- const normalized = normalize3(trimmed);
64397
+ const normalized = normalize4(trimmed);
64002
64398
  if (isPathTraversal2(normalized)) {
64003
64399
  log12.warn(`${fieldLabel} traversal is not allowed for agent ${agentId}: ${trimmed}`);
64004
64400
  return null;
64005
64401
  }
64006
- const resolvedPath = resolve16(rootDir, normalized);
64007
- const rel = relative6(rootDir, resolvedPath);
64008
- if (!rel || rel.startsWith(`..${sep5}`) || rel === ".." || isAbsolute8(rel)) {
64402
+ const resolvedPath = resolve17(rootDir, normalized);
64403
+ const rel = relative7(rootDir, resolvedPath);
64404
+ if (!rel || rel.startsWith(`..${sep6}`) || rel === ".." || isAbsolute9(rel)) {
64009
64405
  log12.warn(`${fieldLabel} escapes project root for agent ${agentId}: ${trimmed}`);
64010
64406
  return null;
64011
64407
  }
@@ -64024,7 +64420,7 @@ async function resolveAgentHeartbeatProcedure(agent, rootDir) {
64024
64420
  return null;
64025
64421
  }
64026
64422
  try {
64027
- const content = await readFile14(filePath, "utf-8");
64423
+ const content = await readFile15(filePath, "utf-8");
64028
64424
  const normalized = trimAndClamp(
64029
64425
  content,
64030
64426
  MAX_INSTRUCTIONS_TEXT_LENGTH2,
@@ -64053,8 +64449,8 @@ async function ensureDefaultHeartbeatProcedureFile(rootDir, procedurePathRel, de
64053
64449
  } catch {
64054
64450
  }
64055
64451
  try {
64056
- await mkdir12(dirname10(filePath), { recursive: true });
64057
- await writeFile11(filePath, defaultContent, "utf-8");
64452
+ await mkdir13(dirname11(filePath), { recursive: true });
64453
+ await writeFile12(filePath, defaultContent, "utf-8");
64058
64454
  log12.log(`Seeded default heartbeat procedure file at ${filePath}`);
64059
64455
  return filePath;
64060
64456
  } catch (err) {
@@ -64154,7 +64550,7 @@ async function resolveAgentInstructions(agent, rootDir, ratingSummary) {
64154
64550
  const filePath = resolveValidatedInstructionsPath(agent.instructionsPath, rootDir, agent.id);
64155
64551
  if (filePath) {
64156
64552
  try {
64157
- const content = await readFile14(filePath, "utf-8");
64553
+ const content = await readFile15(filePath, "utf-8");
64158
64554
  const normalizedContent = trimAndClamp(
64159
64555
  content,
64160
64556
  MAX_INSTRUCTIONS_TEXT_LENGTH2,
@@ -64282,6 +64678,16 @@ var init_agent_instructions = __esm({
64282
64678
  });
64283
64679
 
64284
64680
  // ../engine/src/notification/ntfy-provider.ts
64681
+ function resolveParticipantLabel(metadata, kind) {
64682
+ const nameKey = kind === "from" ? "fromName" : "toName";
64683
+ const idKey = kind === "from" ? "fromId" : "toId";
64684
+ const name = typeof metadata?.[nameKey] === "string" ? metadata[nameKey].trim() : "";
64685
+ if (name.length > 0) {
64686
+ return name;
64687
+ }
64688
+ const id = typeof metadata?.[idKey] === "string" ? metadata[idKey].trim() : "";
64689
+ return id.length > 0 ? id : kind === "from" ? "agent" : "recipient";
64690
+ }
64285
64691
  var SUPPORTED_EVENTS, NtfyNotificationProvider;
64286
64692
  var init_ntfy_provider = __esm({
64287
64693
  "../engine/src/notification/ntfy-provider.ts"() {
@@ -64348,8 +64754,8 @@ var init_ntfy_provider = __esm({
64348
64754
  };
64349
64755
  const identifier = formatTaskIdentifier(taskLike);
64350
64756
  const messageId = typeof payload.metadata?.messageId === "string" ? payload.metadata.messageId : void 0;
64351
- const senderLabel = typeof payload.metadata?.fromId === "string" ? payload.metadata.fromId : "agent";
64352
- const recipientLabel = typeof payload.metadata?.toId === "string" ? payload.metadata.toId : "recipient";
64757
+ const senderLabel = resolveParticipantLabel(payload.metadata, "from");
64758
+ const recipientLabel = resolveParticipantLabel(payload.metadata, "to");
64353
64759
  const preview = typeof payload.metadata?.preview === "string" ? payload.metadata.preview : "(no preview)";
64354
64760
  const replyToMessageId = typeof payload.metadata?.replyToMessageId === "string" ? payload.metadata.replyToMessageId : void 0;
64355
64761
  const clickUrl = buildNtfyClickUrl({
@@ -64441,6 +64847,16 @@ var init_ntfy_provider = __esm({
64441
64847
  });
64442
64848
 
64443
64849
  // ../engine/src/notification/webhook-provider.ts
64850
+ function resolveParticipantLabel2(metadata, kind) {
64851
+ const nameKey = kind === "from" ? "fromName" : "toName";
64852
+ const idKey = kind === "from" ? "fromId" : "toId";
64853
+ const name = typeof metadata?.[nameKey] === "string" ? metadata[nameKey].trim() : "";
64854
+ if (name.length > 0) {
64855
+ return name;
64856
+ }
64857
+ const id = typeof metadata?.[idKey] === "string" ? metadata[idKey].trim() : "";
64858
+ return id.length > 0 ? id : kind === "from" ? "agent" : "recipient";
64859
+ }
64444
64860
  var WebhookNotificationProvider;
64445
64861
  var init_webhook_provider = __esm({
64446
64862
  "../engine/src/notification/webhook-provider.ts"() {
@@ -64550,6 +64966,17 @@ var init_webhook_provider = __esm({
64550
64966
  return "Pipeline gridlocked";
64551
64967
  case "fallback-used":
64552
64968
  return `Fusion recovered by switching from ${String(payload.metadata?.primaryModel ?? "primary model")} to ${String(payload.metadata?.fallbackModel ?? "fallback model")} (${String(payload.metadata?.triggerPoint ?? "unknown trigger")})`;
64969
+ case "message:agent-to-user": {
64970
+ const from = resolveParticipantLabel2(payload.metadata, "from");
64971
+ const preview = typeof payload.metadata?.preview === "string" ? payload.metadata.preview : "(no preview)";
64972
+ return `From: ${from} \u2192 You: ${preview}`;
64973
+ }
64974
+ case "message:agent-to-agent": {
64975
+ const from = resolveParticipantLabel2(payload.metadata, "from");
64976
+ const to = resolveParticipantLabel2(payload.metadata, "to");
64977
+ const preview = typeof payload.metadata?.preview === "string" ? payload.metadata.preview : "(no preview)";
64978
+ return `From: ${from} \u2192 To: ${to}: ${preview}`;
64979
+ }
64553
64980
  default:
64554
64981
  return `Event "${event}" for task ${identifier}`;
64555
64982
  }
@@ -64573,6 +65000,8 @@ var init_webhook_provider = __esm({
64573
65000
  return { content: message };
64574
65001
  }
64575
65002
  const messageId = typeof payload.metadata?.messageId === "string" ? payload.metadata.messageId : void 0;
65003
+ const fromLabel = resolveParticipantLabel2(payload.metadata, "from");
65004
+ const toLabel = resolveParticipantLabel2(payload.metadata, "to");
64576
65005
  return {
64577
65006
  event: payload.event,
64578
65007
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -64580,7 +65009,13 @@ var init_webhook_provider = __esm({
64580
65009
  id: payload.taskId,
64581
65010
  title: payload.taskTitle
64582
65011
  },
64583
- metadata: payload.metadata,
65012
+ metadata: {
65013
+ ...payload.metadata,
65014
+ ...payload.event === "message:agent-to-user" || payload.event === "message:agent-to-agent" ? {
65015
+ fromName: typeof payload.metadata?.fromName === "string" ? payload.metadata.fromName : fromLabel,
65016
+ toName: typeof payload.metadata?.toName === "string" ? payload.metadata.toName : toLabel
65017
+ } : {}
65018
+ },
64584
65019
  clickUrl: buildNtfyClickUrl({
64585
65020
  dashboardHost: this.config.dashboardHost,
64586
65021
  projectId: this.config.projectId,
@@ -64785,6 +65220,8 @@ var init_notification_service = __esm({
64785
65220
  }
64786
65221
  const preview = message.content.length > 100 ? `${message.content.slice(0, 100)}\u2026` : message.content;
64787
65222
  const taskId = typeof message.metadata?.taskId === "string" ? message.metadata.taskId : void 0;
65223
+ const fromName = await this.resolveAgentName(message.fromType, message.fromId, "from");
65224
+ const toName = await this.resolveAgentName(message.toType, message.toId, "to");
64788
65225
  this.maybeNotify(message.id, eventType, {
64789
65226
  taskId,
64790
65227
  taskTitle: void 0,
@@ -64793,8 +65230,10 @@ var init_notification_service = __esm({
64793
65230
  messageId: message.id,
64794
65231
  fromId: message.fromId,
64795
65232
  fromType: message.fromType,
65233
+ ...fromName ? { fromName } : {},
64796
65234
  toId: message.toId,
64797
65235
  toType: message.toType,
65236
+ ...toName ? { toName } : {},
64798
65237
  type: message.type,
64799
65238
  replyToMessageId: message.metadata?.replyTo?.messageId,
64800
65239
  preview
@@ -64804,6 +65243,26 @@ var init_notification_service = __esm({
64804
65243
  `NotificationService.handleMessageSent scheduled eventType=${eventType} messageId=${message.id}`
64805
65244
  );
64806
65245
  }
65246
+ async resolveAgentName(participantType, participantId, direction) {
65247
+ if (participantType !== "agent") {
65248
+ return null;
65249
+ }
65250
+ const resolver = this.options.agentNameResolver;
65251
+ if (!resolver) {
65252
+ return null;
65253
+ }
65254
+ try {
65255
+ const resolved = await resolver(participantId);
65256
+ const trimmed = typeof resolved === "string" ? resolved.trim() : "";
65257
+ return trimmed.length > 0 ? trimmed : null;
65258
+ } catch (error) {
65259
+ const message = error instanceof Error ? error.message : String(error);
65260
+ schedulerLog.log(
65261
+ `NotificationService.handleMessageSent failed to resolve ${direction} agent name agentId=${participantId} error=${message}`
65262
+ );
65263
+ return null;
65264
+ }
65265
+ }
64807
65266
  setNotificationsEnabledFromSettings(settings) {
64808
65267
  this.notificationsEnabled = Boolean(
64809
65268
  settings.ntfyEnabled && settings.ntfyTopic || settings.webhookEnabled && settings.webhookUrl
@@ -65006,7 +65465,8 @@ var init_notifier = __esm({
65006
65465
  this.projectId = options.projectId;
65007
65466
  this.notificationService = notificationService ?? new NotificationService(store, {
65008
65467
  projectId: this.projectId,
65009
- ntfyBaseUrl: options.ntfyBaseUrl
65468
+ ntfyBaseUrl: options.ntfyBaseUrl,
65469
+ agentNameResolver: options.agentNameResolver
65010
65470
  });
65011
65471
  activeNotificationService = this.notificationService;
65012
65472
  }
@@ -65759,20 +66219,20 @@ async function withRateLimitRetry(fn, options = {}) {
65759
66219
  throw lastError ?? new Error("withRateLimitRetry: unexpected state");
65760
66220
  }
65761
66221
  function sleep2(ms, signal) {
65762
- return new Promise((resolve23, reject) => {
66222
+ return new Promise((resolve24, reject) => {
65763
66223
  if (signal?.aborted) {
65764
66224
  reject(signal.reason ?? new Error("Aborted"));
65765
66225
  return;
65766
66226
  }
65767
- const timer = setTimeout(resolve23, ms);
66227
+ const timer = setTimeout(resolve24, ms);
65768
66228
  if (signal) {
65769
66229
  const onAbort = () => {
65770
66230
  clearTimeout(timer);
65771
66231
  reject(signal.reason ?? new Error("Aborted"));
65772
66232
  };
65773
66233
  signal.addEventListener("abort", onAbort, { once: true });
65774
- const origResolve = resolve23;
65775
- resolve23 = () => {
66234
+ const origResolve = resolve24;
66235
+ resolve24 = () => {
65776
66236
  signal.removeEventListener("abort", onAbort);
65777
66237
  origResolve();
65778
66238
  };
@@ -65861,8 +66321,8 @@ If research is disabled or providers are not configured, use the actionable tool
65861
66321
 
65862
66322
  // ../engine/src/triage.ts
65863
66323
  import { Type as Type2 } from "@mariozechner/pi-ai";
65864
- import { readFile as readFile15 } from "node:fs/promises";
65865
- import { join as join30 } from "node:path";
66324
+ import { readFile as readFile16 } from "node:fs/promises";
66325
+ import { join as join31 } from "node:path";
65866
66326
  function extractPromptDeclaredTitle(prompt, taskId) {
65867
66327
  const headingMatch = prompt.match(/^#\s+Task:\s+([A-Z]+-\d+)\s+-\s+(.+)$/m);
65868
66328
  if (!headingMatch) return null;
@@ -65890,10 +66350,10 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
65890
66350
  if (!attachments || attachments.length === 0) {
65891
66351
  return { attachmentContents, imageContents };
65892
66352
  }
65893
- const { readFile: readFile22 } = await import("node:fs/promises");
65894
- const { join: join46 } = await import("node:path");
66353
+ const { readFile: readFile23 } = await import("node:fs/promises");
66354
+ const { join: join47 } = await import("node:path");
65895
66355
  for (const att of attachments) {
65896
- const filePath = join46(
66356
+ const filePath = join47(
65897
66357
  rootDir,
65898
66358
  ".fusion",
65899
66359
  "tasks",
@@ -65903,7 +66363,7 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
65903
66363
  );
65904
66364
  try {
65905
66365
  if (IMAGE_MIME_TYPES.has(att.mimeType)) {
65906
- const data = await readFile22(filePath);
66366
+ const data = await readFile23(filePath);
65907
66367
  imageContents.push({
65908
66368
  type: "image",
65909
66369
  data: data.toString("base64"),
@@ -65915,7 +66375,7 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
65915
66375
  text: null
65916
66376
  });
65917
66377
  } else {
65918
- const data = await readFile22(filePath, "utf-8");
66378
+ const data = await readFile23(filePath, "utf-8");
65919
66379
  const text = data.length > TEXT_INLINE_LIMIT ? data.slice(0, TEXT_INLINE_LIMIT) + "\n... (truncated at 50KB)" : data;
65920
66380
  attachmentContents.push({
65921
66381
  originalName: att.originalName,
@@ -66736,8 +67196,8 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
66736
67196
  return false;
66737
67197
  }
66738
67198
  const settings = await this.store.getSettings();
66739
- const promptPath = join30(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md");
66740
- const written = await readFile15(promptPath, "utf-8").catch((err) => {
67199
+ const promptPath = join31(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md");
67200
+ const written = await readFile16(promptPath, "utf-8").catch((err) => {
66741
67201
  const msg = err instanceof Error ? err.message : String(err);
66742
67202
  planLog.warn(`${task.id}: failed to read PROMPT.md during approved-spec recovery (${promptPath}): ${msg}`);
66743
67203
  return "";
@@ -67244,8 +67704,8 @@ ${triagePluginContributions}` : triageSystemPrompt;
67244
67704
  await this.store.updateTask(task.id, { status: "needs-replan" });
67245
67705
  return;
67246
67706
  }
67247
- const written = await readFile15(
67248
- join30(this.rootDir, promptPath),
67707
+ const written = await readFile16(
67708
+ join31(this.rootDir, promptPath),
67249
67709
  "utf-8"
67250
67710
  ).catch((err) => {
67251
67711
  const msg = err instanceof Error ? err.message : String(err);
@@ -67563,10 +68023,10 @@ Remove or replace these ids and call fn_task_create again.`
67563
68023
  checkpointRef.current = sessionRef.current.sessionManager.getLeafId() ?? null;
67564
68024
  }
67565
68025
  try {
67566
- const { readFile: readFile22 } = await import("node:fs/promises");
67567
- const { join: join46 } = await import("node:path");
67568
- const promptContent = await readFile22(
67569
- join46(rootDir, promptPath),
68026
+ const { readFile: readFile23 } = await import("node:fs/promises");
68027
+ const { join: join47 } = await import("node:path");
68028
+ const promptContent = await readFile23(
68029
+ join47(rootDir, promptPath),
67570
68030
  "utf-8"
67571
68031
  ).catch((err) => {
67572
68032
  const msg = err instanceof Error ? err.message : String(err);
@@ -67797,7 +68257,7 @@ Take a completely different approach to writing this specification. Do NOT repea
67797
68257
  // ../engine/src/verification-utils.ts
67798
68258
  import { spawn as spawn3 } from "node:child_process";
67799
68259
  async function execWithProcessGroup(command, options) {
67800
- return new Promise((resolve23, reject) => {
68260
+ return new Promise((resolve24, reject) => {
67801
68261
  if (options.signal?.aborted) {
67802
68262
  reject(Object.assign(
67803
68263
  new Error(`Command aborted before start: ${command}`),
@@ -67811,7 +68271,13 @@ async function execWithProcessGroup(command, options) {
67811
68271
  shell: true,
67812
68272
  detached: useProcessGroup,
67813
68273
  stdio: ["ignore", "pipe", "pipe"],
67814
- ...options.env !== void 0 && { env: { ...process.env, ...options.env } }
68274
+ env: {
68275
+ ...process.env,
68276
+ // Corepack otherwise prompts interactively before fetching a pinned
68277
+ // packageManager version, hanging the non-TTY child until timeout.
68278
+ COREPACK_ENABLE_DOWNLOAD_PROMPT: "0",
68279
+ ...options.env ?? {}
68280
+ }
67815
68281
  });
67816
68282
  let stdout = "";
67817
68283
  let stderr = "";
@@ -67891,7 +68357,7 @@ async function execWithProcessGroup(command, options) {
67891
68357
  return;
67892
68358
  }
67893
68359
  if (code === 0) {
67894
- resolve23({ stdout, stderr, bufferOverflow: stdoutOverflow || stderrOverflow });
68360
+ resolve24({ stdout, stderr, bufferOverflow: stdoutOverflow || stderrOverflow });
67895
68361
  return;
67896
68362
  }
67897
68363
  reject(Object.assign(
@@ -68286,9 +68752,9 @@ var init_run_audit = __esm({
68286
68752
  // ../engine/src/merger.ts
68287
68753
  import { execSync, exec as exec3, execFile as execFile3 } from "node:child_process";
68288
68754
  import { promisify as promisify4 } from "node:util";
68289
- import { existsSync as existsSync24, readFileSync as readFileSync11, writeFileSync as writeFileSync2, unlinkSync, renameSync as renameSync2 } from "node:fs";
68755
+ import { existsSync as existsSync25, readFileSync as readFileSync11, writeFileSync as writeFileSync2, unlinkSync, renameSync as renameSync2 } from "node:fs";
68290
68756
  import { createHash as createHash6 } from "node:crypto";
68291
- import { join as join31 } from "node:path";
68757
+ import { join as join32 } from "node:path";
68292
68758
  import { hostname } from "node:os";
68293
68759
  import { Type as Type3 } from "typebox";
68294
68760
  function truncateWorkflowScriptOutput(output) {
@@ -68334,7 +68800,7 @@ async function getStagedFiles(cwd) {
68334
68800
  }
68335
68801
  }
68336
68802
  function hasInstallState(rootDir) {
68337
- return existsSync24(join31(rootDir, "node_modules")) || existsSync24(join31(rootDir, ".pnp.cjs"));
68803
+ return existsSync25(join32(rootDir, "node_modules")) || existsSync25(join32(rootDir, ".pnp.cjs"));
68338
68804
  }
68339
68805
  function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
68340
68806
  if (!installStatePresent) return true;
@@ -68343,18 +68809,18 @@ function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
68343
68809
  );
68344
68810
  }
68345
68811
  function getDependencySyncCommand(rootDir) {
68346
- if (existsSync24(join31(rootDir, "pnpm-lock.yaml"))) return "pnpm install --frozen-lockfile";
68347
- if (existsSync24(join31(rootDir, "package-lock.json"))) return "npm install";
68348
- if (existsSync24(join31(rootDir, "yarn.lock"))) return "yarn install --frozen-lockfile";
68349
- if (existsSync24(join31(rootDir, "bun.lock")) || existsSync24(join31(rootDir, "bun.lockb"))) {
68812
+ if (existsSync25(join32(rootDir, "pnpm-lock.yaml"))) return "pnpm install --frozen-lockfile";
68813
+ if (existsSync25(join32(rootDir, "package-lock.json"))) return "npm install";
68814
+ if (existsSync25(join32(rootDir, "yarn.lock"))) return "yarn install --frozen-lockfile";
68815
+ if (existsSync25(join32(rootDir, "bun.lock")) || existsSync25(join32(rootDir, "bun.lockb"))) {
68350
68816
  return "bun install --frozen-lockfile";
68351
68817
  }
68352
68818
  return null;
68353
68819
  }
68354
68820
  function computeLockfileHash(rootDir) {
68355
68821
  for (const name of LOCKFILE_CANDIDATES) {
68356
- const p = join31(rootDir, name);
68357
- if (existsSync24(p)) {
68822
+ const p = join32(rootDir, name);
68823
+ if (existsSync25(p)) {
68358
68824
  try {
68359
68825
  return createHash6("sha256").update(readFileSync11(p)).digest("hex");
68360
68826
  } catch {
@@ -68366,7 +68832,7 @@ function computeLockfileHash(rootDir) {
68366
68832
  }
68367
68833
  function readInstallMarker(rootDir) {
68368
68834
  try {
68369
- const value = readFileSync11(join31(rootDir, INSTALL_MARKER_RELPATH), "utf-8").trim();
68835
+ const value = readFileSync11(join32(rootDir, INSTALL_MARKER_RELPATH), "utf-8").trim();
68370
68836
  return value || null;
68371
68837
  } catch {
68372
68838
  return null;
@@ -68374,7 +68840,7 @@ function readInstallMarker(rootDir) {
68374
68840
  }
68375
68841
  function writeInstallMarker(rootDir, hash) {
68376
68842
  try {
68377
- writeFileSync2(join31(rootDir, INSTALL_MARKER_RELPATH), hash);
68843
+ writeFileSync2(join32(rootDir, INSTALL_MARKER_RELPATH), hash);
68378
68844
  } catch {
68379
68845
  }
68380
68846
  }
@@ -68466,8 +68932,8 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
68466
68932
  buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
68467
68933
  };
68468
68934
  }
68469
- if (existsSync24(join31(rootDir, "pnpm-lock.yaml"))) {
68470
- if (existsSync24(join31(rootDir, "pnpm-workspace.yaml"))) {
68935
+ if (existsSync25(join32(rootDir, "pnpm-lock.yaml"))) {
68936
+ if (existsSync25(join32(rootDir, "pnpm-workspace.yaml"))) {
68471
68937
  mergerLog.warn(
68472
68938
  `Inferred test command "pnpm test" in a pnpm workspace (${rootDir}). This runs the full monorepo suite on every merge. Consider setting an explicit scoped testCommand in project settings, e.g. \`pnpm -r --filter "...[main]" test\`.`
68473
68939
  );
@@ -68478,21 +68944,21 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
68478
68944
  buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
68479
68945
  };
68480
68946
  }
68481
- if (existsSync24(join31(rootDir, "yarn.lock"))) {
68947
+ if (existsSync25(join32(rootDir, "yarn.lock"))) {
68482
68948
  return {
68483
68949
  command: "yarn test",
68484
68950
  testSource: "inferred",
68485
68951
  buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
68486
68952
  };
68487
68953
  }
68488
- if (existsSync24(join31(rootDir, "bun.lock")) || existsSync24(join31(rootDir, "bun.lockb"))) {
68954
+ if (existsSync25(join32(rootDir, "bun.lock")) || existsSync25(join32(rootDir, "bun.lockb"))) {
68489
68955
  return {
68490
68956
  command: "bun test",
68491
68957
  testSource: "inferred",
68492
68958
  buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
68493
68959
  };
68494
68960
  }
68495
- if (existsSync24(join31(rootDir, "package-lock.json"))) {
68961
+ if (existsSync25(join32(rootDir, "package-lock.json"))) {
68496
68962
  return {
68497
68963
  command: "npm test",
68498
68964
  testSource: "inferred",
@@ -68931,7 +69397,7 @@ async function stashTreesEqual(rootDir, aSha, bSha) {
68931
69397
  }
68932
69398
  function writeActiveMergerStatus(rootDir, taskId) {
68933
69399
  try {
68934
- const statusPath = join31(rootDir, ".git", ACTIVE_MERGER_STATUS_FILENAME);
69400
+ const statusPath = join32(rootDir, ".git", ACTIVE_MERGER_STATUS_FILENAME);
68935
69401
  const payload = {
68936
69402
  taskId,
68937
69403
  pid: process.pid,
@@ -69362,8 +69828,8 @@ async function findFilesWithConflictMarkers(rootDir, files) {
69362
69828
  const stillConflicted = [];
69363
69829
  for (const file of files) {
69364
69830
  try {
69365
- const fullPath = join31(rootDir, file);
69366
- if (!existsSync24(fullPath)) continue;
69831
+ const fullPath = join32(rootDir, file);
69832
+ if (!existsSync25(fullPath)) continue;
69367
69833
  const { stdout } = await execAsync2(
69368
69834
  `git grep -l -e "^<<<<<<< " -e "^=======$" -e "^>>>>>>> " --no-index -- ${quoteArg(fullPath)}`,
69369
69835
  { cwd: rootDir, encoding: "utf-8" }
@@ -69389,7 +69855,7 @@ async function tryRecoverHardFailApply(params) {
69389
69855
  if (!patchText.trim()) {
69390
69856
  mergerLog.warn(`${taskId}: autostash ${sha.slice(0, 7)} produced empty patch; cannot 3-way recover`);
69391
69857
  } else {
69392
- const patchPath = join31(rootDir, ".git", `fusion-autostash-${sha.slice(0, 7)}.patch`);
69858
+ const patchPath = join32(rootDir, ".git", `fusion-autostash-${sha.slice(0, 7)}.patch`);
69393
69859
  writeFileSync2(patchPath, patchText, "utf-8");
69394
69860
  try {
69395
69861
  await execAsync2(`git apply --3way --whitespace=nowarn ${quoteArg(patchPath)}`, { cwd: rootDir });
@@ -70226,16 +70692,46 @@ async function resolveTaskDiffBaseRef({
70226
70692
  return mergeBase;
70227
70693
  }
70228
70694
  }
70695
+ let recoveredBase;
70696
+ if (!baseBranch?.trim()) {
70697
+ try {
70698
+ const { stdout } = await execAsync2(`git merge-base ${quotedHeadRef} main`, {
70699
+ cwd,
70700
+ encoding: "utf-8"
70701
+ });
70702
+ recoveredBase = stdout.trim() || void 0;
70703
+ } catch {
70704
+ try {
70705
+ const { stdout } = await execAsync2(`git merge-base ${quotedHeadRef} ${quoteArg("origin/main")}`, {
70706
+ cwd,
70707
+ encoding: "utf-8"
70708
+ });
70709
+ recoveredBase = stdout.trim() || void 0;
70710
+ } catch {
70711
+ }
70712
+ }
70713
+ }
70229
70714
  if (baseCommitSha) {
70230
70715
  try {
70231
70716
  await execAsync2(`git merge-base --is-ancestor ${quoteArg(baseCommitSha)} ${quotedHeadRef}`, {
70232
70717
  cwd,
70233
70718
  encoding: "utf-8"
70234
70719
  });
70720
+ if (recoveredBase && recoveredBase !== baseCommitSha) {
70721
+ try {
70722
+ await execAsync2(`git merge-base --is-ancestor ${quoteArg(baseCommitSha)} ${quoteArg(recoveredBase)}`, {
70723
+ cwd,
70724
+ encoding: "utf-8"
70725
+ });
70726
+ return recoveredBase;
70727
+ } catch {
70728
+ }
70729
+ }
70235
70730
  return baseCommitSha;
70236
70731
  } catch {
70237
70732
  }
70238
70733
  }
70734
+ if (recoveredBase) return recoveredBase;
70239
70735
  try {
70240
70736
  const { stdout } = await execAsync2(`git rev-parse ${quoteArg(`${headRef}~1`)}`, {
70241
70737
  cwd,
@@ -70882,7 +71378,7 @@ async function pushToRemoteAfterMerge(store, rootDir, taskId, settings, options)
70882
71378
  }
70883
71379
  async function createPostMergeWorktree(rootDir, taskId) {
70884
71380
  const randomSuffix = Math.random().toString(36).slice(2, 10);
70885
- const postMergeWorktree = join31(rootDir, ".worktrees", `post-merge-${taskId}-${randomSuffix}`);
71381
+ const postMergeWorktree = join32(rootDir, ".worktrees", `post-merge-${taskId}-${randomSuffix}`);
70886
71382
  try {
70887
71383
  await execAsync2(`git worktree add ${quoteArg(postMergeWorktree)} HEAD`, { cwd: rootDir });
70888
71384
  return postMergeWorktree;
@@ -71460,7 +71956,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
71460
71956
  }
71461
71957
  if (error.name === "VerificationError") {
71462
71958
  const verificationErr = error;
71463
- const maxFixRetries = Math.min(settings.verificationFixRetries ?? 3, 3);
71959
+ const maxFixRetries = Math.min(settings.verificationFixRetries ?? 2, 3);
71464
71960
  if (maxFixRetries > 0 && (verificationErr.verificationResult.testResult || verificationErr.verificationResult.buildResult)) {
71465
71961
  mergerLog.log(`${taskId}: deterministic verification failed \u2014 attempting in-merge fix (up to ${maxFixRetries} attempts)`);
71466
71962
  await store.logEntry(taskId, `Verification failed during merge \u2014 attempting in-merge fix (up to ${maxFixRetries} attempts)`);
@@ -71568,7 +72064,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
71568
72064
  throw error;
71569
72065
  }
71570
72066
  if (error.message?.includes("Build verification failed")) {
71571
- const maxFixRetries = Math.min(settings.verificationFixRetries ?? 3, 3);
72067
+ const maxFixRetries = Math.min(settings.verificationFixRetries ?? 2, 3);
71572
72068
  if (maxFixRetries > 0 && (effectiveTestCommand || effectiveBuildCommand)) {
71573
72069
  mergerLog.log(`${taskId}: build verification failed \u2014 attempting in-merge fix`);
71574
72070
  await store.logEntry(taskId, `Build verification failed during merge \u2014 attempting in-merge fix`);
@@ -71857,7 +72353,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
71857
72353
  }
71858
72354
  }
71859
72355
  throwIfAborted(options.signal, taskId);
71860
- if (worktreePath && existsSync24(worktreePath)) {
72356
+ if (worktreePath && existsSync25(worktreePath)) {
71861
72357
  const otherUser = await findWorktreeUser(store, worktreePath, taskId);
71862
72358
  if (otherUser) {
71863
72359
  mergerLog.log(`Worktree retained \u2014 still needed by ${otherUser}`);
@@ -73092,7 +73588,7 @@ var init_merger = __esm({
73092
73588
  PUSH_TIMEOUT_MS = 6e4;
73093
73589
  MERGE_COMMIT_LOG_MAX_CHARS = 5e3;
73094
73590
  MERGE_DIFF_STAT_MAX_CHARS = 3e3;
73095
- INSTALL_MARKER_RELPATH = join31("node_modules", ".fusion-install-marker");
73591
+ INSTALL_MARKER_RELPATH = join32("node_modules", ".fusion-install-marker");
73096
73592
  LOCKFILE_CANDIDATES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb", "bun.lock"];
73097
73593
  VerificationError = class extends Error {
73098
73594
  constructor(message, verificationResult) {
@@ -73122,8 +73618,8 @@ var init_merger = __esm({
73122
73618
 
73123
73619
  // ../engine/src/worktree-names.ts
73124
73620
  import { readdirSync as readdirSync3 } from "node:fs";
73125
- import { join as join32 } from "node:path";
73126
- import { existsSync as existsSync25 } from "node:fs";
73621
+ import { join as join33 } from "node:path";
73622
+ import { existsSync as existsSync26 } from "node:fs";
73127
73623
  function slugify2(str) {
73128
73624
  return str.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
73129
73625
  }
@@ -73134,7 +73630,7 @@ function generateReservedWorktreeName(rootDir, reservedNames = /* @__PURE__ */ n
73134
73630
  const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
73135
73631
  const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
73136
73632
  const baseName = `${adjective}-${noun}`;
73137
- const worktreesDir = join32(rootDir, ".worktrees");
73633
+ const worktreesDir = join33(rootDir, ".worktrees");
73138
73634
  const existing = getExistingWorktreeNames(worktreesDir);
73139
73635
  for (const reserved of reservedNames) {
73140
73636
  existing.add(reserved);
@@ -73168,10 +73664,10 @@ function planTaskWorktreePath(task, rootDir, naming, reservedNames) {
73168
73664
  break;
73169
73665
  }
73170
73666
  reservedNames.add(worktreeName);
73171
- return join32(rootDir, ".worktrees", worktreeName);
73667
+ return join33(rootDir, ".worktrees", worktreeName);
73172
73668
  }
73173
73669
  function getExistingWorktreeNames(worktreesDir) {
73174
- if (!existsSync25(worktreesDir)) {
73670
+ if (!existsSync26(worktreesDir)) {
73175
73671
  return /* @__PURE__ */ new Set();
73176
73672
  }
73177
73673
  try {
@@ -73308,8 +73804,8 @@ __export(worktree_pool_exports, {
73308
73804
  });
73309
73805
  import { exec as exec4 } from "node:child_process";
73310
73806
  import { promisify as promisify5 } from "node:util";
73311
- import { existsSync as existsSync26, lstatSync, readdirSync as readdirSync4, rmSync as rmSync2 } from "node:fs";
73312
- import { join as join33, relative as relative7, resolve as resolve17, isAbsolute as isAbsolute9 } from "node:path";
73807
+ import { existsSync as existsSync27, lstatSync, readdirSync as readdirSync4, rmSync as rmSync2 } from "node:fs";
73808
+ import { join as join34, relative as relative8, resolve as resolve18, isAbsolute as isAbsolute10 } from "node:path";
73313
73809
  function getExecStdout(result) {
73314
73810
  if (typeof result === "string") return result;
73315
73811
  if (result && typeof result === "object" && "stdout" in result) {
@@ -73341,7 +73837,7 @@ async function getRegisteredWorktreePaths(rootDir) {
73341
73837
  const paths = /* @__PURE__ */ new Set();
73342
73838
  for (const line of stdout.split("\n")) {
73343
73839
  if (line.startsWith("worktree ")) {
73344
- paths.add(resolve17(line.slice("worktree ".length)));
73840
+ paths.add(resolve18(line.slice("worktree ".length)));
73345
73841
  }
73346
73842
  }
73347
73843
  return paths;
@@ -73352,29 +73848,29 @@ async function getRegisteredWorktreePaths(rootDir) {
73352
73848
  }
73353
73849
  }
73354
73850
  async function isRegisteredGitWorktree2(rootDir, worktreePath) {
73355
- return (await getRegisteredWorktreePaths(rootDir)).has(resolve17(worktreePath));
73851
+ return (await getRegisteredWorktreePaths(rootDir)).has(resolve18(worktreePath));
73356
73852
  }
73357
73853
  function hasRequiredWorktreeFiles(worktreePath) {
73358
- return existsSync26(join33(worktreePath, ".git")) && existsSync26(join33(worktreePath, "package.json"));
73854
+ return existsSync27(join34(worktreePath, ".git")) && existsSync27(join34(worktreePath, "package.json"));
73359
73855
  }
73360
73856
  async function isUsableTaskWorktree(rootDir, worktreePath) {
73361
- return existsSync26(worktreePath) && await isRegisteredGitWorktree2(rootDir, worktreePath) && hasRequiredWorktreeFiles(worktreePath);
73857
+ return existsSync27(worktreePath) && await isRegisteredGitWorktree2(rootDir, worktreePath) && hasRequiredWorktreeFiles(worktreePath);
73362
73858
  }
73363
73859
  function isInsideWorktreesDir(rootDir, worktreePath) {
73364
- const worktreesDir = resolve17(rootDir, ".worktrees");
73365
- const target = resolve17(worktreePath);
73366
- const rel = relative7(worktreesDir, target);
73367
- return rel !== "" && !rel.startsWith("..") && !isAbsolute9(rel);
73860
+ const worktreesDir = resolve18(rootDir, ".worktrees");
73861
+ const target = resolve18(worktreePath);
73862
+ const rel = relative8(worktreesDir, target);
73863
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute10(rel);
73368
73864
  }
73369
73865
  async function scanIdleWorktrees(rootDir, store) {
73370
- const worktreesDir = join33(rootDir, ".worktrees");
73371
- if (!existsSync26(worktreesDir)) {
73866
+ const worktreesDir = join34(rootDir, ".worktrees");
73867
+ if (!existsSync27(worktreesDir)) {
73372
73868
  return [];
73373
73869
  }
73374
73870
  let dirs;
73375
73871
  try {
73376
73872
  const entries = readdirSync4(worktreesDir, { withFileTypes: true });
73377
- dirs = entries.filter((e) => e.isDirectory()).map((e) => join33(worktreesDir, e.name));
73873
+ dirs = entries.filter((e) => e.isDirectory()).map((e) => join34(worktreesDir, e.name));
73378
73874
  } catch (err) {
73379
73875
  const errorMessage = err instanceof Error ? err.message : String(err);
73380
73876
  worktreePoolLog.warn(`Failed to read .worktrees/ directory: ${errorMessage}`);
@@ -73384,41 +73880,41 @@ async function scanIdleWorktrees(rootDir, store) {
73384
73880
  return [];
73385
73881
  }
73386
73882
  const registeredWorktrees = await getRegisteredWorktreePaths(rootDir);
73387
- const registeredDirs = dirs.filter((dir) => registeredWorktrees.has(resolve17(dir)));
73883
+ const registeredDirs = dirs.filter((dir) => registeredWorktrees.has(resolve18(dir)));
73388
73884
  const tasks = await store.listTasks({ slim: true, includeArchived: false });
73389
73885
  const activeWorktrees = /* @__PURE__ */ new Set();
73390
73886
  for (const task of tasks) {
73391
- if (task.worktree && task.column !== "done" && registeredWorktrees.has(resolve17(task.worktree))) {
73392
- activeWorktrees.add(resolve17(task.worktree));
73887
+ if (task.worktree && task.column !== "done" && registeredWorktrees.has(resolve18(task.worktree))) {
73888
+ activeWorktrees.add(resolve18(task.worktree));
73393
73889
  } else if (task.worktree && task.column !== "done") {
73394
73890
  worktreePoolLog.log(`Ignoring task ${task.id} worktree metadata because it is not a registered git worktree: ${task.worktree}`);
73395
73891
  }
73396
73892
  }
73397
- return registeredDirs.filter((dir) => !activeWorktrees.has(resolve17(dir)));
73893
+ return registeredDirs.filter((dir) => !activeWorktrees.has(resolve18(dir)));
73398
73894
  }
73399
73895
  async function cleanupOrphanedWorktrees(rootDir, store) {
73400
- const worktreesDir = join33(rootDir, ".worktrees");
73401
- if (!existsSync26(worktreesDir)) {
73896
+ const worktreesDir = join34(rootDir, ".worktrees");
73897
+ if (!existsSync27(worktreesDir)) {
73402
73898
  return 0;
73403
73899
  }
73404
73900
  const orphaned = await scanIdleWorktrees(rootDir, store);
73405
73901
  const registeredWorktrees = await getRegisteredWorktreePaths(rootDir);
73406
73902
  let dirs = [];
73407
- if (existsSync26(worktreesDir)) {
73903
+ if (existsSync27(worktreesDir)) {
73408
73904
  try {
73409
- dirs = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join33(worktreesDir, e.name));
73905
+ dirs = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join34(worktreesDir, e.name));
73410
73906
  } catch (err) {
73411
73907
  const errorMessage = err instanceof Error ? err.message : String(err);
73412
73908
  worktreePoolLog.warn(`Failed to read .worktrees/ directory for cleanup: ${errorMessage}`);
73413
73909
  dirs = [];
73414
73910
  }
73415
73911
  }
73416
- const unregistered = dirs.filter((dir) => !registeredWorktrees.has(resolve17(dir)));
73912
+ const unregistered = dirs.filter((dir) => !registeredWorktrees.has(resolve18(dir)));
73417
73913
  const candidates = [...orphaned, ...unregistered];
73418
73914
  let cleaned = 0;
73419
73915
  for (const worktreePath of candidates) {
73420
73916
  try {
73421
- if (registeredWorktrees.has(resolve17(worktreePath))) {
73917
+ if (registeredWorktrees.has(resolve18(worktreePath))) {
73422
73918
  await execAsync3(`git worktree remove "${worktreePath}" --force`, {
73423
73919
  cwd: rootDir
73424
73920
  });
@@ -73438,8 +73934,8 @@ async function cleanupOrphanedWorktrees(rootDir, store) {
73438
73934
  return cleaned;
73439
73935
  }
73440
73936
  async function reapOrphanWorktrees(projectRoot) {
73441
- const worktreesDir = join33(projectRoot, ".worktrees");
73442
- if (!existsSync26(worktreesDir)) {
73937
+ const worktreesDir = join34(projectRoot, ".worktrees");
73938
+ if (!existsSync27(worktreesDir)) {
73443
73939
  return 0;
73444
73940
  }
73445
73941
  let entries;
@@ -73447,11 +73943,11 @@ async function reapOrphanWorktrees(projectRoot) {
73447
73943
  entries = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => {
73448
73944
  if (!e.isDirectory()) return false;
73449
73945
  try {
73450
- return lstatSync(join33(worktreesDir, e.name)).isDirectory() && !lstatSync(join33(worktreesDir, e.name)).isSymbolicLink();
73946
+ return lstatSync(join34(worktreesDir, e.name)).isDirectory() && !lstatSync(join34(worktreesDir, e.name)).isSymbolicLink();
73451
73947
  } catch {
73452
73948
  return false;
73453
73949
  }
73454
- }).map((e) => ({ name: e.name, fullPath: join33(worktreesDir, e.name) }));
73950
+ }).map((e) => ({ name: e.name, fullPath: join34(worktreesDir, e.name) }));
73455
73951
  } catch (err) {
73456
73952
  const msg = err instanceof Error ? err.message : String(err);
73457
73953
  worktreePoolLog.warn(`reapOrphanWorktrees: failed to read .worktrees/ \u2014 ${msg}`);
@@ -73461,17 +73957,17 @@ async function reapOrphanWorktrees(projectRoot) {
73461
73957
  const registered = await getRegisteredWorktreePaths(projectRoot);
73462
73958
  let removed = 0;
73463
73959
  for (const { name, fullPath } of entries) {
73464
- const resolvedFull = resolve17(fullPath);
73465
- const rel = relative7(resolve17(worktreesDir), resolvedFull);
73466
- if (!rel || rel.startsWith("..") || isAbsolute9(rel)) {
73960
+ const resolvedFull = resolve18(fullPath);
73961
+ const rel = relative8(resolve18(worktreesDir), resolvedFull);
73962
+ if (!rel || rel.startsWith("..") || isAbsolute10(rel)) {
73467
73963
  worktreePoolLog.warn(`reapOrphanWorktrees: skipping out-of-bounds path ${fullPath}`);
73468
73964
  continue;
73469
73965
  }
73470
73966
  if (registered.has(resolvedFull)) {
73471
73967
  continue;
73472
73968
  }
73473
- const dotGit = join33(resolvedFull, ".git");
73474
- if (existsSync26(dotGit)) {
73969
+ const dotGit = join34(resolvedFull, ".git");
73970
+ if (existsSync27(dotGit)) {
73475
73971
  worktreePoolLog.log(`reapOrphanWorktrees: skipping ${name} (has .git entry but not in registered list \u2014 may be partially registered)`);
73476
73972
  continue;
73477
73973
  }
@@ -73531,7 +74027,7 @@ var init_worktree_pool = __esm({
73531
74027
  acquire() {
73532
74028
  for (const path2 of this.idle) {
73533
74029
  this.idle.delete(path2);
73534
- if (existsSync26(path2)) {
74030
+ if (existsSync27(path2)) {
73535
74031
  return path2;
73536
74032
  }
73537
74033
  worktreePoolLog.log(`Pruned stale entry: ${path2}`);
@@ -73578,7 +74074,7 @@ var init_worktree_pool = __esm({
73578
74074
  */
73579
74075
  rehydrate(idlePaths) {
73580
74076
  for (const path2 of idlePaths) {
73581
- if (existsSync26(path2)) {
74077
+ if (existsSync27(path2)) {
73582
74078
  this.idle.add(path2);
73583
74079
  } else {
73584
74080
  worktreePoolLog.log(`Rehydrate skipped (not on disk): ${path2}`);
@@ -73634,7 +74130,7 @@ var init_worktree_pool = __esm({
73634
74130
  throw err;
73635
74131
  }
73636
74132
  const conflictingPath = match[1];
73637
- if (!existsSync26(conflictingPath)) {
74133
+ if (!existsSync27(conflictingPath)) {
73638
74134
  await execAsync3("git worktree prune", { cwd: worktreePath });
73639
74135
  await execAsync3(checkoutCmd, { cwd: worktreePath });
73640
74136
  return branchName;
@@ -73711,8 +74207,8 @@ var init_token_cap_detector = __esm({
73711
74207
  // ../engine/src/step-session-executor.ts
73712
74208
  import { exec as exec5 } from "node:child_process";
73713
74209
  import { promisify as promisify6 } from "node:util";
73714
- import { existsSync as existsSync27 } from "node:fs";
73715
- import { join as join34 } from "node:path";
74210
+ import { existsSync as existsSync28 } from "node:fs";
74211
+ import { join as join35 } from "node:path";
73716
74212
  function parseStepFileScopes(prompt) {
73717
74213
  const result = /* @__PURE__ */ new Map();
73718
74214
  if (!prompt) return result;
@@ -73959,7 +74455,7 @@ function buildReducedStepPrompt(taskDetail, stepIndex) {
73959
74455
  return parts.join("\n").replace(/\n{3,}/g, "\n\n");
73960
74456
  }
73961
74457
  function sleep3(ms) {
73962
- return new Promise((resolve23) => setTimeout(resolve23, ms));
74458
+ return new Promise((resolve24) => setTimeout(resolve24, ms));
73963
74459
  }
73964
74460
  var execAsync4, stepExecLog, MAX_STEP_RETRIES, RETRY_DELAYS_MS, NOOP_TASK_STORE, StepSessionExecutor;
73965
74461
  var init_step_session_executor = __esm({
@@ -74086,7 +74582,7 @@ var init_step_session_executor = __esm({
74086
74582
  }
74087
74583
  for (const [stepIdx, worktreePath] of this.parallelWorktrees) {
74088
74584
  try {
74089
- if (existsSync27(worktreePath)) {
74585
+ if (existsSync28(worktreePath)) {
74090
74586
  await execAsync4(`git worktree remove "${worktreePath}" --force`, {
74091
74587
  cwd: this.options.rootDir
74092
74588
  });
@@ -74442,7 +74938,7 @@ Follow instructions precisely and avoid unrelated changes.`,
74442
74938
  for (const [stepIdx, worktreePath] of worktreePaths) {
74443
74939
  if (worktreePath !== this.options.worktreePath) {
74444
74940
  try {
74445
- if (existsSync27(worktreePath)) {
74941
+ if (existsSync28(worktreePath)) {
74446
74942
  await execAsync4(`git worktree remove "${worktreePath}" --force`, {
74447
74943
  cwd: this.options.rootDir
74448
74944
  });
@@ -74472,7 +74968,7 @@ Follow instructions precisely and avoid unrelated changes.`,
74472
74968
  async createStepWorktree(stepIndex) {
74473
74969
  const { rootDir } = this.options;
74474
74970
  const name = generateWorktreeName(rootDir);
74475
- const worktreePath = join34(rootDir, ".worktrees", name);
74971
+ const worktreePath = join35(rootDir, ".worktrees", name);
74476
74972
  const branchName = `fusion/step-${stepIndex}-${name}`;
74477
74973
  stepExecLog.log(`Creating worktree for step ${stepIndex}: ${worktreePath} (branch: ${branchName})`);
74478
74974
  try {
@@ -74546,8 +75042,8 @@ Follow instructions precisely and avoid unrelated changes.`,
74546
75042
  });
74547
75043
 
74548
75044
  // ../engine/src/spec-staleness.ts
74549
- import { stat as stat5 } from "node:fs/promises";
74550
- import { join as join35 } from "node:path";
75045
+ import { stat as stat6 } from "node:fs/promises";
75046
+ import { join as join36 } from "node:path";
74551
75047
  async function evaluateSpecStaleness(options) {
74552
75048
  const { settings, promptPath, nowMs } = options;
74553
75049
  if (settings.specStalenessEnabled !== true) {
@@ -74564,7 +75060,7 @@ async function evaluateSpecStaleness(options) {
74564
75060
  const now = nowMs ?? Date.now();
74565
75061
  let mtimeMs;
74566
75062
  try {
74567
- const fileStat = await stat5(promptPath);
75063
+ const fileStat = await stat6(promptPath);
74568
75064
  mtimeMs = fileStat.mtimeMs;
74569
75065
  } catch {
74570
75066
  return {
@@ -74587,7 +75083,7 @@ async function evaluateSpecStaleness(options) {
74587
75083
  };
74588
75084
  }
74589
75085
  function getPromptPath(tasksDir, taskId) {
74590
- return join35(tasksDir, taskId, "PROMPT.md");
75086
+ return join36(tasksDir, taskId, "PROMPT.md");
74591
75087
  }
74592
75088
  var DEFAULT_SPEC_STALENESS_MAX_AGE_MS;
74593
75089
  var init_spec_staleness = __esm({
@@ -74618,8 +75114,8 @@ var init_task_completion = __esm({
74618
75114
 
74619
75115
  // ../engine/src/run-verification-tool.ts
74620
75116
  import { spawn as spawn4 } from "node:child_process";
74621
- import { existsSync as existsSync28 } from "node:fs";
74622
- import { isAbsolute as isAbsolute10, join as join36 } from "node:path";
75117
+ import { existsSync as existsSync29 } from "node:fs";
75118
+ import { isAbsolute as isAbsolute11, join as join37 } from "node:path";
74623
75119
  import { Type as Type4 } from "@mariozechner/pi-ai";
74624
75120
  function createBuffer() {
74625
75121
  return { headChunks: [], headBytes: 0, tailChunks: [], tailBytes: 0, totalBytes: 0 };
@@ -74658,11 +75154,17 @@ async function runVerificationCommand3(opts) {
74658
75154
  const warnings = [];
74659
75155
  const stdoutBuf = createBuffer();
74660
75156
  const stderrBuf = createBuffer();
74661
- return new Promise((resolve23) => {
75157
+ return new Promise((resolve24) => {
74662
75158
  const child = spawn4(command, {
74663
75159
  cwd,
74664
75160
  stdio: ["ignore", "pipe", "pipe"],
74665
- env: { ...process.env },
75161
+ env: {
75162
+ ...process.env,
75163
+ // Corepack otherwise prompts interactively before fetching a pinned
75164
+ // packageManager version, which hangs the non-TTY child until the
75165
+ // hard timeout. Disable the prompt so it proceeds (or errors fast).
75166
+ COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
75167
+ },
74666
75168
  shell: true
74667
75169
  });
74668
75170
  let timedOut = false;
@@ -74737,7 +75239,7 @@ async function runVerificationCommand3(opts) {
74737
75239
  `[fn_run_verification] command failed (exit=${exitCode}, signal=${signal ?? "none"}): ${command}`
74738
75240
  );
74739
75241
  }
74740
- resolve23({
75242
+ resolve24({
74741
75243
  success,
74742
75244
  exitCode,
74743
75245
  durationMs,
@@ -74757,7 +75259,7 @@ async function runVerificationCommand3(opts) {
74757
75259
  clearTimeout(hardTimer);
74758
75260
  const durationMs = Date.now() - startMs;
74759
75261
  warnings.push(`Spawn error: ${err.message}`);
74760
- resolve23({
75262
+ resolve24({
74761
75263
  success: false,
74762
75264
  exitCode: null,
74763
75265
  durationMs,
@@ -74789,10 +75291,10 @@ function createRunVerificationTool(opts) {
74789
75291
  log18.warn(`[fn_run_verification] ${taskId}: ${msg}`);
74790
75292
  }
74791
75293
  let resolvedCwd;
74792
- if (params.cwd && isAbsolute10(params.cwd)) {
75294
+ if (params.cwd && isAbsolute11(params.cwd)) {
74793
75295
  resolvedCwd = params.cwd;
74794
75296
  } else if (params.cwd) {
74795
- resolvedCwd = join36(worktreePath, params.cwd);
75297
+ resolvedCwd = join37(worktreePath, params.cwd);
74796
75298
  } else {
74797
75299
  resolvedCwd = worktreePath;
74798
75300
  }
@@ -74807,8 +75309,8 @@ function createRunVerificationTool(opts) {
74807
75309
  }
74808
75310
  let effectiveCommand = command;
74809
75311
  if (command.trimStart().startsWith("pnpm --filter")) {
74810
- const modulesYaml = join36(rootDir, "node_modules", ".modules.yaml");
74811
- if (!existsSync28(modulesYaml)) {
75312
+ const modulesYaml = join37(rootDir, "node_modules", ".modules.yaml");
75313
+ if (!existsSync29(modulesYaml)) {
74812
75314
  const installCmd = "pnpm install --prefer-offline";
74813
75315
  const msg = `node_modules/.modules.yaml not found in workspace root \u2014 auto-prepending \`${installCmd}\` before running the command.`;
74814
75316
  warnings.push(msg);
@@ -74919,9 +75421,9 @@ var init_run_verification_tool = __esm({
74919
75421
  // ../engine/src/executor.ts
74920
75422
  import { exec as exec6 } from "node:child_process";
74921
75423
  import { promisify as promisify7 } from "node:util";
74922
- import { isAbsolute as isAbsolute11, join as join37, relative as relative8, resolve as resolvePath } from "node:path";
74923
- import { existsSync as existsSync29 } from "node:fs";
74924
- import { readFile as readFile16, writeFile as writeFile12 } from "node:fs/promises";
75424
+ import { isAbsolute as isAbsolute12, join as join38, relative as relative9, resolve as resolvePath } from "node:path";
75425
+ import { existsSync as existsSync30 } from "node:fs";
75426
+ import { readFile as readFile17, writeFile as writeFile13 } from "node:fs/promises";
74925
75427
  import { Type as Type5 } from "@mariozechner/pi-ai";
74926
75428
  import { ModelRegistry as ModelRegistry2, SessionManager as SessionManager2 } from "@mariozechner/pi-coding-agent";
74927
75429
  function truncateWorkflowScriptOutput2(output) {
@@ -74983,7 +75485,7 @@ function getExecutorSystemPrompt(settings) {
74983
75485
  ].filter((section) => section.trim());
74984
75486
  return sections.join("\n\n");
74985
75487
  }
74986
- function formatTimestamp2(iso) {
75488
+ function formatTimestamp3(iso) {
74987
75489
  const date = new Date(iso);
74988
75490
  const now = /* @__PURE__ */ new Date();
74989
75491
  const diffMs = now.getTime() - date.getTime();
@@ -75078,7 +75580,7 @@ git log --oneline
75078
75580
  ""
75079
75581
  ];
75080
75582
  for (const comment of recentComments) {
75081
- const timestamp = formatTimestamp2(comment.createdAt);
75583
+ const timestamp = formatTimestamp3(comment.createdAt);
75082
75584
  lines.push(`**${comment.author}** \u2014 ${timestamp}`);
75083
75585
  lines.push(`> ${comment.text}`);
75084
75586
  lines.push("");
@@ -75142,7 +75644,7 @@ If lint is configured and failing, fix that too before completion.
75142
75644
  **CRITICAL: Resolve ALL test failures (and any lint/typecheck failures) before completing the task, even if they appear unrelated or pre-existing.** Unrelated failures left unfixed accumulate technical debt and block future integrations. Investigate and fix or suppress them \u2014 do not defer them to a separate task.`;
75143
75645
  }
75144
75646
  function formatCommentForInjection(comment) {
75145
- const timestamp = formatTimestamp2(comment.createdAt);
75647
+ const timestamp = formatTimestamp3(comment.createdAt);
75146
75648
  return `\u{1F4E3} **New feedback** \u2014 ${timestamp} (${comment.author}):
75147
75649
 
75148
75650
  ${comment.text}
@@ -76545,7 +77047,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
76545
77047
  );
76546
77048
  return false;
76547
77049
  }
76548
- if (task.worktree && existsSync29(task.worktree)) {
77050
+ if (task.worktree && existsSync30(task.worktree)) {
76549
77051
  const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha);
76550
77052
  if (modifiedFiles.length > 0) {
76551
77053
  await this.store.updateTask(task.id, { modifiedFiles });
@@ -76802,7 +77304,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
76802
77304
  const activeMergeStatuses = /* @__PURE__ */ new Set(["merging", "merging-pr", "merging-fix"]);
76803
77305
  const isActiveTask = activeColumns.has(task.column) || activeMergeStatuses.has(task.status ?? "");
76804
77306
  if (!isActiveTask) {
76805
- const tasksDir = join37(this.store.getFusionDir(), "tasks");
77307
+ const tasksDir = join38(this.store.getFusionDir(), "tasks");
76806
77308
  const promptPath = getPromptPath(tasksDir, task.id);
76807
77309
  const staleness = await evaluateSpecStaleness({ settings, promptPath });
76808
77310
  if (staleness.isStale) {
@@ -76849,7 +77351,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
76849
77351
  worktreeName = generateWorktreeName(this.rootDir);
76850
77352
  break;
76851
77353
  }
76852
- worktreePath = join37(this.rootDir, ".worktrees", worktreeName);
77354
+ worktreePath = join38(this.rootDir, ".worktrees", worktreeName);
76853
77355
  }
76854
77356
  let stuckRequeue = null;
76855
77357
  let taskDone = false;
@@ -76876,7 +77378,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
76876
77378
  );
76877
77379
  }
76878
77380
  const branchName = task.branch || `fusion/${task.id.toLowerCase()}`;
76879
- let isResume = existsSync29(worktreePath);
77381
+ let isResume = existsSync30(worktreePath);
76880
77382
  let acquiredFromPool = false;
76881
77383
  const baseBranch = task.executionStartBranch || null;
76882
77384
  if (task.worktree && isResume && !await isUsableTaskWorktree(this.rootDir, worktreePath)) {
@@ -76889,8 +77391,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
76889
77391
  this.currentRunContext
76890
77392
  );
76891
77393
  await this.store.updateTask(task.id, { worktree: null, branch: null });
76892
- worktreePath = join37(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
76893
- isResume = existsSync29(worktreePath);
77394
+ worktreePath = join38(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
77395
+ isResume = existsSync30(worktreePath);
76894
77396
  }
76895
77397
  if (!isResume) {
76896
77398
  if (this.options.pool && settings.recycleWorktrees) {
@@ -77289,7 +77791,7 @@ ${summary}`,
77289
77791
  executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}: ${errorMessage}`);
77290
77792
  await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}): ${errorMessage}`, void 0, this.currentRunContext);
77291
77793
  }
77292
- if (worktreePath && existsSync29(worktreePath)) {
77794
+ if (worktreePath && existsSync30(worktreePath)) {
77293
77795
  try {
77294
77796
  await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
77295
77797
  await audit.git({ type: "worktree:remove", target: worktreePath });
@@ -77357,7 +77859,7 @@ ${summary}`,
77357
77859
  if (!preserveProgress) {
77358
77860
  await this.resetStepsIfWorkLost(latestTask);
77359
77861
  }
77360
- if (worktreePath && existsSync29(worktreePath)) {
77862
+ if (worktreePath && existsSync30(worktreePath)) {
77361
77863
  try {
77362
77864
  await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
77363
77865
  } catch (wtErr) {
@@ -77478,7 +77980,7 @@ ${summary}`,
77478
77980
  const executorFallbackProvider = settings.fallbackProvider;
77479
77981
  const executorFallbackModelId = settings.fallbackModelId;
77480
77982
  const executorThinkingLevel = detail.thinkingLevel ?? settings.defaultThinkingLevel;
77481
- const isResuming = !!task.sessionFile && existsSync29(task.sessionFile);
77983
+ const isResuming = !!task.sessionFile && existsSync30(task.sessionFile);
77482
77984
  const sessionManager = isResuming ? SessionManager2.open(task.sessionFile) : SessionManager2.create(worktreePath);
77483
77985
  executorLog.log(`${task.id}: creating agent session (provider=${executorProvider ?? "default"}, model=${executorModelId ?? "default"}, resuming=${isResuming})`);
77484
77986
  const executorInstructions = await this.resolveInstructionsForRole("executor");
@@ -77993,7 +78495,7 @@ ${executorPluginContributions}` : executorSystemPrompt;
77993
78495
  this.options.onComplete?.(task);
77994
78496
  } else {
77995
78497
  executorLog.log(`${task.id} paused \u2014 moving to todo`);
77996
- if (worktreePath && existsSync29(worktreePath)) {
78498
+ if (worktreePath && existsSync30(worktreePath)) {
77997
78499
  try {
77998
78500
  await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
77999
78501
  executorLog.log(`Removed old worktree for paused task: ${worktreePath}`);
@@ -78089,7 +78591,7 @@ ${executorPluginContributions}` : executorSystemPrompt;
78089
78591
  executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}: ${errorMessage}`);
78090
78592
  await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}): ${errorMessage}`, void 0, this.currentRunContext);
78091
78593
  }
78092
- if (worktreePath && existsSync29(worktreePath)) {
78594
+ if (worktreePath && existsSync30(worktreePath)) {
78093
78595
  try {
78094
78596
  await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
78095
78597
  executorLog.log(`Removed old worktree for transient retry: ${worktreePath}`);
@@ -78161,7 +78663,7 @@ ${executorPluginContributions}` : executorSystemPrompt;
78161
78663
  if (!preserveProgress) {
78162
78664
  await this.resetStepsIfWorkLost(latestTask);
78163
78665
  }
78164
- if (worktreePath && existsSync29(worktreePath)) {
78666
+ if (worktreePath && existsSync30(worktreePath)) {
78165
78667
  try {
78166
78668
  await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
78167
78669
  executorLog.log(`Removed old worktree for stuck-killed retry: ${worktreePath}`);
@@ -78731,10 +79233,10 @@ Take a different approach. Do NOT repeat the rejected strategy. Re-read the step
78731
79233
  * The section is replaced entirely to avoid accumulation of old feedback.
78732
79234
  */
78733
79235
  async injectWorkflowRevisionInstructions(task, feedback) {
78734
- const promptPath = join37(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
79236
+ const promptPath = join38(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
78735
79237
  let content;
78736
79238
  try {
78737
- content = await readFile16(promptPath, "utf-8");
79239
+ content = await readFile17(promptPath, "utf-8");
78738
79240
  } catch {
78739
79241
  executorLog.warn(`${task.id}: PROMPT.md not found at ${promptPath}, skipping revision injection`);
78740
79242
  return;
@@ -78771,7 +79273,7 @@ ${feedback}
78771
79273
  }
78772
79274
  }
78773
79275
  try {
78774
- await writeFile12(promptPath, newContent);
79276
+ await writeFile13(promptPath, newContent);
78775
79277
  executorLog.log(`${task.id}: injected workflow revision instructions into PROMPT.md`);
78776
79278
  } catch (err) {
78777
79279
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -79067,10 +79569,10 @@ Please fix the issues so the verification can pass on the next attempt.`,
79067
79569
  * The section is replaced entirely to avoid accumulation of old feedback.
79068
79570
  */
79069
79571
  async injectWorkflowStepFailureInstructions(task, failureFeedback, stepName, retryCount) {
79070
- const promptPath = join37(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
79572
+ const promptPath = join38(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
79071
79573
  let content;
79072
79574
  try {
79073
- content = await readFile16(promptPath, "utf-8");
79575
+ content = await readFile17(promptPath, "utf-8");
79074
79576
  } catch {
79075
79577
  executorLog.warn(`${task.id}: PROMPT.md not found at ${promptPath}, skipping workflow failure injection`);
79076
79578
  return;
@@ -79120,7 +79622,7 @@ ${failureFeedback}
79120
79622
  }
79121
79623
  }
79122
79624
  try {
79123
- await writeFile12(promptPath, newContent);
79625
+ await writeFile13(promptPath, newContent);
79124
79626
  executorLog.log(`${task.id}: injected workflow step failure instructions into PROMPT.md (retry ${retryCount}/${MAX_WORKFLOW_STEP_RETRIES})`);
79125
79627
  } catch (err) {
79126
79628
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -79677,7 +80179,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
79677
80179
  );
79678
80180
  }
79679
80181
  const delay3 = this.WORKTREE_RETRY_DELAYS[attempt] || 1e3;
79680
- await new Promise((resolve23) => setTimeout(resolve23, delay3));
80182
+ await new Promise((resolve24) => setTimeout(resolve24, delay3));
79681
80183
  }
79682
80184
  }
79683
80185
  throw new Error("Unexpected exit from worktree creation retry loop");
@@ -79899,7 +80401,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
79899
80401
  * rather than fail the task permanently.
79900
80402
  */
79901
80403
  async resolveWorktreeStartPoint(startPoint, taskId) {
79902
- const command = isAbsolute11(startPoint) && existsSync29(startPoint) ? `git -C "${startPoint}" rev-parse --verify HEAD^{commit}` : `git rev-parse --verify "${startPoint}^{commit}"`;
80404
+ const command = isAbsolute12(startPoint) && existsSync30(startPoint) ? `git -C "${startPoint}" rev-parse --verify HEAD^{commit}` : `git rev-parse --verify "${startPoint}^{commit}"`;
79903
80405
  try {
79904
80406
  const { stdout } = await execAsync5(command, { cwd: this.rootDir });
79905
80407
  return stdout.trim() || startPoint;
@@ -79919,7 +80421,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
79919
80421
  */
79920
80422
  async tryCreateWorktree(branch, path2, taskId, startPoint, attemptNumber = 0, recoveryDepth = 0) {
79921
80423
  await this.assertWorktreePathNotNested(path2, taskId);
79922
- if (existsSync29(path2)) {
80424
+ if (existsSync30(path2)) {
79923
80425
  const isRegistered = await this.isRegisteredWorktree(path2);
79924
80426
  if (!isRegistered) {
79925
80427
  await this.store.logEntry(
@@ -80070,7 +80572,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
80070
80572
  );
80071
80573
  if (shouldGenerateNewName) {
80072
80574
  const conflictStartPoint = branch;
80073
- const newPath = join37(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
80575
+ const newPath = join38(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
80074
80576
  for (let suffix = 2; suffix <= 6; suffix++) {
80075
80577
  const suffixedBranch = `${branch}-${suffix}`;
80076
80578
  try {
@@ -80117,8 +80619,8 @@ Review the work done in this worktree and evaluate it against the criteria in yo
80117
80619
  for (const wt of registered) {
80118
80620
  if (wt === rootResolved) continue;
80119
80621
  if (wt === target) continue;
80120
- const rel = relative8(wt, target);
80121
- if (rel && !rel.startsWith("..") && !isAbsolute11(rel)) {
80622
+ const rel = relative9(wt, target);
80623
+ if (rel && !rel.startsWith("..") && !isAbsolute12(rel)) {
80122
80624
  await this.store.logEntry(
80123
80625
  taskId,
80124
80626
  `Refusing to create nested worktree`,
@@ -80694,7 +81196,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
80694
81196
  metadata: { type: "spawned", parentTaskId: taskId }
80695
81197
  });
80696
81198
  const childWorktreeName = generateWorktreeName(this.rootDir);
80697
- const childWorktreePath = join37(this.rootDir, ".worktrees", childWorktreeName);
81199
+ const childWorktreePath = join38(this.rootDir, ".worktrees", childWorktreeName);
80698
81200
  const childBranch = `fusion/spawn-${agent.id}`;
80699
81201
  await this.createWorktree(childBranch, childWorktreePath, taskId, worktreePath);
80700
81202
  await this.options.agentStore.updateAgentState(agent.id, "active");
@@ -81090,9 +81592,9 @@ var init_node_routing_policy = __esm({
81090
81592
  });
81091
81593
 
81092
81594
  // ../engine/src/scheduler.ts
81093
- import { existsSync as existsSync30 } from "node:fs";
81094
- import { readFile as readFile17 } from "node:fs/promises";
81095
- import { join as join38 } from "node:path";
81595
+ import { existsSync as existsSync31 } from "node:fs";
81596
+ import { readFile as readFile18 } from "node:fs/promises";
81597
+ import { join as join39 } from "node:path";
81096
81598
  function pathsOverlap2(a, b) {
81097
81599
  for (const pa of a) {
81098
81600
  const prefixA = pa.endsWith("/*") ? pa.slice(0, -1) : null;
@@ -81264,16 +81766,16 @@ var init_scheduler = __esm({
81264
81766
  * @returns Object with `valid: true` if checks pass, or `valid: false` with a `reason` string if they fail
81265
81767
  */
81266
81768
  async validateTaskFilesystem(id) {
81267
- const taskDir = join38(this.store.getTasksDir(), id);
81268
- if (!existsSync30(taskDir)) {
81769
+ const taskDir = join39(this.store.getTasksDir(), id);
81770
+ if (!existsSync31(taskDir)) {
81269
81771
  return { valid: false, reason: "missing directory" };
81270
81772
  }
81271
- const promptPath = join38(taskDir, "PROMPT.md");
81272
- if (!existsSync30(promptPath)) {
81773
+ const promptPath = join39(taskDir, "PROMPT.md");
81774
+ if (!existsSync31(promptPath)) {
81273
81775
  return { valid: false, reason: "missing or empty PROMPT.md" };
81274
81776
  }
81275
81777
  try {
81276
- const content = await readFile17(promptPath, "utf-8");
81778
+ const content = await readFile18(promptPath, "utf-8");
81277
81779
  if (!content || content.trim().length === 0) {
81278
81780
  return { valid: false, reason: "missing or empty PROMPT.md" };
81279
81781
  }
@@ -83470,8 +83972,8 @@ var agent_reflection_exports = {};
83470
83972
  __export(agent_reflection_exports, {
83471
83973
  AgentReflectionService: () => AgentReflectionService
83472
83974
  });
83473
- import { readFile as readFile18 } from "node:fs/promises";
83474
- import { isAbsolute as isAbsolute12, resolve as resolve18 } from "node:path";
83975
+ import { readFile as readFile19 } from "node:fs/promises";
83976
+ import { isAbsolute as isAbsolute13, resolve as resolve19 } from "node:path";
83475
83977
  var reflectionLog, REFLECTION_SYSTEM_PROMPT, DEFAULT_OUTCOME_LIMIT, AgentReflectionService;
83476
83978
  var init_agent_reflection = __esm({
83477
83979
  "../engine/src/agent-reflection.ts"() {
@@ -83782,9 +84284,9 @@ Rules:
83782
84284
  pieces.push(agent.instructionsText.trim());
83783
84285
  }
83784
84286
  if (agent.instructionsPath?.trim()) {
83785
- const resolvedPath = isAbsolute12(agent.instructionsPath) ? agent.instructionsPath : resolve18(this.rootDir, agent.instructionsPath);
84287
+ const resolvedPath = isAbsolute13(agent.instructionsPath) ? agent.instructionsPath : resolve19(this.rootDir, agent.instructionsPath);
83786
84288
  try {
83787
- const content = await readFile18(resolvedPath, "utf-8");
84289
+ const content = await readFile19(resolvedPath, "utf-8");
83788
84290
  if (content.trim()) {
83789
84291
  pieces.push(content.trim());
83790
84292
  }
@@ -87273,7 +87775,7 @@ var init_evaluator = __esm({
87273
87775
  // ../engine/src/cron-runner.ts
87274
87776
  import { exec as exec7 } from "node:child_process";
87275
87777
  function execCommand(command, options) {
87276
- return new Promise((resolve23, reject) => {
87778
+ return new Promise((resolve24, reject) => {
87277
87779
  exec7(command, options, (error, stdout, stderr) => {
87278
87780
  const stdoutText = typeof stdout === "string" ? stdout : String(stdout ?? "");
87279
87781
  const stderrText = typeof stderr === "string" ? stderr : String(stderr ?? "");
@@ -87284,7 +87786,7 @@ function execCommand(command, options) {
87284
87786
  reject(errWithOutput);
87285
87787
  return;
87286
87788
  }
87287
- resolve23({ stdout: stdoutText, stderr: stderrText });
87789
+ resolve24({ stdout: stdoutText, stderr: stderrText });
87288
87790
  });
87289
87791
  });
87290
87792
  }
@@ -87318,6 +87820,36 @@ function isInProcessBackupCommand(command) {
87318
87820
  }
87319
87821
  return true;
87320
87822
  }
87823
+ function isInProcessMemoryBackupCommand(command) {
87824
+ if (!command) return false;
87825
+ const trimmed = command.trim();
87826
+ if (!trimmed) return false;
87827
+ if (SHELL_METACHARACTERS_REGEX.test(trimmed)) return false;
87828
+ const tokens = trimmed.split(/\s+/).map((tok) => tok.toLowerCase());
87829
+ let cursor = 0;
87830
+ if (tokens[cursor] === "npx") {
87831
+ cursor += 1;
87832
+ while (cursor < tokens.length) {
87833
+ const tok = tokens[cursor];
87834
+ if (tok === void 0 || !tok.startsWith("-")) break;
87835
+ const takesValue = (tok === "-p" || tok === "--package") && cursor + 1 < tokens.length && tokens[cursor + 1] !== void 0 && !tokens[cursor + 1].startsWith("-");
87836
+ cursor += takesValue ? 2 : 1;
87837
+ }
87838
+ }
87839
+ const binary = tokens[cursor];
87840
+ if (!binary || !FUSION_BINARY_TOKENS.has(binary)) return false;
87841
+ cursor += 1;
87842
+ if (tokens[cursor] !== "memory-backup") return false;
87843
+ cursor += 1;
87844
+ if (tokens[cursor] !== "--create") return false;
87845
+ cursor += 1;
87846
+ for (; cursor < tokens.length; cursor += 1) {
87847
+ const tok = tokens[cursor];
87848
+ if (!tok) continue;
87849
+ if (!tok.startsWith("-")) return false;
87850
+ }
87851
+ return true;
87852
+ }
87321
87853
  function isInProcessScheduledEvalCommand(command) {
87322
87854
  if (!command) return false;
87323
87855
  const trimmed = command.trim();
@@ -87525,6 +88057,9 @@ var init_cron_runner = __esm({
87525
88057
  if (isInProcessBackupCommand(schedule.command)) {
87526
88058
  return this.executeBackupInProcess(schedule, startedAt);
87527
88059
  }
88060
+ if (isInProcessMemoryBackupCommand(schedule.command)) {
88061
+ return this.executeMemoryBackupInProcess(schedule, startedAt);
88062
+ }
87528
88063
  if (isInProcessScheduledEvalCommand(schedule.command)) {
87529
88064
  return this.executeScheduledEvalInProcess(schedule, startedAt);
87530
88065
  }
@@ -87578,6 +88113,21 @@ var init_cron_runner = __esm({
87578
88113
  completedAt: (/* @__PURE__ */ new Date()).toISOString()
87579
88114
  };
87580
88115
  }
88116
+ async executeMemoryBackupInProcess(schedule, startedAt) {
88117
+ const action = await this.runMemoryBackupActionInProcess();
88118
+ if (action.success) {
88119
+ log15.log(`\u2713 ${schedule.name} completed in-process`);
88120
+ } else {
88121
+ log15.warn(`\u2717 ${schedule.name} in-process memory backup ${action.error ? `threw: ${action.error}` : `reported failure: ${action.output}`}`);
88122
+ }
88123
+ return {
88124
+ success: action.success,
88125
+ output: action.output,
88126
+ error: action.error,
88127
+ startedAt,
88128
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
88129
+ };
88130
+ }
87581
88131
  /**
87582
88132
  * Shared in-process backup execution used by both the legacy-command path
87583
88133
  * and the command-step path. Returns the success/output/error tuple in
@@ -87635,6 +88185,22 @@ var init_cron_runner = __esm({
87635
88185
  return { success: false, output: "", error: message };
87636
88186
  }
87637
88187
  }
88188
+ async runMemoryBackupActionInProcess() {
88189
+ try {
88190
+ const { runMemoryBackupCommand: runMemoryBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
88191
+ const fusionDir = this.store.getFusionDir();
88192
+ const settings = await this.store.getSettings();
88193
+ const result = await runMemoryBackupCommand2(fusionDir, settings);
88194
+ return {
88195
+ success: result.success,
88196
+ output: truncateOutput2(result.output ?? "", ""),
88197
+ error: result.success ? void 0 : result.output
88198
+ };
88199
+ } catch (err) {
88200
+ const message = err instanceof Error ? err.message : String(err);
88201
+ return { success: false, output: "", error: message };
88202
+ }
88203
+ }
87638
88204
  /**
87639
88205
  * Execute multiple steps sequentially.
87640
88206
  * Aggregates per-step results into an overall AutomationRunResult.
@@ -87736,6 +88302,19 @@ var init_cron_runner = __esm({
87736
88302
  completedAt: (/* @__PURE__ */ new Date()).toISOString()
87737
88303
  };
87738
88304
  }
88305
+ if (isInProcessMemoryBackupCommand(step.command)) {
88306
+ const action = await this.runMemoryBackupActionInProcess();
88307
+ return {
88308
+ stepId: step.id,
88309
+ stepName: step.name,
88310
+ stepIndex,
88311
+ success: action.success,
88312
+ output: action.output,
88313
+ error: action.error,
88314
+ startedAt,
88315
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
88316
+ };
88317
+ }
87739
88318
  try {
87740
88319
  const { stdout, stderr } = await execCommand(step.command, {
87741
88320
  timeout: timeoutMs,
@@ -88109,6 +88688,30 @@ var init_routine_runner = __esm({
88109
88688
  };
88110
88689
  }
88111
88690
  }
88691
+ if (isInProcessMemoryBackupCommand(command) && this.options.taskStore) {
88692
+ try {
88693
+ const { runMemoryBackupCommand: runMemoryBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
88694
+ const fusionDir = this.options.taskStore.getFusionDir();
88695
+ const settings = await this.options.taskStore.getSettings();
88696
+ const result = await runMemoryBackupCommand2(fusionDir, settings);
88697
+ return {
88698
+ success: result.success,
88699
+ output: truncateOutput3(result.output ?? "", ""),
88700
+ error: result.success ? void 0 : result.output,
88701
+ startedAt,
88702
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
88703
+ };
88704
+ } catch (err) {
88705
+ const message = err instanceof Error ? err.message : String(err);
88706
+ return {
88707
+ success: false,
88708
+ output: "",
88709
+ error: message,
88710
+ startedAt,
88711
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
88712
+ };
88713
+ }
88714
+ }
88112
88715
  try {
88113
88716
  const { stdout, stderr } = await execAsync6(command, {
88114
88717
  timeout: timeoutMs ?? DEFAULT_TIMEOUT_MS8,
@@ -88835,8 +89438,8 @@ var init_stuck_task_detector = __esm({
88835
89438
  // ../engine/src/self-healing.ts
88836
89439
  import { exec as exec9, execSync as execSync2 } from "node:child_process";
88837
89440
  import { promisify as promisify9 } from "node:util";
88838
- import { existsSync as existsSync31, readdirSync as readdirSync5, rmSync as rmSync3, statSync as statSync5 } from "node:fs";
88839
- import { isAbsolute as isAbsolute13, join as join39, relative as relative9, resolve as resolve19 } from "node:path";
89441
+ import { existsSync as existsSync32, readdirSync as readdirSync5, rmSync as rmSync3, statSync as statSync5 } from "node:fs";
89442
+ import { isAbsolute as isAbsolute14, join as join40, relative as relative10, resolve as resolve20 } from "node:path";
88840
89443
  function commitOwnedByTask2(taskId, subject, body) {
88841
89444
  return body.includes(`Fusion-Task-Id: ${taskId}`) || subject.includes(taskId);
88842
89445
  }
@@ -88873,7 +89476,7 @@ function isNoTaskDoneFailure(task) {
88873
89476
  function hasStepProgress(task) {
88874
89477
  return task.steps.some((step) => step.status !== "pending");
88875
89478
  }
88876
- var log17, execAsync7, APPROVED_TRIAGE_RECOVERY_GRACE_MS, ORPHANED_EXECUTION_RECOVERY_GRACE_MS, ACTIVE_MERGE_STATUSES, NON_TERMINAL_STEP_STATUSES2, GHOST_REVIEW_PRESERVED_STATUSES, ORPHANED_WITH_WORKTREE_GRACE_MS, MAX_TASK_DONE_RETRIES, MAX_AUTO_MERGE_RETRIES, DEFAULT_STALE_MERGING_STATUS_MIN_AGE_MS, SelfHealingManager;
89479
+ var log17, execAsync7, APPROVED_TRIAGE_RECOVERY_GRACE_MS, ORPHANED_EXECUTION_RECOVERY_GRACE_MS, ACTIVE_MERGE_STATUSES, NON_TERMINAL_STEP_STATUSES2, GHOST_REVIEW_PRESERVED_STATUSES, ORPHANED_WITH_WORKTREE_GRACE_MS, MAX_TASK_DONE_RETRIES, MAX_AUTO_MERGE_RETRIES, DEADLOCK_RECOVERY_COOLDOWN_MS, DEFAULT_STALE_MERGING_STATUS_MIN_AGE_MS, SelfHealingManager;
88877
89480
  var init_self_healing = __esm({
88878
89481
  "../engine/src/self-healing.ts"() {
88879
89482
  "use strict";
@@ -88897,6 +89500,7 @@ var init_self_healing = __esm({
88897
89500
  ORPHANED_WITH_WORKTREE_GRACE_MS = 3e5;
88898
89501
  MAX_TASK_DONE_RETRIES = 3;
88899
89502
  MAX_AUTO_MERGE_RETRIES = 3;
89503
+ DEADLOCK_RECOVERY_COOLDOWN_MS = 15 * 6e4;
88900
89504
  DEFAULT_STALE_MERGING_STATUS_MIN_AGE_MS = 5 * 6e4;
88901
89505
  SelfHealingManager = class _SelfHealingManager {
88902
89506
  constructor(store, options) {
@@ -88913,6 +89517,8 @@ var init_self_healing = __esm({
88913
89517
  maintenanceRunning = false;
88914
89518
  // ── Event listener cleanup ──────────────────────────────────────────
88915
89519
  settingsListener = null;
89520
+ // ── Per-task deadlock recovery cooldown ─────────────────────────────
89521
+ deadlockRecoveryCooldown = /* @__PURE__ */ new Map();
88916
89522
  // ── Lifecycle ───────────────────────────────────────────────────────
88917
89523
  start() {
88918
89524
  this.settingsListener = ({ settings, previous }) => {
@@ -88944,6 +89550,8 @@ var init_self_healing = __esm({
88944
89550
  { name: "failed-pre-merge-steps", fn: () => this.recoverReviewTasksWithFailedPreMergeSteps().then(() => void 0) },
88945
89551
  { name: "interrupted-merging", fn: () => this.recoverInterruptedMergingTasks().then(() => void 0) },
88946
89552
  { name: "done-merge-metadata", fn: () => this.recoverDoneTaskMergeMetadata().then(() => void 0) },
89553
+ { name: "recover-already-merged-review", fn: () => this.recoverAlreadyMergedReviewTasks().then(() => void 0) },
89554
+ { name: "recover-stuck-merge-deadlocks", fn: () => this.recoverStuckMergeDeadlocks().then(() => void 0) },
88947
89555
  { name: "misclassified-failures", fn: () => this.recoverMisclassifiedFailures().then(() => void 0) },
88948
89556
  { name: "partial-progress-no-task-done", fn: () => this.recoverPartialProgressNoTaskDoneFailures().then(() => void 0) },
88949
89557
  { name: "orphaned-executions", fn: () => this.recoverOrphanedExecutions().then(() => void 0) },
@@ -89351,7 +89959,7 @@ var init_self_healing = __esm({
89351
89959
  return null;
89352
89960
  }
89353
89961
  async cleanupWorktreeOnly(task) {
89354
- if (task.worktree && existsSync31(task.worktree)) {
89962
+ if (task.worktree && existsSync32(task.worktree)) {
89355
89963
  try {
89356
89964
  await execAsync7(`git worktree remove ${shellQuote(task.worktree)} --force`, {
89357
89965
  cwd: this.options.rootDir,
@@ -89366,7 +89974,7 @@ var init_self_healing = __esm({
89366
89974
  }
89367
89975
  }
89368
89976
  async cleanupInterruptedMergeArtifacts(task) {
89369
- if (task.worktree && existsSync31(task.worktree)) {
89977
+ if (task.worktree && existsSync32(task.worktree)) {
89370
89978
  try {
89371
89979
  await execAsync7(`git worktree remove ${shellQuote(task.worktree)} --force`, {
89372
89980
  cwd: this.options.rootDir,
@@ -89432,6 +90040,7 @@ var init_self_healing = __esm({
89432
90040
  { name: "recover-mergeable-review", fn: () => this.recoverMergeableReviewTasks() },
89433
90041
  { name: "recover-merged-review", fn: () => this.recoverMergedReviewTasks() },
89434
90042
  { name: "recover-already-merged-review", fn: () => this.recoverAlreadyMergedReviewTasks() },
90043
+ { name: "recover-stuck-merge-deadlocks", fn: () => this.recoverStuckMergeDeadlocks() },
89435
90044
  { name: "recover-misclassified-failures", fn: () => this.recoverMisclassifiedFailures() },
89436
90045
  { name: "recover-no-progress-no-task-done", fn: () => this.recoverNoProgressNoTaskDoneFailures() },
89437
90046
  { name: "recover-partial-progress-no-task-done", fn: () => this.recoverPartialProgressNoTaskDoneFailures() },
@@ -90119,6 +90728,99 @@ var init_self_healing = __esm({
90119
90728
  return 0;
90120
90729
  }
90121
90730
  }
90731
+ /**
90732
+ * Recover deadlocked retry-exhausted merge failures that are still blocking
90733
+ * dispatch via `blockedBy` or retained worktree ownership.
90734
+ */
90735
+ async recoverStuckMergeDeadlocks() {
90736
+ try {
90737
+ const settings = await this.store.getSettings();
90738
+ if (settings.globalPause || settings.enginePaused) return 0;
90739
+ const now = Date.now();
90740
+ const inReview = await this.store.listTasks({ column: "in-review", slim: true });
90741
+ const triage = await this.store.listTasks({ column: "triage", slim: true });
90742
+ const todo = await this.store.listTasks({ column: "todo", slim: true });
90743
+ const inProgress = await this.store.listTasks({ column: "in-progress", slim: true });
90744
+ const dependentsByBlocker = /* @__PURE__ */ new Map();
90745
+ for (const task of [...triage, ...todo, ...inProgress]) {
90746
+ if (!task.blockedBy) continue;
90747
+ const dependents = dependentsByBlocker.get(task.blockedBy) ?? [];
90748
+ dependents.push(task);
90749
+ dependentsByBlocker.set(task.blockedBy, dependents);
90750
+ }
90751
+ const candidates = inReview.filter((task) => {
90752
+ const cooldownStart = this.deadlockRecoveryCooldown.get(task.id) ?? 0;
90753
+ const cooldownElapsed = now - cooldownStart;
90754
+ const hasBlockedDependents = (dependentsByBlocker.get(task.id) ?? []).some(
90755
+ (dep) => dep.column === "triage" || dep.column === "todo"
90756
+ );
90757
+ return task.column === "in-review" && !task.paused && task.status === "failed" && (task.mergeRetries ?? 0) >= MAX_AUTO_MERGE_RETRIES && task.mergeDetails?.mergeConfirmed !== true && (hasBlockedDependents || Boolean(task.worktree)) && cooldownElapsed >= DEADLOCK_RECOVERY_COOLDOWN_MS;
90758
+ });
90759
+ if (candidates.length === 0) return 0;
90760
+ let recovered = 0;
90761
+ for (const task of candidates) {
90762
+ const blockedDependents = dependentsByBlocker.get(task.id) ?? [];
90763
+ const blockedTaskIds = blockedDependents.map((dep) => dep.id);
90764
+ try {
90765
+ const landedCommit = await this.findLandedTaskCommit(task);
90766
+ if (landedCommit) {
90767
+ const mergeDetails = {
90768
+ commitSha: landedCommit.sha,
90769
+ filesChanged: landedCommit.filesChanged,
90770
+ insertions: landedCommit.insertions,
90771
+ deletions: landedCommit.deletions,
90772
+ mergeCommitMessage: landedCommit.subject,
90773
+ mergedAt: (/* @__PURE__ */ new Date()).toISOString(),
90774
+ mergeConfirmed: true,
90775
+ prNumber: task.prInfo?.number
90776
+ };
90777
+ await this.store.updateTask(task.id, {
90778
+ status: null,
90779
+ error: null,
90780
+ mergeRetries: 0,
90781
+ worktree: null,
90782
+ branch: null,
90783
+ mergeDetails
90784
+ });
90785
+ await this.store.moveTask(task.id, "done");
90786
+ await this.cleanupInterruptedMergeArtifacts(task);
90787
+ const clearedDependents = [];
90788
+ for (const dep of blockedDependents) {
90789
+ try {
90790
+ await this.store.updateTask(dep.id, { blockedBy: null });
90791
+ await this.store.logEntry(dep.id, `Auto-recovered: cleared stale blockedBy ${task.id} after deadlock recovery`);
90792
+ clearedDependents.push(dep.id);
90793
+ } catch (depErr) {
90794
+ const depErrMessage = depErr instanceof Error ? depErr.message : String(depErr);
90795
+ log17.warn(`self-heal:deadlock-recovery-dependent-error ${JSON.stringify({ blockerTaskId: task.id, dependentTaskId: dep.id, error: depErrMessage })}`);
90796
+ }
90797
+ }
90798
+ await this.store.logEntry(
90799
+ task.id,
90800
+ `Auto-recovered: merge deadlock resolved via landed commit ${landedCommit.sha.slice(0, 8)}${clearedDependents.length > 0 ? `; cleared blockedBy on ${clearedDependents.join(", ")}` : ""}`
90801
+ );
90802
+ log17.log(`self-heal:deadlock-recovered ${JSON.stringify({ stuckTaskId: task.id, blockedTaskIds, attributedSha: landedCommit.sha, action: "reattributed" })}`);
90803
+ recovered++;
90804
+ } else {
90805
+ await this.store.updateTask(task.id, { paused: true });
90806
+ await this.store.logEntry(task.id, "merge-deadlock-detected: requires manual intervention \u2014 verified content not on main");
90807
+ log17.warn(`self-heal:deadlock-recovered ${JSON.stringify({ stuckTaskId: task.id, blockedTaskIds, attributedSha: null, action: "paused-for-manual" })}`);
90808
+ recovered++;
90809
+ }
90810
+ } catch (err) {
90811
+ const errorMessage = err instanceof Error ? err.message : String(err);
90812
+ log17.warn(`self-heal:deadlock-recovery-error ${JSON.stringify({ stuckTaskId: task.id, blockedTaskIds, error: errorMessage })}`);
90813
+ } finally {
90814
+ this.deadlockRecoveryCooldown.set(task.id, Date.now());
90815
+ }
90816
+ }
90817
+ return recovered;
90818
+ } catch (err) {
90819
+ const errorMessage = err instanceof Error ? err.message : String(err);
90820
+ log17.error(`Stuck merge deadlock recovery failed: ${errorMessage}`);
90821
+ return 0;
90822
+ }
90823
+ }
90122
90824
  /**
90123
90825
  * Recover retry-exhausted failed review tasks whose content already landed on
90124
90826
  * the integration branch via a non-canonical merge lineage.
@@ -90257,7 +90959,7 @@ var init_self_healing = __esm({
90257
90959
  return false;
90258
90960
  }
90259
90961
  const staleness = now - new Date(t.updatedAt).getTime();
90260
- const hasWorktree = t.worktree && existsSync31(t.worktree);
90962
+ const hasWorktree = t.worktree && existsSync32(t.worktree);
90261
90963
  const graceMs = hasWorktree ? ORPHANED_WITH_WORKTREE_GRACE_MS : ORPHANED_EXECUTION_RECOVERY_GRACE_MS;
90262
90964
  return staleness >= graceMs;
90263
90965
  });
@@ -90266,7 +90968,7 @@ var init_self_healing = __esm({
90266
90968
  let recovered = 0;
90267
90969
  for (const task of orphaned) {
90268
90970
  try {
90269
- const hadWorktree = task.worktree && existsSync31(task.worktree);
90971
+ const hadWorktree = task.worktree && existsSync32(task.worktree);
90270
90972
  const reason = hadWorktree ? "worktree exists but no active session" : "missing worktree/session";
90271
90973
  if (this.options.leaseManager && task.checkedOutBy) {
90272
90974
  const leaseRecovered = await this.options.leaseManager.recoverAbandonedLease(
@@ -90569,7 +91271,7 @@ var init_self_healing = __esm({
90569
91271
  }
90570
91272
  }
90571
91273
  async hasRecoverableGitWork(task) {
90572
- if (task.worktree && existsSync31(task.worktree)) {
91274
+ if (task.worktree && existsSync32(task.worktree)) {
90573
91275
  try {
90574
91276
  const { stdout: status } = await execAsync7("git status --porcelain", {
90575
91277
  cwd: task.worktree,
@@ -90754,22 +91456,22 @@ var init_self_healing = __esm({
90754
91456
  * tracks registered idle worktrees, never these orphans.
90755
91457
  */
90756
91458
  async reapUnregisteredOrphans() {
90757
- const worktreesDir = join39(this.options.rootDir, ".worktrees");
90758
- if (!existsSync31(worktreesDir)) return 0;
91459
+ const worktreesDir = join40(this.options.rootDir, ".worktrees");
91460
+ if (!existsSync32(worktreesDir)) return 0;
90759
91461
  let dirs;
90760
91462
  try {
90761
- dirs = readdirSync5(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join39(worktreesDir, e.name));
91463
+ dirs = readdirSync5(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join40(worktreesDir, e.name));
90762
91464
  } catch (err) {
90763
91465
  log17.warn(`Failed to read .worktrees/ for unregistered orphan reap: ${err instanceof Error ? err.message : String(err)}`);
90764
91466
  return 0;
90765
91467
  }
90766
91468
  if (dirs.length === 0) return 0;
90767
91469
  const registered = await getRegisteredWorktreePaths(this.options.rootDir);
90768
- const unregistered = dirs.filter((d) => !registered.has(resolve19(d)));
91470
+ const unregistered = dirs.filter((d) => !registered.has(resolve20(d)));
90769
91471
  let cleaned = 0;
90770
91472
  for (const path2 of unregistered) {
90771
- const rel = relative9(worktreesDir, path2);
90772
- if (rel === "" || rel.startsWith("..") || isAbsolute13(rel)) {
91473
+ const rel = relative10(worktreesDir, path2);
91474
+ if (rel === "" || rel.startsWith("..") || isAbsolute14(rel)) {
90773
91475
  log17.warn(`Refusing to remove path outside .worktrees: ${path2}`);
90774
91476
  continue;
90775
91477
  }
@@ -90863,8 +91565,8 @@ var init_self_healing = __esm({
90863
91565
  }
90864
91566
  /** Remove oldest idle worktrees if total count exceeds 2× maxWorktrees. */
90865
91567
  async enforceWorktreeCap() {
90866
- const worktreesDir = join39(this.options.rootDir, ".worktrees");
90867
- if (!existsSync31(worktreesDir)) return;
91568
+ const worktreesDir = join40(this.options.rootDir, ".worktrees");
91569
+ if (!existsSync32(worktreesDir)) return;
90868
91570
  try {
90869
91571
  const settings = await this.store.getSettings();
90870
91572
  const cap = (settings.maxWorktrees ?? 4) * 2;
@@ -91645,13 +92347,13 @@ var init_plugin_runner = __esm({
91645
92347
  * Returns the result on success, throws on timeout.
91646
92348
  */
91647
92349
  withTimeout(promise, ms, timeoutMessage) {
91648
- return new Promise((resolve23, reject) => {
92350
+ return new Promise((resolve24, reject) => {
91649
92351
  const timer = setTimeout(() => {
91650
92352
  reject(new Error(timeoutMessage));
91651
92353
  }, ms);
91652
92354
  promise.then((result) => {
91653
92355
  clearTimeout(timer);
91654
- resolve23(result);
92356
+ resolve24(result);
91655
92357
  }).catch((err) => {
91656
92358
  clearTimeout(timer);
91657
92359
  reject(err);
@@ -92558,7 +93260,7 @@ var init_in_process_runtime = __esm({
92558
93260
  runtimeLog.log(
92559
93261
  `Waiting for ${metrics.inFlightTasks} in-flight tasks to complete...`
92560
93262
  );
92561
- await new Promise((resolve23) => setTimeout(resolve23, 1e3));
93263
+ await new Promise((resolve24) => setTimeout(resolve24, 1e3));
92562
93264
  }
92563
93265
  const finalMetrics = this.getMetrics();
92564
93266
  if (finalMetrics.inFlightTasks > 0) {
@@ -92988,13 +93690,13 @@ var init_ipc_host = __esm({
92988
93690
  }
92989
93691
  const id = generateCorrelationId();
92990
93692
  const message = { type, id, payload };
92991
- return new Promise((resolve23, reject) => {
93693
+ return new Promise((resolve24, reject) => {
92992
93694
  const timeout = setTimeout(() => {
92993
93695
  this.pendingCommands.delete(id);
92994
93696
  reject(new Error(`Command ${type} timed out after ${timeoutMs ?? this.commandTimeoutMs}ms`));
92995
93697
  }, timeoutMs ?? this.commandTimeoutMs);
92996
93698
  this.pendingCommands.set(id, {
92997
- resolve: resolve23,
93699
+ resolve: resolve24,
92998
93700
  reject,
92999
93701
  timeout,
93000
93702
  type
@@ -93072,7 +93774,7 @@ var init_ipc_host = __esm({
93072
93774
  import { EventEmitter as EventEmitter20 } from "node:events";
93073
93775
  import { fork } from "node:child_process";
93074
93776
  import { fileURLToPath as fileURLToPath3 } from "node:url";
93075
- import { dirname as dirname11, join as join40 } from "node:path";
93777
+ import { dirname as dirname12, join as join41 } from "node:path";
93076
93778
  var HealthMonitor, ChildProcessRuntime;
93077
93779
  var init_child_process_runtime = __esm({
93078
93780
  "../engine/src/runtimes/child-process-runtime.ts"() {
@@ -93232,9 +93934,9 @@ var init_child_process_runtime = __esm({
93232
93934
  */
93233
93935
  getWorkerPath() {
93234
93936
  const isCompiled = !import.meta.url.endsWith(".ts");
93235
- const currentDir = dirname11(fileURLToPath3(import.meta.url));
93937
+ const currentDir = dirname12(fileURLToPath3(import.meta.url));
93236
93938
  const workerFile = isCompiled ? "child-process-worker.js" : "child-process-worker.ts";
93237
- return join40(currentDir, workerFile);
93939
+ return join41(currentDir, workerFile);
93238
93940
  }
93239
93941
  /**
93240
93942
  * Set up event forwarding from IPC host to runtime listeners.
@@ -93803,8 +94505,8 @@ var init_remote_node_client = __esm({
93803
94505
  return error instanceof TypeError;
93804
94506
  }
93805
94507
  async sleep(ms) {
93806
- await new Promise((resolve23) => {
93807
- setTimeout(resolve23, ms);
94508
+ await new Promise((resolve24) => {
94509
+ setTimeout(resolve24, ms);
93808
94510
  });
93809
94511
  }
93810
94512
  };
@@ -94068,14 +94770,14 @@ var init_remote_node_runtime = __esm({
94068
94770
  return error instanceof Error ? error : new Error(String(error));
94069
94771
  }
94070
94772
  async sleep(ms, signal) {
94071
- await new Promise((resolve23) => {
94773
+ await new Promise((resolve24) => {
94072
94774
  const timeout = setTimeout(() => {
94073
94775
  cleanup();
94074
- resolve23();
94776
+ resolve24();
94075
94777
  }, ms);
94076
94778
  const onAbort = () => {
94077
94779
  cleanup();
94078
- resolve23();
94780
+ resolve24();
94079
94781
  };
94080
94782
  const cleanup = () => {
94081
94783
  clearTimeout(timeout);
@@ -95026,10 +95728,10 @@ var init_tunnel_process_manager = __esm({
95026
95728
  lastError: null
95027
95729
  });
95028
95730
  this.emitLog("info", "manager", `Stopping ${currentHandle.provider} tunnel (pid=${currentHandle.child.pid ?? "n/a"})`);
95029
- this.activeStopPromise = new Promise((resolve23) => {
95731
+ this.activeStopPromise = new Promise((resolve24) => {
95030
95732
  const onClose = () => {
95031
95733
  currentHandle.child.removeListener("close", onClose);
95032
- resolve23();
95734
+ resolve24();
95033
95735
  };
95034
95736
  currentHandle.child.once("close", onClose);
95035
95737
  killManagedProcess(currentHandle.child, "SIGTERM");
@@ -95302,17 +96004,25 @@ var init_project_engine = __esm({
95302
96004
  onClosedPrFeedback: (taskId, prInfo, comments) => this.prCommentHandler.createFollowUpTask(taskId, prInfo, comments)
95303
96005
  });
95304
96006
  if (!this.options.skipNotifier) {
96007
+ const agentStore = this.runtime.getAgentStore();
96008
+ const agentNameResolver = agentStore ? async (agentId) => {
96009
+ const agent = await agentStore.getAgent(agentId);
96010
+ const name = typeof agent?.name === "string" ? agent.name.trim() : "";
96011
+ return name.length > 0 ? name : null;
96012
+ } : void 0;
95305
96013
  this.notificationService = new NotificationService(store, {
95306
96014
  projectId: this.options.projectId,
95307
96015
  ntfyBaseUrl: this.options.ntfyBaseUrl,
95308
- messageStore: this.runtime.getMessageStore()
96016
+ messageStore: this.runtime.getMessageStore(),
96017
+ agentNameResolver
95309
96018
  });
95310
96019
  await this.notificationService.start();
95311
96020
  this.notifier = new NtfyNotifier(
95312
96021
  store,
95313
96022
  {
95314
96023
  projectId: this.options.projectId,
95315
- ntfyBaseUrl: this.options.ntfyBaseUrl
96024
+ ntfyBaseUrl: this.options.ntfyBaseUrl,
96025
+ agentNameResolver
95316
96026
  },
95317
96027
  this.notificationService
95318
96028
  );
@@ -95670,12 +96380,12 @@ ${detail}`
95670
96380
  */
95671
96381
  async onMerge(taskId) {
95672
96382
  if (this.mergeActive.has(taskId)) {
95673
- return new Promise((resolve23, reject) => {
95674
- this.manualMergeResolvers.set(taskId, { resolve: resolve23, reject });
96383
+ return new Promise((resolve24, reject) => {
96384
+ this.manualMergeResolvers.set(taskId, { resolve: resolve24, reject });
95675
96385
  });
95676
96386
  }
95677
- return new Promise((resolve23, reject) => {
95678
- this.manualMergeResolvers.set(taskId, { resolve: resolve23, reject });
96387
+ return new Promise((resolve24, reject) => {
96388
+ this.manualMergeResolvers.set(taskId, { resolve: resolve24, reject });
95679
96389
  this.internalEnqueueMerge(taskId);
95680
96390
  });
95681
96391
  }
@@ -95942,7 +96652,15 @@ ${detail}`
95942
96652
  }
95943
96653
  internalEnqueueMerge(taskId) {
95944
96654
  if (this.shuttingDown) return;
95945
- if (this.mergeActive.has(taskId)) return;
96655
+ if (this.mergeActive.has(taskId)) {
96656
+ const isActuallyLive = this.mergeQueue.includes(taskId) || this.activeMergeTaskId === taskId;
96657
+ if (!isActuallyLive) {
96658
+ runtimeLog.warn(
96659
+ `internalEnqueueMerge(${taskId}): skipped \u2014 mergeActive entry is leaked (not queued, not active). reconcileStaleMergeActive() will clear it on the next sweep.`
96660
+ );
96661
+ }
96662
+ return;
96663
+ }
95946
96664
  this.mergeActive.add(taskId);
95947
96665
  this.mergeQueue.push(taskId);
95948
96666
  void this.drainMergeQueue().catch((err) => {
@@ -96421,17 +97139,40 @@ ${detail}`
96421
97139
  setTimeout(async () => {
96422
97140
  try {
96423
97141
  const latestTask = await store.getTask(task.id).catch(() => null);
96424
- if (!latestTask) return;
96425
- if (latestTask.column !== "in-review") return;
96426
- if (latestTask.paused) return;
96427
- if (this.options.getTaskMergeBlocker?.(latestTask)) return;
97142
+ if (!latestTask) {
97143
+ runtimeLog.warn(`Auto-merge handoff (${task.id}): task disappeared during grace period`);
97144
+ return;
97145
+ }
97146
+ if (latestTask.column !== "in-review") {
97147
+ runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: column changed to ${latestTask.column}`);
97148
+ return;
97149
+ }
97150
+ if (latestTask.paused) {
97151
+ runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: task paused`);
97152
+ return;
97153
+ }
97154
+ const blockerReason = this.options.getTaskMergeBlocker?.(latestTask);
97155
+ if (blockerReason) {
97156
+ runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: ${blockerReason}`);
97157
+ return;
97158
+ }
96428
97159
  const settings = await store.getSettings();
96429
- if (settings.globalPause || settings.enginePaused) return;
96430
- if (!settings.autoMerge) return;
97160
+ if (settings.globalPause || settings.enginePaused) {
97161
+ runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: ${settings.globalPause ? "globalPause" : "enginePaused"} active`);
97162
+ return;
97163
+ }
97164
+ if (!settings.autoMerge) {
97165
+ runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: autoMerge disabled`);
97166
+ return;
97167
+ }
97168
+ if (this.mergeActive.has(task.id) && !this.mergeQueue.includes(task.id) && this.activeMergeTaskId !== task.id) {
97169
+ runtimeLog.warn(`Auto-merge handoff (${task.id}): clearing stale mergeActive before enqueue`);
97170
+ this.mergeActive.delete(task.id);
97171
+ }
96431
97172
  this.internalEnqueueMerge(task.id);
96432
97173
  } catch (err) {
96433
97174
  runtimeLog.warn(
96434
- `Auto-merge: failed to read settings for task:moved on ${task.id}: ${err instanceof Error ? err.message : String(err)}`
97175
+ `Auto-merge handoff (${task.id}) failed: ${err instanceof Error ? err.message : String(err)}`
96435
97176
  );
96436
97177
  }
96437
97178
  }, MERGE_HANDOFF_GRACE_MS);
@@ -99674,7 +100415,7 @@ function buildHermesArgs(prompt, settings, resumeSessionId) {
99674
100415
  async function invokeHermesCli(prompt, settings, resumeSessionId, signal) {
99675
100416
  const args = buildHermesArgs(prompt, settings, resumeSessionId);
99676
100417
  const binary = resolveBinaryForSpawn(settings.binaryPath);
99677
- return new Promise((resolve23, reject) => {
100418
+ return new Promise((resolve24, reject) => {
99678
100419
  let settled = false;
99679
100420
  const spawnEnv = { ...process.env, PYTHONUNBUFFERED: "1" };
99680
100421
  if (settings.profile) {
@@ -99742,7 +100483,7 @@ ${combined}`));
99742
100483
  return;
99743
100484
  }
99744
100485
  try {
99745
- resolve23(parseHermesOutput(stdout, stderr));
100486
+ resolve24(parseHermesOutput(stdout, stderr));
99746
100487
  } catch (parseErr) {
99747
100488
  reject(parseErr);
99748
100489
  }
@@ -99772,37 +100513,37 @@ var init_cli_spawn = __esm({
99772
100513
  });
99773
100514
 
99774
100515
  // ../../plugins/fusion-plugin-hermes-runtime/dist/fusion-skill-install.js
99775
- import { cpSync, existsSync as existsSync32, lstatSync as lstatSync2, mkdirSync as mkdirSync6, readFileSync as readFileSync12, readlinkSync, rmSync as rmSync4, symlinkSync, unlinkSync as unlinkSync2 } from "node:fs";
100516
+ import { cpSync, existsSync as existsSync33, lstatSync as lstatSync2, mkdirSync as mkdirSync6, readFileSync as readFileSync12, readlinkSync, rmSync as rmSync4, symlinkSync, unlinkSync as unlinkSync2 } from "node:fs";
99776
100517
  import { homedir as homedir7 } from "node:os";
99777
- import { basename as basename8, dirname as dirname12, join as join41, resolve as resolve20 } from "node:path";
100518
+ import { basename as basename8, dirname as dirname13, join as join42, resolve as resolve21 } from "node:path";
99778
100519
  import { fileURLToPath as fileURLToPath4 } from "node:url";
99779
100520
  function resolveHermesHome(profile) {
99780
- const base = process.env.HERMES_HOME ?? join41(homedir7(), ".hermes");
100521
+ const base = process.env.HERMES_HOME ?? join42(homedir7(), ".hermes");
99781
100522
  if (!profile || profile === "default")
99782
100523
  return base;
99783
- return join41(base, "profiles", profile);
100524
+ return join42(base, "profiles", profile);
99784
100525
  }
99785
100526
  function getFusionSkillSourceCandidates(moduleUrl = import.meta.url) {
99786
100527
  const here = fileURLToPath4(moduleUrl);
99787
- const moduleDir = dirname12(here);
100528
+ const moduleDir = dirname13(here);
99788
100529
  return [
99789
- resolve20(moduleDir, "..", "..", "..", "..", "packages", "cli", "skill", FUSION_SKILL_NAME),
99790
- resolve20(moduleDir, "..", "..", "..", "skill", FUSION_SKILL_NAME),
99791
- resolve20(moduleDir, "..", "..", "skill", FUSION_SKILL_NAME),
99792
- resolve20(moduleDir, "..", "..", "..", "..", "skill", FUSION_SKILL_NAME)
100530
+ resolve21(moduleDir, "..", "..", "..", "..", "packages", "cli", "skill", FUSION_SKILL_NAME),
100531
+ resolve21(moduleDir, "..", "..", "..", "skill", FUSION_SKILL_NAME),
100532
+ resolve21(moduleDir, "..", "..", "skill", FUSION_SKILL_NAME),
100533
+ resolve21(moduleDir, "..", "..", "..", "..", "skill", FUSION_SKILL_NAME)
99793
100534
  ];
99794
100535
  }
99795
100536
  function resolveBundledFusionSkillSource() {
99796
100537
  const candidates = getFusionSkillSourceCandidates();
99797
100538
  for (const candidate of candidates) {
99798
- if (existsSync32(join41(candidate, "SKILL.md")))
100539
+ if (existsSync33(join42(candidate, "SKILL.md")))
99799
100540
  return candidate;
99800
100541
  }
99801
100542
  return null;
99802
100543
  }
99803
100544
  function installFusionSkillIntoHermesHome(options = {}) {
99804
100545
  const sourceDir = options.sourceDir ?? resolveBundledFusionSkillSource();
99805
- const targetDir = join41(resolveHermesHome(options.profile), "skills", FUSION_SKILL_NAME);
100546
+ const targetDir = join42(resolveHermesHome(options.profile), "skills", FUSION_SKILL_NAME);
99806
100547
  if (!sourceDir) {
99807
100548
  return {
99808
100549
  outcome: "warning",
@@ -99812,16 +100553,16 @@ function installFusionSkillIntoHermesHome(options = {}) {
99812
100553
  };
99813
100554
  }
99814
100555
  try {
99815
- mkdirSync6(dirname12(targetDir), { recursive: true });
100556
+ mkdirSync6(dirname13(targetDir), { recursive: true });
99816
100557
  let replaced = false;
99817
- if (existsSync32(targetDir) || isBrokenSymlink(targetDir)) {
99818
- const stat8 = lstatSync2(targetDir);
99819
- if (stat8.isSymbolicLink()) {
100558
+ if (existsSync33(targetDir) || isBrokenSymlink(targetDir)) {
100559
+ const stat9 = lstatSync2(targetDir);
100560
+ if (stat9.isSymbolicLink()) {
99820
100561
  const currentTarget = safeReadlink(targetDir);
99821
- if (currentTarget && resolve20(dirname12(targetDir), currentTarget) === resolve20(sourceDir)) {
100562
+ if (currentTarget && resolve21(dirname13(targetDir), currentTarget) === resolve21(sourceDir)) {
99822
100563
  return { outcome: "already-installed", sourceDir, targetDir };
99823
100564
  }
99824
- if (!looksLikeFusionSkillTarget(resolve20(dirname12(targetDir), currentTarget ?? ""))) {
100565
+ if (!looksLikeFusionSkillTarget(resolve21(dirname13(targetDir), currentTarget ?? ""))) {
99825
100566
  return {
99826
100567
  outcome: "skipped",
99827
100568
  sourceDir,
@@ -99884,15 +100625,15 @@ function safeReadlink(path2) {
99884
100625
  }
99885
100626
  function isBrokenSymlink(path2) {
99886
100627
  try {
99887
- const stat8 = lstatSync2(path2);
99888
- return stat8.isSymbolicLink() && !existsSync32(path2);
100628
+ const stat9 = lstatSync2(path2);
100629
+ return stat9.isSymbolicLink() && !existsSync33(path2);
99889
100630
  } catch {
99890
100631
  return false;
99891
100632
  }
99892
100633
  }
99893
100634
  function looksLikePriorFusionInstall(path2) {
99894
- const skillMd = join41(path2, "SKILL.md");
99895
- if (!existsSync32(skillMd))
100635
+ const skillMd = join42(path2, "SKILL.md");
100636
+ if (!existsSync33(skillMd))
99896
100637
  return false;
99897
100638
  try {
99898
100639
  const body = readFileSync12(skillMd, "utf-8");
@@ -99906,7 +100647,7 @@ function looksLikeFusionSkillTarget(path2) {
99906
100647
  return false;
99907
100648
  if (basename8(path2).toLowerCase() === FUSION_SKILL_NAME)
99908
100649
  return true;
99909
- return existsSync32(join41(path2, "SKILL.md"));
100650
+ return existsSync33(join42(path2, "SKILL.md"));
99910
100651
  }
99911
100652
  var FUSION_SKILL_NAME;
99912
100653
  var init_fusion_skill_install = __esm({
@@ -100069,7 +100810,7 @@ var init_dist = __esm({
100069
100810
  // ../../plugins/fusion-plugin-openclaw-runtime/dist/pi-module.js
100070
100811
  import { spawn as spawn7 } from "node:child_process";
100071
100812
  import { randomUUID as randomUUID18 } from "node:crypto";
100072
- import { readFile as readFile19 } from "node:fs/promises";
100813
+ import { readFile as readFile20 } from "node:fs/promises";
100073
100814
  function asString(v) {
100074
100815
  return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
100075
100816
  }
@@ -100146,8 +100887,8 @@ function createCliSession(opts) {
100146
100887
  };
100147
100888
  }
100148
100889
  async function configureOpenClawMcpServer(opts) {
100149
- const serverValue = await readFile19(opts.serverConfigPath, "utf-8");
100150
- await new Promise((resolve23, reject) => {
100890
+ const serverValue = await readFile20(opts.serverConfigPath, "utf-8");
100891
+ await new Promise((resolve24, reject) => {
100151
100892
  const child = spawn7(opts.binaryPath, ["--no-color", "--profile", opts.profile, "mcp", "set", opts.serverName, serverValue], {
100152
100893
  stdio: ["ignore", "pipe", "pipe"]
100153
100894
  });
@@ -100158,7 +100899,7 @@ async function configureOpenClawMcpServer(opts) {
100158
100899
  child.on("error", (err) => reject(new Error(`openclaw: failed to configure MCP server \u2014 ${err.message}`)));
100159
100900
  child.on("close", (code) => {
100160
100901
  if (code === 0) {
100161
- resolve23();
100902
+ resolve24();
100162
100903
  return;
100163
100904
  }
100164
100905
  reject(new Error(`openclaw: mcp set failed (${String(code)}): ${extractStderrError(stderr)}`));
@@ -100169,7 +100910,7 @@ async function promptCli(session, message, config, callbacks, signal) {
100169
100910
  const args = buildOpenClawArgs(config, session, message);
100170
100911
  const cb = { ...session.callbacks, ...callbacks };
100171
100912
  cb.onToolStart?.("openclaw.agent", { sessionId: session.sessionId });
100172
- return new Promise((resolve23, reject) => {
100913
+ return new Promise((resolve24, reject) => {
100173
100914
  let settled = false;
100174
100915
  const child = spawn7(config.binaryPath, args, {
100175
100916
  stdio: ["ignore", "pipe", "pipe"]
@@ -100262,7 +101003,7 @@ async function promptCli(session, message, config, callbacks, signal) {
100262
101003
  ...metaError ? { error: metaError } : {},
100263
101004
  ...errorText.length > 0 ? { toolErrors: errorText } : {}
100264
101005
  });
100265
- resolve23();
101006
+ resolve24();
100266
101007
  });
100267
101008
  });
100268
101009
  }
@@ -100285,9 +101026,9 @@ var init_pi_module = __esm({
100285
101026
  // ../../plugins/fusion-plugin-openclaw-runtime/dist/mcp-config.js
100286
101027
  import { writeFileSync as writeFileSync3 } from "node:fs";
100287
101028
  import { tmpdir as tmpdir4 } from "node:os";
100288
- import { join as join42 } from "node:path";
101029
+ import { join as join43 } from "node:path";
100289
101030
  import { fileURLToPath as fileURLToPath5 } from "node:url";
100290
- import { dirname as dirname13 } from "node:path";
101031
+ import { dirname as dirname14 } from "node:path";
100291
101032
  function toolsToMcpToolDefs(tools) {
100292
101033
  if (!Array.isArray(tools))
100293
101034
  return [];
@@ -100299,16 +101040,16 @@ function toolsToMcpToolDefs(tools) {
100299
101040
  }
100300
101041
  function writeOpenClawMcpBridgeFiles(toolDefs, cacheKey) {
100301
101042
  const suffix = cacheKey ? `${process.pid}-${cacheKey}` : `${process.pid}`;
100302
- const schemaPath = join42(tmpdir4(), `openclaw-runtime-mcp-schemas-${suffix}.json`);
101043
+ const schemaPath = join43(tmpdir4(), `openclaw-runtime-mcp-schemas-${suffix}.json`);
100303
101044
  writeFileSync3(schemaPath, JSON.stringify(toolDefs));
100304
101045
  const __filename = fileURLToPath5(import.meta.url);
100305
- const __dirname2 = dirname13(__filename);
100306
- const serverPath = join42(__dirname2, "mcp-schema-server.cjs");
101046
+ const __dirname2 = dirname14(__filename);
101047
+ const serverPath = join43(__dirname2, "mcp-schema-server.cjs");
100307
101048
  const serverConfig = {
100308
101049
  command: "node",
100309
101050
  args: [serverPath, schemaPath]
100310
101051
  };
100311
- const serverConfigPath = join42(tmpdir4(), `openclaw-runtime-mcp-server-${suffix}.json`);
101052
+ const serverConfigPath = join43(tmpdir4(), `openclaw-runtime-mcp-server-${suffix}.json`);
100312
101053
  writeFileSync3(serverConfigPath, JSON.stringify(serverConfig));
100313
101054
  return {
100314
101055
  schemaPath,
@@ -100553,7 +101294,7 @@ var init_context = __esm({
100553
101294
 
100554
101295
  // ../dashboard/src/github.ts
100555
101296
  function delay2(ms) {
100556
- return new Promise((resolve23) => setTimeout(resolve23, ms));
101297
+ return new Promise((resolve24) => setTimeout(resolve24, ms));
100557
101298
  }
100558
101299
  function normalizeCheckState(state) {
100559
101300
  switch ((state ?? "").toLowerCase()) {
@@ -107071,10 +107812,10 @@ var require_browser3 = __commonJS({
107071
107812
  text = canvas;
107072
107813
  canvas = void 0;
107073
107814
  }
107074
- return new Promise(function(resolve23, reject) {
107815
+ return new Promise(function(resolve24, reject) {
107075
107816
  try {
107076
107817
  const data = QRCode2.create(text, opts);
107077
- resolve23(renderFunc(data, canvas, opts));
107818
+ resolve24(renderFunc(data, canvas, opts));
107078
107819
  } catch (e) {
107079
107820
  reject(e);
107080
107821
  }
@@ -107156,11 +107897,11 @@ var require_server = __commonJS({
107156
107897
  }
107157
107898
  function render(renderFunc, text, params) {
107158
107899
  if (!params.cb) {
107159
- return new Promise(function(resolve23, reject) {
107900
+ return new Promise(function(resolve24, reject) {
107160
107901
  try {
107161
107902
  const data = QRCode2.create(text, params.opts);
107162
107903
  return renderFunc(data, params.opts, function(err, data2) {
107163
- return err ? reject(err) : resolve23(data2);
107904
+ return err ? reject(err) : resolve24(data2);
107164
107905
  });
107165
107906
  } catch (e) {
107166
107907
  reject(e);
@@ -107621,7 +108362,7 @@ var init_exec_file = __esm({
107621
108362
 
107622
108363
  // ../dashboard/src/routes/register-project-routes.ts
107623
108364
  import * as fsPromises from "node:fs/promises";
107624
- var access5, stat6, mkdir13, readdir8, rm2;
108365
+ var access5, stat7, mkdir14, readdir9, rm3;
107625
108366
  var init_register_project_routes = __esm({
107626
108367
  "../dashboard/src/routes/register-project-routes.ts"() {
107627
108368
  "use strict";
@@ -107631,10 +108372,10 @@ var init_register_project_routes = __esm({
107631
108372
  init_project_store_resolver();
107632
108373
  ({
107633
108374
  access: access5,
107634
- stat: stat6,
107635
- mkdir: mkdir13,
107636
- readdir: readdir8,
107637
- rm: rm2
108375
+ stat: stat7,
108376
+ mkdir: mkdir14,
108377
+ readdir: readdir9,
108378
+ rm: rm3
107638
108379
  } = fsPromises);
107639
108380
  }
107640
108381
  });
@@ -107794,7 +108535,7 @@ var init_agent_generation = __esm({
107794
108535
 
107795
108536
  // ../dashboard/src/routes/register-agent-import-export-generation-routes.ts
107796
108537
  import * as fsPromises2 from "node:fs/promises";
107797
- var mkdtemp, access6, stat7, mkdir14, rm3, fsWriteFile;
108538
+ var mkdtemp, access6, stat8, mkdir15, rm4, fsWriteFile;
107798
108539
  var init_register_agent_import_export_generation_routes = __esm({
107799
108540
  "../dashboard/src/routes/register-agent-import-export-generation-routes.ts"() {
107800
108541
  "use strict";
@@ -107802,7 +108543,7 @@ var init_register_agent_import_export_generation_routes = __esm({
107802
108543
  init_ai_session_diagnostics();
107803
108544
  init_sse_buffer();
107804
108545
  init_agent_generation();
107805
- ({ mkdtemp, access: access6, stat: stat7, mkdir: mkdir14, rm: rm3, writeFile: fsWriteFile } = fsPromises2);
108546
+ ({ mkdtemp, access: access6, stat: stat8, mkdir: mkdir15, rm: rm4, writeFile: fsWriteFile } = fsPromises2);
107806
108547
  }
107807
108548
  });
107808
108549
 
@@ -107889,7 +108630,7 @@ var init_droid_cli_probe = __esm({
107889
108630
  // ../../plugins/fusion-plugin-cursor-runtime/src/cli-spawn.ts
107890
108631
  import { spawn as spawn10 } from "node:child_process";
107891
108632
  async function runCursorCommand(binary, args, timeoutMs) {
107892
- return new Promise((resolve23) => {
108633
+ return new Promise((resolve24) => {
107893
108634
  const child = spawn10(binary, args, { stdio: ["ignore", "pipe", "pipe"] });
107894
108635
  let stdout = "";
107895
108636
  let stderr = "";
@@ -107898,7 +108639,7 @@ async function runCursorCommand(binary, args, timeoutMs) {
107898
108639
  child.kill("SIGKILL");
107899
108640
  } catch {
107900
108641
  }
107901
- resolve23({ code: 124, stdout, stderr });
108642
+ resolve24({ code: 124, stdout, stderr });
107902
108643
  }, timeoutMs);
107903
108644
  child.stdout?.on("data", (c) => {
107904
108645
  stdout += c.toString("utf-8");
@@ -107908,11 +108649,11 @@ async function runCursorCommand(binary, args, timeoutMs) {
107908
108649
  });
107909
108650
  child.once("error", () => {
107910
108651
  clearTimeout(timer);
107911
- resolve23({ code: 127, stdout, stderr });
108652
+ resolve24({ code: 127, stdout, stderr });
107912
108653
  });
107913
108654
  child.once("close", (code) => {
107914
108655
  clearTimeout(timer);
107915
- resolve23({ code, stdout, stderr });
108656
+ resolve24({ code, stdout, stderr });
107916
108657
  });
107917
108658
  });
107918
108659
  }
@@ -108411,7 +109152,7 @@ async function spawnPaperclipCliJson(args, opts) {
108411
109152
  }
108412
109153
  const timeoutMs = opts.cliTimeoutMs ?? 15e3;
108413
109154
  const label = ["paperclipai", ...args].join(" ");
108414
- return new Promise((resolve23, reject) => {
109155
+ return new Promise((resolve24, reject) => {
108415
109156
  let child;
108416
109157
  try {
108417
109158
  child = spawn13(bin, fullArgs, { stdio: ["ignore", "pipe", "pipe"] });
@@ -108457,7 +109198,7 @@ async function spawnPaperclipCliJson(args, opts) {
108457
109198
  return;
108458
109199
  }
108459
109200
  try {
108460
- resolve23(JSON.parse(cleaned));
109201
+ resolve24(JSON.parse(cleaned));
108461
109202
  } catch {
108462
109203
  reject(new Error(`${label} returned non-JSON output: ${cleaned.slice(0, 200)}`));
108463
109204
  }
@@ -108527,7 +109268,7 @@ var init_paperclip_client = __esm({
108527
109268
  // ../../plugins/fusion-plugin-paperclip-runtime/dist/runtime-adapter.js
108528
109269
  import { randomUUID as randomUUID21 } from "node:crypto";
108529
109270
  function sleep4(ms) {
108530
- return new Promise((resolve23) => setTimeout(resolve23, ms));
109271
+ return new Promise((resolve24) => setTimeout(resolve24, ms));
108531
109272
  }
108532
109273
  function asString2(value) {
108533
109274
  return typeof value === "string" ? value : void 0;
@@ -113048,7 +113789,7 @@ var init_auth_middleware = __esm({
113048
113789
 
113049
113790
  // ../dashboard/src/server.ts
113050
113791
  import express from "express";
113051
- import { join as join43, dirname as dirname14 } from "node:path";
113792
+ import { join as join44, dirname as dirname15 } from "node:path";
113052
113793
  import { fileURLToPath as fileURLToPath6 } from "node:url";
113053
113794
  function clearAiSessionCleanupInterval() {
113054
113795
  if (!aiSessionCleanupIntervalHandle) {
@@ -113084,7 +113825,7 @@ var init_server = __esm({
113084
113825
  init_auth_middleware();
113085
113826
  init_remote_auth();
113086
113827
  init_cli_package_version();
113087
- __dirname = dirname14(fileURLToPath6(import.meta.url));
113828
+ __dirname = dirname15(fileURLToPath6(import.meta.url));
113088
113829
  MIN_AI_SESSION_TTL_MS = 10 * 60 * 1e3;
113089
113830
  MAX_AI_SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
113090
113831
  MIN_AI_SESSION_CLEANUP_INTERVAL_MS = 60 * 1e3;
@@ -113156,7 +113897,7 @@ var init_src5 = __esm({
113156
113897
  });
113157
113898
 
113158
113899
  // src/project-context.ts
113159
- import { resolve as resolve21, dirname as dirname15, basename as basename9 } from "node:path";
113900
+ import { resolve as resolve22, dirname as dirname16, basename as basename9 } from "node:path";
113160
113901
  async function resolveProject(projectNameFlag, cwd = process.cwd(), globalDir) {
113161
113902
  const central = new CentralCore(globalDir);
113162
113903
  await central.init();
@@ -113232,10 +113973,10 @@ async function clearDefaultProject(globalDir) {
113232
113973
  await globalStore.updateSettings(rest);
113233
113974
  }
113234
113975
  async function detectProjectFromCwd(cwd, central) {
113235
- const startDir = resolve21(cwd);
113976
+ const startDir = resolve22(cwd);
113236
113977
  let currentDir = startDir;
113237
113978
  while (true) {
113238
- const kbPath = resolve21(currentDir, ".fusion", "fusion.db");
113979
+ const kbPath = resolve22(currentDir, ".fusion", "fusion.db");
113239
113980
  if (isValidSqliteDatabaseFile(kbPath)) {
113240
113981
  const project = await central.getProjectByPath(currentDir);
113241
113982
  if (project) {
@@ -113249,7 +113990,7 @@ async function detectProjectFromCwd(cwd, central) {
113249
113990
  };
113250
113991
  }
113251
113992
  }
113252
- const parentDir = dirname15(currentDir);
113993
+ const parentDir = dirname16(currentDir);
113253
113994
  if (parentDir === currentDir) {
113254
113995
  break;
113255
113996
  }
@@ -113340,8 +114081,8 @@ __export(task_exports, {
113340
114081
  runTaskUpdate: () => runTaskUpdate
113341
114082
  });
113342
114083
  import { createInterface as createInterface2 } from "node:readline/promises";
113343
- import { watchFile, unwatchFile, statSync as statSync6, existsSync as existsSync33, readFileSync as readFileSync13 } from "node:fs";
113344
- import { basename as basename10, join as join44 } from "node:path";
114084
+ import { watchFile, unwatchFile, statSync as statSync6, existsSync as existsSync34, readFileSync as readFileSync13 } from "node:fs";
114085
+ import { basename as basename10, join as join45 } from "node:path";
113345
114086
  function getGitHubIssueUrl(sourceMetadata) {
113346
114087
  if (!sourceMetadata || typeof sourceMetadata !== "object") return void 0;
113347
114088
  const issueUrl = sourceMetadata.issueUrl;
@@ -113517,10 +114258,10 @@ async function runTaskCreate(descriptionArg, attachFiles, depends, projectName,
113517
114258
  }
113518
114259
  console.log(` Path: .fusion/tasks/${task.id}/`);
113519
114260
  if (attachFiles && attachFiles.length > 0) {
113520
- const { readFile: readFile22 } = await import("node:fs/promises");
113521
- const { basename: basename12, extname: extname4, resolve: resolve23 } = await import("node:path");
114261
+ const { readFile: readFile23 } = await import("node:fs/promises");
114262
+ const { basename: basename12, extname: extname4, resolve: resolve24 } = await import("node:path");
113522
114263
  for (const filePath of attachFiles) {
113523
- const resolvedPath = resolve23(filePath);
114264
+ const resolvedPath = resolve24(filePath);
113524
114265
  const filename = basename12(resolvedPath);
113525
114266
  const ext = extname4(filename).toLowerCase();
113526
114267
  const mimeType = MIME_TYPES[ext];
@@ -113530,7 +114271,7 @@ async function runTaskCreate(descriptionArg, attachFiles, depends, projectName,
113530
114271
  }
113531
114272
  let content;
113532
114273
  try {
113533
- content = await readFile22(resolvedPath);
114274
+ content = await readFile23(resolvedPath);
113534
114275
  } catch {
113535
114276
  console.error(` \u2717 Cannot read file: ${filePath}`);
113536
114277
  continue;
@@ -113596,11 +114337,11 @@ async function runTaskLog(id, message, outcome, projectName) {
113596
114337
  console.log(` \u2713 ${id}: logged "${message}"`);
113597
114338
  console.log();
113598
114339
  }
113599
- function formatTimestamp3(timestamp) {
114340
+ function formatTimestamp4(timestamp) {
113600
114341
  return new Date(timestamp).toLocaleTimeString();
113601
114342
  }
113602
114343
  function formatLogEntry(entry) {
113603
- const ts = formatTimestamp3(entry.timestamp);
114344
+ const ts = formatTimestamp4(entry.timestamp);
113604
114345
  const agent = entry.agent ? `[${entry.agent.toUpperCase()}] ` : "";
113605
114346
  switch (entry.type) {
113606
114347
  case "text":
@@ -113654,8 +114395,8 @@ async function runTaskLogs(id, options = {}, projectName) {
113654
114395
  printEntries(filteredEntries);
113655
114396
  if (options.follow) {
113656
114397
  const projectPath = projectContext?.projectPath ?? process.cwd();
113657
- const logPath = join44(projectPath, ".fusion", "tasks", id, "agent.log");
113658
- if (!existsSync33(logPath)) {
114398
+ const logPath = join45(projectPath, ".fusion", "tasks", id, "agent.log");
114399
+ if (!existsSync34(logPath)) {
113659
114400
  console.log(`
113660
114401
  Waiting for log file to be created...`);
113661
114402
  }
@@ -113816,10 +114557,10 @@ async function runTaskMerge(id, projectName) {
113816
114557
  }
113817
114558
  }
113818
114559
  async function runTaskAttach(id, filePath, projectName) {
113819
- const { readFile: readFile22 } = await import("node:fs/promises");
114560
+ const { readFile: readFile23 } = await import("node:fs/promises");
113820
114561
  const { basename: basename12, extname: extname4 } = await import("node:path");
113821
- const { resolve: resolve23 } = await import("node:path");
113822
- const resolvedPath = resolve23(filePath);
114562
+ const { resolve: resolve24 } = await import("node:path");
114563
+ const resolvedPath = resolve24(filePath);
113823
114564
  const filename = basename12(resolvedPath);
113824
114565
  const ext = extname4(filename).toLowerCase();
113825
114566
  const mimeType = MIME_TYPES[ext];
@@ -113830,7 +114571,7 @@ async function runTaskAttach(id, filePath, projectName) {
113830
114571
  }
113831
114572
  let content;
113832
114573
  try {
113833
- content = await readFile22(resolvedPath);
114574
+ content = await readFile23(resolvedPath);
113834
114575
  } catch {
113835
114576
  console.error(`Cannot read file: ${filePath}`);
113836
114577
  process.exit(1);
@@ -114360,12 +115101,12 @@ async function promptText(question) {
114360
115101
  console.log(" (Enter your response. Type DONE on its own line when finished):\n");
114361
115102
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
114362
115103
  const lines = [];
114363
- return new Promise((resolve23) => {
115104
+ return new Promise((resolve24) => {
114364
115105
  const askLine = () => {
114365
115106
  rl.question(" ").then((line) => {
114366
115107
  if (line.trim() === "DONE") {
114367
115108
  rl.close();
114368
- resolve23(lines.join("\n"));
115109
+ resolve24(lines.join("\n"));
114369
115110
  } else {
114370
115111
  lines.push(line);
114371
115112
  askLine();
@@ -114770,9 +115511,9 @@ async function runSkillsInstall(args, options) {
114770
115511
  stdio: "inherit",
114771
115512
  shell: true
114772
115513
  });
114773
- const exitCode = await new Promise((resolve23, reject) => {
115514
+ const exitCode = await new Promise((resolve24, reject) => {
114774
115515
  child.on("exit", (code) => {
114775
- resolve23(code ?? 1);
115516
+ resolve24(code ?? 1);
114776
115517
  });
114777
115518
  child.on("error", (err) => {
114778
115519
  reject(err);
@@ -114800,9 +115541,9 @@ init_gh_cli();
114800
115541
  init_src2();
114801
115542
  import { Type as Type8 } from "typebox";
114802
115543
  import { StringEnum } from "@mariozechner/pi-ai";
114803
- import { resolve as resolve22, basename as basename11, extname as extname3, join as join45 } from "node:path";
114804
- import { readFile as readFile21 } from "node:fs/promises";
114805
- import { existsSync as existsSync34 } from "node:fs";
115544
+ import { resolve as resolve23, basename as basename11, extname as extname3, join as join46 } from "node:path";
115545
+ import { readFile as readFile22 } from "node:fs/promises";
115546
+ import { existsSync as existsSync35 } from "node:fs";
114806
115547
  import { spawn as spawn12 } from "node:child_process";
114807
115548
  var MIME_TYPES2 = {
114808
115549
  ".png": "image/png",
@@ -114820,14 +115561,14 @@ var MIME_TYPES2 = {
114820
115561
  ".xml": "application/xml"
114821
115562
  };
114822
115563
  function resolveProjectRoot2(cwd) {
114823
- let current = resolve22(cwd);
115564
+ let current = resolve23(cwd);
114824
115565
  while (true) {
114825
- if (existsSync34(join45(current, ".fusion"))) {
115566
+ if (existsSync35(join46(current, ".fusion"))) {
114826
115567
  return current;
114827
115568
  }
114828
- const parent = resolve22(current, "..");
115569
+ const parent = resolve23(current, "..");
114829
115570
  if (parent === current) {
114830
- return resolve22(cwd);
115571
+ return resolve23(cwd);
114831
115572
  }
114832
115573
  current = parent;
114833
115574
  }
@@ -114843,7 +115584,7 @@ async function getStore2(cwd) {
114843
115584
  return store;
114844
115585
  }
114845
115586
  function getFusionDir(cwd) {
114846
- return join45(resolveProjectRoot2(cwd), ".fusion");
115587
+ return join46(resolveProjectRoot2(cwd), ".fusion");
114847
115588
  }
114848
115589
  async function validateAssignableAgentId(cwd, agentId, task, override = false) {
114849
115590
  const { AgentStore: AgentStore2, isEphemeralAgent: isEphemeralAgent2 } = await Promise.resolve().then(() => (init_src(), src_exports));
@@ -114991,10 +115732,10 @@ async function sleepWithSignal(ms, signal) {
114991
115732
  if (ms <= 0) {
114992
115733
  return;
114993
115734
  }
114994
- await new Promise((resolve23, reject) => {
115735
+ await new Promise((resolve24, reject) => {
114995
115736
  const timer = setTimeout(() => {
114996
115737
  signal?.removeEventListener("abort", onAbort);
114997
- resolve23();
115738
+ resolve24();
114998
115739
  }, ms);
114999
115740
  const onAbort = () => {
115000
115741
  clearTimeout(timer);
@@ -115332,7 +116073,7 @@ Column: triage
115332
116073
  path: Type8.String({ description: "Path to the file to attach" })
115333
116074
  }),
115334
116075
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
115335
- const filePath = resolve22(ctx.cwd, params.path.replace(/^@/, ""));
116076
+ const filePath = resolve23(ctx.cwd, params.path.replace(/^@/, ""));
115336
116077
  const filename = basename11(filePath);
115337
116078
  const ext = extname3(filename).toLowerCase();
115338
116079
  const mimeType = MIME_TYPES2[ext];
@@ -115343,7 +116084,7 @@ Column: triage
115343
116084
  }
115344
116085
  let content;
115345
116086
  try {
115346
- content = await readFile21(filePath);
116087
+ content = await readFile22(filePath);
115347
116088
  } catch {
115348
116089
  throw new Error(`Cannot read file: ${params.path}`);
115349
116090
  }
@@ -117269,12 +118010,12 @@ ${lines.join("\n")}` }],
117269
118010
  child.stderr?.on("data", (data) => {
117270
118011
  stderr += data.toString();
117271
118012
  });
117272
- const exitCode = await new Promise((resolve23) => {
118013
+ const exitCode = await new Promise((resolve24) => {
117273
118014
  child.on("exit", (code) => {
117274
- resolve23(code ?? 1);
118015
+ resolve24(code ?? 1);
117275
118016
  });
117276
118017
  child.on("error", () => {
117277
- resolve23(1);
118018
+ resolve24(1);
117278
118019
  });
117279
118020
  });
117280
118021
  try {