@effect-agent/storage-cloudflare 0.1.0-beta.36 → 0.1.0-beta.38

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.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { AbortCommand, AbortIntent, AdmissionAdmitted, AdmissionConflict, AdmissionIndeterminate, AdmissionNotAdmitted, AdmissionRequest, AdmissionResolution, AdmissionResult, AppendConflict, AppendResult, ApprovalConflict, ApprovalDecision, ApprovalDecisionCommand, ApprovalDecisionIntent, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, CanonicalBatch, CanonicalRecord, CanonicalRecordEnvelope, CanonicalSequence, CheckpointRejected, ChildAttachmentSnapshot, ChildBudgetReservationRequest, ChildBudgetReservationSnapshot, ChildReservationConflict, ChildReservationStatus, ChildSettledNotification, ChildSettledOutcome, Claim, ClaimJoiningRequest, ClaimRequest, ConversationCheckpoint, ConversationExport, ConversationExportRequest, ConversationMaterialization, ConversationNotMaterialized, ConversationObservation, ConversationRead, ConversationStore, ConversationStoreError, ConversationTail, ConversationTailRequest, DEFAULT_OWNERSHIP_LEASE_DURATION, DefinitionDigests, 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, Settlement, SettlementConflict, SettlementFinalization, SettlementOutcome, SettlementReservation, SettlementReservationSnapshot, SubmissionLedger, SubmissionLookup, SubmissionLookupById, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SuspendRequest, SuspensionReason, SuspensionSnapshot, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, digestCanonicalBatch, settlementFailureFromRecord, submissionAbortRecordId } from "@effect-agent/session";
2
- import { Clock, Context, Crypto, DateTime, Duration, Effect, Layer, Option, Predicate, Ref, Schema, Stream } from "effect";
1
+ import { AbortCommand, AbortIntent, AdmissionAdmitted, AdmissionConflict, AdmissionIndeterminate, AdmissionNotAdmitted, AdmissionRequest, AdmissionResolution, AdmissionResult, AppendConflict, AppendResult, ApprovalConflict, ApprovalDecision, ApprovalDecisionCommand, ApprovalDecisionIntent, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, CanonicalBatch, CanonicalRecord, CanonicalRecordEnvelope, CanonicalSequence, CheckpointRejected, ChildAttachmentSnapshot, ChildBudgetReservationRequest, ChildBudgetReservationSnapshot, ChildReservationConflict, ChildReservationStatus, ChildSettledNotification, ChildSettledOutcome, Claim, ClaimJoiningRequest, ClaimRequest, ConversationCheckpoint, ConversationExport, ConversationExportRequest, ConversationMaterialization, ConversationNotMaterialized, ConversationObservation, ConversationRead, ConversationStore, ConversationStoreError, ConversationTail, ConversationTailRequest, DEFAULT_OWNERSHIP_LEASE_DURATION, DefinitionDigests, 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, SubmissionLedger, SubmissionLookup, SubmissionLookupById, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SuspendRequest, SuspensionReason, SuspensionSnapshot, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, applyScheduleChange, defaultSchedulingLimits, digestCanonicalBatch, scheduleDeadline, scheduleUsesCapacity, settlementFailureFromRecord, submissionAbortRecordId } from "@effect-agent/session";
2
+ import { Clock, Context, Crypto, DateTime, Duration, Effect, Layer, Option, Predicate, Ref, Result, Schema, Stream } from "effect";
3
3
  import { SqliteClient, SqliteMigrator } from "@effect/sql-sqlite-do";
4
4
  import * as SqlClient from "effect/unstable/sql/SqlClient";
5
5
  import { BrowserCrypto } from "@effect/platform-browser";
@@ -477,13 +477,13 @@ const storageError = (operation) => (error) => DoStorageError.make({
477
477
  message: error.message
478
478
  });
479
479
  /** Decode raw Durable Object SQLite rows against a Schema, reporting failures as typed corruption. */
