@effect-agent/storage-sqlite 0.1.0-beta.37 → 0.1.0-beta.39

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.
@@ -73,7 +73,7 @@ import {
73
73
  submissionAbortRecordId,
74
74
  type ChildSettledOutcome,
75
75
  type SuspensionOutcome,
76
- } from "@effect-agent/session";
76
+ } from "@effect-agent/thread";
77
77
  import { NodeCrypto } from "@effect/platform-node";
78
78
  import { SqliteClient } from "@effect/sql-sqlite-node";
79
79
  import { Clock, Context, Crypto, DateTime, Effect, Layer, Option, Schema, Stream } from "effect";
@@ -87,15 +87,15 @@ import {
87
87
  SqliteWriteContention,
88
88
  type SqliteStorageFailpointLocation,
89
89
  } from "./errors.ts";
90
+ import { decodeRows, initializeSqliteJournal } from "./sqlite-journal.ts";
91
+ import { SqliteStorageConfig } from "./sqlite-storage-config.ts";
92
+ import { SqliteStorageFailpoint } from "./sqlite-storage-failpoint.ts";
90
93
  import {
91
94
  storageConfigLayer,
92
95
  storageFailpointLayer,
93
96
  type SqliteStorageInitializationError,
94
97
  type SqliteStorageOptions,
95
- } from "./sqlite-conversation-store.ts";
96
- import { decodeRows, initializeSqliteJournal } from "./sqlite-journal.ts";
97
- import { SqliteStorageConfig } from "./sqlite-storage-config.ts";
98
- import { SqliteStorageFailpoint } from "./sqlite-storage-failpoint.ts";
98
+ } from "./sqlite-thread-store.ts";
99
99
 
100
100
  type SubmissionId = SubmissionSnapshot["submissionId"];
101
101
 
@@ -113,7 +113,7 @@ const WOKEN: ChildSettledOutcome = "woken";
113
113
 
