@mastra/core
Version:
Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack.
1,466 lines (1,454 loc) • 96.2 kB
JavaScript
'use strict';
var chunkGPWMM745_cjs = require('./chunk-GPWMM745.cjs');
var chunk5NTO7S5I_cjs = require('./chunk-5NTO7S5I.cjs');
var chunkX7F4CSGR_cjs = require('./chunk-X7F4CSGR.cjs');
var chunkVF676YCO_cjs = require('./chunk-VF676YCO.cjs');
var chunk6VOPKVYH_cjs = require('./chunk-6VOPKVYH.cjs');
var chunkA5KDVZDL_cjs = require('./chunk-A5KDVZDL.cjs');
var crypto$1 = require('crypto');
var jsonSchemaToZod = require('json-schema-to-zod');
var zod = require('zod');
var schemaCompat = require('@mastra/schema-compat');
var web = require('stream/web');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var jsonSchemaToZod__default = /*#__PURE__*/_interopDefault(jsonSchemaToZod);
// src/ai-tracing/types.ts
var AISpanType = /* @__PURE__ */ ((AISpanType2) => {
AISpanType2["AGENT_RUN"] = "agent_run";
AISpanType2["GENERIC"] = "generic";
AISpanType2["MODEL_GENERATION"] = "model_generation";
AISpanType2["MODEL_STEP"] = "model_step";
AISpanType2["MODEL_CHUNK"] = "model_chunk";
AISpanType2["MCP_TOOL_CALL"] = "mcp_tool_call";
AISpanType2["PROCESSOR_RUN"] = "processor_run";
AISpanType2["TOOL_CALL"] = "tool_call";
AISpanType2["WORKFLOW_RUN"] = "workflow_run";
AISpanType2["WORKFLOW_STEP"] = "workflow_step";
AISpanType2["WORKFLOW_CONDITIONAL"] = "workflow_conditional";
AISpanType2["WORKFLOW_CONDITIONAL_EVAL"] = "workflow_conditional_eval";
AISpanType2["WORKFLOW_PARALLEL"] = "workflow_parallel";
AISpanType2["WORKFLOW_LOOP"] = "workflow_loop";
AISpanType2["WORKFLOW_SLEEP"] = "workflow_sleep";
AISpanType2["WORKFLOW_WAIT_EVENT"] = "workflow_wait_event";
return AISpanType2;
})(AISpanType || {});
var InternalSpans = /* @__PURE__ */ ((InternalSpans2) => {
InternalSpans2[InternalSpans2["NONE"] = 0] = "NONE";
InternalSpans2[InternalSpans2["WORKFLOW"] = 1] = "WORKFLOW";
InternalSpans2[InternalSpans2["AGENT"] = 2] = "AGENT";
InternalSpans2[InternalSpans2["TOOL"] = 4] = "TOOL";
InternalSpans2[InternalSpans2["MODEL"] = 8] = "MODEL";
InternalSpans2[InternalSpans2["ALL"] = 15] = "ALL";
return InternalSpans2;
})(InternalSpans || {});
var SamplingStrategyType = /* @__PURE__ */ ((SamplingStrategyType2) => {
SamplingStrategyType2["ALWAYS"] = "always";
SamplingStrategyType2["NEVER"] = "never";
SamplingStrategyType2["RATIO"] = "ratio";
SamplingStrategyType2["CUSTOM"] = "custom";
return SamplingStrategyType2;
})(SamplingStrategyType || {});
var AITracingEventType = /* @__PURE__ */ ((AITracingEventType2) => {
AITracingEventType2["SPAN_STARTED"] = "span_started";
AITracingEventType2["SPAN_UPDATED"] = "span_updated";
AITracingEventType2["SPAN_ENDED"] = "span_ended";
return AITracingEventType2;
})(AITracingEventType || {});
// src/ai-tracing/spans/base.ts
function isSpanInternal(spanType, flags) {
if (flags === void 0 || flags === 0 /* NONE */) {
return false;
}
switch (spanType) {
// Workflow-related spans
case "workflow_run" /* WORKFLOW_RUN */:
case "workflow_step" /* WORKFLOW_STEP */:
case "workflow_conditional" /* WORKFLOW_CONDITIONAL */:
case "workflow_conditional_eval" /* WORKFLOW_CONDITIONAL_EVAL */:
case "workflow_parallel" /* WORKFLOW_PARALLEL */:
case "workflow_loop" /* WORKFLOW_LOOP */:
case "workflow_sleep" /* WORKFLOW_SLEEP */:
case "workflow_wait_event" /* WORKFLOW_WAIT_EVENT */:
return (flags & 1 /* WORKFLOW */) !== 0;
// Agent-related spans
case "agent_run" /* AGENT_RUN */:
return (flags & 2 /* AGENT */) !== 0;
// Tool-related spans
case "tool_call" /* TOOL_CALL */:
case "mcp_tool_call" /* MCP_TOOL_CALL */:
return (flags & 4 /* TOOL */) !== 0;
// Model-related spans
case "model_generation" /* MODEL_GENERATION */:
case "model_step" /* MODEL_STEP */:
case "model_chunk" /* MODEL_CHUNK */:
return (flags & 8 /* MODEL */) !== 0;
// Default: never internal
default:
return false;
}
}
var BaseAISpan = class {
name;
type;
attributes;
parent;
startTime;
endTime;
isEvent;
isInternal;
aiTracing;
input;
output;
errorInfo;
metadata;
traceState;
/** Parent span ID (for root spans that are children of external spans) */
parentSpanId;
constructor(options, aiTracing) {
this.name = options.name;
this.type = options.type;
this.attributes = deepClean(options.attributes) || {};
this.metadata = deepClean(options.metadata);
this.parent = options.parent;
this.startTime = /* @__PURE__ */ new Date();
this.aiTracing = aiTracing;
this.isEvent = options.isEvent ?? false;
this.isInternal = isSpanInternal(this.type, options.tracingPolicy?.internal);
this.traceState = options.traceState;
if (this.isEvent) {
this.output = deepClean(options.output);
} else {
this.input = deepClean(options.input);
}
}
createChildSpan(options) {
return this.aiTracing.startSpan({ ...options, parent: this, isEvent: false });
}
createEventSpan(options) {
return this.aiTracing.startSpan({ ...options, parent: this, isEvent: true });
}
/** Returns `TRUE` if the span is the root span of a trace */
get isRootSpan() {
return !this.parent;
}
/** Get the closest parent spanId that isn't an internal span */
getParentSpanId(includeInternalSpans) {
if (!this.parent) {
return this.parentSpanId;
}
if (includeInternalSpans) return this.parent.id;
if (this.parent.isInternal) return this.parent.getParentSpanId(includeInternalSpans);
return this.parent.id;
}
/** Find the closest parent span of a specific type by walking up the parent chain */
findParent(spanType) {
let current = this.parent;
while (current) {
if (current.type === spanType) {
return current;
}
current = current.parent;
}
return void 0;
}
/** Returns a lightweight span ready for export */
exportSpan(includeInternalSpans) {
return {
id: this.id,
traceId: this.traceId,
name: this.name,
type: this.type,
attributes: this.attributes,
metadata: this.metadata,
startTime: this.startTime,
endTime: this.endTime,
input: this.input,
output: this.output,
errorInfo: this.errorInfo,
isEvent: this.isEvent,
isRootSpan: this.isRootSpan,
parentSpanId: this.getParentSpanId(includeInternalSpans)
};
}
};
var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
"logger",
"experimental_providerMetadata",
"providerMetadata",
"steps",
"tracingContext"
]);
function deepClean(value, options = {}, _seen = /* @__PURE__ */ new WeakSet(), _depth = 0) {
const { keysToStrip = DEFAULT_KEYS_TO_STRIP, maxDepth = 10 } = options;
if (_depth > maxDepth) {
return "[MaxDepth]";
}
if (value === null || typeof value !== "object") {
try {
JSON.stringify(value);
return value;
} catch (error) {
return `[${error instanceof Error ? error.message : String(error)}]`;
}
}
if (_seen.has(value)) {
return "[Circular]";
}
_seen.add(value);
if (Array.isArray(value)) {
return value.map((item) => deepClean(item, options, _seen, _depth + 1));
}
const cleaned = {};
for (const [key, val] of Object.entries(value)) {
if (keysToStrip.has(key)) {
continue;
}
try {
cleaned[key] = deepClean(val, options, _seen, _depth + 1);
} catch (error) {
cleaned[key] = `[${error instanceof Error ? error.message : String(error)}]`;
}
}
return cleaned;
}
// src/ai-tracing/spans/default.ts
var DefaultAISpan = class extends BaseAISpan {
id;
traceId;
constructor(options, aiTracing) {
super(options, aiTracing);
this.id = generateSpanId();
if (options.parent) {
this.traceId = options.parent.traceId;
} else if (options.traceId) {
if (isValidTraceId(options.traceId)) {
this.traceId = options.traceId;
} else {
console.error(
`[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
);
this.traceId = generateTraceId();
}
} else {
this.traceId = generateTraceId();
}
if (!options.parent && options.parentSpanId) {
if (isValidSpanId(options.parentSpanId)) {
this.parentSpanId = options.parentSpanId;
} else {
console.error(
`[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring parent span ID.`
);
}
}
}
end(options) {
if (this.isEvent) {
return;
}
this.endTime = /* @__PURE__ */ new Date();
if (options?.output !== void 0) {
this.output = deepClean(options.output);
}
if (options?.attributes) {
this.attributes = { ...this.attributes, ...deepClean(options.attributes) };
}
if (options?.metadata) {
this.metadata = { ...this.metadata, ...deepClean(options.metadata) };
}
}
error(options) {
if (this.isEvent) {
return;
}
const { error, endSpan = true, attributes, metadata } = options;
this.errorInfo = error instanceof chunk5NTO7S5I_cjs.MastraError ? {
id: error.id,
details: error.details,
category: error.category,
domain: error.domain,
message: error.message
} : {
message: error.message
};
if (attributes) {
this.attributes = { ...this.attributes, ...deepClean(attributes) };
}
if (metadata) {
this.metadata = { ...this.metadata, ...deepClean(metadata) };
}
if (endSpan) {
this.end();
} else {
this.update({});
}
}
update(options) {
if (this.isEvent) {
return;
}
if (options.input !== void 0) {
this.input = deepClean(options.input);
}
if (options.output !== void 0) {
this.output = deepClean(options.output);
}
if (options.attributes) {
this.attributes = { ...this.attributes, ...deepClean(options.attributes) };
}
if (options.metadata) {
this.metadata = { ...this.metadata, ...deepClean(options.metadata) };
}
}
get isValid() {
return true;
}
async export() {
return JSON.stringify({
spanId: this.id,
traceId: this.traceId,
startTime: this.startTime,
endTime: this.endTime,
attributes: this.attributes,
metadata: this.metadata
});
}
};
function generateSpanId() {
const bytes = new Uint8Array(8);
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
crypto.getRandomValues(bytes);
} else {
for (let i = 0; i < 8; i++) {
bytes[i] = Math.floor(Math.random() * 256);
}
}
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
}
function generateTraceId() {
const bytes = new Uint8Array(16);
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
crypto.getRandomValues(bytes);
} else {
for (let i = 0; i < 16; i++) {
bytes[i] = Math.floor(Math.random() * 256);
}
}
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
}
function isValidTraceId(traceId) {
return /^[0-9a-f]{1,32}$/i.test(traceId);
}
function isValidSpanId(spanId) {
return /^[0-9a-f]{1,16}$/i.test(spanId);
}
// src/ai-tracing/spans/no-op.ts
var NoOpAISpan = class extends BaseAISpan {
id;
traceId;
constructor(options, aiTracing) {
super(options, aiTracing);
this.id = "no-op";
this.traceId = "no-op-trace";
}
end(_options) {
}
error(_options) {
}
update(_options) {
}
get isValid() {
return false;
}
};
// src/ai-tracing/exporters/base.ts
var BaseExporter = class {
/** Mastra logger instance */
logger;
/** Whether this exporter is disabled */
isDisabled = false;
/**
* Initialize the base exporter with logger
*/
constructor(config = {}) {
const logLevel = this.resolveLogLevel(config.logLevel);
this.logger = config.logger ?? new chunkA5KDVZDL_cjs.ConsoleLogger({ level: logLevel, name: this.constructor.name });
}
/**
* Set the logger for the exporter (called by Mastra/AITracing during initialization)
*/
__setLogger(logger) {
this.logger = logger;
this.logger.debug(`Logger updated for exporter [name=${this.name}]`);
}
/**
* Convert string log level to LogLevel enum
*/
resolveLogLevel(logLevel) {
if (!logLevel) {
return chunkA5KDVZDL_cjs.LogLevel.INFO;
}
if (typeof logLevel === "number") {
return logLevel;
}
const logLevelMap = {
debug: chunkA5KDVZDL_cjs.LogLevel.DEBUG,
info: chunkA5KDVZDL_cjs.LogLevel.INFO,
warn: chunkA5KDVZDL_cjs.LogLevel.WARN,
error: chunkA5KDVZDL_cjs.LogLevel.ERROR
};
return logLevelMap[logLevel] ?? chunkA5KDVZDL_cjs.LogLevel.INFO;
}
/**
* Mark the exporter as disabled and log a message
*
* @param reason - Reason why the exporter is disabled
*/
setDisabled(reason) {
this.isDisabled = true;
this.logger.warn(`${this.name} disabled: ${reason}`);
}
/**
* Export a tracing event
*
* This method checks if the exporter is disabled before calling _exportEvent.
* Subclasses should implement _exportEvent instead of overriding this method.
*/
async exportEvent(event) {
if (this.isDisabled) {
return;
}
await this._exportEvent(event);
}
/**
* Shutdown the exporter and clean up resources
*
* Default implementation just logs. Override to add custom cleanup.
*/
async shutdown() {
this.logger.info(`${this.name} shutdown complete`);
}
};
var CoreToolBuilder = class extends chunk6VOPKVYH_cjs.MastraBase {
originalTool;
options;
logType;
constructor(input) {
super({ name: "CoreToolBuilder" });
this.originalTool = input.originalTool;
this.options = input.options;
this.logType = input.logType;
}
// Helper to get parameters based on tool type
getParameters = () => {
if (chunkVF676YCO_cjs.isVercelTool(this.originalTool)) {
let schema2 = this.originalTool.parameters ?? ("inputSchema" in this.originalTool ? this.originalTool.inputSchema : void 0) ?? zod.z.object({});
if (typeof schema2 === "function") {
schema2 = schema2();
}
return schema2;
}
let schema = this.originalTool.inputSchema ?? zod.z.object({});
if (typeof schema === "function") {
schema = schema();
}
return schema;
};
getOutputSchema = () => {
if ("outputSchema" in this.originalTool) {
let schema = this.originalTool.outputSchema;
if (typeof schema === "function") {
schema = schema();
}
return schema;
}
return null;
};
// For provider-defined tools, we need to include all required properties
buildProviderTool(tool) {
if ("type" in tool && tool.type === "provider-defined" && "id" in tool && typeof tool.id === "string" && tool.id.includes(".")) {
const parameters = this.getParameters();
const outputSchema = this.getOutputSchema();
return {
type: "provider-defined",
id: tool.id,
args: "args" in this.originalTool ? this.originalTool.args : {},
description: tool.description,
parameters: parameters.jsonSchema ? parameters : schemaCompat.convertZodSchemaToAISDKSchema(parameters),
...outputSchema ? { outputSchema: outputSchema.jsonSchema ? outputSchema : schemaCompat.convertZodSchemaToAISDKSchema(outputSchema) } : {},
execute: this.originalTool.execute ? this.createExecute(
this.originalTool,
{ ...this.options, description: this.originalTool.description },
this.logType
) : void 0
};
}
return void 0;
}
createLogMessageOptions({ agentName, toolName, type }) {
if (!agentName) {
return {
start: `Executing tool ${toolName}`,
error: `Failed tool execution`
};
}
const prefix = `[Agent:${agentName}]`;
const toolType = type === "toolset" ? "toolset" : "tool";
return {
start: `${prefix} - Executing ${toolType} ${toolName}`,
error: `${prefix} - Failed ${toolType} execution`
};
}
createExecute(tool, options, logType, processedSchema) {
const { logger, mastra: _mastra, memory: _memory, runtimeContext, model, ...rest } = options;
const logModelObject = {
modelId: model?.modelId,
provider: model?.provider,
specificationVersion: model?.specificationVersion
};
const { start, error } = this.createLogMessageOptions({
agentName: options.agentName,
toolName: options.name,
type: logType
});
const execFunction = async (args, execOptions) => {
const tracingContext = execOptions.tracingContext || options.tracingContext;
const toolSpan = tracingContext?.currentSpan?.createChildSpan({
type: "tool_call" /* TOOL_CALL */,
name: `tool: '${options.name}'`,
input: args,
attributes: {
toolId: options.name,
toolDescription: options.description,
toolType: logType || "tool"
},
tracingPolicy: options.tracingPolicy
});
try {
let result;
if (chunkVF676YCO_cjs.isVercelTool(tool)) {
result = await tool?.execute?.(args, execOptions);
} else {
const wrappedMastra = options.mastra ? wrapMastra(options.mastra, { currentSpan: toolSpan }) : options.mastra;
result = await tool?.execute?.(
{
context: args,
threadId: options.threadId,
resourceId: options.resourceId,
mastra: wrappedMastra,
memory: options.memory,
runId: options.runId,
runtimeContext: options.runtimeContext ?? new chunkGPWMM745_cjs.RuntimeContext(),
writer: new chunkX7F4CSGR_cjs.ToolStream(
{
prefix: "tool",
callId: execOptions.toolCallId,
name: options.name,
runId: options.runId
},
options.writableStream || execOptions.writableStream
),
tracingContext: { currentSpan: toolSpan }
},
execOptions
);
}
toolSpan?.end({ output: result });
return result ?? void 0;
} catch (error2) {
toolSpan?.error({ error: error2 });
throw error2;
}
};
return async (args, execOptions) => {
let logger2 = options.logger || this.logger;
try {
logger2.debug(start, { ...rest, model: logModelObject, args });
const parameters = processedSchema || this.getParameters();
const { data, error: error2 } = chunkVF676YCO_cjs.validateToolInput(parameters, args, options.name);
if (error2) {
logger2.warn(`Tool input validation failed for '${options.name}'`, {
toolName: options.name,
errors: error2.validationErrors,
args
});
return error2;
}
args = data;
return await new Promise((resolve, reject) => {
setImmediate(async () => {
try {
const result = await execFunction(args, execOptions);
resolve(result);
} catch (err) {
reject(err);
}
});
});
} catch (err) {
const mastraError = new chunk5NTO7S5I_cjs.MastraError(
{
id: "TOOL_EXECUTION_FAILED",
domain: "TOOL" /* TOOL */,
category: "USER" /* USER */,
details: {
errorMessage: String(error),
argsJson: JSON.stringify(args),
model: model?.modelId ?? ""
}
},
err
);
logger2.trackException(mastraError);
logger2.error(error, { ...rest, model: logModelObject, error: mastraError, args });
return 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
};
if (builtTool.type === "provider-defined") {
const { execute, parameters, ...rest } = base;
const name = 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 = model.specificationVersion !== "v2" ? model.supportsStructuredOutputs ?? false : false;
const modelInfo = {
modelId: model.modelId,
supportsStructuredOutputs,
provider: model.provider
};
schemaCompatLayers.push(
new schemaCompat.OpenAIReasoningSchemaCompatLayer(modelInfo),
new schemaCompat.OpenAISchemaCompatLayer(modelInfo),
new schemaCompat.GoogleSchemaCompatLayer(modelInfo),
new schemaCompat.AnthropicSchemaCompatLayer(modelInfo),
new schemaCompat.DeepSeekSchemaCompatLayer(modelInfo),
new schemaCompat.MetaSchemaCompatLayer(modelInfo)
);
}
let processedZodSchema;
let processedSchema;
const originalSchema = this.getParameters();
const applicableLayer = schemaCompatLayers.find((layer) => layer.shouldApply());
if (applicableLayer && originalSchema) {
processedZodSchema = applicableLayer.processZodType(originalSchema);
processedSchema = schemaCompat.applyCompatLayer({
schema: originalSchema,
compatLayers: schemaCompatLayers,
mode: "aiSdkSchema"
});
} else {
processedZodSchema = originalSchema;
processedSchema = schemaCompat.applyCompatLayer({
schema: originalSchema,
compatLayers: schemaCompatLayers,
mode: "aiSdkSchema"
});
}
let processedOutputSchema;
if (this.getOutputSchema()) {
processedOutputSchema = schemaCompat.applyCompatLayer({
schema: this.getOutputSchema(),
compatLayers: [],
mode: "aiSdkSchema"
});
}
const definition = {
type: "function",
description: this.originalTool.description,
parameters: this.getParameters(),
outputSchema: this.getOutputSchema(),
requireApproval: this.options.requireApproval,
execute: this.originalTool.execute ? this.createExecute(
this.originalTool,
{ ...this.options, description: this.originalTool.description },
this.logType,
processedZodSchema
// Pass the processed Zod schema for validation
) : void 0
};
return {
...definition,
id: "id" in this.originalTool ? this.originalTool.id : void 0,
parameters: processedSchema,
outputSchema: processedOutputSchema
};
}
};
// src/utils.ts
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
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 (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
output[key] = sourceValue;
} else if (sourceValue instanceof Object && targetValue instanceof Object && !Array.isArray(sourceValue) && !Array.isArray(targetValue)) {
output[key] = deepMerge(targetValue, sourceValue);
} else if (sourceValue !== void 0) {
output[key] = sourceValue;
}
});
return output;
}
function generateEmptyFromSchema(schema) {
try {
const parsedSchema = JSON.parse(schema);
if (!parsedSchema || parsedSchema.type !== "object" || !parsedSchema.properties) return {};
const obj = {};
const TYPE_DEFAULTS = {
string: "",
array: [],
object: {},
number: 0,
integer: 0,
boolean: false
};
for (const [key, prop] of Object.entries(parsedSchema.properties)) {
obj[key] = TYPE_DEFAULTS[prop.type] ?? null;
}
return obj;
} catch {
return {};
}
}
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;
}
}
function resolveSerializedZodOutput(schema) {
return Function("z", `"use strict";return (${schema});`)(zod.z);
}
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 crypto$1.createHash("sha256").update(input).digest("hex").slice(0, 8);
}
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
};
}
function ensureToolProperties(tools) {
const toolsWithProperties = Object.keys(tools).reduce((acc, key) => {
const tool = tools?.[key];
if (tool) {
if (chunkVF676YCO_cjs.isVercelTool(tool)) {
acc[key] = setVercelToolProperties(tool);
} else {
acc[key] = tool;
}
}
return acc;
}, {});
return toolsWithProperties;
}
function convertVercelToolParameters(tool) {
let schema = tool.parameters ?? zod.z.object({});
if (typeof schema === "function") {
schema = schema();
}
return isZodType(schema) ? schema : resolveSerializedZodOutput(jsonSchemaToZod__default.default(schema));
}
function makeCoreTool(originalTool, options, logType) {
return new CoreToolBuilder({ originalTool, options, logType }).build();
}
function makeCoreToolV5(originalTool, options, logType) {
return new CoreToolBuilder({ originalTool, options, logType }).buildV5();
}
function createMastraProxy({ mastra, logger }) {
return new Proxy(mastra, {
get(target, prop) {
const hasProp = Reflect.has(target, prop);
if (hasProp) {
const value = Reflect.get(target, prop);
const isFunction = typeof value === "function";
if (isFunction) {
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 === "telemetry") {
logger.warn(`Please use 'getTelemetry' instead, telemetry is deprecated`);
return Reflect.apply(target.getTelemetry, 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 'getAgents' instead, agents is deprecated`);
return Reflect.apply(target.getAgents, 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" || // UI-only role
message.role === "data" || // UI-only role
"toolInvocations" in message || // UI-specific field
"parts" in message || // UI-specific field
"experimental_attachments" in message)) {
return "has-ui-specific-parts";
} else if (typeof message === "object" && message !== null && "content" in message && (Array.isArray(message.content) || // Core messages can have array 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));
}
var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
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;
}
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;
}
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
let retryCount = 0;
let lastError = null;
while (retryCount < maxRetries) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`Request failed with status: ${response.status} ${response.statusText}`);
}
return response;
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
retryCount++;
if (retryCount >= maxRetries) {
break;
}
const delay2 = Math.min(1e3 * Math.pow(2, retryCount) * 1e3, 1e4);
await new Promise((resolve) => setTimeout(resolve, delay2));
}
}
throw lastError || new Error("Request failed after multiple retry attempts");
}
// src/ai-tracing/exporters/cloud.ts
var CloudExporter = class extends BaseExporter {
name = "mastra-cloud-ai-tracing-exporter";
config;
buffer;
flushTimer = null;
constructor(config = {}) {
super(config);
const accessToken = config.accessToken ?? process.env.MASTRA_CLOUD_ACCESS_TOKEN;
if (!accessToken) {
this.setDisabled(
"MASTRA_CLOUD_ACCESS_TOKEN environment variable not set.\n\u{1F680} Sign up at https://cloud.mastra.ai to see your AI traces online and obtain your access token."
);
}
const endpoint = config.endpoint ?? process.env.MASTRA_CLOUD_AI_TRACES_ENDPOINT ?? "https://api.mastra.ai/ai/spans/publish";
this.config = {
logger: this.logger,
logLevel: config.logLevel ?? chunkA5KDVZDL_cjs.LogLevel.INFO,
maxBatchSize: config.maxBatchSize ?? 1e3,
maxBatchWaitMs: config.maxBatchWaitMs ?? 5e3,
maxRetries: config.maxRetries ?? 3,
accessToken: accessToken || "",
endpoint
};
this.buffer = {
spans: [],
totalSize: 0
};
}
async _exportEvent(event) {
if (event.type !== "span_ended" /* SPAN_ENDED */) {
return;
}
this.addToBuffer(event);
if (this.shouldFlush()) {
this.flush().catch((error) => {
this.logger.error("Batch flush failed", {
error: error instanceof Error ? error.message : String(error)
});
});
} else if (this.buffer.totalSize === 1) {
this.scheduleFlush();
}
}
addToBuffer(event) {
if (this.buffer.totalSize === 0) {
this.buffer.firstEventTime = /* @__PURE__ */ new Date();
}
const spanRecord = this.formatSpan(event.exportedSpan);
this.buffer.spans.push(spanRecord);
this.buffer.totalSize++;
}
formatSpan(span) {
const spanRecord = {
traceId: span.traceId,
spanId: span.id,
parentSpanId: span.parentSpanId ?? null,
name: span.name,
spanType: span.type,
attributes: span.attributes ?? null,
metadata: span.metadata ?? null,
startedAt: span.startTime,
endedAt: span.endTime ?? null,
input: span.input ?? null,
output: span.output ?? null,
error: span.errorInfo,
isEvent: span.isEvent,
createdAt: /* @__PURE__ */ new Date(),
updatedAt: null
};
return spanRecord;
}
shouldFlush() {
if (this.buffer.totalSize >= this.config.maxBatchSize) {
return true;
}
if (this.buffer.firstEventTime && this.buffer.totalSize > 0) {
const elapsed = Date.now() - this.buffer.firstEventTime.getTime();
if (elapsed >= this.config.maxBatchWaitMs) {
return true;
}
}
return false;
}
scheduleFlush() {
if (this.flushTimer) {
clearTimeout(this.flushTimer);
}
this.flushTimer = setTimeout(() => {
this.flush().catch((error) => {
const mastraError = new chunk5NTO7S5I_cjs.MastraError(
{
id: `CLOUD_AI_TRACING_FAILED_TO_SCHEDULE_FLUSH`,
domain: "MASTRA_OBSERVABILITY" /* MASTRA_OBSERVABILITY */,
category: "USER" /* USER */
},
error
);
this.logger.trackException(mastraError);
this.logger.error("Scheduled flush failed", mastraError);
});
}, this.config.maxBatchWaitMs);
}
async flush() {
if (this.flushTimer) {
clearTimeout(this.flushTimer);
this.flushTimer = null;
}
if (this.buffer.totalSize === 0) {
return;
}
const startTime = Date.now();
const spansCopy = [...this.buffer.spans];
const flushReason = this.buffer.totalSize >= this.config.maxBatchSize ? "size" : "time";
this.resetBuffer();
try {
await this.batchUpload(spansCopy);
const elapsed = Date.now() - startTime;
this.logger.debug("Batch flushed successfully", {
batchSize: spansCopy.length,
flushReason,
durationMs: elapsed
});
} catch (error) {
const mastraError = new chunk5NTO7S5I_cjs.MastraError(
{
id: `CLOUD_AI_TRACING_FAILED_TO_BATCH_UPLOAD`,
domain: "MASTRA_OBSERVABILITY" /* MASTRA_OBSERVABILITY */,
category: "USER" /* USER */,
details: {
droppedBatchSize: spansCopy.length
}
},
error
);
this.logger.trackException(mastraError);
this.logger.error("Batch upload failed after all retries, dropping batch", mastraError);
}
}
/**
* Uploads spans to cloud API using fetchWithRetry for all retry logic
*/
async batchUpload(spans) {
const headers = {
Authorization: `Bearer ${this.config.accessToken}`,
"Content-Type": "application/json"
};
const options = {
method: "POST",
headers,
body: JSON.stringify({ spans })
};
await fetchWithRetry(this.config.endpoint, options, this.config.maxRetries);
}
resetBuffer() {
this.buffer.spans = [];
this.buffer.firstEventTime = void 0;
this.buffer.totalSize = 0;
}
async shutdown() {
if (this.isDisabled) {
return;
}
if (this.flushTimer) {
clearTimeout(this.flushTimer);
this.flushTimer = null;
}
if (this.buffer.totalSize > 0) {
this.logger.info("Flushing remaining events on shutdown", {
remainingEvents: this.buffer.totalSize
});
try {
await this.flush();
} catch (error) {
const mastraError = new chunk5NTO7S5I_cjs.MastraError(
{
id: `CLOUD_AI_TRACING_FAILED_TO_FLUSH_REMAINING_EVENTS_DURING_SHUTDOWN`,
domain: "MASTRA_OBSERVABILITY" /* MASTRA_OBSERVABILITY */,
category: "USER" /* USER */,
details: {
remainingEvents: this.buffer.totalSize
}
},
error
);
this.logger.trackException(mastraError);
this.logger.error("Failed to flush remaining events during shutdown", mastraError);
}
}
this.logger.info("CloudExporter shutdown complete");
}
};
// src/ai-tracing/exporters/console.ts
var ConsoleExporter = class extends BaseExporter {
name = "tracing-console-exporter";
constructor(config = {}) {
super(config);
}
async _exportEvent(event) {
const span = event.exportedSpan;
const formatAttributes = (attributes) => {
try {
return JSON.stringify(attributes, null, 2);
} catch (error) {
const errMsg = error instanceof Error ? error.message : "Unknown formatting error";
return `[Unable to serialize attributes: ${errMsg}]`;
}
};
const formatDuration = (startTime, endTime) => {
if (!endTime) return "N/A";
const duration = endTime.getTime() - startTime.getTime();
return `${duration}ms`;
};
switch (event.type) {
case "span_started" /* SPAN_STARTED */:
this.logger.info(`\u{1F680} SPAN_STARTED`);
this.logger.info(` Type: ${span.type}`);
this.logger.info(` Name: ${span.name}`);
this.logger.info(` ID: ${span.id}`);
this.logger.info(` Trace ID: ${span.traceId}`);
if (span.input !== void 0) {
this.logger.info(` Input: ${formatAttributes(span.input)}`);
}
this.logger.info(` Attributes: ${formatAttributes(span.attributes)}`);
this.logger.info("\u2500".repeat(80));
break;
case "span_ended" /* SPAN_ENDED */:
const duration = formatDuration(span.startTime, span.endTime);
this.logger.info(`\u2705 SPAN_ENDED`);
this.logger.info(` Type: ${span.type}`);
this.logger.info(` Name: ${span.name}`);
this.logger.info(` ID: ${span.id}`);
this.logger.info(` Duration: ${duration}`);
this.logger.info(` Trace ID: ${span.traceId}`);
if (span.input !== void 0) {
this.logger.info(` Input: ${formatAttributes(span.input)}`);
}
if (span.output !== void 0) {
this.logger.info(` Output: ${formatAttributes(span.output)}`);
}
if (span.errorInfo) {
this.logger.info(` Error: ${formatAttributes(span.errorInfo)}`);
}
this.logger.info(` Attributes: ${formatAttributes(span.attributes)}`);
this.logger.info("\u2500".repeat(80));
break;
case "span_updated" /* SPAN_UPDATED */:
this.logger.info(`\u{1F4DD} SPAN_UPDATED`);
this.logger.info(` Type: ${span.type}`);
this.logger.info(` Name: ${span.name}`);
this.logger.info(` ID: ${span.id}`);
this.logger.info(` Trace ID: ${span.traceId}`);
if (span.input !== void 0) {
this.logger.info(` Input: ${formatAttributes(span.input)}`);
}
if (span.output !== void 0) {
this.logger.info(` Output: ${formatAttributes(span.output)}`);
}
if (span.errorInfo) {
this.logger.info(` Error: ${formatAttributes(span.errorInfo)}`);
}
this.logger.info(` Updated Attributes: ${formatAttributes(span.attributes)}`);
this.logger.info("\u2500".repeat(80));
break;
default:
this.logger.warn(`Tracing event type not implemented: ${event.type}`);
}
}
async shutdown() {
this.logger.info("ConsoleExporter shutdown");
}
};
// src/ai-tracing/exporters/default.ts
function resolveStrategy(userConfig, storage, logger) {
if (userConfig.strategy && userConfig.strategy !== "auto") {
const hints = storage.aiTracingStrategy;
if (hints.supported.includes(userConfig.strategy)) {
return userConfig.strategy;
}
logger.warn("User-specified AI tracing strategy not supported by storage adapter, falling back to auto-selection", {
userStrategy: userConfig.strategy,
storageAdapter: storage.constructor.name,
supportedStrategies: hints.supported,
fallbackStrategy: hints.preferred
});
}
return storage.aiTracingStrategy.preferred;
}
var DefaultExporter = class {
name = "tracing-default-exporter";
logger;
mastra = null;
config;
resolvedStrategy;
buffer;
flushTimer = null;
// Track all spans that have been created, persists across flushes
allCreatedSpans = /* @__PURE__ */ new Set();
constructor(config = {}, logger) {
if (logger) {
this.logger = logger;
} else {
this.logger = new chunkA5KDVZDL_cjs.ConsoleLogger({ level: chunkA5KDVZDL_cjs.LogLevel.INFO });
}
this.config = {
maxBatchSize: config.maxBatchSize ?? 1e3,
maxBufferSize: config.maxBufferSize ?? 1e4,
maxBatchWaitMs: config.maxBatchWaitMs ?? 5e3,
maxRetries: config.maxRetries ?? 4,
retryDelayMs: config.retryDelayMs ?? 500,
strategy: config.strategy ?? "auto"
};
this.buffer = {
creates: [],
updates: [],
insertOnly: [],
seenSpans: /* @__PURE__ */ new Set(),
spanSequences: /* @__PURE__ */ new Map(),
completedSpans: /* @__PURE__ */ new Set(),
outOfOrderCount: 0,
totalSize: 0
};
this.resolvedStrategy = "batch-with-updates";
}
strategyInitialized = false;
/**
* Register the Mastra instance (called after Mastra construction is complete)
*/
__registerMastra(mastra) {
this.mastra = mastra;
}
/**
* Initialize the exporter (called after all dependencies are ready)
*/
init(_config) {
if (!this.mastra) {
throw new Error("DefaultExporter: init() called before __registerMastra()");
}
const storage = this.mastra.getStorage();
if (!storage) {
this.logger.warn("DefaultExporter disabled: Storage not available. Traces will not be persisted.");
return;
}
this.initializeStrategy(storage);
}
/**
* Initialize the resolved strategy once storage is available
*/
initializeStrategy(storage) {
if (this.strategyInitialized) return;
this.resolvedStrategy = resolveStrategy(this.config, storage, this.logger);
this.strategyInitialized = true;
this.logger.debug("AI tracing exporter initialized", {
strategy: this.resolvedStrategy,
source: this.config.strategy !== "auto" ? "user" : "auto",
storageAdapter: storage.constructor.name,
maxBatchSize: this.config.maxBatchSize,
maxBatchWaitMs: this.config.maxBatchWaitMs
});
}
/**
* Builds a unique span key for tracking
*/
buildSpanKey(traceId, spanId) {
return `${traceId}:${spanId}`;
}
/**
* Gets the next sequence number for a span
*/
getNextSequence(spanKey) {
const current = this.buffer.spanSequences.get(spanKey) || 0;
const next = current + 1;
this.buffer.spanSequences.set(spanKey, next);
return next;
}
/**
* Handles out-of-order span updates by logging and skipping
*/
handleOutOfOrderUpdate(event) {
this.logger.warn("Out-of-order span update detected - skipping event", {
spanId: event.exportedSpan.id,
traceId: event.exportedSpan.traceId,
spanName: event.exportedSpan.name,
eventType: event.type
});
}
/**
* Adds an event to the appropriate buffer based on strategy
*/
addToBuffer(event) {
const spanKey = this.buildSpanKey(event.exportedSpan.traceId, event.exportedSpan.id);
if (this.buffer.totalSize === 0) {
this.buffer.firstEventTime = /* @__PURE__ */ new Date();
}
switch (event.type) {
case "span_started" /* SPAN_STARTED */:
if (this.resolvedStrategy === "batch-with-updates") {
const createRecord = this.buildCreateRecord(event.exportedSpan);
this.buffer.creates.push(createRecord);
this.buffer.seenSpans.add(spanKey);
this.allCreatedSpans.add(spanKey);
}
break;
case "span_updated" /* SPAN_UPDATED */:
if (this.resolvedStrategy === "batch-with-updates") {
if (this.allCreatedSpans.has(spanKey)) {
this.buffer.updates.push({
traceId: event.exportedSpan.traceId,
spanId: event.exportedSpan.id,
updates: this.buildUpdateRecord(event.exportedSpan),
sequenceNumber: this.getNextSequence(spanKey)
});
} else {
this.handleOutOfOrderUpdate(event);
this.buffer.outOfOrderCount++;
}
}
break;
case "span_ended" /* SPAN_ENDED */:
if (this.resolvedStrategy === "batch-with-updates") {
if (this.allCreatedSpans.has(spanKey)) {
this.buffer.updates.push({
traceId: event.exportedSpan.traceId,
spanId: event.exportedSpan.id,
updates: this.buildUpdateRecord(event.exportedSpan),
sequenceNumber: this.getNextSequence(spanKey)
});
this.buffer.completedSpans.add(spanKey);
} else if (event.exportedSpan.isEvent) {
const createRecord = this.buildCreateRecord(event.exportedSpan);
this.buffer.creates.push(createRecord);
this.buffer.seenSpans.add(spanKey);
this.allCreatedSpans.add(spanKey);
this.buffer.completedSpans.add(spanKey);
} else {
this.handleOutOfOrderUpdate(event);
this.buffer.outOfOrderCount++;
}
} else if (this.resolvedStrategy === "insert-only") {
const createRecord = this.buildCreateRecord(event.exportedSpan);
this.buffer.insertOnly.push(createRecord);
this.buffer.completedSpans.add(spanKey);
this.allCreatedSpans.add(spanKey);
}
break;
}
this.buffer.totalSize = this.buffer.creates.length + this.buffer.updates.length + this.buffer.insertOnly.length;
}
/**
* Checks if buffer should be flushed based on size or time triggers
*/
shouldFlush() {
if (this.buffer.totalSize >= this.config.maxBufferSize) {
return true;
}
if (this.buffer.totalSize >= this.config.maxBatchSize) {
return true;
}
if (this.buffer.firstEventTime &