UNPKG

@mastra/core

Version:
942 lines (708 loc) 26.8 kB
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt # Agents API The Agents API provides methods to interact with Mastra AI agents, including generating responses, streaming interactions, and managing agent tools. ## Getting all agents Retrieve a list of all available agents: ```typescript const agents = await mastraClient.listAgents() ``` Returns a record of agent IDs to their serialized agent configurations. ## Working with a specific agent Get an instance of a specific agent by its ID: ```typescript export const myAgent = new Agent({ id: 'my-agent', }) ``` ```typescript const agent = mastraClient.getAgent('my-agent') ``` ## Agent methods ### `details()` Retrieve detailed information about an agent: ```typescript const details = await agent.details() ``` ### `generate()` Generate a response from the agent: ```typescript const response = await agent.generate( [ { role: 'user', content: 'Hello, how are you?', }, ], { memory: { thread: 'thread-abc', // Optional: Thread ID for conversation context resource: 'user-123', // Optional: Resource ID }, structuredOutput: {}, // Optional: Structured Output configuration }, ) ``` You can also use the simplified string format with memory options: ```typescript const response = await agent.generate('Hello, how are you?', { memory: { thread: 'thread-1', resource: 'resource-1', }, }) ``` ### `stream()` Stream responses from the agent for real-time interactions: ```typescript const response = await agent.stream('Tell me a story') // Process data stream with the processDataStream util response.processDataStream({ onChunk: async chunk => { console.log(chunk) }, }) ``` You can also use the simplified string format with memory options: ```typescript const response = await agent.stream('Tell me a story', { memory: { thread: 'thread-1', resource: 'resource-1', }, clientTools: { colorChangeTool }, }) response.processDataStream({ onChunk: async chunk => { if (chunk.type === 'text-delta') { console.log(chunk.payload.text) } }, }) ``` You can also read from response body directly: ```typescript const reader = response.body.getReader() while (true) { const { done, value } = await reader.read() if (done) break console.log(new TextDecoder().decode(value)) } ``` #### AI SDK compatible format To stream AI SDK-formatted parts on the client from an `agent.stream(...)` response, wrap `response.processDataStream` into a `ReadableStream<ChunkType>` and use `toAISdkStream`: ```typescript import { createUIMessageStream } from 'ai' import { toAISdkStream } from '@mastra/ai-sdk' import type { ChunkType, MastraModelOutput } from '@mastra/core/stream' const response = await agent.stream('Tell me a story') const chunkStream: ReadableStream<ChunkType> = new ReadableStream<ChunkType>({ start(controller) { response .processDataStream({ onChunk: async chunk => controller.enqueue(chunk as ChunkType), }) .finally(() => controller.close()) }, }) const uiMessageStream = createUIMessageStream({ execute: async ({ writer }) => { for await (const part of toAISdkStream(chunkStream as unknown as MastraModelOutput, { from: 'agent', })) { writer.write(part) } }, }) for await (const part of uiMessageStream) { console.log(part) } ``` ### `sendMessage()` Send user-authored input to an active agent run or idle memory thread. Use this with `subscribeToThread()` so the client can render the stream that wakes from, or receives, the message. ```typescript const agent = mastraClient.getAgent('support-agent') const result = await agent.sendMessage({ message: { contents: 'Also consider the customer note I just added.', attributes: { sentFrom: 'web' }, }, resourceId: 'user-123', threadId: 'thread-abc', }) console.log(result.runId) ``` `message` accepts a string, an array of text/file parts, or an object with `contents`, `attributes`, `metadata`, and `providerOptions`. ### `queueMessage()` Queue user-authored input for the next thread turn. If the thread is active, Mastra starts a new run after the current run completes. If the thread is idle, Mastra starts a run immediately. ```typescript await agent.queueMessage({ message: 'Also check whether the tests need updates.', resourceId: 'user-123', threadId: 'thread-abc', }) ``` ### `sendSignal()` Send a lower-level signal to an active agent run or memory thread. Use this for system-generated context such as reactive reminders or notification-shaped context that doesn't need inbox storage. For durable notification records, use the server-side [`Agent.sendNotificationSignal()`](https://mastra.ai/reference/agents/agent) API. For user-authored input, prefer `sendMessage()` or `queueMessage()`. ```typescript const agent = mastraClient.getAgent('support-agent') const result = await agent.sendSignal({ signal: { type: 'reactive', tagName: 'system-reminder', contents: 'Also consider the latest customer note.', }, resourceId: 'user-123', threadId: 'thread-abc', }) console.log(result.runId) ``` Use `ifActive.behavior` and `ifIdle.behavior` to control whether Mastra delivers, persists, discards, or wakes from a signal: ```typescript await agent.sendSignal({ signal: { type: 'reactive', tagName: 'system-reminder', contents: 'Store this for later.' }, resourceId: 'user-123', threadId: 'thread-abc', ifIdle: { behavior: 'persist', }, }) ``` Pass `ifIdle.streamOptions` when the idle wake-up stream needs options such as model settings, tools, or runtime context: ```typescript await agent.sendSignal({ signal: { type: 'reactive', tagName: 'system-reminder', contents: 'Start from this signal.' }, resourceId: 'user-123', threadId: 'thread-abc', ifIdle: { behavior: 'wake', streamOptions: { maxSteps: 3, }, }, }) ``` Returns `{ accepted: true, runId: string }`. **signal** (`{ type: 'user' | 'reactive' | 'notification' | string; tagName?: string; contents: string | Array<TextPart | FilePart>; attributes?: Record<string, JSONValue>; metadata?: Record<string, unknown>; providerOptions?: ProviderMetadata }`): Lower-level signal payload. Use type for the semantic signal category and tagName for the XML tag shown to the model. providerOptions is attached to the resulting prompt turn and persisted on the stored signal message. **runId** (`string`): Run ID to target directly. **resourceId** (`string`): Resource ID for the memory thread. Use with threadId for thread-targeted signals. **threadId** (`string`): Thread ID to target. Use with resourceId for thread-targeted signals. **ifActive.behavior** (`'deliver' | 'persist' | 'discard'`): Controls what happens when the target thread is active. Defaults to deliver. **ifActive.attributes** (`Record<string, string | number | boolean>`): Attributes merged into the signal when Mastra accepts it while the target thread is active. **ifIdle.behavior** (`'wake' | 'persist' | 'discard'`): Controls what happens when the target thread is idle. Defaults to wake. **ifIdle.streamOptions** (`Omit<AgentExecutionOptions, 'messages'>`): Options for the stream that starts when ifIdle.behavior is wake. **ifIdle.attributes** (`Record<string, string | number | boolean>`): Attributes merged into the signal when Mastra accepts it while the target thread is idle. ### `subscribeToThread()` Subscribe to raw stream chunks for a memory thread. Use this to render output from a thread that may be started or continued by `sendMessage()`, `queueMessage()`, `sendSignal()`, or server-side notification dispatch. ```typescript const agent = mastraClient.getAgent('support-agent') const subscription = await agent.subscribeToThread({ resourceId: 'user-123', threadId: 'thread-abc', }) await subscription.processDataStream({ onChunk: chunk => { console.log(chunk) }, reconnect: true, }) ``` `subscribeToThread()` returns the underlying `Response` plus a `processDataStream()` helper. The helper reads the subscription stream until the connection closes or the request is aborted. Pass `reconnect: true` to resubscribe when the transport closes or a reconnect request fails, such as after a proxy idle timeout. **resourceId** (`string`): Resource ID for the memory thread. **threadId** (`string`): Thread ID to subscribe to. **processDataStream().reconnect** (`boolean | { maxRetries?: number; delayMs?: number }`): Reconnects the subscription stream after it closes or a reconnect request fails. true retries indefinitely with a one-second delay. ### `streamUntilIdle()` Stream a response and keep the stream open until every [background task](https://mastra.ai/docs/long-running-agents/background-tasks) dispatched during the run completes. The server re-enters the agentic loop on each task completion so the LLM can react to results in the same call. Requires background tasks to be [enabled on the Mastra instance](https://mastra.ai/reference/configuration) and a memory thread; otherwise the call falls through to a plain `stream()`. ```typescript const response = await agent.streamUntilIdle('Research solana for me', { memory: { thread: 'thread-1', resource: 'resource-1', }, maxIdleMs: 5 * 60_000, //optional }) response.processDataStream({ onChunk: async chunk => { if (chunk.type === 'background-task-completed') { console.log('task complete:', chunk.payload.taskId) } }, }) ``` ### `resumeStreamUntilIdle()` Resume a suspended agent stream with custom data and keep the stream open until every [background task](https://mastra.ai/docs/long-running-agents/background-tasks) dispatched during the run completes. Use this to continue execution after a suspension point, such as a workflow suspend within an agent. Requires background tasks to be [enabled on the Mastra instance](https://mastra.ai/reference/configuration) and a memory thread; otherwise the call falls through to a plain `resumeStream()`: ```typescript const response = await agent.resumeStreamUntilIdle( { approved: true, selectedOption: 'plan-b' }, { memory: { thread: 'thread-1', resource: 'resource-1', }, runId: 'run-123', toolCallId: 'tool-call-456', // optional maxIdleMs: 5 * 60_000, //optional }, ) await response.processDataStream({ onChunk: chunk => { console.log(chunk) }, }) ``` The stream emits the same chunk types as `stream()`, plus `background-task-*` chunks for task lifecycle events. Visit [`Agent.streamUntilIdle()`](https://mastra.ai/reference/streaming/agents/streamUntilIdle) for the full server-side API and [background task chunks](https://mastra.ai/reference/streaming/ChunkType) for the payload shapes. ### `getTool()` Retrieve information about a specific tool available to the agent: ```typescript const tool = await agent.getTool('tool-id') ``` ### `executeTool()` Execute a specific tool for the agent: ```typescript const result = await agent.executeTool('tool-id', { data: { input: 'value' }, }) ``` ### `network()` Stream responses from an agent network for multi-agent interactions: ```typescript const response = await agent.network('Research this topic and write a summary') response.processDataStream({ onChunk: async chunk => { console.log(chunk) }, }) ``` ### `listSuspendedRuns()` List suspended runs for the agent from storage runs waiting on a tool-call approval or on a tool that suspended. Discovery is backed by storage, so it works after a server restart and across server instances. Pass the returned `runId` to `approveToolCall()`, `declineToolCall()`, or `resumeStream()`. ```typescript const { runs, total } = await agent.listSuspendedRuns({ threadId: 'thread-456', resourceId: 'user-123', }) if (runs[0]) { console.log(runs[0].toolCalls) // [{ toolCallId, toolName, args, requiresApproval }] await agent.approveToolCall({ runId: runs[0].runId, toolCallId: runs[0].toolCalls[0].toolCallId, }) } ``` Accepts optional filters (`threadId`, `resourceId`, `fromDate`, `toDate`) and pagination (`perPage`, `page`). Returns `{ runs, total }`, where `total` is the number of matching runs before pagination. See [`Agent.listSuspendedRuns()`](https://mastra.ai/reference/agents/listSuspendedRuns) for details on the returned run shape. ### `approveToolCall()` Approve a pending tool call and return a continuation stream. Use this when you are rendering the resumed chunks from the approval response. ```typescript const response = await agent.approveToolCall({ runId: 'run-123', toolCallId: 'tool-call-456', }) response.processDataStream({ onChunk: async chunk => { console.log(chunk) }, }) ``` ### `sendToolApproval()` Approve or decline a pending tool call for a subscribed thread. Use this with `subscribeToThread()` when the resumed chunks should arrive through the existing thread subscription instead of a separate continuation stream. ```typescript const result = await agent.sendToolApproval({ resourceId: 'user-123', threadId: 'thread-456', toolCallId: 'tool-call-456', approved: true, }) console.log(result.accepted) ``` Returns `{ accepted: true, runId: string, toolCallId?: string }`. ### `declineToolCall()` Decline a pending tool call and return a continuation stream. Use this when you are rendering the resumed chunks from the decline response. ```typescript const response = await agent.declineToolCall({ runId: 'run-123', toolCallId: 'tool-call-456', }) response.processDataStream({ onChunk: async chunk => { console.log(chunk) }, }) ``` ### `resumeStream()` Resume a suspended agent stream with custom data. Use this to continue execution after a suspension point, such as a workflow suspend within an agent: ```typescript const response = await agent.resumeStream( { approved: true, selectedOption: 'plan-b' }, { runId: 'run-123', toolCallId: 'tool-call-456', // optional }, ) await response.processDataStream({ onChunk: chunk => { console.log(chunk) }, }) ``` ### `approveToolCallGenerate()` Approve a pending tool call when using `generate()` (non-streaming). Returns the complete response: ```typescript const output = await agent.generate('Find user John', { requireToolApproval: true, }) if (output.finishReason === 'suspended') { const result = await agent.approveToolCallGenerate({ runId: output.runId, toolCallId: output.suspendPayload.toolCallId, }) console.log(result.text) } ``` ### `declineToolCallGenerate()` Decline a pending tool call when using `generate()` (non-streaming). Returns the complete response: ```typescript const output = await agent.generate('Find user John', { requireToolApproval: true, }) if (output.finishReason === 'suspended') { const result = await agent.declineToolCallGenerate({ runId: output.runId, toolCallId: output.suspendPayload.toolCallId, }) console.log(result.text) } ``` ## Agent schedules Use the client SDK schedule methods to manage persisted agent schedules over the `/api/schedules` routes. For concepts and server-side examples, see [Schedules](https://mastra.ai/docs/long-running-agents/schedules) and the [`mastra.schedules` reference](https://mastra.ai/reference/schedules/overview). ### `createSchedule()` Create an agent schedule by passing `agentId`. ```typescript const schedule = await mastraClient.createSchedule({ agentId: 'pinger', cron: '0 * * * *', prompt: 'Give me a status update.', }) ``` ### `listSchedules()` List agent schedules. Filter by fields such as `agentId`, `threadId`, `resourceId`, `name`, or `status`. ```typescript const schedules = await mastraClient.listSchedules({ agentId: 'pinger', status: 'active', }) ``` ### `getSchedule()` Fetch a single agent schedule by ID. ```typescript const schedule = await mastraClient.getSchedule('agent_pinger') ``` ### `updateSchedule()` Update an agent schedule. Agent schedules can update fields such as `cron`, `timezone`, `prompt`, `name`, signal delivery options, metadata, and `status`. ```typescript const updated = await mastraClient.updateSchedule('agent_pinger', { cron: '*/30 * * * *', prompt: 'Give me a status update every 30 minutes.', }) ``` ### `deleteSchedule()` Delete an agent schedule. ```typescript await mastraClient.deleteSchedule('agent_pinger') ``` ### `runSchedule()` Fire an agent schedule once immediately without changing its cron cadence. ```typescript const run = await mastraClient.runSchedule('agent_pinger') ``` ### `pauseSchedule()` Pause an agent schedule so the scheduler stops firing it. Returns the updated schedule. ```typescript await mastraClient.pauseSchedule('agent_pinger') ``` ### `resumeSchedule()` Resume a paused agent schedule. The next fire time is recomputed from now, so a long-paused schedule doesn't fire a backlog. Returns the updated schedule. ```typescript await mastraClient.resumeSchedule('agent_pinger') ``` ### `listScheduleTriggers()` List the trigger history for an agent schedule, including the joined run summary for each fire. ```typescript const { triggers } = await mastraClient.listScheduleTriggers('agent_pinger', { limit: 50, }) ``` ## Client tools Client-side tools allow you to execute custom functions on the client side when the agent requests them. ```typescript import { createTool } from '@mastra/client-js' import { z } from 'zod' const colorChangeTool = createTool({ id: 'changeColor', description: 'Changes the background color', inputSchema: z.object({ color: z.string(), }), execute: async inputData => { document.body.style.backgroundColor = inputData.color return { success: true } }, }) // Use with generate const response = await agent.generate('Change the background to blue', { clientTools: { colorChangeTool }, }) // Use with stream const response = await agent.stream('Tell me a story', { memory: { thread: 'thread-1', resource: 'resource-1', }, clientTools: { colorChangeTool }, }) response.processDataStream({ onChunk: async chunk => { if (chunk.type === 'text-delta') { console.log(chunk.payload.text) } else if (chunk.type === 'tool-call') { console.log( `calling tool ${chunk.payload.toolName} with args ${JSON.stringify( chunk.payload.args, null, 2, )}`, ) } }, }) ``` ### Shape client tool output for the model Client tools support `toModelOutput` to control what the model receives, including multimodal content such as images. Because client tools execute locally, the mapping also runs on the client after `execute` completes. The transformed output is sent back to the server alongside the raw result, so the raw result stays available for storage and application logic. ```typescript const screenshotTool = createTool({ id: 'takeScreenshot', description: 'Takes a screenshot of the current page', inputSchema: z.object({}), execute: async () => { const base64 = await captureScreenshot() return { ok: true, data: base64 } }, toModelOutput: output => ({ type: 'content', value: [{ type: 'media', data: output.data, mediaType: 'image/jpeg' }], }), }) ``` ### Tracing client tools When `@mastra/observability` is installed and configured on the server, a client-side tool records a `CLIENT_TOOL_CALL` span as a child of the current `AGENT_RUN` span. The server creates that span when the model emits the client tool call, injects a W3C trace carrier into the outgoing tool-call chunk, and ends the span once the tool arguments are available. Without server-side observability configured, client tool tracing is a no-op. The client SDK also measures the wall-clock duration of each client tool's `execute` function and ships it back to the server, where it's emitted as a `mastra_tool_duration_ms` metric with `toolType: "client"`. For richer telemetry from inside your tool's `execute` function, use the `observe` helper on the execution context to add child spans and structured logs: ```typescript import { createTool } from '@mastra/client-js' import { z } from 'zod' const fetchUserTool = createTool({ id: 'fetchUser', description: 'Fetches the current user profile', inputSchema: z.object({ userId: z.string() }), execute: async ({ userId }, { observe }) => { observe.log('info', 'fetching user', { userId }) const user = await observe.span('http GET /users', async () => { const res = await fetch(`/api/users/${userId}`) return res.json() }) return user }, }) ``` `observe` is always available when no tracing context is active (e.g. running outside a traced agent), `span` runs the function directly and `log` is a no-op. No null-checking needed. The SDK serializes everything the collector buffered as OTLP/JSON and ships it back in the next request body. The server's `@mastra/observability` package validates that the spans belong to the correct trace (preventing cross-trace injection) and forwards each span/log into the same observability bus that server-side telemetry uses. Your existing exporters pick them up automatically after observability is configured. ## Stored agents Stored agents are agent configurations stored in a database that can be created, updated, and deleted at runtime. They reference primitives (tools, workflows, other agents, scorers) by key, which are resolved from the Mastra registry when the agent is instantiated. Memory is configured inline as a `SerializedMemoryConfig` object with options such as `lastMessages` and `semanticRecall`. ### `listStoredAgents()` Retrieve a paginated list of all stored agents: ```typescript const result = await mastraClient.listStoredAgents() console.log(result.agents) // Array of stored agents console.log(result.total) // Total count ``` With pagination and ordering: ```typescript const result = await mastraClient.listStoredAgents({ page: 0, perPage: 20, orderBy: { field: 'createdAt', direction: 'DESC', }, }) ``` ### `createStoredAgent()` Create a new stored agent: ```typescript const agent = await mastraClient.createStoredAgent({ id: 'my-agent', name: 'My Assistant', instructions: 'You are a helpful assistant.', model: { provider: 'openai', name: 'gpt-5.4', }, }) ``` With all options: ```typescript const agent = await mastraClient.createStoredAgent({ id: 'full-agent', name: 'Full Agent', description: 'A fully configured agent', instructions: 'You are a helpful assistant.', model: { provider: 'openai', name: 'gpt-5.4', }, tools: { calculator: {}, weather: {} }, workflows: { 'data-processing': {} }, agents: { 'subagent-1': {} }, memory: { options: { lastMessages: 20, semanticRecall: false, }, }, scorers: { 'quality-scorer': { sampling: { type: 'ratio', rate: 0.1 }, }, }, defaultOptions: { maxSteps: 10, }, metadata: { version: '1.0', team: 'engineering', }, }) ``` ### `getStoredAgent()` Get an instance of a specific stored agent: ```typescript const storedAgent = mastraClient.getStoredAgent('my-agent') ``` ## Stored agent methods ### `details()` Retrieve the stored agent configuration: ```typescript const details = await storedAgent.details() console.log(details.name) console.log(details.instructions) console.log(details.model) ``` ### `update()` Update specific fields of a stored agent. All fields are optional: ```typescript const updated = await storedAgent.update({ name: 'Updated Agent Name', instructions: 'New instructions for the agent.', }) ``` ```typescript // Update just the tools await storedAgent.update({ tools: { 'new-tool-1': {}, 'new-tool-2': {} }, }) // Update metadata await storedAgent.update({ metadata: { version: '2.0', lastModifiedBy: 'admin', }, }) ``` ### `delete()` Delete a stored agent: ```typescript const result = await storedAgent.delete() console.log(result.success) // true ``` ## Version management Both `Agent` (code-defined) and `StoredAgent` instances have methods for managing configuration versions. See the [Editor overview](https://mastra.ai/docs/editor/overview) for an introduction to version management. ### Getting an agent with a specific version Pass a version identifier when getting an agent: ```typescript // Load the published version (default) const agent = mastraClient.getAgent('support-agent') // Load the latest draft const draftAgent = mastraClient.getAgent('support-agent', { status: 'draft' }) // Load a specific version const versionedAgent = mastraClient.getAgent('support-agent', { versionId: 'abc-123' }) ``` For stored agents, pass a status option to `details()`: ```typescript const storedAgent = mastraClient.getStoredAgent('my-agent') const draft = await storedAgent.details(undefined, { status: 'draft' }) ``` ### `listVersions()` List all versions for an agent: ```typescript const versions = await agent.listVersions() console.log(versions.items) // Array of version snapshots console.log(versions.total) ``` With pagination and sorting: ```typescript const versions = await agent.listVersions({ page: 0, perPage: 10, orderBy: { field: 'createdAt', direction: 'DESC', }, }) ``` ### `createVersion()` Create a new version snapshot: ```typescript const version = await agent.createVersion({ changeMessage: 'Updated tone to be more friendly', }) ``` ### `getVersion()` Get a specific version by ID: ```typescript const version = await agent.getVersion('version-123') console.log(version.versionNumber) console.log(version.changedFields) console.log(version.createdAt) ``` ### `activateVersion()` Set a version as the active published version: ```typescript await agent.activateVersion('version-123') ``` ### `restoreVersion()` Restore a previous version by creating a new version with the same configuration: ```typescript await agent.restoreVersion('version-456') ``` ### `deleteVersion()` Delete a version: ```typescript await agent.deleteVersion('version-789') ``` ### `compareVersions()` Compare two versions and return their differences: ```typescript const diff = await agent.compareVersions('version-123', 'version-456') console.log(diff.changes) // Fields that changed between versions ``` ### React SDK In the React SDK, pass an `agentVersionId` through `requestContext` when using the `useChat` hook: ```typescript import { useChat } from '@mastra/react' function Chat() { const { messages, input, handleInputChange, handleSubmit } = useChat({ agentId: 'support-agent', requestContext: { agentVersionId: 'abc-123', }, }) // ... render chat UI } ```