600 lines · 18.3 KB · TypeScript
RawDownload
import type {  OrchestrationEvent,  OrchestrationThreadActivity,  OrchestrationThreadDetailSnapshot,} from "@t3tools/contracts";import { isWorkspaceImagePreviewPath } from "@t3tools/shared/filePreview"; 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;} /** * 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 projectedPayload =    payload.status === "completed" && (itemStatus === "failed" || itemStatus === "declined")      ? { ...payload, status: itemStatus }      : payload;   if (payload.itemType === "mcp_tool_call") {    return {      ...activity,      payload: {        ...projectedPayload,        data: projectMcpToolCallData(data),      },    };  }   const projectedData: Record<string, unknown> = {};  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,    },  };} /** * Matches the validity rule in the web client's * `deriveLatestContextWindowSnapshot`: rows without a finite, non-negative * `usedTokens` are skipped during its backward walk, so they must not shadow * an earlier resolvable row here. */function isResolvableContextWindowActivity(activity: OrchestrationThreadActivity): boolean {  if (activity.kind !== "context-window.updated") {    return false;  }  const payload = asRecord(activity.payload);  const usedTokens = payload?.usedTokens;  return typeof usedTokens === "number" && Number.isFinite(usedTokens) && usedTokens >= 0;} /** * Drops all but the last resolvable context-window activity per turn from a * snapshot. Clients only ever read the latest usage value (walking the array * backwards), so shipping the full history — often thousands of rows on long * threads — buys nothing. Retention is per turn rather than per thread because * a live `thread.reverted` makes the client discard whole turns; keeping each * turn's latest row means the meter can still resolve a value from the turns * that survive. Malformed rows pass through untouched rather than shadowing a * valid earlier row. Live `thread.activity-appended` events are untouched: * newer updates still stream through and supersede the retained rows on the * client. */function dropStaleContextWindowActivities(  activities: ReadonlyArray<OrchestrationThreadActivity>,): ReadonlyArray<OrchestrationThreadActivity> {  const latestIndexByTurn = new Map<string | null, number>();  for (let index = 0; index < activities.length; index += 1) {    if (isResolvableContextWindowActivity(activities[index]!)) {      latestIndexByTurn.set(activities[index]!.turnId, index);    }  }  if (latestIndexByTurn.size === 0) {    return activities;  }  return activities.filter(    (activity, index) =>      !isResolvableContextWindowActivity(activity) ||      latestIndexByTurn.get(activity.turnId) === index,  );} /** * Identity used to retain only the newest lifecycle row for each call in a * thread snapshot. Prefer the runtime item id, then the legacy nested id, and * finally the itemType/title/detail triple. Rows without any identity remain * untouched. */function toolLifecycleIdentity(activity: OrchestrationThreadActivity): string | null {  const payload = asRecord(activity.payload);  if (!payload) {    return null;  }   const toolCallId =    asTrimmedString(payload.toolCallId) ?? asTrimmedString(asRecord(payload.data)?.toolCallId);  if (toolCallId) {    return `id:${toolCallId}`;  }   const itemType = asTrimmedString(payload.itemType) ?? "";  // Mirrors the clients' `normalizeCompactToolLabel`: a completion's title may  // gain a trailing "complete"/"completed" the in-flight updates lack.  const label = (asTrimmedString(payload.title) ?? activity.summary)    .replace(/\s+(?:complete|completed)\s*$/iu, "")

Lines 1–500 of 600.