@mastra/core
Version:
The core foundation of the Mastra framework, providing essential components and interfaces for building AI-powered applications.
1,680 lines (1,673 loc) • 127 kB
JavaScript
'use strict';
var chunkKXWR3FNX_cjs = require('./chunk-KXWR3FNX.cjs');
var chunkZMMZXEOL_cjs = require('./chunk-ZMMZXEOL.cjs');
var chunkUVRGQEMD_cjs = require('./chunk-UVRGQEMD.cjs');
var chunkFXAV2WYX_cjs = require('./chunk-FXAV2WYX.cjs');
var chunkRO52JMKQ_cjs = require('./chunk-RO52JMKQ.cjs');
var chunkO7IW545H_cjs = require('./chunk-O7IW545H.cjs');
var chunkLABUWBKX_cjs = require('./chunk-LABUWBKX.cjs');
var chunkST5RMVLG_cjs = require('./chunk-ST5RMVLG.cjs');
var chunkRWTSGWWL_cjs = require('./chunk-RWTSGWWL.cjs');
var api = require('@opentelemetry/api');
var zod = require('zod');
var radash = require('radash');
var crypto$1 = require('crypto');
var EventEmitter = require('events');
var sift = require('sift');
var xstate = require('xstate');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
var sift__default = /*#__PURE__*/_interopDefault(sift);
// src/workflows/step.ts
var Step = class {
id;
description;
inputSchema;
outputSchema;
payload;
execute;
retryConfig;
mastra;
constructor({
id,
description,
execute,
payload,
outputSchema,
inputSchema,
retryConfig
}) {
this.id = id;
this.description = description ?? "";
this.inputSchema = inputSchema;
this.payload = payload;
this.outputSchema = outputSchema;
this.execute = execute;
this.retryConfig = retryConfig;
}
};
// src/workflows/types.ts
var WhenConditionReturnValue = /* @__PURE__ */(WhenConditionReturnValue2 => {
WhenConditionReturnValue2["CONTINUE"] = "continue";
WhenConditionReturnValue2["CONTINUE_FAILED"] = "continue_failed";
WhenConditionReturnValue2["ABORT"] = "abort";
WhenConditionReturnValue2["LIMBO"] = "limbo";
return WhenConditionReturnValue2;
})(WhenConditionReturnValue || {});
function resoolveMaybePromise(value, cb) {
if (value instanceof Promise) {
return value.then(cb);
}
return cb(value);
}
var _Agent_decorators, _init, _a;
_Agent_decorators = [chunkUVRGQEMD_cjs.InstrumentClass({
prefix: "agent",
excludeMethods: ["hasOwnMemory", "getMemory", "__primitive", "__registerMastra", "__registerPrimitives", "__setTools", "__setLogger", "__setTelemetry", "log", "getModel", "getInstructions", "getTools", "getLLM"]
})];
exports.Agent = class Agent extends (_a = chunkRO52JMKQ_cjs.MastraBase) {
id;
name;
#instructions;
model;
#mastra;
#memory;
#defaultGenerateOptions;
#defaultStreamOptions;
#tools;
/** @deprecated This property is deprecated. Use evals instead. */
metrics;
evals;
#voice;
constructor(config) {
super({
component: chunkO7IW545H_cjs.RegisteredLogger.AGENT
});
this.name = config.name;
this.id = config.name;
this.#instructions = config.instructions;
if (!config.model) {
throw new Error(`LanguageModel is required to create an Agent. Please provide the 'model'.`);
}
this.model = config.model;
this.#defaultGenerateOptions = config.defaultGenerateOptions || {};
this.#defaultStreamOptions = config.defaultStreamOptions || {};
this.#tools = config.tools || {};
this.metrics = {};
this.evals = {};
if (config.mastra) {
this.__registerMastra(config.mastra);
this.__registerPrimitives({
telemetry: config.mastra.getTelemetry(),
logger: config.mastra.getLogger()
});
}
if (config.metrics) {
this.logger.warn("The metrics property is deprecated. Please use evals instead to add evaluation metrics.");
this.metrics = config.metrics;
this.evals = config.metrics;
}
if (config.evals) {
this.evals = config.evals;
}
if (config.memory) {
this.#memory = config.memory;
}
if (config.voice) {
this.#voice = config.voice;
if (typeof config.tools !== "function") {
this.#voice?.addTools(this.tools);
}
if (typeof config.instructions === "string") {
this.#voice?.addInstructions(config.instructions);
}
} else {
this.#voice = new chunkKXWR3FNX_cjs.DefaultVoice();
}
}
hasOwnMemory() {
return Boolean(this.#memory);
}
getMemory() {
return this.#memory ?? this.#mastra?.memory;
}
get voice() {
if (typeof this.#instructions === "function") {
throw new Error("Voice is not compatible when instructions are a function. Please use getVoice() instead.");
}
return this.#voice;
}
async getVoice({
runtimeContext
} = {}) {
if (this.#voice) {
const voice = this.#voice;
voice?.addTools(await this.getTools({
runtimeContext
}));
voice?.addInstructions(await this.getInstructions({
runtimeContext
}));
return voice;
} else {
return new chunkKXWR3FNX_cjs.DefaultVoice();
}
}
get instructions() {
this.logger.warn("The instructions property is deprecated. Please use getInstructions() instead.");
if (typeof this.#instructions === "function") {
throw new Error("Instructions are not compatible when instructions are a function. Please use getInstructions() instead.");
}
return this.#instructions;
}
getInstructions({
runtimeContext = new chunkLABUWBKX_cjs.RuntimeContext()
} = {}) {
if (typeof this.#instructions === "string") {
return this.#instructions;
}
const result = this.#instructions({
runtimeContext
});
return resoolveMaybePromise(result, instructions => {
if (!instructions) {
this.logger.error(`[Agent:${this.name}] - Function-based instructions returned empty value`);
throw new Error("Instructions are required to use an Agent. The function-based instructions returned an empty value.");
}
return instructions;
});
}
get tools() {
this.logger.warn("The tools property is deprecated. Please use getTools() instead.");
if (typeof this.#tools === "function") {
throw new Error("Tools are not compatible when tools are a function. Please use getTools() instead.");
}
return chunkFXAV2WYX_cjs.ensureToolProperties(this.#tools);
}
getTools({
runtimeContext = new chunkLABUWBKX_cjs.RuntimeContext()
} = {}) {
if (typeof this.#tools !== "function") {
return chunkFXAV2WYX_cjs.ensureToolProperties(this.#tools);
}
const result = this.#tools({
runtimeContext
});
return resoolveMaybePromise(result, tools => {
if (!tools) {
this.logger.error(`[Agent:${this.name}] - Function-based tools returned empty value`);
throw new Error("Tools are required when using a function to provide them. The function returned an empty value.");
}
return chunkFXAV2WYX_cjs.ensureToolProperties(tools);
});
}
get llm() {
this.logger.warn("The llm property is deprecated. Please use getLLM() instead.");
if (typeof this.model === "function") {
throw new Error("LLM is not compatible when model is a function. Please use getLLM() instead.");
}
return this.getLLM();
}
/**
* Gets or creates an LLM instance based on the current model
* @param options Options for getting the LLM
* @returns A promise that resolves to the LLM instance
*/
getLLM({
runtimeContext = new chunkLABUWBKX_cjs.RuntimeContext()
} = {}) {
const model = this.getModel({
runtimeContext
});
return resoolveMaybePromise(model, model2 => {
const llm = new chunkZMMZXEOL_cjs.MastraLLM({
model: model2,
mastra: this.#mastra
});
if (this.#primitives) {
llm.__registerPrimitives(this.#primitives);
}
if (this.#mastra) {
llm.__registerMastra(this.#mastra);
}
return llm;
});
}
/**
* Gets the model, resolving it if it's a function
* @param options Options for getting the model
* @returns A promise that resolves to the model
*/
getModel({
runtimeContext = new chunkLABUWBKX_cjs.RuntimeContext()
} = {}) {
if (typeof this.model !== "function") {
if (!this.model) {
this.logger.error(`[Agent:${this.name}] - No model provided`);
throw new Error("Model is required to use an Agent.");
}
return this.model;
}
const result = this.model({
runtimeContext
});
return resoolveMaybePromise(result, model => {
if (!model) {
this.logger.error(`[Agent:${this.name}] - Function-based model returned empty value`);
throw new Error("Model is required to use an Agent. The function-based model returned an empty value.");
}
return model;
});
}
__updateInstructions(newInstructions) {
this.#instructions = newInstructions;
this.logger.debug(`[Agents:${this.name}] Instructions updated.`, {
model: this.model,
name: this.name
});
}
#primitives;
__registerPrimitives(p) {
if (p.telemetry) {
this.__setTelemetry(p.telemetry);
}
if (p.logger) {
this.__setLogger(p.logger);
}
this.#primitives = p;
this.logger.debug(`[Agents:${this.name}] initialized.`, {
model: this.model,
name: this.name
});
}
__registerMastra(mastra) {
this.#mastra = mastra;
}
/**
* Set the concrete tools for the agent
* @param tools
*/
__setTools(tools) {
this.#tools = tools;
this.logger.debug(`[Agents:${this.name}] Tools set for agent ${this.name}`, {
model: this.model,
name: this.name
});
}
async generateTitleFromUserMessage({
message,
runtimeContext = new chunkLABUWBKX_cjs.RuntimeContext()
}) {
const llm = await this.getLLM({
runtimeContext
});
const {
text
} = await llm.__text({
runtimeContext,
messages: [{
role: "system",
content: `
- you will generate a short title based on the first message a user begins a conversation with
- ensure it is not more than 80 characters long
- the title should be a summary of the user's message
- do not use quotes or colons
- the entire text you return will be used as the title`
}, {
role: "user",
content: JSON.stringify(message)
}]
});
const cleanedText = text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
return cleanedText;
}
getMostRecentUserMessage(messages) {
const userMessages = messages.filter(message => message.role === "user");
return userMessages.at(-1);
}
async genTitle(userMessage) {
let title = `New Thread ${(/* @__PURE__ */new Date()).toISOString()}`;
try {
if (userMessage) {
title = await this.generateTitleFromUserMessage({
message: userMessage
});
}
} catch (e) {
console.error("Error generating title:", e);
}
return title;
}
async fetchMemory({
threadId,
thread: passedThread,
memoryConfig,
resourceId,
userMessages,
systemMessage,
runId
}) {
const memory = this.getMemory();
if (memory) {
const thread = passedThread ?? (await memory.getThreadById({
threadId
}));
if (!thread) {
return {
threadId: threadId || "",
messages: userMessages
};
}
const newMessages = chunkFXAV2WYX_cjs.ensureAllMessagesAreCoreMessages(userMessages);
const now = Date.now();
const messages = newMessages.map((u, index) => {
return {
id: this.getMemory()?.generateId(),
createdAt: new Date(now + index),
threadId,
...u,
content: u.content,
role: u.role,
type: "text"
};
});
const [memoryMessages, memorySystemMessage] = threadId && memory ? await Promise.all([memory.rememberMessages({
threadId,
resourceId,
config: memoryConfig,
systemMessage,
vectorMessageSearch: messages.slice(-1).map(m => {
if (typeof m === `string`) {
return m;
}
return m?.content || ``;
}).join(`
`)
}).then(r => r.messages), memory.getSystemMessage({
threadId,
memoryConfig
})]) : [[], null];
this.logger.debug("Saved messages to memory", {
threadId,
runId
});
const processedMessages = memory.processMessages({
messages: this.sanitizeResponseMessages(memoryMessages),
newMessages,
systemMessage: typeof systemMessage?.content === `string` ? systemMessage.content : void 0,
memorySystemMessage: memorySystemMessage ?? ``
});
return {
threadId: thread.id,
messages: [memorySystemMessage ? {
role: "system",
content: memorySystemMessage
} : null, ...processedMessages, ...newMessages].filter(message => Boolean(message))
};
}
return {
threadId: threadId || "",
messages: userMessages
};
}
getResponseMessages({
messages,
threadId,
resourceId,
now
}) {
if (!messages) return [];
const messagesArray = Array.isArray(messages) ? messages : [messages];
return this.sanitizeResponseMessages(messagesArray).map((message, index) => {
const messageId = crypto$1.randomUUID();
let toolCallIds;
let toolCallArgs;
let toolNames;
let type = "text";
if (message.role === "tool") {
toolCallIds = message.content.map(content => content.toolCallId);
type = "tool-result";
}
if (message.role === "assistant") {
const assistantContent = message.content;
const assistantToolCalls = assistantContent.map(content => {
if (content.type === "tool-call") {
return {
toolCallId: content.toolCallId,
toolArgs: content.args,
toolName: content.toolName
};
}
return void 0;
})?.filter(Boolean);
toolCallIds = assistantToolCalls?.map(toolCall => toolCall.toolCallId);
toolCallArgs = assistantToolCalls?.map(toolCall => toolCall.toolArgs);
toolNames = assistantToolCalls?.map(toolCall => toolCall.toolName);
type = assistantContent?.[0]?.type;
}
return {
id: messageId,
threadId,
resourceId,
role: message.role,
content: message.content,
createdAt: new Date(now + index),
// use Date.now() + index to make sure every message is atleast one millisecond apart
toolCallIds: toolCallIds?.length ? toolCallIds : void 0,
toolCallArgs: toolCallArgs?.length ? toolCallArgs : void 0,
toolNames: toolNames?.length ? toolNames : void 0,
type
};
});
}
sanitizeResponseMessages(messages) {
let toolResultIds = [];
let toolCallIds = [];
for (const message of messages) {
if (!Array.isArray(message.content)) continue;
if (message.role === "tool") {
for (const content of message.content) {
if (content.type === "tool-result") {
toolResultIds.push(content.toolCallId);
}
}
} else if (message.role === "assistant" || message.role === "user") {
for (const content of message.content) {
if (typeof content !== `string`) {
if (content.type === `tool-call`) {
toolCallIds.push(content.toolCallId);
}
}
}
}
}
const messagesBySanitizedContent = messages.map(message => {
if (message.role !== "assistant" && message.role !== `tool` && message.role !== `user`) return message;
if (!Array.isArray(message.content)) {
return message;
}
const sanitizedContent = message.content.filter(content => {
if (content.type === `tool-call`) {
return toolResultIds.includes(content.toolCallId);
}
if (content.type === `text`) {
return content.text.trim() !== ``;
}
if (content.type === `tool-result`) {
return toolCallIds.includes(content.toolCallId);
}
return true;
});
return {
...message,
content: sanitizedContent
};
});
return messagesBySanitizedContent.filter(message => {
if (typeof message.content === `string`) {
if (message.role === "assistant") {
return true;
}
return message.content !== "";
}
if (Array.isArray(message.content)) {
return message.content.length && message.content.every(c => {
if (c.type === `text`) {
return c.text && c.text !== "";
}
return true;
});
}
return true;
});
}
async convertTools({
toolsets,
clientTools,
threadId,
resourceId,
runId,
runtimeContext
}) {
this.logger.debug(`[Agents:${this.name}] - Assigning tools`, {
runId,
threadId,
resourceId
});
const memory = this.getMemory();
const memoryTools = memory?.getTools?.();
let mastraProxy = void 0;
const logger = this.logger;
if (this.#mastra) {
mastraProxy = chunkFXAV2WYX_cjs.createMastraProxy({
mastra: this.#mastra,
logger
});
}
const tools = await this.getTools({
runtimeContext
});
const convertedEntries = await Promise.all(Object.entries(tools || {}).map(async ([k, tool]) => {
if (tool) {
const options = {
name: k,
runId,
threadId,
resourceId,
logger: this.logger,
mastra: mastraProxy,
memory,
agentName: this.name,
runtimeContext,
model: typeof this.model === "function" ? await this.getModel({
runtimeContext
}) : this.model
};
return [k, chunkFXAV2WYX_cjs.makeCoreTool(tool, options)];
}
return void 0;
}));
const converted = Object.fromEntries(convertedEntries.filter(entry => Boolean(entry)));
let convertedMemoryTools = {};
if (memoryTools) {
const memoryToolEntries = await Promise.all(Object.entries(memoryTools).map(async ([k, tool]) => {
return [k, {
description: tool.description,
parameters: tool.parameters,
execute: typeof tool?.execute === "function" ? async (args, options) => {
try {
this.logger.debug(`[Agent:${this.name}] - Executing memory tool ${k}`, {
name: k,
description: tool.description,
args,
runId,
threadId,
resourceId
});
return tool?.execute?.({
context: args,
mastra: mastraProxy,
memory,
runId,
threadId,
resourceId,
logger: this.logger,
agentName: this.name,
runtimeContext
}, options) ?? void 0;
} catch (err) {
this.logger.error(`[Agent:${this.name}] - Failed memory tool execution`, {
error: err,
runId,
threadId,
resourceId
});
throw err;
}
} : void 0
}];
}));
convertedMemoryTools = Object.fromEntries(memoryToolEntries.filter(entry => Boolean(entry)));
}
const toolsFromToolsetsConverted = {
...converted,
...convertedMemoryTools
};
const toolsFromToolsets = Object.values(toolsets || {});
if (toolsFromToolsets.length > 0) {
this.logger.debug(`[Agent:${this.name}] - Adding tools from toolsets ${Object.keys(toolsets || {}).join(", ")}`, {
runId
});
for (const toolset of toolsFromToolsets) {
for (const [toolName, tool] of Object.entries(toolset)) {
const toolObj = tool;
const options = {
name: toolName,
runId,
threadId,
resourceId,
logger: this.logger,
mastra: mastraProxy,
memory,
agentName: this.name,
runtimeContext,
model: typeof this.model === "function" ? await this.getModel({
runtimeContext
}) : this.model
};
const convertedToCoreTool = chunkFXAV2WYX_cjs.makeCoreTool(toolObj, options, "toolset");
toolsFromToolsetsConverted[toolName] = convertedToCoreTool;
}
}
}
const clientToolsForInput = Object.entries(clientTools || {});
if (clientToolsForInput.length > 0) {
this.logger.debug(`[Agent:${this.name}] - Adding client tools ${Object.keys(clientTools || {}).join(", ")}`, {
runId
});
for (const [toolName, tool] of clientToolsForInput) {
const {
execute,
...rest
} = tool;
const options = {
name: toolName,
runId,
threadId,
resourceId,
logger: this.logger,
mastra: mastraProxy,
memory,
agentName: this.name,
runtimeContext,
model: typeof this.model === "function" ? await this.getModel({
runtimeContext
}) : this.model
};
const convertedToCoreTool = chunkFXAV2WYX_cjs.makeCoreTool(rest, options, "client-tool");
toolsFromToolsetsConverted[toolName] = convertedToCoreTool;
}
}
return toolsFromToolsetsConverted;
}
async preExecute({
resourceId,
runId,
threadId,
thread,
memoryConfig,
messages,
systemMessage
}) {
let coreMessages = [];
let threadIdToUse = threadId;
this.logger.debug(`Saving user messages in memory for agent ${this.name}`, {
runId
});
const saveMessageResponse = await this.fetchMemory({
threadId,
thread,
resourceId,
userMessages: messages,
memoryConfig,
systemMessage
});
coreMessages = saveMessageResponse.messages;
threadIdToUse = saveMessageResponse.threadId;
return {
coreMessages,
threadIdToUse
};
}
__primitive({
instructions,
messages,
context,
threadId,
memoryConfig,
resourceId,
runId,
toolsets,
clientTools,
runtimeContext
}) {
return {
before: async () => {
if (process.env.NODE_ENV !== "test") {
this.logger.debug(`[Agents:${this.name}] - Starting generation`, {
runId
});
}
const systemMessage = {
role: "system",
content: instructions || `${this.instructions}.`
};
let coreMessages = messages;
let threadIdToUse = threadId;
let thread;
const memory = this.getMemory();
if (threadId && memory && !resourceId) {
throw new Error(`A resourceId must be provided when passing a threadId and using Memory. Saw threadId ${threadId} but resourceId is ${resourceId}`);
}
if (memory && resourceId) {
this.logger.debug(`[Agent:${this.name}] - Memory persistence enabled: store=${this.getMemory()?.constructor.name}, resourceId=${resourceId}`, {
runId,
resourceId,
threadId: threadIdToUse,
memoryStore: this.getMemory()?.constructor.name
});
thread = threadIdToUse ? await memory.getThreadById({
threadId: threadIdToUse
}) : void 0;
if (!thread) {
thread = await memory.createThread({
threadId: threadIdToUse,
resourceId,
memoryConfig
});
}
threadIdToUse = thread.id;
const preExecuteResult = await this.preExecute({
resourceId,
runId,
threadId: threadIdToUse,
thread,
memoryConfig,
messages,
systemMessage
});
coreMessages = preExecuteResult.coreMessages;
threadIdToUse = preExecuteResult.threadIdToUse;
}
let convertedTools;
const reasons = [];
if (toolsets && Object.keys(toolsets || {}).length > 0) {
reasons.push(`toolsets present (${Object.keys(toolsets || {}).length} tools)`);
}
if (this.getMemory() && resourceId) {
reasons.push("memory and resourceId available");
}
this.logger.debug(`[Agent:${this.name}] - Enhancing tools: ${reasons.join(", ")}`, {
runId,
toolsets: toolsets ? Object.keys(toolsets) : void 0,
clientTools: clientTools ? Object.keys(clientTools) : void 0,
hasMemory: !!this.getMemory(),
hasResourceId: !!resourceId
});
convertedTools = await this.convertTools({
toolsets,
clientTools,
threadId: threadIdToUse,
resourceId,
runId,
runtimeContext
});
const messageObjects = [systemMessage, ...(context || []), ...coreMessages];
return {
messageObjects,
convertedTools,
threadId: threadIdToUse,
thread
};
},
after: async ({
result,
thread: threadAfter,
threadId: threadId2,
memoryConfig: memoryConfig2,
outputText,
runId: runId2
}) => {
const resToLog = {
text: result?.text,
object: result?.object,
toolResults: result?.toolResults,
toolCalls: result?.toolCalls,
usage: result?.usage,
steps: result?.steps?.map(s => {
return {
stepType: s?.stepType,
text: result?.text,
object: result?.object,
toolResults: result?.toolResults,
toolCalls: result?.toolCalls,
usage: result?.usage
};
})
};
this.logger.debug(`[Agent:${this.name}] - Post processing LLM response`, {
runId: runId2,
result: resToLog,
threadId: threadId2
});
const memory = this.getMemory();
const thread = threadAfter || (threadId2 ? await memory?.getThreadById({
threadId: threadId2
}) : void 0);
if (memory && resourceId && thread) {
try {
const userMessage = this.getMostRecentUserMessage(messages);
const now = Date.now();
const threadMessages = this.sanitizeResponseMessages(chunkFXAV2WYX_cjs.ensureAllMessagesAreCoreMessages(messages)).map((u, index) => {
return {
id: this.getMemory()?.generateId(),
createdAt: new Date(now + index),
threadId: thread.id,
resourceId,
...u,
content: u.content,
role: u.role,
type: "text"
};
});
const dateResponseMessagesFrom = (threadMessages.at(-1)?.createdAt?.getTime?.() || Date.now()) + 1;
void (async () => {
if (!thread.title?.startsWith("New Thread")) {
return;
}
const config = memory.getMergedThreadConfig(memoryConfig2);
const title = config?.threads?.generateTitle ? await this.genTitle(userMessage) : void 0;
if (!title) {
return;
}
return memory.createThread({
threadId: thread.id,
resourceId,
memoryConfig: memoryConfig2,
title
});
})();
let responseMessages = result.response.messages;
if (!responseMessages && result.object) {
responseMessages = [{
role: "assistant",
content: [{
type: "text",
text: outputText
}]
}];
}
await memory.saveMessages({
messages: [...threadMessages, ...this.getResponseMessages({
threadId: threadId2,
resourceId,
messages: responseMessages,
now: dateResponseMessagesFrom
})],
memoryConfig: memoryConfig2
});
} catch (e) {
const message = e instanceof Error ? e.message : JSON.stringify(e);
this.logger.error("Error saving response", {
error: message,
runId: runId2,
result: resToLog,
threadId: threadId2
});
}
}
if (Object.keys(this.evals || {}).length > 0) {
const input = messages.map(message => message.content).join("\n");
const runIdToUse = runId2 || crypto.randomUUID();
for (const metric of Object.values(this.evals || {})) {
chunkST5RMVLG_cjs.executeHook("onGeneration" /* ON_GENERATION */, {
input,
output: outputText,
runId: runIdToUse,
metric,
agentName: this.name,
instructions: instructions || this.instructions
});
}
}
}
};
}
async generate(messages, generateOptions = {}) {
const {
instructions,
context,
threadId: threadIdInFn,
memoryOptions,
resourceId,
maxSteps,
onStepFinish,
runId,
output,
toolsets,
clientTools,
temperature,
toolChoice = "auto",
experimental_output,
telemetry,
runtimeContext = new chunkLABUWBKX_cjs.RuntimeContext(),
...rest
} = Object.assign({}, this.#defaultGenerateOptions, generateOptions);
let messagesToUse = [];
if (typeof messages === `string`) {
messagesToUse = [{
role: "user",
content: messages
}];
} else if (Array.isArray(messages)) {
messagesToUse = messages.map(message => {
if (typeof message === `string`) {
return {
role: "user",
content: message
};
}
return message;
});
} else {
messagesToUse = [messages];
}
const runIdToUse = runId || crypto$1.randomUUID();
const instructionsToUse = instructions || (await this.getInstructions({
runtimeContext
}));
const llm = await this.getLLM({
runtimeContext
});
const {
before,
after
} = this.__primitive({
instructions: instructionsToUse,
messages: messagesToUse,
context,
threadId: threadIdInFn,
memoryConfig: memoryOptions,
resourceId,
runId: runIdToUse,
toolsets,
clientTools,
runtimeContext
});
const {
threadId,
thread,
messageObjects,
convertedTools
} = await before();
if (!output && experimental_output) {
const result2 = await llm.__text({
messages: messageObjects,
tools: convertedTools,
onStepFinish: result3 => {
void onStepFinish?.(result3);
},
maxSteps,
runId: runIdToUse,
temperature,
toolChoice: toolChoice || "auto",
experimental_output,
threadId,
resourceId,
memory: this.getMemory(),
runtimeContext,
...rest
});
const outputText2 = result2.text;
await after({
result: result2,
threadId,
thread,
memoryConfig: memoryOptions,
outputText: outputText2,
runId: runIdToUse
});
const newResult = result2;
newResult.object = result2.experimental_output;
return newResult;
}
if (!output) {
const result2 = await llm.__text({
messages: messageObjects,
tools: convertedTools,
onStepFinish: result3 => {
void onStepFinish?.(result3);
},
maxSteps,
runId: runIdToUse,
temperature,
toolChoice,
telemetry,
threadId,
resourceId,
memory: this.getMemory(),
runtimeContext,
...rest
});
const outputText2 = result2.text;
await after({
result: result2,
thread,
threadId,
memoryConfig: memoryOptions,
outputText: outputText2,
runId: runIdToUse
});
return result2;
}
const result = await llm.__textObject({
messages: messageObjects,
tools: convertedTools,
structuredOutput: output,
onStepFinish: result2 => {
void onStepFinish?.(result2);
},
maxSteps,
runId: runIdToUse,
temperature,
toolChoice,
telemetry,
memory: this.getMemory(),
runtimeContext,
...rest
});
const outputText = JSON.stringify(result.object);
await after({
result,
thread,
threadId,
memoryConfig: memoryOptions,
outputText,
runId: runIdToUse
});
return result;
}
async stream(messages, streamOptions = {}) {
const {
instructions,
context,
threadId: threadIdInFn,
memoryOptions,
resourceId,
maxSteps,
onFinish,
onStepFinish,
runId,
toolsets,
clientTools,
output,
temperature,
toolChoice = "auto",
experimental_output,
telemetry,
runtimeContext = new chunkLABUWBKX_cjs.RuntimeContext(),
...rest
} = Object.assign({}, this.#defaultStreamOptions, streamOptions);
const runIdToUse = runId || crypto$1.randomUUID();
const instructionsToUse = instructions || (await this.getInstructions({
runtimeContext
}));
const llm = await this.getLLM({
runtimeContext
});
let messagesToUse = [];
if (typeof messages === `string`) {
messagesToUse = [{
role: "user",
content: messages
}];
} else {
messagesToUse = messages.map(message => {
if (typeof message === `string`) {
return {
role: "user",
content: message
};
}
return message;
});
}
const {
before,
after
} = this.__primitive({
instructions: instructionsToUse,
messages: messagesToUse,
context,
threadId: threadIdInFn,
memoryConfig: memoryOptions,
resourceId,
runId: runIdToUse,
toolsets,
clientTools,
runtimeContext
});
const {
threadId,
thread,
messageObjects,
convertedTools
} = await before();
if (!output && experimental_output) {
this.logger.debug(`Starting agent ${this.name} llm stream call`, {
runId
});
const streamResult = await llm.__stream({
messages: messageObjects,
temperature,
tools: convertedTools,
onStepFinish: result => {
void onStepFinish?.(result);
},
onFinish: async result => {
try {
const outputText = result.text;
await after({
result,
thread,
threadId,
memoryConfig: memoryOptions,
outputText,
runId: runIdToUse
});
} catch (e) {
this.logger.error("Error saving memory on finish", {
error: e,
runId
});
}
void onFinish?.(result);
},
maxSteps,
runId: runIdToUse,
toolChoice,
experimental_output,
memory: this.getMemory(),
runtimeContext,
...rest
});
const newStreamResult = streamResult;
newStreamResult.partialObjectStream = streamResult.experimental_partialOutputStream;
return newStreamResult;
} else if (!output) {
this.logger.debug(`Starting agent ${this.name} llm stream call`, {
runId
});
return llm.__stream({
messages: messageObjects,
temperature,
tools: convertedTools,
onStepFinish: result => {
void onStepFinish?.(result);
},
onFinish: async result => {
try {
const outputText = result.text;
await after({
result,
thread,
threadId,
memoryConfig: memoryOptions,
outputText,
runId: runIdToUse
});
} catch (e) {
this.logger.error("Error saving memory on finish", {
error: e,
runId
});
}
void onFinish?.(result);
},
maxSteps,
runId: runIdToUse,
toolChoice,
telemetry,
memory: this.getMemory(),
runtimeContext,
...rest
});
}
this.logger.debug(`Starting agent ${this.name} llm streamObject call`, {
runId
});
return llm.__streamObject({
messages: messageObjects,
tools: convertedTools,
temperature,
structuredOutput: output,
onStepFinish: result => {
void onStepFinish?.(result);
},
onFinish: async result => {
try {
const outputText = JSON.stringify(result.object);
await after({
result,
thread,
threadId,
memoryConfig: memoryOptions,
outputText,
runId: runIdToUse
});
} catch (e) {
this.logger.error("Error saving memory on finish", {
error: e,
runId
});
}
void onFinish?.(result);
},
runId: runIdToUse,
toolChoice,
telemetry,
memory: this.getMemory(),
runtimeContext,
...rest
});
}
/**
* Convert text to speech using the configured voice provider
* @param input Text or text stream to convert to speech
* @param options Speech options including speaker and provider-specific options
* @returns Audio stream
* @deprecated Use agent.voice.speak() instead
*/
async speak(input, options) {
if (!this.voice) {
throw new Error("No voice provider configured");
}
this.logger.warn("Warning: agent.speak() is deprecated. Please use agent.voice.speak() instead.");
try {
return this.voice.speak(input, options);
} catch (e) {
this.logger.error("Error during agent speak", {
error: e
});
throw e;
}
}
/**
* Convert speech to text using the configured voice provider
* @param audioStream Audio stream to transcribe
* @param options Provider-specific transcription options
* @returns Text or text stream
* @deprecated Use agent.voice.listen() instead
*/
async listen(audioStream, options) {
if (!this.voice) {
throw new Error("No voice provider configured");
}
this.logger.warn("Warning: agent.listen() is deprecated. Please use agent.voice.listen() instead");
try {
return this.voice.listen(audioStream, options);
} catch (e) {
this.logger.error("Error during agent listen", {
error: e
});
throw e;
}
}
/**
* Get a list of available speakers from the configured voice provider
* @throws {Error} If no voice provider is configured
* @returns {Promise<Array<{voiceId: string}>>} List of available speakers
* @deprecated Use agent.voice.getSpeakers() instead
*/
async getSpeakers() {
if (!this.voice) {
throw new Error("No voice provider configured");
}
this.logger.warn("Warning: agent.getSpeakers() is deprecated. Please use agent.voice.getSpeakers() instead.");
try {
return await this.voice.getSpeakers();
} catch (e) {
this.logger.error("Error during agent getSpeakers", {
error: e
});
throw e;
}
}
toStep() {
const x = agentToStep(this);
return new Step(x);
}
};
exports.Agent = /*@__PURE__*/(_ => {
_init = chunkRWTSGWWL_cjs.__decoratorStart(_a);
exports.Agent = chunkRWTSGWWL_cjs.__decorateElement(_init, 0, "Agent", _Agent_decorators, exports.Agent);
chunkRWTSGWWL_cjs.__runInitializers(_init, 1, exports.Agent);
// src/workflows/utils.ts
return exports.Agent;
})();
// src/workflows/utils.ts
function isErrorEvent(stateEvent) {
return stateEvent.type.startsWith("xstate.error.actor.");
}
function isTransitionEvent(stateEvent) {
return stateEvent.type.startsWith("xstate.done.actor.");
}
function isVariableReference(value) {
return typeof value === "object" && "step" in value && "path" in value;
}
function getStepResult(result) {
if (result?.status === "success") return result.output;
return void 0;
}
function getSuspendedPaths({
value,
path,
suspendedPaths
}) {
if (typeof value === "string") {
if (value === "suspended") {
suspendedPaths.add(path);
}
} else {
Object.keys(value).forEach(key => getSuspendedPaths({
value: value[key],
path: path ? `${path}.${key}` : key,
suspendedPaths
}));
}
}
function isFinalState(status) {
return ["completed", "failed"].includes(status);
}
function isLimboState(status) {
return status === "limbo";
}
function recursivelyCheckForFinalState({
value,
suspendedPaths,
path
}) {
if (typeof value === "string") {
return isFinalState(value) || isLimboState(value) || suspendedPaths.has(path);
}
return Object.keys(value).every(key => recursivelyCheckForFinalState({
value: value[key],
suspendedPaths,
path: path ? `${path}.${key}` : key
}));
}
function getActivePathsAndStatus(value) {
const paths = [];
const traverse = (current, path = []) => {
for (const [key, value2] of Object.entries(current)) {
const currentPath = [...path, key];
if (typeof value2 === "string") {
paths.push({
stepPath: currentPath,
stepId: key,
status: value2
});
} else if (typeof value2 === "object" && value2 !== null) {
traverse(value2, currentPath);
}
}
};
traverse(value);
return paths;
}
function mergeChildValue(startStepId, parent, child) {
const traverse = current => {
const obj = {};
for (const [key, value] of Object.entries(current)) {
if (key === startStepId) {
obj[key] = {
...child
};
} else if (typeof value === "string") {
obj[key] = value;
} else if (typeof value === "object" && value !== null) {
obj[key] = traverse(value);
}
}
return obj;
};
return traverse(parent);
}
var updateStepInHierarchy = (value, targetStepId) => {
const result = {};
for (const key of Object.keys(value)) {
const currentValue = value[key];
if (key === targetStepId) {
result[key] = "pending";
} else if (typeof currentValue === "object" && currentValue !== null) {
result[key] = updateStepInHierarchy(currentValue, targetStepId);
} else {
result[key] = currentValue;
}
}
return result;
};
function getResultActivePaths(state) {
const activePaths = getActivePathsAndStatus(state.value);
const activePathsAndStatus = activePaths.reduce((acc, curr) => {
const entry = {
status: curr.status,
stepPath: curr.stepPath
};
if (curr.status === "suspended") {
entry.suspendPayload = state.context.steps[curr.stepId].suspendPayload;
entry.stepPath = curr.stepPath;
}
acc.set(curr.stepId, entry);
return acc;
}, /* @__PURE__ */new Map());
return activePathsAndStatus;
}
function isWorkflow(step) {
return step instanceof Workflow;
}
function isAgent(step) {
return step instanceof exports.Agent;
}
function resolveVariables({
runId,
logger,
variables,
context
}) {
const resolvedData = {};
for (const [key, variable] of Object.entries(variables)) {
const sourceData = variable.step === "trigger" ? context.triggerData : getStepResult(context.steps[variable.step.id ?? variable.step.name]);
logger.debug(`Got source data for ${key} variable from ${variable.step === "trigger" ? "trigger" : variable.step.id ?? variable.step.name}`, {
sourceData,
path: variable.path,
runId
});
if (!sourceData && variable.step !== "trigger") {
resolvedData[key] = void 0;
continue;
}
const value = variable.path === "" || variable.path === "." ? sourceData : radash.get(sourceData, variable.path);
logger.debug(`Resolved variable ${key}`, {
value,
runId
});
resolvedData[key] = value;
}
return resolvedData;
}
function agentToStep(agent, {
mastra
} = {}) {
return {
id: agent.name,
inputSchema: zod.z.object({
prompt: zod.z.string(),
resourceId: zod.z.string().optional(),
threadId: zod.z.string().optional()
}),
outputSchema: zod.z.object({
text: zod.z.string()
}),
execute: async ({
context,
runId,
mastra: mastraFromExecute
}) => {
const realMastra = mastraFromExecute ?? mastra;
if (!realMastra) {
throw new Error("Mastra instance not found");
}
agent.__registerMastra(realMastra);
agent.__registerPrimitives({
logger: realMastra.getLogger(),
telemetry: realMastra.getTelemetry()
});
const result = await agent.generate(context.inputData.prompt, {
runId,
resourceId: context.inputData.resourceId,
threadId: context.inputData.threadId
});
return {
text: result.text
};
}
};
}
function workflowToStep(workflow, {
mastra
}) {
workflow.setNested(true);
return {
id: workflow.name,
workflow,
workflowId: toCamelCaseWithRandomSuffix(workflow.name),
execute: async ({
context,
suspend,
emit,
mastra: mastraFromExecute,
runtimeContext
}) => {
const realMastra = mastraFromExecute ?? mastra;
if (realMastra) {
workflow.__registerMastra(realMastra);
workflow.__registerPrimitives({
logger: realMastra.getLogger(),
telemetry: realMastra.getTelemetry()
});
}
const run = context.isResume ? workflow.createRun({
runId: context.isResume.runId
}) : workflow.createRun();
const unwatch = run.watch(state => {
emit("state-update", workflow.name, state.results, {
...context,
...{
[workflow.name]: state.results
}
});
});
const awaitedResult = context.isResume && context.isResume.stepId.includes(".") ? await run.resume({
stepId: context.isResume.stepId.split(".").slice(1).join("."),
context: context.inputData,
runtimeContext
}) : await run.start({
triggerData: context.inputData,
runtimeContext
});
unwatch();
if (!awaitedResult) {
throw new Error("Workflow run failed");
}
if (awaitedResult.activePaths?.size > 0) {
const suspendedStep = [...awaitedResult.activePaths.entries()].find(([, {
status
}]) => {
return status === "suspended";
});
if (suspendedStep) {
await suspend(suspendedStep[1].suspendPayload, {
...awaitedResult,
runId: run.runId
});
}
}
return {
...awaitedResult,
runId: run.runId
};
}
};
}
function toCamelCaseWithRandomSuffix(str) {
if (!str) return "";
const normalizedStr = str.replace(/[-_]/g, " ");
const words = normalizedStr.split(" ").filter(word => word.length > 0);
const camelCase = words.map((word, index) => {
word = word.replace(/[^a-zA-Z0-9]/g, "");
if (index === 0) {
return word.toLowerCase();
}
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
}).join("");
const randomString = generateRandomLetters(3);
return camelCase + randomString;
}
function generateRandomLetters(length) {
const characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
let result = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
result += characters.charAt(randomIndex);
}
return result;
}
function isConditionalKey(key) {
return key.startsWith("__") && (key.includes("_if") || key.includes("_else"));
}
var Machine = class extends EventEmitter__default.default {
logger;
#mastra;
#runtimeContext;
#workflowInstance;
#executionSpan;
#stepGraph;
#machine;
#runId;
#startStepId;
name;
#actor = null;
#steps = {};
#retryConfig;
constructor({
logger,
mastra,
runtimeContext,
workflowInstance,
executionSpan,
name,
runId,
steps,
stepGraph,
retryConfig,
startStepId
}) {
super();
this.#mastra = mastra;
this.#workflowInstance = workflowInstance;
this.#runtimeContext = runtimeContext;
this.#executionSpan = executionSpan;
this.logger = logger;
this.#runId = runId;
this.#startStepId = startStepId;
this.name = name;
this.#stepGraph = stepGraph;
this.#steps = steps;
this.#retryConfig = retryConfig;
this.initializeMachine();
}
get startStepId() {
return this.#startStepId;
}
async execute({
stepId,
input,
snapshot,
resumeData
} = {}) {
if (snapshot) {
this.logger.debug(`Workflow snapshot received`, {
runId: this.#runId,
snapshot
});
}
const origSteps = input.steps;
const isResumedInitialStep = this.#stepGraph?.initial[0]?.step?.id === stepId;
if (isResumedInitialStep) {
snapshot = void 0;
input.steps = {};
}
this.logger.debug(`Machine input prepared`, {
runId: this.#runId,
input
});
const actorSnapshot = snapshot ? {
...snapshot,
context: {
...input,
inputData: {
...(snapshot?.context?.inputData || {}),
...resumeData
},
// ts-ignore is needed here because our snapshot types don't really match xstate snapshot types right now. We should fix