import assert from 'node:assert/strict' import { readFileSync, existsSync } from 'node:fs' import { createHash } from 'node:crypto' import { convertWorkBuddyJsonl, convertWorkBuddyTranscript, migrateImportedWorkBuddyMessages, parseWorkBuddyJsonl, stripWorkBuddyPromptContext, type WorkBuddyImportOptions } from '../src/shared/workbuddy-import' import { projectWorkBuddyHistoryTool } from '../src/shared/workbuddy-history-projection' const options: WorkBuddyImportOptions = { sessionId: 'independent-copy', title: '开始新会话', sourceSessionId: 'original-session', evidenceId: `ev_${'a'.repeat(64)}`, sourceSha256: 'b'.repeat(64), importedAt: 5000, branchMode: 'visible' } const text = (text: string) => [{ type: 'input_text', text }] const fixture = [ { type: 'session-meta', timestamp: 1, meta: { credential: 'never-copy-this' } }, { type: 'message', id: 'u1', role: 'user', timestamp: 10, content: text('First prompt') }, { type: 'reasoning', id: 'r1', parentId: 'u1', timestamp: 20, content: text('Recorded reasoning'), rawContent: ['never-copy-raw'] }, { type: 'function_call', id: 'c1', callId: 'call-1', timestamp: 30, name: 'Bash', arguments: '{"command":"echo hello"}', providerData: { token: 'never-copy-token' } }, { type: 'function_call_result', id: 'result-1', callId: 'call-1', timestamp: 50, status: 'completed', output: { type: 'input_text', text: 'hello\n' }, providerData: { secret: 'never-copy-secret', toolResult: { content: 'hello\n', renderer: { type: 'text', value: 'hello\n' }, rawResponse: { exitCode: 0 }, terminalId: 'never-resume-terminal', mcpMeta: { secret: true } } } }, { type: 'message', id: 'a1', role: 'assistant', timestamp: 60, status: 'completed', content: [{ type: 'output_text', text: 'Done' }] }, { type: 'message', id: 'u2', role: 'user', timestamp: 70, content: [ ...text('Image prompt'), { type: 'input_image', image: { blob_id: 'blob-one', blob_path: 'blobs/one.png', mime: 'image/png', size: 12, original_filename: 'test.png' } } ] }, { type: 'function_call', id: 'c2', callId: 'call-2', timestamp: 80, name: 'Write', arguments: '{bad-json' }, { type: 'reasoning', id: 'r2', timestamp: 81, content: [], rawContent: ['never-copy-encrypted'] }, { type: 'message', id: 'a2', role: 'assistant', timestamp: 90, status: 'incomplete', content: text('Stopped') } ] const result = convertWorkBuddyTranscript(fixture, options) assert.equal(result.messages.length, 4) assert.deepEqual(result.messages[1].parts?.map((part) => part.type), ['reasoning', 'tool', 'text']) assert.equal(result.messages[1].tools?.[0].output, 'hello\n') assert.equal(result.messages[1].tools?.[0].durationMs, 20) assert.equal(result.messages[1].turnDurationMs, 50) assert.equal(result.messages[1].tools?.[0].status, 'ok') assert.equal(result.messages[3].tools?.[0].status, 'cancelled') assert.equal(result.messages[3].tools?.[0].argsRaw, '{bad-json') assert.equal(result.messages[3].status, 'cancelled') assert.equal(result.stats.unresolvedToolCalls, 1) assert.equal(result.messages[2].attachments?.[0].name, 'test.png') assert.equal(result.messages[2].content, 'Image prompt') assert.equal(result.messages[2].attachments?.[0].previewUrl, undefined) assert.equal(result.provenance.referencedAttachments[0].attachmentId, result.messages[2].attachments?.[0].id) assert.equal(result.provenance.referencedAttachments[0].messageId, result.messages[2].id) const serialized = JSON.stringify(result) assert.ok(!serialized.includes('never-copy')) assert.ok(!serialized.includes('never-resume')) assert.ok(!serialized.includes('mcpMeta')) assert.ok(result.messages.every((message) => !message.interactions && !message.requestId)) assert.ok(result.messages.every((message) => !['u1', 'a1', 'u2', 'a2'].includes(message.id))) assert.ok(result.messages.flatMap((message) => message.tools ?? []).every((tool) => !['call-1', 'call-2'].includes(tool.id))) const forks = [ ...fixture.slice(0, 6), { type: 'message', id: 'old-user', role: 'user', timestamp: 70, content: text('Old prompt') }, { type: 'message', id: 'old-answer', role: 'assistant', timestamp: 80, content: text('Old answer') }, { type: 'resend-fork-notice', id: 'fork-1', editedUserItemId: 'old-user' }, { type: 'message', id: 'edited-user', role: 'user', timestamp: 90, content: text('Edited prompt') }, { type: 'message', id: 'new-answer', role: 'assistant', timestamp: 100, content: text('New answer') } ] const visible = convertWorkBuddyTranscript(forks, options) assert.deepEqual(visible.messages.map((message) => message.content), ['First prompt', 'Done', 'Edited prompt', 'New answer']) assert.equal(visible.stats.forksApplied, 1) assert.equal(visible.stats.excludedBranchRecords, 2) const archive = convertWorkBuddyTranscript(forks, { ...options, branchMode: 'archive' }) assert.equal(archive.messages.length, 6) assert.ok(archive.warnings.some((warning) => warning.includes('append-only'))) assert.throws(() => convertWorkBuddyTranscript([{ type: 'resend-fork-notice', editedUserItemId: 'missing' }], options), /visible branch import was stopped/) const longOutput = 'full-output\n'.repeat(1000) const reversed = convertWorkBuddyTranscript([ { type: 'message', role: 'user', timestamp: 1, content: 'Question' }, { type: 'function_call_result', callId: 'out-of-order', status: 'completed', timestamp: 3, output: text(longOutput) }, { type: 'function_call', callId: 'out-of-order', timestamp: 2, name: 'Read', arguments: '{}' } ], options) assert.equal(reversed.messages[1].tools?.[0].output, longOutput) assert.equal(reversed.messages[1].tools?.[0].status, 'ok') assert.equal(reversed.stats.unmatchedToolResults, 0) assert.ok(reversed.messages[1].tools?.[0].summary!.length < longOutput.length) const error = convertWorkBuddyTranscript([ { type: 'function_call', callId: 'failure', name: 'Bash', arguments: '{}' }, { type: 'function_call_result', callId: 'failure', status: 'completed', output: text('Failed'), providerData: { toolResult: { error: { message: 'Recorded tool error' } } } }, { type: 'message', role: 'assistant', status: 'incomplete', content: text('Interrupted'), providerData: { error: { message: 'Recorded network error', credential: 'never-copy' } } } ], options) assert.equal(error.messages[0].tools?.[0].status, 'error') assert.equal(error.messages[0].status, 'error') assert.equal(error.messages[0].error, 'Recorded network error') for (const marker of ['Interrupted by user', '[User Cancelled]']) { const cancelled = convertWorkBuddyTranscript([ { type: 'message', id: 'cancel-user', role: 'user', timestamp: 1, content: 'Prompt' }, { type: 'message', id: 'before-cancel', role: 'assistant', timestamp: 2, content: text('Retained answer') }, { type: 'message', id: 'cancel-record', role: 'assistant', timestamp: 3, status: 'incomplete', content: text(marker), providerData: { error: { message: marker } } } ], options) assert.equal(cancelled.messages[1].status, 'cancelled') assert.equal(cancelled.messages[1].content, 'Retained answer') assert.equal(cancelled.messages[1].error, undefined) const oldMessage = { ...cancelled.messages[1], status: 'error' as const, error: marker, content: `Retained answer\n\n${marker}`, parts: [...cancelled.messages[1].parts!, { type: 'text' as const, text: marker }] } const migrated = migrateImportedWorkBuddyMessages([oldMessage], cancelled.provenance) assert.equal(migrated.repairedCancellations, 1) assert.deepEqual(migrated.messages[0], cancelled.messages[1]) assert.equal(migrateImportedWorkBuddyMessages(migrated.messages, cancelled.provenance).changed, false) assert.equal(migrateImportedWorkBuddyMessages([oldMessage], undefined).changed, false) assert.equal(migrateImportedWorkBuddyMessages([{ ...oldMessage, content: 'User modified body' }], cancelled.provenance).changed, false) assert.equal(oldMessage.error, marker, 'migration must not mutate input') } const rawTool = { id: 'projection', name: 'DeferExecuteTool', args: { toolName: 'Read', params: { file_path: 'sample.txt' } }, argsRaw: '{"toolName":"Read","params":{"file_path":"sample.txt"}}', status: 'ok' as const } assert.deepEqual(projectWorkBuddyHistoryTool(rawTool).args, { file_path: 'sample.txt' }) assert.equal(projectWorkBuddyHistoryTool(rawTool).name, 'read_file') assert.equal(rawTool.name, 'DeferExecuteTool') assert.equal(projectWorkBuddyHistoryTool(rawTool).argsRaw, rawTool.argsRaw) assert.equal(projectWorkBuddyHistoryTool({ ...rawTool, args: { toolName: 'bad target', params: {} } }).name, 'defer_execute_tool') const todo = projectWorkBuddyHistoryTool({ ...rawTool, name: 'TaskUpdate', args: { taskId: '1', status: 'completed' }, result: { acpV2PlanSnapshot: { entries: [{ content: 'Recorded task', status: 'completed' }] } } }) assert.deepEqual((todo.args as { todos: unknown }).todos, [{ content: 'Recorded task', status: 'completed' }]) const question = projectWorkBuddyHistoryTool({ ...rawTool, name: 'AskUserQuestion', args: { questions: [{ id: 'q_4', question: 'Shape?' }, { question: 'Gateway?' }] }, result: { content: ' · q-4 → Desktop\n · Gateway? -> Own gateway' } }) assert.deepEqual((question.metadata?.questionAnswer as { questions: unknown }).questions, [{ id: 'q_4', question: 'Shape?', answers: ['Desktop'] }, { id: 'q-1', question: 'Gateway?', answers: ['Own gateway'] }]) assert.throws(() => convertWorkBuddyTranscript([], { ...options, sessionId: options.sourceSessionId }), /fresh/) assert.throws(() => parseWorkBuddyJsonl('{}\n{bad}\n'), /line 2/) assert.throws(() => parseWorkBuddyJsonl('[]'), /line 1/) assert.deepEqual(parseWorkBuddyJsonl('\uFEFF{}\r\n\r\n'), [{}]) assert.equal(convertWorkBuddyTranscript([{ type: 'message', role: 'user', timestamp: '1791989670167', content: 'Numeric timestamp' }], options).messages[0].createdAt, 1791989670167) assert.equal(stripWorkBuddyPromptContext(' Injected\ncontext \nVisible\nrequest '), 'Visible\nrequest') assert.equal(stripWorkBuddyPromptContext('One\nHidden\nTwo'), 'One\nTwo') assert.equal(stripWorkBuddyPromptContext('A literal InjectedActual request') }, { type: 'function_call', callId: 'background', name: 'Bash', timestamp: 2, arguments: '{}' }, { type: 'message', id: 'notification', role: 'user', timestamp: 3, content: text('one\n\nInjected execution instructions'), providerData: { isMeta: true } }, { type: 'function_call_result', callId: 'background', timestamp: 4, status: 'completed', output: text('Background output') }, { type: 'message', role: 'assistant', timestamp: 5, status: 'completed', content: text('Final answer') } ], options) assert.equal(injected.messages.length, 2) assert.equal(injected.messages[0].content, 'Actual request') assert.equal(injected.messages[1].tools?.[0].output, 'Background output') assert.equal(injected.messages[1].turnDurationMs, 4) assert.equal(injected.stats.hiddenInjectedUserRecords, 1) assert.equal(injected.stats.normalizedUserTexts, 1) assert.equal(injected.provenance.sourceEntries.find((entry) => entry.sourceId === 'notification')?.messageId, undefined) for (const candidate of [ { type: 'message', role: 'user', content: text('Quoted by human') }, { type: 'message', role: 'user', content: text('Visible meta message'), providerData: { isMeta: true } } ]) assert.equal(convertWorkBuddyTranscript([candidate], options).messages.length, 1) const evidencePath = '.tmp/rea-workbuddy-session-export.json' if (existsSync(evidencePath)) { const evidence = JSON.parse(readFileSync(evidencePath, 'utf8')) const file = evidence.normalized_result.file assert.equal(createHash('sha256').update(file.text).digest('hex'), file.sha256) const actual = convertWorkBuddyJsonl(file.text, { ...options, sourceSessionId: evidence.normalized_result.session_id, evidenceId: evidence.evidence_id, sourceSha256: file.sha256 }) assert.equal(actual.stats.sourceRecords, 2810) assert.equal(actual.stats.forksApplied, 2) assert.equal(actual.stats.unmatchedToolResults, 0) assert.equal(actual.stats.hiddenInjectedUserRecords, 10) assert.equal(actual.stats.normalizedUserTexts, 31) assert.equal(actual.messages.filter((message) => message.role === 'user').length, 31) assert.ok(actual.messages.length > 0) assert.equal(actual.provenance.referencedAttachments.length, 4) assert.equal(actual.messages.filter(message => message.status === 'cancelled').length, 6) assert.equal(actual.messages.filter(message => message.status === 'error').length, 1) assert.match(actual.messages.find(message => message.status === 'error')?.error ?? '', /429/) assert.equal(actual.messages.flatMap(message => message.parts ?? []).filter(part => part.type === 'text' && ['Interrupted by user', '[User Cancelled]'].includes(part.text.trim())).length, 0) const originalTools = actual.messages.flatMap(message => message.tools ?? []) const rawBefore = JSON.stringify(originalTools) const projectedTools = originalTools.map(projectWorkBuddyHistoryTool) assert.equal(projectedTools.length, 691) assert.equal(projectedTools.filter(tool => tool.name === 'todo_write').length, 24) assert.equal(projectedTools.filter(tool => tool.name === 'defer_execute_tool').length, 0) assert.equal(projectedTools.filter(tool => tool.name === 'present_files').length, 10) assert.equal(projectedTools.filter(tool => tool.name === 'askuserquestion').length, 1) assert.equal(JSON.stringify(originalTools), rawBefore) assert.deepEqual(projectedTools.map(tool => tool.argsRaw), originalTools.map(tool => tool.argsRaw)) console.log(JSON.stringify({ actualReaEvidence: evidence.evidence_id, stats: actual.stats, roles: { user: actual.messages.filter((message) => message.role === 'user').length, assistant: actual.messages.filter((message) => message.role === 'assistant').length }, reasoningParts: actual.messages.flatMap((message) => message.parts ?? []).filter((part) => part.type === 'reasoning').length, toolStatuses: actual.messages.flatMap((message) => message.tools ?? []).reduce>((counts, tool) => ({ ...counts, [tool.status]: (counts[tool.status] ?? 0) + 1 }), {}), warnings: actual.warnings, attachments: actual.provenance.referencedAttachments.length })) } console.log('WorkBuddy import regression passed')