@runfusion/fusion 0.74.0-beta.7 → 0.74.0-beta.9

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 (100) hide show
  1. package/dist/bin.js +367 -485
  2. package/dist/child-process-worker.js +172 -290
  3. package/dist/client/.vite/manifest.json +266 -266
  4. package/dist/client/assets/{AgentDetailView-CTdyQL11.js → AgentDetailView-DC3B06sE.js} +1 -1
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-DBa7nIQV.js → AgentPermissionPolicyEditor-BtKWfG2h.js} +1 -1
  6. package/dist/client/assets/{AgentsView-B5tss2oK.js → AgentsView-4DiJ2Htx.js} +3 -3
  7. package/dist/client/assets/{ChatView-DAKt1a2L.js → ChatView-1nqZiDRk.js} +1 -1
  8. package/dist/client/assets/{CommandCenter-yPvRo9F6.js → CommandCenter-BHZN2tf0.js} +1 -1
  9. package/dist/client/assets/{DevServerView-BTyS2owH.js → DevServerView-BwPjQxR_.js} +1 -1
  10. package/dist/client/assets/{DirectoryPicker-BjGIupNU.js → DirectoryPicker-40vH29Sy.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-CuxfbpSZ.js → DocumentsView-DI2mQkGF.js} +1 -1
  12. package/dist/client/assets/{EvalsView-DGuy8SHR.js → EvalsView-CTSt1-CF.js} +1 -1
  13. package/dist/client/assets/{ExperimentalAgentOnboardingModal-lK407ZAq.js → ExperimentalAgentOnboardingModal-Z1DJ2xFw.js} +1 -1
  14. package/dist/client/assets/{GoalsView-DAVN0ObR.js → GoalsView-BNyoKq27.js} +1 -1
  15. package/dist/client/assets/{InsightsView-BXQKcNof.js → InsightsView-zSwsbTfk.js} +1 -1
  16. package/dist/client/assets/{MemoryView-D7RKEtV7.js → MemoryView-e7R4rcQY.js} +1 -1
  17. package/dist/client/assets/{PiExtensionsManager-CrOJ57R2.js → PiExtensionsManager-DuZzMGuy.js} +1 -1
  18. package/dist/client/assets/{PluginManager-D7_evt4j.js → PluginManager--lgPTCei.js} +1 -1
  19. package/dist/client/assets/{PullRequestView-C0lRUbth.js → PullRequestView-C9KOlfV-.js} +1 -1
  20. package/dist/client/assets/ReportModal-BiXAfHbg.js +21 -0
  21. package/dist/client/assets/ReportModal-M3uh98sc.css +1 -0
  22. package/dist/client/assets/{ResearchView-e8st2Sxu.js → ResearchView-N4N7-_7C.js} +1 -1
  23. package/dist/client/assets/{SecretsView-BLzckrzb.js → SecretsView-GPt8Mw9d.js} +1 -1
  24. package/dist/client/assets/{SessionTerminal-F_GIj4gh.js → SessionTerminal-D2bnTrLK.js} +1 -1
  25. package/dist/client/assets/SettingsModal-B30rPfYC.css +1 -0
  26. package/dist/client/assets/{SettingsModal-eQbxbkLU.js → SettingsModal-D42Cph4Z.js} +4 -4
  27. package/dist/client/assets/{SettingsModal-XAddgWkF.js → SettingsModal-DLKu3CYU.js} +1 -1
  28. package/dist/client/assets/{SettingsTextareaRow-DVjUHNQp.js → SettingsTextareaRow-terwvp1e.js} +1 -1
  29. package/dist/client/assets/{SetupWizardModal-xID9yIff.js → SetupWizardModal-C-ApHZiL.js} +2 -2
  30. package/dist/client/assets/{SkillsView-BNdMGu6E.js → SkillsView-GYBeq0B-.js} +1 -1
  31. package/dist/client/assets/{TodoView-xcOjHZm1.js → TodoView-_1AmNwaZ.js} +1 -1
  32. package/dist/client/assets/{WorkflowNodeEditor-DZ2WZ-ve.js → WorkflowNodeEditor-CYf2R0ap.js} +1 -1
  33. package/dist/client/assets/{agent-import-generation-0tkW9WMt.js → agent-import-generation-BZPb1nmV.js} +1 -1
  34. package/dist/client/assets/{architectureDiagram-3BPJPVTR-DP9_UvPn.js → architectureDiagram-3BPJPVTR-C_wwNBnV.js} +1 -1
  35. package/dist/client/assets/{blockDiagram-GPEHLZMM-EgKwYMgQ.js → blockDiagram-GPEHLZMM-C7URdbgi.js} +1 -1
  36. package/dist/client/assets/{c4Diagram-AAUBKEIU-Dzv0-_xt.js → c4Diagram-AAUBKEIU-BBc8CXBz.js} +1 -1
  37. package/dist/client/assets/channel-M95yBImf.js +1 -0
  38. package/dist/client/assets/{chunk-2J33WTMH-CNmFmJlQ.js → chunk-2J33WTMH-KfVf5U4u.js} +1 -1
  39. package/dist/client/assets/{chunk-4BX2VUAB-DwtF2sl5.js → chunk-4BX2VUAB-BYzFvVsE.js} +1 -1
  40. package/dist/client/assets/{chunk-55IACEB6-DoDurT6F.js → chunk-55IACEB6-B-sxSeny.js} +1 -1
  41. package/dist/client/assets/{chunk-727SXJPM-DB87A9uH.js → chunk-727SXJPM-B0xJsLsd.js} +1 -1
  42. package/dist/client/assets/{chunk-AQP2D5EJ-ClTDkMp1.js → chunk-AQP2D5EJ-BQ9rJQCA.js} +1 -1
  43. package/dist/client/assets/{chunk-FMBD7UC4-BhY2wlYr.js → chunk-FMBD7UC4-DR-KABew.js} +1 -1
  44. package/dist/client/assets/{chunk-ND2GUHAM-BB8mzlmk.js → chunk-ND2GUHAM-C6ZnAzm4.js} +1 -1
  45. package/dist/client/assets/{chunk-QZHKN3VN-D81qQMGf.js → chunk-QZHKN3VN-zs4tZGnv.js} +1 -1
  46. package/dist/client/assets/classDiagram-4FO5ZUOK-DU8poSst.js +1 -0
  47. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DU8poSst.js +1 -0
  48. package/dist/client/assets/{cose-bilkent-S5V4N54A-CCFUHBJX.js → cose-bilkent-S5V4N54A-DnljjIm-.js} +1 -1
  49. package/dist/client/assets/{dagre-BM42HDAG-xr9MLhRA.js → dagre-BM42HDAG-CEX8DHCo.js} +1 -1
  50. package/dist/client/assets/{dashboard-view-RXKFv3QV.js → dashboard-view-Chxgvuq4.js} +1 -1
  51. package/dist/client/assets/{dashboard-view-lqwQ-pMo.js → dashboard-view-DPCI6MD3.js} +1 -1
  52. package/dist/client/assets/{dashboard-view-oTFt91I3.js → dashboard-view-D_xtIayp.js} +1 -1
  53. package/dist/client/assets/{diagram-2AECGRRQ-mN_tYnBs.js → diagram-2AECGRRQ-Dl9VFNxu.js} +1 -1
  54. package/dist/client/assets/{diagram-5GNKFQAL-xznlq-5C.js → diagram-5GNKFQAL-BPvDRIKP.js} +1 -1
  55. package/dist/client/assets/{diagram-KO2AKTUF-ClRlj1ZA.js → diagram-KO2AKTUF-c1v9YI9G.js} +1 -1
  56. package/dist/client/assets/{diagram-LMA3HP47-CNiPIQqT.js → diagram-LMA3HP47-ChVDgN1K.js} +1 -1
  57. package/dist/client/assets/{diagram-OG6HWLK6-DwQD-W0P.js → diagram-OG6HWLK6-Cns2RBoA.js} +1 -1
  58. package/dist/client/assets/{erDiagram-TEJ5UH35-ZXkiCm8S.js → erDiagram-TEJ5UH35-D33nn5Lx.js} +1 -1
  59. package/dist/client/assets/{flowDiagram-I6XJVG4X-DI5haEH9.js → flowDiagram-I6XJVG4X-BluS50at.js} +1 -1
  60. package/dist/client/assets/{folder-open-NenyCkrb.js → folder-open-oeYf8Ijc.js} +1 -1
  61. package/dist/client/assets/{ganttDiagram-6RSMTGT7-BRHJpfOZ.js → ganttDiagram-6RSMTGT7-D78LznXX.js} +1 -1
  62. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-iXT_YBAZ.js → gitGraphDiagram-PVQCEYII-Cz-VVeuo.js} +1 -1
  63. package/dist/client/assets/index-Clr90mLV.js +2675 -0
  64. package/dist/client/assets/index-DRZtPb3i.css +1 -0
  65. package/dist/client/assets/{infoDiagram-5YYISTIA-BzkuQtqZ.js → infoDiagram-5YYISTIA-DW64QGM7.js} +1 -1
  66. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-5hnPG_98.js → ishikawaDiagram-YF4QCWOH-zB3QANvt.js} +1 -1
  67. package/dist/client/assets/{journeyDiagram-JHISSGLW-Co3fnxbr.js → journeyDiagram-JHISSGLW-nVRvkAiF.js} +1 -1
  68. package/dist/client/assets/{kanban-definition-UN3LZRKU-PZFl_y-h.js → kanban-definition-UN3LZRKU-DIsRSNcB.js} +1 -1
  69. package/dist/client/assets/{mermaid.core-CJRKeU9s.js → mermaid.core-ByWbtMdM.js} +4 -4
  70. package/dist/client/assets/{mindmap-definition-RKZ34NQL-B15e2rkx.js → mindmap-definition-RKZ34NQL-BbZZGyk5.js} +1 -1
  71. package/dist/client/assets/{pieDiagram-4H26LBE5-kGF592D_.js → pieDiagram-4H26LBE5-xEuGzir-.js} +1 -1
  72. package/dist/client/assets/{puzzle-Dgc4yzjO.js → puzzle-DfKZ6p_i.js} +1 -1
  73. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CUShcj70.js → quadrantDiagram-W4KKPZXB-CXU0exeD.js} +1 -1
  74. package/dist/client/assets/{requirementDiagram-4Y6WPE33-D2k4nLqQ.js → requirementDiagram-4Y6WPE33-xrqjvySl.js} +1 -1
  75. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CA03BxhT.js → sankeyDiagram-5OEKKPKP-B_XL-dlB.js} +1 -1
  76. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DDuFN2Pi.js → sequenceDiagram-3UESZ5HK-yPNpJSyq.js} +1 -1
  77. package/dist/client/assets/{shield-alert-BlYWqolM.js → shield-alert-C0mvrTVY.js} +1 -1
  78. package/dist/client/assets/{standing-instructions-template-ujl7UARV.js → standing-instructions-template-BA-Y897i.js} +1 -1
  79. package/dist/client/assets/{stateDiagram-AJRCARHV-yJOkHK4Q.js → stateDiagram-AJRCARHV-B3WacriB.js} +1 -1
  80. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-BgErHdAN.js → stateDiagram-v2-BHNVJYJU-C2J3lqDw.js} +1 -1
  81. package/dist/client/assets/{timeline-definition-PNZ67QCA-BUiDQw21.js → timeline-definition-PNZ67QCA-BMDzGW-H.js} +1 -1
  82. package/dist/client/assets/{upload-DmlZvpMp.js → upload-BcE2JLKs.js} +1 -1
  83. package/dist/client/assets/{users-Cx6ACt-W.js → users-CG9A3bIX.js} +1 -1
  84. package/dist/client/assets/{vennDiagram-CIIHVFJN-DqyIjBY8.js → vennDiagram-CIIHVFJN-C7ZUUi6G.js} +1 -1
  85. package/dist/client/assets/{wardley-L42UT6IY-C93tXAKH.js → wardley-L42UT6IY-zpz98dmH.js} +1 -1
  86. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DmH6-GJV.js → wardleyDiagram-YWT4CUSO-D4TOrUW4.js} +1 -1
  87. package/dist/client/assets/{xychartDiagram-2RQKCTM6-BPwjE4Su.js → xychartDiagram-2RQKCTM6-CDfiiyNj.js} +1 -1
  88. package/dist/client/index.html +4 -3
  89. package/dist/client/theme-data.css +85 -1
  90. package/dist/client/version.json +1 -1
  91. package/dist/extension.js +244 -362
  92. package/package.json +1 -1
  93. package/dist/client/assets/ReportModal-Bg-XvWay.css +0 -1
  94. package/dist/client/assets/ReportModal-Cy5fRNDK.js +0 -21
  95. package/dist/client/assets/SettingsModal-KnUd0942.css +0 -1
  96. package/dist/client/assets/channel-BdTAZBkX.js +0 -1
  97. package/dist/client/assets/classDiagram-4FO5ZUOK-CuoGzmwo.js +0 -1
  98. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CuoGzmwo.js +0 -1
  99. package/dist/client/assets/index-CITV-wDq.css +0 -1
  100. package/dist/client/assets/index-CrAJA2v0.js +0 -2675
