Multiple coordinated fixes for the perceived "dashboard takes forever to load" complaint. Per-page-load HTTP requests drop from ~177 to ~101 and duplicate per-project InProcessRuntime creation is eliminated. - engine: shouldUseHybridExecutor no longer auto-enables for local-only multi-project setups (set FUSION_HYBRID_EXECUTOR=1 to force). The duplicate-runtime path was running self-healing twice per project and contending on the same SQLite file. ProjectEngineManager already handles N local projects with one InProcessRuntime each. - dashboard cli: parallelized independent store inits, started CentralCore.init early in background, ran plugin loading concurrently with extension resolution. Sequenced SQLite store inits to avoid a TOCTOU race in addColumnIfMissing migrations across TaskStore / AutomationStore / PluginStore / AgentStore (all open the same .fusion/fusion.db). Restored try/catch around HybridExecutor.initialize and engineManager.ensureEngine so a paused or broken cwd project no longer aborts dashboard startup. - dashboard client: added in-flight request dedupe wrapped around the top API offenders. /api/plugins/ui-slots drops from 17x to 1x per load. dedupe.forceFresh redirects ALL in-flight waiters to receive the fresh post-mutation response, not just the forcing caller. Generation counters in useAgents and AgentListModal protect against slow polls overwriting fresh state. - dashboard SSE: agent event handler now debounces 250ms with a trailing-edge guard so multi-agent activity bursts coalesce to at most 2 refetches per burst. - dashboard route: PATCH /api/projects/:id with isolationMode change returns 503 with actionable guidance when HybridExecutor is unavailable, instead of silently persisting a config the live runtime won't honor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
43 lines
1.3 KiB
TypeScript
43 lines
1.3 KiB
TypeScript
import type { CentralCore } from "@fusion/core";
|
|
|
|
export interface HybridExecutorGateDecision {
|
|
enabled: boolean;
|
|
reason: string;
|
|
}
|
|
|
|
function parseEnvOverride(value: string | undefined): HybridExecutorGateDecision | null {
|
|
if (value === "1") {
|
|
return { enabled: true, reason: "env-override" };
|
|
}
|
|
|
|
if (value === "0") {
|
|
return { enabled: false, reason: "env-override" };
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export async function shouldUseHybridExecutor(centralCore: CentralCore): Promise<HybridExecutorGateDecision> {
|
|
const envOverride = parseEnvOverride(process.env.FUSION_HYBRID_EXECUTOR);
|
|
if (envOverride) {
|
|
return envOverride;
|
|
}
|
|
|
|
try {
|
|
const nodes = await centralCore.listNodes();
|
|
if (nodes.length > 1) {
|
|
return { enabled: true, reason: "multi-node" };
|
|
}
|
|
|
|
// Local-only single-node: HybridExecutor's value is cross-node routing.
|
|
// ProjectEngineManager already handles N local projects with one
|
|
// InProcessRuntime per project; running HybridExecutor in parallel just
|
|
// creates a second InProcessRuntime per project, duplicating self-healing
|
|
// recovery and adding ~7s to cold start. Skip until a remote node is
|
|
// registered (set FUSION_HYBRID_EXECUTOR=1 to force-enable).
|
|
return { enabled: false, reason: "single-node-local-only" };
|
|
} catch {
|
|
return { enabled: false, reason: "central-unavailable" };
|
|
}
|
|
}
|