Technical docs

Runtime Tooling For AI Teammates

Overview

Support-facing reference for the runtime tools exposed by `backend/src/components/ai/tools/mastra/ToolsProvider.ts`.

Tool sets

  • Node tools (`NODE_TOOLS_LIST`): `http.http_request` and `stargate.execute_task`. This is the publish-time catalog for plain tool nodes, not the Alloy AI runtime allowlist.
  • Base agent tools (`BASE_AGENT_TOOLS_LIST`): `http.http_request`, media URL helpers, `web.check_urls`, `programming.run_code`, `contact.update_contact`, `conversation.escalate_conversation`, `workflows.list`, scheduler tools, and `stargate.execute_task`. Storage tools are no longer part of the base or default lists; they are workspace-scoped and appended only when the AI teammate has a storage workspace.
  • Agent tools (`AGENT_TOOLS_LIST`): base agent tools plus `agent.run_subagent`.
  • Full non-system tools (`TOOLS_LIST`): agent tools plus `contact.send_message`.
  • Virtual tools (`VIRTUAL_TOOLS_LIST`): `web.search` only. It is added only when the selected provider exposes a native search tool.
  • System-only tools (`SYSTEM_TOOLS_LIST`): `system.employee_create`, `system.employee_update`, `system.employee_get`, `system.employees_list`, `system.create_workflow`, `system.update_workflow`, `system.delete_workflow`, `system.validate_workflow`, `system.patch_workflow`, `system.read_workflow`, `system.workflows_list`, `system.test_workflow`, `system.get_workflow_run`, `system.get_workflow_run_step_log`, `system.do_organization_action`, `system.list_documentation`, `system.search_documentation`, and `system.read_documentation`.
  • System tools are exposed only when `organization.is_system` is true.
  • Conversation-scoped tools (`CONVERSATION_SCOPED_TOOLS_LIST`): `conversation.list_spreadsheet_attachments`, `conversation.read_spreadsheet_attachment`, `conversation.read_document_attachment`, `conversation.save_attachment_to_storage`, `conversation.message_list`, and `conversation.message_details`. They are appended only when the workflow run has a conversation.
  • Workspace sandbox tools (`WORKSPACE_SANDBOX_SCOPED_TOOLS_LIST`): `mastra_workspace.sandbox_start`, `mastra_workspace.sandbox_execute_command`, `mastra_workspace.sandbox_import`, `mastra_workspace.sandbox_export`, and `mastra_workspace.sandbox_stop`. They are appended only when the AI teammate has sandbox mode enabled (`is_sandbox_enabled`).
  • Workspace filesystem tools (`WORKSPACE_FILESYSTEM_SCOPED_TOOLS_LIST`): the `storage.*` tools (`list`, `search`, `stat_file`, `read_file`, `write_file`, `write_file_binary`, `edit_file`, `rename`, `copy`, `move`, `delete`, `get_temp_file_url`, `semantic_search`). They are appended when the teammate has a storage filesystem, meaning at least one shared folder is granted (system employees always qualify).

Eager tools vs on-demand tools

  • Built-in Alloy tools carry an `isEager` flag.
  • Organization settings include the `use_lazy_tools` switch, enabled by default.
  • When lazy tools are enabled:
  • eager tools are exposed to the model from step `0`
  • non-eager built-in tools are registered on the agent but hidden until the model loads them through `tools.load_tool`
  • assigned upstream MCP/OAuth toolset tools use the same on-demand path
  • if the current tool set has any on-demand tools, the runtime injects `tools.load_tool` as an eager helper
  • When lazy tools are disabled, configured built-in tools and assigned MCP/OAuth tools are exposed directly to the model instead of being placed behind the on-demand loader.
  • `tools.load_tool` takes `tool_name` as the raw Alloy tool id and returns `{ success, message, data: { id } }` on success.
  • After a successful `tools.load_tool` call, the loaded tool becomes active for later steps in the same run.
  • Tool matching uses sanitized runtime names internally, but `tools.load_tool` still expects the original tool id string.

