@mastra/pg 1.26.0-alpha.3 → 1.26.0-alpha.4

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.26.0-alpha.3"
6
+ version: "1.26.0-alpha.4"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.26.0-alpha.3",
2
+ "version": "1.26.0-alpha.4",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -51,7 +51,7 @@ export const agent = new Agent({
51
51
 
52
52
  **options.observationalMemory** (`boolean | ObservationalMemoryOptions`): Enable Observational Memory for long-context agentic memory. Set to true for defaults, or pass a config object to customize token budgets, models, and scope. See Observational Memory reference for configuration details.
53
53
 
54
- **options.generateTitle** (`boolean | { model: DynamicArgument<MastraLanguageModel>; instructions?: DynamicArgument<string> }`): Controls automatic thread title generation from the conversation transcript. Can be a boolean or an object with custom model and instructions.
54
+ **options.generateTitle** (`boolean | { model?: DynamicArgument<MastraModelConfig>; instructions?: DynamicArgument<string>; minMessages?: number; emitEvent?: boolean }`): Controls automatic thread title generation from the conversation transcript. Accepts a boolean or an object with a custom model (any MastraModelConfig: a model instance, a "provider/model" ID, or an OpenAI-compatible config; defaults to the agent's own model), custom instructions, a minimum message count, and emitEvent. With emitEvent: true the run's stream waits for the title and emits it as a transient data-thread-title chunk before finish (durable and evented agents persist the title but don't emit the chunk yet).
55
55
 
56
56
  ## Returns
57
57
 
package/dist/index.cjs CHANGED
@@ -2360,6 +2360,8 @@ var RoutingDbClient = class {
2360
2360
  return this.active.tx(callback);
2361
2361
  }
2362
2362
  };
2363
+ /** Bytes reserved for the `_xxxxxxxx` collision suffix when hashWhenTruncated applies. */
2364
+ const TRUNCATION_HASH_SUFFIX_LENGTH = 9;
2363
2365
  function truncateIdentifier(value, maxLength = 63) {
2364
2366
  if (maxLength <= 0) return "";
2365
2367
  if (Buffer.byteLength(value, "utf-8") <= maxLength) return value;
@@ -2380,9 +2382,25 @@ function truncateIdentifier(value, maxLength = 63) {
2380
2382
  * in system catalogs (pg_constraint.conname, pg_indexes.indexname, etc.).
2381
2383
  * Without this normalisation, runtime lookups that compare a mixed-case name
2382
2384
  * against the catalog would silently fail.
2385
+ *
2386
+ * With `hashWhenTruncated`, a name that exceeds the limit is truncated further
2387
+ * to make room for `_` + 8 hex chars of the full name's sha256. Plain
2388
+ * truncation cuts the tail, so two names sharing a long `<schema>_<prefix>`
2389
+ * collapse to the same identifier and `CREATE INDEX IF NOT EXISTS` (which
2390
+ * matches by name only) silently skips the second one. The suffix is
2391
+ * deterministic, so creation, warm-init snapshot checks, and DDL export all
2392
+ * agree on the same name. Opt-in because renaming already-released constraint
2393
+ * names would orphan the existing objects in deployed catalogs.
2383
2394
  */
2384
- function buildConstraintName({ baseName, schemaName, maxLength = 63 }) {
2385
- return truncateIdentifier(`${schemaName ? `${schemaName}_` : ""}${baseName}`.toLowerCase(), maxLength);
2395
+ function buildConstraintName({ baseName, schemaName, maxLength = 63, hashWhenTruncated = false }) {
2396
+ const fullName = `${schemaName ? `${schemaName}_` : ""}${baseName}`.toLowerCase();
2397
+ if (hashWhenTruncated && Buffer.byteLength(fullName, "utf-8") > maxLength) {
2398
+ const suffixLength = Math.min(TRUNCATION_HASH_SUFFIX_LENGTH, maxLength);
2399
+ if (suffixLength < 2) return truncateIdentifier(fullName, maxLength);
2400
+ const hash = (0, crypto$1.createHash)("sha256").update(fullName).digest("hex").slice(0, suffixLength - 1);
2401
+ return `${truncateIdentifier(fullName, maxLength - suffixLength)}_${hash}`;
2402
+ }
2403
+ return truncateIdentifier(fullName, maxLength);
2386
2404
  }
2387
2405
  //#endregion
2388
2406
  //#region src/storage/db/pg-errors.ts
@@ -22317,6 +22335,7 @@ const WORKFLOW_SNAPSHOT_STATUS_INDEX = "mastra_workflow_snapshot_name_status_cre
22317
22335
  * Schema-prefixed name of the status index, lowercased and truncated the same way Postgres
22318
22336
  * stores it, so the init snapshot's index set answers "does it exist?" without a probe or a
22319
22337
  * no-op `CREATE INDEX` (schema-prefixed names routinely exceed the 63-byte limit).
22338
+ * Exported for tests.
22320
22339
  */
22321
22340
  function workflowSnapshotStatusIndexName(schemaName) {
22322
22341
  return buildConstraintName({
@@ -22334,6 +22353,48 @@ function workflowSnapshotStatusIndexSQL(indexName, schemaName) {
22334
22353
  schemaName: getSchemaName(schemaName)
22335
22354
  })} (workflow_name, (snapshot ->> 'status'), "createdAt" DESC)`;
22336
22355
  }
22356
+ /** Base name (before any schema prefix) of the expression index backing the threadId filter. */
22357
+ const WORKFLOW_SNAPSHOT_THREAD_ID_INDEX = "mastra_workflow_snapshot_threadid_idx";
22358
+ /**
22359
+ * Schema-prefixed name of the threadId index (see workflowSnapshotStatusIndexName).
22360
+ *
22361
+ * Unlike the status index, truncation appends a collision hash: both index names share the
22362
+ * long `<schema>_mastra_workflow_snapshot_` prefix, so with a schema name of 37+ bytes plain
22363
+ * truncation collapses them to the same 63-byte identifier and `CREATE INDEX IF NOT EXISTS`
22364
+ * silently skips this index. The status index keeps plain truncation because its truncated
22365
+ * name already exists in deployed catalogs; this index is new and free to adopt the rule.
22366
+ * Exported for tests.
22367
+ */
22368
+ function workflowSnapshotThreadIdIndexName(schemaName) {
22369
+ return buildConstraintName({
22370
+ baseName: WORKFLOW_SNAPSHOT_THREAD_ID_INDEX,
22371
+ schemaName: schemaName && schemaName !== "public" ? schemaName : void 0,
22372
+ hashWhenTruncated: true
22373
+ });
22374
+ }
22375
+ /**
22376
+ * Expression extracting the thread id embedded in a snapshot (jsonb columns only). Mirrors
22377
+ * the canonical extraction in `@mastra/core` (`getSnapshotMemoryInfo`), which reads one of
22378
+ * two layouts:
22379
+ * 1. agentic-loop: `context.<suspended step>.suspendPayload.__streamState.messageList.memoryInfo.threadId`
22380
+ * 2. durable loop: `context.input.messageListState.memoryInfo.threadId`
22381
+ *
22382
+ * `jsonb_path_query_first(jsonb, jsonpath)` is IMMUTABLE, so the expression is valid in an
22383
+ * expression index. The WHERE clause in listWorkflowRuns() must use this exact expression
22384
+ * text so the planner can match it against the index. If the snapshot layout changes in
22385
+ * core, this expression must be updated in lockstep or it will wrongly exclude rows.
22386
+ */
22387
+ const WORKFLOW_SNAPSHOT_THREAD_ID_EXPR = `COALESCE(jsonb_path_query_first(snapshot, '$.context.* ? (@.status == "suspended").suspendPayload.__streamState.messageList.memoryInfo.threadId') #>> '{}', snapshot #>> '{context,input,messageListState,memoryInfo,threadId}')`;
22388
+ /**
22389
+ * Expression index on the snapshot-embedded thread id so listWorkflowRuns() threadId filters
22390
+ * (Agent.listSuspendedRuns) can use an index instead of detoasting every snapshot.
22391
+ */
22392
+ function workflowSnapshotThreadIdIndexSQL(indexName, schemaName) {
22393
+ return `CREATE INDEX IF NOT EXISTS "${indexName}" ON ${getTableName({
22394
+ indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
22395
+ schemaName: getSchemaName(schemaName)
22396
+ })} ((${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR}))`;
22397
+ }
22337
22398
  var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorage {
22338
22399
  #db;
22339
22400
  #schema;
@@ -22406,6 +22467,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
22406
22467
  }));
22407
22468
  for (const idx of WorkflowsPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
22408
22469
  statements.push(`${workflowSnapshotStatusIndexSQL(workflowSnapshotStatusIndexName(parsedSchema), schemaName)};`);
22470
+ statements.push(`${workflowSnapshotThreadIdIndexSQL(workflowSnapshotThreadIdIndexName(parsedSchema), schemaName)};`);
22409
22471
  return statements;
22410
22472
  }
22411
22473
  /**
@@ -22432,6 +22494,12 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
22432
22494
  } catch (error) {
22433
22495
  this.logger?.warn?.(`Failed to create index ${indexName}:`, error);
22434
22496
  }
22497
+ const threadIdIndexName = workflowSnapshotThreadIdIndexName(this.#schema);
22498
+ try {
22499
+ await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema));
22500
+ } catch (error) {
22501
+ this.logger?.warn?.(`Failed to create index ${threadIdIndexName}:`, error);
22502
+ }
22435
22503
  }
22436
22504
  async init() {
22437
22505
  await this.#db.createTable({
@@ -22712,7 +22780,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
22712
22780
  }, error);
22713
22781
  }
22714
22782
  }
22715
- async listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId, status } = {}) {
22783
+ async listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId, threadId, status } = {}) {
22716
22784
  try {
22717
22785
  const conditions = [];
22718
22786
  const values = [];
@@ -22733,6 +22801,11 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
22733
22801
  values.push(resourceId);
22734
22802
  paramIndex++;
22735
22803
  } else this.logger?.warn?.(`[${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
22804
+ if (threadId) if (await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb") {
22805
+ conditions.push(`${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR} = $${paramIndex}`);
22806
+ values.push(threadId);
22807
+ paramIndex++;
22808
+ } else this.logger?.warn?.(`[${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}] snapshot column is not jsonb. Skipping threadId filter.`);
22736
22809
  if (fromDate) {
22737
22810
  conditions.push(`"createdAt" >= $${paramIndex}`);
22738
22811
  values.push(fromDate);