import { mkdir, mkdtemp, readFile } from 'fs/promises' import { join } from 'path' import { tmpdir } from 'os' import { describe, expect, test } from 'bun:test' import { cleanAssistantTextForTrajectory, SourceTrajectoryWriter, } from './sourceTrajectoryWriter.js' import type { TaskRun } from './types.js' async function fakeTaskRun(): Promise { const runDir = await mkdtemp(join(tmpdir(), 'trajectory-writer-')) return { taskId: 'demo', runId: 'demo_run', runDir, publicDir: join(runDir, 'public'), workspaceDir: join(runDir, 'workspace'), outputsDir: join(runDir, 'outputs'), logsDir: join(runDir, 'logs'), judgeDir: join(runDir, '..', '.judge_private', 'demo_run'), taskDir: join(runDir, '..', '..', 'tasks', 'demo'), manifest: { version: 1, task_id: 'demo' }, } } describe('SourceTrajectoryWriter', () => { test('filters standalone punctuation noise from clean assistant text only', async () => { expect(cleanAssistantTextForTrajectory('.')).toBeUndefined() expect(cleanAssistantTextForTrajectory('...')).toBeUndefined() expect(cleanAssistantTextForTrajectory('.\n\nNow I understand')).toBe( 'Now I understand', ) expect(cleanAssistantTextForTrajectory('This is fine.')).toBe('This is fine.') const taskRun = await fakeTaskRun() await mkdir(taskRun.logsDir, { recursive: true }) const writer = new SourceTrajectoryWriter(taskRun) await writer.start({ startedAt: '2026-05-14T00:00:00.000Z' }) await writer.agentEvent(1, { type: 'assistant_text', text: '.' }) await writer.agentEvent(1, { type: 'assistant_text', text: '.\n\nNow I understand', }) const raw = await readFile(writer.rawPath, 'utf8') expect(raw).toContain('"text":"."') const clean = await readFile(writer.cleanPath, 'utf8') expect(clean).not.toContain('"text":"."') expect(clean).toContain('"text":"Now I understand"') }) test('writes trajectory warnings to clean trajectory', async () => { const taskRun = await fakeTaskRun() await mkdir(taskRun.logsDir, { recursive: true }) const writer = new SourceTrajectoryWriter(taskRun) await writer.start({ startedAt: '2026-05-14T00:00:00.000Z' }) await writer.agentEvent(1, { type: 'trajectory_warning', code: 'public_dir_mutation', message: 'Bash modified public/.', details: { paths: ['public/visible_data/cases/case_000/input_data/workspace'] }, }) const clean = await readFile(writer.cleanPath, 'utf8') expect(clean).toContain('"kind":"trajectory_warning"') expect(clean).toContain('"code":"public_dir_mutation"') }) test('writes structured validation and run warning events to clean trajectory', async () => { const taskRun = await fakeTaskRun() await mkdir(taskRun.logsDir, { recursive: true }) const writer = new SourceTrajectoryWriter(taskRun) await writer.start({ startedAt: '2026-05-14T00:00:00.000Z' }) await writer.agentEvent(1, { type: 'run_warning', code: 'missing_round_plan', message: 'workspace/plans/round_01.md is missing.', }) await writer.agentEvent(1, { type: 'submission_validation_failed', result: { ok: false, normalizedFiles: [], issues: [ { code: 'missing_output_file', path: 'outputs/case_000.npz', message: 'outputs/case_000.npz is missing', }, ], }, }) await writer.agentEvent(1, { type: 'submission_validation_passed', result: { ok: true, normalizedFiles: ['outputs/case_000.npz'], issues: [], }, }) const clean = await readFile(writer.cleanPath, 'utf8') expect(clean).toContain('"kind":"trajectory_warning"') expect(clean).toContain('"kind":"submission_validation_failed"') expect(clean).toContain('"kind":"submission_validation_passed"') expect(clean).toContain('"normalized_files":["outputs/case_000.npz"]') }) })