114
114
  class SubmissionRow extends Schema.Class<SubmissionRow>("SubmissionRow")({
115
115
  submission_id: BoundedIdentifier,
116
- conversation_id: BoundedIdentifier,
116
+ thread_id: BoundedIdentifier,
117
117
  queue_sequence: QueueSequence,
118
118
  principal: BoundedIdentifier,
119
119
  idempotency_key: BoundedIdentifier,
@@ -208,7 +208,7 @@ class CanonicalRecordIdRow extends Schema.Class<CanonicalRecordIdRow>("Canonical
208
208
 
209
209
  const SUBMISSION_COLUMNS = `
210
210
  submission_id,
211
- conversation_id,
211
+ thread_id,
212
212
  queue_sequence,
213
213
  principal,
214
214
  idempotency_key,
@@ -247,7 +247,7 @@ const CHILD_RESERVATION_COLUMNS = `
247
247
  released_at
248
248
  `;
249
249
 
250
- /** The branded ToolCallId schema, reached through the session port so no core import is needed. */
250
+ /** The branded ToolCallId schema, reached through the thread port so no core import is needed. */
251
251
  const ToolCallIdSchema = ApprovalDecisionCommand.fields.toolCallId;
252
252
  const ToolCallIdList = Schema.Array(ToolCallIdSchema);
253
253
 
@@ -448,20 +448,20 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
448
448
  return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
449
449
  });
450
450
 
451
- const conversationEpoch = Effect.fn("SqliteSubmissionLedger.conversationEpoch")(function* (
451
+ const threadEpoch = Effect.fn("SqliteSubmissionLedger.threadEpoch")(function* (
452
452
  operation: string,
453
- conversationId: string,
453
+ threadId: string,
454
454
  ): Effect.fn.Return<ProducerEpoch, LedgerError> {
455
- const conversations = yield* journal
456
- .getConversation(conversationId)
455
+ const threads = yield* journal
456
+ .getThread(threadId)
457
457
  .pipe(Effect.mapError(internalFailure(operation)));
458
- return conversations.length === 0 ? EPOCH_ZERO : conversations[0].producer_epoch;
458
+ return threads.length === 0 ? EPOCH_ZERO : threads[0].producer_epoch;
459
459
  });
460
460
 
461
461
  /**
462
462
  * Verify inside the surrounding write transaction that the presented token still owns the
463
463
  * Submission's lane; a superseded or missing token fails with OwnershipLost carrying the
464
- * Conversation's current producer epoch (DUR-006).
464
+ * Thread's current producer epoch (DUR-006).
465
465
  */
466
466
  const requireOwnership = Effect.fn("SqliteSubmissionLedger.requireOwnership")(function* (
467
467
  operation: string,
@@ -470,7 +470,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
470
470
  ): Effect.fn.Return<OwnershipRow, OwnershipLost | LedgerError> {
471
471
  const ownership = yield* readOwnership(operation, submission.submission_id);
472
472
  if (Option.isNone(ownership) || ownership.value.ownership_token !== ownershipToken) {
473
- const actualEpoch = yield* conversationEpoch(operation, submission.conversation_id);
473
+ const actualEpoch = yield* threadEpoch(operation, submission.thread_id);
474
474
  const submissionId = yield* Schema.decodeUnknownEffect(
475
475
  SubmissionSnapshot.fields.submissionId,
476
476
  )(submission.submission_id).pipe(Effect.mapError(internalFailure(operation)));
@@ -514,7 +514,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
514
514
  }
515
515
  return yield* decodeSubmissionSnapshotUnknown({
516
516
  submissionId: row.submission_id,
517
- conversationId: row.conversation_id,
517
+ threadId: row.thread_id,
518
518
  queueSequence: row.queue_sequence,
519
519
  principal: row.principal,
520
520
  idempotencyKey: row.idempotency_key,
@@ -847,20 +847,20 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
847
847
  const canonicalAbortRecordId = Effect.fn("SqliteSubmissionLedger.canonicalAbortRecordId")(
848
848
  function* (
849
849
  operation: string,
850
- conversationId: string,
850
+ threadId: string,
851
851
  submissionId: SubmissionId,
852
852
  ): Effect.fn.Return<string | undefined, LedgerError> {
853
853
  const recordId = submissionAbortRecordId(submissionId);
854
854
  const rows = yield* sql<Record<string, unknown>>`
855
855
  SELECT record_id
856
856
  FROM effect_agent_canonical_records
857
- WHERE conversation_id = ${conversationId}
857
+ WHERE thread_id = ${threadId}
858
858
  AND record_id = ${recordId}
859
859
  `.pipe(Effect.mapError(sqlFailure(operation)));
860
860
  const decoded = yield* decodeRows(
861
861
  Schema.Array(CanonicalRecordIdRow),
862
862
  "effect_agent_canonical_records",
863
- `${conversationId}/${recordId}`,
863
+ `${threadId}/${recordId}`,
864
864
  rows,
865
865
  ).pipe(Effect.mapError(internalFailure(operation)));
866
866
  return decoded.length === 0 ? undefined : recordId;
@@ -875,7 +875,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
875
875
  ): Effect.fn.Return<AbortIntent, LedgerError> {
876
876
  const canonicalRecordId = yield* canonicalAbortRecordId(
877
877
  operation,
878
- submission.conversation_id,
878
+ submission.thread_id,
879
879
  submissionId,
880
880
  );
881
881
  return yield* decodeAbortIntent({
@@ -916,11 +916,11 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
916
916
  const result = yield* inWriteTransaction(
917
917
  operation,
918
918
  Effect.gen(function* () {
919
- const keyRowKey = `${validated.conversationId}/${validated.principal}/${validated.idempotencyKey}`;
919
+ const keyRowKey = `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`;
920
920
  const existingRows = yield* sql<Record<string, unknown>>`
921
921
  SELECT ${sql.literal(SUBMISSION_COLUMNS)}
922
922
  FROM effect_agent_submissions
923
- WHERE conversation_id = ${validated.conversationId}
923
+ WHERE thread_id = ${validated.threadId}
924
924
  AND principal = ${validated.principal}
925
925
  AND idempotency_key = ${validated.idempotencyKey}
926
926
  `.pipe(Effect.mapError(sqlFailure(operation)));
@@ -944,7 +944,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
944
944
  existing[0].parent_tool_call_id === validated.parentLinkage.parentToolCallId;
945
945
  if (existing[0].input_digest !== validated.inputDigest || !sameLinkage) {
946
946
  return yield* AdmissionConflict.make({
947
- conversationId: validated.conversationId,
947
+ threadId: validated.threadId,
948
948
  principal: validated.principal,
949
949
  idempotencyKey: validated.idempotencyKey,
950
950
  existingInputDigest: existing[0].input_digest,
@@ -963,12 +963,12 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
963
963
  const maxRows = yield* sql<Record<string, unknown>>`
964
964
  SELECT COALESCE(MAX(queue_sequence), 0) AS max_queue_sequence
965
965
  FROM effect_agent_submissions
966
- WHERE conversation_id = ${validated.conversationId}
966
+ WHERE thread_id = ${validated.threadId}
967
967
  `.pipe(Effect.mapError(sqlFailure(operation)));
968
968
  const decodedMax = yield* decodeRows(
969
969
  Schema.Array(MaxQueueSequenceRow),
970
970
  "effect_agent_submissions",
971
- validated.conversationId,
971
+ validated.threadId,
972
972
  maxRows,
973
973
  ).pipe(Effect.mapError(internalFailure(operation)));
974
974
  const queueSequence = yield* decodeQueueSequence(
@@ -979,7 +979,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
979
979
  yield* sql`
980
980
  INSERT INTO effect_agent_submissions (
981
981
  submission_id,
982
- conversation_id,
982
+ thread_id,
983
983
  queue_sequence,
984
984
  principal,
985
985
  idempotency_key,
@@ -995,7 +995,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
995
995
  parent_tool_call_id
996
996
  ) VALUES (
997
997
  ${mintedSubmissionId},
998
- ${validated.conversationId},
998
+ ${validated.threadId},
999
999
  ${queueSequence},
1000
1000
  ${validated.principal},
1001
1001
  ${validated.idempotencyKey},
@@ -1064,20 +1064,20 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
1064
1064
  const rows = yield* sql<Record<string, unknown>>`
1065
1065
  SELECT ${sql.literal(SUBMISSION_COLUMNS)}
1066
1066
  FROM effect_agent_submissions
1067
- WHERE conversation_id = ${validated.conversationId}
1067
+ WHERE thread_id = ${validated.threadId}
1068
1068
  AND principal = ${validated.principal}
1069
1069
  AND idempotency_key = ${validated.idempotencyKey}
1070
1070
  `.pipe(Effect.mapError(sqlFailure(operation)));
1071
1071
  const decoded = yield* decodeSubmissionRows(
1072
1072
  operation,
1073
- `${validated.conversationId}/${validated.principal}/${validated.idempotencyKey}`,
1073
+ `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`,
1074
1074
  rows,
1075
1075
  );
1076
1076
  if (decoded.length > 1) {
1077
1077
  return yield* corruptionFailure(
1078
1078
  operation,
1079
1079
  "effect_agent_submissions",
1080
- `${validated.conversationId}/${validated.principal}/${validated.idempotencyKey}`,
1080
+ `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`,
1081
1081
  "An admission idempotency key returned more than one row.",
1082
1082
  );
1083
1083
  }
@@ -1099,20 +1099,20 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
1099
1099
  const rows = yield* sql<Record<string, unknown>>`
1100
1100
  SELECT ${sql.literal(SUBMISSION_COLUMNS)}
1101
1101
  FROM effect_agent_submissions
1102
- WHERE conversation_id = ${validated.conversationId}
1102
+ WHERE thread_id = ${validated.threadId}
1103
1103
  AND principal = ${validated.principal}
1104
1104
  AND idempotency_key = ${validated.idempotencyKey}
1105
1105
  `.pipe(Effect.mapError(sqlFailure(operation)));
1106
1106
  const decoded = yield* decodeSubmissionRows(
1107
1107
  operation,
1108
- `${validated.conversationId}/${validated.principal}/${validated.idempotencyKey}`,
1108
+ `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`,
1109
1109
  rows,
1110
1110
  );
1111
1111
  if (decoded.length > 1) {
1112
1112
  return yield* corruptionFailure(
1113
1113
  operation,
1114
1114
  "effect_agent_submissions",
1115
- `${validated.conversationId}/${validated.principal}/${validated.idempotencyKey}`,
1115
+ `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`,
1116
1116
  "An admission idempotency key returned more than one row.",
1117
1117
  );
1118
1118
  }
@@ -1137,12 +1137,12 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
1137
1137
  const headRows = yield* sql<Record<string, unknown>>`
1138
1138
  SELECT ${sql.literal(SUBMISSION_COLUMNS)}
1139
1139
  FROM effect_agent_submissions
1140
- WHERE conversation_id = ${validated.conversationId}
1140
+ WHERE thread_id = ${validated.threadId}
1141
1141
  AND state <> 'settled'
1142
1142
  ORDER BY queue_sequence ASC
1143
1143
  LIMIT 1
1144
1144
  `.pipe(Effect.mapError(sqlFailure(operation)));
1145
- const heads = yield* decodeSubmissionRows(operation, validated.conversationId, headRows);
1145
+ const heads = yield* decodeSubmissionRows(operation, validated.threadId, headRows);
1146
1146
  if (heads.length === 0) return Option.none<Claim>();
1147
1147
  const head = heads[0];
1148
1148
 
@@ -1171,25 +1171,25 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
1171
1171
  if (expiresAt > now.millis) return Option.none<Claim>();
1172
1172
  }
1173
1173
 
1174
- // Bump the Conversation's producer epoch atomically with the claim so every stale
1175
- // Attempt is fenced out of canonical appends (DUR-006). A Conversation that was
1174
+ // Bump the Thread's producer epoch atomically with the claim so every stale
1175
+ // Attempt is fenced out of canonical appends (DUR-006). A Thread that was
1176
1176
  // never materialized (crash between admission and materialization) is created here
1177
1177
  // so recovery can claim first and re-materialize idempotently at this epoch.
1178
- const conversations = yield* journal
1179
- .getConversation(head.conversation_id)
1178
+ const threads = yield* journal
1179
+ .getThread(head.thread_id)
1180
1180
  .pipe(Effect.mapError(internalFailure(operation)));
1181
1181
  let producerEpoch: number;
1182
- if (conversations.length === 0) {
1182
+ if (threads.length === 0) {
1183
1183
  producerEpoch = 1;
1184
1184
  yield* sql`
1185
- INSERT INTO effect_agent_conversations (
1186
- conversation_id,
1185
+ INSERT INTO effect_agent_threads (
1186
+ thread_id,
1187
1187
  created_at,
1188
1188
  tail_sequence,
1189
1189
  tail_digest,
1190
1190
  producer_epoch
1191
1191
  ) VALUES (
1192
- ${head.conversation_id},
1192
+ ${head.thread_id},
1193
1193
  ${now.iso},
1194
1194
  0,
1195
1195
  ${EMPTY_TAIL_DIGEST},
@@ -1197,11 +1197,11 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
1197
1197
  )
1198
1198
  `.pipe(Effect.mapError(sqlFailure(operation)));
1199
1199
  } else {
1200
- producerEpoch = conversations[0].producer_epoch + 1;
1200
+ producerEpoch = threads[0].producer_epoch + 1;
1201
1201
  yield* sql`
1202
- UPDATE effect_agent_conversations
1202
+ UPDATE effect_agent_threads
1203
1203
  SET producer_epoch = ${producerEpoch}
1204
- WHERE conversation_id = ${head.conversation_id}
1204
+ WHERE thread_id = ${head.thread_id}
1205
1205
  `.pipe(Effect.mapError(sqlFailure(operation)));
1206
1206
  }
1207
1207
 
@@ -1234,14 +1234,14 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
1234
1234
  INSERT INTO effect_agent_attempts (
1235
1235
  attempt_id,
1236
1236
  submission_id,
1237
- conversation_id,
1237
+ thread_id,
1238
1238
  owner_producer_id,
1239
1239
  producer_epoch,
1240
1240
  claimed_at
1241
1241
  ) VALUES (
1242
1242
  ${attemptId},
1243
1243
  ${head.submission_id},
1244
- ${head.conversation_id},
1244
+ ${head.thread_id},
1245
1245
  ${validated.producerId},
1246
1246
  ${producerEpoch},
1247
1247
  ${now.iso}
@@ -1673,7 +1673,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
1673
1673
  `.pipe(Effect.mapError(sqlFailure(operation)));
1674
1674
  const canonicalRecordId = yield* canonicalAbortRecordId(
1675
1675
  operation,
1676
- submission.conversation_id,
1676
+ submission.thread_id,
1677
1677
  validated.submissionId,
1678
1678
  );
1679
1679
  return yield* decodeAbortIntent({
@@ -1701,10 +1701,10 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
1701
1701
  operation,
1702
1702
  Effect.gen(function* () {
1703
1703
  const host = yield* requireSubmission(operation, validated.hostSubmissionId);
1704
- if (host.conversation_id !== validated.conversationId) {
1704
+ if (host.thread_id !== validated.threadId) {
1705
1705
  return yield* LedgerError.make({
1706
1706
  operation,
1707
- message: `Host submission ${validated.hostSubmissionId} does not belong to conversation ${validated.conversationId}.`,
1707
+ message: `Host submission ${validated.hostSubmissionId} does not belong to thread ${validated.threadId}.`,
1708
1708
  });
1709
1709
  }
1710
1710
  // The host Attempt already owns the lane; no epoch bump happens here (plan §2.5).
@@ -1712,11 +1712,11 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
1712
1712
  const laterRows = yield* sql<Record<string, unknown>>`
1713
1713
  SELECT ${sql.literal(SUBMISSION_COLUMNS)}
1714
1714
  FROM effect_agent_submissions
1715
- WHERE conversation_id = ${validated.conversationId}
1715
+ WHERE thread_id = ${validated.threadId}
1716
1716
  AND queue_sequence > ${host.queue_sequence}
1717
1717
  ORDER BY queue_sequence ASC
1718
1718
  `.pipe(Effect.mapError(sqlFailure(operation)));
1719
- const later = yield* decodeSubmissionRows(operation, validated.conversationId, laterRows);
1719
+ const later = yield* decodeSubmissionRows(operation, validated.threadId, laterRows);
1720
1720
  const claimed: Array<JoiningClaim> = [];
1721
1721
  for (const row of later) {
1722
1722
  if (claimed.length >= validated.maxCount) break;
@@ -2561,7 +2561,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
2561
2561
  });
2562
2562
 
2563
2563
  interface ScanCursor {
2564
- readonly conversationId: string;
2564
+ readonly threadId: string;
2565
2565
  readonly queueSequence: number;
2566
2566
  }
2567
2567
 
@@ -2578,7 +2578,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
2578
2578
  SELECT ${sql.literal(SUBMISSION_COLUMNS)}
2579
2579
  FROM effect_agent_submissions
2580
2580
  WHERE state <> 'settled'
2581
- ORDER BY conversation_id ASC, queue_sequence ASC
2581
+ ORDER BY thread_id ASC, queue_sequence ASC
2582
2582
  LIMIT ${SCAN_PAGE_SIZE}
2583
2583
  `
2584
2584
  : sql<Record<string, unknown>>`
@@ -2586,13 +2586,13 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
2586
2586
  FROM effect_agent_submissions
2587
2587
  WHERE state <> 'settled'
2588
2588
  AND (
2589
- conversation_id > ${cursor.conversationId}
2589
+ thread_id > ${cursor.threadId}
2590
2590
  OR (
2591
- conversation_id = ${cursor.conversationId}
2591
+ thread_id = ${cursor.threadId}
2592
2592
  AND queue_sequence > ${cursor.queueSequence}
2593
2593
  )
2594
2594
  )
2595
- ORDER BY conversation_id ASC, queue_sequence ASC
2595
+ ORDER BY thread_id ASC, queue_sequence ASC
2596
2596
  LIMIT ${SCAN_PAGE_SIZE}
2597
2597
  `
2598
2598
  ).pipe(Effect.mapError(sqlFailure(operation)));
@@ -2605,7 +2605,7 @@ const makeServices = Effect.fn("SqliteSubmissionLedger.makeServices")(function*
2605
2605
  last === undefined || decoded.length < SCAN_PAGE_SIZE
2606
2606
  ? Option.none()
2607
2607
  : Option.some({
2608
- conversationId: last.conversation_id,
2608
+ threadId: last.thread_id,
2609
2609
  queueSequence: last.queue_sequence,
2610
2610
  });
2611
2611
  return [snapshots, next] as const;
@@ -2864,7 +2864,7 @@ export const submissionLedgerLayer: Layer.Layer<
2864
2864
 
2865
2865
  /**
2866
2866
  * A composition-root convenience Layer for the durable Submission Ledger. Point it at the
2867
- * same database file as the ConversationStore so claims fence the same producer epochs.
2867
+ * same database file as the ThreadStore so claims fence the same producer epochs.
2868
2868
  */
2869
2869
  export const ledgerLayer = (
2870
2870
  options: SqliteStorageOptions,
@@ -18,14 +18,14 @@ import {
18
18
  ScheduleStorageError,
19
19
  ScheduleStore,
20
20
  scheduleDeadline,
21
- } from "@effect-agent/session";
21
+ } from "@effect-agent/thread";
22
22
  import { Effect, Layer, Result, Schema } from "effect";
23
23
  import * as SqlClientService from "effect/unstable/sql/SqlClient";
24
24
 
25
- import type { SqliteStorageInitializationError } from "./sqlite-conversation-store.ts";
26
25
  import { initializeSqliteJournal } from "./sqlite-journal.ts";
27
26
  import type { SqliteStorageConfig } from "./sqlite-storage-config.ts";
28
27
  import type { SqliteStorageFailpoint } from "./sqlite-storage-failpoint.ts";
28
+ import type { SqliteStorageInitializationError } from "./sqlite-thread-store.ts";
29
29
 
30
30
  // Configuration and the immutable pending envelope may each carry the canonical input. Leave
31
31
  // room for JSON escaping and bounded status while rejecting an unreadable oversized row.
@@ -19,7 +19,7 @@ export class SqliteStorageConfigValue extends Schema.Class<SqliteStorageConfigVa
19
19
  * Submission ownership lease duration in milliseconds (D5). The lease is a liveness hint
20
20
  * that makes an abandoned claim reclaimable; correctness never depends on it because every
21
21
  * canonical append is fenced by producer epoch. Convenience layers default this to
22
- * `DEFAULT_OWNERSHIP_LEASE_DURATION` from `@effect-agent/session`.
22
+ * `DEFAULT_OWNERSHIP_LEASE_DURATION` from `@effect-agent/thread`.
23
23
  */
24
24
  ownershipLeaseDuration: OwnershipLeaseMillis,
25
25
  /**
@@ -0,0 +1,38 @@
1
+ import { Context, Effect, Layer, Ref } from "effect";
2
+
3
+ import {
4
+ SqliteStorageFailpoint,
5
+ type SqliteStorageFailpointHandler,
6
+ } from "./sqlite-storage-failpoint.ts";
7
+
8
+ const noFailpoint: SqliteStorageFailpointHandler = () => Effect.void;
9
+
10
+ /** Test-only control for replacing the active SQLite failpoint handler. */
11
+ export class SqliteStorageFailpointTestControl extends Context.Service<
12
+ SqliteStorageFailpointTestControl,
13
+ {
14
+ readonly clear: Effect.Effect<void>;
15
+ readonly setHandler: (handler: SqliteStorageFailpointHandler) => Effect.Effect<void>;
16
+ }
17
+ >()("@effect-agent/storage-sqlite/SqliteStorageFailpointTestControl") {
18
+ /** Reusable test Layer with a control service backed by the same handler Ref. */
19
+ static readonly layer = Layer.effectContext(
20
+ Effect.gen(function* () {
21
+ const handler = yield* Ref.make<SqliteStorageFailpointHandler>(noFailpoint);
22
+ return Context.make(
23
+ SqliteStorageFailpoint,
24
+ SqliteStorageFailpoint.of({
25
+ hit: (location) => Ref.get(handler).pipe(Effect.flatMap((current) => current(location))),
26
+ }),
27
+ ).pipe(
28
+ Context.add(
29
+ SqliteStorageFailpointTestControl,
30
+ SqliteStorageFailpointTestControl.of({
31
+ clear: Ref.set(handler, noFailpoint),
32
+ setHandler: (next) => Ref.set(handler, next),
33
+ }),
34
+ ),
35
+ );
36
+ }),
37
+ );
38
+ }
@@ -1,4 +1,4 @@
1
- import { Context, Effect, Layer, Ref } from "effect";
1
+ import { Context, Effect, Layer } from "effect";
2
2
 
3
3
  import type { SqliteStorageFailpointError, SqliteStorageFailpointLocation } from "./errors.ts";
4
4
 
@@ -8,15 +8,6 @@ export type SqliteStorageFailpointHandler = (
8
8
 
9
9
  const noFailpoint: SqliteStorageFailpointHandler = () => Effect.void;
10
10
 
11
- /** Test-only control for replacing the active SQLite failpoint handler. */
12
- export class SqliteStorageFailpointTestControl extends Context.Service<
13
- SqliteStorageFailpointTestControl,
14
- {
15
- readonly clear: Effect.Effect<void>;
16
- readonly setHandler: (handler: SqliteStorageFailpointHandler) => Effect.Effect<void>;
17
- }
18
- >()("@effect-agent/storage-sqlite/SqliteStorageFailpointTestControl") {}
19
-
20
11
  /** Explicit fault-injection authority used at SQLite operation boundaries. */
21
12
  export class SqliteStorageFailpoint extends Context.Service<
22
13
  SqliteStorageFailpoint,
@@ -26,25 +17,4 @@ export class SqliteStorageFailpoint extends Context.Service<
26
17
  >()("@effect-agent/storage-sqlite/SqliteStorageFailpoint") {
27
18
  /** Production default: no fault injection. */
28
19
  static readonly layer = Layer.succeed(this)({ hit: noFailpoint });
29
-
30
- /** Reusable test Layer with a control service backed by the same handler Ref. */
31
- static readonly layerTest = Layer.effectContext(
32
- Effect.gen(function* () {
33
- const handler = yield* Ref.make<SqliteStorageFailpointHandler>(noFailpoint);
34
- return Context.make(
35
- SqliteStorageFailpoint,
36
- SqliteStorageFailpoint.of({
37
- hit: (location) => Ref.get(handler).pipe(Effect.flatMap((current) => current(location))),
38
- }),
39
- ).pipe(
40
- Context.add(
41
- SqliteStorageFailpointTestControl,
42
- SqliteStorageFailpointTestControl.of({
43
- clear: Ref.set(handler, noFailpoint),
44
- setHandler: (next) => Ref.set(handler, next),
45
- }),
46
- ),
47
- );
48
- }),
49
- );
50
20
  }