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

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 (175) hide show
  1. package/dist/{Admin-BHmp2V9p.d.mts → Admin-Qa3I1oA5.d.mts} +38 -7
  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-CPlw5_7e.mjs} +161 -85
  8. package/dist/DurableAgentRuntime-CPlw5_7e.mjs.map +1 -0
  9. package/dist/{EventSource-C9aaJaXT.d.mts → EventSource-BTu68yDW.d.mts} +2 -2
  10. package/dist/InMemory.d.mts +2 -2
  11. package/dist/{MessageDelivery-BGDm-c46.d.mts → MessageDelivery-DjAZJ4Fd.d.mts} +4 -4
  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-wgY4JMZY.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-CTJ1jGrO.d.mts → RunContinuation-3jB5pULC.d.mts} +10 -4
  18. package/dist/{RunContinuation-4ZATcVLB.mjs → RunContinuation-BiyKJK32.mjs} +74 -40
  19. package/dist/RunContinuation-BiyKJK32.mjs.map +1 -0
  20. package/dist/{RunJournal-CNZqA3Ge.mjs → RunJournal-CPMKcfd0.mjs} +84 -79
  21. package/dist/RunJournal-CPMKcfd0.mjs.map +1 -0
  22. package/dist/{RunStorage-BHjuuERI.mjs → RunStorage-CzutHJBE.mjs} +3 -2
  23. package/dist/RunStorage-CzutHJBE.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-DmIHlOJK.d.mts} +2 -2
  30. package/dist/{SubscriptionInput-BUoXv-_K.d.mts → SubscriptionInput-DgcQhvbe.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-CodYoJoa.mjs → ThreadImport-CjlG57vH.mjs} +8 -8
  34. package/dist/{ThreadImport-CodYoJoa.mjs.map → ThreadImport-CjlG57vH.mjs.map} +1 -1
  35. package/dist/{ThreadImport-CMgzttvr.d.mts → ThreadImport-DP-vOk2F.d.mts} +4 -4
  36. package/dist/{ThreadStore-59uDGXul.mjs → ThreadStore-EuUilthA.mjs} +5 -7
  37. package/dist/ThreadStore-EuUilthA.mjs.map +1 -0
  38. package/dist/{ThreadTransfer-3jigUl3g.mjs → ThreadTransfer-DB_zsrtJ.mjs} +4 -4
  39. package/dist/{ThreadTransfer-3jigUl3g.mjs.map → ThreadTransfer-DB_zsrtJ.mjs.map} +1 -1
  40. package/dist/{WorkerAdmission-DDYJdOsU.mjs → WorkerAdmission-eBeXt_mu.mjs} +5 -5
  41. package/dist/{WorkerAdmission-DDYJdOsU.mjs.map → WorkerAdmission-eBeXt_mu.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 +1 -1
  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-DVhJZ1Qe.d.mts} +2 -2
  151. package/package.json +1 -1
  152. package/src/core/Thread.ts +143 -146
  153. package/src/durable/DurableAgentRuntime.ts +381 -206
  154. package/src/durable/RunContinuation.ts +113 -62
  155. package/src/durable/RunJournal.ts +99 -108
  156. package/src/durable/RunStorage.ts +7 -0
  157. package/src/durable/SqlMemoryStore.ts +8 -6
  158. package/src/durable/SubmissionLedger.ts +7 -0
  159. package/src/durable/ThreadStore.ts +9 -7
  160. package/src/durable/ThreadWork.ts +10 -0
  161. package/src/durable/WakeScheduler.ts +34 -66
  162. package/src/durable/internal/usage-totals.ts +28 -31
  163. package/src/engine/MessagingHost.ts +4 -2
  164. package/src/engine/ThreadHistory.ts +47 -48
  165. package/src/engine/internal/agent-runtime.ts +234 -202
  166. package/src/engine/internal/output-contract.ts +15 -15
  167. package/src/engine/internal/primitive-delta.ts +2 -3
  168. package/src/engine/internal/tool-telemetry.ts +3 -5
  169. package/dist/AgentRuntime-B6qZlD3f.mjs.map +0 -1
  170. package/dist/DurableAgentRuntime-CBBjRNJN.mjs.map +0 -1
  171. package/dist/MessagingHost-CRUK4JBi.mjs.map +0 -1
  172. package/dist/RunContinuation-4ZATcVLB.mjs.map +0 -1
  173. package/dist/RunJournal-CNZqA3Ge.mjs.map +0 -1
  174. package/dist/RunStorage-BHjuuERI.mjs.map +0 -1
  175. 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 {
@@ -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,
@@ -846,6 +848,11 @@ export class RecoverySweepResult extends Schema.Class<RecoverySweepResult>("Reco
846
848
  /** Recover one bounded page of native work, optionally confined to one Thread. */
847
849
  export interface RecoverySweepOptions {
848
850
  readonly threadId?: ThreadId;
851
+ /** Leave this incarnation's untouched root admissions to their first actual claim.
852
+ * A verified sole admission needs no recovery inventory; other owners retain bounded
853
+ * discovery and deferred reports. Neither case supplies ownership or a recovery decision:
854
+ * the Attempt still verifies current controls, journal, fencing and historyDigest. */
855
+ readonly deferUnclaimedAdmissions?: boolean;
849
856
  /** Continue the previous pass with the same Thread selection until its result has no cursor. */
850
857
  readonly cursor?: string;
851
858
  }
@@ -1374,6 +1381,11 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1374
1381
  const reader = ThreadReader.fromStore(store);
1375
1382
  const promptHistory = yield* makePromptHistoryCache();
1376
1383
 
1384
+ // One incarnation remembers only newly accepted root input, never a recovery decision.
1385
+ // A claim consumes the hint before binding/application code can run. Cold owners,
1386
+ // replays, linked inputs, and any changed ledger state retain the recovery path.
1387
+ const unclaimedAdmissions = new Map<SubmissionId, ThreadId>();
1388
+
1377
1389
  const workDiscovery = yield* ThreadWorkDiscovery.pipe(
1378
1390
  Effect.provide(ThreadWorkDiscovery.layer),
1379
1391
  Effect.provideService(ThreadStore, store),
@@ -1444,6 +1456,17 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1444
1456
  // observer must never replace this host choice on a fresh or replacement Attempt.
1445
1457
  const toolFailureObserver = yield* CurrentToolFailureObserver;
1446
1458
 
1459
+ const runServices = Context.make(CurrentToolFailureObserver, toolFailureObserver).pipe(
1460
+ Context.add(RunToolVisibility, runToolVisibility),
1461
+ Context.add(RunToolScheduling, runToolScheduling),
1462
+ Context.add(ContextCompactor, compactor),
1463
+ Context.add(RunContextPreparation, runContextPreparation),
1464
+ );
1465
+
1466
+ const journalServices = Context.make(ThreadReader, reader).pipe(
1467
+ Context.add(Crypto.Crypto, crypto),
1468
+ );
1469
+
1447
1470
  const withCrypto = <A, E>(effect: Effect.Effect<A, E, Crypto.Crypto>): Effect.Effect<A, E> =>
1448
1471
  Effect.provideService(effect, Crypto.Crypto, crypto);
1449
1472
 
@@ -1453,8 +1476,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1453
1476
  suffix: ReadonlyArray<CanonicalRecordEnvelope>,
1454
1477
  ) =>
1455
1478
  initialContext(original).pipe(
1456
- Effect.provideService(ThreadReader, reader),
1457
- Effect.provideService(Crypto.Crypto, crypto),
1479
+ Effect.provideContext(journalServices),
1458
1480
  Effect.map((history) => history.concat(suffix)),
1459
1481
  );
1460
1482
 
@@ -1708,10 +1730,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1708
1730
 
1709
1731
  if (found === undefined) {
1710
1732
  // 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
- );
1733
+ yield* resolveEvidence(threadId, ref).pipe(Effect.provideContext(journalServices));
1715
1734
 
1716
1735
  return yield* invalid(
1717
1736
  "Required Run evidence is absent from its locator; rebuild the index explicitly",
@@ -1778,8 +1797,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1778
1797
  } else {
1779
1798
  saved = yield* readRunContext(initial, input, cursor.savedContext.recordId).pipe(
1780
1799
  Effect.provide(RunContextReader.layer(threadId)),
1781
- Effect.provideService(ThreadReader, reader),
1782
- Effect.provideService(Crypto.Crypto, crypto),
1800
+ Effect.provideContext(journalServices),
1783
1801
  Effect.mapError(() =>
1784
1802
  invalid("Saved original context cannot be resolved from its evidence"),
1785
1803
  ),
@@ -2573,8 +2591,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
2573
2591
  const progress =
2574
2592
  active?.writer ??
2575
2593
  (yield* makeProgressWriter(writer.threadId, config.deploymentId).pipe(
2576
- Effect.provideService(ThreadReader, reader),
2577
- Effect.provideService(Crypto.Crypto, crypto),
2594
+ Effect.provideContext(journalServices),
2578
2595
  ));
2579
2596
 
2580
2597
  const owned: RunWriter = {
@@ -2633,8 +2650,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
2633
2650
  const progress =
2634
2651
  active?.writer ??
2635
2652
  (yield* makeProgressWriter(request.threadId, config.deploymentId).pipe(
2636
- Effect.provideService(ThreadReader, reader),
2637
- Effect.provideService(Crypto.Crypto, crypto),
2653
+ Effect.provideContext(journalServices),
2638
2654
  ));
2639
2655
 
2640
2656
  let tail = {
@@ -4856,23 +4872,35 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
4856
4872
  historyEvidence.set(entry.record.recordId, entry);
4857
4873
  };
4858
4874
 
4875
+ // An exact cache prefix followed only by this fresh input has no response,
4876
+ // policy reservation or context to replay. Keep recovery on the full projector.
4877
+ const freshPrefix =
4878
+ priorContext === undefined &&
4879
+ initialJournal?.prefix !== undefined &&
4880
+ initialJournal.records?.length === 1 &&
4881
+ initialJournal.records[0] === originalInput
4882
+ ? initialJournal.prefix.prompt
4883
+ : undefined;
4884
+
4859
4885
  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
- );
4886
+ freshPrefix !== undefined
4887
+ ? initialRunJournal(freshPrefix)
4888
+ : cached !== undefined &&
4889
+ cached.threadId === ctx.threadId &&
4890
+ cached.through === projectionThrough &&
4891
+ cached.runId === runId &&
4892
+ cached.contextRecordId === priorContext?.recordId
4893
+ ? (boundaries.push(...cached.boundaries),
4894
+ cached.contextEvidence.forEach(retainHistory),
4895
+ cached.journal)
4896
+ : yield* projectRunJournalStream(
4897
+ canonical,
4898
+ runId,
4899
+ (boundary) => boundaries.push(boundary),
4900
+ priorContext,
4901
+ journalMetadata,
4902
+ priorContext === undefined ? retainHistory : undefined,
4903
+ );
4876
4904
 
4877
4905
  if (initialJournal?.prefix !== undefined) {
4878
4906
  const prompt = initialJournal.prefix.prompt;
@@ -5192,9 +5220,6 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5192
5220
  ),
5193
5221
  );
5194
5222
 
5195
- const addUsageTotal = (field: string, left: number, right: number) =>
5196
- checkedUsageTotal(field, left + right);
5197
-
5198
5223
  const subtractUsageTotal = (field: string, left: number, right: number) =>
5199
5224
  checkedUsageTotal(field, left - right);
5200
5225
 
@@ -5204,11 +5229,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5204
5229
  let unobservedModelCalls = journal.usage.unobservedModelCalls ?? 0;
5205
5230
 
5206
5231
  for (const count of stagedUnobservedCalls.values()) {
5207
- unobservedModelCalls = yield* addUsageTotal(
5208
- "unobservedModelCalls",
5209
- unobservedModelCalls,
5210
- count,
5211
- );
5232
+ unobservedModelCalls += count;
5212
5233
  }
5213
5234
  const detailedCalls = [...journal.usage.modelUsage, ...stagedCalls];
5214
5235
 
@@ -5229,17 +5250,15 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5229
5250
  let costMicrousd = journal.usage.costMicrousd;
5230
5251
 
5231
5252
  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);
5253
+ inputTokens += usage.inputTokens;
5254
+ outputTokens += usage.outputTokens;
5255
+ costMicrousd += usage.costMicrousd;
5235
5256
  }
5236
5257
 
5237
- const modelCalls = yield* addUsageTotal(
5238
- "modelCalls",
5239
- journal.usage.modelCalls,
5240
- stagedCalls.length,
5241
- );
5258
+ const modelCalls = journal.usage.modelCalls + stagedCalls.length;
5242
5259
 
5260
+ // Reconciliation must reject negative legacy deltas independently of
5261
+ // the final summary's complete safe-integer and additive checks.
5243
5262
  const legacyCalls = yield* subtractUsageTotal(
5244
5263
  "legacy.modelCalls",
5245
5264
  modelCalls,
@@ -5283,81 +5302,52 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5283
5302
 
5284
5303
  const existing = existingIndex < 0 ? undefined : byModel[existingIndex];
5285
5304
 
5286
- const legacyGroup = ModelUsageGroup.make({
5305
+ const legacyGroup = yield* ModelUsageGroup.makeEffect({
5287
5306
  provider: "unknown",
5288
5307
  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
- ),
5308
+ modelCalls: (existing?.modelCalls ?? 0) + legacyCalls,
5309
+ inputTokens: {
5310
+ total: (existing?.inputTokens.total ?? 0) + legacyInput,
5311
+ uncached: (existing?.inputTokens.uncached ?? 0) + legacyInput,
5305
5312
  cacheRead: existing?.inputTokens.cacheRead ?? 0,
5306
5313
  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
- ),
5314
+ },
5315
+ outputTokens: {
5316
+ total: (existing?.outputTokens.total ?? 0) + legacyOutput,
5317
+ text: (existing?.outputTokens.text ?? 0) + legacyOutput,
5319
5318
  reasoning: existing?.outputTokens.reasoning ?? 0,
5320
- }),
5321
- costMicrousd: yield* addUsageTotal(
5322
- "byModel.costMicrousd",
5323
- existing?.costMicrousd ?? 0,
5324
- legacyCost,
5319
+ },
5320
+ costMicrousd: (existing?.costMicrousd ?? 0) + legacyCost,
5321
+ }).pipe(
5322
+ Effect.mapError((cause) =>
5323
+ RunJournalError.make({
5324
+ message: "Run legacy usage exceeds safe-integer accounting bounds",
5325
+ cause,
5326
+ }),
5325
5327
  ),
5326
- });
5328
+ );
5327
5329
 
