@mastra/core
Version:
1 lines • 302 kB
Source Map (JSON)
{"version":3,"file":"mastra-CSCDBtDZ.cjs","names":["computeNextFireAt","createStep","z","dispatchDueNotifications","createWorkflow","MastraError","ErrorDomain","ErrorCategory","readPositiveIntEnv","#pubsubProxy","#pubsub","agentThreadStreamRuntime","#workers","#backgroundTaskManager","#findSchedulerWorker","#datasets","DatasetsManager","#idGenerator","#editor","#channels","#schedules","Schedules","#schedulesConfig","#versions","#environment","#toolPayloadTransform","#storedAgentsCache","#storedScorersCache","#logger","#server","#studio","#observability","NoOpObservability","#serverCache","InMemoryServerCache","#recoveryConfig","normalizeToolPayloadTransformPolicy","EventEmitterPubSub","#events","#workerFilter","#workersDisabled","OrchestrationWorker","BackgroundTaskWorker","noopLogger","#loggerExplicit","ConsoleLogger","LogLevel","#storageExplicit","InMemoryStore","#storageFallbackWarningPending","augmentWithInit","InMemoryDB","WorkflowsInMemory","BackgroundTasksInMemory","#observabilityExplicit","DualLogger","#storage","#backgroundTaskConfig","#ensureBackgroundTaskManager","#schedulerConfig","#notificationDispatchConfig","#vectors","#mcpServers","#tts","#agents","#scorers","#tools","#processors","#memory","#workflows","#gateways","#workspace","#hiddenWorkflowKeys","defaultGateways","getGatewayId","#serverExplicit","#studioExplicit","#harnesses","#onScorerHook","createOnScorerHook","BackgroundTaskManager","#registerToolWithBackgroundManager","#hasScheduledWorkflow","#schedulerRequested","#collectDeclarativeSchedules","computeNextFireAt","AgentChannels","isDurableAgentLike","createDurableAgent","isToolLoopAgentLike","toolLoopAgentToMastraAgent","#deployer","#workspaces","#internalMastraWorkflows","#runScopedWorkflowTimestamps","#runScopeRefcounts","#runScopes","createRunScope","#sweepStaleRunScopedWorkflows","#releaseRunScope","#ownsWorkflow","#runTracingContexts","#promptBlocks","#processorConfigurations","toJsonSchemaOrUndefined","normalizeWorkflowBuilderDefinition","#buildWorkflowRegistryIndex","collectNestedWorkflowIds","rehydrateWorkflow","#replaceStoredWorkflow","#loadStoredWorkflows","#workersStarted","#ensureSchedulingWorkersStarted","#notificationDispatchReady","#schedulingWorkersStartPromise","#startSchedulingWorkers","#shouldEnableScheduler","SchedulerWorker","#findAgentScheduleWorker","#detectExistingAgentSchedules","#detectExistingNotificationDispatch","#serverAdapter","noOpLoggerContext","noOpMetricsContext","#serverMiddleware","#bundler","#workflowEventProcessor","WorkflowEventProcessor","#wirePushWorkflowSubscription","#userEventSubscriptions","#pushSubscription","#executionWorkersStarted","#executionWorkersStartPromise","#startExecutionWorkers","#syncGatewayRegistry","__registerMastraCtor"],"sources":["../src/notifications/workflow.ts","../src/mastra/index.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport type { Schedule } from '../storage/domains/schedules/base';\nimport { createStep, createWorkflow } from '../workflows/evented';\nimport { computeNextFireAt } from '../workflows/scheduler';\nimport { dispatchDueNotifications } from './dispatcher';\n\nexport const NOTIFICATION_DISPATCH_WORKFLOW_ID = '__mastra_notification_dispatcher';\n\n/**\n * Schedule row id for the lazily-created dispatcher schedule. Deliberately\n * NOT `wf_`-prefixed: `registerDeclarativeSchedules` orphan-cleanup deletes\n * `wf_`-prefixed rows that are no longer declared in code, and this row is\n * created imperatively (like heartbeat rows) on first deferred notification.\n */\nexport const NOTIFICATION_DISPATCH_SCHEDULE_ROW_ID = '__mastra_notification_dispatch';\n\nexport const NOTIFICATION_DISPATCH_DEFAULT_CRON = '*/1 * * * *';\nexport const NOTIFICATION_DISPATCH_DEFAULT_BATCH_SIZE = 100;\n\nexport type NotificationDispatchConfig = {\n /** Defaults to true. Set false to opt out of automatic scheduled dispatch. */\n enabled?: boolean;\n cron?: string;\n batchSize?: number;\n};\n\nexport function parseNotificationDispatchNow(input?: string): Date {\n const now = input ? new Date(input) : new Date();\n if (Number.isNaN(now.getTime())) {\n throw new Error(`Invalid notification dispatch time: ${input}`);\n }\n return now;\n}\n\n/**\n * Builds the imperative schedule row that drives the notification dispatcher.\n * Created lazily by `Mastra.__ensureNotificationDispatchReady()` on the first\n * deferred notification, rather than declared on the workflow, so idle apps\n * never start the scheduler.\n */\nexport function buildNotificationDispatchSchedule({\n cron = NOTIFICATION_DISPATCH_DEFAULT_CRON,\n batchSize = NOTIFICATION_DISPATCH_DEFAULT_BATCH_SIZE,\n}: Omit<NotificationDispatchConfig, 'enabled'> = {}): Schedule {\n const now = Date.now();\n return {\n id: NOTIFICATION_DISPATCH_SCHEDULE_ROW_ID,\n target: {\n type: 'workflow',\n workflowId: NOTIFICATION_DISPATCH_WORKFLOW_ID,\n inputData: { limit: batchSize },\n },\n cron,\n status: 'active',\n nextFireAt: computeNextFireAt(cron, { after: now }),\n createdAt: now,\n updatedAt: now,\n metadata: { internal: true, feature: 'notifications' },\n };\n}\n\nexport function createNotificationDispatchWorkflow({\n batchSize = NOTIFICATION_DISPATCH_DEFAULT_BATCH_SIZE,\n}: Omit<NotificationDispatchConfig, 'enabled' | 'cron'> = {}) {\n const dispatchStep = createStep({\n id: 'dispatch-due-notifications',\n inputSchema: z.object({\n now: z.string().optional(),\n limit: z.number().optional(),\n }),\n outputSchema: z.object({\n delivered: z.number(),\n failed: z.number(),\n }),\n execute: async ({ inputData, mastra }) => {\n const storage = await mastra.getStorage()?.getStore('notifications');\n if (!storage) {\n return { delivered: 0, failed: 0 };\n }\n\n const now = parseNotificationDispatchNow(inputData.now);\n\n const result = await dispatchDueNotifications({\n mastra,\n storage,\n now,\n limit: inputData.limit ?? batchSize,\n });\n\n return { delivered: result.delivered.length, failed: result.failed.length };\n },\n });\n\n return createWorkflow({\n id: NOTIFICATION_DISPATCH_WORKFLOW_ID,\n inputSchema: z.object({\n now: z.string().optional(),\n limit: z.number().optional(),\n }),\n outputSchema: z.object({\n delivered: z.number(),\n failed: z.number(),\n }),\n })\n .then(dispatchStep)\n .commit();\n}\n","import { randomUUID } from 'node:crypto';\nimport type { Agent } from '../agent';\nimport { createDurableAgent } from '../agent/durable/create-durable-agent';\nimport { agentThreadStreamRuntime } from '../agent/thread-stream-runtime';\nimport type { DurableAgentLike } from '../agent/types';\nimport { isDurableAgentLike } from '../agent/types';\nimport type { AgentController } from '../agent-controller';\nimport { BackgroundTaskManager } from '../background-tasks';\nimport type { BackgroundTaskManagerConfig } from '../background-tasks/types';\nimport type { BundlerConfig } from '../bundler/types';\nimport { InMemoryServerCache } from '../cache';\nimport type { MastraServerCache } from '../cache';\nimport { AgentChannels } from '../channels';\nimport type { ChannelProvider } from '../channels';\nimport { DatasetsManager } from '../datasets/manager.js';\nimport type { MastraDeployer } from '../deployer';\nimport type { IMastraEditor } from '../editor';\nimport { MastraError, ErrorDomain, ErrorCategory } from '../error';\nimport type { MastraScorer } from '../evals';\nimport { EventEmitterPubSub } from '../events/event-emitter';\nimport type { PubSub } from '../events/pubsub';\nimport type { Event, EventCallback } from '../events/types';\nimport type { Harness } from '../harness';\nimport { AvailableHooks, deregisterHook, registerHook } from '../hooks';\nimport { LicenseClient } from '../license';\nimport type { MastraModelGatewayInterface } from '../llm/model/gateways';\nimport { getGatewayId } from '../llm/model/gateways';\nimport { defaultGateways } from '../llm/model/gateways/defaults';\nimport { LogLevel, noopLogger, ConsoleLogger, DualLogger } from '../logger';\nimport type { IMastraLogger } from '../logger';\nimport type { MCPServerBase } from '../mcp';\nimport type { MastraMemory } from '../memory';\nimport type { NotificationDispatchConfig } from '../notifications/workflow';\nimport {\n buildNotificationDispatchSchedule,\n createNotificationDispatchWorkflow,\n NOTIFICATION_DISPATCH_SCHEDULE_ROW_ID,\n} from '../notifications/workflow';\nimport type {\n DefinitionSource,\n ObservabilityEntrypoint,\n ObservabilityExporter,\n ObservabilityInstance,\n LoggerContext,\n MetricsContext,\n TracingContext,\n} from '../observability';\nimport { NoOpObservability, noOpLoggerContext, noOpMetricsContext } from '../observability';\nimport { initContextStorage } from '../observability/context-storage';\nimport type { Processor } from '../processors';\nimport { Schedules } from '../schedules/schedules';\nimport type { SchedulesConfig, ScheduleHooks } from '../schedules/types';\nimport type { MastraServerBase } from '../server/base';\nimport type { ApiRoute, Middleware, ServerConfig, StudioConfig } from '../server/types';\nimport type { MastraCompositeStore, WorkflowRuns } from '../storage';\nimport { InMemoryStore } from '../storage';\nimport { BackgroundTasksInMemory } from '../storage/domains/background-tasks/inmemory';\nimport { InMemoryDB } from '../storage/domains/inmemory-db';\nimport type { Schedule, ScheduleUpdate, SchedulesStorage } from '../storage/domains/schedules/base';\nimport { WorkflowsInMemory } from '../storage/domains/workflows/inmemory';\nimport { augmentWithInit } from '../storage/storageWithInit';\nimport type { StorageResolvedPromptBlockType } from '../storage/types';\nimport type { ToolLoopAgentLike } from '../tool-loop-agent';\nimport { isToolLoopAgentLike, toolLoopAgentToMastraAgent } from '../tool-loop-agent';\nimport type { ToolAction, ToolPayloadTransformPolicy } from '../tools';\nimport { normalizeToolPayloadTransformPolicy } from '../tools/payload-transform';\nimport type { MastraTTS } from '../tts';\nimport type { MastraIdGenerator, IdGeneratorContext } from '../types';\nimport { readPositiveIntEnv } from '../utils';\nimport type { MastraVector } from '../vector';\nimport { OrchestrationWorker, SchedulerWorker, BackgroundTaskWorker } from '../worker';\nimport type { MastraWorker, WorkerDeps } from '../worker';\nimport type { AnyWorkflow, Workflow } from '../workflows';\nimport { normalizeWorkflowBuilderDefinition } from '../workflows/builder';\nimport { WorkflowEventProcessor } from '../workflows/evented/workflow-event-processor';\nimport { computeNextFireAt } from '../workflows/scheduler';\nimport type { WorkflowScheduleConfig, SchedulerConfig, Scheduler } from '../workflows/scheduler';\nimport type { StoredWorkflowGraph, WorkflowRegistryIndex, WorkflowRegistrySchemas } from '../workflows/stored';\nimport {\n assertValidStoredWorkflow,\n collectNestedWorkflowIds,\n rehydrateWorkflow,\n toJsonSchemaOrUndefined,\n} from '../workflows/stored';\nimport type { AnyWorkspace, RegisteredWorkspace, Workspace } from '../workspace';\nimport { createOnScorerHook } from './hooks';\nimport { __registerMastraCtor } from './mastra-ctor-holder';\nimport type { RunScope } from './run-scope';\nimport { createRunScope } from './run-scope';\nimport type { VersionOverrides, VersionSelector } from './types';\n\n/**\n * Creates an error for when a null/undefined value is passed to an add* method.\n * This commonly occurs when config is spread ({ ...config }) and the original\n * object had getters or non-enumerable properties.\n */\nfunction createUndefinedPrimitiveError(\n type:\n | 'agent'\n | 'tool'\n | 'processor'\n | 'vector'\n | 'scorer'\n | 'workflow'\n | 'mcp-server'\n | 'gateway'\n | 'memory'\n | 'workspace',\n value: null | undefined,\n key?: string,\n): MastraError {\n const typeLabel = type === 'mcp-server' ? 'MCP server' : type;\n const errorId = `MASTRA_ADD_${type.toUpperCase().replace('-', '_')}_UNDEFINED` as Uppercase<string>;\n return new MastraError({\n id: errorId,\n domain: ErrorDomain.MASTRA,\n category: ErrorCategory.USER,\n text: `Cannot add ${typeLabel}: ${typeLabel} is ${value === null ? 'null' : 'undefined'}. This may occur if config was spread ({ ...config }) and the original object had getters or non-enumerable properties.`,\n details: { status: 400, ...(key && { key }) },\n });\n}\n\n/**\n * Stable JSON-shape comparison for two `Schedule.target` values. Uses\n * JSON.stringify because targets are plain JSON-serializable objects (the\n * storage layer round-trips them through the same encoding). Covers the\n * `inputData` / `initialState` / `requestContext` payload fields that we\n * want to detect changes on across redeploys.\n */\nfunction targetsEqual(a: Schedule['target'] | undefined, b: Schedule['target']): boolean {\n if (a === b) return true;\n if (!a) return false;\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\n/**\n * Reads the declarative schedule configs off a workflow. Supports both the\n * new `getScheduleConfigs(): WorkflowScheduleConfig[]` accessor on the evented\n * engine and a legacy `getScheduleConfig(): WorkflowScheduleConfig | undefined`\n * fallback used in tests that inject a fake getter.\n */\nfunction collectWorkflowScheduleConfigs(workflow: unknown): WorkflowScheduleConfig[] {\n const w = workflow as {\n getScheduleConfigs?: () => WorkflowScheduleConfig[] | undefined;\n getScheduleConfig?: () => WorkflowScheduleConfig | WorkflowScheduleConfig[] | undefined;\n };\n if (typeof w.getScheduleConfigs === 'function') {\n return w.getScheduleConfigs() ?? [];\n }\n if (typeof w.getScheduleConfig === 'function') {\n const cfg = w.getScheduleConfig();\n if (!cfg) return [];\n return Array.isArray(cfg) ? cfg : [cfg];\n }\n return [];\n}\n\n/**\n * Builds the storage row id for a declarative schedule. Workflow and schedule\n * ids are URL-encoded so delimiters in user-supplied ids cannot collide\n * across workflows (e.g. `foo__bar` single vs `foo` array-entry `bar`).\n */\nfunction declarativeScheduleRowId(workflowId: string, scheduleId?: string): string {\n const encodedWorkflow = encodeURIComponent(workflowId);\n if (scheduleId === undefined) return `wf_${encodedWorkflow}`;\n return `wf_${encodedWorkflow}__${encodeURIComponent(scheduleId)}`;\n}\n\n/**\n * Determines whether a stored schedule row id belongs to one of the registered\n * workflows. Returns the owning workflow id when the row id either equals\n * `wf_<encoded(workflowId)>` (single-schedule form) or starts with\n * `wf_<encoded(workflowId)>__` (array form). Returns undefined when no\n * registered workflow owns the row.\n */\nfunction ownerWorkflowIdForRow(rowId: string, byWorkflow: Map<string, Set<string>>): string | undefined {\n for (const workflowId of byWorkflow.keys()) {\n const prefix = `wf_${encodeURIComponent(workflowId)}`;\n if (rowId === prefix || rowId.startsWith(`${prefix}__`)) {\n return workflowId;\n }\n }\n return undefined;\n}\n\n/**\n * Decodes the owning workflow id directly from a `wf_<encoded>` /\n * `wf_<encoded>__<...>` row id without needing the workflow to be in the\n * current registry. Used to identify rows whose workflow has been deleted\n * from code so we can clean them up on startup.\n */\nfunction ownerWorkflowIdFromRowId(rowId: string): string | undefined {\n if (!rowId.startsWith('wf_')) return undefined;\n const rest = rowId.slice('wf_'.length);\n const sep = rest.indexOf('__');\n const encoded = sep === -1 ? rest : rest.slice(0, sep);\n if (!encoded) return undefined;\n try {\n return decodeURIComponent(encoded);\n } catch {\n return undefined;\n }\n}\n\n/** See {@link targetsEqual}. Same approach for free-form metadata. */\nfunction metadataEqual(a: Record<string, unknown> | null | undefined, b: Record<string, unknown> | undefined): boolean {\n const aNorm = a ?? undefined;\n const bNorm = b ?? undefined;\n if (aNorm === bNorm) return true;\n if (!aNorm || !bNorm) return false;\n return JSON.stringify(aNorm) === JSON.stringify(bNorm);\n}\n\n/**\n * Configuration interface for initializing a Mastra instance.\n *\n * The Config interface defines all the optional components that can be registered\n * with a Mastra instance, including agents, workflows, storage, logging, and more.\n *\n * @template TAgents - Record of agent instances keyed by their names\n * @template TWorkflows - Record of workflow instances\n * @template TVectors - Record of vector store instances\n * @template TTTS - Record of text-to-speech instances\n * @template TLogger - Logger implementation type\n * @template TVNextNetworks - Record of agent network instances\n * @template TMCPServers - Record of MCP server instances\n * @template TScorers - Record of scorer instances\n *\n * @example\n * ```typescript\n * const mastra = new Mastra({\n * agents: {\n * weatherAgent: new Agent({\n * id: 'weather-agent',\n * name: 'Weather Agent',\n * instructions: 'You help with weather information',\n * model: 'openai/gpt-5'\n * })\n * },\n * storage: new LibSQLStore({ id: 'mastra-storage', url: ':memory:' }),\n * logger: new PinoLogger({ name: 'MyApp' })\n * });\n * ```\n */\nexport interface Config<\n TAgents extends Record<string, Agent<any>> = Record<string, Agent<any>>,\n TWorkflows extends Record<string, AnyWorkflow> = Record<string, AnyWorkflow>,\n TVectors extends Record<string, MastraVector<any>> = Record<string, MastraVector<any>>,\n TTTS extends Record<string, MastraTTS> = Record<string, MastraTTS>,\n TLogger extends IMastraLogger = IMastraLogger,\n TMCPServers extends Record<string, MCPServerBase<any>> = Record<string, MCPServerBase<any>>,\n TScorers extends Record<string, MastraScorer<any, any, any, any>> = Record<string, MastraScorer<any, any, any, any>>,\n TTools extends Record<string, ToolAction<any, any, any, any, any, any>> = Record<\n string,\n ToolAction<any, any, any, any, any, any>\n >,\n TProcessors extends Record<string, Processor<any>> = Record<string, Processor<any>>,\n TMemory extends Record<string, MastraMemory> = Record<string, MastraMemory>,\n TChannels extends Record<string, ChannelProvider> = Record<string, ChannelProvider>,\n> {\n /**\n * Agents are autonomous systems that can make decisions and take actions.\n * Accepts Mastra Agent instances, AI SDK v6 ToolLoopAgent instances,\n * and durable agent wrappers (e.g., InngestAgent from createInngestAgent).\n * ToolLoopAgent and durable agents are automatically handled during registration.\n */\n agents?: { [K in keyof TAgents]: TAgents[K] | ToolLoopAgentLike | DurableAgentLike };\n\n /**\n * Storage provider for persisting data, conversation history, and workflow state.\n * Required for agent memory and workflow persistence.\n */\n storage?: MastraCompositeStore;\n\n /**\n * Vector stores for semantic search and retrieval-augmented generation (RAG).\n * Used for storing and querying embeddings.\n */\n vectors?: TVectors;\n\n /**\n * Logger implementation for application logging and debugging.\n * Set to `false` to disable logging entirely.\n * @default `INFO` level in development, `WARN` in production.\n */\n logger?: TLogger | false;\n\n /**\n * Workflows provide type-safe, composable task execution with built-in error handling.\n */\n workflows?: TWorkflows;\n\n /**\n * AgentControllers to host on this Mastra instance, keyed by id. Each\n * registered AgentController uses this Mastra (its storage, agents, gateways,\n * and observability) instead of building its own internal one, and is\n * reachable via {@link Mastra.getAgentController} /\n * {@link Mastra.listAgentControllers}. This is how a server exposes multiple\n * AgentControllers' sessions over HTTP.\n */\n agentControllers?: Record<string, AgentController<any>>;\n\n /**\n * Harnesses to host on this Mastra instance, keyed by id.\n *\n * @deprecated Use {@link MastraConfig.agentControllers} instead. `harnesses`\n * is retained as a backwards-compatible alias and will be removed in a future\n * major. Entries from both keys are merged, with `agentControllers` taking\n * precedence on key collisions.\n */\n harnesses?: Record<string, Harness<any>>;\n\n /**\n * Text-to-speech providers for voice synthesis capabilities.\n */\n tts?: TTTS;\n\n /**\n * Observability entrypoint for tracking model interactions and tracing.\n * Pass an instance of the Observability class from @mastra/observability.\n *\n * @example\n * ```typescript\n * import { Observability, MastraStorageExporter, MastraPlatformExporter } from '@mastra/observability';\n *\n * new Mastra({\n * observability: new Observability({\n * configs: {\n * default: {\n * serviceName: 'mastra',\n * exporters: [new MastraStorageExporter(), new MastraPlatformExporter()],\n * },\n * },\n * })\n * })\n * ```\n *\n * `Observability` auto-applies a `SensitiveDataFilter` span output processor\n * to every configured instance. Set `sensitiveDataFilter: false` on the\n * registry config to opt out, or pass a `SensitiveDataFilterOptions` object\n * to customize it.\n */\n observability?: ObservabilityEntrypoint;\n\n /**\n * Custom ID generator function for creating unique identifiers.\n * Receives optional context about what type of ID is being generated\n * and where it's being requested from.\n * @default `crypto.randomUUID()`\n */\n idGenerator?: MastraIdGenerator;\n\n /**\n * Deployment provider for publishing applications to cloud platforms.\n */\n deployer?: MastraDeployer;\n\n /**\n * Server configuration for HTTP endpoints and middleware.\n */\n server?: ServerConfig;\n\n /**\n * Studio-specific authentication and authorization configuration.\n *\n * When configured, Studio uses separate auth from the server (API) auth,\n * allowing different providers for internal team members vs external customers.\n *\n * - `server.auth` handles API authentication (external customers)\n * - `studio.auth` handles Studio authentication (internal team)\n *\n * **Dual auth is opt-in:** If `studio.auth` is not configured, Studio requests\n * fall back to `server.auth` for backward compatibility. To enable strict\n * separation between Studio and API auth, configure both `studio.auth` and\n * `server.auth`.\n *\n * @example\n * ```typescript\n * const mastra = new Mastra({\n * server: {\n * auth: new MastraAuthWorkos({ ... }), // External customers\n * },\n * studio: {\n * auth: new MastraAuthOkta({ ... }), // Internal team\n * rbac: new StaticRBACProvider({\n * roles: DEFAULT_ROLES,\n * getUserRoles: (user) => [user.role],\n * }),\n * },\n * });\n * ```\n */\n studio?: StudioConfig;\n\n /**\n * MCP servers provide tools and resources that agents can use.\n */\n mcpServers?: TMCPServers;\n\n /**\n * Bundler configuration for packaging and deployment.\n */\n bundler?: BundlerConfig;\n\n /**\n * Pub/sub system for event-driven communication between components.\n * @default EventEmitterPubSub\n */\n pubsub?: PubSub;\n\n /**\n * Server cache for storing stream events and other temporary data.\n * Used by durable agents for resumable streams - clients can disconnect\n * and reconnect without missing events.\n *\n * When provided, durable agents created without their own cache will\n * inherit this cache instance.\n *\n * @default InMemoryServerCache\n */\n cache?: MastraServerCache;\n\n /**\n * Scorers help assess the quality of agent responses and workflow outputs.\n */\n scorers?: TScorers;\n\n /**\n * Tools are reusable functions that agents can use to interact with external systems.\n */\n tools?: TTools;\n\n /**\n * Processors transform inputs and outputs for agents and workflows.\n */\n processors?: TProcessors;\n\n /**\n * Memory instances that can be referenced by stored agents.\n * Keys are used to look up memory instances when resolving stored agent configurations.\n */\n memory?: TMemory;\n\n /**\n * Global workspace for file storage, skills, and code execution.\n * Agents inherit this workspace unless they have their own configured.\n * Skills are accessed via workspace.skills when skills is configured.\n */\n workspace?: AnyWorkspace;\n\n /**\n * Custom model router gateways for accessing LLM providers.\n * Gateways handle provider-specific authentication, URL construction, and model resolution.\n */\n gateways?: Record<string, MastraModelGatewayInterface>;\n\n /**\n * Event handlers for custom application events.\n * Maps event topics to handler functions for event-driven architectures.\n */\n events?: {\n [topic: string]: (\n event: Event,\n cb?: () => Promise<void>,\n ) => Promise<void> | ((event: Event, cb?: () => Promise<void>) => Promise<void>)[];\n };\n\n /**\n * Editor instance for handling agent instantiation and configuration.\n * The editor handles complex instantiation logic including memory resolution.\n */\n editor?: IMastraEditor;\n\n /**\n * Global version overrides for primitives.\n * When set, sub-agent delegation (and future primitive resolution) will\n * resolve the specified version instead of the code-defined default.\n *\n * @example\n * ```typescript\n * new Mastra({\n * versions: {\n * agents: {\n * 'researcher-agent': { versionId: '123' },\n * 'writer-agent': { status: 'published' },\n * },\n * },\n * });\n * ```\n */\n versions?: VersionOverrides;\n\n /**\n * Background task configuration for running tool calls asynchronously.\n * When configured, agents can dispatch tool executions to run in the background\n * while the conversation continues.\n */\n backgroundTasks?: BackgroundTaskManagerConfig;\n\n /**\n * Scheduler configuration for cron-driven workflow triggers.\n *\n * The scheduler is auto-enabled when any registered workflow declares a\n * `schedule` config or when `scheduler.enabled` is true. It requires a\n * storage adapter implementing the `schedules` domain (e.g. `@mastra/libsql`).\n */\n scheduler?: SchedulerConfig;\n\n /**\n * Notification runtime configuration. Notification dispatch is scheduled automatically by default.\n */\n notifications?: {\n dispatch?: NotificationDispatchConfig;\n };\n\n /**\n * Schedules runtime configuration. A single lifecycle-hook bundle runs for\n * every agent-schedule fire and is invoked by the agent-schedule worker\n * around schedule-driven agent runs; hooks branch per agent via the\n * `agentId` on each context. Configuring hooks here (rather than on the\n * Agent) lets both code-defined and stored agents share the same hook\n * surface, since stored agents cannot define functions in their serialized\n * config.\n */\n schedules?: SchedulesConfig<Mastra>;\n\n /**\n * Platform channels for messaging integrations (Slack, Discord, etc.).\n * Routes are automatically registered and agents can reference channel configs.\n *\n * @example\n * ```typescript\n * import { SlackProvider } from '@mastra/slack';\n *\n * new Mastra({\n * channels: {\n * slack: new SlackProvider({\n * configToken: process.env.SLACK_APP_CONFIG_TOKEN,\n * refreshToken: process.env.SLACK_APP_CONFIG_REFRESH_TOKEN,\n * }),\n * },\n * });\n * ```\n */\n channels?: TChannels;\n\n /**\n * Deployment environment name (e.g. `'production'`, `'staging'`, `'development'`).\n * When set, the value is automatically attached to all observability signals\n * so they can be filtered by environment without passing\n * `tracingOptions.metadata.environment` on every call.\n *\n * If unset, falls back to `process.env.NODE_ENV`. If neither is set the field\n * is left undefined rather than guessed.\n *\n * Per-call `tracingOptions.metadata.environment` always takes precedence.\n *\n * @example\n * ```typescript\n * new Mastra({\n * environment: 'production',\n * observability: new Observability({ ... }),\n * })\n * ```\n */\n environment?: string;\n /**\n * Optional central transform policy for tool payloads before they are\n * serialized into display streams or user-visible transcripts.\n */\n transform?: ToolPayloadTransformPolicy;\n /**\n * Configure which workers run in this Mastra instance.\n *\n * - `undefined` (default): Auto-creates default workers (existing behavior)\n * - `false`: Disables all event processing — useful when running standalone workers separately\n * - `MastraWorker[]`: Additional workers merged with the auto-created\n * defaults. A custom worker replaces a default with the same `name`;\n * duplicate names within the array throw. Use `false` to run no workers.\n */\n workers?: MastraWorker[] | false;\n\n /**\n * Boot-time recovery behavior for orphaned agent/workflow runs.\n *\n * `durableAgents` controls whether the deployer will automatically call\n * {@link Mastra.recoverAllDurableAgents} for every registered `DurableAgent`\n * when the server starts (right after `restartAllActiveWorkflowRuns`).\n *\n * - `'off'` (default): the deployer never auto-recovers durable agent runs.\n * Operators can still call `mastra.recoverAllDurableAgents()` or\n * `agent.recoverActiveRuns()` by hand.\n * - `'auto'`: the deployer will invoke `recoverAllDurableAgents()` on boot,\n * re-driving every RUNNING durable agent run discovered in storage.\n *\n * Opt-in only. Auto-recovery re-runs the agentic loop from the last persisted\n * snapshot, so it re-issues LLM calls (real cost) and re-executes tool calls\n * (must be idempotent). In multi-instance deploys every replica will race to\n * recover the same runs, since there is no lease/lock yet.\n *\n * @default { durableAgents: 'off' }\n */\n recovery?: MastraRecoveryConfig;\n\n /**\n * Marks this instance as an internally-owned ephemeral Mastra — e.g. the\n * fallback instance a standalone `Agent` lazily creates so its\n * prepare-stream workflow has a pubsub-equipped Mastra to run on.\n *\n * Ephemeral instances skip module-level scorer-hook registration: they have\n * no agent/scorer/editor registries for the hook to resolve against, so the\n * hook could never persist a score — but the module-level emitter would\n * retain the instance (and everything it references) for the lifetime of\n * the process, leaking one Mastra graph per discarded standalone Agent\n * (#19404).\n *\n * @internal Not part of the public API — do not set this on application\n * Mastra instances; it silently disables scorer persistence.\n */\n __ephemeral?: boolean;\n}\n\n/**\n * Boot-time recovery configuration. See {@link Mastra['recoveryConfig']}.\n */\nexport interface MastraRecoveryConfig {\n /**\n * Auto-recover orphaned RUNNING durable agent runs on server boot.\n * @default 'off'\n */\n durableAgents?: 'auto' | 'off';\n}\n\n/**\n * The central orchestrator for Mastra applications, managing agents, workflows, storage, logging, observability, and more.\n *\n * The `Mastra` class serves as the main entry point and registry for all components in a Mastra application.\n * It coordinates the interaction between agents, workflows, storage systems, and other services.\n\n * @template TAgents - Record of agent instances keyed by their names\n * @template TWorkflows - Record of modern workflow instances\n * @template TVectors - Record of vector store instances for semantic search and RAG\n * @template TTTS - Record of text-to-speech provider instances\n * @template TLogger - Logger implementation type for application logging\n * @template TVNextNetworks - Record of next-generation agent network instances\n * @template TMCPServers - Record of Model Context Protocol server instances\n * @template TScorers - Record of evaluation scorer instances for measuring AI performance\n *\n * @example\n * ```typescript\n * const mastra = new Mastra({\n * agents: {\n * weatherAgent: new Agent({\n * id: 'weather-agent',\n * name: 'Weather Agent',\n * instructions: 'You provide weather information',\n * model: 'openai/gpt-5',\n * tools: [getWeatherTool]\n * })\n * },\n * workflows: { dataWorkflow },\n * storage: new LibSQLStore({ id: 'mastra-storage', url: ':memory:' }),\n * logger: new PinoLogger({ name: 'MyApp' })\n * });\n * ```\n */\nexport class Mastra<\n TAgents extends Record<string, Agent<any>> = Record<string, Agent<any>>,\n TWorkflows extends Record<string, AnyWorkflow> = Record<string, AnyWorkflow>,\n TVectors extends Record<string, MastraVector<any>> = Record<string, MastraVector<any>>,\n TTTS extends Record<string, MastraTTS> = Record<string, MastraTTS>,\n TLogger extends IMastraLogger = IMastraLogger,\n TMCPServers extends Record<string, MCPServerBase<any>> = Record<string, MCPServerBase<any>>,\n TScorers extends Record<string, MastraScorer<any, any, any, any>> = Record<string, MastraScorer<any, any, any, any>>,\n TTools extends Record<string, ToolAction<any, any, any, any, any, any>> = Record<\n string,\n ToolAction<any, any, any, any, any, any>\n >,\n TProcessors extends Record<string, Processor<any>> = Record<string, Processor<any>>,\n TMemory extends Record<string, MastraMemory> = Record<string, MastraMemory>,\n TChannels extends Record<string, ChannelProvider> = Record<string, ChannelProvider>,\n> {\n #vectors?: TVectors;\n #agents: TAgents;\n #logger: TLogger;\n #loggerExplicit = false;\n #workflows: TWorkflows;\n #harnesses: Record<string, Harness<any>> = {};\n #hiddenWorkflowKeys = new Set<string>();\n #observability: ObservabilityEntrypoint;\n #observabilityExplicit = false;\n #onScorerHook?: ReturnType<typeof createOnScorerHook>;\n #tts?: TTTS;\n #deployer?: MastraDeployer;\n #serverMiddleware: Array<{\n handler: (c: any, next: () => Promise<void>) => Promise<Response | void>;\n path: string;\n }> = [];\n\n #storage?: MastraCompositeStore;\n #storageExplicit = false;\n #storageFallbackWarningPending = false;\n #recoveryConfig: MastraRecoveryConfig = { durableAgents: 'off' };\n #scorers?: TScorers;\n #tools?: TTools;\n #processors?: TProcessors;\n #processorConfigurations: Map<string, Array<{ processor: Processor; agentId: string; type: 'input' | 'output' }>> =\n new Map();\n #memory?: TMemory;\n #workspace?: Workspace;\n #workspaces: Record<string, RegisteredWorkspace> = {};\n #server?: ServerConfig;\n #serverExplicit = false;\n #studio?: StudioConfig;\n #studioExplicit = false;\n #serverAdapter?: MastraServerBase;\n #mcpServers?: TMCPServers;\n #bundler?: BundlerConfig;\n #idGenerator?: MastraIdGenerator;\n #pubsub: PubSub;\n #backgroundTaskConfig?: BackgroundTaskManagerConfig;\n #backgroundTaskManager?: BackgroundTaskManager;\n #schedulerConfig?: SchedulerConfig;\n #notificationDispatchConfig?: NotificationDispatchConfig;\n /**\n * Tracks whether any registered workflow has declared a `schedule` config.\n * Used as a fast short-circuit so users without scheduled workflows pay\n * zero cost beyond a boolean check.\n */\n #hasScheduledWorkflow = false;\n #gateways?: Record<string, MastraModelGatewayInterface>;\n #channels?: TChannels;\n #schedules?: Schedules;\n #schedulesConfig?: SchedulesConfig<Mastra>;\n #environment?: string;\n #toolPayloadTransform?: ToolPayloadTransformPolicy;\n #workers: MastraWorker[] = [];\n #workerFilter?: Set<string>;\n /**\n * Set when the user (or `MASTRA_WORKERS=false`) explicitly disabled all event\n * processing in this instance via `workers: false`. Gates lazy scheduler /\n * agent-schedule worker injection so runtime triggers (e.g.\n * `schedules.create()`) don't resurrect workers the user opted out of.\n */\n #workersDisabled = false;\n /**\n * Tracks whether `startWorkers()` has already run. Used to decide whether\n * lazy scheduler injection (e.g. from `mastra.schedules.create()` after boot)\n * needs to also `init`/`start` the worker, or whether the normal\n * `startWorkers()` path will pick it up.\n */\n #workersStarted = false;\n /**\n * Set when something has signalled that the scheduler is needed at runtime\n * (e.g. an agent schedule was registered via `__ensureScheduleRuntimeReady()`).\n * Causes `#shouldEnableScheduler()` to return `true` even when there are no\n * declarative scheduled workflows, unless the user explicitly set\n * `scheduler: { enabled: false }`.\n */\n #schedulerRequested = false;\n /**\n * Set once `__ensureNotificationDispatchReady()` has upserted the dispatcher\n * schedule row and requested the scheduler. Makes repeated deferred\n * notification creates free after the first one.\n */\n #notificationDispatchReady = false;\n /**\n * In-flight promise for `#ensureSchedulingWorkersStarted()`. Serializes\n * concurrent startup requests so two callers can't both pass the\n * worker-existence checks and double-subscribe to the scheduling topics.\n */\n #schedulingWorkersStartPromise?: Promise<void>;\n /**\n * In-flight promise for `__ensureExecutionWorkersStarted()`. Serializes\n * concurrent lazy startups triggered by background-task dispatches so two\n * first dispatches on a cold instance can't both init/start the same\n * workers.\n */\n #executionWorkersStartPromise?: Promise<void>;\n /**\n * Fast path for `__ensureExecutionWorkersStarted()`. Set once the execution\n * workers + push wiring are confirmed running; reset by `stopWorkers()`.\n * Kept separate from `#workersStarted`, which partial `startWorkers(name)`\n * calls also set without starting the workflow consumer.\n */\n #executionWorkersStarted = false;\n // Lazily-constructed processor used by handleWorkflowEvent(). Shared between\n // pull-mode workers (OrchestrationWorker) and push-mode entry points\n // (in-process EventEmitter listener, the /api/workers/events HTTP route).\n #workflowEventProcessor?: WorkflowEventProcessor;\n // Callback registered against the pubsub when running in push mode so we can\n // unsubscribe it cleanly during stopWorkers().\n #pushSubscription?: { topic: string; cb: EventCallback };\n // Tracks (topic, listener) pairs registered against the pubsub on behalf of\n // user-defined event listeners during startWorkers(). Used to make\n // startWorkers()/stopWorkers() idempotent — a second startWorkers() call\n // must not double-subscribe the same listener.\n #userEventSubscriptions: Array<{\n topic: string;\n cb: (event: Event, ack?: () => Promise<void>) => Promise<void>;\n }> = [];\n\n #events: {\n [topic: string]: ((event: Event, cb?: () => Promise<void>) => Promise<void>)[];\n } = {};\n #internalMastraWorkflows: Record<string, AnyWorkflow> = {};\n // Tracks registration timestamps for run-scoped internal workflows so a lazy\n // TTL sweep can evict entries from abandoned suspended runs that were never\n // resumed. Unscoped (singleton) entries are not tracked — they live forever.\n #runScopedWorkflowTimestamps: Map<string, { registeredAt: number; runId: string }> = new Map();\n // Per-run bag of non-serializable runtime state (SaveQueueManager,\n // BackgroundTaskManager, MessageList, abort controllers, dynamic tool sets…)\n // shared across step factories within a single run. Never persisted, never\n // published. Lifecycle is refcounted against `__registerInternalWorkflow`\n // calls for the same runId so multiple workflows sharing a run (e.g. an\n // agentic-loop wrapping an agentic-execution) keep the scope alive until the\n // last unregisters. See `./run-scope.ts`.\n #runScopes: Map<string, RunScope> = new Map();\n #runScopeRefcounts: Map<string, number> = new Map();\n // Run-scoped internal workflows older than this TTL (ms) are evicted during the\n // lazy sweep that runs on each new registration. Reads the shared\n // `MASTRA_SUSPENDED_RUN_TTL_MS` so this registry and the agent thread-stream\n // runtime expire a suspended run's state on one bound; production keeps the 30\n // minute default.\n static readonly INTERNAL_WORKFLOW_TTL_MS = readPositiveIntEnv('MASTRA_SUSPENDED_RUN_TTL_MS', 30 * 60 * 1000);\n // Per-run tracing context for evented workflow runs. `currentSpan` is a\n // non-serializable AISpan, so it cannot ride the engine's pubsub events —\n // the event processor reads it from here, keyed by runId, instead.\n #runTracingContexts: Map<string, TracingContext> = new Map();\n // Server cache for temporary persistence and durable agent resumable streams\n #serverCache: MastraServerCache;\n // Cache for stored agents to allow in-memory modifications (like model changes) to persist across requests\n #storedAgentsCache: Map<string, Agent> = new Map();\n // Cache for stored scorers to allow in-memory modifications to persist across requests\n #storedScorersCache: Map<string, MastraScorer<any, any, any, any>> = new Map();\n // Registry for prompt blocks (stored or code-defined)\n #promptBlocks: Record<string, StorageResolvedPromptBlockType> = {};\n // Editor instance for handling agent instantiation and configuration\n #editor?: IMastraEditor;\n #datasets?: DatasetsManager;\n // Global version overrides for primitives (agents, etc.)\n #versions?: VersionOverrides;\n // Cached pubsub proxy that tags internal-workflow events with `_localOnly`\n // so the broker skips relaying multi-MB payloads to non-owning instances.\n #pubsubProxy?: PubSub;\n\n get pubsub(): PubSub {\n if (!this.#pubsubProxy) {\n const raw = this.#pubsub;\n const self = this;\n this.#pubsubProxy = new Proxy(raw, {\n get(target, prop, _receiver) {\n if (prop === 'publish') {\n return function publish(topic: string, event: Omit<Event, 'id' | 'createdAt'>) {\n // Internal execution-workflows / agentic-loops are run-scoped:\n // only the owning instance needs their events. Pass `localOnly`\n // so the broker delivers locally + echoes back to the sender,\n // but does NOT fan out to other clients (avoids serialising\n // cumulative stepResults blobs — often 9 MB+ — across the unix\n // socket). The flag rides on the publish-frame envelope, not on\n // event.data, so WEP consumers never see it.\n if (topic === 'workflows' || topic === 'workflows-finish') {\n const data = event.data as Record<string, unknown> | undefined;\n const wfId = data?.workflowId as string | undefined;\n const rId = data?.runId as string | undefined;\n // Walk parentWorkflow chain to root — nested internal workflows\n // (e.g. `executionWorkflow` inside `agentic-loop`) carry an\n // immediate workflowId that isn't itself in the internal registry,\n // but their root parent (the registered agentic-loop) is. If any\n // ancestor matches an internal registration, this instance owns\n // the run and the event should stay local. Also tag publishes\n // for workflow ids only known to this instance's public registry\n // (e.g. background scheduler runs like the notification\n // dispatcher) — they have no cross-instance consumer.\n const isOwnedHere = (() => {\n if (wfId && rId && self.__hasInternalWorkflow(wfId, rId)) return true;\n let parent = data?.parentWorkflow as\n | { workflowId?: string; runId?: string; parentWorkflow?: unknown }\n | undefined;\n let depth = 0;\n while (parent && depth < 16) {\n const pwfId = parent.workflowId;\n const prId = parent.runId;\n if (pwfId && prId && self.__hasInternalWorkflow(pwfId, prId)) return true;\n parent = parent.parentWorkflow as typeof parent;\n depth++;\n }\n // Scheduler-spawned background workflows: runId carries the\n // schedule row id prefix — `sched_wf_<workflowId>_<timestamp>`\n // for declarative schedules, or the imperative notification\n // dispatcher row id. These ticks fire on every instance\n // independently — events are only meaningful to the\n // publishing process.\n if (rId && rId.startsWith('sched_wf_')) return true;\n if (rId && rId.startsWith(`sched_${NOTIFICATION_DISPATCH_SCHEDULE_ROW_ID}_`)) return true;\n return false;\n })();\n if (isOwnedHere) {\n return target.publish(topic, event, { localOnly: true });\n }\n } else if (topic.startsWith('workflow.events.v2.')) {\n // Per-run watch stream events. Only the publishing process\n // consumes these (execution-engine subscribes per-run). No\n // cross-instance fan-out needed.\n return target.publish(topic, event, { localOnly: true });\n }\n return target.publish(topic, event);\n };\n }\n // Bind methods to `target` so private field access (#subscribers etc.)\n // works correctly — JS Proxies set `this` to the proxy, which breaks\n // private fields since they are scoped to the declaring class instance.\n const val = Reflect.get(target, prop, target);\n if (typeof val === 'function') {\n return val.bind(target);\n }\n return val;\n },\n }) as PubSub;\n }\n return this.#pubsubProxy;\n }\n\n get agentThreadStreamRuntime() {\n return agentThreadStreamRuntime;\n }\n\n get workers(): readonly MastraWorker[] {\n return this.#workers;\n }\n\n getWorker<T extends MastraWorker>(name: string): T | undefined {\n return this.#workers.find(w => w.name === name) as T | undefined;\n }\n\n get backgroundTaskManager() {\n return this.#backgroundTaskManager;\n }\n\n /**\n * Returns the workflow scheduler owned by the SchedulerWorker,\n * or undefined if the scheduler is not enabled / not yet started.\n *\n * The scheduler is created when `startWorkers()` initializes the\n * SchedulerWorker (guarded by `#shouldEnableScheduler()`).\n *\n * This is runtime plumbing (the cron tick loop). To create, list, pause,\n * resume, or delete schedules use `mastra.schedules` instead.\n *\n * @internal\n */\n get scheduler(): Scheduler | undefined {\n return this.#findSchedulerWorker()?.scheduler;\n }\n\n get datasets(): DatasetsManager {\n if (!this.#datasets) {\n this.#datasets = new DatasetsManager(this);\n }\n return this.#datasets;\n }\n\n /**\n * Gets the currently configured ID generator function.\n *\n * @example\n * ```typescript\n * const mastra = new Mastra({\n * idGenerator: context =>\n * context?.idType === 'message' && context.threadId\n * ? `msg-${context.threadId}-${Date.now()}`\n * : `custom-${Date.now()}`\n * });\n * const generator = mastra.getIdGenerator();\n * console.log(generator?.({ idType: 'message', threadId: 'thread-123' })); // \\\"msg-thread-123-1234567890\\\"\n * ```\n */\n public getIdGenerator() {\n return this.#idGenerator;\n }\n\n /**\n * Gets the currently configured editor instance.\n * The editor is responsible for handling agent instantiation and configuration.\n *\n * @example\n * ```typescript\n * const mastra = new Mastra({\n * editor: new MastraEditor({ logger })\n * });\n * const editor = mastra.getEditor();\n * ```\n */\n public getEditor() {\n return this.#editor;\n }\n\n /**\n * Gets a registered channel provider by its key.\n *\n * @example\n * ```typescript\n * import { SlackProvider } from '@mastra/slack';\n * const slack = mastra.getChannelProvider<SlackProvider>('slack');\n * ```\n */\n public getChannelProvider<T extends ChannelProvider = ChannelProvider>(key: string): T | undefined {\n return this.#channels?.[key] as T | undefined;\n }\n\n /**\n * Gets all registered channel providers.\n */\n public getChannelProviders(): Record<string, ChannelProvider> | undefined {\n return this.#channels;\n }\n\n /**\n * Shorthand getter for platform channels.\n * Usage: `mastra.channels.slack.connect(agentId)`\n */\n public get channels(): TChannels {\n return (this.#channels ?? {}) as TChannels;\n }\n\n /**\n * Canonical entrypoint for schedules — recurring agent or workflow runs\n * persisted as schedule rows discriminated by `target.type` (`'agent'` or\n * `'workflow'`). Use to create, list, update, pause/resume, manually fire,\n * or inspect trigger history for schedules across any agent or workflow.\n *\n * Lazily constructed. Operates against `getStorage()?.getStore('schedules')`.\n *\n * @example\n * ```ts\n * const schedule = await mastra.schedules.create({\n * agentId: 'pinger',\n * name: 'morning-checkin',\n * cron: '0 9 * * *',\n * prompt: 'good morning, anything to report?',\n * threadId: 't1',\n * resourceId: 'u1',\n * });\n * await mastra.schedules.list({ agentId: 'pinger' });\n * ```\n */\n public get schedules(): Schedul