import { type ClientModel, type Session, type SessionListEntry, type SessionStatus } from "@opengeni/sdk"; import { findPickerRow, type PickerModelRow } from "./model-policy.js"; import { type SessionDisplayStatus } from "./session-status-model.js"; import { type OpenGeniText } from "./messages/runtime.js"; export { foldForSearch, LOOSE_MATCH_BELOW, rankLabel } from "./app/lib/actions/rank.js"; /** A list row may be a compact page entry or a complete session response. */ export type SessionListRow = Session | SessionListEntry; /** * The title shown for a session: durable agent/user metadata once available, * otherwise a bounded opening-prompt preview while automatic naming is still * pending. Every list, header and row reads identically. */ export declare function sessionDisplayTitle(session: SessionListRow): string; /** The maximum length a session title may be renamed to. */ export declare const SESSION_TITLE_MAX_LENGTH = 200; /** * The value a rename editor seeds from. A safe provisional prompt preview is * editable because it is also what the user sees; the UUID-derived reference * remains an empty draft rather than becoming an accidental rename. */ export declare function sessionRenameSeed(session: SessionListRow): string; /** * Resolve a submitted draft against the current display title. Returns the * trimmed title to persist, or `null` when the edit is a no-op (empty, * unchanged from what is already shown, or still equal to the value that * seeded this edit) and should simply cancel. */ export declare function resolveRenameSubmission(draft: string, display: string, editSeed?: string): string | null; export type SessionRecencyGroup = "today" | "yesterday" | "previous7" | "older"; export declare const SESSION_GROUP_LABELS: (t: OpenGeniText) => Record; /** The render order of recency groups, top → bottom. */ export declare const SESSION_GROUP_ORDER: SessionRecencyGroup[]; export declare function isRunningStatus(status: SessionStatus): boolean; /** An idle session waiting on its own timer, while its control is active. */ export declare function sessionInputWait(session: Pick): { deadlineAt: string; reason: string; } | null; /** * A schedule targeting this session can still start a run. Paused schedules * and one-time schedules whose time has passed are not upcoming. Servers * without the field keep the earlier any-schedule meaning. */ export declare function sessionHasUpcomingSchedule(session: Pick): boolean; export declare function hasActiveEffectiveControl(session: SessionListRow): boolean; export declare function isEffectivelyRunning(session: SessionListRow): boolean; /** Most-recent activity timestamp for a session (updatedAt, then createdAt). */ export declare function sessionActivityTime(session: SessionListRow): number; /** Deterministic newest-first ordering for every flat or forest session list. */ export declare function compareSessionActivity(left: SessionListRow, right: SessionListRow): number; /** Deterministic personal-pin order: newest pin first, then descending id. */ export declare function compareSessionPins(left: SessionListRow, right: SessionListRow): number; /** Split explicit personal pins from ordinary rows without changing the input. */ export declare function partitionPinnedSessions(sessions: T[]): { pinned: T[]; ordinary: T[]; }; /** A pin-aware list as the session page returns it: the person's pins, then ordinary rows. */ export type PinnedSessionLists = { pinned: T[]; sessions: T[]; }; /** The personal pin fields a pin change or a newer read carries. */ export type SessionPinProjection = Pick & Partial>; /** * Apply one session's pin state to a pin-aware list: the row moves between the * pinned section (newest pin first) and the ordinary rows (newest activity * first). Pin revisions are monotonic, so a projection older than the row * already shown (a slow response after a newer change) leaves the lists as * they are, unless it is `authoritative` (a fresh read after a failed change, * which replaces an optimistic revision). A session in neither list is left out. */ export declare function applySessionPinToLists(lists: PinnedSessionLists, projection: SessionPinProjection, options?: { authoritative?: boolean; }): PinnedSessionLists; /** * Which recency bucket a timestamp falls into, relative to `now`. "Today" and * "Yesterday" are calendar-local; "Previous 7 days" is the rest of the trailing * week; everything earlier is "Older". */ export declare function recencyGroupFor(timestampMs: number, now?: Date): SessionRecencyGroup; export type SessionRecencyBucket = { group: SessionRecencyGroup; label: string; sessions: T[]; }; export type GroupedSessions = { /** Running sessions, pinned above every recency group, most-recent first. */ running: T[]; /** Non-running sessions bucketed by recency (empty buckets dropped). */ grouped: SessionRecencyBucket[]; }; /** * Order + bucket the sessions for the rail. Running sessions are lifted into a * synthetic, always-first position regardless of recency (rendered with a * "running" marker); the remainder are bucketed by recency, most-recent first * within each bucket. Empty groups are dropped. */ export declare function groupSessionsForRail(t: OpenGeniText, sessions: T[], now?: Date): GroupedSessions; export declare function relativeTimeLabel(t: OpenGeniText, value: string, now?: Date): string; /** Codex subscription product ids are prefixed `codex/`. */ export declare function isCodexProductModel(modelId: string): boolean; export type SessionStatusTone = "queued" | "running" | "waiting" | "idle" | "failed" | "cancelled"; export declare const SESSION_STATUS_TONE: Record; /** The status dot of a recent-session row: a background command reads as running. */ export declare function recentSessionStatus(session: Pick): { tone: SessionStatusTone; pulse: boolean; }; /** A short `owner/repo` label from the session's first repository resource. */ export declare function sessionRepoLabel(session: Pick): string | null; export declare function recentSessionModelPresentation(modelId: string, catalogRows: readonly PickerModelRow[]): { label: string; billingClass: PickerModelRow["billingClass"]; logoUrl?: string | undefined; }; /** * The home screen's recent sessions: server pins first, then running, then * recency buckets, capped. `sessions` keeps the all-visible-row contract, so * its pins are removed before recombining the explicit pinned section. */ export declare function recentSessionsForHome(t: OpenGeniText, sessions: T[], pinned: T[], limit?: number, now?: Date): T[]; /** * The phone home screen's chats in two sections: every pinned conversation, * then the most recently used others (running first, then by recency), so * pins never push recent work off the screen. Sub-agent sessions are reached * from their parent and are left out of both. */ export declare function homeSessionSections(t: OpenGeniText, sessions: T[], pinned: T[], recentLimit?: number, now?: Date): { pinned: T[]; recent: T[]; }; /** One project (workspace channel) and its sessions, as the web rail groups them. */ export type SessionProjectSection = { /** The channel id, or "default" for unfiled sessions. */ key: string; channelId: string | null; name: string; sessions: T[]; }; /** * Group top-level sessions by project in the server's project order (pinned * projects first), then "Default" for unfiled ones and those whose project no * longer exists. Running sessions lead each project, then the most recent. * Sub-agent sessions are reached from their parent, so they are left out. * Projects without sessions are kept only when `keepEmpty` is set. */ export declare function groupSessionsByProject(sessions: readonly T[], projects: readonly { id: string; name: string; }[], options?: { keepEmpty?: boolean; }): SessionProjectSection[]; export { findPickerRow };