@mastra/mysql 0.13.0 → 0.13.1-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/_types/@internal_core/dist/memory/index.d.ts +102 -0
- package/dist/_types/@internal_core/package.json +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +544 -346
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +544 -346
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +36 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,7 @@ let _mastra_core_error = require("@mastra/core/error");
|
|
|
3
3
|
let _mastra_core_storage = require("@mastra/core/storage");
|
|
4
4
|
let mysql2_promise = require("mysql2/promise");
|
|
5
5
|
let _mastra_core_utils = require("@mastra/core/utils");
|
|
6
|
+
let crypto$1 = require("crypto");
|
|
6
7
|
let _mastra_core_agent = require("@mastra/core/agent");
|
|
7
8
|
let _mastra_core_storage_domains_skills = require("@mastra/core/storage/domains/skills");
|
|
8
9
|
//#region src/storage/db/schema-snapshot.ts
|
|
@@ -6022,9 +6023,108 @@ var MCPServersMySQL = class MCPServersMySQL extends _mastra_core_storage.MCPServ
|
|
|
6022
6023
|
}
|
|
6023
6024
|
};
|
|
6024
6025
|
//#endregion
|
|
6026
|
+
//#region ../../packages/_internal-core/dist/memory/index.js
|
|
6027
|
+
/**
|
|
6028
|
+
* Shared rules for observational memory lifecycle writes. Every storage adapter applies
|
|
6029
|
+
* these inside its own atomic section (lock, transaction, or conditional update), so the
|
|
6030
|
+
* decisions are identical across adapters.
|
|
6031
|
+
*/
|
|
6032
|
+
/**
|
|
6033
|
+
* Observational memory columns that lifecycle writes depend on. Storage adapters merge these
|
|
6034
|
+
* into the table schema they get from `@mastra/core`, so the columns exist even when the
|
|
6035
|
+
* installed core predates them.
|
|
6036
|
+
*/
|
|
6037
|
+
const OBSERVATIONAL_MEMORY_LIFECYCLE_COLUMNS = { supersededBy: {
|
|
6038
|
+
type: "text",
|
|
6039
|
+
nullable: true
|
|
6040
|
+
} };
|
|
6041
|
+
/** `schema` plus {@link OBSERVATIONAL_MEMORY_LIFECYCLE_COLUMNS}; `undefined` when `schema` is. */
|
|
6042
|
+
function withObservationalMemoryLifecycleColumns(schema) {
|
|
6043
|
+
return schema ? {
|
|
6044
|
+
...schema,
|
|
6045
|
+
...OBSERVATIONAL_MEMORY_LIFECYCLE_COLUMNS
|
|
6046
|
+
} : void 0;
|
|
6047
|
+
}
|
|
6048
|
+
/**
|
|
6049
|
+
* A buffered chunk stores `lastObservedAt = max message time + 1ms`. It is wholly covered
|
|
6050
|
+
* (every message it observed is already at or before the cursor) iff
|
|
6051
|
+
* `cursor >= chunk.lastObservedAt - 1ms`.
|
|
6052
|
+
*/
|
|
6053
|
+
function isBufferedChunkCoveredByCursor(chunkLastObservedAt, cursor) {
|
|
6054
|
+
if (!cursor) return false;
|
|
6055
|
+
return new Date(cursor).getTime() >= new Date(chunkLastObservedAt).getTime() - 1;
|
|
6056
|
+
}
|
|
6057
|
+
/** The later of two cursors; `undefined` only when both are absent. */
|
|
6058
|
+
function maxObservationCursor(a, b) {
|
|
6059
|
+
if (!a) return b ? new Date(b) : void 0;
|
|
6060
|
+
if (!b) return new Date(a);
|
|
6061
|
+
return new Date(Math.max(new Date(a).getTime(), new Date(b).getTime()));
|
|
6062
|
+
}
|
|
6063
|
+
/**
|
|
6064
|
+
* Decide the new generation's text when a reflection built from `snapshot` commits against
|
|
6065
|
+
* the stored record.
|
|
6066
|
+
*
|
|
6067
|
+
* - Stored text equals the snapshot: the reflection replaces it.
|
|
6068
|
+
* - Stored text extends the snapshot (activation only appends): the appended tail is kept
|
|
6069
|
+
* after the reflection, and its tokens are added.
|
|
6070
|
+
* - Anything else (a non-append rewrite): `null` — the reflection must not be applied.
|
|
6071
|
+
*/
|
|
6072
|
+
function planReflectionGenerationText(input) {
|
|
6073
|
+
const stored = input.storedObservations ?? "";
|
|
6074
|
+
const snapshot = input.snapshotObservations ?? "";
|
|
6075
|
+
if (stored === snapshot) return {
|
|
6076
|
+
mode: "equal",
|
|
6077
|
+
observations: input.reflection,
|
|
6078
|
+
tokenCount: input.tokenCount
|
|
6079
|
+
};
|
|
6080
|
+
if (!stored.startsWith(snapshot)) return null;
|
|
6081
|
+
const tail = stored.slice(snapshot.length).trimStart();
|
|
6082
|
+
if (!tail.trim()) return {
|
|
6083
|
+
mode: "equal",
|
|
6084
|
+
observations: input.reflection,
|
|
6085
|
+
tokenCount: input.tokenCount
|
|
6086
|
+
};
|
|
6087
|
+
return {
|
|
6088
|
+
mode: "append",
|
|
6089
|
+
observations: input.reflection ? `${input.reflection}\n\n${tail}` : tail,
|
|
6090
|
+
tokenCount: input.tokenCount + Math.max(0, (input.storedObservationTokenCount ?? 0) - (input.snapshotObservationTokenCount ?? 0))
|
|
6091
|
+
};
|
|
6092
|
+
}
|
|
6093
|
+
/**
|
|
6094
|
+
* Backs `updateActiveObservations` in adapters that implement `commitActiveObservations`.
|
|
6095
|
+
* Callers of `updateActiveObservations` (which resolves `void`) can't see a commit result, so a
|
|
6096
|
+
* write that was not applied throws instead of resolving as if it had committed.
|
|
6097
|
+
*/
|
|
6098
|
+
function assertActiveObservationsApplied(result, id) {
|
|
6099
|
+
if (result.applied) return;
|
|
6100
|
+
throw new Error(result.reason === "retired" ? `Observational memory record ${id} was superseded by a newer generation; observations were not written` : `Observational memory record ${id} changed since the observations were composed; observations were not written`);
|
|
6101
|
+
}
|
|
6102
|
+
/**
|
|
6103
|
+
* Whether the stored text can still accept a reflection built from `snapshot`
|
|
6104
|
+
* (equal, or only appended to since).
|
|
6105
|
+
*/
|
|
6106
|
+
function isAppendOnlySince(storedObservations, snapshotObservations) {
|
|
6107
|
+
const stored = storedObservations ?? "";
|
|
6108
|
+
const snapshot = snapshotObservations ?? "";
|
|
6109
|
+
return stored === snapshot || stored.startsWith(snapshot);
|
|
6110
|
+
}
|
|
6111
|
+
//#endregion
|
|
6025
6112
|
//#region src/storage/domains/memory/index.ts
|
|
6026
6113
|
const OM_TABLE = "mastra_observational_memory";
|
|
6027
6114
|
const OM_TABLE_QUOTED = quoteIdentifier(OM_TABLE, "table name");
|
|
6115
|
+
/** Canonical head order: newest generation, then earliest createdAt, then lowest id. */
|
|
6116
|
+
const OM_HEAD_ORDER = `${quoteIdentifier("generationCount", "column name")} DESC, ${quoteIdentifier("createdAt", "column name")} ASC, ${quoteIdentifier("id", "column name")} ASC`;
|
|
6117
|
+
/** Max `supersededBy` hops when redirecting a write aimed at a retired record. */
|
|
6118
|
+
const OM_MAX_HEAD_HOPS = 3;
|
|
6119
|
+
/** Attempts for an OM transaction that hits a deadlock or lock-wait timeout. */
|
|
6120
|
+
const OM_MAX_TRANSACTION_ATTEMPTS = 3;
|
|
6121
|
+
/** How long initialization waits for the per-key `GET_LOCK` before failing. */
|
|
6122
|
+
const OM_INIT_LOCK_TIMEOUT_SECONDS = 30;
|
|
6123
|
+
/** ER_LOCK_DEADLOCK (1213) or ER_LOCK_WAIT_TIMEOUT (1205): the transaction was rolled back and can be retried. */
|
|
6124
|
+
function isRetryableLockError(error) {
|
|
6125
|
+
const errno = error?.errno;
|
|
6126
|
+
return errno === 1213 || errno === 1205;
|
|
6127
|
+
}
|
|
6028
6128
|
function emitValidationError(message) {
|
|
6029
6129
|
return new _mastra_core_error.MastraError({
|
|
6030
6130
|
id: "MYSQL_MEMORY_VALIDATION_ERROR",
|
|
@@ -6131,7 +6231,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6131
6231
|
tableName: _mastra_core_storage.TABLE_RESOURCES,
|
|
6132
6232
|
schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_RESOURCES]
|
|
6133
6233
|
});
|
|
6134
|
-
const omSchema = _mastra_core_storage.OBSERVATIONAL_MEMORY_TABLE_SCHEMA?.[OM_TABLE];
|
|
6234
|
+
const omSchema = withObservationalMemoryLifecycleColumns(_mastra_core_storage.OBSERVATIONAL_MEMORY_TABLE_SCHEMA?.[OM_TABLE]);
|
|
6135
6235
|
if (omSchema) {
|
|
6136
6236
|
await this.operations.createTable({
|
|
6137
6237
|
tableName: OM_TABLE,
|
|
@@ -6154,7 +6254,8 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6154
6254
|
"isBufferingObservation",
|
|
6155
6255
|
"isBufferingReflection",
|
|
6156
6256
|
"lastBufferedAtTokens",
|
|
6157
|
-
"lastBufferedAtTime"
|
|
6257
|
+
"lastBufferedAtTime",
|
|
6258
|
+
"supersededBy"
|
|
6158
6259
|
]
|
|
6159
6260
|
});
|
|
6160
6261
|
}
|
|
@@ -6172,6 +6273,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6172
6273
|
if (err?.errno !== 1061) throw err;
|
|
6173
6274
|
}
|
|
6174
6275
|
}
|
|
6276
|
+
if (omSchema) await this.backfillSupersededBy();
|
|
6175
6277
|
await this.createDefaultIndexes();
|
|
6176
6278
|
await this.createCustomIndexes();
|
|
6177
6279
|
}
|
|
@@ -7288,7 +7390,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7288
7390
|
async getObservationalMemory(threadId, resourceId) {
|
|
7289
7391
|
try {
|
|
7290
7392
|
const lookupKey = this.getOMKey(threadId, resourceId);
|
|
7291
|
-
const [rows] = await this.pool.execute(`SELECT * FROM ${OM_TABLE_QUOTED} WHERE ${omCol("lookupKey")} = ? ORDER BY ${
|
|
7393
|
+
const [rows] = await this.pool.execute(`SELECT * FROM ${OM_TABLE_QUOTED} WHERE ${omCol("lookupKey")} = ? ORDER BY ${OM_HEAD_ORDER} LIMIT 1`, [lookupKey]);
|
|
7292
7394
|
if (!rows || rows.length === 0) return null;
|
|
7293
7395
|
return this.parseOMRow(rows[0]);
|
|
7294
7396
|
} catch (error) {
|
|
@@ -7350,32 +7452,11 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7350
7452
|
}
|
|
7351
7453
|
async initializeObservationalMemory(input) {
|
|
7352
7454
|
try {
|
|
7455
|
+
const lookupKey = this.getOMKey(input.threadId, input.resourceId);
|
|
7353
7456
|
const id = globalThis.crypto.randomUUID();
|
|
7354
7457
|
const now = /* @__PURE__ */ new Date();
|
|
7355
|
-
const
|
|
7356
|
-
|
|
7357
|
-
id,
|
|
7358
|
-
scope: input.scope,
|
|
7359
|
-
threadId: input.threadId,
|
|
7360
|
-
resourceId: input.resourceId,
|
|
7361
|
-
createdAt: now,
|
|
7362
|
-
updatedAt: now,
|
|
7363
|
-
lastObservedAt: void 0,
|
|
7364
|
-
originType: "initial",
|
|
7365
|
-
generationCount: 0,
|
|
7366
|
-
activeObservations: "",
|
|
7367
|
-
totalTokensObserved: 0,
|
|
7368
|
-
observationTokenCount: 0,
|
|
7369
|
-
pendingMessageTokens: 0,
|
|
7370
|
-
isReflecting: false,
|
|
7371
|
-
isObserving: false,
|
|
7372
|
-
isBufferingObservation: false,
|
|
7373
|
-
isBufferingReflection: false,
|
|
7374
|
-
lastBufferedAtTokens: 0,
|
|
7375
|
-
lastBufferedAtTime: null,
|
|
7376
|
-
config: input.config,
|
|
7377
|
-
observedTimezone: input.observedTimezone
|
|
7378
|
-
};
|
|
7458
|
+
const existing = await this.getObservationalMemory(input.threadId, input.resourceId);
|
|
7459
|
+
if (existing) return existing;
|
|
7379
7460
|
const nowSql = transformToSqlValue(now);
|
|
7380
7461
|
const cols = [
|
|
7381
7462
|
"id",
|
|
@@ -7401,36 +7482,53 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7401
7482
|
"lastBufferedAtTime",
|
|
7402
7483
|
"observedTimezone",
|
|
7403
7484
|
"createdAt",
|
|
7404
|
-
"updatedAt"
|
|
7485
|
+
"updatedAt",
|
|
7486
|
+
"supersededBy"
|
|
7405
7487
|
].map(omCol).join(", ");
|
|
7406
|
-
const placeholders = Array.from({ length:
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7488
|
+
const placeholders = Array.from({ length: 25 }, () => "?").join(", ");
|
|
7489
|
+
const lockName = `mastra_om_init:${(0, crypto$1.createHash)("sha256").update(lookupKey).digest("hex").slice(0, 40)}`;
|
|
7490
|
+
const connection = await this.pool.getConnection();
|
|
7491
|
+
try {
|
|
7492
|
+
const [lockRows] = await connection.query("SELECT GET_LOCK(?, ?) AS acquired", [lockName, OM_INIT_LOCK_TIMEOUT_SECONDS]);
|
|
7493
|
+
if (Number(lockRows[0]?.acquired) !== 1) throw new Error(`Timed out waiting for the observational memory initialization lock for ${lookupKey}`);
|
|
7494
|
+
try {
|
|
7495
|
+
const [liveRows] = await connection.query(`SELECT 1 FROM ${OM_TABLE_QUOTED} WHERE ${omCol("lookupKey")} = ? AND ${omCol("supersededBy")} IS NULL LIMIT 1`, [lookupKey]);
|
|
7496
|
+
if (liveRows.length === 0) await connection.execute(`INSERT INTO ${OM_TABLE_QUOTED} (${cols}) VALUES (${placeholders})`, [
|
|
7497
|
+
id,
|
|
7498
|
+
lookupKey,
|
|
7499
|
+
input.scope,
|
|
7500
|
+
input.resourceId,
|
|
7501
|
+
input.threadId || null,
|
|
7502
|
+
"",
|
|
7503
|
+
null,
|
|
7504
|
+
"initial",
|
|
7505
|
+
JSON.stringify(input.config),
|
|
7506
|
+
0,
|
|
7507
|
+
null,
|
|
7508
|
+
null,
|
|
7509
|
+
0,
|
|
7510
|
+
0,
|
|
7511
|
+
0,
|
|
7512
|
+
false,
|
|
7513
|
+
false,
|
|
7514
|
+
false,
|
|
7515
|
+
false,
|
|
7516
|
+
0,
|
|
7517
|
+
null,
|
|
7518
|
+
input.observedTimezone || null,
|
|
7519
|
+
nowSql,
|
|
7520
|
+
nowSql,
|
|
7521
|
+
null
|
|
7522
|
+
]);
|
|
7523
|
+
} finally {
|
|
7524
|
+
await connection.query("SELECT RELEASE_LOCK(?)", [lockName]);
|
|
7525
|
+
}
|
|
7526
|
+
} finally {
|
|
7527
|
+
connection.release();
|
|
7528
|
+
}
|
|
7529
|
+
const head = await this.getObservationalMemory(input.threadId, input.resourceId);
|
|
7530
|
+
if (!head) throwOMNotFound(id, "INITIALIZE_OBSERVATIONAL_MEMORY");
|
|
7531
|
+
return head;
|
|
7434
7532
|
} catch (error) {
|
|
7435
7533
|
rethrowOrWrapOM(error, "", "INITIALIZE_OBSERVATIONAL_MEMORY", {
|
|
7436
7534
|
threadId: input.threadId,
|
|
@@ -7439,115 +7537,65 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7439
7537
|
}
|
|
7440
7538
|
}
|
|
7441
7539
|
async updateActiveObservations(input) {
|
|
7540
|
+
assertActiveObservationsApplied(await this.commitActiveObservations(input), input.id);
|
|
7541
|
+
}
|
|
7542
|
+
async commitActiveObservations(input) {
|
|
7442
7543
|
try {
|
|
7443
|
-
const now = /* @__PURE__ */ new Date();
|
|
7444
7544
|
const observedMessageIdsJson = input.observedMessageIds ? JSON.stringify(input.observedMessageIds) : null;
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
input.
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7545
|
+
return await this.withOMTransaction(async (connection) => {
|
|
7546
|
+
const row = await this.lockOMRow(connection, input.id);
|
|
7547
|
+
if (!row) throwOMNotFound(input.id, "UPDATE_ACTIVE_OBSERVATIONS");
|
|
7548
|
+
if (row.supersededBy) return {
|
|
7549
|
+
applied: false,
|
|
7550
|
+
reason: "retired"
|
|
7551
|
+
};
|
|
7552
|
+
if (input.expectedActiveObservations !== void 0 && input.expectedActiveObservations !== (row.activeObservations || "")) return {
|
|
7553
|
+
applied: false,
|
|
7554
|
+
reason: "conflict"
|
|
7555
|
+
};
|
|
7556
|
+
const lastObservedAt = maxObservationCursor(parseDateTime(row.lastObservedAt), input.lastObservedAt);
|
|
7557
|
+
await connection.execute(`UPDATE ${OM_TABLE_QUOTED} SET
|
|
7558
|
+
${omCol("activeObservations")} = ?,
|
|
7559
|
+
${omCol("lastObservedAt")} = ?,
|
|
7560
|
+
${omCol("pendingMessageTokens")} = 0,
|
|
7561
|
+
${omCol("observationTokenCount")} = ?,
|
|
7562
|
+
${omCol("totalTokensObserved")} = ${omCol("totalTokensObserved")} + ?,
|
|
7563
|
+
${omCol("observedMessageIds")} = ?,
|
|
7564
|
+
${omCol("updatedAt")} = ?
|
|
7565
|
+
WHERE ${omCol("id")} = ?`, [
|
|
7566
|
+
input.observations,
|
|
7567
|
+
transformToSqlValue(lastObservedAt),
|
|
7568
|
+
input.tokenCount,
|
|
7569
|
+
input.tokenCount,
|
|
7570
|
+
observedMessageIdsJson,
|
|
7571
|
+
transformToSqlValue(/* @__PURE__ */ new Date()),
|
|
7572
|
+
input.id
|
|
7573
|
+
]);
|
|
7574
|
+
return { applied: true };
|
|
7575
|
+
});
|
|
7463
7576
|
} catch (error) {
|
|
7464
7577
|
rethrowOrWrapOM(error, input.id, "UPDATE_ACTIVE_OBSERVATIONS");
|
|
7465
7578
|
}
|
|
7466
7579
|
}
|
|
7467
7580
|
async createReflectionGeneration(input) {
|
|
7468
7581
|
try {
|
|
7469
|
-
const
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
isReflecting: false,
|
|
7487
|
-
isObserving: false,
|
|
7488
|
-
isBufferingObservation: false,
|
|
7489
|
-
isBufferingReflection: false,
|
|
7490
|
-
lastBufferedAtTokens: 0,
|
|
7491
|
-
lastBufferedAtTime: null,
|
|
7492
|
-
config: input.currentRecord.config,
|
|
7493
|
-
metadata: input.currentRecord.metadata,
|
|
7494
|
-
observedTimezone: input.currentRecord.observedTimezone
|
|
7495
|
-
};
|
|
7496
|
-
const nowSql = transformToSqlValue(now);
|
|
7497
|
-
const cols = [
|
|
7498
|
-
"id",
|
|
7499
|
-
"lookupKey",
|
|
7500
|
-
"scope",
|
|
7501
|
-
"resourceId",
|
|
7502
|
-
"threadId",
|
|
7503
|
-
"activeObservations",
|
|
7504
|
-
"activeObservationsPendingUpdate",
|
|
7505
|
-
"originType",
|
|
7506
|
-
"config",
|
|
7507
|
-
"generationCount",
|
|
7508
|
-
"lastObservedAt",
|
|
7509
|
-
"lastReflectionAt",
|
|
7510
|
-
"pendingMessageTokens",
|
|
7511
|
-
"totalTokensObserved",
|
|
7512
|
-
"observationTokenCount",
|
|
7513
|
-
"isObserving",
|
|
7514
|
-
"isReflecting",
|
|
7515
|
-
"isBufferingObservation",
|
|
7516
|
-
"isBufferingReflection",
|
|
7517
|
-
"lastBufferedAtTokens",
|
|
7518
|
-
"lastBufferedAtTime",
|
|
7519
|
-
"observedTimezone",
|
|
7520
|
-
"createdAt",
|
|
7521
|
-
"updatedAt"
|
|
7522
|
-
].map(omCol).join(", ");
|
|
7523
|
-
const placeholders = Array.from({ length: 24 }, () => "?").join(", ");
|
|
7524
|
-
await this.pool.execute(`INSERT INTO ${OM_TABLE_QUOTED} (${cols}) VALUES (${placeholders})`, [
|
|
7525
|
-
id,
|
|
7526
|
-
lookupKey,
|
|
7527
|
-
record.scope,
|
|
7528
|
-
record.resourceId,
|
|
7529
|
-
record.threadId || null,
|
|
7530
|
-
input.reflection,
|
|
7531
|
-
null,
|
|
7532
|
-
"reflection",
|
|
7533
|
-
JSON.stringify(record.config),
|
|
7534
|
-
input.currentRecord.generationCount + 1,
|
|
7535
|
-
record.lastObservedAt ? transformToSqlValue(record.lastObservedAt) : null,
|
|
7536
|
-
nowSql,
|
|
7537
|
-
record.pendingMessageTokens,
|
|
7538
|
-
record.totalTokensObserved,
|
|
7539
|
-
record.observationTokenCount,
|
|
7540
|
-
false,
|
|
7541
|
-
false,
|
|
7542
|
-
false,
|
|
7543
|
-
false,
|
|
7544
|
-
0,
|
|
7545
|
-
null,
|
|
7546
|
-
record.observedTimezone || null,
|
|
7547
|
-
nowSql,
|
|
7548
|
-
nowSql
|
|
7549
|
-
]);
|
|
7550
|
-
return record;
|
|
7582
|
+
const { currentRecord } = input;
|
|
7583
|
+
return await this.withOMTransaction(async (connection) => {
|
|
7584
|
+
const row = await this.lockOMRow(connection, currentRecord.id);
|
|
7585
|
+
if (!row) return currentRecord;
|
|
7586
|
+
if (row.supersededBy) return this.parseOMRow(await this.resolveLiveOMRow(connection, row));
|
|
7587
|
+
const stored = this.parseOMRow(row);
|
|
7588
|
+
const plan = planReflectionGenerationText({
|
|
7589
|
+
storedObservations: stored.activeObservations,
|
|
7590
|
+
storedObservationTokenCount: stored.observationTokenCount,
|
|
7591
|
+
snapshotObservations: currentRecord.activeObservations,
|
|
7592
|
+
snapshotObservationTokenCount: currentRecord.observationTokenCount,
|
|
7593
|
+
reflection: input.reflection,
|
|
7594
|
+
tokenCount: input.tokenCount
|
|
7595
|
+
});
|
|
7596
|
+
if (!plan) return stored;
|
|
7597
|
+
return this.rollOverOMRow(connection, stored.id, plan.observations, plan.tokenCount, input.newRecordId, false);
|
|
7598
|
+
});
|
|
7551
7599
|
} catch (error) {
|
|
7552
7600
|
rethrowOrWrapOM(error, input.currentRecord.id, "CREATE_REFLECTION_GENERATION", { currentRecordId: input.currentRecord.id });
|
|
7553
7601
|
}
|
|
@@ -7560,20 +7608,22 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7560
7608
|
}
|
|
7561
7609
|
async setBufferingObservationFlag(id, isBuffering, lastBufferedAtTokens) {
|
|
7562
7610
|
try {
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7611
|
+
await this.withOMTransaction(async (connection) => {
|
|
7612
|
+
const row = await this.lockOMRow(connection, id);
|
|
7613
|
+
if (!row) throwOMNotFound(id, "SET_BUFFERING_OBSERVATION_FLAG");
|
|
7614
|
+
const target = await this.resolveLiveOMRow(connection, row);
|
|
7615
|
+
const sets = [
|
|
7616
|
+
`${omCol("isBufferingObservation")} = ?`,
|
|
7617
|
+
...lastBufferedAtTokens !== void 0 ? [`${omCol("lastBufferedAtTokens")} = ?`] : [],
|
|
7618
|
+
`${omCol("updatedAt")} = ?`
|
|
7619
|
+
].join(", ");
|
|
7620
|
+
await connection.execute(`UPDATE ${OM_TABLE_QUOTED} SET ${sets} WHERE ${omCol("id")} = ?`, [
|
|
7621
|
+
isBuffering,
|
|
7622
|
+
...lastBufferedAtTokens !== void 0 ? [lastBufferedAtTokens] : [],
|
|
7623
|
+
transformToSqlValue(/* @__PURE__ */ new Date()),
|
|
7624
|
+
target.id
|
|
7625
|
+
]);
|
|
7626
|
+
});
|
|
7577
7627
|
} catch (error) {
|
|
7578
7628
|
rethrowOrWrapOM(error, id, "SET_BUFFERING_OBSERVATION_FLAG");
|
|
7579
7629
|
}
|
|
@@ -7594,83 +7644,78 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7594
7644
|
}
|
|
7595
7645
|
async setPendingMessageTokens(id, tokenCount) {
|
|
7596
7646
|
try {
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7647
|
+
await this.withOMTransaction(async (connection) => {
|
|
7648
|
+
const row = await this.lockOMRow(connection, id);
|
|
7649
|
+
if (!row) throwOMNotFound(id, "SET_PENDING_MESSAGE_TOKENS");
|
|
7650
|
+
const target = await this.resolveLiveOMRow(connection, row);
|
|
7651
|
+
await connection.execute(`UPDATE ${OM_TABLE_QUOTED} SET
|
|
7652
|
+
${omCol("pendingMessageTokens")} = ?,
|
|
7653
|
+
${omCol("updatedAt")} = ?
|
|
7654
|
+
WHERE ${omCol("id")} = ?`, [
|
|
7655
|
+
tokenCount,
|
|
7656
|
+
transformToSqlValue(/* @__PURE__ */ new Date()),
|
|
7657
|
+
target.id
|
|
7658
|
+
]);
|
|
7659
|
+
});
|
|
7606
7660
|
} catch (error) {
|
|
7607
7661
|
rethrowOrWrapOM(error, id, "SET_PENDING_MESSAGE_TOKENS");
|
|
7608
7662
|
}
|
|
7609
7663
|
}
|
|
7610
7664
|
async updateBufferedObservations(input) {
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7665
|
+
await this.appendBufferedObservations(input);
|
|
7666
|
+
}
|
|
7667
|
+
async appendBufferedObservations(input) {
|
|
7668
|
+
try {
|
|
7669
|
+
const newChunk = {
|
|
7670
|
+
id: `ombuf-${globalThis.crypto.randomUUID()}`,
|
|
7671
|
+
cycleId: input.chunk.cycleId,
|
|
7672
|
+
observations: input.chunk.observations,
|
|
7673
|
+
tokenCount: input.chunk.tokenCount,
|
|
7674
|
+
messageIds: input.chunk.messageIds,
|
|
7675
|
+
messageTokens: input.chunk.messageTokens,
|
|
7676
|
+
lastObservedAt: input.chunk.lastObservedAt,
|
|
7677
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
7678
|
+
suggestedContinuation: input.chunk.suggestedContinuation,
|
|
7679
|
+
currentTask: input.chunk.currentTask,
|
|
7680
|
+
threadTitle: input.chunk.threadTitle,
|
|
7681
|
+
extractedValues: input.chunk.extractedValues,
|
|
7682
|
+
extractionFailures: input.chunk.extractionFailures
|
|
7683
|
+
};
|
|
7684
|
+
return await this.withOMTransaction(async (connection) => {
|
|
7685
|
+
const row = await this.lockOMRow(connection, input.id);
|
|
7686
|
+
if (!row) throwOMNotFound(input.id, "UPDATE_BUFFERED_OBSERVATIONS");
|
|
7687
|
+
const target = await this.resolveLiveOMRow(connection, row);
|
|
7688
|
+
const existingChunks = parseBufferedChunks(target.bufferedObservationChunks);
|
|
7689
|
+
if (existingChunks.some((existing) => existing.cycleId === input.chunk.cycleId) || isBufferedChunkCoveredByCursor(input.chunk.lastObservedAt, parseDateTime(target.lastObservedAt))) return {
|
|
7690
|
+
persisted: false,
|
|
7691
|
+
recordId: target.id
|
|
7637
7692
|
};
|
|
7638
|
-
const
|
|
7639
|
-
|
|
7640
|
-
const [result] = await connection.execute(`UPDATE ${OM_TABLE_QUOTED} SET
|
|
7693
|
+
const lastBufferedAtTime = maxObservationCursor(parseDateTime(target.lastBufferedAtTime), input.lastBufferedAtTime ?? void 0);
|
|
7694
|
+
await connection.execute(`UPDATE ${OM_TABLE_QUOTED} SET
|
|
7641
7695
|
${omCol("bufferedObservationChunks")} = ?,
|
|
7642
|
-
${omCol("lastBufferedAtTime")} =
|
|
7696
|
+
${omCol("lastBufferedAtTime")} = ?,
|
|
7643
7697
|
${omCol("updatedAt")} = ?
|
|
7644
7698
|
WHERE ${omCol("id")} = ?`, [
|
|
7645
|
-
JSON.stringify(
|
|
7646
|
-
lastBufferedAtTime,
|
|
7647
|
-
|
|
7648
|
-
|
|
7699
|
+
JSON.stringify([...existingChunks, newChunk]),
|
|
7700
|
+
transformToSqlValue(lastBufferedAtTime),
|
|
7701
|
+
transformToSqlValue(/* @__PURE__ */ new Date()),
|
|
7702
|
+
target.id
|
|
7649
7703
|
]);
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
} finally {
|
|
7656
|
-
connection.release();
|
|
7657
|
-
}
|
|
7704
|
+
return {
|
|
7705
|
+
persisted: true,
|
|
7706
|
+
recordId: target.id
|
|
7707
|
+
};
|
|
7708
|
+
});
|
|
7658
7709
|
} catch (error) {
|
|
7659
7710
|
rethrowOrWrapOM(error, input.id, "UPDATE_BUFFERED_OBSERVATIONS");
|
|
7660
7711
|
}
|
|
7661
7712
|
}
|
|
7662
7713
|
async swapBufferedToActive(input) {
|
|
7663
|
-
const connection = await this.pool.getConnection();
|
|
7664
7714
|
try {
|
|
7665
|
-
await
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
const row = currentRows[0];
|
|
7670
|
-
const chunks = parseBufferedChunks(row.bufferedObservationChunks);
|
|
7671
|
-
if (chunks.length === 0) {
|
|
7672
|
-
await connection.commit();
|
|
7673
|
-
return {
|
|
7715
|
+
return await this.withOMTransaction(async (connection) => {
|
|
7716
|
+
const row = await this.lockOMRow(connection, input.id);
|
|
7717
|
+
if (!row) throwOMNotFound(input.id, "SWAP_BUFFERED_TO_ACTIVE");
|
|
7718
|
+
const emptyResult = {
|
|
7674
7719
|
chunksActivated: 0,
|
|
7675
7720
|
messageTokensActivated: 0,
|
|
7676
7721
|
observationTokensActivated: 0,
|
|
@@ -7678,95 +7723,103 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7678
7723
|
activatedCycleIds: [],
|
|
7679
7724
|
activatedMessageIds: []
|
|
7680
7725
|
};
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7726
|
+
if (row.supersededBy) return {
|
|
7727
|
+
...emptyResult,
|
|
7728
|
+
retired: true
|
|
7729
|
+
};
|
|
7730
|
+
const persistedChunks = parseBufferedChunks(row.bufferedObservationChunks);
|
|
7731
|
+
const refreshedWeights = new Map((Array.isArray(input.bufferedChunks) ? input.bufferedChunks : []).map((c) => [c.id, c.messageTokens]));
|
|
7732
|
+
const chunks = persistedChunks.map((c) => refreshedWeights.has(c.id) ? {
|
|
7733
|
+
...c,
|
|
7734
|
+
messageTokens: refreshedWeights.get(c.id)
|
|
7735
|
+
} : c);
|
|
7736
|
+
if (chunks.length === 0) return emptyResult;
|
|
7737
|
+
const retentionFloor = input.messageTokensThreshold * (1 - input.activationRatio);
|
|
7738
|
+
const targetMessageTokens = Math.max(0, input.currentPendingTokens - retentionFloor);
|
|
7739
|
+
let cumulativeMessageTokens = 0;
|
|
7740
|
+
let bestOverBoundary = 0;
|
|
7741
|
+
let bestOverTokens = 0;
|
|
7742
|
+
let bestUnderBoundary = 0;
|
|
7743
|
+
let bestUnderTokens = 0;
|
|
7744
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
7745
|
+
cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
|
|
7746
|
+
const boundary = i + 1;
|
|
7747
|
+
if (cumulativeMessageTokens >= targetMessageTokens) {
|
|
7748
|
+
if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
|
|
7749
|
+
bestOverBoundary = boundary;
|
|
7750
|
+
bestOverTokens = cumulativeMessageTokens;
|
|
7751
|
+
}
|
|
7752
|
+
} else if (cumulativeMessageTokens > bestUnderTokens) {
|
|
7753
|
+
bestUnderBoundary = boundary;
|
|
7754
|
+
bestUnderTokens = cumulativeMessageTokens;
|
|
7696
7755
|
}
|
|
7697
|
-
}
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
${omCol("
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
|
|
7763
|
-
currentTask: latestChunkHints?.currentTask ?? void 0
|
|
7764
|
-
};
|
|
7756
|
+
}
|
|
7757
|
+
const maxOvershoot = retentionFloor * .95;
|
|
7758
|
+
const overshoot = bestOverTokens - targetMessageTokens;
|
|
7759
|
+
const remainingAfterOver = input.currentPendingTokens - bestOverTokens;
|
|
7760
|
+
const remainingAfterUnder = input.currentPendingTokens - bestUnderTokens;
|
|
7761
|
+
const minRemaining = Math.min(1e3, retentionFloor);
|
|
7762
|
+
let chunksToActivate;
|
|
7763
|
+
if (input.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) chunksToActivate = bestOverBoundary;
|
|
7764
|
+
else if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) chunksToActivate = bestOverBoundary;
|
|
7765
|
+
else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) chunksToActivate = bestUnderBoundary;
|
|
7766
|
+
else if (bestOverBoundary > 0) chunksToActivate = bestOverBoundary;
|
|
7767
|
+
else chunksToActivate = 1;
|
|
7768
|
+
const activatedChunks = chunks.slice(0, chunksToActivate);
|
|
7769
|
+
const remainingChunks = persistedChunks.slice(chunksToActivate);
|
|
7770
|
+
const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n");
|
|
7771
|
+
const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0);
|
|
7772
|
+
const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
|
|
7773
|
+
const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + c.messageIds.length, 0);
|
|
7774
|
+
const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((cid) => !!cid);
|
|
7775
|
+
const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds ?? []);
|
|
7776
|
+
const latestChunk = activatedChunks[activatedChunks.length - 1];
|
|
7777
|
+
const derivedLastObservedAt = input.lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date());
|
|
7778
|
+
const lastObservedAt = maxObservationCursor(parseDateTime(row.lastObservedAt), derivedLastObservedAt);
|
|
7779
|
+
const existingActive = row.activeObservations || "";
|
|
7780
|
+
const existingTokenCount = Number(row.observationTokenCount || 0);
|
|
7781
|
+
const newActive = existingActive ? `${existingActive}\n\n${activatedContent}` : activatedContent;
|
|
7782
|
+
const newTokenCount = existingTokenCount + activatedTokens;
|
|
7783
|
+
const existingPending = Number(row.pendingMessageTokens || 0);
|
|
7784
|
+
const newPending = Math.max(0, existingPending - activatedMessageTokens);
|
|
7785
|
+
await connection.execute(`UPDATE ${OM_TABLE_QUOTED} SET
|
|
7786
|
+
${omCol("activeObservations")} = ?,
|
|
7787
|
+
${omCol("observationTokenCount")} = ?,
|
|
7788
|
+
${omCol("pendingMessageTokens")} = ?,
|
|
7789
|
+
${omCol("bufferedObservationChunks")} = ?,
|
|
7790
|
+
${omCol("lastObservedAt")} = ?,
|
|
7791
|
+
${omCol("updatedAt")} = ?
|
|
7792
|
+
WHERE ${omCol("id")} = ?`, [
|
|
7793
|
+
newActive,
|
|
7794
|
+
newTokenCount,
|
|
7795
|
+
newPending,
|
|
7796
|
+
remainingChunks.length > 0 ? JSON.stringify(remainingChunks) : null,
|
|
7797
|
+
transformToSqlValue(lastObservedAt),
|
|
7798
|
+
transformToSqlValue(/* @__PURE__ */ new Date()),
|
|
7799
|
+
input.id
|
|
7800
|
+
]);
|
|
7801
|
+
const latestChunkHints = activatedChunks[activatedChunks.length - 1];
|
|
7802
|
+
return {
|
|
7803
|
+
chunksActivated: activatedChunks.length,
|
|
7804
|
+
messageTokensActivated: activatedMessageTokens,
|
|
7805
|
+
observationTokensActivated: activatedTokens,
|
|
7806
|
+
messagesActivated: activatedMessageCount,
|
|
7807
|
+
activatedCycleIds,
|
|
7808
|
+
activatedMessageIds,
|
|
7809
|
+
observations: activatedContent,
|
|
7810
|
+
perChunk: activatedChunks.map((c) => ({
|
|
7811
|
+
cycleId: c.cycleId ?? "",
|
|
7812
|
+
messageTokens: c.messageTokens ?? 0,
|
|
7813
|
+
observationTokens: c.tokenCount,
|
|
7814
|
+
messageCount: c.messageIds.length,
|
|
7815
|
+
observations: c.observations
|
|
7816
|
+
})),
|
|
7817
|
+
suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
|
|
7818
|
+
currentTask: latestChunkHints?.currentTask ?? void 0
|
|
7819
|
+
};
|
|
7820
|
+
});
|
|
7765
7821
|
} catch (error) {
|
|
7766
|
-
await connection.rollback();
|
|
7767
7822
|
rethrowOrWrapOM(error, input.id, "SWAP_BUFFERED_TO_ACTIVE");
|
|
7768
|
-
} finally {
|
|
7769
|
-
connection.release();
|
|
7770
7823
|
}
|
|
7771
7824
|
}
|
|
7772
7825
|
async updateBufferedReflection(input) {
|
|
@@ -7799,38 +7852,182 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7799
7852
|
}
|
|
7800
7853
|
async swapBufferedReflectionToActive(input) {
|
|
7801
7854
|
try {
|
|
7802
|
-
const
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7855
|
+
const { currentRecord } = input;
|
|
7856
|
+
return await this.withOMTransaction(async (connection) => {
|
|
7857
|
+
const row = await this.lockOMRow(connection, currentRecord.id);
|
|
7858
|
+
if (!row) return currentRecord;
|
|
7859
|
+
if (row.supersededBy) return this.parseOMRow(await this.resolveLiveOMRow(connection, row));
|
|
7860
|
+
const stored = this.parseOMRow(row);
|
|
7861
|
+
const bufferedReflection = stored.bufferedReflection || "";
|
|
7862
|
+
if (!bufferedReflection) throw new _mastra_core_error.MastraError({
|
|
7863
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MYSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NO_CONTENT"),
|
|
7864
|
+
text: "No buffered reflection to swap",
|
|
7865
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
7866
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
7867
|
+
details: { id: currentRecord.id }
|
|
7868
|
+
});
|
|
7869
|
+
if (!isAppendOnlySince(stored.activeObservations, currentRecord.activeObservations)) return stored;
|
|
7870
|
+
const reflectedLineCount = stored.reflectedObservationLineCount ?? 0;
|
|
7871
|
+
const unreflectedContent = (stored.activeObservations || "").split("\n").slice(reflectedLineCount).join("\n").trim();
|
|
7872
|
+
const newObservations = unreflectedContent ? `${bufferedReflection}\n\n${unreflectedContent}` : bufferedReflection;
|
|
7873
|
+
const tokenCount = input.tokenCount + Math.max(0, (stored.observationTokenCount ?? 0) - (currentRecord.observationTokenCount ?? 0));
|
|
7874
|
+
return this.rollOverOMRow(connection, stored.id, newObservations, tokenCount, input.newRecordId, true);
|
|
7820
7875
|
});
|
|
7821
|
-
const nowSql = transformToSqlValue(/* @__PURE__ */ new Date());
|
|
7822
|
-
await this.pool.execute(`UPDATE ${OM_TABLE_QUOTED} SET
|
|
7823
|
-
${omCol("bufferedReflection")} = NULL,
|
|
7824
|
-
${omCol("bufferedReflectionTokens")} = NULL,
|
|
7825
|
-
${omCol("bufferedReflectionInputTokens")} = NULL,
|
|
7826
|
-
${omCol("reflectedObservationLineCount")} = NULL,
|
|
7827
|
-
${omCol("updatedAt")} = ?
|
|
7828
|
-
WHERE ${omCol("id")} = ?`, [nowSql, input.currentRecord.id]);
|
|
7829
|
-
return newRecord;
|
|
7830
7876
|
} catch (error) {
|
|
7831
7877
|
rethrowOrWrapOM(error, input.currentRecord.id, "SWAP_BUFFERED_REFLECTION_TO_ACTIVE");
|
|
7832
7878
|
}
|
|
7833
7879
|
}
|
|
7880
|
+
/**
|
|
7881
|
+
* Runs `fn` in one transaction on a dedicated connection. Every OM lifecycle write locks its
|
|
7882
|
+
* target row by primary key (`SELECT … FOR UPDATE`, a record lock — never a range), so the
|
|
7883
|
+
* only expected lock conflict is a wait; a deadlock or lock-wait timeout (e.g. against the
|
|
7884
|
+
* startup backfill's scan) is retried a bounded number of times.
|
|
7885
|
+
*/
|
|
7886
|
+
async withOMTransaction(fn) {
|
|
7887
|
+
for (let attempt = 1;; attempt++) {
|
|
7888
|
+
const connection = await this.pool.getConnection();
|
|
7889
|
+
try {
|
|
7890
|
+
await connection.beginTransaction();
|
|
7891
|
+
const result = await fn(connection);
|
|
7892
|
+
await connection.commit();
|
|
7893
|
+
return result;
|
|
7894
|
+
} catch (error) {
|
|
7895
|
+
await connection.rollback().catch(() => {});
|
|
7896
|
+
if (attempt < OM_MAX_TRANSACTION_ATTEMPTS && isRetryableLockError(error)) continue;
|
|
7897
|
+
throw error;
|
|
7898
|
+
} finally {
|
|
7899
|
+
connection.release();
|
|
7900
|
+
}
|
|
7901
|
+
}
|
|
7902
|
+
}
|
|
7903
|
+
/** Row-locks one OM record by primary key for the rest of the transaction. */
|
|
7904
|
+
async lockOMRow(connection, id) {
|
|
7905
|
+
const [rows] = await connection.execute(`SELECT * FROM ${OM_TABLE_QUOTED} WHERE ${omCol("id")} = ? FOR UPDATE`, [id]);
|
|
7906
|
+
return rows?.[0] ?? null;
|
|
7907
|
+
}
|
|
7908
|
+
/**
|
|
7909
|
+
* The live, row-locked record a lifecycle write aimed at `row` lands on: `row` itself while
|
|
7910
|
+
* live, otherwise the head. The head is found with a plain (non-locking, so no range locks)
|
|
7911
|
+
* canonical head read, then locked by primary key. Under REPEATABLE READ that read can predate
|
|
7912
|
+
* a rollover committed after this transaction started; the locking read sees the latest
|
|
7913
|
+
* committed row, so a head retired since is followed through `supersededBy` (bounded).
|
|
7914
|
+
*/
|
|
7915
|
+
async resolveLiveOMRow(connection, row) {
|
|
7916
|
+
if (!row.supersededBy) return row;
|
|
7917
|
+
const [heads] = await connection.execute(`SELECT id FROM ${OM_TABLE_QUOTED} WHERE ${omCol("lookupKey")} = ? ORDER BY ${OM_HEAD_ORDER} LIMIT 1`, [row.lookupKey]);
|
|
7918
|
+
const headId = heads?.[0]?.id ?? row.supersededBy;
|
|
7919
|
+
let current = await this.lockOMRow(connection, headId) ?? row;
|
|
7920
|
+
for (let hop = 0; current.supersededBy; hop++) {
|
|
7921
|
+
const next = hop < OM_MAX_HEAD_HOPS ? await this.lockOMRow(connection, current.supersededBy) : null;
|
|
7922
|
+
if (!next) throw new _mastra_core_error.MastraError({
|
|
7923
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MYSQL", "RESOLVE_OBSERVATIONAL_MEMORY_HEAD", "FAILED"),
|
|
7924
|
+
text: `Observational memory record ${row.id} is superseded but no live head was found`,
|
|
7925
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
7926
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
7927
|
+
details: { id: row.id }
|
|
7928
|
+
});
|
|
7929
|
+
current = next;
|
|
7930
|
+
}
|
|
7931
|
+
return current;
|
|
7932
|
+
}
|
|
7933
|
+
/**
|
|
7934
|
+
* Create the next generation from the stored (live, row-locked) record and retire the stored
|
|
7935
|
+
* record in the same transaction. Carried columns are copied in SQL from the stored row, so
|
|
7936
|
+
* nothing round-trips through a client-side date conversion. Buffered chunks move to the new
|
|
7937
|
+
* generation; the cursor, buffering markers, flags, and counters carry over; buffered
|
|
7938
|
+
* reflection state does not (and is cleared on the retired row after a buffered swap).
|
|
7939
|
+
*/
|
|
7940
|
+
async rollOverOMRow(connection, storedId, observations, tokenCount, newRecordId, clearBufferedReflection) {
|
|
7941
|
+
const id = newRecordId ?? globalThis.crypto.randomUUID();
|
|
7942
|
+
const nowSql = transformToSqlValue(/* @__PURE__ */ new Date());
|
|
7943
|
+
const c = omCol;
|
|
7944
|
+
await connection.execute(`INSERT INTO ${OM_TABLE_QUOTED} (
|
|
7945
|
+
${c("id")}, ${c("lookupKey")}, ${c("scope")}, ${c("resourceId")}, ${c("threadId")},
|
|
7946
|
+
${c("activeObservations")}, ${c("activeObservationsPendingUpdate")},
|
|
7947
|
+
${c("originType")}, ${c("config")}, ${c("generationCount")}, ${c("lastObservedAt")}, ${c("lastReflectionAt")},
|
|
7948
|
+
${c("pendingMessageTokens")}, ${c("totalTokensObserved")}, ${c("observationTokenCount")}, ${c("bufferedObservationChunks")},
|
|
7949
|
+
${c("isObserving")}, ${c("isReflecting")}, ${c("isBufferingObservation")}, ${c("isBufferingReflection")},
|
|
7950
|
+
${c("lastBufferedAtTokens")}, ${c("lastBufferedAtTime")},
|
|
7951
|
+
${c("observedTimezone")}, ${c("metadata")}, ${c("supersededBy")}, ${c("createdAt")}, ${c("updatedAt")}
|
|
7952
|
+
)
|
|
7953
|
+
SELECT
|
|
7954
|
+
?, ${c("lookupKey")}, ${c("scope")}, ${c("resourceId")}, ${c("threadId")},
|
|
7955
|
+
?, NULL,
|
|
7956
|
+
'reflection', ${c("config")}, ${c("generationCount")} + 1, ${c("lastObservedAt")}, ?,
|
|
7957
|
+
${c("pendingMessageTokens")}, ${c("totalTokensObserved")}, ?, ${c("bufferedObservationChunks")},
|
|
7958
|
+
false, false, ${c("isBufferingObservation")}, false,
|
|
7959
|
+
${c("lastBufferedAtTokens")}, ${c("lastBufferedAtTime")},
|
|
7960
|
+
${c("observedTimezone")}, ${c("metadata")}, NULL, ?, ?
|
|
7961
|
+
FROM ${OM_TABLE_QUOTED} WHERE ${c("id")} = ?`, [
|
|
7962
|
+
id,
|
|
7963
|
+
observations,
|
|
7964
|
+
nowSql,
|
|
7965
|
+
Math.round(tokenCount),
|
|
7966
|
+
nowSql,
|
|
7967
|
+
nowSql,
|
|
7968
|
+
storedId
|
|
7969
|
+
]);
|
|
7970
|
+
const [retired] = await connection.execute(`UPDATE ${OM_TABLE_QUOTED} SET
|
|
7971
|
+
${c("supersededBy")} = ?,
|
|
7972
|
+
${c("bufferedObservationChunks")} = NULL,
|
|
7973
|
+
${clearBufferedReflection ? `${c("bufferedReflection")} = NULL, ${c("bufferedReflectionTokens")} = NULL,
|
|
7974
|
+
${c("bufferedReflectionInputTokens")} = NULL, ${c("reflectedObservationLineCount")} = NULL,` : ""}
|
|
7975
|
+
${c("updatedAt")} = ?
|
|
7976
|
+
WHERE ${c("id")} = ? AND ${c("supersededBy")} IS NULL`, [
|
|
7977
|
+
id,
|
|
7978
|
+
nowSql,
|
|
7979
|
+
storedId
|
|
7980
|
+
]);
|
|
7981
|
+
if (retired.affectedRows !== 1) throw new _mastra_core_error.MastraError({
|
|
7982
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MYSQL", "CREATE_REFLECTION_GENERATION", "RETIRE_FAILED"),
|
|
7983
|
+
text: `Failed to retire observational memory record ${storedId}`,
|
|
7984
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
7985
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
7986
|
+
details: { id: storedId }
|
|
7987
|
+
});
|
|
7988
|
+
const inserted = await this.lockOMRow(connection, id);
|
|
7989
|
+
if (!inserted) throwOMNotFound(id, "CREATE_REFLECTION_GENERATION");
|
|
7990
|
+
return this.parseOMRow(inserted);
|
|
7991
|
+
}
|
|
7992
|
+
/**
|
|
7993
|
+
* Retire every live row that sorts after its key's canonical head, marking it superseded by
|
|
7994
|
+
* the head. Rows retired by older adapter versions (which never set `supersededBy`) become
|
|
7995
|
+
* frozen. Only keys with more than one live row are touched; each update embeds the head's
|
|
7996
|
+
* ordering key, so a row created by a concurrent rollover (a newer generation) never matches.
|
|
7997
|
+
* Runs at READ COMMITTED so the scan takes no gap locks.
|
|
7998
|
+
*/
|
|
7999
|
+
async backfillSupersededBy() {
|
|
8000
|
+
const [keys] = await this.pool.execute(`SELECT ${omCol("lookupKey")} AS lookupKey FROM ${OM_TABLE_QUOTED}
|
|
8001
|
+
WHERE ${omCol("supersededBy")} IS NULL GROUP BY ${omCol("lookupKey")} HAVING COUNT(*) > 1`);
|
|
8002
|
+
for (const { lookupKey } of keys ?? []) for (let attempt = 1;; attempt++) {
|
|
8003
|
+
const connection = await this.pool.getConnection();
|
|
8004
|
+
try {
|
|
8005
|
+
await connection.query("SET TRANSACTION ISOLATION LEVEL READ COMMITTED");
|
|
8006
|
+
await connection.beginTransaction();
|
|
8007
|
+
await connection.execute(`UPDATE ${OM_TABLE_QUOTED} AS r
|
|
8008
|
+
JOIN (
|
|
8009
|
+
SELECT ${omCol("id")} AS id, ${omCol("generationCount")} AS generationCount, ${omCol("createdAt")} AS createdAt
|
|
8010
|
+
FROM ${OM_TABLE_QUOTED} WHERE ${omCol("lookupKey")} = ? ORDER BY ${OM_HEAD_ORDER} LIMIT 1
|
|
8011
|
+
) AS h
|
|
8012
|
+
SET r.${omCol("supersededBy")} = h.id
|
|
8013
|
+
WHERE r.${omCol("lookupKey")} = ?
|
|
8014
|
+
AND r.${omCol("supersededBy")} IS NULL
|
|
8015
|
+
AND (
|
|
8016
|
+
r.${omCol("generationCount")} < h.generationCount
|
|
8017
|
+
OR (r.${omCol("generationCount")} = h.generationCount AND r.${omCol("createdAt")} > h.createdAt)
|
|
8018
|
+
OR (r.${omCol("generationCount")} = h.generationCount AND r.${omCol("createdAt")} = h.createdAt AND r.${omCol("id")} > h.id)
|
|
8019
|
+
)`, [lookupKey, lookupKey]);
|
|
8020
|
+
await connection.commit();
|
|
8021
|
+
break;
|
|
8022
|
+
} catch (error) {
|
|
8023
|
+
await connection.rollback().catch(() => {});
|
|
8024
|
+
if (attempt < OM_MAX_TRANSACTION_ATTEMPTS && isRetryableLockError(error)) continue;
|
|
8025
|
+
throw error;
|
|
8026
|
+
} finally {
|
|
8027
|
+
connection.release();
|
|
8028
|
+
}
|
|
8029
|
+
}
|
|
8030
|
+
}
|
|
7834
8031
|
async updateOMFlag(id, column, value, operation) {
|
|
7835
8032
|
try {
|
|
7836
8033
|
const [result] = await this.pool.execute(`UPDATE ${OM_TABLE_QUOTED} SET ${omCol(column)} = ?, ${omCol("updatedAt")} = ? WHERE ${omCol("id")} = ?`, [
|
|
@@ -7878,7 +8075,8 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7878
8075
|
config: parseJSONColumn(row.config) ?? {},
|
|
7879
8076
|
metadata: parseJSONColumn(row.metadata),
|
|
7880
8077
|
observedMessageIds: parseJSONColumn(row.observedMessageIds),
|
|
7881
|
-
observedTimezone: row.observedTimezone || void 0
|
|
8078
|
+
observedTimezone: row.observedTimezone || void 0,
|
|
8079
|
+
supersededBy: row.supersededBy ?? null
|
|
7882
8080
|
};
|
|
7883
8081
|
}
|
|
7884
8082
|
};
|