#19452: fix: skip stale responses in interrupt queue mode via generationId
size: L
## Problem
In interrupt queue mode, when a second message arrives while the first response is being generated/sent, the first response still gets delivered before the second (correct) response. This happens because the abort arrives too late — the response is already in the outbound pipeline.
## Solution
Implements a `generationId` approach as discussed in #19426:
1. **`queue/state.ts`** — Added per-session generation counter (`SESSION_GENERATION_IDS` map) with `getSessionGenerationId`, `incrementSessionGenerationId`, and `isGenerationCurrent` helpers.
2. **`get-reply-run.ts`** — On interrupt (when clearing queue and aborting), increment the session's generationId. Capture it and pass to `runReplyAgent`.
3. **`agent-runner.ts`** — Before delivering the final response, check if the run's generationId still matches the session's latest. If superseded by a newer interrupt, skip sending and log the drop.
## Changes
- 3 files changed, 45 insertions
- Zero behavioral change for non-interrupt queue modes (generationId defaults to 0 and only increments on interrupt)
Fixes #19426
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR implements a `generationId` mechanism to suppress stale responses in interrupt queue mode. When a second message arrives while the first response is in the outbound pipeline, the first response is now dropped at the delivery point instead of being sent to the user. The approach — a per-session monotonic counter incremented on each interrupt, captured at run-start, and validated before delivery — is conceptually sound and well-scoped.
Two issues were found:
- **Missing `generationId` in all existing test call-sites (will break CI):** `generationId: number` was added as a required field on the `runReplyAgent` params type, but none of the existing tests in `agent-runner.runreplyagent.test.ts` or `agent-runner.misc.runreplyagent.test.ts` pass it. This produces TypeScript compilation errors. At JS runtime, the missing field is `undefined`, and `isGenerationCurrent(key, undefined)` always returns `false`, causing the stale-response guard to fire on every test run and drop all replies — breaking every test that checks the returned payload. Making `generationId` optional (defaulting to `0`) or adding `generationId: 0` to all existing test call-sites is required.
- **`SESSION_GENERATION_IDS` is never cleaned up (memory leak):** The new module-level map accumulates an entry for every session that ever triggers an interrupt, but `clearSessionQueues` / `clearFollowupQueue` (called on abort, session reset, and subagent stop) only clean up `FOLLOWUP_QUEUES`, not `SESSION_GENERATION_IDS`. In a long-running gateway serving many sessions, this is an unbounded memory leak. The fix is to delete the map entry when the session's followup queue is cleared.
<h3>Confidence Score: 2/5</h3>
- Not safe to merge: the new required `generationId` parameter breaks all existing test call-sites and will cause TypeScript build failures; the missing cleanup path creates a memory leak in long-running processes.
- The core logic of the generationId approach is correct, but two concrete defects block merging: (1) `generationId` is a required param not added to any existing test call-sites, breaking TypeScript compilation and making every omitted-param test assert on a dropped response; (2) `SESSION_GENERATION_IDS` has no cleanup path, leaking memory per session over time.
- src/auto-reply/reply/agent-runner.ts (required param breaks all existing tests), src/auto-reply/reply/queue/cleanup.ts (SESSION_GENERATION_IDS never cleaned up), src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts and agent-runner.runreplyagent.test.ts (all call-sites missing generationId)
<sub>Last reviewed commit: d5ecfa5</sub>
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#15982: fix: pass agentId to resolveSessionFilePath in reply flow (NX-003)
by automagik-genie · 2026-02-14
76.4%
#22982: fix: prevent stale threadId from routing subagent announces to wron...
by unboxed-ai · 2026-02-21
76.2%
#21115: fix(agent): immediately abort active run on stop/abort message during…
by anillBhoi · 2026-02-19
76.0%
#19328: Fix: preserve modelOverride in agent handler (#5369)
by CodeReclaimers · 2026-02-17
75.0%
#2541: fix(agents): add error handling to orphaned message cleanup
by Episkey-G · 2026-01-27
74.2%
#22131: fix: clear seqByRun entries in clearAgentRunContext to prevent memo...
by alanwilhelm · 2026-02-20
74.0%
#3045: [AI-Assisted] fix: preserve pending tasks when subagent completes
by sid1943 · 2026-01-28
74.0%
#20431: fix(sessions): add session contamination guards and self-leak lock ...
by marcomarandiz · 2026-02-18
73.9%
#15792: fix: pass agentId to resolveSessionFilePath in additional call sites
by MisterGuy420 · 2026-02-13
73.9%
#18232: fix: webchat rapid messages create orphan sessions
by MisterGuy420 · 2026-02-16
73.3%