fix(plugin): auto-prefix providerId with 'opencode-' for OC 1.17.8+ native gate (#4527)

Integrated into release/v3.8.34 (supersedes #4445)
This commit is contained in:
Hernan Javier Ardila Sanchez
2026-06-22 22:05:00 +02:00
committed by GitHub
parent b04eac3b8d
commit 5887bc3d6a
8 changed files with 204 additions and 156 deletions

View File

@@ -240,10 +240,18 @@ export function resolveOmniRoutePluginOptions(
opts?: OmniRoutePluginOptions
): Required<Pick<OmniRoutePluginOptions, "providerId" | "displayName" | "modelCacheTtl">> &
Pick<OmniRoutePluginOptions, "baseURL" | "features"> {
const providerId = opts?.providerId ?? OMNIROUTE_PROVIDER_KEY;
const rawProviderId = opts?.providerId ?? OMNIROUTE_PROVIDER_KEY;
// OC 1.17.8+ native-adapter gate rejects providerID not in
// {openai, anthropic, opencode*}. Silently prefix so existing
// configs (providerId: "omniroute") keep working.
const providerId = rawProviderId.startsWith("opencode-")
? rawProviderId
: `opencode-${rawProviderId}`;
const displayName =
opts?.displayName ??
(providerId === OMNIROUTE_PROVIDER_KEY ? "OmniRoute" : `OmniRoute (${providerId})`);
(providerId === `opencode-${OMNIROUTE_PROVIDER_KEY}`
? "OmniRoute"
: `OmniRoute (${providerId})`);
const modelCacheTtl =
typeof opts?.modelCacheTtl === "number" && opts.modelCacheTtl > 0
? opts.modelCacheTtl

View File

@@ -13,12 +13,12 @@ import { createOmniRouteAuthHook } from "../src/index.js";
test("createOmniRouteAuthHook: default providerId is 'omniroute'", () => {
const hook = createOmniRouteAuthHook();
assert.equal(hook.provider, "omniroute");
assert.equal(hook.provider, "opencode-omniroute");
});
test("createOmniRouteAuthHook: custom providerId binds to hook.provider (multi-instance)", () => {
const hook = createOmniRouteAuthHook({ providerId: "omniroute-preprod" });
assert.equal(hook.provider, "omniroute-preprod");
assert.equal(hook.provider, "opencode-omniroute-preprod");
});
test("createOmniRouteAuthHook: methods[0] is type 'api' with label including displayName", () => {
@@ -30,7 +30,7 @@ test("createOmniRouteAuthHook: methods[0] is type 'api' with label including dis
assert.equal(m.label, "OmniRoute API Key");
const custom = createOmniRouteAuthHook({ providerId: "omniroute-preprod" });
assert.equal(custom.methods[0].label, "OmniRoute (omniroute-preprod) API Key");
assert.equal(custom.methods[0].label, "OmniRoute (opencode-omniroute-preprod) API Key");
});
test("createOmniRouteAuthHook: prompts[0] uses key='apiKey' per @opencode-ai/plugin contract", () => {

View File

@@ -447,14 +447,14 @@ test("models() returns combo entries merged into the map", async () => {
// 3 raw models + 1 combo = 4 entries
assert.equal(Object.keys(out).length, 4);
assert.ok(out["omniroute/claude-primary"]);
assert.ok(out["omniroute/claude-secondary"]);
assert.ok(out["omniroute/gemini-3-flash"]);
assert.ok(out["omniroute/claude-tier"]);
assert.ok(out["opencode-omniroute/claude-primary"]);
assert.ok(out["opencode-omniroute/claude-secondary"]);
assert.ok(out["opencode-omniroute/gemini-3-flash"]);
assert.ok(out["opencode-omniroute/claude-tier"]);
const combo = out["omniroute/claude-tier"];
const combo = out["opencode-omniroute/claude-tier"];
assert.equal(combo.name, "Claude Tier");
assert.equal(combo.providerID, "omniroute");
assert.equal(combo.providerID, "opencode-omniroute");
// LCD over claude-primary (200k, reasoning) + claude-secondary (100k, no reasoning)
assert.equal(combo.limit.context, 100_000);
assert.equal(combo.capabilities.reasoning, false);
@@ -478,11 +478,11 @@ test("models(): combo with unknown member ids degrades to all-false LCD posture"
{ fetcher: modelsFetcher, combosFetcher }
);
const out = await hook.models!({} as never, { auth: apiAuth("sk-z") as never });
assert.ok(out["omniroute/phantom-combo"]);
assert.ok(out["opencode-omniroute/phantom-combo"]);
// With zero resolvable members, LCD = all-false (defensive posture).
assert.equal(out["omniroute/phantom-combo"].capabilities.toolcall, false);
assert.equal(out["omniroute/phantom-combo"].capabilities.reasoning, false);
assert.equal(out["omniroute/phantom-combo"].limit.context, 0);
assert.equal(out["opencode-omniroute/phantom-combo"].capabilities.toolcall, false);
assert.equal(out["opencode-omniroute/phantom-combo"].capabilities.reasoning, false);
assert.equal(out["opencode-omniroute/phantom-combo"].limit.context, 0);
});
test("models(): hidden combos are excluded from the map", async () => {
@@ -505,8 +505,8 @@ test("models(): hidden combos are excluded from the map", async () => {
{ fetcher: modelsFetcher, combosFetcher }
);
const out = await hook.models!({} as never, { auth: apiAuth("sk-z") as never });
assert.ok(out["omniroute/visible"]);
assert.ok(!out["omniroute/hidden"], "hidden combo must be omitted");
assert.ok(out["opencode-omniroute/visible"]);
assert.ok(!out["opencode-omniroute/hidden"], "hidden combo must be omitted");
});
test("models(): combo name exactly matches raw model id → raw deleted, raw deleted, no warn", async () => {
@@ -530,8 +530,8 @@ test("models(): combo name exactly matches raw model id → raw deleted, raw del
});
// Raw model replaced by combo of the same key; combo now lives at the bare slug.
assert.ok(out["omniroute/claude-primary"], "combo surfaces under prefixed key");
assert.equal(out["omniroute/claude-primary"].name, "claude-primary");
assert.ok(out["opencode-omniroute/claude-primary"], "combo surfaces under prefixed key");
assert.equal(out["opencode-omniroute/claude-primary"].name, "claude-primary");
// No collision warning fires — dedup makes keys disjoint.
const collisionWarns = warnings.filter((w) => {
@@ -565,8 +565,8 @@ test("models(): two combos with same slug → second gets disambiguator suffix",
const out = await hook.models!({} as never, { auth: apiAuth("sk-z") as never });
// First combo gets the bare slug; second gets disambiguated.
assert.ok(out["omniroute/claude"], "first combo at prefixed slug");
assert.ok(out["omniroute/claude-uuid"], "second combo disambiguated by id prefix");
assert.ok(out["opencode-omniroute/claude"], "first combo at prefixed slug");
assert.ok(out["opencode-omniroute/claude-uuid"], "second combo disambiguated by id prefix");
});
test("models(): combos fetch fails → falls back to models-only, warn emitted, no throw", async () => {
@@ -583,8 +583,8 @@ test("models(): combos fetch fails → falls back to models-only, warn emitted,
// Catalog includes the models but NOT any combo entries.
assert.equal(Object.keys(out).length, 2);
assert.ok(out["omniroute/claude-primary"]);
assert.ok(out["omniroute/claude-secondary"]);
assert.ok(out["opencode-omniroute/claude-primary"]);
assert.ok(out["opencode-omniroute/claude-secondary"]);
// Soft-fail warning surfaced.
const softFail = warnings.find((w) => {
@@ -609,7 +609,7 @@ test("models(): combos cached + reused within TTL (one combo fetch per TTL windo
const second = await hook.models!({} as never, { auth: apiAuth("sk-z") as never });
assert.equal(combosFetcher.callCount(), 1, "combos fetched only once within TTL");
assert.equal(modelsFetcher.callCount(), 1, "models fetched only once within TTL");
assert.ok(second["omniroute/claude-tier"]);
assert.ok(second["opencode-omniroute/claude-tier"]);
});
test("models(): combos refetched after TTL expiry (same key as models)", async () => {
@@ -701,7 +701,7 @@ test("models(): nested combo-ref context is the min of nested + raw members", as
{ fetcher: modelsFetcher, combosFetcher }
);
const out = await hook.models!({} as never, { auth: apiAuth("sk-z") as never });
const masterLight = out["omniroute/master-light"];
const masterLight = out["opencode-omniroute/master-light"];
assert.ok(masterLight, "MASTER-LIGHT entry must exist");
assert.equal(
masterLight.limit.context,

View File

@@ -203,7 +203,7 @@ function makeInput(initialProvider: Record<string, unknown> = {}): Config {
test("config: with valid auth.json + apiKey + baseURL → mutates input.provider[id] with stripped models block", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test-1", baseURL: "https://or.example.com/v1" },
"opencode-omniroute": { type: "api", key: "sk-test-1", baseURL: "https://or.example.com/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE, MODEL_GEMINI]);
const combosFetcher = stubCombosFetcher([COMBO_CLAUDE_TIER]);
@@ -217,8 +217,8 @@ test("config: with valid auth.json + apiKey + baseURL → mutates input.provider
await hook(input);
const provider = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider;
const entry = provider.omniroute;
assert.ok(entry, "input.provider.omniroute set");
const entry = provider["opencode-omniroute"];
assert.ok(entry, "input.provider['opencode-omniroute'] set");
assert.equal(entry.npm, "@ai-sdk/openai-compatible");
assert.equal(entry.name, "OmniRoute");
assert.equal(entry.options.baseURL, "https://or.example.com/v1");
@@ -227,7 +227,7 @@ test("config: with valid auth.json + apiKey + baseURL → mutates input.provider
// Stripped per-model shape: name + cap flags + modalities + (optional)
// cost. OC's SDK static schema accepts only `limit.{context,output}` —
// `limit.input` is NOT in the SDK shape and gets dropped silently.
const claude = entry.models["omniroute/claude-sonnet-4-6"];
const claude = entry.models["opencode-omniroute/claude-sonnet-4-6"];
assert.ok(claude, "claude model surfaced");
assert.equal(claude.name, "claude-sonnet-4-6");
assert.equal(claude.attachment, true);
@@ -248,7 +248,7 @@ test("config: with valid auth.json + apiKey + baseURL → mutates input.provider
// Combo surfaces under bare key + LCD'd
// (gemini's reasoning=false → combo reasoning=false).
const combo = entry.models["omniroute/claude-tier"];
const combo = entry.models["opencode-omniroute/claude-tier"];
assert.ok(combo, "combo surfaced under bare key");
assert.equal(combo.name, "Claude Tier");
assert.equal(combo.reasoning, false, "LCD: any member reasoning=false → combo reasoning=false");
@@ -323,7 +323,7 @@ test("config: existing input.provider[id] → no overwrite (respect manual overr
models: { "manual-model": { name: "manual-model" } },
};
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -333,11 +333,11 @@ test("config: existing input.provider[id] → no overwrite (respect manual overr
{ providerId: "omniroute" },
{ readAuthJson, fetcher, combosFetcher, logger }
);
const input = makeInput({ omniroute: manual });
const input = makeInput({ "opencode-omniroute": manual });
await hook(input);
const provider = (input as { provider: Record<string, unknown> }).provider;
assert.equal(provider.omniroute, manual, "manual override preserved by reference");
assert.equal(provider["opencode-omniroute"], manual, "manual override preserved by reference");
assert.equal(fetcher.callCount(), 0, "no fetch — short-circuited before I/O");
assert.equal(readAuthJson.callCount(), 0, "no auth.json read either");
assert.ok(
@@ -352,7 +352,7 @@ test("config: existing input.provider[id] → no overwrite (respect manual overr
test("config: fetchers throw → warn + emit stub entry with models: {}", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = throwingModelsFetcher();
const combosFetcher = throwingCombosFetcher();
@@ -368,8 +368,9 @@ test("config: fetchers throw → warn + emit stub entry with models: {}", async
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "stub provider entry published even when fetchers fail");
assert.equal(entry.npm, "@ai-sdk/openai-compatible");
assert.deepEqual(entry.models, {}, "models stub is empty object");
@@ -392,7 +393,7 @@ test("config: fetchers throw → warn + emit stub entry with models: {}", async
test("config: combos fetcher throws → emit models-only catalog (no combos in models block)", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE, MODEL_GEMINI]);
const combosFetcher = throwingCombosFetcher();
@@ -405,12 +406,16 @@ test("config: combos fetcher throws → emit models-only catalog (no combos in m
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry);
const ids = Object.keys(entry.models).sort();
assert.deepEqual(ids, ["omniroute/claude-sonnet-4-6", "omniroute/gemini-3-flash"]);
assert.equal(entry.models["omniroute/claude-tier"], undefined, "no combo entry");
assert.deepEqual(ids, [
"opencode-omniroute/claude-sonnet-4-6",
"opencode-omniroute/gemini-3-flash",
]);
assert.equal(entry.models["opencode-omniroute/claude-tier"], undefined, "no combo entry");
assert.ok(
logger.entries.some((e) => String(e[0]).includes("/api/combos fetch failed")),
"combos-fetch breadcrumb emitted"
@@ -423,7 +428,7 @@ test("config: combos fetcher throws → emit models-only catalog (no combos in m
test("config: baseURL from auth.json takes precedence when opts.baseURL absent", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://creds.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://creds.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -437,14 +442,15 @@ test("config: baseURL from auth.json takes precedence when opts.baseURL absent",
await hook(input);
assert.equal(fetcher.callsBy()[0][0], "https://creds.example/v1");
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(entry.options.baseURL, "https://creds.example/v1");
});
test("config: opts.baseURL wins over auth.json's stored baseURL", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://creds.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://creds.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -458,14 +464,15 @@ test("config: opts.baseURL wins over auth.json's stored baseURL", async () => {
await hook(input);
assert.equal(fetcher.callsBy()[0][0], "https://opts.example/v1");
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(entry.options.baseURL, "https://opts.example/v1");
});
test("config: no baseURL resolvable (no opts, no auth.json baseURL) → no-op", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test" }, // NO baseURL on the credential
"opencode-omniroute": { type: "api", key: "sk-test" }, // NO baseURL on the credential
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -493,12 +500,12 @@ test("config: no baseURL resolvable (no opts, no auth.json baseURL) → no-op",
test("config: multi-instance — two plugins with different providerIds publish to their own keys without collision", async () => {
const readAuthJson = stubReadAuthJson({
"omniroute-prod": {
"opencode-omniroute-prod": {
type: "api",
key: "sk-prod",
baseURL: "https://prod.example/v1",
},
"omniroute-preprod": {
"opencode-omniroute-preprod": {
type: "api",
key: "sk-preprod",
baseURL: "https://preprod.example/v1",
@@ -522,15 +529,18 @@ test("config: multi-instance — two plugins with different providerIds publish
await hookB(input);
const provider = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider;
assert.ok(provider["omniroute-prod"], "prod block present");
assert.ok(provider["omniroute-preprod"], "preprod block present");
assert.equal(provider["omniroute-prod"].options.apiKey, "sk-prod");
assert.equal(provider["omniroute-preprod"].options.apiKey, "sk-preprod");
assert.equal(provider["omniroute-prod"].options.baseURL, "https://prod.example/v1");
assert.equal(provider["omniroute-preprod"].options.baseURL, "https://preprod.example/v1");
assert.ok(provider["opencode-omniroute-prod"], "prod block present");
assert.ok(provider["opencode-omniroute-preprod"], "preprod block present");
assert.equal(provider["opencode-omniroute-prod"].options.apiKey, "sk-prod");
assert.equal(provider["opencode-omniroute-preprod"].options.apiKey, "sk-preprod");
assert.equal(provider["opencode-omniroute-prod"].options.baseURL, "https://prod.example/v1");
assert.equal(
provider["opencode-omniroute-preprod"].options.baseURL,
"https://preprod.example/v1"
);
assert.notEqual(
provider["omniroute-prod"],
provider["omniroute-preprod"],
provider["opencode-omniroute-prod"],
provider["opencode-omniroute-preprod"],
"blocks are distinct references"
);
});
@@ -542,7 +552,7 @@ test("config: multi-instance — two plugins with different providerIds publish
test("config + provider share cache: second call uses cached fetch result (single fetch per TTL)", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-shared", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-shared", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([COMBO_CLAUDE_TIER]);
@@ -574,7 +584,7 @@ test("config + provider share cache: second call uses cached fetch result (singl
test("provider → config order also dedupes (cache populated by provider, consumed by config)", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-reverse", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-reverse", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -654,7 +664,7 @@ test("buildStaticProviderEntry: stripped per-model shape matches sibling @omniro
}
// Sanity: claude entry has all expected stripped fields.
const claude = block.models["omniroute/claude-sonnet-4-6"];
const claude = block.models["opencode-omniroute/claude-sonnet-4-6"];
assert.equal(typeof claude.name, "string");
assert.equal(typeof claude.attachment, "boolean");
assert.equal(typeof claude.reasoning, "boolean");
@@ -679,8 +689,8 @@ test("buildStaticProviderEntry: hidden combos are excluded", () => {
"https://or.example/v1",
"sk-test"
);
assert.equal(block.models["omniroute/claude-tier"], undefined);
assert.ok(block.models["omniroute/claude-sonnet-4-6"]);
assert.equal(block.models["opencode-omniroute/claude-tier"], undefined);
assert.ok(block.models["opencode-omniroute/claude-sonnet-4-6"]);
});
// ────────────────────────────────────────────────────────────────────────────
@@ -696,7 +706,7 @@ test("buildStaticProviderEntry: emits modalities.input from raw.input_modalities
"https://or.example/v1",
"sk-test"
);
const claude = block.models["omniroute/claude-sonnet-4-6"];
const claude = block.models["opencode-omniroute/claude-sonnet-4-6"];
assert.deepEqual(claude.modalities?.input, ["text", "image"]);
assert.deepEqual(claude.modalities?.output, ["text"]);
});
@@ -710,7 +720,7 @@ test("buildStaticProviderEntry: never emits limit.input (OC SDK rejects it)", ()
"https://or.example/v1",
"sk-test"
);
const claude = block.models["omniroute/claude-sonnet-4-6"];
const claude = block.models["opencode-omniroute/claude-sonnet-4-6"];
assert.equal((claude.limit as Record<string, unknown>).input, undefined);
assert.equal(typeof claude.limit?.context, "number");
assert.equal(typeof claude.limit?.output, "number");
@@ -738,7 +748,7 @@ test("buildStaticProviderEntry: emits cost when enrichment carries pricing", ()
"sk-test",
enrichment
);
const claude = block.models["omniroute/claude-sonnet-4-6"];
const claude = block.models["opencode-omniroute/claude-sonnet-4-6"];
assert.equal(claude.cost?.input, 3);
assert.equal(claude.cost?.output, 15);
assert.equal(claude.cost?.cache_read, 0.3);
@@ -759,8 +769,8 @@ test("buildStaticProviderEntry: emits release_date when raw carries it; omits wh
"https://or.example/v1",
"sk-test"
);
assert.equal(block.models["omniroute/claude-with-date"].release_date, "2026-02-19");
assert.equal(block.models["omniroute/gemini-3-flash"].release_date, undefined);
assert.equal(block.models["opencode-omniroute/claude-with-date"].release_date, "2026-02-19");
assert.equal(block.models["opencode-omniroute/gemini-3-flash"].release_date, undefined);
});
test("buildStaticProviderEntry: combo modalities = intersection of members (LCD)", () => {
@@ -789,7 +799,7 @@ test("buildStaticProviderEntry: combo modalities = intersection of members (LCD)
"https://or.example/v1",
"sk-test"
);
const combo = block.models["omniroute/mixed-tier"];
const combo = block.models["opencode-omniroute/mixed-tier"];
assert.ok(combo, "combo emitted under slug key");
// claude has text+image, text-only has text → intersection drops image.
assert.deepEqual(combo.modalities?.input, ["text"]);
@@ -813,7 +823,7 @@ test("OmniRoutePlugin factory exposes config hook alongside auth + provider", as
test("config: auth.json entry of wrong type (oauth) → no-op", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "oauth", refresh: "r", access: "a", expires: 0 },
"opencode-omniroute": { type: "oauth", refresh: "r", access: "a", expires: 0 },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -850,7 +860,7 @@ test("config: readAuthJson throws → treat as missing file (silent fallback)",
test("config: initialises input.provider when undefined", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -865,7 +875,7 @@ test("config: initialises input.provider when undefined", async () => {
await hook(input);
const provider = (input as { provider?: Record<string, unknown> }).provider;
assert.ok(provider, "provider bag initialised");
assert.ok(provider!.omniroute);
assert.ok(provider!["opencode-omniroute"]);
});
// ────────────────────────────────────────────────────────────────────────────
@@ -875,7 +885,7 @@ test("config: initialises input.provider when undefined", async () => {
test("config: enrichment fetched + name overlaid on raw-model entries", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE, MODEL_GEMINI]);
const combosFetcher = stubCombosFetcher([COMBO_CLAUDE_TIER]);
@@ -894,19 +904,20 @@ test("config: enrichment fetched + name overlaid on raw-model entries", async ()
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry);
assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6");
assert.equal(entry.models["omniroute/gemini-3-flash"].name, "Gemini 3 Flash");
assert.equal(entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6");
assert.equal(entry.models["opencode-omniroute/gemini-3-flash"].name, "Gemini 3 Flash");
// Combo names still come from /api/combos — enrichment overlay does NOT touch combos.
assert.equal(entry.models["omniroute/claude-tier"].name, "Claude Tier");
assert.equal(entry.models["opencode-omniroute/claude-tier"].name, "Claude Tier");
assert.equal(enrichmentFetcher.callCount(), 1);
});
test("config: features.enrichment=false skips enrichment fetch + keeps raw-id names", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -924,12 +935,13 @@ test("config: features.enrichment=false skips enrichment fetch + keeps raw-id na
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry);
assert.equal(enrichmentFetcher.callCount(), 0, "enrichment fetch suppressed by feature flag");
assert.equal(
entry.models["omniroute/claude-sonnet-4-6"].name,
entry.models["opencode-omniroute/claude-sonnet-4-6"].name,
"claude-sonnet-4-6",
"raw id retained"
);
@@ -937,7 +949,7 @@ test("config: features.enrichment=false skips enrichment fetch + keeps raw-id na
test("config: enrichment fetcher throws → soft-fail (warn + raw-id static catalog)", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -951,11 +963,12 @@ test("config: enrichment fetcher throws → soft-fail (warn + raw-id static cata
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "static block still published on enrichment failure");
assert.equal(
entry.models["omniroute/claude-sonnet-4-6"].name,
entry.models["opencode-omniroute/claude-sonnet-4-6"].name,
"claude-sonnet-4-6",
"raw id retained"
);
@@ -999,7 +1012,7 @@ const MODEL_NV_LLAMA: OmniRouteRawModelEntry = {
test("config: usableOnly=false → no filter (existing behavior)", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CC_OPUS, MODEL_NV_LLAMA]);
const combosFetcher = stubCombosFetcher([]);
@@ -1021,8 +1034,9 @@ test("config: usableOnly=false → no filter (existing behavior)", async () => {
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry.models["cc/claude-opus-4-7"], "claude kept");
assert.ok(entry.models["nvidia/llama-3-70b"], "nvidia kept (filter off)");
assert.equal(providersFetcher.callCount(), 0, "providers fetch not called when feature off");
@@ -1030,7 +1044,7 @@ test("config: usableOnly=false → no filter (existing behavior)", async () => {
test("config: usableOnly=true → drops models for non-usable providers, keeps usable + unknown", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([
MODEL_CC_OPUS,
@@ -1070,8 +1084,9 @@ test("config: usableOnly=true → drops models for non-usable providers, keeps u
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry.models["cc/claude-opus-4-7"], "claude kept (active)");
assert.equal(entry.models["nvidia/llama-3-70b"], undefined, "nvidia dropped (error status)");
assert.ok(entry.models["agentrouter/synthetic-1"], "unknown prefix kept (subtract-filter)");
@@ -1080,7 +1095,7 @@ test("config: usableOnly=true → drops models for non-usable providers, keeps u
test("config: usableOnly=true + providers fetch fails → soft-fail keeps everything", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CC_OPUS, MODEL_NV_LLAMA]);
const combosFetcher = stubCombosFetcher([]);
@@ -1101,8 +1116,9 @@ test("config: usableOnly=true + providers fetch fails → soft-fail keeps everyt
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry.models["cc/claude-opus-4-7"]);
assert.ok(entry.models["nvidia/llama-3-70b"], "soft-fail keeps both");
assert.ok(
@@ -1113,7 +1129,7 @@ test("config: usableOnly=true + providers fetch fails → soft-fail keeps everyt
test("config: diskCache hydrates stale snapshot when /v1/models throws", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = throwingModelsFetcher();
const combosFetcher = stubCombosFetcher([]);
@@ -1150,14 +1166,15 @@ test("config: diskCache hydrates stale snapshot when /v1/models throws", async (
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(
entry.models["omniroute/claude-sonnet-4-6"],
entry.models["opencode-omniroute/claude-sonnet-4-6"],
"stale snapshot hydrated into static block"
);
assert.equal(
entry.models["omniroute/claude-sonnet-4-6"].name,
entry.models["opencode-omniroute/claude-sonnet-4-6"].name,
"Claude Sonnet 4.6 (cached)",
"stale enrichment also reused"
);
@@ -1170,7 +1187,7 @@ test("config: diskCache hydrates stale snapshot when /v1/models throws", async (
test("config: cached rawEnrichment from earlier provider hook is reused (no refetch)", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-shared", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-shared", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -1202,9 +1219,10 @@ test("config: cached rawEnrichment from earlier provider hook is reused (no refe
await configHook(input);
assert.equal(enrichmentFetcher.callCount(), 1, "config reused cached enrichment");
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6");
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6");
});
// ─────────────────────────────────────────────────────────────────────
@@ -1215,7 +1233,7 @@ test("config: cached rawEnrichment from earlier provider hook is reused (no refe
test("config: providerTag (default-on) prepends '<provider> - ' to enriched raw-model names", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE, MODEL_GEMINI]);
const combosFetcher = stubCombosFetcher([COMBO_CLAUDE_TIER]);
@@ -1250,18 +1268,25 @@ test("config: providerTag (default-on) prepends '<provider> - ' to enriched raw-
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry);
assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6");
assert.equal(entry.models["omniroute/gemini-3-flash"].name, "Gemini-cli - Gemini 3 Flash");
assert.equal(
entry.models["opencode-omniroute/claude-sonnet-4-6"].name,
"Claude - Claude Sonnet 4.6"
);
assert.equal(
entry.models["opencode-omniroute/gemini-3-flash"].name,
"Gemini-cli - Gemini 3 Flash"
);
// Combos stay untouched — `Combo: ` prefix already conveys multi-upstream.
assert.equal(entry.models["omniroute/claude-tier"].name, "Claude Tier");
assert.equal(entry.models["opencode-omniroute/claude-tier"].name, "Claude Tier");
});
test("config: providerTag=false suppresses the suffix", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -1279,10 +1304,11 @@ test("config: providerTag=false suppresses the suffix", async () => {
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(
entry.models["omniroute/claude-sonnet-4-6"].name,
entry.models["opencode-omniroute/claude-sonnet-4-6"].name,
"Claude Sonnet 4.6",
"enriched name kept, provider tag suppressed"
);
@@ -1290,7 +1316,7 @@ test("config: providerTag=false suppresses the suffix", async () => {
test("config: providerTag falls back to UPPER(alias) when providerDisplayName missing", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -1311,14 +1337,15 @@ test("config: providerTag falls back to UPPER(alias) when providerDisplayName mi
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "CC - Claude Sonnet 4.6");
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "CC - Claude Sonnet 4.6");
});
test("config: providerTag skipped entirely when neither providerDisplayName nor providerAlias set", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -1337,14 +1364,15 @@ test("config: providerTag skipped entirely when neither providerDisplayName nor
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6");
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6");
});
test("config: providerTag is idempotent — second hook call doesn't double-suffix", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
@@ -1363,16 +1391,24 @@ test("config: providerTag is idempotent — second hook call doesn't double-suff
const inputA = makeInput();
await hook(inputA);
const entryA = (inputA as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.equal(entryA.models["omniroute/claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6");
const entryA = (inputA as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(
entryA.models["opencode-omniroute/claude-sonnet-4-6"].name,
"Claude - Claude Sonnet 4.6"
);
// Second invocation (cache hit) — name must still be single-suffixed.
const inputB = makeInput();
await hook(inputB);
const entryB = (inputB as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.equal(entryB.models["omniroute/claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6");
const entryB = (inputB as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(
entryB.models["opencode-omniroute/claude-sonnet-4-6"].name,
"Claude - Claude Sonnet 4.6"
);
});
// ────────────────────────────────────────────────────────────────────────────
@@ -1424,7 +1460,7 @@ test("buildStaticProviderEntry: nested combo-ref context is the bottleneck acros
);
// Pre-fix: Parent would advertise 200_000 (only raw-big counted).
// Post-fix: Parent should advertise 8_000 (TinyCombo bottleneck).
const parent = block.models["omniroute/parent"];
const parent = block.models["opencode-omniroute/parent"];
assert.ok(parent, "Parent combo must be in the static catalog");
assert.equal(parent.limit?.context, 8_000);
});

View File

@@ -376,7 +376,7 @@ test("provider hook: enrichment fetcher called when features.enrichment !== fals
);
const out = await hook.models!({} as never, { auth: apiAuth("sk") as never });
assert.equal(called, 1, "enrichment fetcher called once");
const m = out["omniroute/claude-sonnet-4-6"];
const m = out["opencode-omniroute/claude-sonnet-4-6"];
assert.equal(m.name, "Claude Sonnet 4.6", "enrichment name overlay applied");
assert.equal(m.cost.input, 3, "enrichment pricing applied");
assert.equal(m.cost.output, 15);
@@ -401,7 +401,11 @@ test("provider hook: enrichment fetcher NOT called when features.enrichment:fals
);
const out = await hook.models!({} as never, { auth: apiAuth("sk") as never });
assert.equal(called, 0, "enrichment fetcher NOT called when gated off");
assert.equal(out["omniroute/claude-sonnet-4-6"].name, "claude-sonnet-4-6", "raw id preserved");
assert.equal(
out["opencode-omniroute/claude-sonnet-4-6"].name,
"claude-sonnet-4-6",
"raw id preserved"
);
});
test("provider hook: compression metadata fetcher NOT called by default (opt-in)", async () => {
@@ -459,7 +463,7 @@ test("provider hook: compression metadata fetcher called when opted in", async (
);
const out = await hook.models!({} as never, { auth: apiAuth("sk") as never });
assert.equal(called, 1, "compression metadata fetcher called");
const combo = out["omniroute/claude-primary"];
const combo = out["opencode-omniroute/claude-primary"];
assert.ok(combo, "combo entry present");
assert.match(
combo.name,
@@ -473,7 +477,7 @@ test("provider hook: compression metadata fetcher called when opted in", async (
// ─────────────────────────────────────────────────────────────────────────
const stubAuthJson = (apiKey: string) => async () => ({
omniroute: { type: "api" as const, key: apiKey },
"opencode-omniroute": { type: "api" as const, key: apiKey },
});
test("config hook: MCP auto-emit OFF by default (no mcp entry)", async () => {
@@ -488,7 +492,7 @@ test("config hook: MCP auto-emit OFF by default (no mcp entry)", async () => {
);
const input: { provider?: Record<string, unknown>; mcp?: Record<string, unknown> } = {};
await hook(input as never);
assert.ok(input.provider?.omniroute, "provider block written");
assert.ok(input.provider?.["opencode-omniroute"], "provider block written");
assert.equal(input.mcp, undefined, "no mcp block written");
});
@@ -508,7 +512,7 @@ test("config hook: features.mcpAutoEmit:true writes mcp entry with provider apiK
);
const input: { provider?: Record<string, unknown>; mcp?: Record<string, unknown> } = {};
await hook(input as never);
const entry = input.mcp?.omniroute as
const entry = input.mcp?.["opencode-omniroute"] as
| { type: string; url: string; enabled: boolean; headers: Record<string, string> }
| undefined;
assert.ok(entry, "mcp entry written");
@@ -538,7 +542,7 @@ test("config hook: features.mcpToken overrides provider apiKey in mcp Bearer", a
);
const input: { provider?: Record<string, unknown>; mcp?: Record<string, unknown> } = {};
await hook(input as never);
const entry = input.mcp?.omniroute as { headers: Record<string, string> };
const entry = input.mcp?.["opencode-omniroute"] as { headers: Record<string, string> };
assert.equal(
entry.headers.Authorization,
"Bearer sk-mcp-narrower",
@@ -561,11 +565,11 @@ test("config hook: existing operator mcp.<providerId> wins (no overwrite)", asyn
}
);
const input: { provider?: Record<string, unknown>; mcp?: Record<string, unknown> } = {
mcp: { omniroute: { type: "custom-user-entry", url: "https://manual.example/mcp" } },
mcp: { "opencode-omniroute": { type: "custom-user-entry", url: "https://manual.example/mcp" } },
};
await hook(input as never);
assert.deepEqual(
input.mcp?.omniroute,
input.mcp?.["opencode-omniroute"],
{ type: "custom-user-entry", url: "https://manual.example/mcp" },
"operator override preserved"
);
@@ -580,7 +584,7 @@ test("config hook: features.mcpAutoEmit:true with /v1 in baseURL → strips corr
},
{
readAuthJson: async () => ({
"omniroute-preprod": { type: "api" as const, key: "sk-preprod" },
"opencode-omniroute-preprod": { type: "api" as const, key: "sk-preprod" },
}),
fetcher: async () => SAMPLE_RAW,
combosFetcher: async () => [],
@@ -589,7 +593,7 @@ test("config hook: features.mcpAutoEmit:true with /v1 in baseURL → strips corr
);
const input: { provider?: Record<string, unknown>; mcp?: Record<string, unknown> } = {};
await hook(input as never);
const entry = input.mcp?.["omniroute-preprod"] as { url: string };
const entry = input.mcp?.["opencode-omniroute-preprod"] as { url: string };
assert.equal(
entry.url,
"https://or-preprod.example.com/api/mcp/stream",

View File

@@ -38,8 +38,8 @@ test("multi-instance: two plugin invocations bind to their own providerId", asyn
baseURL: "https://b.example/v1",
});
assert.equal(a.auth?.provider, "omniroute-prod");
assert.equal(b.auth?.provider, "omniroute-preprod");
assert.equal(a.auth?.provider, "opencode-omniroute-prod");
assert.equal(b.auth?.provider, "opencode-omniroute-preprod");
});
test("multi-instance: hook objects + nested arrays are independent references", async () => {
@@ -70,8 +70,8 @@ test("multi-instance: identical opts twice still yield independent objects", asy
assert.notEqual(first.auth, second.auth);
assert.notEqual(first.auth?.methods, second.auth?.methods);
// Same provider id is fine — what matters is no shared mutable state.
assert.equal(first.auth?.provider, "twin");
assert.equal(second.auth?.provider, "twin");
assert.equal(first.auth?.provider, "opencode-twin");
assert.equal(second.auth?.provider, "opencode-twin");
});
test("multi-instance: mutating instance A's auth.methods does not affect instance B", async () => {
@@ -132,5 +132,5 @@ test("multi-instance: invalid opts on one instance does not poison the other", a
providerId: "recovered",
baseURL: "https://ok.example/v1",
});
assert.equal(ok.auth?.provider, "recovered");
assert.equal(ok.auth?.provider, "opencode-recovered");
});

View File

@@ -75,7 +75,7 @@ const apiAuth = (key: string, baseURL?: string): unknown =>
test("createOmniRouteProviderHook: default providerId is 'omniroute'", () => {
const hook = createOmniRouteProviderHook(undefined, { combosFetcher: async () => [] });
assert.equal(hook.id, "omniroute");
assert.equal(hook.id, "opencode-omniroute");
});
test("createOmniRouteProviderHook: custom providerId binds to hook.id (multi-instance)", () => {
@@ -87,8 +87,8 @@ test("createOmniRouteProviderHook: custom providerId binds to hook.id (multi-ins
{ providerId: "omniroute-local" },
{ combosFetcher: async () => [] }
);
assert.equal(a.id, "omniroute-preprod");
assert.equal(b.id, "omniroute-local");
assert.equal(a.id, "opencode-omniroute-preprod");
assert.equal(b.id, "opencode-omniroute-local");
});
test("models: extracts apiKey from ctx.auth (type=api) and calls fetcher with it", async () => {
@@ -101,7 +101,7 @@ test("models: extracts apiKey from ctx.auth (type=api) and calls fetcher with it
assert.equal(fetcher.callCount(), 1);
assert.deepEqual(fetcher.callsBy()[0], ["https://or.example.com/v1", "sk-abc"]);
assert.equal(Object.keys(out).length, 3);
assert.ok(out["omniroute/claude-primary"]);
assert.ok(out["opencode-omniroute/claude-primary"]);
});
test("models: returns {} when ctx.auth is null/undefined/wrong-type/empty-key", async () => {
@@ -152,13 +152,13 @@ test("models: maps a sample /v1/models entry to ModelV2 (sanity)", async () => {
{ fetcher, combosFetcher: async () => [] }
);
const out = await hook.models!({} as never, { auth: apiAuth("sk-abc") as never });
const claude = out["omniroute/claude-primary"];
const claude = out["opencode-omniroute/claude-primary"];
assert.ok(claude, "claude-primary present");
// `mapRawModelToModelV2` stamps the provider prefix on the id so OC's
// static-catalog reader resolves `(providerID, modelID)` from the key.
assert.equal(claude.id, "omniroute/claude-primary");
assert.equal(claude.id, "opencode-omniroute/claude-primary");
assert.equal(claude.name, "claude-primary");
assert.equal(claude.providerID, "omniroute");
assert.equal(claude.providerID, "opencode-omniroute");
assert.equal(claude.api.id, "openai-compatible");
assert.equal(claude.api.url, "https://or.example.com/v1");
assert.equal(claude.api.npm, "@ai-sdk/openai-compatible");

View File

@@ -26,7 +26,7 @@ test("scaffold: default export is v1 plugin shape { id, server: OmniRoutePlugin
test("resolveOmniRoutePluginOptions: defaults", () => {
const r = resolveOmniRoutePluginOptions();
assert.equal(r.providerId, "omniroute");
assert.equal(r.providerId, "opencode-omniroute");
assert.equal(r.displayName, "OmniRoute");
assert.equal(r.modelCacheTtl, 300_000);
assert.equal(r.baseURL, undefined);
@@ -34,8 +34,8 @@ test("resolveOmniRoutePluginOptions: defaults", () => {
test("resolveOmniRoutePluginOptions: custom providerId derives displayName", () => {
const r = resolveOmniRoutePluginOptions({ providerId: "omniroute-preprod" });
assert.equal(r.providerId, "omniroute-preprod");
assert.equal(r.displayName, "OmniRoute (omniroute-preprod)");
assert.equal(r.providerId, "opencode-omniroute-preprod");
assert.equal(r.displayName, "OmniRoute (opencode-omniroute-preprod)");
});
test("resolveOmniRoutePluginOptions: explicit displayName wins", () => {