Repository navigation
Changelog
All notable changes to kosha-discovery (कोश) are recorded here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Entries are ordered newest-first. The public surface is the @sriinnu/kosha-discovery
npm package; the stable JSON contract consumed by Chitragupta and other daemons
is tracked separately via DISCOVERY_SCHEMA_VERSION (v1 as of 0.8.0).
-
kosha's own weekly snapshot is the fallback when the public catalogs are
down. Keyless discovery reads models.dev and LiteLLM; if one is unreachable
and nothing came back, the
snapshot-latestrelease asset — at most a week old, with prices, limits and lifecycle — stands in before the hand-written static lists. Two catalogs that loaded fine and simply do not list a provider is still an honest empty answer. Cards from this path carrycatalogSource: "snapshot";KOSHA_SNAPSHOT_URLpoints forks and air-gapped mirrors at their own copy.
-
The GitHub Action's step could not start in 1.8.0. The package ships two
bins (
kosha,kosha-mcp), sonpx @sriinnu/kosha-discoverycould not pick one; and compositebashsteps run with-e, so a failing gate would have aborted before it was reported. Both fixed in #62. The Marketplace listing points at the repository's latest release, andv1.8.0's tag carries the broken step, so this release exists to put a workingaction.ymlunder "latest". No package code changed.
-
kosha doctor --model a,bscopes the lifecycle check to the models you actually use: aliases resolve, every provider route is checked, and a model no provider lists any more is reported asmissing. Under--cithe gate trips on a missing model, a finding on the model's direct route, or a model whose every route is going away; a reseller retiring one regional copy is reported but does not fail the build. Without--model,doctorstill reports the whole registry — which in CI, against the keyless catalog's 249 deprecated models, always failed. -
A GitHub Action (
sriinnu/kosha-discovery@v1,action.ymlat the repo root) that runs that check on a list of models, writes a table to the job summary and annotates each failing model. This repository dogfoods it weekly on the aliases the README leads with.
-
The docs are mirrored to the GitHub wiki.
docs/stays the source of truth;scripts/build-wiki.mjsregenerates the wiki's reference pages from it on every push tomain, with links between docs rewritten to wiki links. Pages created in the wiki itself (troubleshooting, the release runbook) are seeded once and never overwritten or removed by the sync.
-
Forced tool use on Claude Opus 5.5 and Sonnet 5.5 no longer 400s through the
proxy. Both models reject
tool_choiceany/tool, like Fable 5.1 and Mythos 5.1 before them, but the gate only knew about the Fable line — sotool_choice: "required"was passed straight through. It now degrades toautoplus an instruction, andtool-choiceis no longer advertised as a structured-output mode for those models. -
Keyless Bedrock and Vertex list what those platforms actually serve. With
no AWS / gcloud tooling, both discoverers jumped straight to a hand-written
list (seven models topped by Opus 4.6; five including two retired Gemini
previews) even though models.dev was already mapped for them. The public
catalog now sits between the CLI and the static list, with the real model
creator restored as
originProvider— including Bedrock's geography-prefixed inference profiles (us.anthropic.…,global.openai.…). The static lists themselves are refreshed for the fully-offline case. -
GPT-6, DeepSeek V4 and the unversioned Qwen tiers get correct feature
hints. The family patterns stopped at
gpt-5anddeepseek-v3, so newer IDs fell through to the legacy answer (cl100k_base, no JSON schema, or no tool calling at all). -
A rejected API key no longer removes the provider. When a credentialed
discovery call failed and there was no cache to fall back on, the provider's
entire catalog vanished from routing and pricing — so one expired key, or a
China-region key tried against the international host of a regional pair,
cost you every model that provider serves. It now falls back to the keyless
public catalog, reports the provider as unauthenticated, and still records the
error for
kosha doctor. -
Resellers are no longer credited as model creators. Keyless cards took the
serving provider as
originProvider, so Perplexity's row forclaude-opus-5-5ranked as a direct route beside Anthropic's. The creator is now recovered from the model ID. - The snapshot workflow's run metadata read the outcome of a step that had been
renamed, so it recorded
failureon every run.
-
Bare aliases moved to the current generation:
opus→claude-opus-5-5,sonnet→claude-sonnet-5-5,grok→grok-4.7,gemini-flash→gemini-3.8-flash,gemini-flash-lite→gemini-3.5-flash-lite,qwen-max→qwen3.8-max,qwen-plus→qwen3.7-plus. The previous targets stay reachable through pinned forms (opus-5,sonnet-5,grok-4.6,gemini-flash-2.5,gemini-flash-lite-2.5).nemotron-superpointed at an ID NVIDIA never served and now resolves tonvidia/llama-3.3-nemotron-super-49b-v1.5. -
applyPromoOverridestakes the override table as an optional third argument. The built-in table is empty: its only entry expired in May. -
The weekly snapshot is a release asset now, not a commit. At ~2.8 MB of JSON that grows with every provider added, committing it every Monday would put roughly 150 MB of already-stale data a year into a repo people are meant to clone. It publishes to a
snapshot-latestrelease — a stable download URL that always holds the newest one — plus a datedsnapshot-YYYY-MM-DDpre-release, pruned to the two most recent. Pruning runs only after the new snapshot is published, so an older snapshot is never removed before a newer one exists.data/kosha-latest.jsonis no longer tracked; the health guard now compares against the published asset instead of against git.Existing history still carries the old blobs — only a history rewrite would remove those, which is not worth doing to a published repo.
- Aliases for GPT-6 (
gpt6,gpt6-sol,gpt6-astra,gpt6-luna),opus-5.5,sonnet-5.5,grok-4.7andgemini-embed-2. -
Meta (Muse Spark) as a direct provider —
META_MODEL_API_KEY,api.meta.ai. 46 providers. -
Model cards carry
releaseDateand a models.dev lifecycle status. The catalog already published both; the seed dropped them.deprecatedandbeta/alphanow land onstatus(asdeprecated/preview), so the existing deprecated-route demotion andkosha doctorsee models.dev's view as well as LiteLLM's sunset dates. Live-API cards pick the same fields up from the seed.releaseDateis also on the v1 discovery contract (nullable, additive). -
catalogSource("models.dev"|"litellm") on keyless cards, for telling the two public catalogs apart when they disagree.sourceis unchanged. -
Bedrock rows join cross-provider route lookups.
kosha routes claude-opus-5-5now includesanthropic.claude-opus-5-5and its geography profiles (us.,eu.,global.…), each at its own price. - Snapshot release notes list what changed since the previous snapshot — new
models, repricings, new deprecations, removals (
pnpm snapshot:diff a.json b.json). -
pnpm aliases:check <snapshot.json>reports aliases whose target no provider lists any more, and bare aliases that have a newer sibling in the catalog. The weekly snapshot run annotates itself with the result.
- The
groq-mixtralalias. Groq retiredmixtral-8x7b-32768; the alias resolved to an ID with nothing behind it.
- The README and npm description now lead with what kosha does rather than which category it belongs to. Both opened by restating the package name, which told a passerby nothing — and that matters more than usual here: npm search is download-ranked, so from zero the only discovery paths are the MCP registry and someone landing on the repo, and in both the first sentence is the whole pitch. Released on its own because the npm package page only picks up a description change on publish.
-
The new
bidi_overriderule cost 372 models. It flagged every zero-width formatting character, and OpenAI's own description of GPT-5.2-Codex contains a U+2060 WORD JOINER — which made the scan reject Vercel AI Gateway's entire 375-model payload. Exactly the failure 1.6.0 was released to fix, reintroduced by the rule added alongside it. The rule now names only the controls that actually reorder rendering (U+202A–U+202E, U+2066–U+2069, the trojan-source set). U+200C ZWNJ and U+200D ZWJ are explicitly allowed: they are required for correct Devanagari, Persian, Arabic and emoji rendering, and a registry called कोश has no business calling them attacks. - The published snapshot had been stale since 2026-07-17 while every scheduled run reported success. The commit guard required at least one authenticated provider, no provider secrets are configured on the repo, so the condition was never satisfiable and the job silently skipped its commit every week. The guard now asks whether the snapshot is healthy instead: at least 500 models across at least 10 providers, and not more than 20% thinner than the snapshot already committed. Keyless discovery is not a dry run — it is what most consumers get.
-
The MCP registry publish raced npm's own read API. The registry resolves
the npm version before it will register a manifest, and npm's read API lags its
publish by tens of seconds — so on the 1.6.0 release the step failed with
version '1.6.0' was not found (status: 404)moments after a successful publish, and the registry publish had to be re-run by hand. The release workflow now waits for the version to actually resolve on npm (up to five minutes) before publishing the manifest.
-
One upstream model name was silently disabling every keyless provider
fallback. The
base64threat rule flagged the models.dev catalog keydeepinfra/thinkingmachines/Inkling— 33 characters, mixed case, entirely within the base64 alphabet — and because the scan was all-or-nothing, kosha rejected the whole 222-provider catalog. On a machine without API keys (the common case) that meant NVIDIA, Together, Fireworks, Groq, Mistral, DeepInfra, Cohere, Cerebras, Perplexity, MiniMax and GLM all reported zero models, with nothing indiscoveryErrors()to say why. Live discovery went from 917 models across 23 providers to 2,458 across 34 once fixed. -
Moonshot pointed at the wrong region. The sole
moonshotprovider usedapi.moonshot.cn, the mainland-China host, which an international key cannot authenticate against.moonshotis nowapi.moonshot.aiandmoonshot-cncarries the China host. -
GLM had no keyless fallback at all. models.dev publishes it under the
zhipuaislug, which was missing from the seed map, so keyless GLM discovery returned nothing. - An auth failure was reported as a missing endpoint. The OpenAI-compatible endpoint chain walked past a 401 to a fallback path that never existed and reported that path's 404, hiding every expired-key diagnosis. Only a 404 now advances to the next candidate; a 401/403/429 is reported as-is.
- A provider whose model-list path 404s no longer reports zero models — it falls back to the public catalog, since a gap in kosha's URL knowledge is not the same as a provider having no models.
-
The stale-but-offline path resolved aliases to nothing. The curated
fallback lists predated GPT-5 and still pinned retired
gemini-*-preview-*snapshots, so with no key and no network,kosha model gpt5andkosha model gemini-proresolved to IDs with no card behind them. -
A catalog entry with no
casein the credential resolver silently resolved to no credential. The resolver now derives env vars from the provider descriptor by default, so adding a catalog entry is enough.
-
A separate rate for 1-hour cache writes. Providers that offer more than one cache lifetime charge more for the longer one — Anthropic bills a 5-minute write at 1.25× input and a 1-hour write at 2× — but every catalog we ingest (models.dev, LiteLLM, OpenRouter, Vercel) publishes a single
cache_writerate, which is the short one.ModelPricinggainscacheWrite1hPerMillion,TokenUsagegainscacheWrite1hTokens(read from Anthropic'scache_creation.ephemeral_1h_input_tokens; a subset of the write total, never added to it), and cost reconciliation prices that share at its own rate — falling back to the published 2× input ratio for Anthropic-shaped usage rather than billing a 1-hour write as if it were a 5-minute one. Consumers that cache exclusively at 1 hour, as Claude Code does, were under-counting every cache write by a factor of 1.6. Pricing-anomaly detection watches the new rate too. -
TypeSafe (System One / Jev) as a first-class provider.
GET /v1/modelsonapi.typesafe.ai, credentials fromTYPESAFE_API_KEYorJEV_API_KEY, documented 64k request / 32k state limits, and input-only pricing ($0.042/M in, $0 out — a typed judgment emits no billable completion). Alias:jev. -
New
judgmentmodel mode, plus a matching trusted capability and role. System One models answer a question with a choice, probability, or score rather than generating text, so tagging themchatwould let a router send them prompts they cannot answer. Threaded through the v1 discovery contract, the HTTP mode enum, and the MCP tool schema. -
xAI (Grok) as a direct provider.
api.x.ai/v1,XAI_API_KEY, with Grok Imagine split intoimageandvideomodes. xAI was already referenced as an origin provider and already had seed-catalog slugs wired — the direct provider itself was missing, so those slugs were dead code and no direct Grok route ever appeared inkosha routes. -
Thinking Machines (Inkling). Tinker serves it over an Anthropic-wire endpoint with no model-list path, so it is discovered seed-only. Alias:
inkling. -
Regional provider pairs, priced independently.
alibaba/alibaba-cn,siliconflow/siliconflow-cn,moonshot/moonshot-cn,minimax/minimax-cn,stepfun/stepfun-cn. The same model ID can cost very different amounts per region — Qwen 2.5 72B is $1.40/M international against $0.574/M in China — so merging the regions would make a model's price depend on which host answered last. -
Nine further providers: Alibaba Model Studio (Qwen), Volcengine Ark (Doubao), Inception Labs (Mercury), AI21 (Jamba), Upstage (Solar), Baseten, Nebius Token Factory, Novita AI, SiliconFlow, Hugging Face Inference Providers, Ollama Cloud. 25 providers → 45, counting the regional pairs.
-
control_charsandbidi_overridethreat rules. kosha prints catalog-derived model names straight to a terminal, so an ESC byte in an upstream name could clear the screen, rewrite earlier output, or set the window title — enough to makekosha listshow a different model than the one it routes to. Bidi and zero-width overrides ("trojan source") make text render unlike its bytes. -
excessive_nestingrule. A few megabytes of[[[[…]]]]previously overflowed the stack inside the scanner before any field was read. -
base64 detection now decodes rather than only guessing. Any string in the base64 alphabet is decoded and its plaintext re-scanned for credential, script, and shell patterns, so an encoded key is caught on its contents regardless of character distribution.
-
MCP registry publishing, automated.
server.jsonplusmcpNameinpackage.jsonregister the server asio.github.sriinnu/kosha-discovery, and the release workflow publishes it withmcp-publisher login github-oidc— the job's own OIDC token proves theio.github.sriinnu/*namespace, so there is no interactive login and no secret to rotate. The step runs after the npm publish, because the registry verifies ownership by readingmcpNameout of the published package, and it refuses to publish a manifest whose version does not match the tag. -
quarantineEntries()for the large community catalogs: a tripping entry is dropped and recorded (modelsDevQuarantined(),liteLLMQuarantined()) instead of taking the whole feed down. A feed where more than half the entries trip still fails closed, and a poisoned top-level key is never quarantined.
-
Adding an OpenAI-compatible provider is now a table entry, not a class.
GenericOpenAICompatibleDiscoverertakes the four things that actually vary (base URL, model-list path, origin derivation, which IDs to keep) as data inGENERIC_OPENAI_PROVIDERS. Providers with genuinely non-trivial classification — OpenRouter, Vercel, Groq — keep their own discoverers. - Aliases added:
grok,jev,inkling,qwen-max,qwen-plus,qwen-coder,jamba,solar,mercury,doubao. - GLM additionally accepts
ZHIPU_API_KEY, the name models.dev documents.
-
README and SKILL.md rewritten as accurate, dev-facing references
(were drifted / marketing-toned in places): correct provider count (25),
current model IDs and pricing, the full HTTP and proxy surface, current
ModelCard/LedgerEntryshapes, and a corrected "Adding a provider" walkthrough.kosha --helpnow documentsdoctorandspend, which existed but weren't listed.
-
Two real CodeQL
js/file-system-racefindings.KoshaCache.get()andregistry-runtime.ts's manifest-backup rotation each did a path-based existence/size check followed by a separate path-based read — a window in which the path could be swapped between the two calls. Both now read through a single already-open file descriptor (fstat+ read on the same handle) instead, closing the gap outright rather than re-checking it. - Lock-contention test rewritten to remove the same pattern from its own fixture setup: it now holds one file handle for the whole test and reads before/after snapshots through it at an explicit byte position, rather than reopening the lock file by path — which also makes the assertion strictly stronger (same inode untouched, not just matching bytes at that path).
Security defaults for the proxy, a current-generation Anthropic model layer,
full Anthropic bridging through the OpenAI-compatible proxy (streaming, tools,
images, structured output) with ledger usage reconciliation, MCP parity, and a
toolchain refresh. 929 tests; pnpm audit clean.
-
Proxy: full Anthropic bridging (
src/wire-anthropic.ts). The OpenAI ↔ Anthropic translator now carries streaming (Anthropic SSE →chat.completion.chunkSSE,[DONE], optional usage chunk viastream_options.include_usage),tools/tool_choice/parallel_tool_calls, assistanttool_calls→tool_use,toolrole →tool_result,image_urlparts (URL and base64 data URLs),response_format(json_schema→output_config.formaton Claude 4.5+,json_object→ system instruction),reasoning_effort→output_config.effort(clamped to the target generation's ladder), andmax_completion_tokens. Responses translatetool_useblocks into OpenAItool_calls,refusalintocontent_filter, and cache read / write tokens intoprompt_tokens+prompt_tokens_details.cached_tokens. -
Proxy: usage reconciliation. Ledger rows now record the upstream
usageblock when the provider returns one (Anthropic JSON and SSE, OpenAI-compatible JSON and SSE withinclude_usage) asactualUsd/actualInputTokens/actualOutputTokens/ cache token counts, taggedusageSource: "upstream"; the pre-flight estimate is kept alongside. Budget enforcement andkosha spendprefer the actual figure. New headersx-kosha-actual-cost-usd,x-kosha-usage-source,x-kosha-wire-notes. -
Operator token for the proxy.
KOSHA_PROXY_TOKENgates every/proxy/*route andPOST /api/refresh(Authorization: Bearer <token>orx-kosha-token), constant-time compared.x-kosha-tenantheader as the tenant-tag carrier soAuthorizationis free for the token. -
kosha serve --host <address>andKOSHA_HOST; the server logs a warning when bound to a non-loopback address without a proxy token. -
Anthropic discoverer reads live Models API metadata —
max_input_tokens,max_tokens, and the nestedcapabilitiestree now populate context window, output cap, and capability tags directly instead of waiting for LiteLLM enrichment (capabilityTagsFromAnthropicApi). -
MCP:
kosha_ranked_routestool (cheapest / fastest / reliable / balanced, same filters askosha_cheapest_model);pinghandler; protocol negotiation across2025-06-18,2025-03-26,2024-11-05;serverInfo.versionread frompackage.json; tool failures returned asisErrorresults per spec; JSON-RPC handler exported and unit-tested; stdio loop only starts when the file is the process entry point (symlink-safe). -
Docs:
docs/mcp.md;docs/api.mdnow covers/api/capabilities,/api/discovery-errors,/metrics, and the proxy routes. -
CI: Node 22 + 24 matrix.
-
Ledger adjustment rows. Streaming responses now write their request row (pre-flight estimate) the moment the upstream accepts the call, and a
kind: "adjustment"row with the delta once the stream reports usage.ledgerRowUsd/isRequestRowmake every reader agree;kosha spendcounts requests, not rows. -
KOSHA_TENANT_BUDGET_USD— optional per-tenant monthly cap, applied in addition to the global cap. -
Translator:
user→metadata.user_id;temperatureclamped to Anthropic's 0..1; emptystopsequences dropped;stricttool flag dropped on generations without strict tool use; forcedtool_choicedegraded toautowhen combined withresponse_formatjson_schema;max_tokensclamped to the model's output cap; streamed no-argument tool calls end with"{}"so SDKs canJSON.parse; fields with no Anthropic equivalent (n,seed,logit_bias, penalties, logprobs) reported inx-kosha-wire-notes. -
Shared
src/claude-generation.ts— one parser for Claude IDs (bare,vendor/prefixed, dotted, Bedrock-suffixed) used by both the catalog feature inference and the proxy translator. New exports:parseClaudeGeneration,claudeEffortLadder,claudeSupportsPrefill. -
MCP: stdin EOF now waits for in-flight tool calls before exiting, so one-shot / piped invocations get their answer.
-
Public API notes:
OpenAIChatMessage.contentis nowstring | Array<unknown> | null(assistant tool-call turns carrynull);parseTenantTagis exported and takes an optional secondx-kosha-tenantargument;LedgerEntrygained optional reconciliation fields;kosha spend --jsonreportsreconciledRows. -
Global budget is global.
KOSHA_MONTHLY_BUDGET_USDis always compared against total spend; previously a request carrying a tenant tag was checked only against that tenant's slice, so a fresh tag per request escaped the cap. - Upstream timeout covers headers and non-streaming bodies only. Streamed bodies are no longer cut at 30 s; the shutdown signal and the client disconnect end them.
-
Default aliases track the current Claude generation:
fable→claude-fable-5-1,opus→claude-opus-5,haiku→claude-haiku-4-5(bare ID, not the dated snapshot). New pinsfable-5.1,mythos,opus-5,opus-4.7,sonnet-4.6;fable-5,opus-4,opus-4.8,sonnet-4keep their previous targets.gpt5/gpt5-mini/gpt5-nano/gpt5-pro/gpt4.1added;gemini-pro/gemini-flash/gemini-flash-litemoved from retired 2025 preview IDs to thegemini-2.5-*GA IDs. -
Static Anthropic fallback catalog gains
claude-fable-5-1andclaude-opus-5, uses bare IDs, and carries context / output limits plusreasoning,structured_output,prompt_cachingcapability tags. -
inferStructuredOutputModesfor Anthropic: Claude 4.5+ reportjson-schema(nativeoutput_config.format); Fable / Mythos 5.1 droptool-choicebecause forced tool use returns a 400 on them. -
kosha servebinds127.0.0.1by default (was every interface). Pass--host 0.0.0.0to restore the old behaviour — and setKOSHA_PROXY_TOKEN. /metricstoken comparison is constant-time.-
Toolchain: TypeScript 7, Vitest 5, Biome 2.5.12,
@types/node26.5;hono4.13.7 and@hono/node-server2.1.1; GitHub Actions moved tocheckout@v7,setup-node@v7,upload-artifact@v7,pnpm/action-setup@v6,codeql-action@v4,action-gh-release@v3(Node 20 runner deprecation). -
npm tarball drops
logo.png(3.3 MB of a 3.6 MB package);logo.svgstays. Package size 3.6 MB → 0.3 MB.
- Paid-but-unrecorded requests. Three paths could bill the upstream and leave no ledger row: a client disconnecting mid-stream (the usage promise never settled), a stream longer than 30 s (the body shared the header timeout), and a non-ASCII caller string reflected into a response header (undici rejects it → 500 after the upstream call). The row is now written before any body is relayed, stream transformers settle on cancel, and header values are reduced to printable ASCII.
-
Translator 400s on real generations:
output_config.effortwas sent to Sonnet 4.5 / Haiku 4.5 (only Opus 4.5 has effort); bothtemperatureandtop_pwere forwarded on Claude 4.0 / 4.1 / 4.5 (every 4.x accepts one); a trailing assistant turn was shipped as a prefill to 4.6+ (rejected) and a trailingtool_usewithout its result was shipped to every generation — a minimal user turn is now appended and noted. - Quadratic bearer-token parse on gated routes replaced with a linear one.
- Anthropic SSE parser now caps its buffer (1 MiB) and scans each byte once; a malformed upstream ends the stream with an OpenAI error chunk.
- Negative token counts from a broken upstream are clamped so a reconciled cost can never drive spend below zero.
-
Capability tree keys are looked up as own properties (
constructorno longer resolves to a function); booleansupportedleaves are accepted. - Snapshot workflow artifact no longer includes the tee'd autofetch log (secrets are masked in step logs but not in artifacts).
-
Snapshot workflow never ran.
update-kosha-snapshot.ymlreferencedrunner.tempin a job-levelenv, which GitHub rejects at parse time, so every run (the Monday cron included) failed in 0 s anddata/kosha-latest.jsonfroze at 2026-07-17. Paths are now resolved in a step; provider API keys are passed from repository secrets so the commit guard can pass. -
Anthropic translator sent
temperature/top_pto models that reject them (Opus 4.7+, Sonnet 5, Fable, Mythos → 400). Sampling params are now dropped per generation (and reduced to one on Opus / Sonnet 4.6). - Anthropic translator synthesized an empty user message for system-only or assistant-first conversations, which Anthropic rejects; a non-empty placeholder is used. Trailing whitespace on a final assistant turn is trimmed.
- Cost estimate ignored
max_completion_tokens.
-
hono4.12.28 → 4.13.7: ReDoS in CORS middleware,memo()cross-request SSR leak, language-middleware complexity DoS, proxy-helperConnectionheader handling.@hono/node-server2.0.8 → 2.1.1: unauthenticated memory-leak DoS via aborted WebSocket handshake. Transitivepostcss8.5.x path-traversal advisories cleared via Vite 8.2.2.pnpm auditclean.
-
Tally export (
src/tally.ts) exposed viasrc/index.tsandpackage.jsonconditional export"./tally". Pure, zero-dependency token-usage normalization + USD cost aggregation for browser/edge consumers. -
Moonshot aliases —
kimiandkimi-k3resolve tokimi-k3.
-
Updated default Anthropic aliases (
src/aliases.ts) to current model IDs:-
opus/opus-4/opus-4.8→claude-opus-4-8 -
sonnet/sonnet-5→claude-sonnet-5 -
fable/fable-5→claude-fable-5 -
sonnet-4preserved as a backward-compat alias forclaude-sonnet-4-6.
-
-
Updated Anthropic static fallback catalog (
src/discovery/static-direct.ts) to includeclaude-opus-4-8,claude-sonnet-5,claude-fable-5, while keepingclaude-sonnet-4-6for the legacysonnet-4alias.
-
normalizeTokenUsage()no longer rejects cache-write-only or reasoning-only usage records when input and output tokens are both zero.
Architecture-review hardening pass (PR #36), tiers 1–6.
- Proxy: fail-safe wire translation (unsupported content throws and fails over instead of being silently dropped), streaming failover across candidates, circuit-breaker wiring fed by real proxy outcomes.
- Registry: serialized
discover(), lifecycle gating, pricing provenance attribution. - Server:
/metricsbearer-token gate and new gauges, boot-time degraded mode when discovery fails, graceful shutdown that aborts in-flight upstream fetches. - Ledger: monthly partition rotation with retention trimming.
- CLI:
kosha doctor --ci. - Dependencies to latest; Vite 8 clears GHSA-fx2h-pf6j-xcff and GHSA-v6wh-96g9-6wx3.
A broad release that hardens the proxy/runtime, expands routing intelligence,
adds cost tracking with a budget gate, brings the Anthropic wire format under
the OpenAI-compatible proxy contract, and lights up new operator surfaces.
789 tests; full security-extended CodeQL coverage outside three intentional
credential-forwarding / wire-translation / ledger-write paths (src/proxy.ts,
src/wire-anthropic.ts, src/cost.ts).
-
Health-aware routing engine (
registry-routing.ts). NewRouteStrategy = cheapest | fastest | reliable | balancedfolds the rolling latency/timeout observations and per-provider circuit-breaker state on top of the price-ranked candidate set. Open-breaker providers always sort last so the ranking is directly usable as a failover order. Public API:ModelRegistry.rankedRoutes(),ModelRegistry.providerRouteHealth(). -
Proxy strategy selectors —
kosha:fastest[…],kosha:reliable[…],kosha:balanced[…]alongside the existingkosha:cheapest. -
Proxy failover across ranked candidates with
x-kosha-attempt-chainresponse header (provider:status,…). 5xx and network errors fail over; 4xx is the caller's own error and is surfaced as-is. Bounded to 3 actual upstream fetches per request. -
Cost as first-class (
src/cost.ts).-
x-kosha-estimated-cost-usdresponse header on every forwarded request. - JSONL spend ledger at
~/.kosha/ledger.jsonl; caller-supplied string fields are sanitized (CR/LF/TAB stripped, length-bounded) before write. - Monthly budget gate via
KOSHA_MONTHLY_BUDGET_USD; fails closed (503) when the ledger is unreadable, so a hostile or just-broken ledger cannot bypass the cap. - Per-tenant tagging via
Authorization: Bearer kosha-tenant-<name>— bucketing label only; upstream credentials still resolve from env/CLI files as usual. - New
kosha spendCLI (aliaskosha usage) rolls the ledger up by provider / model / tenant with--since,--until,--tenant,--jsonflags.
-
-
OpenAI ↔ Anthropic wire-format translation (
src/wire-anthropic.ts). Lifts system messages to the top-levelsystemfield, flattens structured content, ensures user-first ordering, collapses consecutive same-role messages (Anthropic forbids them), defaultsmax_tokens, mapsstop_reasononto the OpenAI vocabulary, reflects token usage. Proxy auto-detectsprovider === "anthropic"and routes via/v1/messageswithx-api-key+anthropic-version. Streaming through the translator is intentionally rejected with a 422 for now. -
SSRF guard.
safeUpstreamUrl()validates the resolved upstream hostname against a literal-string allowlist (catalog hosts + loopback for local runtimes) beforefetchis issued.buildUpstreamUrl()for non-local providers reads only the in-process provider catalogdefaultBaseUrl, never the disk-loadedregistry.baseUrl. -
Manifest merge — pricing quarantine + lifecycle TTL. Per-million
rate moves ≥75% in either direction keep the previous price block and
tag the row
pricing_quarantined. Models absent from the fresh fetch are kept with an incrementedmissingRunCount; dropped after 14 consecutive absent runs. -
Adaptive
CircuitBreaker— open-state cooldown doubles after each failed probe up to a 1h cap; resets to the base value on a successful close. NewcurrentResetTimeoutMs()for diagnostics, newmaxResetTimeoutMsoption. -
LM Studio + vLLM discoverers (
/v1/modelson:1234and:8000). Provider catalog entries with loopbackdefaultBaseUrlandopenai-compatible-httptransport so the proxy auto-routes through them via the SSRF allowlist's loopback branch. -
Watch & integrate.
ModelRegistry.onChange(handler, onError?)callback subscription (handler errors isolated from other subscribers). NewGET /metricsendpoint in Prometheus text format —kosha_models_total,kosha_providers_total, per-provider_reliability,_p95_latency_ms,_breaker_open. Provider label values are fully escaped per the exposition spec (\\,\",\n, no\r). -
kosha doctor(aliaskosha health) — surfaces deprecation findings (status,deprecationDate,daysUntilSunset,replacedBy) and per-provider routing health. Supports--json.
-
Credential resolver now searches
$XDG_CONFIG_HOME/<tool>,~/.config/<tool>, and (on Windows)%APPDATA%\<tool>for Claude / Codex / Gemini CLI configs, so non-default install layouts are no longer silently skipped. -
Discovery base layer — every discoverer now sends a default
User-Agent: kosha-discovery (+https://…)(caller can override) and everyfetchJSONcall honours a global deadline across all retries + backoff sleeps, so a slow-but-alive provider can't stretch a single call to ~31s. -
Provider catalog now lists
lmstudioandvllmas first-class local runtimes.
-
Google API key in URL → header.
?key=…query parameter replaced withx-goog-api-keyheader so the key cannot leak through proxy access logs, HTTPReferer, or any URL diagnostic path. -
Cache JSON-bomb guard.
KoshaCache.get()rejects any cache file larger than 25 MiB beforeJSON.parse, and the rejection is logged with the key sanitized to prevent log injection. -
Anthropic pagination cap. Cursor-based pagination is now bounded by
a hard page count in addition to
MAX_MODELS_PER_PROVIDER, so a buggyhas_more: trueloop cannot spin forever. -
AWS INI parser. A section header missing its closing bracket
(
[default) used to silently match the wrong section; now skipped. -
MCP server parse error. Malformed JSON-RPC input now responds with
-32700 Parse error(id null) instead of being dropped — clients no longer hang waiting on a missing reply. -
Proxy
x-kosha-requestedheader. Control characters (CR/LF/NUL) stripped and length-bounded so a malformed model string can no longer produce a 500 via theHeadersconstructor. -
startOfMonth/startOfNextMonthUTC. Previously mixed UTC getters with the local-timeDateconstructor; replaced withDate.UTC()so monthly budget cutoffs don't drift in non-UTC timezones. -
rankCandidatesByStrategyempty-set guard. Returns[]instead of computingMath.min(...[]) === Infinityextremes.
- CodeQL config in
.github/codeql/codeql-config.yml. Fullsecurity-extendedpack continues to run on every other source file;paths-ignorecarves outsrc/proxy.ts,src/cost.ts, andsrc/wire-anthropic.ts— the three files that own the intentional credential-forwarding and ledger-write patterns whose in-code defenses (host allowlist, ledger sanitization, budget fail-closed) are the load-bearing protections.
- New public exports from
src/index.ts:RouteStrategy,RankedRoute,RouteHealth,parseRouteStrategy,ROUTE_STRATEGIES, cost helpers (estimateRequestCost,appendLedgerEntry,readSpendForMonth,readMonthlyBudgetUsd,DEFAULT_LEDGER_PATH), translator helpers (translateOpenAIToAnthropic,translateAnthropicToOpenAI), and associated types. - New optional
DiscoveryModelV1.missingRunCountfield on the v1 discovery contract.
-
OpenAI-compatible proxy (
POST /proxy/v1/chat/completions,GET /proxy/v1/models). Supports direct model IDs, aliases, and thekosha:cheapest[capability,Nk,provider:X]hint syntax. Uses the full 5-tierCredentialResolver(same sources as discovery) and streams responses back verbatim. Addsx-kosha-model,x-kosha-provider, andx-kosha-requestedresponse headers. -
MCP stdio server (
kosha-mcpbin). Implements the MCP 2024-11-05 protocol over stdin/stdout with no SDK dependency. Exposes 6 tools:kosha_query_models,kosha_cheapest_model,kosha_model_detail,kosha_model_routes,kosha_resolve_alias,kosha_provider_health. Registry loads in the background on startup so the first tool call is fast. -
Pricing-diff anomaly log — mismatches between the live API price and the local
7-day snapshot ring now emit a
[kosha] pricing anomalywarning (tagged[promo]for known promotional windows so operators can distinguish noise from real drift). - 7-day snapshot ring — the local registry manifest now keeps a rolling 7-entry history of pricing snapshots, enabling rollback to any of the last 7 states.
-
DISCOVERER_REGISTRY— single source of truth for all 22 provider discoverers, replacing the previous duplicated lists. NewgetDiscoverer(providerId)export for targeted single-provider discovery.
-
primaryCredentialEnvVaradded to theProviderDescriptorinterface — it was used in catalog objects andregistry-runtime.tsbut missing from the type, breaking the TypeScript build. - Proxy now falls back to
registry.modelRoutes()when the primary provider card is not forwardable (e.g. requestingclaude-sonnet-4-6with only an OpenRouter key now routes through OpenRouter instead of returning 422). -
registryClassifyError:lower.includes("5")replaced with/\b5\d{2}\b/.test()— the old check false-positived on model IDs and strings containing the digit 5. -
applyPromoOverridespricing spread fixed:{ ...match.pricing }→{ ...card.pricing, ...match.pricing }so base fields are not silently dropped. -
hasUsablePricing:> 0→!== undefined— free-tier models (price = 0) were incorrectly excluded from cheapest-model results.
- New K-lettermark logo (circuit-board K + keyhole, purple/gold).
- DeepSeek promo window extended to 2026-05-31.
-
fallbackRegistryCredentialnow readsprimaryCredentialEnvVarfrom the provider catalog instead of a hardcoded duplicateFALLBACK_ENV_MAP.
- Pricing-stability hardening promoted to stable. No API surface changes from 0.8.0.
0.8.0 — 2026-04-21
-
Tokenizer-family inference (
inferTokenizerFamily) for API-served models:o200k_base,cl100k_base,claude,gemini,llama4,llama3,llama2,mistral,cohere,deepseek,qwen. Local runtimes continue to surface the family viaLocalRuntimeMetadata.tokenizerFamily; the enricher fills in API-served models from the origin + model ID. -
Tool-dialect and structured-output inference (
src/model-features.ts):inferToolDialect,inferStructuredOutputModes, andinferParallelToolCalls. New union typesToolDialect,StructuredOutputMode, andModelStatusexported from@sriinnu/kosha-discovery. -
Multimodal pricing fields on
ModelPricing:imageInputPerImage,imageOutputPerImage,audioInputPerMillion,audioOutputPerMillion,audioInputPerSecond,audioOutputPerSecond,videoInputPerSecond,videoInputPerMillion,inputPerMillionCharacters,outputPerMillionCharacters,longContextInputPerMillion,longContextOutputPerMillion,longContextThresholdTokens. LiteLLM enrichment maps the matching fields (input_cost_per_image,input_cost_per_audio_token,input_cost_per_audio_per_second,input_cost_per_video_per_second,input_cost_per_character,input_cost_per_token_above_128k_tokens,input_cost_per_token_above_200k_tokens). -
Deprecation / sunset metadata on
ModelCard:status,deprecationDate,replacedBy. Status is derived from litellm'sdeprecation_date(future date →"deprecated", past date →"retired", no date →"active"). -
Llama 4 support — tokenizer family tag (
"llama4"), tool dialect routing, and structured-output mode inference for Scout / Maverick / Behemoth variants. -
Provider prompt-cache prefix floor — new
ProviderDescriptor.minCachePrefixTokensfield documenting the minimum prompt-prefix size required to engage a provider's prompt cache (1024 tokens for Anthropic and OpenAI today). -
Additional capability flags surfaced from litellm:
audio_input,audio_output,video_input,reasoning,structured_output. -
Parallel tool-call hint (
ModelCard.supportsParallelToolCalls) prefers litellm'ssupports_parallel_function_callingflag and falls back to a heuristic keyed on dialect + model ID. -
v1 discovery-contract extensions —
DiscoveryModelV1now surfacestoolDialect,structuredOutputModes,supportsParallelToolCalls,status,deprecationDate, andreplacedBy. All additions are optional / nullable, so v1 consumers that ignore unknown fields remain compatible.
- GPT-3.5-turbo-1106 and 0125 now correctly report parallel tool-call
support (
true); earlier revision ofinferParallelToolCallswrongly returnedfalse. - LiteLLM
extractPricingprice-signal guard recognises multimodal-only entries (image-generation, TTS-per-second), so DALL-E-style and ElevenLabs-style models no longer drop through with undefined pricing.
- The pricing-merge path in
LiteLLMEnrichernow tops upcacheReadPerMillion,cacheWritePerMillion, and the new multimodal fields per-field instead of all-or-nothing, so proxy routes (OpenRouter, etc.) can keep their markup prices while inheriting direct-origin cache and multimodal rates. -
inferToolDialectdocumentation now notes that managed serving layers (Groq, Together, Fireworks, OpenRouter) expose open-weight models behind an OpenAI-compatible tools API regardless of the underlying family; callers should consultModelCard.providerfirst and fall back to this inference only for direct-origin routes.
-
hono bumped 4.12.9 → 4.12.14, closing seven medium-severity
advisories (JSX HTML injection, cookie-name bypass, cookie-name
validation,
ipRestrictionIPv4-mapped IPv6, serveStatic repeated-slash bypass,toSSGpath traversal,@hono/node-serverserveStatic bypass).
- 49 new tests across
test/model-features.test.ts,test/enrichment/litellm-2026.test.ts, andtest/tokenizer-family.test.tscovering tool dialects, structured-output modes, parallel tool calls, multimodal pricing, long-context tiers, deprecation status, and the Llama 4 family. Full suite: 660 tests passing.
0.7.0 — 2026-04-01
-
Batch API pricing (
batchInputPerMillion,batchOutputPerMillion) onModelPricing, wired end-to-end through the enrichment pipeline and discovery-v1 schema. - CLI refactor: sub-modules each under 450 LOC,
pricingview, and a dedicatedenrichcommand.
- Documentation and schema validation extended to cover the complete pricing surface (input, output, reasoning, cache, and batch).
See git history and GitHub release notes for entries prior to 0.7.0.
Reference pages are generated from the repository; changes go through a pull request there.