@effect-agent/storage-sqlite 0.1.0-beta.38 → 0.1.0-beta.40
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 +34 -114
- package/dist/index.mjs +1693 -494
- package/dist/index.mjs.map +1 -1
- package/dist/sqlite-storage-failpoint-Cr0SXflP.d.mts +95 -0
- package/dist/sqlite-storage-failpoint-DIwWk5dw.mjs +12 -0
- package/dist/sqlite-storage-failpoint-DIwWk5dw.mjs.map +1 -0
- package/dist/testing.d.mts +15 -0
- package/dist/testing.mjs +19 -0
- package/dist/testing.mjs.map +1 -0
- package/package.json +14 -6
- package/src/errors.ts +3 -3
- package/src/index.ts +5 -2
- package/src/migrations.ts +106 -84
- package/src/sqlite-activity-store.ts +503 -0
- package/src/sqlite-journal.ts +147 -156
- package/src/sqlite-ledger.ts +60 -60
- package/src/sqlite-memory-store.ts +532 -0
- package/src/sqlite-schedule-store.ts +2 -2
- package/src/sqlite-storage-config.ts +1 -1
- package/src/sqlite-storage-failpoint-testing.ts +38 -0
- package/src/sqlite-storage-failpoint.ts +1 -31
- package/src/sqlite-subscription-store.ts +935 -0
- package/src/{sqlite-conversation-store.ts → sqlite-thread-store.ts} +248 -267
- package/src/testing.ts +2 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
2
|
+
//#region src/errors.d.ts
|
|
3
|
+
declare const SqliteStorageCompatibilityError_base: Schema.Class<SqliteStorageCompatibilityError, Schema.TaggedStruct<"SqliteStorageCompatibilityError", {
|
|
4
|
+
readonly actualVersion: Schema.Int;
|
|
5
|
+
readonly message: Schema.String;
|
|
6
|
+
readonly supportedVersion: Schema.Int;
|
|
7
|
+
}>, import("effect/Cause").YieldableError>;
|
|
8
|
+
/** The SQLite file uses a private-development format this adapter cannot read. */
|
|
9
|
+
declare class SqliteStorageCompatibilityError extends SqliteStorageCompatibilityError_base {}
|
|
10
|
+
declare const SqliteStorageCorruptionError_base: Schema.Class<SqliteStorageCorruptionError, Schema.TaggedStruct<"SqliteStorageCorruptionError", {
|
|
11
|
+
readonly message: Schema.String;
|
|
12
|
+
readonly rowKey: Schema.String;
|
|
13
|
+
readonly table: Schema.String;
|
|
14
|
+
}>, import("effect/Cause").YieldableError>;
|
|
15
|
+
/** Stored bytes failed the current Schema and cannot be used as recovery truth. */
|
|
16
|
+
declare class SqliteStorageCorruptionError extends SqliteStorageCorruptionError_base {}
|
|
17
|
+
declare const SqliteStorageError_base: Schema.Class<SqliteStorageError, Schema.TaggedStruct<"SqliteStorageError", {
|
|
18
|
+
readonly cause: Schema.optionalKey<Schema.Defect>;
|
|
19
|
+
readonly message: Schema.String;
|
|
20
|
+
readonly operation: Schema.String;
|
|
21
|
+
}>, import("effect/Cause").YieldableError>;
|
|
22
|
+
/** SQLite infrastructure failed while opening or operating the store. */
|
|
23
|
+
declare class SqliteStorageError extends SqliteStorageError_base {}
|
|
24
|
+
declare const SqliteLedgerError_base: Schema.Class<SqliteLedgerError, Schema.TaggedStruct<"SqliteLedgerError", {
|
|
25
|
+
readonly cause: Schema.optionalKey<Schema.Defect>;
|
|
26
|
+
readonly message: Schema.String;
|
|
27
|
+
readonly operation: Schema.String;
|
|
28
|
+
}>, import("effect/Cause").YieldableError>;
|
|
29
|
+
/**
|
|
30
|
+
* SQLite infrastructure failed while operating the Submission Ledger. Surfaces at the
|
|
31
|
+
* SubmissionLedger port as the typed `LedgerError` with this error preserved as its cause,
|
|
32
|
+
* so the adapter-level tag is never erased.
|
|
33
|
+
*/
|
|
34
|
+
declare class SqliteLedgerError extends SqliteLedgerError_base {}
|
|
35
|
+
declare const SqliteAppendConflict_base: Schema.Class<SqliteAppendConflict, Schema.TaggedStruct<"SqliteAppendConflict", {
|
|
36
|
+
readonly message: Schema.String;
|
|
37
|
+
readonly reason: Schema.Literals<readonly ["batch-digest", "record-identity", "tail"]>;
|
|
38
|
+
readonly actualTailSequence: Schema.optionalKey<Schema.brand<Schema.Natural, "@effect-agent/thread/CanonicalSequence">>;
|
|
39
|
+
readonly actualTailDigest: Schema.optionalKey<Schema.String>;
|
|
40
|
+
}>, import("effect/Cause").YieldableError>;
|
|
41
|
+
/**
|
|
42
|
+
* A canonical batch retry conflicts with existing append state. Tail conflicts carry the
|
|
43
|
+
* actual committed tail as a diagnostic resume hint.
|
|
44
|
+
*/
|
|
45
|
+
declare class SqliteAppendConflict extends SqliteAppendConflict_base {}
|
|
46
|
+
declare const SqliteFenceRejected_base: Schema.Class<SqliteFenceRejected, Schema.TaggedStruct<"SqliteFenceRejected", {
|
|
47
|
+
readonly actualEpoch: Schema.brand<Schema.Natural, "@effect-agent/thread/ProducerEpoch">;
|
|
48
|
+
readonly message: Schema.String;
|
|
49
|
+
readonly producerEpoch: Schema.brand<Schema.Natural, "@effect-agent/thread/ProducerEpoch">;
|
|
50
|
+
}>, import("effect/Cause").YieldableError>;
|
|
51
|
+
/**
|
|
52
|
+
* A producer epoch does not match the Thread's current writer registration. Appends
|
|
53
|
+
* require the exact registered epoch, so both older and newer unregistered epochs are fenced;
|
|
54
|
+
* a newer epoch takes over by materializing first.
|
|
55
|
+
*/
|
|
56
|
+
declare class SqliteFenceRejected extends SqliteFenceRejected_base {}
|
|
57
|
+
declare const SqliteWriteContention_base: Schema.Class<SqliteWriteContention, Schema.TaggedStruct<"SqliteWriteContention", {
|
|
58
|
+
readonly cause: Schema.optionalKey<Schema.Defect>;
|
|
59
|
+
readonly message: Schema.String;
|
|
60
|
+
readonly operation: Schema.String;
|
|
61
|
+
}>, import("effect/Cause").YieldableError>;
|
|
62
|
+
/**
|
|
63
|
+
* A write transaction could not acquire the SQLite write lock within the configured busy
|
|
64
|
+
* timeout (SQLITE_BUSY / SQLITE_LOCKED). Another transiently coexisting owner is writing;
|
|
65
|
+
* the operation did not mutate canonical state and is safe to retry.
|
|
66
|
+
*/
|
|
67
|
+
declare class SqliteWriteContention extends SqliteWriteContention_base {}
|
|
68
|
+
declare const SqliteCheckpointConflict_base: Schema.Class<SqliteCheckpointConflict, Schema.TaggedStruct<"SqliteCheckpointConflict", {
|
|
69
|
+
readonly message: Schema.String;
|
|
70
|
+
}>, import("effect/Cause").YieldableError>;
|
|
71
|
+
/** A checkpoint conflicts with a previously stored checkpoint at the same offset. */
|
|
72
|
+
declare class SqliteCheckpointConflict extends SqliteCheckpointConflict_base {}
|
|
73
|
+
declare const SqliteStorageFailpointLocation: Schema.Literals<readonly ["materialize:before", "materialize:after", "append:before", "append:after-batch-insert", "append:after-record-insert", "append:after-tail-update", "append:after", "export:after-thread-read", "save-checkpoint:before", "save-checkpoint:after", "ledger:admit:before", "ledger:admit:after", "ledger:mark-ready:before", "ledger:mark-ready:after", "ledger:claim:before", "ledger:claim:after", "ledger:mark-input-applied:before", "ledger:mark-input-applied:after", "ledger:renew:before", "ledger:renew:after", "ledger:reserve-settlement:before", "ledger:reserve-settlement:after", "ledger:finalize-settlement:before", "ledger:finalize-settlement:after", "ledger:request-abort:before", "ledger:request-abort:after", "ledger:release:before", "ledger:release:after", "ledger:claim-joining:before", "ledger:claim-joining:after", "ledger:mark-joined:before", "ledger:mark-joined:after", "ledger:revert-joining:before", "ledger:revert-joining:after", "ledger:suspend:before", "ledger:suspend:after", "ledger:approval-decision:before", "ledger:approval-decision:after", "ledger:mark-unknown:before", "ledger:mark-unknown:after", "ledger:unknown-resolution:before", "ledger:unknown-resolution:after", "ledger:child-reservation:before", "ledger:child-reservation:after", "ledger:child-attach:before", "ledger:child-attach:after", "ledger:child-release-pending:before", "ledger:child-release-pending:after", "ledger:child-release:before", "ledger:child-release:after", "ledger:child-settled:before", "ledger:child-settled:after"]>;
|
|
74
|
+
type SqliteStorageFailpointLocation = typeof SqliteStorageFailpointLocation.Type;
|
|
75
|
+
declare const SqliteStorageFailpointError_base: Schema.Class<SqliteStorageFailpointError, Schema.TaggedStruct<"SqliteStorageFailpointError", {
|
|
76
|
+
readonly location: Schema.Literals<readonly ["materialize:before", "materialize:after", "append:before", "append:after-batch-insert", "append:after-record-insert", "append:after-tail-update", "append:after", "export:after-thread-read", "save-checkpoint:before", "save-checkpoint:after", "ledger:admit:before", "ledger:admit:after", "ledger:mark-ready:before", "ledger:mark-ready:after", "ledger:claim:before", "ledger:claim:after", "ledger:mark-input-applied:before", "ledger:mark-input-applied:after", "ledger:renew:before", "ledger:renew:after", "ledger:reserve-settlement:before", "ledger:reserve-settlement:after", "ledger:finalize-settlement:before", "ledger:finalize-settlement:after", "ledger:request-abort:before", "ledger:request-abort:after", "ledger:release:before", "ledger:release:after", "ledger:claim-joining:before", "ledger:claim-joining:after", "ledger:mark-joined:before", "ledger:mark-joined:after", "ledger:revert-joining:before", "ledger:revert-joining:after", "ledger:suspend:before", "ledger:suspend:after", "ledger:approval-decision:before", "ledger:approval-decision:after", "ledger:mark-unknown:before", "ledger:mark-unknown:after", "ledger:unknown-resolution:before", "ledger:unknown-resolution:after", "ledger:child-reservation:before", "ledger:child-reservation:after", "ledger:child-attach:before", "ledger:child-attach:after", "ledger:child-release-pending:before", "ledger:child-release-pending:after", "ledger:child-release:before", "ledger:child-release:after", "ledger:child-settled:before", "ledger:child-settled:after"]>;
|
|
77
|
+
}>, import("effect/Cause").YieldableError>;
|
|
78
|
+
/** Deterministic test-only fault or pause injected at a SQLite operation boundary. */
|
|
79
|
+
declare class SqliteStorageFailpointError extends SqliteStorageFailpointError_base {
|
|
80
|
+
get message(): string;
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/sqlite-storage-failpoint.d.ts
|
|
84
|
+
type SqliteStorageFailpointHandler = (location: SqliteStorageFailpointLocation) => Effect.Effect<void, SqliteStorageFailpointError>;
|
|
85
|
+
declare const SqliteStorageFailpoint_base: Context.ServiceClass<SqliteStorageFailpoint, "@effect-agent/storage-sqlite/SqliteStorageFailpoint", {
|
|
86
|
+
readonly hit: SqliteStorageFailpointHandler;
|
|
87
|
+
}>;
|
|
88
|
+
/** Explicit fault-injection authority used at SQLite operation boundaries. */
|
|
89
|
+
declare class SqliteStorageFailpoint extends SqliteStorageFailpoint_base {
|
|
90
|
+
/** Production default: no fault injection. */
|
|
91
|
+
static readonly layer: Layer.Layer<SqliteStorageFailpoint, never, never>;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { SqliteFenceRejected as a, SqliteStorageCorruptionError as c, SqliteStorageFailpointLocation as d, SqliteWriteContention as f, SqliteCheckpointConflict as i, SqliteStorageError as l, SqliteStorageFailpointHandler as n, SqliteLedgerError as o, SqliteAppendConflict as r, SqliteStorageCompatibilityError as s, SqliteStorageFailpoint as t, SqliteStorageFailpointError as u };
|
|
95
|
+
//# sourceMappingURL=sqlite-storage-failpoint-Cr0SXflP.d.mts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
//#region src/sqlite-storage-failpoint.ts
|
|
3
|
+
const noFailpoint = () => Effect.void;
|
|
4
|
+
/** Explicit fault-injection authority used at SQLite operation boundaries. */
|
|
5
|
+
var SqliteStorageFailpoint = class extends Context.Service()("@effect-agent/storage-sqlite/SqliteStorageFailpoint") {
|
|
6
|
+
/** Production default: no fault injection. */
|
|
7
|
+
static layer = Layer.succeed(this)({ hit: noFailpoint });
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { SqliteStorageFailpoint as t };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=sqlite-storage-failpoint-DIwWk5dw.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-storage-failpoint-DIwWk5dw.mjs","names":[],"sources":["../src/sqlite-storage-failpoint.ts"],"sourcesContent":["import { Context, Effect, Layer } from \"effect\";\n\nimport type { SqliteStorageFailpointError, SqliteStorageFailpointLocation } from \"./errors.ts\";\n\nexport type SqliteStorageFailpointHandler = (\n location: SqliteStorageFailpointLocation,\n) => Effect.Effect<void, SqliteStorageFailpointError>;\n\nconst noFailpoint: SqliteStorageFailpointHandler = () => Effect.void;\n\n/** Explicit fault-injection authority used at SQLite operation boundaries. */\nexport class SqliteStorageFailpoint extends Context.Service<\n SqliteStorageFailpoint,\n {\n readonly hit: SqliteStorageFailpointHandler;\n }\n>()(\"@effect-agent/storage-sqlite/SqliteStorageFailpoint\") {\n /** Production default: no fault injection. */\n static readonly layer = Layer.succeed(this)({ hit: noFailpoint });\n}\n"],"mappings":";;AAQA,MAAM,oBAAmD,OAAO;;AAGhE,IAAa,yBAAb,cAA4C,QAAQ,QAKlD,CAAC,CAAC,qDAAqD,CAAC,CAAC;;CAEzD,OAAgB,QAAQ,MAAM,QAAQ,IAAI,CAAC,CAAC,EAAE,KAAK,YAAY,CAAC;AAClE"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { n as SqliteStorageFailpointHandler, t as SqliteStorageFailpoint } from "./sqlite-storage-failpoint-Cr0SXflP.mjs";
|
|
2
|
+
import { Context, Effect, Layer } from "effect";
|
|
3
|
+
//#region src/sqlite-storage-failpoint-testing.d.ts
|
|
4
|
+
declare const SqliteStorageFailpointTestControl_base: Context.ServiceClass<SqliteStorageFailpointTestControl, "@effect-agent/storage-sqlite/SqliteStorageFailpointTestControl", {
|
|
5
|
+
readonly clear: Effect.Effect<void>;
|
|
6
|
+
readonly setHandler: (handler: SqliteStorageFailpointHandler) => Effect.Effect<void>;
|
|
7
|
+
}>;
|
|
8
|
+
/** Test-only control for replacing the active SQLite failpoint handler. */
|
|
9
|
+
declare class SqliteStorageFailpointTestControl extends SqliteStorageFailpointTestControl_base {
|
|
10
|
+
/** Reusable test Layer with a control service backed by the same handler Ref. */
|
|
11
|
+
static readonly layer: Layer.Layer<SqliteStorageFailpoint | SqliteStorageFailpointTestControl, never, never>;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { SqliteStorageFailpointTestControl };
|
|
15
|
+
//# sourceMappingURL=testing.d.mts.map
|
package/dist/testing.mjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { t as SqliteStorageFailpoint } from "./sqlite-storage-failpoint-DIwWk5dw.mjs";
|
|
2
|
+
import { Context, Effect, Layer, Ref } from "effect";
|
|
3
|
+
//#region src/sqlite-storage-failpoint-testing.ts
|
|
4
|
+
const noFailpoint = () => Effect.void;
|
|
5
|
+
/** Test-only control for replacing the active SQLite failpoint handler. */
|
|
6
|
+
var SqliteStorageFailpointTestControl = class SqliteStorageFailpointTestControl extends Context.Service()("@effect-agent/storage-sqlite/SqliteStorageFailpointTestControl") {
|
|
7
|
+
/** Reusable test Layer with a control service backed by the same handler Ref. */
|
|
8
|
+
static layer = Layer.effectContext(Effect.gen(function* () {
|
|
9
|
+
const handler = yield* Ref.make(noFailpoint);
|
|
10
|
+
return Context.make(SqliteStorageFailpoint, SqliteStorageFailpoint.of({ hit: (location) => Ref.get(handler).pipe(Effect.flatMap((current) => current(location))) })).pipe(Context.add(SqliteStorageFailpointTestControl, SqliteStorageFailpointTestControl.of({
|
|
11
|
+
clear: Ref.set(handler, noFailpoint),
|
|
12
|
+
setHandler: (next) => Ref.set(handler, next)
|
|
13
|
+
})));
|
|
14
|
+
}));
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { SqliteStorageFailpointTestControl };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=testing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.mjs","names":[],"sources":["../src/sqlite-storage-failpoint-testing.ts"],"sourcesContent":["import { Context, Effect, Layer, Ref } from \"effect\";\n\nimport {\n SqliteStorageFailpoint,\n type SqliteStorageFailpointHandler,\n} from \"./sqlite-storage-failpoint.ts\";\n\nconst noFailpoint: SqliteStorageFailpointHandler = () => Effect.void;\n\n/** Test-only control for replacing the active SQLite failpoint handler. */\nexport class SqliteStorageFailpointTestControl extends Context.Service<\n SqliteStorageFailpointTestControl,\n {\n readonly clear: Effect.Effect<void>;\n readonly setHandler: (handler: SqliteStorageFailpointHandler) => Effect.Effect<void>;\n }\n>()(\"@effect-agent/storage-sqlite/SqliteStorageFailpointTestControl\") {\n /** Reusable test Layer with a control service backed by the same handler Ref. */\n static readonly layer = Layer.effectContext(\n Effect.gen(function* () {\n const handler = yield* Ref.make<SqliteStorageFailpointHandler>(noFailpoint);\n return Context.make(\n SqliteStorageFailpoint,\n SqliteStorageFailpoint.of({\n hit: (location) => Ref.get(handler).pipe(Effect.flatMap((current) => current(location))),\n }),\n ).pipe(\n Context.add(\n SqliteStorageFailpointTestControl,\n SqliteStorageFailpointTestControl.of({\n clear: Ref.set(handler, noFailpoint),\n setHandler: (next) => Ref.set(handler, next),\n }),\n ),\n );\n }),\n );\n}\n"],"mappings":";;;AAOA,MAAM,oBAAmD,OAAO;;AAGhE,IAAa,oCAAb,MAAa,0CAA0C,QAAQ,QAM7D,CAAC,CAAC,gEAAgE,CAAC,CAAC;;CAEpE,OAAgB,QAAQ,MAAM,cAC5B,OAAO,IAAI,aAAa;EACtB,MAAM,UAAU,OAAO,IAAI,KAAoC,WAAW;EAC1E,OAAO,QAAQ,KACb,wBACA,uBAAuB,GAAG,EACxB,MAAM,aAAa,IAAI,IAAI,OAAO,CAAC,CAAC,KAAK,OAAO,SAAS,YAAY,QAAQ,QAAQ,CAAC,CAAC,EACzF,CAAC,CACH,CAAC,CAAC,KACA,QAAQ,IACN,mCACA,kCAAkC,GAAG;GACnC,OAAO,IAAI,IAAI,SAAS,WAAW;GACnC,aAAa,SAAS,IAAI,IAAI,SAAS,IAAI;EAC7C,CAAC,CACH,CACF;CACF,CAAC,CACH;AACF"}
|
package/package.json
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-agent/storage-sqlite",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.40",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"types": "./dist/index.d.mts",
|
|
7
7
|
"default": "./dist/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"./testing": {
|
|
10
|
+
"types": "./dist/testing.d.mts",
|
|
11
|
+
"default": "./dist/testing.mjs"
|
|
8
12
|
}
|
|
9
13
|
},
|
|
10
14
|
"dependencies": {
|
|
11
|
-
"@effect-agent/
|
|
15
|
+
"@effect-agent/core": "0.1.0-beta.40",
|
|
16
|
+
"@effect-agent/thread": "0.1.0-beta.40",
|
|
12
17
|
"@effect/platform-node": "4.0.0-rc.111",
|
|
13
|
-
"@effect/sql-sqlite-node": "4.0.0-rc.111"
|
|
14
|
-
"effect": "4.0.0-rc.111"
|
|
18
|
+
"@effect/sql-sqlite-node": "4.0.0-rc.111"
|
|
15
19
|
},
|
|
16
|
-
"
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"effect": "^4.0.0-rc.111"
|
|
22
|
+
},
|
|
23
|
+
"description": "Node SQLite persistence adapters for the Effect Agent durable runtime and optional memory lifecycle.",
|
|
17
24
|
"license": "MIT",
|
|
18
25
|
"repository": {
|
|
19
26
|
"type": "git",
|
|
@@ -35,7 +42,8 @@
|
|
|
35
42
|
},
|
|
36
43
|
"devDependencies": {
|
|
37
44
|
"@effect/vitest": "4.0.0-rc.111",
|
|
45
|
+
"effect": "4.0.0-rc.111",
|
|
38
46
|
"typescript": "7.0.2",
|
|
39
|
-
"vite-plus": "0.
|
|
47
|
+
"vite-plus": "0.3.0"
|
|
40
48
|
}
|
|
41
49
|
}
|
package/src/errors.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CanonicalSequence, ProducerEpoch } from "@effect-agent/
|
|
1
|
+
import { CanonicalSequence, ProducerEpoch } from "@effect-agent/thread";
|
|
2
2
|
import { Schema } from "effect";
|
|
3
3
|
|
|
4
4
|
/** The SQLite file uses a private-development format this adapter cannot read. */
|
|
@@ -60,7 +60,7 @@ export class SqliteAppendConflict extends Schema.TaggedError<SqliteAppendConflic
|
|
|
60
60
|
) {}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* A producer epoch does not match the
|
|
63
|
+
* A producer epoch does not match the Thread's current writer registration. Appends
|
|
64
64
|
* require the exact registered epoch, so both older and newer unregistered epochs are fenced;
|
|
65
65
|
* a newer epoch takes over by materializing first.
|
|
66
66
|
*/
|
|
@@ -103,7 +103,7 @@ export const SqliteStorageFailpointLocation = Schema.Literals([
|
|
|
103
103
|
"append:after-record-insert",
|
|
104
104
|
"append:after-tail-update",
|
|
105
105
|
"append:after",
|
|
106
|
-
"export:after-
|
|
106
|
+
"export:after-thread-read",
|
|
107
107
|
"save-checkpoint:before",
|
|
108
108
|
"save-checkpoint:after",
|
|
109
109
|
"ledger:admit:before",
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export * from "./errors.ts";
|
|
2
|
-
export
|
|
3
|
-
export * from "./sqlite-
|
|
2
|
+
export { CurrentSqliteStorageVersion } from "./migrations.ts";
|
|
3
|
+
export * from "./sqlite-activity-store.ts";
|
|
4
|
+
export * from "./sqlite-thread-store.ts";
|
|
4
5
|
export * from "./sqlite-ledger.ts";
|
|
6
|
+
export * from "./sqlite-memory-store.ts";
|
|
5
7
|
export * from "./sqlite-schedule-store.ts";
|
|
8
|
+
export * from "./sqlite-subscription-store.ts";
|
|
6
9
|
export * from "./sqlite-storage-config.ts";
|
|
7
10
|
export * from "./sqlite-storage-failpoint.ts";
|
package/src/migrations.ts
CHANGED
|
@@ -2,15 +2,16 @@ import { SqliteMigrator } from "@effect/sql-sqlite-node";
|
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
import * as SqlClient from "effect/unstable/sql/SqlClient";
|
|
4
4
|
|
|
5
|
-
export const CurrentSqliteStorageVersion =
|
|
5
|
+
export const CurrentSqliteStorageVersion = 7;
|
|
6
6
|
|
|
7
|
+
/** Initialize empty storage with the complete current schema. */
|
|
7
8
|
export const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
8
|
-
"
|
|
9
|
+
"1_current_thread_storage": Effect.gen(function* () {
|
|
9
10
|
const sql = yield* SqlClient.SqlClient;
|
|
10
11
|
|
|
11
12
|
yield* sql`
|
|
12
|
-
CREATE TABLE
|
|
13
|
-
|
|
13
|
+
CREATE TABLE effect_agent_threads (
|
|
14
|
+
thread_id TEXT PRIMARY KEY NOT NULL,
|
|
14
15
|
created_at TEXT NOT NULL,
|
|
15
16
|
tail_sequence INTEGER NOT NULL,
|
|
16
17
|
tail_digest TEXT NOT NULL,
|
|
@@ -20,64 +21,59 @@ export const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
20
21
|
|
|
21
22
|
yield* sql`
|
|
22
23
|
CREATE TABLE effect_agent_canonical_batches (
|
|
23
|
-
|
|
24
|
+
thread_id TEXT NOT NULL,
|
|
24
25
|
batch_id TEXT NOT NULL,
|
|
25
26
|
first_sequence INTEGER NOT NULL,
|
|
26
27
|
last_sequence INTEGER NOT NULL,
|
|
27
28
|
batch_digest TEXT NOT NULL,
|
|
28
29
|
tail_digest TEXT NOT NULL,
|
|
29
30
|
batch_json TEXT NOT NULL,
|
|
30
|
-
PRIMARY KEY (
|
|
31
|
-
FOREIGN KEY (
|
|
32
|
-
REFERENCES
|
|
31
|
+
PRIMARY KEY (thread_id, batch_id),
|
|
32
|
+
FOREIGN KEY (thread_id)
|
|
33
|
+
REFERENCES effect_agent_threads(thread_id)
|
|
33
34
|
ON DELETE RESTRICT
|
|
34
35
|
)
|
|
35
36
|
`.withoutTransform;
|
|
36
37
|
|
|
37
38
|
yield* sql`
|
|
38
39
|
CREATE TABLE effect_agent_canonical_records (
|
|
39
|
-
|
|
40
|
+
thread_id TEXT NOT NULL,
|
|
40
41
|
sequence INTEGER NOT NULL,
|
|
41
42
|
record_id TEXT NOT NULL,
|
|
42
43
|
batch_id TEXT NOT NULL,
|
|
43
44
|
record_json TEXT NOT NULL,
|
|
44
|
-
PRIMARY KEY (
|
|
45
|
-
UNIQUE (
|
|
46
|
-
FOREIGN KEY (
|
|
47
|
-
REFERENCES effect_agent_canonical_batches(
|
|
45
|
+
PRIMARY KEY (thread_id, sequence),
|
|
46
|
+
UNIQUE (thread_id, record_id),
|
|
47
|
+
FOREIGN KEY (thread_id, batch_id)
|
|
48
|
+
REFERENCES effect_agent_canonical_batches(thread_id, batch_id)
|
|
48
49
|
ON DELETE RESTRICT
|
|
49
50
|
)
|
|
50
51
|
`.withoutTransform;
|
|
51
52
|
|
|
52
53
|
yield* sql`
|
|
53
54
|
CREATE INDEX effect_agent_canonical_records_batch
|
|
54
|
-
ON effect_agent_canonical_records (
|
|
55
|
+
ON effect_agent_canonical_records (thread_id, batch_id, sequence)
|
|
55
56
|
`.withoutTransform;
|
|
56
57
|
|
|
57
58
|
yield* sql`
|
|
58
59
|
CREATE TABLE effect_agent_checkpoints (
|
|
59
|
-
|
|
60
|
+
thread_id TEXT NOT NULL,
|
|
60
61
|
through_sequence INTEGER NOT NULL,
|
|
61
62
|
tail_digest TEXT NOT NULL,
|
|
62
63
|
checkpoint_json TEXT NOT NULL,
|
|
63
|
-
PRIMARY KEY (
|
|
64
|
-
FOREIGN KEY (
|
|
65
|
-
REFERENCES
|
|
64
|
+
PRIMARY KEY (thread_id, through_sequence),
|
|
65
|
+
FOREIGN KEY (thread_id)
|
|
66
|
+
REFERENCES effect_agent_threads(thread_id)
|
|
66
67
|
ON DELETE RESTRICT
|
|
67
68
|
)
|
|
68
69
|
`.withoutTransform;
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"2_durable_submission_ledger": Effect.gen(function* () {
|
|
73
|
-
const sql = yield* SqlClient.SqlClient;
|
|
74
|
-
|
|
75
|
-
// Admission rows exist before Conversation materialization (durability §4), so
|
|
76
|
-
// conversation_id intentionally carries no foreign key into effect_agent_conversations.
|
|
71
|
+
// Admission rows exist before Thread materialization (durability §4), so
|
|
72
|
+
// thread_id intentionally carries no foreign key into effect_agent_threads.
|
|
77
73
|
yield* sql`
|
|
78
74
|
CREATE TABLE effect_agent_submissions (
|
|
79
75
|
submission_id TEXT PRIMARY KEY NOT NULL,
|
|
80
|
-
|
|
76
|
+
thread_id TEXT NOT NULL,
|
|
81
77
|
queue_sequence INTEGER NOT NULL,
|
|
82
78
|
principal TEXT NOT NULL,
|
|
83
79
|
idempotency_key TEXT NOT NULL,
|
|
@@ -93,8 +89,15 @@ export const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
93
89
|
ready_at TEXT,
|
|
94
90
|
input_applied_record_id TEXT,
|
|
95
91
|
input_applied_sequence INTEGER,
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
joined_host_submission_id TEXT,
|
|
93
|
+
suspended_reason_json TEXT,
|
|
94
|
+
suspended_at TEXT,
|
|
95
|
+
unknown_reason TEXT,
|
|
96
|
+
unknown_tool_call_ids_json TEXT,
|
|
97
|
+
parent_submission_id TEXT,
|
|
98
|
+
parent_tool_call_id TEXT,
|
|
99
|
+
UNIQUE (thread_id, principal, idempotency_key),
|
|
100
|
+
UNIQUE (thread_id, queue_sequence)
|
|
98
101
|
)
|
|
99
102
|
`.withoutTransform;
|
|
100
103
|
|
|
@@ -116,7 +119,7 @@ export const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
116
119
|
CREATE TABLE effect_agent_attempts (
|
|
117
120
|
attempt_id TEXT PRIMARY KEY NOT NULL,
|
|
118
121
|
submission_id TEXT NOT NULL,
|
|
119
|
-
|
|
122
|
+
thread_id TEXT NOT NULL,
|
|
120
123
|
owner_producer_id TEXT NOT NULL,
|
|
121
124
|
producer_epoch INTEGER NOT NULL,
|
|
122
125
|
claimed_at TEXT NOT NULL,
|
|
@@ -155,42 +158,6 @@ export const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
155
158
|
)
|
|
156
159
|
`.withoutTransform;
|
|
157
160
|
|
|
158
|
-
yield* sql`PRAGMA user_version = 2`.withoutTransform;
|
|
159
|
-
}),
|
|
160
|
-
"3_durable_tools_and_joined_input": Effect.gen(function* () {
|
|
161
|
-
const sql = yield* SqlClient.SqlClient;
|
|
162
|
-
|
|
163
|
-
// Joined queued input (plan §2.5, DUR-016): a joining/joined Submission records which host
|
|
164
|
-
// Run claimed it. The canonical input marker reuses input_applied_record_id/_sequence
|
|
165
|
-
// because the joined input IS the Submission's own canonical `input:{sid}` record.
|
|
166
|
-
yield* sql`
|
|
167
|
-
ALTER TABLE effect_agent_submissions
|
|
168
|
-
ADD COLUMN joined_host_submission_id TEXT
|
|
169
|
-
`.withoutTransform;
|
|
170
|
-
|
|
171
|
-
// Durable approval suspension (plan §2.6): the reason is the Schema-encoded
|
|
172
|
-
// SuspensionReason union so later suspension families stay additive.
|
|
173
|
-
yield* sql`
|
|
174
|
-
ALTER TABLE effect_agent_submissions
|
|
175
|
-
ADD COLUMN suspended_reason_json TEXT
|
|
176
|
-
`.withoutTransform;
|
|
177
|
-
yield* sql`
|
|
178
|
-
ALTER TABLE effect_agent_submissions
|
|
179
|
-
ADD COLUMN suspended_at TEXT
|
|
180
|
-
`.withoutTransform;
|
|
181
|
-
|
|
182
|
-
// Unknown Outcome marking (DUR-009/DUR-017): the marked open Tool Call identities are
|
|
183
|
-
// stored so `recordUnknownResolution` can reopen the lane only when every marked call
|
|
184
|
-
// has a durable resolution intent.
|
|
185
|
-
yield* sql`
|
|
186
|
-
ALTER TABLE effect_agent_submissions
|
|
187
|
-
ADD COLUMN unknown_reason TEXT
|
|
188
|
-
`.withoutTransform;
|
|
189
|
-
yield* sql`
|
|
190
|
-
ALTER TABLE effect_agent_submissions
|
|
191
|
-
ADD COLUMN unknown_tool_call_ids_json TEXT
|
|
192
|
-
`.withoutTransform;
|
|
193
|
-
|
|
194
161
|
yield* sql`
|
|
195
162
|
CREATE INDEX effect_agent_submissions_joined_host
|
|
196
163
|
ON effect_agent_submissions (joined_host_submission_id)
|
|
@@ -226,21 +193,8 @@ export const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
226
193
|
)
|
|
227
194
|
`.withoutTransform;
|
|
228
195
|
|
|
229
|
-
yield* sql`PRAGMA user_version = 3`.withoutTransform;
|
|
230
|
-
}),
|
|
231
|
-
"4_durable_subagents": Effect.gen(function* () {
|
|
232
|
-
const sql = yield* SqlClient.SqlClient;
|
|
233
|
-
|
|
234
196
|
// Durable attached children (spec §12, SUB-004): a child Submission records its immutable
|
|
235
197
|
// parent linkage at admission; the parent-side index serves the recovery attachment view.
|
|
236
|
-
yield* sql`
|
|
237
|
-
ALTER TABLE effect_agent_submissions
|
|
238
|
-
ADD COLUMN parent_submission_id TEXT
|
|
239
|
-
`.withoutTransform;
|
|
240
|
-
yield* sql`
|
|
241
|
-
ALTER TABLE effect_agent_submissions
|
|
242
|
-
ADD COLUMN parent_tool_call_id TEXT
|
|
243
|
-
`.withoutTransform;
|
|
244
198
|
yield* sql`
|
|
245
199
|
CREATE INDEX effect_agent_submissions_parent
|
|
246
200
|
ON effect_agent_submissions (parent_submission_id)
|
|
@@ -270,11 +224,6 @@ export const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
270
224
|
)
|
|
271
225
|
`.withoutTransform;
|
|
272
226
|
|
|
273
|
-
yield* sql`PRAGMA user_version = 4`.withoutTransform;
|
|
274
|
-
}),
|
|
275
|
-
"5_durable_schedules": Effect.gen(function* () {
|
|
276
|
-
const sql = yield* SqlClient.SqlClient;
|
|
277
|
-
|
|
278
227
|
// record_json is authoritative. The remaining columns support owner keyset paging and
|
|
279
228
|
// deadline queries without decoding unrelated future schedules.
|
|
280
229
|
yield* sql`
|
|
@@ -300,6 +249,79 @@ export const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
300
249
|
WHERE deadline_at_millis IS NOT NULL
|
|
301
250
|
`.withoutTransform;
|
|
302
251
|
|
|
303
|
-
yield* sql`
|
|
252
|
+
yield* sql`
|
|
253
|
+
CREATE TABLE effect_agent_subscription_sequences (
|
|
254
|
+
tenant_id TEXT NOT NULL,
|
|
255
|
+
source_address TEXT NOT NULL,
|
|
256
|
+
sequence INTEGER NOT NULL,
|
|
257
|
+
event_scan_cursor TEXT NOT NULL,
|
|
258
|
+
delivery_scan_cursor TEXT NOT NULL,
|
|
259
|
+
recovery_scan_cursor INTEGER NOT NULL,
|
|
260
|
+
PRIMARY KEY (tenant_id, source_address)
|
|
261
|
+
)
|
|
262
|
+
`.withoutTransform;
|
|
263
|
+
yield* sql`
|
|
264
|
+
CREATE TABLE effect_agent_subscriptions (
|
|
265
|
+
tenant_id TEXT NOT NULL,
|
|
266
|
+
source_address TEXT NOT NULL,
|
|
267
|
+
owner_id TEXT NOT NULL,
|
|
268
|
+
subscription_id TEXT NOT NULL,
|
|
269
|
+
ordinal INTEGER NOT NULL,
|
|
270
|
+
source_name TEXT NOT NULL,
|
|
271
|
+
source_version TEXT NOT NULL,
|
|
272
|
+
matching_key TEXT NOT NULL,
|
|
273
|
+
state TEXT NOT NULL,
|
|
274
|
+
expires_at_millis INTEGER NOT NULL,
|
|
275
|
+
recovery_at_millis INTEGER,
|
|
276
|
+
record_json TEXT NOT NULL,
|
|
277
|
+
PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id),
|
|
278
|
+
UNIQUE (tenant_id, source_address, ordinal)
|
|
279
|
+
)
|
|
280
|
+
`.withoutTransform;
|
|
281
|
+
yield* sql`CREATE INDEX effect_agent_subscriptions_owner ON effect_agent_subscriptions (tenant_id, source_address, owner_id, ordinal)`
|
|
282
|
+
.withoutTransform;
|
|
283
|
+
yield* sql`CREATE INDEX effect_agent_subscriptions_candidates ON effect_agent_subscriptions (tenant_id, source_address, source_name, source_version, matching_key, ordinal)`
|
|
284
|
+
.withoutTransform;
|
|
285
|
+
yield* sql`CREATE INDEX effect_agent_subscriptions_recovery ON effect_agent_subscriptions (tenant_id, source_address, recovery_at_millis, ordinal) WHERE recovery_at_millis IS NOT NULL`
|
|
286
|
+
.withoutTransform;
|
|
287
|
+
yield* sql`
|
|
288
|
+
CREATE TABLE effect_agent_subscription_events (
|
|
289
|
+
tenant_id TEXT NOT NULL,
|
|
290
|
+
source_address TEXT NOT NULL,
|
|
291
|
+
event_id TEXT NOT NULL,
|
|
292
|
+
source_name TEXT NOT NULL,
|
|
293
|
+
source_version TEXT NOT NULL,
|
|
294
|
+
matching_key TEXT NOT NULL,
|
|
295
|
+
payload_digest TEXT NOT NULL,
|
|
296
|
+
cutoff INTEGER NOT NULL,
|
|
297
|
+
cursor INTEGER NOT NULL,
|
|
298
|
+
routing_complete INTEGER NOT NULL,
|
|
299
|
+
next_attempt_at_millis INTEGER NOT NULL,
|
|
300
|
+
record_json TEXT NOT NULL,
|
|
301
|
+
PRIMARY KEY (tenant_id, source_address, event_id)
|
|
302
|
+
)
|
|
303
|
+
`.withoutTransform;
|
|
304
|
+
yield* sql`CREATE INDEX effect_agent_subscription_events_pending ON effect_agent_subscription_events (tenant_id, source_address, routing_complete, next_attempt_at_millis, event_id)`
|
|
305
|
+
.withoutTransform;
|
|
306
|
+
yield* sql`
|
|
307
|
+
CREATE TABLE effect_agent_subscription_deliveries (
|
|
308
|
+
tenant_id TEXT NOT NULL,
|
|
309
|
+
source_address TEXT NOT NULL,
|
|
310
|
+
owner_id TEXT NOT NULL,
|
|
311
|
+
subscription_id TEXT NOT NULL,
|
|
312
|
+
event_id TEXT NOT NULL,
|
|
313
|
+
delivery_key TEXT NOT NULL,
|
|
314
|
+
state TEXT NOT NULL,
|
|
315
|
+
next_attempt_at_millis INTEGER NOT NULL,
|
|
316
|
+
record_json TEXT NOT NULL,
|
|
317
|
+
PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id, event_id),
|
|
318
|
+
UNIQUE (tenant_id, source_address, delivery_key)
|
|
319
|
+
)
|
|
320
|
+
`.withoutTransform;
|
|
321
|
+
yield* sql`CREATE INDEX effect_agent_subscription_deliveries_pending ON effect_agent_subscription_deliveries (tenant_id, source_address, state, next_attempt_at_millis, delivery_key)`
|
|
322
|
+
.withoutTransform;
|
|
323
|
+
yield* sql`CREATE INDEX effect_agent_subscription_deliveries_registration ON effect_agent_subscription_deliveries (tenant_id, source_address, owner_id, subscription_id, delivery_key)`
|
|
324
|
+
.withoutTransform;
|
|
325
|
+
yield* sql`PRAGMA user_version = 7`.withoutTransform;
|
|
304
326
|
}),
|
|
305
327
|
});
|