feat(claude-code): pass thinking level as effort

This commit is contained in:
mastertyko 2026-04-12 18:15:22 +02:00
parent cf34383104
commit 5474e99ae2
3 changed files with 39 additions and 6 deletions

View file

@ -5,6 +5,10 @@ export * from "./api-registry.js";
export * from "./env-api-keys.js";
export * from "./models.js";
export * from "./providers/anthropic.js";
export {
mapThinkingLevelToEffort,
supportsAdaptiveThinking,
} from "./providers/anthropic-shared.js";
export * from "./providers/azure-openai-responses.js";
export * from "./providers/google.js";
export * from "./providers/google-gemini-cli.js";

View file

@ -14,10 +14,11 @@ import type {
Context,
Model,
SimpleStreamOptions,
ThinkingLevel,
ToolCall,
} from "@gsd/pi-ai";
import type { ExtensionUIContext } from "@gsd/pi-coding-agent";
import { EventStream } from "@gsd/pi-ai";
import { EventStream, mapThinkingLevelToEffort, supportsAdaptiveThinking } from "@gsd/pi-ai";
import { execSync } from "node:child_process";
import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.js";
import { buildWorkflowMcpServers } from "../gsd/workflow-mcp.js";
@ -600,8 +601,9 @@ export function buildSdkOptions(
modelId: string,
prompt: string,
overrides?: { permissionMode?: "bypassPermissions" | "acceptEdits" | "default" | "plan" },
extraOptions: Record<string, unknown> = {},
extraOptions: Record<string, unknown> & { reasoning?: ThinkingLevel } = {},
): Record<string, unknown> {
const { reasoning, ...sdkExtraOptions } = extraOptions;
const mcpServers = buildWorkflowMcpServers();
const permissionMode = overrides?.permissionMode ?? "bypassPermissions";
const disallowedTools = ["AskUserQuestion"];
@ -620,6 +622,10 @@ export function buildSdkOptions(
"Bash(pwd)",
...(mcpServers ? Object.keys(mcpServers).map((serverName) => `mcp__${serverName}__*`) : []),
];
const effort =
reasoning && supportsAdaptiveThinking(modelId)
? mapThinkingLevelToEffort(reasoning, modelId)
: undefined;
return {
pathToClaudeCodeExecutable: getClaudePath(),
model: modelId,
@ -634,7 +640,8 @@ export function buildSdkOptions(
...(allowedTools.length > 0 ? { allowedTools } : {}),
...(mcpServers ? { mcpServers } : {}),
betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
...extraOptions,
...(effort ? { effort } : {}),
...sdkExtraOptions,
};
}
@ -828,11 +835,12 @@ async function pumpSdkMessages(
{ permissionMode },
typeof (options as ClaudeCodeStreamOptions | undefined)?.extensionUIContext === "object"
? {
reasoning: options?.reasoning,
onElicitation: createClaudeCodeElicitationHandler(
(options as ClaudeCodeStreamOptions | undefined)?.extensionUIContext,
),
}
: {},
: { reasoning: options?.reasoning },
);
const queryResult = sdk.query({

View file

@ -343,6 +343,26 @@ describe("stream-adapter — session persistence (#2859)", () => {
);
});
test("buildSdkOptions maps reasoning to effort for adaptive Claude Code models (#3917)", () => {
const options = buildSdkOptions("claude-sonnet-4-6", "test", undefined, { reasoning: "high" });
assert.equal(options.effort, "high");
});
test("buildSdkOptions upgrades xhigh reasoning to max for opus 4.6 (#3917)", () => {
const options = buildSdkOptions("claude-opus-4-6", "test", undefined, { reasoning: "xhigh" });
assert.equal(options.effort, "max");
});
test("buildSdkOptions omits effort when reasoning is undefined (#3917)", () => {
const options = buildSdkOptions("claude-sonnet-4-6", "test");
assert.equal("effort" in options, false);
});
test("buildSdkOptions omits effort for non-adaptive Claude models (#3917)", () => {
const options = buildSdkOptions("claude-sonnet-4-20250514", "test", undefined, { reasoning: "high" });
assert.equal("effort" in options, false);
});
test("buildSdkOptions includes workflow MCP server config when env is set", () => {
const prev = {
GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
@ -774,11 +794,12 @@ describe("stream-adapter — MCP elicitation bridge", () => {
},
};
const secureValue = "ui-collected-value";
const inputCalls: Array<{ opts?: { secure?: boolean } }> = [];
const handler = createClaudeCodeElicitationHandler({
input: async (_title: string, _placeholder?: string, opts?: { secure?: boolean }) => {
inputCalls.push({ opts });
return "example-secure-input";
return secureValue;
},
} as any);
assert.ok(handler);
@ -787,7 +808,7 @@ describe("stream-adapter — MCP elicitation bridge", () => {
assert.deepEqual(result, {
action: "accept",
content: {
TEST_SECURE_FIELD: "example-secure-input",
TEST_SECURE_FIELD: secureValue,
},
});
assert.equal(inputCalls.length, 1);