Second’s app preview system renders compiled artifacts in a sandboxed iframe. The agent edits a Vite + React + TypeScript workspace, callsDocumentation Index
Fetch the complete documentation index at: https://docs.second.so/llms.txt
Use this file to discover all available pages before exploring further.
done_building, the worker runs a real build, and the frontend renders dist/index.html plus built assets.
No Sandpack. No in-browser bundling.
TLDR
Storage model
| What | Where | Format |
|---|---|---|
| Working copy during agent execution | Worker filesystem (/tmp/second-workspaces/{appId} by default) | Regular project files |
| Durable snapshot | MongoDB app_source_snapshots | Record<string, string> (source + dist/** text files) |
| Snapshot metadata | MongoDB apps | Snapshot IDs, hashes, file counts, and byte sizes |
| Chat messages and run metadata | MongoDB agent_runs | UIMessage[] and run fields |
| Preview runtime | Browser iframe | srcDoc HTML built from current files returned by the web API |
app_source_snapshots is the durable restore snapshot. Live preview/file
explorer reads prefer the worker workspace via /sessions/:appId/files, then
fall back to the persisted Mongo snapshot when the worker workspace is missing
or empty after sandbox churn. The apps document keeps compact metadata so app
lists, navigation, and access checks do not load source files. Legacy embedded
apps.sourceFiles snapshots remain readable until the app is saved or migrated.
Workspace template
New workspaces are scaffolded with a Vite + React + TS + Tailwind + Shadcn starter. Template includes:package.json(dev,typecheck,build,preview)index.htmlvite.config.tstailwind.config.tspostcss.config.jstsconfig*.jsonsrc/main.tsx,src/App.tsx,src/index.csssrc/components/ui/button.tsxsrc/lib/utils.tssrc/lib/second-sdk.ts— the app SDK withuseAgent,useAgentList,useCollection, anduseDochooks. See App Agents and App Datacomponents.json
sourceFiles are provided during scaffold (restore path), those files are written instead of the template.
Build step (done_building)
The mcp__second__done_building tool is the build gate.
Behavior:
- Validates required Vite files exist.
- Awaits the background dependency warmup started at scaffold time (if any).
- Installs dependencies only when still needed (
node_modulesmissing, lock drift, or a declared package is absent fromnode_modules). - Runs
npm run typecheckandnpm run buildin parallel (when both scripts are defined inpackage.json). Scripts are honored as-is, so swapping bundlers or type-checkers “just works”. - Requires
dist/index.htmlto exist. - Collects workspace snapshot (including
dist/**). - Returns structured success payload (
status: "complete") or explicit failure text.
Dependency warmup
Workspaces startnpm install --include=dev in the background as soon as they are scaffolded (see apps/worker/src/dep-warmup.ts). The explicit dev-dependency include matters in production workers because npm otherwise follows NODE_ENV=production and may omit Vite, TypeScript, PostCSS plugins, and type packages required by the generated app build. By the time done_building is called, deps are usually already installed, so the build step is dominated by the (parallel) typecheck + bundle rather than a cold install. Warmup is a no-op when node_modules already exists (warm worker), and done_building performs the same install only if the dependency tree is incomplete or stale.
Snapshot guardrails
Snapshot persistence is deliberately bounded:- Per-file cap:
512KB - Warning threshold:
8MBtotal snapshot size - Hard fail threshold:
12MBtotal snapshot size - No silent truncation, no partial snapshot writes
done_building returns an explicit error and preview persistence does not advance.
Preview rendering
AppPreview resolves rendering in this order:
- Artifact mode (
dist/index.html+dist/*assets) - Legacy ArrowJS fallback (
main.js/main.ts) for older snapshots
srcDoc.
Iframe is sandboxed and intentionally omits allow-same-origin, so the preview
does not regain same-origin privileges with the parent app. Parent-window
bridges validate event.source against the expected iframe window before
handling SDK messages.
Workspace owners and admins can tune generated-app runtime capabilities in
Settings → Runtime settings. The default policy preserves the normal app
experience: scripts run, clipboard writes are available for user-triggered copy
actions, and external links can open new tabs. Hardening those toggles removes
the corresponding iframe sandbox or allow capability without changing the
tenant boundary or granting same-origin access.
done_building success detection
Build-complete UI transitions are triggered only whendone_building returns a successful structured payload (status: "complete").
Failed tool outputs no longer flip the workspace into a false “ready” state.
Persistence and conditional hydration
On each user message:- Web checks worker status (
/sessions/:appId/status). - If restore is not needed, web sends prompt without
sourceFiles. - If restore is needed, web loads the latest draft source snapshot from Mongo and sends it as
sourceFilesto the worker. - Worker scaffolds from
sourceFilesonly when workspace is empty.
Live reads vs durable restore
- Live preview/file explorer reads: web API calls worker
GET /sessions/:appId/filesusingWORKER_URLwhen live files are available, merging them over the persisted snapshot so live source can update without hiding the last compileddist/**artifact. - Idle/cold fallback: if the worker is unavailable or returns an empty workspace, the web API returns the MongoDB source snapshot so the compiled app and file explorer remain visible after the 15-minute sandbox TTL.
- Durable recovery after worker loss: chat restore path loads the MongoDB source snapshot and rehydrates the workspace.
- Result: UI shows current worker filesystem when available; Mongo snapshot is used both to keep the idle preview visible and to recover state after churn.
Key files
| File | Role |
|---|---|
apps/worker/src/workspace-template.ts | Vite + TS + Shadcn scaffold |
apps/worker/src/runner.ts | done_building implementation, snapshot limits, workspace collection |
apps/worker/src/dep-warmup.ts | background npm install started at scaffold time |
apps/worker/src/index.ts | scaffold logic, status/files endpoints, warmup trigger |
apps/web/src/components/app-preview.tsx | artifact-first iframe rendering + legacy fallback |
apps/web/src/components/app-chat.tsx | successful done_building detection in stream UI |
apps/web/src/components/app-workspace.tsx | initial preview visibility logic |
apps/web/src/app/w/[workspaceId]/settings/runtime-settings | workspace runtime capability settings for generated app iframes |
apps/web/src/lib/agent/worker-bridge.ts | bridge stream handling and build-complete gating |
apps/web/src/app/api/.../chat/route.ts | conditional hydration + snapshot persistence |
apps/web/src/lib/db/repositories/app-source-snapshots.ts | source snapshot storage, hashes, file counts, and size summaries |
apps/web/src/lib/db/repositories/apps.ts | app metadata, legacy fallback, and snapshot promotion |
scripts/migrate-app-source-snapshots.mjs | optional operational migration from embedded legacy source maps into snapshot documents |