Sandbox Cold-start HUD
When the agent needs a sandbox to continue, the backend emits sandbox.launch /
sandbox.ready on the stream. Core folds them into sandboxPhase, and the SDK floats
a self-contained "launching" HUD into the chat view's bottom-right, playing a short
ready beat before it fades out.
This is automatic — consumers wire nothing.
Sandbox Cold-start HUD
When the agent needs a sandbox to continue, the backend emits sandbox.launch / sandbox.ready; core folds them into sandboxPhase and the SDK floats a self-contained "launching" HUD into the chat view's bottom-right. The 1s threshold is the point: a warm sandbox is often ready in 100–300ms, and flashing an animation then is just noise — anything finishing inside the threshold stays silent.
- Cold start: press launching, wait past 1s, then ready → stage walks launching → ready → leaving → hidden.
- Warm start: press launching then ready immediately → stage stays hidden the whole time; nothing flashes.
import { useSandboxLaunch } from '@asgard-js/react';
import type { SandboxPhase } from '@asgard-js/core';
// phase comes from channel.sandboxPhase$ (or getSandboxPhase())
const { stage, visible } = useSandboxLaunch(phase, {
thresholdMs: 1000, // launches finishing sooner never show
readyBeatMs: 900, // how long "ready" lingers before the exit
exitMs: 600, // fade-out duration
});
// stage: 'hidden' | 'launching' | 'ready' | 'leaving'The built-in HUD appears on its own — no wiring needed; the hook is for consumers drawing their own launch cue. It is independent of the RunningIndicator at the footer seam: this one tracks only "is the sandbox stuck cold-starting", that one binds the whole run's connection.
useSandboxLaunch state
The 1s Threshold
A warm sandbox is often ready in 100–300ms. Flashing an animation then is just noise,
so the phase must stay launching past the threshold (1000ms by default) before
the HUD shows; anything finishing inside it stays completely silent.
Conversely, the HUD does not vanish the instant ready arrives: it lingers for a ready beat (900ms by default) so the user sees "done", then fades out. If ready beat the threshold (the HUD never showed), the whole thing stays quiet.
phase: idle ──► launching ─────────────────► ready ──► idle
│◄── 1000ms ──►│
stage: hidden hidden launching ready ──► leaving ──► hidden
(animation) (beat) (fade)
sandboxPhase
type SandboxPhase = "idle" | "launching" | "ready";
Available from Channel as either a subscription or a snapshot:
channel.sandboxPhase$.subscribe((phase) => { /* … */ });
const phase = channel.getSandboxPhase();
Drawing Your Own Launch Cue
To replace the built-in HUD, useSandboxLaunch maps the phase onto a display stage:
import { useSandboxLaunch } from "@asgard-js/react";
const { stage, visible } = useSandboxLaunch(phase, {
thresholdMs: 1000, // launches finishing sooner never show
readyBeatMs: 900, // how long "ready" lingers before the exit begins
exitMs: 600, // fade-out duration; must match your CSS
});
// stage: 'hidden' | 'launching' | 'ready' | 'leaving'
// visible: convenience flag for stage !== 'hidden'
Every timer is cleaned up on unmount or phase change.
Independent From the Run Indicator
The connection line at the footer seam (RunningIndicator) binds the whole run's
connection; the HUD tracks only "is the sandbox stuck cold-starting". They can
appear together and never interfere.
The Launched-Sandbox List
launchedSandboxes$ carries each sandbox's identity and capabilities:
interface LaunchedSandbox {
sandboxName: string; // authoritative key; every fs / browser API locates by it
sandboxBlueprintName: string; // the blueprint that created it — usually more readable
workingDirectory: string; // the File Explorer tree root
editorServerEnabled: boolean;
browserEnabled: boolean; // gates the browser-handoff card
}
sandboxPhasesandboxPhase is folded straight from the SSE events; launchedSandboxes$ is not. This list's
sole authority is GET /channel/metadata — SSE events never write into it directly.
asgard.sandbox.launch is only a hint: it records the name as pending and triggers one metadata
refetch, and whatever comes back decides who is live.
So never infer "which sandboxes exist right now" from the sandbox.launch events yourself — that
diverges from the backend's authoritative state.
The File Explorer's sandbox dropdown is fed by this list.
See Also
- sandbox:// Handoff Cards — hand the user off to a sandbox browser or file
- File Explorer — browse and edit files inside a sandbox