Repository navigation
feat(claude): keep the Chat tool catalog stable across ToolSearch loads (opt-in dispatcher) - #8502
Merged
Merged
Conversation
…ds (opt-in dispatcher) Maintainer carry of #8057 by @Gudals0320 onto current dev. On a translated openai-chat route every client ToolSearch load rewrites the tool catalog and the tool-name nudge in the system message, so the next request re-misses the provider prompt cache for the whole conversation. The new opt-in claudeCode.chatToolDispatchModels (same selector grammar, validation, salvage and provider-ID migration as appendOnlyToolLoadingModels) applies only on a direct openai-chat physical adapter: the catalog carries one stable use_loaded_tool dispatcher from the first request, loaded definitions ride the ToolSearch result as text, history is re-encoded deterministically, and dispatcher calls are rewritten to the named tool on both parse paths. Absent, empty or unlisted configuration sends byte-identical requests. Changes on top of #8057: - Every well-formed {name, arguments?} dispatcher call is rewritten to the tool it names, so a deferred tool that was never loaded, or an undeclared name, reaches Claude Code by its real name instead of as use_loaded_tool. Malformed, extra-key, incomplete, empty-name and self-named calls still pass through. - toolSearch, appendOnlyToolLoadingModels and chatToolDispatchModels move to src/types/claude-tool-loading.ts, which OcxClaudeCodeConfig extends, so src/types/config.ts shrinks instead of crossing its 2,000-line cap. - An allowed-tools restriction sends the request unprojected; the shared selector helpers get neutral names. - The Chat-routes docs build on #8493's troubleshooting text and name the opt-in next to the ENABLE_TOOL_SEARCH=false mitigation. Closes #7963 Co-authored-by: Gudals0320 <255312450+Gudals0320@users.noreply.github.com>
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Contributor
|
✅ Deterministic PR hygiene checks passed. |
Contributor
|
Caution Review failedThe pull request is closed.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On a translated Chat Completions route (DeepSeek, GLM and similar), each Claude Code
ToolSearchload changes two parts of the next request: the tool-name list in the system message (src/adapters/tool-catalog-nudge.ts) and the top-leveltoolsarray. The provider's prefix cache then misses for almost the whole conversation. Claude Code v2.1.304+ turns tool search on behind a gateway by default, soocx claudesessions hit this by default. #7453 fixed it for Responses destinations throughadditional_tools; the Chat wire has no such carrier.This carries contributor PR #8057 (closed by its author without merging) onto current
dev, as the owner decided on #7963, after a design review:claudeCode.chatToolDispatchModelstakes the same selector grammar, validation, load-time salvage and provider-rename migration asappendOnlyToolLoadingModels. On a selected directopenai-chatroute, the catalog carries one stableuse_loaded_tool {name, arguments}dispatcher from the first request. A loaded deferred tool's definition is appended as text to theToolSearchresult that loaded it, which is the transcript tail at that moment. Earlier calls are re-encoded through the dispatcher, so earlier bytes never change.tool_choiceon a hidden tool, anallowed_toolsrestriction, and duplicate call ids. The decision is made per physical adapter, so a fallback to a non-Chat target is unaffected.{},[]or an unlisted selector, upstream requests are unchanged. Tests cover streaming and non-streaming, a request withoutToolSearch, and a route listed only inappendOnlyToolLoadingModels.Changes made during the carry, from the design review:
src/types/config.tswas at 1,999 lines and the ratchet fails an unbaselined file at 2,000. The Claude tool-loading fields move tosrc/types/claude-tool-loading.ts, whichOcxClaudeCodeConfigextends and both type barrels re-export (now 1,969 lines).NotebookEdit, deferred) or not declared (Nope) used to reach Claude Code asuse_loaded_tool, which Claude Code then reported as unknown. Every well-formed{name, arguments?}call is now rewritten to the named tool. Claude Code runs a declared tool exactly as a direct call, or reports the unknown tool by its real name. Malformed, extra-key, incomplete, empty-name and self-referencing calls pass through unchanged.ENABLE_TOOL_SEARCH=falseexport, and English and Korean describe both mitigations. The "arguments are not streamed until the call ends" note is reworded, because that is true of every Chat tool call ondev, not a cost of this option.structure/data-planes/claude-tool-loading.mdand its manifest entry now cover the Chat dispatcher.Closes #7963
Co-authored-by: Gudals0320 255312450+Gudals0320@users.noreply.github.com
Verification
"name":"use_loaded_tool"with{"name":"NotebookEdit",…}as input.claude-chat-tool-dispatch,claude-chat-tool-dispatch-routes,claude-inbound-tool-loading,claude-tool-loading-destinations,claude-inbound-tool-reference,claude-translated-tool-restrictions): 100 pass, 0 fail.bun run typecheck,bun run structure:check,bun run privacy:scan: pass. Layout, layout tooling, file-size ratchet, Lab boundary and structure guards: 114 pass, 0 fail.tests/ci-workflows/docs-*.test.ts: 361 pass, 0 fail.dev(29dc0893f, which also editsstructure/manifest.json): typecheck andstructure:checkpass,structure:indexregenerates an identicalINDEX.md, and the guards plus the dispatch suites pass (195 pass, 0 fail).bun run test:changed(2,406 files, isolatedHOME): 54,395 pass, 36 fail, 3 errors. The failures are the container's known environment failures, which fail identically on cleandev:local-control-socket(including the 3 ENOENT errors), real-npm Codex CLI update suites,codex-composed-acceptance, and the onecatalog-omit-disabled-nativecase. Oneclaude-cli-client-httpcase andultrafast-tier-honestyfailed only inside the combined run and pass alone on both this branch andorigin/dev.ocx claudenow hits this by default #7963 and feat(claude): keep the Chat tool catalog stable across ToolSearch loads (opt-in dispatcher) #8057); fullbun run test; macOS/Windows.Checklist
Maintainer judgment