Architecture
The sandbox pattern has three layers:-
Deep agent with sandbox backend: The agent gets filesystem tools
(
read_file,write_file,edit_file,execute) automatically from the sandbox -
Custom API server: A Hono app exposed via
langgraph.json’shttp.appfield, providing file browsing endpoints the frontend can call - IDE frontend: A three-panel layout (file tree, code/diff viewer, chat) that syncs files in real time as the agent makes changes
Sandbox lifecycle
Before diving into the code, it’s important to understand how sandboxes are scoped. The scoping strategy determines who shares a sandbox, how long it lives, and how it’s resolved at runtime.Thread-scoped sandbox (recommended)
Each LangGraph thread gets its own sandbox. The sandbox ID is stored in the thread’s metadata and resolved via the backend factory’sruntime.configurable.thread_id.
This is the recommended approach for most applications:
- Conversations are isolated — file changes in one thread don’t affect another
- Sandbox state persists across page reloads (same thread = same sandbox)
- Cleanup is straightforward: when a thread is deleted, its sandbox can be too
Agent-scoped sandbox
All threads under the same assistant share a single sandbox. Useful for persistent project environments where you want changes to carry across conversations:User-scoped sandbox
Each user gets their own sandbox across all threads. Requires custom authentication and user identification:Session-scoped sandbox (client-side)
For simpler apps without LangGraph threads, the frontend can generate a session ID and pass it directly. This approach doesn’t persist across browser sessions and is best for demos or prototyping:Setting up the agent
Choose a sandbox provider
Deep Agents supports multiple sandbox providers. Any provider that implements theSandboxBackendProtocol works:
read_file, write_file,
edit_file, ls, glob, grep) and an execute tool for running shell
commands. No tool configuration needed.
Use a backend factory for thread-scoped sandboxes
Instead of creating a sandbox at module level (which would be shared across all threads and may expire), use a backend factory that resolves the : sandbox per-thread at runtime. The factory receives aBackendRuntime object
with configurable.thread_id from the current LangGraph run:
Seed the sandbox
Before the agent runs, populate the sandbox with your project files usinguploadFiles:
For LangSmith sandboxes, the container image and resource limits come from a
sandbox template. Pass
templateName when creatingthe sandbox (see getOrCreateSandboxForThread above). uploadFiles seeds or updatesproject files at runtime on top of that image.Adding the file browsing API
The agent can read and write files, but the frontend also needs direct access to browse the sandbox filesystem. Add a custom Hono API server and expose it through thehttp.app field in langgraph.json.
Create the API server
The sandbox API endpoints use the thread ID as a URL path parameter. This ensures the frontend always accesses the correct sandbox for the current conversation, using the samegetOrCreateSandboxForThread function as the
agent’s backend factory:
Both the agent’s backend factory and the API server call the same
getOrCreateSandboxForThread function. This ensures they always resolveto the same sandbox for a given thread. The sandbox ID in thread metadata
is the single source of truth — no in-memory caches needed.Configure langgraph.json
Register both the agent graph and the API server. The http.app field tells
the LangGraph platform to serve your custom routes alongside the default ones:
langgraph dev, that’s http://localhost:2024.
Custom routes defined in
http.app take priority over default LangGraph routes. This means you
can shadow built-in endpoints if needed, but be careful not to accidentally override routes like
/threads or /runs.Building the frontend
The frontend has three panels: a file tree sidebar, a code/diff viewer, and a chat panel. It usesuseStream for the agent conversation and the custom API
endpoints for file browsing.
Thread creation
Create a LangGraph thread when the page loads and persist its ID insessionStorage so page reloads reconnect to the same sandbox:
File state management
Track two snapshots of the sandbox filesystem: the original state (before the agent runs) and the current state (updated in real time). The thread ID is included in the API URL so requests always hit the correct sandbox:Real-time file sync
The key to the IDE experience is updating files as the agent works, not after it finishes. Watch the stream’s messages forToolMessage instances
from file-mutating tools. When a write_file or edit_file tool call
completes, refresh that specific file. When execute completes, refresh
everything (since a shell command could modify any file):
Detecting changed files
Before each agent run, snapshot the current file contents. After files refresh, compare against the snapshot to identify which files changed:Displaying diffs
Use a framework-appropriate diff library to render unified diffs:| Framework | Library | Component |
|---|---|---|
| React | @pierre/diffs | <FileDiff> with parseDiffFromFile |
| Vue | @git-diff-view/vue | <DiffView> with generateDiffFile from @git-diff-view/file |
| Svelte | @git-diff-view/svelte | <DiffView> with generateDiffFile from @git-diff-view/file |
| Angular | ngx-diff | <ngx-unified-diff> with [before] and [after] |
@pierre/diffs (React):
Changed files summary
Show a summary of all modified files with line-level addition/deletion counts. This gives users a quick overview of the agent’s impact — similar to agit status:
The three-panel layout
The IDE layout arranges three panels side by side:| Panel | Width | Purpose |
|---|---|---|
| File tree | Fixed (208px) | Browse sandbox files, see change indicators |
| Code / Diff | Flexible | View file content or unified diff |
| Chat | Fixed (320px) | Interact with the agent |
@iconify-json/vscode-icons)
and amber dots on modified files. Selecting a modified file automatically
switches to the diff tab.
Use cases
A sandbox is the right choice when:- Coding agents that create, modify, and run code need a visual interface beyond chat
- Code review workflows where the agent suggests changes and the user reviews diffs before accepting
- Tutorial or learning apps where an AI assistant helps users build a project step by step, showing changes in context
- Prototyping tools where users describe features in natural language and watch the agent implement them in real time
Best practices
- Use thread-scoped sandboxes for production apps. Store the sandbox ID in
thread metadata and resolve it via the backend factory’s
runtime.configurable.thread_id. This avoids module-level state and keeps sandboxes isolated per conversation. - Share
getOrCreateSandboxForThreadbetween the agent backend factory and the API server. Both should resolve the sandbox the same way — via thread metadata — so there’s a single source of truth with no in-memory caches. - Persist
threadIdinsessionStorageso page reloads reconnect to the same thread and sandbox instead of creating new ones. - Sync files on every relevant tool call, not just when the run finishes. This
makes the IDE feel live. Watch for
write_file,edit_file, andexecutetool messages and refresh immediately. - Default to diff view for changed files. When a user clicks a file that was modified by the agent, show the diff first — that’s what they care about.
- Show compact tool results for read-only operations. Instead of dumping
the full output of
read_filein the chat, show a one-liner likeRead router.js L1-42. Reserve the full output display for mutating tools. - Seed the sandbox with a real project. Starting from an empty sandbox is disorienting. Upload a working starter project so users (and the agent) have context immediately.
- Filter
node_modulesfrom the file tree. Nobody wants to browse thousands of dependency files. Filter them out when fetching the tree.
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