5328
5330
  if (existingIndex < 0) byModel.push(legacyGroup);
5329
5331
  else byModel[existingIndex] = legacyGroup;
5330
5332
  }
5331
5333
 
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({
5334
+ return yield* RunUsageSummary.makeEffect({
5345
5335
  ...(legacyCalls === 0 && detailed.webSearchCalls !== undefined
5346
5336
  ? { webSearchCalls: detailed.webSearchCalls }
5347
5337
  : {}),
5348
5338
  modelCalls,
5349
- inputTokens: InputTokenUsage.make({
5339
+ inputTokens: {
5350
5340
  total: inputTokens,
5351
5341
  // Legacy aggregate inputs are conservatively classified as uncached.
5352
- uncached,
5342
+ uncached: detailed.inputTokens.uncached + legacyInput,
5353
5343
  cacheRead: detailed.inputTokens.cacheRead,
5354
5344
  cacheWrite: detailed.inputTokens.cacheWrite,
5355
- }),
5356
- outputTokens: OutputTokenUsage.make({
5345
+ },
5346
+ outputTokens: {
5357
5347
  total: outputTokens,
5358
- text,
5348
+ text: detailed.outputTokens.text + legacyOutput,
5359
5349
  reasoning: detailed.outputTokens.reasoning,
5360
- }),
5350
+ },
5361
5351
  costMicrousd,
5362
5352
  byModel,
5363
5353
  unobservedModelCalls,
@@ -5373,7 +5363,14 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5373
5363
  ? "unknown"
5374
5364
  : "partial"
5375
5365
  : detailed.pricingStatus,
5376
- });
5366
+ }).pipe(
5367
+ Effect.mapError((cause) =>
5368
+ RunJournalError.make({
5369
+ message: "Run usage summary exceeds safe-integer accounting bounds",
5370
+ cause,
5371
+ }),
5372
+ ),
5373
+ );
5377
5374
  });
