mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-27 19:52:49 +00:00
feat(claude): add Sonnet 5.5 with its model capabilities (#5583)
Claude Code 2.1.284 introduces Sonnet 5.5 with a native 1M context
window, medium default effort, and adaptive thinking that cannot be
turned off, so it takes the same shape as Opus 5.5 in the manifest.
Sonnet 5.5 rejects `thinking: {"type": "disabled"}` with a 400, so its
entry omits supportsThinkingDisabled and its thinking options have no
off. It also takes the Sonnet family tagline, which moves Sonnet 5 to
Previous release.
The catalog gates the entry on Claude Code 2.1.284, the version that
recognizes the model, so older installs keep their current catalog.
The existing major-minor runtime matching already resolves dashed
gateway IDs such as us.anthropic.claude-sonnet-5-5-20260928-v1:0 to
the new entry without collapsing them onto Sonnet 5.
(cherry picked from commit 4340607093)
This commit is contained in:
@@ -82,10 +82,15 @@ const EXPECTED_CLAUDE_MODELS = [
|
||||
model: "Opus 4.8",
|
||||
descriptionFragment: "Previous release",
|
||||
},
|
||||
{
|
||||
id: "claude-sonnet-5-5",
|
||||
model: "Sonnet 5.5",
|
||||
descriptionFragment: "Best for everyday tasks",
|
||||
},
|
||||
{
|
||||
id: "claude-sonnet-5",
|
||||
model: "Sonnet 5",
|
||||
descriptionFragment: "Best for everyday tasks",
|
||||
descriptionFragment: "Previous release",
|
||||
},
|
||||
{
|
||||
id: "claude-opus-4-7[1m]",
|
||||
@@ -430,7 +435,7 @@ try {
|
||||
"--quiet should print the same ordered model IDs returned by --json",
|
||||
);
|
||||
assert(
|
||||
claudeModelsFromJson.some((m) => m.id === "claude-sonnet-5"),
|
||||
claudeModelsFromJson.some((m) => m.id === "claude-sonnet-5-5"),
|
||||
"captured --json output should include the current Claude everyday model id",
|
||||
);
|
||||
console.log("✓ provider models --quiet outputs model IDs only\n");
|
||||
|
||||
@@ -496,7 +496,7 @@ describe("ClaudeAgentClient.fetchCatalog", () => {
|
||||
const client = new ClaudeAgentClient({
|
||||
logger,
|
||||
resolveBinary: async () => "/test/claude/bin",
|
||||
resolveVersion: async () => "2.1.219",
|
||||
resolveVersion: async () => "2.1.284",
|
||||
runtimeSettings: { env: { CLAUDE_CONFIG_DIR: emptyConfigDir } },
|
||||
});
|
||||
const { models } = await client.fetchCatalog({
|
||||
@@ -515,6 +515,8 @@ describe("ClaudeAgentClient.fetchCatalog", () => {
|
||||
expect(getThinkingIds("claude-opus-4-8")).toContain("ultracode");
|
||||
expect(getThinkingIds("claude-sonnet-5")).toContain("xhigh");
|
||||
expect(getThinkingIds("claude-sonnet-5")).toContain("ultracode");
|
||||
expect(getThinkingIds("claude-sonnet-5-5")).toContain("xhigh");
|
||||
expect(getThinkingIds("claude-sonnet-5-5")).not.toContain("off");
|
||||
expect(getThinkingIds("claude-opus-4-7[1m]")).toContain("ultracode");
|
||||
expect(getThinkingIds("claude-opus-4-7")).toContain("ultracode");
|
||||
expect(getThinkingIds("claude-sonnet-4-6")).not.toContain("ultracode");
|
||||
@@ -1238,6 +1240,26 @@ describe("ClaudeAgentSession features", () => {
|
||||
await session.close();
|
||||
});
|
||||
|
||||
test("rejects disabled thinking on Sonnet 5.5, which only runs with adaptive thinking", async () => {
|
||||
const { queryFactory } = createQueryMock();
|
||||
const client = new ClaudeAgentClient({
|
||||
logger,
|
||||
queryFactory,
|
||||
resolveBinary: async () => "/test/claude/bin",
|
||||
});
|
||||
const session = await client.createSession({
|
||||
provider: "claude",
|
||||
cwd: process.cwd(),
|
||||
model: "claude-sonnet-5-5",
|
||||
});
|
||||
|
||||
await expect(session.setThinkingOption?.("off")).rejects.toThrow(
|
||||
"Thinking option 'off' is not available for model 'claude-sonnet-5-5'",
|
||||
);
|
||||
|
||||
await session.close();
|
||||
});
|
||||
|
||||
test("rejects an initial disabled-thinking config for an unsupported model", async () => {
|
||||
const { queryFactory } = createQueryMock();
|
||||
const client = new ClaudeAgentClient({
|
||||
|
||||
@@ -93,10 +93,19 @@ export const CLAUDE_MODEL_MANIFEST = [
|
||||
supportsThinkingDisabled: true,
|
||||
supportsFastMode: true,
|
||||
},
|
||||
{
|
||||
id: "claude-sonnet-5-5",
|
||||
label: "Sonnet 5.5",
|
||||
description: "Sonnet 5.5 · Best for everyday tasks",
|
||||
minimumClaudeCodeVersion: "2.1.284",
|
||||
defaultThinkingOptionId: "medium",
|
||||
contextWindowMaxTokens: 1_000_000,
|
||||
effortLevels: CLAUDE_EFFORT_LEVELS.xhigh,
|
||||
},
|
||||
{
|
||||
id: "claude-sonnet-5",
|
||||
label: "Sonnet 5",
|
||||
description: "Sonnet 5 · Best for everyday tasks",
|
||||
description: "Sonnet 5 · Previous release",
|
||||
contextWindowMaxTokens: 200_000,
|
||||
effortLevels: CLAUDE_EFFORT_LEVELS.xhigh,
|
||||
supportsThinkingDisabled: true,
|
||||
|
||||
@@ -39,7 +39,7 @@ async function createClaudeConfigDirWithRawSettings(settings: string): Promise<s
|
||||
return configDir;
|
||||
}
|
||||
|
||||
function createCatalogClient(claudeCodeVersion = "2.1.280"): ClaudeAgentClient {
|
||||
function createCatalogClient(claudeCodeVersion = "2.1.284"): ClaudeAgentClient {
|
||||
return new ClaudeAgentClient({
|
||||
logger: createTestLogger(),
|
||||
resolveVersion: async () => claudeCodeVersion,
|
||||
@@ -57,6 +57,7 @@ describe("getClaudeModels", () => {
|
||||
"claude-fable-5[1m]",
|
||||
"claude-opus-4-8[1m]",
|
||||
"claude-opus-4-8",
|
||||
"claude-sonnet-5-5",
|
||||
"claude-sonnet-5",
|
||||
"claude-sonnet-5[1m]",
|
||||
"claude-opus-4-7[1m]",
|
||||
@@ -90,6 +91,7 @@ describe("getClaudeModels", () => {
|
||||
["claude-fable-5[1m]", 1_000_000],
|
||||
["claude-opus-4-8[1m]", 1_000_000],
|
||||
["claude-opus-4-8", 200_000],
|
||||
["claude-sonnet-5-5", 1_000_000],
|
||||
["claude-sonnet-5", 200_000],
|
||||
["claude-sonnet-5[1m]", 1_000_000],
|
||||
["claude-opus-4-7[1m]", 1_000_000],
|
||||
@@ -116,6 +118,9 @@ describe("getClaudeModels", () => {
|
||||
expect(getClaudeModels("2.1.279").find((model) => model.isDefault)?.id).toBe("claude-opus-5");
|
||||
expect(getClaudeModels("2.1.280").map((model) => model.id)).toContain("claude-opus-5-5");
|
||||
expect(getClaudeModels("2.1.280").find((model) => model.isDefault)?.id).toBe("claude-opus-5-5");
|
||||
|
||||
expect(getClaudeModels("2.1.283").map((model) => model.id)).not.toContain("claude-sonnet-5-5");
|
||||
expect(getClaudeModels("2.1.284").map((model) => model.id)).toContain("claude-sonnet-5-5");
|
||||
});
|
||||
|
||||
it("derives thinking options from model effort capabilities", () => {
|
||||
@@ -178,6 +183,8 @@ describe("getClaudeModels", () => {
|
||||
it.each([
|
||||
["claude-opus-5-5", false, "medium"],
|
||||
["claude-opus-5-5-20260401", false, "medium"],
|
||||
["claude-sonnet-5-5", false, "medium"],
|
||||
["claude-sonnet-5-5-20260928", false, "medium"],
|
||||
["claude-opus-5", true, "high"],
|
||||
["claude-opus-5-20260724", true, "high"],
|
||||
["claude-sonnet-5", true, "high"],
|
||||
@@ -467,6 +474,16 @@ describe("normalizeClaudeRuntimeModelId", () => {
|
||||
"claude-opus-5",
|
||||
);
|
||||
});
|
||||
|
||||
it("does not collapse a prefixed Sonnet 5.5 onto Sonnet 5", () => {
|
||||
expect(normalizeClaudeRuntimeModelId("anthropic/claude-sonnet-5-5")).toBe("claude-sonnet-5-5");
|
||||
expect(normalizeClaudeRuntimeModelId("us.anthropic.claude-sonnet-5-5-20260928-v1:0")).toBe(
|
||||
"claude-sonnet-5-5",
|
||||
);
|
||||
expect(normalizeClaudeRuntimeModelId("us.anthropic.claude-sonnet-5-20260101-v1:0")).toBe(
|
||||
"claude-sonnet-5",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseClaudeCodeVersion", () => {
|
||||
@@ -600,6 +617,42 @@ describe("Claude Opus 5.5 catalog", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Claude Sonnet 5.5 catalog", () => {
|
||||
it("offers one Sonnet 5.5 entry with a 1M context window", () => {
|
||||
const sonnet55Models = getClaudeModels()
|
||||
.filter((model) => model.id.startsWith("claude-sonnet-5-5"))
|
||||
.map(({ id, label, contextWindowMaxTokens }) => ({ id, label, contextWindowMaxTokens }));
|
||||
|
||||
expect(sonnet55Models).toEqual([
|
||||
{ id: "claude-sonnet-5-5", label: "Sonnet 5.5", contextWindowMaxTokens: 1_000_000 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("offers every effort level except off, because Sonnet 5.5 cannot disable thinking", () => {
|
||||
const sonnet55 = getClaudeModels().find((model) => model.id === "claude-sonnet-5-5");
|
||||
|
||||
expect(sonnet55?.thinkingOptions?.map((option) => option.id)).toEqual([
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh",
|
||||
"max",
|
||||
CLAUDE_ULTRACODE_THINKING_OPTION_ID,
|
||||
]);
|
||||
expect(sonnet55?.defaultThinkingOptionId).toBe("medium");
|
||||
expect(
|
||||
sonnet55?.thinkingOptions?.filter((option) => option.isDefault).map((option) => option.id),
|
||||
).toEqual(["medium"]);
|
||||
});
|
||||
|
||||
it("resolves suffixed and dated Sonnet 5.5 IDs to the single catalog entry", () => {
|
||||
expect(findClaudeModel("claude-sonnet-5-5[1m]")?.id).toBe("claude-sonnet-5-5");
|
||||
expect(findClaudeModel("claude-sonnet-5-5-20260928")?.id).toBe("claude-sonnet-5-5");
|
||||
expect(findClaudeModel("claude-sonnet-5-5-20260928[1m]")?.id).toBe("claude-sonnet-5-5");
|
||||
expect(findClaudeModel("claude-sonnet-5-5[1m]")?.contextWindowMaxTokens).toBe(1_000_000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("claudeManifestModelSupportsFastMode", () => {
|
||||
it("keeps fast mode strict to first-party manifest model IDs", () => {
|
||||
expect(normalizeClaudeManifestModelId("openrouter/anthropic/claude-opus-4-8")).toBeNull();
|
||||
@@ -610,6 +663,7 @@ describe("claudeManifestModelSupportsFastMode", () => {
|
||||
it("supports fast mode on Opus 5 but not on other Claude 5 models", () => {
|
||||
expect(claudeManifestModelSupportsFastMode("claude-opus-5-5")).toBe(true);
|
||||
expect(claudeManifestModelSupportsFastMode("claude-opus-5")).toBe(true);
|
||||
expect(claudeManifestModelSupportsFastMode("claude-sonnet-5-5")).toBe(false);
|
||||
expect(claudeManifestModelSupportsFastMode("claude-sonnet-5")).toBe(false);
|
||||
expect(claudeManifestModelSupportsFastMode("claude-fable-5")).toBe(false);
|
||||
expect(claudeManifestModelSupportsFastMode("claude-fable-5-1")).toBe(false);
|
||||
|
||||
Reference in New Issue
Block a user