feat(catalog): added capability flags and thinking support for Claude Sonnet 5

- Added an `isAnthropicAdaptiveGenAtLeast` utility to classify adaptive-thinking Claude generations at or above a given version threshold.
- Enabled adaptive thinking display, sampling restrictions, and mid-conversation system message flags for Claude Sonnet 5+ models.
- Updated Bedrock and OpenRouter adaptive reasoning effort maps to support five-tier scales on Sonnet 5+ models.
This commit is contained in:
can1357
2026-06-30 16:41:42 +02:00
parent 6b7d7e6e7e
commit f1063cdfbb
7 changed files with 169 additions and 32 deletions
@@ -310,4 +310,68 @@ describe("AgentSession thinking-loop retry", () => {
expect(assistants).toHaveLength(1);
expect(assistants[0].content).toEqual([{ type: "text", text: "Recovered after retry." }]);
});
it("injects a redirect notice on each consecutive thinking-loop retry", async () => {
const model = createMockModel({ provider: "openrouter", id: "google/gemini-3.5-flash" }).model;
const modelRegistry = new ModelRegistry(authStorage);
const calls: string[] = [];
const contexts: Context[] = [];
const agent = new Agent({
getApiKey: requestedModel => `${requestedModel.provider}-test-key`,
initialState: {
model,
systemPrompt: ["Test"],
tools: [],
messages: [],
},
convertToLlm,
streamFn: (requestedModel, context, _options?: SimpleStreamOptions) => {
calls.push(`${requestedModel.provider}/${requestedModel.id}`);
contexts.push(context);
return calls.length <= 2 ? errorIdOnlyThinkingLoopStream(requestedModel) : successStream(requestedModel);
},
});
const settings = Settings.isolated({
"compaction.enabled": false,
"retry.enabled": true,
"retry.baseDelayMs": 0,
"retry.maxDelayMs": 5_000,
"retry.maxRetries": 2,
"retry.modelFallback": false,
"todo.enabled": false,
"model.loopGuard.enabled": true,
});
settings.setModelRole("default", `${model.provider}/${model.id}`);
session = new AgentSession({
agent,
sessionManager: SessionManager.inMemory(),
settings,
modelRegistry,
});
vi.spyOn(scheduler, "wait").mockResolvedValue(undefined);
await session.prompt("Trigger redirect injection after two thinking loops");
await session.waitForIdle();
expect(calls).toHaveLength(3);
const redirects = session.agent.state.messages.filter(
(message): message is CustomMessage =>
message.role === "custom" && message.customType === "thinking-loop-redirect",
);
expect(redirects).toHaveLength(2);
const extractText = (content: string | Array<{ type: string; text?: string }>): string =>
typeof content === "string"
? content
: content.map(part => (part.type === "text" ? (part.text ?? "") : "")).join("");
const thirdAttemptRedirectDevMsgs = contexts[2].messages.filter(
message => message.role === "developer" && extractText(message.content).includes("thinking_loop_detected"),
);
expect(thirdAttemptRedirectDevMsgs).toHaveLength(2);
const assistants = session.agent.state.messages.filter(
(message): message is AssistantMessage => message.role === "assistant",
);
expect(assistants).toHaveLength(1);
expect(assistants[0].content).toEqual([{ type: "text", text: "Recovered after retry." }]);
});
});