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

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 (95) hide show
  1. package/dist/bin.js +339 -473
  2. package/dist/child-process-worker.js +144 -278
  3. package/dist/client/.vite/manifest.json +254 -254
  4. package/dist/client/assets/{AgentDetailView-CTdyQL11.js → AgentDetailView-CnzGIxqj.js} +1 -1
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-DBa7nIQV.js → AgentPermissionPolicyEditor-DMnyR96M.js} +1 -1
  6. package/dist/client/assets/{AgentsView-B5tss2oK.js → AgentsView-CzFtICwM.js} +3 -3
  7. package/dist/client/assets/{ChatView-DAKt1a2L.js → ChatView-Cs6B3fNf.js} +1 -1
  8. package/dist/client/assets/{CommandCenter-yPvRo9F6.js → CommandCenter-DADvho3z.js} +1 -1
  9. package/dist/client/assets/{DevServerView-BTyS2owH.js → DevServerView-Bzrzfs8Z.js} +1 -1
  10. package/dist/client/assets/{DirectoryPicker-BjGIupNU.js → DirectoryPicker-rmip5GcI.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-CuxfbpSZ.js → DocumentsView-BEM4nmJT.js} +1 -1
  12. package/dist/client/assets/{EvalsView-DGuy8SHR.js → EvalsView-Du7NaYMJ.js} +1 -1
  13. package/dist/client/assets/{ExperimentalAgentOnboardingModal-lK407ZAq.js → ExperimentalAgentOnboardingModal-BpIMiZOi.js} +1 -1
  14. package/dist/client/assets/{GoalsView-DAVN0ObR.js → GoalsView-BxKRyXM3.js} +1 -1
  15. package/dist/client/assets/{InsightsView-BXQKcNof.js → InsightsView-Dauq6b3M.js} +1 -1
  16. package/dist/client/assets/{MemoryView-D7RKEtV7.js → MemoryView-DRD9Xvwx.js} +1 -1
  17. package/dist/client/assets/{PiExtensionsManager-CrOJ57R2.js → PiExtensionsManager-DJKE1iAX.js} +1 -1
  18. package/dist/client/assets/{PluginManager-D7_evt4j.js → PluginManager-CScwY-Ib.js} +1 -1
  19. package/dist/client/assets/{PullRequestView-C0lRUbth.js → PullRequestView-uAu5_4__.js} +1 -1
  20. package/dist/client/assets/{ReportModal-Cy5fRNDK.js → ReportModal-nNUcGxYj.js} +1 -1
  21. package/dist/client/assets/{ResearchView-e8st2Sxu.js → ResearchView-BX1kGbWP.js} +1 -1
  22. package/dist/client/assets/{SecretsView-BLzckrzb.js → SecretsView-U_itETHx.js} +1 -1
  23. package/dist/client/assets/{SessionTerminal-F_GIj4gh.js → SessionTerminal-_b15vcd9.js} +1 -1
  24. package/dist/client/assets/SettingsModal-B30rPfYC.css +1 -0
  25. package/dist/client/assets/{SettingsModal-XAddgWkF.js → SettingsModal-DkpUKf4k.js} +1 -1
  26. package/dist/client/assets/{SettingsModal-eQbxbkLU.js → SettingsModal-DpbYR3Co.js} +4 -4
  27. package/dist/client/assets/{SettingsTextareaRow-DVjUHNQp.js → SettingsTextareaRow-Dbv-oqsF.js} +1 -1
  28. package/dist/client/assets/{SetupWizardModal-xID9yIff.js → SetupWizardModal-n_kcJfIP.js} +2 -2
  29. package/dist/client/assets/{SkillsView-BNdMGu6E.js → SkillsView-Bv2uqBre.js} +1 -1
  30. package/dist/client/assets/{TodoView-xcOjHZm1.js → TodoView-y11oUvjw.js} +1 -1
  31. package/dist/client/assets/{WorkflowNodeEditor-DZ2WZ-ve.js → WorkflowNodeEditor-DwY4XzNB.js} +1 -1
  32. package/dist/client/assets/{agent-import-generation-0tkW9WMt.js → agent-import-generation-B_UNOAWY.js} +1 -1
  33. package/dist/client/assets/{architectureDiagram-3BPJPVTR-DP9_UvPn.js → architectureDiagram-3BPJPVTR-BpA9_eoq.js} +1 -1
  34. package/dist/client/assets/{blockDiagram-GPEHLZMM-EgKwYMgQ.js → blockDiagram-GPEHLZMM-DFFA8tCG.js} +1 -1
  35. package/dist/client/assets/{c4Diagram-AAUBKEIU-Dzv0-_xt.js → c4Diagram-AAUBKEIU-Fzhn0dG7.js} +1 -1
  36. package/dist/client/assets/channel-C9C9Rxgn.js +1 -0
  37. package/dist/client/assets/{chunk-2J33WTMH-CNmFmJlQ.js → chunk-2J33WTMH-Bi3oPWBJ.js} +1 -1
  38. package/dist/client/assets/{chunk-4BX2VUAB-DwtF2sl5.js → chunk-4BX2VUAB-UmeI_To2.js} +1 -1
  39. package/dist/client/assets/{chunk-55IACEB6-DoDurT6F.js → chunk-55IACEB6-BvNWeThd.js} +1 -1
  40. package/dist/client/assets/{chunk-727SXJPM-DB87A9uH.js → chunk-727SXJPM-OHmjSQwz.js} +1 -1
  41. package/dist/client/assets/{chunk-AQP2D5EJ-ClTDkMp1.js → chunk-AQP2D5EJ-DCi9eQaE.js} +1 -1
  42. package/dist/client/assets/{chunk-FMBD7UC4-BhY2wlYr.js → chunk-FMBD7UC4-CelGyE3R.js} +1 -1
  43. package/dist/client/assets/{chunk-ND2GUHAM-BB8mzlmk.js → chunk-ND2GUHAM-DespM-6E.js} +1 -1
  44. package/dist/client/assets/{chunk-QZHKN3VN-D81qQMGf.js → chunk-QZHKN3VN-DLkr_Pz0.js} +1 -1
  45. package/dist/client/assets/classDiagram-4FO5ZUOK-BGVE_u03.js +1 -0
  46. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BGVE_u03.js +1 -0
  47. package/dist/client/assets/{cose-bilkent-S5V4N54A-CCFUHBJX.js → cose-bilkent-S5V4N54A-I7MlbwDq.js} +1 -1
  48. package/dist/client/assets/{dagre-BM42HDAG-xr9MLhRA.js → dagre-BM42HDAG-DRwLRj5m.js} +1 -1
  49. package/dist/client/assets/{dashboard-view-oTFt91I3.js → dashboard-view-CIwO3ODB.js} +1 -1
  50. package/dist/client/assets/{dashboard-view-RXKFv3QV.js → dashboard-view-CSxVzmpI.js} +1 -1
  51. package/dist/client/assets/{dashboard-view-lqwQ-pMo.js → dashboard-view-Gb436HIO.js} +1 -1
  52. package/dist/client/assets/{diagram-2AECGRRQ-mN_tYnBs.js → diagram-2AECGRRQ-BuWGDbh_.js} +1 -1
  53. package/dist/client/assets/{diagram-5GNKFQAL-xznlq-5C.js → diagram-5GNKFQAL-hG6N6ArX.js} +1 -1
  54. package/dist/client/assets/{diagram-KO2AKTUF-ClRlj1ZA.js → diagram-KO2AKTUF-BSXKqQJA.js} +1 -1
  55. package/dist/client/assets/{diagram-LMA3HP47-CNiPIQqT.js → diagram-LMA3HP47-wNefeM2W.js} +1 -1
  56. package/dist/client/assets/{diagram-OG6HWLK6-DwQD-W0P.js → diagram-OG6HWLK6-nA8v_k_H.js} +1 -1
  57. package/dist/client/assets/{erDiagram-TEJ5UH35-ZXkiCm8S.js → erDiagram-TEJ5UH35-Dwnvvp2x.js} +1 -1
  58. package/dist/client/assets/{flowDiagram-I6XJVG4X-DI5haEH9.js → flowDiagram-I6XJVG4X-FS4W3U0O.js} +1 -1
  59. package/dist/client/assets/{folder-open-NenyCkrb.js → folder-open-C3GXrzUo.js} +1 -1
  60. package/dist/client/assets/{ganttDiagram-6RSMTGT7-BRHJpfOZ.js → ganttDiagram-6RSMTGT7-D95bgKFE.js} +1 -1
  61. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-iXT_YBAZ.js → gitGraphDiagram-PVQCEYII-zqFbK6on.js} +1 -1
  62. package/dist/client/assets/{index-CrAJA2v0.js → index-BWRNl9tu.js} +274 -274
  63. package/dist/client/assets/index-DRZtPb3i.css +1 -0
  64. package/dist/client/assets/{infoDiagram-5YYISTIA-BzkuQtqZ.js → infoDiagram-5YYISTIA-BBELQBNE.js} +1 -1
  65. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-5hnPG_98.js → ishikawaDiagram-YF4QCWOH-bSZkIXd-.js} +1 -1
  66. package/dist/client/assets/{journeyDiagram-JHISSGLW-Co3fnxbr.js → journeyDiagram-JHISSGLW-8wIimXRT.js} +1 -1
  67. package/dist/client/assets/{kanban-definition-UN3LZRKU-PZFl_y-h.js → kanban-definition-UN3LZRKU-DMj6kFc5.js} +1 -1
  68. package/dist/client/assets/{mermaid.core-CJRKeU9s.js → mermaid.core-UDKENpfo.js} +4 -4
  69. package/dist/client/assets/{mindmap-definition-RKZ34NQL-B15e2rkx.js → mindmap-definition-RKZ34NQL-CJGH0VYQ.js} +1 -1
  70. package/dist/client/assets/{pieDiagram-4H26LBE5-kGF592D_.js → pieDiagram-4H26LBE5-B3cZLcOe.js} +1 -1
  71. package/dist/client/assets/{puzzle-Dgc4yzjO.js → puzzle-D85LM19g.js} +1 -1
  72. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CUShcj70.js → quadrantDiagram-W4KKPZXB-bR1CBrVi.js} +1 -1
  73. package/dist/client/assets/{requirementDiagram-4Y6WPE33-D2k4nLqQ.js → requirementDiagram-4Y6WPE33-DnyJ74cE.js} +1 -1
  74. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CA03BxhT.js → sankeyDiagram-5OEKKPKP-ttv4kq0z.js} +1 -1
  75. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DDuFN2Pi.js → sequenceDiagram-3UESZ5HK-FiUBFoWw.js} +1 -1
  76. package/dist/client/assets/{shield-alert-BlYWqolM.js → shield-alert-D3IVOJV1.js} +1 -1
  77. package/dist/client/assets/{standing-instructions-template-ujl7UARV.js → standing-instructions-template-CWY_pFh6.js} +1 -1
  78. package/dist/client/assets/{stateDiagram-AJRCARHV-yJOkHK4Q.js → stateDiagram-AJRCARHV-BcsYRB77.js} +1 -1
  79. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-BgErHdAN.js → stateDiagram-v2-BHNVJYJU-DsBPFwnG.js} +1 -1
  80. package/dist/client/assets/{timeline-definition-PNZ67QCA-BUiDQw21.js → timeline-definition-PNZ67QCA-B8rOOxEj.js} +1 -1
  81. package/dist/client/assets/{upload-DmlZvpMp.js → upload-WqSaqC__.js} +1 -1
  82. package/dist/client/assets/{users-Cx6ACt-W.js → users-CPP-3wxQ.js} +1 -1
  83. package/dist/client/assets/{vennDiagram-CIIHVFJN-DqyIjBY8.js → vennDiagram-CIIHVFJN-CO0LSFj2.js} +1 -1
  84. package/dist/client/assets/{wardley-L42UT6IY-C93tXAKH.js → wardley-L42UT6IY-QHbclXGi.js} +1 -1
  85. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DmH6-GJV.js → wardleyDiagram-YWT4CUSO-DAsnTuM9.js} +1 -1
  86. package/dist/client/assets/{xychartDiagram-2RQKCTM6-BPwjE4Su.js → xychartDiagram-2RQKCTM6-BmG5EEoO.js} +1 -1
  87. package/dist/client/index.html +2 -2
  88. package/dist/client/version.json +1 -1
  89. package/dist/extension.js +216 -350
  90. package/package.json +1 -1
  91. package/dist/client/assets/SettingsModal-KnUd0942.css +0 -1
  92. package/dist/client/assets/channel-BdTAZBkX.js +0 -1
  93. package/dist/client/assets/classDiagram-4FO5ZUOK-CuoGzmwo.js +0 -1
  94. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CuoGzmwo.js +0 -1
  95. package/dist/client/assets/index-CITV-wDq.css +0 -1
