@mastra/pg 1.10.1 → 1.11.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
2
- import { createVectorErrorId, AgentsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_SCHEMAS, createStorageErrorId, normalizePerPage, calculatePagination, BackgroundTasksStorage, TABLE_BACKGROUND_TASKS, BlobStore, TABLE_SKILL_BLOBS, ChannelsStorage, TABLE_CHANNEL_INSTALLATIONS, TABLE_CHANNEL_CONFIG, DatasetsStorage, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, DATASETS_SCHEMA, TABLE_CONFIGS, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, ensureDate, safelyParseJSON, ExperimentsStorage, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, MCPClientsStorage, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, MCPServersStorage, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, MemoryStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, ObservabilityStorage, TABLE_SPANS, listTracesArgsSchema, toTraceSpans, PromptBlocksStorage, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, SchedulesStorage, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, ScorerDefinitionsStorage, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, ScoresStorage, TABLE_SCORERS, SkillsStorage, TABLE_SKILLS, TABLE_SKILL_VERSIONS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, WorkspacesStorage, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, MastraCompositeStore, TraceStatus, getDefaultValue, transformScoreRow as transformScoreRow$1, getSqlType } from '@mastra/core/storage';
2
+ import { createVectorErrorId, AgentsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_SCHEMAS, createStorageErrorId, normalizePerPage, calculatePagination, BackgroundTasksStorage, TABLE_BACKGROUND_TASKS, BlobStore, TABLE_SKILL_BLOBS, ChannelsStorage, TABLE_CHANNEL_INSTALLATIONS, TABLE_CHANNEL_CONFIG, DatasetsStorage, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, DATASETS_SCHEMA, TABLE_CONFIGS, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, ensureDate, safelyParseJSON, ExperimentsStorage, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, FavoritesStorage, TABLE_FAVORITES, MCPClientsStorage, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, MCPServersStorage, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, MemoryStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, ObservabilityStorage, TABLE_SPANS, listTracesArgsSchema, toTraceSpans, PromptBlocksStorage, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, SchedulesStorage, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, ScorerDefinitionsStorage, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, ScoresStorage, TABLE_SCORERS, SkillsStorage, TABLE_SKILLS, TABLE_SKILL_VERSIONS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, WorkspacesStorage, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, MastraCompositeStore, TraceStatus, getDefaultValue, transformScoreRow as transformScoreRow$1, getSqlType } from '@mastra/core/storage';
3
3
  import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
4
4
  import { MastraVector, validateTopK, validateUpsertInput } from '@mastra/core/vector';
5
5
  import { Mutex } from 'async-mutex';
@@ -2013,6 +2013,7 @@ var PoolAdapter = class {
2013
2013
  constructor($pool) {
2014
2014
  this.$pool = $pool;
2015
2015
  }
2016
+ $pool;
2016
2017
  connect() {
2017
2018
  return this.$pool.connect();
2018
2019
  }
@@ -2081,6 +2082,7 @@ var TransactionClient = class {
2081
2082
  constructor(client) {
2082
2083
  this.client = client;
2083
2084
  }
2085
+ client;
2084
2086
  async none(query, values) {
2085
2087
  await this.client.query(query, values);
2086
2088
  return null;
@@ -3518,12 +3520,12 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3518
3520
  await this.#db.alterTable({
3519
3521
  tableName: TABLE_AGENTS,
3520
3522
  schema: TABLE_SCHEMAS[TABLE_AGENTS],
3521
- ifNotExists: ["status", "authorId"]
3523
+ ifNotExists: ["status", "authorId", "visibility", "favoriteCount"]
3522
3524
  });
3523
3525
  await this.#db.alterTable({
3524
3526
  tableName: TABLE_AGENT_VERSIONS,
3525
3527
  schema: TABLE_SCHEMAS[TABLE_AGENT_VERSIONS],
3526
- ifNotExists: ["mcpClients", "requestContextSchema", "workspace", "skills", "skillsFormat"]
3528
+ ifNotExists: ["mcpClients", "requestContextSchema", "workspace", "skills", "skillsFormat", "browser"]
3527
3529
  });
3528
3530
  await this.#migrateToolsToJsonbFormat();
3529
3531
  await this.createDefaultIndexes();
@@ -3664,14 +3666,28 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3664
3666
  }
3665
3667
  }
3666
3668
  /**
3667
- * Removes stale draft agent records that have no activeVersionId.
3669
+ * Removes stale draft agent records that have no versions at all.
3668
3670
  * These are left behind when createAgent partially fails (inserts thin record
3669
3671
  * but fails to create the version due to schema mismatch).
3672
+ *
3673
+ * A legitimate draft (never published) will have rows in the versions table,
3674
+ * so we must only delete records with zero associated versions.
3670
3675
  */
3671
3676
  async #cleanupStaleDrafts() {