5378
5375
 
5379
5376
  const activeExecution = yield* reader.readExecution({ threadId: ctx.threadId, runId });
@@ -5549,37 +5546,38 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5549
5546
  const recordHalt = <A, R>(
5550
5547
  effect: Effect.Effect<A, DurableWorkerFailure, R>,
5551
5548
  ): 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
- ),
5549
+ Effect.catchCause(effect, (cause) => {
5550
+ const first = Cause.findErrorOption(cause);
5551
+
5552
+ const failure = Effect.failCause(
5553
+ Cause.map(cause, (error) =>
5554
+ error instanceof ThreadStoreError &&
5555
+ error.cause instanceof AgentPersistenceCapacityError
5556
+ ? error.cause
5557
+ : new CoordinatorHalt(error),
5567
5558
  ),
5568
- ),
5569
- );
5559
+ );
5560
+
5561
+ return Option.isNone(first) ||
5562
+ (first.value instanceof ThreadStoreError &&
5563
+ first.value.cause instanceof AgentPersistenceCapacityError)
5564
+ ? failure
5565
+ : Effect.andThen(Ref.set(haltRef, first.value), failure);
5566
+ });
5570
5567
 
5571
5568
  // Infrastructure errors can be wrapped by broker/Tool APIs. The interpreter checks this
