// 重构前 index.ts 只读参考:commit 6a04144d3f5103014f75b637d7e6bc2f45bf007f,blob 56e590c1a6b5ca7ad128bf2c992f60e46c355a58;可用 `git show 6a04144d3f5103014f75b637d7e6bc2f45bf007f:src/components/microservices/code-queue/src/index.ts` 查看。 import { existsSync, readdirSync, readFileSync, statSync, type Dirent } from "node:fs"; import { resolve } from "node:path"; import type { AttemptSummary, FeedbackPromptRecord, JudgeDecision, JudgeResult, JsonValue, LiveOutput, PromptHistoryItem, QueueTask, QueuedStatusReason, RuntimeConfig, SessionCommandOutput, SessionFileChange, TranscriptKind, TranscriptLine, } from "./types"; import { codeAgentPortForModel, codeAgentPortInfo, codeExecutionModeInfo, extractRecord } from "./code-agent/common"; import { currentTaskPromptMarker, resolvedReferenceContextTitle, stripCodeQueueEnvironmentHint, userPromptForDisplay } from "./prompts"; import { outputArchiveSignature, taskFullOutput } from "./task-output"; import { retryPrompt } from "./judge"; import { readOaTraceStepsForTask, type OaTraceStepSummary } from "./oa-events"; export interface TaskViewContext { config: Pick; errorToJson: (error: unknown) => JsonValue; jsonResponse: (body: unknown, status?: number) => Response; logger: (level: "debug" | "info" | "warn" | "error", message: string, data?: JsonValue) => void; mergePromptHistory: (items: PromptHistoryItem[]) => PromptHistoryItem[]; nowIso: () => string; outputPromptHistory: (task: QueueTask) => PromptHistoryItem[]; pageBySeq: (items: T[], url: URL, limit: number) => { mode: "tail" | "after" | "before"; afterSeq: number; beforeSeq: number | null; nextAfterSeq: number; previousBeforeSeq: number | null; hasMore: boolean; hasBefore: boolean; chunk: T[]; }; parseLimit: (url: URL) => number; parseSeqParam: (url: URL, name: string, defaultValue: number | null) => number | null; queueIdOf: (task: QueueTask) => string; queuedStatusReason: (task: QueueTask) => QueuedStatusReason | null; queuedTaskPromptEditable: (task: QueueTask) => boolean; taskQueueEnteredAt: (task: QueueTask) => string; } function isCodexToolLifecycleOutput(output: Pick): boolean { if (output.channel !== "tool" || typeof output.itemId !== "string" || output.itemId.length === 0) return false; const method = String(output.method || ""); if (method !== "item/started" && method !== "item/completed") return false; return /\b(?:webSearch|mcpToolCall|dynamicToolCall)\b/u.test(String(output.text || "")); } function codexToolLifecycleStartedBeforeIn(outputs: Pick[], output: Pick): boolean { if (!isCodexToolLifecycleOutput(output)) return false; return outputs.some((item) => item !== output && isCodexToolLifecycleOutput(item) && item.itemId === output.itemId && item.method === "item/started"); } const judgeFailRetryLimit = 3; const transcriptCache = new Map(); const codexSessionPathCache = new Map(); const codexSessionFileChangeCache = new Map }>(); const codexSessionCommandOutputCache = new Map }>(); let context: TaskViewContext | null = null; export function configureTaskView(runtimeContext: TaskViewContext): void { context = runtimeContext; } function ctx(): TaskViewContext { if (context === null) throw new Error("task-view module is not configured"); return context; } function taskTimestamp(value: string | null): string | null { if (value === null || value.length === 0) return null; const time = Date.parse(value); return Number.isFinite(time) ? new Date(time).toISOString() : null; } function terminalTask(task: QueueTask): boolean { return task.status === "succeeded" || task.status === "failed" || task.status === "canceled"; } function terminalTaskUnread(task: QueueTask): boolean { return terminalTask(task) && task.readAt === null; } function queuedStatusPayload(task: QueueTask): { queuedReason: QueuedStatusReason | null; queuedReasonLabel: string | null } { const reason = ctx().queuedStatusReason(task); return { queuedReason: reason, queuedReasonLabel: reason?.label ?? null, }; } function durationMsBetween(startAt: string | null | undefined, endAt: string | null | undefined): number | null { const start = typeof startAt === "string" ? Date.parse(startAt) : NaN; const end = typeof endAt === "string" ? Date.parse(endAt) : NaN; if (!Number.isFinite(start) || !Number.isFinite(end)) return null; return Math.max(0, end - start); } function safePreview(value: string, max = 900): string { const compact = value.replace(/\s+/gu, " ").trim(); return compact.length > max ? `${compact.slice(0, max)}...` : compact; } function prefixPreview(value: string, max = 900): string { const trimmed = value.trim(); return trimmed.length > max ? `${trimmed.slice(0, max)}...` : trimmed; } function linePreview(text: string, maxLines: number, maxChars: number): { text: string; omittedLines: number } { const clean = text.replace(/\u001b\[[0-9;]*m/gu, "").trimEnd(); if (clean.length === 0) return { text: "", omittedLines: 0 }; const lines = clean.split(/\r?\n/u); const kept: string[] = []; let chars = 0; for (const line of lines) { if (kept.length >= maxLines || chars + line.length > maxChars) break; kept.push(line); chars += line.length + 1; } return { text: kept.join("\n"), omittedLines: Math.max(0, lines.length - kept.length) }; } function completeTraceText(text: string): { text: string; omittedLines: number } { return { text: text.replace(/\u001b\[[0-9;]*m/gu, "").trimEnd(), omittedLines: 0 }; } function editedOutputPreview(text: string): { text: string; omittedLines: number } { return linePreview(compactTranscriptBody(text), 120, 24_000); } function codexSessionDateDir(value: string | null | undefined): string | null { if (typeof value !== "string" || value.length === 0) return null; const date = new Date(value); if (Number.isNaN(date.getTime())) return null; return resolve( ctx().config.codexHome, "sessions", String(date.getUTCFullYear()), String(date.getUTCMonth() + 1).padStart(2, "0"), String(date.getUTCDate()).padStart(2, "0"), ); } function codexSessionSignature(path: string): string | null { try { const stat = statSync(path); return `${stat.size}:${Math.floor(stat.mtimeMs)}`; } catch { return null; } } function findCodexSessionFile(task: QueueTask): string | null { const threadId = task.codexThreadId; if (threadId === null || threadId.length === 0) return null; const cached = codexSessionPathCache.get(threadId); if (cached !== undefined && existsSync(cached)) return cached; const roots = Array.from(new Set([ codexSessionDateDir(task.startedAt), codexSessionDateDir(task.createdAt), codexSessionDateDir(task.updatedAt), resolve(ctx().config.codexHome, "sessions"), ].filter((value): value is string => value !== null && existsSync(value)))); const matches: string[] = []; let scanned = 0; const scan = (dir: string, depth: number): void => { if (depth < 0 || scanned > 4000) return; scanned += 1; let entries: Dirent[]; try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; } for (const entry of entries) { const path = resolve(dir, entry.name); if (entry.isDirectory()) { scan(path, depth - 1); } else if (entry.isFile() && entry.name.endsWith(".jsonl") && entry.name.includes(threadId)) { matches.push(path); } } }; for (const root of roots) scan(root, root.endsWith("/sessions") ? 4 : 1); matches.sort((left, right) => (statMtimeMs(right) - statMtimeMs(left)) || right.localeCompare(left)); const match = matches[0] ?? null; if (match !== null) codexSessionPathCache.set(threadId, match); return match; } function statMtimeMs(path: string): number { try { return statSync(path).mtimeMs; } catch { return 0; } } function parseCodexToolOutputText(raw: string): string { const text = String(raw || ""); try { const parsed = JSON.parse(text) as Record; if (typeof parsed.output === "string") return parsed.output; } catch { // Keep the raw tool output if it is not the JSON wrapper used by Codex. } return text; } function recordStringField(record: Record | null, keys: string[]): string { if (record === null) return ""; for (const key of keys) { const value = record[key]; if (typeof value === "string") return value; } return ""; } function recordNumberField(record: Record | null, keys: string[]): number | null { if (record === null) return null; for (const key of keys) { const value = Number(record[key]); if (Number.isFinite(value)) return value; } return null; } function parseToolOutputStreams(raw: string): { stdout: string; stderr: string; output: string; exitCode: number | null } { const text = String(raw || ""); try { const parsed = JSON.parse(text) as unknown; const record = extractRecord(parsed); const stdout = recordStringField(record, ["stdout", "stdoutText"]); const stderr = recordStringField(record, ["stderr", "stderrText"]); const output = recordStringField(record, ["output", "text"]); const exitCode = recordNumberField(record, ["exitCode", "code", "status"]); if (stdout.length > 0 || stderr.length > 0 || output.length > 0) { return { stdout: stdout || output, stderr, output: output || [stdout, stderr].filter(Boolean).join("\n"), exitCode }; } } catch { // Not a JSON wrapper; parse the Codex CLI tool-result envelope below. } const outputMatch = /(?:^|\n)Output:\n([\s\S]*)$/u.exec(text); const exitMatch = /Process exited with code\s+(-?\d+)/u.exec(text); if (outputMatch !== null) { const output = (outputMatch[1] ?? "").trimEnd(); return { stdout: output, stderr: "", output, exitCode: exitMatch === null ? null : Number(exitMatch[1]), }; } return { stdout: text, stderr: "", output: text, exitCode: exitMatch === null ? null : Number(exitMatch[1]) }; } function formatCommandOutput(output: SessionCommandOutput | null | undefined): string { if (output === null || output === undefined) return ""; const parts: string[] = []; const stdout = output.stdout.trimEnd(); const stderr = output.stderr.trimEnd(); if (stdout.length > 0) parts.push(stderr.length > 0 ? `[stdout]\n${stdout}` : stdout); if (stderr.length > 0) parts.push(`[stderr]\n${stderr}`); if (parts.length > 0) return parts.join("\n"); return output.output.trimEnd(); } function addCommandOutputStreams(line: TranscriptLine, output: SessionCommandOutput | null | undefined, fullText: boolean): TranscriptLine { if (output === null || output === undefined) return line; const stdout = output.stdout.trimEnd(); const stderr = output.stderr.trimEnd(); if (stdout.length > 0) { const preview = fullText ? completeTraceText(stdout) : outputPreview(stdout); if (preview.text.length > 0) { line.stdoutPreview = preview.text; line.stdoutOmittedLines = preview.omittedLines || undefined; } } if (stderr.length > 0) { const preview = fullText ? completeTraceText(stderr) : outputPreview(stderr); if (preview.text.length > 0) { line.stderrPreview = preview.text; line.stderrOmittedLines = preview.omittedLines || undefined; } } return line; } function parseCodexSessionCommandOutputs(path: string): Map { const signature = codexSessionSignature(path); const cached = codexSessionCommandOutputCache.get(path); if (signature !== null && cached?.signature === signature) return cached.outputs; const outputs = new Map(); try { const text = readFileSync(path, "utf8"); for (const line of text.split(/\r?\n/u)) { if (line.trim().length === 0) continue; let record: Record; try { record = JSON.parse(line) as Record; } catch { continue; } const payload = extractRecord(record.payload); if (payload === null) continue; const type = String(payload.type || ""); if (type !== "function_call_output" && type !== "custom_tool_call_output") continue; const callId = typeof payload.call_id === "string" ? payload.call_id : ""; if (callId.length === 0) continue; const parsed = parseToolOutputStreams(String(payload.output || "")); if (parsed.output.trim().length === 0 && parsed.stdout.trim().length === 0 && parsed.stderr.trim().length === 0) continue; outputs.set(callId, { callId, at: typeof record.timestamp === "string" ? record.timestamp : "", stdout: parsed.stdout, stderr: parsed.stderr, output: parsed.output, exitCode: parsed.exitCode, }); } } catch (error) { ctx().logger("warn", "codex_session_command_output_parse_failed", { path, error: ctx().errorToJson(error) }); } if (signature !== null) codexSessionCommandOutputCache.set(path, { signature, outputs }); if (codexSessionCommandOutputCache.size > 40) { const firstKey = codexSessionCommandOutputCache.keys().next().value; if (typeof firstKey === "string") codexSessionCommandOutputCache.delete(firstKey); } return outputs; } function codexSessionCommandOutputsByCallId(task: QueueTask): Map { const path = findCodexSessionFile(task); return path === null ? new Map() : parseCodexSessionCommandOutputs(path); } function isInlineFileChangeInput(name: string, input: string): boolean { return name === "apply_patch" && /^\*\*\* Begin Patch/mu.test(input); } function trimmedPatchForTrace(input: string): string { const normalized = input.replace(/\r\n/gu, "\n").replace(/\r/gu, "\n").trimEnd(); const maxChars = 120_000; if (normalized.length <= maxChars) return normalized; return `${normalized.slice(0, maxChars)}\n...[patch content truncated: ${normalized.length - maxChars} chars omitted]`; } function parseCodexSessionFileChanges(path: string): Map { const signature = codexSessionSignature(path); const cached = codexSessionFileChangeCache.get(path); if (signature !== null && cached?.signature === signature) return cached.changes; const changes = new Map(); try { const text = readFileSync(path, "utf8"); for (const line of text.split(/\r?\n/u)) { if (line.trim().length === 0) continue; let record: Record; try { record = JSON.parse(line) as Record; } catch { continue; } const payload = extractRecord(record.payload); if (payload === null) continue; const type = String(payload.type || ""); const callId = typeof payload.call_id === "string" ? payload.call_id : ""; if (callId.length === 0) continue; if (type === "custom_tool_call") { const name = String(payload.name || ""); const input = typeof payload.input === "string" ? payload.input : ""; if (!isInlineFileChangeInput(name, input)) continue; changes.set(callId, { callId, at: typeof record.timestamp === "string" ? record.timestamp : "", name, input: trimmedPatchForTrace(input), output: "", }); continue; } if (type === "custom_tool_call_output") { const existing = changes.get(callId); if (existing === undefined) continue; existing.output = parseCodexToolOutputText(String(payload.output || "")); } } } catch (error) { ctx().logger("warn", "codex_session_file_change_parse_failed", { path, error: ctx().errorToJson(error) }); } if (signature !== null) codexSessionFileChangeCache.set(path, { signature, changes }); if (codexSessionFileChangeCache.size > 40) { const firstKey = codexSessionFileChangeCache.keys().next().value; if (typeof firstKey === "string") codexSessionFileChangeCache.delete(firstKey); } return changes; } function codexSessionFileChangesByCallId(task: QueueTask): Map { const path = findCodexSessionFile(task); return path === null ? new Map() : parseCodexSessionFileChanges(path); } function fileChangeTextWithInlinePatch(item: LiveOutput, changes: Map): string { if (item.method !== "item/fileChange/outputDelta" || typeof item.itemId !== "string") return item.text; const change = changes.get(item.itemId); if (change === undefined || change.input.length === 0 || item.text.includes(change.input)) return item.text; return `${item.text.trimEnd()}\n\n${change.input}\n`; } function compactNoisyLine(line: string): string { const compact = line.replace(/\s+/gu, " ").trimEnd(); const hasEncodedBlob = /[A-Za-z0-9+/=]{220,}/u.test(compact); const hasSshWrapper = compact.includes("UNIDESK_SSH_TOOL_DIR") || compact.includes("apply_patch") || compact.includes("base64 -d"); if (compact.length > 420 && (hasEncodedBlob || hasSshWrapper)) { return `${compact.slice(0, 220)} ... [omitted noisy wrapper, ${compact.length - 220} chars]`; } return compact.length > 1200 ? `${compact.slice(0, 900)} ... [omitted ${compact.length - 900} chars]` : line; } function compactTranscriptBody(text: string): string { return text.split(/\r?\n/u).map(compactNoisyLine).join("\n"); } function parseCommandLine(text: string): { command: string; status?: string } | null { const match = text.match(/^item\/(?:started|completed):\s+([\s\S]*?)\s+status=([A-Za-z0-9_-]+)/u); if (match === null) return null; return { command: match[1]?.trim() ?? "", status: match[2] }; } function extractOuterQuotedShellArg(text: string): { body: string; trailing: string } | null { const quote = text[0]; if (quote !== "\"" && quote !== "'") return null; let escaped = false; for (let index = 1; index < text.length; index += 1) { const char = text[index] ?? ""; if (quote === "\"" && escaped) { escaped = false; continue; } if (quote === "\"" && char === "\\") { escaped = true; continue; } if (char === quote) return { body: text.slice(1, index), trailing: text.slice(index + 1).trim() }; } return null; } function decodeShellDoubleQuoted(text: string): string { return text .replace(/\\(["\\$`])/gu, "$1") .replace(/\\\r?\n/gu, ""); } function displayCommand(command: string): string { const normalized = command.trim().replace(/\\n/gu, "\n"); const match = normalized.match(/^(?:\/usr\/bin\/env\s+)?(?:\/(?:usr\/)?bin\/)?(?:bash|sh)\s+-lc\s+([\s\S]+)$/u); if (match === null) return normalized; const shellText = (match[1] ?? "").trim(); const shellArg = extractOuterQuotedShellArg(shellText); if (shellArg === null) return (match[1] ?? normalized).trim(); const quote = shellText[0]; const body = quote === "\"" ? decodeShellDoubleQuoted(shellArg.body) : shellArg.body; return shellArg.trailing.length > 0 ? `${body} ${shellArg.trailing}` : body; } function shortCommandTitle(command: string): string { const firstLine = displayCommand(command).split(/\r?\n/u).find((line) => line.trim().length > 0)?.trim() ?? command.trim(); return safePreview(firstLine, 180); } function commandPreview(command: string): { text: string; omittedLines: number } { return linePreview(compactTranscriptBody(displayCommand(command)), 10, 8000); } function outputPreview(text: string): { text: string; omittedLines: number } { return linePreview(compactTranscriptBody(text), 4, 1600); } function fullMessageBody(text: string): { text: string; omittedLines: number } { return linePreview(text.replace(/\u001b\[[0-9;]*m/gu, "").trimEnd(), 12, 4000); } function timestampMs(value: string | null | undefined): number | null { if (typeof value !== "string" || value.length === 0) return null; const time = Date.parse(value); return Number.isFinite(time) ? time : null; } function nonNegativeElapsed(startMs: number | null, endMs: number | null): number | null { if (startMs === null || endMs === null) return null; return Math.max(0, endMs - startMs); } function taskTiming(task: QueueTask): JsonValue { const nowMs = Date.now(); const createdMs = timestampMs(task.createdAt); const startedMs = timestampMs(task.startedAt); const finishedMs = timestampMs(task.finishedAt); const updatedMs = timestampMs(task.updatedAt); const terminal = task.status === "succeeded" || task.status === "failed" || task.status === "canceled"; const effectiveEndMs = finishedMs ?? (terminal ? updatedMs : nowMs); return { queueWaitMs: nonNegativeElapsed(createdMs, startedMs ?? (terminal ? effectiveEndMs : nowMs)), durationMs: nonNegativeElapsed(startedMs, effectiveEndMs), totalElapsedMs: nonNegativeElapsed(createdMs, effectiveEndMs), effectiveEndAt: finishedMs !== null ? task.finishedAt : terminal ? task.updatedAt : null, running: !terminal && startedMs !== null, }; } const codexStatsTimeZone = "Asia/Shanghai"; const codexStatsDateFormatter = new Intl.DateTimeFormat("en-CA", { timeZone: codexStatsTimeZone, year: "numeric", month: "2-digit", day: "2-digit", }); interface DailyTaskStatsBucket { date: string; executedTasks: number; completedTasks: number; retryAttempts: number; succeededTasks: number; failedTasks: number; canceledTasks: number; totalDurationMs: number; durationSamples: number; } function codexStatsDateKey(value: string | Date | null | undefined): string | null { const date = value instanceof Date ? value : typeof value === "string" && value.length > 0 ? new Date(value) : null; if (date === null || Number.isNaN(date.getTime())) return null; const parts = codexStatsDateFormatter.formatToParts(date).reduce>((memo, part) => { if (part.type !== "literal") memo[part.type] = part.value; return memo; }, {}); const year = parts.year; const month = parts.month; const day = parts.day; return year !== undefined && month !== undefined && day !== undefined ? `${year}-${month}-${day}` : null; } function shiftDateKey(dateKey: string, offsetDays: number): string { const [year = "1970", month = "01", day = "01"] = dateKey.split("-"); const shifted = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day) + offsetDays)); return shifted.toISOString().slice(0, 10); } function statsDaysFromUrl(url: URL): number { const value = Number(url.searchParams.get("days") ?? 14); return Number.isInteger(value) && value > 0 ? Math.min(90, value) : 14; } function emptyDailyTaskStatsBucket(date: string): DailyTaskStatsBucket { return { date, executedTasks: 0, completedTasks: 0, retryAttempts: 0, succeededTasks: 0, failedTasks: 0, canceledTasks: 0, totalDurationMs: 0, durationSamples: 0, }; } function retryAttemptDates(task: QueueTask): Array { const attempts = Array.isArray(task.attempts) ? task.attempts : []; const retryAttempts = attempts.filter((attempt, index) => attempt.mode === "retry" || Number(attempt.index || 0) > 1 || index > 0); const fallbackRetryCount = Math.max(0, Math.floor(Number(task.currentAttempt || 0)) - 1); const fallbackAt = taskTimestamp(task.updatedAt) ?? taskTimestamp(task.startedAt) ?? taskTimestamp(task.createdAt); return [ ...retryAttempts.map((attempt) => taskTimestamp(attempt.startedAt) ?? taskTimestamp(attempt.finishedAt) ?? fallbackAt), ...Array.from({ length: Math.max(0, fallbackRetryCount - retryAttempts.length) }, () => fallbackAt), ]; } function completedTaskDurationMs(task: QueueTask, finishedAt: string): number | null { const startedAt = taskTimestamp(task.startedAt) ?? taskTimestamp(task.attempts[0]?.startedAt ?? null) ?? taskTimestamp(task.createdAt); return durationMsBetween(startedAt, finishedAt); } function taskStatisticsSummary(tasks: QueueTask[], days = 14): JsonValue { const generatedAt = ctx().nowIso(); const endDate = codexStatsDateKey(new Date()) ?? generatedAt.slice(0, 10); const safeDays = Math.max(1, Math.min(90, Math.floor(days))); const startDate = shiftDateKey(endDate, 1 - safeDays); const buckets = new Map(); for (let offset = 0; offset < safeDays; offset += 1) { const date = shiftDateKey(startDate, offset); buckets.set(date, emptyDailyTaskStatsBucket(date)); } const bucketFor = (value: string | null): DailyTaskStatsBucket | null => { const date = codexStatsDateKey(value); return date === null ? null : buckets.get(date) ?? null; }; for (const task of tasks) { const executedBucket = bucketFor(taskTimestamp(task.startedAt) ?? taskTimestamp(task.attempts[0]?.startedAt ?? null)); if (executedBucket !== null) executedBucket.executedTasks += 1; for (const retryAt of retryAttemptDates(task)) { const retryBucket = bucketFor(retryAt); if (retryBucket !== null) retryBucket.retryAttempts += 1; } if (!terminalTask(task)) continue; const finishedAt = taskTimestamp(task.finishedAt) ?? taskTimestamp(task.updatedAt); const completedBucket = bucketFor(finishedAt); if (finishedAt === null || completedBucket === null) continue; completedBucket.completedTasks += 1; if (task.status === "succeeded") completedBucket.succeededTasks += 1; if (task.status === "failed") completedBucket.failedTasks += 1; if (task.status === "canceled") completedBucket.canceledTasks += 1; const durationMs = completedTaskDurationMs(task, finishedAt); if (durationMs !== null) { completedBucket.totalDurationMs += durationMs; completedBucket.durationSamples += 1; } } const daily = Array.from(buckets.values()).map((bucket) => ({ date: bucket.date, executedTasks: bucket.executedTasks, completedTasks: bucket.completedTasks, retryAttempts: bucket.retryAttempts, succeededTasks: bucket.succeededTasks, failedTasks: bucket.failedTasks, canceledTasks: bucket.canceledTasks, avgDurationMs: bucket.durationSamples > 0 ? Math.round(bucket.totalDurationMs / bucket.durationSamples) : null, totalDurationMs: bucket.totalDurationMs, durationSamples: bucket.durationSamples, })); const totals = daily.reduce((memo, day) => { memo.executedTasks += day.executedTasks; memo.completedTasks += day.completedTasks; memo.retryAttempts += day.retryAttempts; memo.succeededTasks += day.succeededTasks; memo.failedTasks += day.failedTasks; memo.canceledTasks += day.canceledTasks; memo.totalDurationMs += day.totalDurationMs; memo.durationSamples += day.durationSamples; return memo; }, { executedTasks: 0, completedTasks: 0, retryAttempts: 0, succeededTasks: 0, failedTasks: 0, canceledTasks: 0, totalDurationMs: 0, durationSamples: 0, }); return { generatedAt, timezone: codexStatsTimeZone, days: safeDays, range: { startDate, endDate }, totals: { ...totals, avgDurationMs: totals.durationSamples > 0 ? Math.round(totals.totalDurationMs / totals.durationSamples) : null, }, daily, } as unknown as JsonValue; } function transcriptLine(kind: TranscriptKind, at: string, seq: number, title: string, rawSeqs: number[], body = "", command = "", status?: string, fullText = false): TranscriptLine { const bodyInfo = fullText ? completeTraceText(body) : kind === "message" ? fullMessageBody(body) : kind === "edited" ? editedOutputPreview(body) : outputPreview(body); const commandInfo = command.length > 0 ? fullText ? completeTraceText(displayCommand(command)) : commandPreview(command) : { text: "", omittedLines: 0 }; return { seq, at, kind, title, status, commandPreview: commandInfo.text || undefined, commandOmittedLines: commandInfo.omittedLines || undefined, bodyPreview: bodyInfo.text || undefined, bodyOmittedLines: bodyInfo.omittedLines || undefined, rawSeqs, }; } function pushUniqueRawSeq(rawSeqs: number[], seq: number): void { if (!rawSeqs.includes(seq)) rawSeqs.push(seq); } function messageFragmentMergeKey(line: TranscriptLine): string | null { if (line.kind !== "message") return null; const title = String(line.title || "").trim().toLowerCase(); if (title !== "assistant message" && title !== "reasoning") return null; return title; } function appendMessageText(left: string, right: string): string { if (left.length === 0) return right; if (right.length === 0) return left; if (!/\s$/u.test(left) && !/^\s|^[,.;:!?)}\]]/u.test(right) && /[A-Za-z0-9]/u.test(left.at(-1) || "") && /[A-Za-z0-9]/u.test(right[0] || "")) return `${left} ${right}`; return `${left}${right}`; } function mergeTranscriptMessageGroup(group: TranscriptLine[]): TranscriptLine { const first = group[0]; const last = group[group.length - 1] || first; const body = group.reduce((text, line) => appendMessageText(text, String(line.bodyPreview || "")), ""); const rawSeqs: number[] = []; for (const line of group) { for (const seq of Array.isArray(line.rawSeqs) ? line.rawSeqs : []) pushUniqueRawSeq(rawSeqs, seq); } return { ...first, seq: Number.isFinite(Number(last.seq)) ? Number(last.seq) : Number(first.seq), at: last.at || first.at, status: last.status || first.status, bodyPreview: body.length > 0 ? body : undefined, bodyOmittedLines: group.reduce((sum, line) => sum + Number(line.bodyOmittedLines || 0), 0) || undefined, rawSeqs, }; } function coalesceTranscriptMessageFragments(entries: TranscriptLine[]): TranscriptLine[] { const rows = sortTranscript([...entries]); const merged: TranscriptLine[] = []; let group: TranscriptLine[] = []; let groupKey: string | null = null; const flush = () => { if (group.length === 0) return; merged.push(group.length === 1 ? group[0] : mergeTranscriptMessageGroup(group)); group = []; groupKey = null; }; for (const line of rows) { const key = messageFragmentMergeKey(line); if (key !== null && key === groupKey) { group.push(line); continue; } flush(); if (key !== null) { group = [line]; groupKey = key; } else { merged.push(line); } } flush(); return merged; } function traceMessageOverlayKey(line: TranscriptLine): string | null { return messageFragmentMergeKey(line); } function traceMessageOverlayCandidate(line: TranscriptLine): boolean { return traceMessageOverlayKey(line) !== null && String(line.bodyPreview || "").trim().length > 0; } function overlayTraceMessagesFromRawTranscript(oaLines: TranscriptLine[], rawLines: TranscriptLine[]): TranscriptLine[] { const rawMessages = rawLines.filter(traceMessageOverlayCandidate); if (rawMessages.length === 0) return oaLines; const messageSeqs = new Set(); for (const line of oaLines) { if (traceMessageOverlayKey(line) === null) continue; for (const seq of Array.isArray(line.rawSeqs) && line.rawSeqs.length > 0 ? line.rawSeqs : [line.seq]) { if (Number.isFinite(Number(seq))) messageSeqs.add(Number(seq)); } } const result = oaLines.filter((line) => traceMessageOverlayKey(line) === null); for (const raw of rawMessages) { const seqs = Array.isArray(raw.rawSeqs) && raw.rawSeqs.length > 0 ? raw.rawSeqs : [raw.seq]; if (!seqs.some((seq) => messageSeqs.has(Number(seq)))) continue; result.push(raw); } return coalesceTranscriptMessageFragments(result); } function commandKind(command: string): TranscriptKind { if (/\bwebSearch\b/u.test(command)) return "explored"; if (/\b(apply_patch|git apply|cat >|tee .*<<|sed -i|python3? .*write_text)\b/u.test(command)) return "edited"; if (/\b(rg|grep|find|ls|cat|sed -n|tail|head|git status|git diff|ps)\b/u.test(command)) return "explored"; return "ran"; } function commandKindLabel(kind: TranscriptKind): string { if (kind === "edited") return "Edited"; if (kind === "explored") return "Explored"; return "Ran"; } function jsonPreview(value: unknown, max = 4000): string { try { return safePreview(JSON.stringify(value), max); } catch { return safePreview(String(value ?? ""), max); } } function recordDisplayField(record: Record | null, keys: string[], max = 3000): string { if (record === null) return ""; for (const key of keys) { const value = record[key]; if (typeof value === "string") return value; if (value !== undefined && value !== null) return jsonPreview(value, max); } return ""; } function decodeJsonStringFragment(value: string): string { try { return JSON.parse(`"${value}"`) as string; } catch { return value .replace(/\\"/gu, "\"") .replace(/\\\\/gu, "\\") .replace(/\\n/gu, "\n") .replace(/\\r/gu, "\r") .replace(/\\t/gu, "\t"); } } function partialJsonStringField(text: string, key: string): string { const match = new RegExp(`"${key}"\\s*:\\s*"`, "u").exec(text); if (match === null) return ""; const start = match.index + match[0].length; let escaped = false; for (let index = start; index < text.length; index += 1) { const char = text[index] ?? ""; if (escaped) { escaped = false; continue; } if (char === "\\") { escaped = true; continue; } if (char === "\"") return decodeJsonStringFragment(text.slice(start, index)); } return decodeJsonStringFragment(text.slice(start).replace(/\s*\.\.\.\s*$/u, "")); } function partialJsonNumberField(text: string, key: string): number | null { const match = new RegExp(`"${key}"\\s*:\\s*(-?\\d+(?:\\.\\d+)?)`, "u").exec(text); if (match === null) return null; const value = Number(match[1]); return Number.isFinite(value) ? value : null; } function openCodePartialRecordFromOutput(item: LiveOutput): Record | null { if (!String(item.method || "").startsWith("opencode/")) return null; const text = item.text.trim(); if (!text.includes("\"part\"") && !text.includes("\"tool\"")) return null; const tool = partialJsonStringField(text, "tool"); const command = partialJsonStringField(text, "command") || partialJsonStringField(text, "cmd") || partialJsonStringField(text, "script"); const output = partialJsonStringField(text, "output") || partialJsonStringField(text, "result"); if (tool.length === 0 && command.length === 0 && output.length === 0) return null; const input: Record = {}; for (const key of ["command", "cmd", "script", "filePath", "filepath", "path", "pattern", "query", "title", "description"]) { const value = partialJsonStringField(text, key); if (value.length > 0) input[key] = value; } for (const key of ["offset", "limit"]) { const value = partialJsonNumberField(text, key); if (value !== null) input[key] = value; } return { type: partialJsonStringField(text, "type") || "tool_use", part: { type: "tool", tool: tool || "tool", state: { status: partialJsonStringField(text, "status"), input, output, }, }, }; } function openCodeRecordFromOutput(item: LiveOutput): Record | null { if (!String(item.method || "").startsWith("opencode/")) return null; try { return extractRecord(JSON.parse(item.text) as unknown); } catch { return openCodePartialRecordFromOutput(item); } } function openCodeToolInputCommand(tool: string, input: Record | null): string { const command = recordStringField(input, ["command", "cmd", "script"]); if (command.length > 0) return command; const path = recordStringField(input, ["filePath", "filepath", "path"]); const pattern = recordStringField(input, ["pattern", "query"]); const offset = recordNumberField(input, ["offset"]); const limit = recordNumberField(input, ["limit"]); const args: string[] = [tool]; if (pattern.length > 0) args.push(pattern); if (path.length > 0) args.push(path); if (offset !== null) args.push(`offset=${offset}`); if (limit !== null) args.push(`limit=${limit}`); if (args.length > 1) return args.join(" "); return input === null ? tool : `${tool} ${jsonPreview(input, 1200)}`; } function openCodeToolKind(tool: string, command: string): TranscriptKind { const normalized = `${tool} ${command}`.toLowerCase(); if (/\b(read|grep|glob|list|ls|find|search|view|cat|sed|rg)\b/u.test(normalized)) return "explored"; if (/\b(edit|write|patch|apply|update|create|delete|apply_patch|git apply|sed -i)\b/u.test(normalized)) return "edited"; return commandKind(command); } function openCodeToolTitle(tool: string, command: string, input: Record | null): string { const title = recordStringField(input, ["title", "description"]); if (title.length > 0) return safePreview(title, 180); const path = recordStringField(input, ["filePath", "filepath", "path"]); if (path.length > 0) return `${commandKindLabel(openCodeToolKind(tool, command))} ${path}`; return shortCommandTitle(command || tool); } function openCodeToolDurationMs(part: Record | null, state: Record | null): number | undefined { const explicit = recordNumberField(part, ["durationMs", "elapsedMs"]) ?? recordNumberField(state, ["durationMs", "elapsedMs"]); if (explicit !== null && explicit >= 0) return explicit; const time = extractRecord(state?.time) ?? extractRecord(part?.time); const start = recordNumberField(time, ["start", "startedAt"]); const end = recordNumberField(time, ["end", "finishedAt", "completedAt"]); return start !== null && end !== null && end >= start ? end - start : undefined; } function isOpenCodeStepBoundaryMethod(method: string | undefined): boolean { return method === "opencode/step-start" || method === "opencode/step-finish"; } function openCodeVisibleAssistantText(text: string): string { return String(text || "").replace(/<(?:think|thinking)\b[^>]*>[\s\S]*?<\/(?:think|thinking)>/giu, "").trim(); } function openCodeToolTranscriptLine(item: LiveOutput, fullText: boolean): TranscriptLine | null { const record = openCodeRecordFromOutput(item); const part = extractRecord(record?.part); if (record === null || part === null) return null; const recordType = recordStringField(record, ["type", "event", "name"]).toLowerCase(); const partType = recordStringField(part, ["type"]).toLowerCase(); if (recordType === "step_start" || recordType === "step-start" || partType === "step-start") return null; if (recordType === "step_finish" || recordType === "step-finish" || partType === "step-finish") return null; const state = extractRecord(part.state); const input = extractRecord(state?.input) ?? extractRecord(part.input); const tool = recordStringField(part, ["tool", "title"]) || recordStringField(record, ["type", "event", "name"]) || "tool"; const command = openCodeToolInputCommand(tool, input); const metadata = extractRecord(state?.metadata); const filediff = extractRecord(metadata?.filediff); const rawOutput = recordDisplayField(state, ["output", "result"]) || recordDisplayField(part, ["output", "text", "content"]) || recordDisplayField(record, ["output", "text", "content"]); const diffOutput = recordDisplayField(metadata, ["diff"]) || recordDisplayField(filediff, ["patch"]); const status = recordStringField(state, ["status"]) || recordStringField(part, ["status"]) || recordStringField(record, ["status"]); const kind = openCodeToolKind(tool, command); const body = kind === "edited" && diffOutput.length > 0 ? diffOutput : rawOutput.length > 0 ? rawOutput : jsonPreview(record, 3000); const line = transcriptLine(kind, item.at, item.seq, openCodeToolTitle(tool, command, input), [item.seq], body, command, status || item.method, fullText); const durationMs = openCodeToolDurationMs(part, state); if (durationMs !== undefined) line.durationMs = durationMs; return line; } function promptLineCount(text: string): number { return text.length > 0 ? text.split(/\r\n|\r|\n/u).length : 0; } function promptSnapshot(text: string, maxPreviewChars = 1200): { text: string; preview: string; chars: number; lines: number; truncated: boolean } { const normalized = text.trimEnd(); const preview = safePreview(normalized, maxPreviewChars); return { text: normalized, preview, chars: normalized.length, lines: promptLineCount(normalized), truncated: preview.length < normalized.length, }; } function setAttemptInputPrompt(attempt: AttemptSummary, prompt: string): void { const snapshot = promptSnapshot(prompt, 1200); if (snapshot.chars === 0) return; attempt.inputPrompt = snapshot.text; attempt.inputPromptPreview = snapshot.preview; attempt.inputPromptChars = snapshot.chars; attempt.inputPromptLines = snapshot.lines; } function setAttemptFeedbackPrompt(attempt: AttemptSummary | undefined, prompt: string, source: string, forAttempt: number | null): void { if (attempt === undefined) return; const snapshot = promptSnapshot(prompt, 1600); if (snapshot.chars === 0) return; attempt.feedbackPrompt = snapshot.text; attempt.feedbackPromptPreview = snapshot.preview; attempt.feedbackPromptChars = snapshot.chars; attempt.feedbackPromptLines = snapshot.lines; attempt.feedbackPromptSource = source; attempt.feedbackPromptForAttempt = forAttempt; } function taskInitialPromptLine(task: QueueTask, fullText = false): TranscriptLine | null { const prompt = (task.basePrompt || userPromptForDisplay(task.prompt)).trimEnd(); if (prompt.length === 0) return null; const line = transcriptLine("message", task.createdAt, 0.5, "Submitted prompt", [], prompt, "", "enqueue", fullText); const fullPrompt = task.prompt.trimEnd(); if (fullText && fullPrompt.length > 0 && fullPrompt !== prompt) { line.fullPrompt = fullPrompt; line.fullPromptLines = promptLineCount(fullPrompt); line.fullPromptChars = fullPrompt.length; line.foldedReferencePrompt = true; } return line; } function promptHistoryTranscriptLines(task: QueueTask, fullText = false): TranscriptLine[] { return ctx().mergePromptHistory([...(Array.isArray(task.promptHistory) ? task.promptHistory : []), ...ctx().outputPromptHistory(task)]) .map((item) => transcriptLine("message", item.at, item.seq, "Steer prompt", [item.seq], item.text, "", item.method, fullText)); } function sortTranscript(entries: TranscriptLine[]): TranscriptLine[] { return entries.sort((left, right) => Number(left.seq) - Number(right.seq)); } function boundedTranscript(entries: TranscriptLine[], limit: number): TranscriptLine[] { sortTranscript(entries); if (entries.length <= limit) return entries; const first = entries[0]; if (first?.title === "Submitted prompt" && first.rawSeqs.length === 0 && limit > 1) { return [first, ...entries.slice(-(limit - 1))]; } return entries.slice(-limit); } function buildTaskTranscript(task: QueueTask, limit = 180, rawOutputWindow = 0, fullText = false): TranscriptLine[] { const entries: TranscriptLine[] = []; const initialPrompt = taskInitialPromptLine(task, fullText); if (initialPrompt !== null) entries.push(initialPrompt); const promptHistoryLines = promptHistoryTranscriptLines(task, fullText); const promptHistorySeqs = new Set(promptHistoryLines.map((line) => line.seq)); entries.push(...promptHistoryLines); type ActiveCommand = { seq: number; at: string; command: string; status?: string; body: string; rawSeqs: number[]; itemId?: string }; let activeCommand: ActiveCommand | null = null; const activeCommandsByItemId = new Map(); const pushRawSeq = (command: ActiveCommand, seq: number): void => { if (!command.rawSeqs.includes(seq)) command.rawSeqs.push(seq); }; const flushCommand = (command: ActiveCommand | null = activeCommand): void => { if (command === null) return; const kind = commandKind(command.command); const output = command.itemId === undefined ? null : commandOutputs.get(command.itemId) ?? null; const body = command.body.length > 0 ? command.body : formatCommandOutput(output); const title = command.command.trim().length > 0 ? shortCommandTitle(command.command) : "Command output"; entries.push(addCommandOutputStreams(transcriptLine( kind, command.at, command.seq, title, command.rawSeqs, body, command.command, command.status, fullText, ), output, fullText)); if (command === activeCommand) activeCommand = null; if (command.itemId !== undefined && activeCommandsByItemId.get(command.itemId) === command) { activeCommandsByItemId.delete(command.itemId); } }; const itemIdCommand = (item: LiveOutput, parsed: { command: string; status?: string } | null = null): ActiveCommand | null => { if (typeof item.itemId !== "string" || item.itemId.length === 0) return null; let command = activeCommandsByItemId.get(item.itemId); if (command === undefined) { command = { seq: item.seq, at: item.at, command: parsed?.command ?? "", status: parsed?.status, body: "", rawSeqs: [], itemId: item.itemId, }; activeCommandsByItemId.set(item.itemId, command); } return command; }; const outputSource = rawOutputWindow > 0 ? task.output : taskFullOutput(task); const outputItems = rawOutputWindow > 0 && outputSource.length > rawOutputWindow ? outputSource.slice(-rawOutputWindow) : outputSource; const commandOutputs = outputItems.some((item) => item.channel === "command" && typeof item.itemId === "string") ? codexSessionCommandOutputsByCallId(task) : new Map(); const fileChangeInputs = outputItems.some((item) => item.channel === "diff" && item.method === "item/fileChange/outputDelta" && typeof item.itemId === "string") ? codexSessionFileChangesByCallId(task) : new Map(); type ActiveMessage = { seq: number; at: string; title: string; status?: string; body: string; rawSeqs: number[] }; type ActiveCodexTool = { seq: number; at: string; text: string; status?: string; rawSeqs: number[]; itemId?: string }; let activeMessage: ActiveMessage | null = null; const activeCodexToolsByItemId = new Map(); const flushMessage = (): void => { if (activeMessage === null) return; entries.push(transcriptLine("message", activeMessage.at, activeMessage.seq, activeMessage.title, activeMessage.rawSeqs, activeMessage.body, "", activeMessage.status, fullText)); activeMessage = null; }; const appendMessage = (item: LiveOutput, title: string, body: string, status?: string): void => { if (body.length === 0) return; if (activeMessage !== null && activeMessage.title === title && activeMessage.status === status) { activeMessage.body += body; activeMessage.at = item.at; activeMessage.seq = item.seq; pushUniqueRawSeq(activeMessage.rawSeqs, item.seq); return; } flushMessage(); activeMessage = { seq: item.seq, at: item.at, title, status, body, rawSeqs: [item.seq] }; }; const parseCodexToolLifecycle = (item: LiveOutput): { status: string | undefined; text: string } => { const status = /\bstatus=([A-Za-z0-9_-]+)/u.exec(item.text)?.[1]; return { status, text: String(item.text || "").trimEnd() }; }; const codexToolLifecycleLine = (tool: ActiveCodexTool): TranscriptLine => { const kind = commandKind(tool.text); return transcriptLine(kind, tool.at, tool.seq, shortCommandTitle(tool.text), tool.rawSeqs, "", tool.text, tool.status, fullText); }; const flushCodexTool = (tool: ActiveCodexTool): void => { entries.push(codexToolLifecycleLine(tool)); if (tool.itemId !== undefined && activeCodexToolsByItemId.get(tool.itemId) === tool) activeCodexToolsByItemId.delete(tool.itemId); }; for (const item of outputItems) { if (initialPrompt !== null && item.channel === "user" && item.method === "enqueue") continue; if (item.channel === "user" && item.method === "turn/steer" && promptHistorySeqs.has(item.seq)) continue; if (isOpenCodeStepBoundaryMethod(item.method)) continue; if (item.channel === "command" && item.method === "item/started") { flushMessage(); const parsed = parseCommandLine(item.text); const groupedCommand = itemIdCommand(item, parsed); if (groupedCommand !== null) { flushCommand(); groupedCommand.seq = item.seq; groupedCommand.at = item.at; groupedCommand.command = parsed?.command || item.text; groupedCommand.status = parsed?.status; pushRawSeq(groupedCommand, item.seq); continue; } flushCommand(); activeCommand = { seq: item.seq, at: item.at, command: parsed?.command || item.text, status: parsed?.status, body: "", rawSeqs: [item.seq], ...(typeof item.itemId === "string" ? { itemId: item.itemId } : {}), }; continue; } if (item.channel === "command" && item.method === "item/commandExecution/outputDelta") { flushMessage(); const groupedCommand = itemIdCommand(item); if (groupedCommand !== null) { groupedCommand.body += item.text; pushRawSeq(groupedCommand, item.seq); } else if (activeCommand !== null) { activeCommand.body += item.text; pushRawSeq(activeCommand, item.seq); } else { const output = typeof item.itemId === "string" ? commandOutputs.get(item.itemId) ?? null : null; entries.push(addCommandOutputStreams(transcriptLine("ran", item.at, item.seq, "Command output", [item.seq], item.text || formatCommandOutput(output), "", undefined, fullText), output, fullText)); } continue; } if (item.channel === "command" && item.method === "item/completed") { flushMessage(); const parsed = parseCommandLine(item.text); const groupedCommand = itemIdCommand(item, parsed); if (groupedCommand !== null) { if (parsed?.command) groupedCommand.command = parsed.command; groupedCommand.status = parsed?.status ?? groupedCommand.status; pushRawSeq(groupedCommand, item.seq); flushCommand(groupedCommand); } else if (activeCommand !== null) { activeCommand.status = parsed?.status ?? activeCommand.status; pushRawSeq(activeCommand, item.seq); flushCommand(); } else { const command = parsed?.command || item.text; const kind = commandKind(command); const output = typeof item.itemId === "string" ? commandOutputs.get(item.itemId) ?? null : null; entries.push(addCommandOutputStreams(transcriptLine(kind, item.at, item.seq, shortCommandTitle(command), [item.seq], formatCommandOutput(output), command, parsed?.status, fullText), output, fullText)); } continue; } if (item.channel !== "assistant" && item.channel !== "reasoning") flushMessage(); flushCommand(); if (isCodexToolLifecycleOutput(item)) { const parsed = parseCodexToolLifecycle(item); const itemId = item.itemId || ""; const existing = activeCodexToolsByItemId.get(itemId); if (item.method === "item/started") { if (existing !== undefined) flushCodexTool(existing); activeCodexToolsByItemId.set(itemId, { seq: item.seq, at: item.at, text: parsed.text, status: parsed.status ?? item.method, rawSeqs: [item.seq], itemId, }); } else if (existing !== undefined) { existing.at = item.at; existing.status = parsed.status ?? existing.status; existing.text = parsed.text.length > 0 ? parsed.text : existing.text; pushUniqueRawSeq(existing.rawSeqs, item.seq); flushCodexTool(existing); } else { entries.push(codexToolLifecycleLine({ seq: item.seq, at: item.at, text: parsed.text, status: parsed.status ?? item.method, rawSeqs: [item.seq], itemId, })); } } else if (item.channel === "diff") { const text = fileChangeTextWithInlinePatch(item, fileChangeInputs); entries.push(transcriptLine("edited", item.at, item.seq, "Edited files", [item.seq], text, "", item.method, fullText)); } else if (item.channel === "error") { entries.push(transcriptLine("error", item.at, item.seq, "Error", [item.seq], item.text, "", item.method, fullText)); } else if (item.channel === "assistant") { const body = String(item.method || "").startsWith("opencode/") ? openCodeVisibleAssistantText(item.text) : item.text; appendMessage(item, "Assistant message", body, item.method); } else if (item.channel === "reasoning") { appendMessage(item, "Reasoning", item.text, item.method); } else if (item.channel === "user") { entries.push(transcriptLine("message", item.at, item.seq, item.method === "enqueue" ? "Submitted prompt" : "User prompt", [item.seq], item.text, "", item.method, fullText)); } else if (item.channel === "tool" && String(item.method || "").startsWith("opencode/")) { entries.push(openCodeToolTranscriptLine(item, fullText) ?? transcriptLine("system", item.at, item.seq, "OpenCode tool", [item.seq], item.text, "", item.method, fullText)); } else { const title = item.method === "queue" && item.text.startsWith("attempt ") ? "Attempt started" : item.method === "startup" || item.method === "shutdown" ? "Recovered thread execution" : item.method === "judge" ? "Judge result" : "System"; entries.push(transcriptLine("system", item.at, item.seq, title, [item.seq], item.text, "", item.method, fullText)); } } flushMessage(); flushCommand(); for (const command of Array.from(activeCommandsByItemId.values()).sort((left, right) => left.seq - right.seq)) { flushCommand(command); } for (const tool of Array.from(activeCodexToolsByItemId.values()).sort((left, right) => left.seq - right.seq)) { flushCodexTool(tool); } return boundedTranscript(coalesceTranscriptMessageFragments(entries), limit); } function buildCompactTaskTranscript(task: QueueTask, limit = 12, rawOutputWindow = 24): TranscriptLine[] { return buildTaskTranscript(task, limit, rawOutputWindow, false); } function transcriptSignature(task: QueueTask): string { const last = task.output.at(-1); return `${task.updatedAt}:${task.output.length}:${last?.seq ?? 0}:${last?.text.length ?? 0}:${outputArchiveSignature(task)}:${task.status}:${task.createdAt}:${task.basePrompt.length}:${task.prompt.length}:${task.promptHistory.length}:${task.promptHistory.at(-1)?.seq ?? 0}`; } function cachedTranscript(task: QueueTask, fullText: boolean): TranscriptLine[] { const signature = transcriptSignature(task); const cached = transcriptCache.get(task.id); if (cached?.signature === signature) { const cachedTranscript = fullText ? cached.fullTranscript : cached.previewTranscript; if (cachedTranscript !== undefined) return cachedTranscript; } const transcript = buildTaskTranscript(task, Number.MAX_SAFE_INTEGER, 0, fullText); const next: { signature: string; previewTranscript?: TranscriptLine[]; fullTranscript?: TranscriptLine[] } = cached?.signature === signature ? cached : { signature }; if (fullText) next.fullTranscript = transcript; else next.previewTranscript = transcript; transcriptCache.set(task.id, next); if (transcriptCache.size > 80) { const firstKey = transcriptCache.keys().next().value; if (typeof firstKey === "string") transcriptCache.delete(firstKey); } return transcript; } function cachedFullTranscript(task: QueueTask): TranscriptLine[] { return cachedTranscript(task, true); } function cachedPreviewTranscript(task: QueueTask): TranscriptLine[] { return cachedTranscript(task, false); } function outputForResponse(task: QueueTask, includeRaw: boolean): LiveOutput[] { if (includeRaw) return taskFullOutput(task); return task.output.slice(-80).map((item) => ({ ...item, text: safePreview(item.text, 4000) })); } function attemptForResponse(attempt: AttemptSummary, full = false): JsonValue { const finalResponse = String(attempt.finalResponse ?? ""); const inputPrompt = String(attempt.inputPrompt ?? ""); const feedbackPrompt = String(attempt.feedbackPrompt ?? ""); return { ...attempt, inputPrompt: full ? inputPrompt : undefined, inputPromptPreview: safePreview(String(attempt.inputPromptPreview || inputPrompt), full ? 3000 : 1200), inputPromptChars: Number(attempt.inputPromptChars ?? inputPrompt.length), inputPromptLines: Number(attempt.inputPromptLines ?? promptLineCount(inputPrompt)), finalResponse: full ? finalResponse : safePreview(finalResponse, 1200), finalResponsePreview: safePreview(String(attempt.finalResponsePreview || finalResponse), full ? 3000 : 1200), finalResponseChars: Number(attempt.finalResponseChars ?? finalResponse.length), feedbackPrompt: full ? feedbackPrompt : undefined, feedbackPromptPreview: safePreview(String(attempt.feedbackPromptPreview || feedbackPrompt), full ? 3000 : 1200), feedbackPromptChars: Number(attempt.feedbackPromptChars ?? feedbackPrompt.length), feedbackPromptLines: Number(attempt.feedbackPromptLines ?? promptLineCount(feedbackPrompt)), stderrTail: full ? attempt.stderrTail : safePreview(attempt.stderrTail, 1200), } as unknown as JsonValue; } function taskForResponse(task: QueueTask, full = false, includeRaw = full): JsonValue { const displayPrompt = task.basePrompt || userPromptForDisplay(task.prompt); return { ...task, agentPort: codeAgentPortForModel(task.model), agentPortInfo: codeAgentPortInfo(codeAgentPortForModel(task.model)), judgeFailRetryLimit, stepCount: null, llmStepCount: null, traceStats: null, statsSource: "unavailable", prompt: full ? task.prompt : safePreview(task.prompt, 2000), basePrompt: full ? task.basePrompt : safePreview(task.basePrompt, 2000), displayPrompt: full ? displayPrompt : safePreview(displayPrompt, 2000), promptEditable: ctx().queuedTaskPromptEditable(task), ...queuedStatusPayload(task), referenceTaskIds: task.referenceTaskIds, referenceInjection: task.referenceInjection, finalResponse: full ? task.finalResponse : safePreview(task.finalResponse, 5000), terminalUnread: terminalTaskUnread(task), attempts: task.attempts.map((attempt) => attemptForResponse(attempt, full)), output: outputForResponse(task, includeRaw), events: includeRaw ? task.events : task.events.slice(-120), transcript: full ? fullTranscript(task) : buildTaskTranscript(task, 120, 0), } as unknown as JsonValue; } function fullTranscript(task: QueueTask): TranscriptLine[] { return cachedFullTranscript(task); } function nonNegativeInteger(value: unknown): number | null { const parsed = Number(value); return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : null; } function retainedOutputStartsTraceStep(output: LiveOutput): boolean { if (output.channel === "diff" || output.channel === "tool") return true; if (output.channel !== "command") return false; const method = String(output.method || ""); return method === "item/started" || (method !== "item/commandExecution/outputDelta" && method !== "item/completed"); } function retainedTaskStepCount(task: QueueTask): number { return (Array.isArray(task.output) ? task.output : []).reduce((count, output) => count + (retainedOutputStartsTraceStep(output) ? 1 : 0), 0); } function taskStoredStepCount(task: QueueTask): number | null { return nonNegativeInteger(task.stepCount) ?? nonNegativeInteger(task.llmStepCount); } // Used only as a producer-side seed for OA stats snapshots; frontend/API // display authority is the oa_trace_stats projection applied at route edges. function taskListStepCount(task: QueueTask): number { return taskStoredStepCount(task) ?? retainedTaskStepCount(task); } function taskOutputMaxSeq(task: QueueTask): number { const output = Array.isArray(task.output) ? task.output : []; const promptHistory = Array.isArray(task.promptHistory) ? task.promptHistory : []; const attempts = Array.isArray(task.attempts) ? task.attempts : []; const values = [ nonNegativeInteger(task.outputMaxSeq), nonNegativeInteger(output.at(-1)?.seq), nonNegativeInteger(promptHistory.at(-1)?.seq), ...attempts.map((attempt) => nonNegativeInteger(attempt.outputEndSeq)), ].filter((value): value is number => value !== null); return values.length > 0 ? Math.max(...values) : 0; } function taskForMetaResponse(task: QueueTask): JsonValue { const lastOutputSeq = taskOutputMaxSeq(task); const displayPrompt = task.basePrompt || userPromptForDisplay(task.prompt); return { id: task.id, queueId: ctx().queueIdOf(task), queueEnteredAt: ctx().taskQueueEnteredAt(task), prompt: task.prompt, basePrompt: task.basePrompt, displayPrompt, promptChars: task.prompt.length, basePromptChars: task.basePrompt.length, displayPromptChars: displayPrompt.length, promptEditable: ctx().queuedTaskPromptEditable(task), finalResponseChars: task.finalResponse.length, stepCount: null, llmStepCount: null, traceStats: null, statsSource: "unavailable", summaryOnly: false, referenceTaskIds: task.referenceTaskIds, referenceInjection: task.referenceInjection, providerId: task.providerId, executionMode: task.executionMode, executionModeInfo: codeExecutionModeInfo(task.executionMode), cwd: task.cwd, model: task.model, agentPort: codeAgentPortForModel(task.model), agentPortInfo: codeAgentPortInfo(codeAgentPortForModel(task.model)), reasoningEffort: task.reasoningEffort, maxAttempts: task.maxAttempts, status: task.status, ...queuedStatusPayload(task), createdAt: task.createdAt, updatedAt: task.updatedAt, startedAt: task.startedAt, finishedAt: task.finishedAt, readAt: task.readAt, currentAttempt: task.currentAttempt, currentMode: task.currentMode, judgeFailCount: task.judgeFailCount, judgeFailRetryLimit, codexThreadId: task.codexThreadId, activeTurnId: task.activeTurnId, finalResponse: task.finalResponse, lastError: task.lastError, lastJudge: task.lastJudge, promptHistory: task.promptHistory, attempts: task.attempts, cancelRequested: task.cancelRequested, terminalUnread: terminalTaskUnread(task), nextMode: task.nextMode, outputCount: task.output.length, retainedOutputCount: task.output.length, eventCount: task.events.length, transcriptCount: null, transcriptMaxSeq: lastOutputSeq, timing: taskTiming(task), transcript: [], output: [], events: [], } as unknown as JsonValue; } function taskForCompactMetaResponse(task: QueueTask): JsonValue { const displayPrompt = task.basePrompt || userPromptForDisplay(task.prompt); const lastOutputSeq = taskOutputMaxSeq(task); return { id: task.id, queueId: ctx().queueIdOf(task), queueEnteredAt: ctx().taskQueueEnteredAt(task), prompt: prefixPreview(task.prompt, 900), basePrompt: prefixPreview(task.basePrompt, 900), displayPrompt: prefixPreview(displayPrompt, 900), promptChars: task.prompt.length, basePromptChars: task.basePrompt.length, displayPromptChars: displayPrompt.length, promptEditable: ctx().queuedTaskPromptEditable(task), finalResponseChars: task.finalResponse.length, stepCount: null, llmStepCount: null, traceStats: null, statsSource: "unavailable", summaryOnly: true, referenceTaskIds: task.referenceTaskIds, referenceInjection: task.referenceInjection === null ? null : { injectedAt: task.referenceInjection.injectedAt, itemCount: task.referenceInjection.itemCount, directReferenceTaskIds: task.referenceInjection.directReferenceTaskIds, maxRounds: task.referenceInjection.maxRounds, truncated: task.referenceInjection.truncated, }, providerId: task.providerId, executionMode: task.executionMode, executionModeInfo: codeExecutionModeInfo(task.executionMode), cwd: task.cwd, model: task.model, agentPort: codeAgentPortForModel(task.model), agentPortInfo: codeAgentPortInfo(codeAgentPortForModel(task.model)), reasoningEffort: task.reasoningEffort, maxAttempts: task.maxAttempts, status: task.status, ...queuedStatusPayload(task), createdAt: task.createdAt, updatedAt: task.updatedAt, startedAt: task.startedAt, finishedAt: task.finishedAt, readAt: task.readAt, currentAttempt: task.currentAttempt, currentMode: task.currentMode, judgeFailCount: task.judgeFailCount, judgeFailRetryLimit, codexThreadId: task.codexThreadId, activeTurnId: task.activeTurnId, finalResponse: prefixPreview(task.finalResponse, 1200), lastError: task.lastError, lastJudge: task.lastJudge, promptHistory: task.promptHistory.slice(-8), attempts: task.attempts.slice(-3).map((attempt) => attemptForResponse(attempt, false)), cancelRequested: task.cancelRequested, terminalUnread: terminalTaskUnread(task), nextMode: task.nextMode, outputCount: task.output.length, eventCount: task.events.length, transcriptCount: null, transcriptMaxSeq: lastOutputSeq, timing: taskTiming(task), transcript: [], output: [], events: [], } as unknown as JsonValue; } function lastAssistantMessage(task: QueueTask, transcript = fullTranscript(task)): JsonValue { const assistantLine = transcript.slice().reverse().find((line) => line.kind === "message" && line.title === "Assistant message"); const text = task.finalResponse.trim().length > 0 ? task.finalResponse.trim() : String(assistantLine?.bodyPreview ?? "").trim(); return { text, at: assistantLine?.at ?? task.finishedAt ?? task.updatedAt, seq: assistantLine?.seq ?? null, source: task.finalResponse.trim().length > 0 ? "finalResponse" : assistantLine !== undefined ? "transcript" : "none", }; } function parseToolLimit(url: URL): number { const value = Number(url.searchParams.get("toolLimit") ?? 160); return Number.isInteger(value) && value > 0 ? Math.min(500, value) : 160; } function taskToolSummaryForLimit(task: QueueTask, limit: number, transcript = fullTranscript(task)): JsonValue { const allTools = transcript.filter((line) => line.kind === "ran" || line.kind === "explored" || line.kind === "edited"); const boundedLimit = Math.max(1, Math.min(500, Math.floor(limit))); const start = Math.max(0, allTools.length - boundedLimit); return { count: allTools.length, returned: allTools.length - start, limit: boundedLimit, truncated: start > 0, items: allTools.slice(start).map((line) => ({ seq: line.seq, at: line.at, kind: line.kind, title: line.title, status: line.status ?? null, commandPreview: line.commandPreview ?? "", commandOmittedLines: line.commandOmittedLines ?? 0, outputPreview: line.bodyPreview ?? "", outputOmittedLines: line.bodyOmittedLines ?? 0, rawSeqs: line.rawSeqs, })), } as unknown as JsonValue; } function taskToolSummary(task: QueueTask, url: URL, transcript = fullTranscript(task)): JsonValue { return taskToolSummaryForLimit(task, parseToolLimit(url), transcript); } function uniqueStrings(values: string[], limit = 20): string[] { const result: string[] = []; for (const value of values) { const normalized = value.trim(); if (normalized.length === 0 || result.includes(normalized)) continue; result.push(normalized); if (result.length >= limit) break; } return result; } function cleanTracePath(value: string): string { return value .replace(/^['"`([{<]+/u, "") .replace(/['"`)\]}>.,;:]+$/u, "") .replace(/:\d+(?::\d+)?$/u, "") .replace(/^[ab]\//u, "") .trim(); } function extractTracePaths(text: string): string[] { const source = String(text || ""); const matches = source.match(/(?:~|\.{1,2}|\/)?(?:[A-Za-z0-9_.@+-]+\/)+[A-Za-z0-9_.@+-]+|[A-Za-z0-9_.@+-]+\.(?:c|cc|cpp|h|hpp|js|jsx|ts|tsx|json|md|py|sh|toml|ya?ml|txt|log|lock)/gu) || []; return uniqueStrings(matches.map(cleanTracePath).filter((path) => path.length >= 2 && !path.includes("...") && !/^(http|https|status|method)$/iu.test(path)), 40); } function parseEditedFilesFromText(text: string): string[] { const files: string[] = []; const addFile = (path: string): void => { const cleanPath = cleanTracePath(path); if (cleanPath.length === 0 || cleanPath === "/dev/null" || files.includes(cleanPath)) return; files.push(cleanPath); }; for (const line of text.split(/\r?\n/u)) { const statusMatch = /^([AMDRCU?]{1,2})\s+(.+)$/u.exec(line); if (statusMatch) { addFile(statusMatch[2] || ""); continue; } const patchMatch = /^\*\*\*\s+(?:Add|Update|Delete)\s+File:\s+(.+)$/u.exec(line); if (patchMatch) { addFile(patchMatch[1] || ""); continue; } const moveMatch = /^\*\*\*\s+Move to:\s+(.+)$/u.exec(line); if (moveMatch) { addFile(moveMatch[1] || ""); continue; } const diffMatch = /^diff --git a\/(.+?) b\/(.+)$/u.exec(line); if (diffMatch) { addFile(diffMatch[2] || diffMatch[1] || ""); continue; } const plusMatch = /^\+\+\+ b\/(.+)$/u.exec(line); if (plusMatch) addFile(plusMatch[1] || ""); } return files.length > 0 ? files : extractTracePaths(text); } function transcriptPreviewLines(text: string, maxLines: number, maxChars: number): string[] { const preview = linePreview(text, maxLines, maxChars).text; return preview.length === 0 ? [] : preview.split(/\r?\n/u).slice(0, maxLines); } function transcriptLineSummaryLines(line: TranscriptLine): string[] { const lines: string[] = []; const command = String(line.commandPreview || "").trim(); if (command.length > 0) { for (const item of transcriptPreviewLines(command, 2, 420)) lines.push(`$ ${item}`); } const body = String(line.bodyPreview || "").trim(); const remaining = Math.max(0, 4 - lines.length); if (body.length > 0 && remaining > 0) { for (const item of transcriptPreviewLines(body, remaining, 700)) lines.push(item); } if (lines.length === 0) lines.push(line.status ? `${line.title} (${line.status})` : line.title); return lines.slice(0, 4); } function isToolActionLine(line: TranscriptLine): boolean { return line.kind === "ran" || line.kind === "explored" || line.kind === "edited"; } function toolStepCountsFromTranscript(lines: TranscriptLine[]): { toolCallCount: number; readCount: number; editCount: number; runCount: number } { const counts = lines.reduce((memo, line) => { if (line.kind === "explored") memo.readCount += 1; else if (line.kind === "edited") memo.editCount += 1; else if (line.kind === "ran") memo.runCount += 1; return memo; }, { readCount: 0, editCount: 0, runCount: 0 }); return { ...counts, toolCallCount: counts.readCount + counts.editCount + counts.runCount, }; } function realAttemptWindows(task: QueueTask, transcript: TranscriptLine[]): TraceAttemptWindow[] { return traceAttemptWindows(task, transcript).filter((window) => window.synthetic !== true && window.index > 0); } function taskExecutionTranscript(task: QueueTask, transcript: TranscriptLine[]): TranscriptLine[] { const windows = realAttemptWindows(task, transcript); if (windows.length === 0) return transcript; return sortTranscript(windows.flatMap((window) => executionLinesForAttempt(window.lines))); } function executionSummaryFromTranscript( task: QueueTask, transcript: TranscriptLine[], timing: Record, outputCount = taskFullOutput(task).length, retainedOutputCount = task.output.length, ): JsonValue { const toolLines = transcript.filter(isToolActionLine); const editedFiles = uniqueStrings(toolLines.flatMap((line) => line.kind === "edited" ? parseEditedFilesFromText(`${line.title}\n${line.bodyPreview ?? ""}`) : []), 40); const commands = uniqueStrings(toolLines .map((line) => String(line.commandPreview || "").split(/\r?\n/u).find((row) => row.trim().length > 0)?.trim() ?? "") .filter(Boolean), 30); const counts = toolStepCountsFromTranscript(transcript); return { durationMs: timing.durationMs ?? timing.totalElapsedMs ?? null, totalElapsedMs: timing.totalElapsedMs ?? null, queueWaitMs: timing.queueWaitMs ?? null, toolCallCount: counts.toolCallCount, readCount: counts.readCount, editCount: counts.editCount, runCount: counts.runCount, editedFiles, commands, stepCount: counts.toolCallCount, llmStepCount: counts.toolCallCount, traceLineCount: transcript.filter((line) => line.title !== "Submitted prompt").length, transcriptMaxSeq: transcript.at(-1)?.seq ?? 0, outputCount, retainedOutputCount, } as unknown as JsonValue; } function taskExecutionSummary(task: QueueTask, transcript = cachedPreviewTranscript(task)): JsonValue { return executionSummaryFromTranscript(task, taskExecutionTranscript(task, transcript), taskTiming(task) as Record); } function parseAttemptIndex(text: string): number | null { const match = /\battempt\s+(\d+)\s*\/\s*\d+/iu.exec(text); if (match === null) return null; const value = Number(match[1]); return Number.isInteger(value) && value > 0 ? value : null; } function traceAttemptIndexFromLine(line: TranscriptLine): number | null { return parseAttemptIndex(`${line.bodyPreview ?? ""}\n${line.commandPreview ?? ""}\n${line.title}`); } function parseJudgeLine(text: string): JudgeResult | null { const match = /\bjudge=(complete|retry|fail)\s+confidence=([0-9.]+)\s+source=([A-Za-z0-9_-]+):\s*([\s\S]*)$/u.exec(text.trim()); if (match === null) return null; const confidence = Number(match[2]); const source = match[3] === "minimax" ? "minimax" : "fallback"; const rawReason = (match[4] ?? "").trim(); const detailsMatch = /\bMiniMax failure details:\s+([\s\S]*)$/u.exec(rawReason); const detailsText = detailsMatch?.[1] ?? ""; const detailValue = (key: string): string | null => new RegExp(`\\b${key}=([^\\s]+)`, "u").exec(detailsText)?.[1] ?? null; const detailNumber = (key: string): number | undefined => { const value = Number(detailValue(key)); return Number.isFinite(value) ? value : undefined; }; const errorMessage = /\berror=([\s\S]*)$/u.exec(detailsText)?.[1]?.trim() ?? ""; const failureDetails = detailsText.length === 0 ? null : { provider: "minimax" as const, stage: (detailValue("stage") || "unknown") as JudgeResult["failureDetails"] extends infer T ? T extends { stage: infer S } ? S : never : never, model: "", apiBase: "", occurredAt: "", durationMs: detailNumber("durationMs") ?? 0, timeoutMs: detailNumber("timeoutMs") ?? 0, timedOut: detailValue("timedOut") === "true", errorName: detailValue("errorName") || "", errorMessage, promptChars: detailNumber("promptChars"), promptLines: detailNumber("promptLines"), payloadBytes: detailNumber("payloadBytes"), responseStatus: detailNumber("responseStatus") ?? null, }; return { decision: match[1] as JudgeDecision, confidence: Number.isFinite(confidence) ? confidence : 0, source, reason: detailsMatch === null ? rawReason : rawReason.slice(0, detailsMatch.index).trim(), failureDetails, }; } function judgeFromAttemptLines(lines: TranscriptLine[]): { judge: JudgeResult; at: string | null; seq: number | null } | null { for (const line of lines.slice().reverse()) { if (line.title !== "Judge result" && line.status !== "judge") continue; const judge = parseJudgeLine(String(line.bodyPreview || "")); if (judge === null) continue; return { judge, at: line.at || null, seq: Number.isFinite(Number(line.seq)) ? Number(line.seq) : null }; } return null; } function attemptFeedbackPromptRecord(task: QueueTask, attemptIndex: number, attempt: AttemptSummary | null, judge: JudgeResult | null = attempt?.judge ?? null): FeedbackPromptRecord | null { const directPrompt = String(attempt?.feedbackPrompt ?? "").trimEnd(); if (directPrompt.length > 0) { const snapshot = promptSnapshot(directPrompt, 1600); return { text: snapshot.text, preview: String(attempt?.feedbackPromptPreview || snapshot.preview), chars: Number(attempt?.feedbackPromptChars ?? snapshot.chars), lines: Number(attempt?.feedbackPromptLines ?? snapshot.lines), source: String(attempt?.feedbackPromptSource || "judge-feedback"), forAttempt: Number.isFinite(Number(attempt?.feedbackPromptForAttempt)) ? Number(attempt?.feedbackPromptForAttempt) : attemptIndex + 1, truncated: snapshot.truncated, }; } const hasPendingRetry = task.status === "retry_wait" || task.status === "queued"; if (attemptIndex === task.attempts.length && hasPendingRetry && task.nextMode === "retry" && task.nextPrompt !== null && task.nextPrompt.trim().length > 0) { const snapshot = promptSnapshot(task.nextPrompt, 1600); return { ...snapshot, source: "pending-retry", forAttempt: attemptIndex + 1 }; } const nextAttempt = task.attempts.find((item) => Number(item.index) === attemptIndex + 1) ?? task.attempts[attemptIndex] ?? null; const nextInput = String(nextAttempt?.inputPrompt ?? "").trimEnd(); if (nextInput.length > 0) { const snapshot = promptSnapshot(nextInput, 1600); return { ...snapshot, source: "attempt-input", forAttempt: attemptIndex + 1 }; } if (judge?.decision === "retry") { const generated = retryPrompt(task, judge); const snapshot = promptSnapshot(generated, 1600); return { ...snapshot, source: judge.continuePrompt?.trim() ? "judge-continue-prompt" : "judge-retry-generated", forAttempt: attemptIndex + 1 }; } return null; } function attemptTimingSummary(attempt: AttemptSummary | null, lines: TranscriptLine[]): Record { const startedAt = attempt?.startedAt || lines[0]?.at || null; const finishedAt = attempt?.finishedAt || lines.at(-1)?.at || null; const startedMs = timestampMs(startedAt); const finishedMs = timestampMs(finishedAt); const durationMs = nonNegativeElapsed(startedMs, finishedMs); return { durationMs, totalElapsedMs: durationMs, queueWaitMs: null, effectiveEndAt: finishedAt, }; } interface TraceAttemptWindow { index: number; attempt: AttemptSummary | null; startSeq: number | null; endSeq: number | null; lines: TranscriptLine[]; synthetic?: boolean; label?: string; } type TraceStatsLookup = Map | Record | null | undefined; function transcriptLineSeq(line: TranscriptLine | undefined): number | null { const value = Number(line?.seq ?? NaN); return Number.isFinite(value) ? value : null; } function traceWindowFirstSeq(window: TraceAttemptWindow): number { return transcriptLineSeq(window.lines[0]) ?? window.startSeq ?? Number.POSITIVE_INFINITY; } function isRecoveredThreadLine(line: TranscriptLine): boolean { const status = String(line.status || ""); const text = `${line.title}\n${line.bodyPreview ?? ""}\n${line.commandPreview ?? ""}`; return line.kind === "system" && ( line.title === "Recovered thread execution" || line.title === "Queue recovered" || status === "startup" || status === "shutdown" || /\btask queued for retry\b|Service (?:re)?started while task was active|Service stopping while task was active/iu.test(text) ); } function traceSystemLineIsError(line: TranscriptLine): boolean { const text = [ line.title, line.status, line.bodyPreview, line.commandPreview, line.stderrPreview, line.stdoutPreview, ].map((value) => String(value || "")).join("\n"); return /\b(error|failed|failure|interrupt|interrupted|cancell?ed|watchdog|timeout|closed|refused|aborted|exception)\b/iu.test(text); } function traceLineVisibleInTraceView(line: TranscriptLine): boolean { if (line.title === "Submitted prompt") return false; return line.kind !== "system" || traceSystemLineIsError(line); } function oaTraceStepKind(kind: string): TranscriptKind { const normalized = String(kind || "").trim().toLowerCase(); if (normalized === "read" || normalized === "explored" || normalized === "explore") return "explored"; if (normalized === "edit" || normalized === "edited") return "edited"; if (normalized === "run" || normalized === "ran" || normalized === "command") return "ran"; if (normalized === "error" || normalized === "failed") return "error"; if (normalized === "message" || normalized === "assistant" || normalized === "user") return "message"; return "system"; } function oaTraceStepToTranscriptLine(step: OaTraceStepSummary): TranscriptLine { const kind = oaTraceStepKind(step.kind); const summaryText = step.summaryLines.join("\n").trimEnd(); const commandText = summaryText.startsWith("item/") ? summaryText : ""; return { seq: step.seq, at: step.at, kind, title: step.title || (kind === "explored" ? "Read" : kind === "edited" ? "Edit" : kind === "ran" ? "Run" : "Trace step"), status: step.status || undefined, commandPreview: commandText.length > 0 ? commandText : undefined, bodyPreview: commandText.length > 0 ? undefined : summaryText || undefined, rawSeqs: step.rawSeqs, }; } function isCodexToolLifecycleTranscriptLine(line: TranscriptLine): boolean { const text = `${line.commandPreview ?? ""}\n${line.bodyPreview ?? ""}\n${line.title}`; const status = String(line.status || ""); return (line.kind === "explored" || line.kind === "ran") && (status === "item/started" || status === "item/completed" || /^item\/(?:started|completed):/u.test(text)) && /\b(?:webSearch|mcpToolCall|dynamicToolCall)\b/u.test(text); } function mergeCodexToolLifecycleGroup(group: TranscriptLine[]): TranscriptLine { if (group.length <= 1) return group[0]; const first = group[0]; const last = group.at(-1) || first; const rawSeqs: number[] = []; for (const line of group) { for (const seq of Array.isArray(line.rawSeqs) ? line.rawSeqs : [line.seq]) pushUniqueRawSeq(rawSeqs, Number(seq)); } const command = String(last.commandPreview || first.commandPreview || last.bodyPreview || first.bodyPreview || last.title || first.title || ""); return { ...first, seq: Number.isFinite(Number(last.seq)) ? Number(last.seq) : Number(first.seq), at: last.at || first.at, kind: commandKind(command), title: shortCommandTitle(command) || String(last.title || first.title || "WebSearch"), status: last.status || first.status, commandPreview: command || undefined, commandOmittedLines: Number(first.commandOmittedLines || 0) + Number(last.commandOmittedLines || 0) || undefined, bodyPreview: last.bodyPreview || first.bodyPreview, bodyOmittedLines: Number(first.bodyOmittedLines || 0) + Number(last.bodyOmittedLines || 0) || undefined, rawSeqs, }; } function coalesceCodexToolLifecycleTranscriptLines(lines: TranscriptLine[]): TranscriptLine[] { const rows = sortTranscript([...lines]); const merged: TranscriptLine[] = []; let group: TranscriptLine[] = []; const flush = () => { if (group.length > 0) merged.push(mergeCodexToolLifecycleGroup(group)); group = []; }; for (const line of rows) { if (isCodexToolLifecycleTranscriptLine(line)) { const text = String(line.commandPreview || line.bodyPreview || ""); if ((line.status === "item/started" || /^item\/started:/u.test(text)) && group.length > 0) flush(); group.push(line); if (line.status === "item/completed" || /^item\/completed:/u.test(text)) flush(); continue; } flush(); merged.push(line); } flush(); return merged; } async function oaTraceTranscriptForTask(task: QueueTask, attemptIndex: number | null): Promise { const taskId = task.id; const steps = await readOaTraceStepsForTask(taskId, attemptIndex); const oaLines = coalesceCodexToolLifecycleTranscriptLines(coalesceTranscriptMessageFragments(steps.map(oaTraceStepToTranscriptLine).filter(traceLineVisibleInTraceView))); const rawLines = coalesceCodexToolLifecycleTranscriptLines(fullTranscript(task).filter(traceLineVisibleInTraceView)); return coalesceCodexToolLifecycleTranscriptLines(overlayTraceMessagesFromRawTranscript(oaLines, rawLines)); } function mergeTraceWindowLines(left: TranscriptLine[], right: TranscriptLine[]): TranscriptLine[] { const seen = new Set(); const merged: TranscriptLine[] = []; for (const line of [...left, ...right]) { const key = `${line.seq}:${line.title}:${line.status ?? ""}`; if (seen.has(key)) continue; seen.add(key); merged.push(line); } return sortTranscript(merged); } function minNullableSeq(left: number | null, right: number | null): number | null { if (left === null) return right; if (right === null) return left; return Math.min(left, right); } function isSystemOnlyOrphanGroup(lines: TranscriptLine[]): boolean { const executionLines = executionLinesForAttempt(lines); return executionLines.length > 0 && executionLines.every((line) => line.kind === "system"); } function mergeSystemGroupIntoNextAttempt(windows: TraceAttemptWindow[], lines: TranscriptLine[]): boolean { if (!isSystemOnlyOrphanGroup(lines)) return false; const groupEndSeq = transcriptLineSeq(lines.at(-1)); if (groupEndSeq === null) return false; const target = windows .filter((window) => window.synthetic !== true && traceWindowFirstSeq(window) > groupEndSeq) .sort((left, right) => traceWindowFirstSeq(left) - traceWindowFirstSeq(right))[0]; if (target === undefined) return false; target.lines = mergeTraceWindowLines(lines, target.lines); target.startSeq = minNullableSeq(target.startSeq, transcriptLineSeq(lines[0])); return true; } function traceAttemptWindows(task: QueueTask, transcript: TranscriptLine[]): TraceAttemptWindow[] { const starts = transcript .map((line, position) => ({ line, position, index: line.title === "Attempt started" ? traceAttemptIndexFromLine(line) : null })) .filter((item): item is { line: TranscriptLine; position: number; index: number } => item.index !== null) .sort((left, right) => Number(left.line.seq) - Number(right.line.seq)); const maxStartIndex = starts.reduce((max, item) => Math.max(max, item.index), 0); const maxIndex = Math.max(task.attempts.length, task.currentAttempt || 0, maxStartIndex); const windows: TraceAttemptWindow[] = []; for (let index = 1; index <= maxIndex; index += 1) { const attempt = task.attempts.find((item) => Number(item.index) === index) ?? task.attempts[index - 1] ?? null; const start = starts.find((item) => item.index === index) ?? starts[index - 1] ?? null; const nextStart = starts.find((item) => item.index > index) ?? null; const explicitStartSeq = Number((attempt as AttemptSummary | null)?.outputStartSeq ?? NaN); const explicitEndSeq = Number((attempt as AttemptSummary | null)?.outputEndSeq ?? NaN); const startSeq = Number.isFinite(explicitStartSeq) ? explicitStartSeq : start?.line.seq ?? null; const hasExplicitEndSeq = Number.isFinite(explicitEndSeq); const endSeq = hasExplicitEndSeq ? explicitEndSeq : nextStart !== null ? nextStart.line.seq : null; let lines = startSeq === null ? [] : transcript.filter((line) => Number(line.seq) >= startSeq && (endSeq === null || (hasExplicitEndSeq ? Number(line.seq) <= endSeq : Number(line.seq) < endSeq))); if (lines.length === 0 && attempt !== null) { const startedMs = timestampMs(attempt.startedAt); const finishedMs = timestampMs(attempt.finishedAt); lines = transcript.filter((line) => { const lineMs = timestampMs(line.at); return lineMs !== null && (startedMs === null || lineMs >= startedMs) && (finishedMs === null || lineMs <= finishedMs); }); } windows.push({ index, attempt, startSeq, endSeq, lines }); } const coveredSeqs = new Set(); for (const window of windows) { for (const line of window.lines) coveredSeqs.add(Number(line.seq)); } const orphanedGroups: TranscriptLine[][] = []; let group: TranscriptLine[] = []; const flushGroup = (): void => { if (group.length > 0 && executionLinesForAttempt(group).length > 0) orphanedGroups.push(group); group = []; }; for (const line of transcript) { const seq = Number(line.seq); if (line.title === "Submitted prompt" || coveredSeqs.has(seq)) { flushGroup(); continue; } group.push(line); } flushGroup(); let syntheticIndex = -1; for (const lines of orphanedGroups) { if (mergeSystemGroupIntoNextAttempt(windows, lines)) continue; const hasSteer = lines.some((line) => line.title === "Steer prompt" || line.status === "turn/steer"); const hasRecovery = lines.some(isRecoveredThreadLine); const startSeq = Number(lines[0]?.seq ?? NaN); const endSeq = Number(lines.at(-1)?.seq ?? NaN); windows.push({ index: syntheticIndex, attempt: null, startSeq: Number.isFinite(startSeq) ? startSeq : null, endSeq: Number.isFinite(endSeq) ? endSeq : null, lines, synthetic: true, label: hasRecovery ? hasSteer ? "Recovered thread execution with steer prompt" : "Recovered thread execution" : hasSteer ? "Recovered thread execution with steer prompt" : "System events", }); syntheticIndex -= 1; } return windows.sort((left, right) => Number(left.lines[0]?.seq ?? left.startSeq ?? 0) - Number(right.lines[0]?.seq ?? right.startSeq ?? 0)); } function executionLinesForAttempt(lines: TranscriptLine[]): TranscriptLine[] { return lines.filter((line) => line.title !== "Submitted prompt" && line.title !== "Attempt started" && line.title !== "Judge result"); } function taskTraceAttemptSummaries(task: QueueTask, transcript: TranscriptLine[], oaTraceStatsByScope: TraceStatsLookup = null): JsonValue[] { const windows = traceAttemptWindows(task, transcript); return windows.map((window) => { const attempt = window.attempt; const parsedJudge = judgeFromAttemptLines(window.lines); const storedJudge = attempt?.judge ?? null; const synthetic = window.synthetic === true; const judge = synthetic ? null : storedJudge ?? parsedJudge?.judge ?? (window.index === task.attempts.length && task.lastJudge !== null ? task.lastJudge : null); const finalResponse = synthetic ? "" : String(attempt?.finalResponse ?? attempt?.finalResponsePreview ?? (window.index === task.attempts.length ? task.finalResponse : "")); const finalResponseChars = Number(attempt?.finalResponseChars ?? finalResponse.length); const executionLines = executionLinesForAttempt(window.lines); const attemptStats = synthetic || window.index <= 0 ? null : traceStatsForScope(oaTraceStatsByScope, taskAttemptScopeId(task.id, window.index)); const stepCount = traceStatsNumber(attemptStats, "stepCount"); const readCount = traceStatsNumber(attemptStats, "readCount"); const editCount = traceStatsNumber(attemptStats, "editCount"); const runCount = traceStatsNumber(attemptStats, "runCount"); const errorCount = traceStatsNumber(attemptStats, "errorCount"); const feedbackPrompt = synthetic ? null : attemptFeedbackPromptRecord(task, window.index, attempt, judge); const inputPrompt = promptSnapshot(String(attempt?.inputPrompt ?? ""), 1200); return { ...(attempt ?? {}), index: attempt?.index ?? window.index, synthetic, label: window.label ?? null, mode: attempt?.mode ?? (window.index <= 1 ? "initial" : "retry"), startedAt: attempt?.startedAt ?? window.lines[0]?.at ?? task.startedAt, finishedAt: attempt?.finishedAt ?? null, terminalStatus: attempt?.terminalStatus ?? null, transportClosedBeforeTerminal: attempt?.transportClosedBeforeTerminal ?? false, appServerExitCode: attempt?.appServerExitCode ?? null, appServerSignal: attempt?.appServerSignal ?? null, error: attempt?.error ?? null, stderrTail: attempt?.stderrTail ?? "", startSeq: window.startSeq, endSeq: window.endSeq, inputPrompt: undefined, inputPromptPreview: inputPrompt.preview, inputPromptChars: Number(attempt?.inputPromptChars ?? inputPrompt.chars), inputPromptLines: Number(attempt?.inputPromptLines ?? inputPrompt.lines), finalResponse, finalResponsePreview: attempt?.finalResponsePreview ?? safePreview(finalResponse, 3000), finalResponseChars, finalResponseTruncated: finalResponse.length < finalResponseChars, judge, judgeAt: attempt?.judgeAt ?? parsedJudge?.at ?? null, judgeSeq: attempt?.judgeSeq ?? parsedJudge?.seq ?? null, feedbackPrompt: undefined, feedbackPromptPreview: feedbackPrompt?.preview ?? "", feedbackPromptChars: feedbackPrompt?.chars ?? 0, feedbackPromptLines: feedbackPrompt?.lines ?? 0, feedbackPromptSource: feedbackPrompt?.source ?? null, feedbackPromptForAttempt: feedbackPrompt?.forAttempt ?? null, feedbackPromptTruncated: feedbackPrompt?.truncated ?? false, attemptScopeId: synthetic || window.index <= 0 ? null : taskAttemptScopeId(task.id, window.index), stepCount, readCount, editCount, runCount, errorCount, statsSource: attemptStats === null ? "unavailable" : "oa-event-flow", traceStats: attemptStats, execution: executionSummaryWithOaStats( executionSummaryFromTranscript( task, executionLines, attemptTimingSummary(attempt, executionLines.length > 0 ? executionLines : window.lines), window.lines.length, window.lines.length, ), attemptStats, ), }; }) as unknown as JsonValue[]; } function resolvedReferencePromptParts(prompt: string): { reference: string; userPrompt: string } { const withoutEnvironment = stripCodeQueueEnvironmentHint(prompt); const trimmed = withoutEnvironment.trimStart(); if (!trimmed.startsWith(resolvedReferenceContextTitle)) { return { reference: "", userPrompt: userPromptForDisplay(prompt) }; } const offset = withoutEnvironment.length - trimmed.length; const index = withoutEnvironment.lastIndexOf(currentTaskPromptMarker); if (index < offset) return { reference: "", userPrompt: userPromptForDisplay(prompt) }; return { reference: withoutEnvironment.slice(offset, index).trimEnd(), userPrompt: withoutEnvironment.slice(index + currentTaskPromptMarker.length).trimStart(), }; } function taskTracePromptSummary(task: QueueTask): JsonValue { const displayPrompt = task.basePrompt || userPromptForDisplay(task.prompt); const parts = resolvedReferencePromptParts(task.prompt); return { basePrompt: displayPrompt, basePromptChars: displayPrompt.length, basePromptLines: promptLineCount(displayPrompt), promptChars: task.prompt.length, promptLines: promptLineCount(task.prompt), referencePromptChars: parts.reference.length, referencePromptLines: promptLineCount(parts.reference), hasReferenceInjection: parts.reference.length > 0 || task.referenceInjection !== null, referenceTaskIds: task.referenceTaskIds, referenceInjectionSummary: task.referenceInjection === null ? null : { injectedAt: task.referenceInjection.injectedAt, itemCount: task.referenceInjection.itemCount, directReferenceTaskIds: task.referenceInjection.directReferenceTaskIds, maxRounds: task.referenceInjection.maxRounds, truncated: task.referenceInjection.truncated, }, } as unknown as JsonValue; } function traceStatsRecord(value: JsonValue | null | undefined): Record | null { return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record : null; } function traceStatsNumber(stats: Record | null, key: string): number | null { const value = Number(stats?.[key]); return Number.isFinite(value) && value >= 0 ? Math.floor(value) : null; } function taskAttemptScopeId(taskId: string, attemptIndex: number): string { return `task:${taskId}:attempt:${Math.floor(attemptIndex)}`; } function traceStatsForScope(lookup: TraceStatsLookup, scopeId: string): Record | null { if (lookup === null || lookup === undefined || scopeId.length === 0) return null; const value = lookup instanceof Map ? lookup.get(scopeId) : lookup[scopeId]; return traceStatsRecord(value); } function executionSummaryWithOaStats(execution: JsonValue, stats: Record | null): JsonValue { if (typeof execution !== "object" || execution === null || Array.isArray(execution)) return execution; const record = execution as Record; if (stats === null) { const { stepCount: _stepCount, llmStepCount: _llmStepCount, toolCallCount: _toolCallCount, readCount: _readCount, editCount: _editCount, runCount: _runCount, errorCount: _errorCount, traceLineCount: _traceLineCount, outputMaxSeq: _outputMaxSeq, transcriptMaxSeq: _transcriptMaxSeq, ...rest } = record; return { ...rest, statsSource: "unavailable", traceStats: null, statsUnavailable: true, } as unknown as JsonValue; } const stepCount = traceStatsNumber(stats, "stepCount"); const llmStepCount = traceStatsNumber(stats, "llmStepCount") ?? stepCount; const readCount = traceStatsNumber(stats, "readCount"); const editCount = traceStatsNumber(stats, "editCount"); const runCount = traceStatsNumber(stats, "runCount"); const errorCount = traceStatsNumber(stats, "errorCount"); const traceLineCount = traceStatsNumber(stats, "traceLineCount"); const outputMaxSeq = traceStatsNumber(stats, "outputMaxSeq"); const toolCallCount = readCount === null && editCount === null && runCount === null ? null : (readCount ?? 0) + (editCount ?? 0) + (runCount ?? 0); return { ...record, ...(stepCount === null ? {} : { stepCount }), ...(llmStepCount === null ? {} : { llmStepCount }), ...(toolCallCount === null ? {} : { toolCallCount }), ...(readCount === null ? {} : { readCount }), ...(editCount === null ? {} : { editCount }), ...(runCount === null ? {} : { runCount }), ...(errorCount === null ? {} : { errorCount }), ...(traceLineCount === null ? {} : { traceLineCount }), ...(outputMaxSeq === null ? {} : { outputMaxSeq, transcriptMaxSeq: outputMaxSeq }), statsSource: "oa-event-flow", traceStats: stats, } as unknown as JsonValue; } function taskTraceSummaryResponse(task: QueueTask, oaTraceStats: JsonValue | null = null, oaTraceStatsByScope: TraceStatsLookup = null): JsonValue { const transcript = cachedPreviewTranscript(task); const attempts = taskTraceAttemptSummaries(task, transcript, oaTraceStatsByScope); const stats = traceStatsRecord(oaTraceStats); const stepCount = traceStatsNumber(stats, "stepCount"); const llmStepCount = traceStatsNumber(stats, "llmStepCount") ?? stepCount; const errorCount = traceStatsNumber(stats, "errorCount"); const execution = executionSummaryWithOaStats(taskExecutionSummary(task, transcript), stats); return { id: task.id, queueId: ctx().queueIdOf(task), status: task.status, providerId: task.providerId, executionMode: task.executionMode, executionModeInfo: codeExecutionModeInfo(task.executionMode), model: task.model, agentPort: codeAgentPortForModel(task.model), agentPortInfo: codeAgentPortInfo(codeAgentPortForModel(task.model)), cwd: task.cwd, reasoningEffort: task.reasoningEffort, createdAt: task.createdAt, startedAt: task.startedAt, finishedAt: task.finishedAt, updatedAt: task.updatedAt, currentAttempt: task.currentAttempt, maxAttempts: task.maxAttempts, stepCount, llmStepCount, promptEditable: ctx().queuedTaskPromptEditable(task), prompt: taskTracePromptSummary(task), execution, traceStats: stats, statsSource: stats === null ? "unavailable" : "oa-event-flow", finalResponse: task.finalResponse, finalResponseChars: task.finalResponse.length, lastJudge: task.lastJudge, lastError: task.lastError, errorCount, attempts, timing: taskTiming(task), } as unknown as JsonValue; } function taskFeedbackPromptDetail(task: QueueTask, attemptIndex: number | null): FeedbackPromptRecord | null { const index = attemptIndex ?? (task.attempts.length > 0 ? task.attempts.length : task.currentAttempt || 1); if (!Number.isInteger(index) || index <= 0) return null; const attempt = task.attempts.find((item) => Number(item.index) === index) ?? task.attempts[index - 1] ?? null; let judge = attempt?.judge ?? null; if (judge === null) { const window = traceAttemptWindows(task, cachedPreviewTranscript(task)).find((item) => item.index === index) ?? null; judge = judgeFromAttemptLines(window?.lines ?? [])?.judge ?? null; } return attemptFeedbackPromptRecord(task, index, attempt, judge); } function taskPromptDetailResponse(task: QueueTask, url: URL): Response { const part = String(url.searchParams.get("part") || "full"); const parts = resolvedReferencePromptParts(task.prompt); const basePrompt = task.basePrompt || userPromptForDisplay(task.prompt); if (part === "feedback" || part === "judge-feedback") { const attemptIndex = ctx().parseSeqParam(url, "attempt", null); const detail = taskFeedbackPromptDetail(task, attemptIndex); const text = detail?.text ?? ""; return ctx().jsonResponse({ ok: true, taskId: task.id, queueId: ctx().queueIdOf(task), status: task.status, updatedAt: task.updatedAt, part: "feedback", attempt: attemptIndex, forAttempt: detail?.forAttempt ?? null, source: detail?.source ?? null, text, preview: detail?.preview ?? "", chars: detail?.chars ?? text.length, lines: detail?.lines ?? promptLineCount(text), truncated: detail?.truncated ?? false, }); } const text = part === "base" ? basePrompt : part === "reference" ? parts.reference : task.prompt; return ctx().jsonResponse({ ok: true, taskId: task.id, queueId: ctx().queueIdOf(task), status: task.status, updatedAt: task.updatedAt, part, text, chars: text.length, lines: promptLineCount(text), promptChars: task.prompt.length, basePromptChars: basePrompt.length, referencePromptChars: parts.reference.length, }); } async function taskTraceStepsResponse(task: QueueTask, url: URL): Promise { const limit = ctx().parseLimit(url); const attemptIndex = ctx().parseSeqParam(url, "attempt", null); const agentPort = codeAgentPortForModel(task.model); const transcript = await oaTraceTranscriptForTask(task, attemptIndex); const page = ctx().pageBySeq(transcript, url, limit); return ctx().jsonResponse({ ok: true, taskId: task.id, queueId: ctx().queueIdOf(task), status: task.status, updatedAt: task.updatedAt, agentPort, agentPortInfo: codeAgentPortInfo(agentPort), attempt: attemptIndex, source: "oa-event-flow", steps: page.chunk.map((line) => ({ seq: line.seq, at: line.at, kind: line.kind, title: line.title, status: line.status ?? null, durationMs: line.durationMs ?? null, rawSeqs: line.rawSeqs, summaryLines: transcriptLineSummaryLines(line), hasDetail: true, })), mode: page.mode, afterSeq: page.afterSeq, nextAfterSeq: page.nextAfterSeq, beforeSeq: page.beforeSeq, previousBeforeSeq: page.previousBeforeSeq, hasMore: page.hasMore, hasBefore: page.hasBefore, total: transcript.length, maxSeq: transcript.at(-1)?.seq ?? 0, }); } async function taskTraceStepDetailResponse(task: QueueTask, url: URL): Promise { const seq = Number(url.searchParams.get("seq")); if (!Number.isFinite(seq)) return ctx().jsonResponse({ ok: false, error: "seq is required" }, 400); const agentPort = codeAgentPortForModel(task.model); const transcript = await oaTraceTranscriptForTask(task, null); const line = transcript.find((item) => Number(item.seq) === seq || item.rawSeqs.includes(seq)); if (line === undefined) return ctx().jsonResponse({ ok: false, error: "trace step not found", seq }, 404); return ctx().jsonResponse({ ok: true, taskId: task.id, queueId: ctx().queueIdOf(task), status: task.status, updatedAt: task.updatedAt, agentPort, agentPortInfo: codeAgentPortInfo(agentPort), seq, line, }); } function taskSummaryResponse(task: QueueTask, url: URL): JsonValue { const transcript = fullTranscript(task); const fullOutput = taskFullOutput(task); return { id: task.id, queueId: ctx().queueIdOf(task), status: task.status, providerId: task.providerId, executionMode: task.executionMode, executionModeInfo: codeExecutionModeInfo(task.executionMode), model: task.model, agentPort: codeAgentPortForModel(task.model), agentPortInfo: codeAgentPortInfo(codeAgentPortForModel(task.model)), cwd: task.cwd, reasoningEffort: task.reasoningEffort, maxAttempts: task.maxAttempts, currentAttempt: task.currentAttempt, currentMode: task.currentMode, judgeFailCount: task.judgeFailCount, judgeFailRetryLimit, codexThreadId: task.codexThreadId, activeTurnId: task.activeTurnId, createdAt: task.createdAt, startedAt: task.startedAt, finishedAt: task.finishedAt, updatedAt: task.updatedAt, timing: taskTiming(task), initialPrompt: task.prompt, basePrompt: task.basePrompt, prompt: task.prompt, promptEditable: ctx().queuedTaskPromptEditable(task), referenceTaskIds: task.referenceTaskIds, referenceInjection: task.referenceInjection, lastAssistantMessage: lastAssistantMessage(task, transcript), toolSummary: taskToolSummary(task, url, transcript), attempts: task.attempts, lastJudge: task.lastJudge, lastError: task.lastError, cancelRequested: task.cancelRequested, transcriptCount: transcript.length, transcriptMaxSeq: transcript.at(-1)?.seq ?? 0, outputCount: fullOutput.length, retainedOutputCount: task.output.length, outputMaxSeq: fullOutput.at(-1)?.seq ?? 0, eventCount: task.events.length, cliHint: `bun scripts/cli.ts codex task ${task.id}`, traceHint: `bun scripts/cli.ts codex task ${task.id} --trace --tail --limit 80`, rawOutputHint: `bun scripts/cli.ts codex output ${task.id} --tail --limit 20`, } as unknown as JsonValue; } export { buildCompactTaskTranscript, buildTaskTranscript, cachedPreviewTranscript, codexToolLifecycleStartedBeforeIn, isCodexToolLifecycleOutput, formatCommandOutput, fullTranscript, lastAssistantMessage, promptLineCount, promptSnapshot, recordNumberField, recordStringField, safePreview, prefixPreview, setAttemptFeedbackPrompt, setAttemptInputPrompt, statsDaysFromUrl, taskExecutionSummary, taskListStepCount, taskOutputMaxSeq, taskForCompactMetaResponse, taskForMetaResponse, taskForResponse, taskStatisticsSummary, taskSummaryResponse, taskTiming, timestampMs, nonNegativeElapsed, taskToolSummary, taskTraceStepDetailResponse, taskTraceStepsResponse, taskTraceSummaryResponse, taskPromptDetailResponse, transcriptLineSummaryLines, };