3672
3677
  try {
3673
- const fullTableName = getTableName2({ indexName: TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3674
- await this.#db.client.none(`DELETE FROM ${fullTableName} WHERE status = 'draft' AND "activeVersionId" IS NULL`);
3678
+ const agentsTable = getTableName2({ indexName: TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3679
+ const versionsTable = getTableName2({
3680
+ indexName: TABLE_AGENT_VERSIONS,
3681
+ schemaName: getSchemaName2(this.#schema)
3682
+ });
3683
+ await this.#db.client.none(
3684
+ `DELETE FROM ${agentsTable} a
3685
+ WHERE a.status = 'draft'
3686
+ AND a."activeVersionId" IS NULL
3687
+ AND NOT EXISTS (
3688
+ SELECT 1 FROM ${versionsTable} v WHERE v."agentId" = a.id
3689
+ )`
3690
+ );
3675
3691
  } catch {
3676
3692
  }
3677
3693
  }
@@ -3700,7 +3716,9 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3700
3716
  status: row.status,
3701
3717
  activeVersionId: row.activeVersionId,
3702
3718
  authorId: row.authorId,
3719
+ visibility: row.visibility ?? void 0,
3703
3720
  metadata: parseJsonResilient(row.metadata),
3721
+ favoriteCount: row.favoriteCount === null || row.favoriteCount === void 0 ? 0 : Number(row.favoriteCount),
3704
3722
  createdAt: row.createdAtZ || row.createdAt,
3705
3723
  updatedAt: row.updatedAtZ || row.updatedAt
3706
3724
  };
@@ -3732,17 +3750,20 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3732
3750
  const agentsTable = getTableName2({ indexName: TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3733
3751
  const now = /* @__PURE__ */ new Date();
3734
3752
  const nowIso = now.toISOString();
3753
+ const visibility = agent.visibility ?? (agent.authorId ? "private" : null);
3735
3754
  await this.#db.client.none(
3736
3755
  `INSERT INTO ${agentsTable} (
3737
- id, status, "authorId", metadata,
3756
+ id, status, "authorId", visibility, metadata, "favoriteCount",
3738
3757
  "activeVersionId",
3739
3758
  "createdAt", "createdAtZ", "updatedAt", "updatedAtZ"
3740
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
3759
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
3741
3760
  [
3742
3761
  agent.id,
3743
3762
  "draft",
3744
3763
  agent.authorId ?? null,
3764
+ visibility,
3745
3765
  agent.metadata ? JSON.stringify(agent.metadata) : null,
3766
+ 0,
3746
3767
  null,
3747
3768
  // activeVersionId starts as null
3748
3769
  nowIso,
@@ -3751,7 +3772,7 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3751
3772
  nowIso
3752
3773
  ]
3753
3774
  );
3754
- const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = agent;
3775
+ const { id: _id, authorId: _authorId, visibility: _visibility, metadata: _metadata, ...snapshotConfig } = agent;
3755
3776
  const versionId = crypto.randomUUID();
3756
3777
  await this.createVersion({
3757
3778
  id: versionId,
@@ -3766,7 +3787,9 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3766
3787
  status: "draft",
3767
3788
  activeVersionId: void 0,
3768
3789
  authorId: agent.authorId,
3790
+ visibility: visibility ?? void 0,
3769
3791
  metadata: agent.metadata,
3792
+ favoriteCount: 0,
3770
3793
  createdAt: now,
3771
3794
  updatedAt: now
3772
3795
  };
@@ -3805,7 +3828,7 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3805
3828
  details: { agentId: id }
3806
3829
  });
3807
3830
  }
3808
- const { authorId, activeVersionId, metadata, status } = updates;
3831
+ const { authorId, activeVersionId, metadata, status, visibility } = updates;
3809
3832
  const setClauses = [];
3810
3833
  const values = [];
3811
3834
  let paramIndex = 1;
@@ -3821,6 +3844,10 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3821
3844
  setClauses.push(`status = $${paramIndex++}`);
3822
3845
  values.push(status);
3823
3846
  }
3847
+ if (visibility !== void 0) {
3848
+ setClauses.push(`visibility = $${paramIndex++}`);
3849
+ values.push(visibility);
3850
+ }
3824
3851
  if (metadata !== void 0) {
3825
3852
  setClauses.push(`metadata = $${paramIndex++}`);
3826
3853
  values.push(JSON.stringify(metadata));
@@ -3877,7 +3904,18 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3877
3904
  }
3878
3905
  }
3879
3906
  async list(args) {
3880
- const { page = 0, perPage: perPageInput, orderBy, authorId, metadata, status } = args || {};
3907
+ const {
3908
+ page = 0,
3909
+ perPage: perPageInput,
3910
+ orderBy,
3911
+ authorId,
3912
+ metadata,
3913
+ status,
3914
+ visibility,
3915
+ entityIds,
3916
+ pinFavoritedFor,
3917
+ favoritedOnly
3918
+ } = args || {};
3881
3919
  const { field, direction } = this.parseOrderBy(orderBy);
3882
3920
  if (page < 0) {
3883
3921
  throw new MastraError(
@@ -3893,26 +3931,58 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3893
3931
  const perPage = normalizePerPage(perPageInput, 100);
3894
3932
  const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
3895
3933
  try {
3934
+ if (entityIds && entityIds.length === 0) {
3935
+ return {
3936
+ agents: [],
3937
+ total: 0,
3938
+ page,
3939
+ perPage: perPageForResponse,
3940
+ hasMore: false
3941
+ };
3942
+ }
3896
3943
  const tableName = getTableName2({ indexName: TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3944
+ const favoritesTable = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
3897
3945
  const conditions = [];
3898
3946
  const queryParams = [];
3899
3947
  let paramIdx = 1;
3948
+ const joinUserId = pinFavoritedFor;
3949
+ const useJoin = Boolean(joinUserId);
3950
+ let joinSqlIdx = null;
3951
+ if (useJoin) {
3952
+ joinSqlIdx = paramIdx++;
3953
+ }
3900
3954
  if (status) {
3901
- conditions.push(`status = $${paramIdx++}`);
3955
+ conditions.push(`a.status = $${paramIdx++}`);
3902
3956
  queryParams.push(status);
3903
3957
  }
3904
3958
  if (authorId !== void 0) {
3905
- conditions.push(`"authorId" = $${paramIdx++}`);
3959
+ conditions.push(`a."authorId" = $${paramIdx++}`);
3906
3960
  queryParams.push(authorId);
3907
3961
  }
3962
+ if (visibility !== void 0) {
3963
+ conditions.push(`a.visibility = $${paramIdx++}`);
3964
+ queryParams.push(visibility);
3965
+ }
3908
3966
  if (metadata && Object.keys(metadata).length > 0) {
3909
- conditions.push(`metadata @> $${paramIdx++}::jsonb`);
3967
+ conditions.push(`a.metadata @> $${paramIdx++}::jsonb`);
3910
3968
  queryParams.push(JSON.stringify(metadata));
3911
3969
  }
3970
+ if (entityIds && entityIds.length > 0) {
3971
+ const placeholders = entityIds.map(() => `$${paramIdx++}`).join(", ");
3972
+ conditions.push(`a.id IN (${placeholders})`);
3973
+ queryParams.push(...entityIds);
3974
+ }
3975
+ if (useJoin && favoritedOnly) {
3976
+ conditions.push('s."userId" IS NOT NULL');
3977
+ } else if (favoritedOnly) {
3978
+ conditions.push("1=0");
3979
+ }
3980
+ const joinClause = useJoin && joinSqlIdx !== null ? `LEFT JOIN ${favoritesTable} s ON s."entityType" = 'agent' AND s."entityId" = a.id AND s."userId" = $${joinSqlIdx}` : "";
3981
+ const joinParams = useJoin && joinUserId ? [joinUserId] : [];
3912
3982
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3913
3983
  const countResult = await this.#db.client.one(
3914
- `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`,
3915
- queryParams
3984
+ `SELECT COUNT(*) as count FROM ${tableName} a ${joinClause} ${whereClause}`,
3985
+ [...joinParams, ...queryParams]
3916
3986
  );
3917
3987
  const total = parseInt(countResult.count, 10);
3918
3988
  if (total === 0) {
@@ -3924,10 +3994,19 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3924
3994
  hasMore: false
3925
3995
  };
3926
3996
  }
3997
+ const orderByParts = [];
3998
+ if (useJoin) {
3999
+ orderByParts.push(`(s."userId" IS NOT NULL) DESC`);
4000
+ }
4001
+ orderByParts.push(`a."${field}" ${direction}`);
4002
+ orderByParts.push(`a."id" ASC`);
4003
+ const orderByClause = `ORDER BY ${orderByParts.join(", ")}`;
3927
4004
  const limitValue = perPageInput === false ? total : perPage;
4005
+ const limitIdx = paramIdx++;
4006
+ const offsetIdx = paramIdx++;
3928
4007
  const dataResult = await this.#db.client.manyOrNone(
3929
- `SELECT * FROM ${tableName} ${whereClause} ORDER BY "${field}" ${direction} LIMIT $${paramIdx++} OFFSET $${paramIdx++}`,
3930
- [...queryParams, limitValue, offset]
4008
+ `SELECT a.* FROM ${tableName} a ${joinClause} ${whereClause} ${orderByClause} LIMIT $${limitIdx} OFFSET $${offsetIdx}`,
4009
+ [...joinParams, ...queryParams, limitValue, offset]
3931
4010
  );
3932
4011
  const agents = (dataResult || []).flatMap((row) => {
3933
4012
  try {
@@ -3971,9 +4050,10 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3971
4050
  "defaultOptions", workflows, agents, "integrationTools",
3972
4051
  "inputProcessors", "outputProcessors", memory, scorers,
3973
4052
  "mcpClients", "requestContextSchema", workspace, skills, "skillsFormat",
4053
+ browser,
3974
4054
  "changedFields", "changeMessage",
3975
4055
  "createdAt", "createdAtZ"
3976
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)`,
4056
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26)`,
3977
4057
  [
3978
4058
  input.id,
3979
4059
  input.agentId,
@@ -3996,6 +4076,7 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
3996
4076
  input.workspace ? JSON.stringify(input.workspace) : null,
3997
4077
  input.skills ? JSON.stringify(input.skills) : null,
3998
4078
  input.skillsFormat ?? null,
4079
+ input.browser ? JSON.stringify(input.browser) : null,
3999
4080
  input.changedFields ? JSON.stringify(input.changedFields) : null,
4000
4081
  input.changeMessage ?? null,
4001
4082
  nowIso,
@@ -4245,6 +4326,7 @@ var AgentsPG = class _AgentsPG extends AgentsStorage {
4245
4326
  workspace: parseJsonResilient(row.workspace),
4246
4327
  skills: parseJsonResilient(row.skills),
4247
4328
  skillsFormat: row.skillsFormat,
4329
+ browser: parseJsonResilient(row.browser),
4248
4330
  changedFields: parseJsonResilient(row.changedFields),
4249
4331
  changeMessage: row.changeMessage,
4250
4332
  createdAt: row.createdAtZ || row.createdAt
@@ -6396,6 +6478,250 @@ var ExperimentsPG = class _ExperimentsPG extends ExperimentsStorage {
6396
6478
  await this.#db.clearTable({ tableName: TABLE_EXPERIMENTS });
6397
6479
  }
6398
6480
  };
6481
+ var ENTITY_TABLE = {
6482
+ agent: TABLE_AGENTS,
6483
+ skill: TABLE_SKILLS
6484
+ };
6485
+ var FavoritesPG = class _FavoritesPG extends FavoritesStorage {
6486
+ #db;
6487
+ #schema;
6488
+ static MANAGED_TABLES = [TABLE_FAVORITES];
6489
+ constructor(config) {
6490
+ super();
6491
+ const { client, schemaName, skipDefaultIndexes } = resolvePgConfig(config);
6492
+ this.#db = new PgDB({ client, schemaName, skipDefaultIndexes });
6493
+ this.#schema = schemaName || "public";
6494
+ }
6495
+ static getExportDDL(schemaName) {
6496
+ const statements = [];
6497
+ for (const tableName of _FavoritesPG.MANAGED_TABLES) {
6498
+ statements.push(
6499
+ generateTableSQL({
6500
+ tableName,
6501
+ schema: TABLE_SCHEMAS[tableName],
6502
+ schemaName,
6503
+ compositePrimaryKey: ["userId", "entityType", "entityId"],
6504
+ includeAllConstraints: true
6505
+ })
6506
+ );
6507
+ }
6508
+ const fullFavoritesTable = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(schemaName) });
6509
+ statements.push(
6510
+ `CREATE INDEX IF NOT EXISTS idx_favorites_entity ON ${fullFavoritesTable} ("entityType", "entityId")`
6511
+ );
6512
+ return statements;
6513
+ }
6514
+ async init() {
6515
+ await this.#db.createTable({
6516
+ tableName: TABLE_FAVORITES,
6517
+ schema: TABLE_SCHEMAS[TABLE_FAVORITES],
6518
+ compositePrimaryKey: ["userId", "entityType", "entityId"]
6519
+ });
6520
+ const fullTableName = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6521
+ await this.#db.client.none(
6522
+ `CREATE INDEX IF NOT EXISTS idx_favorites_entity ON ${fullTableName} ("entityType", "entityId")`
6523
+ );
6524
+ }
6525
+ async dangerouslyClearAll() {
6526
+ const fullTableName = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6527
+ const fullAgentsTable = getTableName2({ indexName: TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
6528
+ const fullSkillsTable = getTableName2({ indexName: TABLE_SKILLS, schemaName: getSchemaName2(this.#schema) });
6529
+ await this.#db.client.tx(async (t) => {
6530
+ await t.none(`DELETE FROM ${fullTableName}`);
6531
+ await t.none(`UPDATE ${fullAgentsTable} SET "favoriteCount" = 0 WHERE "favoriteCount" > 0`);
6532
+ await t.none(`UPDATE ${fullSkillsTable} SET "favoriteCount" = 0 WHERE "favoriteCount" > 0`);
6533
+ });
6534
+ }
6535
+ async favorite(input) {
6536
+ const { userId, entityType, entityId } = input;
6537
+ const entityTable = ENTITY_TABLE[entityType];
6538
+ const fullFavoritesTable = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6539
+ const fullEntityTable = getTableName2({ indexName: entityTable, schemaName: getSchemaName2(this.#schema) });
6540
+ try {
6541
+ return await this.#db.client.tx(async (t) => {
6542
+ const entityRow = await t.oneOrNone(`SELECT "favoriteCount" FROM ${fullEntityTable} WHERE id = $1`, [entityId]);
6543
+ if (!entityRow) {
6544
+ throw new MastraError({
6545
+ id: createStorageErrorId("PG", "FAVORITE", "ENTITY_NOT_FOUND"),
6546
+ domain: ErrorDomain.STORAGE,
6547
+ category: ErrorCategory.USER,
6548
+ text: `${entityType} ${entityId} not found`,
6549
+ details: { entityType, entityId }
6550
+ });
6551
+ }
6552
+ const inserted = await t.oneOrNone(
6553
+ `INSERT INTO ${fullFavoritesTable} ("userId", "entityType", "entityId", "createdAt", "createdAtZ")
6554
+ VALUES ($1, $2, $3, $4, $5)
6555
+ ON CONFLICT ("userId", "entityType", "entityId") DO NOTHING
6556
+ RETURNING "userId"`,
6557
+ [userId, entityType, entityId, (/* @__PURE__ */ new Date()).toISOString(), (/* @__PURE__ */ new Date()).toISOString()]
6558
+ );
6559
+ if (inserted) {
6560
+ await t.none(
6561
+ `UPDATE ${fullEntityTable} SET "favoriteCount" = COALESCE("favoriteCount", 0) + 1 WHERE id = $1`,
6562
+ [entityId]
6563
+ );
6564
+ }
6565
+ const after = await t.one(`SELECT "favoriteCount" FROM ${fullEntityTable} WHERE id = $1`, [entityId]);
6566
+ const favoriteCount = Number(after.favoriteCount ?? 0);
6567
+ return { favorited: true, favoriteCount };
6568
+ });
6569
+ } catch (error) {
6570
+ if (error instanceof MastraError) throw error;
6571
+ throw new MastraError(
6572
+ {
6573
+ id: createStorageErrorId("PG", "FAVORITE", "FAILED"),
6574
+ domain: ErrorDomain.STORAGE,
6575
+ category: ErrorCategory.THIRD_PARTY,
6576
+ details: { entityType, entityId }
6577
+ },
6578
+ error
6579
+ );
6580
+ }
6581
+ }
6582
+ async unfavorite(input) {
6583
+ const { userId, entityType, entityId } = input;
6584
+ const entityTable = ENTITY_TABLE[entityType];
6585
+ const fullFavoritesTable = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6586
+ const fullEntityTable = getTableName2({ indexName: entityTable, schemaName: getSchemaName2(this.#schema) });
6587
+ try {
6588
+ return await this.#db.client.tx(async (t) => {
6589
+ const entityRow = await t.oneOrNone(`SELECT "favoriteCount" FROM ${fullEntityTable} WHERE id = $1`, [entityId]);
6590
+ if (!entityRow) {
6591
+ throw new MastraError({
6592
+ id: createStorageErrorId("PG", "UNFAVORITE", "ENTITY_NOT_FOUND"),
6593
+ domain: ErrorDomain.STORAGE,
6594
+ category: ErrorCategory.USER,
6595
+ text: `${entityType} ${entityId} not found`,
6596
+ details: { entityType, entityId }
6597
+ });
6598
+ }
6599
+ const deleted = await t.oneOrNone(
6600
+ `DELETE FROM ${fullFavoritesTable} WHERE "userId" = $1 AND "entityType" = $2 AND "entityId" = $3 RETURNING "userId"`,
6601
+ [userId, entityType, entityId]
6602
+ );
6603
+ if (deleted) {
6604
+ await t.none(
6605
+ `UPDATE ${fullEntityTable} SET "favoriteCount" = GREATEST(COALESCE("favoriteCount", 0) - 1, 0) WHERE id = $1`,
6606
+ [entityId]
6607
+ );
6608
+ }
6609
+ const after = await t.one(`SELECT "favoriteCount" FROM ${fullEntityTable} WHERE id = $1`, [entityId]);
6610
+ const favoriteCount = Number(after.favoriteCount ?? 0);
6611
+ return { favorited: false, favoriteCount };
6612
+ });
6613
+ } catch (error) {
6614
+ if (error instanceof MastraError) throw error;
6615
+ throw new MastraError(
6616
+ {
6617
+ id: createStorageErrorId("PG", "UNFAVORITE", "FAILED"),
6618
+ domain: ErrorDomain.STORAGE,
6619
+ category: ErrorCategory.THIRD_PARTY,
6620
+ details: { entityType, entityId }
6621
+ },
6622
+ error
6623
+ );
6624
+ }
6625
+ }
6626
+ async isFavorited(input) {
6627
+ const fullFavoritesTable = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6628
+ try {
6629
+ const result = await this.#db.client.oneOrNone(
6630
+ `SELECT 1 FROM ${fullFavoritesTable} WHERE "userId" = $1 AND "entityType" = $2 AND "entityId" = $3 LIMIT 1`,
6631
+ [input.userId, input.entityType, input.entityId]
6632
+ );
6633
+ return result !== null;
6634
+ } catch (error) {
6635
+ if (error instanceof MastraError) throw error;
6636
+ throw new MastraError(
6637
+ {
6638
+ id: createStorageErrorId("PG", "IS_FAVORITED", "FAILED"),
6639
+ domain: ErrorDomain.STORAGE,
6640
+ category: ErrorCategory.THIRD_PARTY
6641
+ },
6642
+ error
6643
+ );
6644
+ }
6645
+ }
6646
+ async isFavoritedBatch(input) {
6647
+ const { userId, entityType, entityIds } = input;
6648
+ if (entityIds.length === 0) {
6649
+ return /* @__PURE__ */ new Set();
6650
+ }
6651
+ const fullFavoritesTable = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6652
+ try {
6653
+ const placeholders = entityIds.map((_, i) => `$${i + 3}`).join(", ");
6654
+ const rows = await this.#db.client.manyOrNone(
6655
+ `SELECT "entityId" FROM ${fullFavoritesTable} WHERE "userId" = $1 AND "entityType" = $2 AND "entityId" IN (${placeholders})`,
6656
+ [userId, entityType, ...entityIds]
6657
+ );
6658
+ const set = /* @__PURE__ */ new Set();
6659
+ for (const row of rows ?? []) {
6660
+ set.add(row.entityId);
6661
+ }
6662
+ return set;
6663
+ } catch (error) {
6664
+ if (error instanceof MastraError) throw error;
6665
+ throw new MastraError(
6666
+ {
6667
+ id: createStorageErrorId("PG", "IS_FAVORITED_BATCH", "FAILED"),
6668
+ domain: ErrorDomain.STORAGE,
6669
+ category: ErrorCategory.THIRD_PARTY
6670
+ },
6671
+ error
6672
+ );
6673
+ }
6674
+ }
6675
+ async listFavoritedIds(input) {
6676
+ const fullFavoritesTable = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6677
+ try {
6678
+ const rows = await this.#db.client.manyOrNone(
6679
+ `SELECT "entityId" FROM ${fullFavoritesTable} WHERE "userId" = $1 AND "entityType" = $2 ORDER BY "createdAt" DESC, "entityId" ASC`,
6680
+ [input.userId, input.entityType]
6681
+ );
6682
+ return (rows ?? []).map((row) => row.entityId);
6683
+ } catch (error) {
6684
+ if (error instanceof MastraError) throw error;
6685
+ throw new MastraError(
6686
+ {
6687
+ id: createStorageErrorId("PG", "LIST_FAVORITED_IDS", "FAILED"),
6688
+ domain: ErrorDomain.STORAGE,
6689
+ category: ErrorCategory.THIRD_PARTY
6690
+ },
6691
+ error
6692
+ );
6693
+ }
6694
+ }
6695
+ async deleteFavoritesForEntity(input) {
6696
+ const fullFavoritesTable = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6697
+ const entityTable = ENTITY_TABLE[input.entityType];
6698
+ const fullEntityTable = getTableName2({ indexName: entityTable, schemaName: getSchemaName2(this.#schema) });
6699
+ try {
6700
+ return await this.#db.client.tx(async (t) => {
6701
+ const result = await t.one(
6702
+ `WITH deleted AS (
6703
+ DELETE FROM ${fullFavoritesTable} WHERE "entityType" = $1 AND "entityId" = $2 RETURNING 1
6704
+ )
6705
+ SELECT COUNT(*)::text AS count FROM deleted`,
6706
+ [input.entityType, input.entityId]
6707
+ );
6708
+ await t.none(`UPDATE ${fullEntityTable} SET "favoriteCount" = 0 WHERE id = $1`, [input.entityId]);
6709
+ return Number(result.count);
6710
+ });
6711
+ } catch (error) {
6712
+ if (error instanceof MastraError) throw error;
6713
+ throw new MastraError(
6714
+ {
6715
+ id: createStorageErrorId("PG", "DELETE_FAVORITES_FOR_ENTITY", "FAILED"),
6716
+ domain: ErrorDomain.STORAGE,
6717
+ category: ErrorCategory.THIRD_PARTY,
6718
+ details: { entityType: input.entityType, entityId: input.entityId }
6719
+ },
6720
+ error
6721
+ );
6722
+ }
6723
+ }
6724
+ };
6399
6725
  var SNAPSHOT_FIELDS = ["name", "description", "servers"];
6400
6726
  var MCPClientsPG = class _MCPClientsPG extends MCPClientsStorage {
6401
6727
  #db;
@@ -12702,6 +13028,7 @@ var SNAPSHOT_FIELDS5 = [
12702
13028
  "references",
12703
13029
  "scripts",
12704
13030
  "assets",
13031
+ "files",
12705
13032
  "metadata",
12706
13033
  "tree"
12707
13034
  ];
@@ -12749,6 +13076,16 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
12749
13076
  tableName: TABLE_SKILL_VERSIONS,
12750
13077
  schema: TABLE_SCHEMAS[TABLE_SKILL_VERSIONS]
12751
13078
  });
13079
+ await this.#db.alterTable({
13080
+ tableName: TABLE_SKILLS,
13081
+ schema: TABLE_SCHEMAS[TABLE_SKILLS],
13082
+ ifNotExists: ["visibility", "favoriteCount"]
13083
+ });
13084
+ await this.#db.alterTable({
13085
+ tableName: TABLE_SKILL_VERSIONS,
13086
+ schema: TABLE_SCHEMAS[TABLE_SKILL_VERSIONS],
13087
+ ifNotExists: ["files"]
13088
+ });
12752
13089
  await this.createDefaultIndexes();
12753
13090
  await this.createCustomIndexes();
12754
13091
  }
@@ -12798,14 +13135,15 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
12798
13135
  const tableName = getTableName2({ indexName: TABLE_SKILLS, schemaName: getSchemaName2(this.#schema) });
12799
13136
  const now = /* @__PURE__ */ new Date();
12800
13137
  const nowIso = now.toISOString();
13138
+ const visibility = skill.visibility ?? (skill.authorId ? "private" : void 0);
12801
13139
  await this.#db.client.none(
12802
13140
  `INSERT INTO ${tableName} (
12803
- id, status, "activeVersionId", "authorId",
13141
+ id, status, "activeVersionId", "authorId", visibility, "favoriteCount",
12804
13142
  "createdAt", "createdAtZ", "updatedAt", "updatedAtZ"
12805
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
12806
- [skill.id, "draft", null, skill.authorId ?? null, nowIso, nowIso, nowIso, nowIso]
13143
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
13144
+ [skill.id, "draft", null, skill.authorId ?? null, visibility ?? null, 0, nowIso, nowIso, nowIso, nowIso]
12807
13145
  );
12808
- const { id: _id, authorId: _authorId, ...snapshotConfig } = skill;
13146
+ const { id: _id, authorId: _authorId, visibility: _visibility, ...snapshotConfig } = skill;
12809
13147
  const versionId = crypto.randomUUID();
12810
13148
  await this.createVersion({
12811
13149
  id: versionId,
@@ -12820,6 +13158,8 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
12820
13158
  status: "draft",
12821
13159
  activeVersionId: void 0,
12822
13160
  authorId: skill.authorId,
13161
+ visibility,
13162
+ favoriteCount: 0,
12823
13163
  createdAt: now,
12824
13164
  updatedAt: now
12825
13165
  };
@@ -12861,8 +13201,12 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
12861
13201
  details: { skillId: id }
12862
13202
  });
12863
13203
  }
12864
- const { authorId, activeVersionId, status, ...configFields } = updates;
13204
+ const { authorId, visibility, activeVersionId, status, ...rawConfigFields } = updates;
12865
13205
  let versionCreated = false;
13206
+ const configFields = {};
13207
+ for (const [key, value] of Object.entries(rawConfigFields)) {
13208
+ if (value !== void 0) configFields[key] = value;
13209
+ }
12866
13210
  const hasConfigUpdate = SNAPSHOT_FIELDS5.some((field) => field in configFields);
12867
13211
  if (hasConfigUpdate) {
12868
13212
  const latestVersion = await this.getLatestVersion(id);
@@ -12908,6 +13252,10 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
12908
13252
  setClauses.push(`"authorId" = $${paramIndex++}`);
12909
13253
  values.push(authorId);
12910
13254
  }
13255
+ if (visibility !== void 0) {
13256
+ setClauses.push(`visibility = $${paramIndex++}`);
13257
+ values.push(visibility);
13258
+ }
12911
13259
  if (activeVersionId !== void 0) {
12912
13260
  setClauses.push(`"activeVersionId" = $${paramIndex++}`);
12913
13261
  values.push(activeVersionId);
@@ -12975,7 +13323,17 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
12975
13323
  }
12976
13324
  }
