import type { StatusTone } from "../../components/StatusPill";import type { OrchestrationLatestTurn, OrchestrationSession } from "@t3tools/contracts";import { EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell"; export type ThreadStatusKind = | "pending-approval" | "awaiting-input" | "working" | "connecting" | "error" | "plan-ready"; export interface ThreadStatusPresentation extends StatusTone { readonly kind: ThreadStatusKind; /** Foreground color for the leading status icon. */ readonly iconColor: string; /** Background color for the leading status icon circle. */ readonly iconBackground: string; /** Whether the indicator represents in-flight activity. */ readonly pulse: boolean;} function isLatestTurnSettled( latestTurn: OrchestrationLatestTurn | null, session: OrchestrationSession | null,): boolean { if (!latestTurn?.startedAt) return false; if (!latestTurn.completedAt) return false; if (!session) return true; return session.status !== "running";} /** * Resolves the user-facing status of a thread, in priority order. Returns * `null` for quiescent threads so rows stay free of "Idle"-style noise. * Mirrors `resolveThreadStatusPill` in apps/web/src/components/Sidebar.logic.ts. */export function resolveThreadStatus( thread: EnvironmentThreadShell,): ThreadStatusPresentation | null { if (thread.hasPendingApprovals) { return { kind: "pending-approval", label: "Needs Approval", pillClassName: "bg-warning", textClassName: "text-warning-foreground", iconColor: "#ff9f0a", iconBackground: "rgba(255,159,10,0.22)", pulse: false, }; } if (thread.hasPendingUserInput) { return { kind: "awaiting-input", label: "Awaiting Input", pillClassName: "bg-primary/10", textClassName: "text-foreground-secondary", iconColor: "#5e5ce6", iconBackground: "rgba(94,92,230,0.22)", pulse: false, }; } if (thread.session?.status === "running") { return { kind: "working", label: "Working", pillClassName: "bg-primary/10", textClassName: "text-adaptive-sky-600-400", iconColor: "#0a84ff", iconBackground: "rgba(10,132,255,0.22)", pulse: true, }; } if (thread.session?.status === "starting") { return { kind: "connecting", label: "Connecting", pillClassName: "bg-primary/10", textClassName: "text-foreground-secondary", iconColor: "#0a84ff", iconBackground: "rgba(10,132,255,0.22)", pulse: true, }; } if (thread.session?.status === "error" || thread.latestTurn?.state === "error") { return { kind: "error", label: "Error", pillClassName: "bg-danger", textClassName: "text-danger-foreground", iconColor: "#ff453a", iconBackground: "rgba(255,69,58,0.22)", pulse: false, }; } const hasPlanReadyPrompt = thread.interactionMode === "plan" && isLatestTurnSettled(thread.latestTurn, thread.session) && thread.hasActionableProposedPlan; if (hasPlanReadyPrompt) { return { kind: "plan-ready", label: "Plan Ready", pillClassName: "bg-primary/10", textClassName: "text-foreground-secondary", iconColor: "#bf5af2", iconBackground: "rgba(191,90,242,0.22)", pulse: false, }; } return null;}
import type { StatusTone } from "../../components/StatusPill";
import type { OrchestrationLatestTurn, OrchestrationSession } from "@t3tools/contracts";
import { EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell";
export type ThreadStatusKind =
| "pending-approval"
| "awaiting-input"
| "working"
| "connecting"
| "error"
| "plan-ready";
export interface ThreadStatusPresentation extends StatusTone {
readonly kind: ThreadStatusKind;
/** Foreground color for the leading status icon. */
readonly iconColor: string;
/** Background color for the leading status icon circle. */
readonly iconBackground: string;
/** Whether the indicator represents in-flight activity. */
readonly pulse: boolean;
}
function isLatestTurnSettled(
latestTurn: OrchestrationLatestTurn | null,
session: OrchestrationSession | null,
): boolean {
if (!latestTurn?.startedAt) return false;
if (!latestTurn.completedAt) return false;
if (!session) return true;
return session.status !== "running";
}
/**
* Resolves the user-facing status of a thread, in priority order. Returns
* `null` for quiescent threads so rows stay free of "Idle"-style noise.
* Mirrors `resolveThreadStatusPill` in apps/web/src/components/Sidebar.logic.ts.
*/
export function resolveThreadStatus(
thread: EnvironmentThreadShell,
): ThreadStatusPresentation | null {
if (thread.hasPendingApprovals) {
return {
kind: "pending-approval",
label: "Needs Approval",
pillClassName: "bg-warning",
textClassName: "text-warning-foreground",
iconColor: "#ff9f0a",
iconBackground: "rgba(255,159,10,0.22)",
pulse: false,
};
}
if (thread.hasPendingUserInput) {
return {
kind: "awaiting-input",
label: "Awaiting Input",
pillClassName: "bg-primary/10",
textClassName: "text-foreground-secondary",
iconColor: "#5e5ce6",
iconBackground: "rgba(94,92,230,0.22)",
pulse: false,
};
}
if (thread.session?.status === "running") {
return {
kind: "working",
label: "Working",
pillClassName: "bg-primary/10",
textClassName: "text-adaptive-sky-600-400",
iconColor: "#0a84ff",
iconBackground: "rgba(10,132,255,0.22)",
pulse: true,
};
}
if (thread.session?.status === "starting") {
return {
kind: "connecting",
label: "Connecting",
pillClassName: "bg-primary/10",
textClassName: "text-foreground-secondary",
iconColor: "#0a84ff",
iconBackground: "rgba(10,132,255,0.22)",
pulse: true,
};
}
if (thread.session?.status === "error" || thread.latestTurn?.state === "error") {
return {
kind: "error",
label: "Error",
pillClassName: "bg-danger",
textClassName: "text-danger-foreground",
iconColor: "#ff453a",
iconBackground: "rgba(255,69,58,0.22)",
pulse: false,
};
}
const hasPlanReadyPrompt =
thread.interactionMode === "plan" &&
isLatestTurnSettled(thread.latestTurn, thread.session) &&
thread.hasActionableProposedPlan;
if (hasPlanReadyPrompt) {
return {
kind: "plan-ready",
label: "Plan Ready",
pillClassName: "bg-primary/10",
textClassName: "text-foreground-secondary",
iconColor: "#bf5af2",
iconBackground: "rgba(191,90,242,0.22)",
pulse: false,
};
}
return null;
}