feat(FN-4772): complete Step 3 — wire hybrid executor startup

Fusion-Task-Id: FN-4772
Fusion-Task-Lineage: d296e96a-fe44-4896-928a-fa44da62b41a
This commit is contained in:
Fusion (runfusion.ai)
2026-05-16 11:10:21 -07:00
committed by gsxdsm
parent d9ad416265
commit 19d1b65e3f
3 changed files with 71 additions and 3 deletions

View File

@@ -24,7 +24,13 @@ import {
} from "@fusion/core";
import type { AutomationRunResult, ScheduledTask } from "@fusion/core";
import { createServer, GitHubClient, createSkillsAdapter, getProjectSettingsPath, loadTlsCredentialsFromEnv } from "@fusion/dashboard";
import { ProjectEngineManager, PeerExchangeService, setHostExtensionPaths } from "@fusion/engine";
import {
ProjectEngineManager,
PeerExchangeService,
HybridExecutor,
shouldUseHybridExecutor,
setHostExtensionPaths,
} from "@fusion/engine";
import {
AuthStorage,
DefaultPackageManager,
@@ -327,6 +333,15 @@ export async function runDaemon(opts: DaemonOptions = {}) {
});
await engineManager.startAll();
let hybridExecutor: HybridExecutor | null = null;
const hybridGate = await shouldUseHybridExecutor(sharedCentralCore);
console.log(`[daemon] hybrid executor gate: enabled=${hybridGate.enabled} reason=${hybridGate.reason}`);
if (hybridGate.enabled) {
hybridExecutor = new HybridExecutor(sharedCentralCore);
await hybridExecutor.initialize();
}
engineManager.startReconciliation();
// Backfill Claude Code skills for all registered projects. No-op when
@@ -778,6 +793,10 @@ export async function runDaemon(opts: DaemonOptions = {}) {
shuttingDown = true;
// Stop all project engines uniformly
if (hybridExecutor) {
await hybridExecutor.shutdown();
}
await engineManager.stopAll();
// Stop peer exchange service

View File

@@ -27,7 +27,19 @@ import {
stopAllDevServers,
type RuntimeLogger,
} from "@fusion/dashboard";
import { aiMergeTask, MissionAutopilot, MissionExecutionLoop, HeartbeatMonitor, HeartbeatTriggerScheduler, type WakeContext, ProjectEngineManager, PeerExchangeService, setHostExtensionPaths } from "@fusion/engine";
import {
aiMergeTask,
MissionAutopilot,
MissionExecutionLoop,
HeartbeatMonitor,
HeartbeatTriggerScheduler,
type WakeContext,
ProjectEngineManager,
PeerExchangeService,
HybridExecutor,
shouldUseHybridExecutor,
setHostExtensionPaths,
} from "@fusion/engine";
import { AuthStorage, DefaultPackageManager, ModelRegistry, SettingsManager, discoverAndLoadExtensions, createExtensionRuntime } from "@mariozechner/pi-coding-agent";
import {
getMergeStrategy,
@@ -1494,6 +1506,17 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
// Start engines for all registered projects eagerly
await engineManager.startAll();
let hybridExecutor: HybridExecutor | null = null;
const hybridGate = await shouldUseHybridExecutor(centralCoreForEngine);
logSink.log(
`hybrid executor gate: enabled=${hybridGate.enabled} reason=${hybridGate.reason}`,
"dashboard",
);
if (hybridGate.enabled) {
hybridExecutor = new HybridExecutor(centralCoreForEngine);
await hybridExecutor.initialize();
}
// Start background reconciliation to detect and start engines for projects
// registered after startup (without requiring dashboard UI access).
// This ensures project task execution starts from backend runtime alone.
@@ -1558,6 +1581,9 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
}
disposeCallbacks.push(async () => {
if (hybridExecutor) {
await hybridExecutor.shutdown();
}
await engineManager.stopAll();
await closeCentralCoreBestEffort(centralCoreForEngine, "dispose cleanup");
});
@@ -1565,6 +1591,7 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
app = createServer(store, {
engine: cwdEngine,
engineManager,
hybridExecutor,
centralCore: centralCoreForEngine,
authStorage: dashboardAuthStorage,
modelRegistry,
@@ -1675,6 +1702,10 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
logSink.warn(`Failed to stop dev servers: ${message}`, "dashboard");
}
if (hybridExecutor) {
await hybridExecutor.shutdown();
}
// Stop all project engines uniformly
await engineManager.stopAll();

View File

@@ -24,7 +24,13 @@ import {
} from "@fusion/core";
import type { AutomationRunResult, ScheduledTask } from "@fusion/core";
import { createServer, GitHubClient, createSkillsAdapter, getProjectSettingsPath, loadTlsCredentialsFromEnv } from "@fusion/dashboard";
import { ProjectEngineManager, PeerExchangeService, setHostExtensionPaths } from "@fusion/engine";
import {
ProjectEngineManager,
PeerExchangeService,
HybridExecutor,
shouldUseHybridExecutor,
setHostExtensionPaths,
} from "@fusion/engine";
import {
AuthStorage,
DefaultPackageManager,
@@ -355,6 +361,14 @@ export async function runServe(
// Start engines for all registered projects eagerly
await engineManager.startAll();
let hybridExecutor: HybridExecutor | null = null;
const hybridGate = await shouldUseHybridExecutor(sharedCentralCore);
console.log(`[serve] hybrid executor gate: enabled=${hybridGate.enabled} reason=${hybridGate.reason}`);
if (hybridGate.enabled) {
hybridExecutor = new HybridExecutor(sharedCentralCore);
await hybridExecutor.initialize();
}
// Backfill Claude Code skills for any registered project that's missing
// `.claude/skills/fusion`. Runs only when pi-claude-cli is configured; for
// users on the direct Anthropic provider this is a no-op and leaves no
@@ -974,6 +988,10 @@ export async function runServe(
// Ignore errors getting handle types
}
if (hybridExecutor) {
await hybridExecutor.shutdown();
}
// Stop all project engines uniformly
await engineManager.stopAll();