@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
@@ -119,8 +119,8 @@ export const makeEventSource = Effect.fnUntraced(function* <
119
119
  Effect.provideContext(services),
120
120
  );
121
121
 
122
- const parameters: EventSource["parameters"] = (value) =>
123
- Effect.gen(function* () {
122
+ const parameters: EventSource["parameters"] = Effect.fnUntraced(
123
+ function* (value) {
124
124
  yield* Schema.decodeUnknownEffect(PersistedJson)(value).pipe(Effect.mapError(invalid));
125
125
 
126
126
  const decoded = yield* Schema.decodeUnknownEffect(options.parameters)(value).pipe(
@@ -131,10 +131,12 @@ export const makeEventSource = Effect.fnUntraced(function* <
131
131
  parameters: yield* encode(options.parameters, decoded),
132
132
  matchingKey: options.parameterKey(decoded),
133
133
  };
134
- }).pipe(Effect.scoped, Effect.provideContext(services));
134
+ },
135
+ (effect) => effect.pipe(Effect.scoped, Effect.provideContext(services)),
136
+ );
135
137
 
136
- const matches: EventSource["matches"] = (event, subscription) =>
137
- Effect.gen(function* () {
138
+ const matches: EventSource["matches"] = Effect.fnUntraced(
139
+ function* (event, subscription) {
138
140
  const e = yield* Schema.decodeEffect(options.event)(event.payload).pipe(
139
141
  Effect.mapError(invalid),
140
142
  );
@@ -144,7 +146,9 @@ export const makeEventSource = Effect.fnUntraced(function* <
144
146
  ).pipe(Effect.mapError(invalid));
145
147
 
146
148
  return options.matches(e, p);
147
- }).pipe(Effect.scoped, Effect.provideContext(services));
149
+ },
150
+ (effect) => effect.pipe(Effect.scoped, Effect.provideContext(services)),
151
+ );
148
152
 
149
153
  const reconcile = options.reconcile;
150
154
 
@@ -157,8 +161,8 @@ export const makeEventSource = Effect.fnUntraced(function* <
157
161
  ...(reconcile === undefined
158
162
  ? {}
159
163
  : {
160
- reconcile: (subscription: SubscriptionRecord) =>
161
- Effect.gen(function* () {
164
+ reconcile: Effect.fnUntraced(
165
+ function* (subscription: SubscriptionRecord) {
162
166
  const p = yield* Schema.decodeEffect(options.parameters)(
163
167
  subscription.configuration.parameters,
164
168
  ).pipe(Effect.mapError(invalid));
@@ -166,7 +170,9 @@ export const makeEventSource = Effect.fnUntraced(function* <
166
170
  const event = yield* reconcile(p);
167
171
 
168
172
  return event === null ? null : yield* normalized(event);
169
- }).pipe(Effect.scoped, Effect.provideContext(services)),
173
+ },
174
+ (effect) => effect.pipe(Effect.scoped, Effect.provideContext(services)),
175
+ ),
170
176
  }),
171
177
  };
172
178
  });
