Preserve complete stopped responses and cancel unfinished tools - #8259
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
| const contentBlocks = message.contentBlocks ?? [] | ||
| const contentBlocks = (message.contentBlocks ?? []).map( | ||
| (block): PersistedContentBlock => | ||
| block.toolCall?.state === 'executing' |
There was a problem hiding this comment.
If Stop arrives while a tool is awaiting approval, this check leaves it unchanged because it only cancels executing tools. Replay preserves the awaiting_approval state, so the stopped turn can still show an actionable permission card after reload. Cancel the other unfinished tool states as well.
| /** StreamWriter starts at 1; Redis may trim oldest events or skip corrupt entries. */ | ||
| if (events.length === 0 || !events.every((event, index) => event.seq === index + 1)) return null | ||
| if ( | ||
| events.at(-1)?.type !== 'complete' || |
There was a problem hiding this comment.
Late title event blocks replay
On a first turn, title generation runs independently and can publish a session event after the run's complete event. This check then rejects a complete, gap-free replay. If the owner's message save failed and an identifiers-only Stop retries persistence, the retry returns success without saving the response. The completion check needs to allow later side-effect events.
| const contentBlocks = message.contentBlocks ?? [] | ||
| const contentBlocks = (message.contentBlocks ?? []).map( | ||
| (block): PersistedContentBlock => | ||
| block.toolCall?.state === 'executing' |
There was a problem hiding this comment.
Stopped tools remain unfinished When a stopped turn contains a
pending or awaiting_approval tool, this check leaves its state unchanged while the message gets a cancelled completion block. Both states can survive result persistence or replay, so reloaded history can show a stopped tool as still pending or awaiting approval. Cancel those nonterminal states as well as executing.
| normalizeMessage, | ||
| type PersistedMessage, | ||
| stripToolResultOutput, | ||
| withStoppedContentBlock, |
There was a problem hiding this comment.
New import violates path rule The added
withStoppedContentBlock import uses the relative ./persisted-message path. The repository requires absolute imports in apps/sim; change this import to an @/ path before merging.
Context Used: Import patterns for the Sim application (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
Stop could mistake a contiguous Redis prefix for the full response and persist it before the run owner flushed its tail. Replay now requires the actor's matching chat/run, a terminal run status, and contiguous events through the final completion event. Active, missing, mismatched, and incomplete runs defer to their canonical finalizer.
Stopped responses also mark unfinished tools cancelled, including when a cancelled completion block already exists. This closes the persistence findings from #8257 while retaining its identifiers-only Stop request and queued-edit protection.
Type of Change
Testing
Checklist