FN-8262: route OMP models through ACP runtime

Route omp-cli model selections to the bundled OMP ACP runtime instead of pi registry resolution.

- Derive and validate OMP runtime routing for primary and fallback model selections.
- Preserve mock and test-mode short-circuits and provide actionable plugin remediation.
- Add OMP routing regression coverage and a patch changeset.

Files changed:
 .changeset/fn-8262-omp-model-routing.md            |   7 +
 .../agent-session-helpers-omp-routing.test.ts      | 218 +++++++++++++++++++++
 packages/engine/src/agent-session-helpers.ts       |  87 +++++++-
 3 files changed, 306 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-8262

Fusion-Task-Lineage: 7a77031f-8703-484e-9e53-a2ef097ec17e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-18 01:47:52 -07:00
parent 2bd25b0605
commit e18c3b4584
3 changed files with 306 additions and 6 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Oh My Pi (omp) model selections now run via the OMP ACP runtime instead of failing.
category: fix
dev: agent-session-helpers auto-derives runtime hint "omp" for omp-cli primary/fallback selections (mirrors the Grok CLI no-visible-key seam); short-circuits under test mode/mock provider, validates an explicit "omp" hint against runtime availability, and prevents the "not found in the pi model registry" hard-fail. Throws an actionable error when the OMP runtime plugin is unavailable.

View File

