726 lines · 21.6 KB · TypeScript
RawDownload
import type {  ThreadPendingApproval,  ThreadPendingUserInput,  ThreadUserInputQuestion,} from "@t3tools/client-runtime/state/thread-requests";import {  resolveT3McpToolPresentation,  type T3McpToolLogo,  type T3McpToolPresentation,} from "@t3tools/shared/t3McpToolPresentation";import type {  ChatAttachment,  MessageId,  OrchestrationV2Actor,  OrchestrationV2CreationSource,  OrchestrationV2ProjectedTurnItem,  OrchestrationV2RunStatus,  OrchestrationV2TurnItem,  OrchestrationV2UserMessageInputIntent,  RunId,  ThreadId,} from "@t3tools/contracts";import { formatDuration } from "@t3tools/shared/orchestrationTiming";import * as DateTime from "effect/DateTime"; export type PendingApproval = ThreadPendingApproval;export type PendingUserInput = ThreadPendingUserInput; const MAX_VISIBLE_WORK_LOG_ENTRIES = 1; export interface PendingUserInputDraftAnswer {  readonly selectedOptionLabel?: string;  readonly customAnswer?: string;} export interface ThreadFeedActivity {  readonly id: string;  readonly createdAt: string;  readonly runId: RunId | null;  readonly summary: string;  readonly detail: string | null;  readonly canExpand: boolean;  readonly getFullDetail: () => string | null;  readonly getCopyText: () => string;  readonly icon:    | "agent"    | "alert"    | "check"    | "command"    | "edit"    | "eye"    | "globe"    | "hammer"    | "message"    | "warning"    | "wrench"    | "zap";  readonly logo: T3McpToolLogo | null;  readonly toolLike: boolean;  readonly prominent: boolean;  readonly status: "success" | "failure" | "neutral" | null;  readonly projectedItem: OrchestrationV2ProjectedTurnItem;} export interface ThreadFeedMessage {  readonly id: MessageId;  readonly role: "user" | "assistant";  readonly text: string;  readonly attachments: ReadonlyArray<ChatAttachment>;  readonly runId: RunId | null;  readonly streaming: boolean;  readonly inputIntent?: OrchestrationV2UserMessageInputIntent;  readonly createdBy?: OrchestrationV2Actor;  readonly creationSource?: OrchestrationV2CreationSource;  readonly visibility: OrchestrationV2ProjectedTurnItem["visibility"];  readonly sourceThreadId: ThreadId;  readonly createdAt: string;  readonly updatedAt: string;  readonly projectedItem: OrchestrationV2ProjectedTurnItem;} type RawThreadFeedEntry =  | {      readonly type: "message";      readonly id: string;      readonly createdAt: string;      readonly message: ThreadFeedMessage;    }  | {      readonly type: "activity";      readonly id: string;      readonly createdAt: string;      readonly runId: RunId | null;      readonly activity: ThreadFeedActivity;    }; export type ThreadFeedEntry =  | Extract<RawThreadFeedEntry, { type: "message" }>  | {      readonly type: "working";      readonly id: string;      readonly createdAt: string;    }  | {      readonly type: "activity-group";      readonly id: string;      readonly createdAt: string;      readonly runId: RunId | null;      readonly activities: ReadonlyArray<ThreadFeedActivity>;    }  | {      readonly type: "work-toggle";      readonly id: string;      readonly createdAt: string;      readonly runId: RunId | null;      readonly groupId: string;      readonly hiddenCount: number;      readonly expanded: boolean;      readonly onlyToolActivities: boolean;    }  | {      readonly type: "run-fold";      readonly id: string;      readonly createdAt: string;      readonly runId: RunId;      readonly label: string;      readonly expanded: boolean;    }; export interface ThreadFeedLatestRun {  readonly runId: RunId;  readonly status: OrchestrationV2RunStatus;  readonly startedAt: string | null;  readonly completedAt: string | null;} function normalizeDraftAnswer(value: string | undefined): string | null {  if (typeof value !== "string") return null;  const trimmed = value.trim();  return trimmed.length > 0 ? trimmed : null;} function resolvePendingUserInputAnswer(  draft: PendingUserInputDraftAnswer | undefined,): string | null {  return (    normalizeDraftAnswer(draft?.customAnswer) ?? normalizeDraftAnswer(draft?.selectedOptionLabel)  );} function capitalizePhrase(value: string): string {  const trimmed = value.trim();  return trimmed.length === 0 ? value : `${trimmed.charAt(0).toUpperCase()}${trimmed.slice(1)}`;} function memoizeValue<T>(build: () => T): () => T {  let value: T;  let initialized = false;  return () => {    if (!initialized) {      value = build();      initialized = true;    }    return value;  };} function itemIsToolLike(item: OrchestrationV2TurnItem): boolean {  return (    item.type === "reasoning" ||    item.type === "command_execution" ||    item.type === "file_change" ||    item.type === "file_search" ||    item.type === "web_search" ||    item.type === "approval_request" ||    item.type === "user_input_request" ||    item.type === "dynamic_tool" ||    item.type === "subagent" ||    item.type === "error"  );} function itemIsProminent(item: OrchestrationV2TurnItem): boolean {  return item.type === "fork" || item.type === "thread_created" || item.type === "subagent";} function itemStatus(item: OrchestrationV2TurnItem): ThreadFeedActivity["status"] {  if (!itemIsToolLike(item)) return null;  if (item.type === "error" || item.status === "failed") return "failure";  return item.status === "completed" ? "success" : "neutral";} function itemIcon(item: OrchestrationV2TurnItem): ThreadFeedActivity["icon"] {  switch (item.type) {    case "reasoning":      return "agent";    case "command_execution":      return "command";    case "file_change":      return "edit";    case "file_search":      return "eye";    case "web_search":      return "globe";    case "approval_request":    case "user_input_request":    case "user_message":    case "assistant_message":      return "message";    case "dynamic_tool":      return "wrench";    case "subagent":      return "hammer";    case "run_interrupt_request":    case "run_interrupt_result":      return "warning";    case "error":      return "alert";    case "checkpoint":    case "proposed_plan":    case "todo_list":      return "check";    case "compaction":    case "handoff":    case "fork":    case "thread_created":      return "zap";  }} function itemToolPresentation(item: OrchestrationV2TurnItem): T3McpToolPresentation | null {  if (item.type !== "dynamic_tool") {    return null;  }  return resolveT3McpToolPresentation(item.toolName) ?? resolveT3McpToolPresentation(item.title);} function itemSummary(  item: OrchestrationV2TurnItem,  toolPresentation: T3McpToolPresentation | null = null,): string {  const title = item.title?.trim();  if (title) return toolPresentation?.displayName ?? capitalizePhrase(title);  switch (item.type) {    case "reasoning":      return "Thinking";    case "command_execution":      return "Command";    case "file_change":      return `Changed ${item.fileName}`;    case "file_search":      return "Searched files";    case "web_search":      return "Searched the web";    case "approval_request":      return "Approval requested";    case "user_input_request":      return "Input requested";    case "checkpoint":      return "Checkpoint captured";    case "run_interrupt_request":      return "Interrupt requested";    case "run_interrupt_result":      return "Run interrupted";    case "error":      return "Provider error";    case "compaction":      return "Context compacted";    case "handoff":      return "Context handed off";    case "fork":      return "Thread forked";    case "thread_created":      return "Thread created";    case "subagent":      return "Subagent";    case "dynamic_tool":      return toolPresentation?.displayName ?? item.toolName ?? "Tool call";    case "proposed_plan":      return "Proposed plan";    case "todo_list":      return "Plan updated";    case "user_message":      return "User message";    case "assistant_message":      return "Assistant message";  }} function itemPreview(item: OrchestrationV2TurnItem): string | null {  switch (item.type) {    case "reasoning":      return item.text || null;    case "command_execution":      return item.input || null;    case "file_change":      return item.fileName;    case "file_search":      return item.pattern ?? null;    case "web_search":      return item.patterns?.join(", ") ?? null;    case "approval_request":      return item.prompt ?? null;    case "user_input_request":      return item.questions.map((question) => question.question).join(" · ") || null;    case "checkpoint":      return item.files.length === 1        ? (item.files[0]?.path ?? null)        : `${item.files.length} changed files`;    case "run_interrupt_request":    case "run_interrupt_result":      return item.message || null;    case "error":      return item.failure.message;    case "compaction":    case "handoff":      return item.summary ?? null;    case "fork":    case "thread_created":      return item.targetThreadId;    case "subagent":      return item.result ?? item.progress ?? item.prompt;    case "dynamic_tool":      return null;    case "proposed_plan":      return item.markdown || null;    case "todo_list":      return `${item.steps.filter((step) => step.status === "completed").length}/${item.steps.length} completed`;    case "user_message":    case "assistant_message":      return item.text || null;  }} function toFeedActivity(row: OrchestrationV2ProjectedTurnItem): ThreadFeedActivity {  const item = row.item;  const toolPresentation = itemToolPresentation(item);  const summary = itemSummary(item, toolPresentation);  const detail = itemPreview(item);  const getFullDetail = memoizeValue(() =>    JSON.stringify(      {        visibility: row.visibility,        sourceThreadId: row.sourceThreadId,        sourceItemId: row.sourceItemId,        item,      },      null,      2,    ),  );  const getCopyText = memoizeValue(() =>    [summary, detail, getFullDetail()]      .filter(        (value, index, values): value is string =>          Boolean(value) && values.indexOf(value) === index,      )      .join("\n"),  );  return {    id: `${row.visibility}:${row.sourceThreadId}:${row.sourceItemId}`,    createdAt: DateTime.formatIso(item.startedAt ?? item.updatedAt),    runId: item.runId,    summary,    detail,    canExpand: true,    getFullDetail,    getCopyText,    icon: itemIcon(item),    logo: toolPresentation?.logo ?? null,    toolLike: itemIsToolLike(item),    prominent: itemIsProminent(item),    status: itemStatus(item),    projectedItem: row,  };} function isEmptyMessage(entry: RawThreadFeedEntry): boolean {  return (    entry.type === "message" &&    entry.message.text.trim().length === 0 &&    entry.message.attachments.length === 0  );} function groupAdjacentActivities(entries: ReadonlyArray<RawThreadFeedEntry>): ThreadFeedEntry[] {  const grouped: ThreadFeedEntry[] = [];  // Mutable backing array for the trailing group so appending an activity is  // O(1) instead of re-copying the group (which made this loop quadratic on  // long tool runs). The array is only mutated while it is the trailing group.  let openGroupActivities: ThreadFeedActivity[] | null = null;  let openGroupRunId: string | null = null;  let openGroupHasProminent = false;   for (const entry of entries) {    if (isEmptyMessage(entry)) continue;    if (entry.type !== "activity") {      grouped.push(entry);      openGroupActivities = null;      continue;    }     if (      openGroupActivities !== null &&      openGroupRunId === entry.runId &&      !entry.activity.prominent &&      !openGroupHasProminent    ) {      openGroupActivities.push(entry.activity);      continue;    }     openGroupActivities = [entry.activity];    openGroupRunId = entry.runId;    openGroupHasProminent = entry.activity.prominent === true;    grouped.push({      type: "activity-group",      id: entry.id,      createdAt: entry.createdAt,      runId: entry.runId,      activities: openGroupActivities,    });  }  return grouped;} function computeElapsedMs(startIso: string, endIso: string): number | null {  const start = Date.parse(startIso);  const end = Date.parse(endIso);  return Number.isFinite(start) && Number.isFinite(end) ? Math.max(0, end - start) : null;} function maxIsoTimestamp(left: string | null, right: string | null): string | null {  if (left === null) return right;  if (right === null) return left;  return Date.parse(right) > Date.parse(left) ? right : left;} function unsettledRunId(latestRun: ThreadFeedLatestRun | null): RunId | null {  return threadFeedRunIsUnsettled(latestRun) ? latestRun.runId : null;} export function threadFeedRunIsUnsettled(  run: ThreadFeedLatestRun | null,): run is ThreadFeedLatestRun {  return (    run !== null &&    (run.status === "preparing" ||      run.status === "starting" ||      run.status === "running" ||      run.status === "waiting")  );} interface ThreadFeedRunFold {  readonly runId: RunId;  readonly createdAt: string;  readonly hiddenEntryIds: ReadonlySet<string>;  readonly label: string;} function deriveThreadFeedRunFolds(  feed: ReadonlyArray<ThreadFeedEntry>,  latestRun: ThreadFeedLatestRun | null,): ReadonlyMap<string, ThreadFeedRunFold> {  const terminalAssistantMessageIdByRun = new Map<RunId, string>();  for (const entry of feed) {    if (entry.type === "message" && entry.message.role === "assistant" && entry.message.runId) {      terminalAssistantMessageIdByRun.set(entry.message.runId, entry.id);    }  }   const groupsByRunId = new Map<    RunId,    { entries: ThreadFeedEntry[]; startBoundary: string | null }  >();  let pendingUserBoundary: string | null = null;  for (const entry of feed) {    if (entry.type === "message" && entry.message.role === "user") {      pendingUserBoundary = entry.message.createdAt;      continue;    }    const runId =      entry.type === "message" && entry.message.role === "assistant"        ? entry.message.runId        : entry.type === "activity-group"          ? entry.runId          : null;    if (!runId) continue;    let group = groupsByRunId.get(runId);    if (!group) {      group = { entries: [], startBoundary: pendingUserBoundary };      pendingUserBoundary = null;      groupsByRunId.set(runId, group);    }    group.entries.push(entry);  }   const activeRunId = unsettledRunId(latestRun);  const foldsByAnchorId = new Map<string, ThreadFeedRunFold>();

Lines 1–500 of 726.