diff --git a/src/renderer/src/components/native-chat/use-structured-agent-session-conversation-stop.test.tsx b/src/renderer/src/components/native-chat/use-structured-agent-session-conversation-stop.test.tsx index e3e22070f79..4724de5b4bd 100644 --- a/src/renderer/src/components/native-chat/use-structured-agent-session-conversation-stop.test.tsx +++ b/src/renderer/src/components/native-chat/use-structured-agent-session-conversation-stop.test.tsx @@ -282,4 +282,29 @@ describe("a conversation Stop's first press", () => { await pressWithStamp({ kind: 'not-done', notice: 'No.', answered: true }, true) expect(toast.error).toHaveBeenCalledExactlyOnceWith('No.') }) + + it('says a refusal that answers a resend, after a quiet lost answer on the press', async () => { + const outcomes: StructuredAgentSessionWriteOutcome[] = [ + { kind: 'not-done', notice: 'Lost.', answered: false }, + { kind: 'not-done', notice: "The agent wasn't stopped.", answered: true } + ] + const writeAs = vi.fn(async () => outcomes.shift() ?? DONE) + const { view, stopOutbox } = harness(writeAs) + let pressed: Promise = Promise.resolve() + act(() => { + pressed = view.result.current() + }) + view.rerender({ outbox: [stamped(stopOutbox.mock.calls[0]?.[0])], submissions: [] }) + await act(async () => { + await pressed + }) + for (let step = 0; step < 5; step += 1) { + await act(async () => { + await vi.advanceTimersByTimeAsync(1_000) + }) + } + // The mock outbox keeps the stamp owed, so the Stop may go once more; its answer is said once. + expect(writeAs.mock.calls.length).toBeGreaterThanOrEqual(2) + expect(toast.error).toHaveBeenCalledExactlyOnceWith("The agent wasn't stopped.") + }) }) diff --git a/src/renderer/src/components/native-chat/use-structured-agent-session-conversation-stop.ts b/src/renderer/src/components/native-chat/use-structured-agent-session-conversation-stop.ts index 689436a13e7..4f3fed950e9 100644 --- a/src/renderer/src/components/native-chat/use-structured-agent-session-conversation-stop.ts +++ b/src/renderer/src/components/native-chat/use-structured-agent-session-conversation-stop.ts @@ -102,12 +102,17 @@ export function useStructuredAgentSessionConversationStop(args: { recordStopAnswer(stopOperationId, { kind: 'answered', cursor: outcome.cursor }) return } - // A refusal is said; a lost answer only when nothing will send the Stop again. + // A refusal is said on any attempt, a resend's too; a lost answer only on the press, and + // only when nothing will send the Stop again. if ( outcome.kind === 'not-done' && - firstPress && (outcome.answered || - !stopWillBeResent(latest.current.outbox, latest.current.submissions, stopOperationId)) + (firstPress && + !stopWillBeResent( + latest.current.outbox, + latest.current.submissions, + stopOperationId + ))) ) { toast.error(outcome.notice) }