/** * SessionChrome — compact merged session signals above the composer. * * Built-in session chrome for production and embeds. Token-themed * (`--og-session-chrome-*`); hosts override on `.og-session-chrome` or an ancestor. * * ## Host tokens * Defaults live in `tokens.css`. * * | Token | Role | * | --- | --- | * | `--og-session-chrome-surface` / `-open` | Dock fill (collapsed / expanded) | * | `--og-session-chrome-border` / `-open` | Dock edge | * | `--og-session-chrome-highlight` / `-ring` | Sliding chip selection fill + edge | * | `--og-session-chrome-shadow` / `-open` | Elevation | * | `--og-session-chrome-radius` | Dock corner radius | * | `--og-session-chrome-chip-min-height` | Signal chip height | * | `--og-session-chrome-chip-pad-x` / `--og-session-chrome-chip-gap` | Chip padding / rail gap | * | `--og-session-chrome-rail-pad` | Outer rail inset | * | `--og-session-chrome-panel-pad-x` / `-y` | Expanded panel padding | * | `--og-session-chrome-panel-max-height` | Cap for expanded body (scrolls inside) | * | `--og-session-chrome-duration` / `--og-session-chrome-ease` | Expand + pill motion | * | `--og-session-chrome-crossfade-duration` | Segment content opacity crossfade | * | `--og-session-chrome-row-hover` | Inbox / queue row hover wash | * * Inbox and queue stay separate segments. Queue hover actions wire to * `UseTurnQueueResult` (`editTurn` / `steerTurn` / `moveTurn` / `removeTurn`). * The collapsed queue chip previews the latest queued message beside its Steer * action; open queue rows keep Steer visible and reveal the rest on hover. * Inbox has no product dismiss API; pass `onDismissIncoming` when the host * wants a visible action (dev harness may use a local dummy). * * Queue opens by default when chrome is idle and at least one authoritative * prompt is queued. Closing the queue dismisses that session's offer until * occupancy drains; a different session on the same chrome instance may * offer again. An in-flight optimistic Send never opens the drawer — * admission stays a paint-only receipt on the chip so layout does not jump * under the pointer. * * Segment switches keep the panel shell mounted and crossfade content. The * shell uses one CSS grid-track transition for deliberate open/close actions; * live queue reconciliation never feeds measurements back into layout. */ import type { SessionGoal, SessionStatus } from "@opengeni/sdk"; import { type ReactNode } from "react"; import type { ComposerOptimisticMessage, ComposerState } from "../hooks/use-composer.js"; import type { UseGoalResult } from "../hooks/use-goal.js"; import type { UseTurnQueueResult } from "../hooks/use-turn-queue.js"; import { countAuthoritativeQueuedTurns } from "../queue-presentation-model.js"; import { type OpenGeniText } from "../messages/runtime.js"; export type SessionChromeSignalId = "incoming" | "steering" | "queue" | "goal" | "agents" | "commands"; export type SessionChromeSignalTone = "neutral" | "accent" | "waiting" | "running"; export type SessionChromeAgentsSignal = { count: number; detail?: string | undefined; tone?: SessionChromeSignalTone | undefined; }; export type SessionChromeProps = { /** Authoritative execution status; omitted by older embedding hosts. */ sessionStatus?: SessionStatus | undefined; queue: UseTurnQueueResult; /** Open and focus an existing queued prompt without changing its delivery. */ queueFocusTarget?: { turnId: string; requestId: number; } | undefined; /** Needed for queue edit → composer checkout. Omit with `readOnly`. */ composer?: ComposerState | undefined; /** Focus the composer after a successful queue checkout has applied its draft. * Connect to the composer controller's `focusInput`, or a custom input ref. * Never called for failed checkout or while replacement awaits confirmation. */ onComposerFocus?: (() => void) | undefined; goal?: UseGoalResult | null | undefined; /** Expanded agents body (host supplies tree / list). */ agentsPanel?: ReactNode; /** Active commands only. The host mounts the body only when opened. */ commandsPanel?: ReactNode; commandsCount?: number | undefined; /** Chip summary; when `count > 0` the agents segment appears. */ agentsSignal?: SessionChromeAgentsSignal | undefined; /** * Optional inbox dismiss. Product pending-inputs have no remove API — hosts * (and the gallery) may still pass a handler so the action is visible. */ onDismissIncoming?: ((inputId: string) => void) | undefined; /** Open a typed child update's source through the host's normal authorized route. */ onOpenSession?: ((sessionId: string) => void) | undefined; readOnly?: boolean | undefined; className?: string | undefined; /** Controlled active segment; omit for uncontrolled. */ active?: SessionChromeSignalId | null | undefined; defaultActive?: SessionChromeSignalId | null | undefined; onActiveChange?: ((next: SessionChromeSignalId | null) => void) | undefined; }; type GoalPillState = "pursuing" | "waiting" | "scheduled" | "session_failed" | "blocked" | "held" | "paused" | "invariant_broken" | "completed"; type GoalPillRecord = Pick & { rationale?: SessionGoal["rationale"]; continuation?: SessionGoal["continuation"] | null | undefined; }; /** Pill label, with the pause reason spelled out: "Paused · cap" / "Paused · manually". */ export declare function sessionChromeGoalPillLabel(t: OpenGeniText, state: GoalPillState, record: GoalPillRecord | null | undefined): string; /** * One human sentence explaining WHY the goal is not pursuing right now: the * pause reason, the agent's own `wait_for_input` hold (reason + deadline), or the * next idle-backoff check time. Null when the state needs no explanation. */ export declare function sessionChromeGoalPillExplanation(t: OpenGeniText, state: GoalPillState, record: GoalPillRecord | null | undefined): string | null; export { countAuthoritativeQueuedTurns }; export declare function countOptimisticQueuedMessages(messages: readonly ComposerOptimisticMessage[] | undefined, queuedTurnIds: ReadonlySet, snapshot: UseTurnQueueResult["snapshot"]): number; /** * First uncontrolled segment. An existing authoritative queue opens itself * when the host did not pick another default; optimistic-only occupancy * stays collapsed so a live Send does not shove a drawer under the pointer. */ export declare function sessionChromeInitialActive(input: { defaultActive: SessionChromeSignalId | null; authoritativeQueuedCount: number; }): SessionChromeSignalId | null; /** * Whether idle chrome should offer the queue panel. A dismissed session * stays collapsed until occupancy drains. Controlled hosts own this. */ export declare function sessionChromeShouldOfferQueue(input: { controlled: boolean; active: SessionChromeSignalId | null; activityOpen: boolean; authoritativeQueuedCount: number; suppressed: boolean; }): boolean; /** Select pill state from the goal's authoritative continuation projection. */ export declare function sessionChromeGoalPillState(goalStatus: "active" | "paused" | "completed", continuation: SessionGoal["continuation"] | null | undefined, sessionStatus?: SessionStatus): GoalPillState; /** * Under a single-Send composer (`composer.singleSend`) the chrome speaks of * waiting messages rather than a queue, and offers no Steer on them. */ export declare function SessionChrome(props: SessionChromeProps): import("react").JSX.Element;