@mastra/pg 1.1.0-alpha.0 → 1.1.0-alpha.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @mastra/pg
2
2
 
3
+ ## 1.1.0-alpha.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Stored agent edits no longer fail silently. PATCH requests now save changes correctly. ([#12504](https://github.com/mastra-ai/mastra/pull/12504))
8
+
9
+ - Fix PATCH request JSON-body handling in `@mastra/client-js` so stored agent edit flows work correctly. Fix stored agent schema migration in `@mastra/libsql` and `@mastra/pg` to drop and recreate the versions table when the old snapshot-based schema is detected, clean up stale draft records from partial create failures, and remove lingering legacy tables. Restores create and edit flows for stored agents. ([#12504](https://github.com/mastra-ai/mastra/pull/12504))
10
+
11
+ - Updated dependencies:
12
+ - @mastra/core@1.1.0-alpha.2
13
+
14
+ ## 1.1.0-alpha.1
15
+
16
+ ### Minor Changes
17
+
18
+ - Restructured stored agents to use a thin metadata record with versioned configuration snapshots. ([#12488](https://github.com/mastra-ai/mastra/pull/12488))
19
+
20
+ The agent record now only stores metadata fields (id, status, activeVersionId, authorId, metadata, timestamps). All configuration fields (name, instructions, model, tools, etc.) live exclusively in version snapshot rows, enabling full version history and rollback.
21
+
22
+ **Key changes:**
23
+ - Stored Agent records are now thin metadata-only (StorageAgentType)
24
+ - All config lives in version snapshots (StorageAgentSnapshotType)
25
+ - New resolved type (StorageResolvedAgentType) merges agent record + active version config
26
+ - Renamed `ownerId` to `authorId` for multi-tenant filtering
27
+ - Changed `memory` field type from `string` to `Record<string, unknown>`
28
+ - Added `status` field ('draft' | 'published') to agent records
29
+ - Flattened CreateAgent/UpdateAgent input types (config fields at top level, no nested snapshot)
30
+ - Version config columns are top-level in the agent_versions table (no single snapshot jsonb column)
31
+ - List endpoints return resolved agents (thin record + active version config)
32
+ - Auto-versioning on update with retention limits and race condition handling
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies [[`b99ceac`](https://github.com/mastra-ai/mastra/commit/b99ceace2c830dbdef47c8692d56a91954aefea2), [`deea43e`](https://github.com/mastra-ai/mastra/commit/deea43eb1366d03a864c5e597d16a48592b9893f), [`ac9ec66`](https://github.com/mastra-ai/mastra/commit/ac9ec6672779b2e6d4344e415481d1a6a7d4911a)]:
37
+ - @mastra/core@1.1.0-alpha.1
38
+
3
39
  ## 1.1.0-alpha.0
4
40
 
5
41
  ### Minor Changes
@@ -33,4 +33,4 @@ docs/
33
33
  ## Version
34
34
 
35
35
  Package: @mastra/pg
36
- Version: 1.1.0-alpha.0
36
+ Version: 1.1.0-alpha.2
@@ -5,7 +5,7 @@ description: Documentation for @mastra/pg. Includes links to type definitions an
5
5
 
6
6
  # @mastra/pg Documentation
7
7
 
8
- > **Version**: 1.1.0-alpha.0
8
+ > **Version**: 1.1.0-alpha.2
9
9
  > **Package**: @mastra/pg
10
10
 
11
11
  ## Quick Navigation
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.0-alpha.0",
2
+ "version": "1.1.0-alpha.2",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -3143,10 +3143,124 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3143
3143
  }
3144
3144
  }
3145
3145
  async init() {
3146
+ await this.#migrateFromLegacySchema();
3147
+ await this.#migrateVersionsSchema();
3146
3148
  await this.#db.createTable({ tableName: storage.TABLE_AGENTS, schema: storage.TABLE_SCHEMAS[storage.TABLE_AGENTS] });
3147
3149
  await this.#db.createTable({ tableName: storage.TABLE_AGENT_VERSIONS, schema: storage.TABLE_SCHEMAS[storage.TABLE_AGENT_VERSIONS] });
3150
+ await this.#db.alterTable({
3151
+ tableName: storage.TABLE_AGENTS,
3152
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_AGENTS],
3153
+ ifNotExists: ["status", "authorId"]
3154
+ });
3148
3155
  await this.createDefaultIndexes();
3149
3156
  await this.createCustomIndexes();
3157
+ await this.#cleanupStaleDrafts();
3158
+ }
3159
+ /**
3160
+ * Migrates from the legacy flat agent schema (where config fields like name, instructions, model
3161
+ * were stored directly on mastra_agents) to the new versioned schema (thin agent record + versions table).
3162
+ */
3163
+ async #migrateFromLegacySchema() {
3164
+ const fullTableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3165
+ const fullVersionsTableName = getTableName2({
3166
+ indexName: storage.TABLE_AGENT_VERSIONS,
3167
+ schemaName: getSchemaName2(this.#schema)
3168
+ });
3169
+ const legacyTableName = getTableName2({
3170
+ indexName: `${storage.TABLE_AGENTS}_legacy`,
3171
+ schemaName: getSchemaName2(this.#schema)
3172
+ });
3173
+ const hasLegacyColumns = await this.#db.hasColumn(storage.TABLE_AGENTS, "name");
3174
+ if (hasLegacyColumns) {
3175
+ await this.#db.client.none(`ALTER TABLE ${fullTableName} RENAME TO "${storage.TABLE_AGENTS}_legacy"`);
3176
+ await this.#db.client.none(`DROP TABLE IF EXISTS ${fullVersionsTableName}`);
3177
+ }
3178
+ const legacyExists = await this.#db.hasColumn(`${storage.TABLE_AGENTS}_legacy`, "name");
3179
+ if (!legacyExists) return;
3180
+ const oldAgents = await this.#db.client.manyOrNone(`SELECT * FROM ${legacyTableName}`);
3181
+ await this.#db.createTable({ tableName: storage.TABLE_AGENTS, schema: storage.TABLE_SCHEMAS[storage.TABLE_AGENTS] });
3182
+ await this.#db.createTable({ tableName: storage.TABLE_AGENT_VERSIONS, schema: storage.TABLE_SCHEMAS[storage.TABLE_AGENT_VERSIONS] });
3183
+ for (const row of oldAgents) {
3184
+ const agentId = row.id;
3185
+ if (!agentId) continue;
3186
+ const versionId = crypto.randomUUID();
3187
+ const now = /* @__PURE__ */ new Date();
3188
+ await this.#db.client.none(
3189
+ `INSERT INTO ${fullTableName} (id, status, "activeVersionId", "authorId", metadata, "createdAt", "updatedAt")
3190
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
3191
+ ON CONFLICT (id) DO NOTHING`,
3192
+ [
3193
+ agentId,
3194
+ "published",
3195
+ versionId,
3196
+ row.ownerId ?? row.authorId ?? null,
3197
+ row.metadata ? JSON.stringify(row.metadata) : null,
3198
+ row.createdAt ?? now,
3199
+ row.updatedAt ?? now
3200
+ ]
3201
+ );
3202
+ await this.#db.client.none(
3203
+ `INSERT INTO ${fullVersionsTableName}
3204
+ (id, "agentId", "versionNumber", name, description, instructions, model, tools,
3205
+ "defaultOptions", workflows, agents, "integrationTools", "inputProcessors",
3206
+ "outputProcessors", memory, scorers, "changedFields", "changeMessage", "createdAt")
3207
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19)
3208
+ ON CONFLICT (id) DO NOTHING`,
3209
+ [
3210
+ versionId,
3211
+ agentId,
3212
+ 1,
3213
+ row.name ?? agentId,
3214
+ row.description ?? null,
3215
+ row.instructions ?? "",
3216
+ row.model ? JSON.stringify(row.model) : "{}",
3217
+ row.tools ? JSON.stringify(row.tools) : null,
3218
+ row.defaultOptions ? JSON.stringify(row.defaultOptions) : null,
3219
+ row.workflows ? JSON.stringify(row.workflows) : null,
3220
+ row.agents ? JSON.stringify(row.agents) : null,
3221
+ row.integrationTools ? JSON.stringify(row.integrationTools) : null,
3222
+ row.inputProcessors ? JSON.stringify(row.inputProcessors) : null,
3223
+ row.outputProcessors ? JSON.stringify(row.outputProcessors) : null,
3224
+ row.memory ? JSON.stringify(row.memory) : null,
3225
+ row.scorers ? JSON.stringify(row.scorers) : null,
3226
+ null,
3227
+ "Migrated from legacy schema",
3228
+ row.createdAt ?? now
3229
+ ]
3230
+ );
3231
+ }
3232
+ await this.#db.client.none(`DROP TABLE IF EXISTS ${legacyTableName}`);
3233
+ }
3234
+ /**
3235
+ * Migrates the agent_versions table from the old snapshot-based schema (single `snapshot` JSON column)
3236
+ * to the new flat schema (individual config columns). This handles the case where the agents table
3237
+ * was already migrated but the versions table still has the old schema.
3238
+ */
3239
+ async #migrateVersionsSchema() {
3240
+ const hasSnapshotColumn = await this.#db.hasColumn(storage.TABLE_AGENT_VERSIONS, "snapshot");
3241
+ if (!hasSnapshotColumn) return;
3242
+ const fullVersionsTableName = getTableName2({
3243
+ indexName: storage.TABLE_AGENT_VERSIONS,
3244
+ schemaName: getSchemaName2(this.#schema)
3245
+ });
3246
+ const legacyTableName = getTableName2({
3247
+ indexName: `${storage.TABLE_AGENTS}_legacy`,
3248
+ schemaName: getSchemaName2(this.#schema)
3249
+ });
3250
+ await this.#db.client.none(`DROP TABLE IF EXISTS ${fullVersionsTableName}`);
3251
+ await this.#db.client.none(`DROP TABLE IF EXISTS ${legacyTableName}`);
3252
+ }
3253
+ /**
3254
+ * Removes stale draft agent records that have no activeVersionId.
3255
+ * These are left behind when createAgent partially fails (inserts thin record
3256
+ * but fails to create the version due to schema mismatch).
3257
+ */
3258
+ async #cleanupStaleDrafts() {
3259
+ try {
3260
+ const fullTableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3261
+ await this.#db.client.none(`DELETE FROM ${fullTableName} WHERE status = 'draft' AND "activeVersionId" IS NULL`);
3262
+ } catch {
3263
+ }
3150
3264
  }
