@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.
@@ -272,1272 +272,1274 @@ function* threadRows(state, threadId, after = -1) {
272
272
  if (stored !== void 0) yield stored;
273
273
  }
274
274
  }
275
- /**
276
- * Reference in-memory SubmissionLedger. Admission, ordered selection and canonical settlement
277
- * share the Memory kernel's mutation gate. The kernel Scope owns local Run capabilities;
278
- * selecting work does not write execution ownership into the ledger.
279
- *
280
- * Joining retains the contiguous ready prefix and exact host linkage. Canonical settlement
281
- * authorizes finalization and child wake repair, while bound sessions reject late commands.
282
- * Child wake checks read the child rows directly, without a separate notification marker.
283
- * Admission resolution is strongly consistent; resolveAdmissionFault injects the
284
- * Indeterminate outcome for existing caller conformance workflows.
285
- */
286
- const makeSubmissionLedger = (options = {}) => Effect.gen(function* () {
287
- const journal = yield* MemoryThreadStoreKernel;
288
- const crypto = yield* Crypto.Crypto;
289
- const mintIdentity = (operation) => crypto.randomUUIDv4().pipe(Effect.flatMap(Schema.decodeEffect(MemoryIdentityNonce)), Effect.mapError((cause) => ledgerError(operation, "Cannot mint a Memory identity", cause)));
290
- const state = yield* Ref.make({
291
- byThread: /* @__PURE__ */ new Map(),
292
- receipts: /* @__PURE__ */ new Map(),
293
- joinsByHost: /* @__PURE__ */ new Map(),
294
- childrenByParent: /* @__PURE__ */ new Map(),
295
- submissions: /* @__PURE__ */ new Map(),
296
- admissionIndex: /* @__PURE__ */ new Map(),
297
- lanes: /* @__PURE__ */ new Map(),
298
- childReservations: /* @__PURE__ */ new Map(),
299
- freshAdmissionSequence: 0,
300
- stoppedWorkers: /* @__PURE__ */ new Map(),
301
- latestByThread: /* @__PURE__ */ new Map(),
302
- activeByThread: /* @__PURE__ */ new Map()
303
- });
304
- yield* journal.registerWorkOwner("admissions", {
305
- threads: (request) => Ref.get(state).pipe(Effect.map((current) => {
306
- const candidates = boundedWorkSelection(request.limit, (id) => id, request.afterThreadId);
307
- for (const [threadId, ids] of current.activeByThread) if (ids.size > 0) candidates.add(threadId);
308
- const threadIds = candidates.values;
309
- const afterThreadId = threadIds.length === request.limit ? threadIds.at(-1) : void 0;
310
- return {
311
- threadIds,
312
- ...afterThreadId === void 0 ? {} : { afterThreadId }
313
- };
314
- })),
315
- page: (threadId, after, limit) => Effect.gen(function* () {
316
- const current = yield* Ref.get(state);
317
- const candidates = boundedWorkSelection(limit + 1, (id) => id, after);
318
- for (const id of current.activeByThread.get(threadId) ?? []) candidates.add(id);
319
- const ids = candidates.values;
320
- const entries = [];
321
- for (const id of ids.slice(0, limit)) {
322
- const stored = current.submissions.get(id);
323
- if (stored === void 0 || stored.row.threadId !== threadId || stored.row.state === "settled") return yield* ThreadStoreError.make({
324
- operation: "admission work",
325
- message: "Active admission index is incomplete or corrupt"
326
- });
327
- const entry = admissionWork(stored.row);
328
- entries.push(stored.suspension === void 0 ? entry : {
329
- ...entry,
330
- wait: stored.suspension.reason._tag === "WaitingForChild" ? "child" : "approval"
331
- });
332
- }
333
- const next = ids.length > limit ? ids[limit - 1] : void 0;
334
- return {
335
- entries,
336
- ...next === void 0 ? {} : { after: next }
337
- };
338
- })
339
- });
340
- const admissionFence = yield* SubmissionAdmissionFence;
341
- const capabilities = Effect.succeed(LedgerCapabilities.make({ durability: "non-durable" }));
342
- const admit = Effect.fnUntraced(function* (unvalidated) {
343
- const request = yield* validate(AdmissionRequest, "admit", unvalidated);
344
- 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")));
345
- 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")));
346
- yield* journal.checkThreadCapacity(request.threadId).pipe(Effect.mapError((cause) => ledgerError("admit", cause.message, cause)));
347
- const nowMillis = yield* Clock.currentTimeMillis;
348
- 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"));
349
- const services = yield* Effect.context();
350
- const decision = yield* Ref.modify(state, (current) => {
351
- const key = admissionKey(request.threadId, request.principal, request.idempotencyKey);
352
- const existingId = current.admissionIndex.get(key);
353
- if (existingId !== void 0) {
354
- const existing = current.submissions.get(existingId);
355
- if (existing === void 0) return [failure(ledgerError("admit", "Admission index references a missing Submission")), current];
356
- 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({
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
- existingInputDigest: existing.row.inputDigest,
361
- attemptedInputDigest: request.inputDigest
362
- })), current];
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: existing.row.submissionId,
365
- receiptId: existing.row.receiptId,
366
- queueSequence: existing.row.queueSequence,
367
- state: existing.row.state,
368
- replayed: true
369
- })), current];
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
- if (current.stoppedWorkers.has(request.threadId)) return [failure(AdmissionPolicyError.make({
372
- reason: "refused",
373
- code: "worker-stopped"
374
- })), current];
375
- const first = threadRows(current, request.threadId).next().value;
376
- if (first !== void 0) {
377
- const previous = first.row.workerAdmissionJson === void 0 ? void 0 : Schema.decodeSync(Schema.fromJsonString(WorkerAdmission))(first.row.workerAdmissionJson);
378
- if (!Schema.toEquivalence(Schema.optional(WorkerAdmission.fields.origin))(previous?.origin, request.workerAdmission?.origin)) return [failure(AdmissionPolicyError.make({
379
- reason: "refused",
380
- code: "worker-origin-conflict"
381
- })), current];
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 checked = Effect.runSyncExitWith(services)(admissionFence.check(request));
384
- if (Exit.isFailure(checked)) return [{
385
- _tag: "cause",
386
- cause: checked.cause
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
- if (decision._tag === "failure") return yield* decision.error;
451
- if (decision._tag === "cause") {
452
- for (const reason of decision.cause.reasons) if (Cause.isDieReason(reason) && Cause.isAsyncFiberError(reason.defect)) {
453
- yield* Fiber.interrupt(reason.defect.fiber);
454
- return yield* AdmissionPolicyError.make({
455
- reason: "unavailable",
456
- code: "synchronous-memory-policy-required"
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: "ready",
475
- readyAtMillis: nowMillis
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
- if (decision._tag === "failure") return yield* decision.error;
480
- });
481
- const lookup = Effect.fnUntraced(function* (unvalidated) {
482
- const request = yield* validate(SubmissionLookup, "lookup", unvalidated);
483
- const current = yield* Ref.get(state);
484
- const submissionId = request._tag === "SubmissionLookupById" ? request.submissionId : current.admissionIndex.get(admissionKey(request.threadId, request.principal, request.idempotencyKey));
485
- const stored = submissionId === void 0 ? void 0 : current.submissions.get(submissionId);
486
- return stored === void 0 ? Option.none() : Option.some(toSnapshot(stored.row));
487
- });
488
- const resolveAdmission = Effect.fnUntraced(function* (unvalidated) {
489
- const request = yield* validate(SubmissionLookupByKey, "resolveAdmission", unvalidated);
490
- if (options.resolveAdmissionFault !== void 0) {
491
- const fault = yield* options.resolveAdmissionFault;
492
- if (Option.isSome(fault)) return AdmissionIndeterminate.make({ reason: fault.value });
493
- }
494
- const current = yield* Ref.get(state);
495
- const submissionId = current.admissionIndex.get(admissionKey(request.threadId, request.principal, request.idempotencyKey));
496
- const stored = submissionId === void 0 ? void 0 : current.submissions.get(submissionId);
497
- return stored === void 0 ? AdmissionNotAdmitted.make() : AdmissionAdmitted.make({ submission: toSnapshot(stored.row) });
498
- });
499
- const selectHead = Effect.fn("MemorySubmissionLedger.selectHead")((unvalidated) => Effect.gen(function* () {
500
- const request = yield* validate(ClaimRequest, "claim", unvalidated);
501
- const identity = yield* mintIdentity("claim");
502
- const decision = yield* Ref.modify(state, (current) => {
503
- if (journal.owner.hasOwner(request.threadId)) return [success(Option.none()), current];
504
- let head = findHead(current, request.threadId);
505
- if (request.handoff !== void 0) {
506
- const handoff = request.handoff;
507
- 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);
508
- for (const candidate of candidates) {
509
- if (candidate.row.submissionId === handoff.submissionId) {
510
- head = candidate;
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
- if (!handoff.deferredSubmissionIds.includes(candidate.row.submissionId) || candidate.row.state !== "input-applied" || candidate.abortIntent !== void 0) return [success(Option.none()), current];
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 (head?.row.submissionId !== handoff.submissionId || head.row.state !== "ready" && head.row.state !== "input-applied") return [success(Option.none()), current];
516
- }
517
- if (head === void 0) return [success(Option.none()), current];
518
- if (BLOCKED_HEAD_STATES.has(head.row.state)) return [success(Option.none()), current];
519
- return [success(Option.some(Claim.make({
520
- submissionId: head.row.submissionId,
521
- attemptId: decodeAttemptId(`attempt-memory-${identity}`),
522
- inputPayload: head.row.inputPayload
523
- }))), current];
524
- });
525
- if (decision._tag === "failure") return yield* decision.error;
526
- return decision.value;
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
- if (decision._tag === "failure") return yield* decision.error;
553
- });
554
- const canonicalSettlement = Effect.fnUntraced(function* (submission) {
555
- const record = yield* journal.record(submission.threadId, submissionSettlementRecordId(submission.submissionId));
556
- if (record === void 0) return void 0;
557
- return {
558
- record,
559
- settlement: yield* validateCanonicalSettlement(record, submission)
560
- };
561
- });
562
- const publish = Effect.fn("MemorySettlementPublisher.publish")(function* (input) {
563
- const { request, record, settlement } = yield* validatePublication(input);
564
- const prepared = yield* journal.prepareAppend(request.append);
565
- return yield* journal.owner.withProgress(request.append.threadId, journal.withMutation(Effect.gen(function* () {
566
- const current = yield* Ref.get(state);
567
- const stored = current.submissions.get(request.submissionId);
568
- if (stored === void 0) return yield* ledgerError("publish settlement", "Unknown Submission");
569
- if (stored.row.threadId !== prepared.request.threadId) return yield* ledgerError("publish settlement", "Publication targets another Thread");
570
- yield* validateCanonicalSettlement(record, stored.row);
571
- const existing = yield* canonicalSettlement(stored.row);
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
- ...settlement.outcome === "aborted" && settlement.runId === void 0 ? {} : { runId: runIdForSubmission(request.submissionId) }
580
- });
581
- break;
582
- case "Joined": {
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
- case "QueuedAbort":
591
- 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");
592
- yield* validateCanonicalSettlement(record, {
593
- submissionId: request.submissionId,
594
- receiptId: stored.row.receiptId,
595
- runId: void 0
596
- });
597
- }
598
- if (existing !== void 0) return SettlementPublicationResult.make({
599
- record: existing.record,
600
- tailSequence: tail.tailSequence,
601
- replayed: true
602
- });
603
- if (stored.row.state === "settled") return yield* ledgerError("publish settlement", "Finalized Submission has no canonical settlement");
604
- const appended = yield* journal.appendPrepared(prepared);
605
- return SettlementPublicationResult.make({
606
- record,
607
- tailSequence: appended.lastSequence,
608
- replayed: false
609
- });
610
- })));
611
- });
612
- const finalizeSettlement = Effect.fn("MemorySubmissionLedger.finalizeSettlement")((unvalidated) => Effect.gen(function* () {
613
- const request = yield* validate(SettlementFinalization, "finalizeSettlement", unvalidated);
614
- const before = (yield* Ref.get(state)).submissions.get(request.submissionId);
615
- const canonical = before === void 0 ? void 0 : yield* canonicalSettlement(before.row);
616
- if (canonical === void 0) return yield* ledgerError("finalizeSettlement", "Submission has no canonical settlement");
617
- const { record, settlement } = canonical;
618
- const nowMillis = yield* Clock.currentTimeMillis;
619
- const decision = yield* Ref.modify(state, (current) => {
620
- const stored = current.submissions.get(request.submissionId);
621
- if (stored === void 0) return [failure(ledgerError("finalizeSettlement", `Unknown Submission ${request.submissionId}`)), current];
622
- const settlementFailure = settlementFailureFromRecord(record);
623
- if (settlement.settlementId !== request.settlementId) return [failure(SettlementConflict.make({
624
- submissionId: request.submissionId,
625
- existingOutcome: settlement.outcome
626
- })), current];
627
- if (stored.finalization !== void 0) {
628
- 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];
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(stored.finalization.finalizedAtMillis)
636
- })), current];
637
- }
638
- if (stored.row.state === "settled") return [failure(ledgerError("finalizeSettlement", "Finalized timestamp is missing")), current];
639
- const terminal = stored.row.workerAdmissionJson === void 0 ? void 0 : workerTerminalFromRecord(toSnapshot(stored.row), record);
640
- const latestId = current.latestByThread.get(stored.row.threadId);
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 pendingNewer = terminal === "completed" && latest !== void 0 && latest.row.queueSequence > stored.row.queueSequence && !(latest.joinedHostSubmissionId === stored.row.submissionId && latest.inputApplied !== void 0);
643
- let sealed = current;
644
- if (terminal !== void 0 && !pendingNewer && !current.stoppedWorkers.has(stored.row.threadId)) {
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
- if (!transferPageFits(stored.row.threadId)) return [failure(transferCapacityError("finalizeSettlement")), current];
647
- for (const apply of [false, true]) for (const id of current.activeByThread.get(stored.row.threadId) ?? []) {
648
- const other = submissions.get(id);
649
- if (other === void 0) continue;
650
- if (id !== stored.row.submissionId && !(other.joinedHostSubmissionId === stored.row.submissionId && other.inputApplied !== void 0) && other.abortIntent === void 0) {
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: stored.row.principal,
654
- reason: `Worker assignment ${terminal}`,
655
- requestedAt: utc(nowMillis)
727
+ author: request.author,
728
+ reason: "Worker owner stopped the worker",
729
+ requestedAt: utc(now)
656
730
  });
657
- if (!apply && !abortFitsTransfer(other.row.threadId, abortIntent)) return [failure(transferCapacityError("finalizeSettlement")), current];
731
+ if (!apply && !abortFitsTransfer(stored.row.threadId, abortIntent)) return [failure(transferCapacityError("stopWorker")), current];
658
732
  if (apply) withSubmission(current, {
659
- ...other,
733
+ ...stored,
660
734
  abortIntent
661
735
  });
662
736
  }
663
737
  }
664
- sealed = {
738
+ return [success(owned), {
665
739
  ...current,
666
740
  submissions,
667
- stoppedWorkers: current.stoppedWorkers.set(stored.row.threadId, terminal)
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
- return [success(Settlement.make({
684
- submissionId: stored.row.submissionId,
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
- if (decision._tag === "failure") return yield* decision.error;
693
- if (before !== void 0) journal.owner.closeSubmission(before.row.threadId, request.submissionId);
694
- return decision.value;
695
- }));
696
- const inspectWorker = Effect.fnUntraced(function* (threadId) {
697
- const current = yield* Ref.get(state);
698
- const latestId = current.latestByThread.get(threadId);
699
- const latest = latestId === void 0 ? void 0 : current.submissions.get(latestId);
700
- const active = [...current.activeByThread.get(threadId) ?? []].flatMap((id) => {
701
- const row = current.submissions.get(id);
702
- return row === void 0 ? [] : [row];
703
- }).sort((a, b) => a.row.queueSequence - b.row.queueSequence)[0];
704
- const terminal = current.stoppedWorkers.get(threadId);
705
- return WorkerLedgerState.make({
706
- latest: latest === void 0 ? null : toSnapshot(latest.row),
707
- active: active === void 0 ? null : toSnapshot(active.row),
708
- stopped: current.stoppedWorkers.has(threadId),
709
- ...terminal === void 0 ? {} : { terminal }
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
- const stopWorker = Effect.fnUntraced(function* (unvalidated) {
713
- const request = yield* validate(WorkerStopCommand, "stopWorker", unvalidated);
714
- const now = yield* Clock.currentTimeMillis;
715
- const decision = yield* Ref.modify(state, (current) => {
716
- const submissions = current.submissions;
717
- let owned = 0;
718
- if (!current.stoppedWorkers.has(request.threadId) && !transferPageFits(request.threadId)) return [failure(transferCapacityError("stopWorker")), current];
719
- for (const apply of [false, true]) for (const id of current.activeByThread.get(request.threadId) ?? []) {
720
- const stored = submissions.get(id);
721
- if (stored === void 0) continue;
722
- if (!apply && journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId)) owned++;
723
- if (stored.abortIntent === void 0) {
724
- const abortIntent = AbortIntent.make({
725
- submissionId: id,
726
- author: request.author,
727
- reason: "Worker owner stopped the worker",
728
- requestedAt: utc(now)
729
- });
730
- if (!apply && !abortFitsTransfer(stored.row.threadId, abortIntent)) return [failure(transferCapacityError("stopWorker")), current];
731
- if (apply) withSubmission(current, {
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
- abortIntent
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
- return [success(owned), {
738
- ...current,
739
- submissions,
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 (!abortFitsTransfer(stored.row.threadId, intent)) return [failure(transferCapacityError("requestAbort")), current];
774
- return [success(intent), withSubmission(current, {
775
- ...stored,
776
- abortIntent: intent
777
- })];
829
+ if (decision._tag === "failure") return yield* decision.error;
830
+ return decision.value;
778
831
  });
779
- if (decision._tag === "failure") return yield* decision.error;
780
- return decision.value;
781
- });
782
- const claimJoining = Effect.fnUntraced(function* (unvalidated) {
783
- const request = yield* validate(ClaimJoiningRequest, "claimJoining", unvalidated);
784
- const published = /* @__PURE__ */ new Map();
785
- const before = yield* Ref.get(state);
786
- for (const id of before.activeByThread.get(request.threadId) ?? []) {
787
- const stored = before.submissions.get(id);
788
- if (stored === void 0 || stored.row.state !== "ready") continue;
789
- const canonical = yield* canonicalSettlement(stored.row);
790
- if (canonical !== void 0) published.set(stored.row.submissionId, canonical.settlement.outcome);
791
- }
792
- const decision = yield* Ref.modify(state, (current) => {
793
- if (current.stoppedWorkers.has(request.threadId)) return [success([]), current];
794
- const host = current.submissions.get(request.hostSubmissionId);
795
- if (host === void 0) return [failure(ledgerError("claimJoining", `Unknown Submission ${request.hostSubmissionId}`)), current];
796
- if (host.row.threadId !== request.threadId) return [failure(ledgerError("claimJoining", `Host Submission ${request.hostSubmissionId} does not belong to Thread ${request.threadId}`)), current];
797
- if (!journal.owner.hasOwner(host.row.threadId, host.row.submissionId)) return [failure(ownershipLost(host)), current];
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: "joining"
854
+ state: "joined"
814
855
  },
815
- joinedHostSubmissionId: request.hostSubmissionId
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
- return [success(void 0), withSubmission(current, {
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
- if (decision._tag === "failure") return yield* decision.error;
859
- });
860
- const revertJoining = Effect.fnUntraced(function* (unvalidated) {
861
- const request = yield* validate(RevertJoiningRequest, "revertJoining", unvalidated);
862
- const decision = yield* Ref.modify(state, (current) => {
863
- const stored = current.submissions.get(request.submissionId);
864
- if (stored === void 0) return [failure(ledgerError("revertJoining", `Unknown Submission ${request.submissionId}`)), current];
865
- if (stored.row.state !== "joining") return [success(void 0), current];
866
- const guard = request.guard;
867
- if (guard !== void 0) {
868
- if (stored.joinedHostSubmissionId !== guard.hostSubmissionId) return [success(void 0), current];
869
- const host = current.submissions.get(guard.hostSubmissionId);
870
- 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];
871
- 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];
872
- }
873
- return [success(void 0), withSubmission(current, {
874
- ...stored,
875
- row: {
876
- ...stored.row,
877
- state: "ready"
878
- },
879
- joinedHostSubmissionId: void 0
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
- if (decision._tag === "failure") return yield* decision.error;
883
- });
884
- const suspend = Effect.fnUntraced(function* (unvalidated) {
885
- const request = yield* validate(SuspendRequest, "suspend", unvalidated);
886
- const nowMillis = yield* Clock.currentTimeMillis;
887
- const target = (yield* Ref.get(state)).submissions.get(request.submissionId);
888
- const canonical = target === void 0 ? void 0 : yield* canonicalSettlement(target.row);
889
- const announcedChildren = /* @__PURE__ */ new Set();
890
- if (request.reason._tag === "WaitingForChild") {
891
- const current = yield* Ref.get(state);
892
- for (const { childSubmissionId } of request.reason.children) {
893
- const child = current.submissions.get(childSubmissionId);
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
- const decision = yield* Ref.modify(state, (current) => {
898
- const stored = current.submissions.get(request.submissionId);
899
- if (stored === void 0) return [failure(ledgerError("suspend", `Unknown Submission ${request.submissionId}`)), current];
900
- if (stored.row.state === "settled") {
901
- if (stored.row.settledOutcome === void 0) return [failure(ledgerError("suspend", `Settled Submission ${request.submissionId} is missing its outcome`)), current];
902
- return [failure(SettlementConflict.make({
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: stored.row.settledOutcome
910
+ existingOutcome: canonical.settlement.outcome
905
911
  })), current];
906
- }
907
- if (canonical !== void 0) return [failure(SettlementConflict.make({
908
- submissionId: request.submissionId,
909
- existingOutcome: canonical.settlement.outcome
910
- })), current];
911
- if (!journal.owner.hasOwner(stored.row.threadId, stored.row.submissionId)) return [failure(ownershipLost(stored)), current];
912
- 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];
913
- return [success("suspended"), withSubmission(current, {
914
- ...stored,
915
- row: {
916
- ...stored.row,
917
- state: "suspended"
918
- },
919
- suspension: {
920
- reason: request.reason,
921
- suspendedAtMillis: nowMillis
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
- if (decision._tag === "failure") return yield* decision.error;
926
- if (decision.value === "suspended" && target !== void 0) journal.owner.closeSubmission(target.row.threadId, request.submissionId);
927
- return decision.value;
928
- });
929
- const recordApprovalDecision = Effect.fnUntraced(function* (unvalidated) {
930
- const command = yield* validate(ApprovalDecisionCommand, "recordApprovalDecision", unvalidated);
931
- const nowMillis = yield* Clock.currentTimeMillis;
932
- const decision = yield* Ref.modify(state, (current) => {
933
- const stored = current.submissions.get(command.submissionId);
934
- if (stored === void 0) return [failure(ledgerError("recordApprovalDecision", `Unknown Submission ${command.submissionId}`)), current];
935
- if (stored.row.state === "settled") {
936
- if (stored.row.settledOutcome === void 0) return [failure(ledgerError("recordApprovalDecision", `Settled Submission ${command.submissionId} is missing its outcome`)), current];
937
- return [failure(SettlementConflict.make({
938
- submissionId: command.submissionId,
939
- existingOutcome: stored.row.settledOutcome
940
- })), current];
941
- }
942
- const existing = stored.approvalDecisions.get(command.toolCallId);
943
- if (existing !== void 0) {
944
- if (existing.decision !== command.decision) return [failure(ApprovalConflict.make({
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
- existingDecision: existing.decision
948
- })), current];
949
- return [success(existing), current];
950
- }
951
- const intent = ApprovalDecisionIntent.make({
952
- submissionId: command.submissionId,
953
- toolCallId: command.toolCallId,
954
- decision: command.decision,
955
- resolver: command.resolver,
956
- reason: command.reason,
957
- decidedAt: utc(nowMillis)
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 (!approvalFitsTransfer(stored.row.threadId, intent)) return [failure(transferCapacityError("recordApprovalDecision")), current];
960
- const approvalDecisions = new Map(stored.approvalDecisions).set(command.toolCallId, intent);
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
- if (decision._tag === "failure") return yield* decision.error;
973
- return decision.value;
974
- });
975
- const markUnknown = Effect.fnUntraced(function* (unvalidated) {
976
- const request = yield* validate(MarkUnknownRequest, "markUnknown", unvalidated);
977
- const target = (yield* Ref.get(state)).submissions.get(request.submissionId);
978
- const canonical = target === void 0 ? void 0 : yield* canonicalSettlement(target.row);
979
- const decision = yield* Ref.modify(state, (current) => {
980
- const stored = current.submissions.get(request.submissionId);
981
- if (stored === void 0) return [failure(ledgerError("markUnknown", `Unknown Submission ${request.submissionId}`)), current];
982
- if (stored.row.state === "settled") {
983
- if (stored.row.settledOutcome === void 0) return [failure(ledgerError("markUnknown", `Settled Submission ${request.submissionId} is missing its outcome`)), current];
984
- return [failure(SettlementConflict.make({
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: stored.row.settledOutcome
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
- if (canonical !== void 0) return [failure(SettlementConflict.make({
990
- submissionId: request.submissionId,
991
- existingOutcome: canonical.settlement.outcome
992
- })), current];
993
- const existing = stored.unknownMark;
994
- const known = new Set(existing?.toolCallIds ?? []);
995
- const merged = [...existing?.toolCallIds ?? [], ...request.toolCallIds.filter((toolCallId) => !known.has(toolCallId))];
996
- return [success(void 0), withSubmission(current, {
997
- ...stored,
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
- if (decision._tag === "failure") return yield* decision.error;
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
- existingOutcome: stored.row.settledOutcome
1040
+ toolCallId: command.toolCallId
1033
1041
  })), current];
1034
- }
1035
- const key = JSON.stringify([command.toolCallId, kind]);
1036
- const existing = stored.unknownResolutions.get(key);
1037
- 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({
1038
- submissionId: command.submissionId,
1039
- toolCallId: command.toolCallId
1040
- })), current];
1041
- const intent = existing?.intent ?? UnknownResolutionIntent.make({
1042
- submissionId: command.submissionId,
1043
- toolCallId: command.toolCallId,
1044
- author: command.author,
1045
- reason: command.reason,
1046
- resolution: command.resolution,
1047
- resolvedAt: utc(nowMillis)
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 (existing === void 0 && !resolutionFitsTransfer(stored.row.threadId, intent)) return [failure(transferCapacityError("recordUnknownResolution")), current];
1050
- const unknownResolutions = existing !== void 0 ? stored.unknownResolutions : new Map(stored.unknownResolutions).set(key, { intent });
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
- if (decision._tag === "failure") return yield* decision.error;
1063
- return decision.value;
1064
- });
1065
- const recordChildSettled = Effect.fnUntraced(function* (unvalidated) {
1066
- const request = yield* validate(ChildSettledNotification, "recordChildSettled", unvalidated);
1067
- const before = yield* Ref.get(state);
1068
- const waiting = before.submissions.get(request.parentSubmissionId)?.suspension?.reason;
1069
- const ids = /* @__PURE__ */ new Set([request.childSubmissionId, ...waiting?._tag === "WaitingForChild" ? waiting.children.map((child) => child.childSubmissionId) : []]);
1070
- const announcedChildren = /* @__PURE__ */ new Set();
1071
- for (const id of ids) {
1072
- const child = before.submissions.get(id);
1073
- if (child !== void 0 && (yield* canonicalSettlement(child.row)) !== void 0) announcedChildren.add(id);
1074
- }
1075
- const decision = yield* Ref.modify(state, (current) => {
1076
- const parent = current.submissions.get(request.parentSubmissionId);
1077
- if (parent === void 0) return [failure(ledgerError("recordChildSettled", `Unknown Submission ${request.parentSubmissionId}`)), current];
1078
- if (!announcedChildren.has(request.childSubmissionId)) return [failure(ledgerError("recordChildSettled", `Child Submission ${request.childSubmissionId} has no recorded settlement`)), current];
1079
- if (parent.row.state !== "suspended" || parent.suspension === void 0 || parent.suspension.reason._tag !== "WaitingForChild") return [success("not-waiting"), current];
1080
- const children = parent.suspension.reason.children;
1081
- if (!children.some((entry) => entry.childSubmissionId === request.childSubmissionId)) return [success("not-waiting"), current];
1082
- if (!children.every((entry) => announcedChildren.has(entry.childSubmissionId))) return [success("still-waiting"), current];
1083
- return [success("woken"), withSubmission(current, {
1084
- ...parent,
1085
- row: {
1086
- ...parent.row,
1087
- state: "input-applied"
1088
- },
1089
- suspension: void 0
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
- if (decision._tag === "failure") return yield* decision.error;
1093
- return decision.value;
1094
- });
1095
- const reserveChildBudget = Effect.fnUntraced(function* (unvalidated) {
1096
- const request = yield* validate(ChildBudgetReservationRequest, "reserveChildBudget", unvalidated);
1097
- const nowMillis = yield* Clock.currentTimeMillis;
1098
- const decision = yield* Ref.modify(state, (current) => {
1099
- const existing = current.childReservations.get(request.reservationId);
1100
- if (existing !== void 0) {
1101
- if (!(existing.parentSubmissionId === request.parentSubmissionId && existing.parentToolCallId === request.parentToolCallId && existing.allocationDigest === request.allocationDigest && equivalentPersistedJson(existing.allocation, request.allocation))) return [failure(ChildReservationConflict.make({
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
- status: existing.status,
1104
- message: "A reservation with this identity exists with a different parent Tool Call or allocation."
1105
- })), current];
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(existing),
1108
- replayed: true
1109
- })), current];
1110
- }
1111
- for (const id of current.childrenByParent.get(request.parentSubmissionId) ?? []) {
1112
- const reservation = current.childReservations.get(id);
1113
- if (reservation === void 0) continue;
1114
- if (reservation.parentSubmissionId === request.parentSubmissionId && reservation.parentToolCallId === request.parentToolCallId) return [failure(ChildReservationConflict.make({
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: `Parent Tool Call ${request.parentToolCallId} already owns reservation ${reservation.reservationId}.`
1164
+ message: `Cannot attach a child to a ${reservation.status} reservation.`
1118
1165
  })), current];
1119
- }
1120
- const parent = current.submissions.get(request.parentSubmissionId);
1121
- if (parent === void 0) return [failure(ledgerError("reserveChildBudget", `Unknown Submission ${request.parentSubmissionId}`)), current];
1122
- if (!journal.owner.hasOwner(parent.row.threadId, parent.row.submissionId)) return [failure(ownershipLost(parent)), current];
1123
- const reservation = {
1124
- reservationId: request.reservationId,
1125
- parentSubmissionId: request.parentSubmissionId,
1126
- parentToolCallId: request.parentToolCallId,
1127
- childSubmissionId: void 0,
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
- if (decision._tag === "failure") return yield* decision.error;
1142
- return decision.value;
1143
- });
1144
- const attachChildToReservation = Effect.fnUntraced(function* (unvalidated) {
1145
- const request = yield* validate(AttachChildToReservationRequest, "attachChildToReservation", unvalidated);
1146
- const decision = yield* Ref.modify(state, (current) => {
1147
- const reservation = current.childReservations.get(request.reservationId);
1148
- if (reservation === void 0) return [failure(ledgerError("attachChildToReservation", `Unknown child reservation ${request.reservationId}`)), current];
1149
- if (reservation.childSubmissionId !== void 0) {
1150
- if (reservation.childSubmissionId === request.childSubmissionId) return [success(toReservationSnapshot(reservation)), current];
1151
- return [failure(ChildReservationConflict.make({
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: `Reservation ${request.reservationId} already records child ${reservation.childSubmissionId}.`
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 parent = current.submissions.get(reservation.parentSubmissionId);
1158
- if (parent === void 0) return [failure(ledgerError("attachChildToReservation", `Unknown Submission ${reservation.parentSubmissionId}`)), current];
1159
- if (!journal.owner.hasOwner(parent.row.threadId, parent.row.submissionId)) return [failure(ownershipLost(parent)), current];
1160
- if (reservation.status !== "reserved") return [failure(ChildReservationConflict.make({
1161
- reservationId: request.reservationId,
1162
- status: reservation.status,
1163
- message: `Cannot attach a child to a ${reservation.status} reservation.`
1164
- })), current];
1165
- if (!current.submissions.has(request.childSubmissionId)) return [failure(ledgerError("attachChildToReservation", `Unknown child Submission ${request.childSubmissionId}`)), current];
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
- if (decision._tag === "failure") return yield* decision.error;
1173
- return decision.value;
1174
- });
1175
- const beginChildBudgetRelease = Effect.fnUntraced(function* (unvalidated) {
1176
- const request = yield* validate(BeginChildBudgetReleaseRequest, "beginChildBudgetRelease", unvalidated);
1177
- const nowMillis = yield* Clock.currentTimeMillis;
1178
- const decision = yield* Ref.modify(state, (current) => {
1179
- const reservation = current.childReservations.get(request.reservationId);
1180
- if (reservation === void 0) return [failure(ledgerError("beginChildBudgetRelease", `Unknown child reservation ${request.reservationId}`)), current];
1181
- if (reservation.status !== "reserved") {
1182
- if (reservation.accounting !== void 0 && equivalentPersistedJson(reservation.accounting, request.accounting)) return [success(toReservationSnapshot(reservation)), current];
1183
- return [failure(ChildReservationConflict.make({
1184
- reservationId: request.reservationId,
1185
- status: reservation.status,
1186
- message: "A different accounting decision is already frozen for this reservation."
1187
- })), current];
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
- const frozen = {
1190
- ...reservation,
1191
- status: "releasePending",
1192
- accounting: request.accounting,
1193
- releaseBeganAtMillis: nowMillis
1194
- };
1195
- return [success(toReservationSnapshot(frozen)), withChildReservation(current, frozen)];
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
- if (decision._tag === "failure") return yield* decision.error;
1198
- return decision.value;
1199
- });
1200
- const releaseChildBudget = Effect.fnUntraced(function* (unvalidated) {
1201
- const request = yield* validate(ReleaseChildBudgetRequest, "releaseChildBudget", unvalidated);
1202
- const nowMillis = yield* Clock.currentTimeMillis;
1203
- const decision = yield* Ref.modify(state, (current) => {
1204
- const reservation = current.childReservations.get(request.reservationId);
1205
- if (reservation === void 0) return [failure(ledgerError("releaseChildBudget", `Unknown child reservation ${request.reservationId}`)), current];
1206
- if (reservation.status === "released") return [success(toReservationSnapshot(reservation)), current];
1207
- if (reservation.status !== "releasePending") return [failure(ChildReservationConflict.make({
1208
- reservationId: request.reservationId,
1209
- status: reservation.status,
1210
- message: "Cannot release a reservation whose accounting decision is not frozen."
1211
- })), current];
1212
- const released = {
1213
- ...reservation,
1214
- status: "released",
1215
- releasedAtMillis: nowMillis
1216
- };
1217
- return [success(toReservationSnapshot(released)), withChildReservation(current, released)];
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
- if (decision._tag === "failure") return yield* decision.error;
1220
- return decision.value;
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
- const ledger = SubmissionLedger.of({
1282
- capabilities,
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
- idle: (current.activeByThread.get(threadId)?.size ?? 0) === 0 && (directory?.reservations ?? 0) === 0,
1370
- admissions: directory?.ordered.length ?? 0,
1371
- aborts: directory?.commands.aborts.length ?? 0,
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
- facts: (threadId, section, after) => Effect.sync(() => {
1378
- const current = MutableRef.get(state.ref);
1379
- const facts = {
1380
- admissions: new Array(),
1381
- commands: commands(void 0),
1382
- deliveries: []
1383
- };
1384
- let next = after ?? "";
1385
- if (section === "admissions") {
1386
- const stored = threadRows(current, threadId, after === void 0 ? -1 : Number(after)).next().value;
1387
- if (stored !== void 0) {
1388
- facts.admissions.push(toAdmission(stored.row, stored.finalization?.finalizedAtMillis));
1389
- next = String(stored.row.queueSequence);
1390
- }
1391
- } else {
1392
- const entries = current.byThread.get(threadId)?.commands[section] ?? [];
1393
- const at = after === void 0 ? 0 : commandPosition(entries, after);
1394
- const choice = entries[at + Number(after !== void 0 && entries[at]?.key === after)];
1395
- if (choice !== void 0) {
1396
- next = choice.key;
1397
- const stored = current.submissions.get(choice.submissionId);
1398
- if (section === "aborts" && stored?.abortIntent !== void 0) facts.commands.aborts.push(cleanCommand(stored.abortIntent));
1399
- else if (section === "approvals" && choice.toolCallId !== void 0) {
1400
- const value = stored?.approvalDecisions.get(choice.toolCallId);
1401
- if (value !== void 0) facts.commands.approvals.push(cleanCommand(value));
1402
- } else if (section === "resolutions" && choice.resolutionKey !== void 0) {
1403
- const value = stored?.unknownResolutions.get(choice.resolutionKey);
1404
- if (value !== void 0) facts.commands.resolutions.push(cleanCommand(value.intent));
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
- return {
1409
- facts,
1410
- after: next
1411
- };
1412
- }),
1413
- submissions: (threadId) => Stream.paginate(-1, (after) => Effect.sync(() => {
1414
- const stored = threadRows(MutableRef.get(state.ref), threadId, after).next().value;
1415
- return [stored === void 0 ? [] : [toSnapshot(stored.row)], stored === void 0 ? Option.none() : Option.some(stored.row.queueSequence)];
1416
- })),
1417
- startImport: Effect.fnUntraced(function* (threadId) {
1418
- const current = yield* Ref.get(state);
1419
- if (current.lanes.has(threadId) || current.byThread.has(threadId) || current.stoppedWorkers.has(threadId)) return yield* ThreadImportRejected.make({
1420
- threadId,
1421
- reason: "target-not-empty",
1422
- message: "Destination ledger contains Thread data"
1423
- });
1424
- const staged = {
1425
- submissions: /* @__PURE__ */ new Map(),
1426
- admissionIndex: /* @__PURE__ */ new Map(),
1427
- lanes: /* @__PURE__ */ new Map(),
1428
- childReservations: /* @__PURE__ */ new Map(),
1429
- freshAdmissionSequence: current.freshAdmissionSequence,
1430
- stoppedWorkers: /* @__PURE__ */ new Map(),
1431
- latestByThread: /* @__PURE__ */ new Map(),
1432
- activeByThread: /* @__PURE__ */ new Map(),
1433
- byThread: /* @__PURE__ */ new Map(),
1434
- receipts: /* @__PURE__ */ new Map(),
1435
- joinsByHost: /* @__PURE__ */ new Map(),
1436
- childrenByParent: /* @__PURE__ */ new Map()
1437
- };
1438
- const commandBytes = /* @__PURE__ */ new Map();
1439
- const settledTimes = /* @__PURE__ */ new Map();
1440
- const retainCommandBytes = Effect.fnUntraced(function* (schema, value, id) {
1441
- const wire = yield* Schema.encodeEffect(Schema.toCodecJson(schema))(value).pipe(Effect.mapError(() => invalidThreadArchive("Invalid accepted command wire", threadId)));
1442
- const bytes = (commandBytes.get(id) ?? 0) + utf8ByteLength(canonicalJson(wire));
1443
- if (bytes > MAX_RUN_EVIDENCE_BYTES) return yield* invalidThreadArchive("Accepted commands exceed Run byte bounds", threadId);
1444
- commandBytes.set(id, bytes);
1445
- });
1446
- return {
1447
- reader: {
1448
- admission: (id) => Effect.sync(() => {
1449
- const stored = staged.submissions.get(id);
1450
- return stored === void 0 ? void 0 : toAdmission(stored.row, settledTimes.get(id));
1451
- }),
1452
- runOwner: (runId) => Effect.sync(() => {
1453
- if (!runId.startsWith("run:")) return void 0;
1454
- const id = Schema.decodeSync(SubmissionId)(runId.slice(4));
1455
- const stored = staged.submissions.get(id);
1456
- return stored === void 0 ? void 0 : toAdmission(stored.row, settledTimes.get(id));
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
- commands: (id) => readCommands(staged.submissions.get(id))
1459
- },
1460
- stage: Effect.fnUntraced(function* (page) {
1461
- for (const a of page.archive.admissions) {
1462
- const key = admissionKey(threadId, a.principal, a.idempotencyKey);
1463
- 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({
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: "destination-conflict",
1466
- message: "Admission, receipt, or immutable admission key already exists"
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
- prepareCommit: Effect.fnUntraced(function* (workerSeal) {
1515
- let lastQueue = 0;
1516
- let terminalEvidence = workerSeal?.terminal === void 0;
1517
- for (const stored of threadRows(staged, threadId)) {
1518
- const a = toAdmission(stored.row, settledTimes.get(stored.row.submissionId));
1519
- const rebuilt = yield* rebuildClosedSubmission(a);
1520
- if (rebuilt.settlement !== void 0 && workerTerminalFromRecord(toSnapshot(stored.row), rebuilt.settlement) === workerSeal?.terminal) terminalEvidence = true;
1521
- withSubmission(staged, fromRebuilt(rebuilt));
1522
- lastQueue = Math.max(lastQueue, a.queueSequence);
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;