Local Bash Workspace
The LocalBashWorkspace runs files in a real tmpdir on the host filesystem and runs shell commands as subprocesses. POSIX-only. Suitable for local development and testing where you want real filesystem semantics + the ability to shell out, without spinning up a container.
When to use
- Local POSIX development. When you want files to persist across reads/writes within a session AND you need actual shell commands (
grep,find,git, etc.). - Integration tests that exercise real filesystem behavior — file modes, symlink behavior, glob expansion, etc.
- Trusted code only. This provider runs as the host user with full host privileges. Do NOT use with untrusted input.
For untrusted code, use Cloudflare Sandbox (Firecracker microVM isolation).
Capabilities supported
| Capability | Supported |
|---|---|
fs | ✅ |
shell | ✅ |
code | ❌ |
snapshot | ❌ |
The provider advertises { fs: true, shell: true } on its WorkspaceRef.capabilities.
POSIX-only
Windows is not supported. The provider throws at open() time with a clear error message:
LocalBashWorkspaceProvider: Windows is not supported. Use WSL and run the agent inside it.Windows users should run their agents inside WSL.
Sandbox boundaries
The workspace's filesystem is scoped to a per-session tmpdir created via fs.mkdtemp() under os.tmpdir() (configurable via tmpdirRoot). The internal TmpdirFileSystem enforces the boundary using:
realpathSynccanonicalization on the tmpdir root at construction time.- Ancestor-walk symlink resolution on every fs operation — every component of the requested path is resolved through
realpathbefore access, ensuring no symlink can escape the tmpdir.
This protects against deliberate symlink-escape attacks within the fs methods. It does NOT protect against:
- Untrusted shell commands. Once the LLM calls
workspace__<name>__run('rm -rf /'), the boundary is gone — the shell runs as the host user. - Race conditions between symlink creation and use (TOCTOU). The ancestor walk happens immediately before the operation but is not atomic.
- Code that calls fs APIs outside the workspace (e.g., a custom tool that bypasses the registry).
If you need true isolation against untrusted input, use Cloudflare Sandbox.
Provider config
interface LocalBashWorkspaceConfig {
kind: 'local-bash';
}
interface LocalBashProviderOptions {
/** Override the tmpdir root. Defaults to os.tmpdir(). */
tmpdirRoot?: string;
/** Constraints applied to subprocess shell calls. */
shellConstraints?: SubprocessShellConstraints;
}shellConstraints controls behaviors like maxStdoutBytes, cwd, env, signal, timeoutMs. See the package source for the full type.
Wiring
import * as os from 'node:os';
import { defineAgent } from '@helix-agents/core';
import { JSAgentExecutor } from '@helix-agents/runtime-js';
import { InMemoryStateStore, InMemoryStreamManager } from '@helix-agents/store-memory';
import { LocalBashWorkspaceProvider } from '@helix-agents/workspace-local-bash';
const agent = defineAgent({
name: 'my-agent',
llmConfig: { model: yourModel },
workspaces: {
box: {
provider: { kind: 'local-bash' },
capabilities: { fs: true, shell: true },
},
},
});
const executor = new JSAgentExecutor(
new InMemoryStateStore(),
new InMemoryStreamManager(),
yourLLMAdapter,
{
workspaceProviders: new Map([
['local-bash', new LocalBashWorkspaceProvider({ tmpdirRoot: os.tmpdir() })],
]),
}
);Lifecycle
open()— callsfs.mkdtemp()to create a per-session tmpdir prefixhelix-ws-{sanitizedSessionId}-{random}. Returns theLocalBashWorkspaceand a serializable ref{ tmpdir, workspaceId }.resolve()— re-attaches to the same tmpdir if it still exists. If the tmpdir has been cleaned (process exit, another session's close, tmpfs clear), throwsWorkspaceEvictedErrorso the executor'swithEvictionRetrycan re-open viaopen().close()— removes the tmpdir recursively. Files are gone after close.
Auto-injected tools
All fs tools, plus workspace__<name>__run for shell. See FileSystem and Shell module pages for schemas.
Production unsuitability warning
⚠️ This provider runs commands as the host user with full host privileges. Never use it with untrusted input or in any context where the LLM could be prompted to attack the host. For production code-execution agents, use
CloudflareSandboxWorkspace(Firecracker microVM) or another container-based provider.