UNPKG

@mastra/core

Version:
1,213 lines (1,212 loc) • 48.1 kB
const require_base = require("./base-B6soWsYg.cjs"); const require_error = require("./error-B-e62x-A.cjs"); const require_observability = require("./observability-BzV5axz0.cjs"); const require_utils = require("./utils-CNiGU0Uf.cjs"); require("./tracing-BUrUJwCM.cjs"); const require_request_context = require("./request-context-ByoZMp-j.cjs"); const require_zod_utils = require("./zod-utils-BAGXGqPm.cjs"); const require_tool = require("./tool-d85xHVkl.cjs"); const require_ee_DXvSoTl7 = require("./ee-DXvSoTl7-C7miIhtq.cjs"); const require_background_tasks = require("./background-tasks-lifNqs9M.cjs"); const require_toolchecks = require("./toolchecks-Rfz17G5p.cjs"); const require_types = require("./types-CepZ83u8.cjs"); const require_utils_safe_stringify = require("./utils/safe-stringify.cjs"); let crypto = require("crypto"); let zod_v4 = require("zod/v4"); let _mastra_schema_compat_json_to_zod = require("@mastra/schema-compat/json-to-zod"); let _mastra_schema_compat = require("@mastra/schema-compat"); let _mastra_schema_compat_schema = require("@mastra/schema-compat/schema"); //#region src/tools/tool-builder/builder.ts /** * Merge two RequestContexts so non-serializable values survive the evented * workflow engine's toJSON/reconstruct cycle. * * The evented engine serialises the RequestContext via `toJSON()` when * publishing workflow events. Values that fail `JSON.stringify` (functions, * objects with circular references — e.g. the `controller` context) are silently * dropped. The reconstructed RC handed to steps is therefore *degraded*. * * Tools, however, also hold a reference to the *original* RC captured during * tool conversion (the "closure" RC). By merging both — exec first, then * closure on top — keys that survived serialisation are preserved while * non-serializable keys from the closure (like `controller`) are restored. */ /** * Detect RequestContext-like objects structurally. We cannot use `instanceof` * here because duplicate copies of @mastra/core may be loaded in the same * process (bundlers, monorepos) and the prototype identity is not guaranteed. */ function isRequestContextLike(value) { if (!value || typeof value !== "object") return false; const rc = value; return typeof rc.get === "function" && typeof rc.set === "function" && typeof rc.entries === "function" && typeof rc.size === "function"; } function mergeRequestContexts(closureRC, execRC) { if (closureRC && closureRC === execRC) return closureRC; if (!closureRC && !execRC) return new require_request_context.RequestContext(); if (!closureRC) return isRequestContextLike(execRC) ? execRC : new require_request_context.RequestContext(); if (!execRC || !isRequestContextLike(execRC) || execRC.size() === 0) return closureRC; const merged = new require_request_context.RequestContext(); for (const [key, value] of execRC.entries()) merged.set(key, value); for (const [key, value] of closureRC.entries()) merged.set(key, value); return merged; } /** * Detect Zod v4 schemas. Zod v3 stores the type name as `_def.typeName` * (e.g. "ZodObject"); Zod v4 stores it as `_def.type` (e.g. "object"). We * cannot use `instanceof` here because both Zod versions may be loaded in the * same process and the prototype identity is not guaranteed. */ function isZodV4Schema(schema) { const def = schema?._def; return !!def && typeof def.type === "string" && !def.typeName; } /** * Build a Standard Schema that: * - exposes the spliced JSON Schema (with `_background`/`suspendedToolRunId`/ * `resumeData` properties added) so provider compat layers see the override * fields when serializing the tool to an LLM, and * - delegates runtime `validate` to the *original* schema so Zod v3 * `.transform()` / `.default()` / `.refine()` and other Standard Schema * parsing behavior still run before `execute()` sees the args. * * Injected override keys (`_background`, `suspendedToolRunId`, `resumeData`) * are stripped from the input before delegating, then merged back into the * validated value so the inner `execute()` still receives them — matching the * Zod v4 `.extend()` path's behavior. * * If the original schema has no `~standard.validate` (e.g. a raw JSON Schema * with no Standard Schema wrapper), fall back to validating against the * spliced JSON Schema directly. */ function buildJsonOverrideSchema(originalSchema, splicedJsonSchema, injectedKeys) { const fallback = (0, _mastra_schema_compat_schema.toStandardSchema)(splicedJsonSchema); const original = originalSchema; const originalValidate = original?.["~standard"]?.validate?.bind(original["~standard"]); const splicedProperties = splicedJsonSchema && typeof splicedJsonSchema === "object" && "properties" in splicedJsonSchema ? splicedJsonSchema.properties ?? {} : {}; const injectedProperties = {}; for (const key of injectedKeys) if (splicedProperties[key] !== void 0) injectedProperties[key] = splicedProperties[key]; const injectedValidator = (0, _mastra_schema_compat_schema.toStandardSchema)({ type: "object", properties: injectedProperties, additionalProperties: false }); const stripInjected = (input) => { if (!input || typeof input !== "object" || Array.isArray(input)) return { stripped: input, injected: {} }; const injected = {}; const stripped = {}; for (const [k, v] of Object.entries(input)) if (injectedKeys.includes(k)) injected[k] = v; else stripped[k] = v; return { stripped, injected }; }; const validate = (input) => { const { stripped, injected } = stripInjected(input); const baseResult = originalValidate ? originalValidate(stripped) : fallback["~standard"].validate(stripped); const injectedResult = injectedValidator["~standard"].validate(injected); const combine = (base, inj) => { const baseIssues = "issues" in base ? base.issues ?? [] : []; const injIssues = "issues" in inj ? inj.issues ?? [] : []; if (baseIssues.length || injIssues.length) return { issues: [...baseIssues, ...injIssues] }; const baseValue = base.value; const injValue = inj.value; if (baseValue && typeof baseValue === "object" && !Array.isArray(baseValue)) { const injMerged = injValue && typeof injValue === "object" && !Array.isArray(injValue) ? injValue : injected; return { value: { ...baseValue, ...injMerged } }; } return base; }; const baseIsPromise = baseResult && typeof baseResult.then === "function"; const injIsPromise = injectedResult && typeof injectedResult.then === "function"; if (baseIsPromise || injIsPromise) return Promise.all([baseResult, injectedResult]).then(([b, i]) => combine(b, i)); return combine(baseResult, injectedResult); }; return { "~standard": { version: 1, vendor: "mastra-json-override", validate, jsonSchema: fallback["~standard"].jsonSchema } }; } var CoreToolBuilder = class extends require_base.MastraBase { originalTool; options; logType; constructor(input) { super({ name: "CoreToolBuilder" }); this.originalTool = input.originalTool; this.options = input.options; this.logType = input.logType; const isBackgroundEligible = !!input.backgroundTaskEnabled; const isResumableTool = input.autoResumeSuspendedTools || this.originalTool.id?.startsWith("agent-") || this.originalTool.id?.startsWith("workflow-"); if (!require_toolchecks.isVercelTool(this.originalTool) && !require_toolchecks.isProviderDefinedTool(this.originalTool)) { if (isBackgroundEligible || isResumableTool) { let schema = this.originalTool.inputSchema; if (typeof schema === "function") schema = schema(); if (!schema) schema = zod_v4.z.object({}); if (require_zod_utils.isZodObject(schema) && isZodV4Schema(schema)) { let nextSchema = schema; if (isBackgroundEligible) nextSchema = require_zod_utils.safeExtendZodObject(nextSchema, { _background: require_background_tasks.backgroundOverrideZodSchema }); if (isResumableTool) nextSchema = require_zod_utils.safeExtendZodObject(nextSchema, { suspendedToolRunId: zod_v4.z.string().describe("The runId of the suspended tool").nullable().optional(), resumeData: zod_v4.z.any().describe("The resumeData object created from the resumeSchema of suspended tool").optional() }); this.originalTool.inputSchema = nextSchema; } else { const jsonSchema = (0, _mastra_schema_compat_schema.standardSchemaToJSONSchema)((0, _mastra_schema_compat_schema.isStandardSchemaWithJSON)(schema) ? schema : (0, _mastra_schema_compat_schema.toStandardSchema)(schema), { io: "input" }); if (jsonSchema && typeof jsonSchema === "object" && jsonSchema.type === "object") { const properties = { ...jsonSchema.properties ?? {} }; const injectedKeys = []; if (isBackgroundEligible) { properties._background = require_background_tasks.backgroundOverrideJsonSchema; injectedKeys.push("_background"); } if (isResumableTool) { properties.suspendedToolRunId = { type: ["string", "null"], description: "The runId of the suspended tool" }; properties.resumeData = { description: "The resumeData object created from the resumeSchema of suspended tool" }; injectedKeys.push("suspendedToolRunId", "resumeData"); } this.originalTool.inputSchema = buildJsonOverrideSchema(schema, { ...jsonSchema, properties }, injectedKeys); } } } } } getParameters = () => { if (require_toolchecks.isVercelTool(this.originalTool)) { let schema = this.originalTool.parameters ?? ("inputSchema" in this.originalTool ? this.originalTool.inputSchema : void 0) ?? zod_v4.z.object({}); if (typeof schema === "function") schema = schema(); return schema; } let schema = this.originalTool.inputSchema; if ((0, _mastra_schema_compat_schema.isStandardSchemaWithJSON)(schema)) return schema; if (typeof schema === "function") schema = schema(); return schema; }; getOutputSchema = () => { if ("outputSchema" in this.originalTool) { let schema = this.originalTool.outputSchema; if ((0, _mastra_schema_compat_schema.isStandardSchemaWithJSON)(schema)) return schema; if (typeof schema === "function") schema = schema(); return schema; } return null; }; getResumeSchema = () => { if ("resumeSchema" in this.originalTool) { let schema = this.originalTool.resumeSchema; if (typeof schema === "function") schema = schema(); return schema; } return null; }; getSuspendSchema = () => { if ("suspendSchema" in this.originalTool) { let schema = this.originalTool.suspendSchema; if (typeof schema === "function") schema = schema(); return schema; } return null; }; buildProviderTool(tool) { if ("type" in tool && (tool.type === "provider-defined" || tool.type === "provider") && "id" in tool && typeof tool.id === "string" && tool.id.includes(".")) { let parameters = "parameters" in tool ? tool.parameters : "inputSchema" in tool ? tool.inputSchema : void 0; if (typeof parameters === "function") parameters = parameters(); let outputSchema = "outputSchema" in tool ? tool.outputSchema : void 0; if (typeof outputSchema === "function") outputSchema = outputSchema(); let processedParameters; if (parameters !== void 0 && parameters !== null) if (typeof parameters === "object" && "jsonSchema" in parameters) processedParameters = parameters; else if ((0, _mastra_schema_compat_schema.isStandardSchemaWithJSON)(parameters)) processedParameters = { jsonSchema: (0, _mastra_schema_compat_schema.standardSchemaToJSONSchema)(parameters, { io: "input" }) }; else processedParameters = (0, _mastra_schema_compat.convertZodSchemaToAISDKSchema)(parameters); else processedParameters = { jsonSchema: { type: "object", properties: {}, additionalProperties: false } }; let processedOutputSchema; if (outputSchema !== void 0 && outputSchema !== null) if (typeof outputSchema === "object" && "jsonSchema" in outputSchema) processedOutputSchema = outputSchema; else if ((0, _mastra_schema_compat_schema.isStandardSchemaWithJSON)(outputSchema)) processedOutputSchema = { jsonSchema: (0, _mastra_schema_compat_schema.standardSchemaToJSONSchema)(outputSchema) }; else processedOutputSchema = (0, _mastra_schema_compat.convertZodSchemaToAISDKSchema)(outputSchema); return { ...processedOutputSchema ? { outputSchema: processedOutputSchema } : {}, type: "provider-defined", id: tool.id, ..."name" in tool && typeof tool.name === "string" ? { name: tool.name } : {}, args: "args" in this.originalTool ? this.originalTool.args : {}, description: tool.description, parameters: processedParameters, execute: this.originalTool.execute ? this.createExecute(this.originalTool, { ...this.options, description: this.originalTool.description }, this.logType) : void 0, toModelOutput: "toModelOutput" in this.originalTool ? this.originalTool.toModelOutput : void 0, transform: "transform" in this.originalTool ? this.originalTool.transform : void 0, inputExamples: "inputExamples" in this.originalTool ? this.originalTool.inputExamples : void 0 }; } } createLogMessageOptions({ agentName, toolName, type }) { const toolType = type === "toolset" ? "toolset" : "tool"; return { start: `Executing ${toolType}`, error: `Failed ${toolType} execution`, logData: { agent: agentName, tool: toolName } }; } createExecute(tool, options, logType) { const { logger, mastra: _mastra, memory: _memory, requestContext, model, tracingContext: _tracingContext, tracingPolicy: _tracingPolicy, ...rest } = options; const logModelObject = { modelId: model?.modelId, provider: model?.provider, specificationVersion: model?.specificationVersion }; const { start, logData } = this.createLogMessageOptions({ agentName: options.agentName, toolName: options.name, type: logType }); const mcpMeta = !require_toolchecks.isVercelTool(tool) && "mcpMetadata" in tool ? tool.mcpMetadata : void 0; const execFunction = async (args, execOptions, toolSpan) => { try { let result; let suspendData = null; if (require_toolchecks.isVercelTool(tool)) result = await require_utils.executeWithContext({ span: toolSpan, fn: async () => tool?.execute?.(args, execOptions) }); else { /** * MASTRA INSTANCE TYPES IN TOOL EXECUTION: * * Full Mastra & MastraPrimitives (has getAgent, getWorkflow, etc.): * - Auto-generated workflow tools from agent.listWorkflows() * - These get this.#mastra directly and can be wrapped * * MastraPrimitives only (limited interface): * - Memory tools (from memory.listTools()) * - Assigned tools (agent.tools) * - Toolset tools (from toolsets) * - Client tools (passed as tools in generate/stream options) * - These get mastraProxy and have limited functionality * * TODO: Consider providing full Mastra instance to more tool types for enhanced functionality */ const wrappedMastra = options.mastra ? require_observability.wrapMastra(options.mastra, { currentSpan: toolSpan }) : options.mastra; const resumeSchema = this.getResumeSchema(); const baseContext = { threadId: options.threadId, resourceId: options.resourceId, mastra: wrappedMastra, memory: options.memory, runId: options.runId, requestContext: mergeRequestContexts(options.requestContext, execOptions.requestContext), actor: execOptions.actor, workspace: execOptions.workspace ?? options.workspace, browser: options.browser, observe: execOptions.observe ?? require_types.noopObserve, writer: new require_types.ToolStream({ prefix: "tool", callId: execOptions.toolCallId, name: options.name, runId: options.runId }, options.outputWriter || execOptions.outputWriter), ...require_observability.createObservabilityContext({ currentSpan: toolSpan }), abortSignal: execOptions.abortSignal, suspend: (args, suspendOptions) => { suspendData = args; const newSuspendOptions = { ...suspendOptions ?? {}, resumeSchema: suspendOptions?.resumeSchema ?? (resumeSchema ? JSON.stringify((0, _mastra_schema_compat_schema.standardSchemaToJSONSchema)((0, _mastra_schema_compat_schema.toStandardSchema)(resumeSchema), { io: "input" })) : void 0) }; return execOptions.suspend?.(args, newSuspendOptions); }, resumeData: execOptions.resumeData }; const isAgentExecution = execOptions.toolCallId && execOptions.messages || options.agentName && options.threadId && !options.workflowId; const isWorkflowExecution = !isAgentExecution && (options.workflow || options.workflowId); let toolContext; if (isAgentExecution) { const { suspend, resumeData, threadId, resourceId, ...restBaseContext } = baseContext; toolContext = { ...restBaseContext, agent: { agentId: options.agentId || "", toolCallId: execOptions.toolCallId || "", messages: execOptions.messages || [], suspend, resumeData, threadId, resourceId, outputWriter: options.outputWriter || execOptions.outputWriter, flushMessages: execOptions.flushMessages } }; } else if (isWorkflowExecution) { const { suspend, resumeData, ...restBaseContext } = baseContext; toolContext = { ...restBaseContext, workflow: options.workflow || { runId: options.runId, workflowId: options.workflowId, state: options.state, setState: options.setState, suspend, resumeData } }; } else if (execOptions.mcp) toolContext = { ...baseContext, mcp: execOptions.mcp }; else toolContext = baseContext; const resumeData = execOptions.resumeData; if (resumeData) { const resumeValidation = require_tool.validateToolInput(resumeSchema, resumeData, options.name); if (resumeValidation.error) { logger?.warn(resumeValidation.error.message); toolSpan?.end({ output: resumeValidation.error, attributes: { success: false } }); return resumeValidation.error; } } result = await require_utils.executeWithContext({ span: toolSpan, fn: async () => tool?.execute?.(args, toolContext) }); } if (suspendData) { const suspendValidation = require_tool.validateToolSuspendData(this.getSuspendSchema(), suspendData, options.name); if (suspendValidation.error) { logger?.warn(suspendValidation.error.message); toolSpan?.end({ output: suspendValidation.error, attributes: { success: false } }); return suspendValidation.error; } } if (typeof result === "undefined" && !!suspendData) { toolSpan?.end({ output: result, attributes: { success: true } }); return result; } if (require_toolchecks.isVercelTool(tool)) { const outputValidation = require_tool.validateToolOutput(this.getOutputSchema(), result, options.name, false); if (outputValidation.error) { logger?.warn(outputValidation.error.message); toolSpan?.end({ output: outputValidation.error, attributes: { success: false } }); return outputValidation.error; } result = outputValidation.data; } toolSpan?.end({ output: result, attributes: { success: true } }); return result; } catch (error) { toolSpan?.error({ error, attributes: { success: false } }); throw error; } }; return async (args, execOptions) => { let logger = options.logger || this.logger; const tracingContext = execOptions?.tracingContext || options.tracingContext; const toolRequestContext = execOptions?.requestContext ?? options.requestContext; const toolSpan = require_utils.getOrCreateSpan({ type: mcpMeta ? "mcp_tool_call" : "tool_call", name: mcpMeta ? `mcp_tool: '${options.name}' on '${mcpMeta.serverName}'` : `tool: '${options.name}'`, input: args, entityType: require_utils.EntityType.TOOL, entityId: options.name, entityName: options.name, attributes: mcpMeta ? { mcpServer: mcpMeta.serverName, serverVersion: mcpMeta.serverVersion, toolDescription: options.description, toolCallId: execOptions?.toolCallId } : { toolDescription: options.description, toolType: logType || "tool", toolCallId: execOptions?.toolCallId }, tracingPolicy: options.tracingPolicy, tracingContext, requestContext: toolRequestContext, mastra: options.mastra && "observability" in options.mastra ? options.mastra : void 0 }); const fgaProvider = options.mastra?.getServer?.()?.fga; const user = toolRequestContext?.get("user"); if (fgaProvider) { const { getAgentToolFGAResourceId, getMCPToolFGAResourceId, getStandaloneToolFGAResourceId, requireFGA } = await Promise.resolve().then(() => require("./auth/ee/fga-check.cjs")); await requireFGA({ fgaProvider, user, resource: { type: "tool", id: mcpMeta?.serverName ? getMCPToolFGAResourceId(mcpMeta.serverName, options.name) : options.agentId ? getAgentToolFGAResourceId(options.agentId, options.name) : getStandaloneToolFGAResourceId(options.name) }, permission: require_ee_DXvSoTl7.MastraFGAPermissions.TOOLS_EXECUTE, requestContext: toolRequestContext, actor: execOptions?.actor, context: { resourceId: options.resourceId }, metadata: { toolName: options.name, agentId: options.agentId, agentName: options.agentName, runId: options.runId, threadId: options.threadId, executionResourceId: options.resourceId, mcpMetadata: mcpMeta } }); } try { logger.debug(start, { ...logData, ...rest, model: logModelObject, args }); const isResuming = !!execOptions?.resumeData; const parameters = this.getParameters(); if (!isResuming) { const { data, error } = require_tool.validateToolInput(parameters, args, options.name); const suspendedToolRunIdErrToIgnore = error?.message?.includes("suspendedToolRunId: Required") && !args?.resumeData; if (error && !suspendedToolRunIdErrToIgnore) { logger.warn("Tool input validation failed", { ...logData, validationError: error.message }); toolSpan?.end({ output: error, attributes: { success: false } }); return error; } args = data; } return await new Promise((resolve, reject) => { setImmediate(async () => { try { resolve(await execFunction(args, execOptions, toolSpan)); } catch (err) { reject(err); } }); }); } catch (err) { const mastraError = new require_error.MastraError({ id: "TOOL_EXECUTION_FAILED", domain: require_error.ErrorDomain.TOOL, category: require_error.ErrorCategory.USER, details: { errorMessage: String(err), argsJson: require_utils_safe_stringify.safeStringify(args), model: model?.modelId ?? "" } }, err); toolSpan?.error({ error: mastraError, attributes: { success: false } }); logger.trackException(mastraError, { ...logData, ...rest, model: logModelObject, args }); throw mastraError; } }; } buildV5() { const builtTool = this.build(); if (!builtTool.parameters) throw new Error("Tool parameters are required"); const base = { ...builtTool, inputSchema: builtTool.parameters, onInputStart: "onInputStart" in this.originalTool ? this.originalTool.onInputStart : void 0, onInputDelta: "onInputDelta" in this.originalTool ? this.originalTool.onInputDelta : void 0, onInputAvailable: "onInputAvailable" in this.originalTool ? this.originalTool.onInputAvailable : void 0, onOutput: "onOutput" in this.originalTool ? this.originalTool.onOutput : void 0 }; if (builtTool.type === "provider-defined") { const { execute, parameters, ...rest } = base; const name = ("name" in builtTool && typeof builtTool.name === "string" ? builtTool.name : null) || builtTool.id.split(".")[1] || builtTool.id; return { ...rest, type: builtTool.type, id: builtTool.id, name, args: builtTool.args }; } return base; } build() { const providerTool = this.buildProviderTool(this.originalTool); if (providerTool) return providerTool; const model = this.options.model; const schemaCompatLayers = []; if (model) { const supportsStructuredOutputs = "supportsStructuredOutputs" in model ? model.supportsStructuredOutputs ?? false : false; const modelInfo = { modelId: model.modelId, supportsStructuredOutputs, provider: model.provider }; schemaCompatLayers.push(new _mastra_schema_compat.OpenAIReasoningSchemaCompatLayer(modelInfo), new _mastra_schema_compat.OpenAISchemaCompatLayer(modelInfo), new _mastra_schema_compat.GoogleSchemaCompatLayer(modelInfo), new _mastra_schema_compat.AnthropicSchemaCompatLayer(modelInfo), new _mastra_schema_compat.DeepSeekSchemaCompatLayer(modelInfo), new _mastra_schema_compat.MetaSchemaCompatLayer(modelInfo)); } const originalSchema = this.getParameters(); let processedInputSchema; if (originalSchema) if ((0, _mastra_schema_compat_schema.isStandardSchemaWithJSON)(originalSchema)) { const applicableLayer = schemaCompatLayers.find((layer) => layer.shouldApply()); let schemaToUse; if (applicableLayer) schemaToUse = applicableLayer.processToCompatSchema(originalSchema); else schemaToUse = (0, _mastra_schema_compat_schema.toStandardSchema)(originalSchema); processedInputSchema = (0, _mastra_schema_compat.jsonSchema)((0, _mastra_schema_compat_schema.standardSchemaToJSONSchema)(schemaToUse, { io: "input" }), { validate: (value) => { const result = schemaToUse["~standard"].validate(value); if (result instanceof Promise) return result.then((r) => { if ("issues" in r && r.issues) return { success: false, error: new Error(r.issues.map((i) => i.message).join(", ")) }; return { success: true, value: r.value }; }); if ("issues" in result && result.issues) return { success: false, error: new Error(result.issues.map((i) => i.message).join(", ")) }; return { success: true, value: result.value }; } }); } else processedInputSchema = (0, _mastra_schema_compat.applyCompatLayer)({ schema: originalSchema, compatLayers: schemaCompatLayers, mode: "aiSdkSchema" }); const outputSchema = this.getOutputSchema(); let processedOutputSchema; if (outputSchema) if ((0, _mastra_schema_compat_schema.isStandardSchemaWithJSON)(outputSchema)) processedOutputSchema = (0, _mastra_schema_compat_schema.standardSchemaToJSONSchema)(outputSchema, { io: "output" }); else processedOutputSchema = (0, _mastra_schema_compat.applyCompatLayer)({ schema: outputSchema, compatLayers: [], mode: "aiSdkSchema" }); let requireApproval = false; let needsApprovalFn; if (typeof this.options.requireApproval === "function") { requireApproval = true; needsApprovalFn = this.options.requireApproval; } else if (typeof this.options.requireApproval === "boolean") { requireApproval = this.options.requireApproval; needsApprovalFn = void 0; } if (require_toolchecks.isVercelTool(this.originalTool) && "needsApproval" in this.originalTool) { const needsApproval = this.originalTool.needsApproval; if (typeof needsApproval === "boolean") { requireApproval = needsApproval; needsApprovalFn = void 0; } else if (typeof needsApproval === "function") { needsApprovalFn = needsApproval; requireApproval = true; } } const instanceNeedsApprovalFn = require_toolchecks.getNeedsApprovalFn(this.originalTool); if (!needsApprovalFn && instanceNeedsApprovalFn) { needsApprovalFn = instanceNeedsApprovalFn; requireApproval = true; } return { type: "function", description: this.originalTool.description, requireApproval, needsApprovalFn, hasSuspendSchema: !!this.getSuspendSchema(), execute: this.originalTool.execute ? this.createExecute(this.originalTool, { ...this.options, description: this.originalTool.description }, this.logType) : void 0, id: "id" in this.originalTool ? this.originalTool.id : void 0, parameters: processedInputSchema ?? zod_v4.z.object({}), outputSchema: processedOutputSchema, strict: "strict" in this.originalTool ? this.originalTool.strict : void 0, providerOptions: "providerOptions" in this.originalTool ? this.originalTool.providerOptions : void 0, mcp: "mcp" in this.originalTool ? this.originalTool.mcp : void 0, toModelOutput: "toModelOutput" in this.originalTool ? this.originalTool.toModelOutput : void 0, transform: "transform" in this.originalTool ? this.originalTool.transform : void 0, inputExamples: "inputExamples" in this.originalTool ? this.originalTool.inputExamples : void 0, onInputStart: "onInputStart" in this.originalTool ? this.originalTool.onInputStart : void 0, onInputDelta: "onInputDelta" in this.originalTool ? this.originalTool.onInputDelta : void 0, onInputAvailable: "onInputAvailable" in this.originalTool ? this.originalTool.onInputAvailable : void 0, onOutput: "onOutput" in this.originalTool ? this.originalTool.onOutput : void 0, backgroundConfig: this.options.backgroundConfig }; } }; //#endregion //#region src/utils.ts const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); /** * Read a positive-integer environment variable (e.g. a TTL in ms). Unset, empty, * non-numeric, fractional, or non-positive values fall back to `fallback`. */ function readPositiveIntEnv(name, fallback) { const raw = process.env[name]; if (!raw) return fallback; const parsed = Number(raw); return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback; } /** * Checks if a value is a plain object (not an array, function, Date, RegExp, etc.) */ function isPlainObject(value) { if (value === null || typeof value !== "object") return false; const proto = Object.getPrototypeOf(value); return proto === Object.prototype || proto === null; } /** * Deep merges two objects, recursively merging nested plain objects. * Arrays, functions, and other non-plain objects are replaced (not merged). */ function deepMerge(target, source) { const output = { ...target }; if (!source) return output; Object.keys(source).forEach((key) => { const targetValue = output[key]; const sourceValue = source[key]; if (isPlainObject(targetValue) && isPlainObject(sourceValue)) output[key] = deepMerge(targetValue, sourceValue); else if (sourceValue !== void 0) output[key] = sourceValue; }); return output; } /** * Generate an empty object from a JSON Schema definition. * Accepts both a JSON string and a pre-parsed object. * Recursively initializes nested object properties and respects default values. */ function generateEmptyFromSchema(schema) { try { const parsedSchema = typeof schema === "string" ? JSON.parse(schema) : schema; if (!parsedSchema || parsedSchema.type !== "object" || !parsedSchema.properties) return {}; const obj = {}; for (const [key, prop] of Object.entries(parsedSchema.properties)) if (prop.default !== void 0) obj[key] = typeof prop.default === "object" && prop.default !== null ? JSON.parse(JSON.stringify(prop.default)) : prop.default; else if (prop.type === "object" && prop.properties) obj[key] = generateEmptyFromSchema(prop); else if (prop.type === "object") obj[key] = {}; else if (prop.type === "string") obj[key] = ""; else if (prop.type === "array") obj[key] = []; else if (prop.type === "number" || prop.type === "integer") obj[key] = 0; else if (prop.type === "boolean") obj[key] = false; else obj[key] = null; return obj; } catch { return {}; } } /** * Transforms a stream by masking content between XML tags. * @param stream Input stream to transform * @param tag Tag name to mask between (e.g. for <foo>...</foo>, use 'foo') * @param options Optional configuration for masking behavior */ async function* maskStreamTags(stream, tag, options = {}) { const { onStart, onEnd, onMask } = options; const openTag = `<${tag}>`; const closeTag = `</${tag}>`; let buffer = ""; let fullContent = ""; let isMasking = false; let isBuffering = false; const trimOutsideDelimiter = (text, delimiter, trim) => { if (!text.includes(delimiter)) return text; const parts = text.split(delimiter); if (trim === `before-start`) return `${delimiter}${parts[1]}`; return `${parts[0]}${delimiter}`; }; const startsWith = (text, pattern) => { if (pattern.includes(openTag.substring(0, 3))) pattern = trimOutsideDelimiter(pattern, `<`, `before-start`); return text.trim().startsWith(pattern.trim()); }; for await (const chunk of stream) { fullContent += chunk; if (isBuffering) buffer += chunk; const chunkHasTag = startsWith(chunk, openTag); const bufferHasTag = !chunkHasTag && isBuffering && startsWith(openTag, buffer); let toYieldBeforeMaskedStartTag = ``; if (!isMasking && (chunkHasTag || bufferHasTag)) { isMasking = true; isBuffering = false; const taggedTextToMask = trimOutsideDelimiter(buffer, `<`, `before-start`); if (taggedTextToMask !== buffer.trim()) toYieldBeforeMaskedStartTag = buffer.replace(taggedTextToMask, ``); buffer = ""; onStart?.(); } if (!isMasking && !isBuffering && startsWith(openTag, chunk) && chunk.trim() !== "") { isBuffering = true; buffer += chunk; continue; } if (isBuffering && buffer && !startsWith(openTag, buffer)) { yield buffer; buffer = ""; isBuffering = false; continue; } if (isMasking && fullContent.includes(closeTag)) { onMask?.(chunk); onEnd?.(); isMasking = false; const lastFullContent = fullContent; fullContent = ``; const textUntilEndTag = trimOutsideDelimiter(lastFullContent, closeTag, "after-end"); if (textUntilEndTag !== lastFullContent) yield lastFullContent.replace(textUntilEndTag, ``); continue; } if (isMasking) { onMask?.(chunk); if (toYieldBeforeMaskedStartTag) yield toYieldBeforeMaskedStartTag; continue; } yield chunk; } } /** * Resolve serialized zod output - This function takes the string output ot the `jsonSchemaToZod` function * and instantiates the zod object correctly. * * @param schema - serialized zod object * @returns resolved zod object */ function resolveSerializedZodOutput(schema) { return Function("z", `"use strict";return (${schema});`)(zod_v4.z); } /** * Checks if a value is a Zod type * @param value - The value to check * @returns True if the value is a Zod type, false otherwise */ function isZodType(value) { return typeof value === "object" && value !== null && "_def" in value && "parse" in value && typeof value.parse === "function" && "safeParse" in value && typeof value.safeParse === "function"; } function createDeterministicId(input) { return (0, crypto.createHash)("sha256").update(input).digest("hex").slice(0, 8); } /** * Sets the properties for a Vercel Tool, including an ID and inputSchema * @param tool - The tool to set the properties for * @returns The tool with the properties set */ function setVercelToolProperties(tool) { const inputSchema = "inputSchema" in tool ? tool.inputSchema : convertVercelToolParameters(tool); const toolId = !("id" in tool) ? tool.description ? `tool-${createDeterministicId(tool.description)}` : `tool-${Math.random().toString(36).substring(2, 9)}` : tool.id; return { ...tool, id: toolId, inputSchema }; } /** * Ensures a tool has an ID and inputSchema by generating one if not present * @param tool - The tool to ensure has an ID and inputSchema * @returns The tool with an ID and inputSchema */ function ensureToolProperties(tools) { return Object.keys(tools).reduce((acc, key) => { const tool = tools?.[key]; if (tool) { if (typeof tool === "function" && !(tool instanceof require_tool.Tool) && !require_toolchecks.isVercelTool(tool)) throw new require_error.MastraError({ id: "TOOL_INVALID_FORMAT", domain: require_error.ErrorDomain.TOOL, category: require_error.ErrorCategory.USER, text: `Tool "${key}" is not a valid tool format. Tools must be created using createTool() or be a valid Vercel AI SDK tool. Received a function.` }); if (require_toolchecks.isVercelTool(tool)) acc[key] = setVercelToolProperties(tool); else acc[key] = tool; } return acc; }, {}); } function convertVercelToolParameters(tool) { let schema = tool.parameters ?? zod_v4.z.object({}); if (typeof schema === "function") schema = schema(); return isZodType(schema) ? schema : resolveSerializedZodOutput((0, _mastra_schema_compat_json_to_zod.jsonSchemaToZod)(schema)); } /** * Converts a Vercel Tool or Mastra Tool into a CoreTool format * @param originalTool - The tool to convert (either VercelTool or ToolAction) * @param options - Tool options including Mastra-specific settings * @param logType - Type of tool to log (tool or toolset) * @returns A CoreTool that can be used by the system */ function makeCoreTool(originalTool, options, logType, autoResumeSuspendedTools, backgroundTaskEnabled) { return new CoreToolBuilder({ originalTool, options, logType, autoResumeSuspendedTools, backgroundTaskEnabled }).build(); } function makeCoreToolV5(originalTool, options, logType, autoResumeSuspendedTools, backgroundTaskEnabled) { return new CoreToolBuilder({ originalTool, options, logType, autoResumeSuspendedTools, backgroundTaskEnabled }).buildV5(); } /** * Creates a proxy for a Mastra instance to handle deprecated properties * @param mastra - The Mastra instance to proxy * @param logger - The logger to use for warnings * @returns A proxy for the Mastra instance */ function createMastraProxy({ mastra, logger }) { return new Proxy(mastra, { get(target, prop) { if (Reflect.has(target, prop)) { const value = Reflect.get(target, prop); if (typeof value === "function") return value.bind(target); return value; } if (prop === "logger") { logger.warn("Please use 'getLogger' instead, logger is deprecated"); return Reflect.apply(target.getLogger, target, []); } if (prop === "storage") { logger.warn("Please use 'getStorage' instead, storage is deprecated"); return Reflect.get(target, "storage"); } if (prop === "agents") { logger.warn("Please use 'listAgents' instead, agents is deprecated"); return Reflect.apply(target.listAgents, target, []); } if (prop === "tts") { logger.warn("Please use 'getTTS' instead, tts is deprecated"); return Reflect.apply(target.getTTS, target, []); } if (prop === "vectors") { logger.warn("Please use 'getVectors' instead, vectors is deprecated"); return Reflect.apply(target.getVectors, target, []); } if (prop === "memory") { logger.warn("Please use 'getMemory' instead, memory is deprecated"); return Reflect.get(target, "memory"); } return Reflect.get(target, prop); } }); } function checkEvalStorageFields(traceObject, logger) { const missingFields = []; if (!traceObject.input) missingFields.push("input"); if (!traceObject.output) missingFields.push("output"); if (!traceObject.agentName) missingFields.push("agent_name"); if (!traceObject.metricName) missingFields.push("metric_name"); if (!traceObject.instructions) missingFields.push("instructions"); if (!traceObject.globalRunId) missingFields.push("global_run_id"); if (!traceObject.runId) missingFields.push("run_id"); if (missingFields.length > 0) { if (logger) logger.warn("Skipping evaluation storage due to missing required fields", { missingFields, runId: traceObject.runId, agentName: traceObject.agentName }); else console.warn("Skipping evaluation storage due to missing required fields", { missingFields, runId: traceObject.runId, agentName: traceObject.agentName }); return false; } return true; } function detectSingleMessageCharacteristics(message) { if (typeof message === "object" && message !== null && (message.role === "function" || message.role === "data" || "toolInvocations" in message || "parts" in message || "experimental_attachments" in message)) return "has-ui-specific-parts"; else if (typeof message === "object" && message !== null && "content" in message && (Array.isArray(message.content) || "experimental_providerMetadata" in message || "providerOptions" in message)) return "has-core-specific-parts"; else if (typeof message === "object" && message !== null && "role" in message && "content" in message && typeof message.content === "string" && [ "system", "user", "assistant", "tool" ].includes(message.role)) return "message"; else return "other"; } function isUiMessage(message) { return detectSingleMessageCharacteristics(message) === `has-ui-specific-parts`; } function isCoreMessage(message) { return [`has-core-specific-parts`, `message`].includes(detectSingleMessageCharacteristics(message)); } const SQL_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/; /** * Parses and returns a valid SQL identifier (such as a table or column name). * The identifier must: * - Start with a letter (a-z, A-Z) or underscore (_) * - Contain only letters, numbers, or underscores * - Be at most 63 characters long * * @param name - The identifier string to parse. * @param kind - Optional label for error messages (e.g., 'table name'). * @returns The validated identifier as a branded type. * @throws {Error} If the identifier does not conform to SQL naming rules. * * @example * const id = parseSqlIdentifier('my_table'); // Ok * parseSqlIdentifier('123table'); // Throws error */ function parseSqlIdentifier(name, kind = "identifier") { if (!SQL_IDENTIFIER_PATTERN.test(name) || name.length > 63) throw new Error(`Invalid ${kind}: ${name}. Must start with a letter or underscore, contain only letters, numbers, or underscores, and be at most 63 characters long.`); return name; } /** * Parses and returns a valid dot-separated SQL field key (e.g., 'user.profile.name'). * Each segment must: * - Start with a letter (a-z, A-Z) or underscore (_) * - Contain only letters, numbers, or underscores * - Be at most 63 characters long * * @param key - The dot-separated field key string to parse. * @returns The validated field key as a branded type. * @throws {Error} If any segment of the key is invalid. * * @example * const key = parseFieldKey('user_profile.name'); // Ok * parseFieldKey('user..name'); // Throws error * parseFieldKey('user.123name'); // Throws error */ function parseFieldKey(key) { if (!key) throw new Error("Field key cannot be empty"); const segments = key.split("."); for (const segment of segments) if (!SQL_IDENTIFIER_PATTERN.test(segment) || segment.length > 63) throw new Error(`Invalid field key segment: ${segment} in ${key}`); return key; } /** * Removes specific keys from an object. * @param obj - The original object * @param keysToOmit - Keys to exclude from the returned object * @returns A new object with the specified keys removed */ function omitKeys(obj, keysToOmit) { return Object.fromEntries(Object.entries(obj).filter(([key]) => !keysToOmit.includes(key))); } /** * Selectively extracts specific fields from an object using dot notation. * Does not error if fields don't exist - simply omits them from the result. * @param obj - The source object to extract fields from * @param fields - Array of field paths (supports dot notation like 'output.text') * @returns New object containing only the specified fields */ function selectFields(obj, fields) { if (!obj || typeof obj !== "object") return obj; const result = {}; for (const field of fields) { const value = getNestedValue(obj, field); if (value !== void 0) setNestedValue(result, field, value); } return result; } /** * Gets a nested value from an object using dot notation * @param obj - Source object * @param path - Dot notation path (e.g., 'output.text') * @returns The value at the path, or undefined if not found */ function getNestedValue(obj, path) { return path.split(".").reduce((current, key) => { return current && typeof current === "object" ? current[key] : void 0; }, obj); } /** * Sets a nested value in an object using dot notation * @param obj - Target object * @param path - Dot notation path (e.g., 'output.text') * @param value - Value to set */ function setNestedValue(obj, path, value) { const keys = path.split("."); const lastKey = keys.pop(); if (!lastKey) return; for (const key of keys) if (key === "__proto__" || key === "constructor" || key === "prototype") return; if (lastKey === "__proto__" || lastKey === "constructor" || lastKey === "prototype") return; let current = obj; for (const key of keys) { const existing = Object.prototype.hasOwnProperty.call(current, key) ? current[key] : void 0; if (existing === null || typeof existing !== "object") Object.defineProperty(current, key, { value: Object.create(null), writable: true, enumerable: true, configurable: true }); current = current[key]; } Object.defineProperty(current, lastKey, { value, writable: true, enumerable: true, configurable: true }); } const removeUndefinedValues = (obj) => { return Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== void 0)); }; //#endregion Object.defineProperty(exports, "checkEvalStorageFields", { enumerable: true, get: function() { return checkEvalStorageFields; } }); Object.defineProperty(exports, "createMastraProxy", { enumerable: true, get: function() { return createMastraProxy; } }); Object.defineProperty(exports, "deepMerge", { enumerable: true, get: function() { return deepMerge; } }); Object.defineProperty(exports, "delay", { enumerable: true, get: function() { return delay; } }); Object.defineProperty(exports, "ensureToolProperties", { enumerable: true, get: function() { return ensureToolProperties; } }); Object.defineProperty(exports, "generateEmptyFromSchema", { enumerable: true, get: function() { return generateEmptyFromSchema; } }); Object.defineProperty(exports, "getNestedValue", { enumerable: true, get: function() { return getNestedValue; } }); Object.defineProperty(exports, "isCoreMessage", { enumerable: true, get: function() { return isCoreMessage; } }); Object.defineProperty(exports, "isUiMessage", { enumerable: true, get: function() { return isUiMessage; } }); Object.defineProperty(exports, "isZodType", { enumerable: true, get: function() { return isZodType; } }); Object.defineProperty(exports, "makeCoreTool", { enumerable: true, get: function() { return makeCoreTool; } }); Object.defineProperty(exports, "makeCoreToolV5", { enumerable: true, get: function() { return makeCoreToolV5; } }); Object.defineProperty(exports, "maskStreamTags", { enumerable: true, get: function() { return maskStreamTags; } }); Object.defineProperty(exports, "omitKeys", { enumerable: true, get: function() { return omitKeys; } }); Object.defineProperty(exports, "parseFieldKey", { enumerable: true, get: function() { return parseFieldKey; } }); Object.defineProperty(exports, "parseSqlIdentifier", { enumerable: true, get: function() { return parseSqlIdentifier; } }); Object.defineProperty(exports, "readPositiveIntEnv", { enumerable: true, get: function() { return readPositiveIntEnv; } }); Object.defineProperty(exports, "removeUndefinedValues", { enumerable: true, get: function() { return removeUndefinedValues; } }); Object.defineProperty(exports, "resolveSerializedZodOutput", { enumerable: true, get: function() { return resolveSerializedZodOutput; } }); Object.defineProperty(exports, "selectFields", { enumerable: true, get: function() { return selectFields; } }); Object.defineProperty(exports, "setNestedValue", { enumerable: true, get: function() { return setNestedValue; } }); //# sourceMappingURL=utils-Bw7FoAI3.cjs.map