480
- const decodeRows = Effect.fn((schema, table, rowKey, rows) => Schema.decodeUnknownEffect(schema)(rows).pipe(Effect.mapError((error) => DoStorageCorruptionError.make({
480
+ const decodeRows$1 = Effect.fn((schema, table, rowKey, rows) => Schema.decodeUnknownEffect(schema)(rows).pipe(Effect.mapError((error) => DoStorageCorruptionError.make({
481
481
  table,
482
482
  rowKey,
483
483
  message: String(error)
484
484
  }))));
485
485
  /** Decode exactly one raw row against a Schema, reporting failures as typed corruption. */
486
- const decodeSingleRow = Effect.fn((schema, table, rowKey, rows) => decodeRows(schema, table, rowKey, rows).pipe(Effect.flatMap((decoded) => decoded.length === 1 ? Effect.succeed(decoded[0]) : Effect.fail(DoStorageCorruptionError.make({
486
+ const decodeSingleRow = Effect.fn((schema, table, rowKey, rows) => decodeRows$1(schema, table, rowKey, rows).pipe(Effect.flatMap((decoded) => decoded.length === 1 ? Effect.succeed(decoded[0]) : Effect.fail(DoStorageCorruptionError.make({
487
487
  table,
488
488
  rowKey,
489
489
  message: `Expected exactly one row but found ${decoded.length}.`
@@ -518,7 +518,7 @@ const ensureCurrentStorage = Effect.fn("DoJournal.ensureCurrentStorage")(functio
518
518
  WHERE type = 'table'
519
519
  AND name = 'effect_agent_meta'
520
520
  `.pipe(Effect.mapError(storageError("read storage version table")));
521
- if ((yield* decodeRows(Schema.Array(DoNameRow), "sqlite_master", "effect_agent_meta", metaTableRows)).length === 0) {
521
+ if ((yield* decodeRows$1(Schema.Array(DoNameRow), "sqlite_master", "effect_agent_meta", metaTableRows)).length === 0) {
522
522
  const existingRows = yield* sql`
523
523
  SELECT name
524
524
  FROM sqlite_master
@@ -526,7 +526,7 @@ const ensureCurrentStorage = Effect.fn("DoJournal.ensureCurrentStorage")(functio
526
526
  AND name LIKE 'effect_agent_%'
527
527
  ORDER BY name
528
528
  `.pipe(Effect.mapError(storageError("inspect unversioned storage")));
529
- if ((yield* decodeRows(Schema.Array(DoNameRow), "sqlite_master", "effect_agent_%", existingRows)).length > 0) return yield* DoStorageCompatibilityError.make({
529
+ if ((yield* decodeRows$1(Schema.Array(DoNameRow), "sqlite_master", "effect_agent_%", existingRows)).length > 0) return yield* DoStorageCompatibilityError.make({
530
530
  actualVersion: 0,
531
531
  supportedVersion: 1,
532
532
  message: "The Durable Object contains unversioned Effect Agent tables. Reset the development namespace explicitly; refusing to mutate ambiguous stored data."
@@ -559,7 +559,7 @@ const ensureCurrentStorage = Effect.fn("DoJournal.ensureCurrentStorage")(functio
559
559
  AND name IN ${sql.in([...REQUIRED_TABLES])}
560
560
  ORDER BY name
561
561
  `.pipe(Effect.mapError(storageError("verify storage tables")));
562
- if ((yield* decodeRows(Schema.Array(DoNameRow), "sqlite_master", "required_tables", requiredRows)).length !== REQUIRED_TABLES.length) return yield* DoStorageCompatibilityError.make({
562
+ if ((yield* decodeRows$1(Schema.Array(DoNameRow), "sqlite_master", "required_tables", requiredRows)).length !== REQUIRED_TABLES.length) return yield* DoStorageCompatibilityError.make({
563
563
  actualVersion: 1,
564
564
  supportedVersion: 1,
565
565
  message: "The Durable Object claims the current format but is missing required tables. Reset the corrupt private-development data."
@@ -606,7 +606,7 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
606
606
  FROM effect_agent_conversations
607
607
  WHERE conversation_id = ${conversationId}
608
608
  `.pipe(Effect.mapError(storageError("read materialized conversation")));
609
- const existing = yield* decodeRows(Schema.Array(ConversationRow), "effect_agent_conversations", conversationId, existingRows);
609
+ const existing = yield* decodeRows$1(Schema.Array(ConversationRow), "effect_agent_conversations", conversationId, existingRows);
610
610
  if (existing.length > 1) return yield* DoStorageCorruptionError.make({
611
611
  table: "effect_agent_conversations",
612
612
  rowKey: conversationId,
@@ -653,7 +653,7 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
653
653
  FROM effect_agent_conversations
654
654
  WHERE conversation_id = ${conversationId}
655
655
  `.pipe(Effect.mapError(storageError("read conversation")));
656
- return yield* decodeRows(Schema.Array(ConversationRow), "effect_agent_conversations", conversationId, rows);
656
+ return yield* decodeRows$1(Schema.Array(ConversationRow), "effect_agent_conversations", conversationId, rows);
657
657
  });
658
658
  const append = Effect.fn("DoJournal.append")(function* (request) {
659
659
  if (request.conversationId.length > MAX_IDENTIFIER_LENGTH$1 || request.batchId.length > MAX_IDENTIFIER_LENGTH$1 || request.records.some((record) => record.recordId.length > MAX_IDENTIFIER_LENGTH$1)) return yield* DoStorageError.make({
@@ -699,7 +699,7 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
699
699
  WHERE conversation_id = ${request.conversationId}
700
700
  AND batch_id = ${request.batchId}
701
701
  `.pipe(Effect.mapError(storageError("read idempotent batch")));
702
- const batches = yield* decodeRows(Schema.Array(BatchRow), "effect_agent_canonical_batches", `${request.conversationId}/${request.batchId}`, batchRows);
702
+ const batches = yield* decodeRows$1(Schema.Array(BatchRow), "effect_agent_canonical_batches", `${request.conversationId}/${request.batchId}`, batchRows);
703
703
  if (batches.length > 1) return yield* DoStorageCorruptionError.make({
704
704
  table: "effect_agent_canonical_batches",
705
705
  rowKey: `${request.conversationId}/${request.batchId}`,
@@ -742,7 +742,7 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
742
742
  AND record_id IN ${sql.in([...chunk])}
743
743
  ORDER BY sequence
744
744
  `.pipe(Effect.mapError(storageError("check canonical record identities")));
745
- existingRecords.push(...yield* decodeRows(Schema.Array(RecordRow), "effect_agent_canonical_records", `${request.conversationId}/record_ids`, existingRecordRows));
745
+ existingRecords.push(...yield* decodeRows$1(Schema.Array(RecordRow), "effect_agent_canonical_records", `${request.conversationId}/record_ids`, existingRecordRows));
746
746
  }
747
747
  if (existingRecords.length > 0) return yield* DoAppendConflict.make({
748
748
  message: `Canonical record ID ${existingRecords[0].record_id} already exists.`,
@@ -827,7 +827,7 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
827
827
  ORDER BY sequence
828
828
  LIMIT ${request.limit}
829
829
  `.pipe(Effect.mapError(storageError("read canonical records")));
830
- return yield* decodeRows(Schema.Array(RecordRow), "effect_agent_canonical_records", `${request.conversationId}>${request.fromSequenceExclusive}`, rows);
830
+ return yield* decodeRows$1(Schema.Array(RecordRow), "effect_agent_canonical_records", `${request.conversationId}>${request.fromSequenceExclusive}`, rows);
831
831
  });
832
832
  const exportConversation = Effect.fn("DoJournal.exportConversation")(function* (conversationId) {
833
833
  return yield* sql.withTransaction(Effect.gen(function* () {
@@ -879,9 +879,9 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
879
879
  `.pipe(Effect.mapError(storageError("export checkpoints")));
880
880
  return RawConversationExport.make({
881
881
  conversation,
882
- batches: yield* decodeRows(Schema.Array(BatchRow), "effect_agent_canonical_batches", conversationId, batchRows),
883
- records: yield* decodeRows(Schema.Array(RecordRow), "effect_agent_canonical_records", conversationId, recordRows),
884
- checkpoints: yield* decodeRows(Schema.Array(CheckpointRow), "effect_agent_checkpoints", conversationId, checkpointRows)
882
+ batches: yield* decodeRows$1(Schema.Array(BatchRow), "effect_agent_canonical_batches", conversationId, batchRows),
883
+ records: yield* decodeRows$1(Schema.Array(RecordRow), "effect_agent_canonical_records", conversationId, recordRows),
884
+ checkpoints: yield* decodeRows$1(Schema.Array(CheckpointRow), "effect_agent_checkpoints", conversationId, checkpointRows)
885
885
  });
886
886
  })).pipe(Effect.catchTag("SqlError", (error) => Effect.fail(storageError("export transaction")(error))));
887
887
  });
@@ -914,7 +914,7 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
914
914
  WHERE conversation_id = ${checkpoint.conversationId}
915
915
  AND through_sequence = ${checkpoint.throughSequence}
916
916
  `.pipe(Effect.mapError(storageError("read idempotent checkpoint")));
917
- const existing = yield* decodeRows(Schema.Array(CheckpointRow), "effect_agent_checkpoints", `${checkpoint.conversationId}/${checkpoint.throughSequence}`, checkpointRows);
917
+ const existing = yield* decodeRows$1(Schema.Array(CheckpointRow), "effect_agent_checkpoints", `${checkpoint.conversationId}/${checkpoint.throughSequence}`, checkpointRows);
918
918
  if (existing.length > 1) return yield* DoStorageCorruptionError.make({
919
919
  table: "effect_agent_checkpoints",
920
920
  rowKey: `${checkpoint.conversationId}/${checkpoint.throughSequence}`,
@@ -952,7 +952,7 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
952
952
  ORDER BY through_sequence DESC
953
953
  LIMIT 1
954
954
  `.pipe(Effect.mapError(storageError("load checkpoint")));
955
- return yield* decodeRows(Schema.Array(CheckpointRow), "effect_agent_checkpoints", `${conversationId}<=${atOrBeforeSequence}`, rows);
955
+ return yield* decodeRows$1(Schema.Array(CheckpointRow), "effect_agent_checkpoints", `${conversationId}<=${atOrBeforeSequence}`, rows);
956
956
  });
957
957
  return {
958
958
  append,
@@ -977,7 +977,7 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
977
977
  WHERE conversation_id = ${conversationId}
978
978
  AND last_sequence = ${sequence}
979
979
  `.pipe(Effect.mapError(storageError("read canonical digest at sequence")));
980
- return (yield* decodeRows(Schema.Array(BatchRow), "effect_agent_canonical_batches", `${conversationId}/${sequence}`, rows)).map((batch) => batch.tail_digest);
980
+ return (yield* decodeRows$1(Schema.Array(BatchRow), "effect_agent_canonical_batches", `${conversationId}/${sequence}`, rows)).map((batch) => batch.tail_digest);
981
981
  }),
982
982
  loadCheckpoint,
983
983
  materialize,
@@ -1027,10 +1027,10 @@ const makeJournal = (sql, failpoint, maxStoredValueBytes) => {
1027
1027
  ORDER BY conversation_id, through_sequence
1028
1028
  `.pipe(Effect.mapError(storageError("scan checkpoints")));
1029
1029
  return {
1030
- conversations: yield* decodeRows(Schema.Array(ConversationRow), "effect_agent_conversations", "startup_scan", conversations),
1031
- batches: yield* decodeRows(Schema.Array(BatchRow), "effect_agent_canonical_batches", "startup_scan", batches),
1032
- records: yield* decodeRows(Schema.Array(RecordRow), "effect_agent_canonical_records", "startup_scan", records),
1033
- checkpoints: yield* decodeRows(Schema.Array(CheckpointRow), "effect_agent_checkpoints", "startup_scan", checkpoints)
1030
+ conversations: yield* decodeRows$1(Schema.Array(ConversationRow), "effect_agent_conversations", "startup_scan", conversations),
1031
+ batches: yield* decodeRows$1(Schema.Array(BatchRow), "effect_agent_canonical_batches", "startup_scan", batches),
1032
+ records: yield* decodeRows$1(Schema.Array(RecordRow), "effect_agent_canonical_records", "startup_scan", records),
1033
+ checkpoints: yield* decodeRows$1(Schema.Array(CheckpointRow), "effect_agent_checkpoints", "startup_scan", checkpoints)
1034
1034
  };
1035
1035
  })).pipe(Effect.catchTag("SqlError", (error) => Effect.fail(storageError("startup scan transaction")(error))));
1036
1036
  }),
@@ -1313,7 +1313,7 @@ const decodeStartupPayloads = Effect.fn("DoConversationStore.decodeStartupPayloa
1313
1313
  message: "Canonical rows exist without a materialized Conversation."
1314
1314
  });
1315
1315
  });
1316
- const makeServices$1 = Effect.fn("DoConversationStore.makeServices")(function* () {
1316
+ const makeServices$2 = Effect.fn("DoConversationStore.makeServices")(function* () {
1317
1317
  const config = yield* DoStorageConfig;
1318
1318
  const failpoint = yield* DoStorageFailpoint;
1319
1319
  const sql = yield* SqlClient.SqlClient;
@@ -1491,7 +1491,7 @@ const makeServices$1 = Effect.fn("DoConversationStore.makeServices")(function* (
1491
1491
  * Durable Object Conversation Store implementation with configuration, failpoint, SQL, and
1492
1492
  * Crypto authority kept visible in its input channel.
1493
1493
  */
1494
- const conversationStoreLayer = Layer.effectContext(makeServices$1());
1494
+ const conversationStoreLayer = Layer.effectContext(makeServices$2());
1495
1495
  /**
1496
1496
  * Validated Durable Object storage configuration Layer with the documented defaults applied.
1497
1497
  * Shared by the ConversationStore and SubmissionLedger convenience layers so their defaults
@@ -1717,7 +1717,7 @@ const corruptionFailure = (operation, table, rowKey, message) => internalFailure
1717
1717
  rowKey,
1718
1718
  message
1719
1719
  }));
1720
- const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1720
+ const makeServices$1 = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1721
1721
  const config = yield* DoStorageConfig;
1722
1722
  const failpoint = yield* DoStorageFailpoint;
1723
1723
  const sql = yield* SqlClient.SqlClient;
@@ -1736,7 +1736,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1736
1736
  iso: new Date(millis).toISOString()
1737
1737
  }));
1738
1738
  const timestampMillis = (operation, rowKey) => (timestamp) => decodeUtcInstant(timestamp).pipe(Effect.map(DateTime.toEpochMillis), Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submission_ownership", rowKey, error.message)));
1739
- const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows(Schema.Array(SubmissionRow), "effect_agent_submissions", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
1739
+ const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows$1(Schema.Array(SubmissionRow), "effect_agent_submissions", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
1740
1740
  const readSubmission = Effect.fn("DoSubmissionLedger.readSubmission")(function* (operation, submissionId) {
1741
1741
  const rows = yield* sql`
1742
1742
  SELECT ${sql.literal(SUBMISSION_COLUMNS)}
@@ -1767,7 +1767,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1767
1767
  FROM effect_agent_submission_ownership
1768
1768
  WHERE submission_id = ${submissionId}
1769
1769
  `.pipe(Effect.mapError(sqlFailure(operation)));
1770
- const decoded = yield* decodeRows(Schema.Array(OwnershipRow), "effect_agent_submission_ownership", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1770
+ const decoded = yield* decodeRows$1(Schema.Array(OwnershipRow), "effect_agent_submission_ownership", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1771
1771
  if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_submission_ownership", submissionId, "An ownership primary key returned more than one row.");
1772
1772
  return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
1773
1773
  });
@@ -1832,7 +1832,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1832
1832
  FROM effect_agent_settlement_reservations
1833
1833
  WHERE submission_id = ${submissionId}
1834
1834
  `.pipe(Effect.mapError(sqlFailure(operation)));
1835
- const decoded = yield* decodeRows(Schema.Array(ReservationRow), "effect_agent_settlement_reservations", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1835
+ const decoded = yield* decodeRows$1(Schema.Array(ReservationRow), "effect_agent_settlement_reservations", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1836
1836
  if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_settlement_reservations", submissionId, "A settlement reservation primary key returned more than one row.");
1837
1837
  return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
1838
1838
  });
@@ -1847,7 +1847,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1847
1847
  FROM effect_agent_abort_intents
1848
1848
  WHERE submission_id = ${submissionId}
1849
1849
  `.pipe(Effect.mapError(sqlFailure(operation)));
1850
- const decoded = yield* decodeRows(Schema.Array(AbortIntentRow), "effect_agent_abort_intents", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1850
+ const decoded = yield* decodeRows$1(Schema.Array(AbortIntentRow), "effect_agent_abort_intents", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1851
1851
  if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_abort_intents", submissionId, "An abort intent primary key returned more than one row.");
1852
1852
  return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
1853
1853
  });
@@ -1867,7 +1867,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1867
1867
  WHERE parent_submission_id = ${parentSubmissionId}
1868
1868
  ORDER BY child_submission_id ASC
1869
1869
  `.pipe(Effect.mapError(sqlFailure(operation)));
1870
- return yield* decodeRows(Schema.Array(ChildSettlementMarkerRow), "effect_agent_child_settlements", parentSubmissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1870
+ return yield* decodeRows$1(Schema.Array(ChildSettlementMarkerRow), "effect_agent_child_settlements", parentSubmissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1871
1871
  });
1872
1872
  /**
1873
1873
  * Whether one listed child is provably settled from THIS store: either its own row lives
@@ -1880,7 +1880,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1880
1880
  const childRow = yield* readSubmission(operation, childSubmissionId);
1881
1881
  return Option.isSome(childRow) && childRow.value.state === "settled";
1882
1882
  });
1883
- const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows(Schema.Array(ChildReservationRow), "effect_agent_child_reservations", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
1883
+ const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows$1(Schema.Array(ChildReservationRow), "effect_agent_child_reservations", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
1884
1884
  const readChildReservation = Effect.fn("DoSubmissionLedger.readChildReservation")(function* (operation, reservationId) {
1885
1885
  const rows = yield* sql`
1886
1886
  SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
@@ -1933,7 +1933,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1933
1933
  WHERE submission_id = ${submissionId}
1934
1934
  ORDER BY tool_call_id ASC
1935
1935
  `.pipe(Effect.mapError(sqlFailure(operation)));
1936
- return yield* decodeRows(Schema.Array(ApprovalDecisionRow), "effect_agent_approval_decisions", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1936
+ return yield* decodeRows$1(Schema.Array(ApprovalDecisionRow), "effect_agent_approval_decisions", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1937
1937
  });
1938
1938
  const approvalIntentFromRow = Effect.fn("DoSubmissionLedger.approvalIntentFromRow")(function* (operation, row) {
1939
1939
  return yield* decodeApprovalDecisionIntent({
@@ -1958,7 +1958,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1958
1958
  WHERE submission_id = ${submissionId}
1959
1959
  ORDER BY tool_call_id ASC
1960
1960
  `.pipe(Effect.mapError(sqlFailure(operation)));
1961
- return yield* decodeRows(Schema.Array(UnknownResolutionRow), "effect_agent_unknown_resolutions", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1961
+ return yield* decodeRows$1(Schema.Array(UnknownResolutionRow), "effect_agent_unknown_resolutions", submissionId, rows).pipe(Effect.mapError(internalFailure(operation)));
1962
1962
  });
1963
1963
  const unknownResolutionIntentFromRow = Effect.fn("DoSubmissionLedger.unknownResolutionIntentFromRow")(function* (operation, row) {
1964
1964
  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)));
@@ -1989,7 +1989,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1989
1989
  WHERE conversation_id = ${conversationId}
1990
1990
  AND record_id = ${recordId}
1991
1991
  `.pipe(Effect.mapError(sqlFailure(operation)));
1992
- return (yield* decodeRows(Schema.Array(CanonicalRecordIdRow), "effect_agent_canonical_records", `${conversationId}/${recordId}`, rows).pipe(Effect.mapError(internalFailure(operation)))).length === 0 ? void 0 : recordId;
1992
+ return (yield* decodeRows$1(Schema.Array(CanonicalRecordIdRow), "effect_agent_canonical_records", `${conversationId}/${recordId}`, rows).pipe(Effect.mapError(internalFailure(operation)))).length === 0 ? void 0 : recordId;
1993
1993
  });
1994
1994
  const abortIntentFromRow = Effect.fn("DoSubmissionLedger.abortIntentFromRow")(function* (operation, submission, submissionId, row) {
1995
1995
  const canonicalRecordId = yield* canonicalAbortRecordId(operation, submission.conversation_id, submissionId);
@@ -2048,7 +2048,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
2048
2048
  FROM effect_agent_submissions
2049
2049
  WHERE conversation_id = ${validated.conversationId}
2050
2050
  `.pipe(Effect.mapError(sqlFailure(operation)));
2051
- const decodedMax = yield* decodeRows(Schema.Array(MaxQueueSequenceRow), "effect_agent_submissions", validated.conversationId, maxRows).pipe(Effect.mapError(internalFailure(operation)));
2051
+ const decodedMax = yield* decodeRows$1(Schema.Array(MaxQueueSequenceRow), "effect_agent_submissions", validated.conversationId, maxRows).pipe(Effect.mapError(internalFailure(operation)));
2052
2052
  const queueSequence = yield* decodeQueueSequence((decodedMax[0]?.max_queue_sequence ?? 0) + 1).pipe(Effect.mapError(internalFailure(operation)));
2053
2053
  const now = yield* currentInstant;
2054
2054
  yield* sql`
@@ -3208,13 +3208,369 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
3208
3208
  * database, storage-backed transaction discipline, and producer-epoch fencing substrate.
3209
3209
  * Configuration, failpoint, SQL, and Crypto authority stay visible in the input channel.
3210
3210
  */
3211
- const submissionLedgerLayer = Layer.effectContext(makeServices());
3211
+ const submissionLedgerLayer = Layer.effectContext(makeServices$1());
3212
3212
  /**
3213
3213
  * A composition-root convenience Layer for the durable Submission Ledger. Point it at the
3214
3214
  * same `ctx.storage` as the ConversationStore so claims fence the same producer epochs.
3215
3215
  */
3216
3216
  const ledgerLayer = (options) => Layer.unwrap(Effect.map(DoStorageConfig, (config) => submissionLedgerLayer.pipe(Layer.provide(Layer.mergeAll(Layer.succeed(DoStorageConfig)(config), storageFailpointLayer(options), SqliteClient.layer({ storage: options.storage }), BrowserCrypto.layer))))).pipe(Layer.provide(storageConfigLayer(options)));
3217
3217
  //#endregion
3218
+ //#region src/do-schedule-store.ts
3219
+ const CURRENT_SCHEDULE_STORE_VERSION = 1;
3220
+ const StoredScheduleJson = Schema.String.check(Schema.isMaxLength(19e5));
3221
+ const StoredDeadline = Schema.NullOr(ScheduleInstant);
3222
+ var ScheduleRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleRow")({
3223
+ tenant_id: ScheduleOwner.fields.tenantId,
3224
+ owner_id: ScheduleOwner.fields.ownerId,
3225
+ schedule_id: ScheduleId,
3226
+ deadline_at_millis: StoredDeadline,
3227
+ record_json: StoredScheduleJson
3228
+ }) {};
3229
+ const ScheduleDueRow = Schema.Struct({
3230
+ tenant_id: ScheduleOwner.fields.tenantId,
3231
+ owner_id: ScheduleOwner.fields.ownerId,
3232
+ schedule_id: ScheduleId,
3233
+ deadline_at_millis: ScheduleInstant
3234
+ });
3235
+ var ScheduleCountRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleCountRow")({ schedule_count: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)) }) {};
3236
+ var ScheduleDeadlineRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleDeadlineRow")({ deadline_at_millis: StoredDeadline }) {};
3237
+ var ScheduleStoreStateRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleStoreStateRow")({
3238
+ storage_version: Schema.Int.check(Schema.isGreaterThan(0)),
3239
+ alarm_generation: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
3240
+ }) {};
3241
+ var ScheduleTableNameRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleTableNameRow")({ name: Schema.String }) {};
3242
+ /**
3243
+ * Platform-owned transaction boundary for schedule SQL and logical alarm mutation. The callback
3244
+ * and its `replaceAlarm` capability belong to one fiber and must not escape or fork.
3245
+ */
3246
+ var DoScheduleTransaction = class extends Context.Service()("@effect-agent/storage-cloudflare/DoScheduleTransaction") {};
3247
+ /** Platform driver operations that use the same schedule-state and alarm transaction. */
3248
+ var DoScheduleAlarmControl = class extends Context.Service()("@effect-agent/storage-cloudflare/DoScheduleAlarmControl") {};
3249
+ const unavailable = (operation) => ScheduleStorageError.make({
3250
+ operation,
3251
+ reason: "unavailable"
3252
+ });
3253
+ const corrupt = (operation) => ScheduleStorageError.make({
3254
+ operation,
3255
+ reason: "corrupt"
3256
+ });
3257
+ const decodeRows = Effect.fn("DoScheduleStore.decodeRows")(function* (schema, rows, operation) {
3258
+ return yield* Schema.decodeUnknownEffect(schema)(rows).pipe(Effect.mapError(() => corrupt(operation)));
3259
+ });
3260
+ const decodeBoundary = (schema, value, operation) => Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => corrupt(operation)));
3261
+ const decodeRecord = Effect.fn("DoScheduleStore.decodeRecord")(function* (row) {
3262
+ const record = yield* Schema.decodeEffect(Schema.fromJsonString(ScheduleRecord))(row.record_json).pipe(Effect.mapError(() => corrupt("decode schedule")));
3263
+ if (record.owner.tenantId !== row.tenant_id || record.owner.ownerId !== row.owner_id || record.scheduleId !== row.schedule_id || scheduleDeadline(record) !== row.deadline_at_millis) return yield* corrupt("decode schedule index");
3264
+ return record;
3265
+ });
3266
+ const encodeRecord = Effect.fn("DoScheduleStore.encodeRecord")(function* (record) {
3267
+ const encoded = yield* Schema.encodeEffect(Schema.fromJsonString(ScheduleRecord))(record).pipe(Effect.mapError(() => corrupt("encode schedule")));
3268
+ return yield* Schema.decodeUnknownEffect(StoredScheduleJson)(encoded).pipe(Effect.mapError(() => corrupt("encode schedule bounds")));
3269
+ });
3270
+ const initializeScheduleStore = Effect.fn("DoScheduleStore.initialize")(function* () {
3271
+ const sql = yield* SqlClient.SqlClient;
3272
+ const operation = "initialize schedule store";
3273
+ const rawTables = yield* sql`
3274
+ SELECT name
3275
+ FROM sqlite_master
3276
+ WHERE type = 'table'
3277
+ AND name IN (
3278
+ 'effect_agent_schedule_store_state',
3279
+ 'effect_agent_schedules'
3280
+ )
3281
+ ORDER BY name
3282
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3283
+ const tables = yield* decodeRows(Schema.Array(ScheduleTableNameRow), rawTables, operation);
3284
+ const hasState = tables.some((row) => row.name === "effect_agent_schedule_store_state");
3285
+ const hasSchedules = tables.some((row) => row.name === "effect_agent_schedules");
3286
+ if (!hasState) {
3287
+ if (hasSchedules) return yield* corrupt(operation);
3288
+ yield* sql.withTransaction(Effect.gen(function* () {
3289
+ yield* sql`
3290
+ CREATE TABLE effect_agent_schedule_store_state (
3291
+ singleton INTEGER PRIMARY KEY NOT NULL CHECK (singleton = 1),
3292
+ storage_version INTEGER NOT NULL,
3293
+ alarm_generation INTEGER NOT NULL
3294
+ )
3295
+ `.withoutTransform;
3296
+ yield* sql`
3297
+ CREATE TABLE effect_agent_schedules (
3298
+ tenant_id TEXT NOT NULL,
3299
+ owner_id TEXT NOT NULL,
3300
+ schedule_id TEXT NOT NULL,
3301
+ deadline_at_millis INTEGER,
3302
+ record_json TEXT NOT NULL,
3303
+ PRIMARY KEY (tenant_id, owner_id, schedule_id)
3304
+ )
3305
+ `.withoutTransform;
3306
+ yield* sql`
3307
+ CREATE INDEX effect_agent_schedules_deadline
3308
+ ON effect_agent_schedules (deadline_at_millis, tenant_id, owner_id, schedule_id)
3309
+ WHERE deadline_at_millis IS NOT NULL
3310
+ `.withoutTransform;
3311
+ yield* sql`
3312
+ CREATE INDEX effect_agent_schedules_owner_deadline
3313
+ ON effect_agent_schedules (tenant_id, owner_id, deadline_at_millis, schedule_id)
3314
+ WHERE deadline_at_millis IS NOT NULL
3315
+ `.withoutTransform;
3316
+ yield* sql`
3317
+ INSERT INTO effect_agent_schedule_store_state (
3318
+ singleton, storage_version, alarm_generation
3319
+ ) VALUES (1, ${CURRENT_SCHEDULE_STORE_VERSION}, 0)
3320
+ `.withoutTransform;
3321
+ })).pipe(Effect.mapError(() => unavailable(operation)));
3322
+ return;
3323
+ }
3324
+ if (!hasSchedules) return yield* corrupt(operation);
3325
+ const rawState = yield* sql`
3326
+ SELECT storage_version, alarm_generation
3327
+ FROM effect_agent_schedule_store_state
3328
+ WHERE singleton = 1
3329
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3330
+ const state = yield* decodeRows(Schema.Array(ScheduleStoreStateRow), rawState, operation);
3331
+ if (state.length !== 1 || state[0].storage_version !== CURRENT_SCHEDULE_STORE_VERSION) return yield* corrupt(operation);
3332
+ });
3333
+ const makeServices = Effect.gen(function* () {
3334
+ const sql = yield* SqlClient.SqlClient;
3335
+ const transactions = yield* DoScheduleTransaction;
3336
+ const scheduleFailpoint = yield* ScheduleFailpoint;
3337
+ yield* initializeScheduleStore();
3338
+ const readRows = Effect.fn("DoScheduleStore.readRows")(function* (key, operation) {
3339
+ const rows = yield* sql`
3340
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis, record_json
3341
+ FROM effect_agent_schedules
3342
+ WHERE tenant_id = ${key.owner.tenantId}
3343
+ AND owner_id = ${key.owner.ownerId}
3344
+ AND schedule_id = ${key.scheduleId}
3345
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3346
+ return yield* decodeRows(Schema.Array(ScheduleRow), rows, operation);
3347
+ });
3348
+ const readOne = Effect.fn("DoScheduleStore.readOne")(function* (key, operation) {
3349
+ const rows = yield* readRows(key, operation);
3350
+ if (rows.length === 0) return null;
3351
+ if (rows.length !== 1) return yield* corrupt(operation);
3352
+ return yield* decodeRecord(rows[0]);
3353
+ });
3354
+ const readNextDeadline = Effect.fn("DoScheduleStore.readNextDeadline")(function* (owner, operation) {
3355
+ const rows = owner === void 0 ? yield* sql`
3356
+ SELECT MIN(deadline_at_millis) AS deadline_at_millis
3357
+ FROM effect_agent_schedules
3358
+ WHERE deadline_at_millis IS NOT NULL
3359
+ `.pipe(Effect.mapError(() => unavailable(operation))) : yield* sql`
3360
+ SELECT MIN(deadline_at_millis) AS deadline_at_millis
3361
+ FROM effect_agent_schedules
3362
+ WHERE tenant_id = ${owner.tenantId}
3363
+ AND owner_id = ${owner.ownerId}
3364
+ AND deadline_at_millis IS NOT NULL
3365
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3366
+ const decoded = yield* decodeRows(Schema.Array(ScheduleDeadlineRow), rows, operation);
3367
+ if (decoded.length !== 1) return yield* corrupt(operation);
3368
+ return decoded[0].deadline_at_millis;
3369
+ });
3370
+ const replaceAlarm = Effect.fn("DoScheduleStore.replaceAlarm")(function* (replace, deadlineAtMillis, operation) {
3371
+ const rawState = yield* sql`
3372
+ UPDATE effect_agent_schedule_store_state
3373
+ SET alarm_generation = alarm_generation + 1
3374
+ WHERE singleton = 1
3375
+ RETURNING storage_version, alarm_generation
3376
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3377
+ const state = yield* decodeRows(Schema.Array(ScheduleStoreStateRow), rawState, operation);
3378
+ if (state.length !== 1 || state[0].storage_version !== CURRENT_SCHEDULE_STORE_VERSION) return yield* corrupt(operation);
3379
+ yield* scheduleFailpoint.hit("schedule:alarm:before");
3380
+ yield* replace({
3381
+ deadlineAtMillis,
3382
+ generation: state[0].alarm_generation
3383
+ });
3384
+ yield* scheduleFailpoint.hit("schedule:alarm:after");
3385
+ });
3386
+ const insert = Effect.fn("DoScheduleStore.insert")(function* (record, ownerLimit) {
3387
+ const operation = "insert schedule";
3388
+ const canonical = yield* decodeBoundary(ScheduleRecord, record, operation);
3389
+ const recordJson = yield* encodeRecord(canonical);
3390
+ const result = yield* transactions.run((replace) => Effect.gen(function* () {
3391
+ const existing = yield* readOne(canonical, operation);
3392
+ if (existing !== null) {
3393
+ if (existing.creationFingerprint === canonical.creationFingerprint) return {
3394
+ record: existing,
3395
+ inserted: false
3396
+ };
3397
+ return yield* ScheduleConflict.make({
3398
+ reason: "creation",
3399
+ key: canonical
3400
+ });
3401
+ }
3402
+ const rawCounts = yield* sql`
3403
+ SELECT COUNT(*) AS schedule_count
3404
+ FROM effect_agent_schedules
3405
+ WHERE tenant_id = ${canonical.owner.tenantId}
3406
+ AND owner_id = ${canonical.owner.ownerId}
3407
+ AND (json_extract(record_json, '$.pending') IS NOT NULL OR
3408
+ (json_extract(record_json, '$.state') != 'cancelled' AND json_extract(record_json, '$.nextAtMillis') IS NOT NULL))
3409
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3410
+ const counts = yield* decodeRows(Schema.Array(ScheduleCountRow), rawCounts, operation);
3411
+ if (counts.length !== 1) return yield* corrupt(operation);
3412
+ if (counts[0].schedule_count >= ownerLimit) return yield* ScheduleCapacityError.make({ limit: ownerLimit });
3413
+ yield* scheduleFailpoint.hit("schedule:insert:before");
3414
+ yield* sql`
3415
+ INSERT INTO effect_agent_schedules (
3416
+ tenant_id, owner_id, schedule_id, deadline_at_millis, record_json
3417
+ ) VALUES (
3418
+ ${canonical.owner.tenantId},
3419
+ ${canonical.owner.ownerId},
3420
+ ${canonical.scheduleId},
3421
+ ${scheduleDeadline(canonical)},
3422
+ ${recordJson}
3423
+ )
3424
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3425
+ const deadline = yield* readNextDeadline(void 0, operation);
3426
+ yield* replaceAlarm(replace, deadline, operation);
3427
+ return {
3428
+ record: canonical,
3429
+ inserted: true
3430
+ };
3431
+ }));
3432
+ if (result.inserted) yield* scheduleFailpoint.hit("schedule:insert:after");
3433
+ return result.record;
3434
+ });
3435
+ const get = Effect.fn("DoScheduleStore.get")(function* (key) {
3436
+ const canonical = yield* decodeBoundary(ScheduleKey, key, "get schedule");
3437
+ return yield* readOne(canonical, "get schedule");
3438
+ });
3439
+ const list = Effect.fn("DoScheduleStore.list")(function* (requestValue) {
3440
+ const operation = "list schedules";
3441
+ const request = yield* decodeBoundary(SchedulePageRequest, requestValue, operation);
3442
+ const rows = request.after === void 0 ? yield* sql`
3443
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis, record_json
3444
+ FROM effect_agent_schedules
3445
+ WHERE tenant_id = ${request.owner.tenantId}
3446
+ AND owner_id = ${request.owner.ownerId}
3447
+ ORDER BY schedule_id
3448
+ LIMIT ${request.limit + 1}
3449
+ `.pipe(Effect.mapError(() => unavailable(operation))) : yield* sql`
3450
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis, record_json
3451
+ FROM effect_agent_schedules
3452
+ WHERE tenant_id = ${request.owner.tenantId}
3453
+ AND owner_id = ${request.owner.ownerId}
3454
+ AND schedule_id > ${request.after}
3455
+ ORDER BY schedule_id
3456
+ LIMIT ${request.limit + 1}
3457
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3458
+ const decoded = yield* decodeRows(Schema.Array(ScheduleRow), rows, operation);
3459
+ const records = yield* Effect.forEach(decoded, decodeRecord);
3460
+ const hasNext = records.length > request.limit;
3461
+ const items = hasNext ? records.slice(0, request.limit) : records;
3462
+ return {
3463
+ items,
3464
+ next: hasNext ? items.at(-1)?.scheduleId ?? null : null
3465
+ };
3466
+ });
3467
+ const change = Effect.fn("DoScheduleStore.change")(function* (key, change, ownerLimit = defaultSchedulingLimits.maxSchedulesPerOwner) {
3468
+ const operation = "change schedule";
3469
+ const canonicalKey = yield* decodeBoundary(ScheduleKey, key, operation);
3470
+ const canonicalChange = yield* decodeBoundary(ScheduleChange, change, operation);
3471
+ const result = yield* transactions.run((replace) => Effect.gen(function* () {
3472
+ const current = yield* readOne(canonicalKey, operation);
3473
+ if (current === null) return yield* ScheduleNotFound.make({ key: canonicalKey });
3474
+ const transition = applyScheduleChange(current, canonicalChange);
3475
+ if (Result.isFailure(transition)) return yield* transition.failure;
3476
+ const next = transition.success;
3477
+ if (!scheduleUsesCapacity(current) && scheduleUsesCapacity(next)) {
3478
+ const rawCounts = yield* sql`
3479
+ SELECT COUNT(*) AS schedule_count FROM effect_agent_schedules
3480
+ WHERE tenant_id = ${key.owner.tenantId} AND owner_id = ${key.owner.ownerId}
3481
+ AND (json_extract(record_json, '$.pending') IS NOT NULL OR
3482
+ (json_extract(record_json, '$.state') != 'cancelled' AND json_extract(record_json, '$.nextAtMillis') IS NOT NULL))
3483
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3484
+ const counts = yield* decodeRows(Schema.Array(ScheduleCountRow), rawCounts, operation);
3485
+ if (counts.length !== 1) return yield* corrupt(operation);
3486
+ if (counts[0].schedule_count >= ownerLimit) return yield* ScheduleCapacityError.make({ limit: ownerLimit });
3487
+ }
3488
+ if (next === current) return {
3489
+ record: current,
3490
+ changed: false
3491
+ };
3492
+ const recordJson = yield* encodeRecord(next);
3493
+ yield* scheduleFailpoint.hit(`schedule:${canonicalChange._tag.toLowerCase()}:before`);
3494
+ yield* sql`
3495
+ UPDATE effect_agent_schedules
3496
+ SET deadline_at_millis = ${scheduleDeadline(next)}, record_json = ${recordJson}
3497
+ WHERE tenant_id = ${canonicalKey.owner.tenantId}
3498
+ AND owner_id = ${canonicalKey.owner.ownerId}
3499
+ AND schedule_id = ${canonicalKey.scheduleId}
3500
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3501
+ const deadline = yield* readNextDeadline(void 0, operation);
3502
+ yield* replaceAlarm(replace, deadline, operation);
3503
+ return {
3504
+ record: next,
3505
+ changed: true
3506
+ };
3507
+ }));
3508
+ if (result.changed) yield* scheduleFailpoint.hit(`schedule:${canonicalChange._tag.toLowerCase()}:after`);
3509
+ return result.record;
3510
+ });
3511
+ const due = Effect.fn("DoScheduleStore.due")(function* (nowMillis, limit, owner, after) {
3512
+ const operation = "query due schedules";
3513
+ const cursor = after === void 0 ? void 0 : yield* Schema.decodeUnknownEffect(ScheduleDueCursor)(after).pipe(Effect.mapError(() => corrupt(operation)));
3514
+ const continuation = cursor === void 0 ? sql`1 = 1` : sql`
3515
+ (deadline_at_millis, tenant_id, owner_id, schedule_id) >
3516
+ (${cursor.deadlineAtMillis}, ${cursor.owner.tenantId}, ${cursor.owner.ownerId}, ${cursor.scheduleId})`;
3517
+ const rows = owner === void 0 ? yield* sql`
3518
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis
3519
+ FROM effect_agent_schedules
3520
+ WHERE deadline_at_millis <= ${nowMillis} AND ${continuation}
3521
+ ORDER BY deadline_at_millis, tenant_id, owner_id, schedule_id
3522
+ LIMIT ${limit}
3523
+ `.pipe(Effect.mapError(() => unavailable(operation))) : yield* sql`
3524
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis
3525
+ FROM effect_agent_schedules
3526
+ WHERE tenant_id = ${owner.tenantId}
3527
+ AND owner_id = ${owner.ownerId}
3528
+ AND deadline_at_millis <= ${nowMillis} AND ${continuation}
3529
+ ORDER BY deadline_at_millis, schedule_id
3530
+ LIMIT ${limit}
3531
+ `.pipe(Effect.mapError(() => unavailable(operation)));
3532
+ return (yield* decodeRows(Schema.Array(ScheduleDueRow), rows, operation)).map((row) => ({
3533
+ owner: {
3534
+ tenantId: row.tenant_id,
3535
+ ownerId: row.owner_id
3536
+ },
3537
+ scheduleId: row.schedule_id,
3538
+ deadlineAtMillis: row.deadline_at_millis
3539
+ }));
3540
+ });
3541
+ const nextDeadline = Effect.fn("DoScheduleStore.nextDeadline")(function* (owner) {
3542
+ return yield* readNextDeadline(owner, "query next schedule deadline");
3543
+ });
3544
+ const prearm = Effect.fn("DoScheduleStore.prearm")(function* (deadlineAtMillis) {
3545
+ yield* decodeBoundary(ScheduleInstant, deadlineAtMillis, "pre-arm schedule recovery");
3546
+ yield* transactions.run((replace) => replaceAlarm(replace, deadlineAtMillis, "pre-arm schedule recovery"));
3547
+ yield* scheduleFailpoint.hit("schedule:prearm:after");
3548
+ });
3549
+ const reconcile = Effect.gen(function* () {
3550
+ yield* transactions.run((replace) => Effect.gen(function* () {
3551
+ const deadline = yield* readNextDeadline(void 0, "reconcile schedule alarm");
3552
+ yield* replaceAlarm(replace, deadline, "reconcile schedule alarm");
3553
+ }));
3554
+ yield* scheduleFailpoint.hit("schedule:reconcile:after");
3555
+ });
3556
+ return Context.make(ScheduleStore, {
3557
+ insert,
3558
+ get,
3559
+ list,
3560
+ change,
3561
+ due,
3562
+ nextDeadline
3563
+ }).pipe(Context.add(DoScheduleAlarmControl, {
3564
+ prearm,
3565
+ reconcile
3566
+ }));
3567
+ });
3568
+ /**
3569
+ * Durable Object SQLite ScheduleStore. Platform code supplies the one transaction owner that
3570
+ * combines these SQL mutations with its logical and native alarm lifecycle.
3571
+ */
3572
+ const scheduleStoreLayer = Layer.effectContext(makeServices);
3573
+ //#endregion
3218
3574
  //#region src/port-protocol.ts
3219
3575
  /**
3220
3576
  * The cross-Durable-Object port protocol (plan §1.3, D-P6-3): Schema request/response/error
@@ -3727,6 +4083,6 @@ const handleEncodedPortRequest = Effect.fn("DoPortRouting.handleEncodedPortReque
3727
4083
  return yield* encodePortResponse(yield* decodePortRequest(encoded).pipe(Effect.flatMap(executePortRequest), Effect.catch((error) => Effect.succeed(PortFailed.make({ failure: PortProtocolError.make({ message: boundPortDiagnostic(`The port request could not be decoded: ${error.message}`) }) }))))).pipe(Effect.catch((error) => Effect.succeed(encodedProtocolFailure(`The port response could not be encoded: ${error.message}`))));
3728
4084
  });
3729
4085
  //#endregion
3730
- export { ConversationPortTransport, CurrentDoStorageVersion, DEFAULT_MAX_STORED_VALUE_BYTES, DoAppendConflict, DoCheckpointConflict, DoFenceRejected, DoLedgerError, DoStorageCompatibilityError, DoStorageConfig, DoStorageConfigValue, DoStorageCorruptionError, DoStorageError, DoStorageFailpoint, DoStorageFailpointError, DoStorageFailpointLocation, DoStorageFailpointTestControl, DoValueBoundExceeded, LedgerAdmitCall, LedgerAdmitResult, LedgerLookupCall, LedgerLookupResult, LedgerMarkReadyCall, LedgerMarkReadyResult, LedgerRecordChildSettledCall, LedgerRecordChildSettledResult, LedgerRequestAbortCall, LedgerRequestAbortResult, LedgerResolveAdmissionCall, LedgerResolveAdmissionResult, MAX_PORT_DIAGNOSTIC_LENGTH, PortFailed, PortFailure, PortProtocolError, PortRequest, PortResponse, PortResult, PortSucceeded, PortTransportError, StoreAppendCall, StoreAppendResult, StoreExportCall, StoreExportResult, StoreInspectTailCall, StoreInspectTailResult, StoreMaterializeCall, StoreMaterializeResult, StoreReadPageCall, StoreReadPageResult, boundPortDiagnostic, conversationStoreLayer, decodePortRequest, decodePortResponse, doMigrations, encodePortRequest, encodePortResponse, evictionFailpointHandler, executePortRequest, handleEncodedPortRequest, layer, ledgerLayer, observationOffsetAt, portTransportFailure, routedConversationStoreLayer, routedSubmissionLedgerLayer, storageConfigLayer, storageFailpointLayer, submissionLedgerLayer };
4086
+ export { ConversationPortTransport, CurrentDoStorageVersion, DEFAULT_MAX_STORED_VALUE_BYTES, DoAppendConflict, DoCheckpointConflict, DoFenceRejected, DoLedgerError, DoScheduleAlarmControl, DoScheduleTransaction, DoStorageCompatibilityError, DoStorageConfig, DoStorageConfigValue, DoStorageCorruptionError, DoStorageError, DoStorageFailpoint, DoStorageFailpointError, DoStorageFailpointLocation, DoStorageFailpointTestControl, DoValueBoundExceeded, LedgerAdmitCall, LedgerAdmitResult, LedgerLookupCall, LedgerLookupResult, LedgerMarkReadyCall, LedgerMarkReadyResult, LedgerRecordChildSettledCall, LedgerRecordChildSettledResult, LedgerRequestAbortCall, LedgerRequestAbortResult, LedgerResolveAdmissionCall, LedgerResolveAdmissionResult, MAX_PORT_DIAGNOSTIC_LENGTH, PortFailed, PortFailure, PortProtocolError, PortRequest, PortResponse, PortResult, PortSucceeded, PortTransportError, StoreAppendCall, StoreAppendResult, StoreExportCall, StoreExportResult, StoreInspectTailCall, StoreInspectTailResult, StoreMaterializeCall, StoreMaterializeResult, StoreReadPageCall, StoreReadPageResult, boundPortDiagnostic, conversationStoreLayer, decodePortRequest, decodePortResponse, doMigrations, encodePortRequest, encodePortResponse, evictionFailpointHandler, executePortRequest, handleEncodedPortRequest, layer, ledgerLayer, observationOffsetAt, portTransportFailure, routedConversationStoreLayer, routedSubmissionLedgerLayer, scheduleStoreLayer, storageConfigLayer, storageFailpointLayer, submissionLedgerLayer };
3731
4087
 
3732
4088
  //# sourceMappingURL=index.mjs.map