@mastra/pg 1.24.1-alpha.0 → 1.25.0-alpha.1

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.
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.24.1-alpha.0"
6
+ version: "1.25.0-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.24.1-alpha.0",
2
+ "version": "1.25.0-alpha.1",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -39,7 +39,7 @@ export const agent = new Agent({
39
39
 
40
40
  **options** (`MemoryConfig`): Memory configuration options.
41
41
 
42
- **options.lastMessages** (`number | false`): Number of most recent messages to include in context. Set to false to disable the message history feature entirely (messages are not loaded into context or saved). Use Number.MAX\_SAFE\_INTEGER to retrieve all messages with no limit. To load messages without saving new ones, use the readOnly option.
42
+ **options.lastMessages** (`number | false`): Number of most recent messages to include in context. Set to false to disable the message history feature entirely (messages are not loaded into context or saved). Use Number.MAX\_SAFE\_INTEGER to retrieve all messages with no limit. To load messages without saving new ones, use the readOnly option. The window slides forward on every request, so once a thread exceeds the limit, each turn invalidates the provider prompt cache. For long-running conversations, use Observational Memory instead.
43
43
 
44
44
  **options.readOnly** (`boolean`): When true, prevents memory from saving new messages and provides working memory as read-only context (without the updateWorkingMemory tool). Useful for read-only operations like previews, internal routing agents, or sub agents that should reference but not modify memory.
45
45
 
@@ -147,5 +147,6 @@ export const agent = new Agent({
147
147
  - [listThreads](https://mastra.ai/reference/memory/listThreads)
148
148
  - [deleteMessages](https://mastra.ai/reference/memory/deleteMessages)
149
149
  - [cloneThread](https://mastra.ai/reference/memory/cloneThread)
150
+ - [copyThread](https://mastra.ai/reference/memory/copyThread)
150
151
  - [settled](https://mastra.ai/reference/memory/settled)
151
152
  - [Clone Utility Methods](https://mastra.ai/reference/memory/clone-utilities)
package/dist/index.cjs CHANGED
@@ -10692,6 +10692,57 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
10692
10692
  }, error);
10693
10693
  }
10694
10694
  }
10695
+ /**
10696
+ * Atomically reassign a thread and all of its messages to a different resource.
10697
+ *
10698
+ * Runs inside a single transaction and takes a `SELECT ... FOR UPDATE` row lock on the
10699
+ * thread, so overlapping transfers of the same thread serialize and can never interleave
10700
+ * the thread update with the message update. Either both the thread and every message move
10701
+ * to the new resource, or neither does — there is no split-ownership window. The thread's
10702
+ * `createdAt` is preserved. Callers are responsible for authorizing the reassignment.
10703
+ */
10704
+ async updateThreadResourceId({ threadId, resourceId }) {
10705
+ const threadsTable = getTableName$3({
10706
+ indexName: _mastra_core_storage.TABLE_THREADS,
10707
+ schemaName: getSchemaName$3(this.#schema)
10708
+ });
10709
+ const messagesTable = getTableName$3({
10710
+ indexName: _mastra_core_storage.TABLE_MESSAGES,
10711
+ schemaName: getSchemaName$3(this.#schema)
10712
+ });
10713
+ try {
10714
+ return await this.#db.client.tx(async (t) => {
10715
+ const thread = await t.oneOrNone(`SELECT * FROM ${threadsTable} WHERE id = $1 FOR UPDATE`, [threadId]);
10716
+ if (!thread) throw new Error(`Thread "${threadId}" not found`);
10717
+ const normalized = {
10718
+ id: thread.id,
10719
+ resourceId: thread.resourceId,
10720
+ title: thread.title,
10721
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
10722
+ createdAt: thread.createdAtZ || thread.createdAt,
10723
+ updatedAt: thread.updatedAtZ || thread.updatedAt
10724
+ };
10725
+ if (thread.resourceId === resourceId) return normalized;
10726
+ await t.none(`UPDATE ${threadsTable} SET "resourceId" = $1, "updatedAt" = NOW(), "updatedAtZ" = NOW() WHERE id = $2`, [resourceId, threadId]);
10727
+ await t.none(`UPDATE ${messagesTable} SET "resourceId" = $1 WHERE thread_id = $2`, [resourceId, threadId]);
10728
+ return {
10729
+ ...normalized,
10730
+ resourceId,
10731
+ updatedAt: /* @__PURE__ */ new Date()
10732
+ };
10733
+ });
10734
+ } catch (error) {
10735
+ throw new _mastra_core_error.MastraError({
10736
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_THREAD_RESOURCE_ID", "FAILED"),
10737
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
10738
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
10739
+ details: {
10740
+ threadId,
10741
+ resourceId
10742
+ }
10743
+ }, error);
10744
+ }
10745
+ }
10695
10746
  async listThreads(args) {
10696
10747
  const { page = 0, perPage: perPageInput, orderBy, filter } = args;
10697
10748
  try {
@@ -11696,7 +11747,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11696
11747
  await this.#db.client.none(`UPDATE ${tableName} SET ${updates.join(", ")} WHERE id = $${paramIndex}`, values);
11697
11748
  return updatedResource;
11698
11749
  }
11699
- async cloneThread(args) {
11750
+ async copyThread(args) {
11700
11751
  const { sourceThreadId, newThreadId: providedThreadId, resourceId, title, metadata, options } = args;
11701
11752
  const sourceThread = await this.#getThreadById(this.#db.client, { threadId: sourceThreadId });
11702
11753
  if (!sourceThread) throw new _mastra_core_error.MastraError({
@@ -11722,10 +11773,9 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11722
11773
  indexName: _mastra_core_storage.TABLE_MESSAGES,
11723
11774
  schemaName: getSchemaName$3(this.#schema)
11724
11775
  });
11725
- const hydrateMessages = options?.hydrateMessages ?? true;
11726
11776
  try {
11727
11777
  return await this.#db.client.tx(async (t) => {
11728
- let messageQuery = `SELECT ${hydrateMessages ? `id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"` : `id, "createdAt"`}
11778
+ let messageQuery = `SELECT id, "createdAt"
11729
11779
  FROM ${messageTableName} WHERE thread_id = $1`;
11730
11780
  const messageParams = [sourceThreadId];
11731
11781
  let paramIndex = 2;
@@ -11786,54 +11836,23 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11786
11836
  nowStr,
11787
11837
  nowStr
11788
11838
  ]);
11789
- const clonedMessages = [];
11790
11839
  const messageIdMap = {};
11791
11840
  const targetResourceId = resourceId || sourceThread.resourceId;
11792
11841
  for (const sourceMsg of sourceMessages) {
11793
11842
  const newMessageId = crypto.randomUUID();
11794
11843
  messageIdMap[sourceMsg.id] = newMessageId;
11795
- if (!hydrateMessages) {
11796
- const insertResult = await t.query(`INSERT INTO ${messageTableName} (id, thread_id, content, "createdAt", "createdAtZ", role, type, "resourceId")
11797
- SELECT $1, $2, content, "createdAt", "createdAtZ", role, type, $3
11798
- FROM ${messageTableName} WHERE id = $4`, [
11799
- newMessageId,
11800
- newThreadId,
11801
- targetResourceId,
11802
- sourceMsg.id
11803
- ]);
11804
- if (insertResult.rowCount !== 1) throw new Error(`Failed to clone message ${sourceMsg.id}: expected 1 row copied but got ${insertResult.rowCount}`);
11805
- continue;
11806
- }
11807
- const normalizedMsg = this.normalizeMessageRow(sourceMsg);
11808
- let parsedContent = normalizedMsg.content;
11809
- try {
11810
- parsedContent = JSON.parse(normalizedMsg.content);
11811
- } catch {}
11812
- const createdAt = toUtcISOString(new Date(normalizedMsg.createdAt));
11813
- await t.none(`INSERT INTO ${messageTableName} (id, thread_id, content, "createdAt", "createdAtZ", role, type, "resourceId")
11814
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
11844
+ const insertResult = await t.query(`INSERT INTO ${messageTableName} (id, thread_id, content, "createdAt", "createdAtZ", role, type, "resourceId")
11845
+ SELECT $1, $2, content, "createdAt", "createdAtZ", role, type, $3
11846
+ FROM ${messageTableName} WHERE id = $4`, [
11815
11847
  newMessageId,
11816
11848
  newThreadId,
11817
- typeof normalizedMsg.content === "string" ? normalizedMsg.content : JSON.stringify(normalizedMsg.content),
11818
- createdAt,
11819
- createdAt,
11820
- normalizedMsg.role,
11821
- normalizedMsg.type || "v2",
11822
- targetResourceId
11849
+ targetResourceId,
11850
+ sourceMsg.id
11823
11851
  ]);
11824
- clonedMessages.push({
11825
- id: newMessageId,
11826
- threadId: newThreadId,
11827
- content: parsedContent,
11828
- role: normalizedMsg.role,
11829
- type: normalizedMsg.type,
11830
- createdAt: new Date(normalizedMsg.createdAt),
11831
- resourceId: targetResourceId
11832
- });
11852
+ if (insertResult.rowCount !== 1) throw new Error(`Failed to copy message ${sourceMsg.id}: expected 1 row copied but got ${insertResult.rowCount}`);
11833
11853
  }
11834
11854
  return {
11835
11855
  thread: newThread,
11836
- clonedMessages,
11837
11856
  messageIdMap
11838
11857
  };
11839
11858
  });