import test from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { DatabaseSync } from 'node:sqlite'; import { assembleSessionDetail } from '../packages/core/src/session-detail.ts'; import { persist } from '../packages/core/src/persist.ts'; import { parse as parseCodex } from '../packages/core/src/providers/codex.ts'; const SCHEMA = readFileSync(new URL('../packages/core/src/schema.sql', import.meta.url), 'utf8'); function writeCodexFixture(lines) { const dir = mkdtempSync(join(tmpdir(), 'obelisk-provider-detail-')); const path = join(dir, 'rollout.jsonl'); writeFileSync(path, `${lines.map(line => JSON.stringify(line)).join('\n')}\n`); return path; } test('a provider record stream assembles directly into session detail', () => { const threadId = '019e8951-3e7d-7343-a3e3-05bff48a317d'; const path = writeCodexFixture([ { type: 'session_meta', timestamp: '2026-06-10T10:00:00Z', payload: { id: threadId, cwd: '/proj', timestamp: '2026-06-10T10:00:00Z' }, }, { type: 'event_msg', timestamp: '2026-06-10T10:00:01Z', payload: { type: 'user_message', message: 'inspect the repository' }, }, { type: 'event_msg', timestamp: '2026-06-10T10:00:02Z', payload: { type: 'agent_message', message: 'I will inspect it.' }, }, { type: 'response_item', timestamp: '2026-06-10T10:00:03Z', payload: { type: 'function_call', call_id: 'call_1', name: 'shell', arguments: '{"cmd":"ls"}' }, }, { type: 'response_item', timestamp: '2026-06-10T10:00:04Z', payload: { type: 'function_call_output', call_id: 'call_1', output: 'package.json' }, }, ]); const records = [...parseCodex({ key: path, sessionId: '' }, null)]; const detail = assembleSessionDetail(records); assert.deepEqual(detail.messages.map(message => message.text), [ 'inspect the repository', 'I will inspect it.', ]); assert.equal(detail.messages[1].tool_calls?.[0].name, 'shell'); assert.equal(detail.messages[1].tool_calls?.[0].result?.content, 'package.json'); const db = new DatabaseSync(':memory:'); db.exec(SCHEMA); persist(db, { key: path, sessionId: '' }, parseCodex({ key: path, sessionId: '' }, null)); const persistedDetail = assembleSessionDetail({ session: db.prepare('SELECT * FROM sessions').get(), messages: db.prepare('SELECT * FROM messages ORDER BY timestamp, uuid').all(), toolCalls: db.prepare('SELECT * FROM tool_calls').all(), toolResults: db.prepare('SELECT * FROM tool_results').all(), }); assert.deepEqual(persistedDetail, detail); db.close(); }); test('Codex token_count without usage remains persistable', () => { const threadId = '019e8951-3e7d-7343-a3e3-05bff48a3180'; const path = writeCodexFixture([ { type: 'session_meta', timestamp: '2026-07-14T12:21:21.000Z', payload: { id: threadId, cwd: '/tmp/demo', timestamp: '2026-07-14T12:21:21.000Z' }, }, { type: 'event_msg', timestamp: '2026-07-14T12:21:30.000Z', payload: { type: 'agent_message', message: 'hello' }, }, { type: 'event_msg', timestamp: '2026-07-14T12:21:31.000Z', payload: { type: 'token_count', info: null }, }, ]); const db = new DatabaseSync(':memory:'); db.exec(SCHEMA); assert.doesNotThrow(() => { persist(db, { key: path, sessionId: `codex:${threadId}` }, parseCodex({ key: path, sessionId: `codex:${threadId}`, }, null)); }); const usage = db.prepare( 'SELECT input_tokens, output_tokens FROM messages WHERE text = ?', ).get('hello'); assert.equal(usage.input_tokens, null); assert.equal(usage.output_tokens, null); db.close(); }); test('provider-classified hidden context never reaches session detail', () => { const threadId = '019e8951-3e7d-7343-a3e3-05bff48a317e'; const path = writeCodexFixture([ { type: 'session_meta', timestamp: '2026-06-10T10:00:00Z', payload: { id: threadId, cwd: '/proj', timestamp: '2026-06-10T10:00:00Z' }, }, { type: 'response_item', timestamp: '2026-06-10T10:00:01Z', payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: '\n /proj\n' }], }, }, { type: 'response_item', timestamp: '2026-06-10T10:00:02Z', payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: '\nsecret state\n' }], }, }, { type: 'event_msg', timestamp: '2026-06-10T10:00:03Z', payload: { type: 'user_message', message: 'show the actual request' }, }, ]); const records = [...parseCodex({ key: path, sessionId: '' }, null)]; const detail = assembleSessionDetail(records); assert.deepEqual(detail.messages.map(message => message.text), ['show the actual request']); assert.equal( records.filter(record => record.kind === 'message' && record.visibility === 'hidden').length, 2, ); }); test('provider normalization removes only structural image wrappers before deduplication', () => { const threadId = '019e8951-3e7d-7343-a3e3-05bff48a317f'; const path = writeCodexFixture([ { type: 'session_meta', timestamp: '2026-06-10T10:00:00Z', payload: { id: threadId, cwd: '/proj', timestamp: '2026-06-10T10:00:00Z' }, }, { type: 'event_msg', timestamp: '2026-06-10T10:00:01Z', payload: { type: 'user_message', message: 'look at this screenshot' }, }, { type: 'response_item', timestamp: '2026-06-10T10:00:01Z', payload: { type: 'message', role: 'user', content: [ { type: 'input_text', text: 'look at this screenshot' }, { type: 'input_text', text: '' }, { type: 'input_image', image_url: 'data:image/png;base64,AAAA' }, { type: 'input_text', text: '' }, ], }, }, ]); const records = [...parseCodex({ key: path, sessionId: '' }, null)]; const detail = assembleSessionDetail(records); assert.deepEqual(detail.messages.map(message => message.text), ['look at this screenshot']); }); test('canonical visibility survives persistence before row-based assembly', () => { const threadId = '019e8951-3e7d-7343-a3e3-05bff48a3180'; const path = writeCodexFixture([ { type: 'session_meta', timestamp: '2026-06-10T10:00:00Z', payload: { id: threadId, cwd: '/proj', timestamp: '2026-06-10T10:00:00Z' }, }, { type: 'response_item', timestamp: '2026-06-10T10:00:01Z', payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'hidden' }], }, }, { type: 'event_msg', timestamp: '2026-06-10T10:00:02Z', payload: { type: 'user_message', message: 'visible request' }, }, ]); const db = new DatabaseSync(':memory:'); db.exec(SCHEMA); persist(db, { key: path, sessionId: '' }, parseCodex({ key: path, sessionId: '' }, null)); const messages = db.prepare('SELECT * FROM messages ORDER BY timestamp, uuid').all(); const assembled = assembleSessionDetail({ messages }).messages; assert.equal(messages[0].visibility, 'hidden'); assert.deepEqual(assembled.map(message => message.text), ['visible request']); db.close(); }); test('provider normalization classifies Skill instructions before assembly', () => { const threadId = '019e8951-3e7d-7343-a3e3-05bff48a3181'; const path = writeCodexFixture([ { type: 'session_meta', timestamp: '2026-06-10T10:00:00Z', payload: { id: threadId, cwd: '/proj', timestamp: '2026-06-10T10:00:00Z' }, }, { type: 'response_item', timestamp: '2026-06-10T10:00:01Z', payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'Base directory for this skill: /tmp/skill\n# Instructions' }], }, }, ]); const records = [...parseCodex({ key: path, sessionId: '' }, null)]; const message = records.find(record => record.kind === 'message'); assert.equal(message.content_type, 'skill_instructions'); assert.equal(message.is_meta, 1); assert.equal(message.visibility, 'visible'); });