Skip to content

Fix blank-session restarts after interrupt or failed run - #1135

Closed
bunnysayzz wants to merge 9049 commits into
CodebuffAI:mainfrom
bunnysayzz:fix/preserve-session-state-1054
Closed

Fix blank-session restarts after interrupt or failed run#1135
bunnysayzz wants to merge 9049 commits into
CodebuffAI:mainfrom
bunnysayzz:fix/preserve-session-state-1054

Conversation

@bunnysayzz

Copy link
Copy Markdown

Fixes #1054.

Root cause

The CLI only synced previousRunStateRef (the continuation state passed to the SDK as previousRun) when client.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:

  1. The thrown-error catch path (failed/expired runs) never synced the ref, only disk, so the next prompt in the same process also lost history.
  2. A sessionState-less run state could be persisted and then adopted on restart (loadMostRecentChatState fabricated { output } when run-state.json had no session state), poisoning every resumed chat.

Fix

  • setupStreamingContext accepts an onAbort callback, invoked synchronously at the top of the abort listener, before the input lock is released. useSendMessage passes syncRunState(latestRunStateSnapshot) so an interrupt checkpoints the latest SDK snapshot immediately.
  • useSendMessage now 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 gated handleRunCompletion/handleRunError/finally with it).
  • The catch path syncs the ref too, then persists, so a failed turn resumes from the last snapshot.
  • loadMostRecentChatState treats a run-state without sessionState as unrestorable; the transcript still restores, the agent context does not.

Tests

  • New hook-level suite cli/src/hooks/__tests__/use-send-message.test.tsx drives 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.
  • Helper tests cover the onAbort callback contract (sync ordering before lock release, throwing callback still cleans up).
  • Storage tests cover the load-side poison guard.

All targeted suites pass (92 tests). Typecheck, prettier, and git diff --check are clean; the app bundle builds.

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:triaged Classified by the community triage bot pr:port-candidate Worth porting into the private source tree

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Session state is lost after free session expires or run is interrupted

3 participants