@broberg/ai-sdk 0.61.1 → 0.62.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cost/pricing.ts"],"sourcesContent":["// Versioned per-(provider, model) pricing. F3.6 populates the table + adds tests\n// + MiniMax coverage. F3.1 ships the type + lookup with an empty table, so\n// computeCost returns 0 for every model until F3.6 lands (calls still complete).\nexport interface PricingEntry {\n /** USD per 1M input tokens. */\n inputPer1M: number;\n /** USD per 1M output tokens. */\n outputPer1M: number;\n /** USD per 1M cache-read tokens (falls back to input rate if unset). */\n cacheReadPer1M?: number;\n /** USD per 1M cache-write/creation tokens (falls back to input rate if unset). */\n cacheWritePer1M?: number;\n /** Pricing snapshot version (date or tag) so stale entries are detectable. */\n version: string;\n /** F075 — a second set of rates for prompts LONGER than `aboveTokens` (input +\n * cache-read + cache-write). Claude Haiku 5.5 costs 5x above 100k tokens; one row\n * could only be right for one side of that line. */\n longPrompt?: { aboveTokens: number; inputPer1M: number; outputPer1M: number; cacheReadPer1M?: number; cacheWritePer1M?: number };\n}\n\n// USD per 1M tokens. Anthropic cache multipliers follow the standard model:\n// cache-read ≈ 0.1× input, cache-write ≈ 1.25× input. Verified against the\n// pricing tables in cms (packages/cms-ai/src/providers) + trail (model-lab).\n// MiniMax M2.7 is an estimate pending confirmation against OpenRouter's live\n// price page — flagged in its version string.\nconst V = \"2026-06-02\";\n// Mistral prices come straight from mistral.ai/pricing (per Christian's CD report).\nconst MS = \"2026-06-04-mistral.ai\";\n// Anthropic's own price page, read for F075.\nconst AN = \"2026-10-08-platform.claude.com\";\n\n/** Keyed `${provider}:${model}`. Exported so the catalogue-research job (F014)\n * can enumerate every priced entry and diff it against the live provider lists. */\nexport const PRICING: Record<string, PricingEntry> = {\n // Anthropic (direct API). DEFAULT_TIER_MAP: fast/cheap=haiku, smart/vision=sonnet, powerful=opus.\n // $1.00/$5.00 — Anthropic's published Haiku 4.5 rate, confirmed against the\n // claude-api skill's model table 2026-09-03. F048: this row said $0.80/$4.00 while\n // \"openrouter:anthropic/claude-haiku-4.5\" said $1.00/$5.00 — the SAME model at two\n // prices, and the shorter, likelier id carried the wrong one. A consumer pricing\n // 68M haiku tokens (upmetrics' real volume) was told 20% under.\n \"anthropic:claude-haiku-4-5\": {\n inputPer1M: 1.0,\n outputPer1M: 5.0,\n cacheReadPer1M: 0.1,\n cacheWritePer1M: 1.25,\n version: \"2026-09-03\",\n },\n \"anthropic:claude-sonnet-4-6\": {\n inputPer1M: 3.0,\n outputPer1M: 15.0,\n cacheReadPer1M: 0.3,\n cacheWritePer1M: 3.75,\n version: V,\n },\n // $5.00/$25.00 — Anthropic's published Opus 4.8 rate, confirmed against the\n // claude-api skill's model table 2026-09-03.\n //\n // FOUND BY THE CONTRADICTION GUARD ON ITS FIRST RUN, not by a report. This row said\n // $15.00/$75.00 while \"openrouter:anthropic/claude-opus-4.8\" said $5.00/$25.00 —\n // THREE TIMES too high, the opposite direction from the haiku error and a larger\n // multiple. Nobody had noticed, because both numbers looked like answers.\n \"anthropic:claude-opus-4-8\": {\n inputPer1M: 5.0,\n outputPer1M: 25.0,\n cacheReadPer1M: 0.5,\n cacheWritePer1M: 6.25,\n version: \"2026-09-03\",\n },\n\n // F075 — the rest of what Anthropic's /v1/models lists for our key, read 2026-10-08\n // from platform.claude.com/docs/en/about-claude/pricing. Cache-read is NOT 0.1x\n // everywhere: Opus/Sonnet 5.5 are 0.05x and Fable 5.1 is 0.025x. cacheWrite is the\n // 5-minute rate (the only one the SDK writes).\n \"anthropic:claude-fable-5-1\": { inputPer1M: 10, outputPer1M: 50, cacheReadPer1M: 0.25, cacheWritePer1M: 12.5, version: AN },\n \"anthropic:claude-fable-5\": { inputPer1M: 10, outputPer1M: 50, cacheReadPer1M: 1, cacheWritePer1M: 12.5, version: AN },\n \"anthropic:claude-opus-5-5\": { inputPer1M: 4, outputPer1M: 20, cacheReadPer1M: 0.2, cacheWritePer1M: 5, version: AN },\n \"anthropic:claude-opus-5\": { inputPer1M: 5, outputPer1M: 25, cacheReadPer1M: 0.5, cacheWritePer1M: 6.25, version: AN },\n \"anthropic:claude-opus-4-7\": { inputPer1M: 5, outputPer1M: 25, cacheReadPer1M: 0.5, cacheWritePer1M: 6.25, version: AN },\n \"anthropic:claude-opus-4-6\": { inputPer1M: 5, outputPer1M: 25, cacheReadPer1M: 0.5, cacheWritePer1M: 6.25, version: AN },\n \"anthropic:claude-opus-4-5\": { inputPer1M: 5, outputPer1M: 25, cacheReadPer1M: 0.5, cacheWritePer1M: 6.25, version: AN },\n \"anthropic:claude-sonnet-5-5\": { inputPer1M: 2, outputPer1M: 10, cacheReadPer1M: 0.1, cacheWritePer1M: 2.5, version: AN },\n \"anthropic:claude-sonnet-5\": { inputPer1M: 2, outputPer1M: 10, cacheReadPer1M: 0.2, cacheWritePer1M: 2.5, version: AN },\n \"anthropic:claude-sonnet-4-5\": { inputPer1M: 3, outputPer1M: 15, cacheReadPer1M: 0.3, cacheWritePer1M: 3.75, version: AN },\n \"anthropic:claude-haiku-5-5\": {\n inputPer1M: 0.1, outputPer1M: 0.5, cacheReadPer1M: 0.01, cacheWritePer1M: 0.125, version: AN,\n longPrompt: { aboveTokens: 100_000, inputPer1M: 0.5, outputPer1M: 2.5, cacheReadPer1M: 0.05, cacheWritePer1M: 0.625 },\n },\n\n // OpenAI. embedding default tier = text-embedding-3-small (no output tokens).\n // Cached input is 50% of the input rate — $1.25 vs $2.50 (gpt-4o) and $0.075 vs\n // $0.15 (gpt-4o-mini), read from developers.openai.com/api/docs/pricing on\n // 2026-08-27. NOTE IT IS 50%, NOT the 10% Mistral and Gemini charge: assuming one\n // uniform discount across providers would have understated OpenAI's cost by 4x on\n // the cached half. Caching is automatic above 1,024 tokens — no key, no opt-in.\n // The embedding models list NO cached price (embeddings do not cache), so they\n // deliberately get no row here rather than a guessed one.\n \"openai:text-embedding-3-small\": { inputPer1M: 0.02, outputPer1M: 0, version: V },\n \"openai:text-embedding-3-large\": { inputPer1M: 0.13, outputPer1M: 0, version: V },\n \"openai:gpt-4o\": { inputPer1M: 2.5, cacheReadPer1M: 1.25, outputPer1M: 10.0, version: \"2026-08-27-developers.openai.com\" },\n \"openai:gpt-4o-mini\": { inputPer1M: 0.15, cacheReadPer1M: 0.075, outputPer1M: 0.6, version: \"2026-08-27-developers.openai.com\" },\n // Whisper is priced per MINUTE — see MEDIA_PRICING in ./media-pricing.ts. It used to\n // sit here as 0/0 \"so token-based compute never charges it\", and inwardly that worked.\n // Outwardly it did not: getModelPrice(\"whisper-1\") answered `{unit:\"per_1m_tokens\",\n // inputPer1M: 0}` — i.e. \"this model is free\" — because media rows are consulted LAST\n // and a token row was already there. The per-minute price was unreachable by its own\n // id. computeCost still returns 0 for it (no entry) and transcribe still computes its\n // own cost, so nothing about billing changed; only the public answer stopped lying.\n\n // OpenRouter (meta-router — model slugs include the upstream vendor). Slugs use\n // dots (claude-sonnet-4.6) to match OpenRouter's live ids; the dashed forms\n // never matched a real call. Caught by the F014 catalogue research.\n \"openrouter:anthropic/claude-sonnet-4.6\": { inputPer1M: 3.0, outputPer1M: 15.0, version: V },\n // OpenRouter ground-truth $1/$5 — a markup over Anthropic-direct's $0.8/$4\n // (the `anthropic:` entry above). Was masked while the slug used dashes.\n \"openrouter:anthropic/claude-haiku-4.5\": { inputPer1M: 1.0, outputPer1M: 5.0, version: \"2026-06-04\" },\n \"openrouter:google/gemini-2.5-flash\": { inputPer1M: 0.3, outputPer1M: 2.5, version: V },\n // Ground-truth from OpenRouter /api/v1/models, re-read 2026-10-02 (F067.5; was 0.279/1.2).\n \"openrouter:minimax/minimax-m2.7\": {\n inputPer1M: 0.21,\n outputPer1M: 0.84,\n version: \"2026-10-02-openrouter-live\",\n },\n // DeepSeek V4 (CN-hosted — NOT GDPR-safe; non-personal-data workloads only).\n // On 2026-05-22 DeepSeek made the \"75% off\" promo the permanent official price.\n // V4-Pro $0.435/$0.87 is ~34x cheaper than GPT-5.5 on output; flash is cheaper\n // still. Numbers match OpenRouter /api/v1/models 1:1 (no router markup). A strong\n // cheap route for fleet background work once `claude -p` is API-billed (15 Jun).\n // Re-read from OpenRouter /api/v1/models 2026-10-02 (F067.5). These moved three\n // times in a month (Sep 3: pro 1.02, Oct 1: flash 0.042, Oct 2: flash 0.028) — an\n // OpenRouter call books usage.cost from the response anyway; this row is the fallback.\n \"openrouter:deepseek/deepseek-v4-pro\": { inputPer1M: 0.2088, outputPer1M: 0.4176, version: \"2026-10-02-openrouter-live\" },\n \"openrouter:deepseek/deepseek-v4-flash\": { inputPer1M: 0.028, outputPer1M: 0.056, version: \"2026-10-02-openrouter-live\" },\n // DeepSeek DIRECT API (provider \"deepseek\", F030 non-PII secondary). Rates from\n // api-docs.deepseek.com 2026-06-30 ($0.14/$0.28 per 1M; both map to deepseek-v4-flash).\n // `deepseek-chat` (non-thinking) + `deepseek-reasoner` (thinking) DEPRECATE 2026-07-24.\n // (The bare `deepseek-v4-flash` basename is already priced via the openrouter entry\n // above — kept distinct here to avoid a basename collision in the F027 pricing-API.)\n // Verify against a real key when it lands.\n \"deepseek:deepseek-chat\": { inputPer1M: 0.14, outputPer1M: 0.28, version: \"2026-06-30-deepseek-direct\" },\n \"deepseek:deepseek-reasoner\": { inputPer1M: 0.14, outputPer1M: 0.28, version: \"2026-06-30-deepseek-direct\" },\n // F066 — TypeSafe Jev. docs.typesafe.ai/models, read 2026-10-01: \"$42 per billion\n // tokens (input only)\", output free. Priced on the VERSIONED id because the response\n // reports the model that actually ran (\"jev-1.13.0\") even when you ask for\n // \"jev-latest\" — so a future version arrives unpriced and is booked as such, rather\n // than inheriting a number nobody checked for it.\n \"typesafe:jev-1.13.0\": { inputPer1M: 0.042, outputPer1M: 0, version: \"2026-10-01-typesafe-official\" },\n\n // Cached input tokens cost 10% of the input rate — $0.03 vs $0.30 (2.5-flash) and\n // $0.01 vs $0.10 (2.5-flash-lite), read from ai.google.dev/gemini-api/docs/pricing\n // on 2026-08-27 rather than recalled. NB the storage fee on that page ($1/1M\n // tokens/hour) applies to EXPLICIT context caching, where you create a CachedContent\n // object with a TTL. We use IMPLICIT caching, which has no storage charge — so this\n // table is not silently under-billing.\n // Google Gemini (direct). Provider key is \"gemini\" — matches the adapter's\n // usage.provider + the override.provider callers pass. (Image-gen models are\n // priced per-image in the adapter, not here.)\n \"gemini:gemini-2.5-flash\": { inputPer1M: 0.3, cacheReadPer1M: 0.03, outputPer1M: 2.5, version: \"2026-08-27-ai.google.dev\" },\n // flash-lite is the default `video` tier (F019) — cheap native video understanding.\n \"gemini:gemini-2.5-flash-lite\": { inputPer1M: 0.1, cacheReadPer1M: 0.01, outputPer1M: 0.4, version: \"2026-08-27-ai.google.dev\" },\n\n // Vertex AI (F038) — the EU-resident route to the SAME Gemini models, so Google's\n // published Gemini token prices apply. Listed separately because cost lookups key on\n // `provider:model`: without these rows an EU vision/video call would silently log\n // $0, which is worse than no tracking (a confident wrong number).\n \"vertex:gemini-2.5-flash\": { inputPer1M: 0.3, cacheReadPer1M: 0.03, outputPer1M: 2.5, version: \"2026-08-27-ai.google.dev\" },\n \"vertex:gemini-2.5-flash-lite\": { inputPer1M: 0.1, cacheReadPer1M: 0.01, outputPer1M: 0.4, version: \"2026-08-27-ai.google.dev\" },\n\n // Cached prompt tokens bill at 10% of the input rate (F039, measured 2026-08-27:\n // an 8,810-token prefix reported 8,784 cached on the second call WITH a\n // prompt_cache_key, and 0 without one at every size up to 57k).\n // Mistral (direct, La Plateforme). Official prices from mistral.ai/pricing\n // (2026-06-04, per Christian's CD report). EU/Paris-hosted — the designated\n // GDPR-safe provider for client/personal-data workloads (see F015). NB:\n // medium-3.5 is the premium \"Vibe\" coding tier ($1.5/$7.5); Large 3 ($0.5/$1.5)\n // is the cheaper frontier general-purpose model despite the higher number.\n \"mistral:mistral-large-latest\": { inputPer1M: 0.5, cacheReadPer1M: 0.05, outputPer1M: 1.5, version: MS },\n \"mistral:mistral-large-2512\": { inputPer1M: 0.5, cacheReadPer1M: 0.05, outputPer1M: 1.5, version: MS },\n // Large 4 (F074.1), measured 2026-10-08 on docs.mistral.ai/models/mistral-large-4:\n // LIST price. The page also shows a 50% \"Public Preview\" sale ($0.68/$0.07/$2.09)\n // with no end date; booking the sale would under-report the day it ends, unseen.\n // Cached input shows as \"$0.14\": that is 10% of $1.36 rounded to cents (the sale's\n // \"$0.07\" is 0.068 the same way), and 10% is the rate measured for Mistral (F039).\n \"mistral:mistral-large-4\": { inputPer1M: 1.36, cacheReadPer1M: 0.136, outputPer1M: 4.18, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-large-4-0\": { inputPer1M: 1.36, cacheReadPer1M: 0.136, outputPer1M: 4.18, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-medium-latest\": { inputPer1M: 1.5, cacheReadPer1M: 0.15, outputPer1M: 7.5, version: MS },\n \"mistral:mistral-medium-3.5\": { inputPer1M: 1.5, cacheReadPer1M: 0.15, outputPer1M: 7.5, version: MS },\n // F076.2 — Mistral's model API now lists mistral-medium-3 as an alias of Medium 3.5\n // (measured 2026-10-08), so it bills at 3.5's price, not Medium 3's $0.40/$2.00.\n \"mistral:mistral-medium-3\": { inputPer1M: 1.5, cacheReadPer1M: 0.15, outputPer1M: 7.5, version: \"2026-10-08-docs.mistral.ai\" },\n // F076.1 — Small 4 (2603) is $0.15/$0.60, measured 2026-10-08 on\n // docs.mistral.ai/models/mistral-small-4-0-26-03 AND OpenRouter's mistral-small-2603.\n // This row said $0.10/$0.30 (Small 3's price, carried over when -latest moved to\n // 2603), so the fleet's fast/cheap tiers were booked at about half their cost.\n \"mistral:mistral-small-latest\": { inputPer1M: 0.15, cacheReadPer1M: 0.015, outputPer1M: 0.6, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-small-2603\": { inputPer1M: 0.15, cacheReadPer1M: 0.015, outputPer1M: 0.6, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:ministral-3b-latest\": { inputPer1M: 0.1, cacheReadPer1M: 0.01, outputPer1M: 0.1, version: MS },\n \"mistral:ministral-8b-latest\": { inputPer1M: 0.15, cacheReadPer1M: 0.015, outputPer1M: 0.15, version: MS },\n \"mistral:ministral-14b-latest\": { inputPer1M: 0.2, cacheReadPer1M: 0.02, outputPer1M: 0.2, version: MS },\n // F076.2 — both Magistral aliases now point at the unified models (measured\n // 2026-10-08 via /v1/models): magistral-medium-latest → Medium 3.5, magistral-small-\n // latest → Small 4. They bill at those models' prices, not the old Magistral rates.\n \"mistral:magistral-medium-latest\": { inputPer1M: 1.5, cacheReadPer1M: 0.15, outputPer1M: 7.5, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:magistral-small-latest\": { inputPer1M: 0.15, cacheReadPer1M: 0.015, outputPer1M: 0.6, version: \"2026-10-08-docs.mistral.ai\" },\n // F076.2 — ids Mistral's model API lists for our key that had no row (a call to any\n // of them was booked at $0). Prices from docs.mistral.ai, 2026-10-08; cache-read at\n // the measured 10% (F039).\n \"mistral:mistral-vibe-cli-fast\": { inputPer1M: 0.15, cacheReadPer1M: 0.015, outputPer1M: 0.6, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-medium-3-5\": { inputPer1M: 1.5, cacheReadPer1M: 0.15, outputPer1M: 7.5, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-medium\": { inputPer1M: 1.5, cacheReadPer1M: 0.15, outputPer1M: 7.5, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-medium-2604\": { inputPer1M: 1.5, cacheReadPer1M: 0.15, outputPer1M: 7.5, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-vibe-cli-latest\": { inputPer1M: 1.5, cacheReadPer1M: 0.15, outputPer1M: 7.5, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-vibe-cli-with-tools\": { inputPer1M: 1.5, cacheReadPer1M: 0.15, outputPer1M: 7.5, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:ministral-14b-2512\": { inputPer1M: 0.2, cacheReadPer1M: 0.02, outputPer1M: 0.2, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:ministral-8b-2512\": { inputPer1M: 0.15, cacheReadPer1M: 0.015, outputPer1M: 0.15, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:ministral-3b-2512\": { inputPer1M: 0.1, cacheReadPer1M: 0.01, outputPer1M: 0.1, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:voxtral-small-2507\": { inputPer1M: 0.1, cacheReadPer1M: 0.01, outputPer1M: 0.4, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:voxtral-small-latest\": { inputPer1M: 0.1, cacheReadPer1M: 0.01, outputPer1M: 0.4, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:codestral-2508\": { inputPer1M: 0.3, cacheReadPer1M: 0.03, outputPer1M: 0.9, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-code-latest\": { inputPer1M: 0.3, cacheReadPer1M: 0.03, outputPer1M: 0.9, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:mistral-code-fim-latest\": { inputPer1M: 0.3, cacheReadPer1M: 0.03, outputPer1M: 0.9, version: \"2026-10-08-docs.mistral.ai\" },\n \"mistral:devstral-latest\": { inputPer1M: 0.4, cacheReadPer1M: 0.04, outputPer1M: 2.0, version: MS },\n \"mistral:codestral-latest\": { inputPer1M: 0.3, cacheReadPer1M: 0.03, outputPer1M: 0.9, version: MS },\n \"mistral:open-mistral-nemo\": { inputPer1M: 0.15, cacheReadPer1M: 0.015, outputPer1M: 0.15, version: MS },\n // Moderation (F016.4) — per input token; output 0. (OCR is per-page in the adapter.)\n \"mistral:mistral-moderation-latest\": { inputPer1M: 0.1, cacheReadPer1M: 0.01, outputPer1M: 0, version: MS },\n \"mistral:mistral-moderation-2603\": { inputPer1M: 0.1, cacheReadPer1M: 0.01, outputPer1M: 0, version: MS }, // F077.4 pinned default\n // Embeddings (F016.5) — per input token.\n \"mistral:mistral-embed\": { inputPer1M: 0.1, cacheReadPer1M: 0.01, outputPer1M: 0, version: MS },\n \"mistral:codestral-embed\": { inputPer1M: 0.15, cacheReadPer1M: 0.015, outputPer1M: 0, version: MS },\n // F048 — reported by upmetrics as an unknown model (6 calls). Official mistral.ai/pricing.\n \"mistral:pixtral-large-latest\": { inputPer1M: 2.0, cacheReadPer1M: 0.2, outputPer1M: 6.0, version: \"2026-09-03\" },\n};\n\n/** Strip a provider's dated snapshot suffix: \"claude-haiku-4-5-20251001\" → \"claude-haiku-4-5\".\n * Returns the input unchanged when there is no suffix.\n *\n * ONE definition, used by BOTH lookups. F012 taught this rule to `getPrice` — the\n * internal cost path — and the exported catalogue never learned it, so for two years\n * `usage.costUsd` priced a dated id correctly while `getModelPrice` returned undefined\n * for the same string. Found by upmetrics, who use the catalogue and not our internals:\n * 19,456 calls of `claude-haiku-4-5-20251001` looked like an unknown model. Fixing the\n * instance and not the class is the shape; a copy of this regex in the other lookup\n * would have been the same mistake a third time. */\nexport function stripDatedSuffix(model: string): string {\n return model.replace(/-\\d{8}$/, \"\");\n}\n\nexport function getPrice(provider: string, model: string): PricingEntry | undefined {\n const exact = PRICING[`${provider}:${model}`];\n if (exact) return exact;\n // A real paid call must never be logged as $0 (F012).\n const base = stripDatedSuffix(model);\n if (base !== model) return PRICING[`${provider}:${base}`];\n return undefined;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/routing/tier-map.ts","../src/availability/registry.ts","../src/availability/types.ts","../src/availability/resolve.ts","../src/routing/describe-tier.ts"],"sourcesContent":["// Tier routing: a named Tier resolves to a concrete (provider, model, transport).\n// Precedence is per-call override > client config map > built-in defaults.\nimport type { Tier, TierSpec } from \"../types.js\";\n\n/** Built-in defaults. Every entry is overridable via AiConfig.defaults or a\n * per-call override.\n *\n * F030 — Anthropic API phase-out: `ANTHROPIC_API_KEY` was globally removed, so the\n * default cloud route may NOT hit Anthropic Console. `fast`/`smart`/`powerful`/\n * `vision` now default to **Mistral EU** (Paris-hosted, Schrems II-safe — so every\n * default text/vision call is GDPR-safe by default). Claude stays reachable as a\n * NON-default quality fallback for non-PII via `override:{provider:\"openrouter\",\n * model:\"anthropic/claude-…\"}`. DeepSeek (CN) is the opt-in non-PII secondary\n * (`provider:\"deepseek\"`), never a default. Magistral (reasoning) / mistral-large\n * for vision are per-call overrides, not defaults (don't pay the premium on all). */\n//\n// F077 — PINNED, never \"-latest\" (Christian 2026-10-08: «vi skal ingen steder i flåden\n// tilbyde -latest som default, for så ender vi med at bruge v4 om nogle dage, og\n// regningen følger pludselig efter med en stor overraskelse»). Mistral moves its\n// -latest names on its own schedule (three moved on 2026-10-08 alone), and echoes the\n// alias back so the move is invisible in usage.model. A dated id changes only in a\n// release of ours, with a changelog line and the price difference. The ids below are\n// exactly what each -latest meant on 2026-10-08 — same model, same price.\nexport const DEFAULT_TIER_MAP: Record<Tier, TierSpec> = {\n fast: { provider: \"mistral\", model: \"mistral-small-2603\", transport: \"http\" }, // Small 4\n smart: { provider: \"mistral\", model: \"mistral-large-2512\", transport: \"http\" }, // Large 3\n powerful: { provider: \"mistral\", model: \"mistral-large-2512\", transport: \"http\" }, // Large 3\n cheap: { provider: \"mistral\", model: \"mistral-small-2603\", transport: \"http\" }, // Small 4\n // Vision: small-latest (vision-capable, cheap EU) is the default; override to\n // mistral-large-latest for demanding image/spatial/composition work.\n // F041 — bumped from mistral-small on Christian's ask, and the choice is MEASURED,\n // not assumed from price. On a fine-discrimination test (an 8x10 grid where one cell\n // differs only in its blue channel, 190->150) across 9 cases:\n // mistral-medium-latest 4/9 <- best\n // mistral-small-latest 1/6 + 0/3 = 1/9\n // mistral-large-latest 0/9 <- WORSE than small, despite costing 5x more\n // Large ties small on easy colour blocks (4/4 each) and collapses on subtle ones,\n // so \"bigger is better at vision\" does not hold in Mistral's lineup. Nobody is good\n // at this task; medium is simply the only one that sees anything.\n vision: { provider: \"mistral\", model: \"mistral-medium-2604\", transport: \"http\" }, // Medium 3.5 (was medium-latest, F077)\n // Native video understanding — Gemini leads; flash-lite is the cheap default (F019).\n // NOT Anthropic → out of the F030 phase-out (its own EU epic if/when needed).\n video: { provider: \"gemini\", model: \"gemini-2.5-flash-lite\", transport: \"http\" },\n // NOT Anthropic → out of F030 (EU-embedding migration is its own future epic).\n embedding: { provider: \"openai\", model: \"text-embedding-3-small\", transport: \"http\" },\n};\n\n/**\n * Resolve a Tier to a concrete TierSpec.\n *\n * Merge order (later wins): DEFAULT_TIER_MAP < configMap < override.\n * - `configMap` is the client-level AiConfig.defaults (per-tier full specs).\n * - `override` is a per-call Partial<TierSpec> — only the fields it sets win.\n */\n/** Refuse an override that names a different provider without naming a model (F043.2).\n *\n * Exported and called at EVERY spec merge, not only inside resolveTier. The first\n * version guarded only the six capabilities that route via a tier; image, animate,\n * trainStyle, ocr, moderate, podcast, tts, transcribe and batch merge the override\n * themselves and skipped it entirely — so `ai.image({override:{provider:\"fal\"}})`\n * still posted BFL's flux-2-pro to fal, the exact misleading upstream error this was\n * written to kill. A guard at six of fifteen call sites is a guard you cannot rely on.\n *\n * We REFUSE rather than re-resolve: choosing a model for the new provider would be\n * the SDK making a price decision on the caller's behalf, visible only on the bill.\n *\n * `knownProviders` (when given) lets an UNREGISTERED provider through — that is a\n * typo, and \"no provider adapter registered\" is the useful thing to say about it. */\nexport function assertOverrideProvider(\n base: { provider: string; model: string },\n override: Partial<TierSpec> | undefined,\n label: string,\n knownProviders?: readonly string[],\n): void {\n if (!override?.provider || override.model !== undefined) return;\n if (override.provider === base.provider) return;\n if (knownProviders !== undefined && !knownProviders.includes(override.provider)) return;\n throw new Error(\n `createAI: override sets provider \"${override.provider}\", but \"${label}\" resolves to model ` +\n `\"${base.model}\", which belongs to \"${base.provider}\". Set a model too, e.g. ` +\n `override: { provider: \"${override.provider}\", model: \"<a ${override.provider} model>\" }.`,\n );\n}\n\nexport function resolveTier(\n tier: Tier,\n override?: Partial<TierSpec>,\n configMap?: Partial<Record<Tier, TierSpec>>,\n knownProviders?: readonly string[],\n): TierSpec {\n const base = configMap?.[tier] ?? DEFAULT_TIER_MAP[tier];\n assertOverrideProvider(base, override, tier, knownProviders);\n return { ...base, ...override };\n}\n","// F022 — the model-availability registry: the ONE source both resolveModel()\n// (spawn / call path) and listModels() (UI picker) read. A curated default seed\n// (works offline — the durable floor) plus a mutable overlay that\n// refreshAvailability() updates from the live provider list.\n//\n// Scope note: this is a LIVENESS view (is this id alive right now?), not the\n// rich capability/price inventory (that is F017 src/catalogue). We only track\n// ids we want to assert status on; anything not here is fail-open (treated\n// available) so we never block a model we simply do not track.\nimport { DEFAULT_TIER_MAP } from \"../routing/tier-map.js\";\nimport type { AvailabilityStatus, AvailabilitySource, ModelStatus } from \"./types.js\";\n\n/** Internal registry row. `aliases[0]` surfaces as ModelStatus.alias. */\nexport interface RegistryEntry {\n id: string;\n aliases: string[];\n provider: string;\n available: boolean;\n status: AvailabilityStatus;\n note?: string;\n source: AvailabilitySource;\n}\n\nconst SUSPENDED_FABLE_MYTHOS = \"suspended — US export-control directive (2026-06-12)\";\n\n/** Curated defaults. Aliases here are MODEL-IDENTITY names only (short names for\n * the model itself). TIER aliases — fast/smart/powerful/cheap/vision/video/\n * embedding — are NOT written here: they are derived from DEFAULT_TIER_MAP below,\n * because hand-maintaining them in two places is exactly how they drifted.\n *\n * They HAD drifted, and it crossed the EU border: this list said `smart` was\n * claude-sonnet-4-6 (Anthropic, US) for the ~3 months after F030 pointed the\n * default tiers at Mistral EU, so resolveModel('smart') named a US model while\n * ai.chat({tier:'smart'}) called an EU one. Anyone using resolveModel to SHOW or\n * DECIDE where data goes — the obvious use — got the wrong answer. Measured by\n * fd-sundhed in 0.21.1, still true in 0.28.0, fixed here by deleting the second\n * list rather than correcting it. */\nconst DEFAULTS: RegistryEntry[] = [\n // ── Anthropic ────────────────────────────────────────────────────────────\n { id: \"claude-haiku-4-5\", aliases: [\"haiku\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-sonnet-4-6\", aliases: [\"sonnet\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-opus-4-8\", aliases: [\"opus\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-fable-5\", aliases: [\"fable\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-mythos-5\", aliases: [\"mythos\"], provider: \"anthropic\", available: false, status: \"suspended\", note: SUSPENDED_FABLE_MYTHOS, source: \"default\" },\n // ── Gemini ───────────────────────────────────────────────────────────────\n { id: \"gemini-2.5-flash\", aliases: [\"gemini-flash\"], provider: \"gemini\", available: true, status: \"available\", source: \"default\" },\n { id: \"gemini-2.5-flash-lite\", aliases: [\"gemini-flash-lite\"], provider: \"gemini\", available: true, status: \"available\", source: \"default\" },\n // ── OpenAI ───────────────────────────────────────────────────────────────\n { id: \"text-embedding-3-small\", aliases: [], provider: \"openai\", available: true, status: \"available\", source: \"default\" },\n // ── DeepSeek (CN-hosted — NOT GDPR-safe; non-PII workloads only) ─────────\n //\n // F057.2. These were missing while pricing.ts carried OFFICIAL rates for them\n // and providers/deepseek.ts shipped an adapter (F030.2) — so our own CLAUDE.md\n // instruction (\"if you are GATING, pass requireKnown:true\") refused a route we\n // built, priced and wrote an F-number for. Two tables disagreed, and the gate\n // is the one that decides. Reported by pitch (ref 28546) via components.\n //\n // The note carries the sunset because the registry is what a picker renders, and\n // a row that says only \"available\" would hide it. Both ids were documented to\n // deprecate 2026-07-24 in favour of `deepseek-v4-flash` — we have NOT verified\n // against a live key (no DEEPSEEK_API_KEY here), so this is the curated seed's\n // usual standing: a default, not a measurement. A live refresh may overrule it.\n //\n // `deepseek-v4-flash` on the DIRECT api is deliberately NOT here: it has no\n // price in pricing.ts (measured — getPrice(\"deepseek\",\"deepseek-v4-flash\") is\n // undefined), so a row would pass the gate for a route that silently bills\n // nothing. That is the same green-direction failure this card exists to remove,\n // and it needs a rate from a real source, not a guess. See the plan-doc.\n { id: \"deepseek-chat\", aliases: [], provider: \"deepseek\", available: true, status: \"available\", note: \"direct api.deepseek.com; documented to deprecate 2026-07-24 in favour of deepseek-v4-flash — not live-verified\", source: \"default\" },\n { id: \"deepseek-reasoner\", aliases: [], provider: \"deepseek\", available: true, status: \"available\", note: \"direct api.deepseek.com (thinking); documented to deprecate 2026-07-24 in favour of deepseek-v4-flash — not live-verified\", source: \"default\" },\n // ── TypeSafe (US-hosted — NOT for personal data) ─────────────────────────\n //\n // F066. Two rows, not an alias. TypeSafe's \"jev-latest\" points at jev-1.13.0 TODAY and\n // moves on their next release; writing it here as an alias of 1.13.0 would make this\n // registry assert a mapping we would have to remember to undo. As separate rows, both\n // pass a requireKnown gate and neither claims to be the other.\n { id: \"jev-1.13.0\", aliases: [\"jev\"], provider: \"typesafe\", available: true, status: \"available\", note: \"TypeSafe Jev, pinned version. US-hosted — not for personal data. Decision model: use ai.judge, not ai.chat\", source: \"default\" },\n { id: \"jev-latest\", aliases: [], provider: \"typesafe\", available: true, status: \"available\", note: \"MOVING alias (jev-1.13.0 as of 2026-10-01). Pin jev-1.13.0 if you tuned thresholds on confidence. US-hosted — not for personal data\", source: \"default\" },\n // ── Mistral (EU / GDPR) ──────────────────────────────────────────────────\n { id: \"mistral-large-latest\", aliases: [\"mistral-large\"], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n { id: \"mistral-medium-latest\", aliases: [\"mistral-medium\"], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n { id: \"mistral-small-latest\", aliases: [\"mistral-small\"], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n // F077 — the dated ids the tiers are pinned to. The -latest rows above stay: callers\n // still pass them, and an unknown id would fail a requireKnown gate.\n { id: \"mistral-large-2512\", aliases: [], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n { id: \"mistral-medium-2604\", aliases: [], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n { id: \"mistral-small-2603\", aliases: [], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n];\n\n/** Attach every tier name as an alias of the model that tier ACTUALLY calls.\n * One source: DEFAULT_TIER_MAP decides, the registry follows. A tier whose model\n * is not in the registry is left unaliased on purpose — resolveModel then reports\n * status \"unknown\" (fail-open) instead of inventing a row, and the drift test\n * catches it. */\nexport const TIER_ALIAS_CONFLICTS: string[] = [];\n\nfunction applyTierAliases(entries: RegistryEntry[]): RegistryEntry[] {\n for (const [tier, spec] of Object.entries(DEFAULT_TIER_MAP)) {\n const owner = entries.find((e) => e.id === spec.model && e.provider === spec.provider);\n // A tier name hand-written on any OTHER row is the drift bug returning. It\n // would resolve correctly TODAY only because seed() lets the last write win\n // and the rows happen to be ordered favourably — reorder the array and the\n // tier silently points at the wrong provider again. Record it so a test can\n // fail on it, then strip it so runtime is right regardless.\n for (const e of entries) {\n if (e !== owner && e.aliases.includes(tier)) {\n TIER_ALIAS_CONFLICTS.push(`${tier} is hand-declared on ${e.id} (${e.provider}) but tier ${tier} calls ${spec.model} (${spec.provider})`);\n e.aliases = e.aliases.filter((a) => a !== tier);\n }\n }\n if (owner && !owner.aliases.includes(tier)) owner.aliases.push(tier);\n }\n return entries;\n}\napplyTierAliases(DEFAULTS);\n\n/** The live overlay, keyed by canonical id. Seeded from DEFAULTS (deep-copied so\n * resetting is clean). refreshAvailability() mutates this; resolve/listModels\n * read it synchronously. */\nlet OVERLAY = new Map<string, RegistryEntry>();\n/** alias → canonical id, rebuilt whenever the overlay is seeded. */\nlet ALIAS_INDEX = new Map<string, string>();\n\nfunction seed(): void {\n OVERLAY = new Map(DEFAULTS.map((e) => [e.id, { ...e, aliases: [...e.aliases] }]));\n ALIAS_INDEX = new Map();\n for (const e of DEFAULTS) for (const a of e.aliases) ALIAS_INDEX.set(a, e.id);\n}\nseed();\n\n/** Reset the overlay back to the curated defaults. For tests. */\nexport function resetRegistry(): void {\n seed();\n}\n\n/** Canonical id for a model id OR alias; null when we track neither. */\nexport function canonicalId(requested: string): string | null {\n if (OVERLAY.has(requested)) return requested;\n return ALIAS_INDEX.get(requested) ?? null;\n}\n\n/** The current entry for an id/alias, or undefined when untracked (fail-open). */\nexport function getEntry(requested: string): RegistryEntry | undefined {\n const id = canonicalId(requested);\n return id ? OVERLAY.get(id) : undefined;\n}\n\n/** All tracked entries (optionally provider-scoped), as a public ModelStatus[]. */\nexport function allEntries(provider?: string): ModelStatus[] {\n const rows: ModelStatus[] = [];\n for (const e of OVERLAY.values()) {\n if (provider && e.provider !== provider) continue;\n rows.push({\n id: e.id,\n alias: e.aliases[0],\n provider: e.provider,\n available: e.available,\n status: e.status,\n note: e.note,\n source: e.source,\n });\n }\n return rows;\n}\n\n/** Provider-scoped canonical ids (for refresh reconciliation). */\nexport function providerIds(provider: string): string[] {\n return [...OVERLAY.values()].filter((e) => e.provider === provider).map((e) => e.id);\n}\n\n/** Mark a tracked id available/suspended from a live refresh. No-op if untracked. */\nexport function setAvailability(id: string, available: boolean, note?: string): void {\n const e = OVERLAY.get(id);\n if (!e) return;\n e.available = available;\n e.status = available ? \"available\" : \"suspended\";\n e.source = \"refresh\";\n if (note !== undefined) e.note = note;\n else if (available) e.note = undefined;\n}\n","// F022 — Model Availability Harness. Public types for the availability layer:\n// the shared status read (ModelStatus), the resolve result, and the structured\n// error a caller can flag on. The registry is the one source both the spawn /\n// call path (resolveModel) and UI pickers (listModels) read.\n\nexport type AvailabilityStatus = \"available\" | \"suspended\" | \"unknown\";\n\n/** Where a model's current availability came from: the curated default seed,\n * or a live provider refresh (Anthropic GET /v1/models). */\nexport type AvailabilitySource = \"default\" | \"refresh\";\n\n/** One row of the shared status read — what a UI model-picker renders. */\nexport interface ModelStatus {\n /** Canonical provider model id, e.g. \"claude-fable-5\". */\n id: string;\n /** Short/tier alias, e.g. \"fable\" (the first registered alias). */\n alias?: string;\n /** \"anthropic\" | \"openai\" | \"gemini\" | \"mistral\" | … */\n provider: string;\n available: boolean;\n status: AvailabilityStatus;\n /** Friendly reason, e.g. \"suspended — US export-control directive (2026-06-12)\". */\n note?: string;\n source: AvailabilitySource;\n}\n\n/** Result of resolveModel — the spawn / call path consumes this synchronously. */\nexport interface ResolveResult {\n /** True when the requested model itself is available. */\n ok: boolean;\n /** The id to actually use: `requested` when ok, else the first available fallback. */\n model: string;\n /** What the caller asked for (id or alias, normalized to the canonical id). */\n requested: string;\n provider?: string;\n /** True when `model` differs from `requested` because we fell back. */\n fellBack: boolean;\n status: AvailabilityStatus;\n /** Why it degraded / why it is unavailable. */\n reason?: string;\n /** The registry row's caveat, surfaced on the SUCCESS path too (F057.2).\n *\n * `reason` only ever appears when a model is unavailable, so a caveat written\n * on an AVAILABLE row — \"documented to deprecate 2026-07-24\", \"preview\", a\n * known rate limit — was reachable through listModels() and invisible to the\n * one caller who most needs it: the gate. A consumer passing requireKnown got\n * a clean green with the warning sitting one call away, unread. Carrying it\n * here costs an optional field and makes `ok:true` able to say \"yes, but\". */\n note?: string;\n}\n\n/** Thrown by resolveModel when the requested model is unavailable, no usable\n * fallback exists, and the caller passed `throwIfUnavailable`. Callers flag on\n * `.code === \"model_unavailable\"`. */\nexport class ModelUnavailableError extends Error {\n readonly code = \"model_unavailable\";\n readonly requested: string;\n readonly provider?: string;\n readonly note?: string;\n constructor(requested: string, note?: string, provider?: string) {\n super(`model \"${requested}\" is unavailable${note ? ` (${note})` : \"\"}`);\n this.name = \"ModelUnavailableError\";\n this.requested = requested;\n this.note = note;\n this.provider = provider;\n }\n}\n","// F022 — the synchronous, zero-I/O resolve + status read. This is the spawn /\n// call hot path (buddy's launcher calls resolveModel per spawn, cardmem #4842):\n// it MUST never await and never touch the network. Freshness comes only from a\n// prior async refreshAvailability(); resolve just reads the in-memory registry.\nimport { allEntries, canonicalId, getEntry } from \"./registry.js\";\nimport { ModelUnavailableError } from \"./types.js\";\nimport type { ModelStatus, ResolveResult } from \"./types.js\";\n\nexport interface ResolveOptions {\n /** One id/alias or an ordered chain to try when `requested` is unavailable. */\n fallback?: string | string[];\n /** Scope hint (passed through to the result); does not gate lookup. */\n provider?: string;\n /** Throw ModelUnavailableError instead of returning ok:false when there is no\n * usable fallback. For callers that want to flag rather than degrade. */\n throwIfUnavailable?: boolean;\n /** Treat an id we do not track as UNUSABLE instead of fail-open.\n *\n * Default is fail-open: we never block a model we simply do not track. That is\n * right for liveness, and wrong for a caller who is GATING — cms measured the\n * consequence: resolveModel(\"cheap\") answered {ok:true, model:\"cheap\"}, so a\n * consumer following our own instruction to gate on `ok` passed the gate and\n * then sent the literal string \"cheap\" to a provider as a model id. A\n * success-shaped non-answer is worse than an error, because an error gets\n * handled and a shape does not.\n *\n * Set this when you need \"a model you actually know about\". Off by default so\n * no existing caller changes behaviour. */\n requireKnown?: boolean;\n}\n\n/** The shared status read — UI pickers grey out `available:false` rows. */\nexport function listModels(opts: { provider?: string } = {}): ModelStatus[] {\n return allEntries(opts.provider);\n}\n\n/** Is this id/alias currently usable? Untracked ids are fail-open (true) unless\n * the caller asked for requireKnown. */\nfunction isAvailable(requested: string, requireKnown = false): boolean {\n const e = getEntry(requested);\n if (!e) return !requireKnown; // fail-open on unknown, unless gating\n return e.available;\n}\n\n/**\n * Resolve a requested model (id or alias) to one that is actually usable.\n * Synchronous + offline by contract (cardmem #4842) — reads the registry only.\n *\n * - Available → pass through ({ ok:true, fellBack:false }).\n * - Unavailable + a fallback that IS available → swap ({ ok:false, fellBack:true }).\n * - Unavailable + no usable fallback → throw (throwIfUnavailable) or return ok:false.\n * - Unknown id → treated available (never block a model we do not track).\n */\nexport function resolveModel(requested: string, opts: ResolveOptions = {}): ResolveResult {\n const id = canonicalId(requested) ?? requested;\n const entry = getEntry(requested);\n const provider = opts.provider ?? entry?.provider;\n\n if (isAvailable(requested, opts.requireKnown)) {\n return {\n ok: true,\n model: id,\n requested: id,\n provider,\n fellBack: false,\n status: entry?.status ?? \"unknown\",\n // A green gate that hides a known caveat is the failure this whole card is\n // about, one level up: the answer is shaped like \"fine\" and the warning is\n // somewhere else. If the row has a note, the caller gets it with the yes.\n ...(entry?.note ? { note: entry.note } : {}),\n };\n }\n\n // Requested is suspended — walk the fallback chain for the first available one.\n const chain = opts.fallback === undefined ? [] : Array.isArray(opts.fallback) ? opts.fallback : [opts.fallback];\n for (const fb of chain) {\n if (isAvailable(fb, opts.requireKnown)) {\n const fbId = canonicalId(fb) ?? fb;\n return {\n ok: false,\n model: fbId,\n requested: id,\n provider,\n fellBack: true,\n status: entry?.status ?? \"suspended\",\n reason: entry?.note ?? `${id} is unavailable`,\n };\n }\n }\n\n // No usable fallback.\n const unknownAndGating = !entry && opts.requireKnown;\n const reason = unknownAndGating\n ? `${id} is not a model this registry knows — requireKnown was set, so it is not assumed usable`\n : (entry?.note ?? `${id} is unavailable`);\n if (opts.throwIfUnavailable) {\n throw new ModelUnavailableError(id, reason, provider);\n }\n return {\n ok: false,\n model: id,\n requested: id,\n provider,\n fellBack: false,\n status: entry?.status ?? \"unknown\",\n reason,\n };\n}\n","// F077.2 — \"which concrete version is this tier right now, and what does it cost?\"\n// Asked by trail for its settings page and by the owner for the fleet: a tier name\n// says nothing about the bill, and resolveModel answers the model but not the price.\nimport type { Tier } from \"../types.js\";\nimport { DEFAULT_TIER_MAP } from \"./tier-map.js\";\nimport { getPrice } from \"../cost/pricing.js\";\n\nexport interface TierDescription {\n tier: Tier;\n provider: string;\n model: string;\n /** false when the model is a \"-latest\" alias the provider can move under us. */\n pinned: boolean;\n /** USD per 1M tokens from the pricing table; undefined when the model is unpriced. */\n inputPer1M?: number;\n outputPer1M?: number;\n cacheReadPer1M?: number;\n}\n\n/** The built-in default for a tier. A client's own `defaults` or a per-call\n * `override` can route elsewhere — read usage.model off the response for that. */\nexport function describeTier(tier: Tier): TierDescription {\n const spec = DEFAULT_TIER_MAP[tier];\n if (!spec) throw new Error(`describeTier: unknown tier \"${tier}\" — valid: ${Object.keys(DEFAULT_TIER_MAP).join(\", \")}`);\n const price = getPrice(spec.provider, spec.model);\n return {\n tier,\n provider: spec.provider,\n model: spec.model,\n pinned: !spec.model.endsWith(\"-latest\"),\n ...(price ? { inputPer1M: price.inputPer1M, outputPer1M: price.outputPer1M, ...(price.cacheReadPer1M !== undefined ? { cacheReadPer1M: price.cacheReadPer1M } : {}) } : {}),\n };\n}\n"],"mappings":"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","names":[]}