@effect-agent/storage-memory 0.1.0-beta.11 → 0.1.0-beta.110

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.
Files changed (34) hide show
  1. package/dist/MemoryMessageDeliveryStore.d.mts +15 -0
  2. package/dist/MemoryMessageDeliveryStore.mjs +167 -0
  3. package/dist/MemoryMessageDeliveryStore.mjs.map +1 -0
  4. package/dist/MemoryScheduleStore.d.mts +10 -0
  5. package/dist/MemoryScheduleStore.mjs +169 -0
  6. package/dist/MemoryScheduleStore.mjs.map +1 -0
  7. package/dist/MemorySemanticIndex.d.mts +21 -0
  8. package/dist/MemorySemanticIndex.mjs +222 -0
  9. package/dist/MemorySemanticIndex.mjs.map +1 -0
  10. package/dist/MemorySubmissionLedger.d.mts +24 -0
  11. package/dist/MemorySubmissionLedger.mjs +1058 -0
  12. package/dist/MemorySubmissionLedger.mjs.map +1 -0
  13. package/dist/MemorySubscriptionStore.d.mts +9 -0
  14. package/dist/MemorySubscriptionStore.mjs +849 -0
  15. package/dist/MemorySubscriptionStore.mjs.map +1 -0
  16. package/dist/MemoryThreadStore.d.mts +17 -0
  17. package/dist/MemoryThreadStore.mjs +457 -0
  18. package/dist/MemoryThreadStore.mjs.map +1 -0
  19. package/dist/index.d.mts +7 -30
  20. package/dist/index.mjs +7 -1298
  21. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  22. package/package.json +1 -45
  23. package/src/MemoryMessageDeliveryStore.ts +327 -0
  24. package/src/MemoryScheduleStore.ts +328 -0
  25. package/src/MemorySemanticIndex.ts +357 -0
  26. package/src/{memory-ledger.ts → MemorySubmissionLedger.ts} +511 -107
  27. package/src/MemorySubscriptionStore.ts +1549 -0
  28. package/src/MemoryThreadStore.ts +916 -0
  29. package/src/index.ts +6 -2
  30. package/dist/index.mjs.map +0 -1
  31. package/dist/testing.d.mts +0 -2
  32. package/dist/testing.mjs +0 -2
  33. package/src/memory-storage.ts +0 -614
  34. package/src/testing.ts +0 -10