Current eager built-ins

  • Current built-in tools explicitly marked eager in code include:
  • `programming.run_code`
  • `storage.list`
  • `storage.search`
  • `storage.stat_file`
  • `storage.read_file`
  • `storage.write_file`
  • `storage.write_file_binary`
  • `storage.edit_file`
  • `storage.semantic_search`
  • `system.list_documentation`
  • `system.search_documentation`
  • `system.read_documentation`
  • `conversation.read_document_attachment`
  • `conversation.save_attachment_to_storage`
  • `conversation.message_list`
  • `conversation.message_details`
  • `mastra_workspace.sandbox_start`
  • `mastra_workspace.sandbox_execute_command`
  • `mastra_workspace.sandbox_import`
  • `mastra_workspace.sandbox_export`
  • `mastra_workspace.sandbox_stop`
  • Dynamic `workflow.*` tools are also currently created as eager tools.
  • The eager `storage.*` tools are exposed only when the teammate has a storage workspace (shared-folder grants; system employees always). Conversation-scoped tools require a conversation-backed run. The `mastra_workspace.sandbox_*` tools are exposed only when sandbox mode is enabled for the teammate.

Current non-eager built-ins

  • With lazy tools enabled, current built-in tools that are loaded on demand include:
  • `storage.get_temp_file_url`
  • `storage.rename`
  • `storage.copy`
  • `storage.move`
  • `storage.delete`
  • `stargate.execute_task`
  • With lazy tools enabled, system tools that are not explicitly marked eager and assigned external MCP/OAuth tools also stay behind the loader path.
  • Disabling lazy tools exposes these configured tools directly instead.

Dynamic workflow tools

  • Every other non-`employee` workflow assigned to the same AI teammate is exposed as `workflow.<snake_case_name>`.
  • For Ally, dynamic workflow tools also include workflows from the canonical Ally when it is configured and the current Ally is not that canonical Ally.
  • The runtime builds the tool input schema from `workflow.payload_format`.
  • If a workflow has no payload schema, the runtime falls back to `{}`.
  • Dynamic workflow tools preserve the parent runtime context, validate inherited `state` against the child workflow's `state_definition`, and enforce max workflow nesting depth.

Realtime voice naming

  • Realtime voice tooling converts snake case to camel case and replaces dots with underscores.
  • Example: `contact.send_message` becomes `contact_sendMessage`.

Confirmed behavior by area

Search, web, and HTTP

  • `web.check_urls` performs availability checks only; it is not a content fetcher.
  • `web.search` is virtual and provider-dependent. Codex models use the native OpenAI web-search tool when this capability is selected, matching the OpenAI provider path.
  • `http.http_request` supports runtime placeholder substitution and secret org-variable substitution.
  • `http.http_request` parses JSON responses when possible and otherwise returns text content.
  • `stargate.execute_task` queues an HTTP request for a private-network system through a Stargate gateway. Use it when Alloy cannot reach the system directly; use `http.http_request` for public APIs.
  • `stargate.execute_task` accepts:
  • `system`
  • `uri`
  • `method`
  • optional JSON `body`
  • `stargate.execute_task` waits up to `120` seconds for a gateway response.
  • If the task times out, Alloy marks it as `failed` and the tool returns `success: false`.
  • The current public Stargate gateway posts a JSON envelope with `ok`, `status`, `body`, and optional `error` back to Alloy.
  • Because Alloy currently stores that posted payload verbatim, the tool returns the recorded response string in `data`; callers may need to parse JSON themselves.

