From 735d2e8462533d9baa7c6f700db5c2f59c3ed49a Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Fri, 24 Jul 2026 13:56:45 -0400 Subject: [PATCH] Add Claude Opus 5 support --- README.md | 6 ++--- docs/architecture.md | 8 +++---- packages/agent/README.md | 2 +- .../agent/examples/anthropic-native-smoke.ts | 4 ++-- packages/agent/test/e2e.live.test.ts | 4 ++-- packages/ai/CHANGELOG.md | 8 +++++-- packages/ai/README.md | 4 ++-- packages/ai/docs/supported-models.md | 1 + packages/ai/examples/quickstart.ts | 2 +- packages/ai/src/models.ts | 22 ++++++++++++++++++- packages/ai/src/native-tools.ts | 4 ++-- packages/ai/src/providers/anthropic/index.ts | 1 + .../test/anthropic-native.integration.test.ts | 2 +- packages/ai/test/anthropic-payload.test.ts | 1 + .../ai/test/computer-tool.integration.test.ts | 2 +- packages/ai/test/models.test.ts | 15 +++++++++++++ packages/ai/test/native-tools.test.ts | 6 ++--- packages/cli/README.md | 2 +- packages/cli/src/cli.ts | 9 ++++---- packages/cli/test/harness-models.test.ts | 2 ++ skills/cua-cli/SKILL.md | 2 +- 21 files changed, 76 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 42e29b03..3d6179dc 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Frontier models expose computer use through different tool protocols: - **OpenAI gpt-5.5**: a built-in `computer` tool that emits actions like `{type:"click", x, y}`, `{type:"scroll", x, y, scroll_x, scroll_y}`, `{type:"keypress", keys:[...]}`, … -- **Anthropic claude-opus-4-7**: a built-in `computer_20251124` tool that +- **Anthropic claude-opus-5**: a built-in `computer_20251124` tool that emits `{action:"left_click", coordinate:[x, y]}`, `{action:"scroll", scroll_direction, scroll_amount}`, … - **Google gemini-2.5-pro / gemini-3.x**: a set of predefined @@ -102,7 +102,7 @@ npx tsx packages/cli/src/cli.ts --help # set API keys via env vars export OPENAI_API_KEY=sk-... # for gpt-5.5 -export ANTHROPIC_API_KEY=sk-ant-... # for claude-opus-4-7 +export ANTHROPIC_API_KEY=sk-ant-... # for claude-opus-5 export GOOGLE_API_KEY=... # for gemini-3-flash-preview export META_API_KEY=... # for muse-spark-1.1 export XAI_API_KEY=xai-... # for grok-4.5 @@ -117,7 +117,7 @@ cua -p "Open https://news.ycombinator.com and tell me the top story" cua models # Claude -cua -p --model claude-opus-4-7 "Same prompt" +cua -p --model claude-opus-5 "Same prompt" # Gemini 3 Flash (built-in computer use) cua -p --model gemini-3-flash-preview "Same prompt" diff --git a/docs/architecture.md b/docs/architecture.md index 2d3ea280..ec5d5f4b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -132,10 +132,10 @@ Anthropic model through an allowlisted, Anthropic-API-only early-access tool schema instead of the canonical function tools: `computer_20260701` pairs with `computer` mode and `browser_20260701` with `browser` mode. Model and mode mismatches throw locally before a browser is provisioned. The live-verified -model families are `claude-fable-5`, `claude-opus-4-8`, and `claude-sonnet-5` -for `computer_20260701`; only `claude-opus-4-8` and `claude-sonnet-5` support -`browser_20260701`. The API key's organization must also have the matching -beta entitlement. +model families are `claude-fable-5`, `claude-opus-4-8`, `claude-opus-5`, and +`claude-sonnet-5` for `computer_20260701`; `claude-opus-4-8`, `claude-opus-5`, +and `claude-sonnet-5` support `browser_20260701`. The API key's organization +must also have the matching beta entitlement. The spec routes the model to a CUA-owned api id; the registered `anthropic` provider dispatches it to pi's builtin `anthropic-messages` transport with the diff --git a/packages/agent/README.md b/packages/agent/README.md index 18be1b82..02f607b1 100644 --- a/packages/agent/README.md +++ b/packages/agent/README.md @@ -65,7 +65,7 @@ const first = await harness.prompt("Open example.com and describe what you see." console.log(textOf(first)); // Swap providers mid-session; CUA tools and the default prompt refresh to match. -await harness.setModel("anthropic:claude-opus-4-7"); +await harness.setModel("anthropic:claude-opus-5"); // Turn 2 continues the same transcript on the new model. const second = await harness.prompt("Open the most relevant link from what you found."); diff --git a/packages/agent/examples/anthropic-native-smoke.ts b/packages/agent/examples/anthropic-native-smoke.ts index 3061d665..7dfe0234 100644 --- a/packages/agent/examples/anthropic-native-smoke.ts +++ b/packages/agent/examples/anthropic-native-smoke.ts @@ -1,6 +1,6 @@ // Smoke-test the mode/native-tool matrix against a live Kernel browser: // -// MODEL_REF=anthropic:claude-opus-4-8 CONFIG=native-browser tsx examples/anthropic-native-smoke.ts +// MODEL_REF=anthropic:claude-opus-5 CONFIG=native-browser tsx examples/anthropic-native-smoke.ts // // CONFIG selects the runtime shape: // computer (default) canonical computer-plane (OS input) tools @@ -13,7 +13,7 @@ import { requireCuaEnvApiKeyForModel, type CuaModelRef, type CuaMode, type CuaNa import { CuaAgent } from "../src/index"; import { logAgentEvent, logAssistant } from "./shared/logging"; -const modelRef = (process.env.MODEL_REF as CuaModelRef | undefined) ?? "anthropic:claude-opus-4-8"; +const modelRef = (process.env.MODEL_REF as CuaModelRef | undefined) ?? "anthropic:claude-opus-5"; const config = process.env.CONFIG ?? "computer"; const CONFIGS: Record = { diff --git a/packages/agent/test/e2e.live.test.ts b/packages/agent/test/e2e.live.test.ts index 2b51c6ca..8691120f 100644 --- a/packages/agent/test/e2e.live.test.ts +++ b/packages/agent/test/e2e.live.test.ts @@ -18,7 +18,7 @@ type ProviderCase = { apiKeyEnvVar: string; modelRef: | "openai:gpt-5.5" - | "anthropic:claude-opus-4-7" + | "anthropic:claude-opus-5" | "google:gemini-3-flash-preview" | "meta:muse-spark-1.1" | "xai:grok-4.5" @@ -57,7 +57,7 @@ const cases: ProviderCase[] = [ { name: "anthropic", apiKeyEnvVar: "ANTHROPIC_API_KEY", - modelRef: "anthropic:claude-opus-4-7", + modelRef: "anthropic:claude-opus-5", prompt: [ "Use the tool named `screenshot` exactly once to inspect the browser.", "Pass empty arguments (`{}`).", diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 63e3b2ed..369d6c6f 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,10 +2,14 @@ ## Unreleased +- Add `anthropic:claude-opus-5` with its 1M-token context window, 128k output + limit, adaptive thinking levels, and current pricing. Opus 5 is also accepted + by both July 2026 Anthropic native tools. - Fail locally when `computer_20260701` or `browser_20260701` is paired with an Anthropic model that the live early-access API rejects. The verified - computer models are Claude Fable 5, Claude Opus 4.8, and Claude Sonnet 5; - the verified browser models are Claude Opus 4.8 and Claude Sonnet 5. + computer models are Claude Fable 5, Claude Opus 4.8, Claude Opus 5, and + Claude Sonnet 5; the verified browser models are Claude Opus 4.8, Claude + Opus 5, and Claude Sonnet 5. - Carry each native tool's stop-on-first-failure result contract through `CuaRuntimeSpec`, allowing cua-agent to skip unsafe remaining actions without a provider conditional. diff --git a/packages/ai/README.md b/packages/ai/README.md index 6b630d18..5bd1a8ad 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -92,7 +92,7 @@ for (const block of response.content) { A runnable version ships at [`examples/quickstart.ts`](./examples/quickstart.ts) (with a sample screenshot). In this repo, run it from `packages/ai` with `npm run example:quickstart`; switch providers with the `CUA_MODEL` env var, -e.g. `CUA_MODEL=anthropic:claude-opus-4-7`. +e.g. `CUA_MODEL=anthropic:claude-opus-5`. ## Error Handling @@ -211,7 +211,7 @@ provider/tool metadata. ```ts getCuaModel("openai:gpt-5.5"); -getCuaModel("anthropic:claude-opus-4-7"); +getCuaModel("anthropic:claude-opus-5"); getCuaModel("google:gemini-3-flash-preview"); getCuaModel("meta:muse-spark-1.1"); getCuaModel("xai:grok-4.5"); diff --git a/packages/ai/docs/supported-models.md b/packages/ai/docs/supported-models.md index b7be7aab..25973104 100644 --- a/packages/ai/docs/supported-models.md +++ b/packages/ai/docs/supported-models.md @@ -33,6 +33,7 @@ Family matches (root + numeric revision/dated-snapshot suffixes): - `claude-3-7-sonnet` - `claude-opus-4` +- `claude-opus-5` - `claude-sonnet-4` - `claude-sonnet-5` - `claude-haiku-4` diff --git a/packages/ai/examples/quickstart.ts b/packages/ai/examples/quickstart.ts index 7423a2ae..caac9a8a 100644 --- a/packages/ai/examples/quickstart.ts +++ b/packages/ai/examples/quickstart.ts @@ -8,7 +8,7 @@ import { } from "@onkernel/cua-ai"; // Switch providers by setting CUA_MODEL (and the matching API key env var): -// anthropic:claude-opus-4-7 ANTHROPIC_API_KEY +// anthropic:claude-opus-5 ANTHROPIC_API_KEY // google:gemini-3-flash-preview GOOGLE_API_KEY // meta:muse-spark-1.1 META_API_KEY // xai:grok-4.5 XAI_API_KEY diff --git a/packages/ai/src/models.ts b/packages/ai/src/models.ts index 194a1fef..f054156c 100644 --- a/packages/ai/src/models.ts +++ b/packages/ai/src/models.ts @@ -65,6 +65,7 @@ export const CUA_MODEL_ANNOTATIONS: Record[]> = { openai: [], - anthropic: [], + // Opus 5 is live in Anthropic's API and models.dev but is newer than the + // pinned pi-ai catalog. Remove this override after the next pi-ai bump. + anthropic: [anthropicOpus5Model()], google: [], // pi-ai 0.80.10 still lacks Meta's models.dev catalog entry. meta: [cuaModel("meta", "muse-spark-1.1", "Muse Spark 1.1")], @@ -281,6 +284,23 @@ function isCuaFamilyMatch(id: string, family: string): boolean { .every((segment) => /^\d+$/.test(segment)); } +function anthropicOpus5Model(): Model { + return { + id: "claude-opus-5", + name: "Claude Opus 5", + api: "anthropic-messages", + provider: "anthropic", + baseUrl: "https://api.anthropic.com", + compat: { forceAdaptiveThinking: true, supportsTemperature: false }, + reasoning: true, + thinkingLevelMap: { xhigh: "xhigh", max: "max" }, + input: ["text", "image"], + cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }, + contextWindow: 1_000_000, + maxTokens: 128_000, + }; +} + function cuaModel(provider: Exclude, id: string, name: string): Model { const base = { id, diff --git a/packages/ai/src/native-tools.ts b/packages/ai/src/native-tools.ts index ce7c4cb6..cba33705 100644 --- a/packages/ai/src/native-tools.ts +++ b/packages/ai/src/native-tools.ts @@ -66,7 +66,7 @@ const NATIVE_TOOL_INFO: Record = { provider: "anthropic", betaHeader: "computer-use-2026-07-01", defaultName: "computer", - supportedModelFamilies: ["claude-fable-5", "claude-opus-4-8", "claude-sonnet-5"], + supportedModelFamilies: ["claude-fable-5", "claude-opus-4-8", "claude-opus-5", "claude-sonnet-5"], skippedAfterFailureMessage: "Not executed: an earlier computer action in this turn failed.", }, browser_20260701: { @@ -74,7 +74,7 @@ const NATIVE_TOOL_INFO: Record = { provider: "anthropic", betaHeader: "browser-use-2026-07-01", defaultName: "browser", - supportedModelFamilies: ["claude-opus-4-8", "claude-sonnet-5"], + supportedModelFamilies: ["claude-opus-4-8", "claude-opus-5", "claude-sonnet-5"], skippedAfterFailureMessage: "Not executed: an earlier action in this turn failed.", }, }; diff --git a/packages/ai/src/providers/anthropic/index.ts b/packages/ai/src/providers/anthropic/index.ts index ff056198..1ba6b777 100644 --- a/packages/ai/src/providers/anthropic/index.ts +++ b/packages/ai/src/providers/anthropic/index.ts @@ -72,6 +72,7 @@ function isAdaptiveThinkingModel(model: Model): boolean { id.startsWith("claude-mythos-preview") || id.startsWith("claude-sonnet-5") || id.startsWith("claude-sonnet-4-6") || + id.startsWith("claude-opus-5") || id.startsWith("claude-opus-4-8") || id.startsWith("claude-opus-4-7") || id.startsWith("claude-opus-4-6") diff --git a/packages/ai/test/anthropic-native.integration.test.ts b/packages/ai/test/anthropic-native.integration.test.ts index 42b63766..3d22d0f1 100644 --- a/packages/ai/test/anthropic-native.integration.test.ts +++ b/packages/ai/test/anthropic-native.integration.test.ts @@ -27,7 +27,7 @@ const cases: Array<{ describe("Anthropic early-access native tools", () => { for (const current of cases) { liveIt(`${current.name} survives CUA's pi-ai serialization`, async () => { - const spec = resolveCuaRuntimeSpec("anthropic:claude-opus-4-8", { nativeTool: current.nativeTool }); + const spec = resolveCuaRuntimeSpec("anthropic:claude-opus-5", { nativeTool: current.nativeTool }); const response = await createCuaModels().complete( spec.model, { diff --git a/packages/ai/test/anthropic-payload.test.ts b/packages/ai/test/anthropic-payload.test.ts index b575fdcc..dc97269e 100644 --- a/packages/ai/test/anthropic-payload.test.ts +++ b/packages/ai/test/anthropic-payload.test.ts @@ -21,6 +21,7 @@ describe("anthropicAdaptiveThinkingOnPayload", () => { const payload = { thinking: { type: "enabled", budget_tokens: 8_192 } }; for (const ref of [ + "anthropic:claude-opus-5", "anthropic:claude-sonnet-5", "anthropic:claude-sonnet-4-6", "anthropic:claude-opus-4-8", diff --git a/packages/ai/test/computer-tool.integration.test.ts b/packages/ai/test/computer-tool.integration.test.ts index 96b8ca79..3793c20a 100644 --- a/packages/ai/test/computer-tool.integration.test.ts +++ b/packages/ai/test/computer-tool.integration.test.ts @@ -51,7 +51,7 @@ const cases: ProviderCase[] = [ { provider: "anthropic", envVar: "ANTHROPIC_API_KEY", - modelRef: "anthropic:claude-opus-4-7", + modelRef: "anthropic:claude-opus-5", tools: () => anthropic.computerTools({ actions: ["click"] }), coordinateRange: [0, 1920], requireToolCalls: true, diff --git a/packages/ai/test/models.test.ts b/packages/ai/test/models.test.ts index 30865ed6..4cc847e9 100644 --- a/packages/ai/test/models.test.ts +++ b/packages/ai/test/models.test.ts @@ -45,6 +45,7 @@ describe("CUA model refs", () => { it("lists curated model refs without a default", () => { const models = listCuaModels(); expect(models.some((model) => model.ref === "openai:gpt-5.5")).toBe(true); + expect(models.some((model) => model.ref === "anthropic:claude-opus-5")).toBe(true); expect(models.every((model) => model.ref.includes(":"))).toBe(true); expect(models.some((model) => "default" in model)).toBe(false); expect(models.some((model) => "origin" in model)).toBe(false); @@ -55,6 +56,18 @@ describe("CUA model refs", () => { expect(model.provider).toBe("yutori"); expect(model.api).toBe("yutori-chat-completions"); + const opus = getCuaModel("anthropic:claude-opus-5"); + expect(cuaOverrideModels("anthropic").map((entry) => entry.id)).toContain("claude-opus-5"); + expect(opus).toMatchObject({ + provider: "anthropic", + api: "anthropic-messages", + contextWindow: 1_000_000, + maxTokens: 128_000, + thinkingLevelMap: { xhigh: "xhigh", max: "max" }, + cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }, + }); + expect(opus.compat).toMatchObject({ forceAdaptiveThinking: true, supportsTemperature: false }); + const muse = getCuaModel("meta:muse-spark-1.1"); expect(muse.provider).toBe("meta"); expect(muse.api).toBe(meta.META_RESPONSES_API); @@ -141,6 +154,8 @@ describe("CUA support annotations", () => { expect(findCuaAnnotation("openai", "gpt-5.4-mini")?.match).toEqual({ kind: "family", family: "gpt-5.4-mini" }); expect(findCuaAnnotation("openai", "gpt-5.4-mini-2026-03-17")?.match).toEqual({ kind: "family", family: "gpt-5.4-mini" }); expect(findCuaAnnotation("anthropic", "claude-opus-4-7")).toBeDefined(); + expect(findCuaAnnotation("anthropic", "claude-opus-5")?.match).toEqual({ kind: "family", family: "claude-opus-5" }); + expect(findCuaAnnotation("anthropic", "claude-opus-5-20260724")?.match).toEqual({ kind: "family", family: "claude-opus-5" }); expect(findCuaAnnotation("anthropic", "claude-3-7-sonnet-20250219")).toBeDefined(); }); diff --git a/packages/ai/test/native-tools.test.ts b/packages/ai/test/native-tools.test.ts index bcc4bb07..7dd12e3a 100644 --- a/packages/ai/test/native-tools.test.ts +++ b/packages/ai/test/native-tools.test.ts @@ -44,15 +44,15 @@ describe("native tool validation", () => { /native tool "computer_20260701" is an allowlisted Anthropic API beta.*claude-opus-4-8/s, ); expect(() => resolveCuaRuntimeSpec("anthropic:claude-fable-5", { nativeTool: { type: "browser_20260701" } })).toThrow( - /supported model families: claude-opus-4-8, claude-sonnet-5/, + /supported model families: claude-opus-4-8, claude-opus-5, claude-sonnet-5/, ); }); it("accepts each live-verified native-tool model family", () => { - for (const model of ["claude-fable-5", "claude-opus-4-8", "claude-sonnet-5"] as const) { + for (const model of ["claude-fable-5", "claude-opus-4-8", "claude-opus-5", "claude-sonnet-5"] as const) { expect(() => resolveCuaRuntimeSpec(`anthropic:${model}`, { nativeTool: { type: "computer_20260701" } })).not.toThrow(); } - for (const model of ["claude-opus-4-8", "claude-sonnet-5"] as const) { + for (const model of ["claude-opus-4-8", "claude-opus-5", "claude-sonnet-5"] as const) { expect(() => resolveCuaRuntimeSpec(`anthropic:${model}`, { nativeTool: { type: "browser_20260701" } })).not.toThrow(); } }); diff --git a/packages/cli/README.md b/packages/cli/README.md index 95d9db13..83805eb1 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -43,7 +43,7 @@ cua do "buy a pair of socks on amazon" --max-steps 20 cua models cua models -p openai cua --print --model openai:gpt-5.5 "..." -cua --print --model anthropic:claude-opus-4-7 "..." +cua --print --model anthropic:claude-opus-5 "..." cua --print --model google:gemini-3-flash-preview "..." cua --print --model meta:muse-spark-1.1 "..." cua --print --model xai:grok-4.5 "..." diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 701e5e34..bf31bad0 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -51,7 +51,7 @@ Options: match exactly one entry in \`cua models\`. Recommended: openai: openai:gpt-5.5 - anthropic: anthropic:claude-opus-4-7 + anthropic: anthropic:claude-opus-5 google: google:gemini-3-flash-preview meta: meta:muse-spark-1.1 xai: xai:grok-4.5 @@ -74,9 +74,10 @@ Options: hybrid: both, deduplicated (computer_* + browser_* tools). --native-tool Drive an entitled Anthropic API key through an early-access native schema. computer_20260701 requires --mode computer - and claude-fable-5, claude-opus-4-8, or claude-sonnet-5. - browser_20260701 requires --mode browser and - claude-opus-4-8 or claude-sonnet-5. + and claude-fable-5, claude-opus-4-8, claude-opus-5, + or claude-sonnet-5. browser_20260701 requires --mode + browser and claude-opus-4-8, claude-opus-5, or + claude-sonnet-5. --out Output file for screenshot subcommand --filter Restrict \`cua snapshot\` to interactive elements -o, --output Output format for --print: text (default) | jsonl diff --git a/packages/cli/test/harness-models.test.ts b/packages/cli/test/harness-models.test.ts index a9a9cf85..e951cebc 100644 --- a/packages/cli/test/harness-models.test.ts +++ b/packages/cli/test/harness-models.test.ts @@ -9,6 +9,7 @@ describe("resolveCuaModelRef", () => { it("passes provider-qualified refs through", () => { expect(resolveCuaModelRef("openai:gpt-5.5")).toBe("openai:gpt-5.5"); + expect(resolveCuaModelRef("anthropic:claude-opus-5")).toBe("anthropic:claude-opus-5"); expect(resolveCuaModelRef("meta:muse-spark-1.1")).toBe("meta:muse-spark-1.1"); expect(resolveCuaModelRef("xai:grok-4.5")).toBe("xai:grok-4.5"); expect(resolveCuaModelRef("moonshotai:kimi-k3")).toBe("moonshotai:kimi-k3"); @@ -17,6 +18,7 @@ describe("resolveCuaModelRef", () => { it("accepts bare ids when they match exactly one catalog entry", () => { expect(resolveCuaModelRef("gpt-5.5")).toBe("openai:gpt-5.5"); + expect(resolveCuaModelRef("claude-opus-5")).toBe("anthropic:claude-opus-5"); expect(resolveCuaModelRef("muse-spark-1.1")).toBe("meta:muse-spark-1.1"); expect(resolveCuaModelRef("grok-4.5")).toBe("xai:grok-4.5"); expect(resolveCuaModelRef("kimi-k3")).toBe("moonshotai:kimi-k3"); diff --git a/skills/cua-cli/SKILL.md b/skills/cua-cli/SKILL.md index 361917dc..908646dd 100644 --- a/skills/cua-cli/SKILL.md +++ b/skills/cua-cli/SKILL.md @@ -45,7 +45,7 @@ These resolve a natural-language description with an LLM, so they need the model Useful flags: - `-m ` — pick the LLM for model-mediated subcommands (default `gpt-5.5`). - Other good picks: `claude-opus-4-7`, `gemini-3-flash-preview`, `n1.5-latest`. + Other good picks: `claude-opus-5`, `gemini-3-flash-preview`, `n1.5-latest`. - `cua models` — list supported `-m` values and their providers; filter with `cua models -p openai|anthropic|google|yutori|tzafon` (`gemini` is accepted as an alias for `google`). Model refs print as `provider:model`