3151
3265
  /**
3152
3266
  * Creates custom user-defined indexes for this domain's tables.
@@ -3194,22 +3308,10 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3194
3308
  parseRow(row) {
3195
3309
  return {
3196
3310
  id: row.id,
3197
- name: row.name,
3198
- description: row.description,
3199
- instructions: row.instructions,
3200
- model: this.parseJson(row.model, "model"),
3201
- tools: this.parseJson(row.tools, "tools"),
3202
- defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
3203
- workflows: this.parseJson(row.workflows, "workflows"),
3204
- agents: this.parseJson(row.agents, "agents"),
3205
- integrationTools: this.parseJson(row.integrationTools, "integrationTools"),
3206
- inputProcessors: this.parseJson(row.inputProcessors, "inputProcessors"),
3207
- outputProcessors: this.parseJson(row.outputProcessors, "outputProcessors"),
3208
- memory: this.parseJson(row.memory, "memory"),
3209
- scorers: this.parseJson(row.scorers, "scorers"),
3210
- metadata: this.parseJson(row.metadata, "metadata"),
3211
- ownerId: row.ownerId,
3311
+ status: row.status,
3212
3312
  activeVersionId: row.activeVersionId,
3313
+ authorId: row.authorId,
3314
+ metadata: this.parseJson(row.metadata, "metadata"),
3213
3315
  createdAt: row.createdAtZ || row.createdAt,
3214
3316
  updatedAt: row.updatedAtZ || row.updatedAt
3215
3317
  };
@@ -3236,43 +3338,48 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3236
3338
  }
3237
3339
  async createAgent({ agent }) {
3238
3340
  try {
3239
- const tableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3341
+ const agentsTable = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3240
3342
  const now = /* @__PURE__ */ new Date();
