fix(desktop): make Stop button actually interrupt when a turn is queued
When a follow-up message is queued during a busy turn, the composer clears and the primary button switches back to the Stop affordance. But clicking Stop ran interruptAndSendNextQueued(), which cancelled the turn and *immediately* re-sent the head of the queue. The auto-drain effect (busy true to false) compounded this: any explicit cancel flipped busy false and re-fired the queue. The net effect was that Stop appeared to never interrupt -- the agent kept running on the queued prompt. Fix: - Stop button (busy + empty composer) now always performs a pure interrupt via onCancel(); it no longer hijacks the queue. - An explicit interrupt latches userInterruptedRef so the busy to false auto-drain skips exactly one drain. Queued turns are preserved and the user resumes them deliberately (Cmd/Ctrl+K, Enter, or the per-row send-now arrow), matching the documented Esc=cancel / Cmd+K=send-next affordances. - Extracted the settle decision into shouldAutoDrainOnSettle() with unit tests covering natural completion vs. explicit interrupt.
This commit is contained in:
@@ -188,3 +188,39 @@ export const clearQueuedPrompts = (key: string | null | undefined) => {
|
||||
|
||||
writeSession(sid, [])
|
||||
}
|
||||
|
||||
/** Inputs to {@link shouldAutoDrainOnSettle}, captured at a `busy` transition. */
|
||||
export interface AutoDrainSettleInput {
|
||||
wasBusy: boolean
|
||||
isBusy: boolean
|
||||
queueLength: number
|
||||
userInterrupted: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether the composer should auto-drain the next queued prompt when a
|
||||
* turn settles (busy transitions true → false).
|
||||
*
|
||||
* The queue auto-advances when a turn *completes naturally*, but must NOT
|
||||
* advance when the user *explicitly interrupted* the turn via the Stop button.
|
||||
* Conflating the two made the Stop button appear to "never work": cancelling a
|
||||
* turn flipped busy → false, the queue immediately re-fired its head, and the
|
||||
* agent kept running. An explicit interrupt means stop — the queued turns are
|
||||
* preserved and the user resumes them deliberately (Cmd/Ctrl+K, Enter, or the
|
||||
* per-row "send now" arrow).
|
||||
*/
|
||||
export const shouldAutoDrainOnSettle = (params: AutoDrainSettleInput): boolean => {
|
||||
const { isBusy, queueLength, userInterrupted, wasBusy } = params
|
||||
|
||||
// Only react to a true → false transition; ignore steady state and entry.
|
||||
if (isBusy || !wasBusy) {
|
||||
return false
|
||||
}
|
||||
|
||||
// An explicit Stop suppresses exactly one auto-drain.
|
||||
if (userInterrupted) {
|
||||
return false
|
||||
}
|
||||
|
||||
return queueLength > 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user