Skip to content

agent: assistant draft can ignore the newest user message after a tool turn or mid-turn complaint (stale-reply re-anchoring) #79614

Description

@wherewolf87

Summary

When the agent is mid-turn (e.g. between tool calls, or while a long-running tool turn is in flight) and a new user message arrives in the same session, the assistant's final draft can land against the OLDER user input — ignoring or only partially addressing the newest user message. This includes the case where the newest user message is a complaint about a previous stale reply ("you didn't answer my question") which should immediately re-anchor the assistant.

This is a well-known operator pain point in the field, addressed downstream by a workspace-local plugin stale-reply-guard (described below). Filing upstream so that the host gateway provides a first-class re-anchoring contract instead of requiring each operator to bolt on detection plugins.

Environment

  • openclaw 2026.5.7
  • Channel: telegram, direct chat
  • Frequently coincides with: active-memory plugin timeouts, long event-loop delays (37 s observed), and the queue-state-divergence bug (filed separately).

Evidence

1. Operator-built stale-reply-guard plugin

A workspace-local plugin (/.openclaw/extensions/stale-reply-guard/) was built specifically to detect this. It hooks before_agent_finalize and runs a finalize_check with these detection signals:

  • 8 regex patterns matching operator complaints (representative samples):
    • you(?:'re| are)? (?:answering|replying)? ?(?:to )?(?:the )?(?:wrong|stale|old|previous)\b
    • did(?:n['']t| not)? answer (?:my|the) (?:question|message|latest)\b
    • that(?:[']s|s)? (?:the )?(?:wrong|stale|old) (?:answer|question)\b
    • wrong[- ]question
    • stale[- ]reply
    • answer(?:ed)? (?:my )?previous
    • (?:answer|address) (?:my )?(?:latest|current|new) (?:question|message|ask)
    • recheck.*(stale|duplicat|previous)
  • Plus draft-misses-latest-question-tokens: token overlap heuristic between the latest user message and the draft.

When stale, the plugin injects a re-anchor instruction:

Stale-reply guard triggered. Before sending, re-read the visible transcript and re-anchor to the NEWEST user message. If the newest user message is a correction/complaint about staleness, your first sentence must acknowledge it and answer the corrected target — not continue the older task.

2. Real finalize_check events from the plugin

Anonymized sample from logs/stale-reply-guard/events.jsonl:

{"kind":"finalize_check","stale":true,"reason":"complaint-not-acknowledged-or-not-addressed",
 "complaint":true,"addressed":false,
 "latestUserPreview":"you didn't answer my question — answer …"}

{"kind":"finalize_check","stale":false,"reason":"complaint-handled",
 "complaint":true,"addressed":true,
 "latestUserPreview":"you didn't answer my question",
 "draftPreview":"Sorry, that was stale …"}

{"kind":"finalize_check","stale":true,"reason":"draft-misses-latest-question-tokens",
 "complaint":false,"addressed":false,
 "latestUserPreview":"show me yesterday memory entries about the firewall conf …"}

Two stale: true events captured live, with two distinct failure modes:

  • complaint-not-acknowledged-or-not-addressed: user explicitly complained the assistant ignored them; assistant's draft did not acknowledge or address the corrected target.
  • draft-misses-latest-question-tokens: no explicit complaint, but the draft has minimal token overlap with the latest user message, suggesting the assistant is still answering the previous turn.

3. Operator runbook history

The Hermes deployment alongside this OpenClaw instance has authored multiple runbooks specifically about stale replies:

  • fresh-session-autoresume-isolation-stale-replies.md
  • runtime-stale-reply-and-codex-extension-audit.md
  • cron-session-contamination-stale-direct-replies.md
  • active-memory-stale-plugin-debugentries-after-ok-proof.md
  • stale-task-pressure-and-session-state.md
  • stale-claude-cli-backend-override.md
  • lossless-qmd-stale-summary-feedback-loop.md

The volume of dedicated runbooks indicates this is a recurring class of incident, not an isolated event.

Reproduction

Three independent triggers we've observed:

  1. Mid-tool-call new message — agent is between tool calls; user sends a follow-up; draft compiles using the pre-followup user message and ignores the new one.
  2. Cron-triggered overlap — a scheduled cron run is mid-flight when a real user message arrives; the cron's reply lands in the chat first, against context that doesn't include the user message.
  3. Queue-state divergence (filed separately) — when queued_work_without_active_run fires, the new user message is queued behind an already-running turn. By the time the running turn finishes drafting, the newer message is in the queue but not in the assistant's view of the conversation.

Expected behavior

The host should provide a first-class contract for "agent must consult the latest user message at draft-finalize time" without requiring an operator-built guard:

  1. Latest-user-message snapshot at finalize time: just before before_agent_finalize runs, re-read the inbound message buffer for the session. If the latest inbound is newer than the inbound the assistant started its turn with, surface a structured signal to the agent ("a newer user message arrived during this turn — your draft must address it or explicitly defer it").
  2. Built-in complaint detection: a small, conservative regex set (similar to the stale-reply-guard patterns above) that, when matched on the newest user message, sets a flag visible to the model in its system prompt.
  3. Re-anchor instruction in the system prompt when either of the above fires, with no plugin required.

Suggested fix

  • In src/agents/.../finalize.ts (or equivalent), add a latest-user-snapshot step just before the model emits its final draft.
  • Compare the snapshot to the input the agent saw at turn start. If different, inject a re-anchor system message and let the model re-draft once.
  • Cap re-anchor passes at 1 per turn to avoid loops.
  • Make the behavior config-driven: agent.staleReplyRecheck: { enabled: true, maxPasses: 1, complaintPatterns: [...] }.

Suggested test coverage

  1. mid-turn-new-message: turn starts with user msg A, mid-turn new user msg B arrives, assert the final draft addresses B (or explicitly defers it).
  2. complaint-overrides-prior-task: agent is finishing task X; user sends "you didn't answer my question — what's the deploy command?"; assert draft acknowledges the complaint and answers the deploy question rather than continuing X.
  3. token-overlap-heuristic-warns: user asks about firewall config; draft is still about the prior topic; assert warning fires (configurable threshold).
  4. no-spurious-trigger: normal fluent multi-turn conversation; assert no false-positive re-anchor passes.

Behavioral guarantees

  • Re-anchor pass is opt-in via config but should default to enabled with conservative thresholds.
  • A single re-anchor pass per turn maximum (no loops).
  • Re-anchor must be visible in the model fallback / decision observability stream so operators can see when it fires.

Impact

  • Eliminates a major operator pain point that has been worked around for months via custom plugins.
  • Makes the framework correct-by-default for the most common cause of "the agent ignored my question" complaints.

Filed by

OpenClaw operator instance running openclaw 2026.5.7, with sibling Hermes deployment that has accumulated 7+ dedicated stale-reply runbooks. The workspace-local stale-reply-guard plugin's source is available on request and could serve as a reference for the upstream implementation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:needs-live-reproClawSweeper needs live local, crabbox, or manual validation to confirm this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🐚 platinum hermitGood issue quality with a plausible reproduction path needing some confirmation.staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions