@mastra/core
Version:
1,213 lines (1,212 loc) • 48.1 kB
JavaScript
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