@effect-agent/storage-memory 0.1.0-beta.83 → 0.1.0-beta.85

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.
@@ -30,7 +30,7 @@ const offsetSequence = Effect.fn("MemoryThreadStore.offsetSequence")((threadId,
30
30
  const encodedSequence = offset.startsWith(prefix) ? offset.slice(prefix.length) : "";
31
31
  if (!/^\d+$/.test(encodedSequence)) return Effect.fail(storeError("observe", "Malformed observation offset"));
32
32
  const sequence = Number(encodedSequence);
33
- return Number.isSafeInteger(sequence) ? Schema.decodeUnknownEffect(CanonicalSequence)(sequence).pipe(Effect.mapError(() => storeError("observe", "Malformed observation offset"))) : Effect.fail(storeError("observe", "Malformed observation offset"));
33
+ return Number.isSafeInteger(sequence) ? Schema.decodeEffect(CanonicalSequence)(sequence).pipe(Effect.mapError(() => storeError("observe", "Malformed observation offset"))) : Effect.fail(storeError("observe", "Malformed observation offset"));
34
34
  });
35
35
  const observationOffset = (threadId, sequence) => Schema.decodeSync(ObservationOffset)(`memory:v1:${Encoding.encodeBase64(threadId)}:${sequence}`);
36
36
  const findThread = Effect.fn("MemoryThreadStore.findThread")((state, threadId) => {
@@ -1 +1 @@
1
- {"version":3,"file":"MemoryThreadStore.mjs","names":[],"sources":["../src/MemoryThreadStore.ts"],"sourcesContent":["import { ThreadId } from \"@effect-agent/core/Identifiers\";\nimport { digestCanonicalBatch, EMPTY_TAIL_DIGEST } from \"@effect-agent/thread/Digest\";\nimport {\n type ProducerEpoch,\n type RecordId,\n CanonicalRecordEnvelope,\n CanonicalSequence,\n ObservationOffset,\n type BatchId,\n type Digest,\n} from \"@effect-agent/thread/Records\";\nimport {\n type ThreadCheckpoint,\n AppendConflict,\n AppendResult,\n CheckpointRejected,\n ThreadExportRequest,\n ThreadExport,\n ThreadMaterialization,\n ThreadNotMaterialized,\n ThreadObservation,\n ThreadRead,\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/ThreadStore\";\nimport {\n Context,\n Crypto,\n Effect,\n Encoding,\n Layer,\n Option,\n PubSub,\n Ref,\n Schema,\n Stream,\n} from \"effect\";\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 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.decodeUnknownEffect(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 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 threads.set(request.threadId, {\n ...thread,\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 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(ThreadRead, \"read\", unvalidated);\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 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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"}
1
+ {"version":3,"file":"MemoryThreadStore.mjs","names":[],"sources":["../src/MemoryThreadStore.ts"],"sourcesContent":["import { ThreadId } from \"@effect-agent/core/Identifiers\";\nimport { digestCanonicalBatch, EMPTY_TAIL_DIGEST } from \"@effect-agent/thread/Digest\";\nimport {\n type ProducerEpoch,\n type RecordId,\n CanonicalRecordEnvelope,\n CanonicalSequence,\n ObservationOffset,\n type BatchId,\n type Digest,\n} from \"@effect-agent/thread/Records\";\nimport {\n type ThreadCheckpoint,\n AppendConflict,\n AppendResult,\n CheckpointRejected,\n ThreadExportRequest,\n ThreadExport,\n ThreadMaterialization,\n ThreadNotMaterialized,\n ThreadObservation,\n ThreadRead,\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/ThreadStore\";\nimport {\n Context,\n Crypto,\n Effect,\n Encoding,\n Layer,\n Option,\n PubSub,\n Ref,\n Schema,\n Stream,\n} from \"effect\";\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 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 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 threads.set(request.threadId, {\n ...thread,\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 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(ThreadRead, \"read\", unvalidated);\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 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/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@effect-agent/storage-memory","version":"0.1.0-beta.83","dependencies":{"@effect-agent/core":"0.1.0-beta.83","@effect-agent/thread":"0.1.0-beta.83"},"devDependencies":{"@effect-agent/engine":"0.1.0-beta.83","@effect/platform-node":"4.0.0-rc.112","@effect/vitest":"4.0.0-rc.112","effect":"4.0.0-rc.112","typescript":"7.0.2","vite-plus":"0.3.0"},"peerDependencies":{"effect":"^4.0.0-rc.112"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./MemoryMessageDeliveryStore":{"types":"./dist/MemoryMessageDeliveryStore.d.mts","default":"./dist/MemoryMessageDeliveryStore.mjs"},"./MemoryScheduleStore":{"types":"./dist/MemoryScheduleStore.d.mts","default":"./dist/MemoryScheduleStore.mjs"},"./MemorySemanticIndex":{"types":"./dist/MemorySemanticIndex.d.mts","default":"./dist/MemorySemanticIndex.mjs"},"./MemorySubmissionLedger":{"types":"./dist/MemorySubmissionLedger.d.mts","default":"./dist/MemorySubmissionLedger.mjs"},"./MemorySubscriptionStore":{"types":"./dist/MemorySubscriptionStore.d.mts","default":"./dist/MemorySubscriptionStore.mjs"},"./MemoryThreadStore":{"types":"./dist/MemoryThreadStore.d.mts","default":"./dist/MemoryThreadStore.mjs"}},"description":"In-memory Thread store and submission ledger reference adapters for Effect Agent tests and conformance suites.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/storage-memory"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test --passWithNoTests"}}
1
+ {"name":"@effect-agent/storage-memory","version":"0.1.0-beta.85","dependencies":{"@effect-agent/core":"0.1.0-beta.85","@effect-agent/thread":"0.1.0-beta.85"},"devDependencies":{"@effect-agent/engine":"0.1.0-beta.85","@effect/platform-node":"4.0.0-rc.112","@effect/vitest":"4.0.0-rc.112","effect":"4.0.0-rc.112","typescript":"7.0.2","vite-plus":"0.3.0"},"peerDependencies":{"effect":"^4.0.0-rc.112"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./MemoryMessageDeliveryStore":{"types":"./dist/MemoryMessageDeliveryStore.d.mts","default":"./dist/MemoryMessageDeliveryStore.mjs"},"./MemoryScheduleStore":{"types":"./dist/MemoryScheduleStore.d.mts","default":"./dist/MemoryScheduleStore.mjs"},"./MemorySemanticIndex":{"types":"./dist/MemorySemanticIndex.d.mts","default":"./dist/MemorySemanticIndex.mjs"},"./MemorySubmissionLedger":{"types":"./dist/MemorySubmissionLedger.d.mts","default":"./dist/MemorySubmissionLedger.mjs"},"./MemorySubscriptionStore":{"types":"./dist/MemorySubscriptionStore.d.mts","default":"./dist/MemorySubscriptionStore.mjs"},"./MemoryThreadStore":{"types":"./dist/MemoryThreadStore.d.mts","default":"./dist/MemoryThreadStore.mjs"}},"description":"In-memory Thread store and submission ledger reference adapters for Effect Agent tests and conformance suites.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/storage-memory"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test --passWithNoTests"}}
@@ -13,6 +13,8 @@ import {
13
13
  messageDeliveryDeadline,
14
14
  messageDeliveryKeyString,
15
15
  messageDeliveryUsesCapacity,
16
+ isWorkerUpdateDelivery,
17
+ messageDeliveryCapacity,
16
18
  sameMessageDeliveryIdentity,
17
19
  validateMessageDelivery,
18
20
  } from "@effect-agent/thread/MessageDelivery";
@@ -134,12 +136,16 @@ export const memoryMessageDeliveryStoreLayer = (
134
136
  }
135
137
 
136
138
  const owned = (yield* all()).filter(
137
- (record) => record.key.ownerThreadId === input.key.ownerThreadId,
139
+ (record) =>
140
+ record.key.ownerThreadId === input.key.ownerThreadId &&
141
+ isWorkerUpdateDelivery(record) === isWorkerUpdateDelivery(input),
138
142
  );
139
143
 
144
+ const capacity = messageDeliveryCapacity(config, isWorkerUpdateDelivery(input));
145
+
140
146
  if (
141
- owned.length >= config.maxRetainedPerOwner ||
142
- owned.filter(messageDeliveryUsesCapacity).length >= config.maxPendingPerOwner
147
+ owned.length >= capacity.retained ||
148
+ owned.filter(messageDeliveryUsesCapacity).length >= capacity.pending
143
149
  ) {
144
150
  return yield* MessageDeliveryError.make({
145
151
  reason: "capacity",
@@ -204,6 +210,8 @@ export const memoryMessageDeliveryStoreLayer = (
204
210
  });
205
211
 
206
212
  return MessageDeliveryStore.of({
213
+ limits: config,
214
+ maxStoredValueBytes,
207
215
  insert,
208
216
  get,
209
217
  change,
@@ -221,9 +221,9 @@ const makeScheduleStore = Effect.gen(function* () {
221
221
  let count = 0;
222
222
 
223
223
  for (const text of current.records.values()) {
224
- const candidate = Schema.decodeUnknownResult(
225
- Schema.fromJsonString(ScheduleRecord),
226
- )(text);
224
+ const candidate = Schema.decodeResult(Schema.fromJsonString(ScheduleRecord))(
225
+ text,
226
+ );
227
227
 
228
228
  if (Result.isFailure(candidate))
229
229
  return [Result.fail(storageError("change", "corrupt")), current];
@@ -7,7 +7,7 @@ import {
7
7
  type ThreadId,
8
8
  type SettlementId,
9
9
  } from "@effect-agent/core/Identifiers";
10
- import { MessageAdmission } from "@effect-agent/core/Messaging";
10
+ import { InputMessage } from "@effect-agent/core/Messaging";
11
11
  import {
12
12
  PersistedJson,
13
13
  WorkerAdmission,
@@ -304,7 +304,7 @@ const toSnapshot = (row: SubmissionRow): SubmissionSnapshot =>
304
304
  ...(row.messageAdmissionJson === undefined
305
305
  ? {}
306
306
  : {
307
- messageAdmission: Schema.decodeSync(Schema.fromJsonString(MessageAdmission))(
307
+ messageAdmission: Schema.decodeSync(Schema.fromJsonString(InputMessage))(
308
308
  row.messageAdmissionJson,
309
309
  ),
310
310
  }),
@@ -444,7 +444,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
444
444
  const messageAdmissionJson =
445
445
  request.messageAdmission === undefined
446
446
  ? undefined
447
- : yield* Schema.encodeEffect(Schema.fromJsonString(MessageAdmission))(
447
+ : yield* Schema.encodeEffect(Schema.fromJsonString(InputMessage))(
448
448
  request.messageAdmission,
449
449
  ).pipe(
450
450
  Effect.mapError(() => ledgerError("admit", "Invalid message admission metadata")),
@@ -492,10 +492,10 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
492
492
  ),
493
493
  request.workerAdmission,
494
494
  ) ||
495
- !Schema.toEquivalence(Schema.optional(MessageAdmission))(
495
+ !Schema.toEquivalence(Schema.optional(InputMessage))(
496
496
  existing.row.messageAdmissionJson === undefined
497
497
  ? undefined
498
- : Schema.decodeSync(Schema.fromJsonString(MessageAdmission))(
498
+ : Schema.decodeSync(Schema.fromJsonString(InputMessage))(
499
499
  existing.row.messageAdmissionJson,
500
500
  ),
501
501
  request.messageAdmission,
@@ -132,7 +132,7 @@ const offsetSequence = Effect.fn("MemoryThreadStore.offsetSequence")((
132
132
  const sequence = Number(encodedSequence);
133
133
 
134
134
  return Number.isSafeInteger(sequence)
135
- ? Schema.decodeUnknownEffect(CanonicalSequence)(sequence).pipe(
135
+ ? Schema.decodeEffect(CanonicalSequence)(sequence).pipe(
136
136
  Effect.mapError(() => storeError("observe", "Malformed observation offset")),
137
137
  )
138
138
  : Effect.fail(storeError("observe", "Malformed observation offset"));