import { Plugin, AuthHook, Config, ProviderHook, tool } from '@opencode-ai/plugin'; import { Model } from '@opencode-ai/sdk/v2'; import { z } from 'zod'; type AutoVariant = "coding" | "fast" | "cheap" | "offline" | "smart" | "lkgp"; type FreeModelFreeType = "recurring-daily" | "recurring-monthly" | "recurring-credit" | "one-time-initial" | "keyless" | "discontinued"; /** * Normalise display name so free-tier models get a consistent `[Free] ` prefix. * * "GPT-4.1 (Free)" → "[Free] GPT-4.1" * "DeepSeek V4 Flash Free" → "[Free] DeepSeek V4 Flash" * "Claude Opus 4.7" → "Claude Opus 4.7" (unchanged) */ declare function normaliseFreeLabel(name: string): string; /** * OpenCode plugin for the OmniRoute AI Gateway. * * Implements the official `@opencode-ai/plugin` Plugin contract (auth + * provider + config hooks) to drive a running OmniRoute instance from * OpenCode without hand-curated `provider..models` blocks in * opencode.json[c]: * * - `auth` — registers `/connect ` flow (API key prompt) * - `provider` — dynamic `/v1/models` fetch with TTL cache, capabilities * pass-through (OmniRoute is the source of truth — no * client-side variant synthesis) * - `config` — backward-compat shim for OC versions that predate the * `provider.models` hook (≤ 1.14.48) * * Two ways to consume the plugin: * * 1. Single-instance (default `providerId: "omniroute"`): * * ```json * { * "$schema": "https://opencode.ai/config.json", * "plugin": ["@omniroute/opencode-plugin"] * } * ``` * * 2. Multi-instance via plugin options (prod + preprod side by side): * * ```json * { * "$schema": "https://opencode.ai/config.json", * "plugin": [ * ["@omniroute/opencode-plugin", { "providerId": "omniroute" }], * ["@omniroute/opencode-plugin", { "providerId": "omniroute-preprod" }] * ] * } * ``` * * Then `opencode connect ` to provision the API key per instance. * * Companion library: `@omniroute/opencode-provider` (build-time config generator) * remains supported for users who can't run plugins (CI, scripted scaffolding). * * @see https://opencode.ai/docs/plugins for the OpenCode plugin contract. * @see https://github.com/diegosouzapw/OmniRoute for the AI Gateway. */ declare const optionsSchema: z.ZodObject<{ providerId: z.ZodOptional; displayName: z.ZodOptional; modelCacheTtl: z.ZodOptional; autoSyncIntervalMs: z.ZodOptional; baseURL: z.ZodOptional; managementReadToken: z.ZodOptional; features: z.ZodOptional; autoCombos: z.ZodOptional; enrichment: z.ZodOptional; compressionMetadata: z.ZodOptional; geminiSanitization: z.ZodOptional; mcpAutoEmit: z.ZodOptional; mcpToken: z.ZodOptional; fetchInterceptor: z.ZodOptional; usableOnly: z.ZodOptional; diskCache: z.ZodOptional; providerTag: z.ZodOptional; debugLog: z.ZodOptional; startupDebug: z.ZodOptional; logLevel: z.ZodOptional>; apiFormat: z.ZodOptional>; }, z.core.$strict>>; }, z.core.$strict>>; }, z.core.$strict>; /** * Plugin options shape — inferred directly from the Zod schema so the * validator and the static type can never drift. Replaces the standalone * interface previously declared here (T-02). Every consumer continues to * import `OmniRoutePluginOptions` as before; only the source of truth * shifted from a hand-written interface to `z.infer`. */ type OmniRoutePluginOptions = z.infer; /** * Explicit default state for every boolean `features.*` toggle. * * #7624: `featuresSchema` marks every flag `.optional()` with no default, and * the effective value is applied implicitly at each read site (default-ON flags * use the `features.X !== false` convention, default-OFF flags use * `features.X === true`). That implicit convention is scattered across the file, * so an operator who omits the `features` block cannot tell whether * combos / autoCombos / enrichment are enabled — they think features are * disabled when they are actually on. Centralising the declared defaults here * (mirroring the read-site conventions exactly, so runtime behaviour is * unchanged) makes the effective flags introspectable and self-documenting. */ declare const OMNIROUTE_FEATURE_DEFAULTS: { readonly combos: true; readonly autoCombos: true; readonly enrichment: true; readonly diskCache: true; readonly providerTag: true; readonly fetchInterceptor: true; readonly geminiSanitization: true; readonly compressionMetadata: false; readonly usableOnly: false; readonly mcpAutoEmit: false; readonly debugLog: false; readonly startupDebug: false; }; /** Union of the boolean feature-flag keys declared in `OMNIROUTE_FEATURE_DEFAULTS`. */ type OmniRouteFeatureFlag = keyof typeof OMNIROUTE_FEATURE_DEFAULTS; /** * Resolve the EFFECTIVE boolean state of every feature toggle, applying the * declared default for any flag the operator omitted. A missing `features` * block (or an empty one) yields the full default set — so callers and the * startup diagnostics can surface exactly which features are active instead of * relying on the implicit `!== false` / `=== true` conventions. Purely * derived: it does not mutate options nor change any read-site behaviour. */ declare function resolveEffectiveFeatureFlags(features?: OmniRoutePluginOptions["features"]): Record; declare const OMNIROUTE_PROVIDER_KEY: "omniroute"; /** Deployed plugin version (injected at build time by tsup define). */ declare const PLUGIN_VERSION: string; /** Deployed plugin git commit hash (injected at build time by tsup define). */ declare const PLUGIN_GIT_HASH: string; declare const DEFAULT_MODEL_CACHE_TTL_MS: 300000; /** Default background auto-discovery interval (matches modelCacheTtl default). */ declare const DEFAULT_AUTO_SYNC_INTERVAL_MS: 300000; /** Minimum positive background auto-discovery interval. */ declare const MIN_AUTO_SYNC_INTERVAL_MS: 60000; /** * Sanitize background auto-sync interval. * - unset/invalid → default 300_000 * - explicit 0 → disabled * - (0, 60000) → clamped to 60000 * - ≥ 60000 → kept as integer ms */ declare function sanitizeAutoSyncIntervalMs(value: unknown): number; /** * Resolve effective options from the optional plugin-options object, * applying defaults. Centralises the providerId fallback so every hook * sees a consistent identifier. */ declare function resolveOmniRoutePluginOptions(opts?: OmniRoutePluginOptions): Required> & { /** * #6859: the UNPREFIXED provider id ("omniroute", "omniroute-preprod", …). * `providerId` above is auto-prefixed with "opencode-" ONLY to satisfy OC * 1.17.8+'s native-adapter gate ({openai, anthropic, opencode*}) — that * prefixed value is OC-internal and must be used ONLY for AuthHook.provider * and provider-registration keys (the OC config-hook top-level * `provider.` block). `omnirouteProviderId` MUST be used everywhere an * identifier reaches or represents something OmniRoute's own server parses * (model `id` prefix, `ModelV2.providerID`, combo catalog keys in the * dynamic provider hook) — OmniRoute's `parseModel()` has no alias for * "opencode-", so a prefixed id there is unrecoverable and credential * lookup fails with "No credentials for opencode-". */ omnirouteProviderId: string; } & Pick; /** Fully resolved plugin options (defaults applied). */ type ResolvedOmniRoutePluginOptions = ReturnType; /** * Strict parse of raw plugin options (as received from opencode.json or a * direct factory call) into the validated `OmniRoutePluginOptions` shape. * * - `null` / `undefined` → `{}` (no opts is valid, defaults take over). * - Unknown keys → throws (strict schema catches typos in opencode.json). * - Empty / malformed values (e.g. empty providerId, non-URL baseURL, * negative modelCacheTtl) → throws. * * Validation happens at plugin invocation time (inside `OmniRoutePlugin`), * NOT at module import — so a bad opencode.json fails the affected plugin * instance with an actionable message instead of crashing the whole TUI on * startup. * * Exported so callers and tests can validate options independent of the * full plugin factory invocation. */ declare function parseOmniRoutePluginOptions(opts: unknown): OmniRoutePluginOptions; /** * Default provider-prefix list that triggers the Anthropic SDK format. * Covers OmniRoute's canonical Anthropic aliases: `cc/`, `claude/`, * `anthropic/`, plus the user-configured `kiro/` and `kr/` upstream * connections that proxy Anthropic models. */ declare const DEFAULT_ANTHROPIC_PREFIXES: string[]; /** * Ensure a baseURL ends with `/v1` so the OpenAI-compat SDK constructs * `/v1/chat/completions` correctly. The Anthropic SDK does NOT want `/v1` * (it appends `/v1/messages` automatically), so callers should branch on * format first. */ declare function ensureV1Suffix(url: string): string; /** * Resolve the API block (id + url + npm package) for a given model id. * * Decision matrix: * - If the model id's prefix (the substring before the first `/`) is in * `apiFormat.anthropicPrefixes` (or the default list), return the * Anthropic SDK block: `id: "anthropic"`, `url: baseURL` (no `/v1`), * `npm: "@ai-sdk/anthropic"`. * - Otherwise return the OpenAI-compat block: `id: "openai-compatible"`, * `url: baseURL + "/v1"`, `npm: "@ai-sdk/openai-compatible"`. * * Combos span multiple providers. Callers should pass each combo member's * id through this function and pick the LCD format (lowest common * denominator that every upstream actually understands). */ declare function resolveApiBlock(modelId: string, baseURL: string, apiFormat?: { anthropicPrefixes?: string[]; }): { id: string; url: string; npm: string; }; /** * Build the AuthHook portion of the plugin for a given options bag. Exported * standalone so the auth contract can be unit-tested without faking the full * PluginInput / Hooks surface. * * Contract notes: * - `provider` binds to `providerId` (NOT a hardcoded module constant — fixes * the multi-instance bug in opencode-omniroute-auth@1.2.1 which pinned * `OMNIROUTE_PROVIDER_ID = "omniroute"` at module scope). * - `methods[0]` is the `api` flavor (no OAuth flow; OmniRoute issues bearer * keys directly). Label includes the resolved displayName so multi-instance * setups stay distinguishable in the OC TUI. * - `methods[0].prompts` uses the official `{type:"text", key, message}` * shape from `@opencode-ai/plugin@1.15.6`. The contract does NOT expose * a `mask: true` flag on text prompts — the OC TUI is expected to handle * credential masking by itself (per OC's `auth login` UX). * - `loader` reads the stored credentials via `getAuth()` and projects them * into the AI-SDK `openai-compatible` options shape (`apiKey`, `baseURL`). * The fetch interceptor (`fetch`) is wired in T-04; left absent here so * downstream code falls back to the SDK default fetch. * - The loader rejects non-`api` auth flavors (oauth / wellknown) and empty * keys by returning `{}` — OC then surfaces the `/connect` flow to the * user instead of dispatching a request with bogus credentials. */ declare function createOmniRouteAuthHook(opts?: OmniRoutePluginOptions): AuthHook; /** * Plugin factory. Returns the OpenCode Plugin object wired with the three * hooks. Concrete hook bodies land in subsequent tickets (T-03 provider.models, * T-04 fetch interceptor, T-06 Gemini sanitization, T-07 config backward-compat). * * Per `@opencode-ai/plugin@1.15.6`, the Plugin signature is * `(input: PluginInput, options?: PluginOptions) => Promise` — opts * arrive as the SECOND argument (from the `[name, opts]` tuple in * opencode.json), NOT as a closure binding. Multi-instance support follows * from each plugin tuple invoking the factory with its own opts. */ /** * Invalidate in-memory fetch cache entries for a baseURL (all credential keys). * Returns number of entries removed. */ declare function invalidateOmniRouteFetchCache(cache: OmniRouteFetchCache, baseURL?: string): number; /** * Resolve API credentials for force-sync / background refresh without * depending on the provider.models auth context. */ declare function resolveOmniRouteRuntimeAuth(resolved: ResolvedOmniRoutePluginOptions, readAuthJson?: OmniRouteReadAuthJson): Promise<{ apiKey: string; baseURL: string; managementReadToken: string; } | null>; /** * Force-refresh OmniRoute catalog: clear memory + disk cache, re-fetch /v1/models * (and optional management endpoints), and repopulate the shared cache. * OpenCode equivalent of Pi `/omni sync`. */ declare function forceSyncOmniRouteModels(args: { resolved: ResolvedOmniRoutePluginOptions; cache: OmniRouteFetchCache; readAuthJson?: OmniRouteReadAuthJson; fetcher?: OmniRouteModelsFetcher; combosFetcher?: OmniRouteCombosFetcher; autoCombosFetcher?: OmniRouteAutoCombosFetcher; enrichmentFetcher?: OmniRouteEnrichmentFetcher; compressionMetaFetcher?: OmniRouteCompressionMetaFetcher; providersFetcher?: OmniRouteProvidersFetcher; now?: () => number; }): Promise<{ ok: boolean; count: number; combos: number; provider: string; baseURL?: string; clearedMemory: number; clearedDisk: boolean; error?: string; }>; declare function createOmniRouteSyncModelsTool(args: { resolved: ResolvedOmniRoutePluginOptions; cache: OmniRouteFetchCache; }): ReturnType; /** * Start background auto-discovery while the harness is running. * Quiet: only logs when the model count changes or on errors. * Returns a stop function. */ declare function startOmniRouteAutoSync(args: { resolved: ResolvedOmniRoutePluginOptions; cache: OmniRouteFetchCache; intervalMs?: number; }): () => void; declare const OmniRoutePlugin: Plugin; /** * v1 plugin shape per OC plugin loader (`packages/opencode/src/plugin/shared.ts:readV1Plugin`). * OC checks the default export for an object with `{id, server}` shape FIRST. * If that fails it falls back to legacy `getLegacyPlugins` which walks every * named export and rejects any non-function value — our package has * constants (OMNIROUTE_PROVIDER_KEY, DEFAULT_MODEL_CACHE_TTL_MS) + types + * schemas as named exports, so the legacy path always fails for us. * * Using v1 shape skips the legacy walk entirely. The `id` field is the * plugin MODULE identifier (one per published package); per-instance * `providerId` still flows through `options.providerId` as before. */ declare const OmniRouteV1Plugin: { id: string; server: Plugin; }; /** * Raw shape of a `/v1/models` entry from OmniRoute. Captured verbatim from * the prod gateway response (sample at /tmp/prod-v1-models.json: 455 entries). * STRICT source-of-truth (OQ-3): every field that lands in ModelV2 traces * back to this shape — no client-side variant synthesis. */ interface OmniRouteRawModelEntry { id: string; object?: string; owned_by?: string; root?: string | null; parent?: string | null; context_length?: number; max_input_tokens?: number; max_output_tokens?: number; input_modalities?: string[]; output_modalities?: string[]; capabilities?: { tool_calling?: boolean; reasoning?: boolean; vision?: boolean; thinking?: boolean; attachment?: boolean; structured_output?: boolean; temperature?: boolean; }; release_date?: string; last_updated?: string; api_format?: string; } /** * Fetcher contract: returns the raw `/v1/models` entry list from a running * OmniRoute instance. Surfaced as a dependency so unit tests can inject a * stub without monkey-patching global `fetch`. * * Why we inline this instead of using `@omniroute/opencode-provider`'s * `fetchLiveModels`: the sibling helper returns a stripped `{id, name, * contextLength?}` shape (see opencode-provider/src/index.ts:480-569) that * drops the `capabilities` / `*_modalities` / `max_*_tokens` blocks T-03 * needs for ModelV2 pass-through. Adopting the sibling here would force a * client-side re-fetch or re-introduce the synthesis we explicitly rejected * in OQ-3. A 30-line raw fetcher is cheaper than mutating the sibling's * stable v0.1.0 contract. */ type OmniRouteModelsFetcher = (baseURL: string, apiKey: string, timeoutMs?: number) => Promise; /** * Default fetcher: `GET /v1/models` with bearer auth + AbortController * timeout. Accepts both the `{object:"list", data:[…]}` envelope OmniRoute * emits today and a bare-array envelope (defensive — keeps the plugin * working if a future OmniRoute build trims the wrapper). Anything that * isn't an object with a string `id` is filtered out silently. */ declare const defaultOmniRouteModelsFetcher: OmniRouteModelsFetcher; /** * Map a raw `/v1/models` entry → `ModelV2` (the type @opencode-ai/sdk/v2 * exports as `Model`, re-exported by @opencode-ai/plugin as `ModelV2`). * * ModelV2 (as of @opencode-ai/sdk@v2 — see node_modules path * `@opencode-ai/sdk/dist/v2/gen/types.gen.d.ts:964-1043`) requires a much * richer shape than the T-03 spec's mapping table assumed. Concretely it * expects: * - flat `id`, `name`, `providerID`, `api: {id,url,npm}` * - nested `capabilities: { temperature, reasoning, attachment, toolcall, * input:{text,audio,image,video,pdf}, output:{…}, interleaved }` * - `cost: { input, output, cache:{read,write} }` (NOT optional) * - `limit: { context, input?, output }` * - `status: "alpha"|"beta"|"deprecated"|"active"`, `options:{}`, `headers:{}` * - `release_date: string` * * Deviations from the T-03 spec (documented per ticket §2 "CRITICAL: Check * the actual ModelV2 type and adapt if field names differ"): * 1. Spec's flat `tool_call` / `reasoning` / `attachment` / `modalities` * top-level fields don't exist in ModelV2 — folded into * `capabilities.{toolcall, reasoning, attachment, input.*, output.*}`. * 2. `cost: undefined` is illegal (cost is required). OmniRoute doesn't * surface pricing on /v1/models, so we emit a zeroed cost block. * Downstream OC reads this for display only — the live pricing is * OmniRoute's responsibility at routing time. * 3. `tool_call` (spec) → `toolcall` (ModelV2 field name; one word). * 4. `attachment` (spec) maps from `capabilities.vision` per OmniRoute * convention: vision = ability to receive image attachments. If the * raw entry happens to expose an explicit `capabilities.attachment` * (some combo entries do), that wins. * 5. `thinking` from OmniRoute has no 1:1 ModelV2 slot. We OR it into * `reasoning` so thinking-only models still surface a non-false * reasoning flag. * 6. `last_updated` from OmniRoute has no ModelV2 slot — dropped (the * spec also flagged this as "may not exist", and the prod sample * confirms it's optional). `release_date` lands in ModelV2.release_date * with `""` fallback (the field is required as `string`). * 7. `temperature: true` per OmniRoute convention (OpenAI-compat mode * always supports the temperature knob). If a raw entry sets * `capabilities.temperature` explicitly, that wins. * 8. Input/output modality arrays: each known modality flips its boolean. * Unknown strings (future OmniRoute additions) are ignored — when the * server adds new modalities we can map them here without breaking * existing entries. * 9. `status: "active"` — OmniRoute doesn't tier models alpha/beta on * /v1/models, and OC needs a non-deprecated status to expose the * model in the picker. If a future entry surfaces an explicit * lifecycle hint we can map it then. * 10. `options: {}` and `headers: {}` left empty — they're escape hatches * for OC users to attach per-model overrides; the provider plugin * must not preempt them. * 11. `limit.input` is OPTIONAL on ModelV2 (the `?` modifier). We only * emit it when OmniRoute supplies `max_input_tokens` — keeps the * shape clean for combo entries that only carry context_length. */ declare function mapRawModelToModelV2(raw: OmniRouteRawModelEntry, ctx: { providerId: string; baseURL: string; apiFormat?: { anthropicPrefixes?: string[]; }; }): Model; /** * Raw shape of a single combo entry as returned by OmniRoute's `/api/combos`. * * Schema established via a live probe against * an OmniRoute `/api/combos` endpoint with a management-scoped key * (response saved at /tmp/t05-combos.json) cross-referenced against the * source-of-truth in this repo: * * - `src/app/api/combos/route.ts` GET handler — emits `{combos: [...]}` * envelope after `getCombos()`. * - `src/lib/db/combos.ts` `getCombos()` — returns rows persisted via * `createCombo` / `updateCombo`, each shaped by `normalizeStoredCombo`. * - `src/lib/combos/steps.ts` `ComboModelStep` + `ComboRefStep` — define * the `models[]` array entry shape (a step references a member model * by its full provider-prefixed id, e.g. `"claude-opus-4-5-thinking"`). * * Note: the preprod gateway returned `{combos: []}` at probe time (no combos * provisioned). The defensive parser accepts both `{combos:[...]}` and a * bare array envelope so the plugin keeps working if a future OmniRoute * build trims the wrapper (mirrors the same pattern in the sibling * `@omniroute/opencode-provider#listCombos`). * * STRICT source-of-truth (OQ-3, per T-03): every ModelV2 field a combo * surfaces traces back to either (a) this raw combo entry or (b) the LCD * roll-up across its raw member models. No client-side variant synthesis. */ interface OmniRouteRawComboMemberRef { /** Step kind: "model" references a raw model id; "combo-ref" nests another combo. */ kind?: "model" | "combo-ref"; /** Full model id referenced by this step (when kind === "model"). */ model?: string; /** Nested combo name (when kind === "combo-ref"). */ comboName?: string; /** Routing weight inside the combo (0–100, advisory at LCD time). */ weight?: number; /** Step-local label, distinct from the parent combo's display name. */ label?: string; } interface OmniRouteRawCombo { id: string; name?: string; /** Routing strategy. Surfaced for forward-compat but not consumed by LCD. */ strategy?: string; /** Member step list. Only `kind: "model"` steps participate in LCD. */ models?: OmniRouteRawComboMemberRef[]; /** Hidden combos are excluded from the OC model picker. */ isHidden?: boolean; /** When OmniRoute attaches a lifecycle hint we forward it; today it doesn't. */ release_date?: string; /** * Server-computed context window for this combo (aggregated from member * models using the same logic as /v1/models). When present, the client * uses this value directly instead of re-aggregating from member models. * * Added in 3.9.x — old servers do not send it. */ computed_context_length?: number; } /** * Fetcher contract for `/api/combos`. Same DI shape as * `OmniRouteModelsFetcher` so unit tests can inject a stub instead of * monkey-patching global `fetch`. */ type OmniRouteCombosFetcher = (baseURL: string, apiKey: string, timeoutMs?: number) => Promise; /** * Default fetcher: `GET /api/combos` with bearer auth + * AbortController timeout. Accepts both the `{combos: [...]}` envelope the * gateway emits today and a bare-array envelope (defensive — keeps the * plugin working if a future OmniRoute build trims the wrapper). * * Differences from `defaultOmniRouteModelsFetcher`: * - URL is `/api/combos`, NOT `/v1/combos`. The `/v1/...` namespace is the * OpenAI-compatible surface (chat completions, models); combo discovery * lives on the management plane under `/api/...`. We tolerate both * `https://host` and `https://host/v1` baseURL forms by stripping the * trailing `/v1` segment before appending `/api/combos`. * - Combos endpoint requires a management-scoped API key when * `REQUIRE_API_KEY` is enabled. We don't enforce that here; the * gateway returns 401/403 with an actionable error which we propagate. * * Anything that isn't an object with a string `id` is filtered out silently. */ declare const defaultOmniRouteCombosFetcher: OmniRouteCombosFetcher; /** * Map a raw combo entry → `ModelV2` by computing the lowest-common-denominator * (LCD) of its underlying member models. The LCD policy is the only way to * surface a single capability vector to OpenCode without lying: if any member * lacks a capability, the combo as a whole cannot guarantee it. * * LCD rules: * - `limit.context` = `min(...members.context_length)`. * - `limit.output` = `min(...members.max_output_tokens)`. * - `limit.input` = `min(...members.max_input_tokens)` ONLY when every * member declares one (ModelV2.limit.input is optional — better to * omit than to fabricate a min over partial data). * - `capabilities.toolcall` / `reasoning` / `attachment` / `temperature`: * `every(member ⇒ supports?)`. The `reasoning` axis ORs across * `reasoning` and `thinking` per member before AND-ing across the * combo (mirrors `mapRawModelToModelV2`). The `attachment` axis ORs * across `attachment` and `vision` per member. The `temperature` axis * uses default-true semantics: a member supports temperature unless * it explicitly declares `temperature: false`. * - `capabilities.input.*` / `output.*`: flattened AND across members' * modality flags. Missing arrays default to `["text"]` (same default * as `mapRawModelToModelV2`). * * Defensive: empty members array → ALL capabilities `false`, limits zero. * That's an intentional safety posture (you can't route through an empty * combo, so OC should grey it out in the picker). * * Spec mapping (T-05 §Scope.3): `cost` zeroed; `status = "active"`; * `release_date = combo.release_date ?? ""`; `api.id = "openai-compatible"`; * `name = combo.name ?? combo.id`. * * @param combo Raw `/api/combos` entry. * @param members Raw `/v1/models` entries for THIS combo's member ids. * Caller resolves `combo.models[].model` ids; unknown ids * are silently dropped before this call. * @param providerId OpenCode provider id (multi-instance aware). * @param baseURL Resolved gateway base URL for ModelV2.api.url. */ declare function mapComboToModelV2(combo: OmniRouteRawCombo, members: OmniRouteRawModelEntry[], providerId: string, baseURL: string, apiFormat?: { anthropicPrefixes?: string[]; }): Model; /** * Raw shape of an auto combo entry as returned by OmniRoute's * `/api/combos/auto` endpoint. Auto combos are virtual — they self-manage * provider selection via scoring/bandit exploration at runtime. */ interface OmniRouteRawAutoCombo { /** Stable id (e.g. "auto", "auto/coding"). */ id: string; /** Human-readable name (e.g. "Auto", "Auto Coding"). */ name: string; /** Variant key or undefined for the default auto. */ variant?: AutoVariant; /** Provider names eligible for this auto combo. */ candidatePool?: string[]; /** Number of candidates resolved at fetch time. */ candidateCount?: number; /** MAX of candidates' context windows, served by newer OmniRoute builds. * Absent on older servers — mapper falls back to a safe positive default. */ context_length?: number; /** MAX of candidates' max output tokens (same provenance as context_length). */ max_output_tokens?: number; /** Whether this auto combo should be hidden from the picker. */ isHidden?: boolean; /** Auto-combo configuration. */ config?: { auto?: { candidatePool?: string[]; explorationRate?: number; routerStrategy?: string; }; }; } /** * Fetcher contract for `/api/combos/auto`. Returns the list of virtual * auto combos the server can create. Same DI pattern as other fetchers. */ type OmniRouteAutoCombosFetcher = (baseURL: string, apiKey: string, timeoutMs?: number) => Promise; /** * Default auto combos fetcher: `GET /api/combos/auto`. * * Fault-tolerant: returns empty array on 404 (endpoint doesn't exist yet) * or any non-2xx / network error. Logs a warning in those cases. */ declare const defaultOmniRouteAutoCombosFetcher: OmniRouteAutoCombosFetcher; /** * Convert a raw auto combo into a static model entry for the OpenCode picker. * Auto combos have tool_call=true, reasoning=true by default (they route * to capable models). Context/output limits come from the server (MAX of * the candidate pool's windows — the gateway's context pre-filter routes * oversized requests to large-window candidates); a safe positive fallback * applies when the server omits them. Never 0. */ declare function mapAutoComboToStaticEntry(autoCombo: OmniRouteRawAutoCombo): OmniRouteStaticModelEntry; /** * Per-model enrichment overlay derived from OmniRoute's * `/api/pricing/models` endpoint. The endpoint returns a per-provider * catalog with curated `name` strings (e.g. `Claude 4.7 Opus`, * `GPT 5.5 Pro`, `Gemini 3.1 Pro`) and per-million-token pricing * (`pricing.input`, `pricing.output`, `pricing.cacheRead`, * `pricing.cacheWrite`). These overlay the ModelV2 entries produced by * `mapRawModelToModelV2`. */ interface OmniRouteEnrichmentEntry { /** Human-readable display name. Replaces ModelV2.name when present. */ name?: string; /** Per-million-token cost overlay onto ModelV2.cost. */ pricing?: { input?: number; output?: number; cacheRead?: number; cacheWrite?: number; }; /** * Provider alias prefix seen in `/v1/models` ids (e.g. `cc`, `gemini`). * Populated by `defaultOmniRouteEnrichmentFetcher` from * `/api/pricing/models` keys. Drives the `usableOnly` alias↔canonical * resolution. */ providerAlias?: string; /** * Canonical provider id used by `/api/providers` connections (e.g. * `claude`, `gemini`, `kiro`). Populated from the per-provider * `entry.id` field inside `/api/pricing/models`. */ providerCanonical?: string; /** * Human-readable upstream provider label (e.g. `Claude`, `Kiro`, * `Windsurf`, `GitHub Models`). Populated from the per-provider * `entry.name` field inside `/api/pricing/models`. Used by the * `providerTag` feature to suffix `ModelV2.name` with the routing * destination so the OC TUI picker can differentiate the same * model id sold through different upstream connections. */ providerDisplayName?: string; /** Free-model budget type (from freeModelCatalog). */ freeType?: FreeModelFreeType; /** Monthly token budget for recurring free models. */ monthlyTokens?: number; /** Credit token budget for credit-based free models. */ creditTokens?: number; } /** Map keyed by full model id (possibly namespaced, e.g. `cc/claude-sonnet-4-6`). */ type OmniRouteEnrichmentMap = Map; type OmniRouteEnrichmentFetcher = (baseURL: string, apiKey: string, timeoutMs?: number) => Promise; /** * Default enrichment fetcher — pulls nice display names from * `GET /api/pricing/models` and merges per-million-token pricing from * `GET /api/pricing` (the actual pricing source — `/api/pricing/models` is * a catalog endpoint whose entries are `{id, name, custom}` only). * * `/api/pricing/models` shape (catalog): * - `{ [providerAlias]: { id, alias, name, models: [{ id, name, custom }] } }` * * `/api/pricing` shape (pricing only): * - `{ [providerAlias]: { [modelId]: { input, output, cached, reasoning, cache_creation } } }` * where values are USD per million tokens. * * The two responses are joined on `(providerAlias, modelId)` and the merged * entries are stored under both `${providerAlias}/${modelId}` and bare * `${modelId}` keys so downstream lookups against either form succeed. * * Soft-fails (returns whatever was collected) on non-2xx or parse errors; * the two fetches are independent so one missing source still surfaces the * other. */ declare const defaultOmniRouteEnrichmentFetcher: OmniRouteEnrichmentFetcher; /** * Separator used by `applyProviderTag` between the upstream provider * label (prefix) and the enriched model name. ASCII hyphen with * surrounding spaces — terminal-safe everywhere, never collides with * a model id (those use slashes / dots / underscores). * * Layout: ` - ` (label leads so column scans * group by provider — e.g. `Claude - Claude Opus 4.7`, * `Kiro - Claude Opus 4.7`). */ declare const PROVIDER_TAG_SEPARATOR = " - "; declare function shortProviderLabel(enrichment: OmniRouteEnrichmentEntry | undefined): string | undefined; /** * Prepend the upstream provider label to `model.name` so the OC TUI * picker can differentiate the same model id sold through different * upstream connections (e.g. `cc/claude-opus-4-7` via Anthropic * vs `kr/claude-opus-4-7` via Kiro). Result shape: * * `