UNPKG

rigjs

Version:

A multi-repos dev tool based on yarn and git.Rigjs is intended to be the simplest way to develop,share and deliver codes between different developers or different projects.

921 lines (870 loc) 37.9 kB
import fs from 'fs'; import path from 'path'; import { StringDecoder } from 'string_decoder'; import { readCodexLatestPointer } from './codex-pointer'; type JsonObject = Record<string, any>; export interface CodexIntakeOptions { before?: number; limit?: number; maxChars?: number; full?: boolean; } export interface CodexReadOptions { from?: number; limit?: number; maxChars?: number; full?: boolean; } const CODEX_PREFLUSH_POINTER_MAX_AGE_MS = 2 * 60 * 1000; interface CodexTranscriptLine { line: number; value?: JsonObject; parseError?: string; } interface ToolCall { line: number; turnId?: string; callId: string; tool: string; arguments: unknown; source: string; } interface WebSearchEvidence { line: number; turnId?: string; callId: string; status?: string; action?: JsonObject; } interface TurnAbort { line: number; reason?: string; startedAt?: string; completedAt?: string; durationMs?: number; } interface SessionMeta { threadId?: string; sessionId?: string; cwd?: string; cliVersion?: string; parentThreadId?: string; forkedFromId?: string; agentPath?: string; threadSource?: string; } export function assertCodexTranscriptPath(transcriptPath: string): string { const absolute = path.resolve(transcriptPath); if (!absolute.endsWith('.jsonl')) throw new Error(`Codex transcript must be a .jsonl file: ${absolute}`); let stat: fs.Stats; try { stat = fs.statSync(absolute); } catch { throw new Error(`Codex transcript not found: ${absolute}`); } if (!stat.isFile()) throw new Error(`Codex transcript is not a file: ${absolute}`); return absolute; } export function* iterateCodexTranscript(transcriptPath: string): Generator<CodexTranscriptLine> { const absolute = assertCodexTranscriptPath(transcriptPath); const fd = fs.openSync(absolute, 'r'); const decoder = new StringDecoder('utf8'); const buffer = Buffer.alloc(64 * 1024); let pending = ''; let line = 0; const parse = (raw: string): CodexTranscriptLine => { line++; try { const value = JSON.parse(raw); if (!isObject(value)) return { line, parseError: 'line is not a JSON object' }; return { line, value }; } catch (error) { return { line, parseError: error instanceof Error ? error.message : String(error) }; } }; try { let bytes = 0; while ((bytes = fs.readSync(fd, buffer, 0, buffer.length, null)) > 0) { pending += decoder.write(buffer.subarray(0, bytes)); let newline = pending.indexOf('\n'); while (newline >= 0) { const raw = pending.slice(0, newline).replace(/\r$/, ''); pending = pending.slice(newline + 1); if (raw.trim()) yield parse(raw); newline = pending.indexOf('\n'); } } pending += decoder.end(); if (pending.trim()) yield parse(pending.replace(/\r$/, '')); } finally { fs.closeSync(fd); } } export function intakeCodexTranscript(transcriptPath: string, options: CodexIntakeOptions = {}): JsonObject { const absolute = assertCodexTranscriptPath(transcriptPath); const stat = fs.statSync(absolute); const before = clampInteger(options.before, Number.MAX_SAFE_INTEGER, 1, Number.MAX_SAFE_INTEGER); const limit = clampInteger(options.limit, 24, 1, 200); const maxChars = options.full ? Number.MAX_SAFE_INTEGER : clampInteger(options.maxChars, 4000, 256, 1000000); const topTypes: Record<string, number> = {}; const payloadTypes: Record<string, number> = {}; const unknownPayloadTypes: Record<string, number> = {}; const omitted = { reasoning: 0, telemetry: 0, developerMessages: 0, worldState: 0, encrypted: 0 }; const entries: JsonObject[] = []; const fallbackMessages: JsonObject[] = []; const messageEntries = new Map<string, JsonObject>(); const calls = new Map<string, ToolCall>(); const completedCalls = new Set<string>(); const webSearches = new Map<string, WebSearchEvidence>(); const editedFiles = new Map<string, JsonObject>(); const failedPatches: JsonObject[] = []; const turnStarts = new Map<string, number>(); const turnCompletions = new Map<string, number>(); const turnFinals = new Set<string>(); const turnAbortions = new Map<string, TurnAbort>(); const turnOrder: string[] = []; const seenTurns = new Set<string>(); const rolledBackTurns = new Map<string, number>(); const rollbackEvents: JsonObject[] = []; const subagentIds = new Map<string, { agentPath?: string; lastActivityKind?: string }>(); let session: SessionMeta = {}; let sessionCaptured = false; let firstTimestamp: string | undefined; let lastTimestamp: string | undefined; let totalLines = 0; let malformedLines = 0; let compacted = 0; let lastCompactionLine: number | null = null; let latestTurnId: string | undefined; for (const item of iterateCodexTranscript(absolute)) { totalLines = item.line; if (!item.value) { malformedLines++; continue; } const row = item.value; const topType = stringValue(row.type) || 'unknown'; const payload = isObject(row.payload) ? row.payload : {}; const payloadType = stringValue(payload.type) || ''; topTypes[topType] = (topTypes[topType] || 0) + 1; if (payloadType) payloadTypes[payloadType] = (payloadTypes[payloadType] || 0) + 1; if (typeof row.timestamp === 'string') { if (!firstTimestamp) firstTimestamp = row.timestamp; lastTimestamp = row.timestamp; } if (topType === 'session_meta') { if (!sessionCaptured) { session = captureSessionMeta(payload); sessionCaptured = true; } continue; } if (topType === 'world_state') { omitted.worldState++; continue; } if (topType === 'compacted') { compacted++; lastCompactionLine = item.line; omitted.encrypted += countForbiddenKeys(payload, 'encrypted_content'); continue; } if (topType === 'response_item') { if (payloadType === 'reasoning') { omitted.reasoning++; omitted.encrypted += countForbiddenKeys(payload, 'encrypted_content'); continue; } if (payloadType === 'message') { const role = stringValue(payload.role); if (role !== 'user' && role !== 'assistant') { if (role === 'developer' || role === 'system') omitted.developerMessages++; continue; } const content = extractTextContent(payload.content, maxChars); if (!content.length) continue; const turnId = getTurnId(payload); const phase = stringValue(payload.phase); const entry = removeUndefined({ type: 'message', line: item.line, turnId, role, phase, content, source: 'response_item', }); entries.push(entry); messageEntries.set(messageFingerprint(role, phase, content), entry); rememberTurn(turnId, turnOrder, seenTurns); if (role === 'assistant' && phase === 'final_answer' && turnId) turnFinals.add(turnId); continue; } if (payloadType === 'function_call' || payloadType === 'custom_tool_call') { const call = normalizeToolCall(payload, payloadType, item.line, maxChars); if (call) calls.set(call.callId, call); if (call) rememberTurn(call.turnId, turnOrder, seenTurns); continue; } if (payloadType === 'tool_search_call') { const call = normalizeToolSearchCall(payload, item.line, maxChars); if (call) { calls.set(call.callId, call); rememberTurn(call.turnId, turnOrder, seenTurns); } continue; } if (payloadType === 'function_call_output' || payloadType === 'custom_tool_call_output') { const callId = stringValue(payload.call_id); if (!callId) continue; const call = calls.get(callId); completedCalls.add(callId); entries.push(removeUndefined({ type: 'tool-exchange', line: item.line, callLine: call && call.line, turnId: call && call.turnId || getTurnId(payload), callId, tool: call && call.tool || '<unknown>', arguments: call && call.arguments, output: sanitizeRecovery(payload.output, maxChars), source: call && call.source || payloadType, })); continue; } if (payloadType === 'tool_search_output') { const callId = stringValue(payload.call_id) || stringValue(payload.id); if (!callId) continue; const call = calls.get(callId); completedCalls.add(callId); entries.push(removeUndefined({ type: 'tool-exchange', line: item.line, callLine: call && call.line, turnId: call && call.turnId || getTurnId(payload), callId, tool: 'tool_search', arguments: call && call.arguments, output: removeUndefined({ status: stringValue(payload.status), tools: summarizeToolDefinitions(payload.tools), }), source: 'tool_search', })); continue; } if (payloadType === 'web_search_call') { const callId = stringValue(payload.call_id) || stringValue(payload.id) || `web-search-line-${item.line}`; const evidence = removeUndefined({ line: item.line, turnId: getTurnId(payload), callId, status: stringValue(payload.status), action: normalizeWebSearchAction(payload.action, maxChars), }) as WebSearchEvidence; webSearches.set(callId, evidence); rememberTurn(evidence.turnId, turnOrder, seenTurns); continue; } if (payloadType === 'agent_message') { const content = extractTextContent(payload.content, maxChars); if (content.length) entries.push(removeUndefined({ type: 'agent-message', line: item.line, turnId: getTurnId(payload), author: payload.author, recipient: payload.recipient, content, })); omitted.encrypted += countForbiddenKeys(payload, 'encrypted_content'); continue; } unknownPayloadTypes[`response_item:${payloadType || '<missing>'}`] = (unknownPayloadTypes[`response_item:${payloadType || '<missing>'}`] || 0) + 1; continue; } if (topType === 'event_msg') { if (payloadType === 'agent_reasoning') { omitted.reasoning++; continue; } if (payloadType === 'token_count' || payloadType === 'thread_settings_applied') { omitted.telemetry++; continue; } if (payloadType === 'task_started') { const turnId = stringValue(payload.turn_id); if (turnId) { turnStarts.set(turnId, item.line); rememberTurn(turnId, turnOrder, seenTurns); latestTurnId = turnId; } continue; } if (payloadType === 'task_complete') { const turnId = stringValue(payload.turn_id); if (turnId) turnCompletions.set(turnId, item.line); continue; } if (payloadType === 'turn_aborted') { const turnId = stringValue(payload.turn_id); if (turnId) { rememberTurn(turnId, turnOrder, seenTurns); const aborted = removeUndefined({ line: item.line, reason: stringValue(payload.reason), startedAt: normalizeTimestamp(payload.started_at), completedAt: normalizeTimestamp(payload.completed_at), durationMs: typeof payload.duration_ms === 'number' ? payload.duration_ms : undefined, }) as TurnAbort; turnAbortions.set(turnId, aborted); entries.push(removeUndefined({ type: 'turn-aborted', turnId, ...aborted })); } continue; } if (payloadType === 'thread_rolled_back') { const numTurns = clampInteger( typeof payload.num_turns === 'number' ? payload.num_turns : undefined, 1, 1, 1000, ); const rolledBackTurnIds: string[] = []; for (let index = turnOrder.length - 1; index >= 0 && rolledBackTurnIds.length < numTurns; index--) { const turnId = turnOrder[index]; if (rolledBackTurns.has(turnId)) continue; rolledBackTurns.set(turnId, item.line); rolledBackTurnIds.push(turnId); } const rollback = { line: item.line, numTurns, rolledBackTurnIds }; rollbackEvents.push(rollback); entries.push({ type: 'rollback', ...rollback }); continue; } if (payloadType === 'user_message' || payloadType === 'agent_message') { const role = payloadType === 'user_message' ? 'user' : 'assistant'; const turnId = getTurnId(payload); rememberTurn(turnId, turnOrder, seenTurns); const content = extractTextContent(payload.message, maxChars); const localImages = role === 'user' ? extractLocalImagePaths(payload.local_images || payload.images, maxChars) : []; if (content.length || localImages.length) fallbackMessages.push(removeUndefined({ type: 'message', line: item.line, turnId, role, phase: role === 'assistant' ? payload.phase : undefined, content, localImages: localImages.length ? localImages : undefined, source: 'event_msg', })); continue; } if (payloadType === 'patch_apply_end') { const files = capturePatchFiles(payload.changes, item.line, getTurnId(payload)); for (const file of files) editedFiles.set(file.path, file); if (payload.success === false) failedPatches.push(removeUndefined({ line: item.line, turnId: getTurnId(payload), callId: payload.call_id, status: payload.status, })); continue; } if (payloadType === 'image_generation_end') { entries.push(removeUndefined({ type: 'generated-image', line: item.line, turnId: getTurnId(payload), callId: stringValue(payload.call_id), status: stringValue(payload.status), savedPath: truncateMiddle(payload.saved_path, maxChars), })); continue; } if (payloadType === 'thread_goal_updated') { const goal = isObject(payload.goal) ? payload.goal : {}; entries.push(removeUndefined({ type: 'goal', line: item.line, objective: truncateMiddle(goal.objective, maxChars), status: goal.status, })); continue; } if (payloadType === 'sub_agent_activity') { const threadId = stringValue(payload.agent_thread_id); if (threadId) subagentIds.set(threadId, { agentPath: stringValue(payload.agent_path), lastActivityKind: stringValue(payload.kind), }); continue; } if (payloadType.includes('error')) { entries.push(removeUndefined({ type: 'error', line: item.line, turnId: getTurnId(payload), message: sanitizeRecovery(payload.message || payload.error || payload, maxChars), })); continue; } // Patch/MCP completion rows are intentionally omitted here. Their // response_item call/output pair is the stable model-facing evidence. if (!['context_compacted', 'web_search_end', 'mcp_tool_call_end'].includes(payloadType)) { unknownPayloadTypes[`event_msg:${payloadType || '<missing>'}`] = (unknownPayloadTypes[`event_msg:${payloadType || '<missing>'}`] || 0) + 1; } continue; } if (topType !== 'turn_context' && topType !== 'inter_agent_communication_metadata') { unknownPayloadTypes[`${topType}:${payloadType || '<missing>'}`] = (unknownPayloadTypes[`${topType}:${payloadType || '<missing>'}`] || 0) + 1; } } for (const fallback of fallbackMessages) { const fingerprint = messageFingerprint(fallback.role, fallback.phase, fallback.content); const existing = messageEntries.get(fingerprint); if (existing) { if (Array.isArray(fallback.localImages) && fallback.localImages.length) { existing.localImages = fallback.localImages; } } else { entries.push(fallback); messageEntries.set(fingerprint, fallback); } } for (const call of calls.values()) { if (completedCalls.has(call.callId)) continue; entries.push(removeUndefined({ type: 'tool-exchange', line: call.line, callLine: call.line, turnId: call.turnId, callId: call.callId, tool: call.tool, arguments: call.arguments, pending: true, source: call.source, })); } for (const search of webSearches.values()) entries.push(removeUndefined({ type: 'web-search', ...search })); const activeEntries = entries .filter(entry => !entry.turnId || !rolledBackTurns.has(entry.turnId)) .sort((a, b) => a.line - b.line); const eligible = activeEntries.filter(entry => entry.line < before); const pageChronological = eligible.slice(Math.max(0, eligible.length - limit)); const pageEntries = pageChronological.slice().reverse(); const oldestLine = pageEntries.length ? pageEntries[pageEntries.length - 1].line : null; const hasOlder = eligible.length > pageChronological.length; const activeLatestTurnId = findLatestActiveTurn(turnOrder, rolledBackTurns) || ( latestTurnId && !rolledBackTurns.has(latestTurnId) ? latestTurnId : undefined ); const activeLastUserLine = activeEntries.reduce<number | null>((latest, entry) => ( entry.type === 'message' && entry.role === 'user' ? entry.line : latest ), null); const latestStartedLine = activeLatestTurnId ? turnStarts.get(activeLatestTurnId) : undefined; const latestAbort = activeLatestTurnId ? turnAbortions.get(activeLatestTurnId) : undefined; const pendingToolCalls = Array.from(calls.values()) .filter(call => !completedCalls.has(call.callId) && !rolledBackTurns.has(call.turnId || '') && (!activeLatestTurnId || call.turnId === activeLatestTurnId)) .map(call => ({ callId: call.callId, tool: call.tool, line: call.line })); return { schemaVersion: 1, source: 'codex-rollout', transcriptPath: absolute, bytes: stat.size, modifiedAt: stat.mtime.toISOString(), session: removeUndefined({ ...session, firstTimestamp, lastTimestamp }), stats: { lines: totalLines, malformedLines, topTypes, payloadTypes, recoverableEntries: activeEntries.length, omitted, unknownPayloadTypes, }, compaction: { count: compacted, lastLine: lastCompactionLine, readableCheckpoint: false }, rollbacks: { count: rollbackEvents.length, events: rollbackEvents.slice(-20), excludedTurnIds: Array.from(rolledBackTurns.keys()), }, currentTurn: removeUndefined({ turnId: activeLatestTurnId, startedLine: latestStartedLine, completed: activeLatestTurnId ? turnCompletions.has(activeLatestTurnId) : undefined, completionLine: activeLatestTurnId ? turnCompletions.get(activeLatestTurnId) : undefined, hasFinalAnswer: activeLatestTurnId ? turnFinals.has(activeLatestTurnId) : undefined, aborted: !!latestAbort, abortLine: latestAbort && latestAbort.line, abortReason: latestAbort && latestAbort.reason, lastUserLine: activeLastUserLine, pendingToolCalls, }), workspaceEvidence: { editedFiles: Array.from(editedFiles.values()) .map(file => rolledBackTurns.has(file.turnId || '') ? { ...file, rolledBack: true } : file) .sort((a, b) => b.line - a.line) .slice(0, 80), failedPatches: failedPatches.map(patch => rolledBackTurns.has(patch.turnId || '') ? { ...patch, rolledBack: true } : patch), }, subagentTranscripts: resolveSubagentTranscripts(absolute, subagentIds), page: { direction: 'newest-to-oldest', beforeLine: Math.min(before, totalLines + 1), newestLine: pageEntries.length ? pageEntries[0].line : null, oldestLine, nextBeforeLine: hasOlder ? oldestLine : null, hasOlder, entries: pageEntries, }, guidance: { next: hasOlder ? `If material evidence is missing, rerun with --before ${oldestLine}.` : 'No older recovery entries remain.', privacy: 'Private reasoning, encrypted content, runtime developer messages, world state, and token telemetry were omitted.', state: 'Rolled-back dialogue is excluded and aborted turns are labeled. Reconcile transcript evidence against the current workspace and Git state before continuing.', }, }; } export function inspectCodexTranscript(transcriptPath: string, options: { recent?: number; maxChars?: number } = {}): JsonObject { return intakeCodexTranscript(transcriptPath, { limit: clampInteger(options.recent, 20, 1, 200), maxChars: clampInteger(options.maxChars, 4000, 256, 100000), }); } export function readCodexTranscriptPage(transcriptPath: string, options: CodexReadOptions = {}): JsonObject { const absolute = assertCodexTranscriptPath(transcriptPath); const from = clampInteger(options.from, 1, 1, Number.MAX_SAFE_INTEGER); const limit = clampInteger(options.limit, 80, 1, 1000); const maxChars = options.full ? Number.MAX_SAFE_INTEGER : clampInteger(options.maxChars, 12000, 256, 1000000); const entries: JsonObject[] = []; let endLine = from - 1; let hasMore = false; for (const item of iterateCodexTranscript(absolute)) { if (item.line < from) continue; if (entries.length >= limit) { hasMore = true; break; } endLine = item.line; if (!item.value) entries.push({ line: item.line, type: 'parse-error', error: item.parseError }); else entries.push(normalizeDiagnosticRow(item.value, item.line, maxChars)); } return { schemaVersion: 1, source: 'codex-rollout', transcriptPath: absolute, fromLine: from, endLine, nextLine: hasMore ? endLine + 1 : null, hasMore, entries }; } export function findLatestCodexTranscript(sessionsDir: string, cwd?: string, pointerPath?: string): string | null { const wanted = cwd ? path.resolve(cwd) : undefined; let savedPointer: { path: string; updatedAt: number; fresh: boolean } | null = null; if (pointerPath) { const pointer = readCodexLatestPointer(pointerPath); if (pointer && (!wanted || path.resolve(pointer.cwd) === wanted)) { // The hook's exact path and cwd are authoritative for the live turn. // The JSONL may not have been flushed yet, and session_meta.cwd can // remain the startup directory after the client changes workspace. const pointerTranscript = path.resolve(pointer.transcriptPath); try { const stat = fs.statSync(pointerTranscript); if (stat.isFile()) { const meta = readCodexSessionMeta(pointerTranscript); if (!meta.agentPath && meta.threadSource !== 'subagent') { savedPointer = { path: pointerTranscript, updatedAt: Date.parse(pointer.updatedAt) || stat.mtimeMs, fresh: isFreshPointerTimestamp(pointer.updatedAt), }; } } } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT' && isFreshPointerTimestamp(pointer.updatedAt)) return pointerTranscript; } } } const files: { path: string; mtime: number }[] = []; if (savedPointer && savedPointer.fresh) return savedPointer.path; walkJsonl(sessionsDir, files, 0); files.sort((a, b) => b.mtime - a.mtime); let latestRoot: { path: string; mtime: number } | null = null; for (const candidate of files) { const meta = readCodexSessionMeta(candidate.path); // agent_path/thread_source are explicit subagent markers. parent_thread_id // and forked_from_id may also describe a user-created top-level fork. if (meta.agentPath || meta.threadSource === 'subagent') continue; if (!meta.threadId && !meta.sessionId) continue; if (wanted && (!meta.cwd || path.resolve(meta.cwd) !== wanted)) continue; latestRoot = candidate; break; } if (!savedPointer) return latestRoot && latestRoot.path; if (!latestRoot || latestRoot.path === savedPointer.path) return savedPointer.path; return latestRoot.mtime > savedPointer.updatedAt ? latestRoot.path : savedPointer.path; } export function readCodexSessionMeta(transcriptPath: string): SessionMeta { try { for (const item of iterateCodexTranscript(transcriptPath)) { if (!item.value) continue; if (item.value.type === 'session_meta' && isObject(item.value.payload)) return captureSessionMeta(item.value.payload); if (item.line > 20) break; } } catch { /* invalid candidates are ignored by latest selection */ } return {}; } function normalizeToolCall(payload: JsonObject, payloadType: string, line: number, maxChars: number): ToolCall | null { const callId = stringValue(payload.call_id); if (!callId) return null; const namespace = stringValue(payload.namespace); const name = stringValue(payload.name) || '<unknown>'; const tool = namespace ? `${namespace}.${name}` : name; let args = payloadType === 'function_call' ? payload.arguments : payload.input; if (typeof args === 'string') { try { args = JSON.parse(args); } catch { /* retain string */ } } return { line, turnId: getTurnId(payload), callId, tool, arguments: sanitizeRecovery(args, maxChars), source: payloadType }; } function normalizeToolSearchCall(payload: JsonObject, line: number, maxChars: number): ToolCall | null { const callId = stringValue(payload.call_id) || stringValue(payload.id); if (!callId) return null; let args = payload.arguments; if (typeof args === 'string') { try { args = JSON.parse(args); } catch { /* retain string */ } } return { line, turnId: getTurnId(payload), callId, tool: 'tool_search', arguments: sanitizeRecovery(args, maxChars), source: 'tool_search', }; } function normalizeWebSearchAction(value: unknown, maxChars: number): JsonObject | undefined { if (!isObject(value)) return undefined; const result: JsonObject = {}; for (const key of ['type', 'query', 'url', 'pattern']) { const item = stringValue(value[key]); if (item) result[key] = truncateMiddle(item, maxChars); } if (Array.isArray(value.queries)) { const queries = value.queries .filter((item: unknown): item is string => typeof item === 'string') .slice(0, 50) .map((item: string) => truncateMiddle(item, maxChars)); if (queries.length) result.queries = queries; } return Object.keys(result).length ? result : undefined; } function summarizeToolDefinitions(value: unknown): JsonObject { if (!Array.isArray(value)) return { count: 0, names: [] }; const names: string[] = []; for (const item of value) { if (!isObject(item)) continue; const name = stringValue(item.name) || stringValue(item.tool_name); if (name && !names.includes(name)) names.push(name); if (names.length >= 80) break; } return { count: value.length, names }; } function normalizeDiagnosticRow(row: JsonObject, line: number, maxChars: number): JsonObject { const topType = stringValue(row.type) || 'unknown'; const payload = isObject(row.payload) ? row.payload : {}; const payloadType = stringValue(payload.type) || ''; if (topType === 'session_meta') return { line, type: topType, session: captureSessionMeta(payload) }; if (topType === 'response_item' && payloadType === 'message') { const role = stringValue(payload.role); if (role === 'user' || role === 'assistant') return removeUndefined({ line, type: 'message', role, phase: payload.phase, turnId: getTurnId(payload), content: extractTextContent(payload.content, maxChars) }); } if (topType === 'response_item' && (payloadType === 'function_call' || payloadType === 'custom_tool_call')) { const call = normalizeToolCall(payload, payloadType, line, maxChars); return call ? { type: 'tool-call', ...call } : { line, type: 'omitted', sourceType: `${topType}:${payloadType}` }; } if (topType === 'response_item' && (payloadType === 'function_call_output' || payloadType === 'custom_tool_call_output')) { return { line, type: 'tool-output', callId: payload.call_id, output: sanitizeRecovery(payload.output, maxChars) }; } if (topType === 'response_item' && payloadType === 'web_search_call') { return removeUndefined({ line, type: 'web-search', callId: payload.call_id || payload.id, status: payload.status, action: normalizeWebSearchAction(payload.action, maxChars), }); } if (topType === 'response_item' && payloadType === 'tool_search_call') { const call = normalizeToolSearchCall(payload, line, maxChars); return call ? { type: 'tool-call', ...call } : { line, type: 'omitted', sourceType: `${topType}:${payloadType}` }; } if (topType === 'response_item' && payloadType === 'tool_search_output') { return { line, type: 'tool-output', callId: payload.call_id || payload.id, status: payload.status, tools: summarizeToolDefinitions(payload.tools), }; } if (topType === 'event_msg' && payloadType === 'user_message') { return removeUndefined({ line, type: 'message', role: 'user', turnId: getTurnId(payload), content: extractTextContent(payload.message, maxChars), localImages: extractLocalImagePaths(payload.local_images || payload.images, maxChars), }); } if (topType === 'event_msg' && payloadType === 'patch_apply_end') { return { line, type: 'patch', success: payload.success, status: payload.status, changedFiles: capturePatchFiles(payload.changes, line, getTurnId(payload)) }; } if (topType === 'event_msg' && payloadType === 'turn_aborted') { return removeUndefined({ line, type: 'turn-aborted', turnId: payload.turn_id, reason: payload.reason }); } if (topType === 'event_msg' && payloadType === 'thread_rolled_back') { return { line, type: 'rollback', numTurns: payload.num_turns }; } if (topType === 'event_msg' && payloadType === 'image_generation_end') { return removeUndefined({ line, type: 'generated-image', callId: payload.call_id, status: payload.status, savedPath: truncateMiddle(payload.saved_path, maxChars), }); } return { line, type: 'omitted', sourceType: `${topType}:${payloadType || '<missing>'}` }; } function captureSessionMeta(payload: JsonObject): SessionMeta { return removeUndefined({ threadId: stringValue(payload.id), sessionId: stringValue(payload.session_id), cwd: stringValue(payload.cwd), cliVersion: stringValue(payload.cli_version), parentThreadId: stringValue(payload.parent_thread_id), forkedFromId: stringValue(payload.forked_from_id), agentPath: stringValue(payload.agent_path), threadSource: stringValue(payload.thread_source), }); } function capturePatchFiles(changes: unknown, line: number, turnId?: string): JsonObject[] { if (!isObject(changes)) return []; return Object.entries(changes).map(([filePath, change]) => { const detail = isObject(change) ? change : {}; return removeUndefined({ path: filePath, line, turnId, changeType: detail.type || detail.change_type || detail.kind, movePath: detail.move_path || detail.movePath, }); }); } function extractLocalImagePaths(value: unknown, maxChars: number): JsonObject[] { if (!Array.isArray(value)) return []; const result: JsonObject[] = []; for (const item of value) { const candidate = typeof item === 'string' ? item : isObject(item) ? stringValue(item.path) || stringValue(item.local_path) : undefined; if (!candidate || !path.isAbsolute(candidate)) continue; result.push({ path: truncateMiddle(candidate, maxChars) }); if (result.length >= 40) break; } return result; } function extractTextContent(value: unknown, maxChars: number): JsonObject[] { if (typeof value === 'string') return value.trim() ? [{ type: 'text', text: truncateMiddle(value, maxChars) }] : []; if (!Array.isArray(value)) return []; const result: JsonObject[] = []; for (const block of value) { if (!isObject(block)) continue; const type = stringValue(block.type); if (type !== 'input_text' && type !== 'output_text' && type !== 'text') continue; const text = stringValue(block.text); if (text && text.trim()) result.push({ type: 'text', text: truncateMiddle(text, maxChars) }); } return result; } function messageFingerprint(role: unknown, phase: unknown, content: unknown): string { return `${String(role)}\u0000${String(phase || '')}\u0000${JSON.stringify(content)}`; } function rememberTurn(turnId: string | undefined, order: string[], seen: Set<string>): void { if (!turnId || seen.has(turnId)) return; seen.add(turnId); order.push(turnId); } function findLatestActiveTurn(order: string[], rolledBack: Map<string, number>): string | undefined { for (let index = order.length - 1; index >= 0; index--) { if (!rolledBack.has(order[index])) return order[index]; } return undefined; } function getTurnId(payload: JsonObject): string | undefined { const metadata = isObject(payload.internal_chat_message_metadata_passthrough) ? payload.internal_chat_message_metadata_passthrough : {}; return stringValue(metadata.turn_id) || stringValue(payload.turn_id); } function sanitizeRecovery(value: unknown, maxChars: number, depth = 0): unknown { if (depth > 8) return '[depth omitted]'; if (typeof value === 'string') return truncateMiddle(value, maxChars); if (value == null || typeof value === 'number' || typeof value === 'boolean') return value; if (Array.isArray(value)) return value.slice(0, 200).map(item => sanitizeRecovery(item, maxChars, depth + 1)); if (!isObject(value)) return String(value); const result: JsonObject = {}; for (const [key, child] of Object.entries(value)) { if (isForbiddenKey(key)) continue; result[key] = sanitizeRecovery(child, maxChars, depth + 1); } return result; } function isForbiddenKey(key: string): boolean { return [ 'encrypted_content', 'reasoning', 'signature', 'base_instructions', 'dynamic_tools', 'developer_instructions', 'world_state', 'rate_limits', 'token_count', 'token_usage', ].includes(key); } function countForbiddenKeys(value: unknown, key: string, depth = 0): number { if (depth > 8 || !value || typeof value !== 'object') return 0; if (Array.isArray(value)) return value.reduce((sum, child) => sum + countForbiddenKeys(child, key, depth + 1), 0); let count = 0; for (const [name, child] of Object.entries(value as JsonObject)) { if (name === key) count++; count += countForbiddenKeys(child, key, depth + 1); } return count; } function resolveSubagentTranscripts( transcriptPath: string, ids: Map<string, { agentPath?: string; lastActivityKind?: string }>, ): JsonObject[] { if (ids.size === 0) return []; const root = findSessionsRoot(transcriptPath); if (!root) return Array.from(ids.entries()).map(([threadId, detail]) => ({ threadId, ...detail })); const files: { path: string; mtime: number }[] = []; walkJsonl(root, files, 0); return Array.from(ids.entries()).map(([threadId, detail]) => { const match = files.find(file => path.basename(file.path).includes(threadId)); return removeUndefined({ threadId, path: match && match.path, ...detail }); }); } function findSessionsRoot(value: string): string | null { let current = path.dirname(path.resolve(value)); while (path.dirname(current) !== current) { if (path.basename(current) === 'sessions') return current; current = path.dirname(current); } return null; } function walkJsonl(dir: string, files: { path: string; mtime: number }[], depth: number): void { if (depth > 8) return; let entries: fs.Dirent[]; try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; } for (const entry of entries) { const candidate = path.join(dir, entry.name); if (entry.isDirectory()) walkJsonl(candidate, files, depth + 1); else if (entry.isFile() && entry.name.endsWith('.jsonl')) { try { files.push({ path: candidate, mtime: fs.statSync(candidate).mtimeMs }); } catch { /* ignore races */ } } } } function truncateMiddle(value: unknown, maxChars: number): unknown { if (typeof value !== 'string' || value.length <= maxChars) return value; const marker = `\n… ${value.length - maxChars} chars omitted …\n`; const available = Math.max(2, maxChars - marker.length); const head = Math.ceil(available / 2); return value.slice(0, head) + marker + value.slice(value.length - (available - head)); } function clampInteger(value: number | undefined, fallback: number, min: number, max: number): number { if (value == null || !Number.isFinite(value)) return fallback; return Math.max(min, Math.min(max, Math.floor(value))); } function isFreshPointerTimestamp(value: string): boolean { const timestamp = Date.parse(value); if (!Number.isFinite(timestamp)) return false; const age = Date.now() - timestamp; return age >= -30000 && age <= CODEX_PREFLUSH_POINTER_MAX_AGE_MS; } function stringValue(value: unknown): string | undefined { return typeof value === 'string' && value ? value : undefined; } function normalizeTimestamp(value: unknown): string | undefined { if (typeof value === 'string' && value) return value; if (typeof value !== 'number' || !Number.isFinite(value)) return undefined; const milliseconds = Math.abs(value) < 100000000000 ? value * 1000 : value; try { return new Date(milliseconds).toISOString(); } catch { return undefined; } } function isObject(value: unknown): value is JsonObject { return !!value && typeof value === 'object' && !Array.isArray(value); } function removeUndefined<T extends JsonObject>(value: T): T { for (const key of Object.keys(value)) if (value[key] === undefined) delete value[key]; return value; }