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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/dist/{Admin-BX9ns28E.d.mts → Admin-BHmp2V9p.d.mts} +25 -16
  2. package/dist/{Agent-9aBoIi74.d.mts → Agent-B39jRnFv.d.mts} +2 -2
  3. package/dist/{Agent-0WvBWWbM.mjs → Agent-BiRbr73L.mjs} +7 -6
  4. package/dist/Agent-BiRbr73L.mjs.map +1 -0
  5. package/dist/{AgentRuntime-CQ-qcETl.mjs → AgentRuntime-B6qZlD3f.mjs} +199 -191
  6. package/dist/AgentRuntime-B6qZlD3f.mjs.map +1 -0
  7. package/dist/{AgentRuntime-BfP14534.d.mts → AgentRuntime-BD3ZHAMq.d.mts} +10 -10
  8. package/dist/{Digest-DCuPlhNk.mjs → Digest-EpeqWEdv.mjs} +41 -14
  9. package/dist/Digest-EpeqWEdv.mjs.map +1 -0
  10. package/dist/{DurableAgentRuntime-BAmSkWYr.mjs → DurableAgentRuntime-CBBjRNJN.mjs} +1447 -1424
  11. package/dist/DurableAgentRuntime-CBBjRNJN.mjs.map +1 -0
  12. package/dist/{EventSource-BwliwQz9.d.mts → EventSource-C9aaJaXT.d.mts} +2 -2
  13. package/dist/{MessageDelivery-B943ebI_.d.mts → MessageDelivery-BGDm-c46.d.mts} +42 -42
  14. package/dist/{Messaging-C_6OOg_s.d.mts → Messaging-SVu6rWV8.d.mts} +12 -6
  15. package/dist/{MessagingHost-B8oKM7AC.d.mts → MessagingHost-C6SOAcoG.d.mts} +2 -2
  16. package/dist/{Output-Dsx6k52t.d.mts → Output-CRamN1Fy.d.mts} +2 -2
  17. package/dist/{PreparedInputAdmission-CxSJSiTl.d.mts → PreparedInputAdmission-BvDqlgiP.d.mts} +3 -3
  18. package/dist/{RunContinuation-DHYNAQFi.mjs → RunContinuation-4ZATcVLB.mjs} +113 -56
  19. package/dist/RunContinuation-4ZATcVLB.mjs.map +1 -0
  20. package/dist/{RunContinuation-FHM7XPjq.d.mts → RunContinuation-CTJ1jGrO.d.mts} +6 -6
  21. package/dist/{RunJournal-kfKVxJj7.mjs → RunJournal-CNZqA3Ge.mjs} +33 -5
  22. package/dist/RunJournal-CNZqA3Ge.mjs.map +1 -0
  23. package/dist/{RunOptions-D8zxFIrZ.d.mts → RunOptions-Be77lKVZ.d.mts} +2 -2
  24. package/dist/{RunStorage-uqPxps09.mjs → RunStorage-BHjuuERI.mjs} +9 -6
  25. package/dist/RunStorage-BHjuuERI.mjs.map +1 -0
  26. package/dist/{Scheduling-CVyfXlBR.mjs → Scheduling-NG8slKdI.mjs} +11 -11
  27. package/dist/Scheduling-NG8slKdI.mjs.map +1 -0
  28. package/dist/{Subagent-vpffemql.mjs → Subagent-CTIpnzlB.mjs} +53 -40
  29. package/dist/Subagent-CTIpnzlB.mjs.map +1 -0
  30. package/dist/{Subagent-E6tvd7-k.d.mts → Subagent-DKhZA72u.d.mts} +19 -8
  31. package/dist/{SubagentHost-B6w2m-SI.d.mts → SubagentHost-D1xBgn9g.d.mts} +2 -2
  32. package/dist/{Subscription-DONig54Q.d.mts → Subscription-BNg9VCRl.d.mts} +2 -2
  33. package/dist/{SubscriptionInput-mJIRxWJM.d.mts → SubscriptionInput-BUoXv-_K.d.mts} +2 -2
  34. package/dist/{ThreadImport-DpHxjBrA.d.mts → ThreadImport-CMgzttvr.d.mts} +5 -5
  35. package/dist/{ThreadImport-DckUMEqJ.mjs → ThreadImport-CodYoJoa.mjs} +7 -7
  36. package/dist/ThreadImport-CodYoJoa.mjs.map +1 -0
  37. package/dist/{ThreadStore-CEekRa80.mjs → ThreadStore-59uDGXul.mjs} +163 -84
  38. package/dist/ThreadStore-59uDGXul.mjs.map +1 -0
  39. package/dist/{ThreadTransfer-B4gTQLIl.mjs → ThreadTransfer-3jigUl3g.mjs} +3 -3
  40. package/dist/{ThreadTransfer-B4gTQLIl.mjs.map → ThreadTransfer-3jigUl3g.mjs.map} +1 -1
  41. package/dist/{WorkerAdmission-DFVbbVh2.mjs → WorkerAdmission-DDYJdOsU.mjs} +4 -4
  42. package/dist/{WorkerAdmission-DFVbbVh2.mjs.map → WorkerAdmission-DDYJdOsU.mjs.map} +1 -1
  43. package/dist/{agent-registration-ByMZ_cK5.mjs → agent-registration-DRbyhAu-.mjs} +69 -56
  44. package/dist/agent-registration-DRbyhAu-.mjs.map +1 -0
  45. package/dist/capabilities/Approval.mjs +2 -2
  46. package/dist/capabilities/Approval.mjs.map +1 -1
  47. package/dist/capabilities/BrowserUse.d.mts +102 -1867
  48. package/dist/capabilities/BrowserUse.mjs +74 -798
  49. package/dist/capabilities/BrowserUse.mjs.map +1 -1
  50. package/dist/capabilities/Budget.mjs +3 -1
  51. package/dist/capabilities/Budget.mjs.map +1 -1
  52. package/dist/capabilities/CodeMode.d.mts +1 -1
  53. package/dist/capabilities/CodeMode.mjs +16 -9
  54. package/dist/capabilities/CodeMode.mjs.map +1 -1
  55. package/dist/capabilities/Commands.d.mts +1 -1
  56. package/dist/capabilities/McpClient.mjs +1 -1
  57. package/dist/capabilities/McpClient.mjs.map +1 -1
  58. package/dist/capabilities/Messaging.d.mts +1 -1
  59. package/dist/capabilities/Messaging.mjs +4 -4
  60. package/dist/capabilities/Messaging.mjs.map +1 -1
  61. package/dist/capabilities/ModelContext.mjs +7 -5
  62. package/dist/capabilities/ModelContext.mjs.map +1 -1
  63. package/dist/capabilities/Redaction.mjs +49 -19
  64. package/dist/capabilities/Redaction.mjs.map +1 -1
  65. package/dist/capabilities/RunHooks.d.mts +1 -1
  66. package/dist/capabilities/RunHooks.mjs +1 -1
  67. package/dist/capabilities/RunHooks.mjs.map +1 -1
  68. package/dist/capabilities/Subagent.d.mts +1 -1
  69. package/dist/capabilities/Subagent.mjs +1 -1
  70. package/dist/capabilities/SubagentReservations.mjs +3 -1
  71. package/dist/capabilities/SubagentReservations.mjs.map +1 -1
  72. package/dist/capabilities/WebCapture.mjs.map +1 -1
  73. package/dist/capabilities/WebSearch.mjs +7 -7
  74. package/dist/capabilities/WebSearch.mjs.map +1 -1
  75. package/dist/core/Agent.d.mts +1 -1
  76. package/dist/core/Agent.mjs +1 -1
  77. package/dist/core/AgentUpdates.mjs +8 -8
  78. package/dist/core/AgentUpdates.mjs.map +1 -1
  79. package/dist/core/Thread.mjs +1 -1
  80. package/dist/core/Thread.mjs.map +1 -1
  81. package/dist/core/Usage.mjs +2 -2
  82. package/dist/core/Usage.mjs.map +1 -1
  83. package/dist/durable/Admin.d.mts +1 -1
  84. package/dist/durable/AgentRegistration.d.mts +1 -1
  85. package/dist/durable/AgentRegistration.mjs +1 -1
  86. package/dist/durable/Certification.d.mts +1 -1
  87. package/dist/durable/CommittedActivity.d.mts +1 -1
  88. package/dist/durable/CommittedActivity.mjs +2 -2
  89. package/dist/durable/Digest.mjs +1 -1
  90. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  91. package/dist/durable/DurableAgentRuntime.mjs +2 -2
  92. package/dist/durable/EventSource.d.mts +1 -1
  93. package/dist/durable/EventSource.mjs +6 -6
  94. package/dist/durable/EventSource.mjs.map +1 -1
  95. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  96. package/dist/durable/LifecyclePublication.d.mts +1 -1
  97. package/dist/durable/LifecyclePublication.mjs +1 -1
  98. package/dist/durable/MessageDelivery.d.mts +1 -1
  99. package/dist/durable/MessageDelivery.mjs +1 -1
  100. package/dist/durable/MessageDeliveryStoreConformance.d.mts +2 -2
  101. package/dist/durable/MessageDeliveryStoreConformance.mjs +1 -1
  102. package/dist/durable/PersistentHistory.d.mts +1 -1
  103. package/dist/durable/PersistentHistory.mjs +3 -3
  104. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  105. package/dist/durable/RecordFormat.mjs +2 -2
  106. package/dist/durable/RecordFormat.mjs.map +1 -1
  107. package/dist/durable/Recovery.d.mts +1 -1
  108. package/dist/durable/RunContinuation.d.mts +2 -2
  109. package/dist/durable/RunContinuation.mjs +2 -2
  110. package/dist/durable/RunJournal.d.mts +1 -1
  111. package/dist/durable/RunJournal.mjs +1 -1
  112. package/dist/durable/RunStorage.d.mts +1 -1
  113. package/dist/durable/RunStorage.mjs +1 -1
  114. package/dist/durable/Scheduling.d.mts +1 -1
  115. package/dist/durable/Scheduling.mjs +1 -1
  116. package/dist/durable/SettlementPublisher.d.mts +1 -1
  117. package/dist/durable/SettlementPublisher.mjs +2 -2
  118. package/dist/durable/SubmissionLedger.d.mts +1 -1
  119. package/dist/durable/SubmissionLedger.mjs +5 -1
  120. package/dist/durable/SubmissionLedger.mjs.map +1 -1
  121. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  122. package/dist/durable/SubmissionLedgerConformance.mjs +4 -4
  123. package/dist/durable/SubmissionStatus.d.mts +1 -1
  124. package/dist/durable/Subscription.d.mts +1 -1
  125. package/dist/durable/SubscriptionInput.d.mts +1 -1
  126. package/dist/durable/SubscriptionInput.mjs +3 -3
  127. package/dist/durable/SubscriptionInput.mjs.map +1 -1
  128. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  129. package/dist/durable/SubscriptionStoreConformance.mjs +2 -2
  130. package/dist/durable/SubscriptionStoreConformance.mjs.map +1 -1
  131. package/dist/durable/SubscriptionTools.d.mts +2 -2
  132. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  133. package/dist/durable/Subscriptions.d.mts +5 -5
  134. package/dist/durable/Subscriptions.mjs +2 -2
  135. package/dist/durable/Subscriptions.mjs.map +1 -1
  136. package/dist/durable/ThreadArchiveRange.d.mts +1 -1
  137. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  138. package/dist/durable/ThreadContextHistory.mjs +1 -1
  139. package/dist/durable/ThreadImport.d.mts +1 -1
  140. package/dist/durable/ThreadImport.mjs +1 -1
  141. package/dist/durable/ThreadInvariants.d.mts +3 -3
  142. package/dist/durable/ThreadInvariants.mjs +4 -4
  143. package/dist/durable/ThreadInvariants.mjs.map +1 -1
  144. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  145. package/dist/durable/ThreadReader.d.mts +1 -1
  146. package/dist/durable/ThreadReader.mjs +1 -1
  147. package/dist/durable/ThreadStore.d.mts +1 -1
  148. package/dist/durable/ThreadStore.mjs +1 -1
  149. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  150. package/dist/durable/ThreadStoreConformance.mjs +3 -3
  151. package/dist/durable/ThreadTransfer.d.mts +1 -1
  152. package/dist/durable/ThreadTransfer.mjs +1 -1
  153. package/dist/durable/ThreadWork.d.mts +1 -1
  154. package/dist/durable/ThreadWork.mjs +4 -4
  155. package/dist/durable/ThreadWork.mjs.map +1 -1
  156. package/dist/durable/WorkerAdmission.d.mts +1 -1
  157. package/dist/durable/WorkerAdmission.mjs +1 -1
  158. package/dist/durable/WorkerHost.d.mts +2 -2
  159. package/dist/engine/AgentRuntime.d.mts +1 -1
  160. package/dist/engine/AgentRuntime.mjs +1 -1
  161. package/dist/engine/MessagingHost.d.mts +1 -1
  162. package/dist/engine/Output.d.mts +1 -1
  163. package/dist/engine/RunOptions.d.mts +1 -1
  164. package/dist/engine/SubagentHost.d.mts +1 -1
  165. package/dist/index.d.mts +19 -19
  166. package/dist/index.mjs +14 -14
  167. package/dist/{run-context-reader-OcsZr4bk.d.mts → run-context-reader-CC7ZD95F.d.mts} +2 -2
  168. package/package.json +1 -1
  169. package/src/capabilities/Approval.ts +58 -60
  170. package/src/capabilities/BrowserUse.ts +89 -944
  171. package/src/capabilities/Budget.ts +8 -1
  172. package/src/capabilities/CodeMode.ts +57 -33
  173. package/src/capabilities/McpClient.ts +2 -2
  174. package/src/capabilities/Messaging.ts +51 -55
  175. package/src/capabilities/ModelContext.ts +5 -2
  176. package/src/capabilities/Redaction.ts +64 -34
  177. package/src/capabilities/RunHooks.ts +52 -55
  178. package/src/capabilities/Subagent.ts +33 -33
  179. package/src/capabilities/SubagentReservations.ts +9 -1
  180. package/src/capabilities/WebCapture.ts +6 -0
  181. package/src/capabilities/WebSearch.ts +10 -7
  182. package/src/capabilities/internal/subagent-background.ts +202 -158
  183. package/src/core/Agent.ts +11 -6
  184. package/src/core/AgentUpdates.ts +8 -8
  185. package/src/core/Thread.ts +9 -10
  186. package/src/core/Usage.ts +7 -8
  187. package/src/core/internal/agent-updates.ts +1 -0
  188. package/src/durable/DurableAgentRuntime.ts +645 -625
  189. package/src/durable/EventSource.ts +15 -9
  190. package/src/durable/RecordFormat.ts +5 -3
  191. package/src/durable/RunContinuation.ts +233 -81
  192. package/src/durable/RunJournal.ts +10 -9
  193. package/src/durable/RunStorage.ts +122 -124
  194. package/src/durable/Scheduling.ts +639 -651
  195. package/src/durable/SubmissionLedger.ts +4 -0
  196. package/src/durable/SubscriptionInput.ts +5 -3
  197. package/src/durable/SubscriptionStoreConformance.ts +23 -23
  198. package/src/durable/Subscriptions.ts +18 -19
  199. package/src/durable/ThreadImport.ts +18 -19
  200. package/src/durable/ThreadInvariants.ts +2 -1
  201. package/src/durable/ThreadStore.ts +215 -111
  202. package/src/durable/ThreadWork.ts +1 -2
  203. package/src/durable/internal/StorageOwner.ts +10 -4
  204. package/src/durable/internal/agent-registration.ts +96 -89
  205. package/src/durable/internal/canonical-json.ts +61 -20
  206. package/src/durable/internal/prompt-history-cache.ts +13 -4
  207. package/src/durable/internal/record-encoding.ts +120 -64
  208. package/src/durable/internal/schedule-time.ts +6 -6
  209. package/src/durable/internal/usage-totals.ts +38 -0
  210. package/src/durable/internal/worker-host.ts +69 -74
  211. package/src/engine/internal/agent-runtime.ts +1067 -1018
  212. package/src/engine/internal/provisional-text.ts +12 -10
  213. package/src/engine/internal/tool-exposure.ts +22 -18
  214. package/src/engine/internal/tool-telemetry.ts +12 -7
  215. package/dist/Agent-0WvBWWbM.mjs.map +0 -1
  216. package/dist/AgentRuntime-CQ-qcETl.mjs.map +0 -1
  217. package/dist/Digest-DCuPlhNk.mjs.map +0 -1
  218. package/dist/DurableAgentRuntime-BAmSkWYr.mjs.map +0 -1
  219. package/dist/RunContinuation-DHYNAQFi.mjs.map +0 -1
  220. package/dist/RunJournal-kfKVxJj7.mjs.map +0 -1
  221. package/dist/RunStorage-uqPxps09.mjs.map +0 -1
  222. package/dist/Scheduling-CVyfXlBR.mjs.map +0 -1
  223. package/dist/Subagent-vpffemql.mjs.map +0 -1
  224. package/dist/ThreadImport-DckUMEqJ.mjs.map +0 -1
  225. package/dist/ThreadStore-CEekRa80.mjs.map +0 -1
  226. package/dist/agent-registration-ByMZ_cK5.mjs.map +0 -1
