@effect-agent/storage-memory 0.1.0-beta.56 → 0.1.0-beta.57
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.d.mts +15 -0
- package/dist/MemoryMessageDeliveryStore.mjs +144 -0
- package/dist/MemoryMessageDeliveryStore.mjs.map +1 -0
- package/dist/MemorySubmissionLedger.mjs +17 -2
- package/dist/MemorySubmissionLedger.mjs.map +1 -1
- package/dist/MemoryThreadStore.d.mts +6 -2
- package/dist/MemoryThreadStore.mjs +12 -5
- package/dist/MemoryThreadStore.mjs.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/package.json +1 -1
- package/src/MemoryMessageDeliveryStore.ts +285 -0
- package/src/MemorySubmissionLedger.ts +84 -0
- package/src/MemoryThreadStore.ts +32 -3
- package/src/index.ts +1 -0
|
@@ -7,8 +7,10 @@ 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
11
|
import {
|
|
11
12
|
PersistedJson,
|
|
13
|
+
WorkerAdmission,
|
|
12
14
|
ProducerEpoch,
|
|
13
15
|
type DefinitionDigests,
|
|
14
16
|
type DeploymentId,
|
|
@@ -147,6 +149,8 @@ interface SubmissionRow {
|
|
|
147
149
|
readonly parentLinkage: ParentLinkage | undefined;
|
|
148
150
|
readonly admissionGroup?: string;
|
|
149
151
|
readonly admissionFence?: AdmissionRequest["admissionFence"];
|
|
152
|
+
readonly workerAdmissionJson?: string;
|
|
153
|
+
readonly messageAdmissionJson?: string;
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
interface StoredOwnership {
|
|
@@ -290,6 +294,20 @@ const toSnapshot = (row: SubmissionRow): SubmissionSnapshot =>
|
|
|
290
294
|
createdAt: utc(row.createdAtMillis),
|
|
291
295
|
...(row.admissionGroup === undefined ? {} : { admissionGroup: row.admissionGroup }),
|
|
292
296
|
...(row.admissionFence === undefined ? {} : { admissionFence: row.admissionFence }),
|
|
297
|
+
...(row.workerAdmissionJson === undefined
|
|
298
|
+
? {}
|
|
299
|
+
: {
|
|
300
|
+
workerAdmission: Schema.decodeSync(Schema.fromJsonString(WorkerAdmission))(
|
|
301
|
+
row.workerAdmissionJson,
|
|
302
|
+
),
|
|
303
|
+
}),
|
|
304
|
+
...(row.messageAdmissionJson === undefined
|
|
305
|
+
? {}
|
|
306
|
+
: {
|
|
307
|
+
messageAdmission: Schema.decodeSync(Schema.fromJsonString(MessageAdmission))(
|
|
308
|
+
row.messageAdmissionJson,
|
|
309
|
+
),
|
|
310
|
+
}),
|
|
293
311
|
...(row.settledOutcome === undefined ? {} : { settledOutcome: row.settledOutcome }),
|
|
294
312
|
...(row.readyAtMillis === undefined ? {} : { readyAt: utc(row.readyAtMillis) }),
|
|
295
313
|
...(row.parentLinkage === undefined ? {} : { parentLinkage: row.parentLinkage }),
|
|
@@ -414,6 +432,24 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
414
432
|
Effect.gen(function* () {
|
|
415
433
|
const request = yield* validate(AdmissionRequest, "admit", unvalidated);
|
|
416
434
|
|
|
435
|
+
const workerAdmissionJson =
|
|
436
|
+
request.workerAdmission === undefined
|
|
437
|
+
? undefined
|
|
438
|
+
: yield* Schema.encodeEffect(Schema.fromJsonString(WorkerAdmission))(
|
|
439
|
+
request.workerAdmission,
|
|
440
|
+
).pipe(
|
|
441
|
+
Effect.mapError(() => ledgerError("admit", "Invalid worker admission metadata")),
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
const messageAdmissionJson =
|
|
445
|
+
request.messageAdmission === undefined
|
|
446
|
+
? undefined
|
|
447
|
+
: yield* Schema.encodeEffect(Schema.fromJsonString(MessageAdmission))(
|
|
448
|
+
request.messageAdmission,
|
|
449
|
+
).pipe(
|
|
450
|
+
Effect.mapError(() => ledgerError("admit", "Invalid message admission metadata")),
|
|
451
|
+
);
|
|
452
|
+
|
|
417
453
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
418
454
|
const services = yield* Effect.context<never>();
|
|
419
455
|
|
|
@@ -448,6 +484,22 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
448
484
|
existing.row.inputDigest !== request.inputDigest ||
|
|
449
485
|
!sameParentLinkage(existing.row.parentLinkage, request.parentLinkage) ||
|
|
450
486
|
existing.row.admissionGroup !== request.admissionGroup ||
|
|
487
|
+
!Schema.toEquivalence(Schema.optional(WorkerAdmission))(
|
|
488
|
+
existing.row.workerAdmissionJson === undefined
|
|
489
|
+
? undefined
|
|
490
|
+
: Schema.decodeSync(Schema.fromJsonString(WorkerAdmission))(
|
|
491
|
+
existing.row.workerAdmissionJson,
|
|
492
|
+
),
|
|
493
|
+
request.workerAdmission,
|
|
494
|
+
) ||
|
|
495
|
+
!Schema.toEquivalence(Schema.optional(MessageAdmission))(
|
|
496
|
+
existing.row.messageAdmissionJson === undefined
|
|
497
|
+
? undefined
|
|
498
|
+
: Schema.decodeSync(Schema.fromJsonString(MessageAdmission))(
|
|
499
|
+
existing.row.messageAdmissionJson,
|
|
500
|
+
),
|
|
501
|
+
request.messageAdmission,
|
|
502
|
+
) ||
|
|
451
503
|
!Schema.toEquivalence(Schema.optional(Schema.Json))(
|
|
452
504
|
existing.row.admissionFence,
|
|
453
505
|
request.admissionFence,
|
|
@@ -480,6 +532,36 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
480
532
|
current,
|
|
481
533
|
];
|
|
482
534
|
}
|
|
535
|
+
|
|
536
|
+
// A Thread's first admission fixes its worker origin before canonical materialization.
|
|
537
|
+
const first = [...current.submissions.values()].find(
|
|
538
|
+
({ row }) => row.threadId === request.threadId,
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
if (first !== undefined) {
|
|
542
|
+
const previous =
|
|
543
|
+
first.row.workerAdmissionJson === undefined
|
|
544
|
+
? undefined
|
|
545
|
+
: Schema.decodeSync(Schema.fromJsonString(WorkerAdmission))(
|
|
546
|
+
first.row.workerAdmissionJson,
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
if (
|
|
550
|
+
!Schema.toEquivalence(Schema.optional(WorkerAdmission.fields.origin))(
|
|
551
|
+
previous?.origin,
|
|
552
|
+
request.workerAdmission?.origin,
|
|
553
|
+
)
|
|
554
|
+
)
|
|
555
|
+
return [
|
|
556
|
+
failure(
|
|
557
|
+
AdmissionPolicyError.make({
|
|
558
|
+
reason: "refused",
|
|
559
|
+
code: "worker-origin-conflict",
|
|
560
|
+
}),
|
|
561
|
+
),
|
|
562
|
+
current,
|
|
563
|
+
];
|
|
564
|
+
}
|
|
483
565
|
// A memory policy and the ledger mutation share one synchronous critical section.
|
|
484
566
|
// An asynchronous policy cannot fence this Ref and therefore fails closed.
|
|
485
567
|
const checked = Effect.runSyncExitWith(services)(admissionFence.check(request));
|
|
@@ -535,6 +617,8 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
535
617
|
createdAtMillis: nowMillis,
|
|
536
618
|
readyAtMillis: undefined,
|
|
537
619
|
parentLinkage: request.parentLinkage,
|
|
620
|
+
...(workerAdmissionJson === undefined ? {} : { workerAdmissionJson }),
|
|
621
|
+
...(messageAdmissionJson === undefined ? {} : { messageAdmissionJson }),
|
|
538
622
|
...(request.admissionGroup === undefined
|
|
539
623
|
? {}
|
|
540
624
|
: { admissionGroup: request.admissionGroup }),
|
package/src/MemoryThreadStore.ts
CHANGED
|
@@ -30,12 +30,28 @@ import {
|
|
|
30
30
|
LoadCheckpointRequest,
|
|
31
31
|
SaveCheckpointRequest,
|
|
32
32
|
} from "@effect-agent/thread/ThreadStore";
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
Context,
|
|
35
|
+
Crypto,
|
|
36
|
+
Effect,
|
|
37
|
+
Encoding,
|
|
38
|
+
Layer,
|
|
39
|
+
Option,
|
|
40
|
+
PubSub,
|
|
41
|
+
Ref,
|
|
42
|
+
Schema,
|
|
43
|
+
Stream,
|
|
44
|
+
} from "effect";
|
|
34
45
|
|
|
35
46
|
const MAX_THREADS = 256;
|
|
36
47
|
const MAX_RECORDS_PER_THREAD = 65_536;
|
|
37
48
|
const MAX_CHECKPOINTS_PER_THREAD = 1_024;
|
|
38
49
|
|
|
50
|
+
const ThreadCapacity = Context.Reference<number>(
|
|
51
|
+
"@effect-agent/storage-memory/MemoryThreadStore/ThreadCapacity",
|
|
52
|
+
{ defaultValue: () => MAX_THREADS },
|
|
53
|
+
);
|
|
54
|
+
|
|
39
55
|
interface StoredBatch {
|
|
40
56
|
readonly digest: Digest;
|
|
41
57
|
readonly result: AppendResult;
|
|
@@ -155,6 +171,7 @@ const validateCheckpointVersion = Effect.fn("MemoryThreadStore.validateCheckpoin
|
|
|
155
171
|
);
|
|
156
172
|
|
|
157
173
|
const makeThreadStore = Effect.gen(function* () {
|
|
174
|
+
const maxThreads = yield* ThreadCapacity;
|
|
158
175
|
const crypto = yield* Crypto.Crypto;
|
|
159
176
|
const state = yield* Ref.make<MemoryState>({ threads: new Map() });
|
|
160
177
|
const updates = yield* PubSub.sliding<void>(1);
|
|
@@ -198,11 +215,11 @@ const makeThreadStore = Effect.gen(function* () {
|
|
|
198
215
|
|
|
199
216
|
return [{ _tag: "success" }, { threads }];
|
|
200
217
|
}
|
|
201
|
-
if (current.threads.size >=
|
|
218
|
+
if (current.threads.size >= maxThreads) {
|
|
202
219
|
return [
|
|
203
220
|
{
|
|
204
221
|
_tag: "failure",
|
|
205
|
-
error: storeError("materialize", `In-memory thread limit ${
|
|
222
|
+
error: storeError("materialize", `In-memory thread limit ${maxThreads} exceeded`),
|
|
206
223
|
},
|
|
207
224
|
current,
|
|
208
225
|
];
|
|
@@ -630,3 +647,15 @@ const makeThreadStore = Effect.gen(function* () {
|
|
|
630
647
|
* SubmissionLedger port; this Layer deliberately provides only the ThreadStore.
|
|
631
648
|
*/
|
|
632
649
|
export const MemoryThreadStoreLive = Layer.effect(ThreadStore, makeThreadStore);
|
|
650
|
+
|
|
651
|
+
/** Configure a finite retained Thread capacity. Invalid construction options throw immediately. */
|
|
652
|
+
export const memoryThreadStoreLayer = (options: { readonly maxThreads?: number } = {}) =>
|
|
653
|
+
MemoryThreadStoreLive.pipe(
|
|
654
|
+
Layer.provide(
|
|
655
|
+
Layer.succeed(ThreadCapacity)(
|
|
656
|
+
Schema.decodeSync(
|
|
657
|
+
Schema.Int.check(Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(65_536)),
|
|
658
|
+
)(options.maxThreads ?? MAX_THREADS),
|
|
659
|
+
),
|
|
660
|
+
),
|
|
661
|
+
);
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * as MemoryScheduleStore from "./MemoryScheduleStore.ts";
|
|
2
|
+
export * as MemoryMessageDeliveryStore from "./MemoryMessageDeliveryStore.ts";
|
|
2
3
|
export * as MemorySemanticIndex from "./MemorySemanticIndex.ts";
|
|
3
4
|
export * as MemorySubmissionLedger from "./MemorySubmissionLedger.ts";
|
|
4
5
|
export * as MemorySubscriptionStore from "./MemorySubscriptionStore.ts";
|