diff --git a/src/__tests__/session-cmd.test.ts b/src/__tests__/session-cmd.test.ts index 7d885e0e0..1a6addeba 100644 --- a/src/__tests__/session-cmd.test.ts +++ b/src/__tests__/session-cmd.test.ts @@ -1600,6 +1600,50 @@ describe('adapters: native cwd recovery from JSONL records', () => { }); }); +describe('codex readSession: reasoning', () => { + it('reads the reasoning summary and content Codex writes', async () => { + const SID = '019e0000-1111-7222-8333-666666666666'; + const dir = path.join(mocks.home, '.codex', 'sessions', '2026', '10', '01'); + fs.mkdirSync(dir, { recursive: true }); + const lines = [ + { type: 'session_meta', payload: { id: SID, timestamp: '2026-10-01T10:00:00.000Z', cwd: '/work/app' } }, + { type: 'response_item', payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'why' }] } }, + { + type: 'response_item', + payload: { + type: 'reasoning', + summary: [ + { type: 'summary_text', text: '**Reading the config**' }, + { type: 'summary_text', text: 'The port comes from env.' }, + ], + content: null, + encrypted_content: 'gAAAAA', + }, + }, + { + type: 'response_item', + payload: { + type: 'reasoning', + summary: [{ type: 'summary_text', text: 'short' }], + content: [{ type: 'reasoning_text', text: 'full reasoning' }], + encrypted_content: null, + }, + }, + ]; + fs.writeFileSync( + path.join(dir, `rollout-2026-10-01T10-00-00-${SID}.jsonl`), + lines.map((l) => JSON.stringify({ timestamp: '2026-10-01T10:00:00.000Z', ...l })).join('\n') + '\n', + ); + + const session = await new CodexAdapter().readSession({ sessionId: SID }); + const thinking = session.messages.flatMap((m) => m.content).filter((b) => b.type === 'thinking'); + expect(thinking).toEqual([ + { type: 'thinking', text: '**Reading the config**\n\nThe port comes from env.' }, + { type: 'thinking', text: 'full reasoning' }, + ]); + }); +}); + describe('codex readSession: freeform tool calls', () => { it('keeps the apply_patch input of a custom_tool_call', async () => { const SID = '019e0000-1111-7222-8333-444444444444'; diff --git a/src/session-flow/adapters/codex.ts b/src/session-flow/adapters/codex.ts index 8bda6c9e8..8977cb41d 100644 --- a/src/session-flow/adapters/codex.ts +++ b/src/session-flow/adapters/codex.ts @@ -565,16 +565,17 @@ export class CodexAdapter extends AgentAdapter { messages.push({ role: 'user', content: [block], timestamp: parseCodexTimestamp(rec.timestamp) }); } } else if (ptype === 'reasoning') { - // reasoning → ThinkingBlock - const rawContent = payload.rawContent as Array> | undefined; - let text = ''; - if (Array.isArray(rawContent)) { - for (const part of rawContent) { - if (part.type === 'reasoning_text') { - text += String(part.text ?? ''); - } - } - } + // reasoning → ThinkingBlock. Codex writes the readable part to summary and may add content; + // rawContent is what writeSession emits. + const textOf = (parts: unknown, types: string[]): string[] => + Array.isArray(parts) + ? parts + .filter((p) => p && typeof p === 'object' && types.includes(String((p as Record).type))) + .map((p) => String((p as Record).text ?? '')) + : []; + let text = textOf(payload.rawContent, ['reasoning_text']).join(''); + if (!text) text = textOf(payload.content, ['reasoning_text', 'text']).join(''); + if (!text) text = textOf(payload.summary, ['summary_text']).join('\n\n'); const block: ThinkingBlock = { type: 'thinking', text }; if (messages.length > 0 && messages[messages.length - 1].role === 'assistant') {