fix(sessions): stop agent-crash recovery from wiping task history#3172
Open
richardsolomou wants to merge 1 commit into
Open
fix(sessions): stop agent-crash recovery from wiping task history#3172richardsolomou wants to merge 1 commit into
richardsolomou wants to merge 1 commit into
Conversation
When the agent process died at the wrong moment, the recovery path could
destroy the task's entire transcript:
- clearSessionError recreated the run whenever initialPrompt was still
set, but that flag is only cleared by a live agent event — it survives
a crash (or dropped subscription) before the first event, even when
the run log already holds real work. Teardown + createTaskRun then
orphaned the populated log behind a fresh empty latest_run, and each
auto-retry repeated it. Recreation is now gated on the run having no
conversation history (in memory or in the run log).
- reconnectToLocalSession repainted the session from the log, and
fetchSessionLogs swallows read errors into an empty result — so a
failed read after a crash blanked a transcript we already held. The
previous in-memory events are now kept when the log read is empty.
- Upstream provider failures ("API Error: … timed out / terminated /
Connection error / 5xx") were pattern-matched as fatal session errors
via their "Internal error: …" wrapper, sending a healthy session into
the same teardown/recovery machinery. They're now classified as
transient: no recovery runs, and the user is told to just re-send.
Generated-By: PostHog Code
Task-Id: cb0f3882-78ec-41bc-8ad3-9916bb7734c7
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
Contributor
|
Reviews (1): Last reviewed commit: "fix(sessions): stop agent-crash recovery..." | Re-trigger Greptile |
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.
Problem
When the Claude agent process died at the wrong moment, the local-session recovery path could permanently wipe the task's history and leave the task unrecoverable (reported: "the agent just died and it stopped the task… it actually cleared out the entire history of the task itself"). Three compounding issues:
clearSessionErrorrecreated the session wheneverinitialPromptwas still set. That flag is only cleared by a live agent event, so it survives a crash (or a dropped event subscription) before the first event reaches the renderer — even when the agent already did real, logged work. The recreate branch tore the session down and calledcreateTaskRun, orphaning the populated run log behind a fresh emptylatest_run; the auto-retry loop could repeat this up to three times.reconnectToLocalSessionrepaints the session from the run log, andfetchSessionLogsswallows read errors into an empty result — so an unreadable log after a crash replaced a fully populated in-memory transcript with an empty one.isFatalSessionError's generic"internal error"pattern classified them as fatal and sent a healthy session through the same teardown/recovery machinery.Changes
clearSessionErroronly recreates the run when the conversation genuinely never started: a newrunHasConversationHistoryhelper checks the in-memory transcript, then the persisted run log, for any event beyond the user's own prompt echo. Otherwise it reconnects in place, preserving the run and its history.reconnectToLocalSessionkeeps the previous in-memory events when the log read produces nothing instead of overwriting them with an empty array.isTransientUpstreamErrorin@posthog/shared(patterns kept in sync with the agent'sclassifyAgentError);isFatalSessionErrornow returns false for these. On a transient failure the prompt handler skips recovery, clears the pending-prompt state, and surfaces "The AI provider timed out or dropped the connection. Your session is unaffected — please send the message again." instead of the raw internal error.How did you test this?
sessionServiceRetryConfig.test.ts(in-memory history, log-only history, prompt-echo-only), transcript preservation on empty log read and no-recovery-on-API-timeout insessionServiceHost.test.ts, and 12 classification cases inerrors.test.ts.typecheckclean on shared/core/ui; Biome clean with zeronoRestrictedImportsin core.Automatic notifications
🤖 Generated with Claude Code