fix(dashboard,core,engine): statically import @fusion/engine to fix createFnAgent undefined in published CLI
The dashboard modules used a variable-specifier dynamic import (`const m = "@fusion/engine"; await import(m)`) to defeat bundler static analysis. tsup honored that and left the dynamic import in dist/bin.js, so the published `@runfusion/fusion` package failed at runtime with "createFnAgent2 is not a function" — `@fusion/engine` isn't on npm and the silent catch set the binding to undefined. Replaces the trick with static imports across planning, chat, subtask-breakdown, mission-interview, agent-generation, ai-refine, roadmap-suggestions, milestone-slice-interview, and routes. Core can't statically import engine (cycle), so it now exposes setCreateFnAgent and engine wires itself in at module load. Documents the pattern in AGENTS.md. Fixes Runfusion/Fusion#9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,45 +1,40 @@
|
||||
/**
|
||||
* Shared lazy loader for `@fusion/engine`'s `createFnAgent`.
|
||||
* Shared lazy accessor for `@fusion/engine`'s `createFnAgent`.
|
||||
*
|
||||
* @fusion/engine must be imported dynamically (not statically) so that:
|
||||
* - core can be consumed in test environments where engine isn't resolvable
|
||||
* - a missing engine package fails soft instead of breaking module load
|
||||
* Core can't import engine statically (engine depends on core, so a static
|
||||
* import would create a cycle). Instead, engine wires its `createFnAgent` in
|
||||
* via `setCreateFnAgent` when its module loads, and consumers in core read it
|
||||
* back through `getFnAgent`.
|
||||
*
|
||||
* Using a variable module specifier also prevents bundlers (Vite) from
|
||||
* statically analysing and trying to resolve the import at build time.
|
||||
* If engine never loads (e.g. tests that only import core), `getFnAgent`
|
||||
* returns `undefined` and callers degrade gracefully.
|
||||
*/
|
||||
|
||||
// Engine exports a function type we intentionally don't pull in here — importing
|
||||
// the type would reintroduce the static resolution this module is designed to avoid.
|
||||
// the type would reintroduce the cycle this module is designed to avoid.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type CreateFnAgent = any;
|
||||
|
||||
let createFnAgent: CreateFnAgent | undefined;
|
||||
|
||||
async function initEngine(): Promise<void> {
|
||||
try {
|
||||
const engineModule = "@fusion/engine";
|
||||
const engine = await import(/* @vite-ignore */ engineModule);
|
||||
createFnAgent = engine.createFnAgent;
|
||||
} catch {
|
||||
createFnAgent = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/** Shape of a message in an agent session's state. */
|
||||
export interface AgentMessage {
|
||||
role: string;
|
||||
content?: string | Array<{ type: string; text: string }>;
|
||||
}
|
||||
|
||||
/** Promise that resolves once the initial load attempt has completed. */
|
||||
const engineReady: Promise<void> = initEngine();
|
||||
/**
|
||||
* Wire engine's `createFnAgent` into core. Called by `@fusion/engine` at module
|
||||
* load. Tests can also call this with a stub.
|
||||
*/
|
||||
export function setCreateFnAgent(fn: CreateFnAgent | undefined): void {
|
||||
createFnAgent = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `createFnAgent` from `@fusion/engine`, or `undefined` if the engine
|
||||
* could not be loaded (typical in tests or when engine isn't installed).
|
||||
* Returns `createFnAgent` from `@fusion/engine`, or `undefined` if engine has
|
||||
* not registered itself yet (typical in tests).
|
||||
*/
|
||||
export async function getFnAgent(): Promise<CreateFnAgent> {
|
||||
await engineReady;
|
||||
return createFnAgent;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ export {
|
||||
getTemplatesForRole,
|
||||
} from "./agent-prompts.js";
|
||||
|
||||
// ── Engine wiring (set by @fusion/engine at module load) ────────────
|
||||
export { setCreateFnAgent, getFnAgent, type AgentMessage } from "./ai-engine-loader.js";
|
||||
|
||||
// ── Prompt Overrides ─────────────────────────────────────────────────
|
||||
export {
|
||||
PROMPT_KEY_CATALOG,
|
||||
|
||||
Reference in New Issue
Block a user