Repository navigation
claude-agent: keep a response that streams during a tool re-list - #1804
Merged
Merged
Conversation
While graff waited for Claude Code to re-read its tool list, it dropped every stream event that arrived. When Claude Code was already streaming the model's next response, the turn then read that response without its message_start and failed with "unparseable response". Lines read while waiting on a control answer are now kept in order and read first by the turn; a cancel that arrives then drops its held call as in the turn.
Merged
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.
Symptom
On Claude Agent, a turn can fail with:
Root cause
When graff's tool list changes (for example after
load_tool_schemas), graff tells Claude Code withnotifications/tools/list_changedand waits for thetools/listit sends back (refreshTools/controlinsrc/claude_agent.zig). That wait dropped every non-control line. If Claude Code was already streaming the model's next response (for example after answering a call by itself), itsmessage_startandcontent_block_startwere thrown away. The turn then assembled a body with nomessage_start,assembleAnthropicreturned null, and the JSON fallback failed with "unparseable response".Fix
aside. Stream events and results are kept in order on the session (Kept). A cancel drops its held call, as it does in the turn. Control requests are served as before.turnreads kept lines first (Session.nextLine).Tests
scripts/test-claude-agent.pyadds a scenario: the model loads a tool, and the fakeclaudestarts streaming its next response before it answers the re-list.Keptordering and cleanup.