package/dist/extension.js CHANGED
@@ -135461,7 +135461,8 @@ function wrapPluginRuntime(instance, runtimeId, runtimeName) {
135461
135461
  const result2 = await adapter.createSession(options);
135462
135462
  return {
135463
135463
  session: result2.session ?? result2,
135464
- sessionFile: result2.sessionFile
135464
+ sessionFile: result2.sessionFile,
135465
+ settleFallbackDispatch: result2.settleFallbackDispatch
135465
135466
  };
135466
135467
  }
135467
135468
  throw new Error(`Plugin runtime "${runtimeId}" does not implement createSession`);
@@ -135570,11 +135571,15 @@ var init_runtime_resolution = __esm({
135570
135571
  name = "Default PI Runtime";
135571
135572
  async createSession(options) {
135572
135573
  const { toolOutputMaxChars, mcpServers, ...agentOptions } = options;
135573
- return createFnAgent2({
135574
+ const result2 = await createFnAgent2({
135574
135575
  ...agentOptions,
135575
135576
  toolOutputMaxChars,
135576
135577
  mcpServers: normalizeAgentRuntimeMcpServers(mcpServers)
135577
135578
  });
135579
+ return {
135580
+ ...result2,
135581
+ settleFallbackDispatch: result2.settleFallbackDispatch ?? (async () => void 0)
135582
+ };
135578
135583
  }
135579
135584
  async promptWithFallback(session, prompt, options) {
135580
135585
  return promptWithFallback(session, prompt, options);
@@ -136456,6 +136461,7 @@ async function createResolvedAgentSession(options) {
136456
136461
  return {
136457
136462
  session: result2.session,
136458
136463
  sessionFile: result2.sessionFile,
136464
+ settleFallbackDispatch: result2.settleFallbackDispatch,
136459
136465
  runtimeId: resolved.runtimeId,
136460
136466
  wasConfigured: resolved.wasConfigured
136461
136467
  };
@@ -158451,7 +158457,11 @@ async function createFnAgent2(options) {
158451
158457
  }
158452
158458
  });
158453
158459
  attachSessionIdentity(promptableSession);
158454
- return { session: promptableSession, sessionFile: promptableSession.sessionFile };
158460
+ return {
158461
+ session: promptableSession,
158462
+ sessionFile: promptableSession.sessionFile,
158463
+ settleFallbackDispatch: async () => void 0
158464
+ };
158455
158465
  }
158456
158466
  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
158467
  var init_pi = __esm({
@@ -184940,6 +184950,25 @@ var init_scheduler = __esm({
184940
184950
  });
184941
184951
  }
184942
184952
  }
184953
+ if (from === parked.wip && to2 === parked.hold) {
184954
+ if (source === "engine") {
184955
+ this.recentEngineTodoRequeues.set(task.id, task.columnMovedAt ?? (/* @__PURE__ */ new Date()).toISOString());
184956
+ } else {
184957
+ this.recentEngineTodoRequeues.delete(task.id);
184958
+ }
184959
+ } else if (to2 === parked.review || parked.terminal.has(to2)) {
184960
+ this.recentEngineTodoRequeues.delete(task.id);
184961
+ if (task.dispatchStormCount != null || task.lastDispatchAt != null || task.executeRequeueLoopCount != null || task.executeRequeueLoopSignature != null) {
184962
+ void this.store.updateTask(task.id, {
184963
+ dispatchStormCount: null,
184964
+ lastDispatchAt: null,
184965
+ executeRequeueLoopCount: null,
184966
+ executeRequeueLoopSignature: null
184967
+ }).catch((error3) => {
184968
+ schedulerLog.warn(`Failed to reset dispatch oscillation state for ${task.id} on move to ${to2}: ${error3 instanceof Error ? error3.message : String(error3)}`);
184969
+ });
184970
+ }
184971
+ }
184943
184972
  const resolvedParked = mergeParkedColumns(await resolveTaskParkedColumns(this.store, task.id), lanes);
184944
184973
  if (resolvedParked.terminal.has(to2)) {
184945
184974
  try {
@@ -185000,25 +185029,6 @@ var init_scheduler = __esm({
185000
185029
  schedulerLog.error(`Failed event-driven blocker reconciliation for ${task.id}`, error3);
185001
185030
  }
185002
185031
  }
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
185032
  if (resolvedParked.terminal.has(to2) || to2 === resolvedParked.hold) {
185023
185033
  schedulerLog.log(`Task moved to ${to2} \u2014 triggering scheduling`);
185024
185034
  this.schedule();
@@ -203276,7 +203286,6 @@ var init_self_healing = __esm({
203276
203286
  { name: "reconcile-done-task-integrity", fn: () => this.reconcileDoneTaskIntegrity() },
203277
203287
  { name: "reconcile-stale-merger-status", fn: () => this.reconcileStaleMergerStatus() },
203278
203288
  { name: "reconcile-stale-duplicate-decision", fn: () => this.reconcileStaleDuplicateDecisionPause() },
203279
- { name: "reconcile-external-pr-blockers", fn: () => this.reconcileExternalPrBlockers() },
203280
203289
  // FNXC:OrphanedPendingSteps 2026-07-22-16:35 (FN-8492 review follow-up): also
203281
203290
  // steady-state — a step session can die without an engine restart, and startup-only
203282
203291
  // cadence left that case riding the 3×30-min stall escalator to a deadlock park.
@@ -212173,71 +212182,12 @@ var init_self_healing = __esm({
212173
212182
  }
212174
212183
  }
212175
212184
  /*
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.
212185
+ FNXC:HonestBlockedExit 2026-08-02-23:59 (operator decision — FN-8728 vs PR #2398):
212186
+ The FN-8700 `reconcile-external-pr-blockers` sweep (gh-backed clearing of PR-claim parks)
212187
+ is REMOVED with the whole PR/file-claim blocking mechanism. Open PRs are never blockers;
212188
+ file-scope conflicts are arbitrated only by Fusion's own board. Legacy PR-claim parks are
212189
+ no longer honored by isDurableBlockedTask, so normal recovery paths reclaim them.
212180
212190
  */
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
212191
  async resolveExplicitDuplicateMarkerTasks() {
212242
212192
  try {
212243
212193
  const settings = await this.store.getSettings();
@@ -212991,6 +212941,7 @@ var init_self_healing = __esm({
212991
212941
  // ../engine/src/triage.ts
212992
212942
  import { Type as Type5 } from "@earendil-works/pi-ai";
212993
212943
  import { exec as exec34 } from "node:child_process";
212944
+ import { existsSync as existsSync60 } from "node:fs";
212994
212945
  import { readFile as readFile38, writeFile as writeFile27, rm as rm8 } from "node:fs/promises";
212995
212946
  import { join as join72 } from "node:path";
212996
212947
  import { promisify as promisify48 } from "node:util";
@@ -213497,6 +213448,8 @@ var init_triage = __esm({
213497
213448
  unregisterAdmissionProvider = null;
213498
213449
  /** Timestamps when tasks entered the `processing` set, for staleness detection. */
213499
213450
  processingSince = /* @__PURE__ */ new Map();
213451
+ /** Monotonic provenance token for attempt-local planner fallback observation. */
213452
+ planningAttemptSequence = 0;
213500
213453
  wasGlobalPaused = false;
213501
213454
  wasEnginePaused = false;
213502
213455
  idleSemaphoreLeakCandidateSince = null;
@@ -214792,7 +214745,28 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
214792
214745
  }
214793
214746
  await this.store.logEntry(task.id, `Planning session running in task worktree ${planningCwd}`).catch(() => void 0);
214794
214747
  }
214795
- const { session } = await createResolvedAgentSession({
214748
+ const authoritativePromptPath = join72(this.rootDir, promptPath);
214749
+ const planningAttemptBaseline = existsSync60(authoritativePromptPath) ? await readFile38(authoritativePromptPath, "utf-8").catch(() => void 0) : void 0;
214750
+ const planningAttempt = {
214751
+ id: `${task.id}:${++this.planningAttemptSequence}`,
214752
+ baseline: planningAttemptBaseline,
214753
+ fallbackEngaged: false,
214754
+ fallbackSettlements: []
214755
+ };
214756
+ const fallbackObserver = createFallbackModelObserver({
214757
+ agent: "triage",
214758
+ label: "triage",
214759
+ store: this.store,
214760
+ taskId: task.id,
214761
+ taskTitle: task.title
214762
+ });
214763
+ const onFallbackModelUsed = (payload) => {
214764
+ planningAttempt.fallbackEngaged = true;
214765
+ const settlement = Promise.resolve(fallbackObserver(payload));
214766
+ planningAttempt.fallbackSettlements.push(settlement);
214767
+ return settlement;
214768
+ };
214769
+ const { session, settleFallbackDispatch, runtimeId } = await createResolvedAgentSession({
214796
214770
  sessionPurpose: "triage",
214797
214771
  runtimeHint: triageRuntimeHint,
214798
214772
  pluginRunner: this.options.pluginRunner,
@@ -214831,13 +214805,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
214831
214805
  taskTitle: task.title,
214832
214806
  actionGateContext: this.buildActionGateContext(task.id, triageRunContext.runId, assignedAgent, settings.defaultAgentPermissionPolicy),
214833
214807
  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
- })
214808
+ onFallbackModelUsed
214841
214809
  });
214842
214810
  const modelDesc = formatModelMarkerDetails(describeModel(session), resolvePlanningThinkingLevel(settings, task.planningThinkingLevel ?? task.thinkingLevel));
214843
214811
  planLog.debug(`${task.id}: using model ${modelDesc}`);
@@ -214907,6 +214875,10 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
214907
214875
  agentPrompt,
214908
214876
  imageContents.length > 0 ? { images: imageContents } : void 0
214909
214877
  );
214878
+ const fallbackDispatchBoundaryMissing = typeof settleFallbackDispatch !== "function";
214879
+ if (!fallbackDispatchBoundaryMissing) {
214880
+ await settleFallbackDispatch();
214881
+ }
214910
214882
  checkSessionError(session);
214911
214883
  if (this.pauseAborted.has(task.id)) {
214912
214884
  this.pauseAborted.delete(task.id);
@@ -214999,12 +214971,42 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
214999
214971
  }
215000
214972
  }
215001
214973
  }
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);
214974
+ let settledFallbackCount = 0;
214975
+ while (settledFallbackCount < planningAttempt.fallbackSettlements.length) {
214976
+ const pendingSettlements = planningAttempt.fallbackSettlements.slice(settledFallbackCount);
214977
+ settledFallbackCount = planningAttempt.fallbackSettlements.length;
214978
+ await Promise.all(pendingSettlements);
214979
+ }
214980
+ const artifactChangedByAttempt = planningAttempt.baseline !== written;
214981
+ if (fallbackDispatchBoundaryMissing || planningAttempt.fallbackEngaged || !artifactChangedByAttempt) {
214982
+ 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}`;
214983
+ const decision = computeRecoveryDecision({
214984
+ recoveryRetryCount: task.recoveryRetryCount,
214985
+ nextRecoveryAt: task.nextRecoveryAt
214986
+ });
214987
+ if (decision.shouldRetry) {
214988
+ const retryMessage = `${failure} \u2014 retry ${decision.nextState.recoveryRetryCount}/${MAX_RECOVERY_RETRIES} in ${formatDelay(decision.delayMs)}.`;
214989
+ planLog.warn(`${task.id} ${retryMessage}`);
214990
+ await this.store.logEntry(task.id, retryMessage);
214991
+ await this.updatePlanningStateIfStillCurrent(task, {
214992
+ status: this.restoreStatusAfterInterruptedTriageWork(task),
214993
+ error: null,
214994
+ recoveryRetryCount: decision.nextState.recoveryRetryCount,
214995
+ nextRecoveryAt: decision.nextState.nextRecoveryAt
214996
+ });
214997
+ return;
214998
+ }
214999
+ const failureMessage = `${failure} after ${MAX_RECOVERY_RETRIES} retries. Retry after adjusting the task prompt or model.`;
215000
+ planLog.error(`${task.id} clean planning attempt retry budget exhausted`);
215001
+ await this.store.logEntry(task.id, failureMessage);
215002
+ if (await this.updatePlanningStateIfStillCurrent(task, {
215003
+ status: "failed",
215004
+ error: failureMessage,
215005
+ recoveryRetryCount: null,
215006
+ nextRecoveryAt: null
215007
+ })) {
215008
+ await this.backfillBlankTitleAfterTerminalTriageFailure(task);
215009
+ }
215008
215010
  return;
215009
215011
  }
215010
215012
  const deterministicSpecFailure = await this.validateGeneratedPrompt(task.id, written);
@@ -215046,6 +215048,21 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
215046
215048
  }
215047
215049
  return;
215048
215050
  }
215051
+ const duplicateReport = { outcome: "parked" };
215052
+ if (await this.tryFinalizeExplicitDuplicateMarker(task, written, settings, {
215053
+ isReplan,
215054
+ feedback
215055
+ }, duplicateReport)) {
215056
+ this.options.onSpecifyComplete?.(task, duplicateReport);
215057
+ return;
215058
+ }
215059
+ if (task.recoveryRetryCount != null || task.nextRecoveryAt != null) {
215060
+ await this.updatePlanningStateIfStillCurrent(task, {
215061
+ error: null,
215062
+ recoveryRetryCount: null,
215063
+ nextRecoveryAt: null
215064
+ });
215065
+ }
215049
215066
  const finalizeReport = await this.finalizeApprovedTask(task, written, settings, {
215050
215067
  isReplan,
215051
215068
  feedback
@@ -216082,103 +216099,35 @@ Remove or replace these ids and call fn_task_create again.`
216082
216099
  // ../engine/src/execution-block-classifier.ts
216083
216100
  function partitionBlockedByRefs(blockedBy) {
216084
216101
  const taskIds = [];
216085
- const prNumbers = [];
216086
216102
  for (const raw of blockedBy) {
216087
216103
  const id = raw.trim();
216088
216104
  if (!id) continue;
216089
- const prMatch = id.match(PR_REF_RE);
216090
- if (prMatch) {
216091
- prNumbers.push(Number(prMatch[1]));
216092
- continue;
216093
- }
216105
+ if (LEGACY_PR_REF_RE.test(id)) continue;
216094
216106
  if (TASK_ID_RE.test(id)) {
216095
216107
  const m2 = id.toUpperCase().match(/^([A-Z][A-Z0-9]*)-(\d+)$/);
216096
216108
  taskIds.push(m2 ? `${m2[1]}-${m2[2]}` : id.toUpperCase());
216097
- continue;
216098
216109
  }
216099
216110
  }
216100
- return {
216101
- taskIds: [...new Set(taskIds)],
216102
- prNumbers: [...new Set(prNumbers.filter((n) => Number.isFinite(n) && n > 0))]
216103
- };
216111
+ return { taskIds: [...new Set(taskIds)] };
216104
216112
  }
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) {
216113
+ function classifyBlockedExit(_reason, blockedBy = []) {
216114
+ const { taskIds } = partitionBlockedByRefs(blockedBy);
216115
+ if (taskIds.length > 0) {
216155
216116
  return {
216156
216117
  allowAutoReplan: false,
216157
216118
  class: "external",
216158
- externalBlockers: [],
216159
- thrashSignature: `tasks:${partitioned.taskIds.slice().sort().join(",")}`,
216160
- prNumbers
216161
- };
216162
- }
216163
- if (prNumbers.length > 0) {
216164
- return {
216165
- allowAutoReplan: false,
216166
- class: "external",
216167
- externalBlockers: prNumbers.map((number4) => ({ kind: "github-pr", number: number4 })),
216168
- thrashSignature: prNumbers.map((n) => `pr:${n}`).sort().join("|"),
216169
- prNumbers
216119
+ thrashSignature: `tasks:${taskIds.slice().sort().join(",")}`
216170
216120
  };
216171
216121
  }
216172
216122
  return {
216173
216123
  allowAutoReplan: true,
216174
216124
  class: "plan-defect",
216175
- externalBlockers: [],
216176
- thrashSignature: "plan-defect",
216177
- prNumbers: []
216125
+ thrashSignature: "plan-defect"
216178
216126
  };
216179
216127
  }
216180
216128
  function countBlockedThrashHits(log41, signature, nowMs = Date.now(), windowMs = BLOCKED_THRASH_WINDOW_MS) {
216181
216129
  if (!log41?.length) return 0;
216130
+ if (signature === "plan-defect") return 0;
216182
216131
  const cutoff = nowMs - windowMs;
216183
216132
  let count2 = 0;
216184
216133
  for (const entry of log41) {
@@ -216190,18 +216139,7 @@ function countBlockedThrashHits(log41, signature, nowMs = Date.now(), windowMs =
216190
216139
  continue;
216191
216140
  }
216192
216141
  }
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
216142
  for (const part of signature.split("|")) {
216201
- if (part.startsWith("pr:") && action.includes(`PR #${part.slice(3)}`)) {
216202
- count2 += 1;
216203
- break;
216204
- }
216205
216143
  if (part.startsWith("tasks:") && part.slice(6).split(",").some((id) => id && action.includes(id))) {
216206
216144
  count2 += 1;
216207
216145
  break;
@@ -216210,38 +216148,26 @@ function countBlockedThrashHits(log41, signature, nowMs = Date.now(), windowMs =
216210
216148
  }
216211
216149
  return count2;
216212
216150
  }
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
216151
  function isDurableBlockedTask(task) {
216220
- if (task.status === "failed" && isDurableBlockedError(task.error)) return true;
216221
216152
  const meta2 = task.sourceMetadata;
216222
216153
  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";
216154
+ if (meta2.blockedClass !== "external") return false;
216155
+ return task.status === "failed" || task.status === "needs-replan";
216228
216156
  }
216229
216157
  function buildExternalBlockMetadataPatch(classification, thrashCount) {
216230
216158
  return {
216231
216159
  blockedClass: classification.class,
216232
216160
  blockedThrashSignature: classification.thrashSignature,
216233
216161
  blockedThrashCount: thrashCount,
216234
- externalBlockers: classification.externalBlockers
216162
+ externalBlockers: []
216235
216163
  };
216236
216164
  }
216237
- var TASK_ID_RE, PR_REF_RE, PR_IN_TEXT_RE, CLAIM_REASON_RE, BLOCKED_THRASH_LIMIT, BLOCKED_THRASH_WINDOW_MS;
216165
+ var TASK_ID_RE, LEGACY_PR_REF_RE, BLOCKED_THRASH_LIMIT, BLOCKED_THRASH_WINDOW_MS;
216238
216166
  var init_execution_block_classifier = __esm({
216239
216167
  "../engine/src/execution-block-classifier.ts"() {
216240
216168
  "use strict";
216241
216169
  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;
216170
+ LEGACY_PR_REF_RE = /^(?:pr[:#-]|#|PR-)(\d+)$/i;
216245
216171
  BLOCKED_THRASH_LIMIT = 3;
216246
216172
  BLOCKED_THRASH_WINDOW_MS = 60 * 60 * 1e3;
216247
216173
  }
@@ -219059,7 +218985,7 @@ var init_workflow_completion_summary = __esm({
219059
218985
 
219060
218986
  // ../engine/src/review-checkout.ts
219061
218987
  import { execFileSync as execFileSync3 } from "node:child_process";
219062
- import { existsSync as existsSync60, realpathSync as realpathSync9, statSync as statSync11 } from "node:fs";
218988
+ import { existsSync as existsSync61, realpathSync as realpathSync9, statSync as statSync11 } from "node:fs";
219063
218989
  import { isAbsolute as isAbsolute15 } from "node:path";
219064
218990
  function readMetadataPath(value) {
219065
218991
  if (!value || typeof value !== "object") return void 0;
@@ -219083,7 +219009,7 @@ function resolveReviewCheckoutCwd(task, fallbackCwd) {
219083
219009
  const candidate = getTaskReviewCheckoutPath(task);
219084
219010
  if (!candidate || !isAbsolute15(candidate)) return fallbackCwd;
219085
219011
  try {
219086
- if (!existsSync60(candidate) || !statSync11(candidate).isDirectory()) return fallbackCwd;
219012
+ if (!existsSync61(candidate) || !statSync11(candidate).isDirectory()) return fallbackCwd;
219087
219013
  const realCandidate = realpathSync9(candidate);
219088
219014
  const topLevel = execFileSync3("git", ["rev-parse", "--show-toplevel"], {
219089
219015
  cwd: realCandidate,
@@ -221689,7 +221615,7 @@ var init_external_integration_evidence = __esm({
221689
221615
 
221690
221616
  // ../engine/src/step-runner.ts
221691
221617
  import { exec as exec35 } from "node:child_process";
221692
- import { existsSync as existsSync62, statSync as statSync12 } from "node:fs";
221618
+ import { existsSync as existsSync63, statSync as statSync12 } from "node:fs";
221693
221619
  import { promisify as promisify50 } from "node:util";
221694
221620
  async function runTaskStep(deps, task, stepIndex, opts = {}) {
221695
221621
  const { store, worktreePath } = deps;
@@ -221843,7 +221769,7 @@ function makeAncestryBlastRadiusGuard(opts) {
221843
221769
  function isUsableWorktreeDirectory(candidate) {
221844
221770
  if (!candidate) return false;
221845
221771
  try {
221846
- return existsSync62(candidate) && statSync12(candidate).isDirectory();
221772
+ return existsSync63(candidate) && statSync12(candidate).isDirectory();
221847
221773
  } catch {
221848
221774
  return false;
221849
221775
  }
@@ -221880,7 +221806,7 @@ var init_step_runner = __esm({
221880
221806
 
221881
221807
  // ../engine/src/auto-recovery-handlers/branch-worktree.ts
221882
221808
  import { exec as exec36 } from "node:child_process";
221883
- import { existsSync as existsSync63 } from "node:fs";
221809
+ import { existsSync as existsSync64 } from "node:fs";
221884
221810
  import { promisify as promisify51 } from "node:util";
221885
221811
  var execAsync33, baseLog2, GIT_TIMEOUT_MS8, GIT_MAX_BUFFER6, BranchWorktreeAutoRecoveryHandler;
221886
221812
  var init_branch_worktree = __esm({
@@ -222071,7 +221997,7 @@ var init_branch_worktree = __esm({
222071
221997
  if (inspection.kind === "stale-resolved" || inspection.kind === "fully-subsumed" || inspection.kind === "tip-already-merged") {
222072
221998
  await this.requeueAfterRecovery(ctx.task, failure, inspection.kind, {
222073
221999
  branchExists: await this.hasBranchRef(repoDir, branchName),
222074
- worktreePresent: existsSync63(conflictingWorktreePath),
222000
+ worktreePresent: existsSync64(conflictingWorktreePath),
222075
222001
  tipSha: "tipSha" in inspection ? inspection.tipSha : void 0,
222076
222002
  inspectionKind: inspection.kind
222077
222003
  });
@@ -222141,7 +222067,7 @@ var init_branch_worktree = __esm({
222141
222067
  let branchDeleted = false;
222142
222068
  let worktreeRemoved = false;
222143
222069
  if (!isLiveOwned) {
222144
- if (existsSync63(conflictingWorktreePath)) {
222070
+ if (existsSync64(conflictingWorktreePath)) {
222145
222071
  try {
222146
222072
  await execAsync33(`git worktree remove --force ${this.quote(conflictingWorktreePath)}`, {
222147
222073
  cwd: repoDir,
@@ -222189,7 +222115,7 @@ var init_branch_worktree = __esm({
222189
222115
  });
222190
222116
  await this.requeueAfterRecovery(ctx.task, failure, "live-foreign-discard-and-recreate", {
222191
222117
  branchExists: !branchDeleted,
222192
- worktreePresent: !worktreeRemoved && existsSync63(conflictingWorktreePath),
222118
+ worktreePresent: !worktreeRemoved && existsSync64(conflictingWorktreePath),
222193
222119
  tipSha: tipSha2,
222194
222120
  inspectionKind: inspection.kind
222195
222121
  });
@@ -222202,7 +222128,7 @@ var init_branch_worktree = __esm({
222202
222128
  conflictingWorktreePath,
222203
222129
  inspectionKind: inspection.kind,
222204
222130
  branchExists,
222205
- worktreePresent: existsSync63(conflictingWorktreePath),
222131
+ worktreePresent: existsSync64(conflictingWorktreePath),
222206
222132
  tipSha
222207
222133
  });
222208
222134
  }
@@ -222363,7 +222289,7 @@ var init_file_scope2 = __esm({
222363
222289
  });
222364
222290
 
222365
222291
  // ../engine/src/run-verification-tool.ts
222366
- import { existsSync as existsSync64, readdirSync as readdirSync11, readFileSync as readFileSync23 } from "node:fs";
222292
+ import { existsSync as existsSync65, readdirSync as readdirSync11, readFileSync as readFileSync23 } from "node:fs";
222367
222293
  import { isAbsolute as isAbsolute16, join as join77, relative as relative14 } from "node:path";
222368
222294
  import { Type as Type6 } from "@earendil-works/pi-ai";
222369
222295
  function shellSplit(input) {
@@ -222923,7 +222849,7 @@ function createRunVerificationTool(opts) {
222923
222849
  }
222924
222850
  if (effectiveCommand.trimStart().startsWith("pnpm --filter")) {
222925
222851
  const modulesYaml = join77(rootDir, "node_modules", ".modules.yaml");
222926
- if (!existsSync64(modulesYaml)) {
222852
+ if (!existsSync65(modulesYaml)) {
222927
222853
  const installCmd = "pnpm install --prefer-offline";
222928
222854
  const msg = `node_modules/.modules.yaml not found in workspace root \u2014 auto-prepending \`${installCmd}\` before running the command.`;
222929
222855
  warnings.push(msg);
@@ -223284,7 +223210,7 @@ import { exec as exec39, execFile as execFile26, execSync as execSync7 } from "n
223284
223210
  import { promisify as promisify54 } from "node:util";
223285
223211
  import { setImmediate as setImmediateCb3 } from "node:timers";
223286
223212
  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";
223213
+ import { existsSync as existsSync66, lstatSync as lstatSync3, realpathSync as realpathSync10 } from "node:fs";
223288
223214
  import { readFile as readFile40, rm as rm12, writeFile as writeFile29 } from "node:fs/promises";
223289
223215
  import { Type as Type7 } from "@earendil-works/pi-ai";
223290
223216
  import { SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
@@ -223302,7 +223228,7 @@ function isWorkflowStepSkillDiscoverable(skillName, additionalSkillPaths, ceSkil
223302
223228
  return false;
223303
223229
  }
223304
223230
  return (additionalSkillPaths ?? []).some(
223305
- (skillPath) => basename19(skillPath) === bareSkillName && existsSync65(join78(skillPath, "SKILL.md")) || existsSync65(join78(skillPath, bareSkillName, "SKILL.md"))
223231
+ (skillPath) => basename19(skillPath) === bareSkillName && existsSync66(join78(skillPath, "SKILL.md")) || existsSync66(join78(skillPath, bareSkillName, "SKILL.md"))
223306
223232
  );
223307
223233
  }
223308
223234
  function getPromptSection(prompt, heading) {
@@ -224275,7 +224201,7 @@ If you have just finished a step's work, immediately call \`fn_task_update\` to
224275
224201
 
224276
224202
  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
224203
 
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.
224204
+ **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
224205
  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
224206
 
224281
224207
  ## How to work
@@ -226864,7 +226790,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
226864
226790
  );
226865
226791
  return false;
226866
226792
  }
226867
- if (task.worktree && existsSync65(task.worktree)) {
226793
+ if (task.worktree && existsSync66(task.worktree)) {
226868
226794
  const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha, task.id, void 0, "recovery");
226869
226795
  if (modifiedFiles.length > 0) {
226870
226796
  await this.store.updateTask(task.id, { modifiedFiles });
@@ -229826,7 +229752,7 @@ ${info.feedback}`, revisionKey),
229826
229752
  if (live.firstExecutionAt || live.executionStartedAt) return false;
229827
229753
  if (activeSessionRegistry.isPathActive(live.worktree) || activeSessionRegistry.isPathActive(resolvePath3(live.worktree))) return false;
229828
229754
  if (this.hasLiveTaskSessionSurface(taskId) || executingTaskLock.has(taskId)) return false;
229829
- if (existsSync65(live.worktree)) {
229755
+ if (existsSync66(live.worktree)) {
229830
229756
  if (await this.preExecutionWorktreeHasWork(live.worktree)) {
229831
229757
  executorLog.log(`${taskId}: keeping pre-execution worktree ${live.worktree} \u2014 it carries commits or uncommitted changes`);
229832
229758
  return false;
@@ -229868,7 +229794,7 @@ ${info.feedback}`, revisionKey),
229868
229794
  }
229869
229795
  if (this.workspaceConfig && (this.workspaceConfig.repos.length ?? 0) > 0) return null;
229870
229796
  const live = await this.store.getTask(taskId);
229871
- if (live.worktree && existsSync65(live.worktree)) return live.worktree;
229797
+ if (live.worktree && existsSync66(live.worktree)) return live.worktree;
229872
229798
  const settings = await this.store.getSettings();
229873
229799
  const acquisitionTask = live.worktree ? { ...live, worktree: void 0, sessionFile: void 0 } : live;
229874
229800
  const acquired = await this.ensureGraphCustomNodeWorktree(acquisitionTask, settings, "planning");
@@ -229882,8 +229808,8 @@ ${info.feedback}`, revisionKey),
229882
229808
  if (!requirement.requiresWorktree) return;
229883
229809
  const live = await this.store.getTask(nodeTask.id);
229884
229810
  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;
229811
+ if (live.worktree && existsSync66(live.worktree) && !executionCodeNode) return;
229812
+ const taskForAcquisition = live.worktree && !existsSync66(live.worktree) ? { ...live, worktree: void 0, sessionFile: void 0 } : live;
229887
229813
  if (live.worktree) {
229888
229814
  await this.store.logEntry(
229889
229815
  live.id,
@@ -230009,7 +229935,7 @@ ${info.feedback}`, revisionKey),
230009
229935
  const nodeDisplayName = typeof cfg.name === "string" && cfg.name.trim() ? cfg.name.trim() : node.id;
230010
229936
  const isPlanReviewNode = node.id === "plan-review-step" || nodeDisplayName === "Plan Review" || optionalGroupId === "plan-review";
230011
229937
  if (!this.workspaceConfig) {
230012
- const recordedWorktreeMissing = Boolean(executionTarget.worktree) && !existsSync65(executionTarget.worktree);
229938
+ const recordedWorktreeMissing = Boolean(executionTarget.worktree) && !existsSync66(executionTarget.worktree);
230013
229939
  const shouldAcquire = !executionTarget.worktree || recordedWorktreeMissing && isPlanReviewNode;
230014
229940
  if (shouldAcquire) {
230015
229941
  if (recordedWorktreeMissing) {
@@ -231683,75 +231609,16 @@ Graph value: ${failureValue}` : ""}`, budget.key),
231683
231609
  );
231684
231610
  }
231685
231611
  }
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
231612
  async routeGraphFailureToExecutionResume(live, failedNode, failureValue, resumeLanesMemo) {
231743
231613
  if (live.deletedAt) return false;
231744
231614
  if (live.paused || live.userPaused === true) return false;
231745
231615
  if ((await resolveTerminalColumnsFor(this.store, live.id)).includes(live.column)) return false;
231746
- if (isDurableBlockedTask(live) || isDurableBlockedError(live.error)) {
231616
+ if (isDurableBlockedTask(live)) {
231747
231617
  executorLog.log(
231748
- `${live.id}: graph failure resume skipped \u2014 durable BLOCKED park honored (class from error/metadata)`
231618
+ `${live.id}: graph failure resume skipped \u2014 durable BLOCKED park honored (task-dependency block)`
231749
231619
  );
231750
231620
  return false;
231751
231621
  }
231752
- if (await this.parkDurableBlockedFromRecentLog(live)) {
231753
- return false;
231754
- }
231755
231622
  if (failedNode === COMPLETION_SUMMARY_NODE_ID) return false;
231756
231623
  const incompleteSteps = hasNonTerminalWorkflowSteps(live);
231757
231624
  const implementationIncompleteMergeFailure = this.isMergeGraphFailure(failedNode) && failureValue === "implementation-incomplete";
@@ -232932,7 +232799,7 @@ ${summary}`,
232932
232799
  executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay6}: ${errorMessage2}`);
232933
232800
  await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay6}): ${errorMessage2}`, void 0, this.getRunContextFor(task.id));
232934
232801
  }
232935
- if (worktreePath && existsSync65(worktreePath)) {
232802
+ if (worktreePath && existsSync66(worktreePath)) {
232936
232803
  try {
232937
232804
  const settings2 = await this.store.getSettings();
232938
232805
  await removeWorktree({
@@ -233013,7 +232880,7 @@ ${summary}`,
233013
232880
  const settings2 = await this.store.getSettings();
233014
232881
  const preserveProgress = settings2.preserveProgressOnStuckRequeue !== false;
233015
232882
  await this.resetStepsIfWorkLost(latestTask);
233016
- if (worktreePath && existsSync65(worktreePath)) {
232883
+ if (worktreePath && existsSync66(worktreePath)) {
233017
232884
  try {
233018
232885
  await removeWorktree({
233019
232886
  worktreePath,
@@ -233296,7 +233163,7 @@ ${summary}`,
233296
233163
  nodeId: detail.effectiveNodeId ?? detail.nodeId ?? null,
233297
233164
  agentId: engineRunContext.agentId ?? null
233298
233165
  });
233299
- let isResuming = !!task.sessionFile && existsSync65(task.sessionFile);
233166
+ let isResuming = !!task.sessionFile && existsSync66(task.sessionFile);
233300
233167
  if (isResuming) {
233301
233168
  const persistedWorktreePath = await extractPersistedSessionWorktreePath(task.sessionFile, this.rootDir, settings);
233302
233169
  if (!isSessionWorktreeCompatible(persistedWorktreePath, worktreePath)) {
@@ -234077,7 +233944,7 @@ ${summary}`,
234077
233944
  return;
234078
233945
  } else {
234079
233946
  executorLog.log(`${task.id} paused \u2014 moving to todo`);
234080
- if (worktreePath && existsSync65(worktreePath)) {
233947
+ if (worktreePath && existsSync66(worktreePath)) {
234081
233948
  try {
234082
233949
  const settings2 = await this.store.getSettings();
234083
233950
  await removeWorktree({
@@ -234474,7 +234341,7 @@ ${summary}`,
234474
234341
  executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay6}: ${errorMessage2}`);
234475
234342
  await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay6}): ${errorMessage2}`, void 0, this.getRunContextFor(task.id));
234476
234343
  }
234477
- if (worktreePath && existsSync65(worktreePath)) {
234344
+ if (worktreePath && existsSync66(worktreePath)) {
234478
234345
  try {
234479
234346
  const settings2 = await this.store.getSettings();
234480
234347
  await removeWorktree({
@@ -234610,7 +234477,7 @@ ${summary}`,
234610
234477
  const settings2 = await this.store.getSettings();
234611
234478
  const preserveProgress = settings2.preserveProgressOnStuckRequeue !== false;
234612
234479
  await this.resetStepsIfWorkLost(latestTask);
234613
- if (worktreePath && existsSync65(worktreePath)) {
234480
+ if (worktreePath && existsSync66(worktreePath)) {
234614
234481
  try {
234615
234482
  await removeWorktree({
234616
234483
  worktreePath,
@@ -235037,7 +234904,7 @@ ${summary}`,
235037
234904
  for (const repoRel of Object.keys(workspaceWorktrees).sort()) {
235038
234905
  const repo = workspaceWorktrees[repoRel];
235039
234906
  const expectedBranch = repo.branch || canonicalFusionBranchName(task.id);
235040
- if (!existsSync65(repo.worktreePath)) {
234907
+ if (!existsSync66(repo.worktreePath)) {
235041
234908
  executorLog.log(`${task.id}: workspace worktree for ${repoRel} not found at ${repo.worktreePath} \u2014 skipping git validation`);
235042
234909
  continue;
235043
234910
  }
@@ -235169,7 +235036,7 @@ ${summary}`,
235169
235036
  expected: `resolvable task worktree under ${resolveWorktreesDir(this.rootDir, settings)}/*`
235170
235037
  };
235171
235038
  }
235172
- if (!existsSync65(worktreePath)) {
235039
+ if (!existsSync66(worktreePath)) {
235173
235040
  executorLog.log(
235174
235041
  `${task.id}: worktree directory not found at ${worktreePath} \u2014 skipping git validation for task completion`
235175
235042
  );
@@ -235498,10 +235365,10 @@ ${summary}`,
235498
235365
  { 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
235366
  )),
235500
235367
  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).'
235368
+ 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
235369
  })),
235503
235370
  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").'
235371
+ description: 'Required when outcome="blocked": concrete explanation of what is blocking the work and what is needed to unblock it.'
235505
235372
  }))
235506
235373
  }),
235507
235374
  execute: async (_id, params) => {
@@ -235560,7 +235427,7 @@ ${summary}`,
235560
235427
  }, this.getRunContextFor(taskId));
235561
235428
  await store.logEntry(
235562
235429
  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)`,
235430
+ 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
235431
  void 0,
235565
235432
  this.getRunContextFor(taskId)
235566
235433
  );
@@ -235579,18 +235446,17 @@ ${summary}`,
235579
235446
  parkedAs: autoReplanPark ? "auto-replan" : "failed",
235580
235447
  blockedClass: classification.class,
235581
235448
  thrashCount,
235582
- thrashExhausted,
235583
- prNumbers: classification.prNumbers
235449
+ thrashExhausted
235584
235450
  }
235585
235451
  });
235586
235452
  await this.persistTokenUsage(taskId);
235587
235453
  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"})`}`
235454
+ `\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
235455
  );
235590
235456
  return {
235591
235457
  content: [{
235592
235458
  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."
235459
+ 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
235460
  }],
235595
235461
  details: {}
235596
235462
  };
@@ -237648,7 +237514,7 @@ ${detectedQuestion}
237648
237514
  * rather than fail the task permanently.
237649
237515
  */
237650
237516
  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}"`;
237517
+ const command = isAbsolute18(startPoint) && existsSync66(startPoint) ? `git -C "${startPoint}" rev-parse --verify HEAD^{commit}` : `git rev-parse --verify "${startPoint}^{commit}"`;
237652
237518
  try {
237653
237519
  const { stdout } = await execAsync36(command, { cwd: this.rootDir });
237654
237520
  return stdout.trim() || startPoint;
@@ -237871,7 +237737,7 @@ ${detectedQuestion}
237871
237737
  throw error3;
237872
237738
  }
237873
237739
  };
237874
- if (existsSync65(path10)) {
237740
+ if (existsSync66(path10)) {
237875
237741
  const isRegistered = await this.isRegisteredWorktree(path10);
237876
237742
  if (!isRegistered) {
237877
237743
  await this.store.logEntry(
@@ -238487,7 +238353,7 @@ ${detectedQuestion}
238487
238353
  const stillRegistered = await isRegisteredGitWorktree2(this.rootDir, worktreePath).catch(() => true);
238488
238354
  const activeOwner2 = await this.findActiveWorktreeOwner(worktreePath, taskId).catch(() => "unknown");
238489
238355
  let safeToRemove = isInsideWorktreesDir(this.rootDir, worktreePath, settings) && !stillRegistered && activeOwner2 === null;
238490
- if (safeToRemove && existsSync65(worktreePath)) {
238356
+ if (safeToRemove && existsSync66(worktreePath)) {
238491
238357
  try {
238492
238358
  if (lstatSync3(worktreePath).isSymbolicLink()) {
238493
238359
  safeToRemove = false;
@@ -238515,7 +238381,7 @@ ${detectedQuestion}
238515
238381
  const pruneMsg = pruneErr instanceof Error ? pruneErr.message : String(pruneErr);
238516
238382
  executorLog.warn(`${taskId}: git worktree prune failed during stale-path cleanup of ${worktreePath}: ${pruneMsg}`);
238517
238383
  }
238518
- if (existsSync65(worktreePath)) {
238384
+ if (existsSync66(worktreePath)) {
238519
238385
  try {
238520
238386
  await rm12(worktreePath, { recursive: true, force: true });
238521
238387
  } catch (rmErr) {
@@ -238962,7 +238828,7 @@ ${detectedQuestion}
238962
238828
  this.clearPausedAborted(taskId);
238963
238829
  await this.resetStepsIfWorkLost(latestTask);
238964
238830
  let cleanupFailed = false;
238965
- if (worktreePath && existsSync65(worktreePath)) {
238831
+ if (worktreePath && existsSync66(worktreePath)) {
238966
238832
  try {
238967
238833
  await removeWorktree({
238968
238834
  worktreePath,
@@ -243049,7 +242915,7 @@ var init_overseer_noop_finalize_veto = __esm({
243049
242915
 
243050
242916
  // ../engine/src/merger-ai-worktree.ts
243051
242917
  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";
242918
+ 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
242919
  import { rm as rm13 } from "node:fs/promises";
243054
242920
  import { tmpdir as tmpdir10 } from "node:os";
243055
242921
  import { isAbsolute as isAbsolute19, join as join79, relative as relative17 } from "node:path";
@@ -243084,7 +242950,7 @@ function isBenignAbsentWorktreeError(err) {
243084
242950
  }
243085
242951
  function ensureAiMergeRootIgnored(projectRootDir, settings) {
243086
242952
  const excludePath = join79(projectRootDir, ".git", "info", "exclude");
243087
- if (!existsSync66(excludePath)) return;
242953
+ if (!existsSync67(excludePath)) return;
243088
242954
  try {
243089
242955
  const current = readFileSync24(excludePath, "utf-8");
243090
242956
  const legacyAiMergeRoot = resolveLegacyAiMergeRootPath(projectRootDir);
@@ -243215,7 +243081,7 @@ async function cleanupAiMergeWorktree(input) {
243215
243081
  const cleanupMetadata = { taskId, mergeRoot: canonicalRoot, requestedMergeRoot: mergeRoot };
243216
243082
  let alreadyAbsent = false;
243217
243083
  if (worktreeAdded) {
243218
- if (!existsSync66(canonicalRoot) && !existsSync66(mergeRoot)) {
243084
+ if (!existsSync67(canonicalRoot) && !existsSync67(mergeRoot)) {
243219
243085
  alreadyAbsent = true;
243220
243086
  await log41(`AI merge cleanup: worktree ${canonicalRoot} was already absent before git removal; treating cleanup as idempotent`);
243221
243087
  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 +249403,12 @@ The note must be specific (what/where/why). Prefer citing File Scope, PROMPT, a
249537
249403
  });
249538
249404
 
249539
249405
  // ../engine/src/overseer-watchdog.ts
249540
- import { existsSync as existsSync67, readFileSync as readFileSync25 } from "node:fs";
249406
+ import { existsSync as existsSync68, readFileSync as readFileSync25 } from "node:fs";
249541
249407
  import { dirname as dirname34, join as join82, relative as relative18, resolve as resolve40, sep as sep11 } from "node:path";
249542
249408
  import { homedir as homedir12 } from "node:os";
249543
249409
  function defaultReadText(path10) {
249544
249410
  try {
249545
- if (!existsSync67(path10)) return null;
249411
+ if (!existsSync68(path10)) return null;
249546
249412
  return readFileSync25(path10, "utf8");
249547
249413
  } catch {
249548
249414
  return null;
@@ -255343,7 +255209,7 @@ var init_adapters = __esm({
255343
255209
  });
255344
255210
 
255345
255211
  // ../engine/src/cli-agent/resume-coordinator.ts
255346
- import { existsSync as existsSync68 } from "node:fs";
255212
+ import { existsSync as existsSync69 } from "node:fs";
255347
255213
  import { execFile as execFile30 } from "node:child_process";
255348
255214
  import { promisify as promisify62 } from "node:util";
255349
255215
  async function defaultIsWorktreeDirty(worktreePath) {
@@ -255388,7 +255254,7 @@ var init_resume_coordinator = __esm({
255388
255254
  this.reattachTelemetry = opts.reattachTelemetry;
255389
255255
  this.maxResumeAttempts = opts.maxResumeAttempts ?? DEFAULT_MAX_RESUME_ATTEMPTS;
255390
255256
  this.resumeBackoffBaseMs = opts.resumeBackoffBaseMs ?? DEFAULT_RESUME_BACKOFF_BASE_MS;
255391
- this.worktreeExists = opts.worktreeExists ?? ((p2) => existsSync68(p2));
255257
+ this.worktreeExists = opts.worktreeExists ?? ((p2) => existsSync69(p2));
255392
255258
  this.isWorktreeDirty = opts.isWorktreeDirty ?? defaultIsWorktreeDirty;
255393
255259
  this.log = opts.log ?? (() => {
255394
255260
  });
@@ -283184,7 +283050,7 @@ var init_cli_spawn = __esm({
283184
283050
  // ../../plugins/fusion-plugin-hermes-runtime/src/fusion-skill-install.ts
283185
283051
  import {
283186
283052
  cpSync as cpSync2,
283187
- existsSync as existsSync69,
283053
+ existsSync as existsSync70,
283188
283054
  lstatSync as lstatSync4,
283189
283055
  mkdirSync as mkdirSync13,
283190
283056
  readFileSync as readFileSync26,
@@ -283214,7 +283080,7 @@ function getFusionSkillSourceCandidates(moduleUrl = import.meta.url) {
283214
283080
  function resolveBundledFusionSkillSource() {
283215
283081
  const candidates = getFusionSkillSourceCandidates();
283216
283082
  for (const candidate of candidates) {
283217
- if (existsSync69(join87(candidate, "SKILL.md"))) return candidate;
283083
+ if (existsSync70(join87(candidate, "SKILL.md"))) return candidate;
283218
283084
  }
283219
283085
  return null;
283220
283086
  }
@@ -283232,7 +283098,7 @@ function installFusionSkillIntoHermesHome(options = {}) {
283232
283098
  try {
283233
283099
  mkdirSync13(dirname36(targetDir), { recursive: true });
283234
283100
  let replaced = false;
283235
- if (existsSync69(targetDir) || isBrokenSymlink(targetDir)) {
283101
+ if (existsSync70(targetDir) || isBrokenSymlink(targetDir)) {
283236
283102
  const stat13 = lstatSync4(targetDir);
283237
283103
  if (stat13.isSymbolicLink()) {
283238
283104
  const currentTarget = safeReadlink(targetDir);
@@ -283303,14 +283169,14 @@ function safeReadlink(path10) {
283303
283169
  function isBrokenSymlink(path10) {
283304
283170
  try {
283305
283171
  const stat13 = lstatSync4(path10);
283306
- return stat13.isSymbolicLink() && !existsSync69(path10);
283172
+ return stat13.isSymbolicLink() && !existsSync70(path10);
283307
283173
  } catch {
283308
283174
  return false;
283309
283175
  }
283310
283176
  }
283311
283177
  function looksLikePriorFusionInstall(path10) {
283312
283178
  const skillMd = join87(path10, "SKILL.md");
283313
- if (!existsSync69(skillMd)) return false;
283179
+ if (!existsSync70(skillMd)) return false;
283314
283180
  try {
283315
283181
  const body = readFileSync26(skillMd, "utf-8");
283316
283182
  return /\bfusion\b/i.test(body) && /\bskill\b/i.test(body);
@@ -283321,7 +283187,7 @@ function looksLikePriorFusionInstall(path10) {
283321
283187
  function looksLikeFusionSkillTarget(path10) {
283322
283188
  if (!path10) return false;
283323
283189
  if (basename20(path10).toLowerCase() === FUSION_SKILL_NAME) return true;
283324
- return existsSync69(join87(path10, "SKILL.md"));
283190
+ return existsSync70(join87(path10, "SKILL.md"));
283325
283191
  }
283326
283192
  var FUSION_SKILL_NAME;
283327
283193
  var init_fusion_skill_install = __esm({
@@ -284677,7 +284543,7 @@ var init_provider2 = __esm({
284677
284543
  });
284678
284544
 
284679
284545
  // ../../plugins/fusion-plugin-grok-runtime/src/acp/cli-spawn.ts
284680
- import { existsSync as existsSync70 } from "node:fs";
284546
+ import { existsSync as existsSync71 } from "node:fs";
284681
284547
  import { dirname as dirname38, isAbsolute as isAbsolute22, join as join90, resolve as resolve42 } from "node:path";
284682
284548
  import { fileURLToPath as fileURLToPath7 } from "node:url";
284683
284549
  function asTrimmedString(value) {
@@ -284700,7 +284566,7 @@ function bundledClaudeBridgeBinPath(pluginRoot = pluginRootDir()) {
284700
284566
  }
284701
284567
  function resolveBundledClaudeBridgeBinary(options = {}) {
284702
284568
  const root = options.pluginRoot ?? pluginRootDir();
284703
- const exists2 = options.exists ?? existsSync70;
284569
+ const exists2 = options.exists ?? existsSync71;
284704
284570
  const candidate = bundledClaudeBridgeBinPath(root);
284705
284571
  if (!exists2(candidate)) {
284706
284572
  return {
@@ -286049,7 +285915,7 @@ var init_mcp_forwarding = __esm({
286049
285915
  // ../../plugins/fusion-plugin-grok-runtime/src/skill-loader.ts
286050
285916
  import {
286051
285917
  cpSync as cpSync3,
286052
- existsSync as existsSync71,
285918
+ existsSync as existsSync72,
286053
285919
  mkdirSync as mkdirSync14,
286054
285920
  mkdtempSync as mkdtempSync3,
286055
285921
  readdirSync as readdirSync14,
@@ -286062,7 +285928,7 @@ import { tmpdir as tmpdir15 } from "node:os";
286062
285928
  import { basename as basename22, dirname as dirname40, join as join92, resolve as resolve44 } from "node:path";
286063
285929
  import { fileURLToPath as fileURLToPath8 } from "node:url";
286064
285930
  function isSkillDir(dir) {
286065
- return existsSync71(join92(dir, "SKILL.md"));
285931
+ return existsSync72(join92(dir, "SKILL.md"));
286066
285932
  }
286067
285933
  function pushUnique(out, candidate) {
286068
285934
  if (!candidate) return;
@@ -286126,7 +285992,7 @@ function resolveBundledFusionSkillSource2(moduleUrl = import.meta.url) {
286126
285992
  function installSkillDir(sourceDir, targetDir) {
286127
285993
  if (!isSkillDir(sourceDir)) return false;
286128
285994
  mkdirSync14(dirname40(targetDir), { recursive: true });
286129
- if (existsSync71(targetDir)) {
285995
+ if (existsSync72(targetDir)) {
286130
285996
  rmSync11(targetDir, { recursive: true, force: true });
286131
285997
  }
286132
285998
  try {
@@ -286142,7 +286008,7 @@ function installSkillDir(sourceDir, targetDir) {
286142
286008
  }
286143
286009
  }
286144
286010
  function collectSkillsFromRoot(root, out) {
286145
- if (!existsSync71(root)) return;
286011
+ if (!existsSync72(root)) return;
286146
286012
  if (isSkillDir(root)) {
286147
286013
  out.set(basename22(root), root);
286148
286014
  return;
@@ -286181,7 +286047,7 @@ function stageGrokSessionSkills(options = {}) {
286181
286047
  installSkillDir(source, join92(skillsDir, name2));
286182
286048
  }
286183
286049
  for (const [name2, source] of installed) {
286184
- if (!existsSync71(join92(skillsDir, name2))) {
286050
+ if (!existsSync72(join92(skillsDir, name2))) {
286185
286051
  installSkillDir(source, join92(skillsDir, name2));
286186
286052
  }
286187
286053
  }
@@ -286250,7 +286116,7 @@ var init_skill_loader = __esm({
286250
286116
 
286251
286117
  // ../../plugins/fusion-plugin-grok-runtime/src/tool-bridge.ts
286252
286118
  import { createServer as createServer2 } from "node:http";
286253
- import { existsSync as existsSync72, writeFileSync as writeFileSync15 } from "node:fs";
286119
+ import { existsSync as existsSync73, writeFileSync as writeFileSync15 } from "node:fs";
286254
286120
  import { tmpdir as tmpdir16 } from "node:os";
286255
286121
  import { dirname as dirname41, join as join93 } from "node:path";
286256
286122
  import { fileURLToPath as fileURLToPath9 } from "node:url";
@@ -286296,7 +286162,7 @@ async function startFusionToolBridge(tools) {
286296
286162
  const defs = toolsToMcpToolDefs2(tools);
286297
286163
  if (defs.length === 0) return null;
286298
286164
  const serverPath = fusionToolsMcpServerPath();
286299
- if (!existsSync72(serverPath)) {
286165
+ if (!existsSync73(serverPath)) {
286300
286166
  throw missingMcpSchemaServerError(serverPath);
286301
286167
  }
286302
286168
  const byName = /* @__PURE__ */ new Map();
@@ -286737,7 +286603,7 @@ var init_src11 = __esm({
286737
286603
  });
286738
286604
 
286739
286605
  // ../../plugins/fusion-plugin-claude-runtime/src/acp/cli-spawn.ts
286740
- import { existsSync as existsSync73 } from "node:fs";
286606
+ import { existsSync as existsSync74 } from "node:fs";
286741
286607
  import { dirname as dirname42, isAbsolute as isAbsolute23, join as join94, resolve as resolve45 } from "node:path";
286742
286608
  import { fileURLToPath as fileURLToPath10 } from "node:url";
286743
286609
  function asTrimmedString2(value) {
@@ -286760,7 +286626,7 @@ function bundledClaudeBridgeBinPath2(pluginRoot = pluginRootDir2()) {
286760
286626
  }
286761
286627
  function resolveBundledClaudeBridgeBinary2(options = {}) {
286762
286628
  const root = options.pluginRoot ?? pluginRootDir2();
286763
- const exists2 = options.exists ?? existsSync73;
286629
+ const exists2 = options.exists ?? existsSync74;
286764
286630
  const candidate = bundledClaudeBridgeBinPath2(root);
286765
286631
  if (!exists2(candidate)) {
286766
286632
  return {
@@ -287995,7 +287861,7 @@ var init_acp2 = __esm({
287995
287861
 
287996
287862
  // ../../plugins/fusion-plugin-claude-runtime/src/cli-spawn.ts
287997
287863
  import { spawn as spawn19 } from "node:child_process";
287998
- import { existsSync as existsSync74 } from "node:fs";
287864
+ import { existsSync as existsSync75 } from "node:fs";
287999
287865
  import { basename as basename24, dirname as dirname44, isAbsolute as isAbsolute24, join as join96, resolve as resolve47 } from "node:path";
288000
287866
  import { fileURLToPath as fileURLToPath11 } from "node:url";
288001
287867
  function pluginRootDir3() {
@@ -288007,7 +287873,7 @@ function bundledClaudeBridgeBinPath3(pluginRoot = pluginRootDir3()) {
288007
287873
  }
288008
287874
  function resolveBundledClaudeBridgeBinary3(options = {}) {
288009
287875
  const candidate = bundledClaudeBridgeBinPath3(options.pluginRoot ?? pluginRootDir3());
288010
- const exists2 = options.exists ?? existsSync74;
287876
+ const exists2 = options.exists ?? existsSync75;
288011
287877
  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
287878
  return { kind: "resolved", requested: CLAUDE_CODE_CLI_ACP_BINARY3, path: candidate };
288013
287879
  }
@@ -288170,7 +288036,7 @@ var init_mcp_forwarding2 = __esm({
288170
288036
  // ../../plugins/fusion-plugin-claude-runtime/src/skill-loader.ts
288171
288037
  import {
288172
288038
  cpSync as cpSync4,
288173
- existsSync as existsSync75,
288039
+ existsSync as existsSync76,
288174
288040
  mkdirSync as mkdirSync15,
288175
288041
  mkdtempSync as mkdtempSync4,
288176
288042
  readdirSync as readdirSync15,
@@ -288183,7 +288049,7 @@ import { tmpdir as tmpdir17 } from "node:os";
288183
288049
  import { basename as basename25, dirname as dirname45, join as join97, resolve as resolve48 } from "node:path";
288184
288050
  import { fileURLToPath as fileURLToPath12 } from "node:url";
288185
288051
  function isSkillDir2(dir) {
288186
- return existsSync75(join97(dir, "SKILL.md"));
288052
+ return existsSync76(join97(dir, "SKILL.md"));
288187
288053
  }
288188
288054
  function pushUnique2(out, candidate) {
288189
288055
  if (!candidate) return;
@@ -288247,7 +288113,7 @@ function resolveBundledFusionSkillSource3(moduleUrl = import.meta.url) {
288247
288113
  function installSkillDir2(sourceDir, targetDir) {
288248
288114
  if (!isSkillDir2(sourceDir)) return false;
288249
288115
  mkdirSync15(dirname45(targetDir), { recursive: true });
288250
- if (existsSync75(targetDir)) {
288116
+ if (existsSync76(targetDir)) {
288251
288117
  rmSync12(targetDir, { recursive: true, force: true });
288252
288118
  }
288253
288119
  try {
@@ -288263,7 +288129,7 @@ function installSkillDir2(sourceDir, targetDir) {
288263
288129
  }
288264
288130
  }
288265
288131
  function collectSkillsFromRoot2(root, out) {
288266
- if (!existsSync75(root)) return;
288132
+ if (!existsSync76(root)) return;
288267
288133
  if (isSkillDir2(root)) {
288268
288134
  out.set(basename25(root), root);
288269
288135
  return;
@@ -288302,7 +288168,7 @@ function stageClaudeSessionSkills(options = {}) {
288302
288168
  installSkillDir2(source, join97(skillsDir, name2));
288303
288169
  }
288304
288170
  for (const [name2, source] of installed) {
288305
- if (!existsSync75(join97(skillsDir, name2))) {
288171
+ if (!existsSync76(join97(skillsDir, name2))) {
288306
288172
  installSkillDir2(source, join97(skillsDir, name2));
288307
288173
  }
288308
288174
  }
@@ -288371,7 +288237,7 @@ var init_skill_loader2 = __esm({
288371
288237
 
288372
288238
  // ../../plugins/fusion-plugin-claude-runtime/src/tool-bridge.ts
288373
288239
  import { createServer as createServer3 } from "node:http";
288374
- import { existsSync as existsSync76, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
288240
+ import { existsSync as existsSync77, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
288375
288241
  import { tmpdir as tmpdir18 } from "node:os";
288376
288242
  import { dirname as dirname46, join as join98 } from "node:path";
288377
288243
  import { fileURLToPath as fileURLToPath13 } from "node:url";
@@ -288417,7 +288283,7 @@ async function startFusionToolBridge2(tools, options = {}) {
288417
288283
  const defs = toolsToMcpToolDefs3(tools);
288418
288284
  if (defs.length === 0) return null;
288419
288285
  const serverPath = fusionToolsMcpServerPath2();
288420
- if (!existsSync76(serverPath)) {
288286
+ if (!existsSync77(serverPath)) {
288421
288287
  throw missingMcpSchemaServerError2(serverPath);
288422
288288
  }
288423
288289
  const byName = /* @__PURE__ */ new Map();
@@ -289229,7 +289095,7 @@ var init_provider6 = __esm({
289229
289095
  });
289230
289096
 
289231
289097
  // ../../plugins/fusion-plugin-omp-runtime/src/acp/cli-spawn.ts
289232
- import { existsSync as existsSync77 } from "node:fs";
289098
+ import { existsSync as existsSync78 } from "node:fs";
289233
289099
  import { dirname as dirname47, isAbsolute as isAbsolute25, join as join99, resolve as resolve49 } from "node:path";
289234
289100
  import { fileURLToPath as fileURLToPath14 } from "node:url";
289235
289101
  function asTrimmedString3(value) {
@@ -289252,7 +289118,7 @@ function bundledClaudeBridgeBinPath4(pluginRoot = pluginRootDir4()) {
289252
289118
  }
289253
289119
  function resolveBundledClaudeBridgeBinary4(options = {}) {
289254
289120
  const root = options.pluginRoot ?? pluginRootDir4();
289255
- const exists2 = options.exists ?? existsSync77;
289121
+ const exists2 = options.exists ?? existsSync78;
289256
289122
  const candidate = bundledClaudeBridgeBinPath4(root);
289257
289123
  if (!exists2(candidate)) {
289258
289124
  return {
@@ -290564,7 +290430,7 @@ var init_mcp_forwarding3 = __esm({
290564
290430
 
290565
290431
  // ../../plugins/fusion-plugin-omp-runtime/src/tool-bridge.ts
290566
290432
  import { createServer as createServer4 } from "node:http";
290567
- import { existsSync as existsSync78, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "node:fs";
290433
+ import { existsSync as existsSync79, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "node:fs";
290568
290434
  import { tmpdir as tmpdir19 } from "node:os";
290569
290435
  import { dirname as dirname49, join as join101 } from "node:path";
290570
290436
  import { fileURLToPath as fileURLToPath15 } from "node:url";
@@ -290591,7 +290457,7 @@ function fusionToolsMcpServerPath3() {
290591
290457
  join101(here, "..", "mcp-schema-server.cjs")
290592
290458
  ];
290593
290459
  for (const candidate of candidates) {
290594
- if (existsSync78(candidate)) return candidate;
290460
+ if (existsSync79(candidate)) return candidate;
290595
290461
  }
290596
290462
  return null;
290597
290463
  }
@@ -290729,7 +290595,7 @@ var init_tool_bridge3 = __esm({
290729
290595
  });
290730
290596
 
290731
290597
  // ../../plugins/fusion-plugin-omp-runtime/src/runtime-adapter.ts
290732
- import { existsSync as existsSync79 } from "node:fs";
290598
+ import { existsSync as existsSync80 } from "node:fs";
290733
290599
  function compactDiagnostic3(value) {
290734
290600
  return value.replace(/\s+/g, " ").trim();
290735
290601
  }
@@ -290920,7 +290786,7 @@ var init_runtime_adapter9 = __esm({
290920
290786
  const command = server.command.trim();
290921
290787
  if (!command) return false;
290922
290788
  if (command.includes("/") || command.includes("\\")) {
290923
- return existsSync79(command);
290789
+ return existsSync80(command);
290924
290790
  }
290925
290791
  return true;
290926
290792
  });
@@ -294142,7 +294008,7 @@ __export(task_exports, {
294142
294008
  runTaskUpdate: () => runTaskUpdate
294143
294009
  });
294144
294010
  import { createInterface as createInterface2 } from "node:readline/promises";
294145
- import { watchFile, unwatchFile, statSync as statSync14, existsSync as existsSync80, readFileSync as readFileSync27 } from "node:fs";
294011
+ import { watchFile, unwatchFile, statSync as statSync14, existsSync as existsSync81, readFileSync as readFileSync27 } from "node:fs";
294146
294012
  import { basename as basename28, join as join104 } from "node:path";
294147
294013
  function columnLabel(column) {
294148
294014
  return COLUMN_LABELS[column] ?? column;
@@ -294716,7 +294582,7 @@ async function runTaskLogs(id, options = {}, projectName) {
294716
294582
  if (options.follow) {
294717
294583
  const projectPath = context.projectPath ?? process.cwd();
294718
294584
  const logPath = join104(projectPath, ".fusion", "tasks", id, "agent.log");
294719
- if (!existsSync80(logPath)) {
294585
+ if (!existsSync81(logPath)) {
294720
294586
  console.log(`
294721
294587
  Waiting for log file to be created...`);
294722
294588
  }
@@ -296119,7 +295985,7 @@ import { Type as Type9 } from "typebox";
296119
295985
  import { StringEnum } from "@earendil-works/pi-ai";
296120
295986
  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
295987
  import { readFile as readFile47 } from "node:fs/promises";
296122
- import { existsSync as existsSync81 } from "node:fs";
295988
+ import { existsSync as existsSync82 } from "node:fs";
296123
295989
  import { spawn as spawn23 } from "node:child_process";
296124
295990
  import { AsyncLocalStorage } from "node:async_hooks";
296125
295991
  function truncateAgentDiagnosticText(value, maxChars) {
@@ -296199,12 +296065,12 @@ function resolveProjectRoot2(cwd) {
296199
296065
  warnedMissingProjectRootResolver = true;
296200
296066
  console.warn("[fusion-extension] @fusion/core.getProjectRootFromWorktree is unavailable; using filesystem fallback resolution.");
296201
296067
  }
296202
- if (worktreeProjectRoot && existsSync81(join105(worktreeProjectRoot, ".fusion"))) {
296068
+ if (worktreeProjectRoot && existsSync82(join105(worktreeProjectRoot, ".fusion"))) {
296203
296069
  return worktreeProjectRoot;
296204
296070
  }
296205
296071
  let current = resolve53(cwd);
296206
296072
  while (true) {
296207
- if (existsSync81(join105(current, ".fusion"))) {
296073
+ if (existsSync82(join105(current, ".fusion"))) {
296208
296074
  return current;
296209
296075
  }
296210
296076
  const parent = resolve53(current, "..");