mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 08:03:28 +00:00
fix(native-chat): an adoption that fails to import keeps the conversation open
The attach now writes into the conversation's own open journal, but a failed transcript import still closed it as if it were the attach's provisional one. The conversation stayed indexed with a closed journal, so every later send answered "could not be recorded" and every attach failed again until the app restarted. The import now closes only a journal the attach opened for itself.
This commit is contained in:
+44
@@ -15,6 +15,7 @@ import { performAttach, type AttachFlowInput } from './structured-agent-session-
|
||||
import { AgentSessionJournal } from '../agent-session-journal/journal-store'
|
||||
import { agentSessionJournalCloseRetries } from '../agent-session-journal/journal-close-retry'
|
||||
import * as legacyImport from '../agent-session-journal/journal-legacy-import'
|
||||
import { StructuredAgentSessionHost } from './structured-agent-session-host'
|
||||
|
||||
const NOW = 1_800_000_000_000
|
||||
const SESSION = 'codex_adopting_session'
|
||||
@@ -225,6 +226,49 @@ describe('adopting a provider conversation on create', () => {
|
||||
expect(close).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('leaves the conversation writable when the import fails after acquiring', async () => {
|
||||
root = await mkdtemp(join(tmpdir(), 'orca-adopt-host-failure-'))
|
||||
const transcriptPath = join(root, 'rollout.jsonl')
|
||||
await writeCodexRollout(transcriptPath, 'valid source')
|
||||
store = await AgentSessionRecordStore.open({ directory: join(root, 'store'), hostId: 'local' })
|
||||
const host = new StructuredAgentSessionHost({
|
||||
store,
|
||||
adapter: adapter(),
|
||||
journalRoot: root,
|
||||
claimKeyId: 'key-1',
|
||||
mintSpawnToken: () => 'spawn-a',
|
||||
now: () => NOW
|
||||
})
|
||||
vi.spyOn(AgentSessionJournal.prototype, 'replaceEpochItems').mockRejectedValueOnce(
|
||||
new Error('disk write failed')
|
||||
)
|
||||
const attached = await host
|
||||
.attach({ callerKey: 'client-1' }, attachParams(transcriptPath))
|
||||
.catch(() => null)
|
||||
expect(attached?.ok).not.toBe(true)
|
||||
|
||||
const body = { kind: 'message' as const, role: 'user' as const, blocks: [] }
|
||||
const sent = await host.send(
|
||||
{ callerKey: 'client-1' },
|
||||
{
|
||||
envelope: {
|
||||
sessionId: SESSION,
|
||||
clientOperationId: `${NOW}-${'2'.padStart(32, '0')}`,
|
||||
expectedRuntimeFence: null,
|
||||
payloadFingerprint: computeAgentSessionPayloadFingerprint({
|
||||
method: 'agentSession.send',
|
||||
sessionId: SESSION,
|
||||
fields: { body }
|
||||
})
|
||||
},
|
||||
body
|
||||
}
|
||||
)
|
||||
// The failed attach kept the conversation's own journal open, so the send is recorded.
|
||||
expect(sent).toMatchObject({ ok: true, value: { submission: { dispatchState: 'pending' } } })
|
||||
await host.flushAllStreamedEvents()
|
||||
})
|
||||
|
||||
it('prepares a valid source once before acquisition and imports those exact items', async () => {
|
||||
root = await mkdtemp(join(tmpdir(), 'orca-adopt-once-'))
|
||||
const transcriptPath = join(root, 'rollout.jsonl')
|
||||
|
||||
@@ -60,13 +60,16 @@ export async function importAdoptedTranscript(
|
||||
params: AgentSessionAttachParams,
|
||||
attached: AttachedJournal,
|
||||
record: AgentSessionRecord,
|
||||
prepared: JournalReplacementItem[] | null
|
||||
prepared: JournalReplacementItem[] | null,
|
||||
ownsJournal: boolean
|
||||
): Promise<void> {
|
||||
try {
|
||||
await applyAdoptedTranscript(params, attached, record, prepared)
|
||||
} catch (error) {
|
||||
// Publication has not taken ownership of this provisional journal yet.
|
||||
await agentSessionJournalCloseRetries.closeOrRetain(attached.journal)
|
||||
// A journal the attach opened for itself has no other owner; the conversation's stays open.
|
||||
if (ownsJournal) {
|
||||
await agentSessionJournalCloseRetries.closeOrRetain(attached.journal)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +212,13 @@ export async function performAttach(
|
||||
...(input.openConversation ? { openConversation: input.openConversation } : {}),
|
||||
providerHistoryWindow
|
||||
})
|
||||
await importAdoptedTranscript(params, attached, record, preparedTranscript.items)
|
||||
await importAdoptedTranscript(
|
||||
params,
|
||||
attached,
|
||||
record,
|
||||
preparedTranscript.items,
|
||||
input.openConversation === undefined
|
||||
)
|
||||
await input.onAttached(attached, acquisitionGeneration, acquiredOwner, providerChildPhase)
|
||||
await store.recordOperationOutcome({
|
||||
callerKey: input.callerKey,
|
||||
|
||||
Reference in New Issue
Block a user