Workflows and delegation

  • `workflows.list` currently returns only `id`, `name`, `description`, and `type`, scoped to the current employee's workflows.
  • `agent.call_teammate` runs another employee's default `employee` workflow with `{ message: query }` and waits for the result. The tool still exists for explicit selection where supported, but it is not in the default agent tool list or Ally's built-in tool set.
  • `agent.call_teammate` is user-authorized before the child run starts: the initiating organization user must have read access to the target AI employee (owner/admin or an Access Group grant). The target is resolved inside that scope; an inaccessible target returns `Employee {employee_id} not found`. Runs without an active initiating organization user (for example background or external-channel execution) cannot call a teammate.
  • Personal Ally employees cannot be targets, including the initiating user's own Ally. The tool returns `Personal Ally cannot be called through call_teammate` before creating a child run. Unowned system AI teammates remain callable when the other authorization and runtime checks pass.
  • `agent.run_subagent` creates a temporary child-agent run with a subset of `BASE_AGENT_TOOLS_LIST` plus virtual search when supported. Its `model` input is optional: when omitted, the subagent uses the caller model and configured defaults; when supplied, the model must exist in the catalog.
  • `agent.run_subagent` does not pass through dynamic `workflow.*` tools, `contact.send_message`, or system tools.
  • The former `ally.ask` escalation tool was removed. Teammates can use dynamic `workflow.*` tools, or `agent.call_teammate` when that tool is explicitly configured and the target is not a Personal Ally.
  • System workflow tools cover the full workflow lifecycle:
  • `system.create_workflow`, `system.update_workflow`, `system.read_workflow`, `system.workflows_list`, and `system.test_workflow` create, replace, read, list, and test-run workflows.
  • `system.delete_workflow` deletes an employee or organization workflow; schedulers bound to the workflow are removed with it. Managed default employee workflows (`type: employee`) are refused.
  • `system.validate_workflow` validates a submitted flow without saving anything.
  • `system.patch_workflow` edits individual steps of an existing workflow with ordered `set` and `remove` operations, validates the patched flow as a whole, and saves nothing unless the result is valid. Prefer it over `system.update_workflow` when only a few steps change.
  • `system.get_workflow_run` returns one workflow run with its step logs oldest first. Logs are trimmed to a per-log token budget (long fields are cut in the middle and marked); the tool paginates with optional `page` and `limit` (default `50`, maximum `200`).
  • `system.get_workflow_run_step_log` returns a single workflow-run step log by id in full, untrimmed. Use it to read what `system.get_workflow_run` had to trim.
  • Workflow and workflow-run tools apply the same employee-scoped access as the employee tools and HTTP routes:
  • A workflow run is readable only when the initiating organization user can access the run's AI employee; an inaccessible run reads as `Workflow run ... not found` so the tool never confirms that the run exists.
  • A workflow bound to an AI employee is reachable only by users who can access that employee: read/run tools need read access, and update/delete/patch tools need update access. An unreachable workflow reads as `Workflow ... not found`.
  • An organization workflow with no employee stays available to every member of the organization.

Employee access in AI tools

  • AI-employee tools resolve authorization from the initiating organization user, not from the executing employee's flags:
  • `system.employees_list` requires read authorization and returns only AI employees the initiating user can access.
  • `system.employee_get` requires read authorization and target scope; another user's personal Ally is never exposed.
  • `system.employee_update` requires update authorization and target scope before mutation.
  • `system.employee_create` requires create authorization; regular members are denied.
  • A call without an active initiating user (no or inactive user, no organization) is denied.
  • These employee tools are system-only tools; their authorization affects runs of system AI teammates (including personal Ally) that execute on behalf of a user.

