@yielded/agent-storage-cloudflare 0.1.0-beta.174 → 0.1.0-beta.176

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.
Files changed (41) hide show
  1. package/dist/DoMemoryStore.mjs +1 -1
  2. package/dist/DoMessageDeliveryStore.mjs +1 -1
  3. package/dist/DoStorageFailpoint.d.mts +3 -1
  4. package/dist/DoStorageFailpoint.mjs +7 -2
  5. package/dist/DoStorageFailpoint.mjs.map +1 -1
  6. package/dist/{DoStorageVersion-NKnQU3eR.d.mts → DoStorageVersion-Mc7CXMdN.d.mts} +2 -2
  7. package/dist/DoStorageVersion.d.mts +1 -1
  8. package/dist/DoStorageVersion.mjs +2 -2
  9. package/dist/DoSubmissionLedger.d.mts +1 -1
  10. package/dist/DoSubmissionLedger.mjs +194 -98
  11. package/dist/DoSubmissionLedger.mjs.map +1 -1
  12. package/dist/{DoThreadStore-Bd4fTns3.d.mts → DoThreadStore-BzYJjm4O.d.mts} +18 -8
  13. package/dist/{DoThreadStore-DyzOZDXK.mjs → DoThreadStore-DG0h5J6u.mjs} +25 -19
  14. package/dist/DoThreadStore-DG0h5J6u.mjs.map +1 -0
  15. package/dist/DoThreadStore.d.mts +2 -2
  16. package/dist/DoThreadStore.mjs +3 -2
  17. package/dist/PortProtocol.d.mts +7201 -7201
  18. package/dist/PortRouting.mjs +21 -11
  19. package/dist/PortRouting.mjs.map +1 -1
  20. package/dist/{do-journal-ZMAzzENZ.mjs → do-journal-ySC3eXNv.mjs} +144 -45
  21. package/dist/do-journal-ySC3eXNv.mjs.map +1 -0
  22. package/dist/index.d.mts +2 -2
  23. package/dist/index.mjs +1 -1
  24. package/dist/{migrations-C3N9GN6A.mjs → migrations-CAO_akhD.mjs} +45 -22
  25. package/dist/migrations-CAO_akhD.mjs.map +1 -0
  26. package/dist/{owned-state-DcFh0YNo.mjs → owned-state-BgGOA2Ak.mjs} +4 -3
  27. package/dist/owned-state-BgGOA2Ak.mjs.map +1 -0
  28. package/package.json +1 -1
  29. package/src/DoStorageFailpoint.ts +4 -0
  30. package/src/DoSubmissionLedger.ts +430 -228
  31. package/src/DoThreadStore.ts +25 -44
  32. package/src/PortRouting.ts +57 -31
  33. package/src/internal/do-journal.ts +318 -70
  34. package/src/internal/migrations.ts +52 -10
  35. package/src/internal/owned-state.ts +12 -5
  36. package/src/internal/prompt-checkpoint.ts +6 -3
  37. package/src/internal/storage-span.ts +14 -10
  38. package/dist/DoThreadStore-DyzOZDXK.mjs.map +0 -1
  39. package/dist/do-journal-ZMAzzENZ.mjs.map +0 -1
  40. package/dist/migrations-C3N9GN6A.mjs.map +0 -1
  41. package/dist/owned-state-DcFh0YNo.mjs.map +0 -1
@@ -2,24 +2,24 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { DoStorageConfig } from "./DoStorageConfig.mjs";
3
3
  import { DoStorageFailpoint } from "./DoStorageFailpoint.mjs";
4
4
  import { DoLedgerError, DoStorageCorruptionError, DoStorageError } from "./DoStorageError.mjs";
5
- import { n as ownedRows } from "./owned-state-DcFh0YNo.mjs";
6
- import { i as initializeDoJournal, r as decodeRows } from "./do-journal-ZMAzzENZ.mjs";
7
- import { l as prepareCanonicalAppend, o as storageConfigLayer, s as storageFailpointLayer } from "./DoThreadStore-DyzOZDXK.mjs";
5
+ import { n as ownedRows } from "./owned-state-BgGOA2Ak.mjs";
6
+ import { i as initializeDoJournal, r as decodeRows } from "./do-journal-ySC3eXNv.mjs";
7
+ import { l as prepareCanonicalAppend, o as storageConfigLayer, s as storageFailpointLayer } from "./DoThreadStore-DG0h5J6u.mjs";
8
8
  import { BrowserCrypto } from "@effect/platform-browser";
9
9
  import { Cause, Clock, Context, Crypto, DateTime, Effect, Layer, Option, Schema, Stream } from "effect";
10
10
  import * as SqlClientService from "effect/sql/SqlClient";
11
+ import { SqliteClient } from "@effect/sql-sqlite-do";
11
12
  import { definitionJson, makeCanonicalStorage, recordBatchId, runKey } from "@yielded/agent-storage-sql/sql-canonical-storage";
12
13
  import { SqlStorageProgress } from "@yielded/agent-storage-sql/sql-storage-progress";
13
14
  import { canonicalRecordJson } from "@yielded/agent-storage-sql/sql-thread-archive-range";
14
15
  import { ApprovalDecision, CanonicalSequence, DefinitionDigests, Digest, PersistedJson, RecordEnvelope, RecordId, SettlementOutcome, WorkerAdmission } from "@yielded/agent/records";
15
16
  import { AppendConflict, ThreadNotMaterialized, ThreadStoreDiagnostic, ThreadStoreError, makeTransferFactCheck } from "@yielded/agent/thread-store";
16
- import { SqliteClient } from "@effect/sql-sqlite-do";
17
17
  import { StorageOwner } from "@yielded/agent/run-storage";
18
18
  import { decodeAbortFact, decodeAdmissionFact, decodeApprovalFact, decodeResolutionFact } from "@yielded/agent-storage-sql/sql-admission-facts";
19
19
  import { InputMessage } from "@yielded/agent/messaging";
20
20
  import { runIdForSubmission } from "@yielded/agent/run-journal";
21
21
  import { SettlementPublicationResult, SettlementPublisher, validateCanonicalSettlement, validateJoinedSettlement, validatePublication } from "@yielded/agent/settlement-publisher";