3241
3343
  const nowIso = now.toISOString();
3242
3344
  await this.#db.client.none(
3243
- `INSERT INTO ${tableName} (
3244
- id, name, description, instructions, model, tools,
3245
- "defaultOptions", workflows, agents, "integrationTools",
3246
- "inputProcessors", "outputProcessors", memory, scorers, metadata,
3247
- "ownerId", "activeVersionId",
3345
+ `INSERT INTO ${agentsTable} (
3346
+ id, status, "authorId", metadata,
3347
+ "activeVersionId",
3248
3348
  "createdAt", "createdAtZ", "updatedAt", "updatedAtZ"
3249
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)`,
3349
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
3250
3350
  [
3251
3351
  agent.id,
3252
- agent.name,
3253
- agent.description ?? null,
3254
- agent.instructions,
3255
- JSON.stringify(agent.model),
3256
- agent.tools ? JSON.stringify(agent.tools) : null,
3257
- agent.defaultOptions ? JSON.stringify(agent.defaultOptions) : null,
3258
- agent.workflows ? JSON.stringify(agent.workflows) : null,
3259
- agent.agents ? JSON.stringify(agent.agents) : null,
3260
- agent.integrationTools ? JSON.stringify(agent.integrationTools) : null,
3261
- agent.inputProcessors ? JSON.stringify(agent.inputProcessors) : null,
3262
- agent.outputProcessors ? JSON.stringify(agent.outputProcessors) : null,
3263
- agent.memory ? JSON.stringify(agent.memory) : null,
3264
- agent.scorers ? JSON.stringify(agent.scorers) : null,
3352
+ "draft",
3353
+ agent.authorId ?? null,
3265
3354
  agent.metadata ? JSON.stringify(agent.metadata) : null,
3266
- agent.ownerId ?? null,
3267
- agent.activeVersionId ?? null,
3355
+ null,
3356
+ // activeVersionId starts as null
3268
3357
  nowIso,
3269
3358
  nowIso,
3270
3359
  nowIso,
3271
3360
  nowIso
3272
3361
  ]
3273
3362
  );
3363
+ const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = agent;
3364
+ const versionId = crypto.randomUUID();
3365
+ await this.createVersion({
3366
+ id: versionId,
3367
+ agentId: agent.id,
3368
+ versionNumber: 1,
3369
+ ...snapshotConfig,
3370
+ changedFields: Object.keys(snapshotConfig),
3371
+ changeMessage: "Initial version"
3372
+ });
3373
+ await this.#db.client.none(
3374
+ `UPDATE ${agentsTable} SET "activeVersionId" = $1, status = $2, "updatedAt" = $3, "updatedAtZ" = $4 WHERE id = $5`,
3375
+ [versionId, "published", nowIso, nowIso, agent.id]
3376
+ );
3274
3377
  return {
3275
- ...agent,
3378
+ id: agent.id,
3379
+ status: "published",
3380
+ activeVersionId: versionId,
3381
+ authorId: agent.authorId,
3382
+ metadata: agent.metadata,
3276
3383
  createdAt: now,
3277
3384
  updatedAt: now
3278
3385
  };
@@ -3304,65 +3411,15 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3304
3411
  const setClauses = [];
3305
3412
  const values = [];
3306
3413
  let paramIndex = 1;
3307
- if (updates.name !== void 0) {
3308
- setClauses.push(`name = $${paramIndex++}`);
3309
- values.push(updates.name);
3310
- }
3311
- if (updates.description !== void 0) {
3312
- setClauses.push(`description = $${paramIndex++}`);
3313
- values.push(updates.description);
3314
- }
3315
- if (updates.instructions !== void 0) {
3316
- setClauses.push(`instructions = $${paramIndex++}`);
3317
- values.push(updates.instructions);
3318
- }
3319
- if (updates.model !== void 0) {
3320
- setClauses.push(`model = $${paramIndex++}`);
3321
- values.push(JSON.stringify(updates.model));
3322
- }
3323
- if (updates.tools !== void 0) {
3324
- setClauses.push(`tools = $${paramIndex++}`);
3325
- values.push(JSON.stringify(updates.tools));
3326
- }
3327
- if (updates.defaultOptions !== void 0) {
3328
- setClauses.push(`"defaultOptions" = $${paramIndex++}`);
3329
- values.push(JSON.stringify(updates.defaultOptions));
3330
- }
3331
- if (updates.workflows !== void 0) {
3332
- setClauses.push(`workflows = $${paramIndex++}`);
3333
- values.push(JSON.stringify(updates.workflows));
3334
- }
3335
- if (updates.agents !== void 0) {
3336
- setClauses.push(`agents = $${paramIndex++}`);
3337
- values.push(JSON.stringify(updates.agents));
3338
- }
3339
- if (updates.inputProcessors !== void 0) {
3340
- setClauses.push(`"inputProcessors" = $${paramIndex++}`);
3341
- values.push(JSON.stringify(updates.inputProcessors));
3342
- }
3343
- if (updates.outputProcessors !== void 0) {
3344
- setClauses.push(`"outputProcessors" = $${paramIndex++}`);
3345
- values.push(JSON.stringify(updates.outputProcessors));
3346
- }
3347
- if (updates.memory !== void 0) {
3348
- setClauses.push(`memory = $${paramIndex++}`);
3349
- values.push(JSON.stringify(updates.memory));
3350
- }
3351
- if (updates.scorers !== void 0) {
3352
- setClauses.push(`scorers = $${paramIndex++}`);
3353
- values.push(JSON.stringify(updates.scorers));
3354
- }
3355
- if (updates.integrationTools !== void 0) {
3356
- setClauses.push(`"integrationTools" = $${paramIndex++}`);
3357
- values.push(JSON.stringify(updates.integrationTools));
3358
- }
3359
- if (updates.ownerId !== void 0) {
3360
- setClauses.push(`"ownerId" = $${paramIndex++}`);
3361
- values.push(updates.ownerId);
3414
+ if (updates.authorId !== void 0) {
3415
+ setClauses.push(`"authorId" = $${paramIndex++}`);
3416
+ values.push(updates.authorId);
3362
3417
  }
3363
3418
  if (updates.activeVersionId !== void 0) {
3364
3419
  setClauses.push(`"activeVersionId" = $${paramIndex++}`);
3365
3420
  values.push(updates.activeVersionId);
3421
+ setClauses.push(`status = $${paramIndex++}`);
3422
+ values.push("published");
3366
3423
  }
3367
3424
  if (updates.metadata !== void 0) {
3368
3425
  const mergedMetadata = { ...existingAgent.metadata, ...updates.metadata };
@@ -3487,14 +3544,30 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3487
3544
  const nowIso = now.toISOString();
3488
3545
  await this.#db.client.none(
3489
3546
  `INSERT INTO ${tableName} (
3490
- id, "agentId", "versionNumber", name, snapshot, "changedFields", "changeMessage", "createdAt", "createdAtZ"
3491
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
3547
+ id, "agentId", "versionNumber",
3548
+ name, description, instructions, model, tools,
3549
+ "defaultOptions", workflows, agents, "integrationTools",
3550
+ "inputProcessors", "outputProcessors", memory, scorers,
3551
+ "changedFields", "changeMessage",
3552
+ "createdAt", "createdAtZ"
3553
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)`,
3492
3554
  [
3493
3555
  input.id,
3494
3556
  input.agentId,
3495
3557
  input.versionNumber,
3496
- input.name ?? null,
3497
- JSON.stringify(input.snapshot),
3558
+ input.name,
3559
+ input.description ?? null,
3560
+ input.instructions,
3561
+ JSON.stringify(input.model),
3562
+ input.tools ? JSON.stringify(input.tools) : null,
3563
+ input.defaultOptions ? JSON.stringify(input.defaultOptions) : null,
3564
+ input.workflows ? JSON.stringify(input.workflows) : null,
3565
+ input.agents ? JSON.stringify(input.agents) : null,
3566
+ input.integrationTools ? JSON.stringify(input.integrationTools) : null,
3567
+ input.inputProcessors ? JSON.stringify(input.inputProcessors) : null,
3568
+ input.outputProcessors ? JSON.stringify(input.outputProcessors) : null,
3569
+ input.memory ? JSON.stringify(input.memory) : null,
3570
+ input.scorers ? JSON.stringify(input.scorers) : null,
3498
3571
  input.changedFields ? JSON.stringify(input.changedFields) : null,
3499
3572
  input.changeMessage ?? null,
3500
3573
  nowIso,
@@ -3699,7 +3772,18 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3699
3772
  agentId: row.agentId,
3700
3773
  versionNumber: row.versionNumber,
3701
3774
  name: row.name,
3702
- snapshot: this.parseJson(row.snapshot, "snapshot"),
3775
+ description: row.description,
3776
+ instructions: row.instructions,
3777
+ model: this.parseJson(row.model, "model"),
3778
+ tools: this.parseJson(row.tools, "tools"),
3779
+ defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
3780
+ workflows: this.parseJson(row.workflows, "workflows"),
3781
+ agents: this.parseJson(row.agents, "agents"),
3782
+ integrationTools: this.parseJson(row.integrationTools, "integrationTools"),
3783
+ inputProcessors: this.parseJson(row.inputProcessors, "inputProcessors"),
3784
+ outputProcessors: this.parseJson(row.outputProcessors, "outputProcessors"),
3785
+ memory: this.parseJson(row.memory, "memory"),
3786
+ scorers: this.parseJson(row.scorers, "scorers"),
3703
3787
  changedFields: this.parseJson(row.changedFields, "changedFields"),
3704
3788
  changeMessage: row.changeMessage,
3705
3789
  createdAt: row.createdAtZ || row.createdAt