@mastra/core
Version:
Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack.
1,659 lines (1,655 loc) • 51.2 kB
JavaScript
import { WorkflowEventProcessor } from './chunk-Z6QCWTTO.js';
import { saveScorePayloadSchema } from './chunk-UWTYVVVZ.js';
import { augmentWithInit } from './chunk-436FFEF6.js';
import { PubSub } from './chunk-BVUMKER5.js';
import { InstrumentClass, Telemetry } from './chunk-BLUDYAPI.js';
import { InMemoryServerCache } from './chunk-3NTOFNIU.js';
import { registerHook } from './chunk-TTELJD4F.js';
import { setupAITracing, getAllAITracing, shutdownAITracingRegistry } from './chunk-WIMFJ2BA.js';
import { noopLogger } from './chunk-PFXXH2RP.js';
import { MastraError } from './chunk-PZUZNPFM.js';
import { ConsoleLogger, LogLevel } from './chunk-UXG7PYML.js';
import { __decoratorStart, __decorateElement, __runInitializers } from './chunk-3HXBPDKN.js';
import { randomUUID } from 'crypto';
import EventEmitter from 'events';
import pMap from 'p-map';
var EventEmitterPubSub = class extends PubSub {
emitter;
constructor() {
super();
this.emitter = new EventEmitter();
}
async publish(topic, event) {
const id = crypto.randomUUID();
const createdAt = /* @__PURE__ */new Date();
this.emitter.emit(topic, {
...event,
id,
createdAt
});
}
async subscribe(topic, cb) {
this.emitter.on(topic, cb);
}
async unsubscribe(topic, cb) {
this.emitter.off(topic, cb);
}
async flush() {}
};
function createOnScorerHook(mastra) {
return async hookData => {
const storage = mastra.getStorage();
if (!storage) {
mastra.getLogger()?.warn("Storage not found, skipping score validation and saving");
return;
}
const entityId = hookData.entity.id;
const entityType = hookData.entityType;
const scorer = hookData.scorer;
try {
const scorerToUse = await findScorer(mastra, entityId, entityType, scorer.name);
if (!scorerToUse) {
throw new MastraError({
id: "MASTRA_SCORER_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Scorer with ID ${hookData.scorer.id} not found`
});
}
let input = hookData.input;
let output = hookData.output;
const {
structuredOutput,
...rest
} = hookData;
const runResult = await scorerToUse.scorer.run({
...rest,
input,
output
});
let spanId;
let traceId;
const currentSpan = hookData.tracingContext?.currentSpan;
if (currentSpan && currentSpan.isValid) {
spanId = currentSpan.id;
traceId = currentSpan.traceId;
}
const payload = {
...rest,
...runResult,
entityId,
scorerId: hookData.scorer.name,
spanId,
traceId,
metadata: {
structuredOutput: !!structuredOutput
}
};
await validateAndSaveScore(storage, payload);
if (currentSpan && spanId && traceId) {
await pMap(currentSpan.aiTracing.getExporters(), async exporter => {
if (exporter.addScoreToTrace) {
await exporter.addScoreToTrace({
traceId,
spanId,
score: runResult.score,
reason: runResult.reason,
scorerName: scorerToUse.scorer.name,
metadata: {
...(currentSpan.metadata ?? {})
}
});
}
}, {
concurrency: 3
});
}
} catch (error) {
const mastraError = new MastraError({
id: "MASTRA_SCORER_FAILED_TO_RUN_HOOK",
domain: "SCORER" /* SCORER */,
category: "USER" /* USER */,
details: {
scorerId: scorer.id,
entityId,
entityType
}
}, error);
mastra.getLogger()?.trackException(mastraError);
mastra.getLogger()?.error(mastraError.toString());
}
};
}
async function validateAndSaveScore(storage, payload) {
const payloadToSave = saveScorePayloadSchema.parse(payload);
await storage?.saveScore(payloadToSave);
}
async function findScorer(mastra, entityId, entityType, scorerName) {
let scorerToUse;
if (entityType === "AGENT") {
const scorers = await mastra.getAgentById(entityId).getScorers();
for (const [_, scorer] of Object.entries(scorers)) {
if (scorer.scorer.name === scorerName) {
scorerToUse = scorer;
break;
}
}
} else if (entityType === "WORKFLOW") {
const scorers = await mastra.getWorkflowById(entityId).getScorers();
for (const [_, scorer] of Object.entries(scorers)) {
if (scorer.scorer.name === scorerName) {
scorerToUse = scorer;
break;
}
}
}
if (!scorerToUse) {
const mastraRegisteredScorer = mastra.getScorerByName(scorerName);
scorerToUse = mastraRegisteredScorer ? {
scorer: mastraRegisteredScorer
} : void 0;
}
return scorerToUse;
}
// src/mastra/index.ts
var _Mastra_decorators, _init;
_Mastra_decorators = [InstrumentClass({
prefix: "mastra",
excludeMethods: ["getLogger", "getTelemetry"]
})];
var Mastra = class {
#vectors;
#agents;
#logger;
#legacy_workflows;
#workflows;
#tts;
#deployer;
#serverMiddleware = [];
/**
* @deprecated Use {@link getAITracing()} instead.
*/
#telemetry;
#storage;
#memory;
#scorers;
#server;
#mcpServers;
#bundler;
#idGenerator;
#pubsub;
#gateways;
#events = {};
#internalMastraWorkflows = {};
// This is only used internally for server handlers that require temporary persistence
#serverCache;
/**
* @deprecated use {@link getAITracing()} instead
*/
get telemetry() {
return this.#telemetry;
}
/**
* @deprecated use getStorage() instead
*/
get storage() {
return this.#storage;
}
/**
* @deprecated use getMemory() instead
*/
get memory() {
return this.#memory;
}
get pubsub() {
return this.#pubsub;
}
/**
* Gets the currently configured ID generator function.
*
* @example
* ```typescript
* const mastra = new Mastra({
* idGenerator: () => `custom-${Date.now()}`
* });
* const generator = mastra.getIdGenerator();
* console.log(generator?.()); // "custom-1234567890"
* ```
*/
getIdGenerator() {
return this.#idGenerator;
}
/**
* Generates a unique identifier using the configured generator or defaults to `crypto.randomUUID()`.
*
* This method is used internally by Mastra for creating unique IDs for various entities
* like workflow runs, agent conversations, and other resources that need unique identification.
*
* @throws {MastraError} When the custom ID generator returns an empty string
*
* @example
* ```typescript
* const mastra = new Mastra();
* const id = mastra.generateId();
* console.log(id); // "550e8400-e29b-41d4-a716-446655440000"
* ```
*/
generateId() {
if (this.#idGenerator) {
const id = this.#idGenerator();
if (!id) {
const error = new MastraError({
id: "MASTRA_ID_GENERATOR_RETURNED_EMPTY_STRING",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: "ID generator returned an empty string, which is not allowed"
});
this.#logger?.trackException(error);
throw error;
}
return id;
}
return randomUUID();
}
/**
* Sets a custom ID generator function for creating unique identifiers.
*
* The ID generator function will be used by `generateId()` instead of the default
* `crypto.randomUUID()`. This is useful for creating application-specific ID formats
* or integrating with existing ID generation systems.
*
* @example
* ```typescript
* const mastra = new Mastra();
* mastra.setIdGenerator(() => `custom-${Date.now()}`);
* const id = mastra.generateId();
* console.log(id); // "custom-1234567890"
* ```
*/
setIdGenerator(idGenerator) {
this.#idGenerator = idGenerator;
}
/**
* Creates a new Mastra instance with the provided configuration.
*
* The constructor initializes all the components specified in the config, sets up
* internal systems like logging and telemetry, and registers components with each other.
*
* @example
* ```typescript
* const mastra = new Mastra({
* agents: {
* assistant: new Agent({
* name: 'assistant',
* instructions: 'You are a helpful assistant',
* model: 'openai/gpt-5'
* })
* },
* storage: new PostgresStore({
* connectionString: process.env.DATABASE_URL
* }),
* logger: new PinoLogger({ name: 'MyApp' })
* });
* ```
*/
constructor(config) {
if (config?.serverMiddleware) {
this.#serverMiddleware = config.serverMiddleware.map(m => ({
handler: m.handler,
path: m.path || "/api/*"
}));
}
this.#serverCache = new InMemoryServerCache();
if (config?.pubsub) {
this.#pubsub = config.pubsub;
} else {
this.#pubsub = new EventEmitterPubSub();
}
this.#events = {};
for (const topic in config?.events ?? {}) {
if (!Array.isArray(config?.events?.[topic])) {
this.#events[topic] = [config?.events?.[topic]];
} else {
this.#events[topic] = config?.events?.[topic] ?? [];
}
}
const workflowEventProcessor = new WorkflowEventProcessor({
mastra: this
});
const workflowEventCb = async (event, cb) => {
try {
await workflowEventProcessor.process(event, cb);
} catch (e) {
console.error("Error processing event", e);
}
};
if (this.#events.workflows) {
this.#events.workflows.push(workflowEventCb);
} else {
this.#events.workflows = [workflowEventCb];
}
let logger;
if (config?.logger === false) {
logger = noopLogger;
} else {
if (config?.logger) {
logger = config.logger;
} else {
const levelOnEnv = process.env.NODE_ENV === "production" && process.env.MASTRA_DEV !== "true" ? LogLevel.WARN : LogLevel.INFO;
logger = new ConsoleLogger({
name: "Mastra",
level: levelOnEnv
});
}
}
this.#logger = logger;
this.#idGenerator = config?.idGenerator;
let storage = config?.storage;
if (storage) {
storage = augmentWithInit(storage);
}
this.#telemetry = Telemetry.init(config?.telemetry);
if (config?.telemetry?.enabled !== false && typeof globalThis !== "undefined" && globalThis.___MASTRA_TELEMETRY___ !== true) {
this.#logger?.warn(`Mastra telemetry is enabled, but the required instrumentation file was not loaded. If you are using Mastra outside of the mastra server environment, see: https://mastra.ai/en/docs/observability/tracing#tracing-outside-mastra-server-environment`, `If you are using a custom instrumentation file or want to disable this warning, set the globalThis.___MASTRA_TELEMETRY___ variable to true in your instrumentation file.`);
}
if (config?.telemetry?.enabled !== false) {
this.#logger?.warn(`Mastra telemetry is deprecated and will be removed on the Nov 4th release. Instead use AI Tracing. More info can be found here: https://github.com/mastra-ai/mastra/issues/8577 and here: https://mastra.ai/en/docs/observability/ai-tracing/overview`);
}
if (config?.observability) {
setupAITracing(config.observability);
}
if (this.#telemetry && storage) {
this.#storage = this.#telemetry.traceClass(storage, {
excludeMethods: ["__setTelemetry", "__getTelemetry", "batchTraceInsert", "getTraces", "getEvalsByAgentName"]
});
this.#storage.__setTelemetry(this.#telemetry);
} else {
this.#storage = storage;
}
this.#gateways = config?.gateways;
if (config?.gateways) {
this.#syncGatewayRegistry();
}
if (config?.vectors) {
let vectors = {};
Object.entries(config.vectors).forEach(([key, vector]) => {
if (this.#telemetry) {
vectors[key] = this.#telemetry.traceClass(vector, {
excludeMethods: ["__setTelemetry", "__getTelemetry"]
});
vectors[key].__setTelemetry(this.#telemetry);
} else {
vectors[key] = vector;
}
});
this.#vectors = vectors;
}
if (config?.mcpServers) {
this.#mcpServers = config.mcpServers;
Object.entries(this.#mcpServers).forEach(([key, server]) => {
server.setId(key);
if (this.#telemetry) {
server.__setTelemetry(this.#telemetry);
}
server.__registerMastra(this);
server.__setLogger(this.getLogger());
});
}
if (config && `memory` in config) {
const error = new MastraError({
id: "MASTRA_CONSTRUCTOR_INVALID_MEMORY_CONFIG",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `
Memory should be added to Agents, not to Mastra.
Instead of:
new Mastra({ memory: new Memory() })
do:
new Agent({ memory: new Memory() })
`
});
this.#logger?.trackException(error);
throw error;
}
if (config?.tts) {
this.#tts = config.tts;
Object.entries(this.#tts).forEach(([key, ttsCl]) => {
if (this.#tts?.[key]) {
if (this.#telemetry) {
this.#tts[key] = this.#telemetry.traceClass(ttsCl, {
excludeMethods: ["__setTelemetry", "__getTelemetry"]
});
this.#tts[key].__setTelemetry(this.#telemetry);
}
}
});
}
const agents = {};
if (config?.agents) {
Object.entries(config.agents).forEach(([key, agent]) => {
if (agents[key]) {
const error = new MastraError({
id: "MASTRA_AGENT_REGISTRATION_DUPLICATE_ID",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Agent with name ID:${key} already exists`,
details: {
agentId: key
}
});
this.#logger?.trackException(error);
throw error;
}
agent.__registerMastra(this);
agent.__registerPrimitives({
logger: this.getLogger(),
telemetry: this.#telemetry,
storage: this.storage,
memory: this.memory,
agents,
tts: this.#tts,
vectors: this.#vectors
});
agents[key] = agent;
});
}
this.#agents = agents;
const scorers = {};
if (config?.scorers) {
Object.entries(config.scorers).forEach(([key, scorer]) => {
scorers[key] = scorer;
});
}
this.#scorers = scorers;
this.#legacy_workflows = {};
if (config?.legacy_workflows) {
Object.entries(config.legacy_workflows).forEach(([key, workflow]) => {
workflow.__registerMastra(this);
workflow.__registerPrimitives({
logger: this.getLogger(),
telemetry: this.#telemetry,
storage: this.storage,
memory: this.memory,
agents,
tts: this.#tts,
vectors: this.#vectors
});
this.#legacy_workflows[key] = workflow;
const workflowSteps = Object.values(workflow.steps).filter(step => !!step.workflowId && !!step.workflow);
if (workflowSteps.length > 0) {
workflowSteps.forEach(step => {
this.#legacy_workflows[step.workflowId] = step.workflow;
});
}
});
}
this.#workflows = {};
if (config?.workflows) {
Object.entries(config.workflows).forEach(([key, workflow]) => {
workflow.__registerMastra(this);
workflow.__registerPrimitives({
logger: this.getLogger(),
telemetry: this.#telemetry,
storage: this.storage,
memory: this.memory,
agents,
tts: this.#tts,
vectors: this.#vectors
});
this.#workflows[key] = workflow;
});
}
if (config?.server) {
this.#server = config.server;
}
registerHook("onScorerRun" /* ON_SCORER_RUN */, createOnScorerHook(this));
if (config?.observability) {
this.registerAITracingExporters();
this.initAITracingExporters();
}
this.setLogger({
logger
});
}
/**
* Register this Mastra instance with AI tracing exporters that need it
*/
registerAITracingExporters() {
const allTracingInstances = getAllAITracing();
allTracingInstances.forEach(tracing => {
const exporters = tracing.getExporters();
exporters.forEach(exporter => {
if ("__registerMastra" in exporter && typeof exporter.__registerMastra === "function") {
exporter.__registerMastra(this);
}
});
});
}
/**
* Initialize all AI tracing exporters after registration is complete
*/
initAITracingExporters() {
const allTracingInstances = getAllAITracing();
allTracingInstances.forEach(tracing => {
const config = tracing.getConfig();
const exporters = tracing.getExporters();
exporters.forEach(exporter => {
if ("init" in exporter && typeof exporter.init === "function") {
try {
exporter.init(config);
} catch (error) {
this.#logger?.warn("Failed to initialize AI tracing exporter", {
exporterName: exporter.name,
error: error instanceof Error ? error.message : String(error)
});
}
}
});
});
}
/**
* Retrieves a registered agent by its name.
*
* @template TAgentName - The specific agent name type from the registered agents
* @throws {MastraError} When the agent with the specified name is not found
*
* @example
* ```typescript
* const mastra = new Mastra({
* agents: {
* weatherAgent: new Agent({
* name: 'weather-agent',
* instructions: 'You provide weather information',
* model: 'openai/gpt-5'
* })
* }
* });
* const agent = mastra.getAgent('weatherAgent');
* const response = await agent.generate('What is the weather?');
* ```
*/
getAgent(name) {
const agent = this.#agents?.[name];
if (!agent) {
const error = new MastraError({
id: "MASTRA_GET_AGENT_BY_NAME_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Agent with name ${String(name)} not found`,
details: {
status: 404,
agentName: String(name),
agents: Object.keys(this.#agents ?? {}).join(", ")
}
});
this.#logger?.trackException(error);
throw error;
}
return this.#agents[name];
}
/**
* Retrieves a registered agent by its unique ID.
*
* This method searches for an agent using its internal ID property. If no agent
* is found with the given ID, it also attempts to find an agent using the ID as
* a name (for backward compatibility).
*
* @throws {MastraError} When no agent is found with the specified ID
*
* @example
* ```typescript
* const mastra = new Mastra({
* agents: {
* assistant: new Agent({
* name: 'assistant',
* instructions: 'You are a helpful assistant',
* model: 'openai/gpt-5'
* })
* }
* });
*
* const assistant = mastra.getAgent('assistant');
* const sameAgent = mastra.getAgentById(assistant.id);
* ```
*/
getAgentById(id) {
let agent = Object.values(this.#agents).find(a => a.id === id);
if (!agent) {
try {
agent = this.getAgent(id);
} catch {}
}
if (!agent) {
const error = new MastraError({
id: "MASTRA_GET_AGENT_BY_AGENT_ID_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Agent with id ${String(id)} not found`,
details: {
status: 404,
agentId: String(id),
agents: Object.keys(this.#agents ?? {}).join(", ")
}
});
this.#logger?.trackException(error);
throw error;
}
return agent;
}
/**
* Returns all registered agents as a record keyed by their names.
*
* This method provides access to the complete registry of agents, allowing you to
* iterate over them, check what agents are available, or perform bulk operations.
*
* @example
* ```typescript
* const mastra = new Mastra({
* agents: {
* weatherAgent: new Agent({ name: 'weather', model: openai('gpt-4o') }),
* supportAgent: new Agent({ name: 'support', model: openai('gpt-4o') })
* }
* });
*
* const allAgents = mastra.getAgents();
* console.log(Object.keys(allAgents)); // ['weatherAgent', 'supportAgent']
* ```
*/
getAgents() {
return this.#agents;
}
/**
* Retrieves a registered vector store by its name.
*
* @template TVectorName - The specific vector store name type from the registered vectors
* @throws {MastraError} When the vector store with the specified name is not found
*
* @example Using a vector store for semantic search
* ```typescript
* import { PineconeVector } from '@mastra/pinecone';
* import { OpenAIEmbedder } from '@mastra/embedders';
*
* const mastra = new Mastra({
* vectors: {
* knowledge: new PineconeVector({
* apiKey: process.env.PINECONE_API_KEY,
* indexName: 'knowledge-base',
* embedder: new OpenAIEmbedder({
* apiKey: process.env.OPENAI_API_KEY,
* model: 'text-embedding-3-small'
* })
* }),
* products: new PineconeVector({
* apiKey: process.env.PINECONE_API_KEY,
* indexName: 'product-catalog'
* })
* }
* });
*
* // Get a vector store and perform semantic search
* const knowledgeBase = mastra.getVector('knowledge');
* const results = await knowledgeBase.query({
* query: 'How to reset password?',
* topK: 5
* });
*
* console.log('Relevant documents:', results);
* ```
*/
getVector(name) {
const vector = this.#vectors?.[name];
if (!vector) {
const error = new MastraError({
id: "MASTRA_GET_VECTOR_BY_NAME_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Vector with name ${String(name)} not found`,
details: {
status: 404,
vectorName: String(name),
vectors: Object.keys(this.#vectors ?? {}).join(", ")
}
});
this.#logger?.trackException(error);
throw error;
}
return vector;
}
/**
* Returns all registered vector stores as a record keyed by their names.
*
* @example Listing all vector stores
* ```typescript
* const mastra = new Mastra({
* vectors: {
* documents: new PineconeVector({ indexName: 'docs' }),
* images: new PineconeVector({ indexName: 'images' }),
* products: new ChromaVector({ collectionName: 'products' })
* }
* });
*
* const allVectors = mastra.getVectors();
* console.log(Object.keys(allVectors)); // ['documents', 'images', 'products']
*
* // Check vector store types and configurations
* for (const [name, vectorStore] of Object.entries(allVectors)) {
* console.log(`Vector store ${name}:`, vectorStore.constructor.name);
* }
* ```
*/
getVectors() {
return this.#vectors;
}
/**
* Gets the currently configured deployment provider.
*
* @example
* ```typescript
* const mastra = new Mastra({
* deployer: new VercelDeployer({
* token: process.env.VERCEL_TOKEN,
* projectId: process.env.VERCEL_PROJECT_ID
* })
* });
*
* const deployer = mastra.getDeployer();
* if (deployer) {
* await deployer.deploy({
* name: 'my-mastra-app',
* environment: 'production'
* });
* }
* ```
*/
getDeployer() {
return this.#deployer;
}
/**
* Retrieves a registered legacy workflow by its ID.
*
* Legacy workflows are the previous generation of workflow system in Mastra,
* maintained for backward compatibility. For new implementations, use the
* modern workflow system accessed via `getWorkflow()`.
*
* @template TWorkflowId - The specific workflow ID type from the registered legacy workflows
* @throws {MastraError} When the legacy workflow with the specified ID is not found
* @deprecated Use `getWorkflow()` for new implementations
*
* @example Getting a legacy workflow
* ```typescript
* const mastra = new Mastra({
* legacy_workflows: {
* oldDataFlow: legacyWorkflowInstance
* }
* });
*
* const workflow = mastra.legacy_getWorkflow('oldDataFlow');
* const result = await workflow.execute({ input: 'data' });
* ```
*/
legacy_getWorkflow(id, {
serialized
} = {}) {
const workflow = this.#legacy_workflows?.[id];
if (!workflow) {
const error = new MastraError({
id: "MASTRA_GET_LEGACY_WORKFLOW_BY_ID_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Workflow with ID ${String(id)} not found`,
details: {
status: 404,
workflowId: String(id),
workflows: Object.keys(this.#legacy_workflows ?? {}).join(", ")
}
});
this.#logger?.trackException(error);
throw error;
}
if (serialized) {
return {
name: workflow.name
};
}
return workflow;
}
/**
* Retrieves a registered workflow by its ID.
*
* @template TWorkflowId - The specific workflow ID type from the registered workflows
* @throws {MastraError} When the workflow with the specified ID is not found
*
* @example Getting and executing a workflow
* ```typescript
* import { createWorkflow, createStep } from '@mastra/core/workflows';
* import { z } from 'zod';
*
* const processDataWorkflow = createWorkflow({
* name: 'process-data',
* triggerSchema: z.object({ input: z.string() })
* })
* .then(validateStep)
* .then(transformStep)
* .then(saveStep)
* .commit();
*
* const mastra = new Mastra({
* workflows: {
* dataProcessor: processDataWorkflow
* }
* });
* ```
*/
getWorkflow(id, {
serialized
} = {}) {
const workflow = this.#workflows?.[id];
if (!workflow) {
const error = new MastraError({
id: "MASTRA_GET_WORKFLOW_BY_ID_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Workflow with ID ${String(id)} not found`,
details: {
status: 404,
workflowId: String(id),
workflows: Object.keys(this.#workflows ?? {}).join(", ")
}
});
this.#logger?.trackException(error);
throw error;
}
if (serialized) {
return {
name: workflow.name
};
}
return workflow;
}
__registerInternalWorkflow(workflow) {
workflow.__registerMastra(this);
workflow.__registerPrimitives({
logger: this.getLogger(),
storage: this.storage
});
this.#internalMastraWorkflows[workflow.id] = workflow;
}
__hasInternalWorkflow(id) {
return Object.values(this.#internalMastraWorkflows).some(workflow => workflow.id === id);
}
__getInternalWorkflow(id) {
const workflow = Object.values(this.#internalMastraWorkflows).find(a => a.id === id);
if (!workflow) {
throw new MastraError({
id: "MASTRA_GET_INTERNAL_WORKFLOW_BY_ID_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "SYSTEM" /* SYSTEM */,
text: `Workflow with id ${String(id)} not found`,
details: {
status: 404,
workflowId: String(id)
}
});
}
return workflow;
}
/**
* Retrieves a registered workflow by its unique ID.
*
* This method searches for a workflow using its internal ID property. If no workflow
* is found with the given ID, it also attempts to find a workflow using the ID as
* a name (for backward compatibility).
*
* @throws {MastraError} When no workflow is found with the specified ID
*
* @example Finding a workflow by ID
* ```typescript
* const mastra = new Mastra({
* workflows: {
* dataProcessor: createWorkflow({
* name: 'process-data',
* triggerSchema: z.object({ input: z.string() })
* }).commit()
* }
* });
*
* // Get the workflow's ID
* const workflow = mastra.getWorkflow('dataProcessor');
* const workflowId = workflow.id;
*
* // Later, retrieve the workflow by ID
* const sameWorkflow = mastra.getWorkflowById(workflowId);
* console.log(sameWorkflow.name); // "process-data"
* ```
*/
getWorkflowById(id) {
let workflow = Object.values(this.#workflows).find(a => a.id === id);
if (!workflow) {
try {
workflow = this.getWorkflow(id);
} catch {}
}
if (!workflow) {
const error = new MastraError({
id: "MASTRA_GET_WORKFLOW_BY_ID_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Workflow with id ${String(id)} not found`,
details: {
status: 404,
workflowId: String(id),
workflows: Object.keys(this.#workflows ?? {}).join(", ")
}
});
this.#logger?.trackException(error);
throw error;
}
return workflow;
}
/**
* Returns all registered legacy workflows as a record keyed by their IDs.
*
* Legacy workflows are the previous generation of workflow system in Mastra,
* maintained for backward compatibility. For new implementations, use `getWorkflows()`.
*
* @deprecated Use `getWorkflows()` for new implementations
*
* @example Listing all legacy workflows
* ```typescript
* const mastra = new Mastra({
* legacy_workflows: {
* oldFlow1: legacyWorkflow1,
* oldFlow2: legacyWorkflow2
* }
* });
*
* const allLegacyWorkflows = mastra.legacy_getWorkflows();
* console.log(Object.keys(allLegacyWorkflows)); // ['oldFlow1', 'oldFlow2']
*
* // Execute all legacy workflows
* for (const [id, workflow] of Object.entries(allLegacyWorkflows)) {
* console.log(`Legacy workflow ${id}:`, workflow.name);
* }
* ```
*/
legacy_getWorkflows(props = {}) {
if (props.serialized) {
return Object.entries(this.#legacy_workflows).reduce((acc, [k, v]) => {
return {
...acc,
[k]: {
name: v.name
}
};
}, {});
}
return this.#legacy_workflows;
}
/**
* Returns all registered scorers as a record keyed by their IDs.
*
* @example Listing all scorers
* ```typescript
* import { HelpfulnessScorer, AccuracyScorer, RelevanceScorer } from '@mastra/scorers';
*
* const mastra = new Mastra({
* scorers: {
* helpfulness: new HelpfulnessScorer(),
* accuracy: new AccuracyScorer(),
* relevance: new RelevanceScorer()
* }
* });
*
* const allScorers = mastra.getScorers();
* console.log(Object.keys(allScorers)); // ['helpfulness', 'accuracy', 'relevance']
*
* // Check scorer configurations
* for (const [id, scorer] of Object.entries(allScorers)) {
* console.log(`Scorer ${id}:`, scorer.name, scorer.description);
* }
* ```
*/
getScorers() {
return this.#scorers;
}
/**
* Retrieves a registered scorer by its key.
*
* @template TScorerKey - The specific scorer key type from the registered scorers
* @throws {MastraError} When the scorer with the specified key is not found
*
* @example Getting and using a scorer
* ```typescript
* import { HelpfulnessScorer, AccuracyScorer } from '@mastra/scorers';
*
* const mastra = new Mastra({
* scorers: {
* helpfulness: new HelpfulnessScorer({
* model: openai('gpt-4o'),
* criteria: 'Rate how helpful this response is'
* }),
* accuracy: new AccuracyScorer({
* model: 'openai/gpt-5'
* })
* }
* });
*
* // Get a specific scorer
* const helpfulnessScorer = mastra.getScorer('helpfulness');
* const score = await helpfulnessScorer.score({
* input: 'How do I reset my password?',
* output: 'You can reset your password by clicking the forgot password link.',
* expected: 'Detailed password reset instructions'
* });
*
* console.log('Helpfulness score:', score);
* ```
*/
getScorer(key) {
const scorer = this.#scorers?.[key];
if (!scorer) {
const error = new MastraError({
id: "MASTRA_GET_SCORER_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Scorer with ${String(key)} not found`
});
this.#logger?.trackException(error);
throw error;
}
return scorer;
}
/**
* Retrieves a registered scorer by its name.
*
* This method searches through all registered scorers to find one with the specified name.
* Unlike `getScorer()` which uses the registration key, this method uses the scorer's
* internal name property.
*
* @throws {MastraError} When no scorer is found with the specified name
*
* @example Finding a scorer by name
* ```typescript
* import { HelpfulnessScorer } from '@mastra/scorers';
*
* const mastra = new Mastra({
* scorers: {
* myHelpfulnessScorer: new HelpfulnessScorer({
* name: 'helpfulness-evaluator',
* model: 'openai/gpt-5'
* })
* }
* });
*
* // Find scorer by its internal name, not the registration key
* const scorer = mastra.getScorerByName('helpfulness-evaluator');
* const score = await scorer.score({
* input: 'question',
* output: 'answer'
* });
* ```
*/
getScorerByName(name) {
for (const [_key, value] of Object.entries(this.#scorers ?? {})) {
if (value.name === name) {
return value;
}
}
const error = new MastraError({
id: "MASTRA_GET_SCORER_BY_NAME_NOT_FOUND",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Scorer with name ${String(name)} not found`
});
this.#logger?.trackException(error);
throw error;
}
/**
* Returns all registered workflows as a record keyed by their IDs.
*
* @example Listing all workflows
* ```typescript
* const mastra = new Mastra({
* workflows: {
* dataProcessor: createWorkflow({...}).commit(),
* emailSender: createWorkflow({...}).commit(),
* reportGenerator: createWorkflow({...}).commit()
* }
* });
*
* const allWorkflows = mastra.getWorkflows();
* console.log(Object.keys(allWorkflows)); // ['dataProcessor', 'emailSender', 'reportGenerator']
*
* // Execute all workflows with sample data
* for (const [id, workflow] of Object.entries(allWorkflows)) {
* console.log(`Workflow ${id}:`, workflow.name);
* // const result = await workflow.execute(sampleData);
* }
* ```
*/
getWorkflows(props = {}) {
if (props.serialized) {
return Object.entries(this.#workflows).reduce((acc, [k, v]) => {
return {
...acc,
[k]: {
name: v.name
}
};
}, {});
}
return this.#workflows;
}
/**
* Sets the storage provider for the Mastra instance.
*
* @example
* ```typescript
* const mastra = new Mastra();
*
* // Set PostgreSQL storage
* mastra.setStorage(new PostgresStore({
* connectionString: process.env.DATABASE_URL
* }));
*
* // Now agents can use memory with the storage
* const agent = new Agent({
* name: 'assistant',
* memory: new Memory({ storage: mastra.getStorage() })
* });
* ```
*/
setStorage(storage) {
this.#storage = augmentWithInit(storage);
}
setLogger({
logger
}) {
this.#logger = logger;
if (this.#agents) {
Object.keys(this.#agents).forEach(key => {
this.#agents?.[key]?.__setLogger(this.#logger);
});
}
if (this.#memory) {
this.#memory.__setLogger(this.#logger);
}
if (this.#deployer) {
this.#deployer.__setLogger(this.#logger);
}
if (this.#tts) {
Object.keys(this.#tts).forEach(key => {
this.#tts?.[key]?.__setLogger(this.#logger);
});
}
if (this.#storage) {
this.#storage.__setLogger(this.#logger);
}
if (this.#vectors) {
Object.keys(this.#vectors).forEach(key => {
this.#vectors?.[key]?.__setLogger(this.#logger);
});
}
if (this.#mcpServers) {
Object.keys(this.#mcpServers).forEach(key => {
this.#mcpServers?.[key]?.__setLogger(this.#logger);
});
}
const allTracingInstances = getAllAITracing();
allTracingInstances.forEach(instance => {
instance.__setLogger(this.#logger);
});
}
setTelemetry(telemetry) {
this.#telemetry = Telemetry.init(telemetry);
if (this.#agents) {
Object.keys(this.#agents).forEach(key => {
if (this.#telemetry) {
this.#agents?.[key]?.__setTelemetry(this.#telemetry);
}
});
}
if (this.#memory) {
this.#memory = this.#telemetry.traceClass(this.#memory, {
excludeMethods: ["__setTelemetry", "__getTelemetry"]
});
this.#memory.__setTelemetry(this.#telemetry);
}
if (this.#deployer) {
this.#deployer = this.#telemetry.traceClass(this.#deployer, {
excludeMethods: ["__setTelemetry", "__getTelemetry"]
});
this.#deployer.__setTelemetry(this.#telemetry);
}
if (this.#tts) {
let tts = {};
Object.entries(this.#tts).forEach(([key, ttsCl]) => {
if (this.#telemetry) {
tts[key] = this.#telemetry.traceClass(ttsCl, {
excludeMethods: ["__setTelemetry", "__getTelemetry"]
});
tts[key].__setTelemetry(this.#telemetry);
}
});
this.#tts = tts;
}
if (this.#storage) {
this.#storage = this.#telemetry.traceClass(this.#storage, {
excludeMethods: ["__setTelemetry", "__getTelemetry"]
});
this.#storage.__setTelemetry(this.#telemetry);
}
if (this.#vectors) {
let vectors = {};
Object.entries(this.#vectors).forEach(([key, vector]) => {
if (this.#telemetry) {
vectors[key] = this.#telemetry.traceClass(vector, {
excludeMethods: ["__setTelemetry", "__getTelemetry"]
});
vectors[key].__setTelemetry(this.#telemetry);
}
});
this.#vectors = vectors;
}
}
/**
* Gets all registered text-to-speech (TTS) providers.
*
* @example
* ```typescript
* const mastra = new Mastra({
* tts: {
* openai: new OpenAITTS({
* apiKey: process.env.OPENAI_API_KEY,
* voice: 'alloy'
* })
* }
* });
*
* const ttsProviders = mastra.getTTS();
* const openaiTTS = ttsProviders?.openai;
* if (openaiTTS) {
* const audioBuffer = await openaiTTS.synthesize('Hello, world!');
* }
* ```
*/
getTTS() {
return this.#tts;
}
/**
* Gets the currently configured logger instance.
*
* @example
* ```typescript
* const mastra = new Mastra({
* logger: new PinoLogger({
* name: 'MyApp',
* level: 'info'
* })
* });
*
* const logger = mastra.getLogger();
* logger.info('Application started');
* logger.error('An error occurred', { error: 'details' });
* ```
*/
getLogger() {
return this.#logger;
}
/**
* Gets the currently configured telemetry instance.
*
* @example
* ```typescript
* const mastra = new Mastra({
* telemetry: {
* enabled: true,
* serviceName: 'my-mastra-app'
* }
* });
*
* const telemetry = mastra.getTelemetry();
* if (telemetry) {
* const span = telemetry.startSpan('custom-operation');
* span.setAttributes({ operation: 'data-processing' });
* span.end();
* }
* ```
*
* @deprecated use {@link getAITracing()} instead
*/
getTelemetry() {
return this.#telemetry;
}
/**
* Gets the currently configured memory instance.
*
* @deprecated Memory should be configured directly on agents instead of on the Mastra instance.
* Use `new Agent({ memory: new Memory() })` instead.
*
* @example Legacy memory usage (deprecated)
* ```typescript
* // This approach is deprecated
* const mastra = new Mastra({
* // memory: new Memory() // This is no longer supported
* });
*
* // Use this instead:
* const agent = new Agent({
* name: 'assistant',
* memory: new Memory({
* storage: new LibSQLStore({ url: ':memory:' })
* })
* });
* ```
*/
getMemory() {
return this.#memory;
}
/**
* Gets the currently configured storage provider.
*
* @example
* ```typescript
* const mastra = new Mastra({
* storage: new LibSQLStore({ url: 'file:./data.db' })
* });
*
* // Use the storage in agent memory
* const agent = new Agent({
* name: 'assistant',
* memory: new Memory({
* storage: mastra.getStorage()
* })
* });
* ```
*/
getStorage() {
return this.#storage;
}
getServerMiddleware() {
return this.#serverMiddleware;
}
getServerCache() {
return this.#serverCache;
}
setServerMiddleware(serverMiddleware) {
if (typeof serverMiddleware === "function") {
this.#serverMiddleware = [{
handler: serverMiddleware,
path: "/api/*"
}];
return;
}
if (!Array.isArray(serverMiddleware)) {
const error = new MastraError({
id: "MASTRA_SET_SERVER_MIDDLEWARE_INVALID_TYPE",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: `Invalid middleware: expected a function or array, received ${typeof serverMiddleware}`
});
this.#logger?.trackException(error);
throw error;
}
this.#serverMiddleware = serverMiddleware.map(m => {
if (typeof m === "function") {
return {
handler: m,
path: "/api/*"
};
}
return {
handler: m.handler,
path: m.path || "/api/*"
};
});
}
getServer() {
return this.#server;
}
getBundlerConfig() {
return this.#bundler;
}
async getLogsByRunId({
runId,
transportId,
fromDate,
toDate,
logLevel,
filters,
page,
perPage
}) {
if (!transportId) {
const error = new MastraError({
id: "MASTRA_GET_LOGS_BY_RUN_ID_MISSING_TRANSPORT",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: "Transport ID is required",
details: {
runId,
transportId
}
});
this.#logger?.trackException(error);
throw error;
}
if (!this.#logger?.getLogsByRunId) {
const error = new MastraError({
id: "MASTRA_GET_LOGS_BY_RUN_ID_LOGGER_NOT_CONFIGURED",
domain: "MASTRA" /* MASTRA */,
category: "SYSTEM" /* SYSTEM */,
text: "Logger is not configured or does not support getLogsByRunId operation",
details: {
runId,
transportId
}
});
this.#logger?.trackException(error);
throw error;
}
return await this.#logger.getLogsByRunId({
runId,
transportId,
fromDate,
toDate,
logLevel,
filters,
page,
perPage
});
}
async getLogs(transportId, params) {
if (!transportId) {
const error = new MastraError({
id: "MASTRA_GET_LOGS_MISSING_TRANSPORT",
domain: "MASTRA" /* MASTRA */,
category: "USER" /* USER */,
text: "Transport ID is required",
details: {
transportId
}
});
this.#logger?.trackException(error);
throw error;
}
if (!this.#logger) {
const error = new MastraError({
id: "MASTRA_GET_LOGS_LOGGER_NOT_CONFIGURED",
domain: "MASTRA" /* MASTRA */,
category: "SYSTEM" /* SYSTEM */,
text: "Logger is not set",
details: {
transportId
}
});
throw error;
}
return await this.#logger.getLogs(transportId, params);
}
/**
* Gets all registered Model Context Protocol (MCP) server instances.
*
* @example
* ```typescript
* const mastra = new Mastra({
* mcpServers: {
* filesystem: new FileSystemMCPServer({
* rootPath: '/app/data'
* })
* }
* });
*
* const mcpServers = mastra.getMCPServers();
* if (mcpServers) {
* const fsServer = mcpServers.filesystem;
* const tools = await fsServer.getTools();
* }
* ```
*/
getMCPServers() {
return this.#mcpServers;
}
/**
* Retrieves a specific Model Context Protocol (MCP) server instance by its logical ID.
*
* This method searches for an MCP server using its logical ID. If a version is specified,
* it returns the exact version match. If no version is provided, it returns the server
* with the most recent release date.
*
* @example
* ```typescript
* const mastra = new Mastra({
* mcpServers: {
* filesystem: new FileSystemMCPServer({
* id: 'fs-server',
* version: '1.0.0',
* rootPath: '/app/data'
* })
* }
* });
*
* const fsServer = mastra.getMCPServer('fs-server');
* if (fsServer) {
* const tools = await fsServer.getTools();
* }
* ```
*/
getMCPServer(serverId, version) {
if (!this.#mcpServers) {
return void 0;
}
const allRegisteredServers = Object.values(this.#mcpServers || {});
const matchingLogicalIdServers = allRegisteredServers.filter(server => server.id === serverId);
if (matchingLogicalIdServers.length === 0) {
this.#logger?.debug(`No MCP servers found with logical ID: ${serverId}`);
return void 0;
}
if (version) {
const specificVersionServer = matchingLogicalIdServers.find(server => server.version === version);
if (!specificVersionServer) {
this.#logger?.debug(`MCP server with logical ID '${serverId}' found, but not version '${version}'.`);
}
return specificVersionServer;
} else {
if (matchingLogicalIdServers.length === 1) {
return matchingLogicalIdServers[0];
}
matchingLogicalIdServers.sort((a, b) => {
const dateAVal = a.releaseDate && typeof a.releaseDate === "string" ? new Date(a.releaseDate).getTime() : NaN;
const dateBVal = b.releaseDate && typeof b.releaseDate === "string" ? new Date(b.releaseDate).getTime() : NaN;
if (isNaN(dateAVal) && isNaN(dateBVal)) return 0;
if (isNaN(dateAVal)) return 1;
if (isNaN(dateBVal)) return -1;
return dateBVal - dateAVal;
});
if (matchingLogicalIdServers.length > 0) {
const latestServer = matchingLogicalIdServers[0];
if (latestServer && latestServer.releaseDate && typeof latestServer.releaseDate === "string" && !isNaN(new Date(latestServer.releaseDate).getTime())) {
return latestServer;
}
}
this.#logger?.warn(`Could not determine the latest server for logical ID '${serverId}' due to invalid or missing release dates, or no servers left after filtering.`);
return void 0;
}
}
async addTopicListener(topic, listener) {
await this.#pubsub.subscribe(topic, listener);
}
async removeTopicListener(topic, listener) {
await this.#pubsub.unsubscribe(topic, listener);
}
async startEventEngine() {
for (const topic in this.#events) {
if (!this.#events[topic]) {
continue;
}
const listeners = Array.isArray(this.#events[topic]) ? this.#events[topic] : [this.#events[topic]];
for (const listener of listeners) {
await this.#pubsub.subscribe(topic, listener);
}
}
}
async stopEventEngine() {
for (const topic in this.#events) {
if (!this.#events[topic]) {
continue;
}
const listeners = Array.isArray(this.#events[topic]) ? this.#events[topic] : [this.#events[topic]];
for (const listener of listeners) {
await this.#pubsub.unsubscribe(topic, listener);
}
}
await this.#pubsub.flush();
}
/**
* Returns all registered gateways as a record keyed by their names.
*
* @example
* ```typescript
* const mastra = new Mastra({
* gateways: {
* netlify: new NetlifyGateway(),
* custom: new CustomGateway()
* }
* });
*
* const allGateways = mastra.listGateways();
* console.log(Object.keys(allGateways)); // ['netlify', 'custom']
* ```
*/
listGateways() {
return this.#gateways;
}
/**
* Sync custom gateways with the GatewayRegistry for type generation
* @private
*/
#syncGatewayRegistry() {
try {
if (process.env.MASTRA_DEV !== "true" && process.env.MASTRA_DEV !== "1") {
return;
}
import('./provider-registry-THITZUJ7.js').then(async ({
GatewayRegistry
}) => {
const registry = GatewayRegistry.getInstance();
const customGateways = Object.values(this.#gateways || {});
registry.registerCustomGateways(customGateways);
const logger = this.getLogger();
logger.info("\u{1F504} Syncing custom gateway types...");
await registry.syncGateways(true);
logger.info("\u2705 Custom gateway types synced! Restart your TypeScript server to see autocomplete.");
}).catch(err => {
const logger = this.getLogger();
logger.debug("Gateway registry sync skipped:", err);
});
} catch (err) {
const logger = this.getLogger();
logger.debug("Gateway registry sync failed:", err);
}
}
/**
* Gracefully shuts down the Mastra instance and cleans up all resources.
*
* This method performs a clean shutdown of all Mastra c