UNPKG

@mastra/core

Version:
955 lines (954 loc) 33.9 kB
const require_deep_equal = require("./deep-equal-BvQBG8wE.cjs"); const require_filesystem_versioned = require("./filesystem-versioned-8Ag_Np8l.cjs"); //#region src/storage/domains/agents/base.ts var AgentsStorage = class extends require_filesystem_versioned.VersionedStorageDomain { listKey = "agents"; versionMetadataFields = [ "id", "agentId", "versionNumber", "changedFields", "changeMessage", "createdAt" ]; constructor() { super({ component: "STORAGE", name: "AGENTS" }); } }; //#endregion //#region src/storage/domains/agents/inmemory.ts var InMemoryAgentsStorage = class extends AgentsStorage { db; constructor({ db }) { super(); this.db = db; } async dangerouslyClearAll() { this.db.agents.clear(); this.db.agentVersions.clear(); } async getById(id) { const agent = this.db.agents.get(id); return agent ? this.deepCopyAgent(agent) : null; } async create(input) { const { agent } = input; if (this.db.agents.has(agent.id)) throw new Error(`Agent with id ${agent.id} already exists`); const now = /* @__PURE__ */ new Date(); const visibility = agent.visibility ?? (agent.authorId ? "private" : void 0); const newAgent = { id: agent.id, status: "draft", activeVersionId: void 0, authorId: agent.authorId, visibility, metadata: agent.metadata, favoriteCount: 0, createdAt: now, updatedAt: now }; this.db.agents.set(agent.id, newAgent); const { id: _id, authorId: _authorId, visibility: _visibility, metadata: _metadata, ...snapshotConfig } = agent; const versionId = crypto.randomUUID(); await this.createVersion({ id: versionId, agentId: agent.id, versionNumber: 1, ...snapshotConfig, changedFields: Object.keys(snapshotConfig), changeMessage: "Initial version" }); return this.deepCopyAgent(newAgent); } async update(input) { const { id, ...updates } = input; const existingAgent = this.db.agents.get(id); if (!existingAgent) throw new Error(`Agent with id ${id} not found`); const { authorId, visibility, activeVersionId, metadata, status } = updates; const updatedAgent = { ...existingAgent, ...authorId !== void 0 && { authorId }, ...visibility !== void 0 && { visibility }, ...activeVersionId !== void 0 && { activeVersionId }, ...metadata !== void 0 && { metadata: { ...existingAgent.metadata, ...metadata } }, ...status !== void 0 && { status }, updatedAt: /* @__PURE__ */ new Date() }; this.db.agents.set(id, updatedAgent); return this.deepCopyAgent(updatedAgent); } async delete(id) { this.db.agents.delete(id); await this.deleteVersionsByParentId(id); } async list(args) { const { page = 0, perPage: perPageInput, orderBy, authorId, visibility, metadata, status, entityIds, pinFavoritedFor, favoritedOnly } = args || {}; const { field, direction } = this.parseOrderBy(orderBy); const perPage = require_filesystem_versioned.normalizePerPage(perPageInput, 100); 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"); let agents = Array.from(this.db.agents.values()); if (entityIds !== void 0) { if (entityIds.length === 0) return { agents: [], total: 0, page, perPage: perPageInput === false ? false : perPage, hasMore: false }; const idSet = new Set(entityIds); agents = agents.filter((agent) => idSet.has(agent.id)); } if (status) agents = agents.filter((agent) => agent.status === status); if (authorId !== void 0) agents = agents.filter((agent) => agent.authorId === authorId); if (visibility !== void 0) agents = agents.filter((agent) => agent.visibility === visibility); if (metadata && Object.keys(metadata).length > 0) agents = agents.filter((agent) => { if (!agent.metadata) return false; return Object.entries(metadata).every(([key, value]) => require_deep_equal.deepEqual(agent.metadata[key], value)); }); const favoritedIds = pinFavoritedFor ? this.collectFavoritedIdsFor(pinFavoritedFor) : void 0; if (favoritedOnly) if (favoritedIds) agents = agents.filter((agent) => favoritedIds.has(agent.id)); else agents = []; const clonedAgents = this.sortAgents(agents, field, direction, favoritedIds).map((agent) => this.deepCopyAgent(agent)); const { offset, perPage: perPageForResponse } = require_filesystem_versioned.calculatePagination(page, perPageInput, perPage); return { agents: clonedAgents.slice(offset, offset + perPage), total: clonedAgents.length, page, perPage: perPageForResponse, hasMore: offset + perPage < clonedAgents.length }; } async createVersion(input) { if (this.db.agentVersions.has(input.id)) throw new Error(`Version with id ${input.id} already exists`); for (const version of this.db.agentVersions.values()) if (version.agentId === input.agentId && version.versionNumber === input.versionNumber) throw new Error(`Version number ${input.versionNumber} already exists for agent ${input.agentId}`); const version = { ...input, createdAt: /* @__PURE__ */ new Date() }; this.db.agentVersions.set(input.id, this.deepCopyVersion(version)); return this.deepCopyVersion(version); } async getVersion(id) { const version = this.db.agentVersions.get(id); return version ? this.deepCopyVersion(version) : null; } async getVersionByNumber(agentId, versionNumber) { for (const version of this.db.agentVersions.values()) if (version.agentId === agentId && version.versionNumber === versionNumber) return this.deepCopyVersion(version); return null; } async getLatestVersion(agentId) { let latest = null; for (const version of this.db.agentVersions.values()) if (version.agentId === agentId) { if (!latest || version.versionNumber > latest.versionNumber) latest = version; } return latest ? this.deepCopyVersion(latest) : null; } async listVersions(input) { const { agentId, page = 0, perPage: perPageInput, orderBy } = input; const { field, direction } = this.parseVersionOrderBy(orderBy); const perPage = require_filesystem_versioned.normalizePerPage(perPageInput, 20); 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"); let versions = Array.from(this.db.agentVersions.values()).filter((v) => v.agentId === agentId); versions = this.sortVersions(versions, field, direction); const clonedVersions = versions.map((v) => this.deepCopyVersion(v)); const total = clonedVersions.length; const { offset, perPage: perPageForResponse } = require_filesystem_versioned.calculatePagination(page, perPageInput, perPage); return { versions: clonedVersions.slice(offset, offset + perPage), total, page, perPage: perPageForResponse, hasMore: offset + perPage < total }; } async deleteVersion(id) { this.db.agentVersions.delete(id); } async deleteVersionsByParentId(entityId) { const idsToDelete = []; for (const [id, version] of this.db.agentVersions.entries()) if (version.agentId === entityId) idsToDelete.push(id); for (const id of idsToDelete) this.db.agentVersions.delete(id); } async countVersions(agentId) { let count = 0; for (const version of this.db.agentVersions.values()) if (version.agentId === agentId) count++; return count; } /** * Deep copy a thin agent record to prevent external mutation of stored data */ deepCopyAgent(agent) { return { ...agent, metadata: agent.metadata ? { ...agent.metadata } : agent.metadata }; } /** * Deep copy a version to prevent external mutation of stored data */ deepCopyVersion(version) { return structuredClone(version); } sortAgents(agents, field, direction, favoritedIds) { return agents.sort((a, b) => { if (favoritedIds) { const aFav = favoritedIds.has(a.id) ? 1 : 0; const bFav = favoritedIds.has(b.id) ? 1 : 0; if (aFav !== bFav) return bFav - aFav; } const aValue = new Date(a[field]).getTime(); const bValue = new Date(b[field]).getTime(); if (aValue !== bValue) return direction === "ASC" ? aValue - bValue : bValue - aValue; return a.id < b.id ? -1 : a.id > b.id ? 1 : 0; }); } /** * Collect the set of agent IDs favorited by the given user. Returns an empty * Set when the favorites domain is not wired or the user has no favorites. */ collectFavoritedIdsFor(userId) { const favorited = /* @__PURE__ */ new Set(); for (const row of this.db.favorites.values()) if (row.userId === userId && row.entityType === "agent") favorited.add(row.entityId); return favorited; } sortVersions(versions, field, direction) { return versions.sort((a, b) => { let aVal; let bVal; if (field === "createdAt") { aVal = a.createdAt.getTime(); bVal = b.createdAt.getTime(); } else { aVal = a.versionNumber; bVal = b.versionNumber; } return direction === "ASC" ? aVal - bVal : bVal - aVal; }); } }; //#endregion //#region src/storage/domains/inmemory-db.ts /** * InMemoryDB is a thin database layer for in-memory storage. * It holds all the Maps that store data, similar to how a real database * connection (pg-promise client, libsql client) is shared across domains. * * Each domain receives a reference to this db and operates on the relevant Maps. */ var InMemoryDB = class { threads = /* @__PURE__ */ new Map(); messages = /* @__PURE__ */ new Map(); resources = /* @__PURE__ */ new Map(); workflows = /* @__PURE__ */ new Map(); workflowDefinitions = /* @__PURE__ */ new Map(); scores = /* @__PURE__ */ new Map(); traces = /* @__PURE__ */ new Map(); metricRecords = []; logRecords = []; scoreRecords = []; feedbackRecords = []; observabilityNextCursorId = 1; traceCursorIds = /* @__PURE__ */ new Map(); branchCursorIds = /* @__PURE__ */ new Map(); metricCursorIds = /* @__PURE__ */ new Map(); logCursorIds = /* @__PURE__ */ new Map(); scoreCursorIds = /* @__PURE__ */ new Map(); feedbackCursorIds = /* @__PURE__ */ new Map(); agents = /* @__PURE__ */ new Map(); agentVersions = /* @__PURE__ */ new Map(); promptBlocks = /* @__PURE__ */ new Map(); promptBlockVersions = /* @__PURE__ */ new Map(); scorerDefinitions = /* @__PURE__ */ new Map(); scorerDefinitionVersions = /* @__PURE__ */ new Map(); mcpClients = /* @__PURE__ */ new Map(); mcpClientVersions = /* @__PURE__ */ new Map(); mcpServers = /* @__PURE__ */ new Map(); mcpServerVersions = /* @__PURE__ */ new Map(); workspaces = /* @__PURE__ */ new Map(); workspaceVersions = /* @__PURE__ */ new Map(); skills = /* @__PURE__ */ new Map(); skillVersions = /* @__PURE__ */ new Map(); /** * Favorites keyed by `${userId}\u0000${entityType}\u0000${entityId}`. The * favorites domain owns reads/writes; this Map lives on InMemoryDB so the * favorites domain can also mutate `agents` / `skills` `favoriteCount` atomically * within the same synchronous block. */ favorites = /* @__PURE__ */ new Map(); /** Observational memory records, keyed by resourceId, each holding array of records (generations) */ observationalMemory = /* @__PURE__ */ new Map(); datasets = /* @__PURE__ */ new Map(); datasetItems = /* @__PURE__ */ new Map(); datasetVersions = /* @__PURE__ */ new Map(); experiments = /* @__PURE__ */ new Map(); experimentResults = /* @__PURE__ */ new Map(); backgroundTasks = /* @__PURE__ */ new Map(); schedules = /* @__PURE__ */ new Map(); scheduleTriggers = []; /** * Tool provider connections keyed by `${authorId}\u0000${providerId}\u0000${connectionId}`. */ toolProviderConnections = /* @__PURE__ */ new Map(); /** * Clears all data from all collections. * Useful for testing. */ clear() { this.threads.clear(); this.messages.clear(); this.resources.clear(); this.workflows.clear(); this.workflowDefinitions.clear(); this.scores.clear(); this.traces.clear(); this.metricRecords.length = 0; this.logRecords.length = 0; this.scoreRecords.length = 0; this.feedbackRecords.length = 0; this.observabilityNextCursorId = 1; this.traceCursorIds.clear(); this.branchCursorIds.clear(); this.metricCursorIds.clear(); this.logCursorIds.clear(); this.scoreCursorIds.clear(); this.feedbackCursorIds.clear(); this.agents.clear(); this.agentVersions.clear(); this.promptBlocks.clear(); this.promptBlockVersions.clear(); this.scorerDefinitions.clear(); this.scorerDefinitionVersions.clear(); this.mcpClients.clear(); this.mcpClientVersions.clear(); this.mcpServers.clear(); this.mcpServerVersions.clear(); this.workspaces.clear(); this.workspaceVersions.clear(); this.skills.clear(); this.skillVersions.clear(); this.favorites.clear(); this.observationalMemory.clear(); this.datasets.clear(); this.datasetItems.clear(); this.datasetVersions.clear(); this.experiments.clear(); this.experimentResults.clear(); this.backgroundTasks.clear(); this.schedules.clear(); this.scheduleTriggers.length = 0; this.toolProviderConnections.clear(); } }; //#endregion //#region src/storage/domains/agents/filesystem.ts /** * Fields persisted for filesystem-stored agents. * Only fields that `applyStoredOverrides` actually uses plus the * minimum required by the storage schema (`name`, `model`). */ const PERSISTED_SNAPSHOT_FIELDS = /* @__PURE__ */ new Set([ "name", "instructions", "model", "tools", "integrationTools", "toolProviders", "mcpClients", "requestContextSchema" ]); /** * Fields always excluded from per-entity (code-mode) JSON files regardless * of editor config. `model`/`name` are not editable from Studio for * code-defined agents, so they should not appear in the committed override * JSON — they would otherwise look like settable fields in code review and * could drift from the source-of-truth declaration in code. */ const CODE_MODE_EXCLUDED_FIELDS = /* @__PURE__ */ new Set(["model", "name"]); /** * Fields that depend on per-agent editor ownership. * When the agent's editor config does not own a given field (e.g. * descriptions-only mode does not own raw instructions), it should be * omitted from the on-disk per-entity JSON entirely. */ const OWNED_FIELDS_BY_GROUP$1 = { instructions: ["instructions"], tools: [ "tools", "integrationTools", "mcpClients" ] }; function ownershipFromEditorConfig$1(editorConfig) { if (editorConfig === false) return { ownsInstructions: false, ownsTools: false }; if (editorConfig === void 0 || editorConfig === null) return { ownsInstructions: true, ownsTools: true }; if (typeof editorConfig !== "object") return { ownsInstructions: false, ownsTools: false }; const cfg = editorConfig; const ownsInstructions = cfg.instructions === true; const toolsCfg = cfg.tools; return { ownsInstructions, ownsTools: toolsCfg === true || typeof toolsCfg === "object" && toolsCfg !== null && toolsCfg.description === true }; } function stripUnusedFields(obj) { const result = {}; for (const [key, value] of Object.entries(obj)) if (PERSISTED_SNAPSHOT_FIELDS.has(key)) result[key] = value; return result; } function isAgentNotFoundError(error, entityId) { if (!error || typeof error !== "object") return false; const maybeError = error; return maybeError.id === "MASTRA_GET_AGENT_BY_AGENT_ID_NOT_FOUND" || maybeError.details?.status === 404 && maybeError.details?.agentId === entityId || maybeError.message === `Agent with id ${entityId} not found`; } var FilesystemAgentsStorage = class extends AgentsStorage { helpers; storageMastra; constructor({ db }) { super(); const getCodeAgent = (entityId) => { try { const agent = this.storageMastra?.getAgentById?.(entityId); return agent?.source === "code" ? agent : void 0; } catch (error) { if (isAgentNotFoundError(error, entityId)) return; throw error; } }; const isCodeAgent = (entityId) => Boolean(getCodeAgent(entityId)); const editorConfigFor = (entityId) => getCodeAgent(entityId)?.__getEditorConfig?.(); this.helpers = new require_filesystem_versioned.FilesystemVersionedHelpers({ db, entitiesFile: "agents.json", parentIdField: "agentId", name: "FilesystemAgentsStorage", versionMetadataFields: [ "id", "agentId", "versionNumber", "changedFields", "changeMessage", "createdAt" ], perEntityFilesDir: "agents", shouldPersistToPerEntityFile: (entity) => isCodeAgent(entity.id), perEntitySnapshotFilter: (snapshot, entity) => { const { ownsInstructions, ownsTools } = ownershipFromEditorConfig$1(editorConfigFor(entity.id)); const excludedByOwnership = /* @__PURE__ */ new Set(); if (!ownsInstructions) for (const field of OWNED_FIELDS_BY_GROUP$1.instructions) excludedByOwnership.add(field); if (!ownsTools) for (const field of OWNED_FIELDS_BY_GROUP$1.tools) excludedByOwnership.add(field); const result = {}; for (const [key, value] of Object.entries(snapshot)) { if (CODE_MODE_EXCLUDED_FIELDS.has(key)) continue; if (excludedByOwnership.has(key)) continue; result[key] = value; } return result; } }); } __registerMastra(mastra) { this.storageMastra = mastra; } async init() { await this.helpers.db.init(); } async dangerouslyClearAll() { await this.helpers.dangerouslyClearAll(); } async getById(id) { return this.helpers.getById(id); } async create(input) { const { agent } = input; const now = /* @__PURE__ */ new Date(); const visibility = agent.visibility ?? (agent.authorId ? "private" : void 0); const entity = { id: agent.id, status: "draft", activeVersionId: void 0, authorId: agent.authorId, visibility, metadata: agent.metadata, createdAt: now, updatedAt: now }; await this.helpers.createEntity(agent.id, entity); const { id: _id, authorId: _authorId, visibility: _visibility, metadata: _metadata, ...snapshotConfig } = agent; const filtered = stripUnusedFields(snapshotConfig); const versionId = crypto.randomUUID(); await this.createVersion({ id: versionId, agentId: agent.id, versionNumber: 1, ...filtered, changedFields: Object.keys(filtered), changeMessage: "Initial version" }); return structuredClone(entity); } async update(input) { const { id, ...updates } = input; const entityUpdates = {}; const entityFields = /* @__PURE__ */ new Set([ "authorId", "visibility", "metadata", "activeVersionId", "status" ]); for (const [key, value] of Object.entries(updates)) if (entityFields.has(key)) entityUpdates[key] = value; return this.helpers.updateEntity(id, entityUpdates); } async delete(id) { await this.helpers.deleteEntity(id); } async list(args) { const { page, perPage, orderBy, authorId, visibility, metadata, status } = args || {}; return await this.helpers.listEntities({ page, perPage, orderBy, listKey: "agents", filters: { authorId, visibility, metadata, status } }); } async createVersion(input) { const { id, agentId, versionNumber, changedFields, changeMessage, ...snapshotFields } = input; const filtered = stripUnusedFields(snapshotFields); return this.helpers.createVersion({ id, agentId, versionNumber, changedFields, changeMessage, ...filtered }); } async getVersion(id) { return this.helpers.getVersion(id); } async getVersionByNumber(agentId, versionNumber) { return this.helpers.getVersionByNumber(agentId, versionNumber); } async getLatestVersion(agentId) { return this.helpers.getLatestVersion(agentId); } async listVersions(input) { return await this.helpers.listVersions(input, "agentId"); } async deleteVersion(id) { await this.helpers.deleteVersion(id); } async deleteVersionsByParentId(entityId) { await this.helpers.deleteVersionsByParentId(entityId); } async countVersions(agentId) { return this.helpers.countVersions(agentId); } }; //#endregion //#region src/storage/domains/agents/source.ts const SOURCE_VERSION_PREFIX = "source:"; const COMMON_EXCLUDED_FIELDS = /* @__PURE__ */ new Set([ "id", "model", "scorers", "skills", "workflows", "agents", "integrationTools", "toolProviders", "inputProcessors", "outputProcessors", "memory", "mcpClients", "workspace", "browser", "defaultOptions" ]); const CODE_SOURCE_EXCLUDED_FIELDS = /* @__PURE__ */ new Set(["name"]); const OWNED_FIELDS_BY_GROUP = { instructions: ["instructions"], tools: ["tools"] }; function ownershipFromEditorConfig(editorConfig) { if (editorConfig === false) return { ownsInstructions: false, ownsTools: false }; if (editorConfig === void 0 || editorConfig === null) return { ownsInstructions: true, ownsTools: true }; if (typeof editorConfig !== "object") return { ownsInstructions: false, ownsTools: false }; const cfg = editorConfig; const ownsInstructions = cfg.instructions === true; const toolsCfg = cfg.tools; return { ownsInstructions, ownsTools: toolsCfg === true || typeof toolsCfg === "object" && toolsCfg !== null && toolsCfg.description === true }; } function snapshotFromVersion(version) { const { id, agentId, versionNumber, changedFields, changeMessage, createdAt, ...snapshot } = version; return snapshot; } function filterSourceSnapshot(snapshot, editorConfig, isCodeDefinedAgent) { const excludedByOwnership = /* @__PURE__ */ new Set(); if (isCodeDefinedAgent) { const { ownsInstructions, ownsTools } = ownershipFromEditorConfig(editorConfig); if (!ownsInstructions) for (const field of OWNED_FIELDS_BY_GROUP.instructions) excludedByOwnership.add(field); if (!ownsTools) for (const field of OWNED_FIELDS_BY_GROUP.tools) excludedByOwnership.add(field); } const result = {}; for (const [key, value] of Object.entries(snapshot)) { if (COMMON_EXCLUDED_FIELDS.has(key)) continue; if (isCodeDefinedAgent && CODE_SOURCE_EXCLUDED_FIELDS.has(key)) continue; if (excludedByOwnership.has(key)) continue; if (value === void 0) continue; result[key] = value; } return result; } function parseJsonObject(content) { try { const parsed = JSON.parse(content); return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null; } catch { return null; } } function stableStringify(value) { if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`; if (value && typeof value === "object") return `{${Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`).join(",")}}`; return JSON.stringify(value); } function agentIdFromSourcePath(path) { const prefix = `${require_filesystem_versioned.SOURCE_CONTROL_AGENTS_DIR}/`; if (!path.startsWith(prefix) || !path.endsWith(".json")) return void 0; const filename = path.slice(prefix.length, -5); if (!filename || filename.includes("/")) return void 0; try { return decodeURIComponent(filename); } catch { return filename; } } var SourceAgentsSourceControl = class extends AgentsStorage { provider; knownAgentIds; db = new InMemoryDB(); memory = new InMemoryAgentsStorage({ db: this.db }); storageMastra; providerVersions = /* @__PURE__ */ new Map(); loadedHistory = /* @__PURE__ */ new Set(); hydratedAgents = /* @__PURE__ */ new Set(); activeRefs = /* @__PURE__ */ new Map(); providerAgentIdsDiscovered = false; constructor({ provider, agentIds = [] }) { super(); this.provider = provider; this.knownAgentIds = new Set(agentIds); } __registerMastra(mastra) { this.storageMastra = mastra; } async init() { const capabilities = await this.provider.getCapabilities(); if (!capabilities.canRead) throw new Error(capabilities.reason ?? `Source provider ${this.provider.displayName} cannot read files`); this.refreshKnownAgentIds(); await this.discoverProviderAgentIds(); await Promise.all([...this.knownAgentIds].map((agentId) => this.hydrateAgent(agentId))); } async dangerouslyClearAll() { this.hydratedAgents.clear(); this.loadedHistory.clear(); this.providerVersions.clear(); this.activeRefs.clear(); this.providerAgentIdsDiscovered = false; await this.memory.dangerouslyClearAll(); } async useProviderRef(agentId, ref) { this.activeRefs.set(agentId, ref); this.hydratedAgents.delete(agentId); this.loadedHistory.delete(agentId); for (const [versionId, version] of this.providerVersions.entries()) if (version.agentId === agentId) this.providerVersions.delete(versionId); await this.memory.delete(agentId); await this.hydrateAgent(agentId); } async getById(id) { await this.hydrateAgent(id); return this.memory.getById(id); } async create(input) { await this.hydrateAgent(input.agent.id); if (await this.memory.getById(input.agent.id)) throw new Error(`Agent with id ${input.agent.id} already exists`); await this.persistSnapshot(input.agent.id, { ...input.agent }, "Initial version"); const created = await this.memory.create(input); this.knownAgentIds.add(input.agent.id); return created; } async update(input) { await this.hydrateAgent(input.id); return this.memory.update(input); } async delete(id) { this.knownAgentIds.delete(id); this.hydratedAgents.delete(id); this.loadedHistory.delete(id); for (const versionId of this.providerVersions.keys()) if (this.providerVersions.get(versionId)?.agentId === id) this.providerVersions.delete(versionId); await this.memory.delete(id); } async list(args) { this.refreshKnownAgentIds(); await this.discoverProviderAgentIds(); await Promise.all([...this.knownAgentIds].map((agentId) => this.hydrateAgent(agentId))); return this.memory.list(args); } async createVersion(input) { await this.hydrateAgent(input.agentId); if (await this.memory.getVersion(input.id)) throw new Error(`Version with id ${input.id} already exists`); if (await this.memory.getVersionByNumber(input.agentId, input.versionNumber)) throw new Error(`Version number ${input.versionNumber} already exists for agent ${input.agentId}`); const snapshot = snapshotFromVersion({ ...input, createdAt: /* @__PURE__ */ new Date() }); const result = await this.persistSnapshot(input.agentId, snapshot, input.changeMessage); const version = await this.memory.createVersion(input); this.rememberProviderVersion(input.agentId, version, result); return version; } async getVersion(id) { const providerVersion = this.providerVersions.get(id); if (providerVersion) return structuredClone(providerVersion); return this.memory.getVersion(id); } async getVersionByNumber(agentId, versionNumber) { await this.loadHistory(agentId); const providerVersion = [...this.providerVersions.values()].find((version) => version.agentId === agentId && version.versionNumber === versionNumber); if (providerVersion) return structuredClone(providerVersion); return this.memory.getVersionByNumber(agentId, versionNumber); } async getLatestVersion(agentId) { await this.loadHistory(agentId); const providerLatest = [...this.providerVersions.values()].filter((version) => version.agentId === agentId).sort((a, b) => b.versionNumber - a.versionNumber)[0]; if (providerLatest) return structuredClone(providerLatest); return this.memory.getLatestVersion(agentId); } async listVersions(input) { await this.loadHistory(input.agentId); const providerVersions = [...this.providerVersions.values()].filter((version) => version.agentId === input.agentId); if (providerVersions.length === 0) return this.memory.listVersions(input); const { page = 0, perPage: perPageInput, orderBy } = input; const { field, direction } = this.parseVersionOrderBy(orderBy); const perPage = require_filesystem_versioned.normalizePerPage(perPageInput, 20); const sortedVersions = this.sortVersions(providerVersions, field, direction).map((version) => structuredClone(version)); const total = sortedVersions.length; const { offset, perPage: perPageForResponse } = require_filesystem_versioned.calculatePagination(page, perPageInput, perPage); return { versions: sortedVersions.slice(offset, offset + perPage), total, page, perPage: perPageForResponse, hasMore: offset + perPage < total }; } async deleteVersion(id) { this.providerVersions.delete(id); await this.memory.deleteVersion(id); } async deleteVersionsByParentId(entityId) { for (const [versionId, version] of this.providerVersions.entries()) if (version.agentId === entityId) this.providerVersions.delete(versionId); await this.memory.deleteVersionsByParentId(entityId); } async countVersions(entityId) { await this.loadHistory(entityId); return [...this.providerVersions.values()].filter((version) => version.agentId === entityId).length || this.memory.countVersions(entityId); } refreshKnownAgentIds() { const agents = this.storageMastra?.listAgents?.(); if (!agents) return; for (const agent of Object.values(agents)) if (agent.source === "code") this.knownAgentIds.add(agent.id); } async discoverProviderAgentIds() { if (this.providerAgentIdsDiscovered || !this.provider.listFiles) return; const files = await this.provider.listFiles({ path: require_filesystem_versioned.SOURCE_CONTROL_AGENTS_DIR }); for (const file of files) { const agentId = agentIdFromSourcePath(file.path); if (agentId) this.knownAgentIds.add(agentId); } this.providerAgentIdsDiscovered = true; } async hydrateAgent(agentId) { if (this.hydratedAgents.has(agentId)) return; const ref = this.activeRefs.get(agentId); const file = await this.provider.readFile({ path: require_filesystem_versioned.getSourceAgentFilePath(agentId), ref }); if (!file) { this.hydratedAgents.add(agentId); return; } const snapshot = parseJsonObject(file.content); if (!snapshot) { this.hydratedAgents.add(agentId); return; } this.knownAgentIds.add(agentId); this.hydratedAgents.add(agentId); const now = /* @__PURE__ */ new Date(); const versionId = `hydrated-${agentId}-v1`; this.db.agents.set(agentId, { id: agentId, status: "published", activeVersionId: versionId, favoriteCount: 0, createdAt: now, updatedAt: now }); this.db.agentVersions.set(versionId, { id: versionId, agentId, versionNumber: 1, ...snapshot, createdAt: now }); } getCodeDefinedAgent(agentId) { try { const agent = this.storageMastra?.getAgentById?.(agentId); return agent?.source === "code" ? agent : void 0; } catch { return; } } async persistSnapshot(agentId, snapshot, message) { const capabilities = await this.provider.getCapabilities(); if (!capabilities.canWrite) throw new Error(capabilities.reason ?? `Source provider ${this.provider.displayName} cannot write files`); const agent = this.getCodeDefinedAgent(agentId); const filtered = filterSourceSnapshot(snapshot, agent?.__getEditorConfig?.(), Boolean(agent)); return this.provider.writeFile({ path: require_filesystem_versioned.getSourceAgentFilePath(agentId), ref: this.activeRefs.get(agentId), content: `${stableStringify(filtered)}\n`, message }); } async loadHistory(agentId) { if (this.loadedHistory.has(agentId)) return; if (!(await this.provider.getCapabilities()).canListHistory) { this.loadedHistory.add(agentId); return; } const activeRef = this.activeRefs.get(agentId); const orderedEntries = [...await this.provider.listFileHistory({ path: require_filesystem_versioned.getSourceAgentFilePath(agentId), ref: activeRef })].reverse(); const versions = /* @__PURE__ */ new Map(); let versionNumber = 0; for (const entry of orderedEntries) { const file = await this.provider.readFile({ path: require_filesystem_versioned.getSourceAgentFilePath(agentId), ref: entry.ref ?? entry.id }); if (!file) continue; const snapshot = parseJsonObject(file.content); if (!snapshot) continue; versionNumber += 1; const version = this.versionFromHistoryEntry(agentId, entry, versionNumber, snapshot); versions.set(version.id, version); } for (const [versionId, version] of versions) this.providerVersions.set(versionId, version); this.loadedHistory.add(agentId); } rememberProviderVersion(agentId, version, result) { const versionId = result.commitSha ? `${SOURCE_VERSION_PREFIX}${result.commitSha}:${agentId}` : version.id; this.providerVersions.set(versionId, { ...structuredClone(version), id: versionId, agentId, versionNumber: this.nextProviderVersionNumber(agentId) }); } versionFromHistoryEntry(agentId, entry, versionNumber, snapshot) { return { id: `${SOURCE_VERSION_PREFIX}${entry.id}:${agentId}`, agentId, versionNumber, changeMessage: entry.message, ...snapshot, createdAt: new Date(entry.createdAt) }; } nextProviderVersionNumber(agentId) { return ([...this.providerVersions.values()].filter((version) => version.agentId === agentId).sort((a, b) => b.versionNumber - a.versionNumber)[0]?.versionNumber ?? 0) + 1; } sortVersions(versions, field, direction) { return versions.sort((a, b) => { const aVal = field === "createdAt" ? a.createdAt.getTime() : a.versionNumber; const bVal = field === "createdAt" ? b.createdAt.getTime() : b.versionNumber; return direction === "ASC" ? aVal - bVal : bVal - aVal; }); } }; //#endregion Object.defineProperty(exports, "AgentsStorage", { enumerable: true, get: function() { return AgentsStorage; } }); Object.defineProperty(exports, "FilesystemAgentsStorage", { enumerable: true, get: function() { return FilesystemAgentsStorage; } }); Object.defineProperty(exports, "InMemoryAgentsStorage", { enumerable: true, get: function() { return InMemoryAgentsStorage; } }); Object.defineProperty(exports, "InMemoryDB", { enumerable: true, get: function() { return InMemoryDB; } }); Object.defineProperty(exports, "SourceAgentsSourceControl", { enumerable: true, get: function() { return SourceAgentsSourceControl; } }); //# sourceMappingURL=source-BqLo7mT8.cjs.map