@effect-agent/storage-sqlite 0.1.0-beta.39 → 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 +19 -2
- package/dist/index.mjs +713 -157
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
- package/src/index.ts +2 -0
- package/src/migrations.ts +9 -68
- package/src/sqlite-activity-store.ts +503 -0
- package/src/sqlite-memory-store.ts +532 -0
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { t as SqliteStorageFailpoint } from "./sqlite-storage-failpoint-DIwWk5dw.mjs";
|
|
2
|
-
import { AbortCommand, AbortIntent, AcceptedEvent, AdmissionAdmitted, AdmissionConflict, AdmissionNotAdmitted, AdmissionRequest, AdmissionResult, AppendConflict, AppendResult, ApprovalConflict, ApprovalDecision, ApprovalDecisionCommand, ApprovalDecisionIntent, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, CanonicalBatch, CanonicalRecord, CanonicalRecordEnvelope, CanonicalSequence, CheckpointRejected, ChildAttachmentSnapshot, ChildBudgetReservationRequest, ChildBudgetReservationSnapshot, ChildReservationConflict, ChildReservationStatus, ChildSettledNotification, Claim, ClaimJoiningRequest, ClaimRequest, DEFAULT_OWNERSHIP_LEASE_DURATION, DefinitionDigests, DeliveryChange, Digest, EMPTY_TAIL_DIGEST, FenceRejected, FencedAppendRequest, InputAppliedMarker, JoinSnapshot, JoinedToHost, JoiningClaim, LedgerCapabilities, LedgerError, LoadCheckpointRequest, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, ObservationOffset, OwnershipLost, OwnershipRenewal, OwnershipSnapshot, ParentLinkage, PersistedJson, ProducerEpoch, QueueSequence, RecordEnvelope, RecoverySnapshot, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReleaseOwnershipRequest, RenewOwnershipRequest, ReservedChildBudget, ReservedSettlement, RevertJoiningRequest, SaveCheckpointRequest, ScheduleCapacityError, ScheduleChange, ScheduleConflict, ScheduleDueCursor, ScheduleFailpoint, ScheduleId, ScheduleInstant, ScheduleKey, ScheduleNotFound, ScheduleOwner, SchedulePageRequest, ScheduleRecord, ScheduleStorageError, ScheduleStore, Settlement, SettlementConflict, SettlementFinalization, SettlementOutcome, SettlementReservation, SettlementReservationSnapshot, SourcePartition, SubmissionLedger, SubmissionLookup, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SubscriptionDelivery, SubscriptionDeliveryKey, SubscriptionError, SubscriptionFailpoint, SubscriptionKey, SubscriptionLimits, SubscriptionName, SubscriptionRecord, SubscriptionScanCursors, SubscriptionStore, SuspendRequest, SuspensionReason, SuspensionSnapshot, ThreadCheckpoint, ThreadExport, ThreadExportRequest, ThreadMaterialization, ThreadNotMaterialized, ThreadObservation, ThreadRead, ThreadStore, ThreadStoreError, ThreadTail, ThreadTailRequest, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, applyScheduleChange, applySubscriptionDeliveryChange, defaultSchedulingLimits, digestCanonicalBatch, sameAcceptedEventIdentity, sameSourcePartition, scheduleDeadline, scheduleUsesCapacity, settlementFailureFromRecord, submissionAbortRecordId, subscriptionCanSelect, subscriptionDeliveryCanSelect, subscriptionDeliveryKeyString } from "@effect-agent/thread";
|
|
2
|
+
import { AbortCommand, AbortIntent, AcceptedEvent, ActivityBusy, ActivityClaim, ActivityClaimRequest, ActivityMutationFailpoint, ActivityOwnershipLost, ActivityProcessorKey, ActivityProcessorStore, ActivityProgress, ActivityStoreError, ActivityWorkConflict, AdmissionAdmitted, AdmissionConflict, AdmissionNotAdmitted, AdmissionRequest, AdmissionResult, AppendConflict, AppendResult, ApprovalConflict, ApprovalDecision, ApprovalDecisionCommand, ApprovalDecisionIntent, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, CanonicalBatch, CanonicalRecord, CanonicalRecordEnvelope, CanonicalSequence, CheckpointRejected, ChildAttachmentSnapshot, ChildBudgetReservationRequest, ChildBudgetReservationSnapshot, ChildReservationConflict, ChildReservationStatus, ChildSettledNotification, Claim, ClaimJoiningRequest, ClaimRequest, DEFAULT_OWNERSHIP_LEASE_DURATION, DefinitionDigests, DeliveryChange, Digest, EMPTY_TAIL_DIGEST, FenceRejected, FencedAppendRequest, InputAppliedMarker, JoinSnapshot, JoinedToHost, JoiningClaim, LedgerCapabilities, LedgerError, LoadCheckpointRequest, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, ObservationOffset, OwnershipLost, OwnershipRenewal, OwnershipSnapshot, ParentLinkage, PersistedJson, PreparedActivity, ProducerEpoch, QueueSequence, RecordEnvelope, RecoverySnapshot, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReleaseOwnershipRequest, RenewOwnershipRequest, ReservedChildBudget, ReservedSettlement, RevertJoiningRequest, SaveCheckpointRequest, ScheduleCapacityError, ScheduleChange, ScheduleConflict, ScheduleDueCursor, ScheduleFailpoint, ScheduleId, ScheduleInstant, ScheduleKey, ScheduleNotFound, ScheduleOwner, SchedulePageRequest, ScheduleRecord, ScheduleStorageError, ScheduleStore, Settlement, SettlementConflict, SettlementFinalization, SettlementOutcome, SettlementReservation, SettlementReservationSnapshot, SourcePartition, SubmissionLedger, SubmissionLookup, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SubscriptionDelivery, SubscriptionDeliveryKey, SubscriptionError, SubscriptionFailpoint, SubscriptionKey, SubscriptionLimits, SubscriptionName, SubscriptionRecord, SubscriptionScanCursors, SubscriptionStore, SuspendRequest, SuspensionReason, SuspensionSnapshot, ThreadCheckpoint, ThreadExport, ThreadExportRequest, ThreadMaterialization, ThreadNotMaterialized, ThreadObservation, ThreadRead, ThreadStore, ThreadStoreError, ThreadTail, ThreadTailRequest, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, applyScheduleChange, applySubscriptionDeliveryChange, defaultSchedulingLimits, digestCanonicalBatch, sameAcceptedEventIdentity, sameSourcePartition, scheduleDeadline, scheduleUsesCapacity, settlementFailureFromRecord, submissionAbortRecordId, subscriptionCanSelect, subscriptionDeliveryCanSelect, subscriptionDeliveryKeyString } from "@effect-agent/thread";
|
|
3
3
|
import { Clock, Context, Crypto, DateTime, Duration, Effect, Exit, Layer, Option, Ref, Result, Schema, Stream } from "effect";
|
|
4
4
|
import { SqliteClient, SqliteMigrator } from "@effect/sql-sqlite-node";
|
|
5
5
|
import * as SqlClient from "effect/unstable/sql/SqlClient";
|
|
6
6
|
import { NodeCrypto } from "@effect/platform-node";
|
|
7
|
+
import { MemoryDocument, MemoryKey, MemoryMutationFailpoint, MemoryNamespaceAddress, MemoryOperationConflict, MemoryReader, MemoryStorageError, MemoryWrite, MemoryWriter, applyMemoryWrite } from "@effect-agent/core";
|
|
7
8
|
//#region src/errors.ts
|
|
8
9
|
/** The SQLite file uses a private-development format this adapter cannot read. */
|
|
9
10
|
var SqliteStorageCompatibilityError = class extends Schema.TaggedError()("SqliteStorageCompatibilityError", {
|
|
@@ -132,10 +133,10 @@ var SqliteStorageFailpointError = class extends Schema.TaggedError()("SqliteStor
|
|
|
132
133
|
//#endregion
|
|
133
134
|
//#region src/migrations.ts
|
|
134
135
|
const CurrentSqliteStorageVersion = 7;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
/** Initialize empty storage with the complete current schema. */
|
|
137
|
+
const sqliteMigrations = SqliteMigrator.fromRecord({ "1_current_thread_storage": Effect.gen(function* () {
|
|
138
|
+
const sql = yield* SqlClient.SqlClient;
|
|
139
|
+
yield* sql`
|
|
139
140
|
CREATE TABLE effect_agent_threads (
|
|
140
141
|
thread_id TEXT PRIMARY KEY NOT NULL,
|
|
141
142
|
created_at TEXT NOT NULL,
|
|
@@ -144,7 +145,7 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
144
145
|
producer_epoch INTEGER NOT NULL
|
|
145
146
|
)
|
|
146
147
|
`.withoutTransform;
|
|
147
|
-
|
|
148
|
+
yield* sql`
|
|
148
149
|
CREATE TABLE effect_agent_canonical_batches (
|
|
149
150
|
thread_id TEXT NOT NULL,
|
|
150
151
|
batch_id TEXT NOT NULL,
|
|
@@ -159,7 +160,7 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
159
160
|
ON DELETE RESTRICT
|
|
160
161
|
)
|
|
161
162
|
`.withoutTransform;
|
|
162
|
-
|
|
163
|
+
yield* sql`
|
|
163
164
|
CREATE TABLE effect_agent_canonical_records (
|
|
164
165
|
thread_id TEXT NOT NULL,
|
|
165
166
|
sequence INTEGER NOT NULL,
|
|
@@ -173,11 +174,11 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
173
174
|
ON DELETE RESTRICT
|
|
174
175
|
)
|
|
175
176
|
`.withoutTransform;
|
|
176
|
-
|
|
177
|
+
yield* sql`
|
|
177
178
|
CREATE INDEX effect_agent_canonical_records_batch
|
|
178
179
|
ON effect_agent_canonical_records (thread_id, batch_id, sequence)
|
|
179
180
|
`.withoutTransform;
|
|
180
|
-
|
|
181
|
+
yield* sql`
|
|
181
182
|
CREATE TABLE effect_agent_checkpoints (
|
|
182
183
|
thread_id TEXT NOT NULL,
|
|
183
184
|
through_sequence INTEGER NOT NULL,
|
|
@@ -189,11 +190,7 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
189
190
|
ON DELETE RESTRICT
|
|
190
191
|
)
|
|
191
192
|
`.withoutTransform;
|
|
192
|
-
|
|
193
|
-
}),
|
|
194
|
-
"2_durable_submission_ledger": Effect.gen(function* () {
|
|
195
|
-
const sql = yield* SqlClient.SqlClient;
|
|
196
|
-
yield* sql`
|
|
193
|
+
yield* sql`
|
|
197
194
|
CREATE TABLE effect_agent_submissions (
|
|
198
195
|
submission_id TEXT PRIMARY KEY NOT NULL,
|
|
199
196
|
thread_id TEXT NOT NULL,
|
|
@@ -212,11 +209,18 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
212
209
|
ready_at TEXT,
|
|
213
210
|
input_applied_record_id TEXT,
|
|
214
211
|
input_applied_sequence INTEGER,
|
|
212
|
+
joined_host_submission_id TEXT,
|
|
213
|
+
suspended_reason_json TEXT,
|
|
214
|
+
suspended_at TEXT,
|
|
215
|
+
unknown_reason TEXT,
|
|
216
|
+
unknown_tool_call_ids_json TEXT,
|
|
217
|
+
parent_submission_id TEXT,
|
|
218
|
+
parent_tool_call_id TEXT,
|
|
215
219
|
UNIQUE (thread_id, principal, idempotency_key),
|
|
216
220
|
UNIQUE (thread_id, queue_sequence)
|
|
217
221
|
)
|
|
218
222
|
`.withoutTransform;
|
|
219
|
-
|
|
223
|
+
yield* sql`
|
|
220
224
|
CREATE TABLE effect_agent_submission_ownership (
|
|
221
225
|
submission_id TEXT PRIMARY KEY NOT NULL,
|
|
222
226
|
attempt_id TEXT NOT NULL,
|
|
@@ -229,7 +233,7 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
229
233
|
ON DELETE RESTRICT
|
|
230
234
|
)
|
|
231
235
|
`.withoutTransform;
|
|
232
|
-
|
|
236
|
+
yield* sql`
|
|
233
237
|
CREATE TABLE effect_agent_attempts (
|
|
234
238
|
attempt_id TEXT PRIMARY KEY NOT NULL,
|
|
235
239
|
submission_id TEXT NOT NULL,
|
|
@@ -242,7 +246,7 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
242
246
|
ON DELETE RESTRICT
|
|
243
247
|
)
|
|
244
248
|
`.withoutTransform;
|
|
245
|
-
|
|
249
|
+
yield* sql`
|
|
246
250
|
CREATE TABLE effect_agent_settlement_reservations (
|
|
247
251
|
submission_id TEXT PRIMARY KEY NOT NULL,
|
|
248
252
|
settlement_id TEXT NOT NULL,
|
|
@@ -257,7 +261,7 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
257
261
|
ON DELETE RESTRICT
|
|
258
262
|
)
|
|
259
263
|
`.withoutTransform;
|
|
260
|
-
|
|
264
|
+
yield* sql`
|
|
261
265
|
CREATE TABLE effect_agent_abort_intents (
|
|
262
266
|
submission_id TEXT PRIMARY KEY NOT NULL,
|
|
263
267
|
author TEXT NOT NULL,
|
|
@@ -269,35 +273,11 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
269
273
|
ON DELETE RESTRICT
|
|
270
274
|
)
|
|
271
275
|
`.withoutTransform;
|
|
272
|
-
|
|
273
|
-
}),
|
|
274
|
-
"3_durable_tools_and_joined_input": Effect.gen(function* () {
|
|
275
|
-
const sql = yield* SqlClient.SqlClient;
|
|
276
|
-
yield* sql`
|
|
277
|
-
ALTER TABLE effect_agent_submissions
|
|
278
|
-
ADD COLUMN joined_host_submission_id TEXT
|
|
279
|
-
`.withoutTransform;
|
|
280
|
-
yield* sql`
|
|
281
|
-
ALTER TABLE effect_agent_submissions
|
|
282
|
-
ADD COLUMN suspended_reason_json TEXT
|
|
283
|
-
`.withoutTransform;
|
|
284
|
-
yield* sql`
|
|
285
|
-
ALTER TABLE effect_agent_submissions
|
|
286
|
-
ADD COLUMN suspended_at TEXT
|
|
287
|
-
`.withoutTransform;
|
|
288
|
-
yield* sql`
|
|
289
|
-
ALTER TABLE effect_agent_submissions
|
|
290
|
-
ADD COLUMN unknown_reason TEXT
|
|
291
|
-
`.withoutTransform;
|
|
292
|
-
yield* sql`
|
|
293
|
-
ALTER TABLE effect_agent_submissions
|
|
294
|
-
ADD COLUMN unknown_tool_call_ids_json TEXT
|
|
295
|
-
`.withoutTransform;
|
|
296
|
-
yield* sql`
|
|
276
|
+
yield* sql`
|
|
297
277
|
CREATE INDEX effect_agent_submissions_joined_host
|
|
298
278
|
ON effect_agent_submissions (joined_host_submission_id)
|
|
299
279
|
`.withoutTransform;
|
|
300
|
-
|
|
280
|
+
yield* sql`
|
|
301
281
|
CREATE TABLE effect_agent_approval_decisions (
|
|
302
282
|
submission_id TEXT NOT NULL,
|
|
303
283
|
tool_call_id TEXT NOT NULL,
|
|
@@ -311,7 +291,7 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
311
291
|
ON DELETE RESTRICT
|
|
312
292
|
)
|
|
313
293
|
`.withoutTransform;
|
|
314
|
-
|
|
294
|
+
yield* sql`
|
|
315
295
|
CREATE TABLE effect_agent_unknown_resolutions (
|
|
316
296
|
submission_id TEXT NOT NULL,
|
|
317
297
|
tool_call_id TEXT NOT NULL,
|
|
@@ -325,23 +305,11 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
325
305
|
ON DELETE RESTRICT
|
|
326
306
|
)
|
|
327
307
|
`.withoutTransform;
|
|
328
|
-
|
|
329
|
-
}),
|
|
330
|
-
"4_durable_subagents": Effect.gen(function* () {
|
|
331
|
-
const sql = yield* SqlClient.SqlClient;
|
|
332
|
-
yield* sql`
|
|
333
|
-
ALTER TABLE effect_agent_submissions
|
|
334
|
-
ADD COLUMN parent_submission_id TEXT
|
|
335
|
-
`.withoutTransform;
|
|
336
|
-
yield* sql`
|
|
337
|
-
ALTER TABLE effect_agent_submissions
|
|
338
|
-
ADD COLUMN parent_tool_call_id TEXT
|
|
339
|
-
`.withoutTransform;
|
|
340
|
-
yield* sql`
|
|
308
|
+
yield* sql`
|
|
341
309
|
CREATE INDEX effect_agent_submissions_parent
|
|
342
310
|
ON effect_agent_submissions (parent_submission_id)
|
|
343
311
|
`.withoutTransform;
|
|
344
|
-
|
|
312
|
+
yield* sql`
|
|
345
313
|
CREATE TABLE effect_agent_child_reservations (
|
|
346
314
|
reservation_id TEXT PRIMARY KEY NOT NULL,
|
|
347
315
|
parent_submission_id TEXT NOT NULL,
|
|
@@ -360,11 +328,7 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
360
328
|
ON DELETE RESTRICT
|
|
361
329
|
)
|
|
362
330
|
`.withoutTransform;
|
|
363
|
-
|
|
364
|
-
}),
|
|
365
|
-
"5_durable_schedules": Effect.gen(function* () {
|
|
366
|
-
const sql = yield* SqlClient.SqlClient;
|
|
367
|
-
yield* sql`
|
|
331
|
+
yield* sql`
|
|
368
332
|
CREATE TABLE effect_agent_schedules (
|
|
369
333
|
tenant_id TEXT NOT NULL,
|
|
370
334
|
owner_id TEXT NOT NULL,
|
|
@@ -374,21 +338,17 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
374
338
|
PRIMARY KEY (tenant_id, owner_id, schedule_id)
|
|
375
339
|
)
|
|
376
340
|
`.withoutTransform;
|
|
377
|
-
|
|
341
|
+
yield* sql`
|
|
378
342
|
CREATE INDEX effect_agent_schedules_deadline
|
|
379
343
|
ON effect_agent_schedules (deadline_at_millis, tenant_id, owner_id, schedule_id)
|
|
380
344
|
WHERE deadline_at_millis IS NOT NULL
|
|
381
345
|
`.withoutTransform;
|
|
382
|
-
|
|
346
|
+
yield* sql`
|
|
383
347
|
CREATE INDEX effect_agent_schedules_owner_deadline
|
|
384
348
|
ON effect_agent_schedules (tenant_id, owner_id, deadline_at_millis, schedule_id)
|
|
385
349
|
WHERE deadline_at_millis IS NOT NULL
|
|
386
350
|
`.withoutTransform;
|
|
387
|
-
|
|
388
|
-
}),
|
|
389
|
-
"6_durable_subscriptions": Effect.gen(function* () {
|
|
390
|
-
const sql = yield* SqlClient.SqlClient;
|
|
391
|
-
yield* sql`
|
|
351
|
+
yield* sql`
|
|
392
352
|
CREATE TABLE effect_agent_subscription_sequences (
|
|
393
353
|
tenant_id TEXT NOT NULL,
|
|
394
354
|
source_address TEXT NOT NULL,
|
|
@@ -399,7 +359,7 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
399
359
|
PRIMARY KEY (tenant_id, source_address)
|
|
400
360
|
)
|
|
401
361
|
`.withoutTransform;
|
|
402
|
-
|
|
362
|
+
yield* sql`
|
|
403
363
|
CREATE TABLE effect_agent_subscriptions (
|
|
404
364
|
tenant_id TEXT NOT NULL,
|
|
405
365
|
source_address TEXT NOT NULL,
|
|
@@ -417,10 +377,10 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
417
377
|
UNIQUE (tenant_id, source_address, ordinal)
|
|
418
378
|
)
|
|
419
379
|
`.withoutTransform;
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
380
|
+
yield* sql`CREATE INDEX effect_agent_subscriptions_owner ON effect_agent_subscriptions (tenant_id, source_address, owner_id, ordinal)`.withoutTransform;
|
|
381
|
+
yield* sql`CREATE INDEX effect_agent_subscriptions_candidates ON effect_agent_subscriptions (tenant_id, source_address, source_name, source_version, matching_key, ordinal)`.withoutTransform;
|
|
382
|
+
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`.withoutTransform;
|
|
383
|
+
yield* sql`
|
|
424
384
|
CREATE TABLE effect_agent_subscription_events (
|
|
425
385
|
tenant_id TEXT NOT NULL,
|
|
426
386
|
source_address TEXT NOT NULL,
|
|
@@ -437,8 +397,8 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
437
397
|
PRIMARY KEY (tenant_id, source_address, event_id)
|
|
438
398
|
)
|
|
439
399
|
`.withoutTransform;
|
|
440
|
-
|
|
441
|
-
|
|
400
|
+
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)`.withoutTransform;
|
|
401
|
+
yield* sql`
|
|
442
402
|
CREATE TABLE effect_agent_subscription_deliveries (
|
|
443
403
|
tenant_id TEXT NOT NULL,
|
|
444
404
|
source_address TEXT NOT NULL,
|
|
@@ -453,14 +413,308 @@ const sqliteMigrations = SqliteMigrator.fromRecord({
|
|
|
453
413
|
UNIQUE (tenant_id, source_address, delivery_key)
|
|
454
414
|
)
|
|
455
415
|
`.withoutTransform;
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
416
|
+
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)`.withoutTransform;
|
|
417
|
+
yield* sql`CREATE INDEX effect_agent_subscription_deliveries_registration ON effect_agent_subscription_deliveries (tenant_id, source_address, owner_id, subscription_id, delivery_key)`.withoutTransform;
|
|
418
|
+
yield* sql`PRAGMA user_version = 7`.withoutTransform;
|
|
419
|
+
}) });
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/sqlite-activity-store.ts
|
|
422
|
+
const STORAGE_VERSION$1 = 1;
|
|
423
|
+
const METADATA_COMPONENT$1 = "activity";
|
|
424
|
+
const STATE_TABLE = "effect_agent_activity_processor_state_v1";
|
|
425
|
+
const StoredJson$2 = Schema.String.check(Schema.isMaxLength(16777216));
|
|
426
|
+
const sameKey = Schema.toEquivalence(ActivityProcessorKey);
|
|
427
|
+
const sameWork = Schema.toEquivalence(PreparedActivity);
|
|
428
|
+
var ActivityMetadataRow = class extends Schema.Class("@effect-agent/storage-sqlite/ActivityMetadataRow")({ version: Schema.Int }) {};
|
|
429
|
+
var ActivityTableRow = class extends Schema.Class("@effect-agent/storage-sqlite/ActivityTableRow")({ name: Schema.NonEmptyString }) {};
|
|
430
|
+
var ActivityStateRow = class extends Schema.Class("@effect-agent/storage-sqlite/ActivityStateRow")({
|
|
431
|
+
processor_id: ActivityProcessorKey.fields.processorId,
|
|
432
|
+
processor_version: ActivityProcessorKey.fields.processorVersion,
|
|
433
|
+
thread_id: ActivityProcessorKey.fields.threadId,
|
|
434
|
+
format_version: Schema.Int,
|
|
435
|
+
through_sequence: ActivityProgress.fields.throughSequence,
|
|
436
|
+
epoch: ActivityProgress.fields.epoch,
|
|
437
|
+
owner: ActivityProgress.fields.owner,
|
|
438
|
+
lease_expires_at: ActivityProgress.fields.leaseExpiresAt,
|
|
439
|
+
progress_json: StoredJson$2
|
|
440
|
+
}) {};
|
|
441
|
+
var ActivityChangeCountRow = class extends Schema.Class("@effect-agent/storage-sqlite/ActivityChangeCountRow")({ changed: Schema.Int }) {};
|
|
442
|
+
const StoredVersionHeader$1 = Schema.Struct({ version: Schema.Int });
|
|
443
|
+
const storeError$1 = (operation, reason = "unavailable") => ActivityStoreError.make({
|
|
444
|
+
operation,
|
|
445
|
+
reason
|
|
446
|
+
});
|
|
447
|
+
const query$1 = (effect, operation) => effect.pipe(Effect.mapError(() => storeError$1(operation)));
|
|
448
|
+
const decodeRows$4 = Effect.fn("SqliteActivityStore.decodeRows")(function* (schema, rows, operation) {
|
|
449
|
+
return yield* Schema.decodeUnknownEffect(Schema.Array(schema))(rows).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")));
|
|
450
|
+
});
|
|
451
|
+
const decodeInput$2 = Effect.fn("SqliteActivityStore.decodeInput")(function* (schema, value, operation) {
|
|
452
|
+
return yield* Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => storeError$1(operation, "invalid-input")));
|
|
463
453
|
});
|
|
454
|
+
const encodeProgress = Effect.fn("SqliteActivityStore.encodeProgress")(function* (progress, operation) {
|
|
455
|
+
return yield* Schema.encodeEffect(Schema.fromJsonString(ActivityProgress))(progress).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")), Effect.flatMap((encoded) => Schema.decodeEffect(StoredJson$2)(encoded).pipe(Effect.mapError(() => storeError$1(operation, "invalid-input")))));
|
|
456
|
+
});
|
|
457
|
+
const decodeProgress = Effect.fn("SqliteActivityStore.decodeProgress")(function* (value, operation) {
|
|
458
|
+
if ((yield* Schema.decodeEffect(Schema.fromJsonString(StoredVersionHeader$1))(value).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")))).version !== STORAGE_VERSION$1) return yield* storeError$1(operation, "incompatible");
|
|
459
|
+
const progress = yield* Schema.decodeEffect(Schema.fromJsonString(ActivityProgress))(value).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")));
|
|
460
|
+
if ((yield* encodeProgress(progress, operation)) !== value) return yield* storeError$1(operation, "corrupt");
|
|
461
|
+
return progress;
|
|
462
|
+
});
|
|
463
|
+
const validateProgress = Effect.fn("SqliteActivityStore.validateProgress")(function* (progress, operation) {
|
|
464
|
+
if (progress.epoch < 1 || progress.owner === null && progress.leaseExpiresAt !== 0 || progress.pending !== null && (!sameKey(progress.pending.key, progress.key) || progress.pending.sequence !== progress.throughSequence + 1)) return yield* storeError$1(operation, "corrupt");
|
|
465
|
+
return progress;
|
|
466
|
+
});
|
|
467
|
+
const makeClaim = (progress) => progress.owner === null ? null : ActivityClaim.make({
|
|
468
|
+
key: progress.key,
|
|
469
|
+
owner: progress.owner,
|
|
470
|
+
epoch: progress.epoch,
|
|
471
|
+
throughSequence: progress.throughSequence,
|
|
472
|
+
leaseExpiresAt: progress.leaseExpiresAt,
|
|
473
|
+
pending: progress.pending
|
|
474
|
+
});
|
|
475
|
+
const ownershipLost = (claim) => ActivityOwnershipLost.make({
|
|
476
|
+
key: claim.key,
|
|
477
|
+
owner: claim.owner,
|
|
478
|
+
epoch: claim.epoch
|
|
479
|
+
});
|
|
480
|
+
const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
|
|
481
|
+
const sql = yield* SqlClient.SqlClient;
|
|
482
|
+
const failpoint = yield* ActivityMutationFailpoint;
|
|
483
|
+
yield* failpoint.hit("activity:initialize:before");
|
|
484
|
+
yield* sql.withTransaction(Effect.gen(function* () {
|
|
485
|
+
yield* sql`
|
|
486
|
+
CREATE TABLE IF NOT EXISTS effect_agent_activity_metadata (
|
|
487
|
+
component TEXT PRIMARY KEY NOT NULL,
|
|
488
|
+
version INTEGER NOT NULL
|
|
489
|
+
)
|
|
490
|
+
`;
|
|
491
|
+
const metadataRows = yield* sql`
|
|
492
|
+
SELECT version FROM effect_agent_activity_metadata
|
|
493
|
+
WHERE component = ${METADATA_COMPONENT$1}
|
|
494
|
+
`;
|
|
495
|
+
const metadata = yield* decodeRows$4(ActivityMetadataRow, metadataRows, "decode activity schema version");
|
|
496
|
+
if (metadata.length > 1) return yield* storeError$1("decode activity schema version", "corrupt");
|
|
497
|
+
const currentVersion = metadata[0]?.version;
|
|
498
|
+
if (currentVersion !== void 0 && currentVersion !== STORAGE_VERSION$1) return yield* storeError$1("initialize activity schema", "incompatible");
|
|
499
|
+
if (currentVersion === void 0) {
|
|
500
|
+
const tableRows = yield* sql`
|
|
501
|
+
SELECT name FROM sqlite_master
|
|
502
|
+
WHERE type = 'table' AND name = ${STATE_TABLE}
|
|
503
|
+
`;
|
|
504
|
+
if ((yield* decodeRows$4(ActivityTableRow, tableRows, "inspect activity schema")).length > 0) return yield* storeError$1("initialize activity schema", "incompatible");
|
|
505
|
+
yield* sql`
|
|
506
|
+
CREATE TABLE effect_agent_activity_processor_state_v1 (
|
|
507
|
+
processor_id TEXT NOT NULL,
|
|
508
|
+
processor_version TEXT NOT NULL,
|
|
509
|
+
thread_id TEXT NOT NULL,
|
|
510
|
+
format_version INTEGER NOT NULL,
|
|
511
|
+
through_sequence INTEGER NOT NULL,
|
|
512
|
+
epoch INTEGER NOT NULL,
|
|
513
|
+
owner TEXT,
|
|
514
|
+
lease_expires_at REAL NOT NULL,
|
|
515
|
+
progress_json TEXT NOT NULL,
|
|
516
|
+
PRIMARY KEY (processor_id, processor_version, thread_id)
|
|
517
|
+
)
|
|
518
|
+
`;
|
|
519
|
+
yield* sql`
|
|
520
|
+
INSERT INTO effect_agent_activity_metadata (component, version)
|
|
521
|
+
VALUES (${METADATA_COMPONENT$1}, ${STORAGE_VERSION$1})
|
|
522
|
+
`;
|
|
523
|
+
}
|
|
524
|
+
yield* sql`
|
|
525
|
+
SELECT processor_id, processor_version, thread_id, format_version,
|
|
526
|
+
through_sequence, epoch, owner, lease_expires_at, progress_json
|
|
527
|
+
FROM effect_agent_activity_processor_state_v1
|
|
528
|
+
LIMIT 0
|
|
529
|
+
`;
|
|
530
|
+
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storeError$1("initialize activity schema"))));
|
|
531
|
+
yield* failpoint.hit("activity:initialize:after");
|
|
532
|
+
const readProgress = Effect.fn("SqliteActivityStore.readProgress")(function* (key, operation) {
|
|
533
|
+
const rawRows = yield* query$1(sql`
|
|
534
|
+
SELECT processor_id, processor_version, thread_id, format_version,
|
|
535
|
+
through_sequence, epoch, owner, lease_expires_at, progress_json
|
|
536
|
+
FROM effect_agent_activity_processor_state_v1
|
|
537
|
+
WHERE processor_id = ${key.processorId}
|
|
538
|
+
AND processor_version = ${key.processorVersion}
|
|
539
|
+
AND thread_id = ${key.threadId}
|
|
540
|
+
`, operation);
|
|
541
|
+
const rows = yield* decodeRows$4(ActivityStateRow, rawRows, operation);
|
|
542
|
+
if (rows.length === 0) return null;
|
|
543
|
+
if (rows.length !== 1) return yield* storeError$1(operation, "corrupt");
|
|
544
|
+
const row = rows[0];
|
|
545
|
+
if (row.format_version !== STORAGE_VERSION$1) return yield* storeError$1(operation, "incompatible");
|
|
546
|
+
const progress = yield* decodeProgress(row.progress_json, operation);
|
|
547
|
+
yield* validateProgress(progress, operation);
|
|
548
|
+
if (!sameKey(progress.key, key) || row.processor_id !== key.processorId || row.processor_version !== key.processorVersion || row.thread_id !== key.threadId || row.through_sequence !== progress.throughSequence || row.epoch !== progress.epoch || row.owner !== progress.owner || row.lease_expires_at !== progress.leaseExpiresAt) return yield* storeError$1(operation, "corrupt");
|
|
549
|
+
return progress;
|
|
550
|
+
});
|
|
551
|
+
const checkChanged = Effect.fn("SqliteActivityStore.checkChanged")(function* (operation) {
|
|
552
|
+
const rawRows = yield* query$1(sql`SELECT changes() AS changed`, operation);
|
|
553
|
+
const rows = yield* decodeRows$4(ActivityChangeCountRow, rawRows, operation);
|
|
554
|
+
if (rows.length !== 1 || rows[0].changed !== 1) return yield* storeError$1(operation, "corrupt");
|
|
555
|
+
});
|
|
556
|
+
const insertProgress = Effect.fn("SqliteActivityStore.insertProgress")(function* (progress, operation) {
|
|
557
|
+
const progressJson = yield* encodeProgress(progress, operation);
|
|
558
|
+
yield* sql`
|
|
559
|
+
INSERT INTO effect_agent_activity_processor_state_v1 (
|
|
560
|
+
processor_id, processor_version, thread_id, format_version, through_sequence,
|
|
561
|
+
epoch, owner, lease_expires_at, progress_json
|
|
562
|
+
) VALUES (
|
|
563
|
+
${progress.key.processorId}, ${progress.key.processorVersion}, ${progress.key.threadId},
|
|
564
|
+
${STORAGE_VERSION$1}, ${progress.throughSequence}, ${progress.epoch}, ${progress.owner},
|
|
565
|
+
${progress.leaseExpiresAt}, ${progressJson}
|
|
566
|
+
)
|
|
567
|
+
`;
|
|
568
|
+
yield* checkChanged(operation);
|
|
569
|
+
});
|
|
570
|
+
const updateProgress = Effect.fn("SqliteActivityStore.updateProgress")(function* (current, next, operation) {
|
|
571
|
+
const progressJson = yield* encodeProgress(next, operation);
|
|
572
|
+
yield* sql`
|
|
573
|
+
UPDATE effect_agent_activity_processor_state_v1
|
|
574
|
+
SET format_version = ${STORAGE_VERSION$1},
|
|
575
|
+
through_sequence = ${next.throughSequence},
|
|
576
|
+
epoch = ${next.epoch},
|
|
577
|
+
owner = ${next.owner},
|
|
578
|
+
lease_expires_at = ${next.leaseExpiresAt},
|
|
579
|
+
progress_json = ${progressJson}
|
|
580
|
+
WHERE processor_id = ${current.key.processorId}
|
|
581
|
+
AND processor_version = ${current.key.processorVersion}
|
|
582
|
+
AND thread_id = ${current.key.threadId}
|
|
583
|
+
AND through_sequence = ${current.throughSequence}
|
|
584
|
+
AND epoch = ${current.epoch}
|
|
585
|
+
`;
|
|
586
|
+
yield* checkChanged(operation);
|
|
587
|
+
});
|
|
588
|
+
const requireLive = Effect.fn("SqliteActivityStore.requireLive")(function* (progress, claim, requireSequence) {
|
|
589
|
+
const now = yield* Clock.currentTimeMillis;
|
|
590
|
+
if (progress === null || !sameKey(progress.key, claim.key) || progress.owner !== claim.owner || progress.epoch !== claim.epoch || progress.leaseExpiresAt <= now || requireSequence && progress.throughSequence !== claim.throughSequence) return yield* ownershipLost(claim);
|
|
591
|
+
return progress;
|
|
592
|
+
});
|
|
593
|
+
const inspect = Effect.fn("SqliteActivityStore.inspect")(function* (key) {
|
|
594
|
+
const decodedKey = yield* decodeInput$2(ActivityProcessorKey, key, "inspect activity progress");
|
|
595
|
+
return yield* readProgress(decodedKey, "inspect activity progress");
|
|
596
|
+
});
|
|
597
|
+
const claim = Effect.fn("SqliteActivityStore.claim")(function* (request) {
|
|
598
|
+
const operation = "claim activity progress";
|
|
599
|
+
const decoded = yield* decodeInput$2(ActivityClaimRequest, request, operation);
|
|
600
|
+
yield* failpoint.hit("activity:claim:before");
|
|
601
|
+
const claimed = yield* sql.withTransaction(Effect.gen(function* () {
|
|
602
|
+
const current = yield* readProgress(decoded.key, operation);
|
|
603
|
+
const now = yield* Clock.currentTimeMillis;
|
|
604
|
+
if (current !== null && current.owner !== null && current.leaseExpiresAt > now) return yield* ActivityBusy.make({
|
|
605
|
+
key: decoded.key,
|
|
606
|
+
leaseExpiresAt: current.leaseExpiresAt
|
|
607
|
+
});
|
|
608
|
+
const next = yield* Schema.decodeUnknownEffect(ActivityProgress)({
|
|
609
|
+
version: STORAGE_VERSION$1,
|
|
610
|
+
key: decoded.key,
|
|
611
|
+
throughSequence: current?.throughSequence ?? 0,
|
|
612
|
+
epoch: (current?.epoch ?? 0) + 1,
|
|
613
|
+
owner: decoded.owner,
|
|
614
|
+
leaseExpiresAt: now + decoded.leaseMillis,
|
|
615
|
+
pending: current?.pending ?? null,
|
|
616
|
+
advancedAt: current?.advancedAt ?? null
|
|
617
|
+
}).pipe(Effect.mapError(() => storeError$1(operation, "corrupt")));
|
|
618
|
+
if (current === null) yield* insertProgress(next, operation);
|
|
619
|
+
else yield* updateProgress(current, next, operation);
|
|
620
|
+
yield* failpoint.hit("activity:claim:after-state");
|
|
621
|
+
const result = makeClaim(next);
|
|
622
|
+
if (result === null) return yield* storeError$1(operation, "corrupt");
|
|
623
|
+
return result;
|
|
624
|
+
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storeError$1(operation))));
|
|
625
|
+
yield* failpoint.hit("activity:claim:after");
|
|
626
|
+
return claimed;
|
|
627
|
+
});
|
|
628
|
+
const prepare = Effect.fn("SqliteActivityStore.prepare")(function* (request) {
|
|
629
|
+
const operation = "prepare activity output";
|
|
630
|
+
const claim = yield* decodeInput$2(ActivityClaim, request.claim, operation);
|
|
631
|
+
const work = yield* decodeInput$2(PreparedActivity, request.work, operation);
|
|
632
|
+
yield* failpoint.hit("activity:prepare:before");
|
|
633
|
+
const result = yield* sql.withTransaction(Effect.gen(function* () {
|
|
634
|
+
const current = yield* requireLive(yield* readProgress(claim.key, operation), claim, true);
|
|
635
|
+
if (current.pending !== null) {
|
|
636
|
+
if (sameWork(current.pending, work)) return {
|
|
637
|
+
work: current.pending,
|
|
638
|
+
changed: false
|
|
639
|
+
};
|
|
640
|
+
return yield* ActivityWorkConflict.make({
|
|
641
|
+
key: claim.key,
|
|
642
|
+
workId: work.workId
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
if (!sameKey(work.key, claim.key) || work.sequence !== current.throughSequence + 1) return yield* ActivityWorkConflict.make({
|
|
646
|
+
key: claim.key,
|
|
647
|
+
workId: work.workId
|
|
648
|
+
});
|
|
649
|
+
const next = ActivityProgress.make({
|
|
650
|
+
...current,
|
|
651
|
+
pending: work
|
|
652
|
+
});
|
|
653
|
+
yield* updateProgress(current, next, operation);
|
|
654
|
+
yield* failpoint.hit("activity:prepare:after-state");
|
|
655
|
+
return {
|
|
656
|
+
work,
|
|
657
|
+
changed: true
|
|
658
|
+
};
|
|
659
|
+
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storeError$1(operation))));
|
|
660
|
+
if (result.changed) yield* failpoint.hit("activity:prepare:after");
|
|
661
|
+
return result.work;
|
|
662
|
+
});
|
|
663
|
+
const advance = Effect.fn("SqliteActivityStore.advance")(function* (request) {
|
|
664
|
+
const operation = "advance activity progress";
|
|
665
|
+
const claim = yield* decodeInput$2(ActivityClaim, request.claim, operation);
|
|
666
|
+
const workId = yield* decodeInput$2(Digest, request.workId, operation);
|
|
667
|
+
yield* failpoint.hit("activity:advance:before");
|
|
668
|
+
const nextClaim = yield* sql.withTransaction(Effect.gen(function* () {
|
|
669
|
+
const current = yield* requireLive(yield* readProgress(claim.key, operation), claim, true);
|
|
670
|
+
if (current.pending === null || current.pending.workId !== workId) return yield* ActivityWorkConflict.make({
|
|
671
|
+
key: claim.key,
|
|
672
|
+
workId
|
|
673
|
+
});
|
|
674
|
+
const next = ActivityProgress.make({
|
|
675
|
+
...current,
|
|
676
|
+
throughSequence: current.pending.sequence,
|
|
677
|
+
pending: null,
|
|
678
|
+
advancedAt: yield* Clock.currentTimeMillis
|
|
679
|
+
});
|
|
680
|
+
yield* updateProgress(current, next, operation);
|
|
681
|
+
yield* failpoint.hit("activity:advance:after-state");
|
|
682
|
+
const result = makeClaim(next);
|
|
683
|
+
if (result === null) return yield* storeError$1(operation, "corrupt");
|
|
684
|
+
return result;
|
|
685
|
+
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storeError$1(operation))));
|
|
686
|
+
yield* failpoint.hit("activity:advance:after");
|
|
687
|
+
return nextClaim;
|
|
688
|
+
});
|
|
689
|
+
const release = Effect.fn("SqliteActivityStore.release")(function* (claim) {
|
|
690
|
+
const operation = "release activity claim";
|
|
691
|
+
const decoded = yield* decodeInput$2(ActivityClaim, claim, operation);
|
|
692
|
+
yield* failpoint.hit("activity:release:before");
|
|
693
|
+
yield* sql.withTransaction(Effect.gen(function* () {
|
|
694
|
+
const current = yield* readProgress(decoded.key, operation);
|
|
695
|
+
if (current === null || current.owner !== decoded.owner || current.epoch !== decoded.epoch) return yield* ownershipLost(decoded);
|
|
696
|
+
const next = ActivityProgress.make({
|
|
697
|
+
...current,
|
|
698
|
+
owner: null,
|
|
699
|
+
leaseExpiresAt: 0
|
|
700
|
+
});
|
|
701
|
+
yield* updateProgress(current, next, operation);
|
|
702
|
+
yield* failpoint.hit("activity:release:after-state");
|
|
703
|
+
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storeError$1(operation))));
|
|
704
|
+
yield* failpoint.hit("activity:release:after");
|
|
705
|
+
});
|
|
706
|
+
return ActivityProcessorStore.of({
|
|
707
|
+
inspect,
|
|
708
|
+
claim,
|
|
709
|
+
prepare,
|
|
710
|
+
advance,
|
|
711
|
+
release
|
|
712
|
+
});
|
|
713
|
+
});
|
|
714
|
+
/** SQLite activity progress with mutation failpoints kept injectable for recovery tests. */
|
|
715
|
+
const activityProcessorStoreLayerWithFailpoints = Layer.effect(ActivityProcessorStore, makeActivityStore());
|
|
716
|
+
/** SQLite activity progress with the production no-op mutation failpoint. */
|
|
717
|
+
const activityProcessorStoreLayer = activityProcessorStoreLayerWithFailpoints.pipe(Layer.provide(ActivityMutationFailpoint.layer));
|
|
464
718
|
//#endregion
|
|
465
719
|
//#region src/sqlite-storage-config.ts
|
|
466
720
|
const ObservationPollInterval = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
|
|
@@ -493,11 +747,11 @@ var SqliteStorageConfigValue = class extends Schema.Class("@effect-agent/storage
|
|
|
493
747
|
var SqliteStorageConfig = class extends Context.Service()("@effect-agent/storage-sqlite/SqliteStorageConfig") {};
|
|
494
748
|
//#endregion
|
|
495
749
|
//#region src/sqlite-journal.ts
|
|
496
|
-
const BoundedStoredText$1 = Schema.String.check(Schema.isMaxLength(
|
|
750
|
+
const BoundedStoredText$1 = Schema.String.check(Schema.isMaxLength(16777216));
|
|
497
751
|
const BoundedIdentifier$1 = Schema.NonEmptyString.check(Schema.isMaxLength(1024));
|
|
498
752
|
const NonNegativeInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
|
|
499
753
|
const MAX_RECORDS_PER_THREAD = 65536;
|
|
500
|
-
const MAX_STORED_TEXT_BYTES =
|
|
754
|
+
const MAX_STORED_TEXT_BYTES = 16777216;
|
|
501
755
|
const MAX_IDENTIFIER_LENGTH = 1024;
|
|
502
756
|
const storedTextBytes = (value) => new TextEncoder().encode(value).byteLength;
|
|
503
757
|
var SqliteVersionRow = class extends Schema.Class("SqliteVersionRow")({ user_version: NonNegativeInt }) {};
|
|
@@ -570,19 +824,19 @@ var RawThreadExport = class extends Schema.Class("@effect-agent/storage-sqlite/R
|
|
|
570
824
|
thread: ThreadRow,
|
|
571
825
|
records: Schema.Array(RecordRow)
|
|
572
826
|
}) {};
|
|
573
|
-
const storageError = (operation) => (error) => SqliteStorageError.make({
|
|
827
|
+
const storageError$1 = (operation) => (error) => SqliteStorageError.make({
|
|
574
828
|
cause: error,
|
|
575
829
|
operation,
|
|
576
830
|
message: error.message
|
|
577
831
|
});
|
|
578
832
|
/** Decode raw SQLite rows against a Schema, reporting failures as typed corruption. */
|
|
579
|
-
const decodeRows$
|
|
833
|
+
const decodeRows$3 = Effect.fn("SqliteJournal.decodeRows")((schema, table, rowKey, rows) => Schema.decodeUnknownEffect(schema)(rows).pipe(Effect.mapError((error) => SqliteStorageCorruptionError.make({
|
|
580
834
|
table,
|
|
581
835
|
rowKey,
|
|
582
836
|
message: String(error)
|
|
583
837
|
}))));
|
|
584
838
|
/** Decode exactly one raw SQLite row against a Schema, reporting failures as typed corruption. */
|
|
585
|
-
const decodeSingleRow = Effect.fn("SqliteJournal.decodeSingleRow")((schema, table, rowKey, rows) => decodeRows$
|
|
839
|
+
const decodeSingleRow = Effect.fn("SqliteJournal.decodeSingleRow")((schema, table, rowKey, rows) => decodeRows$3(schema, table, rowKey, rows).pipe(Effect.flatMap((decoded) => decoded.length === 1 ? Effect.succeed(decoded[0]) : Effect.fail(SqliteStorageCorruptionError.make({
|
|
586
840
|
table,
|
|
587
841
|
rowKey,
|
|
588
842
|
message: `Expected exactly one row but found ${decoded.length}.`
|
|
@@ -591,16 +845,16 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
591
845
|
const sql = yield* SqlClient.SqlClient;
|
|
592
846
|
const { hit: failpoint } = yield* SqliteStorageFailpoint;
|
|
593
847
|
const { busyTimeout } = yield* SqliteStorageConfig;
|
|
594
|
-
yield* sql`PRAGMA foreign_keys = ON`.pipe(Effect.mapError(storageError("enable foreign keys")));
|
|
595
|
-
yield* sql.unsafe(`PRAGMA busy_timeout = ${busyTimeout}`).pipe(Effect.mapError(storageError("configure busy timeout")));
|
|
596
|
-
const journalModeRows = yield* sql`PRAGMA journal_mode`.pipe(Effect.mapError(storageError("read journal mode")));
|
|
848
|
+
yield* sql`PRAGMA foreign_keys = ON`.pipe(Effect.mapError(storageError$1("enable foreign keys")));
|
|
849
|
+
yield* sql.unsafe(`PRAGMA busy_timeout = ${busyTimeout}`).pipe(Effect.mapError(storageError$1("configure busy timeout")));
|
|
850
|
+
const journalModeRows = yield* sql`PRAGMA journal_mode`.pipe(Effect.mapError(storageError$1("read journal mode")));
|
|
597
851
|
const journalMode = yield* decodeSingleRow(Schema.Array(SqliteJournalModeRow), "pragma_journal_mode", "singleton", journalModeRows);
|
|
598
852
|
if (journalMode.journal_mode.toLowerCase() !== "wal") return yield* SqliteStorageCompatibilityError.make({
|
|
599
853
|
actualVersion: 0,
|
|
600
854
|
supportedVersion: 7,
|
|
601
855
|
message: `SQLite WAL mode is required; the database reported ${journalMode.journal_mode}.`
|
|
602
856
|
});
|
|
603
|
-
const versionRows = yield* sql`PRAGMA user_version`.pipe(Effect.mapError(storageError("read storage version")));
|
|
857
|
+
const versionRows = yield* sql`PRAGMA user_version`.pipe(Effect.mapError(storageError$1("read storage version")));
|
|
604
858
|
const version = yield* decodeSingleRow(Schema.Array(SqliteVersionRow), "pragma_user_version", "singleton", versionRows);
|
|
605
859
|
if (version.user_version !== 0 && version.user_version !== 7) return yield* SqliteStorageCompatibilityError.make({
|
|
606
860
|
actualVersion: version.user_version,
|
|
@@ -614,8 +868,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
614
868
|
WHERE type = 'table'
|
|
615
869
|
AND name LIKE 'effect_agent_%'
|
|
616
870
|
ORDER BY name
|
|
617
|
-
`.pipe(Effect.mapError(storageError("inspect unversioned storage")));
|
|
618
|
-
if ((yield* decodeRows$
|
|
871
|
+
`.pipe(Effect.mapError(storageError$1("inspect unversioned storage")));
|
|
872
|
+
if ((yield* decodeRows$3(Schema.Array(SqliteNameRow), "sqlite_master", "effect_agent_%", existingRows)).length > 0) return yield* SqliteStorageCompatibilityError.make({
|
|
619
873
|
actualVersion: 0,
|
|
620
874
|
supportedVersion: 7,
|
|
621
875
|
message: "The SQLite file contains unversioned Effect Agent tables. Reset it explicitly; refusing to mutate ambiguous stored data."
|
|
@@ -645,8 +899,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
645
899
|
'effect_agent_schedules'
|
|
646
900
|
)
|
|
647
901
|
ORDER BY name
|
|
648
|
-
`.pipe(Effect.mapError(storageError("verify storage tables")));
|
|
649
|
-
if ((yield* decodeRows$
|
|
902
|
+
`.pipe(Effect.mapError(storageError$1("verify storage tables")));
|
|
903
|
+
if ((yield* decodeRows$3(Schema.Array(SqliteNameRow), "sqlite_master", "required_tables", requiredRows)).length !== 12) return yield* SqliteStorageCompatibilityError.make({
|
|
650
904
|
actualVersion: 7,
|
|
651
905
|
supportedVersion: 7,
|
|
652
906
|
message: "The SQLite file claims the current format but is missing required tables. Reset the corrupt private-development data."
|
|
@@ -655,7 +909,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
655
909
|
cause: error,
|
|
656
910
|
operation,
|
|
657
911
|
message: `Another producer holds the SQLite write lock; ${operation} is safe to retry.`
|
|
658
|
-
}) : storageError(operation)(error);
|
|
912
|
+
}) : storageError$1(operation)(error);
|
|
659
913
|
/**
|
|
660
914
|
* Runs one journal write transaction under `BEGIN IMMEDIATE`. SQLite's deferred `BEGIN`
|
|
661
915
|
* would let a read-then-write transaction start as a reader and fail with
|
|
@@ -688,11 +942,11 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
688
942
|
* `BEGIN` is reported directly because there is no transaction to roll back.
|
|
689
943
|
*/
|
|
690
944
|
const withReadTransaction = (operation) => (effect) => Effect.uninterruptibleMask((restore) => Effect.scoped(Effect.gen(function* () {
|
|
691
|
-
const connection = yield* sql.reserve.pipe(Effect.mapError(storageError(operation)));
|
|
692
|
-
yield* connection.executeUnprepared("BEGIN", [], void 0).pipe(Effect.mapError(storageError(operation)));
|
|
945
|
+
const connection = yield* sql.reserve.pipe(Effect.mapError(storageError$1(operation)));
|
|
946
|
+
yield* connection.executeUnprepared("BEGIN", [], void 0).pipe(Effect.mapError(storageError$1(operation)));
|
|
693
947
|
const exit = yield* restore(Effect.provideService(effect, sql.transactionService, [connection, 0])).pipe(Effect.exit);
|
|
694
948
|
if (Exit.isSuccess(exit)) {
|
|
695
|
-
yield* connection.executeUnprepared("COMMIT", [], void 0).pipe(Effect.mapError(storageError(operation)));
|
|
949
|
+
yield* connection.executeUnprepared("COMMIT", [], void 0).pipe(Effect.mapError(storageError$1(operation)));
|
|
696
950
|
return exit.value;
|
|
697
951
|
}
|
|
698
952
|
yield* Effect.orDie(connection.executeUnprepared("ROLLBACK", [], void 0));
|
|
@@ -713,8 +967,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
713
967
|
producer_epoch
|
|
714
968
|
FROM effect_agent_threads
|
|
715
969
|
WHERE thread_id = ${threadId}
|
|
716
|
-
`.pipe(Effect.mapError(storageError("read materialized thread")));
|
|
717
|
-
const existing = yield* decodeRows$
|
|
970
|
+
`.pipe(Effect.mapError(storageError$1("read materialized thread")));
|
|
971
|
+
const existing = yield* decodeRows$3(Schema.Array(ThreadRow), "effect_agent_threads", threadId, existingRows);
|
|
718
972
|
if (existing.length > 1) return yield* SqliteStorageCorruptionError.make({
|
|
719
973
|
table: "effect_agent_threads",
|
|
720
974
|
rowKey: threadId,
|
|
@@ -735,7 +989,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
735
989
|
${emptyTailDigest},
|
|
736
990
|
${producerEpoch}
|
|
737
991
|
)
|
|
738
|
-
`.pipe(Effect.mapError(storageError("materialize thread")));
|
|
992
|
+
`.pipe(Effect.mapError(storageError$1("materialize thread")));
|
|
739
993
|
return;
|
|
740
994
|
}
|
|
741
995
|
if (producerEpoch < existing[0].producer_epoch) return yield* SqliteFenceRejected.make({
|
|
@@ -747,7 +1001,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
747
1001
|
UPDATE effect_agent_threads
|
|
748
1002
|
SET producer_epoch = ${producerEpoch}
|
|
749
1003
|
WHERE thread_id = ${threadId}
|
|
750
|
-
`.pipe(Effect.mapError(storageError("advance materialization epoch")));
|
|
1004
|
+
`.pipe(Effect.mapError(storageError$1("advance materialization epoch")));
|
|
751
1005
|
}));
|
|
752
1006
|
});
|
|
753
1007
|
const getThread = Effect.fn("SqliteJournal.getThread")(function* (threadId) {
|
|
@@ -760,8 +1014,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
760
1014
|
producer_epoch
|
|
761
1015
|
FROM effect_agent_threads
|
|
762
1016
|
WHERE thread_id = ${threadId}
|
|
763
|
-
`.pipe(Effect.mapError(storageError("read thread")));
|
|
764
|
-
return yield* decodeRows$
|
|
1017
|
+
`.pipe(Effect.mapError(storageError$1("read thread")));
|
|
1018
|
+
return yield* decodeRows$3(Schema.Array(ThreadRow), "effect_agent_threads", threadId, rows);
|
|
765
1019
|
});
|
|
766
1020
|
const append = Effect.fn("SqliteJournal.append")(function* (request) {
|
|
767
1021
|
if (request.threadId.length > MAX_IDENTIFIER_LENGTH || request.batchId.length > MAX_IDENTIFIER_LENGTH || storedTextBytes(request.batchJson) > MAX_STORED_TEXT_BYTES || storedTextBytes(request.batchDigest) > MAX_STORED_TEXT_BYTES || storedTextBytes(request.tailDigest) > MAX_STORED_TEXT_BYTES || request.records.some((record) => record.recordId.length > MAX_IDENTIFIER_LENGTH || storedTextBytes(record.recordJson) > MAX_STORED_TEXT_BYTES)) return yield* SqliteStorageError.make({
|
|
@@ -783,7 +1037,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
783
1037
|
producer_epoch
|
|
784
1038
|
FROM effect_agent_threads
|
|
785
1039
|
WHERE thread_id = ${request.threadId}
|
|
786
|
-
`.pipe(Effect.mapError(storageError("read append tail")));
|
|
1040
|
+
`.pipe(Effect.mapError(storageError$1("read append tail")));
|
|
787
1041
|
const thread = yield* decodeSingleRow(Schema.Array(ThreadRow), "effect_agent_threads", request.threadId, threadRows);
|
|
788
1042
|
if (request.producerEpoch !== thread.producer_epoch) return yield* SqliteFenceRejected.make({
|
|
789
1043
|
producerEpoch: request.producerEpoch,
|
|
@@ -802,8 +1056,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
802
1056
|
FROM effect_agent_canonical_batches
|
|
803
1057
|
WHERE thread_id = ${request.threadId}
|
|
804
1058
|
AND batch_id = ${request.batchId}
|
|
805
|
-
`.pipe(Effect.mapError(storageError("read idempotent batch")));
|
|
806
|
-
const batches = yield* decodeRows$
|
|
1059
|
+
`.pipe(Effect.mapError(storageError$1("read idempotent batch")));
|
|
1060
|
+
const batches = yield* decodeRows$3(Schema.Array(BatchRow), "effect_agent_canonical_batches", `${request.threadId}/${request.batchId}`, batchRows);
|
|
807
1061
|
if (batches.length > 1) return yield* SqliteStorageCorruptionError.make({
|
|
808
1062
|
table: "effect_agent_canonical_batches",
|
|
809
1063
|
rowKey: `${request.threadId}/${request.batchId}`,
|
|
@@ -843,8 +1097,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
843
1097
|
WHERE thread_id = ${request.threadId}
|
|
844
1098
|
AND record_id IN ${sql.in(recordIds)}
|
|
845
1099
|
ORDER BY sequence
|
|
846
|
-
`.pipe(Effect.mapError(storageError("check canonical record identities")));
|
|
847
|
-
const existingRecords = yield* decodeRows$
|
|
1100
|
+
`.pipe(Effect.mapError(storageError$1("check canonical record identities")));
|
|
1101
|
+
const existingRecords = yield* decodeRows$3(Schema.Array(RecordRow), "effect_agent_canonical_records", `${request.threadId}/record_ids`, existingRecordRows);
|
|
848
1102
|
if (existingRecords.length > 0) return yield* SqliteAppendConflict.make({
|
|
849
1103
|
message: `Canonical record ID ${existingRecords[0].record_id} already exists.`,
|
|
850
1104
|
reason: "record-identity"
|
|
@@ -877,7 +1131,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
877
1131
|
${request.tailDigest},
|
|
878
1132
|
${request.batchJson}
|
|
879
1133
|
)
|
|
880
|
-
`.pipe(Effect.mapError(storageError("insert canonical batch")));
|
|
1134
|
+
`.pipe(Effect.mapError(storageError$1("insert canonical batch")));
|
|
881
1135
|
yield* failpoint("append:after-batch-insert");
|
|
882
1136
|
yield* Effect.forEach(request.records, (record, index) => Effect.gen(function* () {
|
|
883
1137
|
yield* sql`
|
|
@@ -894,7 +1148,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
894
1148
|
${request.batchId},
|
|
895
1149
|
${record.recordJson}
|
|
896
1150
|
)
|
|
897
|
-
`.pipe(Effect.mapError(storageError("insert canonical record")));
|
|
1151
|
+
`.pipe(Effect.mapError(storageError$1("insert canonical record")));
|
|
898
1152
|
yield* failpoint("append:after-record-insert");
|
|
899
1153
|
}), { discard: true });
|
|
900
1154
|
yield* sql`
|
|
@@ -904,7 +1158,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
904
1158
|
tail_digest = ${request.tailDigest},
|
|
905
1159
|
producer_epoch = ${request.producerEpoch}
|
|
906
1160
|
WHERE thread_id = ${request.threadId}
|
|
907
|
-
`.pipe(Effect.mapError(storageError("advance thread tail")));
|
|
1161
|
+
`.pipe(Effect.mapError(storageError$1("advance thread tail")));
|
|
908
1162
|
yield* failpoint("append:after-tail-update");
|
|
909
1163
|
return RawAppendResult.make({
|
|
910
1164
|
firstSequence,
|
|
@@ -927,8 +1181,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
927
1181
|
AND sequence > ${request.fromSequenceExclusive}
|
|
928
1182
|
ORDER BY sequence
|
|
929
1183
|
LIMIT ${request.limit}
|
|
930
|
-
`.pipe(Effect.mapError(storageError("read canonical records")));
|
|
931
|
-
return yield* decodeRows$
|
|
1184
|
+
`.pipe(Effect.mapError(storageError$1("read canonical records")));
|
|
1185
|
+
return yield* decodeRows$3(Schema.Array(RecordRow), "effect_agent_canonical_records", `${request.threadId}>${request.fromSequenceExclusive}`, rows);
|
|
932
1186
|
});
|
|
933
1187
|
const exportThread = Effect.fn("SqliteJournal.exportThread")(function* (threadId) {
|
|
934
1188
|
return yield* withReadTransaction("export transaction")(Effect.gen(function* () {
|
|
@@ -941,7 +1195,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
941
1195
|
producer_epoch
|
|
942
1196
|
FROM effect_agent_threads
|
|
943
1197
|
WHERE thread_id = ${threadId}
|
|
944
|
-
`.pipe(Effect.mapError(storageError("export thread")));
|
|
1198
|
+
`.pipe(Effect.mapError(storageError$1("export thread")));
|
|
945
1199
|
const thread = yield* decodeSingleRow(Schema.Array(ThreadRow), "effect_agent_threads", threadId, threadRows);
|
|
946
1200
|
yield* failpoint("export:after-thread-read");
|
|
947
1201
|
const batchRows = yield* sql`
|
|
@@ -956,7 +1210,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
956
1210
|
FROM effect_agent_canonical_batches
|
|
957
1211
|
WHERE thread_id = ${threadId}
|
|
958
1212
|
ORDER BY first_sequence
|
|
959
|
-
`.pipe(Effect.mapError(storageError("export canonical batches")));
|
|
1213
|
+
`.pipe(Effect.mapError(storageError$1("export canonical batches")));
|
|
960
1214
|
const recordRows = yield* sql`
|
|
961
1215
|
SELECT
|
|
962
1216
|
thread_id,
|
|
@@ -967,7 +1221,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
967
1221
|
FROM effect_agent_canonical_records
|
|
968
1222
|
WHERE thread_id = ${threadId}
|
|
969
1223
|
ORDER BY sequence
|
|
970
|
-
`.pipe(Effect.mapError(storageError("export canonical records")));
|
|
1224
|
+
`.pipe(Effect.mapError(storageError$1("export canonical records")));
|
|
971
1225
|
const checkpointRows = yield* sql`
|
|
972
1226
|
SELECT
|
|
973
1227
|
thread_id,
|
|
@@ -977,12 +1231,12 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
977
1231
|
FROM effect_agent_checkpoints
|
|
978
1232
|
WHERE thread_id = ${threadId}
|
|
979
1233
|
ORDER BY through_sequence
|
|
980
|
-
`.pipe(Effect.mapError(storageError("export checkpoints")));
|
|
1234
|
+
`.pipe(Effect.mapError(storageError$1("export checkpoints")));
|
|
981
1235
|
return RawThreadExport.make({
|
|
982
1236
|
thread,
|
|
983
|
-
batches: yield* decodeRows$
|
|
984
|
-
records: yield* decodeRows$
|
|
985
|
-
checkpoints: yield* decodeRows$
|
|
1237
|
+
batches: yield* decodeRows$3(Schema.Array(BatchRow), "effect_agent_canonical_batches", threadId, batchRows),
|
|
1238
|
+
records: yield* decodeRows$3(Schema.Array(RecordRow), "effect_agent_canonical_records", threadId, recordRows),
|
|
1239
|
+
checkpoints: yield* decodeRows$3(Schema.Array(CheckpointRow), "effect_agent_checkpoints", threadId, checkpointRows)
|
|
986
1240
|
});
|
|
987
1241
|
}));
|
|
988
1242
|
});
|
|
@@ -1001,7 +1255,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1001
1255
|
producer_epoch
|
|
1002
1256
|
FROM effect_agent_threads
|
|
1003
1257
|
WHERE thread_id = ${checkpoint.threadId}
|
|
1004
|
-
`.pipe(Effect.mapError(storageError("read checkpoint tail")));
|
|
1258
|
+
`.pipe(Effect.mapError(storageError$1("read checkpoint tail")));
|
|
1005
1259
|
const thread = yield* decodeSingleRow(Schema.Array(ThreadRow), "effect_agent_threads", checkpoint.threadId, threadRows);
|
|
1006
1260
|
if (checkpoint.throughSequence > thread.tail_sequence) return yield* SqliteCheckpointConflict.make({ message: `Checkpoint sequence ${checkpoint.throughSequence} is after canonical tail ${thread.tail_sequence}.` });
|
|
1007
1261
|
const checkpointRows = yield* sql`
|
|
@@ -1013,8 +1267,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1013
1267
|
FROM effect_agent_checkpoints
|
|
1014
1268
|
WHERE thread_id = ${checkpoint.threadId}
|
|
1015
1269
|
AND through_sequence = ${checkpoint.throughSequence}
|
|
1016
|
-
`.pipe(Effect.mapError(storageError("read idempotent checkpoint")));
|
|
1017
|
-
const existing = yield* decodeRows$
|
|
1270
|
+
`.pipe(Effect.mapError(storageError$1("read idempotent checkpoint")));
|
|
1271
|
+
const existing = yield* decodeRows$3(Schema.Array(CheckpointRow), "effect_agent_checkpoints", `${checkpoint.threadId}/${checkpoint.throughSequence}`, checkpointRows);
|
|
1018
1272
|
if (existing.length > 1) return yield* SqliteStorageCorruptionError.make({
|
|
1019
1273
|
table: "effect_agent_checkpoints",
|
|
1020
1274
|
rowKey: `${checkpoint.threadId}/${checkpoint.throughSequence}`,
|
|
@@ -1036,7 +1290,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1036
1290
|
${checkpoint.tailDigest},
|
|
1037
1291
|
${checkpoint.checkpointJson}
|
|
1038
1292
|
)
|
|
1039
|
-
`.pipe(Effect.mapError(storageError("insert checkpoint")));
|
|
1293
|
+
`.pipe(Effect.mapError(storageError$1("insert checkpoint")));
|
|
1040
1294
|
}));
|
|
1041
1295
|
});
|
|
1042
1296
|
const loadCheckpoint = Effect.fn("SqliteJournal.loadCheckpoint")(function* (threadId, atOrBeforeSequence) {
|
|
@@ -1051,8 +1305,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1051
1305
|
AND through_sequence <= ${atOrBeforeSequence}
|
|
1052
1306
|
ORDER BY through_sequence DESC
|
|
1053
1307
|
LIMIT 1
|
|
1054
|
-
`.pipe(Effect.mapError(storageError("load checkpoint")));
|
|
1055
|
-
return yield* decodeRows$
|
|
1308
|
+
`.pipe(Effect.mapError(storageError$1("load checkpoint")));
|
|
1309
|
+
return yield* decodeRows$3(Schema.Array(CheckpointRow), "effect_agent_checkpoints", `${threadId}<=${atOrBeforeSequence}`, rows);
|
|
1056
1310
|
});
|
|
1057
1311
|
return {
|
|
1058
1312
|
append,
|
|
@@ -1075,8 +1329,8 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1075
1329
|
FROM effect_agent_canonical_batches
|
|
1076
1330
|
WHERE thread_id = ${threadId}
|
|
1077
1331
|
AND last_sequence = ${sequence}
|
|
1078
|
-
`.pipe(Effect.mapError(storageError("read canonical digest at sequence")));
|
|
1079
|
-
return (yield* decodeRows$
|
|
1332
|
+
`.pipe(Effect.mapError(storageError$1("read canonical digest at sequence")));
|
|
1333
|
+
return (yield* decodeRows$3(Schema.Array(BatchRow), "effect_agent_canonical_batches", `${threadId}/${sequence}`, rows)).map((batch) => batch.tail_digest);
|
|
1080
1334
|
}),
|
|
1081
1335
|
loadCheckpoint,
|
|
1082
1336
|
materialize,
|
|
@@ -1093,7 +1347,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1093
1347
|
producer_epoch
|
|
1094
1348
|
FROM effect_agent_threads
|
|
1095
1349
|
ORDER BY thread_id
|
|
1096
|
-
`.pipe(Effect.mapError(storageError("scan threads")));
|
|
1350
|
+
`.pipe(Effect.mapError(storageError$1("scan threads")));
|
|
1097
1351
|
const batches = yield* sql`
|
|
1098
1352
|
SELECT
|
|
1099
1353
|
thread_id,
|
|
@@ -1105,7 +1359,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1105
1359
|
batch_json
|
|
1106
1360
|
FROM effect_agent_canonical_batches
|
|
1107
1361
|
ORDER BY thread_id, first_sequence
|
|
1108
|
-
`.pipe(Effect.mapError(storageError("scan canonical batches")));
|
|
1362
|
+
`.pipe(Effect.mapError(storageError$1("scan canonical batches")));
|
|
1109
1363
|
const records = yield* sql`
|
|
1110
1364
|
SELECT
|
|
1111
1365
|
thread_id,
|
|
@@ -1115,7 +1369,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1115
1369
|
record_json
|
|
1116
1370
|
FROM effect_agent_canonical_records
|
|
1117
1371
|
ORDER BY thread_id, sequence
|
|
1118
|
-
`.pipe(Effect.mapError(storageError("scan canonical records")));
|
|
1372
|
+
`.pipe(Effect.mapError(storageError$1("scan canonical records")));
|
|
1119
1373
|
const checkpoints = yield* sql`
|
|
1120
1374
|
SELECT
|
|
1121
1375
|
thread_id,
|
|
@@ -1124,12 +1378,12 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1124
1378
|
checkpoint_json
|
|
1125
1379
|
FROM effect_agent_checkpoints
|
|
1126
1380
|
ORDER BY thread_id, through_sequence
|
|
1127
|
-
`.pipe(Effect.mapError(storageError("scan checkpoints")));
|
|
1381
|
+
`.pipe(Effect.mapError(storageError$1("scan checkpoints")));
|
|
1128
1382
|
return {
|
|
1129
|
-
threads: yield* decodeRows$
|
|
1130
|
-
batches: yield* decodeRows$
|
|
1131
|
-
records: yield* decodeRows$
|
|
1132
|
-
checkpoints: yield* decodeRows$
|
|
1383
|
+
threads: yield* decodeRows$3(Schema.Array(ThreadRow), "effect_agent_threads", "startup_scan", threads),
|
|
1384
|
+
batches: yield* decodeRows$3(Schema.Array(BatchRow), "effect_agent_canonical_batches", "startup_scan", batches),
|
|
1385
|
+
records: yield* decodeRows$3(Schema.Array(RecordRow), "effect_agent_canonical_records", "startup_scan", records),
|
|
1386
|
+
checkpoints: yield* decodeRows$3(Schema.Array(CheckpointRow), "effect_agent_checkpoints", "startup_scan", checkpoints)
|
|
1133
1387
|
};
|
|
1134
1388
|
}));
|
|
1135
1389
|
}),
|
|
@@ -1138,7 +1392,7 @@ const initializeSqliteJournal = Effect.fn("SqliteJournal.initialize")(function*
|
|
|
1138
1392
|
});
|
|
1139
1393
|
//#endregion
|
|
1140
1394
|
//#region src/sqlite-thread-store.ts
|
|
1141
|
-
const OffsetText = Schema.String.check(Schema.isMaxLength(
|
|
1395
|
+
const OffsetText = Schema.String.check(Schema.isMaxLength(4096));
|
|
1142
1396
|
const SQLITE_OFFSET_PREFIX = "effect-agent-sqlite@1:";
|
|
1143
1397
|
const ZERO_CANONICAL_SEQUENCE = Schema.decodeSync(CanonicalSequence)(0);
|
|
1144
1398
|
const isDigest = Schema.is(Digest);
|
|
@@ -1541,7 +1795,7 @@ const layer = (options) => Layer.unwrap(Effect.map(SqliteStorageConfig, (config)
|
|
|
1541
1795
|
const observationOffsetAt = makeOffset;
|
|
1542
1796
|
//#endregion
|
|
1543
1797
|
//#region src/sqlite-ledger.ts
|
|
1544
|
-
const BoundedStoredText = Schema.String.check(Schema.isMaxLength(
|
|
1798
|
+
const BoundedStoredText = Schema.String.check(Schema.isMaxLength(16777216));
|
|
1545
1799
|
const BoundedIdentifier = Schema.NonEmptyString.check(Schema.isMaxLength(1024));
|
|
1546
1800
|
const BoundedTimestamp = Schema.NonEmptyString.check(Schema.isMaxLength(128));
|
|
1547
1801
|
const SCAN_PAGE_SIZE = 256;
|
|
@@ -1750,7 +2004,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
1750
2004
|
iso: new Date(millis).toISOString()
|
|
1751
2005
|
}));
|
|
1752
2006
|
const timestampMillis = (operation, rowKey) => (timestamp) => decodeUtcInstant(timestamp).pipe(Effect.map(DateTime.toEpochMillis), Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submission_ownership", rowKey, error.message)));
|
|
1753
|
-
const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows$
|
|
2007
|
+
const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows$3(Schema.Array(SubmissionRow), "effect_agent_submissions", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
1754
2008
|
const readSubmission = Effect.fn("SqliteSubmissionLedger.readSubmission")(function* (operation, submissionId) {
|
|
1755
2009
|
const rows = yield* sql`
|
|
1756
2010
|
SELECT ${sql.literal(SUBMISSION_COLUMNS)}
|
|
@@ -1781,7 +2035,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
1781
2035
|
FROM effect_agent_submission_ownership
|
|
1782
2036
|
WHERE submission_id = ${submissionId}
|
|
1783
2037
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1784
|
-
const decoded = yield* decodeRows$
|
|
2038
|
+
const decoded = yield* decodeRows$3(Schema.Array(OwnershipRow), "effect_agent_submission_ownership", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
1785
2039
|
if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_submission_ownership", submissionId, "An ownership primary key returned more than one row.");
|
|
1786
2040
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
1787
2041
|
});
|
|
@@ -1846,7 +2100,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
1846
2100
|
FROM effect_agent_settlement_reservations
|
|
1847
2101
|
WHERE submission_id = ${submissionId}
|
|
1848
2102
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1849
|
-
const decoded = yield* decodeRows$
|
|
2103
|
+
const decoded = yield* decodeRows$3(Schema.Array(ReservationRow), "effect_agent_settlement_reservations", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
1850
2104
|
if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_settlement_reservations", submissionId, "A settlement reservation primary key returned more than one row.");
|
|
1851
2105
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
1852
2106
|
});
|
|
@@ -1861,11 +2115,11 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
1861
2115
|
FROM effect_agent_abort_intents
|
|
1862
2116
|
WHERE submission_id = ${submissionId}
|
|
1863
2117
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1864
|
-
const decoded = yield* decodeRows$
|
|
2118
|
+
const decoded = yield* decodeRows$3(Schema.Array(AbortIntentRow), "effect_agent_abort_intents", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
1865
2119
|
if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_abort_intents", submissionId, "An abort intent primary key returned more than one row.");
|
|
1866
2120
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
1867
2121
|
});
|
|
1868
|
-
const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows$
|
|
2122
|
+
const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows$3(Schema.Array(ChildReservationRow), "effect_agent_child_reservations", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
1869
2123
|
const readChildReservation = Effect.fn("SqliteSubmissionLedger.readChildReservation")(function* (operation, reservationId) {
|
|
1870
2124
|
const rows = yield* sql`
|
|
1871
2125
|
SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
|
|
@@ -1918,7 +2172,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
1918
2172
|
WHERE submission_id = ${submissionId}
|
|
1919
2173
|
ORDER BY tool_call_id ASC
|
|
1920
2174
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1921
|
-
return yield* decodeRows$
|
|
2175
|
+
return yield* decodeRows$3(Schema.Array(ApprovalDecisionRow), "effect_agent_approval_decisions", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
1922
2176
|
});
|
|
1923
2177
|
const approvalIntentFromRow = Effect.fn("SqliteSubmissionLedger.approvalIntentFromRow")(function* (operation, row) {
|
|
1924
2178
|
return yield* decodeApprovalDecisionIntent({
|
|
@@ -1943,7 +2197,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
1943
2197
|
WHERE submission_id = ${submissionId}
|
|
1944
2198
|
ORDER BY tool_call_id ASC
|
|
1945
2199
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1946
|
-
return yield* decodeRows$
|
|
2200
|
+
return yield* decodeRows$3(Schema.Array(UnknownResolutionRow), "effect_agent_unknown_resolutions", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
1947
2201
|
});
|
|
1948
2202
|
const unknownResolutionIntentFromRow = Effect.fn("SqliteSubmissionLedger.unknownResolutionIntentFromRow")(function* (operation, row) {
|
|
1949
2203
|
const resolution = yield* parseStoredJsonText(row.resolution_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_unknown_resolutions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
|
|
@@ -1974,7 +2228,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
1974
2228
|
WHERE thread_id = ${threadId}
|
|
1975
2229
|
AND record_id = ${recordId}
|
|
1976
2230
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1977
|
-
return (yield* decodeRows$
|
|
2231
|
+
return (yield* decodeRows$3(Schema.Array(CanonicalRecordIdRow), "effect_agent_canonical_records", `${threadId}/${recordId}`, rows).pipe(Effect.mapError(internalFailure(operation)))).length === 0 ? void 0 : recordId;
|
|
1978
2232
|
});
|
|
1979
2233
|
const abortIntentFromRow = Effect.fn("SqliteSubmissionLedger.abortIntentFromRow")(function* (operation, submission, submissionId, row) {
|
|
1980
2234
|
const canonicalRecordId = yield* canonicalAbortRecordId(operation, submission.thread_id, submissionId);
|
|
@@ -2028,7 +2282,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2028
2282
|
FROM effect_agent_submissions
|
|
2029
2283
|
WHERE thread_id = ${validated.threadId}
|
|
2030
2284
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2031
|
-
const decodedMax = yield* decodeRows$
|
|
2285
|
+
const decodedMax = yield* decodeRows$3(Schema.Array(MaxQueueSequenceRow), "effect_agent_submissions", validated.threadId, maxRows).pipe(Effect.mapError(internalFailure(operation)));
|
|
2032
2286
|
const queueSequence = yield* decodeQueueSequence((decodedMax[0]?.max_queue_sequence ?? 0) + 1).pipe(Effect.mapError(internalFailure(operation)));
|
|
2033
2287
|
const now = yield* currentInstant;
|
|
2034
2288
|
yield* sql`
|
|
@@ -2395,7 +2649,8 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
|
|
|
2395
2649
|
submissionId: validated.submissionId,
|
|
2396
2650
|
existingOutcome: reservation.value.outcome
|
|
2397
2651
|
});
|
|
2398
|
-
const
|
|
2652
|
+
const reservationRecord = yield* decodeRecordEnvelopeText(reservation.value.record_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, error.message)));
|
|
2653
|
+
const settlementFailure = settlementFailureFromRecord(reservationRecord);
|
|
2399
2654
|
if (reservation.value.outcome === "failed" !== (settlementFailure !== void 0)) return yield* corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, "The reserved outcome and canonical failure diagnostic disagree.");
|
|
2400
2655
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
2401
2656
|
if (submission.state === "settled") {
|
|
@@ -3175,8 +3430,308 @@ const submissionLedgerLayer = Layer.effectContext(makeServices());
|
|
|
3175
3430
|
*/
|
|
3176
3431
|
const ledgerLayer = (options) => Layer.unwrap(Effect.map(SqliteStorageConfig, (config) => submissionLedgerLayer.pipe(Layer.provide(Layer.mergeAll(Layer.succeed(SqliteStorageConfig)(config), storageFailpointLayer(options), SqliteClient.layer({ filename: options.filename }), NodeCrypto.layer))))).pipe(Layer.provide(storageConfigLayer(options)));
|
|
3177
3432
|
//#endregion
|
|
3433
|
+
//#region src/sqlite-memory-store.ts
|
|
3434
|
+
const STORAGE_VERSION = 2;
|
|
3435
|
+
const METADATA_COMPONENT = "memory";
|
|
3436
|
+
const DOCUMENT_TABLE = "effect_agent_memory_documents_v1";
|
|
3437
|
+
const RECEIPT_TABLE = "effect_agent_memory_receipts_v1";
|
|
3438
|
+
const StoredJson$1 = Schema.String.check(Schema.isMaxLength(16777216));
|
|
3439
|
+
const EncodedMemoryChange = Schema.Struct({
|
|
3440
|
+
commandJson: StoredJson$1,
|
|
3441
|
+
documentJson: StoredJson$1,
|
|
3442
|
+
resultJson: StoredJson$1
|
|
3443
|
+
});
|
|
3444
|
+
const equivalentContent = Schema.toEquivalence(MemoryWrite.Wire.members[0].fields.content);
|
|
3445
|
+
const equivalentScopes = Schema.toEquivalence(MemoryWrite.Wire.members[0].fields.scopes);
|
|
3446
|
+
var MemoryMetadataRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryMetadataRow")({ version: Schema.Int }) {};
|
|
3447
|
+
var MemoryTableRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryTableRow")({ name: Schema.NonEmptyString }) {};
|
|
3448
|
+
var MemoryDocumentRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryDocumentRow")({
|
|
3449
|
+
namespace: MemoryNamespaceAddress,
|
|
3450
|
+
source_id: MemoryKey.Wire.fields.id,
|
|
3451
|
+
format_version: Schema.Int,
|
|
3452
|
+
generation: Schema.Int,
|
|
3453
|
+
revision: Schema.NonEmptyString,
|
|
3454
|
+
document_json: StoredJson$1
|
|
3455
|
+
}) {};
|
|
3456
|
+
var MemoryReceiptRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryReceiptRow")({
|
|
3457
|
+
namespace: MemoryNamespaceAddress,
|
|
3458
|
+
operation_id: MemoryWrite.Wire.members[0].fields.operationId,
|
|
3459
|
+
source_id: MemoryKey.Wire.fields.id,
|
|
3460
|
+
format_version: Schema.Int,
|
|
3461
|
+
command_json: StoredJson$1,
|
|
3462
|
+
result_json: StoredJson$1
|
|
3463
|
+
}) {};
|
|
3464
|
+
var MemoryChangeCountRow = class extends Schema.Class("@effect-agent/storage-sqlite/MemoryChangeCountRow")({ changed: Schema.Int }) {};
|
|
3465
|
+
var StoredMemoryCommand = class extends Schema.Class("@effect-agent/storage-sqlite/StoredMemoryCommand")({
|
|
3466
|
+
version: Schema.Literal(STORAGE_VERSION),
|
|
3467
|
+
value: MemoryWrite.Wire
|
|
3468
|
+
}) {};
|
|
3469
|
+
var StoredMemoryResult = class extends Schema.Class("@effect-agent/storage-sqlite/StoredMemoryResult")({
|
|
3470
|
+
version: Schema.Literal(STORAGE_VERSION),
|
|
3471
|
+
value: MemoryDocument.Wire
|
|
3472
|
+
}) {};
|
|
3473
|
+
const StoredVersionHeader = Schema.Struct({ version: Schema.Int });
|
|
3474
|
+
const storageError = (operation, reason = "unavailable") => MemoryStorageError.make({
|
|
3475
|
+
operation,
|
|
3476
|
+
reason
|
|
3477
|
+
});
|
|
3478
|
+
const query = (effect, operation) => effect.pipe(Effect.mapError(() => storageError(operation)));
|
|
3479
|
+
const decodeRows$2 = Effect.fn("SqliteMemoryStore.decodeRows")(function* (schema, rows, operation) {
|
|
3480
|
+
return yield* Schema.decodeUnknownEffect(Schema.Array(schema))(rows).pipe(Effect.mapError(() => storageError(operation, "corrupt")));
|
|
3481
|
+
});
|
|
3482
|
+
const decodeInput$1 = Effect.fn("SqliteMemoryStore.decodeInput")(function* (schema, value, operation) {
|
|
3483
|
+
return yield* Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => storageError(operation, "invalid-input")));
|
|
3484
|
+
});
|
|
3485
|
+
const encodeJson = Effect.fn("SqliteMemoryStore.encodeJson")(function* (schema, value, operation) {
|
|
3486
|
+
return yield* Schema.encodeEffect(Schema.fromJsonString(schema))(value).pipe(Effect.mapError(() => storageError(operation, "corrupt")));
|
|
3487
|
+
});
|
|
3488
|
+
const validateEncodedChange = Effect.fn("SqliteMemoryStore.validateEncodedChange")(function* (encoded, operation) {
|
|
3489
|
+
yield* Schema.decodeEffect(EncodedMemoryChange)(encoded).pipe(Effect.mapError(() => storageError(operation, "invalid-input")));
|
|
3490
|
+
});
|
|
3491
|
+
const decodeVersionedJson = Effect.fn("SqliteMemoryStore.decodeVersionedJson")(function* (schema, value, operation) {
|
|
3492
|
+
if ((yield* Schema.decodeEffect(Schema.fromJsonString(StoredVersionHeader))(value).pipe(Effect.mapError(() => storageError(operation, "corrupt")))).version !== STORAGE_VERSION) return yield* storageError(operation, "incompatible");
|
|
3493
|
+
const decoded = yield* Schema.decodeEffect(Schema.fromJsonString(schema))(value).pipe(Effect.mapError(() => storageError(operation, "corrupt")));
|
|
3494
|
+
if ((yield* encodeJson(schema, decoded, operation)) !== value) return yield* storageError(operation, "corrupt");
|
|
3495
|
+
return decoded;
|
|
3496
|
+
});
|
|
3497
|
+
const validateDocument = Effect.fn("SqliteMemoryStore.validateDocument")(function* (document, key, operation) {
|
|
3498
|
+
if (document.key.namespace.address !== key.namespace.address || document.key.id !== key.id || document.source.id !== key.id || document.source.revision !== String(document.generation) || document.generation === 1 !== (document.predecessor === null) || document.predecessor !== null && (document.predecessor.id !== key.id || document.predecessor.revision !== String(document.generation - 1))) return yield* storageError(operation, "corrupt");
|
|
3499
|
+
return document;
|
|
3500
|
+
});
|
|
3501
|
+
const validateReceiptResult = Effect.fn("SqliteMemoryStore.validateReceiptResult")(function* (command, result, operation) {
|
|
3502
|
+
if ((result.predecessor?.revision ?? null) !== command.expectedRevision) return yield* storageError(operation, "corrupt");
|
|
3503
|
+
if (command._tag === "Put") {
|
|
3504
|
+
if (result._tag !== "ActiveMemoryDocument" || result.source.locator !== command.locator) return yield* storageError(operation, "corrupt");
|
|
3505
|
+
if (!equivalentContent(command.content, result.content) || !equivalentScopes(command.scopes, result.scopes)) return yield* storageError(operation, "corrupt");
|
|
3506
|
+
return;
|
|
3507
|
+
}
|
|
3508
|
+
if (result._tag !== "WithdrawnMemoryDocument" || result.reason !== command.reason || result.predecessor === null || result.source.locator !== result.predecessor.locator) return yield* storageError(operation, "corrupt");
|
|
3509
|
+
});
|
|
3510
|
+
const initializeMemorySchema = Effect.fn("SqliteMemoryStore.initialize")(function* () {
|
|
3511
|
+
const sql = yield* SqlClient.SqlClient;
|
|
3512
|
+
const failpoint = yield* MemoryMutationFailpoint;
|
|
3513
|
+
yield* failpoint.hit("memory:initialize:before");
|
|
3514
|
+
yield* sql.withTransaction(Effect.gen(function* () {
|
|
3515
|
+
yield* sql`
|
|
3516
|
+
CREATE TABLE IF NOT EXISTS effect_agent_memory_metadata (
|
|
3517
|
+
component TEXT PRIMARY KEY NOT NULL,
|
|
3518
|
+
version INTEGER NOT NULL
|
|
3519
|
+
)
|
|
3520
|
+
`;
|
|
3521
|
+
const metadataRows = yield* sql`
|
|
3522
|
+
SELECT version
|
|
3523
|
+
FROM effect_agent_memory_metadata
|
|
3524
|
+
WHERE component = ${METADATA_COMPONENT}
|
|
3525
|
+
`;
|
|
3526
|
+
const metadata = yield* decodeRows$2(MemoryMetadataRow, metadataRows, "decode memory schema version");
|
|
3527
|
+
if (metadata.length > 1) return yield* storageError("decode memory schema version", "corrupt");
|
|
3528
|
+
const currentVersion = metadata[0]?.version;
|
|
3529
|
+
if (currentVersion !== void 0 && currentVersion !== STORAGE_VERSION) return yield* storageError("initialize memory schema", "incompatible");
|
|
3530
|
+
if (currentVersion === void 0) {
|
|
3531
|
+
const tableRows = yield* sql`
|
|
3532
|
+
SELECT name
|
|
3533
|
+
FROM sqlite_master
|
|
3534
|
+
WHERE type = 'table' AND name IN (${DOCUMENT_TABLE}, ${RECEIPT_TABLE})
|
|
3535
|
+
`;
|
|
3536
|
+
if ((yield* decodeRows$2(MemoryTableRow, tableRows, "inspect memory schema")).length > 0) return yield* storageError("initialize memory schema", "incompatible");
|
|
3537
|
+
yield* sql`
|
|
3538
|
+
CREATE TABLE effect_agent_memory_documents_v1 (
|
|
3539
|
+
namespace TEXT NOT NULL,
|
|
3540
|
+
source_id TEXT NOT NULL,
|
|
3541
|
+
format_version INTEGER NOT NULL,
|
|
3542
|
+
generation INTEGER NOT NULL,
|
|
3543
|
+
revision TEXT NOT NULL,
|
|
3544
|
+
document_json TEXT NOT NULL,
|
|
3545
|
+
PRIMARY KEY (namespace, source_id)
|
|
3546
|
+
)
|
|
3547
|
+
`;
|
|
3548
|
+
yield* sql`
|
|
3549
|
+
CREATE TABLE effect_agent_memory_receipts_v1 (
|
|
3550
|
+
namespace TEXT NOT NULL,
|
|
3551
|
+
operation_id TEXT NOT NULL,
|
|
3552
|
+
source_id TEXT NOT NULL,
|
|
3553
|
+
format_version INTEGER NOT NULL,
|
|
3554
|
+
command_json TEXT NOT NULL,
|
|
3555
|
+
result_json TEXT NOT NULL,
|
|
3556
|
+
PRIMARY KEY (namespace, operation_id)
|
|
3557
|
+
)
|
|
3558
|
+
`;
|
|
3559
|
+
yield* sql`
|
|
3560
|
+
INSERT INTO effect_agent_memory_metadata (component, version)
|
|
3561
|
+
VALUES (${METADATA_COMPONENT}, ${STORAGE_VERSION})
|
|
3562
|
+
`;
|
|
3563
|
+
}
|
|
3564
|
+
yield* sql`
|
|
3565
|
+
SELECT namespace, source_id, format_version, generation, revision, document_json
|
|
3566
|
+
FROM effect_agent_memory_documents_v1
|
|
3567
|
+
LIMIT 0
|
|
3568
|
+
`;
|
|
3569
|
+
yield* sql`
|
|
3570
|
+
SELECT namespace, operation_id, source_id, format_version, command_json, result_json
|
|
3571
|
+
FROM effect_agent_memory_receipts_v1
|
|
3572
|
+
LIMIT 0
|
|
3573
|
+
`;
|
|
3574
|
+
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storageError("initialize memory schema"))));
|
|
3575
|
+
yield* failpoint.hit("memory:initialize:after");
|
|
3576
|
+
});
|
|
3577
|
+
const makeMemoryReader = Effect.fn("SqliteMemoryStore.makeReader")(function* () {
|
|
3578
|
+
const sql = yield* SqlClient.SqlClient;
|
|
3579
|
+
const readDocument = Effect.fn("SqliteMemoryStore.readDocument")(function* (key, operation) {
|
|
3580
|
+
const rawRows = yield* query(sql`
|
|
3581
|
+
SELECT namespace, source_id, format_version, generation, revision, document_json
|
|
3582
|
+
FROM effect_agent_memory_documents_v1
|
|
3583
|
+
WHERE namespace = ${key.namespace.address} AND source_id = ${key.id}
|
|
3584
|
+
`, operation);
|
|
3585
|
+
const rows = yield* decodeRows$2(MemoryDocumentRow, rawRows, operation);
|
|
3586
|
+
if (rows.length === 0) return null;
|
|
3587
|
+
if (rows.length !== 1) return yield* storageError(operation, "corrupt");
|
|
3588
|
+
const row = rows[0];
|
|
3589
|
+
if (row.format_version !== STORAGE_VERSION) return yield* storageError(operation, "incompatible");
|
|
3590
|
+
const document = (yield* decodeVersionedJson(StoredMemoryResult, row.document_json, operation)).value;
|
|
3591
|
+
yield* validateDocument(document, key, operation);
|
|
3592
|
+
if (row.namespace !== key.namespace.address || row.source_id !== key.id || row.generation !== document.generation || row.revision !== document.source.revision) return yield* storageError(operation, "corrupt");
|
|
3593
|
+
return document;
|
|
3594
|
+
});
|
|
3595
|
+
return {
|
|
3596
|
+
get: Effect.fn("SqliteMemoryStore.get")(function* (key) {
|
|
3597
|
+
const decodedKey = yield* decodeInput$1(MemoryKey.Wire, key, "get memory document");
|
|
3598
|
+
return yield* readDocument(decodedKey, "get memory document");
|
|
3599
|
+
}),
|
|
3600
|
+
readDocument
|
|
3601
|
+
};
|
|
3602
|
+
});
|
|
3603
|
+
const makeMemoryServices = Effect.fn("SqliteMemoryStore.make")(function* () {
|
|
3604
|
+
const sql = yield* SqlClient.SqlClient;
|
|
3605
|
+
const failpoint = yield* MemoryMutationFailpoint;
|
|
3606
|
+
yield* initializeMemorySchema();
|
|
3607
|
+
const { get, readDocument } = yield* makeMemoryReader();
|
|
3608
|
+
const readReceipt = Effect.fn("SqliteMemoryStore.readReceipt")(function* (write, operation) {
|
|
3609
|
+
const rawRows = yield* query(sql`
|
|
3610
|
+
SELECT namespace, operation_id, source_id, format_version, command_json, result_json
|
|
3611
|
+
FROM effect_agent_memory_receipts_v1
|
|
3612
|
+
WHERE namespace = ${write.key.namespace.address} AND operation_id = ${write.operationId}
|
|
3613
|
+
`, operation);
|
|
3614
|
+
const rows = yield* decodeRows$2(MemoryReceiptRow, rawRows, operation);
|
|
3615
|
+
if (rows.length === 0) return null;
|
|
3616
|
+
if (rows.length !== 1) return yield* storageError(operation, "corrupt");
|
|
3617
|
+
const row = rows[0];
|
|
3618
|
+
if (row.format_version !== STORAGE_VERSION) return yield* storageError(operation, "incompatible");
|
|
3619
|
+
const command = yield* decodeVersionedJson(StoredMemoryCommand, row.command_json, `${operation} command`);
|
|
3620
|
+
const result = yield* decodeVersionedJson(StoredMemoryResult, row.result_json, `${operation} result`);
|
|
3621
|
+
if (row.namespace !== command.value.key.namespace.address || row.operation_id !== command.value.operationId || row.source_id !== command.value.key.id || result.value.key.namespace.address !== command.value.key.namespace.address || result.value.key.id !== command.value.key.id) return yield* storageError(operation, "corrupt");
|
|
3622
|
+
yield* validateDocument(result.value, command.value.key, operation);
|
|
3623
|
+
yield* validateReceiptResult(command.value, result.value, operation);
|
|
3624
|
+
return {
|
|
3625
|
+
commandJson: row.command_json,
|
|
3626
|
+
result: result.value
|
|
3627
|
+
};
|
|
3628
|
+
});
|
|
3629
|
+
const change = Effect.fn("SqliteMemoryStore.change")(function* (write) {
|
|
3630
|
+
const operation = "change memory document";
|
|
3631
|
+
const decodedWrite = yield* decodeInput$1(MemoryWrite.Wire, write, operation);
|
|
3632
|
+
const commandJson = yield* encodeJson(StoredMemoryCommand, StoredMemoryCommand.make({
|
|
3633
|
+
version: STORAGE_VERSION,
|
|
3634
|
+
value: decodedWrite
|
|
3635
|
+
}), "encode memory command");
|
|
3636
|
+
yield* failpoint.hit("memory:change:before");
|
|
3637
|
+
const transactionResult = yield* sql.withTransaction(Effect.gen(function* () {
|
|
3638
|
+
const receipt = yield* readReceipt(decodedWrite, operation);
|
|
3639
|
+
if (receipt !== null) {
|
|
3640
|
+
if (receipt.commandJson !== commandJson) return yield* MemoryOperationConflict.make({
|
|
3641
|
+
key: decodedWrite.key,
|
|
3642
|
+
operationId: decodedWrite.operationId
|
|
3643
|
+
});
|
|
3644
|
+
return {
|
|
3645
|
+
document: receipt.result,
|
|
3646
|
+
changed: false
|
|
3647
|
+
};
|
|
3648
|
+
}
|
|
3649
|
+
const current = yield* readDocument(decodedWrite.key, operation);
|
|
3650
|
+
const modifiedAt = yield* Clock.currentTimeMillis;
|
|
3651
|
+
const next = yield* applyMemoryWrite(current, decodedWrite, modifiedAt);
|
|
3652
|
+
const resultJson = yield* encodeJson(StoredMemoryResult, StoredMemoryResult.make({
|
|
3653
|
+
version: STORAGE_VERSION,
|
|
3654
|
+
value: next
|
|
3655
|
+
}), "encode memory result");
|
|
3656
|
+
const documentJson = resultJson;
|
|
3657
|
+
yield* validateEncodedChange({
|
|
3658
|
+
commandJson,
|
|
3659
|
+
documentJson,
|
|
3660
|
+
resultJson
|
|
3661
|
+
}, operation);
|
|
3662
|
+
if (current === null) yield* sql`
|
|
3663
|
+
INSERT INTO effect_agent_memory_documents_v1 (
|
|
3664
|
+
namespace, source_id, format_version, generation, revision, document_json
|
|
3665
|
+
) VALUES (
|
|
3666
|
+
${next.key.namespace.address}, ${next.key.id}, ${STORAGE_VERSION},
|
|
3667
|
+
${next.generation}, ${next.source.revision}, ${documentJson}
|
|
3668
|
+
)
|
|
3669
|
+
`;
|
|
3670
|
+
else yield* sql`
|
|
3671
|
+
UPDATE effect_agent_memory_documents_v1
|
|
3672
|
+
SET format_version = ${STORAGE_VERSION},
|
|
3673
|
+
generation = ${next.generation},
|
|
3674
|
+
revision = ${next.source.revision},
|
|
3675
|
+
document_json = ${documentJson}
|
|
3676
|
+
WHERE namespace = ${next.key.namespace.address}
|
|
3677
|
+
AND source_id = ${next.key.id}
|
|
3678
|
+
AND generation = ${current.generation}
|
|
3679
|
+
AND revision = ${current.source.revision}
|
|
3680
|
+
`;
|
|
3681
|
+
const changedRows = yield* sql`
|
|
3682
|
+
SELECT changes() AS changed
|
|
3683
|
+
`;
|
|
3684
|
+
const changed = yield* decodeRows$2(MemoryChangeCountRow, changedRows, operation);
|
|
3685
|
+
if (changed.length !== 1 || changed[0].changed !== 1) return yield* storageError(operation, "corrupt");
|
|
3686
|
+
yield* failpoint.hit("memory:change:after-state");
|
|
3687
|
+
yield* sql`
|
|
3688
|
+
INSERT INTO effect_agent_memory_receipts_v1 (
|
|
3689
|
+
namespace, operation_id, source_id, format_version, command_json, result_json
|
|
3690
|
+
) VALUES (
|
|
3691
|
+
${decodedWrite.key.namespace.address}, ${decodedWrite.operationId}, ${decodedWrite.key.id},
|
|
3692
|
+
${STORAGE_VERSION}, ${commandJson}, ${resultJson}
|
|
3693
|
+
)
|
|
3694
|
+
`;
|
|
3695
|
+
yield* failpoint.hit("memory:change:after-receipt");
|
|
3696
|
+
return {
|
|
3697
|
+
document: next,
|
|
3698
|
+
changed: true
|
|
3699
|
+
};
|
|
3700
|
+
})).pipe(Effect.catchTag("SqlError", () => Effect.fail(storageError(operation))));
|
|
3701
|
+
if (transactionResult.changed) yield* failpoint.hit("memory:change:after");
|
|
3702
|
+
return transactionResult.document;
|
|
3703
|
+
});
|
|
3704
|
+
return Context.make(MemoryReader, MemoryReader.fromAdapter({ get })).pipe(Context.add(MemoryWriter, MemoryWriter.fromAdapter({ change })));
|
|
3705
|
+
});
|
|
3706
|
+
/** SQLite memory ports with the mutation failpoint kept injectable for recovery tests. */
|
|
3707
|
+
const memoryStoreLayerWithFailpoints = Layer.effectContext(makeMemoryServices());
|
|
3708
|
+
/** SQLite memory reader and writer with the production no-op mutation failpoint. */
|
|
3709
|
+
const memoryStoreLayer = memoryStoreLayerWithFailpoints.pipe(Layer.provide(MemoryMutationFailpoint.layer));
|
|
3710
|
+
/** Reads an existing memory schema without writes, transactions, or mutation failpoints. */
|
|
3711
|
+
const memoryReaderLayer = Layer.effect(MemoryReader, Effect.gen(function* () {
|
|
3712
|
+
const sql = yield* SqlClient.SqlClient;
|
|
3713
|
+
const operation = "open memory reader";
|
|
3714
|
+
const tables = yield* query(sql`
|
|
3715
|
+
SELECT name FROM sqlite_master
|
|
3716
|
+
WHERE type = 'table' AND name IN (
|
|
3717
|
+
'effect_agent_memory_metadata', ${DOCUMENT_TABLE}, ${RECEIPT_TABLE}
|
|
3718
|
+
)
|
|
3719
|
+
`, operation).pipe(Effect.flatMap((rows) => decodeRows$2(MemoryTableRow, rows, operation)));
|
|
3720
|
+
if (tables.length !== 3) return yield* storageError(operation, tables.length === 0 ? "unavailable" : "incompatible");
|
|
3721
|
+
const metadata = yield* query(sql`
|
|
3722
|
+
SELECT version FROM effect_agent_memory_metadata WHERE component = ${METADATA_COMPONENT}
|
|
3723
|
+
`, operation).pipe(Effect.flatMap((rows) => decodeRows$2(MemoryMetadataRow, rows, operation)));
|
|
3724
|
+
if (metadata.length !== 1 || metadata[0].version !== STORAGE_VERSION) return yield* storageError(operation, "incompatible");
|
|
3725
|
+
yield* query(sql`
|
|
3726
|
+
SELECT namespace, source_id, format_version, generation, revision, document_json
|
|
3727
|
+
FROM effect_agent_memory_documents_v1 LIMIT 0
|
|
3728
|
+
`, operation);
|
|
3729
|
+
const { get } = yield* makeMemoryReader();
|
|
3730
|
+
return MemoryReader.fromAdapter({ get });
|
|
3731
|
+
}));
|
|
3732
|
+
//#endregion
|
|
3178
3733
|
//#region src/sqlite-schedule-store.ts
|
|
3179
|
-
const StoredScheduleJson = Schema.String.check(Schema.isMaxLength(
|
|
3734
|
+
const StoredScheduleJson = Schema.String.check(Schema.isMaxLength(16777216));
|
|
3180
3735
|
const StoredDeadline = Schema.NullOr(ScheduleInstant);
|
|
3181
3736
|
var ScheduleRow = class extends Schema.Class("@effect-agent/storage-sqlite/ScheduleRow")({
|
|
3182
3737
|
tenant_id: ScheduleOwner.fields.tenantId,
|
|
@@ -3421,7 +3976,7 @@ const makeScheduleStore = Effect.gen(function* () {
|
|
|
3421
3976
|
const scheduleStoreLayer = Layer.effect(ScheduleStore)(makeScheduleStore);
|
|
3422
3977
|
//#endregion
|
|
3423
3978
|
//#region src/sqlite-subscription-store.ts
|
|
3424
|
-
const StoredJson = Schema.String.check(Schema.isMaxLength(
|
|
3979
|
+
const StoredJson = Schema.String.check(Schema.isMaxLength(16777216));
|
|
3425
3980
|
const CountRow = Schema.Struct({ count: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)) });
|
|
3426
3981
|
const SequenceRow = Schema.Struct({ sequence: Schema.Natural });
|
|
3427
3982
|
const ScanRow = Schema.Struct({
|
|
@@ -3814,7 +4369,8 @@ const makeSubscriptionStore = Effect.fn("SqliteSubscriptionStore.make")(function
|
|
|
3814
4369
|
return false;
|
|
3815
4370
|
}
|
|
3816
4371
|
yield* failpoint.hit("subscription:catch-up:before");
|
|
3817
|
-
|
|
4372
|
+
const effectiveNowMillis = Math.max(nowMillis, yield* Clock.currentTimeMillis);
|
|
4373
|
+
if (!subscriptionCanSelect(record, accepted, effectiveNowMillis, true)) return yield* error("conflict", "catch-up-eligibility");
|
|
3818
4374
|
if ((yield* count(sql`SELECT COUNT(*) AS count FROM effect_agent_subscription_deliveries WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}`, "count deliveries")) >= limits.maxDeliveries) return yield* error("capacity", "deliveries");
|
|
3819
4375
|
if ((yield* count(sql`SELECT COUNT(*) AS count FROM effect_agent_subscription_deliveries WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND owner_id=${record.key.ownerId}`, "count owner deliveries")) >= limits.maxDeliveriesPerOwner) return yield* error("capacity", "owner-deliveries");
|
|
3820
4376
|
yield* insertDelivery(delivery);
|
|
@@ -4004,6 +4560,6 @@ const makeSubscriptionStore = Effect.fn("SqliteSubscriptionStore.make")(function
|
|
|
4004
4560
|
});
|
|
4005
4561
|
const subscriptionStoreLayer = (partition) => Layer.effect(SubscriptionStore, makeSubscriptionStore(partition));
|
|
4006
4562
|
//#endregion
|
|
4007
|
-
export { CurrentSqliteStorageVersion, SqliteAppendConflict, SqliteCheckpointConflict, SqliteFenceRejected, SqliteLedgerError, SqliteStorageCompatibilityError, SqliteStorageConfig, SqliteStorageConfigValue, SqliteStorageCorruptionError, SqliteStorageError, SqliteStorageFailpoint, SqliteStorageFailpointError, SqliteStorageFailpointLocation, SqliteWriteContention, layer, ledgerLayer, observationOffsetAt, scheduleStoreLayer, storageConfigLayer, storageFailpointLayer, submissionLedgerLayer, subscriptionStoreLayer, threadStoreLayer };
|
|
4563
|
+
export { CurrentSqliteStorageVersion, SqliteAppendConflict, SqliteCheckpointConflict, SqliteFenceRejected, SqliteLedgerError, SqliteStorageCompatibilityError, SqliteStorageConfig, SqliteStorageConfigValue, SqliteStorageCorruptionError, SqliteStorageError, SqliteStorageFailpoint, SqliteStorageFailpointError, SqliteStorageFailpointLocation, SqliteWriteContention, activityProcessorStoreLayer, activityProcessorStoreLayerWithFailpoints, layer, ledgerLayer, memoryReaderLayer, memoryStoreLayer, memoryStoreLayerWithFailpoints, observationOffsetAt, scheduleStoreLayer, storageConfigLayer, storageFailpointLayer, submissionLedgerLayer, subscriptionStoreLayer, threadStoreLayer };
|
|
4008
4564
|
|
|
4009
4565
|
//# sourceMappingURL=index.mjs.map
|