From eadd7338f6f6c90e56126f3ca142c3291f61667e Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Wed, 1 Jul 2026 21:50:13 -0300 Subject: [PATCH] fix(providers): add claude-sonnet-5 to Kiro model catalog (#5796) Integrated into release/v3.8.43 --- CHANGELOG.md | 2 ++ .../config/providers/registry/kiro/index.ts | 6 ++++ tests/unit/kiro-claude-sonnet-5-2267.test.ts | 30 +++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/unit/kiro-claude-sonnet-5-2267.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa0af29aa..10467a5204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ ### 🔧 Bug Fixes +- **providers (Kiro — Claude Sonnet 5):** the Kiro provider's model catalog was missing `claude-sonnet-5`, so the model could not be selected or routed even on accounts that already had access to it ("claude-sonnet-5 is not supported"). Added the model to the Kiro registry (`open-sse/config/providers/registry/kiro/index.ts`) as a 1M-context / 128K-output Claude model, mirroring the existing Claude entries; the registry `models[]` feeds both the model selector and the live CodeWhisperer `ListAvailableModels` fallback, so the model is now selectable and routable. Regression guard: `tests/unit/kiro-claude-sonnet-5-2267.test.ts`. (thanks [@openbioinfo](https://github.com/openbioinfo)) + - **settings (model aliases — self-heal after restart):** the Settings → Routing page showed "No exact-match aliases configured" after a server restart even though the aliases were persisted in the DB. Aliases are held in a module-local `_customAliases` map in `modelDeprecation.ts` that the boot path hydrates, but Next.js compiles the app-route module graph separately from the startup graph (the same webpack chunk-splitting class as #5312), so the `GET /api/settings/model-aliases` handler read a different, un-hydrated copy. The handler now self-heals: when its in-memory alias map is empty it reads `settings.modelAliases` from the DB (via the existing `getSettings()` db module — no raw SQL in the route) and repopulates the map, so the UI reflects the persisted aliases on the first GET after a restart. Follow-up: the root cause is now also fixed — the `_customAliases` store in `modelDeprecation.ts` is backed by `globalThis` (key `__omniroute_customAliases__`), so the startup and app-route module graphs share **one** store and the route reads the boot-hydrated aliases directly (the DB self-heal remains as a harmless fallback), mirroring the same `globalThis` singleton pattern already applied to `thinkingBudget.ts`/`backgroundTaskDetector.ts` (#5312). Regression guards: `tests/unit/model-aliases-settings-route-selfheal.test.ts` + `tests/unit/model-aliases-globalthis-5777.test.ts`. ([#5777](https://github.com/diegosouzapw/OmniRoute/pull/5777) — thanks [@jleonar2](https://github.com/jleonar2)) - **providers (grok-cli token auto-refresh):** grok-cli OAuth tokens were never proactively refreshed before their real expiry. `mapTokens` hardcoded `expiresIn: 21600` (6 h) regardless of the token's actual lifetime, so the persisted `expiresAt` was always "now + 6 h" and the proactive `tokenHealthCheck` sweep (refresh when `expiresAt - now < 5 min`) fired 6 h after import instead of shortly before the token really expired. `mapTokens` now computes `expiresIn` from the authoritative `expires_at` field in `~/.grok/auth.json` (ISO → epoch-seconds) with a fallback to the JWT `exp` claim (payload-only decode, no signature trust); the hardcoded `21600` is kept only when neither is present. An already-expired token (real `expires_at`/`exp` in the past) is now clamped to a positive `expiresIn` via `Math.max(1, …)`, so the import route stores a near-future `expiresAt` and AutoCombo refreshes the connection instead of reading a past date and excluding it outright. Regression guards: 5 cases in `tests/unit/grok-cli-oauth.test.ts` (JWT `exp`, JSON `expires_at`, the `21600` fallback, and the two expired-token clamps). ([#5775](https://github.com/diegosouzapw/OmniRoute/pull/5775) — thanks [@Chewji9875](https://github.com/Chewji9875)) diff --git a/open-sse/config/providers/registry/kiro/index.ts b/open-sse/config/providers/registry/kiro/index.ts index b3a9792264..6c4d41cf6e 100644 --- a/open-sse/config/providers/registry/kiro/index.ts +++ b/open-sse/config/providers/registry/kiro/index.ts @@ -41,6 +41,12 @@ export const kiroProvider: RegistryEntry = { contextLength: 1000000, maxOutputTokens: 128000, }, + { + id: "claude-sonnet-5", + name: "Claude Sonnet 5", + contextLength: 1000000, + maxOutputTokens: 128000, + }, { id: "claude-sonnet-4.6", name: "Claude Sonnet 4.6", diff --git a/tests/unit/kiro-claude-sonnet-5-2267.test.ts b/tests/unit/kiro-claude-sonnet-5-2267.test.ts new file mode 100644 index 0000000000..ede4c805cc --- /dev/null +++ b/tests/unit/kiro-claude-sonnet-5-2267.test.ts @@ -0,0 +1,30 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +import { kiroProvider } from "../../open-sse/config/providers/registry/kiro/index.ts"; + +// Regression for the port of decolua/9router#2267 ("claude-sonnet-5 is not supported"). +// +// The Kiro provider's OAuth model catalog lives in `registry/kiro/index.ts` `models[]`. +// That list is both the model selector's source and the fallback for the live +// CodeWhisperer ListAvailableModels fetch (`kiroModels.ts::toFallbackResult`). Because +// `claude-sonnet-5` — a real, shipping Anthropic model already served by Kiro — was +// missing from it, the model could not be selected or routed on the Kiro provider even +// though the account had access. The fix adds the single model entry (mirroring the +// existing Claude entries), with the 1M-context / 128K-output capability Kiro serves it at. + +test("kiro registry exposes claude-sonnet-5", () => { + const ids = kiroProvider.models.map((m) => m.id); + assert.ok( + ids.includes("claude-sonnet-5"), + `expected kiro registry to include claude-sonnet-5, got: ${ids.join(", ")}` + ); +}); + +test("kiro claude-sonnet-5 declares the 1M-context / 128K-output capability", () => { + const sonnet5 = kiroProvider.models.find((m) => m.id === "claude-sonnet-5"); + assert.ok(sonnet5, "claude-sonnet-5 must be present in the kiro registry"); + assert.equal(sonnet5.name, "Claude Sonnet 5"); + assert.equal(sonnet5.contextLength, 1000000); + assert.equal(sonnet5.maxOutputTokens, 128000); +});