@effect-agent/storage-memory 0.1.0-beta.113 → 0.1.0-beta.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/MemoryMessageDeliveryStore.mjs +26 -3
- package/dist/MemoryMessageDeliveryStore.mjs.map +1 -1
- package/dist/MemorySubmissionLedger.mjs +95 -8
- package/dist/MemorySubmissionLedger.mjs.map +1 -1
- package/dist/MemoryThreadStore.mjs +4 -0
- package/dist/MemoryThreadStore.mjs.map +1 -1
- package/package.json +1 -1
- package/src/MemoryMessageDeliveryStore.ts +61 -15
- package/src/MemorySubmissionLedger.ts +171 -6
- package/src/MemoryThreadStore.ts +10 -0
|
@@ -202,6 +202,7 @@ const makeThreadStore = Effect.gen(function* () {
|
|
|
202
202
|
byId.set(entry.record.recordId, entry);
|
|
203
203
|
const payload = entry.record.payload;
|
|
204
204
|
if (payload._tag === "PeerMessagePrepared") peerCount++;
|
|
205
|
+
if ((payload._tag === "UserInputRecorded" || payload._tag === "RunStarted") && payload.runId !== void 0) workerRecords.set(`execution:${payload._tag}`, [entry]);
|
|
205
206
|
const workerKey = payload._tag === "SubtreeBudgetReserved" ? `subtree:${payload.sourceSubmissionId ?? ""}` : payload._tag === "SubagentJoined" ? `joined:${payload.runId}` : [
|
|
206
207
|
"ThreadCreated",
|
|
207
208
|
"WorkerOriginRecorded",
|
|
@@ -280,6 +281,9 @@ const makeThreadStore = Effect.gen(function* () {
|
|
|
280
281
|
if (thread.unverifiedWorkerInputs.size > 0) return yield* storeError("selected read", "Unverified worker acknowledgement");
|
|
281
282
|
records = [...thread.outstanding.values()].flat();
|
|
282
283
|
break;
|
|
284
|
+
case "WorkerExecution":
|
|
285
|
+
records = ["UserInputRecorded", "RunStarted"].flatMap((tag) => thread.workerRecords.get(`execution:${tag}`) ?? []);
|
|
286
|
+
break;
|
|
283
287
|
case "WorkerState": records = [
|
|
284
288
|
...thread.workerRecords.get("worker") ?? [],
|
|
285
289
|
...thread.workerRecords.get(`subtree:${selection.sourceSubmissionId ?? ""}`) ?? [],
|
|
@@ -1 +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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"}
|
|
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 if (\n (payload._tag === \"UserInputRecorded\" || payload._tag === \"RunStarted\") &&\n payload.runId !== undefined\n )\n workerRecords.set(`execution:${payload._tag}`, [entry]);\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 \"WorkerExecution\":\n records = [\"UserInputRecorded\", \"RunStarted\"].flatMap(\n (tag) => thread.workerRecords.get(`execution:${tag}`) ?? [],\n );\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/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@effect-agent/storage-memory","version":"0.1.0-beta.
|
|
1
|
+
{"name":"@effect-agent/storage-memory","version":"0.1.0-beta.115","dependencies":{"effect-agent":"0.1.0-beta.115"},"devDependencies":{"@effect/platform-node":"4.0.0-rc.116","@effect/vitest":"4.0.0-rc.116","effect":"4.0.0-rc.116","typescript":"7.0.2","vite-plus":"0.3.2"},"peerDependencies":{"effect":"^4.0.0-rc.116"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./memory-message-delivery-store":{"types":"./dist/MemoryMessageDeliveryStore.d.mts","default":"./dist/MemoryMessageDeliveryStore.mjs"},"./memory-schedule-store":{"types":"./dist/MemoryScheduleStore.d.mts","default":"./dist/MemoryScheduleStore.mjs"},"./memory-semantic-index":{"types":"./dist/MemorySemanticIndex.d.mts","default":"./dist/MemorySemanticIndex.mjs"},"./memory-submission-ledger":{"types":"./dist/MemorySubmissionLedger.d.mts","default":"./dist/MemorySubmissionLedger.mjs"},"./memory-subscription-store":{"types":"./dist/MemorySubscriptionStore.d.mts","default":"./dist/MemorySubscriptionStore.mjs"},"./memory-thread-store":{"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"}}
|
|
@@ -58,6 +58,7 @@ export const memoryMessageDeliveryStoreLayer = (
|
|
|
58
58
|
const state = yield* Ref.make({
|
|
59
59
|
records: new Map<string, string>(),
|
|
60
60
|
pending: new Map<ThreadId, ReadonlySet<MessageDeliveryKey["messageId"]>>(),
|
|
61
|
+
workers: new Map<string, ReadonlySet<MessageDeliveryKey["messageId"]>>(),
|
|
61
62
|
});
|
|
62
63
|
|
|
63
64
|
const commit = (record: MessageDeliveryRecord, encoded: string) =>
|
|
@@ -72,7 +73,36 @@ export const memoryMessageDeliveryStoreLayer = (
|
|
|
72
73
|
if (keys.size === 0) pending.delete(record.key.ownerThreadId);
|
|
73
74
|
else pending.set(record.key.ownerThreadId, keys);
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
const workers = new Map(current.workers);
|
|
77
|
+
const admission = record.envelope.workerAdmission;
|
|
78
|
+
|
|
79
|
+
if (admission !== undefined) {
|
|
80
|
+
const worker = admission.origin.worker;
|
|
81
|
+
|
|
82
|
+
const startKey = JSON.stringify([
|
|
83
|
+
record.key.ownerThreadId,
|
|
84
|
+
worker.delegationId,
|
|
85
|
+
worker.targetAgentId,
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
if (record.key.messageId === admission.origin.firstMessageId)
|
|
89
|
+
workers.set(
|
|
90
|
+
startKey,
|
|
91
|
+
new Set([...(workers.get(startKey) ?? []), record.key.messageId]),
|
|
92
|
+
);
|
|
93
|
+
const pendingKey = JSON.stringify([record.key.ownerThreadId, worker.threadId]);
|
|
94
|
+
const inputs = new Set(workers.get(pendingKey));
|
|
95
|
+
|
|
96
|
+
if (
|
|
97
|
+
(record.status === "pending" || record.status === "parked") &&
|
|
98
|
+
record.receipt === null
|
|
99
|
+
)
|
|
100
|
+
inputs.add(record.key.messageId);
|
|
101
|
+
else inputs.delete(record.key.messageId);
|
|
102
|
+
workers.set(pendingKey, inputs);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { records: new Map(current.records).set(key, encoded), pending, workers };
|
|
76
106
|
});
|
|
77
107
|
|
|
78
108
|
const lock = yield* Semaphore.make(1);
|
|
@@ -237,20 +267,36 @@ export const memoryMessageDeliveryStoreLayer = (
|
|
|
237
267
|
|
|
238
268
|
const current = yield* Ref.get(state);
|
|
239
269
|
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
270
|
+
const workerKey =
|
|
271
|
+
input.workerStarts !== undefined
|
|
272
|
+
? JSON.stringify([
|
|
273
|
+
input.ownerThreadId,
|
|
274
|
+
input.workerStarts.delegationId,
|
|
275
|
+
input.workerStarts.targetAgentId,
|
|
276
|
+
])
|
|
277
|
+
: input.pendingWorker !== undefined
|
|
278
|
+
? JSON.stringify([input.ownerThreadId, input.pendingWorker])
|
|
279
|
+
: undefined;
|
|
280
|
+
|
|
281
|
+
const retained =
|
|
282
|
+
workerKey !== undefined || input.pendingOnly
|
|
283
|
+
? yield* Effect.forEach(
|
|
284
|
+
[
|
|
285
|
+
...((workerKey === undefined
|
|
286
|
+
? current.pending.get(input.ownerThreadId)
|
|
287
|
+
: current.workers.get(workerKey)) ?? []),
|
|
288
|
+
]
|
|
289
|
+
.filter((messageId) => input.after === undefined || messageId > input.after)
|
|
290
|
+
.sort()
|
|
291
|
+
.slice(0, input.limit + 1),
|
|
292
|
+
(messageId) =>
|
|
293
|
+
decode(
|
|
294
|
+
current.records.get(
|
|
295
|
+
messageDeliveryKeyString({ ownerThreadId: input.ownerThreadId, messageId }),
|
|
296
|
+
) ?? "",
|
|
297
|
+
),
|
|
298
|
+
)
|
|
299
|
+
: yield* all();
|
|
254
300
|
|
|
255
301
|
const records = retained
|
|
256
302
|
.filter(
|
|
@@ -36,6 +36,9 @@ import {
|
|
|
36
36
|
import {
|
|
37
37
|
type ParentLinkage,
|
|
38
38
|
AbortCommand,
|
|
39
|
+
WorkerStopCommand,
|
|
40
|
+
WorkerLedgerState,
|
|
41
|
+
workerTerminalFromRecord,
|
|
39
42
|
AdmissionAdmitted,
|
|
40
43
|
AdmissionConflict,
|
|
41
44
|
AdmissionPolicyError,
|
|
@@ -235,6 +238,9 @@ interface LedgerState {
|
|
|
235
238
|
readonly lanes: ReadonlyMap<ThreadId, LaneState>;
|
|
236
239
|
readonly childReservations: ReadonlyMap<ChildReservationId, StoredChildReservation>;
|
|
237
240
|
readonly mintCounter: number;
|
|
241
|
+
readonly latestByThread: ReadonlyMap<ThreadId, SubmissionId>;
|
|
242
|
+
readonly activeByThread: ReadonlyMap<ThreadId, ReadonlySet<SubmissionId>>;
|
|
243
|
+
readonly stoppedWorkers: ReadonlyMap<ThreadId, WorkerLedgerState["terminal"]>;
|
|
238
244
|
}
|
|
239
245
|
|
|
240
246
|
type Decision<A, E> =
|
|
@@ -361,10 +367,18 @@ const ownsLane = (
|
|
|
361
367
|
stored.ownership.ownershipToken === ownershipToken &&
|
|
362
368
|
stored.ownership.producerEpoch === laneEpoch(state, stored.row.threadId);
|
|
363
369
|
|
|
364
|
-
const withSubmission = (state: LedgerState, stored: StoredSubmission): LedgerState =>
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
370
|
+
const withSubmission = (state: LedgerState, stored: StoredSubmission): LedgerState => {
|
|
371
|
+
const active = new Set(state.activeByThread.get(stored.row.threadId));
|
|
372
|
+
|
|
373
|
+
if (stored.row.state === "settled") active.delete(stored.row.submissionId);
|
|
374
|
+
else active.add(stored.row.submissionId);
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
...state,
|
|
378
|
+
submissions: new Map(state.submissions).set(stored.row.submissionId, stored),
|
|
379
|
+
activeByThread: new Map(state.activeByThread).set(stored.row.threadId, active),
|
|
380
|
+
};
|
|
381
|
+
};
|
|
368
382
|
|
|
369
383
|
const withChildReservation = (
|
|
370
384
|
state: LedgerState,
|
|
@@ -432,6 +446,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
432
446
|
lanes: new Map(),
|
|
433
447
|
childReservations: new Map(),
|
|
434
448
|
mintCounter: 0,
|
|
449
|
+
stoppedWorkers: new Map(),
|
|
450
|
+
latestByThread: new Map(),
|
|
451
|
+
activeByThread: new Map(),
|
|
435
452
|
});
|
|
436
453
|
|
|
437
454
|
const admissionFence = yield* SubmissionAdmissionFence;
|
|
@@ -545,6 +562,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
545
562
|
];
|
|
546
563
|
}
|
|
547
564
|
|
|
565
|
+
if (current.stoppedWorkers.has(request.threadId))
|
|
566
|
+
return [
|
|
567
|
+
failure(AdmissionPolicyError.make({ reason: "refused", code: "worker-stopped" })),
|
|
568
|
+
current,
|
|
569
|
+
];
|
|
570
|
+
|
|
548
571
|
// A Thread's first admission fixes its worker origin before canonical materialization.
|
|
549
572
|
const first = [...current.submissions.values()].find(
|
|
550
573
|
({ row }) => row.threadId === request.threadId,
|
|
@@ -669,7 +692,24 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
669
692
|
replayed: false,
|
|
670
693
|
}),
|
|
671
694
|
),
|
|
672
|
-
{
|
|
695
|
+
{
|
|
696
|
+
...current,
|
|
697
|
+
submissions,
|
|
698
|
+
admissionIndex,
|
|
699
|
+
lanes,
|
|
700
|
+
mintCounter,
|
|
701
|
+
latestByThread: new Map(current.latestByThread).set(
|
|
702
|
+
request.threadId,
|
|
703
|
+
row.submissionId,
|
|
704
|
+
),
|
|
705
|
+
activeByThread: new Map(current.activeByThread).set(
|
|
706
|
+
request.threadId,
|
|
707
|
+
new Set([
|
|
708
|
+
...(current.activeByThread.get(request.threadId) ?? []),
|
|
709
|
+
row.submissionId,
|
|
710
|
+
]),
|
|
711
|
+
),
|
|
712
|
+
},
|
|
673
713
|
];
|
|
674
714
|
},
|
|
675
715
|
);
|
|
@@ -1196,7 +1236,63 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1196
1236
|
];
|
|
1197
1237
|
}
|
|
1198
1238
|
|
|
1199
|
-
const
|
|
1239
|
+
const terminal =
|
|
1240
|
+
stored.row.workerAdmissionJson === undefined
|
|
1241
|
+
? undefined
|
|
1242
|
+
: workerTerminalFromRecord(toSnapshot(stored.row), reservation.record);
|
|
1243
|
+
|
|
1244
|
+
const latestId = current.latestByThread.get(stored.row.threadId);
|
|
1245
|
+
const latest = latestId === undefined ? undefined : current.submissions.get(latestId);
|
|
1246
|
+
|
|
1247
|
+
const pendingNewer =
|
|
1248
|
+
terminal === "completed" &&
|
|
1249
|
+
latest !== undefined &&
|
|
1250
|
+
latest.row.queueSequence > stored.row.queueSequence &&
|
|
1251
|
+
!(
|
|
1252
|
+
latest.joinedHostSubmissionId === stored.row.submissionId &&
|
|
1253
|
+
latest.inputApplied !== undefined
|
|
1254
|
+
);
|
|
1255
|
+
|
|
1256
|
+
let sealed = current;
|
|
1257
|
+
|
|
1258
|
+
if (
|
|
1259
|
+
terminal !== undefined &&
|
|
1260
|
+
!pendingNewer &&
|
|
1261
|
+
!current.stoppedWorkers.has(stored.row.threadId)
|
|
1262
|
+
) {
|
|
1263
|
+
const submissions = new Map(current.submissions);
|
|
1264
|
+
|
|
1265
|
+
for (const id of current.activeByThread.get(stored.row.threadId) ?? []) {
|
|
1266
|
+
const other = submissions.get(id);
|
|
1267
|
+
|
|
1268
|
+
if (other === undefined) continue;
|
|
1269
|
+
if (
|
|
1270
|
+
id !== stored.row.submissionId &&
|
|
1271
|
+
!(
|
|
1272
|
+
other.joinedHostSubmissionId === stored.row.submissionId &&
|
|
1273
|
+
other.inputApplied !== undefined
|
|
1274
|
+
) &&
|
|
1275
|
+
other.abortIntent === undefined
|
|
1276
|
+
) {
|
|
1277
|
+
submissions.set(id, {
|
|
1278
|
+
...other,
|
|
1279
|
+
abortIntent: AbortIntent.make({
|
|
1280
|
+
submissionId: id,
|
|
1281
|
+
author: stored.row.principal,
|
|
1282
|
+
reason: `Worker assignment ${terminal}`,
|
|
1283
|
+
requestedAt: utc(nowMillis),
|
|
1284
|
+
}),
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
sealed = {
|
|
1289
|
+
...current,
|
|
1290
|
+
submissions,
|
|
1291
|
+
stoppedWorkers: new Map(current.stoppedWorkers).set(stored.row.threadId, terminal),
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
const next = withSubmission(sealed, {
|
|
1200
1296
|
...stored,
|
|
1201
1297
|
row: { ...stored.row, state: "settled", settledOutcome: reservation.outcome },
|
|
1202
1298
|
ownership: undefined,
|
|
@@ -1225,6 +1321,71 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1225
1321
|
}),
|
|
1226
1322
|
);
|
|
1227
1323
|
|
|
1324
|
+
const inspectWorker = Effect.fn("MemorySubmissionLedger.inspectWorker")(function* (
|
|
1325
|
+
threadId: ThreadId,
|
|
1326
|
+
) {
|
|
1327
|
+
const current = yield* Ref.get(state);
|
|
1328
|
+
const latestId = current.latestByThread.get(threadId);
|
|
1329
|
+
const latest = latestId === undefined ? undefined : current.submissions.get(latestId);
|
|
1330
|
+
|
|
1331
|
+
const active = [...(current.activeByThread.get(threadId) ?? [])]
|
|
1332
|
+
.flatMap((id) => {
|
|
1333
|
+
const row = current.submissions.get(id);
|
|
1334
|
+
|
|
1335
|
+
return row === undefined ? [] : [row];
|
|
1336
|
+
})
|
|
1337
|
+
.sort((a, b) => a.row.queueSequence - b.row.queueSequence)[0];
|
|
1338
|
+
|
|
1339
|
+
const terminal = current.stoppedWorkers.get(threadId);
|
|
1340
|
+
|
|
1341
|
+
return WorkerLedgerState.make({
|
|
1342
|
+
latest: latest === undefined ? null : toSnapshot(latest.row),
|
|
1343
|
+
active: active === undefined ? null : toSnapshot(active.row),
|
|
1344
|
+
stopped: current.stoppedWorkers.has(threadId),
|
|
1345
|
+
...(terminal === undefined ? {} : { terminal }),
|
|
1346
|
+
});
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
const stopWorker = Effect.fn("MemorySubmissionLedger.stopWorker")(function* (
|
|
1350
|
+
unvalidated: WorkerStopCommand,
|
|
1351
|
+
) {
|
|
1352
|
+
const request = yield* validate(WorkerStopCommand, "stopWorker", unvalidated);
|
|
1353
|
+
const now = yield* Clock.currentTimeMillis;
|
|
1354
|
+
|
|
1355
|
+
return yield* Ref.modify(state, (current) => {
|
|
1356
|
+
const submissions = new Map(current.submissions);
|
|
1357
|
+
let owned = 0;
|
|
1358
|
+
|
|
1359
|
+
for (const id of current.activeByThread.get(request.threadId) ?? []) {
|
|
1360
|
+
const stored = submissions.get(id);
|
|
1361
|
+
|
|
1362
|
+
if (stored === undefined) continue;
|
|
1363
|
+
if (stored.ownership !== undefined) owned++;
|
|
1364
|
+
if (stored.abortIntent === undefined)
|
|
1365
|
+
submissions.set(id, {
|
|
1366
|
+
...stored,
|
|
1367
|
+
abortIntent: AbortIntent.make({
|
|
1368
|
+
submissionId: id,
|
|
1369
|
+
author: request.author,
|
|
1370
|
+
reason: "Worker owner stopped the worker",
|
|
1371
|
+
requestedAt: utc(now),
|
|
1372
|
+
}),
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
return [
|
|
1377
|
+
owned,
|
|
1378
|
+
{
|
|
1379
|
+
...current,
|
|
1380
|
+
submissions,
|
|
1381
|
+
stoppedWorkers: current.stoppedWorkers.has(request.threadId)
|
|
1382
|
+
? current.stoppedWorkers
|
|
1383
|
+
: new Map(current.stoppedWorkers).set(request.threadId, undefined),
|
|
1384
|
+
},
|
|
1385
|
+
] as const;
|
|
1386
|
+
});
|
|
1387
|
+
});
|
|
1388
|
+
|
|
1228
1389
|
const requestAbort: SubmissionLedger["Service"]["requestAbort"] = Effect.fn(
|
|
1229
1390
|
"MemorySubmissionLedger.requestAbort",
|
|
1230
1391
|
)((unvalidated) =>
|
|
@@ -1330,6 +1491,8 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1330
1491
|
Decision<ReadonlyArray<JoiningClaim>, OwnershipLost | LedgerError>,
|
|
1331
1492
|
LedgerState,
|
|
1332
1493
|
] => {
|
|
1494
|
+
if (current.stoppedWorkers.has(request.threadId)) return [success([]), current];
|
|
1495
|
+
|
|
1333
1496
|
const host = current.submissions.get(request.hostSubmissionId);
|
|
1334
1497
|
|
|
1335
1498
|
if (host === undefined) {
|
|
@@ -2602,6 +2765,8 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
2602
2765
|
reserveSettlement,
|
|
2603
2766
|
finalizeSettlement,
|
|
2604
2767
|
requestAbort,
|
|
2768
|
+
stopWorker,
|
|
2769
|
+
inspectWorker,
|
|
2605
2770
|
claimJoining,
|
|
2606
2771
|
markJoined,
|
|
2607
2772
|
revertJoining,
|
package/src/MemoryThreadStore.ts
CHANGED
|
@@ -426,6 +426,11 @@ const makeThreadStore = Effect.gen(function* () {
|
|
|
426
426
|
const payload = entry.record.payload;
|
|
427
427
|
|
|
428
428
|
if (payload._tag === "PeerMessagePrepared") peerCount++;
|
|
429
|
+
if (
|
|
430
|
+
(payload._tag === "UserInputRecorded" || payload._tag === "RunStarted") &&
|
|
431
|
+
payload.runId !== undefined
|
|
432
|
+
)
|
|
433
|
+
workerRecords.set(`execution:${payload._tag}`, [entry]);
|
|
429
434
|
|
|
430
435
|
const workerKey =
|
|
431
436
|
payload._tag === "SubtreeBudgetReserved"
|
|
@@ -571,6 +576,11 @@ const makeThreadStore = Effect.gen(function* () {
|
|
|
571
576
|
return yield* storeError("selected read", "Unverified worker acknowledgement");
|
|
572
577
|
records = [...thread.outstanding.values()].flat();
|
|
573
578
|
break;
|
|
579
|
+
case "WorkerExecution":
|
|
580
|
+
records = ["UserInputRecorded", "RunStarted"].flatMap(
|
|
581
|
+
(tag) => thread.workerRecords.get(`execution:${tag}`) ?? [],
|
|
582
|
+
);
|
|
583
|
+
break;
|
|
574
584
|
case "WorkerState":
|
|
575
585
|
records = [
|
|
576
586
|
...(thread.workerRecords.get("worker") ?? []),
|