fix(typing): force cleanup when dispatch idle is never received

Add a grace timer after markRunComplete so the typing controller
cleans up even when markDispatchIdle is never called, preventing
indefinite typing keepalive loops in cron and announce flows.

Made-with: Cursor
(cherry picked from commit 684eaf2893)
This commit is contained in:
SidQin-cyber
2026-02-26 21:04:31 +08:00
committed by Peter Steinberger
parent 4b259ab81b
commit 79176cc4e5

View File

@@ -61,6 +61,10 @@ export function createTypingController(params: {
clearTimeout(typingTtlTimer);
typingTtlTimer = undefined;
}
if (dispatchIdleTimer) {
clearTimeout(dispatchIdleTimer);
dispatchIdleTimer = undefined;
}
typingLoop.stop();
// Notify the channel to stop its typing indicator (e.g., on NO_REPLY).
// This fires only once (sealed prevents re-entry).
@@ -177,13 +181,28 @@ export function createTypingController(params: {
await startTypingLoop();
};
let dispatchIdleTimer: NodeJS.Timeout | undefined;
const DISPATCH_IDLE_GRACE_MS = 10_000;
const markRunComplete = () => {
runComplete = true;
maybeStopOnIdle();
if (!sealed && !dispatchIdle) {
dispatchIdleTimer = setTimeout(() => {
if (!sealed && !dispatchIdle) {
log?.("typing: dispatch idle not received after run complete; forcing cleanup");
cleanup();
}
}, DISPATCH_IDLE_GRACE_MS);
}
};
const markDispatchIdle = () => {
dispatchIdle = true;
if (dispatchIdleTimer) {
clearTimeout(dispatchIdleTimer);
dispatchIdleTimer = undefined;
}
maybeStopOnIdle();
};