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.export_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).
- Knowledge tools (`KNOWLEDGE_TOOLS_LIST`): `knowledge.list`, `knowledge.read`, `knowledge.search`, `knowledge.write`, `knowledge.move`, `knowledge.delete`, `knowledge.history`, and `knowledge.restore`. They are appended when `is_knowledge_enabled` is on for the AI teammate (the default), independently of whether it has a shared Storage folder. When off, the teammate's prompt also omits the knowledge start index. The individual tools still enforce organization, user and area-knowledge scopes and write permissions. `knowledge.write` accepts the version returned by `knowledge.read` as `expected_version` for safe updates; if the note changed, it returns the current version and instructs the agent to reread and retry. If a note with an expected version has disappeared, it reports that there is no note at the path rather than silently replacing it.
- Storage database tools (`DATABASE_TOOLS_LIST`): `database.list`, `database.schema`, `database.create`, `database.create_table`, `database.alter_table`, `database.insert_rows`, `database.list_rows`, `database.update_rows`, `database.delete_rows`, and `database.query`. The storage-file `database.schema` and `database.query` tools are available with a filesystem workspace; the managed-database tools are included when a database folder is shared (system employees always qualify). Each operation enforces its own readable/writable Storage scope. They are on-demand when lazy tools are enabled. Destructive database, table, and column deletion are not AI tools; see `databases.md`.
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`.
- Gemini Live and OpenAI Realtime preserve registered JSON Schema inputs, including nested objects, array item schemas, required and optional fields, enums, nullability, descriptions, and numeric constraints.
- OpenAI Realtime adapts provider function calls to the current Mastra execution contract: validated tool fields are passed as flat input and the session `RequestContext` is passed in the separate execution context.
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 `20`, maximum `50`).
- `system.get_workflow_run_step_log` reads one field of a workflow-run step log, a window at a time. Called without `field` it returns the log metadata plus the character size of every field (`runtime_context`, `input_data`, `output_data`, `additional_data`); call it with `field` to read that field. A window carries `chunk`, `offset`, `limit`, `total_chars`, `has_more`, and `next_offset`; `limit` defaults to `10,000` characters and is capped at `30,000`, and while `has_more` is true you pass `next_offset` back as `offset` to continue. The window is cut from the field's JSON text, so it can start and end mid-structure, and every answer goes through the standard tool-output budget.
- `system.export_workflow_run_step_log` writes a whole workflow-run step log to a Storage file when reading it window by window is impractical. It takes `step_log_id` and an optional `file_path`, and with no path it writes `workflow-run-logs/{workflow_run_id}/{step_log_id}.json`. The path must end with `.json`. Writes use the same shared-folder grants, path normalization, and indexing as the `storage.*` tools, and the result reports `file_path` and `size`. Read a slice of the exported file with `storage.read_file`, or hand it to something outside Alloy with `storage.get_temp_file_url`.
- 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 step log carries no AI employee of its own, so it is reached through the run it belongs to: reading or exporting a log of an unreachable run answers `Workflow run step log ... not found`.
- 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. Its optional 1-based `page` defaults to `1`, and `limit` defaults to `20` (maximum `100`). The response `data` array contains each employee's `employee_id`, `name`, and workflows with `id`, `name`, `type`, `created_at`, and `updated_at`. The response also includes `pagination` with `page`, `limit`, `total`, and `has_more`; request subsequent pages while `has_more` is true.
- `system.employee_get` requires read authorization and target scope. Owners and admins can reach organization AI employees, including other users' personal Allies; members can reach granted regular AI teammates plus their own personal Ally.
- `system.employee_update` requires update authorization and target scope before mutation and follows the same owner/admin versus member scope.
- `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 isolated sandbox. If both `MICRO_SANDBOX_API_URL` and `MICRO_SANDBOX_API_KEY` are configured, it uses the self-hosted microsandbox panel; otherwise it uses E2B. A partially configured panel falls back to E2B. With E2B and no explicit template, `E2B_RUNCODE_DEFAULT_TEMPLATE` can select the deployment's default code template; the separate agent-workspace E2B default is `E2B_SANDBOX_DEFAULT_TEMPLATE`.
- It accepts an optional `sandbox_template` input naming an approved template ID for the selected provider. Omit the input to use that provider's default runtime; an explicitly blank template is rejected, and a template not on the active deployment allow-list (`MICRO_SANDBOX_RUN_CODE_TEMPLATE_IDS` for the panel, `E2B_RUN_CODE_TEMPLATE_IDS` for E2B) is refused and never silently swapped for the default. An empty allow-list permits only the default runtime.
- 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 use a Storage-upload-supported extension (see `storage.md`). The shared upload registry also covers formats such as HEIC/HEIF, TIFF, Apple and OpenDocument files, RTF, SQLite database variants and journal companions. Output and sandbox exports check the destination extension, validate the bytes before saving, and use the registry's canonical MIME. Supported output formats are not necessarily text-write formats or formats that chat can preview.
- `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.
- Files crossing between organization storage and a sandbox share deployment-configurable transfer ceilings. Defaults are `100 MB` per file and `300 MB` in total per call. `E2B_MAX_TRANSFER_FILE_SIZE_MB` and `E2B_MAX_TRANSFER_TOTAL_SIZE_MB` accept positive integer megabyte values; each unset or invalid value independently falls back to its default.
- These ceilings apply to `storage_mounts`, `outputs`, and conversation attachments copied in by `import_attachments`.
- `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.
- The run-code limit is 60 seconds (an execution timeout on the microsandbox panel; an E2B request timeout on E2B), and the sandbox is destroyed after each run.
- For organization-user-initiated runs, `programming.run_code` resolves the initiating user's Storage grants for mounted files and saved outputs. When Access Group enforcement is enabled, an ordinary teammate's grants are intersected with the user's folder access; system teammates do not bypass those user-scoped grants. Without a user context, system teammates retain their system-level Storage access.
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 exposed when the AI teammate has sandbox mode enabled. A usable sandbox requires either both `MICRO_SANDBOX_API_URL` and `MICRO_SANDBOX_API_KEY` for the self-hosted microsandbox panel, or `E2B_API_KEY` for E2B; with neither configured, the workspace has no sandbox. The panel takes precedence when both providers are configured. The sandbox is a disposable Linux VM separate from 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 use the same deployment-configurable transfer ceilings as `programming.run_code`, whichever provider is in use, defaulting to `100 MB` per file and `300 MB` total per transfer; directory transfers also have a `100`-file cap and a maximum depth of `10`.
- `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. For user-initiated runs under enforced Access Group rules, their Storage operations also require that user's matching group grants; owners and admins retain organization-wide user access. Personal Ally operates with its initiating owner's user-granted scope.
- 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 have system-level Storage access when no initiating user limits their scope. When a system teammate acts for an organization user and Access Group enforcement is on, the user's Storage grants apply to its operations.
- `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.
- Inside a workflow run, the temporary URL is scoped to that run and stops working once the run completes or is aborted.
- Outside a workflow run, including Hosted MCP calls, the tool mints an opaque `/shared/files/link/{link_id}` URL that expires after ten minutes.
- `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. An expired or invalid runless link returns `410 Temporary URL has expired`.
- 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.
- Every `storage.read_file` entry requires `file_name`, `start_line`, and `end_line`. A failed per-file read is not evidence of file contents; callers must correct the request and obtain a successful fresh read before editing.
- Text results contain display-only numbered lines plus `lines_returned`, `total_lines`, `has_more`, `next_start_line`, and `truncated`. Line numbers must not be copied into exact edit strings. 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`.
- `storage.edit_file` requires a nonempty exact `old_string` and a positive finite integer `expected_replacements`, defaulting to `1`. The operation fails unless the actual match count is exactly the expected count. For checkbox-only Markdown updates, the runtime guidance requires a fresh successful read, `expected_replacements: 1`, and a replacement that changes only the selected `[ ]`, `[x]`, or `[X]` marker before reading the file again to verify it.
- 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` first requires the runtime AI employee to exist in the current organization.
- For regular AI teammates, `scheduler.schedule_workflow_run` only works for workflows assigned to that same employee. A system AI teammate may schedule another employee-owned workflow in the organization, provided the workflow exists and has a published flow and starting step.
- `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.