@yielded/agent-storage-cloudflare 0.1.0-beta.166

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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/dist/DoMemoryStore.d.mts +30 -0
  3. package/dist/DoMemoryStore.mjs +66 -0
  4. package/dist/DoMemoryStore.mjs.map +1 -0
  5. package/dist/DoMessageDeliveryStore.d.mts +17 -0
  6. package/dist/DoMessageDeliveryStore.mjs +30 -0
  7. package/dist/DoMessageDeliveryStore.mjs.map +1 -0
  8. package/dist/DoScheduleStore.d.mts +36 -0
  9. package/dist/DoScheduleStore.mjs +388 -0
  10. package/dist/DoScheduleStore.mjs.map +1 -0
  11. package/dist/DoStorageConfig.d.mts +45 -0
  12. package/dist/DoStorageConfig.mjs +52 -0
  13. package/dist/DoStorageConfig.mjs.map +1 -0
  14. package/dist/DoStorageError-CGuAfSwF.d.mts +101 -0
  15. package/dist/DoStorageError.d.mts +2 -0
  16. package/dist/DoStorageError.mjs +167 -0
  17. package/dist/DoStorageError.mjs.map +1 -0
  18. package/dist/DoStorageFailpoint.d.mts +17 -0
  19. package/dist/DoStorageFailpoint.mjs +14 -0
  20. package/dist/DoStorageFailpoint.mjs.map +1 -0
  21. package/dist/DoStorageFailpointTesting.d.mts +24 -0
  22. package/dist/DoStorageFailpointTesting.mjs +22 -0
  23. package/dist/DoStorageFailpointTesting.mjs.map +1 -0
  24. package/dist/DoStorageVersion-iNSyi3cJ.d.mts +12 -0
  25. package/dist/DoStorageVersion.d.mts +2 -0
  26. package/dist/DoStorageVersion.mjs +8 -0
  27. package/dist/DoStorageVersion.mjs.map +1 -0
  28. package/dist/DoSubmissionLedger.d.mts +23 -0
  29. package/dist/DoSubmissionLedger.mjs +1979 -0
  30. package/dist/DoSubmissionLedger.mjs.map +1 -0
  31. package/dist/DoSubscriptionStore.d.mts +25 -0
  32. package/dist/DoSubscriptionStore.mjs +186 -0
  33. package/dist/DoSubscriptionStore.mjs.map +1 -0
  34. package/dist/DoThreadStore-D_83OjCH.d.mts +72 -0
  35. package/dist/DoThreadStore.d.mts +2 -0
  36. package/dist/DoThreadStore.mjs +529 -0
  37. package/dist/DoThreadStore.mjs.map +1 -0
  38. package/dist/MemoryProtocol.d.mts +38160 -0
  39. package/dist/MemoryProtocol.mjs +219 -0
  40. package/dist/MemoryProtocol.mjs.map +1 -0
  41. package/dist/PortProtocol.d.mts +7726 -0
  42. package/dist/PortProtocol.mjs +234 -0
  43. package/dist/PortProtocol.mjs.map +1 -0
  44. package/dist/PortRouting.d.mts +97 -0
  45. package/dist/PortRouting.mjs +544 -0
  46. package/dist/PortRouting.mjs.map +1 -0
  47. package/dist/do-journal-DUhjZkOf.mjs +1352 -0
  48. package/dist/do-journal-DUhjZkOf.mjs.map +1 -0
  49. package/dist/index.d.mts +14 -0
  50. package/dist/index.mjs +14 -0
  51. package/dist/migrations-DtKz_Gxr.mjs +306 -0
  52. package/dist/migrations-DtKz_Gxr.mjs.map +1 -0
  53. package/dist/owned-state-DHm2aRXx.mjs +119 -0
  54. package/dist/owned-state-DHm2aRXx.mjs.map +1 -0
  55. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  56. package/package.json +1 -0
  57. package/src/DoMemoryStore.ts +73 -0
  58. package/src/DoMessageDeliveryStore.ts +39 -0
  59. package/src/DoScheduleStore.ts +622 -0
  60. package/src/DoStorageConfig.ts +53 -0
  61. package/src/DoStorageError.ts +180 -0
  62. package/src/DoStorageFailpoint.ts +20 -0
  63. package/src/DoStorageFailpointTesting.ts +41 -0
  64. package/src/DoStorageVersion.ts +2 -0
  65. package/src/DoSubmissionLedger.ts +4027 -0
  66. package/src/DoSubscriptionStore.ts +327 -0
  67. package/src/DoThreadStore.ts +1121 -0
  68. package/src/MemoryProtocol.ts +355 -0
  69. package/src/PortProtocol.ts +436 -0
  70. package/src/PortRouting.ts +1464 -0
  71. package/src/index.ts +13 -0
  72. package/src/internal/do-journal.ts +2282 -0
  73. package/src/internal/message-delivery-schema.ts +24 -0
  74. package/src/internal/migrations.ts +304 -0
  75. package/src/internal/owned-state.ts +228 -0
  76. package/src/internal/recovery-checkpoint-schema.ts +17 -0
  77. package/src/internal/storage-span.ts +44 -0