@@ -1662,6 +1662,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1662
1662
  let context: RunJournalContext | undefined;
1663
1663
  let progress: RunContinuation | undefined;
1664
1664
  let progressThrough: CanonicalSequence | undefined;
1665
+ let operational: ReadonlyArray<RecordEnvelope> = [];
1665
1666
 
1666
1667
  const invalid = (message: string) =>
1667
1668
  ThreadStoreError.make({
@@ -1822,6 +1823,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1822
1823
  context = saved;
1823
1824
  progress = cursor;
1824
1825
  progressThrough = envelope.throughSequence;
1826
+ operational = envelope.operational;
1825
1827
  }
1826
1828
  }
1827
1829
 
@@ -1833,7 +1835,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1833
1835
  ).values(),
1834
1836
  ].sort((left, right) => left.sequence - right.sequence);
1835
1837
 
1836
- return { canonical: Stream.fromIterable(records), context, progress, progressThrough };
1838
+ return { records, context, progress, progressThrough, operational };
1837
1839
  });
1838
1840
 
1839
1841
  const readControl = Effect.fnUntraced(function* (
@@ -1843,9 +1845,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1843
1845
  const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
1844
1846
  const view = yield* recoveryView(threadId, tail.tailSequence, submissionIds);
1845
1847
 
1846
- return yield* Stream.runCollect(
1847
- view.canonical.pipe(Stream.filter(controlRecords(submissionIds))),
1848
- );
1848
+ return view.records.filter(controlRecords(submissionIds));
1849
1849
  });
1850
1850
 
