Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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"
Expand Down
8 changes: 4 additions & 4 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down
4 changes: 2 additions & 2 deletions packages/agent/examples/anthropic-native-smoke.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<string, { mode?: CuaMode; nativeTool?: CuaNativeToolSpec }> = {
Expand Down
4 changes: 2 additions & 2 deletions packages/agent/test/e2e.live.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 (`{}`).",
Expand Down
8 changes: 6 additions & 2 deletions packages/ai/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions packages/ai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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");
Expand Down
1 change: 1 addition & 0 deletions packages/ai/docs/supported-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
2 changes: 1 addition & 1 deletion packages/ai/examples/quickstart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 21 additions & 1 deletion packages/ai/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const CUA_MODEL_ANNOTATIONS: Record<CuaProvider, readonly CuaModelAnnotat
anthropic: [
{ match: { kind: "family", family: "claude-3-7-sonnet" }, source: "https://docs.anthropic.com/en/docs/build-with-claude/computer-use" },
{ match: { kind: "family", family: "claude-opus-4" }, source: "https://docs.anthropic.com/en/docs/build-with-claude/computer-use" },
{ match: { kind: "family", family: "claude-opus-5" }, source: "https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool" },
{ match: { kind: "family", family: "claude-sonnet-4" }, source: "https://docs.anthropic.com/en/docs/build-with-claude/computer-use" },
{ match: { kind: "family", family: "claude-sonnet-5" }, source: "https://docs.anthropic.com/en/docs/build-with-claude/computer-use" },
{ match: { kind: "family", family: "claude-haiku-4" }, source: "https://docs.anthropic.com/en/docs/build-with-claude/computer-use" },
Expand Down Expand Up @@ -112,7 +113,9 @@ export const CUA_MODEL_ANNOTATIONS: Record<CuaProvider, readonly CuaModelAnnotat
// in CUA_MODEL_ANNOTATIONS above so the support filter recognizes it.
const CUA_MODEL_OVERRIDES: Record<CuaProvider, readonly Model<Api>[]> = {
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")],
Expand Down Expand Up @@ -281,6 +284,23 @@ function isCuaFamilyMatch(id: string, family: string): boolean {
.every((segment) => /^\d+$/.test(segment));
}

function anthropicOpus5Model(): Model<Api> {
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<CuaProvider, "xai" | "moonshotai">, id: string, name: string): Model<Api> {
const base = {
id,
Expand Down
4 changes: 2 additions & 2 deletions packages/ai/src/native-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@ const NATIVE_TOOL_INFO: Record<CuaNativeToolType, NativeToolInfo> = {
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: {
mode: "browser",
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.",
},
};
Expand Down
1 change: 1 addition & 0 deletions packages/ai/src/providers/anthropic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ function isAdaptiveThinkingModel(model: Model<Api>): 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")
Expand Down
2 changes: 1 addition & 1 deletion packages/ai/test/anthropic-native.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
{
Expand Down
1 change: 1 addition & 0 deletions packages/ai/test/anthropic-payload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/ai/test/computer-tool.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
15 changes: 15 additions & 0 deletions packages/ai/test/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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();
});

Expand Down
6 changes: 3 additions & 3 deletions packages/ai/test/native-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
});
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 "..."
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -74,9 +74,10 @@ Options:
hybrid: both, deduplicated (computer_* + browser_* tools).
--native-tool <type> 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 <file|-> Output file for screenshot subcommand
--filter <interactive> Restrict \`cua snapshot\` to interactive elements
-o, --output <fmt> Output format for --print: text (default) | jsonl
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/test/harness-models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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");
Expand Down
2 changes: 1 addition & 1 deletion skills/cua-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ These resolve a natural-language description with an LLM, so they need the model
Useful flags:

- `-m <model>` — 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`
Expand Down