fix: make OMP chat resolve provider-qualified model selectors
Bare picker ids like MiniMax-M2.5 were ambiguous across omp providers and landed on unauthenticated plan aliases, which broke ACP with Internal error. Prefer `omp models --json` selectors, resolve bare ids before spawn, and copy mcp-schema-server.cjs into dist so the Fusion tool bridge can start.
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build": "tsc && node -e \"require('node:fs').copyFileSync('src/mcp-schema-server.cjs','dist/mcp-schema-server.cjs')\"",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run --silent=passed-only --reporter=dot"
|
||||
},
|
||||
|
||||
@@ -91,6 +91,7 @@ const plugin: FusionPlugin = definePlugin({
|
||||
export default plugin;
|
||||
export { probeOmpBinary } from "./probe.js";
|
||||
export { discoverOmpProviderModels } from "./provider.js";
|
||||
export { discoverOmpModels, resolveOmpModelSelector } from "./process-manager.js";
|
||||
export { OmpRuntimeAdapter } from "./runtime-adapter.js";
|
||||
export type { OmpBinaryStatus } from "./types.js";
|
||||
export {
|
||||
|
||||
@@ -5,6 +5,13 @@ FNXC:OmpAcp 2026-07-11-23:35:
|
||||
Model discovery for the omp-cli provider card. Prefer a structured list when
|
||||
available; fall soft to an empty list with a clear reason so the picker stays
|
||||
usable without inventing model ids.
|
||||
|
||||
FNXC:OmpAcp 2026-07-18-16:40:
|
||||
Bare table ids like MiniMax-M2.5 are ambiguous across omp providers
|
||||
(minimax-code vs alibaba-coding-plan). Prefer `omp models --json` `selector`
|
||||
fields (`minimax-code/MiniMax-M2.5`) so ACP `--model` hits the authenticated
|
||||
provider instead of failing with "No API key found for alibaba-coding-plan" /
|
||||
JSON-RPC Internal error.
|
||||
*/
|
||||
|
||||
export interface OmpModelDiscoveryResult {
|
||||
@@ -16,12 +23,20 @@ export interface OmpModelDiscoveryResult {
|
||||
|
||||
/**
|
||||
* Attempt to list models from the local omp install.
|
||||
* Tries `omp models` then falls back to empty (CLI default still works via ACP).
|
||||
* Prefers `omp models --json` selectors, then text table, then empty.
|
||||
*/
|
||||
export async function discoverOmpModels(
|
||||
binary: string,
|
||||
timeoutMs = 8000,
|
||||
): Promise<OmpModelDiscoveryResult> {
|
||||
const jsonResult = await runOmpCommand(binary, ["models", "--json"], timeoutMs);
|
||||
if (jsonResult.code === 0) {
|
||||
const models = parseModelListJson(jsonResult.stdout || jsonResult.stderr);
|
||||
if (models.length > 0) {
|
||||
return { models, source: "omp models --json", fallbackUsed: false };
|
||||
}
|
||||
}
|
||||
|
||||
const result = await runOmpCommand(binary, ["models"], timeoutMs);
|
||||
if (result.code === 0) {
|
||||
const models = parseModelList(result.stdout || result.stderr);
|
||||
@@ -42,6 +57,70 @@ export async function discoverOmpModels(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* FNXC:OmpAcp 2026-07-18-16:40:
|
||||
* Map a bare or omp-cli-prefixed model id to a unique provider-qualified selector
|
||||
* from discovery (e.g. MiniMax-M2.5 → minimax-code/MiniMax-M2.5). Returns the
|
||||
* original id when already qualified, unique, or discovery is empty.
|
||||
*/
|
||||
export async function resolveOmpModelSelector(
|
||||
binary: string,
|
||||
model: string | undefined,
|
||||
timeoutMs = 8000,
|
||||
): Promise<string | undefined> {
|
||||
const normalized = model?.trim();
|
||||
if (!normalized) return undefined;
|
||||
if (normalized.includes("/")) return normalized;
|
||||
|
||||
const discovered = await discoverOmpModels(binary, timeoutMs);
|
||||
if (discovered.models.length === 0) return normalized;
|
||||
|
||||
const exact = discovered.models.filter((id) => id === normalized);
|
||||
if (exact.length === 1) return exact[0];
|
||||
|
||||
const qualified = discovered.models.filter((id) => id.endsWith(`/${normalized}`));
|
||||
if (qualified.length === 1) return qualified[0];
|
||||
if (qualified.length > 1) {
|
||||
// Prefer common coding providers when bare ids collide across plans.
|
||||
const preferred =
|
||||
qualified.find((id) => id.startsWith("minimax-code/"))
|
||||
?? qualified.find((id) => !id.includes("alibaba") && !id.includes("coding-plan"))
|
||||
?? qualified[0];
|
||||
return preferred;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function parseModelListJson(text: string): string[] {
|
||||
try {
|
||||
const data = JSON.parse(text) as unknown;
|
||||
const list = Array.isArray(data)
|
||||
? data
|
||||
: data && typeof data === "object" && Array.isArray((data as { models?: unknown }).models)
|
||||
? (data as { models: unknown[] }).models
|
||||
: null;
|
||||
if (!list) return [];
|
||||
|
||||
const models: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const entry of list) {
|
||||
if (!entry || typeof entry !== "object") continue;
|
||||
const record = entry as Record<string, unknown>;
|
||||
const selector = typeof record.selector === "string" ? record.selector.trim() : "";
|
||||
const provider = typeof record.provider === "string" ? record.provider.trim() : "";
|
||||
const id = typeof record.id === "string" ? record.id.trim() : "";
|
||||
const candidate = selector || (provider && id ? `${provider}/${id}` : id);
|
||||
if (!candidate || candidate.length < 2 || seen.has(candidate)) continue;
|
||||
seen.add(candidate);
|
||||
models.push(candidate);
|
||||
}
|
||||
return models;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function parseModelList(text: string): string[] {
|
||||
const lines = text.split(/\r?\n/);
|
||||
const models: string[] = [];
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
normalizeOmpCliModel,
|
||||
} from "./acp-settings.js";
|
||||
import { toAcpMcpServers, type AcpMcpServer } from "./mcp-forwarding.js";
|
||||
import { resolveOmpModelSelector } from "./process-manager.js";
|
||||
import {
|
||||
startFusionToolBridge,
|
||||
type FusionToolBridge,
|
||||
@@ -32,6 +33,11 @@ Grok ACP:
|
||||
- Operator MCP servers → session/new.mcpServers (stdio/http/sse)
|
||||
- Engine customTools (fn_*) → loopback MCP bridge + fusion-custom-tools server
|
||||
- System rules describe available Fusion MCP tools so omp prefers them for board ops
|
||||
|
||||
FNXC:OmpAcp 2026-07-18-16:40:
|
||||
Resolve bare picker ids (MiniMax-M2.5) to provider-qualified selectors before
|
||||
`omp --model … acp` so multi-provider collisions do not land on an unauthenticated
|
||||
plan provider and return JSON-RPC Internal error.
|
||||
*/
|
||||
|
||||
export type AcpAdapterFactory = (settings: Record<string, unknown>) => {
|
||||
@@ -246,7 +252,16 @@ export class OmpRuntimeAdapter implements AgentRuntime {
|
||||
systemPrompt: "",
|
||||
},
|
||||
): Promise<AgentSessionResult> {
|
||||
const model = normalizeOmpCliModel(options.defaultModelId) ?? "omp/default";
|
||||
const rawModel = normalizeOmpCliModel(options.defaultModelId) ?? "omp/default";
|
||||
/*
|
||||
FNXC:OmpAcp 2026-07-18-16:40:
|
||||
Bare ids from the omp-cli picker can map to multiple upstream providers.
|
||||
Resolve to a unique `provider/id` selector before spawn so authenticated
|
||||
providers (e.g. minimax-code) win over unauthenticated plan aliases.
|
||||
*/
|
||||
const model =
|
||||
(await resolveOmpModelSelector(this.binary, modelForCli(rawModel) ?? rawModel))
|
||||
?? rawModel;
|
||||
const turnAccum: TurnAccum = { text: "" };
|
||||
const resources: SessionResources = {};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user