iterate 0.3.0 → 0.4.0

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 (241) hide show
  1. package/README.md +164 -82
  2. package/dist/{next/api.d.ts → api.d.ts} +197 -33
  3. package/dist/{next/app-server.d.ts → app-server.d.ts} +7 -0
  4. package/dist/{next/app-server.mjs → app-server.mjs} +17 -15
  5. package/dist/app-server.mjs.map +1 -0
  6. package/dist/{next/app-session.mjs → app-session.mjs} +12 -15
  7. package/dist/app-session.mjs.map +1 -0
  8. package/dist/{next/app.mjs → app.mjs} +53 -14
  9. package/dist/app.mjs.map +1 -0
  10. package/dist/{next/client → client}/live-state.d.ts +11 -11
  11. package/dist/client/oauth.d.ts +17 -0
  12. package/dist/{next/client → client}/react.d.ts +10 -42
  13. package/dist/{next/client → client}/socket.d.ts +1 -0
  14. package/dist/client.mjs +156 -4
  15. package/dist/client.mjs.map +1 -0
  16. package/dist/{next/expression.d.ts → expression.d.ts} +11 -69
  17. package/dist/{next/expression.mjs → expression.mjs} +11 -109
  18. package/dist/expression.mjs.map +1 -0
  19. package/dist/lib-BWr-5mFO.mjs +36 -0
  20. package/dist/lib-BWr-5mFO.mjs.map +1 -0
  21. package/dist/{next/lib.d.ts → lib.d.ts} +16 -2
  22. package/dist/{next/lib.mjs → lib.mjs} +32 -3
  23. package/dist/lib.mjs.map +1 -0
  24. package/dist/node.d.ts +15 -3
  25. package/dist/node.mjs +36 -174
  26. package/dist/node.mjs.map +1 -1
  27. package/dist/{next/oauth-scopes.mjs → oauth-scopes.mjs} +1 -1
  28. package/dist/oauth-scopes.mjs.map +1 -0
  29. package/dist/{next/oauth.mjs → oauth.mjs} +14 -2
  30. package/dist/oauth.mjs.map +1 -0
  31. package/dist/principal.d.ts +8 -0
  32. package/dist/principal.mjs +8 -0
  33. package/dist/principal.mjs.map +1 -0
  34. package/dist/project-ingress.d.ts +58 -0
  35. package/dist/project-ingress.mjs +104 -0
  36. package/dist/project-ingress.mjs.map +1 -0
  37. package/dist/{next/react.mjs → react.mjs} +12 -12
  38. package/dist/react.mjs.map +1 -0
  39. package/dist/sdk/auth.d.ts +25 -0
  40. package/dist/sdk/index.d.ts +155 -0
  41. package/dist/sdk/record-pipelined-steps.d.ts +19 -0
  42. package/dist/sdk.mjs +245 -2
  43. package/dist/sdk.mjs.map +1 -0
  44. package/dist/{next/stream → stream}/processor.d.ts +28 -23
  45. package/dist/{next/stream → stream}/processor.mjs +48 -25
  46. package/dist/stream/processor.mjs.map +1 -0
  47. package/dist/{next/stream → stream}/run.d.ts +9 -6
  48. package/dist/{next/stream → stream}/run.mjs +13 -8
  49. package/dist/stream/run.mjs.map +1 -0
  50. package/dist/stream/test-support.d.ts +45 -0
  51. package/dist/stream/test-support.mjs +196 -0
  52. package/dist/stream/test-support.mjs.map +1 -0
  53. package/package.json +65 -219
  54. package/THIRD_PARTY_NOTICES.md +0 -55
  55. package/bin/iterate.js +0 -94
  56. package/dist/api-url-B6404M82.mjs +0 -17
  57. package/dist/api-url-B6404M82.mjs.map +0 -1
  58. package/dist/app-ref-BipL0feU.mjs +0 -35
  59. package/dist/app-ref-BipL0feU.mjs.map +0 -1
  60. package/dist/app-ref-C1CrgXqX.mjs +0 -7
  61. package/dist/app-ref-C1CrgXqX.mjs.map +0 -1
  62. package/dist/app-ref-DYai_om1.mjs +0 -7
  63. package/dist/app-ref-DYai_om1.mjs.map +0 -1
  64. package/dist/cli-D0c-pDL_.mjs +0 -1010
  65. package/dist/cli-D0c-pDL_.mjs.map +0 -1
  66. package/dist/client.d.ts +0 -3
  67. package/dist/cloudflare-BTm90gQ4.mjs +0 -951
  68. package/dist/cloudflare-BTm90gQ4.mjs.map +0 -1
  69. package/dist/contract-s4FW4eES.mjs +0 -309
  70. package/dist/contract-s4FW4eES.mjs.map +0 -1
  71. package/dist/document-review/index.d.ts +0 -5
  72. package/dist/document-review/types.d.ts +0 -107
  73. package/dist/document-review.mjs +0 -7015
  74. package/dist/document-review.mjs.map +0 -1
  75. package/dist/durable-object-processor-durability-CNsTjAJS.mjs +0 -205
  76. package/dist/durable-object-processor-durability-CNsTjAJS.mjs.map +0 -1
  77. package/dist/idempotency-DleloJNt.mjs +0 -28
  78. package/dist/idempotency-DleloJNt.mjs.map +0 -1
  79. package/dist/index.d.mts +0 -5
  80. package/dist/index.mjs +0 -8
  81. package/dist/index.mjs.map +0 -1
  82. package/dist/itx/api-url.d.ts +0 -6
  83. package/dist/itx/itx-node-client.d.ts +0 -65
  84. package/dist/itx/itx-session.d.ts +0 -215
  85. package/dist/itx/owned-rpc-session.d.ts +0 -14
  86. package/dist/itx/query-client.d.ts +0 -10
  87. package/dist/itx-api.generated.d.ts +0 -6195
  88. package/dist/itx-session-sjud8GiT.mjs +0 -534
  89. package/dist/itx-session-sjud8GiT.mjs.map +0 -1
  90. package/dist/live-state-BJNqOwFw.mjs +0 -299
  91. package/dist/live-state-BJNqOwFw.mjs.map +0 -1
  92. package/dist/next/app-server.mjs.map +0 -1
  93. package/dist/next/app-session.mjs.map +0 -1
  94. package/dist/next/app.mjs.map +0 -1
  95. package/dist/next/client/oauth.d.ts +0 -12
  96. package/dist/next/client.mjs +0 -156
  97. package/dist/next/client.mjs.map +0 -1
  98. package/dist/next/expression.mjs.map +0 -1
  99. package/dist/next/lib.mjs.map +0 -1
  100. package/dist/next/oauth-scopes.mjs.map +0 -1
  101. package/dist/next/oauth.mjs.map +0 -1
  102. package/dist/next/principal.d.ts +0 -64
  103. package/dist/next/principal.mjs +0 -98
  104. package/dist/next/principal.mjs.map +0 -1
  105. package/dist/next/project-ingress.d.ts +0 -37
  106. package/dist/next/project-ingress.mjs +0 -75
  107. package/dist/next/project-ingress.mjs.map +0 -1
  108. package/dist/next/react.mjs.map +0 -1
  109. package/dist/next/sdk/auth.d.ts +0 -5
  110. package/dist/next/sdk/index.d.ts +0 -112
  111. package/dist/next/sdk.mjs +0 -139
  112. package/dist/next/sdk.mjs.map +0 -1
  113. package/dist/next/stream/processor.mjs.map +0 -1
  114. package/dist/next/stream/run.mjs.map +0 -1
  115. package/dist/next-node.d.ts +0 -15
  116. package/dist/next-node.mjs +0 -51
  117. package/dist/next-node.mjs.map +0 -1
  118. package/dist/processor-host-capabilities-BMFH3KTM.mjs +0 -56
  119. package/dist/processor-host-capabilities-BMFH3KTM.mjs.map +0 -1
  120. package/dist/processors/cloudflare.d.ts +0 -3
  121. package/dist/processors/durable-object-processor-durability.d.ts +0 -79
  122. package/dist/processors/event-consumption-metrics.d.ts +0 -82
  123. package/dist/processors/idempotency.d.ts +0 -13
  124. package/dist/processors/index.d.ts +0 -12
  125. package/dist/processors/processor-contracts.d.ts +0 -342
  126. package/dist/processors/processor-facet.d.ts +0 -186
  127. package/dist/processors/processor-host-capabilities.d.ts +0 -60
  128. package/dist/processors/prompt-sections.d.ts +0 -17
  129. package/dist/processors/rpc-types.d.ts +0 -515
  130. package/dist/processors/schemas.d.ts +0 -102
  131. package/dist/processors/stream-handle.d.ts +0 -45
  132. package/dist/processors/stream-processor-keepalive.d.ts +0 -95
  133. package/dist/processors/stream-processor-registry.d.ts +0 -233
  134. package/dist/processors/stream-processor-runner.d.ts +0 -289
  135. package/dist/processors/stream-processor.d.ts +0 -339
  136. package/dist/processors/stream-runtime-metrics.d.ts +0 -107
  137. package/dist/processors/testing.d.ts +0 -302
  138. package/dist/processors-BoNyeBfQ.mjs +0 -10
  139. package/dist/processors-BoNyeBfQ.mjs.map +0 -1
  140. package/dist/processors-cloudflare.mjs +0 -3
  141. package/dist/processors-testing.mjs +0 -435
  142. package/dist/processors-testing.mjs.map +0 -1
  143. package/dist/processors.mjs +0 -52
  144. package/dist/processors.mjs.map +0 -1
  145. package/dist/protocol-DnK_f2m6.mjs +0 -251
  146. package/dist/protocol-DnK_f2m6.mjs.map +0 -1
  147. package/dist/sdk/capnweb/index.d.ts +0 -2
  148. package/dist/sdk/capnweb/live-state/compact.d.ts +0 -5
  149. package/dist/sdk/capnweb/live-state/diff.d.ts +0 -41
  150. package/dist/sdk/capnweb/live-state/engine.d.ts +0 -44
  151. package/dist/sdk/capnweb/live-state/index.d.ts +0 -41
  152. package/dist/sdk/capnweb/live-state/protocol.d.ts +0 -87
  153. package/dist/sdk/capnweb/live-state/retain.d.ts +0 -23
  154. package/dist/sdk/capnweb/live-state/store.d.ts +0 -20
  155. package/dist/sdk/capnweb/live-state/types.d.ts +0 -11
  156. package/dist/sdk/capnweb/react.d.ts +0 -45
  157. package/dist/sdk/capnweb/react.mjs +0 -316
  158. package/dist/sdk/capnweb/react.mjs.map +0 -1
  159. package/dist/sdk/capnweb.mjs +0 -4
  160. package/dist/sdk/itx/react.d.ts +0 -191
  161. package/dist/sdk/itx/react.mjs +0 -383
  162. package/dist/sdk/itx/react.mjs.map +0 -1
  163. package/dist/sdk-DMB-IM11.mjs +0 -933
  164. package/dist/sdk-DMB-IM11.mjs.map +0 -1
  165. package/dist/sdk.d.ts +0 -339
  166. package/dist/serve-itx.d.ts +0 -46
  167. package/dist/starter-apps/flake-dashboard/app-ref.d.ts +0 -31
  168. package/dist/starter-apps/flake-dashboard/configured-worker.mjs +0 -1055
  169. package/dist/starter-apps/flake-dashboard/configured-worker.mjs.map +0 -1
  170. package/dist/starter-apps/flake-dashboard/contract.d.ts +0 -4839
  171. package/dist/starter-apps/flake-dashboard/contract.mjs +0 -2
  172. package/dist/starter-apps/flake-dashboard/index.d.ts +0 -17
  173. package/dist/starter-apps/flake-dashboard/index.mjs +0 -56
  174. package/dist/starter-apps/flake-dashboard/index.mjs.map +0 -1
  175. package/dist/starter-apps/flake-dashboard/worker.d.ts +0 -4607
  176. package/dist/starter-apps/github-ai-linter/ai-linter.d.ts +0 -8914
  177. package/dist/starter-apps/github-ai-linter/configured-worker.mjs +0 -17987
  178. package/dist/starter-apps/github-ai-linter/configured-worker.mjs.map +0 -1
  179. package/dist/starter-apps/github-ai-linter/contract.d.ts +0 -9193
  180. package/dist/starter-apps/github-ai-linter/index.d.ts +0 -10
  181. package/dist/starter-apps/github-ai-linter/index.mjs +0 -36
  182. package/dist/starter-apps/github-ai-linter/index.mjs.map +0 -1
  183. package/dist/starter-apps/github-ai-linter/prompt.d.ts +0 -13
  184. package/dist/starter-apps/github-ai-linter/review-bot.d.ts +0 -808
  185. package/dist/starter-apps/github-ai-linter/rules.d.ts +0 -34
  186. package/dist/starter-apps/github-ai-linter/worker-ref.d.ts +0 -19
  187. package/dist/starter-apps/github-ai-linter/worker.d.ts +0 -19
  188. package/dist/starter-apps/github-ai-linter/worker.mjs +0 -947
  189. package/dist/starter-apps/github-ai-linter/worker.mjs.map +0 -1
  190. package/dist/starter-apps/guestbook/app-ref.d.ts +0 -27
  191. package/dist/starter-apps/guestbook/client.d.ts +0 -7
  192. package/dist/starter-apps/guestbook/client.mjs +0 -59
  193. package/dist/starter-apps/guestbook/configured-worker.mjs +0 -205
  194. package/dist/starter-apps/guestbook/configured-worker.mjs.map +0 -1
  195. package/dist/starter-apps/guestbook/index.d.ts +0 -9
  196. package/dist/starter-apps/guestbook/index.mjs +0 -31
  197. package/dist/starter-apps/guestbook/index.mjs.map +0 -1
  198. package/dist/starter-apps/guestbook/processor.d.ts +0 -2267
  199. package/dist/starter-apps/guestbook/worker.d.ts +0 -26
  200. package/dist/starter-apps/guestbook/worker.mjs +0 -191
  201. package/dist/starter-apps/guestbook/worker.mjs.map +0 -1
  202. package/dist/starter-apps/media/configured-worker.mjs +0 -577
  203. package/dist/starter-apps/media/configured-worker.mjs.map +0 -1
  204. package/dist/starter-apps/media/index.mjs +0 -36
  205. package/dist/starter-apps/media/index.mjs.map +0 -1
  206. package/dist/starter-apps/media/ref.mjs +0 -20
  207. package/dist/starter-apps/media/ref.mjs.map +0 -1
  208. package/dist/starter-apps/media/worker.mjs +0 -579
  209. package/dist/starter-apps/media/worker.mjs.map +0 -1
  210. package/dist/starter-apps/notes/configured-worker.mjs +0 -6134
  211. package/dist/starter-apps/notes/configured-worker.mjs.map +0 -1
  212. package/dist/starter-apps/notes/index.mjs +0 -23
  213. package/dist/starter-apps/notes/index.mjs.map +0 -1
  214. package/dist/starter-apps/notes/ref.mjs +0 -21
  215. package/dist/starter-apps/notes/ref.mjs.map +0 -1
  216. package/dist/starter-apps/notes/worker.mjs +0 -427
  217. package/dist/starter-apps/notes/worker.mjs.map +0 -1
  218. package/dist/starter-apps/todo/client.mjs +0 -59
  219. package/dist/starter-apps/todo/configured-worker.mjs +0 -2864
  220. package/dist/starter-apps/todo/configured-worker.mjs.map +0 -1
  221. package/dist/starter-apps/todo/index.d.ts +0 -8
  222. package/dist/starter-apps/todo/index.mjs +0 -29
  223. package/dist/starter-apps/todo/index.mjs.map +0 -1
  224. package/dist/stream-processor-keepalive-DAQTP6m3.mjs +0 -2082
  225. package/dist/stream-processor-keepalive-DAQTP6m3.mjs.map +0 -1
  226. package/dist/usingCtx-mZx5nsAW.mjs +0 -11800
  227. package/dist/usingCtx-mZx5nsAW.mjs.map +0 -1
  228. package/dist/worker-ref-DZxPDmb_.mjs +0 -390
  229. package/dist/worker-ref-DZxPDmb_.mjs.map +0 -1
  230. package/dist/worker.d.mts +0 -33
  231. package/dist/worker.mjs +0 -18
  232. package/dist/worker.mjs.map +0 -1
  233. package/menubar/Iterate.entitlements +0 -12
  234. package/menubar/Iterate.swift +0 -914
  235. package/menubar/IterateIcon.swift +0 -145
  236. package/menubar/README.md +0 -28
  237. package/menubar/build-menubar-app.sh +0 -59
  238. /package/dist/{next/api.mjs → api.mjs} +0 -0
  239. /package/dist/{next/app-session.d.ts → app-session.d.ts} +0 -0
  240. /package/dist/{next/app.d.ts → app.d.ts} +0 -0
  241. /package/dist/{next/oauth-scopes.d.ts → oauth-scopes.d.ts} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"stream-processor-keepalive-DAQTP6m3.mjs","names":["#capacity","#samples","#next","#last","#lastAt","#seconds","#counts","#bytes","#measuredSinceMs","#measuredSinceMs","#consumeOwnAppend","#appendRoundTrip","#deliveryAge","#ingest","#ingestedThroughOffset","#pendingOwnAppends","#batchesIngested","#eventsIngested","#clockOffsetMs","StreamEventSchema","StreamEventInputSchema","#keepAliveWhile","#reduceRawEvent","#isDeliverable","#processorStamp","#appendStamped","#appendTarget","#parseConsumedEvent","#buildEmittedEvent","#appendBuiltEvents","#eventWaiters","#stateChangeObservers","#assertNotDisposed","#enqueue","#readCurrentStreamId","#load","#requireProgress","#eventBatchCallback","#prepareHostedCheckpoint","#loadPreparedStream","#processBatch","#runInBackground","#selfCatchUp","#hasLoaded","#progress","#defaultState","#registerEventWaiter","#disposed","#settleEventWaiter","#highestObservedOffset","#keepAliveBackedWork","#commitBatchContext","#commit","#notifyStateChange","#resolveEventWaiters","#loaded","#loadingStreamId","#freshProgress","#loadWithStreamReplacement","#loadOnce","#rebuildReduction","#assertReadStreamId","#chain","#hooks","#inFlight","#ensureArmedForWork","#sawCleanSettle","#busyRefires","#sawFailure","#reviving","#arm","#disarmAndReset","#revive","#lastReassertAtMs"],"sources":["../src/processors/rpc-types.ts","../src/processors/stream-handle.ts","../src/processors/stream-runtime-metrics.ts","../src/processors/event-consumption-metrics.ts","../src/processors/schemas.ts","../src/processors/processor-contracts.ts","../src/processors/stream-processor.ts","../src/processors/stream-processor-runner.ts","../src/processors/stream-processor-keepalive.ts"],"sourcesContent":["/**\n * The stream + processor RPC surface: stored subscriptions, live connections, the\n * processor state-push contract, and the batch envelope sent to callbacks.\n * These are hand-authored shapes (generics preserved) that both the public itx\n * contract and the server-side processor and connection code build against.\n */\nimport type { StatefulDynamicWorkerRef } from \"../itx-api.generated.ts\";\nimport type { StreamEvent } from \"./schemas.ts\";\n\n/** Maximum serialized durable-event bytes one Stream read may return. */\nexport const MAX_STREAM_EVENT_READ_BYTE_LIMIT = 8 * 1024 * 1024;\n\n/** Source-local identity for one durable subscription that sends matching stream events. */\nexport type SubscriptionName = string;\n\n/** Stable identity for one live connection to a processEventBatch callback. */\nexport type ConnectionKey = string;\n\n/** The read window accepted by `Stream.getEvents` / `Stream.readEvents`. */\nexport type StreamEventReadInput = {\n /** Exclusive lower bound. Defaults to 0. */\n afterOffset?: number;\n /** Exclusive upper bound. Omit/null to read through the latest offset. */\n beforeOffset?: number | null;\n /** Event types to include. Omit or include \"*\" for all; [] matches none. */\n eventTypes?: readonly string[];\n /** Page size, 1-500. Defaults to 500. */\n limit?: number;\n /**\n * Maximum serialized durable-event bytes in one page, from 1 through 8 MiB.\n * It cannot be used with `includeEphemeral`. A shorter nonempty page may\n * still have more matching events; advance from its last offset until an\n * empty page confirms the current head. The first matching event is returned\n * even when it alone exceeds this cap, so the cursor can make progress.\n */\n byteLimit?: number;\n /**\n * Include ephemeral events (default false). The Durable Object incarnation\n * keeps their bodies in a bounded memory buffer;\n * this opt-in merges the events still buffered into the durable page.\n * Restart and FIFO eviction leave permanent offset gaps, so never derive\n * durable state from an ephemeral event.\n */\n includeEphemeral?: boolean;\n};\n\n/** One consistent read of a processor (what `snapshot()` returns): the folded\n * state pinned to the offset of the last event folded into it. */\nexport type ProcessorSnapshot<State> = {\n offset: number;\n state: State;\n};\n\n/**\n * The internal extension used when stream delivery calls a hosted processor.\n * Public processor properties expose only {@link StreamProcessorRpc}; a stored\n * subscription persists an ITX expression that continues one\n * step past that public property to this trusted-only method:\n * `[\"agents\", [\"get\", path], \"processor\", \"wakeStreamProcessor\"]`.\n *\n * `wakeStreamProcessor` is called by trusted stream delivery only\n * (trusted-internal): its processEventBatch callback drives the host's durable\n * checkpoint, so an ordinary session poking it could feed fabricated batches\n * and fast-forward the checkpoint past real events. Multi-processor hosts (an\n * agent stream hosts agent + slack-agent + more) resolve WHICH processor\n * wakes from the request's `name` (which equals the contract slug). Each\n * public domain surface selects that same named processor for inspection,\n * while deliberately omitting this method from its public TypeScript\n * contract, so `agent.processor`, `agent.slack.processor`, and other siblings\n * expose their own snapshots and checkpoints.\n */\nexport type WakeableStreamProcessorRpc<State = unknown> = StreamProcessorRpc<State> & {\n wakeStreamProcessor(request: StreamProcessorWakeRequest): Promise<StreamProcessorWakeResponse>;\n};\n\n/**\n * The read-side RPC surface every stream processor node exposes: inspect\n * runtime state (snapshot plus a processor-specific runtime bag), take an\n * offset-pinned `snapshot()` of the folded state, and `waitUntilProcessed` to\n * block until the processor has durably folded through a given offset.\n */\nexport interface StreamProcessorRpc<State = unknown> {\n getRuntimeState(): Promise<ProcessorRuntimeState<State>>;\n snapshot(): Promise<ProcessorSnapshot<State>>;\n waitUntilProcessed(input: { offset: number; timeoutMs?: number }): Promise<void>;\n}\n\n/**\n * Live handle for one live-state subscription. `ping()` reports liveness (and\n * the call rejects when the hosting incarnation is gone); `unsubscribe()` closes it.\n */\nexport type { LiveStateRpc, LiveStateSubscriptionHandle } from \"../sdk/capnweb/live-state/types.ts\";\n\n/**\n * A node's live state — a source-agnostic reactive value. `get()` reads it once;\n * `subscribe()` opens a channel that pushes a full snapshot then minimal diffs\n * (see `lib/live-state`), which the React `useLiveState` hook reassembles so\n * components pick only the slice they render. ANY RpcTarget can expose one: a\n * Durable Object over its folded state, or a stateless worker over state it\n * computes or fetches.\n *\n * Deliberately READ-ONLY over the wire: the server DERIVES this state (a DO\n * reassembles it from its fold), so writes go through the node's own verbs —\n * events appended, mutations called — never a generic `set`. A wire-level\n * `set`/`assign` would let any principal that can reach the node broadcast\n * fabricated state to every live-state listener.\n */\n/**\n * Batch delivered to stream processors and live connections.\n *\n * Kept named because callback retention, processor hosts, and tests all depend\n * on the same cross-RPC batch envelope.\n */\nexport type StreamEventBatch = {\n projectId: string | null;\n path: string;\n /** Random identity of this event log; changes when the stream is recreated. */\n streamId: string;\n events: StreamEvent[];\n /** Exclusive raw-log cursor from which this delivery scan began. */\n scannedAfterOffset: number;\n /** Inclusive raw-log cursor through which this delivery scan completed. */\n scannedThroughOffset: number;\n streamMaxOffset: number;\n /** Reduced core state, or null when the connection opts out with `state: false`. */\n state: unknown;\n};\n\n/**\n * One atomic stream read: the matching events plus the identity and raw-log\n * head they were read from. Consumers that persist offsets must use this\n * envelope instead of pairing `getEvents()` with a separate state read — a\n * reset between those calls would make equal offsets name a different log.\n */\nexport type StreamEventPage = {\n /** Random identity of the event log that served this page. */\n streamId: string;\n /** Highest assigned raw-log offset when this page was read. */\n streamMaxOffset: number;\n events: StreamEvent[];\n};\n\n/**\n * Callback invoked by the stream send loop for each delivered batch.\n *\n * It stays as a named type because Workers RPC callback lifecycle helpers need\n * to duplicate, retain, and dispose exactly this callback shape.\n */\nexport type ProcessEventBatch = (batch: StreamEventBatch) => unknown;\n\n/**\n * Serializable failure reported after a durable wake delivery finishes.\n *\n * The result crosses an independent one-way RPC hop, so preserve the lifecycle\n * flags and ITX call correlation that distinguish and locate failures. Error\n * prototypes and arbitrary properties do not survive that hop reliably.\n */\nexport type StreamWakeDeliveryError = {\n name: string;\n message: string;\n /** Wide-log ID of the failed ITX call, when the failure crossed that boundary. */\n itxCallId?: string;\n durableObjectReset?: true;\n overloaded?: true;\n retryable?: true;\n};\n\n/** The hosted processor's final result for one durable wake delivery. */\nexport type StreamWakeDeliveryResult =\n | { outcome: \"ok\" }\n | { outcome: \"error\"; error: StreamWakeDeliveryError };\n\n/**\n * One-shot acknowledgement capability owned by a single durable wake batch.\n *\n * It is deliberately independent of the callback call's return value. A\n * processor may append back to the stream that delivered the batch; making\n * the stream await that return value can make two Durable Objects wait for\n * each other forever.\n */\nexport type ReportStreamWakeDeliveryResult = (result: StreamWakeDeliveryResult) => unknown;\n\n/** Internal hosted-processor frame: an ordinary batch plus its one-shot completion callback. */\nexport type StreamWakeEventBatch = StreamEventBatch & {\n reportDeliveryResult: ReportStreamWakeDeliveryResult;\n};\n\n/** Hosted processor callback. Its call result is always disposed without being awaited. */\nexport type ProcessStreamWakeEventBatch = (batch: StreamWakeEventBatch) => unknown;\n\n/**\n * The committed subscription fields carried with a delivery whose cursor the\n * source stream stores. This deliberately omits metadata, provenance, and\n * idempotency bookkeeping that the core reducer does not retain. A receiver\n * uses the included event coordinates and payload to verify the delivery\n * against the subscription it recorded.\n */\nexport type SubscriptionConfigurationForDelivery = {\n type: \"events.iterate.com/stream/subscription-configured\";\n offset: number;\n createdAt: string;\n path: string;\n payload: {\n /** The subscription's caller-chosen name; omitted when the effective name\n * is derived from this event's offset (`subscription:<offset>`). */\n name?: string;\n description?: string;\n filter?: {\n eventTypes?: string[];\n jsonataCondition?: string;\n };\n receiver:\n | {\n /** The processor runs as a facet of the stream's own Durable Object:\n * the subscription NAME is the facet name and the registered-contract\n * selector; delivery is an in-process parent→facet dial (no wake\n * lane). `source` chooses the class — `builtin` (resolved by the\n * stream's path-family registration) or `userspace` (the DurableObject\n * class is loaded from `worker` and hosted as a facet). */\n action: \"facet-processor\";\n source: { kind: \"builtin\" } | { kind: \"userspace\"; worker: StatefulDynamicWorkerRef };\n }\n | {\n /** The processor runs in ANOTHER Durable Object, woken by dialing this\n * itx expression (own-DO or userspace-worker placement). */\n action: \"wake-processor\";\n expression: Array<string | [method: string, ...args: unknown[]]>;\n }\n | {\n action: \"copy-to-stream\";\n receivingStreamPath: string;\n jsonataTransform?: string;\n delivery: {\n start: \"beginning\" | \"now\";\n onFailingEvent: \"halt\";\n };\n }\n | {\n action: \"itx-call\";\n expression: Array<string | [method: string, ...args: unknown[]]>;\n jsonataTransform?: string;\n delivery: {\n start: \"beginning\" | \"now\";\n onFailingEvent: \"halt\" | \"skip\";\n };\n }\n | {\n action: \"webhook-post\";\n url: string;\n jsonataTransform?: string;\n delivery: {\n start: \"beginning\" | \"now\";\n onFailingEvent: \"halt\" | \"skip\";\n };\n };\n };\n};\n\n/**\n * The batch sent to a durable receiver for a subscription whose cursor the\n * source stream stores: delivery coordinates and events plus the fields an\n * at-least-once receiver needs to deduplicate and self-configure. Deliberately\n * not the state-carrying callback batch\n * {@link StreamEventBatch}: ITX calls and copy destinations do not get\n * folded core state, because other subscriptions' configuration, halt errors,\n * and the presence roster are deployment-internal. Webhooks use a narrower\n * per-event envelope for the same reason. Session callbacks and hosted\n * processors still get state-carrying batches because they paint or reduce\n * from stream state.\n */\nexport type StreamDeliveryBatch = {\n projectId: string | null;\n path: string;\n /** Random identity assigned when this source stream's storage was created. */\n streamId: string;\n /** Creation time of this source stream; orders recreated streams whose offsets restarted. */\n streamCreatedAt: string;\n /**\n * For an ITX-call subscription with a `jsonataTransform`, each event's\n * `type`/`payload`/`metadata` are the transform's output while the\n * coordinates keep naming the source rows. Copy batches always carry the\n * untransformed source events: the receiving stream applies its transform\n * before committing.\n */\n events: StreamEvent[];\n streamMaxOffset: number;\n /** The source stream's subscription this delivery serves, by NAME. */\n name: SubscriptionName;\n /**\n * Offset of the configure or cursor-set event that started this delivery run.\n * It stays stable across network retries, but changes after an explicit seek\n * or same-key reconfiguration so those deliberate replays are not deduped as\n * old transport attempts.\n */\n cursorChangedAtSourceOffset: number;\n /**\n * Stable across retries of the same batch and cursor-control event,\n * so receivers can dedupe redeliveries even without per-event bookkeeping.\n * (`${event.path}@${event.offset}` remains the per-event idempotency idiom.)\n */\n deliveryId: string;\n /** 1-based consecutive attempt count for this batch. */\n attempt: number;\n /**\n * The committed `subscription-configured` event this delivery serves — so a\n * receiver can configure itself from committed stream state without a\n * side-channel registry for the source stream, filter, and receiver settings.\n * Narrowed to the fields the fold stores; an honest shape instead of a\n * `StreamEvent` cast that pretends metadata/source survived.\n */\n configuredEvent: SubscriptionConfigurationForDelivery;\n};\n\n/** What a receiving stream durably did with one delivered source batch. */\nexport type CopyReceipt = {\n /**\n * Events the receiver terminally acknowledged: appended now, already\n * present under the same source-coordinate idempotency key, or dropped\n * because their stream-copy path cannot safely continue (cycle/hop limit —\n * audited by an `error-occurred` event on the receiving stream). The sender\n * advances its cursor past every event in an acked batch. The count itself\n * is observability-only wire decoration: the sender never reads it — the\n * awaited call resolving is the whole acknowledgement.\n */\n acknowledged: number;\n};\n\n/**\n * A durable receiver's declaration that it cannot accept ANY batch right now —\n * part of the delivery contract, not an implementation detail. The subscription's cursor row\n * treats a rejection carrying this name as \"the receiver is down/not ready\"\n * and backs off or halts even under `onFailingEvent: \"skip\"`,\n * because failing-event confirmation is a verdict about ONE event and an unavailable\n * receiver fails every event: confirming skips during an outage window steps\n * over healthy events forever (the bootstrap incarnation: the project-worker\n * feed called its receiver before the config repo seeded, and permanently skipped the\n * events that raced the seed).\n *\n * Matched by NAME, not instanceof: the rejection crosses Workers RPC hops\n * (loopback itx roots, DO bindings), which preserve `error.name` but not\n * class identity.\n */\nexport class StreamReceiverUnavailableError extends Error {\n static readonly NAME = \"StreamReceiverUnavailableError\";\n override readonly name = StreamReceiverUnavailableError.NAME;\n}\n\n/** A compare-and-append assertion lost to another committed stream event. */\nexport class StreamOffsetConflictError extends Error {\n static readonly NAME = \"StreamOffsetConflictError\";\n override readonly name = StreamOffsetConflictError.NAME;\n}\n\n/** An operation was bound to a stream lifetime that this path no longer names. */\nexport class StreamIdMismatchError extends Error {\n static readonly NAME = \"StreamIdMismatchError\";\n override readonly name = StreamIdMismatchError.NAME;\n}\n\n/** Canonical guarded-append rejection text, including across RPC hops that\n * normalize the custom error name to `Error`. */\nexport function streamIdMismatchMessage(expectedStreamId: string, actualStreamId: unknown): string {\n return `stream ID changed (${expectedStreamId} -> ${String(actualStreamId)}); append rejected`;\n}\n\nconst STREAM_ID_MISMATCH_MESSAGE = /^stream ID changed \\(.+ -> .+\\); append rejected$/;\n\n/**\n * Match a guarded append rejected because its source stream was recreated.\n * Durable Object RPC preserves the custom name; CapnWeb can reduce it to a\n * plain Error, so the exact canonical message remains a narrow fallback.\n */\nexport function isStreamIdMismatchError(error: unknown): boolean {\n const candidate = error as { message?: unknown; name?: unknown } | null;\n return (\n candidate?.name === StreamIdMismatchError.NAME ||\n (candidate?.name === \"Error\" &&\n typeof candidate.message === \"string\" &&\n STREAM_ID_MISMATCH_MESSAGE.test(candidate.message))\n );\n}\n\n/** Canonical compare-and-append conflict text, including across RPC hops that\n * normalize the custom error name to `Error`. */\nexport function streamOffsetConflictMessage(expectedOffset: number, actualOffset: number): string {\n return `expected next offset ${expectedOffset}, found ${actualOffset}`;\n}\n\nconst STREAM_OFFSET_CONFLICT_MESSAGE = /^expected next offset \\d+, found \\d+$/;\n\n/**\n * Match by name because Durable Object RPC preserves names, not prototypes.\n * CapnWeb's public itx boundary currently normalizes custom error names to\n * `Error`, so retain an exact message fallback for that hop. Keep this\n * deliberately narrow: callers use the result to retry a compare-and-append.\n */\nexport function isStreamOffsetConflictError(error: unknown): boolean {\n const candidate = error as { message?: unknown; name?: unknown } | null;\n return (\n candidate?.name === StreamOffsetConflictError.NAME ||\n (candidate?.name === \"Error\" &&\n typeof candidate.message === \"string\" &&\n STREAM_OFFSET_CONFLICT_MESSAGE.test(candidate.message))\n );\n}\n\nexport function isStreamReceiverUnavailableError(error: unknown): boolean {\n return (error as { name?: string } | null)?.name === StreamReceiverUnavailableError.NAME;\n}\n\n/**\n * One webhook delivery: a single committed event POSTed as JSON to the\n * subscription's URL. Deliberately per-EVENT (external webhook consumers\n * expect individual events, and per-event acking gives mid-batch\n * resumability) and deliberately WITHOUT the `state` batch callbacks receive — core\n * reduced state is internal and has no business leaving the deployment.\n *\n * Webhook delivery is at-least-once: a remote processor must deduplicate by\n * (streamId, event.offset).\n */\nexport type StreamWebhookDelivery = {\n /** Never null: webhooks require a project-scoped stream (egress attribution). */\n projectId: string;\n path: string;\n /** Random identity assigned when this source stream's storage was created. */\n streamId: string;\n /** Creation time of this source stream; orders recreated streams whose offsets restarted. */\n streamCreatedAt: string;\n /**\n * The committed event. When the subscription configures a `jsonataTransform`, its\n * `type`/`payload`/`metadata` are the transform's output while the\n * coordinates (`offset`, `createdAt`, `path`) keep naming the source row.\n */\n event: StreamEvent;\n /** The source stream's subscription this delivery serves, by NAME. */\n name: SubscriptionName;\n /** See {@link StreamDeliveryBatch.cursorChangedAtSourceOffset}. */\n cursorChangedAtSourceOffset: number;\n /** Stable across retries of this event within one delivery run. */\n deliveryId: string;\n /** 1-based consecutive attempt count for this event. */\n attempt: number;\n /** The committed subscription event this delivery serves (see {@link StreamDeliveryBatch}). */\n configuredEvent: SubscriptionConfigurationForDelivery;\n};\n\n/**\n * What the stream sends when waking a hosted processor through\n * `wakeStreamProcessor`: serializable coordinates only.\n */\nexport type StreamProcessorWakeRequest = {\n stream: {\n projectId: string | null;\n path: string;\n /** Random identity of this event log; fences persisted processor checkpoints. */\n streamId: string;\n streamMaxOffset: number;\n };\n /**\n * The subscription's NAME — the caller-chosen per-stream binding this wake\n * serves. Processor-wake names EQUAL their contract slug, so it is also the\n * registered processor name (and, under facet placement, the facet name):\n * hosts route on this one identity, and a name matching no registered\n * processor fails loudly here with the registry's unknown-name error.\n */\n name: SubscriptionName;\n};\n\n/**\n * What the woken processor hands back in one response. The stream retains\n * `processEventBatch` (ownership of a returned stub transfers to\n * the caller) and streams one-way batches into it from `checkpointOffset + 1`;\n * there is no callback registration call in the other direction.\n */\nexport type StreamProcessorWakeResponse = {\n /** Stream identity to which `checkpointOffset` and the returned callback are bound. */\n streamId: string;\n /** The processor's durable checkpoint offset — replay resumes after it. */\n checkpointOffset: number;\n /**\n * The live delivery callback the stream retains and invokes per batch.\n * Calls are one-way; each batch reports completion through its independent\n * `reportDeliveryResult` callback.\n */\n processEventBatch: ProcessStreamWakeEventBatch;\n /**\n * Serializable callback-owner identity (validated against\n * `ConnectionOpenerDescriptor` by the stream) appended as the\n * connection-opened presence fact; carries the processor's contract\n * announcement for the stream's `processorsBySlug` registry.\n */\n openedBy?: unknown;\n /** Live runtime-state capability, retained for the connection lifetime. */\n getRuntimeState?: GetProcessorRuntimeState;\n /** Optional ping capability, retained for the connection lifetime (see {@link StreamConnectionPing}). */\n ping?: StreamConnectionPing;\n};\n\n/**\n * The mutual ping's request half (NTP-style, for real latency measurement\n * between a stream and its callback owners): the requester stamps `t0` on its own\n * clock and observes `t3` when the reply lands.\n */\nexport type StreamPingInput = { t0: number };\n\n/**\n * The mutual ping's reply half: the responder echoes `t0` and reports when it\n * received the request (`t1`) and sent the reply (`t2`) on ITS clock.\n * `rtt = (t3 - t0) - (t2 - t1)` excludes responder processing time, and\n * `((t1 - t0) + (t2 - t3)) / 2` estimates the responder−requester clock\n * offset (see stream-runtime-metrics.pingRoundTrip). Purely observational:\n * ping failures drop the sample and never affect delivery or liveness.\n */\nexport type StreamPingReply = { t0: number; t1: number; t2: number };\n\n/**\n * Optional ping capability a connection owner hands the stream (session\n * `openConnection()` argument or processor wake-response field).\n */\nexport type StreamConnectionPing = (\n input: StreamPingInput,\n) => StreamPingReply | Promise<StreamPingReply>;\n\n/** Serializable snapshot plus optional live runtime debug state for a processor. */\nexport type ProcessorRuntimeState<State = unknown> = {\n snapshot: { offset: number; state: State };\n runtime?: Record<string, unknown>;\n};\n\n/**\n * Optional runtime-state callback exposed by a hosted processor.\n *\n * It accepts sync or async implementations because local processors can return\n * immediately, while RPC-backed processors may need an async round trip.\n */\nexport type GetProcessorRuntimeState = () => ProcessorRuntimeState | Promise<ProcessorRuntimeState>;\n\n/**\n * Live handle returned by `Stream.openConnection`.\n *\n * `ping()` reports liveness: `true` while\n * the connection is still open on the live stream, `false` after it closed\n * (replaced, delivery failure, or explicit close); it rejects when the stream's\n * Durable Object incarnation is gone. Either non-`true` outcome means the\n * owner should open another connection.\n *\n * CAUTION ON THE DATA PROPERTIES BELOW. A handle obtained across the worker\n * relay was measured NOT to materialize `streamMaxOffset` as a number — it is\n * present inside the Stream DO and can arrive undefined to a remote consumer.\n * Prefer the delivery batches for offsets: every connection receives an initial\n * (possibly empty) batch immediately on open, carrying `streamMaxOffset` and\n * `scannedThroughOffset`, and those are the values to seed a resume cursor\n * from. A consumer that advances its cursor from the handle can silently\n * resume at zero.\n */\nexport type StreamConnectionHandle = Disposable & {\n /** Stable identity of this live connection. */\n connectionKey: ConnectionKey;\n /** The stream's max offset when the connection opened. See the caution above. */\n streamMaxOffset: number;\n ping(): boolean | Promise<boolean>;\n /** Close this connection; safe to call more than once. */\n close(): void;\n};\n","// The structural slice of the itx `Stream` surface the processor machinery\n// (and processor implementations) depends on. Deliberately narrow: the\n// platform's full `Stream` (generated into itx-api.generated.ts) satisfies it\n// automatically, and a userspace host only has to provide these five methods\n// — append (processor output and the platform revival fact), readEvents\n// (reduction rebuilds and catch-up self-pulls), getEvent/getEvents (point and\n// page reads processors make from their hooks), and at (sibling-stream\n// appendTo) — instead of faking the whole public stream API.\nimport type { StreamEvent, StreamEventInput } from \"./schemas.ts\";\nimport type { StreamEventPage, StreamEventReadInput } from \"./rpc-types.ts\";\n\n/**\n * Resolve the path accepted by `stream.at(path)`. Absolute paths start at the\n * stream root; relative paths start at `basePath`; `..` may climb only as far\n * as the root. Keeping this next to {@link ProcessorStream} lets processor\n * appends decide whether a resolved destination is their own stream without\n * relying on RPC-target object identity.\n */\nexport function resolveStreamPath(basePath: string, streamPath: string): string {\n const segments = streamPath.startsWith(\"/\") ? [] : basePath.split(\"/\").filter(Boolean);\n for (const segment of streamPath.split(\"/\")) {\n if (segment === \"\" || segment === \".\") continue;\n if (segment === \"..\") {\n if (segments.length === 0) {\n throw new Error(\n `stream path \"${streamPath}\" escapes the stream root (resolved from \"${basePath}\")`,\n );\n }\n segments.pop();\n continue;\n }\n segments.push(segment);\n }\n return segments.length === 0 ? \"/\" : `/${segments.join(\"/\")}`;\n}\n\n/** One open journal read — page with `next()`, dispose when done\n * (`using pager = stream.readEvents(...)`). */\nexport interface ProcessorStreamPager {\n /** Returns [] when no newer matching page is currently available. */\n next(): Promise<StreamEvent[]>;\n [Symbol.dispose](): void;\n}\n\n/** The stream capabilities a processor host must supply — see module doc. */\nexport interface ProcessorStream {\n append(...events: StreamEventInput[]): Promise<StreamEvent[]>;\n /**\n * Append only if this path still names the observed stream lifetime. The\n * identity check and append happen in one stream turn, so a delete/recreate\n * cannot slip between them.\n */\n appendIfStreamId(args: { streamId: string; events: StreamEventInput[] }): Promise<StreamEvent[]>;\n /**\n * Read events together with the stream lifetime that owns their offsets.\n * Processor cursors must never consume a bare event array.\n */\n getEventPage(args?: StreamEventReadInput): Promise<StreamEventPage>;\n readEvents(args?: StreamEventReadInput): ProcessorStreamPager;\n getEvent(\n args: { offset: number; idempotencyKey?: never } | { idempotencyKey: string; offset?: never },\n ): Promise<StreamEvent | undefined>;\n getEvents(args?: StreamEventReadInput): Promise<StreamEvent[]>;\n at(path: string): ProcessorStream;\n}\n","// Pure runtime-metrics primitives for a stream and the callbacks it invokes.\n//\n// Everything here is clock-free and transport-free (timestamps are\n// parameters, like the delivery math atop stream-event-sender.ts), so the rings and buckets are\n// table-testable in plain node and shared verbatim by the Durable Object,\n// the server processor host, and the browser store. All of it is in-memory\n// observability — reset on eviction/reload by design; `measuredSince` tells\n// readers how long the window has been collecting.\n\n/** Serializable summary of a {@link LatencyRing}; `null` until a sample exists. */\nexport type LatencyStats = {\n /** Most recent sample (ms). */\n last: number;\n p50: number;\n p95: number;\n /** Samples currently in the ring (caps at the ring size). */\n samples: number;\n /** Epoch ms of the most recent sample. */\n lastAt: number;\n};\n\n/**\n * Fixed-capacity ring of latency samples. `stats()` is `null` until the first\n * sample — surfaces render \"—\" instead of a made-up number.\n */\nexport class LatencyRing {\n readonly #capacity: number;\n readonly #samples: number[] = [];\n #next = 0;\n #last = 0;\n #lastAt = 0;\n\n constructor(capacity = 32) {\n if (!Number.isInteger(capacity) || capacity <= 0) {\n throw new Error(\"LatencyRing capacity must be a positive integer\");\n }\n this.#capacity = capacity;\n }\n\n record(ms: number, atMs: number): void {\n if (!Number.isFinite(ms)) return;\n const sample = Math.max(0, Math.round(ms));\n if (this.#samples.length < this.#capacity) {\n this.#samples.push(sample);\n } else {\n this.#samples[this.#next] = sample;\n }\n this.#next = (this.#next + 1) % this.#capacity;\n this.#last = sample;\n this.#lastAt = atMs;\n }\n\n stats(): LatencyStats | null {\n if (this.#samples.length === 0) return null;\n const sorted = [...this.#samples].sort((a, b) => a - b);\n // Nearest-rank percentile (ceil(q·n) − 1): with few samples this reports\n // the WORST candidate rather than the best — a dashboard ring mostly\n // holds few samples, and a p95 that hides the spike is a lie.\n const rank = (q: number) => sorted[Math.max(0, Math.ceil(q * sorted.length) - 1)]!;\n return {\n last: this.#last,\n p50: rank(0.5),\n p95: rank(0.95),\n samples: sorted.length,\n lastAt: this.#lastAt,\n };\n }\n}\n\n/** One rolling-minute throughput window. */\nexport type MinuteWindow = {\n /** Events in the last 60 seconds. */\n count: number;\n /** Payload bytes in the last 60 seconds. */\n bytes: number;\n /** `count / 60` — the \"events/s over the last minute\" number. */\n perSecond: number;\n};\n\n/**\n * 60 one-second buckets of {count, bytes}. Stale slots (lapped by the ring)\n * are ignored at read time, so a burst followed by silence decays to zero\n * without a sweeper.\n */\nexport class MinuteBuckets {\n readonly #seconds = new Array<number>(60).fill(-1);\n readonly #counts = new Array<number>(60).fill(0);\n readonly #bytes = new Array<number>(60).fill(0);\n\n bump(atMs: number, count: number, bytes: number): void {\n const second = Math.floor(atMs / 1000);\n const slot = ((second % 60) + 60) % 60;\n if (this.#seconds[slot] !== second) {\n this.#seconds[slot] = second;\n this.#counts[slot] = 0;\n this.#bytes[slot] = 0;\n }\n this.#counts[slot]! += count;\n this.#bytes[slot]! += bytes;\n }\n\n lastMinute(nowMs: number): MinuteWindow {\n const { count, bytes } = this.window(nowMs, 60);\n return { count, bytes, perSecond: count / 60 };\n }\n\n /** Totals over the trailing `seconds` (≤60) — short windows make responsive rates. */\n window(nowMs: number, seconds: number): { count: number; bytes: number } {\n const nowSecond = Math.floor(nowMs / 1000);\n let count = 0;\n let bytes = 0;\n for (let slot = 0; slot < 60; slot += 1) {\n const second = this.#seconds[slot]!;\n if (second < 0 || second > nowSecond || second <= nowSecond - seconds) continue;\n count += this.#counts[slot]!;\n bytes += this.#bytes[slot]!;\n }\n return { count, bytes };\n }\n\n /**\n * The raw per-second buckets, oldest→newest, always exactly 60 entries\n * (silent seconds are zero) — what a UI graphs directly, so the graph is\n * the measurement rather than a client-side reconstruction of it.\n */\n series(nowMs: number): ThroughputSeries {\n const nowSecond = Math.floor(nowMs / 1000);\n const counts = new Array<number>(60).fill(0);\n const bytes = new Array<number>(60).fill(0);\n for (let slot = 0; slot < 60; slot += 1) {\n const second = this.#seconds[slot]!;\n if (second < 0 || second > nowSecond || second <= nowSecond - 60) continue;\n const index = 59 - (nowSecond - second);\n counts[index] = this.#counts[slot]!;\n bytes[index] = this.#bytes[slot]!;\n }\n return { counts, bytes };\n }\n}\n\n/** Per-second buckets over the trailing minute, oldest→newest, length 60. */\nexport type ThroughputSeries = {\n counts: number[];\n bytes: number[];\n};\n\n/**\n * One direction's throughput report: a responsive trailing-5s rate (the\n * number UIs show), the full-minute totals, and the raw 1s series for graphs.\n */\nexport type ThroughputReport = {\n /** Events per second over the trailing 5 seconds. */\n perSecond5s: number;\n /** Payload bytes per second over the trailing 5 seconds. */\n bytesPerSecond5s: number;\n lastMinute: MinuteWindow;\n series: ThroughputSeries;\n};\n\n/** What a stream runtime snapshot reports for the stream's own throughput. */\nexport type StreamThroughputMetrics = {\n /** ISO timestamp when this incarnation started measuring (metrics reset on eviction). */\n measuredSince: string;\n /** ISO timestamp anchoring the trailing windows and final series bucket. */\n reportedAt: string;\n /** Appends committed (all producers). */\n ingress: ThroughputReport;\n /** Event batches sent to all receiving streams and open callbacks. */\n egress: ThroughputReport;\n};\n\n/** The stream Durable Object's in-memory throughput accounting. */\nexport class StreamRuntimeMetrics {\n readonly #measuredSinceMs: number;\n readonly ingress = new MinuteBuckets();\n readonly egress = new MinuteBuckets();\n\n constructor(nowMs: number) {\n this.#measuredSinceMs = nowMs;\n }\n\n report(nowMs: number): StreamThroughputMetrics {\n const direction = (buckets: MinuteBuckets): ThroughputReport => {\n const trailing5s = buckets.window(nowMs, 5);\n return {\n perSecond5s: trailing5s.count / 5,\n bytesPerSecond5s: trailing5s.bytes / 5,\n lastMinute: buckets.lastMinute(nowMs),\n series: buckets.series(nowMs),\n };\n };\n return {\n measuredSince: new Date(this.#measuredSinceMs).toISOString(),\n reportedAt: new Date(nowMs).toISOString(),\n ingress: direction(this.ingress),\n egress: direction(this.egress),\n };\n }\n}\n\n/**\n * Age an event-driven throughput snapshot against the local wall clock. This\n * keeps trailing windows truthful during silence without polling the stream.\n */\nexport function ageStreamThroughputMetrics(\n metrics: StreamThroughputMetrics,\n nowMs: number,\n): StreamThroughputMetrics {\n const reportedAtMs = Date.parse(metrics.reportedAt);\n if (!Number.isFinite(reportedAtMs)) return metrics;\n\n const elapsedSeconds = Math.max(0, Math.floor(nowMs / 1_000) - Math.floor(reportedAtMs / 1_000));\n if (elapsedSeconds === 0) return metrics;\n\n const age = (report: ThroughputReport): ThroughputReport => {\n const shift = (values: number[]) => {\n const seconds = Math.min(values.length, elapsedSeconds);\n return [...values.slice(seconds), ...new Array<number>(seconds).fill(0)];\n };\n const counts = shift(report.series.counts);\n const bytes = shift(report.series.bytes);\n const sum = (values: number[]) => values.reduce((total, value) => total + value, 0);\n const count = sum(counts);\n const byteCount = sum(bytes);\n\n return {\n perSecond5s: sum(counts.slice(-5)) / 5,\n bytesPerSecond5s: sum(bytes.slice(-5)) / 5,\n lastMinute: { count, bytes: byteCount, perSecond: count / 60 },\n series: { counts, bytes },\n };\n };\n\n return {\n ...metrics,\n ingress: age(metrics.ingress),\n egress: age(metrics.egress),\n };\n}\n\n/**\n * The mutual ping's NTP-style math, shared by both requesters (a stream\n * pinging a callback owner; anything pinging the stream). The requester stamps\n * `t0` and observes `t3`; the responder reports receive/reply-send times on\n * ITS clock. RTT excludes responder processing time; `clockOffsetMs`\n * estimates `responderClock - requesterClock`.\n */\nexport function pingRoundTrip(reply: { t0: number; t1: number; t2: number }, t3: number) {\n return {\n rttMs: Math.max(0, t3 - reply.t0 - (reply.t2 - reply.t1)),\n clockOffsetMs: (reply.t1 - reply.t0 + (reply.t2 - t3)) / 2,\n };\n}\n","// Self-measured event-consumption metrics, shared by every processor host.\n//\n// The stream can only observe dispatch; whether (and how fast) a processor\n// actually CONSUMED a batch is knowledge the processor host alone has. So each\n// host — the server-side processor host and the browser store, which runs\n// the same stream processors — owns one of these per running processor\n// and reports it through the `getRuntimeState` capability the stream already\n// retains (`ProcessorRuntimeState.runtime.metrics`). No new reporting\n// channel, and hosts that predate this simply report nothing.\n//\n// Pure and clock-free like the delivery math atop stream-event-sender.ts:\n// every method takes timestamps as arguments, so the whole thing unit-tests\n// in plain node and runs unchanged in a browser (performance-now deltas) or a\n// worker (Date.now).\n\nimport { LatencyRing, type LatencyStats } from \"./stream-runtime-metrics.ts\";\n\n/** The `runtime.metrics` slice a host reports through `getRuntimeState()`. */\nexport type EventConsumptionMetricsReport = {\n /** ISO timestamp when this host runtime started measuring (in-memory; resets on reload). */\n measuredSince: string;\n /**\n * The full consume-your-own-appends loop, one clock: this host called\n * `append()` at t0, and its OWN event connection later delivered (and the\n * host fully ingested) THAT COMMITTED OFFSET.\n *\n * Samples only exist for appends that came back. An append of a type this\n * host does not consume, or one the stream never hands back, contributes\n * NOTHING here — it is not a slow loop, it is no loop, and the two must not\n * be spelled the same. `null` is \"no such append observed\", never a\n * fabricated number.\n */\n consumeOwnAppendMs: LatencyStats | null;\n /** `append()` call → commit acknowledged (the RPC round trip incl. commit). */\n appendRoundTripMs: LatencyStats | null;\n /**\n * Age of the newest event in each batch when the host finished ingesting\n * it, i.e. commit-to-consumed for OTHER producers' events too. Crosses\n * clocks (event `createdAt` is stream time), corrected by the ping-derived\n * offset estimate when one exists — an estimate, and labeled as such in UIs.\n */\n deliveryAgeMs: LatencyStats | null;\n /** Time the host spent ingesting each delivered batch (fold/SQLite write). */\n ingestMs: LatencyStats | null;\n batchesIngested: number;\n eventsIngested: number;\n /** Estimated host−stream clock skew (ms) from observed pings; `null` until pinged. */\n clockOffsetMs: number | null;\n};\n\n/** In-flight own-append correlation entries beyond this are dropped oldest-first. */\nconst MAX_PENDING_OWN_APPENDS = 16;\n\nexport class EventConsumptionMetrics {\n readonly #measuredSinceMs: number;\n readonly #consumeOwnAppend = new LatencyRing();\n readonly #appendRoundTrip = new LatencyRing();\n readonly #deliveryAge = new LatencyRing();\n readonly #ingest = new LatencyRing();\n #batchesIngested = 0;\n #eventsIngested = 0;\n #clockOffsetMs: number | null = null;\n /** Highest offset this host has ingested through (see noteBatchIngested). */\n #ingestedThroughOffset = 0;\n /** Own appends awaiting their loop-back delivery: committed offset + call-start time. */\n #pendingOwnAppends: { offset: number; t0: number }[] = [];\n\n constructor(nowMs: number) {\n this.#measuredSinceMs = nowMs;\n }\n\n /**\n * An `append()` this host issued resolved: `t0` is when the host called it,\n * `atMs` is when the commit came back, and `maxCommittedOffset` is the\n * highest committed offset that CAN come back to this host — the caller's\n * judgement, because only the caller knows what it consumes. `null` means\n * the append carried nothing this host will ever be delivered, and is timed\n * for its round trip alone.\n */\n noteAppendCommitted(args: { maxCommittedOffset: number | null; t0: number; atMs: number }): void {\n this.#appendRoundTrip.record(args.atMs - args.t0, args.atMs);\n if (args.maxCommittedOffset === null) return;\n // The stream fans out BEFORE the append call returns, so our own\n // event callback may have ingested the offset already — in that case the\n // loop closed the moment both halves were done, which is now. Only a\n // still-unseen offset goes on the pending list.\n if (args.maxCommittedOffset <= this.#ingestedThroughOffset) {\n this.#consumeOwnAppend.record(args.atMs - args.t0, args.atMs);\n return;\n }\n this.#pendingOwnAppends.push({ offset: args.maxCommittedOffset, t0: args.t0 });\n if (this.#pendingOwnAppends.length > MAX_PENDING_OWN_APPENDS) {\n this.#pendingOwnAppends.splice(0, this.#pendingOwnAppends.length - MAX_PENDING_OWN_APPENDS);\n }\n }\n\n /** One delivered batch fully ingested (fold applied / SQLite write done). */\n noteBatchIngested(args: {\n /** Highest offset the host has now ingested through (its cursor, not just this batch). */\n ingestedThroughOffset: number;\n /**\n * The offsets this batch actually CARRIED.\n *\n * The cursor above sweeps past rows this host was never handed — a\n * filtered subscription skips them durably — so it cannot tell an own\n * append that came back from one that never will. These can.\n */\n ingestedOffsets: readonly number[];\n /** `Date.parse(newestEvent.createdAt)` for the newest event in the batch, if any. */\n newestEventCreatedAtMs?: number;\n /** When the host started ingesting this batch. */\n ingestStartedAtMs: number;\n atMs: number;\n }): void {\n this.#batchesIngested += 1;\n this.#eventsIngested += args.ingestedOffsets.length;\n this.#ingestedThroughOffset = Math.max(this.#ingestedThroughOffset, args.ingestedThroughOffset);\n this.#ingest.record(args.atMs - args.ingestStartedAtMs, args.atMs);\n if (args.newestEventCreatedAtMs !== undefined && Number.isFinite(args.newestEventCreatedAtMs)) {\n const hostNowOnStreamClock = args.atMs - (this.#clockOffsetMs ?? 0);\n this.#deliveryAge.record(hostNowOnStreamClock - args.newestEventCreatedAtMs, args.atMs);\n }\n if (this.#pendingOwnAppends.length > 0) {\n const stillPending: { offset: number; t0: number }[] = [];\n for (const pending of this.#pendingOwnAppends) {\n if (pending.offset > args.ingestedThroughOffset) {\n stillPending.push(pending);\n continue;\n }\n // The cursor is now past this correlation, so it is over either way.\n // A SAMPLE, though, only when the batch actually carried the offset:\n // that is the append→consume loop this metric names. Anything else —\n // a row the subscription filtered out, an ephemeral event evicted\n // before delivery — never looped at all, and timing the wait until\n // some later unrelated event happened along is how this metric came\n // to report a person's pause between sentences as consumption lag.\n if (args.ingestedOffsets.includes(pending.offset)) {\n this.#consumeOwnAppend.record(args.atMs - pending.t0, args.atMs);\n }\n }\n this.#pendingOwnAppends = stillPending;\n }\n }\n\n /**\n * The stream pinged this host: `t0` is the stream's send time (stream\n * clock), `t1` the host's receive time (host clock). With a one-way-delay\n * estimate (half the host's measured transport RTT, when it has one) this\n * yields the host−stream clock offset used to correct delivery ages.\n */\n notePingObserved(args: { t0: number; t1: number; oneWayEstimateMs?: number }): void {\n this.#clockOffsetMs = args.t1 - args.t0 - (args.oneWayEstimateMs ?? 0);\n }\n\n /** The host's event connection reopened: in-flight own-append correlations are void. */\n clearPendingAppends(): void {\n this.#pendingOwnAppends = [];\n }\n\n report(): EventConsumptionMetricsReport {\n return {\n measuredSince: new Date(this.#measuredSinceMs).toISOString(),\n consumeOwnAppendMs: this.#consumeOwnAppend.stats(),\n appendRoundTripMs: this.#appendRoundTrip.stats(),\n deliveryAgeMs: this.#deliveryAge.stats(),\n ingestMs: this.#ingest.stats(),\n batchesIngested: this.#batchesIngested,\n eventsIngested: this.#eventsIngested,\n clockOffsetMs: this.#clockOffsetMs,\n };\n }\n}\n","import { z } from \"zod\";\n\n/**\n * Maximum number of stream-to-stream copies retained in one event's provenance.\n * Cycles normally stop a chain earlier; this bounds acyclic graphs and the\n * serialized event growth they can produce.\n */\nexport const MAX_COPIED_FROM_HOPS = 32;\n\n/** Append input before the stream assigns offset and timestamp. */\nexport const StreamEventInput = z\n .strictObject({\n type: z.string(),\n payload: z.record(z.string(), z.unknown()).optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n source: z\n .strictObject({\n // Stamped by the StreamProcessor append methods: which processor appended\n // this event, and — for per-event side effects — while processing which\n // event. `stream` is the processor's home stream (where `whileProcessing`\n // offsets resolve), recorded absolutely so the stamp stays meaningful on\n // rows appended cross-stream and on copies produced by a subscription. The stamp is a\n // claim, not authentication: same trust model as idempotency keys.\n processor: z\n .strictObject({\n slug: z.string(),\n version: z.string(),\n stream: z.strictObject({\n path: z.string().trim().min(1),\n projectId: z.string().trim().min(1).nullable(),\n /** Exact lifetime of the processor's home stream. */\n streamId: z.uuid(),\n }),\n whileProcessing: z\n .strictObject({\n offset: z.number().int().nonnegative(),\n type: z.string().trim().min(1),\n })\n .optional(),\n })\n .optional(),\n copiedFrom: z\n .array(\n z.strictObject({\n /** Name of the source stream's subscription that copied this event. */\n name: z.string().trim().min(1),\n /** Random identity assigned when that source stream's storage was created. */\n streamId: z.uuid(),\n /** Creation time of that source stream, used to order destructive recreations. */\n streamCreatedAt: z.string().trim().min(1),\n /** Configure or cursor-set event that started this delivered copy. */\n cursorChangedAtSourceOffset: z.number().int().positive(),\n createdAt: z.string(),\n offset: z.number().int().nonnegative(),\n path: z.string().trim().min(1),\n projectId: z.string().trim().min(1).nullable(),\n type: z.string().trim().min(1),\n }),\n )\n .min(1)\n .max(MAX_COPIED_FROM_HOPS)\n .optional(),\n })\n .optional(),\n idempotencyKey: z.string().trim().min(1).optional(),\n /**\n * Ephemeral events receive real stream offsets but their bodies are NEVER\n * written to the Stream Durable Object's SQLite. The current Durable Object\n * incarnation keeps up to 10 MiB of serialized ephemeral events in memory,\n * evicting the oldest first. A restart forgets all of them.\n *\n * Range reads exclude ephemeral events unless `includeEphemeral: true`;\n * point reads by offset return one only while it remains in memory. Session\n * connections replay currently buffered ephemeral events after their replay\n * cursor and receive new ones live. Durable subscriptions never deliver\n * them. An ephemeral event cannot have an idempotency key.\n *\n * Nothing durable may depend on an ephemeral event. Use it for streaming\n * signals whose durable truth lands separately — for example, LLM response\n * chunks followed by a durable assistant context item.\n * `z.literal(true)`, not boolean: absent = durable, so committed rows stay\n * self-describing and `ephemeral: false` is a loud input error, not a\n * silent synonym for omitting the flag.\n */\n ephemeral: z.literal(true).optional(),\n })\n .superRefine((event, context) => {\n if (event.ephemeral === true && event.idempotencyKey !== undefined) {\n context.addIssue({\n code: \"custom\",\n message: \"ephemeral events cannot have an idempotencyKey\",\n path: [\"idempotencyKey\"],\n });\n }\n });\n\n// A committed event is an append input plus the fields the stream assigns at\n// commit time. Deriving it from `StreamEventInput` keeps the shared `source` /\n// `metadata` / `payload` shapes defined exactly once.\n/** Offset-assigned stream event after commit. */\nexport const StreamEvent = StreamEventInput.safeExtend({\n offset: z.number().int().nonnegative(),\n createdAt: z.string(),\n path: z.string().trim().min(1),\n});\n\n/**\n * One known stream in a project's reduced state — what the project processor\n * records per agent/repo/secret/stream and what the collection `list()`\n * methods return.\n */\nexport const StreamListItem = z.object({\n createdAt: z.string(),\n path: z.string(),\n});\n\n/** Append input for `Stream.append`: event type, JSON payload, optional\n * metadata, provenance source, and idempotency key — everything before the\n * stream assigns offset and timestamp at commit. `ephemeral: true` assigns a\n * real offset but keeps the event body only in the Stream Durable Object's\n * bounded memory until eviction or restart; it cannot be combined with an\n * idempotency key. */\nexport type StreamEventInput = z.infer<typeof StreamEventInput>;\n/** One offset-assigned stream event: type, JSON payload, offset, provenance\n * (processor stamp / source-stream chain), plus the commit-time `createdAt`\n * and stream `path`. Durable events may have an idempotency key;\n * `ephemeral: true` marks a memory-only event (see `StreamEventInput`). */\nexport type StreamEvent = z.infer<typeof StreamEvent>;\n/** One known stream in a project's reduced state — the entry shape the\n * collection `list()` methods return: stream path plus creation time. */\nexport type StreamListItem = z.infer<typeof StreamListItem>;\n","import { z } from \"zod\";\nimport type { StreamEvent, StreamEventInput } from \"./schemas.ts\";\nimport {\n StreamEvent as StreamEventSchema,\n StreamEventInput as StreamEventInputSchema,\n} from \"./schemas.ts\";\n\n/**\n * Merge one processor configuration patch into its current configuration.\n *\n * Configuration patches recurse only through plain JSON objects. Arrays,\n * scalars, and `null` replace the previous value wholesale; omitted keys are\n * retained. Processors validate the merged result with their own complete\n * configuration schema before storing it in reduced state.\n */\nexport function mergeProcessorConfig(base: unknown, patch: unknown): unknown {\n if (!isPlainObject(base) || !isPlainObject(patch)) return patch;\n\n const merged: Record<string, unknown> = { ...base };\n for (const [key, patchValue] of Object.entries(patch)) {\n const baseValue = merged[key];\n merged[key] =\n isPlainObject(baseValue) && isPlainObject(patchValue)\n ? mergeProcessorConfig(baseValue, patchValue)\n : patchValue;\n }\n return merged;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\n// =============================================================================\n// Processor contracts.\n//\n// A contract declares a processor's identity (slug/version/description), its\n// reduced-state schema, the events it owns (`events`, keyed by the durable\n// event type string), the events it `consumes` and `emits`, and optional\n// `processorDeps` — other contracts whose events it may consume/emit without\n// owning them. `defineProcessorContract(...)` validates the declaration and\n// attaches typed `buildEvent` / `parseEvent` / `parseEventInput` /\n// `parseConsumedInput` helpers.\n//\n// The type-level machinery below exists for one purpose: resolving an event\n// type STRING to the payload schema that owns it (local `events` first, then\n// `processorDeps`), so reducers, emit helpers, and append call sites all infer\n// payload types from the same declaration and typos fail at the definition site.\n// =============================================================================\n\n/**\n * One documented example payload for an owned event, rendered on the public\n * event docs site (events.iterate.com). The payload must parse against the\n * event's `payloadSchema` — enforced by the event-docs unit tests rather than\n * at module load, so a bad example fails CI instead of bricking a worker boot.\n */\nexport type EventExample = {\n /** What this example shows, e.g. \"Durable delivery to another stream\". */\n description: string;\n /** The example payload, in the payload schema's input shape. */\n payload: unknown;\n};\n\n/** One owned event: its payload schema plus optional human description and examples. */\nexport type EventDefinition<PayloadOutput = unknown, PayloadInput = PayloadOutput> = {\n description?: string;\n payloadSchema: z.ZodType<PayloadOutput, PayloadInput>;\n /**\n * FORCIBLY ephemeral: every append and parse built from this definition\n * defaults the envelope's `ephemeral` flag to `true` and REJECTS an explicit\n * `ephemeral: false`. For events that must never become durable stream\n * facts (streaming chunks) — declaring it here makes forgetting the flag at\n * an append site impossible instead of a silent storage leak.\n */\n ephemeral?: true;\n examples?: readonly EventExample[];\n};\n\n/** A contract's owned events, keyed by the durable event type string. */\nexport type EventCatalog = Record<string, EventDefinition<unknown, unknown>>;\n\n/** The string-keyed event definitions of a catalog object (index signatures excluded). */\ntype EventCatalogFromObject<Value> = {\n [Key in keyof Value as string extends Key\n ? never\n : number extends Key\n ? never\n : Value[Key] extends EventDefinition\n ? Key\n : never]: Value[Key];\n};\n\n/**\n * A `processorDeps` entry may be a full contract (`{ events: ... }`) or a\n * standalone event catalog, so a processor can depend on another processor's\n * contract or on a small shared catalog.\n */\ntype ContractEventCatalog<ContractOrCatalog> = ContractOrCatalog extends {\n events: infer Events;\n}\n ? EventCatalogFromObject<Events>\n : EventCatalogFromObject<ContractOrCatalog>;\n\n/** All event type strings resolvable from local `events` plus `processorDeps`. */\ntype ResolvedEventType<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n> = Extract<\n keyof EventCatalogFromObject<Events> | EventTypeFromProcessorDeps<ProcessorDeps>,\n string\n>;\n\n/** Union of every event type string owned by any `processorDeps` entry. */\ntype EventTypeFromProcessorDeps<ProcessorDeps extends readonly unknown[]> =\n ProcessorDeps[number] extends infer ProcessorDep\n ? ProcessorDep extends unknown\n ? keyof ContractEventCatalog<ProcessorDep>\n : never\n : never;\n\n/**\n * Resolve a string event type to the definition that owns it. Local events win\n * in the type-level lookup; runtime validation rejects duplicate ownership.\n */\ntype EventDefinitionForType<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Type extends string,\n> = Type extends keyof Events\n ? Events[Type]\n : ProcessorDeps[number] extends infer ProcessorDep\n ? ProcessorDep extends unknown\n ? Type extends keyof ContractEventCatalog<ProcessorDep>\n ? ContractEventCatalog<ProcessorDep>[Type]\n : never\n : never\n : never;\n\n// -----------------------------------------------------------------------------\n// Event shapes: the app's non-generic `StreamEvent` / `StreamEventInput` from\n// `./schemas.ts`, re-expressed with `<Type, Payload>` generics for inference.\n// -----------------------------------------------------------------------------\n\n/** `StreamEventInput` with `type`/`payload` narrowed to one event definition. */\ntype TypedStreamEventInput<Type extends string = string, Payload = Record<string, unknown>> = Omit<\n StreamEventInput,\n \"payload\" | \"type\"\n> & {\n type: Type;\n payload: Payload;\n};\n\n/**\n * A durable processor input. Wake processors never receive ephemeral events, so\n * a domain object's processor-typed append door must not claim that they do.\n */\ntype TypedConsumedEventInput<\n Type extends string = string,\n Payload = Record<string, unknown>,\n> = Omit<TypedStreamEventInput<Type, Payload>, \"ephemeral\"> & { ephemeral?: never };\n\n/** `StreamEvent` with `type`/`payload` narrowed to one event definition. */\ntype TypedStreamEvent<Type extends string = string, Payload = Record<string, unknown>> = Omit<\n StreamEvent,\n \"payload\" | \"type\"\n> &\n TypedStreamEventInput<Type, Payload>;\n\n/** Committed event for one resolved type (payload parsed, so required). */\ntype EventFromType<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Type extends string,\n> = Type extends unknown\n ? EventDefinitionForType<Events, ProcessorDeps, Type> extends EventDefinition<\n infer PayloadOutput,\n unknown\n >\n ? TypedStreamEvent<Type, PayloadOutput> & { payload: PayloadOutput } & ParsedEphemeralEnvelope<\n EventDefinitionForType<Events, ProcessorDeps, Type>\n >\n : never\n : never;\n\n/** A committed event resolved from a contract's owned events or processor dependencies.\n * Unknown event-type strings retain the untyped {@link StreamEvent} shape. */\nexport type ResolvedEvent<Contract, Type extends string> = Contract extends {\n events: EventCatalog;\n}\n ? Type extends ResolvedEventType<ContractEventCatalog<Contract>, ProcessorDepsOf<Contract>>\n ? EventFromType<ContractEventCatalog<Contract>, ProcessorDepsOf<Contract>, Type>\n : StreamEvent\n : StreamEvent;\n\n/** Union of committed-event shapes for a `consumes` tuple; `\"*\"` alone means any `StreamEvent`. */\ntype EventFromTypes<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Types extends readonly string[],\n> = \"*\" extends Types[number]\n ? [Exclude<Types[number], \"*\">] extends [never]\n ? StreamEvent\n : EventFromType<Events, ProcessorDeps, Exclude<Types[number], \"*\">>\n : EventFromType<Events, ProcessorDeps, Types[number]>;\n\n/** Append input for one resolved type (payload accepts the schema's input shape). */\ntype InputFromType<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Type extends string,\n> = Type extends unknown\n ? EventDefinitionForType<Events, ProcessorDeps, Type> extends EventDefinition<\n unknown,\n infer PayloadInput\n >\n ? TypedStreamEventInput<Type, PayloadInput>\n : never\n : never;\n\n/** Durable append input for one event delivered to a wake processor. */\ntype ConsumedInputFromType<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Type extends string,\n> = Type extends unknown\n ? EventDefinitionForType<Events, ProcessorDeps, Type> extends EventDefinition<\n unknown,\n infer PayloadInput\n >\n ? TypedConsumedEventInput<Type, PayloadInput>\n : never\n : never;\n\n/** Durable append-input shapes for a processor's `consumes` tuple. */\ntype ConsumedInputFromTypes<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Types extends readonly string[],\n> = \"*\" extends Types[number]\n ? Omit<StreamEventInput, \"ephemeral\"> & { ephemeral?: never }\n : ConsumedInputFromType<Events, ProcessorDeps, Types[number]>;\n\n/** Parsed append input for one resolved type (payload validated, so required). */\ntype ParsedInputFromType<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Type extends string,\n> = Type extends unknown\n ? EventDefinitionForType<Events, ProcessorDeps, Type> extends EventDefinition<\n infer PayloadOutput,\n unknown\n >\n ? TypedStreamEventInput<Type, PayloadOutput> & {\n payload: PayloadOutput;\n } & ParsedEphemeralEnvelope<EventDefinitionForType<Events, ProcessorDeps, Type>>\n : never\n : never;\n\n/** Contract-forced ephemeral inputs default to `true` during parsing. */\ntype ParsedEphemeralEnvelope<Definition> = Definition extends { ephemeral: true }\n ? { ephemeral: true }\n : unknown;\n\n/** A typed builder preserves the supplied envelope while returning parsed payload/default output. */\ntype BuiltInputFromEvent<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Event extends { type: string },\n> = Event extends unknown\n ? Omit<Event, \"payload\"> &\n Pick<ParsedInputFromType<Events, ProcessorDeps, Event[\"type\"]>, \"payload\"> &\n ParsedEphemeralEnvelope<EventDefinitionForType<Events, ProcessorDeps, Event[\"type\"]>>\n : never;\n\n/** Parsed durable inputs for a processor's `consumes` tuple. */\ntype ParsedConsumedInputFromTypes<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Types extends readonly string[],\n> = \"*\" extends Types[number]\n ? Omit<StreamEventInput, \"ephemeral\"> & { ephemeral?: never }\n : Types[number] extends infer Type extends string\n ? EventDefinitionForType<Events, ProcessorDeps, Type> extends EventDefinition<\n infer PayloadOutput,\n unknown\n >\n ? TypedConsumedEventInput<Type, PayloadOutput>\n : never\n : never;\n\n/** Union of committed-event shapes a contract's `consumes` list can deliver to `reduce`. */\nexport type ConsumedEvent<Contract> = Contract extends {\n events: EventCatalog;\n consumes: infer Consumes extends readonly string[];\n}\n ? EventFromTypes<ContractEventCatalog<Contract>, ProcessorDepsOf<Contract>, Consumes>\n : never;\n\n/**\n * Union of durable append-input shapes accepted by a contract's `consumes`\n * list. Ephemeral events are excluded because hosted processors cannot consume\n * them; append those intentionally through the raw Stream door. This is a\n * schema/vocabulary union, not proof that an event is valid in the processor's\n * current state or came from a particular provenance.\n */\nexport type ConsumedInput<Contract> = Contract extends {\n events: EventCatalog;\n consumes: infer Consumes extends readonly string[];\n}\n ? ConsumedInputFromTypes<ContractEventCatalog<Contract>, ProcessorDepsOf<Contract>, Consumes>\n : never;\n\n/** Union of append-input shapes a contract's `emits` list allows a processor to append. */\nexport type EmittedInput<Contract> = Contract extends {\n events: EventCatalog;\n emits: infer Emits extends readonly string[];\n}\n ? InputFromType<ContractEventCatalog<Contract>, ProcessorDepsOf<Contract>, Emits[number]>\n : never;\n\n/** A contract's `processorDeps` tuple, defaulting to empty when absent. */\ntype ProcessorDepsOf<Contract> = Contract extends {\n processorDeps?: infer ProcessorDeps;\n}\n ? ProcessorDeps extends readonly unknown[]\n ? ProcessorDeps\n : readonly []\n : readonly [];\n\n/** A contract's reduced-state type, inferred from its `stateSchema`. */\nexport type ProcessorState<Contract> = Contract extends {\n stateSchema: infer State extends z.ZodType;\n}\n ? z.output<State>\n : never;\n\n// -----------------------------------------------------------------------------\n// Authoring-time validation types for defineProcessorContract.\n// -----------------------------------------------------------------------------\n\n/** Reduced state must be object-shaped and must accept `{}` (the empty initial state). */\ntype DefaultableObjectStateSchema<StateSchema extends z.ZodType> =\n z.output<StateSchema> extends Record<string, unknown>\n ? {} extends z.input<StateSchema>\n ? StateSchema\n : never\n : never;\n\n/**\n * Compile-time typo guard for `consumes` / `emits`: resolves to `unknown` when\n * every string in `Types` is resolvable (leaving the contract argument\n * unchanged), `never` when one is not — failing the call where the bad string\n * is written. `AllowStar` admits the `\"*\"` wildcard (consumes only).\n */\ntype ResolvedEventTypesOnly<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Types extends readonly string[],\n AllowStar extends string = never,\n> = [Exclude<Exclude<Types[number], AllowStar>, ResolvedEventType<Events, ProcessorDeps>>] extends [\n never,\n]\n ? unknown\n : never;\n\n/** `contract.buildEvent(...)`: validate an append input against the resolved payload schema. */\ntype ProcessorContractBuildEvent<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n> = <\n const Event extends InputFromType<\n Events,\n ProcessorDeps,\n ResolvedEventType<Events, ProcessorDeps>\n > & { type: string },\n>(\n event: Event,\n) => BuiltInputFromEvent<Events, ProcessorDeps, Event>;\n\n/** Resolved contract types compatible with an event's current discriminator type. */\ntype ResolvedTypeFromEvent<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Event extends { type: string },\n> = Extract<ResolvedEventType<Events, ProcessorDeps>, Event[\"type\"]>;\n\n/** `contract.parseEvent(...)`: validate a committed event and infer its output from `event.type`. */\ntype ProcessorContractParseEvent<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n> = <const Event extends StreamEvent>(\n event: Event,\n) => EventFromType<Events, ProcessorDeps, ResolvedTypeFromEvent<Events, ProcessorDeps, Event>>;\n\n/**\n * Same as `parseEvent`, but for append inputs that do not yet have an offset or\n * createdAt. This exists for stream-owned pre-commit policy: the Stream Durable\n * Object must reject some contract-owned events BEFORE they become durable\n * facts (see the core processor's `validate`) — validating them\n * later, in the wake side effect, would leave the invalid event committed and\n * reduced into durable state. The lifecycle e2e tests assert both the rejection\n * and that nothing was committed.\n */\ntype ProcessorContractParseEventInput<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n> = <const Event extends StreamEventInput>(\n event: Event,\n) => ParsedInputFromType<\n Events,\n ProcessorDeps,\n ResolvedTypeFromEvent<Events, ProcessorDeps, Event>\n>;\n\n/**\n * `contract.parseConsumedInput(...)`: validate one domain-object append\n * against the exact event vocabulary delivered to the processor.\n */\ntype ProcessorContractParseConsumedInput<\n Events extends EventCatalog,\n ProcessorDeps extends readonly unknown[],\n Consumes extends readonly string[],\n> = <const Event extends ConsumedInputFromTypes<Events, ProcessorDeps, Consumes>>(\n event: Event,\n) => \"*\" extends Consumes[number]\n ? ParsedConsumedInputFromTypes<Events, ProcessorDeps, Consumes>\n : ParsedConsumedInputFromTypes<\n Events,\n ProcessorDeps,\n readonly Extract<Consumes[number], Event[\"type\"]>[]\n >;\n\n// =============================================================================\n// Runtime event parsers (bound to this app's event schemas).\n// =============================================================================\n\n/**\n * Rebuild the concrete Zod envelope for one committed event from its catalog\n * key plus `payloadSchema`. Contracts author event definitions as plain\n * `{ description, payloadSchema }` values keyed by the event type string, so\n * replay and live delivery share one validation path.\n */\nfunction getEventSchema<const Type extends string, const PayloadSchema extends z.ZodType>(args: {\n type: Type;\n payloadSchema: PayloadSchema;\n ephemeral?: boolean;\n}): z.ZodType<\n TypedStreamEvent<Type, z.output<PayloadSchema>>,\n TypedStreamEvent<Type, z.input<PayloadSchema>>\n> {\n return (\n z\n .looseObject({\n type: z.literal(args.type),\n payload: args.payloadSchema,\n metadata: StreamEventSchema.shape.metadata,\n source: StreamEventSchema.shape.source,\n idempotencyKey: StreamEventSchema.shape.idempotencyKey,\n // COMMITTED events keep their stored flag verbatim — never the\n // definition's forced default. A definition that later became\n // `ephemeral: true` (the connection presence facts) must not rewrite\n // history: durable rows committed before the change parse without the\n // flag and replay durably, exactly as they were folded at commit time.\n // Only the INPUT schema (getEventInputSchema) forces the definition's\n // choice, so no new durable instance can be appended.\n ephemeral: StreamEventSchema.shape.ephemeral,\n offset: StreamEventSchema.shape.offset,\n createdAt: StreamEventSchema.shape.createdAt,\n path: StreamEventSchema.shape.path,\n })\n // Zod widens the object after a dynamic envelope shape plus `superRefine`,\n // so it cannot retain the relationship between `Type`, `PayloadSchema`,\n // and this function's generic result. Every envelope field above comes\n // from the canonical StreamEvent schema, while the literal type and\n // payload schema supply exactly the two narrowed fields; the refinement\n // only rejects an otherwise-invalid combination. The cast restores that\n // precise generic relationship without weakening runtime validation.\n .superRefine(rejectEphemeralIdempotency) as unknown as z.ZodType<\n TypedStreamEvent<Type, z.output<PayloadSchema>>,\n TypedStreamEvent<Type, z.input<PayloadSchema>>\n >\n );\n}\n\n/** The envelope `ephemeral` slot: for a definition marked `ephemeral: true`,\n * absent defaults to `true` and an explicit `false` FAILS the parse — the\n * contract, not the append site, decides that the event never becomes a\n * durable stream fact. */\nfunction ephemeralEnvelopeSchema(forced: boolean | undefined, standard: z.ZodType): z.ZodType {\n return forced === true ? z.literal(true).default(true) : standard;\n}\n\nfunction rejectEphemeralIdempotency(\n event: { ephemeral?: unknown; idempotencyKey?: unknown },\n context: z.RefinementCtx,\n): void {\n if (event.ephemeral === true && event.idempotencyKey !== undefined) {\n context.addIssue({\n code: \"custom\",\n message: \"ephemeral events cannot have an idempotencyKey\",\n path: [\"idempotencyKey\"],\n });\n }\n}\n\n/**\n * `getEventSchema` without offset/createdAt (and strict, so an accidental\n * `offset` key on an append input fails loudly). Gives pre-append policy code\n * the same payload validation as reducers, without fabricating a committed\n * event just to get at the typed payload.\n */\nexport function getEventInputSchema<\n const Type extends string,\n const PayloadSchema extends z.ZodType,\n>(args: {\n type: Type;\n payloadSchema: PayloadSchema;\n ephemeral?: boolean;\n}): z.ZodType<\n TypedStreamEventInput<Type, z.output<PayloadSchema>>,\n TypedStreamEventInput<Type, z.input<PayloadSchema>>\n> {\n return (\n z\n .strictObject({\n type: z.literal(args.type),\n payload: args.payloadSchema,\n metadata: StreamEventInputSchema.shape.metadata,\n source: StreamEventInputSchema.shape.source,\n idempotencyKey: StreamEventInputSchema.shape.idempotencyKey,\n ephemeral: ephemeralEnvelopeSchema(args.ephemeral, StreamEventInputSchema.shape.ephemeral),\n })\n // As above, Zod cannot express that this dynamically assembled and\n // refined object has the input/output types of `PayloadSchema` paired with\n // the literal `Type`. The remaining fields come directly from the\n // canonical StreamEventInput schema and `strictObject` rejects extras, so\n // the cast only recovers the generic relationship already enforced by the\n // runtime shape; it does not admit values the parser would accept unsafely.\n .superRefine(rejectEphemeralIdempotency) as unknown as z.ZodType<\n TypedStreamEventInput<Type, z.output<PayloadSchema>>,\n TypedStreamEventInput<Type, z.input<PayloadSchema>>\n >\n );\n}\n\n// =============================================================================\n// Contract definition + resolution machinery.\n// =============================================================================\n\n/**\n * Memoized twins of {@link getEventSchema} / {@link getEventInputSchema} for\n * hot paths. Constructing the zod wrapper per call costs ~20µs (~50x the\n * parse itself), and the reduce/append paths run once per event. Keyed by\n * payload-schema identity, then event type: catalog entries are module\n * constants, so the WeakMap never grows past the contract surface.\n */\nconst eventSchemaCache = new WeakMap<z.ZodType, Map<string, z.ZodType>>();\n\nfunction cachedSchema(\n cache: WeakMap<z.ZodType, Map<string, z.ZodType>>,\n build: (args: { type: string; payloadSchema: z.ZodType; ephemeral?: boolean }) => z.ZodType,\n args: { type: string; payloadSchema: z.ZodType; ephemeral?: boolean },\n): z.ZodType {\n let byType = cache.get(args.payloadSchema);\n if (byType === undefined) {\n byType = new Map();\n cache.set(args.payloadSchema, byType);\n }\n let schema = byType.get(args.type);\n if (schema === undefined) {\n schema = build(args);\n byType.set(args.type, schema);\n }\n return schema;\n}\n\n/** Memoized {@link getEventSchema} (see {@link eventSchemaCache}). */\nexport function cachedEventSchema(args: {\n type: string;\n payloadSchema: z.ZodType;\n ephemeral?: boolean;\n}): z.ZodType {\n return cachedSchema(eventSchemaCache, getEventSchema, args);\n}\n\n/** Union of append-input shapes for every event a contract can resolve (own + deps). */\ntype ResolvedEventInput<Contract> = Contract extends {\n events: EventCatalog;\n}\n ? InputFromType<\n ContractEventCatalog<Contract>,\n ProcessorDepsOf<Contract>,\n ResolvedEventType<ContractEventCatalog<Contract>, ProcessorDepsOf<Contract>>\n >\n : never;\n\n/**\n * Validate an append input with the payload schema resolved from a processor\n * contract. Prefer the contract-bound `contract.buildEvent(event)` API, which\n * carries the same types without repeating the contract in the argument.\n *\n * @deprecated Use `contract.buildEvent(event)`.\n */\nexport function buildEvent<\n const Contract extends {\n slug?: string;\n events: EventCatalog;\n processorDeps?: readonly unknown[];\n },\n const Event extends ResolvedEventInput<NoInfer<Contract>> & { type: string },\n>(args: {\n contract: Contract;\n event: Event;\n}): BuiltInputFromEvent<ContractEventCatalog<Contract>, ProcessorDepsOf<Contract>, Event> {\n return parseResolvedEventInput(args.contract, args.event) as BuiltInputFromEvent<\n ContractEventCatalog<Contract>,\n ProcessorDepsOf<Contract>,\n Event\n >;\n}\n\nfunction parseResolvedEventInput(\n contract: {\n slug?: string;\n events: EventCatalog;\n processorDeps?: readonly unknown[];\n },\n event: { type: string },\n): unknown {\n const eventDefinition = getResolvedEventDefinition({\n contract,\n eventType: event.type,\n });\n if (eventDefinition === undefined) {\n const owner = contract.slug == null ? \"contract\" : `processor \"${contract.slug}\"`;\n throw new Error(`${owner} cannot build unresolved event \"${event.type}\".`);\n }\n return getEventInputSchema({\n type: event.type,\n payloadSchema: eventDefinition.payloadSchema,\n ephemeral: eventDefinition.ephemeral,\n }).parse(event);\n}\n\n/**\n * Typed identity for processor contracts: validation plus the pre-bound\n * `buildEvent` / `parseEvent` / `parseEventInput` / `parseConsumedInput`\n * helpers.\n *\n * The signature enforces the important invariants at authoring time:\n *\n * - `stateSchema` must parse `{}` to an object-shaped reduced state;\n * - every string in `consumes` and `emits` must resolve against local `events`\n * plus `processorDeps` (and both are contextually typed for autocomplete);\n * - local `events` must not redefine an event already owned by a\n * `processorDeps` contract. Event ownership is intentionally one processor\n * deep: a processor can depend on another owner, but it cannot shadow that\n * owner's public event type with a second payload schema.\n */\nexport function defineProcessorContract<\n const StateSchema extends z.ZodType,\n const Events extends EventCatalog,\n const Consumes extends readonly (ResolvedEventType<Events, ProcessorDeps> | \"*\")[],\n const Emits extends readonly ResolvedEventType<Events, ProcessorDeps>[],\n const ProcessorDeps extends readonly unknown[] = readonly [],\n>(contract: {\n slug: string;\n version: string;\n description: string;\n stateSchema: DefaultableObjectStateSchema<StateSchema>;\n processorDeps?: ProcessorDeps;\n events: Events;\n consumes: Consumes & ResolvedEventTypesOnly<Events, ProcessorDeps, Consumes, \"*\">;\n emits: Emits & ResolvedEventTypesOnly<Events, ProcessorDeps, Emits>;\n}): {\n slug: string;\n version: string;\n description: string;\n stateSchema: StateSchema;\n processorDeps?: ProcessorDeps;\n events: Events;\n consumes: Consumes;\n emits: Emits;\n buildEvent: ProcessorContractBuildEvent<Events, ProcessorDeps>;\n parseEvent: ProcessorContractParseEvent<Events, ProcessorDeps>;\n parseEventInput: ProcessorContractParseEventInput<Events, ProcessorDeps>;\n parseConsumedInput: ProcessorContractParseConsumedInput<Events, ProcessorDeps, Consumes>;\n};\n// The overload above is the public type. The runtime implementation returns\n// `unknown` because TypeScript cannot relate the generic helper-method shapes\n// to the dynamically validated Object.assign result.\nexport function defineProcessorContract(contract: unknown): unknown {\n assertNoLocalProcessorDepEventConflicts(contract);\n assertDefaultStateSchema(contract);\n if (typeof contract !== \"object\" || contract === null) {\n throw new Error(\"Processor contract must be an object.\");\n }\n for (const method of [\n \"buildEvent\",\n \"parseEvent\",\n \"parseEventInput\",\n \"parseConsumedInput\",\n ] as const) {\n if (method in contract) {\n throw new Error(`Processor \"${getProcessorSlug(contract)}\" must not define ${method}.`);\n }\n }\n const typedContract = contract as {\n events: EventCatalog;\n processorDeps?: readonly unknown[];\n consumes: readonly string[];\n };\n return Object.assign(typedContract, {\n buildEvent(event: { type: string }) {\n return parseResolvedEventInput(typedContract, event);\n },\n parseEvent: makeContractEventParser(typedContract, getEventSchema),\n parseEventInput: makeContractEventParser(typedContract, getEventInputSchema),\n parseConsumedInput: makeContractConsumedInputParser(typedContract),\n });\n}\n\n/**\n * Runtime twin of {@link ConsumedInput}. Unlike `parseEventInput`, this parser\n * rejects a resolved event that the processor does not actually consume. A\n * domain object's typed `append` door uses both so its remote runtime boundary\n * cannot drift from the processor contract after TypeScript has been erased.\n */\nfunction makeContractConsumedInputParser(contract: {\n events: EventCatalog;\n processorDeps?: readonly unknown[];\n consumes: readonly string[];\n}) {\n const parserCache = new Map<string, { parse(value: unknown): unknown }>();\n return (event: { type: string }) => {\n if ((event as StreamEventInput).ephemeral === true) {\n throw new Error(\n `Processor \"${getProcessorSlug(contract)}\" cannot consume ephemeral event \"${event.type}\".`,\n );\n }\n const eventDefinition = getConsumedEventDefinition({\n contract,\n eventType: event.type,\n });\n if (eventDefinition === undefined) {\n throw new Error(\n `Processor \"${getProcessorSlug(contract)}\" does not consume event \"${event.type}\".`,\n );\n }\n\n let schema = parserCache.get(event.type);\n if (schema === undefined) {\n schema = getEventInputSchema({\n type: event.type,\n payloadSchema: eventDefinition.payloadSchema,\n ephemeral: eventDefinition.ephemeral,\n });\n parserCache.set(event.type, schema);\n }\n return schema.parse(event);\n };\n}\n\n/**\n * Shared runtime body of `contract.parseEvent(...)` and\n * `contract.parseEventInput(...)`: both resolve the payload schema from the\n * contract catalog, so an edit to a core event schema automatically affects\n * committed-event reduction and pre-commit validation together.\n */\nfunction makeContractEventParser(\n contract: { events: EventCatalog; processorDeps?: readonly unknown[] },\n schemaFor: (args: { type: string; payloadSchema: z.ZodType; ephemeral?: boolean }) => {\n parse(value: unknown): unknown;\n },\n) {\n const parserCache = new Map<string, { parse(value: unknown): unknown }>();\n return (event: { type: string }) => {\n const eventType = event.type;\n const eventDefinition = getResolvedEventDefinition({ contract, eventType });\n if (eventDefinition == null) {\n throw new Error(\n `Processor \"${getProcessorSlug(contract)}\" cannot parse unresolved event \"${eventType}\".`,\n );\n }\n // Memoized: contract.parseEventInput runs inside the synchronous append\n // turn (core policy validation), where per-call schema construction was\n // measurable.\n let schema = parserCache.get(eventType);\n if (schema === undefined) {\n schema = schemaFor({\n type: eventType,\n payloadSchema: eventDefinition.payloadSchema,\n ephemeral: eventDefinition.ephemeral,\n });\n parserCache.set(eventType, schema);\n }\n return schema.parse(event);\n };\n}\n\n/**\n * Enforces the invariant that reduced processor state is object-shaped (so\n * state slices can evolve safely and hooks never branch on primitive state).\n */\nexport function assertObjectProcessorState(args: { processorSlug: string; value: unknown }) {\n if (typeof args.value === \"object\" && args.value !== null && !Array.isArray(args.value)) {\n return;\n }\n throw new Error(`Processor \"${args.processorSlug}\" state must be an object.`);\n}\n\nfunction assertDefaultStateSchema(contract: unknown): void {\n if (typeof contract !== \"object\" || contract === null) {\n throw new Error(\"Processor contract must be an object.\");\n }\n const processorSlug = getProcessorSlug(contract);\n if (!(\"stateSchema\" in contract) || !isZodSchema(contract.stateSchema)) {\n throw new Error(`Processor \"${processorSlug}\" must define stateSchema.`);\n }\n\n let defaultState: unknown;\n try {\n defaultState = contract.stateSchema.parse({});\n } catch (error) {\n throw new Error(`Processor \"${processorSlug}\" stateSchema must parse {}.`, {\n cause: error,\n });\n }\n\n assertObjectProcessorState({ processorSlug, value: defaultState });\n}\n\n/**\n * Resolve the payload schema a processor should use for an incoming event:\n * the named definition when the type is listed in `consumes`, a permissive\n * `z.unknown()` definition when the contract consumes `\"*\"`, and `undefined`\n * when the event is not consumed at all. Runtime counterpart of\n * `ConsumedEvent<Contract>`.\n *\n * `\"*\"` NEVER MATCHES AN EPHEMERAL EVENT, and that one rule is what lets\n * ephemeral types live in `consumes` beside durable ones instead of in a\n * parallel list. Naming a type explicitly is the opt-in: you cannot be handed\n * a microphone firehose by a wildcard you wrote for durable facts, and a\n * processor that wants live events says so by type. Ephemeral bodies live\n * only in the Stream DO's bounded buffer, so a processor receiving one must\n * have decided it can cope with never seeing it again — a decision nobody\n * makes by writing `\"*\"`.\n */\nexport function getConsumedEventDefinition(args: {\n contract: {\n events: EventCatalog;\n processorDeps?: readonly unknown[];\n consumes: readonly string[];\n };\n eventType: string;\n /** Whether the event being resolved is ephemeral; gates the `\"*\"` fallback. */\n ephemeral?: boolean;\n}): EventDefinition | undefined {\n if (!args.contract.consumes.includes(args.eventType)) {\n if (args.ephemeral === true) return undefined;\n if (args.contract.consumes.includes(\"*\")) return { payloadSchema: z.unknown() };\n return undefined;\n }\n const eventDefinition = getResolvedEventDefinition(args);\n if (eventDefinition == null) {\n throw new Error(`Unresolved stream processor consumes event type \"${args.eventType}\".`);\n }\n /*\n * NAMING A TYPE IS NOT THE SAME AS ACCEPTING AN EPHEMERAL COPY OF IT.\n *\n * A type is only ephemeral if its DEFINITION says so; the envelope flag is\n * otherwise per-append, so anyone may append an ephemeral copy of an\n * ordinarily-durable type. Admitting that would let it be folded into\n * reduced state, and reduced state must equal folding the durable log —\n * caught by an existing test that appends `scheduler/schedule-set` both\n * ways and asserts only the durable one survives.\n *\n * So the contract decides on both sides: the catalogue says which types are\n * live-only, `consumes` says which of them you want.\n */\n if (args.ephemeral === true && eventDefinition.ephemeral !== true) return undefined;\n return eventDefinition;\n}\n\nexport function getResolvedEventDefinition(args: {\n contract: {\n events: EventCatalog;\n processorDeps?: readonly unknown[];\n };\n eventType: string;\n}): EventDefinition | undefined {\n const localEventDefinition = args.contract.events[args.eventType];\n if (localEventDefinition != null) return localEventDefinition;\n\n for (const dependency of args.contract.processorDeps ?? []) {\n const dependencyEventDefinition = getDependencyEvents(dependency)?.[args.eventType];\n if (dependencyEventDefinition != null) return dependencyEventDefinition;\n }\n\n return undefined;\n}\n\nfunction getDependencyEvents(dependency: unknown): EventCatalog | undefined {\n if (isEventCatalog(dependency)) return dependency;\n if (\n typeof dependency === \"object\" &&\n dependency !== null &&\n \"events\" in dependency &&\n isEventCatalog(dependency.events)\n ) {\n return dependency.events;\n }\n return undefined;\n}\n\nfunction assertNoLocalProcessorDepEventConflicts(contract: unknown): void {\n if (typeof contract !== \"object\" || contract === null || !(\"events\" in contract)) return;\n if (!isEventCatalog(contract.events)) return;\n\n const processorDeps =\n \"processorDeps\" in contract && Array.isArray(contract.processorDeps)\n ? contract.processorDeps\n : [];\n\n for (const dependency of processorDeps) {\n const dependencyEvents = getDependencyEvents(dependency);\n if (dependencyEvents === undefined) continue;\n\n for (const type of Object.keys(contract.events)) {\n if (!Object.prototype.hasOwnProperty.call(dependencyEvents, type)) continue;\n throw new Error(\n `Processor \"${getProcessorSlug(contract)}\" defines event \"${type}\" that is already owned by processor dependency \"${getProcessorSlug(dependency)}\".`,\n );\n }\n }\n}\n\nfunction isEventCatalog(value: unknown): value is EventCatalog {\n if (typeof value !== \"object\" || value === null) return false;\n return Object.values(value).every(isEventDefinition);\n}\n\nfunction isEventDefinition(value: unknown): value is EventDefinition {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"payloadSchema\" in value &&\n typeof value.payloadSchema === \"object\" &&\n value.payloadSchema !== null\n );\n}\n\nfunction isZodSchema(value: unknown): value is z.ZodType {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"parse\" in value &&\n typeof value.parse === \"function\"\n );\n}\n\nfunction getProcessorSlug(contract: unknown): string {\n if (\n typeof contract === \"object\" &&\n contract !== null &&\n \"slug\" in contract &&\n typeof contract.slug === \"string\"\n ) {\n return contract.slug;\n }\n return \"unknown\";\n}\n\n/**\n * The ONE platform revival fact for every recovery-wired stream processor.\n * Appended by the platform keepalive (`durableObjectRecovery` in\n * durable-object-processor-durability.ts) when a processor is revived after\n * its incarnation died owing background work — never emitted by a processor.\n * Per-processor identity rides the payload's `processorSlug` and the\n * `processor-revived:<slug>@...` idempotency key, not the type string.\n * Consuming it is OPTIONAL: a processor should do so only when it reacts to\n * the fact itself. Its append still wakes delivery when it is unconsumed, and\n * a head-reaching frame receives the runner's eventless\n * `processEvent(event: null, caughtUp: true)` pass so open obligations are not\n * stranded. The event DEFINITION (payload schema) lives with the platform's\n * core stream contract; this constant is here so contracts and the recovery\n * adapter agree on the type string without importing that contract.\n */\nexport const STREAM_PROCESSOR_REVIVED_EVENT_TYPE = \"events.iterate.com/stream/processor-revived\";\n\n/**\n * A processor contract announcement carried on `connection-opened` when the\n * callback owner is a hosted stream processor. UIs and tooling read it from\n * that event and from `runtime.connections[..].openedBy`.\n */\nexport const ProcessorContractAnnouncement = z.object({\n slug: z.string().trim().min(1),\n version: z.string().trim().min(1),\n description: z.string(),\n consumes: z.array(z.string()),\n emits: z.array(z.string()),\n ownedEvents: z.array(\n z.object({\n type: z.string().trim().min(1),\n description: z.string().optional(),\n }),\n ),\n});\n\nexport type ProcessorContractAnnouncement = z.infer<typeof ProcessorContractAnnouncement>;\n\n/**\n * Platform stream events a processor contract may CONSUME without owning —\n * pass as a `processorDeps` entry. Currently just the keepalive revival fact.\n * Consumption is optional and belongs only in processors that react to the\n * fact itself; an unconsumed revival tail receives the runner's eventless\n * at-head turn. The event's authoritative definition lives with the\n * platform's core stream contract, and this catalog is deliberately\n * payload-loose.\n */\nexport const PLATFORM_STREAM_EVENTS = {\n [STREAM_PROCESSOR_REVIVED_EVENT_TYPE]: {\n description:\n \"Platform keepalive revival fact: appended when a processor's incarnation died owing background work; consumed when the processor reacts to the fact itself, otherwise followed by an eventless at-head processEvent turn.\",\n payloadSchema: z.looseObject({}),\n },\n};\n","import { RpcTarget } from \"@iterate-com/capnweb\";\nimport type { z } from \"zod\";\nimport { resolveStreamPath, type ProcessorStream } from \"./stream-handle.ts\";\nimport type { StreamEvent, StreamEventInput } from \"./schemas.ts\";\nimport type { ProcessorRuntimeState, ProcessorSnapshot } from \"./rpc-types.ts\";\n// Type-only by necessity, not just hygiene: the runner imports this module's\n// VALUE (the class, for `runnerHooks`), so a value import back would be a\n// runtime cycle. Types erase; the cycle doesn't exist at runtime.\nimport type { DeliveryContext } from \"./stream-processor-runner.ts\";\nimport { EventConsumptionMetrics } from \"./event-consumption-metrics.ts\";\nimport {\n assertObjectProcessorState,\n cachedEventSchema,\n getConsumedEventDefinition,\n getEventInputSchema,\n getResolvedEventDefinition,\n type ConsumedEvent,\n type EmittedInput,\n type EventCatalog,\n type ProcessorState,\n} from \"./processor-contracts.ts\";\n\n// =============================================================================\n// Class-based stream processor runtime.\n// =============================================================================\n\nexport type MaybePromise<T> = T | Promise<T>;\n\n// `keepAliveWhile` is fire-and-forget from the host's point of view (it only\n// keeps the runtime alive while the work runs), so this bridges the work's\n// result/failure back into a promise the caller can await.\nexport async function awaitKeepAliveBacked<T>(\n keepAliveWhile: ((work: () => Promise<unknown>) => void) | undefined,\n work: () => Promise<T>,\n): Promise<T> {\n if (keepAliveWhile === undefined) return await work();\n\n return await new Promise<T>((resolve, reject) => {\n keepAliveWhile(async () => {\n try {\n const result = await work();\n resolve(result);\n return result;\n } catch (error) {\n reject(error);\n throw error;\n }\n });\n });\n}\n\n/**\n * The structural slice of a processor contract that the class needs. Contracts\n * built with `defineProcessorContract(...)` satisfy this; the full contract\n * type flows through the `Contract` type parameter so event/state inference\n * reaches the hooks.\n */\nexport type StreamProcessorContract = {\n slug: string;\n version: string;\n stateSchema: z.ZodType;\n events: EventCatalog;\n processorDeps?: readonly unknown[];\n consumes: readonly string[];\n emits: readonly string[];\n parseEvent(event: StreamEvent): StreamEvent;\n};\n\n/**\n * Constructor dependencies shared by every processor: the stream append\n * capability and the home stream's identity (`path` / `projectId`, stamped as\n * provenance onto every emitted event), plus an optional `keepAliveWhile`\n * hook for processors whose own out-of-band work (a DO verb like the\n * scheduler's `triggerDue`) must keep the runtime alive while it runs.\n * Delivery, cursors, and checkpoints are NOT deps: the StreamProcessorRunner\n * (stream-processor-runner.ts) owns all of that and drives the processor from\n * outside.\n */\nexport type StreamProcessorBaseDeps = {\n stream: ProcessorStream;\n /** Path of the stream this processor runs on (the stream `stream` points at). */\n path: string;\n /** Owning project, or null on a global (deployment-root) stream. */\n projectId: string | null;\n keepAliveWhile?: (work: () => Promise<unknown>) => void;\n};\n\n// `ReduceArgs` / `ProcessEventArgs` are exported as the one sanctioned\n// spelling for subclass hook annotations: the hooks are `protected`, so\n// `Parameters<StreamProcessor<Contract>[\"method\"]>[0]` is not writable from\n// outside a subclass body.\n//\n// State and events are passed by reference. Hooks must treat them as immutable:\n// `reduce` returns a new state object instead of mutating its input.\ntype ReducedEvent<Contract> = {\n event: ConsumedEvent<Contract>;\n previousState: ProcessorState<Contract>;\n state: ProcessorState<Contract>;\n};\n\n/**\n * A consumed-type event whose shape failed the contract parse. Distinguished\n * from `undefined` (type not consumed at all) so the runner can skip the event\n * AND record the skip durably instead of silently dropping it.\n */\ntype ConsumedEventParseFailure = { parseError: z.ZodError };\n\n/** What `reduce` receives: one consumed event and the state to fold it into. */\nexport type ReduceArgs<Contract> = {\n event: ConsumedEvent<Contract>;\n state: ProcessorState<Contract>;\n};\n\n/**\n * Side-effect scheduling helpers handed to the `process*` hooks. Two\n * primitives, two guarantees — every side effect must pick one deliberately:\n *\n * - `blockProcessorWhile` — SHORT work the next event must not overtake.\n * At-least-once: the cursor is held, a crash resends the event batch, and\n * append idempotency keys collapse the re-run. Long work does NOT belong\n * here: it head-of-line-blocks every later event (including cancellations).\n *\n * - `runInBackground` — a DROPPABLE ATTEMPT. The cursor advances\n * immediately; an eviction loses the closure silently. Every callsite must\n * answer \"what recovers the OUTCOME if this attempt drops?\" — legitimate\n * answers are \"an at-head pass (`processEvent` under\n * `delivery.caughtUp`), via stream-backed requested/completed evidence\" (LLM\n * calls, scripts, debounce timers) or\n * \"nothing, the outcome genuinely doesn't matter\" (telemetry). A naked\n * runInBackground around consequential work with no recovery pass is the bug\n * class the 2026-06-10 / 2026-07-07 incidents came from.\n *\n * Both are keepalive-backed: while either kind of work is in flight the\n * runner's recovery adapter parks a durable alarm ahead of it, so an\n * incarnation that dies owing work is revived and the processors get their\n * at-head pass (docs/writing-stream-processors.md has the full doctrine).\n */\ntype SideEffectHelpers = {\n /** Hold the cursor (and the next event) until this work completes.\n * Blocking is the EXCEPTION, not the default — justify it at the call site\n * with a comment explaining why the next event must wait (i.e. why losing\n * this append would lose a per-event consequence forever).\n * Registrations run STRICTLY IN REGISTRATION ORDER: each blocker starts\n * only after the previous one settles, so a later registration in the same\n * `processEvent` body observes the earlier work's appends. Order\n * state-derived work after per-event work by writing it later in the\n * function — no separate lane needed. */\n blockProcessorWhile: (work: () => Promise<unknown>) => void;\n /** A droppable attempt; failures are caught and logged, evictions lose it. */\n runInBackground: (work: () => Promise<unknown>) => void;\n};\n\n/** What `processEvent` receives: one reduction result plus delivery context and helpers. */\nexport type ProcessEventArgs<Contract> = Omit<ReducedEvent<Contract>, \"event\"> &\n SideEffectHelpers & {\n /**\n * The consumed event being processed — or `null` for an eventless call where\n * `delivery.caughtUp` is true. The runner makes that call when a batch scans\n * through the highest observed offset but no consumed event carried `caughtUp`\n * (for example, the final row is stream/connection-closed). The processor\n * still needs a chance to act on the complete observed fold. A per-event switch MUST guard on\n * `event !== null`; the caught-up processing reads `state` and needs no event.\n */\n event: ReducedEvent<Contract>[\"event\"] | null;\n /**\n * Append one or more events listed in `contract.emits` to this stream,\n * stamped with `source.processor` provenance pointing at THIS event as\n * `whileProcessing` (unstamped on the event-less caught-up call). The binding\n * is a closure, so appends made later from\n * `blockProcessorWhile`/`runInBackground` work scheduled here still stamp\n * the event that was being processed.\n */\n append: (...input: EmittedInput<Contract>[]) => Promise<StreamEvent[]>;\n /** Like `append`, onto a sibling stream (resolved via `stream.at(path)`). */\n appendTo: (path: string, ...input: EmittedInput<Contract>[]) => Promise<StreamEvent[]>;\n /**\n * Honest event-time context (delivery phase, highest observed offset, cursor\n * revision) supplied by the StreamProcessorRunner, the only driver.\n */\n delivery: DeliveryContext;\n };\n\n/**\n * What the PROCESSOR contributes to the published {@link ProcessorRuntimeState}:\n * the operational `runtime` bag only — subclass debug data, never cursor\n * state. The SNAPSHOT half is supplied by the StreamProcessorRunner (the\n * cursor owner) when a host assembles the full runtime state\n * (stream-processor-registry.ts `reads`/`wakeStreamProcessor`, the browser\n * host's capabilities), and self-measured event-consumption metrics are merged\n * in host-side so an override cannot accidentally drop them.\n */\nexport type ProcessorRuntimeContribution = { runtime?: Record<string, unknown> };\n\n/**\n * The read surface a `StreamProcessorRpcTarget` (rpc-targets.ts) serves — the\n * three inspection reads of the public `StreamProcessorRpc` contract. The\n * provider is the hosting registry's `reads(processor)`\n * (stream-processor-registry.ts): the runner owns both cursors, so snapshot /\n * waitUntilEvent answer from the runner's committed progress and\n * `getRuntimeState` pins the runner's snapshot under the processor's own\n * runtime bag.\n */\nexport type ProcessorReads<State> = {\n snapshot(): Promise<ProcessorSnapshot<State>>;\n getRuntimeState(): Promise<ProcessorRuntimeState<State>>;\n waitUntilEvent(input: { offset: number; timeoutMs?: number }): Promise<void>;\n};\n\n/**\n * Constructor args are the base deps plus the subclass's own `Deps` flattened\n * into one object, e.g. `new BrowserRawEventsProcessor({ stream, path,\n * projectId, sql })`.\n */\nexport type StreamProcessorConstructorArgs<Deps extends object = object> = StreamProcessorBaseDeps &\n Deps;\n\n/** The provenance stamp shape (`source.processor`) carried by processor appends. */\ntype ProcessorSourceStamp = NonNullable<NonNullable<StreamEvent[\"source\"]>[\"processor\"]>;\n\n/**\n * @internal The narrow drive surface {@link StreamProcessor.runnerHooks}\n * hands the StreamProcessorRunner (stream-processor-runner.ts): exactly the\n * protected hooks and append methods the event-processing loop needs, nothing an author\n * or operator could reach for. This is how the runner invokes protected\n * members without widening the author-facing surface — authors still only\n * implement `reduce`/`processEvent` (fold-derived side effects ride\n * `processEvent` under `delivery.caughtUp`), and the runner never sees\n * processor-internal state (it owns its own two-cursor progress).\n */\nexport type StreamProcessorRunnerHooks<Contract extends StreamProcessorContract> = {\n readonly contract: Contract;\n /** The schema default — the fold of the empty journal prefix. */\n initialState(): ProcessorState<Contract>;\n /** Validate a persisted fold against the CURRENT state schema (cache-key check). */\n parseState(\n value: unknown,\n ): { success: true; state: ProcessorState<Contract> } | { success: false; error: z.ZodError };\n /** The pure fold step: `undefined` = type not consumed, `parseError` = consumed type, bad shape. */\n reduceRawEvent(args: {\n event: StreamEvent;\n state: ProcessorState<Contract>;\n }): ReducedEvent<Contract> | ConsumedEventParseFailure | undefined;\n /** Whether this event will reach `processEvent` — a consumed type whose\n * payload parses. Stateless (no fold), so the runner can find the last\n * DELIVERED offset of a batch (for `caughtUp`) without pre-folding, and\n * without letting a malformed final event steal the flag. */\n isDeliverable(event: StreamEvent): boolean;\n /** The synchronous per-event side-effect hook (virtual — subclass overrides\n * dispatch). The caught-up processing rides it under `delivery.caughtUp`. */\n processEvent(args: ProcessEventArgs<Contract>): undefined;\n /**\n * Feed the processor's self-measured consumption metrics\n * (`eventConsumptionMetrics.noteBatchIngested`) after a durably committed batch.\n * Without it the wake capability's consumption-lag samples\n * (`runtime.metrics`) go empty under runner drive: appends alone only feed\n * the other half of the consume-your-own-appends loop.\n */\n noteBatchIngested(args: {\n ingestedThroughOffset: number;\n ingestedOffsets: readonly number[];\n newestEventCreatedAtMs?: number;\n ingestStartedAtMs: number;\n atMs: number;\n }): void;\n /** The processor's semantic key — `<slug>/<key>[@<path>:<offset>]`. */\n idempotencyKey(key: string, whileProcessing?: Pick<StreamEvent, \"offset\" | \"path\">): string;\n /** The `source.processor` provenance stamp for runner-authored raw appends. */\n processorStamp(\n streamId: string,\n whileProcessing?: Pick<StreamEvent, \"offset\" | \"type\">,\n ): ProcessorSourceStamp;\n /** Emits-checked, provenance-stamped append to the processor's home stream. */\n append(\n opts: { streamId: string; whileProcessing?: ConsumedEvent<Contract> },\n input: EmittedInput<Contract>[],\n ): Promise<StreamEvent[]>;\n /** Like `append`, onto a sibling stream (resolved via `stream.at(path)`). */\n appendTo(\n path: string,\n opts: { streamId: string; whileProcessing?: ConsumedEvent<Contract> },\n input: EmittedInput<Contract>[],\n ): Promise<StreamEvent[]>;\n};\n\n/**\n * Class-based stream processor.\n *\n * The model in one sentence: the StreamProcessorRunner\n * (stream-processor-runner.ts) delivers ordered events, folds each consumed\n * event into state through `reduce`, hands each reduction to the side-effect\n * hooks, and owns cursors, checkpoints, retry, and recovery — the processor\n * itself is only the hooks.\n *\n * Subclasses override up to two hooks:\n *\n * - `reduce` — pure projection of one consumed event into the next state\n * - `processEvent` — synchronous per-event side effects; what most processors\n * implement. Side effects derived from the whole fold (rather than the\n * delivered event) belong here too, guarded by `args.delivery.caughtUp`.\n * `args.event` is `null` only when a caught-up scan contained no\n * consumed event; authors skip their per-event switch but can still act on\n * the fold.\n *\n * Every hook runs inside the runner's serialized delivery chain: a later\n * batch never starts until the previous one has completed or failed, and the\n * cursor is only committed after the hooks (plus any `blockProcessorWhile`\n * work) succeed.\n */\nexport abstract class StreamProcessor<\n Contract extends StreamProcessorContract,\n Deps extends object = object,\n> extends RpcTarget {\n abstract readonly contract: Contract;\n protected readonly stream: ProcessorStream;\n /** Path of the home stream — the one `this.stream` points at. */\n protected readonly path: string;\n /** Owning project, or null on a global (deployment-root) stream. */\n protected readonly projectId: string | null;\n protected readonly deps: Deps;\n\n /**\n * Self-measured consumption metrics (see event-consumption-metrics.ts): every\n * home-stream append and every committed event batch feeds it (the\n * latter through the driver's `noteBatchIngested`), closing the\n * consume-your-own-appends loop on the processor's own clock. HOSTS merge\n * `eventConsumptionMetrics.report()` into the `getRuntimeState` answer they give\n * the stream (`runtime.metrics`) — merged host-side so a subclass\n * overriding `getRuntimeState` with its own `runtime` bag cannot\n * accidentally drop it. In-memory; resets with the isolate.\n */\n readonly eventConsumptionMetrics = new EventConsumptionMetrics(Date.now());\n\n readonly #keepAliveWhile: ((work: () => Promise<unknown>) => void) | undefined;\n\n constructor(args: StreamProcessorConstructorArgs<Deps>) {\n super();\n // Base deps are destructured out; everything else is the subclass's Deps.\n const { stream, path, projectId, keepAliveWhile, ...deps } = args;\n this.stream = stream;\n this.path = path;\n this.projectId = projectId;\n this.deps = deps as Deps;\n this.#keepAliveWhile = keepAliveWhile;\n }\n\n /**\n * @internal Hands the StreamProcessorRunner its {@link StreamProcessorRunnerHooks}.\n * A STATIC accessor on purpose: statics may reach protected/private members\n * of instances of their own class, so the runner gets the hooks without any\n * new public instance member (nothing for subclasses to see, shadow, or\n * call). Authors never touch this; the runner is its only caller.\n */\n static runnerHooks<Contract extends StreamProcessorContract, Deps extends object>(\n processor: StreamProcessor<Contract, Deps>,\n ): StreamProcessorRunnerHooks<Contract> {\n return {\n contract: processor.contract,\n initialState: () => processor.contract.stateSchema.parse({}) as ProcessorState<Contract>,\n parseState: (value) => {\n const parsed = processor.contract.stateSchema.safeParse(value);\n return parsed.success\n ? { success: true, state: parsed.data as ProcessorState<Contract> }\n : { success: false, error: parsed.error };\n },\n reduceRawEvent: (args) => processor.#reduceRawEvent(args),\n isDeliverable: (event) => processor.#isDeliverable(event),\n processEvent: (args) => processor.processEvent(args),\n noteBatchIngested: (args) => processor.eventConsumptionMetrics.noteBatchIngested(args),\n idempotencyKey: (key, whileProcessing) => processor.idempotencyKey(key, whileProcessing),\n processorStamp: (streamId, whileProcessing) =>\n processor.#processorStamp(streamId, whileProcessing),\n append: (opts, input) =>\n processor.#appendStamped(\n {\n target: processor.stream,\n targetPath: processor.path,\n sourceStreamId: opts.streamId,\n whileProcessing: opts.whileProcessing,\n },\n input,\n ),\n appendTo: (path, opts, input) =>\n processor.#appendStamped(\n {\n ...processor.#appendTarget(path),\n sourceStreamId: opts.streamId,\n whileProcessing: opts.whileProcessing,\n },\n input,\n ),\n };\n }\n\n /**\n * The processor-contributed slice of the published runtime state: the\n * operational `runtime` bag only (see {@link ProcessorRuntimeContribution}).\n * Subclasses override to expose debug data; the base contributes nothing.\n * The snapshot half comes from the runner, and event-consumption metrics are\n * merged in host-side — never read cursor state here.\n */\n async getRuntimeState(): Promise<ProcessorRuntimeContribution> {\n return {};\n }\n\n /** Build and validate an append input for an event listed in `contract.emits`. */\n #buildEmittedEvent(event: EmittedInput<Contract>): EmittedInput<Contract> {\n if (!this.contract.emits.includes(event.type)) {\n throw new Error(\n `Processor \"${this.contract.slug}\" cannot build emitted event \"${event.type}\".`,\n );\n }\n const eventDefinition = getResolvedEventDefinition({\n contract: this.contract,\n eventType: event.type,\n });\n if (eventDefinition === undefined) {\n throw new Error(`Unresolved stream processor emits event type \"${event.type}\".`);\n }\n return getEventInputSchema({\n type: event.type,\n payloadSchema: eventDefinition.payloadSchema,\n ephemeral: eventDefinition.ephemeral,\n }).parse(event) as EmittedInput<Contract>;\n }\n\n /**\n * Pure projection of one consumed event into the next state. Defaults to\n * identity; returning `null`/`undefined` also keeps the current state.\n */\n protected reduce(args: ReduceArgs<Contract>): ProcessorState<Contract> | null | undefined {\n return args.state;\n }\n\n /**\n * Synchronous side-effect hook, called by the runner once per consumed event\n * and, when necessary, once more with `event: null` for a caught-up scan\n * that consumed nothing. It is ALSO the caught-up processing: when\n * `args.delivery.caughtUp` is true (`args.state` is the whole observed fold),\n * an obligation processor\n * drives its undriven obligations and settles dead ones — scheduling that\n * async work via `args.blockProcessorWhile`, keyed by STABLE obligation keys\n * (`this.idempotencyKey(<obligation>)` with the deciding state folded into\n * the key and NO event bound, so a redelivery/revival does not rotate the\n * key and re-run the effect).\n * The runner never sets `caughtUp` below its highest observed offset — no override\n * needs its own mid-catch-up gate. Simple processors ignore the flag.\n */\n protected processEvent(_args: ProcessEventArgs<Contract>): undefined {}\n\n /** Parse a raw event against the contract: `undefined` (type not consumed),\n * a Zod error (consumed type, bad shape), or the typed consumed event.\n * Stateless — shared by {@link #reduceRawEvent} and {@link #isDeliverable}. */\n #parseConsumedEvent(\n event: StreamEvent,\n ): { ok: true; event: ConsumedEvent<Contract> } | { ok: false; error?: z.ZodError } {\n const eventDefinition = getConsumedEventDefinition({\n contract: this.contract,\n eventType: event.type,\n // `\"*\"` must not sweep in ephemeral events; naming the type is the opt-in.\n ephemeral: event.ephemeral,\n });\n if (eventDefinition === undefined) return { ok: false };\n // Rebuilding the parser from the catalog key and payload schema keeps replay\n // and live delivery on the same validation path. Cached: constructing the\n // zod wrapper per event cost ~20µs on the hot reduce path.\n const parsed = cachedEventSchema({\n type: event.type,\n payloadSchema: eventDefinition.payloadSchema,\n ephemeral: eventDefinition.ephemeral,\n }).safeParse(event);\n if (!parsed.success) return { ok: false, error: parsed.error };\n return { ok: true, event: parsed.data as ConsumedEvent<Contract> };\n }\n\n /** True when this event will reach `processEvent`: a consumed type that\n * parses. A malformed consumed event is deliberately NOT deliverable. */\n #isDeliverable(event: StreamEvent): boolean {\n return this.#parseConsumedEvent(event).ok;\n }\n\n /**\n * Reduce one raw stream event against explicit state, without touching any\n * processor-internal state. Returns `undefined` for events this processor\n * does not consume, and a {@link ConsumedEventParseFailure} for events of a\n * consumed TYPE whose shape fails the contract parse — streams accept raw\n * appends by design, so a malformed event is a fact of the log, not an\n * exception: throwing here would wedge the cursor on it forever.\n */\n #reduceRawEvent(args: {\n event: StreamEvent;\n state: ProcessorState<Contract>;\n }): ReducedEvent<Contract> | ConsumedEventParseFailure | undefined {\n const parsed = this.#parseConsumedEvent(args.event);\n if (!parsed.ok) return parsed.error === undefined ? undefined : { parseError: parsed.error };\n const event = parsed.event;\n\n const state = this.reduce({ event, state: args.state }) ?? args.state;\n assertObjectProcessorState({ processorSlug: this.contract.slug, value: state });\n\n return { event, previousState: args.state, state };\n }\n\n /**\n * Fire-and-forget async work backed by the injected keep-alive, with\n * failures logged. For work launched OUTSIDE a delivery hook (DO verbs,\n * alarm handlers); inside `processEvent`, use the `runInBackground` helper\n * from the hook args — that one rides the runner's recovery keepalive.\n */\n protected runInBackground(work: () => Promise<unknown>): void {\n awaitKeepAliveBacked(this.#keepAliveWhile, work).catch((error: unknown) => {\n console.error(\"stream processor background work failed\", error);\n });\n }\n\n /**\n * Append events listed in `contract.emits` to this processor's own stream,\n * stamped with `source.processor` provenance (no `whileProcessing`: this\n * overload is for appends outside any event batch — alarm handlers, DO methods —\n * and for decisions derived from the whole fold). Inside `processEvent`,\n * prefer the event-bound `args.append`.\n */\n protected append(...input: EmittedInput<Contract>[]): Promise<StreamEvent[]> {\n return this.#appendStamped({ target: this.stream, targetPath: this.path }, input);\n }\n\n /** Like {@link append}, onto a sibling stream (resolved via `stream.at(path)`). */\n protected appendTo(path: string, ...input: EmittedInput<Contract>[]): Promise<StreamEvent[]> {\n return this.#appendStamped(this.#appendTarget(path), input);\n }\n\n #appendTarget(path: string): { target: ProcessorStream; targetPath: string } {\n const targetPath = resolveStreamPath(this.path, path);\n return {\n // `StreamRpcTarget.at()` returns a new object even when `path` resolves\n // back to the home stream. Select by resolved address so production and\n // in-memory hosts both retain the guarded-home append semantics.\n target: targetPath === this.path ? this.stream : this.stream.at(path),\n targetPath,\n };\n }\n\n /**\n * Processor-scoped idempotency key: `<slug>/<key>`, plus `@<path>:<offset>`\n * when the append is a deterministic consequence of processing one event —\n * a resent event batch then dedupes instead of double-appending. The path\n * makes fan-in safe: two same-slug processors on different streams\n * forwarding into one target can never collide. Omit `whileProcessing` for\n * state-derived appends and fold the deciding state into `key` instead\n * (e.g. a generation counter).\n */\n protected idempotencyKey(\n key: string,\n whileProcessing?: Pick<StreamEvent, \"offset\" | \"path\">,\n ): string {\n const base = `${this.contract.slug}/${key}`;\n if (whileProcessing === undefined) return base;\n return `${base}@${whileProcessing.path}:${whileProcessing.offset}`;\n }\n\n /**\n * The provenance stamp for one append. Always overwrites any\n * caller-supplied `source.processor`: the stamp describes THIS append, and\n * ancestry stays walkable through `whileProcessing` (and `copiedFrom`\n * for subscription copies, which preserve the original stamp).\n */\n #processorStamp(streamId: string, whileProcessing?: Pick<StreamEvent, \"offset\" | \"type\">) {\n return {\n slug: this.contract.slug,\n version: this.contract.version,\n stream: { path: this.path, projectId: this.projectId, streamId },\n ...(whileProcessing === undefined\n ? {}\n : { whileProcessing: { offset: whileProcessing.offset, type: whileProcessing.type } }),\n };\n }\n\n #appendStamped(\n args: {\n target: ProcessorStream;\n targetPath: string;\n sourceStreamId?: string;\n whileProcessing?: Pick<StreamEvent, \"offset\" | \"type\">;\n },\n input: EmittedInput<Contract>[],\n ): Promise<StreamEvent[]> {\n // Validate emitted types synchronously, preserving the author-facing\n // method's immediate failure behavior even when an out-of-batch append\n // must first read the current stream ID.\n const builtEvents = input.map((event) => this.#buildEmittedEvent(event) as StreamEventInput);\n return this.#appendBuiltEvents(args, builtEvents);\n }\n\n async #appendBuiltEvents(\n args: {\n target: ProcessorStream;\n targetPath: string;\n sourceStreamId?: string;\n whileProcessing?: Pick<StreamEvent, \"offset\" | \"type\">;\n },\n builtEvents: StreamEventInput[],\n ): Promise<StreamEvent[]> {\n // Batch-bound appends receive the exact ID from the delivery envelope.\n // Alarm/DO-method appends bind themselves by reading the home stream now.\n const sourceStreamId =\n args.sourceStreamId ??\n (\n await this.stream.getEventPage({\n afterOffset: Number.MAX_SAFE_INTEGER,\n limit: 1,\n })\n ).streamId;\n const processor = this.#processorStamp(sourceStreamId, args.whileProcessing);\n let events = builtEvents.map((built) => ({\n ...built,\n source: { ...built.source, processor },\n }));\n // Home-stream appends of a CONSUMED type feed the consume-own-append\n // loop: those committed offsets come back through this processor's own\n // subscription, and noteBatchIngested closes the sample. Sibling-stream\n // appends (appendTo) never loop back here, so they are not timed at all.\n // A sibling retains rows across deletion/recreation of the source path, so\n // its committed key includes the source lifetime; offset 1 from A and offset 1 from B are\n // different causes and must both be able to land. Idempotency keys are\n // retry identities, not semantic entity identities: readers determine\n // meaning from event types or reduced processor state, never key spelling.\n if (args.targetPath !== this.path) {\n events = events.map((event) =>\n event.idempotencyKey === undefined\n ? event\n : {\n ...event,\n idempotencyKey: `${event.idempotencyKey}@source-stream:${sourceStreamId}`,\n },\n );\n return args.target.append(...events);\n }\n const t0 = Date.now();\n return this.stream.appendIfStreamId({ streamId: sourceStreamId, events }).then((committed) => {\n if (committed.length === 0) return committed;\n // ONLY an event this processor itself consumes can close the loop, and\n // most processors emit far more than they consume: the voice facet\n // appends ~50 speaker frames a second and consumes none of them. Timing\n // those made every sample the wait until the next thing the processor\n // DID consume — a person's pause between sentences, published as\n // \"seconds to see my own append\". An append with nothing consumable in\n // it is timed for its round trip and nothing else.\n let maxCommittedOffset: number | null = null;\n for (const event of committed) {\n if (!this.#isDeliverable(event)) continue;\n maxCommittedOffset = Math.max(maxCommittedOffset ?? 0, event.offset);\n }\n this.eventConsumptionMetrics.noteAppendCommitted({\n maxCommittedOffset,\n t0,\n atMs: Date.now(),\n });\n return committed;\n });\n }\n}\n","// The stream-processor runner owns everything a processor author should not —\n// cursors, checkpoint cadence, retry, and recovery —\n// so the processor itself can stay pure-ish hooks (reduce / processEvent —\n// fold-derived side effects ride processEvent under `delivery.caughtUp`, not\n// a separate hook). Design:\n// docs/stream-processor-runner-redesign.md; the invariants are pinned by the\n// in-memory harness in stream-processor-runner.test.ts (the executable spec).\n//\n// The shape inversion this file exists for: the legacy host was the star\n// (`createStreamProcessorHost(ctx)` + `host.add(factory)`, hand-fed a Durable\n// Object ctx). Here the PROCESSOR is passed INTO the runner, and the runner is\n// a plain runtime-neutral object — the same class runs in a browser tab over\n// SQLite, in a Durable Object over KV, and in the in-memory test harness that\n// serves as the semantic spec. Nothing Cloudflare-shaped enters the core:\n// anything durable arrives through the optional `durability` adapter, and\n// anything incarnation-shaped through the optional `keepAlive` hook.\n//\n// Event batches: `openEventBatchCallback()` returns the committed processing\n// offset plus the `processEventBatch` callback a direct source can retain and call.\n// `openHostedEventBatchCallback()` is the trusted hosted-source variant: it\n// accepts the source's own lifetime identity and defers any journal refold until\n// the one-way callback, breaking a source-alarm -> facet-wake -> source-read cycle.\n// Hosted wake wraps its promise with an independent one-way settlement\n// capability; a browser's local event database calls the same runtime-neutral\n// API directly.\n// The runner reduces/processes ONE EVENT AT A TIME, so batch division is\n// invisible to processor semantics (the harness pins this: one batch,\n// singletons, or random partitions of the same journal must produce identical\n// outcomes). `blockProcessorWhile` is therefore a strict per-event barrier,\n// never a per-batch one — and registrations within one event run in strict\n// FIFO order (each blocker starts after the previous settles), so authors\n// order fold-derived work after per-event work simply by registering it\n// later.\n//\n// The load-bearing orderings in here are transplanted from the legacy\n// `StreamProcessor.#ingest` (deleted with the host) — the most\n// incident-scarred loop in the system — and each is marked at its new home:\n// - failed batch settles already-started blockers, cursor untouched\n// - persist BEFORE advancing the in-memory cursor\n// - malformed consumed events advance the cursor, diagnostics append AFTER\n// the commit, in the background\n//\n// This runner owns processor reduction, processing, progress, and callback batching.\n\nimport type { z } from \"zod\";\nimport type { ProcessorStream } from \"./stream-handle.ts\";\nimport type { ProcessorState } from \"./processor-contracts.ts\";\nimport type { StreamEvent } from \"./schemas.ts\";\nimport { MAX_STREAM_EVENT_READ_BYTE_LIMIT, type StreamEventBatch } from \"./rpc-types.ts\";\nimport {\n awaitKeepAliveBacked,\n StreamProcessor,\n type MaybePromise,\n type StreamProcessorContract,\n type StreamProcessorRunnerHooks,\n} from \"./stream-processor.ts\";\n\n/**\n * The reduction half of a processor's durable progress: a disposable CACHE of\n * the fold (the journal is the authority). `reducerVersion` is the cache key —\n * a deploy that changes it invalidates the cache and triggers an automatic\n * reduce-only refold at load, which re-runs `reduce` ONLY. That is the whole point of splitting\n * this from {@link ProcessingProgress}: today's single `{offset, state}` cursor\n * makes a routine state-schema deploy refold history AND re-run `processEvent`\n * across it, re-driving real vendor calls.\n */\nexport type ReductionProgress<State> = {\n /** Cache key for the fold; a mismatch discards `state` and refolds. */\n reducerVersion: string;\n /** The highest offset folded into `state`. */\n reducedThroughOffset: number;\n /** The fold through `reducedThroughOffset`, under `reducerVersion`. */\n state: State;\n};\n\n/**\n * The processing half of a processor's durable progress: the AUTHORITATIVE\n * effect-acknowledgement cursor. Unlike the reduction cache it is never\n * discarded — rewinding it re-runs side effects. `cursorRevision` is the CAS\n * fence for exactly those rewinds: every commit asserts it, and a bump makes\n * every in-flight continuation of the old cursor position stale.\n */\nexport type ProcessingProgress = {\n /** Every effect at or below this offset is acknowledged (durably settled). */\n acknowledgedThroughOffset: number;\n /** Monotonic fencing token; a bump is the only sanctioned way to move\n * `acknowledgedThroughOffset` backward. */\n cursorRevision: number;\n};\n\n/**\n * A processor's two durable positions, persisted as one record. Invariant\n * (when persisted): `reduction.reducedThroughOffset <=\n * processing.acknowledgedThroughOffset` — the fold cache may lag the effect\n * cursor (it is rebuildable), but a fold AHEAD of acknowledged effects would\n * let `snapshot()` show state derived from events whose effects a cursor\n * rewind is about to re-run. Core (Phase 2) is the graceful degradation:\n * reduction only, no processing cursor — same structure, same reduce-only refold.\n */\nexport type ProcessorProgress<State> = {\n /** Random identity of the stream lifetime whose offsets and fold this record describes. */\n streamId: string;\n reduction: ReductionProgress<State>;\n processing: ProcessingProgress;\n};\n\n/**\n * Durable progress store, CAS-fenced by `cursorRevision`. The runner reads\n * once at open, then commits once per delivered batch; `commit` rejects\n * (throws) if `expectedCursorRevision` no longer matches the persisted\n * revision — the fence that stops a stale incarnation (or a continuation\n * outliving a cursor rewind) from clobbering the rewound cursor.\n * An absent record reads as revision 0. Backends use DO KV or an in-memory\n * store in tests. Related projections share the same commit boundary.\n */\nexport type ProcessorProgressStore<State> = {\n read(): MaybePromise<ProcessorProgress<State> | undefined>;\n commit(\n progress: ProcessorProgress<State>,\n opts: { expectedCursorRevision: number; expectedStreamId: string | undefined },\n ): MaybePromise<void>;\n /**\n * Atomically replace progress after the stream at this path is recreated.\n * Backends with related durable projections must reset those in the same\n * transaction; omitting this method makes recreation fail closed.\n */\n replaceForStream?(\n progress: ProcessorProgress<State>,\n opts: { expectedCursorRevision: number; expectedStreamId: string },\n ): MaybePromise<void>;\n};\n\n/**\n * Optional recovery capability. Present only for durable processors that own\n * background obligations (`runInBackground` work whose OUTCOME matters).\n *\n * - `keepAliveWhile` schedules a durable alarm ahead of in-flight work, so an\n * incarnation that dies owing work is revived by the alarm's fire. The\n * production adapter is `(work) => keepalive.track(work())` over ONE\n * ProcessorKeepalive (stream-processor-keepalive.ts) — the runner REUSES\n * that machinery wholesale, it never reinvents mark/backoff/quiet-clean.\n * - The adapter's private revival pass appends the core\n * `stream/processor-revived` fact (the payload's `processorSlug` names the\n * revived processor), guaranteeing at least one delivery turn even at zero\n * lag. Consuming the fact is OPTIONAL: an unconsumed head-reaching frame\n * still gets the runner's eventless\n * `processEvent({ event: null, delivery: { caughtUp: true } })` pass.\n * - `handleAlarm` services the durable timer (`ProcessorKeepalive.onAlarm`);\n * the host DO multiplexes its single alarm across runners and routes fires\n * to {@link StreamProcessorRunner.handleAlarm}, which delegates here.\n */\nexport type ProcessorRecovery = {\n keepAliveWhile(work: () => Promise<unknown>): void;\n handleAlarm(info?: unknown): MaybePromise<void>;\n /**\n * Operator seam: clear the keepalive's crash-loop budget and pull an owed\n * retry in to the confirmation lead — the no-deploy antidote for a\n * 3-strikes revival plateau. Optional: in-memory/test recoveries without a\n * durable budget have nothing to reset.\n */\n resetBackoff?(): void;\n};\n\n/**\n * The ONE optional durability adapter a hosting runtime hands the runner:\n * `progress` is required whenever the processor is durable at all (without the\n * adapter the runner keeps progress in memory — tests, ephemeral\n * views); `recovery` is orthogonal and present only when the processor owns\n * background work that must survive eviction. This is deliberately where\n * every runtime-specific concern lives — no Cloudflare `ctx` in the runner.\n */\ntype ProcessorDurability<State> = {\n progress: ProcessorProgressStore<State>;\n recovery?: ProcessorRecovery;\n};\n\n/** Honest delivery information handed to `processEvent`. */\nexport type DeliveryContext = {\n /** Random identity of the stream lifetime that delivered this turn. */\n streamId: string;\n /**\n * The at-head signal: the scan has reached the highest raw stream offset\n * the runner has observed, so `state` is the complete reduction of\n * everything it has seen. It is true on the last consumed event of a\n * head-reaching frame. If that frame contains no consumed event, the runner\n * makes one eventless `processEvent` call (`event: null`) with this flag\n * instead; an unconsumed tail must not strand obligations on an otherwise\n * quiet stream.\n */\n caughtUp: boolean;\n};\n\n/**\n * One transport scan as delivered to the runner. The scan coordinates are\n * first-class rather than inferred from `events`: a delivery may deliberately\n * omit ephemeral or selector-filtered rows, including an entirely empty\n * interval, while still proving that every raw offset in the interval was\n * examined. Advancing through that proof is what prevents filtered rows from\n * leaving a processor cursor below the scanned-through offset.\n */\nexport type StreamProcessorEventBatch = Pick<\n StreamEventBatch,\n \"events\" | \"scannedAfterOffset\" | \"scannedThroughOffset\" | \"streamId\" | \"streamMaxOffset\"\n>;\n\n/** A consumed-type event whose payload failed the contract parse, awaiting its post-commit diagnostic. */\ntype PendingParseFailure = { event: StreamEvent; error: z.ZodError };\n\n/** A pending `waitUntilEvent` waiter (see the method doc for semantics). */\ntype EventWaiterBase = {\n reject: (error: unknown) => void;\n resolve: () => void;\n timer?: ReturnType<typeof setTimeout>;\n signal?: AbortSignal;\n abortListener?: () => void;\n};\n\ntype EventWaiter =\n | (EventWaiterBase & { kind: \"predicate\"; predicate: (event: StreamEvent) => boolean })\n | (EventWaiterBase & { kind: \"offset\"; offset: number });\n\n/** The in-flight fold/cursor context of one batch, committed at batch end. */\ntype BatchContext<State> = {\n /** The revision every commit in this batch asserts (fixed at batch start). */\n revision: number;\n /** When processing this batch began — feeds the per-commit consumption metrics\n * (the legacy `#ingest` timed the whole batch the same way). */\n ingestStartedAtMs: number;\n state: State;\n reducedThroughOffset: number;\n completedThroughOffset: number;\n eventsSinceCommit: number;\n /** New events delivered since the last commit — waiters resolve when their commit lands. */\n uncommittedEvents: StreamEvent[];\n /** Parse failures since the last commit — diagnostics append only AFTER their commit lands. */\n uncommittedParseFailures: PendingParseFailure[];\n};\n\n/**\n * Processes event batches for one processor on one stream. Runtime-neutral:\n * the browser, the Durable Object registry, and the in-memory\n * test harness all instantiate exactly this class and differ only in the\n * `durability` / `keepAlive` adapters they pass. One runner per processor —\n * the \"host\" of old survives only as a thin registry that builds adapters and\n * routes wakes/alarms to the right runner.\n *\n * Serialization: batches and self-pulls share ONE in-memory chain, so a\n * catch-up never interleaves with a half-processed batch. Cross-incarnation\n * races (a stale runner outliving progress made elsewhere) are fenced durably\n * instead, by the progress store's `cursorRevision` CAS + monotonic fence.\n */\nexport class StreamProcessorRunner<\n Contract extends StreamProcessorContract,\n Deps extends object = object,\n> {\n private readonly processor: StreamProcessor<Contract, Deps>;\n private readonly hooks: StreamProcessorRunnerHooks<Contract>;\n private readonly stream: ProcessorStream;\n private readonly durability: ProcessorDurability<ProcessorState<Contract>> | undefined;\n private readonly keepAlive: ((work: () => Promise<unknown>) => void) | undefined;\n private readonly now: () => number;\n private readonly readPageSize: number;\n\n /** Memoized load for one stream lifetime; cleared on failure or recreation. */\n #loaded: Promise<void> | undefined;\n #loadingStreamId: string | undefined;\n /** True once progress reflects a real load (fresh default over an empty\n * store counts; a pending/failed load does not) — the gate that keeps\n * default or partially-refolded state from ever escaping (the legacy\n * `isLoaded` invariant). `snapshot()` additionally\n * awaits the load, so partial state cannot escape through it either. */\n #hasLoaded = false;\n /** The COMMITTED, loaded progress — what snapshots and direct callbacks publish.\n * A hosted wake may publish only the separately-read processing cursor before this\n * reduction cache loads. Batch folds accumulate in locals and land here only after\n * the durable commit. */\n #progress: ProcessorProgress<ProcessorState<Contract>> | undefined;\n /** Highest stream offset observed across all batches this incarnation. */\n #highestObservedOffset = 0;\n /** Serializes batches + self-pulls; failures are contained per entry. */\n #chain: Promise<void> = Promise.resolve();\n #disposed = false;\n readonly #eventWaiters = new Set<EventWaiter>();\n readonly #stateChangeObservers = new Set<\n (snapshot: { offset: number; state: ProcessorState<Contract> }) => void\n >();\n /** Memoized schema default, for pre-load `currentState` reads. */\n #defaultState: ProcessorState<Contract> | undefined;\n\n constructor(args: {\n /** The processor to run — passed in; the runner never constructs one. */\n processor: StreamProcessor<Contract, Deps>;\n /** The processor's home stream (replay reads, revival appends). */\n stream: ProcessorStream;\n /** Durable progress + optional recovery; omit for in-memory (tests, ephemeral views). */\n durability?: ProcessorDurability<ProcessorState<Contract>>;\n /** Keeps in-flight work alive with the hosting DO's `waitUntil`. */\n keepAlive?: (work: () => Promise<unknown>) => void;\n /** Injected clock for the test harness; production uses Date.now. */\n now?: () => number;\n /** Journal read page size (refold/catch-up paging); tests shrink it. */\n readPageSize?: number;\n }) {\n this.processor = args.processor;\n this.hooks = StreamProcessor.runnerHooks(args.processor);\n this.stream = args.stream;\n this.durability = args.durability;\n this.keepAlive = args.keepAlive;\n this.now = args.now ?? (() => Date.now());\n this.readPageSize = args.readPageSize ?? 500;\n }\n\n /**\n * Opens the processor's event-batch callback and returns its committed\n * processing offset. A hosted processor wake returns this pair to a source\n * stream; the browser database writer calls the same method directly.\n *\n * `checkpointOffset` is the PROCESSING cursor (`acknowledgedThroughOffset`),\n * never the reduction offset: the caller resumes after this value, and\n * resuming from a reduction-pinned snapshot\n * offset could skip events whose effects were never acknowledged.\n *\n * `processEventBatch` is the only place transport batching enters the\n * runner; inside it the runner reduces and processes one event at a time. A\n * hosting transport may adapt how the promise is observed, but must not\n * duplicate these semantics.\n */\n async openEventBatchCallback(expectedStreamId?: string): Promise<{\n checkpointOffset: number;\n processEventBatch: (batch: StreamProcessorEventBatch) => Promise<void>;\n }> {\n this.#assertNotDisposed();\n const opened = await this.#enqueue(async () => {\n const streamId = await this.#readCurrentStreamId(expectedStreamId);\n await this.#load(streamId);\n return {\n streamId,\n checkpointOffset: this.#requireProgress().processing.acknowledgedThroughOffset,\n };\n });\n return this.#eventBatchCallback({\n ...opened,\n deferredLoad: false,\n sourceScansAllEvents: false,\n });\n }\n\n /**\n * Open the callback used by a trusted hosted source Stream.\n *\n * The request already carries that source's authoritative stream ID. Reading\n * it back before returning would deadlock a colocated Processor Facet: the\n * source alarm owns the wake RPC while the facet's identity/refold read waits\n * for that same source turn. Return the durable processing cursor without a\n * source read, then finish any reduction-cache load when the source invokes\n * the independent one-way batch callback.\n */\n async openHostedEventBatchCallback(streamId: string): Promise<{\n checkpointOffset: number;\n processEventBatch: (batch: StreamProcessorEventBatch) => Promise<void>;\n }> {\n this.#assertNotDisposed();\n const checkpointOffset = await this.#enqueue(() => this.#prepareHostedCheckpoint(streamId));\n return this.#eventBatchCallback({\n streamId,\n checkpointOffset,\n deferredLoad: true,\n sourceScansAllEvents: true,\n });\n }\n\n #eventBatchCallback(args: {\n streamId: string;\n checkpointOffset: number;\n deferredLoad: boolean;\n sourceScansAllEvents: boolean;\n }): {\n checkpointOffset: number;\n processEventBatch: (batch: StreamProcessorEventBatch) => Promise<void>;\n } {\n return {\n checkpointOffset: args.checkpointOffset,\n processEventBatch: (batch: StreamProcessorEventBatch) => {\n const attempt = this.#enqueue(async () => {\n if (args.deferredLoad) await this.#loadPreparedStream(args.streamId);\n await this.#processBatch(batch);\n });\n // Zero-lag recovery must cover the WHOLE batch attempt, not merely\n // the work registered inside it (the June-10/July-7 incident class):\n // an eviction mid-batch on a stream that also died still gets this\n // processor revived by the keepalive alarm scheduled ahead of `attempt`.\n // A failed attempt reads as failure to the keepalive (routing the\n // next fire to revival); the transport observes the same rejection\n // through the returned promise and owns the redelivery.\n this.durability?.recovery?.keepAliveWhile(() => attempt);\n // Direct callback batches can contain only consumed event types while\n // carrying a later raw stream maximum. Without another reader, an\n // unconsumed tail would keep `delivery.caughtUp` false and strand any\n // obligation the batch opened. The runner therefore pulls the raw\n // journal after a successful direct batch. The pull is serialized with\n // delivery, kept alive independently from the transport promise, and\n // retried by durable recovery if it fails.\n //\n // Hosted stream delivery is different: its source already scans every\n // raw offset and sends empty frames across configured-filter gaps.\n // That transport must remain the only catch-up driver or a runner pull\n // would consume events the subscription explicitly excluded.\n if (!args.sourceScansAllEvents) {\n this.#runInBackground(() =>\n attempt.then(\n () =>\n this.#enqueue(async () => {\n const { processing } = this.#requireProgress();\n if (processing.acknowledgedThroughOffset < batch.streamMaxOffset) {\n await this.#selfCatchUp();\n }\n }),\n () => undefined,\n ),\n );\n }\n return attempt;\n },\n };\n }\n\n /** Handle a durable recovery alarm routed here by the hosting registry. */\n async handleAlarm(info?: unknown): Promise<void> {\n const recovery = this.durability?.recovery;\n if (recovery === undefined) return;\n await recovery.handleAlarm(info);\n }\n\n /** One consistent read of the fold, pinned to `reducedThroughOffset`. */\n async snapshot(): Promise<{ offset: number; state: ProcessorState<Contract> }> {\n return this.#enqueue(async () => {\n const streamId = await this.#readCurrentStreamId();\n await this.#load(streamId);\n const progress = this.#requireProgress();\n return {\n offset: progress.reduction.reducedThroughOffset,\n state: progress.reduction.state,\n };\n });\n }\n\n /**\n * Whether published state IS a real fold rather than the schema default —\n * the legacy `isLoaded` gate. With the runner, the load itself performs any\n * pending refold, so this is true whenever a load has completed and false\n * only before the first successful load.\n */\n get isLoaded(): boolean {\n return this.#hasLoaded;\n }\n\n /** Highest offset whose processing and blocking consequences have committed. */\n get currentAcknowledgedThroughOffset(): number {\n return this.#progress?.processing.acknowledgedThroughOffset ?? 0;\n }\n\n /** Source lifetime paired atomically with the current committed state and cursor. */\n get currentStreamId(): string | undefined {\n return this.#progress?.streamId;\n }\n\n /**\n * The current committed fold, synchronously (the schema default until the\n * first load) — the legacy `StreamProcessor.currentState`,\n * kept so a hosting registry can assemble its\n * live state without an async hop. Gate on {@link isLoaded} first: a cold\n * runner reports the default, and publishing that anywhere live would wipe\n * real facts for state observers.\n */\n get currentState(): ProcessorState<Contract> {\n if (this.#progress !== undefined) return this.#progress.reduction.state;\n this.#defaultState ??= this.hooks.initialState();\n return this.#defaultState;\n }\n\n /**\n * Observe committed reduced-state changes IN-PROCESS: the observer is a\n * local function (the hosting registry wires it to reassemble its\n * live-state engine), never a retained RPC stub. It fires after a batch\n * commit lands durably AND the committed state changed identity — the\n * runner's home for the legacy `StreamProcessor.observeStateChanges` +\n * post-persist notify. Returns a function that stops observing.\n */\n observeStateChanges(\n observer: (snapshot: { offset: number; state: ProcessorState<Contract> }) => void,\n ): () => void {\n this.#stateChangeObservers.add(observer);\n return () => void this.#stateChangeObservers.delete(observer);\n }\n\n /**\n * Read journal pages after the acknowledged cursor and process them until\n * caught up — the public method for read-your-writes and a\n * hosting registry's cold-load healing (the legacy host's `catchUpInternal`\n * shape). One page of lookahead, so every non-final batch carries a\n * `streamMaxOffset` past its own last event and only the genuinely final page is\n * marked caught up. Serialized with delivered batches on the runner's chain; failures\n * RETHROW — the caller owns any swallow-and-log policy.\n */\n catchUp(): Promise<void> {\n return this.#enqueue(async () => {\n const streamId = await this.#readCurrentStreamId();\n await this.#load(streamId);\n await this.#selfCatchUp();\n });\n }\n\n /**\n * Resolve once the ACKNOWLEDGED cursor reaches `offset` — the single\n * wait-for-progress door (read-your-writes: append, then wait on the offset\n * the append returned). The offset form never depends on stream delivery to\n * reach an event that ALREADY EXISTS on the stream: when the cursor is\n * behind, it starts a chain-serialized journal read ({@link catchUp}); the\n * waiting promise covers only a genuinely\n * FUTURE offset the pull cannot reach yet. The predicate form observes\n * FUTURE deliveries only — an event not yet appended (e.g. runScript's\n * completion, appended later by `runInBackground` work; that work runs OFF\n * the runner chain and outside the awaiting handler, so the halted waiter\n * never gates the append or the delivery that resolves it) — and resolves\n * after the batch that delivered the matching event has durably committed,\n * so state already reflects it.\n */\n waitUntilEvent(args: {\n predicate: (event: StreamEvent) => boolean;\n timeoutMs?: number;\n signal?: AbortSignal;\n }): Promise<void>;\n waitUntilEvent(args: { offset: number; timeoutMs?: number; signal?: AbortSignal }): Promise<void>;\n async waitUntilEvent(\n args:\n | { predicate: (event: StreamEvent) => boolean; timeoutMs?: number; signal?: AbortSignal }\n | { offset: number; timeoutMs?: number; signal?: AbortSignal },\n ): Promise<void> {\n if (args.signal?.aborted === true) throw abortReason(args.signal);\n if (\"offset\" in args) {\n if (!Number.isSafeInteger(args.offset) || args.offset < 0) {\n throw new Error(\"waitUntilEvent offset must be a non-negative safe integer\");\n }\n const streamId = await this.#readCurrentStreamId();\n await this.#load(streamId);\n if (this.#requireProgress().processing.acknowledgedThroughOffset >= args.offset) return;\n const { offset, signal, timeoutMs } = args;\n // No await between the check above and registering the waiter below\n // (the helper below registers synchronously), so a batch cannot\n // advance the cursor past `offset` in the gap and be missed.\n const reached = this.#registerEventWaiter({ kind: \"offset\", offset }, { signal, timeoutMs });\n // Self-pull, not wait-and-hope: this form's contract is read-your-writes\n // over an append that already committed. A waiting caller must not depend\n // only on an open callback that may stop responding or on a wake call\n // that may have been lost (the orphaned-announcement incident). The\n // catch-up runs on the runner chain, serialized with delivered\n // batches — no concurrent processing against a live batch, because\n // redelivered offsets dedupe against the acknowledged cursor — and\n // resolves the waiter through the ordinary frame commit. A genuinely\n // future offset stays parked for delivery after a successful pull. A\n // failed pull is authoritative, however: settle this wait immediately\n // so the caller can apply its bounded availability retry instead of\n // hiding the failure behind the full wait timeout.\n void this.catchUp().catch((error: unknown) => {\n reached.reject(error);\n });\n return await reached.promise;\n }\n const { predicate, signal, timeoutMs } = args;\n await this.#registerEventWaiter({ kind: \"predicate\", predicate }, { signal, timeoutMs })\n .promise;\n }\n\n /** Release processor resources. Idempotent; a disposed runner rejects new work. */\n dispose(): void {\n this.#disposed = true;\n for (const waiter of this.#eventWaiters) {\n this.#settleEventWaiter(waiter, { error: new Error(\"StreamProcessorRunner disposed\") });\n }\n this.#stateChangeObservers.clear();\n }\n\n // ---------------------------------------------------------------------------\n // The per-event loop.\n // ---------------------------------------------------------------------------\n\n async #processBatch(batch: StreamProcessorEventBatch): Promise<void> {\n const ingestStartedAtMs = this.now();\n assertProcessorEventBatch(batch);\n const committed = this.#requireProgress();\n if (batch.streamId !== committed.streamId) {\n throw new Error(\n `stream processor \"${this.hooks.contract.slug}\" received batch for stream ID ` +\n `${batch.streamId}; current progress belongs to ${committed.streamId}`,\n );\n }\n const committedThroughOffset = committed.processing.acknowledgedThroughOffset;\n if (batch.scannedAfterOffset > committedThroughOffset) {\n throw new Error(\n `delivery batch starts after the committed scan cursor: ${batch.scannedAfterOffset} > ${committedThroughOffset}`,\n );\n }\n const batchScannedThroughOffset = Math.max(committedThroughOffset, batch.scannedThroughOffset);\n\n // Offset-dedupe against the acknowledged cursor (and within the batch):\n // redelivered events are silent skips, exactly like legacy ingest.\n const pending: StreamEvent[] = [];\n let scan = committedThroughOffset;\n for (const event of batch.events) {\n if (event.offset <= scan) continue;\n scan = event.offset;\n pending.push(event);\n }\n\n // Highest observed offset = max(streamMaxOffset, last scanned offset), monotonic across\n // batches: \"the highest offset the runner has OBSERVED\" never regresses on\n // a stale redelivery, so an older batch can still see that more rows exist.\n this.#highestObservedOffset = Math.max(\n this.#highestObservedOffset,\n batch.streamMaxOffset,\n batchScannedThroughOffset,\n );\n if (pending.length === 0 && batchScannedThroughOffset === committedThroughOffset) return;\n const highestObservedOffset = this.#highestObservedOffset;\n\n // `caughtUp` is a batch property, not a per-event-offset one. If this batch\n // scans through the highest offset observed so far, then by the end of it\n // the processor has seen EVERYTHING the stream has reported, so its LAST consumed event gets\n // `caughtUp: true` even though that event's own offset may sit far below\n // the maximum (a batch of 100 where only the first is consumed still leaves\n // the processor caught up).\n const batchCaughtUp = batchScannedThroughOffset >= highestObservedOffset;\n // The offset of the LAST event this batch will actually deliver to\n // `processEvent` — a consumed type that PARSES. `isDeliverable` folds in\n // the wildcard (`\"*\"` consumes every type) AND excludes malformed consumed\n // events: without the parse check a malformed final event would be\n // selected as \"last consumed\", steal the `caughtUp` flag from the real\n // last-good event (which never gets it), and strand its obligation.\n let lastDeliveredOffset: number | null = null;\n for (const event of pending) {\n if (this.hooks.isDeliverable(event)) lastDeliveredOffset = event.offset;\n }\n // Whether a CONSUMED event carried `caughtUp` this batch. If the scan\n // reached the highest observed offset but none of its events did (all remaining events are\n // unconsumed — a self-pull that folded only foreign events, or a filtered\n // wake batch whose final durable row is an unconsumed presence fact), the runner\n // still owes the processor one caught-up call: it calls `processEvent` with\n // `event: null` after the loop. Without it pending work strands\n // whenever an unconsumed event (e.g. stream/connection-closed) sits\n // at the latest offset — the late-agent preview regression.\n let firedCaughtUp = false;\n\n const ctx: BatchContext<ProcessorState<Contract>> = {\n revision: committed.processing.cursorRevision,\n ingestStartedAtMs,\n state: committed.reduction.state,\n reducedThroughOffset: committed.reduction.reducedThroughOffset,\n completedThroughOffset: committed.processing.acknowledgedThroughOffset,\n eventsSinceCommit: 0,\n uncommittedEvents: [],\n uncommittedParseFailures: [],\n };\n /** Every blocker started anywhere in this batch, for failure settlement. */\n const startedBlockers: Promise<unknown>[] = [];\n\n try {\n for (const event of pending) {\n const reduction = this.hooks.reduceRawEvent({ event, state: ctx.state });\n if (reduction !== undefined && \"parseError\" in reduction) {\n // A malformed consumed event is a fact of the log, not an\n // exception: collect it, keep advancing (the cursor must never\n // wedge on it), and record it AFTER its commit lands (below).\n ctx.uncommittedParseFailures.push({ event, error: reduction.parseError });\n } else if (reduction !== undefined) {\n // `caughtUp` on the LAST delivered event of a batch that scanned through\n // the highest observed offset (not a comparison of this event alone — that\n // fails when a later unconsumed event is the batch's final row).\n const caughtUp = batchCaughtUp && event.offset === lastDeliveredOffset;\n if (caughtUp) firedCaughtUp = true;\n const delivery: DeliveryContext = {\n caughtUp,\n streamId: batch.streamId,\n };\n // FIFO blocker chain: each registration starts only after the\n // previous one settles, so a later registration in the same\n // `processEvent` body observes the earlier registrations' appends.\n // That ordering is load-bearing: e.g. an interrupt's cancel append\n // (registered in the per-event switch) must precede a fold-derived\n // re-fire registered after it, or the re-fire wins the fold and the\n // cancel no-ops. Registration order replaces the deleted deferred\n // `blockProcessorWhileCaughtUp` mechanism.\n let eventChain: Promise<unknown> = Promise.resolve();\n const whileProcessing = reduction.event;\n this.hooks.processEvent({\n event: reduction.event,\n previousState: reduction.previousState,\n state: reduction.state,\n delivery,\n blockProcessorWhile: (work) => {\n const attempt = eventChain.then(() =>\n this.#keepAliveBackedWork(work).catch((error: unknown) => {\n console.error(\n `stream processor blocked work failed (${this.hooks.contract.slug})`,\n error,\n );\n throw error;\n }),\n );\n eventChain = attempt;\n startedBlockers.push(attempt);\n },\n runInBackground: (work) => this.#runInBackground(work),\n append: (...input) =>\n this.hooks.append({ streamId: batch.streamId, whileProcessing }, input),\n appendTo: (path, ...input) =>\n this.hooks.appendTo(path, { streamId: batch.streamId, whileProcessing }, input),\n });\n // STRICT PER-EVENT ORDERING: THIS event's blocking work completes\n // before the next event's processEvent starts. Background work was\n // registered (keepalive-backed) and deliberately NOT awaited — it\n // may overtake later events.\n await eventChain;\n ctx.state = reduction.state;\n }\n // Non-consumed and malformed events advance both cursors too —\n // matching what a filtered delivery's cursor does today.\n ctx.reducedThroughOffset = event.offset;\n ctx.completedThroughOffset = event.offset;\n ctx.eventsSinceCommit += 1;\n ctx.uncommittedEvents.push(event);\n }\n // Eventless caught-up call. Normally `delivery.caughtUp` rides the last\n // consumed event in the final batch. But a batch can scan through the\n // highest observed offset with NO consumed event carrying that flag — a\n // SQLite read that folded only unconsumed remaining events, or a filtered wake batch\n // whose final durable row is an unconsumed presence fact (e.g.\n // stream/connection-closed). Deferring the reconcile to \"the next\n // consumed event\" strands the obligation when the stream then goes quiet\n // (the late-agent preview regression). So the\n // runner calls the processor over the final fold: `event` is null\n // (the processor skips its per-event switch), appends are unstamped, and\n // obligation keys are offset-free (`this.idempotencyKey`), stable across\n // passes. Its blockers are awaited before the deferred batch-end commit.\n if (batchCaughtUp && !firedCaughtUp) {\n const delivery: DeliveryContext = {\n caughtUp: true,\n streamId: batch.streamId,\n };\n // Same FIFO blocker chain as the per-event dispatch; its work is\n // awaited before the deferred batch-end commit.\n let caughtUpChain: Promise<unknown> = Promise.resolve();\n this.hooks.processEvent({\n event: null,\n previousState: ctx.state,\n state: ctx.state,\n delivery,\n blockProcessorWhile: (work) => {\n const attempt = caughtUpChain.then(() =>\n this.#keepAliveBackedWork(work).catch((error: unknown) => {\n console.error(\n `stream processor blocked work failed (${this.hooks.contract.slug})`,\n error,\n );\n throw error;\n }),\n );\n caughtUpChain = attempt;\n startedBlockers.push(attempt);\n },\n runInBackground: (work) => this.#runInBackground(work),\n append: (...input) => this.hooks.append({ streamId: batch.streamId }, input),\n appendTo: (path, ...input) =>\n this.hooks.appendTo(path, { streamId: batch.streamId }, input),\n });\n await caughtUpChain;\n }\n // The batch's scan proof covers omitted rows too. They are deliberate\n // no-ops for this processor, but both cursors must advance across them\n // atomically with the durable events above — including an empty scan.\n ctx.reducedThroughOffset = batchScannedThroughOffset;\n ctx.completedThroughOffset = batchScannedThroughOffset;\n } catch (error) {\n // A failed batch must still settle work it already registered so\n // nothing rejects unobserved. Whatever was not yet durably committed is\n // not committed now — the batch stays retryable and the transport\n // replays it from the last acknowledged cursor.\n // (The legacy #ingest's failure settlement, verbatim.)\n await Promise.allSettled(startedBlockers);\n throw error;\n }\n\n // FIXED CADENCE: one durable commit per delivered batch, after EVERY\n // event's blocking work — including the caught-up call — has settled\n // (the legacy batch checkpoint window exactly). The gap between the\n // in-memory cursor and the last persisted acknowledgement is the\n // deliberate at-least-once replay window (appends stay\n // idempotency-keyed). Committing only at batch end is also what keeps a\n // failed caught-up call retryable: a mid-batch commit of the final\n // event would strand that call with the cursor already at the maximum offset\n // and redelivery empty.\n if (ctx.eventsSinceCommit > 0 || batchScannedThroughOffset > committedThroughOffset) {\n await this.#commitBatchContext(ctx);\n }\n }\n\n /**\n * Persist the batch context, THEN advance the published cursor, resolve\n * waiters, and flush parse-failure diagnostics for the covered events.\n *\n * Persist-before-advance is load-bearing (the legacy #ingest's ordering):\n * if the durable write fails, the batch must stay retryable — the redelivered\n * batch re-reduces from the OLD published state and retries the write.\n * Advancing in-memory first would make the retry a silent no-op (every\n * event filtered out, nothing re-saved), losing the batch durably.\n */\n async #commitBatchContext(ctx: BatchContext<ProcessorState<Contract>>): Promise<void> {\n const streamId = this.#requireProgress().streamId;\n const next: ProcessorProgress<ProcessorState<Contract>> = {\n streamId,\n reduction: {\n reducerVersion: this.hooks.contract.version,\n reducedThroughOffset: ctx.reducedThroughOffset,\n state: ctx.state,\n },\n processing: {\n acknowledgedThroughOffset: ctx.completedThroughOffset,\n cursorRevision: ctx.revision,\n },\n };\n const previousCommittedState = this.#progress?.reduction.state;\n await this.#commit(next, {\n expectedCursorRevision: ctx.revision,\n expectedStreamId: streamId,\n });\n this.#progress = next;\n ctx.eventsSinceCommit = 0;\n const committedEvents = ctx.uncommittedEvents.splice(0);\n const committedFailures = ctx.uncommittedParseFailures.splice(0);\n // The commit is durable and the published cursor advanced — the events\n // are genuinely CONSUMED, which is the moment self-measured event-consumption\n // metrics report (the legacy #ingest's noteBatchIngested placement).\n // Fed through the processor hooks so the wake capability's\n // consumption-lag samples stay current.\n if (committedEvents.length > 0) {\n const newestEventCreatedAtMs = Date.parse(committedEvents.at(-1)!.createdAt);\n this.hooks.noteBatchIngested({\n ingestedThroughOffset: next.processing.acknowledgedThroughOffset,\n // The offsets, not just how many (main's eventCount is subsumed —\n // the metrics derive the count from these): the cursor above sweeps\n // past rows a filtered subscription skipped, so only these can say an\n // own append came BACK rather than merely being overtaken.\n ingestedOffsets: committedEvents.map((event) => event.offset),\n ...(Number.isFinite(newestEventCreatedAtMs) && { newestEventCreatedAtMs }),\n ingestStartedAtMs: ctx.ingestStartedAtMs,\n atMs: this.now(),\n });\n }\n // Observers before waiters, both after the durable commit — the legacy\n // ingest ordering: by the time either\n // fires, published state already reflects the committed batch.\n if (!Object.is(previousCommittedState, next.reduction.state)) {\n this.#notifyStateChange({\n offset: next.reduction.reducedThroughOffset,\n state: next.reduction.state,\n });\n }\n this.#resolveEventWaiters(committedEvents, next.processing.acknowledgedThroughOffset);\n // Record skipped unparseable events AFTER the commit, in the background:\n // the raw event in the log is the authoritative record and the\n // idempotency key dedupes redelivery, so a failing record append can\n // never fail the batch it just rescued again.\n // (This preserves the legacy #ingest parse-failure behavior.)\n for (const { event, error } of committedFailures) {\n const message =\n `stream processor \"${this.hooks.contract.slug}\" skipped event at offset ` +\n `${event.offset} (\"${event.type}\"): it fails the contract's schema`;\n console.error(message, error);\n // A guarded raw append, not a processor-declared emitted event:\n // `stream/error-occurred` is core-owned and deliberately absent from\n // subclass `emits` — this is the runtime speaking, not the processor\n // author. The guard stops a delayed diagnostic for lifetime A from\n // landing after this path has been recreated as lifetime B.\n this.#runInBackground(() =>\n this.stream.appendIfStreamId({\n streamId,\n events: [\n {\n type: \"events.iterate.com/stream/error-occurred\",\n idempotencyKey: this.hooks.idempotencyKey(\"event-parse-failed\", event),\n source: { processor: this.hooks.processorStamp(streamId, event) },\n payload: {\n message,\n error: { name: error.name, message: error.message },\n },\n },\n ],\n }),\n );\n }\n }\n\n // ---------------------------------------------------------------------------\n // Progress load / refold / commit.\n // ---------------------------------------------------------------------------\n\n /**\n * Return the hosted source's authoritative effect cursor without reading\n * that source. Fresh/recreated lifetimes still land their durable fence\n * before the checkpoint escapes; an existing lifetime leaves its disposable\n * reduction cache unloaded until the one-way delivery callback.\n */\n async #prepareHostedCheckpoint(streamId: string): Promise<number> {\n if (this.#hasLoaded && this.#progress?.streamId === streamId) {\n return this.#progress.processing.acknowledgedThroughOffset;\n }\n if (this.#hasLoaded) {\n this.#hasLoaded = false;\n this.#loaded = undefined;\n this.#loadingStreamId = undefined;\n }\n\n const persisted = await this.durability?.progress.read();\n if (persisted === undefined) {\n const fresh = this.#freshProgress(streamId, 0);\n await this.#commit(fresh, {\n expectedCursorRevision: 0,\n expectedStreamId: undefined,\n });\n this.#progress = fresh;\n this.#hasLoaded = true;\n return 0;\n }\n if (persisted.streamId === streamId) {\n return persisted.processing.acknowledgedThroughOffset;\n }\n\n const replaceForStream = this.durability?.progress.replaceForStream;\n if (replaceForStream === undefined) {\n throw new Error(\n `stream processor \"${this.hooks.contract.slug}\" progress belongs to stream ID ` +\n `${persisted.streamId}, but the current stream ID is ${streamId}; ` +\n `this durability backend must reset its related projections before reopening`,\n );\n }\n const fresh = this.#freshProgress(streamId, persisted.processing.cursorRevision + 1);\n await replaceForStream(fresh, {\n expectedCursorRevision: persisted.processing.cursorRevision,\n expectedStreamId: persisted.streamId,\n });\n this.#progress = fresh;\n this.#hasLoaded = true;\n this.#notifyStateChange({ offset: 0, state: fresh.reduction.state });\n return 0;\n }\n\n #load(streamId: string): Promise<void> {\n return this.#loadWithStreamReplacement(streamId, true);\n }\n\n #loadPreparedStream(streamId: string): Promise<void> {\n if (this.#hasLoaded) return Promise.resolve();\n return this.#loadWithStreamReplacement(streamId, false);\n }\n\n #loadWithStreamReplacement(streamId: string, replaceMismatchedStream: boolean): Promise<void> {\n if (this.#hasLoaded && this.#progress?.streamId === streamId) return Promise.resolve();\n if (this.#hasLoaded) {\n this.#hasLoaded = false;\n this.#loaded = undefined;\n this.#loadingStreamId = undefined;\n }\n if (this.#loaded !== undefined) {\n if (this.#loadingStreamId === streamId) return this.#loaded;\n return this.#loaded.then(() =>\n this.#loadWithStreamReplacement(streamId, replaceMismatchedStream),\n );\n }\n this.#loadingStreamId = streamId;\n this.#loaded = this.#loadOnce(streamId, replaceMismatchedStream).catch((error: unknown) => {\n // Clear the memoized load so a later call retries instead of replaying\n // this rejection forever.\n this.#loaded = undefined;\n this.#loadingStreamId = undefined;\n throw error;\n });\n return this.#loaded;\n }\n\n #freshProgress(\n streamId: string,\n cursorRevision: number,\n ): ProcessorProgress<ProcessorState<Contract>> {\n return {\n streamId,\n reduction: {\n reducerVersion: this.hooks.contract.version,\n reducedThroughOffset: 0,\n state: this.hooks.initialState(),\n },\n processing: { acknowledgedThroughOffset: 0, cursorRevision },\n };\n }\n\n async #loadOnce(streamId: string, replaceMismatchedStream: boolean): Promise<void> {\n const persisted = await this.durability?.progress.read();\n if (persisted === undefined) {\n // Fresh processor: nothing observed yet, so the schema default IS the\n // fold of the (empty) acknowledged prefix. Persist the stream ID before\n // a checkpoint can escape, so no later wake can adopt unrelated\n // pre-existing progress.\n const fresh = this.#freshProgress(streamId, 0);\n await this.#commit(fresh, {\n expectedCursorRevision: 0,\n expectedStreamId: undefined,\n });\n this.#progress = fresh;\n this.#hasLoaded = true;\n return;\n }\n\n if (persisted.streamId !== streamId) {\n if (!replaceMismatchedStream) {\n throw new Error(\n `hosted callback for stream ID ${streamId} is stale; processor progress belongs to ` +\n `${persisted.streamId}`,\n );\n }\n const replaceForStream = this.durability?.progress.replaceForStream;\n if (replaceForStream === undefined) {\n throw new Error(\n `stream processor \"${this.hooks.contract.slug}\" progress belongs to stream ID ` +\n `${persisted.streamId}, but the current stream ID is ${streamId}; ` +\n `this durability backend must reset its related projections before reopening`,\n );\n }\n const fresh = this.#freshProgress(streamId, persisted.processing.cursorRevision + 1);\n await replaceForStream(fresh, {\n expectedCursorRevision: persisted.processing.cursorRevision,\n expectedStreamId: persisted.streamId,\n });\n this.#progress = fresh;\n this.#hasLoaded = true;\n this.#notifyStateChange({ offset: 0, state: fresh.reduction.state });\n return;\n }\n\n const acknowledged = persisted.processing.acknowledgedThroughOffset;\n const parsed = this.hooks.parseState(persisted.reduction.state);\n // A persisted reduction AHEAD of the acknowledgement violates the record\n // invariant (see ProcessorProgress): publishing it would show state\n // derived from events whose effects are not acknowledged. Treat it as a\n // cache miss — discard the fold, refold reduce-only through ack (below).\n const reducedAheadOfAck = persisted.reduction.reducedThroughOffset > acknowledged;\n if (\n persisted.reduction.reducerVersion === this.hooks.contract.version &&\n parsed.success &&\n !reducedAheadOfAck\n ) {\n let reduction: ReductionProgress<ProcessorState<Contract>> = {\n ...persisted.reduction,\n state: parsed.state,\n };\n if (reduction.reducedThroughOffset < acknowledged) {\n // The fold cache validly LAGS the acknowledgement (a commit cadence\n // may persist them apart) — but publishing the lagging fold as-is\n // would reduce the NEXT delivery onto state missing the events in\n // (reducedThrough, acknowledged] and then stamp it as reduced through\n // the acknowledged offset: those events' contributions silently vanish. Catch the fold\n // up REDUCE-ONLY (their effects are acknowledged; processEvent never\n // re-runs) and persist the healed cache before publishing.\n reduction = await this.#rebuildReduction(streamId, acknowledged, {\n state: reduction.state,\n reducedThroughOffset: reduction.reducedThroughOffset,\n });\n const progress: ProcessorProgress<ProcessorState<Contract>> = {\n streamId,\n reduction,\n processing: persisted.processing,\n };\n await this.#commit(progress, {\n expectedCursorRevision: persisted.processing.cursorRevision,\n expectedStreamId: streamId,\n });\n this.#progress = progress;\n this.#hasLoaded = true;\n return;\n }\n this.#progress = { streamId, reduction, processing: persisted.processing };\n this.#hasLoaded = true;\n return;\n }\n\n // REDUCE-ONLY REFOLD: the reduction cache is stale (reducer version\n // changed, the persisted fold no longer fits the schema, or the fold ran\n // AHEAD of the acknowledgement — all the same cache miss). DISCARD the\n // fold, KEEP the processing acknowledgement — this is the entire point of\n // the two-cursor split: a routine state-schema deploy rebuilds the cache\n // by re-running `reduce` ONLY, never `processEvent`, never effects. The\n // rebuild stages into locals; nothing partial is observable (every read\n // awaits this load).\n console.warn(\n reducedAheadOfAck\n ? `stream processor \"${this.hooks.contract.slug}\" persisted reduction cursor ` +\n `(${persisted.reduction.reducedThroughOffset}) is AHEAD of the acknowledged cursor ` +\n `(${acknowledged}) — an invalid record; discarding the fold and refolding ` +\n `reduce-only through the acknowledgement`\n : `stream processor \"${this.hooks.contract.slug}\" reduction cache is stale ` +\n `(persisted reducerVersion \"${persisted.reduction.reducerVersion}\", ` +\n `current \"${this.hooks.contract.version}\", state ${parsed.success ? \"valid\" : \"invalid\"}); ` +\n `refolding reduce-only through acknowledged offset ` +\n `${acknowledged}`,\n );\n const reduction = await this.#rebuildReduction(streamId, acknowledged);\n const progress: ProcessorProgress<ProcessorState<Contract>> = {\n streamId,\n reduction,\n processing: persisted.processing,\n };\n await this.#commit(progress, {\n expectedCursorRevision: persisted.processing.cursorRevision,\n expectedStreamId: streamId,\n });\n this.#progress = progress;\n this.#hasLoaded = true;\n }\n\n /** Rebuild the fold through `throughOffset`, reduce ONLY, paged — from\n * offset 0 by default, or extending `from` (a valid persisted fold that\n * LAGS the target, so only the gap's events are read). */\n async #rebuildReduction(\n streamId: string,\n throughOffset: number,\n from?: { state: ProcessorState<Contract>; reducedThroughOffset: number },\n ): Promise<ReductionProgress<ProcessorState<Contract>>> {\n let state = from?.state ?? this.hooks.initialState();\n let afterOffset = from?.reducedThroughOffset ?? 0;\n if (throughOffset > afterOffset) {\n for (;;) {\n const page = await this.stream.getEventPage({\n afterOffset,\n beforeOffset: throughOffset + 1,\n byteLimit: MAX_STREAM_EVENT_READ_BYTE_LIMIT,\n limit: this.readPageSize,\n });\n this.#assertReadStreamId(page.streamId, streamId);\n if (page.events.length === 0) break;\n for (const event of page.events) {\n if (event.offset > throughOffset) continue;\n const reduction = this.hooks.reduceRawEvent({ event, state });\n // Parse failures were recorded when first processed (idempotent);\n // a refold silently folds past them, exactly like live delivery.\n if (reduction !== undefined && !(\"parseError\" in reduction)) {\n state = reduction.state;\n }\n }\n afterOffset = page.events.at(-1)!.offset;\n }\n }\n return {\n reducerVersion: this.hooks.contract.version,\n reducedThroughOffset: throughOffset,\n state,\n };\n }\n\n async #commit(\n progress: ProcessorProgress<ProcessorState<Contract>>,\n opts: { expectedCursorRevision: number; expectedStreamId: string | undefined },\n ): Promise<void> {\n if (this.durability === undefined) return;\n await this.durability.progress.commit(progress, opts);\n }\n\n /**\n * Re-run `reduce` + `processEvent` from the acknowledged cursor by\n * reading the journal itself — catch-up cannot rely on a callback whose\n * starting offset was fixed when it opened. One page of lookahead means\n * every non-final batch carries a streamMaxOffset past its own last event (the\n * `caughtUp` flag appears only on the genuinely final page), matching the\n * host's catch-up.\n */\n async #selfCatchUp(): Promise<void> {\n const streamId = this.#requireProgress().streamId;\n let scannedAfterOffset = this.#requireProgress().processing.acknowledgedThroughOffset;\n let targetOffset: number | undefined;\n for (;;) {\n const page = await this.stream.getEventPage({\n afterOffset: scannedAfterOffset,\n ...(targetOffset === undefined ? {} : { beforeOffset: targetOffset + 1 }),\n byteLimit: MAX_STREAM_EVENT_READ_BYTE_LIMIT,\n limit: this.readPageSize,\n });\n this.#assertReadStreamId(page.streamId, streamId);\n targetOffset ??= page.streamMaxOffset;\n const lastEventOffset = page.events.at(-1)?.offset;\n const isFinalPage = lastEventOffset === undefined || lastEventOffset >= targetOffset;\n const scannedThroughOffset = isFinalPage ? targetOffset : lastEventOffset;\n if (scannedThroughOffset <= scannedAfterOffset) return;\n await this.#processBatch({\n streamId,\n events: page.events,\n scannedAfterOffset,\n scannedThroughOffset,\n streamMaxOffset: targetOffset,\n });\n scannedAfterOffset = scannedThroughOffset;\n if (isFinalPage) return;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Small shared machinery.\n // ---------------------------------------------------------------------------\n\n async #readCurrentStreamId(expectedStreamId?: string): Promise<string> {\n // Identity-only read: the page envelope carries the lifetime and head, so\n // do not transfer the stream's first retained event on every processor read.\n const page = await this.stream.getEventPage({ afterOffset: Number.MAX_SAFE_INTEGER, limit: 1 });\n if (expectedStreamId !== undefined && page.streamId !== expectedStreamId) {\n throw new Error(\n `stream processor \"${this.hooks.contract.slug}\" was opened for stream ID ` +\n `${expectedStreamId}, but the stream at this path is ${page.streamId}`,\n );\n }\n return page.streamId;\n }\n\n #assertReadStreamId(actualStreamId: string, expectedStreamId: string): void {\n if (actualStreamId === expectedStreamId) return;\n throw new Error(\n `stream processor \"${this.hooks.contract.slug}\" stream ID changed during a read ` +\n `(${expectedStreamId} -> ${actualStreamId})`,\n );\n }\n\n /** Fire-and-forget async work backed by the keepalive, with failures logged. */\n #runInBackground(work: () => Promise<unknown>): void {\n this.#keepAliveBackedWork(work).catch((error: unknown) => {\n console.error(\"stream processor runner background work failed\", error);\n });\n }\n\n /**\n * Route registered work through the recovery adapter's keepalive when\n * present (both `blockProcessorWhile` and `runInBackground` ride it — \"the\n * DO died owing work\" must equal \"the alarm was armed\"), else through the\n * plain `keepAlive` hook, else run directly.\n */\n async #keepAliveBackedWork(work: () => Promise<unknown>): Promise<unknown> {\n const keepAliveWhile = this.durability?.recovery?.keepAliveWhile ?? this.keepAlive;\n return await awaitKeepAliveBacked(keepAliveWhile, work);\n }\n\n /** Serialize batches + self-pulls; the chain swallows each entry's\n * failure so one failed batch never wedges the entries behind it. */\n #enqueue<T>(work: () => Promise<T>): Promise<T> {\n const next = this.#chain.then(() => {\n this.#assertNotDisposed();\n return work();\n });\n this.#chain = next.then(\n () => undefined,\n () => undefined,\n );\n return next;\n }\n\n #assertNotDisposed(): void {\n if (this.#disposed) {\n throw new Error(\n `StreamProcessorRunner for \"${this.hooks.contract.slug}\" is disposed; it accepts no new work`,\n );\n }\n }\n\n #requireProgress(): ProcessorProgress<ProcessorState<Contract>> {\n if (this.#progress === undefined) {\n throw new Error(\"StreamProcessorRunner progress read before load — this is a runner bug\");\n }\n return this.#progress;\n }\n\n // A throwing observer is ITS bug, never the batch's: the commit already\n // landed, so failures are logged and the loop continues (the legacy\n // #notifyStateChange, verbatim).\n #notifyStateChange(snapshot: { offset: number; state: ProcessorState<Contract> }): void {\n for (const observer of [...this.#stateChangeObservers]) {\n try {\n observer(snapshot);\n } catch (error) {\n console.error(\"stream processor runner state-change observer failed\", error);\n }\n }\n }\n\n #registerEventWaiter(\n match:\n | { kind: \"predicate\"; predicate: (event: StreamEvent) => boolean }\n | { kind: \"offset\"; offset: number },\n opts: { signal?: AbortSignal; timeoutMs?: number },\n ): { promise: Promise<void>; reject: (error: unknown) => void } {\n let waiter!: EventWaiter;\n const promise = new Promise<void>((resolve, reject) => {\n waiter = { ...match, reject, resolve, signal: opts.signal };\n this.#eventWaiters.add(waiter);\n if (opts.timeoutMs !== undefined) {\n waiter.timer = setTimeout(() => {\n this.#settleEventWaiter(waiter, {\n error: new Error(`waitUntilEvent timed out after ${opts.timeoutMs}ms`),\n });\n }, opts.timeoutMs);\n }\n if (opts.signal !== undefined) {\n waiter.abortListener = () => {\n this.#settleEventWaiter(waiter, { error: abortReason(opts.signal!) });\n };\n opts.signal.addEventListener(\"abort\", waiter.abortListener, { once: true });\n // The caller may abort between the public preflight check and listener\n // registration. Re-check after registration so that edge cannot halt.\n if (opts.signal.aborted) waiter.abortListener();\n }\n });\n return {\n promise,\n reject: (error: unknown) => this.#settleEventWaiter(waiter, { error }),\n };\n }\n\n #settleEventWaiter(\n waiter: EventWaiter,\n outcome: { error: unknown } | { value: undefined },\n ): void {\n if (!this.#eventWaiters.delete(waiter)) return;\n if (waiter.timer !== undefined) clearTimeout(waiter.timer);\n if (waiter.signal !== undefined && waiter.abortListener !== undefined) {\n waiter.signal.removeEventListener(\"abort\", waiter.abortListener);\n }\n if (\"error\" in outcome) waiter.reject(outcome.error);\n else waiter.resolve();\n }\n\n // Settle waiters after the durable commit + published-cursor advance.\n // Predicate waits match actual delivered events; offset waits match the\n // acknowledged scan offset, so an empty/filtered interval cannot leave a\n // read-your-writes waiter halted below a cursor the runner already proved.\n #resolveEventWaiters(events: readonly StreamEvent[], acknowledgedThroughOffset: number): void {\n for (const waiter of this.#eventWaiters) {\n let matched = false;\n try {\n matched =\n waiter.kind === \"offset\"\n ? acknowledgedThroughOffset >= waiter.offset\n : events.some(waiter.predicate);\n } catch (error) {\n this.#settleEventWaiter(waiter, { error });\n continue;\n }\n if (matched) {\n this.#settleEventWaiter(waiter, { value: undefined });\n }\n }\n }\n}\n\nfunction assertProcessorEventBatch(batch: StreamProcessorEventBatch): void {\n const coordinates = [\n [\"scannedAfterOffset\", batch.scannedAfterOffset],\n [\"scannedThroughOffset\", batch.scannedThroughOffset],\n [\"streamMaxOffset\", batch.streamMaxOffset],\n ] as const;\n for (const [name, value] of coordinates) {\n if (!Number.isSafeInteger(value) || value < 0) {\n throw new Error(`stream processor event batch ${name} must be a non-negative safe integer`);\n }\n }\n if (batch.scannedThroughOffset < batch.scannedAfterOffset) {\n throw new Error(\n `stream processor event batch scan regressed: ${batch.scannedAfterOffset} -> ${batch.scannedThroughOffset}`,\n );\n }\n if (batch.streamMaxOffset < batch.scannedThroughOffset) {\n throw new Error(\n `stream processor event batch scan ${batch.scannedThroughOffset} is ahead of stream maximum offset ${batch.streamMaxOffset}`,\n );\n }\n let previousOffset = batch.scannedAfterOffset;\n for (const event of batch.events) {\n if (!Number.isSafeInteger(event.offset) || event.offset <= previousOffset) {\n throw new Error(\n `stream processor event batch events must increase strictly after scan cursor ${previousOffset}; found ${event.offset}`,\n );\n }\n if (event.offset > batch.scannedThroughOffset) {\n throw new Error(\n `stream processor event batch event ${event.offset} is beyond scanned-through offset ${batch.scannedThroughOffset}`,\n );\n }\n previousOffset = event.offset;\n }\n}\n\nfunction abortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new Error(\"waitUntilEvent aborted\");\n}\n","// The processor host's revival guarantee: a Durable Object that dies owing\n// work gets another processor wake.\n//\n// THE GAP THIS CLOSES. Stream-side sending is already durable (the source\n// cursor rows + the stream DO's alarm retry/halt machinery,\n// stream-event-sender.ts). What nothing covered is the ZERO-LAG wedge: a\n// processor journals an obligation (`llm-request-requested`,\n// `script-run-requested`), its checkpoint advances, and the in-flight\n// attempt dies with the incarnation — a deploy evicts every DO. The stream\n// sees no lag, arms no retry, and nothing ever wakes the processor again. The\n// agent sits at `phase: \"requested\"` forever (the 2026-06-10 and 2026-07-07\n// prd incidents).\n//\n// THE MECHANISM. While any registered work is in flight (`blockProcessorWhile`\n// and `runInBackground` both count), schedule a durable DO alarm a few\n// seconds ahead. Work settles cleanly → a confirmation fire finds quiet and\n// disarms. The incarnation dies mid-work → the alarm survives it, fires in a\n// fresh incarnation, and REVIVES: append one persisted revival fact to the\n// stream (which cold-boots the stream DO — its `woken` fan-out restores the\n// sender). Its delivery reaches head for the processor: a consumer receives the\n// fact, while a non-consumer receives the runner's eventless at-head pass.\n// Either path can settle whatever the dead incarnation left behind. Recovery\n// has ONE entrypoint — batch delivery — and the stream records the whole episode: requested → revived → failure\n// completion → reschedule.\n//\n// THE IMPOSSIBILITY GUARANTEE. A bug must never keep a DO awake forever, so\n// the revival alarm is a crash-loop breaker, not a loop: every revival attempt\n// durably marks `revivals + 1` BEFORE doing anything else and arms its next\n// try at a growing backoff (10s → 1m → 5m → 30m → 6h, plateau forever — a\n// permanently failing host costs ~4 wakes a day). The mark only resets on a\n// QUIET-CLEAN confirmation (a fire that finds all tracked work settled\n// successfully — not merely \"the revival pass resolved\", which a\n// crash-looping post-revival batch would reset endlessly) or on a version\n// change: the overwhelmingly likely fix for a deterministic crash loop is a\n// deploy, so a revival that notices a new worker version starts from a fresh\n// budget. Arming is dropped while a revival pass runs — otherwise the\n// pass's own tracked work would pull the alarm back to the short lead and a\n// crash inside the pass would defeat the backoff.\n//\n// This module is transport-free, clock-free, and storage-free — everything\n// arrives through {@link ProcessorKeepaliveHooks}, using the same injected-hook\n// pattern as stream-event-sender.ts, so the whole state machine runs in plain-node vitest\n// with a mutable clock and scripted revivals (stream-processor-keepalive.test.ts).\n\nimport type { StreamEventInput } from \"./schemas.ts\";\n\n/**\n * The durable mark, stored in DO KV BELOW the journal/fold: the crash-loop\n * breaker must live beneath the state reduction it protects (a failing fold\n * cannot be asked to fold its own pause fact). KV is authoritative here;\n * journal facts about revivals are evidence, not enforcement — the deliberate\n * inversion of the usual rule.\n */\nexport type KeepaliveRecord = {\n /** Consecutive revival attempts without a quiet-clean confirmation. */\n revivals: number;\n /** Epoch ms of the most recent revival attempt (drives the backoff). */\n lastRevivalAt: number;\n /** Worker version at the last write; a different live version resets the budget. */\n version: string;\n /**\n * The keepalive's own armed alarm time, or null when disarmed. Persisted so\n * a fresh incarnation can tell \"this fire is mine\" from \"another subsystem's\n * slice of the shared DO alarm is due\" (e.g. the scheduler's) — in-memory\n * state does not survive the eviction that makes revival necessary.\n */\n armedAtMs: number | null;\n};\n\ntype ProcessorKeepaliveHooks = {\n /** Injected clock (epoch ms). */\n now(): number;\n /** Read the durable record. Synchronous DO KV in production. */\n readRecord(): KeepaliveRecord | undefined;\n /** Write the durable record. */\n writeRecord(record: KeepaliveRecord): void;\n /** Repoint (or clear) the keepalive's slice of the DO alarm. */\n armAlarm(atMs: number | null): void;\n /** Keep the DO alive while tracked work runs (ctx.waitUntil). */\n keepAlive(work: Promise<unknown>): void;\n /**\n * The revival pass: append the journaled revival fact, then pull every\n * hosted processor through its pending events so end-of-batch\n * reconciliations run. Must throw on failure — the breaker owns the retry.\n */\n revive(record: KeepaliveRecord): Promise<void>;\n /**\n * Classify and dispose a revival that can never become valid on retry.\n * Return true only after synchronously removing this attempt's durable\n * desire (or proving a newer desire replaced it). The keepalive then stops\n * without arming another retry.\n */\n discardFailedRevival?(error: unknown, record: KeepaliveRecord): boolean;\n /** Best-effort journal evidence (crash-loop warnings). Must not throw. */\n appendFact(event: StreamEventInput): void;\n /** Current worker deploy version (antidote-deploy budget reset). */\n version: string;\n};\n\n/** How far ahead of in-flight work the alarm is scheduled. Bounds post-eviction\n * revival latency; a deploy mid-agent-turn recovers within roughly this. */\nexport const KEEPALIVE_ALARM_LEAD_MS = 10_000;\n\n/**\n * Floor between redundant re-assertions of an already-sufficient alarm.\n * See #ensureArmedForWork: the re-assert exists to heal a lost platform\n * write, and healing within a fraction of the lead is as good as instantly.\n */\nconst KEEPALIVE_REASSERT_MIN_INTERVAL_MS = 2_500;\n\n/** Revival backoff by attempt number (1-based); past the table, the plateau. */\nconst REVIVAL_BACKOFF_MS = [10_000, 60_000, 5 * 60_000, 30 * 60_000];\nexport const REVIVAL_BACKOFF_PLATEAU_MS = 6 * 60 * 60_000;\n\n/** Attempts before the crash-loop evidence fact is appended (once per version). */\nconst CRASH_LOOP_EVIDENCE_THRESHOLD = 3;\n\n/**\n * Consecutive busy fires with NO settlement in between before the window is\n * treated as wedged (a hung promise nothing will ever settle — e.g. a socket\n * with no deadline). 90 fires ≈ 15 minutes at the lead, comfortably past the\n * longest legitimate tracked work (the providers' 10-minute deadlines), so\n * legit work never trips it while a wedge decays into the revival backoff\n * instead of re-arming every lead interval forever.\n */\nexport const MAX_CONSECUTIVE_BUSY_REFIRES = 90;\n\n/** The semantic outcome of one platform alarm reaching the keepalive. */\ntype ProcessorKeepaliveAlarmAction =\n | \"not_due\"\n | \"busy_rearmed\"\n | \"revival_hung_backoff\"\n | \"clean_disarmed\"\n | \"revived\"\n | \"revival_discarded\"\n | \"revival_failed\";\n\nexport function revivalBackoffMs(revivals: number): number {\n return REVIVAL_BACKOFF_MS[revivals - 1] ?? REVIVAL_BACKOFF_PLATEAU_MS;\n}\n\nconst FRESH_RECORD: Omit<KeepaliveRecord, \"version\"> = {\n revivals: 0,\n lastRevivalAt: 0,\n armedAtMs: null,\n};\n\nexport class ProcessorKeepalive {\n readonly #hooks: ProcessorKeepaliveHooks;\n\n #inFlight = 0;\n /** Any tracked work settled successfully since the alarm was armed. A\n * successful revival pass also sets this — the pass IS settled work. */\n #sawCleanSettle = false;\n /** Any tracked work failed since the alarm was armed. Failures mean an\n * obligation may be unsettled (a debounce append that lost its stream), so\n * the next fire revives instead of disarming. */\n #sawFailure = false;\n /** Suppresses arm-earlier while the revival pass runs (see module doc). */\n #reviving = false;\n /** Consecutive busy fires without any settlement (wedged-work detector). */\n #busyRefires = 0;\n\n constructor(hooks: ProcessorKeepaliveHooks) {\n this.#hooks = hooks;\n }\n\n /**\n * The keepalive's current alarm desire, for the host's slice merge. Read\n * straight from the durable record (synchronous DO KV) — a separate\n * in-memory copy would be one more thing to drift after an eviction, and\n * stale copied state is exactly the failure class this module hunts.\n */\n /** When an already-armed desire was last re-asserted; in-memory on purpose\n * (a fresh incarnation should re-assert on its first tracked work). */\n #lastReassertAtMs = 0;\n\n get armedAtMs(): number | null {\n return this.#hooks.readRecord()?.armedAtMs ?? null;\n }\n\n /**\n * Register one unit of in-flight work. Every registered work closure —\n * blocking and background alike — rides through here, so \"the DO died owing\n * work\" is exactly \"the DO died with the alarm armed\".\n */\n track(work: Promise<unknown>): void {\n this.#inFlight += 1;\n this.#ensureArmedForWork();\n this.#hooks.keepAlive(\n work.then(\n () => {\n this.#inFlight -= 1;\n this.#sawCleanSettle = true;\n this.#busyRefires = 0;\n },\n () => {\n this.#inFlight -= 1;\n this.#sawFailure = true;\n this.#busyRefires = 0;\n },\n ),\n );\n }\n\n /**\n * The DO alarm handler body. The shared alarm may fire for another\n * subsystem's slice (the scheduler's), so this self-gates on the persisted\n * armed time and does nothing when the fire is not the keepalive's.\n */\n async onAlarm(): Promise<ProcessorKeepaliveAlarmAction> {\n const now = this.#hooks.now();\n const armedAt = this.armedAtMs;\n if (armedAt === null || now < armedAt) return \"not_due\";\n\n // Still working (or a revival pass is still running — its safety net owns\n // the cadence, and a SECOND pass must never start underneath it): push\n // the alarm ahead again — unless NOTHING has settled for so many\n // consecutive fires that the work is wedged (a hung promise no deadline\n // owns). A wedge falls through to the revival alarm so its cadence\n // decays along the backoff instead of firing at the lead interval forever.\n if (this.#inFlight > 0 || this.#reviving) {\n this.#busyRefires += 1;\n if (this.#busyRefires < MAX_CONSECUTIVE_BUSY_REFIRES) {\n this.#arm(now + KEEPALIVE_ALARM_LEAD_MS);\n return \"busy_rearmed\";\n }\n if (this.#reviving) {\n // The revival pass itself is hung. Starting another would lift the\n // arm-dropping under the running one; schedule at the longest interval instead\n // — the impossibility guarantee holds (~4 wakes/day) and any real\n // settlement resets the counter.\n this.#arm(now + REVIVAL_BACKOFF_PLATEAU_MS);\n return \"revival_hung_backoff\";\n }\n }\n\n // Quiet and clean: nothing in flight and every tracked settlement since\n // arming succeeded. The obligations those settlements journaled were\n // reconciled by their own batches; nothing is owed. Disarm, and reset the\n // crash-loop budget — this confirmation firing is the proof the DO\n // survives its own work.\n if (this.#inFlight === 0 && this.#sawCleanSettle && !this.#sawFailure) {\n this.#sawCleanSettle = false;\n this.#disarmAndReset();\n return \"clean_disarmed\";\n }\n\n // Revival: either this is a fresh incarnation (the armer died — flags\n // empty) or tracked work failed. Mark durably BEFORE doing anything, arm\n // the safety-net retry at the backoff, then run the pass.\n return await this.#revive(now);\n }\n\n async #revive(\n now: number,\n ): Promise<\n Extract<ProcessorKeepaliveAlarmAction, \"revived\" | \"revival_discarded\" | \"revival_failed\">\n > {\n const previous = this.#hooks.readRecord();\n const priorRevivals =\n previous === undefined || previous.version !== this.#hooks.version ? 0 : previous.revivals;\n const record: KeepaliveRecord = {\n revivals: priorRevivals + 1,\n lastRevivalAt: now,\n version: this.#hooks.version,\n armedAtMs: now + revivalBackoffMs(priorRevivals + 1),\n };\n // Mark-before / clear-after: a crash anywhere past this line leaves the\n // incremented mark and an armed retry — the loop can only decay, never\n // tighten. The clear is the quiet-clean confirmation above, deliberately\n // NOT \"the pass resolved\": a pass that resolves but whose follow-on work\n // crashes the DO would otherwise reset the budget every round.\n this.#hooks.writeRecord(record);\n this.#hooks.armAlarm(record.armedAtMs);\n\n if (record.revivals === CRASH_LOOP_EVIDENCE_THRESHOLD) {\n this.#hooks.appendFact({\n type: \"events.iterate.com/stream/error-occurred\",\n idempotencyKey: `processor-host-crash-loop:${record.version}`,\n payload: {\n message:\n `processor host revival has failed ${record.revivals} consecutive times on ` +\n `version ${record.version}; backing off (plateau ${REVIVAL_BACKOFF_PLATEAU_MS / 60_000}m). ` +\n `A deploy resets the budget.`,\n },\n });\n }\n\n this.#sawFailure = false;\n this.#sawCleanSettle = false;\n this.#reviving = true;\n try {\n await this.#hooks.revive(record);\n // The pass itself is settled clean work; the confirmation fire at the\n // short lead observes it (plus anything the pass scheduled) and resets\n // the budget if the window stays quiet. A WEDGED window keeps the\n // safety-net alarm instead: pulling it back to the lead would let the\n // hung work fire at lead cadence forever.\n this.#sawCleanSettle = true;\n const wedged = this.#inFlight > 0 && this.#busyRefires >= MAX_CONSECUTIVE_BUSY_REFIRES;\n if (!wedged) this.#arm(this.#hooks.now() + KEEPALIVE_ALARM_LEAD_MS);\n return \"revived\";\n } catch (error) {\n if (this.#hooks.discardFailedRevival?.(error, record) === true) {\n this.#sawFailure = false;\n this.#sawCleanSettle = false;\n return \"revival_discarded\";\n }\n console.error(\"stream processor host revival failed; backing off\", {\n revivals: record.revivals,\n nextAttemptAt: record.armedAtMs,\n error,\n });\n this.#sawFailure = true;\n // The safety-net alarm armed above owns the retry.\n return \"revival_failed\";\n } finally {\n this.#reviving = false;\n }\n }\n\n /**\n * The operator's no-deploy antidote: clear the crash-loop budget and, when\n * a retry is owed (the record is armed), pull it in to the confirmation\n * lead so the next fire revives promptly on the fresh budget. Without this\n * the mark resets only on a quiet-clean confirmation or a version change —\n * a 3-strikes plateau otherwise mutes a wedged processor for six hours at\n * a time with a deploy as the only cure (the 2026-08-11 prod incident).\n */\n resetBackoff(): void {\n const record = this.#hooks.readRecord();\n if (record === undefined) return;\n if (record.armedAtMs === null) {\n // Nothing owed — just clear the stale budget.\n this.#hooks.writeRecord({ ...FRESH_RECORD, version: this.#hooks.version });\n return;\n }\n const atMs = this.#hooks.now() + KEEPALIVE_ALARM_LEAD_MS;\n this.#hooks.writeRecord({ ...FRESH_RECORD, version: this.#hooks.version, armedAtMs: atMs });\n this.#hooks.armAlarm(atMs);\n }\n\n /** Arm for in-flight work: move the alarm earlier, never later, and never\n * during a revival pass (its backoff safety net must govern). */\n #ensureArmedForWork(): void {\n if (this.#reviving) return;\n const nowMs = this.#hooks.now();\n const atMs = nowMs + KEEPALIVE_ALARM_LEAD_MS;\n const armedAt = this.armedAtMs;\n if (armedAt !== null && armedAt <= atMs) {\n // The record says a sufficient alarm exists — but the record proves the\n // DESIRE, not the platform write (a setAlarm can fail after the KV\n // committed, and the host swallows it into \"platform state unknown\").\n // Re-assert the desire: the host's reconcile is a pure in-memory\n // comparison when the platform alarm matches, and re-issues the write\n // when a previous one failed. Without this, a lost alarm in a WARM\n // incarnation stays lost until the next boot — the boot-time reconcile\n // only covers fresh incarnations.\n //\n // RATE-LIMITED, because for a facet this re-assert is not free: it is\n // an RPC to the parent Durable Object plus two output-gated storage\n // writes there — and `track` runs once per delivered batch, which on a\n // 50 Hz audio lane put that RPC inside every delivery acknowledgement.\n // Healing a lost platform alarm within a couple of seconds of tracked\n // work is every bit as good as healing it instantly: the alarm being\n // guarded fires ten seconds out.\n if (nowMs - this.#lastReassertAtMs < KEEPALIVE_REASSERT_MIN_INTERVAL_MS) return;\n this.#lastReassertAtMs = nowMs;\n this.#hooks.armAlarm(armedAt);\n return;\n }\n this.#lastReassertAtMs = nowMs;\n this.#arm(atMs);\n }\n\n #arm(atMs: number): void {\n const record = this.#hooks.readRecord();\n this.#hooks.writeRecord({\n ...(record?.version === this.#hooks.version\n ? record\n : { ...FRESH_RECORD, version: this.#hooks.version }),\n armedAtMs: atMs,\n });\n this.#hooks.armAlarm(atMs);\n }\n\n #disarmAndReset(): void {\n const record = this.#hooks.readRecord();\n if (record !== undefined && (record.revivals !== 0 || record.armedAtMs !== null)) {\n this.#hooks.writeRecord({ ...FRESH_RECORD, version: this.#hooks.version });\n }\n this.#hooks.armAlarm(null);\n }\n}\n"],"mappings":"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"}