1851
1851
  const readAllTolerant = (
@@ -1888,9 +1888,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
1888
1888
  return { records: [], materialized: false, throughSequence: ZERO_SEQUENCE, submissionIds };
1889
1889
 
1890
1890
  const records = yield* recoveryView(threadId, tail.value.tailSequence, submissionIds).pipe(
1891
- Effect.flatMap((view) =>
1892
- Stream.runCollect(view.canonical.pipe(Stream.filter(controlRecords(submissionIds)))),
1893
- ),
1891
+ Effect.map((view) => view.records.filter(controlRecords(submissionIds))),
1894
1892
  Effect.catchTag("ThreadNotMaterialized", (error) =>
1895
1893
  ThreadStoreError.make({
1896
1894
  operation: "read recovery history",
@@ -2629,50 +2627,49 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
2629
2627
  // also covers a destination handoff written before dispatch or after Run settlement.
2630
2628
  const progressStore = ThreadStore.of({
2631
2629
  ...store,
2632
- append: (request) =>
2633
- Effect.gen(function* () {
2634
- const active = activeProgress.get(request.threadId);
2630
+ append: Effect.fnUntraced(function* (request) {
2631
+ const active = activeProgress.get(request.threadId);
2635
2632
 
2636
- const progress =
2637
- active?.writer ??
2638
- (yield* makeProgressWriter(request.threadId, config.deploymentId).pipe(
2639
- Effect.provideService(ThreadReader, reader),
2640
- Effect.provideService(Crypto.Crypto, crypto),
2641
- ));
2633
+ const progress =
2634
+ active?.writer ??
2635
+ (yield* makeProgressWriter(request.threadId, config.deploymentId).pipe(
2636
+ Effect.provideService(ThreadReader, reader),
2637
+ Effect.provideService(Crypto.Crypto, crypto),
2638
+ ));
2642
2639
 
2643
- let tail = {
2644
- sequence: request.expectedTailSequence,
2645
- };
2640
+ let tail = {
2641
+ sequence: request.expectedTailSequence,
2642
+ };
2646
2643
 
2647
- return yield* progress.commit(request.batch).pipe(
2648
- Effect.provideService(CurrentRunWriter, {
2649
- threadId: request.threadId,
2650
- tail: Effect.sync(() => tail),
2651
- withProgress: (effect) => storageOwner.withProgress(request.threadId, effect),
2652
- append: (batch, executions) =>
2653
- store
2654
- .append(
2655
- AppendRequest.make({
2656
- ...request,
2657
- expectedTailSequence: tail.sequence,
2658
-
2659
- batch,
2660
- executions,
2644
+ return yield* progress.commit(request.batch).pipe(
2645
+ Effect.provideService(CurrentRunWriter, {
2646
+ threadId: request.threadId,
2647
+ tail: Effect.sync(() => tail),
2648
+ withProgress: (effect) => storageOwner.withProgress(request.threadId, effect),
2649
+ append: (batch, executions) =>
2650
+ store
2651
+ .append(
2652
+ AppendRequest.make({
2653
+ ...request,
2654
+ expectedTailSequence: tail.sequence,
2655
+
2656
+ batch,
2657
+ executions,
2658
+ }),
2659
+ )
2660
+ .pipe(
2661
+ Effect.tapErrorTag("AppendConflict", (conflict) =>
2662
+ Effect.sync(() => {
2663
+ if (conflict.reason === "tail" && conflict.actualTailSequence !== undefined)
2664
+ tail = {
2665
+ sequence: conflict.actualTailSequence,
2666
+ };
2661
2667
  }),
2662
- )
2663
- .pipe(
2664
- Effect.tapErrorTag("AppendConflict", (conflict) =>
2665
- Effect.sync(() => {
2666
- if (conflict.reason === "tail" && conflict.actualTailSequence !== undefined)
2667
- tail = {
2668
- sequence: conflict.actualTailSequence,
2669
- };
2670
- }),
2671
- ),
2672
2668
  ),
2673
- }),
2674
- );
2675
- }),
2669
+ ),
2670
+ }),
2671
+ );
2672
+ }),
2676
2673
  });
2677
2674
 
2678
2675
  const appendBatch = (ctx: Pick<RunWriter, "threadId" | "append">, batch: CanonicalBatch) =>
@@ -2701,51 +2698,49 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
2701
2698
  threadId: ctx.threadId,
2702
2699
  tail: ctx.tail,
2703
2700
  withProgress: ctx.withProgress,
2704
- publishSettlement: (prepared, executions) =>
2705
- Effect.gen(function* () {
2706
- let tail = yield* ctx.tail;
2707
-
2708
- for (let retries = 0; ; retries++) {
2709
- const result = yield* runStorage
2710
- .publishSettlement(
2711
- SettlementPublication.make({
2712
- submissionId,
2713
- authority,
2714
- append: AppendRequest.make({
2715
- threadId: ctx.threadId,
2716
- expectedTailSequence: tail.sequence,
2717
-
2718
- batch: prepared,
2719
- executions,
2720
- }),
2701
+ publishSettlement: Effect.fnUntraced(function* (prepared, executions) {
2702
+ let tail = yield* ctx.tail;
2703
+
2704
+ for (let retries = 0; ; retries++) {
2705
+ const result = yield* runStorage
2706
+ .publishSettlement(
2707
+ SettlementPublication.make({
2708
+ submissionId,
2709
+ authority,
2710
+ append: AppendRequest.make({
2711
+ threadId: ctx.threadId,
2712
+ expectedTailSequence: tail.sequence,
2713
+
2714
+ batch: prepared,
2715
+ executions,
2721
2716
  }),
2722
- )
2723
- .pipe(
2724
- (effect) =>
2725
- storageOwner.withMutation(Effect.andThen(ctx.checkActive, effect)),
2726
- Effect.catchTag("AppendConflict", (conflict) => {
2727
- if (
2728
- conflict.reason !== "tail" ||
2729
- conflict.actualTailSequence === undefined ||
2730
- retries >= 8
2731
- )
2732
- return Effect.fail(conflict);
2733
- tail = {
2734
- sequence: conflict.actualTailSequence,
2735
- };
2736
-
2737
- if (executions !== undefined && executions.length > 0)
2738
- return ctx.refresh.pipe(Effect.andThen(Effect.fail(conflict)));
2739
-
2740
- return Effect.succeed(undefined);
2741
- }),
2742
- );
2717
+ }),
2718
+ )
2719
+ .pipe(
2720
+ (effect) => storageOwner.withMutation(Effect.andThen(ctx.checkActive, effect)),
2721
+ Effect.catchTag("AppendConflict", (conflict) => {
2722
+ if (
2723
+ conflict.reason !== "tail" ||
2724
+ conflict.actualTailSequence === undefined ||
2725
+ retries >= 8
2726
+ )
2727
+ return Effect.fail(conflict);
2728
+ tail = {
2729
+ sequence: conflict.actualTailSequence,
2730
+ };
2743
2731
 
2744
- if (result === undefined) continue;
2732
+ if (executions !== undefined && executions.length > 0)
2733
+ return ctx.refresh.pipe(Effect.andThen(Effect.fail(conflict)));
2745
2734
 
2746
- return result;
2747
- }
2748
- }),
2735
+ return Effect.succeed(undefined);
2736
+ }),
2737
+ );
2738
+
2739
+ if (result === undefined) continue;
2740
+
2741
+ return result;
2742
+ }
2743
+ }),
2749
2744
  }),
2750
2745
  );
2751
2746
  });
@@ -3207,15 +3202,23 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
3207
3202
  yield* ownership.markInputApplied({ recordId, sequence: result.firstSequence });
3208
3203
  });
3209
3204
 
3210
- const readRunStart = Effect.fnUntraced(function* (threadId: ThreadId, runId: RunId) {
3211
- const loaded = yield* reader
3212
- .readExecution({ threadId, runId })
3213
- .pipe(Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed(Option.none())));
3205
+ const readRunStart = Effect.fnUntraced(function* (
3206
+ threadId: ThreadId,
3207
+ runId: RunId,
3208
+ operational?: ReadonlyArray<RecordEnvelope>,
3209
+ ) {
3210
+ if (operational === undefined) {
3211
+ const loaded = yield* reader
3212
+ .readExecution({ threadId, runId })
3213
+ .pipe(Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed(Option.none())));
3214
+
3215
+ if (Option.isNone(loaded)) return undefined;
3216
+ operational = loaded.value.operational;
3217
+ }
3214
3218
 
3215
- if (Option.isNone(loaded)) return undefined;
3216
3219
  const recordId = runStartedRecordId(runId);
3217
- const starts = loaded.value.operational.filter(({ payload }) => payload._tag === "RunStarted");
3218
- const existing = loaded.value.operational.find((record) => record.recordId === recordId);
3220
+ const starts = operational.filter(({ payload }) => payload._tag === "RunStarted");
3221
+ const existing = operational.find((record) => record.recordId === recordId);
3219
3222
 
3220
3223
  if (
3221
3224
  starts.length > 1 ||
@@ -3234,11 +3237,12 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
3234
3237
  submission: SubmissionSnapshot,
3235
3238
  maxDurationMillis: number,
3236
3239
  records: ReadonlyArray<CanonicalRecordEnvelope>,
3240
+ operational: ReadonlyArray<RecordEnvelope>,
3237
3241
  ) {
3238
3242
  const writer = yield* CurrentRunWriter;
3239
3243
  const runId = runIdForSubmission(submission.submissionId);
3240
3244
  const recordId = runStartedRecordId(runId);
3241
- const existing = yield* readRunStart(submission.threadId, runId);
3245
+ const existing = yield* readRunStart(submission.threadId, runId, operational);
3242
3246
  let start: RecordEnvelope;
3243
3247
  let allowance = maxDurationMillis;
3244
3248
  let committed = existing !== undefined;
@@ -4762,70 +4766,71 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
4762
4766
  knownIds.add(recordId);
4763
4767
  });
4764
4768
 
4765
- /**
4766
- * Drive the shared interpreter Effect with history rebuilt by the run journal, commit
4767
- * each Turn under the local owner, and watch for durable abort intent. Engine Run failures settle `failed`; coordinator failures abort the
4768
- * Attempt cleanly with the obligation still owed.
4769
- *
4770
- * The interpreter supplies validated response, closed Tool-result, and completion facts.
4771
- * Responses precede approval and effectful dispatch; readonly responses may join their closed
4772
- * results. Tool results precede input drains and the next Turn. A no-tool response and Run
4773
- * completion share one batch. A completion Tool's result
4774
- * precedes its terminal-only completion batch, so recovery can repeat a pure output projection
4775
- * without replaying the handler. Pending batches resume from their canonical declarations.
4776
- */
4777
- const runModel = <
4778
- InputSchema extends Schema.Top,
4779
- OutputSchema extends Schema.Top,
4780
- Instructions,
4781
- Tools extends Record<string, Tool.Any>,
4782
- Provider,
4783
- ModelProvides,
4784
- ModelRequires,
4785
- InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
4786
- InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
4787
- RunDispositionValue extends
4788
- | RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
4789
- | undefined = undefined,
4790
- InputPromptValue extends InputPromptSource<InputSchema["Type"], unknown, unknown> | undefined =
4791
- undefined,
4792
- UpdatesSchema extends Schema.Top | undefined = undefined,
4793
- >(
4794
- agent: RuntimeBinding<
4795
- InputSchema,
4796
- OutputSchema,
4769
+ const runModel = Effect.fnUntraced(
4770
+ /**
4771
+ * Drive the shared interpreter Effect with history rebuilt by the run journal, commit
4772
+ * each Turn under the local owner, and watch for durable abort intent. Engine Run failures settle `failed`; coordinator failures abort the
4773
+ * Attempt cleanly with the obligation still owed.
4774
+ *
4775
+ * The interpreter supplies validated response, closed Tool-result, and completion facts.
4776
+ * Responses precede approval and effectful dispatch; readonly responses may join their closed
4777
+ * results. Tool results precede input drains and the next Turn. A no-tool response and Run
4778
+ * completion share one batch. A completion Tool's result
4779
+ * precedes its terminal-only completion batch, so recovery can repeat a pure output projection
4780
+ * without replaying the handler. Pending batches resume from their canonical declarations.
4781
+ */
4782
+ function* <
4783
+ InputSchema extends Schema.Top,
4784
+ OutputSchema extends Schema.Top,
4797
4785
  Instructions,
4798
- Tools,
4786
+ Tools extends Record<string, Tool.Any>,
4799
4787
  Provider,
4800
4788
  ModelProvides,
4801
4789
  ModelRequires,
4802
- InstructionError,
4803
- InstructionRequirements,
4804
- RunDispositionValue,
4805
- InputPromptValue,
4806
- UpdatesSchema
4807
- >,
4808
- ctx: AttemptAppendContext,
4809
- submission: SubmissionSnapshot,
4810
- session: RunStorageSession,
4811
- records: ReadonlyArray<CanonicalRecordEnvelope>,
4812
- canonical: Stream.Stream<
4813
- JournalRecordEnvelope,
4814
- ThreadStoreError | ThreadNotMaterialized | RunJournalError
4815
- >,
4816
- initialJournal: InitialJournalSource | undefined,
4817
- canonicalThrough: CanonicalSequence,
4818
- progressThrough: CanonicalSequence | undefined,
4819
- continuation: RunContinuation | undefined,
4820
- priorContext: RunJournalContext | undefined,
4821
- journalMetadata: JournalMetadata | undefined,
4822
- lineage: AttemptLineage,
4823
- approvalDecisions: ReadonlyArray<ApprovalDecisionIntent>,
4824
- currentContracts: Readonly<Record<string, Digest>>,
4825
- runTiming: RunTiming,
4826
- yieldAfter?: DateTime.Utc,
4827
- ) =>
4828
- Effect.gen(function* () {
4790
+ InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
4791
+ InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
4792
+ RunDispositionValue extends
4793
+ | RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
4794
+ | undefined = undefined,
4795
+ InputPromptValue extends
4796
+ | InputPromptSource<InputSchema["Type"], unknown, unknown>
4797
+ | undefined = undefined,
4798
+ UpdatesSchema extends Schema.Top | undefined = undefined,
4799
+ >(
4800
+ agent: RuntimeBinding<
4801
+ InputSchema,
4802
+ OutputSchema,
4803
+ Instructions,
4804
+ Tools,
4805
+ Provider,
4806
+ ModelProvides,
4807
+ ModelRequires,
4808
+ InstructionError,
4809
+ InstructionRequirements,
4810
+ RunDispositionValue,
4811
+ InputPromptValue,
4812
+ UpdatesSchema
4813
+ >,
4814
+ ctx: AttemptAppendContext,
4815
+ submission: SubmissionSnapshot,
4816
+ session: RunStorageSession,
4817
+ records: ReadonlyArray<CanonicalRecordEnvelope>,
4818
+ canonical: Stream.Stream<
4819
+ JournalRecordEnvelope,
4820
+ ThreadStoreError | ThreadNotMaterialized | RunJournalError
4821
+ >,
4822
+ initialJournal: InitialJournalSource | undefined,
4823
+ canonicalThrough: CanonicalSequence,
4824
+ progressThrough: CanonicalSequence | undefined,
4825
+ continuation: RunContinuation | undefined,
4826
+ priorContext: RunJournalContext | undefined,
4827
+ journalMetadata: JournalMetadata | undefined,
4828
+ lineage: AttemptLineage,
4829
+ approvalDecisions: ReadonlyArray<ApprovalDecisionIntent>,
4830
+ currentContracts: Readonly<Record<string, Digest>>,
4831
+ runTiming: RunTiming,
4832
+ yieldAfter?: DateTime.Utc,
4833
+ ) {
4829
4834
  const submissionId = submission.submissionId;
4830
4835
 
4831
4836
  const runId = runIdForSubmission(submissionId);
@@ -6065,9 +6070,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
6065
6070
  // The interpreter supplies only closed siblings, in declaration order.
6066
6071
  // Per-call identities retain each result before the child suspension.
6067
6072
  for (const result of toolResults) {
6068
- const batch = yield* withCrypto(
6069
- turnResultsBatch({ ...input, toolResults: [result] }),
6070
- );
6073
+ const batch = yield* turnResultsBatch({ ...input, toolResults: [result] });
6071
6074
 
6072
6075
  yield* appendBatch(
6073
6076
  ctx,
@@ -6100,47 +6103,45 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
6100
6103
  turnId: commit.turnId,
6101
6104
  // The journal owns encoded messages. Detach the remaining small metadata
6102
6105
  // trees too: Schema validation alone is not an ownership transfer.
6103
- batch: yield* withCrypto(
6104
- turnCanonicalBatch({
6105
- ...input,
6106
- toolParameterRejections: input.toolParameterRejections?.map((rejection) => ({
6107
- ...rejection,
6108
- parameters: copyJson(rejection.parameters),
6109
- error: { ...rejection.error, reason: { ...rejection.error.reason } },
6110
- })),
6111
- toolExposure:
6112
- input.toolExposure === undefined
6113
- ? undefined
6114
- : Snapshot.make({
6115
- exposedToolNames: [...input.toolExposure.exposedToolNames],
6116
- ...(input.toolExposure.selection === undefined
6117
- ? {}
6118
- : {
6119
- selection: Selection.make({
6120
- toolNames: [...input.toolExposure.selection.toolNames],
6121
- }),
6106
+ batch: yield* turnCanonicalBatch({
6107
+ ...input,
6108
+ toolParameterRejections: input.toolParameterRejections?.map((rejection) => ({
6109
+ ...rejection,
6110
+ parameters: copyJson(rejection.parameters),
6111
+ error: { ...rejection.error, reason: { ...rejection.error.reason } },
6112
+ })),
6113
+ toolExposure:
6114
+ input.toolExposure === undefined
6115
+ ? undefined
6116
+ : Snapshot.make({
6117
+ exposedToolNames: [...input.toolExposure.exposedToolNames],
6118
+ ...(input.toolExposure.selection === undefined
6119
+ ? {}
6120
+ : {
6121
+ selection: Selection.make({
6122
+ toolNames: [...input.toolExposure.selection.toolNames],
6122
6123
  }),
6123
- }),
6124
- usage:
6125
- input.usage === undefined
6126
- ? undefined
6127
- : {
6128
- ...input.usage,
6129
- modelUsage: input.usage.modelUsage?.map((usage) =>
6130
- ModelCallUsage.make({
6131
- ...usage,
6132
- ...(usage.response === undefined
6133
- ? {}
6134
- : {
6135
- response: ModelResponseIdentity.make({ ...usage.response }),
6136
- }),
6137
- inputTokens: InputTokenUsage.make({ ...usage.inputTokens }),
6138
- outputTokens: OutputTokenUsage.make({ ...usage.outputTokens }),
6139
6124
  }),
6140
- ),
6141
- },
6142
- }),
6143
- ),
6125
+ }),
6126
+ usage:
6127
+ input.usage === undefined
6128
+ ? undefined
6129
+ : {
6130
+ ...input.usage,
6131
+ modelUsage: input.usage.modelUsage?.map((usage) =>
6132
+ ModelCallUsage.make({
6133
+ ...usage,
6134
+ ...(usage.response === undefined
6135
+ ? {}
6136
+ : {
6137
+ response: ModelResponseIdentity.make({ ...usage.response }),
6138
+ }),
6139
+ inputTokens: InputTokenUsage.make({ ...usage.inputTokens }),
6140
+ outputTokens: OutputTokenUsage.make({ ...usage.outputTokens }),
6141
+ }),
6142
+ ),
6143
+ },
6144
+ }),
6144
6145
  };
6145
6146
 
6146
6147
  const progress = progressWriters.get(ctx);
@@ -6163,17 +6164,15 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
6163
6164
  let batch: CanonicalBatch;
6164
6165
 
6165
6166
  if (deferred === undefined) {
6166
- batch = yield* withCrypto(
6167
- commit._tag === "Response"
6168
- ? turnResponseBatch(input)
6169
- : response === undefined
6170
- ? turnResultsBatch(input)
6171
- : turnCanonicalBatch(input),
6172
- );
6167
+ batch = yield* commit._tag === "Response"
6168
+ ? turnResponseBatch(input)
6169
+ : response === undefined
6170
+ ? turnResultsBatch(input)
6171
+ : turnCanonicalBatch(input);
6173
6172
  } else if (toolResults.length === 0 && runCompletion === undefined) {
6174
6173
  batch = deferred.batch;
6175
6174
  } else {
6176
- const results = yield* withCrypto(turnResultsBatch(input));
6175
+ const results = yield* turnResultsBatch(input);
6177
6176
 
6178
6177
  batch = CanonicalBatch.make({
6179
6178
  ...deferred.batch,
@@ -6347,214 +6346,208 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
6347
6346
  .get(ctx)
6348
6347
  ?.stageUsage(runId, terminalUsageCharge(uncommittedModelUsage()));
6349
6348
  }),
6350
- commitCompaction: (commit) =>
6351
- Effect.gen(function* () {
6352
- const canonicalTurn = commit.turn;
6353
- const recordId = compactionRecordId(runId, canonicalTurn, commit.kind);
6354
-
6355
- if (knownIds.has(recordId)) {
6356
- return yield* CompactionError.make({
6357
- message: "Compaction is already committed for this Turn and kind",
6358
- });
6359
- }
6349
+ commitCompaction: Effect.fnUntraced(function* (commit) {
6350
+ const canonicalTurn = commit.turn;
6351
+ const recordId = compactionRecordId(runId, canonicalTurn, commit.kind);
6360
6352
 
6361
- let sourceJournal = journal;
6362
- let sourceBoundaries = boundaries;
6353
+ if (knownIds.has(recordId)) {
6354
+ return yield* CompactionError.make({
6355
+ message: "Compaction is already committed for this Turn and kind",
6356
+ });
6357
+ }
6363
6358
 
6364
- if (initialJournal !== undefined) {
6365
- initialJournal.cacheable = false;
6366
- initialJournal.history = undefined;
6367
- }
6368
- if (commit.kind !== "summarize" || initialJournal?.prefix !== undefined) {
6369
- // Results must be canonical before pruning or rollover can cover them. Newly committed
6370
- // compactions remain overlays on this Attempt's append-only source, so omit those
6371
- // overlays while reconstructing the exact source-to-record mapping.
6372
- const tail = yield* ctx.tail;
6373
-
6374
- const source =
6375
- initialJournal === undefined
6376
- ? canonical
6377
- : Stream.fromIterable(
6378
- yield* recordHalt(
6379
- readInitialJournal(initialJournal.original, initialJournal.suffix),
6380
- ),
6381
- );
6359
+ let sourceJournal = journal;
6360
+ let sourceBoundaries = boundaries;
6382
6361
 
6383
- sourceBoundaries = [];
6384
- sourceJournal = yield* recordHalt(
6385
- projectRunJournalStream(
6386
- Stream.concat(
6387
- source,
6388
- Stream.fromIterable(
6389
- yield* recordHalt(
6390
- selectedRange(
6391
- ctx.threadId,
6392
- [submissionId],
6393
- tail.sequence,
6394
- canonicalThrough,
6395
- ),
6396
- ),
6362
+ if (initialJournal !== undefined) {
6363
+ initialJournal.cacheable = false;
6364
+ initialJournal.history = undefined;
6365
+ }
6366
+ if (commit.kind !== "summarize" || initialJournal?.prefix !== undefined) {
6367
+ // Results must be canonical before pruning or rollover can cover them. Newly committed
6368
+ // compactions remain overlays on this Attempt's append-only source, so omit those
6369
+ // overlays while reconstructing the exact source-to-record mapping.
6370
+ const tail = yield* ctx.tail;
6371
+
6372
+ const source =
6373
+ initialJournal === undefined
6374
+ ? canonical
6375
+ : Stream.fromIterable(
6376
+ yield* recordHalt(
6377
+ readInitialJournal(initialJournal.original, initialJournal.suffix),
6397
6378
  ),
6398
- ).pipe(
6399
- Stream.filter(
6400
- (envelope) =>
6401
- envelope.record.payload._tag !== "CompactionCreated" ||
6402
- envelope.sequence <= canonicalThrough,
6379
+ );
6380
+
6381
+ sourceBoundaries = [];
6382
+ sourceJournal = yield* recordHalt(
6383
+ projectRunJournalStream(
6384
+ Stream.concat(
6385
+ source,
6386
+ Stream.fromIterable(
6387
+ yield* recordHalt(
6388
+ selectedRange(ctx.threadId, [submissionId], tail.sequence, canonicalThrough),
6403
6389
  ),
6404
6390
  ),
6405
- runId,
6406
- (boundary) => sourceBoundaries.push(boundary),
6407
- priorContext,
6391
+ ).pipe(
6392
+ Stream.filter(
6393
+ (envelope) =>
6394
+ envelope.record.payload._tag !== "CompactionCreated" ||
6395
+ envelope.sequence <= canonicalThrough,
6396
+ ),
6408
6397
  ),
6409
- );
6410
- }
6411
-
6412
- // Summaries use the initial prior-Run boundaries; pruning and rollover also admit
6413
- // complete current-Run boundaries from the fresh canonical source above.
6414
- const coverable = sourceBoundaries.filter(
6415
- (boundary) =>
6416
- commit.kind !== "summarize" ||
6417
- ownerFirstSequence === undefined ||
6418
- boundary.sequence < ownerFirstSequence,
6398
+ runId,
6399
+ (boundary) => sourceBoundaries.push(boundary),
6400
+ priorContext,
6401
+ ),
6419
6402
  );
6403
+ }
6420
6404
 
6421
- if (coverable.length === 0) {
6422
- return yield* CompactionError.make({
6423
- message:
6424
- commit.kind === "rollover"
6425
- ? "Durable rollover requires complete canonical records"
6426
- : "Durable compaction requires eligible canonical records",
6427
- });
6428
- }
6405
+ // Summaries use the initial prior-Run boundaries; pruning and rollover also admit
6406
+ // complete current-Run boundaries from the fresh canonical source above.
6407
+ const coverable = sourceBoundaries.filter(
6408
+ (boundary) =>
6409
+ commit.kind !== "summarize" ||
6410
+ ownerFirstSequence === undefined ||
6411
+ boundary.sequence < ownerFirstSequence,
6412
+ );
6429
6413
 
6430
- const comparisonView = instructionView(
6431
- sourceJournal.prompt.content,
6432
- initialInstructions,
6433
- false,
6434
- sourceJournal.historyBefore.content.length,
6435
- );
6414
+ if (coverable.length === 0) {
6415
+ return yield* CompactionError.make({
6416
+ message:
6417
+ commit.kind === "rollover"
6418
+ ? "Durable rollover requires complete canonical records"
6419
+ : "Durable compaction requires eligible canonical records",
6420
+ });
6421
+ }
6436
6422
 
6437
- // Compare each visible message once. A transformed source can authorize only its
6438
- // exact canonical prefix; no per-candidate rereads or full encoded Prompt copies.
6439
- let matchingPrefix = 0;
6440
- let requiredThrough = 0;
6441
- const comparisonLimit = Math.min(comparisonView.messages.length, commit.through);
6423
+ const comparisonView = instructionView(
6424
+ sourceJournal.prompt.content,
6425
+ initialInstructions,
6426
+ false,
6427
+ sourceJournal.historyBefore.content.length,
6428
+ );
6442
6429
 
6443
- for (let index = 0; index < commit.through; index += 1) {
6444
- const message = commit.source.content[index];
6430
+ // Compare each visible message once. A transformed source can authorize only its
6431
+ // exact canonical prefix; no per-candidate rereads or full encoded Prompt copies.
6432
+ let matchingPrefix = 0;
6433
+ let requiredThrough = 0;
6434
+ const comparisonLimit = Math.min(comparisonView.messages.length, commit.through);
6445
6435
 
6446
- if (
6447
- message !== undefined &&
6448
- (commit.kind === "clear-tool-results"
6449
- ? message.role === "tool"
6450
- : message.role !== "system")
6451
- )
6452
- requiredThrough = index + 1;
6453
- if (index >= comparisonLimit || index !== matchingPrefix || message === undefined)
6454
- continue;
6455
- const canonicalMessage = comparisonView.messages[index];
6456
-
6457
- if (canonicalMessage === undefined) continue;
6458
-
6459
- // Preserve Prompt's JSON projection while ignoring persisted object-key order.
6460
- const encode = (entry: Prompt.Message) =>
6461
- encodeCompactionMessageJson(entry).pipe(
6462
- Effect.flatMap(decodeCompactionMessageJson),
6463
- Effect.map(canonicalJson),
6464
- Effect.mapError((cause) =>
6465
- CompactionError.make({
6466
- message: "Could not encode the canonical compaction prefix",
6467
- cause,
6468
- }),
6469
- ),
6470
- );
6436
+ for (let index = 0; index < commit.through; index += 1) {
6437
+ const message = commit.source.content[index];
6471
6438
 
6472
- const left = yield* encode(canonicalMessage);
6473
- const right = yield* encode(message);
6439
+ if (
6440
+ message !== undefined &&
6441
+ (commit.kind === "clear-tool-results"
6442
+ ? message.role === "tool"
6443
+ : message.role !== "system")
6444
+ )
6445
+ requiredThrough = index + 1;
6446
+ if (index >= comparisonLimit || index !== matchingPrefix || message === undefined)
6447
+ continue;
6448
+ const canonicalMessage = comparisonView.messages[index];
6449
+
6450
+ if (canonicalMessage === undefined) continue;
6451
+
6452
+ // Preserve Prompt's JSON projection while ignoring persisted object-key order.
6453
+ const encode = (entry: Prompt.Message) =>
6454
+ encodeCompactionMessageJson(entry).pipe(
6455
+ Effect.flatMap(decodeCompactionMessageJson),
6456
+ Effect.map(canonicalJson),
6457
+ Effect.mapError((cause) =>
6458
+ CompactionError.make({
6459
+ message: "Could not encode the canonical compaction prefix",
6460
+ cause,
6461
+ }),
6462
+ ),
6463
+ );
6474
6464
 
6475
- if (left === right) matchingPrefix += 1;
6476
- }
6465
+ const left = yield* encode(canonicalMessage);
6466
+ const right = yield* encode(message);
6477
6467
 
6478
- let lastCovered: JournalBoundary | undefined;
6468
+ if (left === right) matchingPrefix += 1;
6469
+ }
6479
6470
 
6480
- for (let index = 0; index < coverable.length; index += 1) {
6481
- const candidate = coverable[index];
6471
+ let lastCovered: JournalBoundary | undefined;
6482
6472
 
6483
- if (candidate === undefined) continue;
6484
- const following = coverable[index + 1];
6473
+ for (let index = 0; index < coverable.length; index += 1) {
6474
+ const candidate = coverable[index];
6485
6475
 
6486
- if (following?.tag === "ToolCallSettled") continue;
6476
+ if (candidate === undefined) continue;
6477
+ const following = coverable[index + 1];
6487
6478
 
6488
- const length = comparisonView.prefixLength(candidate.promptLength);
6479
+ if (following?.tag === "ToolCallSettled") continue;
6489
6480
 
6490
- if (length === 0 || length > commit.through) continue;
6491
- if (length < requiredThrough || length > matchingPrefix) continue;
6492
- lastCovered = candidate;
6493
- }
6494
- if (lastCovered === undefined) {
6495
- return yield* CompactionError.make({
6496
- message:
6497
- commit.kind === "rollover"
6498
- ? "Rollover coverage cannot be mapped to complete canonical records"
6499
- : "Compaction coverage cannot be mapped to complete canonical records",
6500
- });
6501
- }
6502
- const coveredSequence = lastCovered.sequence;
6481
+ const length = comparisonView.prefixLength(candidate.promptLength);
6503
6482
 
6504
- if (
6505
- commit.kind !== "summarize" &&
6506
- sourceBoundaries.some(
6507
- (boundary) =>
6508
- boundary.incomplete === true &&
6509
- boundary.terminalPriorRun !== true &&
6510
- boundary.sequence <= coveredSequence,
6511
- )
6512
- ) {
6513
- return yield* CompactionError.make({
6514
- message: "Compaction cannot cover an incomplete Tool batch",
6515
- });
6516
- }
6517
- if (commit.kind === "summarize" && (commit.summary ?? "").trim().length === 0) {
6518
- return yield* CompactionError.make({
6519
- message: "A summarize compaction commit carried no summary",
6520
- });
6521
- }
6483
+ if (length === 0 || length > commit.through) continue;
6484
+ if (length < requiredThrough || length > matchingPrefix) continue;
6485
+ lastCovered = candidate;
6486
+ }
6487
+ if (lastCovered === undefined) {
6488
+ return yield* CompactionError.make({
6489
+ message:
6490
+ commit.kind === "rollover"
6491
+ ? "Rollover coverage cannot be mapped to complete canonical records"
6492
+ : "Compaction coverage cannot be mapped to complete canonical records",
6493
+ });
6494
+ }
6495
+ const coveredSequence = lastCovered.sequence;
6522
6496
 
6523
- const payload = yield* Schema.decodeEffect(CompactionCreated)({
6524
- _tag: "CompactionCreated",
6525
- runId,
6526
- turn: canonicalTurn,
6527
- kind: commit.kind,
6528
- coversThrough: lastCovered.sequence,
6529
- ...(commit.kind === "summarize" ? { summary: commit.summary } : {}),
6530
- ...(commit.kind === "rollover" && commit.handoff !== undefined
6531
- ? { handoff: commit.handoff }
6532
- : {}),
6533
- }).pipe(
6534
- Effect.mapError((cause) =>
6535
- CompactionError.make({
6536
- message: "Compaction decision exceeds canonical persistence bounds",
6537
- cause,
6538
- }),
6539
- ),
6540
- );
6497
+ if (
6498
+ commit.kind !== "summarize" &&
6499
+ sourceBoundaries.some(
6500
+ (boundary) =>
6501
+ boundary.incomplete === true &&
6502
+ boundary.terminalPriorRun !== true &&
6503
+ boundary.sequence <= coveredSequence,
6504
+ )
6505
+ ) {
6506
+ return yield* CompactionError.make({
6507
+ message: "Compaction cannot cover an incomplete Tool batch",
6508
+ });
6509
+ }
6510
+ if (commit.kind === "summarize" && (commit.summary ?? "").trim().length === 0) {
6511
+ return yield* CompactionError.make({
6512
+ message: "A summarize compaction commit carried no summary",
6513
+ });
6514
+ }
6515
+
6516
+ const payload = yield* Schema.decodeEffect(CompactionCreated)({
6517
+ _tag: "CompactionCreated",
6518
+ runId,
6519
+ turn: canonicalTurn,
6520
+ kind: commit.kind,
6521
+ coversThrough: lastCovered.sequence,
6522
+ ...(commit.kind === "summarize" ? { summary: commit.summary } : {}),
6523
+ ...(commit.kind === "rollover" && commit.handoff !== undefined
6524
+ ? { handoff: commit.handoff }
6525
+ : {}),
6526
+ }).pipe(
6527
+ Effect.mapError((cause) =>
6528
+ CompactionError.make({
6529
+ message: "Compaction decision exceeds canonical persistence bounds",
6530
+ cause,
6531
+ }),
6532
+ ),
6533
+ );
6541
6534
 
6542
- const envelope = yield* recordHalt(makeEnvelope(recordId, payload));
6535
+ const envelope = yield* recordHalt(makeEnvelope(recordId, payload));
6543
6536
 
6544
- yield* recordHalt(hit("compaction:before-canonical-append"));
6545
- yield* recordHalt(
6546
- appendBatch(
6547
- ctx,
6548
- CanonicalBatch.make({
6549
- batchId: compactionBatchId(runId, canonicalTurn, commit.kind),
6550
- producerId: config.producerId,
6551
- records: [envelope],
6552
- }),
6553
- ),
6554
- );
6555
- knownIds.add(recordId);
6556
- yield* recordHalt(hit("compaction:after-canonical-append"));
6557
- }),
6537
+ yield* recordHalt(hit("compaction:before-canonical-append"));
6538
+ yield* recordHalt(
6539
+ appendBatch(
6540
+ ctx,
6541
+ CanonicalBatch.make({
6542
+ batchId: compactionBatchId(runId, canonicalTurn, commit.kind),
6543
+ producerId: config.producerId,
6544
+ records: [envelope],
6545
+ }),
6546
+ ),
6547
+ );
6548
+ knownIds.add(recordId);
6549
+ yield* recordHalt(hit("compaction:after-canonical-append"));
6550
+ }),
6558
6551
  };
6559
6552
 
6560
6553
  /** Digest of one declared call's canonical encoded parameters (the original normalized wire). */
@@ -6803,37 +6796,34 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
6803
6796
  * so the next Turn's response commit makes it model-visible canonically — exactly the
6804
6797
  * prompt-coverage rule recovery relies on.
6805
6798
  */
6806
- const renderJoinedInput = ({
6807
- input: encodedInput,
6808
- messageAdmission,
6809
- }: Pick<UserInputRecorded, "input" | "messageAdmission">) =>
6810
- Effect.gen(function* () {
6811
- if (Schema.is(FrameworkMessage)(messageAdmission))
6812
- return yield* Schema.encodeEffect(Schema.fromJsonString(FrameworkMessage))(
6813
- messageAdmission,
6814
- ).pipe(
6815
- Effect.mapError(() =>
6816
- AgentInputError.make({ message: "Invalid worker completion message" }),
6817
- ),
6818
- );
6819
- const inputPrompt = agent.definition.inputPrompt;
6820
-
6821
- if (inputPrompt === undefined) {
6822
- return yield* renderInputPrompt(undefined, encodedInput, encodedInput);
6823
- }
6824
-
6825
- const decodedInput = yield* Schema.decodeEffect(agent.definition.input)(
6826
- encodedInput,
6799
+ const renderJoinedInput = Effect.fnUntraced(function* (
6800
+ encodedInput: UserInputRecorded["input"],
6801
+ messageAdmission: UserInputRecorded["messageAdmission"],
6802
+ ) {
6803
+ if (Schema.is(FrameworkMessage)(messageAdmission))
6804
+ return yield* Schema.encodeEffect(Schema.fromJsonString(FrameworkMessage))(
6805
+ messageAdmission,
6827
6806
  ).pipe(
6828
- Effect.mapError((cause) =>
6829
- AgentInputDecodeError.make({
6830
- message: cause.message,
6831
- }),
6807
+ Effect.mapError(() =>
6808
+ AgentInputError.make({ message: "Invalid worker completion message" }),
6832
6809
  ),
6833
6810
  );
6811
+ const inputPrompt = agent.definition.inputPrompt;
6834
6812
 
6835
- return yield* renderInputPrompt(inputPrompt, decodedInput, encodedInput);
6836
- });
6813
+ if (inputPrompt === undefined) {
6814
+ return yield* renderInputPrompt(undefined, encodedInput, encodedInput);
6815
+ }
6816
+
6817
+ const decodedInput = yield* Schema.decodeEffect(agent.definition.input)(encodedInput).pipe(
6818
+ Effect.mapError((cause) =>
6819
+ AgentInputDecodeError.make({
6820
+ message: cause.message,
6821
+ }),
6822
+ ),
6823
+ );
6824
+
6825
+ return yield* renderInputPrompt(inputPrompt, decodedInput, encodedInput);
6826
+ });
6837
6827
 
6838
6828
  // Claims are authority, wakes are hints. A cancelled waiter retains every claim
6839
6829
  // for the seam drain; it never appends input while an old response can still commit.
@@ -6892,7 +6882,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
6892
6882
  : { messageAdmission: snapshot.submission.messageAdmission }),
6893
6883
  };
6894
6884
 
6895
- const rendered = yield* Effect.result(renderJoinedInput(input));
6885
+ const rendered = yield* Effect.result(
6886
+ renderJoinedInput(input.input, input.messageAdmission),
6887
+ );
6896
6888
 
6897
6889
  if (Result.isFailure(rendered)) {
6898
6890
  // A rejected prompt and its suffix retain their own Runs in queue order.
@@ -6932,118 +6924,117 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
6932
6924
  Agent.DefinitionRequirements<(typeof agent)["definition"]>
6933
6925
  > = {
6934
6926
  awaitJoin,
6935
- drain: (policy) =>
6936
- Effect.gen(function* () {
6937
- const joinedInputs = yield* recordHalt(
6938
- Effect.gen(function* () {
6939
- const limit = policy === "one" ? 1 : MAX_JOIN_DRAIN;
6927
+ drain: Effect.fnUntraced(function* (policy) {
6928
+ const joinedInputs = yield* recordHalt(
6929
+ Effect.gen(function* () {
6930
+ const limit = policy === "one" ? 1 : MAX_JOIN_DRAIN;
6940
6931
 
6941
- const joinedInputs: Array<
6942
- Pick<UserInputRecorded, "input" | "messageAdmission"> & {
6943
- readonly rendered?: Prompt.RawInput;
6944
- }
6945
- > = [];
6932
+ const joinedInputs: Array<
6933
+ Pick<UserInputRecorded, "input" | "messageAdmission"> & {
6934
+ readonly rendered?: Prompt.RawInput;
6935
+ }
6936
+ > = [];
6946
6937
 
6947
- if (joinBacklog === undefined) {
6948
- const hostSnapshot = yield* ledger.loadRecoverySnapshot(
6949
- RecoverySnapshotRequest.make({ submissionId }),
6950
- );
6938
+ if (joinBacklog === undefined) {
6939
+ const hostSnapshot = yield* ledger.loadRecoverySnapshot(
6940
+ RecoverySnapshotRequest.make({ submissionId }),
6941
+ );
6951
6942
 
6952
- joinBacklog = hostSnapshot.joins;
6943
+ joinBacklog = hostSnapshot.joins;
6944
+ }
6945
+ // These inputs were already consumed by an earlier attempt. Restore the
6946
+ // entire uncovered prompt; only fresh claims are subject to the drain bound.
6947
+ for (const join of joinBacklog) {
6948
+ const joinId = join.submissionId;
6949
+
6950
+ if (deliveredJoinInputs.has(joinId)) continue;
6951
+ if (join.state !== "joining" && join.state !== "joined") continue;
6952
+ const existing = joinedInputEnvelopes.get(joinId);
6953
+
6954
+ if (existing === undefined) continue;
6955
+ if (join.state === "joining") {
6956
+ // Canonical input without its joined marker (crash between the append and
6957
+ // `markJoined`): repair the marker from history before reattaching.
6958
+ yield* session.markJoined(joinId, {
6959
+ recordId: existing.record.recordId,
6960
+ sequence: existing.sequence,
6961
+ });
6953
6962
  }
6954
- // These inputs were already consumed by an earlier attempt. Restore the
6955
- // entire uncovered prompt; only fresh claims are subject to the drain bound.
6956
- for (const join of joinBacklog) {
6957
- const joinId = join.submissionId;
6958
-
6959
- if (deliveredJoinInputs.has(joinId)) continue;
6960
- if (join.state !== "joining" && join.state !== "joined") continue;
6961
- const existing = joinedInputEnvelopes.get(joinId);
6962
-
6963
- if (existing === undefined) continue;
6964
- if (join.state === "joining") {
6965
- // Canonical input without its joined marker (crash between the append and
6966
- // `markJoined`): repair the marker from history before reattaching.
6967
- yield* session.markJoined(joinId, {
6968
- recordId: existing.record.recordId,
6969
- sequence: existing.sequence,
6970
- });
6971
- }
6972
- deliveredJoinInputs.add(joinId);
6973
- if (
6974
- lastHostResponseSequence !== undefined &&
6975
- lastHostResponseSequence > existing.sequence
6976
- ) {
6977
- continue;
6978
- }
6979
- const payload = existing.record.payload;
6980
-
6981
- if (payload._tag !== "UserInputRecorded") continue;
6982
- joinedInputs.push(payload);
6963
+ deliveredJoinInputs.add(joinId);
6964
+ if (
6965
+ lastHostResponseSequence !== undefined &&
6966
+ lastHostResponseSequence > existing.sequence
6967
+ ) {
6968
+ continue;
6983
6969
  }
6984
- const claims = yield* prepareInputs(limit);
6985
-
6986
- pendingJoinClaims.splice(0, claims.length);
6987
- for (const { claim, input: payload, rendered } of claims) {
6988
- const recordId = submissionInputRecordId(claim.submissionId);
6989
- let sequence: CanonicalSequence;
6990
- const existing = joinedInputEnvelopes.get(claim.submissionId);
6991
-
6992
- if (existing !== undefined) {
6993
- // Defensive reattach: the exact record is already canonical, so only the
6994
- // marker and the delivery remain (DUR-016 — never a duplicate append).
6995
- sequence = existing.sequence;
6996
- } else {
6997
- const envelope = yield* makeEnvelope(
6998
- recordId,
6999
- UserInputRecorded.make({
7000
- submissionId: claim.submissionId,
7001
- kind: "steering",
7002
- runId,
7003
- input: claim.inputPayload,
7004
- ...(payload.messageAdmission === undefined
7005
- ? {}
7006
- : { messageAdmission: payload.messageAdmission }),
7007
- }),
7008
- );
6970
+ const payload = existing.record.payload;
7009
6971
 
7010
- const result = yield* appendBatch(
7011
- ctx,
7012
- CanonicalBatch.make({
7013
- batchId: submissionInputBatchId(claim.submissionId),
7014
- producerId: config.producerId,
7015
- records: [envelope],
7016
- }),
7017
- );
6972
+ if (payload._tag !== "UserInputRecorded") continue;
6973
+ joinedInputs.push(payload);
6974
+ }
6975
+ const claims = yield* prepareInputs(limit);
6976
+
6977
+ pendingJoinClaims.splice(0, claims.length);
6978
+ for (const { claim, input: payload, rendered } of claims) {
6979
+ const recordId = submissionInputRecordId(claim.submissionId);
6980
+ let sequence: CanonicalSequence;
6981
+ const existing = joinedInputEnvelopes.get(claim.submissionId);
6982
+
6983
+ if (existing !== undefined) {
6984
+ // Defensive reattach: the exact record is already canonical, so only the
6985
+ // marker and the delivery remain (DUR-016 — never a duplicate append).
6986
+ sequence = existing.sequence;
6987
+ } else {
6988
+ const envelope = yield* makeEnvelope(
6989
+ recordId,
6990
+ UserInputRecorded.make({
6991
+ submissionId: claim.submissionId,
6992
+ kind: "steering",
6993
+ runId,
6994
+ input: claim.inputPayload,
6995
+ ...(payload.messageAdmission === undefined
6996
+ ? {}
6997
+ : { messageAdmission: payload.messageAdmission }),
6998
+ }),
6999
+ );
7018
7000
 
7019
- sequence = result.firstSequence;
7020
- knownIds.add(recordId);
7021
- yield* hit("join:after-canonical-append");
7022
- }
7023
- yield* session.markJoined(claim.submissionId, { recordId, sequence });
7024
- deliveredJoinInputs.add(claim.submissionId);
7025
- joinedInputs.push({
7026
- ...payload,
7027
- rendered,
7028
- });
7001
+ const result = yield* appendBatch(
7002
+ ctx,
7003
+ CanonicalBatch.make({
7004
+ batchId: submissionInputBatchId(claim.submissionId),
7005
+ producerId: config.producerId,
7006
+ records: [envelope],
7007
+ }),
7008
+ );
7009
+
7010
+ sequence = result.firstSequence;
7011
+ knownIds.add(recordId);
7012
+ yield* hit("join:after-canonical-append");
7029
7013
  }
7014
+ yield* session.markJoined(claim.submissionId, { recordId, sequence });
7015
+ deliveredJoinInputs.add(claim.submissionId);
7016
+ joinedInputs.push({
7017
+ ...payload,
7018
+ rendered,
7019
+ });
7020
+ }
7030
7021
 
7031
- return joinedInputs;
7032
- }),
7033
- );
7022
+ return joinedInputs;
7023
+ }),
7024
+ );
7034
7025
 
7035
- return yield* Effect.forEach(joinedInputs, (joinedInput) =>
7036
- (joinedInput.rendered === undefined
7037
- ? renderJoinedInput(joinedInput)
7038
- : Effect.succeed(joinedInput.rendered)
7039
- ).pipe(
7040
- Effect.map((input): RunInputCommand => ({
7041
- kind: "steering",
7042
- input,
7043
- })),
7044
- ),
7045
- );
7046
- }),
7026
+ return yield* Effect.forEach(joinedInputs, (joinedInput) =>
7027
+ (joinedInput.rendered === undefined
7028
+ ? renderJoinedInput(joinedInput.input, joinedInput.messageAdmission)
7029
+ : Effect.succeed(joinedInput.rendered)
7030
+ ).pipe(
7031
+ Effect.map((input): RunInputCommand => ({
7032
+ kind: "steering",
7033
+ input,
7034
+ })),
7035
+ ),
7036
+ );
7037
+ }),
7047
7038
  };
7048
7039
 
7049
7040
  /**
@@ -7726,50 +7717,49 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
7726
7717
  },
7727
7718
  }),
7728
7719
  ...(preparedContext === undefined ? {} : { context: preparedContext }),
7729
- beforeTurn: () =>
7730
- Effect.gen(function* () {
7720
+ beforeTurn: Effect.fnUntraced(function* () {
7721
+ if (
7722
+ yieldAfter !== undefined &&
7723
+ (yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(yieldAfter)
7724
+ ) {
7725
+ yield* Deferred.succeed(yieldSignal, undefined);
7726
+
7727
+ // The successful signal wins the outer race and closes the execution Scope;
7728
+ // no synthetic engine RunFailed event or terminal Settlement is produced.
7729
+ return yield* Effect.never;
7730
+ }
7731
+ if (submissionScheduling.yieldTo !== undefined && (yield* Ref.get(turnCounter)) > 0) {
7732
+ const candidate = yield* recordHalt(
7733
+ eligibleThreadHead(submission.threadId, submission.queueSequence),
7734
+ );
7735
+
7731
7736
  if (
7732
- yieldAfter !== undefined &&
7733
- (yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(yieldAfter)
7737
+ Option.isSome(candidate) &&
7738
+ (candidate.value.state === "ready" || candidate.value.state === "input-applied")
7734
7739
  ) {
7735
- yield* Deferred.succeed(yieldSignal, undefined);
7736
-
7737
- // The successful signal wins the outer race and closes the execution Scope;
7738
- // no synthetic engine RunFailed event or terminal Settlement is produced.
7739
- return yield* Effect.never;
7740
- }
7741
- if (submissionScheduling.yieldTo !== undefined && (yield* Ref.get(turnCounter)) > 0) {
7742
- const candidate = yield* recordHalt(
7743
- eligibleThreadHead(submission.threadId, submission.queueSequence),
7740
+ const next = yield* recordHalt(
7741
+ ledger.lookup(
7742
+ SubmissionLookupById.make({
7743
+ submissionId: candidate.value.submissionId,
7744
+ }),
7745
+ ),
7744
7746
  );
7745
7747
 
7746
7748
  if (
7747
- Option.isSome(candidate) &&
7748
- (candidate.value.state === "ready" || candidate.value.state === "input-applied")
7749
+ Option.isSome(next) &&
7750
+ next.value.agentId === submission.agentId &&
7751
+ (next.value.state === "ready" || next.value.state === "input-applied") &&
7752
+ (yield* recordHalt(
7753
+ submissionScheduling.yieldTo({ active: submission, next: next.value }),
7754
+ ))
7749
7755
  ) {
7750
- const next = yield* recordHalt(
7751
- ledger.lookup(
7752
- SubmissionLookupById.make({
7753
- submissionId: candidate.value.submissionId,
7754
- }),
7755
- ),
7756
- );
7756
+ yield* Deferred.succeed(yieldSignal, next.value.submissionId);
7757
7757
 
7758
- if (
7759
- Option.isSome(next) &&
7760
- next.value.agentId === submission.agentId &&
7761
- (next.value.state === "ready" || next.value.state === "input-applied") &&
7762
- (yield* recordHalt(
7763
- submissionScheduling.yieldTo({ active: submission, next: next.value }),
7764
- ))
7765
- ) {
7766
- yield* Deferred.succeed(yieldSignal, next.value.submissionId);
7767
-
7768
- return yield* Effect.never;
7769
- }
7758
+ return yield* Effect.never;
7770
7759
  }
7771
7760
  }
7772
- }),
7761
+ }
7762
+ }),
7773
7763
  ...(runContextPreparation.transientContext === undefined
7774
7764
  ? {}
7775
7765
  : { transientContext: runContextPreparation.transientContext }),
@@ -8112,7 +8102,8 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8112
8102
  };
8113
8103
 
8114
8104
  return completed;
8115
- });
8105
+ },
8106
+ );
8116
8107
 
8117
8108
  /**
8118
8109
  * One ownership period over the claimed lane head. The step order mirrors the pure recovery
@@ -8121,43 +8112,44 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8121
8112
  * (Unknown Outcomes, or a durable approval suspension) — no settlement occurs and the
8122
8113
  * obligation stays owed.
8123
8114
  */
8124
- const runAttempt = <
8125
- InputSchema extends Schema.Top,
8126
- OutputSchema extends Schema.Top,
8127
- Instructions,
8128
- Tools extends Record<string, Tool.Any>,
8129
- Provider,
8130
- ModelProvides,
8131
- ModelRequires,
8132
- InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
8133
- InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
8134
- RunDispositionValue extends
8135
- | RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
8136
- | undefined = undefined,
8137
- InputPromptValue extends InputPromptSource<InputSchema["Type"], unknown, unknown> | undefined =
8138
- undefined,
8139
- UpdatesSchema extends Schema.Top | undefined = undefined,
8140
- >(
8141
- registeredAgent: RuntimeBinding<
8142
- InputSchema,
8143
- OutputSchema,
8115
+ const runAttempt = Effect.fnUntraced(
8116
+ function* <
8117
+ InputSchema extends Schema.Top,
8118
+ OutputSchema extends Schema.Top,
8144
8119
  Instructions,
8145
- Tools,
8120
+ Tools extends Record<string, Tool.Any>,
8146
8121
  Provider,
8147
8122
  ModelProvides,
8148
8123
  ModelRequires,
8149
- InstructionError,
8150
- InstructionRequirements,
8151
- RunDispositionValue,
8152
- InputPromptValue,
8153
- UpdatesSchema
8154
- >,
8155
- session: RunStorageSession,
8156
- resumeAfterRetention: () => void,
8157
- onHandoff: (nextSubmissionId: SubmissionId) => void,
8158
- yieldAfter?: DateTime.Utc,
8159
- ) =>
8160
- Effect.gen(function* () {
8124
+ InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
8125
+ InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
8126
+ RunDispositionValue extends
8127
+ | RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
8128
+ | undefined = undefined,
8129
+ InputPromptValue extends
8130
+ | InputPromptSource<InputSchema["Type"], unknown, unknown>
8131
+ | undefined = undefined,
8132
+ UpdatesSchema extends Schema.Top | undefined = undefined,
8133
+ >(
8134
+ registeredAgent: RuntimeBinding<
8135
+ InputSchema,
8136
+ OutputSchema,
8137
+ Instructions,
8138
+ Tools,
8139
+ Provider,
8140
+ ModelProvides,
8141
+ ModelRequires,
8142
+ InstructionError,
8143
+ InstructionRequirements,
8144
+ RunDispositionValue,
8145
+ InputPromptValue,
8146
+ UpdatesSchema
8147
+ >,
8148
+ session: RunStorageSession,
8149
+ resumeAfterRetention: () => void,
8150
+ onHandoff: (nextSubmissionId: SubmissionId) => void,
8151
+ yieldAfter?: DateTime.Utc,
8152
+ ) {
8161
8153
  // Recheck ownership outside the disposable cache's failure recovery.
8162
8154
  yield* session.checkActive;
8163
8155
 
@@ -8273,9 +8265,8 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8273
8265
  return metadata;
8274
8266
  };
8275
8267
 
8276
- let records: ReadonlyArray<CanonicalRecordEnvelope> = yield* Stream.runCollect(
8277
- initialView.canonical.pipe(Stream.filter(collectControl)),
8278
- );
8268
+ let records: ReadonlyArray<CanonicalRecordEnvelope> =
8269
+ initialView.records.filter(collectControl);
8279
8270
 
8280
8271
  // The append-only prefix remains valid for this Attempt. Retain only this Run's control
8281
8272
  // evidence and validate each newly visible suffix against its own captured tail.
@@ -8382,6 +8373,8 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8382
8373
  submission,
8383
8374
  Duration.toMillis(agent.definition.policy.maxDuration),
8384
8375
  yield* refreshControl(),
8376
+ // Applying the input cannot change the Run start captured by this Attempt's owner.
8377
+ initialView.operational,
8385
8378
  ).pipe(Effect.provideService(CurrentRunWriter, ctx));
8386
8379
 
8387
8380
  const savedRunTiming = start.timing;
@@ -8625,7 +8618,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8625
8618
  let canonical: Stream.Stream<
8626
8619
  JournalRecordEnvelope,
8627
8620
  ThreadStoreError | ThreadNotMaterialized | RunJournalError
8628
- > = view.canonical;
8621
+ > = Stream.fromIterable(view.records);
8629
8622
 
8630
8623
  let initialJournal: InitialJournalSource | undefined;
8631
8624
 
@@ -8885,7 +8878,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8885
8878
 
8886
8879
  return Option.some(settlement);
8887
8880
  }
8888
- }).pipe(Effect.scoped);
8881
+ },
8882
+ (effect) => effect.pipe(Effect.scoped),
8883
+ );
8889
8884
 
8890
8885
  /** A missing current binding leaves both roots and children owed, with their claims released. */
8891
8886
  type CapturedWorkerBinding = Effect.Success<ReturnType<typeof makeLegacyWorkerBinding>>;
@@ -9094,7 +9089,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
9094
9089
  }
9095
9090
  }).pipe((body) => withProcessing(threadId, body));
9096
9091
 
9097
- const processThreadImpl = <
9092
+ const processThreadImpl = Effect.fnUntraced(function* <
9098
9093
  InputSchema extends Schema.Top,
9099
9094
  OutputSchema extends Schema.Top,
9100
9095
  Instructions,
@@ -9126,26 +9121,25 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
9126
9121
  UpdatesSchema
9127
9122
  >,
9128
9123
  threadId: ThreadId,
9129
- ) =>
9130
- Effect.gen(function* () {
9131
- // The legacy single-binding worker is a singleton resolver (plan §1.7): identity-exact —
9132
- // a claimed head with a different Agent never runs against this binding (the latent P4
9133
- // gap) — and digest-transparent, because this call site registers no digest authority.
9134
- const binding = yield* makeLegacyWorkerBinding(agent);
9135
-
9136
- return yield* drainThread(
9137
- (submission) =>
9138
- submission.agentId === binding.agentId
9139
- ? Effect.succeed(binding)
9140
- : Effect.fail(
9141
- BindingUnavailable.make({
9142
- agentId: submission.agentId,
9143
- message: `This worker is bound to Agent ${binding.agentId}; the claimed head belongs to Agent ${submission.agentId} (SUB-023)`,
9144
- }),
9145
- ),
9146
- threadId,
9147
- );
9148
- });
9124
+ ) {
9125
+ // The legacy single-binding worker is a singleton resolver (plan §1.7): identity-exact —
9126
+ // a claimed head with a different Agent never runs against this binding (the latent P4
9127
+ // gap) — and digest-transparent, because this call site registers no digest authority.
9128
+ const binding = yield* makeLegacyWorkerBinding(agent);
9129
+
9130
+ return yield* drainThread(
9131
+ (submission) =>
9132
+ submission.agentId === binding.agentId
9133
+ ? Effect.succeed(binding)
9134
+ : Effect.fail(
9135
+ BindingUnavailable.make({
9136
+ agentId: submission.agentId,
9137
+ message: `This worker is bound to Agent ${binding.agentId}; the claimed head belongs to Agent ${submission.agentId} (SUB-023)`,
9138
+ }),
9139
+ ),
9140
+ threadId,
9141
+ );
9142
+ });
9149
9143
 
9150
9144
  const processThreadResolvedImpl = (
9151
9145
  threadId: ThreadId,
@@ -10057,9 +10051,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
10057
10051
  const hostId = snapshot.hostSubmissionId;
10058
10052
  const hostView = yield* recoveryView(submission.threadId, history.throughSequence, [hostId]);
10059
10053
 
10060
- const hostRecords = yield* Stream.runCollect(
10061
- hostView.canonical.pipe(Stream.filter(controlRecords([hostId]))),
10062
- );
10054
+ const hostRecords = hostView.records.filter(controlRecords([hostId]));
10063
10055
 
10064
10056
  const merged = new Map(history.records.map((entry) => [entry.sequence, entry]));
10065
10057
 
@@ -11248,14 +11240,8 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
11248
11240
  }
11249
11241
  if (snapshot.value.state !== "settled") return Option.none();
11250
11242
 
11251
- // Finalization replay reads the canonical outcome without changing settledAt.
11252
- const settlement = yield* ledger.finalizeSettlement(
11253
- SettlementFinalization.make({
11254
- submissionId: receipt.submissionId,
11255
- settlementId: submissionSettlementId(receipt.submissionId),
11256
- }),
11257
- );
11258
-
11243
+ // Finalization replay reads the canonical outcome for adapters without finalized
11244
+ // metadata. Native projections already retain the first settledAt and exact record.
11259
11245
  const canonical = yield* getRecord({
11260
11246
  threadId: receipt.threadId,
11261
11247
  recordId: submissionSettlementRecordId(receipt.submissionId),
@@ -11278,6 +11264,40 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
11278
11264
  }
11279
11265
 
11280
11266
  const record = yield* settlementPayloadFromRecord(canonical.value.record, receipt.submissionId);
11267
+ const finalized = snapshot.value;
11268
+
11269
+ const retainedFinalization =
11270
+ finalized.settledAt !== undefined &&
11271
+ finalized.settledRecordId !== undefined &&
11272
+ finalized.settledOutcome !== undefined;
11273
+
11274
+ if (
11275
+ retainedFinalization &&
11276
+ (finalized.settledRecordId !== canonical.value.record.recordId ||
11277
+ finalized.settledOutcome !== record.outcome ||
11278
+ canonical.value.batchId !== submissionSettlementBatchId(receipt.submissionId))
11279
+ ) {
11280
+ return yield* LedgerError.make({
11281
+ operation: "awaitSettlement",
11282
+ message: "Settled projection disagrees with its canonical Settlement",
11283
+ });
11284
+ }
11285
+
11286
+ const settlement = retainedFinalization
11287
+ ? Settlement.make({
11288
+ submissionId: receipt.submissionId,
11289
+ settlementId: record.settlementId,
11290
+ receiptId: record.receiptId,
11291
+ outcome: record.outcome,
11292
+ ...(record.outcome === "failed" ? { failure: record.result } : {}),
11293
+ settledAt: finalized.settledAt,
11294
+ })
11295
+ : yield* ledger.finalizeSettlement(
11296
+ SettlementFinalization.make({
11297
+ submissionId: receipt.submissionId,
11298
+ settlementId: submissionSettlementId(receipt.submissionId),
11299
+ }),
11300
+ );
11281
11301
 
11282
11302
  if (
11283
11303
  record.receiptId !== receipt.receiptId ||