fix(think): preserve orphaned execution outcomes - #2059
Merged
Conversation
🦋 Changeset detectedLatest commit: f2b5d3a The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
agents
@cloudflare/ai-chat
@cloudflare/codemode
hono-agents
@cloudflare/shell
@cloudflare/think
@cloudflare/voice
@cloudflare/worker-bundler
commit: |
ben-reitz
marked this pull request as draft
August 6, 2026 14:49
ben-reitz
marked this pull request as ready for review
August 10, 2026 09:41
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.
Fixes #2010.
What is the bug
Think can pause an execution while it waits for human approval. If compaction removes that execution's tool call before the approval arrives, the action still resolves, but the next model call fails because instead of saving the approval as part of the original tool call, it saves the approval as a system message in the middle of the conversation as a fallback outcome.
AI SDK v7 rejects it locally, while strict Anthropic-compatible routes return a 400 as they don't allow system messages to be inserted into the conversation mid-conversation. That message stays in history, so later turns keep failing too.
Why it happens
Normally Think replaces the paused tool result in place and continues the conversation. When the original tool part is gone, Think appends a text fallback so an already-applied approval or rejection is not silently lost.
That fallback is framework-authored, but providers cannot safely receive it as an arbitrarily placed
systemmessage.The fix
We're keeping the same framework-authored system notes in durable session history as the fallback here, so clients and recovery logic do not mistake them for human messages.
The change is that when assembling provider messages, Think will now project recognised execution-outcome notes to ordinary user context. The same projection handles notes already stored by older Think releases without rewriting session history.
The durable-role/provider-role split is a narrow compatibility fix until Think has first-class framework events with independent storage, UI, recovery, and model projections.
Options I tried
before landing on this one:
Related issue
I think solving #1676 would give us that more holistic event model, but it will be a much larger change and needs more thought and planning. This fix solves the immediate problem.