import { mkdir } from 'node:fs/promises'; import { join } from 'node:path'; import { getTaskLogsDir } from '@@/data-path'; import type { TaskInfo } from '@@/api/chat-types'; type ChatMessage = | { role: 'user'; text: string } | { role: 'assistant'; text: string } | { role: 'tool'; toolName: string; toolInput: Record; toolUseId: string; output?: string; isError?: boolean; } | { role: 'result'; costUsd: number; durationMs: number; numTurns: number; isError: boolean } | { role: 'error'; text: string }; type TaskLog = { taskName: string; taskDirName: string; entryName: string; entryType: 'file' | 'directory'; provider: string; model: string; startedAt: string; completedAt: string | null; messages: ChatMessage[]; }; type LogEntry = { email: string; filePath: string; log: TaskLog; }; const activeLogs = new Map(); let logCounter = 0; export function createTaskLog(email: string, taskInfo: TaskInfo, provider: string, model: string): string { const logId = `log_${Date.now()}_${++logCounter}`; const dir = getTaskLogsDir(email); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const filename = `${timestamp}-${taskInfo.taskDirName}.json`; const filePath = join(dir, filename); const log: TaskLog = { taskName: taskInfo.taskName, taskDirName: taskInfo.taskDirName, entryName: taskInfo.entryName, entryType: taskInfo.entryType, provider, model, startedAt: new Date().toISOString(), completedAt: null, messages: [], }; activeLogs.set(logId, { email, filePath, log }); return logId; } export function appendToLog(logId: string, message: ChatMessage) { const entry = activeLogs.get(logId); if (!entry) return; // For tool results, update the existing tool message instead of appending if (message.role === 'tool' && message.output !== undefined) { const existing = entry.log.messages.find((m) => m.role === 'tool' && m.toolUseId === message.toolUseId); if (existing && existing.role === 'tool') { existing.output = message.output; existing.isError = message.isError; return; } } entry.log.messages.push(message); } export async function finalizeLog(logId: string) { const entry = activeLogs.get(logId); if (!entry) return; entry.log.completedAt = new Date().toISOString(); try { await mkdir(join(entry.filePath, '..'), { recursive: true }); await Bun.write(entry.filePath, JSON.stringify(entry.log, null, 2)); } catch (err) { console.error('[task-logger] Failed to write log:', err); } activeLogs.delete(logId); }