5572
5569
  // 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);
5570
+ const progress = progressWriters.get(ctx);
5576
5571
 
5577
- if (failure !== undefined) return yield* failure;
5578
- const progress = progressWriters.get(ctx);
5572
+ const checkProgress =
5573
+ progress === undefined
5574
+ ? Effect.void
5575
+ : progress.check(runId).pipe(Effect.provideService(CurrentRunWriter, ctx));
5579
5576
 
5580
- if (progress !== undefined)
5581
- yield* progress.check(runId).pipe(Effect.provideService(CurrentRunWriter, ctx));
5582
- }),
5577
+ const checkpoint = recordHalt(
5578
+ Effect.flatMap(Ref.get(haltRef), (failure) =>
5579
+ failure === undefined ? checkProgress : Effect.fail(failure),
5580
+ ),
5583
5581
  );
5584
5582
 
5585
5583
  // One readonly Turn may own its response without publishing it. Promotion and settlement
@@ -7834,35 +7832,39 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
7834
7832
  stagedUnobservedCalls.set(turn, (stagedUnobservedCalls.get(turn) ?? 0) + 1);
7835
7833
  }),
7836
7834
  }),
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),
7835
+ (body) =>
7836
+ body.pipe(
7837
+ Effect.provideServiceEffect(ThreadHistory, ThreadHistory.makeFromStore),
7838
+ Effect.provideServiceEffect(ConversationStore, makeMemoryStore),
7839
+ Effect.scoped,
7840
+ ),
7841
+ Effect.provideContext(
7842
+ runServices.pipe(
7843
+ Context.add(SubagentHost.forTool, (source) =>
7844
+ source.threadId !== submission.threadId ||
7845
+ source.agentId !== submission.agentId ||
7846
+ source.runId !== runId
7847
+ ? SubagentHost.unavailable
7848
+ : workerRuntime.facet(
7849
+ {
7850
+ source,
7851
+ policy: agent.definition.policy,
7852
+ depth: delegationDepth,
7853
+ ...(inheritedGrant === undefined ? {} : { grant: inheritedGrant }),
7854
+ },
7855
+ submission.principal,
7856
+ submission.submissionId,
7857
+ ),
7858
+ ),
7859
+ Context.add(MessagingHost.forTool, (source) =>
7860
+ source.threadId !== submission.threadId ||
7861
+ source.agentId !== submission.agentId ||
7862
+ source.runId !== runId
7863
+ ? MessagingHost.unavailable
7864
+ : messagingRuntime.forTool(source, submission.principal),
7865
+ ),
7866
+ ),
7860
7867
  ),
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
7868
  Effect.andThen(checkpoint),
