fix: use text blocks in recall follow-up to prevent tool leaking to client#332
Merged
Conversation
…lient The previous fix (#330) kept recall in the follow-up tools list, which allowed the model to call recall again. The follow-up response is piped raw without recall interception, so a second recall tool_use leaks through to the client (OpenCode), which rejects it as an unknown tool. Fix: replace tool_use/tool_result with plain text blocks in the follow-up messages. The assistant message gets a marker text instead of the raw tool_use, and the user message gets recall results as plain text instead of tool_result. This eliminates the API constraint (tool_use must reference a defined tool) and allows stripping recall from the tools list to prevent re-invocation. Also add defense-in-depth recall suppression to both streaming and non-streaming follow-up response paths.
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
recallin the follow-up tools list, which allowed the model to call it again. The follow-up response is piped raw (noRecallAwareAccumulator), so a secondrecalltool_use leaked through to OpenCode, which rejected it as an unknown tool.📚 Searching...) instead of the rawtool_useblock, and the user message gets recall results as plain text instead oftool_result. This allows strippingrecallfrom the tools list without violating the API constraint that tool_use must reference a defined tool.Root cause
The recall follow-up has a 3-way constraint:
recallin tools → model calls it again, follow-up is piped raw, tool_use leaks to clientrecallfrom tools while keepingtool_usein messages → API rejects (tool_use references undefined tool)tool_use/tool_result→ no tool reference constraint, recall stripped from toolsFiles changed
packages/gateway/src/recall.tsbuildRecallFollowUp()to use text blocks instead of tool_use/tool_result, strip recall from toolspackages/gateway/src/pipeline.tspackages/gateway/test/recall.test.tsbuildRecallFollowUptest assertions for text block format, add empty-result test