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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/dist/{Admin-BHmp2V9p.d.mts → Admin-DTDihBra.d.mts} +50 -13
  2. package/dist/{AgentRuntime-BD3ZHAMq.d.mts → AgentRuntime-CHDyRbSf.d.mts} +3 -3
  3. package/dist/{AgentRuntime-B6qZlD3f.mjs → AgentRuntime-DaDKMasp.mjs} +123 -114
  4. package/dist/AgentRuntime-DaDKMasp.mjs.map +1 -0
  5. package/dist/{Digest-EpeqWEdv.mjs → Digest-C3EODOee.mjs} +2 -2
  6. package/dist/{Digest-EpeqWEdv.mjs.map → Digest-C3EODOee.mjs.map} +1 -1
  7. package/dist/{DurableAgentRuntime-CBBjRNJN.mjs → DurableAgentRuntime-BJbuIqQq.mjs} +237 -114
  8. package/dist/DurableAgentRuntime-BJbuIqQq.mjs.map +1 -0
  9. package/dist/{EventSource-C9aaJaXT.d.mts → EventSource-Cc58uEso.d.mts} +2 -2
  10. package/dist/InMemory.d.mts +2 -2
  11. package/dist/{MessageDelivery-BGDm-c46.d.mts → MessageDelivery-ClHg7c0O.d.mts} +42 -42
  12. package/dist/{MessagingHost-CRUK4JBi.mjs → MessagingHost-CZCyrnvJ.mjs} +27 -26
  13. package/dist/MessagingHost-CZCyrnvJ.mjs.map +1 -0
  14. package/dist/{PreparedInputAdmission-BvDqlgiP.d.mts → PreparedInputAdmission-CjcX1OP0.d.mts} +3 -3
  15. package/dist/{Records-BiuJDWNe.mjs → Records-DzL4Kbtw.mjs} +2 -2
  16. package/dist/{Records-BiuJDWNe.mjs.map → Records-DzL4Kbtw.mjs.map} +1 -1
  17. package/dist/{RunContinuation-4ZATcVLB.mjs → RunContinuation-CWHuciup.mjs} +74 -40
  18. package/dist/RunContinuation-CWHuciup.mjs.map +1 -0
  19. package/dist/{RunContinuation-CTJ1jGrO.d.mts → RunContinuation-DSnUTSSb.d.mts} +11 -5
  20. package/dist/{RunJournal-CNZqA3Ge.mjs → RunJournal-DGblbFas.mjs} +86 -81
  21. package/dist/RunJournal-DGblbFas.mjs.map +1 -0
  22. package/dist/{RunStorage-BHjuuERI.mjs → RunStorage-BF2YgWkg.mjs} +44 -33
  23. package/dist/RunStorage-BF2YgWkg.mjs.map +1 -0
  24. package/dist/{Scheduling-NG8slKdI.mjs → Scheduling-07_dsGED.mjs} +3 -3
  25. package/dist/{Scheduling-NG8slKdI.mjs.map → Scheduling-07_dsGED.mjs.map} +1 -1
  26. package/dist/{Subagent-CTIpnzlB.mjs → Subagent-CexndZSp.mjs} +2 -2
  27. package/dist/{Subagent-CTIpnzlB.mjs.map → Subagent-CexndZSp.mjs.map} +1 -1
  28. package/dist/{Subagent-DKhZA72u.d.mts → Subagent-xhN_aD1r.d.mts} +3 -3
  29. package/dist/{Subscription-BNg9VCRl.d.mts → Subscription-DuC_PcGx.d.mts} +2 -2
  30. package/dist/{SubscriptionInput-BUoXv-_K.d.mts → SubscriptionInput-B1IeAEDi.d.mts} +2 -2
  31. package/dist/{Thread-dk-QiNml.d.mts → Thread-DzdJAY4J.d.mts} +4 -3
  32. package/dist/{ThreadHistory-ByxS8HU1.d.mts → ThreadHistory-KPPBJLJb.d.mts} +12 -2
  33. package/dist/{ThreadImport-CMgzttvr.d.mts → ThreadImport-D_Biid6w.d.mts} +42 -42
  34. package/dist/{ThreadImport-CodYoJoa.mjs → ThreadImport-ZgWxf7bB.mjs} +8 -8
  35. package/dist/{ThreadImport-CodYoJoa.mjs.map → ThreadImport-ZgWxf7bB.mjs.map} +1 -1
  36. package/dist/{ThreadStore-59uDGXul.mjs → ThreadStore-BC4V-mY-.mjs} +42 -23
  37. package/dist/ThreadStore-BC4V-mY-.mjs.map +1 -0
  38. package/dist/{ThreadTransfer-3jigUl3g.mjs → ThreadTransfer-S7FnTRHt.mjs} +4 -4
  39. package/dist/{ThreadTransfer-3jigUl3g.mjs.map → ThreadTransfer-S7FnTRHt.mjs.map} +1 -1
  40. package/dist/{WorkerAdmission-DDYJdOsU.mjs → WorkerAdmission-C47Vy_cK.mjs} +5 -5
  41. package/dist/{WorkerAdmission-DDYJdOsU.mjs.map → WorkerAdmission-C47Vy_cK.mjs.map} +1 -1
  42. package/dist/{agent-registration-DRbyhAu-.mjs → agent-registration-a6QYaG3h.mjs} +4 -4
  43. package/dist/{agent-registration-DRbyhAu-.mjs.map → agent-registration-a6QYaG3h.mjs.map} +1 -1
  44. package/dist/capabilities/Messaging.mjs +1 -1
  45. package/dist/capabilities/ModelContext.d.mts +1 -1
  46. package/dist/capabilities/RunHooks.d.mts +1 -1
  47. package/dist/capabilities/Subagent.d.mts +1 -1
  48. package/dist/capabilities/Subagent.mjs +1 -1
  49. package/dist/core/Thread.d.mts +2 -2
  50. package/dist/core/Thread.mjs +5 -3
  51. package/dist/core/Thread.mjs.map +1 -1
  52. package/dist/durable/ActivityStore.mjs +1 -1
  53. package/dist/durable/Admin.d.mts +1 -1
  54. package/dist/durable/Admin.mjs +1 -1
  55. package/dist/durable/AgentRegistration.d.mts +1 -1
  56. package/dist/durable/AgentRegistration.mjs +1 -1
  57. package/dist/durable/Certification.d.mts +1 -1
  58. package/dist/durable/CommittedActivity.d.mts +1 -1
  59. package/dist/durable/CommittedActivity.mjs +3 -3
  60. package/dist/durable/Digest.mjs +1 -1
  61. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  62. package/dist/durable/DurableAgentRuntime.mjs +2 -2
  63. package/dist/durable/EventSource.d.mts +1 -1
  64. package/dist/durable/EventSource.mjs +1 -1
  65. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  66. package/dist/durable/LifecyclePublication.d.mts +1 -1
  67. package/dist/durable/LifecyclePublication.mjs +2 -2
  68. package/dist/durable/MessageDelivery.d.mts +1 -1
  69. package/dist/durable/MessageDelivery.mjs +2 -2
  70. package/dist/durable/MessageDeliveryStoreConformance.d.mts +40 -40
  71. package/dist/durable/MessageDeliveryStoreConformance.mjs +2 -2
  72. package/dist/durable/PersistentHistory.d.mts +2 -2
  73. package/dist/durable/PersistentHistory.mjs +4 -4
  74. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  75. package/dist/durable/RecordFormat.mjs +1 -1
  76. package/dist/durable/Records.mjs +1 -1
  77. package/dist/durable/Recovery.d.mts +1 -1
  78. package/dist/durable/Recovery.mjs +1 -1
  79. package/dist/durable/RunContinuation.d.mts +2 -2
  80. package/dist/durable/RunContinuation.mjs +3 -3
  81. package/dist/durable/RunJournal.d.mts +2 -2
  82. package/dist/durable/RunJournal.mjs +2 -2
  83. package/dist/durable/RunStorage.d.mts +1 -1
  84. package/dist/durable/RunStorage.mjs +1 -1
  85. package/dist/durable/Schedule.mjs +1 -1
  86. package/dist/durable/ScheduleStoreConformance.mjs +1 -1
  87. package/dist/durable/ScheduleTransition.mjs +1 -1
  88. package/dist/durable/Scheduling.d.mts +1 -1
  89. package/dist/durable/Scheduling.mjs +1 -1
  90. package/dist/durable/SettlementPublisher.d.mts +1 -1
  91. package/dist/durable/SettlementPublisher.mjs +3 -3
  92. package/dist/durable/SqlMemoryStore.mjs +5 -3
  93. package/dist/durable/SqlMemoryStore.mjs.map +1 -1
  94. package/dist/durable/SubmissionLedger.d.mts +1 -1
  95. package/dist/durable/SubmissionLedger.mjs +7 -3
  96. package/dist/durable/SubmissionLedger.mjs.map +1 -1
  97. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  98. package/dist/durable/SubmissionLedgerConformance.mjs +6 -6
  99. package/dist/durable/SubmissionStatus.d.mts +1 -1
  100. package/dist/durable/Subscription.d.mts +1 -1
  101. package/dist/durable/Subscription.mjs +2 -2
  102. package/dist/durable/SubscriptionInput.d.mts +1 -1
  103. package/dist/durable/SubscriptionInput.mjs +2 -2
  104. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  105. package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
  106. package/dist/durable/SubscriptionTools.d.mts +2 -2
  107. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  108. package/dist/durable/SubscriptionTransition.mjs +1 -1
  109. package/dist/durable/Subscriptions.d.mts +5 -5
  110. package/dist/durable/Subscriptions.mjs +2 -2
  111. package/dist/durable/ThreadArchiveRange.d.mts +1 -1
  112. package/dist/durable/ThreadArchiveRange.mjs +1 -1
  113. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  114. package/dist/durable/ThreadContextHistory.mjs +2 -2
  115. package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
  116. package/dist/durable/ThreadImport.d.mts +1 -1
  117. package/dist/durable/ThreadImport.mjs +1 -1
  118. package/dist/durable/ThreadInvariants.d.mts +3 -3
  119. package/dist/durable/ThreadInvariants.mjs +4 -4
  120. package/dist/durable/ThreadProjection.mjs +1 -1
  121. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  122. package/dist/durable/ThreadReader.d.mts +1 -1
  123. package/dist/durable/ThreadReader.mjs +1 -1
  124. package/dist/durable/ThreadStore.d.mts +1 -1
  125. package/dist/durable/ThreadStore.mjs +2 -2
  126. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  127. package/dist/durable/ThreadStoreConformance.mjs +4 -4
  128. package/dist/durable/ThreadTransfer.d.mts +1 -1
  129. package/dist/durable/ThreadTransfer.mjs +1 -1
  130. package/dist/durable/ThreadWork.d.mts +2 -2
  131. package/dist/durable/ThreadWork.mjs +12 -6
  132. package/dist/durable/ThreadWork.mjs.map +1 -1
  133. package/dist/durable/ToolReconciler.mjs +1 -1
  134. package/dist/durable/WakeScheduler.mjs +23 -39
  135. package/dist/durable/WakeScheduler.mjs.map +1 -1
  136. package/dist/durable/WorkerAdmission.d.mts +68 -68
  137. package/dist/durable/WorkerAdmission.mjs +1 -1
  138. package/dist/durable/WorkerHost.d.mts +1 -1
  139. package/dist/engine/AgentRuntime.d.mts +1 -1
  140. package/dist/engine/AgentRuntime.mjs +1 -1
  141. package/dist/engine/MessagingHost.mjs +1 -1
  142. package/dist/engine/Output.mjs +1 -1
  143. package/dist/engine/ThreadHistory.d.mts +1 -1
  144. package/dist/engine/ThreadHistory.mjs +3 -2
  145. package/dist/engine/ThreadHistory.mjs.map +1 -1
  146. package/dist/index.d.mts +12 -12
  147. package/dist/index.mjs +13 -13
  148. package/dist/{output-contract-CMIhu4Cw.mjs → output-contract-DCnLYCdp.mjs} +12 -15
  149. package/dist/{output-contract-CMIhu4Cw.mjs.map → output-contract-DCnLYCdp.mjs.map} +1 -1
  150. package/dist/{run-context-reader-CC7ZD95F.d.mts → run-context-reader-DvTEq2RC.d.mts} +2 -2
  151. package/package.json +1 -1
  152. package/src/core/Thread.ts +143 -146
  153. package/src/durable/DurableAgentRuntime.ts +549 -260
  154. package/src/durable/RunContinuation.ts +115 -64
  155. package/src/durable/RunJournal.ts +107 -112
  156. package/src/durable/RunStorage.ts +90 -61
  157. package/src/durable/SqlMemoryStore.ts +8 -6
  158. package/src/durable/SubmissionLedger.ts +15 -0
  159. package/src/durable/ThreadStore.ts +48 -26
  160. package/src/durable/ThreadWork.ts +10 -0
  161. package/src/durable/WakeScheduler.ts +34 -66
  162. package/src/durable/internal/initial-context.ts +17 -2
  163. package/src/durable/internal/prompt-history-cache.ts +13 -8
  164. package/src/durable/internal/usage-totals.ts +28 -31
  165. package/src/engine/MessagingHost.ts +4 -2
  166. package/src/engine/ThreadHistory.ts +47 -48
  167. package/src/engine/internal/agent-runtime.ts +234 -202
  168. package/src/engine/internal/output-contract.ts +15 -15
  169. package/src/engine/internal/primitive-delta.ts +2 -3
  170. package/src/engine/internal/tool-telemetry.ts +3 -5
  171. package/dist/AgentRuntime-B6qZlD3f.mjs.map +0 -1
  172. package/dist/DurableAgentRuntime-CBBjRNJN.mjs.map +0 -1
  173. package/dist/MessagingHost-CRUK4JBi.mjs.map +0 -1
  174. package/dist/RunContinuation-4ZATcVLB.mjs.map +0 -1
  175. package/dist/RunJournal-CNZqA3Ge.mjs.map +0 -1
  176. package/dist/RunStorage-BHjuuERI.mjs.map +0 -1
  177. package/dist/ThreadStore-59uDGXul.mjs.map +0 -1
