@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
|
@@ -865,6 +865,11 @@ var init_execution_and_ui = __esm({
|
|
|
865
865
|
"dawn",
|
|
866
866
|
// 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.
|
|
867
867
|
"sage",
|
|
868
|
+
/*
|
|
869
|
+
FNXC:MidnightTheme 2026-08-03-02:05:
|
|
870
|
+
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.
|
|
871
|
+
*/
|
|
872
|
+
"midnight",
|
|
868
873
|
// 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.
|
|
869
874
|
"factory-dark",
|
|
870
875
|
// 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.
|
|
@@ -96696,15 +96701,20 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
|
|
|
96696
96701
|
task.workspaceWorktrees = updates.workspaceWorktrees;
|
|
96697
96702
|
}
|
|
96698
96703
|
let movedToTriage = false;
|
|
96704
|
+
let respecifyFromColumn;
|
|
96705
|
+
let respecifyMoveLanes;
|
|
96699
96706
|
if (updates.dependencies !== void 0) {
|
|
96700
96707
|
const oldDeps = new Set((task.dependencies ?? []).map((dependency) => dependency.trim()).filter(Boolean));
|
|
96701
96708
|
const normalizedDependencies = updates.dependencies.map((dependency) => dependency.trim()).filter(Boolean);
|
|
96702
96709
|
const hasNewDeps = normalizedDependencies.some((d) => !oldDeps.has(d));
|
|
96703
96710
|
task.dependencies = normalizedDependencies;
|
|
96704
|
-
const
|
|
96711
|
+
const respecifyIr = hasNewDeps ? await resolveWorkflowIrForTask(store, id).catch(() => void 0) : void 0;
|
|
96712
|
+
respecifyMoveLanes = toTaskMoveLanes(respecifyIr);
|
|
96713
|
+
const depLanes = respecifyIr ? resolveLifecycleColumns(respecifyIr) : void 0;
|
|
96705
96714
|
const holdLane = depLanes === void 0 ? "todo" : depLanes.hold;
|
|
96706
96715
|
if (hasNewDeps && holdLane !== void 0 && task.column === holdLane) {
|
|
96707
96716
|
const intakeLane = depLanes?.intake;
|
|
96717
|
+
respecifyFromColumn = task.column;
|
|
96708
96718
|
const relocating = intakeLane !== void 0 && intakeLane !== task.column;
|
|
96709
96719
|
if (relocating) {
|
|
96710
96720
|
task.column = intakeLane;
|
|
@@ -97382,10 +97392,15 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
|
|
|
97382
97392
|
storeLog.warn(`[task-detail] failed to sync PROMPT.md heading for ${task.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
97383
97393
|
}
|
|
97384
97394
|
}
|
|
97385
|
-
if (movedToTriage) {
|
|
97386
|
-
|
|
97387
|
-
store.
|
|
97388
|
-
|
|
97395
|
+
if (movedToTriage && respecifyFromColumn !== void 0 && respecifyFromColumn !== task.column && respecifyMoveLanes) {
|
|
97396
|
+
store.laneCache.set(task.id, respecifyMoveLanes);
|
|
97397
|
+
store.emit("task:moved", {
|
|
97398
|
+
task,
|
|
97399
|
+
from: respecifyFromColumn,
|
|
97400
|
+
to: task.column,
|
|
97401
|
+
source: "engine",
|
|
97402
|
+
lanes: respecifyMoveLanes
|
|
97403
|
+
});
|
|
97389
97404
|
}
|
|
97390
97405
|
store.emitTaskLifecycleEventSafely("task:updated", [task]);
|
|
97391
97406
|
return task;
|
|
@@ -97397,7 +97412,6 @@ var init_task_update = __esm({
|
|
|
97397
97412
|
init_store();
|
|
97398
97413
|
init_workflow_lifecycle_traits();
|
|
97399
97414
|
init_workflow_ir_resolver();
|
|
97400
|
-
init_workflow_lifecycle_traits();
|
|
97401
97415
|
init_errors3();
|
|
97402
97416
|
init_task_fields();
|
|
97403
97417
|
init_builtin_traits();
|
|
@@ -159300,7 +159314,11 @@ async function createFnAgent2(options) {
|
|
|
159300
159314
|
}
|
|
159301
159315
|
});
|
|
159302
159316
|
attachSessionIdentity(promptableSession);
|
|
159303
|
-
return {
|
|
159317
|
+
return {
|
|
159318
|
+
session: promptableSession,
|
|
159319
|
+
sessionFile: promptableSession.sessionFile,
|
|
159320
|
+
settleFallbackDispatch: async () => void 0
|
|
159321
|
+
};
|
|
159304
159322
|
}
|
|
159305
159323
|
var execAsync10, 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;
|
|
159306
159324
|
var init_pi = __esm({
|
|
@@ -159424,7 +159442,8 @@ function wrapPluginRuntime(instance, runtimeId, runtimeName) {
|
|
|
159424
159442
|
const result = await adapter.createSession(options);
|
|
159425
159443
|
return {
|
|
159426
159444
|
session: result.session ?? result,
|
|
159427
|
-
sessionFile: result.sessionFile
|
|
159445
|
+
sessionFile: result.sessionFile,
|
|
159446
|
+
settleFallbackDispatch: result.settleFallbackDispatch
|
|
159428
159447
|
};
|
|
159429
159448
|
}
|
|
159430
159449
|
throw new Error(`Plugin runtime "${runtimeId}" does not implement createSession`);
|
|
@@ -159533,11 +159552,15 @@ var init_runtime_resolution = __esm({
|
|
|
159533
159552
|
name = "Default PI Runtime";
|
|
159534
159553
|
async createSession(options) {
|
|
159535
159554
|
const { toolOutputMaxChars, mcpServers, ...agentOptions } = options;
|
|
159536
|
-
|
|
159555
|
+
const result = await createFnAgent2({
|
|
159537
159556
|
...agentOptions,
|
|
159538
159557
|
toolOutputMaxChars,
|
|
159539
159558
|
mcpServers: normalizeAgentRuntimeMcpServers(mcpServers)
|
|
159540
159559
|
});
|
|
159560
|
+
return {
|
|
159561
|
+
...result,
|
|
159562
|
+
settleFallbackDispatch: result.settleFallbackDispatch ?? (async () => void 0)
|
|
159563
|
+
};
|
|
159541
159564
|
}
|
|
159542
159565
|
async promptWithFallback(session, prompt, options) {
|
|
159543
159566
|
return promptWithFallback(session, prompt, options);
|
|
@@ -160404,6 +160427,7 @@ async function createResolvedAgentSession(options) {
|
|
|
160404
160427
|
return {
|
|
160405
160428
|
session: result.session,
|
|
160406
160429
|
sessionFile: result.sessionFile,
|
|
160430
|
+
settleFallbackDispatch: result.settleFallbackDispatch,
|
|
160407
160431
|
runtimeId: resolved.runtimeId,
|
|
160408
160432
|
wasConfigured: resolved.wasConfigured
|
|
160409
160433
|
};
|
|
@@ -190840,6 +190864,25 @@ var Scheduler = class {
|
|
|
190840
190864
|
});
|
|
190841
190865
|
}
|
|
190842
190866
|
}
|
|
190867
|
+
if (from === parked.wip && to2 === parked.hold) {
|
|
190868
|
+
if (source === "engine") {
|
|
190869
|
+
this.recentEngineTodoRequeues.set(task.id, task.columnMovedAt ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
190870
|
+
} else {
|
|
190871
|
+
this.recentEngineTodoRequeues.delete(task.id);
|
|
190872
|
+
}
|
|
190873
|
+
} else if (to2 === parked.review || parked.terminal.has(to2)) {
|
|
190874
|
+
this.recentEngineTodoRequeues.delete(task.id);
|
|
190875
|
+
if (task.dispatchStormCount != null || task.lastDispatchAt != null || task.executeRequeueLoopCount != null || task.executeRequeueLoopSignature != null) {
|
|
190876
|
+
void this.store.updateTask(task.id, {
|
|
190877
|
+
dispatchStormCount: null,
|
|
190878
|
+
lastDispatchAt: null,
|
|
190879
|
+
executeRequeueLoopCount: null,
|
|
190880
|
+
executeRequeueLoopSignature: null
|
|
190881
|
+
}).catch((error2) => {
|
|
190882
|
+
schedulerLog.warn(`Failed to reset dispatch oscillation state for ${task.id} on move to ${to2}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
190883
|
+
});
|
|
190884
|
+
}
|
|
190885
|
+
}
|
|
190843
190886
|
const resolvedParked = mergeParkedColumns(await resolveTaskParkedColumns(this.store, task.id), lanes);
|
|
190844
190887
|
if (resolvedParked.terminal.has(to2)) {
|
|
190845
190888
|
try {
|
|
@@ -190900,25 +190943,6 @@ var Scheduler = class {
|
|
|
190900
190943
|
schedulerLog.error(`Failed event-driven blocker reconciliation for ${task.id}`, error2);
|
|
190901
190944
|
}
|
|
190902
190945
|
}
|
|
190903
|
-
if (from === resolvedParked.wip && to2 === resolvedParked.hold) {
|
|
190904
|
-
if (source === "engine") {
|
|
190905
|
-
this.recentEngineTodoRequeues.set(task.id, task.columnMovedAt ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
190906
|
-
} else {
|
|
190907
|
-
this.recentEngineTodoRequeues.delete(task.id);
|
|
190908
|
-
}
|
|
190909
|
-
} else if (to2 === resolvedParked.review || resolvedParked.terminal.has(to2)) {
|
|
190910
|
-
this.recentEngineTodoRequeues.delete(task.id);
|
|
190911
|
-
if (task.dispatchStormCount != null || task.lastDispatchAt != null || task.executeRequeueLoopCount != null || task.executeRequeueLoopSignature != null) {
|
|
190912
|
-
void this.store.updateTask(task.id, {
|
|
190913
|
-
dispatchStormCount: null,
|
|
190914
|
-
lastDispatchAt: null,
|
|
190915
|
-
executeRequeueLoopCount: null,
|
|
190916
|
-
executeRequeueLoopSignature: null
|
|
190917
|
-
}).catch((error2) => {
|
|
190918
|
-
schedulerLog.warn(`Failed to reset dispatch oscillation state for ${task.id} on move to ${to2}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
190919
|
-
});
|
|
190920
|
-
}
|
|
190921
|
-
}
|
|
190922
190946
|
if (resolvedParked.terminal.has(to2) || to2 === resolvedParked.hold) {
|
|
190923
190947
|
schedulerLog.log(`Task moved to ${to2} \u2014 triggering scheduling`);
|
|
190924
190948
|
this.schedule();
|
|
@@ -192692,110 +192716,40 @@ init_src2();
|
|
|
192692
192716
|
|
|
192693
192717
|
// ../engine/src/execution-block-classifier.ts
|
|
192694
192718
|
var TASK_ID_RE = /^[A-Z][A-Z0-9]*-\d+$/i;
|
|
192695
|
-
var
|
|
192696
|
-
var PR_IN_TEXT_RE = /\bPR\s*#?\s*(\d+)\b/gi;
|
|
192697
|
-
var 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;
|
|
192719
|
+
var LEGACY_PR_REF_RE = /^(?:pr[:#-]|#|PR-)(\d+)$/i;
|
|
192698
192720
|
function partitionBlockedByRefs(blockedBy) {
|
|
192699
192721
|
const taskIds = [];
|
|
192700
|
-
const prNumbers = [];
|
|
192701
192722
|
for (const raw of blockedBy) {
|
|
192702
192723
|
const id = raw.trim();
|
|
192703
192724
|
if (!id) continue;
|
|
192704
|
-
|
|
192705
|
-
if (prMatch) {
|
|
192706
|
-
prNumbers.push(Number(prMatch[1]));
|
|
192707
|
-
continue;
|
|
192708
|
-
}
|
|
192725
|
+
if (LEGACY_PR_REF_RE.test(id)) continue;
|
|
192709
192726
|
if (TASK_ID_RE.test(id)) {
|
|
192710
192727
|
const m2 = id.toUpperCase().match(/^([A-Z][A-Z0-9]*)-(\d+)$/);
|
|
192711
192728
|
taskIds.push(m2 ? `${m2[1]}-${m2[2]}` : id.toUpperCase());
|
|
192712
|
-
continue;
|
|
192713
192729
|
}
|
|
192714
192730
|
}
|
|
192715
|
-
return {
|
|
192716
|
-
taskIds: [...new Set(taskIds)],
|
|
192717
|
-
prNumbers: [...new Set(prNumbers.filter((n) => Number.isFinite(n) && n > 0))]
|
|
192718
|
-
};
|
|
192731
|
+
return { taskIds: [...new Set(taskIds)] };
|
|
192719
192732
|
}
|
|
192720
|
-
function
|
|
192721
|
-
const
|
|
192722
|
-
|
|
192723
|
-
const n = Number(match[1]);
|
|
192724
|
-
if (Number.isFinite(n) && n > 0) found.push(n);
|
|
192725
|
-
}
|
|
192726
|
-
return [...new Set(found)];
|
|
192727
|
-
}
|
|
192728
|
-
function isFileClaimBlockedReason(reason) {
|
|
192729
|
-
return CLAIM_REASON_RE.test(reason);
|
|
192730
|
-
}
|
|
192731
|
-
function classifyBlockedExit(reason, blockedBy = []) {
|
|
192732
|
-
const trimmed = reason.trim();
|
|
192733
|
-
const partitioned = partitionBlockedByRefs(blockedBy);
|
|
192734
|
-
const prFromText = extractPrNumbersFromText(trimmed);
|
|
192735
|
-
const prNumbers = [.../* @__PURE__ */ new Set([...partitioned.prNumbers, ...prFromText])];
|
|
192736
|
-
const isClaim = isFileClaimBlockedReason(trimmed) || prNumbers.length > 0 && /claim/i.test(trimmed);
|
|
192737
|
-
if (isClaim || prNumbers.length > 0 && isFileClaimBlockedReason(trimmed)) {
|
|
192738
|
-
const externalBlockers = [
|
|
192739
|
-
...prNumbers.map((number4) => ({ kind: "github-pr", number: number4 }))
|
|
192740
|
-
];
|
|
192741
|
-
if (externalBlockers.length === 0 && isClaim) {
|
|
192742
|
-
externalBlockers.push({
|
|
192743
|
-
kind: "file-claim",
|
|
192744
|
-
prNumber: prNumbers[0]
|
|
192745
|
-
});
|
|
192746
|
-
} else if (isClaim) {
|
|
192747
|
-
for (const n of prNumbers) {
|
|
192748
|
-
if (!externalBlockers.some((b3) => b3.kind === "github-pr" && b3.number === n)) {
|
|
192749
|
-
externalBlockers.push({ kind: "github-pr", number: n });
|
|
192750
|
-
}
|
|
192751
|
-
}
|
|
192752
|
-
if (prNumbers.length === 0) {
|
|
192753
|
-
externalBlockers.push({ kind: "file-claim" });
|
|
192754
|
-
}
|
|
192755
|
-
}
|
|
192756
|
-
const thrashSignature = [
|
|
192757
|
-
"file-claim",
|
|
192758
|
-
...prNumbers.map((n) => `pr:${n}`).sort(),
|
|
192759
|
-
...partitioned.taskIds.slice().sort()
|
|
192760
|
-
].join("|");
|
|
192761
|
-
return {
|
|
192762
|
-
allowAutoReplan: false,
|
|
192763
|
-
class: "file-claim",
|
|
192764
|
-
externalBlockers,
|
|
192765
|
-
thrashSignature: thrashSignature || "file-claim",
|
|
192766
|
-
prNumbers
|
|
192767
|
-
};
|
|
192768
|
-
}
|
|
192769
|
-
if (partitioned.taskIds.length > 0) {
|
|
192770
|
-
return {
|
|
192771
|
-
allowAutoReplan: false,
|
|
192772
|
-
class: "external",
|
|
192773
|
-
externalBlockers: [],
|
|
192774
|
-
thrashSignature: `tasks:${partitioned.taskIds.slice().sort().join(",")}`,
|
|
192775
|
-
prNumbers
|
|
192776
|
-
};
|
|
192777
|
-
}
|
|
192778
|
-
if (prNumbers.length > 0) {
|
|
192733
|
+
function classifyBlockedExit(_reason, blockedBy = []) {
|
|
192734
|
+
const { taskIds } = partitionBlockedByRefs(blockedBy);
|
|
192735
|
+
if (taskIds.length > 0) {
|
|
192779
192736
|
return {
|
|
192780
192737
|
allowAutoReplan: false,
|
|
192781
192738
|
class: "external",
|
|
192782
|
-
|
|
192783
|
-
thrashSignature: prNumbers.map((n) => `pr:${n}`).sort().join("|"),
|
|
192784
|
-
prNumbers
|
|
192739
|
+
thrashSignature: `tasks:${taskIds.slice().sort().join(",")}`
|
|
192785
192740
|
};
|
|
192786
192741
|
}
|
|
192787
192742
|
return {
|
|
192788
192743
|
allowAutoReplan: true,
|
|
192789
192744
|
class: "plan-defect",
|
|
192790
|
-
|
|
192791
|
-
thrashSignature: "plan-defect",
|
|
192792
|
-
prNumbers: []
|
|
192745
|
+
thrashSignature: "plan-defect"
|
|
192793
192746
|
};
|
|
192794
192747
|
}
|
|
192795
192748
|
var BLOCKED_THRASH_LIMIT = 3;
|
|
192796
192749
|
var BLOCKED_THRASH_WINDOW_MS = 60 * 60 * 1e3;
|
|
192797
192750
|
function countBlockedThrashHits(log41, signature, nowMs = Date.now(), windowMs = BLOCKED_THRASH_WINDOW_MS) {
|
|
192798
192751
|
if (!log41?.length) return 0;
|
|
192752
|
+
if (signature === "plan-defect") return 0;
|
|
192799
192753
|
const cutoff = nowMs - windowMs;
|
|
192800
192754
|
let count2 = 0;
|
|
192801
192755
|
for (const entry of log41) {
|
|
@@ -192807,18 +192761,7 @@ function countBlockedThrashHits(log41, signature, nowMs = Date.now(), windowMs =
|
|
|
192807
192761
|
continue;
|
|
192808
192762
|
}
|
|
192809
192763
|
}
|
|
192810
|
-
if (signature === "plan-defect") continue;
|
|
192811
|
-
if (signature.includes("file-claim") || signature.startsWith("pr:") || signature.includes("|pr:")) {
|
|
192812
|
-
if (isFileClaimBlockedReason(action) || /PR\s*#?\s*\d+/i.test(action) || action.includes("durable external block")) {
|
|
192813
|
-
count2 += 1;
|
|
192814
|
-
continue;
|
|
192815
|
-
}
|
|
192816
|
-
}
|
|
192817
192764
|
for (const part of signature.split("|")) {
|
|
192818
|
-
if (part.startsWith("pr:") && action.includes(`PR #${part.slice(3)}`)) {
|
|
192819
|
-
count2 += 1;
|
|
192820
|
-
break;
|
|
192821
|
-
}
|
|
192822
192765
|
if (part.startsWith("tasks:") && part.slice(6).split(",").some((id) => id && action.includes(id))) {
|
|
192823
192766
|
count2 += 1;
|
|
192824
192767
|
break;
|
|
@@ -192827,28 +192770,18 @@ function countBlockedThrashHits(log41, signature, nowMs = Date.now(), windowMs =
|
|
|
192827
192770
|
}
|
|
192828
192771
|
return count2;
|
|
192829
192772
|
}
|
|
192830
|
-
function isDurableBlockedError(error2) {
|
|
192831
|
-
if (!error2?.startsWith("BLOCKED:")) return false;
|
|
192832
|
-
const reason = error2.slice("BLOCKED:".length).trim();
|
|
192833
|
-
const classification = classifyBlockedExit(reason, []);
|
|
192834
|
-
return !classification.allowAutoReplan;
|
|
192835
|
-
}
|
|
192836
192773
|
function isDurableBlockedTask(task) {
|
|
192837
|
-
if (task.status === "failed" && isDurableBlockedError(task.error)) return true;
|
|
192838
192774
|
const meta2 = task.sourceMetadata;
|
|
192839
192775
|
if (!meta2 || typeof meta2 !== "object") return false;
|
|
192840
|
-
if (meta2.blockedClass
|
|
192841
|
-
|
|
192842
|
-
}
|
|
192843
|
-
const blockers = meta2.externalBlockers;
|
|
192844
|
-
return Array.isArray(blockers) && blockers.length > 0 && task.status === "failed";
|
|
192776
|
+
if (meta2.blockedClass !== "external") return false;
|
|
192777
|
+
return task.status === "failed" || task.status === "needs-replan";
|
|
192845
192778
|
}
|
|
192846
192779
|
function buildExternalBlockMetadataPatch(classification, thrashCount) {
|
|
192847
192780
|
return {
|
|
192848
192781
|
blockedClass: classification.class,
|
|
192849
192782
|
blockedThrashSignature: classification.thrashSignature,
|
|
192850
192783
|
blockedThrashCount: thrashCount,
|
|
192851
|
-
externalBlockers:
|
|
192784
|
+
externalBlockers: []
|
|
192852
192785
|
};
|
|
192853
192786
|
}
|
|
192854
192787
|
|
|
@@ -208426,7 +208359,6 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
208426
208359
|
{ name: "reconcile-done-task-integrity", fn: () => this.reconcileDoneTaskIntegrity() },
|
|
208427
208360
|
{ name: "reconcile-stale-merger-status", fn: () => this.reconcileStaleMergerStatus() },
|
|
208428
208361
|
{ name: "reconcile-stale-duplicate-decision", fn: () => this.reconcileStaleDuplicateDecisionPause() },
|
|
208429
|
-
{ name: "reconcile-external-pr-blockers", fn: () => this.reconcileExternalPrBlockers() },
|
|
208430
208362
|
// FNXC:OrphanedPendingSteps 2026-07-22-16:35 (FN-8492 review follow-up): also
|
|
208431
208363
|
// steady-state — a step session can die without an engine restart, and startup-only
|
|
208432
208364
|
// cadence left that case riding the 3×30-min stall escalator to a deadlock park.
|
|
@@ -209930,6 +209862,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
209930
209862
|
}
|
|
209931
209863
|
async reclaimStaleActiveBranches() {
|
|
209932
209864
|
const reclaimArchivedColumns = await resolveProjectColumnsForRoles(this.store, ["archived"]);
|
|
209865
|
+
const reclaimCompleteColumns = await resolveProjectColumnsForRoles(this.store, ["complete"]);
|
|
209933
209866
|
try {
|
|
209934
209867
|
const settings = await this.store.getSettings();
|
|
209935
209868
|
if (settings.globalPause || settings.enginePaused) return 0;
|
|
@@ -210027,10 +209960,12 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
210027
209960
|
if (task.worktree && await isUsableTaskWorktree(this.options.rootDir, task.worktree)) continue;
|
|
210028
209961
|
const inspection = await this.inspectOrphanedBranch(branch);
|
|
210029
209962
|
if (!inspection) continue;
|
|
210030
|
-
|
|
209963
|
+
const isCompleteColumn2 = reclaimCompleteColumns.has(task.column);
|
|
209964
|
+
if (inspection.uniqueCommitCount > 0 && !isCompleteColumn2) {
|
|
210031
209965
|
log34.warn(`[recovery] stale-active-branch-rescue-needed ${task.id} branch=${branch} unique=${inspection.uniqueCommitCount} tip=${inspection.tipSha.slice(0, 12)}`);
|
|
210032
209966
|
continue;
|
|
210033
209967
|
}
|
|
209968
|
+
const reclaimReason = inspection.uniqueCommitCount > 0 ? "complete-column-unique-commits-force" : "zero-unique-commits-no-worktree";
|
|
210034
209969
|
await execAsync33(`git branch -D ${JSON.stringify(branch)}`, {
|
|
210035
209970
|
cwd: this.options.rootDir,
|
|
210036
209971
|
timeout: 12e4,
|
|
@@ -210048,7 +209983,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
210048
209983
|
});
|
|
210049
209984
|
await this.store.logEntry(
|
|
210050
209985
|
task.id,
|
|
210051
|
-
`[recovery] stale-active-branch-reclaim ${task.id} branch=${branch} reason
|
|
209986
|
+
`[recovery] stale-active-branch-reclaim ${task.id} branch=${branch} reason=${reclaimReason}`
|
|
210052
209987
|
);
|
|
210053
209988
|
try {
|
|
210054
209989
|
const auditor = createRunAuditor(this.store, {
|
|
@@ -210066,7 +210001,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
210066
210001
|
branch,
|
|
210067
210002
|
tipSha: inspection.tipSha,
|
|
210068
210003
|
uniqueCommitCount: inspection.uniqueCommitCount,
|
|
210069
|
-
reason:
|
|
210004
|
+
reason: reclaimReason
|
|
210070
210005
|
}
|
|
210071
210006
|
});
|
|
210072
210007
|
} catch (auditErr) {
|
|
@@ -210113,10 +210048,9 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
210113
210048
|
const baseBranch = task.baseBranch || await resolveIntegrationBranch(this.options.rootDir, void 0);
|
|
210114
210049
|
const comparison = await listUniqueBranchCommits(this.options.rootDir, baseBranch, branchName);
|
|
210115
210050
|
if (comparison.commits.length > 0) {
|
|
210116
|
-
log34.
|
|
210117
|
-
`[self-healing] reconcileCompletedTask ${task.id}: branch ${branchName} has ${comparison.commits.length} unique commit(s) vs ${comparison.mainRef};
|
|
210051
|
+
log34.log(
|
|
210052
|
+
`[self-healing] reconcileCompletedTask ${task.id}: branch ${branchName} has ${comparison.commits.length} unique commit(s) vs ${comparison.mainRef}; force-deleting post-completion (squash-safe)`
|
|
210118
210053
|
);
|
|
210119
|
-
return false;
|
|
210120
210054
|
}
|
|
210121
210055
|
try {
|
|
210122
210056
|
await execAsync33(`git branch -D ${shellQuote2(branchName)}`, {
|
|
@@ -217323,71 +217257,12 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
217323
217257
|
}
|
|
217324
217258
|
}
|
|
217325
217259
|
/*
|
|
217326
|
-
FNXC:HonestBlockedExit 2026-08-02-
|
|
217327
|
-
|
|
217328
|
-
|
|
217329
|
-
|
|
217260
|
+
FNXC:HonestBlockedExit 2026-08-02-23:59 (operator decision — FN-8728 vs PR #2398):
|
|
217261
|
+
The FN-8700 `reconcile-external-pr-blockers` sweep (gh-backed clearing of PR-claim parks)
|
|
217262
|
+
is REMOVED with the whole PR/file-claim blocking mechanism. Open PRs are never blockers;
|
|
217263
|
+
file-scope conflicts are arbitrated only by Fusion's own board. Legacy PR-claim parks are
|
|
217264
|
+
no longer honored by isDurableBlockedTask, so normal recovery paths reclaim them.
|
|
217330
217265
|
*/
|
|
217331
|
-
async reconcileExternalPrBlockers() {
|
|
217332
|
-
try {
|
|
217333
|
-
if (!await isGhAvailable()) {
|
|
217334
|
-
log34.debug("reconcile-external-pr-blockers skipped \u2014 gh unavailable");
|
|
217335
|
-
return 0;
|
|
217336
|
-
}
|
|
217337
|
-
const tasks2 = await this.store.listTasks({ slim: true, includeArchived: false, limit: 500 });
|
|
217338
|
-
let cleared = 0;
|
|
217339
|
-
for (const task of tasks2.slice(0, 80)) {
|
|
217340
|
-
if (task.status !== "failed" || !task.error?.startsWith("BLOCKED:")) continue;
|
|
217341
|
-
const meta2 = task.sourceMetadata;
|
|
217342
|
-
const blockers = meta2?.externalBlockers;
|
|
217343
|
-
if (!Array.isArray(blockers) || blockers.length === 0) continue;
|
|
217344
|
-
const prNumbers = blockers.map((b3) => b3 && typeof b3 === "object" && b3.kind === "github-pr" ? Number(b3.number) : NaN).filter((n) => Number.isFinite(n) && n > 0);
|
|
217345
|
-
if (prNumbers.length === 0) continue;
|
|
217346
|
-
let allResolved = true;
|
|
217347
|
-
for (const n of prNumbers) {
|
|
217348
|
-
try {
|
|
217349
|
-
const pr2 = await runGhJsonAsync(
|
|
217350
|
-
["pr", "view", String(n), "--json", "state,mergedAt"],
|
|
217351
|
-
{ timeoutMs: 15e3 }
|
|
217352
|
-
);
|
|
217353
|
-
const state = String(pr2?.state ?? "").toUpperCase();
|
|
217354
|
-
const merged = Boolean(pr2?.mergedAt) || state === "MERGED";
|
|
217355
|
-
const closed = state === "CLOSED" || state === "MERGED";
|
|
217356
|
-
if (!merged && !closed) {
|
|
217357
|
-
allResolved = false;
|
|
217358
|
-
break;
|
|
217359
|
-
}
|
|
217360
|
-
} catch {
|
|
217361
|
-
allResolved = false;
|
|
217362
|
-
break;
|
|
217363
|
-
}
|
|
217364
|
-
}
|
|
217365
|
-
if (!allResolved) continue;
|
|
217366
|
-
await this.store.updateTask(task.id, {
|
|
217367
|
-
status: null,
|
|
217368
|
-
error: null,
|
|
217369
|
-
sourceMetadataPatch: {
|
|
217370
|
-
externalBlockers: [],
|
|
217371
|
-
blockedClass: null,
|
|
217372
|
-
blockedThrashSignature: null,
|
|
217373
|
-
blockedThrashCount: null,
|
|
217374
|
-
externalPrBlockersClearedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
217375
|
-
externalPrBlockersCleared: prNumbers
|
|
217376
|
-
}
|
|
217377
|
-
});
|
|
217378
|
-
await this.store.logEntry(
|
|
217379
|
-
task.id,
|
|
217380
|
-
`Auto-recovered: external PR blocker(s) ${prNumbers.map((n) => `#${n}`).join(", ")} merged/closed \u2014 cleared durable BLOCKED park for re-dispatch`
|
|
217381
|
-
);
|
|
217382
|
-
log34.log(`Cleared durable PR block for ${task.id} (prs=${prNumbers.join(",")})`);
|
|
217383
|
-
cleared += 1;
|
|
217384
|
-
}
|
|
217385
|
-
return cleared;
|
|
217386
|
-
} catch (error2) {
|
|
217387
|
-
log34.warn(`reconcile-external-pr-blockers failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
217388
|
-
return 0;
|
|
217389
|
-
}
|
|
217390
|
-
}
|
|
217391
217266
|
async resolveExplicitDuplicateMarkerTasks() {
|
|
217392
217267
|
try {
|
|
217393
217268
|
const settings = await this.store.getSettings();
|
|
@@ -219772,7 +219647,7 @@ If you have just finished a step's work, immediately call \`fn_task_update\` to
|
|
|
219772
219647
|
|
|
219773
219648
|
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.
|
|
219774
219649
|
|
|
219775
|
-
**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
|
|
219650
|
+
**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.
|
|
219776
219651
|
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.
|
|
219777
219652
|
|
|
219778
219653
|
## How to work
|
|
@@ -227231,75 +227106,16 @@ Graph value: ${failureValue}` : ""}`, budget.key),
|
|
|
227231
227106
|
);
|
|
227232
227107
|
}
|
|
227233
227108
|
}
|
|
227234
|
-
/*
|
|
227235
|
-
FNXC:HonestBlockedExit 2026-08-02-01:30:
|
|
227236
|
-
Agents often log `BLOCKED: … actively claimed by PR #N` without calling fn_task_done.
|
|
227237
|
-
Without promotion, incomplete-step graph resume requeues the card and thrash restarts
|
|
227238
|
-
(FN-8700). Promote claim/external blocks to a durable failed park here.
|
|
227239
|
-
*/
|
|
227240
|
-
async parkDurableBlockedFromRecentLog(live) {
|
|
227241
|
-
const recent = [...live.log ?? []].reverse().find((entry) => {
|
|
227242
|
-
const action = entry.action ?? "";
|
|
227243
|
-
return action.startsWith("BLOCKED:") || action.includes("durable external block");
|
|
227244
|
-
});
|
|
227245
|
-
if (!recent?.action) return false;
|
|
227246
|
-
const reason = recent.action.startsWith("BLOCKED:") ? recent.action.slice("BLOCKED:".length).trim() : recent.action;
|
|
227247
|
-
const classification = classifyBlockedExit(reason, []);
|
|
227248
|
-
if (classification.allowAutoReplan) return false;
|
|
227249
|
-
const thrashCount = countBlockedThrashHits(live.log, classification.thrashSignature) + 1;
|
|
227250
|
-
const thrashExhausted = thrashCount >= BLOCKED_THRASH_LIMIT;
|
|
227251
|
-
const parkError = thrashExhausted ? `BLOCKED: ${reason} [thrash-exhausted after ${thrashCount} identical durable blocks]` : `BLOCKED: ${reason}`;
|
|
227252
|
-
const metaPatch = buildExternalBlockMetadataPatch(classification, thrashCount);
|
|
227253
|
-
await this.store.updateTask(live.id, {
|
|
227254
|
-
status: "failed",
|
|
227255
|
-
error: parkError,
|
|
227256
|
-
paused: false,
|
|
227257
|
-
pausedByAgentId: null,
|
|
227258
|
-
sourceMetadataPatch: metaPatch
|
|
227259
|
-
}, this.getRunContextFor(live.id));
|
|
227260
|
-
await this.store.logEntry(
|
|
227261
|
-
live.id,
|
|
227262
|
-
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})`,
|
|
227263
|
-
void 0,
|
|
227264
|
-
this.getRunContextFor(live.id)
|
|
227265
|
-
);
|
|
227266
|
-
await this.store.recordRunAuditEvent?.({
|
|
227267
|
-
taskId: live.id,
|
|
227268
|
-
agentId: "executor",
|
|
227269
|
-
runId: generateSyntheticRunId("execution-blocked-log-promote", live.id),
|
|
227270
|
-
domain: "database",
|
|
227271
|
-
mutationType: "task:execution-blocked-parked",
|
|
227272
|
-
target: live.id,
|
|
227273
|
-
metadata: {
|
|
227274
|
-
taskId: live.id,
|
|
227275
|
-
blockedBy: [],
|
|
227276
|
-
hasReason: true,
|
|
227277
|
-
parkedAs: "failed",
|
|
227278
|
-
blockedClass: classification.class,
|
|
227279
|
-
thrashCount,
|
|
227280
|
-
thrashExhausted,
|
|
227281
|
-
prNumbers: classification.prNumbers,
|
|
227282
|
-
source: "session-log-promote"
|
|
227283
|
-
}
|
|
227284
|
-
});
|
|
227285
|
-
executorLog.warn(
|
|
227286
|
-
`${live.id}: promoted durable BLOCKED from session log (class=${classification.class}; thrash=${thrashCount})`
|
|
227287
|
-
);
|
|
227288
|
-
return true;
|
|
227289
|
-
}
|
|
227290
227109
|
async routeGraphFailureToExecutionResume(live, failedNode, failureValue, resumeLanesMemo) {
|
|
227291
227110
|
if (live.deletedAt) return false;
|
|
227292
227111
|
if (live.paused || live.userPaused === true) return false;
|
|
227293
227112
|
if ((await resolveTerminalColumnsFor(this.store, live.id)).includes(live.column)) return false;
|
|
227294
|
-
if (isDurableBlockedTask(live)
|
|
227113
|
+
if (isDurableBlockedTask(live)) {
|
|
227295
227114
|
executorLog.log(
|
|
227296
|
-
`${live.id}: graph failure resume skipped \u2014 durable BLOCKED park honored (
|
|
227115
|
+
`${live.id}: graph failure resume skipped \u2014 durable BLOCKED park honored (task-dependency block)`
|
|
227297
227116
|
);
|
|
227298
227117
|
return false;
|
|
227299
227118
|
}
|
|
227300
|
-
if (await this.parkDurableBlockedFromRecentLog(live)) {
|
|
227301
|
-
return false;
|
|
227302
|
-
}
|
|
227303
227119
|
if (failedNode === COMPLETION_SUMMARY_NODE_ID) return false;
|
|
227304
227120
|
const incompleteSteps = hasNonTerminalWorkflowSteps(live);
|
|
227305
227121
|
const implementationIncompleteMergeFailure = this.isMergeGraphFailure(failedNode) && failureValue === "implementation-incomplete";
|
|
@@ -231046,10 +230862,10 @@ ${summary}`,
|
|
|
231046
230862
|
{ 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.' }
|
|
231047
230863
|
)),
|
|
231048
230864
|
blockedBy: Type6.Optional(Type6.Array(Type6.String(), {
|
|
231049
|
-
description: 'When outcome="blocked": task IDs (e.g. ["FN-8145"])
|
|
230865
|
+
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.'
|
|
231050
230866
|
})),
|
|
231051
230867
|
reason: Type6.Optional(Type6.String({
|
|
231052
|
-
description: 'Required when outcome="blocked": concrete explanation of what is blocking the work and what is needed to unblock it.
|
|
230868
|
+
description: 'Required when outcome="blocked": concrete explanation of what is blocking the work and what is needed to unblock it.'
|
|
231053
230869
|
}))
|
|
231054
230870
|
}),
|
|
231055
230871
|
execute: async (_id, params) => {
|
|
@@ -231108,7 +230924,7 @@ ${summary}`,
|
|
|
231108
230924
|
}, this.getRunContextFor(taskId));
|
|
231109
230925
|
await store.logEntry(
|
|
231110
230926
|
taskId,
|
|
231111
|
-
thrashExhausted ? `${parkError} \u2014 durable external block thrash-exhausted (signature=${classification.thrashSignature}); parked failed, no auto-requeue` :
|
|
230927
|
+
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)`,
|
|
231112
230928
|
void 0,
|
|
231113
230929
|
this.getRunContextFor(taskId)
|
|
231114
230930
|
);
|
|
@@ -231127,18 +230943,17 @@ ${summary}`,
|
|
|
231127
230943
|
parkedAs: autoReplanPark ? "auto-replan" : "failed",
|
|
231128
230944
|
blockedClass: classification.class,
|
|
231129
230945
|
thrashCount,
|
|
231130
|
-
thrashExhausted
|
|
231131
|
-
prNumbers: classification.prNumbers
|
|
230946
|
+
thrashExhausted
|
|
231132
230947
|
}
|
|
231133
230948
|
});
|
|
231134
230949
|
await this.persistTokenUsage(taskId);
|
|
231135
230950
|
executorLog.log(
|
|
231136
|
-
`\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"}
|
|
230951
|
+
`\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"})`}`
|
|
231137
230952
|
);
|
|
231138
230953
|
return {
|
|
231139
230954
|
content: [{
|
|
231140
230955
|
type: "text",
|
|
231141
|
-
text: autoReplanPark ? "Task parked as blocked with no
|
|
230956
|
+
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.`
|
|
231142
230957
|
}],
|
|
231143
230958
|
details: {}
|
|
231144
230959
|
};
|
|
@@ -243299,6 +243114,7 @@ init_transient_error_detector();
|
|
|
243299
243114
|
init_rate_limit_retry();
|
|
243300
243115
|
init_agent_tools();
|
|
243301
243116
|
import { exec as exec42 } from "node:child_process";
|
|
243117
|
+
import { existsSync as existsSync67 } from "node:fs";
|
|
243302
243118
|
import { readFile as readFile40, writeFile as writeFile29, rm as rm12 } from "node:fs/promises";
|
|
243303
243119
|
import { join as join78 } from "node:path";
|
|
243304
243120
|
import { promisify as promisify57 } from "node:util";
|
|
@@ -243600,6 +243416,8 @@ var TriageProcessor = class _TriageProcessor {
|
|
|
243600
243416
|
unregisterAdmissionProvider = null;
|
|
243601
243417
|
/** Timestamps when tasks entered the `processing` set, for staleness detection. */
|
|
243602
243418
|
processingSince = /* @__PURE__ */ new Map();
|
|
243419
|
+
/** Monotonic provenance token for attempt-local planner fallback observation. */
|
|
243420
|
+
planningAttemptSequence = 0;
|
|
243603
243421
|
wasGlobalPaused = false;
|
|
243604
243422
|
wasEnginePaused = false;
|
|
243605
243423
|
idleSemaphoreLeakCandidateSince = null;
|
|
@@ -244895,7 +244713,28 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
244895
244713
|
}
|
|
244896
244714
|
await this.store.logEntry(task.id, `Planning session running in task worktree ${planningCwd}`).catch(() => void 0);
|
|
244897
244715
|
}
|
|
244898
|
-
const
|
|
244716
|
+
const authoritativePromptPath = join78(this.rootDir, promptPath);
|
|
244717
|
+
const planningAttemptBaseline = existsSync67(authoritativePromptPath) ? await readFile40(authoritativePromptPath, "utf-8").catch(() => void 0) : void 0;
|
|
244718
|
+
const planningAttempt = {
|
|
244719
|
+
id: `${task.id}:${++this.planningAttemptSequence}`,
|
|
244720
|
+
baseline: planningAttemptBaseline,
|
|
244721
|
+
fallbackEngaged: false,
|
|
244722
|
+
fallbackSettlements: []
|
|
244723
|
+
};
|
|
244724
|
+
const fallbackObserver = createFallbackModelObserver({
|
|
244725
|
+
agent: "triage",
|
|
244726
|
+
label: "triage",
|
|
244727
|
+
store: this.store,
|
|
244728
|
+
taskId: task.id,
|
|
244729
|
+
taskTitle: task.title
|
|
244730
|
+
});
|
|
244731
|
+
const onFallbackModelUsed = (payload) => {
|
|
244732
|
+
planningAttempt.fallbackEngaged = true;
|
|
244733
|
+
const settlement = Promise.resolve(fallbackObserver(payload));
|
|
244734
|
+
planningAttempt.fallbackSettlements.push(settlement);
|
|
244735
|
+
return settlement;
|
|
244736
|
+
};
|
|
244737
|
+
const { session, settleFallbackDispatch, runtimeId } = await createResolvedAgentSession({
|
|
244899
244738
|
sessionPurpose: "triage",
|
|
244900
244739
|
runtimeHint: triageRuntimeHint,
|
|
244901
244740
|
pluginRunner: this.options.pluginRunner,
|
|
@@ -244934,13 +244773,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
244934
244773
|
taskTitle: task.title,
|
|
244935
244774
|
actionGateContext: this.buildActionGateContext(task.id, triageRunContext.runId, assignedAgent, settings.defaultAgentPermissionPolicy),
|
|
244936
244775
|
permanentAgentGating: this.buildPermanentAgentGatingContext(task.id, triageRunContext.runId, assignedAgent, settings.defaultAgentPermissionPolicy),
|
|
244937
|
-
onFallbackModelUsed
|
|
244938
|
-
agent: "triage",
|
|
244939
|
-
label: "triage",
|
|
244940
|
-
store: this.store,
|
|
244941
|
-
taskId: task.id,
|
|
244942
|
-
taskTitle: task.title
|
|
244943
|
-
})
|
|
244776
|
+
onFallbackModelUsed
|
|
244944
244777
|
});
|
|
244945
244778
|
const modelDesc = formatModelMarkerDetails(describeModel(session), resolvePlanningThinkingLevel(settings, task.planningThinkingLevel ?? task.thinkingLevel));
|
|
244946
244779
|
planLog.debug(`${task.id}: using model ${modelDesc}`);
|
|
@@ -245010,6 +244843,10 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
245010
244843
|
agentPrompt,
|
|
245011
244844
|
imageContents.length > 0 ? { images: imageContents } : void 0
|
|
245012
244845
|
);
|
|
244846
|
+
const fallbackDispatchBoundaryMissing = typeof settleFallbackDispatch !== "function";
|
|
244847
|
+
if (!fallbackDispatchBoundaryMissing) {
|
|
244848
|
+
await settleFallbackDispatch();
|
|
244849
|
+
}
|
|
245013
244850
|
checkSessionError(session);
|
|
245014
244851
|
if (this.pauseAborted.has(task.id)) {
|
|
245015
244852
|
this.pauseAborted.delete(task.id);
|
|
@@ -245102,12 +244939,42 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
245102
244939
|
}
|
|
245103
244940
|
}
|
|
245104
244941
|
}
|
|
245105
|
-
|
|
245106
|
-
|
|
245107
|
-
|
|
245108
|
-
|
|
245109
|
-
|
|
245110
|
-
|
|
244942
|
+
let settledFallbackCount = 0;
|
|
244943
|
+
while (settledFallbackCount < planningAttempt.fallbackSettlements.length) {
|
|
244944
|
+
const pendingSettlements = planningAttempt.fallbackSettlements.slice(settledFallbackCount);
|
|
244945
|
+
settledFallbackCount = planningAttempt.fallbackSettlements.length;
|
|
244946
|
+
await Promise.all(pendingSettlements);
|
|
244947
|
+
}
|
|
244948
|
+
const artifactChangedByAttempt = planningAttempt.baseline !== written;
|
|
244949
|
+
if (fallbackDispatchBoundaryMissing || planningAttempt.fallbackEngaged || !artifactChangedByAttempt) {
|
|
244950
|
+
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}`;
|
|
244951
|
+
const decision = computeRecoveryDecision({
|
|
244952
|
+
recoveryRetryCount: task.recoveryRetryCount,
|
|
244953
|
+
nextRecoveryAt: task.nextRecoveryAt
|
|
244954
|
+
});
|
|
244955
|
+
if (decision.shouldRetry) {
|
|
244956
|
+
const retryMessage = `${failure} \u2014 retry ${decision.nextState.recoveryRetryCount}/${MAX_RECOVERY_RETRIES} in ${formatDelay(decision.delayMs)}.`;
|
|
244957
|
+
planLog.warn(`${task.id} ${retryMessage}`);
|
|
244958
|
+
await this.store.logEntry(task.id, retryMessage);
|
|
244959
|
+
await this.updatePlanningStateIfStillCurrent(task, {
|
|
244960
|
+
status: this.restoreStatusAfterInterruptedTriageWork(task),
|
|
244961
|
+
error: null,
|
|
244962
|
+
recoveryRetryCount: decision.nextState.recoveryRetryCount,
|
|
244963
|
+
nextRecoveryAt: decision.nextState.nextRecoveryAt
|
|
244964
|
+
});
|
|
244965
|
+
return;
|
|
244966
|
+
}
|
|
244967
|
+
const failureMessage = `${failure} after ${MAX_RECOVERY_RETRIES} retries. Retry after adjusting the task prompt or model.`;
|
|
244968
|
+
planLog.error(`${task.id} clean planning attempt retry budget exhausted`);
|
|
244969
|
+
await this.store.logEntry(task.id, failureMessage);
|
|
244970
|
+
if (await this.updatePlanningStateIfStillCurrent(task, {
|
|
244971
|
+
status: "failed",
|
|
244972
|
+
error: failureMessage,
|
|
244973
|
+
recoveryRetryCount: null,
|
|
244974
|
+
nextRecoveryAt: null
|
|
244975
|
+
})) {
|
|
244976
|
+
await this.backfillBlankTitleAfterTerminalTriageFailure(task);
|
|
244977
|
+
}
|
|
245111
244978
|
return;
|
|
245112
244979
|
}
|
|
245113
244980
|
const deterministicSpecFailure = await this.validateGeneratedPrompt(task.id, written);
|
|
@@ -245149,6 +245016,21 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
245149
245016
|
}
|
|
245150
245017
|
return;
|
|
245151
245018
|
}
|
|
245019
|
+
const duplicateReport = { outcome: "parked" };
|
|
245020
|
+
if (await this.tryFinalizeExplicitDuplicateMarker(task, written, settings, {
|
|
245021
|
+
isReplan,
|
|
245022
|
+
feedback
|
|
245023
|
+
}, duplicateReport)) {
|
|
245024
|
+
this.options.onSpecifyComplete?.(task, duplicateReport);
|
|
245025
|
+
return;
|
|
245026
|
+
}
|
|
245027
|
+
if (task.recoveryRetryCount != null || task.nextRecoveryAt != null) {
|
|
245028
|
+
await this.updatePlanningStateIfStillCurrent(task, {
|
|
245029
|
+
error: null,
|
|
245030
|
+
recoveryRetryCount: null,
|
|
245031
|
+
nextRecoveryAt: null
|
|
245032
|
+
});
|
|
245033
|
+
}
|
|
245152
245034
|
const finalizeReport = await this.finalizeApprovedTask(task, written, settings, {
|
|
245153
245035
|
isReplan,
|
|
245154
245036
|
feedback
|
|
@@ -249882,14 +249764,14 @@ function extractAdvisorAssistantText(session) {
|
|
|
249882
249764
|
|
|
249883
249765
|
// ../engine/src/overseer-watchdog.ts
|
|
249884
249766
|
init_logger();
|
|
249885
|
-
import { existsSync as
|
|
249767
|
+
import { existsSync as existsSync68, readFileSync as readFileSync24 } from "node:fs";
|
|
249886
249768
|
import { dirname as dirname34, join as join79, relative as relative17, resolve as resolve40, sep as sep11 } from "node:path";
|
|
249887
249769
|
import { homedir as homedir12 } from "node:os";
|
|
249888
249770
|
var log38 = createLogger("overseer-watchdog");
|
|
249889
249771
|
var WATCHDOG_FILENAMES = ["OVERSEER.md", "WATCHDOG.md"];
|
|
249890
249772
|
function defaultReadText(path5) {
|
|
249891
249773
|
try {
|
|
249892
|
-
if (!
|
|
249774
|
+
if (!existsSync68(path5)) return null;
|
|
249893
249775
|
return readFileSync24(path5, "utf8");
|
|
249894
249776
|
} catch {
|
|
249895
249777
|
return null;
|
|
@@ -251033,7 +250915,7 @@ init_worktree_paths();
|
|
|
251033
250915
|
// ../engine/src/merger-ai-worktree.ts
|
|
251034
250916
|
init_active_session_registry();
|
|
251035
250917
|
import { execFile as execFile28 } from "node:child_process";
|
|
251036
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
250918
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync69, mkdirSync as mkdirSync11, readdirSync as readdirSync12, readFileSync as readFileSync25, realpathSync as realpathSync11, rmSync as rmSync9, statSync as statSync13 } from "node:fs";
|
|
251037
250919
|
import { rm as rm13 } from "node:fs/promises";
|
|
251038
250920
|
import { tmpdir as tmpdir10 } from "node:os";
|
|
251039
250921
|
import { isAbsolute as isAbsolute21, join as join80, relative as relative18 } from "node:path";
|
|
@@ -251070,7 +250952,7 @@ function isBenignAbsentWorktreeError(err) {
|
|
|
251070
250952
|
}
|
|
251071
250953
|
function ensureAiMergeRootIgnored(projectRootDir, settings) {
|
|
251072
250954
|
const excludePath = join80(projectRootDir, ".git", "info", "exclude");
|
|
251073
|
-
if (!
|
|
250955
|
+
if (!existsSync69(excludePath)) return;
|
|
251074
250956
|
try {
|
|
251075
250957
|
const current = readFileSync25(excludePath, "utf-8");
|
|
251076
250958
|
const legacyAiMergeRoot = resolveLegacyAiMergeRootPath(projectRootDir);
|
|
@@ -251201,7 +251083,7 @@ async function cleanupAiMergeWorktree(input) {
|
|
|
251201
251083
|
const cleanupMetadata = { taskId, mergeRoot: canonicalRoot, requestedMergeRoot: mergeRoot };
|
|
251202
251084
|
let alreadyAbsent = false;
|
|
251203
251085
|
if (worktreeAdded) {
|
|
251204
|
-
if (!
|
|
251086
|
+
if (!existsSync69(canonicalRoot) && !existsSync69(mergeRoot)) {
|
|
251205
251087
|
alreadyAbsent = true;
|
|
251206
251088
|
await log41(`AI merge cleanup: worktree ${canonicalRoot} was already absent before git removal; treating cleanup as idempotent`);
|
|
251207
251089
|
await audit.git({ type: "merge:ai-worktree-cleanup", target: canonicalRoot, metadata: { ...cleanupMetadata, phase: "git-remove", success: true, alreadyAbsent: true, idempotent: true, code: "ENOENT" } });
|