'use strict'; var chunkUNCK5F5J_cjs = require('./chunk-UNCK5F5J.cjs'); var chunkOLZ4GVNA_cjs = require('./chunk-OLZ4GVNA.cjs'); var chunkGJE2FI3V_cjs = require('./chunk-GJE2FI3V.cjs'); var chunkPY3SBFQA_cjs = require('./chunk-PY3SBFQA.cjs'); var chunkM3CR7ZNG_cjs = require('./chunk-M3CR7ZNG.cjs'); var chunkSC3Z566U_cjs = require('./chunk-SC3Z566U.cjs'); var chunk4332X24K_cjs = require('./chunk-4332X24K.cjs'); var chunkZVZXKOMS_cjs = require('./chunk-ZVZXKOMS.cjs'); var chunk2UAP4LDC_cjs = require('./chunk-2UAP4LDC.cjs'); var chunkQYKNPGZG_cjs = require('./chunk-QYKNPGZG.cjs'); var chunkUC46OXET_cjs = require('./chunk-UC46OXET.cjs'); var chunkD324RFFU_cjs = require('./chunk-D324RFFU.cjs'); var chunk6E6FPM76_cjs = require('./chunk-6E6FPM76.cjs'); var chunkLP4WZA6D_cjs = require('./chunk-LP4WZA6D.cjs'); var chunkWSD4JNMB_cjs = require('./chunk-WSD4JNMB.cjs'); var chunk2TATDSHU_cjs = require('./chunk-2TATDSHU.cjs'); var chunkXSOONORA_cjs = require('./chunk-XSOONORA.cjs'); var chunkPJIAL3WK_cjs = require('./chunk-PJIAL3WK.cjs'); var jsonToZod = require('@mastra/schema-compat/json-to-zod'); var v4 = require('zod/v4'); var crypto$1 = require('crypto'); var path = require('path'); var fs = require('fs'); // src/storage/domains/observability/base.ts var ObservabilityStorage = class _ObservabilityStorage extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "OBSERVABILITY" }); } async dangerouslyClearAll() { } /** * Provides hints for tracing strategy selection by the MastraStorageExporter. * Storage adapters can override this to specify their preferred and supported strategies. */ get observabilityStrategy() { return { preferred: "batch-with-updates", // Default for most SQL stores supported: ["realtime", "batch-with-updates", "insert-only"] }; } /** * Provides hints for tracing strategy selection by the MastraStorageExporter. * Storage adapters can override this to specify their preferred and supported strategies. * @deprecated Use {@link observabilityStrategy} instead. * @see {@link observabilityStrategy} for the replacement property. */ get tracingStrategy() { return this.observabilityStrategy; } /** * Reports the tracing strategy currently in effect for this attached observability store. * * Single-strategy stores can rely on the default implementation. Multi-strategy stores * should override this getter only when they can determine the actual configured mode * from storage-owned configuration, not exporter state. */ get runtimeTracingStrategy() { const supportedStrategies = this.observabilityStrategy.supported; return supportedStrategies.length === 1 ? supportedStrategies[0] : void 0; } /** * Optional feature list for observability storage APIs. * Stores that implement delta polling should override this and opt in explicitly. * Older stores and older package versions will simply omit it, which keeps page mode working. */ getFeatures() { return void 0; } /** * Creates a single Span record in the storage provider. */ async createSpan(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_CREATE_SPAN_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support creating spans" }); } /** * Updates a single Span with partial data. Primarily used for realtime trace creation. * * @deprecated This method only works with stores that support span updates, * It will be removed in the future. Instead try to add all data to a span before * ending it. */ async updateSpan(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_UPDATE_SPAN_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support updating spans" }); } /** * Retrieves a single span. */ async getSpan(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_SPAN_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support getting spans" }); } /** * Retrieves a single root span. */ async getRootSpan(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_ROOT_SPAN_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support getting root spans" }); } /** * Retrieves a single trace with all its associated spans. */ async getTrace(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_TRACE_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support getting traces" }); } /** * Retrieves the structural skeleton of a trace -- parent/child links, span * type, timing, and status -- with heavy fields (input, output, attributes, * metadata, tags, links) excluded. Intended for waterfall/timeline rendering * where the full payload would be wasteful. * * Default implementation forwards to {@link getTraceLight} (the legacy * override surface). Backends should override either method -- the response * shape is identical, and the unimplemented one delegates to the * implemented one. The cycle guard is what makes that safe. */ async getStructure(args) { if (this.getTraceLight === _ObservabilityStorage.prototype.getTraceLight) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_STRUCTURE_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support getting trace structure" }); } return this.getTraceLight(args); } /** * @deprecated Use {@link getStructure} instead. Default implementation * forwards to {@link getStructure} so backends that only override the * canonical name still work for legacy callers. */ async getTraceLight(args) { if (this.getStructure === _ObservabilityStorage.prototype.getStructure) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_TRACE_LIGHT_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support getting lightweight traces" }); } return this.getStructure(args); } /** * Retrieves the subtree of spans rooted at a given span, optionally bounded * to `depth` levels of descendants. * * Default implementation prefers a two-step path: fetch the lightweight * structure to determine which spans belong to the branch, then batch-fetch * only those with full data. This avoids pulling the entire trace when the * branch is a small slice of a large trace. Backends that don't yet * implement {@link getStructure} or {@link getSpans} fall back to fetching * the full trace and walking it in memory. */ async getBranch(args) { const parsed = chunkQYKNPGZG_cjs.getBranchArgsSchema.parse(args); try { const skeleton = await this.getStructure({ traceId: parsed.traceId }); if (!skeleton) return null; const branchSpanIds = chunkQYKNPGZG_cjs.extractBranchSpans(skeleton.spans, parsed.spanId, parsed.depth).map((s) => s.spanId); if (branchSpanIds.length === 0) return null; const { spans: spans2 } = await this.getSpans({ traceId: parsed.traceId, spanIds: branchSpanIds }); if (spans2.length === 0) return null; spans2.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime()); return { traceId: parsed.traceId, spans: spans2 }; } catch (error) { const isFallbackTrigger = error instanceof chunkXSOONORA_cjs.MastraError && (error.id === "OBSERVABILITY_STORAGE_GET_STRUCTURE_NOT_IMPLEMENTED" || error.id === "OBSERVABILITY_STORAGE_GET_TRACE_LIGHT_NOT_IMPLEMENTED" || error.id === "OBSERVABILITY_STORAGE_GET_SPANS_NOT_IMPLEMENTED"); if (!isFallbackTrigger) throw error; } const trace = await this.getTrace({ traceId: parsed.traceId }); if (!trace) return null; const spans = chunkQYKNPGZG_cjs.extractBranchSpans(trace.spans, parsed.spanId, parsed.depth); if (spans.length === 0) return null; return { traceId: parsed.traceId, spans }; } /** * Batch-fetches spans by spanId within a single trace. Used by the * optimized {@link getBranch} path to fetch only the spans that belong to * the requested branch (after walking the lightweight structure to identify * them) instead of pulling the entire trace. */ async getSpans(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_SPANS_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support batch-fetching spans" }); } /** * Retrieves a list of traces with optional filtering. */ async listTraces(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_LIST_TRACES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support listing traces" }); } /** * Retrieves a lightweight list of traces with optional filtering. */ async listTracesLight(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_LIST_TRACES_LIGHT_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support listing lightweight traces" }); } /** * Lists trace branches across all traces. Unlike {@link listTraces} (which * returns one row per root-rooted trace), each row here is a single branch * anchor span, including ones nested under a different root entity -- useful * for "show me every run of agent X" regardless of caller. Pairs with * {@link getBranch} to expand a single branch into its subtree. */ async listBranches(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_LIST_BRANCHES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support listing trace branches" }); } /** * Creates multiple Spans in a single batch. */ async batchCreateSpans(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_SPAN_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support batch creating spans" }); } /** * Updates multiple Spans in a single batch. */ async batchUpdateSpans(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_UPDATE_SPANS_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support batch updating spans" }); } /** * Deletes multiple traces and all their associated spans in a single batch operation. */ async batchDeleteTraces(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_DELETE_TRACES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support batch deleting traces" }); } // ============================================================================ // Logs // ============================================================================ /** * Creates multiple log records in a single batch. */ async batchCreateLogs(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_LOGS_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support batch creating logs" }); } /** * Retrieves a list of logs with optional filtering. */ async listLogs(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_LIST_LOGS_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support listing logs" }); } // ============================================================================ // Metrics // ============================================================================ /** * Creates multiple metric observations in a single batch. */ async batchCreateMetrics(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_METRICS_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support batch creating metrics" }); } async listMetrics(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_LIST_METRICS_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support listing metrics" }); } async getMetricAggregate(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_AGGREGATE_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support metric aggregation" }); } async getMetricBreakdown(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_BREAKDOWN_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support metric breakdown" }); } async getMetricTimeSeries(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_TIME_SERIES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support metric time series" }); } async getMetricPercentiles(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_PERCENTILES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support metric percentiles" }); } // ============================================================================ // Discovery / Metadata Methods // ============================================================================ async getMetricNames(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_NAMES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support metric name discovery" }); } async getMetricLabelKeys(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_LABEL_KEYS_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support metric label key discovery" }); } async getMetricLabelValues(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_LABEL_VALUES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support label value discovery" }); } async getEntityTypes(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_ENTITY_TYPES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support entity type discovery" }); } async getEntityNames(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_ENTITY_NAMES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support entity name discovery" }); } async getServiceNames(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_SERVICE_NAMES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support service name discovery" }); } async getEnvironments(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_ENVIRONMENTS_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support environment discovery" }); } async getTags(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_TAGS_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support tag discovery" }); } // ============================================================================ // Scores // ============================================================================ /** * Creates a single score record. */ async createScore(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_CREATE_SCORE_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support creating scores" }); } /** * Creates multiple score observations in a single batch. */ async batchCreateScores(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_SCORES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support batch creating scores" }); } /** * Retrieves a list of scores with optional filtering. */ async listScores(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_LIST_SCORES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support listing scores" }); } /** * Retrieves a single score by its score ID. */ async getScoreById(_scoreId) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_BY_ID_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support getting scores by ID" }); } async getScoreAggregate(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_AGGREGATE_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support score aggregation" }); } async getScoreBreakdown(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_BREAKDOWN_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support score breakdown" }); } async getScoreTimeSeries(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_TIME_SERIES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support score time series" }); } async getScorePercentiles(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_PERCENTILES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support score percentiles" }); } // ============================================================================ // Feedback // ============================================================================ /** * Creates a single feedback record. */ async createFeedback(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_CREATE_FEEDBACK_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support creating feedback" }); } /** * Creates multiple feedback observations in a single batch. */ async batchCreateFeedback(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_FEEDBACK_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support batch creating feedback" }); } /** * Retrieves a list of feedback with optional filtering. */ async listFeedback(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_LIST_FEEDBACK_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support listing feedback" }); } async getFeedbackAggregate(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_FEEDBACK_AGGREGATE_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support feedback aggregation" }); } async getFeedbackBreakdown(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_FEEDBACK_BREAKDOWN_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support feedback breakdown" }); } async getFeedbackTimeSeries(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_FEEDBACK_TIME_SERIES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support feedback time series" }); } async getFeedbackPercentiles(_args) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_STORAGE_GET_FEEDBACK_PERCENTILES_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support feedback percentiles" }); } }; // src/storage/utils.ts function safelyParseJSON(input) { if (input && typeof input === "object") return input; if (input == null) return {}; if (typeof input === "string") { try { return JSON.parse(input); } catch { return input; } } return {}; } function transformRow(row, tableName, options = {}) { const { preferredTimestampFields = {}, convertTimestamps = false, nullValuePattern, fieldMappings = {} } = options; const tableSchema = chunkQYKNPGZG_cjs.TABLE_SCHEMAS[tableName]; const result = {}; for (const [key, columnSchema] of Object.entries(tableSchema)) { const sourceKey = fieldMappings[key] ?? key; let value = row[sourceKey]; if (preferredTimestampFields[key]) { value = row[preferredTimestampFields[key]] ?? value; } if (value === void 0 || value === null) { continue; } if (nullValuePattern && value === nullValuePattern) { continue; } if (columnSchema.type === "jsonb") { if (typeof value === "string") { result[key] = safelyParseJSON(value); } else if (typeof value === "object") { result[key] = value; } else { result[key] = value; } } else if (columnSchema.type === "timestamp" && convertTimestamps && typeof value === "string") { result[key] = new Date(value); } else { result[key] = value; } } return result; } function transformScoreRow(row, options = {}) { return transformRow(row, chunkQYKNPGZG_cjs.TABLE_SCORERS, options); } function toUpperSnakeCase(str) { return str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, ""); } function createStoreErrorId(type, store, operation, status) { const normalizedStore = toUpperSnakeCase(store); const normalizedOperation = toUpperSnakeCase(operation); const normalizedStatus = toUpperSnakeCase(status); const typePrefix = type === "storage" ? "STORAGE" : "VECTOR"; return `MASTRA_${typePrefix}_${normalizedStore}_${normalizedOperation}_${normalizedStatus}`; } function createStorageErrorId(store, operation, status) { return createStoreErrorId("storage", store, operation, status); } function createVectorErrorId(store, operation, status) { return createStoreErrorId("vector", store, operation, status); } function getSqlType(type) { switch (type) { case "text": return "TEXT"; case "timestamp": return "TIMESTAMP"; case "float": return "FLOAT"; case "integer": return "INTEGER"; case "bigint": return "BIGINT"; case "jsonb": return "JSONB"; case "boolean": return "BOOLEAN"; default: return "TEXT"; } } function getDefaultValue(type) { switch (type) { case "text": case "uuid": return "DEFAULT ''"; case "timestamp": return "DEFAULT '1970-01-01 00:00:00'"; case "integer": case "bigint": case "float": return "DEFAULT 0"; case "jsonb": return "DEFAULT '{}'"; case "boolean": return "DEFAULT FALSE"; default: return "DEFAULT ''"; } } function ensureDate(date) { if (!date) return void 0; return date instanceof Date ? date : new Date(date); } function serializeDate(date) { if (!date) return void 0; const dateObj = ensureDate(date); return dateObj?.toISOString(); } function filterByDateRange(items, getCreatedAt, dateRange) { if (!dateRange) return items; let result = items; if (dateRange.start) { const startTime = ensureDate(dateRange.start).getTime(); result = result.filter((item) => { const itemTime = getCreatedAt(item).getTime(); return dateRange.startExclusive ? itemTime > startTime : itemTime >= startTime; }); } if (dateRange.end) { const endTime = ensureDate(dateRange.end).getTime(); result = result.filter((item) => { const itemTime = getCreatedAt(item).getTime(); return dateRange.endExclusive ? itemTime < endTime : itemTime <= endTime; }); } return result; } function jsonValueEquals(a, b) { if (a === void 0 || b === void 0) { return a === b; } if (a === null || b === null) { return a === b; } if (typeof a !== typeof b) { return false; } if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime(); } if (a instanceof Date || b instanceof Date) { return false; } if (typeof a === "object") { if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; return a.every((val, i) => jsonValueEquals(val, b[i])); } if (Array.isArray(a) || Array.isArray(b)) { return false; } const aKeys = Object.keys(a); const bKeys = Object.keys(b); if (aKeys.length !== bKeys.length) return false; return aKeys.every( (key) => jsonValueEquals(a[key], b[key]) ); } return a === b; } // src/storage/domains/observability/inmemory.ts var OBSERVABILITY_DELTA_POLLING_FEATURE = "observability-delta-polling"; var ObservabilityInMemory = class extends ObservabilityStorage { db; constructor({ db }) { super(); this.db = db; } getFeatures() { if (!this.deltaPollingFeatureEnabled()) { return void 0; } return ["delta-polling"]; } async dangerouslyClearAll() { this.db.traces.clear(); this.db.metricRecords.length = 0; this.db.logRecords.length = 0; this.db.scoreRecords.length = 0; this.db.feedbackRecords.length = 0; this.db.observabilityNextCursorId = 1; this.db.traceCursorIds.clear(); this.db.branchCursorIds.clear(); this.db.metricCursorIds.clear(); this.db.logCursorIds.clear(); this.db.scoreCursorIds.clear(); this.db.feedbackCursorIds.clear(); } deltaPollingFeatureEnabled() { return chunk6E6FPM76_cjs.coreFeatures.has(OBSERVABILITY_DELTA_POLLING_FEATURE); } assertDeltaPollingEnabled() { if (this.deltaPollingFeatureEnabled()) { return; } throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_DELTA_POLLING_NOT_SUPPORTED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "This storage provider does not support observability delta polling" }); } allocateObservabilityCursorId() { const cursorId = this.db.observabilityNextCursorId; this.db.observabilityNextCursorId += 1; return cursorId; } /** * Upserts a record into an append-only collection keyed by an id field. * * If an existing record with the same id is found, it is replaced in place * (preserving its cursor id so delta polling does not re-emit it). Otherwise * the record is appended and a fresh cursor id is allocated. */ upsertByIdField(records, cursorIds, record, idField) { const id = record[idField]; if (id == null) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_MISSING_RECORD_ID", domain: chunkXSOONORA_cjs.ErrorDomain.STORAGE, category: chunkXSOONORA_cjs.ErrorCategory.USER, text: `Observability record is missing required id field '${String(idField)}'` }); } const existingIndex = records.findIndex((existing) => existing[idField] === id); if (existingIndex !== -1) { const previous = records[existingIndex]; const cursorId = cursorIds.get(previous); cursorIds.delete(previous); records[existingIndex] = record; if (cursorId !== void 0) { cursorIds.set(record, cursorId); } return; } records.push(record); cursorIds.set(record, this.allocateObservabilityCursorId()); } encodeDeltaCursor(cursorId) { return (cursorId ?? 0).toString(); } decodeDeltaCursor(cursor) { if (!/^\d+$/.test(cursor)) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_INVALID_DELTA_CURSOR", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.USER, text: "Invalid observability delta cursor" }); } const cursorId = Number.parseInt(cursor, 10); if (!Number.isInteger(cursorId) || cursorId < 0) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_INVALID_DELTA_CURSOR", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.USER, text: "Invalid observability delta cursor" }); } return cursorId; } pageDeltaCursor(cursorId) { if (!this.deltaPollingFeatureEnabled()) { return {}; } return { deltaCursor: this.encodeDeltaCursor(cursorId) }; } maxMatchingCursorId(rows, cursorIds, matches) { let maxCursorId = null; for (const row of rows) { const cursorId = cursorIds.get(row); if (cursorId === void 0 || !matches(row)) { continue; } if (maxCursorId === null || cursorId > maxCursorId) { maxCursorId = cursorId; } } return maxCursorId; } createBranchCursorKey(traceId, spanId) { return `${traceId}\0${spanId}`; } maybeRegisterTraceCursor(traceEntry) { const rootSpan = traceEntry.rootSpan; if (!rootSpan) { return; } if (!this.db.traceCursorIds.has(rootSpan.traceId)) { this.db.traceCursorIds.set(rootSpan.traceId, this.allocateObservabilityCursorId()); } } maybeRegisterBranchCursor(span) { if (!chunkQYKNPGZG_cjs.BRANCH_SPAN_TYPE_SET.has(span.spanType)) { return; } const key = this.createBranchCursorKey(span.traceId, span.spanId); if (!this.db.branchCursorIds.has(key)) { this.db.branchCursorIds.set(key, this.allocateObservabilityCursorId()); } } buildDeltaResponse(rows, limit, fallbackCursorId) { const visibleRows = rows.slice(0, limit); const hasMore = rows.length > limit; return { rows: visibleRows.map((entry) => entry.row), delta: { limit, hasMore }, deltaCursor: visibleRows.length > 0 ? this.encodeDeltaCursor(visibleRows[visibleRows.length - 1].cursorId) : this.encodeDeltaCursor(fallbackCursorId) }; } listAppendOnlyDelta(rows, cursorIds, matches, after, limit) { const currentCursorId = this.maxMatchingCursorId(rows, cursorIds, matches); const streamCursorId = this.maxMatchingCursorId(rows, cursorIds, () => true); const fallbackCursorId = currentCursorId ?? streamCursorId; if (after === void 0) { return { rows: [], delta: { limit, hasMore: false }, deltaCursor: this.encodeDeltaCursor(fallbackCursorId) }; } const afterCursorId = this.decodeDeltaCursor(after); const matchingRows = rows.flatMap((row) => { const cursorId = cursorIds.get(row); if (cursorId === void 0 || cursorId <= afterCursorId || !matches(row)) { return []; } return [{ cursorId, row }]; }).sort((a, b) => a.cursorId - b.cursorId).slice(0, limit + 1); return this.buildDeltaResponse(matchingRows, limit, fallbackCursorId); } getTraceCursorId(traceId, filters) { const cursorId = this.db.traceCursorIds.get(traceId); const traceEntry = this.db.traces.get(traceId); if (cursorId === void 0 || !traceEntry?.rootSpan || !this.traceMatchesFilters(traceEntry, filters)) { return null; } return cursorId; } getMaxTraceCursorId(filters) { let maxCursorId = null; for (const traceId of this.db.traceCursorIds.keys()) { const cursorId = this.getTraceCursorId(traceId, filters); if (cursorId === null) { continue; } if (maxCursorId === null || cursorId > maxCursorId) { maxCursorId = cursorId; } } return maxCursorId; } getMaxTraceStreamCursorId() { let maxCursorId = null; for (const cursorId of this.db.traceCursorIds.values()) { if (maxCursorId === null || cursorId > maxCursorId) { maxCursorId = cursorId; } } return maxCursorId; } getBranchCursorId(key, filters) { const cursorId = this.db.branchCursorIds.get(key); if (cursorId === void 0) { return null; } const [traceId, spanId] = key.split("\0"); if (!traceId || !spanId) { return null; } const traceEntry = this.db.traces.get(traceId); const span = traceEntry?.spans[spanId]; if (!span || !this.spanMatchesBranchFilters(span, filters)) { return null; } return cursorId; } getMaxBranchCursorId(filters) { let maxCursorId = null; for (const key of this.db.branchCursorIds.keys()) { const cursorId = this.getBranchCursorId(key, filters); if (cursorId === null) { continue; } if (maxCursorId === null || cursorId > maxCursorId) { maxCursorId = cursorId; } } return maxCursorId; } getMaxBranchStreamCursorId() { let maxCursorId = null; for (const cursorId of this.db.branchCursorIds.values()) { if (maxCursorId === null || cursorId > maxCursorId) { maxCursorId = cursorId; } } return maxCursorId; } async createSpan(args) { const { span } = args; this.validateCreateSpan(span); const now = /* @__PURE__ */ new Date(); const record = { ...span, createdAt: now, updatedAt: now }; this.upsertSpanToTrace(record); } async batchCreateSpans(args) { const now = /* @__PURE__ */ new Date(); for (const span of args.records) { this.validateCreateSpan(span); const record = { ...span, createdAt: now, updatedAt: now }; this.upsertSpanToTrace(record); } } validateCreateSpan(record) { if (!record.spanId) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_SPAN_ID_REQUIRED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "Span ID is required for creating a span" }); } if (!record.traceId) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_TRACE_ID_REQUIRED", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "Trace ID is required for creating a span" }); } } /** * Inserts or updates a span in the trace and recomputes trace-level properties */ upsertSpanToTrace(span) { const { traceId, spanId } = span; let traceEntry = this.db.traces.get(traceId); if (!traceEntry) { traceEntry = { spans: {}, rootSpan: null, status: "running" /* RUNNING */, hasChildError: false }; this.db.traces.set(traceId, traceEntry); } traceEntry.spans[spanId] = span; if (span.parentSpanId == null) { traceEntry.rootSpan = span; } this.recomputeTraceProperties(traceEntry); this.maybeRegisterTraceCursor(traceEntry); this.maybeRegisterBranchCursor(span); } /** * Recomputes derived trace properties from all spans */ recomputeTraceProperties(traceEntry) { const spans = Object.values(traceEntry.spans); if (spans.length === 0) return; traceEntry.hasChildError = spans.some((s) => s.error != null); const rootSpan = traceEntry.rootSpan; if (rootSpan) { if (rootSpan.error != null) { traceEntry.status = "error" /* ERROR */; } else if (rootSpan.endedAt == null) { traceEntry.status = "running" /* RUNNING */; } else { traceEntry.status = "success" /* SUCCESS */; } } else { traceEntry.status = "running" /* RUNNING */; } } async getSpan(args) { const { traceId, spanId } = args; const traceEntry = this.db.traces.get(traceId); if (!traceEntry) { return null; } const span = traceEntry.spans[spanId]; if (!span) { return null; } return { span }; } async getSpans(args) { const { traceId, spanIds } = args; const traceEntry = this.db.traces.get(traceId); if (!traceEntry) { return { traceId, spans: [] }; } const spans = []; for (const spanId of spanIds) { const span = traceEntry.spans[spanId]; if (span) spans.push(span); } return { traceId, spans }; } async getRootSpan(args) { const { traceId } = args; const traceEntry = this.db.traces.get(traceId); if (!traceEntry || !traceEntry.rootSpan) { return null; } return { span: traceEntry.rootSpan }; } async getTrace(args) { const { traceId } = args; const traceEntry = this.db.traces.get(traceId); if (!traceEntry) { return null; } const spans = Object.values(traceEntry.spans); if (spans.length === 0) { return null; } spans.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime()); return { traceId, spans }; } async getTraceLight(args) { const { traceId } = args; const traceEntry = this.db.traces.get(traceId); if (!traceEntry) { return null; } const spans = Object.values(traceEntry.spans); if (spans.length === 0) { return null; } spans.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime()); return { traceId, spans: spans.map( (span) => ({ traceId: span.traceId, spanId: span.spanId, parentSpanId: span.parentSpanId, name: span.name, spanType: span.spanType, isEvent: span.isEvent, startedAt: span.startedAt, endedAt: span.endedAt, error: span.error, entityType: span.entityType, entityId: span.entityId, entityName: span.entityName, createdAt: span.createdAt, updatedAt: span.updatedAt }) ) }; } getMatchingRootSpans(args) { const { filters, pagination, orderBy } = chunkQYKNPGZG_cjs.listTracesArgsSchema.parse(args); const matchingRootSpans = []; for (const [, traceEntry] of this.db.traces) { if (!traceEntry.rootSpan) continue; if (this.traceMatchesFilters(traceEntry, filters)) { matchingRootSpans.push(traceEntry.rootSpan); } } const { field: sortField, direction: sortDirection } = orderBy; matchingRootSpans.sort((a, b) => { if (sortField === "endedAt") { const aVal = a.endedAt; const bVal = b.endedAt; if (aVal == null && bVal == null) return 0; if (aVal == null) return sortDirection === "DESC" ? -1 : 1; if (bVal == null) return sortDirection === "DESC" ? 1 : -1; const diff = aVal.getTime() - bVal.getTime(); return sortDirection === "DESC" ? -diff : diff; } else { const diff = a.startedAt.getTime() - b.startedAt.getTime(); return sortDirection === "DESC" ? -diff : diff; } }); const total = matchingRootSpans.length; const { page, perPage } = pagination; const start = page * perPage; const end = start + perPage; const paged = matchingRootSpans.slice(start, end); return { paged, total, page, perPage, hasMore: end < total }; } async listTraces(args) { const { mode, filters, after, limit } = chunkQYKNPGZG_cjs.listTracesArgsSchema.parse(args); if (mode === "delta") { this.assertDeltaPollingEnabled(); const currentCursorId = this.getMaxTraceCursorId(filters); const fallbackCursorId = currentCursorId ?? this.getMaxTraceStreamCursorId(); if (after === void 0) { return { spans: [], delta: { limit, hasMore: false }, deltaCursor: this.encodeDeltaCursor(fallbackCursorId) }; } const afterCursorId = this.decodeDeltaCursor(after); const matchingRootSpans = Array.from(this.db.traceCursorIds.entries()).flatMap(([traceId, cursorId]) => { if (cursorId <= afterCursorId) { return []; } const traceEntry = this.db.traces.get(traceId); if (!traceEntry?.rootSpan || !this.traceMatchesFilters(traceEntry, filters)) { return []; } return [{ cursorId, row: traceEntry.rootSpan }]; }).sort((a, b) => a.cursorId - b.cursorId).slice(0, limit + 1); const deltaResponse = this.buildDeltaResponse(matchingRootSpans, limit, fallbackCursorId); return { spans: chunkQYKNPGZG_cjs.toTraceSpans(deltaResponse.rows), delta: deltaResponse.delta, deltaCursor: deltaResponse.deltaCursor }; } const { paged, total, page, perPage, hasMore } = this.getMatchingRootSpans(args); return { spans: chunkQYKNPGZG_cjs.toTraceSpans(paged), pagination: { total, page, perPage, hasMore }, ...this.pageDeltaCursor(this.getMaxTraceCursorId(filters) ?? this.getMaxTraceStreamCursorId()) }; } async listTracesLight(args) { const { paged, total, page, perPage, hasMore } = this.getMatchingRootSpans(args); return { spans: paged.map((span) => ({ traceId: span.traceId, spanId: span.spanId, parentSpanId: span.parentSpanId, name: span.name, spanType: span.spanType, isEvent: span.isEvent, startedAt: span.startedAt, endedAt: span.endedAt, error: span.error, entityType: span.entityType, entityId: span.entityId, entityName: span.entityName, createdAt: span.createdAt, updatedAt: span.updatedAt })), pagination: { total, page, perPage, hasMore } }; } /** * Check if a trace matches all provided filters */ traceMatchesFilters(traceEntry, filters) { if (!filters) return true; const rootSpan = traceEntry.rootSpan; if (!rootSpan) return false; if (filters.startedAt) { if (filters.startedAt.start && rootSpan.startedAt < filters.startedAt.start) { return false; } if (filters.startedAt.end && rootSpan.startedAt > filters.startedAt.end) { return false; } } if (filters.endedAt) { if (rootSpan.endedAt == null) { return false; } if (filters.endedAt.start && rootSpan.endedAt < filters.endedAt.start) { return false; } if (filters.endedAt.end && rootSpan.endedAt > filters.endedAt.end) { return false; } } if (filters.spanType !== void 0 && rootSpan.spanType !== filters.spanType) { return false; } if (filters.entityType !== void 0 && rootSpan.entityType !== filters.entityType) { return false; } if (filters.entityId !== void 0 && rootSpan.entityId !== filters.entityId) { return false; } if (filters.entityName !== void 0 && rootSpan.entityName !== filters.entityName) { return false; } if (filters.entityVersionId !== void 0 && rootSpan.entityVersionId !== filters.entityVersionId) { return false; } if (filters.experimentId !== void 0 && rootSpan.experimentId !== filters.experimentId) { return false; } if (filters.userId !== void 0 && rootSpan.userId !== filters.userId) { return false; } if (filters.organizationId !== void 0 && rootSpan.organizationId !== filters.organizationId) { return false; } if (filters.resourceId !== void 0 && rootSpan.resourceId !== filters.resourceId) { return false; } if (filters.runId !== void 0 && rootSpan.runId !== filters.runId) { return false; } if (filters.sessionId !== void 0 && rootSpan.sessionId !== filters.sessionId) { return false; } if (filters.threadId !== void 0 && rootSpan.threadId !== filters.threadId) { return false; } if (filters.requestId !== void 0 && rootSpan.requestId !== filters.requestId) { return false; } if (filters.environment !== void 0 && rootSpan.environment !== filters.environment) { return false; } if (filters.source !== void 0 && rootSpan.source !== filters.source) { return false; } if (filters.serviceName !== void 0 && rootSpan.serviceName !== filters.serviceName) { return false; } if (filters.scope != null && rootSpan.scope != null) { for (const [key, value] of Object.entries(filters.scope)) { if (!jsonValueEquals(rootSpan.scope[key], value)) { return false; } } } else if (filters.scope != null && rootSpan.scope == null) { return false; } if (filters.metadata != null && rootSpan.metadata != null) { for (const [key, value] of Object.entries(filters.metadata)) { if (!jsonValueEquals(rootSpan.metadata[key], value)) { return false; } } } else if (filters.metadata != null && rootSpan.metadata == null) { return false; } if (filters.tags != null && filters.tags.length > 0) { if (rootSpan.tags == null) { return false; } for (const tag of filters.tags) { if (!rootSpan.tags.includes(tag)) { return false; } } } if (filters.status !== void 0 && traceEntry.status !== filters.status) { return false; } if (filters.hasChildError !== void 0 && traceEntry.hasChildError !== filters.hasChildError) { return false; } return true; } async listBranches(args) { const { mode, filters, pagination, orderBy, after, limit } = chunkQYKNPGZG_cjs.listBranchesArgsSchema.parse(args); if (mode === "delta") { this.assertDeltaPollingEnabled(); const currentCursorId = this.getMaxBranchCursorId(filters); const fallbackCursorId = currentCursorId ?? this.getMaxBranchStreamCursorId(); if (after === void 0) { return { branches: [], delta: { limit, hasMore: false }, deltaCursor: this.encodeDeltaCursor(fallbackCursorId) }; } const afterCursorId = this.decodeDeltaCursor(after); const matches2 = Array.from(this.db.branchCursorIds.entries()).flatMap(([key, cursorId]) => { if (cursorId <= afterCursorId) { return []; } const [traceId, spanId] = key.split("\0"); if (!traceId || !spanId) { return []; } const traceEntry = this.db.traces.get(traceId); const span = traceEntry?.spans[spanId]; if (!span || !this.spanMatchesBranchFilters(span, filters)) { return []; } return [{ cursorId, row: span }]; }).sort((a, b) => a.cursorId - b.cursorId).slice(0, limit + 1); const deltaResponse = this.buildDeltaResponse(matches2, limit, fallbackCursorId); return { branches: deltaResponse.rows.map(chunkQYKNPGZG_cjs.toTraceSpan), delta: deltaResponse.delta, deltaCursor: deltaResponse.deltaCursor }; } const allowedSpanTypes = filters?.spanType ? chunkQYKNPGZG_cjs.BRANCH_SPAN_TYPE_SET.has(filters.spanType) ? /* @__PURE__ */ new Set([filters.spanType]) : /* @__PURE__ */ new Set() : chunkQYKNPGZG_cjs.BRANCH_SPAN_TYPE_SET; const matches = []; for (const [, traceEntry] of this.db.traces) { for (const span of Object.values(traceEntry.spans)) { if (!allowedSpanTypes.has(span.spanType)) continue; if (!this.spanMatchesBranchFilters(span, filters)) continue; matches.push(span); } } const { field: sortField, direction: sortDirection } = orderBy; matches.sort((a, b) => { if (sortField === "endedAt") { const aVal = a.endedAt; const bVal = b.endedAt; if (aVal == null && bVal == null) return 0; if (aVal == null) return sortDirection === "DESC" ? -1 : 1; if (bVal == null) return sortDirection === "DESC" ? 1 : -1; const diff2 = aVal.getTime() - bVal.getTime(); return sortDirection === "DESC" ? -diff2 : diff2; } const diff = a.startedAt.getTime() - b.startedAt.getTime(); return sortDirection === "DESC" ? -diff : diff; }); const total = matches.length; const { page, perPage } = pagination; const start = page * perPage; const end = start + perPage; const paged = matches.slice(start, end); return { pagination: { total, page, perPage, hasMore: end < total }, branches: paged.map(chunkQYKNPGZG_cjs.toTraceSpan), ...this.pageDeltaCursor(this.getMaxBranchCursorId(filters) ?? this.getMaxBranchStreamCursorId()) }; } /** * Check if a single anchor span matches all provided branch filters. All * predicates apply to the span itself (not the trace root) -- this is the * key difference from {@link traceMatchesFilters}. */ spanMatchesBranchFilters(span, filters) { if (!filters) return true; if (filters.startedAt) { if (filters.startedAt.start && span.startedAt < filters.startedAt.start) return false; if (filters.startedAt.end && span.startedAt > filters.startedAt.end) return false; } if (filters.endedAt) { if (span.endedAt == null) return false; if (filters.endedAt.start && span.endedAt < filters.endedAt.start) return false; if (filters.endedAt.end && span.endedAt > filters.endedAt.end) return false; } if (filters.traceId !== void 0 && span.traceId !== filters.traceId) return false; if (filters.entityType !== void 0 && span.entityType !== filters.entityType) return false; if (filters.entityId !== void 0 && span.entityId !== filters.entityId) return false; if (filters.entityName !== void 0 && span.entityName !== filters.entityName) return false; if (filters.entityVersionId !== void 0 && span.entityVersionId !== filters.entityVersionId) return false; if (filters.parentEntityType !== void 0 && span.parentEntityType !== filters.parentEntityType) return false; if (filters.parentEntityId !== void 0 && span.parentEntityId !== filters.parentEntityId) return false; if (filters.parentEntityName !== void 0 && span.parentEntityName !== filters.parentEntityName) return false; if (filters.parentEntityVersionId !== void 0 && span.parentEntityVersionId !== filters.parentEntityVersionId) return false; if (filters.rootEntityType !== void 0 && span.rootEntityType !== filters.rootEntityType) return false; if (filters.rootEntityId !== void 0 && span.rootEntityId !== filters.rootEntityId) return false; if (filters.rootEntityName !== void 0 && span.rootEntityName !== filters.rootEntityName) return false; if (filters.rootEntityVersionId !== void 0 && span.rootEntityVersionId !== filters.rootEntityVersionId) return false; if (filters.experimentId !== void 0 && span.experimentId !== filters.experimentId) return false; if (filters.userId !== void 0 && span.userId !== filters.userId) return false; if (filters.organizationId !== void 0 && span.organizationId !== filters.organizationId) return false; if (filters.resourceId !== void 0 && span.resourceId !== filters.resourceId) return false; if (filters.runId !== void 0 && span.runId !== filters.runId) return false; if (filters.sessionId !== void 0 && span.sessionId !== filters.sessionId) return false; if (filters.threadId !== void 0 && span.threadId !== filters.threadId) return false; if (filters.requestId !== void 0 && span.requestId !== filters.requestId) return false; if (filters.environment !== void 0 && span.environment !== filters.environment) return false; if (filters.source !== void 0 && span.source !== filters.source) return false; if (filters.serviceName !== void 0 && span.serviceName !== filters.serviceName) return false; if (filters.scope != null && span.scope != null) { for (const [key, value] of Object.entries(filters.scope)) { if (!jsonValueEquals(span.scope[key], value)) return false; } } else if (filters.scope != null && span.scope == null) { return false; } if (filters.metadata != null && span.metadata != null) { for (const [key, value] of Object.entries(filters.metadata)) { if (!jsonValueEquals(span.metadata[key], value)) return false; } } else if (filters.metadata != null && span.metadata == null) { return false; } if (filters.tags != null && filters.tags.length > 0) { if (span.tags == null) return false; for (const tag of filters.tags) { if (!span.tags.includes(tag)) return false; } } if (filters.status !== void 0) { const spanStatus = chunkQYKNPGZG_cjs.toTraceSpan(span).status; if (spanStatus !== filters.status) return false; } return true; } async updateSpan(args) { const { traceId, spanId, updates } = args; const traceEntry = this.db.traces.get(traceId); if (!traceEntry) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_UPDATE_SPAN_NOT_FOUND", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "Trace not found for span update" }); } const span = traceEntry.spans[spanId]; if (!span) { throw new chunkXSOONORA_cjs.MastraError({ id: "OBSERVABILITY_UPDATE_SPAN_NOT_FOUND", domain: chunkXSOONORA_cjs.ErrorDomain.MASTRA_OBSERVABILITY, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: "Span not found for update" }); } const updatedSpan = { ...span, ...updates, updatedAt: /* @__PURE__ */ new Date() }; traceEntry.spans[spanId] = updatedSpan; if (updatedSpan.parentSpanId == null) { traceEntry.rootSpan = updatedSpan; } this.recomputeTraceProperties(traceEntry); this.maybeRegisterTraceCursor(traceEntry); this.maybeRegisterBranchCursor(updatedSpan); } async batchUpdateSpans(args) { for (const record of args.records) { await this.updateSpan(record); } } async batchDeleteTraces(args) { for (const traceId of args.traceIds) { const traceEntry = this.db.traces.get(traceId); if (traceEntry) { this.db.traceCursorIds.delete(traceId); for (const spanId of Object.keys(traceEntry.spans)) { this.db.branchCursorIds.delete(this.createBranchCursorKey(traceId, spanId)); } } this.db.traces.delete(traceId); } } // ============================================================================ // Metrics // ============================================================================ async batchCreateMetrics(args) { for (const metric of args.metrics) { const record = metric; this.upsertByIdField(this.db.metricRecords, this.db.metricCursorIds, record, "metricId"); } } async listMetrics(args) { const { mode, filters, pagination, orderBy, after, limit } = chunkLP4WZA6D_cjs.listMetricsArgsSchema.parse(args); if (mode === "delta") { this.assertDeltaPollingEnabled(); const deltaResponse = this.listAppendOnlyDelta( this.db.metricRecords, this.db.metricCursorIds, (metric) => this.metricMatchesFilters(metric, filters), after, limit ); return { metrics: deltaResponse.rows, delta: deltaResponse.delta, deltaCursor: deltaResponse.deltaCursor }; } let matching = this.filterMetrics(filters); const dir = orderBy.direction === "DESC" ? -1 : 1; matching.sort((a, b) => dir * (a.timestamp.getTime() - b.timestamp.getTime())); const total = matching.length; const page = Number(pagination.page); const perPage = Number(pagination.perPage); const start = page * perPage; return { metrics: matching.slice(start, start + perPage), pagination: { total, page, perPage, hasMore: start + perPage < total }, ...this.pageDeltaCursor( this.maxMatchingCursorId( this.db.metricRecords, this.db.metricCursorIds, (metric) => this.metricMatchesFilters(metric, filters) ) ) }; } filterMetrics(filters) { if (!filters) return [...this.db.metricRecords]; return this.db.metricRecords.filter((metric) => this.metricMatchesFilters(metric, filters)); } metricMatchesFilters(m, filters) { if (!filters) return true; if (filters.timestamp) { const ts = filters.timestamp; if (ts.start && (ts.startExclusive ? m.timestamp <= ts.start : m.timestamp < ts.start)) return false; if (ts.end && (ts.endExclusive ? m.timestamp >= ts.end : m.timestamp > ts.end)) return false; } if (filters.name != null) { if (!filters.name.includes(m.name)) return false; } if (filters.traceId !== void 0 && m.traceId !== filters.traceId) return false; if (filters.spanId !== void 0 && m.spanId !== filters.spanId) return false; if (filters.provider !== void 0 && m.provider !== filters.provider) return false; if (filters.model !== void 0 && m.model !== filters.model) return false; if (filters.costUnit !== void 0 && m.costUnit !== filters.costUnit) return false; if (filters.entityType !== void 0 && m.entityType !== filters.entityType) return false; if (filters.entityName !== void 0 && m.entityName !== filters.entityName) return false; if (filters.entityVersionId !== void 0 && m.entityVersionId !== filters.entityVersionId) return false; if (filters.parentEntityVersionId !== void 0 && m.parentEntityVersionId !== filters.parentEntityVersionId) return false; if (filters.rootEntityVersionId !== void 0 && m.rootEntityVersionId !== filters.rootEntityVersionId) return false; if (filters.userId !== void 0 && m.userId !== filters.userId) return false; if (filters.organizationId !== void 0 && m.organizationId !== filters.organizationId) return false; if (filters.resourceId !== void 0 && m.resourceId !== filters.resourceId) return false; if (filters.runId !== void 0 && m.runId !== filters.runId) return false; if (filters.sessionId !== void 0 && m.sessionId !== filters.sessionId) return false; if (filters.threadId !== void 0 && m.threadId !== filters.threadId) return false; if (filters.requestId !== void 0 && m.requestId !== filters.requestId) return false; if (filters.experimentId !== void 0 && m.experimentId !== filters.experimentId) return false; if (filters.serviceName !== void 0 && m.serviceName !== filters.serviceName) return false; if (filters.environment !== void 0 && m.environment !== filters.environment) return false; const metricExecutionSource = m.executionSource ?? m.source ?? null; if (filters.executionSource !== void 0 && metricExecutionSource !== filters.executionSource) return false; if (filters.source !== void 0 && metricExecutionSource !== filters.source) return false; if (filters.parentEntityType !== void 0 && m.parentEntityType !== filters.parentEntityType) return false; if (filters.parentEntityName !== void 0 && m.parentEntityName !== filters.parentEntityName) return false; if (filters.rootEntityType !== void 0 && m.rootEntityType !== filters.rootEntityType) return false; if (filters.rootEntityName !== void 0 && m.rootEntityName !== filters.rootEntityName) return false; if (filters.tags != null && Array.isArray(filters.tags) && filters.tags.length > 0) { if (m.tags == null) return false; for (const tag of filters.tags) { if (!m.tags.includes(tag)) return false; } } if (filters.labels) { const labelFilters = filters.labels; for (const [k, v] of Object.entries(labelFilters)) { if (m.labels[k] !== v) return false; } } return true; } aggregate(values, type, timestamps, distinctValues) { if (type === "count_distinct") { if (!distinctValues) return 0; const set = /* @__PURE__ */ new Set(); for (const v of distinctValues) { if (v === null || v === void 0) continue; set.add(v); } return set.size; } if (values.length === 0) return null; switch (type) { case "sum": return values.reduce((a, b) => a + b, 0); case "avg": return values.reduce((a, b) => a + b, 0) / values.length; case "min": return Math.min(...values); case "max": return Math.max(...values); case "count": return values.length; case "last": { if (!timestamps || timestamps.length !== values.length) { return values[values.length - 1]; } let latestIndex = 0; let latestTimestamp = timestamps[0]; for (let i = 1; i < timestamps.length; i++) { const timestamp = timestamps[i]; if (timestamp >= latestTimestamp) { latestTimestamp = timestamp; latestIndex = i; } } return values[latestIndex]; } default: return values.reduce((a, b) => a + b, 0); } } extractDistinctValues(records, distinctColumn) { if (!distinctColumn) return void 0; return records.map((r) => { const raw = r[distinctColumn]; if (raw === null || raw === void 0) return null; if (typeof raw === "string" || typeof raw === "number") return raw; return String(raw); }); } interpolatePercentile(sortedValues, percentile) { if (sortedValues.length === 0) return 0; const position = percentile * (sortedValues.length - 1); const lowerIndex = Math.floor(position); const upperIndex = Math.ceil(position); const lowerValue = sortedValues[lowerIndex]; const upperValue = sortedValues[upperIndex]; if (lowerIndex === upperIndex) { return lowerValue; } return lowerValue + (upperValue - lowerValue) * (position - lowerIndex); } /** * Cost is returned alongside value-based OLAP results so callers can derive * token and monetary views from the same filtered scan. */ summarizeCost(records) { const costValues = records.map((record) => record.estimatedCost).filter((value) => typeof value === "number" && Number.isFinite(value)); const costUnits = new Set( records.map((record) => record.costUnit).filter((unit) => typeof unit === "string") ); return { estimatedCost: costValues.length > 0 ? costValues.reduce((sum, value) => sum + value, 0) : null, costUnit: costUnits.size === 1 ? Array.from(costUnits)[0] : null }; } async getMetricAggregate(args) { const names = Array.isArray(args.name) ? args.name : [args.name]; const filtered = this.filterMetrics(args.filters).filter((m) => names.includes(m.name)); const value = this.aggregate( filtered.map((m) => m.value), args.aggregation, void 0, this.extractDistinctValues(filtered, args.distinctColumn) ); const costSummary = this.summarizeCost(filtered); if (args.comparePeriod && args.filters?.timestamp) { const ts = args.filters.timestamp; if (ts.start && ts.end) { const duration = ts.end.getTime() - ts.start.getTime(); let prevStart; let prevEnd; switch (args.comparePeriod) { case "previous_period": prevStart = new Date(ts.start.getTime() - duration); prevEnd = new Date(ts.end.getTime() - duration); break; case "previous_day": prevStart = new Date(ts.start.getTime() - 864e5); prevEnd = new Date(ts.end.getTime() - 864e5); break; case "previous_week": prevStart = new Date(ts.start.getTime() - 6048e5); prevEnd = new Date(ts.end.getTime() - 6048e5); break; } const prevFiltered = this.filterMetrics({ ...args.filters, timestamp: { ...ts, start: prevStart, end: prevEnd } }).filter((m) => names.includes(m.name)); const previousValue = this.aggregate( prevFiltered.map((m) => m.value), args.aggregation, void 0, this.extractDistinctValues(prevFiltered, args.distinctColumn) ); const previousCostSummary = this.summarizeCost(prevFiltered); let changePercent = null; if (previousValue !== null && previousValue !== 0 && value !== null) { changePercent = (value - previousValue) / Math.abs(previousValue) * 100; } let costChangePercent = null; if (previousCostSummary.estimatedCost !== null && previousCostSummary.estimatedCost !== 0 && costSummary.estimatedCost !== null) { costChangePercent = (costSummary.estimatedCost - previousCostSummary.estimatedCost) / Math.abs(previousCostSummary.estimatedCost) * 100; } return { value, estimatedCost: costSummary.estimatedCost, costUnit: costSummary.costUnit, previousValue, previousEstimatedCost: previousCostSummary.estimatedCost, changePercent, costChangePercent }; } } return { value, estimatedCost: costSummary.estimatedCost, costUnit: costSummary.costUnit }; } async getMetricBreakdown(args) { const names = Array.isArray(args.name) ? args.name : [args.name]; const filtered = this.filterMetrics(args.filters).filter((m) => names.includes(m.name)); const groupMap = /* @__PURE__ */ new Map(); for (const m of filtered) { const dims = {}; for (const col of args.groupBy) { dims[col] = m[col] ?? m.labels[col] ?? null; } const key = JSON.stringify(dims); if (!groupMap.has(key)) groupMap.set(key, []); groupMap.get(key).push(m); } const groups = Array.from(groupMap.entries()).map(([key, records]) => { const costSummary = this.summarizeCost(records); return { dimensions: JSON.parse(key), value: this.aggregate( records.map((record) => record.value), args.aggregation, void 0, this.extractDistinctValues(records, args.distinctColumn) ) ?? 0, estimatedCost: costSummary.estimatedCost, costUnit: costSummary.costUnit }; }); const direction = args.orderDirection === "ASC" ? 1 : -1; groups.sort((a, b) => (a.value - b.value) * direction); const limited = typeof args.limit === "number" ? groups.slice(0, args.limit) : groups; return { groups: limited }; } async getMetricTimeSeries(args) { const names = Array.isArray(args.name) ? args.name : [args.name]; const filtered = this.filterMetrics(args.filters).filter((m) => names.includes(m.name)); const intervalMs = this.intervalToMs(args.interval); if (args.groupBy && args.groupBy.length > 0) { const seriesMap = /* @__PURE__ */ new Map(); for (const m of filtered) { const values = args.groupBy.map((col) => String(m[col] ?? m.labels[col] ?? "")); const key = JSON.stringify(values); const displayName = values.join("|"); let entry = seriesMap.get(key); if (!entry) { entry = { displayName, buckets: /* @__PURE__ */ new Map() }; seriesMap.set(key, entry); } const bucket = Math.floor(m.timestamp.getTime() / intervalMs) * intervalMs; if (!entry.buckets.has(bucket)) entry.buckets.set(bucket, []); entry.buckets.get(bucket).push(m); } return { series: Array.from(seriesMap.values()).map(({ displayName, buckets }) => { const seriesRecords = Array.from(buckets.values()).flat(); const costSummary2 = this.summarizeCost(seriesRecords); return { name: displayName, costUnit: costSummary2.costUnit, points: Array.from(buckets.entries()).sort(([a], [b]) => a - b).map(([ts, records]) => ({ timestamp: new Date(ts), value: this.aggregate( records.map((record) => record.value), args.aggregation, void 0, this.extractDistinctValues(records, args.distinctColumn) ) ?? 0, estimatedCost: this.summarizeCost(records).estimatedCost })) }; }) }; } const bucketMap = /* @__PURE__ */ new Map(); for (const m of filtered) { const bucket = Math.floor(m.timestamp.getTime() / intervalMs) * intervalMs; if (!bucketMap.has(bucket)) bucketMap.set(bucket, []); bucketMap.get(bucket).push(m); } const metricName = Array.isArray(args.name) ? args.name.join(",") : args.name; const costSummary = this.summarizeCost(filtered); return { series: [ { name: metricName, costUnit: costSummary.costUnit, points: Array.from(bucketMap.entries()).sort(([a], [b]) => a - b).map(([ts, records]) => ({ timestamp: new Date(ts), value: this.aggregate( records.map((record) => record.value), args.aggregation, void 0, this.extractDistinctValues(records, args.distinctColumn) ) ?? 0, estimatedCost: this.summarizeCost(records).estimatedCost })) } ] }; } async getMetricPercentiles(args) { const filtered = this.filterMetrics(args.filters).filter((m) => m.name === args.name); const intervalMs = this.intervalToMs(args.interval); const bucketMap = /* @__PURE__ */ new Map(); for (const m of filtered) { const bucket = Math.floor(m.timestamp.getTime() / intervalMs) * intervalMs; if (!bucketMap.has(bucket)) bucketMap.set(bucket, []); bucketMap.get(bucket).push(m.value); } const sortedBuckets = Array.from(bucketMap.entries()).sort(([a], [b]) => a - b); return { series: args.percentiles.map((p) => ({ percentile: p, points: sortedBuckets.map(([ts, values]) => { const sorted = [...values].sort((a, b) => a - b); const idx = Math.min(Math.floor(p * sorted.length), sorted.length - 1); return { timestamp: new Date(ts), value: sorted[idx] ?? 0 }; }) })) }; } intervalToMs(interval) { switch (interval) { case "1m": return 6e4; case "5m": return 3e5; case "15m": return 9e5; case "1h": return 36e5; case "1d": return 864e5; default: return 36e5; } } // ============================================================================ // Discovery / Metadata Methods // ============================================================================ async getMetricNames(args) { const nameSet = /* @__PURE__ */ new Set(); for (const m of this.db.metricRecords) { if (args.prefix && !m.name.startsWith(args.prefix)) continue; nameSet.add(m.name); } let names = Array.from(nameSet).sort(); if (args.limit) names = names.slice(0, args.limit); return { names }; } async getMetricLabelKeys(args) { const keySet = /* @__PURE__ */ new Set(); for (const m of this.db.metricRecords) { if (m.name !== args.metricName) continue; for (const key of Object.keys(m.labels)) { keySet.add(key); } } return { keys: Array.from(keySet).sort() }; } async getMetricLabelValues(args) { const valueSet = /* @__PURE__ */ new Set(); for (const m of this.db.metricRecords) { if (m.name !== args.metricName) continue; const val = m.labels[args.labelKey]; if (val === void 0) continue; if (args.prefix && !val.startsWith(args.prefix)) continue; valueSet.add(val); } let values = Array.from(valueSet).sort(); if (args.limit) values = values.slice(0, args.limit); return { values }; } /** * Iterates every record across spans, logs, and metrics with shared * context fields. Discovery operations need to surface entities and * dimensions emitted on any observability surface, not just spans. */ *iterateObservabilityContextRecords() { for (const [, traceEntry] of this.db.traces) { for (const span of Object.values(traceEntry.spans)) { yield span; } } for (const log of this.db.logRecords) { yield log; } for (const metric of this.db.metricRecords) { yield metric; } } async getEntityTypes(_args) { const validTypes = new Set(Object.values(chunkLP4WZA6D_cjs.EntityType)); const typeSet = /* @__PURE__ */ new Set(); for (const record of this.iterateObservabilityContextRecords()) { if (record.entityType && validTypes.has(record.entityType)) { typeSet.add(record.entityType); } } return { entityTypes: Array.from(typeSet).sort() }; } async getEntityNames(args) { const nameSet = /* @__PURE__ */ new Set(); for (const record of this.iterateObservabilityContextRecords()) { if (!record.entityName) continue; if (args.entityType && record.entityType !== args.entityType) continue; nameSet.add(record.entityName); } return { names: Array.from(nameSet).sort() }; } async getServiceNames(_args) { const nameSet = /* @__PURE__ */ new Set(); for (const record of this.iterateObservabilityContextRecords()) { if (record.serviceName) nameSet.add(record.serviceName); } return { serviceNames: Array.from(nameSet).sort() }; } async getEnvironments(_args) { const envSet = /* @__PURE__ */ new Set(); for (const record of this.iterateObservabilityContextRecords()) { if (record.environment) envSet.add(record.environment); } return { environments: Array.from(envSet).sort() }; } async getTags(args) { const tagSet = /* @__PURE__ */ new Set(); for (const record of this.iterateObservabilityContextRecords()) { if (!record.tags) continue; if (args.entityType && record.entityType !== args.entityType) continue; for (const tag of record.tags) { tagSet.add(tag); } } return { tags: Array.from(tagSet).sort() }; } // ============================================================================ // Logs // ============================================================================ async batchCreateLogs(args) { for (const log of args.logs) { const record = log; this.upsertByIdField(this.db.logRecords, this.db.logCursorIds, record, "logId"); } } async listLogs(args) { const { mode, filters, pagination, orderBy, after, limit } = chunkLP4WZA6D_cjs.listLogsArgsSchema.parse(args); if (mode === "delta") { this.assertDeltaPollingEnabled(); const deltaResponse = this.listAppendOnlyDelta( this.db.logRecords, this.db.logCursorIds, (log) => this.logMatchesFilters(log, filters), after, limit ); return { logs: deltaResponse.rows, delta: deltaResponse.delta, deltaCursor: deltaResponse.deltaCursor }; } let matching = this.db.logRecords.filter((log) => this.logMatchesFilters(log, filters)); const dir = orderBy.direction === "DESC" ? -1 : 1; matching.sort((a, b) => dir * (a.timestamp.getTime() - b.timestamp.getTime())); const total = matching.length; const page = Number(pagination.page); const perPage = Number(pagination.perPage); const start = page * perPage; return { logs: matching.slice(start, start + perPage), pagination: { total, page, perPage, hasMore: start + perPage < total }, ...this.pageDeltaCursor( this.maxMatchingCursorId(this.db.logRecords, this.db.logCursorIds, (log) => this.logMatchesFilters(log, filters)) ) }; } logMatchesFilters(log, filters) { if (!filters) return true; if (filters.timestamp) { if (filters.timestamp.start && (filters.timestamp.startExclusive ? log.timestamp <= filters.timestamp.start : log.timestamp < filters.timestamp.start)) { return false; } if (filters.timestamp.end && (filters.timestamp.endExclusive ? log.timestamp >= filters.timestamp.end : log.timestamp > filters.timestamp.end)) { return false; } } if (filters.level !== void 0) { const levels = Array.isArray(filters.level) ? filters.level : [filters.level]; if (!levels.includes(log.level)) return false; } if (filters.traceId !== void 0 && log.traceId !== filters.traceId) return false; if (filters.spanId !== void 0 && log.spanId !== filters.spanId) return false; if (filters.entityType !== void 0 && log.entityType !== filters.entityType) return false; if (filters.entityName !== void 0 && log.entityName !== filters.entityName) return false; if (filters.entityVersionId !== void 0 && log.entityVersionId !== filters.entityVersionId) return false; if (filters.parentEntityVersionId !== void 0 && log.parentEntityVersionId !== filters.parentEntityVersionId) return false; if (filters.rootEntityVersionId !== void 0 && log.rootEntityVersionId !== filters.rootEntityVersionId) return false; if (filters.userId !== void 0 && log.userId !== filters.userId) return false; if (filters.organizationId !== void 0 && log.organizationId !== filters.organizationId) return false; if (filters.resourceId !== void 0 && log.resourceId !== filters.resourceId) return false; if (filters.runId !== void 0 && log.runId !== filters.runId) return false; if (filters.sessionId !== void 0 && log.sessionId !== filters.sessionId) return false; if (filters.threadId !== void 0 && log.threadId !== filters.threadId) return false; if (filters.requestId !== void 0 && log.requestId !== filters.requestId) return false; if (filters.parentEntityType !== void 0 && log.parentEntityType !== filters.parentEntityType) return false; if (filters.parentEntityName !== void 0 && log.parentEntityName !== filters.parentEntityName) return false; if (filters.rootEntityType !== void 0 && log.rootEntityType !== filters.rootEntityType) return false; if (filters.rootEntityName !== void 0 && log.rootEntityName !== filters.rootEntityName) return false; if (filters.serviceName !== void 0 && log.serviceName !== filters.serviceName) return false; if (filters.environment !== void 0 && log.environment !== filters.environment) return false; const logExecutionSource = log.executionSource ?? log.source ?? null; if (filters.executionSource !== void 0 && logExecutionSource !== filters.executionSource) return false; if (filters.source !== void 0 && logExecutionSource !== filters.source) return false; if (filters.experimentId !== void 0 && log.experimentId !== filters.experimentId) return false; if (filters.tags != null && filters.tags.length > 0) { if (log.tags == null) return false; for (const tag of filters.tags) { if (!log.tags.includes(tag)) return false; } } return true; } // ============================================================================ // Scores // ============================================================================ async createScore(args) { const scoreSource = args.score.scoreSource ?? args.score.source ?? null; const record = { ...args.score, scoreSource, source: scoreSource }; this.upsertByIdField(this.db.scoreRecords, this.db.scoreCursorIds, record, "scoreId"); } async batchCreateScores(args) { for (const score of args.scores) { const scoreSource = score.scoreSource ?? score.source ?? null; const record = { ...score, scoreSource, source: scoreSource }; this.upsertByIdField(this.db.scoreRecords, this.db.scoreCursorIds, record, "scoreId"); } } async listScores(args) { const { mode, filters, pagination, orderBy, after, limit } = chunkLP4WZA6D_cjs.listScoresArgsSchema.parse(args); if (mode === "delta") { this.assertDeltaPollingEnabled(); const deltaResponse = this.listAppendOnlyDelta( this.db.scoreRecords, this.db.scoreCursorIds, (score) => this.scoreMatchesFilters(score, filters), after, limit ); return { scores: deltaResponse.rows, delta: deltaResponse.delta, deltaCursor: deltaResponse.deltaCursor }; } let matching = this.db.scoreRecords.filter((score) => this.scoreMatchesFilters(score, filters)); const dir = orderBy.direction === "DESC" ? -1 : 1; if (orderBy.field === "score") { matching.sort((a, b) => dir * (a.score - b.score)); } else { matching.sort((a, b) => dir * (a.timestamp.getTime() - b.timestamp.getTime())); } const total = matching.length; const page = Number(pagination.page); const perPage = Number(pagination.perPage); const start = page * perPage; return { scores: matching.slice(start, start + perPage), pagination: { total, page, perPage, hasMore: start + perPage < total }, ...this.pageDeltaCursor( this.maxMatchingCursorId( this.db.scoreRecords, this.db.scoreCursorIds, (score) => this.scoreMatchesFilters(score, filters) ) ) }; } async getScoreById(scoreId) { return this.db.scoreRecords.find((score) => score.scoreId === scoreId) ?? null; } scoreMatchesFilters(score, filters) { if (!filters) return true; if (filters.timestamp) { if (filters.timestamp.start && score.timestamp < filters.timestamp.start) return false; if (filters.timestamp.end && score.timestamp > filters.timestamp.end) return false; } if (filters.traceId !== void 0 && score.traceId !== filters.traceId) return false; if (filters.spanId !== void 0 && score.spanId !== filters.spanId) return false; if (filters.entityType !== void 0 && score.entityType !== filters.entityType) return false; if (filters.entityName !== void 0 && score.entityName !== filters.entityName) return false; if (filters.entityVersionId !== void 0 && score.entityVersionId !== filters.entityVersionId) return false; if (filters.parentEntityVersionId !== void 0 && score.parentEntityVersionId !== filters.parentEntityVersionId) return false; if (filters.rootEntityVersionId !== void 0 && score.rootEntityVersionId !== filters.rootEntityVersionId) return false; if (filters.userId !== void 0 && score.userId !== filters.userId) return false; if (filters.organizationId !== void 0 && score.organizationId !== filters.organizationId) return false; if (filters.resourceId !== void 0 && score.resourceId !== filters.resourceId) return false; if (filters.runId !== void 0 && score.runId !== filters.runId) return false; if (filters.sessionId !== void 0 && score.sessionId !== filters.sessionId) return false; if (filters.threadId !== void 0 && score.threadId !== filters.threadId) return false; if (filters.requestId !== void 0 && score.requestId !== filters.requestId) return false; if (filters.parentEntityType !== void 0 && score.parentEntityType !== filters.parentEntityType) return false; if (filters.parentEntityName !== void 0 && score.parentEntityName !== filters.parentEntityName) return false; if (filters.rootEntityType !== void 0 && score.rootEntityType !== filters.rootEntityType) return false; if (filters.rootEntityName !== void 0 && score.rootEntityName !== filters.rootEntityName) return false; if (filters.serviceName !== void 0 && score.serviceName !== filters.serviceName) return false; if (filters.environment !== void 0 && score.environment !== filters.environment) return false; if (filters.executionSource !== void 0 && score.executionSource !== filters.executionSource) return false; if (filters.scorerId !== void 0) { const names = Array.isArray(filters.scorerId) ? filters.scorerId : [filters.scorerId]; if (!names.includes(score.scorerId)) return false; } const scoreSource = score.scoreSource ?? score.source ?? null; if (filters.scoreSource !== void 0 && scoreSource !== filters.scoreSource) return false; if (filters.source !== void 0 && scoreSource !== filters.source) return false; if (filters.experimentId !== void 0 && score.experimentId !== filters.experimentId) return false; if (filters.tags != null && filters.tags.length > 0) { if (score.tags == null) return false; for (const tag of filters.tags) { if (!score.tags.includes(tag)) return false; } } return true; } async getScoreAggregate(args) { const filtered = this.db.scoreRecords.filter((score) => this.scoreMatchesFilters(score, args.filters)).filter((score) => score.scorerId === args.scorerId).filter((score) => args.scoreSource ? (score.scoreSource ?? score.source ?? null) === args.scoreSource : true); const value = this.aggregate( filtered.map((score) => score.score), args.aggregation, filtered.map((score) => score.timestamp.getTime()) ); if (args.comparePeriod && args.filters?.timestamp) { const previousRange = this.getComparisonDateRange(args.comparePeriod, args.filters.timestamp); if (previousRange) { const previousFiltered = this.db.scoreRecords.filter( (score) => this.scoreMatchesFilters(score, { ...args.filters ?? {}, timestamp: previousRange }) ).filter((score) => score.scorerId === args.scorerId).filter( (score) => args.scoreSource ? (score.scoreSource ?? score.source ?? null) === args.scoreSource : true ); const previousValue = this.aggregate( previousFiltered.map((score) => score.score), args.aggregation, previousFiltered.map((score) => score.timestamp.getTime()) ); let changePercent = null; if (previousValue !== null && previousValue !== 0 && value !== null) { changePercent = (value - previousValue) / Math.abs(previousValue) * 100; } return { value, previousValue, changePercent }; } } return { value }; } async getScoreBreakdown(args) { const filtered = this.db.scoreRecords.filter((score) => this.scoreMatchesFilters(score, args.filters)).filter((score) => score.scorerId === args.scorerId).filter((score) => args.scoreSource ? (score.scoreSource ?? score.source ?? null) === args.scoreSource : true); const groupMap = /* @__PURE__ */ new Map(); for (const score of filtered) { const dims = {}; for (const col of args.groupBy) { const value = score[col]; dims[col] = value === null || value === void 0 ? null : String(value); } const key = JSON.stringify(dims); if (!groupMap.has(key)) groupMap.set(key, []); groupMap.get(key).push(score); } const groups = Array.from(groupMap.entries()).map(([key, records]) => ({ dimensions: JSON.parse(key), value: this.aggregate( records.map((record) => record.score), args.aggregation, records.map((record) => record.timestamp.getTime()) ) ?? 0 })); groups.sort((a, b) => b.value - a.value); return { groups }; } async getScoreTimeSeries(args) { const filtered = this.db.scoreRecords.filter((score) => this.scoreMatchesFilters(score, args.filters)).filter((score) => score.scorerId === args.scorerId).filter((score) => args.scoreSource ? (score.scoreSource ?? score.source ?? null) === args.scoreSource : true); const intervalMs = this.intervalToMs(args.interval); if (args.groupBy && args.groupBy.length > 0) { const seriesMap = /* @__PURE__ */ new Map(); const seriesNames = /* @__PURE__ */ new Map(); for (const score of filtered) { const values = args.groupBy.map((col) => score[col] ?? ""); const key = JSON.stringify(values); if (!seriesMap.has(key)) seriesMap.set(key, /* @__PURE__ */ new Map()); if (!seriesNames.has(key)) { seriesNames.set( key, values.map((value) => value === null || value === void 0 ? "" : String(value)).join("|") ); } const bucket = Math.floor(score.timestamp.getTime() / intervalMs) * intervalMs; const bucketMap2 = seriesMap.get(key); if (!bucketMap2.has(bucket)) bucketMap2.set(bucket, []); bucketMap2.get(bucket).push(score); } return { series: Array.from(seriesMap.entries()).map(([key, bucketMap2]) => ({ name: seriesNames.get(key), points: Array.from(bucketMap2.entries()).sort(([a], [b]) => a - b).map(([ts, records]) => ({ timestamp: new Date(ts), value: this.aggregate( records.map((record) => record.score), args.aggregation, records.map((record) => record.timestamp.getTime()) ) ?? 0 })) })) }; } const bucketMap = /* @__PURE__ */ new Map(); for (const score of filtered) { const bucket = Math.floor(score.timestamp.getTime() / intervalMs) * intervalMs; if (!bucketMap.has(bucket)) bucketMap.set(bucket, []); bucketMap.get(bucket).push(score); } return { series: [ { name: args.scoreSource ? `${args.scorerId}|${args.scoreSource}` : args.scorerId, points: Array.from(bucketMap.entries()).sort(([a], [b]) => a - b).map(([ts, records]) => ({ timestamp: new Date(ts), value: this.aggregate( records.map((record) => record.score), args.aggregation, records.map((record) => record.timestamp.getTime()) ) ?? 0 })) } ] }; } async getScorePercentiles(args) { const filtered = this.db.scoreRecords.filter((score) => this.scoreMatchesFilters(score, args.filters)).filter((score) => score.scorerId === args.scorerId).filter((score) => args.scoreSource ? (score.scoreSource ?? score.source ?? null) === args.scoreSource : true); const intervalMs = this.intervalToMs(args.interval); const bucketMap = /* @__PURE__ */ new Map(); for (const score of filtered) { const bucket = Math.floor(score.timestamp.getTime() / intervalMs) * intervalMs; if (!bucketMap.has(bucket)) bucketMap.set(bucket, []); bucketMap.get(bucket).push(score.score); } const sortedBuckets = Array.from(bucketMap.entries()).sort(([a], [b]) => a - b); return { series: args.percentiles.map((percentile) => ({ percentile, points: sortedBuckets.map(([ts, values]) => { const sorted = [...values].sort((a, b) => a - b); return { timestamp: new Date(ts), value: this.interpolatePercentile(sorted, percentile) }; }) })) }; } getNumericFeedbackValue(value) { if (typeof value === "number") { return Number.isFinite(value) ? value : null; } if (typeof value === "string") { const trimmed = value.trim(); if (trimmed.length === 0) return null; const numeric = Number(trimmed); return Number.isFinite(numeric) ? numeric : null; } return null; } getComparisonDateRange(comparePeriod, timestamp) { if (!timestamp.start || !timestamp.end) return null; const duration = timestamp.end.getTime() - timestamp.start.getTime(); switch (comparePeriod) { case "previous_period": return { start: new Date(timestamp.start.getTime() - duration), end: new Date(timestamp.end.getTime() - duration), startExclusive: timestamp.startExclusive, endExclusive: timestamp.endExclusive }; case "previous_day": return { start: new Date(timestamp.start.getTime() - 864e5), end: new Date(timestamp.end.getTime() - 864e5), startExclusive: timestamp.startExclusive, endExclusive: timestamp.endExclusive }; case "previous_week": return { start: new Date(timestamp.start.getTime() - 6048e5), end: new Date(timestamp.end.getTime() - 6048e5), startExclusive: timestamp.startExclusive, endExclusive: timestamp.endExclusive }; } } // ============================================================================ // Feedback // ============================================================================ async createFeedback(args) { const record = { ...args.feedback, feedbackSource: args.feedback.feedbackSource ?? args.feedback.source ?? "", source: args.feedback.feedbackSource ?? args.feedback.source ?? "", feedbackUserId: args.feedback.feedbackUserId ?? args.feedback.userId ?? (typeof args.feedback.metadata?.userId === "string" ? args.feedback.metadata.userId : null) }; this.upsertByIdField(this.db.feedbackRecords, this.db.feedbackCursorIds, record, "feedbackId"); } async batchCreateFeedback(args) { for (const fb of args.feedbacks) { const record = { ...fb, feedbackSource: fb.feedbackSource ?? fb.source ?? "", source: fb.feedbackSource ?? fb.source ?? "", feedbackUserId: fb.feedbackUserId ?? fb.userId ?? (typeof fb.metadata?.userId === "string" ? fb.metadata.userId : null) }; this.upsertByIdField(this.db.feedbackRecords, this.db.feedbackCursorIds, record, "feedbackId"); } } async listFeedback(args) { const { mode, filters, pagination, orderBy, after, limit } = chunkLP4WZA6D_cjs.listFeedbackArgsSchema.parse(args); if (mode === "delta") { this.assertDeltaPollingEnabled(); const deltaResponse = this.listAppendOnlyDelta( this.db.feedbackRecords, this.db.feedbackCursorIds, (feedback) => this.feedbackMatchesFilters(feedback, filters), after, limit ); return { feedback: deltaResponse.rows, delta: deltaResponse.delta, deltaCursor: deltaResponse.deltaCursor }; } let matching = this.db.feedbackRecords.filter((fb) => this.feedbackMatchesFilters(fb, filters)); const dir = orderBy.direction === "DESC" ? -1 : 1; matching.sort((a, b) => dir * (a.timestamp.getTime() - b.timestamp.getTime())); const total = matching.length; const page = Number(pagination.page); const perPage = Number(pagination.perPage); const start = page * perPage; return { feedback: matching.slice(start, start + perPage), pagination: { total, page, perPage, hasMore: start + perPage < total }, ...this.pageDeltaCursor( this.maxMatchingCursorId( this.db.feedbackRecords, this.db.feedbackCursorIds, (feedback) => this.feedbackMatchesFilters(feedback, filters) ) ) }; } async getFeedbackAggregate(args) { const filtered = this.db.feedbackRecords.filter((feedback) => this.feedbackMatchesFilters(feedback, args.filters)).filter((feedback) => feedback.feedbackType === args.feedbackType).filter( (feedback) => args.feedbackSource ? (feedback.feedbackSource ?? feedback.source ?? "") === args.feedbackSource : true ); const numericEntries = filtered.flatMap((feedback) => { const numericValue = this.getNumericFeedbackValue(feedback.value); return numericValue === null ? [] : [{ numericValue, timestamp: feedback.timestamp.getTime() }]; }); const value = this.aggregate( numericEntries.map((entry) => entry.numericValue), args.aggregation, numericEntries.map((entry) => entry.timestamp) ); if (args.comparePeriod && args.filters?.timestamp) { const previousRange = this.getComparisonDateRange(args.comparePeriod, args.filters.timestamp); if (previousRange) { const previousNumericEntries = this.db.feedbackRecords.filter( (feedback) => this.feedbackMatchesFilters(feedback, { ...args.filters ?? {}, timestamp: previousRange }) ).filter((feedback) => feedback.feedbackType === args.feedbackType).filter( (feedback) => args.feedbackSource ? (feedback.feedbackSource ?? feedback.source ?? "") === args.feedbackSource : true ).flatMap((feedback) => { const numericValue = this.getNumericFeedbackValue(feedback.value); return numericValue === null ? [] : [{ numericValue, timestamp: feedback.timestamp.getTime() }]; }); const previousValue = this.aggregate( previousNumericEntries.map((entry) => entry.numericValue), args.aggregation, previousNumericEntries.map((entry) => entry.timestamp) ); let changePercent = null; if (previousValue !== null && previousValue !== 0 && value !== null) { changePercent = (value - previousValue) / Math.abs(previousValue) * 100; } return { value, previousValue, changePercent }; } } return { value }; } async getFeedbackBreakdown(args) { const filtered = this.db.feedbackRecords.filter((feedback) => this.feedbackMatchesFilters(feedback, args.filters)).filter((feedback) => feedback.feedbackType === args.feedbackType).filter( (feedback) => args.feedbackSource ? (feedback.feedbackSource ?? feedback.source ?? "") === args.feedbackSource : true ).filter((feedback) => this.getNumericFeedbackValue(feedback.value) !== null); const groupMap = /* @__PURE__ */ new Map(); for (const feedback of filtered) { const dims = {}; for (const col of args.groupBy) { const rawValue = feedback[col]; dims[col] = rawValue === null || rawValue === void 0 ? null : String(rawValue); } const key = JSON.stringify(dims); if (!groupMap.has(key)) groupMap.set(key, []); groupMap.get(key).push(feedback); } const groups = Array.from(groupMap.entries()).map(([key, records]) => ({ dimensions: JSON.parse(key), value: (() => { const numericEntries = records.flatMap((record) => { const numericValue = this.getNumericFeedbackValue(record.value); return numericValue === null ? [] : [{ numericValue, timestamp: record.timestamp.getTime() }]; }); return this.aggregate( numericEntries.map((entry) => entry.numericValue), args.aggregation, numericEntries.map((entry) => entry.timestamp) ) ?? 0; })() })); groups.sort((a, b) => b.value - a.value); return { groups }; } async getFeedbackTimeSeries(args) { const filtered = this.db.feedbackRecords.filter((feedback) => this.feedbackMatchesFilters(feedback, args.filters)).filter((feedback) => feedback.feedbackType === args.feedbackType).filter( (feedback) => args.feedbackSource ? (feedback.feedbackSource ?? feedback.source ?? "") === args.feedbackSource : true ).filter((feedback) => this.getNumericFeedbackValue(feedback.value) !== null); const intervalMs = this.intervalToMs(args.interval); if (args.groupBy && args.groupBy.length > 0) { const seriesMap = /* @__PURE__ */ new Map(); const seriesNames = /* @__PURE__ */ new Map(); for (const feedback of filtered) { const values = args.groupBy.map((col) => feedback[col] ?? ""); const key = JSON.stringify(values); if (!seriesMap.has(key)) seriesMap.set(key, /* @__PURE__ */ new Map()); if (!seriesNames.has(key)) { seriesNames.set( key, values.map((value) => value === null || value === void 0 ? "" : String(value)).join("|") ); } const bucket = Math.floor(feedback.timestamp.getTime() / intervalMs) * intervalMs; const bucketMap2 = seriesMap.get(key); if (!bucketMap2.has(bucket)) bucketMap2.set(bucket, []); bucketMap2.get(bucket).push(feedback); } return { series: Array.from(seriesMap.entries()).map(([key, bucketMap2]) => ({ name: seriesNames.get(key), points: Array.from(bucketMap2.entries()).sort(([a], [b]) => a - b).map(([ts, records]) => ({ timestamp: new Date(ts), value: (() => { const numericEntries = records.flatMap((record) => { const numericValue = this.getNumericFeedbackValue(record.value); return numericValue === null ? [] : [{ numericValue, timestamp: record.timestamp.getTime() }]; }); return this.aggregate( numericEntries.map((entry) => entry.numericValue), args.aggregation, numericEntries.map((entry) => entry.timestamp) ) ?? 0; })() })) })) }; } const bucketMap = /* @__PURE__ */ new Map(); for (const feedback of filtered) { const bucket = Math.floor(feedback.timestamp.getTime() / intervalMs) * intervalMs; if (!bucketMap.has(bucket)) bucketMap.set(bucket, []); bucketMap.get(bucket).push(feedback); } return { series: [ { name: args.feedbackSource ? `${args.feedbackType}|${args.feedbackSource}` : args.feedbackType, points: Array.from(bucketMap.entries()).sort(([a], [b]) => a - b).map(([ts, records]) => ({ timestamp: new Date(ts), value: (() => { const numericEntries = records.flatMap((record) => { const numericValue = this.getNumericFeedbackValue(record.value); return numericValue === null ? [] : [{ numericValue, timestamp: record.timestamp.getTime() }]; }); return this.aggregate( numericEntries.map((entry) => entry.numericValue), args.aggregation, numericEntries.map((entry) => entry.timestamp) ) ?? 0; })() })) } ] }; } async getFeedbackPercentiles(args) { const filtered = this.db.feedbackRecords.filter((feedback) => this.feedbackMatchesFilters(feedback, args.filters)).filter((feedback) => feedback.feedbackType === args.feedbackType).filter( (feedback) => args.feedbackSource ? (feedback.feedbackSource ?? feedback.source ?? "") === args.feedbackSource : true ); const intervalMs = this.intervalToMs(args.interval); const bucketMap = /* @__PURE__ */ new Map(); for (const feedback of filtered) { const numericValue = this.getNumericFeedbackValue(feedback.value); if (numericValue === null) continue; const bucket = Math.floor(feedback.timestamp.getTime() / intervalMs) * intervalMs; if (!bucketMap.has(bucket)) bucketMap.set(bucket, []); bucketMap.get(bucket).push(numericValue); } const sortedBuckets = Array.from(bucketMap.entries()).sort(([a], [b]) => a - b); return { series: args.percentiles.map((percentile) => ({ percentile, points: sortedBuckets.map(([ts, values]) => { const sorted = [...values].sort((a, b) => a - b); return { timestamp: new Date(ts), value: this.interpolatePercentile(sorted, percentile) }; }) })) }; } feedbackMatchesFilters(fb, filters) { if (!filters) return true; if (filters.timestamp) { if (filters.timestamp.start && fb.timestamp < filters.timestamp.start) return false; if (filters.timestamp.end && fb.timestamp > filters.timestamp.end) return false; } if (filters.traceId !== void 0 && fb.traceId !== filters.traceId) return false; if (filters.spanId !== void 0 && fb.spanId !== filters.spanId) return false; if (filters.entityType !== void 0 && fb.entityType !== filters.entityType) return false; if (filters.entityName !== void 0 && fb.entityName !== filters.entityName) return false; if (filters.entityVersionId !== void 0 && fb.entityVersionId !== filters.entityVersionId) return false; if (filters.parentEntityVersionId !== void 0 && fb.parentEntityVersionId !== filters.parentEntityVersionId) return false; if (filters.rootEntityVersionId !== void 0 && fb.rootEntityVersionId !== filters.rootEntityVersionId) return false; if (filters.userId !== void 0 && fb.userId !== filters.userId) return false; if (filters.organizationId !== void 0 && fb.organizationId !== filters.organizationId) return false; if (filters.resourceId !== void 0 && fb.resourceId !== filters.resourceId) return false; if (filters.runId !== void 0 && fb.runId !== filters.runId) return false; if (filters.sessionId !== void 0 && fb.sessionId !== filters.sessionId) return false; if (filters.threadId !== void 0 && fb.threadId !== filters.threadId) return false; if (filters.requestId !== void 0 && fb.requestId !== filters.requestId) return false; if (filters.parentEntityType !== void 0 && fb.parentEntityType !== filters.parentEntityType) return false; if (filters.parentEntityName !== void 0 && fb.parentEntityName !== filters.parentEntityName) return false; if (filters.rootEntityType !== void 0 && fb.rootEntityType !== filters.rootEntityType) return false; if (filters.rootEntityName !== void 0 && fb.rootEntityName !== filters.rootEntityName) return false; if (filters.serviceName !== void 0 && fb.serviceName !== filters.serviceName) return false; if (filters.environment !== void 0 && fb.environment !== filters.environment) return false; if (filters.executionSource !== void 0 && fb.executionSource !== filters.executionSource) return false; if (filters.feedbackType !== void 0) { const types = Array.isArray(filters.feedbackType) ? filters.feedbackType : [filters.feedbackType]; if (!types.includes(fb.feedbackType)) return false; } const feedbackSource = fb.feedbackSource ?? fb.source ?? ""; if (filters.feedbackSource !== void 0 && feedbackSource !== filters.feedbackSource) return false; if (filters.source !== void 0 && feedbackSource !== filters.source) return false; if (filters.experimentId !== void 0 && fb.experimentId !== filters.experimentId) return false; if (filters.feedbackUserId !== void 0 && fb.feedbackUserId !== filters.feedbackUserId) return false; if (filters.tags != null && filters.tags.length > 0) { if (fb.tags == null) return false; for (const tag of filters.tags) { if (!fb.tags.includes(tag)) return false; } } return true; } }; // src/storage/domains/observability/record-builders.ts var entityTypeValues = new Set(Object.values(chunkLP4WZA6D_cjs.EntityType)); function toEntityType(value) { if (value && entityTypeValues.has(value)) { return value; } return null; } function getStringOrNull(value) { return typeof value === "string" ? value : null; } function getObjectOrNull(value) { return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null; } function serializeSpanAttributes(span) { if (!span.attributes) { return null; } try { return JSON.parse( JSON.stringify(span.attributes, (_key, value) => { if (value instanceof Date) { return value.toISOString(); } return value; }) ); } catch { return null; } } function buildCorrelationRecordFields(context) { return { tags: context?.tags ?? null, entityType: context?.entityType ?? null, entityId: context?.entityId ?? null, entityName: context?.entityName ?? null, entityVersionId: context?.entityVersionId ?? null, parentEntityType: context?.parentEntityType ?? null, parentEntityId: context?.parentEntityId ?? null, parentEntityName: context?.parentEntityName ?? null, parentEntityVersionId: context?.parentEntityVersionId ?? null, rootEntityType: context?.rootEntityType ?? null, rootEntityId: context?.rootEntityId ?? null, rootEntityName: context?.rootEntityName ?? null, rootEntityVersionId: context?.rootEntityVersionId ?? null, userId: context?.userId ?? null, organizationId: context?.organizationId ?? null, resourceId: context?.resourceId ?? null, runId: context?.runId ?? null, sessionId: context?.sessionId ?? null, threadId: context?.threadId ?? null, requestId: context?.requestId ?? null, environment: context?.environment ?? null, executionSource: context?.source ?? null, serviceName: context?.serviceName ?? null, experimentId: context?.experimentId ?? null }; } function buildLegacyMetricLabelCorrelationFields(labels) { return { entityType: toEntityType(labels.entity_type), entityName: getStringOrNull(labels.entity_name), parentEntityType: toEntityType(labels.parent_type), parentEntityName: getStringOrNull(labels.parent_name), serviceName: getStringOrNull(labels.service_name) }; } function stripLegacyMetricCorrelationLabels(labels) { const sanitized = { ...labels }; delete sanitized.entity_type; delete sanitized.entity_name; delete sanitized.parent_type; delete sanitized.parent_name; delete sanitized.service_name; return sanitized; } function buildLegacyLogMetadataCorrelationFields(metadata) { return { entityType: toEntityType(getStringOrNull(metadata?.entity_type) ?? void 0), entityName: getStringOrNull(metadata?.entity_name), parentEntityType: toEntityType(getStringOrNull(metadata?.parent_type) ?? void 0), parentEntityName: getStringOrNull(metadata?.parent_name), rootEntityType: toEntityType(getStringOrNull(metadata?.root_type) ?? void 0), rootEntityName: getStringOrNull(metadata?.root_name), environment: getStringOrNull(metadata?.environment), executionSource: getStringOrNull(metadata?.source), serviceName: getStringOrNull(metadata?.service_name) }; } function buildCreateSpanRecord(span) { const metadata = span.metadata ?? {}; return { traceId: span.traceId, spanId: span.id, parentSpanId: span.parentSpanId ?? null, name: span.name, // Entity identification - from span entityType: span.entityType ?? null, entityId: span.entityId ?? null, entityName: span.entityName ?? null, entityVersionId: getStringOrNull(metadata.entityVersionId), // Identity & Tenancy - extracted from metadata if present userId: getStringOrNull(metadata.userId), organizationId: getStringOrNull(metadata.organizationId), resourceId: getStringOrNull(metadata.resourceId), // Correlation IDs - extracted from metadata if present runId: getStringOrNull(metadata.runId), sessionId: getStringOrNull(metadata.sessionId), threadId: getStringOrNull(metadata.threadId), requestId: getStringOrNull(metadata.requestId), // Deployment context - extracted from metadata if present environment: getStringOrNull(metadata.environment), source: getStringOrNull(metadata.source), serviceName: getStringOrNull(metadata.serviceName), scope: getObjectOrNull(metadata.scope), // Experimentation experimentId: getStringOrNull(metadata.experimentId), // Span data spanType: span.type, attributes: serializeSpanAttributes(span), metadata: span.metadata ?? null, tags: span.tags ?? null, links: null, input: span.input ?? null, output: span.output ?? null, error: span.errorInfo ?? null, isEvent: span.isEvent, // Request context requestContext: span.requestContext ?? null, // Timestamps startedAt: span.startTime, endedAt: span.endTime ?? null }; } function buildUpdateSpanRecord(span) { return { name: span.name, scope: null, attributes: serializeSpanAttributes(span), metadata: span.metadata ?? null, links: null, endedAt: span.endTime ?? null, input: span.input, output: span.output, error: span.errorInfo ?? null }; } function buildMetricRecord(event) { const m = event.metric; const labels = stripLegacyMetricCorrelationLabels(m.labels); const correlationFields = buildCorrelationRecordFields(m.correlationContext); const legacyCorrelationFields = buildLegacyMetricLabelCorrelationFields(m.labels); const cost = m.costContext; return { metricId: m.metricId, timestamp: m.timestamp, name: m.name, value: m.value, labels, traceId: m.traceId ?? m.correlationContext?.traceId ?? null, spanId: m.spanId ?? m.correlationContext?.spanId ?? null, ...correlationFields, scope: null, entityType: correlationFields.entityType ?? legacyCorrelationFields.entityType ?? null, entityName: correlationFields.entityName ?? legacyCorrelationFields.entityName ?? null, parentEntityType: correlationFields.parentEntityType ?? legacyCorrelationFields.parentEntityType ?? null, parentEntityName: correlationFields.parentEntityName ?? legacyCorrelationFields.parentEntityName ?? null, serviceName: correlationFields.serviceName ?? legacyCorrelationFields.serviceName ?? null, provider: cost?.provider ?? null, model: cost?.model ?? null, estimatedCost: cost?.estimatedCost ?? null, costUnit: cost?.costUnit ?? null, costMetadata: cost?.costMetadata ?? null, metadata: m.metadata ?? null }; } function buildLogRecord(event) { const l = event.log; const correlationFields = buildCorrelationRecordFields(l.correlationContext); const legacyCorrelationFields = buildLegacyLogMetadataCorrelationFields(l.metadata ?? null); return { logId: l.logId, timestamp: l.timestamp, level: l.level, message: l.message, data: l.data ?? null, ...correlationFields, traceId: l.traceId ?? l.correlationContext?.traceId ?? null, spanId: l.spanId ?? l.correlationContext?.spanId ?? null, tags: correlationFields.tags ?? l.tags ?? null, entityType: correlationFields.entityType ?? legacyCorrelationFields.entityType ?? null, entityName: correlationFields.entityName ?? legacyCorrelationFields.entityName ?? null, parentEntityType: correlationFields.parentEntityType ?? legacyCorrelationFields.parentEntityType ?? null, parentEntityName: correlationFields.parentEntityName ?? legacyCorrelationFields.parentEntityName ?? null, rootEntityType: correlationFields.rootEntityType ?? legacyCorrelationFields.rootEntityType ?? null, rootEntityName: correlationFields.rootEntityName ?? legacyCorrelationFields.rootEntityName ?? null, environment: correlationFields.environment ?? legacyCorrelationFields.environment ?? null, executionSource: correlationFields.executionSource ?? legacyCorrelationFields.executionSource ?? null, serviceName: correlationFields.serviceName ?? legacyCorrelationFields.serviceName ?? null, scope: null, metadata: l.metadata ?? null }; } function buildScoreRecord(event) { const s = event.score; const correlationFields = buildCorrelationRecordFields(s.correlationContext); return { scoreId: s.scoreId, timestamp: s.timestamp, traceId: s.traceId ?? s.correlationContext?.traceId ?? null, spanId: s.spanId ?? s.correlationContext?.spanId ?? null, scorerId: s.scorerId, scorerName: s.scorerName ?? null, scorerVersion: s.scorerVersion ?? null, scoreSource: s.scoreSource ?? s.source ?? null, source: s.scoreSource ?? s.source ?? null, score: s.score, reason: s.reason ?? null, ...correlationFields, entityType: correlationFields.entityType ?? s.targetEntityType ?? null, experimentId: correlationFields.experimentId ?? s.experimentId ?? null, scope: null, scoreTraceId: s.scoreTraceId ?? null, metadata: s.metadata ?? null }; } function buildFeedbackRecord(event) { const fb = event.feedback; const correlationFields = buildCorrelationRecordFields(fb.correlationContext); return { feedbackId: fb.feedbackId, timestamp: fb.timestamp, traceId: fb.traceId ?? fb.correlationContext?.traceId ?? null, spanId: fb.spanId ?? fb.correlationContext?.spanId ?? null, feedbackSource: fb.feedbackSource ?? fb.source ?? "", source: fb.feedbackSource ?? fb.source ?? "", feedbackType: fb.feedbackType, value: fb.value, comment: fb.comment ?? null, ...correlationFields, experimentId: correlationFields.experimentId ?? fb.experimentId ?? null, feedbackUserId: fb.feedbackUserId ?? fb.userId ?? (typeof fb.metadata?.userId === "string" ? fb.metadata.userId : null), scope: null, sourceId: fb.sourceId ?? null, metadata: fb.metadata ?? null }; } // src/storage/domains/background-tasks/base.ts var BackgroundTasksStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "BACKGROUND_TASKS" }); } async dangerouslyClearAll() { } }; // src/storage/domains/background-tasks/inmemory.ts var BackgroundTasksInMemory = class extends BackgroundTasksStorage { db; constructor({ db }) { super(); this.db = db; } async dangerouslyClearAll() { this.db.backgroundTasks.clear(); } async createTask(task) { this.db.backgroundTasks.set(task.id, { ...task }); } async updateTask(taskId, update) { const existing = this.db.backgroundTasks.get(taskId); if (!existing) return; this.db.backgroundTasks.set(taskId, { ...existing, ...update }); } async getTask(taskId) { const task = this.db.backgroundTasks.get(taskId); return task ? { ...task } : null; } async listTasks(filter) { let tasks = Array.from(this.db.backgroundTasks.values()); if (filter.status) { const statuses = Array.isArray(filter.status) ? filter.status : [filter.status]; tasks = tasks.filter((t) => statuses.includes(t.status)); } if (filter.agentId) { tasks = tasks.filter((t) => t.agentId === filter.agentId); } if (filter.threadId) { tasks = tasks.filter((t) => t.threadId === filter.threadId); } if (filter.resourceId) { tasks = tasks.filter((t) => t.resourceId === filter.resourceId); } if (filter.toolName) { tasks = tasks.filter((t) => t.toolName === filter.toolName); } if (filter.toolCallId) { tasks = tasks.filter((t) => t.toolCallId === filter.toolCallId); } if (filter.runId) { tasks = tasks.filter((t) => t.runId === filter.runId); } const dateCol = filter.dateFilterBy ?? "createdAt"; if (filter.fromDate) { tasks = tasks.filter((t) => { const val = t[dateCol]; return val != null && val >= filter.fromDate; }); } if (filter.toDate) { tasks = tasks.filter((t) => { const val = t[dateCol]; return val != null && val < filter.toDate; }); } const orderBy = filter.orderBy ?? "createdAt"; const direction = filter.orderDirection ?? "asc"; tasks.sort((a, b) => { const aVal = a[orderBy]?.getTime() ?? 0; const bVal = b[orderBy]?.getTime() ?? 0; return direction === "asc" ? aVal - bVal : bVal - aVal; }); const total = tasks.length; if (filter.page != null && filter.perPage != null) { const start = filter.page * filter.perPage; tasks = tasks.slice(start, start + filter.perPage); } else if (filter.perPage != null) { tasks = tasks.slice(0, filter.perPage); } return { tasks: tasks.map((t) => ({ ...t })), total }; } async deleteTask(taskId) { this.db.backgroundTasks.delete(taskId); } async deleteTasks(filter) { const { tasks } = await this.listTasks(filter); for (const task of tasks) { this.db.backgroundTasks.delete(task.id); } } async getRunningCount() { let count = 0; for (const task of this.db.backgroundTasks.values()) { if (task.status === "running") count++; } return count; } async getRunningCountByAgent(agentId) { let count = 0; for (const task of this.db.backgroundTasks.values()) { if (task.status === "running" && task.agentId === agentId) count++; } return count; } }; // src/storage/domains/blobs/base.ts var BlobStore = class extends chunkWSD4JNMB_cjs.MastraBase { constructor() { super({ component: "STORAGE", name: "BLOBS" }); } }; // src/storage/domains/blobs/inmemory.ts var InMemoryBlobStore = class extends BlobStore { #blobs = /* @__PURE__ */ new Map(); async init() { } async put(entry) { if (!this.#blobs.has(entry.hash)) { this.#blobs.set(entry.hash, entry); } } async get(hash) { return this.#blobs.get(hash) ?? null; } async has(hash) { return this.#blobs.has(hash); } async delete(hash) { return this.#blobs.delete(hash); } async putMany(entries) { for (const entry of entries) { await this.put(entry); } } async getMany(hashes) { const result = /* @__PURE__ */ new Map(); for (const hash of hashes) { const blob = this.#blobs.get(hash); if (blob) { result.set(hash, blob); } } return result; } async dangerouslyClearAll() { this.#blobs.clear(); } }; // src/storage/domains/channels/base.ts var ChannelsStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "CHANNELS" }); } }; // src/storage/domains/channels/inmemory.ts var InMemoryChannelsStorage = class extends ChannelsStorage { #installations = /* @__PURE__ */ new Map(); #configs = /* @__PURE__ */ new Map(); async saveInstallation(installation) { this.#installations.set(installation.id, { ...installation }); } async getInstallation(id) { const inst = this.#installations.get(id); return inst ? { ...inst } : null; } async getInstallationByAgent(platform, agentId) { const statusPriority = { active: 0, pending: 1, error: 2 }; let best = null; for (const installation of this.#installations.values()) { if (installation.platform === platform && installation.agentId === agentId) { if (!best || (statusPriority[installation.status] ?? 3) < (statusPriority[best.status] ?? 3)) { best = installation; } } } return best ? { ...best } : null; } async getInstallationByWebhookId(webhookId) { for (const installation of this.#installations.values()) { if (installation.webhookId === webhookId) { return { ...installation }; } } return null; } async listInstallations(platform) { const results = []; for (const installation of this.#installations.values()) { if (installation.platform === platform) { results.push({ ...installation }); } } return results; } async deleteInstallation(id) { this.#installations.delete(id); } async saveConfig(config) { this.#configs.set(config.platform, { ...config }); } async getConfig(platform) { const config = this.#configs.get(platform); return config ? { ...config } : null; } async deleteConfig(platform) { this.#configs.delete(platform); } async dangerouslyClearAll() { this.#installations.clear(); this.#configs.clear(); } }; // src/datasets/validation/errors.ts var SchemaValidationError = class extends Error { constructor(field, errors) { const summary = errors.slice(0, 3).map((e) => e.message).join("; "); super(`Validation failed for ${field}: ${summary}`); this.field = field; this.errors = errors; this.name = "SchemaValidationError"; } field; errors; }; var SchemaUpdateValidationError = class extends Error { constructor(failingItems) { const count = failingItems.length; super(`Cannot update schema: ${count} existing item(s) would fail validation`); this.failingItems = failingItems; this.name = "SchemaUpdateValidationError"; } failingItems; }; // src/datasets/validation/validator.ts function resolveZodSchema(zodString) { return Function("z", `"use strict";return (${zodString});`)(v4.z); } var SchemaValidator = class { cache = /* @__PURE__ */ new Map(); /** Get or compile validator for schema */ getValidator(schema, cacheKey) { let zodSchema = this.cache.get(cacheKey); if (!zodSchema) { const zodString = jsonToZod.jsonSchemaToZod(schema); zodSchema = resolveZodSchema(zodString); this.cache.set(cacheKey, zodSchema); } return zodSchema; } /** Clear cached validator (call when schema changes) */ clearCache(cacheKey) { this.cache.delete(cacheKey); } /** Validate data against schema */ validate(data, schema, field, cacheKey) { const zodSchema = this.getValidator(schema, cacheKey); const result = zodSchema.safeParse(data); if (!result.success) { throw new SchemaValidationError(field, this.formatErrors(result.error)); } } /** Validate multiple items, returning valid/invalid split */ validateBatch(items, inputSchema, outputSchema, cacheKeyPrefix, maxErrors = 10) { const result = { valid: [], invalid: [] }; const inputValidator = inputSchema ? this.getValidator(inputSchema, `${cacheKeyPrefix}:input`) : null; const outputValidator = outputSchema ? this.getValidator(outputSchema, `${cacheKeyPrefix}:output`) : null; for (const [i, item] of items.entries()) { let hasError = false; if (inputValidator) { const inputResult = inputValidator.safeParse(item.input); if (!inputResult.success) { result.invalid.push({ index: i, data: item, field: "input", errors: this.formatErrors(inputResult.error) }); hasError = true; if (result.invalid.length >= maxErrors) break; } } if (!hasError && outputValidator && item.groundTruth !== void 0) { const outputResult = outputValidator.safeParse(item.groundTruth); if (!outputResult.success) { result.invalid.push({ index: i, data: item, field: "groundTruth", errors: this.formatErrors(outputResult.error) }); hasError = true; if (result.invalid.length >= maxErrors) break; } } if (!hasError) { result.valid.push({ index: i, data: item }); } } return result; } /** Format Zod errors to FieldError array */ formatErrors(error) { return error.issues.slice(0, 5).map((issue) => ({ // Convert Zod path array to JSON Pointer string path: issue.path.length > 0 ? "/" + issue.path.join("/") : "/", code: issue.code, message: issue.message })); } }; var validatorInstance = null; function getSchemaValidator() { if (!validatorInstance) { validatorInstance = new SchemaValidator(); } return validatorInstance; } function createValidator() { return new SchemaValidator(); } // src/storage/domains/datasets/base.ts var DatasetsStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "DATASETS" }); } async dangerouslyClearAll() { } /** * Update a dataset. Validates existing items against new schemas if schemas are changing. * Subclasses implement _doUpdateDataset for actual storage operation. */ async updateDataset(args) { const existing = await this.getDatasetById({ id: args.id }); if (!existing) { throw new Error(`Dataset not found: ${args.id}`); } const inputSchemaChanging = args.inputSchema !== void 0 && JSON.stringify(args.inputSchema) !== JSON.stringify(existing.inputSchema); const groundTruthSchemaChanging = args.groundTruthSchema !== void 0 && JSON.stringify(args.groundTruthSchema) !== JSON.stringify(existing.groundTruthSchema); if (inputSchemaChanging || groundTruthSchemaChanging) { const itemsResult = await this.listItems({ datasetId: args.id, pagination: { page: 0, perPage: false } // Get all items }); const items = itemsResult.items; if (items.length > 0) { const validator = getSchemaValidator(); const newInputSchema = args.inputSchema !== void 0 ? args.inputSchema : existing.inputSchema; const newOutputSchema = args.groundTruthSchema !== void 0 ? args.groundTruthSchema : existing.groundTruthSchema; const result = validator.validateBatch( items.map((i) => ({ input: i.input, groundTruth: i.groundTruth })), newInputSchema, newOutputSchema, `dataset:${args.id}:schema-update`, 10 // Max 10 errors to report ); if (result.invalid.length > 0) { throw new SchemaUpdateValidationError(result.invalid); } validator.clearCache(`dataset:${args.id}:input`); validator.clearCache(`dataset:${args.id}:output`); } } return this._doUpdateDataset(args); } /** * Add an item to a dataset. Validates input/groundTruth against dataset schemas. * Subclasses implement _doAddItem which handles SCD-2 versioning internally. */ async addItem(args) { const dataset = await this.getDatasetById({ id: args.datasetId }); if (!dataset) { throw new Error(`Dataset not found: ${args.datasetId}`); } const validator = getSchemaValidator(); const cacheKey = `dataset:${args.datasetId}`; if (dataset.inputSchema) { validator.validate(args.input, dataset.inputSchema, "input", `${cacheKey}:input`); } if (dataset.groundTruthSchema && args.groundTruth !== void 0) { validator.validate(args.groundTruth, dataset.groundTruthSchema, "groundTruth", `${cacheKey}:output`); } return this._doAddItem(args); } /** * Update an item in a dataset. Validates changed fields against dataset schemas. * Subclasses implement _doUpdateItem which handles SCD-2 versioning internally. */ async updateItem(args) { const dataset = await this.getDatasetById({ id: args.datasetId }); if (!dataset) { throw new Error(`Dataset not found: ${args.datasetId}`); } const validator = getSchemaValidator(); const cacheKey = `dataset:${args.datasetId}`; if (args.input !== void 0 && dataset.inputSchema) { validator.validate(args.input, dataset.inputSchema, "input", `${cacheKey}:input`); } if (args.groundTruth !== void 0 && dataset.groundTruthSchema) { validator.validate(args.groundTruth, dataset.groundTruthSchema, "groundTruth", `${cacheKey}:output`); } return this._doUpdateItem(args); } /** * Delete an item from a dataset. Creates a tombstone row via SCD-2. * Subclasses implement _doDeleteItem which handles SCD-2 versioning internally. */ async deleteItem(args) { return this._doDeleteItem(args); } /** * Batch insert items to a dataset. Validates all items against dataset schemas, * then delegates to subclass which handles SCD-2 versioning internally. */ async batchInsertItems(input) { const dataset = await this.getDatasetById({ id: input.datasetId }); if (!dataset) { throw new Error(`Dataset not found: ${input.datasetId}`); } const validator = getSchemaValidator(); const cacheKey = `dataset:${input.datasetId}`; for (const itemData of input.items) { if (dataset.inputSchema) { validator.validate(itemData.input, dataset.inputSchema, "input", `${cacheKey}:input`); } if (dataset.groundTruthSchema && itemData.groundTruth !== void 0) { validator.validate(itemData.groundTruth, dataset.groundTruthSchema, "groundTruth", `${cacheKey}:output`); } } return this._doBatchInsertItems(input); } /** * Batch delete items from a dataset. Creates tombstone rows via SCD-2. * Subclasses implement _doBatchDeleteItems which handles SCD-2 versioning internally. */ async batchDeleteItems(input) { const dataset = await this.getDatasetById({ id: input.datasetId }); if (!dataset) { throw new Error(`Dataset not found: ${input.datasetId}`); } return this._doBatchDeleteItems(input); } }; // src/storage/domains/datasets/inmemory.ts function toDatasetItem(row) { return { id: row.id, datasetId: row.datasetId, datasetVersion: row.datasetVersion, input: row.input, groundTruth: row.groundTruth, expectedTrajectory: row.expectedTrajectory, requestContext: row.requestContext, metadata: row.metadata, source: row.source, createdAt: row.createdAt, updatedAt: row.updatedAt }; } function toDatasetRecord(record) { return { ...record, inputSchema: record.inputSchema ?? void 0, groundTruthSchema: record.groundTruthSchema ?? void 0, requestContextSchema: record.requestContextSchema ?? void 0 }; } var DatasetsInMemory = class extends DatasetsStorage { db; constructor({ db }) { super(); this.db = db; } async dangerouslyClearAll() { this.db.datasets.clear(); this.db.datasetItems.clear(); this.db.datasetVersions.clear(); } // Dataset CRUD async createDataset(input) { const id = crypto.randomUUID(); const now = /* @__PURE__ */ new Date(); const dataset = { id, name: input.name, description: input.description, metadata: input.metadata, inputSchema: input.inputSchema, groundTruthSchema: input.groundTruthSchema, requestContextSchema: input.requestContextSchema, targetType: input.targetType, targetIds: input.targetIds, scorerIds: input.scorerIds ?? null, version: 0, createdAt: now, updatedAt: now }; this.db.datasets.set(id, dataset); return toDatasetRecord(dataset); } async getDatasetById({ id }) { const record = this.db.datasets.get(id); return record ? toDatasetRecord(record) : null; } async _doUpdateDataset(args) { const existing = this.db.datasets.get(args.id); if (!existing) { throw new Error(`Dataset not found: ${args.id}`); } const updated = { ...existing, name: args.name ?? existing.name, description: args.description ?? existing.description, metadata: args.metadata ?? existing.metadata, inputSchema: args.inputSchema !== void 0 ? args.inputSchema : existing.inputSchema, groundTruthSchema: args.groundTruthSchema !== void 0 ? args.groundTruthSchema : existing.groundTruthSchema, requestContextSchema: args.requestContextSchema !== void 0 ? args.requestContextSchema : existing.requestContextSchema, tags: args.tags !== void 0 ? args.tags : existing.tags, targetType: args.targetType !== void 0 ? args.targetType : existing.targetType, targetIds: args.targetIds !== void 0 ? args.targetIds : existing.targetIds, scorerIds: args.scorerIds !== void 0 ? args.scorerIds : existing.scorerIds, updatedAt: /* @__PURE__ */ new Date() }; this.db.datasets.set(args.id, updated); return toDatasetRecord(updated); } async deleteDataset({ id }) { for (const [itemId, rows] of this.db.datasetItems) { if (rows.length > 0 && rows[0].datasetId === id) { this.db.datasetItems.delete(itemId); } } for (const [vId, v] of this.db.datasetVersions) { if (v.datasetId === id) { this.db.datasetVersions.delete(vId); } } for (const [expId, exp] of this.db.experiments) { if (exp.datasetId === id) { this.db.experiments.set(expId, { ...exp, datasetId: null, datasetVersion: null }); } } this.db.datasets.delete(id); } async listDatasets(args) { const datasets = Array.from(this.db.datasets.values()); datasets.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); const { page, perPage: perPageInput } = args.pagination; const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, 100); const { offset: start, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); const end = perPageInput === false ? datasets.length : start + perPage; return { datasets: datasets.slice(start, end).map(toDatasetRecord), pagination: { total: datasets.length, page, perPage: perPageForResponse, hasMore: perPageInput === false ? false : datasets.length > end } }; } // --- SCD-2 item mutations --- async _doAddItem(args) { const dataset = this.db.datasets.get(args.datasetId); if (!dataset) { throw new Error(`Dataset not found: ${args.datasetId}`); } const newVersion = dataset.version + 1; this.db.datasets.set(args.datasetId, { ...dataset, version: newVersion }); const now = /* @__PURE__ */ new Date(); const id = crypto.randomUUID(); const row = { id, datasetId: args.datasetId, datasetVersion: newVersion, validTo: null, isDeleted: false, input: args.input, groundTruth: args.groundTruth, expectedTrajectory: args.expectedTrajectory, requestContext: args.requestContext, metadata: args.metadata, source: args.source, createdAt: now, updatedAt: now }; this.db.datasetItems.set(id, [row]); await this.createDatasetVersion(args.datasetId, newVersion); return toDatasetItem(row); } async _doUpdateItem(args) { const rows = this.db.datasetItems.get(args.id); if (!rows || rows.length === 0) { throw new Error(`Item not found: ${args.id}`); } const currentRow = rows.find((r) => r.validTo === null && !r.isDeleted); if (!currentRow) { throw new Error(`Item not found: ${args.id}`); } if (currentRow.datasetId !== args.datasetId) { throw new Error(`Item ${args.id} does not belong to dataset ${args.datasetId}`); } const dataset = this.db.datasets.get(args.datasetId); if (!dataset) { throw new Error(`Dataset not found: ${args.datasetId}`); } const newVersion = dataset.version + 1; this.db.datasets.set(args.datasetId, { ...dataset, version: newVersion }); currentRow.validTo = newVersion; const now = /* @__PURE__ */ new Date(); const newRow = { id: args.id, datasetId: args.datasetId, datasetVersion: newVersion, validTo: null, isDeleted: false, input: args.input !== void 0 ? args.input : currentRow.input, groundTruth: args.groundTruth !== void 0 ? args.groundTruth : currentRow.groundTruth, expectedTrajectory: args.expectedTrajectory !== void 0 ? args.expectedTrajectory : currentRow.expectedTrajectory, requestContext: args.requestContext !== void 0 ? args.requestContext : currentRow.requestContext, metadata: args.metadata !== void 0 ? args.metadata : currentRow.metadata, source: args.source !== void 0 ? args.source : currentRow.source, createdAt: currentRow.createdAt, updatedAt: now }; rows.push(newRow); await this.createDatasetVersion(args.datasetId, newVersion); return toDatasetItem(newRow); } async _doDeleteItem({ id, datasetId }) { const rows = this.db.datasetItems.get(id); if (!rows || rows.length === 0) { return; } const currentRow = rows.find((r) => r.validTo === null && !r.isDeleted); if (!currentRow) { return; } if (currentRow.datasetId !== datasetId) { throw new Error(`Item ${id} does not belong to dataset ${datasetId}`); } const dataset = this.db.datasets.get(datasetId); if (!dataset) { throw new Error(`Dataset not found: ${datasetId}`); } const newVersion = dataset.version + 1; this.db.datasets.set(datasetId, { ...dataset, version: newVersion }); currentRow.validTo = newVersion; const now = /* @__PURE__ */ new Date(); rows.push({ id, datasetId, datasetVersion: newVersion, validTo: null, isDeleted: true, input: currentRow.input, groundTruth: currentRow.groundTruth, requestContext: currentRow.requestContext, metadata: currentRow.metadata, createdAt: currentRow.createdAt, updatedAt: now }); await this.createDatasetVersion(datasetId, newVersion); } // --- SCD-2 queries --- async getItemById(args) { const rows = this.db.datasetItems.get(args.id); if (!rows || rows.length === 0) return null; if (args.datasetVersion !== void 0) { const row = rows.find((r) => r.datasetVersion === args.datasetVersion && !r.isDeleted); return row ? toDatasetItem(row) : null; } const current = rows.find((r) => r.validTo === null && !r.isDeleted); return current ? toDatasetItem(current) : null; } async getItemsByVersion({ datasetId, version }) { const items = []; for (const rows of this.db.datasetItems.values()) { if (rows.length === 0 || rows[0].datasetId !== datasetId) continue; const visible = rows.find( (r) => r.datasetVersion <= version && (r.validTo === null || r.validTo > version) && !r.isDeleted ); if (visible) { items.push(toDatasetItem(visible)); } } items.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime() || b.id.localeCompare(a.id)); return items; } async getItemHistory(itemId) { const rows = this.db.datasetItems.get(itemId); if (!rows) return []; return [...rows].sort((a, b) => b.datasetVersion - a.datasetVersion); } async listItems(args) { let items; if (args.version !== void 0) { items = await this.getItemsByVersion({ datasetId: args.datasetId, version: args.version }); } else { items = []; for (const rows of this.db.datasetItems.values()) { if (rows.length === 0 || rows[0].datasetId !== args.datasetId) continue; const current = rows.find((r) => r.validTo === null && !r.isDeleted); if (current) { items.push(toDatasetItem(current)); } } } if (args.search) { const searchLower = args.search.toLowerCase(); items = items.filter((item) => { const inputStr = typeof item.input === "string" ? item.input : JSON.stringify(item.input); const outputStr = item.groundTruth ? typeof item.groundTruth === "string" ? item.groundTruth : JSON.stringify(item.groundTruth) : ""; return inputStr.toLowerCase().includes(searchLower) || outputStr.toLowerCase().includes(searchLower); }); } items.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime() || b.id.localeCompare(a.id)); const { page, perPage: perPageInput } = args.pagination; const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, 100); const { offset: start, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); const end = perPageInput === false ? items.length : start + perPage; return { items: items.slice(start, end), pagination: { total: items.length, page, perPage: perPageForResponse, hasMore: perPageInput === false ? false : items.length > end } }; } // --- Dataset version methods --- async createDatasetVersion(datasetId, version) { const id = crypto.randomUUID(); const dsVersion = { id, datasetId, version, createdAt: /* @__PURE__ */ new Date() }; this.db.datasetVersions.set(id, dsVersion); return dsVersion; } async listDatasetVersions(input) { const versions = []; for (const v of this.db.datasetVersions.values()) { if (v.datasetId === input.datasetId) { versions.push(v); } } versions.sort((a, b) => b.version - a.version); const { page, perPage: perPageInput } = input.pagination; const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, 100); const { offset: start, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); const end = perPageInput === false ? versions.length : start + perPage; return { versions: versions.slice(start, end), pagination: { total: versions.length, page, perPage: perPageForResponse, hasMore: perPageInput === false ? false : versions.length > end } }; } // --- Bulk operations (SCD-2 internally) --- async _doBatchInsertItems(input) { const dataset = this.db.datasets.get(input.datasetId); if (!dataset) { throw new Error(`Dataset not found: ${input.datasetId}`); } const newVersion = dataset.version + 1; this.db.datasets.set(input.datasetId, { ...dataset, version: newVersion }); const now = /* @__PURE__ */ new Date(); const items = []; for (const itemInput of input.items) { const id = crypto.randomUUID(); const row = { id, datasetId: input.datasetId, datasetVersion: newVersion, validTo: null, isDeleted: false, input: itemInput.input, groundTruth: itemInput.groundTruth, expectedTrajectory: itemInput.expectedTrajectory, requestContext: itemInput.requestContext, metadata: itemInput.metadata, source: itemInput.source, createdAt: now, updatedAt: now }; this.db.datasetItems.set(id, [row]); items.push(toDatasetItem(row)); } await this.createDatasetVersion(input.datasetId, newVersion); return items; } async _doBatchDeleteItems(input) { const dataset = this.db.datasets.get(input.datasetId); if (!dataset) { throw new Error(`Dataset not found: ${input.datasetId}`); } const newVersion = dataset.version + 1; this.db.datasets.set(input.datasetId, { ...dataset, version: newVersion }); const now = /* @__PURE__ */ new Date(); for (const itemId of input.itemIds) { const rows = this.db.datasetItems.get(itemId); if (!rows) continue; const currentRow = rows.find((r) => r.validTo === null && !r.isDeleted); if (!currentRow || currentRow.datasetId !== input.datasetId) continue; currentRow.validTo = newVersion; rows.push({ id: itemId, datasetId: input.datasetId, datasetVersion: newVersion, validTo: null, isDeleted: true, input: currentRow.input, groundTruth: currentRow.groundTruth, requestContext: currentRow.requestContext, metadata: currentRow.metadata, createdAt: currentRow.createdAt, updatedAt: now }); } await this.createDatasetVersion(input.datasetId, newVersion); } }; // src/storage/domains/experiments/base.ts var ExperimentsStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "EXPERIMENTS" }); } async dangerouslyClearAll() { } }; // src/storage/domains/experiments/inmemory.ts var ExperimentsInMemory = class extends ExperimentsStorage { db; constructor({ db }) { super(); this.db = db; } async dangerouslyClearAll() { this.db.experiments.clear(); this.db.experimentResults.clear(); } // Experiment lifecycle async createExperiment(input) { const now = /* @__PURE__ */ new Date(); const experiment = { id: input.id ?? crypto.randomUUID(), datasetId: input.datasetId, datasetVersion: input.datasetVersion, agentVersion: input.agentVersion ?? null, targetType: input.targetType, targetId: input.targetId, name: input.name, description: input.description, metadata: input.metadata, status: "pending", totalItems: input.totalItems, succeededCount: 0, failedCount: 0, skippedCount: 0, startedAt: null, completedAt: null, createdAt: now, updatedAt: now }; this.db.experiments.set(experiment.id, experiment); return experiment; } async updateExperiment(input) { const existing = this.db.experiments.get(input.id); if (!existing) { throw new Error(`Experiment not found: ${input.id}`); } const updated = { ...existing, status: input.status ?? existing.status, totalItems: input.totalItems ?? existing.totalItems, succeededCount: input.succeededCount ?? existing.succeededCount, failedCount: input.failedCount ?? existing.failedCount, skippedCount: input.skippedCount ?? existing.skippedCount, startedAt: input.startedAt ?? existing.startedAt, completedAt: input.completedAt ?? existing.completedAt, name: input.name ?? existing.name, description: input.description ?? existing.description, metadata: input.metadata ?? existing.metadata, updatedAt: /* @__PURE__ */ new Date() }; this.db.experiments.set(input.id, updated); return updated; } async getExperimentById(args) { return this.db.experiments.get(args.id) ?? null; } async listExperiments(args) { let experiments = Array.from(this.db.experiments.values()); if (args.datasetId) { experiments = experiments.filter((r) => r.datasetId === args.datasetId); } if (args.targetType) { experiments = experiments.filter((r) => r.targetType === args.targetType); } if (args.targetId) { experiments = experiments.filter((r) => r.targetId === args.targetId); } if (args.agentVersion) { experiments = experiments.filter((r) => r.agentVersion === args.agentVersion); } if (args.status) { experiments = experiments.filter((r) => r.status === args.status); } experiments.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); const { page, perPage: perPageInput } = args.pagination; const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, 100); const { offset: start, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); const end = perPageInput === false ? experiments.length : start + perPage; return { experiments: experiments.slice(start, end), pagination: { total: experiments.length, page, perPage: perPageForResponse, hasMore: perPageInput === false ? false : experiments.length > end } }; } async deleteExperiment(args) { this.db.experiments.delete(args.id); for (const [resultId, result] of this.db.experimentResults) { if (result.experimentId === args.id) { this.db.experimentResults.delete(resultId); } } } // Results (per-item) async addExperimentResult(input) { const now = /* @__PURE__ */ new Date(); const result = { id: input.id ?? crypto.randomUUID(), experimentId: input.experimentId, itemId: input.itemId, itemDatasetVersion: input.itemDatasetVersion, input: input.input, output: input.output, groundTruth: input.groundTruth, error: input.error, startedAt: input.startedAt, completedAt: input.completedAt, retryCount: input.retryCount, traceId: input.traceId ?? null, status: input.status ?? null, tags: input.tags ?? null, createdAt: now }; this.db.experimentResults.set(result.id, result); return result; } async updateExperimentResult(input) { const existing = this.db.experimentResults.get(input.id); if (!existing) { throw new Error(`Experiment result not found: ${input.id}`); } if (input.experimentId && existing.experimentId !== input.experimentId) { throw new Error(`Experiment result ${input.id} does not belong to experiment ${input.experimentId}`); } const updated = { ...existing, status: input.status !== void 0 ? input.status : existing.status, tags: input.tags !== void 0 ? input.tags : existing.tags }; this.db.experimentResults.set(input.id, updated); return updated; } async getExperimentResultById(args) { return this.db.experimentResults.get(args.id) ?? null; } async listExperimentResults(args) { let results = Array.from(this.db.experimentResults.values()).filter((r) => r.experimentId === args.experimentId); if (args.traceId) { results = results.filter((r) => r.traceId === args.traceId); } if (args.status) { results = results.filter((r) => r.status === args.status); } results.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime()); const { page, perPage: perPageInput } = args.pagination; const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, 100); const { offset: start, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); const end = perPageInput === false ? results.length : start + perPage; return { results: results.slice(start, end), pagination: { total: results.length, page, perPage: perPageForResponse, hasMore: perPageInput === false ? false : results.length > end } }; } async deleteExperimentResults(args) { for (const [resultId, result] of this.db.experimentResults) { if (result.experimentId === args.experimentId) { this.db.experimentResults.delete(resultId); } } } async getReviewSummary() { const counts = /* @__PURE__ */ new Map(); for (const result of this.db.experimentResults.values()) { let entry = counts.get(result.experimentId); if (!entry) { entry = { experimentId: result.experimentId, total: 0, needsReview: 0, reviewed: 0, complete: 0 }; counts.set(result.experimentId, entry); } entry.total++; if (result.status === "needs-review") entry.needsReview++; else if (result.status === "reviewed") entry.reviewed++; else if (result.status === "complete") entry.complete++; } return Array.from(counts.values()); } }; // src/storage/domains/harness/base.ts var HarnessStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "HARNESS" }); } async updateSession(sessionId, updates) { const record = await this.loadSession(sessionId); if (!record) { throw new Error(`Harness session "${sessionId}" was not found`); } const next = { ...record, ...updates, id: record.id, createdAt: record.createdAt, lastActivityAt: updates.lastActivityAt ?? /* @__PURE__ */ new Date() }; await this.saveSession(next); return next; } async appendPendingItem(sessionId, item) { const record = await this.loadSession(sessionId); if (!record) { throw new Error(`Harness session "${sessionId}" was not found`); } if (record.pending?.some((existing) => existing.id === item.id)) { throw new Error(`Harness pending item "${item.id}" already exists on session "${sessionId}"`); } return this.updateSession(sessionId, { pending: [...record.pending ?? [], item] }); } async updatePendingItem(sessionId, pendingItemId, updates) { const record = await this.loadSession(sessionId); if (!record) { throw new Error(`Harness session "${sessionId}" was not found`); } let found = false; const pending = (record.pending ?? []).map((item) => { if (item.id !== pendingItemId) return item; found = true; return { ...item, ...updates, id: item.id, sessionId: item.sessionId, createdAt: item.createdAt, updatedAt: /* @__PURE__ */ new Date() }; }); if (!found) { throw new Error(`Harness pending item "${pendingItemId}" was not found on session "${sessionId}"`); } return this.updateSession(sessionId, { pending }); } async removePendingItem(sessionId, pendingItemId) { const record = await this.loadSession(sessionId); if (!record) { throw new Error(`Harness session "${sessionId}" was not found`); } return this.updateSession(sessionId, { pending: (record.pending ?? []).filter((item) => item.id !== pendingItemId) }); } }; // src/storage/domains/harness/inmemory.ts function clonePendingItemRecord(item) { return { ...item, createdAt: new Date(item.createdAt), updatedAt: new Date(item.updatedAt), payload: item.payload ? structuredClone(item.payload) : void 0, response: item.response ? structuredClone(item.response) : void 0 }; } function cloneSessionRecord(record) { return { ...record, source: record.source ? { ...record.source } : void 0, metadata: record.metadata ? structuredClone(record.metadata) : void 0, state: record.state ? structuredClone(record.state) : void 0, pending: record.pending ? record.pending.map(clonePendingItemRecord) : void 0, createdAt: new Date(record.createdAt), lastActivityAt: new Date(record.lastActivityAt), closingAt: record.closingAt ? new Date(record.closingAt) : record.closingAt, closeDeadlineAt: record.closeDeadlineAt ? new Date(record.closeDeadlineAt) : record.closeDeadlineAt, closedAt: record.closedAt ? new Date(record.closedAt) : record.closedAt, deletedAt: record.deletedAt ? new Date(record.deletedAt) : record.deletedAt }; } var InMemoryHarness = class extends HarnessStorage { #sessions = /* @__PURE__ */ new Map(); async dangerouslyClearAll() { this.#sessions.clear(); } async loadSession(sessionId) { const record = this.#sessions.get(sessionId); return record ? cloneSessionRecord(record) : null; } async saveSession(record) { this.#sessions.set(record.id, cloneSessionRecord(record)); } async listSessions() { return [...this.#sessions.values()].map(cloneSessionRecord); } async updateSession(sessionId, updates) { const record = this.#sessions.get(sessionId); if (!record) { throw new Error(`Harness session "${sessionId}" was not found`); } const next = cloneSessionRecord({ ...record, ...updates, id: record.id, createdAt: record.createdAt, lastActivityAt: updates.lastActivityAt ?? /* @__PURE__ */ new Date() }); this.#sessions.set(sessionId, next); return cloneSessionRecord(next); } }; // src/storage/domains/memory/base.ts function isPlainObj(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } var SAFE_METADATA_KEY_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/; var MAX_METADATA_KEY_LENGTH = 128; var DISALLOWED_METADATA_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]); var MemoryStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { /** * Whether this storage adapter supports Observational Memory. * Adapters that implement OM methods should set this to true. * Defaults to false for backwards compatibility with custom adapters. */ supportsObservationalMemory = false; constructor() { super({ component: "STORAGE", name: "MEMORY" }); } /** * List messages by resource ID only (across all threads). * Used by Observational Memory and LongMemEval for resource-scoped queries. * * @param args - Resource ID and pagination/filtering options * @returns Paginated list of messages for the resource */ async listMessagesByResourceId(_args) { throw new Error( `Resource-scoped message listing is not implemented by this storage adapter (${this.constructor.name}). Use an adapter that supports Observational Memory (pg, libsql, mongodb) or disable observational memory.` ); } async deleteMessages(_messageIds) { throw new Error( `Message deletion is not supported by this storage adapter (${this.constructor.name}). The deleteMessages method needs to be implemented in the storage adapter.` ); } /** * Clone a thread and its messages to create a new independent thread. * The cloned thread will have clone metadata stored in its metadata field. * * @param args - Clone configuration options * @returns The newly created thread and the cloned messages */ async cloneThread(_args) { throw new Error( `Thread cloning is not implemented by this storage adapter (${this.constructor.name}). The cloneThread method needs to be implemented in the storage adapter.` ); } async getResourceById(_) { throw new Error( `Resource working memory is not implemented by this storage adapter (${this.constructor.name}). This is likely a bug - all Mastra storage adapters should implement resource support. Please report this issue at https://github.com/mastra-ai/mastra/issues` ); } async saveResource(_) { throw new Error( `Resource working memory is not implemented by this storage adapter (${this.constructor.name}). This is likely a bug - all Mastra storage adapters should implement resource support. Please report this issue at https://github.com/mastra-ai/mastra/issues` ); } async updateResource(_) { throw new Error( `Resource working memory is not implemented by this storage adapter (${this.constructor.name}). This is likely a bug - all Mastra storage adapters should implement resource support. Please report this issue at https://github.com/mastra-ai/mastra/issues` ); } parseOrderBy(orderBy, defaultDirection = "DESC") { return { field: orderBy?.field && orderBy.field in THREAD_ORDER_BY_SET ? orderBy.field : "createdAt", direction: orderBy?.direction && orderBy.direction in THREAD_THREAD_SORT_DIRECTION_SET ? orderBy.direction : defaultDirection }; } // ============================================ // Observational Memory Methods // ============================================ /** * Get the current observational memory record for a thread/resource. * Returns the most recent active record. */ async getObservationalMemory(_threadId, _resourceId) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Get observational memory history (previous generations). * Returns records in reverse chronological order (newest first). */ async getObservationalMemoryHistory(_threadId, _resourceId, _limit, _options) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Create a new observational memory record. * Called when starting observations for a new thread/resource. */ async initializeObservationalMemory(_input) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Update active observations. * Called when observations are created and immediately activated (no buffering). */ async updateActiveObservations(_input) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } // ============================================ // Buffering Methods (for async observation/reflection) // These methods support async buffering when `bufferTokens` is configured. // ============================================ /** * Update buffered observations. * Called when observations are created asynchronously via `bufferTokens`. */ async updateBufferedObservations(_input) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Swap buffered observations to active. * Atomic operation that: * 1. Appends bufferedObservations → activeObservations (based on activationRatio) * 2. Moves activated bufferedMessageIds → observedMessageIds * 3. Keeps remaining buffered content if activationRatio < 100 * 4. Updates lastObservedAt * * Returns info about what was activated for UI feedback. */ async swapBufferedToActive(_input) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Create a new generation from a reflection. * Creates a new record with: * - originType: 'reflection' * - activeObservations containing the reflection * - generationCount incremented from the current record */ async createReflectionGeneration(_input) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Update buffered reflection (async reflection in progress). * Called when reflection runs asynchronously via `bufferTokens`. */ async updateBufferedReflection(_input) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Swap buffered reflection to active observations. * Creates a new generation where activeObservations = bufferedReflection + unreflected observations. * The `tokenCount` in input is the processor-computed token count for the combined content. */ async swapBufferedReflectionToActive(_input) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Set the isReflecting flag. */ async setReflectingFlag(_id, _isReflecting) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Set the isObserving flag. */ async setObservingFlag(_id, _isObserving) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Set the isBufferingObservation flag and update lastBufferedAtTokens. * Called when async observation buffering starts (true) or ends/fails (false). * @param id - Record ID * @param isBuffering - Whether buffering is in progress * @param lastBufferedAtTokens - The pending token count at which this buffer was triggered (only set when isBuffering=true) */ async setBufferingObservationFlag(_id, _isBuffering, _lastBufferedAtTokens) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Set the isBufferingReflection flag. * Called when async reflection buffering starts (true) or ends/fails (false). */ async setBufferingReflectionFlag(_id, _isBuffering) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Insert a fully-formed observational memory record. * Used by thread cloning to copy OM state with remapped IDs. */ async insertObservationalMemoryRecord(_record) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Clear all observational memory for a thread/resource. * Removes all records and history. */ async clearObservationalMemory(_threadId, _resourceId) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Set the pending message token count. * Called at the end of each OM processing step to persist the current * context window token count so the UI can display it on page load. */ async setPendingMessageTokens(_id, _tokenCount) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Update the config of an existing observational memory record. * The provided config is deep-merged into the record's existing config. */ async updateObservationalMemoryConfig(_input) { throw new Error(`Observational memory is not implemented by this storage adapter (${this.constructor.name}).`); } /** * Deep-merge two plain objects. Available for subclasses to merge * partial config overrides into existing record configs. */ deepMergeConfig(target, source) { const output = { ...target }; for (const key of Object.keys(source)) { const tVal = target[key]; const sVal = source[key]; if (isPlainObj(tVal) && isPlainObj(sVal)) { output[key] = this.deepMergeConfig(tVal, sVal); } else if (sVal !== void 0) { output[key] = sVal; } } return output; } /** * Validates metadata keys to prevent SQL injection attacks and prototype pollution. * Keys must start with a letter or underscore, followed by alphanumeric characters or underscores. * @param metadata - The metadata object to validate * @throws Error if any key contains invalid characters or is a disallowed key */ validateMetadataKeys(metadata) { if (!metadata) return; for (const key of Object.keys(metadata)) { if (DISALLOWED_METADATA_KEYS.has(key)) { throw new Error(`Invalid metadata key: "${key}".`); } if (!SAFE_METADATA_KEY_PATTERN.test(key)) { throw new Error( `Invalid metadata key: "${key}". Keys must start with a letter or underscore and contain only alphanumeric characters and underscores.` ); } if (key.length > MAX_METADATA_KEY_LENGTH) { throw new Error(`Metadata key "${key}" exceeds maximum length of ${MAX_METADATA_KEY_LENGTH} characters.`); } } } /** * Validates pagination parameters and returns safe offset. * @param page - Page number (0-indexed) * @param perPage - Items per page (0 is allowed and returns empty results) * @throws Error if page is negative, perPage is negative/invalid, or offset would overflow */ validatePagination(page, perPage) { if (!Number.isFinite(page) || !Number.isSafeInteger(page) || page < 0) { throw new Error("page must be >= 0"); } if (!Number.isFinite(perPage) || !Number.isSafeInteger(perPage) || perPage < 0) { throw new Error("perPage must be >= 0"); } if (perPage === 0) { return; } const offset = page * perPage; if (!Number.isSafeInteger(offset) || offset > Number.MAX_SAFE_INTEGER) { throw new Error("page value too large"); } } /** * Validates pagination input before normalization. * Use this when accepting raw perPageInput (number | false) from callers. * * When perPage is false (fetch all), page must be 0 since pagination is disabled. * When perPage is a number, delegates to validatePagination for full validation. * * @param page - Page number (0-indexed) * @param perPageInput - Items per page as number, or false to fetch all results * @throws Error if perPageInput is false and page !== 0 * @throws Error if perPageInput is invalid (not false or a non-negative safe integer) * @throws Error if page is invalid or offset would overflow */ validatePaginationInput(page, perPageInput) { if (perPageInput !== false) { if (typeof perPageInput !== "number" || !Number.isFinite(perPageInput) || !Number.isSafeInteger(perPageInput)) { throw new Error("perPage must be false or a safe integer"); } if (perPageInput < 0) { throw new Error("perPage must be >= 0"); } } if (perPageInput === false) { if (page !== 0) { throw new Error("page must be 0 when perPage is false"); } if (!Number.isFinite(page) || !Number.isSafeInteger(page)) { throw new Error("page must be >= 0"); } return; } this.validatePagination(page, perPageInput); } }; var THREAD_ORDER_BY_SET = { createdAt: true, updatedAt: true }; var THREAD_THREAD_SORT_DIRECTION_SET = { ASC: true, DESC: true }; // src/storage/domains/memory/inmemory.ts var InMemoryMemory = class extends MemoryStorage { supportsObservationalMemory = true; db; constructor({ db }) { super(); this.db = db; } async dangerouslyClearAll() { this.db.threads.clear(); this.db.messages.clear(); this.db.resources.clear(); this.db.observationalMemory.clear(); } async getThreadById({ threadId, resourceId }) { const thread = this.db.threads.get(threadId); if (!thread || resourceId !== void 0 && thread.resourceId !== resourceId) return null; return { ...thread, metadata: thread.metadata ? { ...thread.metadata } : thread.metadata }; } async saveThread({ thread }) { const key = thread.id; this.db.threads.set(key, thread); return thread; } async updateThread({ id, title, metadata }) { const thread = this.db.threads.get(id); if (!thread) { throw new Error(`Thread with id ${id} not found`); } if (thread) { thread.title = title; thread.metadata = { ...thread.metadata, ...metadata }; thread.updatedAt = /* @__PURE__ */ new Date(); } return thread; } async deleteThread({ threadId }) { this.db.threads.delete(threadId); this.db.messages.forEach((msg, key) => { if (msg.thread_id === threadId) { this.db.messages.delete(key); } }); } async listMessages({ threadId, resourceId: optionalResourceId, include, filter, perPage: perPageInput, page = 0, orderBy }) { const threadIds = Array.isArray(threadId) ? threadId : [threadId]; if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) { throw new Error("threadId must be a non-empty string or array of non-empty strings"); } const threadIdSet = new Set(threadIds); const { field, direction } = this.parseOrderBy(orderBy, "ASC"); const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, 40); if (page < 0) { throw new Error("page must be >= 0"); } const maxOffset = Number.MAX_SAFE_INTEGER / 2; if (page * perPage > maxOffset) { throw new Error("page value too large"); } const { offset, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); if (perPage === 0 && (!include || include.length === 0)) { return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false }; } let threadMessages = Array.from(this.db.messages.values()).filter((msg) => { if (threadIdSet && !threadIdSet.has(msg.thread_id)) return false; if (optionalResourceId && msg.resourceId !== optionalResourceId) return false; return true; }); threadMessages = filterByDateRange(threadMessages, (msg) => new Date(msg.createdAt), filter?.dateRange); threadMessages.sort((a, b) => { const isDateField = field === "createdAt" || field === "updatedAt"; const aValue = isDateField ? new Date(a[field]).getTime() : a[field]; const bValue = isDateField ? new Date(b[field]).getTime() : b[field]; if (typeof aValue === "number" && typeof bValue === "number") { return direction === "ASC" ? aValue - bValue : bValue - aValue; } return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue)); }); const totalThreadMessages = perPage === 0 ? 0 : threadMessages.length; const paginatedThreadMessages = perPage === 0 ? [] : threadMessages.slice(offset, offset + perPage); const messages = []; const messageIds = /* @__PURE__ */ new Set(); for (const msg of paginatedThreadMessages) { const convertedMessage = this.parseStoredMessage(msg); messages.push(convertedMessage); messageIds.add(msg.id); } if (include && include.length > 0) { for (const includeItem of include) { const targetMessage = this.db.messages.get(includeItem.id); if (targetMessage) { const convertedMessage = { id: targetMessage.id, threadId: targetMessage.thread_id, content: safelyParseJSON(targetMessage.content), role: targetMessage.role, type: targetMessage.type, createdAt: targetMessage.createdAt, resourceId: targetMessage.resourceId }; if (!messageIds.has(convertedMessage.id)) { messages.push(convertedMessage); messageIds.add(convertedMessage.id); } if (includeItem.withPreviousMessages) { const allThreadMessages = Array.from(this.db.messages.values()).filter((msg) => msg.thread_id === (includeItem.threadId || threadId)).sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()); const targetIndex = allThreadMessages.findIndex((msg) => msg.id === includeItem.id); if (targetIndex !== -1) { const startIndex = Math.max(0, targetIndex - (includeItem.withPreviousMessages || 0)); for (let i = startIndex; i < targetIndex; i++) { const message = allThreadMessages[i]; if (message && !messageIds.has(message.id)) { const convertedPrevMessage = { id: message.id, threadId: message.thread_id, content: safelyParseJSON(message.content), role: message.role, type: message.type, createdAt: message.createdAt, resourceId: message.resourceId }; messages.push(convertedPrevMessage); messageIds.add(message.id); } } } } if (includeItem.withNextMessages) { const allThreadMessages = Array.from(this.db.messages.values()).filter((msg) => msg.thread_id === (includeItem.threadId || threadId)).sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()); const targetIndex = allThreadMessages.findIndex((msg) => msg.id === includeItem.id); if (targetIndex !== -1) { const endIndex = Math.min( allThreadMessages.length, targetIndex + (includeItem.withNextMessages || 0) + 1 ); for (let i = targetIndex + 1; i < endIndex; i++) { const message = allThreadMessages[i]; if (message && !messageIds.has(message.id)) { const convertedNextMessage = { id: message.id, threadId: message.thread_id, content: safelyParseJSON(message.content), role: message.role, type: message.type, createdAt: message.createdAt, resourceId: message.resourceId }; messages.push(convertedNextMessage); messageIds.add(message.id); } } } } } } } messages.sort((a, b) => { const isDateField = field === "createdAt" || field === "updatedAt"; const aValue = isDateField ? new Date(a[field]).getTime() : a[field]; const bValue = isDateField ? new Date(b[field]).getTime() : b[field]; if (typeof aValue === "number" && typeof bValue === "number") { return direction === "ASC" ? aValue - bValue : bValue - aValue; } return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue)); }); let hasMore; if (perPage === 0) { hasMore = false; } else if (include && include.length > 0) { const returnedThreadMessageIds = new Set(messages.filter((m) => m.threadId === threadId).map((m) => m.id)); hasMore = returnedThreadMessageIds.size < totalThreadMessages; } else { hasMore = offset + perPage < totalThreadMessages; } return { messages, total: totalThreadMessages, page, perPage: perPageForResponse, hasMore }; } async listMessagesByResourceId({ resourceId, filter, perPage: perPageInput, page = 0, orderBy }) { const { field, direction } = this.parseOrderBy(orderBy, "ASC"); const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, 40); if (page < 0) { throw new Error("page must be >= 0"); } const maxOffset = Number.MAX_SAFE_INTEGER / 2; if (page * perPage > maxOffset) { throw new Error("page value too large"); } const { offset, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); let messages = Array.from(this.db.messages.values()).filter((msg) => msg.resourceId === resourceId); messages = filterByDateRange(messages, (msg) => new Date(msg.createdAt), filter?.dateRange); messages.sort((a, b) => { const isDateField = field === "createdAt" || field === "updatedAt"; const aValue = isDateField ? new Date(a[field]).getTime() : a[field]; const bValue = isDateField ? new Date(b[field]).getTime() : b[field]; if (typeof aValue === "number" && typeof bValue === "number") { return direction === "ASC" ? aValue - bValue : bValue - aValue; } return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue)); }); const total = messages.length; const paginatedMessages = messages.slice(offset, offset + perPage); const list = new chunk2TATDSHU_cjs.MessageList().add( paginatedMessages.map((m) => this.parseStoredMessage(m)), "memory" ); const hasMore = offset + paginatedMessages.length < total; return { messages: list.get.all.db(), total, page, perPage: perPageForResponse, hasMore }; } parseStoredMessage(message) { const { resourceId, content, role, thread_id, ...rest } = message; let parsedContent = safelyParseJSON(content); if (typeof parsedContent === "string") { parsedContent = { format: 2, content: parsedContent, parts: [{ type: "text", text: parsedContent }] }; } return { ...rest, threadId: thread_id, ...message.resourceId && { resourceId: message.resourceId }, content: parsedContent, role }; } async listMessagesById({ messageIds }) { const rawMessages = messageIds.map((id) => this.db.messages.get(id)).filter((message) => !!message); const list = new chunk2TATDSHU_cjs.MessageList().add( rawMessages.map((m) => this.parseStoredMessage(m)), "memory" ); return { messages: list.get.all.db() }; } async saveMessages(args) { const { messages } = args; if (messages.some((msg) => msg.id === "error-message" || msg.resourceId === null)) { throw new Error("Simulated error for testing"); } const threadIds = new Set(messages.map((msg) => msg.threadId).filter((id) => Boolean(id))); for (const threadId of threadIds) { const thread = this.db.threads.get(threadId); if (thread) { thread.updatedAt = /* @__PURE__ */ new Date(); } } for (const message of messages) { const key = message.id; const storageMessage = { id: message.id, thread_id: message.threadId || "", content: JSON.stringify(message.content), role: message.role || "user", type: message.type || "text", createdAt: message.createdAt, resourceId: message.resourceId || null }; this.db.messages.set(key, storageMessage); } const list = new chunk2TATDSHU_cjs.MessageList().add(messages, "memory"); return { messages: list.get.all.db() }; } async updateMessages(args) { const updatedMessages = []; for (const update of args.messages) { const storageMsg = this.db.messages.get(update.id); if (!storageMsg) continue; const oldThreadId = storageMsg.thread_id; const newThreadId = update.threadId || oldThreadId; let threadIdChanged = false; if (update.threadId && update.threadId !== oldThreadId) { threadIdChanged = true; } if (update.role !== void 0) storageMsg.role = update.role; if (update.type !== void 0) storageMsg.type = update.type; if (update.createdAt !== void 0) storageMsg.createdAt = update.createdAt; if (update.resourceId !== void 0) storageMsg.resourceId = update.resourceId; if (update.content !== void 0) { let oldContent = safelyParseJSON(storageMsg.content); let newContent = update.content; if (typeof newContent === "object" && typeof oldContent === "object") { newContent = { ...oldContent, ...newContent }; if (oldContent.metadata && newContent.metadata) { newContent.metadata = { ...oldContent.metadata, ...newContent.metadata }; } } storageMsg.content = JSON.stringify(newContent); } if (threadIdChanged) { storageMsg.thread_id = newThreadId; const base = Date.now(); let oldThreadNewTime; const oldThread = this.db.threads.get(oldThreadId); if (oldThread) { const prev = new Date(oldThread.updatedAt).getTime(); oldThreadNewTime = Math.max(base, prev + 1); oldThread.updatedAt = new Date(oldThreadNewTime); } const newThread = this.db.threads.get(newThreadId); if (newThread) { const prev = new Date(newThread.updatedAt).getTime(); let newThreadNewTime = Math.max(base + 1, prev + 1); if (oldThreadNewTime !== void 0 && newThreadNewTime <= oldThreadNewTime) { newThreadNewTime = oldThreadNewTime + 1; } newThread.updatedAt = new Date(newThreadNewTime); } } else { const thread = this.db.threads.get(oldThreadId); if (thread) { const prev = new Date(thread.updatedAt).getTime(); let newTime = Date.now(); if (newTime <= prev) newTime = prev + 1; thread.updatedAt = new Date(newTime); } } this.db.messages.set(update.id, storageMsg); updatedMessages.push({ id: storageMsg.id, threadId: storageMsg.thread_id, content: safelyParseJSON(storageMsg.content), role: storageMsg.role === "user" || storageMsg.role === "assistant" ? storageMsg.role : "user", type: storageMsg.type, createdAt: storageMsg.createdAt, resourceId: storageMsg.resourceId === null ? void 0 : storageMsg.resourceId }); } return updatedMessages; } async deleteMessages(messageIds) { if (!messageIds || messageIds.length === 0) { return; } const threadIds = /* @__PURE__ */ new Set(); for (const messageId of messageIds) { const message = this.db.messages.get(messageId); if (message && message.thread_id) { threadIds.add(message.thread_id); } this.db.messages.delete(messageId); } const now = /* @__PURE__ */ new Date(); for (const threadId of threadIds) { const thread = this.db.threads.get(threadId); if (thread) { thread.updatedAt = now; } } } async listThreads(args) { const { page = 0, perPage: perPageInput, orderBy, filter } = args; const { field, direction } = this.parseOrderBy(orderBy); this.validatePaginationInput(page, perPageInput ?? 100); const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, 100); let threads = Array.from(this.db.threads.values()); if (filter?.resourceId) { threads = threads.filter((t) => t.resourceId === filter.resourceId); } this.validateMetadataKeys(filter?.metadata); if (filter?.metadata && Object.keys(filter.metadata).length > 0) { threads = threads.filter((thread) => { if (!thread.metadata) return false; return Object.entries(filter.metadata).every(([key, value]) => jsonValueEquals(thread.metadata[key], value)); }); } const sortedThreads = this.sortThreads(threads, field, direction); const clonedThreads = sortedThreads.map((thread) => ({ ...thread, metadata: thread.metadata ? { ...thread.metadata } : thread.metadata })); const { offset, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); return { threads: clonedThreads.slice(offset, offset + perPage), total: clonedThreads.length, page, perPage: perPageForResponse, hasMore: offset + perPage < clonedThreads.length }; } async getResourceById({ resourceId }) { const resource = this.db.resources.get(resourceId); return resource ? { ...resource, metadata: resource.metadata ? { ...resource.metadata } : resource.metadata } : null; } async saveResource({ resource }) { this.db.resources.set(resource.id, resource); return resource; } async updateResource({ resourceId, workingMemory, metadata }) { let resource = this.db.resources.get(resourceId); if (!resource) { resource = { id: resourceId, workingMemory, metadata: metadata || {}, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() }; } else { resource = { ...resource, workingMemory: workingMemory !== void 0 ? workingMemory : resource.workingMemory, metadata: { ...resource.metadata, ...metadata }, updatedAt: /* @__PURE__ */ new Date() }; } this.db.resources.set(resourceId, resource); return resource; } async cloneThread(args) { const { sourceThreadId, newThreadId: providedThreadId, resourceId, title, metadata, options } = args; const sourceThread = this.db.threads.get(sourceThreadId); if (!sourceThread) { throw new Error(`Source thread with id ${sourceThreadId} not found`); } const newThreadId = providedThreadId || crypto.randomUUID(); if (this.db.threads.has(newThreadId)) { throw new Error(`Thread with id ${newThreadId} already exists`); } let sourceMessages = Array.from(this.db.messages.values()).filter((msg) => msg.thread_id === sourceThreadId).sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()); if (options?.messageFilter) { const { startDate, endDate, messageIds } = options.messageFilter; if (messageIds && messageIds.length > 0) { const messageIdSet = new Set(messageIds); sourceMessages = sourceMessages.filter((msg) => messageIdSet.has(msg.id)); } if (startDate) { sourceMessages = sourceMessages.filter((msg) => new Date(msg.createdAt) >= startDate); } if (endDate) { sourceMessages = sourceMessages.filter((msg) => new Date(msg.createdAt) <= endDate); } } if (options?.messageLimit && options.messageLimit > 0 && sourceMessages.length > options.messageLimit) { sourceMessages = sourceMessages.slice(-options.messageLimit); } const now = /* @__PURE__ */ new Date(); const lastMessageId = sourceMessages.length > 0 ? sourceMessages[sourceMessages.length - 1].id : void 0; const cloneMetadata = { sourceThreadId, clonedAt: now, ...lastMessageId && { lastMessageId } }; const newThread = { id: newThreadId, resourceId: resourceId || sourceThread.resourceId, title: title || (sourceThread.title ? `Clone of ${sourceThread.title}` : void 0), metadata: { ...metadata, clone: cloneMetadata }, createdAt: now, updatedAt: now }; this.db.threads.set(newThreadId, newThread); const clonedMessages = []; const messageIdMap = {}; for (const sourceMsg of sourceMessages) { const newMessageId = crypto.randomUUID(); messageIdMap[sourceMsg.id] = newMessageId; const parsedContent = safelyParseJSON(sourceMsg.content); const newStorageMessage = { id: newMessageId, thread_id: newThreadId, content: sourceMsg.content, role: sourceMsg.role, type: sourceMsg.type, createdAt: sourceMsg.createdAt, resourceId: resourceId || sourceMsg.resourceId }; this.db.messages.set(newMessageId, newStorageMessage); clonedMessages.push({ id: newMessageId, threadId: newThreadId, content: parsedContent, role: sourceMsg.role, type: sourceMsg.type, createdAt: sourceMsg.createdAt, resourceId: resourceId || sourceMsg.resourceId || void 0 }); } return { thread: newThread, clonedMessages, messageIdMap }; } sortThreads(threads, field, direction) { return threads.sort((a, b) => { const isDateField = field === "createdAt" || field === "updatedAt"; const aValue = isDateField ? new Date(a[field]).getTime() : a[field]; const bValue = isDateField ? new Date(b[field]).getTime() : b[field]; if (typeof aValue === "number" && typeof bValue === "number") { if (direction === "ASC") { return aValue - bValue; } else { return bValue - aValue; } } return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue)); }); } // ============================================ // Observational Memory Implementation // ============================================ getObservationalMemoryKey(threadId, resourceId) { if (threadId) { return `thread:${threadId}`; } return `resource:${resourceId}`; } async getObservationalMemory(threadId, resourceId) { const key = this.getObservationalMemoryKey(threadId, resourceId); const records = this.db.observationalMemory.get(key); return records?.[0] ?? null; } async getObservationalMemoryHistory(threadId, resourceId, limit, options) { const key = this.getObservationalMemoryKey(threadId, resourceId); let records = this.db.observationalMemory.get(key) ?? []; if (options?.from) { records = records.filter((r) => r.createdAt >= options.from); } if (options?.to) { records = records.filter((r) => r.createdAt <= options.to); } if (options?.offset != null) { records = records.slice(options.offset); } return limit != null ? records.slice(0, limit) : records; } async initializeObservationalMemory(input) { const { threadId, resourceId, scope, config, observedTimezone } = input; const key = this.getObservationalMemoryKey(threadId, resourceId); const now = /* @__PURE__ */ new Date(); const record = { id: crypto.randomUUID(), scope, threadId, resourceId, // Timestamps at top level createdAt: now, updatedAt: now, // lastObservedAt starts undefined - all messages are "unobserved" initially // This ensures historical data (like LongMemEval fixtures) works correctly lastObservedAt: void 0, originType: "initial", generationCount: 0, activeObservations: "", // Buffering (for async observation/reflection) bufferedObservations: void 0, bufferedReflection: void 0, // Message tracking // Note: Message ID tracking removed in favor of cursor-based lastObservedAt // Token tracking totalTokensObserved: 0, observationTokenCount: 0, pendingMessageTokens: 0, // State flags isReflecting: false, isObserving: false, isBufferingObservation: false, isBufferingReflection: false, lastBufferedAtTokens: 0, lastBufferedAtTime: null, // Configuration config, // Timezone used for observation date formatting observedTimezone, // Extensible metadata (optional) metadata: {} }; const existing = this.db.observationalMemory.get(key) ?? []; this.db.observationalMemory.set(key, [record, ...existing]); return record; } async insertObservationalMemoryRecord(record) { const key = this.getObservationalMemoryKey(record.threadId, record.resourceId); const existing = this.db.observationalMemory.get(key) ?? []; let inserted = false; for (let i = 0; i < existing.length; i++) { if (record.generationCount >= existing[i].generationCount) { existing.splice(i, 0, record); inserted = true; break; } } if (!inserted) existing.push(record); this.db.observationalMemory.set(key, existing); } async updateActiveObservations(input) { const { id, observations, tokenCount, lastObservedAt, observedMessageIds } = input; const record = this.findObservationalMemoryRecordById(id); if (!record) { throw new Error(`Observational memory record not found: ${id}`); } record.activeObservations = observations; record.observationTokenCount = tokenCount; record.totalTokensObserved += tokenCount; record.pendingMessageTokens = 0; record.lastObservedAt = lastObservedAt; record.updatedAt = /* @__PURE__ */ new Date(); if (observedMessageIds) { record.observedMessageIds = observedMessageIds; } } async updateBufferedObservations(input) { const { id, chunk } = input; const record = this.findObservationalMemoryRecordById(id); if (!record) { throw new Error(`Observational memory record not found: ${id}`); } const newChunk = { id: `ombuf-${crypto.randomUUID()}`, cycleId: chunk.cycleId, observations: chunk.observations, tokenCount: chunk.tokenCount, messageIds: chunk.messageIds, messageTokens: chunk.messageTokens, lastObservedAt: chunk.lastObservedAt, createdAt: /* @__PURE__ */ new Date(), suggestedContinuation: chunk.suggestedContinuation, currentTask: chunk.currentTask, threadTitle: chunk.threadTitle }; const existingChunks = Array.isArray(record.bufferedObservationChunks) ? record.bufferedObservationChunks : []; record.bufferedObservationChunks = [...existingChunks, newChunk]; if (input.lastBufferedAtTime) { record.lastBufferedAtTime = input.lastBufferedAtTime; } record.updatedAt = /* @__PURE__ */ new Date(); } async swapBufferedToActive(input) { const { id, activationRatio, lastObservedAt } = input; const record = this.findObservationalMemoryRecordById(id); if (!record) { throw new Error(`Observational memory record not found: ${id}`); } const persistedChunks = Array.isArray(record.bufferedObservationChunks) ? record.bufferedObservationChunks : []; const chunks = Array.isArray(input.bufferedChunks) ? input.bufferedChunks : persistedChunks; if (chunks.length === 0) { return { chunksActivated: 0, messageTokensActivated: 0, observationTokensActivated: 0, messagesActivated: 0, activatedCycleIds: [], activatedMessageIds: [] }; } const retentionFloor = input.messageTokensThreshold * (1 - activationRatio); const targetMessageTokens = Math.max(0, input.currentPendingTokens - retentionFloor); let cumulativeMessageTokens = 0; let bestOverBoundary = 0; let bestOverTokens = 0; let bestUnderBoundary = 0; let bestUnderTokens = 0; for (let i = 0; i < chunks.length; i++) { cumulativeMessageTokens += chunks[i].messageTokens ?? 0; const boundary = i + 1; if (cumulativeMessageTokens >= targetMessageTokens) { if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) { bestOverBoundary = boundary; bestOverTokens = cumulativeMessageTokens; } } else { if (cumulativeMessageTokens > bestUnderTokens) { bestUnderBoundary = boundary; bestUnderTokens = cumulativeMessageTokens; } } } const maxOvershoot = retentionFloor * 0.95; const overshoot = bestOverTokens - targetMessageTokens; const remainingAfterOver = input.currentPendingTokens - bestOverTokens; const remainingAfterUnder = input.currentPendingTokens - bestUnderTokens; const minRemaining = Math.min(1e3, retentionFloor); let chunksToActivate; if (input.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) { chunksToActivate = bestOverBoundary; } else if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) { chunksToActivate = bestOverBoundary; } else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) { chunksToActivate = bestUnderBoundary; } else if (bestOverBoundary > 0) { chunksToActivate = bestOverBoundary; } else { chunksToActivate = 1; } const activatedChunks = chunks.slice(0, chunksToActivate); const remainingChunks = chunks.slice(chunksToActivate); const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n"); const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0); const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0); const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + c.messageIds.length, 0); const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((id2) => !!id2); const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds); const latestChunk = activatedChunks[activatedChunks.length - 1]; const derivedLastObservedAt = lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date()); if (record.activeObservations) { const boundary = ` --- message boundary (${derivedLastObservedAt.toISOString()}) --- `; record.activeObservations = `${record.activeObservations}${boundary}${activatedContent}`; } else { record.activeObservations = activatedContent; } record.observationTokenCount = (record.observationTokenCount ?? 0) + activatedTokens; record.pendingMessageTokens = Math.max(0, (record.pendingMessageTokens ?? 0) - activatedMessageTokens); record.bufferedObservationChunks = remainingChunks.length > 0 ? remainingChunks : void 0; record.lastObservedAt = derivedLastObservedAt; record.updatedAt = /* @__PURE__ */ new Date(); const latestChunkHints = activatedChunks[activatedChunks.length - 1]; return { chunksActivated: activatedChunks.length, messageTokensActivated: activatedMessageTokens, observationTokensActivated: activatedTokens, messagesActivated: activatedMessageCount, activatedCycleIds, activatedMessageIds, observations: activatedContent, perChunk: activatedChunks.map((c) => ({ cycleId: c.cycleId ?? "", messageTokens: c.messageTokens ?? 0, observationTokens: c.tokenCount, messageCount: c.messageIds.length, observations: c.observations })), suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0, currentTask: latestChunkHints?.currentTask ?? void 0 }; } async createReflectionGeneration(input) { const { currentRecord, reflection, tokenCount } = input; const key = this.getObservationalMemoryKey(currentRecord.threadId, currentRecord.resourceId); const now = /* @__PURE__ */ new Date(); const newRecord = { id: crypto.randomUUID(), scope: currentRecord.scope, threadId: currentRecord.threadId, resourceId: currentRecord.resourceId, // Timestamps at top level createdAt: now, updatedAt: now, lastObservedAt: currentRecord.lastObservedAt ?? now, // Carry over from observation (which always runs before reflection) originType: "reflection", generationCount: currentRecord.generationCount + 1, activeObservations: reflection, config: currentRecord.config, totalTokensObserved: currentRecord.totalTokensObserved, observationTokenCount: tokenCount, pendingMessageTokens: 0, isReflecting: false, isObserving: false, isBufferingObservation: false, isBufferingReflection: false, lastBufferedAtTokens: 0, lastBufferedAtTime: null, // Timezone used for observation date formatting observedTimezone: currentRecord.observedTimezone, // Extensible metadata (optional) metadata: {} }; const existing = this.db.observationalMemory.get(key) ?? []; this.db.observationalMemory.set(key, [newRecord, ...existing]); return newRecord; } async updateBufferedReflection(input) { const { id, reflection, tokenCount, inputTokenCount, reflectedObservationLineCount } = input; const record = this.findObservationalMemoryRecordById(id); if (!record) { throw new Error(`Observational memory record not found: ${id}`); } const existing = record.bufferedReflection || ""; record.bufferedReflection = existing ? `${existing} ${reflection}` : reflection; record.bufferedReflectionTokens = (record.bufferedReflectionTokens || 0) + tokenCount; record.bufferedReflectionInputTokens = (record.bufferedReflectionInputTokens || 0) + inputTokenCount; record.reflectedObservationLineCount = reflectedObservationLineCount; record.updatedAt = /* @__PURE__ */ new Date(); } async swapBufferedReflectionToActive(input) { const { currentRecord } = input; const record = this.findObservationalMemoryRecordById(currentRecord.id); if (!record) { throw new Error(`Observational memory record not found: ${currentRecord.id}`); } if (!record.bufferedReflection) { throw new Error("No buffered reflection to swap"); } const bufferedReflection = record.bufferedReflection; const reflectedLineCount = record.reflectedObservationLineCount ?? 0; const currentObservations = record.activeObservations ?? ""; const allLines = currentObservations.split("\n"); const unreflectedLines = allLines.slice(reflectedLineCount); const unreflectedContent = unreflectedLines.join("\n").trim(); const newObservations = unreflectedContent ? `${bufferedReflection} ${unreflectedContent}` : bufferedReflection; const newRecord = await this.createReflectionGeneration({ currentRecord: record, reflection: newObservations, tokenCount: input.tokenCount }); record.bufferedReflection = void 0; record.bufferedReflectionTokens = void 0; record.bufferedReflectionInputTokens = void 0; record.reflectedObservationLineCount = void 0; return newRecord; } async setReflectingFlag(id, isReflecting) { const record = this.findObservationalMemoryRecordById(id); if (!record) { throw new Error(`Observational memory record not found: ${id}`); } record.isReflecting = isReflecting; record.updatedAt = /* @__PURE__ */ new Date(); } async setObservingFlag(id, isObserving) { const record = this.findObservationalMemoryRecordById(id); if (!record) { throw new Error(`Observational memory record not found: ${id}`); } record.isObserving = isObserving; record.updatedAt = /* @__PURE__ */ new Date(); } async setBufferingObservationFlag(id, isBuffering, lastBufferedAtTokens) { const record = this.findObservationalMemoryRecordById(id); if (!record) { throw new Error(`Observational memory record not found: ${id}`); } record.isBufferingObservation = isBuffering; if (lastBufferedAtTokens !== void 0) { record.lastBufferedAtTokens = lastBufferedAtTokens; } record.updatedAt = /* @__PURE__ */ new Date(); } async setBufferingReflectionFlag(id, isBuffering) { const record = this.findObservationalMemoryRecordById(id); if (!record) { throw new Error(`Observational memory record not found: ${id}`); } record.isBufferingReflection = isBuffering; record.updatedAt = /* @__PURE__ */ new Date(); } async clearObservationalMemory(threadId, resourceId) { const key = this.getObservationalMemoryKey(threadId, resourceId); this.db.observationalMemory.delete(key); } async setPendingMessageTokens(id, tokenCount) { const record = this.findObservationalMemoryRecordById(id); if (!record) { throw new Error(`Observational memory record not found: ${id}`); } record.pendingMessageTokens = tokenCount; record.updatedAt = /* @__PURE__ */ new Date(); } async updateObservationalMemoryConfig(input) { const record = this.findObservationalMemoryRecordById(input.id); if (!record) { throw new Error(`Observational memory record not found: ${input.id}`); } record.config = this.deepMergeConfig(record.config, input.config); record.updatedAt = /* @__PURE__ */ new Date(); } /** * Helper to find an observational memory record by ID across all keys */ findObservationalMemoryRecordById(id) { for (const records of this.db.observationalMemory.values()) { const record = records.find((r) => r.id === id); if (record) return record; } return null; } }; var AGENT_THREAD_KEY_SEPARATOR = "\0"; var AGENT_THREAD_STREAM_TOPIC_PREFIX = "agent.thread-stream"; var defaultAgentThreadPubSub = new chunkD324RFFU_cjs.EventEmitterPubSub(); function withThreadMemory(memory, resourceId, threadId) { return { ...memory && typeof memory === "object" ? memory : {}, resource: memory?.resource ?? resourceId, thread: memory?.thread ?? threadId }; } function createRuntimeState() { return { threadRunsById: /* @__PURE__ */ new Map(), threadKeysByRunId: /* @__PURE__ */ new Map(), activeThreadRunIds: /* @__PURE__ */ new Map(), approvalSuspendedRunIds: /* @__PURE__ */ new Set(), pendingSignalsByThread: /* @__PURE__ */ new Map(), preRunSignalsByThread: /* @__PURE__ */ new Map(), pendingIdleSignalsByThread: /* @__PURE__ */ new Map(), pendingContinuationsByThread: /* @__PURE__ */ new Map(), watchedThreadRunIds: /* @__PURE__ */ new Set(), preparedRunsById: /* @__PURE__ */ new Map(), abortedRunIds: /* @__PURE__ */ new Set() }; } var AgentThreadStreamRuntime = class { #id; #statesByPubSub = /* @__PURE__ */ new WeakMap(); #getPubSub(pubsub) { return pubsub ?? defaultAgentThreadPubSub; } #getSourceId() { this.#id ??= crypto$1.randomUUID(); return this.#id; } #getState(pubsub) { const resolvedPubSub = this.#getPubSub(pubsub); let state = this.#statesByPubSub.get(resolvedPubSub); if (!state) { state = createRuntimeState(); this.#statesByPubSub.set(resolvedPubSub, state); } return state; } #threadKey(resourceId, threadId) { return [resourceId ?? "", threadId].join(AGENT_THREAD_KEY_SEPARATOR); } #threadTopic(key) { return `${AGENT_THREAD_STREAM_TOPIC_PREFIX}.${encodeURIComponent(key)}`; } #isApprovalSuspendedRun(state, runId) { return state.approvalSuspendedRunIds.has(runId); } #isThreadBlockingRun(state, record) { return record.output.status === "running" || this.#isApprovalSuspendedRun(state, record.runId); } #serializeSignal(signal) { return signal; } getThreadState(options, pubsub) { const state = this.#getState(pubsub); const key = this.#threadKey(options.resourceId, options.threadId); const activeRunId = state.activeThreadRunIds.get(key); if (!activeRunId) return "idle"; const activeRecord = state.threadRunsById.get(activeRunId); if (activeRecord && !this.#isThreadBlockingRun(state, activeRecord)) { state.activeThreadRunIds.delete(key); return "idle"; } return "active"; } #publish(pubsub, key, event) { void this.#publishAndWait(pubsub, key, event).catch(() => { }); } async #publishAndWait(pubsub, key, event) { await this.#getPubSub(pubsub).publish(this.#threadTopic(key), { type: event.type, runId: event.runId, data: event }); } #withBroadcastStream(output, pubsub, key) { const runtime = this; const parts = []; const waiters = /* @__PURE__ */ new Set(); let started = false; let done = false; let error; const wake = () => { const pending = [...waiters]; waiters.clear(); for (const waiter of pending) waiter(); }; const emitPart = async (part) => { if (part && typeof part === "object" && "type" in part && part.type === "tool-call-approval") { runtime.#getState(pubsub).approvalSuspendedRunIds.add(output.runId); } parts.push(part); await runtime.#publishAndWait(pubsub, key, { type: "stream-part", runId: output.runId, part, sourceId: runtime.#getSourceId() }); wake(); }; const start = () => { if (started) return; started = true; void (async () => { try { const source = output.fullStream; if (!source) return; if (typeof source.getReader === "function") { const reader = source.getReader(); try { while (true) { const { value: part, done: streamDone } = await reader.read(); if (streamDone) break; await emitPart(part); } } finally { reader.releaseLock(); } } else { for await (const part of source) { await emitPart(part); } } } catch (caught) { error = caught; } finally { done = true; wake(); } })(); }; const createStream = () => { let index = 0; let closed = false; let waiter; return new ReadableStream({ async pull(controller) { start(); while (!closed) { if (index < parts.length) { controller.enqueue(parts[index++]); return; } if (error) { controller.error(error); return; } if (done) { controller.close(); return; } await new Promise((resolve3) => { waiter = resolve3; waiters.add(resolve3); }); if (waiter) { waiters.delete(waiter); waiter = void 0; } } }, cancel() { closed = true; if (waiter) { waiters.delete(waiter); waiter(); waiter = void 0; } } }); }; return { output, createSubscriberStream: createStream, startBroadcast: start }; } #getThreadTarget(options) { const thread = options?.memory?.thread; const threadId = options?.requestContext?.get(chunkPJIAL3WK_cjs.MASTRA_THREAD_ID_KEY) || (typeof thread === "string" ? thread : thread?.id); const resourceId = options?.requestContext?.get(chunkPJIAL3WK_cjs.MASTRA_RESOURCE_ID_KEY) || options?.memory?.resource; return { threadId, resourceId }; } prepareRunOptions(options, pubsub) { const { threadId } = this.#getThreadTarget(options); if (!threadId || !options.runId) return options; const state = this.#getState(pubsub); const abortController = new AbortController(); const upstreamAbortSignal = options.abortSignal; const abort = () => abortController.abort(); if (upstreamAbortSignal?.aborted) { abort(); } else { upstreamAbortSignal?.addEventListener("abort", abort, { once: true }); } state.preparedRunsById.set(options.runId, { abortController, cleanup: () => upstreamAbortSignal?.removeEventListener("abort", abort) }); if (state.abortedRunIds.has(options.runId)) { abort(); } return { ...options, abortSignal: abortController.signal }; } abortRun(runId, pubsub) { const state = this.#getState(pubsub); const preparedRun = state.preparedRunsById.get(runId); if (!preparedRun) { state.abortedRunIds.add(runId); return false; } preparedRun.abortController.abort(); state.abortedRunIds.add(runId); const key = state.threadKeysByRunId.get(runId); if (key) { this.#publish(pubsub, key, { type: "run-aborted", runId }); } return true; } getActiveThreadRunId(options, pubsub) { const state = this.#getState(pubsub); const key = this.#threadKey(options.resourceId, options.threadId); const activeRunId = state.activeThreadRunIds.get(key); if (!activeRunId) return void 0; const record = state.threadRunsById.get(activeRunId); if (record && !this.#isThreadBlockingRun(state, record)) return void 0; return activeRunId; } abortThread(options, pubsub) { const activeRunId = this.getActiveThreadRunId(options, pubsub); if (!activeRunId) return false; return this.abortRun(activeRunId, pubsub); } /** @internal */ resetForTests() { for (const pubsub of [defaultAgentThreadPubSub]) { this.#resetState(pubsub); void pubsub.close?.(); } defaultAgentThreadPubSub = new chunkD324RFFU_cjs.EventEmitterPubSub(); } #resetState(pubsub) { const state = this.#statesByPubSub.get(pubsub); if (!state) return; state.preparedRunsById.forEach((preparedRun) => { preparedRun.abortController.abort(); preparedRun.cleanup(); }); state.threadRunsById.clear(); state.threadKeysByRunId.clear(); state.activeThreadRunIds.clear(); state.approvalSuspendedRunIds.clear(); state.pendingSignalsByThread.clear(); state.preRunSignalsByThread.clear(); state.pendingIdleSignalsByThread.clear(); state.pendingContinuationsByThread.clear(); state.watchedThreadRunIds.clear(); state.preparedRunsById.clear(); state.abortedRunIds.clear(); } #cleanupPreparedRun(state, runId) { state.preparedRunsById.get(runId)?.cleanup(); state.preparedRunsById.delete(runId); state.abortedRunIds.delete(runId); } async #persistSignal(agent, signal, resourceId, threadId, requestContext) { const memory = await agent.getMemory({ requestContext }); if (!memory) return; await memory.saveMessages({ messages: [signal.toDBMessage({ resourceId, threadId })] }); } #broadcastPersistedSignal(state, pubsub, key, runId, signal, resourceId, threadId) { let finish; const finished = new Promise((resolve3) => { finish = resolve3; }); const parts = [ { type: "start", runId }, { ...signal.toDataPart(), runId }, { type: "finish", runId, payload: { stepResult: { reason: "stop" }, usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 } } } ]; const output = { runId, status: "running", fullStream: new ReadableStream({ start(controller) { for (const part of parts) controller.enqueue(part); controller.close(); finish(); } }), _waitUntilFinished: () => finished }; const { output: outputForSubscribers, createSubscriberStream, startBroadcast } = this.#withBroadcastStream(output, pubsub, key); const record = { agent: { id: `persisted-signal:${signal.id}` }, output: outputForSubscribers, runId, threadId, resourceId, streamOptions: {}, createSubscriberStream }; state.threadRunsById.set(runId, record); state.threadKeysByRunId.set(runId, key); const registered = this.#publishAndWait(pubsub, key, { type: "run-registered", runId }); void registered.then(startBroadcast, startBroadcast); void outputForSubscribers._waitUntilFinished().finally(() => { setTimeout(() => { state.threadRunsById.delete(runId); state.threadKeysByRunId.delete(runId); if (state.activeThreadRunIds.get(key) === runId) { state.activeThreadRunIds.delete(key); } this.#publish(pubsub, key, { type: "run-completed", runId }); }, 0); }); } async #persistAndBroadcastIdleSignal(state, pubsub, key, runId, agent, signal, resourceId, threadId, requestContext) { await this.#persistSignal(agent, signal, resourceId, threadId, requestContext); this.#broadcastPersistedSignal(state, pubsub, key, runId, signal, resourceId, threadId); } registerRun(agent, output, streamOptions, pubsub) { const { threadId, resourceId } = this.#getThreadTarget(streamOptions); if (!threadId) return; const state = this.#getState(pubsub); const key = this.#threadKey(resourceId, threadId); const { output: outputForSubscribers, createSubscriberStream, startBroadcast } = this.#withBroadcastStream(output, pubsub, key); const record = { agent, output: outputForSubscribers, runId: output.runId, threadId, resourceId, streamOptions, createSubscriberStream }; state.threadRunsById.set(output.runId, record); state.threadKeysByRunId.set(output.runId, key); state.activeThreadRunIds.set(key, output.runId); const registered = this.#publishAndWait(pubsub, key, { type: "run-registered", runId: output.runId }); void registered.then(startBroadcast, startBroadcast); this.#watchThreadRunCompletion(state, pubsub, key, record); } #watchThreadRunCompletion(state, pubsub, key, record) { if (state.watchedThreadRunIds.has(record.runId)) return; state.watchedThreadRunIds.add(record.runId); void record.output._waitUntilFinished().finally(() => { state.watchedThreadRunIds.delete(record.runId); this.#cleanupPreparedRun(state, record.runId); if (record.output.status === "suspended" && this.#isApprovalSuspendedRun(state, record.runId)) { this.#publish(pubsub, key, { type: "run-suspended", runId: record.runId }); return; } state.approvalSuspendedRunIds.delete(record.runId); state.threadRunsById.delete(record.runId); state.threadKeysByRunId.delete(record.runId); if (state.activeThreadRunIds.get(key) === record.runId) { state.activeThreadRunIds.delete(key); } this.#publish(pubsub, key, { type: "run-completed", runId: record.runId }); void this.#drainPendingSignals(state, pubsub, key, record); }); } async #drainPendingSignals(state, pubsub, key, previousRun) { if (state.activeThreadRunIds.has(key)) { return; } const preRunLeftover = state.preRunSignalsByThread.get(key); if (preRunLeftover?.length) { state.preRunSignalsByThread.delete(key); state.pendingSignalsByThread.set(key, [...preRunLeftover, ...state.pendingSignalsByThread.get(key) ?? []]); } const queue = state.pendingSignalsByThread.get(key); const signal = queue?.shift(); if (signal && queue) { if (queue.length === 0) { state.pendingSignalsByThread.delete(key); } const output = await previousRun.agent.stream(signal, { ...previousRun.streamOptions, runId: crypto$1.randomUUID(), memory: withThreadMemory( previousRun.streamOptions.memory, previousRun.resourceId ?? "", previousRun.threadId ?? "" ) }); if (queue.length > 0) { const nextRecord = state.threadRunsById.get(output.runId); if (nextRecord) { this.#watchThreadRunCompletion(state, pubsub, key, nextRecord); } } return; } if (await this.#drainPendingContinuations(state, pubsub, key)) { return; } await this.#drainPendingIdleSignals(state, pubsub, key); } async #drainPendingContinuations(state, pubsub, key) { if (state.activeThreadRunIds.has(key)) { return false; } const queue = state.pendingContinuationsByThread.get(key); const pending = queue?.shift(); if (!pending || !queue) { return false; } if (queue.length === 0) { state.pendingContinuationsByThread.delete(key); } this.#startContinuation(state, pubsub, key, pending); return true; } #startContinuation(state, pubsub, key, pending) { state.activeThreadRunIds.set(key, pending.runId); state.threadKeysByRunId.set(pending.runId, key); void pending.agent.stream(pending.messages, { ...pending.streamOptions, runId: pending.runId, memory: withThreadMemory(pending.streamOptions?.memory, pending.resourceId, pending.threadId) }).then((output) => { if ((state.pendingContinuationsByThread.get(key)?.length ?? 0) > 0) { const nextRecord = state.threadRunsById.get(output.runId); if (nextRecord) { this.#watchThreadRunCompletion(state, pubsub, key, nextRecord); } } }).catch((err) => { state.threadKeysByRunId.delete(pending.runId); this.#cleanupPreparedRun(state, pending.runId); if (state.activeThreadRunIds.get(key) === pending.runId) { state.activeThreadRunIds.delete(key); } this.#publish(pubsub, key, { type: "run-failed", runId: pending.runId, error: chunkXSOONORA_cjs.getErrorFromUnknown(err).message }); void this.#drainPendingContinuations(state, pubsub, key).then((started) => { if (!started) { void this.#drainPendingIdleSignals(state, pubsub, key); } }); }); } continueWithMessages(agent, messages, target, pubsub) { const state = this.#getState(pubsub); const key = this.#threadKey(target.resourceId, target.threadId); const runId = target.runId ?? crypto$1.randomUUID(); const pending = { agent, messages, runId, resourceId: target.resourceId, threadId: target.threadId, streamOptions: target.streamOptions }; const activeRunId = state.activeThreadRunIds.get(key); const activeRecord = activeRunId ? state.threadRunsById.get(activeRunId) : void 0; if (state.activeThreadRunIds.has(key)) { const queue = state.pendingContinuationsByThread.get(key) ?? []; queue.push(pending); state.pendingContinuationsByThread.set(key, queue); if (activeRecord) { this.#watchThreadRunCompletion(state, pubsub, key, activeRecord); } return { accepted: true, runId }; } this.#startContinuation(state, pubsub, key, pending); return { accepted: true, runId }; } async #drainPendingIdleSignals(state, pubsub, key) { if (state.activeThreadRunIds.has(key)) { return; } const idleQueue = state.pendingIdleSignalsByThread.get(key); const pendingIdle = idleQueue?.shift(); if (!pendingIdle || !idleQueue) { return; } if (idleQueue.length === 0) { state.pendingIdleSignalsByThread.delete(key); } state.activeThreadRunIds.set(key, pendingIdle.runId); state.threadKeysByRunId.set(pendingIdle.runId, key); try { const output = await pendingIdle.agent.stream(pendingIdle.signal, { ...pendingIdle.streamOptions, runId: pendingIdle.runId, memory: withThreadMemory(pendingIdle.streamOptions?.memory, pendingIdle.resourceId, pendingIdle.threadId) }); if ((idleQueue?.length ?? 0) > 0) { const nextRecord = state.threadRunsById.get(output.runId); if (nextRecord) { this.#watchThreadRunCompletion(state, pubsub, key, nextRecord); } } } catch (err) { state.threadKeysByRunId.delete(pendingIdle.runId); this.#cleanupPreparedRun(state, pendingIdle.runId); if (state.activeThreadRunIds.get(key) === pendingIdle.runId) { state.activeThreadRunIds.delete(key); } this.#publish(pubsub, key, { type: "run-failed", runId: pendingIdle.runId, error: chunkXSOONORA_cjs.getErrorFromUnknown(err).message }); void this.#drainPendingIdleSignals(state, pubsub, key); } } /** * Drains queued signals for a run. * * - `scope: 'pending'` (default) returns active-run follow-up signals — each * becomes its own model turn via `signalDrainStep`. * - `scope: 'pre-run'` returns signals queued before the run's first model * request — the first LLM step folds these into that request. */ drainPendingSignals(runId, pubsub, scope = "pending") { const state = this.#getState(pubsub); const record = state.threadRunsById.get(runId); const key = record ? this.#threadKey(record.resourceId, record.threadId) : state.threadKeysByRunId.get(runId); if (!key) return []; const signalsByThread = scope === "pre-run" ? state.preRunSignalsByThread : state.pendingSignalsByThread; const queue = signalsByThread.get(key); if (!queue || queue.length === 0) { return []; } signalsByThread.delete(key); return queue; } async waitForCrossAgentThreadRun(agent, options, pubsub) { const { threadId, resourceId } = this.#getThreadTarget(options); if (!threadId) return; const state = this.#getState(pubsub); const key = this.#threadKey(resourceId, threadId); while (true) { const activeRunId = state.activeThreadRunIds.get(key); if (!activeRunId) return; const activeRecord = state.threadRunsById.get(activeRunId); if (activeRecord) { if (activeRecord.agent.id === agent.id || !this.#isThreadBlockingRun(state, activeRecord)) { return; } await activeRecord.output._waitUntilFinished().catch(() => { }); continue; } if (state.threadKeysByRunId.get(activeRunId) === key) return; await this.#waitForRemoteRunToFinish(pubsub, key, activeRunId); } } async #waitForRemoteRunToFinish(pubsub, key, runId) { const resolvedPubSub = this.#getPubSub(pubsub); const topic = this.#threadTopic(key); await new Promise((resolve3) => { const onEvent = (event) => { const data = event.data; if ((data?.type === "run-completed" || data?.type === "run-aborted" || data?.type === "run-failed") && data.runId === runId) { void resolvedPubSub.unsubscribe(topic, onEvent).catch(() => { }); resolve3(); } }; void resolvedPubSub.subscribe(topic, onEvent).catch(() => resolve3()); }); } async subscribeToThread(agent, options, pubsub) { const resolvedPubSub = this.#getPubSub(pubsub); const state = this.#getState(resolvedPubSub); const key = this.#threadKey(options.resourceId, options.threadId); const topic = this.#threadTopic(key); const seenRunIds = /* @__PURE__ */ new Set(); const pendingRuns = []; const waiters = []; const remoteRuns = /* @__PURE__ */ new Map(); let done = false; const wake = () => { while (waiters.length) waiters.shift()?.(); }; const activeRunId = () => { const runId = state.activeThreadRunIds.get(key); if (!runId) return null; const record = state.threadRunsById.get(runId); if (!record) return runId; return this.#isThreadBlockingRun(state, record) ? runId : null; }; const enqueueRun = (record) => { if (done || seenRunIds.has(record.runId)) return; seenRunIds.add(record.runId); pendingRuns.push(record); wake(); }; const createRemoteRun = (runId) => { const remoteRun = { parts: [], waiters: [], finishWaiters: [], done: false, stream: void 0, closed: false }; remoteRun.stream = new ReadableStream({ pull(controller) { const drain = () => { if (remoteRun.closed) return; while (remoteRun.parts.length > 0) { controller.enqueue(remoteRun.parts.shift()); } if (remoteRun.done) { remoteRun.closed = true; controller.close(); } }; drain(); if (!remoteRun.done && !remoteRun.closed) { remoteRun.waiters.push(drain); } }, cancel() { remoteRun.done = true; remoteRun.closed = true; remoteRun.waiters.length = 0; while (remoteRun.finishWaiters.length) remoteRun.finishWaiters.shift()?.(); } }); remoteRuns.set(runId, remoteRun); return { agent, output: { runId, status: "running", fullStream: remoteRun.stream, _waitUntilFinished: async () => { if (remoteRun.done) return; await new Promise((resolve3) => remoteRun.finishWaiters.push(resolve3)); } }, runId, threadId: options.threadId, resourceId: options.resourceId, streamOptions: {} }; }; const onEvent = (event) => { const data = event.data; if (!data) return; if (data.type === "run-registered") { state.activeThreadRunIds.set(key, data.runId); const record = state.threadRunsById.get(data.runId) ?? createRemoteRun(data.runId); enqueueRun(record); wake(); return; } if (data.type === "stream-part") { if (data.sourceId === this.#id) return; let remoteRun = remoteRuns.get(data.runId); if (!remoteRun) { state.activeThreadRunIds.set(key, data.runId); enqueueRun(createRemoteRun(data.runId)); remoteRun = remoteRuns.get(data.runId); if (!remoteRun) return; } remoteRun.parts.push(data.part); while (remoteRun.waiters.length) remoteRun.waiters.shift()?.(); return; } if (data.type === "signal-enqueued") { if (data.sourceId === this.#id) return; const signalsByThread = data.preRun ? state.preRunSignalsByThread : state.pendingSignalsByThread; const queue = signalsByThread.get(key) ?? []; queue.push(chunk2TATDSHU_cjs.createSignal(data.signal)); signalsByThread.set(key, queue); return; } if (data.type === "run-failed") { if (state.activeThreadRunIds.get(key) === data.runId) { state.activeThreadRunIds.delete(key); } const errorRun = createRemoteRun(data.runId); const remoteRun = remoteRuns.get(data.runId); if (remoteRun) { remoteRun.parts.push({ type: "error", payload: { error: new Error(data.error) } }); remoteRun.done = true; while (remoteRun.waiters.length) remoteRun.waiters.shift()?.(); while (remoteRun.finishWaiters.length) remoteRun.finishWaiters.shift()?.(); remoteRuns.delete(data.runId); } enqueueRun(errorRun); seenRunIds.delete(data.runId); void this.#drainPendingIdleSignals(state, resolvedPubSub, key); wake(); return; } if (data.type === "run-completed" || data.type === "run-aborted" || data.type === "run-suspended") { if ((data.type !== "run-suspended" || !state.approvalSuspendedRunIds.has(data.runId)) && state.activeThreadRunIds.get(key) === data.runId) { state.activeThreadRunIds.delete(key); } if (data.type !== "run-suspended") { state.approvalSuspendedRunIds.delete(data.runId); } const remoteRun = remoteRuns.get(data.runId); if (remoteRun) { remoteRun.done = true; while (remoteRun.waiters.length) remoteRun.waiters.shift()?.(); while (remoteRun.finishWaiters.length) remoteRun.finishWaiters.shift()?.(); remoteRuns.delete(data.runId); } if (data.type === "run-aborted" && activeReaderRunId === data.runId && currentReader) { cancelledByAbort = true; try { void currentReader.cancel(); } catch { } } seenRunIds.delete(data.runId); if (data.type !== "run-suspended") { void this.#drainPendingIdleSignals(state, resolvedPubSub, key); } wake(); } }; await resolvedPubSub.subscribe(topic, onEvent); const currentRunId = activeRunId(); const currentRecord = currentRunId ? state.threadRunsById.get(currentRunId) : void 0; if (currentRecord) { enqueueRun(currentRecord); } let currentReader = null; let activeReaderRunId = null; let cancelledByAbort = false; const unsubscribe = () => { if (done) return; done = true; void resolvedPubSub.unsubscribe(topic, onEvent).catch(() => { }); if (currentReader) { try { void currentReader.cancel(); } catch { } } wake(); }; return { activeRunId, abort: () => this.abortThread(options, resolvedPubSub), unsubscribe, stream: (async function* () { try { while (!done || pendingRuns.length > 0) { if (pendingRuns.length === 0) { await new Promise((resolve3) => waiters.push(resolve3)); continue; } const run = pendingRuns.shift(); const subscriberStream = run.createSubscriberStream?.() ?? run.output.fullStream; const reader = subscriberStream.getReader(); currentReader = reader; activeReaderRunId = run.runId; let readerReleased = false; try { while (true) { const { value: part, done: streamDone } = await reader.read(); if (streamDone) break; const typedPart = part; yield typedPart; if (done) break; if (typedPart.type === "finish" || typedPart.type === "error" || typedPart.type === "abort" || typedPart.type === "tool-call-suspended") { readerReleased = true; void (async () => { try { while (true) { const { done: d } = await reader.read(); if (d) break; } } catch { } reader.releaseLock(); })(); break; } } if (!readerReleased && !done && cancelledByAbort) { yield { type: "abort", runId: run.runId }; cancelledByAbort = false; } } finally { currentReader = null; activeReaderRunId = null; if (!readerReleased) { reader.releaseLock(); } } } } finally { unsubscribe(); } })() }; } sendMessage(agent, message, target, pubsub) { return this.sendSignal(agent, chunk2TATDSHU_cjs.createMessageSignal(message, { acceptedAt: /* @__PURE__ */ new Date() }), target, pubsub); } queueMessage(agent, message, target, pubsub) { const state = this.#getState(pubsub); const signal = chunk2TATDSHU_cjs.createMessageSignal(message, { acceptedAt: /* @__PURE__ */ new Date() }); let key; let runId = target.runId; let activeRecord; if (target.resourceId && target.threadId) { key = this.#threadKey(target.resourceId, target.threadId); const activeRunId = state.activeThreadRunIds.get(key); activeRecord = activeRunId ? state.threadRunsById.get(activeRunId) : void 0; if (activeRecord && !this.#isThreadBlockingRun(state, activeRecord)) { state.activeThreadRunIds.delete(key); activeRecord = void 0; } runId ??= activeRunId; } if (runId) { activeRecord ??= state.threadRunsById.get(runId); if (activeRecord) { key ??= this.#threadKey(activeRecord.resourceId, activeRecord.threadId); } } const resourceId = target.resourceId ?? activeRecord?.resourceId; const threadId = target.threadId ?? activeRecord?.threadId; if (!resourceId || !threadId) { throw new Error("resourceId and threadId are required to queue a message"); } key ??= this.#threadKey(resourceId, threadId); const queuedRunId = crypto$1.randomUUID(); const queuedStreamOptions = target.ifIdle?.streamOptions ?? activeRecord?.streamOptions; if (activeRecord) { const idleQueue = state.pendingIdleSignalsByThread.get(key) ?? []; idleQueue.push({ agent, signal, runId: queuedRunId, resourceId, threadId, streamOptions: queuedStreamOptions }); state.pendingIdleSignalsByThread.set(key, idleQueue); this.#watchThreadRunCompletion(state, pubsub, key, activeRecord); return { accepted: true, runId: queuedRunId, signal }; } return this.sendSignal( agent, signal, { ...target, runId, resourceId, threadId, ifIdle: { ...target.ifIdle, behavior: "wake" } }, pubsub ); } async sendStateSignal(agent, stateInput, target, pubsub) { if (!target.resourceId || !target.threadId) { throw new Error("resourceId and threadId are required to send a state signal"); } const resourceId = target.resourceId; const threadId = target.threadId; const requestContext = target.ifIdle?.streamOptions?.requestContext; const memoryContext = chunkUC46OXET_cjs.parseMemoryRequestContext(requestContext); const memory = await agent.getMemory({ requestContext }); if (!memory) { throw new Error("sendStateSignal requires Mastra memory"); } const loadedThread = await memory.getThreadById({ threadId }) ?? memoryContext?.thread; if (!loadedThread) { throw new Error(`sendStateSignal could not load thread ${threadId}`); } const thread = { ...loadedThread, id: threadId, resourceId: loadedThread.resourceId ?? resourceId, createdAt: loadedThread.createdAt ?? /* @__PURE__ */ new Date(), updatedAt: loadedThread.updatedAt ?? /* @__PURE__ */ new Date(), metadata: loadedThread.metadata }; const applied = await chunkUC46OXET_cjs.applyStateSignal({ input: stateInput, memory, thread, resourceId, threadId, memoryConfig: memoryContext?.memoryConfig, acceptedAt: /* @__PURE__ */ new Date() }); if (applied.skipped) { return { accepted: true, skipped: true, reason: "unchanged" }; } return this.sendSignal(agent, applied.signal, target, pubsub); } /** * Routes a signal to an agent thread. * * Signals can land in three places: * - an active same-agent run, where they are queued for the execution loop to drain; * - a reserved thread run that has not registered its stream record yet; * - a new idle-started run, when the caller opts into `ifIdle`. * * Cross-agent active runs are intentionally not interrupted here. They either finish first * through `waitForCrossAgentThreadRun()` on the stream path, or this method falls through to * the idle-start path when the caller provided a resource/thread target and `ifIdle` options. */ sendSignal(agent, signalInput, target, pubsub) { const state = this.#getState(pubsub); let signal = chunk2TATDSHU_cjs.createSignal({ ...signalInput, acceptedAt: /* @__PURE__ */ new Date() }); let key; let runId = target.runId; const activeBehavior = target.ifActive?.behavior ?? "deliver"; const idleBehavior = target.ifIdle?.behavior ?? "wake"; let activeRecord; if (target.resourceId && target.threadId) { key = this.#threadKey(target.resourceId, target.threadId); const activeRunId = state.activeThreadRunIds.get(key); activeRecord = activeRunId ? state.threadRunsById.get(activeRunId) : void 0; if (activeRecord && !this.#isThreadBlockingRun(state, activeRecord)) { state.activeThreadRunIds.delete(key); activeRecord = void 0; } if (activeRecord && activeRecord.agent.id === agent.id) { runId = activeRecord.runId; } else if (activeRunId && !activeRecord) { runId = activeRunId; } } const isActiveTarget = Boolean( runId && (activeRecord?.output.status === "running" || key && state.activeThreadRunIds.get(key) === runId) ); const resourceId = target.resourceId ?? activeRecord?.resourceId; const threadId = target.threadId ?? activeRecord?.threadId; signal = chunk2TATDSHU_cjs.resolveDeliveryAttributes( signal, isActiveTarget ? target.ifActive?.attributes : target.ifIdle?.attributes ); if (isActiveTarget && activeBehavior !== "deliver") { if (activeBehavior === "persist") { if (!resourceId || !threadId) { throw new Error("resourceId and threadId are required to persist an active signal"); } const persisted = this.#persistSignal( agent, signal, resourceId, threadId, target.ifIdle?.streamOptions?.requestContext ); void persisted.catch(() => { }); return { accepted: true, runId, signal, persisted }; } return { accepted: true, runId, signal }; } if (runId) { activeRecord ??= state.threadRunsById.get(runId); if (activeRecord && this.#isThreadBlockingRun(state, activeRecord)) { key ??= this.#threadKey(activeRecord.resourceId, activeRecord.threadId); if (activeRecord.agent.id === agent.id) { const queue = state.pendingSignalsByThread.get(key) ?? []; queue.push(signal); state.pendingSignalsByThread.set(key, queue); this.#publish(pubsub, key, { type: "signal-enqueued", runId, signal: this.#serializeSignal(signal), sourceId: this.#getSourceId() }); this.#watchThreadRunCompletion(state, pubsub, key, activeRecord); return { accepted: true, runId, signal }; } } if (key && state.activeThreadRunIds.get(key) === runId) { const isLocalReservedRun = state.threadKeysByRunId.get(runId) === key; if (isLocalReservedRun) { const queue = state.preRunSignalsByThread.get(key) ?? []; queue.push(signal); state.preRunSignalsByThread.set(key, queue); } this.#publish(pubsub, key, { type: "signal-enqueued", runId, signal: this.#serializeSignal(signal), sourceId: this.#getSourceId(), preRun: isLocalReservedRun }); return { accepted: true, runId, signal }; } } if (!resourceId || !threadId) { throw new Error("No active agent run found for signal target"); } runId = crypto$1.randomUUID(); key ??= this.#threadKey(resourceId, threadId); if (idleBehavior === "persist") { const persisted = this.#persistAndBroadcastIdleSignal( state, pubsub, key, runId, agent, signal, resourceId, threadId, target.ifIdle?.streamOptions?.requestContext ); void persisted.catch(() => { }); return { accepted: true, runId, signal, persisted }; } if (idleBehavior !== "wake") { return { accepted: true, runId, signal }; } if (state.activeThreadRunIds.has(key)) { const idleQueue = state.pendingIdleSignalsByThread.get(key) ?? []; idleQueue.push({ agent, signal, runId, resourceId, threadId, streamOptions: target.ifIdle?.streamOptions }); state.pendingIdleSignalsByThread.set(key, idleQueue); if (activeRecord) { this.#watchThreadRunCompletion(state, pubsub, key, activeRecord); } return { accepted: true, runId, signal }; } state.activeThreadRunIds.set(key, runId); state.threadKeysByRunId.set(runId, key); void agent.stream(signal, { ...target.ifIdle?.streamOptions, runId, memory: withThreadMemory(target.ifIdle?.streamOptions?.memory, resourceId, threadId) }).catch((err) => { state.threadKeysByRunId.delete(runId); this.#cleanupPreparedRun(state, runId); if (state.activeThreadRunIds.get(key) === runId) { state.activeThreadRunIds.delete(key); } this.#publish(pubsub, key, { type: "run-failed", runId, error: chunkXSOONORA_cjs.getErrorFromUnknown(err).message }); void this.#drainPendingIdleSignals(state, pubsub, key); }); return { accepted: true, runId, signal }; } }; var agentThreadStreamRuntime = new AgentThreadStreamRuntime(); // src/notifications/signals.ts function notificationSignalAttributes(notification) { return { ...notification.attributes, id: notification.id, source: notification.source, type: notification.kind, kind: notification.kind, priority: notification.priority, status: notification.status, ...notification.coalescedCount && notification.coalescedCount > 1 ? { coalescedCount: notification.coalescedCount } : {} }; } function notificationSummaryContents(summary) { const sources = Object.entries(summary.bySource).sort(([a], [b]) => a.localeCompare(b)).map(([source, count]) => `${source}: ${count}`).join(", "); return sources || "No pending notifications"; } var priorityOrder = ["low", "medium", "high", "urgent"]; function highestPriority(byPriority) { for (let i = priorityOrder.length - 1; i >= 0; i -= 1) { const priority = priorityOrder[i]; if (priority && (byPriority[priority] ?? 0) > 0) return priority; } return void 0; } function notificationSignalMetadata(notification) { return { signal: "notification", recordId: notification.id, source: notification.source, kind: notification.kind, priority: notification.priority, status: notification.status, ...notification.coalescedCount && notification.coalescedCount > 1 ? { coalescedCount: notification.coalescedCount } : {}, ...notification.deliveredAt ? { deliveredAt: notification.deliveredAt.toISOString() } : {}, ...notification.seenAt ? { seenAt: notification.seenAt.toISOString() } : {} }; } function notificationSummarySignalMetadata(summary) { const priority = highestPriority(summary.byPriority); return { signal: "summary", pending: summary.pending, groups: Object.entries(summary.bySource).sort(([a], [b]) => a.localeCompare(b)).map(([source, count]) => ({ source, count })), byPriority: summary.byPriority, notificationIds: summary.notificationIds, ...priority ? { priority } : {} }; } function createNotificationSignal(notification) { return chunk2TATDSHU_cjs.createSignal({ type: "notification", tagName: "notification", contents: notification.summary, attributes: notificationSignalAttributes(notification), metadata: { ...notification.metadata, notification: notificationSignalMetadata(notification) } }); } function createNotificationSummarySignal(summary) { const notification = notificationSummarySignalMetadata(summary); return chunk2TATDSHU_cjs.createSignal({ type: "notification", tagName: "notification-summary", contents: notificationSummaryContents(summary), attributes: { pending: summary.pending, ...notification.priority ? { priority: notification.priority } : {} }, metadata: { notification, notificationSummary: summary, notificationIds: summary.notificationIds } }); } function summarizeNotifications(notifications) { const pendingNotifications = notifications.filter((notification) => notification.status === "pending"); const first = pendingNotifications[0] ?? notifications[0]; return pendingNotifications.reduce( (summary, notification) => { summary.pending += 1; summary.bySource[notification.source] = (summary.bySource[notification.source] ?? 0) + 1; summary.byPriority[notification.priority] = (summary.byPriority[notification.priority] ?? 0) + 1; summary.notificationIds.push(notification.id); return summary; }, { threadId: first?.threadId ?? "", resourceId: first?.resourceId, agentId: first?.agentId, pending: 0, bySource: {}, byPriority: {}, notificationIds: [] } ); } // src/notifications/dispatcher.ts var errorMessage = (error) => error instanceof Error ? error.message : String(error); var isSummaryDue = (record, now) => Boolean(record.summaryAt && record.summaryAt.getTime() <= now.getTime()); var deliveryPriority = { urgent: 0, high: 1, medium: 2, low: 3 }; var compareDueDispatchItems = (a, b) => deliveryPriority[a.priority] - deliveryPriority[b.priority] || a.createdAt.getTime() - b.createdAt.getTime(); var getHighestPriority = (records) => records.reduce( (highest, record) => deliveryPriority[record.priority] < deliveryPriority[highest] ? record.priority : highest, "low" ); var getEarliestCreatedAt = (records) => records.reduce( (earliest, record) => record.createdAt.getTime() < earliest.getTime() ? record.createdAt : earliest, records[0].createdAt ); var groupKey = (record) => { if (!record.agentId || !record.resourceId || !record.threadId) return void 0; return [record.agentId, record.resourceId, record.threadId].join("\0"); }; async function recordDeliveryFailure({ storage, record, now, error }) { await storage.updateNotification({ id: record.id, threadId: record.threadId, deliveryAttempts: (record.deliveryAttempts ?? 0) + 1, lastDeliveryAttemptAt: now, lastDeliveryError: errorMessage(error) }); } async function sendNotificationRecord({ mastra, storage, record, now, batchThreadState }) { const current = await storage.getNotification({ threadId: record.threadId, id: record.id }); if (!current || current.status !== "pending" || current.deliveredSignalId) return null; if (!current.agentId) throw new Error(`Notification ${current.id} is missing agentId`); if (!current.resourceId) throw new Error(`Notification ${current.id} is missing resourceId`); const agent = await mastra.getAgentById(current.agentId); if (current.priority === "high" && current.summarySignalId) { const threadState = batchThreadState ?? agentThreadStreamRuntime.getThreadState( { resourceId: current.resourceId, threadId: current.threadId }, agent.getPubSub?.() ); if (threadState === "active") return null; } const signal = createNotificationSignal({ ...current, status: "delivered", deliveredAt: now}); const target = { resourceId: current.resourceId, threadId: current.threadId }; const result = agent.sendSignal(signal, target); await result.persisted; if (!result.accepted) { throw new Error(`Notification ${current.id} signal was rejected`); } const updated = await storage.updateNotification({ id: current.id, threadId: current.threadId, status: "delivered", deliveredSignalId: result.signal.id, lastDeliveryAttemptAt: now }); return { record: updated, signal: result.signal }; } async function sendNotificationSummary({ mastra, storage, records, now }) { const first = records[0]; if (!first?.agentId) throw new Error("Notification summary is missing agentId"); if (!first.resourceId) throw new Error("Notification summary is missing resourceId"); const agent = await mastra.getAgentById(first.agentId); const summary = summarizeNotifications(records); const signal = createNotificationSummarySignal(summary); const target = records.every((record) => record.priority === "low") ? { resourceId: first.resourceId, threadId: first.threadId, ifIdle: { behavior: "persist" } } : { resourceId: first.resourceId, threadId: first.threadId }; const result = agent.sendSignal(signal, target); await result.persisted; if (!result.accepted) { throw new Error(`Notification summary for thread ${first.threadId} was rejected`); } const updatedRecords = []; for (const record of records) { updatedRecords.push( await storage.updateNotification({ id: record.id, threadId: record.threadId, summaryAt: null, summarySignalId: result.signal.id, lastDeliveryAttemptAt: now }) ); } return { records: updatedRecords, signal: result.signal }; } async function getBatchThreadState({ mastra, group }) { const agent = await mastra.getAgentById(group.agentId); return agentThreadStreamRuntime.getThreadState( { resourceId: group.resourceId, threadId: group.threadId }, agent.getPubSub?.() ); } async function dispatchDueNotifications({ mastra, storage, now = /* @__PURE__ */ new Date(), limit = 100 }) { const due = await storage.listDueNotifications({ now, limit }); const delivered = []; const failed = []; const signals = []; const groups = /* @__PURE__ */ new Map(); const ungroupedIndividual = []; for (const record of due) { const key = groupKey(record); if (!key) { if (isSummaryDue(record, now)) { const error = new Error( `Notification ${record.id} cannot be summarized without agentId, resourceId, and threadId` ); await recordDeliveryFailure({ storage, record, now, error }); failed.push({ record, error: error.message }); } else { ungroupedIndividual.push(record); } continue; } const group = groups.get(key) ?? { key, agentId: record.agentId, resourceId: record.resourceId, threadId: record.threadId, summaryRecords: [], individualRecords: [] }; if (isSummaryDue(record, now)) { group.summaryRecords.push(record); } else { group.individualRecords.push(record); } groups.set(key, group); } for (const group of groups.values()) { const records = [...group.summaryRecords, ...group.individualRecords]; let batchThreadState; try { batchThreadState = await getBatchThreadState({ mastra, group }); } catch (error) { for (const record of records) { await recordDeliveryFailure({ storage, record, now, error }); failed.push({ record, error: errorMessage(error) }); } continue; } const items = group.individualRecords.map((record) => ({ type: "individual", record, priority: record.priority, createdAt: record.createdAt })); if (group.summaryRecords.length > 0) { items.push({ type: "summary", records: group.summaryRecords, priority: getHighestPriority(group.summaryRecords), createdAt: getEarliestCreatedAt(group.summaryRecords) }); } items.sort(compareDueDispatchItems); for (const item of items) { if (item.type === "summary") { try { const result = await sendNotificationSummary({ mastra, storage, records: item.records, now }); delivered.push(...result.records); signals.push(result.signal); } catch (error) { for (const record of item.records) { await recordDeliveryFailure({ storage, record, now, error }); failed.push({ record, error: errorMessage(error) }); } } continue; } try { const result = await sendNotificationRecord({ mastra, storage, record: item.record, now, batchThreadState }); if (!result) continue; delivered.push(result.record); signals.push(result.signal); } catch (error) { await recordDeliveryFailure({ storage, record: item.record, now, error }); failed.push({ record: item.record, error: errorMessage(error) }); } } } ungroupedIndividual.sort( (a, b) => compareDueDispatchItems( { priority: a.priority, createdAt: a.createdAt }, { priority: b.priority, createdAt: b.createdAt } ) ); for (const record of ungroupedIndividual) { try { const result = await sendNotificationRecord({ mastra, storage, record, now }); if (!result) continue; delivered.push(result.record); signals.push(result.signal); } catch (error) { await recordDeliveryFailure({ storage, record, now, error }); failed.push({ record, error: errorMessage(error) }); } } return { delivered, failed, signals }; } var NotificationsStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "NOTIFICATIONS" }); } }; var cloneDate = (value) => value ? new Date(value) : void 0; var notificationKey = (threadId, id) => `${threadId}\0${id}`; var cloneValue = (value) => value === void 0 ? void 0 : structuredClone(value); var cloneRecord = (record) => ({ ...record, createdAt: new Date(record.createdAt), updatedAt: new Date(record.updatedAt), deliveredAt: cloneDate(record.deliveredAt), seenAt: cloneDate(record.seenAt), dismissedAt: cloneDate(record.dismissedAt), archivedAt: cloneDate(record.archivedAt), discardedAt: cloneDate(record.discardedAt), deliverAt: cloneDate(record.deliverAt), summaryAt: cloneDate(record.summaryAt), lastDeliveryAttemptAt: cloneDate(record.lastDeliveryAttemptAt), payload: cloneValue(record.payload), attributes: cloneValue(record.attributes), metadata: cloneValue(record.metadata) }); var statusTimestamp = (status, now) => { if (status === "delivered") return { deliveredAt: now }; if (status === "seen") return { seenAt: now }; if (status === "dismissed") return { dismissedAt: now }; if (status === "archived") return { archivedAt: now }; if (status === "discarded") return { discardedAt: now }; return {}; }; var valueMatches = (value, filter) => { if (!filter) return true; return Array.isArray(filter) ? filter.includes(value) : value === filter; }; var dueTime = (record) => { const deliverAt = record.deliverAt?.getTime(); const summaryAt = record.summaryAt?.getTime(); if (deliverAt !== void 0 && summaryAt !== void 0) return Math.min(deliverAt, summaryAt); return deliverAt ?? summaryAt ?? Number.POSITIVE_INFINITY; }; var InMemoryNotificationsStorage = class extends NotificationsStorage { #notifications = /* @__PURE__ */ new Map(); async createNotification(input) { const existing = this.findCoalescable(input); if (existing) { const now2 = /* @__PURE__ */ new Date(); const next = { ...existing, summary: input.summary, payload: cloneValue(input.payload ?? existing.payload), priority: input.priority ?? existing.priority, attributes: input.attributes ? { ...cloneValue(existing.attributes), ...cloneValue(input.attributes) } : cloneValue(existing.attributes), updatedAt: now2, deliverAt: input.deliverAt ?? existing.deliverAt, summaryAt: input.summaryAt ?? existing.summaryAt, deliveryReason: input.deliveryReason ?? existing.deliveryReason, coalescedCount: (existing.coalescedCount ?? 1) + 1, metadata: input.metadata ? { ...cloneValue(existing.metadata), ...cloneValue(input.metadata) } : cloneValue(existing.metadata) }; this.#notifications.set(notificationKey(next.threadId, next.id), next); return cloneRecord(next); } const now = input.createdAt ?? /* @__PURE__ */ new Date(); const record = { id: input.id ?? crypto$1.randomUUID(), threadId: input.threadId, source: input.source, kind: input.kind, priority: input.priority ?? "medium", status: "pending", summary: input.summary, payload: cloneValue(input.payload), resourceId: input.resourceId, agentId: input.agentId, sourceId: input.sourceId, dedupeKey: input.dedupeKey, coalesceKey: input.coalesceKey, coalescedCount: 1, attributes: cloneValue(input.attributes), createdAt: now, updatedAt: now, deliverAt: input.deliverAt, summaryAt: input.summaryAt, deliveryReason: input.deliveryReason, deliveryAttempts: 0, metadata: cloneValue(input.metadata) }; this.#notifications.set(notificationKey(record.threadId, record.id), record); return cloneRecord(record); } async listNotifications(input) { const search = input.search?.toLowerCase(); const results = [...this.#notifications.values()].filter((record) => record.threadId === input.threadId).filter((record) => valueMatches(record.status, input.status)).filter((record) => valueMatches(record.priority, input.priority)).filter((record) => !input.source || record.source === input.source).filter((record) => !input.resourceId || record.resourceId === input.resourceId).filter((record) => !input.agentId || record.agentId === input.agentId).filter( (record) => !search || record.summary.toLowerCase().includes(search) || record.kind.toLowerCase().includes(search) || record.source.toLowerCase().includes(search) ).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()); return results.slice(0, input.limit ?? results.length).map(cloneRecord); } async listDueNotifications(input) { const now = input.now.getTime(); const results = [...this.#notifications.values()].filter((record) => record.status === "pending").filter((record) => !input.agentId || record.agentId === input.agentId).filter((record) => !input.resourceId || record.resourceId === input.resourceId).filter((record) => dueTime(record) <= now).sort((a, b) => dueTime(a) - dueTime(b) || a.updatedAt.getTime() - b.updatedAt.getTime()); return results.slice(0, input.limit ?? results.length).map(cloneRecord); } async getNotification(input) { const record = this.#notifications.get(notificationKey(input.threadId, input.id)); if (!record) return null; return cloneRecord(record); } async updateNotification(input) { const existing = this.#notifications.get(notificationKey(input.threadId, input.id)); if (!existing) { throw new Error(`Notification ${input.id} was not found for thread ${input.threadId}`); } const now = /* @__PURE__ */ new Date(); const next = { ...existing, ...input.status ? { status: input.status, ...statusTimestamp(input.status, now) } : {}, ...input.summary !== void 0 ? { summary: input.summary } : {}, ...input.payload !== void 0 ? { payload: cloneValue(input.payload) } : {}, ...input.attributes !== void 0 ? { attributes: cloneValue(input.attributes) } : {}, ...input.metadata !== void 0 ? { metadata: cloneValue(input.metadata) } : {}, ...input.deliverAt !== void 0 ? { deliverAt: input.deliverAt ?? void 0 } : {}, ...input.summaryAt !== void 0 ? { summaryAt: input.summaryAt ?? void 0 } : {}, ...input.deliveryReason !== void 0 ? { deliveryReason: input.deliveryReason } : {}, ...input.deliveryAttempts !== void 0 ? { deliveryAttempts: input.deliveryAttempts } : {}, ...input.lastDeliveryAttemptAt !== void 0 ? { lastDeliveryAttemptAt: input.lastDeliveryAttemptAt } : {}, ...input.lastDeliveryError !== void 0 ? { lastDeliveryError: input.lastDeliveryError } : {}, ...input.deliveredSignalId !== void 0 ? { deliveredSignalId: input.deliveredSignalId } : {}, ...input.summarySignalId !== void 0 ? { summarySignalId: input.summarySignalId } : {}, updatedAt: now }; this.#notifications.set(notificationKey(next.threadId, next.id), next); return cloneRecord(next); } async dangerouslyClearAll() { this.#notifications.clear(); } findCoalescable(input) { if (!input.dedupeKey && !input.coalesceKey) return void 0; return [...this.#notifications.values()].find((record) => { if (record.threadId !== input.threadId || record.source !== input.source || record.kind !== input.kind || record.status !== "pending") return false; if (record.agentId !== input.agentId || record.resourceId !== input.resourceId) return false; return Boolean( input.dedupeKey && record.dedupeKey === input.dedupeKey || input.coalesceKey && record.coalesceKey === input.coalesceKey ); }); } }; // src/storage/domains/schedules/base.ts var SchedulesStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "SCHEDULES" }); } async dangerouslyClearAll() { } }; // src/storage/domains/schedules/inmemory.ts function clone(value) { return value == null ? value : JSON.parse(JSON.stringify(value)); } var InMemorySchedulesStorage = class extends SchedulesStorage { db; constructor({ db }) { super(); this.db = db; } async dangerouslyClearAll() { this.db.schedules.clear(); this.db.scheduleTriggers.length = 0; } async createSchedule(schedule) { if (this.db.schedules.has(schedule.id)) { throw new Error(`Schedule ${schedule.id} already exists`); } const stored = clone(schedule); this.db.schedules.set(stored.id, stored); return clone(stored); } async getSchedule(id) { const found = this.db.schedules.get(id); return found ? clone(found) : null; } async listSchedules(filter) { let rows = Array.from(this.db.schedules.values()); if (filter?.status) { rows = rows.filter((r) => r.status === filter.status); } if (filter?.workflowId) { rows = rows.filter((r) => r.target.type === "workflow" && r.target.workflowId === filter.workflowId); } if (filter?.ownerType !== void 0) { rows = rows.filter((r) => (r.ownerType ?? null) === filter.ownerType); } if (filter?.ownerId !== void 0) { rows = rows.filter((r) => (r.ownerId ?? null) === filter.ownerId); } rows.sort((a, b) => a.createdAt - b.createdAt); return rows.map(clone); } async listDueSchedules(now, limit) { const due = []; for (const row of this.db.schedules.values()) { if (row.status === "active" && row.nextFireAt <= now) { due.push(row); } } due.sort((a, b) => a.nextFireAt - b.nextFireAt); const cap = limit ?? due.length; return due.slice(0, cap).map(clone); } async updateSchedule(id, patch) { const existing = this.db.schedules.get(id); if (!existing) { throw new Error(`Schedule ${id} not found`); } const updated = { ...existing, ...patch, target: patch.target !== void 0 ? patch.target : existing.target, metadata: patch.metadata !== void 0 ? patch.metadata : existing.metadata, updatedAt: Date.now() }; const stored = clone(updated); this.db.schedules.set(id, stored); return clone(stored); } async updateScheduleNextFire(id, expectedNextFireAt, newNextFireAt, lastFireAt, lastRunId) { const existing = this.db.schedules.get(id); if (!existing) return false; if (existing.nextFireAt !== expectedNextFireAt) return false; if (existing.status !== "active") return false; const stored = { ...existing, nextFireAt: newNextFireAt, lastFireAt, lastRunId, updatedAt: Date.now() }; this.db.schedules.set(id, stored); return true; } async deleteSchedule(id) { this.db.schedules.delete(id); for (let i = this.db.scheduleTriggers.length - 1; i >= 0; i--) { if (this.db.scheduleTriggers[i].scheduleId === id) { this.db.scheduleTriggers.splice(i, 1); } } } async recordTrigger(trigger) { const stored = { ...trigger, id: trigger.id ?? crypto$1.randomUUID(), triggerKind: trigger.triggerKind ?? "schedule-fire" }; this.db.scheduleTriggers.push(clone(stored)); } async listTriggers(scheduleId, opts) { let rows = this.db.scheduleTriggers.filter((f) => f.scheduleId === scheduleId); if (opts?.fromActualFireAt != null) { rows = rows.filter((f) => f.actualFireAt >= opts.fromActualFireAt); } if (opts?.toActualFireAt != null) { rows = rows.filter((f) => f.actualFireAt < opts.toActualFireAt); } rows.sort((a, b) => b.actualFireAt - a.actualFireAt); if (opts?.limit != null) { rows = rows.slice(0, opts.limit); } return rows.map(clone); } }; // src/storage/domains/scores/base.ts var ScoresStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "SCORES" }); } async dangerouslyClearAll() { } async listScoresBySpan({ traceId, spanId, pagination: _pagination }) { throw new chunkXSOONORA_cjs.MastraError({ id: "SCORES_STORAGE_GET_SCORES_BY_SPAN_NOT_IMPLEMENTED", domain: chunkXSOONORA_cjs.ErrorDomain.STORAGE, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, details: { traceId, spanId } }); } }; // src/storage/domains/scores/inmemory.ts var ScoresInMemory = class extends ScoresStorage { db; constructor({ db }) { super(); this.db = db; } async dangerouslyClearAll() { this.db.scores.clear(); } async getScoreById({ id }) { return this.db.scores.get(id) ?? null; } async saveScore(score) { const newScore = { id: crypto.randomUUID(), createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date(), ...score }; this.db.scores.set(newScore.id, newScore); return { score: newScore }; } async listScoresByScorerId({ scorerId, pagination, entityId, entityType, source }) { const scores = Array.from(this.db.scores.values()).filter((score) => { let baseFilter = score.scorerId === scorerId; if (entityId) { baseFilter = baseFilter && score.entityId === entityId; } if (entityType) { baseFilter = baseFilter && score.entityType === entityType; } if (source) { baseFilter = baseFilter && score.source === source; } return baseFilter; }); const { page, perPage: perPageInput } = pagination; const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER); const { offset: start, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); const end = perPageInput === false ? scores.length : start + perPage; return { scores: scores.slice(start, end), pagination: { total: scores.length, page, perPage: perPageForResponse, hasMore: perPageInput === false ? false : scores.length > end } }; } async listScoresByRunId({ runId, pagination }) { const scores = Array.from(this.db.scores.values()).filter((score) => score.runId === runId); const { page, perPage: perPageInput } = pagination; const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER); const { offset: start, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); const end = perPageInput === false ? scores.length : start + perPage; return { scores: scores.slice(start, end), pagination: { total: scores.length, page, perPage: perPageForResponse, hasMore: perPageInput === false ? false : scores.length > end } }; } async listScoresByEntityId({ entityId, entityType, pagination }) { const scores = Array.from(this.db.scores.values()).filter((score) => { const baseFilter = score.entityId === entityId && score.entityType === entityType; return baseFilter; }); const { page, perPage: perPageInput } = pagination; const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER); const { offset: start, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); const end = perPageInput === false ? scores.length : start + perPage; return { scores: scores.slice(start, end), pagination: { total: scores.length, page, perPage: perPageForResponse, hasMore: perPageInput === false ? false : scores.length > end } }; } async listScoresBySpan({ traceId, spanId, pagination }) { const scores = Array.from(this.db.scores.values()).filter( (score) => score.traceId === traceId && score.spanId === spanId ); scores.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); const { page, perPage: perPageInput } = pagination; const perPage = chunk2UAP4LDC_cjs.normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER); const { offset: start, perPage: perPageForResponse } = chunk2UAP4LDC_cjs.calculatePagination(page, perPageInput, perPage); const end = perPageInput === false ? scores.length : start + perPage; return { scores: scores.slice(start, end), pagination: { total: scores.length, page, perPage: perPageForResponse, hasMore: perPageInput === false ? false : scores.length > end } }; } }; // src/storage/domains/tool-provider-connections/base.ts var ToolProviderConnectionsStorage = class extends chunkWSD4JNMB_cjs.MastraBase { constructor() { super({ component: "STORAGE", name: "TOOL_PROVIDER_CONNECTIONS" }); } }; // src/storage/domains/tool-provider-connections/inmemory.ts function connKey(authorId, providerId, connectionId) { return `${authorId}\0${providerId}\0${connectionId}`; } var InMemoryToolProviderConnectionsStorage = class extends ToolProviderConnectionsStorage { db; constructor({ db }) { super(); this.db = db; } async init() { } async dangerouslyClearAll() { this.db.toolProviderConnections.clear(); } async getConnectionById({ authorId, providerId, connectionId }) { return this.db.toolProviderConnections.get(connKey(authorId, providerId, connectionId)) ?? null; } async upsertConnection(input) { const key = connKey(input.authorId, input.providerId, input.connectionId); const existing = this.db.toolProviderConnections.get(key); const now = /* @__PURE__ */ new Date(); const row = { authorId: input.authorId, providerId: input.providerId, toolkit: input.toolkit, connectionId: input.connectionId, label: input.label, scope: input.scope ?? existing?.scope ?? "per-author", createdAt: existing?.createdAt ?? now, updatedAt: now }; this.db.toolProviderConnections.set(key, row); return row; } async listConnectionsByAuthor({ authorId, providerId, toolkit, scope }) { const rows = []; for (const row of this.db.toolProviderConnections.values()) { if (authorId !== void 0 && row.authorId !== authorId) continue; if (providerId && row.providerId !== providerId) continue; if (toolkit && row.toolkit !== toolkit) continue; if (scope && row.scope !== scope) continue; rows.push(row); } return rows; } async deleteConnection({ authorId, providerId, connectionId }) { this.db.toolProviderConnections.delete(connKey(authorId, providerId, connectionId)); } }; // src/storage/workflow-snapshot.ts var PENDING_MARKER_KEY = "__mastra_pending__"; function isPendingMarker(val) { return val !== null && typeof val === "object" && Object.prototype.hasOwnProperty.call(val, PENDING_MARKER_KEY) && val[PENDING_MARKER_KEY] === true && Object.keys(val).length === 1; } function isSuspendedStepResult(val) { const result = val; return val !== null && typeof val === "object" && "status" in val && result?.status === "suspended" && ("suspendPayload" in val || "suspendedAt" in val); } function canResetWithPendingMarker(val) { if (val == null || isPendingMarker(val)) { return true; } return isSuspendedStepResult(val); } function createEmptyWorkflowSnapshot(runId) { return { context: {}, activePaths: [], activeStepsPath: {}, timestamp: Date.now(), suspendedPaths: {}, resumeLabels: {}, serializedStepGraph: [], value: {}, waitingPaths: {}, status: "pending", runId }; } function mergeWorkflowStepResult({ snapshot, stepId, result, requestContext }) { if (!snapshot?.context) { throw new Error(`Snapshot context not found for runId ${snapshot?.runId}`); } const existingResult = snapshot.context[stepId]; if (existingResult && "output" in existingResult && Array.isArray(existingResult.output) && result && typeof result === "object" && "output" in result && Array.isArray(result.output)) { const existingOutput = existingResult.output; const newOutput = result.output; const mergedOutput = [...existingOutput]; const hasPendingMarker = newOutput.some(isPendingMarker); for (let i = 0; i < Math.max(existingOutput.length, newOutput.length); i++) { if (i < newOutput.length) { const newVal = newOutput[i]; if (isPendingMarker(newVal)) { if (i >= existingOutput.length || canResetWithPendingMarker(existingOutput[i])) { mergedOutput[i] = null; } } else if (newVal !== null && newVal !== void 0 && !hasPendingMarker) { mergedOutput[i] = newVal; } else if (i >= existingOutput.length) { mergedOutput[i] = null; } } } snapshot.context[stepId] = { ...existingResult, // Pending-marker writes are reset commands built from an earlier snapshot, // so keep existing step-level fields and ignore sibling values they carry. ...hasPendingMarker ? {} : result, output: mergedOutput }; } else { snapshot.context[stepId] = result; } snapshot.requestContext = { ...snapshot.requestContext, ...requestContext }; try { return JSON.parse(JSON.stringify(snapshot.context)); } catch { return { ...snapshot.context }; } } // src/storage/domains/workflows/base.ts var WorkflowsStorage = class extends chunk2UAP4LDC_cjs.StorageDomain { constructor() { super({ component: "STORAGE", name: "WORKFLOWS" }); } }; // src/storage/domains/workflows/inmemory.ts function cloneRunData(value) { return deepCloneForRun(value, /* @__PURE__ */ new WeakMap()); } function deepCloneForRun(value, seen) { if (value === null || typeof value !== "object") return value; const cached = seen.get(value); if (cached !== void 0) return cached; if (value instanceof Date) { return new Date(value.getTime()); } if (value instanceof RegExp) { return new RegExp(value.source, value.flags); } if (value instanceof URL) { return new URL(value.href); } if (value instanceof Map) { const out2 = /* @__PURE__ */ new Map(); seen.set(value, out2); for (const [k, v] of value) { out2.set(deepCloneForRun(k, seen), deepCloneForRun(v, seen)); } return out2; } if (value instanceof Set) { const out2 = /* @__PURE__ */ new Set(); seen.set(value, out2); for (const v of value) { out2.add(deepCloneForRun(v, seen)); } return out2; } if (value instanceof ArrayBuffer) { return value.slice(0); } if (ArrayBuffer.isView(value)) { if (value instanceof DataView) { return new DataView(value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength)); } const typed = value; return new typed.constructor(typed); } if (value instanceof Error) { const out2 = Object.create(Object.getPrototypeOf(value)); Object.defineProperty(out2, "message", { value: value.message, writable: true, configurable: true, enumerable: true }); Object.defineProperty(out2, "name", { value: value.name, writable: true, configurable: true }); const errRecord = value; let includeStack = value.stack !== void 0; if (includeStack && typeof errRecord.toJSON === "function") { try { const serialized = errRecord.toJSON(); if (serialized && typeof serialized === "object" && !("stack" in serialized)) { includeStack = false; } } catch { } } if (includeStack) { Object.defineProperty(out2, "stack", { value: value.stack, writable: true, configurable: true }); } seen.set(value, out2); const outRecord = out2; if (value.cause !== void 0) outRecord.cause = deepCloneForRun(value.cause, seen); for (const key of Object.keys(value)) { outRecord[key] = deepCloneForRun(errRecord[key], seen); } return out2; } if (Array.isArray(value)) { const out2 = new Array(value.length); seen.set(value, out2); for (let i = 0; i < value.length; i++) { out2[i] = deepCloneForRun(value[i], seen); } return out2; } const proto = Object.getPrototypeOf(value); const out = proto === Object.prototype ? {} : Object.create(proto); seen.set(value, out); for (const key of Object.keys(value)) { out[key] = deepCloneForRun(value[key], seen); } return out; } var WorkflowsInMemory = class extends WorkflowsStorage { db; constructor({ db }) { super(); this.db = db; } supportsConcurrentUpdates() { return true; } async dangerouslyClearAll() { this.db.workflows.clear(); } getWorkflowKey(workflowName, runId) { return `${workflowName}-${runId}`; } async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext }) { const key = this.getWorkflowKey(workflowName, runId); const run = this.db.workflows.get(key); if (!run) { return {}; } let snapshot; if (!run.snapshot) { snapshot = createEmptyWorkflowSnapshot(run.run_id); this.db.workflows.set(key, { ...run, snapshot }); } else { snapshot = typeof run.snapshot === "string" ? JSON.parse(run.snapshot) : run.snapshot; } if (!snapshot || !snapshot?.context) { throw new Error(`Snapshot not found for runId ${runId}`); } const context = mergeWorkflowStepResult({ snapshot, stepId, result, requestContext }); this.db.workflows.set(key, { ...run, snapshot }); return cloneRunData(context); } async updateWorkflowState({ workflowName, runId, opts }) { const key = this.getWorkflowKey(workflowName, runId); const run = this.db.workflows.get(key); if (!run) { return; } let snapshot; if (!run.snapshot) { snapshot = createEmptyWorkflowSnapshot(run.run_id); this.db.workflows.set(key, { ...run, snapshot }); } else { snapshot = typeof run.snapshot === "string" ? JSON.parse(run.snapshot) : run.snapshot; } if (!snapshot || !snapshot?.context) { throw new Error(`Snapshot not found for runId ${runId}`); } snapshot = { ...snapshot, ...opts }; this.db.workflows.set(key, { ...run, snapshot }); return snapshot; } async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) { const key = this.getWorkflowKey(workflowName, runId); const now = /* @__PURE__ */ new Date(); const data = { workflow_name: workflowName, run_id: runId, resourceId, snapshot, createdAt: createdAt ?? now, updatedAt: updatedAt ?? now }; this.db.workflows.set(key, data); } async loadWorkflowSnapshot({ workflowName, runId }) { const key = this.getWorkflowKey(workflowName, runId); const run = this.db.workflows.get(key); if (!run) { return null; } const snapshot = typeof run.snapshot === "string" ? JSON.parse(run.snapshot) : run.snapshot; return snapshot ? cloneRunData(snapshot) : null; } async listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId, status } = {}) { if (page !== void 0 && page < 0) { throw new Error("page must be >= 0"); } let runs = Array.from(this.db.workflows.values()); if (workflowName) runs = runs.filter((run) => run.workflow_name === workflowName); if (status) { runs = runs.filter((run) => { let snapshot = run?.snapshot; if (!snapshot) { return false; } if (typeof snapshot === "string") { try { snapshot = JSON.parse(snapshot); } catch { return false; } } else { snapshot = cloneRunData(snapshot); } return snapshot.status === status; }); } if (fromDate && toDate) { runs = runs.filter( (run) => new Date(run.createdAt).getTime() >= fromDate.getTime() && new Date(run.createdAt).getTime() <= toDate.getTime() ); } else if (fromDate) { runs = runs.filter((run) => new Date(run.createdAt).getTime() >= fromDate.getTime()); } else if (toDate) { runs = runs.filter((run) => new Date(run.createdAt).getTime() <= toDate.getTime()); } if (resourceId) runs = runs.filter((run) => run.resourceId === resourceId); const total = runs.length; runs.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); if (perPage !== void 0 && page !== void 0) { const normalizedPerPage = chunk2UAP4LDC_cjs.normalizePerPage(perPage, Number.MAX_SAFE_INTEGER); const offset = page * normalizedPerPage; const start = offset; const end = start + normalizedPerPage; runs = runs.slice(start, end); } const parsedRuns = runs.map((run) => ({ ...run, snapshot: typeof run.snapshot === "string" ? JSON.parse(run.snapshot) : cloneRunData(run.snapshot), createdAt: new Date(run.createdAt), updatedAt: new Date(run.updatedAt), runId: run.run_id, workflowName: run.workflow_name, resourceId: run.resourceId })); return { runs: parsedRuns, total }; } async getWorkflowRunById({ runId, workflowName }) { const runs = Array.from(this.db.workflows.values()).filter((r) => r.run_id === runId); let run = runs.find((r) => r.workflow_name === workflowName); if (!run) return null; const parsedRun = { ...run, snapshot: typeof run.snapshot === "string" ? JSON.parse(run.snapshot) : cloneRunData(run.snapshot), createdAt: new Date(run.createdAt), updatedAt: new Date(run.updatedAt), runId: run.run_id, workflowName: run.workflow_name, resourceId: run.resourceId }; return parsedRun; } async deleteWorkflowRunById({ runId, workflowName }) { const key = this.getWorkflowKey(workflowName, runId); this.db.workflows.delete(key); } }; // src/storage/mock.ts var InMemoryStore = class extends chunk2UAP4LDC_cjs.MastraCompositeStore { stores; /** * Internal database layer shared across all domains. * This is an implementation detail - domains interact with this * rather than managing their own data structures. */ #db; constructor({ id = "in-memory" } = {}) { super({ id, name: "InMemoryStorage" }); this.hasInitialized = Promise.resolve(true); this.#db = new chunkOLZ4GVNA_cjs.InMemoryDB(); this.stores = { memory: new InMemoryMemory({ db: this.#db }), workflows: new WorkflowsInMemory({ db: this.#db }), scores: new ScoresInMemory({ db: this.#db }), observability: new ObservabilityInMemory({ db: this.#db }), agents: new chunkOLZ4GVNA_cjs.InMemoryAgentsStorage({ db: this.#db }), channels: new InMemoryChannelsStorage(), notifications: new InMemoryNotificationsStorage(), datasets: new DatasetsInMemory({ db: this.#db }), experiments: new ExperimentsInMemory({ db: this.#db }), promptBlocks: new chunkSC3Z566U_cjs.InMemoryPromptBlocksStorage({ db: this.#db }), scorerDefinitions: new chunk4332X24K_cjs.InMemoryScorerDefinitionsStorage({ db: this.#db }), mcpClients: new chunkPY3SBFQA_cjs.InMemoryMCPClientsStorage({ db: this.#db }), mcpServers: new chunkM3CR7ZNG_cjs.InMemoryMCPServersStorage({ db: this.#db }), workspaces: new chunkUNCK5F5J_cjs.InMemoryWorkspacesStorage({ db: this.#db }), skills: new chunkZVZXKOMS_cjs.InMemorySkillsStorage({ db: this.#db }), favorites: new chunkGJE2FI3V_cjs.InMemoryFavoritesStorage({ db: this.#db }), blobs: new InMemoryBlobStore(), backgroundTasks: new BackgroundTasksInMemory({ db: this.#db }), schedules: new InMemorySchedulesStorage({ db: this.#db }), harness: new InMemoryHarness(), toolProviderConnections: new InMemoryToolProviderConnectionsStorage({ db: this.#db }), threadState: new chunk2UAP4LDC_cjs.InMemoryThreadStateStorage() }; } /** * Clears all data from the in-memory database. * Useful for testing. * @deprecated Use dangerouslyClearAll() on individual domains instead. */ clear() { this.#db.clear(); void this.stores.channels?.dangerouslyClearAll?.(); void this.stores.harness?.dangerouslyClearAll?.(); void this.stores.notifications?.dangerouslyClearAll?.(); } }; var MockStore = InMemoryStore; var FilesystemDB = class { dir; /** In-memory cache of parsed domain data, keyed by filename */ cache = /* @__PURE__ */ new Map(); initialized = false; constructor(dir) { this.dir = dir; } /** * Initialize the storage directory. Called once; subsequent calls are no-ops. */ async init() { if (this.initialized) return; this.ensureDir(); this.initialized = true; } /** * Ensure the storage directory and skills subdirectory exist. */ ensureDir() { if (!fs.existsSync(this.dir)) { fs.mkdirSync(this.dir, { recursive: true }); } const skillsDir = path.join(this.dir, "skills"); if (!fs.existsSync(skillsDir)) { fs.mkdirSync(skillsDir, { recursive: true }); } } // ========================================================================== // Domain-level JSON operations // ========================================================================== /** * Read a domain JSON file and return its entity map. * Uses in-memory cache; reads from disk on first access. */ readDomain(filename) { if (this.cache.has(filename)) { return this.cache.get(filename); } const filePath = path.join(this.dir, filename); let data = {}; if (fs.existsSync(filePath)) { try { const raw = fs.readFileSync(filePath, "utf-8"); data = JSON.parse(raw, dateReviver); } catch { data = {}; } } this.cache.set(filename, data); return data; } /** * Write a domain's full entity map to its JSON file. * Uses atomic write (write to .tmp, then rename) to prevent corruption. */ writeDomain(filename, data) { this.cache.set(filename, data); const filePath = path.join(this.dir, filename); const tmpPath = filePath + ".tmp"; const parentDir = path.dirname(filePath); if (!fs.existsSync(parentDir)) { fs.mkdirSync(parentDir, { recursive: true }); } fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf-8"); fs.renameSync(tmpPath, filePath); } /** * Clear all data from a domain JSON file. */ clearDomain(filename) { this.writeDomain(filename, {}); } listDomainFiles(directory, extension = ".json") { const baseDir = path.resolve(this.dir, directory); const rootDir = path.resolve(this.dir); if (!baseDir.startsWith(rootDir + path.sep) && baseDir !== rootDir) { throw new Error(`Path traversal detected: directory "${directory}" escapes storage directory`); } if (!fs.existsSync(baseDir)) return []; if (!fs.statSync(baseDir).isDirectory()) { throw new Error(`Configured domain path "${directory}" is a file, expected a directory`); } return fs.readdirSync(baseDir).filter((file) => path.extname(file) === extension && fs.statSync(path.join(baseDir, file)).isFile()).map((file) => `${directory}/${file}`); } /** * Check whether a domain file currently exists on disk. */ domainFileExists(filename) { const filePath = path.resolve(this.dir, filename); const rootDir = path.resolve(this.dir); if (!filePath.startsWith(rootDir + path.sep) && filePath !== rootDir) { throw new Error(`Path traversal detected: file "${filename}" escapes storage directory`); } return fs.existsSync(filePath); } removeDomainFile(filename) { this.cache.delete(filename); const filePath = path.resolve(this.dir, filename); const rootDir = path.resolve(this.dir); if (!filePath.startsWith(rootDir + path.sep) && filePath !== rootDir) { throw new Error(`Path traversal detected: file "${filename}" escapes storage directory`); } if (fs.existsSync(filePath)) { fs.rmSync(filePath); } } /** * Invalidate the in-memory cache for a domain, forcing a re-read from disk on next access. */ invalidateCache(filename) { if (filename) { this.cache.delete(filename); } else { this.cache.clear(); } } // ========================================================================== // Entity-level convenience methods (used by FilesystemVersionedHelpers) // ========================================================================== /** * Get a single entity by ID from a domain JSON file. */ get(filename, id) { const data = this.readDomain(filename); return data[id] ?? null; } /** * Get all entities from a domain JSON file as an array. */ getAll(filename) { const data = this.readDomain(filename); return Object.values(data); } /** * Set (create or update) an entity in a domain JSON file. */ set(filename, id, entity) { const data = this.readDomain(filename); data[id] = entity; this.writeDomain(filename, data); } /** * Remove an entity by ID from a domain JSON file. No-op if not found. */ remove(filename, id) { const data = this.readDomain(filename); if (id in data) { delete data[id]; this.writeDomain(filename, data); } } // ========================================================================= // Skills directory operations (real file tree, not JSON) // ========================================================================= /** * Get the path to a skill's directory. */ skillDir(skillName) { const skillsBase = path.join(this.dir, "skills"); const dir = path.resolve(skillsBase, skillName); if (!dir.startsWith(skillsBase + path.sep) && dir !== skillsBase) { throw new Error(`Path traversal detected: skill name "${skillName}" escapes skills directory`); } return dir; } /** * Resolve a file path within a skill directory, throwing if it escapes. */ safeSkillPath(skillName, relativePath) { const base = this.skillDir(skillName); const resolved = path.resolve(base, relativePath); if (!resolved.startsWith(base + path.sep) && resolved !== base) { throw new Error(`Path traversal detected: "${relativePath}" escapes skill directory`); } return resolved; } /** * List all files in a skill's directory, returning relative paths. */ listSkillFiles(skillName) { const dir = this.skillDir(skillName); if (!fs.existsSync(dir)) return []; return walkDir(dir).map((abs) => path.relative(dir, abs).split(path.sep).join("/")); } /** * Read a file from a skill's directory. */ readSkillFile(skillName, relativePath) { const filePath = this.safeSkillPath(skillName, relativePath); if (!fs.existsSync(filePath)) return null; try { return fs.readFileSync(filePath); } catch { return null; } } /** * Write a file to a skill's directory. */ writeSkillFile(skillName, relativePath, content) { const filePath = this.safeSkillPath(skillName, relativePath); const parentDir = path.dirname(filePath); if (!fs.existsSync(parentDir)) { fs.mkdirSync(parentDir, { recursive: true }); } fs.writeFileSync(filePath, content); } /** * Delete a skill's entire directory. */ deleteSkillDir(skillName) { const dir = this.skillDir(skillName); if (fs.existsSync(dir)) { fs.rmSync(dir, { recursive: true, force: true }); } } }; function dateReviver(_key, value) { if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) { const d = new Date(value); if (!isNaN(d.getTime())) return d; } return value; } function walkDir(dir) { const results = []; for (const entry of fs.readdirSync(dir)) { const fullPath = path.join(dir, entry); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { results.push(...walkDir(fullPath)); } else { results.push(fullPath); } } return results; } // src/storage/filesystem.ts var FilesystemStore = class extends chunk2UAP4LDC_cjs.MastraCompositeStore { #db; #dir; constructor(config = {}) { const dir = path.resolve(config.dir ?? ".mastra-storage"); super({ id: "filesystem", name: "FilesystemStore" }); this.#dir = dir; this.#db = new FilesystemDB(dir); this.stores = { agents: new chunkOLZ4GVNA_cjs.FilesystemAgentsStorage({ db: this.#db }), promptBlocks: new chunkSC3Z566U_cjs.FilesystemPromptBlocksStorage({ db: this.#db }), scorerDefinitions: new chunk4332X24K_cjs.FilesystemScorerDefinitionsStorage({ db: this.#db }), mcpClients: new chunkPY3SBFQA_cjs.FilesystemMCPClientsStorage({ db: this.#db }), mcpServers: new chunkM3CR7ZNG_cjs.FilesystemMCPServersStorage({ db: this.#db }), workspaces: new chunkUNCK5F5J_cjs.FilesystemWorkspacesStorage({ db: this.#db }), skills: new chunkZVZXKOMS_cjs.FilesystemSkillsStorage({ db: this.#db }) }; } /** * The absolute path to the storage directory. */ get dir() { return this.#dir; } }; // src/storage/providers/github.ts var GitHubSourceControlProvider = class { id = "github"; displayName = "GitHub"; endpoint; token; pathPrefix; fetch; constructor(config) { this.endpoint = normalizeApiEndpoint(config.endpoint); this.token = config.token; this.pathPrefix = normalizePathPrefix(config.pathPrefix ?? "mastra/editor"); this.fetch = config.fetch ?? fetch; } async getCapabilities() { return this.request("/capabilities"); } async readFile(input) { const path = this.sourcePath(input.path); const query = new URLSearchParams({ path }); if (input.ref) query.set("ref", input.ref); const result = await this.request(`/files?${query.toString()}`); return result ? { ...result, path: input.path } : null; } async writeFile(input) { const result = await this.request("/files", { method: "POST", body: JSON.stringify({ ...input, path: this.sourcePath(input.path) }) }); return { ...result, path: input.path }; } async listFileHistory(input) { const query = new URLSearchParams({ path: this.sourcePath(input.path) }); if (input.ref) query.set("ref", input.ref); if (input.limit) query.set("limit", String(input.limit)); return this.request(`/files/history?${query.toString()}`); } async listFiles(input) { const query = new URLSearchParams({ path: this.sourcePath(input.path) }); if (input.ref) query.set("ref", input.ref); const files = await this.request(`/files/list?${query.toString()}`); return files.map((file) => ({ ...file, path: this.unsourcePath(file.path) })); } async openChangeRequest(input) { return this.request("/change-requests", { method: "POST", body: JSON.stringify({ ...input, files: input.files.map((file) => ({ ...file, path: this.sourcePath(file.path) })) }) }); } sourcePath(path) { const normalizedPath = stripLeadingSlashes(path); return this.pathPrefix ? `${this.pathPrefix}/${normalizedPath}` : normalizedPath; } unsourcePath(path) { const normalizedPath = stripLeadingSlashes(path); const prefix = this.pathPrefix ? `${this.pathPrefix}/` : ""; return prefix && normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : normalizedPath; } async request(path, init) { const res = await this.fetch(`${this.endpoint}/v1/server/source-storage/github${path}`, { ...init, headers: { Authorization: `Bearer ${this.token}`, Accept: "application/json", "Content-Type": "application/json", ...init?.headers } }); if (!res.ok) { let detail = `GitHub source control request failed: ${res.status}`; try { const body = await res.json(); detail = body.detail ?? detail; } catch { } throw new Error(detail); } return await res.json(); } }; function createGitHubSourceControlProviderFromEnv(env = process.env, defaults) { if (env.MASTRA_SOURCE_PROVIDER !== "github") return void 0; const endpoint = env.MASTRA_SOURCE_PROVIDER_ENDPOINT ?? env.MASTRA_SHARED_API_URL ?? env.MASTRA_CLOUD_API_ENDPOINT; const token = env.MASTRA_PLATFORM_ACCESS_TOKEN ?? env.MASTRA_CLOUD_ACCESS_TOKEN; if (!endpoint || !token) return void 0; return new GitHubSourceControlProvider({ endpoint: normalizeApiEndpoint(endpoint), token, pathPrefix: env.MASTRA_SOURCE_STORAGE_PATH_PREFIX ?? defaults?.pathPrefix }); } function normalizeApiEndpoint(endpoint) { const trimmed = stripTrailingSlashes(endpoint); const withoutV1 = trimmed.endsWith("/v1") ? trimmed.slice(0, -3) : trimmed; return stripTrailingSlashes(withoutV1); } function normalizePathPrefix(pathPrefix) { return stripTrailingSlashes(stripLeadingSlashes(pathPrefix)); } function stripLeadingSlashes(value) { let start = 0; while (start < value.length && value[start] === "/") start += 1; return value.slice(start); } function stripTrailingSlashes(value) { let end = value.length; while (end > 0 && value[end - 1] === "/") end -= 1; return value.slice(0, end); } // src/storage/domains/operations/base.ts var StoreOperations = class extends chunkWSD4JNMB_cjs.MastraBase { constructor() { super({ component: "STORAGE", name: "OPERATIONS" }); } getSqlType(type) { switch (type) { case "text": return "TEXT"; case "timestamp": return "TIMESTAMP"; case "float": return "FLOAT"; case "integer": return "INTEGER"; case "bigint": return "BIGINT"; case "jsonb": return "JSONB"; default: return "TEXT"; } } getDefaultValue(type) { switch (type) { case "text": case "uuid": return "DEFAULT ''"; case "timestamp": return "DEFAULT '1970-01-01 00:00:00'"; case "integer": case "bigint": case "float": return "DEFAULT 0"; case "jsonb": return "DEFAULT '{}'"; default: return "DEFAULT ''"; } } /** * DATABASE INDEX MANAGEMENT * Optional methods for database index management. * Storage adapters can override these to provide index management capabilities. */ /** * Creates a database index on specified columns * @throws {MastraError} if not supported by the storage adapter */ async createIndex(_options) { throw new chunkXSOONORA_cjs.MastraError({ id: "MASTRA_STORAGE_CREATE_INDEX_NOT_SUPPORTED", domain: chunkXSOONORA_cjs.ErrorDomain.STORAGE, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: `Index management is not supported by this storage adapter` }); } /** * Drops a database index by name * @throws {MastraError} if not supported by the storage adapter */ async dropIndex(_indexName) { throw new chunkXSOONORA_cjs.MastraError({ id: "MASTRA_STORAGE_DROP_INDEX_NOT_SUPPORTED", domain: chunkXSOONORA_cjs.ErrorDomain.STORAGE, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: `Index management is not supported by this storage adapter` }); } /** * Lists database indexes for a table or all tables * @throws {MastraError} if not supported by the storage adapter */ async listIndexes(_tableName) { throw new chunkXSOONORA_cjs.MastraError({ id: "MASTRA_STORAGE_LIST_INDEXES_NOT_SUPPORTED", domain: chunkXSOONORA_cjs.ErrorDomain.STORAGE, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: `Index management is not supported by this storage adapter` }); } /** * Gets detailed statistics for a specific index * @throws {MastraError} if not supported by the storage adapter */ async describeIndex(_indexName) { throw new chunkXSOONORA_cjs.MastraError({ id: "MASTRA_STORAGE_DESCRIBE_INDEX_NOT_SUPPORTED", domain: chunkXSOONORA_cjs.ErrorDomain.STORAGE, category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM, text: `Index management is not supported by this storage adapter` }); } /** * Returns definitions for automatic performance indexes * Storage adapters can override this to define indexes that should be created during initialization * @returns Array of index definitions to create automatically */ getAutomaticIndexDefinitions() { return []; } }; // src/storage/domains/operations/inmemory.ts var StoreOperationsInMemory = class extends StoreOperations { data; constructor() { super(); this.data = { mastra_workflow_snapshot: /* @__PURE__ */ new Map(), mastra_messages: /* @__PURE__ */ new Map(), mastra_threads: /* @__PURE__ */ new Map(), mastra_traces: /* @__PURE__ */ new Map(), mastra_resources: /* @__PURE__ */ new Map(), mastra_scorers: /* @__PURE__ */ new Map(), mastra_ai_spans: /* @__PURE__ */ new Map(), mastra_agents: /* @__PURE__ */ new Map(), mastra_agent_versions: /* @__PURE__ */ new Map(), mastra_observational_memory: /* @__PURE__ */ new Map(), mastra_prompt_blocks: /* @__PURE__ */ new Map(), mastra_prompt_block_versions: /* @__PURE__ */ new Map(), mastra_scorer_definitions: /* @__PURE__ */ new Map(), mastra_scorer_definition_versions: /* @__PURE__ */ new Map(), mastra_mcp_clients: /* @__PURE__ */ new Map(), mastra_mcp_client_versions: /* @__PURE__ */ new Map(), mastra_mcp_servers: /* @__PURE__ */ new Map(), mastra_mcp_server_versions: /* @__PURE__ */ new Map(), mastra_workspaces: /* @__PURE__ */ new Map(), mastra_workspace_versions: /* @__PURE__ */ new Map(), mastra_skills: /* @__PURE__ */ new Map(), mastra_skill_versions: /* @__PURE__ */ new Map(), mastra_skill_blobs: /* @__PURE__ */ new Map(), mastra_datasets: /* @__PURE__ */ new Map(), mastra_dataset_items: /* @__PURE__ */ new Map(), mastra_dataset_versions: /* @__PURE__ */ new Map(), mastra_experiments: /* @__PURE__ */ new Map(), mastra_experiment_results: /* @__PURE__ */ new Map(), mastra_background_tasks: /* @__PURE__ */ new Map(), mastra_favorites: /* @__PURE__ */ new Map(), mastra_schedules: /* @__PURE__ */ new Map(), mastra_schedule_triggers: /* @__PURE__ */ new Map(), mastra_channel_installations: /* @__PURE__ */ new Map(), mastra_channel_config: /* @__PURE__ */ new Map(), mastra_tool_provider_connections: /* @__PURE__ */ new Map(), mastra_notifications: /* @__PURE__ */ new Map(), mastra_harness_sessions: /* @__PURE__ */ new Map(), mastra_thread_state: /* @__PURE__ */ new Map() }; } getDatabase() { return this.data; } async insert({ tableName, record }) { const table = this.data[tableName]; let key = record.id; if (tableName === chunkQYKNPGZG_cjs.TABLE_NOTIFICATIONS && record.threadId && record.id) { key = `${record.threadId}\0${record.id}`; } else if ([chunkQYKNPGZG_cjs.TABLE_WORKFLOW_SNAPSHOT].includes(tableName) && !record.id && record.run_id) { key = record.workflow_name ? `${record.workflow_name}-${record.run_id}` : record.run_id; record.id = key; } else if (!record.id) { key = `auto-${Date.now()}-${Math.random()}`; record.id = key; } table.set(key, record); } async batchInsert({ tableName, records }) { const table = this.data[tableName]; for (const record of records) { let key = record.id; if (tableName === chunkQYKNPGZG_cjs.TABLE_NOTIFICATIONS && record.threadId && record.id) { key = `${record.threadId}\0${record.id}`; } else if ([chunkQYKNPGZG_cjs.TABLE_WORKFLOW_SNAPSHOT].includes(tableName) && !record.id && record.run_id) { key = record.run_id; record.id = key; } else if (!record.id) { key = `auto-${Date.now()}-${Math.random()}`; record.id = key; } table.set(key, record); } } async load({ tableName, keys }) { const table = this.data[tableName]; const records = Array.from(table.values()); return records.filter((record) => Object.keys(keys).every((key) => record[key] === keys[key]))?.[0]; } async createTable({ tableName, schema: _schema }) { this.data[tableName] = /* @__PURE__ */ new Map(); } async clearTable({ tableName }) { this.data[tableName].clear(); } async dropTable({ tableName }) { this.data[tableName].clear(); } async alterTable({ tableName: _tableName, schema: _schema }) { } async hasColumn(_table, _column) { return true; } }; exports.BackgroundTasksInMemory = BackgroundTasksInMemory; exports.BackgroundTasksStorage = BackgroundTasksStorage; exports.BlobStore = BlobStore; exports.ChannelsStorage = ChannelsStorage; exports.DatasetsInMemory = DatasetsInMemory; exports.DatasetsStorage = DatasetsStorage; exports.ExperimentsInMemory = ExperimentsInMemory; exports.ExperimentsStorage = ExperimentsStorage; exports.FilesystemDB = FilesystemDB; exports.FilesystemStore = FilesystemStore; exports.GitHubSourceControlProvider = GitHubSourceControlProvider; exports.HarnessStorage = HarnessStorage; exports.InMemoryBlobStore = InMemoryBlobStore; exports.InMemoryChannelsStorage = InMemoryChannelsStorage; exports.InMemoryHarness = InMemoryHarness; exports.InMemoryMemory = InMemoryMemory; exports.InMemoryNotificationsStorage = InMemoryNotificationsStorage; exports.InMemorySchedulesStorage = InMemorySchedulesStorage; exports.InMemoryStore = InMemoryStore; exports.InMemoryToolProviderConnectionsStorage = InMemoryToolProviderConnectionsStorage; exports.MemoryStorage = MemoryStorage; exports.MockStore = MockStore; exports.NotificationsStorage = NotificationsStorage; exports.ObservabilityInMemory = ObservabilityInMemory; exports.ObservabilityStorage = ObservabilityStorage; exports.SchedulesStorage = SchedulesStorage; exports.SchemaUpdateValidationError = SchemaUpdateValidationError; exports.SchemaValidationError = SchemaValidationError; exports.SchemaValidator = SchemaValidator; exports.ScoresInMemory = ScoresInMemory; exports.ScoresStorage = ScoresStorage; exports.StoreOperations = StoreOperations; exports.StoreOperationsInMemory = StoreOperationsInMemory; exports.ToolProviderConnectionsStorage = ToolProviderConnectionsStorage; exports.WorkflowsInMemory = WorkflowsInMemory; exports.WorkflowsStorage = WorkflowsStorage; exports.agentThreadStreamRuntime = agentThreadStreamRuntime; exports.buildCreateSpanRecord = buildCreateSpanRecord; exports.buildFeedbackRecord = buildFeedbackRecord; exports.buildLogRecord = buildLogRecord; exports.buildMetricRecord = buildMetricRecord; exports.buildScoreRecord = buildScoreRecord; exports.buildUpdateSpanRecord = buildUpdateSpanRecord; exports.cloneRunData = cloneRunData; exports.createEmptyWorkflowSnapshot = createEmptyWorkflowSnapshot; exports.createGitHubSourceControlProviderFromEnv = createGitHubSourceControlProviderFromEnv; exports.createNotificationSignal = createNotificationSignal; exports.createNotificationSummarySignal = createNotificationSummarySignal; exports.createStorageErrorId = createStorageErrorId; exports.createStoreErrorId = createStoreErrorId; exports.createValidator = createValidator; exports.createVectorErrorId = createVectorErrorId; exports.dispatchDueNotifications = dispatchDueNotifications; exports.ensureDate = ensureDate; exports.filterByDateRange = filterByDateRange; exports.getDefaultValue = getDefaultValue; exports.getObjectOrNull = getObjectOrNull; exports.getSchemaValidator = getSchemaValidator; exports.getSqlType = getSqlType; exports.getStringOrNull = getStringOrNull; exports.jsonValueEquals = jsonValueEquals; exports.mergeWorkflowStepResult = mergeWorkflowStepResult; exports.notificationSignalAttributes = notificationSignalAttributes; exports.notificationSignalMetadata = notificationSignalMetadata; exports.notificationSummaryContents = notificationSummaryContents; exports.notificationSummarySignalMetadata = notificationSummarySignalMetadata; exports.safelyParseJSON = safelyParseJSON; exports.serializeDate = serializeDate; exports.serializeSpanAttributes = serializeSpanAttributes; exports.summarizeNotifications = summarizeNotifications; exports.toEntityType = toEntityType; exports.transformRow = transformRow; exports.transformScoreRow = transformScoreRow; //# sourceMappingURL=chunk-TMPKR5YT.cjs.map //# sourceMappingURL=chunk-TMPKR5YT.cjs.map