import { describe, expect, it } from 'vitest'; import { completeCompaction, createPendingCompaction, getOrderedSessionParts, getOrderedSessionCompactions, hydrateOpenCodeSession, reduceOpenCodeEvent, removeCompaction, } from '@/lib/opencode-session-state'; describe('OpenCode native session state', () => { it('hydrates native parts without dropping execution metadata', () => { const state = hydrateOpenCodeSession('ses_1', [{ info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1', time: { created: 1_700_000_000_000 }, }, parts: [ { id: 'text_1', type: 'text', text: 'Final answer' }, { id: 'reason_1', type: 'reasoning', text: 'Checking the repository' }, { id: 'tool_1', type: 'tool', tool: 'read', callID: 'call_1', state: { status: 'completed', input: { path: 'README.md' }, output: 'ok', time: { start: 1_700_000_000_010, end: 1_700_000_000_110 }, }, }, { id: 'step_1', type: 'step-finish', reason: 'stop', cost: 0.01 }, { id: 'future_1', type: 'future-native-part', detail: 'preserve me' }, ], }]); const parts = getOrderedSessionParts(state, 'msg_1'); expect(parts.map((part) => part.type)).toEqual([ 'text', 'reasoning', 'tool', 'step-finish', 'unknown', ]); expect(parts[2]).toEqual(expect.objectContaining({ tool: 'read', callID: 'call_1', state: expect.objectContaining({ status: 'completed' }), })); expect(parts.at(-1)).toEqual(expect.objectContaining({ rawType: 'future-native-part', detail: expect.stringContaining('preserve me'), })); }); it('applies full updates, deltas, and removals by stable part id', () => { const base = hydrateOpenCodeSession('ses_1', []); const updated = reduceOpenCodeEvent(base, { type: 'message.part.updated', payload: { sessionID: 'ses_1', messageID: 'msg_1', part: { id: 'part_1', type: 'text', text: 'Hello' }, }, }); const withDelta = reduceOpenCodeEvent(updated, { type: 'message.part.delta', payload: { sessionID: 'ses_1', messageID: 'msg_1', partID: 'part_1', field: 'text', delta: ' world', }, }); const duplicateDelta = reduceOpenCodeEvent(withDelta, { type: 'message.part.delta', payload: { sessionID: 'ses_1', messageID: 'msg_1', partID: 'part_1', field: 'text', delta: ' world', }, }); expect(getOrderedSessionParts(duplicateDelta, 'msg_1')[0]?.text).toBe('Hello world'); const removed = reduceOpenCodeEvent(duplicateDelta, { type: 'message.part.removed', payload: { sessionID: 'ses_1', messageID: 'msg_1', partID: 'part_1', }, }); expect(getOrderedSessionParts(removed, 'msg_1')).toEqual([]); }); it('ignores events from another session and tracks native lifecycle status', () => { const state = hydrateOpenCodeSession('ses_1', []); const untouched = reduceOpenCodeEvent(state, { type: 'message.part.delta', payload: { sessionID: 'ses_2', messageID: 'msg_2', partID: 'part_2', delta: 'wrong session', }, }); expect(untouched).toBe(state); const busy = reduceOpenCodeEvent(state, { type: 'session.status', payload: { sessionID: 'ses_1', status: { type: 'retry' } }, }); expect(busy?.status).toBe('retry'); const idle = reduceOpenCodeEvent(busy, { type: 'session.idle', payload: { sessionID: 'ses_1' }, }); expect(idle?.status).toBe('idle'); }); it('deduplicates replayed delta events by event identity without dropping equal real chunks', () => { let state = hydrateOpenCodeSession('ses_1', []); state = reduceOpenCodeEvent(state, { type: 'message.part.updated', payload: { sessionID: 'ses_1', messageID: 'msg_1', part: { id: 'part_1', type: 'text', text: '' }, }, }); state = reduceOpenCodeEvent(state, { type: 'message.part.delta', payload: { sessionID: 'ses_1', messageID: 'msg_1', partID: 'part_1', delta: 'x', eventID: 'evt_1', }, }); state = reduceOpenCodeEvent(state, { type: 'session.status', payload: { sessionID: 'ses_1', status: { type: 'busy' } }, }); state = reduceOpenCodeEvent(state, { type: 'message.part.delta', payload: { sessionID: 'ses_1', messageID: 'msg_1', partID: 'part_1', delta: 'x', eventID: 'evt_1', }, }); expect(getOrderedSessionParts(state, 'msg_1')[0]?.text).toBe('x'); const secondRealChunk = reduceOpenCodeEvent(state, { type: 'message.part.delta', payload: { sessionID: 'ses_1', messageID: 'msg_1', partID: 'part_1', delta: 'x', eventID: 'evt_2', }, }); expect(getOrderedSessionParts(secondRealChunk, 'msg_1')[0]?.text).toBe('xx'); }); it('hydrates historical compaction Parts as completed timeline events in transcript order', () => { const state = hydrateOpenCodeSession('ses_1', [{ info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' }, parts: [ { id: 'compact_1', type: 'compaction', auto: false }, { id: 'compact_2', type: 'compaction', auto: true }, ], }]); expect(getOrderedSessionCompactions(state)).toEqual([ expect.objectContaining({ id: 'compaction:ses_1:part:compact_1', source: 'manual', status: 'completed', order: 0, anchorMessageID: 'msg_1', anchorPartID: 'compact_1', nativePartID: 'compact_1', }), expect.objectContaining({ source: 'automatic', status: 'completed', order: 1, nativePartID: 'compact_2', }), ]); }); it('hydrates cold compaction snapshots as completed even while the session is busy', () => { const state = hydrateOpenCodeSession('ses_1', [{ info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' }, parts: [ { id: 'compact_1', type: 'compaction', auto: true }, { id: 'compact_2', type: 'compaction', auto: true }, ], }], 'busy'); expect(getOrderedSessionCompactions(state).map((event) => event.status)) .toEqual(['completed', 'completed']); }); it('does not attach a historical compaction to a later ordinary busy run', () => { const current = hydrateOpenCodeSession('ses_1', [{ info: { id: 'msg_2', role: 'user', sessionID: 'ses_1' }, parts: [{ id: 'text_2', type: 'text', text: 'continue normally' }], }], 'busy'); const state = hydrateOpenCodeSession('ses_1', [ { info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' }, parts: [{ id: 'compact_1', type: 'compaction', auto: true }], }, { info: { id: 'msg_2', role: 'user', sessionID: 'ses_1' }, parts: [{ id: 'text_2', type: 'text', text: 'continue normally' }], }, ], 'busy', current); expect(getOrderedSessionCompactions(state)).toEqual([ expect.objectContaining({ nativePartID: 'compact_1', status: 'completed' }), ]); }); it('keeps a matching real-time native compaction running during active hydration', () => { const current = reduceOpenCodeEvent( hydrateOpenCodeSession('ses_1', [], 'busy'), { type: 'message.part.updated', payload: { sessionID: 'ses_1', messageID: 'msg_1', part: { id: 'compact_1', type: 'compaction', auto: true }, }, }, ); const state = hydrateOpenCodeSession('ses_1', [{ info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' }, parts: [{ id: 'compact_1', type: 'compaction', auto: true }], }], 'busy', current); expect(getOrderedSessionCompactions(state)).toEqual([ expect.objectContaining({ nativePartID: 'compact_1', status: 'running' }), ]); }); it('does not downgrade a completed compaction during busy history hydration', () => { const snapshot = [{ info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' }, parts: [{ id: 'compact_1', type: 'compaction', auto: true }], }]; const running = hydrateOpenCodeSession('ses_1', snapshot, 'busy'); const completed = reduceOpenCodeEvent(running, { type: 'session.compacted', payload: { sessionID: 'ses_1', eventID: 'evt_compacted_1' }, }); const rehydrated = hydrateOpenCodeSession('ses_1', snapshot, 'busy', completed); expect(getOrderedSessionCompactions(rehydrated)).toEqual([ expect.objectContaining({ nativePartID: 'compact_1', status: 'completed', }), ]); }); it('merges a manual pending event with its native Part without changing the UI id', () => { const empty = hydrateOpenCodeSession('ses_1', [], 'busy'); const pending = createPendingCompaction(empty, { id: 'ui_compact_1', source: 'manual', runID: 'run_1', generation: 3, startedAt: 1_700_000_000_000, }); const withNativePart = reduceOpenCodeEvent(pending, { type: 'message.part.updated', payload: { sessionID: 'ses_1', eventID: 'evt_part_1', messageID: 'msg_1', part: { id: 'native_compact_1', type: 'compaction', auto: false, runID: 'run_1', generation: 3, }, }, }); expect(getOrderedSessionCompactions(withNativePart)).toEqual([ expect.objectContaining({ id: 'ui_compact_1', nativePartID: 'native_compact_1', nativeEventID: 'evt_part_1', anchorMessageID: 'msg_1', status: 'running', }), ]); const replayed = reduceOpenCodeEvent(withNativePart, { type: 'message.part.updated', payload: { sessionID: 'ses_1', eventID: 'evt_part_2', messageID: 'msg_1', part: { id: 'native_compact_1', type: 'compaction', auto: false }, }, }); expect(getOrderedSessionCompactions(replayed)).toHaveLength(1); expect(getOrderedSessionCompactions(replayed)[0]?.id).toBe('ui_compact_1'); }); it('preserves unmatched running pending events across active hydration only', () => { const current = createPendingCompaction( hydrateOpenCodeSession('ses_1', [], 'busy'), { id: 'ui_pending', source: 'manual', runID: 'run_1', generation: 1 }, ); const active = hydrateOpenCodeSession('ses_1', [], 'busy', current); expect(getOrderedSessionCompactions(active).map((event) => event.id)).toEqual(['ui_pending']); const historical = hydrateOpenCodeSession('ses_1', [], 'idle', current); expect(getOrderedSessionCompactions(historical)).toEqual([]); }); it('merges an active snapshot Part into the current manual pending UI identity', () => { const current = createPendingCompaction( hydrateOpenCodeSession('ses_1', [], 'busy'), { id: 'ui_pending', source: 'manual', runID: 'run_1', generation: 1 }, ); const hydrated = hydrateOpenCodeSession('ses_1', [{ info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' }, parts: [{ id: 'native_compact_1', type: 'compaction', auto: false }], }], 'busy', current); expect(getOrderedSessionCompactions(hydrated)).toEqual([ expect.objectContaining({ id: 'ui_pending', nativePartID: 'native_compact_1', status: 'running', runID: 'run_1', generation: 1, }), ]); }); it('closes an older running native compaction when a later one arrives', () => { let state = hydrateOpenCodeSession('ses_1', [], 'busy'); for (const [messageID, partID] of [['msg_1', 'compact_1'], ['msg_2', 'compact_2']]) { state = reduceOpenCodeEvent(state, { type: 'message.part.updated', payload: { sessionID: 'ses_1', messageID, part: { id: partID, type: 'compaction', auto: true }, }, })!; } expect(getOrderedSessionCompactions(state).map((event) => event.status)) .toEqual(['completed', 'running']); }); it('completes and removes only the compaction matching all supplied correlation keys', () => { let state = createPendingCompaction( hydrateOpenCodeSession('ses_1', [], 'busy'), { id: 'ui_1', source: 'manual', runID: 'run_1', generation: 1 }, ); state = createPendingCompaction(state, { id: 'ui_2', source: 'manual', runID: 'run_2', generation: 2, nativePartID: 'native_2', }); const wrongGeneration = completeCompaction( state, { runID: 'run_2', generation: 1, nativePartID: 'native_2' }, ); expect(wrongGeneration).toBe(state); state = completeCompaction( state, { runID: 'run_2', generation: 2, nativePartID: 'native_2' }, { nativeEventID: 'evt_complete_2', completedAt: 1_700_000_000_100 }, ); expect(state.compactionsById.ui_2).toEqual(expect.objectContaining({ status: 'completed', nativeEventID: 'evt_complete_2', completedAt: 1_700_000_000_100, })); expect(state.compactionsById.ui_1?.status).toBe('running'); const untouched = removeCompaction(state, { runID: 'run_1', generation: 2 }); expect(untouched).toBe(state); state = removeCompaction(state, { runID: 'run_1', generation: 1 }); expect(getOrderedSessionCompactions(state).map((event) => event.id)).toEqual(['ui_2']); }); it('completes compaction on session.compacted without marking the transcript idle', () => { const busy = createPendingCompaction( hydrateOpenCodeSession('ses_1', [], 'busy'), { id: 'ui_compact_1', source: 'automatic', runID: 'run_1', generation: 4 }, ); const compacted = reduceOpenCodeEvent(busy, { type: 'session.compacted', payload: { sessionID: 'ses_1', eventID: 'evt_complete_1', runID: 'run_1', generation: 4, }, }); expect(compacted?.status).toBe('busy'); expect(compacted?.compactionsById.ui_compact_1).toEqual(expect.objectContaining({ status: 'completed', nativeEventID: 'evt_complete_1', })); const idle = reduceOpenCodeEvent(compacted, { type: 'session.idle', payload: { sessionID: 'ses_1' }, }); expect(idle?.status).toBe('idle'); }); it('uses session.idle as a successful completion fallback', () => { const busy = createPendingCompaction( hydrateOpenCodeSession('ses_1', [], 'busy'), { id: 'ui_compact_idle', source: 'manual', runID: 'run_idle', generation: 5 }, ); const idle = reduceOpenCodeEvent(busy, { type: 'session.idle', payload: { sessionID: 'ses_1' }, }); expect(idle?.status).toBe('idle'); expect(idle?.compactionsById.ui_compact_idle?.status).toBe('completed'); }); it('removes unfinished compactions on a fatal session error', () => { const busy = createPendingCompaction( hydrateOpenCodeSession('ses_1', [], 'busy'), { id: 'ui_compact_error', source: 'automatic', runID: 'run_error', generation: 6 }, ); const failed = reduceOpenCodeEvent(busy, { type: 'session.error', payload: { sessionID: 'ses_1', error: 'provider failed' }, }); expect(failed?.status).toBe('idle'); expect(failed?.compactionOrder).toEqual([]); expect(failed?.compactionsById.ui_compact_error).toBeUndefined(); }); });