Files
openclaw/src
Ivan Z b381559184 fix(auto-reply): don't block reply completion on transcript mirror (#99549)
* fix(auto-reply): don't block reply completion on transcript mirror

mirrorTranscriptAfterDispatcherDelivery() opens with
`await dispatcher.waitForIdle()` and is awaited from sendFinalPayload,
i.e. before the reply operation completes. When a follow-up is queued
(queueDepth>=2) the dispatcher cannot go idle until this operation
clears, and the operation cannot clear until sendFinalPayload returns —
a completion<->dispatcher-idle deadlock. The reply operation stays
phase=running and surfaces as stalled_agent_run (recovery=none) until
the ~15-minute stuck-session abort.

Run the transcript mirror as fire-and-forget: it is post-delivery
bookkeeping and does not need to gate completion. The operation
completes immediately, the queued follow-up proceeds, the dispatcher
goes idle, and the detached mirror then records the transcript.

Validated live across three backends (LMStudio openai-completions,
claude-cli, DeepSeek cloud): queueDepth 2 and 3, zero stalls.

* test(auto-reply): regression coverage for detached transcript mirror

Two focused tests on the queueDepth>=2 completion<->dispatcher-idle deadlock:
- final reply completes even when dispatcher.waitForIdle() never resolves
  (pre-fix this awaited-mirror path deadlocked; the test hangs on the old code).
- a rejecting background mirror does not fail the reply (the detached .catch
  contains it instead of surfacing an unhandled rejection).

* fix(auto-reply): settle transcript mirrors after delivery

Co-authored-by: Zaytsev Ivan <ivan@jad.ru>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-03 18:19:58 -07:00
..