7867
7869
  // A wrapped Tool/approval failure cannot outrank retained infrastructure authority.
7868
7870
  // Preserve independent defects, interruptions and already-marked coordinator failures.
@@ -7909,21 +7911,20 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
7909
7911
  }),
7910
7912
  );
7911
7913
 
7912
- const execution = Effect.raceFirst(consume, abortWatcher);
7913
-
7914
- const raced = (
7915
- yieldAfter === undefined && submissionScheduling.yieldTo === undefined
7916
- ? execution
7917
- : Effect.raceFirst(
7918
- execution,
7914
+ const raced = Effect.raceAllFirst([
7915
+ consume,
7916
+ abortWatcher,
7917
+ ...(yieldAfter === undefined && submissionScheduling.yieldTo === undefined
7918
+ ? []
7919
+ : [
7919
7920
  Deferred.await(yieldSignal).pipe(
7920
7921
  Effect.map((nextSubmissionId) => ({
7921
7922
  _tag: "yielded" as const,
7922
7923
  ...(nextSubmissionId === undefined ? {} : { nextSubmissionId }),
7923
7924
  })),
7924
7925
  ),
7925
- )
7926
- ).pipe(Effect.provideService(IdGenerator, idGenerator));
7926
+ ]),
7927
+ ]).pipe(Effect.provideService(IdGenerator, idGenerator));
7927
7928
 
7928
7929
  const result = yield* raced.pipe(
7929
7930
  Effect.catchCauseFilter(
@@ -8149,17 +8150,14 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8149
8150
  resumeAfterRetention: () => void,
8150
8151
  onHandoff: (nextSubmissionId: SubmissionId) => void,
8151
8152
  yieldAfter?: DateTime.Utc,
8153
+ freshlyAdmitted = false,
8152
8154
  ) {
8153
8155
  // Recheck ownership outside the disposable cache's failure recovery.
8154
8156
  yield* session.checkActive;
8155
8157
 
8156
8158
  const checkpointPrompt = yield* promptHistory
8157
8159
  .take({ threadId: session.threadId })
8158
- .pipe(
8159
- Effect.provideService(ThreadStore, store),
8160
- Effect.provideService(ThreadReader, reader),
8161
- Effect.provideService(Crypto.Crypto, crypto),
8162
- );
8160
+ .pipe(Effect.provideService(ThreadStore, store), Effect.provideContext(journalServices));
8163
8161
 
8164
8162
  // withProgressSession exposes only progress.commit/publish. Their prepare step owns and
8165
8163
  // freezes each canonical fact before reaching this raw writer. Snapshot the private array
@@ -8173,7 +8171,8 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8173
8171
  committedPromptSuffix = undefined;
8174
8172
  }),
8175
8173
  );
8176
- session = yield* withProgressSession({
8174
+
8175
+ const promptSession: RunStorageSession = {
8177
8176
  ...storageSession,
8178
8177
  append: (batch, executions) => {
8179
8178
  const suffix = committedPromptSuffix;
@@ -8208,17 +8207,117 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8208
8207
  Effect.tapCause(() => Effect.sync(() => suffix.discard())),
8209
8208
  );
8210
8209
  },
8211
- });
8210
+ };
8211
+
8212
+ session = yield* withProgressSession(promptSession);
8212
8213
  const { claim, threadId } = session;
8213
8214
  const submissionId = claim.submissionId;
8214
8215
 
