docs
TypeScript SDKCore Concepts

Session Lifecycle

The nine honest phases, scale-to-zero waking UX, Session expiry, idle handling, and DVR recordings

Sessions are honest by default. The SDK never hides what a GPU Session is doing behind one opaque spinner: waking, waiting for capacity, expiring, and idling are all first-class, typed states with drop-in UI. This page is the end-to-end lifecycle toolkit.

The nine phases

idle → queued | unavailable | provisioning → connecting → live → ended | expired
                                                        ↘ error
PhaseMeaning
idleCreated, not yet admitted or connected
queuedWaiting in the admission queue (phase.queue = { position, depth })
unavailableNo runtime exists to serve this function — non-terminal; recovers when one appears
provisioningAdmitted; compute is being allocated/warmed
connectingTransport is establishing the media Session
liveFirst media track has arrived
errorAdmission or transport failed (phase.error.kind names why)
endedIntentionally disconnected / closed
expiredReached its declared maximum length — terminal and honest, not an error

Three helpers cover most phase UI: describeSessionPhase(p) returns honest default copy ("Starting model…", a queue position, "Runtime unavailable — …"); isWakingPhase(p) names the pre-live waiting set; whenLive(session) / session.whenLive() is the "is media ready?" gate that rejects with a typed SessionFailedError (kind + full SessionStatus) instead of an opaque failure. UrunSessionStatus renders the phase line; UrunSessionGate gates children on live.

Scale-to-zero waking

Apps scale to zero, so a first Session may wake a runtime. The SDK makes that wait honest and short:

  • useUrunPrewake({ function: 'generate' }) — the intent signal. While a page that will start a Session is mounted, it pings the advisory prewake (on mount, then every 60s) so capacity is already warming when the user clicks start. Never throws, never affects the Session flow.
  • useSessionWake(session) — a live { waking, state, reason, seconds } view of the pre-live wait, counted from phase.wakingSince as one continuous clock across queued/unavailable/provisioning (never resetting per sub-phase).
  • UrunSessionWaking — the drop-in "waking (Ns)" element; renders nothing when not waking.
import { useUrunPrewake, UrunSessionWaking } from '@urun-sh/react'

function GeneratePage({ session }) {
  useUrunPrewake({ function: 'generate' })     // warm while the page shows intent
  return <UrunSessionWaking session={session} />
}

While waiting, phase.runtime reports why: unavailable (no runtime registered), starting (cold-starting/warming), or busy (all runtimes serving other Sessions), plus a frontend-safe reason.

Session clock and expiry

A function can declare a maximum Session length; the platform then ends the Session honestly at its deadline with the terminal phase expired — distinct from error because nothing failed.

  • session.endsAt / useSessionEndsAt(session) — the deadline as a Date, or null when no maximum was declared.
  • UrunSessionClock — an mm:ss countdown to the deadline; renders nothing without one (compose it into any header unconditionally), flags data-urgent in the last minute.
  • UrunSessionEnded — the terminal ended/expired surface, with an onNewSession hook to start fresh.

Idle warning

Attached-but-inert Sessions time out (platform default; Apps configure or opt out server-side). The SDK auto-stamps pointer/key/visibility activity into the Session's awareness; before the kick, the server raises a warning in the control doc:

  • useSessionIdle(session) — the raised warning ({ warning, deadlineEpochS, idleSinceEpochS }), or null in the steady state.
  • UrunIdleWarning — the "Are you still there?" overlay with a countdown; its button confirms presence via session.touch().
  • session.touch() — record application-specific activity explicitly (throttled internally). Vanilla (non-React) applications can wire DOM activity with createActivityTracker from @urun-sh/core.
import { UrunSessionClock, UrunSessionEnded, UrunIdleWarning } from '@urun-sh/react'

function Shell({ session, children }) {
  return (
    <>
      <header><UrunSessionClock session={session} /></header>
      {children}
      <UrunIdleWarning session={session} />
      <UrunSessionEnded session={session} onNewSession={() => location.reload()} />
    </>
  )
}

Recordings: the DVR

Session streams are recorded by default — every Session is replayable, and stream.seek(t) addresses the recording while seek('live') snaps back to the live edge. Retention is explicit:

  • Recordings start in the short-lived temp bucket; retain(id, '7d') keeps one for a duration, pin(id) moves it to the durable bucket, permanent(id) locks it, delete(id) removes it.
  • Mutations are async intents: a recording reports pending_pin / pending_delete / … until the reconciler settles it — surface that (isPendingRecording(r)) instead of assuming completion.
const recordings = await session.recordings.list()   // this Session's recordings
await session.recordings.retain(recordings[0].id, '7d')

Replay after end. Recording playback outlives the Session: attaching to an ended Session whose recording exists (Session.attach(id)) answers a replay Session — the platform replays the recording through the normal Session stream surface (no GPU runtime is woken), so the same <Video> / track rendering works unchanged. Drive playback with stream('video').seek(0). Video streams replay today; an ended Session with no recording stays an honest dead end rather than a faked replay.

const replay = await Session.attach(endedSessionId, options)   // replay Session for an ended id
await replay.stream('video').seek(0)                            // play the recording from the start

session.recordings requires functionsUrl on your AppOptions (a clear error tells you if it's missing); the standalone RecordingsClient from @urun-sh/core covers non-Session (org-scoped) access. Files the runtime produces (e.g. rendered outputs) surface separately as artifacts: session.artifacts.list() and getDownloadUrl(artifactId) return short-lived object-store URLs (RuntimeArtifactsClient is the standalone form).

Related:

  • Types — SessionPhase, RuntimeAvailability, SessionFailureKind, SessionStatus
  • Hooks — the Session-state hooks in one table
  • Transport — reconnection and transport negotiation beneath the phases

Was this page helpful?

0/2000

On this page