@mastra/oracledb 0.5.1 → 0.5.2-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.
- 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/docs/references/docs-memory-observational-memory.md +17 -15
- package/dist/index.cjs +677 -170
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +678 -171
- package/dist/index.js.map +1 -1
- package/dist/storage/db/run-fencing.d.ts +32 -0
- package/dist/storage/db/run-fencing.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +28 -8
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/messages.d.ts +6 -4
- package/dist/storage/domains/memory/messages.d.ts.map +1 -1
- package/dist/storage/domains/memory/observational-buffering.d.ts +6 -5
- package/dist/storage/domains/memory/observational-buffering.d.ts.map +1 -1
- package/dist/storage/domains/memory/observational.d.ts +25 -3
- package/dist/storage/domains/memory/observational.d.ts.map +1 -1
- package/dist/storage/domains/memory/resources.d.ts +3 -2
- package/dist/storage/domains/memory/resources.d.ts.map +1 -1
- package/dist/storage/domains/memory/schema.d.ts +11 -0
- package/dist/storage/domains/memory/schema.d.ts.map +1 -1
- package/dist/storage/domains/memory/threads.d.ts +5 -3
- package/dist/storage/domains/memory/threads.d.ts.map +1 -1
- package/dist/storage/domains/memory/utils.d.ts +15 -2
- package/dist/storage/domains/memory/utils.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +19 -6
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -3369,6 +3369,124 @@ function jsonComparableValue$1(value) {
|
|
|
3369
3369
|
return value;
|
|
3370
3370
|
}
|
|
3371
3371
|
//#endregion
|
|
3372
|
+
//#region ../../packages/_internal-core/dist/memory/index.js
|
|
3373
|
+
/**
|
|
3374
|
+
* A buffered chunk stores `lastObservedAt = max message time + 1ms`. It is wholly covered
|
|
3375
|
+
* (every message it observed is already at or before the cursor) iff
|
|
3376
|
+
* `cursor >= chunk.lastObservedAt - 1ms`.
|
|
3377
|
+
*/
|
|
3378
|
+
function isBufferedChunkCoveredByCursor(chunkLastObservedAt, cursor) {
|
|
3379
|
+
if (!cursor) return false;
|
|
3380
|
+
return new Date(cursor).getTime() >= new Date(chunkLastObservedAt).getTime() - 1;
|
|
3381
|
+
}
|
|
3382
|
+
/** The later of two cursors; `undefined` only when both are absent. */
|
|
3383
|
+
function maxObservationCursor(a, b) {
|
|
3384
|
+
if (!a) return b ? new Date(b) : void 0;
|
|
3385
|
+
if (!b) return new Date(a);
|
|
3386
|
+
return new Date(Math.max(new Date(a).getTime(), new Date(b).getTime()));
|
|
3387
|
+
}
|
|
3388
|
+
/**
|
|
3389
|
+
* Decide the new generation's text when a reflection built from `snapshot` commits against
|
|
3390
|
+
* the stored record.
|
|
3391
|
+
*
|
|
3392
|
+
* - Stored text equals the snapshot: the reflection replaces it.
|
|
3393
|
+
* - Stored text extends the snapshot (activation only appends): the appended tail is kept
|
|
3394
|
+
* after the reflection, and its tokens are added.
|
|
3395
|
+
* - Anything else (a non-append rewrite): `null` — the reflection must not be applied.
|
|
3396
|
+
*/
|
|
3397
|
+
function planReflectionGenerationText(input) {
|
|
3398
|
+
const stored = input.storedObservations ?? "";
|
|
3399
|
+
const snapshot = input.snapshotObservations ?? "";
|
|
3400
|
+
if (stored === snapshot) return {
|
|
3401
|
+
mode: "equal",
|
|
3402
|
+
observations: input.reflection,
|
|
3403
|
+
tokenCount: input.tokenCount
|
|
3404
|
+
};
|
|
3405
|
+
if (!stored.startsWith(snapshot)) return null;
|
|
3406
|
+
const tail = stored.slice(snapshot.length).trimStart();
|
|
3407
|
+
if (!tail.trim()) return {
|
|
3408
|
+
mode: "equal",
|
|
3409
|
+
observations: input.reflection,
|
|
3410
|
+
tokenCount: input.tokenCount
|
|
3411
|
+
};
|
|
3412
|
+
return {
|
|
3413
|
+
mode: "append",
|
|
3414
|
+
observations: input.reflection ? `${input.reflection}\n\n${tail}` : tail,
|
|
3415
|
+
tokenCount: input.tokenCount + Math.max(0, (input.storedObservationTokenCount ?? 0) - (input.snapshotObservationTokenCount ?? 0))
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
3418
|
+
/**
|
|
3419
|
+
* Backs `updateActiveObservations` in adapters that implement `commitActiveObservations`.
|
|
3420
|
+
* Callers of `updateActiveObservations` (which resolves `void`) can't see a commit result, so a
|
|
3421
|
+
* write that was not applied throws instead of resolving as if it had committed.
|
|
3422
|
+
*/
|
|
3423
|
+
function assertActiveObservationsApplied(result, id) {
|
|
3424
|
+
if (result.applied) return;
|
|
3425
|
+
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`);
|
|
3426
|
+
}
|
|
3427
|
+
/**
|
|
3428
|
+
* Whether the stored text can still accept a reflection built from `snapshot`
|
|
3429
|
+
* (equal, or only appended to since).
|
|
3430
|
+
*/
|
|
3431
|
+
function isAppendOnlySince(storedObservations, snapshotObservations) {
|
|
3432
|
+
const stored = storedObservations ?? "";
|
|
3433
|
+
const snapshot = snapshotObservations ?? "";
|
|
3434
|
+
return stored === snapshot || stored.startsWith(snapshot);
|
|
3435
|
+
}
|
|
3436
|
+
//#endregion
|
|
3437
|
+
//#region src/storage/db/run-fencing.ts
|
|
3438
|
+
const SINCE_EPOCH = `(SYS_EXTRACT_UTC(SYSTIMESTAMP) - TIMESTAMP '1970-01-01 00:00:00')`;
|
|
3439
|
+
/**
|
|
3440
|
+
* Epoch milliseconds on the database clock. Oracle evaluates `SYSTIMESTAMP`
|
|
3441
|
+
* once per statement, so every reference here reads the same instant, and a
|
|
3442
|
+
* statement that waits on a lock compares against the time it started. That
|
|
3443
|
+
* error only makes an existing lease look longer and a new lease shorter.
|
|
3444
|
+
*/
|
|
3445
|
+
const DB_NOW_MS = `(EXTRACT(DAY FROM ${SINCE_EPOCH}) * 86400000 + EXTRACT(HOUR FROM ${SINCE_EPOCH}) * 3600000 + EXTRACT(MINUTE FROM ${SINCE_EPOCH}) * 60000 + FLOOR(EXTRACT(SECOND FROM ${SINCE_EPOCH}) * 1000))`;
|
|
3446
|
+
const ORA_UNIQUE_CONSTRAINT = -1;
|
|
3447
|
+
const ORA_DEADLOCK = -60;
|
|
3448
|
+
const MAX_CLAIM_ATTEMPTS = 5;
|
|
3449
|
+
/**
|
|
3450
|
+
* The check half of a fenced write; call it first inside the write's
|
|
3451
|
+
* transaction. Oracle has no shared row lock, so this takes the claim row
|
|
3452
|
+
* `FOR UPDATE`: a claim that updates the row waits for the write to commit,
|
|
3453
|
+
* and a write queued behind a claim re-reads the row once the claim commits
|
|
3454
|
+
* and sees the new generation. Writes from one run serialize on its row,
|
|
3455
|
+
* which they mostly do anyway.
|
|
3456
|
+
*/
|
|
3457
|
+
async function assertRunFence(client, claimsTable, fence, operation) {
|
|
3458
|
+
const row = await client.oneOrNone(`SELECT generation AS "generation", "ownerId" FROM ${claimsTable} WHERE "runId" = :runId FOR UPDATE`, { runId: fence.runId });
|
|
3459
|
+
if (!(0, _mastra_core_storage.matchesRunFence)(row ? {
|
|
3460
|
+
generation: Number(row.generation),
|
|
3461
|
+
ownerId: String(row.ownerId)
|
|
3462
|
+
} : null, fence)) throw new _mastra_core_storage.RunFenceConflictError(fence, operation);
|
|
3463
|
+
}
|
|
3464
|
+
/**
|
|
3465
|
+
* Runs `write` in a transaction, behind the fence check when it carries a
|
|
3466
|
+
* fence.
|
|
3467
|
+
*/
|
|
3468
|
+
function withRunFence(db, claimsTable, fence, operation, write) {
|
|
3469
|
+
return db.tx(async (client) => {
|
|
3470
|
+
if (fence) await assertRunFence(client, claimsTable, fence, operation);
|
|
3471
|
+
return write(client);
|
|
3472
|
+
});
|
|
3473
|
+
}
|
|
3474
|
+
/**
|
|
3475
|
+
* Runs a transaction that creates or updates a run's claim row, retrying when
|
|
3476
|
+
* two first claims race to insert it. `FOR UPDATE` locks nothing while the row
|
|
3477
|
+
* is missing, so racing first claims both insert; the second waits on the
|
|
3478
|
+
* primary key and fails once the first commits. On retry the row exists and
|
|
3479
|
+
* the claim serializes behind its row lock. A deadlock is retried too.
|
|
3480
|
+
*/
|
|
3481
|
+
async function claimTransaction(db, fn) {
|
|
3482
|
+
for (let attempt = 1;; attempt++) try {
|
|
3483
|
+
return await db.tx((client) => fn(client));
|
|
3484
|
+
} catch (error) {
|
|
3485
|
+
const retryable = isOracleErrorCode(error, [ORA_UNIQUE_CONSTRAINT, ORA_DEADLOCK]);
|
|
3486
|
+
if (attempt >= MAX_CLAIM_ATTEMPTS || !retryable) throw error;
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
//#endregion
|
|
3372
3490
|
//#region src/storage/domains/memory/utils.ts
|
|
3373
3491
|
const STORE_NAME$5 = "ORACLEDB";
|
|
3374
3492
|
const EMPTY_STRING_SENTINEL = "__MASTRA_ORACLE_EMPTY_STRING__";
|
|
@@ -3376,6 +3494,14 @@ const STRING_SENTINEL_ESCAPE_PREFIX = "__MASTRA_ORACLE_ESCAPED__";
|
|
|
3376
3494
|
function table(ctx, tableName) {
|
|
3377
3495
|
return qualifyName(tableName, ctx.schemaName);
|
|
3378
3496
|
}
|
|
3497
|
+
/**
|
|
3498
|
+
* The fence check for a memory write; call it first inside the write's
|
|
3499
|
+
* transaction. `fence` is the one `MemoryOracle` already resolved against the
|
|
3500
|
+
* run fence scope, so an unfenced write skips the check.
|
|
3501
|
+
*/
|
|
3502
|
+
async function assertMemoryFence(ctx, client, fence, operation) {
|
|
3503
|
+
if (fence) await assertRunFence(client, table(ctx, _mastra_core_storage.TABLE_MEMORY_RUN_FENCES), fence, operation);
|
|
3504
|
+
}
|
|
3379
3505
|
function storageError$1(operation, reason, details, cause, category = _mastra_core_error.ErrorCategory.THIRD_PARTY) {
|
|
3380
3506
|
return createOracleStorageError({
|
|
3381
3507
|
storeName: STORE_NAME$5,
|
|
@@ -3406,6 +3532,17 @@ function chunkValues(values, size = 900) {
|
|
|
3406
3532
|
for (let index = 0; index < values.length; index += size) chunks.push(values.slice(index, index + size));
|
|
3407
3533
|
return chunks;
|
|
3408
3534
|
}
|
|
3535
|
+
/**
|
|
3536
|
+
* Bind an instant as TIMESTAMP WITH TIME ZONE. A bare `Date` binds as TIMESTAMP WITH LOCAL
|
|
3537
|
+
* TIME ZONE, which the server converts with the session's fixed offset (captured at connect
|
|
3538
|
+
* time); an instant on the other side of a DST change is then stored shifted by an hour.
|
|
3539
|
+
*/
|
|
3540
|
+
function timestampBind(value) {
|
|
3541
|
+
return value === void 0 || value === null ? null : {
|
|
3542
|
+
type: oracledb.default.DB_TYPE_TIMESTAMP_TZ,
|
|
3543
|
+
val: toDate(value)
|
|
3544
|
+
};
|
|
3545
|
+
}
|
|
3409
3546
|
function boolToNumber(value) {
|
|
3410
3547
|
return value ? 1 : 0;
|
|
3411
3548
|
}
|
|
@@ -3554,12 +3691,48 @@ const OM_LAST_BUFFERED_AT_TOKENS = "\"lastBufferedAtTokens\"";
|
|
|
3554
3691
|
const OM_LAST_BUFFERED_AT_TIME = "\"lastBufferedAtTime\"";
|
|
3555
3692
|
const OM_CREATED_AT = "\"createdAt\"";
|
|
3556
3693
|
const OM_UPDATED_AT = "\"updatedAt\"";
|
|
3694
|
+
const OM_SUPERSEDED_BY = "\"supersededBy\"";
|
|
3557
3695
|
async function initMemorySchema(ctx) {
|
|
3558
3696
|
await ctx.db.withConnection(async (connection) => {
|
|
3559
3697
|
await createTables(ctx, connection);
|
|
3560
3698
|
await createIndexes(ctx, connection);
|
|
3699
|
+
await backfillObservationalMemorySupersededBy(ctx, connection);
|
|
3561
3700
|
});
|
|
3562
3701
|
}
|
|
3702
|
+
/**
|
|
3703
|
+
* Retire every live observational memory row that sorts after its key's canonical head
|
|
3704
|
+
* (`generationCount DESC, createdAt ASC, id ASC`), marking it superseded by the head. Rows
|
|
3705
|
+
* retired by older adapter versions (which never set `supersededBy`) become frozen. Idempotent.
|
|
3706
|
+
* One statement: the head's ordering key is part of the condition, so a row created by a
|
|
3707
|
+
* concurrent rollover (higher generation) never matches, and a row locked by one is
|
|
3708
|
+
* re-evaluated after it commits.
|
|
3709
|
+
*/
|
|
3710
|
+
async function backfillObservationalMemorySupersededBy(ctx, connection) {
|
|
3711
|
+
const om = table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY);
|
|
3712
|
+
const heads = `
|
|
3713
|
+
SELECT ${OM_LOOKUP_KEY} AS head_key, id AS head_id, ${OM_GENERATION_COUNT} AS head_gen, ${OM_CREATED_AT} AS head_created
|
|
3714
|
+
FROM (
|
|
3715
|
+
SELECT ${OM_LOOKUP_KEY}, id, ${OM_GENERATION_COUNT}, ${OM_CREATED_AT},
|
|
3716
|
+
ROW_NUMBER() OVER (PARTITION BY ${OM_LOOKUP_KEY} ORDER BY ${OM_GENERATION_COUNT} DESC, ${OM_CREATED_AT} ASC, id ASC) AS rn
|
|
3717
|
+
FROM ${om}
|
|
3718
|
+
WHERE ${OM_LOOKUP_KEY} IN (
|
|
3719
|
+
SELECT ${OM_LOOKUP_KEY} FROM ${om} WHERE ${OM_SUPERSEDED_BY} IS NULL GROUP BY ${OM_LOOKUP_KEY} HAVING COUNT(*) > 1
|
|
3720
|
+
)
|
|
3721
|
+
)
|
|
3722
|
+
WHERE rn = 1`;
|
|
3723
|
+
await connection.execute(`UPDATE ${om} r
|
|
3724
|
+
SET r.${OM_SUPERSEDED_BY} = (SELECT h.head_id FROM (${heads}) h WHERE h.head_key = r.${OM_LOOKUP_KEY})
|
|
3725
|
+
WHERE r.${OM_SUPERSEDED_BY} IS NULL
|
|
3726
|
+
AND EXISTS (
|
|
3727
|
+
SELECT 1 FROM (${heads}) h
|
|
3728
|
+
WHERE h.head_key = r.${OM_LOOKUP_KEY}
|
|
3729
|
+
AND (
|
|
3730
|
+
r.${OM_GENERATION_COUNT} < h.head_gen
|
|
3731
|
+
OR (r.${OM_GENERATION_COUNT} = h.head_gen AND r.${OM_CREATED_AT} > h.head_created)
|
|
3732
|
+
OR (r.${OM_GENERATION_COUNT} = h.head_gen AND r.${OM_CREATED_AT} = h.head_created AND r.id > h.head_id)
|
|
3733
|
+
)
|
|
3734
|
+
)`, {}, { autoCommit: true });
|
|
3735
|
+
}
|
|
3563
3736
|
async function clearAllMemoryTables(ctx) {
|
|
3564
3737
|
await ctx.db.tx(async (client) => {
|
|
3565
3738
|
await client.none(`DELETE FROM ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)}`);
|
|
@@ -3633,7 +3806,8 @@ async function createTables(ctx, connection) {
|
|
|
3633
3806
|
${OM_LAST_BUFFERED_AT_TIME} TIMESTAMP WITH TIME ZONE,
|
|
3634
3807
|
metadata JSON,
|
|
3635
3808
|
${OM_CREATED_AT} TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
3636
|
-
${OM_UPDATED_AT} TIMESTAMP WITH TIME ZONE NOT NULL
|
|
3809
|
+
${OM_UPDATED_AT} TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
3810
|
+
${OM_SUPERSEDED_BY} VARCHAR2(512)
|
|
3637
3811
|
)`, [-955]);
|
|
3638
3812
|
await ensureObservationalMemoryColumns(ctx, connection);
|
|
3639
3813
|
await relaxObservationalMemoryNullability(ctx, connection);
|
|
@@ -3771,6 +3945,10 @@ async function ensureObservationalMemoryColumns(ctx, connection) {
|
|
|
3771
3945
|
{
|
|
3772
3946
|
name: OM_UPDATED_AT,
|
|
3773
3947
|
type: "TIMESTAMP WITH TIME ZONE"
|
|
3948
|
+
},
|
|
3949
|
+
{
|
|
3950
|
+
name: OM_SUPERSEDED_BY,
|
|
3951
|
+
type: "VARCHAR2(512)"
|
|
3774
3952
|
}
|
|
3775
3953
|
];
|
|
3776
3954
|
for (const column of columns) await executeDdl(connection, `ALTER TABLE ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)} ADD (${column.name} ${column.type})`, [-1430]);
|
|
@@ -3881,7 +4059,7 @@ async function listMessagesByResourceId(ctx, args) {
|
|
|
3881
4059
|
orderBy
|
|
3882
4060
|
});
|
|
3883
4061
|
}
|
|
3884
|
-
async function saveMessages(ctx, { messages }) {
|
|
4062
|
+
async function saveMessages(ctx, { messages, fence }) {
|
|
3885
4063
|
if (messages.length === 0) return { messages: [] };
|
|
3886
4064
|
const threadIds = /* @__PURE__ */ new Set();
|
|
3887
4065
|
try {
|
|
@@ -3891,6 +4069,7 @@ async function saveMessages(ctx, { messages }) {
|
|
|
3891
4069
|
}
|
|
3892
4070
|
for (const threadId of threadIds) if (!await ctx.getThreadById({ threadId })) throw storageError$1("SAVE_MESSAGES", "FAILED", { threadId }, /* @__PURE__ */ new Error(`Thread ${threadId} not found`), _mastra_core_error.ErrorCategory.USER);
|
|
3893
4071
|
await ctx.db.tx(async (client) => {
|
|
4072
|
+
await assertMemoryFence(ctx, client, fence, "saveMessages");
|
|
3894
4073
|
await insertMessageBatch(ctx, client, messages);
|
|
3895
4074
|
});
|
|
3896
4075
|
return { messages: new _mastra_core_agent.MessageList().add(messages, "memory").get.all.db() };
|
|
@@ -3968,7 +4147,7 @@ async function insertMessageBatch(ctx, client, messages) {
|
|
|
3968
4147
|
threadId: messageThreadId
|
|
3969
4148
|
})));
|
|
3970
4149
|
}
|
|
3971
|
-
async function updateMessages(ctx, { messages }) {
|
|
4150
|
+
async function updateMessages(ctx, { messages, fence }) {
|
|
3972
4151
|
if (messages.length === 0) return [];
|
|
3973
4152
|
const messageIds = messages.map((message) => message.id);
|
|
3974
4153
|
const existingMessages = (await ctx.listMessagesById({ messageIds })).messages;
|
|
@@ -3977,6 +4156,7 @@ async function updateMessages(ctx, { messages }) {
|
|
|
3977
4156
|
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
3978
4157
|
try {
|
|
3979
4158
|
await ctx.db.tx(async (client) => {
|
|
4159
|
+
await assertMemoryFence(ctx, client, fence, "updateMessages");
|
|
3980
4160
|
const semanticRecallMessageIds = [];
|
|
3981
4161
|
for (const updatePayload of messages) {
|
|
3982
4162
|
const existing = existingById.get(updatePayload.id);
|
|
@@ -4023,13 +4203,15 @@ async function updateMessages(ctx, { messages }) {
|
|
|
4023
4203
|
});
|
|
4024
4204
|
return (await ctx.listMessagesById({ messageIds })).messages;
|
|
4025
4205
|
} catch (error) {
|
|
4206
|
+
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
4026
4207
|
throw storageError$1("UPDATE_MESSAGES", "FAILED", { messageIds: messageIds.join(",") }, error);
|
|
4027
4208
|
}
|
|
4028
4209
|
}
|
|
4029
|
-
async function deleteMessages(ctx, messageIds) {
|
|
4210
|
+
async function deleteMessages(ctx, messageIds, fence) {
|
|
4030
4211
|
if (!messageIds.length) return;
|
|
4031
4212
|
try {
|
|
4032
4213
|
await ctx.db.tx(async (client) => {
|
|
4214
|
+
await assertMemoryFence(ctx, client, fence, "deleteMessages");
|
|
4033
4215
|
const threadIds = /* @__PURE__ */ new Set();
|
|
4034
4216
|
for (const [chunkIndex, chunk] of chunkValues(messageIds).entries()) {
|
|
4035
4217
|
const { sql, binds } = inClause(`messageId${chunkIndex}`, chunk);
|
|
@@ -4045,6 +4227,7 @@ async function deleteMessages(ctx, messageIds) {
|
|
|
4045
4227
|
})));
|
|
4046
4228
|
});
|
|
4047
4229
|
} catch (error) {
|
|
4230
|
+
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
4048
4231
|
throw storageError$1("DELETE_MESSAGES", "FAILED", { messageIds: messageIds.join(",") }, error);
|
|
4049
4232
|
}
|
|
4050
4233
|
}
|
|
@@ -4286,14 +4469,15 @@ function mergeMessageContent(existingContent, updateContent) {
|
|
|
4286
4469
|
}
|
|
4287
4470
|
//#endregion
|
|
4288
4471
|
//#region src/storage/domains/memory/observational.ts
|
|
4472
|
+
/** Hops a write aimed at a retired record may follow to reach the head before giving up. */
|
|
4473
|
+
const OM_MAX_HEAD_HOPS = 3;
|
|
4474
|
+
/** Canonical head order for a lookup key. */
|
|
4475
|
+
const OM_HEAD_ORDER = `${OM_GENERATION_COUNT} DESC, ${OM_CREATED_AT} ASC, id ASC`;
|
|
4289
4476
|
async function getObservationalMemory(ctx, threadId, resourceId) {
|
|
4290
4477
|
try {
|
|
4291
4478
|
const lookupKey = getOMKey(threadId, resourceId);
|
|
4292
4479
|
return await ctx.db.withConnection(async (connection) => {
|
|
4293
|
-
const row =
|
|
4294
|
-
WHERE ${OM_LOOKUP_KEY} = :lookupKey
|
|
4295
|
-
ORDER BY ${OM_GENERATION_COUNT} DESC
|
|
4296
|
-
FETCH FIRST 1 ROWS ONLY`, asBindParameters({ lookupKey }), executeOptions()))[0];
|
|
4480
|
+
const row = await getHeadOMRow(ctx, connection, lookupKey);
|
|
4297
4481
|
return row ? parseOMRow(row) : null;
|
|
4298
4482
|
});
|
|
4299
4483
|
} catch (error) {
|
|
@@ -4325,11 +4509,11 @@ async function getObservationalMemoryHistory(ctx, threadId, resourceId, limit =
|
|
|
4325
4509
|
}
|
|
4326
4510
|
if (options?.from) {
|
|
4327
4511
|
conditions.push(`${OM_CREATED_AT} >= :fromDate`);
|
|
4328
|
-
binds.fromDate = options.from;
|
|
4512
|
+
binds.fromDate = timestampBind(options.from);
|
|
4329
4513
|
}
|
|
4330
4514
|
if (options?.to) {
|
|
4331
4515
|
conditions.push(`${OM_CREATED_AT} <= :toDate`);
|
|
4332
|
-
binds.toDate = options.to;
|
|
4516
|
+
binds.toDate = timestampBind(options.to);
|
|
4333
4517
|
}
|
|
4334
4518
|
if (options?.groupId !== void 0) {
|
|
4335
4519
|
conditions.push(`(DBMS_LOB.INSTR(${OM_ACTIVE_OBSERVATIONS}, :groupPrefix) > 0 OR EXISTS (
|
|
@@ -4368,6 +4552,7 @@ async function getObservationalMemoryHistory(ctx, threadId, resourceId, limit =
|
|
|
4368
4552
|
}
|
|
4369
4553
|
async function initializeObservationalMemory(ctx, input) {
|
|
4370
4554
|
const now = /* @__PURE__ */ new Date();
|
|
4555
|
+
const lookupKey = getOMKey(input.threadId, input.resourceId);
|
|
4371
4556
|
const record = {
|
|
4372
4557
|
id: globalThis.crypto.randomUUID(),
|
|
4373
4558
|
scope: input.scope,
|
|
@@ -4389,13 +4574,22 @@ async function initializeObservationalMemory(ctx, input) {
|
|
|
4389
4574
|
lastBufferedAtTokens: 0,
|
|
4390
4575
|
lastBufferedAtTime: null,
|
|
4391
4576
|
config: input.config,
|
|
4392
|
-
observedTimezone: input.observedTimezone
|
|
4577
|
+
observedTimezone: input.observedTimezone,
|
|
4578
|
+
supersededBy: null
|
|
4393
4579
|
};
|
|
4394
4580
|
try {
|
|
4395
|
-
await ctx.
|
|
4581
|
+
const existing = await getObservationalMemory(ctx, input.threadId, input.resourceId);
|
|
4582
|
+
if (existing) return existing;
|
|
4583
|
+
if (await ctx.db.tx(async (_client, connection) => {
|
|
4584
|
+
const omTable = table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY);
|
|
4585
|
+
await connection.execute(`LOCK TABLE ${omTable} IN SHARE ROW EXCLUSIVE MODE`);
|
|
4586
|
+
if ((await connection.execute(`SELECT 1 FROM ${omTable} WHERE "lookupKey" = :lookupKey AND "supersededBy" IS NULL FETCH FIRST 1 ROWS ONLY`, { lookupKey })).rows?.length) return false;
|
|
4396
4587
|
await insertOMRecord(ctx, connection, record);
|
|
4397
|
-
|
|
4398
|
-
return record;
|
|
4588
|
+
return true;
|
|
4589
|
+
})) return record;
|
|
4590
|
+
const head = await getObservationalMemory(ctx, input.threadId, input.resourceId);
|
|
4591
|
+
if (!head) throw new Error(`Observational memory record not found after initialization: ${lookupKey}`);
|
|
4592
|
+
return head;
|
|
4399
4593
|
} catch (error) {
|
|
4400
4594
|
throw storageError$1("INITIALIZE_OBSERVATIONAL_MEMORY", "FAILED", {
|
|
4401
4595
|
threadId: input.threadId ?? "",
|
|
@@ -4415,9 +4609,20 @@ async function insertObservationalMemoryRecord(ctx, record) {
|
|
|
4415
4609
|
}, error);
|
|
4416
4610
|
}
|
|
4417
4611
|
}
|
|
4418
|
-
async function updateActiveObservations(ctx, input) {
|
|
4612
|
+
async function updateActiveObservations(ctx, input, fence) {
|
|
4419
4613
|
try {
|
|
4420
|
-
await ctx.db.tx(async (
|
|
4614
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
4615
|
+
await assertMemoryFence(ctx, client, fence, "commitActiveObservations");
|
|
4616
|
+
const row = await lockOMRow(ctx, connection, input.id, "UPDATE_ACTIVE_OBSERVATIONS");
|
|
4617
|
+
if (row.supersededBy) return {
|
|
4618
|
+
applied: false,
|
|
4619
|
+
reason: "retired"
|
|
4620
|
+
};
|
|
4621
|
+
if (input.expectedActiveObservations !== void 0 && input.expectedActiveObservations !== stringOrEmpty(row.activeObservations)) return {
|
|
4622
|
+
applied: false,
|
|
4623
|
+
reason: "conflict"
|
|
4624
|
+
};
|
|
4625
|
+
const lastObservedAt = maxObservationCursor(row.lastObservedAt, input.lastObservedAt);
|
|
4421
4626
|
assertRowsAffected((await connection.execute(`
|
|
4422
4627
|
UPDATE ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)}
|
|
4423
4628
|
SET ${OM_ACTIVE_OBSERVATIONS} = :activeObservations,
|
|
@@ -4431,52 +4636,88 @@ async function updateActiveObservations(ctx, input) {
|
|
|
4431
4636
|
WHERE id = :id`, {
|
|
4432
4637
|
id: input.id,
|
|
4433
4638
|
activeObservations: nullableClobBind(input.observations),
|
|
4434
|
-
lastObservedAt:
|
|
4639
|
+
lastObservedAt: timestampBind(lastObservedAt),
|
|
4435
4640
|
tokenCount: Math.round(input.tokenCount),
|
|
4436
4641
|
observedMessageIds: nullableJsonBind(input.observedMessageIds),
|
|
4437
4642
|
observedTimezone: input.observedTimezone ?? null,
|
|
4438
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
4643
|
+
updatedAt: timestampBind(/* @__PURE__ */ new Date())
|
|
4439
4644
|
})).rowsAffected, "UPDATE_ACTIVE_OBSERVATIONS", input.id);
|
|
4645
|
+
return { applied: true };
|
|
4440
4646
|
});
|
|
4441
4647
|
} catch (error) {
|
|
4442
4648
|
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
4443
4649
|
throw storageError$1("UPDATE_ACTIVE_OBSERVATIONS", "FAILED", { id: input.id }, error);
|
|
4444
4650
|
}
|
|
4445
4651
|
}
|
|
4446
|
-
async function createReflectionGeneration(ctx, input) {
|
|
4652
|
+
async function createReflectionGeneration(ctx, input, fence) {
|
|
4653
|
+
const { currentRecord } = input;
|
|
4654
|
+
try {
|
|
4655
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
4656
|
+
await assertMemoryFence(ctx, client, fence, "createReflectionGeneration");
|
|
4657
|
+
const row = await findOMRowForUpdate(ctx, connection, currentRecord.id);
|
|
4658
|
+
if (!row) return currentRecord;
|
|
4659
|
+
if (row.supersededBy) return parseOMRow(await resolveLiveOMRow(ctx, connection, row));
|
|
4660
|
+
const stored = parseOMRow(row);
|
|
4661
|
+
const plan = planReflectionGenerationText({
|
|
4662
|
+
storedObservations: stored.activeObservations,
|
|
4663
|
+
storedObservationTokenCount: stored.observationTokenCount,
|
|
4664
|
+
snapshotObservations: currentRecord.activeObservations,
|
|
4665
|
+
snapshotObservationTokenCount: currentRecord.observationTokenCount,
|
|
4666
|
+
reflection: input.reflection,
|
|
4667
|
+
tokenCount: input.tokenCount
|
|
4668
|
+
});
|
|
4669
|
+
if (!plan) return stored;
|
|
4670
|
+
return rollOverOMRow(ctx, connection, stored, plan.observations, plan.tokenCount, input.newRecordId);
|
|
4671
|
+
});
|
|
4672
|
+
} catch (error) {
|
|
4673
|
+
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
4674
|
+
throw storageError$1("CREATE_REFLECTION_GENERATION", "FAILED", { id: currentRecord.id }, error);
|
|
4675
|
+
}
|
|
4676
|
+
}
|
|
4677
|
+
/**
|
|
4678
|
+
* Create the next generation from the stored (live, row-locked) record and retire it in the
|
|
4679
|
+
* same transaction. Buffered chunks move to the new generation; the cursor, buffering markers,
|
|
4680
|
+
* flags, and counters carry over; buffered reflection state does not.
|
|
4681
|
+
*/
|
|
4682
|
+
async function rollOverOMRow(ctx, connection, stored, observations, tokenCount, newRecordId) {
|
|
4447
4683
|
const now = /* @__PURE__ */ new Date();
|
|
4448
4684
|
const record = {
|
|
4449
|
-
id: globalThis.crypto.randomUUID(),
|
|
4450
|
-
scope:
|
|
4451
|
-
threadId:
|
|
4452
|
-
resourceId:
|
|
4685
|
+
id: newRecordId ?? globalThis.crypto.randomUUID(),
|
|
4686
|
+
scope: stored.scope,
|
|
4687
|
+
threadId: stored.threadId,
|
|
4688
|
+
resourceId: stored.resourceId,
|
|
4453
4689
|
createdAt: now,
|
|
4454
4690
|
updatedAt: now,
|
|
4455
|
-
lastObservedAt:
|
|
4691
|
+
lastObservedAt: stored.lastObservedAt,
|
|
4456
4692
|
originType: "reflection",
|
|
4457
|
-
generationCount:
|
|
4458
|
-
activeObservations:
|
|
4459
|
-
totalTokensObserved:
|
|
4460
|
-
observationTokenCount: Math.round(
|
|
4461
|
-
pendingMessageTokens:
|
|
4693
|
+
generationCount: stored.generationCount + 1,
|
|
4694
|
+
activeObservations: observations,
|
|
4695
|
+
totalTokensObserved: stored.totalTokensObserved,
|
|
4696
|
+
observationTokenCount: Math.round(tokenCount),
|
|
4697
|
+
pendingMessageTokens: stored.pendingMessageTokens,
|
|
4462
4698
|
isReflecting: false,
|
|
4463
4699
|
isObserving: false,
|
|
4464
|
-
isBufferingObservation:
|
|
4700
|
+
isBufferingObservation: stored.isBufferingObservation,
|
|
4465
4701
|
isBufferingReflection: false,
|
|
4466
|
-
lastBufferedAtTokens:
|
|
4467
|
-
lastBufferedAtTime: null,
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4702
|
+
lastBufferedAtTokens: stored.lastBufferedAtTokens,
|
|
4703
|
+
lastBufferedAtTime: stored.lastBufferedAtTime ?? null,
|
|
4704
|
+
bufferedObservationChunks: stored.bufferedObservationChunks?.length ? stored.bufferedObservationChunks : void 0,
|
|
4705
|
+
config: stored.config,
|
|
4706
|
+
metadata: stored.metadata,
|
|
4707
|
+
observedTimezone: stored.observedTimezone,
|
|
4708
|
+
supersededBy: null
|
|
4471
4709
|
};
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4710
|
+
await insertOMRecord(ctx, connection, record, now);
|
|
4711
|
+
if ((await connection.execute(`UPDATE ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)}
|
|
4712
|
+
SET "supersededBy" = :newId,
|
|
4713
|
+
"bufferedObservationChunks" = NULL,
|
|
4714
|
+
"updatedAt" = :updatedAt
|
|
4715
|
+
WHERE id = :id AND "supersededBy" IS NULL`, {
|
|
4716
|
+
newId: record.id,
|
|
4717
|
+
updatedAt: timestampBind(now),
|
|
4718
|
+
id: stored.id
|
|
4719
|
+
})).rowsAffected !== 1) throw storageError$1("CREATE_REFLECTION_GENERATION", "RETIRE_FAILED", { id: stored.id }, /* @__PURE__ */ new Error(`Failed to retire observational memory record ${stored.id}`));
|
|
4720
|
+
return record;
|
|
4480
4721
|
}
|
|
4481
4722
|
async function setReflectingFlag(ctx, id, isReflecting) {
|
|
4482
4723
|
await updateOMFlag(ctx, id, OM_IS_REFLECTING, isReflecting, "SET_REFLECTING_FLAG");
|
|
@@ -4491,9 +4732,10 @@ async function setBufferingObservationFlag(ctx, id, isBuffering, lastBufferedAtT
|
|
|
4491
4732
|
const binds = {
|
|
4492
4733
|
id,
|
|
4493
4734
|
isBuffering: boolToNumber(isBuffering),
|
|
4494
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
4735
|
+
updatedAt: timestampBind(/* @__PURE__ */ new Date())
|
|
4495
4736
|
};
|
|
4496
4737
|
if (lastBufferedAtTokens !== void 0) binds.lastBufferedAtTokens = Math.round(lastBufferedAtTokens);
|
|
4738
|
+
binds.id = (await resolveLiveOMRow(ctx, connection, await lockOMRow(ctx, connection, id, "SET_BUFFERING_OBSERVATION_FLAG"))).id;
|
|
4497
4739
|
assertRowsAffected((await connection.execute(`UPDATE ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)}
|
|
4498
4740
|
SET ${OM_IS_BUFFERING_OBSERVATION} = :isBuffering,
|
|
4499
4741
|
${OM_UPDATED_AT} = :updatedAt
|
|
@@ -4524,7 +4766,17 @@ async function clearObservationalMemory(ctx, threadId, resourceId) {
|
|
|
4524
4766
|
}
|
|
4525
4767
|
async function setPendingMessageTokens(ctx, id, tokenCount) {
|
|
4526
4768
|
try {
|
|
4527
|
-
await
|
|
4769
|
+
await ctx.db.tx(async (_client, connection) => {
|
|
4770
|
+
const target = await resolveLiveOMRow(ctx, connection, await lockOMRow(ctx, connection, id, "SET_PENDING_MESSAGE_TOKENS"));
|
|
4771
|
+
assertRowsAffected((await connection.execute(`UPDATE ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)}
|
|
4772
|
+
SET ${OM_PENDING_MESSAGE_TOKENS} = :tokenCount,
|
|
4773
|
+
${OM_UPDATED_AT} = :updatedAt
|
|
4774
|
+
WHERE id = :id`, {
|
|
4775
|
+
tokenCount: Math.round(tokenCount),
|
|
4776
|
+
updatedAt: timestampBind(/* @__PURE__ */ new Date()),
|
|
4777
|
+
id: target.id
|
|
4778
|
+
})).rowsAffected, "SET_PENDING_MESSAGE_TOKENS", id);
|
|
4779
|
+
});
|
|
4528
4780
|
} catch (error) {
|
|
4529
4781
|
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
4530
4782
|
throw storageError$1("SET_PENDING_MESSAGE_TOKENS", "FAILED", {
|
|
@@ -4546,7 +4798,7 @@ async function updateObservationalMemoryConfig(ctx, input) {
|
|
|
4546
4798
|
WHERE id = :id`, {
|
|
4547
4799
|
id: input.id,
|
|
4548
4800
|
config: jsonBind(merged),
|
|
4549
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
4801
|
+
updatedAt: timestampBind(/* @__PURE__ */ new Date())
|
|
4550
4802
|
})).rowsAffected, "UPDATE_OM_CONFIG", input.id);
|
|
4551
4803
|
});
|
|
4552
4804
|
} catch (error) {
|
|
@@ -4592,6 +4844,7 @@ async function insertOMRecord(ctx, connection, record, timestamp = record.create
|
|
|
4592
4844
|
${OM_LAST_BUFFERED_AT_TOKENS},
|
|
4593
4845
|
${OM_LAST_BUFFERED_AT_TIME},
|
|
4594
4846
|
metadata,
|
|
4847
|
+
${OM_SUPERSEDED_BY},
|
|
4595
4848
|
${OM_CREATED_AT},
|
|
4596
4849
|
${OM_UPDATED_AT}
|
|
4597
4850
|
) VALUES (
|
|
@@ -4627,6 +4880,7 @@ async function insertOMRecord(ctx, connection, record, timestamp = record.create
|
|
|
4627
4880
|
:lastBufferedAtTokens,
|
|
4628
4881
|
:lastBufferedAtTime,
|
|
4629
4882
|
:metadata,
|
|
4883
|
+
:supersededBy,
|
|
4630
4884
|
:createdAt,
|
|
4631
4885
|
:updatedAt
|
|
4632
4886
|
)`, {
|
|
@@ -4640,8 +4894,8 @@ async function insertOMRecord(ctx, connection, record, timestamp = record.create
|
|
|
4640
4894
|
originType: record.originType ?? "initial",
|
|
4641
4895
|
config: jsonBind(record.config ?? {}),
|
|
4642
4896
|
generationCount: record.generationCount ?? 0,
|
|
4643
|
-
lastObservedAt: record.lastObservedAt
|
|
4644
|
-
lastReflectionAt: record.originType === "reflection" ? timestamp : null,
|
|
4897
|
+
lastObservedAt: timestampBind(record.lastObservedAt),
|
|
4898
|
+
lastReflectionAt: record.originType === "reflection" ? timestampBind(timestamp) : null,
|
|
4645
4899
|
pendingMessageTokens: Math.round(record.pendingMessageTokens ?? 0),
|
|
4646
4900
|
totalTokensObserved: Math.round(record.totalTokensObserved ?? 0),
|
|
4647
4901
|
observationTokenCount: Math.round(record.observationTokenCount ?? 0),
|
|
@@ -4660,10 +4914,11 @@ async function insertOMRecord(ctx, connection, record, timestamp = record.create
|
|
|
4660
4914
|
isBufferingObservation: boolToNumber(record.isBufferingObservation),
|
|
4661
4915
|
isBufferingReflection: boolToNumber(record.isBufferingReflection),
|
|
4662
4916
|
lastBufferedAtTokens: Math.round(record.lastBufferedAtTokens ?? 0),
|
|
4663
|
-
lastBufferedAtTime: record.lastBufferedAtTime
|
|
4917
|
+
lastBufferedAtTime: timestampBind(record.lastBufferedAtTime),
|
|
4664
4918
|
metadata: nullableJsonBind(record.metadata),
|
|
4665
|
-
|
|
4666
|
-
|
|
4919
|
+
supersededBy: record.supersededBy ?? null,
|
|
4920
|
+
createdAt: timestampBind(record.createdAt),
|
|
4921
|
+
updatedAt: timestampBind(record.updatedAt)
|
|
4667
4922
|
});
|
|
4668
4923
|
}
|
|
4669
4924
|
async function updateOMFlag(ctx, id, column, value, operation) {
|
|
@@ -4687,7 +4942,7 @@ async function updateOMColumns(ctx, id, operation, columns) {
|
|
|
4687
4942
|
WHERE id = :id`, {
|
|
4688
4943
|
...binds,
|
|
4689
4944
|
id,
|
|
4690
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
4945
|
+
updatedAt: timestampBind(/* @__PURE__ */ new Date())
|
|
4691
4946
|
})).rowsAffected, operation, id);
|
|
4692
4947
|
});
|
|
4693
4948
|
}
|
|
@@ -4725,6 +4980,7 @@ function omSelect() {
|
|
|
4725
4980
|
${OM_LAST_BUFFERED_AT_TOKENS} AS "lastBufferedAtTokens",
|
|
4726
4981
|
${OM_LAST_BUFFERED_AT_TIME} AS "lastBufferedAtTime",
|
|
4727
4982
|
metadata AS "metadata",
|
|
4983
|
+
${OM_SUPERSEDED_BY} AS "supersededBy",
|
|
4728
4984
|
${OM_CREATED_AT} AS "createdAt",
|
|
4729
4985
|
${OM_UPDATED_AT} AS "updatedAt"`;
|
|
4730
4986
|
}
|
|
@@ -4760,15 +5016,61 @@ function parseOMRow(row) {
|
|
|
4760
5016
|
config: parseJson(row.config),
|
|
4761
5017
|
metadata: parseOptionalJsonObject(row.metadata, { emptyObjectAsUndefined: true }),
|
|
4762
5018
|
observedMessageIds: parseOptionalStringArray(row.observedMessageIds),
|
|
4763
|
-
observedTimezone: row.observedTimezone ? String(row.observedTimezone) : void 0
|
|
5019
|
+
observedTimezone: row.observedTimezone ? String(row.observedTimezone) : void 0,
|
|
5020
|
+
supersededBy: row.supersededBy ? String(row.supersededBy) : null
|
|
4764
5021
|
};
|
|
4765
5022
|
}
|
|
5023
|
+
/** The canonical head row of a lookup key (non-locking read). */
|
|
5024
|
+
async function getHeadOMRow(ctx, connection, lookupKey) {
|
|
5025
|
+
return rows(await connection.execute(`${omSelect()} FROM ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)}
|
|
5026
|
+
WHERE ${OM_LOOKUP_KEY} = :lookupKey
|
|
5027
|
+
ORDER BY ${OM_HEAD_ORDER}
|
|
5028
|
+
FETCH FIRST 1 ROWS ONLY`, asBindParameters({ lookupKey }), executeOptions()))[0];
|
|
5029
|
+
}
|
|
5030
|
+
/** Row-locks one record by primary key for the rest of the transaction; `undefined` when missing. */
|
|
5031
|
+
async function findOMRowForUpdate(ctx, connection, id) {
|
|
5032
|
+
return rows(await connection.execute(`${omSelect()} FROM ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)} WHERE id = :id FOR UPDATE`, { id }, executeOptions()))[0];
|
|
5033
|
+
}
|
|
5034
|
+
/**
|
|
5035
|
+
* Row-locks one record by primary key. Observational memory updates are incremental and
|
|
5036
|
+
* order-sensitive, so mutating paths derive their next state from a locked row.
|
|
5037
|
+
* Throws "record not found" when it is missing.
|
|
5038
|
+
*/
|
|
5039
|
+
async function lockOMRow(ctx, connection, id, operation) {
|
|
5040
|
+
const row = await findOMRowForUpdate(ctx, connection, id);
|
|
5041
|
+
if (!row) {
|
|
5042
|
+
assertRowsAffected(0, operation, id);
|
|
5043
|
+
throw new Error(`Observational memory record not found: ${id}`);
|
|
5044
|
+
}
|
|
5045
|
+
return row;
|
|
5046
|
+
}
|
|
5047
|
+
/**
|
|
5048
|
+
* The live, row-locked record a lifecycle write aimed at `row` lands on: `row` itself while
|
|
5049
|
+
* live, otherwise the head of its lookup key (locked, then re-checked — a rollover may retire
|
|
5050
|
+
* it between the head read and the lock).
|
|
5051
|
+
*/
|
|
5052
|
+
async function resolveLiveOMRow(ctx, connection, row) {
|
|
5053
|
+
let current = row;
|
|
5054
|
+
for (let hop = 0; current.supersededBy; hop++) {
|
|
5055
|
+
const head = hop < OM_MAX_HEAD_HOPS ? await getHeadOMRow(ctx, connection, current.lookupKey) : void 0;
|
|
5056
|
+
const locked = head && head.id !== current.id ? await findOMRowForUpdate(ctx, connection, head.id) : void 0;
|
|
5057
|
+
if (!locked) throw storageError$1("RESOLVE_OBSERVATIONAL_MEMORY_HEAD", "FAILED", { id: row.id }, /* @__PURE__ */ new Error(`Observational memory record ${row.id} is superseded but no live head was found`));
|
|
5058
|
+
current = locked;
|
|
5059
|
+
}
|
|
5060
|
+
return current;
|
|
5061
|
+
}
|
|
4766
5062
|
//#endregion
|
|
4767
5063
|
//#region src/storage/domains/memory/observational-buffering.ts
|
|
4768
|
-
async function updateBufferedObservations(ctx, input) {
|
|
5064
|
+
async function updateBufferedObservations(ctx, input, fence) {
|
|
4769
5065
|
try {
|
|
4770
|
-
await ctx.db.tx(async (
|
|
4771
|
-
|
|
5066
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
5067
|
+
await assertMemoryFence(ctx, client, fence, "appendBufferedObservations");
|
|
5068
|
+
const row = await resolveLiveOMRow(ctx, connection, await lockOMRow(ctx, connection, input.id, "UPDATE_BUFFERED_OBSERVATIONS"));
|
|
5069
|
+
const existingChunks = parseBufferedChunks(row.bufferedObservationChunks);
|
|
5070
|
+
if (existingChunks.some((existing) => existing.cycleId === input.chunk.cycleId) || isBufferedChunkCoveredByCursor(input.chunk.lastObservedAt, row.lastObservedAt)) return {
|
|
5071
|
+
persisted: false,
|
|
5072
|
+
recordId: row.id
|
|
5073
|
+
};
|
|
4772
5074
|
const newChunk = {
|
|
4773
5075
|
id: `ombuf-${globalThis.crypto.randomUUID()}`,
|
|
4774
5076
|
cycleId: input.chunk.cycleId,
|
|
@@ -4784,32 +5086,48 @@ async function updateBufferedObservations(ctx, input) {
|
|
|
4784
5086
|
extractedValues: input.chunk.extractedValues,
|
|
4785
5087
|
extractionFailures: input.chunk.extractionFailures
|
|
4786
5088
|
};
|
|
4787
|
-
const
|
|
4788
|
-
const lastBufferedAtTimeSql = input.lastBufferedAtTime === void 0 || input.lastBufferedAtTime === null ? "" : `,\n ${OM_LAST_BUFFERED_AT_TIME} = :lastBufferedAtTime`;
|
|
4789
|
-
const binds = {
|
|
4790
|
-
id: input.id,
|
|
4791
|
-
bufferedObservationChunks: nullableJsonBind(updatedChunks),
|
|
4792
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
4793
|
-
};
|
|
4794
|
-
if (input.lastBufferedAtTime !== void 0 && input.lastBufferedAtTime !== null) binds.lastBufferedAtTime = toDate(input.lastBufferedAtTime);
|
|
5089
|
+
const lastBufferedAtTime = maxObservationCursor(row.lastBufferedAtTime, input.lastBufferedAtTime);
|
|
4795
5090
|
assertRowsAffected((await connection.execute(`UPDATE ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)}
|
|
4796
5091
|
SET ${OM_BUFFERED_OBSERVATION_CHUNKS} = :bufferedObservationChunks,
|
|
4797
|
-
${
|
|
4798
|
-
|
|
5092
|
+
${OM_LAST_BUFFERED_AT_TIME} = :lastBufferedAtTime,
|
|
5093
|
+
${OM_UPDATED_AT} = :updatedAt
|
|
5094
|
+
WHERE id = :id`, asBindParameters({
|
|
5095
|
+
id: row.id,
|
|
5096
|
+
bufferedObservationChunks: nullableJsonBind([...existingChunks, newChunk]),
|
|
5097
|
+
lastBufferedAtTime: timestampBind(lastBufferedAtTime),
|
|
5098
|
+
updatedAt: timestampBind(/* @__PURE__ */ new Date())
|
|
5099
|
+
}))).rowsAffected, "UPDATE_BUFFERED_OBSERVATIONS", row.id);
|
|
5100
|
+
return {
|
|
5101
|
+
persisted: true,
|
|
5102
|
+
recordId: row.id
|
|
5103
|
+
};
|
|
4799
5104
|
});
|
|
4800
5105
|
} catch (error) {
|
|
4801
5106
|
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
4802
5107
|
throw storageError$1("UPDATE_BUFFERED_OBSERVATIONS", "FAILED", { id: input.id }, error);
|
|
4803
5108
|
}
|
|
4804
5109
|
}
|
|
4805
|
-
async function swapBufferedToActive(ctx, input) {
|
|
5110
|
+
async function swapBufferedToActive(ctx, input, fence) {
|
|
4806
5111
|
try {
|
|
4807
|
-
return await ctx.db.tx(async (
|
|
5112
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
5113
|
+
await assertMemoryFence(ctx, client, fence, "swapBufferedToActive");
|
|
4808
5114
|
const row = await lockOMRow(ctx, connection, input.id, "SWAP_BUFFERED_TO_ACTIVE");
|
|
4809
|
-
|
|
5115
|
+
if (row.supersededBy) return {
|
|
5116
|
+
...emptySwapResult(),
|
|
5117
|
+
retired: true
|
|
5118
|
+
};
|
|
5119
|
+
const refreshedWeights = new Map((input.bufferedChunks ?? []).map((chunk) => [chunk.id, chunk.messageTokens]));
|
|
5120
|
+
const chunks = parseBufferedChunks(row.bufferedObservationChunks).map((chunk) => {
|
|
5121
|
+
const weight = refreshedWeights.get(chunk.id);
|
|
5122
|
+
return weight === void 0 ? chunk : {
|
|
5123
|
+
...chunk,
|
|
5124
|
+
messageTokens: weight
|
|
5125
|
+
};
|
|
5126
|
+
});
|
|
4810
5127
|
if (chunks.length === 0) return emptySwapResult();
|
|
4811
5128
|
const activation = calculateBufferedActivation(chunks, input);
|
|
4812
5129
|
const lastObservedAt = input.lastObservedAt ?? (activation.activatedChunks.at(-1)?.lastObservedAt ? toDate(activation.activatedChunks.at(-1).lastObservedAt) : /* @__PURE__ */ new Date());
|
|
5130
|
+
const storedCursor = maxObservationCursor(row.lastObservedAt, lastObservedAt);
|
|
4813
5131
|
const boundary = `\n\n--- message boundary (${lastObservedAt.toISOString()}) ---\n\n`;
|
|
4814
5132
|
const existingActive = stringOrEmpty(row.activeObservations);
|
|
4815
5133
|
const newActive = existingActive ? `${existingActive}${boundary}${activation.activatedContent}` : activation.activatedContent;
|
|
@@ -4827,8 +5145,8 @@ async function swapBufferedToActive(ctx, input) {
|
|
|
4827
5145
|
observationTokens: activation.activatedTokens,
|
|
4828
5146
|
pendingMessageTokens: pendingTokens,
|
|
4829
5147
|
bufferedObservationChunks: nullableJsonBind(activation.remainingChunks.length > 0 ? activation.remainingChunks : null),
|
|
4830
|
-
lastObservedAt,
|
|
4831
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
5148
|
+
lastObservedAt: timestampBind(storedCursor),
|
|
5149
|
+
updatedAt: timestampBind(/* @__PURE__ */ new Date())
|
|
4832
5150
|
})).rowsAffected, "SWAP_BUFFERED_TO_ACTIVE", input.id);
|
|
4833
5151
|
return activation.result;
|
|
4834
5152
|
});
|
|
@@ -4837,9 +5155,10 @@ async function swapBufferedToActive(ctx, input) {
|
|
|
4837
5155
|
throw storageError$1("SWAP_BUFFERED_TO_ACTIVE", "FAILED", { id: input.id }, error);
|
|
4838
5156
|
}
|
|
4839
5157
|
}
|
|
4840
|
-
async function updateBufferedReflection(ctx, input) {
|
|
5158
|
+
async function updateBufferedReflection(ctx, input, fence) {
|
|
4841
5159
|
try {
|
|
4842
|
-
await ctx.db.tx(async (
|
|
5160
|
+
await ctx.db.tx(async (client, connection) => {
|
|
5161
|
+
await assertMemoryFence(ctx, client, fence, "updateBufferedReflection");
|
|
4843
5162
|
assertRowsAffected((await connection.execute(`UPDATE ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)}
|
|
4844
5163
|
SET ${OM_BUFFERED_REFLECTION} = CASE
|
|
4845
5164
|
WHEN ${OM_BUFFERED_REFLECTION} IS NOT NULL AND DBMS_LOB.GETLENGTH(${OM_BUFFERED_REFLECTION}) > 0
|
|
@@ -4856,7 +5175,7 @@ async function updateBufferedReflection(ctx, input) {
|
|
|
4856
5175
|
tokenCount: Math.round(input.tokenCount),
|
|
4857
5176
|
inputTokenCount: Math.round(input.inputTokenCount),
|
|
4858
5177
|
reflectedObservationLineCount: Math.round(input.reflectedObservationLineCount),
|
|
4859
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
5178
|
+
updatedAt: timestampBind(/* @__PURE__ */ new Date())
|
|
4860
5179
|
})).rowsAffected, "UPDATE_BUFFERED_REFLECTION", input.id);
|
|
4861
5180
|
});
|
|
4862
5181
|
} catch (error) {
|
|
@@ -4864,67 +5183,34 @@ async function updateBufferedReflection(ctx, input) {
|
|
|
4864
5183
|
throw storageError$1("UPDATE_BUFFERED_REFLECTION", "FAILED", { id: input.id }, error);
|
|
4865
5184
|
}
|
|
4866
5185
|
}
|
|
4867
|
-
async function swapBufferedReflectionToActive(ctx, input) {
|
|
5186
|
+
async function swapBufferedReflectionToActive(ctx, input, fence) {
|
|
5187
|
+
const { currentRecord } = input;
|
|
4868
5188
|
try {
|
|
4869
|
-
return await ctx.db.tx(async (
|
|
4870
|
-
|
|
4871
|
-
const
|
|
4872
|
-
if (!
|
|
4873
|
-
|
|
4874
|
-
const
|
|
4875
|
-
const
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
const
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
threadId: lockedRecord.threadId,
|
|
4882
|
-
resourceId: lockedRecord.resourceId,
|
|
4883
|
-
createdAt: now,
|
|
4884
|
-
updatedAt: now,
|
|
4885
|
-
lastObservedAt: lockedRecord.lastObservedAt,
|
|
4886
|
-
originType: "reflection",
|
|
4887
|
-
generationCount: lockedRecord.generationCount + 1,
|
|
4888
|
-
activeObservations: newObservations,
|
|
4889
|
-
totalTokensObserved: lockedRecord.totalTokensObserved,
|
|
4890
|
-
observationTokenCount: Math.round(input.tokenCount),
|
|
4891
|
-
pendingMessageTokens: 0,
|
|
4892
|
-
isReflecting: false,
|
|
4893
|
-
isObserving: false,
|
|
4894
|
-
isBufferingObservation: false,
|
|
4895
|
-
isBufferingReflection: false,
|
|
4896
|
-
lastBufferedAtTokens: 0,
|
|
4897
|
-
lastBufferedAtTime: null,
|
|
4898
|
-
config: lockedRecord.config,
|
|
4899
|
-
metadata: lockedRecord.metadata,
|
|
4900
|
-
observedTimezone: lockedRecord.observedTimezone
|
|
4901
|
-
};
|
|
4902
|
-
await insertOMRecord(ctx, connection, newRecord, now);
|
|
5189
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
5190
|
+
await assertMemoryFence(ctx, client, fence, "swapBufferedReflectionToActive");
|
|
5191
|
+
const row = await findOMRowForUpdate(ctx, connection, currentRecord.id);
|
|
5192
|
+
if (!row) return currentRecord;
|
|
5193
|
+
if (row.supersededBy) return parseOMRow(await resolveLiveOMRow(ctx, connection, row));
|
|
5194
|
+
const stored = parseOMRow(row);
|
|
5195
|
+
const bufferedReflection = stored.bufferedReflection ?? "";
|
|
5196
|
+
if (!bufferedReflection) throw storageError$1("SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NO_CONTENT", { id: currentRecord.id }, /* @__PURE__ */ new Error("No buffered reflection to swap"), _mastra_core_error.ErrorCategory.USER);
|
|
5197
|
+
if (!isAppendOnlySince(stored.activeObservations, currentRecord.activeObservations)) return stored;
|
|
5198
|
+
const reflectedLineCount = stored.reflectedObservationLineCount ?? 0;
|
|
5199
|
+
const unreflectedContent = stored.activeObservations.split("\n").slice(reflectedLineCount).join("\n").trim();
|
|
5200
|
+
const newRecord = await rollOverOMRow(ctx, connection, stored, unreflectedContent ? `${bufferedReflection}\n\n${unreflectedContent}` : bufferedReflection, input.tokenCount + Math.max(0, stored.observationTokenCount - (currentRecord.observationTokenCount ?? 0)), input.newRecordId);
|
|
4903
5201
|
assertRowsAffected((await connection.execute(`UPDATE ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)}
|
|
4904
5202
|
SET ${OM_BUFFERED_REFLECTION} = NULL,
|
|
4905
5203
|
${OM_BUFFERED_REFLECTION_TOKENS} = NULL,
|
|
4906
5204
|
${OM_BUFFERED_REFLECTION_INPUT_TOKENS} = NULL,
|
|
4907
|
-
${OM_REFLECTED_OBSERVATION_LINE_COUNT} = NULL
|
|
4908
|
-
|
|
4909
|
-
WHERE id = :id`, {
|
|
4910
|
-
id: input.currentRecord.id,
|
|
4911
|
-
updatedAt: now
|
|
4912
|
-
})).rowsAffected, "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", input.currentRecord.id);
|
|
5205
|
+
${OM_REFLECTED_OBSERVATION_LINE_COUNT} = NULL
|
|
5206
|
+
WHERE id = :id`, { id: stored.id })).rowsAffected, "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", stored.id);
|
|
4913
5207
|
return newRecord;
|
|
4914
5208
|
});
|
|
4915
5209
|
} catch (error) {
|
|
4916
5210
|
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
4917
|
-
throw storageError$1("SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "FAILED", { id:
|
|
5211
|
+
throw storageError$1("SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "FAILED", { id: currentRecord.id }, error);
|
|
4918
5212
|
}
|
|
4919
5213
|
}
|
|
4920
|
-
async function lockOMRow(ctx, connection, id, operation) {
|
|
4921
|
-
const row = rows(await connection.execute(`${omSelect()} FROM ${table(ctx, _mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY)} WHERE id = :id FOR UPDATE`, { id }, executeOptions()))[0];
|
|
4922
|
-
if (!row) {
|
|
4923
|
-
assertRowsAffected(0, operation, id);
|
|
4924
|
-
throw new Error(`Observational memory record not found: ${id}`);
|
|
4925
|
-
}
|
|
4926
|
-
return row;
|
|
4927
|
-
}
|
|
4928
5214
|
function emptySwapResult() {
|
|
4929
5215
|
return {
|
|
4930
5216
|
chunksActivated: 0,
|
|
@@ -5054,17 +5340,24 @@ async function saveResource(ctx, { resource }) {
|
|
|
5054
5340
|
throw storageError$1("SAVE_RESOURCE", "FAILED", { resourceId: resource.id }, error);
|
|
5055
5341
|
}
|
|
5056
5342
|
}
|
|
5057
|
-
async function updateResource(ctx, { resourceId, workingMemory, metadata }) {
|
|
5343
|
+
async function updateResource(ctx, { resourceId, workingMemory, metadata, fence }) {
|
|
5058
5344
|
const existing = await ctx.getResourceById({ resourceId });
|
|
5059
|
-
|
|
5345
|
+
const newResource = () => ({
|
|
5060
5346
|
id: resourceId,
|
|
5061
5347
|
workingMemory,
|
|
5062
5348
|
metadata: metadata ?? {},
|
|
5063
5349
|
createdAt: /* @__PURE__ */ new Date(),
|
|
5064
5350
|
updatedAt: /* @__PURE__ */ new Date()
|
|
5065
|
-
}
|
|
5351
|
+
});
|
|
5352
|
+
if (!existing && !fence) return ctx.saveResource({ resource: newResource() });
|
|
5066
5353
|
try {
|
|
5067
|
-
return await ctx.db.tx(async (
|
|
5354
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
5355
|
+
await assertMemoryFence(ctx, client, fence, "updateResource");
|
|
5356
|
+
if (!existing) {
|
|
5357
|
+
const resource = newResource();
|
|
5358
|
+
await mergeResourceRow(ctx, connection, resource);
|
|
5359
|
+
return resource;
|
|
5360
|
+
}
|
|
5068
5361
|
const row = rows(await connection.execute(`${resourceSelect()} FROM ${table(ctx, _mastra_core_storage.TABLE_RESOURCES)} WHERE id = :resourceId FOR UPDATE`, { resourceId }, executeOptions()))[0];
|
|
5069
5362
|
const locked = row ? parseResource(row) : existing;
|
|
5070
5363
|
const updated = {
|
|
@@ -5158,11 +5451,15 @@ async function getThreadById(ctx, { threadId, resourceId }) {
|
|
|
5158
5451
|
throw storageError$1("GET_THREAD_BY_ID", "FAILED", { threadId }, error);
|
|
5159
5452
|
}
|
|
5160
5453
|
}
|
|
5161
|
-
async function saveThread(ctx, { thread }) {
|
|
5454
|
+
async function saveThread(ctx, { thread, fence }) {
|
|
5162
5455
|
try {
|
|
5163
|
-
await
|
|
5456
|
+
await ctx.db.tx(async (client) => {
|
|
5457
|
+
await assertMemoryFence(ctx, client, fence, "saveThread");
|
|
5458
|
+
await mergeThreadRow(ctx, client, thread);
|
|
5459
|
+
});
|
|
5164
5460
|
return thread;
|
|
5165
5461
|
} catch (error) {
|
|
5462
|
+
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
5166
5463
|
throw storageError$1("SAVE_THREAD", "FAILED", { threadId: thread.id }, error);
|
|
5167
5464
|
}
|
|
5168
5465
|
}
|
|
@@ -5247,7 +5544,7 @@ async function insertThreadRow(ctx, client, thread) {
|
|
|
5247
5544
|
updatedAt: thread.updatedAt ?? /* @__PURE__ */ new Date()
|
|
5248
5545
|
});
|
|
5249
5546
|
}
|
|
5250
|
-
async function updateThread(ctx, { id, title, metadata }) {
|
|
5547
|
+
async function updateThread(ctx, { id, title, metadata, fence }) {
|
|
5251
5548
|
const existingThread = await ctx.getThreadById({ threadId: id });
|
|
5252
5549
|
if (!existingThread) throw storageError$1("UPDATE_THREAD", "FAILED", {
|
|
5253
5550
|
threadId: id,
|
|
@@ -5259,16 +5556,19 @@ async function updateThread(ctx, { id, title, metadata }) {
|
|
|
5259
5556
|
};
|
|
5260
5557
|
const updatedAt = /* @__PURE__ */ new Date();
|
|
5261
5558
|
try {
|
|
5262
|
-
await ctx.db.
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5559
|
+
await ctx.db.tx(async (client) => {
|
|
5560
|
+
await assertMemoryFence(ctx, client, fence, "updateThread");
|
|
5561
|
+
await client.none(`
|
|
5562
|
+
UPDATE ${table(ctx, _mastra_core_storage.TABLE_THREADS)}
|
|
5563
|
+
SET title = COALESCE(:title, title),
|
|
5564
|
+
metadata = :metadata,
|
|
5565
|
+
${THREAD_UPDATED_AT} = :updatedAt
|
|
5566
|
+
WHERE id = :id`, {
|
|
5567
|
+
id,
|
|
5568
|
+
title: optionalStringBind(title),
|
|
5569
|
+
metadata: jsonBind(mergedMetadata),
|
|
5570
|
+
updatedAt
|
|
5571
|
+
});
|
|
5272
5572
|
});
|
|
5273
5573
|
const updatedThread = await ctx.getThreadById({ threadId: id });
|
|
5274
5574
|
if (!updatedThread) throw storageError$1("UPDATE_THREAD", "FAILED", {
|
|
@@ -5420,7 +5720,8 @@ var MemoryOracle = class MemoryOracle extends _mastra_core_storage.MemoryStorage
|
|
|
5420
5720
|
_mastra_core_storage.TABLE_THREADS,
|
|
5421
5721
|
_mastra_core_storage.TABLE_MESSAGES,
|
|
5422
5722
|
_mastra_core_storage.TABLE_RESOURCES,
|
|
5423
|
-
_mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY
|
|
5723
|
+
_mastra_core_storage.TABLE_OBSERVATIONAL_MEMORY,
|
|
5724
|
+
_mastra_core_storage.TABLE_MEMORY_RUN_FENCES
|
|
5424
5725
|
];
|
|
5425
5726
|
db;
|
|
5426
5727
|
schemaName;
|
|
@@ -5458,18 +5759,77 @@ var MemoryOracle = class MemoryOracle extends _mastra_core_storage.MemoryStorage
|
|
|
5458
5759
|
}
|
|
5459
5760
|
async init() {
|
|
5460
5761
|
await initMemorySchema(this.ctx);
|
|
5762
|
+
await this.db.createTable({
|
|
5763
|
+
tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
|
|
5764
|
+
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_MEMORY_RUN_FENCES]
|
|
5765
|
+
});
|
|
5766
|
+
}
|
|
5767
|
+
/**
|
|
5768
|
+
* Mark observational memory rows that are not their key's canonical head as superseded.
|
|
5769
|
+
* Runs as part of `init()`; `OracleStore` also runs it on every start when the memory schema
|
|
5770
|
+
* migration is unchanged and skipped.
|
|
5771
|
+
* @internal
|
|
5772
|
+
*/
|
|
5773
|
+
async backfillObservationalMemorySupersededBy() {
|
|
5774
|
+
await this.ctx.db.withConnection((connection) => backfillObservationalMemorySupersededBy(this.ctx, connection));
|
|
5461
5775
|
}
|
|
5462
5776
|
async dangerouslyClearAll() {
|
|
5463
5777
|
await clearAllMemoryTables(this.ctx);
|
|
5778
|
+
await this.db.clearTable(_mastra_core_storage.TABLE_MEMORY_RUN_FENCES);
|
|
5779
|
+
}
|
|
5780
|
+
supportsRunFencing() {
|
|
5781
|
+
return true;
|
|
5782
|
+
}
|
|
5783
|
+
async raiseRunFence(fence) {
|
|
5784
|
+
const fences = table(this.ctx, _mastra_core_storage.TABLE_MEMORY_RUN_FENCES);
|
|
5785
|
+
try {
|
|
5786
|
+
return await claimTransaction(this.db, async (client) => {
|
|
5787
|
+
const row = await client.oneOrNone(`SELECT generation AS "generation", "ownerId" FROM ${fences} WHERE "runId" = :runId FOR UPDATE`, { runId: fence.runId });
|
|
5788
|
+
const binds = {
|
|
5789
|
+
runId: fence.runId,
|
|
5790
|
+
generation: fence.generation,
|
|
5791
|
+
ownerId: fence.ownerId
|
|
5792
|
+
};
|
|
5793
|
+
if (!row) {
|
|
5794
|
+
await client.none(`INSERT INTO ${fences} ("runId", generation, "ownerId") VALUES (:runId, :generation, :ownerId)`, binds);
|
|
5795
|
+
return true;
|
|
5796
|
+
}
|
|
5797
|
+
const generation = Number(row.generation);
|
|
5798
|
+
if (!(generation < fence.generation) && !(generation === fence.generation && String(row.ownerId) === fence.ownerId)) return false;
|
|
5799
|
+
await client.none(`UPDATE ${fences} SET generation = :generation, "ownerId" = :ownerId, "retiredAt" = NULL WHERE "runId" = :runId`, binds);
|
|
5800
|
+
return true;
|
|
5801
|
+
});
|
|
5802
|
+
} catch (error) {
|
|
5803
|
+
throw storageError$1("RAISE_RUN_FENCE", "FAILED", { runId: fence.runId }, error);
|
|
5804
|
+
}
|
|
5805
|
+
}
|
|
5806
|
+
async retireRunFence(fence) {
|
|
5807
|
+
const fences = table(this.ctx, _mastra_core_storage.TABLE_MEMORY_RUN_FENCES);
|
|
5808
|
+
try {
|
|
5809
|
+
return await this.db.tx(async (client) => {
|
|
5810
|
+
const row = await client.oneOrNone(`SELECT generation AS "generation", "ownerId" FROM ${fences} WHERE "runId" = :runId FOR UPDATE`, { runId: fence.runId });
|
|
5811
|
+
if (!row || Number(row.generation) !== fence.generation || String(row.ownerId) !== fence.ownerId) return false;
|
|
5812
|
+
await client.none(`UPDATE ${fences} SET "retiredAt" = ${DB_NOW_MS} WHERE "runId" = :runId`, { runId: fence.runId });
|
|
5813
|
+
return true;
|
|
5814
|
+
});
|
|
5815
|
+
} catch (error) {
|
|
5816
|
+
throw storageError$1("RETIRE_RUN_FENCE", "FAILED", { runId: fence.runId }, error);
|
|
5817
|
+
}
|
|
5464
5818
|
}
|
|
5465
5819
|
async getThreadById(args) {
|
|
5466
5820
|
return getThreadById(this.ctx, args);
|
|
5467
5821
|
}
|
|
5468
|
-
async saveThread(args) {
|
|
5469
|
-
return saveThread(this.ctx,
|
|
5822
|
+
async saveThread({ fence, ...args }) {
|
|
5823
|
+
return saveThread(this.ctx, {
|
|
5824
|
+
...args,
|
|
5825
|
+
fence: (0, _mastra_core_storage.resolveRunFence)(this, fence)
|
|
5826
|
+
});
|
|
5470
5827
|
}
|
|
5471
|
-
async updateThread(args) {
|
|
5472
|
-
return updateThread(this.ctx,
|
|
5828
|
+
async updateThread({ fence, ...args }) {
|
|
5829
|
+
return updateThread(this.ctx, {
|
|
5830
|
+
...args,
|
|
5831
|
+
fence: (0, _mastra_core_storage.resolveRunFence)(this, fence)
|
|
5832
|
+
});
|
|
5473
5833
|
}
|
|
5474
5834
|
async deleteThread(args) {
|
|
5475
5835
|
return deleteThread(this.ctx, args);
|
|
@@ -5489,14 +5849,20 @@ var MemoryOracle = class MemoryOracle extends _mastra_core_storage.MemoryStorage
|
|
|
5489
5849
|
async listMessagesByResourceId(args) {
|
|
5490
5850
|
return listMessagesByResourceId(this.ctx, args);
|
|
5491
5851
|
}
|
|
5492
|
-
async saveMessages(args) {
|
|
5493
|
-
return saveMessages(this.ctx,
|
|
5852
|
+
async saveMessages({ fence, ...args }) {
|
|
5853
|
+
return saveMessages(this.ctx, {
|
|
5854
|
+
...args,
|
|
5855
|
+
fence: (0, _mastra_core_storage.resolveRunFence)(this, fence)
|
|
5856
|
+
});
|
|
5494
5857
|
}
|
|
5495
|
-
async updateMessages(args) {
|
|
5496
|
-
return updateMessages(this.ctx,
|
|
5858
|
+
async updateMessages({ fence, ...args }) {
|
|
5859
|
+
return updateMessages(this.ctx, {
|
|
5860
|
+
...args,
|
|
5861
|
+
fence: (0, _mastra_core_storage.resolveRunFence)(this, fence)
|
|
5862
|
+
});
|
|
5497
5863
|
}
|
|
5498
|
-
async deleteMessages(messageIds) {
|
|
5499
|
-
return deleteMessages(this.ctx, messageIds);
|
|
5864
|
+
async deleteMessages(messageIds, options) {
|
|
5865
|
+
return deleteMessages(this.ctx, messageIds, (0, _mastra_core_storage.resolveRunFence)(this, options?.fence));
|
|
5500
5866
|
}
|
|
5501
5867
|
async getResourceById(args) {
|
|
5502
5868
|
return getResourceById(this.ctx, args);
|
|
@@ -5504,8 +5870,11 @@ var MemoryOracle = class MemoryOracle extends _mastra_core_storage.MemoryStorage
|
|
|
5504
5870
|
async saveResource(args) {
|
|
5505
5871
|
return saveResource(this.ctx, args);
|
|
5506
5872
|
}
|
|
5507
|
-
async updateResource(args) {
|
|
5508
|
-
return updateResource(this.ctx,
|
|
5873
|
+
async updateResource({ fence, ...args }) {
|
|
5874
|
+
return updateResource(this.ctx, {
|
|
5875
|
+
...args,
|
|
5876
|
+
fence: (0, _mastra_core_storage.resolveRunFence)(this, fence)
|
|
5877
|
+
});
|
|
5509
5878
|
}
|
|
5510
5879
|
async cloneThread(args) {
|
|
5511
5880
|
const sourceThread = await this.getThreadById({ threadId: args.sourceThreadId });
|
|
@@ -5595,10 +5964,13 @@ var MemoryOracle = class MemoryOracle extends _mastra_core_storage.MemoryStorage
|
|
|
5595
5964
|
return insertObservationalMemoryRecord(this.ctx, record);
|
|
5596
5965
|
}
|
|
5597
5966
|
async updateActiveObservations(input) {
|
|
5598
|
-
|
|
5967
|
+
assertActiveObservationsApplied(await this.commitActiveObservations(input), input.id);
|
|
5968
|
+
}
|
|
5969
|
+
async commitActiveObservations(input) {
|
|
5970
|
+
return updateActiveObservations(this.ctx, input, (0, _mastra_core_storage.resolveRunFence)(this, void 0));
|
|
5599
5971
|
}
|
|
5600
5972
|
async createReflectionGeneration(input) {
|
|
5601
|
-
return createReflectionGeneration(this.ctx, input);
|
|
5973
|
+
return createReflectionGeneration(this.ctx, input, (0, _mastra_core_storage.resolveRunFence)(this, void 0));
|
|
5602
5974
|
}
|
|
5603
5975
|
async setReflectingFlag(id, isReflecting) {
|
|
5604
5976
|
return setReflectingFlag(this.ctx, id, isReflecting);
|
|
@@ -5622,16 +5994,19 @@ var MemoryOracle = class MemoryOracle extends _mastra_core_storage.MemoryStorage
|
|
|
5622
5994
|
return updateObservationalMemoryConfig(this.ctx, input);
|
|
5623
5995
|
}
|
|
5624
5996
|
async updateBufferedObservations(input) {
|
|
5625
|
-
|
|
5997
|
+
await this.appendBufferedObservations(input);
|
|
5998
|
+
}
|
|
5999
|
+
async appendBufferedObservations(input) {
|
|
6000
|
+
return updateBufferedObservations(this.ctx, input, (0, _mastra_core_storage.resolveRunFence)(this, void 0));
|
|
5626
6001
|
}
|
|
5627
6002
|
async swapBufferedToActive(input) {
|
|
5628
|
-
return swapBufferedToActive(this.ctx, input);
|
|
6003
|
+
return swapBufferedToActive(this.ctx, input, (0, _mastra_core_storage.resolveRunFence)(this, void 0));
|
|
5629
6004
|
}
|
|
5630
6005
|
async updateBufferedReflection(input) {
|
|
5631
|
-
return updateBufferedReflection(this.ctx, input);
|
|
6006
|
+
return updateBufferedReflection(this.ctx, input, (0, _mastra_core_storage.resolveRunFence)(this, void 0));
|
|
5632
6007
|
}
|
|
5633
6008
|
async swapBufferedReflectionToActive(input) {
|
|
5634
|
-
return swapBufferedReflectionToActive(this.ctx, input);
|
|
6009
|
+
return swapBufferedReflectionToActive(this.ctx, input, (0, _mastra_core_storage.resolveRunFence)(this, void 0));
|
|
5635
6010
|
}
|
|
5636
6011
|
};
|
|
5637
6012
|
//#endregion
|
|
@@ -7976,8 +8351,19 @@ const STORE_NAME$1 = "ORACLEDB";
|
|
|
7976
8351
|
const WORKFLOW_CREATED_AT = "\"createdAt\"";
|
|
7977
8352
|
const WORKFLOW_UPDATED_AT = "\"updatedAt\"";
|
|
7978
8353
|
const WORKFLOW_RESOURCE_ID = "\"resourceId\"";
|
|
8354
|
+
const RUN_OWNER_COLUMNS = `generation AS "generation", "ownerId", "leaseExpiresAt", ${DB_NOW_MS} AS "nowMs"`;
|
|
8355
|
+
function toRunOwnershipRecord(runId, row) {
|
|
8356
|
+
const leaseExpiresAt = row.leaseExpiresAt === null ? null : Number(row.leaseExpiresAt);
|
|
8357
|
+
return {
|
|
8358
|
+
runId,
|
|
8359
|
+
generation: Number(row.generation),
|
|
8360
|
+
ownerId: String(row.ownerId),
|
|
8361
|
+
leaseExpiresAt: leaseExpiresAt === null ? null : new Date(leaseExpiresAt),
|
|
8362
|
+
live: leaseExpiresAt !== null && leaseExpiresAt > Number(row.nowMs)
|
|
8363
|
+
};
|
|
8364
|
+
}
|
|
7979
8365
|
var WorkflowsOracle = class WorkflowsOracle extends _mastra_core_storage.WorkflowsStorage {
|
|
7980
|
-
static MANAGED_TABLES = [_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT];
|
|
8366
|
+
static MANAGED_TABLES = [_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS];
|
|
7981
8367
|
db;
|
|
7982
8368
|
schemaName;
|
|
7983
8369
|
skipDefaultIndexes;
|
|
@@ -7998,10 +8384,114 @@ var WorkflowsOracle = class WorkflowsOracle extends _mastra_core_storage.Workflo
|
|
|
7998
8384
|
}
|
|
7999
8385
|
async dangerouslyClearAll() {
|
|
8000
8386
|
await this.db.clearTable(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT);
|
|
8387
|
+
await this.db.clearTable(_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS);
|
|
8388
|
+
}
|
|
8389
|
+
supportsRunFencing() {
|
|
8390
|
+
return true;
|
|
8391
|
+
}
|
|
8392
|
+
async claimRunOwnership({ runId, ownerId, leaseMs, force, expectedGeneration }) {
|
|
8393
|
+
const table = this.runOwnersTable();
|
|
8394
|
+
try {
|
|
8395
|
+
return await claimTransaction(this.db, async (client) => {
|
|
8396
|
+
const existing = await client.oneOrNone(`SELECT generation AS "generation" FROM ${table} WHERE "runId" = :runId FOR UPDATE`, { runId });
|
|
8397
|
+
if (!existing) {
|
|
8398
|
+
if (expectedGeneration !== void 0 && expectedGeneration !== 0) return {
|
|
8399
|
+
acquired: false,
|
|
8400
|
+
record: null
|
|
8401
|
+
};
|
|
8402
|
+
await client.none(`INSERT INTO ${table} ("runId", generation, "ownerId", "leaseExpiresAt", "updatedAt")
|
|
8403
|
+
VALUES (:runId, ${DB_NOW_MS}, :ownerId, ${DB_NOW_MS} + :leaseMs, ${DB_NOW_MS})`, {
|
|
8404
|
+
runId,
|
|
8405
|
+
ownerId,
|
|
8406
|
+
leaseMs
|
|
8407
|
+
});
|
|
8408
|
+
return {
|
|
8409
|
+
acquired: true,
|
|
8410
|
+
record: await this.readRunOwner(client, runId)
|
|
8411
|
+
};
|
|
8412
|
+
}
|
|
8413
|
+
await client.none(`UPDATE ${table}
|
|
8414
|
+
SET generation = generation + 1, "ownerId" = :ownerId, "leaseExpiresAt" = ${DB_NOW_MS} + :leaseMs,
|
|
8415
|
+
"updatedAt" = ${DB_NOW_MS}
|
|
8416
|
+
WHERE "runId" = :runId
|
|
8417
|
+
AND (:expectedGeneration IS NULL OR generation = :expectedGeneration)
|
|
8418
|
+
AND (:force = 1 OR "leaseExpiresAt" IS NULL OR "leaseExpiresAt" <= ${DB_NOW_MS})`, {
|
|
8419
|
+
runId,
|
|
8420
|
+
ownerId,
|
|
8421
|
+
leaseMs,
|
|
8422
|
+
expectedGeneration: expectedGeneration ?? null,
|
|
8423
|
+
force: force === true ? 1 : 0
|
|
8424
|
+
});
|
|
8425
|
+
const record = await this.readRunOwner(client, runId);
|
|
8426
|
+
return record && record.generation === Number(existing.generation) + 1 ? {
|
|
8427
|
+
acquired: true,
|
|
8428
|
+
record
|
|
8429
|
+
} : {
|
|
8430
|
+
acquired: false,
|
|
8431
|
+
record
|
|
8432
|
+
};
|
|
8433
|
+
});
|
|
8434
|
+
} catch (error) {
|
|
8435
|
+
throw this.storageError("CLAIM_RUN_OWNERSHIP", "FAILED", { runId }, error);
|
|
8436
|
+
}
|
|
8437
|
+
}
|
|
8438
|
+
async renewRunOwnership({ leaseMs, ...fence }) {
|
|
8439
|
+
try {
|
|
8440
|
+
return await this.db.tx(async (client) => {
|
|
8441
|
+
await client.none(`UPDATE ${this.runOwnersTable()}
|
|
8442
|
+
SET "leaseExpiresAt" = ${DB_NOW_MS} + :leaseMs, "updatedAt" = ${DB_NOW_MS}
|
|
8443
|
+
WHERE "runId" = :runId AND generation = :generation AND "ownerId" = :ownerId
|
|
8444
|
+
AND "leaseExpiresAt" IS NOT NULL`, {
|
|
8445
|
+
runId: fence.runId,
|
|
8446
|
+
generation: fence.generation,
|
|
8447
|
+
ownerId: fence.ownerId,
|
|
8448
|
+
leaseMs
|
|
8449
|
+
});
|
|
8450
|
+
const record = await this.readRunOwner(client, fence.runId);
|
|
8451
|
+
return record !== null && record.generation === fence.generation && record.ownerId === fence.ownerId && record.leaseExpiresAt !== null ? {
|
|
8452
|
+
renewed: true,
|
|
8453
|
+
record
|
|
8454
|
+
} : {
|
|
8455
|
+
renewed: false,
|
|
8456
|
+
record
|
|
8457
|
+
};
|
|
8458
|
+
});
|
|
8459
|
+
} catch (error) {
|
|
8460
|
+
throw this.storageError("RENEW_RUN_OWNERSHIP", "FAILED", { runId: fence.runId }, error);
|
|
8461
|
+
}
|
|
8462
|
+
}
|
|
8463
|
+
async releaseRunOwnership(fence) {
|
|
8464
|
+
try {
|
|
8465
|
+
return await this.db.tx(async (client) => {
|
|
8466
|
+
await client.none(`UPDATE ${this.runOwnersTable()} SET "leaseExpiresAt" = NULL, "updatedAt" = ${DB_NOW_MS}
|
|
8467
|
+
WHERE "runId" = :runId AND generation = :generation AND "ownerId" = :ownerId`, {
|
|
8468
|
+
runId: fence.runId,
|
|
8469
|
+
generation: fence.generation,
|
|
8470
|
+
ownerId: fence.ownerId
|
|
8471
|
+
});
|
|
8472
|
+
const record = await this.readRunOwner(client, fence.runId);
|
|
8473
|
+
return record !== null && record.generation === fence.generation && record.ownerId === fence.ownerId;
|
|
8474
|
+
});
|
|
8475
|
+
} catch (error) {
|
|
8476
|
+
throw this.storageError("RELEASE_RUN_OWNERSHIP", "FAILED", { runId: fence.runId }, error);
|
|
8477
|
+
}
|
|
8001
8478
|
}
|
|
8002
|
-
async
|
|
8479
|
+
async getRunOwnership({ runId }) {
|
|
8480
|
+
try {
|
|
8481
|
+
return await this.readRunOwner(this.db, runId);
|
|
8482
|
+
} catch (error) {
|
|
8483
|
+
throw this.storageError("GET_RUN_OWNERSHIP", "FAILED", { runId }, error);
|
|
8484
|
+
}
|
|
8485
|
+
}
|
|
8486
|
+
async readRunOwner(client, runId) {
|
|
8487
|
+
const row = await client.oneOrNone(`SELECT ${RUN_OWNER_COLUMNS} FROM ${this.runOwnersTable()} WHERE "runId" = :runId`, { runId });
|
|
8488
|
+
return row ? toRunOwnershipRecord(runId, row) : null;
|
|
8489
|
+
}
|
|
8490
|
+
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state, fence: explicitFence }) {
|
|
8491
|
+
const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
|
|
8003
8492
|
try {
|
|
8004
8493
|
return await this.db.tx(async (client) => {
|
|
8494
|
+
if (fence) await assertRunFence(client, this.runOwnersTable(), fence, "updateWorkflowResults");
|
|
8005
8495
|
await this.ensureWorkflowRunRow(client, workflowName, runId);
|
|
8006
8496
|
const existing = await client.one(`SELECT snapshot AS "snapshot", ${WORKFLOW_RESOURCE_ID} AS "resourceId" FROM ${this.table()} WHERE workflow_name = :workflowName AND run_id = :runId FOR UPDATE`, {
|
|
8007
8497
|
workflowName,
|
|
@@ -8025,6 +8515,7 @@ var WorkflowsOracle = class WorkflowsOracle extends _mastra_core_storage.Workflo
|
|
|
8025
8515
|
return snapshot.context;
|
|
8026
8516
|
});
|
|
8027
8517
|
} catch (error) {
|
|
8518
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
8028
8519
|
throw this.storageError("UPDATE_WORKFLOW_RESULTS", "FAILED", {
|
|
8029
8520
|
workflowName,
|
|
8030
8521
|
runId,
|
|
@@ -8032,9 +8523,11 @@ var WorkflowsOracle = class WorkflowsOracle extends _mastra_core_storage.Workflo
|
|
|
8032
8523
|
}, error);
|
|
8033
8524
|
}
|
|
8034
8525
|
}
|
|
8035
|
-
async updateWorkflowState({ workflowName, runId, opts }) {
|
|
8526
|
+
async updateWorkflowState({ workflowName, runId, opts, fence: explicitFence }) {
|
|
8527
|
+
const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
|
|
8036
8528
|
try {
|
|
8037
8529
|
return await this.db.tx(async (client) => {
|
|
8530
|
+
if (fence) await assertRunFence(client, this.runOwnersTable(), fence, "updateWorkflowState");
|
|
8038
8531
|
const existing = await client.oneOrNone(`SELECT snapshot AS "snapshot" FROM ${this.table()} WHERE workflow_name = :workflowName AND run_id = :runId FOR UPDATE`, {
|
|
8039
8532
|
workflowName,
|
|
8040
8533
|
runId
|
|
@@ -8057,24 +8550,26 @@ var WorkflowsOracle = class WorkflowsOracle extends _mastra_core_storage.Workflo
|
|
|
8057
8550
|
return updatedSnapshot;
|
|
8058
8551
|
});
|
|
8059
8552
|
} catch (error) {
|
|
8553
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
8060
8554
|
throw this.storageError("UPDATE_WORKFLOW_STATE", "FAILED", {
|
|
8061
8555
|
workflowName,
|
|
8062
8556
|
runId
|
|
8063
8557
|
}, error);
|
|
8064
8558
|
}
|
|
8065
8559
|
}
|
|
8066
|
-
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) {
|
|
8560
|
+
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
|
|
8067
8561
|
try {
|
|
8068
8562
|
const now = /* @__PURE__ */ new Date();
|
|
8069
|
-
await this.db.none(this.workflowMergeSql(), {
|
|
8563
|
+
await withRunFence(this.db, this.runOwnersTable(), (0, _mastra_core_storage.resolveRunFence)(this, fence, runId), "persistWorkflowSnapshot", (client) => client.none(this.workflowMergeSql(), {
|
|
8070
8564
|
workflowName,
|
|
8071
8565
|
runId,
|
|
8072
8566
|
resourceId: resourceId ?? null,
|
|
8073
8567
|
snapshot: jsonBind(snapshot),
|
|
8074
8568
|
createdAt: createdAt ?? now,
|
|
8075
8569
|
updatedAt: updatedAt ?? now
|
|
8076
|
-
});
|
|
8570
|
+
}));
|
|
8077
8571
|
} catch (error) {
|
|
8572
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
8078
8573
|
throw this.storageError("PERSIST_WORKFLOW_SNAPSHOT", "FAILED", {
|
|
8079
8574
|
workflowName,
|
|
8080
8575
|
runId
|
|
@@ -8138,13 +8633,14 @@ var WorkflowsOracle = class WorkflowsOracle extends _mastra_core_storage.Workflo
|
|
|
8138
8633
|
}, error);
|
|
8139
8634
|
}
|
|
8140
8635
|
}
|
|
8141
|
-
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
8636
|
+
async deleteWorkflowRunById({ runId, workflowName, fence }) {
|
|
8142
8637
|
try {
|
|
8143
|
-
await this.db.none(`DELETE FROM ${this.table()} WHERE workflow_name = :workflowName AND run_id = :runId`, {
|
|
8638
|
+
await withRunFence(this.db, this.runOwnersTable(), (0, _mastra_core_storage.resolveRunFence)(this, fence, runId), "deleteWorkflowRunById", (client) => client.none(`DELETE FROM ${this.table()} WHERE workflow_name = :workflowName AND run_id = :runId`, {
|
|
8144
8639
|
workflowName,
|
|
8145
8640
|
runId
|
|
8146
|
-
});
|
|
8641
|
+
}));
|
|
8147
8642
|
} catch (error) {
|
|
8643
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
8148
8644
|
throw this.storageError("DELETE_WORKFLOW_RUN_BY_ID", "FAILED", {
|
|
8149
8645
|
runId,
|
|
8150
8646
|
workflowName
|
|
@@ -8163,6 +8659,10 @@ var WorkflowsOracle = class WorkflowsOracle extends _mastra_core_storage.Workflo
|
|
|
8163
8659
|
CONSTRAINT ${this.constraint("MASTRA_WORKFLOW_SNAPSHOT_PK")} PRIMARY KEY (workflow_name, run_id)
|
|
8164
8660
|
)`, [-955]);
|
|
8165
8661
|
await this.db.executeDdl(`ALTER TABLE ${this.table()} ADD (${WORKFLOW_RESOURCE_ID} VARCHAR2(512))`, [-1430]);
|
|
8662
|
+
await this.db.createTable({
|
|
8663
|
+
tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
|
|
8664
|
+
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS]
|
|
8665
|
+
});
|
|
8166
8666
|
}
|
|
8167
8667
|
async createIndexes() {
|
|
8168
8668
|
await this.db.withConnection(async (connection) => {
|
|
@@ -8300,6 +8800,9 @@ var WorkflowsOracle = class WorkflowsOracle extends _mastra_core_storage.Workflo
|
|
|
8300
8800
|
table() {
|
|
8301
8801
|
return qualifyName(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, this.schemaName);
|
|
8302
8802
|
}
|
|
8803
|
+
runOwnersTable() {
|
|
8804
|
+
return qualifyName(_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS, this.schemaName);
|
|
8805
|
+
}
|
|
8303
8806
|
indexName(indexName) {
|
|
8304
8807
|
return indexNameForTable(indexName, "IDX");
|
|
8305
8808
|
}
|
|
@@ -8501,7 +9004,7 @@ function checksumMigration(input) {
|
|
|
8501
9004
|
//#region src/storage/index.ts
|
|
8502
9005
|
const STORE_NAME = "ORACLEDB";
|
|
8503
9006
|
const DOMAIN_SCHEMA_VERSIONS = {
|
|
8504
|
-
R001_MEMORY_SCHEMA:
|
|
9007
|
+
R001_MEMORY_SCHEMA: 2,
|
|
8505
9008
|
R002_WORKFLOWS_SCHEMA: 1,
|
|
8506
9009
|
R003_OBSERVABILITY_SCHEMA: 1,
|
|
8507
9010
|
R004_SCORES_SCHEMA: 1,
|
|
@@ -8589,6 +9092,10 @@ var OracleStore = class extends _mastra_core_storage.MastraCompositeStore {
|
|
|
8589
9092
|
async runMigrations(forceRepeatable) {
|
|
8590
9093
|
try {
|
|
8591
9094
|
const results = await this.migrationRegistry.run(this.storageMigrations(), { forceRepeatable });
|
|
9095
|
+
if (results.find((result) => result.id === "R001_MEMORY_SCHEMA")?.status === "skipped") {
|
|
9096
|
+
const memory = this.stores.memory;
|
|
9097
|
+
if (memory instanceof MemoryOracle) await memory.backfillObservationalMemorySupersededBy();
|
|
9098
|
+
}
|
|
8592
9099
|
this.isInitialized = true;
|
|
8593
9100
|
return results;
|
|
8594
9101
|
} catch (error) {
|