fix(native-chat): returned text is skipped only when the draft already ends with it

A message handed back to the composer was dropped whenever its text appeared anywhere in the
draft ("go" inside a longer sentence), and its first line lost its indentation. It is now skipped
only when the draft is that text or ends with it after a blank line, and only the end is trimmed.
This commit is contained in:
Brennan Benson
2026-10-04 03:17:04 -07:00
parent 62ef8e7804
commit 1cd798685b
2 changed files with 29 additions and 9 deletions
@@ -66,12 +66,29 @@ describe('returnNativeChatDraftText', () => {
expect(readNativeChatDraftCache('agent-session:s1')).toBe('typed meanwhile\n\nunsent message')
})
it('is idempotent: text the draft already holds is not added again', () => {
it('is idempotent: text the draft already ends with is not added again', () => {
returnNativeChatDraftText('agent-session:s1', 'unsent message')
returnNativeChatDraftText('agent-session:s1', 'unsent message')
writeNativeChatDraftCache('agent-session:s1', 'before\n\nunsent message\n\nafter')
returnNativeChatDraftText('agent-session:s1', '\nunsent message ')
expect(readNativeChatDraftCache('agent-session:s1')).toBe('before\n\nunsent message\n\nafter')
expect(readNativeChatDraftCache('agent-session:s1')).toBe('unsent message')
writeNativeChatDraftCache('agent-session:s1', 'before\n\nunsent message \n')
returnNativeChatDraftText('agent-session:s1', 'unsent message ')
expect(readNativeChatDraftCache('agent-session:s1')).toBe('before\n\nunsent message \n')
})
it('still returns text that only appears inside a longer draft', () => {
writeNativeChatDraftCache('agent-session:s1', 'use the other algorithm and go')
returnNativeChatDraftText('agent-session:s1', 'go')
expect(readNativeChatDraftCache('agent-session:s1')).toBe(
'use the other algorithm and go\n\ngo'
)
writeNativeChatDraftCache('agent-session:s1', 'yes\n\nthen run the tests')
returnNativeChatDraftText('agent-session:s1', 'yes')
expect(readNativeChatDraftCache('agent-session:s1')).toBe('yes\n\nthen run the tests\n\nyes')
})
it("keeps the returned text's leading indentation", () => {
returnNativeChatDraftText('agent-session:s1', ' indented code\n')
expect(readNativeChatDraftCache('agent-session:s1')).toBe(' indented code')
})
it('puts no blank lines before text returned to a whitespace-only draft', () => {
@@ -60,16 +60,19 @@ export function appendNativeChatDraftCache(scopeKey: string, text: string): void
/**
* Hands text Orca could not deliver back to the person, with or without a composer showing it.
* Why skipped when already there: a hand-back can repeat (a crash before its copy was removed,
* two windows settling one message), and the person must see it once.
* Why skipped when the draft already ends with it: a hand-back can repeat (a crash before its copy
* was removed, two windows settling one message), and the person must see it once. Only the end
* counts, so text that merely appears inside a longer draft, or a second copy, still comes back.
*/
export function returnNativeChatDraftText(scopeKey: string, text: string): void {
const returned = text.trim()
if (returned === '') {
// Only the end is trimmed, as a send trims it: a first line's indentation is part of the text.
const returned = text.trimEnd()
if (returned.trim() === '') {
return
}
const previous = readNativeChatDraftCache(scopeKey)
if (previous.includes(returned)) {
const draft = previous.trimEnd()
if (draft === returned || draft.endsWith(`\n\n${returned}`)) {
return
}
appendToDraft(scopeKey, returned, previous)