fix(agent): don't output a message on a warm resume boot#2780
Draft
skoob13 wants to merge 1 commit into
Draft
Conversation
A pre-warmed sandbox for a terminal conversation is created as a resume successor (resume_from_run_id set, await_user_message set) with no pending user message. On boot the agent loaded resume state and sent the "Continue from where you left off. The user is waiting for your response." prompt — so it produced a turn (re-answering the previous turn) before the user had sent anything. The user's real message then arrived as a separate follow-up turn, surfacing a stray assistant message ahead of the answer. A warm run has no waiting user. When the run is awaiting its first user message, hydrate the resume context (apply the checkpoint, build the history preamble) but don't prompt. The first real user message then prepends that context, so the agent answers it with full continuity in a single turn — reusing the exact prompt shape the pending-message resume path already uses. Genuine (non-warm) resumes are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
Contributor
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
packages/agent/src/server/agent-server.test.ts:1123-1136
**Non-parameterised negative-case tests**
The "does not treat a normal (non-warm) run as awaiting a first message" test packs three distinct input shapes into one block with multiple inline `expect` calls. The team's stated preference is parameterised tests (`it.each`), which would give each case its own label, independent failure output, and make it easy to add new edge cases (e.g., `await_user_message: false`) without touching the assertion logic.
Reviews (1): Last reviewed commit: "fix(agent): don't speak on a warm resume..." | 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 a sandbox conversation is pre-warmed after going idle, the agent emits a stray turn — re-answering the previous message — before the user's new message is processed. The user sees an out-of-place assistant reply ahead of the real answer.
Root cause: pre-warming a conversation whose latest run is terminal creates a resume successor run (
resume_from_run_idset,await_user_messageset) with no pending user message. On boot the agent loads resume state, finds no pending message, and sends the"Continue from where you left off. The user is waiting for your response."prompt — so it produces a turn into an empty room. The user's actual message then arrives as a separate follow-up turn.A warm run has no waiting user, so that assumption is false.
Changes
Enforce one rule: a warm/resume boot must not make the agent speak — the resumed history rides along with the user's first real message.
isAwaitingFirstUserMessage(taskRun)— detect a warm run viastate.await_user_message(set only by the pre-warm path).prepareResumeContext()— extracted fromsendResumeMessage: applies the git checkpoint (filesystem reuse — pre-warm latency preserved) and builds the history preamble, without prompting.buildResumePromptWithMessage()— the existing "history + the user's message + respond-to-it" block shape, now shared.sendInitialTaskMessage): on a warm resume, hydrate context and return without prompting.user_messagehandler: if a deferred resume context exists, prepend it to the first message (one-shot) — the agent answers that message with full continuity in a single turn, reusing the exact prompt shape the pending-message resume path already uses.Genuine (non-warm) crash-recovery resumes are unchanged. Rendering needs no change: the warm run still has
resume_from_run_id, so the client's resume-context filter hides the replayed preamble and the visible bubble comes from the optimistic echo — the user sees their message, then the answer, as one turn.How did you test this?
pnpm --filter @posthog/agent exec vitest run src/server/agent-server.test.ts→ 70 passed (67 existing + 3 new underwarm resume defers the first turn to the user's message: warm detection, non-warm negatives, and the single-turn merge with the user's message as a distinct block).biome checkclean on both changed files.tscerrors (identical error count with and without the change; remaining errors are pre-existing unbuilt-workspace-dep noise).Automatic notifications