@yielded/agent-storage-cloudflare 0.1.0-beta.167 → 0.1.0-beta.169
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DoMemoryStore.mjs +1 -1
- package/dist/DoMessageDeliveryStore.d.mts +1 -1
- package/dist/DoMessageDeliveryStore.mjs +3 -2
- package/dist/DoMessageDeliveryStore.mjs.map +1 -1
- package/dist/DoScheduleStore.mjs +16 -37
- package/dist/DoScheduleStore.mjs.map +1 -1
- package/dist/DoStorageConfig.d.mts +2 -10
- package/dist/DoStorageConfig.mjs +2 -11
- package/dist/DoStorageConfig.mjs.map +1 -1
- package/dist/{DoStorageError-CGuAfSwF.d.mts → DoStorageError-CxjMnzUA.d.mts} +7 -18
- package/dist/DoStorageError.d.mts +2 -2
- package/dist/DoStorageError.mjs +14 -30
- package/dist/DoStorageError.mjs.map +1 -1
- package/dist/DoStorageFailpoint.d.mts +1 -1
- package/dist/DoStorageFailpointTesting.d.mts +1 -1
- package/dist/{DoStorageVersion-iNSyi3cJ.d.mts → DoStorageVersion-DTFoLglB.d.mts} +3 -4
- package/dist/DoStorageVersion.d.mts +1 -1
- package/dist/DoStorageVersion.mjs +2 -2
- package/dist/DoStorageVersion.mjs.map +1 -1
- package/dist/DoSubmissionLedger.d.mts +7 -5
- package/dist/DoSubmissionLedger.mjs +378 -528
- package/dist/DoSubmissionLedger.mjs.map +1 -1
- package/dist/DoSubscriptionStore.mjs +5 -24
- package/dist/DoSubscriptionStore.mjs.map +1 -1
- package/dist/{DoThreadStore-D_83OjCH.d.mts → DoThreadStore-Bd4fTns3.d.mts} +17 -18
- package/dist/DoThreadStore-CwBvTLoq.mjs +468 -0
- package/dist/DoThreadStore-CwBvTLoq.mjs.map +1 -0
- package/dist/DoThreadStore.d.mts +2 -2
- package/dist/DoThreadStore.mjs +4 -529
- package/dist/MemoryProtocol.mjs +3 -3
- package/dist/MemoryProtocol.mjs.map +1 -1
- package/dist/PortProtocol.d.mts +11727 -3410
- package/dist/PortProtocol.mjs +103 -15
- package/dist/PortProtocol.mjs.map +1 -1
- package/dist/PortRouting.d.mts +14 -9
- package/dist/PortRouting.mjs +155 -29
- package/dist/PortRouting.mjs.map +1 -1
- package/dist/do-journal-Cawnsetj.mjs +761 -0
- package/dist/do-journal-Cawnsetj.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/migrations-D_EP3KYf.mjs +148 -0
- package/dist/migrations-D_EP3KYf.mjs.map +1 -0
- package/dist/{owned-state-DHm2aRXx.mjs → owned-state-BHzUi0eb.mjs} +50 -19
- package/dist/owned-state-BHzUi0eb.mjs.map +1 -0
- package/package.json +1 -1
- package/src/DoMessageDeliveryStore.ts +2 -1
- package/src/DoScheduleStore.ts +19 -59
- package/src/DoStorageConfig.ts +2 -11
- package/src/DoStorageError.ts +3 -26
- package/src/DoStorageVersion.ts +1 -1
- package/src/DoSubmissionLedger.ts +1806 -2030
- package/src/DoSubscriptionStore.ts +5 -47
- package/src/DoThreadStore.ts +276 -622
- package/src/MemoryProtocol.ts +3 -3
- package/src/PortProtocol.ts +198 -12
- package/src/PortRouting.ts +527 -108
- package/src/internal/canonical-append.ts +15 -0
- package/src/internal/do-journal.ts +548 -1440
- package/src/internal/migrations.ts +184 -287
- package/src/internal/owned-state.ts +81 -30
- package/src/internal/prompt-checkpoint.ts +158 -0
- package/src/internal/storage-span.ts +2 -5
- package/dist/DoThreadStore.mjs.map +0 -1
- package/dist/do-journal-DUhjZkOf.mjs +0 -1352
- package/dist/do-journal-DUhjZkOf.mjs.map +0 -1
- package/dist/migrations-DtKz_Gxr.mjs +0 -306
- package/dist/migrations-DtKz_Gxr.mjs.map +0 -1
- package/dist/owned-state-DHm2aRXx.mjs.map +0 -1
- package/src/internal/message-delivery-schema.ts +0 -24
- package/src/internal/recovery-checkpoint-schema.ts +0 -17
|
@@ -2,19 +2,24 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
|
2
2
|
import { DoStorageConfig } from "./DoStorageConfig.mjs";
|
|
3
3
|
import { DoStorageFailpoint } from "./DoStorageFailpoint.mjs";
|
|
4
4
|
import { DoLedgerError, DoStorageCorruptionError, DoStorageError } from "./DoStorageError.mjs";
|
|
5
|
-
import { n as ownedRows } from "./owned-state-
|
|
6
|
-
import {
|
|
7
|
-
import { storageConfigLayer, storageFailpointLayer } from "./DoThreadStore.mjs";
|
|
5
|
+
import { n as ownedRows } from "./owned-state-BHzUi0eb.mjs";
|
|
6
|
+
import { i as initializeDoJournal, r as decodeRows } from "./do-journal-Cawnsetj.mjs";
|
|
7
|
+
import { l as prepareCanonicalAppend, o as storageConfigLayer, s as storageFailpointLayer } from "./DoThreadStore-CwBvTLoq.mjs";
|
|
8
|
+
import { BrowserCrypto } from "@effect/platform-browser";
|
|
8
9
|
import { Cause, Clock, Context, Crypto, DateTime, Effect, Layer, Option, Schema, Stream } from "effect";
|
|
9
10
|
import * as SqlClientService from "effect/sql/SqlClient";
|
|
10
|
-
import {
|
|
11
|
+
import { definitionJson, makeCanonicalStorage, recordBatchId, runKey } from "@yielded/agent-storage-sql/sql-canonical-storage";
|
|
11
12
|
import { SqlStorageProgress } from "@yielded/agent-storage-sql/sql-storage-progress";
|
|
12
|
-
import {
|
|
13
|
-
import { ApprovalDecision, CanonicalSequence, DefinitionDigests, Digest, PersistedJson,
|
|
14
|
-
import { ThreadStoreDiagnostic } from "@yielded/agent/thread-store";
|
|
15
|
-
import {
|
|
13
|
+
import { canonicalRecordJson } from "@yielded/agent-storage-sql/sql-thread-archive-range";
|
|
14
|
+
import { ApprovalDecision, CanonicalSequence, DefinitionDigests, Digest, PersistedJson, RecordEnvelope, SettlementOutcome, WorkerAdmission } from "@yielded/agent/records";
|
|
15
|
+
import { AppendConflict, ThreadNotMaterialized, ThreadStoreDiagnostic, ThreadStoreError, makeTransferFactCheck } from "@yielded/agent/thread-store";
|
|
16
|
+
import { SqliteClient } from "@effect/sql-sqlite-do";
|
|
17
|
+
import { StorageOwner } from "@yielded/agent/run-storage";
|
|
18
|
+
import { decodeAbortFact, decodeAdmissionFact, decodeApprovalFact, decodeResolutionFact } from "@yielded/agent-storage-sql/sql-admission-facts";
|
|
16
19
|
import { InputMessage } from "@yielded/agent/messaging";
|
|
17
|
-
import {
|
|
20
|
+
import { runIdForSubmission } from "@yielded/agent/run-journal";
|
|
21
|
+
import { SettlementPublicationResult, SettlementPublisher, validateCanonicalSettlement, validateJoinedSettlement, validatePublication } from "@yielded/agent/settlement-publisher";
|
|
22
|
+
import { AbortCommand, AbortIntent, AbortIntentRequest, AdmissionAdmitted, AdmissionConflict, AdmissionFence, AdmissionGroup, AdmissionNotAdmitted, AdmissionPolicyError, AdmissionRequest, AdmissionResult, ApprovalConflict, ApprovalDecisionCommand, ApprovalDecisionIntent, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, ChildAttachmentSnapshot, ChildBudgetReservationRequest, ChildBudgetReservationSnapshot, ChildReservationConflict, ChildReservationStatus, ChildSettledNotification, Claim, ClaimJoiningRequest, ClaimRequest, FundingOwner, FundingOwnerRequest, InputAppliedMarker, JoinSnapshot, JoinedToHost, JoiningClaim, LedgerCapabilities, LedgerError, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, OwnershipLost, ParentLinkage, QueueSequence, RecoverySnapshot, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReservedChildBudget, RevertJoiningRequest, Settlement, SettlementConflict, SettlementFinalization, SubmissionAdmissionFence, SubmissionLedger, SubmissionLookup, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SubmissionWorkItem, SuspendRequest, SuspensionReason, SuspensionSnapshot, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, UnknownResolutionKind, WorkerLedgerState, WorkerStopCommand, approvalDecisionsCover, settlementFailureFromRecord, submissionAbortRecordId, submissionSettlementBatchId, submissionSettlementRecordId, unknownResolutionKind, unknownResolutionMatchesResult, workerTerminalFromRecord } from "@yielded/agent/submission-ledger";
|
|
18
23
|
import { AssignmentTerminal } from "@yielded/agent/worker";
|
|
19
24
|
//#region src/DoSubmissionLedger.ts
|
|
20
25
|
var DoSubmissionLedger_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -29,7 +34,7 @@ const BoundedStoredText = Schema.String.check(Schema.isMaxLength(2e6));
|
|
|
29
34
|
const BoundedIdentifier = Schema.NonEmptyString.check(Schema.isMaxLength(1024));
|
|
30
35
|
const BoundedTimestamp = Schema.NonEmptyString.check(Schema.isMaxLength(128));
|
|
31
36
|
const SCAN_PAGE_SIZE = 256;
|
|
32
|
-
const
|
|
37
|
+
const admissionFitsTransfer = makeTransferFactCheck(AdmissionRequest);
|
|
33
38
|
const RESUME_IMMEDIATELY = "resume-immediately";
|
|
34
39
|
const SUSPENDED = "suspended";
|
|
35
40
|
const NOT_WAITING = "not-waiting";
|
|
@@ -50,6 +55,8 @@ var SubmissionRow = class extends Schema.Class("SubmissionRow")({
|
|
|
50
55
|
receipt_id: BoundedIdentifier,
|
|
51
56
|
state: SubmissionState,
|
|
52
57
|
settled_outcome: Schema.NullOr(SettlementOutcome),
|
|
58
|
+
settled_record_id: Schema.NullOr(BoundedIdentifier),
|
|
59
|
+
finalized_at: Schema.NullOr(BoundedTimestamp),
|
|
53
60
|
created_at: BoundedTimestamp,
|
|
54
61
|
ready_at: Schema.NullOr(BoundedTimestamp),
|
|
55
62
|
input_applied_record_id: Schema.NullOr(BoundedIdentifier),
|
|
@@ -106,29 +113,12 @@ var ApprovalDecisionRow = class extends Schema.Class("ApprovalDecisionRow")({
|
|
|
106
113
|
var UnknownResolutionRow = class extends Schema.Class("UnknownResolutionRow")({
|
|
107
114
|
submission_id: BoundedIdentifier,
|
|
108
115
|
tool_call_id: BoundedIdentifier,
|
|
116
|
+
resolution_kind: UnknownResolutionKind,
|
|
109
117
|
author: BoundedIdentifier,
|
|
110
118
|
reason: BoundedStoredText,
|
|
111
119
|
resolution_json: BoundedStoredText,
|
|
112
120
|
resolved_at: BoundedTimestamp
|
|
113
121
|
}) {};
|
|
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
122
|
var AbortIntentRow = class extends Schema.Class("AbortIntentRow")({
|
|
133
123
|
submission_id: BoundedIdentifier,
|
|
134
124
|
author: BoundedIdentifier,
|
|
@@ -148,13 +138,14 @@ const SUBMISSION_COLUMNS = `
|
|
|
148
138
|
principal,
|
|
149
139
|
idempotency_key,
|
|
150
140
|
agent_id,
|
|
151
|
-
agent_digests_json,
|
|
152
141
|
deployment_id,
|
|
153
142
|
input_json,
|
|
154
143
|
input_digest,
|
|
155
144
|
receipt_id,
|
|
156
145
|
state,
|
|
157
146
|
settled_outcome,
|
|
147
|
+
settled_record_id,
|
|
148
|
+
finalized_at,
|
|
158
149
|
created_at,
|
|
159
150
|
ready_at,
|
|
160
151
|
input_applied_record_id,
|
|
@@ -189,7 +180,6 @@ const ToolCallIdSchema = ApprovalDecisionCommand.fields.toolCallId;
|
|
|
189
180
|
const ToolCallIdList = Schema.Array(ToolCallIdSchema);
|
|
190
181
|
const encodePersistedJsonText = Schema.encodeEffect(Schema.fromJsonString(PersistedJson));
|
|
191
182
|
const encodeDefinitionDigestsText = Schema.encodeEffect(Schema.fromJsonString(DefinitionDigests));
|
|
192
|
-
const encodeRecordEnvelopeText = Schema.encodeEffect(Schema.fromJsonString(RecordEnvelope));
|
|
193
183
|
const decodeRecordEnvelopeText = Schema.decodeEffect(Schema.fromJsonString(RecordEnvelope));
|
|
194
184
|
const encodeSuspensionReasonText = Schema.encodeEffect(Schema.fromJsonString(SuspensionReason));
|
|
195
185
|
const encodeUnknownResolutionText = Schema.encodeEffect(Schema.fromJsonString(UnknownResolution));
|
|
@@ -198,12 +188,10 @@ const decodeToolCallIdsText = Schema.decodeEffect(Schema.fromJsonString(ToolCall
|
|
|
198
188
|
const parseStoredJsonText = Schema.decodeEffect(Schema.fromJsonString(Schema.Json));
|
|
199
189
|
const decodeAdmissionResult = Schema.decodeUnknownEffect(AdmissionResult);
|
|
200
190
|
const decodeClaim = Schema.decodeUnknownEffect(Claim);
|
|
201
|
-
const decodeOwnershipRenewal = Schema.decodeUnknownEffect(OwnershipRenewal);
|
|
202
191
|
const decodeSettlement = Schema.decodeUnknownEffect(Settlement);
|
|
203
192
|
const decodeAbortIntent = Schema.decodeUnknownEffect(AbortIntent);
|
|
204
|
-
const decodeOwnershipSnapshot = Schema.decodeUnknownEffect(OwnershipSnapshot);
|
|
205
193
|
const decodeInputAppliedMarker = Schema.decodeUnknownEffect(InputAppliedMarker);
|
|
206
|
-
const
|
|
194
|
+
const decodeSubmissionSnapshotType = (input) => Schema.decodeEffect(Schema.toType(Schema.Struct(SubmissionSnapshot.fields)))(input).pipe(Effect.map((fields) => SubmissionSnapshot.make(fields)));
|
|
207
195
|
const decodeSubmissionId = Schema.decodeUnknownEffect(SubmissionSnapshot.fields.submissionId);
|
|
208
196
|
const decodeQueueSequence = Schema.decodeUnknownEffect(QueueSequence);
|
|
209
197
|
const decodeUtcInstant = Schema.decodeUnknownEffect(Schema.DateTimeUtcFromString);
|
|
@@ -243,27 +231,32 @@ const submissionsRows = ownedRows(SubmissionRow, "effect_agent_submissions", (ro
|
|
|
243
231
|
"thread_id",
|
|
244
232
|
"principal",
|
|
245
233
|
"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");
|
|
234
|
+
]], (sql) => sql`*, ${definitionJson(sql)} AS agent_digests_json`);
|
|
249
235
|
const abortsRows = ownedRows(AbortIntentRow, "effect_agent_abort_intents", (row) => row.submission_id, "submission_id");
|
|
250
236
|
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([
|
|
237
|
+
const resolutionsRows = ownedRows(UnknownResolutionRow, "effect_agent_unknown_resolutions", (row) => JSON.stringify([
|
|
238
|
+
row.submission_id,
|
|
239
|
+
row.tool_call_id,
|
|
240
|
+
row.resolution_kind
|
|
241
|
+
]));
|
|
252
242
|
const childSettlementsRows = ownedRows(ChildSettlementMarkerRow, "effect_agent_child_settlements", (row) => JSON.stringify([row.parent_submission_id, row.child_submission_id]));
|
|
253
243
|
const childReservationsRows = ownedRows(ChildReservationRow, "effect_agent_child_reservations", (row) => row.reservation_id, "reservation_id");
|
|
254
244
|
const workerStopsRows = ownedRows(WorkerStopRow, "effect_agent_worker_stops", (row) => row.thread_id, "thread_id");
|
|
255
245
|
const submissionWorkRows = ownedRows(SubmissionWorkRow, "effect_agent_submissions", (row) => row.submission_id);
|
|
256
|
-
const makeServices = Effect.
|
|
246
|
+
const makeServices = Effect.fnUntraced(function* () {
|
|
257
247
|
const progress = yield* SqlStorageProgress;
|
|
258
248
|
const recordProgress = (kind, operation) => progress.committed(kind).pipe(Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, internalFailure(operation)))));
|
|
259
249
|
const config = yield* DoStorageConfig;
|
|
260
250
|
const failpoint = yield* DoStorageFailpoint;
|
|
261
251
|
const sql = yield* SqlClientService.SqlClient;
|
|
262
252
|
const crypto = yield* Crypto.Crypto;
|
|
253
|
+
const physical = yield* makeCanonicalStorage();
|
|
254
|
+
const submissionColumns = sql`${sql.literal(SUBMISSION_COLUMNS)}, ${definitionJson(sql)} AS agent_digests_json`;
|
|
263
255
|
const admissionFence = yield* SubmissionAdmissionFence;
|
|
264
256
|
const journal = yield* initializeDoJournal(sql, failpoint.hit, config.maxStoredValueBytes);
|
|
265
257
|
const lifecycle = journal.lifecycle;
|
|
266
258
|
const state = journal.state;
|
|
259
|
+
const owner = state.owner;
|
|
267
260
|
const submissionViews = submissionsRows(state, sql);
|
|
268
261
|
const workRows = submissionWorkRows(state, sql);
|
|
269
262
|
const rows = {
|
|
@@ -273,8 +266,6 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
273
266
|
seed: submissionViews.seed,
|
|
274
267
|
write: (effect) => submissionViews.write(effect).pipe(Effect.tap((changed) => workRows.write(Effect.succeed(changed))))
|
|
275
268
|
},
|
|
276
|
-
ownership: ownershipRows(state, sql),
|
|
277
|
-
reservations: reservationsRows(state, sql),
|
|
278
269
|
aborts: abortsRows(state, sql),
|
|
279
270
|
approvals: approvalsRows(state, sql),
|
|
280
271
|
resolutions: resolutionsRows(state, sql),
|
|
@@ -284,20 +275,12 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
284
275
|
workerStops: workerStopsRows(state, sql)
|
|
285
276
|
};
|
|
286
277
|
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
278
|
const byKey = Effect.fnUntraced(function* (request, operation) {
|
|
293
|
-
|
|
294
|
-
if (lane === void 0) return yield* cached(rows.submissions.byFields([
|
|
279
|
+
return yield* cached(rows.submissions.byFields([
|
|
295
280
|
["thread_id", request.threadId],
|
|
296
281
|
["principal", request.principal],
|
|
297
282
|
["idempotency_key", request.idempotencyKey]
|
|
298
283
|
]), 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
284
|
}, state.read);
|
|
302
285
|
const retainLifecycle = (submission, fact) => lifecycle === void 0 ? Effect.void : Effect.gen(function* () {
|
|
303
286
|
const ownerThreadId = yield* Schema.decodeEffect(SubmissionSnapshot.fields.threadId)(submission.thread_id).pipe(Effect.mapError(internalFailure("lifecycle owner")));
|
|
@@ -331,18 +314,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
331
314
|
* failures surface as LedgerError carrying the typed `DoStorageError` as cause.
|
|
332
315
|
*/
|
|
333
316
|
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)));
|
|
317
|
+
const mintUuid = (operation) => crypto.randomUUIDv7().pipe(Effect.mapError((error) => internalFailure(operation)(error)));
|
|
335
318
|
const currentInstant = Effect.map(Clock.currentTimeMillis, (millis) => ({
|
|
336
319
|
millis,
|
|
337
320
|
iso: new Date(millis).toISOString()
|
|
338
321
|
}));
|
|
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
322
|
const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows(Schema.Array(SubmissionRow), "effect_agent_submissions", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
341
|
-
const readSubmission = Effect.
|
|
323
|
+
const readSubmission = Effect.fnUntraced(function* (operation, submissionId) {
|
|
342
324
|
const found = yield* cached(rows.submissions.by("submission_id", submissionId), operation);
|
|
343
325
|
return Option.fromUndefinedOr(found[0]);
|
|
344
326
|
});
|
|
345
|
-
const requireSubmission = Effect.
|
|
327
|
+
const requireSubmission = Effect.fnUntraced(function* (operation, submissionId) {
|
|
346
328
|
const submission = yield* readSubmission(operation, submissionId);
|
|
347
329
|
if (Option.isNone(submission)) return yield* LedgerError.make({
|
|
348
330
|
operation,
|
|
@@ -350,33 +332,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
350
332
|
});
|
|
351
333
|
return submission.value;
|
|
352
334
|
});
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
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;
|
|
335
|
+
const requireOwner = Effect.fnUntraced(function* (operation, submission) {
|
|
336
|
+
if (owner.hasOwner(submission.thread_id, submission.submission_id)) return;
|
|
337
|
+
const submissionId = yield* decodeSubmissionId(submission.submission_id).pipe(Effect.mapError(internalFailure(operation)));
|
|
338
|
+
return yield* OwnershipLost.make({ submissionId });
|
|
360
339
|
});
|
|
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
340
|
const submissions = /* @__PURE__ */ new WeakMap();
|
|
379
|
-
const decodeSubmissionSnapshot = Effect.
|
|
341
|
+
const decodeSubmissionSnapshot = Effect.fnUntraced(function* (operation, row) {
|
|
380
342
|
const existing = submissions.get(row);
|
|
381
343
|
if (existing !== void 0) return existing;
|
|
382
344
|
const decodeFailure = (error) => internalFailure(operation)(DoStorageCorruptionError.make({
|
|
@@ -390,42 +352,39 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
390
352
|
issueTag: error.issue._tag
|
|
391
353
|
})
|
|
392
354
|
}));
|
|
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
355
|
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
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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,
|
|
356
|
+
const admission = yield* decodeAdmissionFact(row).pipe(Effect.mapError(decodeFailure));
|
|
357
|
+
const readyAt = row.ready_at === null ? void 0 : yield* decodeUtcInstant(row.ready_at).pipe(Effect.mapError(decodeFailure));
|
|
358
|
+
const snapshot = yield* decodeSubmissionSnapshotType({
|
|
359
|
+
...admission,
|
|
408
360
|
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
361
|
...row.settled_outcome === null ? {} : { settledOutcome: row.settled_outcome },
|
|
415
|
-
...
|
|
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
|
-
} }
|
|
362
|
+
...readyAt === void 0 ? {} : { readyAt }
|
|
420
363
|
}).pipe(Effect.mapError(decodeFailure));
|
|
421
364
|
submissions.set(row, snapshot);
|
|
422
365
|
return snapshot;
|
|
423
366
|
});
|
|
424
|
-
const
|
|
425
|
-
const
|
|
426
|
-
|
|
367
|
+
const readCanonicalSettlement = Effect.fnUntraced(function* (operation, submission) {
|
|
368
|
+
const expected = yield* decodeSubmissionSnapshot(operation, submission);
|
|
369
|
+
const recordId = submissionSettlementRecordId(expected.submissionId);
|
|
370
|
+
const found = yield* sql`SELECT ${recordBatchId(sql)} AS batch_id, record_id, ${canonicalRecordJson(sql)} AS record_json
|
|
371
|
+
FROM effect_agent_canonical_records WHERE thread_id = ${submission.thread_id}
|
|
372
|
+
AND record_id = ${JSON.stringify(recordId)}`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
373
|
+
const decoded = yield* decodeRows(Schema.Array(Schema.Struct({
|
|
374
|
+
batch_id: Schema.fromJsonString(BoundedIdentifier),
|
|
375
|
+
record_id: Schema.fromJsonString(BoundedIdentifier),
|
|
376
|
+
record_json: BoundedStoredText
|
|
377
|
+
})), "effect_agent_canonical_records", recordId, found).pipe(Effect.mapError(internalFailure(operation)));
|
|
378
|
+
if (decoded.length === 0) return void 0;
|
|
379
|
+
const row = decoded[0];
|
|
380
|
+
if (decoded.length !== 1 || row.batch_id !== submissionSettlementBatchId(expected.submissionId) || row.record_id !== recordId) return yield* corruptionFailure(operation, "effect_agent_canonical_records", recordId, "Canonical settlement identity is inconsistent.");
|
|
381
|
+
const record = yield* decodeRecordEnvelopeText(row.record_json).pipe(Effect.mapError(internalFailure(operation)));
|
|
382
|
+
return {
|
|
383
|
+
record,
|
|
384
|
+
settlement: yield* validateCanonicalSettlement(record, expected)
|
|
385
|
+
};
|
|
427
386
|
});
|
|
428
|
-
const readAbortIntent = Effect.
|
|
387
|
+
const readAbortIntent = Effect.fnUntraced(function* (operation, submissionId) {
|
|
429
388
|
const found = yield* cached(rows.aborts.by("submission_id", submissionId), operation);
|
|
430
389
|
return Option.fromUndefinedOr(found[0]);
|
|
431
390
|
});
|
|
@@ -434,7 +393,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
434
393
|
* Submission (the DC realization of the port's "cross-store adapters record a durable
|
|
435
394
|
* notification marker" contract).
|
|
436
395
|
*/
|
|
437
|
-
const readChildSettlementMarkers = Effect.
|
|
396
|
+
const readChildSettlementMarkers = Effect.fnUntraced(function* (operation, parentSubmissionId) {
|
|
438
397
|
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
398
|
});
|
|
440
399
|
/**
|
|
@@ -443,13 +402,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
443
402
|
* cross-store notification marker was recorded for it (the child's row lives in another
|
|
444
403
|
* Durable Object and its owner reported the settlement through `recordChildSettled`).
|
|
445
404
|
*/
|
|
446
|
-
const childProvablySettled = Effect.
|
|
405
|
+
const childProvablySettled = Effect.fnUntraced(function* (operation, markerChildren, childSubmissionId) {
|
|
447
406
|
if (markerChildren.has(childSubmissionId)) return true;
|
|
448
407
|
const childRow = yield* readSubmission(operation, childSubmissionId);
|
|
449
|
-
return Option.isSome(childRow) && childRow.value
|
|
408
|
+
return Option.isSome(childRow) && (yield* readCanonicalSettlement(operation, childRow.value)) !== void 0;
|
|
450
409
|
});
|
|
451
410
|
const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows(Schema.Array(ChildReservationRow), "effect_agent_child_reservations", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
|
|
452
|
-
const readChildReservation = Effect.
|
|
411
|
+
const readChildReservation = Effect.fnUntraced(function* (operation, reservationId) {
|
|
453
412
|
const rows = yield* sql`
|
|
454
413
|
SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
|
|
455
414
|
FROM effect_agent_child_reservations
|
|
@@ -459,7 +418,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
459
418
|
if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_child_reservations", reservationId, "A child reservation primary key returned more than one row.");
|
|
460
419
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
461
420
|
});
|
|
462
|
-
const readChildReservationForCall = Effect.
|
|
421
|
+
const readChildReservationForCall = Effect.fnUntraced(function* (operation, parentSubmissionId, parentToolCallId) {
|
|
463
422
|
const rows = yield* sql`
|
|
464
423
|
SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
|
|
465
424
|
FROM effect_agent_child_reservations
|
|
@@ -470,7 +429,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
470
429
|
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
430
|
return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
|
|
472
431
|
});
|
|
473
|
-
const childReservationSnapshotFromRow = Effect.
|
|
432
|
+
const childReservationSnapshotFromRow = Effect.fnUntraced(function* (operation, row) {
|
|
474
433
|
const rowFailure = (error) => corruptionFailure(operation, "effect_agent_child_reservations", row.reservation_id, error.message);
|
|
475
434
|
const allocation = yield* parseStoredJsonText(row.allocation_json).pipe(Effect.mapError(rowFailure));
|
|
476
435
|
const accounting = row.accounting_json === null ? void 0 : yield* parseStoredJsonText(row.accounting_json).pipe(Effect.mapError(rowFailure));
|
|
@@ -488,35 +447,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
488
447
|
...row.released_at === null ? {} : { releasedAt: row.released_at }
|
|
489
448
|
}).pipe(Effect.mapError(rowFailure));
|
|
490
449
|
});
|
|
491
|
-
const readApprovalDecisions = Effect.
|
|
450
|
+
const readApprovalDecisions = Effect.fnUntraced(function* (operation, submissionId) {
|
|
492
451
|
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
452
|
});
|
|
494
|
-
const approvalIntentFromRow = Effect.
|
|
495
|
-
|
|
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) {
|
|
453
|
+
const approvalIntentFromRow = (operation, row) => decodeApprovalFact(row).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_approval_decisions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
|
|
454
|
+
const readUnknownResolutions = Effect.fnUntraced(function* (operation, submissionId) {
|
|
505
455
|
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
456
|
});
|
|
507
|
-
const unknownResolutionIntentFromRow = Effect.
|
|
508
|
-
|
|
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)));
|
|
457
|
+
const unknownResolutionIntentFromRow = Effect.fnUntraced(function* (operation, row) {
|
|
458
|
+
return yield* decodeResolutionFact(row).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_unknown_resolutions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
|
|
517
459
|
});
|
|
518
460
|
/** The Submission's marked-unknown open Tool Call identities, empty when never marked. */
|
|
519
|
-
const storedUnknownToolCallIds = Effect.
|
|
461
|
+
const storedUnknownToolCallIds = Effect.fnUntraced(function* (operation, submission) {
|
|
520
462
|
if (submission.unknown_tool_call_ids_json === null) return [];
|
|
521
463
|
return yield* decodeToolCallIdsText(submission.unknown_tool_call_ids_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", submission.submission_id, error.message)));
|
|
522
464
|
});
|
|
@@ -525,22 +467,20 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
525
467
|
* derived from the shared canonical-records table using the deterministic abort record
|
|
526
468
|
* identity, never from a cached ledger marker.
|
|
527
469
|
*/
|
|
528
|
-
const canonicalAbortRecordId = Effect.
|
|
470
|
+
const canonicalAbortRecordId = Effect.fnUntraced(function* (operation, threadId, submissionId) {
|
|
529
471
|
const recordId = submissionAbortRecordId(submissionId);
|
|
530
472
|
return (yield* journal.hasRecord(threadId, recordId).pipe(Effect.mapError(internalFailure(operation)))) ? recordId : void 0;
|
|
531
473
|
});
|
|
532
|
-
const abortIntentFromRow = Effect.
|
|
474
|
+
const abortIntentFromRow = Effect.fnUntraced(function* (operation, submission, submissionId, row) {
|
|
533
475
|
const canonicalRecordId = yield* canonicalAbortRecordId(operation, submission.thread_id, submissionId);
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
reason: row.reason,
|
|
538
|
-
requestedAt: row.requested_at,
|
|
476
|
+
const fact = yield* decodeAbortFact(row).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_abort_intents", row.submission_id, error.message)));
|
|
477
|
+
return AbortIntent.make({
|
|
478
|
+
...fact,
|
|
539
479
|
...canonicalRecordId === void 0 ? {} : { canonicalRecordId }
|
|
540
|
-
})
|
|
480
|
+
});
|
|
541
481
|
});
|
|
542
482
|
const capabilities = Effect.succeed(LedgerCapabilities.make({ durability: "durable-cloudflare" }));
|
|
543
|
-
const admit = Effect.
|
|
483
|
+
const admit = Effect.fnUntraced(function* (request) {
|
|
544
484
|
const operation = "ledger admit";
|
|
545
485
|
const validated = yield* Schema.decodeEffect(Schema.toType(AdmissionRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
546
486
|
const inputJson = yield* encodePersistedJsonText(validated.inputPayload).pipe(Effect.mapError(internalFailure(operation)));
|
|
@@ -595,15 +535,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
595
535
|
replayed: true
|
|
596
536
|
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
597
537
|
}
|
|
538
|
+
if (!admissionFitsTransfer(validated.threadId, validated)) return yield* LedgerError.make({
|
|
539
|
+
operation,
|
|
540
|
+
message: "Admission exceeds the complete transfer page byte bound"
|
|
541
|
+
});
|
|
598
542
|
if ((yield* cached(rows.workerStops.by("thread_id", validated.threadId), operation)).length > 0) return yield* AdmissionPolicyError.make({
|
|
599
543
|
reason: "refused",
|
|
600
544
|
code: "worker-stopped"
|
|
601
545
|
});
|
|
602
|
-
const
|
|
603
|
-
const firstRows = lane === void 0 ? yield* sql`
|
|
546
|
+
const firstRows = yield* sql`
|
|
604
547
|
SELECT worker_admission_json FROM effect_agent_submissions
|
|
605
548
|
WHERE thread_id=${validated.threadId} ORDER BY queue_sequence LIMIT 1
|
|
606
|
-
`.pipe(Effect.mapError(sqlFailure(operation)))
|
|
549
|
+
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
607
550
|
const first = yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ worker_admission_json: Schema.NullOr(BoundedStoredText) })))(firstRows).pipe(Effect.mapError(internalFailure(operation)));
|
|
608
551
|
if (first[0] !== void 0) {
|
|
609
552
|
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)));
|
|
@@ -622,11 +565,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
622
565
|
code: "admission-group"
|
|
623
566
|
});
|
|
624
567
|
}
|
|
625
|
-
const maxRows =
|
|
568
|
+
const maxRows = yield* sql`
|
|
626
569
|
SELECT COALESCE(MAX(queue_sequence), 0) AS max_queue_sequence
|
|
627
570
|
FROM effect_agent_submissions
|
|
628
571
|
WHERE thread_id = ${validated.threadId}
|
|
629
|
-
`.pipe(Effect.mapError(sqlFailure(operation)))
|
|
572
|
+
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
630
573
|
const decodedMax = yield* decodeRows(Schema.Array(MaxQueueSequenceRow), "effect_agent_submissions", validated.threadId, maxRows).pipe(Effect.mapError(internalFailure(operation)));
|
|
631
574
|
const queueSequence = yield* decodeQueueSequence((decodedMax[0]?.max_queue_sequence ?? 0) + 1).pipe(Effect.mapError(internalFailure(operation)));
|
|
632
575
|
const now = yield* currentInstant;
|
|
@@ -638,7 +581,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
638
581
|
principal,
|
|
639
582
|
idempotency_key,
|
|
640
583
|
agent_id,
|
|
641
|
-
|
|
584
|
+
definition_key,
|
|
642
585
|
deployment_id,
|
|
643
586
|
input_json,
|
|
644
587
|
input_digest,
|
|
@@ -658,7 +601,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
658
601
|
${validated.principal},
|
|
659
602
|
${validated.idempotencyKey},
|
|
660
603
|
${validated.agentId},
|
|
661
|
-
${agentDigestsJson},
|
|
604
|
+
${yield* physical.definition(agentDigestsJson).pipe(Effect.mapError(internalFailure(operation)))},
|
|
662
605
|
${validated.deploymentId},
|
|
663
606
|
${inputJson},
|
|
664
607
|
${validated.inputDigest},
|
|
@@ -672,10 +615,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
672
615
|
${workerAdmissionJson},
|
|
673
616
|
${messageAdmissionJson}
|
|
674
617
|
)
|
|
675
|
-
RETURNING
|
|
618
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
619
|
+
if (queueSequence === 1) {
|
|
620
|
+
if ((yield* journal.getThread(validated.threadId).pipe(Effect.mapError(internalFailure(operation)))).length === 0) yield* journal.work.initialize(validated.threadId).pipe(Effect.mapError(internalFailure(operation)));
|
|
621
|
+
}
|
|
676
622
|
rows.aborts.seed("submission_id", mintedSubmissionId, []);
|
|
677
|
-
rows.ownership.seed("submission_id", mintedSubmissionId, []);
|
|
678
|
-
rows.reservations.seed("submission_id", mintedSubmissionId, []);
|
|
679
623
|
rows.submissions.seed("joined_host_submission_id", mintedSubmissionId, []);
|
|
680
624
|
rows.approvals.seed("submission_id", mintedSubmissionId, []);
|
|
681
625
|
rows.resolutions.seed("submission_id", mintedSubmissionId, []);
|
|
@@ -693,7 +637,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
693
637
|
yield* hitFailpoint("ledger:admit:after", operation);
|
|
694
638
|
return result;
|
|
695
639
|
});
|
|
696
|
-
const markReady = Effect.
|
|
640
|
+
const markReady = Effect.fnUntraced(function* (request) {
|
|
697
641
|
const operation = "ledger mark ready";
|
|
698
642
|
const validated = yield* Schema.decodeEffect(Schema.toType(MarkReadyRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
699
643
|
yield* hitFailpoint("ledger:mark-ready:before", operation);
|
|
@@ -705,7 +649,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
705
649
|
UPDATE effect_agent_submissions
|
|
706
650
|
SET state = 'ready', ready_at = ${now.iso}
|
|
707
651
|
WHERE submission_id = ${validated.submissionId}
|
|
708
|
-
RETURNING
|
|
652
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
709
653
|
yield* recordProgress("submission", operation);
|
|
710
654
|
yield* retainLifecycle(submission, {
|
|
711
655
|
_tag: "SubmissionReady",
|
|
@@ -714,7 +658,29 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
714
658
|
}));
|
|
715
659
|
yield* hitFailpoint("ledger:mark-ready:after", operation);
|
|
716
660
|
});
|
|
717
|
-
const
|
|
661
|
+
const resolveFundingOwner = Effect.fnUntraced(function* (input) {
|
|
662
|
+
const operation = "resolve funding owner";
|
|
663
|
+
const request = yield* Schema.decodeEffect(FundingOwnerRequest)(input).pipe(Effect.mapError(internalFailure(operation)));
|
|
664
|
+
const selected = yield* requireSubmission(operation, request.submissionId);
|
|
665
|
+
if (selected.thread_id !== request.threadId) return yield* LedgerError.make({
|
|
666
|
+
operation,
|
|
667
|
+
message: "Selected funding input belongs to another Thread."
|
|
668
|
+
});
|
|
669
|
+
if (selected.state === "joining") return yield* LedgerError.make({
|
|
670
|
+
operation,
|
|
671
|
+
message: "Selected funding input is still joining; retry after durable binding."
|
|
672
|
+
});
|
|
673
|
+
const hostId = selected.joined_host_submission_id;
|
|
674
|
+
if (hostId === null && selected.state === "joined") return yield* corruptionFailure(operation, "effect_agent_submissions", selected.submission_id, "Joined input has no stable host identity.");
|
|
675
|
+
if (hostId !== null && selected.state !== "joined" && selected.state !== "settled") return yield* corruptionFailure(operation, "effect_agent_submissions", selected.submission_id, "Host linkage is inconsistent with selected input state.");
|
|
676
|
+
const owner = hostId === null ? selected : yield* requireSubmission(operation, hostId);
|
|
677
|
+
if (owner.thread_id !== request.threadId || hostId !== null && (owner.submission_id === selected.submission_id || owner.joined_host_submission_id !== null || owner.state === "joining" || owner.state === "joined" || owner.queue_sequence >= selected.queue_sequence)) return yield* corruptionFailure(operation, "effect_agent_submissions", selected.submission_id, "Funding host is not a stable earlier input in this Thread.");
|
|
678
|
+
return yield* Schema.decodeEffect(Schema.toType(FundingOwner))({
|
|
679
|
+
selected: yield* decodeSubmissionSnapshot(operation, selected),
|
|
680
|
+
owner: yield* decodeSubmissionSnapshot(operation, owner)
|
|
681
|
+
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
682
|
+
}, state.read);
|
|
683
|
+
const lookup = Effect.fnUntraced(function* (request) {
|
|
718
684
|
const operation = "ledger lookup";
|
|
719
685
|
const validated = yield* Schema.decodeEffect(Schema.toType(SubmissionLookup))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
720
686
|
if (validated._tag === "SubmissionLookupById") {
|
|
@@ -728,7 +694,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
728
694
|
if (decoded.length === 0) return Option.none();
|
|
729
695
|
return Option.some(yield* decodeSubmissionSnapshot(operation, decoded[0]));
|
|
730
696
|
});
|
|
731
|
-
const resolveAdmission = Effect.
|
|
697
|
+
const resolveAdmission = Effect.fnUntraced(function* (request) {
|
|
732
698
|
const operation = "ledger resolve admission";
|
|
733
699
|
const validated = yield* Schema.decodeEffect(Schema.toType(SubmissionLookupByKey))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
734
700
|
const foundRows = yield* byKey(validated, operation);
|
|
@@ -737,30 +703,15 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
737
703
|
if (decoded.length === 0) return AdmissionNotAdmitted.make();
|
|
738
704
|
return AdmissionAdmitted.make({ submission: yield* decodeSubmissionSnapshot(operation, decoded[0]) });
|
|
739
705
|
});
|
|
740
|
-
const
|
|
741
|
-
const operation = "ledger
|
|
706
|
+
const selectHead = Effect.fn("DoSubmissionLedger.selectHead")(function* (request) {
|
|
707
|
+
const operation = "ledger select head";
|
|
742
708
|
const validated = yield* Schema.decodeEffect(Schema.toType(ClaimRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
743
709
|
const attemptId = `attempt-${yield* mintUuid(operation)}`;
|
|
744
|
-
const ownershipToken = `owner-${yield* mintUuid(operation)}`;
|
|
745
710
|
yield* hitFailpoint("ledger:claim:before", operation);
|
|
746
|
-
const claimed = yield*
|
|
747
|
-
|
|
748
|
-
const
|
|
749
|
-
|
|
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)}
|
|
711
|
+
const claimed = yield* owner.withMutation(Effect.gen(function* () {
|
|
712
|
+
if (owner.hasOwner(validated.threadId)) return Option.none();
|
|
713
|
+
const headRows = yield* sql`
|
|
714
|
+
SELECT ${submissionColumns}
|
|
764
715
|
FROM effect_agent_submissions
|
|
765
716
|
WHERE thread_id = ${validated.threadId}
|
|
766
717
|
AND state <> 'settled'
|
|
@@ -772,16 +723,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
772
723
|
)
|
|
773
724
|
ORDER BY queue_sequence ASC
|
|
774
725
|
LIMIT ${validated.handoff === void 0 ? 1 : validated.handoff.deferredSubmissionIds.length + 1}
|
|
775
|
-
`.pipe(Effect.mapError(sqlFailure(operation)))
|
|
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
|
-
});
|
|
726
|
+
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
785
727
|
const heads = yield* decodeSubmissionRows(operation, validated.threadId, headRows);
|
|
786
728
|
if (heads.length === 0) return Option.none();
|
|
787
729
|
let head = heads[0];
|
|
@@ -794,146 +736,26 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
794
736
|
}
|
|
795
737
|
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
738
|
}
|
|
797
|
-
if (head.submission_id !== handoff.submissionId || head.state !== "ready" && head.state !== "
|
|
739
|
+
if (head.submission_id !== handoff.submissionId || head.state !== "ready" && head.state !== "input-applied") return Option.none();
|
|
798
740
|
}
|
|
799
741
|
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
742
|
const inputPayload = yield* parseStoredJsonText(head.input_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", head.submission_id, error.message)));
|
|
875
743
|
return Option.some(yield* decodeClaim({
|
|
876
744
|
submissionId: head.submission_id,
|
|
877
745
|
attemptId,
|
|
878
|
-
ownershipToken,
|
|
879
|
-
producerEpoch,
|
|
880
|
-
leaseExpiresAt,
|
|
881
746
|
inputPayload
|
|
882
747
|
}).pipe(Effect.mapError(internalFailure(operation))));
|
|
883
748
|
}));
|
|
884
749
|
yield* hitFailpoint("ledger:claim:after", operation);
|
|
885
750
|
return claimed;
|
|
886
751
|
});
|
|
887
|
-
const
|
|
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) {
|
|
752
|
+
const markInputApplied = Effect.fnUntraced(function* (request) {
|
|
931
753
|
const operation = "ledger mark input applied";
|
|
932
754
|
const validated = yield* Schema.decodeEffect(Schema.toType(MarkInputAppliedRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
933
755
|
yield* hitFailpoint("ledger:mark-input-applied:before", operation);
|
|
934
756
|
yield* inWriteTransaction(operation, Effect.gen(function* () {
|
|
935
757
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
936
|
-
yield*
|
|
758
|
+
yield* requireOwner(operation, submission);
|
|
937
759
|
if (submission.input_applied_record_id !== null) {
|
|
938
760
|
if (submission.input_applied_record_id === validated.recordId && submission.input_applied_sequence === validated.sequence) return;
|
|
939
761
|
return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A different canonical input marker is already recorded for this Submission.");
|
|
@@ -944,122 +766,132 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
944
766
|
input_applied_record_id = ${validated.recordId},
|
|
945
767
|
input_applied_sequence = ${validated.sequence},
|
|
946
768
|
state = CASE
|
|
947
|
-
WHEN state IN ('admitted', 'ready'
|
|
769
|
+
WHEN state IN ('admitted', 'ready') THEN 'input-applied'
|
|
948
770
|
ELSE state
|
|
949
771
|
END
|
|
950
772
|
WHERE submission_id = ${validated.submissionId}
|
|
951
|
-
RETURNING
|
|
773
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
952
774
|
}));
|
|
953
775
|
yield* hitFailpoint("ledger:mark-input-applied:after", operation);
|
|
954
776
|
});
|
|
955
|
-
const
|
|
956
|
-
const operation = "
|
|
957
|
-
const
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
777
|
+
const publish = Effect.fn("DoSubmissionLedger.publishSettlement")(function* (input) {
|
|
778
|
+
const operation = "publish settlement";
|
|
779
|
+
const { request, record, settlement } = yield* validatePublication(input);
|
|
780
|
+
const prepared = yield* prepareCanonicalAppend(request.append).pipe(Effect.provideService(Crypto.Crypto, crypto));
|
|
781
|
+
const mapAppendError = (error) => ThreadStoreError.make({
|
|
782
|
+
operation,
|
|
783
|
+
message: error.message,
|
|
784
|
+
cause: error
|
|
785
|
+
});
|
|
786
|
+
yield* journal.prepareAppend(prepared.raw).pipe(Effect.mapError(mapAppendError));
|
|
787
|
+
return yield* owner.withProgress(request.append.threadId, owner.withMutation(Effect.gen(function* () {
|
|
788
|
+
yield* hitFailpoint("append:before", operation);
|
|
789
|
+
const result = yield* journal.withWriteTransaction(operation)(Effect.gen(function* () {
|
|
790
|
+
const submission = yield* requireSubmission(operation, request.submissionId);
|
|
791
|
+
if (submission.thread_id !== prepared.request.threadId) return yield* LedgerError.make({
|
|
792
|
+
operation,
|
|
793
|
+
message: "Settlement publication belongs to another Thread."
|
|
967
794
|
});
|
|
968
|
-
const
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
795
|
+
const snapshot = yield* decodeSubmissionSnapshot(operation, submission);
|
|
796
|
+
yield* validateCanonicalSettlement(record, snapshot);
|
|
797
|
+
const existing = yield* readCanonicalSettlement(operation, submission);
|
|
798
|
+
const thread = (yield* journal.getThread(prepared.request.threadId))[0];
|
|
799
|
+
if (thread === void 0) return yield* ThreadNotMaterialized.make({ threadId: prepared.request.threadId });
|
|
800
|
+
switch (request.authority._tag) {
|
|
801
|
+
case "Owned":
|
|
802
|
+
yield* requireOwner(operation, submission);
|
|
803
|
+
yield* validateCanonicalSettlement(record, {
|
|
804
|
+
submissionId: request.submissionId,
|
|
805
|
+
receiptId: snapshot.receiptId,
|
|
806
|
+
...settlement.outcome === "aborted" && settlement.runId === void 0 ? {} : { runId: runIdForSubmission(request.submissionId) }
|
|
807
|
+
});
|
|
808
|
+
break;
|
|
809
|
+
case "Joined": {
|
|
810
|
+
if (submission.state !== "joined" && !(submission.state === "settled" && existing !== void 0) || submission.joined_host_submission_id !== request.authority.hostSubmissionId) return yield* LedgerError.make({
|
|
811
|
+
operation,
|
|
812
|
+
message: "Submission is not joined to the named host."
|
|
813
|
+
});
|
|
814
|
+
const host = yield* requireSubmission(operation, request.authority.hostSubmissionId);
|
|
815
|
+
if (host.thread_id !== submission.thread_id) return yield* LedgerError.make({
|
|
816
|
+
operation,
|
|
817
|
+
message: "Joined host belongs to another Thread."
|
|
818
|
+
});
|
|
819
|
+
const canonicalHost = yield* readCanonicalSettlement(operation, host);
|
|
820
|
+
if (canonicalHost === void 0) return yield* LedgerError.make({
|
|
821
|
+
operation,
|
|
822
|
+
message: "Joined host has no canonical settlement."
|
|
823
|
+
});
|
|
824
|
+
yield* validateJoinedSettlement(settlement, canonicalHost.settlement);
|
|
825
|
+
break;
|
|
826
|
+
}
|
|
827
|
+
case "QueuedAbort":
|
|
828
|
+
if (submission.state !== "ready" && !(submission.state === "settled" && existing !== void 0) || settlement.outcome !== "aborted" || Option.isNone(yield* readAbortIntent(operation, request.submissionId)) || owner.hasOwner(submission.thread_id, request.submissionId)) return yield* LedgerError.make({
|
|
829
|
+
operation,
|
|
830
|
+
message: "Queued abort is no longer authorized."
|
|
831
|
+
});
|
|
832
|
+
yield* validateCanonicalSettlement(record, {
|
|
833
|
+
submissionId: request.submissionId,
|
|
834
|
+
receiptId: snapshot.receiptId,
|
|
835
|
+
runId: void 0
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
if (existing !== void 0) {
|
|
839
|
+
yield* journal.checkCommitIntent(prepared.raw);
|
|
840
|
+
return SettlementPublicationResult.make({
|
|
841
|
+
record: existing.record,
|
|
842
|
+
tailSequence: thread.tail_sequence,
|
|
843
|
+
replayed: true
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
if (submission.state === "settled") return yield* LedgerError.make({
|
|
847
|
+
operation,
|
|
848
|
+
message: "Finalized Submission has no canonical settlement."
|
|
849
|
+
});
|
|
850
|
+
const appended = yield* journal.appendPrepared(prepared.raw);
|
|
851
|
+
return SettlementPublicationResult.make({
|
|
973
852
|
record,
|
|
974
|
-
|
|
975
|
-
replayed:
|
|
853
|
+
tailSequence: appended.lastSequence,
|
|
854
|
+
replayed: appended.replayed
|
|
976
855
|
});
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
existingOutcome: submission.settled_outcome
|
|
856
|
+
})).pipe(Effect.mapError((error) => {
|
|
857
|
+
if (error._tag === "DoAppendConflict") return AppendConflict.make({
|
|
858
|
+
threadId: prepared.request.threadId,
|
|
859
|
+
batchId: prepared.request.batch.batchId,
|
|
860
|
+
reason: error.reason,
|
|
861
|
+
...error.actualTailSequence !== void 0 ? { actualTailSequence: error.actualTailSequence } : {}
|
|
984
862
|
});
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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;
|
|
863
|
+
return isDoStorageError(error) || error._tag === "DoStorageCorruptionError" || error._tag === "DoStorageFailpointError" || error._tag === "DoValueBoundExceeded" ? mapAppendError(error) : error;
|
|
864
|
+
}));
|
|
865
|
+
yield* hitFailpoint("append:after", operation);
|
|
866
|
+
return result;
|
|
867
|
+
})));
|
|
1033
868
|
});
|
|
1034
|
-
const validateFinalization = Effect.
|
|
869
|
+
const validateFinalization = Effect.fnUntraced(function* (validated, submission) {
|
|
1035
870
|
const operation = "ledger finalize settlement";
|
|
1036
|
-
|
|
871
|
+
const canonical = yield* readCanonicalSettlement(operation, submission);
|
|
872
|
+
if (canonical === void 0) return yield* LedgerError.make({
|
|
1037
873
|
operation,
|
|
1038
|
-
message: `No settlement
|
|
874
|
+
message: `No canonical settlement exists for submission ${validated.submissionId}.`
|
|
1039
875
|
});
|
|
1040
|
-
if (
|
|
876
|
+
if (canonical.settlement.settlementId !== validated.settlementId) return yield* SettlementConflict.make({
|
|
1041
877
|
submissionId: validated.submissionId,
|
|
1042
|
-
existingOutcome:
|
|
878
|
+
existingOutcome: canonical.settlement.outcome
|
|
1043
879
|
});
|
|
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
880
|
return {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
settlementFailure
|
|
881
|
+
...canonical,
|
|
882
|
+
settlementFailure: settlementFailureFromRecord(canonical.record)
|
|
1051
883
|
};
|
|
1052
884
|
});
|
|
1053
|
-
const replayFinalization = Effect.
|
|
885
|
+
const replayFinalization = Effect.fnUntraced(function* (validated, submission, { record, settlement, settlementFailure }) {
|
|
1054
886
|
const operation = "ledger finalize settlement";
|
|
1055
|
-
if (
|
|
887
|
+
if (submission.finalized_at === null || submission.settled_record_id !== record.recordId || submission.settled_outcome !== settlement.outcome) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "Settled projection disagrees with its canonical settlement.");
|
|
1056
888
|
return yield* decodeSettlement({
|
|
1057
889
|
submissionId: validated.submissionId,
|
|
1058
890
|
settlementId: validated.settlementId,
|
|
1059
891
|
receiptId: submission.receipt_id,
|
|
1060
|
-
outcome:
|
|
892
|
+
outcome: settlement.outcome,
|
|
1061
893
|
...settlementFailure === void 0 ? {} : { failure: settlementFailure },
|
|
1062
|
-
settledAt:
|
|
894
|
+
settledAt: submission.finalized_at
|
|
1063
895
|
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
1064
896
|
});
|
|
1065
897
|
const finalizeSettlement = Effect.fn("DoSubmissionLedger.finalizeSettlement")(function* (request) {
|
|
@@ -1069,20 +901,27 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1069
901
|
const replay = yield* state.read(Effect.gen(function* () {
|
|
1070
902
|
const submission = yield* readSubmission(operation, validated.submissionId);
|
|
1071
903
|
if (Option.isNone(submission) || submission.value.state !== "settled") return Option.none();
|
|
1072
|
-
const finalization = yield* validateFinalization(validated,
|
|
1073
|
-
return Option.some(
|
|
904
|
+
const finalization = yield* validateFinalization(validated, submission.value);
|
|
905
|
+
return Option.some({
|
|
906
|
+
settlement: yield* replayFinalization(validated, submission.value, finalization),
|
|
907
|
+
threadId: submission.value.thread_id
|
|
908
|
+
});
|
|
1074
909
|
}));
|
|
1075
910
|
if (Option.isSome(replay)) {
|
|
911
|
+
owner.closeSubmission(replay.value.threadId, validated.submissionId);
|
|
1076
912
|
yield* hitFailpoint("ledger:finalize-settlement:after", operation);
|
|
1077
|
-
return replay.value;
|
|
913
|
+
return replay.value.settlement;
|
|
1078
914
|
}
|
|
1079
915
|
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
916
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1083
|
-
|
|
917
|
+
const state = yield* validateFinalization(validated, submission);
|
|
918
|
+
const { settlement: canonicalSettlement, record: canonicalRecord, settlementFailure } = state;
|
|
919
|
+
if (submission.state === "settled") return {
|
|
920
|
+
settlement: yield* replayFinalization(validated, submission, state),
|
|
921
|
+
threadId: submission.thread_id
|
|
922
|
+
};
|
|
1084
923
|
const now = yield* currentInstant;
|
|
1085
|
-
const terminal = submission.worker_admission_json === null ? void 0 : workerTerminalFromRecord(yield* decodeSubmissionSnapshot(operation, submission),
|
|
924
|
+
const terminal = submission.worker_admission_json === null ? void 0 : workerTerminalFromRecord(yield* decodeSubmissionSnapshot(operation, submission), canonicalRecord);
|
|
1086
925
|
let sealedTerminal;
|
|
1087
926
|
if (terminal !== void 0) {
|
|
1088
927
|
if ((terminal === "completed" ? yield* sql`SELECT submission_id FROM effect_agent_submissions
|
|
@@ -1103,37 +942,33 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1103
942
|
}
|
|
1104
943
|
yield* sql`
|
|
1105
944
|
UPDATE effect_agent_submissions
|
|
1106
|
-
SET state = 'settled', settled_outcome = ${
|
|
945
|
+
SET state = 'settled', settled_outcome = ${canonicalSettlement.outcome},
|
|
946
|
+
settled_record_id = ${canonicalRecord.recordId}, finalized_at = ${now.iso}
|
|
1107
947
|
WHERE submission_id = ${validated.submissionId}
|
|
1108
|
-
RETURNING
|
|
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)));
|
|
948
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
1118
949
|
yield* recordProgress("submission", operation);
|
|
1119
950
|
if (sealedTerminal !== void 0) yield* retainWorkerSeal(submission.thread_id, sealedTerminal);
|
|
1120
|
-
return
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
951
|
+
return {
|
|
952
|
+
threadId: submission.thread_id,
|
|
953
|
+
settlement: yield* decodeSettlement({
|
|
954
|
+
submissionId: validated.submissionId,
|
|
955
|
+
settlementId: validated.settlementId,
|
|
956
|
+
receiptId: submission.receipt_id,
|
|
957
|
+
outcome: canonicalSettlement.outcome,
|
|
958
|
+
...settlementFailure === void 0 ? {} : { failure: settlementFailure },
|
|
959
|
+
settledAt: now.iso
|
|
960
|
+
}).pipe(Effect.mapError(internalFailure(operation)))
|
|
961
|
+
};
|
|
1128
962
|
}));
|
|
963
|
+
owner.closeSubmission(settlement.threadId, validated.submissionId);
|
|
1129
964
|
yield* hitFailpoint("ledger:finalize-settlement:after", operation);
|
|
1130
|
-
return settlement;
|
|
965
|
+
return settlement.settlement;
|
|
1131
966
|
});
|
|
1132
|
-
const inspectWorker = Effect.
|
|
967
|
+
const inspectWorker = Effect.fnUntraced(function* (threadId) {
|
|
1133
968
|
const operation = "inspect worker";
|
|
1134
969
|
return yield* state.transaction(Effect.gen(function* () {
|
|
1135
970
|
const read = Effect.fnUntraced(function* (active) {
|
|
1136
|
-
const rows = yield* sql`SELECT ${
|
|
971
|
+
const rows = yield* sql`SELECT ${submissionColumns} FROM effect_agent_submissions
|
|
1137
972
|
WHERE thread_id = ${threadId} ${active ? sql`AND state <> 'settled'` : sql``}
|
|
1138
973
|
ORDER BY queue_sequence ${active ? sql`ASC` : sql`DESC`} LIMIT 1`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1139
974
|
const decoded = yield* decodeSubmissionRows(operation, threadId, rows);
|
|
@@ -1150,7 +985,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1150
985
|
}).pipe(Effect.mapError(internalFailure(operation)));
|
|
1151
986
|
})).pipe(Effect.provideService(SqlClientService.SqlClient, sql), Effect.catchTag("SqlError", (cause) => sqlFailure(operation)(cause)));
|
|
1152
987
|
});
|
|
1153
|
-
const stopWorker = Effect.
|
|
988
|
+
const stopWorker = Effect.fnUntraced(function* (request) {
|
|
1154
989
|
const operation = "ledger stop worker";
|
|
1155
990
|
const validated = yield* Schema.decodeEffect(WorkerStopCommand)(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1156
991
|
return yield* inWriteTransaction(operation, Effect.gen(function* () {
|
|
@@ -1159,15 +994,12 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1159
994
|
const aborted = yield* sql`INSERT OR IGNORE INTO effect_agent_abort_intents (submission_id, author, reason, requested_at)
|
|
1160
995
|
SELECT submission_id, ${validated.author}, 'Worker owner stopped the worker', ${now.iso}
|
|
1161
996
|
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
997
|
if (sealed.length > 0 || aborted.length > 0) yield* recordProgress("control", operation);
|
|
1166
998
|
if (sealed.length > 0) yield* retainWorkerSeal(validated.threadId, null);
|
|
1167
|
-
return
|
|
999
|
+
return owner.hasOwner(validated.threadId) ? 1 : 0;
|
|
1168
1000
|
}));
|
|
1169
1001
|
});
|
|
1170
|
-
const requestAbort = Effect.
|
|
1002
|
+
const requestAbort = Effect.fnUntraced(function* (request) {
|
|
1171
1003
|
const operation = "ledger request abort";
|
|
1172
1004
|
const validated = yield* Schema.decodeEffect(Schema.toType(AbortCommand))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1173
1005
|
yield* hitFailpoint("ledger:request-abort:before", operation);
|
|
@@ -1222,7 +1054,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1222
1054
|
yield* hitFailpoint("ledger:request-abort:after", operation);
|
|
1223
1055
|
return intent;
|
|
1224
1056
|
});
|
|
1225
|
-
const claimJoining = Effect.
|
|
1057
|
+
const claimJoining = Effect.fnUntraced(function* (request) {
|
|
1226
1058
|
const operation = "ledger claim joining";
|
|
1227
1059
|
const validated = yield* Schema.decodeEffect(Schema.toType(ClaimJoiningRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1228
1060
|
yield* hitFailpoint("ledger:claim-joining:before", operation);
|
|
@@ -1232,16 +1064,15 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1232
1064
|
operation,
|
|
1233
1065
|
message: `Host submission ${validated.hostSubmissionId} does not belong to thread ${validated.threadId}.`
|
|
1234
1066
|
});
|
|
1235
|
-
yield*
|
|
1067
|
+
yield* requireOwner(operation, host);
|
|
1236
1068
|
if ((yield* cached(rows.workerStops.by("thread_id", validated.threadId), operation)).length > 0) return [];
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
SELECT ${sql.literal(SUBMISSION_COLUMNS)}
|
|
1069
|
+
const laterRows = yield* sql`
|
|
1070
|
+
SELECT ${submissionColumns}
|
|
1240
1071
|
FROM effect_agent_submissions
|
|
1241
1072
|
WHERE thread_id = ${validated.threadId}
|
|
1242
1073
|
AND queue_sequence > ${host.queue_sequence}
|
|
1243
1074
|
ORDER BY queue_sequence ASC
|
|
1244
|
-
`.pipe(Effect.mapError(sqlFailure(operation)))
|
|
1075
|
+
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1245
1076
|
const later = yield* decodeSubmissionRows(operation, validated.threadId, laterRows);
|
|
1246
1077
|
const claimed = [];
|
|
1247
1078
|
for (const row of later) {
|
|
@@ -1249,11 +1080,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1249
1080
|
if ((row.state === "joining" || row.state === "joined") && row.joined_host_submission_id === validated.hostSubmissionId) continue;
|
|
1250
1081
|
if (row.state === "settled" && row.settled_outcome === "aborted") continue;
|
|
1251
1082
|
if (row.state !== "ready") break;
|
|
1083
|
+
const canonical = yield* readCanonicalSettlement(operation, row);
|
|
1084
|
+
if (canonical?.settlement.outcome === "aborted") continue;
|
|
1085
|
+
if (canonical !== void 0) break;
|
|
1252
1086
|
yield* sql`
|
|
1253
1087
|
UPDATE effect_agent_submissions
|
|
1254
1088
|
SET state = 'joining', joined_host_submission_id = ${validated.hostSubmissionId}
|
|
1255
1089
|
WHERE submission_id = ${row.submission_id}
|
|
1256
|
-
RETURNING
|
|
1090
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
1257
1091
|
const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", row.submission_id, error.message)));
|
|
1258
1092
|
claimed.push(yield* decodeJoiningClaim({
|
|
1259
1093
|
submissionId: row.submission_id,
|
|
@@ -1266,7 +1100,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1266
1100
|
yield* hitFailpoint("ledger:claim-joining:after", operation);
|
|
1267
1101
|
return claims;
|
|
1268
1102
|
});
|
|
1269
|
-
const markJoined = Effect.
|
|
1103
|
+
const markJoined = Effect.fnUntraced(function* (request) {
|
|
1270
1104
|
const operation = "ledger mark joined";
|
|
1271
1105
|
const validated = yield* Schema.decodeEffect(Schema.toType(MarkJoinedRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1272
1106
|
yield* hitFailpoint("ledger:mark-joined:before", operation);
|
|
@@ -1277,7 +1111,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1277
1111
|
message: `Submission ${validated.submissionId} was never claimed for joining.`
|
|
1278
1112
|
});
|
|
1279
1113
|
const host = yield* requireSubmission(operation, submission.joined_host_submission_id);
|
|
1280
|
-
yield*
|
|
1114
|
+
yield* requireOwner(operation, host);
|
|
1281
1115
|
if (submission.input_applied_record_id !== null) {
|
|
1282
1116
|
if (submission.input_applied_record_id === validated.recordId && submission.input_applied_sequence === validated.sequence) return;
|
|
1283
1117
|
return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A different join marker is already recorded for this Submission.");
|
|
@@ -1293,25 +1127,37 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1293
1127
|
input_applied_sequence = ${validated.sequence},
|
|
1294
1128
|
state = 'joined'
|
|
1295
1129
|
WHERE submission_id = ${validated.submissionId}
|
|
1296
|
-
RETURNING
|
|
1130
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
1297
1131
|
}));
|
|
1298
1132
|
yield* hitFailpoint("ledger:mark-joined:after", operation);
|
|
1299
1133
|
});
|
|
1300
|
-
const revertJoining = Effect.
|
|
1134
|
+
const revertJoining = Effect.fnUntraced(function* (request) {
|
|
1301
1135
|
const operation = "ledger revert joining";
|
|
1302
1136
|
const validated = yield* Schema.decodeEffect(Schema.toType(RevertJoiningRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1303
1137
|
yield* hitFailpoint("ledger:revert-joining:before", operation);
|
|
1304
1138
|
yield* inWriteTransaction(operation, Effect.gen(function* () {
|
|
1305
|
-
|
|
1139
|
+
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1140
|
+
if (submission.state !== "joining") return;
|
|
1141
|
+
const guard = validated.guard;
|
|
1142
|
+
if (guard !== void 0) {
|
|
1143
|
+
if (submission.joined_host_submission_id !== guard.hostSubmissionId) return;
|
|
1144
|
+
const host = yield* requireSubmission(operation, guard.hostSubmissionId);
|
|
1145
|
+
if (host.thread_id !== submission.thread_id) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "The joined host belongs to another Thread.");
|
|
1146
|
+
if (host.state !== "settled") yield* requireOwner(operation, host).pipe(Effect.mapError((cause) => cause._tag === "OwnershipLost" ? LedgerError.make({
|
|
1147
|
+
operation,
|
|
1148
|
+
message: "Joining recovery no longer owns the linked host.",
|
|
1149
|
+
cause
|
|
1150
|
+
}) : cause));
|
|
1151
|
+
}
|
|
1306
1152
|
yield* sql`
|
|
1307
1153
|
UPDATE effect_agent_submissions
|
|
1308
1154
|
SET state = 'ready', joined_host_submission_id = NULL
|
|
1309
1155
|
WHERE submission_id = ${validated.submissionId}
|
|
1310
|
-
RETURNING
|
|
1156
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
1311
1157
|
}));
|
|
1312
1158
|
yield* hitFailpoint("ledger:revert-joining:after", operation);
|
|
1313
1159
|
});
|
|
1314
|
-
const suspend = Effect.
|
|
1160
|
+
const suspend = Effect.fnUntraced(function* (request) {
|
|
1315
1161
|
const operation = "ledger suspend";
|
|
1316
1162
|
const validated = yield* Schema.decodeEffect(Schema.toType(SuspendRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1317
1163
|
const reasonJson = yield* encodeSuspensionReasonText(validated.reason).pipe(Effect.mapError(internalFailure(operation)));
|
|
@@ -1325,16 +1171,19 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1325
1171
|
existingOutcome: submission.settled_outcome
|
|
1326
1172
|
});
|
|
1327
1173
|
}
|
|
1328
|
-
const
|
|
1329
|
-
if (
|
|
1174
|
+
const canonical = yield* readCanonicalSettlement(operation, submission);
|
|
1175
|
+
if (canonical !== void 0) return yield* SettlementConflict.make({
|
|
1330
1176
|
submissionId: validated.submissionId,
|
|
1331
|
-
existingOutcome:
|
|
1177
|
+
existingOutcome: canonical.settlement.outcome
|
|
1332
1178
|
});
|
|
1333
|
-
yield*
|
|
1179
|
+
yield* requireOwner(operation, submission);
|
|
1334
1180
|
if (validated.reason._tag === "ApprovalPending") {
|
|
1335
1181
|
const decisions = yield* readApprovalDecisions(operation, validated.submissionId);
|
|
1336
|
-
const decided = new
|
|
1337
|
-
if (validated.reason.toolCallIds
|
|
1182
|
+
const decided = new Map(decisions.map((row) => [row.tool_call_id, row.decision]));
|
|
1183
|
+
if (approvalDecisionsCover(validated.reason.toolCallIds, (toolCallId) => decided.get(toolCallId))) return {
|
|
1184
|
+
outcome: RESUME_IMMEDIATELY,
|
|
1185
|
+
threadId: submission.thread_id
|
|
1186
|
+
};
|
|
1338
1187
|
} else {
|
|
1339
1188
|
const markers = yield* readChildSettlementMarkers(operation, validated.submissionId);
|
|
1340
1189
|
const markerChildren = new Set(markers.map((row) => row.child_submission_id));
|
|
@@ -1343,7 +1192,10 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1343
1192
|
allSettled = false;
|
|
1344
1193
|
break;
|
|
1345
1194
|
}
|
|
1346
|
-
if (allSettled) return
|
|
1195
|
+
if (allSettled) return {
|
|
1196
|
+
outcome: RESUME_IMMEDIATELY,
|
|
1197
|
+
threadId: submission.thread_id
|
|
1198
|
+
};
|
|
1347
1199
|
}
|
|
1348
1200
|
const now = yield* currentInstant;
|
|
1349
1201
|
yield* sql`
|
|
@@ -1353,34 +1205,34 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1353
1205
|
suspended_reason_json = ${reasonJson},
|
|
1354
1206
|
suspended_at = ${now.iso}
|
|
1355
1207
|
WHERE submission_id = ${validated.submissionId}
|
|
1356
|
-
RETURNING
|
|
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)));
|
|
1208
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
1361
1209
|
yield* retainLifecycle(submission, {
|
|
1362
1210
|
_tag: "SubmissionSuspended",
|
|
1363
1211
|
submissionId: validated.submissionId,
|
|
1364
1212
|
reason: validated.reason
|
|
1365
1213
|
});
|
|
1366
|
-
return
|
|
1214
|
+
return {
|
|
1215
|
+
outcome: SUSPENDED,
|
|
1216
|
+
threadId: submission.thread_id
|
|
1217
|
+
};
|
|
1367
1218
|
}));
|
|
1219
|
+
if (outcome.outcome === SUSPENDED) owner.closeSubmission(outcome.threadId, validated.submissionId);
|
|
1368
1220
|
yield* hitFailpoint("ledger:suspend:after", operation);
|
|
1369
|
-
return outcome;
|
|
1221
|
+
return outcome.outcome;
|
|
1370
1222
|
});
|
|
1371
1223
|
/**
|
|
1372
|
-
* Once
|
|
1224
|
+
* Once one pending call is denied or every pending call has a decision intent,
|
|
1373
1225
|
* the lane wakes: suspended → input-applied, suspension cleared (plan §2.6). A
|
|
1374
1226
|
* WaitingForChild suspension wakes only through recordChildSettled. Runs inside the caller's
|
|
1375
1227
|
* write transaction.
|
|
1376
1228
|
*/
|
|
1377
|
-
const wakeSuspendedIfCovered = Effect.
|
|
1229
|
+
const wakeSuspendedIfCovered = Effect.fnUntraced(function* (operation, submission) {
|
|
1378
1230
|
if (submission.state !== "suspended" || submission.suspended_reason_json === null) return;
|
|
1379
1231
|
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
1232
|
if (reason._tag !== "ApprovalPending") return;
|
|
1381
1233
|
const decisions = yield* readApprovalDecisions(operation, submission.submission_id);
|
|
1382
|
-
const decided = new
|
|
1383
|
-
if (!reason.toolCallIds
|
|
1234
|
+
const decided = new Map(decisions.map((row) => [row.tool_call_id, row.decision]));
|
|
1235
|
+
if (!approvalDecisionsCover(reason.toolCallIds, (toolCallId) => decided.get(toolCallId))) return;
|
|
1384
1236
|
yield* sql`
|
|
1385
1237
|
UPDATE effect_agent_submissions
|
|
1386
1238
|
SET
|
|
@@ -1388,13 +1240,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1388
1240
|
suspended_reason_json = NULL,
|
|
1389
1241
|
suspended_at = NULL
|
|
1390
1242
|
WHERE submission_id = ${submission.submission_id}
|
|
1391
|
-
RETURNING
|
|
1243
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
1392
1244
|
yield* retainLifecycle(submission, {
|
|
1393
1245
|
_tag: "SubmissionResumed",
|
|
1394
1246
|
submissionId: yield* decodeSubmissionId(submission.submission_id).pipe(Effect.mapError(internalFailure(operation)))
|
|
1395
1247
|
});
|
|
1396
1248
|
});
|
|
1397
|
-
const recordApprovalDecision = Effect.
|
|
1249
|
+
const recordApprovalDecision = Effect.fnUntraced(function* (command) {
|
|
1398
1250
|
const operation = "ledger record approval decision";
|
|
1399
1251
|
const validated = yield* Schema.decodeEffect(Schema.toType(ApprovalDecisionCommand))(command).pipe(Effect.mapError(internalFailure(operation)));
|
|
1400
1252
|
yield* hitFailpoint("ledger:approval-decision:before", operation);
|
|
@@ -1448,7 +1300,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1448
1300
|
yield* hitFailpoint("ledger:approval-decision:after", operation);
|
|
1449
1301
|
return intent;
|
|
1450
1302
|
});
|
|
1451
|
-
const markUnknown = Effect.
|
|
1303
|
+
const markUnknown = Effect.fnUntraced(function* (request) {
|
|
1452
1304
|
const operation = "ledger mark unknown";
|
|
1453
1305
|
const validated = yield* Schema.decodeEffect(Schema.toType(MarkUnknownRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1454
1306
|
yield* hitFailpoint("ledger:mark-unknown:before", operation);
|
|
@@ -1461,10 +1313,10 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1461
1313
|
existingOutcome: submission.settled_outcome
|
|
1462
1314
|
});
|
|
1463
1315
|
}
|
|
1464
|
-
const
|
|
1465
|
-
if (
|
|
1316
|
+
const canonical = yield* readCanonicalSettlement(operation, submission);
|
|
1317
|
+
if (canonical !== void 0) return yield* SettlementConflict.make({
|
|
1466
1318
|
submissionId: validated.submissionId,
|
|
1467
|
-
existingOutcome:
|
|
1319
|
+
existingOutcome: canonical.settlement.outcome
|
|
1468
1320
|
});
|
|
1469
1321
|
const existingIds = yield* storedUnknownToolCallIds(operation, submission);
|
|
1470
1322
|
const known = new Set(existingIds);
|
|
@@ -1477,7 +1329,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1477
1329
|
unknown_reason = ${submission.unknown_reason ?? validated.reason},
|
|
1478
1330
|
unknown_tool_call_ids_json = ${idsJson}
|
|
1479
1331
|
WHERE submission_id = ${validated.submissionId}
|
|
1480
|
-
RETURNING
|
|
1332
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
1481
1333
|
if (submission.state !== "unknown") yield* retainLifecycle(submission, {
|
|
1482
1334
|
_tag: "SubmissionUnknown",
|
|
1483
1335
|
submissionId: validated.submissionId
|
|
@@ -1485,7 +1337,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1485
1337
|
}));
|
|
1486
1338
|
yield* hitFailpoint("ledger:mark-unknown:after", operation);
|
|
1487
1339
|
});
|
|
1488
|
-
const recordUnknownResolution = Effect.
|
|
1340
|
+
const recordUnknownResolution = Effect.fnUntraced(function* (command) {
|
|
1489
1341
|
const operation = "ledger record unknown resolution";
|
|
1490
1342
|
const validated = yield* Schema.decodeEffect(Schema.toType(UnknownResolutionCommand))(command).pipe(Effect.mapError(internalFailure(operation)));
|
|
1491
1343
|
const resolutionJson = yield* encodeUnknownResolutionText(validated.resolution).pipe(Effect.mapError(internalFailure(operation)));
|
|
@@ -1494,17 +1346,37 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1494
1346
|
const submission = yield* requireSubmission(operation, validated.submissionId);
|
|
1495
1347
|
if (submission.state === "settled") {
|
|
1496
1348
|
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({
|
|
1349
|
+
if (validated.resolution._tag !== "CompletedWithResult" && validated.resolution._tag !== "NeverHappened") return yield* SettlementConflict.make({
|
|
1498
1350
|
submissionId: validated.submissionId,
|
|
1499
1351
|
existingOutcome: submission.settled_outcome
|
|
1500
1352
|
});
|
|
1501
1353
|
}
|
|
1502
|
-
const
|
|
1354
|
+
const resolutions = yield* readUnknownResolutions(operation, validated.submissionId);
|
|
1355
|
+
const kind = unknownResolutionKind(validated.resolution);
|
|
1356
|
+
const existing = resolutions.find((row) => row.tool_call_id === validated.toolCallId && row.resolution_kind === kind);
|
|
1503
1357
|
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({
|
|
1358
|
+
if (existingIntent !== void 0 && !equivalentUnknownResolution(existingIntent.resolution, validated.resolution) || existing === void 0 && kind === "execution" && resolutions.some((row) => row.tool_call_id === validated.toolCallId && row.resolution_kind === "factual")) return yield* UnknownResolutionConflict.make({
|
|
1505
1359
|
submissionId: validated.submissionId,
|
|
1506
1360
|
toolCallId: validated.toolCallId
|
|
1507
1361
|
});
|
|
1362
|
+
if (existingIntent === void 0 && kind === "factual") {
|
|
1363
|
+
const runId = runIdForSubmission(validated.submissionId);
|
|
1364
|
+
const found = yield* sql`
|
|
1365
|
+
SELECT ${canonicalRecordJson(sql)} AS record_json FROM effect_agent_canonical_records
|
|
1366
|
+
WHERE thread_id = ${submission.thread_id}
|
|
1367
|
+
AND record_tag = 27
|
|
1368
|
+
AND run_id = ${runKey(sql, sql`${submission.thread_id}`, sql`${JSON.stringify(runId)}`)}
|
|
1369
|
+
AND tool_call_id = ${JSON.stringify(validated.toolCallId)}
|
|
1370
|
+
LIMIT 2
|
|
1371
|
+
`.pipe(Effect.mapError(sqlFailure(operation)));
|
|
1372
|
+
const outcomes = yield* decodeRows(Schema.Array(Schema.Struct({ record_json: BoundedStoredText })), "effect_agent_canonical_records", validated.toolCallId, found).pipe(Effect.mapError(internalFailure(operation)));
|
|
1373
|
+
const result = outcomes[0] === void 0 ? void 0 : (yield* decodeRecordEnvelopeText(outcomes[0].record_json).pipe(Effect.mapError(internalFailure(operation)))).payload;
|
|
1374
|
+
if (outcomes.length > 1 || result !== void 0 && (result._tag !== "ToolCallSettled" || result.runId !== runId || result.toolCallId !== validated.toolCallId)) return yield* corruptionFailure(operation, "effect_agent_canonical_records", validated.toolCallId, "Canonical Tool result identity is inconsistent.");
|
|
1375
|
+
if (result?._tag === "ToolCallSettled" && !unknownResolutionMatchesResult(validated.resolution, result)) return yield* UnknownResolutionConflict.make({
|
|
1376
|
+
submissionId: validated.submissionId,
|
|
1377
|
+
toolCallId: validated.toolCallId
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1508
1380
|
let resolved;
|
|
1509
1381
|
if (existingIntent !== void 0) resolved = existingIntent;
|
|
1510
1382
|
else {
|
|
@@ -1513,6 +1385,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1513
1385
|
INSERT INTO effect_agent_unknown_resolutions (
|
|
1514
1386
|
submission_id,
|
|
1515
1387
|
tool_call_id,
|
|
1388
|
+
resolution_kind,
|
|
1516
1389
|
author,
|
|
1517
1390
|
reason,
|
|
1518
1391
|
resolution_json,
|
|
@@ -1520,6 +1393,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1520
1393
|
) VALUES (
|
|
1521
1394
|
${validated.submissionId},
|
|
1522
1395
|
${validated.toolCallId},
|
|
1396
|
+
${kind},
|
|
1523
1397
|
${validated.author},
|
|
1524
1398
|
${validated.reason},
|
|
1525
1399
|
${resolutionJson},
|
|
@@ -1549,7 +1423,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1549
1423
|
unknown_reason = NULL,
|
|
1550
1424
|
unknown_tool_call_ids_json = NULL
|
|
1551
1425
|
WHERE submission_id = ${validated.submissionId}
|
|
1552
|
-
RETURNING
|
|
1426
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
1553
1427
|
yield* retainLifecycle(submission, {
|
|
1554
1428
|
_tag: "SubmissionResumed",
|
|
1555
1429
|
submissionId: validated.submissionId
|
|
@@ -1561,15 +1435,15 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1561
1435
|
yield* hitFailpoint("ledger:unknown-resolution:after", operation);
|
|
1562
1436
|
return intent;
|
|
1563
1437
|
});
|
|
1564
|
-
const recordChildSettled = Effect.
|
|
1438
|
+
const recordChildSettled = Effect.fnUntraced(function* (request) {
|
|
1565
1439
|
const operation = "ledger record child settled";
|
|
1566
1440
|
const validated = yield* Schema.decodeEffect(Schema.toType(ChildSettledNotification))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1567
1441
|
yield* hitFailpoint("ledger:child-settled:before", operation);
|
|
1568
1442
|
const outcome = yield* inWriteTransaction(operation, Effect.gen(function* () {
|
|
1569
1443
|
const parent = yield* requireSubmission(operation, validated.parentSubmissionId);
|
|
1570
1444
|
const child = yield* readSubmission(operation, validated.childSubmissionId);
|
|
1571
|
-
const
|
|
1572
|
-
if (Option.isSome(child) &&
|
|
1445
|
+
const childCanonical = Option.isSome(child) ? yield* readCanonicalSettlement(operation, child.value) : void 0;
|
|
1446
|
+
if (Option.isSome(child) && childCanonical === void 0) return yield* LedgerError.make({
|
|
1573
1447
|
operation,
|
|
1574
1448
|
message: `Child submission ${validated.childSubmissionId} has no recorded settlement.`
|
|
1575
1449
|
});
|
|
@@ -1583,7 +1457,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1583
1457
|
) VALUES (
|
|
1584
1458
|
${validated.parentSubmissionId},
|
|
1585
1459
|
${validated.childSubmissionId},
|
|
1586
|
-
${
|
|
1460
|
+
${childCanonical?.settlement.outcome ?? null},
|
|
1587
1461
|
${now.iso}
|
|
1588
1462
|
)
|
|
1589
1463
|
ON CONFLICT (parent_submission_id, child_submission_id) DO NOTHING
|
|
@@ -1602,7 +1476,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1602
1476
|
suspended_reason_json = NULL,
|
|
1603
1477
|
suspended_at = NULL
|
|
1604
1478
|
WHERE submission_id = ${validated.parentSubmissionId}
|
|
1605
|
-
RETURNING
|
|
1479
|
+
RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
|
|
1606
1480
|
yield* retainLifecycle(parent, {
|
|
1607
1481
|
_tag: "SubmissionResumed",
|
|
1608
1482
|
submissionId: validated.parentSubmissionId
|
|
@@ -1612,7 +1486,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1612
1486
|
yield* hitFailpoint("ledger:child-settled:after", operation);
|
|
1613
1487
|
return outcome;
|
|
1614
1488
|
});
|
|
1615
|
-
const reserveChildBudget = Effect.
|
|
1489
|
+
const reserveChildBudget = Effect.fnUntraced(function* (request) {
|
|
1616
1490
|
const operation = "ledger reserve child budget";
|
|
1617
1491
|
const validated = yield* Schema.decodeEffect(Schema.toType(ChildBudgetReservationRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1618
1492
|
const allocationJson = yield* encodePersistedJsonText(validated.allocation).pipe(Effect.mapError(internalFailure(operation)));
|
|
@@ -1638,7 +1512,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1638
1512
|
message: `Parent Tool Call ${validated.parentToolCallId} already owns reservation ${collision.value.reservation_id}.`
|
|
1639
1513
|
});
|
|
1640
1514
|
const parent = yield* requireSubmission(operation, validated.parentSubmissionId);
|
|
1641
|
-
yield*
|
|
1515
|
+
yield* requireOwner(operation, parent);
|
|
1642
1516
|
const now = yield* currentInstant;
|
|
1643
1517
|
yield* sql`
|
|
1644
1518
|
INSERT INTO effect_agent_child_reservations (
|
|
@@ -1669,7 +1543,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1669
1543
|
yield* hitFailpoint("ledger:child-reservation:after", operation);
|
|
1670
1544
|
return reserved;
|
|
1671
1545
|
});
|
|
1672
|
-
const attachChildToReservation = Effect.
|
|
1546
|
+
const attachChildToReservation = Effect.fnUntraced(function* (request) {
|
|
1673
1547
|
const operation = "ledger attach child to reservation";
|
|
1674
1548
|
const validated = yield* Schema.decodeEffect(Schema.toType(AttachChildToReservationRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1675
1549
|
yield* hitFailpoint("ledger:child-attach:before", operation);
|
|
@@ -1688,7 +1562,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1688
1562
|
});
|
|
1689
1563
|
}
|
|
1690
1564
|
const parent = yield* requireSubmission(operation, existing.value.parent_submission_id);
|
|
1691
|
-
yield*
|
|
1565
|
+
yield* requireOwner(operation, parent);
|
|
1692
1566
|
if (existing.value.status !== "reserved") return yield* ChildReservationConflict.make({
|
|
1693
1567
|
reservationId: validated.reservationId,
|
|
1694
1568
|
status: existing.value.status,
|
|
@@ -1706,7 +1580,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1706
1580
|
yield* hitFailpoint("ledger:child-attach:after", operation);
|
|
1707
1581
|
return attached;
|
|
1708
1582
|
});
|
|
1709
|
-
const beginChildBudgetRelease = Effect.
|
|
1583
|
+
const beginChildBudgetRelease = Effect.fnUntraced(function* (request) {
|
|
1710
1584
|
const operation = "ledger begin child budget release";
|
|
1711
1585
|
const validated = yield* Schema.decodeEffect(Schema.toType(BeginChildBudgetReleaseRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1712
1586
|
const accountingJson = yield* encodePersistedJsonText(validated.accounting).pipe(Effect.mapError(internalFailure(operation)));
|
|
@@ -1742,7 +1616,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1742
1616
|
yield* hitFailpoint("ledger:child-release-pending:after", operation);
|
|
1743
1617
|
return frozen;
|
|
1744
1618
|
});
|
|
1745
|
-
const releaseChildBudget = Effect.
|
|
1619
|
+
const releaseChildBudget = Effect.fnUntraced(function* (request) {
|
|
1746
1620
|
const operation = "ledger release child budget";
|
|
1747
1621
|
const validated = yield* Schema.decodeEffect(Schema.toType(ReleaseChildBudgetRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1748
1622
|
yield* hitFailpoint("ledger:child-release:before", operation);
|
|
@@ -1771,7 +1645,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1771
1645
|
yield* hitFailpoint("ledger:child-release:after", operation);
|
|
1772
1646
|
return released;
|
|
1773
1647
|
});
|
|
1774
|
-
const scanPage = Effect.
|
|
1648
|
+
const scanPage = Effect.fnUntraced(function* (cursor) {
|
|
1775
1649
|
const operation = "ledger scan nonterminal";
|
|
1776
1650
|
const rows = yield* (cursor === void 0 ? sql`
|
|
1777
1651
|
SELECT submission_id AS "submissionId", thread_id AS "threadId",
|
|
@@ -1816,7 +1690,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1816
1690
|
}))).pipe(Effect.mapError(internalFailure("ledger scan nonterminal")));
|
|
1817
1691
|
return Stream.fromIterable(work);
|
|
1818
1692
|
})));
|
|
1819
|
-
const readAbortIntentForSubmission = Effect.
|
|
1693
|
+
const readAbortIntentForSubmission = Effect.fnUntraced(function* (request) {
|
|
1820
1694
|
const operation = "ledger read abort intent";
|
|
1821
1695
|
const validated = yield* Schema.decodeEffect(Schema.toType(AbortIntentRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1822
1696
|
return yield* state.read(Effect.gen(function* () {
|
|
@@ -1825,38 +1699,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1825
1699
|
return Option.isNone(intent) ? void 0 : yield* abortIntentFromRow(operation, submission, validated.submissionId, intent.value);
|
|
1826
1700
|
}));
|
|
1827
1701
|
});
|
|
1828
|
-
const loadRecoverySnapshot = Effect.
|
|
1702
|
+
const loadRecoverySnapshot = Effect.fnUntraced(function* (request) {
|
|
1829
1703
|
const operation = "ledger load recovery snapshot";
|
|
1830
1704
|
const validated = yield* Schema.decodeEffect(Schema.toType(RecoverySnapshotRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
|
|
1831
1705
|
return yield* state.read(Effect.gen(function* () {
|
|
1832
1706
|
const submissionRow = yield* requireSubmission(operation, validated.submissionId);
|
|
1833
1707
|
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
1708
|
let inputApplied;
|
|
1843
1709
|
if (submissionRow.input_applied_record_id !== null && submissionRow.input_applied_sequence !== null) inputApplied = yield* decodeInputAppliedMarker({
|
|
1844
1710
|
recordId: submissionRow.input_applied_record_id,
|
|
1845
1711
|
sequence: submissionRow.input_applied_sequence
|
|
1846
1712
|
}).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
1713
|
let abortIntent;
|
|
1861
1714
|
const abortRow = yield* readAbortIntent(operation, validated.submissionId);
|
|
1862
1715
|
if (Option.isSome(abortRow)) abortIntent = yield* abortIntentFromRow(operation, submissionRow, validated.submissionId, abortRow.value);
|
|
@@ -1923,9 +1776,8 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1923
1776
|
...parentLinkage === void 0 ? {} : { parentLinkage },
|
|
1924
1777
|
...hostSubmissionId === void 0 ? {} : { hostSubmissionId },
|
|
1925
1778
|
...suspension === void 0 ? {} : { suspension },
|
|
1926
|
-
|
|
1779
|
+
hasOwner: owner.hasOwner(submissionRow.thread_id, submissionRow.submission_id),
|
|
1927
1780
|
...inputApplied === void 0 ? {} : { inputApplied },
|
|
1928
|
-
...reservation === void 0 ? {} : { reservation },
|
|
1929
1781
|
...abortIntent === void 0 ? {} : { abortIntent }
|
|
1930
1782
|
});
|
|
1931
1783
|
}));
|
|
@@ -1935,20 +1787,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1935
1787
|
admit,
|
|
1936
1788
|
markReady,
|
|
1937
1789
|
lookup,
|
|
1790
|
+
resolveFundingOwner,
|
|
1938
1791
|
resolveAdmission,
|
|
1939
|
-
|
|
1940
|
-
renewOwnership,
|
|
1941
|
-
releaseOwnership,
|
|
1792
|
+
selectHead,
|
|
1942
1793
|
markInputApplied,
|
|
1943
|
-
|
|
1944
|
-
finalizeSettlement,
|
|
1794
|
+
finalizeSettlement: (request) => owner.withMutation(finalizeSettlement(request)),
|
|
1945
1795
|
requestAbort,
|
|
1946
1796
|
stopWorker,
|
|
1947
1797
|
inspectWorker,
|
|
1948
1798
|
claimJoining,
|
|
1949
1799
|
markJoined,
|
|
1950
1800
|
revertJoining,
|
|
1951
|
-
suspend,
|
|
1801
|
+
suspend: (request) => owner.withMutation(suspend(request)),
|
|
1952
1802
|
recordApprovalDecision,
|
|
1953
1803
|
markUnknown,
|
|
1954
1804
|
recordUnknownResolution,
|
|
@@ -1960,17 +1810,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
|
|
|
1960
1810
|
scanNonterminal,
|
|
1961
1811
|
loadRecoverySnapshot,
|
|
1962
1812
|
readAbortIntent: readAbortIntentForSubmission
|
|
1963
|
-
}));
|
|
1813
|
+
})).pipe(Context.add(StorageOwner, owner), Context.add(SettlementPublisher, { publish }));
|
|
1964
1814
|
});
|
|
1965
1815
|
/**
|
|
1966
1816
|
* Durable Object SubmissionLedger implementation sharing the journal's private SQLite
|
|
1967
|
-
* database, storage-backed
|
|
1817
|
+
* database, storage-backed transactions, and scoped local owner.
|
|
1968
1818
|
* Configuration, failpoint, SQL, and Crypto authority stay visible in the input channel.
|
|
1969
1819
|
*/
|
|
1970
1820
|
const submissionLedgerLayer = Layer.effectContext(makeServices());
|
|
1971
1821
|
/**
|
|
1972
1822
|
* A composition-root convenience Layer for the durable Submission Ledger. Point it at the
|
|
1973
|
-
* same `ctx.storage` as the ThreadStore so
|
|
1823
|
+
* same `ctx.storage` as the ThreadStore so both share the same scoped storage owner.
|
|
1974
1824
|
*/
|
|
1975
1825
|
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
1826
|
//#endregion
|