8215
- const snapshot = yield* ledger.loadRecoverySnapshot(
8216
- RecoverySnapshotRequest.make({ submissionId }),
8217
- );
8216
+ // The hint only selects this preparation. A real claim, current controls, exact
8217
+ // evidence and checkpoint absence are verified under its owner before any reuse.
8218
+ // No binding, instructions, model, Tool or cross-Thread operation runs in the window.
8219
+ let prepared =
8220
+ freshlyAdmitted && session.withInitialization !== undefined
8221
+ ? yield* session.withInitialization(
8222
+ Effect.gen(function* () {
8223
+ const snapshot = yield* ledger.loadRecoverySnapshot(
8224
+ RecoverySnapshotRequest.make({ submissionId }),
8225
+ );
8226
+
8227
+ const submission = snapshot.submission;
8228
+
8229
+ if (
8230
+ submission.state !== "ready" ||
8231
+ submission.workerAdmission !== undefined ||
8232
+ submission.messageAdmission !== undefined ||
8233
+ submission.parentLinkage !== undefined ||
8234
+ snapshot.inputApplied !== undefined ||
8235
+ snapshot.abortIntent !== undefined ||
8236
+ snapshot.approvalDecisions.length !== 0
8237
+ )
8238
+ return undefined;
8239
+ yield* ensureThreadCreated(threadId, submission.agentId, submission.agentDigests);
8240
+ yield* session.refresh;
8241
+ const before = (yield* session.tail).sequence;
8242
+
8243
+ const view = yield* recoveryView(
8244
+ threadId,
8245
+ before,
8246
+ [submissionId],
8247
+ runIdForSubmission(submissionId),
8248
+ );
8249
+
8250
+ if (
8251
+ view.progress !== undefined ||
8252
+ view.context !== undefined ||
8253
+ view.operational.length !== 0 ||
8254
+ view.records.some(({ record }) => record.payload._tag !== "ThreadCreated")
8255
+ )
8256
+ return undefined;
8257
+ const progress = progressWriters.get(session);
8258
+
8259
+ const applyInput = applyCanonicalInput(
8260
+ session,
8261
+ submission,
8262
+ session,
8263
+ view.records,
8264
+ undefined,
8265
+ );
8266
+
8267
+ yield* progress === undefined
8268
+ ? applyInput
8269
+ : progress
8270
+ .withVerifiedEmptyRun(runIdForSubmission(submissionId), before, applyInput)
8271
+ .pipe(Effect.provideService(CurrentRunWriter, promptSession));
8272
+ const through = (yield* session.tail).sequence;
8273
+ const added = yield* selectedRange(threadId, [submissionId], through, before);
8274
+ const records = [...view.records, ...added];
8275
+
8276
+ const accepted = progressWriters
8277
+ .get(session)
8278
+ ?.acceptedCheckpoint(runIdForSubmission(submissionId));
8279
+
8280
+ if (
8281
+ accepted === undefined ||
8282
+ accepted.throughSequence !== through ||
8283
+ accepted.progress.runId !== runIdForSubmission(submissionId) ||
8284
+ accepted.progress.submissionId !== submissionId
8285
+ )
8286
+ return undefined;
8287
+
8288
+ const start = yield* makeRunStart(
8289
+ submission,
8290
+ Duration.toMillis(registeredAgent.definition.policy.maxDuration),
8291
+ records,
8292
+ view.operational,
8293
+ ).pipe(Effect.provideService(CurrentRunWriter, session));
8294
+
8295
+ return {
8296
+ snapshot,
8297
+ through,
8298
+ view: {
8299
+ ...view,
8300
+ records,
8301
+ progress: accepted.progress,
8302
+ progressThrough: accepted.throughSequence,
8303
+ operational: accepted.operational,
8304
+ },
8305
+ start,
8306
+ };
8307
+ }),
8308
+ )
8309
+ : undefined;
8310
+
8311
+ const preparedInput = prepared !== undefined;
8312
+
8313
+ const snapshot =
8314
+ prepared?.snapshot ??
8315
+ (yield* ledger.loadRecoverySnapshot(RecoverySnapshotRequest.make({ submissionId })));
8218
8316
 
8219
8317
  const submission = snapshot.submission;
8220
8318
 
8221
- yield* ensureThreadCreated(threadId, submission.agentId, submission.agentDigests);
8319
+ if (prepared === undefined)
8320
+ yield* ensureThreadCreated(threadId, submission.agentId, submission.agentDigests);
8222
8321
  if (submission.workerAdmission?.origin.reporting?.mode === "standard")
8223
8322
  yield* updateRuntime.repairRun(threadId, submissionId);
8224
8323
  if (submission.workerAdmission !== undefined) {
@@ -8233,18 +8332,20 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8233
8332
  if (submission.state === "admitted") {
8234
8333
  yield* ledger.markReady(MarkReadyRequest.make({ submissionId }));
8235
8334
  }
8236
- yield* session.refresh;
8335
+ if (prepared === undefined) yield* session.refresh;
8237
8336
  const ctx: AttemptAppendContext = session;
8238
- let controlThrough = (yield* session.tail).sequence;
8337
+ let controlThrough = prepared?.through ?? (yield* session.tail).sequence;
8239
8338
 
8240
8339
  const initialThrough = controlThrough;
8241
8340
 
8242
- const initialView = yield* recoveryView(
8243
- threadId,
8244
- initialThrough,
8245
- [submissionId],
8246
- runIdForSubmission(submissionId),
8247
- );
8341
+ const initialView =
8342
+ prepared?.view ??
8343
+ (yield* recoveryView(
8344
+ threadId,
8345
+ initialThrough,
8346
+ [submissionId],
8347
+ runIdForSubmission(submissionId),
8348
+ ));
8248
8349
 
8249
8350
  let journalMetadata: ReturnType<typeof makeJournalMetadata> | undefined;
8250
8351
 
@@ -8367,15 +8468,17 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8367
8468
  ),
