import { projectQuestionToolInput } from "@t3tools/shared/toolActivity";import type { OrchestrationEvent, OrchestrationThreadActivity, OrchestrationThreadDetailSnapshot,} from "@t3tools/contracts";import { isWorkspaceImagePreviewPath } from "@t3tools/shared/filePreview";import { extractJsonObject } from "@t3tools/shared/schemaJson"; function asRecord(value: unknown): Record<string, unknown> | null { return value !== null && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : null;} function asTrimmedString(value: unknown): string | null { if (typeof value !== "string") { return null; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null;} function pushChangedFile(target: string[], seen: Set<string>, value: unknown): void { const normalized = asTrimmedString(value); if (!normalized || seen.has(normalized)) { return; } seen.add(normalized); target.push(normalized);} function collectChangedFiles( value: unknown, target: string[], seen: Set<string>, depth: number,): void { if (depth > 4 || target.length >= 12) { return; } if (Array.isArray(value)) { for (const entry of value) { collectChangedFiles(entry, target, seen, depth + 1); if (target.length >= 12) { return; } } return; } const record = asRecord(value); if (!record) { return; } pushChangedFile(target, seen, record.path); pushChangedFile(target, seen, record.filePath); pushChangedFile(target, seen, record.relativePath); pushChangedFile(target, seen, record.filename); pushChangedFile(target, seen, record.newPath); pushChangedFile(target, seen, record.oldPath); for (const nestedKey of [ "item", "result", "input", "data", "changes", "files", "edits", "patch", "patches", "operations", ]) { if (!(nestedKey in record)) { continue; } collectChangedFiles(record[nestedKey], target, seen, depth + 1); if (target.length >= 12) { return; } }} function projectCommandData(data: Record<string, unknown>): Record<string, unknown> | undefined { const item = asRecord(data.item); if (!item) { return undefined; } const projectedItem: Record<string, unknown> = {}; if ("command" in item) { projectedItem.command = item.command; } const aggregatedOutput = asTrimmedString(item.aggregatedOutput); if (aggregatedOutput) { const summary = summarizeToolTextOutput(aggregatedOutput); if (summary) { projectedItem.aggregatedOutput = summary; } } const input = asRecord(item.input); if (input && "command" in input) { projectedItem.input = { command: input.command }; } const result = asRecord(item.result); if (result) { const projectedResult: Record<string, unknown> = {}; if ("command" in result) { projectedResult.command = result.command; } const content = asTrimmedString(result.content); if (content) { const summary = summarizeToolTextOutput(content); if (summary) { projectedResult.content = summary; } } if (Object.keys(projectedResult).length > 0) { projectedItem.result = projectedResult; } } return Object.keys(projectedItem).length > 0 ? projectedItem : undefined;} function projectCommandValue(data: Record<string, unknown>): unknown { if (data.command !== undefined) { return data.command; } const input = asRecord(data.input); if (input?.command !== undefined) { return input.command; } const stateInput = asRecord(asRecord(data.state)?.input); if (stateInput?.command !== undefined) { return stateInput.command; } return undefined;} function projectViewedImagePath(data: Record<string, unknown>): string | undefined { const directPath = asTrimmedString(data.imagePath); if (directPath && isWorkspaceImagePreviewPath(directPath)) { return directPath; } const toolName = asTrimmedString(data.toolName)?.toLowerCase(); if (toolName !== "read" && toolName !== "read file") { return undefined; } const input = asRecord(data.input); const inputPath = asTrimmedString(input?.file_path) ?? asTrimmedString(input?.path); return inputPath && isWorkspaceImagePreviewPath(inputPath) ? inputPath : undefined;} function summarizeToolTextOutput(value: string): string | null { let meaningfulLineCount = 0; let offset = 0; while (offset <= value.length) { const newlineIndex = value.indexOf("\n", offset); const lineEnd = newlineIndex === -1 ? value.length : newlineIndex; const line = value.slice(offset, lineEnd).replace(/\s+/g, " ").trim(); if (line.length > 0) { meaningfulLineCount += 1; if (line !== "```") { const summary = line.length <= 84 ? line : `${line.slice(0, 83).trimEnd()}…`; // V8 can retain the full tool output behind a short sliced string. // Join a tiny character array so the returned preview owns its bytes. return Array.from(summary).join(""); } } if (newlineIndex === -1) { break; } offset = newlineIndex + 1; } return meaningfulLineCount > 1 ? `${meaningfulLineCount.toLocaleString()} lines` : null;} /** * Fields of an MCP tool-call item both clients render in the expanded * work-log row. Everything else — notably `result`, which carries the full * tool output and dominates wire size on MCP-heavy threads — is summarized * or dropped. Full payloads remain in persistence. */const MCP_ITEM_KEPT_FIELDS = [ "type", "id", "tool", "server", "status", "arguments", "appContext", "error", "durationMs",] as const; /** * Pulls renderable text out of an MCP tool result: either a Codex-style * `{content: [{type: "text", text}, ...]}` record or a raw Claude * `tool_result` block whose `content` is a string or block array. */function extractMcpResultText(result: unknown): string | null { const record = asRecord(result); if (!record) { return typeof result === "string" ? result : null; } if (typeof record.content === "string") { return record.content; } if (Array.isArray(record.content)) { const texts: string[] = []; for (const entry of record.content) { const text = asRecord(entry)?.text; if (typeof text === "string" && text.trim().length > 0) { texts.push(text); } } if (texts.length > 0) { return texts.join("\n"); } } return null;} function summarizeMcpResult(result: unknown): Record<string, unknown> | undefined { if (result === undefined || result === null) { return undefined; } const text = extractMcpResultText(result); const summary = text ? summarizeToolTextOutput(text) : null; return summary ? { content: summary } : undefined;} /** Reuse the page URL already returned by preview tools before slimming their output. */function projectPreviewToolMetadata(data: Record<string, unknown>, status: unknown) { const item = asRecord(data.item); const name = item ? `mcp__${item.server}__${item.tool}` : (data.toolName ?? data.tool); if ( typeof name !== "string" || !/^(?:mcp__)?(?:t3-code|t3_code|t3code)_{1,2}preview_(?:open|navigate|status|snapshot|click|type|press|scroll|resize|set_appearance|evaluate|wait_for|recording_start|recording_stop)$/.test( name, ) ) return {}; const state = asRecord(data.state); const result = item?.result ?? data.result ?? state?.output; const record = asRecord(result); if ( status === "failed" || status === "declined" || state?.status === "error" || item?.error != null || record?.isError === true || record?.is_error === true ) return {}; let page = record; let output: unknown = result; for (let depth = 0; depth < 3; depth += 1) { if (page?.isError === true || page?.is_error === true) return {}; const structured = asRecord(page?.structuredContent); if (structured) { page = structured; break; } const text = extractMcpResultText(output)?.slice(0, 2 * 1024 * 1024); if (!text) break; try { page = asRecord(JSON.parse(extractJsonObject(text))); } catch { // A truncated MCP envelope can still contain a complete first text block. const firstBlock = /^\s*\{\s*"content"\s*:\s*\[\s*/.exec(text); if (!firstBlock) return {}; try { const block = asRecord(JSON.parse(extractJsonObject(text.slice(firstBlock[0].length)))); page = block?.type === "text" ? { content: [block] } : null; } catch { return {}; } } output = page; } const rawUrl = asTrimmedString( asRecord(page?.toolIcon)?.pageUrl ?? (/preview_(?:open|navigate|status|snapshot)$/.test(name) ? page?.url : undefined), ); if (!rawUrl || rawUrl.length > 4096) return {}; try { const url = new URL(rawUrl); if (url.protocol !== "http:" && url.protocol !== "https:") return {}; return { toolIcon: { _tag: "website", pageUrl: url.href } }; } catch { return {}; }} /** * MCP tool calls carry full tool results (`data.item.result` on Codex, * `data.result` on Claude/OpenCode) that used to bypass slimming entirely to * keep the expanded-row UI working. Keep the fields the UI actually renders * and summarize the result like regular tool output. */function projectMcpToolCallData(data: Record<string, unknown>): Record<string, unknown> { const projectedData: Record<string, unknown> = {}; const item = asRecord(data.item); if (item) { const projectedItem: Record<string, unknown> = {}; for (const key of MCP_ITEM_KEPT_FIELDS) { if (key in item) { projectedItem[key] = item[key]; } } const result = summarizeMcpResult(item.result); if (result) { projectedItem.result = result; } projectedData.item = projectedItem; } if ("toolName" in data) { projectedData.toolName = data.toolName; } if ("input" in data) { projectedData.input = data.input; } if (!item) { const result = summarizeMcpResult(data.result); if (result) { projectedData.result = result; } } if ("toolCallId" in data) { projectedData.toolCallId = data.toolCallId; } if ("kind" in data) { projectedData.kind = data.kind; } const changedFiles: string[] = []; collectChangedFiles(data, changedFiles, new Set<string>(), 0); if (changedFiles.length > 0) { projectedData.files = changedFiles.map((path) => ({ path })); } return projectedData;} function projectRawOutput(value: unknown): Record<string, unknown> | undefined { const direct = asTrimmedString(value); if (direct) { const summary = summarizeToolTextOutput(direct); return summary ? { content: summary } : undefined; } const rawOutput = asRecord(value); if (!rawOutput) { return undefined; } if (typeof rawOutput.totalFiles === "number" && Number.isFinite(rawOutput.totalFiles)) { return { totalFiles: rawOutput.totalFiles, ...(rawOutput.truncated === true ? { truncated: true } : {}), }; } const content = asTrimmedString(rawOutput.content); if (content) { const summary = summarizeToolTextOutput(content); return summary ? { content: summary } : undefined; } const stdout = asTrimmedString(rawOutput.stdout); if (stdout) { const summary = summarizeToolTextOutput(stdout); return summary ? { content: summary } : undefined; } const stderr = asTrimmedString(rawOutput.stderr); if (stderr) { const summary = summarizeToolTextOutput(stderr); return summary ? { content: summary } : undefined; } return undefined;} function projectAcpContent(value: unknown): Record<string, unknown> | undefined { if (!Array.isArray(value)) { return undefined; } const text = value .map((entryValue) => { const entry = asRecord(entryValue); const content = asRecord(entry?.content); return entry?.type === "content" && content?.type === "text" ? asTrimmedString(content.text) : null; }) .filter((entry): entry is string => entry !== null) .join("\n"); const summary = summarizeToolTextOutput(text); return summary ? { content: summary } : undefined;} /** * Removes activity payload fields that no current client reads while retaining * the full payload in persistence and the event store. */export function projectActivityPayload( activity: OrchestrationThreadActivity,): OrchestrationThreadActivity { const payload = asRecord(activity.payload); const data = asRecord(payload?.data); if (!payload || !data) { return activity; } const itemStatus = asRecord(data.item)?.status; const statusPayload = payload.status === "completed" && (itemStatus === "failed" || itemStatus === "declined") ? { ...payload, status: itemStatus } : payload; const projectedPayload = { ...projectPreviewToolMetadata(data, statusPayload.status), ...statusPayload, }; const questionInput = projectQuestionToolInput(data, payload.title); if (payload.itemType === "mcp_tool_call") { return { ...activity, payload: { ...projectedPayload, data: { ...projectMcpToolCallData(data), ...questionInput }, }, }; } const projectedData: Record<string, unknown> = { ...questionInput }; const item = projectCommandData(data); if (item) { projectedData.item = item; } const command = projectCommandValue(data); if (command !== undefined) { projectedData.command = command; } const imagePath = projectViewedImagePath(data); if (imagePath) { projectedData.imagePath = imagePath; } const changedFiles: string[] = []; collectChangedFiles(data, changedFiles, new Set<string>(), 0); if (changedFiles.length > 0) { // Both clients discover file names by walking objects with path-like keys. projectedData.files = changedFiles.map((path) => ({ path })); } if ("toolCallId" in data) { projectedData.toolCallId = data.toolCallId; } if ("kind" in data) { projectedData.kind = data.kind; } if ("toolName" in data) { projectedData.toolName = data.toolName; } const rawOutput = projectRawOutput(data.rawOutput) ?? projectAcpContent(data.content) ?? (payload.itemType === "command_execution" ? summarizeMcpResult(data.result) : undefined); if (rawOutput) { projectedData.rawOutput = rawOutput; } return { ...activity, payload: { ...projectedPayload, data: projectedData, }, };Lines 1–500 of 671. Load more
import { projectQuestionToolInput } from "@t3tools/shared/toolActivity"; import type { OrchestrationEvent, OrchestrationThreadActivity, OrchestrationThreadDetailSnapshot, } from "@t3tools/contracts"; import { isWorkspaceImagePreviewPath } from "@t3tools/shared/filePreview"; import { extractJsonObject } from "@t3tools/shared/schemaJson"; function asRecord(value: unknown): Record<string, unknown> | null { return value !== null && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : null; } function asTrimmedString(value: unknown): string | null { if (typeof value !== "string") { return null; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; } function pushChangedFile(target: string[], seen: Set<string>, value: unknown): void { const normalized = asTrimmedString(value); if (!normalized || seen.has(normalized)) { return; } seen.add(normalized); target.push(normalized); } function collectChangedFiles( value: unknown, target: string[], seen: Set<string>, depth: number, ): void { if (depth > 4 || target.length >= 12) { return; } if (Array.isArray(value)) { for (const entry of value) { collectChangedFiles(entry, target, seen, depth + 1); if (target.length >= 12) { return; } } return; } const record = asRecord(value); if (!record) { return; } pushChangedFile(target, seen, record.path); pushChangedFile(target, seen, record.filePath); pushChangedFile(target, seen, record.relativePath); pushChangedFile(target, seen, record.filename); pushChangedFile(target, seen, record.newPath); pushChangedFile(target, seen, record.oldPath); for (const nestedKey of [ "item", "result", "input", "data", "changes", "files", "edits", "patch", "patches", "operations", ]) { if (!(nestedKey in record)) { continue; } collectChangedFiles(record[nestedKey], target, seen, depth + 1); if (target.length >= 12) { return; } } } function projectCommandData(data: Record<string, unknown>): Record<string, unknown> | undefined { const item = asRecord(data.item); if (!item) { return undefined; } const projectedItem: Record<string, unknown> = {}; if ("command" in item) { projectedItem.command = item.command; } const aggregatedOutput = asTrimmedString(item.aggregatedOutput); if (aggregatedOutput) { const summary = summarizeToolTextOutput(aggregatedOutput); if (summary) { projectedItem.aggregatedOutput = summary; } } const input = asRecord(item.input); if (input && "command" in input) { projectedItem.input = { command: input.command }; } const result = asRecord(item.result); if (result) { const projectedResult: Record<string, unknown> = {}; if ("command" in result) { projectedResult.command = result.command; } const content = asTrimmedString(result.content); if (content) { const summary = summarizeToolTextOutput(content); if (summary) { projectedResult.content = summary; } } if (Object.keys(projectedResult).length > 0) { projectedItem.result = projectedResult; } } return Object.keys(projectedItem).length > 0 ? projectedItem : undefined; } function projectCommandValue(data: Record<string, unknown>): unknown { if (data.command !== undefined) { return data.command; } const input = asRecord(data.input); if (input?.command !== undefined) { return input.command; } const stateInput = asRecord(asRecord(data.state)?.input); if (stateInput?.command !== undefined) { return stateInput.command; } return undefined; } function projectViewedImagePath(data: Record<string, unknown>): string | undefined { const directPath = asTrimmedString(data.imagePath); if (directPath && isWorkspaceImagePreviewPath(directPath)) { return directPath; } const toolName = asTrimmedString(data.toolName)?.toLowerCase(); if (toolName !== "read" && toolName !== "read file") { return undefined; } const input = asRecord(data.input); const inputPath = asTrimmedString(input?.file_path) ?? asTrimmedString(input?.path); return inputPath && isWorkspaceImagePreviewPath(inputPath) ? inputPath : undefined; } function summarizeToolTextOutput(value: string): string | null { let meaningfulLineCount = 0; let offset = 0; while (offset <= value.length) { const newlineIndex = value.indexOf("\n", offset); const lineEnd = newlineIndex === -1 ? value.length : newlineIndex; const line = value.slice(offset, lineEnd).replace(/\s+/g, " ").trim(); if (line.length > 0) { meaningfulLineCount += 1; if (line !== "```") { const summary = line.length <= 84 ? line : `${line.slice(0, 83).trimEnd()}…`; // V8 can retain the full tool output behind a short sliced string. // Join a tiny character array so the returned preview owns its bytes. return Array.from(summary).join(""); } } if (newlineIndex === -1) { break; } offset = newlineIndex + 1; } return meaningfulLineCount > 1 ? `${meaningfulLineCount.toLocaleString()} lines` : null; } /** * Fields of an MCP tool-call item both clients render in the expanded * work-log row. Everything else — notably `result`, which carries the full * tool output and dominates wire size on MCP-heavy threads — is summarized * or dropped. Full payloads remain in persistence. */ const MCP_ITEM_KEPT_FIELDS = [ "type", "id", "tool", "server", "status", "arguments", "appContext", "error", "durationMs", ] as const; /** * Pulls renderable text out of an MCP tool result: either a Codex-style * `{content: [{type: "text", text}, ...]}` record or a raw Claude * `tool_result` block whose `content` is a string or block array. */ function extractMcpResultText(result: unknown): string | null { const record = asRecord(result); if (!record) { return typeof result === "string" ? result : null; } if (typeof record.content === "string") { return record.content; } if (Array.isArray(record.content)) { const texts: string[] = []; for (const entry of record.content) { const text = asRecord(entry)?.text; if (typeof text === "string" && text.trim().length > 0) { texts.push(text); } } if (texts.length > 0) { return texts.join("\n"); } } return null; } function summarizeMcpResult(result: unknown): Record<string, unknown> | undefined { if (result === undefined || result === null) { return undefined; } const text = extractMcpResultText(result); const summary = text ? summarizeToolTextOutput(text) : null; return summary ? { content: summary } : undefined; } /** Reuse the page URL already returned by preview tools before slimming their output. */ function projectPreviewToolMetadata(data: Record<string, unknown>, status: unknown) { const item = asRecord(data.item); const name = item ? `mcp__${item.server}__${item.tool}` : (data.toolName ?? data.tool); if ( typeof name !== "string" || !/^(?:mcp__)?(?:t3-code|t3_code|t3code)_{1,2}preview_(?:open|navigate|status|snapshot|click|type|press|scroll|resize|set_appearance|evaluate|wait_for|recording_start|recording_stop)$/.test( name, ) ) return {}; const state = asRecord(data.state); const result = item?.result ?? data.result ?? state?.output; const record = asRecord(result); if ( status === "failed" || status === "declined" || state?.status === "error" || item?.error != null || record?.isError === true || record?.is_error === true ) return {}; let page = record; let output: unknown = result; for (let depth = 0; depth < 3; depth += 1) { if (page?.isError === true || page?.is_error === true) return {}; const structured = asRecord(page?.structuredContent); if (structured) { page = structured; break; } const text = extractMcpResultText(output)?.slice(0, 2 * 1024 * 1024); if (!text) break; try { page = asRecord(JSON.parse(extractJsonObject(text))); } catch { // A truncated MCP envelope can still contain a complete first text block. const firstBlock = /^\s*\{\s*"content"\s*:\s*\[\s*/.exec(text); if (!firstBlock) return {}; try { const block = asRecord(JSON.parse(extractJsonObject(text.slice(firstBlock[0].length)))); page = block?.type === "text" ? { content: [block] } : null; } catch { return {}; } } output = page; } const rawUrl = asTrimmedString( asRecord(page?.toolIcon)?.pageUrl ?? (/preview_(?:open|navigate|status|snapshot)$/.test(name) ? page?.url : undefined), ); if (!rawUrl || rawUrl.length > 4096) return {}; try { const url = new URL(rawUrl); if (url.protocol !== "http:" && url.protocol !== "https:") return {}; return { toolIcon: { _tag: "website", pageUrl: url.href } }; } catch { return {}; } } /** * MCP tool calls carry full tool results (`data.item.result` on Codex, * `data.result` on Claude/OpenCode) that used to bypass slimming entirely to * keep the expanded-row UI working. Keep the fields the UI actually renders * and summarize the result like regular tool output. */ function projectMcpToolCallData(data: Record<string, unknown>): Record<string, unknown> { const projectedData: Record<string, unknown> = {}; const item = asRecord(data.item); if (item) { const projectedItem: Record<string, unknown> = {}; for (const key of MCP_ITEM_KEPT_FIELDS) { if (key in item) { projectedItem[key] = item[key]; } } const result = summarizeMcpResult(item.result); if (result) { projectedItem.result = result; } projectedData.item = projectedItem; } if ("toolName" in data) { projectedData.toolName = data.toolName; } if ("input" in data) { projectedData.input = data.input; } if (!item) { const result = summarizeMcpResult(data.result); if (result) { projectedData.result = result; } } if ("toolCallId" in data) { projectedData.toolCallId = data.toolCallId; } if ("kind" in data) { projectedData.kind = data.kind; } const changedFiles: string[] = []; collectChangedFiles(data, changedFiles, new Set<string>(), 0); if (changedFiles.length > 0) { projectedData.files = changedFiles.map((path) => ({ path })); } return projectedData; } function projectRawOutput(value: unknown): Record<string, unknown> | undefined { const direct = asTrimmedString(value); if (direct) { const summary = summarizeToolTextOutput(direct); return summary ? { content: summary } : undefined; } const rawOutput = asRecord(value); if (!rawOutput) { return undefined; } if (typeof rawOutput.totalFiles === "number" && Number.isFinite(rawOutput.totalFiles)) { return { totalFiles: rawOutput.totalFiles, ...(rawOutput.truncated === true ? { truncated: true } : {}), }; } const content = asTrimmedString(rawOutput.content); if (content) { const summary = summarizeToolTextOutput(content); return summary ? { content: summary } : undefined; } const stdout = asTrimmedString(rawOutput.stdout); if (stdout) { const summary = summarizeToolTextOutput(stdout); return summary ? { content: summary } : undefined; } const stderr = asTrimmedString(rawOutput.stderr); if (stderr) { const summary = summarizeToolTextOutput(stderr); return summary ? { content: summary } : undefined; } return undefined; } function projectAcpContent(value: unknown): Record<string, unknown> | undefined { if (!Array.isArray(value)) { return undefined; } const text = value .map((entryValue) => { const entry = asRecord(entryValue); const content = asRecord(entry?.content); return entry?.type === "content" && content?.type === "text" ? asTrimmedString(content.text) : null; }) .filter((entry): entry is string => entry !== null) .join("\n"); const summary = summarizeToolTextOutput(text); return summary ? { content: summary } : undefined; } /** * Removes activity payload fields that no current client reads while retaining * the full payload in persistence and the event store. */ export function projectActivityPayload( activity: OrchestrationThreadActivity, ): OrchestrationThreadActivity { const payload = asRecord(activity.payload); const data = asRecord(payload?.data); if (!payload || !data) { return activity; } const itemStatus = asRecord(data.item)?.status; const statusPayload = payload.status === "completed" && (itemStatus === "failed" || itemStatus === "declined") ? { ...payload, status: itemStatus } : payload; const projectedPayload = { ...projectPreviewToolMetadata(data, statusPayload.status), ...statusPayload, }; const questionInput = projectQuestionToolInput(data, payload.title); if (payload.itemType === "mcp_tool_call") { return { ...activity, payload: { ...projectedPayload, data: { ...projectMcpToolCallData(data), ...questionInput }, }, }; } const projectedData: Record<string, unknown> = { ...questionInput }; const item = projectCommandData(data); if (item) { projectedData.item = item; } const command = projectCommandValue(data); if (command !== undefined) { projectedData.command = command; } const imagePath = projectViewedImagePath(data); if (imagePath) { projectedData.imagePath = imagePath; } const changedFiles: string[] = []; collectChangedFiles(data, changedFiles, new Set<string>(), 0); if (changedFiles.length > 0) { // Both clients discover file names by walking objects with path-like keys. projectedData.files = changedFiles.map((path) => ({ path })); } if ("toolCallId" in data) { projectedData.toolCallId = data.toolCallId; } if ("kind" in data) { projectedData.kind = data.kind; } if ("toolName" in data) { projectedData.toolName = data.toolName; } const rawOutput = projectRawOutput(data.rawOutput) ?? projectAcpContent(data.content) ?? (payload.itemType === "command_execution" ? summarizeMcpResult(data.result) : undefined); if (rawOutput) { projectedData.rawOutput = rawOutput; } return { ...activity, payload: { ...projectedPayload, data: projectedData, }, };