Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/auto-cleanup-fixup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,6 @@ jobs:
FAILED_RUN_ID: ${{ github.event.workflow_run.id }}
run: |
bunx mux@next run \
--model anthropic:claude-opus-5 \
--model anthropic:claude-opus-5-1 \
--thinking xhigh \
< .github/prompts/auto-cleanup-fixup.md
2 changes: 1 addition & 1 deletion .github/workflows/auto-cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,6 @@ jobs:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
bunx @coder/xum@next run \
--model anthropic:claude-opus-5 \
--model anthropic:claude-opus-5-1 \
--thinking xhigh \
< .github/prompts/auto-cleanup.md
4 changes: 2 additions & 2 deletions .github/workflows/nightly-terminal-bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
workflow_dispatch:
inputs:
models:
description: 'Models to test (comma-separated, or "all" for opus-5 + gpt-5.6-sol + google/gemini-3-pro-preview + google/gemini-3-flash-preview + google/gemini-3.7-flash)'
description: 'Models to test (comma-separated, or "all" for opus-5-1 + gpt-5.6-sol + google/gemini-3-pro-preview + google/gemini-3-flash-preview + google/gemini-3.7-flash)'
required: false
default: "all"
type: string
Expand Down Expand Up @@ -129,7 +129,7 @@ jobs:
INPUT_MODELS: ${{ inputs.models }}
run: |
if [ "$INPUT_MODELS" = "all" ] || [ -z "$INPUT_MODELS" ]; then
echo 'models=["anthropic/claude-opus-5","openai/gpt-5.6-sol","google/gemini-3-pro-preview","google/gemini-3-flash-preview","google/gemini-3.7-flash"]' >> "$GITHUB_OUTPUT"
echo 'models=["anthropic/claude-opus-5-1","openai/gpt-5.6-sol","google/gemini-3-pro-preview","google/gemini-3-flash-preview","google/gemini-3.7-flash"]' >> "$GITHUB_OUTPUT"
Comment thread
ThomasK33 marked this conversation as resolved.
else
# Convert comma-separated to JSON array
models_json=$(echo "$INPUT_MODELS" | jq -R -s -c 'split(",") | map(gsub("^\\s+|\\s+$"; ""))')
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/terminal-bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_call:
inputs:
model_name:
description: "Model to use (e.g., anthropic/claude-opus-5, openai/gpt-5.6-sol)"
description: "Model to use (e.g., anthropic/claude-opus-5-1, openai/gpt-5.6-sol)"
required: false
type: string
dataset:
Expand Down Expand Up @@ -98,7 +98,7 @@ on:
required: false
type: string
model_name:
description: "Model to use (e.g., anthropic/claude-opus-5, openai/gpt-5.6-sol)"
description: "Model to use (e.g., anthropic/claude-opus-5-1, openai/gpt-5.6-sol)"
required: false
type: string
mux_run_args:
Expand Down
7 changes: 7 additions & 0 deletions benchmarks/terminal_bench/prepare_leaderboard_submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@
"model_org_display_name": "Anthropic",
"folder_name": "Claude-Opus-5",
},
"anthropic/claude-opus-5-1": {
"model_name": "claude-opus-5-1",
"model_provider": "anthropic",
"model_display_name": "Claude Opus 5.1",
"model_org_display_name": "Anthropic",
"folder_name": "Claude-Opus-5.1",
},
# Keep historical GPT metadata alongside the current GPT-5.6 Sol bench target
# so mixed or older artifact sets still map to the canonical leaderboard names.
"openai/gpt-5.2": {
Expand Down
2 changes: 1 addition & 1 deletion docs/config/models.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Xum ships with curated models kept up to date with the frontier. Use any custom
| ---------------------- | ----------------------------- | ------------------------------------------------------------ | ------- |
| Fable 5.1 | anthropic:claude-fable-5-1 | `fable` | |
| Mythos 5 | anthropic:claude-mythos-5 | `mythos` | |
| Opus 5 | anthropic:claude-opus-5 | `opus` | βœ“ |
| Opus 5.1 | anthropic:claude-opus-5-1 | `opus` | βœ“ |
| Sonnet 5 | anthropic:claude-sonnet-5 | `sonnet` | |
| Haiku 4.5 | anthropic:claude-haiku-4-5 | `haiku` | |
| GPT-5.6 Sol | openai:gpt-5.6-sol | `gpt`, `sol` | |
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/github-actions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ jobs:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
bunx @coder/xum@next run \
--model anthropic:claude-opus-5 \
--model anthropic:claude-opus-5-1 \
--thinking xhigh \
< .github/prompts/auto-cleanup.md
```
Expand Down
4 changes: 2 additions & 2 deletions src/browser/features/ChatInput/useCreationWorkspace.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1360,7 +1360,7 @@ describe("useCreationWorkspace", () => {
});
expect(onWorkspaceCreated.mock.calls[0][1]).toEqual({
autoNavigate: true,
pendingStreamModel: "anthropic:claude-opus-5",
pendingStreamModel: "anthropic:claude-opus-5-1",
markPendingInitialSend: false,
});
});
Expand Down Expand Up @@ -1838,7 +1838,7 @@ describe("useCreationWorkspace", () => {
expect(onWorkspaceCreated.mock.calls.length).toBe(1);
expect(onWorkspaceCreated.mock.calls[0][1]).toEqual({
autoNavigate: true,
pendingStreamModel: "anthropic:claude-opus-5",
pendingStreamModel: "anthropic:claude-opus-5-1",
markPendingInitialSend: true,
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ describe("xum CLI", () => {
test("shows default model as opus", async () => {
const result = await runCli(["run", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("anthropic:claude-opus-5");
expect(result.stdout).toContain("anthropic:claude-opus-5-1");
});

test("--service-tier has no default auto", async () => {
Expand Down
7 changes: 7 additions & 0 deletions src/common/config/schemas/appConfigOnDisk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ export const AppConfigMigrationsSchema = z
* source key.
*/
defaultModelFallbacksSeededFable51: z.boolean().optional(),
/**
* One-time chain-target migration after the opus alias moved to Opus 5.1:
* chains still exactly equal to the superseded shipped default
* (Fable 5.1 β†’ Opus 5) move to the current default (Fable 5.1 β†’ Opus 5.1);
* any user-edited chain is left untouched.
*/
defaultModelFallbacksSeededOpus51: z.boolean().optional(),
/** One-time migration from the legacy auto-delete default to persistent sub-agents. */
persistentSubagentsDefaulted: z.boolean().optional(),
})
Expand Down
16 changes: 15 additions & 1 deletion src/common/constants/knownModels.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
*/

import { describe, test, expect } from "@jest/globals";
import { KNOWN_MODELS, MODEL_ABBREVIATIONS } from "@/common/constants/knownModels";
import {
KNOWN_MODELS,
MODEL_ABBREVIATIONS,
TOKENIZER_MODEL_OVERRIDES,
} from "@/common/constants/knownModels";
import modelsJson from "@/common/utils/tokens/models.json";
import { findMissingKnownModels } from "@/common/utils/tokens/updateModelsData";

Expand All @@ -19,6 +23,16 @@ describe("Known Models Integration", () => {
}
});

test("opus alias tracks Opus 5.1 and retired ids keep tokenizer overrides", () => {
expect(MODEL_ABBREVIATIONS.opus).toBe("anthropic:claude-opus-5-1");
expect(KNOWN_MODELS.OPUS.id).toBe("anthropic:claude-opus-5-1");
// Exact-id lookup for retired-but-documented custom model strings must keep
// resolving to the Opus 4.5 approximation instead of falling back (with a
// warning) to the generic per-provider tokenizer.
expect(TOKENIZER_MODEL_OVERRIDES["anthropic:claude-opus-5"]).toBe("anthropic/claude-opus-4.5");
expect(TOKENIZER_MODEL_OVERRIDES[KNOWN_MODELS.OPUS.id]).toBe("anthropic/claude-opus-4.5");
});

test("gemini-flash resolves to the stable Gemini 3.7 Flash model", () => {
expect(MODEL_ABBREVIATIONS["gemini-flash"]).toBe("google:gemini-3.7-flash");
});
Expand Down
11 changes: 6 additions & 5 deletions src/common/constants/knownModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ const MODEL_DEFINITIONS = {
// approximate counting; real usage can run ~1.0-1.3x higher.
tokenizerOverride: "anthropic/claude-opus-4.5",
},
// Claude Opus 5 - released July 24, 2026. Successor to Opus 4.8 at the same pricing
// ($5/M input, $25/M output). API id `claude-opus-5`; Opus 4.8 stays usable as the
// custom model string `anthropic:claude-opus-4-8`.
// Claude Opus 5.1 - successor to Opus 5 (released July 24, 2026) at unchanged pricing
// ($5/M input, $25/M output). API id `claude-opus-5-1`; Opus 5 stays usable as the
// custom model string `anthropic:claude-opus-5`.
OPUS: {
provider: "anthropic",
providerModelId: "claude-opus-5",
providerModelId: "claude-opus-5-1",
aliases: ["opus"],
warm: true,
// Opus 5 uses the newer Opus 4.7+ tokenizer (~30% more tokens for the same text),
// Opus 5.1 uses the newer Opus 4.7+ tokenizer (~30% more tokens for the same text),
// which isn't published upstream; reuse Opus 4.5 for approximate counting. Real
// usage can run ~1.0-1.3x higher than this estimate (same situation as FABLE above).
tokenizerOverride: "anthropic/claude-opus-4.5",
Expand Down Expand Up @@ -280,6 +280,7 @@ export const MODEL_ABBREVIATIONS: Record<string, string> = Object.fromEntries(
// lookup does not fall back to the generic per-provider tokenizer.
const LEGACY_TOKENIZER_MODEL_OVERRIDES: Record<string, string> = {
"anthropic:claude-fable-5": "anthropic/claude-opus-4.5",
"anthropic:claude-opus-5": "anthropic/claude-opus-4.5",
"anthropic:claude-opus-4-8": "anthropic/claude-opus-4.5",
};

Expand Down
1 change: 1 addition & 0 deletions src/common/utils/ai/modelDisplay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe("formatCompactModelDisplayName", () => {

test("falls back to the full display name when no shorter label is clearer", () => {
expect(formatCompactModelDisplayName("claude-opus-5")).toBe("Opus 5");
expect(formatCompactModelDisplayName("claude-opus-5-1")).toBe("Opus 5.1");
expect(formatCompactModelDisplayName("gpt-5.3-codex-spark")).toBe("Spark 5.3");
});
});
Expand Down
24 changes: 22 additions & 2 deletions src/common/utils/ai/modelFallbacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export const MODEL_FALLBACK_CHAIN_LIMIT = 3;
*
* Seeded into the config exactly once, guarded by
* migrations.defaultModelFallbacksSeeded (plus the one-shot
* defaultModelFallbacksSeededFable51 re-seed for the key move to Fable 5.1)
* defaultModelFallbacksSeededFable51 re-seed for the key move to Fable 5.1,
* and the one-shot defaultModelFallbacksSeededOpus51 target migration for the
* Opus 5.1 promotion, see SUPERSEDED_DEFAULT_MODEL_FALLBACKS)
* β€” on versions that know the flag,
* user edits or deletions of these chains are never overridden by updates.
* (Versions predating the flag strip it on save, so a downgrade→save→
Expand All @@ -37,14 +39,32 @@ export const DEFAULT_MODEL_FALLBACKS: ModelFallbacks = {
* refusal fallback intact.
*/
export const LEGACY_DEFAULT_MODEL_FALLBACKS: ModelFallbacks = {
"anthropic:claude-fable-5": { models: [KNOWN_MODELS.OPUS.id] },
// Target pinned to the literal Opus 5 id (not KNOWN_MODELS.OPUS.id): this
// chain exists for downgraded pre-5.1 builds, whose own shipped default was
// Fable 5 β†’ Opus 5. Pinning keeps the seeded bytes identical no matter which
// build ran the original seed pass.
"anthropic:claude-fable-5": { models: ["anthropic:claude-opus-5"] },
};

/**
* Shipped defaults superseded by the Opus 5.1 promotion, keyed like
* DEFAULT_MODEL_FALLBACKS. Guarded by migrations.defaultModelFallbacksSeededOpus51,
* config load rewrites a chain to the current default exactly once IF the stored
* entry still deep-equals the superseded default (same single-model chain, no
* enabled/triggers customization). Any deviation is user intent and is never
* touched; the legacy `anthropic:claude-fable-5` chain is deliberately not
* migrated so downgraded builds keep their expected bytes on disk.
*/
export const SUPERSEDED_DEFAULT_MODEL_FALLBACKS: ModelFallbacks = {
[KNOWN_MODELS.FABLE.id]: { models: ["anthropic:claude-opus-5"] },
};
// Deep-freeze: entries are spread by reference into live configs (fresh-install
// defaults, seed merge). Accidental in-place mutation must crash fast instead
// of silently corrupting the process-wide default.
for (const entry of [
...Object.values(Object.freeze(DEFAULT_MODEL_FALLBACKS)),
...Object.values(Object.freeze(LEGACY_DEFAULT_MODEL_FALLBACKS)),
...Object.values(Object.freeze(SUPERSEDED_DEFAULT_MODEL_FALLBACKS)),
]) {
Object.freeze(entry);
Object.freeze(entry.models);
Expand Down
8 changes: 7 additions & 1 deletion src/common/utils/ai/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,15 @@ describe("Anthropic 1M context classification", () => {
expect(hasNative1MContext("anthropic:claude-sonnet-4-5")).toBe(false);
});

it("treats Opus 4.6 to 4.8, Opus 5, and Sonnet 4.6 as native 1M models", () => {
it("treats Opus 4.6 to 4.8, Opus 5, Opus 5.1, and Sonnet 4.6 as native 1M models", () => {
expect(getAnthropic1MContextMode("anthropic:claude-opus-5")).toBe("native");
expect(getAnthropic1MContextMode("anthropic:claude-opus-5-20260724")).toBe("native");
// The claude-opus-5 pattern only tolerates date suffixes, so the 5.1 id
// needs (and has) its own pattern.
expect(getAnthropic1MContextMode("anthropic:claude-opus-5-1")).toBe("native");
expect(getAnthropic1MContextMode("mux-gateway:anthropic/claude-opus-5-1")).toBe("native");
expect(hasNative1MContext("anthropic:claude-opus-5-1")).toBe(true);
expect(supports1MContext("anthropic:claude-opus-5-1")).toBe(false);
expect(getAnthropic1MContextMode("anthropic:claude-opus-4-8")).toBe("native");
expect(getAnthropic1MContextMode("anthropic:claude-opus-4-8-20260528")).toBe("native");
expect(getAnthropic1MContextMode("anthropic:claude-opus-4-6")).toBe("native");
Expand Down
1 change: 1 addition & 0 deletions src/common/utils/ai/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ const ANTHROPIC_NATIVE_1M_PATTERNS = [
new RegExp(`^claude-fable-5-1${OPTIONAL_VERSION_SUFFIX}$`, "i"),
new RegExp(`^claude-mythos-5${OPTIONAL_VERSION_SUFFIX}$`, "i"),
new RegExp(`^claude-opus-5${OPTIONAL_VERSION_SUFFIX}$`, "i"),
new RegExp(`^claude-opus-5-1${OPTIONAL_VERSION_SUFFIX}$`, "i"),
new RegExp(`^claude-opus-4-8${OPTIONAL_VERSION_SUFFIX}$`, "i"),
new RegExp(`^claude-opus-4-7${OPTIONAL_VERSION_SUFFIX}$`, "i"),
new RegExp(`^claude-opus-4-6${OPTIONAL_VERSION_SUFFIX}$`, "i"),
Expand Down
7 changes: 6 additions & 1 deletion src/common/utils/ai/providerOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,12 @@ describe("buildProviderOptions - Anthropic", () => {
// Native-xhigh models (Opus 4.7+ / Sonnet 5+): xhigh is a distinct native
// effort and adaptive thinking requires `display: "summarized"` to return
// thinking content.
for (const model of ["claude-opus-4-7", "claude-opus-5", "claude-sonnet-5"] as const) {
for (const model of [
"claude-opus-4-7",
"claude-opus-5",
"claude-opus-5-1",
"claude-sonnet-5",
] as const) {
describe(`${model} (native xhigh effort + summarized display)`, () => {
for (const { thinking, expectedThinking, effort } of [
{
Expand Down
10 changes: 10 additions & 0 deletions src/common/utils/thinking/policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,16 @@ describe("getThinkingPolicyForModel", () => {
"xhigh",
"max",
]);
// Opus 5.1 rides the same Opus 5+ wildcard: full 6-level ladder, and
// (unlike Mythos-class models) "off" stays available.
expect(getThinkingPolicyForModel("anthropic:claude-opus-5-1")).toEqual([
"off",
"low",
"medium",
"high",
"xhigh",
"max",
]);
expect(getThinkingPolicyForModel("anthropic:claude-opus-5-0")).toEqual([
"off",
"low",
Expand Down
19 changes: 19 additions & 0 deletions src/common/utils/tokens/models-extra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,25 @@ export const modelsExtra: Record<string, ModelData> = {
supports_response_schema: true,
},

// Claude Opus 5.1 - successor to Opus 5 at the same pricing/shape: $5/M input,
// $25/M output, cache write 1.25x input / cache read 0.1x input, native 1M
// context, 128K max output, full effort ladder with native xhigh and max.
"claude-opus-5-1": {
max_input_tokens: 1000000,
max_output_tokens: 128000,
input_cost_per_token: 0.000005, // $5 per million input tokens
output_cost_per_token: 0.000025, // $25 per million output tokens
cache_creation_input_token_cost: 0.00000625, // $6.25 per million tokens (1.25Γ— input)
cache_read_input_token_cost: 0.0000005, // $0.50 per million tokens (0.1Γ— input)
litellm_provider: "anthropic",
mode: "chat",
supports_function_calling: true,
supports_vision: true,
supports_pdf_input: true,
supports_reasoning: true,
supports_response_schema: true,
},

// Claude Opus 5 - Released July 24, 2026
// Same pricing/shape as Opus 4.8: $5/M input, $25/M output, native 1M context
// (both default and maximum), 128K max output, full effort ladder with native
Expand Down
1 change: 1 addition & 0 deletions src/common/utils/tools/tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ describe("supportsAnthropicNativeWebFetch", () => {
["claude-mythos-5", true],
// Two-segment IDs at/after the 4.6 cutoff.
["claude-fable-5-1", true],
["claude-opus-5-1", true],
["claude-sonnet-4-6", true],
["claude-opus-4-6", true],
["claude-opus-4-8", true],
Expand Down
Loading
Loading