@@ -0,0 +1,1979 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { DoStorageConfig } from "./DoStorageConfig.mjs";
3
+ import { DoStorageFailpoint } from "./DoStorageFailpoint.mjs";
4
+ import { DoLedgerError, DoStorageCorruptionError, DoStorageError } from "./DoStorageError.mjs";
5
+ import { n as ownedRows } from "./owned-state-DHm2aRXx.mjs";
6
+ import { a as initializeDoJournal, i as decodeRows, s as withStorageSpan } from "./do-journal-DUhjZkOf.mjs";
7
+ import { storageConfigLayer, storageFailpointLayer } from "./DoThreadStore.mjs";
8
+ import { Cause, Clock, Context, Crypto, DateTime, Effect, Layer, Option, Schema, Stream } from "effect";
9
+ import * as SqlClientService from "effect/sql/SqlClient";
10
+ import { SqliteClient } from "@effect/sql-sqlite-do";
11
+ import { SqlStorageProgress } from "@yielded/agent-storage-sql/sql-storage-progress";
12
+ import { EMPTY_TAIL_DIGEST } from "@yielded/agent/digest";
13
+ import { ApprovalDecision, CanonicalSequence, DefinitionDigests, Digest, PersistedJson, ProducerEpoch, RecordEnvelope, SettlementOutcome, WorkerAdmission } from "@yielded/agent/records";
14
+ import { ThreadStoreDiagnostic } from "@yielded/agent/thread-store";
15
+ import { BrowserCrypto } from "@effect/platform-browser";
16
+ import { InputMessage } from "@yielded/agent/messaging";
17
+ 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, InputAppliedMarker, JoinSnapshot, JoinedToHost, JoiningClaim, LedgerCapabilities, LedgerError, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, OwnershipLost, OwnershipRenewal, OwnershipSnapshot, ParentLinkage, QueueSequence, RecoverySnapshot, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReleaseOwnershipRequest, RenewOwnershipRequest, ReservedChildBudget, ReservedSettlement, RevertJoiningRequest, Settlement, SettlementConflict, SettlementFinalization, SettlementReservation, SettlementReservationSnapshot, SubmissionAdmissionFence, SubmissionLedger, SubmissionLookup, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SubmissionWorkItem, SuspendRequest, SuspensionReason, SuspensionSnapshot, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, WorkerLedgerState, WorkerStopCommand, settlementFailureFromRecord, submissionAbortRecordId, workerTerminalFromRecord } from "@yielded/agent/submission-ledger";
18
+ import { AssignmentTerminal } from "@yielded/agent/worker";
19
+ //#region src/DoSubmissionLedger.ts
20
+ var DoSubmissionLedger_exports = /* @__PURE__ */ __exportAll({
21
+ ledgerLayer: () => ledgerLayer,
22
+ submissionLedgerLayer: () => submissionLedgerLayer
23
+ });
24
+ /**
25
+ * Static decode-side ceiling; writes are bounded in bytes by the configured
26
+ * `maxStoredValueBytes` (see do-journal.ts).
27
+ */
28
+ const BoundedStoredText = Schema.String.check(Schema.isMaxLength(2e6));
29
+ const BoundedIdentifier = Schema.NonEmptyString.check(Schema.isMaxLength(1024));
30
+ const BoundedTimestamp = Schema.NonEmptyString.check(Schema.isMaxLength(128));
31
+ const SCAN_PAGE_SIZE = 256;
32
+ const EPOCH_ZERO = Schema.decodeSync(ProducerEpoch)(0);
33
+ const RESUME_IMMEDIATELY = "resume-immediately";
34
+ const SUSPENDED = "suspended";
35
+ const NOT_WAITING = "not-waiting";
36
+ const STILL_WAITING = "still-waiting";
37
+ const WOKEN = "woken";
38
+ const MAX_IDENTIFIER_LENGTH = 1024;
39
+ var SubmissionRow = class extends Schema.Class("SubmissionRow")({
40
+ submission_id: BoundedIdentifier,
41
+ thread_id: BoundedIdentifier,
42
+ queue_sequence: QueueSequence,
43
+ principal: BoundedIdentifier,
44
+ idempotency_key: BoundedIdentifier,
45
+ agent_id: BoundedIdentifier,
46
+ agent_digests_json: BoundedStoredText,
47
+ deployment_id: BoundedIdentifier,
48
+ input_json: BoundedStoredText,
49
+ input_digest: Digest,
50
+ receipt_id: BoundedIdentifier,
51
+ state: SubmissionState,
52
+ settled_outcome: Schema.NullOr(SettlementOutcome),
53
+ created_at: BoundedTimestamp,
54
+ ready_at: Schema.NullOr(BoundedTimestamp),
55
+ input_applied_record_id: Schema.NullOr(BoundedIdentifier),
56
+ input_applied_sequence: Schema.NullOr(CanonicalSequence),
57
+ joined_host_submission_id: Schema.NullOr(BoundedIdentifier),
58
+ suspended_reason_json: Schema.NullOr(BoundedStoredText),
59
+ suspended_at: Schema.NullOr(BoundedTimestamp),
60
+ unknown_reason: Schema.NullOr(BoundedStoredText),
61
+ unknown_tool_call_ids_json: Schema.NullOr(BoundedStoredText),
62
+ parent_submission_id: Schema.NullOr(BoundedIdentifier),
63
+ parent_tool_call_id: Schema.NullOr(BoundedIdentifier),
64
+ admission_group: Schema.NullOr(AdmissionGroup),
65
+ admission_fence_json: Schema.NullOr(BoundedStoredText),
66
+ worker_admission_json: Schema.NullOr(BoundedStoredText),
67
+ message_admission_json: Schema.NullOr(BoundedStoredText)
68
+ }) {};
69
+ const SubmissionWorkRow = Schema.Struct({
70
+ submission_id: SubmissionRow.fields.submission_id,
71
+ thread_id: SubmissionRow.fields.thread_id,
72
+ queue_sequence: SubmissionRow.fields.queue_sequence,
73
+ principal: SubmissionRow.fields.principal,
74
+ idempotency_key: SubmissionRow.fields.idempotency_key,
75
+ deployment_id: SubmissionRow.fields.deployment_id,
76
+ receipt_id: SubmissionRow.fields.receipt_id,
77
+ state: SubmissionRow.fields.state
78
+ });
79
+ var ChildReservationRow = class extends Schema.Class("ChildReservationRow")({
80
+ reservation_id: BoundedIdentifier,
81
+ parent_submission_id: BoundedIdentifier,
82
+ parent_tool_call_id: BoundedIdentifier,
83
+ child_submission_id: Schema.NullOr(BoundedIdentifier),
84
+ status: ChildReservationStatus,
85
+ allocation_json: BoundedStoredText,
86
+ allocation_digest: Digest,
87
+ accounting_json: Schema.NullOr(BoundedStoredText),
88
+ reserved_at: BoundedTimestamp,
89
+ release_began_at: Schema.NullOr(BoundedTimestamp),
90
+ released_at: Schema.NullOr(BoundedTimestamp)
91
+ }) {};
92
+ var ChildSettlementMarkerRow = class extends Schema.Class("ChildSettlementMarkerRow")({
93
+ parent_submission_id: BoundedIdentifier,
94
+ child_submission_id: BoundedIdentifier,
95
+ child_outcome: Schema.NullOr(SettlementOutcome),
96
+ recorded_at: BoundedTimestamp
97
+ }) {};
98
+ var ApprovalDecisionRow = class extends Schema.Class("ApprovalDecisionRow")({
99
+ submission_id: BoundedIdentifier,
100
+ tool_call_id: BoundedIdentifier,
101
+ decision: ApprovalDecision,
102
+ resolver: BoundedIdentifier,
103
+ reason: BoundedStoredText,
104
+ decided_at: BoundedTimestamp
105
+ }) {};
106
+ var UnknownResolutionRow = class extends Schema.Class("UnknownResolutionRow")({
107
+ submission_id: BoundedIdentifier,
108
+ tool_call_id: BoundedIdentifier,
109
+ author: BoundedIdentifier,
110
+ reason: BoundedStoredText,
111
+ resolution_json: BoundedStoredText,
112
+ resolved_at: BoundedTimestamp
113
+ }) {};
114
+ var OwnershipRow = class extends Schema.Class("OwnershipRow")({
115
+ submission_id: BoundedIdentifier,
116
+ attempt_id: BoundedIdentifier,
117
+ ownership_token: BoundedIdentifier,
118
+ producer_epoch: ProducerEpoch,
119
+ owner_producer_id: BoundedIdentifier,
120
+ lease_expires_at: BoundedTimestamp
121
+ }) {};
122
+ var ReservationRow = class extends Schema.Class("ReservationRow")({
123
+ submission_id: BoundedIdentifier,
124
+ settlement_id: BoundedIdentifier,
125
+ outcome: SettlementOutcome,
126
+ record_id: BoundedIdentifier,
127
+ record_json: BoundedStoredText,
128
+ record_digest: Digest,
129
+ reserved_at: BoundedTimestamp,
130
+ finalized_at: Schema.NullOr(BoundedTimestamp)
131
+ }) {};
132
+ var AbortIntentRow = class extends Schema.Class("AbortIntentRow")({
133
+ submission_id: BoundedIdentifier,
134
+ author: BoundedIdentifier,
135
+ reason: BoundedStoredText,
136
+ requested_at: BoundedTimestamp,
137
+ canonical_record_id: Schema.NullOr(BoundedIdentifier)
138
+ }) {};
139
+ var WorkerStopRow = class extends Schema.Class("WorkerStopRow")({
140
+ thread_id: BoundedIdentifier,
141
+ terminal: Schema.NullOr(AssignmentTerminal)
142
+ }) {};
143
+ var MaxQueueSequenceRow = class extends Schema.Class("MaxQueueSequenceRow")({ max_queue_sequence: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)) }) {};
144
+ const SUBMISSION_COLUMNS = `
145
+ submission_id,
146
+ thread_id,
147
+ queue_sequence,
148
+ principal,
149
+ idempotency_key,
150
+ agent_id,
151
+ agent_digests_json,
152
+ deployment_id,
153
+ input_json,
154
+ input_digest,
155
+ receipt_id,
156
+ state,
157
+ settled_outcome,
158
+ created_at,
159
+ ready_at,
160
+ input_applied_record_id,
161
+ input_applied_sequence,
162
+ joined_host_submission_id,
163
+ suspended_reason_json,
164
+ suspended_at,
165
+ unknown_reason,
166
+ unknown_tool_call_ids_json,
167
+ parent_submission_id,
168
+ parent_tool_call_id,
169
+ admission_group,
170
+ admission_fence_json,
171
+ worker_admission_json,
172
+ message_admission_json
173
+ `;
174
+ const CHILD_RESERVATION_COLUMNS = `
175
+ reservation_id,
176
+ parent_submission_id,
177
+ parent_tool_call_id,
178
+ child_submission_id,
179
+ status,
180
+ allocation_json,
181
+ allocation_digest,
182
+ accounting_json,
183
+ reserved_at,
184
+ release_began_at,
185
+ released_at
186
+ `;
187
+ /** The branded ToolCallId schema, reached through the thread port so no core import is needed. */
188
+ const ToolCallIdSchema = ApprovalDecisionCommand.fields.toolCallId;
189
+ const ToolCallIdList = Schema.Array(ToolCallIdSchema);
190
+ const encodePersistedJsonText = Schema.encodeEffect(Schema.fromJsonString(PersistedJson));
191
+ const encodeDefinitionDigestsText = Schema.encodeEffect(Schema.fromJsonString(DefinitionDigests));
192
+ const encodeRecordEnvelopeText = Schema.encodeEffect(Schema.fromJsonString(RecordEnvelope));
193
+ const decodeRecordEnvelopeText = Schema.decodeEffect(Schema.fromJsonString(RecordEnvelope));
194
+ const encodeSuspensionReasonText = Schema.encodeEffect(Schema.fromJsonString(SuspensionReason));
195
+ const encodeUnknownResolutionText = Schema.encodeEffect(Schema.fromJsonString(UnknownResolution));
196
+ const encodeToolCallIdsText = Schema.encodeEffect(Schema.fromJsonString(ToolCallIdList));
197
+ const decodeToolCallIdsText = Schema.decodeEffect(Schema.fromJsonString(ToolCallIdList));
198
+ const parseStoredJsonText = Schema.decodeEffect(Schema.fromJsonString(Schema.Json));
199
+ const decodeAdmissionResult = Schema.decodeUnknownEffect(AdmissionResult);
200
+ const decodeClaim = Schema.decodeUnknownEffect(Claim);
201
+ const decodeOwnershipRenewal = Schema.decodeUnknownEffect(OwnershipRenewal);
202
+ const decodeSettlement = Schema.decodeUnknownEffect(Settlement);
203
+ const decodeAbortIntent = Schema.decodeUnknownEffect(AbortIntent);
204
+ const decodeOwnershipSnapshot = Schema.decodeUnknownEffect(OwnershipSnapshot);
205
+ const decodeInputAppliedMarker = Schema.decodeUnknownEffect(InputAppliedMarker);
206
+ const decodeSubmissionSnapshotUnknown = Schema.decodeUnknownEffect(SubmissionSnapshot);
207
+ const decodeSubmissionId = Schema.decodeUnknownEffect(SubmissionSnapshot.fields.submissionId);
208
+ const decodeQueueSequence = Schema.decodeUnknownEffect(QueueSequence);
209
+ const decodeUtcInstant = Schema.decodeUnknownEffect(Schema.DateTimeUtcFromString);
210
+ const decodeJoiningClaim = Schema.decodeUnknownEffect(JoiningClaim);
211
+ const decodeJoinSnapshot = Schema.decodeUnknownEffect(JoinSnapshot);
212
+ const decodeSuspensionSnapshot = Schema.decodeUnknownEffect(SuspensionSnapshot);
213
+ const decodeApprovalDecisionIntent = Schema.decodeUnknownEffect(ApprovalDecisionIntent);
214
+ const decodeUnknownResolutionIntent = Schema.decodeUnknownEffect(UnknownResolutionIntent);
215
+ const decodeParentLinkage = Schema.decodeUnknownEffect(ParentLinkage);
216
+ const decodeChildReservationSnapshotUnknown = Schema.decodeUnknownEffect(ChildBudgetReservationSnapshot);
217
+ const decodeChildAttachmentSnapshot = Schema.decodeUnknownEffect(ChildAttachmentSnapshot);
218
+ const equivalentPersistedJson = Schema.toEquivalence(PersistedJson);
219
+ const equivalentUnknownResolution = Schema.toEquivalence(UnknownResolution);
220
+ const isDoStorageError = Schema.is(DoStorageError);
221
+ /** Wrap an adapter-internal failure into the port's LedgerError without erasing its tag. */
222
+ const internalFailure = (operation) => (error) => LedgerError.make({
223
+ operation,
224
+ message: error.message,
225
+ cause: error
226
+ });
227
+ /**
228
+ * Classify raw SQL failures. Within one Durable Object there is exactly one writer, so the
229
+ * Node adapter's retryable `SqliteWriteContention` classification has no analogue: every raw
230
+ * failure is a `DoLedgerError` preserved as the LedgerError's cause.
231
+ */
232
+ const sqlFailure = (operation) => (error) => internalFailure(operation)(DoLedgerError.make({
233
+ cause: error,
234
+ operation,
235
+ message: error.message
236
+ }));
237
+ const corruptionFailure = (operation, table, rowKey, message) => internalFailure(operation)(DoStorageCorruptionError.make({
238
+ table,
239
+ rowKey,
240
+ message
241
+ }));
242
+ const submissionsRows = ownedRows(SubmissionRow, "effect_agent_submissions", (row) => row.submission_id, "submission_id", [[
243
+ "thread_id",
244
+ "principal",
245
+ "idempotency_key"
246
+ ]]);
247
+ const ownershipRows = ownedRows(OwnershipRow, "effect_agent_submission_ownership", (row) => row.submission_id, "submission_id");
248
+ const reservationsRows = ownedRows(ReservationRow, "effect_agent_settlement_reservations", (row) => row.submission_id, "submission_id");
249
+ const abortsRows = ownedRows(AbortIntentRow, "effect_agent_abort_intents", (row) => row.submission_id, "submission_id");
250
+ const approvalsRows = ownedRows(ApprovalDecisionRow, "effect_agent_approval_decisions", (row) => JSON.stringify([row.submission_id, row.tool_call_id]));
251
+ const resolutionsRows = ownedRows(UnknownResolutionRow, "effect_agent_unknown_resolutions", (row) => JSON.stringify([row.submission_id, row.tool_call_id]));
252
+ const childSettlementsRows = ownedRows(ChildSettlementMarkerRow, "effect_agent_child_settlements", (row) => JSON.stringify([row.parent_submission_id, row.child_submission_id]));
253
+ const childReservationsRows = ownedRows(ChildReservationRow, "effect_agent_child_reservations", (row) => row.reservation_id, "reservation_id");
254
+ const workerStopsRows = ownedRows(WorkerStopRow, "effect_agent_worker_stops", (row) => row.thread_id, "thread_id");
255
+ const submissionWorkRows = ownedRows(SubmissionWorkRow, "effect_agent_submissions", (row) => row.submission_id);
256
+ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
257
+ const progress = yield* SqlStorageProgress;
258
+ const recordProgress = (kind, operation) => progress.committed(kind).pipe(Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, internalFailure(operation)))));
259
+ const config = yield* DoStorageConfig;
260
+ const failpoint = yield* DoStorageFailpoint;
261
+ const sql = yield* SqlClientService.SqlClient;
262
+ const crypto = yield* Crypto.Crypto;
263
+ const admissionFence = yield* SubmissionAdmissionFence;
264
+ const journal = yield* initializeDoJournal(sql, failpoint.hit, config.maxStoredValueBytes);
265
+ const lifecycle = journal.lifecycle;
266
+ const state = journal.state;
267
+ const submissionViews = submissionsRows(state, sql);
268
+ const workRows = submissionWorkRows(state, sql);
269
+ const rows = {
270
+ submissions: {
271
+ by: submissionViews.by,
272
+ byFields: submissionViews.byFields,
273
+ seed: submissionViews.seed,
274
+ write: (effect) => submissionViews.write(effect).pipe(Effect.tap((changed) => workRows.write(Effect.succeed(changed))))
275
+ },
276
+ ownership: ownershipRows(state, sql),
277
+ reservations: reservationsRows(state, sql),
278
+ aborts: abortsRows(state, sql),
279
+ approvals: approvalsRows(state, sql),
280
+ resolutions: resolutionsRows(state, sql),
281
+ childSettlements: childSettlementsRows(state, sql),
282
+ childReservations: childReservationsRows(state, sql),
283
+ threads: journal.threads,
284
+ workerStops: workerStopsRows(state, sql)
285
+ };
286
+ const cached = (effect, operation) => effect.pipe(Effect.mapError(internalFailure(operation)));
287
+ const laneWork = Effect.fnUntraced(function* (threadId, operation) {
288
+ const work = yield* cached(workRows.matching(JSON.stringify(["lane", threadId]), (row) => row.thread_id === threadId, sql`SELECT submission_id, thread_id, queue_sequence, principal, idempotency_key,
289
+ deployment_id, receipt_id, state FROM effect_agent_submissions WHERE thread_id = ${threadId} LIMIT 129`, 128), operation);
290
+ return work.length > 128 ? void 0 : [...work].sort((a, b) => a.queue_sequence - b.queue_sequence);
291
+ });
292
+ const byKey = Effect.fnUntraced(function* (request, operation) {
293
+ const lane = yield* laneWork(request.threadId, operation);
294
+ if (lane === void 0) return yield* cached(rows.submissions.byFields([
295
+ ["thread_id", request.threadId],
296
+ ["principal", request.principal],
297
+ ["idempotency_key", request.idempotencyKey]
298
+ ]), operation);
299
+ const matches = lane.filter((row) => row.principal === request.principal && row.idempotency_key === request.idempotencyKey);
300
+ return yield* Effect.forEach(matches, (row) => requireSubmission(operation, row.submission_id));
301
+ }, state.read);
302
+ const retainLifecycle = (submission, fact) => lifecycle === void 0 ? Effect.void : Effect.gen(function* () {
303
+ const ownerThreadId = yield* Schema.decodeEffect(SubmissionSnapshot.fields.threadId)(submission.thread_id).pipe(Effect.mapError(internalFailure("lifecycle owner")));
304
+ yield* lifecycle.retain({
305
+ ownerThreadId,
306
+ createdAt: yield* DateTime.now,
307
+ fact
308
+ }).pipe(Effect.mapError(internalFailure("retain lifecycle publication")));
309
+ });
310
+ const retainWorkerSeal = (threadId, terminal) => lifecycle === void 0 ? Effect.void : Effect.gen(function* () {
311
+ const operation = "retain worker inbox seal";
312
+ const ownerThreadId = yield* Schema.decodeEffect(SubmissionSnapshot.fields.threadId)(threadId).pipe(Effect.mapError(internalFailure(operation)));
313
+ const active = yield* sql`SELECT submission_id FROM effect_agent_submissions WHERE thread_id = ${threadId} AND state <> 'settled' ORDER BY queue_sequence`.pipe(Effect.mapError(sqlFailure(operation)));
314
+ const activeSubmissionIds = yield* Effect.forEach(active, (row) => decodeSubmissionId(row.submission_id).pipe(Effect.mapError(internalFailure(operation))));
315
+ yield* lifecycle.retain({
316
+ id: JSON.stringify([threadId, "inbox-sealed"]),
317
+ ownerThreadId,
318
+ createdAt: yield* DateTime.now,
319
+ fact: {
320
+ _tag: "WorkerInboxSealed",
321
+ threadId: ownerThreadId,
322
+ activeSubmissionIds,
323
+ terminal
324
+ }
325
+ }).pipe(Effect.mapError(internalFailure(operation)));
326
+ });
327
+ const hitFailpoint = (location, operation) => failpoint.hit(location).pipe(Effect.mapError((error) => internalFailure(operation)(error)));
328
+ /**
329
+ * Run one ledger mutation under the journal's Durable Object storage-backed transaction so
330
+ * ownership-token and epoch checks are atomic with their writes (DUR-006). Transaction
331
+ * failures surface as LedgerError carrying the typed `DoStorageError` as cause.
332
+ */
333
+ const inWriteTransaction = (operation, effect, expected) => journal.withWriteTransaction(operation, expected)(effect).pipe(Effect.mapError((error) => isDoStorageError(error) ? internalFailure(operation)(error) : error));
334
+ const mintUuid = (operation) => crypto.randomUUIDv7.pipe(Effect.mapError((error) => internalFailure(operation)(error)));
335
+ const currentInstant = Effect.map(Clock.currentTimeMillis, (millis) => ({
336
+ millis,
337
+ iso: new Date(millis).toISOString()
338
+ }));
339
+ const timestampMillis = (operation, rowKey) => (timestamp) => decodeUtcInstant(timestamp).pipe(Effect.map(DateTime.toEpochMillis), Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submission_ownership", rowKey, error.message)));
340
+ const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows(Schema.Array(SubmissionRow), "effect_agent_submissions", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
341
+ const readSubmission = Effect.fn("DoSubmissionLedger.readSubmission")(function* (operation, submissionId) {
342
+ const found = yield* cached(rows.submissions.by("submission_id", submissionId), operation);
343
+ return Option.fromUndefinedOr(found[0]);
344
+ });
345
+ const requireSubmission = Effect.fn("DoSubmissionLedger.requireSubmission")(function* (operation, submissionId) {
346
+ const submission = yield* readSubmission(operation, submissionId);
347
+ if (Option.isNone(submission)) return yield* LedgerError.make({
348
+ operation,
349
+ message: `Unknown submission ${submissionId}.`
350
+ });
351
+ return submission.value;
352
+ });
353
+ const readOwnership = Effect.fn("DoSubmissionLedger.readOwnership")(function* (operation, submissionId) {
354
+ const found = yield* cached(rows.ownership.by("submission_id", submissionId), operation);
355
+ return Option.fromUndefinedOr(found[0]);
356
+ });
357
+ const threadEpoch = Effect.fn("DoSubmissionLedger.threadEpoch")(function* (operation, threadId) {
358
+ const threads = yield* journal.getThread(threadId).pipe(Effect.mapError(internalFailure(operation)));
359
+ return threads.length === 0 ? EPOCH_ZERO : threads[0].producer_epoch;
360
+ });
361
+ /**
362
+ * Verify inside the surrounding write transaction that the presented token still owns the
363
+ * Submission's lane; a superseded or missing token fails with OwnershipLost carrying the
364
+ * Thread's current producer epoch (DUR-006).
365
+ */
366
+ const requireOwnership = Effect.fnUntraced(function* (operation, submission, ownershipToken) {
367
+ const ownership = yield* readOwnership(operation, submission.submission_id);
368
+ const actualEpoch = yield* threadEpoch(operation, submission.thread_id);
369
+ if (Option.isNone(ownership) || ownership.value.ownership_token !== ownershipToken || ownership.value.producer_epoch !== actualEpoch) {
370
+ const submissionId = yield* Schema.decodeEffect(SubmissionSnapshot.fields.submissionId)(submission.submission_id).pipe(Effect.mapError(internalFailure(operation)));
371
+ return yield* OwnershipLost.make({
372
+ submissionId,
373
+ actualEpoch
374
+ });
375
+ }
376
+ return ownership.value;
377
+ }, withStorageSpan("DoSubmissionLedger.requireOwnership", (error) => error._tag === "OwnershipLost"));
378
+ const submissions = /* @__PURE__ */ new WeakMap();
379
+ const decodeSubmissionSnapshot = Effect.fn("DoSubmissionLedger.decodeSubmissionSnapshot")(function* (operation, row) {
380
+ const existing = submissions.get(row);
381
+ if (existing !== void 0) return existing;
382
+ const decodeFailure = (error) => internalFailure(operation)(DoStorageCorruptionError.make({
383
+ table: "effect_agent_submissions",
384
+ rowKey: row.submission_id,
385
+ message: "Stored submission does not satisfy the ledger schema",
386
+ diagnostic: ThreadStoreDiagnostic.make({
387
+ causeTag: error._tag,
388
+ operation,
389
+ decoder: "SubmissionSnapshot",
390
+ issueTag: error.issue._tag
391
+ })
392
+ }));
393
+ const agentDigests = yield* parseStoredJsonText(row.agent_digests_json).pipe(Effect.mapError(decodeFailure));
394
+ const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(Effect.mapError(decodeFailure));
395
+ if (row.parent_submission_id === null !== (row.parent_tool_call_id === null)) return yield* corruptionFailure(operation, "effect_agent_submissions", row.submission_id, "A parent linkage must record both the parent Submission and the parent Tool Call.");
396
+ const snapshot = yield* decodeSubmissionSnapshotUnknown({
397
+ submissionId: row.submission_id,
398
+ threadId: row.thread_id,
399
+ queueSequence: row.queue_sequence,
400
+ principal: row.principal,
401
+ idempotencyKey: row.idempotency_key,
402
+ agentId: row.agent_id,
403
+ agentDigests,
404
+ deploymentId: row.deployment_id,
405
+ inputPayload,
406
+ inputDigest: row.input_digest,
407
+ receiptId: row.receipt_id,
408
+ state: row.state,
409
+ createdAt: row.created_at,
410
+ ...row.admission_group === null ? {} : { admissionGroup: row.admission_group },
411
+ ...row.worker_admission_json === null ? {} : { workerAdmission: yield* parseStoredJsonText(row.worker_admission_json).pipe(Effect.mapError(decodeFailure)) },
412
+ ...row.message_admission_json === null ? {} : { messageAdmission: yield* parseStoredJsonText(row.message_admission_json).pipe(Effect.mapError(decodeFailure)) },
413
+ ...row.admission_fence_json === null ? {} : { admissionFence: yield* parseStoredJsonText(row.admission_fence_json).pipe(Effect.mapError(decodeFailure)) },
414
+ ...row.settled_outcome === null ? {} : { settledOutcome: row.settled_outcome },
415
+ ...row.ready_at === null ? {} : { readyAt: row.ready_at },
416
+ ...row.parent_submission_id === null || row.parent_tool_call_id === null ? {} : { parentLinkage: {
417
+ parentSubmissionId: row.parent_submission_id,
418
+ parentToolCallId: row.parent_tool_call_id
419
+ } }
420
+ }).pipe(Effect.mapError(decodeFailure));
421
+ submissions.set(row, snapshot);
422
+ return snapshot;
423
+ });
424
+ const readReservation = Effect.fn("DoSubmissionLedger.readReservation")(function* (operation, submissionId) {
425
+ const found = yield* cached(rows.reservations.by("submission_id", submissionId), operation);
426
+ return Option.fromUndefinedOr(found[0]);
427
+ });
428
+ const readAbortIntent = Effect.fn("DoSubmissionLedger.readAbortIntent")(function* (operation, submissionId) {
429
+ const found = yield* cached(rows.aborts.by("submission_id", submissionId), operation);
430
+ return Option.fromUndefinedOr(found[0]);
431
+ });
432
+ /**
433
+ * Read the durable cross-store child-settlement markers recorded against one parent
434
+ * Submission (the DC realization of the port's "cross-store adapters record a durable
435
+ * notification marker" contract).
436
+ */
437
+ const readChildSettlementMarkers = Effect.fn("DoSubmissionLedger.readChildSettlementMarkers")(function* (operation, parentSubmissionId) {
438
+ return (yield* cached(rows.childSettlements.by("parent_submission_id", parentSubmissionId), operation)).toSorted((a, b) => a.child_submission_id < b.child_submission_id ? -1 : a.child_submission_id > b.child_submission_id ? 1 : 0);
439
+ });
440
+ /**
441
+ * Whether one listed child is provably settled from THIS store: either its own row lives
442
+ * here and is settled (single-store evidence, identical to the Node adapter), or a durable
443
+ * cross-store notification marker was recorded for it (the child's row lives in another
444
+ * Durable Object and its owner reported the settlement through `recordChildSettled`).
445
+ */
446
+ const childProvablySettled = Effect.fn("DoSubmissionLedger.childProvablySettled")(function* (operation, markerChildren, childSubmissionId) {
447
+ if (markerChildren.has(childSubmissionId)) return true;
448
+ const childRow = yield* readSubmission(operation, childSubmissionId);
449
+ return Option.isSome(childRow) && childRow.value.state === "settled";
450
+ });
451
+ const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows(Schema.Array(ChildReservationRow), "effect_agent_child_reservations", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
452
+ const readChildReservation = Effect.fn("DoSubmissionLedger.readChildReservation")(function* (operation, reservationId) {
453
+ const rows = yield* sql`
454
+ SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
455
+ FROM effect_agent_child_reservations
456
+ WHERE reservation_id = ${reservationId}
457
+ `.pipe(Effect.mapError(sqlFailure(operation)));
458
+ const decoded = yield* decodeChildReservationRows(operation, reservationId, rows);
459
+ if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_child_reservations", reservationId, "A child reservation primary key returned more than one row.");
460
+ return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
461
+ });
462
+ const readChildReservationForCall = Effect.fn("DoSubmissionLedger.readChildReservationForCall")(function* (operation, parentSubmissionId, parentToolCallId) {
463
+ const rows = yield* sql`
464
+ SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
465
+ FROM effect_agent_child_reservations
466
+ WHERE parent_submission_id = ${parentSubmissionId}
467
+ AND parent_tool_call_id = ${parentToolCallId}
468
+ `.pipe(Effect.mapError(sqlFailure(operation)));
469
+ const decoded = yield* decodeChildReservationRows(operation, `${parentSubmissionId}/${parentToolCallId}`, rows);
470
+ if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_child_reservations", `${parentSubmissionId}/${parentToolCallId}`, "A parent Tool Call returned more than one child reservation.");
471
+ return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
472
+ });
473
+ const childReservationSnapshotFromRow = Effect.fn("DoSubmissionLedger.childReservationSnapshotFromRow")(function* (operation, row) {
474
+ const rowFailure = (error) => corruptionFailure(operation, "effect_agent_child_reservations", row.reservation_id, error.message);
475
+ const allocation = yield* parseStoredJsonText(row.allocation_json).pipe(Effect.mapError(rowFailure));
476
+ const accounting = row.accounting_json === null ? void 0 : yield* parseStoredJsonText(row.accounting_json).pipe(Effect.mapError(rowFailure));
477
+ return yield* decodeChildReservationSnapshotUnknown({
478
+ reservationId: row.reservation_id,
479
+ parentSubmissionId: row.parent_submission_id,
480
+ parentToolCallId: row.parent_tool_call_id,
481
+ status: row.status,
482
+ allocation,
483
+ allocationDigest: row.allocation_digest,
484
+ reservedAt: row.reserved_at,
485
+ ...row.child_submission_id === null ? {} : { childSubmissionId: row.child_submission_id },
486
+ ...accounting === void 0 ? {} : { accounting },
487
+ ...row.release_began_at === null ? {} : { releaseBeganAt: row.release_began_at },
488
+ ...row.released_at === null ? {} : { releasedAt: row.released_at }
489
+ }).pipe(Effect.mapError(rowFailure));
490
+ });
491
+ const readApprovalDecisions = Effect.fn("DoSubmissionLedger.readApprovalDecisions")(function* (operation, submissionId) {
492
+ return (yield* cached(rows.approvals.by("submission_id", submissionId), operation)).toSorted((a, b) => a.tool_call_id < b.tool_call_id ? -1 : a.tool_call_id > b.tool_call_id ? 1 : 0);
493
+ });
494
+ const approvalIntentFromRow = Effect.fn("DoSubmissionLedger.approvalIntentFromRow")(function* (operation, row) {
495
+ return yield* decodeApprovalDecisionIntent({
496
+ submissionId: row.submission_id,
497
+ toolCallId: row.tool_call_id,
498
+ decision: row.decision,
499
+ resolver: row.resolver,
500
+ reason: row.reason,
501
+ decidedAt: row.decided_at
502
+ }).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_approval_decisions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
503
+ });
504
+ const readUnknownResolutions = Effect.fn("DoSubmissionLedger.readUnknownResolutions")(function* (operation, submissionId) {
505
+ return (yield* cached(rows.resolutions.by("submission_id", submissionId), operation)).toSorted((a, b) => a.tool_call_id < b.tool_call_id ? -1 : a.tool_call_id > b.tool_call_id ? 1 : 0);
506
+ });
507
+ const unknownResolutionIntentFromRow = Effect.fn("DoSubmissionLedger.unknownResolutionIntentFromRow")(function* (operation, row) {
508
+ const resolution = yield* parseStoredJsonText(row.resolution_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_unknown_resolutions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
509
+ return yield* decodeUnknownResolutionIntent({
510
+ submissionId: row.submission_id,
511
+ toolCallId: row.tool_call_id,
512
+ author: row.author,
513
+ reason: row.reason,
514
+ resolution,
515
+ resolvedAt: row.resolved_at
516
+ }).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_unknown_resolutions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
517
+ });
518
+ /** The Submission's marked-unknown open Tool Call identities, empty when never marked. */
519
+ const storedUnknownToolCallIds = Effect.fn("DoSubmissionLedger.storedUnknownToolCallIds")(function* (operation, submission) {
520
+ if (submission.unknown_tool_call_ids_json === null) return [];
521
+ return yield* decodeToolCallIdsText(submission.unknown_tool_call_ids_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", submission.submission_id, error.message)));
522
+ });
523
+ /**
524
+ * Canonical history is the abort authority (DUR-015): the intent's canonicalRecordId is
525
+ * derived from the shared canonical-records table using the deterministic abort record
526
+ * identity, never from a cached ledger marker.
527
+ */
528
+ const canonicalAbortRecordId = Effect.fn("DoSubmissionLedger.canonicalAbortRecordId")(function* (operation, threadId, submissionId) {
529
+ const recordId = submissionAbortRecordId(submissionId);
530
+ return (yield* journal.hasRecord(threadId, recordId).pipe(Effect.mapError(internalFailure(operation)))) ? recordId : void 0;
531
+ });
532
+ const abortIntentFromRow = Effect.fn("DoSubmissionLedger.abortIntentFromRow")(function* (operation, submission, submissionId, row) {
533
+ const canonicalRecordId = yield* canonicalAbortRecordId(operation, submission.thread_id, submissionId);
534
+ return yield* decodeAbortIntent({
535
+ submissionId: row.submission_id,
536
+ author: row.author,
537
+ reason: row.reason,
538
+ requestedAt: row.requested_at,
539
+ ...canonicalRecordId === void 0 ? {} : { canonicalRecordId }
540
+ }).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_abort_intents", row.submission_id, error.message)));
541
+ });
542
+ const capabilities = Effect.succeed(LedgerCapabilities.make({ durability: "durable-cloudflare" }));
543
+ const admit = Effect.fn("DoSubmissionLedger.admit")(function* (request) {
544
+ const operation = "ledger admit";
545
+ const validated = yield* Schema.decodeEffect(Schema.toType(AdmissionRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
546
+ const inputJson = yield* encodePersistedJsonText(validated.inputPayload).pipe(Effect.mapError(internalFailure(operation)));
547
+ yield* journal.checkValueBound(operation, inputJson).pipe(Effect.mapError(internalFailure(operation)));
548
+ const workerAdmissionJson = validated.workerAdmission === void 0 ? null : yield* Schema.encodeEffect(Schema.fromJsonString(WorkerAdmission))(validated.workerAdmission).pipe(Effect.mapError(internalFailure(operation)));
549
+ if (workerAdmissionJson !== null && new TextEncoder().encode(workerAdmissionJson).byteLength > config.maxStoredValueBytes) return yield* LedgerError.make({
550
+ operation,
551
+ message: "Worker admission metadata exceeds the stored value bound"
552
+ });
553
+ const messageAdmissionJson = validated.messageAdmission === void 0 ? null : yield* Schema.encodeEffect(Schema.fromJsonString(InputMessage))(validated.messageAdmission).pipe(Effect.mapError(internalFailure(operation)));
554
+ if (messageAdmissionJson !== null && new TextEncoder().encode(messageAdmissionJson).byteLength > config.maxStoredValueBytes) return yield* LedgerError.make({
555
+ operation,
556
+ message: "Message admission metadata exceeds the stored value bound"
557
+ });
558
+ const agentDigestsJson = yield* encodeDefinitionDigestsText(validated.agentDigests).pipe(Effect.mapError(internalFailure(operation)));
559
+ const mintedSubmissionId = `${yield* mintUuid(operation)}:${validated.threadId}`;
560
+ if (mintedSubmissionId.length > MAX_IDENTIFIER_LENGTH) return yield* LedgerError.make({
561
+ operation,
562
+ message: `A routable Submission identity of ${mintedSubmissionId.length} characters exceeds the ${MAX_IDENTIFIER_LENGTH}-character ledger row bound; shorten the Thread identity.`
563
+ });
564
+ const mintedReceiptId = `receipt-${yield* mintUuid(operation)}`;
565
+ yield* hitFailpoint("ledger:admit:before", operation);
566
+ const result = yield* inWriteTransaction(operation, Effect.gen(function* () {
567
+ const keyRowKey = `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`;
568
+ const existingRows = yield* byKey(validated, operation);
569
+ const existing = yield* decodeSubmissionRows(operation, keyRowKey, existingRows);
570
+ if (existing.length > 1) return yield* corruptionFailure(operation, "effect_agent_submissions", keyRowKey, "An admission idempotency key returned more than one row.");
571
+ if (existing.length === 1) {
572
+ 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;
573
+ if (existing[0].input_digest !== validated.inputDigest || !sameLinkage) return yield* AdmissionConflict.make({
574
+ threadId: validated.threadId,
575
+ principal: validated.principal,
576
+ idempotencyKey: validated.idempotencyKey,
577
+ existingInputDigest: existing[0].input_digest,
578
+ attemptedInputDigest: validated.inputDigest
579
+ });
580
+ const retainedWorkerAdmission = existing[0].worker_admission_json === null ? void 0 : yield* Schema.decodeEffect(Schema.fromJsonString(WorkerAdmission))(existing[0].worker_admission_json).pipe(Effect.mapError(internalFailure(operation)));
581
+ const retainedInputMessage = existing[0].message_admission_json === null ? void 0 : yield* Schema.decodeEffect(Schema.fromJsonString(InputMessage))(existing[0].message_admission_json).pipe(Effect.mapError(internalFailure(operation)));
582
+ const retainedFence = existing[0].admission_fence_json === null ? void 0 : yield* Schema.decodeEffect(Schema.fromJsonString(AdmissionFence))(existing[0].admission_fence_json).pipe(Effect.mapError(internalFailure(operation)));
583
+ if ((existing[0].admission_group ?? void 0) !== validated.admissionGroup || !Schema.toEquivalence(Schema.optional(WorkerAdmission))(retainedWorkerAdmission, validated.workerAdmission) || !Schema.toEquivalence(Schema.optional(InputMessage))(retainedInputMessage, validated.messageAdmission) || !Schema.toEquivalence(Schema.optional(AdmissionFence))(retainedFence, validated.admissionFence)) return yield* AdmissionConflict.make({
584
+ threadId: validated.threadId,
585
+ principal: validated.principal,
586
+ idempotencyKey: validated.idempotencyKey,
587
+ existingInputDigest: existing[0].input_digest,
588
+ attemptedInputDigest: validated.inputDigest
589
+ });
590
+ return yield* decodeAdmissionResult({
591
+ submissionId: existing[0].submission_id,
592
+ receiptId: existing[0].receipt_id,
593
+ queueSequence: existing[0].queue_sequence,
594
+ state: existing[0].state,
595
+ replayed: true
596
+ }).pipe(Effect.mapError(internalFailure(operation)));
597
+ }
598
+ if ((yield* cached(rows.workerStops.by("thread_id", validated.threadId), operation)).length > 0) return yield* AdmissionPolicyError.make({
599
+ reason: "refused",
600
+ code: "worker-stopped"
601
+ });
602
+ const lane = yield* laneWork(validated.threadId, operation);
603
+ const firstRows = lane === void 0 ? yield* sql`
604
+ SELECT worker_admission_json FROM effect_agent_submissions
605
+ WHERE thread_id=${validated.threadId} ORDER BY queue_sequence LIMIT 1
606
+ `.pipe(Effect.mapError(sqlFailure(operation))) : lane[0] === void 0 ? [] : [yield* requireSubmission(operation, lane[0].submission_id)];
607
+ const first = yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ worker_admission_json: Schema.NullOr(BoundedStoredText) })))(firstRows).pipe(Effect.mapError(internalFailure(operation)));
608
+ if (first[0] !== void 0) {
609
+ 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)));
610
+ if (!Schema.toEquivalence(Schema.optional(WorkerAdmission.fields.origin))(previous?.origin, validated.workerAdmission?.origin)) return yield* AdmissionPolicyError.make({
611
+ reason: "refused",
612
+ code: "worker-origin-conflict"
613
+ });
614
+ }
615
+ yield* admissionFence.check(validated);
616
+ if (validated.admissionGroup !== void 0) {
617
+ if ((yield* sql`
618
+ SELECT submission_id FROM effect_agent_submissions
619
+ WHERE thread_id=${validated.threadId} AND admission_group=${validated.admissionGroup} AND state<>'settled' LIMIT 1
620
+ `.pipe(Effect.mapError(sqlFailure(operation)))).length > 0) return yield* AdmissionPolicyError.make({
621
+ reason: "occupied",
622
+ code: "admission-group"
623
+ });
624
+ }
625
+ const maxRows = lane === void 0 ? yield* sql`
626
+ SELECT COALESCE(MAX(queue_sequence), 0) AS max_queue_sequence
627
+ FROM effect_agent_submissions
628
+ WHERE thread_id = ${validated.threadId}
629
+ `.pipe(Effect.mapError(sqlFailure(operation))) : [{ max_queue_sequence: lane.at(-1)?.queue_sequence ?? 0 }];
630
+ const decodedMax = yield* decodeRows(Schema.Array(MaxQueueSequenceRow), "effect_agent_submissions", validated.threadId, maxRows).pipe(Effect.mapError(internalFailure(operation)));
631
+ const queueSequence = yield* decodeQueueSequence((decodedMax[0]?.max_queue_sequence ?? 0) + 1).pipe(Effect.mapError(internalFailure(operation)));
632
+ const now = yield* currentInstant;
633
+ yield* sql`
634
+ INSERT INTO effect_agent_submissions (
635
+ submission_id,
636
+ thread_id,
637
+ queue_sequence,
638
+ principal,
639
+ idempotency_key,
640
+ agent_id,
641
+ agent_digests_json,
642
+ deployment_id,
643
+ input_json,
644
+ input_digest,
645
+ receipt_id,
646
+ state,
647
+ created_at,
648
+ parent_submission_id,
649
+ parent_tool_call_id,
650
+ admission_group,
651
+ admission_fence_json,
652
+ worker_admission_json,
653
+ message_admission_json
654
+ ) VALUES (
655
+ ${mintedSubmissionId},
656
+ ${validated.threadId},
657
+ ${queueSequence},
658
+ ${validated.principal},
659
+ ${validated.idempotencyKey},
660
+ ${validated.agentId},
661
+ ${agentDigestsJson},
662
+ ${validated.deploymentId},
663
+ ${inputJson},
664
+ ${validated.inputDigest},
665
+ ${mintedReceiptId},
666
+ 'admitted',
667
+ ${now.iso},
668
+ ${validated.parentLinkage?.parentSubmissionId ?? null},
669
+ ${validated.parentLinkage?.parentToolCallId ?? null},
670
+ ${validated.admissionGroup ?? null},
671
+ ${validated.admissionFence === void 0 ? null : JSON.stringify(validated.admissionFence)},
672
+ ${workerAdmissionJson},
673
+ ${messageAdmissionJson}
674
+ )
675
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
676
+ rows.aborts.seed("submission_id", mintedSubmissionId, []);
677
+ rows.ownership.seed("submission_id", mintedSubmissionId, []);
678
+ rows.reservations.seed("submission_id", mintedSubmissionId, []);
679
+ rows.submissions.seed("joined_host_submission_id", mintedSubmissionId, []);
680
+ rows.approvals.seed("submission_id", mintedSubmissionId, []);
681
+ rows.resolutions.seed("submission_id", mintedSubmissionId, []);
682
+ rows.childReservations.seed("parent_submission_id", mintedSubmissionId, []);
683
+ rows.childSettlements.seed("parent_submission_id", mintedSubmissionId, []);
684
+ yield* recordProgress("submission", operation);
685
+ return yield* decodeAdmissionResult({
686
+ submissionId: mintedSubmissionId,
687
+ receiptId: mintedReceiptId,
688
+ queueSequence,
689
+ state: "admitted",
690
+ replayed: false
691
+ }).pipe(Effect.mapError(internalFailure(operation)));
692
+ }));
693
+ yield* hitFailpoint("ledger:admit:after", operation);
694
+ return result;
695
+ });
696
+ const markReady = Effect.fn("DoSubmissionLedger.markReady")(function* (request) {
697
+ const operation = "ledger mark ready";
698
+ const validated = yield* Schema.decodeEffect(Schema.toType(MarkReadyRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
699
+ yield* hitFailpoint("ledger:mark-ready:before", operation);
700
+ yield* inWriteTransaction(operation, Effect.gen(function* () {
701
+ const submission = yield* requireSubmission(operation, validated.submissionId);
702
+ if (submission.state !== "admitted") return;
703
+ const now = yield* currentInstant;
704
+ yield* sql`
705
+ UPDATE effect_agent_submissions
706
+ SET state = 'ready', ready_at = ${now.iso}
707
+ WHERE submission_id = ${validated.submissionId}
708
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
709
+ yield* recordProgress("submission", operation);
710
+ yield* retainLifecycle(submission, {
711
+ _tag: "SubmissionReady",
712
+ submissionId: validated.submissionId
713
+ });
714
+ }));
715
+ yield* hitFailpoint("ledger:mark-ready:after", operation);
716
+ });
717
+ const lookup = Effect.fn("DoSubmissionLedger.lookup")(function* (request) {
718
+ const operation = "ledger lookup";
719
+ const validated = yield* Schema.decodeEffect(Schema.toType(SubmissionLookup))(request).pipe(Effect.mapError(internalFailure(operation)));
720
+ if (validated._tag === "SubmissionLookupById") {
721
+ const row = yield* readSubmission(operation, validated.submissionId);
722
+ if (Option.isNone(row)) return Option.none();
723
+ return Option.some(yield* decodeSubmissionSnapshot(operation, row.value));
724
+ }
725
+ const foundRows = yield* byKey(validated, operation);
726
+ const decoded = yield* decodeSubmissionRows(operation, `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`, foundRows);
727
+ 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.");
728
+ if (decoded.length === 0) return Option.none();
729
+ return Option.some(yield* decodeSubmissionSnapshot(operation, decoded[0]));
730
+ });
731
+ const resolveAdmission = Effect.fn("DoSubmissionLedger.resolveAdmission")(function* (request) {
732
+ const operation = "ledger resolve admission";
733
+ const validated = yield* Schema.decodeEffect(Schema.toType(SubmissionLookupByKey))(request).pipe(Effect.mapError(internalFailure(operation)));
734
+ const foundRows = yield* byKey(validated, operation);
735
+ const decoded = yield* decodeSubmissionRows(operation, `${validated.threadId}/${validated.principal}/${validated.idempotencyKey}`, foundRows);
736
+ 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.");
737
+ if (decoded.length === 0) return AdmissionNotAdmitted.make();
738
+ return AdmissionAdmitted.make({ submission: yield* decodeSubmissionSnapshot(operation, decoded[0]) });
739
+ });
740
+ const claim = Effect.fn("DoSubmissionLedger.claim")(function* (request) {
741
+ const operation = "ledger claim";
742
+ const validated = yield* Schema.decodeEffect(Schema.toType(ClaimRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
743
+ const attemptId = `attempt-${yield* mintUuid(operation)}`;
744
+ const ownershipToken = `owner-${yield* mintUuid(operation)}`;
745
+ yield* hitFailpoint("ledger:claim:before", operation);
746
+ const claimed = yield* inWriteTransaction(operation, Effect.gen(function* () {
747
+ const now = yield* currentInstant;
748
+ const lane = yield* laneWork(validated.threadId, operation);
749
+ const ownershipRows = lane === void 0 ? yield* sql`
750
+ SELECT ownership.*
751
+ FROM effect_agent_submission_ownership AS ownership
752
+ JOIN effect_agent_submissions AS submission
753
+ ON submission.submission_id = ownership.submission_id
754
+ WHERE submission.thread_id = ${validated.threadId}
755
+ ORDER BY ownership.lease_expires_at DESC
756
+ LIMIT 1
757
+ `.pipe(Effect.mapError(sqlFailure(operation))) : (yield* Effect.forEach(lane.filter((item) => item.state !== "settled"), (item) => cached(rows.ownership.by("submission_id", item.submission_id), operation))).flat().sort((a, b) => b.lease_expires_at.localeCompare(a.lease_expires_at)).slice(0, 1);
758
+ const ownership = yield* decodeRows(Schema.Array(OwnershipRow), "effect_agent_submission_ownership", validated.threadId, ownershipRows).pipe(Effect.mapError(internalFailure(operation)));
759
+ if (ownership.length > 0) {
760
+ if ((yield* timestampMillis(operation, ownership[0].submission_id)(ownership[0].lease_expires_at)) > now.millis) return Option.none();
761
+ }
762
+ const headRows = lane === void 0 ? yield* sql`
763
+ SELECT ${sql.literal(SUBMISSION_COLUMNS)}
764
+ FROM effect_agent_submissions
765
+ WHERE thread_id = ${validated.threadId}
766
+ AND state <> 'settled'
767
+ AND (
768
+ state <> 'unknown' OR EXISTS (
769
+ SELECT 1 FROM effect_agent_abort_intents
770
+ WHERE submission_id = effect_agent_submissions.submission_id
771
+ )
772
+ )
773
+ ORDER BY queue_sequence ASC
774
+ LIMIT ${validated.handoff === void 0 ? 1 : validated.handoff.deferredSubmissionIds.length + 1}
775
+ `.pipe(Effect.mapError(sqlFailure(operation))) : yield* Effect.gen(function* () {
776
+ const heads = [];
777
+ for (const item of lane) {
778
+ if (item.state === "settled") continue;
779
+ if (item.state === "unknown" && Option.isNone(yield* readAbortIntent(operation, item.submission_id))) continue;
780
+ heads.push(yield* requireSubmission(operation, item.submission_id));
781
+ if (heads.length >= (validated.handoff === void 0 ? 1 : validated.handoff.deferredSubmissionIds.length + 1)) break;
782
+ }
783
+ return heads;
784
+ });
785
+ const heads = yield* decodeSubmissionRows(operation, validated.threadId, headRows);
786
+ if (heads.length === 0) return Option.none();
787
+ let head = heads[0];
788
+ if (validated.handoff !== void 0) {
789
+ const handoff = validated.handoff;
790
+ for (const candidate of heads) {
791
+ if (candidate.submission_id === handoff.submissionId) {
792
+ head = candidate;
793
+ break;
794
+ }
795
+ if (!handoff.deferredSubmissionIds.some((id) => id === candidate.submission_id) || candidate.state !== "input-applied" || Option.isSome(yield* readAbortIntent(operation, candidate.submission_id))) return Option.none();
796
+ }
797
+ if (head.submission_id !== handoff.submissionId || head.state !== "ready" && head.state !== "running" && head.state !== "input-applied") return Option.none();
798
+ }
799
+ if (head.state === "joining" || head.state === "joined" || head.state === "suspended" || head.state === "unknown" && Option.isNone(yield* readAbortIntent(operation, head.submission_id))) return Option.none();
800
+ const threads = yield* journal.getThread(head.thread_id).pipe(Effect.mapError(internalFailure(operation)));
801
+ if (validated.handoff !== void 0 && threads[0]?.producer_epoch !== validated.handoff.producerEpoch) return Option.none();
802
+ let producerEpoch;
803
+ if (threads.length === 0) {
804
+ producerEpoch = 1;
805
+ yield* sql`
806
+ INSERT INTO effect_agent_threads (
807
+ thread_id,
808
+ created_at,
809
+ tail_sequence,
810
+ tail_digest,
811
+ producer_epoch
812
+ ) VALUES (
813
+ ${head.thread_id},
814
+ ${now.iso},
815
+ 0,
816
+ ${EMPTY_TAIL_DIGEST},
817
+ ${producerEpoch}
818
+ )
819
+ RETURNING *`.pipe(rows.threads.write, Effect.mapError(internalFailure(operation)));
820
+ } else {
821
+ producerEpoch = threads[0].producer_epoch + 1;
822
+ yield* sql`
823
+ UPDATE effect_agent_threads
824
+ SET producer_epoch = ${producerEpoch}
825
+ WHERE thread_id = ${head.thread_id}
826
+ RETURNING *`.pipe(rows.threads.write, Effect.mapError(internalFailure(operation)));
827
+ }
828
+ const leaseExpiresAt = new Date(now.millis + config.ownershipLeaseDuration).toISOString();
829
+ yield* sql`
830
+ INSERT INTO effect_agent_submission_ownership (
831
+ submission_id,
832
+ attempt_id,
833
+ ownership_token,
834
+ producer_epoch,
835
+ owner_producer_id,
836
+ lease_expires_at
837
+ ) VALUES (
838
+ ${head.submission_id},
839
+ ${attemptId},
840
+ ${ownershipToken},
841
+ ${producerEpoch},
842
+ ${validated.producerId},
843
+ ${leaseExpiresAt}
844
+ )
845
+ ON CONFLICT (submission_id) DO UPDATE SET
846
+ attempt_id = excluded.attempt_id,
847
+ ownership_token = excluded.ownership_token,
848
+ producer_epoch = excluded.producer_epoch,
849
+ owner_producer_id = excluded.owner_producer_id,
850
+ lease_expires_at = excluded.lease_expires_at
851
+ RETURNING *`.pipe(rows.ownership.write, Effect.mapError(internalFailure(operation)));
852
+ yield* sql`
853
+ INSERT INTO effect_agent_attempts (
854
+ attempt_id,
855
+ submission_id,
856
+ thread_id,
857
+ owner_producer_id,
858
+ producer_epoch,
859
+ claimed_at
860
+ ) VALUES (
861
+ ${attemptId},
862
+ ${head.submission_id},
863
+ ${head.thread_id},
864
+ ${validated.producerId},
865
+ ${producerEpoch},
866
+ ${now.iso}
867
+ )
868
+ `.pipe(Effect.mapError(sqlFailure(operation)));
869
+ if (head.state === "ready") yield* sql`
870
+ UPDATE effect_agent_submissions
871
+ SET state = 'running'
872
+ WHERE submission_id = ${head.submission_id}
873
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
874
+ const inputPayload = yield* parseStoredJsonText(head.input_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", head.submission_id, error.message)));
875
+ return Option.some(yield* decodeClaim({
876
+ submissionId: head.submission_id,
877
+ attemptId,
878
+ ownershipToken,
879
+ producerEpoch,
880
+ leaseExpiresAt,
881
+ inputPayload
882
+ }).pipe(Effect.mapError(internalFailure(operation))));
883
+ }));
884
+ yield* hitFailpoint("ledger:claim:after", operation);
885
+ return claimed;
886
+ });
887
+ const renewOwnership = Effect.fn("DoSubmissionLedger.renewOwnership")(function* (request) {
888
+ const operation = "ledger renew ownership";
889
+ const validated = yield* Schema.decodeEffect(Schema.toType(RenewOwnershipRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
890
+ yield* hitFailpoint("ledger:renew:before", operation);
891
+ const renewal = yield* inWriteTransaction(operation, Effect.gen(function* () {
892
+ const submission = yield* requireSubmission(operation, validated.submissionId);
893
+ yield* requireOwnership(operation, submission, validated.ownershipToken);
894
+ const now = yield* currentInstant;
895
+ const leaseExpiresAt = new Date(now.millis + config.ownershipLeaseDuration).toISOString();
896
+ yield* sql`
897
+ UPDATE effect_agent_submission_ownership
898
+ SET lease_expires_at = ${leaseExpiresAt}
899
+ WHERE submission_id = ${validated.submissionId}
900
+ RETURNING *`.pipe(rows.ownership.write, Effect.mapError(internalFailure(operation)));
901
+ return yield* decodeOwnershipRenewal({
902
+ ownershipToken: validated.ownershipToken,
903
+ leaseExpiresAt
904
+ }).pipe(Effect.mapError(internalFailure(operation)));
905
+ }));
906
+ yield* hitFailpoint("ledger:renew:after", operation);
907
+ return renewal;
908
+ });
909
+ const releaseOwnership = Effect.fnUntraced(function* (request) {
910
+ const operation = "ledger release ownership";
911
+ const validated = yield* Schema.decodeEffect(Schema.toType(ReleaseOwnershipRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
912
+ yield* hitFailpoint("ledger:release:before", operation);
913
+ const current = yield* requireSubmission(operation, validated.submissionId);
914
+ yield* requireOwnership(operation, current, validated.ownershipToken);
915
+ yield* inWriteTransaction(operation, Effect.gen(function* () {
916
+ const submission = yield* requireSubmission(operation, validated.submissionId);
917
+ yield* requireOwnership(operation, submission, validated.ownershipToken);
918
+ yield* sql`
919
+ DELETE FROM effect_agent_submission_ownership
920
+ WHERE submission_id = ${validated.submissionId}
921
+ RETURNING *`.pipe(rows.ownership.remove, Effect.mapError(internalFailure(operation)));
922
+ if (submission.state === "running") yield* sql`
923
+ UPDATE effect_agent_submissions
924
+ SET state = 'ready'
925
+ WHERE submission_id = ${validated.submissionId}
926
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
927
+ }), (error) => error._tag === "OwnershipLost");
928
+ yield* hitFailpoint("ledger:release:after", operation);
929
+ }, withStorageSpan("DoSubmissionLedger.releaseOwnership", (error) => error._tag === "OwnershipLost"));
930
+ const markInputApplied = Effect.fn("DoSubmissionLedger.markInputApplied")(function* (request) {
931
+ const operation = "ledger mark input applied";
932
+ const validated = yield* Schema.decodeEffect(Schema.toType(MarkInputAppliedRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
933
+ yield* hitFailpoint("ledger:mark-input-applied:before", operation);
934
+ yield* inWriteTransaction(operation, Effect.gen(function* () {
935
+ const submission = yield* requireSubmission(operation, validated.submissionId);
936
+ yield* requireOwnership(operation, submission, validated.ownershipToken);
937
+ if (submission.input_applied_record_id !== null) {
938
+ if (submission.input_applied_record_id === validated.recordId && submission.input_applied_sequence === validated.sequence) return;
939
+ return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A different canonical input marker is already recorded for this Submission.");
940
+ }
941
+ yield* sql`
942
+ UPDATE effect_agent_submissions
943
+ SET
944
+ input_applied_record_id = ${validated.recordId},
945
+ input_applied_sequence = ${validated.sequence},
946
+ state = CASE
947
+ WHEN state IN ('admitted', 'ready', 'running') THEN 'input-applied'
948
+ ELSE state
949
+ END
950
+ WHERE submission_id = ${validated.submissionId}
951
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
952
+ }));
953
+ yield* hitFailpoint("ledger:mark-input-applied:after", operation);
954
+ });
955
+ const reserveSettlement = Effect.fn("DoSubmissionLedger.reserveSettlement")(function* (request) {
956
+ const operation = "ledger reserve settlement";
957
+ const validated = yield* Schema.decodeEffect(Schema.toType(SettlementReservation))(request).pipe(Effect.mapError(internalFailure(operation)));
958
+ const recordJson = yield* encodeRecordEnvelopeText(validated.record).pipe(Effect.mapError(internalFailure(operation)));
959
+ yield* journal.checkValueBound(operation, recordJson).pipe(Effect.mapError(internalFailure(operation)));
960
+ yield* hitFailpoint("ledger:reserve-settlement:before", operation);
961
+ const reserved = yield* inWriteTransaction(operation, Effect.gen(function* () {
962
+ const existing = yield* readReservation(operation, validated.submissionId);
963
+ if (Option.isSome(existing)) {
964
+ if (!(existing.value.settlement_id === validated.settlementId && existing.value.outcome === validated.outcome && existing.value.record_digest === validated.recordDigest && existing.value.record_json === recordJson)) return yield* SettlementConflict.make({
965
+ submissionId: validated.submissionId,
966
+ existingOutcome: existing.value.outcome
967
+ });
968
+ const record = yield* decodeRecordEnvelopeText(existing.value.record_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, error.message)));
969
+ return ReservedSettlement.make({
970
+ submissionId: validated.submissionId,
971
+ settlementId: validated.settlementId,
972
+ outcome: validated.outcome,
973
+ record,
974
+ recordDigest: validated.recordDigest,
975
+ replayed: true
976
+ });
977
+ }
978
+ const submission = yield* requireSubmission(operation, validated.submissionId);
979
+ if (submission.state === "settled") {
980
+ if (submission.settled_outcome === null) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A settled Submission carries no terminal outcome.");
981
+ return yield* SettlementConflict.make({
982
+ submissionId: validated.submissionId,
983
+ existingOutcome: submission.settled_outcome
984
+ });
985
+ }
986
+ if (!(submission.state === "joined" && submission.joined_host_submission_id !== null)) {
987
+ let queuedAbortSettlement = false;
988
+ if (validated.outcome === "aborted" && (submission.state === "ready" || submission.state === "terminalizing")) {
989
+ const abortIntent = yield* readAbortIntent(operation, validated.submissionId);
990
+ if (Option.isSome(abortIntent)) {
991
+ const ownership = yield* readOwnership(operation, validated.submissionId);
992
+ queuedAbortSettlement = Option.isNone(ownership);
993
+ }
994
+ }
995
+ if (!queuedAbortSettlement) yield* requireOwnership(operation, submission, validated.ownershipToken);
996
+ }
997
+ const now = yield* currentInstant;
998
+ yield* sql`
999
+ INSERT INTO effect_agent_settlement_reservations (
1000
+ submission_id,
1001
+ settlement_id,
1002
+ outcome,
1003
+ record_id,
1004
+ record_json,
1005
+ record_digest,
1006
+ reserved_at
1007
+ ) VALUES (
1008
+ ${validated.submissionId},
1009
+ ${validated.settlementId},
1010
+ ${validated.outcome},
1011
+ ${validated.record.recordId},
1012
+ ${recordJson},
1013
+ ${validated.recordDigest},
1014
+ ${now.iso}
1015
+ )
1016
+ RETURNING *`.pipe(rows.reservations.write, Effect.mapError(internalFailure(operation)));
1017
+ yield* sql`
1018
+ UPDATE effect_agent_submissions
1019
+ SET state = 'terminalizing'
1020
+ WHERE submission_id = ${validated.submissionId}
1021
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1022
+ return ReservedSettlement.make({
1023
+ submissionId: validated.submissionId,
1024
+ settlementId: validated.settlementId,
1025
+ outcome: validated.outcome,
1026
+ record: validated.record,
1027
+ recordDigest: validated.recordDigest,
1028
+ replayed: false
1029
+ });
1030
+ }));
1031
+ yield* hitFailpoint("ledger:reserve-settlement:after", operation);
1032
+ return reserved;
1033
+ });
1034
+ const validateFinalization = Effect.fn("DoSubmissionLedger.validateFinalization")(function* (validated, reservation) {
1035
+ const operation = "ledger finalize settlement";
1036
+ if (Option.isNone(reservation)) return yield* LedgerError.make({
1037
+ operation,
1038
+ message: `No settlement reservation exists for submission ${validated.submissionId}.`
1039
+ });
1040
+ if (reservation.value.settlement_id !== validated.settlementId) return yield* SettlementConflict.make({
1041
+ submissionId: validated.submissionId,
1042
+ existingOutcome: reservation.value.outcome
1043
+ });
1044
+ const reservationRecord = yield* decodeRecordEnvelopeText(reservation.value.record_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, error.message)));
1045
+ const settlementFailure = settlementFailureFromRecord(reservationRecord);
1046
+ if (reservation.value.outcome === "failed" !== (settlementFailure !== void 0)) return yield* corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, "The reserved outcome and canonical failure diagnostic disagree.");
1047
+ return {
1048
+ reservation: reservation.value,
1049
+ reservationRecord,
1050
+ settlementFailure
1051
+ };
1052
+ });
1053
+ const replayFinalization = Effect.fn("DoSubmissionLedger.replayFinalization")(function* (validated, submission, { reservation, settlementFailure }) {
1054
+ const operation = "ledger finalize settlement";
1055
+ if (reservation.finalized_at === null) return yield* corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, "A settled Submission's reservation carries no finalization timestamp.");
1056
+ return yield* decodeSettlement({
1057
+ submissionId: validated.submissionId,
1058
+ settlementId: validated.settlementId,
1059
+ receiptId: submission.receipt_id,
1060
+ outcome: reservation.outcome,
1061
+ ...settlementFailure === void 0 ? {} : { failure: settlementFailure },
1062
+ settledAt: reservation.finalized_at
1063
+ }).pipe(Effect.mapError(internalFailure(operation)));
1064
+ });
1065
+ const finalizeSettlement = Effect.fn("DoSubmissionLedger.finalizeSettlement")(function* (request) {
1066
+ const operation = "ledger finalize settlement";
1067
+ const validated = yield* Schema.decodeEffect(Schema.toType(SettlementFinalization))(request).pipe(Effect.mapError(internalFailure(operation)));
1068
+ yield* hitFailpoint("ledger:finalize-settlement:before", operation);
1069
+ const replay = yield* state.read(Effect.gen(function* () {
1070
+ const submission = yield* readSubmission(operation, validated.submissionId);
1071
+ if (Option.isNone(submission) || submission.value.state !== "settled") return Option.none();
1072
+ const finalization = yield* validateFinalization(validated, yield* readReservation(operation, validated.submissionId));
1073
+ return Option.some(yield* replayFinalization(validated, submission.value, finalization));
1074
+ }));
1075
+ if (Option.isSome(replay)) {
1076
+ yield* hitFailpoint("ledger:finalize-settlement:after", operation);
1077
+ return replay.value;
1078
+ }
1079
+ const settlement = yield* inWriteTransaction(operation, Effect.gen(function* () {
1080
+ const state = yield* validateFinalization(validated, yield* readReservation(operation, validated.submissionId));
1081
+ const { reservation, reservationRecord, settlementFailure } = state;
1082
+ const submission = yield* requireSubmission(operation, validated.submissionId);
1083
+ if (submission.state === "settled") return yield* replayFinalization(validated, submission, state);
1084
+ const now = yield* currentInstant;
1085
+ const terminal = submission.worker_admission_json === null ? void 0 : workerTerminalFromRecord(yield* decodeSubmissionSnapshot(operation, submission), reservationRecord);
1086
+ let sealedTerminal;
1087
+ if (terminal !== void 0) {
1088
+ if ((terminal === "completed" ? yield* sql`SELECT submission_id FROM effect_agent_submissions
1089
+ WHERE thread_id = ${submission.thread_id} AND queue_sequence > ${submission.queue_sequence}
1090
+ AND queue_sequence = (SELECT MAX(queue_sequence) FROM effect_agent_submissions WHERE thread_id = ${submission.thread_id})
1091
+ AND (joined_host_submission_id IS NULL OR joined_host_submission_id <> ${submission.submission_id}
1092
+ OR input_applied_record_id IS NULL) LIMIT 1`.pipe(Effect.mapError(sqlFailure(operation))) : []).length === 0) {
1093
+ const sealed = yield* sql`INSERT OR IGNORE INTO effect_agent_worker_stops (thread_id, terminal)
1094
+ VALUES (${submission.thread_id}, ${terminal}) RETURNING *`.pipe(rows.workerStops.write, Effect.mapError(internalFailure(operation)));
1095
+ yield* sql`INSERT OR IGNORE INTO effect_agent_abort_intents (submission_id, author, reason, requested_at)
1096
+ SELECT submission_id, ${submission.principal}, ${`Worker assignment ${terminal}`}, ${now.iso}
1097
+ FROM effect_agent_submissions WHERE thread_id = ${submission.thread_id} AND state <> 'settled'
1098
+ AND submission_id <> ${submission.submission_id}
1099
+ AND (joined_host_submission_id IS NULL OR joined_host_submission_id <> ${submission.submission_id}
1100
+ OR input_applied_record_id IS NULL) RETURNING *`.pipe(rows.aborts.write, Effect.mapError(internalFailure(operation)));
1101
+ if (sealed.length > 0) sealedTerminal = terminal;
1102
+ }
1103
+ }
1104
+ yield* sql`
1105
+ UPDATE effect_agent_submissions
1106
+ SET state = 'settled', settled_outcome = ${reservation.outcome}
1107
+ WHERE submission_id = ${validated.submissionId}
1108
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1109
+ yield* sql`
1110
+ UPDATE effect_agent_settlement_reservations
1111
+ SET finalized_at = ${now.iso}
1112
+ WHERE submission_id = ${validated.submissionId}
1113
+ RETURNING *`.pipe(rows.reservations.write, Effect.mapError(internalFailure(operation)));
1114
+ yield* sql`
1115
+ DELETE FROM effect_agent_submission_ownership
1116
+ WHERE submission_id = ${validated.submissionId}
1117
+ RETURNING *`.pipe(rows.ownership.remove, Effect.mapError(internalFailure(operation)));
1118
+ yield* recordProgress("submission", operation);
1119
+ if (sealedTerminal !== void 0) yield* retainWorkerSeal(submission.thread_id, sealedTerminal);
1120
+ return yield* decodeSettlement({
1121
+ submissionId: validated.submissionId,
1122
+ settlementId: validated.settlementId,
1123
+ receiptId: submission.receipt_id,
1124
+ outcome: reservation.outcome,
1125
+ ...settlementFailure === void 0 ? {} : { failure: settlementFailure },
1126
+ settledAt: now.iso
1127
+ }).pipe(Effect.mapError(internalFailure(operation)));
1128
+ }));
1129
+ yield* hitFailpoint("ledger:finalize-settlement:after", operation);
1130
+ return settlement;
1131
+ });
1132
+ const inspectWorker = Effect.fn("DoSubmissionLedger.inspectWorker")(function* (threadId) {
1133
+ const operation = "inspect worker";
1134
+ return yield* state.transaction(Effect.gen(function* () {
1135
+ const read = Effect.fnUntraced(function* (active) {
1136
+ const rows = yield* sql`SELECT ${sql.literal(SUBMISSION_COLUMNS)} FROM effect_agent_submissions
1137
+ WHERE thread_id = ${threadId} ${active ? sql`AND state <> 'settled'` : sql``}
1138
+ ORDER BY queue_sequence ${active ? sql`ASC` : sql`DESC`} LIMIT 1`.pipe(Effect.mapError(sqlFailure(operation)));
1139
+ const decoded = yield* decodeSubmissionRows(operation, threadId, rows);
1140
+ return decoded[0] === void 0 ? null : yield* decodeSubmissionSnapshot(operation, decoded[0]);
1141
+ });
1142
+ const latest = yield* read(false);
1143
+ const active = yield* read(true);
1144
+ const stops = yield* cached(rows.workerStops.by("thread_id", threadId), operation);
1145
+ return yield* Schema.decodeEffect(Schema.toType(WorkerLedgerState))({
1146
+ latest,
1147
+ active,
1148
+ stopped: stops.length > 0,
1149
+ ...stops[0] === void 0 || stops[0].terminal === null ? {} : { terminal: stops[0].terminal }
1150
+ }).pipe(Effect.mapError(internalFailure(operation)));
1151
+ })).pipe(Effect.provideService(SqlClientService.SqlClient, sql), Effect.catchTag("SqlError", (cause) => sqlFailure(operation)(cause)));
1152
+ });
1153
+ const stopWorker = Effect.fn("DoSubmissionLedger.stopWorker")(function* (request) {
1154
+ const operation = "ledger stop worker";
1155
+ const validated = yield* Schema.decodeEffect(WorkerStopCommand)(request).pipe(Effect.mapError(internalFailure(operation)));
1156
+ return yield* inWriteTransaction(operation, Effect.gen(function* () {
1157
+ const now = yield* currentInstant;
1158
+ const sealed = yield* sql`INSERT OR IGNORE INTO effect_agent_worker_stops (thread_id) VALUES (${validated.threadId}) RETURNING *`.pipe(rows.workerStops.write, Effect.mapError(internalFailure(operation)));
1159
+ const aborted = yield* sql`INSERT OR IGNORE INTO effect_agent_abort_intents (submission_id, author, reason, requested_at)
1160
+ SELECT submission_id, ${validated.author}, 'Worker owner stopped the worker', ${now.iso}
1161
+ FROM effect_agent_submissions WHERE thread_id = ${validated.threadId} AND state <> 'settled' RETURNING *`.pipe(rows.aborts.write, Effect.mapError(internalFailure(operation)));
1162
+ const owners = yield* sql`SELECT o.submission_id FROM effect_agent_submission_ownership o
1163
+ JOIN effect_agent_submissions s ON s.submission_id = o.submission_id
1164
+ WHERE s.thread_id = ${validated.threadId} AND s.state <> 'settled'`.pipe(Effect.mapError(sqlFailure(operation)));
1165
+ if (sealed.length > 0 || aborted.length > 0) yield* recordProgress("control", operation);
1166
+ if (sealed.length > 0) yield* retainWorkerSeal(validated.threadId, null);
1167
+ return owners.length;
1168
+ }));
1169
+ });
1170
+ const requestAbort = Effect.fn("DoSubmissionLedger.requestAbort")(function* (request) {
1171
+ const operation = "ledger request abort";
1172
+ const validated = yield* Schema.decodeEffect(Schema.toType(AbortCommand))(request).pipe(Effect.mapError(internalFailure(operation)));
1173
+ yield* hitFailpoint("ledger:request-abort:before", operation);
1174
+ const intent = yield* inWriteTransaction(operation, Effect.gen(function* () {
1175
+ const submission = yield* requireSubmission(operation, validated.submissionId);
1176
+ if (submission.state === "settled") {
1177
+ if (submission.settled_outcome === null) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A settled Submission carries no terminal outcome.");
1178
+ return yield* SettlementConflict.make({
1179
+ submissionId: validated.submissionId,
1180
+ existingOutcome: submission.settled_outcome
1181
+ });
1182
+ }
1183
+ if (submission.state === "joined") {
1184
+ if (submission.joined_host_submission_id === null) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A joined Submission carries no host linkage.");
1185
+ const hostSubmissionId = yield* decodeSubmissionId(submission.joined_host_submission_id).pipe(Effect.mapError(internalFailure(operation)));
1186
+ return yield* JoinedToHost.make({
1187
+ submissionId: validated.submissionId,
1188
+ hostSubmissionId
1189
+ });
1190
+ }
1191
+ const existing = yield* readAbortIntent(operation, validated.submissionId);
1192
+ if (Option.isSome(existing)) return yield* abortIntentFromRow(operation, submission, validated.submissionId, existing.value);
1193
+ const now = yield* currentInstant;
1194
+ yield* sql`
1195
+ INSERT INTO effect_agent_abort_intents (
1196
+ submission_id,
1197
+ author,
1198
+ reason,
1199
+ requested_at
1200
+ ) VALUES (
1201
+ ${validated.submissionId},
1202
+ ${validated.author},
1203
+ ${validated.reason},
1204
+ ${now.iso}
1205
+ )
1206
+ RETURNING *`.pipe(rows.aborts.write, Effect.mapError(internalFailure(operation)));
1207
+ yield* recordProgress("control", operation);
1208
+ const canonicalRecordId = yield* canonicalAbortRecordId(operation, submission.thread_id, validated.submissionId);
1209
+ const intent = yield* decodeAbortIntent({
1210
+ submissionId: validated.submissionId,
1211
+ author: validated.author,
1212
+ reason: validated.reason,
1213
+ requestedAt: now.iso,
1214
+ ...canonicalRecordId === void 0 ? {} : { canonicalRecordId }
1215
+ }).pipe(Effect.mapError(internalFailure(operation)));
1216
+ yield* retainLifecycle(submission, {
1217
+ _tag: "AbortIntentRecorded",
1218
+ intent
1219
+ });
1220
+ return intent;
1221
+ }));
1222
+ yield* hitFailpoint("ledger:request-abort:after", operation);
1223
+ return intent;
1224
+ });
1225
+ const claimJoining = Effect.fn("DoSubmissionLedger.claimJoining")(function* (request) {
1226
+ const operation = "ledger claim joining";
1227
+ const validated = yield* Schema.decodeEffect(Schema.toType(ClaimJoiningRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1228
+ yield* hitFailpoint("ledger:claim-joining:before", operation);
1229
+ const claims = yield* inWriteTransaction(operation, Effect.gen(function* () {
1230
+ const host = yield* requireSubmission(operation, validated.hostSubmissionId);
1231
+ if (host.thread_id !== validated.threadId) return yield* LedgerError.make({
1232
+ operation,
1233
+ message: `Host submission ${validated.hostSubmissionId} does not belong to thread ${validated.threadId}.`
1234
+ });
1235
+ yield* requireOwnership(operation, host, validated.ownershipToken);
1236
+ if ((yield* cached(rows.workerStops.by("thread_id", validated.threadId), operation)).length > 0) return [];
1237
+ const lane = yield* laneWork(validated.threadId, operation);
1238
+ const laterRows = lane === void 0 ? yield* sql`
1239
+ SELECT ${sql.literal(SUBMISSION_COLUMNS)}
1240
+ FROM effect_agent_submissions
1241
+ WHERE thread_id = ${validated.threadId}
1242
+ AND queue_sequence > ${host.queue_sequence}
1243
+ ORDER BY queue_sequence ASC
1244
+ `.pipe(Effect.mapError(sqlFailure(operation))) : yield* Effect.forEach(lane.filter((item) => item.queue_sequence > host.queue_sequence), (item) => requireSubmission(operation, item.submission_id));
1245
+ const later = yield* decodeSubmissionRows(operation, validated.threadId, laterRows);
1246
+ const claimed = [];
1247
+ for (const row of later) {
1248
+ if (claimed.length >= validated.maxCount) break;
1249
+ if ((row.state === "joining" || row.state === "joined") && row.joined_host_submission_id === validated.hostSubmissionId) continue;
1250
+ if (row.state === "settled" && row.settled_outcome === "aborted") continue;
1251
+ if (row.state !== "ready") break;
1252
+ yield* sql`
1253
+ UPDATE effect_agent_submissions
1254
+ SET state = 'joining', joined_host_submission_id = ${validated.hostSubmissionId}
1255
+ WHERE submission_id = ${row.submission_id}
1256
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1257
+ const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", row.submission_id, error.message)));
1258
+ claimed.push(yield* decodeJoiningClaim({
1259
+ submissionId: row.submission_id,
1260
+ queueSequence: row.queue_sequence,
1261
+ inputPayload
1262
+ }).pipe(Effect.mapError(internalFailure(operation))));
1263
+ }
1264
+ return claimed;
1265
+ }));
1266
+ yield* hitFailpoint("ledger:claim-joining:after", operation);
1267
+ return claims;
1268
+ });
1269
+ const markJoined = Effect.fn("DoSubmissionLedger.markJoined")(function* (request) {
1270
+ const operation = "ledger mark joined";
1271
+ const validated = yield* Schema.decodeEffect(Schema.toType(MarkJoinedRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1272
+ yield* hitFailpoint("ledger:mark-joined:before", operation);
1273
+ yield* inWriteTransaction(operation, Effect.gen(function* () {
1274
+ const submission = yield* requireSubmission(operation, validated.submissionId);
1275
+ if (submission.joined_host_submission_id === null) return yield* LedgerError.make({
1276
+ operation,
1277
+ message: `Submission ${validated.submissionId} was never claimed for joining.`
1278
+ });
1279
+ const host = yield* requireSubmission(operation, submission.joined_host_submission_id);
1280
+ yield* requireOwnership(operation, host, validated.ownershipToken);
1281
+ if (submission.input_applied_record_id !== null) {
1282
+ if (submission.input_applied_record_id === validated.recordId && submission.input_applied_sequence === validated.sequence) return;
1283
+ return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A different join marker is already recorded for this Submission.");
1284
+ }
1285
+ if (submission.state !== "joining" && submission.state !== "joined") return yield* LedgerError.make({
1286
+ operation,
1287
+ message: `Cannot mark submission ${validated.submissionId} joined from state ${submission.state}.`
1288
+ });
1289
+ yield* sql`
1290
+ UPDATE effect_agent_submissions
1291
+ SET
1292
+ input_applied_record_id = ${validated.recordId},
1293
+ input_applied_sequence = ${validated.sequence},
1294
+ state = 'joined'
1295
+ WHERE submission_id = ${validated.submissionId}
1296
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1297
+ }));
1298
+ yield* hitFailpoint("ledger:mark-joined:after", operation);
1299
+ });
1300
+ const revertJoining = Effect.fn("DoSubmissionLedger.revertJoining")(function* (request) {
1301
+ const operation = "ledger revert joining";
1302
+ const validated = yield* Schema.decodeEffect(Schema.toType(RevertJoiningRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1303
+ yield* hitFailpoint("ledger:revert-joining:before", operation);
1304
+ yield* inWriteTransaction(operation, Effect.gen(function* () {
1305
+ if ((yield* requireSubmission(operation, validated.submissionId)).state !== "joining") return;
1306
+ yield* sql`
1307
+ UPDATE effect_agent_submissions
1308
+ SET state = 'ready', joined_host_submission_id = NULL
1309
+ WHERE submission_id = ${validated.submissionId}
1310
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1311
+ }));
1312
+ yield* hitFailpoint("ledger:revert-joining:after", operation);
1313
+ });
1314
+ const suspend = Effect.fn("DoSubmissionLedger.suspend")(function* (request) {
1315
+ const operation = "ledger suspend";
1316
+ const validated = yield* Schema.decodeEffect(Schema.toType(SuspendRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1317
+ const reasonJson = yield* encodeSuspensionReasonText(validated.reason).pipe(Effect.mapError(internalFailure(operation)));
1318
+ yield* hitFailpoint("ledger:suspend:before", operation);
1319
+ const outcome = yield* inWriteTransaction(operation, Effect.gen(function* () {
1320
+ const submission = yield* requireSubmission(operation, validated.submissionId);
1321
+ if (submission.state === "settled") {
1322
+ if (submission.settled_outcome === null) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A settled Submission carries no terminal outcome.");
1323
+ return yield* SettlementConflict.make({
1324
+ submissionId: validated.submissionId,
1325
+ existingOutcome: submission.settled_outcome
1326
+ });
1327
+ }
1328
+ const reservation = yield* readReservation(operation, validated.submissionId);
1329
+ if (Option.isSome(reservation)) return yield* SettlementConflict.make({
1330
+ submissionId: validated.submissionId,
1331
+ existingOutcome: reservation.value.outcome
1332
+ });
1333
+ yield* requireOwnership(operation, submission, validated.ownershipToken);
1334
+ if (validated.reason._tag === "ApprovalPending") {
1335
+ const decisions = yield* readApprovalDecisions(operation, validated.submissionId);
1336
+ const decided = new Set(decisions.map((row) => row.tool_call_id));
1337
+ if (validated.reason.toolCallIds.every((toolCallId) => decided.has(toolCallId))) return RESUME_IMMEDIATELY;
1338
+ } else {
1339
+ const markers = yield* readChildSettlementMarkers(operation, validated.submissionId);
1340
+ const markerChildren = new Set(markers.map((row) => row.child_submission_id));
1341
+ let allSettled = true;
1342
+ for (const child of validated.reason.children) if (!(yield* childProvablySettled(operation, markerChildren, child.childSubmissionId))) {
1343
+ allSettled = false;
1344
+ break;
1345
+ }
1346
+ if (allSettled) return RESUME_IMMEDIATELY;
1347
+ }
1348
+ const now = yield* currentInstant;
1349
+ yield* sql`
1350
+ UPDATE effect_agent_submissions
1351
+ SET
1352
+ state = 'suspended',
1353
+ suspended_reason_json = ${reasonJson},
1354
+ suspended_at = ${now.iso}
1355
+ WHERE submission_id = ${validated.submissionId}
1356
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1357
+ yield* sql`
1358
+ DELETE FROM effect_agent_submission_ownership
1359
+ WHERE submission_id = ${validated.submissionId}
1360
+ RETURNING *`.pipe(rows.ownership.remove, Effect.mapError(internalFailure(operation)));
1361
+ yield* retainLifecycle(submission, {
1362
+ _tag: "SubmissionSuspended",
1363
+ submissionId: validated.submissionId,
1364
+ reason: validated.reason
1365
+ });
1366
+ return SUSPENDED;
1367
+ }));
1368
+ yield* hitFailpoint("ledger:suspend:after", operation);
1369
+ return outcome;
1370
+ });
1371
+ /**
1372
+ * Once every pending call of a recorded ApprovalPending suspension has a decision intent,
1373
+ * the lane wakes: suspended → input-applied, suspension cleared (plan §2.6). A
1374
+ * WaitingForChild suspension wakes only through recordChildSettled. Runs inside the caller's
1375
+ * write transaction.
1376
+ */
1377
+ const wakeSuspendedIfCovered = Effect.fn("DoSubmissionLedger.wakeSuspendedIfCovered")(function* (operation, submission) {
1378
+ if (submission.state !== "suspended" || submission.suspended_reason_json === null) return;
1379
+ const reason = yield* Schema.decodeEffect(Schema.fromJsonString(SuspensionReason))(submission.suspended_reason_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", submission.submission_id, error.message)));
1380
+ if (reason._tag !== "ApprovalPending") return;
1381
+ const decisions = yield* readApprovalDecisions(operation, submission.submission_id);
1382
+ const decided = new Set(decisions.map((row) => row.tool_call_id));
1383
+ if (!reason.toolCallIds.every((toolCallId) => decided.has(toolCallId))) return;
1384
+ yield* sql`
1385
+ UPDATE effect_agent_submissions
1386
+ SET
1387
+ state = 'input-applied',
1388
+ suspended_reason_json = NULL,
1389
+ suspended_at = NULL
1390
+ WHERE submission_id = ${submission.submission_id}
1391
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1392
+ yield* retainLifecycle(submission, {
1393
+ _tag: "SubmissionResumed",
1394
+ submissionId: yield* decodeSubmissionId(submission.submission_id).pipe(Effect.mapError(internalFailure(operation)))
1395
+ });
1396
+ });
1397
+ const recordApprovalDecision = Effect.fn("DoSubmissionLedger.recordApprovalDecision")(function* (command) {
1398
+ const operation = "ledger record approval decision";
1399
+ const validated = yield* Schema.decodeEffect(Schema.toType(ApprovalDecisionCommand))(command).pipe(Effect.mapError(internalFailure(operation)));
1400
+ yield* hitFailpoint("ledger:approval-decision:before", operation);
1401
+ const intent = yield* inWriteTransaction(operation, Effect.gen(function* () {
1402
+ const submission = yield* requireSubmission(operation, validated.submissionId);
1403
+ if (submission.state === "settled") {
1404
+ if (submission.settled_outcome === null) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A settled Submission carries no terminal outcome.");
1405
+ return yield* SettlementConflict.make({
1406
+ submissionId: validated.submissionId,
1407
+ existingOutcome: submission.settled_outcome
1408
+ });
1409
+ }
1410
+ const existing = (yield* readApprovalDecisions(operation, validated.submissionId)).find((row) => row.tool_call_id === validated.toolCallId);
1411
+ if (existing !== void 0) {
1412
+ if (existing.decision !== validated.decision) return yield* ApprovalConflict.make({
1413
+ submissionId: validated.submissionId,
1414
+ toolCallId: validated.toolCallId,
1415
+ existingDecision: existing.decision
1416
+ });
1417
+ return yield* approvalIntentFromRow(operation, existing);
1418
+ }
1419
+ const now = yield* currentInstant;
1420
+ yield* sql`
1421
+ INSERT INTO effect_agent_approval_decisions (
1422
+ submission_id,
1423
+ tool_call_id,
1424
+ decision,
1425
+ resolver,
1426
+ reason,
1427
+ decided_at
1428
+ ) VALUES (
1429
+ ${validated.submissionId},
1430
+ ${validated.toolCallId},
1431
+ ${validated.decision},
1432
+ ${validated.resolver},
1433
+ ${validated.reason},
1434
+ ${now.iso}
1435
+ )
1436
+ RETURNING *`.pipe(rows.approvals.write, Effect.mapError(internalFailure(operation)));
1437
+ yield* recordProgress("control", operation);
1438
+ yield* wakeSuspendedIfCovered(operation, submission);
1439
+ return yield* decodeApprovalDecisionIntent({
1440
+ submissionId: validated.submissionId,
1441
+ toolCallId: validated.toolCallId,
1442
+ decision: validated.decision,
1443
+ resolver: validated.resolver,
1444
+ reason: validated.reason,
1445
+ decidedAt: now.iso
1446
+ }).pipe(Effect.mapError(internalFailure(operation)));
1447
+ }));
1448
+ yield* hitFailpoint("ledger:approval-decision:after", operation);
1449
+ return intent;
1450
+ });
1451
+ const markUnknown = Effect.fn("DoSubmissionLedger.markUnknown")(function* (request) {
1452
+ const operation = "ledger mark unknown";
1453
+ const validated = yield* Schema.decodeEffect(Schema.toType(MarkUnknownRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1454
+ yield* hitFailpoint("ledger:mark-unknown:before", operation);
1455
+ yield* inWriteTransaction(operation, Effect.gen(function* () {
1456
+ const submission = yield* requireSubmission(operation, validated.submissionId);
1457
+ if (submission.state === "settled") {
1458
+ if (submission.settled_outcome === null) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A settled Submission carries no terminal outcome.");
1459
+ return yield* SettlementConflict.make({
1460
+ submissionId: validated.submissionId,
1461
+ existingOutcome: submission.settled_outcome
1462
+ });
1463
+ }
1464
+ const reservation = yield* readReservation(operation, validated.submissionId);
1465
+ if (Option.isSome(reservation)) return yield* SettlementConflict.make({
1466
+ submissionId: validated.submissionId,
1467
+ existingOutcome: reservation.value.outcome
1468
+ });
1469
+ const existingIds = yield* storedUnknownToolCallIds(operation, submission);
1470
+ const known = new Set(existingIds);
1471
+ const merged = [...existingIds, ...validated.toolCallIds.filter((toolCallId) => !known.has(toolCallId))];
1472
+ const idsJson = yield* encodeToolCallIdsText(merged).pipe(Effect.mapError(internalFailure(operation)));
1473
+ yield* sql`
1474
+ UPDATE effect_agent_submissions
1475
+ SET
1476
+ state = 'unknown',
1477
+ unknown_reason = ${submission.unknown_reason ?? validated.reason},
1478
+ unknown_tool_call_ids_json = ${idsJson}
1479
+ WHERE submission_id = ${validated.submissionId}
1480
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1481
+ if (submission.state !== "unknown") yield* retainLifecycle(submission, {
1482
+ _tag: "SubmissionUnknown",
1483
+ submissionId: validated.submissionId
1484
+ });
1485
+ }));
1486
+ yield* hitFailpoint("ledger:mark-unknown:after", operation);
1487
+ });
1488
+ const recordUnknownResolution = Effect.fn("DoSubmissionLedger.recordUnknownResolution")(function* (command) {
1489
+ const operation = "ledger record unknown resolution";
1490
+ const validated = yield* Schema.decodeEffect(Schema.toType(UnknownResolutionCommand))(command).pipe(Effect.mapError(internalFailure(operation)));
1491
+ const resolutionJson = yield* encodeUnknownResolutionText(validated.resolution).pipe(Effect.mapError(internalFailure(operation)));
1492
+ yield* hitFailpoint("ledger:unknown-resolution:before", operation);
1493
+ const intent = yield* inWriteTransaction(operation, Effect.gen(function* () {
1494
+ const submission = yield* requireSubmission(operation, validated.submissionId);
1495
+ if (submission.state === "settled") {
1496
+ if (submission.settled_outcome === null) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A settled Submission carries no terminal outcome.");
1497
+ return yield* SettlementConflict.make({
1498
+ submissionId: validated.submissionId,
1499
+ existingOutcome: submission.settled_outcome
1500
+ });
1501
+ }
1502
+ const existing = (yield* readUnknownResolutions(operation, validated.submissionId)).find((row) => row.tool_call_id === validated.toolCallId);
1503
+ const existingIntent = existing === void 0 ? void 0 : yield* unknownResolutionIntentFromRow(operation, existing);
1504
+ if (existingIntent !== void 0 && !equivalentUnknownResolution(existingIntent.resolution, validated.resolution)) return yield* UnknownResolutionConflict.make({
1505
+ submissionId: validated.submissionId,
1506
+ toolCallId: validated.toolCallId
1507
+ });
1508
+ let resolved;
1509
+ if (existingIntent !== void 0) resolved = existingIntent;
1510
+ else {
1511
+ const now = yield* currentInstant;
1512
+ yield* sql`
1513
+ INSERT INTO effect_agent_unknown_resolutions (
1514
+ submission_id,
1515
+ tool_call_id,
1516
+ author,
1517
+ reason,
1518
+ resolution_json,
1519
+ resolved_at
1520
+ ) VALUES (
1521
+ ${validated.submissionId},
1522
+ ${validated.toolCallId},
1523
+ ${validated.author},
1524
+ ${validated.reason},
1525
+ ${resolutionJson},
1526
+ ${now.iso}
1527
+ )
1528
+ RETURNING *`.pipe(rows.resolutions.write, Effect.mapError(internalFailure(operation)));
1529
+ yield* recordProgress("control", operation);
1530
+ const resolution = yield* parseStoredJsonText(resolutionJson).pipe(Effect.mapError(internalFailure(operation)));
1531
+ resolved = yield* decodeUnknownResolutionIntent({
1532
+ submissionId: validated.submissionId,
1533
+ toolCallId: validated.toolCallId,
1534
+ author: validated.author,
1535
+ reason: validated.reason,
1536
+ resolution,
1537
+ resolvedAt: now.iso
1538
+ }).pipe(Effect.mapError(internalFailure(operation)));
1539
+ }
1540
+ if (submission.state === "unknown" && submission.unknown_tool_call_ids_json !== null) {
1541
+ const markedIds = yield* storedUnknownToolCallIds(operation, submission);
1542
+ const covering = yield* readUnknownResolutions(operation, validated.submissionId);
1543
+ const coveredIds = new Set(covering.map((row) => row.tool_call_id));
1544
+ if (markedIds.every((toolCallId) => coveredIds.has(toolCallId))) {
1545
+ yield* sql`
1546
+ UPDATE effect_agent_submissions
1547
+ SET
1548
+ state = 'input-applied',
1549
+ unknown_reason = NULL,
1550
+ unknown_tool_call_ids_json = NULL
1551
+ WHERE submission_id = ${validated.submissionId}
1552
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1553
+ yield* retainLifecycle(submission, {
1554
+ _tag: "SubmissionResumed",
1555
+ submissionId: validated.submissionId
1556
+ });
1557
+ }
1558
+ }
1559
+ return resolved;
1560
+ }));
1561
+ yield* hitFailpoint("ledger:unknown-resolution:after", operation);
1562
+ return intent;
1563
+ });
1564
+ const recordChildSettled = Effect.fn("DoSubmissionLedger.recordChildSettled")(function* (request) {
1565
+ const operation = "ledger record child settled";
1566
+ const validated = yield* Schema.decodeEffect(Schema.toType(ChildSettledNotification))(request).pipe(Effect.mapError(internalFailure(operation)));
1567
+ yield* hitFailpoint("ledger:child-settled:before", operation);
1568
+ const outcome = yield* inWriteTransaction(operation, Effect.gen(function* () {
1569
+ const parent = yield* requireSubmission(operation, validated.parentSubmissionId);
1570
+ const child = yield* readSubmission(operation, validated.childSubmissionId);
1571
+ const childReservation = yield* readReservation(operation, validated.childSubmissionId);
1572
+ if (Option.isSome(child) && child.value.state !== "settled" && !(child.value.state === "terminalizing" && Option.isSome(childReservation))) return yield* LedgerError.make({
1573
+ operation,
1574
+ message: `Child submission ${validated.childSubmissionId} has no recorded settlement.`
1575
+ });
1576
+ const now = yield* currentInstant;
1577
+ if ((yield* sql`
1578
+ INSERT INTO effect_agent_child_settlements (
1579
+ parent_submission_id,
1580
+ child_submission_id,
1581
+ child_outcome,
1582
+ recorded_at
1583
+ ) VALUES (
1584
+ ${validated.parentSubmissionId},
1585
+ ${validated.childSubmissionId},
1586
+ ${Option.isSome(child) && child.value.state === "settled" ? child.value.settled_outcome : Option.isSome(childReservation) ? childReservation.value.outcome : null},
1587
+ ${now.iso}
1588
+ )
1589
+ ON CONFLICT (parent_submission_id, child_submission_id) DO NOTHING
1590
+ RETURNING *`.pipe(rows.childSettlements.write, Effect.mapError(internalFailure(operation)))).length > 0) yield* recordProgress("control", operation);
1591
+ if (parent.state !== "suspended" || parent.suspended_reason_json === null) return NOT_WAITING;
1592
+ const reason = yield* Schema.decodeEffect(Schema.fromJsonString(SuspensionReason))(parent.suspended_reason_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", parent.submission_id, error.message)));
1593
+ if (reason._tag !== "WaitingForChild") return NOT_WAITING;
1594
+ if (!reason.children.some((entry) => entry.childSubmissionId === validated.childSubmissionId)) return NOT_WAITING;
1595
+ const markers = yield* readChildSettlementMarkers(operation, validated.parentSubmissionId);
1596
+ const markerChildren = new Set(markers.map((row) => row.child_submission_id));
1597
+ for (const entry of reason.children) if (!(yield* childProvablySettled(operation, markerChildren, entry.childSubmissionId))) return STILL_WAITING;
1598
+ yield* sql`
1599
+ UPDATE effect_agent_submissions
1600
+ SET
1601
+ state = 'input-applied',
1602
+ suspended_reason_json = NULL,
1603
+ suspended_at = NULL
1604
+ WHERE submission_id = ${validated.parentSubmissionId}
1605
+ RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1606
+ yield* retainLifecycle(parent, {
1607
+ _tag: "SubmissionResumed",
1608
+ submissionId: validated.parentSubmissionId
1609
+ });
1610
+ return WOKEN;
1611
+ }));
1612
+ yield* hitFailpoint("ledger:child-settled:after", operation);
1613
+ return outcome;
1614
+ });
1615
+ const reserveChildBudget = Effect.fn("DoSubmissionLedger.reserveChildBudget")(function* (request) {
1616
+ const operation = "ledger reserve child budget";
1617
+ const validated = yield* Schema.decodeEffect(Schema.toType(ChildBudgetReservationRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1618
+ const allocationJson = yield* encodePersistedJsonText(validated.allocation).pipe(Effect.mapError(internalFailure(operation)));
1619
+ yield* hitFailpoint("ledger:child-reservation:before", operation);
1620
+ const reserved = yield* inWriteTransaction(operation, Effect.gen(function* () {
1621
+ const existing = yield* readChildReservation(operation, validated.reservationId);
1622
+ if (Option.isSome(existing)) {
1623
+ const existingSnapshot = yield* childReservationSnapshotFromRow(operation, existing.value);
1624
+ if (!(existing.value.parent_submission_id === validated.parentSubmissionId && existing.value.parent_tool_call_id === validated.parentToolCallId && existing.value.allocation_digest === validated.allocationDigest && equivalentPersistedJson(existingSnapshot.allocation, validated.allocation))) return yield* ChildReservationConflict.make({
1625
+ reservationId: validated.reservationId,
1626
+ status: existing.value.status,
1627
+ message: "A reservation with this identity exists with a different parent Tool Call or allocation."
1628
+ });
1629
+ return ReservedChildBudget.make({
1630
+ reservation: existingSnapshot,
1631
+ replayed: true
1632
+ });
1633
+ }
1634
+ const collision = yield* readChildReservationForCall(operation, validated.parentSubmissionId, validated.parentToolCallId);
1635
+ if (Option.isSome(collision)) return yield* ChildReservationConflict.make({
1636
+ reservationId: validated.reservationId,
1637
+ status: collision.value.status,
1638
+ message: `Parent Tool Call ${validated.parentToolCallId} already owns reservation ${collision.value.reservation_id}.`
1639
+ });
1640
+ const parent = yield* requireSubmission(operation, validated.parentSubmissionId);
1641
+ yield* requireOwnership(operation, parent, validated.ownershipToken);
1642
+ const now = yield* currentInstant;
1643
+ yield* sql`
1644
+ INSERT INTO effect_agent_child_reservations (
1645
+ reservation_id,
1646
+ parent_submission_id,
1647
+ parent_tool_call_id,
1648
+ status,
1649
+ allocation_json,
1650
+ allocation_digest,
1651
+ reserved_at
1652
+ ) VALUES (
1653
+ ${validated.reservationId},
1654
+ ${validated.parentSubmissionId},
1655
+ ${validated.parentToolCallId},
1656
+ 'reserved',
1657
+ ${allocationJson},
1658
+ ${validated.allocationDigest},
1659
+ ${now.iso}
1660
+ )
1661
+ RETURNING *`.pipe(rows.childReservations.write, Effect.mapError(internalFailure(operation)));
1662
+ const inserted = yield* readChildReservation(operation, validated.reservationId);
1663
+ if (Option.isNone(inserted)) return yield* corruptionFailure(operation, "effect_agent_child_reservations", validated.reservationId, "An inserted child reservation row is missing inside its own transaction.");
1664
+ return ReservedChildBudget.make({
1665
+ reservation: yield* childReservationSnapshotFromRow(operation, inserted.value),
1666
+ replayed: false
1667
+ });
1668
+ }));
1669
+ yield* hitFailpoint("ledger:child-reservation:after", operation);
1670
+ return reserved;
1671
+ });
1672
+ const attachChildToReservation = Effect.fn("DoSubmissionLedger.attachChildToReservation")(function* (request) {
1673
+ const operation = "ledger attach child to reservation";
1674
+ const validated = yield* Schema.decodeEffect(Schema.toType(AttachChildToReservationRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1675
+ yield* hitFailpoint("ledger:child-attach:before", operation);
1676
+ const attached = yield* inWriteTransaction(operation, Effect.gen(function* () {
1677
+ const existing = yield* readChildReservation(operation, validated.reservationId);
1678
+ if (Option.isNone(existing)) return yield* LedgerError.make({
1679
+ operation,
1680
+ message: `Unknown child reservation ${validated.reservationId}.`
1681
+ });
1682
+ if (existing.value.child_submission_id !== null) {
1683
+ if (existing.value.child_submission_id === validated.childSubmissionId) return yield* childReservationSnapshotFromRow(operation, existing.value);
1684
+ return yield* ChildReservationConflict.make({
1685
+ reservationId: validated.reservationId,
1686
+ status: existing.value.status,
1687
+ message: `Reservation ${validated.reservationId} already records child ${existing.value.child_submission_id}.`
1688
+ });
1689
+ }
1690
+ const parent = yield* requireSubmission(operation, existing.value.parent_submission_id);
1691
+ yield* requireOwnership(operation, parent, validated.ownershipToken);
1692
+ if (existing.value.status !== "reserved") return yield* ChildReservationConflict.make({
1693
+ reservationId: validated.reservationId,
1694
+ status: existing.value.status,
1695
+ message: `Cannot attach a child to a ${existing.value.status} reservation.`
1696
+ });
1697
+ yield* sql`
1698
+ UPDATE effect_agent_child_reservations
1699
+ SET child_submission_id = ${validated.childSubmissionId}
1700
+ WHERE reservation_id = ${validated.reservationId}
1701
+ RETURNING *`.pipe(rows.childReservations.write, Effect.mapError(internalFailure(operation)));
1702
+ const updated = yield* readChildReservation(operation, validated.reservationId);
1703
+ if (Option.isNone(updated)) return yield* corruptionFailure(operation, "effect_agent_child_reservations", validated.reservationId, "An updated child reservation row is missing inside its own transaction.");
1704
+ return yield* childReservationSnapshotFromRow(operation, updated.value);
1705
+ }));
1706
+ yield* hitFailpoint("ledger:child-attach:after", operation);
1707
+ return attached;
1708
+ });
1709
+ const beginChildBudgetRelease = Effect.fn("DoSubmissionLedger.beginChildBudgetRelease")(function* (request) {
1710
+ const operation = "ledger begin child budget release";
1711
+ const validated = yield* Schema.decodeEffect(Schema.toType(BeginChildBudgetReleaseRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1712
+ const accountingJson = yield* encodePersistedJsonText(validated.accounting).pipe(Effect.mapError(internalFailure(operation)));
1713
+ yield* hitFailpoint("ledger:child-release-pending:before", operation);
1714
+ const frozen = yield* inWriteTransaction(operation, Effect.gen(function* () {
1715
+ const existing = yield* readChildReservation(operation, validated.reservationId);
1716
+ if (Option.isNone(existing)) return yield* LedgerError.make({
1717
+ operation,
1718
+ message: `Unknown child reservation ${validated.reservationId}.`
1719
+ });
1720
+ if (existing.value.status !== "reserved") {
1721
+ const existingSnapshot = yield* childReservationSnapshotFromRow(operation, existing.value);
1722
+ if (existingSnapshot.accounting !== void 0 && equivalentPersistedJson(existingSnapshot.accounting, validated.accounting)) return existingSnapshot;
1723
+ return yield* ChildReservationConflict.make({
1724
+ reservationId: validated.reservationId,
1725
+ status: existing.value.status,
1726
+ message: "A different accounting decision is already frozen for this reservation."
1727
+ });
1728
+ }
1729
+ const now = yield* currentInstant;
1730
+ yield* sql`
1731
+ UPDATE effect_agent_child_reservations
1732
+ SET
1733
+ status = 'releasePending',
1734
+ accounting_json = ${accountingJson},
1735
+ release_began_at = ${now.iso}
1736
+ WHERE reservation_id = ${validated.reservationId}
1737
+ RETURNING *`.pipe(rows.childReservations.write, Effect.mapError(internalFailure(operation)));
1738
+ const updated = yield* readChildReservation(operation, validated.reservationId);
1739
+ if (Option.isNone(updated)) return yield* corruptionFailure(operation, "effect_agent_child_reservations", validated.reservationId, "An updated child reservation row is missing inside its own transaction.");
1740
+ return yield* childReservationSnapshotFromRow(operation, updated.value);
1741
+ }));
1742
+ yield* hitFailpoint("ledger:child-release-pending:after", operation);
1743
+ return frozen;
1744
+ });
1745
+ const releaseChildBudget = Effect.fn("DoSubmissionLedger.releaseChildBudget")(function* (request) {
1746
+ const operation = "ledger release child budget";
1747
+ const validated = yield* Schema.decodeEffect(Schema.toType(ReleaseChildBudgetRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1748
+ yield* hitFailpoint("ledger:child-release:before", operation);
1749
+ const released = yield* inWriteTransaction(operation, Effect.gen(function* () {
1750
+ const existing = yield* readChildReservation(operation, validated.reservationId);
1751
+ if (Option.isNone(existing)) return yield* LedgerError.make({
1752
+ operation,
1753
+ message: `Unknown child reservation ${validated.reservationId}.`
1754
+ });
1755
+ if (existing.value.status === "released") return yield* childReservationSnapshotFromRow(operation, existing.value);
1756
+ if (existing.value.status !== "releasePending") return yield* ChildReservationConflict.make({
1757
+ reservationId: validated.reservationId,
1758
+ status: existing.value.status,
1759
+ message: "Cannot release a reservation whose accounting decision is not frozen."
1760
+ });
1761
+ const now = yield* currentInstant;
1762
+ yield* sql`
1763
+ UPDATE effect_agent_child_reservations
1764
+ SET status = 'released', released_at = ${now.iso}
1765
+ WHERE reservation_id = ${validated.reservationId}
1766
+ RETURNING *`.pipe(rows.childReservations.write, Effect.mapError(internalFailure(operation)));
1767
+ const updated = yield* readChildReservation(operation, validated.reservationId);
1768
+ if (Option.isNone(updated)) return yield* corruptionFailure(operation, "effect_agent_child_reservations", validated.reservationId, "An updated child reservation row is missing inside its own transaction.");
1769
+ return yield* childReservationSnapshotFromRow(operation, updated.value);
1770
+ }));
1771
+ yield* hitFailpoint("ledger:child-release:after", operation);
1772
+ return released;
1773
+ });
1774
+ const scanPage = Effect.fn("DoSubmissionLedger.scanPage")(function* (cursor) {
1775
+ const operation = "ledger scan nonterminal";
1776
+ const rows = yield* (cursor === void 0 ? sql`
1777
+ SELECT submission_id AS "submissionId", thread_id AS "threadId",
1778
+ queue_sequence AS "queueSequence", principal, idempotency_key AS "idempotencyKey",
1779
+ deployment_id AS "deploymentId", receipt_id AS "receiptId", state
1780
+ FROM effect_agent_submissions
1781
+ WHERE state <> 'settled'
1782
+ ORDER BY thread_id ASC, queue_sequence ASC
1783
+ LIMIT ${SCAN_PAGE_SIZE}
1784
+ ` : sql`
1785
+ SELECT submission_id AS "submissionId", thread_id AS "threadId",
1786
+ queue_sequence AS "queueSequence", principal, idempotency_key AS "idempotencyKey",
1787
+ deployment_id AS "deploymentId", receipt_id AS "receiptId", state
1788
+ FROM effect_agent_submissions
1789
+ WHERE state <> 'settled'
1790
+ AND (thread_id, queue_sequence) > (${cursor.threadId}, ${cursor.queueSequence})
1791
+ ORDER BY thread_id ASC, queue_sequence ASC
1792
+ LIMIT ${SCAN_PAGE_SIZE}
1793
+ `).pipe(Effect.mapError(sqlFailure(operation)));
1794
+ const decoded = yield* decodeRows(Schema.Array(SubmissionWorkItem), "effect_agent_submissions", "nonterminal_scan", rows, "SubmissionWorkItem").pipe(Effect.mapError(internalFailure(operation)));
1795
+ const last = decoded[decoded.length - 1];
1796
+ return [decoded, last === void 0 || decoded.length < SCAN_PAGE_SIZE ? Option.none() : Option.some({
1797
+ threadId: last.threadId,
1798
+ queueSequence: last.queueSequence
1799
+ })];
1800
+ });
1801
+ const scanNonterminal = Stream.unwrap(state.read(Effect.gen(function* () {
1802
+ const active = yield* cached(workRows.matching("nonterminal", (row) => row.state !== "settled", sql`SELECT submission_id, thread_id, queue_sequence, principal, idempotency_key,
1803
+ deployment_id, receipt_id, state
1804
+ FROM effect_agent_submissions WHERE state <> 'settled' LIMIT 129`, 128), "ledger scan nonterminal");
1805
+ if (active.length > 128) return Stream.paginate(void 0, scanPage);
1806
+ 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);
1807
+ const work = yield* Schema.decodeEffect(Schema.Array(SubmissionWorkItem))(sorted.map((row) => ({
1808
+ submissionId: row.submission_id,
1809
+ threadId: row.thread_id,
1810
+ queueSequence: row.queue_sequence,
1811
+ principal: row.principal,
1812
+ idempotencyKey: row.idempotency_key,
1813
+ deploymentId: row.deployment_id,
1814
+ receiptId: row.receipt_id,
1815
+ state: row.state
1816
+ }))).pipe(Effect.mapError(internalFailure("ledger scan nonterminal")));
1817
+ return Stream.fromIterable(work);
1818
+ })));
1819
+ const readAbortIntentForSubmission = Effect.fn("DoSubmissionLedger.readAbortIntentForSubmission")(function* (request) {
1820
+ const operation = "ledger read abort intent";
1821
+ const validated = yield* Schema.decodeEffect(Schema.toType(AbortIntentRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1822
+ return yield* state.read(Effect.gen(function* () {
1823
+ const submission = yield* requireSubmission(operation, validated.submissionId);
1824
+ const intent = yield* readAbortIntent(operation, validated.submissionId);
1825
+ return Option.isNone(intent) ? void 0 : yield* abortIntentFromRow(operation, submission, validated.submissionId, intent.value);
1826
+ }));
1827
+ });
1828
+ const loadRecoverySnapshot = Effect.fn("DoSubmissionLedger.loadRecoverySnapshot")(function* (request) {
1829
+ const operation = "ledger load recovery snapshot";
1830
+ const validated = yield* Schema.decodeEffect(Schema.toType(RecoverySnapshotRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1831
+ return yield* state.read(Effect.gen(function* () {
1832
+ const submissionRow = yield* requireSubmission(operation, validated.submissionId);
1833
+ const submission = yield* decodeSubmissionSnapshot(operation, submissionRow);
1834
+ let ownership;
1835
+ const ownershipRow = yield* readOwnership(operation, validated.submissionId);
1836
+ if (Option.isSome(ownershipRow)) ownership = yield* decodeOwnershipSnapshot({
1837
+ attemptId: ownershipRow.value.attempt_id,
1838
+ ownerProducerId: ownershipRow.value.owner_producer_id,
1839
+ producerEpoch: ownershipRow.value.producer_epoch,
1840
+ leaseExpiresAt: ownershipRow.value.lease_expires_at
1841
+ }).pipe(Effect.mapError(internalFailure(operation)));
1842
+ let inputApplied;
1843
+ if (submissionRow.input_applied_record_id !== null && submissionRow.input_applied_sequence !== null) inputApplied = yield* decodeInputAppliedMarker({
1844
+ recordId: submissionRow.input_applied_record_id,
1845
+ sequence: submissionRow.input_applied_sequence
1846
+ }).pipe(Effect.mapError(internalFailure(operation)));
1847
+ let reservation;
1848
+ const reservationRow = yield* readReservation(operation, validated.submissionId);
1849
+ if (Option.isSome(reservationRow)) {
1850
+ const record = yield* decodeRecordEnvelopeText(reservationRow.value.record_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, error.message)));
1851
+ const settlementId = yield* Schema.decodeEffect(SettlementReservationSnapshot.fields.settlementId)(reservationRow.value.settlement_id).pipe(Effect.mapError(internalFailure(operation)));
1852
+ reservation = SettlementReservationSnapshot.make({
1853
+ settlementId,
1854
+ outcome: reservationRow.value.outcome,
1855
+ record,
1856
+ recordDigest: reservationRow.value.record_digest,
1857
+ finalized: reservationRow.value.finalized_at !== null
1858
+ });
1859
+ }
1860
+ let abortIntent;
1861
+ const abortRow = yield* readAbortIntent(operation, validated.submissionId);
1862
+ if (Option.isSome(abortRow)) abortIntent = yield* abortIntentFromRow(operation, submissionRow, validated.submissionId, abortRow.value);
1863
+ 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);
1864
+ const joinSubmissions = yield* decodeSubmissionRows(operation, validated.submissionId, joinRows);
1865
+ const joins = yield* Effect.forEach(joinSubmissions, (row) => decodeJoinSnapshot({
1866
+ submissionId: row.submission_id,
1867
+ state: row.state,
1868
+ hostSubmissionId: validated.submissionId
1869
+ }).pipe(Effect.mapError(internalFailure(operation))));
1870
+ let hostSubmissionId;
1871
+ if (submissionRow.joined_host_submission_id !== null) hostSubmissionId = yield* decodeSubmissionId(submissionRow.joined_host_submission_id).pipe(Effect.mapError(internalFailure(operation)));
1872
+ let suspension;
1873
+ if (submissionRow.suspended_reason_json !== null && submissionRow.suspended_at !== null) {
1874
+ const reason = yield* parseStoredJsonText(submissionRow.suspended_reason_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, error.message)));
1875
+ suspension = yield* decodeSuspensionSnapshot({
1876
+ reason,
1877
+ suspendedAt: submissionRow.suspended_at
1878
+ }).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, error.message)));
1879
+ }
1880
+ const decisionRows = yield* readApprovalDecisions(operation, validated.submissionId);
1881
+ const approvalDecisions = yield* Effect.forEach(decisionRows, (row) => approvalIntentFromRow(operation, row));
1882
+ const resolutionRows = yield* readUnknownResolutions(operation, validated.submissionId);
1883
+ const unknownResolutions = yield* Effect.forEach(resolutionRows, (row) => unknownResolutionIntentFromRow(operation, row));
1884
+ 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));
1885
+ const decodedChildReservations = yield* decodeChildReservationRows(operation, validated.submissionId, childReservationRows);
1886
+ const childReservations = yield* Effect.forEach(decodedChildReservations, (row) => childReservationSnapshotFromRow(operation, row));
1887
+ const markers = yield* readChildSettlementMarkers(operation, validated.submissionId);
1888
+ const markersByChild = new Map(markers.map((row) => [row.child_submission_id, row]));
1889
+ const childAttachments = [];
1890
+ for (const row of decodedChildReservations) {
1891
+ if (row.child_submission_id === null) continue;
1892
+ const child = yield* readSubmission(operation, row.child_submission_id);
1893
+ if (Option.isSome(child)) {
1894
+ childAttachments.push(yield* decodeChildAttachmentSnapshot({
1895
+ toolCallId: row.parent_tool_call_id,
1896
+ childSubmissionId: row.child_submission_id,
1897
+ childState: child.value.state,
1898
+ ...child.value.settled_outcome === null ? {} : { childOutcome: child.value.settled_outcome }
1899
+ }).pipe(Effect.mapError(internalFailure(operation))));
1900
+ continue;
1901
+ }
1902
+ const marker = markersByChild.get(row.child_submission_id);
1903
+ if (marker === void 0) continue;
1904
+ childAttachments.push(yield* decodeChildAttachmentSnapshot({
1905
+ toolCallId: row.parent_tool_call_id,
1906
+ childSubmissionId: row.child_submission_id,
1907
+ childState: "settled",
1908
+ ...marker.child_outcome === null ? {} : { childOutcome: marker.child_outcome }
1909
+ }).pipe(Effect.mapError(internalFailure(operation))));
1910
+ }
1911
+ let parentLinkage;
1912
+ if (submissionRow.parent_submission_id !== null && submissionRow.parent_tool_call_id !== null) parentLinkage = yield* decodeParentLinkage({
1913
+ parentSubmissionId: submissionRow.parent_submission_id,
1914
+ parentToolCallId: submissionRow.parent_tool_call_id
1915
+ }).pipe(Effect.mapError(internalFailure(operation)));
1916
+ return RecoverySnapshot.make({
1917
+ submission,
1918
+ joins,
1919
+ approvalDecisions,
1920
+ unknownResolutions,
1921
+ childReservations,
1922
+ childAttachments,
1923
+ ...parentLinkage === void 0 ? {} : { parentLinkage },
1924
+ ...hostSubmissionId === void 0 ? {} : { hostSubmissionId },
1925
+ ...suspension === void 0 ? {} : { suspension },
1926
+ ...ownership === void 0 ? {} : { ownership },
1927
+ ...inputApplied === void 0 ? {} : { inputApplied },
1928
+ ...reservation === void 0 ? {} : { reservation },
1929
+ ...abortIntent === void 0 ? {} : { abortIntent }
1930
+ });
1931
+ }));
1932
+ });
1933
+ return Context.make(SubmissionLedger, SubmissionLedger.of({
1934
+ capabilities,
1935
+ admit,
1936
+ markReady,
1937
+ lookup,
1938
+ resolveAdmission,
1939
+ claim,
1940
+ renewOwnership,
1941
+ releaseOwnership,
1942
+ markInputApplied,
1943
+ reserveSettlement,
1944
+ finalizeSettlement,
1945
+ requestAbort,
1946
+ stopWorker,
1947
+ inspectWorker,
1948
+ claimJoining,
1949
+ markJoined,
1950
+ revertJoining,
1951
+ suspend,
1952
+ recordApprovalDecision,
1953
+ markUnknown,
1954
+ recordUnknownResolution,
1955
+ recordChildSettled,
1956
+ reserveChildBudget,
1957
+ attachChildToReservation,
1958
+ beginChildBudgetRelease,
1959
+ releaseChildBudget,
1960
+ scanNonterminal,
1961
+ loadRecoverySnapshot,
1962
+ readAbortIntent: readAbortIntentForSubmission
1963
+ }));
1964
+ });
1965
+ /**
1966
+ * Durable Object SubmissionLedger implementation sharing the journal's private SQLite
1967
+ * database, storage-backed transaction discipline, and producer-epoch fencing substrate.
1968
+ * Configuration, failpoint, SQL, and Crypto authority stay visible in the input channel.
1969
+ */
1970
+ const submissionLedgerLayer = Layer.effectContext(makeServices());
1971
+ /**
1972
+ * A composition-root convenience Layer for the durable Submission Ledger. Point it at the
1973
+ * same `ctx.storage` as the ThreadStore so claims fence the same producer epochs.
1974
+ */
1975
+ const ledgerLayer = (options) => Layer.unwrap(Effect.map(DoStorageConfig, (config) => submissionLedgerLayer.pipe(Layer.provide(Layer.mergeAll(Layer.succeed(DoStorageConfig)(config), storageFailpointLayer(options), SqliteClient.layer({ storage: options.storage }), BrowserCrypto.layer))))).pipe(Layer.provide(storageConfigLayer(options)));
1976
+ //#endregion
1977
+ export { ledgerLayer, submissionLedgerLayer, DoSubmissionLedger_exports as t };
1978
+
1979
+ //# sourceMappingURL=DoSubmissionLedger.mjs.map