@effect-agent/storage-cloudflare 0.1.0-beta.42 → 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/dist/testing.mjs.map +1 -1
- package/package.json +1 -52
- package/src/do-journal.ts +42 -0
- package/src/do-ledger.ts +309 -0
- package/src/do-schedule-store.ts +49 -0
- package/src/do-storage-failpoint-testing.ts +1 -0
- package/src/do-subscription-store.ts +153 -0
- package/src/do-thread-store.ts +74 -0
- package/src/errors.ts +1 -0
- package/src/memory-protocol.ts +24 -0
- package/src/port-protocol.ts +3 -0
- package/src/routing.ts +41 -0
package/src/do-ledger.ts
CHANGED
|
@@ -290,9 +290,11 @@ const decodeSuspensionSnapshot = Schema.decodeUnknownEffect(SuspensionSnapshot);
|
|
|
290
290
|
const decodeApprovalDecisionIntent = Schema.decodeUnknownEffect(ApprovalDecisionIntent);
|
|
291
291
|
const decodeUnknownResolutionIntent = Schema.decodeUnknownEffect(UnknownResolutionIntent);
|
|
292
292
|
const decodeParentLinkage = Schema.decodeUnknownEffect(ParentLinkage);
|
|
293
|
+
|
|
293
294
|
const decodeChildReservationSnapshotUnknown = Schema.decodeUnknownEffect(
|
|
294
295
|
ChildBudgetReservationSnapshot,
|
|
295
296
|
);
|
|
297
|
+
|
|
296
298
|
const decodeChildAttachmentSnapshot = Schema.decodeUnknownEffect(ChildAttachmentSnapshot);
|
|
297
299
|
const equivalentPersistedJson = Schema.toEquivalence(PersistedJson);
|
|
298
300
|
const equivalentUnknownResolution = Schema.toEquivalence(UnknownResolution);
|
|
@@ -394,7 +396,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
394
396
|
FROM effect_agent_submissions
|
|
395
397
|
WHERE submission_id = ${submissionId}
|
|
396
398
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
399
|
+
|
|
397
400
|
const decoded = yield* decodeSubmissionRows(operation, submissionId, rows);
|
|
401
|
+
|
|
398
402
|
if (decoded.length > 1) {
|
|
399
403
|
return yield* corruptionFailure(
|
|
400
404
|
operation,
|
|
@@ -403,6 +407,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
403
407
|
"A submission primary key returned more than one row.",
|
|
404
408
|
);
|
|
405
409
|
}
|
|
410
|
+
|
|
406
411
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
407
412
|
});
|
|
408
413
|
|
|
@@ -411,12 +416,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
411
416
|
submissionId: string,
|
|
412
417
|
): Effect.fn.Return<SubmissionRow, LedgerError> {
|
|
413
418
|
const submission = yield* readSubmission(operation, submissionId);
|
|
419
|
+
|
|
414
420
|
if (Option.isNone(submission)) {
|
|
415
421
|
return yield* LedgerError.make({
|
|
416
422
|
operation,
|
|
417
423
|
message: `Unknown submission ${submissionId}.`,
|
|
418
424
|
});
|
|
419
425
|
}
|
|
426
|
+
|
|
420
427
|
return submission.value;
|
|
421
428
|
});
|
|
422
429
|
|
|
@@ -435,12 +442,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
435
442
|
FROM effect_agent_submission_ownership
|
|
436
443
|
WHERE submission_id = ${submissionId}
|
|
437
444
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
445
|
+
|
|
438
446
|
const decoded = yield* decodeRows(
|
|
439
447
|
Schema.Array(OwnershipRow),
|
|
440
448
|
"effect_agent_submission_ownership",
|
|
441
449
|
submissionId,
|
|
442
450
|
rows,
|
|
443
451
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
452
|
+
|
|
444
453
|
if (decoded.length > 1) {
|
|
445
454
|
return yield* corruptionFailure(
|
|
446
455
|
operation,
|
|
@@ -449,6 +458,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
449
458
|
"An ownership primary key returned more than one row.",
|
|
450
459
|
);
|
|
451
460
|
}
|
|
461
|
+
|
|
452
462
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
453
463
|
});
|
|
454
464
|
|
|
@@ -459,6 +469,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
459
469
|
const threads = yield* journal
|
|
460
470
|
.getThread(threadId)
|
|
461
471
|
.pipe(Effect.mapError(internalFailure(operation)));
|
|
472
|
+
|
|
462
473
|
return threads.length === 0 ? EPOCH_ZERO : threads[0].producer_epoch;
|
|
463
474
|
});
|
|
464
475
|
|
|
@@ -473,13 +484,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
473
484
|
ownershipToken: string,
|
|
474
485
|
): Effect.fn.Return<OwnershipRow, OwnershipLost | LedgerError> {
|
|
475
486
|
const ownership = yield* readOwnership(operation, submission.submission_id);
|
|
487
|
+
|
|
476
488
|
if (Option.isNone(ownership) || ownership.value.ownership_token !== ownershipToken) {
|
|
477
489
|
const actualEpoch = yield* threadEpoch(operation, submission.thread_id);
|
|
490
|
+
|
|
478
491
|
const submissionId = yield* Schema.decodeUnknownEffect(
|
|
479
492
|
SubmissionSnapshot.fields.submissionId,
|
|
480
493
|
)(submission.submission_id).pipe(Effect.mapError(internalFailure(operation)));
|
|
494
|
+
|
|
481
495
|
return yield* OwnershipLost.make({ submissionId, actualEpoch });
|
|
482
496
|
}
|
|
497
|
+
|
|
483
498
|
return ownership.value;
|
|
484
499
|
});
|
|
485
500
|
|
|
@@ -498,6 +513,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
498
513
|
),
|
|
499
514
|
),
|
|
500
515
|
);
|
|
516
|
+
|
|
501
517
|
const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(
|
|
502
518
|
Effect.mapError((error) =>
|
|
503
519
|
corruptionFailure(
|
|
@@ -508,6 +524,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
508
524
|
),
|
|
509
525
|
),
|
|
510
526
|
);
|
|
527
|
+
|
|
511
528
|
if ((row.parent_submission_id === null) !== (row.parent_tool_call_id === null)) {
|
|
512
529
|
return yield* corruptionFailure(
|
|
513
530
|
operation,
|
|
@@ -516,6 +533,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
516
533
|
"A parent linkage must record both the parent Submission and the parent Tool Call.",
|
|
517
534
|
);
|
|
518
535
|
}
|
|
536
|
+
|
|
519
537
|
return yield* decodeSubmissionSnapshotUnknown({
|
|
520
538
|
submissionId: row.submission_id,
|
|
521
539
|
threadId: row.thread_id,
|
|
@@ -570,12 +588,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
570
588
|
FROM effect_agent_settlement_reservations
|
|
571
589
|
WHERE submission_id = ${submissionId}
|
|
572
590
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
591
|
+
|
|
573
592
|
const decoded = yield* decodeRows(
|
|
574
593
|
Schema.Array(ReservationRow),
|
|
575
594
|
"effect_agent_settlement_reservations",
|
|
576
595
|
submissionId,
|
|
577
596
|
rows,
|
|
578
597
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
598
|
+
|
|
579
599
|
if (decoded.length > 1) {
|
|
580
600
|
return yield* corruptionFailure(
|
|
581
601
|
operation,
|
|
@@ -584,6 +604,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
584
604
|
"A settlement reservation primary key returned more than one row.",
|
|
585
605
|
);
|
|
586
606
|
}
|
|
607
|
+
|
|
587
608
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
588
609
|
});
|
|
589
610
|
|
|
@@ -601,12 +622,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
601
622
|
FROM effect_agent_abort_intents
|
|
602
623
|
WHERE submission_id = ${submissionId}
|
|
603
624
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
625
|
+
|
|
604
626
|
const decoded = yield* decodeRows(
|
|
605
627
|
Schema.Array(AbortIntentRow),
|
|
606
628
|
"effect_agent_abort_intents",
|
|
607
629
|
submissionId,
|
|
608
630
|
rows,
|
|
609
631
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
632
|
+
|
|
610
633
|
if (decoded.length > 1) {
|
|
611
634
|
return yield* corruptionFailure(
|
|
612
635
|
operation,
|
|
@@ -615,6 +638,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
615
638
|
"An abort intent primary key returned more than one row.",
|
|
616
639
|
);
|
|
617
640
|
}
|
|
641
|
+
|
|
618
642
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
619
643
|
});
|
|
620
644
|
|
|
@@ -638,6 +662,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
638
662
|
WHERE parent_submission_id = ${parentSubmissionId}
|
|
639
663
|
ORDER BY child_submission_id ASC
|
|
640
664
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
665
|
+
|
|
641
666
|
return yield* decodeRows(
|
|
642
667
|
Schema.Array(ChildSettlementMarkerRow),
|
|
643
668
|
"effect_agent_child_settlements",
|
|
@@ -660,6 +685,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
660
685
|
): Effect.fn.Return<boolean, LedgerError> {
|
|
661
686
|
if (markerChildren.has(childSubmissionId)) return true;
|
|
662
687
|
const childRow = yield* readSubmission(operation, childSubmissionId);
|
|
688
|
+
|
|
663
689
|
return Option.isSome(childRow) && childRow.value.state === "settled";
|
|
664
690
|
});
|
|
665
691
|
|
|
@@ -680,7 +706,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
680
706
|
FROM effect_agent_child_reservations
|
|
681
707
|
WHERE reservation_id = ${reservationId}
|
|
682
708
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
709
|
+
|
|
683
710
|
const decoded = yield* decodeChildReservationRows(operation, reservationId, rows);
|
|
711
|
+
|
|
684
712
|
if (decoded.length > 1) {
|
|
685
713
|
return yield* corruptionFailure(
|
|
686
714
|
operation,
|
|
@@ -689,6 +717,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
689
717
|
"A child reservation primary key returned more than one row.",
|
|
690
718
|
);
|
|
691
719
|
}
|
|
720
|
+
|
|
692
721
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
693
722
|
});
|
|
694
723
|
|
|
@@ -704,11 +733,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
704
733
|
WHERE parent_submission_id = ${parentSubmissionId}
|
|
705
734
|
AND parent_tool_call_id = ${parentToolCallId}
|
|
706
735
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
736
|
+
|
|
707
737
|
const decoded = yield* decodeChildReservationRows(
|
|
708
738
|
operation,
|
|
709
739
|
`${parentSubmissionId}/${parentToolCallId}`,
|
|
710
740
|
rows,
|
|
711
741
|
);
|
|
742
|
+
|
|
712
743
|
if (decoded.length > 1) {
|
|
713
744
|
return yield* corruptionFailure(
|
|
714
745
|
operation,
|
|
@@ -717,6 +748,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
717
748
|
"A parent Tool Call returned more than one child reservation.",
|
|
718
749
|
);
|
|
719
750
|
}
|
|
751
|
+
|
|
720
752
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
721
753
|
},
|
|
722
754
|
);
|
|
@@ -734,13 +766,16 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
734
766
|
row.reservation_id,
|
|
735
767
|
error.message,
|
|
736
768
|
);
|
|
769
|
+
|
|
737
770
|
const allocation = yield* parseStoredJsonText(row.allocation_json).pipe(
|
|
738
771
|
Effect.mapError(rowFailure),
|
|
739
772
|
);
|
|
773
|
+
|
|
740
774
|
const accounting =
|
|
741
775
|
row.accounting_json === null
|
|
742
776
|
? undefined
|
|
743
777
|
: yield* parseStoredJsonText(row.accounting_json).pipe(Effect.mapError(rowFailure));
|
|
778
|
+
|
|
744
779
|
return yield* decodeChildReservationSnapshotUnknown({
|
|
745
780
|
reservationId: row.reservation_id,
|
|
746
781
|
parentSubmissionId: row.parent_submission_id,
|
|
@@ -772,6 +807,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
772
807
|
WHERE submission_id = ${submissionId}
|
|
773
808
|
ORDER BY tool_call_id ASC
|
|
774
809
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
810
|
+
|
|
775
811
|
return yield* decodeRows(
|
|
776
812
|
Schema.Array(ApprovalDecisionRow),
|
|
777
813
|
"effect_agent_approval_decisions",
|
|
@@ -819,6 +855,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
819
855
|
WHERE submission_id = ${submissionId}
|
|
820
856
|
ORDER BY tool_call_id ASC
|
|
821
857
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
858
|
+
|
|
822
859
|
return yield* decodeRows(
|
|
823
860
|
Schema.Array(UnknownResolutionRow),
|
|
824
861
|
"effect_agent_unknown_resolutions",
|
|
@@ -843,6 +880,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
843
880
|
),
|
|
844
881
|
),
|
|
845
882
|
);
|
|
883
|
+
|
|
846
884
|
return yield* decodeUnknownResolutionIntent({
|
|
847
885
|
submissionId: row.submission_id,
|
|
848
886
|
toolCallId: row.tool_call_id,
|
|
@@ -869,6 +907,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
869
907
|
submission: SubmissionRow,
|
|
870
908
|
): Effect.fn.Return<ReadonlyArray<typeof ToolCallIdSchema.Type>, LedgerError> {
|
|
871
909
|
if (submission.unknown_tool_call_ids_json === null) return [];
|
|
910
|
+
|
|
872
911
|
return yield* decodeToolCallIdsText(submission.unknown_tool_call_ids_json).pipe(
|
|
873
912
|
Effect.mapError((error) =>
|
|
874
913
|
corruptionFailure(
|
|
@@ -893,18 +932,21 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
893
932
|
submissionId: SubmissionId,
|
|
894
933
|
): Effect.fn.Return<string | undefined, LedgerError> {
|
|
895
934
|
const recordId = submissionAbortRecordId(submissionId);
|
|
935
|
+
|
|
896
936
|
const rows = yield* sql<Record<string, unknown>>`
|
|
897
937
|
SELECT record_id
|
|
898
938
|
FROM effect_agent_canonical_records
|
|
899
939
|
WHERE thread_id = ${threadId}
|
|
900
940
|
AND record_id = ${recordId}
|
|
901
941
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
942
|
+
|
|
902
943
|
const decoded = yield* decodeRows(
|
|
903
944
|
Schema.Array(CanonicalRecordIdRow),
|
|
904
945
|
"effect_agent_canonical_records",
|
|
905
946
|
`${threadId}/${recordId}`,
|
|
906
947
|
rows,
|
|
907
948
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
949
|
+
|
|
908
950
|
return decoded.length === 0 ? undefined : recordId;
|
|
909
951
|
});
|
|
910
952
|
|
|
@@ -919,6 +961,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
919
961
|
submission.thread_id,
|
|
920
962
|
submissionId,
|
|
921
963
|
);
|
|
964
|
+
|
|
922
965
|
return yield* decodeAbortIntent({
|
|
923
966
|
submissionId: row.submission_id,
|
|
924
967
|
author: row.author,
|
|
@@ -947,25 +990,31 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
947
990
|
const admit: SubmissionLedger["Service"]["admit"] = Effect.fn("DoSubmissionLedger.admit")(
|
|
948
991
|
function* (request: AdmissionRequest) {
|
|
949
992
|
const operation = "ledger admit";
|
|
993
|
+
|
|
950
994
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(AdmissionRequest))(
|
|
951
995
|
request,
|
|
952
996
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
997
|
+
|
|
953
998
|
const inputJson = yield* encodePersistedJsonText(validated.inputPayload).pipe(
|
|
954
999
|
Effect.mapError(internalFailure(operation)),
|
|
955
1000
|
);
|
|
1001
|
+
|
|
956
1002
|
// The platform's ~2 MB per-value bound, refused typed BEFORE any durable mutation
|
|
957
1003
|
// (resource-limits gate; oversized payloads are the designed R2 overflow path).
|
|
958
1004
|
yield* journal
|
|
959
1005
|
.checkValueBound(operation, inputJson)
|
|
960
1006
|
.pipe(Effect.mapError(internalFailure(operation)));
|
|
1007
|
+
|
|
961
1008
|
const agentDigestsJson = yield* encodeDefinitionDigestsText(validated.agentDigests).pipe(
|
|
962
1009
|
Effect.mapError(internalFailure(operation)),
|
|
963
1010
|
);
|
|
1011
|
+
|
|
964
1012
|
// Routable Submission identity (D-P6-5): `{uuidv7}:{threadId}`. The cross-DO
|
|
965
1013
|
// routing layer parses ITS OWN minted format (split at the first ":") to address
|
|
966
1014
|
// submissionId-only operations to the owning Thread Object; the id stays opaque
|
|
967
1015
|
// to every other component, exactly like DN's `submission-{uuid}` prefix.
|
|
968
1016
|
const mintedSubmissionId = `${yield* mintUuid(operation)}:${validated.threadId}`;
|
|
1017
|
+
|
|
969
1018
|
if (mintedSubmissionId.length > MAX_IDENTIFIER_LENGTH) {
|
|
970
1019
|
return yield* LedgerError.make({
|
|
971
1020
|
operation,
|
|
@@ -975,11 +1024,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
975
1024
|
});
|
|
976
1025
|
}
|
|
977
1026
|
const mintedReceiptId = `receipt-${yield* mintUuid(operation)}`;
|
|
1027
|
+
|
|
978
1028
|
yield* hitFailpoint("ledger:admit:before", operation);
|
|
1029
|
+
|
|
979
1030
|
const result = yield* inWriteTransaction(
|
|
980
1031
|
operation,
|
|
981
1032
|
Effect.gen(function* () {
|
|
982
1033
|
const keyRowKey = `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`;
|
|
1034
|
+
|
|
983
1035
|
const existingRows = yield* sql<Record<string, unknown>>`
|
|
984
1036
|
SELECT ${sql.literal(SUBMISSION_COLUMNS)}
|
|
985
1037
|
FROM effect_agent_submissions
|
|
@@ -987,7 +1039,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
987
1039
|
AND principal = ${validated.principal}
|
|
988
1040
|
AND idempotency_key = ${validated.idempotencyKey}
|
|
989
1041
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1042
|
+
|
|
990
1043
|
const existing = yield* decodeSubmissionRows(operation, keyRowKey, existingRows);
|
|
1044
|
+
|
|
991
1045
|
if (existing.length > 1) {
|
|
992
1046
|
return yield* corruptionFailure(
|
|
993
1047
|
operation,
|
|
@@ -1005,6 +1059,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1005
1059
|
existing[0].parent_tool_call_id === null
|
|
1006
1060
|
: existing[0].parent_submission_id === validated.parentLinkage.parentSubmissionId &&
|
|
1007
1061
|
existing[0].parent_tool_call_id === validated.parentLinkage.parentToolCallId;
|
|
1062
|
+
|
|
1008
1063
|
if (existing[0].input_digest !== validated.inputDigest || !sameLinkage) {
|
|
1009
1064
|
return yield* AdmissionConflict.make({
|
|
1010
1065
|
threadId: validated.threadId,
|
|
@@ -1014,6 +1069,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1014
1069
|
attemptedInputDigest: validated.inputDigest,
|
|
1015
1070
|
});
|
|
1016
1071
|
}
|
|
1072
|
+
|
|
1017
1073
|
return yield* decodeAdmissionResult({
|
|
1018
1074
|
submissionId: existing[0].submission_id,
|
|
1019
1075
|
receiptId: existing[0].receipt_id,
|
|
@@ -1028,15 +1084,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1028
1084
|
FROM effect_agent_submissions
|
|
1029
1085
|
WHERE thread_id = ${validated.threadId}
|
|
1030
1086
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1087
|
+
|
|
1031
1088
|
const decodedMax = yield* decodeRows(
|
|
1032
1089
|
Schema.Array(MaxQueueSequenceRow),
|
|
1033
1090
|
"effect_agent_submissions",
|
|
1034
1091
|
validated.threadId,
|
|
1035
1092
|
maxRows,
|
|
1036
1093
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1094
|
+
|
|
1037
1095
|
const queueSequence = yield* decodeQueueSequence(
|
|
1038
1096
|
(decodedMax[0]?.max_queue_sequence ?? 0) + 1,
|
|
1039
1097
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1098
|
+
|
|
1040
1099
|
const now = yield* currentInstant;
|
|
1041
1100
|
|
|
1042
1101
|
yield* sql`
|
|
@@ -1084,7 +1143,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1084
1143
|
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
1085
1144
|
}),
|
|
1086
1145
|
);
|
|
1146
|
+
|
|
1087
1147
|
yield* hitFailpoint("ledger:admit:after", operation);
|
|
1148
|
+
|
|
1088
1149
|
return result;
|
|
1089
1150
|
},
|
|
1090
1151
|
);
|
|
@@ -1093,16 +1154,20 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1093
1154
|
"DoSubmissionLedger.markReady",
|
|
1094
1155
|
)(function* (request: MarkReadyRequest) {
|
|
1095
1156
|
const operation = "ledger mark ready";
|
|
1157
|
+
|
|
1096
1158
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(MarkReadyRequest))(
|
|
1097
1159
|
request,
|
|
1098
1160
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1161
|
+
|
|
1099
1162
|
yield* hitFailpoint("ledger:mark-ready:before", operation);
|
|
1100
1163
|
yield* inWriteTransaction(
|
|
1101
1164
|
operation,
|
|
1102
1165
|
Effect.gen(function* () {
|
|
1103
1166
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1167
|
+
|
|
1104
1168
|
if (submission.state !== "admitted") return;
|
|
1105
1169
|
const now = yield* currentInstant;
|
|
1170
|
+
|
|
1106
1171
|
yield* sql`
|
|
1107
1172
|
UPDATE effect_agent_submissions
|
|
1108
1173
|
SET state = 'ready', ready_at = ${now.iso}
|
|
@@ -1116,14 +1181,19 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1116
1181
|
const lookup: SubmissionLedger["Service"]["lookup"] = Effect.fn("DoSubmissionLedger.lookup")(
|
|
1117
1182
|
function* (request: SubmissionLookup) {
|
|
1118
1183
|
const operation = "ledger lookup";
|
|
1184
|
+
|
|
1119
1185
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(SubmissionLookup))(
|
|
1120
1186
|
request,
|
|
1121
1187
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1188
|
+
|
|
1122
1189
|
if (validated._tag === "SubmissionLookupById") {
|
|
1123
1190
|
const row = yield* readSubmission(operation, validated.submissionId);
|
|
1191
|
+
|
|
1124
1192
|
if (Option.isNone(row)) return Option.none();
|
|
1193
|
+
|
|
1125
1194
|
return Option.some(yield* decodeSubmissionSnapshot(operation, row.value));
|
|
1126
1195
|
}
|
|
1196
|
+
|
|
1127
1197
|
const rows = yield* sql<Record<string, unknown>>`
|
|
1128
1198
|
SELECT ${sql.literal(SUBMISSION_COLUMNS)}
|
|
1129
1199
|
FROM effect_agent_submissions
|
|
@@ -1131,11 +1201,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1131
1201
|
AND principal = ${validated.principal}
|
|
1132
1202
|
AND idempotency_key = ${validated.idempotencyKey}
|
|
1133
1203
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1204
|
+
|
|
1134
1205
|
const decoded = yield* decodeSubmissionRows(
|
|
1135
1206
|
operation,
|
|
1136
1207
|
`${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`,
|
|
1137
1208
|
rows,
|
|
1138
1209
|
);
|
|
1210
|
+
|
|
1139
1211
|
if (decoded.length > 1) {
|
|
1140
1212
|
return yield* corruptionFailure(
|
|
1141
1213
|
operation,
|
|
@@ -1145,6 +1217,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1145
1217
|
);
|
|
1146
1218
|
}
|
|
1147
1219
|
if (decoded.length === 0) return Option.none();
|
|
1220
|
+
|
|
1148
1221
|
return Option.some(yield* decodeSubmissionSnapshot(operation, decoded[0]));
|
|
1149
1222
|
},
|
|
1150
1223
|
);
|
|
@@ -1157,9 +1230,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1157
1230
|
"DoSubmissionLedger.resolveAdmission",
|
|
1158
1231
|
)(function* (request: SubmissionLookupByKey) {
|
|
1159
1232
|
const operation = "ledger resolve admission";
|
|
1233
|
+
|
|
1160
1234
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(SubmissionLookupByKey))(
|
|
1161
1235
|
request,
|
|
1162
1236
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1237
|
+
|
|
1163
1238
|
const rows = yield* sql<Record<string, unknown>>`
|
|
1164
1239
|
SELECT ${sql.literal(SUBMISSION_COLUMNS)}
|
|
1165
1240
|
FROM effect_agent_submissions
|
|
@@ -1167,11 +1242,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1167
1242
|
AND principal = ${validated.principal}
|
|
1168
1243
|
AND idempotency_key = ${validated.idempotencyKey}
|
|
1169
1244
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1245
|
+
|
|
1170
1246
|
const decoded = yield* decodeSubmissionRows(
|
|
1171
1247
|
operation,
|
|
1172
1248
|
`${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`,
|
|
1173
1249
|
rows,
|
|
1174
1250
|
);
|
|
1251
|
+
|
|
1175
1252
|
if (decoded.length > 1) {
|
|
1176
1253
|
return yield* corruptionFailure(
|
|
1177
1254
|
operation,
|
|
@@ -1181,6 +1258,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1181
1258
|
);
|
|
1182
1259
|
}
|
|
1183
1260
|
if (decoded.length === 0) return AdmissionNotAdmitted.make();
|
|
1261
|
+
|
|
1184
1262
|
return AdmissionAdmitted.make({
|
|
1185
1263
|
submission: yield* decodeSubmissionSnapshot(operation, decoded[0]),
|
|
1186
1264
|
});
|
|
@@ -1189,12 +1267,16 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1189
1267
|
const claim: SubmissionLedger["Service"]["claim"] = Effect.fn("DoSubmissionLedger.claim")(
|
|
1190
1268
|
function* (request: ClaimRequest) {
|
|
1191
1269
|
const operation = "ledger claim";
|
|
1270
|
+
|
|
1192
1271
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ClaimRequest))(
|
|
1193
1272
|
request,
|
|
1194
1273
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1274
|
+
|
|
1195
1275
|
const attemptId = `attempt-${yield* mintUuid(operation)}`;
|
|
1196
1276
|
const ownershipToken = `owner-${yield* mintUuid(operation)}`;
|
|
1277
|
+
|
|
1197
1278
|
yield* hitFailpoint("ledger:claim:before", operation);
|
|
1279
|
+
|
|
1198
1280
|
const claimed = yield* inWriteTransaction(
|
|
1199
1281
|
operation,
|
|
1200
1282
|
Effect.gen(function* () {
|
|
@@ -1206,7 +1288,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1206
1288
|
ORDER BY queue_sequence ASC
|
|
1207
1289
|
LIMIT 1
|
|
1208
1290
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1291
|
+
|
|
1209
1292
|
const heads = yield* decodeSubmissionRows(operation, validated.threadId, headRows);
|
|
1293
|
+
|
|
1210
1294
|
if (heads.length === 0) return Option.none<Claim>();
|
|
1211
1295
|
const head = heads[0];
|
|
1212
1296
|
|
|
@@ -1225,11 +1309,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1225
1309
|
|
|
1226
1310
|
const now = yield* currentInstant;
|
|
1227
1311
|
const ownership = yield* readOwnership(operation, head.submission_id);
|
|
1312
|
+
|
|
1228
1313
|
if (Option.isSome(ownership)) {
|
|
1229
1314
|
const expiresAt = yield* timestampMillis(
|
|
1230
1315
|
operation,
|
|
1231
1316
|
head.submission_id,
|
|
1232
1317
|
)(ownership.value.lease_expires_at);
|
|
1318
|
+
|
|
1233
1319
|
// A live lease blocks every new claim; expiry alone only revokes the liveness
|
|
1234
1320
|
// assumption — correctness stays with producer-epoch fencing (D5). In DC a live
|
|
1235
1321
|
// lease under another token can only come from an evicted incarnation.
|
|
@@ -1243,7 +1329,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1243
1329
|
const threads = yield* journal
|
|
1244
1330
|
.getThread(head.thread_id)
|
|
1245
1331
|
.pipe(Effect.mapError(internalFailure(operation)));
|
|
1332
|
+
|
|
1246
1333
|
let producerEpoch: number;
|
|
1334
|
+
|
|
1247
1335
|
if (threads.length === 0) {
|
|
1248
1336
|
producerEpoch = 1;
|
|
1249
1337
|
yield* sql`
|
|
@@ -1271,6 +1359,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1271
1359
|
}
|
|
1272
1360
|
|
|
1273
1361
|
const leaseExpiresAt = new Date(now.millis + config.ownershipLeaseDuration).toISOString();
|
|
1362
|
+
|
|
1274
1363
|
yield* sql`
|
|
1275
1364
|
INSERT INTO effect_agent_submission_ownership (
|
|
1276
1365
|
submission_id,
|
|
@@ -1331,6 +1420,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1331
1420
|
),
|
|
1332
1421
|
),
|
|
1333
1422
|
);
|
|
1423
|
+
|
|
1334
1424
|
return Option.some(
|
|
1335
1425
|
yield* decodeClaim({
|
|
1336
1426
|
submissionId: head.submission_id,
|
|
@@ -1343,7 +1433,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1343
1433
|
);
|
|
1344
1434
|
}),
|
|
1345
1435
|
);
|
|
1436
|
+
|
|
1346
1437
|
yield* hitFailpoint("ledger:claim:after", operation);
|
|
1438
|
+
|
|
1347
1439
|
return claimed;
|
|
1348
1440
|
},
|
|
1349
1441
|
);
|
|
@@ -1352,29 +1444,37 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1352
1444
|
"DoSubmissionLedger.renewOwnership",
|
|
1353
1445
|
)(function* (request: RenewOwnershipRequest) {
|
|
1354
1446
|
const operation = "ledger renew ownership";
|
|
1447
|
+
|
|
1355
1448
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(RenewOwnershipRequest))(
|
|
1356
1449
|
request,
|
|
1357
1450
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1451
|
+
|
|
1358
1452
|
yield* hitFailpoint("ledger:renew:before", operation);
|
|
1453
|
+
|
|
1359
1454
|
const renewal = yield* inWriteTransaction(
|
|
1360
1455
|
operation,
|
|
1361
1456
|
Effect.gen(function* () {
|
|
1362
1457
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1458
|
+
|
|
1363
1459
|
yield* requireOwnership(operation, submission, validated.ownershipToken);
|
|
1364
1460
|
const now = yield* currentInstant;
|
|
1365
1461
|
const leaseExpiresAt = new Date(now.millis + config.ownershipLeaseDuration).toISOString();
|
|
1462
|
+
|
|
1366
1463
|
yield* sql`
|
|
1367
1464
|
UPDATE effect_agent_submission_ownership
|
|
1368
1465
|
SET lease_expires_at = ${leaseExpiresAt}
|
|
1369
1466
|
WHERE submission_id = ${validated.submissionId}
|
|
1370
1467
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1468
|
+
|
|
1371
1469
|
return yield* decodeOwnershipRenewal({
|
|
1372
1470
|
ownershipToken: validated.ownershipToken,
|
|
1373
1471
|
leaseExpiresAt,
|
|
1374
1472
|
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
1375
1473
|
}),
|
|
1376
1474
|
);
|
|
1475
|
+
|
|
1377
1476
|
yield* hitFailpoint("ledger:renew:after", operation);
|
|
1477
|
+
|
|
1378
1478
|
return renewal;
|
|
1379
1479
|
});
|
|
1380
1480
|
|
|
@@ -1382,14 +1482,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1382
1482
|
"DoSubmissionLedger.releaseOwnership",
|
|
1383
1483
|
)(function* (request: ReleaseOwnershipRequest) {
|
|
1384
1484
|
const operation = "ledger release ownership";
|
|
1485
|
+
|
|
1385
1486
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ReleaseOwnershipRequest))(
|
|
1386
1487
|
request,
|
|
1387
1488
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1489
|
+
|
|
1388
1490
|
yield* hitFailpoint("ledger:release:before", operation);
|
|
1389
1491
|
yield* inWriteTransaction(
|
|
1390
1492
|
operation,
|
|
1391
1493
|
Effect.gen(function* () {
|
|
1392
1494
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1495
|
+
|
|
1393
1496
|
yield* requireOwnership(operation, submission, validated.ownershipToken);
|
|
1394
1497
|
yield* sql`
|
|
1395
1498
|
DELETE FROM effect_agent_submission_ownership
|
|
@@ -1411,14 +1514,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1411
1514
|
"DoSubmissionLedger.markInputApplied",
|
|
1412
1515
|
)(function* (request: MarkInputAppliedRequest) {
|
|
1413
1516
|
const operation = "ledger mark input applied";
|
|
1517
|
+
|
|
1414
1518
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(MarkInputAppliedRequest))(
|
|
1415
1519
|
request,
|
|
1416
1520
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1521
|
+
|
|
1417
1522
|
yield* hitFailpoint("ledger:mark-input-applied:before", operation);
|
|
1418
1523
|
yield* inWriteTransaction(
|
|
1419
1524
|
operation,
|
|
1420
1525
|
Effect.gen(function* () {
|
|
1421
1526
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1527
|
+
|
|
1422
1528
|
yield* requireOwnership(operation, submission, validated.ownershipToken);
|
|
1423
1529
|
if (submission.input_applied_record_id !== null) {
|
|
1424
1530
|
if (
|
|
@@ -1427,6 +1533,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1427
1533
|
) {
|
|
1428
1534
|
return;
|
|
1429
1535
|
}
|
|
1536
|
+
|
|
1430
1537
|
return yield* corruptionFailure(
|
|
1431
1538
|
operation,
|
|
1432
1539
|
"effect_agent_submissions",
|
|
@@ -1454,34 +1561,41 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1454
1561
|
"DoSubmissionLedger.reserveSettlement",
|
|
1455
1562
|
)(function* (request: SettlementReservation) {
|
|
1456
1563
|
const operation = "ledger reserve settlement";
|
|
1564
|
+
|
|
1457
1565
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(SettlementReservation))(
|
|
1458
1566
|
request,
|
|
1459
1567
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1568
|
+
|
|
1460
1569
|
const recordJson = yield* encodeRecordEnvelopeText(validated.record).pipe(
|
|
1461
1570
|
Effect.mapError(internalFailure(operation)),
|
|
1462
1571
|
);
|
|
1572
|
+
|
|
1463
1573
|
// The reserved record is appended canonically later; refuse over-bound payloads typed
|
|
1464
1574
|
// before the reservation row exists.
|
|
1465
1575
|
yield* journal
|
|
1466
1576
|
.checkValueBound(operation, recordJson)
|
|
1467
1577
|
.pipe(Effect.mapError(internalFailure(operation)));
|
|
1468
1578
|
yield* hitFailpoint("ledger:reserve-settlement:before", operation);
|
|
1579
|
+
|
|
1469
1580
|
const reserved = yield* inWriteTransaction(
|
|
1470
1581
|
operation,
|
|
1471
1582
|
Effect.gen(function* () {
|
|
1472
1583
|
const existing = yield* readReservation(operation, validated.submissionId);
|
|
1584
|
+
|
|
1473
1585
|
if (Option.isSome(existing)) {
|
|
1474
1586
|
const identical =
|
|
1475
1587
|
existing.value.settlement_id === validated.settlementId &&
|
|
1476
1588
|
existing.value.outcome === validated.outcome &&
|
|
1477
1589
|
existing.value.record_digest === validated.recordDigest &&
|
|
1478
1590
|
existing.value.record_json === recordJson;
|
|
1591
|
+
|
|
1479
1592
|
if (!identical) {
|
|
1480
1593
|
return yield* SettlementConflict.make({
|
|
1481
1594
|
submissionId: validated.submissionId,
|
|
1482
1595
|
existingOutcome: existing.value.outcome,
|
|
1483
1596
|
});
|
|
1484
1597
|
}
|
|
1598
|
+
|
|
1485
1599
|
const record = yield* decodeRecordEnvelopeText(existing.value.record_json).pipe(
|
|
1486
1600
|
Effect.mapError((error) =>
|
|
1487
1601
|
corruptionFailure(
|
|
@@ -1492,6 +1606,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1492
1606
|
),
|
|
1493
1607
|
),
|
|
1494
1608
|
);
|
|
1609
|
+
|
|
1495
1610
|
return ReservedSettlement.make({
|
|
1496
1611
|
submissionId: validated.submissionId,
|
|
1497
1612
|
settlementId: validated.settlementId,
|
|
@@ -1503,6 +1618,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1503
1618
|
}
|
|
1504
1619
|
|
|
1505
1620
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1621
|
+
|
|
1506
1622
|
if (submission.state === "settled") {
|
|
1507
1623
|
if (submission.settled_outcome === null) {
|
|
1508
1624
|
return yield* corruptionFailure(
|
|
@@ -1512,6 +1628,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1512
1628
|
"A settled Submission carries no terminal outcome.",
|
|
1513
1629
|
);
|
|
1514
1630
|
}
|
|
1631
|
+
|
|
1515
1632
|
return yield* SettlementConflict.make({
|
|
1516
1633
|
submissionId: validated.submissionId,
|
|
1517
1634
|
existingOutcome: submission.settled_outcome,
|
|
@@ -1526,13 +1643,16 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1526
1643
|
// ABORTED settlement (`terminalizing` is the same pass's crash replay). Every other
|
|
1527
1644
|
// reservation stays fenced by the target lane's live ownership.
|
|
1528
1645
|
let queuedAbortSettlement = false;
|
|
1646
|
+
|
|
1529
1647
|
if (
|
|
1530
1648
|
validated.outcome === "aborted" &&
|
|
1531
1649
|
(submission.state === "ready" || submission.state === "terminalizing")
|
|
1532
1650
|
) {
|
|
1533
1651
|
const abortIntent = yield* readAbortIntent(operation, validated.submissionId);
|
|
1652
|
+
|
|
1534
1653
|
if (Option.isSome(abortIntent)) {
|
|
1535
1654
|
const ownership = yield* readOwnership(operation, validated.submissionId);
|
|
1655
|
+
|
|
1536
1656
|
queuedAbortSettlement = Option.isNone(ownership);
|
|
1537
1657
|
}
|
|
1538
1658
|
}
|
|
@@ -1541,6 +1661,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1541
1661
|
}
|
|
1542
1662
|
}
|
|
1543
1663
|
const now = yield* currentInstant;
|
|
1664
|
+
|
|
1544
1665
|
yield* sql`
|
|
1545
1666
|
INSERT INTO effect_agent_settlement_reservations (
|
|
1546
1667
|
submission_id,
|
|
@@ -1565,6 +1686,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1565
1686
|
SET state = 'terminalizing'
|
|
1566
1687
|
WHERE submission_id = ${validated.submissionId}
|
|
1567
1688
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1689
|
+
|
|
1568
1690
|
return ReservedSettlement.make({
|
|
1569
1691
|
submissionId: validated.submissionId,
|
|
1570
1692
|
settlementId: validated.settlementId,
|
|
@@ -1575,7 +1697,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1575
1697
|
});
|
|
1576
1698
|
}),
|
|
1577
1699
|
);
|
|
1700
|
+
|
|
1578
1701
|
yield* hitFailpoint("ledger:reserve-settlement:after", operation);
|
|
1702
|
+
|
|
1579
1703
|
return reserved;
|
|
1580
1704
|
});
|
|
1581
1705
|
|
|
@@ -1583,14 +1707,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1583
1707
|
"DoSubmissionLedger.finalizeSettlement",
|
|
1584
1708
|
)(function* (request: SettlementFinalization) {
|
|
1585
1709
|
const operation = "ledger finalize settlement";
|
|
1710
|
+
|
|
1586
1711
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(SettlementFinalization))(
|
|
1587
1712
|
request,
|
|
1588
1713
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1714
|
+
|
|
1589
1715
|
yield* hitFailpoint("ledger:finalize-settlement:before", operation);
|
|
1716
|
+
|
|
1590
1717
|
const settlement = yield* inWriteTransaction(
|
|
1591
1718
|
operation,
|
|
1592
1719
|
Effect.gen(function* () {
|
|
1593
1720
|
const reservation = yield* readReservation(operation, validated.submissionId);
|
|
1721
|
+
|
|
1594
1722
|
if (Option.isNone(reservation)) {
|
|
1595
1723
|
return yield* LedgerError.make({
|
|
1596
1724
|
operation,
|
|
@@ -1603,6 +1731,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1603
1731
|
existingOutcome: reservation.value.outcome,
|
|
1604
1732
|
});
|
|
1605
1733
|
}
|
|
1734
|
+
|
|
1606
1735
|
const reservationRecord = yield* decodeRecordEnvelopeText(
|
|
1607
1736
|
reservation.value.record_json,
|
|
1608
1737
|
).pipe(
|
|
@@ -1615,7 +1744,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1615
1744
|
),
|
|
1616
1745
|
),
|
|
1617
1746
|
);
|
|
1747
|
+
|
|
1618
1748
|
const settlementFailure = settlementFailureFromRecord(reservationRecord);
|
|
1749
|
+
|
|
1619
1750
|
if ((reservation.value.outcome === "failed") !== (settlementFailure !== undefined)) {
|
|
1620
1751
|
return yield* corruptionFailure(
|
|
1621
1752
|
operation,
|
|
@@ -1625,6 +1756,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1625
1756
|
);
|
|
1626
1757
|
}
|
|
1627
1758
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1759
|
+
|
|
1628
1760
|
if (submission.state === "settled") {
|
|
1629
1761
|
if (reservation.value.finalized_at === null) {
|
|
1630
1762
|
return yield* corruptionFailure(
|
|
@@ -1634,6 +1766,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1634
1766
|
"A settled Submission's reservation carries no finalization timestamp.",
|
|
1635
1767
|
);
|
|
1636
1768
|
}
|
|
1769
|
+
|
|
1637
1770
|
return yield* decodeSettlement({
|
|
1638
1771
|
submissionId: validated.submissionId,
|
|
1639
1772
|
settlementId: validated.settlementId,
|
|
@@ -1644,6 +1777,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1644
1777
|
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
1645
1778
|
}
|
|
1646
1779
|
const now = yield* currentInstant;
|
|
1780
|
+
|
|
1647
1781
|
yield* sql`
|
|
1648
1782
|
UPDATE effect_agent_submissions
|
|
1649
1783
|
SET state = 'settled', settled_outcome = ${reservation.value.outcome}
|
|
@@ -1658,6 +1792,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1658
1792
|
DELETE FROM effect_agent_submission_ownership
|
|
1659
1793
|
WHERE submission_id = ${validated.submissionId}
|
|
1660
1794
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1795
|
+
|
|
1661
1796
|
return yield* decodeSettlement({
|
|
1662
1797
|
submissionId: validated.submissionId,
|
|
1663
1798
|
settlementId: validated.settlementId,
|
|
@@ -1668,7 +1803,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1668
1803
|
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
1669
1804
|
}),
|
|
1670
1805
|
);
|
|
1806
|
+
|
|
1671
1807
|
yield* hitFailpoint("ledger:finalize-settlement:after", operation);
|
|
1808
|
+
|
|
1672
1809
|
return settlement;
|
|
1673
1810
|
});
|
|
1674
1811
|
|
|
@@ -1676,14 +1813,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1676
1813
|
"DoSubmissionLedger.requestAbort",
|
|
1677
1814
|
)(function* (request: AbortCommand) {
|
|
1678
1815
|
const operation = "ledger request abort";
|
|
1816
|
+
|
|
1679
1817
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(AbortCommand))(request).pipe(
|
|
1680
1818
|
Effect.mapError(internalFailure(operation)),
|
|
1681
1819
|
);
|
|
1820
|
+
|
|
1682
1821
|
yield* hitFailpoint("ledger:request-abort:before", operation);
|
|
1822
|
+
|
|
1683
1823
|
const intent = yield* inWriteTransaction(
|
|
1684
1824
|
operation,
|
|
1685
1825
|
Effect.gen(function* () {
|
|
1686
1826
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1827
|
+
|
|
1687
1828
|
if (submission.state === "settled") {
|
|
1688
1829
|
if (submission.settled_outcome === null) {
|
|
1689
1830
|
return yield* corruptionFailure(
|
|
@@ -1693,6 +1834,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1693
1834
|
"A settled Submission carries no terminal outcome.",
|
|
1694
1835
|
);
|
|
1695
1836
|
}
|
|
1837
|
+
|
|
1696
1838
|
return yield* SettlementConflict.make({
|
|
1697
1839
|
submissionId: validated.submissionId,
|
|
1698
1840
|
existingOutcome: submission.settled_outcome,
|
|
@@ -1710,15 +1852,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1710
1852
|
"A joined Submission carries no host linkage.",
|
|
1711
1853
|
);
|
|
1712
1854
|
}
|
|
1855
|
+
|
|
1713
1856
|
const hostSubmissionId = yield* decodeSubmissionId(
|
|
1714
1857
|
submission.joined_host_submission_id,
|
|
1715
1858
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1859
|
+
|
|
1716
1860
|
return yield* JoinedToHost.make({
|
|
1717
1861
|
submissionId: validated.submissionId,
|
|
1718
1862
|
hostSubmissionId,
|
|
1719
1863
|
});
|
|
1720
1864
|
}
|
|
1721
1865
|
const existing = yield* readAbortIntent(operation, validated.submissionId);
|
|
1866
|
+
|
|
1722
1867
|
if (Option.isSome(existing)) {
|
|
1723
1868
|
return yield* abortIntentFromRow(
|
|
1724
1869
|
operation,
|
|
@@ -1728,6 +1873,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1728
1873
|
);
|
|
1729
1874
|
}
|
|
1730
1875
|
const now = yield* currentInstant;
|
|
1876
|
+
|
|
1731
1877
|
yield* sql`
|
|
1732
1878
|
INSERT INTO effect_agent_abort_intents (
|
|
1733
1879
|
submission_id,
|
|
@@ -1741,11 +1887,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1741
1887
|
${now.iso}
|
|
1742
1888
|
)
|
|
1743
1889
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1890
|
+
|
|
1744
1891
|
const canonicalRecordId = yield* canonicalAbortRecordId(
|
|
1745
1892
|
operation,
|
|
1746
1893
|
submission.thread_id,
|
|
1747
1894
|
validated.submissionId,
|
|
1748
1895
|
);
|
|
1896
|
+
|
|
1749
1897
|
return yield* decodeAbortIntent({
|
|
1750
1898
|
submissionId: validated.submissionId,
|
|
1751
1899
|
author: validated.author,
|
|
@@ -1755,7 +1903,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1755
1903
|
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
1756
1904
|
}),
|
|
1757
1905
|
);
|
|
1906
|
+
|
|
1758
1907
|
yield* hitFailpoint("ledger:request-abort:after", operation);
|
|
1908
|
+
|
|
1759
1909
|
return intent;
|
|
1760
1910
|
});
|
|
1761
1911
|
|
|
@@ -1763,14 +1913,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1763
1913
|
"DoSubmissionLedger.claimJoining",
|
|
1764
1914
|
)(function* (request: ClaimJoiningRequest) {
|
|
1765
1915
|
const operation = "ledger claim joining";
|
|
1916
|
+
|
|
1766
1917
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ClaimJoiningRequest))(
|
|
1767
1918
|
request,
|
|
1768
1919
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
1920
|
+
|
|
1769
1921
|
yield* hitFailpoint("ledger:claim-joining:before", operation);
|
|
1922
|
+
|
|
1770
1923
|
const claims = yield* inWriteTransaction(
|
|
1771
1924
|
operation,
|
|
1772
1925
|
Effect.gen(function* () {
|
|
1773
1926
|
const host = yield* requireSubmission(operation, validated.hostSubmissionId);
|
|
1927
|
+
|
|
1774
1928
|
if (host.thread_id !== validated.threadId) {
|
|
1775
1929
|
return yield* LedgerError.make({
|
|
1776
1930
|
operation,
|
|
@@ -1779,6 +1933,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1779
1933
|
}
|
|
1780
1934
|
// The host Attempt already owns the lane; no epoch bump happens here (plan §2.5).
|
|
1781
1935
|
yield* requireOwnership(operation, host, validated.ownershipToken);
|
|
1936
|
+
|
|
1782
1937
|
const laterRows = yield* sql<Record<string, unknown>>`
|
|
1783
1938
|
SELECT ${sql.literal(SUBMISSION_COLUMNS)}
|
|
1784
1939
|
FROM effect_agent_submissions
|
|
@@ -1786,8 +1941,10 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1786
1941
|
AND queue_sequence > ${host.queue_sequence}
|
|
1787
1942
|
ORDER BY queue_sequence ASC
|
|
1788
1943
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1944
|
+
|
|
1789
1945
|
const later = yield* decodeSubmissionRows(operation, validated.threadId, laterRows);
|
|
1790
1946
|
const claimed: Array<JoiningClaim> = [];
|
|
1947
|
+
|
|
1791
1948
|
for (const row of later) {
|
|
1792
1949
|
if (claimed.length >= validated.maxCount) break;
|
|
1793
1950
|
// Rows already claimed by THIS host extend its contiguous prefix and are skipped;
|
|
@@ -1810,6 +1967,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1810
1967
|
SET state = 'joining', joined_host_submission_id = ${validated.hostSubmissionId}
|
|
1811
1968
|
WHERE submission_id = ${row.submission_id}
|
|
1812
1969
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1970
|
+
|
|
1813
1971
|
const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(
|
|
1814
1972
|
Effect.mapError((error) =>
|
|
1815
1973
|
corruptionFailure(
|
|
@@ -1820,6 +1978,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1820
1978
|
),
|
|
1821
1979
|
),
|
|
1822
1980
|
);
|
|
1981
|
+
|
|
1823
1982
|
claimed.push(
|
|
1824
1983
|
yield* decodeJoiningClaim({
|
|
1825
1984
|
submissionId: row.submission_id,
|
|
@@ -1828,10 +1987,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1828
1987
|
}).pipe(Effect.mapError(internalFailure(operation))),
|
|
1829
1988
|
);
|
|
1830
1989
|
}
|
|
1990
|
+
|
|
1831
1991
|
return claimed;
|
|
1832
1992
|
}),
|
|
1833
1993
|
);
|
|
1994
|
+
|
|
1834
1995
|
yield* hitFailpoint("ledger:claim-joining:after", operation);
|
|
1996
|
+
|
|
1835
1997
|
return claims;
|
|
1836
1998
|
});
|
|
1837
1999
|
|
|
@@ -1839,14 +2001,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1839
2001
|
"DoSubmissionLedger.markJoined",
|
|
1840
2002
|
)(function* (request: MarkJoinedRequest) {
|
|
1841
2003
|
const operation = "ledger mark joined";
|
|
2004
|
+
|
|
1842
2005
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(MarkJoinedRequest))(
|
|
1843
2006
|
request,
|
|
1844
2007
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
2008
|
+
|
|
1845
2009
|
yield* hitFailpoint("ledger:mark-joined:before", operation);
|
|
1846
2010
|
yield* inWriteTransaction(
|
|
1847
2011
|
operation,
|
|
1848
2012
|
Effect.gen(function* () {
|
|
1849
2013
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
2014
|
+
|
|
1850
2015
|
if (submission.joined_host_submission_id === null) {
|
|
1851
2016
|
return yield* LedgerError.make({
|
|
1852
2017
|
operation,
|
|
@@ -1854,6 +2019,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1854
2019
|
});
|
|
1855
2020
|
}
|
|
1856
2021
|
const host = yield* requireSubmission(operation, submission.joined_host_submission_id);
|
|
2022
|
+
|
|
1857
2023
|
// The lane is host-owned: the presented token must own the HOST's ownership period,
|
|
1858
2024
|
// which also lets a later host Attempt repair a lost marker from history (DUR-016).
|
|
1859
2025
|
yield* requireOwnership(operation, host, validated.ownershipToken);
|
|
@@ -1864,6 +2030,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1864
2030
|
) {
|
|
1865
2031
|
return;
|
|
1866
2032
|
}
|
|
2033
|
+
|
|
1867
2034
|
return yield* corruptionFailure(
|
|
1868
2035
|
operation,
|
|
1869
2036
|
"effect_agent_submissions",
|
|
@@ -1894,14 +2061,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1894
2061
|
"DoSubmissionLedger.revertJoining",
|
|
1895
2062
|
)(function* (request: RevertJoiningRequest) {
|
|
1896
2063
|
const operation = "ledger revert joining";
|
|
2064
|
+
|
|
1897
2065
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(RevertJoiningRequest))(
|
|
1898
2066
|
request,
|
|
1899
2067
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
2068
|
+
|
|
1900
2069
|
yield* hitFailpoint("ledger:revert-joining:before", operation);
|
|
1901
2070
|
yield* inWriteTransaction(
|
|
1902
2071
|
operation,
|
|
1903
2072
|
Effect.gen(function* () {
|
|
1904
2073
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
2074
|
+
|
|
1905
2075
|
// Idempotent and recovery-only: only a still-`joining` Submission reverts; an
|
|
1906
2076
|
// already-joined (or already-reverted) Submission is a no-op (DUR-016).
|
|
1907
2077
|
if (submission.state !== "joining") return;
|
|
@@ -1918,17 +2088,22 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1918
2088
|
const suspend: SubmissionLedger["Service"]["suspend"] = Effect.fn("DoSubmissionLedger.suspend")(
|
|
1919
2089
|
function* (request: SuspendRequest) {
|
|
1920
2090
|
const operation = "ledger suspend";
|
|
2091
|
+
|
|
1921
2092
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(SuspendRequest))(
|
|
1922
2093
|
request,
|
|
1923
2094
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
2095
|
+
|
|
1924
2096
|
const reasonJson = yield* encodeSuspensionReasonText(validated.reason).pipe(
|
|
1925
2097
|
Effect.mapError(internalFailure(operation)),
|
|
1926
2098
|
);
|
|
2099
|
+
|
|
1927
2100
|
yield* hitFailpoint("ledger:suspend:before", operation);
|
|
2101
|
+
|
|
1928
2102
|
const outcome = yield* inWriteTransaction(
|
|
1929
2103
|
operation,
|
|
1930
2104
|
Effect.gen(function* () {
|
|
1931
2105
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
2106
|
+
|
|
1932
2107
|
if (submission.state === "settled") {
|
|
1933
2108
|
if (submission.settled_outcome === null) {
|
|
1934
2109
|
return yield* corruptionFailure(
|
|
@@ -1938,6 +2113,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1938
2113
|
"A settled Submission carries no terminal outcome.",
|
|
1939
2114
|
);
|
|
1940
2115
|
}
|
|
2116
|
+
|
|
1941
2117
|
return yield* SettlementConflict.make({
|
|
1942
2118
|
submissionId: validated.submissionId,
|
|
1943
2119
|
existingOutcome: submission.settled_outcome,
|
|
@@ -1946,6 +2122,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1946
2122
|
// An exact terminal outcome is already reserved (DUR-011); suspension would
|
|
1947
2123
|
// contradict it, so the reservation wins.
|
|
1948
2124
|
const reservation = yield* readReservation(operation, validated.submissionId);
|
|
2125
|
+
|
|
1949
2126
|
if (Option.isSome(reservation)) {
|
|
1950
2127
|
return yield* SettlementConflict.make({
|
|
1951
2128
|
submissionId: validated.submissionId,
|
|
@@ -1962,6 +2139,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1962
2139
|
if (validated.reason._tag === "ApprovalPending") {
|
|
1963
2140
|
const decisions = yield* readApprovalDecisions(operation, validated.submissionId);
|
|
1964
2141
|
const decided = new Set(decisions.map((row) => row.tool_call_id));
|
|
2142
|
+
|
|
1965
2143
|
if (validated.reason.toolCallIds.every((toolCallId) => decided.has(toolCallId))) {
|
|
1966
2144
|
return RESUME_IMMEDIATELY;
|
|
1967
2145
|
}
|
|
@@ -1969,12 +2147,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1969
2147
|
const markers = yield* readChildSettlementMarkers(operation, validated.submissionId);
|
|
1970
2148
|
const markerChildren = new Set(markers.map((row) => row.child_submission_id));
|
|
1971
2149
|
let allSettled = true;
|
|
2150
|
+
|
|
1972
2151
|
for (const child of validated.reason.children) {
|
|
1973
2152
|
const settled = yield* childProvablySettled(
|
|
1974
2153
|
operation,
|
|
1975
2154
|
markerChildren,
|
|
1976
2155
|
child.childSubmissionId,
|
|
1977
2156
|
);
|
|
2157
|
+
|
|
1978
2158
|
if (!settled) {
|
|
1979
2159
|
allSettled = false;
|
|
1980
2160
|
break;
|
|
@@ -1985,6 +2165,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1985
2165
|
}
|
|
1986
2166
|
}
|
|
1987
2167
|
const now = yield* currentInstant;
|
|
2168
|
+
|
|
1988
2169
|
yield* sql`
|
|
1989
2170
|
UPDATE effect_agent_submissions
|
|
1990
2171
|
SET
|
|
@@ -1999,10 +2180,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1999
2180
|
DELETE FROM effect_agent_submission_ownership
|
|
2000
2181
|
WHERE submission_id = ${validated.submissionId}
|
|
2001
2182
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2183
|
+
|
|
2002
2184
|
return SUSPENDED;
|
|
2003
2185
|
}),
|
|
2004
2186
|
);
|
|
2187
|
+
|
|
2005
2188
|
yield* hitFailpoint("ledger:suspend:after", operation);
|
|
2189
|
+
|
|
2006
2190
|
return outcome;
|
|
2007
2191
|
},
|
|
2008
2192
|
);
|
|
@@ -2018,6 +2202,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2018
2202
|
submission: SubmissionRow,
|
|
2019
2203
|
): Effect.fn.Return<void, LedgerError> {
|
|
2020
2204
|
if (submission.state !== "suspended" || submission.suspended_reason_json === null) return;
|
|
2205
|
+
|
|
2021
2206
|
const reason = yield* Schema.decodeEffect(Schema.fromJsonString(SuspensionReason))(
|
|
2022
2207
|
submission.suspended_reason_json,
|
|
2023
2208
|
).pipe(
|
|
@@ -2030,9 +2215,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2030
2215
|
),
|
|
2031
2216
|
),
|
|
2032
2217
|
);
|
|
2218
|
+
|
|
2033
2219
|
if (reason._tag !== "ApprovalPending") return;
|
|
2034
2220
|
const decisions = yield* readApprovalDecisions(operation, submission.submission_id);
|
|
2035
2221
|
const decided = new Set(decisions.map((row) => row.tool_call_id));
|
|
2222
|
+
|
|
2036
2223
|
if (!reason.toolCallIds.every((toolCallId) => decided.has(toolCallId))) return;
|
|
2037
2224
|
yield* sql`
|
|
2038
2225
|
UPDATE effect_agent_submissions
|
|
@@ -2048,14 +2235,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2048
2235
|
"DoSubmissionLedger.recordApprovalDecision",
|
|
2049
2236
|
)(function* (command: ApprovalDecisionCommand) {
|
|
2050
2237
|
const operation = "ledger record approval decision";
|
|
2238
|
+
|
|
2051
2239
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ApprovalDecisionCommand))(
|
|
2052
2240
|
command,
|
|
2053
2241
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
2242
|
+
|
|
2054
2243
|
yield* hitFailpoint("ledger:approval-decision:before", operation);
|
|
2244
|
+
|
|
2055
2245
|
const intent = yield* inWriteTransaction(
|
|
2056
2246
|
operation,
|
|
2057
2247
|
Effect.gen(function* () {
|
|
2058
2248
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
2249
|
+
|
|
2059
2250
|
if (submission.state === "settled") {
|
|
2060
2251
|
if (submission.settled_outcome === null) {
|
|
2061
2252
|
return yield* corruptionFailure(
|
|
@@ -2065,6 +2256,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2065
2256
|
"A settled Submission carries no terminal outcome.",
|
|
2066
2257
|
);
|
|
2067
2258
|
}
|
|
2259
|
+
|
|
2068
2260
|
return yield* SettlementConflict.make({
|
|
2069
2261
|
submissionId: validated.submissionId,
|
|
2070
2262
|
existingOutcome: submission.settled_outcome,
|
|
@@ -2072,6 +2264,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2072
2264
|
}
|
|
2073
2265
|
const decisions = yield* readApprovalDecisions(operation, validated.submissionId);
|
|
2074
2266
|
const existing = decisions.find((row) => row.tool_call_id === validated.toolCallId);
|
|
2267
|
+
|
|
2075
2268
|
if (existing !== undefined) {
|
|
2076
2269
|
// Idempotent per (submissionId, toolCallId): repeating the SAME decision replays
|
|
2077
2270
|
// the recorded intent unchanged; a divergent re-decision conflicts.
|
|
@@ -2082,9 +2275,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2082
2275
|
existingDecision: existing.decision,
|
|
2083
2276
|
});
|
|
2084
2277
|
}
|
|
2278
|
+
|
|
2085
2279
|
return yield* approvalIntentFromRow(operation, existing);
|
|
2086
2280
|
}
|
|
2087
2281
|
const now = yield* currentInstant;
|
|
2282
|
+
|
|
2088
2283
|
yield* sql`
|
|
2089
2284
|
INSERT INTO effect_agent_approval_decisions (
|
|
2090
2285
|
submission_id,
|
|
@@ -2103,6 +2298,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2103
2298
|
)
|
|
2104
2299
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2105
2300
|
yield* wakeSuspendedIfCovered(operation, submission);
|
|
2301
|
+
|
|
2106
2302
|
return yield* decodeApprovalDecisionIntent({
|
|
2107
2303
|
submissionId: validated.submissionId,
|
|
2108
2304
|
toolCallId: validated.toolCallId,
|
|
@@ -2113,7 +2309,9 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2113
2309
|
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
2114
2310
|
}),
|
|
2115
2311
|
);
|
|
2312
|
+
|
|
2116
2313
|
yield* hitFailpoint("ledger:approval-decision:after", operation);
|
|
2314
|
+
|
|
2117
2315
|
return intent;
|
|
2118
2316
|
});
|
|
2119
2317
|
|
|
@@ -2121,14 +2319,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2121
2319
|
"DoSubmissionLedger.markUnknown",
|
|
2122
2320
|
)(function* (request: MarkUnknownRequest) {
|
|
2123
2321
|
const operation = "ledger mark unknown";
|
|
2322
|
+
|
|
2124
2323
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(MarkUnknownRequest))(
|
|
2125
2324
|
request,
|
|
2126
2325
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
2326
|
+
|
|
2127
2327
|
yield* hitFailpoint("ledger:mark-unknown:before", operation);
|
|
2128
2328
|
yield* inWriteTransaction(
|
|
2129
2329
|
operation,
|
|
2130
2330
|
Effect.gen(function* () {
|
|
2131
2331
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
2332
|
+
|
|
2132
2333
|
if (submission.state === "settled") {
|
|
2133
2334
|
if (submission.settled_outcome === null) {
|
|
2134
2335
|
return yield* corruptionFailure(
|
|
@@ -2138,6 +2339,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2138
2339
|
"A settled Submission carries no terminal outcome.",
|
|
2139
2340
|
);
|
|
2140
2341
|
}
|
|
2342
|
+
|
|
2141
2343
|
return yield* SettlementConflict.make({
|
|
2142
2344
|
submissionId: validated.submissionId,
|
|
2143
2345
|
existingOutcome: submission.settled_outcome,
|
|
@@ -2146,6 +2348,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2146
2348
|
// A reserved exact outcome wins over a late Unknown marking (DUR-011); the recovery
|
|
2147
2349
|
// classifier orders reservation ahead of MarkUnknown for the same reason.
|
|
2148
2350
|
const reservation = yield* readReservation(operation, validated.submissionId);
|
|
2351
|
+
|
|
2149
2352
|
if (Option.isSome(reservation)) {
|
|
2150
2353
|
return yield* SettlementConflict.make({
|
|
2151
2354
|
submissionId: validated.submissionId,
|
|
@@ -2156,13 +2359,16 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2156
2359
|
// set while the first recorded reason is kept.
|
|
2157
2360
|
const existingIds = yield* storedUnknownToolCallIds(operation, submission);
|
|
2158
2361
|
const known = new Set(existingIds);
|
|
2362
|
+
|
|
2159
2363
|
const merged = [
|
|
2160
2364
|
...existingIds,
|
|
2161
2365
|
...validated.toolCallIds.filter((toolCallId) => !known.has(toolCallId)),
|
|
2162
2366
|
];
|
|
2367
|
+
|
|
2163
2368
|
const idsJson = yield* encodeToolCallIdsText(merged).pipe(
|
|
2164
2369
|
Effect.mapError(internalFailure(operation)),
|
|
2165
2370
|
);
|
|
2371
|
+
|
|
2166
2372
|
yield* sql`
|
|
2167
2373
|
UPDATE effect_agent_submissions
|
|
2168
2374
|
SET
|
|
@@ -2180,17 +2386,22 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2180
2386
|
"DoSubmissionLedger.recordUnknownResolution",
|
|
2181
2387
|
)(function* (command: UnknownResolutionCommand) {
|
|
2182
2388
|
const operation = "ledger record unknown resolution";
|
|
2389
|
+
|
|
2183
2390
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(UnknownResolutionCommand))(
|
|
2184
2391
|
command,
|
|
2185
2392
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
2393
|
+
|
|
2186
2394
|
const resolutionJson = yield* encodeUnknownResolutionText(validated.resolution).pipe(
|
|
2187
2395
|
Effect.mapError(internalFailure(operation)),
|
|
2188
2396
|
);
|
|
2397
|
+
|
|
2189
2398
|
yield* hitFailpoint("ledger:unknown-resolution:before", operation);
|
|
2399
|
+
|
|
2190
2400
|
const intent = yield* inWriteTransaction(
|
|
2191
2401
|
operation,
|
|
2192
2402
|
Effect.gen(function* () {
|
|
2193
2403
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
2404
|
+
|
|
2194
2405
|
if (submission.state === "settled") {
|
|
2195
2406
|
if (submission.settled_outcome === null) {
|
|
2196
2407
|
return yield* corruptionFailure(
|
|
@@ -2200,6 +2411,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2200
2411
|
"A settled Submission carries no terminal outcome.",
|
|
2201
2412
|
);
|
|
2202
2413
|
}
|
|
2414
|
+
|
|
2203
2415
|
return yield* SettlementConflict.make({
|
|
2204
2416
|
submissionId: validated.submissionId,
|
|
2205
2417
|
existingOutcome: submission.settled_outcome,
|
|
@@ -2207,10 +2419,12 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2207
2419
|
}
|
|
2208
2420
|
const resolutions = yield* readUnknownResolutions(operation, validated.submissionId);
|
|
2209
2421
|
const existing = resolutions.find((row) => row.tool_call_id === validated.toolCallId);
|
|
2422
|
+
|
|
2210
2423
|
const existingIntent =
|
|
2211
2424
|
existing === undefined
|
|
2212
2425
|
? undefined
|
|
2213
2426
|
: yield* unknownResolutionIntentFromRow(operation, existing);
|
|
2427
|
+
|
|
2214
2428
|
if (
|
|
2215
2429
|
existingIntent !== undefined &&
|
|
2216
2430
|
!equivalentUnknownResolution(existingIntent.resolution, validated.resolution)
|
|
@@ -2221,12 +2435,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2221
2435
|
});
|
|
2222
2436
|
}
|
|
2223
2437
|
let resolved: UnknownResolutionIntent;
|
|
2438
|
+
|
|
2224
2439
|
if (existingIntent !== undefined) {
|
|
2225
2440
|
// Idempotent replay of the recorded intent (author/reason may differ; the stored
|
|
2226
2441
|
// audit fields win, exactly like requestAbort).
|
|
2227
2442
|
resolved = existingIntent;
|
|
2228
2443
|
} else {
|
|
2229
2444
|
const now = yield* currentInstant;
|
|
2445
|
+
|
|
2230
2446
|
yield* sql`
|
|
2231
2447
|
INSERT INTO effect_agent_unknown_resolutions (
|
|
2232
2448
|
submission_id,
|
|
@@ -2244,9 +2460,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2244
2460
|
${now.iso}
|
|
2245
2461
|
)
|
|
2246
2462
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2463
|
+
|
|
2247
2464
|
const resolution = yield* parseStoredJsonText(resolutionJson).pipe(
|
|
2248
2465
|
Effect.mapError(internalFailure(operation)),
|
|
2249
2466
|
);
|
|
2467
|
+
|
|
2250
2468
|
resolved = yield* decodeUnknownResolutionIntent({
|
|
2251
2469
|
submissionId: validated.submissionId,
|
|
2252
2470
|
toolCallId: validated.toolCallId,
|
|
@@ -2263,6 +2481,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2263
2481
|
const markedIds = yield* storedUnknownToolCallIds(operation, submission);
|
|
2264
2482
|
const covering = yield* readUnknownResolutions(operation, validated.submissionId);
|
|
2265
2483
|
const coveredIds = new Set(covering.map((row) => row.tool_call_id));
|
|
2484
|
+
|
|
2266
2485
|
if (markedIds.every((toolCallId) => coveredIds.has(toolCallId))) {
|
|
2267
2486
|
yield* sql`
|
|
2268
2487
|
UPDATE effect_agent_submissions
|
|
@@ -2274,10 +2493,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2274
2493
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2275
2494
|
}
|
|
2276
2495
|
}
|
|
2496
|
+
|
|
2277
2497
|
return resolved;
|
|
2278
2498
|
}),
|
|
2279
2499
|
);
|
|
2500
|
+
|
|
2280
2501
|
yield* hitFailpoint("ledger:unknown-resolution:after", operation);
|
|
2502
|
+
|
|
2281
2503
|
return intent;
|
|
2282
2504
|
});
|
|
2283
2505
|
|
|
@@ -2285,10 +2507,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2285
2507
|
"DoSubmissionLedger.recordChildSettled",
|
|
2286
2508
|
)(function* (request: ChildSettledNotification) {
|
|
2287
2509
|
const operation = "ledger record child settled";
|
|
2510
|
+
|
|
2288
2511
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ChildSettledNotification))(
|
|
2289
2512
|
request,
|
|
2290
2513
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
2514
|
+
|
|
2291
2515
|
yield* hitFailpoint("ledger:child-settled:before", operation);
|
|
2516
|
+
|
|
2292
2517
|
const outcome = yield* inWriteTransaction(
|
|
2293
2518
|
operation,
|
|
2294
2519
|
Effect.gen(function* () {
|
|
@@ -2301,6 +2526,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2301
2526
|
// child's owning Durable Object is the settlement evidence this store records durably.
|
|
2302
2527
|
const child = yield* readSubmission(operation, validated.childSubmissionId);
|
|
2303
2528
|
const childReservation = yield* readReservation(operation, validated.childSubmissionId);
|
|
2529
|
+
|
|
2304
2530
|
if (
|
|
2305
2531
|
Option.isSome(child) &&
|
|
2306
2532
|
child.value.state !== "settled" &&
|
|
@@ -2316,6 +2542,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2316
2542
|
// before the parent's suspend transaction is observed by that suspend's covering
|
|
2317
2543
|
// check, even when the parent is not (or not yet) suspended.
|
|
2318
2544
|
const now = yield* currentInstant;
|
|
2545
|
+
|
|
2319
2546
|
yield* sql`
|
|
2320
2547
|
INSERT INTO effect_agent_child_settlements (
|
|
2321
2548
|
parent_submission_id,
|
|
@@ -2340,6 +2567,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2340
2567
|
if (parent.state !== "suspended" || parent.suspended_reason_json === null) {
|
|
2341
2568
|
return NOT_WAITING;
|
|
2342
2569
|
}
|
|
2570
|
+
|
|
2343
2571
|
const reason = yield* Schema.decodeEffect(Schema.fromJsonString(SuspensionReason))(
|
|
2344
2572
|
parent.suspended_reason_json,
|
|
2345
2573
|
).pipe(
|
|
@@ -2352,6 +2580,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2352
2580
|
),
|
|
2353
2581
|
),
|
|
2354
2582
|
);
|
|
2583
|
+
|
|
2355
2584
|
if (reason._tag !== "WaitingForChild") {
|
|
2356
2585
|
return NOT_WAITING;
|
|
2357
2586
|
}
|
|
@@ -2365,12 +2594,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2365
2594
|
// check so a recovering caller wakes the lane idempotently.
|
|
2366
2595
|
const markers = yield* readChildSettlementMarkers(operation, validated.parentSubmissionId);
|
|
2367
2596
|
const markerChildren = new Set(markers.map((row) => row.child_submission_id));
|
|
2597
|
+
|
|
2368
2598
|
for (const entry of reason.children) {
|
|
2369
2599
|
const settled = yield* childProvablySettled(
|
|
2370
2600
|
operation,
|
|
2371
2601
|
markerChildren,
|
|
2372
2602
|
entry.childSubmissionId,
|
|
2373
2603
|
);
|
|
2604
|
+
|
|
2374
2605
|
if (!settled) {
|
|
2375
2606
|
return STILL_WAITING;
|
|
2376
2607
|
}
|
|
@@ -2383,10 +2614,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2383
2614
|
suspended_at = NULL
|
|
2384
2615
|
WHERE submission_id = ${validated.parentSubmissionId}
|
|
2385
2616
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2617
|
+
|
|
2386
2618
|
return WOKEN;
|
|
2387
2619
|
}),
|
|
2388
2620
|
);
|
|
2621
|
+
|
|
2389
2622
|
yield* hitFailpoint("ledger:child-settled:after", operation);
|
|
2623
|
+
|
|
2390
2624
|
return outcome;
|
|
2391
2625
|
});
|
|
2392
2626
|
|
|
@@ -2394,22 +2628,28 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2394
2628
|
"DoSubmissionLedger.reserveChildBudget",
|
|
2395
2629
|
)(function* (request: ChildBudgetReservationRequest) {
|
|
2396
2630
|
const operation = "ledger reserve child budget";
|
|
2631
|
+
|
|
2397
2632
|
const validated = yield* Schema.decodeUnknownEffect(
|
|
2398
2633
|
Schema.toType(ChildBudgetReservationRequest),
|
|
2399
2634
|
)(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
2635
|
+
|
|
2400
2636
|
const allocationJson = yield* encodePersistedJsonText(validated.allocation).pipe(
|
|
2401
2637
|
Effect.mapError(internalFailure(operation)),
|
|
2402
2638
|
);
|
|
2639
|
+
|
|
2403
2640
|
yield* hitFailpoint("ledger:child-reservation:before", operation);
|
|
2641
|
+
|
|
2404
2642
|
const reserved = yield* inWriteTransaction(
|
|
2405
2643
|
operation,
|
|
2406
2644
|
Effect.gen(function* () {
|
|
2407
2645
|
const existing = yield* readChildReservation(operation, validated.reservationId);
|
|
2646
|
+
|
|
2408
2647
|
if (Option.isSome(existing)) {
|
|
2409
2648
|
const existingSnapshot = yield* childReservationSnapshotFromRow(
|
|
2410
2649
|
operation,
|
|
2411
2650
|
existing.value,
|
|
2412
2651
|
);
|
|
2652
|
+
|
|
2413
2653
|
// Identical replays short-circuit before the fence, mirroring reserveSettlement: a
|
|
2414
2654
|
// replay creates nothing, so a recovering caller resumes rather than duplicates.
|
|
2415
2655
|
const identical =
|
|
@@ -2417,6 +2657,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2417
2657
|
existing.value.parent_tool_call_id === validated.parentToolCallId &&
|
|
2418
2658
|
existing.value.allocation_digest === validated.allocationDigest &&
|
|
2419
2659
|
equivalentPersistedJson(existingSnapshot.allocation, validated.allocation);
|
|
2660
|
+
|
|
2420
2661
|
if (!identical) {
|
|
2421
2662
|
return yield* ChildReservationConflict.make({
|
|
2422
2663
|
reservationId: validated.reservationId,
|
|
@@ -2425,16 +2666,19 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2425
2666
|
"A reservation with this identity exists with a different parent Tool Call or allocation.",
|
|
2426
2667
|
});
|
|
2427
2668
|
}
|
|
2669
|
+
|
|
2428
2670
|
return ReservedChildBudget.make({
|
|
2429
2671
|
reservation: existingSnapshot,
|
|
2430
2672
|
replayed: true,
|
|
2431
2673
|
});
|
|
2432
2674
|
}
|
|
2675
|
+
|
|
2433
2676
|
const collision = yield* readChildReservationForCall(
|
|
2434
2677
|
operation,
|
|
2435
2678
|
validated.parentSubmissionId,
|
|
2436
2679
|
validated.parentToolCallId,
|
|
2437
2680
|
);
|
|
2681
|
+
|
|
2438
2682
|
if (Option.isSome(collision)) {
|
|
2439
2683
|
return yield* ChildReservationConflict.make({
|
|
2440
2684
|
reservationId: validated.reservationId,
|
|
@@ -2443,10 +2687,12 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2443
2687
|
});
|
|
2444
2688
|
}
|
|
2445
2689
|
const parent = yield* requireSubmission(operation, validated.parentSubmissionId);
|
|
2690
|
+
|
|
2446
2691
|
// Creation is fenced by the parent lane's live ownership (spec §12 step 2): a stale
|
|
2447
2692
|
// parent Attempt can never create new reservation state.
|
|
2448
2693
|
yield* requireOwnership(operation, parent, validated.ownershipToken);
|
|
2449
2694
|
const now = yield* currentInstant;
|
|
2695
|
+
|
|
2450
2696
|
yield* sql`
|
|
2451
2697
|
INSERT INTO effect_agent_child_reservations (
|
|
2452
2698
|
reservation_id,
|
|
@@ -2467,6 +2713,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2467
2713
|
)
|
|
2468
2714
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2469
2715
|
const inserted = yield* readChildReservation(operation, validated.reservationId);
|
|
2716
|
+
|
|
2470
2717
|
if (Option.isNone(inserted)) {
|
|
2471
2718
|
return yield* corruptionFailure(
|
|
2472
2719
|
operation,
|
|
@@ -2475,13 +2722,16 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2475
2722
|
"An inserted child reservation row is missing inside its own transaction.",
|
|
2476
2723
|
);
|
|
2477
2724
|
}
|
|
2725
|
+
|
|
2478
2726
|
return ReservedChildBudget.make({
|
|
2479
2727
|
reservation: yield* childReservationSnapshotFromRow(operation, inserted.value),
|
|
2480
2728
|
replayed: false,
|
|
2481
2729
|
});
|
|
2482
2730
|
}),
|
|
2483
2731
|
);
|
|
2732
|
+
|
|
2484
2733
|
yield* hitFailpoint("ledger:child-reservation:after", operation);
|
|
2734
|
+
|
|
2485
2735
|
return reserved;
|
|
2486
2736
|
});
|
|
2487
2737
|
|
|
@@ -2490,14 +2740,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2490
2740
|
request: AttachChildToReservationRequest,
|
|
2491
2741
|
) {
|
|
2492
2742
|
const operation = "ledger attach child to reservation";
|
|
2743
|
+
|
|
2493
2744
|
const validated = yield* Schema.decodeUnknownEffect(
|
|
2494
2745
|
Schema.toType(AttachChildToReservationRequest),
|
|
2495
2746
|
)(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
2747
|
+
|
|
2496
2748
|
yield* hitFailpoint("ledger:child-attach:before", operation);
|
|
2749
|
+
|
|
2497
2750
|
const attached = yield* inWriteTransaction(
|
|
2498
2751
|
operation,
|
|
2499
2752
|
Effect.gen(function* () {
|
|
2500
2753
|
const existing = yield* readChildReservation(operation, validated.reservationId);
|
|
2754
|
+
|
|
2501
2755
|
if (Option.isNone(existing)) {
|
|
2502
2756
|
return yield* LedgerError.make({
|
|
2503
2757
|
operation,
|
|
@@ -2509,6 +2763,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2509
2763
|
if (existing.value.child_submission_id === validated.childSubmissionId) {
|
|
2510
2764
|
return yield* childReservationSnapshotFromRow(operation, existing.value);
|
|
2511
2765
|
}
|
|
2766
|
+
|
|
2512
2767
|
return yield* ChildReservationConflict.make({
|
|
2513
2768
|
reservationId: validated.reservationId,
|
|
2514
2769
|
status: existing.value.status,
|
|
@@ -2516,6 +2771,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2516
2771
|
});
|
|
2517
2772
|
}
|
|
2518
2773
|
const parent = yield* requireSubmission(operation, existing.value.parent_submission_id);
|
|
2774
|
+
|
|
2519
2775
|
yield* requireOwnership(operation, parent, validated.ownershipToken);
|
|
2520
2776
|
if (existing.value.status !== "reserved") {
|
|
2521
2777
|
return yield* ChildReservationConflict.make({
|
|
@@ -2534,6 +2790,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2534
2790
|
WHERE reservation_id = ${validated.reservationId}
|
|
2535
2791
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2536
2792
|
const updated = yield* readChildReservation(operation, validated.reservationId);
|
|
2793
|
+
|
|
2537
2794
|
if (Option.isNone(updated)) {
|
|
2538
2795
|
return yield* corruptionFailure(
|
|
2539
2796
|
operation,
|
|
@@ -2542,10 +2799,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2542
2799
|
"An updated child reservation row is missing inside its own transaction.",
|
|
2543
2800
|
);
|
|
2544
2801
|
}
|
|
2802
|
+
|
|
2545
2803
|
return yield* childReservationSnapshotFromRow(operation, updated.value);
|
|
2546
2804
|
}),
|
|
2547
2805
|
);
|
|
2806
|
+
|
|
2548
2807
|
yield* hitFailpoint("ledger:child-attach:after", operation);
|
|
2808
|
+
|
|
2549
2809
|
return attached;
|
|
2550
2810
|
});
|
|
2551
2811
|
|
|
@@ -2553,17 +2813,22 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2553
2813
|
"DoSubmissionLedger.beginChildBudgetRelease",
|
|
2554
2814
|
)(function* (request: BeginChildBudgetReleaseRequest) {
|
|
2555
2815
|
const operation = "ledger begin child budget release";
|
|
2816
|
+
|
|
2556
2817
|
const validated = yield* Schema.decodeUnknownEffect(
|
|
2557
2818
|
Schema.toType(BeginChildBudgetReleaseRequest),
|
|
2558
2819
|
)(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
2820
|
+
|
|
2559
2821
|
const accountingJson = yield* encodePersistedJsonText(validated.accounting).pipe(
|
|
2560
2822
|
Effect.mapError(internalFailure(operation)),
|
|
2561
2823
|
);
|
|
2824
|
+
|
|
2562
2825
|
yield* hitFailpoint("ledger:child-release-pending:before", operation);
|
|
2826
|
+
|
|
2563
2827
|
const frozen = yield* inWriteTransaction(
|
|
2564
2828
|
operation,
|
|
2565
2829
|
Effect.gen(function* () {
|
|
2566
2830
|
const existing = yield* readChildReservation(operation, validated.reservationId);
|
|
2831
|
+
|
|
2567
2832
|
if (Option.isNone(existing)) {
|
|
2568
2833
|
return yield* LedgerError.make({
|
|
2569
2834
|
operation,
|
|
@@ -2575,6 +2840,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2575
2840
|
operation,
|
|
2576
2841
|
existing.value,
|
|
2577
2842
|
);
|
|
2843
|
+
|
|
2578
2844
|
// The accounting decision was already frozen exactly once; an identical replay is a
|
|
2579
2845
|
// no-op and a divergent decision conflicts (spec §12 join step 6).
|
|
2580
2846
|
if (
|
|
@@ -2583,6 +2849,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2583
2849
|
) {
|
|
2584
2850
|
return existingSnapshot;
|
|
2585
2851
|
}
|
|
2852
|
+
|
|
2586
2853
|
return yield* ChildReservationConflict.make({
|
|
2587
2854
|
reservationId: validated.reservationId,
|
|
2588
2855
|
status: existing.value.status,
|
|
@@ -2590,6 +2857,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2590
2857
|
});
|
|
2591
2858
|
}
|
|
2592
2859
|
const now = yield* currentInstant;
|
|
2860
|
+
|
|
2593
2861
|
yield* sql`
|
|
2594
2862
|
UPDATE effect_agent_child_reservations
|
|
2595
2863
|
SET
|
|
@@ -2599,6 +2867,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2599
2867
|
WHERE reservation_id = ${validated.reservationId}
|
|
2600
2868
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2601
2869
|
const updated = yield* readChildReservation(operation, validated.reservationId);
|
|
2870
|
+
|
|
2602
2871
|
if (Option.isNone(updated)) {
|
|
2603
2872
|
return yield* corruptionFailure(
|
|
2604
2873
|
operation,
|
|
@@ -2607,10 +2876,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2607
2876
|
"An updated child reservation row is missing inside its own transaction.",
|
|
2608
2877
|
);
|
|
2609
2878
|
}
|
|
2879
|
+
|
|
2610
2880
|
return yield* childReservationSnapshotFromRow(operation, updated.value);
|
|
2611
2881
|
}),
|
|
2612
2882
|
);
|
|
2883
|
+
|
|
2613
2884
|
yield* hitFailpoint("ledger:child-release-pending:after", operation);
|
|
2885
|
+
|
|
2614
2886
|
return frozen;
|
|
2615
2887
|
});
|
|
2616
2888
|
|
|
@@ -2618,14 +2890,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2618
2890
|
"DoSubmissionLedger.releaseChildBudget",
|
|
2619
2891
|
)(function* (request: ReleaseChildBudgetRequest) {
|
|
2620
2892
|
const operation = "ledger release child budget";
|
|
2893
|
+
|
|
2621
2894
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ReleaseChildBudgetRequest))(
|
|
2622
2895
|
request,
|
|
2623
2896
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
2897
|
+
|
|
2624
2898
|
yield* hitFailpoint("ledger:child-release:before", operation);
|
|
2899
|
+
|
|
2625
2900
|
const released = yield* inWriteTransaction(
|
|
2626
2901
|
operation,
|
|
2627
2902
|
Effect.gen(function* () {
|
|
2628
2903
|
const existing = yield* readChildReservation(operation, validated.reservationId);
|
|
2904
|
+
|
|
2629
2905
|
if (Option.isNone(existing)) {
|
|
2630
2906
|
return yield* LedgerError.make({
|
|
2631
2907
|
operation,
|
|
@@ -2645,12 +2921,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2645
2921
|
});
|
|
2646
2922
|
}
|
|
2647
2923
|
const now = yield* currentInstant;
|
|
2924
|
+
|
|
2648
2925
|
yield* sql`
|
|
2649
2926
|
UPDATE effect_agent_child_reservations
|
|
2650
2927
|
SET status = 'released', released_at = ${now.iso}
|
|
2651
2928
|
WHERE reservation_id = ${validated.reservationId}
|
|
2652
2929
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
2653
2930
|
const updated = yield* readChildReservation(operation, validated.reservationId);
|
|
2931
|
+
|
|
2654
2932
|
if (Option.isNone(updated)) {
|
|
2655
2933
|
return yield* corruptionFailure(
|
|
2656
2934
|
operation,
|
|
@@ -2659,10 +2937,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2659
2937
|
"An updated child reservation row is missing inside its own transaction.",
|
|
2660
2938
|
);
|
|
2661
2939
|
}
|
|
2940
|
+
|
|
2662
2941
|
return yield* childReservationSnapshotFromRow(operation, updated.value);
|
|
2663
2942
|
}),
|
|
2664
2943
|
);
|
|
2944
|
+
|
|
2665
2945
|
yield* hitFailpoint("ledger:child-release:after", operation);
|
|
2946
|
+
|
|
2666
2947
|
return released;
|
|
2667
2948
|
});
|
|
2668
2949
|
|
|
@@ -2678,6 +2959,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2678
2959
|
LedgerError
|
|
2679
2960
|
> {
|
|
2680
2961
|
const operation = "ledger scan nonterminal";
|
|
2962
|
+
|
|
2681
2963
|
const rows = yield* (
|
|
2682
2964
|
cursor === undefined
|
|
2683
2965
|
? sql<Record<string, unknown>>`
|
|
@@ -2702,11 +2984,15 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2702
2984
|
LIMIT ${SCAN_PAGE_SIZE}
|
|
2703
2985
|
`
|
|
2704
2986
|
).pipe(Effect.mapError(sqlFailure(operation)));
|
|
2987
|
+
|
|
2705
2988
|
const decoded = yield* decodeSubmissionRows(operation, "nonterminal_scan", rows);
|
|
2989
|
+
|
|
2706
2990
|
const snapshots = yield* Effect.forEach(decoded, (row) =>
|
|
2707
2991
|
decodeSubmissionSnapshot(operation, row),
|
|
2708
2992
|
);
|
|
2993
|
+
|
|
2709
2994
|
const last = decoded[decoded.length - 1];
|
|
2995
|
+
|
|
2710
2996
|
const next: Option.Option<ScanCursor | undefined> =
|
|
2711
2997
|
last === undefined || decoded.length < SCAN_PAGE_SIZE
|
|
2712
2998
|
? Option.none()
|
|
@@ -2714,6 +3000,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2714
3000
|
threadId: last.thread_id,
|
|
2715
3001
|
queueSequence: last.queue_sequence,
|
|
2716
3002
|
});
|
|
3003
|
+
|
|
2717
3004
|
return [snapshots, next] as const;
|
|
2718
3005
|
});
|
|
2719
3006
|
|
|
@@ -2727,9 +3014,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2727
3014
|
"DoSubmissionLedger.loadRecoverySnapshot",
|
|
2728
3015
|
)(function* (request: RecoverySnapshotRequest) {
|
|
2729
3016
|
const operation = "ledger load recovery snapshot";
|
|
3017
|
+
|
|
2730
3018
|
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(RecoverySnapshotRequest))(
|
|
2731
3019
|
request,
|
|
2732
3020
|
).pipe(Effect.mapError(internalFailure(operation)));
|
|
3021
|
+
|
|
2733
3022
|
return yield* sql
|
|
2734
3023
|
.withTransaction(
|
|
2735
3024
|
Effect.gen(function* () {
|
|
@@ -2738,6 +3027,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2738
3027
|
|
|
2739
3028
|
let ownership: OwnershipSnapshot | undefined;
|
|
2740
3029
|
const ownershipRow = yield* readOwnership(operation, validated.submissionId);
|
|
3030
|
+
|
|
2741
3031
|
if (Option.isSome(ownershipRow)) {
|
|
2742
3032
|
ownership = yield* decodeOwnershipSnapshot({
|
|
2743
3033
|
attemptId: ownershipRow.value.attempt_id,
|
|
@@ -2748,6 +3038,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2748
3038
|
}
|
|
2749
3039
|
|
|
2750
3040
|
let inputApplied: InputAppliedMarker | undefined;
|
|
3041
|
+
|
|
2751
3042
|
if (
|
|
2752
3043
|
submissionRow.input_applied_record_id !== null &&
|
|
2753
3044
|
submissionRow.input_applied_sequence !== null
|
|
@@ -2760,6 +3051,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2760
3051
|
|
|
2761
3052
|
let reservation: SettlementReservationSnapshot | undefined;
|
|
2762
3053
|
const reservationRow = yield* readReservation(operation, validated.submissionId);
|
|
3054
|
+
|
|
2763
3055
|
if (Option.isSome(reservationRow)) {
|
|
2764
3056
|
const record = yield* decodeRecordEnvelopeText(reservationRow.value.record_json).pipe(
|
|
2765
3057
|
Effect.mapError((error) =>
|
|
@@ -2771,9 +3063,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2771
3063
|
),
|
|
2772
3064
|
),
|
|
2773
3065
|
);
|
|
3066
|
+
|
|
2774
3067
|
const settlementId = yield* Schema.decodeUnknownEffect(
|
|
2775
3068
|
SettlementReservationSnapshot.fields.settlementId,
|
|
2776
3069
|
)(reservationRow.value.settlement_id).pipe(Effect.mapError(internalFailure(operation)));
|
|
3070
|
+
|
|
2777
3071
|
reservation = SettlementReservationSnapshot.make({
|
|
2778
3072
|
settlementId,
|
|
2779
3073
|
outcome: reservationRow.value.outcome,
|
|
@@ -2785,6 +3079,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2785
3079
|
|
|
2786
3080
|
let abortIntent: AbortIntent | undefined;
|
|
2787
3081
|
const abortRow = yield* readAbortIntent(operation, validated.submissionId);
|
|
3082
|
+
|
|
2788
3083
|
if (Option.isSome(abortRow)) {
|
|
2789
3084
|
abortIntent = yield* abortIntentFromRow(
|
|
2790
3085
|
operation,
|
|
@@ -2802,11 +3097,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2802
3097
|
WHERE joined_host_submission_id = ${validated.submissionId}
|
|
2803
3098
|
ORDER BY queue_sequence ASC
|
|
2804
3099
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
3100
|
+
|
|
2805
3101
|
const joinSubmissions = yield* decodeSubmissionRows(
|
|
2806
3102
|
operation,
|
|
2807
3103
|
validated.submissionId,
|
|
2808
3104
|
joinRows,
|
|
2809
3105
|
);
|
|
3106
|
+
|
|
2810
3107
|
const joins = yield* Effect.forEach(joinSubmissions, (row) =>
|
|
2811
3108
|
decodeJoinSnapshot({
|
|
2812
3109
|
submissionId: row.submission_id,
|
|
@@ -2816,6 +3113,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2816
3113
|
);
|
|
2817
3114
|
|
|
2818
3115
|
let hostSubmissionId: RecoverySnapshot["hostSubmissionId"];
|
|
3116
|
+
|
|
2819
3117
|
if (submissionRow.joined_host_submission_id !== null) {
|
|
2820
3118
|
hostSubmissionId = yield* decodeSubmissionId(
|
|
2821
3119
|
submissionRow.joined_host_submission_id,
|
|
@@ -2823,6 +3121,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2823
3121
|
}
|
|
2824
3122
|
|
|
2825
3123
|
let suspension: SuspensionSnapshot | undefined;
|
|
3124
|
+
|
|
2826
3125
|
if (submissionRow.suspended_reason_json !== null && submissionRow.suspended_at !== null) {
|
|
2827
3126
|
const reason = yield* parseStoredJsonText(submissionRow.suspended_reason_json).pipe(
|
|
2828
3127
|
Effect.mapError((error) =>
|
|
@@ -2834,6 +3133,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2834
3133
|
),
|
|
2835
3134
|
),
|
|
2836
3135
|
);
|
|
3136
|
+
|
|
2837
3137
|
suspension = yield* decodeSuspensionSnapshot({
|
|
2838
3138
|
reason,
|
|
2839
3139
|
suspendedAt: submissionRow.suspended_at,
|
|
@@ -2850,11 +3150,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2850
3150
|
}
|
|
2851
3151
|
|
|
2852
3152
|
const decisionRows = yield* readApprovalDecisions(operation, validated.submissionId);
|
|
3153
|
+
|
|
2853
3154
|
const approvalDecisions = yield* Effect.forEach(decisionRows, (row) =>
|
|
2854
3155
|
approvalIntentFromRow(operation, row),
|
|
2855
3156
|
);
|
|
2856
3157
|
|
|
2857
3158
|
const resolutionRows = yield* readUnknownResolutions(operation, validated.submissionId);
|
|
3159
|
+
|
|
2858
3160
|
const unknownResolutions = yield* Effect.forEach(resolutionRows, (row) =>
|
|
2859
3161
|
unknownResolutionIntentFromRow(operation, row),
|
|
2860
3162
|
);
|
|
@@ -2871,20 +3173,25 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2871
3173
|
WHERE parent_submission_id = ${validated.submissionId}
|
|
2872
3174
|
ORDER BY parent_tool_call_id ASC
|
|
2873
3175
|
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
3176
|
+
|
|
2874
3177
|
const decodedChildReservations = yield* decodeChildReservationRows(
|
|
2875
3178
|
operation,
|
|
2876
3179
|
validated.submissionId,
|
|
2877
3180
|
childReservationRows,
|
|
2878
3181
|
);
|
|
3182
|
+
|
|
2879
3183
|
const childReservations = yield* Effect.forEach(decodedChildReservations, (row) =>
|
|
2880
3184
|
childReservationSnapshotFromRow(operation, row),
|
|
2881
3185
|
);
|
|
3186
|
+
|
|
2882
3187
|
const markers = yield* readChildSettlementMarkers(operation, validated.submissionId);
|
|
2883
3188
|
const markersByChild = new Map(markers.map((row) => [row.child_submission_id, row]));
|
|
2884
3189
|
const childAttachments: Array<ChildAttachmentSnapshot> = [];
|
|
3190
|
+
|
|
2885
3191
|
for (const row of decodedChildReservations) {
|
|
2886
3192
|
if (row.child_submission_id === null) continue;
|
|
2887
3193
|
const child = yield* readSubmission(operation, row.child_submission_id);
|
|
3194
|
+
|
|
2888
3195
|
if (Option.isSome(child)) {
|
|
2889
3196
|
childAttachments.push(
|
|
2890
3197
|
yield* decodeChildAttachmentSnapshot({
|
|
@@ -2899,6 +3206,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2899
3206
|
continue;
|
|
2900
3207
|
}
|
|
2901
3208
|
const marker = markersByChild.get(row.child_submission_id);
|
|
3209
|
+
|
|
2902
3210
|
if (marker === undefined) continue;
|
|
2903
3211
|
childAttachments.push(
|
|
2904
3212
|
yield* decodeChildAttachmentSnapshot({
|
|
@@ -2911,6 +3219,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
2911
3219
|
}
|
|
2912
3220
|
|
|
2913
3221
|
let parentLinkage: ParentLinkage | undefined;
|
|
3222
|
+
|
|
2914
3223
|
if (
|
|
2915
3224
|
submissionRow.parent_submission_id !== null &&
|
|
2916
3225
|
submissionRow.parent_tool_call_id !== null
|