Fix blank-session restarts after interrupt or failed run - #1135
Closed
bunnysayzz wants to merge 9049 commits into
Closed
Fix blank-session restarts after interrupt or failed run#1135bunnysayzz wants to merge 9049 commits into
bunnysayzz wants to merge 9049 commits into
Conversation
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 #1054.
Root cause
The CLI only synced
previousRunStateRef(the continuation state passed to the SDK aspreviousRun) whenclient.run()settled. But Esc releases the input lock immediately, inside the abort listener. So a follow-up message sent the moment the user hits Esc could be built from a stale (or null) ref. With a null ref the SDK builds a fresh session state and the chat comes back as an empty, brand-new conversation, which reads exactly like a hard reset.Two more holes in the same family:
loadMostRecentChatStatefabricated{ output }when run-state.json had no session state), poisoning every resumed chat.Fix
setupStreamingContextaccepts anonAbortcallback, invoked synchronously at the top of the abort listener, before the input lock is released.useSendMessagepassessyncRunState(latestRunStateSnapshot)so an interrupt checkpoints the latest SDK snapshot immediately.useSendMessagenow owns a generation token per admitted run. A superseded run settling late can never adopt state, persist a checkpoint, or touch shared queue state over the run that replaced it (also gatedhandleRunCompletion/handleRunError/finallywith it).loadMostRecentChatStatetreats a run-state withoutsessionStateas unrestorable; the transcript still restores, the agent context does not.Tests
cli/src/hooks/__tests__/use-send-message.test.tsxdrives the real wiring (createRunConfig+ a controllable client via a small DI seam, matching the repo documented DI-over-module-mocking approach): abort-then-follow-up carries full history, a superseded run settling late cannot clobber the newer run state, rejected runs resume from the last snapshot, and sessionState-less states are never adopted.onAbortcallback contract (sync ordering before lock release, throwing callback still cleans up).All targeted suites pass (92 tests). Typecheck, prettier, and
git diff --checkare clean; the app bundle builds.