diff --git a/src/renderer/src/components/native-chat/structured-agent-session-outbox-chat-close.ts b/src/renderer/src/components/native-chat/structured-agent-session-outbox-chat-close.ts index c442f93a425..3a87403d43e 100644 --- a/src/renderer/src/components/native-chat/structured-agent-session-outbox-chat-close.ts +++ b/src/renderer/src/components/native-chat/structured-agent-session-outbox-chat-close.ts @@ -1,8 +1,8 @@ // What closing a chat does to its outbox: it never throws away a message the person sent. A -// cancelled launch's own prompt, never sent, goes with the launch (its notes back on the shelf). -// Any other message that never went out comes back to the conversation's draft, with its notes -// following the text. One that went out may be the host's, so it stays and settles when the chat -// is reopened. +// cancelled launch's own prompt, never sent, goes with the launch. Any other message that never +// went out comes back to the conversation's draft; its notes follow the text, except for a +// cancelled launch, whose draft no chat shows, so they go back on the shelf. One that went out may +// be the host's, so it stays and settles when the chat is reopened. import type { StructuredAgentSessionOutboxEntry } from '../../../../shared/structured-agent-session-outbox' import { structuredAgentSessionEntryAwaitsSettlement } from '../../../../shared/structured-agent-session-outbox-admission' @@ -31,7 +31,7 @@ export function settleStructuredAgentSessionOutboxForClosedChat( } else if (!(options.cancelledLaunch && entry.source === 'launch')) { // The draft holds the text before the notes it carried are cleared. returnStructuredAgentSessionMessage(entry) - endStructuredAgentSessionEntry(entry, 'returned') + endStructuredAgentSessionEntry(entry, options.cancelledLaunch ? 'discarded' : 'returned') } } if (kept.length !== current.length) { diff --git a/src/renderer/src/store/slices/tabs/structured-chat-close-keeps-outbox.test.tsx b/src/renderer/src/store/slices/tabs/structured-chat-close-keeps-outbox.test.tsx index 5bb9c50240a..2b86ad811e7 100644 --- a/src/renderer/src/store/slices/tabs/structured-chat-close-keeps-outbox.test.tsx +++ b/src/renderer/src/store/slices/tabs/structured-chat-close-keeps-outbox.test.tsx @@ -55,6 +55,7 @@ import { resetStructuredAgentSessionCarriedNotesForTests } from '@/components/na import { useStructuredAgentSessionOutbox } from '@/components/native-chat/use-structured-agent-session-outbox' import { markStructuredAgentSessionLaunchCancelled } from '@/lib/structured-agent-session-launch-registry' import { isNoteInFlight } from '@/lib/notes-send-in-flight' +import { subscribeToStructuredAgentSessionEntryEndings } from '@/components/native-chat/structured-agent-session-entry-endings' const WT = 'repo1::/path/wt1' const SID = 'session-close-1' @@ -193,4 +194,24 @@ describe('closing a chat tab while the host is out of reach', () => { expect(draft()).toBe('') expect(isNoteInFlight('note-a')).toBe(false) }) + + // No chat shows a cancelled launch's draft, so a message typed into it gives its notes back. + it("ends a cancelled launch's other unsent messages as discarded, so their notes come back", () => { + const { store, chat } = seed() + markStructuredAgentSessionLaunchCancelled(WT, SID, 'local') + const typed = entry('TYPED-TEXT', { carriedNoteKeys: ['note-b'] }) + writeOutbox(SID, [typed]) + const endings: string[] = [] + const unsubscribe = subscribeToStructuredAgentSessionEntryEndings((ended, ending) => { + if (ended.clientMessageId === typed.clientMessageId) { + endings.push(ending) + } + }) + + store.getState().closeUnifiedTab(chat.id) + unsubscribe() + expect(endings).toEqual(['discarded']) + expect(readOutbox(SID)).toEqual([]) + expect(isNoteInFlight('note-b')).toBe(false) + }) })