Skip to content

fix(agent): don't output a message on a warm resume boot#2780

Draft
skoob13 wants to merge 1 commit into
mainfrom
fix/warm-resume-no-premature-turn
Draft

fix(agent): don't output a message on a warm resume boot#2780
skoob13 wants to merge 1 commit into
mainfrom
fix/warm-resume-no-premature-turn

Conversation

@skoob13

@skoob13 skoob13 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

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_id set, await_user_message set) 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 via state.await_user_message (set only by the pre-warm path).
  • prepareResumeContext() — extracted from sendResumeMessage: 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.
  • Dispatch (sendInitialTaskMessage): on a warm resume, hydrate context and return without prompting.
  • user_message handler: 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 under warm 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 check clean on both changed files.
  • Typecheck: confirmed the change introduces zero new tsc errors (identical error count with and without the change; remaining errors are pre-existing unbuilt-workspace-dep noise).

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

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>
@github-actions

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 4814dde.

@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix 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

Comment thread packages/agent/src/server/agent-server.test.ts
@skoob13 skoob13 changed the title fix(agent): don't speak on a warm resume boot fix(agent): don't output a message on a warm resume boot Jun 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant