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
13 changes: 11 additions & 2 deletions apps/app/.ladle/model-picker-query-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { SystemExecutionOptionsResponse } from "@bb/server-contract";
import { systemExecutionOptionsQueryKey } from "../src/hooks/queries/query-keys";
import type { PickerOption } from "../src/components/pickers/OptionPicker";
import {
STORY_CLAUDE_CODE_MORE_MODELS,
STORY_CLAUDE_CODE_MODELS,
STORY_CLAUDE_REASONING,
STORY_CODEX_MODELS,
Expand Down Expand Up @@ -81,9 +82,11 @@ function makeSupportedReasoningEfforts(
function makeAvailableModels({
models,
reasoningOptions,
markFirstDefault = true,
}: {
models: readonly PickerOption<string>[];
reasoningOptions: readonly PickerOption<ReasoningLevel>[];
markFirstDefault?: boolean;
}): AvailableModel[] {
const defaultReasoningEffort =
reasoningOptions.find((option) => option.value === "medium")?.value ??
Expand All @@ -99,17 +102,18 @@ function makeAvailableModels({
description: "",
supportedReasoningEfforts,
defaultReasoningEffort,
isDefault: index === 0,
isDefault: markFirstDefault && index === 0,
}));
}

function makeExecutionOptions(
models: AvailableModel[],
selectedOnlyModels: AvailableModel[] = [],
): SystemExecutionOptionsResponse {
return {
providers: STORY_PROVIDER_INFOS,
models,
selectedOnlyModels: [],
selectedOnlyModels,
modelLoadError: null,
};
}
Expand Down Expand Up @@ -140,6 +144,11 @@ function createStoryQueryClient(): QueryClient {
models: STORY_CLAUDE_CODE_MODELS,
reasoningOptions: STORY_CLAUDE_REASONING,
}),
makeAvailableModels({
models: STORY_CLAUDE_CODE_MORE_MODELS,
reasoningOptions: STORY_CLAUDE_REASONING,
markFirstDefault: false,
}),
),
pi: makeExecutionOptions(
makeAvailableModels({
Expand Down
9 changes: 7 additions & 2 deletions apps/app/.ladle/story-fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,13 @@ export const STORY_CODEX_MODELS: readonly PickerOption<string>[] = [
];

export const STORY_CLAUDE_CODE_MODELS: readonly PickerOption<string>[] = [
{ value: "claude-opus-4-7-1m", label: "Claude Opus 4.7 (1M)" },
{ value: "claude-opus-4-7", label: "Claude Opus 4.7" },
{ value: "claude-fable-5", label: "Claude Fable 5" },
{ value: "claude-opus-4-8[1m]", label: "Claude Opus 4.8 (1M)" },
{ value: "claude-sonnet-5", label: "Claude Sonnet 5" },
];

export const STORY_CLAUDE_CODE_MORE_MODELS: readonly PickerOption<string>[] = [
{ value: "claude-sonnet-4-6[1m]", label: "Claude Sonnet 4.6 (1M)" },
{ value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
{ value: "claude-haiku-4-5", label: "Claude Haiku 4.5" },
];
Expand Down
16 changes: 15 additions & 1 deletion apps/app/src/components/pickers/ModelReasoningPicker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ModelReasoningPicker } from "./ModelReasoningPicker";
import { StoryCard, StoryRow } from "../../../.ladle/story-card";
import {
STORY_CLAUDE_CODE_MODELS,
STORY_CLAUDE_CODE_MORE_MODELS,
STORY_CLAUDE_REASONING,
STORY_CODEX_MODELS,
STORY_CODEX_REASONING,
Expand Down Expand Up @@ -52,8 +53,9 @@ const codexBase = {
const claudeBase = {
...codexBase,
selectedProviderId: "claude-code",
modelValue: "claude-sonnet-4-6",
modelValue: "claude-sonnet-5",
modelOptions: STORY_CLAUDE_CODE_MODELS,
moreModelOptions: STORY_CLAUDE_CODE_MORE_MODELS,
reasoningOptions: STORY_CLAUDE_REASONING,
showFastModeToggle: false,
};
Expand All @@ -67,6 +69,15 @@ const MODEL_OPTIONS_BY_PROVIDER_ID: Record<
pi: STORY_PI_MODELS,
};

const MORE_MODEL_OPTIONS_BY_PROVIDER_ID: Record<
string,
readonly (typeof STORY_CODEX_MODELS)[number][]
> = {
codex: [],
"claude-code": STORY_CLAUDE_CODE_MORE_MODELS,
pi: [],
};

const REASONING_OPTIONS_BY_PROVIDER_ID: Record<
string,
readonly (typeof STORY_CODEX_REASONING)[number][]
Expand Down Expand Up @@ -147,6 +158,8 @@ function ModelReasoningPickerInteractive() {
const [selectedProviderId, setSelectedProviderId] = useState("codex");
const modelOptions =
MODEL_OPTIONS_BY_PROVIDER_ID[selectedProviderId] ?? STORY_CODEX_MODELS;
const moreModelOptions =
MORE_MODEL_OPTIONS_BY_PROVIDER_ID[selectedProviderId] ?? [];
const reasoningOptions =
REASONING_OPTIONS_BY_PROVIDER_ID[selectedProviderId] ??
STORY_CODEX_REASONING;
Expand All @@ -165,6 +178,7 @@ function ModelReasoningPickerInteractive() {
}}
modelValue={modelValue}
modelOptions={modelOptions}
moreModelOptions={moreModelOptions}
reasoningValue={reasoningValue}
reasoningOptions={reasoningOptions}
showFastModeToggle={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { StoryCard, StoryRow } from "../../../.ladle/story-card";
import {
makeEnvironment,
makeExecutionControlsProps,
STORY_CLAUDE_CODE_MORE_MODELS,
STORY_CLAUDE_CODE_MODELS,
STORY_CLAUDE_REASONING,
STORY_CODEX_MODELS,
Expand Down Expand Up @@ -78,10 +79,10 @@ const claudePlanExecution = makeExecutionControlsProps({
displayName: "Claude Code",
},
model: {
active: { model: "claude-sonnet-4-6" },
selected: "claude-sonnet-4-6",
active: { model: "claude-sonnet-5" },
selected: "claude-sonnet-5",
options: STORY_CLAUDE_CODE_MODELS,
moreOptions: [],
moreOptions: STORY_CLAUDE_CODE_MORE_MODELS,
isLoading: false,
loadFailed: false,
onChange: noop,
Expand Down
13 changes: 7 additions & 6 deletions apps/app/src/components/promptbox/NewThreadPromptBox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
HOST_IDS,
PROJECT_IDS,
STORY_BRANCH_OPTIONS,
STORY_CLAUDE_CODE_MORE_MODELS,
STORY_PROJECTS,
STORY_PROJECT_SOURCES,
STORY_WORKTREE_OPTIONS,
Expand Down Expand Up @@ -512,14 +513,14 @@ function ClaudeProviderRow() {
...baseExecution,
provider: { ...baseExecution.provider, selectedId: "claude-code" },
model: {
active: { model: "claude-sonnet-4-6" },
selected: "claude-sonnet-4-6",
active: { model: "claude-sonnet-5" },
selected: "claude-sonnet-5",
options: [
{ value: "claude-opus-4-7", label: "Claude Opus 4.7" },
{ value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
{ value: "claude-haiku-4-5", label: "Claude Haiku 4.5" },
{ value: "claude-fable-5", label: "Claude Fable 5" },
{ value: "claude-opus-4-8[1m]", label: "Claude Opus 4.8 (1M)" },
{ value: "claude-sonnet-5", label: "Claude Sonnet 5" },
],
moreOptions: [],
moreOptions: STORY_CLAUDE_CODE_MORE_MODELS,
isLoading: false,
loadFailed: false,
onChange: noop,
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"test:integration": "vitest run --config vitest.integration.config.ts"
},
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.3.162",
"@anthropic-ai/claude-agent-sdk": "^0.3.197",
"@anthropic-ai/sdk": "^0.93.0",
"@bb/agent-providers": "workspace:*",
"@bb/domain": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1486,25 +1486,16 @@ describe("bridge", () => {
isDefault: false,
}),
expect.objectContaining({
id: "claude-sonnet-4-6[1m]",
model: "claude-sonnet-4-6[1m]",
displayName: "Sonnet 4.6 (1M)",
isDefault: false,
}),
expect.objectContaining({
id: "claude-sonnet-4-6",
model: "claude-sonnet-4-6",
displayName: "Sonnet 4.6",
isDefault: false,
}),
expect.objectContaining({
id: "claude-haiku-4-5",
model: "claude-haiku-4-5",
displayName: "Haiku 4.5",
id: "claude-sonnet-5",
model: "claude-sonnet-5",
displayName: "Sonnet 5",
isDefault: false,
}),
]);
expect(selectedOnlyModels.map((model) => model.model)).toEqual([
"claude-sonnet-4-6[1m]",
"claude-sonnet-4-6",
"claude-haiku-4-5",
"claude-opus-4-8",
"claude-opus-4-7",
"claude-opus-4-6[1m]",
Expand Down
1 change: 1 addition & 0 deletions packages/agent-runtime/src/claude-code/error-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function getProviderErrorCategoryFromClaudeCode(
case "rate_limit":
return "rate-limit";
case "invalid_request":
case "model_not_found":
return "bad-request";
case "server_error":
return "internal";
Expand Down
44 changes: 39 additions & 5 deletions packages/agent-runtime/src/claude-code/model-list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ describe("listClaudeCodeModels", () => {
"claude-mythos-5",
"claude-opus-4-8[1m]",
"claude-opus-4-7[1m]",
"claude-sonnet-4-6[1m]",
"claude-sonnet-4-6",
"claude-haiku-4-5",
"claude-sonnet-5",
]);
});

Expand All @@ -27,8 +25,17 @@ describe("listClaudeCodeModels", () => {
);
});

it("routes non-active Opus models to the selected-only bucket", () => {
it("routes secondary and non-active Claude models to the selected-only bucket", () => {
const { models, selectedOnlyModels } = listClaudeCodeModels();
expect(models.map((model) => model.model)).not.toContain(
"claude-sonnet-4-6[1m]",
);
expect(models.map((model) => model.model)).not.toContain(
"claude-sonnet-4-6",
);
expect(models.map((model) => model.model)).not.toContain(
"claude-haiku-4-5",
);
expect(models.map((model) => model.model)).not.toContain("claude-opus-4-8");
expect(models.map((model) => model.model)).not.toContain("claude-opus-4-7");
expect(models.map((model) => model.model)).not.toContain("claude-opus-4-6");
Expand Down Expand Up @@ -106,10 +113,12 @@ describe("listClaudeCodeModels", () => {
"ultracode",
"max",
]);
expect(effortLevelsByModel.get("claude-sonnet-4-6")).toEqual([
expect(effortLevelsByModel.get("claude-sonnet-5")).toEqual([
"low",
"medium",
"high",
"xhigh",
"ultracode",
"max",
]);
});
Expand All @@ -119,6 +128,9 @@ describe("listClaudeCodeModels", () => {
const activeIds = models.map((model) => model.model);
const selectedOnlyIds = selectedOnlyModels.map((model) => model.model);
for (const selectedOnlyModel of [
"claude-sonnet-4-6[1m]",
"claude-sonnet-4-6",
"claude-haiku-4-5",
"claude-opus-4-8",
"claude-opus-4-7",
"claude-opus-4-6[1m]",
Expand Down Expand Up @@ -151,5 +163,27 @@ describe("listClaudeCodeModels", () => {
]),
}),
);
expect(
selectedOnlyModels.find((model) => model.model === "claude-sonnet-4-6"),
).toEqual(
expect.objectContaining({
displayName: "Sonnet 4.6",
defaultReasoningEffort: "medium",
supportedReasoningEfforts: expect.arrayContaining([
expect.objectContaining({ reasoningEffort: "max" }),
]),
}),
);
expect(
selectedOnlyModels.find((model) => model.model === "claude-haiku-4-5"),
).toEqual(
expect.objectContaining({
displayName: "Haiku 4.5",
defaultReasoningEffort: "low",
supportedReasoningEfforts: [
expect.objectContaining({ reasoningEffort: "low" }),
],
}),
);
});
});
21 changes: 15 additions & 6 deletions packages/agent-runtime/src/claude-code/model-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const CLAUDE_MYTHOS_5_MODEL = "claude-mythos-5";
const CLAUDE_OPUS_4_8_MODEL = "claude-opus-4-8";
const CLAUDE_OPUS_4_7_MODEL = "claude-opus-4-7";
const CLAUDE_OPUS_4_6_MODEL = "claude-opus-4-6";
const CLAUDE_SONNET_5_MODEL = "claude-sonnet-5";
const CLAUDE_SONNET_4_6_MODEL = "claude-sonnet-4-6";
const CLAUDE_HAIKU_4_5_MODEL = "claude-haiku-4-5";

Expand All @@ -65,9 +66,9 @@ function withOneMillionContext(model: string): string {

const DEFAULT_CLAUDE_CODE_MODEL = withOneMillionContext(CLAUDE_OPUS_4_8_MODEL);

// Keep the active catalog version-pinned. Moving aliases and retired model
// strings live in the selected-only catalog so existing stored selections can
// render with their proper label without being offered as fresh choices.
// Keep the active catalog version-pinned. Secondary "More models" choices,
// moving aliases, and retired model strings live in the selected-only catalog
// so existing stored selections can render with their proper label.
const CLAUDE_CODE_CATALOG: readonly ClaudeCodeCatalogEntry[] = [
{
id: CLAUDE_FABLE_5_MODEL,
Expand Down Expand Up @@ -102,6 +103,17 @@ const CLAUDE_CODE_CATALOG: readonly ClaudeCodeCatalogEntry[] = [
supportedReasoningEfforts: OPUS_4_7_REASONING_EFFORTS,
defaultReasoningEffort: "medium",
},
{
id: CLAUDE_SONNET_5_MODEL,
model: CLAUDE_SONNET_5_MODEL,
displayName: "Sonnet 5",
description: "Sonnet 5 for everyday coding tasks with deeper reasoning",
supportedReasoningEfforts: XHIGH_CAPABLE_REASONING_EFFORTS,
defaultReasoningEffort: "medium",
},
];

const CLAUDE_CODE_SELECTED_ONLY_CATALOG: readonly ClaudeCodeCatalogEntry[] = [
{
id: withOneMillionContext(CLAUDE_SONNET_4_6_MODEL),
model: withOneMillionContext(CLAUDE_SONNET_4_6_MODEL),
Expand All @@ -126,9 +138,6 @@ const CLAUDE_CODE_CATALOG: readonly ClaudeCodeCatalogEntry[] = [
supportedReasoningEfforts: HAIKU_REASONING_EFFORTS,
defaultReasoningEffort: "low",
},
];

const CLAUDE_CODE_SELECTED_ONLY_CATALOG: readonly ClaudeCodeCatalogEntry[] = [
{
id: CLAUDE_OPUS_4_8_MODEL,
model: CLAUDE_OPUS_4_8_MODEL,
Expand Down
1 change: 1 addition & 0 deletions packages/agent-runtime/src/claude-code/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export const claudeAssistantMessageErrorSchema = z.enum([
"billing_error",
"rate_limit",
"invalid_request",
"model_not_found",
"server_error",
"unknown",
"max_output_tokens",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe("resolveClaudeModelContextWindowHint", () => {
});

it("uses the default Claude context window for non-1M models", () => {
expect(resolveClaudeModelContextWindowHint("claude-sonnet-4-6")).toBe(
expect(resolveClaudeModelContextWindowHint("claude-sonnet-5")).toBe(
200_000,
);
});
Expand Down
Loading
Loading