Conversation
PR SummaryMedium Risk Overview Refactors agent/provider quirks by centralizing provider capability flags (e.g., Kimi aliases) and using them to gate Anthropic tool schema/tool_choice normalization and thinking-signature preservation; related tests are converted to table-driven cases. Adjusts Written by Cursor Bugbot for commit 4c71606. This will update automatically on new commits. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| payloadObj.tool_choice = normalizeOpenAiStringModeAnthropicToolChoice( | ||
| payloadObj.tool_choice, | ||
| ); | ||
| } |
There was a problem hiding this comment.
BaseUrl-based kimi detection path becomes dead code
Medium Severity
requiresAnthropicToolPayloadCompatibility can return true via baseUrl matching (e.g., a kimi.com/coding URL with a custom provider name), but the inner guards usesOpenAiFunctionAnthropicToolSchema(model.provider) and usesOpenAiStringModeAnthropicToolChoice(model.provider) both check the provider string against the capabilities table. When the match came from baseUrl alone (provider is not "kimi-coding"), both inner checks return false, so neither tool schema nor tool_choice normalization happens. The old code applied both normalizations unconditionally once the endpoint matched. This is a regression for users who configure a kimi.com/coding endpoint with a non-standard provider key.
Additional Locations (1)
Greptile SummaryThis PR is a release hardening follow-up that centralizes provider-specific quirks into a new Key changes:
Confidence Score: 3/5
Last reviewed commit: 4c71606 |
| if ( | ||
| payload && | ||
| typeof payload === "object" && | ||
| requiresAnthropicToolPayloadCompatibility(model) | ||
| ) { | ||
| const payloadObj = payload as Record<string, unknown>; | ||
| if (Array.isArray(payloadObj.tools)) { | ||
| if ( | ||
| Array.isArray(payloadObj.tools) && | ||
| usesOpenAiFunctionAnthropicToolSchema( | ||
| typeof model.provider === "string" ? model.provider : undefined, | ||
| ) | ||
| ) { | ||
| payloadObj.tools = payloadObj.tools | ||
| .map((tool) => normalizeKimiCodingToolDefinition(tool)) | ||
| .map((tool) => normalizeOpenAiFunctionAnthropicToolDefinition(tool)) | ||
| .filter((tool): tool is Record<string, unknown> => !!tool); | ||
| } | ||
| payloadObj.tool_choice = normalizeKimiCodingToolChoice(payloadObj.tool_choice); | ||
| if ( | ||
| usesOpenAiStringModeAnthropicToolChoice( | ||
| typeof model.provider === "string" ? model.provider : undefined, | ||
| ) | ||
| ) { | ||
| payloadObj.tool_choice = normalizeOpenAiStringModeAnthropicToolChoice( | ||
| payloadObj.tool_choice, | ||
| ); | ||
| } |
There was a problem hiding this comment.
URL-based kimi detection is now a no-op for normalization
requiresAnthropicToolPayloadCompatibility can return true via the URL-matching path (kimi.com/coding hostname) when model.provider is not set to "kimi-coding". However, both inner normalization guards (usesOpenAiFunctionAnthropicToolSchema and usesOpenAiStringModeAnthropicToolChoice) receive undefined in that case, which resolves to the default "native" capabilities — so neither tools array nor tool_choice is ever normalized.
Before this refactor, isKimiCodingAnthropicEndpoint returning true unconditionally applied both normalizations. Now, a user who configures api: "anthropic-messages" + baseUrl: "https://api.kimi.com/coding/..." without explicitly setting provider: "kimi-coding" will silently get no transformation, breaking their tool calls.
If the URL-based fallback is intentionally kept for future extensibility, consider also driving the capability resolution from the URL match so the nested guards can fire:
// inside createAnthropicToolPayloadCompatibilityWrapper onPayload
if (payload && typeof payload === "object" && requiresAnthropicToolPayloadCompatibility(model)) {
const payloadObj = payload as Record<string, unknown>;
// Resolve capabilities from provider; fall back to kimi-coding caps when URL-matched
const effectiveProvider =
typeof model.provider === "string"
? model.provider
: requiresAnthropicToolPayloadCompatibility(model) // URL match
? "kimi-coding"
: undefined;
if (Array.isArray(payloadObj.tools) && usesOpenAiFunctionAnthropicToolSchema(effectiveProvider)) {
...
}
if (usesOpenAiStringModeAnthropicToolChoice(effectiveProvider)) {
...
}
}Alternatively, if the URL-based path is no longer needed now that provider IDs are normalized, remove the hostname check from requiresAnthropicToolPayloadCompatibility to avoid dead code.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/extra-params.ts
Line: 902-926
Comment:
**URL-based kimi detection is now a no-op for normalization**
`requiresAnthropicToolPayloadCompatibility` can return `true` via the URL-matching path (kimi.com/coding hostname) when `model.provider` is *not* set to `"kimi-coding"`. However, both inner normalization guards (`usesOpenAiFunctionAnthropicToolSchema` and `usesOpenAiStringModeAnthropicToolChoice`) receive `undefined` in that case, which resolves to the default `"native"` capabilities — so neither tools array nor `tool_choice` is ever normalized.
Before this refactor, `isKimiCodingAnthropicEndpoint` returning `true` unconditionally applied both normalizations. Now, a user who configures `api: "anthropic-messages"` + `baseUrl: "https://api.kimi.com/coding/..."` *without* explicitly setting `provider: "kimi-coding"` will silently get no transformation, breaking their tool calls.
If the URL-based fallback is intentionally kept for future extensibility, consider also driving the capability resolution from the URL match so the nested guards can fire:
```ts
// inside createAnthropicToolPayloadCompatibilityWrapper onPayload
if (payload && typeof payload === "object" && requiresAnthropicToolPayloadCompatibility(model)) {
const payloadObj = payload as Record<string, unknown>;
// Resolve capabilities from provider; fall back to kimi-coding caps when URL-matched
const effectiveProvider =
typeof model.provider === "string"
? model.provider
: requiresAnthropicToolPayloadCompatibility(model) // URL match
? "kimi-coding"
: undefined;
if (Array.isArray(payloadObj.tools) && usesOpenAiFunctionAnthropicToolSchema(effectiveProvider)) {
...
}
if (usesOpenAiStringModeAnthropicToolChoice(effectiveProvider)) {
...
}
}
```
Alternatively, if the URL-based path is no longer needed now that provider IDs are normalized, remove the hostname check from `requiresAnthropicToolPayloadCompatibility` to avoid dead code.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c7160604d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Array.isArray(payloadObj.tools) && | ||
| usesOpenAiFunctionAnthropicToolSchema( | ||
| typeof model.provider === "string" ? model.provider : undefined, | ||
| ) | ||
| ) { |
There was a problem hiding this comment.
Normalize Kimi tool payloads when baseUrl detection triggers
When requiresAnthropicToolPayloadCompatibility(model) is satisfied via the baseUrl fallback (for example, a custom provider using https://api.kimi.com/coding/), this branch still gates normalization on model.provider capabilities, so the tools[]/tool_choice rewrites are skipped. That leaves Anthropic-shaped tool payloads on a Kimi coding endpoint, which can reject tool calls; before this refactor, baseUrl detection alone applied the normalization.
Useful? React with 👍 / 👎.
|
|
||
| const existingApi = resolveProviderApi(existing); | ||
| const nextApi = resolveProviderApi(nextEntry); | ||
| return !existingApi || !nextApi || existingApi === nextApi; |
There was a problem hiding this comment.
Require API match before preserving existing provider baseUrl
This predicate keeps the old baseUrl whenever either side is missing api, so merge mode preserves stale endpoints from older/manual models.json entries that omit api even after the generated provider now points to a different API/backend (notably implicit openai-codex). That prevents automatic endpoint migration and can leave requests routed to incompatible URLs.
Useful? React with 👍 / 👎.
* main: (70 commits) Refactor release hardening follow-ups (openclaw#39959) docs: clarify bot review conversation ownership (openclaw#39942) fix: harden talk silence timeout parsing (openclaw#39607) (thanks @danodoesdesign) talk: add configurable silence timeout transcript-policy: use named Set for anthropic signature-excluded providers transcript-policy: don't preserve thinking signatures for kimi-coding (openclaw#39798) fix: land mac universal release defaults (openclaw#33891) (thanks @cgdusek) Docs: clarify notarization handoff in mac release flow Docs: mark basic mac dist example as non-notarized Docs: clarify release build arch defaults for mac packaging macOS: default release app builds to universal binaries fix(issue-39839): address tool-call extra params parsing for kimi anthropic-messages docs: use alphabetical provider ordering fix: follow up openclaw#39321 and openclaw#38445 landings docs: note /landpr merge process fix: land Brave llm-context gaps (openclaw#33383) (thanks @thirumaleshp) feat: add Brave Search LLM Context API mode for web_search fix(feishu): restore @larksuiteoapi/node-sdk in root dependencies refactor: tighten codex inline api fallback follow-up macOS: set speech recognition taskHint for Talk Mode mic capture ...
* build: fail fast on stale host-env swift policy * build: sync generated host env swift policy * build: guard bundled extension root dependency gaps * refactor: centralize provider capability quirks * test: table-drive provider regression coverage * fix: block merge when prep branch has unpushed commits * refactor: simplify models config merge preservation
* build: fail fast on stale host-env swift policy * build: sync generated host env swift policy * build: guard bundled extension root dependency gaps * refactor: centralize provider capability quirks * test: table-drive provider regression coverage * fix: block merge when prep branch has unpushed commits * refactor: simplify models config merge preservation
* build: fail fast on stale host-env swift policy * build: sync generated host env swift policy * build: guard bundled extension root dependency gaps * refactor: centralize provider capability quirks * test: table-drive provider regression coverage * fix: block merge when prep branch has unpushed commits * refactor: simplify models config merge preservation
* build: fail fast on stale host-env swift policy * build: sync generated host env swift policy * build: guard bundled extension root dependency gaps * refactor: centralize provider capability quirks * test: table-drive provider regression coverage * fix: block merge when prep branch has unpushed commits * refactor: simplify models config merge preservation
* build: fail fast on stale host-env swift policy * build: sync generated host env swift policy * build: guard bundled extension root dependency gaps * refactor: centralize provider capability quirks * test: table-drive provider regression coverage * fix: block merge when prep branch has unpushed commits * refactor: simplify models config merge preservation
* build: fail fast on stale host-env swift policy * build: sync generated host env swift policy * build: guard bundled extension root dependency gaps * refactor: centralize provider capability quirks * test: table-drive provider regression coverage * fix: block merge when prep branch has unpushed commits * refactor: simplify models config merge preservation
* build: fail fast on stale host-env swift policy * build: sync generated host env swift policy * build: guard bundled extension root dependency gaps * refactor: centralize provider capability quirks * test: table-drive provider regression coverage * fix: block merge when prep branch has unpushed commits * refactor: simplify models config merge preservation
…39959) Partial pick — discards gutted files (models-config, pi-embedded-runner, transcript-policy, provider-capabilities, test/release-check.test.ts). Alive changes: - build: fail fast on stale host-env swift policy (move check to front) - build: guard bundled extension root dependency gaps - fix: block merge when prep branch has unpushed commits - HostEnvSecurityPolicy.generated.swift: restore blocked override keys Rebrand: openclaw→remoteclaw in release-check.ts PackageJson type and bundled extension dependency mirror validation. Cherry-picked-from: eba9dcc
…39959) Partial pick — discards gutted files (models-config, pi-embedded-runner, transcript-policy, provider-capabilities, test/release-check.test.ts). Alive changes: - build: fail fast on stale host-env swift policy (move check to front) - build: guard bundled extension root dependency gaps - fix: block merge when prep branch has unpushed commits - HostEnvSecurityPolicy.generated.swift: restore blocked override keys Rebrand: openclaw→remoteclaw in release-check.ts PackageJson type and bundled extension dependency mirror validation. Cherry-picked-from: eba9dcc
…39959) (#1256) Partial pick — discards gutted files (models-config, pi-embedded-runner, transcript-policy, provider-capabilities, test/release-check.test.ts). Alive changes: - build: fail fast on stale host-env swift policy (move check to front) - build: guard bundled extension root dependency gaps - fix: block merge when prep branch has unpushed commits - HostEnvSecurityPolicy.generated.swift: restore blocked override keys Rebrand: openclaw→remoteclaw in release-check.ts PackageJson type and bundled extension dependency mirror validation. Cherry-picked-from: eba9dcc Co-authored-by: Peter Steinberger <[email protected]>
* build: fail fast on stale host-env swift policy * build: sync generated host env swift policy * build: guard bundled extension root dependency gaps * refactor: centralize provider capability quirks * test: table-drive provider regression coverage * fix: block merge when prep branch has unpushed commits * refactor: simplify models config merge preservation
* build: fail fast on stale host-env swift policy * build: sync generated host env swift policy * build: guard bundled extension root dependency gaps * refactor: centralize provider capability quirks * test: table-drive provider regression coverage * fix: block merge when prep branch has unpushed commits * refactor: simplify models config merge preservation


Summary
models-configmerge preservation so stale baseUrls are only kept when provider API still matchesValidation