@@ -0,0 +1,218 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import * as fusionCore from "@fusion/core";
import { createResolvedAgentSession } from "../agent-session-helpers.js";
import { MOCK_PROVIDER_ID } from "../providers/mock-provider.js";
const mockCreateFnAgent = vi.hoisted(() => vi.fn());
vi.mock("../pi.js", () => ({
createFnAgent: mockCreateFnAgent,
promptWithFallback: vi.fn().mockResolvedValue(undefined),
describeModel: vi.fn().mockReturnValue("pi/default"),
wrapToolsWithActionGate: vi.fn((tools) => tools),
wrapToolsWithPermanentAgentGating: vi.fn((tools) => tools),
wrapToolsWithRtkRewrite: vi.fn((tools) => tools),
}));
/*
FNXC:OmpAcp 2026-07-18-09:00:
FN-8262 regression coverage keeps `omp-cli` selections out of pi's model registry: primary/fallback selections use the ACP runtime, unavailable runtime states provide the plugin remediation, and mock/test mode makes no OMP lookup.
*/
function makeOmpPluginRunnerStub(options?: { includeOmp?: boolean; includeOther?: boolean }) {
const createSession = vi.fn().mockResolvedValue({
session: { model: "omp/MiniMax-M2.5", messages: [], dispose: vi.fn() },
});
const ompRegistration = {
pluginId: "fusion-plugin-omp-runtime",
runtime: {
metadata: { runtimeId: "omp", name: "OMP Runtime" },
factory: vi.fn().mockResolvedValue({
id: "omp",
name: "OMP Runtime",
createSession,
promptWithFallback: vi.fn(),
describeModel: vi.fn(() => "omp/MiniMax-M2.5"),
}),
},
};
const otherRegistration = {
pluginId: "other-runtime",
runtime: {
metadata: { runtimeId: "other", name: "Other Runtime" },
factory: vi.fn().mockResolvedValue({
id: "other",
name: "Other Runtime",
createSession,
promptWithFallback: vi.fn(),
describeModel: vi.fn(() => "other/model"),
}),
},
};
const getRuntimeById = vi.fn((runtimeId: string) => {
if (runtimeId === "omp" && options?.includeOmp !== false) return ompRegistration;
if (runtimeId === "other" && options?.includeOther) return otherRegistration;
return undefined;
});
return {
pluginRunner: {
getRuntimeById,
createRuntimeContext: vi.fn().mockResolvedValue({
pluginId: "fusion-plugin-omp-runtime",
taskStore: {},
settings: {},
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
emitEvent: vi.fn(),
}),
},
getRuntimeById,
createSession,
};
}
function sessionOptions(overrides: Record<string, unknown> = {}) {
return {
sessionPurpose: "executor" as const,
cwd: "/tmp/project",
systemPrompt: "system",
...overrides,
};
}
describe("createResolvedAgentSession OMP runtime routing", () => {
beforeEach(() => {
vi.restoreAllMocks();
mockCreateFnAgent.mockReset().mockResolvedValue({
session: { model: "pi/default", messages: [], dispose: vi.fn() },
});
});
it.each(["MiniMax-M2.5", "omp-cli/MiniMax-M2.5"])(
"routes primary omp-cli model %s through OMP ACP instead of pi registry",
async (defaultModelId) => {
const { pluginRunner, getRuntimeById, createSession } = makeOmpPluginRunnerStub();
const audit = { database: vi.fn().mockResolvedValue(undefined) };
const result = await createResolvedAgentSession(sessionOptions({
pluginRunner: pluginRunner as never,
runAuditor: audit as never,
defaultProvider: "omp-cli",
defaultModelId,
}));
expect(result.runtimeId).toBe("omp");
expect(getRuntimeById).toHaveBeenCalledWith("omp");
expect(createSession).toHaveBeenCalledWith(expect.objectContaining({
defaultProvider: "omp-cli",
defaultModelId: "MiniMax-M2.5",
}));
expect(mockCreateFnAgent).not.toHaveBeenCalled();
expect(audit.database).toHaveBeenCalledWith(expect.objectContaining({
type: "session:runtime-resolved",
target: "omp",
metadata: expect.objectContaining({ reason: "omp-cli-runtime" }),
}));
},
);
it("promotes an omp-cli fallback pair and thinking level into the OMP session", async () => {
const { pluginRunner, createSession } = makeOmpPluginRunnerStub();
const result = await createResolvedAgentSession(sessionOptions({
pluginRunner: pluginRunner as never,
defaultProvider: undefined,
defaultModelId: undefined,
fallbackProvider: "omp-cli",
fallbackModelId: "omp-cli/MiniMax-M2.5",
fallbackThinkingLevel: "high",
}));
expect(result.runtimeId).toBe("omp");
expect(createSession).toHaveBeenCalledWith(expect.objectContaining({
defaultProvider: "omp-cli",
defaultModelId: "MiniMax-M2.5",
defaultThinkingLevel: "high",
fallbackProvider: undefined,
fallbackModelId: undefined,
fallbackThinkingLevel: undefined,
}));
});
it.each([
["an absent runtime registration", makeOmpPluginRunnerStub({ includeOmp: false }).pluginRunner],
["no pluginRunner", undefined],
])("reports OMP plugin remediation for %s", async (_label, pluginRunner) => {
const promise = createResolvedAgentSession(sessionOptions({
pluginRunner: pluginRunner as never,
defaultProvider: "omp-cli",
defaultModelId: "MiniMax-M2.5",
}));
await expect(promise).rejects.toThrow(/OMP runtime plugin/);
await expect(promise).rejects.not.toThrow(/not found in the pi model registry/);
});
it("reports OMP plugin remediation for an unavailable explicit omp hint", async () => {
const { pluginRunner } = makeOmpPluginRunnerStub({ includeOmp: false });
await expect(createResolvedAgentSession(sessionOptions({
pluginRunner: pluginRunner as never,
runtimeHint: "omp",
defaultProvider: "omp-cli",
defaultModelId: "MiniMax-M2.5",
}))).rejects.toThrow(/OMP runtime plugin/);
});
it("uses mock without OMP lookup in test mode or when mock is primary", async () => {
const testModeRunner = makeOmpPluginRunnerStub();
const testModeResult = await createResolvedAgentSession(sessionOptions({
pluginRunner: testModeRunner.pluginRunner as never,
settings: { testMode: true } as never,
defaultProvider: "omp-cli",
defaultModelId: "MiniMax-M2.5",
}));
expect(testModeResult.runtimeId).toBe(MOCK_PROVIDER_ID);
expect(testModeRunner.getRuntimeById).not.toHaveBeenCalled();
const mockRunner = makeOmpPluginRunnerStub();
const mockResult = await createResolvedAgentSession(sessionOptions({
pluginRunner: mockRunner.pluginRunner as never,
defaultProvider: MOCK_PROVIDER_ID,
defaultModelId: "scripted",
fallbackProvider: "omp-cli",
fallbackModelId: "MiniMax-M2.5",
}));
expect(mockResult.runtimeId).toBe(MOCK_PROVIDER_ID);
expect(mockRunner.getRuntimeById).not.toHaveBeenCalled();
const configuredMockRunner = makeOmpPluginRunnerStub();
const configuredMockResult = await createResolvedAgentSession(sessionOptions({
pluginRunner: configuredMockRunner.pluginRunner as never,
settings: { defaultProvider: MOCK_PROVIDER_ID } as never,
defaultProvider: "omp-cli",
defaultModelId: "MiniMax-M2.5",
}));
expect(configuredMockResult.runtimeId).toBe(MOCK_PROVIDER_ID);
expect(configuredMockRunner.getRuntimeById).not.toHaveBeenCalled();
});
it("respects a non-OMP explicit runtime hint and leaves Grok routing independent", async () => {
const { pluginRunner, getRuntimeById } = makeOmpPluginRunnerStub({ includeOther: true });
const explicitResult = await createResolvedAgentSession(sessionOptions({
pluginRunner: pluginRunner as never,
runtimeHint: "other",
defaultProvider: "omp-cli",
defaultModelId: "MiniMax-M2.5",
}));
expect(explicitResult.runtimeId).toBe("other");
expect(getRuntimeById).not.toHaveBeenCalledWith("omp");
vi.spyOn(fusionCore, "isGrokApiKeyFusionVisible").mockReturnValue(true);
const grokRunner = makeOmpPluginRunnerStub();
await createResolvedAgentSession(sessionOptions({
pluginRunner: grokRunner.pluginRunner as never,
defaultProvider: "grok-cli",
defaultModelId: "grok-4.5",
}));
expect(grokRunner.getRuntimeById).not.toHaveBeenCalledWith("omp");
});
});

