import { describe, expect, it } from 'vitest'; import { buildSessionTranscriptViewModel } from '@/pages/Chat/session-transcript-view-model'; import { hydrateOpenCodeSession } from '@/lib/opencode-session-state'; describe('session transcript view model', () => { it('keeps the final answer separate from execution narration and native branches', () => { const nativeTranscript = hydrateOpenCodeSession('ses_1', [{ info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' }, parts: [ { id: 'native_reasoning', type: 'reasoning', text: 'Inspecting the change.' }, { id: 'native_narration', type: 'text', text: 'Reading the files first.' }, { id: 'native_subtask', type: 'subtask', parentID: 'root', sessionID: 'ses_child' }, { id: 'native_retry', type: 'retry', message: 'retrying' }, { id: 'native_final', type: 'text', text: 'The change is ready.' }, ], }], 'retry'); const model = buildSessionTranscriptViewModel({ messages: [{ id: 'msg_1', role: 'assistant', content: [ { type: 'text', text: 'Reading the files first.' }, { type: 'tool_use', id: 'call_1', name: 'read_file', input: { path: 'src/app.ts' } }, { type: 'text', text: 'The change is ready.' }, { type: 'thinking', thinking: 'Inspecting the change.' }, ], }], nativeTranscript, active: true, attention: [{ id: 'question_1', kind: 'question', sessionID: 'ses_1' }], }); expect(model.finalAnswer).toBe('The change is ready.'); expect(model.finalAnswerMessageId).toBe('msg_1'); expect(model.executionTrace.map((item) => item.kind)).toEqual(expect.arrayContaining([ 'narration', 'tool', 'subtask', 'retry', ])); expect(model.reasoningBlocks).toHaveLength(1); expect(model.reasoningBlocks[0]).toMatchObject({ collapsed: true }); expect(model.activeExecution).toBe(true); expect(model.status).toBe('retry'); expect(model.attention).toEqual([{ id: 'question_1', kind: 'question', sessionID: 'ses_1' }]); expect(model.nestedSubtasks).toEqual([ expect.objectContaining({ id: 'native_subtask', childSessionID: 'ses_child' }), ]); }); });