From d2952515506181714446a41f6dbf9b519d525a1f Mon Sep 17 00:00:00 2001 From: Michael Nguyen Date: Fri, 31 Jul 2026 11:15:42 -0700 Subject: [PATCH] feat(adapter-claude): add Claude Sonnet 5 to the static model fallback (#10280) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents pick their model from a dropdown in agent config, populated per-adapter by `listAdapterModels()` → each adapter's live provider catalog merged over a static fallback list > - For `claude_local`, newer model ids only reach the dropdown via the live Anthropic `/v1/models` fetch, which needs a server `ANTHROPIC_API_KEY`, a <5s round-trip, non-Bedrock mode, and account entitlement; on any miss it silently falls back to the static `models` array > - Claude Sonnet 5 (`claude-sonnet-5`) is a current flagship but was absent from that static fallback, so it appeared only when live discovery happened to succeed — i.e. "the newest model doesn't consistently show up" > - This pull request adds `claude-sonnet-5` to the `claude_local` static model list so it is selectable regardless of the live-discovery path > - The benefit is a consistent, reliable dropdown that no longer depends on a flaky live fetch to surface a shipped flagship model ## Linked Issues or Issue Description No public GitHub issue. The bug is described inline following the bug-report template: **What happened** The `claude_local` agent-config model dropdown intermittently omitted Claude Sonnet 5. `claude-sonnet-5` was missing from the adapter's static fallback `models` array (`packages/adapters/claude-local/src/index.ts`), so it only surfaced when the live Anthropic `/v1/models` discovery happened to succeed. **Expected behavior** Claude Sonnet 5 is a shipped flagship model and should always be selectable in the dropdown, independent of whether live discovery succeeds. **Steps to reproduce** 1. Run the server without a working live Anthropic `/v1/models` path (no `ANTHROPIC_API_KEY`, Bedrock mode, a discovery timeout, or a cache miss). 2. Open agent config for a `claude_local` agent and inspect the model dropdown. 3. Observe that `claude-sonnet-5` is absent because the static fallback list omitted it. **Deployment mode** Self-hosted / local adapter (`claude_local`); the server process reads `ANTHROPIC_API_KEY` from its environment. ## What Changed - Added `{ id: "claude-sonnet-5", label: "Claude Sonnet 5" }` to the `claude_local` static `models` fallback, immediately after `claude-opus-4-8` (so Opus 4.8 stays the default first option). - Added an explicit regression assertion in `server/src/__tests__/adapter-models.test.ts` that `claude-sonnet-5` is present in the `claude_local` fallback when live discovery is unavailable. ## Verification - `pnpm -C server exec vitest run src/__tests__/adapter-models.test.ts -t "claude fallback"` — **passes** (the new `claude-sonnet-5` assertion included). - Reviewed the consuming tests: the fallback test also asserts `models[0]?.id === "claude-opus-4-8"` (still index 0 — Sonnet 5 is index 1, unaffected); `adapter-registry.test.ts` reads `builtIn?.models` dynamically, so no exact-array snapshot breaks. - Change is a single static-data addition plus a test assertion; no control-flow change. ## Risks - Low risk. Pure additive change to a fallback list; no control-flow change. Worst case is an id that a given account isn't entitled to, which the existing "current"/manual-model UI paths already tolerate. ## Model Used Claude (Anthropic), model id `claude-opus-4-8` (Opus 4.8), extended thinking + tool use, run as the Paperclip CTO agent. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [ ] My branch name describes the change (branch is the assigned execution-workspace branch and cannot be renamed this run) - [x] I have run tests locally and they pass (server adapter-models "claude fallback" case) - [x] I have added or updated tests where applicable (explicit `claude-sonnet-5` fallback assertion) - [x] I have updated relevant documentation to reflect my changes (n/a — no docs reference this list) - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green (pending CI) - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups (pending review) - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Claude Opus 4.8 Co-authored-by: Paperclip --- packages/adapters/claude-local/src/index.ts | 1 + server/src/__tests__/adapter-models.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/adapters/claude-local/src/index.ts b/packages/adapters/claude-local/src/index.ts index fc20d2eab1..88fb662612 100644 --- a/packages/adapters/claude-local/src/index.ts +++ b/packages/adapters/claude-local/src/index.ts @@ -7,6 +7,7 @@ export const SANDBOX_INSTALL_COMMAND = "npm install -g @anthropic-ai/claude-code export const models = [ { id: "claude-opus-4-8", label: "Claude Opus 4.8" }, + { id: "claude-sonnet-5", label: "Claude Sonnet 5" }, { id: "claude-fable-5", label: "Claude Fable 5" }, { id: "claude-mythos-5", label: "Claude Mythos 5" }, { id: "claude-opus-5", label: "Claude Opus 5" }, diff --git a/server/src/__tests__/adapter-models.test.ts b/server/src/__tests__/adapter-models.test.ts index f7d28c3116..d60a73a353 100644 --- a/server/src/__tests__/adapter-models.test.ts +++ b/server/src/__tests__/adapter-models.test.ts @@ -65,6 +65,7 @@ describe("adapter model listing", () => { expect(models.some((model) => model.id === "claude-opus-4-8")).toBe(true); // Newer flagship models are offered, but Opus 4.8 stays the default (first) option. expect(models[0]?.id).toBe("claude-opus-4-8"); + expect(models.some((model) => model.id === "claude-sonnet-5")).toBe(true); expect(models.some((model) => model.id === "claude-fable-5")).toBe(true); expect(models.some((model) => model.id === "claude-mythos-5")).toBe(true); // Opus 5 is a current GA flagship and must be offered even when live discovery is unavailable.