8368
8469
  );
8369
8470
  }
8370
- yield* applyCanonicalInput(ctx, submission, session, records, snapshot.inputApplied);
8471
+ if (prepared === undefined)
8472
+ yield* applyCanonicalInput(ctx, submission, session, records, snapshot.inputApplied);
8371
8473
 
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));
8474
+ const start =
8475
+ prepared?.start ??
8476
+ (yield* makeRunStart(
8477
+ submission,
8478
+ Duration.toMillis(agent.definition.policy.maxDuration),
8479
+ yield* refreshControl(),
8480
+ initialView.operational,
8481
+ ).pipe(Effect.provideService(CurrentRunWriter, ctx)));
8379
8482
 
8380
8483
  const savedRunTiming = start.timing;
8381
8484
 
@@ -8397,8 +8500,11 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8397
8500
  };
8398
8501
 
8399
8502
  let expiredChildObligation = false;
8503
+ let preparedControl = prepared !== undefined;
8400
8504
 
8401
8505
  if ((yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(runTiming.deadline)) {
8506
+ prepared = undefined;
8507
+ preparedControl = false;
8402
8508
  yield* commitStart;
8403
8509
  yield* reconcileRetainedChildren(ctx, submission, session);
8404
8510
  expiredChildObligation = yield* completeJoinedReleases(submission);
@@ -8406,7 +8512,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8406
8512
 
8407
8513
  // Recheck after duration interruption too: that Attempt may have declared new ordinary calls.
8408
8514
  const ordinaryCallsResolved = Effect.gen(function* () {
8409
- const reconciliationRecords = yield* refreshControl();
8515
+ const reconciliationRecords = preparedControl ? records : yield* refreshControl();
8516
+
8517
+ preparedControl = false;
8410
8518
 
8411
8519
  const reconciliationEvidence = yield* evidenceFor(
8412
8520
  reconciliationRecords,
@@ -8456,7 +8564,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8456
8564
  if (!expiredChildObligation && !(yield* ordinaryCallsResolved))
8457
8565
  return Option.none<Settlement>();
8458
8566
 
8459
- const continuationRecords = yield* refreshControl();
8567
+ const continuationRecords = prepared === undefined ? yield* refreshControl() : records;
8460
8568
 
8461
8569
  const continuation = yield* pendingToolBatchFor(
8462
8570
  continuationRecords,
@@ -8596,24 +8704,30 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8596
8704
 
8597
8705
  const lineage: AttemptLineage = {
8598
8706
  attemptId: claim.attemptId,
8599
- inputWasRecorded: evidence.inputRecorded,
8707
+ inputWasRecorded: !preparedInput && evidence.inputRecorded,
8600
8708
  };
8601
8709
 
8602
8710
  let approvalDecisionIntents = snapshot.approvalDecisions;
8603
8711
 
8604
8712
  while (true) {
8605
- yield* session.refresh;
8713
+ const initialization = prepared;
8714
+
8715
+ prepared = undefined;
8716
+ if (initialization === undefined) yield* session.refresh;
8606
8717
  const tail = yield* session.tail;
8607
8718
 
8608
- const currentRecords = yield* refreshControl(tail.sequence);
8719
+ const currentRecords =
8720
+ initialization === undefined ? yield* refreshControl(tail.sequence) : records;
8609
8721
 
8610
- const view = yield* recoveryView(
8611
- threadId,
8612
- tail.sequence,
8613
- [submissionId],
8614
- runIdForSubmission(submissionId),
8615
- currentRecords,
8616
- );
8722
+ const view =
8723
+ initialization?.view ??
8724
+ (yield* recoveryView(
8725
+ threadId,
8726
+ tail.sequence,
8727
+ [submissionId],
8728
+ runIdForSubmission(submissionId),
8729
+ currentRecords,
8730
+ ));
8617
8731
 
8618
8732
  let canonical: Stream.Stream<
8619
8733
  JournalRecordEnvelope,
@@ -8933,6 +9047,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8933
9047
  const session = claimed.value;
8934
9048
  const { claim } = session;
8935
9049
 
9050
+ const freshlyAdmitted = unclaimedAdmissions.get(claim.submissionId) === threadId;
9051
+
9052
+ unclaimedAdmissions.delete(claim.submissionId);
8936
9053
  firstClaimedSubmissionId ??= claim.submissionId;
8937
9054
 
8938
9055
  const attributes = {
@@ -9017,6 +9134,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
9017
9134
  });
9018
9135
  },
9019
9136
  options?.yieldAfter,
9137
+ freshlyAdmitted,
9020
9138
  ),
9021
9139
  threadId,
9022
9140
  claim,
@@ -10395,6 +10513,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
10395
10513
  const recoverOwnedWork = Effect.fnUntraced(function* (
10396
10514
  request: WorkRecoveryRequest,
10397
10515
  recoveredAdmissions = new Map<SubmissionId, RecoveryReport>(),
10516
+ deferUnclaimedAdmissions = false,
10398
10517
  ): Effect.fn.Return<WorkRecoveryReport, DurableWorkerFailure> {
10399
10518
  const { threadId, work: entry } = yield* Schema.decodeEffect(WorkRecoveryRequest)(request).pipe(
10400
10519
  Effect.mapError((cause) =>
@@ -10448,6 +10567,21 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
10448
10567
  const original = yield* lookupKnownSubmission("recover work", owner.submissionId);
10449
10568
 
10450
10569
  if (original.threadId !== threadId) return yield* invalid();
10570
+ if (
10571
+ deferUnclaimedAdmissions &&
10572
+ unclaimedAdmissions.get(owner.submissionId) === threadId &&
10573
+ original.state === "ready" &&
10574
+ !storageOwner.hasOwner(threadId) &&
10575
+ (yield* ledger.readAbortIntent(
10576
+ AbortIntentRequest.make({ submissionId: owner.submissionId }),
10577
+ )) === undefined
10578
+ ) {
10579
+ // The worker's first scoped claim performs the authoritative control/journal
10580
+ // read. This hint skips only a redundant pre-claim recovery traversal; it grants
10581
+ // no claim, synthesizes no decision, and supplies no model-visible facts.
10582
+ return report("deferred");
10583
+ }
10584
+ unclaimedAdmissions.delete(owner.submissionId);
10451
10585
  const submission = yield* recoverAdmission(owner.submissionId);
10452
10586
 
10453
10587
  return report(submission.disposition, submission);
@@ -10926,6 +11060,29 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
10926
11060
 
10927
11061
  const outcome = yield* isolateRecovery(
10928
11062
  Effect.gen(function* () {
11063
+ // This incarnation admitted the sole untouched root. The native snapshot proves
11064
+ // there is no old owner to repair; claim and live controls still run normally.
11065
+ // New source mutations retain their dirty generation for the next maintenance scan.
11066
+ if (
11067
+ workCursor === undefined &&
11068
+ options?.deferUnclaimedAdmissions === true &&
11069
+ workDiscovery.isOnlyFreshAdmission !== undefined &&
11070
+ !storageOwner.hasOwner(threadId)
11071
+ ) {
11072
+ const fresh = [...unclaimedAdmissions].find(([, owner]) => owner === threadId);
11073
+
11074
+ if (
11075
+ fresh !== undefined &&
11076
+ (yield* workDiscovery.isOnlyFreshAdmission({
11077
+ threadId,
11078
+ submissionId: fresh[0],
11079
+ }))
11080
+ ) {
11081
+ complete = true;
11082
+
11083
+ return;
11084
+ }
11085
+ }
10929
11086
  for (
10930
11087
  let pages = 0;
10931
11088
  pages < MAX_RECOVERY_PAGES && workReports.length < MAX_RECOVERY_WORK_ITEMS;
@@ -10941,7 +11098,11 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
10941
11098
 
10942
11099
  phase = "recovery";
10943
11100
  for (const work of workPage.entries) {
10944
- const repaired = yield* recoverOwnedWork({ threadId, work }, recoveredAdmissions);
11101
+ const repaired = yield* recoverOwnedWork(
11102
+ { threadId, work },
11103
+ recoveredAdmissions,
11104
+ options?.deferUnclaimedAdmissions === true,
11105
+ );
10945
11106
 
10946
11107
  workReports.push(repaired);
10947
11108
  if (
@@ -11165,7 +11326,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
11165
11326
  queueSequence: admitted.queueSequence,
11166
11327
  });
11167
11328
  }
11168
- const admitted = yield* ledger.admit(request);
11329
+ const admitted = yield* ledger.admit(request, { readyIfInitialized: true });
11169
11330
 
11170
11331
  yield* hit("submit:after-admit");
11171
11332
 
@@ -11181,10 +11342,24 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
11181
11342
  if (admitted.replayed && admitted.state !== "admitted") {
11182
11343
  return receipt;
11183
11344
  }
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 }));
11345
+ if (admitted.state === "admitted") {
11346
+ yield* materializeThread(options.threadId);
11347
+ yield* ensureThreadCreated(options.threadId, agent.definition.id, options.definitions);
11348
+ yield* hit("submit:after-materialize");
11349
+ yield* ledger.markReady(MarkReadyRequest.make({ submissionId: admitted.submissionId }));
11350
+ }
11351
+ if (
11352
+ !admitted.replayed &&
11353
+ messageAdmission === undefined &&
11354
+ options.workerAdmission === undefined
11355
+ ) {
11356
+ unclaimedAdmissions.set(admitted.submissionId, options.threadId);
11357
+ if (unclaimedAdmissions.size > 128) {
11358
+ const oldest = unclaimedAdmissions.keys().next().value;
11359
+
11360
+ if (oldest !== undefined) unclaimedAdmissions.delete(oldest);
11361
+ }
11362
+ }
11188
11363
  yield* wake.notify(options.threadId);
11189
11364
 
11190
11365
  return receipt;