@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.
- package/dist/bin.js +367 -485
- package/dist/child-process-worker.js +172 -290
- package/dist/client/.vite/manifest.json +266 -266
- package/dist/client/assets/{AgentDetailView-CTdyQL11.js → AgentDetailView-DC3B06sE.js} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-DBa7nIQV.js → AgentPermissionPolicyEditor-BtKWfG2h.js} +1 -1
- package/dist/client/assets/{AgentsView-B5tss2oK.js → AgentsView-4DiJ2Htx.js} +3 -3
- package/dist/client/assets/{ChatView-DAKt1a2L.js → ChatView-1nqZiDRk.js} +1 -1
- package/dist/client/assets/{CommandCenter-yPvRo9F6.js → CommandCenter-BHZN2tf0.js} +1 -1
- package/dist/client/assets/{DevServerView-BTyS2owH.js → DevServerView-BwPjQxR_.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-BjGIupNU.js → DirectoryPicker-40vH29Sy.js} +1 -1
- package/dist/client/assets/{DocumentsView-CuxfbpSZ.js → DocumentsView-DI2mQkGF.js} +1 -1
- package/dist/client/assets/{EvalsView-DGuy8SHR.js → EvalsView-CTSt1-CF.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-lK407ZAq.js → ExperimentalAgentOnboardingModal-Z1DJ2xFw.js} +1 -1
- package/dist/client/assets/{GoalsView-DAVN0ObR.js → GoalsView-BNyoKq27.js} +1 -1
- package/dist/client/assets/{InsightsView-BXQKcNof.js → InsightsView-zSwsbTfk.js} +1 -1
- package/dist/client/assets/{MemoryView-D7RKEtV7.js → MemoryView-e7R4rcQY.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-CrOJ57R2.js → PiExtensionsManager-DuZzMGuy.js} +1 -1
- package/dist/client/assets/{PluginManager-D7_evt4j.js → PluginManager--lgPTCei.js} +1 -1
- package/dist/client/assets/{PullRequestView-C0lRUbth.js → PullRequestView-C9KOlfV-.js} +1 -1
- package/dist/client/assets/ReportModal-BiXAfHbg.js +21 -0
- package/dist/client/assets/ReportModal-M3uh98sc.css +1 -0
- package/dist/client/assets/{ResearchView-e8st2Sxu.js → ResearchView-N4N7-_7C.js} +1 -1
- package/dist/client/assets/{SecretsView-BLzckrzb.js → SecretsView-GPt8Mw9d.js} +1 -1
- package/dist/client/assets/{SessionTerminal-F_GIj4gh.js → SessionTerminal-D2bnTrLK.js} +1 -1
- package/dist/client/assets/SettingsModal-B30rPfYC.css +1 -0
- package/dist/client/assets/{SettingsModal-eQbxbkLU.js → SettingsModal-D42Cph4Z.js} +4 -4
- package/dist/client/assets/{SettingsModal-XAddgWkF.js → SettingsModal-DLKu3CYU.js} +1 -1
- package/dist/client/assets/{SettingsTextareaRow-DVjUHNQp.js → SettingsTextareaRow-terwvp1e.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-xID9yIff.js → SetupWizardModal-C-ApHZiL.js} +2 -2
- package/dist/client/assets/{SkillsView-BNdMGu6E.js → SkillsView-GYBeq0B-.js} +1 -1
- package/dist/client/assets/{TodoView-xcOjHZm1.js → TodoView-_1AmNwaZ.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-DZ2WZ-ve.js → WorkflowNodeEditor-CYf2R0ap.js} +1 -1
- package/dist/client/assets/{agent-import-generation-0tkW9WMt.js → agent-import-generation-BZPb1nmV.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-DP9_UvPn.js → architectureDiagram-3BPJPVTR-C_wwNBnV.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-EgKwYMgQ.js → blockDiagram-GPEHLZMM-C7URdbgi.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-Dzv0-_xt.js → c4Diagram-AAUBKEIU-BBc8CXBz.js} +1 -1
- package/dist/client/assets/channel-M95yBImf.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-CNmFmJlQ.js → chunk-2J33WTMH-KfVf5U4u.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-DwtF2sl5.js → chunk-4BX2VUAB-BYzFvVsE.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-DoDurT6F.js → chunk-55IACEB6-B-sxSeny.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-DB87A9uH.js → chunk-727SXJPM-B0xJsLsd.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-ClTDkMp1.js → chunk-AQP2D5EJ-BQ9rJQCA.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BhY2wlYr.js → chunk-FMBD7UC4-DR-KABew.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-BB8mzlmk.js → chunk-ND2GUHAM-C6ZnAzm4.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-D81qQMGf.js → chunk-QZHKN3VN-zs4tZGnv.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DU8poSst.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DU8poSst.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-CCFUHBJX.js → cose-bilkent-S5V4N54A-DnljjIm-.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-xr9MLhRA.js → dagre-BM42HDAG-CEX8DHCo.js} +1 -1
- package/dist/client/assets/{dashboard-view-RXKFv3QV.js → dashboard-view-Chxgvuq4.js} +1 -1
- package/dist/client/assets/{dashboard-view-lqwQ-pMo.js → dashboard-view-DPCI6MD3.js} +1 -1
- package/dist/client/assets/{dashboard-view-oTFt91I3.js → dashboard-view-D_xtIayp.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-mN_tYnBs.js → diagram-2AECGRRQ-Dl9VFNxu.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-xznlq-5C.js → diagram-5GNKFQAL-BPvDRIKP.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-ClRlj1ZA.js → diagram-KO2AKTUF-c1v9YI9G.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-CNiPIQqT.js → diagram-LMA3HP47-ChVDgN1K.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-DwQD-W0P.js → diagram-OG6HWLK6-Cns2RBoA.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-ZXkiCm8S.js → erDiagram-TEJ5UH35-D33nn5Lx.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-DI5haEH9.js → flowDiagram-I6XJVG4X-BluS50at.js} +1 -1
- package/dist/client/assets/{folder-open-NenyCkrb.js → folder-open-oeYf8Ijc.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BRHJpfOZ.js → ganttDiagram-6RSMTGT7-D78LznXX.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-iXT_YBAZ.js → gitGraphDiagram-PVQCEYII-Cz-VVeuo.js} +1 -1
- package/dist/client/assets/index-Clr90mLV.js +2675 -0
- package/dist/client/assets/index-DRZtPb3i.css +1 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-BzkuQtqZ.js → infoDiagram-5YYISTIA-DW64QGM7.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-5hnPG_98.js → ishikawaDiagram-YF4QCWOH-zB3QANvt.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-Co3fnxbr.js → journeyDiagram-JHISSGLW-nVRvkAiF.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-PZFl_y-h.js → kanban-definition-UN3LZRKU-DIsRSNcB.js} +1 -1
- package/dist/client/assets/{mermaid.core-CJRKeU9s.js → mermaid.core-ByWbtMdM.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-B15e2rkx.js → mindmap-definition-RKZ34NQL-BbZZGyk5.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-kGF592D_.js → pieDiagram-4H26LBE5-xEuGzir-.js} +1 -1
- package/dist/client/assets/{puzzle-Dgc4yzjO.js → puzzle-DfKZ6p_i.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CUShcj70.js → quadrantDiagram-W4KKPZXB-CXU0exeD.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-D2k4nLqQ.js → requirementDiagram-4Y6WPE33-xrqjvySl.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CA03BxhT.js → sankeyDiagram-5OEKKPKP-B_XL-dlB.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DDuFN2Pi.js → sequenceDiagram-3UESZ5HK-yPNpJSyq.js} +1 -1
- package/dist/client/assets/{shield-alert-BlYWqolM.js → shield-alert-C0mvrTVY.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-ujl7UARV.js → standing-instructions-template-BA-Y897i.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-yJOkHK4Q.js → stateDiagram-AJRCARHV-B3WacriB.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-BgErHdAN.js → stateDiagram-v2-BHNVJYJU-C2J3lqDw.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BUiDQw21.js → timeline-definition-PNZ67QCA-BMDzGW-H.js} +1 -1
- package/dist/client/assets/{upload-DmlZvpMp.js → upload-BcE2JLKs.js} +1 -1
- package/dist/client/assets/{users-Cx6ACt-W.js → users-CG9A3bIX.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DqyIjBY8.js → vennDiagram-CIIHVFJN-C7ZUUi6G.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-C93tXAKH.js → wardley-L42UT6IY-zpz98dmH.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DmH6-GJV.js → wardleyDiagram-YWT4CUSO-D4TOrUW4.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-BPwjE4Su.js → xychartDiagram-2RQKCTM6-CDfiiyNj.js} +1 -1
- package/dist/client/index.html +4 -3
- package/dist/client/theme-data.css +85 -1
- package/dist/client/version.json +1 -1
- package/dist/extension.js +244 -362
- package/package.json +1 -1
- package/dist/client/assets/ReportModal-Bg-XvWay.css +0 -1
- package/dist/client/assets/ReportModal-Cy5fRNDK.js +0 -21
- package/dist/client/assets/SettingsModal-KnUd0942.css +0 -1
- package/dist/client/assets/channel-BdTAZBkX.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CuoGzmwo.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CuoGzmwo.js +0 -1
- package/dist/client/assets/index-CITV-wDq.css +0 -1
- 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
|
|
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
|
-
|
|
97309
|
-
store.
|
|
97310
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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:
|
|
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.
|
|
204967
|
-
`[self-healing] reconcileCompletedTask ${task.id}: branch ${branchName} has ${comparison.commits.length} unique commit(s) vs ${comparison.mainRef};
|
|
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-
|
|
212177
|
-
|
|
212178
|
-
|
|
212179
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
215003
|
-
|
|
215004
|
-
|
|
215005
|
-
|
|
215006
|
-
|
|
215007
|
-
|
|
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
|
-
|
|
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
|
|
216106
|
-
const
|
|
216107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
216224
|
-
|
|
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:
|
|
216178
|
+
externalBlockers: []
|
|
216235
216179
|
};
|
|
216236
216180
|
}
|
|
216237
|
-
var TASK_ID_RE,
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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 (
|
|
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 &&
|
|
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:
|
|
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
|
|
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 (!
|
|
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
|
|
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 &&
|
|
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
|
|
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 &&
|
|
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 (
|
|
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 &&
|
|
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 &&
|
|
229886
|
-
const taskForAcquisition = live.worktree && !
|
|
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) && !
|
|
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)
|
|
231632
|
+
if (isDurableBlockedTask(live)) {
|
|
231747
231633
|
executorLog.log(
|
|
231748
|
-
`${live.id}: graph failure resume skipped \u2014 durable BLOCKED park honored (
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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 (!
|
|
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 (!
|
|
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"])
|
|
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.
|
|
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` :
|
|
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"}
|
|
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
|
|
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) &&
|
|
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 (
|
|
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 &&
|
|
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 (
|
|
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 &&
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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) =>
|
|
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
|
|
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 (
|
|
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 (
|
|
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() && !
|
|
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 (!
|
|
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
|
|
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
|
|
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 ??
|
|
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
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 ??
|
|
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
|
|
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 ??
|
|
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
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 ??
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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 &&
|
|
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 (
|
|
296089
|
+
if (existsSync82(join105(current, ".fusion"))) {
|
|
296208
296090
|
return current;
|
|
296209
296091
|
}
|
|
296210
296092
|
const parent = resolve53(current, "..");
|