Keep Tool calls stable across provider history and async polls - #952
Draft
Y1fe1Zh0u wants to merge 3 commits into
Draft
Keep Tool calls stable across provider history and async polls#952Y1fe1Zh0u wants to merge 3 commits into
Y1fe1Zh0u wants to merge 3 commits into
Conversation
Set the existing protocol repair, safe-read replay, and model-visible Tool episode limits to ten while preserving their current independent state and execution semantics. Update focused tests and planning artifacts to make the off-by-one behavior explicit. Constraint: Tool-related retry and repair limits must be ten without restructuring the existing counters Rejected: Unify protocol, Receipt, and model-visible repair state now | counter redesign is intentionally deferred Confidence: high Scope-risk: moderate Directive: Keep the independent counters until the planned repair-control refactor; do not infer identical attempt semantics from the shared numeric limit Tested: 911 Runtime and Tool pytest cases; scoped Ruff; fatal-level caller Ruff; py_compile; git diff --check Not-tested: Live Provider credentials
Gemini reuses provider call identifiers across assistant turns, so a history-wide lookup allowed later calls to overwrite earlier tool names. Scope name resolution to the active assistant exchange while preserving provider wire identifiers. Constraint: Gemini provider call IDs are not globally unique across turns Rejected: Persist globally unique provider IDs | would alter provider wire identity and conflict with existing Runtime correlation Confidence: high Scope-risk: narrow Directive: Keep Tool Result name resolution exchange-local when modifying Gemini history lowering Tested: backend/tests/test_llm_single_step.py; LLM scoped tests; Ruff Not-tested: Live Gemini API
Synthetic poll calls belong to the original accepted Tool call but use a new runtime call ID. Record the origin call ID on the synthetic Assistant message and derive the poll execution entry from the frozen StepToolContext without replacing checkpoint state. Constraint: Keep the fix local to the reproduced async poll context mismatch. Rejected: Add a separate async lifecycle state machine | unnecessary for preserving the accepted Tool contract. Confidence: high Scope-risk: narrow Directive: Do not resolve the ToolProvider again for a poll generated from an accepted Tool call. Tested: 130 scoped Runtime Tool, node executor, LLM, and runtime schema tests; scoped Ruff; git diff --check. Not-tested: Real MCP provider and process-restart end-to-end execution.
This was referenced Aug 12, 2026
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.
What changed
Why
Gemini previously resolved Tool Result names through a whole-history ID map, so a later duplicate ID could rename an earlier result. Async poll calls also used a new Assistant and Call ID while retaining the original frozen StepToolContext, causing
tool_context_corrupton resume.The fix stays local: Gemini uses exchange-local identity, while async polls record their originating accepted Call ID and temporarily derive execution data from the existing frozen context. No new lifecycle state, database schema, scheduler, cancellation, or reconciliation machinery is introduced.
Impact
Validation
git diff --checkpassedStack
This draft targets
002-tool-runtime-contractbecause the frozen StepToolContext implementation is currently in PR #945 and has not yet merged intomain.