Skip to content

Workspaces

Workspaces give your agent a typed I/O surface for files, shell commands, code execution, and snapshots — all auto-injected as tools the LLM can call. Pluggable providers back the surface with different storage and execution models.

Why workspaces

Without workspaces, every agent that needs to manipulate files or run code has to define its own bespoke tools. That means duplicated tool implementations, inconsistent semantics across agents, and no path to swap "in-memory for tests" with "real container for prod."

Workspaces solve that by:

  • Decoupling capability from backing store. Declare what your agent needs (fs, shell, code, snapshot); the framework injects matching tools and wires them to whichever provider you configure.
  • Auto-injecting LLM tools. A workspace named box with fs: true produces workspace__box__read_file, workspace__box__write_file, workspace__box__ls, etc., automatically. No bespoke tool code needed.
  • Surviving runtime boundaries. The framework persists serializable refs to your provider's storage so sessions resume cleanly across DO hibernation, Temporal replay, or process restarts.

The four built-in providers

ProviderBackingModulesUse case
In-MemoryJavaScript MapfsTests, dev, ephemeral agents. No persistence.
Local Bashtmpdir + POSIX shellfs, shellLocal development on POSIX systems. Not for production (no isolation).
Cloudflare FilestoreDurable Object SQLite + optional R2fsLightest CF option for durable file storage. No container, no cold start.
Cloudflare SandboxWorkers Container (Firecracker microVM)fs, shell, code, snapshotFull Linux container for code execution. Real shell, Python/JS interpreter, R2-backed snapshots.

See per-provider pages for setup, capabilities, and lifecycle details.

Decision matrix

If you need...Use
Tests / dev / no persistenceIn-Memory
Local POSIX dev + real shellLocal Bash
Durable file storage on Cloudflare DOCloudflare Filestore
Code execution / shell on CloudflareCloudflare Sandbox

Quick start

The simplest possible workspace — in-memory, fs only, on the JS runtime:

typescript
import { defineAgent } from '@helix-agents/core';
import { JSAgentExecutor } from '@helix-agents/runtime-js';
import { InMemoryStateStore, InMemoryStreamManager } from '@helix-agents/store-memory';
import { InMemoryWorkspaceProvider } from '@helix-agents/workspace-memory';

const agent = defineAgent({
  name: 'file-writer',
  llmConfig: { model: yourModel },
  workspaces: {
    notes: {
      provider: { kind: 'in-memory' },
      capabilities: { fs: true },  // → injects workspace__notes__read_file, write_file, etc.
    },
  },
});

const executor = new JSAgentExecutor(
  new InMemoryStateStore(),
  new InMemoryStreamManager(),
  yourLLMAdapter,
  { workspaceProviders: new Map([['in-memory', new InMemoryWorkspaceProvider()]]) }
);

await executor.execute(agent, { message: 'Write a poem to /poem.txt' }, { sessionId: 'demo' });

Three things going on:

  1. workspaces.notes declares a workspace named notes. The agent's LLM sees auto-injected tools prefixed workspace__notes__*.
  2. provider: { kind: 'in-memory' } picks the provider. The discriminator (kind) matches the registered provider's id.
  3. workspaceProviders on the executor registers provider instances. The executor calls provider.open(config, session) when the agent first uses a workspace tool.

Capability config

Capabilities are declared per-workspace. Each capability accepts either true (defaults) or an object with policy options:

typescript
workspaces: {
  box: {
    provider: { kind: 'cloudflare-sandbox' },
    capabilities: {
      fs: { maxFileSizeMb: 10 },           // policy-style
      shell: { allowedCommands: ['ls', 'cat'] },
      code: { languages: ['python'], isStateful: true },
      snapshot: true,
    },
  },
},

A few rules:

  • A capability set to true (or an object) → the framework auto-injects matching LLM tools.
  • A capability set to false (or omitted) → no tools injected. The LLM literally cannot call them.
  • Capability config drives BOTH which tools get injected AND which policies apply at the tool layer (allowlists, max sizes, etc.). Provider configuration is separate (provider-side options live under provider).

See per-module pages for full capability config schemas:

Auto-injected tools

For a workspace named box with fs: true, the LLM sees these tools (a subset based on the module):

  • workspace__box__read_file(path)
  • workspace__box__write_file(path, content)
  • workspace__box__edit_file(path, oldText, newText)
  • workspace__box__ls(path)
  • workspace__box__glob(pattern)
  • workspace__box__grep(pattern, opts?)
  • workspace__box__stat(path)
  • workspace__box__mkdir(path, opts?)
  • workspace__box__rm(path, opts?)

When shell: true is added: workspace__box__run(command, opts?).

When code: { languages, isStateful } is added: workspace__box__run_code(language, code). With isStateful: true, three more: create_code_context, run_in_code_context, delete_code_context.

When snapshot: true is added: workspace__box__snapshot() and workspace__box__restore(ref). If the provider implements branch?, workspace__box__branch(ref) too.

Lifecycle

A workspace's life cycle:

  1. Declared in the agent config (defineAgent({ workspaces: { ... } })).
  2. Opened lazily on first tool use — the framework calls provider.open(config, session).
  3. Used by the LLM via auto-injected tools, which dispatch through the runtime to the live Workspace instance.
  4. Refed — the framework persists a serializable WorkspaceRef returned by open() so it can reattach later.
  5. Resolved after a runtime boundary (DO hibernation, Temporal replay, executor restart) via provider.resolve(ref).
  6. Closed at session end via workspace.close().

Different providers handle (1)–(6) differently — see per-provider pages.

Runnable example

The Workspaces Showcase example runs the same agent against all four providers via env-var dispatch. Single source of truth for "what does each provider feel like in code".

For a real-world integration story, see the Research Assistant (Cloudflare DO) example — a production-shape agent that adopts CloudflareFileStoreWorkspace to persist research notes durably. The example's README walks through a BEFORE/AFTER migration.

Next steps

  • Pick a provider based on your runtime + persistence needs (decision matrix above).
  • Read the per-provider page for setup specifics (wrangler config, DO bindings, Dockerfiles where applicable).
  • Read per-module pages to understand the auto-injected tool surface and capability config options.
  • Building your own provider? Start with Building a Provider.

Released under the MIT License.