@effect-agent/storage-sqlite 0.1.0-beta.40 → 0.1.0-beta.41
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/index.d.mts +2 -11
- package/dist/index.mjs +94 -394
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/sqlite-memory-store.ts +7 -532
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { Clock, Context, Crypto, DateTime, Duration, Effect, Exit, Layer, Option
|
|
|
4
4
|
import { SqliteClient, SqliteMigrator } from "@effect/sql-sqlite-node";
|
|
5
5
|
import * as SqlClient from "effect/unstable/sql/SqlClient";
|
|
6
6
|
import { NodeCrypto } from "@effect/platform-node";
|
|
7
|
-
import {
|
|
7
|
+
import { memoryReaderLayer, memoryStoreLayer, memoryStoreLayerWithFailpoints } from "@effect-agent/thread/sql-memory";
|
|
8
8
|
//#region src/errors.ts
|
|
9
9
|
/** The SQLite file uses a private-development format this adapter cannot read. */
|
|
10
10
|
var SqliteStorageCompatibilityError = class extends Schema.TaggedError()("SqliteStorageCompatibilityError", {
|
|
@@ -419,10 +419,10 @@ const sqliteMigrations = SqliteMigrator.fromRecord({ "1_current_thread_storage":
|
|
|
419
419
|
}) });
|
|
420
420
|
//#endregion
|
|
421
421
|
//#region src/sqlite-activity-store.ts
|
|
422
|
-
const STORAGE_VERSION
|
|
423
|
-
const METADATA_COMPONENT
|
|
422
|
+
const STORAGE_VERSION = 1;
|
|
423
|
+
const METADATA_COMPONENT = "activity";
|
|
424
424
|
const STATE_TABLE = "effect_agent_activity_processor_state_v1";
|
|
425
|
-
const StoredJson$
|
|
425
|
+
const StoredJson$1 = Schema.String.check(Schema.isMaxLength(16777216));
|
|
426
426
|
const sameKey = Schema.toEquivalence(ActivityProcessorKey);
|
|
427
427
|
const sameWork = Schema.toEquivalence(PreparedActivity);
|
|
428
428
|
var ActivityMetadataRow = class extends Schema.Class("@effect-agent/storage-sqlite/ActivityMetadataRow")({ version: Schema.Int }) {};
|
|
@@ -436,26 +436,26 @@ var ActivityStateRow = class extends Schema.Class("@effect-agent/storage-sqlite/
|
|
|
436
436
|
epoch: ActivityProgress.fields.epoch,
|
|
437
437
|
owner: ActivityProgress.fields.owner,
|
|
438
438
|
lease_expires_at: ActivityProgress.fields.leaseExpiresAt,
|
|
439
|
-
progress_json: StoredJson$
|
|
439
|
+
progress_json: StoredJson$1
|
|
440
440
|
}) {};
|
|
441
441
|
var ActivityChangeCountRow = class extends Schema.Class("@effect-agent/storage-sqlite/ActivityChangeCountRow")({ changed: Schema.Int }) {};
|
|
442
|
-
const StoredVersionHeader
|
|
442
|
+
const StoredVersionHeader = Schema.Struct({ version: Schema.Int });
|
|
443
443
|
const storeError$1 = (operation, reason = "unavailable") => ActivityStoreError.make({
|
|
444
444
|
operation,
|
|
445
445
|
reason
|
|
446
446
|
});
|
|
447
|
-
const query
|
|
448
|
-
const decodeRows$
|
|
447
|
+
const query = (effect, operation) => effect.pipe(Effect.mapError(() => storeError$1(operation)));
|
|
448
|
+
const decodeRows$3 = Effect.fn("SqliteActivityStore.decodeRows")(function* (schema, rows, operation) {
|
|
449
449
|
return yield* Schema.decodeUnknownEffect(Schema.Array(schema))(rows).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")));
|
|
450
450
|
});
|
|
451
|
-
const decodeInput$
|
|
451
|
+
const decodeInput$1 = Effect.fn("SqliteActivityStore.decodeInput")(function* (schema, value, operation) {
|
|
452
452
|
return yield* Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => storeError$1(operation, "invalid-input")));
|
|
453
453
|
});
|
|
454
454
|
const encodeProgress = Effect.fn("SqliteActivityStore.encodeProgress")(function* (progress, operation) {
|
|
455
|
-
return yield* Schema.encodeEffect(Schema.fromJsonString(ActivityProgress))(progress).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")), Effect.flatMap((encoded) => Schema.decodeEffect(StoredJson$
|
|
455
|
+
return yield* Schema.encodeEffect(Schema.fromJsonString(ActivityProgress))(progress).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")), Effect.flatMap((encoded) => Schema.decodeEffect(StoredJson$1)(encoded).pipe(Effect.mapError(() => storeError$1(operation, "invalid-input")))));
|
|
456
456
|
});
|
|
457
457
|
const decodeProgress = Effect.fn("SqliteActivityStore.decodeProgress")(function* (value, operation) {
|
|
458
|
-
if ((yield* Schema.decodeEffect(Schema.fromJsonString(StoredVersionHeader
|
|
458
|
+
if ((yield* Schema.decodeEffect(Schema.fromJsonString(StoredVersionHeader))(value).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")))).version !== STORAGE_VERSION) return yield* storeError$1(operation, "incompatible");
|
|
459
459
|
const progress = yield* Schema.decodeEffect(Schema.fromJsonString(ActivityProgress))(value).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")));
|
|
460
460
|
if ((yield* encodeProgress(progress, operation)) !== value) return yield* storeError$1(operation, "corrupt");
|
|
461
461
|
return progress;
|
|
@@ -490,18 +490,18 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
490
490
|
`;
|
|
491
491
|
const metadataRows = yield* sql`
|
|
492
492
|
SELECT version FROM effect_agent_activity_metadata
|
|
493
|
-
WHERE component = ${METADATA_COMPONENT
|
|
493
|
+
WHERE component = ${METADATA_COMPONENT}
|
|
494
494
|
`;
|
|
495
|
-
const metadata = yield* decodeRows$
|
|
495
|
+
const metadata = yield* decodeRows$3(ActivityMetadataRow, metadataRows, "decode activity schema version");
|
|
496
496
|
if (metadata.length > 1) return yield* storeError$1("decode activity schema version", "corrupt");
|
|
497
497
|
const currentVersion = metadata[0]?.version;
|
|
498
|
-
if (currentVersion !== void 0 && currentVersion !== STORAGE_VERSION
|
|
498
|
+
if (currentVersion !== void 0 && currentVersion !== STORAGE_VERSION) return yield* storeError$1("initialize activity schema", "incompatible");
|
|
499
499
|
if (currentVersion === void 0) {
|
|
500
500
|
const tableRows = yield* sql`
|
|
501
501
|
SELECT name FROM sqlite_master
|
|
502
502
|
WHERE type = 'table' AND name = ${STATE_TABLE}
|
|
503
503
|
`;
|
|
504
|
-
if ((yield* decodeRows$
|
|
504
|
+
if ((yield* decodeRows$3(ActivityTableRow, tableRows, "inspect activity schema")).length > 0) return yield* storeError$1("initialize activity schema", "incompatible");
|
|
505
505
|
yield* sql`
|
|
506
506
|
CREATE TABLE effect_agent_activity_processor_state_v1 (
|
|
507
507
|
processor_id TEXT NOT NULL,
|
|
@@ -518,7 +518,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
518
518
|
`;
|
|
519
519
|
yield* sql`
|
|
520
520
|
INSERT INTO effect_agent_activity_metadata (component, version)
|
|
521
|
-
VALUES (${METADATA_COMPONENT
|
|
521
|
+
VALUES (${METADATA_COMPONENT}, ${STORAGE_VERSION})
|
|
522
522
|
`;
|
|
523
523
|
}
|
|
524
524
|
yield* sql`
|
|
@@ -530,7 +530,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
530
530
|
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storeError$1("initialize activity schema"))));
|
|
531
531
|
yield* failpoint.hit("activity:initialize:after");
|
|
532
532
|
const readProgress = Effect.fn("SqliteActivityStore.readProgress")(function* (key, operation) {
|
|
533
|
-
const rawRows = yield* query
|
|
533
|
+
const rawRows = yield* query(sql`
|
|
534
534
|
SELECT processor_id, processor_version, thread_id, format_version,
|
|
535
535
|
through_sequence, epoch, owner, lease_expires_at, progress_json
|
|
536
536
|
FROM effect_agent_activity_processor_state_v1
|
|
@@ -538,19 +538,19 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
538
538
|
AND processor_version = ${key.processorVersion}
|
|
539
539
|
AND thread_id = ${key.threadId}
|
|
540
540
|
`, operation);
|
|
541
|
-
const rows = yield* decodeRows$
|
|
541
|
+
const rows = yield* decodeRows$3(ActivityStateRow, rawRows, operation);
|
|
542
542
|
if (rows.length === 0) return null;
|
|
543
543
|
if (rows.length !== 1) return yield* storeError$1(operation, "corrupt");
|
|
544
544
|
const row = rows[0];
|
|
545
|
-
if (row.format_version !== STORAGE_VERSION
|
|
545
|
+
if (row.format_version !== STORAGE_VERSION) return yield* storeError$1(operation, "incompatible");
|
|
546
546
|
const progress = yield* decodeProgress(row.progress_json, operation);
|
|
547
547
|
yield* validateProgress(progress, operation);
|
|
548
548
|
if (!sameKey(progress.key, key) || row.processor_id !== key.processorId || row.processor_version !== key.processorVersion || row.thread_id !== key.threadId || row.through_sequence !== progress.throughSequence || row.epoch !== progress.epoch || row.owner !== progress.owner || row.lease_expires_at !== progress.leaseExpiresAt) return yield* storeError$1(operation, "corrupt");
|
|
549
549
|
return progress;
|
|
550
550
|
});
|
|
551
551
|
const checkChanged = Effect.fn("SqliteActivityStore.checkChanged")(function* (operation) {
|
|
552
|
-
const rawRows = yield* query
|
|
553
|
-
const rows = yield* decodeRows$
|
|
552
|
+
const rawRows = yield* query(sql`SELECT changes() AS changed`, operation);
|
|
553
|
+
const rows = yield* decodeRows$3(ActivityChangeCountRow, rawRows, operation);
|
|
554
554
|
if (rows.length !== 1 || rows[0].changed !== 1) return yield* storeError$1(operation, "corrupt");
|
|
555
555
|
});
|
|
556
556
|
const insertProgress = Effect.fn("SqliteActivityStore.insertProgress")(function* (progress, operation) {
|
|
@@ -561,7 +561,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
561
561
|
epoch, owner, lease_expires_at, progress_json
|
|
562
562
|
) VALUES (
|
|
563
563
|
${progress.key.processorId}, ${progress.key.processorVersion}, ${progress.key.threadId},
|
|
564
|
-
${STORAGE_VERSION
|
|
564
|
+
${STORAGE_VERSION}, ${progress.throughSequence}, ${progress.epoch}, ${progress.owner},
|
|
565
565
|
${progress.leaseExpiresAt}, ${progressJson}
|
|
566
566
|
)
|
|
567
567
|
`;
|
|
@@ -571,7 +571,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
571
571
|
const progressJson = yield* encodeProgress(next, operation);
|
|
572
572
|
yield* sql`
|
|
573
573
|
UPDATE effect_agent_activity_processor_state_v1
|
|
574
|
-
SET format_version = ${STORAGE_VERSION
|
|
574
|
+
SET format_version = ${STORAGE_VERSION},
|
|
575
575
|
through_sequence = ${next.throughSequence},
|
|
576
576
|
epoch = ${next.epoch},
|
|
577
577
|
owner = ${next.owner},
|
|
@@ -591,12 +591,12 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
591
591
|
return progress;
|
|
592
592
|
});
|
|
593
593
|
const inspect = Effect.fn("SqliteActivityStore.inspect")(function* (key) {
|
|
594
|
-
const decodedKey = yield* decodeInput$
|
|
594
|
+
const decodedKey = yield* decodeInput$1(ActivityProcessorKey, key, "inspect activity progress");
|
|
595
595
|
return yield* readProgress(decodedKey, "inspect activity progress");
|
|
596
596
|
});
|
|
597
597
|
const claim = Effect.fn("SqliteActivityStore.claim")(function* (request) {
|
|
598
598
|
const operation = "claim activity progress";
|
|
599
|
-
const decoded = yield* decodeInput$
|
|
599
|
+
const decoded = yield* decodeInput$1(ActivityClaimRequest, request, operation);
|
|
600
600
|
yield* failpoint.hit("activity:claim:before");
|
|
601
601
|
const claimed = yield* sql.withTransaction(Effect.gen(function* () {
|
|
602
602
|
const current = yield* readProgress(decoded.key, operation);
|
|
@@ -606,7 +606,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
606
606
|
leaseExpiresAt: current.leaseExpiresAt
|
|
607
607
|
});
|
|
608
608
|
const next = yield* Schema.decodeUnknownEffect(ActivityProgress)({
|
|
609
|
-
version: STORAGE_VERSION
|
|
609
|
+
version: STORAGE_VERSION,
|
|
610
610
|
key: decoded.key,
|
|
611
611
|
throughSequence: current?.throughSequence ?? 0,
|
|
612
612
|
epoch: (current?.epoch ?? 0) + 1,
|
|
@@ -627,8 +627,8 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
627
627
|
});
|
|
628
628
|
const prepare = Effect.fn("SqliteActivityStore.prepare")(function* (request) {
|
|
629
629
|
const operation = "prepare activity output";
|
|
630
|
-
const claim = yield* decodeInput$
|
|
631
|
-
const work = yield* decodeInput$
|
|
630
|
+
const claim = yield* decodeInput$1(ActivityClaim, request.claim, operation);
|
|
631
|
+
const work = yield* decodeInput$1(PreparedActivity, request.work, operation);
|
|
632
632
|
yield* failpoint.hit("activity:prepare:before");
|
|
633
633
|
const result = yield* sql.withTransaction(Effect.gen(function* () {
|
|
634
634
|
const current = yield* requireLive(yield* readProgress(claim.key, operation), claim, true);
|
|
@@ -662,8 +662,8 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
662
662
|
});
|
|
663
663
|
const advance = Effect.fn("SqliteActivityStore.advance")(function* (request) {
|
|
664
664
|
const operation = "advance activity progress";
|
|
665
|
-
const claim = yield* decodeInput$
|
|
666
|
-
const workId = yield* decodeInput$
|
|
665
|
+
const claim = yield* decodeInput$1(ActivityClaim, request.claim, operation);
|
|
666
|
+
const workId = yield* decodeInput$1(Digest, request.workId, operation);
|
|
667
667
|
yield* failpoint.hit("activity:advance:before");
|
|
668
668
|
const nextClaim = yield* sql.withTransaction(Effect.gen(function* () {
|
|
669
669
|
const current = yield* requireLive(yield* readProgress(claim.key, operation), claim, true);
|
|
@@ -688,7 +688,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
|
688
688
|
});
|
|
689
689
|
const release = Effect.fn("SqliteActivityStore.release")(function* (claim) {
|
|
690
690
|
const operation = "release activity claim";
|
|
691
|
-
const decoded = yield* decodeInput$
|
|
691
|
+
const decoded = yield* decodeInput$1(ActivityClaim, claim, operation);
|
|
692
692
|
yield* failpoint.hit("activity:release:before");
|
|
693
693
|
yield* sql.withTransaction(Effect.gen(function* () {
|
|
694
694
|
const current = yield* readProgress(decoded.key, operation);
|
|
@@ -824,19 +824,19 @@ var RawThreadExport = class extends Schema.Class("@effect-agent/storage-sqlite/R
|
|
|
824
824
|
thread: ThreadRow,
|
|
825
825
|
records: Schema.Array(RecordRow)
|
|
826
826
|
}) {};
|
|
827
|
-
const storageError
|
|
827
|
+
const storageError = (operation) => (error) => SqliteStorageError.make({
|
|
828
828
|
cause: error,
|
|
829
829
|
operation,
|
|
830
830
|
message: error.message
|
|
831
831
|
});
|
|
832
832
|
/** Decode raw SQLite rows against a Schema, reporting failures as typed corruption. */
|
|
833
|
-
const decodeRows$
|
|
833
|
+
const decodeRows$2 = Effect.fn("SqliteJournal.decodeRows")((schema, table, rowKey, rows) => Schema.decodeUnknownEffect(schema)(rows).pipe(Effect.mapError((error) => SqliteStorageCorruptionError.make({
|
|
834
834
|
table,
|
|
835
835
|
rowKey,
|
|
836
836
|
message: String(error)
|
|
837
837
|
}))));
|
|
838
838
|
/** Decode exactly one raw SQLite row against a Schema, reporting failures as typed corruption. */
|
|
839
|
-
const decodeSingleRow = Effect.fn("SqliteJournal.decodeSingleRow")((schema, table, rowKey, rows) => decodeRows$
|
|
839
|
+
const decodeSingleRow = Effect.fn("SqliteJournal.decodeSingleRow")((schema, table, rowKey, rows) => decodeRows$2(schema, table, rowKey, rows).pipe(Effect.flatMap((decoded) => decoded.length === 1 ? Effect.succeed(decoded[0]) : Effect.fail(SqliteStorageCorruptionError.make({
|
|
840
840
|
table,
|
|
841
841
|
rowKey,
|
|
842
842
|
message: `Expected exactly one row but found ${decoded.length}.`
|
|
@@ -845,16 +845,16 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
845
845
|
const sql = yield* SqlClient.SqlClient;
|
|
846
846
|
const { hit: failpoint } = yield* SqliteStorageFailpoint;
|
|
847
847
|
const { busyTimeout } = yield* SqliteStorageConfig;
|
|
848
|
-
yield* sql`PRAGMA foreign_keys = ON`.pipe(Effect.mapError(storageError
|
|
849
|
-
yield* sql.unsafe(`PRAGMA busy_timeout = ${busyTimeout}`).pipe(Effect.mapError(storageError
|
|
850
|
-
const journalModeRows = yield* sql`PRAGMA journal_mode`.pipe(Effect.mapError(storageError
|
|
848
|
+
yield* sql`PRAGMA foreign_keys = ON`.pipe(Effect.mapError(storageError("enable foreign keys")));
|
|
849
|
+
yield* sql.unsafe(`PRAGMA busy_timeout = ${busyTimeout}`).pipe(Effect.mapError(storageError("configure busy timeout")));
|
|
850
|
+
const journalModeRows = yield* sql`PRAGMA journal_mode`.pipe(Effect.mapError(storageError("read journal mode")));
|
|
851
851
|
const journalMode = yield* decodeSingleRow(Schema.Array(SqliteJournalModeRow), "pragma_journal_mode", "singleton", journalModeRows);
|
|
852
852
|
if (journalMode.journal_mode.toLowerCase() !== "wal") return yield* SqliteStorageCompatibilityError.make({
|
|
853
853
|
actualVersion: 0,
|
|
854
854
|
supportedVersion: 7,
|
|
855
855
|
message: `SQLite WAL mode is required; the database reported ${journalMode.journal_mode}.`
|
|
856
856
|
});
|
|
857
|
-
const versionRows = yield* sql`PRAGMA user_version`.pipe(Effect.mapError(storageError
|
|
857
|
+
const versionRows = yield* sql`PRAGMA user_version`.pipe(Effect.mapError(storageError("read storage version")));
|
|
858
858
|
const version = yield* decodeSingleRow(Schema.Array(SqliteVersionRow), "pragma_user_version", "singleton", versionRows);
|
|
859
859
|
if (version.user_version !== 0 && version.user_version !== 7) return yield* SqliteStorageCompatibilityError.make({
|
|
860
860
|
actualVersion: version.user_version,
|
|
@@ -868,8 +868,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
868
868
|
WHERE type = 'table'
|
|
869
869
|
AND name LIKE 'effect_agent_%'
|
|
870
870
|
ORDER BY name
|
|
871
|
-
`.pipe(Effect.mapError(storageError
|
|
872
|
-
if ((yield* decodeRows$
|
|
871
|
+
`.pipe(Effect.mapError(storageError("inspect unversioned storage")));
|
|
872
|
+
if ((yield* decodeRows$2(Schema.Array(SqliteNameRow), "sqlite_master", "effect_agent_%", existingRows)).length > 0) return yield* SqliteStorageCompatibilityError.make({
|
|
873
873
|
actualVersion: 0,
|
|
874
874
|
supportedVersion: 7,
|
|
875
875
|
message: "The SQLite file contains unversioned Effect Agent tables. Reset it explicitly; refusing to mutate ambiguous stored data."
|
|
@@ -899,8 +899,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
899
899
|
'effect_agent_schedules'
|
|
900
900
|
)
|
|
901
901
|
ORDER BY name
|
|
902
|
-
`.pipe(Effect.mapError(storageError
|
|
903
|
-
if ((yield* decodeRows$
|
|
902
|
+
`.pipe(Effect.mapError(storageError("verify storage tables")));
|
|
903
|
+
if ((yield* decodeRows$2(Schema.Array(SqliteNameRow), "sqlite_master", "required_tables", requiredRows)).length !== 12) return yield* SqliteStorageCompatibilityError.make({
|
|
904
904
|
actualVersion: 7,
|
|
905
905
|
supportedVersion: 7,
|
|
906
906
|
message: "The SQLite file claims the current format but is missing required tables. Reset the corrupt private-development data."
|
|
@@ -909,7 +909,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
909
909
|
cause: error,
|
|
910
910
|
operation,
|
|
911
911
|
message: `Another producer holds the SQLite write lock; ${operation} is safe to retry.`
|
|
912
|
-
}) : storageError
|
|
912
|
+
}) : storageError(operation)(error);
|
|
913
913
|
/**
|
|
914
914
|
* Runs one journal write transaction under `BEGIN IMMEDIATE`. SQLite's deferred `BEGIN`
|
|
915
915
|
* would let a read-then-write transaction start as a reader and fail with
|
|
@@ -942,11 +942,11 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
942
942
|
* `BEGIN` is reported directly because there is no transaction to roll back.
|
|
943
943
|
*/
|
|
944
944
|
const withReadTransaction = (operation) => (effect) => Effect.uninterruptibleMask((restore) => Effect.scoped(Effect.gen(function* () {
|
|
945
|
-
const connection = yield* sql.reserve.pipe(Effect.mapError(storageError
|
|
946
|
-
yield* connection.executeUnprepared("BEGIN", [], void 0).pipe(Effect.mapError(storageError
|
|
945
|
+
const connection = yield* sql.reserve.pipe(Effect.mapError(storageError(operation)));
|
|
946
|
+
yield* connection.executeUnprepared("BEGIN", [], void 0).pipe(Effect.mapError(storageError(operation)));
|
|
947
947
|
const exit = yield* restore(Effect.provideService(effect, sql.transactionService, [connection, 0])).pipe(Effect.exit);
|
|
948
948
|
if (Exit.isSuccess(exit)) {
|
|
949
|
-
yield* connection.executeUnprepared("COMMIT", [], void 0).pipe(Effect.mapError(storageError
|
|
949
|
+
yield* connection.executeUnprepared("COMMIT", [], void 0).pipe(Effect.mapError(storageError(operation)));
|
|
950
950
|
return exit.value;
|
|
951
951
|
}
|
|
952
952
|
yield* Effect.orDie(connection.executeUnprepared("ROLLBACK", [], void 0));
|
|
@@ -967,8 +967,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
967
967
|
producer_epoch
|
|
968
968
|
FROM effect_agent_threads
|
|
969
969
|
WHERE thread_id = ${threadId}
|
|
970
|
-
`.pipe(Effect.mapError(storageError
|
|
971
|
-
const existing = yield* decodeRows$
|
|
970
|
+
`.pipe(Effect.mapError(storageError("read materialized thread")));
|
|
971
|
+
const existing = yield* decodeRows$2(Schema.Array(ThreadRow), "effect_agent_threads", threadId, existingRows);
|
|
972
972
|
if (existing.length > 1) return yield* SqliteStorageCorruptionError.make({
|
|
973
973
|
table: "effect_agent_threads",
|
|
974
974
|
rowKey: threadId,
|
|
@@ -989,7 +989,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
989
989
|
${emptyTailDigest},
|
|
990
990
|
${producerEpoch}
|
|
991
991
|
)
|
|
992
|
-
`.pipe(Effect.mapError(storageError
|
|
992
|
+
`.pipe(Effect.mapError(storageError("materialize thread")));
|
|
993
993
|
return;
|
|
994
994
|
}
|
|
995
995
|
if (producerEpoch < existing[0].producer_epoch) return yield* SqliteFenceRejected.make({
|
|
@@ -1001,7 +1001,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1001
1001
|
UPDATE effect_agent_threads
|
|
1002
1002
|
SET producer_epoch = ${producerEpoch}
|
|
1003
1003
|
WHERE thread_id = ${threadId}
|
|
1004
|
-
`.pipe(Effect.mapError(storageError
|
|
1004
|
+
`.pipe(Effect.mapError(storageError("advance materialization epoch")));
|
|
1005
1005
|
}));
|
|
1006
1006
|
});
|
|
1007
1007
|
const getThread = Effect.fn("SqliteJournal.getThread")(function* (threadId) {
|
|
@@ -1014,8 +1014,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1014
1014
|
producer_epoch
|
|
1015
1015
|
FROM effect_agent_threads
|
|
1016
1016
|
WHERE thread_id = ${threadId}
|
|
1017
|
-
`.pipe(Effect.mapError(storageError
|
|
1018
|
-
return yield* decodeRows$
|
|
1017
|
+
`.pipe(Effect.mapError(storageError("read thread")));
|
|
1018
|
+
return yield* decodeRows$2(Schema.Array(ThreadRow), "effect_agent_threads", threadId, rows);
|
|
1019
1019
|
});
|
|
1020
1020
|
const append = Effect.fn("SqliteJournal.append")(function* (request) {
|
|
1021
1021
|
if (request.threadId.length > MAX_IDENTIFIER_LENGTH || request.batchId.length > MAX_IDENTIFIER_LENGTH || storedTextBytes(request.batchJson) > MAX_STORED_TEXT_BYTES || storedTextBytes(request.batchDigest) > MAX_STORED_TEXT_BYTES || storedTextBytes(request.tailDigest) > MAX_STORED_TEXT_BYTES || request.records.some((record) => record.recordId.length > MAX_IDENTIFIER_LENGTH || storedTextBytes(record.recordJson) > MAX_STORED_TEXT_BYTES)) return yield* SqliteStorageError.make({
|
|
@@ -1037,7 +1037,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1037
1037
|
producer_epoch
|
|
1038
1038
|
FROM effect_agent_threads
|
|
1039
1039
|
WHERE thread_id = ${request.threadId}
|
|
1040
|
-
`.pipe(Effect.mapError(storageError
|
|
1040
|
+
`.pipe(Effect.mapError(storageError("read append tail")));
|
|
1041
1041
|
const thread = yield* decodeSingleRow(Schema.Array(ThreadRow), "effect_agent_threads", request.threadId, threadRows);
|
|
1042
1042
|
if (request.producerEpoch !== thread.producer_epoch) return yield* SqliteFenceRejected.make({
|
|
1043
1043
|
producerEpoch: request.producerEpoch,
|
|
@@ -1056,8 +1056,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1056
1056
|
FROM effect_agent_canonical_batches
|
|
1057
1057
|
WHERE thread_id = ${request.threadId}
|
|
1058
1058
|
AND batch_id = ${request.batchId}
|
|
1059
|
-
`.pipe(Effect.mapError(storageError
|
|
1060
|
-
const batches = yield* decodeRows$
|
|
1059
|
+
`.pipe(Effect.mapError(storageError("read idempotent batch")));
|
|
1060
|
+
const batches = yield* decodeRows$2(Schema.Array(BatchRow), "effect_agent_canonical_batches", `${request.threadId}/${request.batchId}`, batchRows);
|
|
1061
1061
|
if (batches.length > 1) return yield* SqliteStorageCorruptionError.make({
|
|
1062
1062
|
table: "effect_agent_canonical_batches",
|
|
1063
1063
|
rowKey: `${request.threadId}/${request.batchId}`,
|
|
@@ -1097,8 +1097,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1097
1097
|
WHERE thread_id = ${request.threadId}
|
|
1098
1098
|
AND record_id IN ${sql.in(recordIds)}
|
|
1099
1099
|
ORDER BY sequence
|
|
1100
|
-
`.pipe(Effect.mapError(storageError
|
|
1101
|
-
const existingRecords = yield* decodeRows$
|
|
1100
|
+
`.pipe(Effect.mapError(storageError("check canonical record identities")));
|
|
1101
|
+
const existingRecords = yield* decodeRows$2(Schema.Array(RecordRow), "effect_agent_canonical_records", `${request.threadId}/record_ids`, existingRecordRows);
|
|
1102
1102
|
if (existingRecords.length > 0) return yield* SqliteAppendConflict.make({
|
|
1103
1103
|
message: `Canonical record ID ${existingRecords[0].record_id} already exists.`,
|
|
1104
1104
|
reason: "record-identity"
|
|
@@ -1131,7 +1131,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1131
1131
|
${request.tailDigest},
|
|
1132
1132
|
${request.batchJson}
|
|
1133
1133
|
)
|
|
1134
|
-
`.pipe(Effect.mapError(storageError
|
|
1134
|
+
`.pipe(Effect.mapError(storageError("insert canonical batch")));
|
|
1135
1135
|
yield* failpoint("append:after-batch-insert");
|
|
1136
1136
|
yield* Effect.forEach(request.records, (record, index) => Effect.gen(function* () {
|
|
1137
1137
|
yield* sql`
|
|
@@ -1148,7 +1148,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1148
1148
|
${request.batchId},
|
|
1149
1149
|
${record.recordJson}
|
|
1150
1150
|
)
|
|
1151
|
-
`.pipe(Effect.mapError(storageError
|
|
1151
|
+
`.pipe(Effect.mapError(storageError("insert canonical record")));
|
|
1152
1152
|
yield* failpoint("append:after-record-insert");
|
|
1153
1153
|
}), { discard: true });
|
|
1154
1154
|
yield* sql`
|
|
@@ -1158,7 +1158,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1158
1158
|
tail_digest = ${request.tailDigest},
|
|
1159
1159
|
producer_epoch = ${request.producerEpoch}
|
|
1160
1160
|
WHERE thread_id = ${request.threadId}
|
|
1161
|
-
`.pipe(Effect.mapError(storageError
|
|
1161
|
+
`.pipe(Effect.mapError(storageError("advance thread tail")));
|
|
1162
1162
|
yield* failpoint("append:after-tail-update");
|
|
1163
1163
|
return RawAppendResult.make({
|
|
1164
1164
|
firstSequence,
|
|
@@ -1181,8 +1181,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1181
1181
|
AND sequence > ${request.fromSequenceExclusive}
|
|
1182
1182
|
ORDER BY sequence
|
|
1183
1183
|
LIMIT ${request.limit}
|
|
1184
|
-
`.pipe(Effect.mapError(storageError
|
|
1185
|
-
return yield* decodeRows$
|
|
1184
|
+
`.pipe(Effect.mapError(storageError("read canonical records")));
|
|
1185
|
+
return yield* decodeRows$2(Schema.Array(RecordRow), "effect_agent_canonical_records", `${request.threadId}>${request.fromSequenceExclusive}`, rows);
|
|
1186
1186
|
});
|
|
1187
1187
|
const exportThread = Effect.fn("SqliteJournal.exportThread")(function* (threadId) {
|
|
1188
1188
|
return yield* withReadTransaction("export transaction")(Effect.gen(function* () {
|
|
@@ -1195,7 +1195,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1195
1195
|
producer_epoch
|
|
1196
1196
|
FROM effect_agent_threads
|
|
1197
1197
|
WHERE thread_id = ${threadId}
|
|
1198
|
-
`.pipe(Effect.mapError(storageError
|
|
1198
|
+
`.pipe(Effect.mapError(storageError("export thread")));
|
|
1199
1199
|
const thread = yield* decodeSingleRow(Schema.Array(ThreadRow), "effect_agent_threads", threadId, threadRows);
|
|
1200
1200
|
yield* failpoint("export:after-thread-read");
|
|
1201
1201
|
const batchRows = yield* sql`
|
|
@@ -1210,7 +1210,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1210
1210
|
FROM effect_agent_canonical_batches
|
|
1211
1211
|
WHERE thread_id = ${threadId}
|
|
1212
1212
|
ORDER BY first_sequence
|
|
1213
|
-
`.pipe(Effect.mapError(storageError
|
|
1213
|
+
`.pipe(Effect.mapError(storageError("export canonical batches")));
|
|
1214
1214
|
const recordRows = yield* sql`
|
|
1215
1215
|
SELECT
|
|
1216
1216
|
thread_id,
|
|
@@ -1221,7 +1221,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1221
1221
|
FROM effect_agent_canonical_records
|
|
1222
1222
|
WHERE thread_id = ${threadId}
|
|
1223
1223
|
ORDER BY sequence
|
|
1224
|
-
`.pipe(Effect.mapError(storageError
|
|
1224
|
+
`.pipe(Effect.mapError(storageError("export canonical records")));
|
|
1225
1225
|
const checkpointRows = yield* sql`
|
|
1226
1226
|
SELECT
|
|
1227
1227
|
thread_id,
|
|
@@ -1231,12 +1231,12 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1231
1231
|
FROM effect_agent_checkpoints
|
|
1232
1232
|
WHERE thread_id = ${threadId}
|
|
1233
1233
|
ORDER BY through_sequence
|
|
1234
|
-
`.pipe(Effect.mapError(storageError
|
|
1234
|
+
`.pipe(Effect.mapError(storageError("export checkpoints")));
|
|
1235
1235
|
return RawThreadExport.make({
|
|
1236
1236
|
thread,
|
|
1237
|
-
batches: yield* decodeRows$
|
|
1238
|
-
records: yield* decodeRows$
|
|
1239
|
-
checkpoints: yield* decodeRows$
|
|
1237
|
+
batches: yield* decodeRows$2(Schema.Array(BatchRow), "effect_agent_canonical_batches", threadId, batchRows),
|
|
1238
|
+
records: yield* decodeRows$2(Schema.Array(RecordRow), "effect_agent_canonical_records", threadId, recordRows),
|
|
1239
|
+
checkpoints: yield* decodeRows$2(Schema.Array(CheckpointRow), "effect_agent_checkpoints", threadId, checkpointRows)
|
|
1240
1240
|
});
|
|
1241
1241
|
}));
|
|
1242
1242
|
});
|
|
@@ -1255,7 +1255,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1255
1255
|
producer_epoch
|
|
1256
1256
|
FROM effect_agent_threads
|
|
1257
1257
|
WHERE thread_id = ${checkpoint.threadId}
|
|
1258
|
-
`.pipe(Effect.mapError(storageError
|
|
1258
|
+
`.pipe(Effect.mapError(storageError("read checkpoint tail")));
|
|
1259
1259
|
const thread = yield* decodeSingleRow(Schema.Array(ThreadRow), "effect_agent_threads", checkpoint.threadId, threadRows);
|
|
1260
1260
|
if (checkpoint.throughSequence > thread.tail_sequence) return yield* SqliteCheckpointConflict.make({ message: `Checkpoint sequence ${checkpoint.throughSequence} is after canonical tail ${thread.tail_sequence}.` });
|
|
1261
1261
|
const checkpointRows = yield* sql`
|
|
@@ -1267,8 +1267,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1267
1267
|
FROM effect_agent_checkpoints
|
|
1268
1268
|
WHERE thread_id = ${checkpoint.threadId}
|
|
1269
1269
|
AND through_sequence = ${checkpoint.throughSequence}
|
|
1270
|
-
`.pipe(Effect.mapError(storageError
|
|
1271
|
-
const existing = yield* decodeRows$
|
|
1270
|
+
`.pipe(Effect.mapError(storageError("read idempotent checkpoint")));
|
|
1271
|
+
const existing = yield* decodeRows$2(Schema.Array(CheckpointRow), "effect_agent_checkpoints", `${checkpoint.threadId}/${checkpoint.throughSequence}`, checkpointRows);
|
|
1272
1272
|
if (existing.length > 1) return yield* SqliteStorageCorruptionError.make({
|
|
1273
1273
|
table: "effect_agent_checkpoints",
|
|
1274
1274
|
rowKey: `${checkpoint.threadId}/${checkpoint.throughSequence}`,
|
|
@@ -1290,7 +1290,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1290
1290
|
${checkpoint.tailDigest},
|
|
1291
1291
|
${checkpoint.checkpointJson}
|
|
1292
1292
|
)
|
|
1293
|
-
`.pipe(Effect.mapError(storageError
|
|
1293
|
+
`.pipe(Effect.mapError(storageError("insert checkpoint")));
|
|
1294
1294
|
}));
|
|
1295
1295
|
});
|
|
1296
1296
|
const loadCheckpoint = Effect.fn("SqliteJournal.loadCheckpoint")(function* (threadId, atOrBeforeSequence) {
|
|
@@ -1305,8 +1305,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1305
1305
|
AND through_sequence <= ${atOrBeforeSequence}
|
|
1306
1306
|
ORDER BY through_sequence DESC
|
|
1307
1307
|
LIMIT 1
|
|
1308
|
-
`.pipe(Effect.mapError(storageError
|
|
1309
|
-
return yield* decodeRows$
|
|
1308
|
+
`.pipe(Effect.mapError(storageError("load checkpoint")));
|
|
1309
|
+
return yield* decodeRows$2(Schema.Array(CheckpointRow), "effect_agent_checkpoints", `${threadId}<=${atOrBeforeSequence}`, rows);
|
|
1310
1310
|
});
|
|
1311
1311
|
return {
|
|
1312
1312
|
append,
|
|
@@ -1329,8 +1329,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1329
1329
|
FROM effect_agent_canonical_batches
|
|
1330
1330
|
WHERE thread_id = ${threadId}
|
|
1331
1331
|
AND last_sequence = ${sequence}
|
|
1332
|
-
`.pipe(Effect.mapError(storageError
|
|
1333
|
-
return (yield* decodeRows$
|
|
1332
|
+
`.pipe(Effect.mapError(storageError("read canonical digest at sequence")));
|
|
1333
|
+
return (yield* decodeRows$2(Schema.Array(BatchRow), "effect_agent_canonical_batches", `${threadId}/${sequence}`, rows)).map((batch) => batch.tail_digest);
|
|
1334
1334
|
}),
|
|
1335
1335
|
loadCheckpoint,
|
|
1336
1336
|
materialize,
|
|
@@ -1347,7 +1347,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1347
1347
|
producer_epoch
|
|
1348
1348
|
FROM effect_agent_threads
|
|
1349
1349
|
ORDER BY thread_id
|
|
1350
|
-
`.pipe(Effect.mapError(storageError
|
|
1350
|
+
`.pipe(Effect.mapError(storageError("scan threads")));
|
|
1351
1351
|
const batches = yield* sql`
|
|
1352
1352
|
SELECT
|
|
1353
1353
|
thread_id,
|
|
@@ -1359,7 +1359,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1359
1359
|
batch_json
|
|
1360
1360
|
FROM effect_agent_canonical_batches
|
|
1361
1361
|
ORDER BY thread_id, first_sequence
|
|
1362
|
-
`.pipe(Effect.mapError(storageError
|
|
1362
|
+
`.pipe(Effect.mapError(storageError("scan canonical batches")));
|
|
1363
1363
|
const records = yield* sql`
|
|
1364
1364
|
SELECT
|
|
1365
1365
|
thread_id,
|
|
@@ -1369,7 +1369,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1369
1369
|
record_json
|
|
1370
1370
|
FROM effect_agent_canonical_records
|
|
1371
1371
|
ORDER BY thread_id, sequence
|
|
1372
|
-
`.pipe(Effect.mapError(storageError
|
|
1372
|
+
`.pipe(Effect.mapError(storageError("scan canonical records")));
|
|
1373
1373
|
const checkpoints = yield* sql`
|
|
1374
1374
|
SELECT
|
|
1375
1375
|
thread_id,
|
|
@@ -1378,12 +1378,12 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1378
1378
|
checkpoint_json
|
|
1379
1379
|
FROM effect_agent_checkpoints
|
|
1380
1380
|
ORDER BY thread_id, through_sequence
|
|
1381
|
-
`.pipe(Effect.mapError(storageError
|
|
1381
|
+
`.pipe(Effect.mapError(storageError("scan checkpoints")));
|
|
1382
1382
|
return {
|
|
1383
|
-
threads: yield* decodeRows$
|
|
1384
|
-
batches: yield* decodeRows$
|
|
1385
|
-
records: yield* decodeRows$
|
|
1386
|
-
checkpoints: yield* decodeRows$
|
|
1383
|
+
threads: yield* decodeRows$2(Schema.Array(ThreadRow), "effect_agent_threads", "startup_scan", threads),
|
|
1384
|
+
batches: yield* decodeRows$2(Schema.Array(BatchRow), "effect_agent_canonical_batches", "startup_scan", batches),
|
|
1385
|
+
records: yield* decodeRows$2(Schema.Array(RecordRow), "effect_agent_canonical_records", "startup_scan", records),
|
|
1386
|
+
checkpoints: yield* decodeRows$2(Schema.Array(CheckpointRow), "effect_agent_checkpoints", "startup_scan", checkpoints)
|
|
1387
1387
|
};
|
|
1388
1388
|
}));
|
|
1389
1389
|
}),
|
|
@@ -2004,7 +2004,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2004
2004
|
iso: new Date(millis).toISOString()
|
|
2005
2005
|
}));
|
|
2006
2006
|
const timestampMillis = (operation, rowKey) => (timestamp) => decodeUtcInstant(timestamp).pipe(Effect.map(DateTime.toEpochMillis), Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submission_ownership", rowKey, error.message)));
|
|
2007
|
-
const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows$
|
|
2007
|
+
const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows$2(Schema.Array(SubmissionRow), "effect_agent_submissions", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
2008
2008
|
const readSubmission = Effect.fn("SqliteSubmissionLedger.readSubmission")(function* (operation, submissionId) {
|
|
2009
2009
|
const rows = yield* sql`
|
|
2010
2010
|
SELECT ${sql.literal(SUBMISSION_COLUMNS)}
|
|
@@ -2035,7 +2035,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2035
2035
|
FROM effect_agent_submission_ownership
|
|
2036
2036
|
WHERE submission_id = ${submissionId}
|
|
2037
2037
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2038
|
-
const decoded = yield* decodeRows$
|
|
2038
|
+
const decoded = yield* decodeRows$2(Schema.Array(OwnershipRow), "effect_agent_submission_ownership", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
2039
2039
|
if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_submission_ownership", submissionId, "An ownership primary key returned more than one row.");
|
|
2040
2040
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
2041
2041
|
});
|
|
@@ -2100,7 +2100,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2100
2100
|
FROM effect_agent_settlement_reservations
|
|
2101
2101
|
WHERE submission_id = ${submissionId}
|
|
2102
2102
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2103
|
-
const decoded = yield* decodeRows$
|
|
2103
|
+
const decoded = yield* decodeRows$2(Schema.Array(ReservationRow), "effect_agent_settlement_reservations", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
2104
2104
|
if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_settlement_reservations", submissionId, "A settlement reservation primary key returned more than one row.");
|
|
2105
2105
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
2106
2106
|
});
|
|
@@ -2115,11 +2115,11 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2115
2115
|
FROM effect_agent_abort_intents
|
|
2116
2116
|
WHERE submission_id = ${submissionId}
|
|
2117
2117
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2118
|
-
const decoded = yield* decodeRows$
|
|
2118
|
+
const decoded = yield* decodeRows$2(Schema.Array(AbortIntentRow), "effect_agent_abort_intents", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
2119
2119
|
if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_abort_intents", submissionId, "An abort intent primary key returned more than one row.");
|
|
2120
2120
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
2121
2121
|
});
|
|
2122
|
-
const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows$
|
|
2122
|
+
const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows$2(Schema.Array(ChildReservationRow), "effect_agent_child_reservations", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
2123
2123
|
const readChildReservation = Effect.fn("SqliteSubmissionLedger.readChildReservation")(function* (operation, reservationId) {
|
|
2124
2124
|
const rows = yield* sql`
|
|
2125
2125
|
SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
|
|
@@ -2172,7 +2172,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2172
2172
|
WHERE submission_id = ${submissionId}
|
|
2173
2173
|
ORDER BY tool_call_id ASC
|
|
2174
2174
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2175
|
-
return yield* decodeRows$
|
|
2175
|
+
return yield* decodeRows$2(Schema.Array(ApprovalDecisionRow), "effect_agent_approval_decisions", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
2176
2176
|
});
|
|
2177
2177
|
const approvalIntentFromRow = Effect.fn("SqliteSubmissionLedger.approvalIntentFromRow")(function* (operation, row) {
|
|
2178
2178
|
return yield* decodeApprovalDecisionIntent({
|
|
@@ -2197,7 +2197,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2197
2197
|
WHERE submission_id = ${submissionId}
|
|
2198
2198
|
ORDER BY tool_call_id ASC
|
|
2199
2199
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2200
|
-
return yield* decodeRows$
|
|
2200
|
+
return yield* decodeRows$2(Schema.Array(UnknownResolutionRow), "effect_agent_unknown_resolutions", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
2201
2201
|
});
|
|
2202
2202
|
const unknownResolutionIntentFromRow = Effect.fn("SqliteSubmissionLedger.unknownResolutionIntentFromRow")(function* (operation, row) {
|
|
2203
2203
|
const resolution = yield* parseStoredJsonText(row.resolution_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_unknown_resolutions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
|
|
@@ -2228,7 +2228,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2228
2228
|
WHERE thread_id = ${threadId}
|
|
2229
2229
|
AND record_id = ${recordId}
|
|
2230
2230
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2231
|
-
return (yield* decodeRows$
|
|
2231
|
+
return (yield* decodeRows$2(Schema.Array(CanonicalRecordIdRow), "effect_agent_canonical_records", `${threadId}/${recordId}`, rows).pipe(Effect.mapError(internalFailure(operation)))).length === 0 ? void 0 : recordId;
|
|
2232
2232
|
});
|
|
2233
2233
|
const abortIntentFromRow = Effect.fn("SqliteSubmissionLedger.abortIntentFromRow")(function* (operation, submission, submissionId, row) {
|
|
2234
2234
|
const canonicalRecordId = yield* canonicalAbortRecordId(operation, submission.thread_id, submissionId);
|
|
@@ -2282,7 +2282,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2282
2282
|
FROM effect_agent_submissions
|
|
2283
2283
|
WHERE thread_id = ${validated.threadId}
|
|
2284
2284
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2285
|
-
const decodedMax = yield* decodeRows$
|
|
2285
|
+
const decodedMax = yield* decodeRows$2(Schema.Array(MaxQueueSequenceRow), "effect_agent_submissions", validated.threadId, maxRows).pipe(Effect.mapError(internalFailure(operation)));
|
|
2286
2286
|
const queueSequence = yield* decodeQueueSequence((decodedMax[0]?.max_queue_sequence ?? 0) + 1).pipe(Effect.mapError(internalFailure(operation)));
|
|
2287
2287
|
const now = yield* currentInstant;
|
|
2288
2288
|
yield* sql`
|
|
@@ -3430,306 +3430,6 @@ const submissionLedgerLayer = Layer.effectContext(makeServices());
|
|
|
3430
3430
|
*/
|
|
3431
3431
|
const ledgerLayer = (options) => Layer.unwrap(Effect.map(SqliteStorageConfig, (config) => submissionLedgerLayer.pipe(Layer.provide(Layer.mergeAll(Layer.succeed(SqliteStorageConfig)(config), storageFailpointLayer(options), SqliteClient.layer({ filename: options.filename }), NodeCrypto.layer))))).pipe(Layer.provide(storageConfigLayer(options)));
|
|
3432
3432
|
//#endregion
|
|
3433
|
-
//#region src/sqlite-memory-store.ts
|
|
3434
|
-
const STORAGE_VERSION = 2;
|
|
3435
|
-
const METADATA_COMPONENT = "memory";
|
|
3436
|
-
const DOCUMENT_TABLE = "effect_agent_memory_documents_v1";
|
|
3437
|
-
const RECEIPT_TABLE = "effect_agent_memory_receipts_v1";
|
|
3438
|
-
const StoredJson$1 = Schema.String.check(Schema.isMaxLength(16777216));
|
|
3439
|
-
const EncodedMemoryChange = Schema.Struct({
|
|
3440
|
-
commandJson: StoredJson$1,
|
|
3441
|
-
documentJson: StoredJson$1,
|
|
3442
|
-
resultJson: StoredJson$1
|
|
3443
|
-
});
|
|
3444
|
-
const equivalentContent = Schema.toEquivalence(MemoryWrite.Wire.members[0].fields.content);
|
|
3445
|
-
const equivalentScopes = Schema.toEquivalence(MemoryWrite.Wire.members[0].fields.scopes);
|
|
3446
|
-
var MemoryMetadataRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryMetadataRow")({ version: Schema.Int }) {};
|
|
3447
|
-
var MemoryTableRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryTableRow")({ name: Schema.NonEmptyString }) {};
|
|
3448
|
-
var MemoryDocumentRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryDocumentRow")({
|
|
3449
|
-
namespace: MemoryNamespaceAddress,
|
|
3450
|
-
source_id: MemoryKey.Wire.fields.id,
|
|
3451
|
-
format_version: Schema.Int,
|
|
3452
|
-
generation: Schema.Int,
|
|
3453
|
-
revision: Schema.NonEmptyString,
|
|
3454
|
-
document_json: StoredJson$1
|
|
3455
|
-
}) {};
|
|
3456
|
-
var MemoryReceiptRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryReceiptRow")({
|
|
3457
|
-
namespace: MemoryNamespaceAddress,
|
|
3458
|
-
operation_id: MemoryWrite.Wire.members[0].fields.operationId,
|
|
3459
|
-
source_id: MemoryKey.Wire.fields.id,
|
|
3460
|
-
format_version: Schema.Int,
|
|
3461
|
-
command_json: StoredJson$1,
|
|
3462
|
-
result_json: StoredJson$1
|
|
3463
|
-
}) {};
|
|
3464
|
-
var MemoryChangeCountRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryChangeCountRow")({ changed: Schema.Int }) {};
|
|
3465
|
-
var StoredMemoryCommand = class extends Schema.Class("@effect-agent/storage-sqlite/StoredMemoryCommand")({
|
|
3466
|
-
version: Schema.Literal(STORAGE_VERSION),
|
|
3467
|
-
value: MemoryWrite.Wire
|
|
3468
|
-
}) {};
|
|
3469
|
-
var StoredMemoryResult = class extends Schema.Class("@effect-agent/storage-sqlite/StoredMemoryResult")({
|
|
3470
|
-
version: Schema.Literal(STORAGE_VERSION),
|
|
3471
|
-
value: MemoryDocument.Wire
|
|
3472
|
-
}) {};
|
|
3473
|
-
const StoredVersionHeader = Schema.Struct({ version: Schema.Int });
|
|
3474
|
-
const storageError = (operation, reason = "unavailable") => MemoryStorageError.make({
|
|
3475
|
-
operation,
|
|
3476
|
-
reason
|
|
3477
|
-
});
|
|
3478
|
-
const query = (effect, operation) => effect.pipe(Effect.mapError(() => storageError(operation)));
|
|
3479
|
-
const decodeRows$2 = Effect.fn("SqliteMemoryStore.decodeRows")(function* (schema, rows, operation) {
|
|
3480
|
-
return yield* Schema.decodeUnknownEffect(Schema.Array(schema))(rows).pipe(Effect.mapError(() => storageError(operation, "corrupt")));
|
|
3481
|
-
});
|
|
3482
|
-
const decodeInput$1 = Effect.fn("SqliteMemoryStore.decodeInput")(function* (schema, value, operation) {
|
|
3483
|
-
return yield* Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => storageError(operation, "invalid-input")));
|
|
3484
|
-
});
|
|
3485
|
-
const encodeJson = Effect.fn("SqliteMemoryStore.encodeJson")(function* (schema, value, operation) {
|
|
3486
|
-
return yield* Schema.encodeEffect(Schema.fromJsonString(schema))(value).pipe(Effect.mapError(() => storageError(operation, "corrupt")));
|
|
3487
|
-
});
|
|
3488
|
-
const validateEncodedChange = Effect.fn("SqliteMemoryStore.validateEncodedChange")(function* (encoded, operation) {
|
|
3489
|
-
yield* Schema.decodeEffect(EncodedMemoryChange)(encoded).pipe(Effect.mapError(() => storageError(operation, "invalid-input")));
|
|
3490
|
-
});
|
|
3491
|
-
const decodeVersionedJson = Effect.fn("SqliteMemoryStore.decodeVersionedJson")(function* (schema, value, operation) {
|
|
3492
|
-
if ((yield* Schema.decodeEffect(Schema.fromJsonString(StoredVersionHeader))(value).pipe(Effect.mapError(() => storageError(operation, "corrupt")))).version !== STORAGE_VERSION) return yield* storageError(operation, "incompatible");
|
|
3493
|
-
const decoded = yield* Schema.decodeEffect(Schema.fromJsonString(schema))(value).pipe(Effect.mapError(() => storageError(operation, "corrupt")));
|
|
3494
|
-
if ((yield* encodeJson(schema, decoded, operation)) !== value) return yield* storageError(operation, "corrupt");
|
|
3495
|
-
return decoded;
|
|
3496
|
-
});
|
|
3497
|
-
const validateDocument = Effect.fn("SqliteMemoryStore.validateDocument")(function* (document, key, operation) {
|
|
3498
|
-
if (document.key.namespace.address !== key.namespace.address || document.key.id !== key.id || document.source.id !== key.id || document.source.revision !== String(document.generation) || document.generation === 1 !== (document.predecessor === null) || document.predecessor !== null && (document.predecessor.id !== key.id || document.predecessor.revision !== String(document.generation - 1))) return yield* storageError(operation, "corrupt");
|
|
3499
|
-
return document;
|
|
3500
|
-
});
|
|
3501
|
-
const validateReceiptResult = Effect.fn("SqliteMemoryStore.validateReceiptResult")(function* (command, result, operation) {
|
|
3502
|
-
if ((result.predecessor?.revision ?? null) !== command.expectedRevision) return yield* storageError(operation, "corrupt");
|
|
3503
|
-
if (command._tag === "Put") {
|
|
3504
|
-
if (result._tag !== "ActiveMemoryDocument" || result.source.locator !== command.locator) return yield* storageError(operation, "corrupt");
|
|
3505
|
-
if (!equivalentContent(command.content, result.content) || !equivalentScopes(command.scopes, result.scopes)) return yield* storageError(operation, "corrupt");
|
|
3506
|
-
return;
|
|
3507
|
-
}
|
|
3508
|
-
if (result._tag !== "WithdrawnMemoryDocument" || result.reason !== command.reason || result.predecessor === null || result.source.locator !== result.predecessor.locator) return yield* storageError(operation, "corrupt");
|
|
3509
|
-
});
|
|
3510
|
-
const initializeMemorySchema = Effect.fn("SqliteMemoryStore.initialize")(function* () {
|
|
3511
|
-
const sql = yield* SqlClient.SqlClient;
|
|
3512
|
-
const failpoint = yield* MemoryMutationFailpoint;
|
|
3513
|
-
yield* failpoint.hit("memory:initialize:before");
|
|
3514
|
-
yield* sql.withTransaction(Effect.gen(function* () {
|
|
3515
|
-
yield* sql`
|
|
3516
|
-
CREATE TABLE IF NOT EXISTS effect_agent_memory_metadata (
|
|
3517
|
-
component TEXT PRIMARY KEY NOT NULL,
|
|
3518
|
-
version INTEGER NOT NULL
|
|
3519
|
-
)
|
|
3520
|
-
`;
|
|
3521
|
-
const metadataRows = yield* sql`
|
|
3522
|
-
SELECT version
|
|
3523
|
-
FROM effect_agent_memory_metadata
|
|
3524
|
-
WHERE component = ${METADATA_COMPONENT}
|
|
3525
|
-
`;
|
|
3526
|
-
const metadata = yield* decodeRows$2(MemoryMetadataRow, metadataRows, "decode memory schema version");
|
|
3527
|
-
if (metadata.length > 1) return yield* storageError("decode memory schema version", "corrupt");
|
|
3528
|
-
const currentVersion = metadata[0]?.version;
|
|
3529
|
-
if (currentVersion !== void 0 && currentVersion !== STORAGE_VERSION) return yield* storageError("initialize memory schema", "incompatible");
|
|
3530
|
-
if (currentVersion === void 0) {
|
|
3531
|
-
const tableRows = yield* sql`
|
|
3532
|
-
SELECT name
|
|
3533
|
-
FROM sqlite_master
|
|
3534
|
-
WHERE type = 'table' AND name IN (${DOCUMENT_TABLE}, ${RECEIPT_TABLE})
|
|
3535
|
-
`;
|
|
3536
|
-
if ((yield* decodeRows$2(MemoryTableRow, tableRows, "inspect memory schema")).length > 0) return yield* storageError("initialize memory schema", "incompatible");
|
|
3537
|
-
yield* sql`
|
|
3538
|
-
CREATE TABLE effect_agent_memory_documents_v1 (
|
|
3539
|
-
namespace TEXT NOT NULL,
|
|
3540
|
-
source_id TEXT NOT NULL,
|
|
3541
|
-
format_version INTEGER NOT NULL,
|
|
3542
|
-
generation INTEGER NOT NULL,
|
|
3543
|
-
revision TEXT NOT NULL,
|
|
3544
|
-
document_json TEXT NOT NULL,
|
|
3545
|
-
PRIMARY KEY (namespace, source_id)
|
|
3546
|
-
)
|
|
3547
|
-
`;
|
|
3548
|
-
yield* sql`
|
|
3549
|
-
CREATE TABLE effect_agent_memory_receipts_v1 (
|
|
3550
|
-
namespace TEXT NOT NULL,
|
|
3551
|
-
operation_id TEXT NOT NULL,
|
|
3552
|
-
source_id TEXT NOT NULL,
|
|
3553
|
-
format_version INTEGER NOT NULL,
|
|
3554
|
-
command_json TEXT NOT NULL,
|
|
3555
|
-
result_json TEXT NOT NULL,
|
|
3556
|
-
PRIMARY KEY (namespace, operation_id)
|
|
3557
|
-
)
|
|
3558
|
-
`;
|
|
3559
|
-
yield* sql`
|
|
3560
|
-
INSERT INTO effect_agent_memory_metadata (component, version)
|
|
3561
|
-
VALUES (${METADATA_COMPONENT}, ${STORAGE_VERSION})
|
|
3562
|
-
`;
|
|
3563
|
-
}
|
|
3564
|
-
yield* sql`
|
|
3565
|
-
SELECT namespace, source_id, format_version, generation, revision, document_json
|
|
3566
|
-
FROM effect_agent_memory_documents_v1
|
|
3567
|
-
LIMIT 0
|
|
3568
|
-
`;
|
|
3569
|
-
yield* sql`
|
|
3570
|
-
SELECT namespace, operation_id, source_id, format_version, command_json, result_json
|
|
3571
|
-
FROM effect_agent_memory_receipts_v1
|
|
3572
|
-
LIMIT 0
|
|
3573
|
-
`;
|
|
3574
|
-
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storageError("initialize memory schema"))));
|
|
3575
|
-
yield* failpoint.hit("memory:initialize:after");
|
|
3576
|
-
});
|
|
3577
|
-
const makeMemoryReader = Effect.fn("SqliteMemoryStore.makeReader")(function* () {
|
|
3578
|
-
const sql = yield* SqlClient.SqlClient;
|
|
3579
|
-
const readDocument = Effect.fn("SqliteMemoryStore.readDocument")(function* (key, operation) {
|
|
3580
|
-
const rawRows = yield* query(sql`
|
|
3581
|
-
SELECT namespace, source_id, format_version, generation, revision, document_json
|
|
3582
|
-
FROM effect_agent_memory_documents_v1
|
|
3583
|
-
WHERE namespace = ${key.namespace.address} AND source_id = ${key.id}
|
|
3584
|
-
`, operation);
|
|
3585
|
-
const rows = yield* decodeRows$2(MemoryDocumentRow, rawRows, operation);
|
|
3586
|
-
if (rows.length === 0) return null;
|
|
3587
|
-
if (rows.length !== 1) return yield* storageError(operation, "corrupt");
|
|
3588
|
-
const row = rows[0];
|
|
3589
|
-
if (row.format_version !== STORAGE_VERSION) return yield* storageError(operation, "incompatible");
|
|
3590
|
-
const document = (yield* decodeVersionedJson(StoredMemoryResult, row.document_json, operation)).value;
|
|
3591
|
-
yield* validateDocument(document, key, operation);
|
|
3592
|
-
if (row.namespace !== key.namespace.address || row.source_id !== key.id || row.generation !== document.generation || row.revision !== document.source.revision) return yield* storageError(operation, "corrupt");
|
|
3593
|
-
return document;
|
|
3594
|
-
});
|
|
3595
|
-
return {
|
|
3596
|
-
get: Effect.fn("SqliteMemoryStore.get")(function* (key) {
|
|
3597
|
-
const decodedKey = yield* decodeInput$1(MemoryKey.Wire, key, "get memory document");
|
|
3598
|
-
return yield* readDocument(decodedKey, "get memory document");
|
|
3599
|
-
}),
|
|
3600
|
-
readDocument
|
|
3601
|
-
};
|
|
3602
|
-
});
|
|
3603
|
-
const makeMemoryServices = Effect.fn("SqliteMemoryStore.make")(function* () {
|
|
3604
|
-
const sql = yield* SqlClient.SqlClient;
|
|
3605
|
-
const failpoint = yield* MemoryMutationFailpoint;
|
|
3606
|
-
yield* initializeMemorySchema();
|
|
3607
|
-
const { get, readDocument } = yield* makeMemoryReader();
|
|
3608
|
-
const readReceipt = Effect.fn("SqliteMemoryStore.readReceipt")(function* (write, operation) {
|
|
3609
|
-
const rawRows = yield* query(sql`
|
|
3610
|
-
SELECT namespace, operation_id, source_id, format_version, command_json, result_json
|
|
3611
|
-
FROM effect_agent_memory_receipts_v1
|
|
3612
|
-
WHERE namespace = ${write.key.namespace.address} AND operation_id = ${write.operationId}
|
|
3613
|
-
`, operation);
|
|
3614
|
-
const rows = yield* decodeRows$2(MemoryReceiptRow, rawRows, operation);
|
|
3615
|
-
if (rows.length === 0) return null;
|
|
3616
|
-
if (rows.length !== 1) return yield* storageError(operation, "corrupt");
|
|
3617
|
-
const row = rows[0];
|
|
3618
|
-
if (row.format_version !== STORAGE_VERSION) return yield* storageError(operation, "incompatible");
|
|
3619
|
-
const command = yield* decodeVersionedJson(StoredMemoryCommand, row.command_json, `${operation} command`);
|
|
3620
|
-
const result = yield* decodeVersionedJson(StoredMemoryResult, row.result_json, `${operation} result`);
|
|
3621
|
-
if (row.namespace !== command.value.key.namespace.address || row.operation_id !== command.value.operationId || row.source_id !== command.value.key.id || result.value.key.namespace.address !== command.value.key.namespace.address || result.value.key.id !== command.value.key.id) return yield* storageError(operation, "corrupt");
|
|
3622
|
-
yield* validateDocument(result.value, command.value.key, operation);
|
|
3623
|
-
yield* validateReceiptResult(command.value, result.value, operation);
|
|
3624
|
-
return {
|
|
3625
|
-
commandJson: row.command_json,
|
|
3626
|
-
result: result.value
|
|
3627
|
-
};
|
|
3628
|
-
});
|
|
3629
|
-
const change = Effect.fn("SqliteMemoryStore.change")(function* (write) {
|
|
3630
|
-
const operation = "change memory document";
|
|
3631
|
-
const decodedWrite = yield* decodeInput$1(MemoryWrite.Wire, write, operation);
|
|
3632
|
-
const commandJson = yield* encodeJson(StoredMemoryCommand, StoredMemoryCommand.make({
|
|
3633
|
-
version: STORAGE_VERSION,
|
|
3634
|
-
value: decodedWrite
|
|
3635
|
-
}), "encode memory command");
|
|
3636
|
-
yield* failpoint.hit("memory:change:before");
|
|
3637
|
-
const transactionResult = yield* sql.withTransaction(Effect.gen(function* () {
|
|
3638
|
-
const receipt = yield* readReceipt(decodedWrite, operation);
|
|
3639
|
-
if (receipt !== null) {
|
|
3640
|
-
if (receipt.commandJson !== commandJson) return yield* MemoryOperationConflict.make({
|
|
3641
|
-
key: decodedWrite.key,
|
|
3642
|
-
operationId: decodedWrite.operationId
|
|
3643
|
-
});
|
|
3644
|
-
return {
|
|
3645
|
-
document: receipt.result,
|
|
3646
|
-
changed: false
|
|
3647
|
-
};
|
|
3648
|
-
}
|
|
3649
|
-
const current = yield* readDocument(decodedWrite.key, operation);
|
|
3650
|
-
const modifiedAt = yield* Clock.currentTimeMillis;
|
|
3651
|
-
const next = yield* applyMemoryWrite(current, decodedWrite, modifiedAt);
|
|
3652
|
-
const resultJson = yield* encodeJson(StoredMemoryResult, StoredMemoryResult.make({
|
|
3653
|
-
version: STORAGE_VERSION,
|
|
3654
|
-
value: next
|
|
3655
|
-
}), "encode memory result");
|
|
3656
|
-
const documentJson = resultJson;
|
|
3657
|
-
yield* validateEncodedChange({
|
|
3658
|
-
commandJson,
|
|
3659
|
-
documentJson,
|
|
3660
|
-
resultJson
|
|
3661
|
-
}, operation);
|
|
3662
|
-
if (current === null) yield* sql`
|
|
3663
|
-
INSERT INTO effect_agent_memory_documents_v1 (
|
|
3664
|
-
namespace, source_id, format_version, generation, revision, document_json
|
|
3665
|
-
) VALUES (
|
|
3666
|
-
${next.key.namespace.address}, ${next.key.id}, ${STORAGE_VERSION},
|
|
3667
|
-
${next.generation}, ${next.source.revision}, ${documentJson}
|
|
3668
|
-
)
|
|
3669
|
-
`;
|
|
3670
|
-
else yield* sql`
|
|
3671
|
-
UPDATE effect_agent_memory_documents_v1
|
|
3672
|
-
SET format_version = ${STORAGE_VERSION},
|
|
3673
|
-
generation = ${next.generation},
|
|
3674
|
-
revision = ${next.source.revision},
|
|
3675
|
-
document_json = ${documentJson}
|
|
3676
|
-
WHERE namespace = ${next.key.namespace.address}
|
|
3677
|
-
AND source_id = ${next.key.id}
|
|
3678
|
-
AND generation = ${current.generation}
|
|
3679
|
-
AND revision = ${current.source.revision}
|
|
3680
|
-
`;
|
|
3681
|
-
const changedRows = yield* sql`
|
|
3682
|
-
SELECT changes() AS changed
|
|
3683
|
-
`;
|
|
3684
|
-
const changed = yield* decodeRows$2(MemoryChangeCountRow, changedRows, operation);
|
|
3685
|
-
if (changed.length !== 1 || changed[0].changed !== 1) return yield* storageError(operation, "corrupt");
|
|
3686
|
-
yield* failpoint.hit("memory:change:after-state");
|
|
3687
|
-
yield* sql`
|
|
3688
|
-
INSERT INTO effect_agent_memory_receipts_v1 (
|
|
3689
|
-
namespace, operation_id, source_id, format_version, command_json, result_json
|
|
3690
|
-
) VALUES (
|
|
3691
|
-
${decodedWrite.key.namespace.address}, ${decodedWrite.operationId}, ${decodedWrite.key.id},
|
|
3692
|
-
${STORAGE_VERSION}, ${commandJson}, ${resultJson}
|
|
3693
|
-
)
|
|
3694
|
-
`;
|
|
3695
|
-
yield* failpoint.hit("memory:change:after-receipt");
|
|
3696
|
-
return {
|
|
3697
|
-
document: next,
|
|
3698
|
-
changed: true
|
|
3699
|
-
};
|
|
3700
|
-
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storageError(operation))));
|
|
3701
|
-
if (transactionResult.changed) yield* failpoint.hit("memory:change:after");
|
|
3702
|
-
return transactionResult.document;
|
|
3703
|
-
});
|
|
3704
|
-
return Context.make(MemoryReader, MemoryReader.fromAdapter({ get })).pipe(Context.add(MemoryWriter, MemoryWriter.fromAdapter({ change })));
|
|
3705
|
-
});
|
|
3706
|
-
/** SQLite memory ports with the mutation failpoint kept injectable for recovery tests. */
|
|
3707
|
-
const memoryStoreLayerWithFailpoints = Layer.effectContext(makeMemoryServices());
|
|
3708
|
-
/** SQLite memory reader and writer with the production no-op mutation failpoint. */
|
|
3709
|
-
const memoryStoreLayer = memoryStoreLayerWithFailpoints.pipe(Layer.provide(MemoryMutationFailpoint.layer));
|
|
3710
|
-
/** Reads an existing memory schema without writes, transactions, or mutation failpoints. */
|
|
3711
|
-
const memoryReaderLayer = Layer.effect(MemoryReader, Effect.gen(function* () {
|
|
3712
|
-
const sql = yield* SqlClient.SqlClient;
|
|
3713
|
-
const operation = "open memory reader";
|
|
3714
|
-
const tables = yield* query(sql`
|
|
3715
|
-
SELECT name FROM sqlite_master
|
|
3716
|
-
WHERE type = 'table' AND name IN (
|
|
3717
|
-
'effect_agent_memory_metadata', ${DOCUMENT_TABLE}, ${RECEIPT_TABLE}
|
|
3718
|
-
)
|
|
3719
|
-
`, operation).pipe(Effect.flatMap((rows) => decodeRows$2(MemoryTableRow, rows, operation)));
|
|
3720
|
-
if (tables.length !== 3) return yield* storageError(operation, tables.length === 0 ? "unavailable" : "incompatible");
|
|
3721
|
-
const metadata = yield* query(sql`
|
|
3722
|
-
SELECT version FROM effect_agent_memory_metadata WHERE component = ${METADATA_COMPONENT}
|
|
3723
|
-
`, operation).pipe(Effect.flatMap((rows) => decodeRows$2(MemoryMetadataRow, rows, operation)));
|
|
3724
|
-
if (metadata.length !== 1 || metadata[0].version !== STORAGE_VERSION) return yield* storageError(operation, "incompatible");
|
|
3725
|
-
yield* query(sql`
|
|
3726
|
-
SELECT namespace, source_id, format_version, generation, revision, document_json
|
|
3727
|
-
FROM effect_agent_memory_documents_v1 LIMIT 0
|
|
3728
|
-
`, operation);
|
|
3729
|
-
const { get } = yield* makeMemoryReader();
|
|
3730
|
-
return MemoryReader.fromAdapter({ get });
|
|
3731
|
-
}));
|
|
3732
|
-
//#endregion
|
|
3733
3433
|
//#region src/sqlite-schedule-store.ts
|
|
3734
3434
|
const StoredScheduleJson = Schema.String.check(Schema.isMaxLength(16777216));
|
|
3735
3435
|
const StoredDeadline = Schema.NullOr(ScheduleInstant);
|