feat: resume / re-run for aborted and failed sessions - #170
Closed
lucastononro wants to merge 4 commits into
Closed
Conversation
Backend:
- New POST /sessions/{id}/resume endpoint: relaunches the agent for a
failed / cancelled / timed-out (or stale *_running) session. Guards:
404 unknown session, 409 while an agent is running, 400 for a fresh
session with no prior run.
- services/agent/resume.py builds the recovered-progress block from the
three durable traces of the interrupted run: persisted agent_thought
tool history, the session task list, and the workspace file listing —
so the relaunched agent skips steps whose artifacts already exist on
the volume instead of redoing them.
- run_agent grows an opt-in resume_context kwarg (appended to the system
prompt; full prior conversation injected since a resume has no fresh
user message). Default path is unchanged.
- "resume" vs "retry" mode only shades the relaunch prompt.
Frontend:
- Resume/Retry banner above the ChatPane input when the session is
failed / cancelled / timed_out and idle; label + mode follow the state.
- New `session_resumed` SSE event -> status bubble + spinner in
useSessionStream; api.resumeSession() client call.
Tests: 10 new (endpoint guards, relaunch kwargs, SSE publish, context
assembly, and a legacy-path test proving send_message launches carry no
resume kwargs).
Closes #106
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
This was referenced Jul 19, 2026
…chat/workspace components (closes #97)
- resume.py: prior-run wording no longer claims "stopped early" for done/*_done sessions (resumed as re-runs of finished work) — new _prior_run_outcome helper used by both build_resume_context and build_resume_prompt. - resume.py: widen _clip annotation to str | None to match its runtime None guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # backend/services/agent/runner.py # frontend/src/components/chat/ChatPane.tsx # frontend/src/lib/useSessionStream.ts
lucastononro
added a commit
that referenced
this pull request
Jul 29, 2026
Owner
Author
|
Merged into integration branch |
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.
Summary
abort_agentcancels the asyncio task and all in-flight work is lost — with 10-minute sandbox executions and 50-trial Optuna runs there was no way to resume or retry afailed/cancelled/timed_outsession without re-driving the whole conversation. This adds a backend resume/retry endpoint that relaunches the agent with the prior tool history + completed-task state loaded, skipping steps whose artifacts already exist on the volume, plus a Resume/Retry action in the studio.Changes
Backend
POST /sessions/{id}/resume(routers/sessions.py): relaunches the chat agent for an interrupted session. Guards:404unknown session,409when an agent is already running (no silent abort — that's what this feature protects against),400for a freshcreatedsession with nothing to resume. Publishes asession_resumedSSE event and mirrorssend_message's launch wrapper (state eagerly set tochat_running,done/cancelled/failedtransitions on completion).services/agent/resume.py(new):build_resume_context()assembles a recovered-progress prompt block from the three durable traces of the interrupted run — persistedagent_thoughttool_use/tool_result history (capped at 40 blocks × 500 chars), the session task list, and the workspace file listing from the volume — with explicit resume rules ("treat listed files as completed work; verify cheaply instead of regenerating").run_agentgrows an opt-inresume_context: str | None = Nonekwarg: appended to the system prompt, and the full prior conversation is injected (a resume has no freshly-persisted user message to exclude). When the kwarg is absent the code path is unchanged.SessionResumeschema: optionalmode(resume|retry— only shades the relaunch prompt),model,agent_models,agent_thinking.Frontend
ChatPane: amber Resume/Retry banner above the input when the session isfailed/cancelled/timed_outand idle ("Retry" for failed, "Resume" otherwise).useSessionStream: reducer case for the newsession_resumedSSE event → status bubble + eagerisRunning.api.resumeSession(sessionId, mode);SSEEventunion extended withsession_resumed.Test plan
Existing (proving no regression): full backend suite green — 306 passed, 8 skipped (was 296/8 before);
tsc --noEmit,next lint,next buildall green.New (
backend/tests/test_resume.py, 10 tests):createdstate, 409 while runningrun_agentawaited withstage="chat", the builtresume_context, and a mode-shadeduser_prompt; session transitions todonewhen the run completessession_resumedSSE published exactly once with{mode, prior_state}build_resume_context: tool history (with ERROR marker) / task list / workspace listing sections, plain chat messages not duplicated, empty-session placeholdersis_resumable_statematrixsend_message(run_agent=true)launch carries noresume_contextkwarg and an unchangeduser_prompt— default behavior is untouched when the feature isn't triggeredCloses #106
🤖 Generated with Claude Code
https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
Greptile Summary
This PR adds resume/retry capability for
failed,cancelled, andtimed_outsessions, addressing the pain of losing in-flight work on long-running sandbox and Optuna runs. A newPOST /sessions/{id}/resumeendpoint relaunches the agent with recovered context (tool history, task list, workspace file listing) injected into the system prompt so completed steps are skipped.services/agent/resume.pyassembles the recovery prompt block from three durable traces;run_agentgains an opt-inresume_contextkwarg that appends the block and injects the full conversation history (rather than excluding the last message as normal follow-ups do); the router endpoint mirrorssend_message's fire-and-forget task pattern with 404/409/400 guards.useSessionStreamhandles the newsession_resumedSSE event to eagerly flipisRunning;api.resumeSessionand theSSEEventunion are extended to match.Confidence Score: 4/5
Safe to merge after addressing the missing error logging in the resume background task.
The _run_resume background task's except Exception block silently swallows errors without calling logger.exception or capture_exception, unlike the parallel _run_followup in send_message. A failing resume run would update the DB state to failed but leave no trace in logs or Sentry, making these failures invisible in production. All other aspects of the change — the new endpoint guards, the resume-context assembly, the runner changes, and the frontend wiring — are well-implemented and well-tested.
Files Needing Attention: backend/routers/sessions.py — the _run_resume exception handler needs the same logger.exception + capture_exception calls present in _run_followup
Important Files Changed
Sequence Diagram
sequenceDiagram participant UI as Frontend (ChatPane) participant API as POST /sessions/{id}/resume participant Resume as services/agent/resume participant Runner as run_agent participant SSE as SSE Broadcaster UI->>API: "POST /resume {mode}" API->>API: 404 if session not found API->>API: 409 if agent already running API->>API: 400 if not resumable state API->>Resume: build_resume_context(session_id, prior_state) Resume-->>API: system prompt block (tool history + tasks + files) API->>API: "session.state = chat_running" API->>SSE: "publish session_resumed {mode, prior_state}" SSE-->>UI: session_resumed → setIsRunning(true) API->>Runner: asyncio.create_task(run_agent(..., resume_context)) API-->>UI: "{status: resumed, mode, prior_state}" Runner->>Runner: "system_prompt += resume_context" Runner->>Runner: inject full history (no last-msg exclusion) Runner-->>API: done / CancelledError / Exception API->>API: "session.state = done / cancelled / failed"Reviews (2): Last reviewed commit: "Merge branch 'staging-v0.0.5' into fix/1..." | Re-trigger Greptile