@effect-agent/storage-memory 0.1.0-beta.41 → 0.1.0-beta.44
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/index.mjs.map +1 -1
- package/package.json +1 -48
- package/src/memory-ledger.ts +192 -0
- package/src/memory-schedule-store.ts +42 -0
- package/src/memory-storage.ts +48 -0
- package/src/memory-subscription-store.ts +149 -0
- package/src/semantic-memory-index.ts +37 -0
- package/src/testing.ts +1 -0
package/src/memory-ledger.ts
CHANGED
|
@@ -329,10 +329,12 @@ const withChildReservation = (
|
|
|
329
329
|
|
|
330
330
|
const findHead = (state: LedgerState, threadId: ThreadId): StoredSubmission | undefined => {
|
|
331
331
|
let head: StoredSubmission | undefined;
|
|
332
|
+
|
|
332
333
|
for (const stored of state.submissions.values()) {
|
|
333
334
|
if (stored.row.threadId !== threadId || stored.row.state === "settled") continue;
|
|
334
335
|
if (head === undefined || stored.row.queueSequence < head.row.queueSequence) head = stored;
|
|
335
336
|
}
|
|
337
|
+
|
|
336
338
|
return head;
|
|
337
339
|
};
|
|
338
340
|
|
|
@@ -379,6 +381,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
379
381
|
childReservations: new Map(),
|
|
380
382
|
mintCounter: 0,
|
|
381
383
|
});
|
|
384
|
+
|
|
382
385
|
const leaseMillis = Duration.toMillis(DEFAULT_OWNERSHIP_LEASE_DURATION);
|
|
383
386
|
|
|
384
387
|
const capabilities = Effect.succeed(LedgerCapabilities.make({ durability: "non-durable" }));
|
|
@@ -388,6 +391,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
388
391
|
Effect.gen(function* () {
|
|
389
392
|
const request = yield* validate(AdmissionRequest, "admit", unvalidated);
|
|
390
393
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
394
|
+
|
|
391
395
|
const decision = yield* Ref.modify(
|
|
392
396
|
state,
|
|
393
397
|
(
|
|
@@ -398,8 +402,10 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
398
402
|
] => {
|
|
399
403
|
const key = admissionKey(request.threadId, request.principal, request.idempotencyKey);
|
|
400
404
|
const existingId = current.admissionIndex.get(key);
|
|
405
|
+
|
|
401
406
|
if (existingId !== undefined) {
|
|
402
407
|
const existing = current.submissions.get(existingId);
|
|
408
|
+
|
|
403
409
|
if (existing === undefined) {
|
|
404
410
|
return [
|
|
405
411
|
failure(
|
|
@@ -427,6 +433,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
427
433
|
current,
|
|
428
434
|
];
|
|
429
435
|
}
|
|
436
|
+
|
|
430
437
|
return [
|
|
431
438
|
success(
|
|
432
439
|
AdmissionResult.make({
|
|
@@ -448,11 +455,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
448
455
|
current,
|
|
449
456
|
];
|
|
450
457
|
}
|
|
458
|
+
|
|
451
459
|
const lane = current.lanes.get(request.threadId) ?? {
|
|
452
460
|
nextQueueSequence: 1,
|
|
453
461
|
producerEpoch: 0,
|
|
454
462
|
};
|
|
463
|
+
|
|
455
464
|
const mintCounter = current.mintCounter + 1;
|
|
465
|
+
|
|
456
466
|
const row: SubmissionRow = {
|
|
457
467
|
submissionId: decodeSubmissionId(`submission-memory-${mintCounter}`),
|
|
458
468
|
threadId: request.threadId,
|
|
@@ -471,6 +481,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
471
481
|
readyAtMillis: undefined,
|
|
472
482
|
parentLinkage: request.parentLinkage,
|
|
473
483
|
};
|
|
484
|
+
|
|
474
485
|
const submissions = new Map(current.submissions).set(row.submissionId, {
|
|
475
486
|
row,
|
|
476
487
|
ownership: undefined,
|
|
@@ -483,11 +494,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
483
494
|
approvalDecisions: new Map<ToolCallId, ApprovalDecisionIntent>(),
|
|
484
495
|
unknownResolutions: new Map<ToolCallId, StoredUnknownResolution>(),
|
|
485
496
|
});
|
|
497
|
+
|
|
486
498
|
const admissionIndex = new Map(current.admissionIndex).set(key, row.submissionId);
|
|
499
|
+
|
|
487
500
|
const lanes = new Map(current.lanes).set(request.threadId, {
|
|
488
501
|
nextQueueSequence: lane.nextQueueSequence + 1,
|
|
489
502
|
producerEpoch: lane.producerEpoch,
|
|
490
503
|
});
|
|
504
|
+
|
|
491
505
|
return [
|
|
492
506
|
success(
|
|
493
507
|
AdmissionResult.make({
|
|
@@ -502,7 +516,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
502
516
|
];
|
|
503
517
|
},
|
|
504
518
|
);
|
|
519
|
+
|
|
505
520
|
if (decision._tag === "failure") return yield* decision.error;
|
|
521
|
+
|
|
506
522
|
return decision.value;
|
|
507
523
|
}),
|
|
508
524
|
);
|
|
@@ -513,10 +529,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
513
529
|
Effect.gen(function* () {
|
|
514
530
|
const request = yield* validate(MarkReadyRequest, "markReady", unvalidated);
|
|
515
531
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
532
|
+
|
|
516
533
|
const decision = yield* Ref.modify(
|
|
517
534
|
state,
|
|
518
535
|
(current): readonly [Decision<void, LedgerError>, LedgerState] => {
|
|
519
536
|
const stored = current.submissions.get(request.submissionId);
|
|
537
|
+
|
|
520
538
|
if (stored === undefined) {
|
|
521
539
|
return [
|
|
522
540
|
failure(ledgerError("markReady", `Unknown Submission ${request.submissionId}`)),
|
|
@@ -524,6 +542,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
524
542
|
];
|
|
525
543
|
}
|
|
526
544
|
if (stored.row.state !== "admitted") return [success(undefined), current];
|
|
545
|
+
|
|
527
546
|
return [
|
|
528
547
|
success(undefined),
|
|
529
548
|
withSubmission(current, {
|
|
@@ -533,6 +552,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
533
552
|
];
|
|
534
553
|
},
|
|
535
554
|
);
|
|
555
|
+
|
|
536
556
|
if (decision._tag === "failure") return yield* decision.error;
|
|
537
557
|
}),
|
|
538
558
|
);
|
|
@@ -543,14 +563,17 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
543
563
|
Effect.gen(function* () {
|
|
544
564
|
const request = yield* validate(SubmissionLookup, "lookup", unvalidated);
|
|
545
565
|
const current = yield* Ref.get(state);
|
|
566
|
+
|
|
546
567
|
const submissionId =
|
|
547
568
|
request._tag === "SubmissionLookupById"
|
|
548
569
|
? request.submissionId
|
|
549
570
|
: current.admissionIndex.get(
|
|
550
571
|
admissionKey(request.threadId, request.principal, request.idempotencyKey),
|
|
551
572
|
);
|
|
573
|
+
|
|
552
574
|
const stored =
|
|
553
575
|
submissionId === undefined ? undefined : current.submissions.get(submissionId);
|
|
576
|
+
|
|
554
577
|
return stored === undefined ? Option.none() : Option.some(toSnapshot(stored.row));
|
|
555
578
|
}),
|
|
556
579
|
);
|
|
@@ -560,20 +583,25 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
560
583
|
)((unvalidated) =>
|
|
561
584
|
Effect.gen(function* () {
|
|
562
585
|
const request = yield* validate(SubmissionLookupByKey, "resolveAdmission", unvalidated);
|
|
586
|
+
|
|
563
587
|
// Test-only fault seam: lets suites exercise the Indeterminate classification that a
|
|
564
588
|
// single strongly consistent store never produces on its own (SUB-031, P6 honesty).
|
|
565
589
|
if (options.resolveAdmissionFault !== undefined) {
|
|
566
590
|
const fault = yield* options.resolveAdmissionFault;
|
|
591
|
+
|
|
567
592
|
if (Option.isSome(fault)) {
|
|
568
593
|
return AdmissionIndeterminate.make({ reason: fault.value });
|
|
569
594
|
}
|
|
570
595
|
}
|
|
571
596
|
const current = yield* Ref.get(state);
|
|
597
|
+
|
|
572
598
|
const submissionId = current.admissionIndex.get(
|
|
573
599
|
admissionKey(request.threadId, request.principal, request.idempotencyKey),
|
|
574
600
|
);
|
|
601
|
+
|
|
575
602
|
const stored =
|
|
576
603
|
submissionId === undefined ? undefined : current.submissions.get(submissionId);
|
|
604
|
+
|
|
577
605
|
return stored === undefined
|
|
578
606
|
? AdmissionNotAdmitted.make()
|
|
579
607
|
: AdmissionAdmitted.make({ submission: toSnapshot(stored.row) });
|
|
@@ -585,10 +613,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
585
613
|
Effect.gen(function* () {
|
|
586
614
|
const request = yield* validate(ClaimRequest, "claim", unvalidated);
|
|
587
615
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
616
|
+
|
|
588
617
|
const decision = yield* Ref.modify(
|
|
589
618
|
state,
|
|
590
619
|
(current): readonly [Decision<Option.Option<Claim>, LedgerError>, LedgerState] => {
|
|
591
620
|
const head = findHead(current, request.threadId);
|
|
621
|
+
|
|
592
622
|
if (head === undefined) return [success(Option.none()), current];
|
|
593
623
|
if (
|
|
594
624
|
BLOCKED_HEAD_STATES.has(head.row.state) ||
|
|
@@ -603,6 +633,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
603
633
|
return [success(Option.none()), current];
|
|
604
634
|
}
|
|
605
635
|
const lane = current.lanes.get(request.threadId);
|
|
636
|
+
|
|
606
637
|
if (lane === undefined) {
|
|
607
638
|
return [
|
|
608
639
|
failure(ledgerError("claim", "Claimable head without a Thread lane")),
|
|
@@ -611,6 +642,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
611
642
|
}
|
|
612
643
|
const producerEpoch = decodeProducerEpoch(lane.producerEpoch + 1);
|
|
613
644
|
const mintCounter = current.mintCounter + 1;
|
|
645
|
+
|
|
614
646
|
const ownership: StoredOwnership = {
|
|
615
647
|
attemptId: decodeAttemptId(`attempt-memory-${mintCounter}`),
|
|
616
648
|
ownershipToken: decodeOwnershipToken(`ownership-memory-${mintCounter}`),
|
|
@@ -618,13 +650,17 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
618
650
|
ownerProducerId: request.producerId,
|
|
619
651
|
leaseExpiresAtMillis: nowMillis + leaseMillis,
|
|
620
652
|
};
|
|
653
|
+
|
|
621
654
|
const row: SubmissionRow =
|
|
622
655
|
head.row.state === "ready" ? { ...head.row, state: "running" } : head.row;
|
|
656
|
+
|
|
623
657
|
const next = withSubmission(current, { ...head, row, ownership });
|
|
658
|
+
|
|
624
659
|
const lanes = new Map(next.lanes).set(request.threadId, {
|
|
625
660
|
nextQueueSequence: lane.nextQueueSequence,
|
|
626
661
|
producerEpoch: lane.producerEpoch + 1,
|
|
627
662
|
});
|
|
663
|
+
|
|
628
664
|
return [
|
|
629
665
|
success(
|
|
630
666
|
Option.some(
|
|
@@ -642,7 +678,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
642
678
|
];
|
|
643
679
|
},
|
|
644
680
|
);
|
|
681
|
+
|
|
645
682
|
if (decision._tag === "failure") return yield* decision.error;
|
|
683
|
+
|
|
646
684
|
return decision.value;
|
|
647
685
|
}),
|
|
648
686
|
);
|
|
@@ -653,12 +691,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
653
691
|
Effect.gen(function* () {
|
|
654
692
|
const request = yield* validate(RenewOwnershipRequest, "renewOwnership", unvalidated);
|
|
655
693
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
694
|
+
|
|
656
695
|
const decision = yield* Ref.modify(
|
|
657
696
|
state,
|
|
658
697
|
(
|
|
659
698
|
current,
|
|
660
699
|
): readonly [Decision<OwnershipRenewal, OwnershipLost | LedgerError>, LedgerState] => {
|
|
661
700
|
const stored = current.submissions.get(request.submissionId);
|
|
701
|
+
|
|
662
702
|
if (stored === undefined) {
|
|
663
703
|
return [
|
|
664
704
|
failure(
|
|
@@ -670,10 +710,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
670
710
|
if (stored.ownership === undefined || !ownsLane(stored, request.ownershipToken)) {
|
|
671
711
|
return [failure(ownershipLost(current, stored)), current];
|
|
672
712
|
}
|
|
713
|
+
|
|
673
714
|
const ownership: StoredOwnership = {
|
|
674
715
|
...stored.ownership,
|
|
675
716
|
leaseExpiresAtMillis: nowMillis + leaseMillis,
|
|
676
717
|
};
|
|
718
|
+
|
|
677
719
|
return [
|
|
678
720
|
success(
|
|
679
721
|
OwnershipRenewal.make({
|
|
@@ -685,7 +727,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
685
727
|
];
|
|
686
728
|
},
|
|
687
729
|
);
|
|
730
|
+
|
|
688
731
|
if (decision._tag === "failure") return yield* decision.error;
|
|
732
|
+
|
|
689
733
|
return decision.value;
|
|
690
734
|
}),
|
|
691
735
|
);
|
|
@@ -695,10 +739,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
695
739
|
)((unvalidated) =>
|
|
696
740
|
Effect.gen(function* () {
|
|
697
741
|
const request = yield* validate(ReleaseOwnershipRequest, "releaseOwnership", unvalidated);
|
|
742
|
+
|
|
698
743
|
const decision = yield* Ref.modify(
|
|
699
744
|
state,
|
|
700
745
|
(current): readonly [Decision<void, OwnershipLost | LedgerError>, LedgerState] => {
|
|
701
746
|
const stored = current.submissions.get(request.submissionId);
|
|
747
|
+
|
|
702
748
|
if (stored === undefined) {
|
|
703
749
|
return [
|
|
704
750
|
failure(
|
|
@@ -710,12 +756,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
710
756
|
if (!ownsLane(stored, request.ownershipToken)) {
|
|
711
757
|
return [failure(ownershipLost(current, stored)), current];
|
|
712
758
|
}
|
|
759
|
+
|
|
713
760
|
return [
|
|
714
761
|
success(undefined),
|
|
715
762
|
withSubmission(current, { ...stored, ownership: undefined }),
|
|
716
763
|
];
|
|
717
764
|
},
|
|
718
765
|
);
|
|
766
|
+
|
|
719
767
|
if (decision._tag === "failure") return yield* decision.error;
|
|
720
768
|
}),
|
|
721
769
|
);
|
|
@@ -725,10 +773,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
725
773
|
)((unvalidated) =>
|
|
726
774
|
Effect.gen(function* () {
|
|
727
775
|
const request = yield* validate(MarkInputAppliedRequest, "markInputApplied", unvalidated);
|
|
776
|
+
|
|
728
777
|
const decision = yield* Ref.modify(
|
|
729
778
|
state,
|
|
730
779
|
(current): readonly [Decision<void, OwnershipLost | LedgerError>, LedgerState] => {
|
|
731
780
|
const stored = current.submissions.get(request.submissionId);
|
|
781
|
+
|
|
732
782
|
if (stored === undefined) {
|
|
733
783
|
return [
|
|
734
784
|
failure(
|
|
@@ -740,20 +790,24 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
740
790
|
if (!ownsLane(stored, request.ownershipToken)) {
|
|
741
791
|
return [failure(ownershipLost(current, stored)), current];
|
|
742
792
|
}
|
|
793
|
+
|
|
743
794
|
const marker = InputAppliedMarker.make({
|
|
744
795
|
recordId: request.recordId,
|
|
745
796
|
sequence: request.sequence,
|
|
746
797
|
});
|
|
798
|
+
|
|
747
799
|
const row: SubmissionRow =
|
|
748
800
|
STATE_RANK[stored.row.state] < STATE_RANK["input-applied"]
|
|
749
801
|
? { ...stored.row, state: "input-applied" }
|
|
750
802
|
: stored.row;
|
|
803
|
+
|
|
751
804
|
return [
|
|
752
805
|
success(undefined),
|
|
753
806
|
withSubmission(current, { ...stored, row, inputApplied: marker }),
|
|
754
807
|
];
|
|
755
808
|
},
|
|
756
809
|
);
|
|
810
|
+
|
|
757
811
|
if (decision._tag === "failure") return yield* decision.error;
|
|
758
812
|
}),
|
|
759
813
|
);
|
|
@@ -763,6 +817,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
763
817
|
)((unvalidated) =>
|
|
764
818
|
Effect.gen(function* () {
|
|
765
819
|
const request = yield* validate(SettlementReservation, "reserveSettlement", unvalidated);
|
|
820
|
+
|
|
766
821
|
const decision = yield* Ref.modify(
|
|
767
822
|
state,
|
|
768
823
|
(
|
|
@@ -772,6 +827,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
772
827
|
LedgerState,
|
|
773
828
|
] => {
|
|
774
829
|
const stored = current.submissions.get(request.submissionId);
|
|
830
|
+
|
|
775
831
|
if (stored === undefined) {
|
|
776
832
|
return [
|
|
777
833
|
failure(
|
|
@@ -780,11 +836,13 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
780
836
|
current,
|
|
781
837
|
];
|
|
782
838
|
}
|
|
839
|
+
|
|
783
840
|
// A `joined` Submission settles WITH its host (plan §2.5) and its lane is never
|
|
784
841
|
// worker-claimable, so no ownership token can exist for it: the recorded host
|
|
785
842
|
// linkage authorizes the reservation and the presented token is not consulted.
|
|
786
843
|
const joinedSettlement =
|
|
787
844
|
stored.row.state === "joined" && stored.joinedHostSubmissionId !== undefined;
|
|
845
|
+
|
|
788
846
|
// P7 §7(c): an aborted, never-claimed, still-queued Submission likewise has no
|
|
789
847
|
// live ownership to fence against — its durable abort intent authorizes exactly
|
|
790
848
|
// its ABORTED settlement (`terminalizing` is the same pass's crash replay). Every
|
|
@@ -794,6 +852,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
794
852
|
stored.abortIntent !== undefined &&
|
|
795
853
|
stored.ownership === undefined &&
|
|
796
854
|
(stored.row.state === "ready" || stored.row.state === "terminalizing");
|
|
855
|
+
|
|
797
856
|
if (
|
|
798
857
|
!joinedSettlement &&
|
|
799
858
|
!queuedAbortSettlement &&
|
|
@@ -802,6 +861,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
802
861
|
return [failure(ownershipLost(current, stored)), current];
|
|
803
862
|
}
|
|
804
863
|
const existing = stored.reservation;
|
|
864
|
+
|
|
805
865
|
if (existing !== undefined) {
|
|
806
866
|
if (
|
|
807
867
|
existing.settlementId !== request.settlementId ||
|
|
@@ -818,6 +878,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
818
878
|
current,
|
|
819
879
|
];
|
|
820
880
|
}
|
|
881
|
+
|
|
821
882
|
return [
|
|
822
883
|
success(
|
|
823
884
|
ReservedSettlement.make({
|
|
@@ -832,6 +893,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
832
893
|
current,
|
|
833
894
|
];
|
|
834
895
|
}
|
|
896
|
+
|
|
835
897
|
const reservation: StoredReservation = {
|
|
836
898
|
settlementId: request.settlementId,
|
|
837
899
|
outcome: request.outcome,
|
|
@@ -839,10 +901,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
839
901
|
recordDigest: request.recordDigest,
|
|
840
902
|
finalizedAtMillis: undefined,
|
|
841
903
|
};
|
|
904
|
+
|
|
842
905
|
const row: SubmissionRow =
|
|
843
906
|
STATE_RANK[stored.row.state] < STATE_RANK.terminalizing
|
|
844
907
|
? { ...stored.row, state: "terminalizing" }
|
|
845
908
|
: stored.row;
|
|
909
|
+
|
|
846
910
|
return [
|
|
847
911
|
success(
|
|
848
912
|
ReservedSettlement.make({
|
|
@@ -858,7 +922,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
858
922
|
];
|
|
859
923
|
},
|
|
860
924
|
);
|
|
925
|
+
|
|
861
926
|
if (decision._tag === "failure") return yield* decision.error;
|
|
927
|
+
|
|
862
928
|
return decision.value;
|
|
863
929
|
}),
|
|
864
930
|
);
|
|
@@ -869,12 +935,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
869
935
|
Effect.gen(function* () {
|
|
870
936
|
const request = yield* validate(SettlementFinalization, "finalizeSettlement", unvalidated);
|
|
871
937
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
938
|
+
|
|
872
939
|
const decision = yield* Ref.modify(
|
|
873
940
|
state,
|
|
874
941
|
(
|
|
875
942
|
current,
|
|
876
943
|
): readonly [Decision<Settlement, SettlementConflict | LedgerError>, LedgerState] => {
|
|
877
944
|
const stored = current.submissions.get(request.submissionId);
|
|
945
|
+
|
|
878
946
|
if (stored === undefined) {
|
|
879
947
|
return [
|
|
880
948
|
failure(
|
|
@@ -884,6 +952,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
884
952
|
];
|
|
885
953
|
}
|
|
886
954
|
const reservation = stored.reservation;
|
|
955
|
+
|
|
887
956
|
if (reservation === undefined) {
|
|
888
957
|
return [
|
|
889
958
|
failure(
|
|
@@ -896,6 +965,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
896
965
|
];
|
|
897
966
|
}
|
|
898
967
|
const settlementFailure = settlementFailureFromRecord(reservation.record);
|
|
968
|
+
|
|
899
969
|
if ((reservation.outcome === "failed") !== (settlementFailure !== undefined)) {
|
|
900
970
|
return [
|
|
901
971
|
failure(
|
|
@@ -933,12 +1003,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
933
1003
|
current,
|
|
934
1004
|
];
|
|
935
1005
|
}
|
|
1006
|
+
|
|
936
1007
|
const next = withSubmission(current, {
|
|
937
1008
|
...stored,
|
|
938
1009
|
row: { ...stored.row, state: "settled", settledOutcome: reservation.outcome },
|
|
939
1010
|
ownership: undefined,
|
|
940
1011
|
reservation: { ...reservation, finalizedAtMillis: nowMillis },
|
|
941
1012
|
});
|
|
1013
|
+
|
|
942
1014
|
return [
|
|
943
1015
|
success(
|
|
944
1016
|
Settlement.make({
|
|
@@ -954,7 +1026,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
954
1026
|
];
|
|
955
1027
|
},
|
|
956
1028
|
);
|
|
1029
|
+
|
|
957
1030
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1031
|
+
|
|
958
1032
|
return decision.value;
|
|
959
1033
|
}),
|
|
960
1034
|
);
|
|
@@ -965,6 +1039,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
965
1039
|
Effect.gen(function* () {
|
|
966
1040
|
const request = yield* validate(AbortCommand, "requestAbort", unvalidated);
|
|
967
1041
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1042
|
+
|
|
968
1043
|
const decision = yield* Ref.modify(
|
|
969
1044
|
state,
|
|
970
1045
|
(
|
|
@@ -974,6 +1049,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
974
1049
|
LedgerState,
|
|
975
1050
|
] => {
|
|
976
1051
|
const stored = current.submissions.get(request.submissionId);
|
|
1052
|
+
|
|
977
1053
|
if (stored === undefined) {
|
|
978
1054
|
return [
|
|
979
1055
|
failure(ledgerError("requestAbort", `Unknown Submission ${request.submissionId}`)),
|
|
@@ -995,6 +1071,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
995
1071
|
current,
|
|
996
1072
|
];
|
|
997
1073
|
}
|
|
1074
|
+
|
|
998
1075
|
return [
|
|
999
1076
|
failure(
|
|
1000
1077
|
JoinedToHost.make({
|
|
@@ -1017,6 +1094,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1017
1094
|
current,
|
|
1018
1095
|
];
|
|
1019
1096
|
}
|
|
1097
|
+
|
|
1020
1098
|
return [
|
|
1021
1099
|
failure(
|
|
1022
1100
|
SettlementConflict.make({
|
|
@@ -1028,16 +1106,20 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1028
1106
|
];
|
|
1029
1107
|
}
|
|
1030
1108
|
if (stored.abortIntent !== undefined) return [success(stored.abortIntent), current];
|
|
1109
|
+
|
|
1031
1110
|
const intent = AbortIntent.make({
|
|
1032
1111
|
submissionId: request.submissionId,
|
|
1033
1112
|
author: request.author,
|
|
1034
1113
|
reason: request.reason,
|
|
1035
1114
|
requestedAt: utc(nowMillis),
|
|
1036
1115
|
});
|
|
1116
|
+
|
|
1037
1117
|
return [success(intent), withSubmission(current, { ...stored, abortIntent: intent })];
|
|
1038
1118
|
},
|
|
1039
1119
|
);
|
|
1120
|
+
|
|
1040
1121
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1122
|
+
|
|
1041
1123
|
return decision.value;
|
|
1042
1124
|
}),
|
|
1043
1125
|
);
|
|
@@ -1047,6 +1129,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1047
1129
|
)((unvalidated) =>
|
|
1048
1130
|
Effect.gen(function* () {
|
|
1049
1131
|
const request = yield* validate(ClaimJoiningRequest, "claimJoining", unvalidated);
|
|
1132
|
+
|
|
1050
1133
|
const decision = yield* Ref.modify(
|
|
1051
1134
|
state,
|
|
1052
1135
|
(
|
|
@@ -1056,6 +1139,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1056
1139
|
LedgerState,
|
|
1057
1140
|
] => {
|
|
1058
1141
|
const host = current.submissions.get(request.hostSubmissionId);
|
|
1142
|
+
|
|
1059
1143
|
if (host === undefined) {
|
|
1060
1144
|
return [
|
|
1061
1145
|
failure(
|
|
@@ -1078,6 +1162,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1078
1162
|
if (!ownsLane(host, request.ownershipToken)) {
|
|
1079
1163
|
return [failure(ownershipLost(current, host)), current];
|
|
1080
1164
|
}
|
|
1165
|
+
|
|
1081
1166
|
const later = [...current.submissions.values()]
|
|
1082
1167
|
.filter(
|
|
1083
1168
|
(stored) =>
|
|
@@ -1085,8 +1170,10 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1085
1170
|
stored.row.queueSequence > host.row.queueSequence,
|
|
1086
1171
|
)
|
|
1087
1172
|
.sort((left, right) => left.row.queueSequence - right.row.queueSequence);
|
|
1173
|
+
|
|
1088
1174
|
const claims: Array<JoiningClaim> = [];
|
|
1089
1175
|
const submissions = new Map(current.submissions);
|
|
1176
|
+
|
|
1090
1177
|
for (const stored of later) {
|
|
1091
1178
|
if (claims.length >= request.maxCount) break;
|
|
1092
1179
|
// Rows already claimed by THIS host extend its contiguous prefix and are skipped;
|
|
@@ -1119,10 +1206,13 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1119
1206
|
}),
|
|
1120
1207
|
);
|
|
1121
1208
|
}
|
|
1209
|
+
|
|
1122
1210
|
return [success(claims), { ...current, submissions }];
|
|
1123
1211
|
},
|
|
1124
1212
|
);
|
|
1213
|
+
|
|
1125
1214
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1215
|
+
|
|
1126
1216
|
return decision.value;
|
|
1127
1217
|
}),
|
|
1128
1218
|
);
|
|
@@ -1132,10 +1222,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1132
1222
|
)((unvalidated) =>
|
|
1133
1223
|
Effect.gen(function* () {
|
|
1134
1224
|
const request = yield* validate(MarkJoinedRequest, "markJoined", unvalidated);
|
|
1225
|
+
|
|
1135
1226
|
const decision = yield* Ref.modify(
|
|
1136
1227
|
state,
|
|
1137
1228
|
(current): readonly [Decision<void, OwnershipLost | LedgerError>, LedgerState] => {
|
|
1138
1229
|
const stored = current.submissions.get(request.submissionId);
|
|
1230
|
+
|
|
1139
1231
|
if (stored === undefined) {
|
|
1140
1232
|
return [
|
|
1141
1233
|
failure(ledgerError("markJoined", `Unknown Submission ${request.submissionId}`)),
|
|
@@ -1154,6 +1246,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1154
1246
|
];
|
|
1155
1247
|
}
|
|
1156
1248
|
const host = current.submissions.get(stored.joinedHostSubmissionId);
|
|
1249
|
+
|
|
1157
1250
|
if (host === undefined) {
|
|
1158
1251
|
return [
|
|
1159
1252
|
failure(
|
|
@@ -1177,6 +1270,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1177
1270
|
) {
|
|
1178
1271
|
return [success(undefined), current];
|
|
1179
1272
|
}
|
|
1273
|
+
|
|
1180
1274
|
return [
|
|
1181
1275
|
failure(
|
|
1182
1276
|
ledgerError(
|
|
@@ -1198,10 +1292,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1198
1292
|
current,
|
|
1199
1293
|
];
|
|
1200
1294
|
}
|
|
1295
|
+
|
|
1201
1296
|
const marker = InputAppliedMarker.make({
|
|
1202
1297
|
recordId: request.recordId,
|
|
1203
1298
|
sequence: request.sequence,
|
|
1204
1299
|
});
|
|
1300
|
+
|
|
1205
1301
|
return [
|
|
1206
1302
|
success(undefined),
|
|
1207
1303
|
withSubmission(current, {
|
|
@@ -1212,6 +1308,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1212
1308
|
];
|
|
1213
1309
|
},
|
|
1214
1310
|
);
|
|
1311
|
+
|
|
1215
1312
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1216
1313
|
}),
|
|
1217
1314
|
);
|
|
@@ -1221,10 +1318,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1221
1318
|
)((unvalidated) =>
|
|
1222
1319
|
Effect.gen(function* () {
|
|
1223
1320
|
const request = yield* validate(RevertJoiningRequest, "revertJoining", unvalidated);
|
|
1321
|
+
|
|
1224
1322
|
const decision = yield* Ref.modify(
|
|
1225
1323
|
state,
|
|
1226
1324
|
(current): readonly [Decision<void, LedgerError>, LedgerState] => {
|
|
1227
1325
|
const stored = current.submissions.get(request.submissionId);
|
|
1326
|
+
|
|
1228
1327
|
if (stored === undefined) {
|
|
1229
1328
|
return [
|
|
1230
1329
|
failure(ledgerError("revertJoining", `Unknown Submission ${request.submissionId}`)),
|
|
@@ -1234,6 +1333,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1234
1333
|
// Idempotent and recovery-only: only a still-`joining` Submission reverts; an
|
|
1235
1334
|
// already-joined (or already-reverted) Submission is a no-op (DUR-016).
|
|
1236
1335
|
if (stored.row.state !== "joining") return [success(undefined), current];
|
|
1336
|
+
|
|
1237
1337
|
return [
|
|
1238
1338
|
success(undefined),
|
|
1239
1339
|
withSubmission(current, {
|
|
@@ -1244,6 +1344,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1244
1344
|
];
|
|
1245
1345
|
},
|
|
1246
1346
|
);
|
|
1347
|
+
|
|
1247
1348
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1248
1349
|
}),
|
|
1249
1350
|
);
|
|
@@ -1254,6 +1355,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1254
1355
|
Effect.gen(function* () {
|
|
1255
1356
|
const request = yield* validate(SuspendRequest, "suspend", unvalidated);
|
|
1256
1357
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1358
|
+
|
|
1257
1359
|
const decision = yield* Ref.modify(
|
|
1258
1360
|
state,
|
|
1259
1361
|
(
|
|
@@ -1263,6 +1365,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1263
1365
|
LedgerState,
|
|
1264
1366
|
] => {
|
|
1265
1367
|
const stored = current.submissions.get(request.submissionId);
|
|
1368
|
+
|
|
1266
1369
|
if (stored === undefined) {
|
|
1267
1370
|
return [
|
|
1268
1371
|
failure(ledgerError("suspend", `Unknown Submission ${request.submissionId}`)),
|
|
@@ -1281,6 +1384,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1281
1384
|
current,
|
|
1282
1385
|
];
|
|
1283
1386
|
}
|
|
1387
|
+
|
|
1284
1388
|
return [
|
|
1285
1389
|
failure(
|
|
1286
1390
|
SettlementConflict.make({
|
|
@@ -1307,6 +1411,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1307
1411
|
if (!ownsLane(stored, request.ownershipToken)) {
|
|
1308
1412
|
return [failure(ownershipLost(current, stored)), current];
|
|
1309
1413
|
}
|
|
1414
|
+
|
|
1310
1415
|
// A covering event that raced ahead of the suspend transaction (an approval decision,
|
|
1311
1416
|
// or a child settlement observed directly from the child's row in this single store)
|
|
1312
1417
|
// resumes the caller immediately WITHOUT releasing the lane (plan §2.6, spec §12).
|
|
@@ -1319,9 +1424,11 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1319
1424
|
(child) =>
|
|
1320
1425
|
current.submissions.get(child.childSubmissionId)?.row.state === "settled",
|
|
1321
1426
|
);
|
|
1427
|
+
|
|
1322
1428
|
if (alreadyCovered) {
|
|
1323
1429
|
return [success("resume-immediately" as const), current];
|
|
1324
1430
|
}
|
|
1431
|
+
|
|
1325
1432
|
return [
|
|
1326
1433
|
success("suspended" as const),
|
|
1327
1434
|
withSubmission(current, {
|
|
@@ -1333,7 +1440,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1333
1440
|
];
|
|
1334
1441
|
},
|
|
1335
1442
|
);
|
|
1443
|
+
|
|
1336
1444
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1445
|
+
|
|
1337
1446
|
return decision.value;
|
|
1338
1447
|
}),
|
|
1339
1448
|
);
|
|
@@ -1347,7 +1456,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1347
1456
|
"recordApprovalDecision",
|
|
1348
1457
|
unvalidated,
|
|
1349
1458
|
);
|
|
1459
|
+
|
|
1350
1460
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1461
|
+
|
|
1351
1462
|
const decision = yield* Ref.modify(
|
|
1352
1463
|
state,
|
|
1353
1464
|
(
|
|
@@ -1357,6 +1468,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1357
1468
|
LedgerState,
|
|
1358
1469
|
] => {
|
|
1359
1470
|
const stored = current.submissions.get(command.submissionId);
|
|
1471
|
+
|
|
1360
1472
|
if (stored === undefined) {
|
|
1361
1473
|
return [
|
|
1362
1474
|
failure(
|
|
@@ -1380,6 +1492,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1380
1492
|
current,
|
|
1381
1493
|
];
|
|
1382
1494
|
}
|
|
1495
|
+
|
|
1383
1496
|
return [
|
|
1384
1497
|
failure(
|
|
1385
1498
|
SettlementConflict.make({
|
|
@@ -1391,6 +1504,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1391
1504
|
];
|
|
1392
1505
|
}
|
|
1393
1506
|
const existing = stored.approvalDecisions.get(command.toolCallId);
|
|
1507
|
+
|
|
1394
1508
|
if (existing !== undefined) {
|
|
1395
1509
|
if (existing.decision !== command.decision) {
|
|
1396
1510
|
return [
|
|
@@ -1404,8 +1518,10 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1404
1518
|
current,
|
|
1405
1519
|
];
|
|
1406
1520
|
}
|
|
1521
|
+
|
|
1407
1522
|
return [success(existing), current];
|
|
1408
1523
|
}
|
|
1524
|
+
|
|
1409
1525
|
const intent = ApprovalDecisionIntent.make({
|
|
1410
1526
|
submissionId: command.submissionId,
|
|
1411
1527
|
toolCallId: command.toolCallId,
|
|
@@ -1414,10 +1530,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1414
1530
|
reason: command.reason,
|
|
1415
1531
|
decidedAt: utc(nowMillis),
|
|
1416
1532
|
});
|
|
1533
|
+
|
|
1417
1534
|
const approvalDecisions = new Map(stored.approvalDecisions).set(
|
|
1418
1535
|
command.toolCallId,
|
|
1419
1536
|
intent,
|
|
1420
1537
|
);
|
|
1538
|
+
|
|
1421
1539
|
// Once every pending call of an ApprovalPending suspension is decided, the lane
|
|
1422
1540
|
// wakes: suspended → input-applied (plan §2.6). A WaitingForChild suspension wakes
|
|
1423
1541
|
// only through recordChildSettled.
|
|
@@ -1428,6 +1546,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1428
1546
|
stored.suspension.reason.toolCallIds.every((toolCallId) =>
|
|
1429
1547
|
approvalDecisions.has(toolCallId),
|
|
1430
1548
|
);
|
|
1549
|
+
|
|
1431
1550
|
return [
|
|
1432
1551
|
success(intent),
|
|
1433
1552
|
withSubmission(current, {
|
|
@@ -1439,7 +1558,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1439
1558
|
];
|
|
1440
1559
|
},
|
|
1441
1560
|
);
|
|
1561
|
+
|
|
1442
1562
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1563
|
+
|
|
1443
1564
|
return decision.value;
|
|
1444
1565
|
}),
|
|
1445
1566
|
);
|
|
@@ -1449,10 +1570,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1449
1570
|
)((unvalidated) =>
|
|
1450
1571
|
Effect.gen(function* () {
|
|
1451
1572
|
const request = yield* validate(MarkUnknownRequest, "markUnknown", unvalidated);
|
|
1573
|
+
|
|
1452
1574
|
const decision = yield* Ref.modify(
|
|
1453
1575
|
state,
|
|
1454
1576
|
(current): readonly [Decision<void, SettlementConflict | LedgerError>, LedgerState] => {
|
|
1455
1577
|
const stored = current.submissions.get(request.submissionId);
|
|
1578
|
+
|
|
1456
1579
|
if (stored === undefined) {
|
|
1457
1580
|
return [
|
|
1458
1581
|
failure(ledgerError("markUnknown", `Unknown Submission ${request.submissionId}`)),
|
|
@@ -1471,6 +1594,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1471
1594
|
current,
|
|
1472
1595
|
];
|
|
1473
1596
|
}
|
|
1597
|
+
|
|
1474
1598
|
return [
|
|
1475
1599
|
failure(
|
|
1476
1600
|
SettlementConflict.make({
|
|
@@ -1498,10 +1622,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1498
1622
|
// set while the first recorded reason is kept.
|
|
1499
1623
|
const existing = stored.unknownMark;
|
|
1500
1624
|
const known = new Set(existing?.toolCallIds ?? []);
|
|
1625
|
+
|
|
1501
1626
|
const merged = [
|
|
1502
1627
|
...(existing?.toolCallIds ?? []),
|
|
1503
1628
|
...request.toolCallIds.filter((toolCallId) => !known.has(toolCallId)),
|
|
1504
1629
|
];
|
|
1630
|
+
|
|
1505
1631
|
return [
|
|
1506
1632
|
success(undefined),
|
|
1507
1633
|
withSubmission(current, {
|
|
@@ -1513,6 +1639,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1513
1639
|
];
|
|
1514
1640
|
},
|
|
1515
1641
|
);
|
|
1642
|
+
|
|
1516
1643
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1517
1644
|
}),
|
|
1518
1645
|
);
|
|
@@ -1525,7 +1652,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1525
1652
|
"recordUnknownResolution",
|
|
1526
1653
|
unvalidated,
|
|
1527
1654
|
);
|
|
1655
|
+
|
|
1528
1656
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1657
|
+
|
|
1529
1658
|
const decision = yield* Ref.modify(
|
|
1530
1659
|
state,
|
|
1531
1660
|
(
|
|
@@ -1538,6 +1667,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1538
1667
|
LedgerState,
|
|
1539
1668
|
] => {
|
|
1540
1669
|
const stored = current.submissions.get(command.submissionId);
|
|
1670
|
+
|
|
1541
1671
|
if (stored === undefined) {
|
|
1542
1672
|
return [
|
|
1543
1673
|
failure(
|
|
@@ -1561,6 +1691,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1561
1691
|
current,
|
|
1562
1692
|
];
|
|
1563
1693
|
}
|
|
1694
|
+
|
|
1564
1695
|
return [
|
|
1565
1696
|
failure(
|
|
1566
1697
|
SettlementConflict.make({
|
|
@@ -1572,6 +1703,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1572
1703
|
];
|
|
1573
1704
|
}
|
|
1574
1705
|
const existing = stored.unknownResolutions.get(command.toolCallId);
|
|
1706
|
+
|
|
1575
1707
|
if (
|
|
1576
1708
|
existing !== undefined &&
|
|
1577
1709
|
!equivalentUnknownResolution(existing.intent.resolution, command.resolution)
|
|
@@ -1586,6 +1718,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1586
1718
|
current,
|
|
1587
1719
|
];
|
|
1588
1720
|
}
|
|
1721
|
+
|
|
1589
1722
|
const intent =
|
|
1590
1723
|
existing?.intent ??
|
|
1591
1724
|
UnknownResolutionIntent.make({
|
|
@@ -1596,12 +1729,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1596
1729
|
resolution: command.resolution,
|
|
1597
1730
|
resolvedAt: utc(nowMillis),
|
|
1598
1731
|
});
|
|
1732
|
+
|
|
1599
1733
|
const unknownResolutions =
|
|
1600
1734
|
existing !== undefined
|
|
1601
1735
|
? stored.unknownResolutions
|
|
1602
1736
|
: new Map(stored.unknownResolutions).set(command.toolCallId, {
|
|
1603
1737
|
intent,
|
|
1604
1738
|
});
|
|
1739
|
+
|
|
1605
1740
|
// The lane reopens only when EVERY marked open call has a durable resolution
|
|
1606
1741
|
// intent: unknown → input-applied (DUR-017). Replays re-run the coverage check so
|
|
1607
1742
|
// a recovering caller can wake the lane idempotently.
|
|
@@ -1611,6 +1746,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1611
1746
|
stored.unknownMark.toolCallIds.every((toolCallId) =>
|
|
1612
1747
|
unknownResolutions.has(toolCallId),
|
|
1613
1748
|
);
|
|
1749
|
+
|
|
1614
1750
|
return [
|
|
1615
1751
|
success(intent),
|
|
1616
1752
|
withSubmission(current, {
|
|
@@ -1622,7 +1758,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1622
1758
|
];
|
|
1623
1759
|
},
|
|
1624
1760
|
);
|
|
1761
|
+
|
|
1625
1762
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1763
|
+
|
|
1626
1764
|
return decision.value;
|
|
1627
1765
|
}),
|
|
1628
1766
|
);
|
|
@@ -1636,10 +1774,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1636
1774
|
"recordChildSettled",
|
|
1637
1775
|
unvalidated,
|
|
1638
1776
|
);
|
|
1777
|
+
|
|
1639
1778
|
const decision = yield* Ref.modify(
|
|
1640
1779
|
state,
|
|
1641
1780
|
(current): readonly [Decision<ChildSettledOutcome, LedgerError>, LedgerState] => {
|
|
1642
1781
|
const parent = current.submissions.get(request.parentSubmissionId);
|
|
1782
|
+
|
|
1643
1783
|
if (parent === undefined) {
|
|
1644
1784
|
return [
|
|
1645
1785
|
failure(
|
|
@@ -1656,10 +1796,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1656
1796
|
// narrow durable prefix that makes that ordering admissible; earlier states remain
|
|
1657
1797
|
// a caller error.
|
|
1658
1798
|
const child = current.submissions.get(request.childSubmissionId);
|
|
1799
|
+
|
|
1659
1800
|
const announced =
|
|
1660
1801
|
child !== undefined &&
|
|
1661
1802
|
(child.row.state === "settled" ||
|
|
1662
1803
|
(child.row.state === "terminalizing" && child.reservation !== undefined));
|
|
1804
|
+
|
|
1663
1805
|
if (!announced) {
|
|
1664
1806
|
return [
|
|
1665
1807
|
failure(
|
|
@@ -1679,19 +1821,24 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1679
1821
|
return [success("not-waiting" as const), current];
|
|
1680
1822
|
}
|
|
1681
1823
|
const children = parent.suspension.reason.children;
|
|
1824
|
+
|
|
1682
1825
|
if (!children.some((entry) => entry.childSubmissionId === request.childSubmissionId)) {
|
|
1683
1826
|
return [success("not-waiting" as const), current];
|
|
1684
1827
|
}
|
|
1828
|
+
|
|
1685
1829
|
// Every listed child must be either finalized or canonically announced from the
|
|
1686
1830
|
// exact terminalizing reservation. Replays re-run this coverage check idempotently.
|
|
1687
1831
|
const allSettled = children.every((entry) => {
|
|
1688
1832
|
const listed = current.submissions.get(entry.childSubmissionId);
|
|
1833
|
+
|
|
1689
1834
|
return (
|
|
1690
1835
|
listed?.row.state === "settled" ||
|
|
1691
1836
|
(listed?.row.state === "terminalizing" && listed.reservation !== undefined)
|
|
1692
1837
|
);
|
|
1693
1838
|
});
|
|
1839
|
+
|
|
1694
1840
|
if (!allSettled) return [success("still-waiting" as const), current];
|
|
1841
|
+
|
|
1695
1842
|
return [
|
|
1696
1843
|
success("woken" as const),
|
|
1697
1844
|
withSubmission(current, {
|
|
@@ -1702,7 +1849,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1702
1849
|
];
|
|
1703
1850
|
},
|
|
1704
1851
|
);
|
|
1852
|
+
|
|
1705
1853
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1854
|
+
|
|
1706
1855
|
return decision.value;
|
|
1707
1856
|
}),
|
|
1708
1857
|
);
|
|
@@ -1716,7 +1865,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1716
1865
|
"reserveChildBudget",
|
|
1717
1866
|
unvalidated,
|
|
1718
1867
|
);
|
|
1868
|
+
|
|
1719
1869
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1870
|
+
|
|
1720
1871
|
const decision = yield* Ref.modify(
|
|
1721
1872
|
state,
|
|
1722
1873
|
(
|
|
@@ -1726,6 +1877,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1726
1877
|
LedgerState,
|
|
1727
1878
|
] => {
|
|
1728
1879
|
const existing = current.childReservations.get(request.reservationId);
|
|
1880
|
+
|
|
1729
1881
|
if (existing !== undefined) {
|
|
1730
1882
|
// Identical replays short-circuit before the fence, mirroring reserveSettlement:
|
|
1731
1883
|
// a replay creates nothing, so a recovering caller resumes rather than duplicates.
|
|
@@ -1734,6 +1886,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1734
1886
|
existing.parentToolCallId === request.parentToolCallId &&
|
|
1735
1887
|
existing.allocationDigest === request.allocationDigest &&
|
|
1736
1888
|
equivalentPersistedJson(existing.allocation, request.allocation);
|
|
1889
|
+
|
|
1737
1890
|
if (!identical) {
|
|
1738
1891
|
return [
|
|
1739
1892
|
failure(
|
|
@@ -1747,6 +1900,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1747
1900
|
current,
|
|
1748
1901
|
];
|
|
1749
1902
|
}
|
|
1903
|
+
|
|
1750
1904
|
return [
|
|
1751
1905
|
success(
|
|
1752
1906
|
ReservedChildBudget.make({
|
|
@@ -1775,6 +1929,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1775
1929
|
}
|
|
1776
1930
|
}
|
|
1777
1931
|
const parent = current.submissions.get(request.parentSubmissionId);
|
|
1932
|
+
|
|
1778
1933
|
if (parent === undefined) {
|
|
1779
1934
|
return [
|
|
1780
1935
|
failure(
|
|
@@ -1791,6 +1946,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1791
1946
|
if (!ownsLane(parent, request.ownershipToken)) {
|
|
1792
1947
|
return [failure(ownershipLost(current, parent)), current];
|
|
1793
1948
|
}
|
|
1949
|
+
|
|
1794
1950
|
const reservation: StoredChildReservation = {
|
|
1795
1951
|
reservationId: request.reservationId,
|
|
1796
1952
|
parentSubmissionId: request.parentSubmissionId,
|
|
@@ -1804,6 +1960,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1804
1960
|
releaseBeganAtMillis: undefined,
|
|
1805
1961
|
releasedAtMillis: undefined,
|
|
1806
1962
|
};
|
|
1963
|
+
|
|
1807
1964
|
return [
|
|
1808
1965
|
success(
|
|
1809
1966
|
ReservedChildBudget.make({
|
|
@@ -1815,7 +1972,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1815
1972
|
];
|
|
1816
1973
|
},
|
|
1817
1974
|
);
|
|
1975
|
+
|
|
1818
1976
|
if (decision._tag === "failure") return yield* decision.error;
|
|
1977
|
+
|
|
1819
1978
|
return decision.value;
|
|
1820
1979
|
}),
|
|
1821
1980
|
);
|
|
@@ -1828,6 +1987,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1828
1987
|
"attachChildToReservation",
|
|
1829
1988
|
unvalidated,
|
|
1830
1989
|
);
|
|
1990
|
+
|
|
1831
1991
|
const decision = yield* Ref.modify(
|
|
1832
1992
|
state,
|
|
1833
1993
|
(
|
|
@@ -1840,6 +2000,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1840
2000
|
LedgerState,
|
|
1841
2001
|
] => {
|
|
1842
2002
|
const reservation = current.childReservations.get(request.reservationId);
|
|
2003
|
+
|
|
1843
2004
|
if (reservation === undefined) {
|
|
1844
2005
|
return [
|
|
1845
2006
|
failure(
|
|
@@ -1856,6 +2017,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1856
2017
|
if (reservation.childSubmissionId === request.childSubmissionId) {
|
|
1857
2018
|
return [success(toReservationSnapshot(reservation)), current];
|
|
1858
2019
|
}
|
|
2020
|
+
|
|
1859
2021
|
return [
|
|
1860
2022
|
failure(
|
|
1861
2023
|
ChildReservationConflict.make({
|
|
@@ -1868,6 +2030,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1868
2030
|
];
|
|
1869
2031
|
}
|
|
1870
2032
|
const parent = current.submissions.get(reservation.parentSubmissionId);
|
|
2033
|
+
|
|
1871
2034
|
if (parent === undefined) {
|
|
1872
2035
|
return [
|
|
1873
2036
|
failure(
|
|
@@ -1907,17 +2070,21 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1907
2070
|
current,
|
|
1908
2071
|
];
|
|
1909
2072
|
}
|
|
2073
|
+
|
|
1910
2074
|
const attached: StoredChildReservation = {
|
|
1911
2075
|
...reservation,
|
|
1912
2076
|
childSubmissionId: request.childSubmissionId,
|
|
1913
2077
|
};
|
|
2078
|
+
|
|
1914
2079
|
return [
|
|
1915
2080
|
success(toReservationSnapshot(attached)),
|
|
1916
2081
|
withChildReservation(current, attached),
|
|
1917
2082
|
];
|
|
1918
2083
|
},
|
|
1919
2084
|
);
|
|
2085
|
+
|
|
1920
2086
|
if (decision._tag === "failure") return yield* decision.error;
|
|
2087
|
+
|
|
1921
2088
|
return decision.value;
|
|
1922
2089
|
}),
|
|
1923
2090
|
);
|
|
@@ -1930,7 +2097,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1930
2097
|
"beginChildBudgetRelease",
|
|
1931
2098
|
unvalidated,
|
|
1932
2099
|
);
|
|
2100
|
+
|
|
1933
2101
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
2102
|
+
|
|
1934
2103
|
const decision = yield* Ref.modify(
|
|
1935
2104
|
state,
|
|
1936
2105
|
(
|
|
@@ -1940,6 +2109,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1940
2109
|
LedgerState,
|
|
1941
2110
|
] => {
|
|
1942
2111
|
const reservation = current.childReservations.get(request.reservationId);
|
|
2112
|
+
|
|
1943
2113
|
if (reservation === undefined) {
|
|
1944
2114
|
return [
|
|
1945
2115
|
failure(
|
|
@@ -1960,6 +2130,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1960
2130
|
) {
|
|
1961
2131
|
return [success(toReservationSnapshot(reservation)), current];
|
|
1962
2132
|
}
|
|
2133
|
+
|
|
1963
2134
|
return [
|
|
1964
2135
|
failure(
|
|
1965
2136
|
ChildReservationConflict.make({
|
|
@@ -1972,19 +2143,23 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1972
2143
|
current,
|
|
1973
2144
|
];
|
|
1974
2145
|
}
|
|
2146
|
+
|
|
1975
2147
|
const frozen: StoredChildReservation = {
|
|
1976
2148
|
...reservation,
|
|
1977
2149
|
status: "releasePending",
|
|
1978
2150
|
accounting: request.accounting,
|
|
1979
2151
|
releaseBeganAtMillis: nowMillis,
|
|
1980
2152
|
};
|
|
2153
|
+
|
|
1981
2154
|
return [
|
|
1982
2155
|
success(toReservationSnapshot(frozen)),
|
|
1983
2156
|
withChildReservation(current, frozen),
|
|
1984
2157
|
];
|
|
1985
2158
|
},
|
|
1986
2159
|
);
|
|
2160
|
+
|
|
1987
2161
|
if (decision._tag === "failure") return yield* decision.error;
|
|
2162
|
+
|
|
1988
2163
|
return decision.value;
|
|
1989
2164
|
}),
|
|
1990
2165
|
);
|
|
@@ -1998,7 +2173,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
1998
2173
|
"releaseChildBudget",
|
|
1999
2174
|
unvalidated,
|
|
2000
2175
|
);
|
|
2176
|
+
|
|
2001
2177
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
2178
|
+
|
|
2002
2179
|
const decision = yield* Ref.modify(
|
|
2003
2180
|
state,
|
|
2004
2181
|
(
|
|
@@ -2008,6 +2185,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
2008
2185
|
LedgerState,
|
|
2009
2186
|
] => {
|
|
2010
2187
|
const reservation = current.childReservations.get(request.reservationId);
|
|
2188
|
+
|
|
2011
2189
|
if (reservation === undefined) {
|
|
2012
2190
|
return [
|
|
2013
2191
|
failure(
|
|
@@ -2037,18 +2215,22 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
2037
2215
|
current,
|
|
2038
2216
|
];
|
|
2039
2217
|
}
|
|
2218
|
+
|
|
2040
2219
|
const released: StoredChildReservation = {
|
|
2041
2220
|
...reservation,
|
|
2042
2221
|
status: "released",
|
|
2043
2222
|
releasedAtMillis: nowMillis,
|
|
2044
2223
|
};
|
|
2224
|
+
|
|
2045
2225
|
return [
|
|
2046
2226
|
success(toReservationSnapshot(released)),
|
|
2047
2227
|
withChildReservation(current, released),
|
|
2048
2228
|
];
|
|
2049
2229
|
},
|
|
2050
2230
|
);
|
|
2231
|
+
|
|
2051
2232
|
if (decision._tag === "failure") return yield* decision.error;
|
|
2233
|
+
|
|
2052
2234
|
return decision.value;
|
|
2053
2235
|
}),
|
|
2054
2236
|
);
|
|
@@ -2066,6 +2248,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
2066
2248
|
: left.row.queueSequence - right.row.queueSequence,
|
|
2067
2249
|
)
|
|
2068
2250
|
.map((stored) => toSnapshot(stored.row));
|
|
2251
|
+
|
|
2069
2252
|
return Stream.fromIterable(snapshots);
|
|
2070
2253
|
}),
|
|
2071
2254
|
),
|
|
@@ -2080,14 +2263,17 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
2080
2263
|
"loadRecoverySnapshot",
|
|
2081
2264
|
unvalidated,
|
|
2082
2265
|
);
|
|
2266
|
+
|
|
2083
2267
|
const current = yield* Ref.get(state);
|
|
2084
2268
|
const stored = current.submissions.get(request.submissionId);
|
|
2269
|
+
|
|
2085
2270
|
if (stored === undefined) {
|
|
2086
2271
|
return yield* ledgerError(
|
|
2087
2272
|
"loadRecoverySnapshot",
|
|
2088
2273
|
`Unknown Submission ${request.submissionId}`,
|
|
2089
2274
|
);
|
|
2090
2275
|
}
|
|
2276
|
+
|
|
2091
2277
|
const joins = [...current.submissions.values()]
|
|
2092
2278
|
.filter((candidate) => candidate.joinedHostSubmissionId === request.submissionId)
|
|
2093
2279
|
.sort((left, right) => left.row.queueSequence - right.row.queueSequence)
|
|
@@ -2098,11 +2284,13 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
2098
2284
|
hostSubmissionId: request.submissionId,
|
|
2099
2285
|
}),
|
|
2100
2286
|
);
|
|
2287
|
+
|
|
2101
2288
|
const byToolCallId = <A extends { readonly toolCallId: ToolCallId }>(
|
|
2102
2289
|
left: A,
|
|
2103
2290
|
right: A,
|
|
2104
2291
|
): number =>
|
|
2105
2292
|
left.toolCallId < right.toolCallId ? -1 : left.toolCallId > right.toolCallId ? 1 : 0;
|
|
2293
|
+
|
|
2106
2294
|
// Parent-side subagent view: this Submission's child budget reservations in parent Tool
|
|
2107
2295
|
// Call order, plus each attached child's current lane state (a disposable derived view;
|
|
2108
2296
|
// the canonical records stay the recovery truth, DUR-015).
|
|
@@ -2115,10 +2303,13 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
2115
2303
|
? 1
|
|
2116
2304
|
: 0,
|
|
2117
2305
|
);
|
|
2306
|
+
|
|
2118
2307
|
const childAttachments: Array<ChildAttachmentSnapshot> = [];
|
|
2308
|
+
|
|
2119
2309
|
for (const reservation of childReservations) {
|
|
2120
2310
|
if (reservation.childSubmissionId === undefined) continue;
|
|
2121
2311
|
const child = current.submissions.get(reservation.childSubmissionId);
|
|
2312
|
+
|
|
2122
2313
|
if (child === undefined) continue;
|
|
2123
2314
|
childAttachments.push(
|
|
2124
2315
|
ChildAttachmentSnapshot.make({
|
|
@@ -2131,6 +2322,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
|
|
|
2131
2322
|
}),
|
|
2132
2323
|
);
|
|
2133
2324
|
}
|
|
2325
|
+
|
|
2134
2326
|
return RecoverySnapshot.make({
|
|
2135
2327
|
submission: toSnapshot(stored.row),
|
|
2136
2328
|
joins,
|