diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ed3a3077a9..f37bf4402fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai - Mattermost replies: keep `root_id` pinned to the existing thread root when an agent replies inside a thread, while still using reply-target threading for top-level posts. (#27744) thanks @hnykda. - Agents/failover: detect Amazon Bedrock `Too many tokens per day` quota errors as rate limits across fallback, cron retry, and memory embeddings while keeping context-window `too many tokens per request` errors out of the rate-limit lane. (#39377) Thanks @gambletan. - Android/Play distribution: remove self-update, background location, `screen.record`, and background mic capture from the Android app, narrow the foreground service to `dataSync` only, and clean up the legacy `location.enabledMode=always` preference migration. (#39660) Thanks @obviyus. +- Kimi coding tool calls: normalize Anthropic `tool_choice` modes like `auto`, `none`, and `required` before sending Kimi coding anthropic-messages requests so tool calls keep working on `.7`. (#39854) Thanks @GeekCheyun. ## 2026.3.7 diff --git a/src/agents/pi-embedded-runner-extraparams.test.ts b/src/agents/pi-embedded-runner-extraparams.test.ts index 2da38cbc8b3..146b3c872dd 100644 --- a/src/agents/pi-embedded-runner-extraparams.test.ts +++ b/src/agents/pi-embedded-runner-extraparams.test.ts @@ -804,37 +804,45 @@ describe("applyExtraParamsToAgent", () => { }); it("normalizes anthropic tool_choice modes for kimi-coding endpoints", () => { - const payloads: Record[] = []; - const baseStreamFn: StreamFn = (_model, _context, options) => { - const payload: Record = { - tools: [ - { - name: "read", - description: "Read file", - input_schema: { type: "object", properties: {} }, - }, - ], - tool_choice: { type: "auto" }, + const cases = [ + { input: { type: "auto" }, expected: "auto" }, + { input: { type: "none" }, expected: "none" }, + { input: { type: "required" }, expected: "required" }, + ] as const; + + for (const testCase of cases) { + const payloads: Record[] = []; + const baseStreamFn: StreamFn = (_model, _context, options) => { + const payload: Record = { + tools: [ + { + name: "read", + description: "Read file", + input_schema: { type: "object", properties: {} }, + }, + ], + tool_choice: testCase.input, + }; + options?.onPayload?.(payload); + payloads.push(payload); + return {} as ReturnType; }; - options?.onPayload?.(payload); - payloads.push(payload); - return {} as ReturnType; - }; - const agent = { streamFn: baseStreamFn }; + const agent = { streamFn: baseStreamFn }; - applyExtraParamsToAgent(agent, undefined, "kimi-coding", "k2p5", undefined, "low"); + applyExtraParamsToAgent(agent, undefined, "kimi-coding", "k2p5", undefined, "low"); - const model = { - api: "anthropic-messages", - provider: "kimi-coding", - id: "k2p5", - baseUrl: "https://api.kimi.com/coding/", - } as Model<"anthropic-messages">; - const context: Context = { messages: [] }; - void agent.streamFn?.(model, context, {}); + const model = { + api: "anthropic-messages", + provider: "kimi-coding", + id: "k2p5", + baseUrl: "https://api.kimi.com/coding/", + } as Model<"anthropic-messages">; + const context: Context = { messages: [] }; + void agent.streamFn?.(model, context, {}); - expect(payloads).toHaveLength(1); - expect(payloads[0]?.tool_choice).toBe("auto"); + expect(payloads).toHaveLength(1); + expect(payloads[0]?.tool_choice).toBe(testCase.expected); + } }); it("does not rewrite anthropic tool schema for non-kimi endpoints", () => {