import type { OrchestrationV2ProjectedTurnItem, OrchestrationV2ProviderCapabilities, OrchestrationV2ThreadProjection, } from "@t3tools/contracts"; import { copySorted } from "@t3tools/shared/Array"; type Projection = OrchestrationV2ThreadProjection; type Run = Projection["runs"][number]; type ProviderSession = Projection["providerSessions"][number]; const ACTIVE_RUN_STATUSES = new Set(["preparing", "starting", "running", "waiting"]); export interface QueuedThreadRun { readonly run: Run; readonly text: string; } export interface ThreadQueueWorkflowState { readonly activeRun: Run | null; readonly queuedRuns: ReadonlyArray; readonly canReorder: boolean; readonly canPromoteToSteer: boolean; } export function resolveActiveThreadRun(projection: Projection): Run | null { return projection.runs.findLast((run) => ACTIVE_RUN_STATUSES.has(run.status)) ?? null; } export function resolveThreadProviderSession(projection: Projection): ProviderSession | null { const activeRun = resolveActiveThreadRun(projection); const providerThreadId = activeRun?.providerThreadId ?? projection.thread.activeProviderThreadId; const activeProviderThread = providerThreadId === null ? null : (projection.providerThreads.find((thread) => thread.id === providerThreadId) ?? null); const attachedProviderThread = activeProviderThread ?? projection.providerThreads.find( (thread) => thread.appThreadId === projection.thread.id && thread.providerSessionId !== null, ) ?? null; const sessionId = attachedProviderThread?.providerSessionId ?? null; if (sessionId !== null) { return projection.providerSessions.find((session) => session.id === sessionId) ?? null; } return ( projection.providerSessions.findLast( (session) => session.status !== "stopped" && session.status !== "error", ) ?? null ); } export function deriveThreadQueueWorkflowState(projection: Projection): ThreadQueueWorkflowState { const activeRun = resolveActiveThreadRun(projection); const session = resolveThreadProviderSession(projection); const capabilities = session?.capabilities.turns; const queuedRuns = copySorted( projection.runs.filter((run) => run.status === "queued"), (left, right) => (left.queuePosition ?? left.ordinal) - (right.queuePosition ?? right.ordinal) || left.ordinal - right.ordinal, ).map((run) => ({ run, text: projection.messages.find((message) => message.id === run.userMessageId)?.text ?? "Queued message", })); return { activeRun, queuedRuns, canReorder: capabilities?.supportsQueuedMessages === true, canPromoteToSteer: activeRun !== null && (capabilities?.supportsActiveSteering === true || capabilities?.supportsSteeringByInterruptRestart === true), }; } export function canForkProjectedAssistantItem(input: { readonly projectedItem: OrchestrationV2ProjectedTurnItem; readonly capabilities?: OrchestrationV2ProviderCapabilities | undefined; }): boolean { const item = input.projectedItem.item; if (item.type !== "assistant_message" || item.runId === null || item.status !== "completed") { return false; } if (input.capabilities === undefined) { // Historical and inherited rows may outlive their provider-session record. // Keep the portable server-side fallback available when capability evidence // is absent; a known incapable provider is rejected below. return true; } const capabilities = input.capabilities; const canForkNatively = capabilities.threads.canForkThread && capabilities.threads.canForkFromTurn && capabilities.identity.nativeThreadIds === "strong"; return canForkNatively || capabilities.context.supportsFullThreadHandoff; } export function canDetachThreadProviderSession(projection: Projection): boolean { const session = resolveThreadProviderSession(projection); return session !== null && session.status !== "stopped" && session.status !== "error"; }