@@ -45,7 +45,9 @@ export const decodeExportRecordJson = (
45
45
  // Both codecs validate RecordJson before this getter; decode the supported view once.
46
46
  const decodeView = SchemaGetter.transformEffect((value: RecordJson) =>
47
47
  decodeRecord(value).pipe(
48
- Effect.map((record) => new ExportedRecord({ ...record, wire: value }, { disableChecks: true })),
48
+ Effect.map((record) =>
49
+ ExportedRecord.make({ ...record, wire: value }, { disableChecks: true }),
50
+ ),
49
51
  Effect.mapError((error) => error.issue),
50
52
  ),
51
53
  );
@@ -104,8 +106,8 @@ export const ReadEnvelope = Schema.Struct({
104
106
  record: ReadRecord,
105
107
  }).pipe(
106
108
  Schema.decodeTo(Schema.toType(CanonicalRecordEnvelope), {
107
- decode: SchemaGetter.transform(
108
- (fields) => new CanonicalRecordEnvelope(fields, { disableChecks: true }),
109
+ decode: SchemaGetter.transform((fields) =>
110
+ CanonicalRecordEnvelope.make(fields, { disableChecks: true }),
109
111
  ),
110
112
  encode: SchemaGetter.transform((envelope) => envelope),
111
113
  }),
@@ -1,12 +1,12 @@
1
- import { Context, Crypto, DateTime, Effect, Option, Schema, Semaphore, Stream } from "effect";
1
+ import { Context, DateTime, Effect, Option, Result, Schema, Semaphore, Stream } from "effect";
2
2
  import { Prompt } from "effect/ai";
3
3
 
4
4
  import { AgentPersistenceCapacityError } from "../core/AgentError.ts";
5
5
  import type { RunId, SubmissionId, ThreadId } from "../core/Identifiers.ts";
6
6
  import { ToolCallId } from "../core/Identifiers.ts";
7
7
  import { utf8ByteLength } from "../core/internal/utf8.ts";
8
- import { summarizeModelUsage } from "../core/Usage.ts";
9
8
  import { reference, resolveEvidence } from "./internal/evidence.ts";
9
+ import { continuationUsage } from "./internal/usage-totals.ts";
10
10
  export { reference, resolveEvidence } from "./internal/evidence.ts";
11
11
  import {
12
12
  captureRecord,
@@ -119,6 +119,43 @@ const isExecutionProgressRecord = (
119
119
  record.payload._tag === "RunContinuation";
120
120
 
121
121
  const decodeResponsePrompt = Schema.decodeUnknownEffect(Prompt.Prompt);
122
+ // The decoded Prompt stays private. Progress and capacity are recomputed on every invocation.
123
+ const responsePrompts = new WeakMap<ModelResponseRecorded, Prompt.Prompt>();
124
+
125
+ /** Cache eligibility only: freezing does not make accessors or custom iterators immutable. */
126
+ const isFrozenJsonData = (input: Schema.Json): boolean => {
127
+ const pending: Array<unknown> = [input];
128
+ const seen = new WeakSet<object>();
129
+
130
+ while (pending.length > 0) {
131
+ const value = pending.pop();
132
+
133
+ if (value === null) continue;
134
+ if (typeof value !== "object") {
135
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean")
136
+ return false;
137
+ continue;
138
+ }
139
+ if (seen.has(value)) continue;
140
+ seen.add(value);
141
+ const prototype = Object.getPrototypeOf(value);
142
+
143
+ if (
144
+ !Object.isFrozen(value) ||
145
+ (prototype !== null && prototype !== Object.prototype && prototype !== Array.prototype)
146
+ )
147
+ return false;
148
+ for (const key of Reflect.ownKeys(value)) {
149
+ if (typeof key !== "string") return false;
150
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
151
+
152
+ if (descriptor === undefined || !("value" in descriptor)) return false;
153
+ pending.push(descriptor.value);
154
+ }
155
+ }
156
+
157
+ return true;
158
+ };
122
159
 
123
160
  const validateBatchHeader = Schema.decodeSync(
124
161
  Schema.Struct({
@@ -136,39 +173,76 @@ const ContinuationBytes = Schema.Struct({
136
173
  terminalBytes: RunContinuation.fields.terminalBytes,
137
174
  });
138
175
 
176
+ const decodeExecutionFrontier = Schema.decodeUnknownEffect(
177
+ Schema.Struct({
178
+ canonicalRecords: ExecutionCheckpoint.fields.canonicalRecords,
179
+ throughSequence: ExecutionCheckpoint.fields.throughSequence,
180
+ }),
181
+ );
182
+
139
183
  /** One private snapshot for every retry; callers cannot change facts while awaiting the gate. */
140
184
  const captureFacts = (batch: CommitBatch) =>
141
185
  Effect.try({
142
- try: () => {
143
- const header = validateBatchHeader(batch);
144
-
145
- validateBatchCount(batch.records);
146
- const records = batch.records.map((record) => captureRecord(record).canonical);
147
-
148
- return Object.freeze(
149
- new CanonicalBatch(
150
- { ...header, records: [records[0]!, ...records.slice(1)] },
151
- { disableChecks: true },
152
- ),
153
- );
154
- },
186
+ try: () =>
187
+ Result.gen(function* () {
188
+ const header = validateBatchHeader(batch);
189
+
190
+ validateBatchCount(batch.records);
191
+ const encodings = yield* Result.all(batch.records.map(captureRecord));
192
+ const records = encodings.map((record) => record.canonical);
193
+
194
+ return Object.freeze(
195
+ CanonicalBatch.make(
196
+ { ...header, records: [records[0]!, ...records.slice(1)] },
197
+ { disableChecks: true },
198
+ ),
199
+ );
200
+ }),
155
201
  catch: (cause) => failure("Cannot capture canonical progress facts", cause),
156
- });
202
+ }).pipe(
203
+ Effect.flatMap(Effect.fromResult),
204
+ Effect.mapError((cause) =>
205
+ Schema.is(ThreadStoreError)(cause)
206
+ ? cause
207
+ : failure("Cannot capture canonical progress facts", cause),
208
+ ),
209
+ );
157
210
 
158
211
  /** Exact UTF-8 wire accounting reuses the append's privately captured Schema encoding. */
159
- export const canonicalRecordBytes = (record: RecordEnvelope): number =>
160
- recordEncoding(record).bytes;
212
+ export const canonicalRecordBytes = (record: RecordEnvelope): number => {
213
+ const encoded = recordEncoding(record);
214
+
215
+ if (Result.isFailure(encoded)) {
216
+ // oxlint-disable-next-line effect/no-untyped-throw, effect/no-native-error -- Public synchronous byte-accounting validation must reject malformed records before returning a number.
217
+ throw encoded.failure.cause ?? new RangeError(encoded.failure.message);
218
+ }
219
+
220
+ return encoded.success.bytes;
221
+ };
161
222
 
162
223
  export type { ProgressAppendRecord } from "./internal/record-encoding.ts";
163
224
 
164
225
  /** Capture before acquiring the mutation owner; summaries never cross a wire boundary. */
165
226
  export const prepareProgressAppend = (
166
227
  records: ReadonlyArray<RecordEnvelope>,
167
- ): ReadonlyArray<ProgressAppendRecord> =>
168
- Object.freeze(records.map((record) => captureRecord(record).progress));
228
+ ): ReadonlyArray<ProgressAppendRecord> => {
229
+ const encodings = Result.all(records.map(captureRecord));
169
230
 
170
- const factUsageCharge = (facts: ReadonlyArray<RecordEnvelope>): number =>
171
- facts.reduce((bytes, record) => bytes + recordEncoding(record).progress.terminalUsageBytes, 0);
231
+ if (Result.isFailure(encodings)) {
232
+ // oxlint-disable-next-line effect/no-untyped-throw, effect/no-native-error -- Public synchronous progress construction must reject malformed records before acquiring a mutation owner.
233
+ throw encodings.failure.cause ?? new RangeError(encodings.failure.message);
234
+ }
235
+
236
+ return Object.freeze(encodings.success.map((record) => record.progress));
237
+ };
238
+
239
+ const factUsageCharge = (facts: ReadonlyArray<RecordEnvelope>) =>
240
+ Effect.fromResult(Result.all(facts.map(recordEncoding))).pipe(
241
+ Effect.map((encodings) =>
242
+ encodings.reduce((bytes, record) => bytes + record.progress.terminalUsageBytes, 0),
243
+ ),
244
+ Effect.mapError((cause) => failure("Cannot account canonical progress facts", cause)),
245
+ );
172
246
 
173
247
  /** These values belong to the active Run, not its permanent conversation history. */
174
248
  export const isOperationalRecord = ({ payload }: RecordEnvelope): boolean =>
@@ -663,10 +737,27 @@ const advanceFacts = Effect.fnUntraced(function* (
663
737
  if (payload.turn !== accounting.committedTurns + 1)
664
738
  return yield* failure("Canonical Turn progress must advance once");
665
739
 
666
- const messages = yield* decodeResponsePrompt(payload.messages).pipe(
667
- Effect.mapError((cause) => failure("Invalid model evidence", cause)),
668
- );
740
+ const promptInput = payload.messages;
741
+ const retainedPrompt = responsePrompts.get(payload);
742
+
743
+ const messages =
744
+ retainedPrompt ??
745
+ (yield* decodeResponsePrompt(promptInput).pipe(
746
+ Effect.mapError((cause) => failure("Invalid model evidence", cause)),
747
+ ));
669
748
 
749
+ if (retainedPrompt === undefined) {
750
+ const property = Object.getOwnPropertyDescriptor(payload, "messages");
751
+
752
+ if (
753
+ Object.isFrozen(payload) &&
754
+ property !== undefined &&
755
+ "value" in property &&
756
+ property.value === promptInput &&
757
+ isFrozenJsonData(promptInput)
758
+ )
759
+ responsePrompts.set(payload, messages);
760
+ }
670
761
  responsePrompt = messages;
671
762
 
672
763
  const calls = messages.content.flatMap((message) =>
@@ -678,18 +769,18 @@ const advanceFacts = Effect.fnUntraced(function* (
678
769
  const summary =
679
770
  payload.modelUsage === undefined
680
771
  ? undefined
681
- : yield* summarizeModelUsage(payload.modelUsage).pipe(
772
+ : yield* continuationUsage(payload.modelUsage).pipe(
682
773
  Effect.mapError((cause) => failure("Invalid usage evidence", cause)),
683
774
  );
684
775
 
685
776
  accounting.committedTurns = payload.turn;
686
777
  accounting.toolCalls += calls.length;
687
778
  accounting.modelCalls += summary?.modelCalls ?? 1;
688
- accounting.inputTokens += summary?.inputTokens.total ?? payload.inputTokens ?? 0;
689
- accounting.outputTokens += summary?.outputTokens.total ?? payload.outputTokens ?? 0;
779
+ accounting.inputTokens += summary?.inputTokens ?? payload.inputTokens ?? 0;
780
+ accounting.outputTokens += summary?.outputTokens ?? payload.outputTokens ?? 0;
690
781
  accounting.costMicrousd += summary?.costMicrousd ?? payload.costMicrousd ?? 0;
691
- accounting.lastInputTokens = summary?.inputTokens.total ?? payload.inputTokens ?? 0;
692
- accounting.lastOutputTokens = summary?.outputTokens.total ?? payload.outputTokens ?? 0;
782
+ accounting.lastInputTokens = summary?.inputTokens ?? payload.inputTokens ?? 0;
783
+ accounting.lastOutputTokens = summary?.outputTokens ?? payload.outputTokens ?? 0;
693
784
  accounting.unobservedModelCalls += payload.unobservedModelCalls ?? 0;
694
785
  latestResponse = yield* reference(fact);
695
786
  response = payload;
@@ -754,14 +845,14 @@ const advanceFacts = Effect.fnUntraced(function* (
754
845
  if (payload.restart !== (accounting.modelRestarts ?? 0) + 1)
755
846
  return yield* failure("Model restart progress must advance once");
756
847
 
757
- const summary = yield* summarizeModelUsage(payload.modelUsage).pipe(
848
+ const summary = yield* continuationUsage(payload.modelUsage).pipe(
758
849
  Effect.mapError((cause) => failure("Invalid cancelled model usage", cause)),
759
850
  );
760
851
 
761
852
  accounting.modelRestarts = payload.restart;
762
853
  accounting.modelCalls += summary.modelCalls;
763
- accounting.inputTokens += summary.inputTokens.total;
764
- accounting.outputTokens += summary.outputTokens.total;
854
+ accounting.inputTokens += summary.inputTokens;
855
+ accounting.outputTokens += summary.outputTokens;
765
856
  accounting.costMicrousd += summary.costMicrousd;
766
857
  accounting.unobservedModelCalls += payload.unobservedModelCalls;
767
858
  break;
@@ -802,6 +893,7 @@ const advanceFacts = Effect.fnUntraced(function* (
802
893
  ) {
803
894
  const messages =
804
895
  responsePrompt ??
896
+ responsePrompts.get(response) ??
805
897
  (yield* decodeResponsePrompt(response.messages).pipe(
806
898
  Effect.mapError((cause) => failure("Invalid batch evidence", cause)),
807
899
  ));
@@ -848,7 +940,7 @@ const advanceFacts = Effect.fnUntraced(function* (
848
940
  (previous?.continuation.turn === turn ? previous.continuation.turnBytes : initialBytes) +
849
941
  factBytes;
850
942
 
851
- const usageCharge = factUsageCharge(facts);
943
+ const usageCharge = yield* factUsageCharge(facts);
852
944
  const terminalUsageBytes = (previous?.continuation.terminalUsageBytes ?? 0) + usageCharge;
853
945
 
854
946
  const closing = facts.filter(isTerminalBudgetFact);
@@ -951,7 +1043,6 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
951
1043
  deploymentId: DeploymentId,
952
1044
  ) {
953
1045
  const reader = yield* ThreadReader;
954
- const crypto = yield* Crypto.Crypto;
955
1046
  const gate = yield* Semaphore.make(1);
956
1047
  const cached = new Map<RunId, ProgressState>();
957
1048
 
@@ -967,16 +1058,10 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
967
1058
 
968
1059
  const stagedUsage = new Map<RunId, number>();
969
1060
 
970
- const provide = <A, E>(effect: Effect.Effect<A, E, ThreadReader | Crypto.Crypto>) =>
971
- effect.pipe(
972
- Effect.provideService(ThreadReader, reader),
973
- Effect.provideService(Crypto.Crypto, crypto),
974
- );
975
-
976
- const loadState = Effect.fnUntraced(function* (runId: RunId, after: CanonicalSequence) {
977
- const retained = cached.get(runId);
1061
+ const provide = <A, E>(effect: Effect.Effect<A, E, ThreadReader>) =>
1062
+ Effect.provideService(effect, ThreadReader, reader);
978
1063
 
979
- if (retained !== undefined) return retained;
1064
+ const loadUncachedState = Effect.fnUntraced(function* (runId: RunId, after: CanonicalSequence) {
980
1065
  const loaded = yield* provide(readContinuation(threadId, runId, after));
981
1066
 
982
1067
  if (Option.isNone(loaded)) return undefined;
@@ -1095,11 +1180,21 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1095
1180
  };
1096
1181
  });
1097
1182
 
1098
- const checkFutureShapes = (state: ProgressState): Effect.Effect<void, ThreadStoreError> => {
1183
+ const loadState = (runId: RunId, after: CanonicalSequence) =>
1184
+ Effect.suspend(() => {
1185
+ const retained = cached.get(runId);
1186
+
1187
+ return retained === undefined ? loadUncachedState(runId, after) : Effect.succeed(retained);
1188
+ });
1189
+
1190
+ const checkFutureShapes = (
1191
+ state: ProgressState,
1192
+ cursorBytes?: number,
1193
+ ): Effect.Effect<void, ThreadStoreError> => {
1099
1194
  const progress = state.continuation;
1100
1195
 
1101
1196
  if (progress.position === "settled") return Effect.void;
1102
- const size = canonicalRecordBytes(state.record);
1197
+ const size = cursorBytes ?? canonicalRecordBytes(state.record);
1103
1198
  const textBytes = (value: string) => utf8ByteLength(JSON.stringify(value));
1104
1199
  const refBytes = (value: EvidenceReference) => utf8ByteLength(JSON.stringify(value));
1105
1200
 
@@ -1213,14 +1308,20 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1213
1308
  return Effect.void;
1214
1309
  };
1215
1310
 
1216
- const prepare = Effect.fnUntraced(function* (batch: CanonicalBatch, after: CanonicalSequence) {
1311
+ const prepare = Effect.fnUntraced(function* (
1312
+ batch: CanonicalBatch,
1313
+ after: CanonicalSequence,
1314
+ mode: "commit" | "preflight" = "commit",
1315
+ ) {
1217
1316
  if (batch.records.some((record) => record.payload._tag === "RunContinuation"))
1218
1317
  return yield* failure("A caller cannot supply interpreter progress to the progress writer");
1219
1318
 
1220
- const ownedRecords = yield* Effect.try({
1221
- try: () => batch.records.map((record) => captureRecord(record).canonical),
1222
- catch: (cause) => failure("Cannot capture canonical progress facts", cause),
1223
- });
1319
+ const ownedRecords = yield* Effect.fromResult(
1320
+ Result.all(batch.records.map(captureRecord)),
1321
+ ).pipe(
1322
+ Effect.map((encodings) => encodings.map((record) => record.canonical)),
1323
+ Effect.mapError((cause) => failure("Cannot capture canonical progress facts", cause)),
1324
+ );
1224
1325
 
1225
1326
  const groups = new Map<RunId, Array<RecordEnvelope>>();
1226
1327
 
@@ -1234,6 +1335,7 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1234
1335
  }
1235
1336
  const executions: Array<ExecutionUpdate> = [];
1236
1337
  const accepted = new Map<RunId, ProgressState>();
1338
+ let executionCount = 0;
1237
1339
 
1238
1340
  for (const [runId, facts] of groups) {
1239
1341
  const previous = yield* loadState(runId, after);
@@ -1337,12 +1439,17 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1337
1439
 
1338
1440
  if (payload._tag !== "SubmissionSettled" || payload.runId !== runId) continue;
1339
1441
 
1340
- const retained = captureRecord(
1341
- RecordEnvelope.make({
1342
- ...fact,
1343
- payload: { ...payload, historyRecordCount: canonicalRecords },
1344
- }),
1345
- ).canonical;
1442
+ const retained = yield* Effect.fromResult(
1443
+ captureRecord(
1444
+ RecordEnvelope.make({
1445
+ ...fact,
1446
+ payload: { ...payload, historyRecordCount: canonicalRecords },
1447
+ }),
1448
+ ),
1449
+ ).pipe(
1450
+ Effect.map((record) => record.canonical),
1451
+ Effect.mapError((cause) => failure("Cannot capture canonical progress facts", cause)),
1452
+ );
1346
1453
 
1347
1454
  facts[index] = retained;
1348
1455
  ownedRecords[ownedRecords.indexOf(fact)] = retained;
@@ -1353,9 +1460,9 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1353
1460
  ? previous.continuation.originalInput
1354
1461
  : original === undefined
1355
1462
  ? yield* failure("Run progress has no original input")
1356
- : yield* provide(reference(original));
1463
+ : yield* reference(original);
1357
1464
 
1358
- const next = yield* provide(advanceFacts(previous, facts, submissionId, initialBytes));
1465
+ const next = yield* advanceFacts(previous, facts, submissionId, initialBytes);
1359
1466
 
1360
1467
  const {
1361
1468
  accounting,
@@ -1391,7 +1498,7 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1391
1498
  ...(savedContext === undefined ? {} : { savedContext }),
1392
1499
  ...(latestResponse === undefined ? {} : { latestResponse }),
1393
1500
  ...(terminal === undefined ? {} : { terminal }),
1394
- lastFact: yield* provide(reference(last)),
1501
+ lastFact: yield* reference(last),
1395
1502
  position,
1396
1503
  accounting,
1397
1504
  }).pipe(
@@ -1451,23 +1558,63 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1451
1558
  );
1452
1559
 
1453
1560
  // Every other field was checked above; byte accounting changes only these counters.
1454
- continuation = new RunContinuation(
1561
+ continuation = RunContinuation.make(
1455
1562
  { ...continuation, ...checkedBytes },
1456
1563
  { disableChecks: true },
1457
1564
  );
1458
1565
 
1459
- const record = captureRecord(
1460
- new RecordEnvelope({ ...header, payload: continuation }, { disableChecks: true }),
1461
- ).canonical;
1566
+ const proposedRecord = RecordEnvelope.make(
1567
+ { ...header, payload: continuation },
1568
+ { disableChecks: true },
1569
+ );
1570
+
1571
+ const cursorBytes = turnBytes - turnBase;
1462
1572
 
1463
- if (!isExecutionProgressRecord(record))
1464
- return yield* failure("Captured execution has no Run continuation");
1465
- if (canonicalRecordBytes(record) !== turnBytes - turnBase)
1466
- return yield* failure("Continuation byte accounting differs from its Schema encoding");
1467
- if (canonicalRecordBytes(record) > MAX_RUN_CONTINUATION_BYTES)
1573
+ if (cursorBytes > MAX_RUN_CONTINUATION_BYTES)
1468
1574
  return yield* capacityFailure(
1469
1575
  "Encoded continuation exceeds 8192 bytes including its envelope",
1470
1576
  );
1577
+ executionCount++;
1578
+ if (
1579
+ mode === "preflight" &&
1580
+ previous?.checkpoint !== undefined &&
1581
+ facts.every(({ payload }) => payload._tag === "ModelResponseRecorded")
1582
+ ) {
1583
+ // The payload and header are validated, and operational facts are unchanged.
1584
+ // Dispatch needs their capacity proof; only a commit needs checkpoint wire.
1585
+ yield* decodeExecutionFrontier({
1586
+ canonicalRecords,
1587
+ throughSequence: after + ownedRecords.filter((fact) => !isOperationalRecord(fact)).length,
1588
+ }).pipe(Effect.mapError((cause) => failure("Active execution exceeds its bounds", cause)));
1589
+
1590
+ const state: ProgressState = {
1591
+ continuation,
1592
+ record: proposedRecord,
1593
+ ...(response === undefined ? {} : { response }),
1594
+ results,
1595
+ childResults,
1596
+ ...(completionBytes === undefined ? {} : { completionBytes }),
1597
+ };
1598
+
1599
+ yield* checkCapacity(
1600
+ state,
1601
+ new Set(batch.records.map(({ recordId }) => recordId)),
1602
+ undefined,
1603
+ usageCharge,
1604
+ );
1605
+ yield* checkFutureShapes(state, cursorBytes);
1606
+ continue;
1607
+ }
1608
+
1609
+ const record = yield* Effect.fromResult(captureRecord(proposedRecord)).pipe(
1610
+ Effect.map((record) => record.canonical),
1611
+ Effect.mapError((cause) => failure("Cannot capture canonical progress facts", cause)),
1612
+ );
1613
+
1614
+ if (!isExecutionProgressRecord(record))
1615
+ return yield* failure("Captured execution has no Run continuation");
1616
+ if (canonicalRecordBytes(record) !== cursorBytes)
1617
+ return yield* failure("Continuation byte accounting differs from its Schema encoding");
1471
1618
 
1472
1619
  const operational = new Map(
1473
1620
  (previous?.checkpoint?.operational ?? []).map((fact) => [fact.recordId, fact]),
@@ -1478,7 +1625,15 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1478
1625
  for (const fact of changed) {
1479
1626
  const prior = operational.get(fact.recordId);
1480
1627
 
1481
- if (prior !== undefined && recordEncoding(prior).json !== recordEncoding(fact).json)
1628
+ if (
1629
+ prior !== undefined &&
1630
+ (yield* Effect.fromResult(recordEncoding(prior)).pipe(
1631
+ Effect.mapError((cause) => failure("Cannot encode active Run fact", cause)),
1632
+ )).json !==
1633
+ (yield* Effect.fromResult(recordEncoding(fact)).pipe(
1634
+ Effect.mapError((cause) => failure("Cannot encode active Run fact", cause)),
1635
+ )).json
1636
+ )
1482
1637
  return yield* failure("An active Run fact cannot change after becoming durable");
1483
1638
  operational.set(fact.recordId, fact);
1484
1639
  }
@@ -1535,10 +1690,10 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1535
1690
  accepted.set(runId, state);
1536
1691
  }
1537
1692
 
1538
- if (ownedRecords.length + executions.length > 256)
1693
+ if (ownedRecords.length + executionCount > 256)
1539
1694
  return yield* failure("Progress and facts exceed the atomic batch bound");
1540
1695
 
1541
- const prepared = new CommitBatch(
1696
+ const prepared = CommitBatch.make(
1542
1697
  {
1543
1698
  ...batch,
1544
1699
  records: ownedRecords.filter((fact) => !isOperationalRecord(fact)),
@@ -1566,14 +1721,11 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1566
1721
  };
1567
1722
  });
1568
1723
 
1569
- const tail = Effect.gen(function* () {
1570
- const writer = yield* CurrentRunWriter;
1571
-
1572
- if (writer.threadId !== threadId)
1573
- return yield* failure("Progress writer belongs to another Thread");
1574
-
1575
- return (yield* writer.tail).sequence;
1576
- });
1724
+ const tail = Effect.flatMap(CurrentRunWriter, (writer) =>
1725
+ writer.threadId !== threadId
1726
+ ? Effect.fail(failure("Progress writer belongs to another Thread"))
1727
+ : Effect.map(writer.tail, (position) => position.sequence),
1728
+ );
1577
1729
 
1578
1730
  const commitCaptured = Effect.fnUntraced(
1579
1731
  function* (batch: CanonicalBatch) {
@@ -1699,7 +1851,7 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1699
1851
 
1700
1852
  const owned = yield* captureFacts(batch);
1701
1853
 
1702
- yield* prepare(owned, after);
1854
+ yield* prepare(owned, after, "preflight");
1703
1855
 
1704
1856
  const response = owned.records.find(
1705
1857
  (record) => record.payload._tag === "ModelResponseRecorded",
@@ -1,4 +1,4 @@
1
- import { Brand, type Crypto, Effect, Predicate, Schema, Stream, type DateTime } from "effect";
1
+ import { Brand, Effect, Predicate, Schema, Stream, type DateTime } from "effect";
2
2
  import { Prompt } from "effect/ai";
3
3
 
4
4
  import {
@@ -22,13 +22,14 @@ import {
22
22
  contextWindowMessage,
23
23
  } from "../engine/Compaction.ts";
24
24
  import type { RunTurnToolResult } from "../engine/RunOptions.ts";
25
- import { digestJson, type DigestError } from "./Digest.ts";
25
+ import { digestJson } from "./Digest.ts";
26
26
  import {
27
27
  makeJournalMetadata,
28
28
  toolCallSettledRecordId,
29
29
  type JournalMetadata,
30
30
  type JournalRecordEnvelope,
31
31
  } from "./internal/journal-metadata.ts";
32
+ import { continuationUsage } from "./internal/usage-totals.ts";
32
33
  import {
33
34
  type BatchId,
34
35
  type RecordId,
@@ -1704,7 +1705,7 @@ const requireCanonicalTurn = (turn: number): Effect.Effect<void, RunJournalError
1704
1705
 
1705
1706
  const modelResponseRecord = Effect.fnUntraced(function* (
1706
1707
  input: TurnCommitInput,
1707
- ): Effect.fn.Return<RecordEnvelope, RunJournalError | DigestError, Crypto.Crypto> {
1708
+ ): Effect.fn.Return<RecordEnvelope, RunJournalError> {
1708
1709
  const promptMessages = input.responseMessages;
1709
1710
 
1710
1711
  if (promptMessages.length === 0) {
@@ -1753,14 +1754,14 @@ const modelResponseRecord = Effect.fnUntraced(function* (
1753
1754
  return yield* journalError("Turn model usage must contain at least one completed call");
1754
1755
  }
1755
1756
 
1756
- const summary = yield* summarizeModelUsage(modelUsage).pipe(
1757
+ const summary = yield* continuationUsage(modelUsage).pipe(
1757
1758
  Effect.mapError((cause) => journalError("Turn model usage exceeds accounting bounds", cause)),
1758
1759
  );
1759
1760
 
1760
1761
  if (
1761
1762
  input.usage === undefined ||
1762
- input.usage.inputTokens !== summary.inputTokens.total ||
1763
- input.usage.outputTokens !== summary.outputTokens.total ||
1763
+ input.usage.inputTokens !== summary.inputTokens ||
1764
+ input.usage.outputTokens !== summary.outputTokens ||
1764
1765
  (input.usage.costMicrousd ?? 0) !== summary.costMicrousd
1765
1766
  ) {
1766
1767
  return yield* journalError("Turn detailed and aggregate model usage disagree");
@@ -1882,7 +1883,7 @@ const runCompletionRecord = Effect.fnUntraced(function* (input: TurnCommitInput)
1882
1883
  */
1883
1884
  export const turnCanonicalBatch = Effect.fnUntraced(function* (
1884
1885
  input: TurnCommitInput,
1885
- ): Effect.fn.Return<CanonicalBatch, RunJournalError | DigestError, Crypto.Crypto> {
1886
+ ): Effect.fn.Return<CanonicalBatch, RunJournalError> {
1886
1887
  yield* requireCanonicalTurn(input.turn);
1887
1888
  const modelResponse = yield* modelResponseRecord(input);
1888
1889
  const toolRecords = yield* toolSettledRecords(input);
@@ -1907,7 +1908,7 @@ export const turnCanonicalBatch = Effect.fnUntraced(function* (
1907
1908
  */
1908
1909
  export const turnResponseBatch = Effect.fnUntraced(function* (
1909
1910
  input: TurnCommitInput,
1910
- ): Effect.fn.Return<CanonicalBatch, RunJournalError | DigestError, Crypto.Crypto> {
1911
+ ): Effect.fn.Return<CanonicalBatch, RunJournalError> {
1911
1912
  yield* requireCanonicalTurn(input.turn);
1912
1913
  const modelResponse = yield* modelResponseRecord(input);
1913
1914
 
@@ -1926,7 +1927,7 @@ export const turnResponseBatch = Effect.fnUntraced(function* (
1926
1927
  */
1927
1928
  export const turnResultsBatch = Effect.fnUntraced(function* (
1928
1929
  input: TurnCommitInput,
1929
- ): Effect.fn.Return<CanonicalBatch, RunJournalError | DigestError, Crypto.Crypto> {
1930
+ ): Effect.fn.Return<CanonicalBatch, RunJournalError> {
1930
1931
  yield* requireCanonicalTurn(input.turn);
1931
1932
  const toolRecords = yield* toolSettledRecords(input);
1932
1933
  const first = toolRecords[0];