22
- import { AbortCommand, AbortIntent, AbortIntentRequest, AdmissionAdmitted, AdmissionConflict, AdmissionFence, AdmissionGroup, AdmissionNotAdmitted, AdmissionPolicyError, AdmissionRequest, AdmissionResult, ApprovalConflict, ApprovalDecisionCommand, ApprovalDecisionIntent, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, ChildAttachmentSnapshot, ChildBudgetReservationRequest, ChildBudgetReservationSnapshot, ChildReservationConflict, ChildReservationStatus, ChildSettledNotification, Claim, ClaimJoiningRequest, ClaimRequest, FundingOwner, FundingOwnerRequest, InputAppliedMarker, JoinSnapshot, JoinedToHost, JoiningClaim, LedgerCapabilities, LedgerError, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, OwnershipLost, ParentLinkage, QueueSequence, RecoverySnapshot, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReservedChildBudget, RevertJoiningRequest, Settlement, SettlementConflict, SettlementFinalization, SubmissionAdmissionFence, SubmissionLedger, SubmissionLookup, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SubmissionWorkItem, SuspendRequest, SuspensionReason, SuspensionSnapshot, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, UnknownResolutionKind, WorkerLedgerState, WorkerStopCommand, approvalDecisionsCover, settlementFailureFromRecord, submissionAbortRecordId, submissionSettlementBatchId, submissionSettlementRecordId, unknownResolutionKind, unknownResolutionMatchesResult, workerTerminalFromRecord } from "@yielded/agent/submission-ledger";
22
+ import { AbortCommand, AbortIntent, AbortIntentRequest, AdmissionAdmitted, AdmissionConflict, AdmissionFence, AdmissionGroup, AdmissionNotAdmitted, AdmissionPolicyError, AdmissionRequest, AdmissionResult, ApprovalConflict, ApprovalDecisionCommand, ApprovalDecisionIntent, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, ChildAttachmentSnapshot, ChildBudgetReservationRequest, ChildBudgetReservationSnapshot, ChildReservationConflict, ChildReservationStatus, ChildSettledNotification, Claim, ClaimJoiningRequest, ClaimRequest, FundingOwner, FundingOwnerRequest, InputAppliedMarker, JoinSnapshot, JoinedToHost, JoiningClaim, LedgerCapabilities, LedgerError, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, OwnershipLost, ParentLinkage, QueueSequence, RecoverySnapshot, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReservedChildBudget, RevertJoiningRequest, Settlement, SettlementConflict, SettlementFinalization, SubmissionAdmissionFence, SubmissionLedger, SubmissionLookup, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SubmissionWorkItem, SuspendRequest, SuspensionReason, SuspensionSnapshot, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, UnknownResolutionKind, WorkerLedgerState, WorkerStopCommand, approvalDecisionsCover, settlementFailureFromRecord, submissionAbortRecordId, submissionInputBatchId, submissionInputRecordId, submissionSettlementBatchId, submissionSettlementRecordId, unknownResolutionKind, unknownResolutionMatchesResult, workerTerminalFromRecord } from "@yielded/agent/submission-ledger";
23
23
  import { AssignmentTerminal } from "@yielded/agent/worker";
24
24
  //#region src/DoSubmissionLedger.ts
25
25
  var DoSubmissionLedger_exports = /* @__PURE__ */ __exportAll({
@@ -191,7 +191,7 @@ const decodeClaim = Schema.decodeUnknownEffect(Claim);
191
191
  const decodeSettlement = Schema.decodeUnknownEffect(Settlement);
192
192
  const decodeAbortIntent = Schema.decodeUnknownEffect(AbortIntent);
193
193
  const decodeInputAppliedMarker = Schema.decodeUnknownEffect(InputAppliedMarker);
194
- const decodeSubmissionSnapshotType = (input) => Schema.decodeEffect(Schema.toType(Schema.Struct(SubmissionSnapshot.fields)))(input).pipe(Effect.map((fields) => SubmissionSnapshot.make(fields)));
194
+ const decodeSubmissionSnapshotType = (input) => SubmissionSnapshot.makeEffect(input).pipe(Effect.mapError((issue) => new Schema.SchemaError(issue)));
195
195
  const decodeSubmissionId = Schema.decodeUnknownEffect(SubmissionSnapshot.fields.submissionId);
196
196
  const decodeQueueSequence = Schema.decodeUnknownEffect(QueueSequence);
197
197
  const decodeUtcInstant = Schema.decodeUnknownEffect(Schema.DateTimeUtcFromString);
@@ -205,6 +205,26 @@ const decodeChildReservationSnapshotUnknown = Schema.decodeUnknownEffect(ChildBu
205
205
  const decodeChildAttachmentSnapshot = Schema.decodeUnknownEffect(ChildAttachmentSnapshot);
206
206
  const equivalentPersistedJson = Schema.toEquivalence(PersistedJson);
207
207
  const equivalentUnknownResolution = Schema.toEquivalence(UnknownResolution);
208
+ const decodeAdmissionRequestType = Schema.decodeEffect(Schema.toType(AdmissionRequest));
209
+ const decodeSubmissionLookupType = Schema.decodeEffect(Schema.toType(SubmissionLookup));
210
+ const decodeClaimRequestType = Schema.decodeEffect(Schema.toType(ClaimRequest));
211
+ const decodeMarkInputAppliedRequestType = Schema.decodeEffect(Schema.toType(MarkInputAppliedRequest));
212
+ const decodeSettlementFinalizationType = Schema.decodeEffect(Schema.toType(SettlementFinalization));
213
+ const decodeClaimJoiningRequestType = Schema.decodeEffect(Schema.toType(ClaimJoiningRequest));
214
+ const decodeAbortIntentRequestType = Schema.decodeEffect(Schema.toType(AbortIntentRequest));
215
+ const decodeRecoverySnapshotRequestType = Schema.decodeEffect(Schema.toType(RecoverySnapshotRequest));
216
+ const SubmissionRows = Schema.Array(SubmissionRow);
217
+ const ChildReservationRows = Schema.Array(ChildReservationRow);
218
+ const MaxQueueSequenceRows = Schema.Array(MaxQueueSequenceRow);
219
+ const SubmissionWorkItems = Schema.Array(SubmissionWorkItem);
220
+ const decodeSubmissionWorkItems = Schema.decodeEffect(SubmissionWorkItems);
221
+ const equivalentWorkerOrigin = Schema.toEquivalence(Schema.optional(WorkerAdmission.fields.origin));
222
+ const decodeFirstWorkerAdmissions = Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ worker_admission_json: Schema.NullOr(BoundedStoredText) })));
223
+ const CanonicalSettlementRows = Schema.Array(Schema.Struct({
224
+ batch_id: Schema.fromJsonString(BoundedIdentifier),
225
+ record_id: Schema.fromJsonString(BoundedIdentifier),
226
+ record_json: BoundedStoredText
227
+ }));
208
228
  const isDoStorageError = Schema.is(DoStorageError);