12977
13325
  async list(args) {
12978
- const { page = 0, perPage: perPageInput, orderBy, authorId } = args || {};
13326
+ const {
13327
+ page = 0,
13328
+ perPage: perPageInput,
13329
+ orderBy,
13330
+ authorId,
13331
+ visibility,
13332
+ status,
13333
+ entityIds,
13334
+ pinFavoritedFor,
13335
+ favoritedOnly
13336
+ } = args || {};
12979
13337
  const { field, direction } = this.parseOrderBy(orderBy);
12980
13338
  if (page < 0) {
12981
13339
  throw new MastraError(
@@ -12991,18 +13349,54 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
12991
13349
  const perPage = normalizePerPage(perPageInput, 100);
12992
13350
  const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
12993
13351
  try {
13352
+ if (entityIds && entityIds.length === 0) {
13353
+ return {
13354
+ skills: [],
13355
+ total: 0,
13356
+ page,
13357
+ perPage: perPageForResponse,
13358
+ hasMore: false
13359
+ };
13360
+ }
12994
13361
  const tableName = getTableName2({ indexName: TABLE_SKILLS, schemaName: getSchemaName2(this.#schema) });
13362
+ const favoritesTable = getTableName2({ indexName: TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
12995
13363
  const conditions = [];
12996
13364
  const queryParams = [];
12997
13365
  let paramIdx = 1;
13366
+ const joinUserId = pinFavoritedFor;
13367
+ const useJoin = Boolean(joinUserId);
13368
+ let joinSqlIdx = null;
13369
+ if (useJoin) {
13370
+ joinSqlIdx = paramIdx++;
13371
+ }
13372
+ if (status) {
13373
+ conditions.push(`s.status = $${paramIdx++}`);
13374
+ queryParams.push(status);
13375
+ }
12998
13376
  if (authorId !== void 0) {
12999
- conditions.push(`"authorId" = $${paramIdx++}`);
13377
+ conditions.push(`s."authorId" = $${paramIdx++}`);
13000
13378
  queryParams.push(authorId);
13001
13379
  }
13380
+ if (visibility !== void 0) {
13381
+ conditions.push(`s.visibility = $${paramIdx++}`);
13382
+ queryParams.push(visibility);
13383
+ }
13384
+ if (entityIds && entityIds.length > 0) {
13385
+ const placeholders = entityIds.map(() => `$${paramIdx++}`).join(", ");
13386
+ conditions.push(`s.id IN (${placeholders})`);
13387
+ queryParams.push(...entityIds);
13388
+ }
13389
+ if (useJoin && favoritedOnly) {
13390
+ conditions.push('sr."userId" IS NOT NULL');
13391
+ } else if (favoritedOnly) {
13392
+ conditions.push("1=0");
13393
+ }
13394
+ const joinClause = useJoin && joinSqlIdx !== null ? `LEFT JOIN ${favoritesTable} sr ON sr."entityType" = 'skill' AND sr."entityId" = s.id AND sr."userId" = $${joinSqlIdx}` : "";
13395
+ const joinParams = useJoin && joinUserId ? [joinUserId] : [];
13002
13396
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
13003
13397
  const countResult = await this.#db.client.one(
13004
- `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`,
13005
- queryParams
13398
+ `SELECT COUNT(*) as count FROM ${tableName} s ${joinClause} ${whereClause}`,
13399
+ [...joinParams, ...queryParams]
13006
13400
  );
13007
13401
  const total = parseInt(countResult.count, 10);
13008
13402
  if (total === 0) {
@@ -13014,10 +13408,19 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
13014
13408
  hasMore: false
13015
13409
  };
13016
13410
  }
13411
+ const orderByParts = [];
13412
+ if (useJoin) {
13413
+ orderByParts.push(`(sr."userId" IS NOT NULL) DESC`);
13414
+ }
13415
+ orderByParts.push(`s."${field}" ${direction}`);
13416
+ orderByParts.push(`s."id" ASC`);
13417
+ const orderByClause = `ORDER BY ${orderByParts.join(", ")}`;
13017
13418
  const limitValue = perPageInput === false ? total : perPage;
13419
+ const limitIdx = paramIdx++;
13420
+ const offsetIdx = paramIdx++;
13018
13421
  const dataResult = await this.#db.client.manyOrNone(
13019
- `SELECT * FROM ${tableName} ${whereClause} ORDER BY "${field}" ${direction} LIMIT $${paramIdx++} OFFSET $${paramIdx++}`,
13020
- [...queryParams, limitValue, offset]
13422
+ `SELECT s.* FROM ${tableName} s ${joinClause} ${whereClause} ${orderByClause} LIMIT $${limitIdx} OFFSET $${offsetIdx}`,
13423
+ [...joinParams, ...queryParams, limitValue, offset]
13021
13424
  );
13022
13425
  const skills = (dataResult || []).flatMap((row) => {
13023
13426
  try {
@@ -13061,10 +13464,10 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
13061
13464
  `INSERT INTO ${tableName} (
13062
13465
  id, "skillId", "versionNumber",
13063
13466
  name, description, instructions, license, compatibility,
13064
- source, "references", scripts, assets, metadata, tree,
13467
+ source, "references", scripts, assets, files, metadata, tree,
13065
13468
  "changedFields", "changeMessage",
13066
13469
  "createdAt", "createdAtZ"
13067
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)`,
13470
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)`,
13068
13471
  [
13069
13472
  input.id,
13070
13473
  input.skillId,
@@ -13078,6 +13481,7 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
13078
13481
  input.references ? JSON.stringify(input.references) : null,
13079
13482
  input.scripts ? JSON.stringify(input.scripts) : null,
13080
13483
  input.assets ? JSON.stringify(input.assets) : null,
13484
+ input.files ? JSON.stringify(input.files) : null,
13081
13485
  input.metadata ? JSON.stringify(input.metadata) : null,
13082
13486
  input.tree ? JSON.stringify(input.tree) : null,
13083
13487
  input.changedFields ? JSON.stringify(input.changedFields) : null,
@@ -13320,6 +13724,8 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
13320
13724
  status: row.status,
13321
13725
  activeVersionId: row.activeVersionId,
13322
13726
  authorId: row.authorId,
13727
+ visibility: row.visibility,
13728
+ favoriteCount: row.favoriteCount === null || row.favoriteCount === void 0 ? 0 : Number(row.favoriteCount),
13323
13729
  createdAt: new Date(row.createdAtZ || row.createdAt),
13324
13730
  updatedAt: new Date(row.updatedAtZ || row.updatedAt)
13325
13731
  };
@@ -13338,6 +13744,7 @@ var SkillsPG = class _SkillsPG extends SkillsStorage {
13338
13744
  references: parseJsonResilient(row.references),
13339
13745
  scripts: parseJsonResilient(row.scripts),
13340
13746
  assets: parseJsonResilient(row.assets),
13747
+ files: parseJsonResilient(row.files),
13341
13748
  metadata: parseJsonResilient(row.metadata),
13342
13749
  tree: parseJsonResilient(row.tree),
13343
13750
  changedFields: parseJsonResilient(row.changedFields),
@@ -13939,8 +14346,12 @@ var WorkspacesPG = class _WorkspacesPG extends WorkspacesStorage {
13939
14346
  details: { workspaceId: id }
13940
14347
  });
13941
14348
  }
13942
- const { authorId, activeVersionId, metadata, status, ...configFields } = updates;
14349
+ const { authorId, activeVersionId, metadata, status, ...rawConfigFields } = updates;
13943
14350
  let versionCreated = false;
14351
+ const configFields = {};
14352
+ for (const [key, value] of Object.entries(rawConfigFields)) {
14353
+ if (value !== void 0) configFields[key] = value;
14354
+ }
13944
14355
  const hasConfigUpdate = SNAPSHOT_FIELDS6.some((field) => field in configFields);
13945
14356
  if (hasConfigUpdate) {
13946
14357
  const latestVersion = await this.getLatestVersion(id);
@@ -14448,6 +14859,7 @@ var ALL_DOMAINS = [
14448
14859
  DatasetsPG,
14449
14860
  ExperimentsPG,
14450
14861
  BackgroundTasksPG,
14862
+ FavoritesPG,
14451
14863
  ChannelsPG,
14452
14864
  SchedulesPG
14453
14865
  ];
@@ -14501,6 +14913,7 @@ var PostgresStore = class extends MastraCompositeStore {
14501
14913
  mcpServers: new MCPServersPG(domainConfig),
14502
14914
  workspaces: new WorkspacesPG(domainConfig),
14503
14915
  skills: new SkillsPG(domainConfig),
14916
+ favorites: new FavoritesPG(domainConfig),
14504
14917
  blobs: new BlobsPG(domainConfig),
14505
14918
  datasets: new DatasetsPG(domainConfig),
14506
14919
  experiments: new ExperimentsPG(domainConfig),
@@ -14695,6 +15108,6 @@ Example Complex Query:
14695
15108
  ]
14696
15109
  }`;
14697
15110
 
14698
- export { AgentsPG, BackgroundTasksPG, BlobsPG, ChannelsPG, DatasetsPG, ExperimentsPG, MCPClientsPG, MCPServersPG, MemoryPG, ObservabilityPG, PGVECTOR_PROMPT, PgVector, PoolAdapter, PostgresStore, PromptBlocksPG, SchedulesPG, ScorerDefinitionsPG, ScoresPG, SkillsPG, WorkflowsPG, WorkspacesPG, exportSchemas };
15111
+ export { AgentsPG, BackgroundTasksPG, BlobsPG, ChannelsPG, DatasetsPG, ExperimentsPG, FavoritesPG, MCPClientsPG, MCPServersPG, MemoryPG, ObservabilityPG, PGVECTOR_PROMPT, PgVector, PoolAdapter, PostgresStore, PromptBlocksPG, SchedulesPG, ScorerDefinitionsPG, ScoresPG, SkillsPG, WorkflowsPG, WorkspacesPG, exportSchemas };
14699
15112
  //# sourceMappingURL=index.js.map
14700
15113
  //# sourceMappingURL=index.js.map