@@ -0,0 +1,457 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { Context, Crypto, Effect, Encoding, Layer, Option, PubSub, Ref, Schema, Stream } from "effect";
3
+ import { ThreadId } from "effect-agent/identifiers";
4
+ import { CanonicalRecordEnvelope, CanonicalSequence, ObservationOffset } from "effect-agent/records";
5
+ import { EMPTY_TAIL_DIGEST, digestCanonicalBatch } from "effect-agent/digest";
6
+ import { runIdForSubmission } from "effect-agent/run-journal";
7
+ import { AppendConflict, AppendResult, CheckpointRejected, FenceRejected, FencedAppendRequest, LoadCheckpointRequest, MAX_THREAD_EXPORT_RECORDS, SaveCheckpointRequest, SaveRecoveryCheckpointRequest, ThreadExport, ThreadExportRequest, ThreadMaterialization, ThreadNotMaterialized, ThreadObservation, ThreadPeerCountRequest, ThreadReadRequest, ThreadStore, ThreadStoreError, ThreadTail, ThreadTailRequest } from "effect-agent/thread-store";
8
+ //#region src/MemoryThreadStore.ts
9
+ var MemoryThreadStore_exports = /* @__PURE__ */ __exportAll({
10
+ MemoryThreadStoreLive: () => MemoryThreadStoreLive,
11
+ memoryThreadStoreLayer: () => memoryThreadStoreLayer
12
+ });
13
+ const MAX_THREADS = 256;
14
+ const MAX_RECORDS_PER_THREAD = MAX_THREAD_EXPORT_RECORDS;
15
+ const MAX_CHECKPOINTS_PER_THREAD = 1024;
16
+ const ThreadCapacity = Context.Reference("@effect-agent/storage-memory/MemoryThreadStore/ThreadCapacity", { defaultValue: () => MAX_THREADS });
17
+ const storeError = (operation, message, cause) => cause === void 0 ? ThreadStoreError.make({
18
+ operation,
19
+ message
20
+ }) : ThreadStoreError.make({
21
+ operation,
22
+ message,
23
+ cause
24
+ });
25
+ const validate = Effect.fn("MemoryThreadStore.validate")((schema, operation, value) => Schema.encodeUnknownEffect(schema)(value).pipe(Effect.flatMap(Schema.decodeUnknownEffect(schema)), Effect.mapError((error) => storeError(operation, `Invalid ${operation} request`, error))));
26
+ const decodeCanonicalSequence = Schema.decodeSync(CanonicalSequence);
27
+ const ZERO_CANONICAL_SEQUENCE = decodeCanonicalSequence(0);
28
+ const offsetSequence = Effect.fn("MemoryThreadStore.offsetSequence")((threadId, offset) => {
29
+ if (offset === void 0) return Effect.succeed(ZERO_CANONICAL_SEQUENCE);
30
+ const prefix = `memory:v1:${Encoding.encodeBase64(threadId)}:`;
31
+ const encodedSequence = offset.startsWith(prefix) ? offset.slice(prefix.length) : "";
32
+ if (!/^\d+$/.test(encodedSequence)) return Effect.fail(storeError("observe", "Malformed observation offset"));
33
+ const sequence = Number(encodedSequence);
34
+ return Number.isSafeInteger(sequence) ? Schema.decodeEffect(CanonicalSequence)(sequence).pipe(Effect.mapError(() => storeError("observe", "Malformed observation offset"))) : Effect.fail(storeError("observe", "Malformed observation offset"));
35
+ });
36
+ const observationOffset = (threadId, sequence) => Schema.decodeSync(ObservationOffset)(`memory:v1:${Encoding.encodeBase64(threadId)}:${sequence}`);
37
+ const findThread = Effect.fn("MemoryThreadStore.findThread")((state, threadId) => {
38
+ const thread = state.threads.get(threadId);
39
+ return thread === void 0 ? Effect.fail(ThreadNotMaterialized.make({ threadId })) : Effect.succeed(thread);
40
+ });
41
+ const CheckpointVersionEnvelope = Schema.Struct({ checkpoint: Schema.Struct({
42
+ threadId: ThreadId,
43
+ schemaVersion: Schema.Natural
44
+ }) });
45
+ const validateCheckpointVersion = Effect.fn("MemoryThreadStore.validateCheckpointVersion")(function* (value) {
46
+ const envelope = yield* Schema.decodeUnknownEffect(CheckpointVersionEnvelope)(value).pipe(Effect.mapError(() => storeError("saveCheckpoint", "Invalid saveCheckpoint request")));
47
+ if (envelope.checkpoint.schemaVersion !== 1) return yield* CheckpointRejected.make({
48
+ threadId: envelope.checkpoint.threadId,
49
+ reason: "unsupported-version"
50
+ });
51
+ });
52
+ const makeThreadStore = Effect.gen(function* () {
53
+ const maxThreads = yield* ThreadCapacity;
54
+ const crypto = yield* Crypto.Crypto;
55
+ const state = yield* Ref.make({ threads: /* @__PURE__ */ new Map() });
56
+ const updates = yield* PubSub.sliding(1);
57
+ yield* Effect.addFinalizer(() => PubSub.shutdown(updates));
58
+ const materialize = Effect.fn("MemoryThreadStore.materialize")((unvalidated) => Effect.gen(function* () {
59
+ const request = yield* validate(ThreadMaterialization, "materialize", unvalidated);
60
+ const decision = yield* Ref.modify(state, (current) => {
61
+ const existing = current.threads.get(request.threadId);
62
+ if (existing !== void 0) {
63
+ if (request.producerEpoch < existing.producerEpoch) return [{
64
+ _tag: "failure",
65
+ error: FenceRejected.make({
66
+ threadId: request.threadId,
67
+ actualEpoch: existing.producerEpoch,
68
+ attemptedEpoch: request.producerEpoch
69
+ })
70
+ }, current];
71
+ if (request.producerEpoch === existing.producerEpoch) return [{ _tag: "success" }, current];
72
+ const threads = new Map(current.threads);
73
+ threads.set(request.threadId, {
74
+ ...existing,
75
+ producerEpoch: request.producerEpoch
76
+ });
77
+ return [{ _tag: "success" }, { threads }];
78
+ }
79
+ if (current.threads.size >= maxThreads) return [{
80
+ _tag: "failure",
81
+ error: storeError("materialize", `In-memory thread limit ${maxThreads} exceeded`)
82
+ }, current];
83
+ const threads = new Map(current.threads);
84
+ threads.set(request.threadId, {
85
+ producerEpoch: request.producerEpoch,
86
+ tailSequence: ZERO_CANONICAL_SEQUENCE,
87
+ tailDigest: EMPTY_TAIL_DIGEST,
88
+ unverifiedWorkerInputs: /* @__PURE__ */ new Set(),
89
+ byId: /* @__PURE__ */ new Map(),
90
+ workerRecords: /* @__PURE__ */ new Map(),
91
+ peerCount: 0,
92
+ runInputs: /* @__PURE__ */ new Map(),
93
+ outstanding: /* @__PURE__ */ new Map(),
94
+ records: [],
95
+ recordIds: /* @__PURE__ */ new Set(),
96
+ batches: /* @__PURE__ */ new Map(),
97
+ tailDigests: /* @__PURE__ */ new Map([[ZERO_CANONICAL_SEQUENCE, EMPTY_TAIL_DIGEST]]),
98
+ checkpoints: /* @__PURE__ */ new Map()
99
+ });
100
+ return [{ _tag: "success" }, { threads }];
101
+ });
102
+ if (decision._tag === "failure") return yield* decision.error;
103
+ }));
104
+ const append = Effect.fn("MemoryThreadStore.append")((unvalidated) => Effect.gen(function* () {
105
+ const request = yield* validate(FencedAppendRequest, "append", unvalidated);
106
+ const digest = yield* digestCanonicalBatch(request.expectedTailDigest, request.batch).pipe(Effect.provideService(Crypto.Crypto, crypto), Effect.mapError((error) => storeError("append", error.message, error)));
107
+ const decision = yield* Effect.uninterruptible(Ref.modify(state, (current) => {
108
+ const thread = current.threads.get(request.threadId);
109
+ if (thread === void 0) return [{
110
+ _tag: "failure",
111
+ error: ThreadNotMaterialized.make({ threadId: request.threadId })
112
+ }, current];
113
+ if (request.producerEpoch !== thread.producerEpoch) return [{
114
+ _tag: "failure",
115
+ error: FenceRejected.make({
116
+ threadId: request.threadId,
117
+ actualEpoch: thread.producerEpoch,
118
+ attemptedEpoch: request.producerEpoch
119
+ })
120
+ }, current];
121
+ const previous = thread.batches.get(request.batch.batchId);
122
+ if (previous !== void 0) {
123
+ if (previous.digest !== digest) return [{
124
+ _tag: "failure",
125
+ error: AppendConflict.make({
126
+ threadId: request.threadId,
127
+ batchId: request.batch.batchId,
128
+ reason: "batch-digest"
129
+ })
130
+ }, current];
131
+ return [{
132
+ _tag: "success",
133
+ result: AppendResult.make({
134
+ firstSequence: previous.result.firstSequence,
135
+ lastSequence: previous.result.lastSequence,
136
+ tailDigest: previous.result.tailDigest,
137
+ replayed: true
138
+ }),
139
+ records: []
140
+ }, current];
141
+ }
142
+ if (request.expectedTailSequence !== thread.tailSequence || request.expectedTailDigest !== thread.tailDigest) return [{
143
+ _tag: "failure",
144
+ error: AppendConflict.make({
145
+ threadId: request.threadId,
146
+ batchId: request.batch.batchId,
147
+ reason: "tail",
148
+ actualTailSequence: thread.tailSequence,
149
+ actualTailDigest: thread.tailDigest
150
+ })
151
+ }, current];
152
+ if (thread.records.length + request.batch.records.length > MAX_RECORDS_PER_THREAD) return [{
153
+ _tag: "failure",
154
+ error: storeError("append", `In-memory record limit ${MAX_RECORDS_PER_THREAD} exceeded`)
155
+ }, current];
156
+ const batchRecordIds = /* @__PURE__ */ new Set();
157
+ for (const record of request.batch.records) {
158
+ if (thread.recordIds.has(record.recordId) || batchRecordIds.has(record.recordId)) return [{
159
+ _tag: "failure",
160
+ error: AppendConflict.make({
161
+ threadId: request.threadId,
162
+ batchId: request.batch.batchId,
163
+ reason: "record-identity"
164
+ })
165
+ }, current];
166
+ batchRecordIds.add(record.recordId);
167
+ }
168
+ const records = request.batch.records.map((record, index) => {
169
+ const sequence = decodeCanonicalSequence(thread.tailSequence + index + 1);
170
+ return CanonicalRecordEnvelope.make({
171
+ threadId: request.threadId,
172
+ batchId: request.batch.batchId,
173
+ sequence,
174
+ offset: observationOffset(request.threadId, sequence),
175
+ record
176
+ });
177
+ });
178
+ const lastSequence = decodeCanonicalSequence(thread.tailSequence + records.length);
179
+ const result = AppendResult.make({
180
+ firstSequence: decodeCanonicalSequence(thread.tailSequence + 1),
181
+ lastSequence,
182
+ tailDigest: digest,
183
+ replayed: false
184
+ });
185
+ const batches = new Map(thread.batches);
186
+ batches.set(request.batch.batchId, {
187
+ digest,
188
+ result
189
+ });
190
+ const recordIds = new Set(thread.recordIds);
191
+ for (const recordId of batchRecordIds) recordIds.add(recordId);
192
+ const tailDigests = new Map(thread.tailDigests);
193
+ tailDigests.set(lastSequence, digest);
194
+ const threads = new Map(current.threads);
195
+ const unverifiedWorkerInputs = new Set(thread.unverifiedWorkerInputs);
196
+ let peerCount = thread.peerCount;
197
+ const workerRecords = new Map(thread.workerRecords);
198
+ const byId = new Map(thread.byId);
199
+ const runInputs = new Map(thread.runInputs);
200
+ const outstanding = new Map(thread.outstanding);
201
+ for (const entry of records) {
202
+ byId.set(entry.record.recordId, entry);
203
+ const payload = entry.record.payload;
204
+ if (payload._tag === "PeerMessagePrepared") peerCount++;
205
+ const workerKey = payload._tag === "SubtreeBudgetReserved" ? `subtree:${payload.sourceSubmissionId ?? ""}` : payload._tag === "SubagentJoined" ? `joined:${payload.runId}` : [
206
+ "ThreadCreated",
207
+ "WorkerOriginRecorded",
208
+ "SubagentLineageRecorded",
209
+ "WorkerInputRequested",
210
+ "WorkerInputCompleted"
211
+ ].includes(payload._tag) ? "worker" : void 0;
212
+ if (workerKey !== void 0) workerRecords.set(workerKey, [...workerRecords.get(workerKey) ?? [], entry]);
213
+ if (payload._tag === "UserInputRecorded" && payload.kind === "user" && payload.runId !== void 0) runInputs.set(payload.runId, runInputs.has(payload.runId) ? null : entry);
214
+ if (payload._tag === "ToolCallPrepared" || payload._tag === "ToolCallUnknown" || payload._tag === "ToolCallSettled") {
215
+ const key = JSON.stringify([payload.runId, payload.toolCallId]);
216
+ if (payload._tag === "ToolCallSettled") outstanding.delete(key);
217
+ else outstanding.set(key, [...(outstanding.get(key) ?? []).filter((prior) => payload._tag !== "ToolCallUnknown" || prior.record.payload._tag !== "ToolCallPrepared"), entry]);
218
+ }
219
+ if (payload._tag === "WorkerInputRequested") outstanding.set(`worker:${payload.admission.messageId}`, [...outstanding.get(`worker:${payload.admission.messageId}`) ?? [], entry]);
220
+ if (payload._tag === "WorkerInputCompleted") {
221
+ if (payload.effectsResolved) {
222
+ outstanding.delete(`worker:${payload.messageId}`);
223
+ unverifiedWorkerInputs.delete(payload.messageId);
224
+ } else unverifiedWorkerInputs.add(payload.messageId);
225
+ }
226
+ }
227
+ threads.set(request.threadId, {
228
+ ...thread,
229
+ byId,
230
+ workerRecords,
231
+ peerCount,
232
+ runInputs,
233
+ outstanding,
234
+ unverifiedWorkerInputs,
235
+ tailSequence: lastSequence,
236
+ tailDigest: digest,
237
+ records: [...thread.records, ...records],
238
+ recordIds,
239
+ batches,
240
+ tailDigests
241
+ });
242
+ return [{
243
+ _tag: "success",
244
+ result,
245
+ records
246
+ }, { threads }];
247
+ }).pipe(Effect.tap((decision) => decision._tag === "success" && decision.records.length > 0 ? PubSub.publish(updates, void 0) : Effect.void)));
248
+ if (decision._tag === "failure") return yield* decision.error;
249
+ return decision.result;
250
+ }));
251
+ const countPeerMessages = Effect.fnUntraced(function* (request) {
252
+ yield* validate(ThreadPeerCountRequest, "countPeerMessages", request);
253
+ const thread = yield* findThread(yield* Ref.get(state), request.threadId);
254
+ return Math.min(thread.peerCount, request.limit);
255
+ });
256
+ const readSnapshot = Effect.fn("MemoryThreadStore.readSnapshot")((threadId, afterSequence, limit) => Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, threadId)), Effect.map((thread) => {
257
+ const start = afterSequence ?? ZERO_CANONICAL_SEQUENCE;
258
+ return thread.records.slice(start, start + limit);
259
+ })));
260
+ const read = (unvalidated) => Stream.unwrap(Effect.gen(function* () {
261
+ const request = yield* validate(ThreadReadRequest, "read", unvalidated);
262
+ if ("selection" in request) {
263
+ const thread = yield* findThread(yield* Ref.get(state), request.threadId);
264
+ const selection = request.selection;
265
+ if ("expectedTailSequence" in selection && (selection.expectedTailSequence !== thread.tailSequence || selection.expectedTailDigest !== thread.tailDigest)) return yield* storeError("selected read", "Canonical tail changed");
266
+ let records;
267
+ switch (selection._tag) {
268
+ case "RecordId": {
269
+ const record = thread.byId.get(selection.recordId);
270
+ records = record === void 0 ? [] : [record];
271
+ break;
272
+ }
273
+ case "RunInput": {
274
+ const input = thread.runInputs.get(selection.runId);
275
+ if (input === null) return yield* storeError("selected read", "Ambiguous original Run input");
276
+ records = input === void 0 ? [] : [input];
277
+ break;
278
+ }
279
+ case "Outstanding":
280
+ if (thread.unverifiedWorkerInputs.size > 0) return yield* storeError("selected read", "Unverified worker acknowledgement");
281
+ records = [...thread.outstanding.values()].flat();
282
+ break;
283
+ case "WorkerState": records = [
284
+ ...thread.workerRecords.get("worker") ?? [],
285
+ ...thread.workerRecords.get(`subtree:${selection.sourceSubmissionId ?? ""}`) ?? [],
286
+ ...selection.sourceSubmissionId === void 0 ? [] : thread.workerRecords.get(`joined:${runIdForSubmission(selection.sourceSubmissionId)}`) ?? []
287
+ ];
288
+ }
289
+ return Stream.fromIterable(records.filter((entry) => entry.sequence > (request.page.afterSequence ?? 0)).sort((a, b) => a.sequence - b.sequence).slice(0, request.page.limit));
290
+ }
291
+ const records = yield* readSnapshot(request.threadId, request.afterSequence, request.limit);
292
+ return Stream.fromIterable(records);
293
+ }));
294
+ const observe = (unvalidated) => Stream.unwrap(Effect.gen(function* () {
295
+ const request = yield* validate(ThreadObservation, "observe", unvalidated);
296
+ const afterSequence = yield* offsetSequence(request.threadId, request.afterOffset);
297
+ return Stream.unwrap(Effect.gen(function* () {
298
+ const subscription = yield* PubSub.subscribe(updates);
299
+ const initial = yield* readSnapshot(request.threadId, afterSequence, MAX_RECORDS_PER_THREAD);
300
+ const highWater = initial.length === 0 ? afterSequence : initial.at(-1)?.sequence ?? afterSequence;
301
+ const live = Stream.fromEffectRepeat(PubSub.take(subscription)).pipe(Stream.mapAccumEffect(() => highWater, (lastSequence) => readSnapshot(request.threadId, lastSequence, MAX_RECORDS_PER_THREAD).pipe(Effect.map((records) => [records.at(-1)?.sequence ?? lastSequence, records]))));
302
+ return Stream.fromIterable(initial).pipe(Stream.concat(live));
303
+ }));
304
+ }));
305
+ const exportThread = Effect.fn("MemoryThreadStore.export")((unvalidated) => Effect.gen(function* () {
306
+ const request = yield* validate(ThreadExportRequest, "export", unvalidated);
307
+ const thread = yield* Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, request.threadId)));
308
+ return ThreadExport.make({
309
+ format: "effect-agent/thread@1",
310
+ threadId: request.threadId,
311
+ tailSequence: thread.tailSequence,
312
+ tailDigest: thread.tailDigest,
313
+ records: thread.records
314
+ });
315
+ }));
316
+ const inspectTail = Effect.fn("MemoryThreadStore.inspectTail")((unvalidated) => Effect.gen(function* () {
317
+ const request = yield* validate(ThreadTailRequest, "inspectTail", unvalidated);
318
+ const thread = yield* Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, request.threadId)));
319
+ return ThreadTail.make({
320
+ threadId: request.threadId,
321
+ tailSequence: thread.tailSequence,
322
+ tailDigest: thread.tailDigest,
323
+ producerEpoch: thread.producerEpoch
324
+ });
325
+ }));
326
+ const saveCheckpoint = Effect.fn("MemoryThreadStore.saveCheckpoint")((unvalidated) => Effect.gen(function* () {
327
+ yield* validateCheckpointVersion(unvalidated);
328
+ const request = yield* validate(SaveCheckpointRequest, "saveCheckpoint", unvalidated);
329
+ const decision = yield* Ref.modify(state, (current) => {
330
+ const checkpoint = request.checkpoint;
331
+ const thread = current.threads.get(checkpoint.threadId);
332
+ if (thread === void 0) return [{
333
+ _tag: "failure",
334
+ error: ThreadNotMaterialized.make({ threadId: checkpoint.threadId })
335
+ }, current];
336
+ if (checkpoint.throughSequence > thread.tailSequence) return [{
337
+ _tag: "failure",
338
+ error: CheckpointRejected.make({
339
+ threadId: checkpoint.threadId,
340
+ reason: "ahead-of-tail"
341
+ })
342
+ }, current];
343
+ if (thread.tailDigests.get(checkpoint.throughSequence) !== checkpoint.tailDigest) return [{
344
+ _tag: "failure",
345
+ error: CheckpointRejected.make({
346
+ threadId: checkpoint.threadId,
347
+ reason: "digest-mismatch"
348
+ })
349
+ }, current];
350
+ if (!thread.checkpoints.has(checkpoint.throughSequence) && thread.checkpoints.size >= MAX_CHECKPOINTS_PER_THREAD) return [{
351
+ _tag: "failure",
352
+ error: storeError("saveCheckpoint", `In-memory checkpoint limit ${MAX_CHECKPOINTS_PER_THREAD} exceeded`)
353
+ }, current];
354
+ const checkpoints = new Map(thread.checkpoints);
355
+ checkpoints.set(checkpoint.throughSequence, checkpoint);
356
+ const threads = new Map(current.threads);
357
+ threads.set(checkpoint.threadId, {
358
+ ...thread,
359
+ checkpoints
360
+ });
361
+ return [{ _tag: "success" }, { threads }];
362
+ });
363
+ if (decision._tag === "failure") return yield* decision.error;
364
+ }));
365
+ const loadCheckpoint = Effect.fn("MemoryThreadStore.loadCheckpoint")((unvalidated) => Effect.gen(function* () {
366
+ const request = yield* validate(LoadCheckpointRequest, "loadCheckpoint", unvalidated);
367
+ const thread = yield* Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, request.threadId)));
368
+ const maximum = request.atOrBeforeSequence ?? thread.tailSequence;
369
+ let selected;
370
+ for (const [sequence, checkpoint] of thread.checkpoints) if (sequence <= maximum && (selected === void 0 || sequence > selected.throughSequence)) selected = checkpoint;
371
+ if (selected !== void 0 && thread.tailDigests.get(selected.throughSequence) !== selected.tailDigest) return yield* CheckpointRejected.make({
372
+ threadId: request.threadId,
373
+ reason: "digest-mismatch"
374
+ });
375
+ return Option.fromNullishOr(selected);
376
+ }));
377
+ const saveRecoveryCheckpoint = Effect.fn("MemoryThreadStore.saveRecoveryCheckpoint")(function* (unvalidated) {
378
+ const request = yield* validate(SaveRecoveryCheckpointRequest, "saveRecoveryCheckpoint", unvalidated);
379
+ const decision = yield* Ref.modify(state, (current) => {
380
+ const checkpoint = request.checkpoint;
381
+ const thread = current.threads.get(checkpoint.threadId);
382
+ if (thread === void 0) return [{
383
+ _tag: "failure",
384
+ error: ThreadNotMaterialized.make({ threadId: checkpoint.threadId })
385
+ }, current];
386
+ if (thread.producerEpoch !== request.producerEpoch) return [{
387
+ _tag: "failure",
388
+ error: FenceRejected.make({
389
+ threadId: checkpoint.threadId,
390
+ actualEpoch: thread.producerEpoch,
391
+ attemptedEpoch: request.producerEpoch
392
+ })
393
+ }, current];
394
+ if (checkpoint.throughSequence > thread.tailSequence) return [{
395
+ _tag: "failure",
396
+ error: CheckpointRejected.make({
397
+ threadId: checkpoint.threadId,
398
+ reason: "ahead-of-tail"
399
+ })
400
+ }, current];
401
+ if (thread.tailDigests.get(checkpoint.throughSequence) !== checkpoint.tailDigest) return [{
402
+ _tag: "failure",
403
+ error: CheckpointRejected.make({
404
+ threadId: checkpoint.threadId,
405
+ reason: "digest-mismatch"
406
+ })
407
+ }, current];
408
+ if ((thread.recoveryCheckpoint?.throughSequence ?? -1) > checkpoint.throughSequence) return [{ _tag: "success" }, current];
409
+ const threads = new Map(current.threads);
410
+ threads.set(checkpoint.threadId, {
411
+ ...thread,
412
+ recoveryCheckpoint: checkpoint
413
+ });
414
+ return [{ _tag: "success" }, { threads }];
415
+ });
416
+ if (decision._tag === "failure") return yield* decision.error;
417
+ });
418
+ const loadRecoveryCheckpoint = Effect.fn("MemoryThreadStore.loadRecoveryCheckpoint")(function* (unvalidated) {
419
+ const request = yield* validate(LoadCheckpointRequest, "loadRecoveryCheckpoint", unvalidated);
420
+ const thread = yield* Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, request.threadId)));
421
+ const checkpoint = thread.recoveryCheckpoint;
422
+ if (checkpoint === void 0 || checkpoint.throughSequence > (request.atOrBeforeSequence ?? thread.tailSequence)) return Option.none();
423
+ if (thread.tailDigests.get(checkpoint.throughSequence) !== checkpoint.tailDigest) return yield* CheckpointRejected.make({
424
+ threadId: request.threadId,
425
+ reason: "digest-mismatch"
426
+ });
427
+ return Option.some(checkpoint);
428
+ });
429
+ return ThreadStore.of({
430
+ countPeerMessages,
431
+ materialize,
432
+ append,
433
+ read,
434
+ observe,
435
+ export: exportThread,
436
+ inspectTail,
437
+ checkpoints: {
438
+ save: saveCheckpoint,
439
+ load: loadCheckpoint
440
+ },
441
+ recoveryCheckpoints: {
442
+ save: saveRecoveryCheckpoint,
443
+ load: loadRecoveryCheckpoint
444
+ }
445
+ });
446
+ });
447
+ /**
448
+ * In-memory canonical Thread persistence. Durable accepted work is served by the separate
449
+ * SubmissionLedger port; this Layer deliberately provides only the ThreadStore.
450
+ */
451
+ const MemoryThreadStoreLive = Layer.effect(ThreadStore, makeThreadStore);
452
+ /** Configure a finite retained Thread capacity. Invalid construction options throw immediately. */
453
+ const memoryThreadStoreLayer = (options = {}) => MemoryThreadStoreLive.pipe(Layer.provide(Layer.succeed(ThreadCapacity)(Schema.decodeSync(Schema.Int.check(Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(65536)))(options.maxThreads ?? MAX_THREADS))));
454
+ //#endregion
455
+ export { MemoryThreadStoreLive, memoryThreadStoreLayer, MemoryThreadStore_exports as t };
456
+
457
+ //# sourceMappingURL=MemoryThreadStore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MemoryThreadStore.mjs","names":[],"sources":["../src/MemoryThreadStore.ts"],"sourcesContent":["import {\n Context,\n Crypto,\n Effect,\n Encoding,\n Layer,\n Option,\n PubSub,\n Ref,\n Schema,\n Stream,\n} from \"effect\";\nimport { digestCanonicalBatch, EMPTY_TAIL_DIGEST } from \"effect-agent/digest\";\nimport { ThreadId } from \"effect-agent/identifiers\";\nimport {\n type ProducerEpoch,\n type RecordId,\n CanonicalRecordEnvelope,\n CanonicalSequence,\n ObservationOffset,\n type BatchId,\n type Digest,\n} from \"effect-agent/records\";\nimport { runIdForSubmission } from \"effect-agent/run-journal\";\nimport {\n type ThreadCheckpoint,\n ThreadPeerCountRequest,\n AppendConflict,\n AppendResult,\n CheckpointRejected,\n ThreadExportRequest,\n ThreadExport,\n ThreadMaterialization,\n ThreadNotMaterialized,\n ThreadObservation,\n ThreadReadRequest,\n ThreadStore,\n type ThreadCheckpoints,\n ThreadStoreError,\n ThreadTail,\n ThreadTailRequest,\n FenceRejected,\n FencedAppendRequest,\n LoadCheckpointRequest,\n SaveCheckpointRequest,\n SaveRecoveryCheckpointRequest,\n type ThreadRecoveryCheckpoints,\n MAX_THREAD_EXPORT_RECORDS,\n} from \"effect-agent/thread-store\";\n\nconst MAX_THREADS = 256;\nconst MAX_RECORDS_PER_THREAD = MAX_THREAD_EXPORT_RECORDS;\nconst MAX_CHECKPOINTS_PER_THREAD = 1_024;\n\nconst ThreadCapacity = Context.Reference<number>(\n \"@effect-agent/storage-memory/MemoryThreadStore/ThreadCapacity\",\n { defaultValue: () => MAX_THREADS },\n);\n\ninterface StoredBatch {\n readonly digest: Digest;\n readonly result: AppendResult;\n}\n\ninterface StoredThread {\n readonly peerCount: number;\n readonly unverifiedWorkerInputs: ReadonlySet<string>;\n readonly workerRecords: ReadonlyMap<string, ReadonlyArray<CanonicalRecordEnvelope>>;\n readonly byId: ReadonlyMap<string, CanonicalRecordEnvelope>;\n readonly runInputs: ReadonlyMap<string, CanonicalRecordEnvelope | null>;\n readonly outstanding: ReadonlyMap<string, ReadonlyArray<CanonicalRecordEnvelope>>;\n readonly producerEpoch: ProducerEpoch;\n readonly tailSequence: CanonicalSequence;\n readonly tailDigest: Digest;\n readonly records: ReadonlyArray<CanonicalRecordEnvelope>;\n readonly recordIds: ReadonlySet<RecordId>;\n readonly batches: ReadonlyMap<BatchId, StoredBatch>;\n readonly tailDigests: ReadonlyMap<CanonicalSequence, Digest>;\n readonly checkpoints: ReadonlyMap<CanonicalSequence, ThreadCheckpoint>;\n readonly recoveryCheckpoint?: ThreadCheckpoint;\n}\n\ninterface MemoryState {\n readonly threads: ReadonlyMap<ThreadId, StoredThread>;\n}\n\ntype AppendDecision =\n | {\n readonly _tag: \"failure\";\n readonly error: ThreadStoreError | ThreadNotMaterialized | AppendConflict | FenceRejected;\n }\n | {\n readonly _tag: \"success\";\n readonly result: AppendResult;\n readonly records: ReadonlyArray<CanonicalRecordEnvelope>;\n };\n\ntype MaterializeDecision =\n | { readonly _tag: \"failure\"; readonly error: ThreadStoreError | FenceRejected }\n | { readonly _tag: \"success\" };\n\ntype CheckpointDecision =\n | {\n readonly _tag: \"failure\";\n readonly error: ThreadNotMaterialized | ThreadStoreError | CheckpointRejected;\n }\n | { readonly _tag: \"success\" };\n\nconst storeError = (operation: string, message: string, cause?: unknown): ThreadStoreError =>\n cause === undefined\n ? ThreadStoreError.make({ operation, message })\n : ThreadStoreError.make({ operation, message, cause });\n\nconst validate = Effect.fn(\"MemoryThreadStore.validate\")(\n <A, I>(\n schema: Schema.Codec<A, I>,\n operation: string,\n value: unknown,\n ): Effect.Effect<A, ThreadStoreError> =>\n Schema.encodeUnknownEffect(schema)(value).pipe(\n Effect.flatMap(Schema.decodeUnknownEffect(schema)),\n Effect.mapError((error) => storeError(operation, `Invalid ${operation} request`, error)),\n ),\n);\n\nconst decodeCanonicalSequence = Schema.decodeSync(CanonicalSequence);\nconst ZERO_CANONICAL_SEQUENCE = decodeCanonicalSequence(0);\n\nconst offsetSequence = Effect.fn(\"MemoryThreadStore.offsetSequence\")((\n threadId: ThreadId,\n offset: ObservationOffset | undefined,\n): Effect.Effect<CanonicalSequence, ThreadStoreError> => {\n if (offset === undefined) return Effect.succeed(ZERO_CANONICAL_SEQUENCE);\n const prefix = `memory:v1:${Encoding.encodeBase64(threadId)}:`;\n const encodedSequence = offset.startsWith(prefix) ? offset.slice(prefix.length) : \"\";\n\n if (!/^\\d+$/.test(encodedSequence)) {\n return Effect.fail(storeError(\"observe\", \"Malformed observation offset\"));\n }\n const sequence = Number(encodedSequence);\n\n return Number.isSafeInteger(sequence)\n ? Schema.decodeEffect(CanonicalSequence)(sequence).pipe(\n Effect.mapError(() => storeError(\"observe\", \"Malformed observation offset\")),\n )\n : Effect.fail(storeError(\"observe\", \"Malformed observation offset\"));\n});\n\nconst observationOffset = (threadId: ThreadId, sequence: CanonicalSequence): ObservationOffset =>\n Schema.decodeSync(ObservationOffset)(`memory:v1:${Encoding.encodeBase64(threadId)}:${sequence}`);\n\nconst findThread = Effect.fn(\"MemoryThreadStore.findThread\")((\n state: MemoryState,\n threadId: ThreadId,\n): Effect.Effect<StoredThread, ThreadNotMaterialized> => {\n const thread = state.threads.get(threadId);\n\n return thread === undefined\n ? Effect.fail(ThreadNotMaterialized.make({ threadId }))\n : Effect.succeed(thread);\n});\n\nconst CheckpointVersionEnvelope = Schema.Struct({\n checkpoint: Schema.Struct({\n threadId: ThreadId,\n schemaVersion: Schema.Natural,\n }),\n});\n\nconst validateCheckpointVersion = Effect.fn(\"MemoryThreadStore.validateCheckpointVersion\")(\n function* (value: unknown): Effect.fn.Return<void, ThreadStoreError | CheckpointRejected> {\n const envelope = yield* Schema.decodeUnknownEffect(CheckpointVersionEnvelope)(value).pipe(\n Effect.mapError(() => storeError(\"saveCheckpoint\", \"Invalid saveCheckpoint request\")),\n );\n\n if (envelope.checkpoint.schemaVersion !== 1) {\n return yield* CheckpointRejected.make({\n threadId: envelope.checkpoint.threadId,\n reason: \"unsupported-version\",\n });\n }\n },\n);\n\nconst makeThreadStore = Effect.gen(function* () {\n const maxThreads = yield* ThreadCapacity;\n const crypto = yield* Crypto.Crypto;\n const state = yield* Ref.make<MemoryState>({ threads: new Map() });\n const updates = yield* PubSub.sliding<void>(1);\n\n yield* Effect.addFinalizer(() => PubSub.shutdown(updates));\n\n const materialize: ThreadStore[\"Service\"][\"materialize\"] = Effect.fn(\n \"MemoryThreadStore.materialize\",\n )((unvalidated) =>\n Effect.gen(function* () {\n const request = yield* validate(ThreadMaterialization, \"materialize\", unvalidated);\n\n const decision = yield* Ref.modify(\n state,\n (current): readonly [MaterializeDecision, MemoryState] => {\n const existing = current.threads.get(request.threadId);\n\n if (existing !== undefined) {\n if (request.producerEpoch < existing.producerEpoch) {\n return [\n {\n _tag: \"failure\",\n error: FenceRejected.make({\n threadId: request.threadId,\n actualEpoch: existing.producerEpoch,\n attemptedEpoch: request.producerEpoch,\n }),\n },\n current,\n ];\n }\n if (request.producerEpoch === existing.producerEpoch) {\n return [{ _tag: \"success\" }, current];\n }\n const threads = new Map(current.threads);\n\n threads.set(request.threadId, {\n ...existing,\n producerEpoch: request.producerEpoch,\n });\n\n return [{ _tag: \"success\" }, { threads }];\n }\n if (current.threads.size >= maxThreads) {\n return [\n {\n _tag: \"failure\",\n error: storeError(\"materialize\", `In-memory thread limit ${maxThreads} exceeded`),\n },\n current,\n ];\n }\n const threads = new Map(current.threads);\n\n threads.set(request.threadId, {\n producerEpoch: request.producerEpoch,\n tailSequence: ZERO_CANONICAL_SEQUENCE,\n tailDigest: EMPTY_TAIL_DIGEST,\n unverifiedWorkerInputs: new Set(),\n byId: new Map(),\n workerRecords: new Map(),\n peerCount: 0,\n runInputs: new Map(),\n outstanding: new Map(),\n records: [],\n recordIds: new Set(),\n batches: new Map(),\n tailDigests: new Map([[ZERO_CANONICAL_SEQUENCE, EMPTY_TAIL_DIGEST]]),\n checkpoints: new Map(),\n });\n\n return [{ _tag: \"success\" }, { threads }];\n },\n );\n\n if (decision._tag === \"failure\") return yield* decision.error;\n }),\n );\n\n const append: ThreadStore[\"Service\"][\"append\"] = Effect.fn(\"MemoryThreadStore.append\")(\n (unvalidated) =>\n Effect.gen(function* () {\n const request = yield* validate(FencedAppendRequest, \"append\", unvalidated);\n\n const digest = yield* digestCanonicalBatch(request.expectedTailDigest, request.batch).pipe(\n Effect.provideService(Crypto.Crypto, crypto),\n Effect.mapError((error) => storeError(\"append\", error.message, error)),\n );\n\n const decision = yield* Effect.uninterruptible(\n Ref.modify(state, (current): readonly [AppendDecision, MemoryState] => {\n const thread = current.threads.get(request.threadId);\n\n if (thread === undefined) {\n return [\n {\n _tag: \"failure\",\n error: ThreadNotMaterialized.make({\n threadId: request.threadId,\n }),\n },\n current,\n ];\n }\n if (request.producerEpoch !== thread.producerEpoch) {\n return [\n {\n _tag: \"failure\",\n error: FenceRejected.make({\n threadId: request.threadId,\n actualEpoch: thread.producerEpoch,\n attemptedEpoch: request.producerEpoch,\n }),\n },\n current,\n ];\n }\n const previous = thread.batches.get(request.batch.batchId);\n\n if (previous !== undefined) {\n if (previous.digest !== digest) {\n return [\n {\n _tag: \"failure\",\n error: AppendConflict.make({\n threadId: request.threadId,\n batchId: request.batch.batchId,\n reason: \"batch-digest\",\n }),\n },\n current,\n ];\n }\n\n return [\n {\n _tag: \"success\",\n result: AppendResult.make({\n firstSequence: previous.result.firstSequence,\n lastSequence: previous.result.lastSequence,\n tailDigest: previous.result.tailDigest,\n replayed: true,\n }),\n records: [],\n },\n current,\n ];\n }\n if (\n request.expectedTailSequence !== thread.tailSequence ||\n request.expectedTailDigest !== thread.tailDigest\n ) {\n return [\n {\n _tag: \"failure\",\n error: AppendConflict.make({\n threadId: request.threadId,\n batchId: request.batch.batchId,\n reason: \"tail\",\n actualTailSequence: thread.tailSequence,\n actualTailDigest: thread.tailDigest,\n }),\n },\n current,\n ];\n }\n if (thread.records.length + request.batch.records.length > MAX_RECORDS_PER_THREAD) {\n return [\n {\n _tag: \"failure\",\n error: storeError(\n \"append\",\n `In-memory record limit ${MAX_RECORDS_PER_THREAD} exceeded`,\n ),\n },\n current,\n ];\n }\n\n const batchRecordIds = new Set<RecordId>();\n\n for (const record of request.batch.records) {\n if (thread.recordIds.has(record.recordId) || batchRecordIds.has(record.recordId)) {\n return [\n {\n _tag: \"failure\",\n error: AppendConflict.make({\n threadId: request.threadId,\n batchId: request.batch.batchId,\n reason: \"record-identity\",\n }),\n },\n current,\n ];\n }\n batchRecordIds.add(record.recordId);\n }\n\n const records = request.batch.records.map((record, index) => {\n const sequence = decodeCanonicalSequence(thread.tailSequence + index + 1);\n\n return CanonicalRecordEnvelope.make({\n threadId: request.threadId,\n batchId: request.batch.batchId,\n sequence,\n offset: observationOffset(request.threadId, sequence),\n record,\n });\n });\n\n const lastSequence = decodeCanonicalSequence(thread.tailSequence + records.length);\n\n const result = AppendResult.make({\n firstSequence: decodeCanonicalSequence(thread.tailSequence + 1),\n lastSequence,\n tailDigest: digest,\n replayed: false,\n });\n\n const batches = new Map(thread.batches);\n\n batches.set(request.batch.batchId, { digest, result });\n const recordIds = new Set(thread.recordIds);\n\n for (const recordId of batchRecordIds) recordIds.add(recordId);\n const tailDigests = new Map(thread.tailDigests);\n\n tailDigests.set(lastSequence, digest);\n const threads = new Map(current.threads);\n\n const unverifiedWorkerInputs = new Set(thread.unverifiedWorkerInputs);\n let peerCount = thread.peerCount;\n const workerRecords = new Map(thread.workerRecords);\n const byId = new Map(thread.byId);\n const runInputs = new Map(thread.runInputs);\n const outstanding = new Map(thread.outstanding);\n\n for (const entry of records) {\n byId.set(entry.record.recordId, entry);\n const payload = entry.record.payload;\n\n if (payload._tag === \"PeerMessagePrepared\") peerCount++;\n\n const workerKey =\n payload._tag === \"SubtreeBudgetReserved\"\n ? `subtree:${payload.sourceSubmissionId ?? \"\"}`\n : payload._tag === \"SubagentJoined\"\n ? `joined:${payload.runId}`\n : [\n \"ThreadCreated\",\n \"WorkerOriginRecorded\",\n \"SubagentLineageRecorded\",\n \"WorkerInputRequested\",\n \"WorkerInputCompleted\",\n ].includes(payload._tag)\n ? \"worker\"\n : undefined;\n\n if (workerKey !== undefined)\n workerRecords.set(workerKey, [...(workerRecords.get(workerKey) ?? []), entry]);\n\n if (\n payload._tag === \"UserInputRecorded\" &&\n payload.kind === \"user\" &&\n payload.runId !== undefined\n )\n runInputs.set(payload.runId, runInputs.has(payload.runId) ? null : entry);\n if (\n payload._tag === \"ToolCallPrepared\" ||\n payload._tag === \"ToolCallUnknown\" ||\n payload._tag === \"ToolCallSettled\"\n ) {\n const key = JSON.stringify([payload.runId, payload.toolCallId]);\n\n if (payload._tag === \"ToolCallSettled\") outstanding.delete(key);\n else\n outstanding.set(key, [\n ...(outstanding.get(key) ?? []).filter(\n (prior) =>\n payload._tag !== \"ToolCallUnknown\" ||\n prior.record.payload._tag !== \"ToolCallPrepared\",\n ),\n entry,\n ]);\n }\n if (payload._tag === \"WorkerInputRequested\")\n outstanding.set(`worker:${payload.admission.messageId}`, [\n ...(outstanding.get(`worker:${payload.admission.messageId}`) ?? []),\n entry,\n ]);\n if (payload._tag === \"WorkerInputCompleted\") {\n if (payload.effectsResolved) {\n outstanding.delete(`worker:${payload.messageId}`);\n unverifiedWorkerInputs.delete(payload.messageId);\n } else unverifiedWorkerInputs.add(payload.messageId);\n }\n }\n threads.set(request.threadId, {\n ...thread,\n byId,\n workerRecords,\n peerCount,\n runInputs,\n outstanding,\n unverifiedWorkerInputs,\n tailSequence: lastSequence,\n tailDigest: digest,\n records: [...thread.records, ...records],\n recordIds,\n batches,\n tailDigests,\n });\n\n return [{ _tag: \"success\", result, records }, { threads }];\n }).pipe(\n Effect.tap((decision) =>\n decision._tag === \"success\" && decision.records.length > 0\n ? PubSub.publish(updates, undefined)\n : Effect.void,\n ),\n ),\n );\n\n if (decision._tag === \"failure\") return yield* decision.error;\n\n return decision.result;\n }),\n );\n\n const countPeerMessages: NonNullable<ThreadStore[\"Service\"][\"countPeerMessages\"]> =\n Effect.fnUntraced(function* (request) {\n yield* validate(ThreadPeerCountRequest, \"countPeerMessages\", request);\n const thread = yield* findThread(yield* Ref.get(state), request.threadId);\n\n return Math.min(thread.peerCount, request.limit);\n });\n\n const readSnapshot = Effect.fn(\"MemoryThreadStore.readSnapshot\")(\n (threadId: ThreadId, afterSequence: CanonicalSequence | undefined, limit: number) =>\n Ref.get(state).pipe(\n Effect.flatMap((current) => findThread(current, threadId)),\n Effect.map((thread) => {\n // Append assigns gap-free sequences starting at 1, so the exclusive cursor is an index.\n const start = afterSequence ?? ZERO_CANONICAL_SEQUENCE;\n\n return thread.records.slice(start, start + limit);\n }),\n ),\n );\n\n const read: ThreadStore[\"Service\"][\"read\"] = (unvalidated) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const request = yield* validate(ThreadReadRequest, \"read\", unvalidated);\n\n if (\"selection\" in request) {\n const thread = yield* findThread(yield* Ref.get(state), request.threadId);\n const selection = request.selection;\n\n if (\n \"expectedTailSequence\" in selection &&\n (selection.expectedTailSequence !== thread.tailSequence ||\n selection.expectedTailDigest !== thread.tailDigest)\n )\n return yield* storeError(\"selected read\", \"Canonical tail changed\");\n let records: ReadonlyArray<CanonicalRecordEnvelope>;\n\n switch (selection._tag) {\n case \"RecordId\": {\n const record = thread.byId.get(selection.recordId);\n\n records = record === undefined ? [] : [record];\n break;\n }\n case \"RunInput\": {\n const input = thread.runInputs.get(selection.runId);\n\n if (input === null)\n return yield* storeError(\"selected read\", \"Ambiguous original Run input\");\n records = input === undefined ? [] : [input];\n break;\n }\n case \"Outstanding\":\n if (thread.unverifiedWorkerInputs.size > 0)\n return yield* storeError(\"selected read\", \"Unverified worker acknowledgement\");\n records = [...thread.outstanding.values()].flat();\n break;\n case \"WorkerState\":\n records = [\n ...(thread.workerRecords.get(\"worker\") ?? []),\n ...(thread.workerRecords.get(`subtree:${selection.sourceSubmissionId ?? \"\"}`) ??\n []),\n ...(selection.sourceSubmissionId === undefined\n ? []\n : (thread.workerRecords.get(\n `joined:${runIdForSubmission(selection.sourceSubmissionId)}`,\n ) ?? [])),\n ];\n break;\n }\n\n return Stream.fromIterable(\n records\n .filter((entry) => entry.sequence > (request.page.afterSequence ?? 0))\n .sort((a, b) => a.sequence - b.sequence)\n .slice(0, request.page.limit),\n );\n }\n const records = yield* readSnapshot(request.threadId, request.afterSequence, request.limit);\n\n return Stream.fromIterable(records);\n }),\n );\n\n const observe: ThreadStore[\"Service\"][\"observe\"] = (unvalidated) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const request = yield* validate(ThreadObservation, \"observe\", unvalidated);\n const afterSequence = yield* offsetSequence(request.threadId, request.afterOffset);\n\n return Stream.unwrap(\n Effect.gen(function* () {\n const subscription = yield* PubSub.subscribe(updates);\n\n const initial = yield* readSnapshot(\n request.threadId,\n afterSequence,\n MAX_RECORDS_PER_THREAD,\n );\n\n const highWater =\n initial.length === 0 ? afterSequence : (initial.at(-1)?.sequence ?? afterSequence);\n\n const live = Stream.fromEffectRepeat(PubSub.take(subscription)).pipe(\n Stream.mapAccumEffect(\n () => highWater,\n (lastSequence) =>\n readSnapshot(request.threadId, lastSequence, MAX_RECORDS_PER_THREAD).pipe(\n Effect.map(\n (records) => [records.at(-1)?.sequence ?? lastSequence, records] as const,\n ),\n ),\n ),\n );\n\n return Stream.fromIterable(initial).pipe(Stream.concat(live));\n }),\n );\n }),\n );\n\n const exportThread: ThreadStore[\"Service\"][\"export\"] = Effect.fn(\"MemoryThreadStore.export\")(\n (unvalidated) =>\n Effect.gen(function* () {\n const request = yield* validate(ThreadExportRequest, \"export\", unvalidated);\n\n const thread = yield* Ref.get(state).pipe(\n Effect.flatMap((current) => findThread(current, request.threadId)),\n );\n\n return ThreadExport.make({\n format: \"effect-agent/thread@1\",\n threadId: request.threadId,\n tailSequence: thread.tailSequence,\n tailDigest: thread.tailDigest,\n records: thread.records,\n });\n }),\n );\n\n const inspectTail: ThreadStore[\"Service\"][\"inspectTail\"] = Effect.fn(\n \"MemoryThreadStore.inspectTail\",\n )((unvalidated) =>\n Effect.gen(function* () {\n const request = yield* validate(ThreadTailRequest, \"inspectTail\", unvalidated);\n\n const thread = yield* Ref.get(state).pipe(\n Effect.flatMap((current) => findThread(current, request.threadId)),\n );\n\n return ThreadTail.make({\n threadId: request.threadId,\n tailSequence: thread.tailSequence,\n tailDigest: thread.tailDigest,\n producerEpoch: thread.producerEpoch,\n });\n }),\n );\n\n const saveCheckpoint: ThreadCheckpoints[\"save\"] = Effect.fn(\"MemoryThreadStore.saveCheckpoint\")(\n (unvalidated) =>\n Effect.gen(function* () {\n yield* validateCheckpointVersion(unvalidated);\n const request = yield* validate(SaveCheckpointRequest, \"saveCheckpoint\", unvalidated);\n\n const decision = yield* Ref.modify(\n state,\n (current): readonly [CheckpointDecision, MemoryState] => {\n const checkpoint = request.checkpoint;\n const thread = current.threads.get(checkpoint.threadId);\n\n if (thread === undefined) {\n return [\n {\n _tag: \"failure\",\n error: ThreadNotMaterialized.make({\n threadId: checkpoint.threadId,\n }),\n },\n current,\n ];\n }\n if (checkpoint.throughSequence > thread.tailSequence) {\n return [\n {\n _tag: \"failure\",\n error: CheckpointRejected.make({\n threadId: checkpoint.threadId,\n reason: \"ahead-of-tail\",\n }),\n },\n current,\n ];\n }\n if (thread.tailDigests.get(checkpoint.throughSequence) !== checkpoint.tailDigest) {\n return [\n {\n _tag: \"failure\",\n error: CheckpointRejected.make({\n threadId: checkpoint.threadId,\n reason: \"digest-mismatch\",\n }),\n },\n current,\n ];\n }\n if (\n !thread.checkpoints.has(checkpoint.throughSequence) &&\n thread.checkpoints.size >= MAX_CHECKPOINTS_PER_THREAD\n ) {\n return [\n {\n _tag: \"failure\",\n error: storeError(\n \"saveCheckpoint\",\n `In-memory checkpoint limit ${MAX_CHECKPOINTS_PER_THREAD} exceeded`,\n ),\n },\n current,\n ];\n }\n const checkpoints = new Map(thread.checkpoints);\n\n checkpoints.set(checkpoint.throughSequence, checkpoint);\n const threads = new Map(current.threads);\n\n threads.set(checkpoint.threadId, { ...thread, checkpoints });\n\n return [{ _tag: \"success\" }, { threads }];\n },\n );\n\n if (decision._tag === \"failure\") return yield* decision.error;\n }),\n );\n\n const loadCheckpoint: ThreadCheckpoints[\"load\"] = Effect.fn(\"MemoryThreadStore.loadCheckpoint\")(\n (unvalidated) =>\n Effect.gen(function* () {\n const request = yield* validate(LoadCheckpointRequest, \"loadCheckpoint\", unvalidated);\n\n const thread = yield* Ref.get(state).pipe(\n Effect.flatMap((current) => findThread(current, request.threadId)),\n );\n\n const maximum = request.atOrBeforeSequence ?? thread.tailSequence;\n let selected: ThreadCheckpoint | undefined;\n\n for (const [sequence, checkpoint] of thread.checkpoints) {\n if (\n sequence <= maximum &&\n (selected === undefined || sequence > selected.throughSequence)\n ) {\n selected = checkpoint;\n }\n }\n if (\n selected !== undefined &&\n thread.tailDigests.get(selected.throughSequence) !== selected.tailDigest\n ) {\n return yield* CheckpointRejected.make({\n threadId: request.threadId,\n reason: \"digest-mismatch\",\n });\n }\n\n return Option.fromNullishOr(selected);\n }),\n );\n\n const saveRecoveryCheckpoint: ThreadRecoveryCheckpoints[\"save\"] = Effect.fn(\n \"MemoryThreadStore.saveRecoveryCheckpoint\",\n )(function* (unvalidated) {\n const request = yield* validate(\n SaveRecoveryCheckpointRequest,\n \"saveRecoveryCheckpoint\",\n unvalidated,\n );\n\n const decision = yield* Ref.modify(\n state,\n (\n current,\n ): readonly [\n CheckpointDecision | { readonly _tag: \"failure\"; readonly error: FenceRejected },\n MemoryState,\n ] => {\n const checkpoint = request.checkpoint;\n const thread = current.threads.get(checkpoint.threadId);\n\n if (thread === undefined)\n return [\n {\n _tag: \"failure\",\n error: ThreadNotMaterialized.make({ threadId: checkpoint.threadId }),\n },\n current,\n ];\n if (thread.producerEpoch !== request.producerEpoch)\n return [\n {\n _tag: \"failure\",\n error: FenceRejected.make({\n threadId: checkpoint.threadId,\n actualEpoch: thread.producerEpoch,\n attemptedEpoch: request.producerEpoch,\n }),\n },\n current,\n ];\n if (checkpoint.throughSequence > thread.tailSequence)\n return [\n {\n _tag: \"failure\",\n error: CheckpointRejected.make({\n threadId: checkpoint.threadId,\n reason: \"ahead-of-tail\",\n }),\n },\n current,\n ];\n if (thread.tailDigests.get(checkpoint.throughSequence) !== checkpoint.tailDigest)\n return [\n {\n _tag: \"failure\",\n error: CheckpointRejected.make({\n threadId: checkpoint.threadId,\n reason: \"digest-mismatch\",\n }),\n },\n current,\n ];\n if ((thread.recoveryCheckpoint?.throughSequence ?? -1) > checkpoint.throughSequence)\n return [{ _tag: \"success\" }, current];\n const threads = new Map(current.threads);\n\n threads.set(checkpoint.threadId, { ...thread, recoveryCheckpoint: checkpoint });\n\n return [{ _tag: \"success\" }, { threads }];\n },\n );\n\n if (decision._tag === \"failure\") return yield* decision.error;\n });\n\n const loadRecoveryCheckpoint: ThreadRecoveryCheckpoints[\"load\"] = Effect.fn(\n \"MemoryThreadStore.loadRecoveryCheckpoint\",\n )(function* (unvalidated) {\n const request = yield* validate(LoadCheckpointRequest, \"loadRecoveryCheckpoint\", unvalidated);\n\n const thread = yield* Ref.get(state).pipe(\n Effect.flatMap((current) => findThread(current, request.threadId)),\n );\n\n const checkpoint = thread.recoveryCheckpoint;\n\n if (\n checkpoint === undefined ||\n checkpoint.throughSequence > (request.atOrBeforeSequence ?? thread.tailSequence)\n )\n return Option.none();\n if (thread.tailDigests.get(checkpoint.throughSequence) !== checkpoint.tailDigest)\n return yield* CheckpointRejected.make({\n threadId: request.threadId,\n reason: \"digest-mismatch\",\n });\n\n return Option.some(checkpoint);\n });\n\n return ThreadStore.of({\n countPeerMessages,\n materialize,\n append,\n read,\n observe,\n export: exportThread,\n inspectTail,\n checkpoints: { save: saveCheckpoint, load: loadCheckpoint },\n recoveryCheckpoints: { save: saveRecoveryCheckpoint, load: loadRecoveryCheckpoint },\n });\n});\n\n/**\n * In-memory canonical Thread persistence. Durable accepted work is served by the separate\n * SubmissionLedger port; this Layer deliberately provides only the ThreadStore.\n */\nexport const MemoryThreadStoreLive = Layer.effect(ThreadStore, makeThreadStore);\n\n/** Configure a finite retained Thread capacity. Invalid construction options throw immediately. */\nexport const memoryThreadStoreLayer = (options: { readonly maxThreads?: number } = {}) =>\n MemoryThreadStoreLive.pipe(\n Layer.provide(\n Layer.succeed(ThreadCapacity)(\n Schema.decodeSync(\n Schema.Int.check(Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(65_536)),\n )(options.maxThreads ?? MAX_THREADS),\n ),\n ),\n );\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,30 +1,7 @@
1
- import { ConversationStore, SubmissionLedger } from "@effect-agent/session";
2
- import { Crypto, Effect, Layer, Option } from "effect";
3
- //#region src/memory-ledger.d.ts
4
- /** Construction options for the in-memory reference SubmissionLedger. */
5
- interface MemorySubmissionLedgerOptions {
6
- /**
7
- * Test-only fault seam for `resolveAdmission` (SUB-031): when the effect yields a reason,
8
- * the resolution answers `Indeterminate` with it instead of consulting the store — modelling
9
- * an authoritative child owner that is temporarily unreachable. `Option.none()` restores the
10
- * store-derived answer. Ledger state is never mutated by the fault.
11
- */
12
- readonly resolveAdmissionFault?: Effect.Effect<Option.Option<string>>;
13
- }
14
- /**
15
- * In-memory reference SubmissionLedger Layer (durability `non-durable`). All state lives in one
16
- * `Ref` owned by the Layer's Scope; no daemon fibers are spawned and no wall clock is consulted.
17
- */
18
- declare const memorySubmissionLedgerLayer: (options?: MemorySubmissionLedgerOptions) => Layer.Layer<SubmissionLedger>;
19
- declare const MemorySubmissionLedgerLive: Layer.Layer<SubmissionLedger>;
20
- //#endregion
21
- //#region src/memory-storage.d.ts
22
- declare const MemoryConversationStoreLive: Layer.Layer<ConversationStore, never, Crypto.Crypto>;
23
- /**
24
- * In-memory canonical Conversation persistence. Durable accepted work is served by the separate
25
- * SubmissionLedger port; this Layer deliberately provides only the ConversationStore.
26
- */
27
- declare const MemoryStorageLive: Layer.Layer<ConversationStore, never, Crypto.Crypto>;
28
- //#endregion
29
- export { MemoryConversationStoreLive, MemoryStorageLive, MemorySubmissionLedgerLive, MemorySubmissionLedgerOptions, memorySubmissionLedgerLayer };
30
- //# sourceMappingURL=index.d.mts.map
1
+ import { t as MemoryMessageDeliveryStore_d_exports } from "./MemoryMessageDeliveryStore.mjs";
2
+ import { t as MemoryScheduleStore_d_exports } from "./MemoryScheduleStore.mjs";
3
+ import { t as MemorySemanticIndex_d_exports } from "./MemorySemanticIndex.mjs";
4
+ import { t as MemorySubmissionLedger_d_exports } from "./MemorySubmissionLedger.mjs";
5
+ import { t as MemorySubscriptionStore_d_exports } from "./MemorySubscriptionStore.mjs";
6
+ import { t as MemoryThreadStore_d_exports } from "./MemoryThreadStore.mjs";
7
+ export { MemoryMessageDeliveryStore_d_exports as MemoryMessageDeliveryStore, MemoryScheduleStore_d_exports as MemoryScheduleStore, MemorySemanticIndex_d_exports as MemorySemanticIndex, MemorySubmissionLedger_d_exports as MemorySubmissionLedger, MemorySubscriptionStore_d_exports as MemorySubscriptionStore, MemoryThreadStore_d_exports as MemoryThreadStore };