209
229
  /** Wrap an adapter-internal failure into the port's LedgerError without erasing its tag. */
210
230
  const internalFailure = (operation) => (error) => LedgerError.make({
@@ -319,7 +339,7 @@ const makeServices = Effect.fnUntraced(function* () {
319
339
  millis,
320
340
  iso: new Date(millis).toISOString()
321
341
  }));
322
- const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows(Schema.Array(SubmissionRow), "effect_agent_submissions", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
342
+ const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows(SubmissionRows, "effect_agent_submissions", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
323
343
  const readSubmission = Effect.fnUntraced(function* (operation, submissionId) {
324
344
  const found = yield* cached(rows.submissions.by("submission_id", submissionId), operation);
325
345
  return Option.fromUndefinedOr(found[0]);
@@ -373,11 +393,7 @@ const makeServices = Effect.fnUntraced(function* () {
373
393
  const found = yield* sql`SELECT ${recordBatchId(sql)} AS batch_id, record_id, ${canonicalRecordJson(sql)} AS record_json
374
394
  FROM effect_agent_canonical_records WHERE thread_id = ${submission.thread_id}
375
395
  AND record_id = ${JSON.stringify(recordId)}`.pipe(Effect.mapError(sqlFailure(operation)));
376
- const decoded = yield* decodeRows(Schema.Array(Schema.Struct({
377
- batch_id: Schema.fromJsonString(BoundedIdentifier),
378
- record_id: Schema.fromJsonString(BoundedIdentifier),
379
- record_json: BoundedStoredText
380
- })), "effect_agent_canonical_records", recordId, found).pipe(Effect.mapError(internalFailure(operation)));
396
+ const decoded = yield* decodeRows(CanonicalSettlementRows, "effect_agent_canonical_records", recordId, found).pipe(Effect.mapError(internalFailure(operation)));
381
397
  if (decoded.length === 0) return void 0;
382
398
  const row = decoded[0];
383
399
  if (decoded.length !== 1 || row.batch_id !== submissionSettlementBatchId(expected.submissionId) || row.record_id !== recordId) return yield* corruptionFailure(operation, "effect_agent_canonical_records", recordId, "Canonical settlement identity is inconsistent.");
@@ -410,7 +426,7 @@ const makeServices = Effect.fnUntraced(function* () {
410
426
  const childRow = yield* readSubmission(operation, childSubmissionId);
411
427
  return Option.isSome(childRow) && (yield* readCanonicalSettlement(operation, childRow.value)) !== void 0;
412
428
  });
413
- const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows(Schema.Array(ChildReservationRow), "effect_agent_child_reservations", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
429
+ const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows(ChildReservationRows, "effect_agent_child_reservations", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
414
430
  const readChildReservation = Effect.fnUntraced(function* (operation, reservationId) {
415
431
  const rows = yield* sql`
416
432
  SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
@@ -483,9 +499,9 @@ const makeServices = Effect.fnUntraced(function* () {
483
499
  });
484
500
  });
485
501
  const capabilities = Effect.succeed(LedgerCapabilities.make({ durability: "durable-cloudflare" }));
486
- const admit = Effect.fnUntraced(function* (request) {
502
+ const admit = Effect.fnUntraced(function* (request, options) {
487
503
  const operation = "ledger admit";
488
- const validated = yield* Schema.decodeEffect(Schema.toType(AdmissionRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
504
+ const validated = yield* decodeAdmissionRequestType(request).pipe(Effect.mapError(internalFailure(operation)));
489
505
  const inputJson = yield* encodePersistedJsonText(validated.inputPayload).pipe(Effect.mapError(internalFailure(operation)));
490
506
  yield* journal.checkValueBound(operation, inputJson).pipe(Effect.mapError(internalFailure(operation)));
491
507
  const workerAdmissionJson = validated.workerAdmission === void 0 ? null : yield* Schema.encodeEffect(Schema.fromJsonString(WorkerAdmission))(validated.workerAdmission).pipe(Effect.mapError(internalFailure(operation)));
@@ -508,8 +524,7 @@ const makeServices = Effect.fnUntraced(function* () {
508
524
  yield* hitFailpoint("ledger:admit:before", operation);
509
525
  const result = yield* inWriteTransaction(operation, Effect.gen(function* () {
510
526
  const keyRowKey = `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`;
511
- const existingRows = yield* byKey(validated, operation);
512
- const existing = yield* decodeSubmissionRows(operation, keyRowKey, existingRows);
527
+ const existing = yield* byKey(validated, operation);
513
528
  if (existing.length > 1) return yield* corruptionFailure(operation, "effect_agent_submissions", keyRowKey, "An admission idempotency key returned more than one row.");
514
529
  if (existing.length === 1) {
515
530
  const sameLinkage = validated.parentLinkage === void 0 ? existing[0].parent_submission_id === null && existing[0].parent_tool_call_id === null : existing[0].parent_submission_id === validated.parentLinkage.parentSubmissionId && existing[0].parent_tool_call_id === validated.parentLinkage.parentToolCallId;
@@ -550,10 +565,10 @@ const makeServices = Effect.fnUntraced(function* () {
550
565
  SELECT worker_admission_json FROM effect_agent_submissions
551
566
  WHERE thread_id=${validated.threadId} ORDER BY queue_sequence LIMIT 1
552
567
  `.pipe(Effect.mapError(sqlFailure(operation)));
553
- const first = yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ worker_admission_json: Schema.NullOr(BoundedStoredText) })))(firstRows).pipe(Effect.mapError(internalFailure(operation)));
568
+ const first = yield* decodeFirstWorkerAdmissions(firstRows).pipe(Effect.mapError(internalFailure(operation)));
554
569
  if (first[0] !== void 0) {
555
570
  const previous = first[0].worker_admission_json === null ? void 0 : yield* Schema.decodeEffect(Schema.fromJsonString(WorkerAdmission))(first[0].worker_admission_json).pipe(Effect.mapError(internalFailure(operation)));
556
- if (!Schema.toEquivalence(Schema.optional(WorkerAdmission.fields.origin))(previous?.origin, validated.workerAdmission?.origin)) return yield* AdmissionPolicyError.make({
571
+ if (!equivalentWorkerOrigin(previous?.origin, validated.workerAdmission?.origin)) return yield* AdmissionPolicyError.make({
557
572
  reason: "refused",
558
573
  code: "worker-origin-conflict"
559
574
  });
@@ -573,9 +588,10 @@ const makeServices = Effect.fnUntraced(function* () {
573
588
  FROM effect_agent_submissions
574
589
  WHERE thread_id = ${validated.threadId}
575
590
  `.pipe(Effect.mapError(sqlFailure(operation)));
576
- const decodedMax = yield* decodeRows(Schema.Array(MaxQueueSequenceRow), "effect_agent_submissions", validated.threadId, maxRows).pipe(Effect.mapError(internalFailure(operation)));
591
+ const decodedMax = yield* decodeRows(MaxQueueSequenceRows, "effect_agent_submissions", validated.threadId, maxRows).pipe(Effect.mapError(internalFailure(operation)));
577
592
  const queueSequence = yield* decodeQueueSequence((decodedMax[0]?.max_queue_sequence ?? 0) + 1).pipe(Effect.mapError(internalFailure(operation)));
578
593
  const now = yield* currentInstant;
594
+ const ready = options?.readyIfInitialized === true && validated.workerAdmission === void 0 && (yield* journal.getThread(validated.threadId).pipe(Effect.mapError(internalFailure(operation)))).some((thread) => thread.tail_sequence > 0);
579
595
  yield* sql`
580
596
  INSERT INTO effect_agent_submissions (
581
597
  submission_id,
@@ -591,6 +607,7 @@ const makeServices = Effect.fnUntraced(function* () {
591
607
  receipt_id,
592
608
  state,
593
609
  created_at,
610
+ ready_at,
594
611
  parent_submission_id,
595
612
  parent_tool_call_id,
596
613
  admission_group,
@@ -609,8 +626,9 @@ const makeServices = Effect.fnUntraced(function* () {
609
626
  ${inputJson},
610
627
  ${validated.inputDigest},
611
628
  ${mintedReceiptId},
612
- 'admitted',
629
+ ${ready ? "ready" : "admitted"},
613
630
  ${now.iso},
631
+ ${ready ? now.iso : null},
614
632
  ${validated.parentLinkage?.parentSubmissionId ?? null},
615
633
  ${validated.parentLinkage?.parentToolCallId ?? null},
616
634
  ${validated.admissionGroup ?? null},
@@ -622,6 +640,11 @@ const makeServices = Effect.fnUntraced(function* () {
622
640
  if (queueSequence === 1) {
623
641
  if ((yield* journal.getThread(validated.threadId).pipe(Effect.mapError(internalFailure(operation)))).length === 0) yield* journal.work.initialize(validated.threadId).pipe(Effect.mapError(internalFailure(operation)));
624
642
  }
643
+ submissionViews.seedMatching(JSON.stringify([
644
+ "joining-suffix",
645
+ validated.threadId,
646
+ queueSequence
647
+ ]), (row) => row.thread_id === validated.threadId && row.queue_sequence > queueSequence, [], 0);
625
648
  rows.aborts.seed("submission_id", mintedSubmissionId, []);
626
649
  rows.submissions.seed("joined_host_submission_id", mintedSubmissionId, []);
627
650
  rows.approvals.seed("submission_id", mintedSubmissionId, []);
@@ -629,13 +652,18 @@ const makeServices = Effect.fnUntraced(function* () {
629
652
  rows.childReservations.seed("parent_submission_id", mintedSubmissionId, []);
630
653
  rows.childSettlements.seed("parent_submission_id", mintedSubmissionId, []);
631
654
  yield* recordProgress("submission", operation);
632
- return yield* decodeAdmissionResult({
655
+ const admitted = yield* decodeAdmissionResult({
633
656
  submissionId: mintedSubmissionId,
634
657
  receiptId: mintedReceiptId,
635
658
  queueSequence,
636
- state: "admitted",
659
+ state: ready ? "ready" : "admitted",
637
660
  replayed: false
638
661
  }).pipe(Effect.mapError(internalFailure(operation)));
662
+ if (ready && lifecycle !== void 0) yield* retainLifecycle(yield* requireSubmission(operation, mintedSubmissionId), {
663
+ _tag: "SubmissionReady",
664
+ submissionId: admitted.submissionId
665
+ });
666
+ return admitted;
639
667
  }));
640
668
  yield* hitFailpoint("ledger:admit:after", operation);
641
669
  return result;
@@ -685,14 +713,13 @@ const makeServices = Effect.fnUntraced(function* () {
685
713
  }, state.read);
686
714
  const lookup = Effect.fnUntraced(function* (request) {
687
715
  const operation = "ledger lookup";
688
- const validated = yield* Schema.decodeEffect(Schema.toType(SubmissionLookup))(request).pipe(Effect.mapError(internalFailure(operation)));
716
+ const validated = yield* decodeSubmissionLookupType(request).pipe(Effect.mapError(internalFailure(operation)));
689
717
  if (validated._tag === "SubmissionLookupById") {
690
718
  const row = yield* readSubmission(operation, validated.submissionId);
691
719
  if (Option.isNone(row)) return Option.none();
692
720
  return Option.some(yield* decodeSubmissionSnapshot(operation, row.value));
693
721
  }
694
- const foundRows = yield* byKey(validated, operation);
695
- const decoded = yield* decodeSubmissionRows(operation, `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`, foundRows);
722
+ const decoded = yield* byKey(validated, operation);
696
723
  if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_submissions", `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`, "An admission idempotency key returned more than one row.");
697
724
  if (decoded.length === 0) return Option.none();
698
725
  return Option.some(yield* decodeSubmissionSnapshot(operation, decoded[0]));
@@ -700,20 +727,30 @@ const makeServices = Effect.fnUntraced(function* () {
700
727
  const resolveAdmission = Effect.fnUntraced(function* (request) {
701
728
  const operation = "ledger resolve admission";
702
729
  const validated = yield* Schema.decodeEffect(Schema.toType(SubmissionLookupByKey))(request).pipe(Effect.mapError(internalFailure(operation)));
703
- const foundRows = yield* byKey(validated, operation);
704
- const decoded = yield* decodeSubmissionRows(operation, `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`, foundRows);
730
+ const decoded = yield* byKey(validated, operation);
705
731
  if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_submissions", `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`, "An admission idempotency key returned more than one row.");
706
732
  if (decoded.length === 0) return AdmissionNotAdmitted.make();
707
733
  return AdmissionAdmitted.make({ submission: yield* decodeSubmissionSnapshot(operation, decoded[0]) });
708
734
  });
709
735
  const selectHead = Effect.fn("DoSubmissionLedger.selectHead")(function* (request) {
710
736
  const operation = "ledger select head";
711
- const validated = yield* Schema.decodeEffect(Schema.toType(ClaimRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
737
+ const validated = yield* decodeClaimRequestType(request).pipe(Effect.mapError(internalFailure(operation)));
712
738
  const attemptId = `attempt-${yield* mintUuid(operation)}`;
713
739
  yield* hitFailpoint("ledger:claim:before", operation);
714
740
  const claimed = yield* owner.withMutation(Effect.gen(function* () {
715
741
  if (owner.hasOwner(validated.threadId)) return Option.none();
716
- const headRows = yield* sql`
742
+ const active = yield* activeNonterminal;
743
+ const heads = [];
744
+ const limit = validated.handoff === void 0 ? 1 : validated.handoff.deferredSubmissionIds.length + 1;
745
+ if (active.length <= 128) {
746
+ const candidates = active.filter((row) => row.thread_id === validated.threadId).sort((left, right) => left.queue_sequence - right.queue_sequence);
747
+ for (const candidate of candidates) {
748
+ if (candidate.state === "unknown" && Option.isNone(yield* readAbortIntent(operation, candidate.submission_id))) continue;
749
+ heads.push(yield* requireSubmission(operation, candidate.submission_id));
750
+ if (heads.length === limit) break;
751
+ }
752
+ } else {
753
+ const headRows = yield* sql`
717
754
  SELECT ${submissionColumns}
718
755
  FROM effect_agent_submissions
719
756
  WHERE thread_id = ${validated.threadId}
@@ -727,7 +764,8 @@ const makeServices = Effect.fnUntraced(function* () {
727
764
  ORDER BY queue_sequence ASC
728
765
  LIMIT ${validated.handoff === void 0 ? 1 : validated.handoff.deferredSubmissionIds.length + 1}
729
766
  `.pipe(Effect.mapError(sqlFailure(operation)));
730
- const heads = yield* decodeSubmissionRows(operation, validated.threadId, headRows);
767
+ heads.push(...yield* decodeSubmissionRows(operation, validated.threadId, headRows));
768
+ }
731
769
  if (heads.length === 0) return Option.none();
732
770
  let head = heads[0];
733
771
  if (validated.handoff !== void 0) {
@@ -752,31 +790,76 @@ const makeServices = Effect.fnUntraced(function* () {
752
790
  yield* hitFailpoint("ledger:claim:after", operation);
753
791
  return claimed;
754
792
  });
793
+ const markInputAppliedInTransaction = Effect.fnUntraced(function* (validated) {
794
+ const operation = "ledger mark input applied";
795
+ const submission = yield* requireSubmission(operation, validated.submissionId);
796
+ yield* requireOwner(operation, submission);
797
+ if (submission.input_applied_record_id !== null) {
798
+ if (submission.input_applied_record_id === validated.recordId && submission.input_applied_sequence === validated.sequence) return;
799
+ return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A different canonical input marker is already recorded for this Submission.");
800
+ }
801
+ yield* sql`
802
+ UPDATE effect_agent_submissions
803
+ SET
804
+ input_applied_record_id = ${validated.recordId},
805
+ input_applied_sequence = ${validated.sequence},
806
+ state = CASE
807
+ WHEN state IN ('admitted', 'ready') THEN 'input-applied'
808
+ ELSE state
809
+ END
810
+ WHERE submission_id = ${validated.submissionId}
811
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
812
+ });
755
813
  const markInputApplied = Effect.fnUntraced(function* (request) {
756
814
  const operation = "ledger mark input applied";
757
- const validated = yield* Schema.decodeEffect(Schema.toType(MarkInputAppliedRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
815
+ const validated = yield* decodeMarkInputAppliedRequestType(request).pipe(Effect.mapError(internalFailure(operation)));
758
816
  yield* hitFailpoint("ledger:mark-input-applied:before", operation);
759
- yield* inWriteTransaction(operation, Effect.gen(function* () {
760
- const submission = yield* requireSubmission(operation, validated.submissionId);
761
- yield* requireOwner(operation, submission);
762
- if (submission.input_applied_record_id !== null) {
763
- if (submission.input_applied_record_id === validated.recordId && submission.input_applied_sequence === validated.sequence) return;
764
- return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A different canonical input marker is already recorded for this Submission.");
765
- }
766
- yield* sql`
767
- UPDATE effect_agent_submissions
768
- SET
769
- input_applied_record_id = ${validated.recordId},
770
- input_applied_sequence = ${validated.sequence},
771
- state = CASE
772
- WHEN state IN ('admitted', 'ready') THEN 'input-applied'
773
- ELSE state
774
- END
775
- WHERE submission_id = ${validated.submissionId}
776
- RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
777
- }));
817
+ yield* inWriteTransaction(operation, markInputAppliedInTransaction(validated));
778
818
  yield* hitFailpoint("ledger:mark-input-applied:after", operation);
779
819
  });
820
+ const appendInput = Effect.fnUntraced(function* (submissionId, request) {
821
+ const operation = "append original input and context";
822
+ const prepared = yield* prepareCanonicalAppend(request).pipe(Effect.provideService(Crypto.Crypto, crypto));
823
+ const record = prepared.request.batch.records[0];
824
+ const context = prepared.request.executions?.flatMap((update) => update.facts ?? []).find(({ payload }) => payload._tag === "RunContextRecorded");
825
+ if (prepared.request.executions?.length !== 1 || prepared.request.executions[0]?.previousRevision !== 0 || prepared.request.batch.records.length !== 1 || record?.payload._tag !== "UserInputRecorded" || record.payload.kind !== "user" || record.payload.submissionId !== submissionId || record.payload.runId !== runIdForSubmission(submissionId) || record.recordId !== submissionInputRecordId(submissionId) || prepared.request.batch.batchId !== submissionInputBatchId(submissionId) || context?.payload._tag === "RunContextRecorded" && context.payload.historyThrough !== prepared.request.expectedTailSequence) return yield* ThreadStoreError.make({
826
+ operation,
827
+ message: "Original input publication has a different context boundary"
828
+ });
829
+ const mapError = (cause) => ThreadStoreError.make({
830
+ operation,
831
+ message: cause.message,
832
+ cause
833
+ });
834
+ yield* journal.prepareAppend(prepared.raw).pipe(Effect.mapError(mapError));
835
+ return yield* owner.withProgress(prepared.request.threadId, owner.withMutation(Effect.gen(function* () {
836
+ yield* hitFailpoint("append:before", operation);
837
+ yield* hitFailpoint("ledger:mark-input-applied:before", operation);
838
+ const result = yield* journal.withWriteTransaction(operation)(Effect.gen(function* () {
839
+ const submission = yield* requireSubmission(operation, submissionId);
840
+ yield* requireOwner(operation, submission);
841
+ if (submission.thread_id !== prepared.request.threadId) return yield* LedgerError.make({
842
+ operation,
843
+ message: "Original input belongs to another Thread"
844
+ });
845
+ const result = yield* journal.appendPrepared(prepared.raw);
846
+ yield* markInputAppliedInTransaction(MarkInputAppliedRequest.make({
847
+ submissionId,
848
+ recordId: record.recordId,
849
+ sequence: result.firstSequence
850
+ }));
851
+ return result;
852
+ })).pipe(Effect.mapError((error) => error._tag === "DoAppendConflict" ? AppendConflict.make({
853
+ threadId: prepared.request.threadId,
854
+ batchId: prepared.request.batch.batchId,
855
+ reason: error.reason,
856
+ ...error.actualTailSequence === void 0 ? {} : { actualTailSequence: error.actualTailSequence }
857
+ }) : error._tag === "OwnershipLost" ? error : mapError(error)));
858
+ yield* hitFailpoint("ledger:mark-input-applied:after", operation);
859
+ yield* hitFailpoint("append:after", operation);
860
+ return result;
861
+ }).pipe(Effect.mapError((error) => error._tag === "OwnershipLost" || error._tag === "ThreadStoreError" || error._tag === "AppendConflict" ? error : mapError(error)))));
862
+ });
780
863
  const publish = Effect.fn("DoSubmissionLedger.publishSettlement")(function* (input) {
781
864
  const operation = "publish settlement";
782
865
  const { request, record, settlement } = yield* validatePublication(input);
@@ -899,7 +982,7 @@ const makeServices = Effect.fnUntraced(function* () {
899
982
  });
900
983
  const finalizeSettlement = Effect.fn("DoSubmissionLedger.finalizeSettlement")(function* (request) {
901
984
  const operation = "ledger finalize settlement";
902
- const validated = yield* Schema.decodeEffect(Schema.toType(SettlementFinalization))(request).pipe(Effect.mapError(internalFailure(operation)));
985
+ const validated = yield* decodeSettlementFinalizationType(request).pipe(Effect.mapError(internalFailure(operation)));
903
986
  yield* hitFailpoint("ledger:finalize-settlement:before", operation);
904
987
  const replay = yield* state.read(Effect.gen(function* () {
905
988
  const submission = yield* readSubmission(operation, validated.submissionId);
@@ -1059,9 +1142,9 @@ const makeServices = Effect.fnUntraced(function* () {
1059
1142
  });
1060
1143
  const claimJoining = Effect.fnUntraced(function* (request) {
1061
1144
  const operation = "ledger claim joining";
1062
- const validated = yield* Schema.decodeEffect(Schema.toType(ClaimJoiningRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1145
+ const validated = yield* decodeClaimJoiningRequestType(request).pipe(Effect.mapError(internalFailure(operation)));
1063
1146
  yield* hitFailpoint("ledger:claim-joining:before", operation);
1064
- const claims = yield* inWriteTransaction(operation, Effect.gen(function* () {
1147
+ const claims = yield* owner.withMutation(Effect.gen(function* () {
1065
1148
  const host = yield* requireSubmission(operation, validated.hostSubmissionId);
1066
1149
  if (host.thread_id !== validated.threadId) return yield* LedgerError.make({
1067
1150
  operation,
@@ -1069,36 +1152,42 @@ const makeServices = Effect.fnUntraced(function* () {
1069
1152
  });
1070
1153
  yield* requireOwner(operation, host);
1071
1154
  if ((yield* cached(rows.workerStops.by("thread_id", validated.threadId), operation)).length > 0) return [];
1072
- const laterRows = yield* sql`
1073
- SELECT ${submissionColumns}
1074
- FROM effect_agent_submissions
1075
- WHERE thread_id = ${validated.threadId}
1076
- AND queue_sequence > ${host.queue_sequence}
1077
- ORDER BY queue_sequence ASC
1078
- `.pipe(Effect.mapError(sqlFailure(operation)));
1079
- const later = yield* decodeSubmissionRows(operation, validated.threadId, laterRows);
1080
- const claimed = [];
1081
- for (const row of later) {
1082
- if (claimed.length >= validated.maxCount) break;
1083
- if ((row.state === "joining" || row.state === "joined") && row.joined_host_submission_id === validated.hostSubmissionId) continue;
1084
- if (row.state === "settled" && row.settled_outcome === "aborted") continue;
1085
- if (row.state !== "ready") break;
1086
- const canonical = yield* readCanonicalSettlement(operation, row);
1087
- if (canonical?.settlement.outcome === "aborted") continue;
1088
- if (canonical !== void 0) break;
1089
- yield* sql`
1155
+ const later = yield* submissionViews.matching(JSON.stringify([
1156
+ "joining-suffix",
1157
+ validated.threadId,
1158
+ host.queue_sequence
1159
+ ]), (row) => row.thread_id === validated.threadId && row.queue_sequence > host.queue_sequence, sql`
1160
+ SELECT ${submissionColumns}
1161
+ FROM effect_agent_submissions
1162
+ WHERE thread_id = ${validated.threadId}
1163
+ AND queue_sequence > ${host.queue_sequence}
1164
+ ORDER BY queue_sequence ASC
1165
+ `, 0).pipe(Effect.mapError((error) => error._tag === "SqlError" ? sqlFailure(operation)(error) : internalFailure(operation)(error)));
1166
+ if (later.length === 0) return [];
1167
+ return yield* inWriteTransaction(operation, Effect.gen(function* () {
1168
+ const claimed = [];
1169
+ for (const row of later) {
1170
+ if (claimed.length >= validated.maxCount) break;
1171
+ if ((row.state === "joining" || row.state === "joined") && row.joined_host_submission_id === validated.hostSubmissionId) continue;
1172
+ if (row.state === "settled" && row.settled_outcome === "aborted") continue;
1173
+ if (row.state !== "ready") break;
1174
+ const canonical = yield* readCanonicalSettlement(operation, row);
1175
+ if (canonical?.settlement.outcome === "aborted") continue;
1176
+ if (canonical !== void 0) break;
1177
+ yield* sql`
1090
1178
  UPDATE effect_agent_submissions
1091
1179
  SET state = 'joining', joined_host_submission_id = ${validated.hostSubmissionId}
1092
1180
  WHERE submission_id = ${row.submission_id}
1093
1181
  RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1094
- const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", row.submission_id, error.message)));
1095
- claimed.push(yield* decodeJoiningClaim({
1096
- submissionId: row.submission_id,
1097
- queueSequence: row.queue_sequence,
1098
- inputPayload
1099
- }).pipe(Effect.mapError(internalFailure(operation))));
1100
- }
1101
- return claimed;
1182
+ const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", row.submission_id, error.message)));
1183
+ claimed.push(yield* decodeJoiningClaim({
1184
+ submissionId: row.submission_id,
1185
+ queueSequence: row.queue_sequence,
1186
+ inputPayload
1187
+ }).pipe(Effect.mapError(internalFailure(operation))));
1188
+ }
1189
+ return claimed;
1190
+ }));
1102
1191
  }));
1103
1192
  yield* hitFailpoint("ledger:claim-joining:after", operation);
1104
1193
  return claims;
@@ -1668,20 +1757,21 @@ const makeServices = Effect.fnUntraced(function* () {
1668
1757
  ORDER BY thread_id ASC, queue_sequence ASC
1669
1758
  LIMIT ${SCAN_PAGE_SIZE}
1670
1759
  `).pipe(Effect.mapError(sqlFailure(operation)));
1671
- const decoded = yield* decodeRows(Schema.Array(SubmissionWorkItem), "effect_agent_submissions", "nonterminal_scan", rows, "SubmissionWorkItem").pipe(Effect.mapError(internalFailure(operation)));
1760
+ const decoded = yield* decodeRows(SubmissionWorkItems, "effect_agent_submissions", "nonterminal_scan", rows, "SubmissionWorkItem").pipe(Effect.mapError(internalFailure(operation)));
1672
1761
  const last = decoded[decoded.length - 1];
1673
1762
  return [decoded, last === void 0 || decoded.length < SCAN_PAGE_SIZE ? Option.none() : Option.some({
1674
1763
  threadId: last.threadId,
1675
1764
  queueSequence: last.queueSequence
1676
1765
  })];
1677
1766
  });
1678
- const scanNonterminal = Stream.unwrap(state.read(Effect.gen(function* () {
1679
- const active = yield* cached(workRows.matching("nonterminal", (row) => row.state !== "settled", sql`SELECT submission_id, thread_id, queue_sequence, principal, idempotency_key,
1680
- deployment_id, receipt_id, state
1681
- FROM effect_agent_submissions WHERE state <> 'settled' LIMIT 129`, 128), "ledger scan nonterminal");
1682
- if (active.length > 128) return Stream.paginate(void 0, scanPage);
1767
+ const activeNonterminal = cached(workRows.matching("nonterminal", (row) => row.state !== "settled", sql`SELECT submission_id, thread_id, queue_sequence, principal, idempotency_key,
1768
+ deployment_id, receipt_id, state
1769
+ FROM effect_agent_submissions WHERE state <> 'settled' LIMIT 129`, 128), "ledger scan nonterminal");
1770
+ const boundedNonterminal = state.read(Effect.gen(function* () {
1771
+ const active = yield* activeNonterminal;
1772
+ if (active.length > 128) return void 0;
1683
1773
  const sorted = [...active].sort((left, right) => left.thread_id < right.thread_id ? -1 : left.thread_id > right.thread_id ? 1 : left.queue_sequence - right.queue_sequence);
1684
- const work = yield* Schema.decodeEffect(Schema.Array(SubmissionWorkItem))(sorted.map((row) => ({
1774
+ return yield* decodeSubmissionWorkItems(sorted.map((row) => ({
1685
1775
  submissionId: row.submission_id,
1686
1776
  threadId: row.thread_id,
1687
1777
  queueSequence: row.queue_sequence,
@@ -1691,11 +1781,13 @@ const makeServices = Effect.fnUntraced(function* () {
1691
1781
  receiptId: row.receipt_id,
1692
1782
  state: row.state
1693
1783
  }))).pipe(Effect.mapError(internalFailure("ledger scan nonterminal")));
1694
- return Stream.fromIterable(work);
1695
- })));
1784
+ }));
1785
+ const pagedNonterminal = Stream.paginate(void 0, scanPage);
1786
+ const scanNonterminal = Stream.unwrap(Effect.map(boundedNonterminal, (work) => work === void 0 ? pagedNonterminal : Stream.fromIterable(work)));
1787
+ const listNonterminal = Effect.filterOrElse(boundedNonterminal, (work) => work !== void 0, () => Stream.runCollect(pagedNonterminal));
1696
1788
  const readAbortIntentForSubmission = Effect.fnUntraced(function* (request) {
1697
1789
  const operation = "ledger read abort intent";
1698
- const validated = yield* Schema.decodeEffect(Schema.toType(AbortIntentRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1790
+ const validated = yield* decodeAbortIntentRequestType(request).pipe(Effect.mapError(internalFailure(operation)));
1699
1791
  return yield* state.read(Effect.gen(function* () {
1700
1792
  const submission = yield* requireSubmission(operation, validated.submissionId);
1701
1793
  const intent = yield* readAbortIntent(operation, validated.submissionId);
@@ -1704,7 +1796,7 @@ const makeServices = Effect.fnUntraced(function* () {
1704
1796
  });
1705
1797
  const loadRecoverySnapshot = Effect.fnUntraced(function* (request) {
1706
1798
  const operation = "ledger load recovery snapshot";
1707
- const validated = yield* Schema.decodeEffect(Schema.toType(RecoverySnapshotRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1799
+ const validated = yield* decodeRecoverySnapshotRequestType(request).pipe(Effect.mapError(internalFailure(operation)));
1708
1800
  return yield* state.read(Effect.gen(function* () {
1709
1801
  const submissionRow = yield* requireSubmission(operation, validated.submissionId);
1710
1802
  const submission = yield* decodeSubmissionSnapshot(operation, submissionRow);
@@ -1717,8 +1809,8 @@ const makeServices = Effect.fnUntraced(function* () {
1717
1809
  const abortRow = yield* readAbortIntent(operation, validated.submissionId);
1718
1810
  if (Option.isSome(abortRow)) abortIntent = yield* abortIntentFromRow(operation, submissionRow, validated.submissionId, abortRow.value);
1719
1811
  const joinRows = (yield* cached(rows.submissions.by("joined_host_submission_id", validated.submissionId), operation)).filter((row) => row.joined_host_submission_id === validated.submissionId).sort((a, b) => a.queue_sequence - b.queue_sequence);
1720
- const joinSubmissions = yield* decodeSubmissionRows(operation, validated.submissionId, joinRows);
1721
- const joins = yield* Effect.forEach(joinSubmissions, (row) => decodeJoinSnapshot({
1812
+ const joins = [];
1813
+ for (const row of joinRows) joins.push(yield* decodeJoinSnapshot({
1722
1814
  submissionId: row.submission_id,
1723
1815
  state: row.state,
1724
1816
  hostSubmissionId: validated.submissionId
@@ -1734,16 +1826,18 @@ const makeServices = Effect.fnUntraced(function* () {
1734
1826
  }).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, error.message)));
1735
1827
  }
1736
1828
  const decisionRows = yield* readApprovalDecisions(operation, validated.submissionId);
1737
- const approvalDecisions = yield* Effect.forEach(decisionRows, (row) => approvalIntentFromRow(operation, row));
1829
+ const approvalDecisions = [];
1830
+ for (const row of decisionRows) approvalDecisions.push(yield* approvalIntentFromRow(operation, row));
1738
1831
  const resolutionRows = yield* readUnknownResolutions(operation, validated.submissionId);
1739
- const unknownResolutions = yield* Effect.forEach(resolutionRows, (row) => unknownResolutionIntentFromRow(operation, row));
1832
+ const unknownResolutions = [];
1833
+ for (const row of resolutionRows) unknownResolutions.push(yield* unknownResolutionIntentFromRow(operation, row));
1740
1834
  const childReservationRows = (yield* cached(rows.childReservations.by("parent_submission_id", validated.submissionId), operation)).filter((row) => row.parent_submission_id === validated.submissionId).sort((a, b) => a.parent_tool_call_id.localeCompare(b.parent_tool_call_id));
1741
- const decodedChildReservations = yield* decodeChildReservationRows(operation, validated.submissionId, childReservationRows);
1742
- const childReservations = yield* Effect.forEach(decodedChildReservations, (row) => childReservationSnapshotFromRow(operation, row));
1835
+ const childReservations = [];
1836
+ for (const row of childReservationRows) childReservations.push(yield* childReservationSnapshotFromRow(operation, row));
1743
1837
  const markers = yield* readChildSettlementMarkers(operation, validated.submissionId);
1744
1838
  const markersByChild = new Map(markers.map((row) => [row.child_submission_id, row]));
1745
1839
  const childAttachments = [];
1746
- for (const row of decodedChildReservations) {
1840
+ for (const row of childReservationRows) {
1747
1841
  if (row.child_submission_id === null) continue;
1748
1842
  const child = yield* readSubmission(operation, row.child_submission_id);
1749
1843
  if (Option.isSome(child)) {
@@ -1794,6 +1888,7 @@ const makeServices = Effect.fnUntraced(function* () {
1794
1888
  resolveAdmission,
1795
1889
  selectHead,
1796
1890
  markInputApplied,
1891
+ appendInput,
1797
1892
  finalizeSettlement: (request) => owner.withMutation(finalizeSettlement(request)),
1798
1893
  requestAbort,
1799
1894
  stopWorker,
@@ -1811,6 +1906,7 @@ const makeServices = Effect.fnUntraced(function* () {
1811
1906
  beginChildBudgetRelease,
1812
1907
  releaseChildBudget,
1813
1908
  scanNonterminal,
1909
+ listNonterminal,
1814
1910
  loadRecoverySnapshot,
1815
1911
  readAbortIntent: readAbortIntentForSubmission
1816
1912
  })).pipe(Context.add(StorageOwner, owner), Context.add(SettlementPublisher, { publish }));