diff --git a/src/main/native-chat/agent-session-journal/journal-item-appender.ts b/src/main/native-chat/agent-session-journal/journal-item-appender.ts index 146078d830c..1faec275ffd 100644 --- a/src/main/native-chat/agent-session-journal/journal-item-appender.ts +++ b/src/main/native-chat/agent-session-journal/journal-item-appender.ts @@ -8,7 +8,8 @@ import type { JournalReducerState } from './journal-reducer' import type { JournalAppendResult, JournalItemAppendOptions } from './journal-store-contracts' import type { JournalRow } from './journal-row-schema' -const NOTHING_RESOLVED = new Error('journal_item_resolved_to_nothing') +// A module-level Error's lazy stack retains the bundle's startup code and read buffer. +class NothingResolvedError extends Error {} export type JournalResolvedItem = { identity: AgentJournalItemIdentity; body: AgentJournalItemBody } @@ -41,7 +42,7 @@ export class JournalItemAppender { .enqueue((seq, ts) => { const resolved = resolve() if (resolved === null) { - throw NOTHING_RESOLVED + throw new NothingResolvedError('journal_item_resolved_to_nothing') } itemId = agentJournalItemKey(resolved.identity) return journalItemRowBuilder( @@ -54,7 +55,7 @@ export class JournalItemAppender { .then( (row) => appendResult(row, itemId), (error: unknown) => { - if (error === NOTHING_RESOLVED) { + if (error instanceof NothingResolvedError) { return null } throw error diff --git a/src/main/native-chat/agent-session-journal/journal-write-queue.test.ts b/src/main/native-chat/agent-session-journal/journal-write-queue.test.ts index 47764033180..69e4dd22f00 100644 --- a/src/main/native-chat/agent-session-journal/journal-write-queue.test.ts +++ b/src/main/native-chat/agent-session-journal/journal-write-queue.test.ts @@ -110,6 +110,31 @@ describe('when a journal write lands', () => { await inner expect(sequenceOf(journal, 2)).toBe(sequenceOf(journal, 1)! + 1) }) + + it('writes no row for a resolve that finds nothing, and keeps the line moving', async () => { + const journal = await idleJournal() + const before = journal.cursor().sequence + + await expect(journal.appendResolvedItem(() => null, OPTIONS)).resolves.toBeNull() + expect(journal.cursor().sequence).toBe(before) + await journal.appendItem(item(1), reply('after'), OPTIONS) + expect(journal.itemBody(agentJournalItemKey(item(1)))).toEqual(reply('after')) + }) + + it('preserves other resolver errors even when their message matches the no-row error', async () => { + const journal = await idleJournal() + const before = journal.cursor().sequence + const error = new Error('journal_item_resolved_to_nothing') + + await expect( + journal.appendResolvedItem(() => { + throw error + }, OPTIONS) + ).rejects.toBe(error) + expect(journal.cursor().sequence).toBe(before) + await journal.appendItem(item(1), reply('after'), OPTIONS) + expect(journal.itemBody(agentJournalItemKey(item(1)))).toEqual(reply('after')) + }) }) describe('the journal write queue', () => {