Sandbox Viz

An interactive walkthrough of the multi-layered safety pipeline every agent tool call passes through before any code runs. Four built-in scenarios demonstrate the four distinct paths through the system: a read_file that auto-approves and runs in a process sandbox, a write_file('.env', …) that hard-blocks at the approval gate, an edit_file that waits for human review and then runs in gVisor, and a search_web whose result is sanitised post-execution by a quarantine model.

Each press of Step advances the active scenario one stage. The active card scales gently, its pill recolours by tier, and the narration line beneath updates — so the viewer can see the pipeline's structure without reading any source code.

Scenario
1
Tool Call
2
Risk Classifier
Auto-approve
Human review
Block
3
Approval Gate
4
Sandbox
5
Execution
6
Result

Every tool call flows through a multi-layered safety pipeline before anything executes. Pick a scenario, then step through the pipeline.

Customize
Scenario
safe-read

Installation

npx shadcn@latest add https://craftbits.dev/r/sandbox-viz.json

Usage

import { SandboxViz } from "@craft-bits/viz/sandbox-viz";
 
<SandboxViz />

Drive it externally (controlled scenario):

<SandboxViz
  scenarioId={scenarioId}
  onScenarioChange={setScenarioId}
  onComplete={({ finalStage, approvalOutcome }) => {
    /* lift the final stage into an external chart / analytics */
  }}
/>

Replace the default scenarios with your own:

<SandboxViz
  scenarios={[
    {
      id: "ssh-key",
      label: "SSH key read",
      toolCall: { tool: "read_file", args: { path: "~/.ssh/id_rsa" } },
      riskTier: "block",
      riskReason: "Read from a known credential path",
      approvalOutcome: "blocked",
      sandboxTier: "process",
      executionResult: "",
      resultText: "BLOCKED — credential path",
      finalStage: "approval",
    },
  ]}
/>

Understanding the component

  1. Stage column. Six numbered cards stack vertically with down-arrow connectors between them: incoming call, risk classifier, approval gate, sandbox, execution, result.
  2. Reach state. Each card lights up only after the active step reaches it. Cards before that float at 0.25 opacity; the active card scales briefly from 0.96 to 1.01.
  3. Tier ladder. The classifier card renders three tier rows (auto / review / block); the row matching the scenario's riskTier scales up and recolours, and a 300ms shimmer sweeps across the card when it activates.
  4. Sandbox tiers. The sandbox card renders three concentric layers (microvm / gvisor / process); the layer matching the scenario's sandboxTier (and every layer below) light up with a small per-row stagger.
  5. Approval gate. The gate shows one of three states (approved / waiting / blocked). The waiting state holds for ≈1.5s before resolving to approved, simulating the human round-trip.
  6. Final stage. For blocked scenarios the pipeline terminates at the approval gate. For all other scenarios it ends at the result card, and a chip strip lists every stage the run activated.
  7. Reduced motion. Under prefers-reduced-motion: reduce, every entrance, scale cycle, and shimmer disables, and the approval-wait timer collapses to 200ms.

Props

PropTypeDefaultDescription
scenariosreadonly SandboxVizScenario[]4 built-insOverride the scenario set.
scenarioIdstringControlled scenario id. Pair with onScenarioChange.
defaultScenarioIdstringfirst scenarioUncontrolled initial scenario id.
onScenarioChange(id: string) => voidFires when the viewer picks a different scenario.
onComplete(summary) => voidFires when the pipeline reaches its final stage.
transitionTransitionSPRINGS.snapOverride card entrance / pill colour transition.
classNamestringMerged onto the root via cn().

Accessibility

  • The narration paragraph lives in a polite live region (aria-live="polite", aria-atomic) so screen-reader users hear the stage explanation update as the run advances.
  • The scenario selector is a labelled role="group"; each button reports its selection via aria-pressed.
  • Every interactive control meets the 36px hit-target minimum and exposes a visible focus-visible ring.
  • Decorative SVGs (chevrons, icons, sandbox-tier rectangles) are marked aria-hidden.
  • Colour is paired with text labels at every signal — tier name, approval outcome, and result line are all readable without colour.
  • Motion respects prefers-reduced-motion: reduce — every entrance, scale cycle, and shimmer disables, and the approval-wait timer shortens to 200ms.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/systems/SandboxViz.tsx). The source was a lesson component that bundled a Widget/ModeStrip chrome, a ChallengeBtn + ScoreDots predict-and-challenge quiz, undo/redo history via useWidgetHistory, and the lesson's narration framing. The viz extract keeps only the interactive Explore pipeline — the multiple-choice quizzes were curriculum-specific and live in the lesson source. Per-track palette tokens (--color-fail-400, --color-success-400, --color-warn-400, --color-accent-400, --color-ink-*) are remapped to var(--cb-error) / var(--cb-success) / var(--cb-warning) / var(--cb-accent) / var(--cb-fg-*) so consumer themes repaint freely, and inline SPRINGS.snappy / SPRINGS.gentle are re-keyed to the canonical SPRINGS.snap / SPRINGS.smooth from @craft-bits/core/motion. The infinite shimmer loop on the classifier was tightened to a single 300ms sweep — craft-bits/duration-max-300ms rules out the longer 600ms pulse.