238 lines · 8.6 KB · TypeScript
RawDownload
import type {  ThreadLinkedPullRequest,  EnvironmentId,  MessageId,  OrchestrationProjectShell,  OrchestrationV2RunStatus,  OrchestrationV2ThreadProjection,  OrchestrationV2ThreadShell,  PlanId,  ProjectId,  ProviderInstanceId,  RunId,  ThreadId,} from "@t3tools/contracts";import * as DateTime from "effect/DateTime"; export interface EnvironmentProject extends OrchestrationProjectShell {  readonly environmentId: EnvironmentId;} /** * A pristine V2 thread projection paired with the environment that produced it. * * The projection stays nested so the server projection and all structurally * shared collections retain their identities. Rich consumers read V2 state * directly instead of a second presentation-shaped thread graph. */export interface EnvironmentThread {  readonly environmentId: EnvironmentId;  readonly projection: OrchestrationV2ThreadProjection;} export interface ThreadRunSummary {  readonly runId: RunId;  readonly status: OrchestrationV2RunStatus;  readonly requestedAt: string | null;  readonly startedAt: string | null;  readonly completedAt: string | null;  readonly assistantMessageId: MessageId | null;  readonly sourcePlanRef?: {    readonly threadId: ThreadId;    readonly planId: PlanId;  };} export interface ThreadRuntimeSummary {  readonly status: OrchestrationV2RunStatus | "idle";  readonly activeRunId: RunId | null;  readonly providerInstanceId: ProviderInstanceId;  readonly providerName: string | null;  readonly lastError: string | null;  readonly updatedAt: string;} export function threadRuntimeIsActive(runtime: ThreadRuntimeSummary | null | undefined): boolean {  return runtime !== null && runtime !== undefined && threadRunStatusIsActive(runtime.status);} export function threadRunStatusIsActive(status: ThreadRuntimeSummary["status"]): boolean {  return (    status === "preparing" ||    status === "queued" ||    status === "starting" ||    status === "running" ||    status === "waiting"  );} export interface EnvironmentThreadShell {  readonly environmentId: EnvironmentId;  readonly id: ThreadId;  readonly projectId: ProjectId;  readonly title: string;  readonly providerInstanceId: ProviderInstanceId;  readonly modelSelection: OrchestrationV2ThreadShell["modelSelection"];  readonly runtimeMode: OrchestrationV2ThreadShell["runtimeMode"];  readonly interactionMode: OrchestrationV2ThreadShell["interactionMode"];  readonly branch: string | null;  readonly worktreePath: string | null;  readonly lineage: OrchestrationV2ThreadShell["lineage"];  readonly forkedFrom: OrchestrationV2ThreadShell["forkedFrom"];  readonly activeProviderThreadId: OrchestrationV2ThreadShell["activeProviderThreadId"];  readonly latestRun: ThreadRunSummary | null;  readonly runtime: ThreadRuntimeSummary | null;  readonly latestUserMessageAt: string | null;  readonly hasPendingApprovals: boolean;  readonly hasPendingUserInput: boolean;  readonly hasActionableProposedPlan: boolean;  readonly pendingBackgroundTasks: ReadonlyArray<    NonNullable<OrchestrationV2ThreadShell["pendingBackgroundTasks"]>[number]  >;  readonly itemCount: number;  readonly visibleItemCount: number;  readonly createdAt: string;  readonly updatedAt: string;  readonly archivedAt: string | null;  readonly settledOverride: "settled" | "active" | null;  readonly settledAt: string | null;  readonly snoozedUntil: string | null;  readonly snoozedAt: string | null;  readonly pinnedAt: string | null;  /** Slot in the user-arranged pinned order; null for keyless (legacy) pins. */  readonly pinOrderKey: string | null;  /**   * Pull request the user linked to the thread (#8160). The v2 server does not   * project this yet, so it stays undefined on v2 environments; UI treats   * undefined and null alike.   */  readonly linkedPullRequest?: ThreadLinkedPullRequest | null;  /**   * Server-tracked visited watermark. `undefined` means the environment's   * server predates visited tracking and clients should fall back to any   * local visited state they keep.   */  readonly lastVisitedAt?: string | null;  /** Pending title regeneration marker; null when no request is in flight. */  readonly titleRegeneration?: { readonly requestId: string; readonly startedAt: string } | null;  readonly deletedAt: string | null;  readonly source: OrchestrationV2ThreadShell;} function iso(value: DateTime.Utc): string {  return DateTime.formatIso(value);} function nullableIso(value: DateTime.Utc | null): string | null {  return value === null ? null : iso(value);} function terminalRunStatus(status: OrchestrationV2RunStatus): boolean {  return (    status === "completed" ||    status === "interrupted" ||    status === "failed" ||    status === "cancelled" ||    status === "rolled_back"  );} // Park runtime at idle when the post-settlement background roster is nonempty// so #4415 waiting-presentation Waiting (session.idle) can consume CTM runtime.// The server suppresses the roster while an interruptible activity run exists,// so a remaining roster is stronger than checkpoint-oriented waiting.// latestRun keeps the latest run's status for history presentation.function shellRuntime(thread: OrchestrationV2ThreadShell): ThreadRuntimeSummary | null {  if (thread.latestRunId === null && thread.activeProviderThreadId === null) return null;  const hasPendingBackgroundTasks = (thread.pendingBackgroundTasks?.length ?? 0) > 0;  const status = hasPendingBackgroundTasks ? "idle" : (thread.activityRunStatus ?? thread.status);  return {    status,    activeRunId: thread.activeRunId,    providerInstanceId: thread.providerInstanceId,    providerName: null,    lastError: thread.lastError ?? null,    updatedAt: iso(thread.updatedAt),  };} export function scopeProject(  environmentId: EnvironmentId,  project: OrchestrationProjectShell,): EnvironmentProject {  return { ...project, environmentId };} export function presentThreadShell(  environmentId: EnvironmentId,  thread: OrchestrationV2ThreadShell,): EnvironmentThreadShell {  const updatedAt = iso(thread.updatedAt);  const latestRun =    thread.latestRunId === null      ? null      : ({          runId: thread.latestRunId,          status: thread.status === "idle" ? "completed" : thread.status,          requestedAt: nullableIso(thread.latestRunRequestedAt ?? null),          startedAt: nullableIso(thread.latestRunStartedAt ?? null),          completedAt:            thread.latestRunCompletedAt === undefined              ? thread.status === "idle" || terminalRunStatus(thread.status)                ? updatedAt                : null              : nullableIso(thread.latestRunCompletedAt),          assistantMessageId: null,        } satisfies ThreadRunSummary);  return {    environmentId,    id: thread.id,    projectId: thread.projectId,    title: thread.title,    providerInstanceId: thread.providerInstanceId,    modelSelection: thread.modelSelection,    runtimeMode: thread.runtimeMode,    interactionMode: thread.interactionMode,    branch: thread.branch,    worktreePath: thread.worktreePath,    linkedPullRequest: thread.linkedPullRequest ?? null,    lineage: thread.lineage,    forkedFrom: thread.forkedFrom,    activeProviderThreadId: thread.activeProviderThreadId,    latestRun,    runtime: shellRuntime(thread),    latestUserMessageAt: nullableIso(thread.latestUserMessageAt),    hasPendingApprovals:      thread.pendingRuntimeRequest !== null &&      thread.pendingRuntimeRequest.kind !== "user_input" &&      thread.pendingRuntimeRequest.kind !== "auth_refresh",    hasPendingUserInput: thread.pendingRuntimeRequest?.kind === "user_input",    hasActionableProposedPlan: thread.hasActionableProposedPlan,    pendingBackgroundTasks: thread.pendingBackgroundTasks ?? [],    itemCount: thread.itemCount,    visibleItemCount: thread.visibleItemCount,    createdAt: iso(thread.createdAt),    updatedAt,    archivedAt: nullableIso(thread.archivedAt),    settledOverride: thread.settledOverride,    settledAt: nullableIso(thread.settledAt),    snoozedUntil: nullableIso(thread.snoozedUntil ?? null),    snoozedAt: nullableIso(thread.snoozedAt ?? null),    pinnedAt: nullableIso(thread.pinnedAt ?? null),    pinOrderKey: thread.pinOrderKey ?? null,    ...(thread.lastVisitedAt === undefined      ? {}      : { lastVisitedAt: nullableIso(thread.lastVisitedAt) }),    titleRegeneration:      thread.titleRegeneration == null        ? null        : {            requestId: thread.titleRegeneration.requestId,            startedAt: iso(thread.titleRegeneration.startedAt),          },    deletedAt: nullableIso(thread.deletedAt),    source: thread,  };} export const scopeThreadShell = presentThreadShell;