mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 14:38:23 -10:00
* fix(core): settle the loop reminder delivery at the model-acceptance boundary takeLoopReminder() cleared the pending map and logged the 'reminded' telemetry while the ToolResult request was only being assembled. Between the take and turn.run(), the send still awaits microcompaction and memory recall and yields pending goal events, so a consumer that stops the generator there loses the streak's warning (dropPendingReminders only unmarks still-pending keys) while telemetry credits a delivery the model never saw. The delivery is now only prepared at the take: the client commits it when the first model-accepted event arrives and abandons it — restoring the pending entries — in the send's finally when the request never reached the model. A full reset drops a prepared delivery so a late abandon cannot leak a stale warning into a new prompt. The agent-core round loop keeps its take-time accounting with an explicit commit since it has no consumer-visible suspension between the take and the model call. Fixes #15537 * fix(core): defer the agent-core loop reminder commit to model acceptance * fix(core): drop the ineffective pre-round reminder abandons The three abandonLoopReminderDelivery() calls at the abort/maxTurns/time checks could not change anything: each is followed by a break out of the round loop, the detector is method-local, and abandon only moves entries between two private maps without logging. They made the take-site comment and the maxTurns control read as if they were what keeps `reminded` truthful, so drop them and say what the control actually pins — no round crosses a model-acceptance boundary, so nothing commits. Also, from the same review round: - abandon's doc block named the wrong function. A retry drops the streak through resetToolCallCount(), which deletes the three hard-coded keys; dropPendingReminders() cannot reach entries already taken out of the pending map. - client.test.ts asserted not.toHaveBeenCalled() immediately after mockClear(), which cannot fail. Compare the call count with the value captured before the send instead, so the "suspended before turn.run" premise is actually pinned. - cover the multi-entry path: commit credits every entry of a delivery and abandon restores all of them, not just the first. - telemetry.md and both languages of the loop-protection design doc now say `reminded` is logged at model acceptance, and the "sent, never accepted, credited on a later send" case is added to the enumeration of not-counted cases. --------- Co-authored-by: suzhiguo7 <[email protected]> Co-authored-by: Shaojin Wen <[email protected]>