621 lines · 17.8 KB · TypeScript
RawDownload
import {  ApprovalRequestId,  type OrchestrationLatestTurn,  type OrchestrationThreadActivity,  type OrchestrationProposedPlanId,  type ProviderKind,  type UserInputQuestion,  type TurnId,} from "@t3tools/contracts"; import type { ChatMessage, ProposedPlan, SessionPhase, ThreadSession, TurnDiffSummary } from "./types"; export type ProviderPickerKind = ProviderKind | "claudeCode" | "cursor"; export const PROVIDER_OPTIONS: Array<{  value: ProviderPickerKind;  label: string;  available: boolean;}> = [  { value: "codex", label: "Codex", available: true },  { value: "claudeCode", label: "Claude Code", available: false },  { value: "cursor", label: "Cursor", available: false },]; export interface WorkLogEntry {  id: string;  createdAt: string;  label: string;  detail?: string;  command?: string;  changedFiles?: ReadonlyArray<string>;  tone: "thinking" | "tool" | "info" | "error";} export interface PendingApproval {  requestId: ApprovalRequestId;  requestKind: "command" | "file-read" | "file-change";  createdAt: string;  detail?: string;} export interface PendingUserInput {  requestId: ApprovalRequestId;  createdAt: string;  questions: ReadonlyArray<UserInputQuestion>;} export interface ActivePlanState {  createdAt: string;  turnId: TurnId | null;  explanation?: string | null;  steps: Array<{    step: string;    status: "pending" | "inProgress" | "completed";  }>;} export interface LatestProposedPlanState {  id: OrchestrationProposedPlanId;  createdAt: string;  updatedAt: string;  turnId: TurnId | null;  planMarkdown: string;} export type TimelineEntry =  | {      id: string;      kind: "message";      createdAt: string;      message: ChatMessage;    }  | {      id: string;      kind: "proposed-plan";      createdAt: string;      proposedPlan: ProposedPlan;    }  | {      id: string;      kind: "work";      createdAt: string;      entry: WorkLogEntry;    }; export function formatTimestamp(isoDate: string): string {  return new Intl.DateTimeFormat(undefined, {    hour: "numeric",    minute: "2-digit",    second: "2-digit",  }).format(new Date(isoDate));} export function formatDuration(durationMs: number): string {  if (!Number.isFinite(durationMs) || durationMs < 0) return "0ms";  if (durationMs < 1_000) return `${Math.max(1, Math.round(durationMs))}ms`;  if (durationMs < 10_000) return `${(durationMs / 1_000).toFixed(1)}s`;  if (durationMs < 60_000) return `${Math.round(durationMs / 1_000)}s`;  const minutes = Math.floor(durationMs / 60_000);  const seconds = Math.round((durationMs % 60_000) / 1_000);  if (seconds === 0) return `${minutes}m`;  if (seconds === 60) return `${minutes + 1}m`;  return `${minutes}m ${seconds}s`;} export function formatElapsed(startIso: string, endIso: string | undefined): string | null {  if (!endIso) return null;  const startedAt = Date.parse(startIso);  const endedAt = Date.parse(endIso);  if (Number.isNaN(startedAt) || Number.isNaN(endedAt) || endedAt < startedAt) {    return null;  }  return formatDuration(endedAt - startedAt);} type LatestTurnTiming = Pick<OrchestrationLatestTurn, "turnId" | "startedAt" | "completedAt">;type SessionActivityState = Pick<ThreadSession, "orchestrationStatus" | "activeTurnId">; export function isLatestTurnSettled(  latestTurn: LatestTurnTiming | null,  session: SessionActivityState | null,): boolean {  if (!latestTurn?.startedAt) return false;  if (!latestTurn.completedAt) return false;  if (!session) return true;  if (session.orchestrationStatus === "running") return false;  return true;} export function deriveActiveWorkStartedAt(  latestTurn: LatestTurnTiming | null,  session: SessionActivityState | null,  sendStartedAt: string | null,): string | null {  if (!isLatestTurnSettled(latestTurn, session)) {    return latestTurn?.startedAt ?? sendStartedAt;  }  return sendStartedAt;} function requestKindFromRequestType(  requestType: unknown,): PendingApproval["requestKind"] | null {  switch (requestType) {    case "command_execution_approval":    case "exec_command_approval":      return "command";    case "file_read_approval":      return "file-read";    case "file_change_approval":    case "apply_patch_approval":      return "file-change";    default:      return null;  }} export function derivePendingApprovals(  activities: ReadonlyArray<OrchestrationThreadActivity>,): PendingApproval[] {  const openByRequestId = new Map<ApprovalRequestId, PendingApproval>();  const ordered = [...activities].toSorted(compareActivitiesByOrder);   for (const activity of ordered) {    const payload =      activity.payload && typeof activity.payload === "object"        ? (activity.payload as Record<string, unknown>)        : null;    const requestId =      payload && typeof payload.requestId === "string"        ? ApprovalRequestId.makeUnsafe(payload.requestId)        : null;    const requestKind =      payload &&      (payload.requestKind === "command" ||        payload.requestKind === "file-read" ||        payload.requestKind === "file-change")        ? payload.requestKind        : payload          ? requestKindFromRequestType(payload.requestType)          : null;    const detail = payload && typeof payload.detail === "string" ? payload.detail : undefined;     if (activity.kind === "approval.requested" && requestId && requestKind) {      openByRequestId.set(requestId, {        requestId,        requestKind,        createdAt: activity.createdAt,        ...(detail ? { detail } : {}),      });      continue;    }     if (activity.kind === "approval.resolved" && requestId) {      openByRequestId.delete(requestId);      continue;    }     if (      activity.kind === "provider.approval.respond.failed" &&      requestId &&      detail?.includes("Unknown pending permission request")    ) {      openByRequestId.delete(requestId);      continue;    }  }   return [...openByRequestId.values()].toSorted((left, right) =>    left.createdAt.localeCompare(right.createdAt),  );} function parseUserInputQuestions(  payload: Record<string, unknown> | null,): ReadonlyArray<UserInputQuestion> | null {  const questions = payload?.questions;  if (!Array.isArray(questions)) {    return null;  }  const parsed = questions    .map<UserInputQuestion | null>((entry) => {      if (!entry || typeof entry !== "object") return null;      const question = entry as Record<string, unknown>;      if (        typeof question.id !== "string" ||        typeof question.header !== "string" ||        typeof question.question !== "string" ||        !Array.isArray(question.options)      ) {        return null;      }      const options = question.options        .map<UserInputQuestion["options"][number] | null>((option) => {          if (!option || typeof option !== "object") return null;          const optionRecord = option as Record<string, unknown>;          if (            typeof optionRecord.label !== "string" ||            typeof optionRecord.description !== "string"          ) {            return null;          }          return {            label: optionRecord.label,            description: optionRecord.description,          };        })        .filter((option): option is UserInputQuestion["options"][number] => option !== null);      if (options.length === 0) {        return null;      }      return {        id: question.id,        header: question.header,        question: question.question,        options,      };    })    .filter((question): question is UserInputQuestion => question !== null);  return parsed.length > 0 ? parsed : null;} export function derivePendingUserInputs(  activities: ReadonlyArray<OrchestrationThreadActivity>,): PendingUserInput[] {  const openByRequestId = new Map<ApprovalRequestId, PendingUserInput>();  const ordered = [...activities].toSorted(compareActivitiesByOrder);   for (const activity of ordered) {    const payload =      activity.payload && typeof activity.payload === "object"        ? (activity.payload as Record<string, unknown>)        : null;    const requestId =      payload && typeof payload.requestId === "string"        ? ApprovalRequestId.makeUnsafe(payload.requestId)        : null;     if (activity.kind === "user-input.requested" && requestId) {      const questions = parseUserInputQuestions(payload);      if (!questions) {        continue;      }      openByRequestId.set(requestId, {        requestId,        createdAt: activity.createdAt,        questions,      });      continue;    }     if (activity.kind === "user-input.resolved" && requestId) {      openByRequestId.delete(requestId);    }  }   return [...openByRequestId.values()].toSorted((left, right) =>    left.createdAt.localeCompare(right.createdAt),  );} export function deriveActivePlanState(  activities: ReadonlyArray<OrchestrationThreadActivity>,  latestTurnId: TurnId | undefined,): ActivePlanState | null {  const ordered = [...activities].toSorted(compareActivitiesByOrder);  const candidates = ordered.filter((activity) => {    if (activity.kind !== "turn.plan.updated") {      return false;    }    if (!latestTurnId) {      return true;    }    return activity.turnId === latestTurnId;  });  const latest = candidates.at(-1);  if (!latest) {    return null;  }  const payload =    latest.payload && typeof latest.payload === "object"      ? (latest.payload as Record<string, unknown>)      : null;  const rawPlan = payload?.plan;  if (!Array.isArray(rawPlan)) {    return null;  }  const steps = rawPlan    .map((entry) => {      if (!entry || typeof entry !== "object") return null;      const record = entry as Record<string, unknown>;      if (typeof record.step !== "string") {        return null;      }      const status =        record.status === "completed" || record.status === "inProgress"          ? record.status          : "pending";      return {        step: record.step,        status,      };    })    .filter(      (        step,      ): step is {        step: string;        status: "pending" | "inProgress" | "completed";      } => step !== null,    );  if (steps.length === 0) {    return null;  }  return {    createdAt: latest.createdAt,    turnId: latest.turnId,    ...(payload && "explanation" in payload ? { explanation: payload.explanation as string | null } : {}),    steps,  };} export function findLatestProposedPlan(  proposedPlans: ReadonlyArray<ProposedPlan>,  latestTurnId: TurnId | string | null | undefined,): LatestProposedPlanState | null {  if (latestTurnId) {    const matchingTurnPlan = [...proposedPlans]      .filter((proposedPlan) => proposedPlan.turnId === latestTurnId)      .toSorted(        (left, right) =>          left.updatedAt.localeCompare(right.updatedAt) || left.id.localeCompare(right.id),      )      .at(-1);    if (matchingTurnPlan) {      return {        id: matchingTurnPlan.id,        createdAt: matchingTurnPlan.createdAt,        updatedAt: matchingTurnPlan.updatedAt,        turnId: matchingTurnPlan.turnId,        planMarkdown: matchingTurnPlan.planMarkdown,      };    }  }   const latestPlan = [...proposedPlans]    .toSorted(      (left, right) =>        left.updatedAt.localeCompare(right.updatedAt) || left.id.localeCompare(right.id),    )    .at(-1);  if (!latestPlan) {    return null;  }   return {    id: latestPlan.id,    createdAt: latestPlan.createdAt,    updatedAt: latestPlan.updatedAt,    turnId: latestPlan.turnId,    planMarkdown: latestPlan.planMarkdown,  };} export function deriveWorkLogEntries(  activities: ReadonlyArray<OrchestrationThreadActivity>,  latestTurnId: TurnId | undefined,): WorkLogEntry[] {  const ordered = [...activities].toSorted(compareActivitiesByOrder);  return ordered    .filter((activity) => (latestTurnId ? activity.turnId === latestTurnId : true))    .filter((activity) => activity.kind !== "tool.started")    .filter((activity) => activity.kind !== "task.started" && activity.kind !== "task.completed")    .filter((activity) => activity.summary !== "Checkpoint captured")    .map((activity) => {      const payload =        activity.payload && typeof activity.payload === "object"          ? (activity.payload as Record<string, unknown>)          : null;      const command = extractToolCommand(payload);      const changedFiles = extractChangedFiles(payload);      const entry: WorkLogEntry = {        id: activity.id,        createdAt: activity.createdAt,        label: activity.summary,        tone: activity.tone === "approval" ? "info" : activity.tone,      };      if (payload && typeof payload.detail === "string" && payload.detail.length > 0) {        entry.detail = payload.detail;      }      if (command) {        entry.command = command;      }      if (changedFiles.length > 0) {        entry.changedFiles = changedFiles;      }      return entry;    });} function asRecord(value: unknown): Record<string, unknown> | null {  return value && typeof value === "object" ? (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 normalizeCommandValue(value: unknown): string | null {  const direct = asTrimmedString(value);  if (direct) {    return direct;  }  if (!Array.isArray(value)) {    return null;  }  const parts = value    .map((entry) => asTrimmedString(entry))    .filter((entry): entry is string => entry !== null);  return parts.length > 0 ? parts.join(" ") : null;} function extractToolCommand(payload: Record<string, unknown> | null): string | null {  const data = asRecord(payload?.data);  const item = asRecord(data?.item);  const itemResult = asRecord(item?.result);  const itemInput = asRecord(item?.input);  const candidates = [    normalizeCommandValue(item?.command),    normalizeCommandValue(itemInput?.command),    normalizeCommandValue(itemResult?.command),    normalizeCommandValue(data?.command),  ];  return candidates.find((candidate) => candidate !== null) ?? null;} function pushChangedFile(target: string[], seen: Set<string>, value: unknown) {  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,) {  if (depth > 4 || target.length >= 12) {    return;  }  if (Array.isArray(value)) {    for (const entry of value) {

Lines 1–500 of 621.