← Back to PRs

#6582: fix(steer): prevent cross-thread reply contamination in queue mode

by NSEvent open 2026-02-01 20:44 View on GitHub →
channel: whatsapp-web commands agents
## Summary Fixes cross-thread reply contamination when using queue mode with steering enabled (#5615). When an agent is actively streaming a response, the fast "steer" path injects new messages directly into the running conversation via `queueEmbeddedPiMessage()`. However, this path only passes the prompt text—it doesn't carry the `originatingThreadId` that determines where replies should be routed. **The bug:** If User A sends a message in Thread A while the agent is responding to Thread B, User A's message gets steered into Thread B's conversation, and the reply goes to Thread B instead of Thread A. ## Changes - Add `ACTIVE_RUN_THREAD_CONTEXT` map to track thread context for each active run - `setActiveEmbeddedRun()` now accepts and stores thread context (wired through from `params.messageThreadId` in attempt.ts) - `hasActiveRunThreadContext()` checks if context is registered AND run is active, cleaning up stale entries if run ended - `getActiveRunThreadContext()` only returns context if run is active - Update `runReplyAgent()` to compare threads before fast steering: - Require `hasThreadContext` for ALL fast-steer cases - If context not registered → fall back to queue (conservative) - Use strict equality (no type coercion) to avoid false positives - Added clarifying comments: `undefined` thread value means "unthreaded/DM", not "unknown" ## Test plan - [x] Added 6 test cases covering all thread routing scenarios - [x] All 806+ test files pass - [x] `pnpm lint` passes - [x] `pnpm build` passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR fixes cross-thread reply contamination in queue+steer mode by tracking the originating thread for an active embedded run and requiring an exact thread match before using the fast `queueEmbeddedPiMessage()` steering path. The core change is in `src/agents/pi-embedded-runner/runs.ts`, which now stores a per-session thread context alongside the active run handle and exposes `hasActiveRunThreadContext()`/`getActiveRunThreadContext()` helpers. `runReplyAgent()` (`src/auto-reply/reply/agent-runner.ts`) uses these helpers to gate fast steering: if thread context isn’t registered, or if the incoming message’s `originatingThreadId` doesn’t exactly match the active run’s thread ID (including type), it falls back to enqueuing the follow-up run to preserve per-message routing. A dedicated test suite (`src/auto-reply/reply/agent-runner.steer-thread-routing.test.ts`) covers same-thread steering, cross-thread rejection, unregistered context conservatism, and type-mismatch cases. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk. - The change is tightly scoped, adds conservative guards to prevent misrouting, and includes focused test coverage for the new thread-routing behavior. The thread-context lifecycle is cleaned up on run completion and defensively on stale entries. - No files require special attention <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs