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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/dist/{Admin-BX9ns28E.d.mts → Admin-Qa3I1oA5.d.mts} +61 -21
  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-BfP14534.d.mts → AgentRuntime-CHDyRbSf.d.mts} +12 -12
  6. package/dist/{AgentRuntime-CQ-qcETl.mjs → AgentRuntime-DaDKMasp.mjs} +290 -273
  7. package/dist/AgentRuntime-DaDKMasp.mjs.map +1 -0
  8. package/dist/{Digest-DCuPlhNk.mjs → Digest-C3EODOee.mjs} +42 -15
  9. package/dist/Digest-C3EODOee.mjs.map +1 -0
  10. package/dist/{DurableAgentRuntime-BAmSkWYr.mjs → DurableAgentRuntime-CPlw5_7e.mjs} +1557 -1458
  11. package/dist/DurableAgentRuntime-CPlw5_7e.mjs.map +1 -0
  12. package/dist/{EventSource-BwliwQz9.d.mts → EventSource-BTu68yDW.d.mts} +2 -2
  13. package/dist/InMemory.d.mts +2 -2
  14. package/dist/{MessageDelivery-B943ebI_.d.mts → MessageDelivery-DjAZJ4Fd.d.mts} +42 -42
  15. package/dist/{Messaging-C_6OOg_s.d.mts → Messaging-SVu6rWV8.d.mts} +12 -6
  16. package/dist/{MessagingHost-B8oKM7AC.d.mts → MessagingHost-C6SOAcoG.d.mts} +2 -2
  17. package/dist/{MessagingHost-CRUK4JBi.mjs → MessagingHost-CZCyrnvJ.mjs} +27 -26
  18. package/dist/MessagingHost-CZCyrnvJ.mjs.map +1 -0
  19. package/dist/{Output-Dsx6k52t.d.mts → Output-CRamN1Fy.d.mts} +2 -2
  20. package/dist/{PreparedInputAdmission-CxSJSiTl.d.mts → PreparedInputAdmission-wgY4JMZY.d.mts} +3 -3
  21. package/dist/{Records-BiuJDWNe.mjs → Records-DzL4Kbtw.mjs} +2 -2
  22. package/dist/{Records-BiuJDWNe.mjs.map → Records-DzL4Kbtw.mjs.map} +1 -1
  23. package/dist/{RunContinuation-FHM7XPjq.d.mts → RunContinuation-3jB5pULC.d.mts} +13 -7
  24. package/dist/{RunContinuation-DHYNAQFi.mjs → RunContinuation-BiyKJK32.mjs} +173 -82
  25. package/dist/RunContinuation-BiyKJK32.mjs.map +1 -0
  26. package/dist/{RunJournal-kfKVxJj7.mjs → RunJournal-CPMKcfd0.mjs} +106 -73
  27. package/dist/RunJournal-CPMKcfd0.mjs.map +1 -0
  28. package/dist/{RunOptions-D8zxFIrZ.d.mts → RunOptions-Be77lKVZ.d.mts} +2 -2
  29. package/dist/{RunStorage-uqPxps09.mjs → RunStorage-CzutHJBE.mjs} +10 -6
  30. package/dist/RunStorage-CzutHJBE.mjs.map +1 -0
  31. package/dist/{Scheduling-CVyfXlBR.mjs → Scheduling-07_dsGED.mjs} +12 -12
  32. package/dist/Scheduling-07_dsGED.mjs.map +1 -0
  33. package/dist/{Subagent-vpffemql.mjs → Subagent-CexndZSp.mjs} +53 -40
  34. package/dist/Subagent-CexndZSp.mjs.map +1 -0
  35. package/dist/{Subagent-E6tvd7-k.d.mts → Subagent-xhN_aD1r.d.mts} +20 -9
  36. package/dist/{SubagentHost-B6w2m-SI.d.mts → SubagentHost-D1xBgn9g.d.mts} +2 -2
  37. package/dist/{Subscription-DONig54Q.d.mts → Subscription-DmIHlOJK.d.mts} +2 -2
  38. package/dist/{SubscriptionInput-mJIRxWJM.d.mts → SubscriptionInput-DgcQhvbe.d.mts} +2 -2
  39. package/dist/{Thread-dk-QiNml.d.mts → Thread-DzdJAY4J.d.mts} +4 -3
  40. package/dist/{ThreadHistory-ByxS8HU1.d.mts → ThreadHistory-KPPBJLJb.d.mts} +12 -2
  41. package/dist/{ThreadImport-DckUMEqJ.mjs → ThreadImport-CjlG57vH.mjs} +9 -9
  42. package/dist/ThreadImport-CjlG57vH.mjs.map +1 -0
  43. package/dist/{ThreadImport-DpHxjBrA.d.mts → ThreadImport-DP-vOk2F.d.mts} +5 -5
  44. package/dist/{ThreadStore-CEekRa80.mjs → ThreadStore-EuUilthA.mjs} +162 -85
  45. package/dist/ThreadStore-EuUilthA.mjs.map +1 -0
  46. package/dist/{ThreadTransfer-B4gTQLIl.mjs → ThreadTransfer-DB_zsrtJ.mjs} +4 -4
  47. package/dist/{ThreadTransfer-B4gTQLIl.mjs.map → ThreadTransfer-DB_zsrtJ.mjs.map} +1 -1
  48. package/dist/{WorkerAdmission-DFVbbVh2.mjs → WorkerAdmission-eBeXt_mu.mjs} +5 -5
  49. package/dist/{WorkerAdmission-DFVbbVh2.mjs.map → WorkerAdmission-eBeXt_mu.mjs.map} +1 -1
  50. package/dist/{agent-registration-ByMZ_cK5.mjs → agent-registration-a6QYaG3h.mjs} +70 -57
  51. package/dist/agent-registration-a6QYaG3h.mjs.map +1 -0
  52. package/dist/capabilities/Approval.mjs +2 -2
  53. package/dist/capabilities/Approval.mjs.map +1 -1
  54. package/dist/capabilities/BrowserUse.d.mts +102 -1867
  55. package/dist/capabilities/BrowserUse.mjs +74 -798
  56. package/dist/capabilities/BrowserUse.mjs.map +1 -1
  57. package/dist/capabilities/Budget.mjs +3 -1
  58. package/dist/capabilities/Budget.mjs.map +1 -1
  59. package/dist/capabilities/CodeMode.d.mts +1 -1
  60. package/dist/capabilities/CodeMode.mjs +16 -9
  61. package/dist/capabilities/CodeMode.mjs.map +1 -1
  62. package/dist/capabilities/Commands.d.mts +1 -1
  63. package/dist/capabilities/McpClient.mjs +1 -1
  64. package/dist/capabilities/McpClient.mjs.map +1 -1
  65. package/dist/capabilities/Messaging.d.mts +1 -1
  66. package/dist/capabilities/Messaging.mjs +5 -5
  67. package/dist/capabilities/Messaging.mjs.map +1 -1
  68. package/dist/capabilities/ModelContext.d.mts +1 -1
  69. package/dist/capabilities/ModelContext.mjs +7 -5
  70. package/dist/capabilities/ModelContext.mjs.map +1 -1
  71. package/dist/capabilities/Redaction.mjs +49 -19
  72. package/dist/capabilities/Redaction.mjs.map +1 -1
  73. package/dist/capabilities/RunHooks.d.mts +2 -2
  74. package/dist/capabilities/RunHooks.mjs +1 -1
  75. package/dist/capabilities/RunHooks.mjs.map +1 -1
  76. package/dist/capabilities/Subagent.d.mts +1 -1
  77. package/dist/capabilities/Subagent.mjs +1 -1
  78. package/dist/capabilities/SubagentReservations.mjs +3 -1
  79. package/dist/capabilities/SubagentReservations.mjs.map +1 -1
  80. package/dist/capabilities/WebCapture.mjs.map +1 -1
  81. package/dist/capabilities/WebSearch.mjs +7 -7
  82. package/dist/capabilities/WebSearch.mjs.map +1 -1
  83. package/dist/core/Agent.d.mts +1 -1
  84. package/dist/core/Agent.mjs +1 -1
  85. package/dist/core/AgentUpdates.mjs +8 -8
  86. package/dist/core/AgentUpdates.mjs.map +1 -1
  87. package/dist/core/Thread.d.mts +2 -2
  88. package/dist/core/Thread.mjs +6 -4
  89. package/dist/core/Thread.mjs.map +1 -1
  90. package/dist/core/Usage.mjs +2 -2
  91. package/dist/core/Usage.mjs.map +1 -1
  92. package/dist/durable/ActivityStore.mjs +1 -1
  93. package/dist/durable/Admin.d.mts +1 -1
  94. package/dist/durable/Admin.mjs +1 -1
  95. package/dist/durable/AgentRegistration.d.mts +1 -1
  96. package/dist/durable/AgentRegistration.mjs +1 -1
  97. package/dist/durable/Certification.d.mts +1 -1
  98. package/dist/durable/CommittedActivity.d.mts +1 -1
  99. package/dist/durable/CommittedActivity.mjs +3 -3
  100. package/dist/durable/Digest.mjs +1 -1
  101. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  102. package/dist/durable/DurableAgentRuntime.mjs +2 -2
  103. package/dist/durable/EventSource.d.mts +1 -1
  104. package/dist/durable/EventSource.mjs +7 -7
  105. package/dist/durable/EventSource.mjs.map +1 -1
  106. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  107. package/dist/durable/LifecyclePublication.d.mts +1 -1
  108. package/dist/durable/LifecyclePublication.mjs +2 -2
  109. package/dist/durable/MessageDelivery.d.mts +1 -1
  110. package/dist/durable/MessageDelivery.mjs +2 -2
  111. package/dist/durable/MessageDeliveryStoreConformance.d.mts +40 -40
  112. package/dist/durable/MessageDeliveryStoreConformance.mjs +2 -2
  113. package/dist/durable/PersistentHistory.d.mts +2 -2
  114. package/dist/durable/PersistentHistory.mjs +4 -4
  115. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  116. package/dist/durable/RecordFormat.mjs +3 -3
  117. package/dist/durable/RecordFormat.mjs.map +1 -1
  118. package/dist/durable/Records.mjs +1 -1
  119. package/dist/durable/Recovery.d.mts +1 -1
  120. package/dist/durable/Recovery.mjs +1 -1
  121. package/dist/durable/RunContinuation.d.mts +2 -2
  122. package/dist/durable/RunContinuation.mjs +3 -3
  123. package/dist/durable/RunJournal.d.mts +2 -2
  124. package/dist/durable/RunJournal.mjs +2 -2
  125. package/dist/durable/RunStorage.d.mts +1 -1
  126. package/dist/durable/RunStorage.mjs +1 -1
  127. package/dist/durable/Schedule.mjs +1 -1
  128. package/dist/durable/ScheduleStoreConformance.mjs +1 -1
  129. package/dist/durable/ScheduleTransition.mjs +1 -1
  130. package/dist/durable/Scheduling.d.mts +1 -1
  131. package/dist/durable/Scheduling.mjs +1 -1
  132. package/dist/durable/SettlementPublisher.d.mts +1 -1
  133. package/dist/durable/SettlementPublisher.mjs +3 -3
  134. package/dist/durable/SqlMemoryStore.mjs +5 -3
  135. package/dist/durable/SqlMemoryStore.mjs.map +1 -1
  136. package/dist/durable/SubmissionLedger.d.mts +1 -1
  137. package/dist/durable/SubmissionLedger.mjs +11 -3
  138. package/dist/durable/SubmissionLedger.mjs.map +1 -1
  139. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  140. package/dist/durable/SubmissionLedgerConformance.mjs +6 -6
  141. package/dist/durable/SubmissionStatus.d.mts +1 -1
  142. package/dist/durable/Subscription.d.mts +1 -1
  143. package/dist/durable/Subscription.mjs +2 -2
  144. package/dist/durable/SubscriptionInput.d.mts +1 -1
  145. package/dist/durable/SubscriptionInput.mjs +4 -4
  146. package/dist/durable/SubscriptionInput.mjs.map +1 -1
  147. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  148. package/dist/durable/SubscriptionStoreConformance.mjs +3 -3
  149. package/dist/durable/SubscriptionStoreConformance.mjs.map +1 -1
  150. package/dist/durable/SubscriptionTools.d.mts +2 -2
  151. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  152. package/dist/durable/SubscriptionTransition.mjs +1 -1
  153. package/dist/durable/Subscriptions.d.mts +5 -5
  154. package/dist/durable/Subscriptions.mjs +3 -3
  155. package/dist/durable/Subscriptions.mjs.map +1 -1
  156. package/dist/durable/ThreadArchiveRange.d.mts +1 -1
  157. package/dist/durable/ThreadArchiveRange.mjs +1 -1
  158. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  159. package/dist/durable/ThreadContextHistory.mjs +2 -2
  160. package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
  161. package/dist/durable/ThreadImport.d.mts +1 -1
  162. package/dist/durable/ThreadImport.mjs +1 -1
  163. package/dist/durable/ThreadInvariants.d.mts +3 -3
  164. package/dist/durable/ThreadInvariants.mjs +5 -5
  165. package/dist/durable/ThreadInvariants.mjs.map +1 -1
  166. package/dist/durable/ThreadProjection.mjs +1 -1
  167. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  168. package/dist/durable/ThreadReader.d.mts +1 -1
  169. package/dist/durable/ThreadReader.mjs +1 -1
  170. package/dist/durable/ThreadStore.d.mts +1 -1
  171. package/dist/durable/ThreadStore.mjs +2 -2
  172. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  173. package/dist/durable/ThreadStoreConformance.mjs +4 -4
  174. package/dist/durable/ThreadTransfer.d.mts +1 -1
  175. package/dist/durable/ThreadTransfer.mjs +1 -1
  176. package/dist/durable/ThreadWork.d.mts +2 -2
  177. package/dist/durable/ThreadWork.mjs +12 -6
  178. package/dist/durable/ThreadWork.mjs.map +1 -1
  179. package/dist/durable/ToolReconciler.mjs +1 -1
  180. package/dist/durable/WakeScheduler.mjs +23 -39
  181. package/dist/durable/WakeScheduler.mjs.map +1 -1
  182. package/dist/durable/WorkerAdmission.d.mts +1 -1
  183. package/dist/durable/WorkerAdmission.mjs +1 -1
  184. package/dist/durable/WorkerHost.d.mts +2 -2
  185. package/dist/engine/AgentRuntime.d.mts +1 -1
  186. package/dist/engine/AgentRuntime.mjs +1 -1
  187. package/dist/engine/MessagingHost.d.mts +1 -1
  188. package/dist/engine/MessagingHost.mjs +1 -1
  189. package/dist/engine/Output.d.mts +1 -1
  190. package/dist/engine/Output.mjs +1 -1
  191. package/dist/engine/RunOptions.d.mts +1 -1
  192. package/dist/engine/SubagentHost.d.mts +1 -1
  193. package/dist/engine/ThreadHistory.d.mts +1 -1
  194. package/dist/engine/ThreadHistory.mjs +3 -2
  195. package/dist/engine/ThreadHistory.mjs.map +1 -1
  196. package/dist/index.d.mts +21 -21
  197. package/dist/index.mjs +16 -16
  198. package/dist/{output-contract-CMIhu4Cw.mjs → output-contract-DCnLYCdp.mjs} +12 -15
  199. package/dist/{output-contract-CMIhu4Cw.mjs.map → output-contract-DCnLYCdp.mjs.map} +1 -1
  200. package/dist/{run-context-reader-OcsZr4bk.d.mts → run-context-reader-DVhJZ1Qe.d.mts} +2 -2
  201. package/package.json +1 -1
  202. package/src/capabilities/Approval.ts +58 -60
  203. package/src/capabilities/BrowserUse.ts +89 -944
  204. package/src/capabilities/Budget.ts +8 -1
  205. package/src/capabilities/CodeMode.ts +57 -33
  206. package/src/capabilities/McpClient.ts +2 -2
  207. package/src/capabilities/Messaging.ts +51 -55
  208. package/src/capabilities/ModelContext.ts +5 -2
  209. package/src/capabilities/Redaction.ts +64 -34
  210. package/src/capabilities/RunHooks.ts +52 -55
  211. package/src/capabilities/Subagent.ts +33 -33
  212. package/src/capabilities/SubagentReservations.ts +9 -1
  213. package/src/capabilities/WebCapture.ts +6 -0
  214. package/src/capabilities/WebSearch.ts +10 -7
  215. package/src/capabilities/internal/subagent-background.ts +202 -158
  216. package/src/core/Agent.ts +11 -6
  217. package/src/core/AgentUpdates.ts +8 -8
  218. package/src/core/Thread.ts +142 -146
  219. package/src/core/Usage.ts +7 -8
  220. package/src/core/internal/agent-updates.ts +1 -0
  221. package/src/durable/DurableAgentRuntime.ts +1024 -829
  222. package/src/durable/EventSource.ts +15 -9
  223. package/src/durable/RecordFormat.ts +5 -3
  224. package/src/durable/RunContinuation.ts +316 -113
  225. package/src/durable/RunJournal.ts +106 -114
  226. package/src/durable/RunStorage.ts +129 -124
  227. package/src/durable/Scheduling.ts +639 -651
  228. package/src/durable/SqlMemoryStore.ts +8 -6
  229. package/src/durable/SubmissionLedger.ts +11 -0
  230. package/src/durable/SubscriptionInput.ts +5 -3
  231. package/src/durable/SubscriptionStoreConformance.ts +23 -23
  232. package/src/durable/Subscriptions.ts +18 -19
  233. package/src/durable/ThreadImport.ts +18 -19
  234. package/src/durable/ThreadInvariants.ts +2 -1
  235. package/src/durable/ThreadStore.ts +217 -111
  236. package/src/durable/ThreadWork.ts +11 -2
  237. package/src/durable/WakeScheduler.ts +34 -66
  238. package/src/durable/internal/StorageOwner.ts +10 -4
  239. package/src/durable/internal/agent-registration.ts +96 -89
  240. package/src/durable/internal/canonical-json.ts +61 -20
  241. package/src/durable/internal/prompt-history-cache.ts +13 -4
  242. package/src/durable/internal/record-encoding.ts +120 -64
  243. package/src/durable/internal/schedule-time.ts +6 -6
  244. package/src/durable/internal/usage-totals.ts +35 -0
  245. package/src/durable/internal/worker-host.ts +69 -74
  246. package/src/engine/MessagingHost.ts +4 -2
  247. package/src/engine/ThreadHistory.ts +47 -48
  248. package/src/engine/internal/agent-runtime.ts +1298 -1217
  249. package/src/engine/internal/output-contract.ts +15 -15
  250. package/src/engine/internal/primitive-delta.ts +2 -3
  251. package/src/engine/internal/provisional-text.ts +12 -10
  252. package/src/engine/internal/tool-exposure.ts +22 -18
  253. package/src/engine/internal/tool-telemetry.ts +15 -12
  254. package/dist/Agent-0WvBWWbM.mjs.map +0 -1
  255. package/dist/AgentRuntime-CQ-qcETl.mjs.map +0 -1
  256. package/dist/Digest-DCuPlhNk.mjs.map +0 -1
  257. package/dist/DurableAgentRuntime-BAmSkWYr.mjs.map +0 -1
  258. package/dist/MessagingHost-CRUK4JBi.mjs.map +0 -1
  259. package/dist/RunContinuation-DHYNAQFi.mjs.map +0 -1
  260. package/dist/RunJournal-kfKVxJj7.mjs.map +0 -1
  261. package/dist/RunStorage-uqPxps09.mjs.map +0 -1
  262. package/dist/Scheduling-CVyfXlBR.mjs.map +0 -1
  263. package/dist/Subagent-vpffemql.mjs.map +0 -1
  264. package/dist/ThreadImport-DckUMEqJ.mjs.map +0 -1
  265. package/dist/ThreadStore-CEekRa80.mjs.map +0 -1
  266. package/dist/agent-registration-ByMZ_cK5.mjs.map +0 -1
@@ -1,4 +1,3 @@
1
- import type { Crypto } from "effect";
2
1
  import { Context, Effect, Layer, Option, Schema, Stream } from "effect";
3
2
 
4
3
  import { ReceiptId, RunId, SubmissionId, ThreadId, ToolCallId } from "../core/Identifiers.ts";
@@ -145,6 +144,10 @@ export const ThreadWorkCursor = Schema.Struct({
145
144
 
146
145
  export type ThreadWorkCursor = typeof ThreadWorkCursor.Type;
147
146
 
147
+ /** Local snapshot request; it grants no claim or dispatch authority. */
148
+ export const FreshAdmissionWork = Schema.Struct({ threadId: ThreadId, submissionId: SubmissionId });
149
+ export type FreshAdmissionWork = typeof FreshAdmissionWork.Type;
150
+
148
151
  export const WorkThreadsRequest = Schema.Struct({
149
152
  afterThreadId: Schema.optionalKey(ThreadId),
150
153
  limit: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 32 })),
@@ -193,6 +196,12 @@ export class WorkDiscoveryUnavailable extends Schema.TaggedError<WorkDiscoveryUn
193
196
 
194
197
  /** Native owner pages and canonical-index maintenance, implemented at the storage owner. */
195
198
  export interface ThreadWorkStorage {
199
+ /** Native proof that this untouched root admission has no other recovery obligations.
200
+ * A caller must still claim and validate current controls before dispatch. */
201
+ readonly isOnlyFreshAdmission?: (
202
+ request: FreshAdmissionWork,
203
+ ) => Effect.Effect<boolean, ThreadStoreError>;
204
+
196
205
  readonly threads: (
197
206
  request: WorkThreadsRequest,
198
207
  ) => Effect.Effect<WorkThreadsPage, ThreadStoreError>;
@@ -487,7 +496,7 @@ export type WorkIndexChange =
487
496
  export const workIndexChanges = Effect.fnUntraced(function* (
488
497
  envelope: CanonicalRecordEnvelope,
489
498
  reporting: WorkerReportingMode,
490
- ): Effect.fn.Return<ReadonlyArray<WorkIndexChange>, ThreadStoreError, Crypto.Crypto> {
499
+ ): Effect.fn.Return<ReadonlyArray<WorkIndexChange>, ThreadStoreError> {
491
500
  const { record, sequence } = envelope;
492
501
  const payload = record.payload;
493
502
 
@@ -1,5 +1,5 @@
1
1
  import type { Scope } from "effect";
2
- import { Context, Deferred, Effect, Layer, Ref, Stream } from "effect";
2
+ import { Context, Deferred, Effect, Layer, Stream } from "effect";
3
3
 
4
4
  import { type ThreadId } from "../core/Identifiers.ts";
5
5
 
@@ -17,79 +17,47 @@ export interface WakeSubscriptionHub {
17
17
  readonly notify: (threadId: ThreadId) => Effect.Effect<void>;
18
18
  }
19
19
 
20
- interface WakeRegistration {
21
- readonly id: number;
22
- readonly deferred: Deferred.Deferred<void>;
23
- }
24
-
25
- type WakeRegistrations = ReadonlyMap<ThreadId, ReadonlyMap<number, Deferred.Deferred<void>>>;
26
-
27
20
  /**
28
21
  * Build the shared per-incarnation registration primitive used by host WakeSchedulers. The hub
29
22
  * contains hints only: eviction may discard it, while a reconnecting caller re-subscribes and
30
23
  * rechecks canonical storage. Scope finalizers remove cancelled waiters, and notify broadcasts
31
24
  * instead of consuming a signal that belongs to another waiter.
32
25
  */
33
- export const makeWakeSubscriptionHub: Effect.Effect<WakeSubscriptionHub> = Effect.gen(function* () {
34
- const registrations = yield* Ref.make<WakeRegistrations>(new Map());
35
- const nextId = yield* Ref.make(0);
36
-
37
- const remove = (threadId: ThreadId, id: number) =>
38
- Ref.update(registrations, (current) => {
39
- const existing = current.get(threadId);
40
-
41
- if (existing === undefined || !existing.has(id)) return current;
42
- const next = new Map(current);
43
- const thread = new Map(existing);
44
-
45
- thread.delete(id);
46
- if (thread.size === 0) {
47
- next.delete(threadId);
48
- } else {
49
- next.set(threadId, thread);
50
- }
51
-
52
- return next;
26
+ export const makeWakeSubscriptionHub: Effect.Effect<WakeSubscriptionHub> = Effect.sync(() => {
27
+ const registrations = new Map<ThreadId, Set<Deferred.Deferred<void>>>();
28
+
29
+ const subscribe = (threadId: ThreadId) =>
30
+ Effect.acquireRelease(
31
+ Effect.sync(() => {
32
+ const deferred = Deferred.makeUnsafe<void>();
33
+ let waiters = registrations.get(threadId);
34
+
35
+ if (waiters === undefined) {
36
+ waiters = new Set();
37
+ registrations.set(threadId, waiters);
38
+ }
39
+ waiters.add(deferred);
40
+
41
+ return deferred;
42
+ }),
43
+ (deferred) =>
44
+ Effect.sync(() => {
45
+ const waiters = registrations.get(threadId);
46
+
47
+ if (waiters?.delete(deferred) && waiters.size === 0) registrations.delete(threadId);
48
+ }),
49
+ ).pipe(Effect.map(Deferred.await));
50
+
51
+ const notify = (threadId: ThreadId) =>
52
+ Effect.sync(() => {
53
+ const waiters = registrations.get(threadId);
54
+
55
+ if (waiters === undefined) return;
56
+ // Detach before resuming: a resumed waiter may register its next wait immediately.
57
+ registrations.delete(threadId);
58
+ for (const waiter of waiters) Deferred.doneUnsafe(waiter, Effect.void);
53
59
  });
54
60
 
55
- const subscribe = Effect.fnUntraced(function* (
56
- threadId: ThreadId,
57
- ): Effect.fn.Return<Effect.Effect<void>, never, Scope.Scope> {
58
- const deferred = yield* Deferred.make<void>();
59
- const id = yield* Ref.getAndUpdate(nextId, (current) => current + 1);
60
- const registration: WakeRegistration = { id, deferred };
61
-
62
- yield* Effect.addFinalizer(() => remove(threadId, registration.id));
63
- yield* Ref.update(registrations, (current) => {
64
- const next = new Map(current);
65
- const thread = new Map(current.get(threadId) ?? []);
66
-
67
- thread.set(registration.id, registration.deferred);
68
- next.set(threadId, thread);
69
-
70
- return next;
71
- });
72
-
73
- return Deferred.await(registration.deferred);
74
- });
75
-
76
- const notify = Effect.fnUntraced(function* (threadId: ThreadId) {
77
- const waiters = yield* Ref.modify(registrations, (current) => {
78
- const thread = current.get(threadId);
79
-
80
- if (thread === undefined) return [[], current] as const;
81
- const next = new Map(current);
82
-
83
- next.delete(threadId);
84
-
85
- return [[...thread.values()], next] as const;
86
- });
87
-
88
- yield* Effect.forEach(waiters, (waiter) => Deferred.succeed(waiter, undefined), {
89
- discard: true,
90
- });
91
- });
92
-
93
61
  return { subscribe, notify };
94
62
  });
95
63
 
@@ -52,9 +52,13 @@ export const makeStorageOwner = Effect.fnUntraced(function* (gate: MutationGate)
52
52
  const active = new Map<string, { readonly claim: Claim; closed: boolean }>();
53
53
 
54
54
  const withMutation: MutationGate = (effect) =>
55
- Effect.flatMap(ActiveOwner, (current) =>
56
- current === identity ? effect : gate(Effect.provideService(effect, ActiveOwner, identity)),
57
- );
55
+ Effect.withFiber((fiber) => {
56
+ const current = fiber.getRef(ActiveOwner);
57
+
58
+ return current === identity
59
+ ? effect
60
+ : gate(Effect.provideService(effect, ActiveOwner, identity));
61
+ });
58
62
 
59
63
  yield* Effect.addFinalizer(() =>
60
64
  withMutation(
@@ -70,7 +74,9 @@ export const makeStorageOwner = Effect.fnUntraced(function* (gate: MutationGate)
70
74
  identity,
71
75
  withMutation,
72
76
  withProgress: (threadId, effect) =>
73
- Effect.flatMap(ActiveProgress, (current) => {
77
+ Effect.withFiber((fiber) => {
78
+ const current = fiber.getRef(ActiveProgress);
79
+
74
80
  if (current?.owner === identity && current.threadId === threadId) return effect;
75
81
 
76
82
  return Effect.suspend(() => {
@@ -105,6 +105,11 @@ export const toolReplayContracts = Effect.fnUntraced(function* (
105
105
  // different host Crypto service must never inherit another host's digest map.
106
106
  const crypto = Option.getOrUndefined(yield* Effect.serviceOption(Crypto.Crypto));
107
107
 
108
+ const invalidContracts = () =>
109
+ DigestError.make({
110
+ message: "Operation contracts require serializable codecs and a version for every Tool",
111
+ });
112
+
108
113
  const prepared = yield* Effect.try({
109
114
  try: () => {
110
115
  const tools = Object.values(definition.toolkit.tools);
@@ -146,16 +151,38 @@ export const toolReplayContracts = Effect.fnUntraced(function* (
146
151
  return projection;
147
152
  };
148
153
 
149
- const declarations = tools.map((tool) => {
150
- const version = versions === undefined ? fallbackVersion : versions.tools[tool.name];
154
+ return {
155
+ _tag: "Compile" as const,
156
+ jsonSchema,
157
+ cacheable,
158
+ tools,
159
+ completionTool,
160
+ completionFromTools,
161
+ };
162
+ },
163
+ catch: () =>
164
+ DigestError.make({
165
+ message: "Operation contracts require serializable codecs and a version for every Tool",
166
+ }),
167
+ });
168
+
169
+ if (prepared._tag === "Cached") return prepared.contracts;
151
170
 
152
- if (
153
- version === undefined ||
154
- (versions !== undefined && !Object.hasOwn(versions.tools, tool.name))
155
- )
156
- throw new Error("Every Tool needs an explicit replay semantic version");
171
+ const declarations = yield* Effect.forEach(
172
+ prepared.tools,
173
+ Effect.fnUntraced(function* (tool) {
174
+ const { version, present } = yield* Effect.try({
175
+ try: () => ({
176
+ version: versions === undefined ? fallbackVersion : versions.tools[tool.name],
177
+ present: versions === undefined || Object.hasOwn(versions.tools, tool.name),
178
+ }),
179
+ catch: invalidContracts,
180
+ });
157
181
 
158
- return {
182
+ if (version === undefined || !present) return yield* invalidContracts();
183
+
184
+ return yield* Effect.try({
185
+ try: () => ({
159
186
  name: tool.name,
160
187
  contract: {
161
188
  version,
@@ -170,9 +197,9 @@ export const toolReplayContracts = Effect.fnUntraced(function* (
170
197
  readonly: Context.get(tool.annotations, Tool.Readonly),
171
198
  }
172
199
  : {}),
173
- parameters: jsonSchema(tool.parametersSchema),
174
- success: jsonSchema(tool.successSchema),
175
- failure: jsonSchema(tool.failureSchema),
200
+ parameters: prepared.jsonSchema(tool.parametersSchema),
201
+ success: prepared.jsonSchema(tool.successSchema),
202
+ failure: prepared.jsonSchema(tool.failureSchema),
176
203
  failureMode: tool.failureMode,
177
204
  needsApproval:
178
205
  typeof tool.needsApproval === "function" ? "dynamic" : (tool.needsApproval ?? false),
@@ -185,28 +212,14 @@ export const toolReplayContracts = Effect.fnUntraced(function* (
185
212
  (declaration) => declaration.tool === tool.name,
186
213
  ) ?? false,
187
214
  },
188
- };
215
+ }),
216
+ catch: invalidContracts,
189
217
  });
190
-
191
- return {
192
- _tag: "Compile" as const,
193
- declarations,
194
- cacheable,
195
- tools,
196
- completionTool,
197
- completionFromTools,
198
- };
199
- },
200
- catch: () =>
201
- DigestError.make({
202
- message: "Operation contracts require serializable codecs and a version for every Tool",
203
- }),
204
- });
205
-
206
- if (prepared._tag === "Cached") return prepared.contracts;
218
+ }),
219
+ );
207
220
 
208
221
  const contracts = Object.fromEntries(
209
- yield* Effect.forEach(prepared.declarations, ({ name, contract }) =>
222
+ yield* Effect.forEach(declarations, ({ name, contract }) =>
210
223
  digestJson(contract).pipe(Effect.map((digest) => [name, digest] as const)),
211
224
  ),
212
225
  );
@@ -365,7 +378,7 @@ const omitTraceContext = Context.omit(
365
378
  const withoutTraceContext = <R>(context: Context.Context<R>): Context.Context<R> =>
366
379
  omitTraceContext(context) as Context.Context<R>;
367
380
 
368
- const captureReporting = <R>(reports: ReadonlyArray<WorkerReporting<unknown, R>>) =>
381
+ const captureReporting = <E, R>(reports: ReadonlyArray<WorkerReporting<E, R>>) =>
369
382
  Effect.map(
370
383
  Effect.context<Exclude<R, Scope.Scope>>().pipe(Effect.map(withoutTraceContext)),
371
384
  (context) =>
@@ -405,18 +418,19 @@ export interface AgentAttemptContext {
405
418
  readonly attemptId: Claim["attemptId"];
406
419
  }
407
420
 
408
- const capture = <A extends ExecutableAgentBinding, Provides = never, Requires = never>(
421
+ const capture = <A extends ExecutableAgentBinding, Requires>(
409
422
  agent: A,
410
- attemptLayer?: (context: AgentAttemptContext) => Layer.Layer<Provides, never, Requires>,
411
- ): Effect.Effect<
412
- CapturedBinding,
413
- never,
414
- Exclude<DurableWorkerRequirements<A>, Provides> | Requires
415
- > =>
423
+ prepare: (
424
+ execute: ReturnType<CapturedAttempt<A>>,
425
+ context: AgentAttemptContext,
426
+ ) => Effect.Effect<
427
+ Option.Option<Settlement>,
428
+ DurableWorkerFailure | DurableBindingFailure,
429
+ Requires
430
+ >,
431
+ ): Effect.Effect<CapturedBinding, never, Requires> =>
416
432
  Effect.map(
417
- Effect.context<Exclude<DurableWorkerRequirements<A>, Provides> | Requires>().pipe(
418
- Effect.map(withoutTraceContext),
419
- ),
433
+ Effect.context<Requires>().pipe(Effect.map(withoutTraceContext)),
420
434
  (context): CapturedBinding => ({
421
435
  agentId: agent.definition.id,
422
436
  definition: agent.definition,
@@ -433,29 +447,23 @@ const capture = <A extends ExecutableAgentBinding, Provides = never, Requires =
433
447
  claim.attemptId,
434
448
  );
435
449
 
436
- const scoped =
437
- attemptLayer === undefined
438
- ? execute
439
- : execute.pipe(
440
- Effect.provide(
441
- Layer.fresh(
442
- attemptLayer({
443
- threadId,
444
- submissionId: claim.submissionId,
445
- attemptId: claim.attemptId,
446
- }),
447
- ),
448
- ),
449
- );
450
-
451
- return scoped.pipe(Effect.provide(context)) as Effect.Effect<
452
- Option.Option<Settlement>,
453
- DurableWorkerFailure | DurableBindingFailure
454
- >;
450
+ return prepare(execute, {
451
+ threadId,
452
+ submissionId: claim.submissionId,
453
+ attemptId: claim.attemptId,
454
+ }).pipe(Effect.provide(context));
455
455
  },
456
456
  }),
457
457
  );
458
458
 
459
+ const captureWithAttemptLayer = <A extends ExecutableAgentBinding, Provides, Requires>(
460
+ agent: A,
461
+ attemptLayer: (context: AgentAttemptContext) => Layer.Layer<Provides, never, Requires>,
462
+ ) =>
463
+ capture(agent, (execute, context) =>
464
+ execute.pipe(Effect.provide(Layer.fresh(attemptLayer(context)))),
465
+ );
466
+
459
467
  /**
460
468
  * Build one exact worker registration from an executable Agent Binding and
461
469
  * its already-computed definition digests.
@@ -471,7 +479,7 @@ export const DurableWorkerBinding = {
471
479
  digests: DefinitionDigests,
472
480
  ): Effect.Effect<ResolvedBinding, never, DurableWorkerRequirements<A>> =>
473
481
  Effect.gen(function* () {
474
- const binding = yield* capture(agent);
482
+ const binding = yield* capture(agent, (execute) => execute);
475
483
  const reporting = yield* captureReporting(backgroundReports(agent.definition));
476
484
 
477
485
  return { ...binding, digests, reporting };
@@ -479,7 +487,8 @@ export const DurableWorkerBinding = {
479
487
  } as const;
480
488
 
481
489
  /** INTERNAL identity-only capture retained for the legacy direct worker path. */
482
- export const makeLegacyWorkerBinding = capture;
490
+ export const makeLegacyWorkerBinding = <A extends ExecutableAgentBinding>(agent: A) =>
491
+ capture(agent, (execute) => execute);
483
492
 
484
493
  export const resolveWorkerBinding = Effect.fnUntraced(function* (
485
494
  bindings: ReadonlyArray<ResolvedBinding>,
@@ -576,11 +585,9 @@ type EntryRequirements<Entry> = Entry extends unknown
576
585
  >
577
586
  : never;
578
587
 
579
- type RegistrationRequirements<Entries extends ReadonlyArray<AgentRegistration>> = [
580
- Entries[number],
581
- ] extends [never]
582
- ? never
583
- : EntryRequirements<Entries[number]>;
588
+ type RegistrationRequirements<Entries extends ReadonlyArray<AgentRegistration>> = EntryRequirements<
589
+ Entries[number]
590
+ >;
584
591
 
585
592
  const registrationDefinitions = (entry: AgentRegistration): DefinitionDigestInput => {
586
593
  const definition = entry.model === undefined ? entry.agent.definition : entry.agent;
@@ -655,10 +662,13 @@ const registrationDefinitions = (entry: AgentRegistration): DefinitionDigestInpu
655
662
  };
656
663
  };
657
664
 
658
- const compileRegistration = <Entry extends AgentRegistration>(
665
+ // The public signature distributes over descriptors, including optional attempt Layers.
666
+ // The implementation consumes their erased union and captures the selected Layer's context.
667
+ function compileRegistration<Entry extends AgentRegistration>(
659
668
  entry: Entry,
660
- ): Effect.Effect<ResolvedBinding, DigestError, Crypto.Crypto | EntryRequirements<Entry>> =>
661
- Effect.flatMap(
669
+ ): Effect.Effect<ResolvedBinding, DigestError, Crypto.Crypto | EntryRequirements<Entry>>;
670
+ function compileRegistration(entry: AgentRegistration) {
671
+ return Effect.flatMap(
662
672
  Effect.try({
663
673
  try: () => registrationDefinitions(entry),
664
674
  catch: () =>
@@ -666,10 +676,19 @@ const compileRegistration = <Entry extends AgentRegistration>(
666
676
  }),
667
677
  (definitions) =>
668
678
  Effect.gen(function* () {
669
- const binding = yield* capture(
670
- entry.model === undefined ? entry.agent : { definition: entry.agent, model: entry.model },
671
- entry.attemptLayer,
672
- );
679
+ const agent =
680
+ // The heterogeneous descriptor erases model services; the overload retains EntryRequirements.
681
+ // @effect-diagnostics-next-line anyUnknownInErrorContext:off
682
+ entry.model === undefined ? entry.agent : { definition: entry.agent, model: entry.model };
683
+
684
+ // Omitted attempt services need no Layer construction or provisioning scope.
685
+ const binding = yield* entry.attemptLayer === undefined
686
+ ? // Capture closes the erased requirements over the registration's actual Context.
687
+ // @effect-diagnostics-next-line anyUnknownInErrorContext:off
688
+ capture(agent, (execute) => execute)
689
+ : // The selected Layer discharges its services before the remaining Context is captured.
690
+ // @effect-diagnostics-next-line anyUnknownInErrorContext:off
691
+ captureWithAttemptLayer(agent, entry.attemptLayer);
673
692
 
674
693
  const reporting = yield* captureReporting(backgroundReports(binding.definition));
675
694
 
@@ -683,9 +702,8 @@ const compileRegistration = <Entry extends AgentRegistration>(
683
702
  reporting,
684
703
  };
685
704
  }),
686
- // The erased descriptor accepts arbitrary provided services. Restore the concrete entry's
687
- // Exclude<worker requirements, provided services> | layer requirements at this collection seam.
688
- ) as Effect.Effect<ResolvedBinding, DigestError, Crypto.Crypto | EntryRequirements<Entry>>;
705
+ );
706
+ }
689
707
 
690
708
  /** Compile heterogeneous Agent descriptors into exact, dependency-closed worker registrations. */
691
709
  export const compileRegistrations = <const Entries extends ReadonlyArray<AgentRegistration>>(
@@ -695,16 +713,5 @@ export const compileRegistrations = <const Entries extends ReadonlyArray<AgentRe
695
713
  DigestError,
696
714
  Crypto.Crypto | RegistrationRequirements<Entries>
697
715
  > => {
698
- // `Effect.forEach` instantiates a generic callback at its constraint. Specialize the callback to
699
- // this tuple's distributed requirement union before collection so empty and heterogeneous tuples
700
- // retain their exact service requirements.
701
- const compileEntry = compileRegistration as unknown as (
702
- entry: Entries[number],
703
- ) => Effect.Effect<
704
- ResolvedBinding,
705
- DigestError,
706
- Crypto.Crypto | RegistrationRequirements<Entries>
707
- >;
708
-
709
- return Effect.forEach(entries, compileEntry);
716
+ return Effect.forEach(entries, compileRegistration<Entries[number]>);
710
717
  };
@@ -1,4 +1,4 @@
1
- import { Array, type Schema } from "effect";
1
+ import { Array, Predicate, Result, Schema } from "effect";
2
2
 
3
3
  interface JsonLimits {
4
4
  readonly depth: number;
@@ -7,19 +7,36 @@ interface JsonLimits {
7
7
  readonly textUnits: number;
8
8
  }
9
9
 
10
+ export class CanonicalJsonLimitError extends Schema.TaggedError<CanonicalJsonLimitError>()(
11
+ "CanonicalJsonLimitError",
12
+ { message: Schema.String },
13
+ ) {}
14
+
15
+ // Only this codec's private synchronous aborts become typed bounds failures.
16
+ const boundsFailures = new WeakMap<object, string>();
17
+
18
+ const exceeded = (bound: "text" | "traversal" | "collection"): never => {
19
+ const message = `Canonical JSON exceeds its ${bound} bound`;
20
+ // oxlint-disable-next-line effect/no-native-error -- Private synchronous codec abort; the Result boundary translates only errors owned here.
21
+ const error = new RangeError(message);
22
+
23
+ boundsFailures.set(error, message);
24
+ // oxlint-disable-next-line effect/no-untyped-throw -- Abort synchronous JSON traversal at the first exceeded bound; the Result boundary catches this owned error.
25
+ throw error;
26
+ };
27
+
10
28
  /** Values have already crossed their Schema boundary; visit each serialized occurrence. */
11
- export const canonicalJson = (value: Schema.Json, limits?: JsonLimits): string => {
29
+ const serialize = (value: Schema.Json, limits?: JsonLimits): string => {
12
30
  let nodes = 0;
13
31
  let textUnits = 0;
14
32
 
15
33
  const charge = (length: number) => {
16
- if (limits !== undefined && (textUnits += length) > limits.textUnits)
17
- throw new RangeError("Canonical JSON exceeds its text bound");
34
+ if (limits !== undefined && (textUnits += length) > limits.textUnits) return exceeded("text");
18
35
  };
19
36
 
20
37
  const visit = (value: Schema.Json, depth: number): string => {
21
38
  if (limits !== undefined && (depth > limits.depth || ++nodes > limits.nodes))
22
- throw new RangeError("Canonical JSON exceeds its traversal bound");
39
+ return exceeded("traversal");
23
40
  if (
24
41
  value === null ||
25
42
  typeof value === "boolean" ||
@@ -34,30 +51,54 @@ export const canonicalJson = (value: Schema.Json, limits?: JsonLimits): string =
34
51
  }
35
52
  if (Array.isArray<Schema.Json>(value)) {
36
53
  if (limits !== undefined && value.length > limits.collectionLength)
37
- throw new RangeError("Canonical JSON exceeds its collection bound");
54
+ return exceeded("collection");
38
55
  charge(2 + Math.max(0, value.length - 1));
39
56
 
40
- return `[${globalThis.Array.from(value, (entry) => visit(entry, depth + 1)).join(",")}]`;
57
+ let json = "[";
58
+
59
+ for (let index = 0; index < value.length; index++) {
60
+ if (index > 0) json += ",";
61
+ json += visit(value[index], depth + 1);
62
+ }
63
+
64
+ return `${json}]`;
41
65
  }
42
66
 
43
- const entries = Object.entries(value).sort(([left], [right]) =>
44
- left < right ? -1 : left > right ? 1 : 0,
45
- );
67
+ const keys = Object.keys(value).sort();
46
68
 
47
- if (limits !== undefined && entries.length > limits.collectionLength)
48
- throw new RangeError("Canonical JSON exceeds its collection bound");
49
- charge(2 + Math.max(0, entries.length - 1));
69
+ if (limits !== undefined && keys.length > limits.collectionLength)
70
+ return exceeded("collection");
71
+ charge(2 + Math.max(0, keys.length - 1));
72
+ let json = "{";
50
73
 
51
- return `{${entries
52
- .map(([key, entry]) => {
53
- const encodedKey = JSON.stringify(key);
74
+ for (let index = 0; index < keys.length; index++) {
75
+ const key = keys[index];
76
+ const encodedKey = JSON.stringify(key);
54
77
 
55
- charge(encodedKey.length + 1);
78
+ charge(encodedKey.length + 1);
79
+ if (index > 0) json += ",";
80
+ json += `${encodedKey}:${visit(value[key], depth + 1)}`;
81
+ }
56
82
 
57
- return `${encodedKey}:${visit(entry, depth + 1)}`;
58
- })
59
- .join(",")}}`;
83
+ return `${json}}`;
60
84
  };
61
85
 
62
86
  return visit(value, 0);
63
87
  };
88
+
89
+ export const canonicalJson = (value: Schema.Json): string => serialize(value);
90
+
91
+ export const boundedCanonicalJson = (
92
+ value: Schema.Json,
93
+ limits: JsonLimits,
94
+ ): Result.Result<string, CanonicalJsonLimitError> => {
95
+ try {
96
+ return Result.succeed(serialize(value, limits));
97
+ } catch (cause) {
98
+ const message = Predicate.isObjectKeyword(cause) ? boundsFailures.get(cause) : undefined;
99
+
100
+ if (message !== undefined) return Result.fail(CanonicalJsonLimitError.make({ message }));
101
+ // oxlint-disable-next-line effect/no-untyped-throw -- Foreign JSON/getter errors retain their identity for the caller's typed capture boundary.
102
+ throw cause;
103
+ }
104
+ };
@@ -1,4 +1,4 @@
1
- import { Effect, Option, Schema, Stream } from "effect";
1
+ import { Effect, Option, Result, Schema, Stream } from "effect";
2
2
  import { Prompt } from "effect/ai";
3
3
 
4
4
  import type { RunId, ThreadId } from "../../core/Identifiers.ts";
@@ -87,7 +87,8 @@ function hydratePart(part: Prompt.PartEncoded): Prompt.Part {
87
87
  case "tool-approval-response":
88
88
  return Prompt.toolApprovalResponsePart(part);
89
89
  }
90
- throw new Error(`Unhandled Prompt part: ${part satisfies never}`);
90
+
91
+ return part satisfies never;
91
92
  }
92
93
 
93
94
  const hydrateMessage = (message: Prompt.MessageEncoded): Prompt.Message => {
@@ -116,7 +117,8 @@ const hydrateMessage = (message: Prompt.MessageEncoded): Prompt.Message => {
116
117
  content: message.content.map((p) => hydratePart(p)),
117
118
  });
118
119
  }
119
- throw new Error(`Unhandled Prompt message: ${message satisfies never}`);
120
+
121
+ return message satisfies never;
120
122
  };
121
123
 
122
124
  export interface PromptHistory {
@@ -291,7 +293,14 @@ export const makeCommittedPromptSuffix = (original: CanonicalRecordEnvelope) =>
291
293
  // as readPrompt, never raw provider values. Failure invalidates this derivative, not the commit.
292
294
  try {
293
295
  for (const fact of facts) {
294
- const owned = captureRecord(fact);
296
+ const captured = captureRecord(fact);
297
+
298
+ if (Result.isFailure(captured)) {
299
+ discard();
300
+
301
+ return;
302
+ }
303
+ const owned = captured.success;
295
304
  const payload = owned.canonical.payload;
296
305
 
297
306
  if (