package/dist/extension.js CHANGED
@@ -787,6 +787,11 @@ var init_execution_and_ui = __esm({
787
787
  "dawn",
788
788
  // FNXC:DashboardTheming 2026-07-31-21:28: Sage must stay order-synchronized across core, selector metadata, and both first-paint validators so saved preferences survive pre-hydration validation.
789
789
  "sage",
790
+ /*
791
+ FNXC:MidnightTheme 2026-08-03-02:05:
792
+ Midnight is a persisted selectable dashboard theme. Keep its id and order synchronized with selector metadata and both web/Electron first-paint validators so saved choices survive before hydration, while shadcn-ember remains the unset default.
793
+ */
794
+ "midnight",
790
795
  // FNXC:DashboardTheming 2026-07-31-20:39: Factory Dark must stay order-synchronized across core, selector metadata, and both first-paint validators so saved preferences survive pre-hydration validation.
791
796
  "factory-dark",
792
797
  // FNXC:DashboardTheming 2026-07-31-23:51: Factory Light must stay order-synchronized across core, selector metadata, and both first-paint validators so saved preferences survive pre-hydration validation.
@@ -96618,15 +96623,20 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
96618
96623
  task.workspaceWorktrees = updates.workspaceWorktrees;
96619
96624
  }
96620
96625
  let movedToTriage = false;
96626
+ let respecifyFromColumn;
96627
+ let respecifyMoveLanes;
96621
96628
  if (updates.dependencies !== void 0) {
96622
96629
  const oldDeps = new Set((task.dependencies ?? []).map((dependency) => dependency.trim()).filter(Boolean));
96623
96630
  const normalizedDependencies = updates.dependencies.map((dependency) => dependency.trim()).filter(Boolean);
96624
96631
  const hasNewDeps = normalizedDependencies.some((d) => !oldDeps.has(d));
96625
96632
  task.dependencies = normalizedDependencies;
96626
- const depLanes = hasNewDeps ? await resolveTaskLifecycleColumns(store, id).catch(() => void 0) : void 0;
96633
+ const respecifyIr = hasNewDeps ? await resolveWorkflowIrForTask(store, id).catch(() => void 0) : void 0;
96634
+ respecifyMoveLanes = toTaskMoveLanes(respecifyIr);
96635
+ const depLanes = respecifyIr ? resolveLifecycleColumns(respecifyIr) : void 0;
96627
96636
  const holdLane = depLanes === void 0 ? "todo" : depLanes.hold;
96628
96637
  if (hasNewDeps && holdLane !== void 0 && task.column === holdLane) {
96629
96638
  const intakeLane = depLanes?.intake;
96639
+ respecifyFromColumn = task.column;
96630
96640
  const relocating = intakeLane !== void 0 && intakeLane !== task.column;
96631
96641
  if (relocating) {
96632
96642
  task.column = intakeLane;
@@ -97304,10 +97314,15 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
97304
97314
  storeLog.warn(`[task-detail] failed to sync PROMPT.md heading for ${task.id}: ${err instanceof Error ? err.message : String(err)}`);
97305
97315
  }
97306
97316
  }
97307
- if (movedToTriage) {
97308
- const lanes = toTaskMoveLanes(await resolveWorkflowIrForTask(store, id).catch(() => void 0));
97309
- store.laneCache.set(task.id, lanes);
97310
- store.emit("task:moved", { task, from: "todo", to: "triage", source: "engine", lanes });
97317
+ if (movedToTriage && respecifyFromColumn !== void 0 && respecifyFromColumn !== task.column && respecifyMoveLanes) {
97318
+ store.laneCache.set(task.id, respecifyMoveLanes);
97319
+ store.emit("task:moved", {
97320
+ task,
97321
+ from: respecifyFromColumn,
97322
+ to: task.column,
97323
+ source: "engine",
97324
+ lanes: respecifyMoveLanes
97325
+ });
97311
97326
  }
97312
97327
  store.emitTaskLifecycleEventSafely("task:updated", [task]);
97313
97328
  return task;
@@ -97319,7 +97334,6 @@ var init_task_update = __esm({
97319
97334
  init_store();
97320
97335
  init_workflow_lifecycle_traits();
97321
97336
  init_workflow_ir_resolver();
97322
- init_workflow_lifecycle_traits();
97323
97337
  init_errors3();
97324
97338
  init_task_fields();
97325
97339
  init_builtin_traits();
@@ -135461,7 +135475,8 @@ function wrapPluginRuntime(instance, runtimeId, runtimeName) {
135461
135475
  const result2 = await adapter.createSession(options);
135462
135476
  return {
135463
135477
  session: result2.session ?? result2,
135464
- sessionFile: result2.sessionFile
135478
+ sessionFile: result2.sessionFile,
135479
+ settleFallbackDispatch: result2.settleFallbackDispatch
135465
135480
  };
135466
135481
  }
135467
135482
  throw new Error(`Plugin runtime "${runtimeId}" does not implement createSession`);
@@ -135570,11 +135585,15 @@ var init_runtime_resolution = __esm({
135570
135585
  name = "Default PI Runtime";
135571
135586
  async createSession(options) {
135572
135587
  const { toolOutputMaxChars, mcpServers, ...agentOptions } = options;
135573
- return createFnAgent2({
135588
+ const result2 = await createFnAgent2({
135574
135589
  ...agentOptions,
135575
135590
  toolOutputMaxChars,
135576
135591
  mcpServers: normalizeAgentRuntimeMcpServers(mcpServers)
135577
135592
  });
135593
+ return {
135594
+ ...result2,
135595
+ settleFallbackDispatch: result2.settleFallbackDispatch ?? (async () => void 0)
135596
+ };
135578
135597
  }
135579
135598
  async promptWithFallback(session, prompt, options) {
135580
135599
  return promptWithFallback(session, prompt, options);
@@ -136456,6 +136475,7 @@ async function createResolvedAgentSession(options) {
136456
136475
  return {
136457
136476
  session: result2.session,
136458
136477
  sessionFile: result2.sessionFile,
136478
+ settleFallbackDispatch: result2.settleFallbackDispatch,
136459
136479
  runtimeId: resolved.runtimeId,
136460
136480
  wasConfigured: resolved.wasConfigured
136461
136481
  };
@@ -158451,7 +158471,11 @@ async function createFnAgent2(options) {
158451
158471
  }
158452
158472
  });
158453
158473
  attachSessionIdentity(promptableSession);
158454
- return { session: promptableSession, sessionFile: promptableSession.sessionFile };
158474
+ return {
158475
+ session: promptableSession,
158476
+ sessionFile: promptableSession.sessionFile,
158477
+ settleFallbackDispatch: async () => void 0
158478
+ };
158455
158479
  }
158456
158480
  var execAsync, RTK_ACCEPTED_REWRITE_EXIT_CODES, RTK_EXPECTED_PASSTHROUGH_EXIT_CODES, RTK_EXPECTED_FAIL_OPEN_ERROR_CODES, RTK_REWRITE_MAX_BUFFER_BYTES, hostExtensionPaths, FN_MEMORY_APPEND_TOOL_NAME, FUSION_SHUTDOWN_WRAP_FLAG, promptWithFallbackInFlight, COMPACTION_FALLBACK_INSTRUCTIONS, MAX_COMPACTED_PROMPT_MEMORY_CHARS, MAX_COMPACTED_SUBTASK_GUIDANCE_CHARS, MAX_COMPACTED_ATTACHMENTS_CHARS, MAX_COMPACTED_EXISTING_SPEC_CHARS, MAX_COMPACTED_TASK_PROMPT_CHARS, MAX_COMPACTED_USER_COMMENTS_CHARS, ModelFallbackExhaustedError, GATE_BYPASS_TOOL_NAMES, TOOL_OUTPUT_BUDGET_OVERRIDES;
158457
158481
  var init_pi = __esm({
@@ -184940,6 +184964,25 @@ var init_scheduler = __esm({
184940
184964
  });
184941
184965
  }
184942
184966
  }
184967
+ if (from === parked.wip && to2 === parked.hold) {
184968
+ if (source === "engine") {
184969
+ this.recentEngineTodoRequeues.set(task.id, task.columnMovedAt ?? (/* @__PURE__ */ new Date()).toISOString());
184970
+ } else {
184971
+ this.recentEngineTodoRequeues.delete(task.id);
184972
+ }
184973
+ } else if (to2 === parked.review || parked.terminal.has(to2)) {
184974
+ this.recentEngineTodoRequeues.delete(task.id);
184975
+ if (task.dispatchStormCount != null || task.lastDispatchAt != null || task.executeRequeueLoopCount != null || task.executeRequeueLoopSignature != null) {
184976
+ void this.store.updateTask(task.id, {
184977
+ dispatchStormCount: null,
184978
+ lastDispatchAt: null,
184979
+ executeRequeueLoopCount: null,
184980
+ executeRequeueLoopSignature: null
184981
+ }).catch((error3) => {
184982
+ schedulerLog.warn(`Failed to reset dispatch oscillation state for ${task.id} on move to ${to2}: ${error3 instanceof Error ? error3.message : String(error3)}`);
184983
+ });
184984
+ }
184985
+ }
184943
184986
  const resolvedParked = mergeParkedColumns(await resolveTaskParkedColumns(this.store, task.id), lanes);
184944
184987
  if (resolvedParked.terminal.has(to2)) {
184945
184988
  try {
@@ -185000,25 +185043,6 @@ var init_scheduler = __esm({
185000
185043
  schedulerLog.error(`Failed event-driven blocker reconciliation for ${task.id}`, error3);
185001
185044
  }
185002
185045
  }
185003
- if (from === resolvedParked.wip && to2 === resolvedParked.hold) {
185004
- if (source === "engine") {
185005
- this.recentEngineTodoRequeues.set(task.id, task.columnMovedAt ?? (/* @__PURE__ */ new Date()).toISOString());
185006
- } else {
185007
- this.recentEngineTodoRequeues.delete(task.id);
185008
- }
185009
- } else if (to2 === resolvedParked.review || resolvedParked.terminal.has(to2)) {
185010
- this.recentEngineTodoRequeues.delete(task.id);
185011
- if (task.dispatchStormCount != null || task.lastDispatchAt != null || task.executeRequeueLoopCount != null || task.executeRequeueLoopSignature != null) {
185012
- void this.store.updateTask(task.id, {
185013
- dispatchStormCount: null,
185014
- lastDispatchAt: null,
185015
- executeRequeueLoopCount: null,
185016
- executeRequeueLoopSignature: null
185017
- }).catch((error3) => {
185018
- schedulerLog.warn(`Failed to reset dispatch oscillation state for ${task.id} on move to ${to2}: ${error3 instanceof Error ? error3.message : String(error3)}`);
185019
- });
185020
- }
185021
- }
185022
185046
  if (resolvedParked.terminal.has(to2) || to2 === resolvedParked.hold) {
185023
185047
  schedulerLog.log(`Task moved to ${to2} \u2014 triggering scheduling`);
185024
185048
  this.schedule();
@@ -203276,7 +203300,6 @@ var init_self_healing = __esm({
203276
203300
  { name: "reconcile-done-task-integrity", fn: () => this.reconcileDoneTaskIntegrity() },
203277
203301
  { name: "reconcile-stale-merger-status", fn: () => this.reconcileStaleMergerStatus() },
203278
203302
  { name: "reconcile-stale-duplicate-decision", fn: () => this.reconcileStaleDuplicateDecisionPause() },
203279
- { name: "reconcile-external-pr-blockers", fn: () => this.reconcileExternalPrBlockers() },
203280
203303
  // FNXC:OrphanedPendingSteps 2026-07-22-16:35 (FN-8492 review follow-up): also
203281
203304
  // steady-state — a step session can die without an engine restart, and startup-only
203282
203305
  // cadence left that case riding the 3×30-min stall escalator to a deadlock park.
@@ -204780,6 +204803,7 @@ var init_self_healing = __esm({
204780
204803
  }
204781
204804
  async reclaimStaleActiveBranches() {
204782
204805
  const reclaimArchivedColumns = await resolveProjectColumnsForRoles(this.store, ["archived"]);
204806
+ const reclaimCompleteColumns = await resolveProjectColumnsForRoles(this.store, ["complete"]);
204783
204807
  try {
204784
204808
  const settings = await this.store.getSettings();
204785
204809
  if (settings.globalPause || settings.enginePaused) return 0;
@@ -204877,10 +204901,12 @@ var init_self_healing = __esm({
204877
204901
  if (task.worktree && await isUsableTaskWorktree(this.options.rootDir, task.worktree)) continue;
204878
204902
  const inspection = await this.inspectOrphanedBranch(branch);
204879
204903
  if (!inspection) continue;
204880
- if (inspection.uniqueCommitCount > 0) {
204904
+ const isCompleteColumn2 = reclaimCompleteColumns.has(task.column);
204905
+ if (inspection.uniqueCommitCount > 0 && !isCompleteColumn2) {
204881
204906
  log33.warn(`[recovery] stale-active-branch-rescue-needed ${task.id} branch=${branch} unique=${inspection.uniqueCommitCount} tip=${inspection.tipSha.slice(0, 12)}`);
204882
204907
  continue;
204883
204908
  }
204909
+ const reclaimReason = inspection.uniqueCommitCount > 0 ? "complete-column-unique-commits-force" : "zero-unique-commits-no-worktree";
204884
204910
  await execAsync18(`git branch -D ${JSON.stringify(branch)}`, {
204885
204911
  cwd: this.options.rootDir,
204886
204912
  timeout: 12e4,
@@ -204898,7 +204924,7 @@ var init_self_healing = __esm({
204898
204924
  });
204899
204925
  await this.store.logEntry(
204900
204926
  task.id,
204901
- `[recovery] stale-active-branch-reclaim ${task.id} branch=${branch} reason=zero-unique-commits-no-worktree`
204927
+ `[recovery] stale-active-branch-reclaim ${task.id} branch=${branch} reason=${reclaimReason}`
204902
204928
  );
204903
204929
  try {
204904
204930
  const auditor = createRunAuditor(this.store, {
@@ -204916,7 +204942,7 @@ var init_self_healing = __esm({
204916
204942
  branch,
204917
204943
  tipSha: inspection.tipSha,
204918
204944
  uniqueCommitCount: inspection.uniqueCommitCount,
204919
- reason: "zero-unique-commits-no-worktree"
204945
+ reason: reclaimReason
204920
204946
  }
204921
204947
  });
204922
204948
  } catch (auditErr) {
@@ -204963,10 +204989,9 @@ var init_self_healing = __esm({
204963
204989
  const baseBranch = task.baseBranch || await resolveIntegrationBranch(this.options.rootDir, void 0);
204964
204990
  const comparison = await listUniqueBranchCommits(this.options.rootDir, baseBranch, branchName);
204965
204991
  if (comparison.commits.length > 0) {
204966
- log33.warn(
204967
- `[self-healing] reconcileCompletedTask ${task.id}: branch ${branchName} has ${comparison.commits.length} unique commit(s) vs ${comparison.mainRef}; skip deletion`
204992
+ log33.log(
204993
+ `[self-healing] reconcileCompletedTask ${task.id}: branch ${branchName} has ${comparison.commits.length} unique commit(s) vs ${comparison.mainRef}; force-deleting post-completion (squash-safe)`
204968
204994
  );
204969
- return false;
204970
204995
  }
204971
204996
  try {
204972
204997
  await execAsync18(`git branch -D ${shellQuote2(branchName)}`, {
@@ -212173,71 +212198,12 @@ var init_self_healing = __esm({
212173
212198
  }
212174
212199
  }
212175
212200
  /*
212176
- FNXC:HonestBlockedExit 2026-08-02-01:30:
212177
- Durable parks store github-pr externalBlockers (FN-8700 file-claim). When every blocking
212178
- PR is MERGED or CLOSED, clear the failed park so the scheduler can re-dispatch. Fail-soft
212179
- when gh is unavailable leave the park for the operator.
212201
+ FNXC:HonestBlockedExit 2026-08-02-23:59 (operator decision — FN-8728 vs PR #2398):
212202
+ The FN-8700 `reconcile-external-pr-blockers` sweep (gh-backed clearing of PR-claim parks)
212203
+ is REMOVED with the whole PR/file-claim blocking mechanism. Open PRs are never blockers;
212204
+ file-scope conflicts are arbitrated only by Fusion's own board. Legacy PR-claim parks are
212205
+ no longer honored by isDurableBlockedTask, so normal recovery paths reclaim them.
212180
212206
  */
212181
- async reconcileExternalPrBlockers() {
212182
- try {
212183
- if (!await isGhAvailable()) {
212184
- log33.debug("reconcile-external-pr-blockers skipped \u2014 gh unavailable");
212185
- return 0;
212186
- }
212187
- const tasks2 = await this.store.listTasks({ slim: true, includeArchived: false, limit: 500 });
212188
- let cleared = 0;
212189
- for (const task of tasks2.slice(0, 80)) {
212190
- if (task.status !== "failed" || !task.error?.startsWith("BLOCKED:")) continue;
212191
- const meta2 = task.sourceMetadata;
212192
- const blockers = meta2?.externalBlockers;
212193
- if (!Array.isArray(blockers) || blockers.length === 0) continue;
212194
- const prNumbers = blockers.map((b3) => b3 && typeof b3 === "object" && b3.kind === "github-pr" ? Number(b3.number) : NaN).filter((n) => Number.isFinite(n) && n > 0);
212195
- if (prNumbers.length === 0) continue;
212196
- let allResolved = true;
212197
- for (const n of prNumbers) {
212198
- try {
212199
- const pr2 = await runGhJsonAsync(
212200
- ["pr", "view", String(n), "--json", "state,mergedAt"],
212201
- { timeoutMs: 15e3 }
212202
- );
212203
- const state = String(pr2?.state ?? "").toUpperCase();
212204
- const merged = Boolean(pr2?.mergedAt) || state === "MERGED";
212205
- const closed = state === "CLOSED" || state === "MERGED";
212206
- if (!merged && !closed) {
212207
- allResolved = false;
212208
- break;
212209
- }
212210
- } catch {
212211
- allResolved = false;
212212
- break;
212213
- }
212214
- }
212215
- if (!allResolved) continue;
212216
- await this.store.updateTask(task.id, {
212217
- status: null,
212218
- error: null,
212219
- sourceMetadataPatch: {
212220
- externalBlockers: [],
212221
- blockedClass: null,
212222
- blockedThrashSignature: null,
212223
- blockedThrashCount: null,
212224
- externalPrBlockersClearedAt: (/* @__PURE__ */ new Date()).toISOString(),
212225
- externalPrBlockersCleared: prNumbers
212226
- }
212227
- });
212228
- await this.store.logEntry(
212229
- task.id,
212230
- `Auto-recovered: external PR blocker(s) ${prNumbers.map((n) => `#${n}`).join(", ")} merged/closed \u2014 cleared durable BLOCKED park for re-dispatch`
212231
- );
212232
- log33.log(`Cleared durable PR block for ${task.id} (prs=${prNumbers.join(",")})`);
212233
- cleared += 1;
212234
- }
212235
- return cleared;
212236
- } catch (error3) {
212237
- log33.warn(`reconcile-external-pr-blockers failed: ${error3 instanceof Error ? error3.message : String(error3)}`);
212238
- return 0;
212239
- }
212240
- }
212241
212207
  async resolveExplicitDuplicateMarkerTasks() {
212242
212208
  try {
212243
212209
  const settings = await this.store.getSettings();
@@ -212991,6 +212957,7 @@ var init_self_healing = __esm({
212991
212957
  // ../engine/src/triage.ts
212992
212958
  import { Type as Type5 } from "@earendil-works/pi-ai";
212993
212959
  import { exec as exec34 } from "node:child_process";
212960
+ import { existsSync as existsSync60 } from "node:fs";
212994
212961
  import { readFile as readFile38, writeFile as writeFile27, rm as rm8 } from "node:fs/promises";
212995
212962
  import { join as join72 } from "node:path";
212996
212963
  import { promisify as promisify48 } from "node:util";
@@ -213497,6 +213464,8 @@ var init_triage = __esm({
213497
213464
  unregisterAdmissionProvider = null;
213498
213465
  /** Timestamps when tasks entered the `processing` set, for staleness detection. */
213499
213466
  processingSince = /* @__PURE__ */ new Map();
213467
+ /** Monotonic provenance token for attempt-local planner fallback observation. */
213468
+ planningAttemptSequence = 0;
213500
213469
  wasGlobalPaused = false;
213501
213470
  wasEnginePaused = false;
213502
213471
  idleSemaphoreLeakCandidateSince = null;
@@ -214792,7 +214761,28 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
214792
214761
  }
214793
214762
  await this.store.logEntry(task.id, `Planning session running in task worktree ${planningCwd}`).catch(() => void 0);
214794
214763
  }
214795
- const { session } = await createResolvedAgentSession({
214764
+ const authoritativePromptPath = join72(this.rootDir, promptPath);
214765
+ const planningAttemptBaseline = existsSync60(authoritativePromptPath) ? await readFile38(authoritativePromptPath, "utf-8").catch(() => void 0) : void 0;
214766
+ const planningAttempt = {
214767
+ id: `${task.id}:${++this.planningAttemptSequence}`,
214768
+ baseline: planningAttemptBaseline,
214769
+ fallbackEngaged: false,
214770
+ fallbackSettlements: []
214771
+ };
214772
+ const fallbackObserver = createFallbackModelObserver({
214773
+ agent: "triage",
214774
+ label: "triage",
214775
+ store: this.store,
214776
+ taskId: task.id,
214777
+ taskTitle: task.title
214778
+ });
214779
+ const onFallbackModelUsed = (payload) => {
214780
+ planningAttempt.fallbackEngaged = true;
214781
+ const settlement = Promise.resolve(fallbackObserver(payload));
214782
+ planningAttempt.fallbackSettlements.push(settlement);
214783
+ return settlement;
214784
+ };
214785
+ const { session, settleFallbackDispatch, runtimeId } = await createResolvedAgentSession({
214796
214786
  sessionPurpose: "triage",
214797
214787
  runtimeHint: triageRuntimeHint,
214798
214788
  pluginRunner: this.options.pluginRunner,
@@ -214831,13 +214821,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
214831
214821
  taskTitle: task.title,
214832
214822
  actionGateContext: this.buildActionGateContext(task.id, triageRunContext.runId, assignedAgent, settings.defaultAgentPermissionPolicy),
214833
214823
  permanentAgentGating: this.buildPermanentAgentGatingContext(task.id, triageRunContext.runId, assignedAgent, settings.defaultAgentPermissionPolicy),
214834
- onFallbackModelUsed: createFallbackModelObserver({
214835
- agent: "triage",
214836
- label: "triage",
214837
- store: this.store,
214838
- taskId: task.id,
214839
- taskTitle: task.title
214840
- })
214824
+ onFallbackModelUsed
214841
214825
  });
214842
214826
  const modelDesc = formatModelMarkerDetails(describeModel(session), resolvePlanningThinkingLevel(settings, task.planningThinkingLevel ?? task.thinkingLevel));
214843
214827
  planLog.debug(`${task.id}: using model ${modelDesc}`);
@@ -214907,6 +214891,10 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
214907
214891
  agentPrompt,
214908
214892
  imageContents.length > 0 ? { images: imageContents } : void 0
214909
214893
  );
214894
+ const fallbackDispatchBoundaryMissing = typeof settleFallbackDispatch !== "function";
214895
+ if (!fallbackDispatchBoundaryMissing) {
214896
+ await settleFallbackDispatch();
214897
+ }
214910
214898
  checkSessionError(session);
214911
214899
  if (this.pauseAborted.has(task.id)) {
214912
214900
  this.pauseAborted.delete(task.id);
@@ -214999,12 +214987,42 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
214999
214987
  }
215000
214988
  }
215001
214989
  }
215002
- const duplicateReport = { outcome: "parked" };
215003
- if (await this.tryFinalizeExplicitDuplicateMarker(task, written, settings, {
215004
- isReplan,
215005
- feedback
215006
- }, duplicateReport)) {
215007
- this.options.onSpecifyComplete?.(task, duplicateReport);
214990
+ let settledFallbackCount = 0;
214991
+ while (settledFallbackCount < planningAttempt.fallbackSettlements.length) {
214992
+ const pendingSettlements = planningAttempt.fallbackSettlements.slice(settledFallbackCount);
214993
+ settledFallbackCount = planningAttempt.fallbackSettlements.length;
214994
+ await Promise.all(pendingSettlements);
214995
+ }
214996
+ const artifactChangedByAttempt = planningAttempt.baseline !== written;
214997
+ if (fallbackDispatchBoundaryMissing || planningAttempt.fallbackEngaged || !artifactChangedByAttempt) {
214998
+ const failure = fallbackDispatchBoundaryMissing ? `Planner runtime ${runtimeId} did not provide a fallback-dispatch settlement boundary for attempt ${planningAttempt.id}` : planningAttempt.fallbackEngaged ? `Planner fallback engaged during attempt ${planningAttempt.id}` : `Planner did not update the authoritative PROMPT.md during attempt ${planningAttempt.id}`;
214999
+ const decision = computeRecoveryDecision({
215000
+ recoveryRetryCount: task.recoveryRetryCount,
215001
+ nextRecoveryAt: task.nextRecoveryAt
215002
+ });
215003
+ if (decision.shouldRetry) {
215004
+ const retryMessage = `${failure} \u2014 retry ${decision.nextState.recoveryRetryCount}/${MAX_RECOVERY_RETRIES} in ${formatDelay(decision.delayMs)}.`;
215005
+ planLog.warn(`${task.id} ${retryMessage}`);
215006
+ await this.store.logEntry(task.id, retryMessage);
215007
+ await this.updatePlanningStateIfStillCurrent(task, {
215008
+ status: this.restoreStatusAfterInterruptedTriageWork(task),
215009
+ error: null,
215010
+ recoveryRetryCount: decision.nextState.recoveryRetryCount,
215011
+ nextRecoveryAt: decision.nextState.nextRecoveryAt
215012
+ });
215013
+ return;
215014
+ }
215015
+ const failureMessage = `${failure} after ${MAX_RECOVERY_RETRIES} retries. Retry after adjusting the task prompt or model.`;
215016
+ planLog.error(`${task.id} clean planning attempt retry budget exhausted`);
215017
+ await this.store.logEntry(task.id, failureMessage);
215018
+ if (await this.updatePlanningStateIfStillCurrent(task, {
215019
+ status: "failed",
215020
+ error: failureMessage,
215021
+ recoveryRetryCount: null,
215022
+ nextRecoveryAt: null
215023
+ })) {
215024
+ await this.backfillBlankTitleAfterTerminalTriageFailure(task);
215025
+ }
215008
215026
  return;
215009
215027
  }
215010
215028
  const deterministicSpecFailure = await this.validateGeneratedPrompt(task.id, written);
@@ -215046,6 +215064,21 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
215046
215064
  }
215047
215065
  return;
215048
215066
  }
215067
+ const duplicateReport = { outcome: "parked" };
215068
+ if (await this.tryFinalizeExplicitDuplicateMarker(task, written, settings, {
215069
+ isReplan,
215070
+ feedback
215071
+ }, duplicateReport)) {
215072
+ this.options.onSpecifyComplete?.(task, duplicateReport);
215073
+ return;
215074
+ }
215075
+ if (task.recoveryRetryCount != null || task.nextRecoveryAt != null) {
215076
+ await this.updatePlanningStateIfStillCurrent(task, {
215077
+ error: null,
215078
+ recoveryRetryCount: null,
215079
+ nextRecoveryAt: null
215080
+ });
215081
+ }
215049
215082
  const finalizeReport = await this.finalizeApprovedTask(task, written, settings, {
215050
215083
  isReplan,
215051
215084
  feedback
@@ -216082,103 +216115,35 @@ Remove or replace these ids and call fn_task_create again.`
216082
216115
  // ../engine/src/execution-block-classifier.ts
216083
216116
  function partitionBlockedByRefs(blockedBy) {
216084
216117
  const taskIds = [];
216085
- const prNumbers = [];
216086
216118
  for (const raw of blockedBy) {
216087
216119
  const id = raw.trim();
216088
216120
  if (!id) continue;
216089
- const prMatch = id.match(PR_REF_RE);
216090
- if (prMatch) {
216091
- prNumbers.push(Number(prMatch[1]));
216092
- continue;
216093
- }
216121
+ if (LEGACY_PR_REF_RE.test(id)) continue;
216094
216122
  if (TASK_ID_RE.test(id)) {
216095
216123
  const m2 = id.toUpperCase().match(/^([A-Z][A-Z0-9]*)-(\d+)$/);
216096
216124
  taskIds.push(m2 ? `${m2[1]}-${m2[2]}` : id.toUpperCase());
216097
- continue;
216098
216125
  }
216099
216126
  }
216100
- return {
216101
- taskIds: [...new Set(taskIds)],
216102
- prNumbers: [...new Set(prNumbers.filter((n) => Number.isFinite(n) && n > 0))]
216103
- };
216127
+ return { taskIds: [...new Set(taskIds)] };
216104
216128
  }
216105
- function extractPrNumbersFromText(text2) {
216106
- const found = [];
216107
- for (const match of text2.matchAll(PR_IN_TEXT_RE)) {
216108
- const n = Number(match[1]);
216109
- if (Number.isFinite(n) && n > 0) found.push(n);
216110
- }
216111
- return [...new Set(found)];
216112
- }
216113
- function isFileClaimBlockedReason(reason) {
216114
- return CLAIM_REASON_RE.test(reason);
216115
- }
216116
- function classifyBlockedExit(reason, blockedBy = []) {
216117
- const trimmed = reason.trim();
216118
- const partitioned = partitionBlockedByRefs(blockedBy);
216119
- const prFromText = extractPrNumbersFromText(trimmed);
216120
- const prNumbers = [.../* @__PURE__ */ new Set([...partitioned.prNumbers, ...prFromText])];
216121
- const isClaim = isFileClaimBlockedReason(trimmed) || prNumbers.length > 0 && /claim/i.test(trimmed);
216122
- if (isClaim || prNumbers.length > 0 && isFileClaimBlockedReason(trimmed)) {
216123
- const externalBlockers = [
216124
- ...prNumbers.map((number4) => ({ kind: "github-pr", number: number4 }))
216125
- ];
216126
- if (externalBlockers.length === 0 && isClaim) {
216127
- externalBlockers.push({
216128
- kind: "file-claim",
216129
- prNumber: prNumbers[0]
216130
- });
216131
- } else if (isClaim) {
216132
- for (const n of prNumbers) {
216133
- if (!externalBlockers.some((b3) => b3.kind === "github-pr" && b3.number === n)) {
216134
- externalBlockers.push({ kind: "github-pr", number: n });
216135
- }
216136
- }
216137
- if (prNumbers.length === 0) {
216138
- externalBlockers.push({ kind: "file-claim" });
216139
- }
216140
- }
216141
- const thrashSignature = [
216142
- "file-claim",
216143
- ...prNumbers.map((n) => `pr:${n}`).sort(),
216144
- ...partitioned.taskIds.slice().sort()
216145
- ].join("|");
216146
- return {
216147
- allowAutoReplan: false,
216148
- class: "file-claim",
216149
- externalBlockers,
216150
- thrashSignature: thrashSignature || "file-claim",
216151
- prNumbers
216152
- };
216153
- }
216154
- if (partitioned.taskIds.length > 0) {
216155
- return {
216156
- allowAutoReplan: false,
216157
- class: "external",
216158
- externalBlockers: [],
216159
- thrashSignature: `tasks:${partitioned.taskIds.slice().sort().join(",")}`,
216160
- prNumbers
216161
- };
216162
- }
216163
- if (prNumbers.length > 0) {
216129
+ function classifyBlockedExit(_reason, blockedBy = []) {
216130
+ const { taskIds } = partitionBlockedByRefs(blockedBy);
216131
+ if (taskIds.length > 0) {
216164
216132
  return {
216165
216133
  allowAutoReplan: false,
216166
216134
  class: "external",
216167
- externalBlockers: prNumbers.map((number4) => ({ kind: "github-pr", number: number4 })),
216168
- thrashSignature: prNumbers.map((n) => `pr:${n}`).sort().join("|"),
216169
- prNumbers
216135
+ thrashSignature: `tasks:${taskIds.slice().sort().join(",")}`
216170
216136
  };
216171
216137
  }
216172
216138
  return {
216173
216139
  allowAutoReplan: true,
216174
216140
  class: "plan-defect",
216175
- externalBlockers: [],
216176
- thrashSignature: "plan-defect",
216177
- prNumbers: []
216141
+ thrashSignature: "plan-defect"
216178
216142
  };
216179
216143
  }
216180
216144
  function countBlockedThrashHits(log41, signature, nowMs = Date.now(), windowMs = BLOCKED_THRASH_WINDOW_MS) {
216181
216145
  if (!log41?.length) return 0;
216146
+ if (signature === "plan-defect") return 0;
216182
216147
  const cutoff = nowMs - windowMs;
216183
216148
  let count2 = 0;
216184
216149
  for (const entry of log41) {
@@ -216190,18 +216155,7 @@ function countBlockedThrashHits(log41, signature, nowMs = Date.now(), windowMs =
216190
216155
  continue;
216191
216156
  }
216192
216157
  }
216193
- if (signature === "plan-defect") continue;
216194
- if (signature.includes("file-claim") || signature.startsWith("pr:") || signature.includes("|pr:")) {
216195
- if (isFileClaimBlockedReason(action) || /PR\s*#?\s*\d+/i.test(action) || action.includes("durable external block")) {
216196
- count2 += 1;
216197
- continue;
216198
- }
216199
- }
216200
216158
  for (const part of signature.split("|")) {
216201
- if (part.startsWith("pr:") && action.includes(`PR #${part.slice(3)}`)) {
216202
- count2 += 1;
216203
- break;
216204
- }
216205
216159
  if (part.startsWith("tasks:") && part.slice(6).split(",").some((id) => id && action.includes(id))) {
216206
216160
  count2 += 1;
216207
216161
  break;
@@ -216210,38 +216164,26 @@ function countBlockedThrashHits(log41, signature, nowMs = Date.now(), windowMs =
216210
216164
  }
216211
216165
  return count2;
216212
216166
  }
216213
- function isDurableBlockedError(error3) {
216214
- if (!error3?.startsWith("BLOCKED:")) return false;
216215
- const reason = error3.slice("BLOCKED:".length).trim();
216216
- const classification = classifyBlockedExit(reason, []);
216217
- return !classification.allowAutoReplan;
216218
- }
216219
216167
  function isDurableBlockedTask(task) {
216220
- if (task.status === "failed" && isDurableBlockedError(task.error)) return true;
216221
216168
  const meta2 = task.sourceMetadata;
216222
216169
  if (!meta2 || typeof meta2 !== "object") return false;
216223
- if (meta2.blockedClass === "file-claim" || meta2.blockedClass === "external") {
216224
- if (task.status === "failed" || task.status === "needs-replan") return true;
216225
- }
216226
- const blockers = meta2.externalBlockers;
216227
- return Array.isArray(blockers) && blockers.length > 0 && task.status === "failed";
216170
+ if (meta2.blockedClass !== "external") return false;
216171
+ return task.status === "failed" || task.status === "needs-replan";
216228
216172
  }
216229
216173
  function buildExternalBlockMetadataPatch(classification, thrashCount) {
216230
216174
  return {
216231
216175
  blockedClass: classification.class,
216232
216176
  blockedThrashSignature: classification.thrashSignature,
216233
216177
  blockedThrashCount: thrashCount,
216234
- externalBlockers: classification.externalBlockers
216178
+ externalBlockers: []
216235
216179
  };
216236
216180
  }
216237
- var TASK_ID_RE, PR_REF_RE, PR_IN_TEXT_RE, CLAIM_REASON_RE, BLOCKED_THRASH_LIMIT, BLOCKED_THRASH_WINDOW_MS;
216181
+ var TASK_ID_RE, LEGACY_PR_REF_RE, BLOCKED_THRASH_LIMIT, BLOCKED_THRASH_WINDOW_MS;
216238
216182
  var init_execution_block_classifier = __esm({
216239
216183
  "../engine/src/execution-block-classifier.ts"() {
216240
216184
  "use strict";
216241
216185
  TASK_ID_RE = /^[A-Z][A-Z0-9]*-\d+$/i;
216242
- PR_REF_RE = /^(?:pr[:#-]|#|PR-)(\d+)$/i;
216243
- PR_IN_TEXT_RE = /\bPR\s*#?\s*(\d+)\b/gi;
216244
- CLAIM_REASON_RE = /check-file-claimed|actively claimed|file[- ]claim|claimed by (?:open )?pr|open pr\s*#?\s*\d+|collision policy|claimed paths?|file claims?/i;
216186
+ LEGACY_PR_REF_RE = /^(?:pr[:#-]|#|PR-)(\d+)$/i;
216245
216187
  BLOCKED_THRASH_LIMIT = 3;
216246
216188
  BLOCKED_THRASH_WINDOW_MS = 60 * 60 * 1e3;
216247
216189
  }
@@ -219059,7 +219001,7 @@ var init_workflow_completion_summary = __esm({
219059
219001
 
219060
219002
  // ../engine/src/review-checkout.ts
219061
219003
  import { execFileSync as execFileSync3 } from "node:child_process";
219062
- import { existsSync as existsSync60, realpathSync as realpathSync9, statSync as statSync11 } from "node:fs";
219004
+ import { existsSync as existsSync61, realpathSync as realpathSync9, statSync as statSync11 } from "node:fs";
219063
219005
  import { isAbsolute as isAbsolute15 } from "node:path";
219064
219006
  function readMetadataPath(value) {
219065
219007
  if (!value || typeof value !== "object") return void 0;
@@ -219083,7 +219025,7 @@ function resolveReviewCheckoutCwd(task, fallbackCwd) {
219083
219025
  const candidate = getTaskReviewCheckoutPath(task);
219084
219026
  if (!candidate || !isAbsolute15(candidate)) return fallbackCwd;
219085
219027
  try {
219086
- if (!existsSync60(candidate) || !statSync11(candidate).isDirectory()) return fallbackCwd;
219028
+ if (!existsSync61(candidate) || !statSync11(candidate).isDirectory()) return fallbackCwd;
219087
219029
  const realCandidate = realpathSync9(candidate);
219088
219030
  const topLevel = execFileSync3("git", ["rev-parse", "--show-toplevel"], {
219089
219031
  cwd: realCandidate,
@@ -221689,7 +221631,7 @@ var init_external_integration_evidence = __esm({
221689
221631
 
221690
221632
  // ../engine/src/step-runner.ts
221691
221633
  import { exec as exec35 } from "node:child_process";
221692
- import { existsSync as existsSync62, statSync as statSync12 } from "node:fs";
221634
+ import { existsSync as existsSync63, statSync as statSync12 } from "node:fs";
221693
221635
  import { promisify as promisify50 } from "node:util";
221694
221636
  async function runTaskStep(deps, task, stepIndex, opts = {}) {
221695
221637
  const { store, worktreePath } = deps;
@@ -221843,7 +221785,7 @@ function makeAncestryBlastRadiusGuard(opts) {
221843
221785
  function isUsableWorktreeDirectory(candidate) {
221844
221786
  if (!candidate) return false;
221845
221787
  try {
221846
- return existsSync62(candidate) && statSync12(candidate).isDirectory();
221788
+ return existsSync63(candidate) && statSync12(candidate).isDirectory();
221847
221789
  } catch {
221848
221790
  return false;
221849
221791
  }
@@ -221880,7 +221822,7 @@ var init_step_runner = __esm({
221880
221822
 
221881
221823
  // ../engine/src/auto-recovery-handlers/branch-worktree.ts
221882
221824
  import { exec as exec36 } from "node:child_process";
221883
- import { existsSync as existsSync63 } from "node:fs";
221825
+ import { existsSync as existsSync64 } from "node:fs";
221884
221826
  import { promisify as promisify51 } from "node:util";
221885
221827
  var execAsync33, baseLog2, GIT_TIMEOUT_MS8, GIT_MAX_BUFFER6, BranchWorktreeAutoRecoveryHandler;
221886
221828
  var init_branch_worktree = __esm({
@@ -222071,7 +222013,7 @@ var init_branch_worktree = __esm({
222071
222013
  if (inspection.kind === "stale-resolved" || inspection.kind === "fully-subsumed" || inspection.kind === "tip-already-merged") {
222072
222014
  await this.requeueAfterRecovery(ctx.task, failure, inspection.kind, {
222073
222015
  branchExists: await this.hasBranchRef(repoDir, branchName),
222074
- worktreePresent: existsSync63(conflictingWorktreePath),
222016
+ worktreePresent: existsSync64(conflictingWorktreePath),
222075
222017
  tipSha: "tipSha" in inspection ? inspection.tipSha : void 0,
222076
222018
  inspectionKind: inspection.kind
222077
222019
  });
@@ -222141,7 +222083,7 @@ var init_branch_worktree = __esm({
222141
222083
  let branchDeleted = false;
222142
222084
  let worktreeRemoved = false;
222143
222085
  if (!isLiveOwned) {
222144
- if (existsSync63(conflictingWorktreePath)) {
222086
+ if (existsSync64(conflictingWorktreePath)) {
222145
222087
  try {
222146
222088
  await execAsync33(`git worktree remove --force ${this.quote(conflictingWorktreePath)}`, {
222147
222089
  cwd: repoDir,
@@ -222189,7 +222131,7 @@ var init_branch_worktree = __esm({
222189
222131
  });
222190
222132
  await this.requeueAfterRecovery(ctx.task, failure, "live-foreign-discard-and-recreate", {
222191
222133
  branchExists: !branchDeleted,
222192
- worktreePresent: !worktreeRemoved && existsSync63(conflictingWorktreePath),
222134
+ worktreePresent: !worktreeRemoved && existsSync64(conflictingWorktreePath),
222193
222135
  tipSha: tipSha2,
222194
222136
  inspectionKind: inspection.kind
222195
222137
  });
@@ -222202,7 +222144,7 @@ var init_branch_worktree = __esm({
222202
222144
  conflictingWorktreePath,
222203
222145
  inspectionKind: inspection.kind,
222204
222146
  branchExists,
222205
- worktreePresent: existsSync63(conflictingWorktreePath),
222147
+ worktreePresent: existsSync64(conflictingWorktreePath),
222206
222148
  tipSha
222207
222149
  });
222208
222150
  }
@@ -222363,7 +222305,7 @@ var init_file_scope2 = __esm({
222363
222305
  });
222364
222306
 
222365
222307
  // ../engine/src/run-verification-tool.ts
222366
- import { existsSync as existsSync64, readdirSync as readdirSync11, readFileSync as readFileSync23 } from "node:fs";
222308
+ import { existsSync as existsSync65, readdirSync as readdirSync11, readFileSync as readFileSync23 } from "node:fs";
222367
222309
  import { isAbsolute as isAbsolute16, join as join77, relative as relative14 } from "node:path";
222368
222310
  import { Type as Type6 } from "@earendil-works/pi-ai";
222369
222311
  function shellSplit(input) {
@@ -222923,7 +222865,7 @@ function createRunVerificationTool(opts) {
222923
222865
  }
222924
222866
  if (effectiveCommand.trimStart().startsWith("pnpm --filter")) {
222925
222867
  const modulesYaml = join77(rootDir, "node_modules", ".modules.yaml");
222926
- if (!existsSync64(modulesYaml)) {
222868
+ if (!existsSync65(modulesYaml)) {
222927
222869
  const installCmd = "pnpm install --prefer-offline";
222928
222870
  const msg = `node_modules/.modules.yaml not found in workspace root \u2014 auto-prepending \`${installCmd}\` before running the command.`;
222929
222871
  warnings.push(msg);
@@ -223284,7 +223226,7 @@ import { exec as exec39, execFile as execFile26, execSync as execSync7 } from "n
223284
223226
  import { promisify as promisify54 } from "node:util";
223285
223227
  import { setImmediate as setImmediateCb3 } from "node:timers";
223286
223228
  import { basename as basename19, delimiter as delimiter2, isAbsolute as isAbsolute18, join as join78, relative as relative16, resolve as resolvePath3 } from "node:path";
223287
- import { existsSync as existsSync65, lstatSync as lstatSync3, realpathSync as realpathSync10 } from "node:fs";
223229
+ import { existsSync as existsSync66, lstatSync as lstatSync3, realpathSync as realpathSync10 } from "node:fs";
223288
223230
  import { readFile as readFile40, rm as rm12, writeFile as writeFile29 } from "node:fs/promises";
223289
223231
  import { Type as Type7 } from "@earendil-works/pi-ai";
223290
223232
  import { SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
@@ -223302,7 +223244,7 @@ function isWorkflowStepSkillDiscoverable(skillName, additionalSkillPaths, ceSkil
223302
223244
  return false;
223303
223245
  }
223304
223246
  return (additionalSkillPaths ?? []).some(
223305
- (skillPath) => basename19(skillPath) === bareSkillName && existsSync65(join78(skillPath, "SKILL.md")) || existsSync65(join78(skillPath, bareSkillName, "SKILL.md"))
223247
+ (skillPath) => basename19(skillPath) === bareSkillName && existsSync66(join78(skillPath, "SKILL.md")) || existsSync66(join78(skillPath, bareSkillName, "SKILL.md"))
223306
223248
  );
223307
223249
  }
223308
223250
  function getPromptSection(prompt, heading) {
@@ -224275,7 +224217,7 @@ If you have just finished a step's work, immediately call \`fn_task_update\` to
224275
224217
 
224276
224218
  The user is not watching this conversation in real-time. They will read the final result. Asking permission wastes a full retry cycle and may orphan committed work.
224277
224219
 
224278
- **Cannot proceed \u2014 the honest blocked exit.** If the work genuinely cannot be finished (an upstream API break, a missing prerequisite task, an unresolvable external error, or a **file claim / open PR collision**), call \`fn_task_done(outcome="blocked", reason="<concrete blocker + what would unblock it>", blockedBy=["FN-XXXX"] or ["pr:2398"])\`. File-claim reasons must name the PR (e.g. "actively claimed by PR #2398" + \`blockedBy:["pr:2398"]\`). That parks durable failed WITHOUT auto-replan so the engine does not thrash; task IDs requeue when those tasks complete; PR refs clear when the PR merges/closes. Do NOT skip remaining steps to fake completion.
224220
+ **Cannot proceed \u2014 the honest blocked exit.** If the work genuinely cannot be finished (an upstream API break, a missing prerequisite task, or an unresolvable external error), call \`fn_task_done(outcome="blocked", reason="<concrete blocker + what would unblock it>", blockedBy=["FN-XXXX"])\`. That parks durable failed WITHOUT auto-replan so the engine does not thrash; task IDs requeue when those tasks complete. Blockers must be Fusion board tasks \u2014 do NOT treat open GitHub PRs touching the same files as blockers; other PRs are not claims on your file scope. Do NOT skip remaining steps to fake completion.
224279
224221
  This is THE correct action when you are stuck \u2014 do NOT instead mark the remaining steps \`skipped\` and call \`fn_task_done\` to make the task look finished. Skipping steps to escape a blocker launders a failure into \`done\` and is never the right move. (\`skipped\` remains valid only for the stale-premise path below, when the requested work is already present on HEAD.) Never write the blocker as plain prose.
224280
224222
 
224281
224223
  ## How to work
@@ -226864,7 +226806,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
226864
226806
  );
226865
226807
  return false;
226866
226808
  }
226867
- if (task.worktree && existsSync65(task.worktree)) {
226809
+ if (task.worktree && existsSync66(task.worktree)) {
226868
226810
  const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha, task.id, void 0, "recovery");
226869
226811
  if (modifiedFiles.length > 0) {
226870
226812
  await this.store.updateTask(task.id, { modifiedFiles });
@@ -229826,7 +229768,7 @@ ${info.feedback}`, revisionKey),
229826
229768
  if (live.firstExecutionAt || live.executionStartedAt) return false;
229827
229769
  if (activeSessionRegistry.isPathActive(live.worktree) || activeSessionRegistry.isPathActive(resolvePath3(live.worktree))) return false;
229828
229770
  if (this.hasLiveTaskSessionSurface(taskId) || executingTaskLock.has(taskId)) return false;
229829
- if (existsSync65(live.worktree)) {
229771
+ if (existsSync66(live.worktree)) {
229830
229772
  if (await this.preExecutionWorktreeHasWork(live.worktree)) {
229831
229773
  executorLog.log(`${taskId}: keeping pre-execution worktree ${live.worktree} \u2014 it carries commits or uncommitted changes`);
229832
229774
  return false;
@@ -229868,7 +229810,7 @@ ${info.feedback}`, revisionKey),
229868
229810
  }
229869
229811
  if (this.workspaceConfig && (this.workspaceConfig.repos.length ?? 0) > 0) return null;
229870
229812
  const live = await this.store.getTask(taskId);
229871
- if (live.worktree && existsSync65(live.worktree)) return live.worktree;
229813
+ if (live.worktree && existsSync66(live.worktree)) return live.worktree;
229872
229814
  const settings = await this.store.getSettings();
229873
229815
  const acquisitionTask = live.worktree ? { ...live, worktree: void 0, sessionFile: void 0 } : live;
229874
229816
  const acquired = await this.ensureGraphCustomNodeWorktree(acquisitionTask, settings, "planning");
@@ -229882,8 +229824,8 @@ ${info.feedback}`, revisionKey),
229882
229824
  if (!requirement.requiresWorktree) return;
229883
229825
  const live = await this.store.getTask(nodeTask.id);
229884
229826
  const executionCodeNode = node.kind === "code";
229885
- if (live.worktree && existsSync65(live.worktree) && !executionCodeNode) return;
229886
- const taskForAcquisition = live.worktree && !existsSync65(live.worktree) ? { ...live, worktree: void 0, sessionFile: void 0 } : live;
229827
+ if (live.worktree && existsSync66(live.worktree) && !executionCodeNode) return;
229828
+ const taskForAcquisition = live.worktree && !existsSync66(live.worktree) ? { ...live, worktree: void 0, sessionFile: void 0 } : live;
229887
229829
  if (live.worktree) {
229888
229830
  await this.store.logEntry(
229889
229831
  live.id,
@@ -230009,7 +229951,7 @@ ${info.feedback}`, revisionKey),
230009
229951
  const nodeDisplayName = typeof cfg.name === "string" && cfg.name.trim() ? cfg.name.trim() : node.id;
230010
229952
  const isPlanReviewNode = node.id === "plan-review-step" || nodeDisplayName === "Plan Review" || optionalGroupId === "plan-review";
230011
229953
  if (!this.workspaceConfig) {
230012
- const recordedWorktreeMissing = Boolean(executionTarget.worktree) && !existsSync65(executionTarget.worktree);
229954
+ const recordedWorktreeMissing = Boolean(executionTarget.worktree) && !existsSync66(executionTarget.worktree);
230013
229955
  const shouldAcquire = !executionTarget.worktree || recordedWorktreeMissing && isPlanReviewNode;
230014
229956
  if (shouldAcquire) {
230015
229957
  if (recordedWorktreeMissing) {
@@ -231683,75 +231625,16 @@ Graph value: ${failureValue}` : ""}`, budget.key),
231683
231625
  );
231684
231626
  }
231685
231627
  }
231686
- /*
231687
- FNXC:HonestBlockedExit 2026-08-02-01:30:
231688
- Agents often log `BLOCKED: … actively claimed by PR #N` without calling fn_task_done.
231689
- Without promotion, incomplete-step graph resume requeues the card and thrash restarts
231690
- (FN-8700). Promote claim/external blocks to a durable failed park here.
231691
- */
231692
- async parkDurableBlockedFromRecentLog(live) {
231693
- const recent = [...live.log ?? []].reverse().find((entry) => {
231694
- const action = entry.action ?? "";
231695
- return action.startsWith("BLOCKED:") || action.includes("durable external block");
231696
- });
231697
- if (!recent?.action) return false;
231698
- const reason = recent.action.startsWith("BLOCKED:") ? recent.action.slice("BLOCKED:".length).trim() : recent.action;
231699
- const classification = classifyBlockedExit(reason, []);
231700
- if (classification.allowAutoReplan) return false;
231701
- const thrashCount = countBlockedThrashHits(live.log, classification.thrashSignature) + 1;
231702
- const thrashExhausted = thrashCount >= BLOCKED_THRASH_LIMIT;
231703
- const parkError = thrashExhausted ? `BLOCKED: ${reason} [thrash-exhausted after ${thrashCount} identical durable blocks]` : `BLOCKED: ${reason}`;
231704
- const metaPatch = buildExternalBlockMetadataPatch(classification, thrashCount);
231705
- await this.store.updateTask(live.id, {
231706
- status: "failed",
231707
- error: parkError,
231708
- paused: false,
231709
- pausedByAgentId: null,
231710
- sourceMetadataPatch: metaPatch
231711
- }, this.getRunContextFor(live.id));
231712
- await this.store.logEntry(
231713
- live.id,
231714
- thrashExhausted ? `${parkError} \u2014 promoted from session log; thrash-exhausted, no auto-requeue` : `${parkError} \u2014 promoted from session log to durable external block park (class=${classification.class})`,
231715
- void 0,
231716
- this.getRunContextFor(live.id)
231717
- );
231718
- await this.store.recordRunAuditEvent?.({
231719
- taskId: live.id,
231720
- agentId: "executor",
231721
- runId: generateSyntheticRunId("execution-blocked-log-promote", live.id),
231722
- domain: "database",
231723
- mutationType: "task:execution-blocked-parked",
231724
- target: live.id,
231725
- metadata: {
231726
- taskId: live.id,
231727
- blockedBy: [],
231728
- hasReason: true,
231729
- parkedAs: "failed",
231730
- blockedClass: classification.class,
231731
- thrashCount,
231732
- thrashExhausted,
231733
- prNumbers: classification.prNumbers,
231734
- source: "session-log-promote"
231735
- }
231736
- });
231737
- executorLog.warn(
231738
- `${live.id}: promoted durable BLOCKED from session log (class=${classification.class}; thrash=${thrashCount})`
231739
- );
231740
- return true;
231741
- }
231742
231628
  async routeGraphFailureToExecutionResume(live, failedNode, failureValue, resumeLanesMemo) {
231743
231629
  if (live.deletedAt) return false;
231744
231630
  if (live.paused || live.userPaused === true) return false;
231745
231631
  if ((await resolveTerminalColumnsFor(this.store, live.id)).includes(live.column)) return false;
231746
- if (isDurableBlockedTask(live) || isDurableBlockedError(live.error)) {
231632
+ if (isDurableBlockedTask(live)) {
231747
231633
  executorLog.log(
231748
- `${live.id}: graph failure resume skipped \u2014 durable BLOCKED park honored (class from error/metadata)`
231634
+ `${live.id}: graph failure resume skipped \u2014 durable BLOCKED park honored (task-dependency block)`
231749
231635
  );
231750
231636
  return false;
231751
231637
  }
231752
- if (await this.parkDurableBlockedFromRecentLog(live)) {
231753
- return false;
231754
- }
231755
231638
  if (failedNode === COMPLETION_SUMMARY_NODE_ID) return false;
231756
231639
  const incompleteSteps = hasNonTerminalWorkflowSteps(live);
231757
231640
  const implementationIncompleteMergeFailure = this.isMergeGraphFailure(failedNode) && failureValue === "implementation-incomplete";
@@ -232932,7 +232815,7 @@ ${summary}`,
232932
232815
  executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay6}: ${errorMessage2}`);
232933
232816
  await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay6}): ${errorMessage2}`, void 0, this.getRunContextFor(task.id));
232934
232817
  }
232935
- if (worktreePath && existsSync65(worktreePath)) {
232818
+ if (worktreePath && existsSync66(worktreePath)) {
232936
232819
  try {
232937
232820
  const settings2 = await this.store.getSettings();
232938
232821
  await removeWorktree({
@@ -233013,7 +232896,7 @@ ${summary}`,
233013
232896
  const settings2 = await this.store.getSettings();
233014
232897
  const preserveProgress = settings2.preserveProgressOnStuckRequeue !== false;
233015
232898
  await this.resetStepsIfWorkLost(latestTask);
233016
- if (worktreePath && existsSync65(worktreePath)) {
232899
+ if (worktreePath && existsSync66(worktreePath)) {
233017
232900
  try {
233018
232901
  await removeWorktree({
233019
232902
  worktreePath,
@@ -233296,7 +233179,7 @@ ${summary}`,
233296
233179
  nodeId: detail.effectiveNodeId ?? detail.nodeId ?? null,
233297
233180
  agentId: engineRunContext.agentId ?? null
233298
233181
  });
233299
- let isResuming = !!task.sessionFile && existsSync65(task.sessionFile);
233182
+ let isResuming = !!task.sessionFile && existsSync66(task.sessionFile);
233300
233183
  if (isResuming) {
233301
233184
  const persistedWorktreePath = await extractPersistedSessionWorktreePath(task.sessionFile, this.rootDir, settings);
233302
233185
  if (!isSessionWorktreeCompatible(persistedWorktreePath, worktreePath)) {
@@ -234077,7 +233960,7 @@ ${summary}`,
234077
233960
  return;
234078
233961
  } else {
234079
233962
  executorLog.log(`${task.id} paused \u2014 moving to todo`);
234080
- if (worktreePath && existsSync65(worktreePath)) {
233963
+ if (worktreePath && existsSync66(worktreePath)) {
234081
233964
  try {
234082
233965
  const settings2 = await this.store.getSettings();
234083
233966
  await removeWorktree({
@@ -234474,7 +234357,7 @@ ${summary}`,
234474
234357
  executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay6}: ${errorMessage2}`);
234475
234358
  await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay6}): ${errorMessage2}`, void 0, this.getRunContextFor(task.id));
234476
234359
  }
234477
- if (worktreePath && existsSync65(worktreePath)) {
234360
+ if (worktreePath && existsSync66(worktreePath)) {
234478
234361
  try {
234479
234362
  const settings2 = await this.store.getSettings();
234480
234363
  await removeWorktree({
@@ -234610,7 +234493,7 @@ ${summary}`,
234610
234493
  const settings2 = await this.store.getSettings();
234611
234494
  const preserveProgress = settings2.preserveProgressOnStuckRequeue !== false;
234612
234495
  await this.resetStepsIfWorkLost(latestTask);
234613
- if (worktreePath && existsSync65(worktreePath)) {
234496
+ if (worktreePath && existsSync66(worktreePath)) {
234614
234497
  try {
234615
234498
  await removeWorktree({
234616
234499
  worktreePath,
@@ -235037,7 +234920,7 @@ ${summary}`,
235037
234920
  for (const repoRel of Object.keys(workspaceWorktrees).sort()) {
235038
234921
  const repo = workspaceWorktrees[repoRel];
235039
234922
  const expectedBranch = repo.branch || canonicalFusionBranchName(task.id);
235040
- if (!existsSync65(repo.worktreePath)) {
234923
+ if (!existsSync66(repo.worktreePath)) {
235041
234924
  executorLog.log(`${task.id}: workspace worktree for ${repoRel} not found at ${repo.worktreePath} \u2014 skipping git validation`);
235042
234925
  continue;
235043
234926
  }
@@ -235169,7 +235052,7 @@ ${summary}`,
235169
235052
  expected: `resolvable task worktree under ${resolveWorktreesDir(this.rootDir, settings)}/*`
235170
235053
  };
235171
235054
  }
235172
- if (!existsSync65(worktreePath)) {
235055
+ if (!existsSync66(worktreePath)) {
235173
235056
  executorLog.log(
235174
235057
  `${task.id}: worktree directory not found at ${worktreePath} \u2014 skipping git validation for task completion`
235175
235058
  );
@@ -235498,10 +235381,10 @@ ${summary}`,
235498
235381
  { description: '"completed" (default) finishes the task; "blocked" honestly parks it as failed because the work cannot proceed. Use "blocked" instead of skipping steps + completing when you are stuck.' }
235499
235382
  )),
235500
235383
  blockedBy: Type7.Optional(Type7.Array(Type7.String(), {
235501
- description: 'When outcome="blocked": task IDs (e.g. ["FN-8145"]) and/or PR refs (e.g. ["pr:2398"]) that must clear before this task can proceed. Task IDs become real dependency edges; PR refs are stored as external blockers (durable park until the PR merges/closes).'
235384
+ description: 'When outcome="blocked": Fusion task IDs (e.g. ["FN-8145"]) that must complete before this task can proceed. Task IDs become real dependency edges. Open GitHub PRs are not valid blockers.'
235502
235385
  })),
235503
235386
  reason: Type7.Optional(Type7.String({
235504
- description: 'Required when outcome="blocked": concrete explanation of what is blocking the work and what is needed to unblock it. File-claim / open-PR collisions should name the PR (e.g. "actively claimed by PR #2398").'
235387
+ description: 'Required when outcome="blocked": concrete explanation of what is blocking the work and what is needed to unblock it.'
235505
235388
  }))
235506
235389
  }),
235507
235390
  execute: async (_id, params) => {
@@ -235560,7 +235443,7 @@ ${summary}`,
235560
235443
  }, this.getRunContextFor(taskId));
235561
235444
  await store.logEntry(
235562
235445
  taskId,
235563
- thrashExhausted ? `${parkError} \u2014 durable external block thrash-exhausted (signature=${classification.thrashSignature}); parked failed, no auto-requeue` : classification.externalBlockers.length > 0 ? `${parkError} \u2014 durable external block (${classification.class}; pr=${classification.prNumbers.join(",") || "none"}; tasks=${blockedByIds.join(",") || "none"}) \u2014 parked failed (honest blocked exit; steps preserved)` : `${parkError} \u2014 recorded dependencies: ${blockedByIds.join(", ")} \u2014 parked failed (honest blocked exit; steps preserved)`,
235446
+ thrashExhausted ? `${parkError} \u2014 durable external block thrash-exhausted (signature=${classification.thrashSignature}); parked failed, no auto-requeue` : `${parkError} \u2014 recorded dependencies: ${blockedByIds.join(", ")} \u2014 parked failed (honest blocked exit; steps preserved)`,
235564
235447
  void 0,
235565
235448
  this.getRunContextFor(taskId)
235566
235449
  );
@@ -235579,18 +235462,17 @@ ${summary}`,
235579
235462
  parkedAs: autoReplanPark ? "auto-replan" : "failed",
235580
235463
  blockedClass: classification.class,
235581
235464
  thrashCount,
235582
- thrashExhausted,
235583
- prNumbers: classification.prNumbers
235465
+ thrashExhausted
235584
235466
  }
235585
235467
  });
235586
235468
  await this.persistTokenUsage(taskId);
235587
235469
  executorLog.log(
235588
- `\u26D4 ${taskId} ${autoReplanPark ? "parked for automatic replan via blocked exit (plan defect, no dependencies)" : thrashExhausted ? `parked failed via blocked thrash-exhaustion (class=${classification.class})` : `parked failed via durable blocked exit (class=${classification.class}; blockedBy tasks: ${blockedByIds.join(", ") || "none"}; pr: ${classification.prNumbers.join(",") || "none"})`}`
235470
+ `\u26D4 ${taskId} ${autoReplanPark ? "parked for automatic replan via blocked exit (plan defect, no dependencies)" : thrashExhausted ? `parked failed via blocked thrash-exhaustion (class=${classification.class})` : `parked failed via durable blocked exit (class=${classification.class}; blockedBy tasks: ${blockedByIds.join(", ") || "none"})`}`
235589
235471
  );
235590
235472
  return {
235591
235473
  content: [{
235592
235474
  type: "text",
235593
- text: autoReplanPark ? "Task parked as blocked with no external/file-claim blocker \u2014 queued for automatic replan so the plan can resolve the conflict. Steps left in their true statuses; no completion recorded." : thrashExhausted ? "Task parked as blocked (failed) after repeated identical durable blocks \u2014 no further automatic retries. Resolve the external claim/PR or replan manually." : blockedByIds.length > 0 ? `Task parked as blocked (failed). Recorded ${blockedByIds.length} blocking task dependency(ies); it will requeue once they complete. Steps left in their true statuses; no completion recorded.` : classification.prNumbers.length > 0 ? `Task parked as blocked (failed) on open PR #${classification.prNumbers.join(", #")}. It will not auto-replan or re-execute until that claim clears or an operator retries. Steps preserved.` : "Task parked as blocked (failed) on a durable external/file-claim blocker. No automatic replan. Steps preserved."
235475
+ text: autoReplanPark ? "Task parked as blocked with no blocking task dependencies \u2014 queued for automatic replan so the plan can resolve the conflict. Steps left in their true statuses; no completion recorded." : thrashExhausted ? "Task parked as blocked (failed) after repeated identical durable blocks \u2014 no further automatic retries. Resolve the blocking tasks or replan manually." : `Task parked as blocked (failed). Recorded ${blockedByIds.length} blocking task dependency(ies); it will requeue once they complete. Steps left in their true statuses; no completion recorded.`
235594
235476
  }],
235595
235477
  details: {}
235596
235478
  };
@@ -237648,7 +237530,7 @@ ${detectedQuestion}
237648
237530
  * rather than fail the task permanently.
237649
237531
  */
237650
237532
  async resolveWorktreeStartPoint(startPoint, taskId) {
237651
- const command = isAbsolute18(startPoint) && existsSync65(startPoint) ? `git -C "${startPoint}" rev-parse --verify HEAD^{commit}` : `git rev-parse --verify "${startPoint}^{commit}"`;
237533
+ const command = isAbsolute18(startPoint) && existsSync66(startPoint) ? `git -C "${startPoint}" rev-parse --verify HEAD^{commit}` : `git rev-parse --verify "${startPoint}^{commit}"`;
237652
237534
  try {
237653
237535
  const { stdout } = await execAsync36(command, { cwd: this.rootDir });
237654
237536
  return stdout.trim() || startPoint;
@@ -237871,7 +237753,7 @@ ${detectedQuestion}
237871
237753
  throw error3;
237872
237754
  }
237873
237755
  };
237874
- if (existsSync65(path10)) {
237756
+ if (existsSync66(path10)) {
237875
237757
  const isRegistered = await this.isRegisteredWorktree(path10);
237876
237758
  if (!isRegistered) {
237877
237759
  await this.store.logEntry(
@@ -238487,7 +238369,7 @@ ${detectedQuestion}
238487
238369
  const stillRegistered = await isRegisteredGitWorktree2(this.rootDir, worktreePath).catch(() => true);
238488
238370
  const activeOwner2 = await this.findActiveWorktreeOwner(worktreePath, taskId).catch(() => "unknown");
238489
238371
  let safeToRemove = isInsideWorktreesDir(this.rootDir, worktreePath, settings) && !stillRegistered && activeOwner2 === null;
238490
- if (safeToRemove && existsSync65(worktreePath)) {
238372
+ if (safeToRemove && existsSync66(worktreePath)) {
238491
238373
  try {
238492
238374
  if (lstatSync3(worktreePath).isSymbolicLink()) {
238493
238375
  safeToRemove = false;
@@ -238515,7 +238397,7 @@ ${detectedQuestion}
238515
238397
  const pruneMsg = pruneErr instanceof Error ? pruneErr.message : String(pruneErr);
238516
238398
  executorLog.warn(`${taskId}: git worktree prune failed during stale-path cleanup of ${worktreePath}: ${pruneMsg}`);
238517
238399
  }
238518
- if (existsSync65(worktreePath)) {
238400
+ if (existsSync66(worktreePath)) {
238519
238401
  try {
238520
238402
  await rm12(worktreePath, { recursive: true, force: true });
238521
238403
  } catch (rmErr) {
@@ -238962,7 +238844,7 @@ ${detectedQuestion}
238962
238844
  this.clearPausedAborted(taskId);
238963
238845
  await this.resetStepsIfWorkLost(latestTask);
238964
238846
  let cleanupFailed = false;
238965
- if (worktreePath && existsSync65(worktreePath)) {
238847
+ if (worktreePath && existsSync66(worktreePath)) {
238966
238848
  try {
238967
238849
  await removeWorktree({
238968
238850
  worktreePath,
@@ -243049,7 +242931,7 @@ var init_overseer_noop_finalize_veto = __esm({
243049
242931
 
243050
242932
  // ../engine/src/merger-ai-worktree.ts
243051
242933
  import { execFile as execFile27 } from "node:child_process";
243052
- import { appendFileSync as appendFileSync2, existsSync as existsSync66, mkdirSync as mkdirSync11, readdirSync as readdirSync12, readFileSync as readFileSync24, realpathSync as realpathSync11, rmSync as rmSync9, statSync as statSync13 } from "node:fs";
242934
+ import { appendFileSync as appendFileSync2, existsSync as existsSync67, mkdirSync as mkdirSync11, readdirSync as readdirSync12, readFileSync as readFileSync24, realpathSync as realpathSync11, rmSync as rmSync9, statSync as statSync13 } from "node:fs";
243053
242935
  import { rm as rm13 } from "node:fs/promises";
243054
242936
  import { tmpdir as tmpdir10 } from "node:os";
243055
242937
  import { isAbsolute as isAbsolute19, join as join79, relative as relative17 } from "node:path";
@@ -243084,7 +242966,7 @@ function isBenignAbsentWorktreeError(err) {
243084
242966
  }
243085
242967
  function ensureAiMergeRootIgnored(projectRootDir, settings) {
243086
242968
  const excludePath = join79(projectRootDir, ".git", "info", "exclude");
243087
- if (!existsSync66(excludePath)) return;
242969
+ if (!existsSync67(excludePath)) return;
243088
242970
  try {
243089
242971
  const current = readFileSync24(excludePath, "utf-8");
243090
242972
  const legacyAiMergeRoot = resolveLegacyAiMergeRootPath(projectRootDir);
@@ -243215,7 +243097,7 @@ async function cleanupAiMergeWorktree(input) {
243215
243097
  const cleanupMetadata = { taskId, mergeRoot: canonicalRoot, requestedMergeRoot: mergeRoot };
243216
243098
  let alreadyAbsent = false;
243217
243099
  if (worktreeAdded) {
243218
- if (!existsSync66(canonicalRoot) && !existsSync66(mergeRoot)) {
243100
+ if (!existsSync67(canonicalRoot) && !existsSync67(mergeRoot)) {
243219
243101
  alreadyAbsent = true;
243220
243102
  await log41(`AI merge cleanup: worktree ${canonicalRoot} was already absent before git removal; treating cleanup as idempotent`);
243221
243103
  await audit.git({ type: "merge:ai-worktree-cleanup", target: canonicalRoot, metadata: { ...cleanupMetadata, phase: "git-remove", success: true, alreadyAbsent: true, idempotent: true, code: "ENOENT" } });
@@ -249537,12 +249419,12 @@ The note must be specific (what/where/why). Prefer citing File Scope, PROMPT, a
249537
249419
  });
249538
249420
 
249539
249421
  // ../engine/src/overseer-watchdog.ts
249540
- import { existsSync as existsSync67, readFileSync as readFileSync25 } from "node:fs";
249422
+ import { existsSync as existsSync68, readFileSync as readFileSync25 } from "node:fs";
249541
249423
  import { dirname as dirname34, join as join82, relative as relative18, resolve as resolve40, sep as sep11 } from "node:path";
249542
249424
  import { homedir as homedir12 } from "node:os";
249543
249425
  function defaultReadText(path10) {
249544
249426
  try {
249545
- if (!existsSync67(path10)) return null;
249427
+ if (!existsSync68(path10)) return null;
249546
249428
  return readFileSync25(path10, "utf8");
249547
249429
  } catch {
249548
249430
  return null;
@@ -255343,7 +255225,7 @@ var init_adapters = __esm({
255343
255225
  });
255344
255226
 
255345
255227
  // ../engine/src/cli-agent/resume-coordinator.ts
255346
- import { existsSync as existsSync68 } from "node:fs";
255228
+ import { existsSync as existsSync69 } from "node:fs";
255347
255229
  import { execFile as execFile30 } from "node:child_process";
255348
255230
  import { promisify as promisify62 } from "node:util";
255349
255231
  async function defaultIsWorktreeDirty(worktreePath) {
@@ -255388,7 +255270,7 @@ var init_resume_coordinator = __esm({
255388
255270
  this.reattachTelemetry = opts.reattachTelemetry;
255389
255271
  this.maxResumeAttempts = opts.maxResumeAttempts ?? DEFAULT_MAX_RESUME_ATTEMPTS;
255390
255272
  this.resumeBackoffBaseMs = opts.resumeBackoffBaseMs ?? DEFAULT_RESUME_BACKOFF_BASE_MS;
255391
- this.worktreeExists = opts.worktreeExists ?? ((p2) => existsSync68(p2));
255273
+ this.worktreeExists = opts.worktreeExists ?? ((p2) => existsSync69(p2));
255392
255274
  this.isWorktreeDirty = opts.isWorktreeDirty ?? defaultIsWorktreeDirty;
255393
255275
  this.log = opts.log ?? (() => {
255394
255276
  });
@@ -283184,7 +283066,7 @@ var init_cli_spawn = __esm({
283184
283066
  // ../../plugins/fusion-plugin-hermes-runtime/src/fusion-skill-install.ts
283185
283067
  import {
283186
283068
  cpSync as cpSync2,
283187
- existsSync as existsSync69,
283069
+ existsSync as existsSync70,
283188
283070
  lstatSync as lstatSync4,
283189
283071
  mkdirSync as mkdirSync13,
283190
283072
  readFileSync as readFileSync26,
@@ -283214,7 +283096,7 @@ function getFusionSkillSourceCandidates(moduleUrl = import.meta.url) {
283214
283096
  function resolveBundledFusionSkillSource() {
283215
283097
  const candidates = getFusionSkillSourceCandidates();
283216
283098
  for (const candidate of candidates) {
283217
- if (existsSync69(join87(candidate, "SKILL.md"))) return candidate;
283099
+ if (existsSync70(join87(candidate, "SKILL.md"))) return candidate;
283218
283100
  }
283219
283101
  return null;
283220
283102
  }
@@ -283232,7 +283114,7 @@ function installFusionSkillIntoHermesHome(options = {}) {
283232
283114
  try {
283233
283115
  mkdirSync13(dirname36(targetDir), { recursive: true });
283234
283116
  let replaced = false;
283235
- if (existsSync69(targetDir) || isBrokenSymlink(targetDir)) {
283117
+ if (existsSync70(targetDir) || isBrokenSymlink(targetDir)) {
283236
283118
  const stat13 = lstatSync4(targetDir);
283237
283119
  if (stat13.isSymbolicLink()) {
283238
283120
  const currentTarget = safeReadlink(targetDir);
@@ -283303,14 +283185,14 @@ function safeReadlink(path10) {
283303
283185
  function isBrokenSymlink(path10) {
283304
283186
  try {
283305
283187
  const stat13 = lstatSync4(path10);
283306
- return stat13.isSymbolicLink() && !existsSync69(path10);
283188
+ return stat13.isSymbolicLink() && !existsSync70(path10);
283307
283189
  } catch {
283308
283190
  return false;
283309
283191
  }
283310
283192
  }
283311
283193
  function looksLikePriorFusionInstall(path10) {
283312
283194
  const skillMd = join87(path10, "SKILL.md");
283313
- if (!existsSync69(skillMd)) return false;
283195
+ if (!existsSync70(skillMd)) return false;
283314
283196
  try {
283315
283197
  const body = readFileSync26(skillMd, "utf-8");
283316
283198
  return /\bfusion\b/i.test(body) && /\bskill\b/i.test(body);
@@ -283321,7 +283203,7 @@ function looksLikePriorFusionInstall(path10) {
283321
283203
  function looksLikeFusionSkillTarget(path10) {
283322
283204
  if (!path10) return false;
283323
283205
  if (basename20(path10).toLowerCase() === FUSION_SKILL_NAME) return true;
283324
- return existsSync69(join87(path10, "SKILL.md"));
283206
+ return existsSync70(join87(path10, "SKILL.md"));
283325
283207
  }
283326
283208
  var FUSION_SKILL_NAME;
283327
283209
  var init_fusion_skill_install = __esm({
@@ -284677,7 +284559,7 @@ var init_provider2 = __esm({
284677
284559
  });
284678
284560
 
284679
284561
  // ../../plugins/fusion-plugin-grok-runtime/src/acp/cli-spawn.ts
284680
- import { existsSync as existsSync70 } from "node:fs";
284562
+ import { existsSync as existsSync71 } from "node:fs";
284681
284563
  import { dirname as dirname38, isAbsolute as isAbsolute22, join as join90, resolve as resolve42 } from "node:path";
284682
284564
  import { fileURLToPath as fileURLToPath7 } from "node:url";
284683
284565
  function asTrimmedString(value) {
@@ -284700,7 +284582,7 @@ function bundledClaudeBridgeBinPath(pluginRoot = pluginRootDir()) {
284700
284582
  }
284701
284583
  function resolveBundledClaudeBridgeBinary(options = {}) {
284702
284584
  const root = options.pluginRoot ?? pluginRootDir();
284703
- const exists2 = options.exists ?? existsSync70;
284585
+ const exists2 = options.exists ?? existsSync71;
284704
284586
  const candidate = bundledClaudeBridgeBinPath(root);
284705
284587
  if (!exists2(candidate)) {
284706
284588
  return {
@@ -286049,7 +285931,7 @@ var init_mcp_forwarding = __esm({
286049
285931
  // ../../plugins/fusion-plugin-grok-runtime/src/skill-loader.ts
286050
285932
  import {
286051
285933
  cpSync as cpSync3,
286052
- existsSync as existsSync71,
285934
+ existsSync as existsSync72,
286053
285935
  mkdirSync as mkdirSync14,
286054
285936
  mkdtempSync as mkdtempSync3,
286055
285937
  readdirSync as readdirSync14,
@@ -286062,7 +285944,7 @@ import { tmpdir as tmpdir15 } from "node:os";
286062
285944
  import { basename as basename22, dirname as dirname40, join as join92, resolve as resolve44 } from "node:path";
286063
285945
  import { fileURLToPath as fileURLToPath8 } from "node:url";
286064
285946
  function isSkillDir(dir) {
286065
- return existsSync71(join92(dir, "SKILL.md"));
285947
+ return existsSync72(join92(dir, "SKILL.md"));
286066
285948
  }
286067
285949
  function pushUnique(out, candidate) {
286068
285950
  if (!candidate) return;
@@ -286126,7 +286008,7 @@ function resolveBundledFusionSkillSource2(moduleUrl = import.meta.url) {
286126
286008
  function installSkillDir(sourceDir, targetDir) {
286127
286009
  if (!isSkillDir(sourceDir)) return false;
286128
286010
  mkdirSync14(dirname40(targetDir), { recursive: true });
286129
- if (existsSync71(targetDir)) {
286011
+ if (existsSync72(targetDir)) {
286130
286012
  rmSync11(targetDir, { recursive: true, force: true });
286131
286013
  }
286132
286014
  try {
@@ -286142,7 +286024,7 @@ function installSkillDir(sourceDir, targetDir) {
286142
286024
  }
286143
286025
  }
286144
286026
  function collectSkillsFromRoot(root, out) {
286145
- if (!existsSync71(root)) return;
286027
+ if (!existsSync72(root)) return;
286146
286028
  if (isSkillDir(root)) {
286147
286029
  out.set(basename22(root), root);
286148
286030
  return;
@@ -286181,7 +286063,7 @@ function stageGrokSessionSkills(options = {}) {
286181
286063
  installSkillDir(source, join92(skillsDir, name2));
286182
286064
  }
286183
286065
  for (const [name2, source] of installed) {
286184
- if (!existsSync71(join92(skillsDir, name2))) {
286066
+ if (!existsSync72(join92(skillsDir, name2))) {
286185
286067
  installSkillDir(source, join92(skillsDir, name2));
286186
286068
  }
286187
286069
  }
@@ -286250,7 +286132,7 @@ var init_skill_loader = __esm({
286250
286132
 
286251
286133
  // ../../plugins/fusion-plugin-grok-runtime/src/tool-bridge.ts
286252
286134
  import { createServer as createServer2 } from "node:http";
286253
- import { existsSync as existsSync72, writeFileSync as writeFileSync15 } from "node:fs";
286135
+ import { existsSync as existsSync73, writeFileSync as writeFileSync15 } from "node:fs";
286254
286136
  import { tmpdir as tmpdir16 } from "node:os";
286255
286137
  import { dirname as dirname41, join as join93 } from "node:path";
286256
286138
  import { fileURLToPath as fileURLToPath9 } from "node:url";
@@ -286296,7 +286178,7 @@ async function startFusionToolBridge(tools) {
286296
286178
  const defs = toolsToMcpToolDefs2(tools);
286297
286179
  if (defs.length === 0) return null;
286298
286180
  const serverPath = fusionToolsMcpServerPath();
286299
- if (!existsSync72(serverPath)) {
286181
+ if (!existsSync73(serverPath)) {
286300
286182
  throw missingMcpSchemaServerError(serverPath);
286301
286183
  }
286302
286184
  const byName = /* @__PURE__ */ new Map();
@@ -286737,7 +286619,7 @@ var init_src11 = __esm({
286737
286619
  });
286738
286620
 
286739
286621
  // ../../plugins/fusion-plugin-claude-runtime/src/acp/cli-spawn.ts
286740
- import { existsSync as existsSync73 } from "node:fs";
286622
+ import { existsSync as existsSync74 } from "node:fs";
286741
286623
  import { dirname as dirname42, isAbsolute as isAbsolute23, join as join94, resolve as resolve45 } from "node:path";
286742
286624
  import { fileURLToPath as fileURLToPath10 } from "node:url";
286743
286625
  function asTrimmedString2(value) {
@@ -286760,7 +286642,7 @@ function bundledClaudeBridgeBinPath2(pluginRoot = pluginRootDir2()) {
286760
286642
  }
286761
286643
  function resolveBundledClaudeBridgeBinary2(options = {}) {
286762
286644
  const root = options.pluginRoot ?? pluginRootDir2();
286763
- const exists2 = options.exists ?? existsSync73;
286645
+ const exists2 = options.exists ?? existsSync74;
286764
286646
  const candidate = bundledClaudeBridgeBinPath2(root);
286765
286647
  if (!exists2(candidate)) {
286766
286648
  return {
@@ -287995,7 +287877,7 @@ var init_acp2 = __esm({
287995
287877
 
287996
287878
  // ../../plugins/fusion-plugin-claude-runtime/src/cli-spawn.ts
287997
287879
  import { spawn as spawn19 } from "node:child_process";
287998
- import { existsSync as existsSync74 } from "node:fs";
287880
+ import { existsSync as existsSync75 } from "node:fs";
287999
287881
  import { basename as basename24, dirname as dirname44, isAbsolute as isAbsolute24, join as join96, resolve as resolve47 } from "node:path";
288000
287882
  import { fileURLToPath as fileURLToPath11 } from "node:url";
288001
287883
  function pluginRootDir3() {
@@ -288007,7 +287889,7 @@ function bundledClaudeBridgeBinPath3(pluginRoot = pluginRootDir3()) {
288007
287889
  }
288008
287890
  function resolveBundledClaudeBridgeBinary3(options = {}) {
288009
287891
  const candidate = bundledClaudeBridgeBinPath3(options.pluginRoot ?? pluginRootDir3());
288010
- const exists2 = options.exists ?? existsSync74;
287892
+ const exists2 = options.exists ?? existsSync75;
288011
287893
  if (!exists2(candidate) || !isAbsolute24(candidate)) return { kind: "not_resolved", requested: CLAUDE_CODE_CLI_ACP_BINARY3, path: candidate, reason: `Staged ${CLAUDE_CODE_CLI_ACP_BINARY3} bridge was not found at ${candidate}` };
288012
287894
  return { kind: "resolved", requested: CLAUDE_CODE_CLI_ACP_BINARY3, path: candidate };
288013
287895
  }
@@ -288170,7 +288052,7 @@ var init_mcp_forwarding2 = __esm({
288170
288052
  // ../../plugins/fusion-plugin-claude-runtime/src/skill-loader.ts
288171
288053
  import {
288172
288054
  cpSync as cpSync4,
288173
- existsSync as existsSync75,
288055
+ existsSync as existsSync76,
288174
288056
  mkdirSync as mkdirSync15,
288175
288057
  mkdtempSync as mkdtempSync4,
288176
288058
  readdirSync as readdirSync15,
@@ -288183,7 +288065,7 @@ import { tmpdir as tmpdir17 } from "node:os";
288183
288065
  import { basename as basename25, dirname as dirname45, join as join97, resolve as resolve48 } from "node:path";
288184
288066
  import { fileURLToPath as fileURLToPath12 } from "node:url";
288185
288067
  function isSkillDir2(dir) {
288186
- return existsSync75(join97(dir, "SKILL.md"));
288068
+ return existsSync76(join97(dir, "SKILL.md"));
288187
288069
  }
288188
288070
  function pushUnique2(out, candidate) {
288189
288071
  if (!candidate) return;
@@ -288247,7 +288129,7 @@ function resolveBundledFusionSkillSource3(moduleUrl = import.meta.url) {
288247
288129
  function installSkillDir2(sourceDir, targetDir) {
288248
288130
  if (!isSkillDir2(sourceDir)) return false;
288249
288131
  mkdirSync15(dirname45(targetDir), { recursive: true });
288250
- if (existsSync75(targetDir)) {
288132
+ if (existsSync76(targetDir)) {
288251
288133
  rmSync12(targetDir, { recursive: true, force: true });
288252
288134
  }
288253
288135
  try {
@@ -288263,7 +288145,7 @@ function installSkillDir2(sourceDir, targetDir) {
288263
288145
  }
288264
288146
  }
288265
288147
  function collectSkillsFromRoot2(root, out) {
288266
- if (!existsSync75(root)) return;
288148
+ if (!existsSync76(root)) return;
288267
288149
  if (isSkillDir2(root)) {
288268
288150
  out.set(basename25(root), root);
288269
288151
  return;
@@ -288302,7 +288184,7 @@ function stageClaudeSessionSkills(options = {}) {
288302
288184
  installSkillDir2(source, join97(skillsDir, name2));
288303
288185
  }
288304
288186
  for (const [name2, source] of installed) {
288305
- if (!existsSync75(join97(skillsDir, name2))) {
288187
+ if (!existsSync76(join97(skillsDir, name2))) {
288306
288188
  installSkillDir2(source, join97(skillsDir, name2));
288307
288189
  }
288308
288190
  }
@@ -288371,7 +288253,7 @@ var init_skill_loader2 = __esm({
288371
288253
 
288372
288254
  // ../../plugins/fusion-plugin-claude-runtime/src/tool-bridge.ts
288373
288255
  import { createServer as createServer3 } from "node:http";
288374
- import { existsSync as existsSync76, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
288256
+ import { existsSync as existsSync77, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
288375
288257
  import { tmpdir as tmpdir18 } from "node:os";
288376
288258
  import { dirname as dirname46, join as join98 } from "node:path";
288377
288259
  import { fileURLToPath as fileURLToPath13 } from "node:url";
@@ -288417,7 +288299,7 @@ async function startFusionToolBridge2(tools, options = {}) {
288417
288299
  const defs = toolsToMcpToolDefs3(tools);
288418
288300
  if (defs.length === 0) return null;
288419
288301
  const serverPath = fusionToolsMcpServerPath2();
288420
- if (!existsSync76(serverPath)) {
288302
+ if (!existsSync77(serverPath)) {
288421
288303
  throw missingMcpSchemaServerError2(serverPath);
288422
288304
  }
288423
288305
  const byName = /* @__PURE__ */ new Map();
@@ -289229,7 +289111,7 @@ var init_provider6 = __esm({
289229
289111
  });
289230
289112
 
289231
289113
  // ../../plugins/fusion-plugin-omp-runtime/src/acp/cli-spawn.ts
289232
- import { existsSync as existsSync77 } from "node:fs";
289114
+ import { existsSync as existsSync78 } from "node:fs";
289233
289115
  import { dirname as dirname47, isAbsolute as isAbsolute25, join as join99, resolve as resolve49 } from "node:path";
289234
289116
  import { fileURLToPath as fileURLToPath14 } from "node:url";
289235
289117
  function asTrimmedString3(value) {
@@ -289252,7 +289134,7 @@ function bundledClaudeBridgeBinPath4(pluginRoot = pluginRootDir4()) {
289252
289134
  }
289253
289135
  function resolveBundledClaudeBridgeBinary4(options = {}) {
289254
289136
  const root = options.pluginRoot ?? pluginRootDir4();
289255
- const exists2 = options.exists ?? existsSync77;
289137
+ const exists2 = options.exists ?? existsSync78;
289256
289138
  const candidate = bundledClaudeBridgeBinPath4(root);
289257
289139
  if (!exists2(candidate)) {
289258
289140
  return {
@@ -290564,7 +290446,7 @@ var init_mcp_forwarding3 = __esm({
290564
290446
 
290565
290447
  // ../../plugins/fusion-plugin-omp-runtime/src/tool-bridge.ts
290566
290448
  import { createServer as createServer4 } from "node:http";
290567
- import { existsSync as existsSync78, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "node:fs";
290449
+ import { existsSync as existsSync79, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "node:fs";
290568
290450
  import { tmpdir as tmpdir19 } from "node:os";
290569
290451
  import { dirname as dirname49, join as join101 } from "node:path";
290570
290452
  import { fileURLToPath as fileURLToPath15 } from "node:url";
@@ -290591,7 +290473,7 @@ function fusionToolsMcpServerPath3() {
290591
290473
  join101(here, "..", "mcp-schema-server.cjs")
290592
290474
  ];
290593
290475
  for (const candidate of candidates) {
290594
- if (existsSync78(candidate)) return candidate;
290476
+ if (existsSync79(candidate)) return candidate;
290595
290477
  }
290596
290478
  return null;
290597
290479
  }
@@ -290729,7 +290611,7 @@ var init_tool_bridge3 = __esm({
290729
290611
  });
290730
290612
 
290731
290613
  // ../../plugins/fusion-plugin-omp-runtime/src/runtime-adapter.ts
290732
- import { existsSync as existsSync79 } from "node:fs";
290614
+ import { existsSync as existsSync80 } from "node:fs";
290733
290615
  function compactDiagnostic3(value) {
290734
290616
  return value.replace(/\s+/g, " ").trim();
290735
290617
  }
@@ -290920,7 +290802,7 @@ var init_runtime_adapter9 = __esm({
290920
290802
  const command = server.command.trim();
290921
290803
  if (!command) return false;
290922
290804
  if (command.includes("/") || command.includes("\\")) {
290923
- return existsSync79(command);
290805
+ return existsSync80(command);
290924
290806
  }
290925
290807
  return true;
290926
290808
  });
@@ -294142,7 +294024,7 @@ __export(task_exports, {
294142
294024
  runTaskUpdate: () => runTaskUpdate
294143
294025
  });
294144
294026
  import { createInterface as createInterface2 } from "node:readline/promises";
294145
- import { watchFile, unwatchFile, statSync as statSync14, existsSync as existsSync80, readFileSync as readFileSync27 } from "node:fs";
294027
+ import { watchFile, unwatchFile, statSync as statSync14, existsSync as existsSync81, readFileSync as readFileSync27 } from "node:fs";
294146
294028
  import { basename as basename28, join as join104 } from "node:path";
294147
294029
  function columnLabel(column) {
294148
294030
  return COLUMN_LABELS[column] ?? column;
@@ -294716,7 +294598,7 @@ async function runTaskLogs(id, options = {}, projectName) {
294716
294598
  if (options.follow) {
294717
294599
  const projectPath = context.projectPath ?? process.cwd();
294718
294600
  const logPath = join104(projectPath, ".fusion", "tasks", id, "agent.log");
294719
- if (!existsSync80(logPath)) {
294601
+ if (!existsSync81(logPath)) {
294720
294602
  console.log(`
294721
294603
  Waiting for log file to be created...`);
294722
294604
  }
@@ -296119,7 +296001,7 @@ import { Type as Type9 } from "typebox";
296119
296001
  import { StringEnum } from "@earendil-works/pi-ai";
296120
296002
  import { resolve as resolve53, relative as relative20, isAbsolute as isAbsolute26, sep as sep16, basename as basename29, extname as extname4, join as join105 } from "node:path";
296121
296003
  import { readFile as readFile47 } from "node:fs/promises";
296122
- import { existsSync as existsSync81 } from "node:fs";
296004
+ import { existsSync as existsSync82 } from "node:fs";
296123
296005
  import { spawn as spawn23 } from "node:child_process";
296124
296006
  import { AsyncLocalStorage } from "node:async_hooks";
296125
296007
  function truncateAgentDiagnosticText(value, maxChars) {
@@ -296199,12 +296081,12 @@ function resolveProjectRoot2(cwd) {
296199
296081
  warnedMissingProjectRootResolver = true;
296200
296082
  console.warn("[fusion-extension] @fusion/core.getProjectRootFromWorktree is unavailable; using filesystem fallback resolution.");
296201
296083
  }
296202
- if (worktreeProjectRoot && existsSync81(join105(worktreeProjectRoot, ".fusion"))) {
296084
+ if (worktreeProjectRoot && existsSync82(join105(worktreeProjectRoot, ".fusion"))) {
296203
296085
  return worktreeProjectRoot;
296204
296086
  }
296205
296087
  let current = resolve53(cwd);
296206
296088
  while (true) {
296207
- if (existsSync81(join105(current, ".fusion"))) {
296089
+ if (existsSync82(join105(current, ".fusion"))) {
296208
296090
  return current;
296209
296091
  }
296210
296092
  const parent = resolve53(current, "..");