import { type SessionCapabilities, type SessionEvent } from "@opengeni/sdk"; import { type ClientOverride } from "../provider.js"; export type SessionCapabilitiesState = "idle" | "negotiating" | "ready" | "cold" | "on-demand" | "error"; export type UseSessionCapabilitiesOptions = ClientOverride & { /** * Live event log to fold `stream.url.rotated` from (usually * `useSessionEvents().events`). When present the desktop socket stays fresh on * a box rollover without a round-trip; stale-epoch rotations are dropped. */ events?: SessionEvent[] | undefined; /** * Whether to acquire a viewer holder for the desktop pixel plane. Requires the * un-redacted acknowledgment to have been recorded (else the attach 409s and * the hook surfaces the consent requirement). Default false: read-only * negotiation (no holder, no warm) — terminal/files/git work without it. */ attachDesktop?: boolean | undefined; /** * Whether to acquire a viewer holder to warm the box for the REAL interactive * terminal (the ttyd pty-ws plane). Symmetric with `attachDesktop` and shares * the SAME viewer attach (one warm box serves both planes), but needs NO * un-redacted acknowledgment — a shell is interactive by nature, and the gate * is the scoped tunnel URL + stream token. Default false: the terminal stays on * the read-only Channel-A firehose until the user opens/focuses it. The attach * folds the minted `pty-ws` url+token into the `Terminal` cell. */ attachTerminal?: boolean | undefined; /** * Whether to acquire a viewer holder to warm the box for a FILE WRITE — the * wake-on-edit INTENT (a first keystroke in the editor), NOT passive browsing. * Shares the SAME viewer attach as the desktop/terminal (one warm box, one * holder) and — like the terminal — needs NO un-redacted acknowledgment: * reading/writing files is the ordinary Channel-A control plane, not the pixel * plane. It folds NO live URL (files ride the stateless HTTP plane) — it only * refcounts liveness. Unlike desktop/terminal it warms even a COLD box: an edit * legitimately cold-creates (that IS the wake), so the write lands ~100ms warm * instead of paying the ~5s cold resume. Default false. IMPORTANT: merely * opening/reading the Files tab must NOT set this — browsing capture-served * trees/diffs needs no box, and warming one on a cold glance burns box-hours to * serve reads the capture already answers for free. */ attachFiles?: boolean | undefined; /** Hold off negotiating (e.g. the workbench panel is collapsed). Default true. */ enabled?: boolean | undefined; /** Poll cadence (ms) while the lease is cold/warming. Default 1500. */ warmingPollMs?: number | undefined; /** * Give up waiting for `warm` after this long while polling (ms) and surface a * stalled error with a manual `renegotiate`. Default 30000. This is a UI * patience deadline, independent from the worker's sandbox warming timeout. * 0 disables the deadline. */ warmingDeadlineMs?: number | undefined; }; export type UseSessionCapabilitiesResult = { /** The negotiated capability doc — the single source of UI truth. */ capabilities: SessionCapabilities | null; state: SessionCapabilitiesState; error: Error | null; /** * 409 from the desktop attach: the un-redacted (or shared) plane needs explicit * acknowledgment before a viewer holder is granted. The workbench uses this * to complete acknowledgment automatically while Desktop is opening. */ acknowledgmentRequired: "unredacted" | "shared" | null; /** 429 from the desktop attach: the per-session viewer cap is reached. */ viewerCapReached: boolean; /** The viewer holder id minted on a desktop attach (for detach/heartbeat). */ viewerId: string | null; /** Force a re-negotiation (after acknowledging, a resolution change, etc.). */ renegotiate: () => void; }; /** * The capability-negotiation hook. Discovers what THIS session+backend+OS * supports (FileSystem/Terminal/Git always-ish; DesktopStream/Recording * sometimes), drives capability-gated rendering, and — when `attachDesktop` — * holds a viewer lease + heartbeats it so the box stays warm while watched. * * Degradation is a value, never a crash: an unsupported surface comes back * `available:false`/`transport:null` + a `reason`; the components render the * reason-aware empty state. 409 (acknowledgment) and 429 (viewer cap) are * surfaced as typed signals, not thrown. */ export declare function useSessionCapabilities(sessionId: string | null | undefined, options?: UseSessionCapabilitiesOptions): UseSessionCapabilitiesResult;