Programming

  • `programming.run_code` runs Python or JavaScript in a fresh E2B sandbox.
  • It accepts an optional `sandbox_template` input naming an approved immutable E2B template ID. Blank or omitted uses the standard runtime; a template not on the deployment allow-list (`E2B_RUN_CODE_TEMPLATE_IDS`) is refused and never silently swapped for the default.
  • It supports optional package installation, read-only `storage_mounts`, `import_attachments` for files attached to the current conversation, exposing selected organization variables through `env_variables`, saving `/outputs/*` back to Storage, and `chat_files` for chat attachments.
  • Files saved back to Storage through `outputs` must currently use one of the upload-supported extensions: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.svg`, `.pdf`, `.doc`, `.docx`, `.xls`, `.xlsx`, `.pptx`, `.txt`, `.htm`, `.html`, `.md`, `.csv`, `.tsv`, `.json`, `.yaml`, `.yml`, `.ts`, `.tsx`, `.js`, `.jsx`, `.py`, `.sql`, `.css`, `.scss`, `.sh`, `.log`, `.xml`, `.toml`, `.ini`.
  • `env_variables` accepts organization variable names only. The runtime injects only variables that already exist in the organization and fails the tool call if any requested name is missing.
  • Code that requests `env_variables` reads them inside the sandbox from `os.environ['NAME']` in Python or `process.env.NAME` in JavaScript.
  • `storage_mounts` and `outputs` are each capped at 5 items.
  • `import_attachments` is capped at 5 items and copies files attached to the current workflow run conversation into the sandbox under `/attachments/` before the code runs. Each item takes `attachment_id` and an optional `path`; without `path`, the attachment keeps its own filename. The result reports each mounted file under `mounted_attachments` (`attachment_id`, `filename`, `sandbox_path`, `size_bytes`, `mime_type`). Attachments are resolved and size-checked before the sandbox is created, and only attachments belonging to the run's own conversation are accepted.
  • `chat_files` is capped at 10 items and shares the chat attachment count limit.
  • Generated `.pdf` files requested through either `outputs` or `chat_files` are parsed and validated before saving or attachment creation.
  • An unreadable PDF, or one detected with missing-glyph placeholder runs, fails preflight and must be regenerated with an embedded Unicode TrueType or OpenType font that covers every writing system used.
  • The tool guidance explicitly warns that ReportLab's built-in Helvetica does not support Cyrillic. For Cyrillic ReportLab output it directs the agent to register DejaVu Sans and set `fontName` on every paragraph style; CJK and other scripts require a font with matching glyph coverage.
  • Execution timeout is 60 seconds, and the sandbox is killed after each run.
  • For system AI teammates, `programming.run_code` skips shared-folder access filtering for mounted storage files and saved output targets.

Contact and conversation

  • `contact.update_contact` works only when the current workflow run is tied to a conversation contact.
  • `contact.send_message` is a normal selectable tool in text mode and is also auto-injected in voice mode so the model can send non-spoken text such as long URLs or codes.
  • `conversation.escalate_conversation` sets the conversation to `waiting_for_operator` unless it is already escalated or assigned.
  • `conversation.escalate_conversation` currently ignores its legacy input payload and acts only on the current conversation run context.
  • `conversation.read_document_attachment` reads an attachment from the current workflow run conversation. It returns text for PDF, DOCX, and plain-text/code files; CSV and XLSX must use `conversation.read_spreadsheet_attachment` instead.
  • The same tool handles PNG, JPEG, WEBP, and GIF image attachments. It also extracts viewable images embedded in DOCX/PDF files, marks them in text as `[image1]`, `[image2]`, and so on, and injects the relevant images into the next model step.
  • `conversation.read_document_attachment` accepts `attachment_id`, optional `message_id`, and optional `offset`. Documents are capped at 25 MB and return text in chunks of 30,000 characters; standalone images use the 10 MB conversation-image cap. When `has_more` is true, call again with `offset` set to `next_offset`; only embedded images whose markers fall in the returned chunk are attached.
  • The tool is scoped to the current workflow run conversation. It returns `found: false` when the run has no conversation, the attachment is not found in scope, the file type is unsupported, or a spreadsheet is passed to the document reader.
  • `conversation.save_attachment_to_storage` copies an attachment from the current run's conversation into organization storage. Inputs are `attachment_id`, destination `file_path` including the file name, and optional `overwrite` (default `true`). Parent folders are created as needed; storage write grants, the upload extension/MIME policy, and the 10 MB binary-write limit are enforced. The original message attachment remains in place.
  • `conversation.message_list` reads older, non-deleted messages from the current workflow run's conversation only. It does not search other conversations or channels.
  • Its optional `limit` is 1 through 20 and defaults to 20. Omit `before_timestamp` for the newest page; when `has_more` is true, pass the returned `next_before_timestamp` to fetch the next page backwards. Messages are oldest-first within each page and use the same chat-message shape as the initial history.
  • `conversation.message_details` accepts a `message_id` from the initial chat history or `conversation.message_list` and returns that assistant message's full stored generation record, including recorded reasoning, tool inputs, and tool results.
  • Before an agent claims that an earlier response used a tool, read or checked something, completed an action, or encountered a failure, it must inspect the relevant generation record instead of inferring execution history from the visible reply.
  • Both tools are eager and conversation-scoped. A message ID is resolved only inside the current conversation. Details can be large, so agents should inspect only the relevant assistant message and only when the record materially affects verification, continuation, correction, audit, or failure diagnosis.
  • The initial prompt uses the stored content payload for the most recent assistant message, preserving that turn's tool calls and arguments. Each tool output above `1,000` characters is replayed as a `1,000`-character excerpt with a total-length marker. Older assistant messages use normal rendered history, so agents retrieve a specific earlier generation record on demand with `conversation.message_details`.

Workspace sandbox tools

  • The `mastra_workspace.sandbox_*` tools are available only when the AI teammate has sandbox mode enabled. The sandbox is a disposable, billed Linux VM, separate from the organization storage.
  • `mastra_workspace.sandbox_start` starts the sandbox with an optional `timeout_ms` (default 5 minutes, maximum 20 minutes). Starting an already running sandbox just resets its lifetime; it keeps its filesystem.
  • `mastra_workspace.sandbox_execute_command` runs a shell command inside the sandbox and returns exit code, stdout, stderr, and execution time. Default command timeout is 60 seconds, maximum 5 minutes. `env_variables` accepts organization variable names only; missing names fail the call rather than being dropped.
  • `mastra_workspace.sandbox_import` copies files into the sandbox from two optional sources: `imports` takes files or directories from organization storage (directories recursively), and `attachments` takes files attached to the current conversation by `attachment_id` with a destination `sandbox_path`. Pass either or both; attachment lookup is conversation-scoped and size-checked before copying. Storage imports read only what the teammate may read.
  • `mastra_workspace.sandbox_export` copies files or directories from the sandbox into the organization storage, recursively for directories. It writes only into folders the teammate may write to. `send_to_chat` on a single-file item attaches the copied file to the current chat message as a download. Import/export enforce copy budgets: per-file, total bytes, file count, and directory depth limits.
  • `mastra_workspace.sandbox_stop` destroys the sandbox and releases it. Everything inside is lost, so exports must happen before stopping.

Storage

  • Non-system AI teammates are limited to folders explicitly shared with the AI teammate.
  • The `storage.*` tool set is now workspace-scoped: it is assembled only when the AI teammate has a storage filesystem (at least one shared folder; system employees always). File operations route through the shared `AiEmployeeStorageFilesystem`, so grants, WebSocket `storage_file.*` events, and vector indexing stay consistent across the tools, the workspace file tools, and sandbox import/export.
  • System AI teammates use system-level storage access across the storage tool set.
  • `storage.semantic_search` is the meaning-based lookup tool.
  • `storage.search` is exact/regex search. It returns `truncated` when the match count is a lower bound and `resultsTruncated` when the listing is partial; the tool message tells the model to narrow the pattern or path when a scan hit its limits.
  • `storage.stat_file` returns metadata plus bounded `line_count` for supported text files.
  • `storage.stat_file` accepts 1 to 10 file paths per call.
  • `storage.get_temp_file_url` returns a temporary HTTPS URL for a readable storage file.
  • The temporary URL is scoped to the current workflow run and stops working once that run completes or is aborted.
  • `storage.get_temp_file_url` is intended for downstream tool calls or external systems that need a URL rather than inline bytes.
  • Do not expose temporary file URLs directly to users; treat them as short-lived bearer-style links.
  • Prefer `storage.read_file` for supported text, spreadsheets, documents, and model-viewable images, or `programming.run_code` with `storage_mounts` for sandbox processing.
  • `storage.get_temp_file_url` currently returns `File not found` when the file does not yet have an `OrganizationDocument` record.
  • Temporary-link downloads support readable dotfiles and paths containing dot-prefixed folder segments after normal organization-storage containment and symlink checks.
  • `storage.read_file` reads 1 to 5 files with a 10 MB per-file read limit. It supports `.txt`, `.md`, `.json`, `.yaml`, `.yml`, `.htm`, `.html`, `.csv`, `.tsv`, `.xlsx`, `.ts`, `.tsx`, `.js`, `.jsx`, `.py`, `.sql`, `.css`, `.scss`, `.sh`, `.log`, `.xml`, `.toml`, `.ini`, `.docx`, text-layer `.pdf`, and image files.
  • Text results contain numbered lines plus `lines_returned`, `total_lines`, `has_more`, `next_start_line`, and `truncated`. When `has_more` is true, call again with `start_line` set to `next_start_line`. CSV rows use the same paging contract.
  • Standalone images and images embedded in DOCX/PDF files are handled by `storage.read_file`. Embedded images are marked in text as `[image1]`, `[image2]`, and so on. Only PNG, JPEG, WEBP, and GIF are injected into the next model step; unsupported image formats are reported instead of being sent to the provider.
  • A single model injection is capped at 10 images and 20 MB total, in addition to the per-image read limit. For paged documents, only images whose markers fall in the returned lines are attached.
  • `storage.write_file` and `storage.edit_file` support `.txt`, `.md`, `.htm`, `.html`, `.csv`, `.tsv`, `.json`, `.webmanifest`, `.yaml`, `.yml`, `.ts`, `.tsx`, `.js`, `.mjs`, `.jsx`, `.py`, `.sql`, `.css`, `.scss`, `.sh`, `.log`, `.xml`, `.toml`, and `.ini`.
  • They also accept exact extensionless `LICENSE` and `LICENCE` basenames case-insensitively at any folder depth. Other extensionless names and near-matches are rejected.
  • `storage.write_file_binary` writes binary files from base64 content (images, PDFs, archives, office documents). It accepts a `data:<mime>;base64,` prefix, enforces the upload extension policy, caps files at 10 MB, validates the base64 payload before decoding, and defaults `overwrite` to `true`.
  • `storage.rename`, `storage.copy`, and `storage.move` enforce shared-folder permissions and path-traversal checks for non-system teammates.
  • `storage.move` treats `dest_path` as the full target path including the new name, so a move can rename in the same step. Passing an existing directory as `dest_path` returns an error explaining that the full target path is required.
  • `storage.delete` accepts `{ path }` and deletes a file or a folder recursively through the shared Storage filesystem. It requires write access to the parent folder and follows the normal `.trashbin` retention behavior rather than immediately erasing entries outside `.trashbin`.
  • `storage.delete` is non-eager. It is included for Ally, workspace-filesystem-scoped AI teammates, and hosted MCP.

Scheduler

  • `scheduler.set_reminder` schedules the current employee's default `employee` workflow with `{ message }`.
  • `scheduler.schedule_workflow_run` takes `workflow_id`, `payload`, `at`, and optional `repeat.every_minutes`.
  • Scheduler datetime inputs currently accept ISO 8601 timestamps with timezone offsets.
  • Recurring intervals are minute-based only, with a minimum of 10 minutes.
  • Reminder and workflow-run scheduling require a live workflow run context and reject target-organization execution.
  • `scheduler.schedule_workflow_run` only works for workflows assigned to the current employee.
  • `scheduler.list_scheduled_items` returns a preview when `item_type` is omitted; full cursor pagination works only within a single `item_type`.
  • When `scheduler.list_scheduled_items` uses `kind: "reminder"`, any supplied `workflow_id` is ignored because reminders are resolved from the current employee's default workflow. The same filter remains active for `kind: "workflow_run"` and when `kind` is omitted.
  • `scheduler.list_runs` returns execution history for reminder and workflow-run jobs, filtered by scheduled job record creation time rather than actual fire time.
  • `scheduler.cancel_scheduled_item` and `scheduler.reschedule_item` use normalized refs such as `job:<uuid>` and `schedule:<uuid>`.
  • Scheduler tools only operate on items owned by the current employee and reject target-organization execution.

System documentation tools

  • `system.list_documentation` lists accessible documentation storage content.
  • `system.search_documentation` performs semantic search across documentation chunks.
  • `system.read_documentation` reads documentation files by line range.
  • These documentation tools are system-only and use the canonical Ally configured through platform settings.

Output shaping

  • Most tool execution goes through `toolExecutor`.
  • If `result.data` serializes above 40,000 characters, the backend keeps the beginning and end, inserts an `[... omitted N symbols]` marker in the middle, replaces `data` with that bounded string, and sends the existing configured tool-output truncation warning.
  • `storage.read_file` opts out of the generic object truncater so it can preserve structured image descriptors and paging metadata. It applies the same 40,000-character budget to whole numbered lines, reports `has_more` / `next_start_line`, and marks a single overlong line with its own omission marker.

Start building your AI team