@yielded/agent-storage-cloudflare 0.1.0-beta.168 → 0.1.0-beta.170

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.
@@ -1,4 +1,8 @@
1
- import { makeSqlSubscriptionStore } from "@yielded/agent-storage-sql/sql-subscription-store";
1
+ import {
2
+ backfillSubscriptionSettlementObservation,
3
+ makeSqlSubscriptionStore,
4
+ } from "@yielded/agent-storage-sql/sql-subscription-store";
5
+ import { sameSqliteLayoutStatement } from "@yielded/agent-storage-sql/sqlite-layout-inspection";
2
6
  import {
3
7
  SourcePartition,
4
8
  SubscriptionError,
@@ -10,7 +14,7 @@ import { Context, Effect, Layer, Schema } from "effect";
10
14
  import * as SqlClientService from "effect/sql/SqlClient";
11
15
  import type { SqlError } from "effect/sql/SqlError";
12
16
 
13
- const CURRENT_SUBSCRIPTION_STORE_VERSION = 3;
17
+ const CURRENT_SUBSCRIPTION_STORE_VERSION = 4;
14
18
 
15
19
  const ScanRow = Schema.Struct({
16
20
  event_scan_cursor: Schema.String,
@@ -64,99 +68,133 @@ const validate = <A, I>(schema: Schema.Codec<A, I>, value: unknown, code: string
64
68
  const decodeRows = <A, I>(schema: Schema.Codec<A, I>, rows: unknown, code: string) =>
65
69
  Schema.decodeUnknownEffect(Schema.Array(schema))(rows).pipe(Effect.mapError(() => corrupt(code)));
66
70
 
67
- const initializeDoSubscriptionStore = Effect.fnUntraced(function* () {
68
- const sql = yield* SqlClientService.SqlClient;
69
-
70
- const names = yield* sql<Record<string, unknown>>`
71
- SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'effect_agent_subscription_%'
72
- `.pipe(Effect.mapError(() => unavailable("inspect subscription storage")));
71
+ const layoutStatements = [
72
+ "CREATE TABLE effect_agent_subscription_store_state ( singleton INTEGER PRIMARY KEY NOT NULL CHECK(singleton=1), storage_version INTEGER NOT NULL, alarm_generation INTEGER NOT NULL )",
73
+ "CREATE TABLE effect_agent_subscription_sequences ( tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, sequence INTEGER NOT NULL, event_scan_cursor TEXT NOT NULL, delivery_scan_cursor TEXT NOT NULL, recovery_scan_cursor INTEGER NOT NULL, PRIMARY KEY (tenant_id, source_address) )",
74
+ "CREATE TABLE effect_agent_subscriptions ( tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, owner_id TEXT NOT NULL, subscription_id TEXT NOT NULL, ordinal INTEGER NOT NULL, source_name TEXT NOT NULL, source_version TEXT NOT NULL, matching_key TEXT NOT NULL, state TEXT NOT NULL, expires_at_millis INTEGER, recovery_at_millis INTEGER, recovery_present INTEGER NOT NULL DEFAULT 0, record_json TEXT NOT NULL, PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id), UNIQUE (tenant_id, source_address, ordinal) )",
75
+ "CREATE INDEX effect_agent_subscriptions_owner ON effect_agent_subscriptions (tenant_id, source_address, owner_id, ordinal)",
76
+ "CREATE INDEX effect_agent_subscriptions_capacity ON effect_agent_subscriptions (tenant_id, source_address, expires_at_millis) WHERE state IN ('active', 'paused')",
77
+ "CREATE INDEX effect_agent_subscriptions_owner_capacity ON effect_agent_subscriptions (tenant_id, source_address, owner_id, expires_at_millis) WHERE state IN ('active', 'paused')",
78
+ "CREATE INDEX effect_agent_subscriptions_candidates ON effect_agent_subscriptions (tenant_id, source_address, source_name, source_version, matching_key, ordinal)",
79
+ "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",
80
+ "CREATE TABLE effect_agent_subscription_events ( tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, event_id TEXT NOT NULL, source_name TEXT NOT NULL, source_version TEXT NOT NULL, matching_key TEXT NOT NULL, payload_digest TEXT NOT NULL, cutoff INTEGER NOT NULL, cursor INTEGER NOT NULL, routing_complete INTEGER NOT NULL, tombstone INTEGER NOT NULL DEFAULT 0, next_attempt_at_millis INTEGER NOT NULL, record_json TEXT NOT NULL, PRIMARY KEY (tenant_id, source_address, event_id) )",
81
+ "CREATE INDEX effect_agent_subscription_events_pending ON effect_agent_subscription_events (tenant_id, source_address, routing_complete, next_attempt_at_millis, event_id)",
82
+ "CREATE TABLE effect_agent_subscription_deliveries ( tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, owner_id TEXT NOT NULL, subscription_id TEXT NOT NULL, event_id TEXT NOT NULL, delivery_key TEXT NOT NULL, state TEXT NOT NULL, next_attempt_at_millis INTEGER NOT NULL, record_json TEXT NOT NULL, observe_settlement INTEGER NOT NULL DEFAULT 0 CHECK (observe_settlement IN (0, 1)), PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id, event_id), UNIQUE (tenant_id, source_address, delivery_key) )",
83
+ "CREATE INDEX effect_agent_subscription_deliveries_pending ON effect_agent_subscription_deliveries (tenant_id, source_address, state, next_attempt_at_millis, delivery_key)",
84
+ "CREATE INDEX effect_agent_subscription_deliveries_registration ON effect_agent_subscription_deliveries (tenant_id, source_address, owner_id, subscription_id, delivery_key)",
85
+ "CREATE INDEX effect_agent_subscription_deliveries_capacity ON effect_agent_subscription_deliveries (tenant_id, source_address, owner_id) WHERE state IN ('selected', 'prepared')",
86
+ "CREATE INDEX effect_agent_subscription_deliveries_observing ON effect_agent_subscription_deliveries (tenant_id, source_address, owner_id) WHERE state='delivered' AND observe_settlement=1",
87
+ ] as const;
88
+
89
+ const layoutObjects = layoutStatements.map((statement, index) => {
90
+ const name = /^CREATE (TABLE|INDEX) ([a-z_][a-z_0-9]*)/.exec(statement);
91
+
92
+ if (name === null) throw new Error("Invalid subscription layout statement");
93
+
94
+ return [name[1].toLowerCase(), name[2], index] as const;
95
+ });
73
96
 
74
- const decodedNames = yield* decodeRows(
75
- Schema.Struct({ name: Schema.String }),
76
- names,
77
- "inspect subscription storage",
78
- );
97
+ const addedIndexes = new Set([
98
+ "effect_agent_subscriptions_capacity",
99
+ "effect_agent_subscriptions_owner_capacity",
100
+ "effect_agent_subscription_deliveries_capacity",
101
+ "effect_agent_subscription_deliveries_observing",
102
+ ]);
103
+
104
+ const LayoutObject = Schema.Struct({
105
+ name: Schema.String,
106
+ type: Schema.String,
107
+ sql: Schema.NullOr(Schema.String),
108
+ });
79
109
 
80
- const expected = new Set([
81
- "effect_agent_subscription_store_state",
82
- "effect_agent_subscription_sequences",
83
- "effect_agent_subscriptions",
84
- "effect_agent_subscription_events",
85
- "effect_agent_subscription_deliveries",
86
- ]);
110
+ /** Only the released v3 layout may upgrade; schema, backfill and version publish commit together. */
111
+ const initializeDoSubscriptionStore = Effect.fnUntraced(function* () {
112
+ const sql = (yield* SqlClientService.SqlClient).withoutTransforms();
113
+ const tables = layoutObjects.filter(([type]) => type === "table").map(([, name]) => name);
87
114
 
88
- const hasState = decodedNames.some(
89
- ({ name }) => name === "effect_agent_subscription_store_state",
115
+ const readObjects = sql`SELECT name, type, sql FROM sqlite_master
116
+ WHERE name GLOB 'effect_agent_subscription*' OR (type='trigger' AND tbl_name IN ${sql.in(tables)})`.pipe(
117
+ Effect.flatMap((rows) => decodeRows(LayoutObject, rows, "subscription storage layout")),
90
118
  );
91
119
 
92
- if (!hasState && decodedNames.length > 0) return yield* corrupt("partial subscription storage");
93
- if (!hasState) {
94
- yield* sql
95
- .withTransaction(
96
- Effect.gen(function* () {
97
- yield* sql`CREATE TABLE effect_agent_subscription_store_state (
98
- singleton INTEGER PRIMARY KEY NOT NULL CHECK(singleton=1), storage_version INTEGER NOT NULL, alarm_generation INTEGER NOT NULL
99
- )`.withoutTransform;
100
- yield* sql`CREATE TABLE effect_agent_subscription_sequences (
101
- tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, sequence INTEGER NOT NULL,
102
- event_scan_cursor TEXT NOT NULL, delivery_scan_cursor TEXT NOT NULL, recovery_scan_cursor INTEGER NOT NULL,
103
- PRIMARY KEY (tenant_id, source_address)
104
- )`.withoutTransform;
105
- yield* sql`CREATE TABLE effect_agent_subscriptions (
106
- tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, owner_id TEXT NOT NULL, subscription_id TEXT NOT NULL,
107
- ordinal INTEGER NOT NULL, source_name TEXT NOT NULL, source_version TEXT NOT NULL, matching_key TEXT NOT NULL,
108
- state TEXT NOT NULL, expires_at_millis INTEGER, recovery_at_millis INTEGER, recovery_present INTEGER NOT NULL DEFAULT 0, record_json TEXT NOT NULL,
109
- PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id), UNIQUE (tenant_id, source_address, ordinal)
110
- )`.withoutTransform;
111
- yield* sql`CREATE INDEX effect_agent_subscriptions_owner ON effect_agent_subscriptions (tenant_id, source_address, owner_id, ordinal)`
112
- .withoutTransform;
113
- yield* sql`CREATE INDEX effect_agent_subscriptions_candidates ON effect_agent_subscriptions (tenant_id, source_address, source_name, source_version, matching_key, ordinal)`
114
- .withoutTransform;
115
- 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`
116
- .withoutTransform;
117
- yield* sql`CREATE TABLE effect_agent_subscription_events (
118
- tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, event_id TEXT NOT NULL, source_name TEXT NOT NULL,
119
- source_version TEXT NOT NULL, matching_key TEXT NOT NULL, payload_digest TEXT NOT NULL, cutoff INTEGER NOT NULL,
120
- cursor INTEGER NOT NULL, routing_complete INTEGER NOT NULL, tombstone INTEGER NOT NULL DEFAULT 0, next_attempt_at_millis INTEGER NOT NULL, record_json TEXT NOT NULL,
121
- PRIMARY KEY (tenant_id, source_address, event_id)
122
- )`.withoutTransform;
123
- 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)`
124
- .withoutTransform;
125
- yield* sql`CREATE TABLE effect_agent_subscription_deliveries (
126
- tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, owner_id TEXT NOT NULL, subscription_id TEXT NOT NULL,
127
- event_id TEXT NOT NULL, delivery_key TEXT NOT NULL, state TEXT NOT NULL, next_attempt_at_millis INTEGER NOT NULL,
128
- record_json TEXT NOT NULL, PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id, event_id),
129
- UNIQUE (tenant_id, source_address, delivery_key)
130
- )`.withoutTransform;
131
- 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)`
132
- .withoutTransform;
133
- yield* sql`CREATE INDEX effect_agent_subscription_deliveries_registration ON effect_agent_subscription_deliveries (tenant_id, source_address, owner_id, subscription_id, delivery_key)`
134
- .withoutTransform;
135
- yield* sql`INSERT INTO effect_agent_subscription_store_state (singleton, storage_version, alarm_generation)
136
- VALUES (1, ${CURRENT_SUBSCRIPTION_STORE_VERSION}, 0)`.withoutTransform;
137
- }),
120
+ const checkLayout = Effect.fnUntraced(function* (
121
+ objects: ReadonlyArray<typeof LayoutObject.Type>,
122
+ version: number,
123
+ ) {
124
+ if (
125
+ objects.some(
126
+ ({ name, type }) =>
127
+ (type === "table" && !tables.includes(name)) ||
128
+ type === "trigger" ||
129
+ (version === 3 && addedIndexes.has(name)),
138
130
  )
139
- .pipe(Effect.mapError(() => unavailable("initialize subscription storage")));
140
-
141
- return;
142
- }
143
- if (decodedNames.length !== expected.size || decodedNames.some(({ name }) => !expected.has(name)))
144
- return yield* corrupt("subscription storage tables");
131
+ )
132
+ return yield* corrupt("ambiguous subscription storage layout");
133
+ for (const [type, name, index] of layoutObjects) {
134
+ if (version === 3 && addedIndexes.has(name)) continue;
135
+
136
+ const expected =
137
+ version === 3
138
+ ? layoutStatements[index].replace(
139
+ ", observe_settlement INTEGER NOT NULL DEFAULT 0 CHECK (observe_settlement IN (0, 1))",
140
+ "",
141
+ )
142
+ : layoutStatements[index];
143
+
144
+ const actual = objects.find((row) => row.name === name);
145
+
146
+ if (
147
+ actual?.type !== type ||
148
+ actual.sql === null ||
149
+ !sameSqliteLayoutStatement(actual.sql, expected)
150
+ )
151
+ return yield* corrupt("incompatible subscription storage layout " + name);
152
+ }
153
+ });
145
154
 
146
- const rows = yield* sql<
147
- Record<string, unknown>
148
- >`SELECT storage_version, alarm_generation FROM effect_agent_subscription_store_state WHERE singleton=1`.pipe(
149
- Effect.mapError(() => unavailable("read subscription storage version")),
150
- );
155
+ yield* sql
156
+ .withTransaction(
157
+ Effect.gen(function* () {
158
+ const objects = yield* readObjects;
151
159
 
152
- const state = yield* decodeRows(StoreStateRow, rows, "read subscription storage version");
160
+ const hasState = objects.some(
161
+ ({ name }) => name === "effect_agent_subscription_store_state",
162
+ );
153
163
 
154
- if (state.length !== 1 || state[0].storage_version !== CURRENT_SUBSCRIPTION_STORE_VERSION)
155
- return yield* corrupt(
156
- state.length === 1
157
- ? `incompatible subscription storage version ${state[0].storage_version}; expected ${CURRENT_SUBSCRIPTION_STORE_VERSION}`
158
- : "invalid subscription storage version row",
159
- );
164
+ if (!hasState) {
165
+ if (objects.length > 0) return yield* corrupt("partial subscription storage");
166
+ for (const statement of layoutStatements) yield* sql.unsafe(statement).withoutTransform;
167
+ yield* sql`INSERT INTO effect_agent_subscription_store_state (singleton, storage_version, alarm_generation)
168
+ VALUES (1, ${CURRENT_SUBSCRIPTION_STORE_VERSION}, 0)`;
169
+
170
+ return;
171
+ }
172
+
173
+ const state = yield* decodeRows(
174
+ StoreStateRow,
175
+ yield* sql`SELECT storage_version, alarm_generation FROM effect_agent_subscription_store_state`,
176
+ "read subscription storage version",
177
+ );
178
+
179
+ if (state.length !== 1) return yield* corrupt("invalid subscription storage version row");
180
+ const version = state[0].storage_version;
181
+
182
+ if (version !== 3 && version !== CURRENT_SUBSCRIPTION_STORE_VERSION)
183
+ return yield* corrupt(
184
+ `incompatible subscription storage version ${version}; expected ${CURRENT_SUBSCRIPTION_STORE_VERSION}`,
185
+ );
186
+ yield* checkLayout(objects, version);
187
+ if (version === CURRENT_SUBSCRIPTION_STORE_VERSION) return;
188
+
189
+ yield* sql`ALTER TABLE effect_agent_subscription_deliveries ADD COLUMN observe_settlement INTEGER NOT NULL DEFAULT 0 CHECK (observe_settlement IN (0, 1))`;
190
+ yield* backfillSubscriptionSettlementObservation();
191
+ for (const [, name, index] of layoutObjects)
192
+ if (addedIndexes.has(name)) yield* sql.unsafe(layoutStatements[index]).withoutTransform;
193
+ yield* sql`UPDATE effect_agent_subscription_store_state SET storage_version=${CURRENT_SUBSCRIPTION_STORE_VERSION} WHERE singleton=1`;
194
+ yield* checkLayout(yield* readObjects, CURRENT_SUBSCRIPTION_STORE_VERSION);
195
+ }),
196
+ )
197
+ .pipe(Effect.catchTag("SqlError", () => unavailable("initialize subscription storage")));
160
198
  });
161
199
 
162
200
  const makeSubscriptionStore = Effect.fnUntraced(function* (owned: SourcePartition) {
@@ -15,7 +15,6 @@ import {
15
15
  canonicalRecordJson,
16
16
  makeSqlThreadArchiveRange,
17
17
  } from "@yielded/agent-storage-sql/sql-thread-archive-range";
18
- import { makeProgressAppendValidation } from "@yielded/agent-storage-sql/sql-thread-native-reads";
19
18
  import { makeSqlThreadWork } from "@yielded/agent-storage-sql/sql-thread-work";
20
19
  import { ThreadId } from "@yielded/agent/identifiers";
21
20
  import {
@@ -395,10 +394,6 @@ const makeJournal = (
395
394
  cause,
396
395
  });
397
396
 
398
- const validateProgress = yield* makeProgressAppendValidation().pipe(
399
- Effect.provideService(SqlClient.SqlClient, sql),
400
- );
401
-
402
397
  const executions = yield* makeSqlExecutions().pipe(
403
398
  Effect.provideService(SqlClient.SqlClient, sql),
404
399
  );
@@ -739,7 +734,6 @@ const makeJournal = (
739
734
  );
740
735
 
741
736
  yield* executionConflict(executions.validate(request));
742
- yield* validateProgress(request);
743
737
 
744
738
  const result = RawAppendResult.make({
745
739
  firstSequence,
@@ -1,3 +1,4 @@
1
+ import { backfillPeerMessageRetention } from "@yielded/agent-storage-sql/sql-message-delivery-store";
1
2
  import { SQL_PROMPT_PREDICATE } from "@yielded/agent-storage-sql/sql-thread-native-reads";
2
3
  import { createSqlThreadWorkTables } from "@yielded/agent-storage-sql/sql-thread-work";
3
4
  import { makeSqliteLayoutInspection } from "@yielded/agent-storage-sql/sqlite-layout-inspection";
@@ -12,7 +13,7 @@ import {
12
13
  DoStorageError,
13
14
  } from "../DoStorageError.ts";
14
15
 
15
- export const CurrentDoStorageVersion = 25;
16
+ export const CurrentDoStorageVersion = 26;
16
17
 
17
18
  /** Fresh layout only. Unsupported stores are rejected before these statements execute. */
18
19
  const layoutStatements = [
@@ -44,7 +45,7 @@ const layoutStatements = [
44
45
  'CREATE INDEX effect_agent_records_subtree ON "effect_agent_canonical_records"(thread_id, source_submission_id, sequence) WHERE record_tag = 22',
45
46
  'CREATE INDEX effect_agent_records_worker_input ON "effect_agent_canonical_records"(thread_id, message_id) WHERE record_tag = 34',
46
47
  "CREATE INDEX effect_agent_submissions_nonterminal ON effect_agent_submissions (thread_id, queue_sequence) WHERE state <> 'settled'",
47
- "CREATE TABLE effect_agent_message_deliveries ( owner_thread_id TEXT NOT NULL, message_id TEXT NOT NULL, version INTEGER NOT NULL, state TEXT NOT NULL, deadline_at_millis INTEGER, record_json TEXT NOT NULL, PRIMARY KEY (owner_thread_id, message_id) )",
48
+ "CREATE TABLE effect_agent_message_deliveries ( owner_thread_id TEXT NOT NULL, message_id TEXT NOT NULL, version INTEGER NOT NULL, state TEXT NOT NULL, deadline_at_millis INTEGER, record_json TEXT NOT NULL, peer_retained_until_millis INTEGER, PRIMARY KEY (owner_thread_id, message_id) )",
48
49
  "CREATE INDEX effect_agent_message_deliveries_due ON effect_agent_message_deliveries (deadline_at_millis, owner_thread_id, message_id) WHERE deadline_at_millis IS NOT NULL",
49
50
  "CREATE INDEX effect_agent_message_deliveries_pending ON \"effect_agent_message_deliveries\"(owner_thread_id, message_id) WHERE state NOT IN ('processed', 'refused')",
50
51
  "CREATE TABLE effect_agent_worker_stops (thread_id TEXT PRIMARY KEY NOT NULL, terminal TEXT)",
@@ -60,7 +61,8 @@ const layoutStatements = [
60
61
  "CREATE INDEX effect_agent_records_worker_completed ON effect_agent_canonical_records(thread_id, message_id, sequence) WHERE record_tag = 32",
61
62
  "CREATE INDEX effect_agent_records_worker_stop ON effect_agent_canonical_records(thread_id, worker_thread_id, sequence) WHERE record_tag = 38",
62
63
  "CREATE INDEX effect_agent_message_deliveries_identity ON effect_agent_message_deliveries(owner_thread_id, json_quote(message_id))",
63
- "CREATE INDEX effect_agent_message_deliveries_peer ON effect_agent_message_deliveries(owner_thread_id, message_id) WHERE state NOT IN ('processed', 'refused') AND json_extract(record_json, '$.envelope.messageAdmission.schemaVersion') = 1",
64
+ "CREATE INDEX effect_agent_message_deliveries_peer_retained ON effect_agent_message_deliveries(owner_thread_id, peer_retained_until_millis, message_id) WHERE peer_retained_until_millis IS NOT NULL",
65
+ "CREATE INDEX effect_agent_message_deliveries_peer ON effect_agent_message_deliveries(owner_thread_id, message_id) WHERE state NOT IN ('processed', 'refused') AND json_extract(record_json, '$.envelope.messageAdmission.peerName') IS NOT NULL",
64
66
  "CREATE TABLE effect_agent_journal_ranges (thread_id TEXT NOT NULL, first_sequence INTEGER NOT NULL, last_sequence INTEGER NOT NULL, record_count INTEGER NOT NULL, batch_count INTEGER NOT NULL, byte_count INTEGER NOT NULL, state TEXT NOT NULL, locator TEXT, PRIMARY KEY (thread_id, first_sequence), FOREIGN KEY (thread_id) REFERENCES effect_agent_threads(thread_id) ON DELETE RESTRICT)",
65
67
  "CREATE TABLE effect_agent_archive_batches (thread_id TEXT NOT NULL, batch_id TEXT NOT NULL, range_first_sequence INTEGER NOT NULL, batch_json TEXT NOT NULL, PRIMARY KEY (thread_id, batch_id), FOREIGN KEY (thread_id, batch_id) REFERENCES effect_agent_canonical_batches(thread_id, batch_id) ON DELETE RESTRICT, CONSTRAINT effect_agent_archive_batches_range_fkey FOREIGN KEY (thread_id, range_first_sequence) REFERENCES effect_agent_journal_ranges(thread_id, first_sequence) ON DELETE RESTRICT)",
66
68
  "CREATE TABLE effect_agent_archive_records (thread_id TEXT NOT NULL, sequence INTEGER NOT NULL, range_first_sequence INTEGER NOT NULL, record_json TEXT NOT NULL, PRIMARY KEY (thread_id, sequence), FOREIGN KEY (thread_id, sequence) REFERENCES effect_agent_canonical_records(thread_id, sequence) ON DELETE RESTRICT, CONSTRAINT effect_agent_archive_records_range_fkey FOREIGN KEY (thread_id, range_first_sequence) REFERENCES effect_agent_journal_ranges(thread_id, first_sequence) ON DELETE RESTRICT)",
@@ -82,13 +84,14 @@ const layoutStatements = [
82
84
  const headerStatement =
83
85
  "CREATE TABLE effect_agent_schema (singleton INTEGER PRIMARY KEY NOT NULL CHECK (singleton = 1), layout_version INTEGER NOT NULL CHECK (layout_version > 0), record_format TEXT NOT NULL CHECK (length(record_format) > 0))";
84
86
 
85
- const layoutObjects = layoutStatements.map((statement, index) => {
86
- const name = /^CREATE (TABLE|(?:UNIQUE )?INDEX|TRIGGER) "?([a-z_][a-z_0-9]*)"?/.exec(statement);
87
+ const layoutObjects = (statements: ReadonlyArray<string>) =>
88
+ statements.map((statement, index) => {
89
+ const name = /^CREATE (TABLE|(?:UNIQUE )?INDEX|TRIGGER) "?([a-z_][a-z_0-9]*)"?/.exec(statement);
87
90
 
88
- if (name === null) throw new Error("Invalid fresh layout statement");
91
+ if (name === null) throw new Error("Invalid fresh layout statement");
89
92
 
90
- return [name[1].replace("UNIQUE ", "").toLowerCase(), name[2], index] as const;
91
- });
93
+ return [name[1].replace("UNIQUE ", "").toLowerCase(), name[2], index] as const;
94
+ });
92
95
 
93
96
  const Legacy = Schema.Tuple([
94
97
  Schema.Struct({
@@ -115,22 +118,42 @@ const incompatible = (actualVersion: number, message: string) =>
115
118
  const storageError = (cause: SqlError) =>
116
119
  DoStorageError.make({ operation: "inspect storage layout", cause, message: cause.message });
117
120
 
118
- const { decode, readObjects, readHeader } = makeSqliteLayoutInspection({
119
- version: CurrentDoStorageVersion,
120
- statements: layoutStatements,
121
- objects: layoutObjects,
122
- headerStatement,
123
- incompatible,
124
- storageError,
125
- corruption: (table) =>
126
- DoStorageCorruptionError.make({
127
- table,
128
- rowKey: "schema",
129
- message: "Malformed storage layout or version header.",
130
- }),
131
- });
121
+ const inspection = (version: number, statements: ReadonlyArray<string>) =>
122
+ makeSqliteLayoutInspection({
123
+ version,
124
+ statements,
125
+ objects: layoutObjects(statements),
126
+ headerStatement,
127
+ incompatible,
128
+ storageError,
129
+ corruption: (table) =>
130
+ DoStorageCorruptionError.make({
131
+ table,
132
+ rowKey: "schema",
133
+ message: "Malformed storage layout or version header.",
134
+ }),
135
+ });
136
+
137
+ // Only the released predecessor is eligible; its record format and canonical tables are unchanged.
138
+ const addedIndexes = new Set(["effect_agent_message_deliveries_peer_retained"]);
139
+
140
+ const layout25Statements = layoutObjects(layoutStatements)
141
+ .filter(([, name]) => !addedIndexes.has(name))
142
+ .map(([, , index]) =>
143
+ layoutStatements[index]
144
+ .replace(", peer_retained_until_millis INTEGER", "")
145
+ .replace(
146
+ "'$.envelope.messageAdmission.peerName') IS NOT NULL",
147
+ "'$.envelope.messageAdmission.schemaVersion') = 1",
148
+ ),
149
+ );
132
150
 
133
- const inspectStorage = Effect.fnUntraced(function* (): Effect.fn.Return<
151
+ const { decode, readObjects, readHeader } = inspection(CurrentDoStorageVersion, layoutStatements);
152
+ const predecessor = inspection(25, layout25Statements);
153
+
154
+ const inspectStorage = Effect.fnUntraced(function* (
155
+ allowUpgrade = false,
156
+ ): Effect.fn.Return<
134
157
  DoStorageHeader | undefined,
135
158
  DoStorageCompatibilityError | DoStorageCorruptionError | DoStorageError,
136
159
  SqlClient.SqlClient
@@ -157,6 +180,16 @@ const inspectStorage = Effect.fnUntraced(function* (): Effect.fn.Return<
157
180
 
158
181
  const version = legacy.value;
159
182
 
183
+ if (allowUpgrade && version === 25) {
184
+ if (objects.some(({ name }) => addedIndexes.has(name)))
185
+ return yield* incompatible(version, "Mixed layout 25 and 26 objects.");
186
+ const header = yield* predecessor.readHeader(objects, version);
187
+
188
+ yield* predecessor.readManagedTriggers(true);
189
+
190
+ return header;
191
+ }
192
+
160
193
  return yield* readHeader(objects, version);
161
194
  });
162
195
 
@@ -170,15 +203,35 @@ export const readDoStorageHeader = Effect.fnUntraced(function* () {
170
203
  return header;
171
204
  });
172
205
 
173
- /** Validate under the writer transaction before any DDL; initialize only a fresh store. */
206
+ /** Initialize fresh storage or atomically upgrade the exact released layout 25. */
174
207
  export const ensureDoStorageLayout = Effect.fn("DoStorage.initializeLayout")(function* () {
175
208
  const sql = (yield* SqlClient.SqlClient).withoutTransforms();
176
209
 
177
210
  return yield* sql
178
211
  .withTransaction(
179
212
  Effect.gen(function* () {
180
- const header = yield* inspectStorage();
181
-
213
+ const header = yield* inspectStorage(true);
214
+
215
+ if (header?.layoutVersion === 25) {
216
+ yield* sql`ALTER TABLE effect_agent_message_deliveries ADD COLUMN peer_retained_until_millis INTEGER`;
217
+ yield* backfillPeerMessageRetention().pipe(
218
+ Effect.catchTag("MessageDeliveryError", () =>
219
+ DoStorageCorruptionError.make({
220
+ table: "effect_agent_message_deliveries",
221
+ rowKey: "upgrade",
222
+ message: "Invalid layout-25 delivery evidence; upgrade rolled back.",
223
+ }),
224
+ ),
225
+ );
226
+ yield* sql`DROP INDEX effect_agent_message_deliveries_peer`;
227
+ for (const [, name, index] of layoutObjects(layoutStatements))
228
+ if (addedIndexes.has(name) || name === "effect_agent_message_deliveries_peer")
229
+ yield* sql.unsafe(layoutStatements[index]).withoutTransform;
230
+ yield* sql`UPDATE effect_agent_schema SET layout_version=${CurrentDoStorageVersion} WHERE singleton=1`;
231
+ yield* sql`UPDATE effect_agent_meta SET value=${String(CurrentDoStorageVersion)} WHERE key='storage_version'`;
232
+
233
+ return yield* readDoStorageHeader();
234
+ }
182
235
  if (header !== undefined) return header;
183
236
  for (const statement of layoutStatements) yield* sql.unsafe(statement).withoutTransform;
184
237
  yield* createSqlThreadWorkTables().pipe(
@@ -1 +0,0 @@
1
- {"version":3,"file":"do-journal-Cawnsetj.mjs","names":["isSqlError","SqlClient"],"sources":["../src/internal/storage-span.ts","../src/internal/do-journal.ts"],"sourcesContent":["import { Effect, Predicate, Result } from \"effect\";\n\ntype Tagged = { readonly _tag: string; readonly reason?: unknown };\n\nexport const isAppendContention = (error: Tagged): boolean =>\n (error._tag === \"DoAppendConflict\" || error._tag === \"AppendConflict\") && error.reason === \"tail\";\n\n/** Expected refusals remain typed at the port, but are outcomes of the storage operation. */\nexport const storageResult = <A, E extends Tagged, R>(\n effect: Effect.Effect<A, E, R>,\n expected: (error: E) => boolean,\n) =>\n effect.pipe(\n Effect.map(Result.succeed),\n Effect.catchIf(expected, (error) => Effect.succeed(Result.fail(error))),\n );\n\n/** Only error discriminants enter telemetry; messages, SQL, identities and payloads do not. */\nexport const annotateStorageError = (error: Tagged) =>\n Effect.annotateCurrentSpan({\n \"error.type\": error._tag,\n ...(Predicate.hasProperty(error, \"cause\") &&\n Predicate.hasProperty(error.cause, \"_tag\") &&\n typeof error.cause._tag === \"string\"\n ? { \"error.cause.type\": error.cause._tag }\n : {}),\n });\n\nexport const withStorageSpan =\n (name: string, expected: (error: Tagged) => boolean = () => false) =>\n <A, E extends Tagged, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n storageResult(effect, expected).pipe(\n Effect.tap((result) =>\n Result.isFailure(result)\n ? Effect.annotateCurrentSpan({ \"storage.outcome\": result.failure._tag })\n : Effect.void,\n ),\n Effect.tapError(annotateStorageError),\n Effect.withSpan(name),\n Effect.flatMap(Effect.fromResult),\n );\n","import {\n makeCanonicalStorage,\n recordBatchId,\n} from \"@yielded/agent-storage-sql/sql-canonical-storage\";\nimport type { RawAppendRequest } from \"@yielded/agent-storage-sql/sql-journal\";\nimport { makeSqlExecutions } from \"@yielded/agent-storage-sql/sql-journal\";\nimport {\n makeSqlLifecyclePublication,\n type SqlLifecycleRetainMany,\n SqlLifecycleSource,\n SqlLifecycleRetainer,\n} from \"@yielded/agent-storage-sql/sql-lifecycle-publication\";\nimport { SqlStorageProgress } from \"@yielded/agent-storage-sql/sql-storage-progress\";\nimport {\n canonicalRecordJson,\n makeSqlThreadArchiveRange,\n} from \"@yielded/agent-storage-sql/sql-thread-archive-range\";\nimport { makeProgressAppendValidation } from \"@yielded/agent-storage-sql/sql-thread-native-reads\";\nimport { makeSqlThreadWork } from \"@yielded/agent-storage-sql/sql-thread-work\";\nimport { ThreadId } from \"@yielded/agent/identifiers\";\nimport {\n LifecyclePublicationFact,\n LifecyclePublicationError,\n} from \"@yielded/agent/lifecycle-publication\";\nimport { CanonicalRecord, CanonicalSequence } from \"@yielded/agent/records\";\nimport { SqlStorageOwner } from \"@yielded/agent/sql-memory-store\";\nimport {\n type AppendConflict,\n ThreadStoreDiagnostic,\n ThreadStoreError,\n} from \"@yielded/agent/thread-store\";\nimport { Cause, Clock, Effect, Option, Schema, Stream } from \"effect\";\nimport * as SqlClient from \"effect/sql/SqlClient\";\nimport { SqlError } from \"effect/sql/SqlError\";\n\nimport {\n type DoStorageFailpointError,\n DoAppendConflict,\n DoCheckpointConflict,\n DoStorageCorruptionError,\n DoStorageError,\n DoValueBoundExceeded,\n type DoStorageFailpointLocation,\n} from \"../DoStorageError.ts\";\nimport { ensureDoStorageLayout } from \"./migrations.ts\";\nimport { ownedState, ownedRows, type OwnedState } from \"./owned-state.ts\";\nimport {\n isAppendContention,\n annotateStorageError,\n storageResult,\n withStorageSpan,\n} from \"./storage-span.ts\";\n\n/**\n * Static schema ceiling for stored text columns. Writes are bounded in BYTES by the\n * configured `maxStoredValueBytes` (always ≤ 2,000,000); UTF-8 byte length is never smaller\n * than UTF-16 string length, so any value that passed the byte bound also passes this\n * decode-side character ceiling.\n */\nconst BoundedStoredText = Schema.String.check(Schema.isMaxLength(2_000_000));\nconst BoundedIdentifier = Schema.NonEmptyString.check(Schema.isMaxLength(1024));\nconst MAX_IDENTIFIER_LENGTH = 1_024;\nconst MAX_READ_PAGE_JSON_BYTES = 4 * 1024 * 1024;\n// A shared isolate budget permits multi-page hydration without multiplying its\n// retained payload by the number of resident Durable Object instances.\nconst MAX_RECORD_CACHE_JSON_BYTES = 8 * 1024 * 1024;\nconst MAX_RECORD_CACHE_ENTRIES = 4_096;\n/** Durable Object SQL storage allows at most 100 bound parameters per statement. */\nconst MAX_BOUND_PARAMETERS = 100;\nconst isSqlError = Schema.is(SqlError);\n\nconst storedTextBytes = (value: string): number => new TextEncoder().encode(value).byteLength;\n\nconst chunked = <A>(values: ReadonlyArray<A>, size: number): Array<ReadonlyArray<A>> => {\n const chunks: Array<ReadonlyArray<A>> = [];\n\n for (let index = 0; index < values.length; index += size) {\n chunks.push(values.slice(index, index + size));\n }\n\n return chunks;\n};\n\nclass ThreadRow extends Schema.Class<ThreadRow>(\"ThreadRow\")({\n thread_id: BoundedIdentifier,\n created_at: Schema.NullOr(Schema.NonEmptyString.check(Schema.isMaxLength(128))),\n tail_sequence: CanonicalSequence,\n}) {}\n\nconst threadRows = ownedRows(\n ThreadRow,\n \"effect_agent_threads\",\n (row) => row.thread_id,\n \"thread_id\",\n);\n\nclass RecordRow extends Schema.Class<RecordRow>(\"RecordRow\")({\n batch_id: Schema.fromJsonString(BoundedIdentifier),\n thread_id: BoundedIdentifier,\n record_id: Schema.fromJsonString(BoundedIdentifier),\n record_json: BoundedStoredText,\n sequence: CanonicalSequence,\n}) {}\n\ninterface RecordCacheEntry {\n readonly row: RecordRow;\n readonly bytes: number;\n readonly evict: () => void;\n}\n\n// Entries retain only their local row map, never a storage owner or incarnation.\nconst isolateRecords = new Map<RecordCacheEntry, true>();\nlet isolateRecordBytes = 0;\n\nconst releaseRecord = (entry: RecordCacheEntry) => {\n if (!isolateRecords.delete(entry)) return;\n isolateRecordBytes -= entry.bytes;\n entry.evict();\n};\n\nconst recordCaches = new WeakMap<OwnedState, ReturnType<typeof makeRecordCache>>();\n\nconst makeRecordCache = () => {\n const records = new Map<string, RecordCacheEntry>();\n const key = (thread: string, sequence: number) => JSON.stringify([thread, sequence]);\n\n return {\n clear: () => {\n for (const entry of records.values()) releaseRecord(entry);\n },\n get: (thread: string, sequence: number) => records.get(key(thread, sequence))?.row,\n prefix: (thread: string, through: number): ReadonlyArray<RecordRow> | undefined => {\n const prefix: Array<RecordRow> = [];\n\n for (let sequence = 1; sequence <= through; sequence++) {\n const row = records.get(key(thread, sequence));\n\n if (row === undefined) return undefined;\n prefix.push(row.row);\n }\n\n return prefix;\n },\n put: (row: RecordRow, capturedBytes?: number) => {\n const id = key(row.thread_id, row.sequence);\n const prior = records.get(id);\n\n if (prior?.row === row) return;\n const size = capturedBytes ?? storedTextBytes(row.record_json);\n\n if (prior !== undefined) releaseRecord(prior);\n\n const entry: RecordCacheEntry = {\n row,\n bytes: size,\n evict: () => records.delete(id),\n };\n\n records.set(id, entry);\n isolateRecords.set(entry, true);\n isolateRecordBytes += size;\n while (\n isolateRecordBytes > MAX_RECORD_CACHE_JSON_BYTES ||\n isolateRecords.size > MAX_RECORD_CACHE_ENTRIES\n ) {\n const oldest = isolateRecords.keys().next().value;\n\n if (oldest === undefined) break;\n releaseRecord(oldest);\n }\n },\n };\n};\n\nconst ReadPlanRow = Schema.Struct({\n sequence: CanonicalSequence,\n record_json_bytes: Schema.Natural.check(Schema.isLessThanOrEqualTo(MAX_READ_PAGE_JSON_BYTES)),\n});\n\ntype ReadPage = [typeof ReadPlanRow.Type, ...Array<typeof ReadPlanRow.Type>];\n\nclass CheckpointRow extends Schema.Class<CheckpointRow>(\"CheckpointRow\")({\n checkpoint_json: BoundedStoredText,\n thread_id: BoundedIdentifier,\n through_sequence: CanonicalSequence,\n}) {}\n\nconst LifecycleCursorRow = Schema.Struct({\n thread_id: BoundedIdentifier,\n through_sequence: CanonicalSequence,\n});\n\nconst lifecycleCursorRows = ownedRows(\n LifecycleCursorRow,\n \"effect_agent_lifecycle_cursors\",\n (row) => row.thread_id,\n \"thread_id\",\n);\n\nconst initializedLifecycleSources = new WeakSet<OwnedState>();\n\nexport type { RawAppendRequest } from \"@yielded/agent-storage-sql/sql-journal\";\n\nexport class RawAppendResult extends Schema.Class<RawAppendResult>(\n \"@effect-agent/storage-cloudflare/RawAppendResult\",\n)({\n firstSequence: CanonicalSequence,\n lastSequence: CanonicalSequence,\n replayed: Schema.Boolean,\n}) {}\n\nexport class RawReadRequest extends Schema.Class<RawReadRequest>(\n \"@effect-agent/storage-cloudflare/RawReadRequest\",\n)({\n threadId: BoundedIdentifier,\n fromSequenceExclusive: CanonicalSequence,\n limit: Schema.Int.check(Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(1_024)),\n}) {}\n\nexport class RawCheckpoint extends Schema.Class<RawCheckpoint>(\n \"@effect-agent/storage-cloudflare/RawCheckpoint\",\n)({\n checkpointJson: BoundedStoredText,\n threadId: BoundedIdentifier,\n throughSequence: CanonicalSequence,\n}) {}\n\ntype AppendError =\n | ThreadStoreError\n | DoAppendConflict\n | DoStorageCorruptionError\n | DoStorageError\n | DoStorageFailpointError\n | DoValueBoundExceeded;\n\ntype CheckpointError =\n | DoCheckpointConflict\n | DoStorageCorruptionError\n | DoStorageError\n | DoValueBoundExceeded;\n\ntype DoJournalFailpoint = (\n location: DoStorageFailpointLocation,\n) => Effect.Effect<void, DoStorageFailpointError>;\n\nconst noFailpoint: DoJournalFailpoint = () => Effect.void;\n\nconst storageError =\n (operation: string) =>\n (error: SqlError): DoStorageError =>\n DoStorageError.make({\n cause: error,\n operation,\n message: error.message,\n diagnostic: ThreadStoreDiagnostic.make({\n causeTag: error._tag,\n operation,\n issueTag: error.reason._tag,\n }),\n });\n\n/** Decode raw Durable Object SQLite rows against a Schema, reporting failures as typed corruption. */\nexport const decodeRows = <A, I>(\n schema: Schema.Codec<ReadonlyArray<A>, ReadonlyArray<I>>,\n table: string,\n rowKey: string,\n rows: unknown,\n decoder: string = table,\n): Effect.Effect<ReadonlyArray<A>, DoStorageCorruptionError> =>\n Schema.decodeUnknownEffect(schema)(rows).pipe(\n Effect.mapError((error) =>\n DoStorageCorruptionError.make({\n table,\n rowKey,\n message: \"Stored rows do not satisfy the storage schema\",\n diagnostic: ThreadStoreDiagnostic.make({\n causeTag: error._tag,\n operation: \"decode storage rows\",\n decoder,\n issueTag: error.issue._tag,\n }),\n }),\n ),\n Effect.tapError((error) =>\n Effect.annotateCurrentSpan({\n \"storage.failure.operation\": error.diagnostic?.operation,\n \"storage.failure.cause\": error.diagnostic?.causeTag,\n \"storage.failure.decoder\": decoder,\n \"storage.failure.issue\": error.diagnostic?.issueTag,\n }),\n ),\n );\n\n/** Decode exactly one raw row against a Schema, reporting failures as typed corruption. */\nexport const decodeSingleRow = <A, I>(\n schema: Schema.Codec<ReadonlyArray<A>, ReadonlyArray<I>>,\n table: string,\n rowKey: string,\n rows: unknown,\n): Effect.Effect<A, DoStorageCorruptionError> =>\n decodeRows(schema, table, rowKey, rows).pipe(\n Effect.flatMap((decoded) =>\n decoded.length === 1\n ? Effect.succeed(decoded[0])\n : Effect.fail(\n DoStorageCorruptionError.make({\n table,\n rowKey,\n message: `Expected exactly one row but found ${decoded.length}.`,\n }),\n ),\n ),\n );\n\n/** Open a validated layout and bind this Object's storage owner. */\nconst ensureCurrentStorage = Effect.fnUntraced(function* (\n sql: SqlClient.SqlClient,\n failpoint: DoJournalFailpoint = noFailpoint,\n maxStoredValueBytes: number,\n) {\n yield* ensureDoStorageLayout().pipe(Effect.provideService(SqlClient.SqlClient, sql));\n\n const state = yield* ownedState(sql);\n const owner = (yield* SqlStorageOwner) ?? state;\n let journal: DoJournal | undefined;\n\n const lifecycle = yield* makeSqlLifecyclePublication(undefined, maxStoredValueBytes).pipe(\n Effect.provideService(SqlStorageOwner, owner),\n Effect.provideService(SqlLifecycleSource, {\n beforeRetain: (threadId) =>\n Effect.suspend(() =>\n journal === undefined\n ? Effect.fail(LifecyclePublicationError.make({ reason: \"unavailable\" }))\n : journal.flushCanonical(threadId),\n ),\n beforePending: Effect.suspend(() =>\n journal === undefined\n ? Effect.fail(LifecyclePublicationError.make({ reason: \"unavailable\" }))\n : journal.flushPublications(),\n ),\n }),\n Effect.provideService(SqlClient.SqlClient, sql),\n Effect.mapError((cause) =>\n DoStorageError.make({\n operation: \"initialize lifecycle publication\",\n message: \"Native publication storage unavailable\",\n cause,\n }),\n ),\n );\n\n journal = yield* makeJournal(sql, failpoint, maxStoredValueBytes, lifecycle, state).pipe(\n Effect.provideService(SqlStorageOwner, owner),\n );\n if (lifecycle !== undefined) yield* journal.initializeLifecycleSource();\n\n return journal;\n});\n\nconst makeJournal = (\n sql: SqlClient.SqlClient,\n failpoint: DoJournalFailpoint,\n maxStoredValueBytes: number,\n lifecycle: Effect.Success<ReturnType<typeof makeSqlLifecyclePublication>>,\n state: OwnedState,\n) =>\n Effect.gen(function* () {\n const owner = (yield* SqlStorageOwner) ?? state;\n const progress = yield* SqlStorageProgress;\n\n const work = yield* makeSqlThreadWork({\n read: (body) => state.read(body),\n write: (body) =>\n owner.transaction(body.pipe(Effect.tap(() => state.invalidate))).pipe(\n Effect.provideService(SqlClient.SqlClient, sql),\n Effect.catchTag(\"SqlError\", (cause) =>\n ThreadStoreError.make({\n operation: \"rebuild Thread work\",\n message: \"Thread work transaction failed\",\n cause,\n }),\n ),\n ),\n }).pipe(Effect.provideService(SqlClient.SqlClient, sql));\n\n yield* Effect.acquireRelease(\n Effect.sync(() => state.invalidators.add(work.invalidate)),\n () => Effect.sync(() => state.invalidators.delete(work.invalidate)),\n );\n\n const workFailure = (cause: unknown) =>\n DoStorageError.make({\n operation: \"publish Thread work\",\n message: \"Canonical work metadata could not be published\",\n cause,\n });\n\n const validateProgress = yield* makeProgressAppendValidation().pipe(\n Effect.provideService(SqlClient.SqlClient, sql),\n );\n\n const executions = yield* makeSqlExecutions().pipe(\n Effect.provideService(SqlClient.SqlClient, sql),\n );\n\n const executionConflict = <A>(effect: Effect.Effect<A, AppendConflict | ThreadStoreError>) =>\n effect.pipe(\n Effect.catchTag(\"AppendConflict\", (error) =>\n Effect.fail(\n DoAppendConflict.make({\n reason: error.reason,\n message: \"Active execution conflicts with the captured commit\",\n ...(error.actualTailSequence === undefined\n ? {}\n : { actualTailSequence: error.actualTailSequence }),\n }),\n ),\n ),\n );\n\n const threads = threadRows(state, sql);\n const cursors = lifecycleCursorRows(state, sql);\n let records = recordCaches.get(state);\n\n if (records === undefined) {\n records = makeRecordCache();\n recordCaches.set(state, records);\n state.invalidators.add(records.clear);\n }\n const recordCache = records;\n\n const archives = yield* makeSqlThreadArchiveRange({\n read: (body) => state.read(body),\n write: (body) =>\n owner.transaction(body.pipe(Effect.tap(() => state.invalidate))).pipe(\n Effect.provideService(SqlClient.SqlClient, sql),\n Effect.catchTag(\"SqlError\", (cause) =>\n ThreadStoreError.make({\n operation: \"archive write\",\n message: \"Archive transaction failed.\",\n cause,\n }),\n ),\n ),\n }).pipe(Effect.provideService(SqlClient.SqlClient, sql));\n\n const recordJson = canonicalRecordJson(sql);\n\n const physical = yield* makeCanonicalStorage().pipe(\n Effect.provideService(SqlClient.SqlClient, sql),\n );\n\n /** Typed pre-write refusal for any single value over the configured byte bound. */\n const checkValueBound = (\n operation: string,\n value: string,\n actualBytes = storedTextBytes(value),\n ): Effect.Effect<void, DoValueBoundExceeded> => {\n return actualBytes > maxStoredValueBytes\n ? Effect.fail(\n DoValueBoundExceeded.make({\n actualBytes,\n maxBytes: maxStoredValueBytes,\n operation,\n }),\n )\n : Effect.void;\n };\n\n /**\n * Runs one journal write transaction on the Durable Object storage-backed\n * `withTransaction` (`ctx.storage.transaction()` under the hood). Within one Durable\n * Object there is exactly ONE writer, so the Node `BEGIN IMMEDIATE` + busy-retry +\n * `SqliteWriteContention` machinery has no analogue here and is deliberately absent.\n * The shared local owner gate covers the transaction and acknowledgement.\n *\n * Journal write transactions are always top level: the Durable Object client rejects\n * nested transactions, so new journal operations must not wrap this helper inside another\n * transaction. Expected outcomes may be captured only when every such refusal precedes\n * mutation; real faults still roll back. Successful preflight reads never authorize a write.\n */\n const withWriteTransaction =\n (operation: string, expected: (error: { readonly _tag: string }) => boolean = () => false) =>\n <A, E extends { readonly _tag: string }>(\n effect: Effect.Effect<A, E>,\n ): Effect.Effect<A, E | DoStorageError> =>\n state.owner.withMutation(owner.transaction(storageResult(effect, expected))).pipe(\n Effect.provideService(SqlClient.SqlClient, sql),\n Effect.mapError((error) => (isSqlError(error) ? storageError(operation)(error) : error)),\n Effect.tapError(annotateStorageError),\n Effect.withSpan(\"DoJournal.withWriteTransaction\", { attributes: { operation } }),\n Effect.flatMap(Effect.fromResult),\n );\n\n const materialize = Effect.fnUntraced(function* (\n threadId: string,\n createdAt: string,\n ): Effect.fn.Return<void, DoStorageCorruptionError | DoStorageError | DoValueBoundExceeded> {\n if (threadId.length > MAX_IDENTIFIER_LENGTH) {\n return yield* DoStorageError.make({\n operation: \"materialize thread\",\n message: \"Thread identity exceeds the Durable Object storage bounds.\",\n });\n }\n // Refusals and idempotent materialization need no transaction. Recheck before writes.\n const current = yield* getThread(threadId);\n\n if (current.length === 1) {\n return;\n }\n yield* withWriteTransaction(\"materialize transaction\")(\n Effect.gen(function* () {\n const existingRows = yield* getThread(threadId);\n\n const existing = yield* decodeRows(\n Schema.Array(ThreadRow),\n \"effect_agent_threads\",\n threadId,\n existingRows,\n );\n\n if (existing.length > 1) {\n return yield* DoStorageCorruptionError.make({\n table: \"effect_agent_threads\",\n rowKey: threadId,\n message: \"A thread primary key returned more than one row.\",\n });\n }\n if (existing.length === 0) {\n yield* sql`\n INSERT INTO effect_agent_threads (\n thread_id,\n created_at,\n tail_sequence\n ) VALUES (\n ${threadId},\n ${createdAt},\n 0\n ) ON CONFLICT(thread_id) DO UPDATE SET created_at=excluded.created_at\n WHERE effect_agent_threads.created_at IS NULL\n RETURNING *`.pipe(\n threads.write,\n Effect.mapError((error) =>\n error._tag === \"SqlError\" ? storageError(\"materialize thread\")(error) : error,\n ),\n );\n\n if (lifecycle !== undefined)\n yield* sql`INSERT INTO effect_agent_lifecycle_cursors (thread_id, through_sequence)\n VALUES (${threadId}, 0) RETURNING *`.pipe(\n cursors.write,\n Effect.mapError((error) =>\n error._tag === \"SqlError\"\n ? storageError(\"initialize lifecycle source\")(error)\n : error,\n ),\n );\n\n return;\n }\n }),\n );\n }, withStorageSpan(\"DoJournal.materialize\"));\n\n const getThread = Effect.fnUntraced(function* (threadId: string) {\n return (yield* threads\n .by(\"thread_id\", threadId)\n .pipe(\n Effect.mapError((error) =>\n error._tag === \"SqlError\" ? storageError(\"read thread\")(error) : error,\n ),\n )).filter((row) => row.thread_id === threadId && row.created_at !== null);\n });\n\n const hasRecord = Effect.fnUntraced(function* (threadId: string, recordId: string) {\n const thread = (yield* getThread(threadId))[0];\n const prefix = recordCache.prefix(threadId, thread?.tail_sequence ?? 0);\n\n if (prefix !== undefined) return prefix.some((row) => row.record_id === recordId);\n\n const rows = yield* sql`SELECT record_id FROM effect_agent_canonical_records\n WHERE thread_id = ${threadId} AND record_id = ${JSON.stringify(recordId)}`.pipe(\n Effect.mapError(storageError(\"read canonical record identity\")),\n );\n\n return rows.length > 0;\n });\n\n const prepareAppend = Effect.fnUntraced(function* (request: RawAppendRequest) {\n if (\n request.threadId.length > MAX_IDENTIFIER_LENGTH ||\n request.batchId.length > MAX_IDENTIFIER_LENGTH ||\n request.executionWrites.some(\n ({ update }) => update.checkpoint.progress.runId.length > MAX_IDENTIFIER_LENGTH,\n ) ||\n request.records.some((record) => record.recordId.length > MAX_IDENTIFIER_LENGTH)\n ) {\n return yield* DoStorageError.make({\n operation: \"append canonical batch\",\n message: \"Canonical identifiers exceed the Durable Object storage bounds.\",\n });\n }\n // Archives store the full batch as one value, so their ceiling also bounds hot appends.\n // The trusted append SPI pairs captured JSON with its exact UTF-8 width.\n yield* checkValueBound(\"append canonical batch\", request.batchJson, request.batchBytes);\n for (const record of request.records) {\n if (record.recordBytes > maxStoredValueBytes)\n return yield* DoValueBoundExceeded.make({\n actualBytes: record.recordBytes,\n maxBytes: maxStoredValueBytes,\n operation: \"append canonical record\",\n });\n }\n for (const write of request.executionWrites)\n yield* checkValueBound(\"commit active execution\", write.json, write.bytes);\n\n const recordIds: Array<string> = request.records.map((record) => record.recordId);\n\n if (new Set(recordIds).size !== recordIds.length) {\n return yield* DoAppendConflict.make({\n message: `Batch ${request.batchId} contains duplicate canonical record IDs.`,\n reason: \"record-identity\",\n });\n }\n\n return request;\n });\n\n const appendPrepared = Effect.fnUntraced(function* (\n request: RawAppendRequest,\n ): Effect.fn.Return<RawAppendResult, AppendError> {\n const recordIds: Array<string> = request.records.map((record) => record.recordId);\n const threadRows = yield* getThread(request.threadId);\n\n const thread = threadRows.length === 1 ? threadRows[0] : undefined;\n\n if (thread === undefined)\n return yield* DoStorageCorruptionError.make({\n table: \"effect_agent_threads\",\n rowKey: request.threadId,\n message: `Expected exactly one row but found ${threadRows.length}.`,\n });\n\n const prefix = recordCache.prefix(request.threadId, thread.tail_sequence);\n\n const replay = yield* executionConflict(executions.replay(request));\n\n if (replay !== undefined) return RawAppendResult.make(replay);\n\n const existing =\n prefix !== undefined && !prefix.some((row) => row.batch_id === request.batchId)\n ? undefined\n : yield* archives.replay(request.threadId, request.batchId);\n\n if (existing !== undefined) {\n if (existing.batch_json !== request.batchJson) {\n return yield* DoAppendConflict.make({\n message: `Batch ${request.batchId} already exists with different canonical content.`,\n reason: \"batch-content\",\n });\n }\n\n return RawAppendResult.make({\n firstSequence: existing.first_sequence,\n lastSequence: existing.last_sequence,\n replayed: true,\n });\n }\n\n yield* executionConflict(executions.rejectSuperseded(request));\n\n if (request.expectedTailSequence !== thread.tail_sequence) {\n return yield* DoAppendConflict.make({\n message:\n `Expected tail ${request.expectedTailSequence} ` + `but found ${thread.tail_sequence}.`,\n reason: \"tail\",\n actualTailSequence: thread.tail_sequence,\n });\n }\n\n // Chunked to respect the Durable Object platform's 100-bound-parameter statement\n // limit: a batch may carry up to 256 records.\n const existingRecords: Array<RecordRow> =\n prefix === undefined ? [] : prefix.filter((row) => recordIds.includes(row.record_id));\n\n for (const chunk of prefix === undefined\n ? chunked(recordIds, MAX_BOUND_PARAMETERS - 10)\n : []) {\n const existingRecordRows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n sequence,\n record_id,\n ${recordBatchId(sql)} AS batch_id,\n ${recordJson} AS record_json\n FROM effect_agent_canonical_records\n WHERE thread_id = ${request.threadId}\n AND record_id IN ${sql.in(chunk.map((id) => JSON.stringify(id)))}\n `.pipe(Effect.mapError(storageError(\"check canonical record identities\")));\n\n existingRecords.push(\n ...(yield* decodeRows(\n Schema.Array(RecordRow),\n \"effect_agent_canonical_records\",\n `${request.threadId}/record_ids`,\n existingRecordRows,\n )),\n );\n }\n if (existingRecords.length > 0) {\n return yield* DoAppendConflict.make({\n message: `Canonical record ID ${existingRecords[0].record_id} already exists.`,\n reason: \"record-identity\",\n });\n }\n\n const firstSequence = yield* Schema.decodeEffect(CanonicalSequence)(\n thread.tail_sequence + (request.records.length === 0 ? 0 : 1),\n ).pipe(\n Effect.mapError((error) =>\n DoStorageError.make({\n cause: error,\n operation: \"append canonical batch\",\n message: error.message,\n }),\n ),\n );\n\n const lastSequence = yield* Schema.decodeEffect(CanonicalSequence)(\n thread.tail_sequence + request.records.length,\n ).pipe(\n Effect.mapError((error) =>\n DoStorageError.make({\n cause: error,\n operation: \"append canonical batch\",\n message: error.message,\n }),\n ),\n );\n\n yield* executionConflict(executions.validate(request));\n yield* validateProgress(request);\n\n const result = RawAppendResult.make({\n firstSequence,\n lastSequence,\n replayed: false,\n });\n\n if (request.records.length > 0) {\n yield* sql`\n INSERT INTO effect_agent_canonical_batches (\n thread_id,\n batch_id,\n first_sequence,\n last_sequence,\n producer_id_json\n ) VALUES (\n ${request.threadId},\n ${JSON.stringify(request.batchId)},\n ${firstSequence},\n ${lastSequence},\n ${request.producerIdJson}\n )\n `.pipe(Effect.mapError(storageError(\"insert canonical batch\")));\n }\n yield* failpoint(\"append:after-batch-insert\");\n\n const physicalRecords = yield* physical.records(\n request.threadId,\n firstSequence,\n request.records,\n );\n\n const records = physicalRecords.map(({ columns, memberships }, index) => ({\n record: request.records[index]!,\n canonical: request.records[index]!.canonical,\n row: columns,\n memberships,\n }));\n\n // Captured scalar locators avoid parsing canonical JSON under the writer.\n // Eighteen bound columns per row stay within workerd's statement limit.\n for (const group of chunked(records, Math.floor(MAX_BOUND_PARAMETERS / 18))) {\n yield* sql`INSERT INTO effect_agent_canonical_records ${sql.insert(group.map(({ row }) => row))}`.pipe(\n Effect.mapError(storageError(\"insert canonical records\")),\n );\n\n const memberships = group.flatMap(({ memberships }) => memberships);\n\n for (const members of chunked(memberships, Math.floor(MAX_BOUND_PARAMETERS / 3)))\n yield* sql`INSERT INTO effect_agent_record_runs ${sql.insert(members)}`.pipe(\n Effect.mapError(storageError(\"index canonical Run membership\")),\n );\n for (const { row, record } of group) {\n recordCache.put(\n RecordRow.make({\n ...row,\n record_id: record.recordId,\n batch_id: request.batchId,\n record_json: record.recordJson,\n sequence: Schema.decodeSync(CanonicalSequence)(row.sequence),\n }),\n record.recordBytes,\n );\n yield* failpoint(\"append:after-record-insert\");\n }\n }\n\n if (request.records.length > 0) {\n const workFields = yield* work\n .apply(\n yield* Schema.decodeEffect(ThreadId)(request.threadId).pipe(\n Effect.mapError(workFailure),\n ),\n firstSequence,\n request.records.map((record) => record.canonical),\n )\n .pipe(Effect.mapError(workFailure));\n\n yield* sql`UPDATE effect_agent_threads SET ${sql.update({ tail_sequence: lastSequence, ...workFields })}\n WHERE thread_id=${request.threadId} RETURNING *`.pipe(\n threads.write,\n Effect.mapError((error) =>\n error._tag === \"SqlError\" ? storageError(\"advance thread tail\")(error) : error,\n ),\n );\n }\n yield* executionConflict(executions.commit(request, result));\n if (request.executionWrites.length > 0) yield* failpoint(\"append:after-record-insert\");\n if (\n lifecycle !== undefined &&\n request.executionWrites.some(({ update }) =>\n update.facts.some(({ payload }) => payload._tag === \"RunStarted\"),\n )\n )\n yield* flushCanonical(request.threadId).pipe(\n Effect.provideService(SqlLifecycleRetainer, { retainMany: lifecycle.retainMany }),\n Effect.mapError((cause) =>\n DoStorageError.make({\n operation: \"retain lifecycle start prefix\",\n message: \"Lifecycle start intent could not be retained\",\n cause,\n }),\n ),\n );\n for (const { update } of request.executionWrites) {\n for (const fact of update.facts) {\n if (\n lifecycle !== undefined &&\n Schema.is(Schema.toType(LifecyclePublicationFact))(fact.payload)\n )\n yield* lifecycle\n .retain({\n id: JSON.stringify([request.threadId, \"record\", fact.recordId]),\n ownerThreadId: request.threadId,\n canonicalSequence: lastSequence,\n createdAt: fact.createdAt,\n fact: fact.payload,\n })\n .pipe(\n Effect.mapError((cause) =>\n DoStorageError.make({\n operation: \"retain operational lifecycle\",\n message: cause.message,\n cause,\n }),\n ),\n );\n }\n }\n yield* failpoint(\"append:after-tail-update\");\n\n // Retain one start prefix atomically with its canonical proof, before model/tool\n // execution. Later facts stay journal-backed until the settlement publication wave.\n if (\n lifecycle !== undefined &&\n records.some(\n ({ canonical: { payload } }) =>\n payload._tag === \"RunStarted\" || payload._tag === \"SubagentStarted\",\n )\n )\n yield* flushCanonical(request.threadId).pipe(\n Effect.provideService(SqlLifecycleRetainer, { retainMany: lifecycle.retainMany }),\n Effect.mapError((cause) =>\n DoStorageError.make({\n operation: \"retain lifecycle start prefix\",\n message: \"Lifecycle start intent could not be retained\",\n cause,\n }),\n ),\n );\n\n yield* progress.committed(\"canonical\").pipe(\n Effect.catchCause((cause) =>\n Effect.failCause(\n Cause.map(cause, (error) =>\n DoStorageError.make({\n operation: \"enroll canonical progress\",\n message: error.message,\n cause: error,\n }),\n ),\n ),\n ),\n );\n\n return result;\n });\n\n const append = Effect.fnUntraced(\n function* (\n request: RawAppendRequest,\n observed: ThreadRow,\n ): Effect.fn.Return<RawAppendResult, AppendError> {\n yield* prepareAppend(request);\n // The facade already read this tail. Reject a known stale writer without opening a\n // transaction; a matching tail is still checked atomically below. Replays precede tail\n // conflicts, preserving retry identity even after the log has advanced.\n if (request.records.length > 0 && request.expectedTailSequence !== observed.tail_sequence) {\n const batches = yield* sql`\n SELECT batch_id FROM effect_agent_canonical_batches\n WHERE thread_id = ${request.threadId} AND batch_id = ${JSON.stringify(request.batchId)}\n UNION ALL SELECT last_commit_id FROM effect_agent_executions\n WHERE thread_id = ${request.threadId} AND last_commit_id = ${request.batchId}\n LIMIT 1\n `.pipe(Effect.mapError(storageError(\"preflight append replay\")));\n\n // A possible replay still checks exact content in the transaction.\n if (batches.length === 0) {\n return yield* DoAppendConflict.make({\n reason: \"tail\",\n message: \"The canonical tail has advanced.\",\n actualTailSequence: observed.tail_sequence,\n });\n }\n }\n\n return yield* withWriteTransaction(\n \"append transaction\",\n isAppendContention,\n )(appendPrepared(request));\n },\n withStorageSpan(\"DoJournal.append\", isAppendContention),\n );\n\n const read = Effect.fnUntraced(function* (request: RawReadRequest) {\n const thread = (yield* getThread(request.threadId))[0];\n\n const through = Math.min(\n thread?.tail_sequence ?? 0,\n request.fromSequenceExclusive + request.limit,\n );\n\n const cached: Array<RecordRow> = [];\n\n for (let sequence = request.fromSequenceExclusive + 1; sequence <= through; sequence++) {\n const record = recordCache.get(request.threadId, sequence);\n\n if (record === undefined) break;\n cached.push(record);\n }\n if (cached.length === Math.max(0, through - request.fromSequenceExclusive)) {\n return { count: cached.length, records: Stream.fromIterable(cached) };\n }\n\n // Capture membership without retaining payloads. Append-only sequences keep each later\n // payload query inside this snapshot, even when new records arrive during consumption.\n const planRows = yield* sql<Record<string, unknown>>`\n SELECT\n sequence,\n length(CAST(${recordJson} AS BLOB)) AS record_json_bytes\n FROM effect_agent_canonical_records\n WHERE thread_id = ${request.threadId}\n AND sequence > ${request.fromSequenceExclusive}\n ORDER BY sequence\n LIMIT ${request.limit}\n `.pipe(Effect.mapError(storageError(\"read canonical records\")));\n\n const plan = yield* decodeRows(\n Schema.Array(ReadPlanRow),\n \"effect_agent_canonical_records\",\n `${request.threadId}>${request.fromSequenceExclusive}`,\n planRows,\n \"ReadPlanRow\",\n );\n\n const mismatch = () =>\n DoStorageCorruptionError.make({\n table: \"effect_agent_canonical_records\",\n rowKey: `${request.threadId}>${request.fromSequenceExclusive}`,\n message:\n \"Canonical read membership, sequence, or payload size changed from its read plan.\",\n });\n\n if (plan.length > request.limit) return yield* mismatch();\n\n const pages: Array<ReadPage> = [];\n let page: ReadPage | undefined;\n let pageBytes = 0;\n let previousSequence = request.fromSequenceExclusive;\n\n for (const row of plan) {\n if (row.sequence !== previousSequence + 1) return yield* mismatch();\n previousSequence = row.sequence;\n if (page === undefined || pageBytes + row.record_json_bytes > MAX_READ_PAGE_JSON_BYTES) {\n page = [row];\n pages.push(page);\n pageBytes = row.record_json_bytes;\n } else {\n page.push(row);\n pageBytes += row.record_json_bytes;\n }\n }\n\n const readPage = Effect.fnUntraced(function* (page: ReadPage) {\n const rows = yield* sql<Record<string, unknown>>`\n SELECT thread_id, sequence, record_id, ${recordBatchId(sql)} AS batch_id, ${recordJson} AS record_json\n FROM effect_agent_canonical_records\n WHERE thread_id = ${request.threadId}\n AND sequence >= ${page[0].sequence}\n AND sequence <= ${page[page.length - 1].sequence}\n ORDER BY sequence\n `.pipe(Effect.mapError(storageError(\"read canonical records\")));\n\n const decoded = yield* decodeRows(\n Schema.Array(RecordRow),\n \"effect_agent_canonical_records\",\n `${request.threadId}/${page[0].sequence}`,\n rows,\n \"RecordRow\",\n );\n\n if (\n decoded.length !== page.length ||\n decoded.some(\n (row, index) =>\n row.thread_id !== request.threadId ||\n row.sequence !== page[index].sequence ||\n storedTextBytes(row.record_json) !== page[index].record_json_bytes,\n )\n ) {\n return yield* mismatch();\n }\n\n for (const row of decoded) recordCache.put(row);\n\n return decoded;\n });\n\n return {\n count: plan.length,\n records: Stream.fromIterable(pages).pipe(\n Stream.flatMap((page) => Stream.fromIterableEffect(state.read(readPage(page)))),\n ),\n };\n });\n\n const saveCheckpoint = Effect.fnUntraced(function* (\n checkpoint: RawCheckpoint,\n ): Effect.fn.Return<void, CheckpointError> {\n if (checkpoint.threadId.length > MAX_IDENTIFIER_LENGTH) {\n return yield* DoStorageError.make({\n operation: \"save checkpoint\",\n message: \"Checkpoint identity exceeds the Durable Object storage bounds.\",\n });\n }\n yield* checkValueBound(\"save checkpoint\", checkpoint.checkpointJson);\n yield* withWriteTransaction(\"checkpoint transaction\")(\n Effect.gen(function* () {\n const threadRows = yield* getThread(checkpoint.threadId);\n\n const thread = yield* decodeSingleRow(\n Schema.Array(ThreadRow),\n \"effect_agent_threads\",\n checkpoint.threadId,\n threadRows,\n );\n\n if (checkpoint.throughSequence > thread.tail_sequence) {\n return yield* DoCheckpointConflict.make({\n reason: \"ahead-of-tail\",\n message:\n `Checkpoint sequence ${checkpoint.throughSequence} is after canonical tail ` +\n `${thread.tail_sequence}.`,\n });\n }\n\n if (!(yield* isBatchBoundary(checkpoint.threadId, checkpoint.throughSequence))) {\n return yield* DoCheckpointConflict.make({\n reason: \"not-batch-boundary\",\n message: \"Checkpoint sequence is not the end of a complete canonical batch.\",\n });\n }\n\n const checkpointRows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n through_sequence,\n checkpoint_json\n FROM effect_agent_checkpoints\n WHERE thread_id = ${checkpoint.threadId}\n AND through_sequence = ${checkpoint.throughSequence}\n `.pipe(Effect.mapError(storageError(\"read idempotent checkpoint\")));\n\n const existing = yield* decodeRows(\n Schema.Array(CheckpointRow),\n \"effect_agent_checkpoints\",\n `${checkpoint.threadId}/${checkpoint.throughSequence}`,\n checkpointRows,\n );\n\n if (existing.length > 1) {\n return yield* DoStorageCorruptionError.make({\n table: \"effect_agent_checkpoints\",\n rowKey: `${checkpoint.threadId}/${checkpoint.throughSequence}`,\n message: \"A checkpoint primary key returned more than one row.\",\n });\n }\n if (existing.length === 1) {\n if (existing[0].checkpoint_json !== checkpoint.checkpointJson) {\n return yield* DoCheckpointConflict.make({\n reason: \"corrupt\",\n message: \"A different checkpoint already exists at this canonical sequence.\",\n });\n }\n\n return;\n }\n\n yield* sql`\n INSERT INTO effect_agent_checkpoints (\n thread_id,\n through_sequence,\n checkpoint_json\n ) VALUES (\n ${checkpoint.threadId},\n ${checkpoint.throughSequence},\n ${checkpoint.checkpointJson}\n )\n `.pipe(Effect.mapError(storageError(\"insert checkpoint\")));\n }),\n );\n });\n\n const loadCheckpoint = Effect.fnUntraced(function* (\n threadId: string,\n atOrBeforeSequence: CanonicalSequence,\n ) {\n const rows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n through_sequence,\n checkpoint_json\n FROM effect_agent_checkpoints\n WHERE thread_id = ${threadId}\n AND through_sequence <= ${atOrBeforeSequence}\n ORDER BY through_sequence DESC\n LIMIT 1\n `.pipe(Effect.mapError(storageError(\"load checkpoint\")));\n\n return yield* decodeRows(\n Schema.Array(CheckpointRow),\n \"effect_agent_checkpoints\",\n `${threadId}<=${atOrBeforeSequence}`,\n rows,\n );\n });\n\n const isBatchBoundary = Effect.fnUntraced(function* (\n threadId: string,\n sequence: CanonicalSequence,\n ) {\n if (sequence === 0) return true;\n\n const rows = yield* sql<Record<string, unknown>>`\n SELECT last_sequence FROM effect_agent_canonical_batches\n WHERE thread_id = ${threadId}\n AND batch_id = (\n SELECT ${recordBatchId(sql)} FROM effect_agent_canonical_records\n WHERE thread_id = ${threadId} AND sequence = ${sequence}\n )\n AND last_sequence = ${sequence}\n `.pipe(Effect.mapError(storageError(\"read canonical batch boundary\")));\n\n const batches = yield* decodeRows(\n Schema.Array(Schema.Struct({ last_sequence: CanonicalSequence })),\n \"effect_agent_canonical_batches\",\n `${threadId}/${sequence}`,\n rows,\n );\n\n return batches.length === 1;\n });\n\n const sourceFailure = (cause: unknown) =>\n LifecyclePublicationError.make({ reason: \"unavailable\", cause });\n\n const sourceThreads = Effect.fnUntraced(function* () {\n const all = yield* threads.matching(\n \"lifecycle-owners\",\n (row) => row.created_at !== null,\n sql`SELECT * FROM effect_agent_threads WHERE created_at IS NOT NULL LIMIT 129`,\n 128,\n );\n\n if (all.length <= 128) return all;\n\n // Large custom hosts hydrate only owners whose journal has an unmaterialized suffix.\n return yield* Schema.decodeUnknownEffect(Schema.Array(ThreadRow))(\n yield* sql`SELECT t.* FROM effect_agent_threads t LEFT JOIN effect_agent_lifecycle_cursors c ON c.thread_id=t.thread_id\n WHERE t.created_at IS NOT NULL AND (c.thread_id IS NULL OR t.tail_sequence > c.through_sequence) ORDER BY t.thread_id LIMIT 128`,\n );\n });\n\n const initializeLifecycleSource = Effect.fnUntraced(function* () {\n if (initializedLifecycleSources.has(state)) return;\n yield* state\n .transaction(\n Effect.gen(function* () {\n const existing =\n yield* sql`SELECT name FROM sqlite_master WHERE name='effect_agent_lifecycle_cursors'`;\n\n if (existing.length === 0) {\n yield* sql`CREATE TABLE effect_agent_lifecycle_cursors (\n thread_id TEXT PRIMARY KEY, through_sequence INTEGER NOT NULL\n )`;\n // Existing inline writers already retained their facts. Enabling publication on an\n // old store starts here too; it does not publish historical facts retroactively.\n }\n // New Threads created with publication disabled have no cursor. Never advance\n // existing cursors: they can owe intent from an interrupted Attempt.\n yield* sql`INSERT INTO effect_agent_lifecycle_cursors(thread_id, through_sequence)\n SELECT thread_id, tail_sequence FROM effect_agent_threads WHERE created_at IS NOT NULL\n ON CONFLICT(thread_id) DO NOTHING`;\n yield* sourceThreads();\n }),\n )\n .pipe(\n Effect.provideService(SqlClient.SqlClient, sql),\n Effect.mapError((cause) =>\n DoStorageError.make({\n operation: \"initialize lifecycle source\",\n message: \"Lifecycle source cursor unavailable\",\n cause,\n }),\n ),\n );\n initializedLifecycleSources.add(state);\n });\n\n /** The journal is durable intent; only the bounded publication wave materializes a write set. */\n const flushCanonical = Effect.fnUntraced(function* (threadId: string, maxRecords = Infinity) {\n const { retainMany } = yield* SqlLifecycleRetainer;\n const thread = (yield* getThread(threadId))[0];\n\n if (thread === undefined) return;\n const cursor = (yield* cursors.by(\"thread_id\", threadId))[0];\n\n if (cursor === undefined || cursor.through_sequence > thread.tail_sequence)\n return yield* LifecyclePublicationError.make({ reason: \"corrupt\" });\n let through = cursor.through_sequence;\n const bound = Math.min(thread.tail_sequence, through + maxRecords);\n\n while (through < bound) {\n const page = yield* read(\n RawReadRequest.make({\n threadId,\n fromSequenceExclusive: through,\n limit: Math.min(128, bound - through),\n }),\n );\n\n const facts: Array<Parameters<SqlLifecycleRetainMany>[0][number]> = [];\n let bytes = 0;\n\n yield* Stream.runForEach(page.records, (row) =>\n Effect.gen(function* () {\n const record = yield* Schema.decodeEffect(Schema.fromJsonString(CanonicalRecord))(\n row.record_json,\n );\n\n if (record.recordId !== row.record_id || row.sequence !== through + 1)\n return yield* LifecyclePublicationError.make({ reason: \"corrupt\" });\n if (Schema.is(Schema.toType(LifecyclePublicationFact))(record.payload)) {\n if (\n bytes + storedTextBytes(row.record_json) > MAX_READ_PAGE_JSON_BYTES &&\n facts.length > 0\n ) {\n yield* retainMany(facts);\n facts.length = 0;\n bytes = 0;\n }\n facts.push({\n id: JSON.stringify([threadId, \"record\", record.recordId]),\n ownerThreadId: yield* Schema.decodeEffect(ThreadId)(threadId),\n canonicalSequence: row.sequence,\n createdAt: record.createdAt,\n fact: record.payload,\n });\n bytes += storedTextBytes(row.record_json);\n }\n through = row.sequence;\n }),\n );\n if (page.count === 0) return yield* LifecyclePublicationError.make({ reason: \"corrupt\" });\n yield* retainMany(facts);\n yield* sql`UPDATE effect_agent_lifecycle_cursors SET through_sequence=${through} WHERE thread_id=${threadId} RETURNING *`.pipe(\n cursors.write,\n );\n }\n }, Effect.mapError(sourceFailure));\n\n const flushPublications = Effect.fnUntraced(function* () {\n let owners = 0;\n\n for (const thread of yield* sourceThreads()) {\n const cursor = (yield* cursors.by(\"thread_id\", thread.thread_id))[0];\n\n if (cursor !== undefined && cursor.through_sequence === thread.tail_sequence) continue;\n yield* flushCanonical(thread.thread_id, 1024);\n if (++owners === 4) break;\n }\n }, Effect.mapError(sourceFailure));\n\n const sourcePending = Effect.gen(function* () {\n for (const thread of yield* sourceThreads()) {\n const cursor = (yield* cursors.by(\"thread_id\", thread.thread_id))[0];\n\n if (cursor === undefined || cursor.through_sequence > thread.tail_sequence)\n return yield* LifecyclePublicationError.make({ reason: \"corrupt\" });\n if (cursor.through_sequence < thread.tail_sequence) return true;\n }\n\n return false;\n }).pipe(Effect.mapError(sourceFailure));\n\n const ownedLifecycle =\n lifecycle === undefined\n ? undefined\n : {\n ...lifecycle,\n storage: {\n ...lifecycle.storage,\n retainedPendingDeadline: lifecycle.storage.pendingDeadline,\n pendingDeadline: state.read(\n Effect.gen(function* () {\n const deadline = yield* lifecycle.storage.pendingDeadline;\n\n if (!(yield* sourcePending)) return deadline;\n const now = yield* Clock.currentTimeMillis;\n\n return Option.some(Option.isSome(deadline) ? Math.min(now, deadline.value) : now);\n }),\n ),\n },\n };\n\n return {\n archives: archives.storage,\n archiveRanges: archives,\n work,\n state,\n owner,\n threads,\n lifecycle: ownedLifecycle,\n initializeLifecycleSource,\n flushCanonical,\n flushPublications,\n append,\n prepareAppend,\n appendPrepared,\n checkCommitIntent: (request: RawAppendRequest) =>\n executionConflict(executions.checkCommitIntent(request)),\n readExecution: executions.read,\n checkValueBound,\n getThread,\n hasRecord,\n isBatchBoundary,\n loadCheckpoint,\n materialize,\n read: (request: RawReadRequest) => state.read(read(request)),\n saveCheckpoint,\n withWriteTransaction,\n } as const;\n });\n\nexport type DoJournal = Effect.Success<ReturnType<typeof makeJournal>>;\n\nexport const initializeDoJournal = ensureCurrentStorage;\n"],"mappings":"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"}