@@ -63,6 +63,7 @@ import {
63
63
  getToolExecutionKind,
64
64
  SubagentParentLink,
65
65
  } from "../core/SubagentContract.ts";
66
+ import { Store as ConversationStore, makeMemoryStore } from "../core/Thread.ts";
66
67
  import { Selection, Snapshot } from "../core/ToolExposure.ts";
67
68
  import type { ToolParameterRejection } from "../core/ToolResult.ts";
68
69
  import {
@@ -157,7 +158,7 @@ import {
157
158
  } from "./internal/agent-registration.ts";
158
159
  import { makeAgentUpdateRuntime } from "./internal/agent-updates.ts";
159
160
  import { inspectForeignDiagnostic, safeUnknownString } from "./internal/foreign-diagnostic.ts";
160
- import { initialContext } from "./internal/initial-context.ts";
161
+ import { initialContext, type InitialContextInput } from "./internal/initial-context.ts";
161
162
  import {
162
163
  makeJournalMetadata,
163
164
  type JournalMetadata,
@@ -280,6 +281,7 @@ import {
280
281
  modelResponseInterruptedRecordId,
281
282
  modelResponseRecordId,
282
283
  projectRunJournalStream,
284
+ initialRunJournal,
283
285
  type JournalBoundary,
284
286
  type RunJournalProjection,
285
287
  type RunJournalContext,
@@ -401,6 +403,9 @@ import {
401
403
  getRunInput,
402
404
  ThreadReader,
403
405
  MAX_THREAD_EXPORT_PAGE_BYTES,
406
+ PreparedAppend,
407
+ type CommitBatch,
408
+ type ExecutionUpdate,
404
409
  } from "./ThreadStore.ts";
405
410
  import {
406
411
  type WorkIndexRebuildRequest,
@@ -592,6 +597,11 @@ export { threadCreatedBatchId, threadCreatedRecordId } from "./internal/thread-i
592
597
  export const recoveryRepairBatchId = (submissionId: SubmissionId, decisionTag: string): BatchId =>
593
598
  decodeBatchId(`repair:${submissionId}:${decisionTag}`);
594
599
 
600
+ const isOriginalInputRecord = (
601
+ record: RecordEnvelope,
602
+ ): record is RecordEnvelope & { readonly payload: UserInputRecorded } =>
603
+ record.payload._tag === "UserInputRecorded";
604
+
595
605
  /** Deterministic record identity of one executed recovery decision's `RepairAnnotated` record. */
596
606
  export const recoveryRepairRecordId = (submissionId: SubmissionId, decisionTag: string): RecordId =>
597
607
  decodeRecordId(`repair:${submissionId}:${decisionTag}`);
@@ -846,6 +856,11 @@ export class RecoverySweepResult extends Schema.Class<RecoverySweepResult>("Reco
846
856
  /** Recover one bounded page of native work, optionally confined to one Thread. */
847
857
  export interface RecoverySweepOptions {
848
858
  readonly threadId?: ThreadId;
859
+ /** Leave this incarnation's untouched root admissions to their first actual claim.
860
+ * A verified sole admission needs no recovery inventory; other owners retain bounded
861
+ * discovery and deferred reports. Neither case supplies ownership or a recovery decision:
862
+ * the Attempt still verifies current controls, journal, fencing and historyDigest. */
863
+ readonly deferUnclaimedAdmissions?: boolean;
849
864
  /** Continue the previous pass with the same Thread selection until its result has no cursor. */
850
865
  readonly cursor?: string;
851
866
  }
@@ -1357,7 +1372,7 @@ interface OpenCallReview {
1357
1372
 
1358
1373
  interface InitialJournalSource {
1359
1374
  records: ReadonlyArray<JournalRecordEnvelope> | undefined;
1360
- readonly original: CanonicalRecordEnvelope;
1375
+ readonly original: InitialContextInput;
1361
1376
  readonly suffix: ReadonlyArray<CanonicalRecordEnvelope>;
1362
1377
  cacheable: boolean;
1363
1378
  readonly prefix?: CachedPromptHistory;
@@ -1374,6 +1389,11 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1374
1389
  const reader = ThreadReader.fromStore(store);
1375
1390
  const promptHistory = yield* makePromptHistoryCache();
1376
1391
 
1392
+ // One incarnation remembers only newly accepted root input, never a recovery decision.
1393
+ // A claim consumes the hint before binding/application code can run. Cold owners,
1394
+ // replays, linked inputs, and any changed ledger state retain the recovery path.
1395
+ const unclaimedAdmissions = new Map<SubmissionId, ThreadId>();
1396
+
1377
1397
  const workDiscovery = yield* ThreadWorkDiscovery.pipe(
1378
1398
  Effect.provide(ThreadWorkDiscovery.layer),
1379
1399
  Effect.provideService(ThreadStore, store),
@@ -1444,17 +1464,27 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1444
1464
  // observer must never replace this host choice on a fresh or replacement Attempt.
1445
1465
  const toolFailureObserver = yield* CurrentToolFailureObserver;
1446
1466
 
1467
+ const runServices = Context.make(CurrentToolFailureObserver, toolFailureObserver).pipe(
1468
+ Context.add(RunToolVisibility, runToolVisibility),
1469
+ Context.add(RunToolScheduling, runToolScheduling),
1470
+ Context.add(ContextCompactor, compactor),
1471
+ Context.add(RunContextPreparation, runContextPreparation),
1472
+ );
1473
+
1474
+ const journalServices = Context.make(ThreadReader, reader).pipe(
1475
+ Context.add(Crypto.Crypto, crypto),
1476
+ );
1477
+
1447
1478
  const withCrypto = <A, E>(effect: Effect.Effect<A, E, Crypto.Crypto>): Effect.Effect<A, E> =>
1448
1479
  Effect.provideService(effect, Crypto.Crypto, crypto);
1449
1480
 
1450
1481
  // Rebuild the pinned admission and captured suffix using this runtime's host dependencies.
1451
1482
  const readInitialJournal = (
1452
- original: CanonicalRecordEnvelope,
1483
+ original: InitialContextInput,
1453
1484
  suffix: ReadonlyArray<CanonicalRecordEnvelope>,
1454
1485
  ) =>
1455
1486
  initialContext(original).pipe(
1456
- Effect.provideService(ThreadReader, reader),
1457
- Effect.provideService(Crypto.Crypto, crypto),
1487
+ Effect.provideContext(journalServices),
1458
1488
  Effect.map((history) => history.concat(suffix)),
1459
1489
  );
1460
1490
 
@@ -1708,10 +1738,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1708
1738
 
1709
1739
  if (found === undefined) {
1710
1740
  // An exact lookup distinguishes missing evidence from an incomplete native index.
1711
- yield* resolveEvidence(threadId, ref).pipe(
1712
- Effect.provideService(ThreadReader, reader),
1713
- Effect.provideService(Crypto.Crypto, crypto),
1714
- );
1741
+ yield* resolveEvidence(threadId, ref).pipe(Effect.provideContext(journalServices));
1715
1742
 
1716
1743
  return yield* invalid(
1717
1744
  "Required Run evidence is absent from its locator; rebuild the index explicitly",
@@ -1778,8 +1805,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1778
1805
  } else {
1779
1806
  saved = yield* readRunContext(initial, input, cursor.savedContext.recordId).pipe(
1780
1807
  Effect.provide(RunContextReader.layer(threadId)),
1781
- Effect.provideService(ThreadReader, reader),
1782
- Effect.provideService(Crypto.Crypto, crypto),
1808
+ Effect.provideContext(journalServices),
1783
1809
  Effect.mapError(() =>
1784
1810
  invalid("Saved original context cannot be resolved from its evidence"),
1785
1811
  ),
@@ -2573,8 +2599,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
2573
2599
  const progress =
2574
2600
  active?.writer ??
2575
2601
  (yield* makeProgressWriter(writer.threadId, config.deploymentId).pipe(
2576
- Effect.provideService(ThreadReader, reader),
2577
- Effect.provideService(Crypto.Crypto, crypto),
2602
+ Effect.provideContext(journalServices),
2578
2603
  ));
2579
2604
 
2580
2605
  const owned: RunWriter = {
@@ -2600,7 +2625,10 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
2600
2625
  Effect.map(({ owned }) => owned),
2601
2626
  );
2602
2627
 
2603
- const withProgressSession = Effect.fnUntraced(function* (session: RunStorageSession) {
2628
+ const withProgressSession = Effect.fnUntraced(function* (
2629
+ session: RunStorageSession,
2630
+ originalInput?: () => InitialContextInput | undefined,
2631
+ ) {
2604
2632
  const { owned, progress } = yield* withProgress(session);
2605
2633
 
2606
2634
  activeProgress.set(session.threadId, { writer: progress });
@@ -2611,9 +2639,44 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
2611
2639
  }),
2612
2640
  );
2613
2641
 
2642
+ const inputWriter =
2643
+ session.appendInput === undefined
2644
+ ? undefined
2645
+ : {
2646
+ ...session,
2647
+ append: session.appendInput,
2648
+ };
2649
+
2614
2650
  const wrapped: RunStorageSession = {
2615
2651
  ...session,
2616
2652
  append: owned.append,
2653
+ ...(inputWriter === undefined
2654
+ ? {}
2655
+ : {
2656
+ appendInput: (batch: CommitBatch) =>
2657
+ storageOwner.withProgress(
2658
+ session.threadId,
2659
+ storageOwner.withMutation(
2660
+ Effect.gen(function* () {
2661
+ yield* session.checkActive;
2662
+ // The captured empty canonical prefix is enforced again by the append tail and
2663
+ // checkpoint revision-zero CAS. It is not a cache entry across preparation IO.
2664
+ const original = originalInput?.();
2665
+ const commit = progress.commit(batch);
2666
+
2667
+ return yield* (
2668
+ original === undefined
2669
+ ? commit
2670
+ : progress.withVerifiedEmptyRun(
2671
+ runIdForSubmission(session.claim.submissionId),
2672
+ CanonicalSequence.make(original.sequence - 1),
2673
+ commit,
2674
+ )
2675
+ ).pipe(Effect.provideService(CurrentRunWriter, inputWriter));
2676
+ }),
2677
+ ),
2678
+ ),
2679
+ }),
2617
2680
  publishSettlement: (batch) =>
2618
2681
  progress.publish(batch).pipe(Effect.provideService(CurrentRunSettlement, session)),
2619
2682
  };
@@ -2633,8 +2696,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
2633
2696
  const progress =
2634
2697
  active?.writer ??
2635
2698
  (yield* makeProgressWriter(request.threadId, config.deploymentId).pipe(
2636
- Effect.provideService(ThreadReader, reader),
2637
- Effect.provideService(Crypto.Crypto, crypto),
2699
+ Effect.provideContext(journalServices),
2638
2700
  ));
2639
2701
 
2640
2702
  let tail = {
@@ -3151,6 +3213,20 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
3151
3213
  );
3152
3214
  });
3153
3215
 
3216
+ const prepareOriginalInput = (submission: SubmissionSnapshot) =>
3217
+ makeEnvelope(
3218
+ submissionInputRecordId(submission.submissionId),
3219
+ UserInputRecorded.make({
3220
+ submissionId: submission.submissionId,
3221
+ kind: "user",
3222
+ runId: runIdForSubmission(submission.submissionId),
3223
+ input: submission.inputPayload,
3224
+ ...(submission.messageAdmission === undefined
3225
+ ? {}
3226
+ : { messageAdmission: submission.messageAdmission }),
3227
+ }),
3228
+ );
3229
+
3154
3230
  /**
3155
3231
  * Plan step 2: append the deterministic `UserInputRecorded` record (batch idempotency makes it
3156
3232
  * exactly-once canonical, DUR-007) and mark it applied. When canonical history already carries
@@ -3176,18 +3252,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
3176
3252
  return;
3177
3253
  }
3178
3254
 
3179
- const envelope = yield* makeEnvelope(
3180
- recordId,
3181
- UserInputRecorded.make({
3182
- submissionId,
3183
- kind: "user",
3184
- runId: runIdForSubmission(submissionId),
3185
- input: submission.inputPayload,
3186
- ...(submission.messageAdmission === undefined
3187
- ? {}
3188
- : { messageAdmission: submission.messageAdmission }),
3189
- }),
3190
- );
3255
+ const envelope = yield* prepareOriginalInput(submission);
3191
3256
 
3192
3257
  const result = yield* appendBatch(
3193
3258
  ctx,
@@ -3238,8 +3303,13 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
3238
3303
  maxDurationMillis: number,
3239
3304
  records: ReadonlyArray<CanonicalRecordEnvelope>,
3240
3305
  operational: ReadonlyArray<RecordEnvelope>,
3306
+ input?: RecordEnvelope,
3241
3307
  ) {
3242
3308
  const writer = yield* CurrentRunWriter;
3309
+ const append = input === undefined ? writer.append : writer.appendInput;
3310
+
3311
+ if (append === undefined)
3312
+ return yield* RunJournalError.make({ message: "Fresh startup requires an input publisher" });
3243
3313
  const runId = runIdForSubmission(submission.submissionId);
3244
3314
  const recordId = runStartedRecordId(runId);
3245
3315
  const existing = yield* readRunStart(submission.threadId, runId, operational);
@@ -3288,13 +3358,21 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
3288
3358
  }
3289
3359
  yield* hit("run:before-start-append");
3290
3360
  yield* appendBatch(
3291
- writer,
3361
+ { threadId: writer.threadId, append },
3292
3362
  CanonicalBatch.make({
3293
- batchId: runStartedBatchId(runId),
3363
+ batchId:
3364
+ input === undefined
3365
+ ? runStartedBatchId(runId)
3366
+ : submissionInputBatchId(submission.submissionId),
3294
3367
  producerId: config.producerId,
3295
- records: [start, ...(context === undefined ? [] : [context])],
3368
+ records: [
3369
+ input ?? start,
3370
+ ...(input === undefined ? [] : [start]),
3371
+ ...(context === undefined ? [] : [context]),
3372
+ ],
3296
3373
  }),
3297
3374
  );
3375
+ if (input !== undefined) yield* hit("input:after-canonical-append");
3298
3376
  committed = true;
3299
3377
  yield* hit("run:after-start-append");
3300
3378
  });
@@ -4840,12 +4918,14 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
4840
4918
  // Unrelated Thread traffic does not invalidate an already validated owner projection.
4841
4919
  const projectionThrough = Math.max(progressThrough ?? 0, records.at(-1)?.sequence ?? 0);
4842
4920
 
4843
- const originalInput = records.find(
4844
- ({ record: { payload } }) =>
4845
- payload._tag === "UserInputRecorded" &&
4846
- payload.kind === "user" &&
4847
- payload.submissionId === submissionId,
4848
- );
4921
+ const originalInput =
4922
+ initialJournal?.original ??
4923
+ records.find(
4924
+ ({ record: { payload } }) =>
4925
+ payload._tag === "UserInputRecorded" &&
4926
+ payload.kind === "user" &&
4927
+ payload.submissionId === submissionId,
4928
+ );
4849
4929
 
4850
4930
  if (originalInput === undefined)
4851
4931
  return yield* RunJournalError.make({ message: "Run has no original context boundary" });
@@ -4856,23 +4936,35 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
4856
4936
  historyEvidence.set(entry.record.recordId, entry);
4857
4937
  };
4858
4938
 
4939
+ // An exact cache prefix followed only by this fresh input has no response,
4940
+ // policy reservation or context to replay. Keep recovery on the full projector.
4941
+ const freshPrefix =
4942
+ priorContext === undefined &&
4943
+ initialJournal?.prefix !== undefined &&
4944
+ initialJournal.records?.length === 1 &&
4945
+ initialJournal.records[0] === originalInput
4946
+ ? initialJournal.prefix.prompt
4947
+ : undefined;
4948
+
4859
4949
  let journal =
4860
- cached !== undefined &&
4861
- cached.threadId === ctx.threadId &&
4862
- cached.through === projectionThrough &&
4863
- cached.runId === runId &&
4864
- cached.contextRecordId === priorContext?.recordId
4865
- ? (boundaries.push(...cached.boundaries),
4866
- cached.contextEvidence.forEach(retainHistory),
4867
- cached.journal)
4868
- : yield* projectRunJournalStream(
4869
- canonical,
4870
- runId,
4871
- (boundary) => boundaries.push(boundary),
4872
- priorContext,
4873
- journalMetadata,
4874
- priorContext === undefined ? retainHistory : undefined,
4875
- );
4950
+ freshPrefix !== undefined
4951
+ ? initialRunJournal(freshPrefix)
4952
+ : cached !== undefined &&
4953
+ cached.threadId === ctx.threadId &&
4954
+ cached.through === projectionThrough &&
4955
+ cached.runId === runId &&
4956
+ cached.contextRecordId === priorContext?.recordId
4957
+ ? (boundaries.push(...cached.boundaries),
4958
+ cached.contextEvidence.forEach(retainHistory),
4959
+ cached.journal)
4960
+ : yield* projectRunJournalStream(
4961
+ canonical,
4962
+ runId,
4963
+ (boundary) => boundaries.push(boundary),
4964
+ priorContext,
4965
+ journalMetadata,
4966
+ priorContext === undefined ? retainHistory : undefined,
4967
+ );
4876
4968
 
4877
4969
  if (initialJournal?.prefix !== undefined) {
4878
4970
  const prompt = initialJournal.prefix.prompt;
@@ -5192,9 +5284,6 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5192
5284
  ),
5193
5285
  );
5194
5286
 
5195
- const addUsageTotal = (field: string, left: number, right: number) =>
5196
- checkedUsageTotal(field, left + right);
5197
-
5198
5287
  const subtractUsageTotal = (field: string, left: number, right: number) =>
5199
5288
  checkedUsageTotal(field, left - right);
5200
5289
 
@@ -5204,11 +5293,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5204
5293
  let unobservedModelCalls = journal.usage.unobservedModelCalls ?? 0;
5205
5294
 
5206
5295
  for (const count of stagedUnobservedCalls.values()) {
5207
- unobservedModelCalls = yield* addUsageTotal(
5208
- "unobservedModelCalls",
5209
- unobservedModelCalls,
5210
- count,
5211
- );
5296
+ unobservedModelCalls += count;
5212
5297
  }
5213
5298
  const detailedCalls = [...journal.usage.modelUsage, ...stagedCalls];
5214
5299
 
@@ -5229,17 +5314,15 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5229
5314
  let costMicrousd = journal.usage.costMicrousd;
5230
5315
 
5231
5316
  for (const usage of stagedUsage.values()) {
5232
- inputTokens = yield* addUsageTotal("inputTokens", inputTokens, usage.inputTokens);
5233
- outputTokens = yield* addUsageTotal("outputTokens", outputTokens, usage.outputTokens);
5234
- costMicrousd = yield* addUsageTotal("costMicrousd", costMicrousd, usage.costMicrousd);
5317
+ inputTokens += usage.inputTokens;
5318
+ outputTokens += usage.outputTokens;
5319
+ costMicrousd += usage.costMicrousd;
5235
5320
  }
5236
5321
 
5237
- const modelCalls = yield* addUsageTotal(
5238
- "modelCalls",
5239
- journal.usage.modelCalls,
5240
- stagedCalls.length,
5241
- );
5322
+ const modelCalls = journal.usage.modelCalls + stagedCalls.length;
5242
5323
 
5324
+ // Reconciliation must reject negative legacy deltas independently of
5325
+ // the final summary's complete safe-integer and additive checks.
5243
5326
  const legacyCalls = yield* subtractUsageTotal(
5244
5327
  "legacy.modelCalls",
5245
5328
  modelCalls,
@@ -5283,81 +5366,52 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5283
5366
 
5284
5367
  const existing = existingIndex < 0 ? undefined : byModel[existingIndex];
5285
5368
 
5286
- const legacyGroup = ModelUsageGroup.make({
5369
+ const legacyGroup = yield* ModelUsageGroup.makeEffect({
5287
5370
  provider: "unknown",
5288
5371
  model: "legacy-record",
5289
- modelCalls: yield* addUsageTotal(
5290
- "byModel.modelCalls",
5291
- existing?.modelCalls ?? 0,
5292
- legacyCalls,
5293
- ),
5294
- inputTokens: InputTokenUsage.make({
5295
- total: yield* addUsageTotal(
5296
- "byModel.inputTokens.total",
5297
- existing?.inputTokens.total ?? 0,
5298
- legacyInput,
5299
- ),
5300
- uncached: yield* addUsageTotal(
5301
- "byModel.inputTokens.uncached",
5302
- existing?.inputTokens.uncached ?? 0,
5303
- legacyInput,
5304
- ),
5372
+ modelCalls: (existing?.modelCalls ?? 0) + legacyCalls,
5373
+ inputTokens: {
5374
+ total: (existing?.inputTokens.total ?? 0) + legacyInput,
5375
+ uncached: (existing?.inputTokens.uncached ?? 0) + legacyInput,
5305
5376
  cacheRead: existing?.inputTokens.cacheRead ?? 0,
5306
5377
  cacheWrite: existing?.inputTokens.cacheWrite ?? 0,
5307
- }),
5308
- outputTokens: OutputTokenUsage.make({
5309
- total: yield* addUsageTotal(
5310
- "byModel.outputTokens.total",
5311
- existing?.outputTokens.total ?? 0,
5312
- legacyOutput,
5313
- ),
5314
- text: yield* addUsageTotal(
5315
- "byModel.outputTokens.text",
5316
- existing?.outputTokens.text ?? 0,
5317
- legacyOutput,
5318
- ),
5378
+ },
5379
+ outputTokens: {
5380
+ total: (existing?.outputTokens.total ?? 0) + legacyOutput,
5381
+ text: (existing?.outputTokens.text ?? 0) + legacyOutput,
5319
5382
  reasoning: existing?.outputTokens.reasoning ?? 0,
5320
- }),
5321
- costMicrousd: yield* addUsageTotal(
5322
- "byModel.costMicrousd",
5323
- existing?.costMicrousd ?? 0,
5324
- legacyCost,
5383
+ },
5384
+ costMicrousd: (existing?.costMicrousd ?? 0) + legacyCost,
5385
+ }).pipe(
5386
+ Effect.mapError((cause) =>
5387
+ RunJournalError.make({
5388
+ message: "Run legacy usage exceeds safe-integer accounting bounds",
5389
+ cause,
5390
+ }),
5325
5391
  ),
5326
- });
5392
+ );
5327
5393
 
5328
5394
  if (existingIndex < 0) byModel.push(legacyGroup);
5329
5395
  else byModel[existingIndex] = legacyGroup;
5330
5396
  }
5331
5397
 
5332
- const uncached = yield* addUsageTotal(
5333
- "inputTokens.uncached",
5334
- detailed.inputTokens.uncached,
5335
- legacyInput,
5336
- );
5337
-
5338
- const text = yield* addUsageTotal(
5339
- "outputTokens.text",
5340
- detailed.outputTokens.text,
5341
- legacyOutput,
5342
- );
5343
-
5344
- return RunUsageSummary.make({
5398
+ return yield* RunUsageSummary.makeEffect({
5345
5399
  ...(legacyCalls === 0 && detailed.webSearchCalls !== undefined
5346
5400
  ? { webSearchCalls: detailed.webSearchCalls }
5347
5401
  : {}),
5348
5402
  modelCalls,
5349
- inputTokens: InputTokenUsage.make({
5403
+ inputTokens: {
5350
5404
  total: inputTokens,
5351
5405
  // Legacy aggregate inputs are conservatively classified as uncached.
5352
- uncached,
5406
+ uncached: detailed.inputTokens.uncached + legacyInput,
5353
5407
  cacheRead: detailed.inputTokens.cacheRead,
5354
5408
  cacheWrite: detailed.inputTokens.cacheWrite,
5355
- }),
5356
- outputTokens: OutputTokenUsage.make({
5409
+ },
5410
+ outputTokens: {
5357
5411
  total: outputTokens,
5358
- text,
5412
+ text: detailed.outputTokens.text + legacyOutput,
5359
5413
  reasoning: detailed.outputTokens.reasoning,
5360
- }),
5414
+ },
5361
5415
  costMicrousd,
5362
5416
  byModel,
5363
5417
  unobservedModelCalls,
@@ -5373,7 +5427,14 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5373
5427
  ? "unknown"
5374
5428
  : "partial"
5375
5429
  : detailed.pricingStatus,
5376
- });
5430
+ }).pipe(
5431
+ Effect.mapError((cause) =>
5432
+ RunJournalError.make({
5433
+ message: "Run usage summary exceeds safe-integer accounting bounds",
5434
+ cause,
5435
+ }),
5436
+ ),
5437
+ );
5377
5438
  });
5378
5439
 
5379
5440
  const activeExecution = yield* reader.readExecution({ threadId: ctx.threadId, runId });
@@ -5450,18 +5511,6 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5450
5511
  usageSummary: yield* currentUsageSummary(),
5451
5512
  };
5452
5513
  }
5453
- // Summaries cover only prior Runs. Pruning and rollover also map fully settled
5454
- // current-Run records and preserves the canonical instruction/input block during replay.
5455
- let ownerFirstSequence: CanonicalSequence | undefined;
5456
-
5457
- for (const envelope of records) {
5458
- const payload = envelope.record.payload;
5459
-
5460
- if ("runId" in payload && payload.runId === runId) {
5461
- ownerFirstSequence = envelope.sequence;
5462
- break;
5463
- }
5464
- }
5465
5514
 
5466
5515
  /** Only canonical joined inputs can require restoration from an earlier Attempt. */
5467
5516
  let joinBacklog: ReadonlyArray<JoinSnapshot> | undefined =
@@ -5549,37 +5598,38 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5549
5598
  const recordHalt = <A, R>(
5550
5599
  effect: Effect.Effect<A, DurableWorkerFailure, R>,
5551
5600
  ): Effect.Effect<A, CoordinatorHalt | AgentPersistenceCapacityError, R> =>
5552
- effect.pipe(
5553
- Effect.tapError((failure) =>
5554
- failure instanceof ThreadStoreError &&
5555
- failure.cause instanceof AgentPersistenceCapacityError
5556
- ? Effect.void
5557
- : Ref.set(haltRef, failure),
5558
- ),
5559
- Effect.catchCause((cause) =>
5560
- Effect.failCause(
5561
- Cause.map(cause, (failure) =>
5562
- failure instanceof ThreadStoreError &&
5563
- failure.cause instanceof AgentPersistenceCapacityError
5564
- ? failure.cause
5565
- : new CoordinatorHalt(failure),
5566
- ),
5601
+ Effect.catchCause(effect, (cause) => {
5602
+ const first = Cause.findErrorOption(cause);
5603
+
5604
+ const failure = Effect.failCause(
5605
+ Cause.map(cause, (error) =>
5606
+ error instanceof ThreadStoreError &&
5607
+ error.cause instanceof AgentPersistenceCapacityError
5608
+ ? error.cause
5609
+ : new CoordinatorHalt(error),
5567
5610
  ),
5568
- ),
5569
- );
5611
+ );
5612
+
5613
+ return Option.isNone(first) ||
5614
+ (first.value instanceof ThreadStoreError &&
5615
+ first.value.cause instanceof AgentPersistenceCapacityError)
5616
+ ? failure
5617
+ : Effect.andThen(Ref.set(haltRef, first.value), failure);
5618
+ });
5570
5619
 
5571
5620
  // Infrastructure errors can be wrapped by broker/Tool APIs. The interpreter checks this
5572
5621
  // authority before semantic work; progress delivery is not a coordinator checkpoint.
5573
- const checkpoint = recordHalt(
5574
- Effect.gen(function* () {
5575
- const failure = yield* Ref.get(haltRef);
5622
+ const progress = progressWriters.get(ctx);
5576
5623
 
5577
- if (failure !== undefined) return yield* failure;
5578
- const progress = progressWriters.get(ctx);
5624
+ const checkProgress =
5625
+ progress === undefined
5626
+ ? Effect.void
5627
+ : progress.check(runId).pipe(Effect.provideService(CurrentRunWriter, ctx));
5579
5628
 
5580
- if (progress !== undefined)
5581
- yield* progress.check(runId).pipe(Effect.provideService(CurrentRunWriter, ctx));
5582
- }),
5629
+ const checkpoint = recordHalt(
5630
+ Effect.flatMap(Ref.get(haltRef), (failure) =>
5631
+ failure === undefined ? checkProgress : Effect.fail(failure),
5632
+ ),
5583
5633
  );
5584
5634
 
5585
5635
  // One readonly Turn may own its response without publishing it. Promotion and settlement
@@ -5841,6 +5891,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5841
5891
  const start = yield* CurrentRunStart;
5842
5892
 
5843
5893
  yield* start.commit(yield* makeEnvelope(recordId, payload));
5894
+ knownIds.add(originalInput.record.recordId);
5844
5895
  knownIds.add(runStartedRecordId(runId));
5845
5896
  knownIds.add(recordId);
5846
5897
  historyEvidence.clear();
@@ -6402,13 +6453,10 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
6402
6453
  );
6403
6454
  }
6404
6455
 
6405
- // Summaries use the initial prior-Run boundaries; pruning and rollover also admit
6406
- // complete current-Run boundaries from the fresh canonical source above.
6456
+ // The validated original input bounds summaries even when startup deferred its append.
6457
+ // Pruning and rollover also admit complete current-Run canonical boundaries.
6407
6458
  const coverable = sourceBoundaries.filter(
6408
- (boundary) =>
6409
- commit.kind !== "summarize" ||
6410
- ownerFirstSequence === undefined ||
6411
- boundary.sequence < ownerFirstSequence,
6459
+ (boundary) => commit.kind !== "summarize" || boundary.sequence < originalInput.sequence,
6412
6460
  );
6413
6461
 
6414
6462
  if (coverable.length === 0) {
@@ -7834,35 +7882,39 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
7834
7882
  stagedUnobservedCalls.set(turn, (stagedUnobservedCalls.get(turn) ?? 0) + 1);
7835
7883
  }),
7836
7884
  }),
7837
- Effect.provide(ThreadHistory.layer),
7838
- Effect.provideService(SubagentHost.forTool, (source) =>
7839
- source.threadId !== submission.threadId ||
7840
- source.agentId !== submission.agentId ||
7841
- source.runId !== runId
7842
- ? SubagentHost.unavailable
7843
- : workerRuntime.facet(
7844
- {
7845
- source,
7846
- policy: agent.definition.policy,
7847
- depth: delegationDepth,
7848
- ...(inheritedGrant === undefined ? {} : { grant: inheritedGrant }),
7849
- },
7850
- submission.principal,
7851
- submission.submissionId,
7852
- ),
7853
- ),
7854
- Effect.provideService(MessagingHost.forTool, (source) =>
7855
- source.threadId !== submission.threadId ||
7856
- source.agentId !== submission.agentId ||
7857
- source.runId !== runId
7858
- ? MessagingHost.unavailable
7859
- : messagingRuntime.forTool(source, submission.principal),
7885
+ (body) =>
7886
+ body.pipe(
7887
+ Effect.provideServiceEffect(ThreadHistory, ThreadHistory.makeFromStore),
7888
+ Effect.provideServiceEffect(ConversationStore, makeMemoryStore),
7889
+ Effect.scoped,
7890
+ ),
7891
+ Effect.provideContext(
7892
+ runServices.pipe(
7893
+ Context.add(SubagentHost.forTool, (source) =>
7894
+ source.threadId !== submission.threadId ||
7895
+ source.agentId !== submission.agentId ||
7896
+ source.runId !== runId
7897
+ ? SubagentHost.unavailable
7898
+ : workerRuntime.facet(
7899
+ {
7900
+ source,
7901
+ policy: agent.definition.policy,
7902
+ depth: delegationDepth,
7903
+ ...(inheritedGrant === undefined ? {} : { grant: inheritedGrant }),
7904
+ },
7905
+ submission.principal,
7906
+ submission.submissionId,
7907
+ ),
7908
+ ),
7909
+ Context.add(MessagingHost.forTool, (source) =>
7910
+ source.threadId !== submission.threadId ||
7911
+ source.agentId !== submission.agentId ||
7912
+ source.runId !== runId
7913
+ ? MessagingHost.unavailable
7914
+ : messagingRuntime.forTool(source, submission.principal),
7915
+ ),
7916
+ ),
7860
7917
  ),
7861
- Effect.provideService(CurrentToolFailureObserver, toolFailureObserver),
7862
- Effect.provideService(RunToolVisibility, runToolVisibility),
7863
- Effect.provideService(RunToolScheduling, runToolScheduling),
7864
- Effect.provideService(ContextCompactor, compactor),
7865
- Effect.provideService(RunContextPreparation, runContextPreparation),
7866
7918
  Effect.andThen(checkpoint),
7867
7919
  // A wrapped Tool/approval failure cannot outrank retained infrastructure authority.
7868
7920
  // Preserve independent defects, interruptions and already-marked coordinator failures.
@@ -7909,21 +7961,20 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
7909
7961
  }),
7910
7962
  );
7911
7963
 
7912
- const execution = Effect.raceFirst(consume, abortWatcher);
7913
-
7914
- const raced = (
7915
- yieldAfter === undefined && submissionScheduling.yieldTo === undefined
7916
- ? execution
7917
- : Effect.raceFirst(
7918
- execution,
7964
+ const raced = Effect.raceAllFirst([
7965
+ consume,
7966
+ abortWatcher,
7967
+ ...(yieldAfter === undefined && submissionScheduling.yieldTo === undefined
7968
+ ? []
7969
+ : [
7919
7970
  Deferred.await(yieldSignal).pipe(
7920
7971
  Effect.map((nextSubmissionId) => ({
7921
7972
  _tag: "yielded" as const,
7922
7973
  ...(nextSubmissionId === undefined ? {} : { nextSubmissionId }),
7923
7974
  })),
7924
7975
  ),
7925
- )
7926
- ).pipe(Effect.provideService(IdGenerator, idGenerator));
7976
+ ]),
7977
+ ]).pipe(Effect.provideService(IdGenerator, idGenerator));
7927
7978
 
7928
7979
  const result = yield* raced.pipe(
7929
7980
  Effect.catchCauseFilter(
@@ -8149,23 +8200,21 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8149
8200
  resumeAfterRetention: () => void,
8150
8201
  onHandoff: (nextSubmissionId: SubmissionId) => void,
8151
8202
  yieldAfter?: DateTime.Utc,
8203
+ freshlyAdmitted = false,
8152
8204
  ) {
8153
8205
  // Recheck ownership outside the disposable cache's failure recovery.
8154
8206
  yield* session.checkActive;
8155
8207
 
8156
8208
  const checkpointPrompt = yield* promptHistory
8157
8209
  .take({ threadId: session.threadId })
8158
- .pipe(
8159
- Effect.provideService(ThreadStore, store),
8160
- Effect.provideService(ThreadReader, reader),
8161
- Effect.provideService(Crypto.Crypto, crypto),
8162
- );
8210
+ .pipe(Effect.provideService(ThreadStore, store), Effect.provideContext(journalServices));
8163
8211
 
8164
8212
  // withProgressSession exposes only progress.commit/publish. Their prepare step owns and
8165
8213
  // freezes each canonical fact before reaching this raw writer. Snapshot the private array
8166
8214
  // before suspension; only acknowledged ranges enter this Attempt's disposable suffix.
8167
8215
  let committedPromptSuffix: CommittedPromptSuffix | undefined;
8168
8216
  const storageSession = session;
8217
+ let pendingOriginal: InitialContextInput | undefined;
8169
8218
 
8170
8219
  yield* Effect.addFinalizer(() =>
8171
8220
  Effect.sync(() => {
@@ -8173,8 +8222,30 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8173
8222
  committedPromptSuffix = undefined;
8174
8223
  }),
8175
8224
  );
8176
- session = yield* withProgressSession({
8225
+
8226
+ const promptSession: RunStorageSession = {
8177
8227
  ...storageSession,
8228
+ ...(storageSession.appendInput === undefined
8229
+ ? {}
8230
+ : {
8231
+ appendInput: (batch: CommitBatch, executions?: ReadonlyArray<ExecutionUpdate>) =>
8232
+ storageSession.appendInput!(batch, executions).pipe(
8233
+ Effect.tap((result) =>
8234
+ Effect.sync(() => {
8235
+ if (
8236
+ pendingOriginal !== undefined &&
8237
+ !result.replayed &&
8238
+ result.firstSequence === pendingOriginal.sequence
8239
+ )
8240
+ committedPromptSuffix = makeCommittedPromptSuffix({
8241
+ threadId: storageSession.threadId,
8242
+ sequence: result.firstSequence,
8243
+ record: batch.records[0]!,
8244
+ });
8245
+ }),
8246
+ ),
8247
+ ),
8248
+ }),
8178
8249
  append: (batch, executions) => {
8179
8250
  const suffix = committedPromptSuffix;
8180
8251
  const append = storageSession.append(batch, executions);
@@ -8208,17 +8279,153 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8208
8279
  Effect.tapCause(() => Effect.sync(() => suffix.discard())),
8209
8280
  );
8210
8281
  },
8211
- });
8282
+ };
8283
+
8284
+ session = yield* withProgressSession(promptSession, () => pendingOriginal);
8212
8285
  const { claim, threadId } = session;
8213
8286
  const submissionId = claim.submissionId;
8214
8287
 
8215
- const snapshot = yield* ledger.loadRecoverySnapshot(
8216
- RecoverySnapshotRequest.make({ submissionId }),
8217
- );
8288
+ // The hint only selects this preparation. A real claim, current controls, exact
8289
+ // evidence and checkpoint absence are verified under its owner before any reuse.
8290
+ // No binding, instructions, model, Tool or cross-Thread operation runs in the window.
8291
+ let prepared =
8292
+ freshlyAdmitted && session.withInitialization !== undefined
8293
+ ? yield* session.withInitialization(
8294
+ Effect.gen(function* () {
8295
+ const snapshot = yield* ledger.loadRecoverySnapshot(
8296
+ RecoverySnapshotRequest.make({ submissionId }),
8297
+ );
8298
+
8299
+ const submission = snapshot.submission;
8300
+
8301
+ if (
8302
+ submission.state !== "ready" ||
8303
+ submission.workerAdmission !== undefined ||
8304
+ submission.messageAdmission !== undefined ||
8305
+ submission.parentLinkage !== undefined ||
8306
+ snapshot.inputApplied !== undefined ||
8307
+ snapshot.abortIntent !== undefined ||
8308
+ snapshot.approvalDecisions.length !== 0
8309
+ )
8310
+ return undefined;
8311
+ yield* ensureThreadCreated(threadId, submission.agentId, submission.agentDigests);
8312
+ yield* session.refresh;
8313
+ const before = (yield* session.tail).sequence;
8314
+
8315
+ const view = yield* recoveryView(
8316
+ threadId,
8317
+ before,
8318
+ [submissionId],
8319
+ runIdForSubmission(submissionId),
8320
+ );
8321
+
8322
+ if (
8323
+ view.progress !== undefined ||
8324
+ view.context !== undefined ||
8325
+ view.operational.length !== 0 ||
8326
+ view.records.some(({ record }) => record.payload._tag !== "ThreadCreated")
8327
+ )
8328
+ return undefined;
8329
+ if (session.appendInput !== undefined) {
8330
+ const captured = yield* PreparedAppend.capture(
8331
+ AppendRequest.make({
8332
+ threadId,
8333
+ expectedTailSequence: before,
8334
+ batch: CanonicalBatch.make({
8335
+ batchId: submissionInputBatchId(submissionId),
8336
+ producerId: config.producerId,
8337
+ records: [yield* prepareOriginalInput(submission)],
8338
+ }),
8339
+ }),
8340
+ );
8341
+
8342
+ const record = captured.records[0]!.canonical;
8343
+
8344
+ if (!isOriginalInputRecord(record))
8345
+ return yield* RunJournalError.make({
8346
+ message: "Captured startup has no original input",
8347
+ });
8348
+ pendingOriginal = {
8349
+ threadId,
8350
+ sequence: CanonicalSequence.make(before + 1),
8351
+ record,
8352
+ };
8353
+
8354
+ const start = yield* makeRunStart(
8355
+ submission,
8356
+ Duration.toMillis(registeredAgent.definition.policy.maxDuration),
8357
+ view.records,
8358
+ view.operational,
8359
+ record,
8360
+ ).pipe(Effect.provideService(CurrentRunWriter, session));
8361
+
8362
+ return { snapshot, through: before, view, start, input: pendingOriginal };
8363
+ }
8364
+ const progress = progressWriters.get(session);
8365
+
8366
+ const applyInput = applyCanonicalInput(
8367
+ session,
8368
+ submission,
8369
+ session,
8370
+ view.records,
8371
+ undefined,
8372
+ );
8373
+
8374
+ yield* progress === undefined
8375
+ ? applyInput
8376
+ : progress
8377
+ .withVerifiedEmptyRun(runIdForSubmission(submissionId), before, applyInput)
8378
+ .pipe(Effect.provideService(CurrentRunWriter, promptSession));
8379
+ const through = (yield* session.tail).sequence;
8380
+ const added = yield* selectedRange(threadId, [submissionId], through, before);
8381
+ const records = [...view.records, ...added];
8382
+
8383
+ const accepted = progressWriters
8384
+ .get(session)
8385
+ ?.acceptedCheckpoint(runIdForSubmission(submissionId));
8386
+
8387
+ if (
8388
+ accepted === undefined ||
8389
+ accepted.throughSequence !== through ||
8390
+ accepted.progress.runId !== runIdForSubmission(submissionId) ||
8391
+ accepted.progress.submissionId !== submissionId
8392
+ )
8393
+ return undefined;
8394
+
8395
+ const start = yield* makeRunStart(
8396
+ submission,
8397
+ Duration.toMillis(registeredAgent.definition.policy.maxDuration),
8398
+ records,
8399
+ view.operational,
8400
+ ).pipe(Effect.provideService(CurrentRunWriter, session));
8401
+
8402
+ return {
8403
+ snapshot,
8404
+ through,
8405
+ view: {
8406
+ ...view,
8407
+ records,
8408
+ progress: accepted.progress,
8409
+ progressThrough: accepted.throughSequence,
8410
+ operational: accepted.operational,
8411
+ },
8412
+ start,
8413
+ input: undefined,
8414
+ };
8415
+ }),
8416
+ )
8417
+ : undefined;
8418
+
8419
+ const preparedInput = prepared !== undefined;
8420
+
8421
+ const snapshot =
8422
+ prepared?.snapshot ??
8423
+ (yield* ledger.loadRecoverySnapshot(RecoverySnapshotRequest.make({ submissionId })));
8218
8424
 
8219
8425
  const submission = snapshot.submission;
8220
8426
 
8221
- yield* ensureThreadCreated(threadId, submission.agentId, submission.agentDigests);
8427
+ if (prepared === undefined)
8428
+ yield* ensureThreadCreated(threadId, submission.agentId, submission.agentDigests);
8222
8429
  if (submission.workerAdmission?.origin.reporting?.mode === "standard")
8223
8430
  yield* updateRuntime.repairRun(threadId, submissionId);
8224
8431
  if (submission.workerAdmission !== undefined) {
@@ -8233,18 +8440,20 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8233
8440
  if (submission.state === "admitted") {
8234
8441
  yield* ledger.markReady(MarkReadyRequest.make({ submissionId }));
8235
8442
  }
8236
- yield* session.refresh;
8443
+ if (prepared === undefined) yield* session.refresh;
8237
8444
  const ctx: AttemptAppendContext = session;
8238
- let controlThrough = (yield* session.tail).sequence;
8445
+ let controlThrough = prepared?.through ?? (yield* session.tail).sequence;
8239
8446
 
8240
8447
  const initialThrough = controlThrough;
8241
8448
 
8242
- const initialView = yield* recoveryView(
8243
- threadId,
8244
- initialThrough,
8245
- [submissionId],
8246
- runIdForSubmission(submissionId),
8247
- );
8449
+ const initialView =
8450
+ prepared?.view ??
8451
+ (yield* recoveryView(
8452
+ threadId,
8453
+ initialThrough,
8454
+ [submissionId],
8455
+ runIdForSubmission(submissionId),
8456
+ ));
8248
8457
 
8249
8458
  let journalMetadata: ReturnType<typeof makeJournalMetadata> | undefined;
8250
8459
 
@@ -8367,15 +8576,17 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8367
8576
  ),
8368
8577
  );
8369
8578
  }
8370
- yield* applyCanonicalInput(ctx, submission, session, records, snapshot.inputApplied);
8579
+ if (prepared === undefined)
8580
+ yield* applyCanonicalInput(ctx, submission, session, records, snapshot.inputApplied);
8371
8581
 
8372
- const start = yield* makeRunStart(
8373
- submission,
8374
- Duration.toMillis(agent.definition.policy.maxDuration),
8375
- yield* refreshControl(),
8376
- // Applying the input cannot change the Run start captured by this Attempt's owner.
8377
- initialView.operational,
8378
- ).pipe(Effect.provideService(CurrentRunWriter, ctx));
8582
+ const start =
8583
+ prepared?.start ??
8584
+ (yield* makeRunStart(
8585
+ submission,
8586
+ Duration.toMillis(agent.definition.policy.maxDuration),
8587
+ yield* refreshControl(),
8588
+ initialView.operational,
8589
+ ).pipe(Effect.provideService(CurrentRunWriter, ctx)));
8379
8590
 
8380
8591
  const savedRunTiming = start.timing;
8381
8592
 
@@ -8397,8 +8608,11 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8397
8608
  };
8398
8609
 
8399
8610
  let expiredChildObligation = false;
8611
+ let preparedControl = prepared !== undefined;
8400
8612
 
8401
8613
  if ((yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(runTiming.deadline)) {
8614
+ prepared = undefined;
8615
+ preparedControl = false;
8402
8616
  yield* commitStart;
8403
8617
  yield* reconcileRetainedChildren(ctx, submission, session);
8404
8618
  expiredChildObligation = yield* completeJoinedReleases(submission);
@@ -8406,7 +8620,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8406
8620
 
8407
8621
  // Recheck after duration interruption too: that Attempt may have declared new ordinary calls.
8408
8622
  const ordinaryCallsResolved = Effect.gen(function* () {
8409
- const reconciliationRecords = yield* refreshControl();
8623
+ const reconciliationRecords = preparedControl ? records : yield* refreshControl();
8624
+
8625
+ preparedControl = false;
8410
8626
 
8411
8627
  const reconciliationEvidence = yield* evidenceFor(
8412
8628
  reconciliationRecords,
@@ -8456,7 +8672,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8456
8672
  if (!expiredChildObligation && !(yield* ordinaryCallsResolved))
8457
8673
  return Option.none<Settlement>();
8458
8674
 
8459
- const continuationRecords = yield* refreshControl();
8675
+ const continuationRecords = prepared === undefined ? yield* refreshControl() : records;
8460
8676
 
8461
8677
  const continuation = yield* pendingToolBatchFor(
8462
8678
  continuationRecords,
@@ -8596,24 +8812,30 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8596
8812
 
8597
8813
  const lineage: AttemptLineage = {
8598
8814
  attemptId: claim.attemptId,
8599
- inputWasRecorded: evidence.inputRecorded,
8815
+ inputWasRecorded: !preparedInput && evidence.inputRecorded,
8600
8816
  };
8601
8817
 
8602
8818
  let approvalDecisionIntents = snapshot.approvalDecisions;
8603
8819
 
8604
8820
  while (true) {
8605
- yield* session.refresh;
8821
+ const initialization = prepared;
8822
+
8823
+ prepared = undefined;
8824
+ if (initialization === undefined) yield* session.refresh;
8606
8825
  const tail = yield* session.tail;
8607
8826
 
8608
- const currentRecords = yield* refreshControl(tail.sequence);
8827
+ const currentRecords =
8828
+ initialization === undefined ? yield* refreshControl(tail.sequence) : records;
8609
8829
 
8610
- const view = yield* recoveryView(
8611
- threadId,
8612
- tail.sequence,
8613
- [submissionId],
8614
- runIdForSubmission(submissionId),
8615
- currentRecords,
8616
- );
8830
+ const view =
8831
+ initialization?.view ??
8832
+ (yield* recoveryView(
8833
+ threadId,
8834
+ tail.sequence,
8835
+ [submissionId],
8836
+ runIdForSubmission(submissionId),
8837
+ currentRecords,
8838
+ ));
8617
8839
 
8618
8840
  let canonical: Stream.Stream<
8619
8841
  JournalRecordEnvelope,
@@ -8623,12 +8845,14 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8623
8845
  let initialJournal: InitialJournalSource | undefined;
8624
8846
 
8625
8847
  if (view.context === undefined) {
8626
- const original = currentRecords.find(
8627
- ({ record }) =>
8628
- record.payload._tag === "UserInputRecorded" &&
8629
- record.payload.kind === "user" &&
8630
- record.payload.submissionId === submissionId,
8631
- );
8848
+ const original =
8849
+ initialization?.input ??
8850
+ currentRecords.find(
8851
+ ({ record }) =>
8852
+ record.payload._tag === "UserInputRecorded" &&
8853
+ record.payload.kind === "user" &&
8854
+ record.payload.submissionId === submissionId,
8855
+ );
8632
8856
 
8633
8857
  if (original === undefined)
8634
8858
  return yield* RunJournalError.make({
@@ -8638,7 +8862,10 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8638
8862
  const suffix = currentRecords.filter((entry) => entry.sequence > original.sequence);
8639
8863
 
8640
8864
  const fresh =
8641
- !lineage.inputWasRecorded && tail.sequence === original.sequence && suffix.length === 0;
8865
+ initialization?.input !== undefined ||
8866
+ (!lineage.inputWasRecorded &&
8867
+ tail.sequence === original.sequence &&
8868
+ suffix.length === 0);
8642
8869
 
8643
8870
  // Every replay stays bounded at the ORIGINAL admission, never the later Thread tail.
8644
8871
  const source: InitialJournalSource = yield* Effect.gen(function* () {
@@ -8681,9 +8908,10 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8681
8908
  }
8682
8909
 
8683
8910
  committedPromptSuffix?.discard();
8684
- committedPromptSuffix = initialJournal?.cacheable
8685
- ? makeCommittedPromptSuffix(initialJournal.original)
8686
- : undefined;
8911
+ committedPromptSuffix =
8912
+ initialJournal?.cacheable && initialization?.input === undefined
8913
+ ? makeCommittedPromptSuffix(initialJournal.original)
8914
+ : undefined;
8687
8915
 
8688
8916
  const outcome = yield* runModel(
8689
8917
  agent,
@@ -8693,7 +8921,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8693
8921
  currentRecords,
8694
8922
  canonical,
8695
8923
  initialJournal,
8696
- tail.sequence,
8924
+ initialization?.input?.sequence ?? tail.sequence,
8697
8925
  view.progressThrough,
8698
8926
  view.progress,
8699
8927
  view.context,
@@ -8933,6 +9161,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8933
9161
  const session = claimed.value;
8934
9162
  const { claim } = session;
8935
9163
 
9164
+ const freshlyAdmitted = unclaimedAdmissions.get(claim.submissionId) === threadId;
9165
+
9166
+ unclaimedAdmissions.delete(claim.submissionId);
8936
9167
  firstClaimedSubmissionId ??= claim.submissionId;
8937
9168
 
8938
9169
  const attributes = {
@@ -9017,6 +9248,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
9017
9248
  });
9018
9249
  },
9019
9250
  options?.yieldAfter,
9251
+ freshlyAdmitted,
9020
9252
  ),
9021
9253
  threadId,
9022
9254
  claim,
@@ -10395,6 +10627,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
10395
10627
  const recoverOwnedWork = Effect.fnUntraced(function* (
10396
10628
  request: WorkRecoveryRequest,
10397
10629
  recoveredAdmissions = new Map<SubmissionId, RecoveryReport>(),
10630
+ deferUnclaimedAdmissions = false,
10398
10631
  ): Effect.fn.Return<WorkRecoveryReport, DurableWorkerFailure> {
10399
10632
  const { threadId, work: entry } = yield* Schema.decodeEffect(WorkRecoveryRequest)(request).pipe(
10400
10633
  Effect.mapError((cause) =>
@@ -10448,6 +10681,21 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
10448
10681
  const original = yield* lookupKnownSubmission("recover work", owner.submissionId);
10449
10682
 
10450
10683
  if (original.threadId !== threadId) return yield* invalid();
10684
+ if (
10685
+ deferUnclaimedAdmissions &&
10686
+ unclaimedAdmissions.get(owner.submissionId) === threadId &&
10687
+ original.state === "ready" &&
10688
+ !storageOwner.hasOwner(threadId) &&
10689
+ (yield* ledger.readAbortIntent(
10690
+ AbortIntentRequest.make({ submissionId: owner.submissionId }),
10691
+ )) === undefined
10692
+ ) {
10693
+ // The worker's first scoped claim performs the authoritative control/journal
10694
+ // read. This hint skips only a redundant pre-claim recovery traversal; it grants
10695
+ // no claim, synthesizes no decision, and supplies no model-visible facts.
10696
+ return report("deferred");
10697
+ }
10698
+ unclaimedAdmissions.delete(owner.submissionId);
10451
10699
  const submission = yield* recoverAdmission(owner.submissionId);
10452
10700
 
10453
10701
  return report(submission.disposition, submission);
@@ -10926,6 +11174,29 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
10926
11174
 
10927
11175
  const outcome = yield* isolateRecovery(
10928
11176
  Effect.gen(function* () {
11177
+ // This incarnation admitted the sole untouched root. The native snapshot proves
11178
+ // there is no old owner to repair; claim and live controls still run normally.
11179
+ // New source mutations retain their dirty generation for the next maintenance scan.
11180
+ if (
11181
+ workCursor === undefined &&
11182
+ options?.deferUnclaimedAdmissions === true &&
11183
+ workDiscovery.isOnlyFreshAdmission !== undefined &&
11184
+ !storageOwner.hasOwner(threadId)
11185
+ ) {
11186
+ const fresh = [...unclaimedAdmissions].find(([, owner]) => owner === threadId);
11187
+
11188
+ if (
11189
+ fresh !== undefined &&
11190
+ (yield* workDiscovery.isOnlyFreshAdmission({
11191
+ threadId,
11192
+ submissionId: fresh[0],
11193
+ }))
11194
+ ) {
11195
+ complete = true;
11196
+
11197
+ return;
11198
+ }
11199
+ }
10929
11200
  for (
10930
11201
  let pages = 0;
10931
11202
  pages < MAX_RECOVERY_PAGES && workReports.length < MAX_RECOVERY_WORK_ITEMS;
@@ -10941,7 +11212,11 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
10941
11212
 
10942
11213
  phase = "recovery";
10943
11214
  for (const work of workPage.entries) {
10944
- const repaired = yield* recoverOwnedWork({ threadId, work }, recoveredAdmissions);
11215
+ const repaired = yield* recoverOwnedWork(
11216
+ { threadId, work },
11217
+ recoveredAdmissions,
11218
+ options?.deferUnclaimedAdmissions === true,
11219
+ );
10945
11220
 
10946
11221
  workReports.push(repaired);
10947
11222
  if (
@@ -11165,7 +11440,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
11165
11440
  queueSequence: admitted.queueSequence,
11166
11441
  });
11167
11442
  }
11168
- const admitted = yield* ledger.admit(request);
11443
+ const admitted = yield* ledger.admit(request, { readyIfInitialized: true });
11169
11444
 
11170
11445
  yield* hit("submit:after-admit");
11171
11446
 
@@ -11181,10 +11456,24 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
11181
11456
  if (admitted.replayed && admitted.state !== "admitted") {
11182
11457
  return receipt;
11183
11458
  }
11184
- yield* materializeThread(options.threadId);
11185
- yield* ensureThreadCreated(options.threadId, agent.definition.id, options.definitions);
11186
- yield* hit("submit:after-materialize");
11187
- yield* ledger.markReady(MarkReadyRequest.make({ submissionId: admitted.submissionId }));
11459
+ if (admitted.state === "admitted") {
11460
+ yield* materializeThread(options.threadId);
11461
+ yield* ensureThreadCreated(options.threadId, agent.definition.id, options.definitions);
11462
+ yield* hit("submit:after-materialize");
11463
+ yield* ledger.markReady(MarkReadyRequest.make({ submissionId: admitted.submissionId }));
11464
+ }
11465
+ if (
11466
+ !admitted.replayed &&
11467
+ messageAdmission === undefined &&
11468
+ options.workerAdmission === undefined
11469
+ ) {
11470
+ unclaimedAdmissions.set(admitted.submissionId, options.threadId);
11471
+ if (unclaimedAdmissions.size > 128) {
11472
+ const oldest = unclaimedAdmissions.keys().next().value;
11473
+
11474
+ if (oldest !== undefined) unclaimedAdmissions.delete(oldest);
11475
+ }
11476
+ }
11188
11477
  yield* wake.notify(options.threadId);
11189
11478
 
11190
11479
  return receipt;