UNPKG

@mastra/core

Version:
1,381 lines • 273 kB
import { i as __toESM, t as __commonJSMin } from "./rolldown-runtime-DP3BCW9_.js"; import { t as MastraBase } from "./base-BeUQ6mLP.js"; import { i as MastraError, n as ErrorDomain, t as ErrorCategory } from "./error-MjDSls8S.js"; import { Kt as listScoresArgsSchema, Ut as listMetricsArgsSchema, Vt as listLogsArgsSchema, f as EntityType, zt as listFeedbackArgsSchema } from "./utils-DxsDNzD2.js"; import { n as MessageList } from "./message-list-mC29laJJ.js"; import { $ as TABLE_SCORERS, Gt as listBranchesArgsSchema, Jt as listTracesArgsSchema, Mt as getBranchArgsSchema, Q as TABLE_SCHEMAS, bt as BRANCH_SPAN_TYPE_SET, jt as extractBranchSpans, nn as toTraceSpan, rn as toTraceSpans } from "./constants-BfpAlX25.js"; import { c as MastraCompositeStore, d as normalizePerPage, f as InMemoryThreadStateStorage, u as calculatePagination } from "./filesystem-versioned-M7zmYTpZ.js"; import { t as StorageDomain } from "./base-Bt5IshKX.js"; import { coreFeatures } from "./features/index.js"; import { i as InMemoryAgentsStorage, n as FilesystemAgentsStorage, r as InMemoryDB } from "./source-xpSE-8BU.js"; import { t as InMemoryFavoritesStorage } from "./inmemory-BRKxS_86.js"; import { n as InMemoryMCPClientsStorage, t as FilesystemMCPClientsStorage } from "./filesystem-CW80Gvlh.js"; import { n as InMemoryMCPServersStorage, t as FilesystemMCPServersStorage } from "./filesystem-BAFBDAG1.js"; import { t as InMemoryNotificationsStorage } from "./storage-B1u4gxRl.js"; import { n as InMemoryPromptBlocksStorage, t as FilesystemPromptBlocksStorage } from "./filesystem-hzUov7lW.js"; import { n as InMemoryScorerDefinitionsStorage, t as FilesystemScorerDefinitionsStorage } from "./filesystem-CffraaeW.js"; import { n as InMemorySkillsStorage, t as FilesystemSkillsStorage } from "./filesystem-DDQhIkK3.js"; import { n as InMemoryWorkspacesStorage, t as FilesystemWorkspacesStorage } from "./filesystem-ePb2Dod3.js"; import { randomUUID } from "crypto"; import { z } from "zod/v4"; import { jsonSchemaToZod } from "@mastra/schema-compat/json-to-zod"; import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "fs"; import { dirname, extname, join, relative, resolve, sep } from "path"; //#region src/storage/factory-storage.ts /** * Thrown by `insertOne`/`upsertOne` when a unique constraint rejects the row. * Backends map their native duplicate-key errors onto this type so domains * can implement insert-or-recover races portably. */ var UniqueViolationError = class extends Error { collection; constructor(collection, options) { super(`Unique constraint violation on collection '${collection}'`, options); this.name = "UniqueViolationError"; this.collection = collection; } }; /** * Base class for application domains owned by a {@link FactoryStorage}. * Domains are bound once when registered and share their owner's connection. */ var FactoryStorageDomain = class extends StorageDomain { name; #storage; constructor(name) { if (!name.trim()) throw new Error("Factory storage domain name must not be empty"); super({ component: "STORAGE", name }); this.name = name; } /** @internal Bound by {@link FactoryStorage.registerDomain}. */ __bindFactoryStorage(storage) { if (this.#storage && this.#storage !== storage) throw new Error(`Factory storage domain '${this.name}' is already bound to another storage instance`); this.#storage = storage; } get storage() { if (!this.#storage) throw new Error(`Factory storage domain '${this.name}' has not been registered`); return this.#storage; } /** * Initialize this domain (via its owning storage) if it hasn't been yet. * Lets consumers holding a domain handle run the same fail-soft readiness * check as {@link FactoryStorage.ensureDomainReady} without also needing a * reference to the storage backend. */ ensureReady() { return this.storage.ensureDomainReady(this.name); } get ops() { return this.storage.ops; } ensureCollections(schemas) { return this.storage.ensureCollections(schemas); } }; /** * A pluggable application-storage backend: one database powering agent state * (via {@link getMastraStorage}) and app-owned collections (via {@link ops}). */ var FactoryStorage = class { #domains = /* @__PURE__ */ new Map(); #readyDomains = /* @__PURE__ */ new Set(); #domainErrors = /* @__PURE__ */ new Map(); #domainInitPromises = /* @__PURE__ */ new Map(); #storageReady = false; #storageInitPromise; /** Open/validate the backend, then initialize registered domains fail-soft. */ async init() { await this.#ensureStorageReady(); await Promise.all([...this.#domains.keys()].map((name) => this.#initDomain(name).catch(() => void 0))); } registerDomain(domain) { if (this.#domains.has(domain.name)) throw new Error(`Factory storage domain '${domain.name}' is already registered`); domain.__bindFactoryStorage(this); this.#domains.set(domain.name, domain); return domain; } getDomain(name) { const domain = this.#domains.get(name); if (!domain) throw new Error(`Factory storage domain '${name}' is not registered`); return domain; } hasDomain(name) { return this.#domains.has(name); } domainNames() { return [...this.#domains.keys()]; } isDomainReady(name) { return this.#readyDomains.has(name); } domainInitError(name) { return this.#domainErrors.get(name); } async ensureDomainReady(name) { this.getDomain(name); await this.#ensureStorageReady(); await this.#initDomain(name); } async #ensureStorageReady() { if (this.#storageReady) return; if (this.#storageInitPromise) return this.#storageInitPromise; const initPromise = (async () => { await this.initStorage(); this.#storageReady = true; })(); this.#storageInitPromise = initPromise; try { await initPromise; } finally { if (this.#storageInitPromise === initPromise) this.#storageInitPromise = void 0; } } #initDomain(name) { if (this.#readyDomains.has(name)) return Promise.resolve(); const pending = this.#domainInitPromises.get(name); if (pending) return pending; const domain = this.getDomain(name); this.#domainErrors.delete(name); const initPromise = (async () => { try { await domain.init(); this.#readyDomains.add(name); } catch (error) { this.#domainErrors.set(name, error); throw error; } finally { this.#domainInitPromises.delete(name); } })(); this.#domainInitPromises.set(name, initPromise); return initPromise; } }; //#endregion //#region src/storage/domains/observability/base.ts /** * Base storage class for observability data (traces, metrics, logs, scores, feedback). * Not abstract -- provides default implementations that throw "not implemented" errors. * Storage adapters override only the methods they support. */ var ObservabilityStorage = class ObservabilityStorage extends 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", 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 should override this to opt in to the APIs they support explicitly. * Older stores and older package versions will simply omit it, which keeps page mode working. */ getFeatures() {} /** * Creates a single Span record in the storage provider. */ async createSpan(_args) { throw new MastraError({ id: "OBSERVABILITY_CREATE_SPAN_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_UPDATE_SPAN_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support updating spans" }); } /** * Retrieves a single span. */ async getSpan(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_SPAN_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support getting spans" }); } /** * Retrieves a single root span. */ async getRootSpan(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_ROOT_SPAN_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_GET_TRACE_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_GET_STRUCTURE_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_GET_TRACE_LIGHT_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 = getBranchArgsSchema.parse(args); try { const skeleton = await this.getStructure({ traceId: parsed.traceId }); if (!skeleton) return null; const branchSpanIds = extractBranchSpans(skeleton.spans, parsed.spanId, parsed.depth).map((s) => s.spanId); if (branchSpanIds.length === 0) return null; const { spans } = await this.getSpans({ traceId: parsed.traceId, spanIds: branchSpanIds }); if (spans.length === 0) return null; spans.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime()); return { traceId: parsed.traceId, spans }; } catch (error) { if (!(error instanceof 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"))) throw error; } const trace = await this.getTrace({ traceId: parsed.traceId }); if (!trace) return null; const spans = 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 MastraError({ id: "OBSERVABILITY_STORAGE_GET_SPANS_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_LIST_TRACES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_LIST_TRACES_LIGHT_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_LIST_BRANCHES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support listing trace branches" }); } /** * Creates multiple Spans in a single batch. */ async batchCreateSpans(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_SPAN_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support batch creating spans" }); } /** * Updates multiple Spans in a single batch. */ async batchUpdateSpans(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_UPDATE_SPANS_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_DELETE_TRACES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support batch deleting traces" }); } /** * Creates multiple log records in a single batch. */ async batchCreateLogs(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_LOGS_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_LIST_LOGS_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support listing logs" }); } /** * Creates multiple metric observations in a single batch. */ async batchCreateMetrics(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_METRICS_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support batch creating metrics" }); } async listMetrics(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_LIST_METRICS_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support listing metrics" }); } async getMetricAggregate(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_AGGREGATE_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support metric aggregation" }); } async getMetricBreakdown(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_BREAKDOWN_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support metric breakdown" }); } async getMetricTimeSeries(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_TIME_SERIES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support metric time series" }); } async getMetricPercentiles(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_PERCENTILES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support metric percentiles" }); } async getMetricNames(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_NAMES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support metric name discovery" }); } async getMetricLabelKeys(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_METRIC_LABEL_KEYS_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support metric label key discovery" }); } async getMetricLabelValues(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_LABEL_VALUES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support label value discovery" }); } async getEntityTypes(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_ENTITY_TYPES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support entity type discovery" }); } async getEntityNames(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_ENTITY_NAMES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support entity name discovery" }); } async getServiceNames(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_SERVICE_NAMES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support service name discovery" }); } async getEnvironments(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_ENVIRONMENTS_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support environment discovery" }); } async getTags(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_TAGS_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support tag discovery" }); } /** * Creates a single score record. */ async createScore(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_CREATE_SCORE_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support creating scores" }); } /** * Creates multiple score observations in a single batch. */ async batchCreateScores(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_SCORES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_LIST_SCORES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support listing scores" }); } /** * Retrieves a single score by its score ID. */ async getScoreById(_scoreId) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_BY_ID_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support getting scores by ID" }); } async getScoreAggregate(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_AGGREGATE_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support score aggregation" }); } async getScoreBreakdown(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_BREAKDOWN_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support score breakdown" }); } async getScoreTimeSeries(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_TIME_SERIES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support score time series" }); } async getScorePercentiles(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_SCORE_PERCENTILES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support score percentiles" }); } /** * Creates a single feedback record. */ async createFeedback(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_CREATE_FEEDBACK_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support creating feedback" }); } /** * Creates multiple feedback observations in a single batch. */ async batchCreateFeedback(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_BATCH_CREATE_FEEDBACK_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_STORAGE_LIST_FEEDBACK_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support listing feedback" }); } async getFeedbackAggregate(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_FEEDBACK_AGGREGATE_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support feedback aggregation" }); } async getFeedbackBreakdown(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_FEEDBACK_BREAKDOWN_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support feedback breakdown" }); } async getFeedbackTimeSeries(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_FEEDBACK_TIME_SERIES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support feedback time series" }); } async getFeedbackPercentiles(_args) { throw new MastraError({ id: "OBSERVABILITY_STORAGE_GET_FEEDBACK_PERCENTILES_NOT_IMPLEMENTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "This storage provider does not support feedback percentiles" }); } }; //#endregion //#region src/storage/utils.ts function hasErrorCode(error, codes) { const seen = /* @__PURE__ */ new Set(); let current = error; while (current && typeof current === "object" && !seen.has(current)) { seen.add(current); if ("code" in current && codes.has(current.code)) return true; current = "cause" in current ? current.cause : void 0; } return false; } const DURATION_UNIT_MS = { ms: 1, s: 1e3, m: 60 * 1e3, h: 3600 * 1e3, d: 1440 * 60 * 1e3, w: 10080 * 60 * 1e3 }; /** * Parses a retention {@link Duration} into milliseconds. * * Accepts a raw number of milliseconds or a `<number><unit>` string where unit * is one of `ms`, `s`, `m`, `h`, `d`, `w`. * * @throws Error if the input is not a valid duration. */ function parseDuration(duration) { if (typeof duration === "number") { if (!Number.isFinite(duration) || duration < 0) throw new Error(`Invalid retention duration: ${duration}. Must be a non-negative finite number of milliseconds.`); return duration; } const match = /^(\d+(?:\.\d+)?)(ms|s|m|h|d|w)$/.exec(duration); if (!match) throw new Error(`Invalid retention duration: "${duration}". Expected a number of milliseconds or a "<number><unit>" string (ms, s, m, h, d, w).`); const value = Number(match[1]); const unit = match[2]; return value * DURATION_UNIT_MS[unit]; } 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 {}; } const SAFE_METADATA_KEY_PATTERN$1 = /^[a-zA-Z_][a-zA-Z0-9_]*$/; const MAX_METADATA_KEY_LENGTH$1 = 128; const DISALLOWED_METADATA_KEYS$1 = /* @__PURE__ */ new Set([ "__proto__", "prototype", "constructor" ]); function validateStorageMetadataFilter(metadata) { if (metadata === void 0) return void 0; if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) throw new TypeError("Metadata filter must be an object."); const entries = Object.entries(metadata); for (const [key, value] of entries) { if (key.length > MAX_METADATA_KEY_LENGTH$1 || !SAFE_METADATA_KEY_PATTERN$1.test(key) || DISALLOWED_METADATA_KEYS$1.has(key)) throw new TypeError(`Invalid metadata filter key "${key}".`); if (value !== null && typeof value !== "string" && typeof value !== "boolean" && !(typeof value === "number" && Number.isFinite(value))) throw new TypeError(`Invalid metadata filter value for key "${key}". Values must be string, finite number, boolean, or null.`); } return entries.length > 0 ? metadata : void 0; } function storageMessageMatchesMetadataFilter(content, filter) { if (!filter) return true; const parsedContent = typeof content === "string" ? safelyParseJSON(content) : content; if (!parsedContent || typeof parsedContent !== "object" || Array.isArray(parsedContent)) return false; const metadata = parsedContent.metadata; if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return false; const metadataRecord = metadata; return Object.entries(filter).every(([key, expected]) => Object.prototype.hasOwnProperty.call(metadataRecord, key) && metadataRecord[key] === expected); } /** * Generic schema-driven row transformer. * Uses TABLE_SCHEMAS to determine field types and apply appropriate transformations: * - 'jsonb' fields: parsed from JSON strings using safelyParseJSON * - 'timestamp' fields: optionally converted to Date objects * * @param row - The raw row from storage * @param tableName - The table name to look up schema from TABLE_SCHEMAS * @param options - Optional configuration for store-specific behavior * @returns Transformed row with proper types */ function transformRow(row, tableName, options = {}) { const { preferredTimestampFields = {}, convertTimestamps = false, nullValuePattern, fieldMappings = {} } = options; const tableSchema = TABLE_SCHEMAS[tableName]; const result = {}; for (const [key, columnSchema] of Object.entries(tableSchema)) { let value = row[fieldMappings[key] ?? key]; 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; } /** * Transform a raw score row from storage to ScoreRowData. * Convenience wrapper around transformRow for the scores table (TABLE_SCORERS). * * @param row - The raw row from storage * @param options - Optional configuration for store-specific behavior * @returns Transformed ScoreRowData */ function transformScoreRow(row, options = {}) { return transformRow(row, TABLE_SCORERS, options); } /** * Converts a string to UPPER_SNAKE_CASE, preserving word boundaries from camelCase, PascalCase, kebab-case, etc. */ 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, ""); } /** * Generates a standardized error ID for storage and vector operations. * * Formats: * - Storage: MASTRA_STORAGE_{STORE}_{OPERATION}_{STATUS} * - Vector: MASTRA_VECTOR_{STORE}_{OPERATION}_{STATUS} * * This function auto-normalizes inputs to UPPER_SNAKE_CASE for flexibility. * The store parameter is type-checked against canonical store names for IDE autocomplete. * * @param type - The operation type ('storage' or 'vector') * @param store - The store adapter name (type-checked canonical names) * @param operation - The operation that failed (e.g., 'LIST_THREADS_BY_RESOURCE_ID', 'QUERY') * @param status - The status/error type (e.g., 'FAILED', 'INVALID_THREAD_ID', 'DUPLICATE_KEY') * * @example * ```ts * // Storage operations * createStoreErrorId('storage', 'PG', 'LIST_THREADS', 'FAILED') * // Returns: 'MASTRA_STORAGE_PG_LIST_THREADS_FAILED' * * // Vector operations * createStoreErrorId('vector', 'CHROMA', 'QUERY', 'FAILED') * // Returns: 'MASTRA_VECTOR_CHROMA_QUERY_FAILED' * * // Auto-normalizes any casing * createStoreErrorId('storage', 'PG', 'listMessagesById', 'failed') * // Returns: 'MASTRA_STORAGE_PG_LIST_MESSAGES_BY_ID_FAILED' * ``` */ function createStoreErrorId(type, store, operation, status) { const normalizedStore = toUpperSnakeCase(store); const normalizedOperation = toUpperSnakeCase(operation); const normalizedStatus = toUpperSnakeCase(status); return `MASTRA_${type === "storage" ? "STORAGE" : "VECTOR"}_${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; return ensureDate(date)?.toISOString(); } /** * Filter an array of items by date range. Used by in-memory storage adapters. * * This provides a consistent implementation of date range filtering with * support for inclusive/exclusive bounds across all storage adapters. * * @param items - Array of items to filter * @param getCreatedAt - Function to extract the createdAt date from an item * @param dateRange - Optional date range filter configuration * @returns Filtered array of items * * @example * ```ts * const filtered = filterByDateRange( * messages, * (msg) => new Date(msg.createdAt), * { start: new Date('2024-01-01'), startExclusive: true } * ); * ``` */ 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; } /** * Deep equality check for JSON values. * Compares primitives, arrays, objects, and Date instances recursively. * * @param a - First value to compare * @param b - Second value to compare * @returns true if values are deeply equal, false otherwise */ 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; } //#endregion //#region src/storage/domains/observability/inmemory.ts const OBSERVABILITY_DELTA_POLLING_FEATURE = "observability-delta-polling"; /** In-memory implementation of ObservabilityStorage for testing and development. */ var ObservabilityInMemory = class extends ObservabilityStorage { db; constructor({ db }) { super(); this.db = db; } getFeatures() { if (!this.deltaPollingFeatureEnabled()) return; 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 coreFeatures.has(OBSERVABILITY_DELTA_POLLING_FEATURE); } assertDeltaPollingEnabled() { if (this.deltaPollingFeatureEnabled()) return; throw new MastraError({ id: "OBSERVABILITY_DELTA_POLLING_NOT_SUPPORTED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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 MastraError({ id: "OBSERVABILITY_MISSING_RECORD_ID", domain: ErrorDomain.STORAGE, category: 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 MastraError({ id: "OBSERVABILITY_INVALID_DELTA_CURSOR", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.USER, text: "Invalid observability delta cursor" }); const cursorId = Number.parseInt(cursor, 10); if (!Number.isInteger(cursorId) || cursorId < 0) throw new MastraError({ id: "OBSERVABILITY_INVALID_DELTA_CURSOR", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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}\u0000${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 (!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 span = this.db.traces.get(traceId)?.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 MastraError({ id: "OBSERVABILITY_SPAN_ID_REQUIRED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: ErrorCategory.SYSTEM, text: "Span ID is required for creating a span" }); if (!record.traceId) throw new MastraError({ id: "OBSERVABILITY_TRACE_ID_REQUIRED", domain: ErrorDomain.MASTRA_OBSERVABILITY, category: 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", 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"; else if (rootSpan.endedAt == null) traceEntry.status = "running"; else traceEntry.status = "success"; else traceEntry.status = "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 } = 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; return { paged: matchingRootSpans.slice(start, end), total, page, perPage, hasMore: end < total }; } async listTraces(args) { const { mode, filters, after, limit } = listTracesArgsSchema.parse(args); if (mode === "delta") { this.assertDeltaPollingEnabled(); const fallbackCursorId = this.getMaxTraceCursorId(filters) ?? 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: toTraceSpans(deltaResponse.rows), delta: deltaResponse.delta, deltaCursor: deltaResponse.deltaCursor }; } const { paged, total, page, perPage, hasMore } = this.getMatchingRootSpans(args); return { spans: toTraceSpans(paged), pagination: { total, page, perPage, hasMore }, ...this.pageDeltaCursor(this.getMaxTraceCursorId(filters) ?? this.getMaxTraceStreamCursorId()) }; } async listTracesLig