UNPKG

@mastra/core

Version:

Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack.

360 lines (265 loc) 15.8 kB
# Workspaces **Added in:** `@mastra/core@1.1.0` A Mastra workspace gives agents a persistent environment for storing files and executing commands. Agents use workspace tools to read and write files, run shell commands, and search indexed content. A workspace supports the following features: - **[Filesystem](https://mastra.ai/docs/workspace/filesystem)**: File storage (read, write, list, delete, copy, move, grep) - **[Sandbox](https://mastra.ai/docs/workspace/sandbox)**: Command execution (shell commands) and background processes - **[LSP inspection](https://mastra.ai/docs/workspace/lsp)**: Hover, definition, and implementation queries through language servers - **[Search](https://mastra.ai/docs/workspace/search)**: BM25, vector, or hybrid search over indexed content - **[Skills](https://mastra.ai/docs/workspace/skills)**: Reusable instructions for agents ## When to use workspaces Use a workspace when your agent needs access to the local filesystem, shell commands, semantic code inspection, indexed search, or reusable skill instructions. ## How it works When you assign a workspace to an agent, Mastra includes the corresponding tools in the agent's toolset. The agent can then use these tools to interact with files and execute commands. You can create a workspace with any combination of the supported features. The agent receives only the tools relevant to what's configured. ## Usage ### Creating a workspace Create a workspace by instantiating the `Workspace` class with your desired features: ```typescript import { Workspace, LocalFilesystem, LocalSandbox } from '@mastra/core/workspace' const workspace = new Workspace({ filesystem: new LocalFilesystem({ basePath: './workspace', }), sandbox: new LocalSandbox({ workingDirectory: './workspace', }), skills: ['skills'], }) ``` The `skills` array specifies paths to directories containing skill definitions, see [Skills](https://mastra.ai/docs/workspace/skills). ### Global workspace Set a workspace on the Mastra instance. All agents inherit this workspace unless they define their own: ```typescript import { Mastra } from '@mastra/core' import { Workspace, LocalFilesystem } from '@mastra/core/workspace' const workspace = new Workspace({ filesystem: new LocalFilesystem({ basePath: './workspace' }), }) const mastra = new Mastra({ workspace, }) ``` ### Agent-level workspace Assign a workspace directly to an agent to override the global workspace: ```typescript import { Agent } from '@mastra/core/agent' import { Workspace, LocalFilesystem } from '@mastra/core/workspace' const workspace = new Workspace({ filesystem: new LocalFilesystem({ basePath: './agent-workspace' }), }) export const myAgent = new Agent({ id: 'my-agent', model: 'openai/gpt-5.4', workspace, }) ``` ## Configuration patterns Workspaces support several configuration patterns depending on what capabilities your agent needs. The two main building blocks are `filesystem` (file tools) and `sandbox` (command execution), with `mounts` as the way to bridge cloud storage into sandboxes. ### Filesystem + sandbox (local) For local development, pair a `LocalFilesystem` and `LocalSandbox` pointed at the same directory. Since both operate on the local machine, files written through the filesystem are immediately available to commands in the sandbox: ```typescript const workspace = new Workspace({ filesystem: new LocalFilesystem({ basePath: './workspace' }), sandbox: new LocalSandbox({ workingDirectory: './workspace' }), }) ``` The agent receives both file tools and `execute_command`. This is the simplest full-featured setup. ### Mounts + sandbox (cloud storage) When you need cloud storage accessible inside a sandbox, use `mounts`. This FUSE-mounts the cloud filesystem into the sandbox so commands can read and write files at the mount path: ```typescript const workspace = new Workspace({ mounts: { '/data': new S3Filesystem({ bucket: 'my-bucket', region: 'us-east-1', accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }), '/skills': new GCSFilesystem({ bucket: 'agent-skills', }), }, sandbox: new E2BSandbox({ id: 'dev-sandbox' }), }) ``` Under the hood, `mounts` creates a [CompositeFilesystem](https://mastra.ai/docs/workspace/filesystem) that routes file tool operations to the correct provider based on path prefix. Commands in the sandbox access the mounted paths directly (e.g., `ls /data`). You can mount multiple providers at different paths. Each mount path must be unique and non-overlapping. > **Note:** `filesystem` and `mounts` are mutually exclusive — you can't use both in the same workspace. Use `filesystem` for a single provider without a sandbox, or `mounts` when you need to combine cloud storage with a sandbox. ### Filesystem only Use a single `filesystem` when agents only need to read and write files. No command execution is available. ```typescript const workspace = new Workspace({ filesystem: new S3Filesystem({ bucket: 'my-bucket', region: 'us-east-1', accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }), }) ``` The agent receives file tools (`read_file`, `write_file`, `list_directory`, `grep`, etc.) that operate directly against the storage provider. ### Sandbox only Use a single `sandbox` when agents only need to execute commands. No file tools are added. ```typescript const workspace = new Workspace({ sandbox: new E2BSandbox({ id: 'dev-sandbox' }), }) ``` The agent receives the `execute_command` tool. ### Dynamic filesystem (per-request) Pass a resolver function to `filesystem` to return a different filesystem per request. This is useful for multi-tenant applications or multi-role agents where each request needs a different storage root or different permissions. ```typescript const workspace = new Workspace({ filesystem: ({ requestContext }) => { const role = requestContext.get('agent-role') || 'guest' return new LocalFilesystem({ basePath: `/workspaces/${role}`, readOnly: role !== 'admin', }) }, }) ``` One workspace instance serves all requests. The resolver runs at tool execution time, so each request gets its own filesystem. See [dynamic filesystem](https://mastra.ai/docs/workspace/filesystem) for details. ### Which pattern should I use? | Scenario | Pattern | | --------------------------------------------------------- | ----------------------------------------------------- | | Local development with files and commands | `filesystem` + `sandbox` (both local, same directory) | | Cloud storage accessible inside a cloud sandbox | `mounts` + `sandbox` | | Multiple cloud providers in one sandbox | `mounts` + `sandbox` (one mount per provider) | | Agent reads/writes files, no command execution needed | `filesystem` only | | Agent runs commands, no file tools needed | `sandbox` only | | Multi-role or multi-tenant agent with per-request storage | `filesystem` with resolver function | ## Tool configuration Configure tool behavior through the `tools` option on the workspace. This controls which tools are enabled and how they behave. ```typescript import { Workspace, LocalFilesystem, LocalSandbox, WORKSPACE_TOOLS } from '@mastra/core/workspace' const workspace = new Workspace({ filesystem: new LocalFilesystem({ basePath: './workspace' }), sandbox: new LocalSandbox({ workingDirectory: './workspace' }), tools: { // Global defaults enabled: true, requireApproval: false, // Per-tool overrides [WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE]: { requireApproval: true, requireReadBeforeWrite: true, }, [WORKSPACE_TOOLS.FILESYSTEM.DELETE]: { enabled: false, }, [WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: { requireApproval: true, }, }, }) ``` ### Tool options | Option | Type | Description | | ------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `enabled` | `boolean \| (context) => boolean` | Whether the tool is available (default: `true`). When a function, evaluated at tool-listing time. | | `requireApproval` | `boolean \| (context) => boolean` | Whether the tool requires user approval before execution (default: `false`). When a function, evaluated at execution time with access to `args`. | | `requireReadBeforeWrite` | `boolean \| (context) => boolean` | For write tools: require reading the file first (default: `false`). When a function, evaluated at execution time with access to `args`. | | `name` | `string` | Custom name for the tool. Replaces the default `mastra_workspace_*` name. | | `maxOutputTokens` | `number` | Maximum tokens for tool output (default: `2000`). Output exceeding this limit is truncated using tiktoken. | ### Dynamic tool configuration Tool options that accept functions receive a context object and return a boolean. This enables context-aware tool behavior. ```typescript const workspace = new Workspace({ filesystem: new LocalFilesystem({ basePath: './workspace' }), tools: { // Dynamic enabled: disable command execution unless explicitly allowed [WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: { enabled: async ({ requestContext }) => { return requestContext['allowExecution'] === 'true' }, }, // Dynamic requireApproval: only require approval for protected paths [WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE]: { requireApproval: async ({ args }) => { return (args.path as string).startsWith('/protected') }, requireReadBeforeWrite: true, }, }, }) ``` Functions for `enabled` receive `{ requestContext, workspace }`. Functions for `requireApproval` and `requireReadBeforeWrite` also receive `args` since they are evaluated when the tool is called. ### Tool name remapping Rename workspace tools to match the conventions your agent expects. The config key remains the original `WORKSPACE_TOOLS` constant — only the exposed name changes. ```typescript import { Workspace, LocalFilesystem, LocalSandbox, WORKSPACE_TOOLS } from '@mastra/core/workspace' const workspace = new Workspace({ filesystem: new LocalFilesystem({ basePath: './workspace' }), sandbox: new LocalSandbox({ workingDirectory: './workspace' }), lsp: true, tools: { [WORKSPACE_TOOLS.FILESYSTEM.READ_FILE]: { name: 'view' }, [WORKSPACE_TOOLS.FILESYSTEM.GREP]: { name: 'search_content' }, [WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES]: { name: 'find_files' }, [WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: { name: 'execute_command' }, [WORKSPACE_TOOLS.LSP.LSP_INSPECT]: { name: 'lsp_inspect' }, }, }) ``` The agent sees `view`, `search_content`, `find_files`, `execute_command`, and `lsp_inspect` instead of the default `mastra_workspace_*` names. Tool names must be unique — duplicate names or conflicts with other default names throw an error. ## LSP inspection Enable `lsp` on a workspace to add semantic code inspection through language servers. This adds the `mastra_workspace_lsp_inspect` tool by default, which can return hover information, definition locations, and implementations for a symbol at a specific cursor position. See [LSP inspection](https://mastra.ai/docs/workspace/lsp) for configuration, examples, and tool name remapping. ### Output truncation Workspace tools automatically truncate large outputs to avoid exceeding LLM context limits. Two layers of truncation apply: 1. **Line-based tail**: Command output is limited to the last 200 lines by default (configurable per-command via the `tail` parameter) 2. **Token-based limit**: Tool output is capped at 2000 tokens by default Set `maxOutputTokens` per tool to adjust the token limit: ```typescript const workspace = new Workspace({ // ... tools: { [WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: { maxOutputTokens: 5000, }, }, }) ``` ANSI escape codes (colors, cursor sequences) are automatically stripped from command output before it reaches the model. ### Read-before-write When `requireReadBeforeWrite` is enabled on write tools, agents must read a file before writing to it. This prevents overwriting files the agent hasn't seen: - **New files**: Can be written without reading (they don't exist yet) - **Existing files**: Must be read first - **Externally modified files**: If a file changed since the agent read it, the write fails File write safety is enforced at two layers: 1. **Tool layer**: Before a write tool runs, the read tracker checks whether the file was modified since it was last read. If it was, the tool throws a `FileReadRequiredError`. 2. **Filesystem layer**: At write time, `writeFile()` compares the file's current modification time against the expected value (passed via `expectedMtime` in write options). If they don't match, it throws a `StaleFileError`. This catches external modifications (for example, an editor saving the file) that happen between the tool-level check and the actual write. When `requireReadBeforeWrite` is enabled, workspace tools pass the recorded modification time through automatically. You can also use `expectedMtime` directly when calling `filesystem.writeFile()` outside of tools: ```typescript const stat = await filesystem.stat('/docs/file.md') // ... later ... await filesystem.writeFile('/docs/file.md', newContent, { expectedMtime: stat.modifiedAt, }) ``` ## Initialization Calling `init()` is optional in most cases—some providers initialize on first operation. Call `init()` manually when using a workspace outside of Mastra (standalone scripts, tests) or when you need to pre-provision resources before the first agent interaction. ```typescript import { Workspace, LocalFilesystem, LocalSandbox } from '@mastra/core/workspace' const workspace = new Workspace({ filesystem: new LocalFilesystem({ basePath: './workspace' }), sandbox: new LocalSandbox({ workingDirectory: './workspace' }), }) // Optional: pre-create directories and sandbox before first use await workspace.init() ``` ### What `init()` does Initialization runs setup logic for each configured provider: - `LocalFilesystem`: Creates the base directory if it doesn't exist - `LocalSandbox`: Creates the working directory - `Search` (if configured): Indexes files from `autoIndexPaths`, see [Search and Indexing](https://mastra.ai/docs/workspace/search) External providers may perform additional setup like establishing connections or authenticating. ## Related - [Filesystem](https://mastra.ai/docs/workspace/filesystem) - [Sandbox](https://mastra.ai/docs/workspace/sandbox) - [LSP inspection](https://mastra.ai/docs/workspace/lsp) - [Skills](https://mastra.ai/docs/workspace/skills) - [Search and indexing](https://mastra.ai/docs/workspace/search) - [Workspace class reference](https://mastra.ai/reference/workspace/workspace-class)