Agent Sandbox Runtime#
The in-app agent sandbox is a containerized code execution environment that gives the Langfuse in-app agent isolated access to filesystem operations (read, write, edit) and shell commands (bash). It is part of the EE in-app-agent feature and underpins the agent's ability to safely perform multi-step agentic tasks without affecting the host process.
Current location (post-refactor): packages/shared/src/in-app-agent/server/sandbox/
Original location (before refactor): web/src/ee/features/in-app-agent/server/sandbox/
Architecture#
The sandbox is structured into three layers:
Sandbox Runtime Package#
packages/in-app-agent-sandbox-runtime/ is a standalone Node.js package that runs as a lightweight HTTP server (port 5000) inside the sandbox container/VM. It exposes a single /sandbox operation endpoint for the four tool operations, plus a health check endpoint .
Key files:
src/server.ts— HTTP control server; handles path validation,read/write/edit/bashdispatch, and enforces a 10 MB request body limitsrc/contracts.ts— Zod schemas for operation validationDockerfile— Alpine Node.js image running as unprivilegedsandbox-serveruser;/workspaceis writable,/workspace/tool_callsis recreated fresh before each operation
Provider Implementations#
The sandbox uses a SandboxProvider / SandboxSession interface to abstract two backends :
| Provider | Use | Mechanism |
|---|---|---|
dangerous-docker | Dev-only | Launches isolated Docker containers from the sandbox runtime image; communicates via HTTP on localhost:5000 |
lambda-microvm | Production | Starts AWS Lambda MicroVMs from a pre-built image; communicates via AWS-assigned HTTPS endpoint with X-aws-proxy-auth token |
Provider is selected via the LANGFUSE_IN_APP_AGENT_SANDBOX_PROVIDER environment variable . Additional Lambda env vars: LANGFUSE_IN_APP_AGENT_SANDBOX_AWS_LAMBDA_MICROVM_IMAGE_IDENTIFIER, LANGFUSE_IN_APP_AGENT_SANDBOX_AWS_LAMBDA_MICROVM_EXECUTION_ROLE_ARN, LANGFUSE_IN_APP_AGENT_SANDBOX_AWS_LAMBDA_MICROVM_REGION.
Sandbox Service Layer#
sandbox/service.ts manages the per-conversation lifecycle :
ensureSession()— creates or resumes a session byproviderSessionIdsyncReadonlyFiles()— rebuilds/workspace/tool_calls/*.jsonfrom prior tool call events before each turnonTurnEnded()— persists the session ID back to the conversation record in the database
Process Isolation and Security#
Container/VM isolation: Each sandbox runs in a fully isolated environment — a Docker container (dev) or Lambda MicroVM (prod). Containers run unprivileged and are recreated if stale. Lambda MicroVMs support session suspend/resume and automatic auth token refresh .
Path escaping: All file operation paths are validated via resolveSandboxPath() in the runtime server. The function resolves relative paths to /workspace, normalizes them, and rejects any path that escapes the workspace root .
Request body limits: The runtime's readJsonBody enforces a 10 MB ceiling to prevent unbounded memory buffering and process crashes from oversized payloads .
Write-lock mechanism: Sandbox conversations are read-locked 8 hours after creation. The handler checks isInAppAgentConversationWriteLocked() before each new turn and rejects it with HTTP 412 if the lock is active. This prevents data loss from long-lived concurrent sessions .
Known limitations (from code review at initial PR #14712):
- The
editoperation usesString.prototype.replace(), which silently replaces only the first occurrence. IfoldTextappears multiple times, the agent receives no ambiguity signal . - Docker provider uses dynamic imports inside function bodies (lazy-loading for dev-only isolation), deviating from the team's module-level import convention .
Session and State Management#
Sessions are stateless at the HTTP level. The providerSessionId (and sandboxProvider enum) are persisted on the InAppAgentConversation database record so sessions can be resumed across requests without in-memory state .
Before each turn, prior non-sandbox tool call results are reconstructed from persisted events by getSandboxToolCallFiles() in persistence.ts and synced into /workspace/tool_calls/ as read-only JSON files. This gives the sandbox agent consistent context regardless of provider or whether the session was resumed .
The operation flow per turn is:
- Handler loads prior events → calls
getSandboxToolCallFiles() - Sandbox service calls
syncReadonlyFiles()on the provider session - Agent invokes a sandbox tool → provider POSTs to the runtime HTTP server
- Runtime validates path, executes the operation, returns result
- Handler calls
onTurnEnded()→ persists updatedproviderSessionIdviasaveState()
Architectural Evolution: Standalone to packages/shared#
PR #15421 refactored the entire in-app-agent runtime — including sandbox infrastructure — from web/src/ee/features/in-app-agent/ into packages/shared/src/in-app-agent/ as 23 renamed files with no behavior changes.
Motivation: The background-execution RFC requires the agent executor to run in a worker. Rather than maintaining two copies of the ~6k-line agent loop during the canary window, the runtime is centralized once in shared. Web imports from there today; the worker's background processor will import the same modules .
Module boundary enforcement:
@langfuse/shared/in-app-agent— client-safe contracts only (Zod schemas, constants, id helpers)@langfuse/shared/in-app-agent/server(+ per-module subpaths) — runtime code; never re-exported through the client entry- CI's
pnpm run scan:client-bundleguards this boundary automatically
Other changes in this refactor:
- The system prompt moved from a
.txtasset to a checked-in TypeScript module, enabling compilation under plaintscwithout a bundler — required for worker compatibility LANGFUSE_IN_APP_AGENT_SANDBOX_*andAWS_PROFILEenv vars moved into the shared env schema- Web's
vitest.config.mtswas updated to alias all@langfuse/sharedentry points to source files (not dist), preventingvi.mockbypass and split-brain module instances in tests - A pnpm alias
ai-sdk-amazon-bedrock-v4handles a version conflict between Mastra's pinned@ai-sdk/amazon-bedrock@4.0.81and shared's^5
Developer impact: Editing runtime/sandbox code now requires working in packages/shared and running tsc --watch there (already part of pnpm run dev) .