View File

@@ -355,6 +355,69 @@ function stripGrokCliModelProviderPrefix(modelId: string | undefined): string |
: normalized;
}
const OMP_CLI_PROVIDER_ID = "omp-cli";
function isOmpCliSelection(runtimeOptions: AgentRuntimeOptions): boolean {
return runtimeOptions.defaultProvider === OMP_CLI_PROVIDER_ID
|| runtimeOptions.fallbackProvider === OMP_CLI_PROVIDER_ID;
}
function stripOmpCliModelProviderPrefix(modelId: string | undefined): string | undefined {
const normalized = modelId?.trim();
if (!normalized) return normalized;
const ompCliPrefix = `${OMP_CLI_PROVIDER_ID}/`;
return normalized.startsWith(ompCliPrefix)
? normalized.slice(ompCliPrefix.length)
: normalized;
}
/*
FNXC:OmpAcp 2026-07-18-09:00:
FN-8262: `omp-cli/*` models are dynamically discovered from `omp models` and are never registered in pi's execution registry, so route primary and fallback selections to the bundled `omp` ACP runtime before pi resolves a model. Test mode must short-circuit to mock without looking up OMP; an unavailable explicit `runtimeHint: "omp"` must report the OMP plugin remediation rather than pi's misleading model-not-found error.
*/
function buildMissingOmpRuntimeError(): Error {
return new Error(
"Oh My Pi (omp) models require the bundled OMP runtime plugin. "
+ "Install and enable the OMP Runtime plugin (fusion-plugin-omp-runtime) and ensure the `omp` binary is installed and authenticated (`omp acp`, credentials under ~/.omp).",
);
}
function deriveOmpRuntimeHint(
runtimeOptions: AgentRuntimeOptions,
pluginRunner: PluginRunner | undefined,
): string | undefined {
if (!isOmpCliSelection(runtimeOptions)) return undefined;
try {
if (pluginRunner?.getRuntimeById("omp")) return "omp";
} catch {
throw buildMissingOmpRuntimeError();
}
throw buildMissingOmpRuntimeError();
}
function applyOmpCliRuntimeOptions(runtimeOptions: AgentRuntimeOptions): AgentRuntimeOptions {
if (runtimeOptions.defaultProvider === OMP_CLI_PROVIDER_ID) {
return {
...runtimeOptions,
defaultModelId: stripOmpCliModelProviderPrefix(runtimeOptions.defaultModelId),
};
}
if (runtimeOptions.fallbackProvider === OMP_CLI_PROVIDER_ID) {
return {
...runtimeOptions,
defaultProvider: runtimeOptions.fallbackProvider,
defaultModelId: stripOmpCliModelProviderPrefix(runtimeOptions.fallbackModelId),
defaultThinkingLevel: runtimeOptions.fallbackThinkingLevel ?? runtimeOptions.defaultThinkingLevel,
fallbackProvider: undefined,
fallbackModelId: undefined,
fallbackThinkingLevel: undefined,
};
}
return runtimeOptions;
}
function buildMissingGrokRuntimeError(): Error {
return new Error(
"Grok CLI models require the bundled Grok CLI runtime when no Fusion-visible GROK_API_KEY is set. "
@@ -646,7 +709,9 @@ export async function createResolvedAgentSession(
// FNXC:McpConfig 2026-06-25-22:06:
// createResolvedAgentSession is the common lane helper for executor, reviewer, validator, workflow model-node, summarization, and merger-adjacent paths that pass MCP through this seam. Preserve `mcpServers` verbatim here; runtime-resolution/pi own support-gated forwarding and content-free skip logging.
const useMockRuntime = isMockProviderId(runtimeOptions.defaultProvider);
const testModeActive = settings ? isTestModeActive(settings) : false;
const mockProviderActive = isMockProviderId(runtimeOptions.defaultProvider);
const useMockRuntime = mockProviderActive || testModeActive;
const effectiveRuntimeOptions = useMockRuntime
? {
...runtimeOptions,
@@ -676,10 +741,21 @@ export async function createResolvedAgentSession(
const autoGrokRuntimeHint = !useMockRuntime && !runtimeHint
? deriveGrokRuntimeHintForNoVisibleKey(runtimeOptions, pluginRunner)
: undefined;
const effectiveRuntimeHint = autoGrokRuntimeHint ?? runtimeHint;
const autoOmpRuntimeHint = !useMockRuntime && !runtimeHint
? deriveOmpRuntimeHint(runtimeOptions, pluginRunner)
: undefined;
const effectiveRuntimeHint = autoGrokRuntimeHint ?? autoOmpRuntimeHint ?? runtimeHint;
const usesOmpRuntime = effectiveRuntimeHint === "omp" && isOmpCliSelection(runtimeOptions);
if (usesOmpRuntime) {
// resolveRuntime intentionally falls back to pi for an unavailable hint; OMP
// selections must fail here instead so pi never attempts registry resolution.
deriveOmpRuntimeHint(runtimeOptions, pluginRunner);
}
const effectiveRuntimeOptionsWithModel: AgentRuntimeOptions = autoGrokRuntimeHint
? applyGrokCliNoKeyRuntimeOptions(effectiveRuntimeOptions)
: effectiveRuntimeOptions;
: usesOmpRuntime
? applyOmpCliRuntimeOptions(effectiveRuntimeOptions)
: effectiveRuntimeOptions;
const resolved = useMockRuntime
? {
@@ -722,8 +798,6 @@ export async function createResolvedAgentSession(
};
const result = await resolved.runtime.createSession(sessionCreateOptions);
const testModeActive = settings ? isTestModeActive(settings) : false;
const mockProviderActive = isMockProviderId(runtimeOptions.defaultProvider);
const noModelResolved = !mockProviderActive && !testModeActive && (!runtimeOptions.defaultProvider || !runtimeOptions.defaultModelId);
const runtimeBuiltInFallbackModel = noModelResolved ? resolved.runtime.describeModel(result.session) : undefined;
if (noModelResolved) {
@@ -751,7 +825,8 @@ export async function createResolvedAgentSession(
...(noModelResolved ? { noModelResolved: true, runtimeBuiltInFallbackModel } : {}),
...(effectiveRuntimeHint ? { runtimeHint: effectiveRuntimeHint } : {}),
...(autoGrokRuntimeHint ? { reason: "grok-cli-no-visible-key" } : {}),
...(!autoGrokRuntimeHint && "fallbackReason" in resolved && resolved.fallbackReason ? { reason: resolved.fallbackReason } : {}),
...(autoOmpRuntimeHint ? { reason: "omp-cli-runtime" } : {}),
...(!autoGrokRuntimeHint && !autoOmpRuntimeHint && "fallbackReason" in resolved && resolved.fallbackReason ? { reason: resolved.fallbackReason } : {}),
},
});
} catch (err) {