@yielded/agent-storage-memory 0.1.0-beta.173 → 0.1.0-beta.174
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/MemoryMessageDeliveryStore.mjs +1 -1
- package/dist/MemoryMessageDeliveryStore.mjs.map +1 -1
- package/dist/MemorySubmissionLedger.mjs +1179 -1177
- package/dist/MemorySubmissionLedger.mjs.map +1 -1
- package/dist/MemoryThreadStore.mjs +14 -14
- package/dist/MemoryThreadStore.mjs.map +1 -1
- package/package.json +1 -1
- package/src/MemoryMessageDeliveryStore.ts +24 -25
- package/src/MemorySubmissionLedger.ts +49 -49
- package/src/MemoryThreadStore.ts +42 -31
|
@@ -272,1272 +272,1274 @@ function* threadRows(state, threadId, after = -1) {
|
|
|
272
272
|
if (stored !== void 0) yield stored;
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
275
|
+
const makeSubmissionLedger = Effect.fnUntraced(
|
|
276
|
+
/**
|
|
277
|
+
* Reference in-memory SubmissionLedger. Admission, ordered selection and canonical settlement
|
|
278
|
+
* share the Memory kernel's mutation gate. The kernel Scope owns local Run capabilities;
|
|
279
|
+
* selecting work does not write execution ownership into the ledger.
|
|
280
|
+
*
|
|
281
|
+
* Joining retains the contiguous ready prefix and exact host linkage. Canonical settlement
|
|
282
|
+
* authorizes finalization and child wake repair, while bound sessions reject late commands.
|
|
283
|
+
* Child wake checks read the child rows directly, without a separate notification marker.
|
|
284
|
+
* Admission resolution is strongly consistent; resolveAdmissionFault injects the
|
|
285
|
+
* Indeterminate outcome for existing caller conformance workflows.
|
|
286
|
+
*/
|
|
287
|
+
function* (options = {}) {
|
|
288
|
+
const journal = yield* MemoryThreadStoreKernel;
|
|
289
|
+
const crypto = yield* Crypto.Crypto;
|
|
290
|
+
const mintIdentity = (operation) => crypto.randomUUIDv4().pipe(Effect.flatMap(Schema.decodeEffect(MemoryIdentityNonce)), Effect.mapError((cause) => ledgerError(operation, "Cannot mint a Memory identity", cause)));
|
|
291
|
+
const state = yield* Ref.make({
|
|
292
|
+
byThread: /* @__PURE__ */ new Map(),
|
|
293
|
+
receipts: /* @__PURE__ */ new Map(),
|
|
294
|
+
joinsByHost: /* @__PURE__ */ new Map(),
|
|
295
|
+
childrenByParent: /* @__PURE__ */ new Map(),
|
|
296
|
+
submissions: /* @__PURE__ */ new Map(),
|
|
297
|
+
admissionIndex: /* @__PURE__ */ new Map(),
|
|
298
|
+
lanes: /* @__PURE__ */ new Map(),
|
|
299
|
+
childReservations: /* @__PURE__ */ new Map(),
|
|
300
|
+
freshAdmissionSequence: 0,
|
|
301
|
+
stoppedWorkers: /* @__PURE__ */ new Map(),
|
|
302
|
+
latestByThread: /* @__PURE__ */ new Map(),
|
|
303
|
+
activeByThread: /* @__PURE__ */ new Map()
|
|
304
|
+
});
|
|
305
|
+
yield* journal.registerWorkOwner("admissions", {
|
|
306
|
+
threads: (request) => Ref.get(state).pipe(Effect.map((current) => {
|
|
307
|
+
const candidates = boundedWorkSelection(request.limit, (id) => id, request.afterThreadId);
|
|
308
|
+
for (const [threadId, ids] of current.activeByThread) if (ids.size > 0) candidates.add(threadId);
|
|
309
|
+
const threadIds = candidates.values;
|
|
310
|
+
const afterThreadId = threadIds.length === request.limit ? threadIds.at(-1) : void 0;
|
|
311
|
+
return {
|
|
312
|
+
threadIds,
|
|
313
|
+
...afterThreadId === void 0 ? {} : { afterThreadId }
|
|
314
|
+
};
|
|
315
|
+
})),
|
|
316
|
+
page: Effect.fnUntraced(function* (threadId, after, limit) {
|
|
317
|
+
const current = yield* Ref.get(state);
|
|
318
|
+
const candidates = boundedWorkSelection(limit + 1, (id) => id, after);
|
|
319
|
+
for (const id of current.activeByThread.get(threadId) ?? []) candidates.add(id);
|
|
320
|
+
const ids = candidates.values;
|
|
321
|
+
const entries = [];
|
|
322
|
+
for (const id of ids.slice(0, limit)) {
|
|
323
|
+
const stored = current.submissions.get(id);
|
|
324
|
+
if (stored === void 0 || stored.row.threadId !== threadId || stored.row.state === "settled") return yield* ThreadStoreError.make({
|
|
325
|
+
operation: "admission work",
|
|
326
|
+
message: "Active admission index is incomplete or corrupt"
|
|
327
|
+
});
|
|
328
|
+
const entry = admissionWork(stored.row);
|
|
329
|
+
entries.push(stored.suspension === void 0 ? entry : {
|
|
330
|
+
...entry,
|
|
331
|
+
wait: stored.suspension.reason._tag === "WaitingForChild" ? "child" : "approval"
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
const next = ids.length > limit ? ids[limit - 1] : void 0;
|
|
335
|
+
return {
|
|
336
|
+
entries,
|
|
337
|
+
...next === void 0 ? {} : { after: next }
|
|
338
|
+
};
|
|
339
|
+
})
|
|
340
|
+
});
|
|
341
|
+
const admissionFence = yield* SubmissionAdmissionFence;
|
|
342
|
+
const capabilities = Effect.succeed(LedgerCapabilities.make({ durability: "non-durable" }));
|
|
343
|
+
const admit = Effect.fnUntraced(function* (unvalidated) {
|
|
344
|
+
const request = yield* validate(AdmissionRequest, "admit", unvalidated);
|
|
345
|
+
const workerAdmissionJson = request.workerAdmission === void 0 ? void 0 : yield* Schema.encodeEffect(Schema.fromJsonString(WorkerAdmission))(request.workerAdmission).pipe(Effect.mapError(() => ledgerError("admit", "Invalid worker admission metadata")));
|
|
346
|
+
const messageAdmissionJson = request.messageAdmission === void 0 ? void 0 : yield* Schema.encodeEffect(Schema.fromJsonString(InputMessage))(request.messageAdmission).pipe(Effect.mapError(() => ledgerError("admit", "Invalid message admission metadata")));
|
|
347
|
+
yield* journal.checkThreadCapacity(request.threadId).pipe(Effect.mapError((cause) => ledgerError("admit", cause.message, cause)));
|
|
348
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
349
|
+
const identity = (yield* Ref.get(state)).admissionIndex.has(admissionKey(request.threadId, request.principal, request.idempotencyKey)) ? Result.fail(ledgerError("admit", "Retained admission disappeared")) : yield* Effect.result(mintIdentity("admit"));
|
|
350
|
+
const services = yield* Effect.context();
|
|
351
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
352
|
+
const key = admissionKey(request.threadId, request.principal, request.idempotencyKey);
|
|
353
|
+
const existingId = current.admissionIndex.get(key);
|
|
354
|
+
if (existingId !== void 0) {
|
|
355
|
+
const existing = current.submissions.get(existingId);
|
|
356
|
+
if (existing === void 0) return [failure(ledgerError("admit", "Admission index references a missing Submission")), current];
|
|
357
|
+
if (existing.row.inputDigest !== request.inputDigest || !sameParentLinkage(existing.row.parentLinkage, request.parentLinkage) || existing.row.admissionGroup !== request.admissionGroup || !Schema.toEquivalence(Schema.optional(WorkerAdmission))(existing.row.workerAdmissionJson === void 0 ? void 0 : Schema.decodeSync(Schema.fromJsonString(WorkerAdmission))(existing.row.workerAdmissionJson), request.workerAdmission) || !Schema.toEquivalence(Schema.optional(InputMessage))(existing.row.messageAdmissionJson === void 0 ? void 0 : Schema.decodeSync(Schema.fromJsonString(InputMessage))(existing.row.messageAdmissionJson), request.messageAdmission) || !Schema.toEquivalence(Schema.optional(Schema.Json))(existing.row.admissionFence, request.admissionFence)) return [failure(AdmissionConflict.make({
|
|
358
|
+
threadId: request.threadId,
|
|
359
|
+
principal: request.principal,
|
|
360
|
+
idempotencyKey: request.idempotencyKey,
|
|
361
|
+
existingInputDigest: existing.row.inputDigest,
|
|
362
|
+
attemptedInputDigest: request.inputDigest
|
|
363
|
+
})), current];
|
|
364
|
+
return [success(AdmissionResult.make({
|
|
365
|
+
submissionId: existing.row.submissionId,
|
|
366
|
+
receiptId: existing.row.receiptId,
|
|
367
|
+
queueSequence: existing.row.queueSequence,
|
|
368
|
+
state: existing.row.state,
|
|
369
|
+
replayed: true
|
|
370
|
+
})), current];
|
|
371
|
+
}
|
|
372
|
+
if (current.stoppedWorkers.has(request.threadId)) return [failure(AdmissionPolicyError.make({
|
|
373
|
+
reason: "refused",
|
|
374
|
+
code: "worker-stopped"
|
|
375
|
+
})), current];
|
|
376
|
+
const first = threadRows(current, request.threadId).next().value;
|
|
377
|
+
if (first !== void 0) {
|
|
378
|
+
const previous = first.row.workerAdmissionJson === void 0 ? void 0 : Schema.decodeSync(Schema.fromJsonString(WorkerAdmission))(first.row.workerAdmissionJson);
|
|
379
|
+
if (!Schema.toEquivalence(Schema.optional(WorkerAdmission.fields.origin))(previous?.origin, request.workerAdmission?.origin)) return [failure(AdmissionPolicyError.make({
|
|
380
|
+
reason: "refused",
|
|
381
|
+
code: "worker-origin-conflict"
|
|
382
|
+
})), current];
|
|
383
|
+
}
|
|
384
|
+
const checked = Effect.runSyncExitWith(services)(admissionFence.check(request));
|
|
385
|
+
if (Exit.isFailure(checked)) return [{
|
|
386
|
+
_tag: "cause",
|
|
387
|
+
cause: checked.cause
|
|
388
|
+
}, current];
|
|
389
|
+
if (request.admissionGroup !== void 0 && Array.from(current.activeByThread.get(request.threadId) ?? []).some((id) => current.submissions.get(id)?.row.admissionGroup === request.admissionGroup)) return [failure(AdmissionPolicyError.make({
|
|
390
|
+
reason: "occupied",
|
|
391
|
+
code: "admission-group"
|
|
392
|
+
})), current];
|
|
393
|
+
if (Result.isFailure(identity)) return [failure(identity.failure), current];
|
|
394
|
+
const lane = current.lanes.get(request.threadId) ?? { nextQueueSequence: 1 };
|
|
395
|
+
const freshAdmissionSequence = current.freshAdmissionSequence + 1;
|
|
396
|
+
if (!Number.isSafeInteger(freshAdmissionSequence)) return [failure(ledgerError("admit", "Fresh admission sequence is exhausted")), current];
|
|
397
|
+
const orderedIdentity = `${String(freshAdmissionSequence).padStart(16, "0")}-${identity.success}`;
|
|
398
|
+
const submissionId = decodeSubmissionId(`submission-memory-${orderedIdentity}`);
|
|
399
|
+
const receiptId = decodeReceiptId(`receipt-memory-${orderedIdentity}`);
|
|
400
|
+
if (current.submissions.has(submissionId) || current.receipts.has(receiptId)) return [failure(ledgerError("admit", "Fresh admission identity already exists")), current];
|
|
401
|
+
const row = {
|
|
402
|
+
submissionId,
|
|
357
403
|
threadId: request.threadId,
|
|
404
|
+
queueSequence: decodeQueueSequence(lane.nextQueueSequence),
|
|
358
405
|
principal: request.principal,
|
|
359
406
|
idempotencyKey: request.idempotencyKey,
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
407
|
+
agentId: request.agentId,
|
|
408
|
+
agentDigests: request.agentDigests,
|
|
409
|
+
deploymentId: request.deploymentId,
|
|
410
|
+
inputPayload: request.inputPayload,
|
|
411
|
+
inputDigest: request.inputDigest,
|
|
412
|
+
receiptId,
|
|
413
|
+
state: "admitted",
|
|
414
|
+
settledOutcome: void 0,
|
|
415
|
+
createdAtMillis: nowMillis,
|
|
416
|
+
readyAtMillis: void 0,
|
|
417
|
+
parentLinkage: request.parentLinkage,
|
|
418
|
+
...workerAdmissionJson === void 0 ? {} : { workerAdmissionJson },
|
|
419
|
+
...messageAdmissionJson === void 0 ? {} : { messageAdmissionJson },
|
|
420
|
+
...request.admissionGroup === void 0 ? {} : { admissionGroup: request.admissionGroup },
|
|
421
|
+
...request.admissionFence === void 0 ? {} : { admissionFence: request.admissionFence }
|
|
422
|
+
};
|
|
423
|
+
const stored = {
|
|
424
|
+
row,
|
|
425
|
+
inputApplied: void 0,
|
|
426
|
+
finalization: void 0,
|
|
427
|
+
abortIntent: void 0,
|
|
428
|
+
joinedHostSubmissionId: void 0,
|
|
429
|
+
suspension: void 0,
|
|
430
|
+
unknownMark: void 0,
|
|
431
|
+
approvalDecisions: /* @__PURE__ */ new Map(),
|
|
432
|
+
unknownResolutions: /* @__PURE__ */ new Map()
|
|
433
|
+
};
|
|
434
|
+
if (!admissionFitsTransfer(request.threadId, toAdmission(row))) return [failure(transferCapacityError("admit")), current];
|
|
435
|
+
journal.initializeWork(request.threadId);
|
|
436
|
+
const admissionIndex = current.admissionIndex;
|
|
437
|
+
const lanes = current.lanes.set(request.threadId, { nextQueueSequence: lane.nextQueueSequence + 1 });
|
|
363
438
|
return [success(AdmissionResult.make({
|
|
364
|
-
submissionId:
|
|
365
|
-
receiptId:
|
|
366
|
-
queueSequence:
|
|
367
|
-
state:
|
|
368
|
-
replayed:
|
|
369
|
-
})),
|
|
439
|
+
submissionId: row.submissionId,
|
|
440
|
+
receiptId: row.receiptId,
|
|
441
|
+
queueSequence: row.queueSequence,
|
|
442
|
+
state: row.state,
|
|
443
|
+
replayed: false
|
|
444
|
+
})), {
|
|
445
|
+
...withSubmission(current, stored),
|
|
446
|
+
admissionIndex,
|
|
447
|
+
lanes,
|
|
448
|
+
freshAdmissionSequence
|
|
449
|
+
}];
|
|
450
|
+
});
|
|
451
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
452
|
+
if (decision._tag === "cause") {
|
|
453
|
+
for (const reason of decision.cause.reasons) if (Cause.isDieReason(reason) && Cause.isAsyncFiberError(reason.defect)) {
|
|
454
|
+
yield* Fiber.interrupt(reason.defect.fiber);
|
|
455
|
+
return yield* AdmissionPolicyError.make({
|
|
456
|
+
reason: "unavailable",
|
|
457
|
+
code: "synchronous-memory-policy-required"
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
return yield* Effect.failCause(decision.cause);
|
|
370
461
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
const
|
|
378
|
-
if (
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
462
|
+
return decision.value;
|
|
463
|
+
}, Effect.uninterruptible);
|
|
464
|
+
const markReady = Effect.fnUntraced(function* (unvalidated) {
|
|
465
|
+
const request = yield* validate(MarkReadyRequest, "markReady", unvalidated);
|
|
466
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
467
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
468
|
+
const stored = current.submissions.get(request.submissionId);
|
|
469
|
+
if (stored === void 0) return [failure(ledgerError("markReady", `Unknown Submission ${request.submissionId}`)), current];
|
|
470
|
+
if (stored.row.state !== "admitted") return [success(void 0), current];
|
|
471
|
+
return [success(void 0), withSubmission(current, {
|
|
472
|
+
...stored,
|
|
473
|
+
row: {
|
|
474
|
+
...stored.row,
|
|
475
|
+
state: "ready",
|
|
476
|
+
readyAtMillis: nowMillis
|
|
477
|
+
}
|
|
478
|
+
})];
|
|
479
|
+
});
|
|
480
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
481
|
+
});
|
|
482
|
+
const lookup = Effect.fnUntraced(function* (unvalidated) {
|
|
483
|
+
const request = yield* validate(SubmissionLookup, "lookup", unvalidated);
|
|
484
|
+
const current = yield* Ref.get(state);
|
|
485
|
+
const submissionId = request._tag === "SubmissionLookupById" ? request.submissionId : current.admissionIndex.get(admissionKey(request.threadId, request.principal, request.idempotencyKey));
|
|
486
|
+
const stored = submissionId === void 0 ? void 0 : current.submissions.get(submissionId);
|
|
487
|
+
return stored === void 0 ? Option.none() : Option.some(toSnapshot(stored.row));
|
|
488
|
+
});
|
|
489
|
+
const resolveAdmission = Effect.fnUntraced(function* (unvalidated) {
|
|
490
|
+
const request = yield* validate(SubmissionLookupByKey, "resolveAdmission", unvalidated);
|
|
491
|
+
if (options.resolveAdmissionFault !== void 0) {
|
|
492
|
+
const fault = yield* options.resolveAdmissionFault;
|
|
493
|
+
if (Option.isSome(fault)) return AdmissionIndeterminate.make({ reason: fault.value });
|
|
382
494
|
}
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}, current];
|
|
388
|
-
if (request.admissionGroup !== void 0 && Array.from(current.activeByThread.get(request.threadId) ?? []).some((id) => current.submissions.get(id)?.row.admissionGroup === request.admissionGroup)) return [failure(AdmissionPolicyError.make({
|
|
389
|
-
reason: "occupied",
|
|
390
|
-
code: "admission-group"
|
|
391
|
-
})), current];
|
|
392
|
-
if (Result.isFailure(identity)) return [failure(identity.failure), current];
|
|
393
|
-
const lane = current.lanes.get(request.threadId) ?? { nextQueueSequence: 1 };
|
|
394
|
-
const freshAdmissionSequence = current.freshAdmissionSequence + 1;
|
|
395
|
-
if (!Number.isSafeInteger(freshAdmissionSequence)) return [failure(ledgerError("admit", "Fresh admission sequence is exhausted")), current];
|
|
396
|
-
const orderedIdentity = `${String(freshAdmissionSequence).padStart(16, "0")}-${identity.success}`;
|
|
397
|
-
const submissionId = decodeSubmissionId(`submission-memory-${orderedIdentity}`);
|
|
398
|
-
const receiptId = decodeReceiptId(`receipt-memory-${orderedIdentity}`);
|
|
399
|
-
if (current.submissions.has(submissionId) || current.receipts.has(receiptId)) return [failure(ledgerError("admit", "Fresh admission identity already exists")), current];
|
|
400
|
-
const row = {
|
|
401
|
-
submissionId,
|
|
402
|
-
threadId: request.threadId,
|
|
403
|
-
queueSequence: decodeQueueSequence(lane.nextQueueSequence),
|
|
404
|
-
principal: request.principal,
|
|
405
|
-
idempotencyKey: request.idempotencyKey,
|
|
406
|
-
agentId: request.agentId,
|
|
407
|
-
agentDigests: request.agentDigests,
|
|
408
|
-
deploymentId: request.deploymentId,
|
|
409
|
-
inputPayload: request.inputPayload,
|
|
410
|
-
inputDigest: request.inputDigest,
|
|
411
|
-
receiptId,
|
|
412
|
-
state: "admitted",
|
|
413
|
-
settledOutcome: void 0,
|
|
414
|
-
createdAtMillis: nowMillis,
|
|
415
|
-
readyAtMillis: void 0,
|
|
416
|
-
parentLinkage: request.parentLinkage,
|
|
417
|
-
...workerAdmissionJson === void 0 ? {} : { workerAdmissionJson },
|
|
418
|
-
...messageAdmissionJson === void 0 ? {} : { messageAdmissionJson },
|
|
419
|
-
...request.admissionGroup === void 0 ? {} : { admissionGroup: request.admissionGroup },
|
|
420
|
-
...request.admissionFence === void 0 ? {} : { admissionFence: request.admissionFence }
|
|
421
|
-
};
|
|
422
|
-
const stored = {
|
|
423
|
-
row,
|
|
424
|
-
inputApplied: void 0,
|
|
425
|
-
finalization: void 0,
|
|
426
|
-
abortIntent: void 0,
|
|
427
|
-
joinedHostSubmissionId: void 0,
|
|
428
|
-
suspension: void 0,
|
|
429
|
-
unknownMark: void 0,
|
|
430
|
-
approvalDecisions: /* @__PURE__ */ new Map(),
|
|
431
|
-
unknownResolutions: /* @__PURE__ */ new Map()
|
|
432
|
-
};
|
|
433
|
-
if (!admissionFitsTransfer(request.threadId, toAdmission(row))) return [failure(transferCapacityError("admit")), current];
|
|
434
|
-
journal.initializeWork(request.threadId);
|
|
435
|
-
const admissionIndex = current.admissionIndex;
|
|
436
|
-
const lanes = current.lanes.set(request.threadId, { nextQueueSequence: lane.nextQueueSequence + 1 });
|
|
437
|
-
return [success(AdmissionResult.make({
|
|
438
|
-
submissionId: row.submissionId,
|
|
439
|
-
receiptId: row.receiptId,
|
|
440
|
-
queueSequence: row.queueSequence,
|
|
441
|
-
state: row.state,
|
|
442
|
-
replayed: false
|
|
443
|
-
})), {
|
|
444
|
-
...withSubmission(current, stored),
|
|
445
|
-
admissionIndex,
|
|
446
|
-
lanes,
|
|
447
|
-
freshAdmissionSequence
|
|
448
|
-
}];
|
|
495
|
+
const current = yield* Ref.get(state);
|
|
496
|
+
const submissionId = current.admissionIndex.get(admissionKey(request.threadId, request.principal, request.idempotencyKey));
|
|
497
|
+
const stored = submissionId === void 0 ? void 0 : current.submissions.get(submissionId);
|
|
498
|
+
return stored === void 0 ? AdmissionNotAdmitted.make() : AdmissionAdmitted.make({ submission: toSnapshot(stored.row) });
|
|
449
499
|
});
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
return
|
|
455
|
-
|
|
456
|
-
|
|
500
|
+
const selectHead = Effect.fn("MemorySubmissionLedger.selectHead")((unvalidated) => Effect.gen(function* () {
|
|
501
|
+
const request = yield* validate(ClaimRequest, "claim", unvalidated);
|
|
502
|
+
const identity = yield* mintIdentity("claim");
|
|
503
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
504
|
+
if (journal.owner.hasOwner(request.threadId)) return [success(Option.none()), current];
|
|
505
|
+
let head = findHead(current, request.threadId);
|
|
506
|
+
if (request.handoff !== void 0) {
|
|
507
|
+
const handoff = request.handoff;
|
|
508
|
+
const candidates = Array.from(current.activeByThread.get(request.threadId) ?? [], (id) => current.submissions.get(id)).filter((entry) => entry !== void 0 && !(entry.row.state === "unknown" && entry.abortIntent === void 0)).sort((a, b) => a.row.queueSequence - b.row.queueSequence);
|
|
509
|
+
for (const candidate of candidates) {
|
|
510
|
+
if (candidate.row.submissionId === handoff.submissionId) {
|
|
511
|
+
head = candidate;
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
if (!handoff.deferredSubmissionIds.includes(candidate.row.submissionId) || candidate.row.state !== "input-applied" || candidate.abortIntent !== void 0) return [success(Option.none()), current];
|
|
515
|
+
}
|
|
516
|
+
if (head?.row.submissionId !== handoff.submissionId || head.row.state !== "ready" && head.row.state !== "input-applied") return [success(Option.none()), current];
|
|
517
|
+
}
|
|
518
|
+
if (head === void 0) return [success(Option.none()), current];
|
|
519
|
+
if (BLOCKED_HEAD_STATES.has(head.row.state)) return [success(Option.none()), current];
|
|
520
|
+
return [success(Option.some(Claim.make({
|
|
521
|
+
submissionId: head.row.submissionId,
|
|
522
|
+
attemptId: decodeAttemptId(`attempt-memory-${identity}`),
|
|
523
|
+
inputPayload: head.row.inputPayload
|
|
524
|
+
}))), current];
|
|
525
|
+
});
|
|
526
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
527
|
+
return decision.value;
|
|
528
|
+
}));
|
|
529
|
+
const markInputApplied = Effect.fnUntraced(function* (unvalidated) {
|
|
530
|
+
const request = yield* validate(MarkInputAppliedRequest, "markInputApplied", unvalidated);
|
|
531
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
532
|
+
const stored = current.submissions.get(request.submissionId);
|
|
533
|
+
if (stored === void 0) return [failure(ledgerError("markInputApplied", `Unknown Submission ${request.submissionId}`)), current];
|
|
534
|
+
if (!journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId)) return [failure(ownershipLost(stored)), current];
|
|
535
|
+
if (stored.inputApplied !== void 0) {
|
|
536
|
+
if (stored.inputApplied.recordId === request.recordId && stored.inputApplied.sequence === request.sequence) return [success(void 0), current];
|
|
537
|
+
return [failure(ledgerError("markInputApplied", `A different canonical input marker is already recorded for Submission ${request.submissionId}`)), current];
|
|
538
|
+
}
|
|
539
|
+
const marker = InputAppliedMarker.make({
|
|
540
|
+
recordId: request.recordId,
|
|
541
|
+
sequence: request.sequence
|
|
457
542
|
});
|
|
458
|
-
|
|
459
|
-
return yield* Effect.failCause(decision.cause);
|
|
460
|
-
}
|
|
461
|
-
return decision.value;
|
|
462
|
-
}, Effect.uninterruptible);
|
|
463
|
-
const markReady = Effect.fnUntraced(function* (unvalidated) {
|
|
464
|
-
const request = yield* validate(MarkReadyRequest, "markReady", unvalidated);
|
|
465
|
-
const nowMillis = yield* Clock.currentTimeMillis;
|
|
466
|
-
const decision = yield* Ref.modify(state, (current) => {
|
|
467
|
-
const stored = current.submissions.get(request.submissionId);
|
|
468
|
-
if (stored === void 0) return [failure(ledgerError("markReady", `Unknown Submission ${request.submissionId}`)), current];
|
|
469
|
-
if (stored.row.state !== "admitted") return [success(void 0), current];
|
|
470
|
-
return [success(void 0), withSubmission(current, {
|
|
471
|
-
...stored,
|
|
472
|
-
row: {
|
|
543
|
+
const row = STATE_RANK[stored.row.state] < STATE_RANK["input-applied"] ? {
|
|
473
544
|
...stored.row,
|
|
474
|
-
state: "
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
545
|
+
state: "input-applied"
|
|
546
|
+
} : stored.row;
|
|
547
|
+
return [success(void 0), withSubmission(current, {
|
|
548
|
+
...stored,
|
|
549
|
+
row,
|
|
550
|
+
inputApplied: marker
|
|
551
|
+
})];
|
|
552
|
+
});
|
|
553
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
478
554
|
});
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
555
|
+
const canonicalSettlement = Effect.fnUntraced(function* (submission) {
|
|
556
|
+
const record = yield* journal.record(submission.threadId, submissionSettlementRecordId(submission.submissionId));
|
|
557
|
+
if (record === void 0) return void 0;
|
|
558
|
+
return {
|
|
559
|
+
record,
|
|
560
|
+
settlement: yield* validateCanonicalSettlement(record, submission)
|
|
561
|
+
};
|
|
562
|
+
});
|
|
563
|
+
const publish = Effect.fn("MemorySettlementPublisher.publish")(function* (input) {
|
|
564
|
+
const { request, record, settlement } = yield* validatePublication(input);
|
|
565
|
+
const prepared = yield* journal.prepareAppend(request.append);
|
|
566
|
+
return yield* journal.owner.withProgress(request.append.threadId, journal.withMutation(Effect.gen(function* () {
|
|
567
|
+
const current = yield* Ref.get(state);
|
|
568
|
+
const stored = current.submissions.get(request.submissionId);
|
|
569
|
+
if (stored === void 0) return yield* ledgerError("publish settlement", "Unknown Submission");
|
|
570
|
+
if (stored.row.threadId !== prepared.request.threadId) return yield* ledgerError("publish settlement", "Publication targets another Thread");
|
|
571
|
+
yield* validateCanonicalSettlement(record, stored.row);
|
|
572
|
+
const existing = yield* canonicalSettlement(stored.row);
|
|
573
|
+
const tail = yield* journal.tail(stored.row.threadId);
|
|
574
|
+
switch (request.authority._tag) {
|
|
575
|
+
case "Owned":
|
|
576
|
+
if (!journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId)) return yield* ownershipLost(stored);
|
|
577
|
+
yield* validateCanonicalSettlement(record, {
|
|
578
|
+
submissionId: request.submissionId,
|
|
579
|
+
receiptId: stored.row.receiptId,
|
|
580
|
+
...settlement.outcome === "aborted" && settlement.runId === void 0 ? {} : { runId: runIdForSubmission(request.submissionId) }
|
|
581
|
+
});
|
|
582
|
+
break;
|
|
583
|
+
case "Joined": {
|
|
584
|
+
if (stored.row.state !== "joined" && !(stored.row.state === "settled" && existing !== void 0) || stored.joinedHostSubmissionId !== request.authority.hostSubmissionId) return yield* ledgerError("publish settlement", "Submission is not joined to this host");
|
|
585
|
+
const host = current.submissions.get(request.authority.hostSubmissionId);
|
|
586
|
+
const canonicalHost = host === void 0 || host.row.threadId !== stored.row.threadId ? void 0 : yield* canonicalSettlement(host.row);
|
|
587
|
+
if (canonicalHost === void 0) return yield* ledgerError("publish settlement", "Joined host has no canonical settlement");
|
|
588
|
+
yield* validateJoinedSettlement(settlement, canonicalHost.settlement);
|
|
511
589
|
break;
|
|
512
590
|
}
|
|
513
|
-
|
|
591
|
+
case "QueuedAbort":
|
|
592
|
+
if (stored.row.state !== "ready" && !(stored.row.state === "settled" && existing !== void 0) || stored.abortIntent === void 0 || journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId) || settlement.outcome !== "aborted") return yield* ledgerError("publish settlement", "Submission is not an unclaimed queued abort");
|
|
593
|
+
yield* validateCanonicalSettlement(record, {
|
|
594
|
+
submissionId: request.submissionId,
|
|
595
|
+
receiptId: stored.row.receiptId,
|
|
596
|
+
runId: void 0
|
|
597
|
+
});
|
|
514
598
|
}
|
|
515
|
-
if (
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
const markInputApplied = Effect.fnUntraced(function* (unvalidated) {
|
|
529
|
-
const request = yield* validate(MarkInputAppliedRequest, "markInputApplied", unvalidated);
|
|
530
|
-
const decision = yield* Ref.modify(state, (current) => {
|
|
531
|
-
const stored = current.submissions.get(request.submissionId);
|
|
532
|
-
if (stored === void 0) return [failure(ledgerError("markInputApplied", `Unknown Submission ${request.submissionId}`)), current];
|
|
533
|
-
if (!journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId)) return [failure(ownershipLost(stored)), current];
|
|
534
|
-
if (stored.inputApplied !== void 0) {
|
|
535
|
-
if (stored.inputApplied.recordId === request.recordId && stored.inputApplied.sequence === request.sequence) return [success(void 0), current];
|
|
536
|
-
return [failure(ledgerError("markInputApplied", `A different canonical input marker is already recorded for Submission ${request.submissionId}`)), current];
|
|
537
|
-
}
|
|
538
|
-
const marker = InputAppliedMarker.make({
|
|
539
|
-
recordId: request.recordId,
|
|
540
|
-
sequence: request.sequence
|
|
541
|
-
});
|
|
542
|
-
const row = STATE_RANK[stored.row.state] < STATE_RANK["input-applied"] ? {
|
|
543
|
-
...stored.row,
|
|
544
|
-
state: "input-applied"
|
|
545
|
-
} : stored.row;
|
|
546
|
-
return [success(void 0), withSubmission(current, {
|
|
547
|
-
...stored,
|
|
548
|
-
row,
|
|
549
|
-
inputApplied: marker
|
|
550
|
-
})];
|
|
599
|
+
if (existing !== void 0) return SettlementPublicationResult.make({
|
|
600
|
+
record: existing.record,
|
|
601
|
+
tailSequence: tail.tailSequence,
|
|
602
|
+
replayed: true
|
|
603
|
+
});
|
|
604
|
+
if (stored.row.state === "settled") return yield* ledgerError("publish settlement", "Finalized Submission has no canonical settlement");
|
|
605
|
+
const appended = yield* journal.appendPrepared(prepared);
|
|
606
|
+
return SettlementPublicationResult.make({
|
|
607
|
+
record,
|
|
608
|
+
tailSequence: appended.lastSequence,
|
|
609
|
+
replayed: false
|
|
610
|
+
});
|
|
611
|
+
})));
|
|
551
612
|
});
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
const tail = yield* journal.tail(stored.row.threadId);
|
|
573
|
-
switch (request.authority._tag) {
|
|
574
|
-
case "Owned":
|
|
575
|
-
if (!journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId)) return yield* ownershipLost(stored);
|
|
576
|
-
yield* validateCanonicalSettlement(record, {
|
|
577
|
-
submissionId: request.submissionId,
|
|
613
|
+
const finalizeSettlement = Effect.fn("MemorySubmissionLedger.finalizeSettlement")((unvalidated) => Effect.gen(function* () {
|
|
614
|
+
const request = yield* validate(SettlementFinalization, "finalizeSettlement", unvalidated);
|
|
615
|
+
const before = (yield* Ref.get(state)).submissions.get(request.submissionId);
|
|
616
|
+
const canonical = before === void 0 ? void 0 : yield* canonicalSettlement(before.row);
|
|
617
|
+
if (canonical === void 0) return yield* ledgerError("finalizeSettlement", "Submission has no canonical settlement");
|
|
618
|
+
const { record, settlement } = canonical;
|
|
619
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
620
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
621
|
+
const stored = current.submissions.get(request.submissionId);
|
|
622
|
+
if (stored === void 0) return [failure(ledgerError("finalizeSettlement", `Unknown Submission ${request.submissionId}`)), current];
|
|
623
|
+
const settlementFailure = settlementFailureFromRecord(record);
|
|
624
|
+
if (settlement.settlementId !== request.settlementId) return [failure(SettlementConflict.make({
|
|
625
|
+
submissionId: request.submissionId,
|
|
626
|
+
existingOutcome: settlement.outcome
|
|
627
|
+
})), current];
|
|
628
|
+
if (stored.finalization !== void 0) {
|
|
629
|
+
if (stored.row.state !== "settled" || stored.row.settledOutcome !== settlement.outcome || stored.finalization.settlementId !== request.settlementId || stored.finalization.recordId !== record.recordId) return [failure(ledgerError("finalizeSettlement", "Finalized identity disagrees with canonical settlement")), current];
|
|
630
|
+
return [success(Settlement.make({
|
|
631
|
+
submissionId: stored.row.submissionId,
|
|
632
|
+
settlementId: settlement.settlementId,
|
|
578
633
|
receiptId: stored.row.receiptId,
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (stored.row.state !== "joined" && !(stored.row.state === "settled" && existing !== void 0) || stored.joinedHostSubmissionId !== request.authority.hostSubmissionId) return yield* ledgerError("publish settlement", "Submission is not joined to this host");
|
|
584
|
-
const host = current.submissions.get(request.authority.hostSubmissionId);
|
|
585
|
-
const canonicalHost = host === void 0 || host.row.threadId !== stored.row.threadId ? void 0 : yield* canonicalSettlement(host.row);
|
|
586
|
-
if (canonicalHost === void 0) return yield* ledgerError("publish settlement", "Joined host has no canonical settlement");
|
|
587
|
-
yield* validateJoinedSettlement(settlement, canonicalHost.settlement);
|
|
588
|
-
break;
|
|
634
|
+
outcome: settlement.outcome,
|
|
635
|
+
...settlementFailure === void 0 ? {} : { failure: settlementFailure },
|
|
636
|
+
settledAt: utc(stored.finalization.finalizedAtMillis)
|
|
637
|
+
})), current];
|
|
589
638
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
639
|
+
if (stored.row.state === "settled") return [failure(ledgerError("finalizeSettlement", "Finalized timestamp is missing")), current];
|
|
640
|
+
const terminal = stored.row.workerAdmissionJson === void 0 ? void 0 : workerTerminalFromRecord(toSnapshot(stored.row), record);
|
|
641
|
+
const latestId = current.latestByThread.get(stored.row.threadId);
|
|
642
|
+
const latest = latestId === void 0 ? void 0 : current.submissions.get(latestId);
|
|
643
|
+
const pendingNewer = terminal === "completed" && latest !== void 0 && latest.row.queueSequence > stored.row.queueSequence && !(latest.joinedHostSubmissionId === stored.row.submissionId && latest.inputApplied !== void 0);
|
|
644
|
+
let sealed = current;
|
|
645
|
+
if (terminal !== void 0 && !pendingNewer && !current.stoppedWorkers.has(stored.row.threadId)) {
|
|
646
|
+
const submissions = current.submissions;
|
|
647
|
+
if (!transferPageFits(stored.row.threadId)) return [failure(transferCapacityError("finalizeSettlement")), current];
|
|
648
|
+
for (const apply of [false, true]) for (const id of current.activeByThread.get(stored.row.threadId) ?? []) {
|
|
649
|
+
const other = submissions.get(id);
|
|
650
|
+
if (other === void 0) continue;
|
|
651
|
+
if (id !== stored.row.submissionId && !(other.joinedHostSubmissionId === stored.row.submissionId && other.inputApplied !== void 0) && other.abortIntent === void 0) {
|
|
652
|
+
const abortIntent = AbortIntent.make({
|
|
653
|
+
submissionId: id,
|
|
654
|
+
author: stored.row.principal,
|
|
655
|
+
reason: `Worker assignment ${terminal}`,
|
|
656
|
+
requestedAt: utc(nowMillis)
|
|
657
|
+
});
|
|
658
|
+
if (!apply && !abortFitsTransfer(other.row.threadId, abortIntent)) return [failure(transferCapacityError("finalizeSettlement")), current];
|
|
659
|
+
if (apply) withSubmission(current, {
|
|
660
|
+
...other,
|
|
661
|
+
abortIntent
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
sealed = {
|
|
666
|
+
...current,
|
|
667
|
+
submissions,
|
|
668
|
+
stoppedWorkers: current.stoppedWorkers.set(stored.row.threadId, terminal)
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
const next = withSubmission(sealed, {
|
|
672
|
+
...stored,
|
|
673
|
+
row: {
|
|
674
|
+
...stored.row,
|
|
675
|
+
state: "settled",
|
|
676
|
+
settledOutcome: settlement.outcome
|
|
677
|
+
},
|
|
678
|
+
finalization: {
|
|
679
|
+
settlementId: settlement.settlementId,
|
|
680
|
+
recordId: record.recordId,
|
|
681
|
+
finalizedAtMillis: nowMillis
|
|
682
|
+
}
|
|
683
|
+
});
|
|
629
684
|
return [success(Settlement.make({
|
|
630
685
|
submissionId: stored.row.submissionId,
|
|
631
686
|
settlementId: settlement.settlementId,
|
|
632
687
|
receiptId: stored.row.receiptId,
|
|
633
688
|
outcome: settlement.outcome,
|
|
634
689
|
...settlementFailure === void 0 ? {} : { failure: settlementFailure },
|
|
635
|
-
settledAt: utc(
|
|
636
|
-
})),
|
|
637
|
-
}
|
|
638
|
-
if (
|
|
639
|
-
|
|
640
|
-
|
|
690
|
+
settledAt: utc(nowMillis)
|
|
691
|
+
})), next];
|
|
692
|
+
});
|
|
693
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
694
|
+
if (before !== void 0) journal.owner.closeSubmission(before.row.threadId, request.submissionId);
|
|
695
|
+
return decision.value;
|
|
696
|
+
}));
|
|
697
|
+
const inspectWorker = Effect.fnUntraced(function* (threadId) {
|
|
698
|
+
const current = yield* Ref.get(state);
|
|
699
|
+
const latestId = current.latestByThread.get(threadId);
|
|
641
700
|
const latest = latestId === void 0 ? void 0 : current.submissions.get(latestId);
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
701
|
+
const active = [...current.activeByThread.get(threadId) ?? []].flatMap((id) => {
|
|
702
|
+
const row = current.submissions.get(id);
|
|
703
|
+
return row === void 0 ? [] : [row];
|
|
704
|
+
}).sort((a, b) => a.row.queueSequence - b.row.queueSequence)[0];
|
|
705
|
+
const terminal = current.stoppedWorkers.get(threadId);
|
|
706
|
+
return WorkerLedgerState.make({
|
|
707
|
+
latest: latest === void 0 ? null : toSnapshot(latest.row),
|
|
708
|
+
active: active === void 0 ? null : toSnapshot(active.row),
|
|
709
|
+
stopped: current.stoppedWorkers.has(threadId),
|
|
710
|
+
...terminal === void 0 ? {} : { terminal }
|
|
711
|
+
});
|
|
712
|
+
});
|
|
713
|
+
const stopWorker = Effect.fnUntraced(function* (unvalidated) {
|
|
714
|
+
const request = yield* validate(WorkerStopCommand, "stopWorker", unvalidated);
|
|
715
|
+
const now = yield* Clock.currentTimeMillis;
|
|
716
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
645
717
|
const submissions = current.submissions;
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
if (
|
|
718
|
+
let owned = 0;
|
|
719
|
+
if (!current.stoppedWorkers.has(request.threadId) && !transferPageFits(request.threadId)) return [failure(transferCapacityError("stopWorker")), current];
|
|
720
|
+
for (const apply of [false, true]) for (const id of current.activeByThread.get(request.threadId) ?? []) {
|
|
721
|
+
const stored = submissions.get(id);
|
|
722
|
+
if (stored === void 0) continue;
|
|
723
|
+
if (!apply && journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId)) owned++;
|
|
724
|
+
if (stored.abortIntent === void 0) {
|
|
651
725
|
const abortIntent = AbortIntent.make({
|
|
652
726
|
submissionId: id,
|
|
653
|
-
author:
|
|
654
|
-
reason:
|
|
655
|
-
requestedAt: utc(
|
|
727
|
+
author: request.author,
|
|
728
|
+
reason: "Worker owner stopped the worker",
|
|
729
|
+
requestedAt: utc(now)
|
|
656
730
|
});
|
|
657
|
-
if (!apply && !abortFitsTransfer(
|
|
731
|
+
if (!apply && !abortFitsTransfer(stored.row.threadId, abortIntent)) return [failure(transferCapacityError("stopWorker")), current];
|
|
658
732
|
if (apply) withSubmission(current, {
|
|
659
|
-
...
|
|
733
|
+
...stored,
|
|
660
734
|
abortIntent
|
|
661
735
|
});
|
|
662
736
|
}
|
|
663
737
|
}
|
|
664
|
-
|
|
738
|
+
return [success(owned), {
|
|
665
739
|
...current,
|
|
666
740
|
submissions,
|
|
667
|
-
stoppedWorkers: current.stoppedWorkers.set(
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
const next = withSubmission(sealed, {
|
|
671
|
-
...stored,
|
|
672
|
-
row: {
|
|
673
|
-
...stored.row,
|
|
674
|
-
state: "settled",
|
|
675
|
-
settledOutcome: settlement.outcome
|
|
676
|
-
},
|
|
677
|
-
finalization: {
|
|
678
|
-
settlementId: settlement.settlementId,
|
|
679
|
-
recordId: record.recordId,
|
|
680
|
-
finalizedAtMillis: nowMillis
|
|
681
|
-
}
|
|
741
|
+
stoppedWorkers: current.stoppedWorkers.has(request.threadId) ? current.stoppedWorkers : current.stoppedWorkers.set(request.threadId, void 0)
|
|
742
|
+
}];
|
|
682
743
|
});
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
settlementId: settlement.settlementId,
|
|
686
|
-
receiptId: stored.row.receiptId,
|
|
687
|
-
outcome: settlement.outcome,
|
|
688
|
-
...settlementFailure === void 0 ? {} : { failure: settlementFailure },
|
|
689
|
-
settledAt: utc(nowMillis)
|
|
690
|
-
})), next];
|
|
744
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
745
|
+
return decision.value;
|
|
691
746
|
});
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
747
|
+
const requestAbort = Effect.fnUntraced(function* (unvalidated) {
|
|
748
|
+
const request = yield* validate(AbortCommand, "requestAbort", unvalidated);
|
|
749
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
750
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
751
|
+
const stored = current.submissions.get(request.submissionId);
|
|
752
|
+
if (stored === void 0) return [failure(ledgerError("requestAbort", `Unknown Submission ${request.submissionId}`)), current];
|
|
753
|
+
if (stored.row.state === "joined") {
|
|
754
|
+
if (stored.joinedHostSubmissionId === void 0) return [failure(ledgerError("requestAbort", `Joined Submission ${request.submissionId} is missing its host linkage`)), current];
|
|
755
|
+
return [failure(JoinedToHost.make({
|
|
756
|
+
submissionId: request.submissionId,
|
|
757
|
+
hostSubmissionId: stored.joinedHostSubmissionId
|
|
758
|
+
})), current];
|
|
759
|
+
}
|
|
760
|
+
if (stored.row.state === "settled") {
|
|
761
|
+
if (stored.row.settledOutcome === void 0) return [failure(ledgerError("requestAbort", `Settled Submission ${request.submissionId} is missing its outcome`)), current];
|
|
762
|
+
return [failure(SettlementConflict.make({
|
|
763
|
+
submissionId: request.submissionId,
|
|
764
|
+
existingOutcome: stored.row.settledOutcome
|
|
765
|
+
})), current];
|
|
766
|
+
}
|
|
767
|
+
if (stored.abortIntent !== void 0) return [success(stored.abortIntent), current];
|
|
768
|
+
const intent = AbortIntent.make({
|
|
769
|
+
submissionId: request.submissionId,
|
|
770
|
+
author: request.author,
|
|
771
|
+
reason: request.reason,
|
|
772
|
+
requestedAt: utc(nowMillis)
|
|
773
|
+
});
|
|
774
|
+
if (!abortFitsTransfer(stored.row.threadId, intent)) return [failure(transferCapacityError("requestAbort")), current];
|
|
775
|
+
return [success(intent), withSubmission(current, {
|
|
776
|
+
...stored,
|
|
777
|
+
abortIntent: intent
|
|
778
|
+
})];
|
|
779
|
+
});
|
|
780
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
781
|
+
return decision.value;
|
|
710
782
|
});
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
if (
|
|
783
|
+
const claimJoining = Effect.fnUntraced(function* (unvalidated) {
|
|
784
|
+
const request = yield* validate(ClaimJoiningRequest, "claimJoining", unvalidated);
|
|
785
|
+
const published = /* @__PURE__ */ new Map();
|
|
786
|
+
const before = yield* Ref.get(state);
|
|
787
|
+
for (const id of before.activeByThread.get(request.threadId) ?? []) {
|
|
788
|
+
const stored = before.submissions.get(id);
|
|
789
|
+
if (stored === void 0 || stored.row.state !== "ready") continue;
|
|
790
|
+
const canonical = yield* canonicalSettlement(stored.row);
|
|
791
|
+
if (canonical !== void 0) published.set(stored.row.submissionId, canonical.settlement.outcome);
|
|
792
|
+
}
|
|
793
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
794
|
+
if (current.stoppedWorkers.has(request.threadId)) return [success([]), current];
|
|
795
|
+
const host = current.submissions.get(request.hostSubmissionId);
|
|
796
|
+
if (host === void 0) return [failure(ledgerError("claimJoining", `Unknown Submission ${request.hostSubmissionId}`)), current];
|
|
797
|
+
if (host.row.threadId !== request.threadId) return [failure(ledgerError("claimJoining", `Host Submission ${request.hostSubmissionId} does not belong to Thread ${request.threadId}`)), current];
|
|
798
|
+
if (!journal.owner.hasOwner(host.row.threadId, host.row.submissionId)) return [failure(ownershipLost(host)), current];
|
|
799
|
+
const later = threadRows(current, request.threadId, host.row.queueSequence);
|
|
800
|
+
const claims = [];
|
|
801
|
+
const submissions = current.submissions;
|
|
802
|
+
for (const stored of later) {
|
|
803
|
+
if (claims.length >= request.maxCount) break;
|
|
804
|
+
if ((stored.row.state === "joining" || stored.row.state === "joined") && stored.joinedHostSubmissionId === request.hostSubmissionId) continue;
|
|
805
|
+
if (stored.row.state === "settled" && stored.row.settledOutcome === "aborted") continue;
|
|
806
|
+
if (stored.row.state !== "ready") break;
|
|
807
|
+
const terminal = published.get(stored.row.submissionId);
|
|
808
|
+
if (terminal === "aborted") continue;
|
|
809
|
+
if (terminal !== void 0) break;
|
|
810
|
+
withSubmission(current, {
|
|
732
811
|
...stored,
|
|
733
|
-
|
|
812
|
+
row: {
|
|
813
|
+
...stored.row,
|
|
814
|
+
state: "joining"
|
|
815
|
+
},
|
|
816
|
+
joinedHostSubmissionId: request.hostSubmissionId
|
|
734
817
|
});
|
|
818
|
+
claims.push(JoiningClaim.make({
|
|
819
|
+
submissionId: stored.row.submissionId,
|
|
820
|
+
queueSequence: stored.row.queueSequence,
|
|
821
|
+
inputPayload: stored.row.inputPayload
|
|
822
|
+
}));
|
|
735
823
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
stoppedWorkers: current.stoppedWorkers.has(request.threadId) ? current.stoppedWorkers : current.stoppedWorkers.set(request.threadId, void 0)
|
|
741
|
-
}];
|
|
742
|
-
});
|
|
743
|
-
if (decision._tag === "failure") return yield* decision.error;
|
|
744
|
-
return decision.value;
|
|
745
|
-
});
|
|
746
|
-
const requestAbort = Effect.fnUntraced(function* (unvalidated) {
|
|
747
|
-
const request = yield* validate(AbortCommand, "requestAbort", unvalidated);
|
|
748
|
-
const nowMillis = yield* Clock.currentTimeMillis;
|
|
749
|
-
const decision = yield* Ref.modify(state, (current) => {
|
|
750
|
-
const stored = current.submissions.get(request.submissionId);
|
|
751
|
-
if (stored === void 0) return [failure(ledgerError("requestAbort", `Unknown Submission ${request.submissionId}`)), current];
|
|
752
|
-
if (stored.row.state === "joined") {
|
|
753
|
-
if (stored.joinedHostSubmissionId === void 0) return [failure(ledgerError("requestAbort", `Joined Submission ${request.submissionId} is missing its host linkage`)), current];
|
|
754
|
-
return [failure(JoinedToHost.make({
|
|
755
|
-
submissionId: request.submissionId,
|
|
756
|
-
hostSubmissionId: stored.joinedHostSubmissionId
|
|
757
|
-
})), current];
|
|
758
|
-
}
|
|
759
|
-
if (stored.row.state === "settled") {
|
|
760
|
-
if (stored.row.settledOutcome === void 0) return [failure(ledgerError("requestAbort", `Settled Submission ${request.submissionId} is missing its outcome`)), current];
|
|
761
|
-
return [failure(SettlementConflict.make({
|
|
762
|
-
submissionId: request.submissionId,
|
|
763
|
-
existingOutcome: stored.row.settledOutcome
|
|
764
|
-
})), current];
|
|
765
|
-
}
|
|
766
|
-
if (stored.abortIntent !== void 0) return [success(stored.abortIntent), current];
|
|
767
|
-
const intent = AbortIntent.make({
|
|
768
|
-
submissionId: request.submissionId,
|
|
769
|
-
author: request.author,
|
|
770
|
-
reason: request.reason,
|
|
771
|
-
requestedAt: utc(nowMillis)
|
|
824
|
+
return [success(claims), {
|
|
825
|
+
...current,
|
|
826
|
+
submissions
|
|
827
|
+
}];
|
|
772
828
|
});
|
|
773
|
-
if (
|
|
774
|
-
return
|
|
775
|
-
...stored,
|
|
776
|
-
abortIntent: intent
|
|
777
|
-
})];
|
|
829
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
830
|
+
return decision.value;
|
|
778
831
|
});
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
const later = threadRows(current, request.threadId, host.row.queueSequence);
|
|
799
|
-
const claims = [];
|
|
800
|
-
const submissions = current.submissions;
|
|
801
|
-
for (const stored of later) {
|
|
802
|
-
if (claims.length >= request.maxCount) break;
|
|
803
|
-
if ((stored.row.state === "joining" || stored.row.state === "joined") && stored.joinedHostSubmissionId === request.hostSubmissionId) continue;
|
|
804
|
-
if (stored.row.state === "settled" && stored.row.settledOutcome === "aborted") continue;
|
|
805
|
-
if (stored.row.state !== "ready") break;
|
|
806
|
-
const terminal = published.get(stored.row.submissionId);
|
|
807
|
-
if (terminal === "aborted") continue;
|
|
808
|
-
if (terminal !== void 0) break;
|
|
809
|
-
withSubmission(current, {
|
|
832
|
+
const markJoined = Effect.fnUntraced(function* (unvalidated) {
|
|
833
|
+
const request = yield* validate(MarkJoinedRequest, "markJoined", unvalidated);
|
|
834
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
835
|
+
const stored = current.submissions.get(request.submissionId);
|
|
836
|
+
if (stored === void 0) return [failure(ledgerError("markJoined", `Unknown Submission ${request.submissionId}`)), current];
|
|
837
|
+
if (stored.joinedHostSubmissionId === void 0) return [failure(ledgerError("markJoined", `Submission ${request.submissionId} was never claimed for joining`)), current];
|
|
838
|
+
const host = current.submissions.get(stored.joinedHostSubmissionId);
|
|
839
|
+
if (host === void 0) return [failure(ledgerError("markJoined", `Host Submission ${stored.joinedHostSubmissionId} is missing`)), current];
|
|
840
|
+
if (!journal.owner.hasOwner(host.row.threadId, host.row.submissionId)) return [failure(ownershipLost(host)), current];
|
|
841
|
+
if (stored.inputApplied !== void 0) {
|
|
842
|
+
if (stored.inputApplied.recordId === request.recordId && stored.inputApplied.sequence === request.sequence) return [success(void 0), current];
|
|
843
|
+
return [failure(ledgerError("markJoined", `A different join marker is already recorded for Submission ${request.submissionId}`)), current];
|
|
844
|
+
}
|
|
845
|
+
if (stored.row.state !== "joining" && stored.row.state !== "joined") return [failure(ledgerError("markJoined", `Cannot mark Submission ${request.submissionId} joined from state ${stored.row.state}`)), current];
|
|
846
|
+
const marker = InputAppliedMarker.make({
|
|
847
|
+
recordId: request.recordId,
|
|
848
|
+
sequence: request.sequence
|
|
849
|
+
});
|
|
850
|
+
return [success(void 0), withSubmission(current, {
|
|
810
851
|
...stored,
|
|
811
852
|
row: {
|
|
812
853
|
...stored.row,
|
|
813
|
-
state: "
|
|
854
|
+
state: "joined"
|
|
814
855
|
},
|
|
815
|
-
|
|
816
|
-
});
|
|
817
|
-
claims.push(JoiningClaim.make({
|
|
818
|
-
submissionId: stored.row.submissionId,
|
|
819
|
-
queueSequence: stored.row.queueSequence,
|
|
820
|
-
inputPayload: stored.row.inputPayload
|
|
821
|
-
}));
|
|
822
|
-
}
|
|
823
|
-
return [success(claims), {
|
|
824
|
-
...current,
|
|
825
|
-
submissions
|
|
826
|
-
}];
|
|
827
|
-
});
|
|
828
|
-
if (decision._tag === "failure") return yield* decision.error;
|
|
829
|
-
return decision.value;
|
|
830
|
-
});
|
|
831
|
-
const markJoined = Effect.fnUntraced(function* (unvalidated) {
|
|
832
|
-
const request = yield* validate(MarkJoinedRequest, "markJoined", unvalidated);
|
|
833
|
-
const decision = yield* Ref.modify(state, (current) => {
|
|
834
|
-
const stored = current.submissions.get(request.submissionId);
|
|
835
|
-
if (stored === void 0) return [failure(ledgerError("markJoined", `Unknown Submission ${request.submissionId}`)), current];
|
|
836
|
-
if (stored.joinedHostSubmissionId === void 0) return [failure(ledgerError("markJoined", `Submission ${request.submissionId} was never claimed for joining`)), current];
|
|
837
|
-
const host = current.submissions.get(stored.joinedHostSubmissionId);
|
|
838
|
-
if (host === void 0) return [failure(ledgerError("markJoined", `Host Submission ${stored.joinedHostSubmissionId} is missing`)), current];
|
|
839
|
-
if (!journal.owner.hasOwner(host.row.threadId, host.row.submissionId)) return [failure(ownershipLost(host)), current];
|
|
840
|
-
if (stored.inputApplied !== void 0) {
|
|
841
|
-
if (stored.inputApplied.recordId === request.recordId && stored.inputApplied.sequence === request.sequence) return [success(void 0), current];
|
|
842
|
-
return [failure(ledgerError("markJoined", `A different join marker is already recorded for Submission ${request.submissionId}`)), current];
|
|
843
|
-
}
|
|
844
|
-
if (stored.row.state !== "joining" && stored.row.state !== "joined") return [failure(ledgerError("markJoined", `Cannot mark Submission ${request.submissionId} joined from state ${stored.row.state}`)), current];
|
|
845
|
-
const marker = InputAppliedMarker.make({
|
|
846
|
-
recordId: request.recordId,
|
|
847
|
-
sequence: request.sequence
|
|
856
|
+
inputApplied: marker
|
|
857
|
+
})];
|
|
848
858
|
});
|
|
849
|
-
|
|
850
|
-
...stored,
|
|
851
|
-
row: {
|
|
852
|
-
...stored.row,
|
|
853
|
-
state: "joined"
|
|
854
|
-
},
|
|
855
|
-
inputApplied: marker
|
|
856
|
-
})];
|
|
859
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
857
860
|
});
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
|
|
861
|
+
const revertJoining = Effect.fnUntraced(function* (unvalidated) {
|
|
862
|
+
const request = yield* validate(RevertJoiningRequest, "revertJoining", unvalidated);
|
|
863
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
864
|
+
const stored = current.submissions.get(request.submissionId);
|
|
865
|
+
if (stored === void 0) return [failure(ledgerError("revertJoining", `Unknown Submission ${request.submissionId}`)), current];
|
|
866
|
+
if (stored.row.state !== "joining") return [success(void 0), current];
|
|
867
|
+
const guard = request.guard;
|
|
868
|
+
if (guard !== void 0) {
|
|
869
|
+
if (stored.joinedHostSubmissionId !== guard.hostSubmissionId) return [success(void 0), current];
|
|
870
|
+
const host = current.submissions.get(guard.hostSubmissionId);
|
|
871
|
+
if (host === void 0 || host.row.threadId !== stored.row.threadId) return [failure(ledgerError("revertJoining", `Host Submission ${guard.hostSubmissionId} is missing or belongs to another Thread`)), current];
|
|
872
|
+
if (host.row.state !== "settled" && !journal.owner.hasOwner(host.row.threadId, host.row.submissionId)) return [failure(ledgerError("revertJoining", "The linked host has no active local session")), current];
|
|
873
|
+
}
|
|
874
|
+
return [success(void 0), withSubmission(current, {
|
|
875
|
+
...stored,
|
|
876
|
+
row: {
|
|
877
|
+
...stored.row,
|
|
878
|
+
state: "ready"
|
|
879
|
+
},
|
|
880
|
+
joinedHostSubmissionId: void 0
|
|
881
|
+
})];
|
|
882
|
+
});
|
|
883
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
881
884
|
});
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
if (child !== void 0 && (yield* canonicalSettlement(child.row)) !== void 0) announcedChildren.add(childSubmissionId);
|
|
885
|
+
const suspend = Effect.fnUntraced(function* (unvalidated) {
|
|
886
|
+
const request = yield* validate(SuspendRequest, "suspend", unvalidated);
|
|
887
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
888
|
+
const target = (yield* Ref.get(state)).submissions.get(request.submissionId);
|
|
889
|
+
const canonical = target === void 0 ? void 0 : yield* canonicalSettlement(target.row);
|
|
890
|
+
const announcedChildren = /* @__PURE__ */ new Set();
|
|
891
|
+
if (request.reason._tag === "WaitingForChild") {
|
|
892
|
+
const current = yield* Ref.get(state);
|
|
893
|
+
for (const { childSubmissionId } of request.reason.children) {
|
|
894
|
+
const child = current.submissions.get(childSubmissionId);
|
|
895
|
+
if (child !== void 0 && (yield* canonicalSettlement(child.row)) !== void 0) announcedChildren.add(childSubmissionId);
|
|
896
|
+
}
|
|
895
897
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
898
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
899
|
+
const stored = current.submissions.get(request.submissionId);
|
|
900
|
+
if (stored === void 0) return [failure(ledgerError("suspend", `Unknown Submission ${request.submissionId}`)), current];
|
|
901
|
+
if (stored.row.state === "settled") {
|
|
902
|
+
if (stored.row.settledOutcome === void 0) return [failure(ledgerError("suspend", `Settled Submission ${request.submissionId} is missing its outcome`)), current];
|
|
903
|
+
return [failure(SettlementConflict.make({
|
|
904
|
+
submissionId: request.submissionId,
|
|
905
|
+
existingOutcome: stored.row.settledOutcome
|
|
906
|
+
})), current];
|
|
907
|
+
}
|
|
908
|
+
if (canonical !== void 0) return [failure(SettlementConflict.make({
|
|
903
909
|
submissionId: request.submissionId,
|
|
904
|
-
existingOutcome:
|
|
910
|
+
existingOutcome: canonical.settlement.outcome
|
|
905
911
|
})), current];
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
})];
|
|
912
|
+
if (!journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId)) return [failure(ownershipLost(stored)), current];
|
|
913
|
+
if (request.reason._tag === "ApprovalPending" ? approvalDecisionsCover(request.reason.toolCallIds, (toolCallId) => stored.approvalDecisions.get(toolCallId)?.decision) : request.reason.children.every((child) => announcedChildren.has(child.childSubmissionId))) return [success("resume-immediately"), current];
|
|
914
|
+
return [success("suspended"), withSubmission(current, {
|
|
915
|
+
...stored,
|
|
916
|
+
row: {
|
|
917
|
+
...stored.row,
|
|
918
|
+
state: "suspended"
|
|
919
|
+
},
|
|
920
|
+
suspension: {
|
|
921
|
+
reason: request.reason,
|
|
922
|
+
suspendedAtMillis: nowMillis
|
|
923
|
+
}
|
|
924
|
+
})];
|
|
925
|
+
});
|
|
926
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
927
|
+
if (decision.value === "suspended" && target !== void 0) journal.owner.closeSubmission(target.row.threadId, request.submissionId);
|
|
928
|
+
return decision.value;
|
|
924
929
|
});
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
930
|
+
const recordApprovalDecision = Effect.fnUntraced(function* (unvalidated) {
|
|
931
|
+
const command = yield* validate(ApprovalDecisionCommand, "recordApprovalDecision", unvalidated);
|
|
932
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
933
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
934
|
+
const stored = current.submissions.get(command.submissionId);
|
|
935
|
+
if (stored === void 0) return [failure(ledgerError("recordApprovalDecision", `Unknown Submission ${command.submissionId}`)), current];
|
|
936
|
+
if (stored.row.state === "settled") {
|
|
937
|
+
if (stored.row.settledOutcome === void 0) return [failure(ledgerError("recordApprovalDecision", `Settled Submission ${command.submissionId} is missing its outcome`)), current];
|
|
938
|
+
return [failure(SettlementConflict.make({
|
|
939
|
+
submissionId: command.submissionId,
|
|
940
|
+
existingOutcome: stored.row.settledOutcome
|
|
941
|
+
})), current];
|
|
942
|
+
}
|
|
943
|
+
const existing = stored.approvalDecisions.get(command.toolCallId);
|
|
944
|
+
if (existing !== void 0) {
|
|
945
|
+
if (existing.decision !== command.decision) return [failure(ApprovalConflict.make({
|
|
946
|
+
submissionId: command.submissionId,
|
|
947
|
+
toolCallId: command.toolCallId,
|
|
948
|
+
existingDecision: existing.decision
|
|
949
|
+
})), current];
|
|
950
|
+
return [success(existing), current];
|
|
951
|
+
}
|
|
952
|
+
const intent = ApprovalDecisionIntent.make({
|
|
945
953
|
submissionId: command.submissionId,
|
|
946
954
|
toolCallId: command.toolCallId,
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
955
|
+
decision: command.decision,
|
|
956
|
+
resolver: command.resolver,
|
|
957
|
+
reason: command.reason,
|
|
958
|
+
decidedAt: utc(nowMillis)
|
|
959
|
+
});
|
|
960
|
+
if (!approvalFitsTransfer(stored.row.threadId, intent)) return [failure(transferCapacityError("recordApprovalDecision")), current];
|
|
961
|
+
const approvalDecisions = new Map(stored.approvalDecisions).set(command.toolCallId, intent);
|
|
962
|
+
const wakes = stored.row.state === "suspended" && stored.suspension !== void 0 && stored.suspension.reason._tag === "ApprovalPending" && approvalDecisionsCover(stored.suspension.reason.toolCallIds, (toolCallId) => approvalDecisions.get(toolCallId)?.decision);
|
|
963
|
+
return [success(intent), withSubmission(current, {
|
|
964
|
+
...stored,
|
|
965
|
+
row: wakes ? {
|
|
966
|
+
...stored.row,
|
|
967
|
+
state: "input-applied"
|
|
968
|
+
} : stored.row,
|
|
969
|
+
suspension: wakes ? void 0 : stored.suspension,
|
|
970
|
+
approvalDecisions
|
|
971
|
+
})];
|
|
958
972
|
});
|
|
959
|
-
if (
|
|
960
|
-
|
|
961
|
-
const wakes = stored.row.state === "suspended" && stored.suspension !== void 0 && stored.suspension.reason._tag === "ApprovalPending" && approvalDecisionsCover(stored.suspension.reason.toolCallIds, (toolCallId) => approvalDecisions.get(toolCallId)?.decision);
|
|
962
|
-
return [success(intent), withSubmission(current, {
|
|
963
|
-
...stored,
|
|
964
|
-
row: wakes ? {
|
|
965
|
-
...stored.row,
|
|
966
|
-
state: "input-applied"
|
|
967
|
-
} : stored.row,
|
|
968
|
-
suspension: wakes ? void 0 : stored.suspension,
|
|
969
|
-
approvalDecisions
|
|
970
|
-
})];
|
|
973
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
974
|
+
return decision.value;
|
|
971
975
|
});
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
976
|
+
const markUnknown = Effect.fnUntraced(function* (unvalidated) {
|
|
977
|
+
const request = yield* validate(MarkUnknownRequest, "markUnknown", unvalidated);
|
|
978
|
+
const target = (yield* Ref.get(state)).submissions.get(request.submissionId);
|
|
979
|
+
const canonical = target === void 0 ? void 0 : yield* canonicalSettlement(target.row);
|
|
980
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
981
|
+
const stored = current.submissions.get(request.submissionId);
|
|
982
|
+
if (stored === void 0) return [failure(ledgerError("markUnknown", `Unknown Submission ${request.submissionId}`)), current];
|
|
983
|
+
if (stored.row.state === "settled") {
|
|
984
|
+
if (stored.row.settledOutcome === void 0) return [failure(ledgerError("markUnknown", `Settled Submission ${request.submissionId} is missing its outcome`)), current];
|
|
985
|
+
return [failure(SettlementConflict.make({
|
|
986
|
+
submissionId: request.submissionId,
|
|
987
|
+
existingOutcome: stored.row.settledOutcome
|
|
988
|
+
})), current];
|
|
989
|
+
}
|
|
990
|
+
if (canonical !== void 0) return [failure(SettlementConflict.make({
|
|
985
991
|
submissionId: request.submissionId,
|
|
986
|
-
existingOutcome:
|
|
992
|
+
existingOutcome: canonical.settlement.outcome
|
|
987
993
|
})), current];
|
|
994
|
+
const existing = stored.unknownMark;
|
|
995
|
+
const known = new Set(existing?.toolCallIds ?? []);
|
|
996
|
+
const merged = [...existing?.toolCallIds ?? [], ...request.toolCallIds.filter((toolCallId) => !known.has(toolCallId))];
|
|
997
|
+
return [success(void 0), withSubmission(current, {
|
|
998
|
+
...stored,
|
|
999
|
+
row: stored.row.state === "unknown" ? stored.row : {
|
|
1000
|
+
...stored.row,
|
|
1001
|
+
state: "unknown"
|
|
1002
|
+
},
|
|
1003
|
+
unknownMark: {
|
|
1004
|
+
reason: existing?.reason ?? request.reason,
|
|
1005
|
+
toolCallIds: merged
|
|
1006
|
+
}
|
|
1007
|
+
})];
|
|
1008
|
+
});
|
|
1009
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
1010
|
+
});
|
|
1011
|
+
const recordUnknownResolution = Effect.fnUntraced(function* (unvalidated) {
|
|
1012
|
+
const command = yield* validate(UnknownResolutionCommand, "recordUnknownResolution", unvalidated);
|
|
1013
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1014
|
+
const before = (yield* Ref.get(state)).submissions.get(command.submissionId);
|
|
1015
|
+
const kind = unknownResolutionKind(command.resolution);
|
|
1016
|
+
if (before !== void 0 && kind === "factual" && !before.unknownResolutions.has(JSON.stringify([command.toolCallId, kind]))) {
|
|
1017
|
+
const runId = runIdForSubmission(before.row.submissionId);
|
|
1018
|
+
const outcomes = yield* journal.toolCallResults(before.row.threadId, runId, command.toolCallId);
|
|
1019
|
+
const result = outcomes[0]?.payload;
|
|
1020
|
+
if (outcomes.length > 1 || result !== void 0 && (result._tag !== "ToolCallSettled" || result.runId !== runId || result.toolCallId !== command.toolCallId)) return yield* ledgerError("recordUnknownResolution", "Canonical Tool result identity is inconsistent");
|
|
1021
|
+
if (result?._tag === "ToolCallSettled" && !unknownResolutionMatchesResult(command.resolution, result)) return yield* UnknownResolutionConflict.make({
|
|
1022
|
+
submissionId: command.submissionId,
|
|
1023
|
+
toolCallId: command.toolCallId
|
|
1024
|
+
});
|
|
988
1025
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
row: stored.row.state === "unknown" ? stored.row : {
|
|
999
|
-
...stored.row,
|
|
1000
|
-
state: "unknown"
|
|
1001
|
-
},
|
|
1002
|
-
unknownMark: {
|
|
1003
|
-
reason: existing?.reason ?? request.reason,
|
|
1004
|
-
toolCallIds: merged
|
|
1026
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
1027
|
+
const stored = current.submissions.get(command.submissionId);
|
|
1028
|
+
if (stored === void 0) return [failure(ledgerError("recordUnknownResolution", `Unknown Submission ${command.submissionId}`)), current];
|
|
1029
|
+
if (stored.row.state === "settled") {
|
|
1030
|
+
if (stored.row.settledOutcome === void 0) return [failure(ledgerError("recordUnknownResolution", `Settled Submission ${command.submissionId} is missing its outcome`)), current];
|
|
1031
|
+
if (command.resolution._tag !== "CompletedWithResult" && command.resolution._tag !== "NeverHappened") return [failure(SettlementConflict.make({
|
|
1032
|
+
submissionId: command.submissionId,
|
|
1033
|
+
existingOutcome: stored.row.settledOutcome
|
|
1034
|
+
})), current];
|
|
1005
1035
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
});
|
|
1010
|
-
const recordUnknownResolution = Effect.fnUntraced(function* (unvalidated) {
|
|
1011
|
-
const command = yield* validate(UnknownResolutionCommand, "recordUnknownResolution", unvalidated);
|
|
1012
|
-
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1013
|
-
const before = (yield* Ref.get(state)).submissions.get(command.submissionId);
|
|
1014
|
-
const kind = unknownResolutionKind(command.resolution);
|
|
1015
|
-
if (before !== void 0 && kind === "factual" && !before.unknownResolutions.has(JSON.stringify([command.toolCallId, kind]))) {
|
|
1016
|
-
const runId = runIdForSubmission(before.row.submissionId);
|
|
1017
|
-
const outcomes = yield* journal.toolCallResults(before.row.threadId, runId, command.toolCallId);
|
|
1018
|
-
const result = outcomes[0]?.payload;
|
|
1019
|
-
if (outcomes.length > 1 || result !== void 0 && (result._tag !== "ToolCallSettled" || result.runId !== runId || result.toolCallId !== command.toolCallId)) return yield* ledgerError("recordUnknownResolution", "Canonical Tool result identity is inconsistent");
|
|
1020
|
-
if (result?._tag === "ToolCallSettled" && !unknownResolutionMatchesResult(command.resolution, result)) return yield* UnknownResolutionConflict.make({
|
|
1021
|
-
submissionId: command.submissionId,
|
|
1022
|
-
toolCallId: command.toolCallId
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
const decision = yield* Ref.modify(state, (current) => {
|
|
1026
|
-
const stored = current.submissions.get(command.submissionId);
|
|
1027
|
-
if (stored === void 0) return [failure(ledgerError("recordUnknownResolution", `Unknown Submission ${command.submissionId}`)), current];
|
|
1028
|
-
if (stored.row.state === "settled") {
|
|
1029
|
-
if (stored.row.settledOutcome === void 0) return [failure(ledgerError("recordUnknownResolution", `Settled Submission ${command.submissionId} is missing its outcome`)), current];
|
|
1030
|
-
if (command.resolution._tag !== "CompletedWithResult" && command.resolution._tag !== "NeverHappened") return [failure(SettlementConflict.make({
|
|
1036
|
+
const key = JSON.stringify([command.toolCallId, kind]);
|
|
1037
|
+
const existing = stored.unknownResolutions.get(key);
|
|
1038
|
+
if (existing !== void 0 && !equivalentUnknownResolution(existing.intent.resolution, command.resolution) || existing === void 0 && kind === "execution" && stored.unknownResolutions.has(JSON.stringify([command.toolCallId, "factual"]))) return [failure(UnknownResolutionConflict.make({
|
|
1031
1039
|
submissionId: command.submissionId,
|
|
1032
|
-
|
|
1040
|
+
toolCallId: command.toolCallId
|
|
1033
1041
|
})), current];
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1042
|
+
const intent = existing?.intent ?? UnknownResolutionIntent.make({
|
|
1043
|
+
submissionId: command.submissionId,
|
|
1044
|
+
toolCallId: command.toolCallId,
|
|
1045
|
+
author: command.author,
|
|
1046
|
+
reason: command.reason,
|
|
1047
|
+
resolution: command.resolution,
|
|
1048
|
+
resolvedAt: utc(nowMillis)
|
|
1049
|
+
});
|
|
1050
|
+
if (existing === void 0 && !resolutionFitsTransfer(stored.row.threadId, intent)) return [failure(transferCapacityError("recordUnknownResolution")), current];
|
|
1051
|
+
const unknownResolutions = existing !== void 0 ? stored.unknownResolutions : new Map(stored.unknownResolutions).set(key, { intent });
|
|
1052
|
+
const wakes = stored.row.state === "unknown" && stored.unknownMark !== void 0 && stored.unknownMark.toolCallIds.every((toolCallId) => unknownResolutions.has(JSON.stringify([toolCallId, "execution"])) || unknownResolutions.has(JSON.stringify([toolCallId, "factual"])));
|
|
1053
|
+
return [success(intent), withSubmission(current, {
|
|
1054
|
+
...stored,
|
|
1055
|
+
row: wakes ? {
|
|
1056
|
+
...stored.row,
|
|
1057
|
+
state: "input-applied"
|
|
1058
|
+
} : stored.row,
|
|
1059
|
+
unknownMark: wakes ? void 0 : stored.unknownMark,
|
|
1060
|
+
unknownResolutions
|
|
1061
|
+
})];
|
|
1048
1062
|
});
|
|
1049
|
-
if (
|
|
1050
|
-
|
|
1051
|
-
const wakes = stored.row.state === "unknown" && stored.unknownMark !== void 0 && stored.unknownMark.toolCallIds.every((toolCallId) => unknownResolutions.has(JSON.stringify([toolCallId, "execution"])) || unknownResolutions.has(JSON.stringify([toolCallId, "factual"])));
|
|
1052
|
-
return [success(intent), withSubmission(current, {
|
|
1053
|
-
...stored,
|
|
1054
|
-
row: wakes ? {
|
|
1055
|
-
...stored.row,
|
|
1056
|
-
state: "input-applied"
|
|
1057
|
-
} : stored.row,
|
|
1058
|
-
unknownMark: wakes ? void 0 : stored.unknownMark,
|
|
1059
|
-
unknownResolutions
|
|
1060
|
-
})];
|
|
1063
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
1064
|
+
return decision.value;
|
|
1061
1065
|
});
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1066
|
+
const recordChildSettled = Effect.fnUntraced(function* (unvalidated) {
|
|
1067
|
+
const request = yield* validate(ChildSettledNotification, "recordChildSettled", unvalidated);
|
|
1068
|
+
const before = yield* Ref.get(state);
|
|
1069
|
+
const waiting = before.submissions.get(request.parentSubmissionId)?.suspension?.reason;
|
|
1070
|
+
const ids = /* @__PURE__ */ new Set([request.childSubmissionId, ...waiting?._tag === "WaitingForChild" ? waiting.children.map((child) => child.childSubmissionId) : []]);
|
|
1071
|
+
const announcedChildren = /* @__PURE__ */ new Set();
|
|
1072
|
+
for (const id of ids) {
|
|
1073
|
+
const child = before.submissions.get(id);
|
|
1074
|
+
if (child !== void 0 && (yield* canonicalSettlement(child.row)) !== void 0) announcedChildren.add(id);
|
|
1075
|
+
}
|
|
1076
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
1077
|
+
const parent = current.submissions.get(request.parentSubmissionId);
|
|
1078
|
+
if (parent === void 0) return [failure(ledgerError("recordChildSettled", `Unknown Submission ${request.parentSubmissionId}`)), current];
|
|
1079
|
+
if (!announcedChildren.has(request.childSubmissionId)) return [failure(ledgerError("recordChildSettled", `Child Submission ${request.childSubmissionId} has no recorded settlement`)), current];
|
|
1080
|
+
if (parent.row.state !== "suspended" || parent.suspension === void 0 || parent.suspension.reason._tag !== "WaitingForChild") return [success("not-waiting"), current];
|
|
1081
|
+
const children = parent.suspension.reason.children;
|
|
1082
|
+
if (!children.some((entry) => entry.childSubmissionId === request.childSubmissionId)) return [success("not-waiting"), current];
|
|
1083
|
+
if (!children.every((entry) => announcedChildren.has(entry.childSubmissionId))) return [success("still-waiting"), current];
|
|
1084
|
+
return [success("woken"), withSubmission(current, {
|
|
1085
|
+
...parent,
|
|
1086
|
+
row: {
|
|
1087
|
+
...parent.row,
|
|
1088
|
+
state: "input-applied"
|
|
1089
|
+
},
|
|
1090
|
+
suspension: void 0
|
|
1091
|
+
})];
|
|
1092
|
+
});
|
|
1093
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
1094
|
+
return decision.value;
|
|
1091
1095
|
});
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1096
|
+
const reserveChildBudget = Effect.fnUntraced(function* (unvalidated) {
|
|
1097
|
+
const request = yield* validate(ChildBudgetReservationRequest, "reserveChildBudget", unvalidated);
|
|
1098
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1099
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
1100
|
+
const existing = current.childReservations.get(request.reservationId);
|
|
1101
|
+
if (existing !== void 0) {
|
|
1102
|
+
if (!(existing.parentSubmissionId === request.parentSubmissionId && existing.parentToolCallId === request.parentToolCallId && existing.allocationDigest === request.allocationDigest && equivalentPersistedJson(existing.allocation, request.allocation))) return [failure(ChildReservationConflict.make({
|
|
1103
|
+
reservationId: request.reservationId,
|
|
1104
|
+
status: existing.status,
|
|
1105
|
+
message: "A reservation with this identity exists with a different parent Tool Call or allocation."
|
|
1106
|
+
})), current];
|
|
1107
|
+
return [success(ReservedChildBudget.make({
|
|
1108
|
+
reservation: toReservationSnapshot(existing),
|
|
1109
|
+
replayed: true
|
|
1110
|
+
})), current];
|
|
1111
|
+
}
|
|
1112
|
+
for (const id of current.childrenByParent.get(request.parentSubmissionId) ?? []) {
|
|
1113
|
+
const reservation = current.childReservations.get(id);
|
|
1114
|
+
if (reservation === void 0) continue;
|
|
1115
|
+
if (reservation.parentSubmissionId === request.parentSubmissionId && reservation.parentToolCallId === request.parentToolCallId) return [failure(ChildReservationConflict.make({
|
|
1116
|
+
reservationId: request.reservationId,
|
|
1117
|
+
status: reservation.status,
|
|
1118
|
+
message: `Parent Tool Call ${request.parentToolCallId} already owns reservation ${reservation.reservationId}.`
|
|
1119
|
+
})), current];
|
|
1120
|
+
}
|
|
1121
|
+
const parent = current.submissions.get(request.parentSubmissionId);
|
|
1122
|
+
if (parent === void 0) return [failure(ledgerError("reserveChildBudget", `Unknown Submission ${request.parentSubmissionId}`)), current];
|
|
1123
|
+
if (!journal.owner.hasOwner(parent.row.threadId, parent.row.submissionId)) return [failure(ownershipLost(parent)), current];
|
|
1124
|
+
const reservation = {
|
|
1102
1125
|
reservationId: request.reservationId,
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1126
|
+
parentSubmissionId: request.parentSubmissionId,
|
|
1127
|
+
parentToolCallId: request.parentToolCallId,
|
|
1128
|
+
childSubmissionId: void 0,
|
|
1129
|
+
status: "reserved",
|
|
1130
|
+
allocation: request.allocation,
|
|
1131
|
+
allocationDigest: request.allocationDigest,
|
|
1132
|
+
accounting: void 0,
|
|
1133
|
+
reservedAtMillis: nowMillis,
|
|
1134
|
+
releaseBeganAtMillis: void 0,
|
|
1135
|
+
releasedAtMillis: void 0
|
|
1136
|
+
};
|
|
1106
1137
|
return [success(ReservedChildBudget.make({
|
|
1107
|
-
reservation: toReservationSnapshot(
|
|
1108
|
-
replayed:
|
|
1109
|
-
})), current];
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1138
|
+
reservation: toReservationSnapshot(reservation),
|
|
1139
|
+
replayed: false
|
|
1140
|
+
})), withChildReservation(current, reservation)];
|
|
1141
|
+
});
|
|
1142
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
1143
|
+
return decision.value;
|
|
1144
|
+
});
|
|
1145
|
+
const attachChildToReservation = Effect.fnUntraced(function* (unvalidated) {
|
|
1146
|
+
const request = yield* validate(AttachChildToReservationRequest, "attachChildToReservation", unvalidated);
|
|
1147
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
1148
|
+
const reservation = current.childReservations.get(request.reservationId);
|
|
1149
|
+
if (reservation === void 0) return [failure(ledgerError("attachChildToReservation", `Unknown child reservation ${request.reservationId}`)), current];
|
|
1150
|
+
if (reservation.childSubmissionId !== void 0) {
|
|
1151
|
+
if (reservation.childSubmissionId === request.childSubmissionId) return [success(toReservationSnapshot(reservation)), current];
|
|
1152
|
+
return [failure(ChildReservationConflict.make({
|
|
1153
|
+
reservationId: request.reservationId,
|
|
1154
|
+
status: reservation.status,
|
|
1155
|
+
message: `Reservation ${request.reservationId} already records child ${reservation.childSubmissionId}.`
|
|
1156
|
+
})), current];
|
|
1157
|
+
}
|
|
1158
|
+
const parent = current.submissions.get(reservation.parentSubmissionId);
|
|
1159
|
+
if (parent === void 0) return [failure(ledgerError("attachChildToReservation", `Unknown Submission ${reservation.parentSubmissionId}`)), current];
|
|
1160
|
+
if (!journal.owner.hasOwner(parent.row.threadId, parent.row.submissionId)) return [failure(ownershipLost(parent)), current];
|
|
1161
|
+
if (reservation.status !== "reserved") return [failure(ChildReservationConflict.make({
|
|
1115
1162
|
reservationId: request.reservationId,
|
|
1116
1163
|
status: reservation.status,
|
|
1117
|
-
message: `
|
|
1164
|
+
message: `Cannot attach a child to a ${reservation.status} reservation.`
|
|
1118
1165
|
})), current];
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
status: "reserved",
|
|
1129
|
-
allocation: request.allocation,
|
|
1130
|
-
allocationDigest: request.allocationDigest,
|
|
1131
|
-
accounting: void 0,
|
|
1132
|
-
reservedAtMillis: nowMillis,
|
|
1133
|
-
releaseBeganAtMillis: void 0,
|
|
1134
|
-
releasedAtMillis: void 0
|
|
1135
|
-
};
|
|
1136
|
-
return [success(ReservedChildBudget.make({
|
|
1137
|
-
reservation: toReservationSnapshot(reservation),
|
|
1138
|
-
replayed: false
|
|
1139
|
-
})), withChildReservation(current, reservation)];
|
|
1166
|
+
if (!current.submissions.has(request.childSubmissionId)) return [failure(ledgerError("attachChildToReservation", `Unknown child Submission ${request.childSubmissionId}`)), current];
|
|
1167
|
+
const attached = {
|
|
1168
|
+
...reservation,
|
|
1169
|
+
childSubmissionId: request.childSubmissionId
|
|
1170
|
+
};
|
|
1171
|
+
return [success(toReservationSnapshot(attached)), withChildReservation(current, attached)];
|
|
1172
|
+
});
|
|
1173
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
1174
|
+
return decision.value;
|
|
1140
1175
|
});
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1176
|
+
const beginChildBudgetRelease = Effect.fnUntraced(function* (unvalidated) {
|
|
1177
|
+
const request = yield* validate(BeginChildBudgetReleaseRequest, "beginChildBudgetRelease", unvalidated);
|
|
1178
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1179
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
1180
|
+
const reservation = current.childReservations.get(request.reservationId);
|
|
1181
|
+
if (reservation === void 0) return [failure(ledgerError("beginChildBudgetRelease", `Unknown child reservation ${request.reservationId}`)), current];
|
|
1182
|
+
if (reservation.status !== "reserved") {
|
|
1183
|
+
if (reservation.accounting !== void 0 && equivalentPersistedJson(reservation.accounting, request.accounting)) return [success(toReservationSnapshot(reservation)), current];
|
|
1184
|
+
return [failure(ChildReservationConflict.make({
|
|
1185
|
+
reservationId: request.reservationId,
|
|
1186
|
+
status: reservation.status,
|
|
1187
|
+
message: "A different accounting decision is already frozen for this reservation."
|
|
1188
|
+
})), current];
|
|
1189
|
+
}
|
|
1190
|
+
const frozen = {
|
|
1191
|
+
...reservation,
|
|
1192
|
+
status: "releasePending",
|
|
1193
|
+
accounting: request.accounting,
|
|
1194
|
+
releaseBeganAtMillis: nowMillis
|
|
1195
|
+
};
|
|
1196
|
+
return [success(toReservationSnapshot(frozen)), withChildReservation(current, frozen)];
|
|
1197
|
+
});
|
|
1198
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
1199
|
+
return decision.value;
|
|
1200
|
+
});
|
|
1201
|
+
const releaseChildBudget = Effect.fnUntraced(function* (unvalidated) {
|
|
1202
|
+
const request = yield* validate(ReleaseChildBudgetRequest, "releaseChildBudget", unvalidated);
|
|
1203
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
1204
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
1205
|
+
const reservation = current.childReservations.get(request.reservationId);
|
|
1206
|
+
if (reservation === void 0) return [failure(ledgerError("releaseChildBudget", `Unknown child reservation ${request.reservationId}`)), current];
|
|
1207
|
+
if (reservation.status === "released") return [success(toReservationSnapshot(reservation)), current];
|
|
1208
|
+
if (reservation.status !== "releasePending") return [failure(ChildReservationConflict.make({
|
|
1152
1209
|
reservationId: request.reservationId,
|
|
1153
1210
|
status: reservation.status,
|
|
1154
|
-
message:
|
|
1211
|
+
message: "Cannot release a reservation whose accounting decision is not frozen."
|
|
1155
1212
|
})), current];
|
|
1213
|
+
const released = {
|
|
1214
|
+
...reservation,
|
|
1215
|
+
status: "released",
|
|
1216
|
+
releasedAtMillis: nowMillis
|
|
1217
|
+
};
|
|
1218
|
+
return [success(toReservationSnapshot(released)), withChildReservation(current, released)];
|
|
1219
|
+
});
|
|
1220
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
1221
|
+
return decision.value;
|
|
1222
|
+
});
|
|
1223
|
+
const scanNonterminal = Stream.paginate(void 0, (after) => Effect.sync(() => {
|
|
1224
|
+
const current = MutableRef.get(state.ref);
|
|
1225
|
+
const candidates = boundedWorkSelection(8, (stored) => `${stored.row.threadId}\u0000${String(stored.row.queueSequence).padStart(16, "0")}`, after);
|
|
1226
|
+
for (const ids of current.activeByThread.values()) for (const id of ids) {
|
|
1227
|
+
const stored = current.submissions.get(id);
|
|
1228
|
+
if (stored !== void 0) candidates.add(stored);
|
|
1156
1229
|
}
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
const attached = {
|
|
1167
|
-
...reservation,
|
|
1168
|
-
childSubmissionId: request.childSubmissionId
|
|
1169
|
-
};
|
|
1170
|
-
return [success(toReservationSnapshot(attached)), withChildReservation(current, attached)];
|
|
1230
|
+
const items = candidates.values.map(({ row }) => SubmissionWorkItem.make(row));
|
|
1231
|
+
const last = candidates.values.at(-1)?.row;
|
|
1232
|
+
return [items, last === void 0 ? Option.none() : Option.some(`${last.threadId}\u0000${String(last.queueSequence).padStart(16, "0")}`)];
|
|
1233
|
+
}));
|
|
1234
|
+
const readAbortIntent = Effect.fnUntraced(function* (unvalidated) {
|
|
1235
|
+
const request = yield* validate(AbortIntentRequest, "readAbortIntent", unvalidated);
|
|
1236
|
+
const stored = (yield* Ref.get(state)).submissions.get(request.submissionId);
|
|
1237
|
+
if (stored === void 0) return yield* ledgerError("readAbortIntent", `Unknown Submission ${request.submissionId}`);
|
|
1238
|
+
return stored.abortIntent;
|
|
1171
1239
|
});
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1240
|
+
const loadRecoverySnapshot = Effect.fnUntraced(function* (unvalidated) {
|
|
1241
|
+
const request = yield* validate(RecoverySnapshotRequest, "loadRecoverySnapshot", unvalidated);
|
|
1242
|
+
const current = yield* Ref.get(state);
|
|
1243
|
+
const stored = current.submissions.get(request.submissionId);
|
|
1244
|
+
if (stored === void 0) return yield* ledgerError("loadRecoverySnapshot", `Unknown Submission ${request.submissionId}`);
|
|
1245
|
+
const joins = Array.from(current.joinsByHost.get(request.submissionId) ?? [], (id) => current.submissions.get(id)).filter((candidate) => candidate !== void 0).sort((left, right) => left.row.queueSequence - right.row.queueSequence).map((candidate) => JoinSnapshot.make({
|
|
1246
|
+
submissionId: candidate.row.submissionId,
|
|
1247
|
+
state: candidate.row.state,
|
|
1248
|
+
hostSubmissionId: request.submissionId
|
|
1249
|
+
}));
|
|
1250
|
+
const byToolCallId = (left, right) => left.toolCallId < right.toolCallId ? -1 : left.toolCallId > right.toolCallId ? 1 : 0;
|
|
1251
|
+
const childReservations = Array.from(current.childrenByParent.get(request.submissionId) ?? [], (id) => current.childReservations.get(id)).filter((reservation) => reservation !== void 0).sort((left, right) => left.parentToolCallId < right.parentToolCallId ? -1 : left.parentToolCallId > right.parentToolCallId ? 1 : 0);
|
|
1252
|
+
const childAttachments = [];
|
|
1253
|
+
for (const reservation of childReservations) {
|
|
1254
|
+
if (reservation.childSubmissionId === void 0) continue;
|
|
1255
|
+
const child = current.submissions.get(reservation.childSubmissionId);
|
|
1256
|
+
if (child === void 0) continue;
|
|
1257
|
+
childAttachments.push(ChildAttachmentSnapshot.make({
|
|
1258
|
+
toolCallId: reservation.parentToolCallId,
|
|
1259
|
+
childSubmissionId: reservation.childSubmissionId,
|
|
1260
|
+
childState: child.row.state,
|
|
1261
|
+
...child.row.settledOutcome === void 0 ? {} : { childOutcome: child.row.settledOutcome }
|
|
1262
|
+
}));
|
|
1188
1263
|
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1264
|
+
return RecoverySnapshot.make({
|
|
1265
|
+
submission: toSnapshot(stored.row),
|
|
1266
|
+
joins,
|
|
1267
|
+
approvalDecisions: [...stored.approvalDecisions.values()].sort(byToolCallId),
|
|
1268
|
+
unknownResolutions: [...stored.unknownResolutions.values()].map((resolution) => resolution.intent).sort(byToolCallId),
|
|
1269
|
+
childReservations: childReservations.map(toReservationSnapshot),
|
|
1270
|
+
childAttachments,
|
|
1271
|
+
...stored.row.parentLinkage === void 0 ? {} : { parentLinkage: stored.row.parentLinkage },
|
|
1272
|
+
...stored.joinedHostSubmissionId === void 0 ? {} : { hostSubmissionId: stored.joinedHostSubmissionId },
|
|
1273
|
+
...stored.suspension === void 0 ? {} : { suspension: SuspensionSnapshot.make({
|
|
1274
|
+
reason: stored.suspension.reason,
|
|
1275
|
+
suspendedAt: utc(stored.suspension.suspendedAtMillis)
|
|
1276
|
+
}) },
|
|
1277
|
+
hasOwner: journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId),
|
|
1278
|
+
...stored.inputApplied === void 0 ? {} : { inputApplied: stored.inputApplied },
|
|
1279
|
+
...stored.abortIntent === void 0 ? {} : { abortIntent: stored.abortIntent }
|
|
1280
|
+
});
|
|
1196
1281
|
});
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
})
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1282
|
+
const ledger = SubmissionLedger.of({
|
|
1283
|
+
capabilities,
|
|
1284
|
+
resolveFundingOwner: Effect.fnUntraced(function* (unvalidated) {
|
|
1285
|
+
const request = yield* validate(FundingOwnerRequest, "funding owner", unvalidated);
|
|
1286
|
+
const current = MutableRef.get(state.ref);
|
|
1287
|
+
const selected = current.submissions.get(request.submissionId);
|
|
1288
|
+
if (selected === void 0 || selected.row.threadId !== request.threadId) return yield* ledgerError("funding owner", "Selected admission belongs to another Thread or is missing");
|
|
1289
|
+
if (selected.row.state === "joining" || selected.joinedHostSubmissionId !== void 0 && selected.inputApplied === void 0) return yield* ledgerError("funding owner", "Joining funding owner is not yet frozen by exact applied input evidence");
|
|
1290
|
+
const owner = selected.joinedHostSubmissionId === void 0 ? selected : current.submissions.get(selected.joinedHostSubmissionId);
|
|
1291
|
+
if (owner === void 0 || owner.row.threadId !== request.threadId || owner !== selected && owner.row.queueSequence >= selected.row.queueSequence) return yield* ledgerError("funding owner", "Exact joined funding owner is missing or inconsistent");
|
|
1292
|
+
return FundingOwner.make({
|
|
1293
|
+
selected: toSnapshot(selected.row),
|
|
1294
|
+
owner: toSnapshot(owner.row)
|
|
1295
|
+
});
|
|
1296
|
+
}),
|
|
1297
|
+
admit: (request) => journal.withMutation(admit(request)),
|
|
1298
|
+
markReady: (request) => journal.withMutation(markReady(request)),
|
|
1299
|
+
lookup,
|
|
1300
|
+
resolveAdmission,
|
|
1301
|
+
selectHead: (request) => journal.withMutation(selectHead(request)),
|
|
1302
|
+
markInputApplied: (request) => journal.withMutation(markInputApplied(request)),
|
|
1303
|
+
finalizeSettlement: (request) => journal.withMutation(finalizeSettlement(request)),
|
|
1304
|
+
requestAbort: (request) => journal.withMutation(requestAbort(request)),
|
|
1305
|
+
stopWorker: (request) => journal.withMutation(stopWorker(request)),
|
|
1306
|
+
inspectWorker,
|
|
1307
|
+
claimJoining: (request) => journal.withMutation(claimJoining(request)),
|
|
1308
|
+
markJoined: (request) => journal.withMutation(markJoined(request)),
|
|
1309
|
+
revertJoining: (request) => journal.withMutation(revertJoining(request)),
|
|
1310
|
+
suspend: (request) => journal.withMutation(suspend(request)),
|
|
1311
|
+
recordApprovalDecision: (request) => journal.withMutation(recordApprovalDecision(request)),
|
|
1312
|
+
markUnknown: (request) => journal.withMutation(markUnknown(request)),
|
|
1313
|
+
recordUnknownResolution: (request) => journal.withMutation(recordUnknownResolution(request)),
|
|
1314
|
+
recordChildSettled: (request) => journal.withMutation(recordChildSettled(request)),
|
|
1315
|
+
reserveChildBudget: (request) => journal.withMutation(reserveChildBudget(request)),
|
|
1316
|
+
attachChildToReservation: (request) => journal.withMutation(attachChildToReservation(request)),
|
|
1317
|
+
beginChildBudgetRelease: (request) => journal.withMutation(beginChildBudgetRelease(request)),
|
|
1318
|
+
releaseChildBudget: (request) => journal.withMutation(releaseChildBudget(request)),
|
|
1319
|
+
scanNonterminal,
|
|
1320
|
+
loadRecoverySnapshot,
|
|
1321
|
+
readAbortIntent
|
|
1218
1322
|
});
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
const scanNonterminal = Stream.paginate(void 0, (after) => Effect.sync(() => {
|
|
1223
|
-
const current = MutableRef.get(state.ref);
|
|
1224
|
-
const candidates = boundedWorkSelection(8, (stored) => `${stored.row.threadId}\u0000${String(stored.row.queueSequence).padStart(16, "0")}`, after);
|
|
1225
|
-
for (const ids of current.activeByThread.values()) for (const id of ids) {
|
|
1226
|
-
const stored = current.submissions.get(id);
|
|
1227
|
-
if (stored !== void 0) candidates.add(stored);
|
|
1228
|
-
}
|
|
1229
|
-
const items = candidates.values.map(({ row }) => SubmissionWorkItem.make(row));
|
|
1230
|
-
const last = candidates.values.at(-1)?.row;
|
|
1231
|
-
return [items, last === void 0 ? Option.none() : Option.some(`${last.threadId}\u0000${String(last.queueSequence).padStart(16, "0")}`)];
|
|
1232
|
-
}));
|
|
1233
|
-
const readAbortIntent = Effect.fnUntraced(function* (unvalidated) {
|
|
1234
|
-
const request = yield* validate(AbortIntentRequest, "readAbortIntent", unvalidated);
|
|
1235
|
-
const stored = (yield* Ref.get(state)).submissions.get(request.submissionId);
|
|
1236
|
-
if (stored === void 0) return yield* ledgerError("readAbortIntent", `Unknown Submission ${request.submissionId}`);
|
|
1237
|
-
return stored.abortIntent;
|
|
1238
|
-
});
|
|
1239
|
-
const loadRecoverySnapshot = Effect.fnUntraced(function* (unvalidated) {
|
|
1240
|
-
const request = yield* validate(RecoverySnapshotRequest, "loadRecoverySnapshot", unvalidated);
|
|
1241
|
-
const current = yield* Ref.get(state);
|
|
1242
|
-
const stored = current.submissions.get(request.submissionId);
|
|
1243
|
-
if (stored === void 0) return yield* ledgerError("loadRecoverySnapshot", `Unknown Submission ${request.submissionId}`);
|
|
1244
|
-
const joins = Array.from(current.joinsByHost.get(request.submissionId) ?? [], (id) => current.submissions.get(id)).filter((candidate) => candidate !== void 0).sort((left, right) => left.row.queueSequence - right.row.queueSequence).map((candidate) => JoinSnapshot.make({
|
|
1245
|
-
submissionId: candidate.row.submissionId,
|
|
1246
|
-
state: candidate.row.state,
|
|
1247
|
-
hostSubmissionId: request.submissionId
|
|
1248
|
-
}));
|
|
1249
|
-
const byToolCallId = (left, right) => left.toolCallId < right.toolCallId ? -1 : left.toolCallId > right.toolCallId ? 1 : 0;
|
|
1250
|
-
const childReservations = Array.from(current.childrenByParent.get(request.submissionId) ?? [], (id) => current.childReservations.get(id)).filter((reservation) => reservation !== void 0).sort((left, right) => left.parentToolCallId < right.parentToolCallId ? -1 : left.parentToolCallId > right.parentToolCallId ? 1 : 0);
|
|
1251
|
-
const childAttachments = [];
|
|
1252
|
-
for (const reservation of childReservations) {
|
|
1253
|
-
if (reservation.childSubmissionId === void 0) continue;
|
|
1254
|
-
const child = current.submissions.get(reservation.childSubmissionId);
|
|
1255
|
-
if (child === void 0) continue;
|
|
1256
|
-
childAttachments.push(ChildAttachmentSnapshot.make({
|
|
1257
|
-
toolCallId: reservation.parentToolCallId,
|
|
1258
|
-
childSubmissionId: reservation.childSubmissionId,
|
|
1259
|
-
childState: child.row.state,
|
|
1260
|
-
...child.row.settledOutcome === void 0 ? {} : { childOutcome: child.row.settledOutcome }
|
|
1261
|
-
}));
|
|
1262
|
-
}
|
|
1263
|
-
return RecoverySnapshot.make({
|
|
1264
|
-
submission: toSnapshot(stored.row),
|
|
1265
|
-
joins,
|
|
1266
|
-
approvalDecisions: [...stored.approvalDecisions.values()].sort(byToolCallId),
|
|
1267
|
-
unknownResolutions: [...stored.unknownResolutions.values()].map((resolution) => resolution.intent).sort(byToolCallId),
|
|
1268
|
-
childReservations: childReservations.map(toReservationSnapshot),
|
|
1269
|
-
childAttachments,
|
|
1270
|
-
...stored.row.parentLinkage === void 0 ? {} : { parentLinkage: stored.row.parentLinkage },
|
|
1271
|
-
...stored.joinedHostSubmissionId === void 0 ? {} : { hostSubmissionId: stored.joinedHostSubmissionId },
|
|
1272
|
-
...stored.suspension === void 0 ? {} : { suspension: SuspensionSnapshot.make({
|
|
1273
|
-
reason: stored.suspension.reason,
|
|
1274
|
-
suspendedAt: utc(stored.suspension.suspendedAtMillis)
|
|
1275
|
-
}) },
|
|
1276
|
-
hasOwner: journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId),
|
|
1277
|
-
...stored.inputApplied === void 0 ? {} : { inputApplied: stored.inputApplied },
|
|
1278
|
-
...stored.abortIntent === void 0 ? {} : { abortIntent: stored.abortIntent }
|
|
1323
|
+
const toAdmission = (row, settledAtMillis) => ThreadAdmission.make({
|
|
1324
|
+
...toSnapshot(row),
|
|
1325
|
+
...settledAtMillis === void 0 ? {} : { settledAt: utc(settledAtMillis) }
|
|
1279
1326
|
});
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
resolveFundingOwner: Effect.fnUntraced(function* (unvalidated) {
|
|
1284
|
-
const request = yield* validate(FundingOwnerRequest, "funding owner", unvalidated);
|
|
1285
|
-
const current = MutableRef.get(state.ref);
|
|
1286
|
-
const selected = current.submissions.get(request.submissionId);
|
|
1287
|
-
if (selected === void 0 || selected.row.threadId !== request.threadId) return yield* ledgerError("funding owner", "Selected admission belongs to another Thread or is missing");
|
|
1288
|
-
if (selected.row.state === "joining" || selected.joinedHostSubmissionId !== void 0 && selected.inputApplied === void 0) return yield* ledgerError("funding owner", "Joining funding owner is not yet frozen by exact applied input evidence");
|
|
1289
|
-
const owner = selected.joinedHostSubmissionId === void 0 ? selected : current.submissions.get(selected.joinedHostSubmissionId);
|
|
1290
|
-
if (owner === void 0 || owner.row.threadId !== request.threadId || owner !== selected && owner.row.queueSequence >= selected.row.queueSequence) return yield* ledgerError("funding owner", "Exact joined funding owner is missing or inconsistent");
|
|
1291
|
-
return FundingOwner.make({
|
|
1292
|
-
selected: toSnapshot(selected.row),
|
|
1293
|
-
owner: toSnapshot(owner.row)
|
|
1294
|
-
});
|
|
1295
|
-
}),
|
|
1296
|
-
admit: (request) => journal.withMutation(admit(request)),
|
|
1297
|
-
markReady: (request) => journal.withMutation(markReady(request)),
|
|
1298
|
-
lookup,
|
|
1299
|
-
resolveAdmission,
|
|
1300
|
-
selectHead: (request) => journal.withMutation(selectHead(request)),
|
|
1301
|
-
markInputApplied: (request) => journal.withMutation(markInputApplied(request)),
|
|
1302
|
-
finalizeSettlement: (request) => journal.withMutation(finalizeSettlement(request)),
|
|
1303
|
-
requestAbort: (request) => journal.withMutation(requestAbort(request)),
|
|
1304
|
-
stopWorker: (request) => journal.withMutation(stopWorker(request)),
|
|
1305
|
-
inspectWorker,
|
|
1306
|
-
claimJoining: (request) => journal.withMutation(claimJoining(request)),
|
|
1307
|
-
markJoined: (request) => journal.withMutation(markJoined(request)),
|
|
1308
|
-
revertJoining: (request) => journal.withMutation(revertJoining(request)),
|
|
1309
|
-
suspend: (request) => journal.withMutation(suspend(request)),
|
|
1310
|
-
recordApprovalDecision: (request) => journal.withMutation(recordApprovalDecision(request)),
|
|
1311
|
-
markUnknown: (request) => journal.withMutation(markUnknown(request)),
|
|
1312
|
-
recordUnknownResolution: (request) => journal.withMutation(recordUnknownResolution(request)),
|
|
1313
|
-
recordChildSettled: (request) => journal.withMutation(recordChildSettled(request)),
|
|
1314
|
-
reserveChildBudget: (request) => journal.withMutation(reserveChildBudget(request)),
|
|
1315
|
-
attachChildToReservation: (request) => journal.withMutation(attachChildToReservation(request)),
|
|
1316
|
-
beginChildBudgetRelease: (request) => journal.withMutation(beginChildBudgetRelease(request)),
|
|
1317
|
-
releaseChildBudget: (request) => journal.withMutation(releaseChildBudget(request)),
|
|
1318
|
-
scanNonterminal,
|
|
1319
|
-
loadRecoverySnapshot,
|
|
1320
|
-
readAbortIntent
|
|
1321
|
-
});
|
|
1322
|
-
const toAdmission = (row, settledAtMillis) => ThreadAdmission.make({
|
|
1323
|
-
...toSnapshot(row),
|
|
1324
|
-
...settledAtMillis === void 0 ? {} : { settledAt: utc(settledAtMillis) }
|
|
1325
|
-
});
|
|
1326
|
-
const cleanCommand = (value) => {
|
|
1327
|
-
const { canonicalRecordId: _, ...fact } = value;
|
|
1328
|
-
return fact;
|
|
1329
|
-
};
|
|
1330
|
-
const commands = (stored) => {
|
|
1331
|
-
return {
|
|
1332
|
-
aborts: stored?.abortIntent === void 0 ? [] : [cleanCommand(stored.abortIntent)],
|
|
1333
|
-
approvals: Array.from(stored?.approvalDecisions.values() ?? [], cleanCommand),
|
|
1334
|
-
resolutions: Array.from(stored?.unknownResolutions.values() ?? [], ({ intent }) => cleanCommand(intent))
|
|
1327
|
+
const cleanCommand = (value) => {
|
|
1328
|
+
const { canonicalRecordId: _, ...fact } = value;
|
|
1329
|
+
return fact;
|
|
1335
1330
|
};
|
|
1336
|
-
|
|
1337
|
-
const readCommands = Effect.fnUntraced(function* (stored) {
|
|
1338
|
-
if ((stored?.approvalDecisions.size ?? 0) > MAX_RUN_EVIDENCE_RECORDS || (stored?.unknownResolutions.size ?? 0) > MAX_RUN_EVIDENCE_RECORDS) return yield* ThreadStoreError.make({
|
|
1339
|
-
operation: "read commands",
|
|
1340
|
-
message: "Accepted commands exceed Run record bounds"
|
|
1341
|
-
});
|
|
1342
|
-
let bytes = 0;
|
|
1343
|
-
const add = (schema, value) => Schema.encodeEffect(Schema.toCodecJson(schema))(value).pipe(Effect.mapError(() => ThreadStoreError.make({
|
|
1344
|
-
operation: "read commands",
|
|
1345
|
-
message: "Invalid accepted command wire"
|
|
1346
|
-
})), Effect.flatMap((wire) => {
|
|
1347
|
-
bytes += utf8ByteLength(canonicalJson(wire));
|
|
1348
|
-
return bytes > MAX_RUN_EVIDENCE_BYTES ? Effect.fail(ThreadStoreError.make({
|
|
1349
|
-
operation: "read commands",
|
|
1350
|
-
message: "Accepted commands exceed Run byte bounds"
|
|
1351
|
-
})) : Effect.void;
|
|
1352
|
-
}));
|
|
1353
|
-
if (stored?.abortIntent !== void 0) yield* add(AbortIntent, stored.abortIntent);
|
|
1354
|
-
for (const value of stored?.approvalDecisions.values() ?? []) yield* add(ApprovalDecisionIntent, value);
|
|
1355
|
-
for (const { intent } of stored?.unknownResolutions.values() ?? []) yield* add(UnknownResolutionIntent, intent);
|
|
1356
|
-
return commands(stored);
|
|
1357
|
-
});
|
|
1358
|
-
const lookupAdmission = (threadId, submissionId) => Effect.sync(() => {
|
|
1359
|
-
const stored = MutableRef.get(state.ref).submissions.get(submissionId);
|
|
1360
|
-
return stored?.row.threadId === threadId ? toAdmission(stored.row, stored.finalization?.finalizedAtMillis) : void 0;
|
|
1361
|
-
});
|
|
1362
|
-
yield* journal.registerLedgerTransfer({
|
|
1363
|
-
admission: lookupAdmission,
|
|
1364
|
-
commands: (id) => readCommands(MutableRef.get(state.ref).submissions.get(id)),
|
|
1365
|
-
snapshot: (threadId) => Effect.sync(() => {
|
|
1366
|
-
const current = MutableRef.get(state.ref);
|
|
1367
|
-
const directory = current.byThread.get(threadId);
|
|
1331
|
+
const commands = (stored) => {
|
|
1368
1332
|
return {
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
approvals: directory?.commands.approvals.length ?? 0,
|
|
1373
|
-
resolutions: directory?.commands.resolutions.length ?? 0,
|
|
1374
|
-
...current.stoppedWorkers.has(threadId) ? { workerSeal: current.stoppedWorkers.get(threadId) === void 0 ? {} : { terminal: current.stoppedWorkers.get(threadId) } } : {}
|
|
1333
|
+
aborts: stored?.abortIntent === void 0 ? [] : [cleanCommand(stored.abortIntent)],
|
|
1334
|
+
approvals: Array.from(stored?.approvalDecisions.values() ?? [], cleanCommand),
|
|
1335
|
+
resolutions: Array.from(stored?.unknownResolutions.values() ?? [], ({ intent }) => cleanCommand(intent))
|
|
1375
1336
|
};
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1337
|
+
};
|
|
1338
|
+
const readCommands = Effect.fnUntraced(function* (stored) {
|
|
1339
|
+
if ((stored?.approvalDecisions.size ?? 0) > MAX_RUN_EVIDENCE_RECORDS || (stored?.unknownResolutions.size ?? 0) > MAX_RUN_EVIDENCE_RECORDS) return yield* ThreadStoreError.make({
|
|
1340
|
+
operation: "read commands",
|
|
1341
|
+
message: "Accepted commands exceed Run record bounds"
|
|
1342
|
+
});
|
|
1343
|
+
let bytes = 0;
|
|
1344
|
+
const add = (schema, value) => Schema.encodeEffect(Schema.toCodecJson(schema))(value).pipe(Effect.mapError(() => ThreadStoreError.make({
|
|
1345
|
+
operation: "read commands",
|
|
1346
|
+
message: "Invalid accepted command wire"
|
|
1347
|
+
})), Effect.flatMap((wire) => {
|
|
1348
|
+
bytes += utf8ByteLength(canonicalJson(wire));
|
|
1349
|
+
return bytes > MAX_RUN_EVIDENCE_BYTES ? Effect.fail(ThreadStoreError.make({
|
|
1350
|
+
operation: "read commands",
|
|
1351
|
+
message: "Accepted commands exceed Run byte bounds"
|
|
1352
|
+
})) : Effect.void;
|
|
1353
|
+
}));
|
|
1354
|
+
if (stored?.abortIntent !== void 0) yield* add(AbortIntent, stored.abortIntent);
|
|
1355
|
+
for (const value of stored?.approvalDecisions.values() ?? []) yield* add(ApprovalDecisionIntent, value);
|
|
1356
|
+
for (const { intent } of stored?.unknownResolutions.values() ?? []) yield* add(UnknownResolutionIntent, intent);
|
|
1357
|
+
return commands(stored);
|
|
1358
|
+
});
|
|
1359
|
+
const lookupAdmission = (threadId, submissionId) => Effect.sync(() => {
|
|
1360
|
+
const stored = MutableRef.get(state.ref).submissions.get(submissionId);
|
|
1361
|
+
return stored?.row.threadId === threadId ? toAdmission(stored.row, stored.finalization?.finalizedAtMillis) : void 0;
|
|
1362
|
+
});
|
|
1363
|
+
yield* journal.registerLedgerTransfer({
|
|
1364
|
+
admission: lookupAdmission,
|
|
1365
|
+
commands: (id) => readCommands(MutableRef.get(state.ref).submissions.get(id)),
|
|
1366
|
+
snapshot: (threadId) => Effect.sync(() => {
|
|
1367
|
+
const current = MutableRef.get(state.ref);
|
|
1368
|
+
const directory = current.byThread.get(threadId);
|
|
1369
|
+
return {
|
|
1370
|
+
idle: (current.activeByThread.get(threadId)?.size ?? 0) === 0 && (directory?.reservations ?? 0) === 0,
|
|
1371
|
+
admissions: directory?.ordered.length ?? 0,
|
|
1372
|
+
aborts: directory?.commands.aborts.length ?? 0,
|
|
1373
|
+
approvals: directory?.commands.approvals.length ?? 0,
|
|
1374
|
+
resolutions: directory?.commands.resolutions.length ?? 0,
|
|
1375
|
+
...current.stoppedWorkers.has(threadId) ? { workerSeal: current.stoppedWorkers.get(threadId) === void 0 ? {} : { terminal: current.stoppedWorkers.get(threadId) } } : {}
|
|
1376
|
+
};
|
|
1377
|
+
}),
|
|
1378
|
+
facts: (threadId, section, after) => Effect.sync(() => {
|
|
1379
|
+
const current = MutableRef.get(state.ref);
|
|
1380
|
+
const facts = {
|
|
1381
|
+
admissions: new Array(),
|
|
1382
|
+
commands: commands(void 0),
|
|
1383
|
+
deliveries: []
|
|
1384
|
+
};
|
|
1385
|
+
let next = after ?? "";
|
|
1386
|
+
if (section === "admissions") {
|
|
1387
|
+
const stored = threadRows(current, threadId, after === void 0 ? -1 : Number(after)).next().value;
|
|
1388
|
+
if (stored !== void 0) {
|
|
1389
|
+
facts.admissions.push(toAdmission(stored.row, stored.finalization?.finalizedAtMillis));
|
|
1390
|
+
next = String(stored.row.queueSequence);
|
|
1391
|
+
}
|
|
1392
|
+
} else {
|
|
1393
|
+
const entries = current.byThread.get(threadId)?.commands[section] ?? [];
|
|
1394
|
+
const at = after === void 0 ? 0 : commandPosition(entries, after);
|
|
1395
|
+
const choice = entries[at + Number(after !== void 0 && entries[at]?.key === after)];
|
|
1396
|
+
if (choice !== void 0) {
|
|
1397
|
+
next = choice.key;
|
|
1398
|
+
const stored = current.submissions.get(choice.submissionId);
|
|
1399
|
+
if (section === "aborts" && stored?.abortIntent !== void 0) facts.commands.aborts.push(cleanCommand(stored.abortIntent));
|
|
1400
|
+
else if (section === "approvals" && choice.toolCallId !== void 0) {
|
|
1401
|
+
const value = stored?.approvalDecisions.get(choice.toolCallId);
|
|
1402
|
+
if (value !== void 0) facts.commands.approvals.push(cleanCommand(value));
|
|
1403
|
+
} else if (section === "resolutions" && choice.resolutionKey !== void 0) {
|
|
1404
|
+
const value = stored?.unknownResolutions.get(choice.resolutionKey);
|
|
1405
|
+
if (value !== void 0) facts.commands.resolutions.push(cleanCommand(value.intent));
|
|
1406
|
+
}
|
|
1405
1407
|
}
|
|
1406
1408
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1409
|
+
return {
|
|
1410
|
+
facts,
|
|
1411
|
+
after: next
|
|
1412
|
+
};
|
|
1413
|
+
}),
|
|
1414
|
+
submissions: (threadId) => Stream.paginate(-1, (after) => Effect.sync(() => {
|
|
1415
|
+
const stored = threadRows(MutableRef.get(state.ref), threadId, after).next().value;
|
|
1416
|
+
return [stored === void 0 ? [] : [toSnapshot(stored.row)], stored === void 0 ? Option.none() : Option.some(stored.row.queueSequence)];
|
|
1417
|
+
})),
|
|
1418
|
+
startImport: Effect.fnUntraced(function* (threadId) {
|
|
1419
|
+
const current = yield* Ref.get(state);
|
|
1420
|
+
if (current.lanes.has(threadId) || current.byThread.has(threadId) || current.stoppedWorkers.has(threadId)) return yield* ThreadImportRejected.make({
|
|
1421
|
+
threadId,
|
|
1422
|
+
reason: "target-not-empty",
|
|
1423
|
+
message: "Destination ledger contains Thread data"
|
|
1424
|
+
});
|
|
1425
|
+
const staged = {
|
|
1426
|
+
submissions: /* @__PURE__ */ new Map(),
|
|
1427
|
+
admissionIndex: /* @__PURE__ */ new Map(),
|
|
1428
|
+
lanes: /* @__PURE__ */ new Map(),
|
|
1429
|
+
childReservations: /* @__PURE__ */ new Map(),
|
|
1430
|
+
freshAdmissionSequence: current.freshAdmissionSequence,
|
|
1431
|
+
stoppedWorkers: /* @__PURE__ */ new Map(),
|
|
1432
|
+
latestByThread: /* @__PURE__ */ new Map(),
|
|
1433
|
+
activeByThread: /* @__PURE__ */ new Map(),
|
|
1434
|
+
byThread: /* @__PURE__ */ new Map(),
|
|
1435
|
+
receipts: /* @__PURE__ */ new Map(),
|
|
1436
|
+
joinsByHost: /* @__PURE__ */ new Map(),
|
|
1437
|
+
childrenByParent: /* @__PURE__ */ new Map()
|
|
1438
|
+
};
|
|
1439
|
+
const commandBytes = /* @__PURE__ */ new Map();
|
|
1440
|
+
const settledTimes = /* @__PURE__ */ new Map();
|
|
1441
|
+
const retainCommandBytes = Effect.fnUntraced(function* (schema, value, id) {
|
|
1442
|
+
const wire = yield* Schema.encodeEffect(Schema.toCodecJson(schema))(value).pipe(Effect.mapError(() => invalidThreadArchive("Invalid accepted command wire", threadId)));
|
|
1443
|
+
const bytes = (commandBytes.get(id) ?? 0) + utf8ByteLength(canonicalJson(wire));
|
|
1444
|
+
if (bytes > MAX_RUN_EVIDENCE_BYTES) return yield* invalidThreadArchive("Accepted commands exceed Run byte bounds", threadId);
|
|
1445
|
+
commandBytes.set(id, bytes);
|
|
1446
|
+
});
|
|
1447
|
+
return {
|
|
1448
|
+
reader: {
|
|
1449
|
+
admission: (id) => Effect.sync(() => {
|
|
1450
|
+
const stored = staged.submissions.get(id);
|
|
1451
|
+
return stored === void 0 ? void 0 : toAdmission(stored.row, settledTimes.get(id));
|
|
1452
|
+
}),
|
|
1453
|
+
runOwner: (runId) => Effect.sync(() => {
|
|
1454
|
+
if (!runId.startsWith("run:")) return void 0;
|
|
1455
|
+
const id = Schema.decodeSync(SubmissionId)(runId.slice(4));
|
|
1456
|
+
const stored = staged.submissions.get(id);
|
|
1457
|
+
return stored === void 0 ? void 0 : toAdmission(stored.row, settledTimes.get(id));
|
|
1458
|
+
}),
|
|
1459
|
+
commands: (id) => readCommands(staged.submissions.get(id))
|
|
1460
|
+
},
|
|
1461
|
+
stage: Effect.fnUntraced(function* (page) {
|
|
1462
|
+
for (const a of page.archive.admissions) {
|
|
1463
|
+
const key = admissionKey(threadId, a.principal, a.idempotencyKey);
|
|
1464
|
+
if (current.submissions.has(a.submissionId) || staged.submissions.has(a.submissionId) || current.receipts.has(a.receiptId) || staged.receipts.has(a.receiptId) || current.admissionIndex.has(key) || staged.admissionIndex.has(key)) return yield* ThreadImportRejected.make({
|
|
1465
|
+
threadId,
|
|
1466
|
+
reason: "destination-conflict",
|
|
1467
|
+
message: "Admission, receipt, or immutable admission key already exists"
|
|
1468
|
+
});
|
|
1469
|
+
if (threadRows(staged, threadId, a.queueSequence - 1).next().value?.row.queueSequence === a.queueSequence) return yield* invalidThreadArchive("Duplicate admission queue position", threadId);
|
|
1470
|
+
if (a.settledAt !== void 0) settledTimes.set(a.submissionId, DateTime.toEpochMillis(a.settledAt));
|
|
1471
|
+
withSubmission(staged, fromRebuilt(RebuiltSubmission.make({
|
|
1472
|
+
admission: a,
|
|
1473
|
+
state: "admitted",
|
|
1474
|
+
unknownToolCallIds: [],
|
|
1475
|
+
approvals: [],
|
|
1476
|
+
resolutions: []
|
|
1477
|
+
})));
|
|
1478
|
+
}
|
|
1479
|
+
for (const fact of page.archive.commands.aborts) {
|
|
1480
|
+
const stored = staged.submissions.get(fact.submissionId);
|
|
1481
|
+
if (stored === void 0 || stored.abortIntent !== void 0) return yield* invalidThreadArchive("Missing admission or duplicate abort", threadId);
|
|
1482
|
+
const intent = AbortIntent.make(fact);
|
|
1483
|
+
yield* retainCommandBytes(AbortIntent, intent, fact.submissionId);
|
|
1484
|
+
withSubmission(staged, {
|
|
1485
|
+
...stored,
|
|
1486
|
+
abortIntent: intent
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
for (const fact of page.archive.commands.approvals) {
|
|
1490
|
+
const stored = staged.submissions.get(fact.submissionId);
|
|
1491
|
+
if (stored === void 0 || stored.approvalDecisions.has(fact.toolCallId)) return yield* invalidThreadArchive("Missing admission or duplicate approval", threadId);
|
|
1492
|
+
if (stored.approvalDecisions.size >= MAX_RUN_EVIDENCE_RECORDS) return yield* invalidThreadArchive("Accepted approvals exceed Run record bounds", threadId);
|
|
1493
|
+
const intent = ApprovalDecisionIntent.make(fact);
|
|
1494
|
+
yield* retainCommandBytes(ApprovalDecisionIntent, intent, fact.submissionId);
|
|
1495
|
+
const approvals = new Map(stored.approvalDecisions).set(fact.toolCallId, intent);
|
|
1496
|
+
withSubmission(staged, {
|
|
1497
|
+
...stored,
|
|
1498
|
+
approvalDecisions: approvals
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
for (const fact of page.archive.commands.resolutions) {
|
|
1502
|
+
const stored = staged.submissions.get(fact.submissionId);
|
|
1503
|
+
const key = JSON.stringify([fact.toolCallId, unknownResolutionKind(fact.resolution)]);
|
|
1504
|
+
if (stored === void 0 || stored.unknownResolutions.has(key)) return yield* invalidThreadArchive("Missing admission or duplicate resolution", threadId);
|
|
1505
|
+
if (stored.unknownResolutions.size >= MAX_RUN_EVIDENCE_RECORDS) return yield* invalidThreadArchive("Accepted resolutions exceed Run record bounds", threadId);
|
|
1506
|
+
const intent = UnknownResolutionIntent.make(fact);
|
|
1507
|
+
yield* retainCommandBytes(UnknownResolutionIntent, intent, fact.submissionId);
|
|
1508
|
+
const resolutions = new Map(stored.unknownResolutions).set(key, { intent });
|
|
1509
|
+
withSubmission(staged, {
|
|
1510
|
+
...stored,
|
|
1511
|
+
unknownResolutions: resolutions
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1457
1514
|
}),
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1515
|
+
prepareCommit: Effect.fnUntraced(function* (workerSeal) {
|
|
1516
|
+
let lastQueue = 0;
|
|
1517
|
+
let terminalEvidence = workerSeal?.terminal === void 0;
|
|
1518
|
+
for (const stored of threadRows(staged, threadId)) {
|
|
1519
|
+
const a = toAdmission(stored.row, settledTimes.get(stored.row.submissionId));
|
|
1520
|
+
const rebuilt = yield* rebuildClosedSubmission(a);
|
|
1521
|
+
if (rebuilt.settlement !== void 0 && workerTerminalFromRecord(toSnapshot(stored.row), rebuilt.settlement) === workerSeal?.terminal) terminalEvidence = true;
|
|
1522
|
+
withSubmission(staged, fromRebuilt(rebuilt));
|
|
1523
|
+
lastQueue = Math.max(lastQueue, a.queueSequence);
|
|
1524
|
+
}
|
|
1525
|
+
if (!terminalEvidence) return yield* invalidThreadArchive("Worker seal terminal has no exact completed input evidence", threadId);
|
|
1526
|
+
if (!Number.isSafeInteger(lastQueue + 1)) return yield* ThreadImportRejected.make({
|
|
1464
1527
|
threadId,
|
|
1465
|
-
reason: "
|
|
1466
|
-
message: "Admission
|
|
1467
|
-
});
|
|
1468
|
-
if (threadRows(staged, threadId, a.queueSequence - 1).next().value?.row.queueSequence === a.queueSequence) return yield* invalidThreadArchive("Duplicate admission queue position", threadId);
|
|
1469
|
-
if (a.settledAt !== void 0) settledTimes.set(a.submissionId, DateTime.toEpochMillis(a.settledAt));
|
|
1470
|
-
withSubmission(staged, fromRebuilt(RebuiltSubmission.make({
|
|
1471
|
-
admission: a,
|
|
1472
|
-
state: "admitted",
|
|
1473
|
-
unknownToolCallIds: [],
|
|
1474
|
-
approvals: [],
|
|
1475
|
-
resolutions: []
|
|
1476
|
-
})));
|
|
1477
|
-
}
|
|
1478
|
-
for (const fact of page.archive.commands.aborts) {
|
|
1479
|
-
const stored = staged.submissions.get(fact.submissionId);
|
|
1480
|
-
if (stored === void 0 || stored.abortIntent !== void 0) return yield* invalidThreadArchive("Missing admission or duplicate abort", threadId);
|
|
1481
|
-
const intent = AbortIntent.make(fact);
|
|
1482
|
-
yield* retainCommandBytes(AbortIntent, intent, fact.submissionId);
|
|
1483
|
-
withSubmission(staged, {
|
|
1484
|
-
...stored,
|
|
1485
|
-
abortIntent: intent
|
|
1486
|
-
});
|
|
1487
|
-
}
|
|
1488
|
-
for (const fact of page.archive.commands.approvals) {
|
|
1489
|
-
const stored = staged.submissions.get(fact.submissionId);
|
|
1490
|
-
if (stored === void 0 || stored.approvalDecisions.has(fact.toolCallId)) return yield* invalidThreadArchive("Missing admission or duplicate approval", threadId);
|
|
1491
|
-
if (stored.approvalDecisions.size >= MAX_RUN_EVIDENCE_RECORDS) return yield* invalidThreadArchive("Accepted approvals exceed Run record bounds", threadId);
|
|
1492
|
-
const intent = ApprovalDecisionIntent.make(fact);
|
|
1493
|
-
yield* retainCommandBytes(ApprovalDecisionIntent, intent, fact.submissionId);
|
|
1494
|
-
const approvals = new Map(stored.approvalDecisions).set(fact.toolCallId, intent);
|
|
1495
|
-
withSubmission(staged, {
|
|
1496
|
-
...stored,
|
|
1497
|
-
approvalDecisions: approvals
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
for (const fact of page.archive.commands.resolutions) {
|
|
1501
|
-
const stored = staged.submissions.get(fact.submissionId);
|
|
1502
|
-
const key = JSON.stringify([fact.toolCallId, unknownResolutionKind(fact.resolution)]);
|
|
1503
|
-
if (stored === void 0 || stored.unknownResolutions.has(key)) return yield* invalidThreadArchive("Missing admission or duplicate resolution", threadId);
|
|
1504
|
-
if (stored.unknownResolutions.size >= MAX_RUN_EVIDENCE_RECORDS) return yield* invalidThreadArchive("Accepted resolutions exceed Run record bounds", threadId);
|
|
1505
|
-
const intent = UnknownResolutionIntent.make(fact);
|
|
1506
|
-
yield* retainCommandBytes(UnknownResolutionIntent, intent, fact.submissionId);
|
|
1507
|
-
const resolutions = new Map(stored.unknownResolutions).set(key, { intent });
|
|
1508
|
-
withSubmission(staged, {
|
|
1509
|
-
...stored,
|
|
1510
|
-
unknownResolutions: resolutions
|
|
1528
|
+
reason: "unsupported-capacity",
|
|
1529
|
+
message: "Admission queue sequence is exhausted"
|
|
1511
1530
|
});
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
if (!terminalEvidence) return yield* invalidThreadArchive("Worker seal terminal has no exact completed input evidence", threadId);
|
|
1525
|
-
if (!Number.isSafeInteger(lastQueue + 1)) return yield* ThreadImportRejected.make({
|
|
1526
|
-
threadId,
|
|
1527
|
-
reason: "unsupported-capacity",
|
|
1528
|
-
message: "Admission queue sequence is exhausted"
|
|
1529
|
-
});
|
|
1530
|
-
return () => {
|
|
1531
|
-
for (const stored of staged.submissions.values()) withSubmission(current, stored);
|
|
1532
|
-
current.lanes.set(threadId, { nextQueueSequence: lastQueue + 1 });
|
|
1533
|
-
if (workerSeal !== void 0) current.stoppedWorkers.set(threadId, workerSeal.terminal);
|
|
1534
|
-
};
|
|
1535
|
-
})
|
|
1536
|
-
};
|
|
1537
|
-
})
|
|
1538
|
-
});
|
|
1539
|
-
return Context.make(SubmissionLedger, ledger).pipe(Context.add(SettlementPublisher, { publish }));
|
|
1540
|
-
});
|
|
1531
|
+
return () => {
|
|
1532
|
+
for (const stored of staged.submissions.values()) withSubmission(current, stored);
|
|
1533
|
+
current.lanes.set(threadId, { nextQueueSequence: lastQueue + 1 });
|
|
1534
|
+
if (workerSeal !== void 0) current.stoppedWorkers.set(threadId, workerSeal.terminal);
|
|
1535
|
+
};
|
|
1536
|
+
})
|
|
1537
|
+
};
|
|
1538
|
+
})
|
|
1539
|
+
});
|
|
1540
|
+
return Context.make(SubmissionLedger, ledger).pipe(Context.add(SettlementPublisher, { publish }));
|
|
1541
|
+
}
|
|
1542
|
+
);
|
|
1541
1543
|
/**
|
|
1542
1544
|
* In-memory reference SubmissionLedger Layer (durability `non-durable`). All state lives in one
|
|
1543
1545
|
* `Ref` owned by the Layer's Scope. Fresh identities use the supplied `Crypto` service;
|