@mastra/core
Version:
1,022 lines (1,015 loc) • 35.9 kB
JavaScript
'use strict';
var chunk3SDZERO4_cjs = require('./chunk-3SDZERO4.cjs');
var chunkHHO5TO2N_cjs = require('./chunk-HHO5TO2N.cjs');
// src/storage/domains/agents/base.ts
var AgentsStorage = class extends chunk3SDZERO4_cjs.VersionedStorageDomain {
listKey = "agents";
versionMetadataFields = [
"id",
"agentId",
"versionNumber",
"changedFields",
"changeMessage",
"createdAt"
];
constructor() {
super({
component: "STORAGE",
name: "AGENTS"
});
}
};
// 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();
}
// ==========================================================================
// Agent CRUD Methods
// ==========================================================================
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 = chunk3SDZERO4_cjs.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]) => chunkHHO5TO2N_cjs.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 sortedAgents = this.sortAgents(agents, field, direction, favoritedIds);
const clonedAgents = sortedAgents.map((agent) => this.deepCopyAgent(agent));
const { offset, perPage: perPageForResponse } = chunk3SDZERO4_cjs.calculatePagination(page, perPageInput, perPage);
return {
agents: clonedAgents.slice(offset, offset + perPage),
total: clonedAgents.length,
page,
perPage: perPageForResponse,
hasMore: offset + perPage < clonedAgents.length
};
}
// ==========================================================================
// Agent Version Methods
// ==========================================================================
async createVersion(input) {
if (this.db.agentVersions.has(input.id)) {
throw new Error(`Version with id ${input.id} already exists`);
}
for (const version2 of this.db.agentVersions.values()) {
if (version2.agentId === input.agentId && version2.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 = chunk3SDZERO4_cjs.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 } = chunk3SDZERO4_cjs.calculatePagination(page, perPageInput, perPage);
const paginatedVersions = clonedVersions.slice(offset, offset + perPage);
return {
versions: paginatedVersions,
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;
}
// ==========================================================================
// Private Helper Methods
// ==========================================================================
/**
* 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;
});
}
};
// src/storage/domains/inmemory-db.ts
var InMemoryDB = class {
threads = /* @__PURE__ */ new Map();
messages = /* @__PURE__ */ new Map();
resources = /* @__PURE__ */ new Map();
workflows = /* @__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();
// Dataset domain maps
datasets = /* @__PURE__ */ new Map();
datasetItems = /* @__PURE__ */ new Map();
datasetVersions = /* @__PURE__ */ new Map();
// Experiment domain maps
experiments = /* @__PURE__ */ new Map();
experimentResults = /* @__PURE__ */ new Map();
// Background tasks domain
backgroundTasks = /* @__PURE__ */ new Map();
// Schedules domain
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.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();
}
};
// src/storage/domains/agents/filesystem.ts
var PERSISTED_SNAPSHOT_FIELDS = /* @__PURE__ */ new Set([
"name",
"instructions",
"model",
"tools",
"integrationTools",
"toolProviders",
"mcpClients",
"requestContextSchema"
]);
var CODE_MODE_EXCLUDED_FIELDS = /* @__PURE__ */ new Set(["model", "name"]);
var OWNED_FIELDS_BY_GROUP = {
instructions: ["instructions"],
tools: ["tools", "integrationTools", "mcpClients"]
};
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;
const ownsTools = toolsCfg === true || typeof toolsCfg === "object" && toolsCfg !== null && toolsCfg.description === true;
return { ownsInstructions, ownsTools };
}
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 void 0;
}
throw error;
}
};
const isCodeAgent = (entityId) => Boolean(getCodeAgent(entityId));
const editorConfigFor = (entityId) => getCodeAgent(entityId)?.__getEditorConfig?.();
this.helpers = new chunk3SDZERO4_cjs.FilesystemVersionedHelpers({
db,
entitiesFile: "agents.json",
parentIdField: "agentId",
name: "FilesystemAgentsStorage",
versionMetadataFields: ["id", "agentId", "versionNumber", "changedFields", "changeMessage", "createdAt"],
perEntityFilesDir: "agents",
// Per-entity layout is used only for code-mode agents — i.e. agents
// that are declared in code (`source === 'code'`). For db-mode and
// user-created stored agents we keep the shared `agents.json` layout.
shouldPersistToPerEntityFile: (entity) => isCodeAgent(entity.id),
perEntitySnapshotFilter: (snapshot, entity) => {
const { ownsInstructions, ownsTools } = ownershipFromEditorConfig(editorConfigFor(entity.id));
const excludedByOwnership = /* @__PURE__ */ new Set();
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 (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 || {};
const result = await this.helpers.listEntities({
page,
perPage,
orderBy,
listKey: "agents",
filters: { authorId, visibility, metadata, status }
});
return result;
}
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) {
const result = await this.helpers.listVersions(input, "agentId");
return result;
}
async deleteVersion(id) {
await this.helpers.deleteVersion(id);
}
async deleteVersionsByParentId(entityId) {
await this.helpers.deleteVersionsByParentId(entityId);
}
async countVersions(agentId) {
return this.helpers.countVersions(agentId);
}
};
// src/storage/domains/agents/source.ts
var SOURCE_VERSION_PREFIX = "source:";
var COMMON_EXCLUDED_FIELDS = /* @__PURE__ */ new Set([
"id",
"model",
"scorers",
"skills",
"workflows",
"agents",
"integrationTools",
"toolProviders",
"inputProcessors",
"outputProcessors",
"memory",
"mcpClients",
"workspace",
"browser",
"defaultOptions"
]);
var CODE_SOURCE_EXCLUDED_FIELDS = /* @__PURE__ */ new Set(["name"]);
var OWNED_FIELDS_BY_GROUP2 = {
instructions: ["instructions"],
tools: ["tools"]
};
function ownershipFromEditorConfig2(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;
const ownsTools = toolsCfg === true || typeof toolsCfg === "object" && toolsCfg !== null && toolsCfg.description === true;
return { ownsInstructions, ownsTools };
}
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 } = ownershipFromEditorConfig2(editorConfig);
if (!ownsInstructions) {
for (const field of OWNED_FIELDS_BY_GROUP2.instructions) excludedByOwnership.add(field);
}
if (!ownsTools) {
for (const field of OWNED_FIELDS_BY_GROUP2.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") {
const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([a], [b]) => a.localeCompare(b));
return `{${entries.map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`).join(",")}}`;
}
return JSON.stringify(value);
}
function agentIdFromSourcePath(path) {
const prefix = `${chunk3SDZERO4_cjs.SOURCE_CONTROL_AGENTS_DIR}/`;
if (!path.startsWith(prefix) || !path.endsWith(".json")) return void 0;
const filename = path.slice(prefix.length, -".json".length);
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);
const existing = await this.memory.getById(input.agent.id);
if (existing) {
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);
const existingVersion = await this.memory.getVersion(input.id);
if (existingVersion) {
throw new Error(`Version with id ${input.id} already exists`);
}
const existingVersionNumber = await this.memory.getVersionByNumber(input.agentId, input.versionNumber);
if (existingVersionNumber) {
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 = chunk3SDZERO4_cjs.normalizePerPage(perPageInput, 20);
const sortedVersions = this.sortVersions(providerVersions, field, direction).map(
(version) => structuredClone(version)
);
const total = sortedVersions.length;
const { offset, perPage: perPageForResponse } = chunk3SDZERO4_cjs.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);
const providerCount = [...this.providerVersions.values()].filter((version) => version.agentId === entityId).length;
return providerCount || 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: chunk3SDZERO4_cjs.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: chunk3SDZERO4_cjs.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 void 0;
}
}
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: chunk3SDZERO4_cjs.getSourceAgentFilePath(agentId),
ref: this.activeRefs.get(agentId),
content: `${stableStringify(filtered)}
`,
message
});
}
async loadHistory(agentId) {
if (this.loadedHistory.has(agentId)) return;
const capabilities = await this.provider.getCapabilities();
if (!capabilities.canListHistory) {
this.loadedHistory.add(agentId);
return;
}
const activeRef = this.activeRefs.get(agentId);
const entries = await this.provider.listFileHistory({ path: chunk3SDZERO4_cjs.getSourceAgentFilePath(agentId), ref: activeRef });
const orderedEntries = [...entries].reverse();
const versions = /* @__PURE__ */ new Map();
let versionNumber = 0;
for (const entry of orderedEntries) {
const file = await this.provider.readFile({ path: chunk3SDZERO4_cjs.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) {
const latest = [...this.providerVersions.values()].filter((version) => version.agentId === agentId).sort((a, b) => b.versionNumber - a.versionNumber)[0];
return (latest?.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;
});
}
};
exports.AgentsStorage = AgentsStorage;
exports.FilesystemAgentsStorage = FilesystemAgentsStorage;
exports.InMemoryAgentsStorage = InMemoryAgentsStorage;
exports.InMemoryDB = InMemoryDB;
exports.SourceAgentsSourceControl = SourceAgentsSourceControl;
//# sourceMappingURL=chunk-H3YX6CF5.cjs.map
//# sourceMappingURL=chunk-H3YX6CF5.cjs.map