@demlik/tea 0.20.0 → 0.22.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 (130) hide show
  1. package/README.md +11 -2
  2. package/dist/agent/index.d.ts +36 -23
  3. package/dist/agent/index.js +12 -10
  4. package/dist/{chunk-UHAQ4PI6.js → chunk-2A5XTKQQ.js} +205 -59
  5. package/dist/chunk-2A5XTKQQ.js.map +1 -0
  6. package/dist/{chunk-JAKM53JF.js → chunk-7K7SEYCH.js} +16 -15
  7. package/dist/chunk-7K7SEYCH.js.map +1 -0
  8. package/dist/{chunk-RKVUVALV.js → chunk-A3QPMX5M.js} +31 -9
  9. package/dist/chunk-A3QPMX5M.js.map +1 -0
  10. package/dist/{chunk-DVNXEB7V.js → chunk-BVFS3ICC.js} +3 -3
  11. package/dist/chunk-BVFS3ICC.js.map +1 -0
  12. package/dist/{chunk-FTCYY2DO.js → chunk-CPE5ZQXQ.js} +3 -3
  13. package/dist/{chunk-FTCYY2DO.js.map → chunk-CPE5ZQXQ.js.map} +1 -1
  14. package/dist/chunk-DEAPNMKO.js +11 -0
  15. package/dist/chunk-DEAPNMKO.js.map +1 -0
  16. package/dist/{chunk-XWIE5V7R.js → chunk-ETRNSJCR.js} +4 -4
  17. package/dist/{chunk-XWIE5V7R.js.map → chunk-ETRNSJCR.js.map} +1 -1
  18. package/dist/chunk-FXWDDNGD.js +98 -0
  19. package/dist/chunk-FXWDDNGD.js.map +1 -0
  20. package/dist/chunk-GFGOQIEC.js +10 -0
  21. package/dist/chunk-GFGOQIEC.js.map +1 -0
  22. package/dist/{chunk-2R62Q5UK.js → chunk-IAHEYPYJ.js} +3 -3
  23. package/dist/{chunk-2R62Q5UK.js.map → chunk-IAHEYPYJ.js.map} +1 -1
  24. package/dist/{chunk-NGYFBD5G.js → chunk-J5JNLXO7.js} +3 -3
  25. package/dist/{chunk-NGYFBD5G.js.map → chunk-J5JNLXO7.js.map} +1 -1
  26. package/dist/{chunk-72CN3VPY.js → chunk-NFOGYDRS.js} +5 -5
  27. package/dist/{chunk-72CN3VPY.js.map → chunk-NFOGYDRS.js.map} +1 -1
  28. package/dist/{chunk-A73IJ27X.js → chunk-R2YNDRGL.js} +3 -3
  29. package/dist/{chunk-A73IJ27X.js.map → chunk-R2YNDRGL.js.map} +1 -1
  30. package/dist/{chunk-5HVKDHY6.js → chunk-R5NXOPL2.js} +3 -3
  31. package/dist/{chunk-5HVKDHY6.js.map → chunk-R5NXOPL2.js.map} +1 -1
  32. package/dist/chunk-RMBZ7YH7.js +52 -0
  33. package/dist/chunk-RMBZ7YH7.js.map +1 -0
  34. package/dist/{chunk-AIXC33SE.js → chunk-SHXUSTHM.js} +62 -15
  35. package/dist/chunk-SHXUSTHM.js.map +1 -0
  36. package/dist/{chunk-TAKPC6SD.js → chunk-UGTC33VH.js} +4 -4
  37. package/dist/{chunk-TAKPC6SD.js.map → chunk-UGTC33VH.js.map} +1 -1
  38. package/dist/{chunk-EA47MI5Q.js → chunk-VH5MYJ2K.js} +4 -4
  39. package/dist/{chunk-EA47MI5Q.js.map → chunk-VH5MYJ2K.js.map} +1 -1
  40. package/dist/{chunk-2774I242.js → chunk-WZEDIUWR.js} +21 -20
  41. package/dist/chunk-WZEDIUWR.js.map +1 -0
  42. package/dist/{chunk-TGJLXZAU.js → chunk-ZRRET3RP.js} +11 -11
  43. package/dist/chunk-ZRRET3RP.js.map +1 -0
  44. package/dist/{core-CtvAyPpH.d.ts → core-DNDAEhO2.d.ts} +9 -12
  45. package/dist/devtools/index.d.ts +4 -4
  46. package/dist/do/index.d.ts +103 -38
  47. package/dist/do/index.js +40 -21
  48. package/dist/do/index.js.map +1 -1
  49. package/dist/{drive-loop-Dw1sGSsW.d.ts → drive-loop-DlOEv49c.d.ts} +2 -2
  50. package/dist/{durable-effects-Ump5lSdO.d.ts → durable-effects-wLWEdy9H.d.ts} +1 -1
  51. package/dist/effect/index.d.ts +121 -28
  52. package/dist/effect/index.js +45 -31
  53. package/dist/effect/index.js.map +1 -1
  54. package/dist/extension/index.d.ts +3 -3
  55. package/dist/extension/index.js +2 -2
  56. package/dist/extension/index.js.map +1 -1
  57. package/dist/flow/index.d.ts +17 -13
  58. package/dist/flow/index.js +15 -12
  59. package/dist/flow/index.js.map +1 -1
  60. package/dist/idempotency/index.d.ts +3 -3
  61. package/dist/idempotency/index.js +3 -3
  62. package/dist/{index-CrcoVfd_.d.ts → index-9P1PaXIV.d.ts} +2 -2
  63. package/dist/{index-D3NY3VCN.d.ts → index-BBLI2WWN.d.ts} +1 -1
  64. package/dist/{index-Dl71V7kt.d.ts → index-C1WhBZ6a.d.ts} +4 -5
  65. package/dist/{index-CKtEKGyp.d.ts → index-C92SgNWz.d.ts} +3 -3
  66. package/dist/{index-CZZ3HI-Z.d.ts → index-CAsUE_d1.d.ts} +4 -4
  67. package/dist/{index-EIgfl83J.d.ts → index-CO34esWT.d.ts} +2 -2
  68. package/dist/{index-Bk2hBUdK.d.ts → index-COeX9Bmz.d.ts} +11 -9
  69. package/dist/{index-CVU7SK3a.d.ts → index-Cg0QXnzf.d.ts} +2 -2
  70. package/dist/index.d.ts +9 -9
  71. package/dist/index.js +2 -2
  72. package/dist/jev/index.d.ts +15 -15
  73. package/dist/jev/index.js +5 -5
  74. package/dist/jev/index.js.map +1 -1
  75. package/dist/laws-DTtCDDlU.d.ts +135 -0
  76. package/dist/loop-DReKGV1X.d.ts +70 -0
  77. package/dist/{machine-Dtre2TmN.d.ts → machine-C0WhOAyB.d.ts} +7 -7
  78. package/dist/machine-viz/index.d.ts +1 -1
  79. package/dist/machine-viz/index.js +2 -2
  80. package/dist/mem/index.d.ts +4 -3
  81. package/dist/mem/index.js +2 -2
  82. package/dist/mem/index.js.map +1 -1
  83. package/dist/node/index.d.ts +57 -9
  84. package/dist/node/index.js +175 -68
  85. package/dist/node/index.js.map +1 -1
  86. package/dist/otel/index.d.ts +28 -12
  87. package/dist/otel/index.js +25 -21
  88. package/dist/otel/index.js.map +1 -1
  89. package/dist/paginate/index.d.ts +4 -4
  90. package/dist/paginate/index.js +5 -5
  91. package/dist/parity/index.d.ts +11 -15
  92. package/dist/parity/index.js +4 -9
  93. package/dist/parity/index.js.map +1 -1
  94. package/dist/pbt/index.d.ts +279 -67
  95. package/dist/pbt/index.js +299 -58
  96. package/dist/pbt/index.js.map +1 -1
  97. package/dist/persistence/index.d.ts +10 -12
  98. package/dist/persistence/index.js +4 -4
  99. package/dist/promise/index.d.ts +118 -21
  100. package/dist/promise/index.js +6 -4
  101. package/dist/react/index.d.ts +34 -21
  102. package/dist/react/index.js +271 -42
  103. package/dist/react/index.js.map +1 -1
  104. package/dist/resilience/index.d.ts +6 -6
  105. package/dist/resilience/index.js +5 -5
  106. package/dist/{run-fiBe7pOG.d.ts → run-Cnw4qXj2.d.ts} +105 -23
  107. package/dist/{runtime-types-DEluQgGO.d.ts → runtime-types-CP-ykwgf.d.ts} +157 -29
  108. package/dist/testing/effect/index.d.ts +162 -9
  109. package/dist/testing/effect/index.js +110 -10
  110. package/dist/testing/effect/index.js.map +1 -1
  111. package/dist/testing/index.d.ts +6 -8
  112. package/dist/testing/index.js +4 -24
  113. package/dist/testing/index.js.map +1 -1
  114. package/dist/testing/promise/index.d.ts +4 -4
  115. package/dist/testing/promise/index.js +5 -5
  116. package/dist/timing/index.d.ts +4 -4
  117. package/dist/timing/index.js +4 -4
  118. package/dist/{types-CXNzlbAg.d.ts → types-S53WDy_h.d.ts} +1 -1
  119. package/dist/work-queue/index.d.ts +4 -4
  120. package/dist/work-queue/index.js +2 -2
  121. package/package.json +19 -17
  122. package/dist/chunk-2774I242.js.map +0 -1
  123. package/dist/chunk-AIXC33SE.js.map +0 -1
  124. package/dist/chunk-DVNXEB7V.js.map +0 -1
  125. package/dist/chunk-HX3C6LDK.js +0 -34
  126. package/dist/chunk-HX3C6LDK.js.map +0 -1
  127. package/dist/chunk-JAKM53JF.js.map +0 -1
  128. package/dist/chunk-RKVUVALV.js.map +0 -1
  129. package/dist/chunk-TGJLXZAU.js.map +0 -1
  130. package/dist/chunk-UHAQ4PI6.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/do/durable-timer.ts","../../src/do/event-sourced-store.ts","../../src/do/resume.ts","../../src/do/sse.ts","../../src/do/agent-host.ts","../../src/do/presence.ts","../../src/do/command-socket.ts","../../src/do/deferred-gateway.ts","../../src/do/idempotent-effect.ts","../../src/do/projection.ts","../../src/do/step-host.ts","../../src/do/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * `@demlik/tea/do` — the durable-timer / alarm-carrier port (#180).\n *\n * Every native-DO grain that wants a wall-clock tick hand-rolls the SAME\n * eviction-safe pattern: compute the next absolute deadline off the live state,\n * push it to `ctx.storage.setAlarm`, and — when the DO `alarm()` lifecycle hook\n * fires — map that fire back to a Msg, then re-arm off the post-fire state. The\n * `raft/do.ts` election/heartbeat deadline does it (`rearmAlarm` + `onAlarm`),\n * and the vortex arena tick (`room.ts` `armTick` + `onTick`) replicates the\n * exact shape for the 18 Hz game clock. The two old `do_alarm`/`do_ws` Subs were\n * dropped (see `./index`'s historical note) because `toMachine` fixes the agent\n * machine's Sub type to `DeadlinesSub`, so they could never union into a grain's\n * Sub set — leaving the alarm re-arm a HOST concern with no first-class home.\n *\n * {@link durableTimer} is that home: a tiny, transport- and Msg-agnostic helper\n * that owns the three repeated responsibilities and nothing else —\n *\n * 1. **Compute-next-deadline → `setAlarm`.** {@link DurableTimer.rearm} calls\n * the injected {@link DurableTimerConfig.nextDeadline}, and arms the alarm\n * at the returned absolute epoch-ms target.\n * 2. **Idle-when-no-work.** `nextDeadline` returns `null` when there is nothing\n * to schedule (an empty room, a node with no pending election/heartbeat),\n * and `rearm` then arms NOTHING — the DO is allowed to go idle / hibernate\n * instead of spinning the alarm forever. Re-arming is a positive act gated\n * on real work, never an unconditional reschedule.\n * 3. **Fire → Msg → re-arm.** {@link DurableTimer.onAlarm} is the body the DO\n * `alarm()` hook calls: it runs the injected {@link DurableTimerConfig.onFire}\n * (the consumer dispatches its timer Msg there — a `Tick`, a re-derived\n * deadline Msg) and THEN re-arms off the now-advanced state. Re-arm always\n * follows the fire, so a still-active grain keeps ticking and an idle one\n * stops by construction.\n *\n * **Eviction-safe across hibernation.** A DO alarm is the one timer that\n * survives eviction (an isolate `setInterval` dies the moment the isolate\n * sleeps), so the same `durableTimer` re-armed on a cold wake resumes the\n * schedule that a never-evicted instance would have held. The recovery contract\n * is just: after the host rebuilds state (`store.load()` → `replay`), call\n * {@link DurableTimer.rearm} once — it recomputes `nextDeadline` off the\n * recovered state and re-arms (or idles) identically to the live path. The timer\n * holds no in-memory schedule of its own; the deadline is always a pure function\n * of the persisted state, which is what makes the cold-wake re-arm correct.\n *\n * **Dependency-injected / testable.** The alarm arrives as a {@link AlarmStorage}\n * port — the SAME minimal `setAlarm` slice `stepHost` already takes (a real\n * `DurableObjectStorage` satisfies it structurally; a test supplies a fake with\n * no live Workers runtime), mirroring how `event-sourced-store.ts` takes its\n * storage. `nextDeadline` and `onFire` are injected thunks, so the whole timer\n * unit-tests with a faked alarm + a faked clock and no `cloudflare:workers`\n * import. See `.patterns/tea-do/recovery.md` for the cold-wake replay contract\n * the re-arm rides on.\n */\n\nimport type { AlarmStorage } from \"./step-host\";\n\n// Re-export the alarm port under this module too, so a `durableTimer` consumer\n// can name the carrier it injects from the same import as the timer itself. It\n// stays the ONE `setAlarm` slice the `/do` surface defines (no parallel type).\nexport type { AlarmStorage };\n\n/**\n * The construction inputs for {@link durableTimer} — the impure edges the grain\n * injects, kept out of the pure reducer exactly like raft/do's\n * `RaftGrainCtx`/room's `ArenaPorts`.\n */\nexport interface DurableTimerConfig {\n /**\n * The DO alarm carrier. `setAlarm(atMs)` schedules the next fire at an\n * absolute epoch-ms target. A real `DurableObjectStorage` satisfies this\n * structurally; a test supplies a fake. Only `setAlarm` is read — never the\n * whole `DurableObjectStorage`.\n */\n readonly alarm: AlarmStorage;\n /**\n * Compute the next absolute deadline (epoch ms) off the live state, or `null`\n * when there is no work to schedule. Returning `null` is the idle signal:\n * {@link DurableTimer.rearm} arms nothing and the DO is free to hibernate.\n *\n * Called fresh on every `rearm` (including the post-fire re-arm and the\n * cold-wake re-arm), so it MUST read live state / the injected clock each call\n * — that freshness is what keeps the schedule a pure function of the persisted\n * state and the re-arm eviction-safe. Examples:\n * - raft: `node.subs(state, now())[0]?.atMs ?? null` (election XOR heartbeat).\n * - vortex: `connectionCount() > 0 ? now() + TICK_INTERVAL_MS : null`.\n */\n nextDeadline(): number | null | Promise<number | null>;\n /**\n * Run the timer's effect when the alarm fires — the consumer dispatches its\n * timer Msg here (a `Tick`, a re-derived deadline Msg) and persists/broadcasts\n * as its grain requires. {@link DurableTimer.onAlarm} awaits this BEFORE it\n * re-arms, so the re-arm sees the post-fire state.\n */\n onFire(): void | Promise<void>;\n}\n\n/**\n * The activated durable timer. Two methods, both idempotent to call from the\n * host edge:\n * - {@link DurableTimer.rearm} — recompute + (re-)arm or idle. Call it once\n * after cold-wake recovery, and after any state transition that can change\n * whether/when the next deadline should fire.\n * - {@link DurableTimer.onAlarm} — the DO `alarm()` body: fire, then re-arm.\n */\nexport interface DurableTimer {\n /**\n * Recompute {@link DurableTimerConfig.nextDeadline} off the live state and arm\n * the alarm at it — or, when it returns `null`, arm nothing (idle). Safe to\n * call repeatedly; arming the same absolute target twice is a harmless\n * overwrite on the DO alarm API.\n */\n rearm(): Promise<void>;\n /**\n * The body the DO `alarm()` lifecycle hook delegates to: run\n * {@link DurableTimerConfig.onFire}, then {@link DurableTimer.rearm} off the\n * post-fire state. A still-active grain keeps ticking; an idle one (its\n * `nextDeadline` now `null`) stops here.\n */\n onAlarm(): Promise<void>;\n}\n\n/**\n * Build a {@link DurableTimer} over an injected alarm carrier, a next-deadline\n * computation, and a fire handler. The returned timer owns the\n * compute-next-deadline → `setAlarm` → `alarm()` → fire → re-arm loop and holds\n * no schedule of its own (the deadline is always recomputed from live state),\n * which is what makes it eviction-safe across hibernation.\n */\nexport function durableTimer(config: DurableTimerConfig): DurableTimer {\n const { alarm, nextDeadline, onFire } = config;\n const timer: DurableTimer = {\n async rearm() {\n // Re-arm is a POSITIVE act gated on work: a `null` deadline means idle, so\n // we touch the alarm only when there is a concrete target to schedule.\n const at = await nextDeadline();\n if (at !== null) await alarm.setAlarm(at);\n },\n async onAlarm() {\n // Fire first (the consumer dispatches its timer Msg), THEN re-arm off the\n // now-advanced state — so a still-active grain keeps ticking and an idle\n // one stops because its `nextDeadline` has gone `null`.\n await onFire();\n await timer.rearm();\n },\n };\n return timer;\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @demlik/tea/do — event-sourced persistence mode for a Durable Object actor.\n *\n * `doStore` (sibling, `./index`) is the DEFAULT and stays snapshot-only: it\n * writes the whole `S` blob to one cell on every transition and reads it back\n * on boot. That is byte-for-byte unchanged by this file.\n *\n * `doEventSourcedStore` is the OPT-IN alternative. Instead of (only)\n * checkpointing the latest state, it:\n *\n * 1. APPENDS each applied `Msg` to an append-only log in `DurableObjectStorage`\n * (one cell per event, monotonically keyed).\n * 2. Takes a PERIODIC SNAPSHOT every `snapshotEvery` events (count-based\n * retention — mirrors Akka `RetentionCriteria.snapshotEvery`) so the log\n * that must replay on the next activation is bounded. A grain that needs\n * replay bounded by ELAPSED TIME/TICKS rather than raw event count drives\n * `snapshotNow()` instead (or alongside) — see that method.\n * 3. On activation REBUILDS state by FOLDING the log on top of the latest\n * snapshot. The fold is the substrate's own `replay` — `init(snapshot)`\n * then one reducer step per logged Msg. `replay` returns Cmds as data and\n * never interprets them, so **side effects are suppressed on replay**\n * (recovery.md). This is the same primitive `foldEvents` / the recorder\n * use; it is reused here, not reinvented (ADR 0003: \"the fold already\n * exists; wire it to Store\").\n *\n * The \"recovered/ready\" signal (recovery.md `RecoveryCompleted`) is delivered\n * exactly once per activation via `onReady`, EVEN for a fresh actor with an\n * empty log, and is documented to be idempotent (it may fire again on a later\n * activation, so the handler must tolerate repeats).\n *\n * Integration with the cooperating DO:\n *\n * const es = doEventSourcedStore(storage, machine, ctx, {\n * snapshotEvery: 100,\n * parse, // optional boundary parse for the snapshot\n * onReady: (state) => { ... }, // optional, idempotent end-of-recovery hook\n * });\n * const runtime = run(machine, { ctx, store: es.store });\n * // Append every applied Msg to the log. `observe` delivers only applied\n * // Msgs now (#47) — boot has no event, so there is no `null` arm to skip.\n * runtime.observe((msg) => { es.append(msg); });\n * await runtime.ready; // store.load() has folded; onReady has fired once.\n *\n * A time/tick-driven grain (e.g. one that derives simulation ticks between\n * sparse intents) bounds its cold-wake replay by ELAPSED TIME rather than event\n * count by calling `es.snapshotNow()` on its own cadence — every N derived ticks\n * or on a wall-clock interval — so a quiet span that never trips `snapshotEvery`\n * is still snapshotted before its replay tail grows unbounded (#190):\n *\n * // in the grain's tick loop, after the turn's appends have settled:\n * if (tick % SNAPSHOT_EVERY_TICKS === 0) await es.snapshotNow();\n */\n\nimport type { Cmd, Machine, Store, Sub } from \"../index\";\nimport { replay } from \"../index\";\n\n/** Default cell prefixes. Chosen to sort log keys lexicographically by seq. */\nconst DEFAULT_SNAPSHOT_KEY = \"@@es/snapshot\";\nconst DEFAULT_META_KEY = \"@@es/meta\";\nconst DEFAULT_EVENT_PREFIX = \"@@es/evt/\";\n/** Zero-pad seq numbers so `storage.list({ prefix })` returns them in order. */\nconst SEQ_WIDTH = 16;\n\nfunction eventKey(prefix: string, seq: number): string {\n return prefix + String(seq).padStart(SEQ_WIDTH, \"0\");\n}\n\n/**\n * Persisted snapshot envelope. `seq` is the highest event sequence number the\n * snapshot's state already folds in — events strictly after it are the replay\n * tail. `state` is the folded `S` at that point.\n */\ninterface SnapshotCell<S> {\n readonly seq: number;\n readonly state: S;\n}\n\n/** Persisted meta cell: the highest sequence number ever appended. */\ninterface MetaCell {\n readonly lastSeq: number;\n}\n\n/**\n * Options for {@link doEventSourcedStore}.\n */\nexport interface EventSourcedOptions<S, M> {\n /**\n * Take a snapshot every N appended events (count-based retention, mirrors\n * Akka `RetentionCriteria.snapshotEvery`). A snapshot bounds how many events\n * replay on the next activation — it never changes the fold's result, only\n * its length (recovery.md). Must be >= 1. Default 100.\n */\n readonly snapshotEvery?: number;\n /**\n * Boundary parse for the persisted SNAPSHOT cell (the `S` blob). Returns `S`\n * on a recognized shape, `null` on an unrecognized one, and must NOT throw.\n * Unlike `doStore`'s `parse`, `null` loses nothing: the snapshot is only a\n * shortcut, so a `null` discards it and replays the whole event log from\n * seq 0. Default accepts any non-null, non-array object as `S`.\n */\n readonly parse?: (raw: unknown) => S | null;\n /**\n * Boundary parse for a persisted EVENT (one logged `Msg`). Returns the `M` on\n * a recognized shape; returning `null` DROPS the event from replay (use for a\n * removed Msg variant). Must NOT throw. Default accepts any object carrying a\n * string `type` as `M`.\n */\n readonly parseEvent?: (raw: unknown) => M | null;\n /**\n * End-of-recovery hook (recovery.md `RecoveryCompleted`). Fires EXACTLY ONCE\n * per activation, after the fold completes, carrying the recovered state —\n * even when the log was empty (fresh actor). It may fire again on a later\n * activation, so it MUST be idempotent. Side effects that belong \"once after\n * recovery\" go here, never in the reducer (the reducer re-runs on every\n * replay).\n */\n readonly onReady?: (state: S) => void;\n /** Cell-key overrides (rarely needed; for coexisting actors in one DO). */\n readonly keys?: {\n readonly snapshot?: string;\n readonly meta?: string;\n readonly eventPrefix?: string;\n };\n}\n\n/**\n * One persisted log entry handed to a {@link EventSourcedStore.readEvents}\n * consumer: the monotonic `seq` the event was appended under, paired with the\n * decoded `event`. `seq` is the store's own append counter (the key the event\n * lives under), so it both orders the stream and bounds an {@link EventLogRange}.\n */\nexport interface PersistedEvent<M> {\n /** The append sequence number this event was logged under (1-based, gap-free). */\n readonly seq: number;\n /** The decoded `Msg`, parsed through the store's `parseEvent` boundary. */\n readonly event: M;\n}\n\n/**\n * Optional inclusive bounds for {@link EventSourcedStore.readEvents}. Both ends\n * are inclusive and match against the event's `seq`; omitting an end leaves it\n * unbounded. `{ fromSeq: 5, toSeq: 10 }` yields seqs 5..10; `{}` (or no\n * argument) yields the whole log.\n */\nexport interface EventLogRange {\n /** Lowest `seq` to include (inclusive). Default: the first event. */\n readonly fromSeq?: number;\n /** Highest `seq` to include (inclusive). Default: the last event. */\n readonly toSeq?: number;\n}\n\n/**\n * The handle returned by {@link doEventSourcedStore}: a `Store<S>` to hand to\n * `run(...)`, plus the append + recovery surface the cooperating DO drives.\n */\nexport interface EventSourcedStore<S, M> {\n /**\n * The `Store<S>` to pass as `run(machine, { ctx, store })`. Its `load()`\n * performs the fold (snapshot + log replay); its `save(state)` is the\n * count-based snapshot writer. `migrate` forwards to `parse`.\n */\n readonly store: Store<S>;\n /**\n * Append one applied `Msg` to the log. Call from `runtime.observe` for every\n * non-null msg. Resolves once the event is durably written. Triggers a\n * snapshot when the event count crosses a `snapshotEvery` boundary.\n */\n append(msg: M): Promise<void>;\n /**\n * Take a snapshot NOW, independent of the count-based `snapshotEvery` trigger\n * (a time/tick-based retention trigger, #190). The count-based trigger bounds\n * the replay tail by raw EVENT COUNT; a grain that derives many state\n * transitions per logged event (e.g. simulation ticks between sparse intents)\n * needs the tail bounded by ELAPSED TIME instead — so it calls `snapshotNow()`\n * on its own cadence (every N derived ticks, or on a wall-clock interval) to\n * checkpoint the current folded state without injecting filler events purely\n * to advance the counter.\n *\n * It checkpoints the latest state the substrate handed `save()` at the current\n * highest sequence, then advances the count-based baseline (`snapshotEvery`\n * counts afresh from here, so a manual snapshot defers the next automatic one).\n *\n * Returns `true` if a snapshot was written, `false` on a no-op: nothing has\n * been applied yet (fresh actor), or no new event exists since the last\n * snapshot (already current). Like the count-based path, it pairs the held\n * state with the highest sequence, so the caller MUST invoke it between\n * settled transitions — after the turn's `append()`s have resolved — never\n * mid-transition (the same boundary discipline `append`'s own snapshot relies\n * on). Resolves once the snapshot is durably written.\n */\n snapshotNow(): Promise<boolean>;\n /**\n * Stream the persisted event log in `seq` order, optionally bounded by an\n * inclusive {@link EventLogRange}. This is the PUBLIC read side of the\n * event-sourced write model — the surface replay / projection / audit\n * consumers (CQRS reads, recovery.md / projections.md) read through, instead\n * of mirroring the store's private `@@es/evt/` key convention.\n *\n * The log is APPEND-ONLY and is never truncated by snapshotting (a snapshot\n * only bounds the *replay* tail, never the log), so this yields EVERY event\n * in range — including ones a snapshot already folds. Each entry carries its\n * `seq`, so a consumer can resume from a known offset (`{ fromSeq }`) or read\n * a window (`{ fromSeq, toSeq }`). An empty (or fully out-of-range) log yields\n * nothing. Events whose `parseEvent` returns `null` (a retired Msg variant)\n * are dropped, identical to the recovery fold.\n *\n * Read-only: it never appends, snapshots, or mutates store state, so streaming\n * the log can never perturb the live actor.\n */\n readEvents(range?: EventLogRange): AsyncIterable<PersistedEvent<M>>;\n}\n\nconst defaultParse = <S>(raw: unknown): S | null =>\n raw !== null && typeof raw === \"object\" && !Array.isArray(raw)\n ? (raw as S)\n : null;\n\nconst defaultParseEvent = <M>(raw: unknown): M | null =>\n raw !== null &&\n typeof raw === \"object\" &&\n !Array.isArray(raw) &&\n typeof (raw as { type?: unknown }).type === \"string\"\n ? (raw as M)\n : null;\n\n/**\n * Build an event-sourced `Store<S>` over `DurableObjectStorage`.\n *\n * The substrate calls `store.load()` once at boot — that is where the fold\n * happens — and `store.save(state)` after every transition — that is where the\n * count-based snapshot is taken. The cooperating DO additionally calls\n * `append(msg)` from `runtime.observe` to grow the log.\n *\n * `machine` and `ctx` are required because the fold is `replay(machine, {\n * loaded: snapshot, msgs: log, ctx })` — the same pure reducer the live runtime\n * uses, so the rebuilt state is identical to the never-evicted state by\n * construction. `ctx` is only used to satisfy `init`'s signature during the\n * fold; an event-sourced machine's `init` rehydrate branch is a pure\n * passthrough (TEA invariant 2), so the fold touches nothing in `ctx`.\n */\n// Cmd/Sub are INFERRED type params (`C`/`U`) that DEFAULT to the substrate's\n// base unions, not pinned to them. The fold itself only ever touches `init` +\n// `update`, so the body never depends on the concrete C/U — but pinning the\n// machine param to the base `Cmd` made `interpret` *structurally required* via\n// the `Machine` conditional (`[C] extends [Cmd<never>] ? { interpret? } : {\n// interpret }`): a CMDLESS grain (`Cmd = never`, which legitimately declares no\n// `interpret`) then could not satisfy the param without a cast (#195). Threading\n// `C`/`U` lets TS infer `C = never` for such a grain, relaxing `interpret` to\n// optional exactly as `run`/`replay` already do for a `Cmd = never` machine — so\n// a cmdless machine type-checks with no cast and no no-op `interpret`. The\n// defaults keep inference byte-for-byte identical for a commandful machine (and\n// for the existing `<S, M, Ctx>`-explicit callers, whose C/U fall to the base\n// defaults exactly as before); the machine keeps its precise types at its own\n// definition site.\nexport function doEventSourcedStore<\n S,\n M extends { type: string },\n Ctx,\n C extends Cmd = Cmd,\n U extends Sub = Sub,\n>(\n storage: DurableObjectStorage,\n machine: Machine<S, M, C, U, Ctx>,\n ctx: Ctx,\n opts: EventSourcedOptions<S, M> = {},\n): EventSourcedStore<S, M> {\n const snapshotEvery = opts.snapshotEvery ?? 100;\n if (snapshotEvery < 1 || !Number.isInteger(snapshotEvery)) {\n throw new Error(\n `@demlik/tea/do: snapshotEvery must be an integer >= 1, got ${snapshotEvery}`,\n );\n }\n const parse = opts.parse ?? defaultParse;\n const parseEvent = opts.parseEvent ?? defaultParseEvent;\n const snapshotKey = opts.keys?.snapshot ?? DEFAULT_SNAPSHOT_KEY;\n const metaKey = opts.keys?.meta ?? DEFAULT_META_KEY;\n const eventPrefix = opts.keys?.eventPrefix ?? DEFAULT_EVENT_PREFIX;\n\n // In-memory mirror of the durable seq counter, set during load() and kept in\n // step by append(). Before load() it is unknown; append() lazily reads it.\n let lastSeq: number | null = null;\n // The seq the latest snapshot already folds (so we know the replay tail).\n let snapshotSeq = 0;\n // The latest state the substrate handed us via save(). The substrate calls\n // save(state) BEFORE the observer fires append(msg) for the same transition\n // (run()'s order: state=next → save → reconcile → interpret → observers), so\n // at append(N) time this holds the post-event-N state. The snapshot is taken\n // from the append path (not save) so its recorded `seq` and its `state` agree\n // on the same event boundary — recording seq at save time would lag the state\n // by one event and re-apply that event on recovery.\n let latestState: S | null = null;\n // Idempotency latch for onReady within a single activation.\n let readyFired = false;\n\n async function readMeta(): Promise<number> {\n const raw = await storage.get<string>(metaKey);\n if (raw === undefined || raw === null) return 0;\n const meta = JSON.parse(raw) as MetaCell;\n return typeof meta.lastSeq === \"number\" ? meta.lastSeq : 0;\n }\n\n /**\n * Return the durable `lastSeq`, lazily learning it from meta if a write\n * (`append`/`snapshotNow`) fires before any `load()` in this isolate\n * (defensive; the documented flow loads first). The guard lives here once so\n * `append` and `snapshotNow` can't drift on the append-before-load path.\n */\n async function ensureLastSeq(): Promise<number> {\n if (lastSeq === null) lastSeq = await readMeta();\n return lastSeq;\n }\n\n /**\n * Read the persisted snapshot cell, parsed through the boundary. Returns the\n * folded state + the seq it covers, or `{ state: null, seq: 0 }` when absent\n * or unrecognized (fold then starts from a fresh `init`).\n */\n async function readSnapshot(): Promise<{ state: S | null; seq: number }> {\n const raw = await storage.get<string>(snapshotKey);\n if (raw === undefined || raw === null) return { state: null, seq: 0 };\n // Structural JSON malformation throws here, as in doStore — infra error,\n // not a schema mismatch.\n const cell = JSON.parse(raw) as SnapshotCell<unknown>;\n const parsed = parse(cell.state);\n if (parsed === null) return { state: null, seq: 0 };\n const seq = typeof cell.seq === \"number\" ? cell.seq : 0;\n return { state: parsed, seq };\n }\n\n /**\n * Stream the persisted event log in `seq` order, bounded by the optional\n * inclusive `[fromSeq, toSeq]` range. The single owner of the `@@es/evt/`\n * key convention on the read side — both the public {@link EventSourcedStore.readEvents}\n * and the private fold's `readLog` flow through it, so the prefix/decode rule\n * lives in exactly one place.\n */\n async function* readEvents(\n range: EventLogRange = {},\n ): AsyncGenerator<PersistedEvent<M>> {\n const fromSeq = range.fromSeq ?? Number.NEGATIVE_INFINITY;\n const toSeq = range.toSeq ?? Number.POSITIVE_INFINITY;\n // list() returns keys in lexicographic order; zero-padded seq keys sort by\n // sequence, so this is already seq-ascending. Bounds are matched on the\n // parsed seq (inclusive both ends).\n const rows = await storage.list<string>({ prefix: eventPrefix });\n for (const [key, value] of rows) {\n const seq = Number(key.slice(eventPrefix.length));\n if (!Number.isFinite(seq) || seq < fromSeq || seq > toSeq) continue;\n const decoded = parseEvent(JSON.parse(value));\n // A `null` decode drops a retired Msg variant (documented in parseEvent).\n if (decoded !== null) yield { seq, event: decoded };\n }\n }\n\n /** Read the logged events strictly after `afterSeq`, in seq order. */\n async function readLog(afterSeq: number): Promise<M[]> {\n // The replay tail is the public reader bounded to seqs > afterSeq — one\n // shared source for the key convention, no second `storage.list` walk.\n const events: M[] = [];\n for await (const { event } of readEvents({ fromSeq: afterSeq + 1 })) {\n events.push(event);\n }\n return events;\n }\n\n const store: Store<S> = {\n async load(): Promise<unknown> {\n const { state: snapshot, seq: snapSeq } = await readSnapshot();\n snapshotSeq = snapSeq;\n lastSeq = await readMeta();\n\n const events = await readLog(snapSeq);\n\n // THE FOLD. `replay` boots `init(snapshot, ctx)` then applies one reducer\n // step per event. It returns Cmds as data and never interprets them, so\n // no side effect fires on replay (recovery.md). The result is identical\n // to the live runtime's state for the same (snapshot, events) — same\n // reducer, same order.\n const { state } = replay(machine, {\n msgs: events,\n ctx,\n loaded: snapshot,\n });\n\n // RecoveryCompleted: fire once per activation, AFTER the fold, even when\n // the log + snapshot were both empty (fresh actor). Idempotent within the\n // activation via `readyFired`; may fire again on a future activation.\n if (!readyFired) {\n readyFired = true;\n opts.onReady?.(state);\n }\n\n // Returned as `unknown` and re-validated through `migrate` (= parse), same\n // boundary discipline as doStore. The substrate feeds it to `init` as\n // `loaded`, so the live `init` rehydrate branch runs on the folded state.\n return state;\n },\n\n async save(state: S): Promise<void> {\n // The substrate calls save() after every transition, BEFORE the observer\n // fires append() for that same transition. We don't snapshot here — we\n // only hold the latest state so the append path can pair it with the\n // freshly-advanced seq (see `latestState`). Holding (not snapshotting)\n // here is what keeps the snapshot's seq and state on the same boundary.\n latestState = state;\n },\n\n migrate(raw: unknown): S | null {\n return parse(raw);\n },\n };\n\n /**\n * Durably write the snapshot cell pairing `seq` with `state`, then advance the\n * in-memory `snapshotSeq` so the recovery fold replays only events strictly\n * after `seq`. Shared by the count-based path (in `append`) and the\n * time/tick-based path (`snapshotNow`) so both record the boundary the one,\n * identical way — a snapshot bounds replay length; it never changes the fold's\n * result (snapshotting.md).\n */\n async function writeSnapshot(seq: number, state: S): Promise<void> {\n const cell: SnapshotCell<S> = { seq, state };\n await storage.put(snapshotKey, JSON.stringify(cell));\n snapshotSeq = seq;\n }\n\n async function append(msg: M): Promise<void> {\n const seq = (await ensureLastSeq()) + 1;\n lastSeq = seq;\n // Write the event then advance the meta cell. DO storage is transactional\n // within a single `alarm`/fetch turn; both puts land together.\n await storage.put(eventKey(eventPrefix, seq), JSON.stringify(msg));\n await storage.put(\n metaKey,\n JSON.stringify({ lastSeq: seq } satisfies MetaCell),\n );\n\n // Count-based retention (Akka `RetentionCriteria.snapshotEvery`). When the\n // events since the last snapshot reach `snapshotEvery`, checkpoint the\n // post-this-event state under THIS seq. `latestState` was set by the\n // save(state) that ran earlier in this same transition, so its state folds\n // exactly events 1..seq — recording `seq` here is consistent, and the\n // recovery fold replays only events strictly after `seq`. A snapshot bounds\n // replay length; it never changes the fold's result (snapshotting.md).\n if (seq - snapshotSeq >= snapshotEvery && latestState !== null) {\n await writeSnapshot(seq, latestState);\n }\n }\n\n /**\n * Time/tick-based snapshot trigger (#190) — checkpoint NOW, independent of the\n * count-based `snapshotEvery`. See {@link EventSourcedStore.snapshotNow}.\n */\n async function snapshotNow(): Promise<boolean> {\n const seq = await ensureLastSeq();\n // No-op when there is nothing new to checkpoint: a fresh actor with no\n // applied state yet (`latestState === null`), or no event appended since the\n // last snapshot (`seq <= snapshotSeq`). Both keep the snapshot's `seq`\n // and `state` paired on the same boundary the count-based path guarantees.\n if (latestState === null || seq <= snapshotSeq) return false;\n await writeSnapshot(seq, latestState);\n return true;\n }\n\n return { store, append, snapshotNow, readEvents };\n}\n","/**\n * Cold-wake resume — the boot/hibernation re-fire path: the generalized\n * `bootResume` port, its agent specialization `autoBoot`, and the wake-time\n * re-emit of surviving tool round-trips. See `./host` for the transport-model\n * rationale.\n */\n\nimport {\n type AgentMachineMsg,\n type AgentState,\n agentBootMsg,\n status,\n} from \"../agent/index\";\nimport type { RunHandle } from \"../index\";\nimport type { DurableCommandCarrier } from \"./deferred-gateway\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// bootResume + agentIsResumable + autoBoot — cold-wake resume on rehydrate (seam B).\n//\n// A run that loaded from storage mid-loop must re-fire its one outstanding\n// effect. `bootResume` is the GENERALIZED helper (issue #231): after\n// `runtime.ready`, it derives a single resume Msg from the rehydrated State via\n// a caller-supplied `ResumePort` and dispatches it exactly once — a no-op on a\n// fresh DO. `autoBoot` is the AGENT specialization of it (the `agentIsResumable`\n// predicate + the `agentBootMsg` typed port, issue #60), so no divergent second\n// resume pattern is left standing.\n//\n// It leans on the init-purity contract (Invariant 2, enforced by `replay` in\n// `../index`): `init(loaded)` returns ZERO Cmds on rehydrate, so effects never\n// re-fire from boot — `bootResume`, NOT `init`, is the boot-effect hook. See\n// `.patterns/tea-do/recovery.md`.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The typed cold-wake resume port `bootResume` fires through — the agent's\n * `AgentBootPort`, generalized to any DO-hosted machine.\n *\n * - `isResumable(state)` — true iff the rehydrated State is mid-loop and needs a\n * resume dispatch. False on a fresh boot, which makes `bootResume` a no-op.\n * - `resumeMsg(now)` — the TYPED constructor for the single resume Msg (never a\n * hand-built `{ type: … }` literal); the `agentBootMsg` shape lifted out so a\n * non-agent grain supplies its own boot Msg constructor.\n */\nexport interface ResumePort<S, M extends { type: string }> {\n readonly isResumable: (state: S) => boolean;\n readonly resumeMsg: (now: number) => M;\n}\n\n/**\n * After `runtime.ready`, derive the single resume Msg from the rehydrated State\n * via `port` and dispatch it exactly once — the generalized AgentBoot. On a\n * fresh (non-rehydrated) machine `port.isResumable` is false and this is a\n * no-op. Call once right after building the runtime; it standardizes the\n * hand-written `if (isResumable(state)) await runtime.dispatch(bootMsg(now()))`\n * every DO-hosted machine otherwise re-derives.\n *\n * `await ready` is the single boot gate (issue #45): `getState()` is total only\n * on the booted handle, so state cannot be inspected before boot populates\n * it. Takes any engine's {@link RunHandle}. `now` is injected (the sole boot clock read) so a test can pin it.\n */\nexport async function bootResume<\n S,\n M extends { type: string },\n E extends { type: string } = never,\n>(\n booting: RunHandle<S, M, E>,\n port: ResumePort<S, M>,\n now: () => number = Date.now,\n): Promise<void> {\n const runtime = await booting.ready;\n if (port.isResumable(runtime.getState())) {\n await runtime.dispatch(port.resumeMsg(now()));\n }\n}\n\n/**\n * True iff an agent slice loaded from storage is mid-loop (running + awaiting\n * tools) — the resumable case `agent_boot` re-fires.\n *\n * The resumability question IS \"is the run suspended on tools?\" — so this\n * delegates to the agent's own typed `status` channel (issue #49) rather than\n * re-deriving the private `run.phase` / `conversation.awaiting` shape by hand.\n * The host no longer leaks the agent's internal slice structure; a change to\n * `Awaiting` surfaces as a compile error in `status`, not a silent break here.\n */\nexport function agentIsResumable<\n Stage,\n P extends string,\n O extends Record<P, unknown>,\n R,\n>(state: AgentState<Stage, P, O, R>): boolean {\n return status(state).kind === \"suspended\";\n}\n\n/**\n * The AGENT specialization of {@link bootResume}: after\n * `runtime.ready`, self-dispatch `agent_boot` iff the rehydrated slice is\n * resumable, a no-op on a fresh DO. Wires the agent slice's `agentIsResumable`\n * predicate + the agent-owned `agentBootMsg` typed port into the\n * generalized helper, so the resume dispatch lives in ONE place — see\n * `bootResume`'s docblock for the boot-gate / init-purity rationale.\n *\n * `now` is injected (the host's only clock read for boot) so a test can pin it.\n */\nexport async function autoBoot<\n Stage,\n P extends string,\n O extends Record<P, unknown>,\n R,\n E extends { type: string } = never,\n>(\n booting: RunHandle<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R>, E>,\n now: () => number = Date.now,\n): Promise<void> {\n await bootResume(\n booting,\n { isResumable: agentIsResumable, resumeMsg: agentBootMsg },\n now,\n );\n}\n\n/**\n * Re-fire every round-trip a rehydrated actor still owes, on wake. Given a\n * durable carrier whose recorder was rehydrated from the persisted ledger\n * events (e.g.\n * `durableCommandCarrier(deferredGateway(), pendingEffectsLedger({ events }),\n * …)` on a wake), re-fire every owed-but-unconfirmed round-trip via\n * `reissue(callId)` — the consumer's \"re-send the command frame for this\n * `callId`\" callback. The survivors are read straight off the carrier's own\n * recorder (`recorder.surviving()`), which folded the restore log at\n * construction — the carrier is the single source of the in-flight set, so the\n * caller does not re-fold the log here.\n *\n * Idempotent by construction:\n * - the ledger's monotonic `deliveryId` is the dedup key the RECEIVER uses, so\n * a late/duplicate reply for an already-resolved round-trip is absorbed\n * (at-least-once delivery — see `survivingEffects`);\n * - re-issuing the SAME survivor twice within one activation re-sends once —\n * if `reissue` routes through the carrier's `await`, the gateway returns the\n * open Promise for an in-flight `callId` without re-firing `send`.\n *\n * Returns the `callId`s re-emitted (oldest-first by id) — the host can log /\n * assert the re-emit set. On a fresh or fully-confirmed carrier this is empty\n * (nothing re-fired).\n *\n * Call this once, right after the runtime's boot gate resolves (alongside\n * {@link autoBoot}): `autoBoot` re-fires the agent's own `agent_boot`;\n * `reissueSurvivingEffects` re-fires the in-flight TOOL round-trips. Together\n * they restore a suspended run's full in-flight state after a wake.\n */\nexport function reissueSurvivingEffects<R>(\n carrier: DurableCommandCarrier<R>,\n reissue: (callId: string) => void,\n): readonly string[] {\n const reemitted: string[] = [];\n for (const { effect } of carrier.recorder.surviving()) {\n reissue(effect.callId);\n reemitted.push(effect.callId);\n }\n return reemitted;\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * The runtime→SSE plumbing — the hub, its projection expression, and the\n * semantic-event adapter. See `./host` for the transport-model rationale.\n */\n\nimport { AGENT_EVENT_TYPES, type AgentEvent } from \"../agent/index\";\nimport type { Projection } from \"./projection\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// sseStream — runtime→SSE adapter (seam D).\n//\n// The sink-set + ReadableStream + encoder + cancel is generic; only the\n// Msg→event mapping is domain. Sibling-in-spirit to `@demlik/tea/react`: the\n// host owns the plumbing, the consumer supplies the mapping.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A set of SSE sinks plus the plumbing to fan an event out to all of them and\n * to open a `text/event-stream` Response wired to a fresh sink.\n *\n * The consumer:\n * 1. holds one `SseHub<E>` per DO,\n * 2. calls `hub.emit(event)` from its Msg→event mapping (driven off\n * `runtime.observe`), and\n * 3. returns `hub.open()` from the `/sse` route.\n *\n * `register(sink)` exists for tests: subscribe a sink directly and observe the\n * exact event sequence a live client would receive (a live `text/event-stream`\n * fights vitest-pool-workers teardown). Deletes the consumer's `sseSinks` set,\n * `openSse`, `emitSse`, and `onSseEvent`.\n */\nexport interface SseHub<E> {\n emit(event: E): void;\n open(): Response;\n register(sink: (event: E) => void): () => void;\n}\n\n/** Build an SSE hub for event type `E` (the consumer's domain event shape). */\nexport function sseHub<E>(): SseHub<E> {\n const sinks = new Set<(event: E) => void>();\n const encoder = new TextEncoder();\n\n return {\n emit(event) {\n for (const sink of sinks) {\n try {\n sink(event);\n } catch {\n /* broken stream — dropped on its cancel */\n }\n }\n },\n open() {\n let sink!: (event: E) => void;\n const stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n sink = (event) => {\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(event)}\\n\\n`),\n );\n };\n sinks.add(sink);\n },\n cancel: () => {\n sinks.delete(sink);\n },\n });\n return new Response(stream, {\n headers: {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n },\n });\n },\n register(sink) {\n sinks.add(sink);\n return () => {\n sinks.delete(sink);\n };\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// sseProjection — express `sseHub` as ONE projection over the seam (#69).\n//\n// `sseHub` (above) is the ad-hoc form: a sink-set the consumer fans\n// `runtime.observe` events out to by hand. `sseProjection` names it: the SSE\n// view is ONE projection (`name: \"sse\"`) over the `Projection<Model, View>`\n// seam in `./projection`. Its `apply` maps the write-model update to the\n// consumer's SSE event shape; its `emit` is `hub.emit` — the SAME hub, the SAME\n// public API. Register it on a `projectionRegistry` (driven off `observe`) and\n// the host's runtime→SSE plumbing is one projection among many, not a special\n// case.\n//\n// ADDITIVE, not a replacement: `sseHub()` keeps working untouched for every\n// existing caller (it is the volatile, offset-free case — a live `/sse` stream\n// has no durable read model to resume). `sseProjection` is the seam expression\n// of it for a consumer that wants SSE to sit on the same registry as a durable\n// report/storage projection.\n//\n// The SSE view is the most-recently-emitted event (a \"latest\" read model); the\n// fold ignores the previous view and re-derives from each update via\n// `toEvent`. `toEvent` returns `null` to SKIP an update the SSE view does not\n// care about (the canon's `Future.successful(Done)` skip) — the fold then keeps\n// the prior view and does not re-emit.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Express an {@link SseHub} as a {@link Projection}. The projection's view is\n * the latest emitted SSE event (or `null` before the first); `apply` maps each\n * write-model update to an event via `toEvent` and emits it through `hub.emit`,\n * so the SSE stream is driven by the SAME hub `sseHub()` returns — no API\n * change for the `/sse` route (`hub.open()`) or any existing caller.\n *\n * - `toEvent(msg, model)` returns the SSE event for this update, or `null` to\n * skip it (the view stays, nothing is emitted).\n * - `name`/`key` default to `\"sse\"`/`\"main\"`; override for a second SSE view.\n *\n * @example\n * ```ts\n * import type { Runtime } from \"@demlik/tea\";\n * import {\n * driveProjections,\n * projectionRegistry,\n * sseHub,\n * sseProjection,\n * } from \"@demlik/tea/do\";\n *\n * type MyState = { readonly turns: number };\n * type MyMsg = { type: \"turn_done\"; turn: number } | { type: \"tick\" };\n * type MyEvent = { kind: \"turn\"; turn: number };\n * declare const runtime: Runtime<MyState, MyMsg>;\n *\n * const hub = sseHub<MyEvent>();\n * const registry = projectionRegistry<MyState, MyMsg>();\n * registry.register(sseProjection(hub, (msg: MyMsg | null) =>\n * msg?.type === \"turn_done\" ? { kind: \"turn\", turn: msg.turn } : null,\n * ));\n * driveProjections(registry, runtime); // SSE is now one projection\n * // /sse route unchanged:\n * const sseRoute = (): Response => hub.open();\n * ```\n */\nexport function sseProjection<Model, Msg extends { type: string }, E>(\n hub: SseHub<E>,\n toEvent: (msg: Msg | null, model: Model) => E | null,\n id: { name?: string; key?: string } = {},\n): Projection<Model, Msg, E | null> {\n // The SSE view is \"the latest event to push, or null when the update was a\n // skip\". `apply` derives it; `emit` pushes the non-null case at the hub. The\n // fold stays pure (no side effect in `apply`); the sink stays the hub (no API\n // change). A skip resolves to a `null` view → `emit` pushes nothing.\n return {\n id: { name: id.name ?? \"sse\", key: id.key ?? \"main\" },\n initial: null,\n apply(_view, update) {\n // Each SSE update re-derives from the write model — the view is the\n // latest event, not an accumulation, so the prior view is ignored.\n // Idempotent by construction: re-deriving the same update yields the same\n // event (the exclusive-offset guard already drops re-presented offsets).\n return toEvent(update.msg, update.model);\n },\n emit(view) {\n // The runner calls `emit` after every APPLIED update (offset strictly past\n // the stored one). `null` is the skip — push nothing. A real event is\n // pushed through the hub, so `hub.open()` / existing callers are unchanged.\n if (view !== null) hub.emit(view);\n },\n };\n}\n\n// captureLastTurn DELETED (issue #46). It existed only to reconstruct the run's\n// terminal output off the `observe` firehose — match the private `resilient_ok`\n// Msg, validate `result.output`, and snapshot it BEFORE `succeed` cleared the\n// conversation on stage retire. The agent now models termination first-class:\n// `state.output` holds the terminal turn on `done`, and `Runtime.result()` /\n// `Runtime.done()` read it. A consumer reads `runtime.result()?.output` — no\n// observer hook, no internal-Msg coupling, no state-clear race.\n\n// ─────────────────────────────────────────────────────────────────────────────\n// sseFromAgentEvents — drive an SseHub off the SEMANTIC AgentEvent stream.\n//\n// The pre-#47 host fanned SSE frames off `runtime.observe` by hand-matching the\n// agent's PRIVATE Msg names (`resilient_ok` / `agent_tool_ok`) — coupling the\n// transport to the retry/loop plumbing. This wires the hub to `runtime.on`\n// instead: the runtime (built with `events: agentEvents()`) projects the public\n// `AgentEvent` union, and this helper subscribes ONE `on(type, …)` per event\n// type and pushes `toFrame(event)` (or `null` to skip) at the hub. The SSE seam\n// now reads the named lifecycle events, never the firehose, never a private Msg.\n//\n// Built on `runtime.on` (the typed semantic channel) rather than `sseProjection`\n// because SSE is the volatile, offset-free case — a live `/sse` stream has no\n// durable read model to resume — so the projection-registry's offset machinery\n// buys nothing here. A consumer wanting SSE on the SAME registry as a durable\n// report projection still uses `sseProjection`; this is the lighter path that\n// expresses \"SSE = a projection of the semantic event stream\" directly.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Wire an {@link SseHub} to a runtime's semantic {@link AgentEvent} stream.\n * For each event `type` the runtime can emit, subscribes via `runtime.on` and\n * pushes `toFrame(event)` at the hub (returning `null` skips that event). The\n * runtime MUST have been built with `run(machine, { events: agentEvents() })`,\n * which is what makes `on` deliver the `AgentEvent`s. Returns a cleanup that\n * detaches every subscription.\n *\n * This is the runtime→SSE adapter expressed over the typed event channel: no\n * `observe` firehose, no private-Msg coupling. `hub.open()` and every existing\n * `sseHub` caller are unchanged — only the SOURCE of `hub.emit` moves from a\n * hand-rolled Msg switch to `on`.\n *\n * @example\n * ```ts\n * import type { Runtime } from \"@demlik/tea\";\n * import type { AgentEndedStatus, AgentEvent } from \"@demlik/tea/agent\";\n * import { sseFromAgentEvents, sseHub } from \"@demlik/tea/do\";\n *\n * type MyFrame = { kind: \"ended\"; status: AgentEndedStatus };\n * // Built with `run(machine, { ctx, events: agentEvents() }).ready`.\n * declare const runtime: Runtime<unknown, { type: string }, AgentEvent<string>>;\n *\n * const hub = sseHub<MyFrame>();\n * const off = sseFromAgentEvents(runtime, hub, (e) =>\n * e.type === \"RunDone\" ? { kind: \"ended\", status: e.status } : null,\n * );\n * // /sse route unchanged:\n * const sseRoute = (): Response => hub.open();\n * ```\n */\nexport function sseFromAgentEvents<R, Frame>(\n runtime: {\n on<K extends AgentEvent<R>[\"type\"]>(\n type: K,\n handler: (event: Extract<AgentEvent<R>, { type: K }>) => void,\n ): () => void;\n },\n hub: SseHub<Frame>,\n toFrame: (event: AgentEvent<R>) => Frame | null,\n): () => void {\n const push = (event: AgentEvent<R>): void => {\n const frame = toFrame(event);\n if (frame !== null) hub.emit(frame);\n };\n // One subscription per event type, off the one list that is checked against\n // the `AgentEvent` union, so a new variant reaches `toFrame` without an edit\n // here.\n const offs = AGENT_EVENT_TYPES.map((type) => runtime.on(type, push));\n return () => {\n for (const off of offs) off();\n };\n}\n","/**\n * `createAgentHost` — the runtime+events+autoBoot assembly + the test seam,\n * ONCE (issue #53). The facade over the concern modules: the deferred gateway\n * (`./deferred-gateway`), the WS carrier (`./command-socket`), the SSE plumbing\n * (`./sse`), and the cold-wake resume (`./resume`). See `./host` for the\n * transport-model rationale.\n */\n\nimport {\n type AgentEvent,\n type AgentMachineMsg,\n type AgentState,\n type AgentStatus,\n type AgentTurn,\n agentEvents,\n status,\n} from \"../agent/index\";\nimport type {\n BootedRunHandle,\n Cmd,\n CtxArg,\n EngineRun,\n RunHandle,\n Store,\n Sub,\n Wired,\n} from \"../index\";\nimport { autoBoot } from \"./resume\";\nimport { type SseHub, sseFromAgentEvents, sseHub } from \"./sse\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// createAgentHost — the runtime+events+autoBoot assembly + the test seam, ONCE\n// (issue #53).\n//\n// Every DO that drives a `createAgent` runtime hand-rolls the SAME ordered\n// wiring inside its `getRuntime()`: build the machine → `doStore` → `run(...)`\n// with `terminal` + `events: agentEvents()` → wire `sseFromAgentEvents` →\n// `autoBoot` → `await ready` → cache. And every one re-exposes the SAME\n// framework test surface as public methods (`isSuspended` / `runPhase` /\n// `verdict` / `settle`). The concern modules (`deferredGateway`, `autoBoot`,\n// `sseFromAgentEvents`, …) already lift each STEP; this lifts the ASSEMBLY.\n//\n// `createAgentHost(config)` owns:\n// - the lazy build-once-boot-once runtime cell (the `if (this.runtime) return`\n// dance + the `await booting.ready` gate),\n// - the `events: agentEvents()` wiring + `sseFromAgentEvents(runtime, hub, …)`\n// so SSE is driven off the SEMANTIC `AgentEvent` stream, never the private\n// Msg firehose,\n// - the `autoBoot` re-fire on a rehydrated suspended run,\n// - the SSE hub (so `host.sse.open()` / `host.sse.register(...)` is the route +\n// the test seam), and\n// - the framework test/lifecycle seam ONCE: `status()`, `result()`, `reset()`.\n//\n// The consumer supplies ONLY its mappings: the engine's `run`, how to build the\n// machine, the Store, the Ctx, the terminal predicate, and the `AgentEvent → SSE frame` projection.\n// Its `getRuntime()` collapses to `host.runtime()`; its `isSuspended` /\n// `runPhase` / `verdict` collapse to reads off `host.status()` / `host.result()`.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * What the consumer supplies to {@link createAgentHost} — the domain mappings,\n * nothing of the wiring. `Stage`/`P`/`O`/`R` are the agent slice's type\n * parameters (the consumer names them once); `Frame` is its SSE event shape.\n * `C`/`U`/`Ctx` are the machine's own Cmd/Sub/Ctx — inferred from\n * `buildMachine` at the `createAgentHost` call site (never name them by hand;\n * an erased `any` here cost consumers all checking on this layer — #278).\n */\nexport interface AgentHostConfig<\n Stage,\n P extends string,\n O extends Record<P, AgentTurn>,\n R,\n Frame,\n C extends Cmd = Cmd,\n U extends Sub = Sub,\n Ctx = unknown,\n> {\n /**\n * The engine's `run` — `run` from `@demlik/tea/promise`, or any engine's. The\n * host imports no engine; it boots the machine through this.\n */\n readonly run: EngineRun<\n AgentState<Stage, P, O, R>,\n AgentMachineMsg<P, O, R>,\n C,\n U,\n Ctx,\n AgentEvent<R>\n >;\n /**\n * Build the wired agent machine and the handlers it runs under — its\n * `interpret` table and `subscribe` runners (a machine carries none — #278,\n * #279). Called once per host build (per activation), AFTER the previous\n * runtime — if any — was torn down. The consumer wires its per-tool interpret\n * here, and `agent.toMachine({ toolInterpret })` already returns exactly this.\n */\n readonly buildMachine: () => Wired<\n AgentState<Stage, P, O, R>,\n AgentMachineMsg<P, O, R>,\n C,\n U,\n Ctx\n >;\n /** The durable `Store` for the agent slice (typically `doStore(storage, parse)`). */\n readonly store: Store<AgentState<Stage, P, O, R>>;\n /** The plain Ctx the machine threads to its interpret cells. */\n readonly ctx: Ctx;\n /**\n * The run-terminality predicate (#46) — what makes `result()` first-class.\n * Defaults to the agent's own terminal phases (`done` / `failed`).\n */\n readonly terminal?: (state: AgentState<Stage, P, O, R>) => boolean;\n /**\n * Map one semantic {@link AgentEvent} to the consumer's SSE frame, or `null`\n * to skip it. This is the ONLY place the consumer touches the SSE seam — the\n * host owns the subscription wiring (`sseFromAgentEvents`) and the hub.\n */\n readonly toSseFrame: (event: AgentEvent<R>) => Frame | null;\n /** Clock injected for `autoBoot` (the host's only boot clock read). */\n readonly now?: () => number;\n}\n\n/**\n * The assembled host: the runtime handle + the SSE hub + the framework test seam,\n * owned ONCE. A DO holds one `AgentHost` and delegates to it; its own surface\n * shrinks to the domain (the gateway, the WS bridge, the command-send mapping).\n *\n * @typeParam Stage Pipeline stage type.\n * @typeParam P Brain-call purposes.\n * @typeParam O Per-purpose outputs (bound to {@link AgentTurn}).\n * @typeParam R Tool result type.\n * @typeParam Frame The consumer's SSE event shape.\n */\nexport interface AgentHost<\n Stage,\n P extends string,\n O extends Record<P, AgentTurn>,\n R,\n Frame,\n> {\n /**\n * Get (or build) the booted runtime. Build-once-boot-once: the first call\n * builds the machine, wires SSE off the semantic event stream, awaits the boot\n * gate, runs the `autoBoot` re-fire for a rehydrated suspended run, and caches\n * the booted handle; subsequent calls return the cached handle. The single\n * assembly every consumer used to hand-roll in `getRuntime()`.\n */\n runtime(): Promise<\n BootedRunHandle<\n AgentState<Stage, P, O, R>,\n AgentMachineMsg<P, O, R>,\n AgentEvent<R>\n >\n >;\n /**\n * The agent's lifecycle status (#49) — the ONE typed channel a consumer reads\n * instead of re-deriving `run.phase` / `awaiting` by hand. Ensures the runtime\n * is built + boot-reconciled first. Replaces the per-consumer `isSuspended` /\n * `runPhase` test methods (read `status().kind`: `idle` / `running` /\n * `suspended` / `done` / `failed`). A hydrated DO that has not yet seen\n * `agent_start` reads `idle`, not `running` (#92).\n */\n status(): Promise<AgentStatus<Stage>>;\n /**\n * The run's terminal State (#46), or `undefined` while in flight. Read the\n * run's product off it (e.g. `result()?.output`). Replaces the per-consumer\n * `verdict` plumbing's source. Ensures the runtime is built first.\n */\n result(): Promise<AgentState<Stage, P, O, R> | undefined>;\n /**\n * The SSE hub — the route (`host.sse.open()`) AND the test seam\n * (`host.sse.register(sink)`). The host drives `hub.emit` off the semantic\n * event stream; the consumer never touches it except to open / register.\n */\n readonly sse: SseHub<Frame>;\n /**\n * Tear down the cached runtime (drain its tail via `stop()`, drop the handle)\n * so the next `runtime()` rebuilds from storage. The framework `settle` test\n * seam, owned once.\n */\n reset(): Promise<void>;\n}\n\n/**\n * Build an {@link AgentHost} from the consumer's domain mappings. Owns the\n * runtime+events+autoBoot assembly and the framework test seam — the wiring\n * issue #53 had every consumer re-write.\n *\n * @example\n * ```ts\n * import type { Interpret, Migrated } from \"@demlik/tea\";\n * import type {\n * AgentKnob,\n * AgentMachineMsg,\n * AgentState,\n * AgentTurn,\n * ToolCall,\n * } from \"@demlik/tea/agent\";\n * import { createAgentHost, doStore } from \"@demlik/tea/do\";\n * import { run } from \"@demlik/tea/promise\";\n *\n * type Stage = \"plan\" | \"act\";\n * type Purpose = \"plan_turn\" | \"act_turn\";\n * type Outputs = Record<Purpose, AgentTurn>;\n * type ClientResult = string;\n * type ToolCmd = { readonly type: \"run_tool\" } & ToolCall;\n * type SseEvent =\n * | { kind: \"result\"; callId: string; result: ClientResult }\n * | { kind: \"verdict\"; verdict: string }\n * | { kind: \"phase\"; phase: string };\n * interface Ctx { readonly apiKey: string }\n * type S = AgentState<Stage, Purpose, Outputs, ClientResult>;\n *\n * // From `createAgent(...)`, and the interpret for its tool Cmd.\n * declare const agent: AgentKnob<Stage, Purpose, Outputs, ClientResult, ToolCmd, false, false>;\n * declare const toolInterpret: Interpret<AgentMachineMsg<Purpose, Outputs, ClientResult>, ToolCmd, Ctx>;\n * declare const storage: DurableObjectStorage;\n * declare const parse: (raw: unknown) => Migrated<S>;\n * declare const ctx: Ctx;\n *\n * const host = createAgentHost({\n * run,\n * buildMachine: () => agent.toMachine<Ctx>({ toolInterpret }),\n * store: doStore(storage, parse),\n * ctx,\n * toSseFrame: (e): SseEvent | null =>\n * e.type === \"ToolSettled\" ? { kind: \"result\", callId: e.callId, result: e.result } :\n * e.type === \"TurnSettled\" && e.turn.toolCalls.length === 0\n * ? { kind: \"verdict\", verdict: e.turn.content } :\n * e.type === \"RunDone\" ? { kind: \"phase\", phase: e.status.kind } : null,\n * });\n * // route:\n * const sseRoute = (): Response => host.sse.open();\n * // start:\n * await (await host.runtime()).dispatch({ type: \"agent_start\", runId: \"run-1\", at: Date.now() });\n * // test:\n * const suspended = (await host.status()).kind === \"suspended\";\n * ```\n */\nexport function createAgentHost<\n Stage,\n P extends string,\n O extends Record<P, AgentTurn>,\n R,\n Frame,\n C extends Cmd = Cmd,\n U extends Sub = Sub,\n Ctx = unknown,\n>(\n config: AgentHostConfig<Stage, P, O, R, Frame, C, U, Ctx>,\n): AgentHost<Stage, P, O, R, Frame> {\n type S = AgentState<Stage, P, O, R>;\n type M = AgentMachineMsg<P, O, R>;\n type E = AgentEvent<R>;\n\n const sse = sseHub<Frame>();\n const now = config.now ?? Date.now;\n // Every terminal phase, `cancelled` included: a run stopped from outside has\n // ended, so `done()` must resolve on it rather than wait for a `done` that is\n // not coming.\n const isTerminal =\n config.terminal ??\n ((s: S) =>\n s.run.phase === \"done\" ||\n s.run.phase === \"failed\" ||\n s.run.phase === \"cancelled\");\n\n // The build-once cell memoizes the IN-FLIGHT build (a Promise), not just the\n // settled runtime (#313). Were it the settled handle, two concurrent\n // activations — e.g. an `/sse` open and an inbound dispatch arriving in the\n // same wake — would each pass the fast-path guard (the cell is empty until\n // after the boot await) and each `run(buildMachine(), …)` + wire SSE + autoBoot\n // against the same storage: a double boot with duplicate SSE wiring, not the\n // build-once-boot-once this host promises. Assigning the promise synchronously,\n // before the first await, means the second caller shares the one build.\n let cached: Promise<BootedRunHandle<S, M, E>> | null = null;\n\n async function build(): Promise<BootedRunHandle<S, M, E>> {\n // `run()` hands back a `RunHandle` synchronously (#45). Wire the SEMANTIC\n // event projector (#47) so `runtime.on(...)` lights up, then drive the SSE\n // hub off that named stream (never the private-Msg firehose).\n const wired = config.buildMachine();\n // `ctx` goes in as its own `CtxArg<Ctx>`: over a generic `Ctx` that\n // conditional stays deferred, and tsc relates it only to itself, not to an\n // object that spreads the handlers beside it.\n const ctxArg = { ctx: config.ctx } as CtxArg<Ctx>;\n const booting: RunHandle<S, M, E> = config.run(wired.machine, {\n ...wired,\n ...ctxArg,\n store: config.store,\n events: agentEvents<Stage, P, O, R>(),\n });\n\n // SSE off the semantic stream. Subscriptions attach on the booting handle\n // (total before boot), so the hub catches every event from the first\n // transition. The cleanup is implicit: the host tears the whole runtime\n // down in `reset()`, dropping every subscription with it.\n sseFromAgentEvents(booting, sse, config.toSseFrame);\n\n // Re-fire the one outstanding tool effect IFF this wake rehydrated a\n // suspended run (no-op on a fresh DO). `autoBoot` awaits the same boot gate.\n await autoBoot(booting, now);\n return booting.ready;\n }\n\n function runtime(): Promise<BootedRunHandle<S, M, E>> {\n if (cached !== null) return cached;\n const building = build();\n cached = building;\n // A failed build clears the cell so a later call retries rather than\n // caching the rejection forever (matches the pre-#313 `cached`-stays-null\n // semantics). Guard on identity so a reset+rebuild isn't clobbered.\n building.catch(() => {\n if (cached === building) cached = null;\n });\n return cached;\n }\n\n return {\n runtime,\n async status(): Promise<AgentStatus<Stage>> {\n return status((await runtime()).getState());\n },\n async result(): Promise<S | undefined> {\n // The terminal read, off the host's own predicate rather than an engine's\n // `result()` — the handle every engine returns does not carry one.\n const state = (await runtime()).getState();\n return isTerminal(state) ? state : undefined;\n },\n sse,\n async reset(): Promise<void> {\n if (cached === null) return;\n // Await the settled build before tearing down — a reset racing an\n // in-flight build still drains the one runtime rather than orphaning it.\n // A failed build has no runtime to stop.\n const built = await cached.catch(() => null);\n cached = null;\n await built?.stop();\n },\n };\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @demlik/tea/do — generic hibernatable PRESENCE + BROADCAST for a NATIVE\n * (non-agent) Durable-Object grain.\n *\n * ## The gap this fills\n *\n * `./host` ships `acceptCommandSocket` / `acceptDurableCommandSocket` /\n * `broadcast` / `broadcastHibernatable`, but those are framed for — and live\n * alongside — the LLM-agent **deferred-tool gateway** (`createAgentHost`): the\n * socket is \"the command-runner socket\", the frame is \"a command\", and the\n * fan-out is the agent's `sendCommand` loop. A plain realtime grain — a game\n * room, a presence list, a collab document — does not have an agent, a gateway,\n * or commands. It just wants to accept hibernatable client sockets and fan a\n * frame out to whoever is connected. Today every such grain (vortex's\n * `ArenaGrain`, #162) hand-rolls the same `ctx.acceptWebSocket(server)` +\n * `for (const ws of ctx.getWebSockets()) ws.send(...)` boilerplate (#181).\n *\n * This module lifts exactly that, decoupled from the agent host:\n *\n * - {@link broadcastFrame} — the fan-out, with the **socket set as the input**\n * (not a `ctx`), so it is a pure function testable with fake sockets and\n * reusable over any iterable (`ctx.getWebSockets()`, a filtered subset, a\n * test array). It serializes once, sends to every OPEN socket, and skips a\n * closed/errored one — returning a {@link BroadcastReport} count rather than\n * swallowing the skip silently (errors are data).\n * - {@link registerHibernatableSocket} — register an already-created server\n * socket on the DO via the Cloudflare **Hibernation API**\n * (`ctx.acceptWebSocket`), optionally tagging it and persisting a\n * per-socket attachment that survives eviction. Pure over the `ctx` +\n * socket seam, so the registration logic is testable with fakes.\n * - {@link acceptPresenceSocket} — the full upgrade: build the\n * `WebSocketPair`, register the server end, and return the `server` socket\n * plus the 101 upgrade `Response`. The one piece that touches the\n * runtime-only `WebSocketPair` (so it is exercised by type, not by a unit\n * test without workerd) — the registration it delegates to *is* unit-tested.\n * - {@link presenceCount} — the live connection count off `ctx.getWebSockets`,\n * the trivial roster-size lift a presence grain reaches for.\n *\n * It is transport/Msg-agnostic: `frame` is `unknown` (JSON-serialized by\n * default, or via a caller `serialize`), and nothing here references an agent,\n * a gateway, a Sub, or a Msg. It is ADDITIVE — `./host`'s agent-coupled\n * helpers keep working untouched for the agent consumer.\n */\n\n/**\n * The minimal \"send a string frame\" surface {@link broadcastFrame} needs. A real\n * Cloudflare `WebSocket` satisfies it structurally (its `send` accepts a string\n * and it carries a numeric `readyState`), so `ctx.getWebSockets()` flows in\n * directly; a test supplies a fake with just `send` (and optionally\n * `readyState`).\n *\n * `readyState` is OPTIONAL: when present and not {@link WS_READY_STATE_OPEN},\n * the socket is skipped WITHOUT calling `send`; when absent, `send` is attempted\n * and a throw is caught (a fake that models only liveness via a throwing `send`\n * needs no `readyState`).\n */\nexport interface PresenceSocket {\n send(message: string): void;\n readonly readyState?: number;\n}\n\n/**\n * The Hibernation slice of `DurableObjectState` a presence grain needs — the\n * accept that hands the socket to the runtime (surviving eviction) and the\n * getter that repopulates the live set after a wake. A real `DurableObjectState`\n * satisfies this structurally; a test supplies a fake (no live Workers runtime\n * needed to exercise the accept/registry seam).\n */\nexport interface PresenceCtx {\n acceptWebSocket(ws: WebSocket, tags?: string[]): void;\n getWebSockets(tag?: string): WebSocket[];\n}\n\n/**\n * The OPEN `readyState` value (`WebSocket.READY_STATE_OPEN` in the Cloudflare\n * runtime). Named here so {@link broadcastFrame} can skip a non-open socket\n * without referencing the runtime constant (keeping the fold pure + testable).\n */\nexport const WS_READY_STATE_OPEN = 1;\n\n/**\n * What a {@link broadcastFrame} fan-out did, surfaced rather than swallowed:\n * `sent` is the number of sockets the frame reached; `skipped` is the number\n * passed over (not OPEN, errored on `send`, or the `except` socket). A caller\n * can log or assert these — a silent fan-out hides a room going dark.\n */\nexport interface BroadcastReport {\n readonly sent: number;\n readonly skipped: number;\n}\n\n/** Optional settings for {@link broadcastFrame}. */\nexport interface BroadcastOptions<S extends PresenceSocket> {\n /**\n * A socket to exclude from the fan-out — typically the sender, so a collab /\n * chat frame is not echoed back to its originator. Compared by identity.\n */\n readonly except?: S;\n /**\n * How to turn `frame` into the wire string. Defaults to `JSON.stringify`.\n * Override for a non-JSON wire format (e.g. a pre-encoded string passthrough).\n */\n readonly serialize?: (frame: unknown) => string;\n}\n\n/**\n * Serialize `frame` ONCE and send it to every OPEN socket in `sockets`, skipping\n * any that is closed, errors on `send`, or is the `except` socket. The socket\n * set is the INPUT (not a `ctx`), so this is a pure function: pass\n * `ctx.getWebSockets()` in a live grain, a filtered subset for a targeted\n * fan-out, or a fake array in a test.\n *\n * Skip semantics (a dead socket never crashes the broadcast):\n * - `readyState` present and not {@link WS_READY_STATE_OPEN} → skipped, `send`\n * not called;\n * - `send` throws (a socket closing mid-fan-out) → skipped, the throw is\n * caught (the close lifecycle reaps it);\n * - the `except` socket → skipped.\n *\n * Returns a {@link BroadcastReport} of `{ sent, skipped }` — the skip is data,\n * not silence.\n */\nexport function broadcastFrame<S extends PresenceSocket>(\n sockets: Iterable<S>,\n frame: unknown,\n options?: BroadcastOptions<S>,\n): BroadcastReport {\n const serialize = options?.serialize ?? JSON.stringify;\n const except = options?.except;\n const json = serialize(frame);\n let sent = 0;\n let skipped = 0;\n for (const ws of sockets) {\n if (except !== undefined && ws === except) {\n skipped++;\n continue;\n }\n if (ws.readyState !== undefined && ws.readyState !== WS_READY_STATE_OPEN) {\n skipped++;\n continue;\n }\n try {\n ws.send(json);\n sent++;\n } catch {\n // A socket closing mid-broadcast is reaped by its close lifecycle; the\n // failed send is safe to drop here, surfaced as a skip rather than a throw.\n skipped++;\n }\n }\n return { sent, skipped };\n}\n\n/** Optional settings for {@link registerHibernatableSocket} /\n * {@link acceptPresenceSocket}. */\nexport interface RegisterOptions<A> {\n /**\n * Hibernation tags for the socket — the same strings `ctx.getWebSockets(tag)`\n * filters on, so a grain can fan out to a subset (e.g. a team, a channel).\n */\n readonly tags?: readonly string[];\n /**\n * A per-socket value to persist via `server.serializeAttachment` so it\n * survives DO eviction (e.g. the player/session id the grain re-reads in\n * `webSocketMessage`/`webSocketClose`). Omitted ⇒ no attachment written.\n */\n readonly attachment?: A;\n}\n\n/**\n * The subset of `WebSocket` {@link registerHibernatableSocket} writes to — the\n * attachment serializer. A real server `WebSocket` satisfies it; a test supplies\n * a fake that records the serialized attachment.\n */\nexport interface AttachableSocket {\n serializeAttachment(value: unknown): void;\n}\n\n/**\n * Register an already-created `server` socket on the DO via the Cloudflare\n * **Hibernation API**: `ctx.acceptWebSocket(server, tags)` hands the socket to\n * the runtime (it survives eviction and re-delivers inbound frames to the DO's\n * `webSocketMessage` lifecycle method), and — when an `attachment` is supplied —\n * persists it via `server.serializeAttachment` so it survives the wake.\n *\n * Pure over the `ctx` + socket seam (no `WebSocketPair` construction), so the\n * registration logic — that accept is called with the right tags, that the\n * attachment is serialized iff present — is unit-testable with fakes.\n * {@link acceptPresenceSocket} is the thin wrapper that also builds the pair.\n */\nexport function registerHibernatableSocket<S extends AttachableSocket, A>(\n ctx: PresenceCtx,\n server: S,\n options?: RegisterOptions<A>,\n): void {\n // The Hibernation API accept: the runtime owns the socket across eviction; no\n // isolate-resident `message`/`close` listeners (those are lost on eviction) —\n // inbound frames arrive via the DO's `webSocketMessage` lifecycle method.\n ctx.acceptWebSocket(\n server as unknown as WebSocket,\n options?.tags === undefined ? undefined : [...options.tags],\n );\n if (options?.attachment !== undefined) {\n server.serializeAttachment(options.attachment);\n }\n}\n\n/** What {@link acceptPresenceSocket} hands back: the live server end (to send\n * initial frames on) and the 101 upgrade `Response` (to return from `fetch`). */\nexport interface PresenceUpgrade {\n /** The server end of the pair — send the welcome / initial snapshot on it. */\n readonly server: WebSocket;\n /** The 101 Switching-Protocols upgrade response carrying the client end. */\n readonly response: Response;\n}\n\n/**\n * Accept a client WebSocket upgrade on a native DO and register it for\n * hibernation. Builds the `WebSocketPair`, registers the server end via\n * {@link registerHibernatableSocket} (tags + optional attachment), and returns\n * the `server` socket plus the 101 upgrade {@link PresenceUpgrade.response}.\n *\n * The caller's `fetch` collapses to: send any initial frames on `server`\n * (welcome, current roster/snapshot), then `return upgrade.response`. This lifts\n * the `new WebSocketPair()` + `ctx.acceptWebSocket` + `serializeAttachment` +\n * `new Response(null, { status: 101, webSocket })` boilerplate a presence grain\n * hand-rolls.\n *\n * NOTE: `WebSocketPair` is a runtime-only global (no workerd in unit tests), so\n * this assembly is exercised by TYPE here; its registration seam is unit-tested\n * via {@link registerHibernatableSocket}.\n *\n * @example\n * ```ts\n * import { acceptPresenceSocket, type PresenceCtx } from \"@demlik/tea/do\";\n *\n * declare const room: { snapshot(): unknown };\n *\n * class Lobby {\n * constructor(readonly ctx: PresenceCtx) {}\n *\n * async fetch(request: Request): Promise<Response> {\n * if (request.headers.get(\"Upgrade\") !== \"websocket\") {\n * return new Response(\"expected a websocket upgrade\", { status: 426 });\n * }\n * const playerId = new URL(request.url).searchParams.get(\"player\");\n * const welcome = { type: \"welcome\", playerId };\n * const { server, response } = acceptPresenceSocket(this.ctx, {\n * attachment: { playerId },\n * });\n * server.send(JSON.stringify(welcome));\n * server.send(JSON.stringify(room.snapshot()));\n * return response;\n * }\n * }\n * ```\n */\nexport function acceptPresenceSocket<A>(\n ctx: PresenceCtx,\n options?: RegisterOptions<A>,\n): PresenceUpgrade {\n const pair = new WebSocketPair();\n const client = pair[0];\n const server = pair[1];\n registerHibernatableSocket(ctx, server, options);\n return {\n server,\n response: new Response(null, { status: 101, webSocket: client }),\n };\n}\n\n/**\n * The live connection count for a presence grain — `ctx.getWebSockets(tag)\n * .length`, repopulated by the runtime after a wake. Pass a `tag` to count only\n * the sockets registered under it. The trivial roster-size lift a presence /\n * game grain reaches for (vortex's `connectionCount`).\n */\nexport function presenceCount(ctx: PresenceCtx, tag?: string): number {\n return ctx.getWebSockets(tag).length;\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * The command-runner WebSocket host + broadcast — the WS half of the\n * `@demlik/tea/do` transport, in both the resident (`acceptCommandSocket`) and\n * hibernation-aware (`acceptDurableCommandSocket`) forms. See `./host` for the\n * transport-model rationale (ONE Sub type, gateway-bridged I/O).\n */\n\nimport { broadcastFrame } from \"./presence\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// acceptCommandSocket — WS host + inbound bridge (seam C).\n//\n// Accept the command-runner socket, keep it in a client registry, and route\n// inbound string frames to a handler (which the consumer points at\n// `gateway.settle(...)`). Lifts the consumer's `acceptClient` boilerplate.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Accept a command-runner WebSocket on the DO's `fetch`. Adds the server end to\n * `clients`, wires `message` → `onFrame(data)` (string frames only) and\n * `close` → deregister, and returns the 101 upgrade Response.\n *\n * The consumer's `onFrame` parses the frame and calls `gateway.settle(callId,\n * result)` — the inbound bridge. Deletes the consumer's `acceptClient` method.\n */\nexport function acceptCommandSocket(\n clients: Set<WebSocket>,\n onFrame: (data: string) => void,\n): Response {\n const pair = new WebSocketPair();\n const [client, server] = [pair[0], pair[1]];\n server.accept();\n clients.add(server);\n server.addEventListener(\"message\", (ev) => {\n if (typeof ev.data === \"string\") onFrame(ev.data);\n });\n server.addEventListener(\"close\", () => clients.delete(server));\n return new Response(null, { status: 101, webSocket: client });\n}\n\n/**\n * Broadcast a JSON frame to every connected command-runner socket. A dead\n * socket is skipped (dropped on its own `close` event). Lifts the consumer's\n * fan-out loop in `sendCommand`.\n *\n * Thin delegation to {@link broadcastFrame} — the general fan-out (serialize\n * once, send to every OPEN socket, skip a closed/errored one). The\n * {@link BroadcastReport} it returns is discarded here to keep the `void`\n * command-socket signature.\n */\nexport function broadcast(clients: Set<WebSocket>, frame: unknown): void {\n broadcastFrame(clients, frame);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// acceptDurableCommandSocket — a HIBERNATION-AWARE command-runner socket (#91).\n//\n// `acceptCommandSocket` (above) uses `server.accept()` — the NON-hibernatable\n// WebSocket API. The DO must stay resident to keep the socket alive; on eviction\n// the runtime tears the socket down (close 1006) and the inbound bridge dies.\n// That is half of #91. The other half — the LOAD-BEARING half — is that the\n// in-flight tool round-trips themselves are volatile: `deferredGateway`'s\n// `pending` Map is isolate-local, so a round-trip awaiting a reply when the DO\n// hibernates is lost with the heap, and nothing in replayed state records it is\n// still OWED. The durable carrier fixes BOTH:\n//\n// - the socket is accepted with `ctx.acceptWebSocket(server)` — the Cloudflare\n// Hibernation API. The runtime may evict the isolate while the socket stays\n// open; on the next inbound frame it re-creates the DO and calls the\n// `webSocketMessage(ws, data)` lifecycle method. The consumer routes that to\n// `onFrame` (which calls `carrier.settle(...)`). No isolate-resident\n// `message` listener to lose.\n// - each round-trip is recorded in the durable-effects ledger via\n// `durableDeferredGateway`, so the consumer PERSISTS an `effect_owed` before\n// the send and an `effect_confirmed` on settle/fail. On wake,\n// `reissueSurvivingEffects(carrier, persistedEvents, reissue)` folds the log\n// and re-fires every owed-but-unconfirmed round-trip — idempotent by the\n// ledger's monotonic `deliveryId`, deduped at the receiver.\n//\n// The carrier stays OUTSIDE the agent's Sub system (the `./host` rationale): the\n// round-trip is still a Promise the interpret cell awaits, bridged into\n// `dispatch` by the gateway. The DURABILITY lives in the ledger + the consumer's\n// storage, NOT in widening the machine's Sub type. `acceptCommandSocket` keeps\n// working untouched for callers that do not need eviction survival. (The carrier\n// itself lives in `./deferred-gateway`; the wake-path re-emit in `./resume`.)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The slice of `DurableObjectState` the durable carrier needs: the Hibernation\n * API accept + the registry of hibernatable sockets. A real `DurableObjectState`\n * satisfies this structurally; a test supplies a fake (no live Workers runtime\n * needed to exercise the accept/registry seam).\n */\nexport interface HibernatableCtx {\n acceptWebSocket(ws: WebSocket): void;\n getWebSockets(): WebSocket[];\n}\n\n/**\n * Accept a command-runner WebSocket using the Cloudflare **Hibernation API** so\n * the socket survives DO eviction. Unlike {@link acceptCommandSocket}, this does\n * NOT attach an isolate-resident `message` listener (that listener is what dies\n * on eviction) — instead `ctx.acceptWebSocket(server)` hands the socket to the\n * runtime, which re-creates the DO and calls its `webSocketMessage(ws, data)`\n * lifecycle method on the next inbound frame. The consumer routes that method to\n * its `onFrame` (which calls `carrier.settle(callId, result)`).\n *\n * Returns the 101 upgrade Response. There is no `clients` registry to keep — the\n * live sockets are `ctx.getWebSockets()` (see {@link broadcastHibernatable}),\n * which the runtime repopulates after a wake.\n */\nexport function acceptDurableCommandSocket(ctx: HibernatableCtx): Response {\n const pair = new WebSocketPair();\n const [client, server] = [pair[0], pair[1]];\n // Hibernation API: the runtime owns the socket across eviction; no resident\n // `message`/`close` listeners (they would be lost on eviction). Inbound frames\n // arrive via the DO's `webSocketMessage` lifecycle method instead.\n ctx.acceptWebSocket(server);\n return new Response(null, { status: 101, webSocket: client });\n}\n\n/**\n * Broadcast a JSON frame to every hibernatable command-runner socket. The live\n * set is read from `ctx.getWebSockets()` (the runtime repopulates it after a\n * wake), NOT a resident `Set<WebSocket>` — so it is correct immediately after an\n * eviction, before any other lifecycle method has run. A dead socket is skipped.\n *\n * Thin delegation to {@link broadcastFrame} over `ctx.getWebSockets()` — the\n * general fan-out (serialize once, send to every OPEN socket, skip a\n * closed/errored one). The {@link BroadcastReport} it returns is discarded here\n * to keep the `void` command-socket signature.\n */\nexport function broadcastHibernatable(\n ctx: HibernatableCtx,\n frame: unknown,\n): void {\n broadcastFrame(ctx.getWebSockets(), frame);\n}\n","/**\n * The deferred-tool gateway + its durable wrappers — the Promise-owning half of\n * the `@demlik/tea/do` transport. See `./host` for the transport-model\n * rationale (ONE Sub type, gateway-bridged I/O).\n */\n\nimport type {\n EffectConfirmed,\n EffectOwed,\n PendingEffectsRecorder,\n} from \"./durable-effects\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// deferredGateway — the deferred-tool gateway (HEADLINE).\n//\n// Lifts the consumer's `pending` Map + `awaitClient` + `Promise.race` timeout +\n// settle-on-reply. A tool's interpret cell calls `gateway.await(callId, send,\n// deadlineMs)`: the gateway registers the round-trip, fires `send`, and returns\n// a Promise the transport settles via `gateway.settle(callId, result)` (or that\n// the deadline rejects). The cell awaits it and maps Ok/Err to\n// `agent_tool_ok` / `agent_tool_err` — that is ALL the consumer's tool cell does\n// after this.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** A single in-flight tool round-trip, keyed by `callId`. */\ninterface PendingCall<R> {\n resolve(result: R): void;\n reject(reason: Error): void;\n readonly promise: Promise<R>;\n}\n\n/**\n * The deferred-tool gateway. `R` is the consumer's tool-result type (what\n * `agent_tool_ok` carries).\n *\n * - `await(callId, send, deadlineMs)` — register the round-trip, fire `send`,\n * return the Promise the reply (or the deadline) settles. Idempotent per\n * `callId`: a second call for an in-flight id returns the SAME promise\n * without re-firing `send` (the boot-reconcile re-fire is a no-op at the\n * gateway, mirroring the agent's idempotent boot).\n * - `settle(callId, result)` — the transport calls this on a reply; resolves\n * the awaited promise. A settle for an unknown / already-settled id is a\n * no-op (the callId guard the consumer used to keep in its pending Map).\n * - `fail(callId, reason)` — settle the round-trip as an error on demand\n * (e.g. a transport-level close). Same unknown-id no-op.\n * - `inFlight()` — the callIds with an open round-trip (tests / introspection).\n *\n * The deadline → reject path is what makes \"timeout is data\": the tool cell's\n * `catch` maps the rejection to `agent_tool_err`, recorded on the conversation,\n * never a hang.\n */\nexport interface DeferredGateway<R> {\n await(callId: string, send: () => void, deadlineMs: number): Promise<R>;\n settle(callId: string, result: R): void;\n fail(callId: string, reason: string): void;\n inFlight(): readonly string[];\n}\n\n/**\n * Build a deferred-tool gateway. No clock / RNG captured at construction — the\n * deadline is per-call (`await(..., deadlineMs)`), so one gateway serves every\n * tool family with its own timeout.\n *\n * Deletes from the consumer: the `pending` Map, the `Pending` interface, the\n * `awaitClient` method, the `forceTimeout` plumbing, and the `deliverResult`\n * callId-guard body — all of it collapses to this object plus the tool cell's\n * `await` + try/catch.\n */\nexport function deferredGateway<R>(): DeferredGateway<R> {\n const pending = new Map<string, PendingCall<R>>();\n\n return {\n await(callId, send, deadlineMs) {\n const existing = pending.get(callId);\n // Idempotent re-fire (boot reconcile re-issues the same callId): return\n // the open promise, do NOT re-fire `send`.\n if (existing !== undefined) return existing.promise;\n\n let resolve!: (r: R) => void;\n let reject!: (e: Error) => void;\n const promise = new Promise<R>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n const timer = setTimeout(() => {\n if (pending.delete(callId)) {\n reject(new Error(`deadline_exceeded after ${deadlineMs}ms`));\n }\n }, deadlineMs);\n pending.set(callId, {\n resolve: (v) => {\n clearTimeout(timer);\n resolve(v);\n },\n reject: (e) => {\n clearTimeout(timer);\n reject(e);\n },\n promise,\n });\n send();\n return promise;\n },\n\n settle(callId, result) {\n const entry = pending.get(callId);\n if (entry === undefined) return; // unknown / stale / already settled.\n pending.delete(callId);\n entry.resolve(result);\n },\n\n fail(callId, reason) {\n const entry = pending.get(callId);\n if (entry === undefined) return;\n pending.delete(callId);\n entry.reject(new Error(reason));\n },\n\n inFlight() {\n return [...pending.keys()];\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// durableDeferredGateway — bridge `deferredGateway` to the durable ledger.\n//\n// `deferredGateway` is volatile: its `pending` Map lives in the isolate and is\n// LOST on hibernation, so a tool round-trip in flight when the DO sleeps is\n// gone — exactly the durable-effects tax (ADR 0003 #1). This bridge makes the\n// round-trip a DURABLE owed effect WITHOUT changing the gateway's fast path:\n//\n// - the in-memory `await`/`settle`/`fail`/`inFlight` behavior is untouched\n// (the Promise the interpret cell awaits is still resolved in-isolate);\n// - around it, `await` records an `effect_owed` event and `settle`/`fail`\n// record an `effect_confirmed`, which the consumer PERSISTS into the same\n// event log the actor replays. On the next activation the rebuilt ledger's\n// `survivingEffects` are the round-trips to re-fire — idempotent by id.\n//\n// It is OPT-IN and ADDITIVE: a consumer that wants only the volatile gateway\n// keeps calling `deferredGateway()` directly; one that wants durability wraps\n// it here. The `record*` callbacks hand the consumer the events to persist (the\n// host does not own the consumer's log, so it cannot persist them itself —\n// keeping the bridge pure of storage and faithful to \"persist the intent before\n// deliver\").\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** A `callId`-keyed durable round-trip: maps the gateway's string id to the\n * ledger's monotonic delivery id so a `settle(callId)` finds the owed entry. */\ninterface DurableDeferredGateway<R> extends DeferredGateway<R> {\n /** The ledger recorder this gateway feeds (for `surviving()` on activation). */\n readonly recorder: PendingEffectsRecorder<{ callId: string }>;\n}\n\n/**\n * Wrap a `DeferredGateway<R>` so every round-trip is also recorded in a durable\n * `PendingEffectsRecorder`. The in-memory gateway is the fast path; the recorder\n * is the durability slice.\n *\n * - `recordOwed(callId, event)` — fires from `await`, BEFORE `send`, carrying\n * the `effect_owed` event the consumer must persist (then deliver).\n * - `recordConfirmed(callId, event)` — fires from `settle`/`fail` for a known\n * id, carrying the `effect_confirmed` event to persist.\n * - `reissue(callId, send, deadlineMs)` — re-arm a surviving round-trip on\n * activation. Idempotent: the gateway's own `await` returns the open promise\n * without re-firing `send` for an in-flight `callId`, so re-issuing an id\n * that is somehow still pending is a no-op (mirrors the boot-reconcile\n * re-fire). The effect is already owed, so no second `effect_owed` is\n * recorded.\n *\n * The effect payload the ledger stores is the `callId` (the correlation key the\n * consumer round-trips); the consumer maps it back to its tool-call context.\n */\nexport function durableDeferredGateway<R>(\n inner: DeferredGateway<R>,\n recorder: PendingEffectsRecorder<{ callId: string }>,\n hooks: {\n recordOwed(callId: string, event: EffectOwed<{ callId: string }>): void;\n recordConfirmed(callId: string, event: EffectConfirmed): void;\n },\n): DurableDeferredGateway<R> {\n // callId → ledger deliveryId, so settle/fail can confirm the right entry.\n const idByCallId = new Map<string, number>();\n\n return {\n recorder,\n\n await(callId, send, deadlineMs) {\n // Already owed (re-entrant await for an in-flight id) → don't re-owe;\n // delegate so the gateway returns the SAME open promise without re-send.\n if (!idByCallId.has(callId)) {\n const { id, event } = recorder.owe({ callId });\n idByCallId.set(callId, id);\n // Persist the intent BEFORE the send fires (which `inner.await` does).\n hooks.recordOwed(callId, event);\n }\n return inner.await(callId, send, deadlineMs);\n },\n\n settle(callId, result) {\n confirm(callId);\n inner.settle(callId, result);\n },\n\n fail(callId, reason) {\n confirm(callId);\n inner.fail(callId, reason);\n },\n\n inFlight() {\n return inner.inFlight();\n },\n };\n\n function confirm(callId: string): void {\n const id = idByCallId.get(callId);\n if (id === undefined) return; // unknown / already confirmed — no-op.\n idByCallId.delete(callId);\n const { event } = recorder.confirm(id);\n hooks.recordConfirmed(callId, event);\n }\n}\n\n/**\n * The durable command carrier — a {@link DurableDeferredGateway} whose every\n * tool round-trip is also a durable owed effect.\n *\n * A consumer's tool interpret cell still calls `carrier.await(callId, send,\n * deadlineMs)` and awaits the Promise (mapping Ok/Err to `agent_tool_ok` /\n * `agent_tool_err`) — the SAME fast path as the volatile gateway. The addition\n * is durability: `await` hands the consumer an `effect_owed` event (via\n * `recordOwed`) to PERSIST before the send, and `settle`/`fail` hand it an\n * `effect_confirmed` (via `recordConfirmed`) to persist. On the next activation\n * the consumer reads its persisted ledger events back and calls\n * `reissueSurvivingEffects` (see `./resume`) to re-fire the survivors.\n *\n * It IS a `DurableDeferredGateway` (so it is a drop-in for the gateway the\n * interpret cell already awaits) and exposes the underlying `recorder` for a\n * host that wants to read `surviving()` directly. The #91 \"command carrier\"\n * concept is exactly the durable gateway under a domain name — a type alias, not\n * a second byte-identical interface that could drift.\n */\nexport type DurableCommandCarrier<R> = DurableDeferredGateway<R>;\n\n/**\n * Build a durable command carrier over a (volatile) `DeferredGateway<R>` and a\n * `PendingEffectsRecorder`. Thin assembly over {@link durableDeferredGateway}:\n * it names the #91 piece — \"the command carrier whose round-trips survive\n * eviction\" — and keeps the recorder reachable for the activation-time re-emit.\n *\n * - `inner` — the volatile gateway whose in-isolate Promise the interpret cell\n * awaits (typically `deferredGateway<R>()`).\n * - `recorder` — the durable-effects recorder (typically\n * `pendingEffectsLedger({ events, lastId })` rehydrated from the persisted\n * log on a wake, or a fresh `pendingEffectsLedger()` on a cold boot).\n * - `hooks.recordOwed` / `hooks.recordConfirmed` — the persist callbacks the\n * consumer points at its event log (so the ledger survives eviction).\n *\n * The carrier never persists anything itself (it does not own the consumer's\n * log) — it hands the consumer the events, faithful to \"persist the intent\n * before deliver\".\n */\nexport function durableCommandCarrier<R>(\n inner: DeferredGateway<R>,\n recorder: PendingEffectsRecorder<{ callId: string }>,\n hooks: {\n recordOwed(callId: string, event: EffectOwed<{ callId: string }>): void;\n recordConfirmed(callId: string, event: EffectConfirmed): void;\n },\n): DurableCommandCarrier<R> {\n return durableDeferredGateway(inner, recorder, hooks);\n}\n","/**\n * @demlik/tea/do — the idempotent-effect brick (the receiver/effect-side dedup).\n *\n * ## The half this closes (do NOT re-implement the ledger)\n *\n * The runtime's `stepDispatch` step order is SAVE-BEFORE-EFFECTS: state is\n * persisted (`store.save(next)`) *before* its commands are interpreted\n * (`packages/tea/src/index.ts` — \"Save-before-effects is the hard ordering;\n * tests pin it\"). So a DO evicted after `save` but before an effect's result is\n * folded RE-FIRES that effect on the next activation.\n *\n * `durable-effects.ts` (sibling) owns the SENDER half of this: the at-least-once\n * `pendingEffectsLedger`, whose surviving owed entries `reissueSurvivingEffects`\n * re-fires on activation. Its own header states the contract this file fulfils:\n * \"At-least-once ⇒ a confirmed-but-not-yet-persisted delivery WILL be re-sent\n * after a crash, so the receiver must deduplicate by id\" and \"the receiver owns\n * its own dedup.\" This module IS that receiver-side dedup — the piece that\n * upgrades the ledger's at-least-once re-fire to **effectively-once at the\n * external write**:\n *\n * - ledger (`survivingEffects` / `reissueSurvivingEffects`) = at-least-once re-fire;\n * - this brick (`idempotentEffect` + `appliedEffects`) = effect-side dedup\n * ⇒ together, effectively-once.\n *\n * It COMPLEMENTS the ledger; it does not re-implement it. The ledger keys owed\n * effects by a monotonic `DeliveryId` (its private sender bookkeeping); this\n * brick keys applied effects by a caller-supplied durable `EffectKey` (e.g.\n * `(runId, violationId)`), because dedup identity must be stable across the\n * re-fire — the same external write must resolve to the same key on every\n * activation, which a fresh monotonic id cannot guarantee.\n *\n * ## The load-bearing rule (mirrors `durable-effects.ts`)\n *\n * The applied-marker set is a PURE FOLD over the same kind of event stream the\n * actor already replays — it is **NOT** a separately-persisted side table:\n *\n * - `effect_applied` — `{ key }` ADDS the key to the applied set.\n * - `effect_forgotten` — `{ key }` REMOVES it (retention — see below).\n *\n * `foldApplied(events)` is `events.reduce(applyAppliedEvent, empty)` — the same\n * fold shape `replay` / `foldLedger` use. This makes the marker live \"alongside\n * State (single authority)\" for BOTH persistence modes:\n *\n * - `doEventSourcedStore`: persist the two events into the SAME log it already\n * appends; folding the log on activation rebuilds the applied set for free.\n * - default snapshot `doStore`: carry the `AppliedEffects` set as a field of\n * the Model, round-tripped by the store's `serialize`/`parse` hooks (a `Set`\n * flattens under `JSON.stringify`, exactly the `Map` sharp edge #182), so it\n * is checkpointed with the rest of `S`.\n *\n * Either way the marker is folded/snapshotted with State, never a side channel.\n *\n * ## Ordering — why the marker is recorded AFTER `fn` (at-least-once, not at-most)\n *\n * `run` invokes `fn` and only THEN yields the `effect_applied` event to persist.\n * An eviction in the window between `fn` succeeding and that event being folded\n * re-fires the effect (at-least-once) — correct, because recording the marker\n * BEFORE `fn` would let a crash strand the marker with the write never made\n * (at-most-once ⇒ a silently-dropped effect). Once the marker is folded, every\n * subsequent re-fire of that key is a proven no-op: `fn` is not invoked again.\n * The ledger guarantees the re-fire; this brick swallows the duplicate.\n *\n * ## Retention — pruning mirrors the ledger's confirm-removes-entry (not a new policy)\n *\n * An applied-marker is only needed while its effect can still re-fire. The\n * ledger stops re-firing an effect once its `effect_confirmed` removes the owed\n * entry — so at that same point the marker is dead weight and is pruned via\n * `effect_forgotten` (`guard.forget(key)`), keeping the applied set bounded. This\n * deliberately reuses the ledger's confirm-removes-entry retention shape rather\n * than inventing a second policy (the #227 triage's one open question).\n */\n\nimport type { Cmd } from \"../index\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// The two events the applied-marker set folds. Ordinary `Cmd` (Msg) variants —\n// persist them into the same log `doEventSourcedStore` appends, or carry the\n// folded set in the snapshot Model; either way the set rebuilds with no side\n// table. Mirrors `durable-effects.ts`'s `effect_owed`/`effect_confirmed`.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The caller-supplied durable dedup identity for an effect — stable across the\n * re-fire (e.g. `` `${runId}:${violationId}` ``). Unlike the ledger's monotonic\n * `DeliveryId`, it is chosen by the domain so the SAME external write resolves\n * to the SAME key on every activation.\n */\nexport type EffectKey = string;\n\n/** An effect keyed `key` has been APPLIED. Adds `key` to the set when folded. */\nexport interface EffectApplied extends Cmd<\"effect_applied\"> {\n readonly key: EffectKey;\n}\n\n/**\n * An applied-marker is no longer needed (its effect can no longer re-fire, e.g.\n * its ledger entry was confirmed) and may be pruned. Removes `key` when folded.\n */\nexport interface EffectForgotten extends Cmd<\"effect_forgotten\"> {\n readonly key: EffectKey;\n}\n\n/** The event union the applied-marker set folds over. */\nexport type AppliedEffectsEvent = EffectApplied | EffectForgotten;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// The applied-marker set — a pure value reconstructed by `foldApplied`. Never\n// persisted on its own; the events (or the snapshot that embeds it) are.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** The set of keys whose effect has been applied. A pure fold result. */\nexport type AppliedEffects = ReadonlySet<EffectKey>;\n\n/** The empty applied set — the fold's starting value (a fresh actor has\n * applied nothing). */\nexport function emptyApplied(): AppliedEffects {\n return new Set<EffectKey>();\n}\n\n/**\n * Apply ONE event — the reducer at the heart of the fold.\n *\n * - `effect_applied` → add `key` (a duplicate add is a no-op — a re-fired\n * already-applied key tolerated by at-least-once).\n * - `effect_forgotten` → remove `key` (a no-op if already absent).\n *\n * Pure: returns a NEW set on change, never mutates the input — so folding the\n * same events twice (live and post-eviction) yields equal results.\n */\nexport function applyAppliedEvent(\n applied: AppliedEffects,\n event: AppliedEffectsEvent,\n): AppliedEffects {\n switch (event.type) {\n case \"effect_applied\": {\n if (applied.has(event.key)) return applied;\n const next = new Set(applied);\n next.add(event.key);\n return next;\n }\n case \"effect_forgotten\": {\n if (!applied.has(event.key)) return applied;\n const next = new Set(applied);\n next.delete(event.key);\n return next;\n }\n }\n}\n\n/**\n * THE FOLD. Rebuild the applied set from an event stream — the same fold shape\n * `replay`/`foldLedger` use. After a simulated eviction this is called over the\n * persisted events and yields a set equal to the never-evicted one, because a\n * pure fold over identical input is identical. This is what proves \"no side\n * table\": the set is `events` reduced, nothing more.\n */\nexport function foldApplied(\n events: Iterable<AppliedEffectsEvent>,\n): AppliedEffects {\n let applied = emptyApplied();\n for (const event of events) applied = applyAppliedEvent(applied, event);\n return applied;\n}\n\n/** Has this key's effect already been applied (folded into the set)? */\nexport function isApplied(applied: AppliedEffects, key: EffectKey): boolean {\n return applied.has(key);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// idempotentEffect — the effect descriptor: a durable key paired with the effect\n// thunk. It is only a pairing; the `appliedEffects` guard decides whether to run\n// it. Mirrors how the ledger's `owe(effect)` pairs a payload with its bookkeeping.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A keyed external effect. `key` is the durable dedup identity; `fn` is the\n * irreversible external write (HTTP write, DB upsert, finding/ticket publish).\n */\nexport interface IdempotentEffect<A> {\n readonly key: EffectKey;\n readonly fn: () => Promise<A> | A;\n}\n\n/**\n * Pair a durable `key` with an external effect `fn` — this module's primary\n * surface. Run it through {@link appliedEffects}'s guard; a re-fire whose key is\n * already applied is a proven no-op (`fn` is not invoked a second time).\n */\nexport function idempotentEffect<A>(\n key: EffectKey,\n fn: () => Promise<A> | A,\n): IdempotentEffect<A> {\n return { key, fn };\n}\n\n/**\n * The outcome of running a keyed effect through the guard. A boolean-discriminated\n * union so the skipped arm cannot carry a (never-produced) `result`/`event`:\n *\n * - `ran: true` — first application: `fn` ran, `result` is its value, and\n * `event` is the `effect_applied` marker the caller MUST persist (fold into\n * State) so future re-fires are swallowed.\n * - `ran: false` — the key was already applied: `fn` was NOT invoked, the\n * duplicate is swallowed, and there is no new event to persist.\n */\nexport type IdempotentOutcome<A> =\n | {\n readonly ran: true;\n readonly key: EffectKey;\n readonly result: A;\n readonly event: EffectApplied;\n }\n | { readonly ran: false; readonly key: EffectKey };\n\n/**\n * A live guard over an applied-marker set. Mirrors `pendingEffectsLedger`'s\n * recorder shape: it keeps an in-memory mirror folded from the same events the\n * caller persists, so the dedup decision is available without a re-read.\n *\n * - `run(effect)` — apply-once: skip (no `fn`) if the key is already applied,\n * else await `fn` and yield the `effect_applied` event to PERSIST.\n * - `forget(key)` — prune a marker once its effect can no longer re-fire;\n * yields the `effect_forgotten` event to PERSIST.\n * - `applied()` — the current folded set.\n * - `isApplied(key)`— convenience membership test against the mirror.\n */\nexport interface AppliedEffectsGuard {\n run<A>(effect: IdempotentEffect<A>): Promise<IdempotentOutcome<A>>;\n forget(key: EffectKey): { readonly event: EffectForgotten };\n applied(): AppliedEffects;\n isApplied(key: EffectKey): boolean;\n}\n\n/**\n * Build a live guard. `restore` rehydrates it after an eviction: pass the events\n * read back from the log (or the applied set carried in the reloaded snapshot,\n * as single-element `effect_applied` events) so the mirror reflects what was\n * already applied. Omit `restore` for a fresh actor.\n */\nexport function appliedEffects(restore?: {\n readonly events?: Iterable<AppliedEffectsEvent>;\n}): AppliedEffectsGuard {\n let mirror: AppliedEffects = restore?.events\n ? foldApplied(restore.events)\n : emptyApplied();\n\n return {\n async run<A>(effect: IdempotentEffect<A>): Promise<IdempotentOutcome<A>> {\n // Already-applied ⇒ swallow the re-fire WITHOUT invoking `fn` — the proven\n // no-op the receiver owes the at-least-once ledger.\n if (mirror.has(effect.key)) {\n return { ran: false, key: effect.key };\n }\n // First application: run the effect, THEN record the marker (see the\n // ordering rationale in the module header — after, never before).\n const result = await effect.fn();\n const event: EffectApplied = { type: \"effect_applied\", key: effect.key };\n mirror = applyAppliedEvent(mirror, event);\n return { ran: true, key: effect.key, result, event };\n },\n forget(key: EffectKey): { readonly event: EffectForgotten } {\n const event: EffectForgotten = { type: \"effect_forgotten\", key };\n mirror = applyAppliedEvent(mirror, event);\n return { event };\n },\n applied(): AppliedEffects {\n return mirror;\n },\n isApplied(key: EffectKey): boolean {\n return mirror.has(key);\n },\n };\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @demlik/tea/do — CQRS projections as a first-class seam.\n *\n * TODAY (`./host`), \"Model → views\" is ad-hoc: `sseHub` is a hand-rolled\n * sink-set fanning `runtime.observe` events out. It re-derives a common shape:\n * subscribe to `observe`, fold each update into a private view, push the view\n * at a sink. (The run's terminal output was once another such observe-driven\n * read model — `captureLastTurn` — but that is now modelled first-class on the\n * write model itself as `state.output` / `runtime.result()`, #46.)\n *\n * This file names that shape. A `Projection<Model, View>` is the canon's\n * \"one write model, many projections\" contract made concrete on the tea\n * substrate (`.patterns/tea-do/projections.md`):\n *\n * 1. ONE WRITE MODEL, MANY PROJECTIONS. The write model is the agent's `run()`\n * State (`Model`) plus its `Msg` stream. Each projection is an INDEPENDENT\n * `(events|Model) → view` derivation into its OWN read model, with its own\n * id-scoped offset. Adding a projection never touches the write model or\n * any other projection — it only registers another consumer of `observe`.\n *\n * 2. OFFSET-RESUME + IDEMPOTENT APPLY ARE ONE UNIT. A durable projection\n * tracks an EXCLUSIVE offset — the last applied position. On restart it\n * resumes STRICTLY AFTER the stored offset (never ±1); at-least-once\n * delivery means the same update may be re-presented, so `apply` must be\n * idempotent (an upsert of the derived view, never a blind increment).\n * WHEN the offset is committed relative to the view write IS the delivery\n * semantic (`.patterns/tea-do/delivery-semantics.md`):\n * - commit AFTER the view write → at-least-once (may reprocess)\n * - commit in the SAME step → exactly-once (the default here)\n * We persist offset-with-view as one unit (a single `OffsetView` cell), so\n * a durable projection is exactly-once by construction; the at-least-once\n * replay window is still exercised because `drive()` may re-present an\n * update at/below the stored offset (boot reconcile, double-observe), and\n * the exclusive-offset guard makes that a no-op.\n *\n * 3. A VIEW IS ALWAYS REBUILDABLE. Clearing the view and resetting the offset\n * to the start (`rebuild`) replays the `Msg`/`Model` stream from zero. The\n * live view and the rebuilt view are equal by construction — both are the\n * same pure `apply` fold over the same ordered updates.\n *\n * Relationship to `./host`: `sseHub` is re-expressed here as ONE projection\n * (`sseProjection`) WITHOUT changing its public API — `host.sseHub()` keeps\n * working for existing callers (it is the volatile, offset-free case: a live\n * SSE stream has no durable read model to resume).\n *\n * Relationship to `./event-sourced-store`: a durable projection resumes over\n * the SAME append-only event log `doEventSourcedStore` (#68) writes. The fold\n * is the projection's `apply`, scoped to the projection's offset — independent\n * of the write model's snapshot/replay. This file does NOT import the store; it\n * is offset-shaped so a consumer can wire either the live `observe` stream or a\n * replay over the log into the same `apply`.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ProjectionId — name + key identifies a running instance.\n//\n// `name` is the view (\"sse\", \"report\", \"last-turn\"); `key` distinguishes\n// instances (shards, partitions). The stored offset is scoped to the id, so the\n// key is also what lets two instances of one view track offsets independently\n// (projections.md \"ProjectionId\"). Two projections sharing an id share an offset\n// row and would fight over it — so an id is the unit of offset isolation.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** A projection's identity: `name` (the view) + `key` (the instance). */\nexport interface ProjectionId {\n readonly name: string;\n readonly key: string;\n}\n\n/** Render a `ProjectionId` to its canonical `name-key` string. */\nexport function projectionIdString(id: ProjectionId): string {\n return `${id.name}-${id.key}`;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ProjectionErrorContext / ProjectionOnError — the isolation sink.\n//\n// A projection's `apply`/`emit` may throw. The driver isolates that throw so it\n// never strands sibling projections — but isolation is NOT swallowing\n// (errors-are-data). The caught throw routes to an injected `onError` sink,\n// tagged with the id of the projection that failed, so a broken projection\n// surfaces instead of vanishing. The runtime layer's `OnError`/`RuntimeError\n// Context` twin, kept here so `do/projection` stays a types-only boundary that\n// imports nothing from the substrate.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Context handed to a {@link ProjectionOnError} sink alongside the throw. */\nexport interface ProjectionErrorContext {\n /** The id of the projection whose `apply`/`emit` threw. */\n readonly id: ProjectionId;\n}\n\n/**\n * Sink for a projection `apply`/`emit` throw the driver isolated. Injected into\n * {@link projectionRegistry}; called with the original error and the id of the\n * failing projection so a broken projection surfaces instead of being silently\n * discarded (errors-are-data). Should be total.\n */\nexport type ProjectionOnError = (\n error: unknown,\n context: ProjectionErrorContext,\n) => void;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Update — the envelope a projection folds.\n//\n// What flows past `runtime.observe(observer)` is `(msg, state)` — the applied\n// `Msg` (the event) and the post-transition `Model` (the write-model state). A\n// projection can fold either: an event-shaped projection reads `msg`, a\n// state-shaped one reads `model`. `offset` is the exclusive position of THIS\n// update in the ordered stream (monotonic, 1-based; 0 means \"before the first\n// update\", i.e. the NoOffset start). The boot observe (`msg === null`) carries\n// the initial state and is offset 0 — a projection that only folds events skips\n// it; one that derives from state may seed its view from it.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * One unit the projection driver presents to a projection's `apply`. Mirrors\n * the `(msg, model)` pair `runtime.observe` fans out, plus the exclusive\n * `offset` the driver assigns. `msg === null` is the boot update (no event).\n */\nexport interface ProjectionUpdate<Model, Msg> {\n /** The applied event, or `null` on the boot update (initial state). */\n readonly msg: Msg | null;\n /** The post-transition write-model state. */\n readonly model: Model;\n /**\n * The exclusive position of this update in the ordered stream — monotonic,\n * 1-based; 0 is the boot update (the `NoOffset` start). The stored offset is\n * EXCLUSIVE: re-presenting an update whose offset is `<=` the stored offset\n * must be a no-op (the at-least-once replay window). Do NOT ±1 it.\n */\n readonly offset: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Projection — the named fold + its sink.\n//\n// `apply(view, update)` is the per-envelope reducer (projections.md \"Handler\"):\n// it derives the next `View` from the previous `View` and one update. It MUST be\n// pure and idempotent w.r.t. the update's offset — re-applying the same offset\n// yields the same view (upsert, not blind increment). `emit(view)` is the sink:\n// SSE frame, durable cell, report row. `initial` seeds the view for a fresh /\n// rebuilt run (the `NoOffset` start state).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A named CQRS projection: an independent fold of the write model's\n * `(Msg|Model)` stream into a private `View`, plus a sink to publish it.\n *\n * @typeParam Model The write-model state (`run()`'s `S`).\n * @typeParam Msg The write-model event (`run()`'s `M`).\n * @typeParam View The read model this projection derives.\n */\nexport interface Projection<Model, Msg extends { type: string }, View> {\n /** This projection's id (offset-isolation unit). */\n readonly id: ProjectionId;\n /** The seed view for a fresh or rebuilt run (the `NoOffset` start). */\n readonly initial: View;\n /**\n * The per-envelope reducer: derive the next view from the previous view and\n * one update. Pure; MUST be idempotent — re-applying an update at the same\n * offset yields the same view (upsert the derived value, never a blind\n * increment). Returning the SAME view reference (or an equal one) for an\n * update the view does not care about is the canonical \"skip\".\n */\n apply(view: View, update: ProjectionUpdate<Model, Msg>): View;\n /**\n * The sink — publish the derived view. SSE frame, durable cell, report row.\n * Fired by the driver after `apply` produces a new view. A throwing `emit` is\n * isolated by the driver (it must not strand sibling projections).\n */\n emit(view: View): void;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ProjectionRunner — a single projection's live read model + offset.\n//\n// Wraps one `Projection` with its current `view` and its EXCLUSIVE stored\n// `offset`. `present(update)` is the at-least-once entry point: it folds the\n// update IFF the update is strictly after the stored offset (the exclusivity\n// guard), commits the offset WITH the view as one unit (exactly-once by\n// construction), then emits. `reset()` clears the view to `initial` and the\n// offset to 0 — the canonical view-rebuild operation.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A live, running projection instance: the projection's current `View`, its\n * exclusive stored `offset`, and the `present`/`reset` operations the driver\n * (or a rebuild) calls.\n */\nexport interface ProjectionRunner<Model, Msg extends { type: string }, View> {\n readonly id: ProjectionId;\n /** The current derived view (the read model). */\n view(): View;\n /**\n * The exclusive stored offset — the position of the LAST applied update.\n * Resume reads strictly after this; `0` is the `NoOffset` start.\n */\n offset(): number;\n /**\n * Present one update to the projection. At-least-once entry point:\n * - if `update.offset <= storedOffset` the update was already applied →\n * NO-OP (idempotent replay window; offset stays, view stays, no emit).\n * - otherwise fold via `apply`, advance the offset TO `update.offset`, and\n * `emit` the new view. Offset-and-view advance together (one unit) →\n * exactly-once: the stored offset always matches the folded view.\n * Returns true iff the update was applied (false on the no-op replay).\n */\n present(update: ProjectionUpdate<Model, Msg>): boolean;\n /**\n * Clear the view to `initial` and the offset to 0 — the canonical rebuild\n * reset (offset-tracking.md: \"resetting it to NoOffset is the view-rebuild\n * operation\"). After `reset`, re-presenting the full stream rebuilds the view.\n */\n reset(): void;\n}\n\n/**\n * Build a {@link ProjectionRunner} for a projection, starting from a stored\n * offset (default 0 = `NoOffset`, a fresh run). The view is seeded to\n * `projection.initial`; the caller resumes by presenting updates strictly after\n * `startOffset` (the driver's offset guard enforces exclusivity even if an\n * already-applied update is re-presented).\n *\n * The runner never emits the seed: a fresh runner holds `initial` but does not\n * push it at the sink until the first real update lands (an SSE client that just\n * connected wants the next event, not a replay of the seed), and `reset()`\n * likewise re-seeds `view`/`offset` silently — `emit` fires only from `present`\n * when a strictly-newer update is folded.\n */\nexport function runProjection<Model, Msg extends { type: string }, View>(\n projection: Projection<Model, Msg, View>,\n startOffset = 0,\n): ProjectionRunner<Model, Msg, View> {\n let view = projection.initial;\n let storedOffset = startOffset;\n\n return {\n id: projection.id,\n view: () => view,\n offset: () => storedOffset,\n present(update) {\n // EXCLUSIVE-OFFSET GUARD. The stored offset is the last applied position;\n // an update at or below it was already folded into `view`. Re-presenting\n // it (boot reconcile, double-observe, log replay overlap) is the\n // at-least-once replay window — fold it again and a non-idempotent view\n // double-counts. We make the guard structural: skip it entirely. This is\n // what \"exclusive offset, do NOT ±1\" means in code — the stored value is\n // re-usable as-is as the resume point; the next applied update is the\n // first one strictly greater than it.\n if (update.offset <= storedOffset) return false;\n const next = projection.apply(view, update);\n // Offset advances WITH the view, as one unit — exactly-once. The stored\n // offset is never ahead of (nor behind) the view it describes.\n view = next;\n storedOffset = update.offset;\n projection.emit(next);\n return true;\n },\n reset() {\n view = projection.initial;\n storedOffset = 0;\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ProjectionRegistry — the driver. Fans `runtime.observe` to N projections.\n//\n// One write model, many projections: the registry holds a set of independent\n// runners and `dispatch(update)` presents the SAME update to each. Each runner\n// applies its own `apply` into its own view at its own offset — a failure or\n// reset in one never touches another (the driver isolates `apply`/`emit`\n// throws). `from(runtime)` wires the registry to `runtime.observe`, assigning a\n// monotonic exclusive offset per observed transition (the boot observe is\n// offset 0; the first real Msg is offset 1).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A driver over a set of projections sharing one write-model stream. Holds the\n * monotonic offset counter; presents each observed update to every registered\n * runner independently.\n */\nexport interface ProjectionRegistry<Model, Msg extends { type: string }> {\n /** Register a projection; returns its live runner. */\n register<View>(\n projection: Projection<Model, Msg, View>,\n startOffset?: number,\n ): ProjectionRunner<Model, Msg, View>;\n /**\n * Present one update to EVERY registered runner. Each folds independently; an\n * `apply`/`emit` throw in one runner is caught so it cannot strand the others\n * (errors-are-data: a broken projection is dropped from THIS update, not\n * allowed to corrupt siblings) and routed to the registry's `onError` sink so\n * it surfaces rather than vanishing. Returns the offset assigned to this update.\n */\n dispatch(msg: Msg | null, model: Model): number;\n /** The current monotonic offset (the last dispatched update's position). */\n offset(): number;\n /** The registered runners (introspection / tests). */\n runners(): readonly ProjectionRunner<Model, Msg, unknown>[];\n}\n\n/**\n * Build an empty {@link ProjectionRegistry}. Pass an `onError` sink to receive\n * any `apply`/`emit` throw the driver isolates — without it a broken projection\n * is still isolated from its siblings, but its failure is not surfaced (the\n * substrate's `run(machine, { onError })` is the natural source to forward).\n */\nexport function projectionRegistry<Model, Msg extends { type: string }>(\n onError?: ProjectionOnError,\n): ProjectionRegistry<Model, Msg> {\n const runners = new Set<ProjectionRunner<Model, Msg, unknown>>();\n // The boot observe is offset 0; the first applied Msg is offset 1. We start at\n // -1 and pre-increment so `dispatch(null, initialState)` (the boot fanout)\n // lands on 0 and the first real Msg on 1.\n let counter = -1;\n\n return {\n register(projection, startOffset) {\n const runner = runProjection(projection, startOffset);\n runners.add(runner as ProjectionRunner<Model, Msg, unknown>);\n return runner;\n },\n dispatch(msg, model) {\n const offset = ++counter;\n const update = { msg, model, offset };\n for (const runner of runners) {\n try {\n runner.present(update);\n } catch (err) {\n // A projection's apply/emit threw. Isolate it — the sibling runners\n // still see this update. The broken runner keeps its prior view +\n // offset (it did not advance), so it self-heals on a later update or\n // a rebuild. Isolation is NOT swallowing: route the throw to the\n // injected `onError` sink, tagged with the failing projection's id, so\n // it surfaces (errors-are-data: surface via the sink, never strand).\n onError?.(err, { id: runner.id });\n }\n }\n return offset;\n },\n offset: () => counter,\n runners: () => [...runners],\n };\n}\n\n/**\n * Wire a {@link ProjectionRegistry} to a runtime's transition stream. The boot\n * update (the initial state, offset 0) is presented via `onBoot`; every applied\n * transition (offset 1, 2, …) via `observe` — together they reproduce the\n * \"every transition including boot\" stream the single `observe(msg | null)`\n * channel used to carry, now that `observe`'s `msg` is total (#47). The registry\n * still assigns its monotonic exclusive offset (boot → 0, first Msg → 1).\n * Returns a cleanup that detaches both subscriptions.\n *\n * Typed structurally against `{ observe, onBoot }` so it accepts any\n * `Runtime<Model, Msg>` without importing the substrate's `Runtime` (types-only\n * boundary).\n */\nexport function driveProjections<Model, Msg extends { type: string }>(\n registry: ProjectionRegistry<Model, Msg>,\n runtime: {\n observe(observer: (msg: Msg, model: Model) => void): () => void;\n onBoot(handler: (model: Model) => void): () => void;\n },\n): () => void {\n // Boot first → the registry's pre-incremented counter lands it on offset 0,\n // exactly as the old `dispatch(null, model)` boot observe did.\n const unboot = runtime.onBoot((model) => {\n registry.dispatch(null, model);\n });\n const unobserve = runtime.observe((msg, model) => {\n registry.dispatch(msg, model);\n });\n return () => {\n unobserve();\n unboot();\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// rebuildProjection — replay a Msg/Model stream into a fresh runner.\n//\n// The rebuild operation (projections.md \"a view is rebuildable\"): start a fresh\n// runner (offset 0, view = initial) and present the ordered stream from the\n// start. The result equals the live view by construction — both are the same\n// pure `apply` fold over the same ordered updates. The exclusive-offset guard\n// makes the replay safe even if the stream overlaps an already-applied prefix.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Rebuild a projection's view by folding an ordered event stream from the\n * `NoOffset` start. The runner is seeded to `projection.initial` (the implicit\n * boot / `NoOffset` state), then each `updates[i]` is presented at offset\n * `i + 1` — so the FIRST event (`updates[0]`) lands at offset 1 and is folded,\n * exactly mirroring the live driver, which assigns the first applied `Msg`\n * offset 1 (the boot observe is offset 0 and never enters this list). Returns\n * the rebuilt runner whose `view()` equals the live runner's view for the same\n * ordered stream.\n *\n * Pass the ordered events only; do NOT prepend a boot (`msg === null`) entry.\n * With the `i + 1` offsets a leading boot entry would be applied at offset 1\n * like any event (the seed already covers the `NoOffset` start), not skipped —\n * the offset-0 skip that drops the boot is the live driver's, not this fold's.\n *\n * This is the rebuild-equivalence oracle: a view cleared and replayed from the\n * stream equals the live view.\n */\nexport function rebuildProjection<Model, Msg extends { type: string }, View>(\n projection: Projection<Model, Msg, View>,\n updates: readonly { readonly msg: Msg | null; readonly model: Model }[],\n): ProjectionRunner<Model, Msg, View> {\n const runner = runProjection(projection, 0);\n updates.forEach((u, i) => {\n // Present at offset `i + 1`, NOT `i`: the runner's stored offset starts at 0\n // (the `NoOffset` seed) and `present` skips any update with `offset <= 0`, so\n // an offset-`i` first event (offset 0) would be silently dropped. Offsetting\n // by one starts the stream at offset 1 — the live driver's first-Msg offset —\n // so `updates[0]` is folded and the rebuild mirrors the live view.\n runner.present({ msg: u.msg, model: u.model, offset: i + 1 });\n });\n return runner;\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @demlik/tea/do — `stepHost`, the HTTP-PULL control carrier (#92).\n *\n * ## Carrier selection — pick by WHO INITIATES the message\n *\n * `stepHost` is a SIBLING to {@link acceptCommandSocket}, never a replacement.\n * Both carriers commonly run together in ONE Durable Object; choose between\n * them per *plane*, by who sends the next message:\n *\n * - **Pull (this file — `stepHost` over `/step`)** — the CONTROL plane. One\n * driver (the hands) initiates every message: it asks for the next tool,\n * runs it, posts the result, asks again. There is exactly one outstanding\n * question at a time and the DO is COLD between steps — nothing is owed\n * while no request is in flight. Hibernation is the resting state, not a\n * failure: each POST wakes the DO, advances the run one step from its\n * durable checkpoint, and lets it hibernate again.\n * - **Push (`acceptCommandSocket` / `sseHub` — WS + SSE)** — the VIEW /\n * fan-out plane. The DO initiates UNSOLICITED messages: broadcast a frame\n * to every connected client, stream a live dashboard, push an event no\n * client asked for. A long-lived socket is the right carrier because the\n * sender is the DO, not a polling client.\n *\n * The rule in one line: **pull when the client drives a cold-between-steps\n * control loop; push when the DO drives unsolicited fan-out.** A hibernating\n * agent whose only traffic is \"run the next tool\" wants pull — that is the case\n * a production consumer adopted this carrier for.\n *\n * ## Why pull is hibernation-correct\n *\n * The WS push carrier keeps a socket open across steps; if the DO evicts, the\n * socket dies (or, with #91's `acceptDurableCommandSocket`, survives but still\n * holds an in-flight round-trip the heap forgot). The pull carrier holds NO\n * cross-step state in the heap. Every `/step` POST is a FRESH request that:\n *\n * 1. **validates** the per-run capability token (constant-time compare),\n * 2. **re-arms** the eviction-safe give-up alarm via `ctx.storage.setAlarm`\n * (the DO-native alarm API — the `do_alarm` Sub was dropped, see the\n * `host.ts` header; the alarm is a host concern, not a machine Sub),\n * 3. **settles** the posted tool result IDEMPOTENTLY through\n * `idempotent-intake` — a re-POSTed result is absorbed exactly once and\n * the duplicate replays the same next step,\n * 4. **resumes** the run from its durable checkpoint (the #68 event-sourced\n * store / #91 in-flight ledger) by dispatching the settle and running to\n * DISPATCH-QUIESCENCE,\n * 5. **returns** the next step (the next tool call) — or the terminal output\n * read off `runtime.result()` — and lets the DO HIBERNATE.\n *\n * No warm loop is held between responses. The durable checkpoint IS the loop's\n * continuation; the heap is disposable.\n *\n * ## It is ASSEMBLY over bricks tea already ships\n *\n * - `idempotent-intake` (`createIntake`) — the dedupe-by-key receive-once\n * settle. The posted result's `callId` is the idempotency key, so a\n * re-POST lands as a duplicate and replays the cached decision.\n * - `poller` primitives (`retry-backoff`) — the hands-side {@link runStepLoop}\n * pull loop: absolute deadline + backoff + dedupe-by-callId.\n * - the #68/#91 durable checkpoint — read by the consumer's `resume` callback\n * (it dispatches the settle into the runtime, whose `doEventSourcedStore`\n * rebuilt state from the log on this cold wake).\n *\n * This file owns NONE of those primitives — it wires them into the `/step`\n * request/response contract and the token lifecycle.\n */\n\nimport { createIntake } from \"../internal/idempotency/idempotent-intake/index\";\nimport {\n defaultRetryPolicy,\n initRetry,\n nextDelayMs,\n type RetryPolicy,\n type Rng,\n recordFailure,\n shouldRetry,\n} from \"../retry-backoff/index\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// The `/step` request/response envelope (#92.1).\n//\n// The control plane's wire contract. A request carries the capability token,\n// the runId, and the RESULT of the previously-returned step (null on the first\n// request, before any step has been handed out). A response carries the NEXT\n// step — the next tool call to execute — or a terminal `{ done, output }`.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The tool result the hands POST back for the step they just executed. `callId`\n * matches the step the previous response handed out (it is BOTH the correlation\n * id and the idempotency key — a re-POST carries the same `callId` and is\n * absorbed once). `result` is the opaque tool output `R`.\n */\nexport interface StepResult<R> {\n readonly callId: string;\n readonly result: R;\n}\n\n/**\n * A `/step` request. `token` is the per-run capability (validated\n * constant-time). `runId` names the run. `posted` is the result of the\n * previously-returned step, or `null` on the FIRST request (no step has been\n * handed out yet, so there is nothing to settle).\n */\nexport interface StepRequest<R> {\n readonly token: string;\n readonly runId: string;\n readonly posted: StepResult<R> | null;\n}\n\n/**\n * The next tool call to execute — the \"continue\" arm of a `/step` response.\n * `callId` is the correlation/idempotency id the hands echo back in the next\n * request's `posted`. `tool` + `input` describe the call (opaque to the host).\n */\nexport interface NextStep<I> {\n readonly callId: string;\n readonly tool: string;\n readonly input: I;\n}\n\n/**\n * A `/step` response — a discriminated union on `done`:\n * - `{ done: false, step }` — execute `step`, POST its result, ask again.\n * - `{ done: true, output }` — the run is terminal; `output` is its result.\n *\n * This is the ORIGINAL 2-arm INLINE contract, kept byte-identical: a host that\n * resolves each step inside the held request returns exactly these two shapes.\n * The not-ready arm ({@link StepWorking}) is a SEPARATE, opt-in type reachable\n * only through the defer-resume hook — see {@link DeferredStepResponse}. Keeping\n * `StepResponse` a strict subset is what makes inline adopters unaffected: their\n * `if (r.done) … else r.step` narrowing never sees a third member.\n */\nexport type StepResponse<I, O> =\n | { readonly done: false; readonly step: NextStep<I> }\n | { readonly done: true; readonly output: O };\n\n/**\n * The NOT-READY arm — the run is computing OUT-OF-BAND under a defer-resume host\n * (a production incident: a non-blocking pull carrier must\n * answer a pull with an explicit \"computing, poll again\" instead of holding the\n * request across a multi-second step). The held `/step` request returns this\n * IMMEDIATELY instead of inline-awaiting `engine.resume`; the hands re-poll\n * (optionally after `retryAfterMs`) until the deferred compute lands in the\n * durable checkpoint and a real {@link StepResponse} step/done arm is returned.\n *\n * It is a first-class discriminated member (`working: true`), NOT a hollow\n * `{ done: false }` with an absent/stale `step` — the not-ready state is made\n * representable so it can never be confused with a real pending step.\n */\nexport interface StepWorking {\n readonly done: false;\n readonly working: true;\n /**\n * Advisory earliest re-poll delay as a hint to the hands (ms to wait before\n * the next pull). Purely advisory — a host may omit it and the hands fall back\n * to their own backoff curve.\n */\n readonly retryAfterMs?: number;\n}\n\n/**\n * The 3-arm response a DEFER-RESUME host returns — the inline {@link StepResponse}\n * arms PLUS {@link StepWorking}. A superset of `StepResponse`, so a 2-arm value is\n * assignable to it (the widening is safe for existing transports); discriminate\n * with `if (r.done)` for terminal, then `\"working\" in r` for the not-ready arm,\n * else the step arm.\n */\nexport type DeferredStepResponse<I, O> = StepResponse<I, O> | StepWorking;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Capability token (#92.3) — a per-run bearer secret, constant-time compared.\n//\n// NOT OAuth: `token-refresh` is OAuth-shaped (access/refresh pairs, expiry,\n// rotation) and the WRONG tool here. A `/step` token is a single random secret\n// minted once per run and persisted in DO storage; every `/step` POST for that\n// run must present it. The compare is constant-time so a caller cannot probe\n// the secret byte-by-byte via response timing.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Mint a fresh per-run capability token: 32 random bytes, hex-encoded. Uses the\n * Web Crypto `crypto.getRandomValues` available in the Workers runtime (and in\n * Node's test runtime via the global `crypto`). Persist the returned string in\n * DO storage keyed by runId; hand the SAME string to the hands once, at run\n * start.\n */\nexport function mintRunToken(\n randomBytes: (n: number) => Uint8Array = defaultRandomBytes,\n): string {\n const bytes = randomBytes(32);\n let hex = \"\";\n for (const b of bytes) hex += b.toString(16).padStart(2, \"0\");\n return hex;\n}\n\nfunction defaultRandomBytes(n: number): Uint8Array {\n const out = new Uint8Array(n);\n crypto.getRandomValues(out);\n return out;\n}\n\n/**\n * Constant-time string equality. Returns `true` iff `a` and `b` are\n * byte-for-byte equal, in time that depends ONLY on `a.length` — never on WHERE\n * the first mismatch is. A length mismatch still returns `false`, but the\n * comparison loop always runs `a.length` iterations against a same-length view,\n * so the early-exit timing oracle a naive `===` leaks is closed.\n *\n * The XOR-accumulate idiom: fold every position's difference into one\n * accumulator and test it once at the end. No `break`, no short-circuit.\n */\nexport function constantTimeEqual(a: string, b: string): boolean {\n // Length is not secret (it is bounded + fixed by `mintRunToken`), so folding\n // it into the verdict up front is safe; the loop below still runs a fixed\n // number of iterations for a given `a`, leaking no position information.\n const lengthMatches = a.length === b.length;\n // Compare against `a` itself when lengths differ, so the loop body does the\n // same work every call and the mismatch is carried solely by `lengthMatches`.\n const rhs = lengthMatches ? b : a;\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ rhs.charCodeAt(i);\n }\n return diff === 0 && lengthMatches;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// stepHost (#92.2) — the `/step` handler.\n//\n// The host-layer assembly. The consumer supplies a `StepEngine<R, I, O>` — the\n// run-specific operations the handler orchestrates (token lookup, settle into\n// the runtime, resume to quiescence, read next step / terminal). `stepHost`\n// owns the ORDER (validate → setAlarm → idempotent-settle → resume → return →\n// hibernate), the idempotency, and the HTTP status mapping; the engine owns the\n// run-specific I/O. This is the same dependency-injection shape as\n// `durableCommandCarrier` (hooks) and `createAgentHost` (mappings).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The DO-native alarm slice `stepHost` re-arms. A real `DurableObjectStorage`\n * satisfies this structurally; a test supplies a fake (no live Workers runtime).\n * Only `setAlarm` is needed — the give-up deadline is re-armed on every step so\n * a run abandoned mid-loop (the hands stop POSTing) is eventually evicted by the\n * alarm firing, not left resident forever.\n */\nexport interface AlarmStorage {\n setAlarm(scheduledTime: number): void | Promise<void>;\n}\n\n/** The minimal `ctx` slice `stepHost` reads — just the alarm-bearing storage. */\nexport interface StepCtx {\n readonly storage: AlarmStorage;\n}\n\n/**\n * The run-specific operations `stepHost` orchestrates. The consumer implements\n * these against its runtime + storage; `stepHost` never reaches into the agent\n * machine itself (that lives in `../agent`, off-limits to this carrier).\n */\nexport interface StepEngine<R, I, O> {\n /**\n * The persisted capability token for `runId`, or `null` if the run is\n * unknown. Read from DO storage. `stepHost` compares the request token\n * against this constant-time.\n */\n tokenFor(runId: string): Promise<string | null> | string | null;\n /**\n * Settle the posted tool result into the run, then RESUME from the durable\n * checkpoint: dispatch the settle and run to DISPATCH-QUIESCENCE (e.g.\n * `await runtime.dispatch(settleMsg)` — dispatch drains the follow-up chain\n * by default, #50). Called at most once per distinct\n * `callId` — `stepHost` dedupes re-POSTs via `idempotent-intake` BEFORE\n * calling this, so the side effect runs exactly once. On the first request\n * (`posted === null`) `stepHost` skips settle and calls {@link nextStep}\n * directly to hand out the first step.\n */\n resume(runId: string, posted: StepResult<R>): Promise<void> | void;\n /**\n * Read the next step the run is now waiting on — the next tool call — off the\n * resumed runtime's durable state, or `null` if the run has reached its\n * terminal State (then {@link terminalOutput} supplies the output).\n */\n nextStep(runId: string): Promise<NextStep<I> | null> | NextStep<I> | null;\n /**\n * Read the run's terminal output (off `runtime.result()`). Only called when\n * {@link nextStep} returned `null`.\n */\n terminalOutput(runId: string): Promise<O> | O;\n}\n\n/** Tuning for `stepHost`'s give-up alarm. */\nexport interface StepHostConfig {\n /**\n * How far ahead, in ms, to re-arm the give-up alarm on every step. A run that\n * stops POSTing for longer than this is abandoned and the alarm fires (the\n * consumer's `alarm()` handler tears the run down). Defaults to 5 minutes.\n */\n readonly giveUpAfterMs?: number;\n}\n\n/**\n * The DEFER-RESUME hook — the opt-in seam that drives `engine.resume` OUT of the\n * held `/step` request. A non-blocking host (a non-blocking production audit carrier)\n * cannot inline-await a multi-second step inside the held request, so instead of\n * `stepHost` calling `engine.resume` inline it hands the posted result to this\n * hook to SETTLE-AND-ENQUEUE (record it durably, schedule the compute in a\n * returning activation) and returns the {@link StepWorking} arm PROMPTLY. A later\n * pull re-reads the durable next-step once {@link DeferResumeHook.settled} reports\n * the enqueued compute has landed.\n *\n * Providing this hook is what flips `stepHost` from the 2-arm inline contract to\n * the 3-arm deferred contract — see the `stepHost` overloads. Omit it and the\n * host stays byte-identical inline.\n */\nexport interface DeferResumeHook<R> {\n /**\n * SETTLE-AND-ENQUEUE the posted result to resume OUT of the held request:\n * durably record it and schedule the compute (e.g. `ctx.storage.setAlarm` or a\n * queue) so a returning activation runs `engine.resume`. MUST return promptly —\n * it never awaits the step compute. Called at most once per distinct `callId`\n * (`stepHost` dedupes re-POSTs through `idempotent-intake` first), so a re-POST\n * of the same step does not re-enqueue.\n */\n enqueue(runId: string, posted: StepResult<R>): void | Promise<void>;\n /**\n * Has the enqueued resume for `callId` COMPLETED and been written to the\n * durable checkpoint? `false` ⇒ the held request returns {@link StepWorking}\n * (poll again); `true` ⇒ `stepHost` reads the next step / terminal output off\n * the now-advanced checkpoint, exactly as the inline path does.\n */\n settled(runId: string, callId: string): boolean | Promise<boolean>;\n /**\n * Optional advisory re-poll delay (ms) surfaced on the {@link StepWorking} arm\n * so the hands can back off to the host's expected step latency.\n */\n readonly retryAfterMs?: number;\n}\n\n/**\n * `StepHostConfig` with the defer-resume hook engaged — the presence of\n * `deferResume` is the type-level switch that selects the 3-arm response (see the\n * `stepHost` overloads). Extends the inline config, so the alarm tuning is shared.\n */\nexport interface DeferStepHostConfig<R> extends StepHostConfig {\n readonly deferResume: DeferResumeHook<R>;\n}\n\n/**\n * A structured `/step` outcome `stepHost` returns — the response body plus the\n * HTTP status the consumer's route should send. Kept transport-agnostic (no\n * `Response`) so the same handler is unit-testable without a live `fetch`.\n */\nexport type StepOutcome<I, O> =\n | { readonly status: 200; readonly body: StepResponse<I, O> }\n | { readonly status: 401 | 404; readonly body: { readonly error: string } };\n\n/**\n * The `/step` outcome a DEFER-RESUME host returns — like {@link StepOutcome} but\n * its 200 body is the 3-arm {@link DeferredStepResponse} (it can carry the\n * not-ready arm). Returned only from the `deferResume` overload of `stepHost`;\n * the inline overload keeps the narrower {@link StepOutcome}.\n */\nexport type DeferredStepOutcome<I, O> =\n | { readonly status: 200; readonly body: DeferredStepResponse<I, O> }\n | { readonly status: 401 | 404; readonly body: { readonly error: string } };\n\nconst DEFAULT_GIVE_UP_MS = 5 * 60_000;\n\n/**\n * Build the {@link StepWorking} arm, attaching `retryAfterMs` only when the host\n * supplied one — so the wire body stays minimal (`{ done, working }`) unless a\n * re-poll hint is offered.\n */\nfunction workingBody(retryAfterMs?: number): StepWorking {\n return retryAfterMs === undefined\n ? { done: false, working: true }\n : { done: false, working: true, retryAfterMs };\n}\n\n/**\n * Build the `/step` handler for a DO that drives one or more runs over the pull\n * carrier. Returns `handle(request, now)` — call it from the DO's `/step`\n * route, map the `StepOutcome` to a `Response`, and let the DO hibernate.\n *\n * The handler is STATELESS between calls except for its `idempotent-intake`\n * dedupe ledger, which the consumer should itself persist alongside the run's\n * checkpoint if cross-eviction dedupe of an identical re-POST is required; the\n * in-isolate ledger here dedupes re-POSTs within one activation (the common\n * \"the hands' POST timed out and got retried\" case), and the underlying\n * `resume` is itself idempotent against the durable checkpoint, so a re-POST\n * that crosses an eviction re-settles harmlessly.\n *\n * @param ctx the DO state slice (alarm-bearing storage)\n * @param engine the run-specific operations\n * @param config alarm tuning\n */\n// Overloads (specific → general): a config carrying `deferResume` selects the\n// 3-arm deferred handler; every other call — no config, or alarm-only config —\n// keeps the 2-arm inline handler, byte-and-type-identical to before this change.\nexport function stepHost<R, I, O>(\n ctx: StepCtx,\n engine: StepEngine<R, I, O>,\n config: DeferStepHostConfig<R>,\n): {\n handle(\n request: StepRequest<R>,\n now: number,\n ): Promise<DeferredStepOutcome<I, O>>;\n};\nexport function stepHost<R, I, O>(\n ctx: StepCtx,\n engine: StepEngine<R, I, O>,\n config?: StepHostConfig,\n): {\n handle(request: StepRequest<R>, now: number): Promise<StepOutcome<I, O>>;\n};\nexport function stepHost<R, I, O>(\n ctx: StepCtx,\n engine: StepEngine<R, I, O>,\n config: StepHostConfig | DeferStepHostConfig<R> = {},\n): {\n handle(\n request: StepRequest<R>,\n now: number,\n ): Promise<DeferredStepOutcome<I, O>>;\n} {\n const giveUpAfterMs = config.giveUpAfterMs ?? DEFAULT_GIVE_UP_MS;\n // The opt-in defer-resume hook. Present ⇒ resume runs OUT of the held request\n // and a not-ready pull returns the working arm; absent ⇒ the inline path below\n // runs unchanged. `\"deferResume\" in config` is the runtime switch matching the\n // type-level overload split.\n const deferResume: DeferResumeHook<R> | null =\n \"deferResume\" in config ? config.deferResume : null;\n\n // The receive-once settle ledger. Key = the posted result's `callId`, so a\n // re-POST of the SAME step lands as a duplicate and replays the cached\n // decision instead of re-settling. `R = \"settled\"` — the cached value is just\n // a marker that this callId's settle already happened; the actual next step is\n // re-read off the (idempotent) durable checkpoint either way.\n const intake = createIntake<StepResult<R>, \"settled\">({\n keyOf: (posted) => posted.callId,\n });\n // The intake slice lives in the isolate for this activation. It is a plain\n // serializable value (the consumer MAY persist `intakeState` next to its\n // checkpoint for cross-eviction dedupe); within one activation it absorbs the\n // duplicate POST so `resume` fires at most once per callId here.\n let intakeState = intake.init();\n\n async function handle(\n request: StepRequest<R>,\n now: number,\n ): Promise<DeferredStepOutcome<I, O>> {\n // 1 — VALIDATE the capability token (constant-time). A missing run is 404;\n // a present run with a wrong/missing token is 401. The constant-time\n // compare runs regardless of whether the stored token exists (compare\n // against the empty string) so the 401 path leaks no timing.\n const stored = await engine.tokenFor(request.runId);\n if (stored === null) {\n return { status: 404, body: { error: \"unknown run\" } };\n }\n if (!constantTimeEqual(request.token, stored)) {\n return { status: 401, body: { error: \"invalid token\" } };\n }\n\n // 2 — RE-ARM the eviction-safe give-up alarm via the DO-native alarm API.\n // Every step pushes the deadline out; a run that stops POSTing is\n // eventually evicted when the alarm fires. (The `do_alarm` Sub was\n // dropped — see the `host.ts` header — so this is a direct storage call,\n // not a machine Sub.)\n await ctx.storage.setAlarm(now + giveUpAfterMs);\n\n // 3 — SETTLE the posted result (skip on the first request, where there is\n // nothing to settle). Two shapes, chosen by whether the defer-resume\n // hook is engaged:\n const posted = request.posted;\n if (posted !== null) {\n if (deferResume === null) {\n // INLINE (default, unchanged) — settle + resume idempotently INSIDE the\n // held request. `intake.receive` decides new-vs-duplicate and emits the\n // decision as a Cmd; a genuinely-new callId runs `resume` exactly once, a\n // duplicate replays (no re-settle). Either way step 4 re-reads the next\n // step off the durable checkpoint, so a duplicate POST returns the SAME\n // next step.\n const [nextIntake, cmds] = intake.receive(\n intakeState,\n posted,\n now,\n posted.callId,\n );\n intakeState = nextIntake;\n for (const cmd of cmds) {\n if (cmd.type === \"intake:process\") {\n await engine.resume(request.runId, posted);\n const [completed] = intake.complete(\n intakeState,\n cmd.key,\n \"settled\",\n now,\n );\n intakeState = completed;\n }\n // `intake:replay` (a duplicate) needs no action: `resume` already ran\n // for this callId, and step 4 re-derives the next step below.\n }\n } else {\n // DEFER (opt-in) — resume runs OUT of the held request. Readiness is the\n // gate: if the enqueued resume has already landed in the durable\n // checkpoint, fall through to step 4 and read the next step (identical to\n // the inline tail). If not, (idempotently) enqueue the resume to run in a\n // returning activation and return the WORKING arm — the held request\n // never awaits the step compute (the same production incident). The\n // per-callId enqueue is idempotent host-side, so re-POSTs across cold\n // wakes do not re-enqueue; the in-isolate `intake` ledger is not used on\n // this path because it cannot survive the eviction between pulls.\n const ready = await deferResume.settled(request.runId, posted.callId);\n if (!ready) {\n await deferResume.enqueue(request.runId, posted);\n return { status: 200, body: workingBody(deferResume.retryAfterMs) };\n }\n }\n }\n\n // 4 — RETURN the next step, or the terminal output. Read off the resumed\n // run's durable state — NOT off any warm loop. The DO hibernates after\n // this response; the checkpoint is the continuation.\n const next = await engine.nextStep(request.runId);\n if (next === null) {\n const output = await engine.terminalOutput(request.runId);\n return { status: 200, body: { done: true, output } };\n }\n return { status: 200, body: { done: false, step: next } };\n }\n\n return { handle };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// runStepLoop (#92.4) — the hands-side pull loop over `/step`.\n//\n// The thin client the HANDS run: POST `/step` → get the next tool → execute it\n// (consumer callback) → POST the result → repeat until terminal. Built on the\n// `poller` primitives (`retry-backoff`) — absolute deadline + backoff + dedupe\n// by callId — exactly the loop the poller knob composes internally, lifted to a\n// standalone async driver (the hands are not a TEA machine; they drive one).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The `/step` transport the hands POST through (injected; faked in tests).\n * Returns the 3-arm {@link DeferredStepResponse} — a defer-resume host may answer\n * with the not-ready arm, which {@link runStepLoop} re-polls. An inline host's\n * 2-arm transport (`=> Promise<StepResponse>`) is assignable here (the narrower\n * return widens), so existing inline adopters are unaffected.\n */\nexport type StepTransport<R, I, O> = (\n request: StepRequest<R>,\n) => Promise<DeferredStepResponse<I, O>>;\n\n/** Execute one tool call and produce its result (the consumer's hands). */\nexport type ExecuteStep<I, R> = (step: NextStep<I>) => Promise<R> | R;\n\n/** Tuning for {@link runStepLoop}. */\nexport interface RunStepLoopConfig {\n /**\n * Absolute wall-clock deadline (ms epoch). The loop stops asking once `now()`\n * reaches it, surfacing a `deadline_exceeded` outcome. Mirrors the poller's\n * absolute-deadline contract (a resumed loop honors the SAME moment).\n */\n readonly deadlineMs: number;\n /** Backoff policy for FAILED `/step` POSTs. Defaults to `defaultRetryPolicy`. */\n readonly retry?: RetryPolicy;\n /** Clock, injected for determinism. Defaults to `Date.now`. */\n readonly now?: () => number;\n /** `(ms) => Promise` sleep, injected for determinism. Defaults to real sleep. */\n readonly sleep?: (ms: number) => Promise<void>;\n /** Backoff jitter RNG, injected for determinism. Defaults to `Math.random`. */\n readonly rng?: Rng;\n}\n\n/** The terminal outcome of a full {@link runStepLoop} drive. */\nexport type StepLoopOutcome<O> =\n | { readonly kind: \"done\"; readonly output: O }\n | { readonly kind: \"deadline_exceeded\" }\n | { readonly kind: \"gave_up\"; readonly error: unknown };\n\n/**\n * Drive the pull loop to completion. Starting from `{ posted: null }`, POST\n * `/step`, and for each non-terminal response execute the step and POST its\n * result, until the response is terminal (`done: true`) — then return its\n * output.\n *\n * Resilience composed from `poller`'s primitives:\n * - **absolute deadline** — `config.deadlineMs`. Before each POST the loop\n * checks `now() >= deadlineMs` and stops with `deadline_exceeded` rather\n * than asking forever.\n * - **backoff** — a FAILED POST (transport reject) is retried on the\n * `retry-backoff` curve; once `shouldRetry` is exhausted the loop stops\n * with `gave_up`. A success resets the retry counter.\n * - **dedupe** — a step whose `callId` was already executed in THIS drive is\n * not re-executed; its prior result is re-POSTed. Closes the \"the host\n * re-handed the same step after a duplicate settle\" path (the host side is\n * idempotent; the hands match it).\n * - **not-ready re-poll** — a {@link StepWorking} arm (a defer-resume host\n * computing the step out-of-band) is not a step: the loop re-POSTs the SAME\n * `posted` after a backoff (honoring the host's advisory `retryAfterMs`),\n * until a real step / done arrives. Bounded only by the absolute deadline —\n * an inline host never returns this arm, so its drive is unchanged.\n */\nexport async function runStepLoop<R, I, O>(\n token: string,\n runId: string,\n transport: StepTransport<R, I, O>,\n execute: ExecuteStep<I, R>,\n config: RunStepLoopConfig,\n): Promise<StepLoopOutcome<O>> {\n const policy = config.retry ?? defaultRetryPolicy;\n const now = config.now ?? Date.now;\n const sleep = config.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n const rng = config.rng;\n\n // Dedupe-by-callId for THIS drive: the result already produced for a callId,\n // so a re-handed step re-POSTs the cached result instead of re-executing.\n const executed = new Map<string, R>();\n // The result to POST on the NEXT request (null on the first request).\n let posted: StepResult<R> | null = null;\n // Consecutive not-ready (\"working\") polls, driving the re-poll backoff curve.\n // Reset on any real step / terminal response. Bounded by the absolute deadline,\n // NOT by `maxAttempts` — a host may legitimately compute for many polls.\n let pollRetry = initRetry();\n\n for (;;) {\n if (now() >= config.deadlineMs) return { kind: \"deadline_exceeded\" };\n\n // POST `/step` with backoff on transport failure.\n let response: DeferredStepResponse<I, O>;\n let retry = initRetry();\n for (;;) {\n try {\n response = await transport({ token, runId, posted });\n break;\n } catch (error) {\n retry = recordFailure(retry, error);\n if (!shouldRetry(retry, policy)) return { kind: \"gave_up\", error };\n if (now() >= config.deadlineMs) return { kind: \"deadline_exceeded\" };\n await sleep(nextDelayMs(retry, policy, rng));\n }\n }\n\n if (response.done) return { kind: \"done\", output: response.output };\n\n // NOT-READY arm — a defer-resume host is computing the step OUT-OF-BAND. Do\n // NOT execute anything: re-poll with the SAME `posted` (idempotent re-POST)\n // after a backoff, until the host yields a real step / done. Honor the host's\n // advisory `retryAfterMs` when present, else fall back to the poll backoff\n // curve. The absolute deadline is the only terminal bound here.\n if (\"working\" in response) {\n pollRetry = recordFailure(pollRetry, \"working\");\n if (now() >= config.deadlineMs) return { kind: \"deadline_exceeded\" };\n await sleep(response.retryAfterMs ?? nextDelayMs(pollRetry, policy, rng));\n continue;\n }\n pollRetry = initRetry(); // a real step resets the poll backoff\n\n // Execute the next step (dedupe: a callId already run in this drive re-POSTs\n // its cached result rather than executing twice).\n const step = response.step;\n const cached = executed.get(step.callId);\n const result = cached !== undefined ? cached : await execute(step);\n if (cached === undefined) executed.set(step.callId, result);\n posted = { callId: step.callId, result };\n }\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @packageDocumentation\n * @demlik/tea/do — Durable Object adapter for `@demlik/tea`.\n *\n * The integration is built on ONE transport model and one durability primitive:\n *\n * 1. `doStore<S>(storage, parse, key?)` — `Store<S>` impl backed by\n * `DurableObjectStorage`. JSON-stringifies on save, JSON-parses on load\n * and returns `unknown` (invariant-8 boundary); the substrate then calls\n * `migrate(raw)` (which forwards to `parse`) to turn it into `S`, `null`\n * or a refusal. Returns `null` (typed as `unknown`) when the key is\n * absent. Structural JSON malformation throws at load, and `run()`'s boot\n * path turns that into a `StoreRefusedError` on `ready`. A shape\n * mismatch is `parse`'s to answer with `refuse(reason)` (#316).\n *\n * 2. `deferredGateway` + `createAgentHost` (from `./host`) — THE transport for\n * a DO-hosted agent. A `createAgent().toMachine()` machine owns exactly one\n * Sub type, `DeadlinesSub` (the agent's own retry + watchdog timers); it has\n * no DO-native Sub variant to compose, and it needs none. The gateway owns\n * each deferred tool round-trip as a Promise the interpret cell awaits:\n * - Inbound WebSocket frames are bridged straight into `gateway.settle(...)`\n * from the DO's `webSocketMessage` lifecycle method — a direct dispatch,\n * NOT a Sub.\n * - The per-tool deadline is a `setTimeout` inside the gateway, NOT a DO\n * alarm.\n * One model: the transport lives outside the Sub system, bridged into\n * `dispatch` by the gateway. See `./host`'s module doc for the full rationale\n * and `.patterns/tea/durable-actors.md` for the host-layer north star.\n *\n * (Historical note: this module previously also shipped `do_ws` / `do_alarm`\n * Subs + a `doSubscribe` registry. Because `toMachine` fixes the agent machine's\n * Sub type to `DeadlinesSub`, those Subs could never union into an agent host's\n * Sub set — they were structurally unusable by the flagship consumer and had no\n * callers. They were dropped in favor of the gateway as the single transport.)\n */\n\nimport type { DeletableStore, FencedStore, Migrated } from \"../index\";\nimport { StoreConflictError } from \"../index\";\n\n// Durable pending-effects ledger (ADR 0003 primitive #1 — durable effects).\n// A pure fold over `effect_owed` / `effect_confirmed` events (NOT a side\n// table): owed adds, confirmed removes, and the surviving entries re-emit on\n// activation, idempotent by monotonic delivery id. Persist the two events into\n// the same log `doEventSourcedStore` appends and the ledger rebuilds for free.\nexport {\n applyEffectEvent,\n type DeliveryId,\n type EffectConfirmed,\n type EffectLedgerEvent,\n type EffectOwed,\n emptyLedger,\n foldLedger,\n isOwed,\n type OwedEffect,\n type PendingEffectsLedger,\n type PendingEffectsRecorder,\n pendingEffectsLedger,\n survivingEffects,\n} from \"./durable-effects\";\n// The first-class durable-timer / alarm-carrier (#180). Every native-DO grain\n// (raft's election/heartbeat deadline, vortex's game tick) hand-rolled the same\n// compute-next-deadline → `setAlarm` → `alarm()` → fire → re-arm loop after the\n// `do_alarm` Sub was dropped (see the historical note above). `durableTimer`\n// owns that loop in ONE tested place: idle-when-no-work (`nextDeadline` → null),\n// eviction-safe across hibernation (the deadline is a pure function of persisted\n// state, re-armed on cold wake), alarm injected as the same minimal\n// `AlarmStorage` port `stepHost` takes. See `./durable-timer`.\nexport {\n type DurableTimer,\n type DurableTimerConfig,\n durableTimer,\n} from \"./durable-timer\";\n// Opt-in event-sourcing persistence mode. `doStore` below stays the DEFAULT\n// (snapshot-only, byte-for-byte unchanged); `doEventSourcedStore` is the\n// explicit alternative that appends each Msg to a log, snapshots periodically,\n// and rebuilds state by folding the log on the latest snapshot at activation.\nexport {\n doEventSourcedStore,\n type EventLogRange,\n type EventSourcedOptions,\n type EventSourcedStore,\n type PersistedEvent,\n} from \"./event-sourced-store\";\n// The minimum-viable DO HOST for a `createAgent` runtime — the deferred-tool\n// gateway, auto-boot, WS accept + inbound bridge, runtime→SSE plumbing, the\n// terminal-output capture. Kept in a sibling file so this\n// module stays the durability + subs surface; the host re-exports through the\n// same `@demlik/tea/do` subpath the consumer already imports `doStore` from.\nexport {\n type AgentHost,\n type AgentHostConfig,\n acceptCommandSocket,\n acceptDurableCommandSocket,\n agentIsResumable,\n autoBoot,\n bootResume,\n broadcast,\n broadcastHibernatable,\n createAgentHost,\n type DeferredGateway,\n type DurableCommandCarrier,\n deferredGateway,\n durableCommandCarrier,\n durableDeferredGateway,\n type HibernatableCtx,\n type ResumePort,\n reissueSurvivingEffects,\n type SseHub,\n sseFromAgentEvents,\n sseHub,\n sseProjection,\n} from \"./host\";\n// The receiver/effect-side dedup half of durable effects (#227). The ledger\n// above is at-least-once (surviving owed effects re-fire on activation); this\n// brick swallows the duplicate at the external write, keyed by a caller-supplied\n// durable `EffectKey` folded into the Store alongside State (an `effect_applied`\n// marker, pruned by `effect_forgotten` — the ledger's confirm-removes-entry\n// retention). Together: effectively-once. See `./idempotent-effect`.\nexport {\n type AppliedEffects,\n type AppliedEffectsEvent,\n type AppliedEffectsGuard,\n appliedEffects,\n applyAppliedEvent,\n type EffectApplied,\n type EffectForgotten,\n type EffectKey,\n emptyApplied,\n foldApplied,\n type IdempotentEffect,\n type IdempotentOutcome,\n idempotentEffect,\n isApplied,\n} from \"./idempotent-effect\";\n// Generic hibernatable PRESENCE + BROADCAST for a NATIVE (non-agent) DO grain\n// (#181). The `./host` broadcast/accept helpers above are framed for the\n// LLM-agent deferred-tool gateway (`createAgentHost`); these are the decoupled,\n// transport/Msg-agnostic equivalents a game / presence / collab grain uses —\n// `broadcastFrame` takes the socket SET as input (DI-friendly, pure, testable\n// with fakes), and `acceptPresenceSocket` / `registerHibernatableSocket` lift\n// the `WebSocketPair` + `ctx.acceptWebSocket` + `serializeAttachment` boilerplate.\nexport {\n type AttachableSocket,\n acceptPresenceSocket,\n type BroadcastOptions,\n type BroadcastReport,\n broadcastFrame,\n type PresenceCtx,\n type PresenceSocket,\n type PresenceUpgrade,\n presenceCount,\n type RegisterOptions,\n registerHibernatableSocket,\n WS_READY_STATE_OPEN,\n} from \"./presence\";\n// CQRS projections as a first-class seam (#69). One write model, many\n// projections: each is an independent `(events|Model) → view` fold into its own\n// id-scoped read model with an EXCLUSIVE offset (resume + idempotent apply are\n// one unit). `sseHub` (re-exported above) is now expressible as ONE projection\n// via `sseProjection`. See `.patterns/tea-do/projections.md`,\n// `offset-tracking.md`, `delivery-semantics.md`.\nexport {\n driveProjections,\n type Projection,\n type ProjectionErrorContext,\n type ProjectionId,\n type ProjectionOnError,\n type ProjectionRegistry,\n type ProjectionRunner,\n type ProjectionUpdate,\n projectionIdString,\n projectionRegistry,\n rebuildProjection,\n runProjection,\n} from \"./projection\";\n// The HTTP-PULL control carrier (#92) — a hibernation-correct SIBLING to\n// `acceptCommandSocket`. The hands drive a `/step` loop (execute a tool, POST\n// the result, get the next tool); each POST wakes the DO, settles the result\n// idempotently, resumes from the durable checkpoint, returns the next step, and\n// lets the DO hibernate. Carrier-selection rule lives in the `step-host.ts`\n// header: PULL for the cold-between-steps control plane, PUSH (WS/SSE above)\n// for unsolicited view/fan-out. Both commonly run in one DO.\nexport {\n type AlarmStorage,\n constantTimeEqual,\n type DeferResumeHook,\n type DeferredStepOutcome,\n type DeferredStepResponse,\n type DeferStepHostConfig,\n type ExecuteStep,\n mintRunToken,\n type NextStep,\n type RunStepLoopConfig,\n runStepLoop,\n type StepCtx,\n type StepEngine,\n type StepHostConfig,\n type StepLoopOutcome,\n type StepOutcome,\n type StepRequest,\n type StepResponse,\n type StepResult,\n type StepTransport,\n type StepWorking,\n stepHost,\n} from \"./step-host\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// doStore — `Store<S>` over DurableObjectStorage.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_STATE_KEY = \"@@state\";\n\n/**\n * Options for {@link doStore}. Mirrors the optional-bag shape\n * `doEventSourcedStore` already uses for its `parse`/`keys`, so the two DO\n * stores read alike.\n */\nexport interface DoStoreOptions<S> {\n /** Storage key the snapshot cell lives at. Defaults to `@@state`. */\n readonly key?: string;\n /**\n * The serialize half of the boundary (#182) — the INVERSE of `doStore`'s\n * `parse`. Maps the live `S` to a JSON-safe carrier before `JSON.stringify`,\n * so a Model holding a `Map`/`Set`/`Date` (which `JSON.stringify` would\n * silently flatten) round-trips: `parse` reconstructs the rich `S` from the\n * carrier on load. Omit for a plain-JSON Model (the identity default — the\n * `Record`-not-`Map` constraint, unchanged). When present, `parse` MUST\n * accept whatever `serialize` produces.\n */\n readonly serialize?: (state: S) => unknown;\n /**\n * Refuse a second live writer (#143). With `{ fenced: true }` the returned\n * store is a `FencedStore<S>`: the version lives in its own storage cell\n * (`<key>@@version`) and the compare-and-swap runs inside\n * `storage.transaction`, so the read, the compare and the two writes are one\n * atomic unit. Inside a single DO the platform already guarantees one writer;\n * fencing is what refuses a SECOND grain — a stale zombie isolate mid-migration,\n * or a second DO id pointed at the same state by a routing bug.\n */\n readonly fenced?: true;\n}\n\n/**\n * `Store<S>` over `DurableObjectStorage`.\n *\n * `parse` is REQUIRED because DO storage is a real serialization boundary —\n * the bytes that come back through `JSON.parse` are structurally `unknown`,\n * and the caller (the DO that owns `S`) is the only party with enough\n * information to validate the shape. `parse` returns `S` for a shape it\n * recognizes, `null` when nothing was saved (the substrate boots `init` with\n * `loaded = null`), and `refuse(reason)` for saved bytes it cannot read.\n * `parse` must NOT throw per the `Store<S>.migrate` contract; a shape\n * mismatch is a value (a refusal), not a panic.\n *\n * Structural malformations (the stored cell isn't even JSON) still throw at\n * `load()` — that's an infrastructure error, distinct from a schema\n * mismatch. Either way `runtime.ready` rejects with `StoreRefusedError`\n * before `init` runs, so nothing is saved over the old bytes.\n *\n * Concrete bite this closes: when a `State` variant is renamed between\n * deploys, the DO's new code previously received a value typed as the new\n * `S` that held a runtime string from the old `S` — silent undefined\n * behavior in `init`/`update`. After this, the boundary parse refuses the\n * old shape and the DO fails to start instead of running on it or saving\n * a fresh state over it.\n *\n * Serialization sharp edge (#182): `save` JSON-serializes the Model, and\n * `JSON.stringify` does NOT survive a `Map`/`Set`/`Date` (a `Map` stringifies\n * to `{}`, silently losing every entry). A Model that wants a `Map` (the\n * natural \"roster keyed by id\") therefore supplies a `serialize` hook — the\n * INVERSE of `parse` — so the boundary owns both directions: `serialize` turns\n * the live `S` into a JSON-safe carrier on the way out, `parse` reconstructs\n * the rich `S` from that carrier on the way back in. They are a matched pair:\n * `parse(JSON.parse(JSON.stringify(serialize(state))))` must round-trip to a\n * value equal to `state`. Omit `serialize` and the Model must stay plain-JSON\n * (the `Record`-not-`Map` constraint the default imposes, unchanged).\n *\n * @param parse - boundary parse, the deserialize half (`unknown -> S | null`,\n * or `refuse(reason)` for bytes it cannot read).\n * @param keyOrOptions - either the storage key (legacy positional form,\n * defaults to `@@state`) or a {@link DoStoreOptions} bag carrying `key`\n * and/or the `serialize` hook. A bare string is treated as `{ key }`.\n *\n * Either form is a `DeletableStore<S>`: `delete()` removes the state cell and\n * its `<key>@@version` cell in one `storage.delete` call.\n */\nexport function doStore<S>(\n storage: DurableObjectStorage,\n parse: (raw: unknown) => Migrated<S>,\n keyOrOptions: DoStoreOptions<S> & { readonly fenced: true },\n): FencedStore<S> & DeletableStore<S>;\nexport function doStore<S>(\n storage: DurableObjectStorage,\n parse: (raw: unknown) => Migrated<S>,\n keyOrOptions?: string | DoStoreOptions<S>,\n): DeletableStore<S>;\nexport function doStore<S>(\n storage: DurableObjectStorage,\n parse: (raw: unknown) => Migrated<S>,\n keyOrOptions: string | DoStoreOptions<S> = DEFAULT_STATE_KEY,\n): DeletableStore<S> | (FencedStore<S> & DeletableStore<S>) {\n // Normalize the legacy positional `key` string and the options bag to one\n // shape, so the body reads a single `key` + `serialize` regardless of which\n // call form the caller used. Backward-compatible: a string `keyOrOptions`\n // is exactly the old behavior with the identity serializer.\n const options: DoStoreOptions<S> =\n typeof keyOrOptions === \"string\" ? { key: keyOrOptions } : keyOrOptions;\n const key = options.key ?? DEFAULT_STATE_KEY;\n // Identity by default: the Model is serialized as-is (the plain-JSON path).\n const serialize: (state: S) => unknown =\n options.serialize ?? ((state: S): unknown => state);\n const versionKey = `${key}@@version`;\n const base: DeletableStore<S> = {\n async load(): Promise<unknown> {\n const raw = await storage.get<string>(key);\n if (raw === undefined || raw === null) return null;\n // JSON.parse throws on malformed — propagate per PRD throw-semantics.\n // The decoded value is intentionally returned as `unknown`; the\n // substrate's `migrate` callback (forwarded from `parse`) is the\n // boundary parse that turns it into `S`, `null` or a refusal.\n return JSON.parse(raw);\n },\n async save(state: S): Promise<void> {\n // `serialize` is the inverse of `parse` — it maps the live `S` (which may\n // hold a Map/Set) to a JSON-safe carrier before `JSON.stringify` (#182).\n await storage.put(key, JSON.stringify(serialize(state)));\n },\n migrate(raw: unknown): Migrated<S> {\n return parse(raw);\n },\n async delete(): Promise<void> {\n // Both cells, fenced or not: a key written fenced can be opened unfenced,\n // and a version left behind would outlive its state. One multi-key\n // `delete` commits atomically, so no reader sees one cell without the\n // other.\n await storage.delete([key, versionKey]);\n },\n };\n if (options.fenced !== true) return base;\n\n return {\n ...base,\n fenced: true,\n async loadFenced(): Promise<{ raw: unknown; version: number }> {\n const stored = await storage.get<number>(versionKey);\n return { raw: await base.load(), version: stored ?? 0 };\n },\n async saveFenced(state: S, expectedVersion: number): Promise<number> {\n // `storage.transaction` is the DO's own compare-and-swap primitive: the\n // read, the compare and both writes commit as one unit, and a throw from\n // the closure rolls the whole thing back — so a refused save leaves the\n // state cell exactly as the winner wrote it.\n return await storage.transaction(async (txn) => {\n const actual = (await txn.get<number>(versionKey)) ?? 0;\n if (actual !== expectedVersion) {\n throw new StoreConflictError(expectedVersion, actual);\n }\n const next = expectedVersion + 1;\n await txn.put(key, JSON.stringify(serialize(state)));\n await txn.put(versionKey, next);\n return next;\n });\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/do/durable-timer.ts","../../src/do/event-sourced-store.ts","../../src/do/resume.ts","../../src/do/sse.ts","../../src/do/agent-host.ts","../../src/do/presence.ts","../../src/do/command-socket.ts","../../src/do/deferred-gateway.ts","../../src/do/idempotent-effect.ts","../../src/do/projection.ts","../../src/do/step-host.ts","../../src/do/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * `@demlik/tea/do` — the durable-timer / alarm-carrier port (#180).\n *\n * Every native-DO grain that wants a wall-clock tick hand-rolls the SAME\n * eviction-safe pattern: compute the next absolute deadline off the live state,\n * push it to `ctx.storage.setAlarm`, and — when the DO `alarm()` lifecycle hook\n * fires — map that fire back to a Msg, then re-arm off the post-fire state. The\n * `raft/do.ts` election/heartbeat deadline does it (`rearmAlarm` + `onAlarm`),\n * and the vortex arena tick (`room.ts` `armTick` + `onTick`) replicates the\n * exact shape for the 18 Hz game clock. The two old `do_alarm`/`do_ws` Subs were\n * dropped (see `./index`'s historical note) because `toMachine` fixes the agent\n * machine's Sub type to `DeadlinesSub`, so they could never union into a grain's\n * Sub set — leaving the alarm re-arm a HOST concern with no first-class home.\n *\n * {@link durableTimer} is that home: a tiny, transport- and Msg-agnostic helper\n * that owns the three repeated responsibilities and nothing else —\n *\n * 1. **Compute-next-deadline → `setAlarm`.** {@link DurableTimer.rearm} calls\n * the injected {@link DurableTimerConfig.nextDeadline}, and arms the alarm\n * at the returned absolute epoch-ms target.\n * 2. **Idle-when-no-work.** `nextDeadline` returns `null` when there is nothing\n * to schedule (an empty room, a node with no pending election/heartbeat),\n * and `rearm` then arms NOTHING — the DO is allowed to go idle / hibernate\n * instead of spinning the alarm forever. Re-arming is a positive act gated\n * on real work, never an unconditional reschedule.\n * 3. **Fire → Msg → re-arm.** {@link DurableTimer.onAlarm} is the body the DO\n * `alarm()` hook calls: it runs the injected {@link DurableTimerConfig.onFire}\n * (the consumer dispatches its timer Msg there — a `Tick`, a re-derived\n * deadline Msg) and THEN re-arms off the now-advanced state. Re-arm always\n * follows the fire, so a still-active grain keeps ticking and an idle one\n * stops by construction.\n *\n * **Eviction-safe across hibernation.** A DO alarm is the one timer that\n * survives eviction (an isolate `setInterval` dies the moment the isolate\n * sleeps), so the same `durableTimer` re-armed on a cold wake resumes the\n * schedule that a never-evicted instance would have held. The recovery contract\n * is just: after the host rebuilds state (`store.load()` → `replay`), call\n * {@link DurableTimer.rearm} once — it recomputes `nextDeadline` off the\n * recovered state and re-arms (or idles) identically to the live path. The timer\n * holds no in-memory schedule of its own; the deadline is always a pure function\n * of the persisted state, which is what makes the cold-wake re-arm correct.\n *\n * **Dependency-injected / testable.** The alarm arrives as a {@link AlarmStorage}\n * port — the SAME minimal `setAlarm` slice `stepHost` already takes (a real\n * `DurableObjectStorage` satisfies it structurally; a test supplies a fake with\n * no live Workers runtime), mirroring how `event-sourced-store.ts` takes its\n * storage. `nextDeadline` and `onFire` are injected thunks, so the whole timer\n * unit-tests with a faked alarm + a faked clock and no `cloudflare:workers`\n * import. See `.patterns/tea-do/recovery.md` for the cold-wake replay contract\n * the re-arm rides on.\n */\n\nimport type { AlarmStorage } from \"./step-host\";\n\n// Re-export the alarm port under this module too, so a `durableTimer` consumer\n// can name the carrier it injects from the same import as the timer itself. It\n// stays the ONE `setAlarm` slice the `/do` surface defines (no parallel type).\nexport type { AlarmStorage };\n\n/**\n * The construction inputs for {@link durableTimer} — the impure edges the grain\n * injects, kept out of the pure reducer exactly like raft/do's\n * `RaftGrainCtx`/room's `ArenaPorts`.\n */\nexport interface DurableTimerConfig {\n /**\n * The DO alarm carrier. `setAlarm(atMs)` schedules the next fire at an\n * absolute epoch-ms target. A real `DurableObjectStorage` satisfies this\n * structurally; a test supplies a fake. Only `setAlarm` is read — never the\n * whole `DurableObjectStorage`.\n */\n readonly alarm: AlarmStorage;\n /**\n * Compute the next absolute deadline (epoch ms) off the live state, or `null`\n * when there is no work to schedule. Returning `null` is the idle signal:\n * {@link DurableTimer.rearm} arms nothing and the DO is free to hibernate.\n *\n * Called fresh on every `rearm` (including the post-fire re-arm and the\n * cold-wake re-arm), so it MUST read live state / the injected clock each call\n * — that freshness is what keeps the schedule a pure function of the persisted\n * state and the re-arm eviction-safe. Examples:\n * - raft: `node.subs(state, now())[0]?.atMs ?? null` (election XOR heartbeat).\n * - vortex: `connectionCount() > 0 ? now() + TICK_INTERVAL_MS : null`.\n */\n nextDeadline(): number | null | Promise<number | null>;\n /**\n * Run the timer's effect when the alarm fires — the consumer dispatches its\n * timer Msg here (a `Tick`, a re-derived deadline Msg) and persists/broadcasts\n * as its grain requires. {@link DurableTimer.onAlarm} awaits this BEFORE it\n * re-arms, so the re-arm sees the post-fire state.\n */\n onFire(): void | Promise<void>;\n}\n\n/**\n * The activated durable timer. Two methods, both idempotent to call from the\n * host edge:\n * - {@link DurableTimer.rearm} — recompute + (re-)arm or idle. Call it once\n * after cold-wake recovery, and after any state transition that can change\n * whether/when the next deadline should fire.\n * - {@link DurableTimer.onAlarm} — the DO `alarm()` body: fire, then re-arm.\n */\nexport interface DurableTimer {\n /**\n * Recompute {@link DurableTimerConfig.nextDeadline} off the live state and arm\n * the alarm at it — or, when it returns `null`, arm nothing (idle). Safe to\n * call repeatedly; arming the same absolute target twice is a harmless\n * overwrite on the DO alarm API.\n */\n rearm(): Promise<void>;\n /**\n * The body the DO `alarm()` lifecycle hook delegates to: run\n * {@link DurableTimerConfig.onFire}, then {@link DurableTimer.rearm} off the\n * post-fire state. A still-active grain keeps ticking; an idle one (its\n * `nextDeadline` now `null`) stops here.\n */\n onAlarm(): Promise<void>;\n}\n\n/**\n * Build a {@link DurableTimer} over an injected alarm carrier, a next-deadline\n * computation, and a fire handler. The returned timer owns the\n * compute-next-deadline → `setAlarm` → `alarm()` → fire → re-arm loop and holds\n * no schedule of its own (the deadline is always recomputed from live state),\n * which is what makes it eviction-safe across hibernation.\n */\nexport function durableTimer(config: DurableTimerConfig): DurableTimer {\n const { alarm, nextDeadline, onFire } = config;\n const timer: DurableTimer = {\n async rearm() {\n // Re-arm is a POSITIVE act gated on work: a `null` deadline means idle, so\n // we touch the alarm only when there is a concrete target to schedule.\n const at = await nextDeadline();\n if (at !== null) await alarm.setAlarm(at);\n },\n async onAlarm() {\n // Fire first (the consumer dispatches its timer Msg), THEN re-arm off the\n // now-advanced state — so a still-active grain keeps ticking and an idle\n // one stops because its `nextDeadline` has gone `null`.\n await onFire();\n await timer.rearm();\n },\n };\n return timer;\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @demlik/tea/do — event-sourced persistence mode for a Durable Object actor.\n *\n * `doStore` (sibling, `./index`) is the DEFAULT and stays snapshot-only: it\n * writes the whole `S` blob to one cell on every transition and reads it back\n * on boot. That is byte-for-byte unchanged by this file.\n *\n * `doEventSourcedStore` is the OPT-IN alternative. Instead of (only)\n * checkpointing the latest state, it:\n *\n * 1. APPENDS each applied `Msg` to an append-only log in `DurableObjectStorage`\n * (one cell per event, monotonically keyed).\n * 2. Takes a PERIODIC SNAPSHOT every `snapshotEvery` events (count-based\n * retention — mirrors Akka `RetentionCriteria.snapshotEvery`) so the log\n * that must replay on the next activation is bounded. A grain that needs\n * replay bounded by ELAPSED TIME/TICKS rather than raw event count drives\n * `snapshotNow()` instead (or alongside) — see that method.\n * 3. On activation REBUILDS state by FOLDING the log on top of the latest\n * snapshot. The fold starts directly from a snapshot; without one it\n * starts from fresh init. One reducer step per logged Msg follows, with\n * no Cmd handlers executed: **side effects are suppressed on replay**\n * (recovery.md). This is the same primitive `foldEvents` / the recorder\n * use; it is reused here, not reinvented (ADR 0003: \"the fold already\n * exists; wire it to Store\").\n *\n * The \"recovered/ready\" signal (recovery.md `RecoveryCompleted`) is delivered\n * exactly once per activation via `onReady`, EVEN for a fresh actor with an\n * empty log, and is documented to be idempotent (it may fire again on a later\n * activation, so the handler must tolerate repeats).\n *\n * Integration with the cooperating DO:\n *\n * const es = doEventSourcedStore(storage, machine, ctx, {\n * snapshotEvery: 100,\n * parse, // optional boundary parse for the snapshot\n * onReady: (state) => { ... }, // optional, idempotent end-of-recovery hook\n * });\n * const runtime = run(machine, { ctx, store: es.store });\n * // Append every applied Msg to the log. `observe` delivers only applied\n * // Msgs now (#47) — boot has no event, so there is no `null` arm to skip.\n * runtime.observe((msg) => { es.append(msg); });\n * await runtime.ready; // store.load() has folded; onReady has fired once.\n *\n * A time/tick-driven grain (e.g. one that derives simulation ticks between\n * sparse intents) bounds its cold-wake replay by ELAPSED TIME rather than event\n * count by calling `es.snapshotNow()` on its own cadence — every N derived ticks\n * or on a wall-clock interval — so a quiet span that never trips `snapshotEvery`\n * is still snapshotted before its replay tail grows unbounded (#190):\n *\n * // in the grain's tick loop, after the turn's appends have settled:\n * if (tick % SNAPSHOT_EVERY_TICKS === 0) await es.snapshotNow();\n */\n\nimport type { Cmd, Machine, Store, Sub } from \"../index\";\nimport { foldMsgs, replay } from \"../index\";\n\n/** Default cell prefixes. Chosen to sort log keys lexicographically by seq. */\nconst DEFAULT_SNAPSHOT_KEY = \"@@es/snapshot\";\nconst DEFAULT_META_KEY = \"@@es/meta\";\nconst DEFAULT_EVENT_PREFIX = \"@@es/evt/\";\n/** Zero-pad seq numbers so `storage.list({ prefix })` returns them in order. */\nconst SEQ_WIDTH = 16;\n\nfunction eventKey(prefix: string, seq: number): string {\n return prefix + String(seq).padStart(SEQ_WIDTH, \"0\");\n}\n\n/**\n * Persisted snapshot envelope. `seq` is the highest event sequence number the\n * snapshot's state already folds in — events strictly after it are the replay\n * tail. `state` is the folded `S` at that point.\n */\ninterface SnapshotCell<S> {\n readonly seq: number;\n readonly state: S;\n}\n\n/** Persisted meta cell: the highest sequence number ever appended. */\ninterface MetaCell {\n readonly lastSeq: number;\n}\n\n/**\n * Options for {@link doEventSourcedStore}.\n */\nexport interface EventSourcedOptions<S, M> {\n /**\n * Take a snapshot every N appended events (count-based retention, mirrors\n * Akka `RetentionCriteria.snapshotEvery`). A snapshot bounds how many events\n * replay on the next activation — it never changes the fold's result, only\n * its length (recovery.md). Must be >= 1. Default 100.\n */\n readonly snapshotEvery?: number;\n /**\n * Boundary parse for the persisted SNAPSHOT cell (the `S` blob). Returns `S`\n * on a recognized shape, `null` on an unrecognized one, and must NOT throw.\n * Unlike `doStore`'s `parse`, `null` loses nothing: the snapshot is only a\n * shortcut, so a `null` discards it and replays the whole event log from\n * seq 0. Default accepts any non-null, non-array object as `S`.\n */\n readonly parse?: (raw: unknown) => S | null;\n /**\n * Boundary parse for a persisted EVENT (one logged `Msg`). Returns the `M` on\n * a recognized shape; returning `null` DROPS the event from replay (use for a\n * removed Msg variant). Must NOT throw. Default accepts any object carrying a\n * string `type` as `M`.\n */\n readonly parseEvent?: (raw: unknown) => M | null;\n /**\n * End-of-recovery hook (recovery.md `RecoveryCompleted`). Fires EXACTLY ONCE\n * per activation, after the fold completes, carrying the recovered state —\n * even when the log was empty (fresh actor). It may fire again on a later\n * activation, so it MUST be idempotent. Side effects that belong \"once after\n * recovery\" go here, never in the reducer (the reducer re-runs on every\n * replay).\n */\n readonly onReady?: (state: S) => void;\n /** Cell-key overrides (rarely needed; for coexisting actors in one DO). */\n readonly keys?: {\n readonly snapshot?: string;\n readonly meta?: string;\n readonly eventPrefix?: string;\n };\n}\n\n/**\n * One persisted log entry handed to a {@link EventSourcedStore.readEvents}\n * consumer: the monotonic `seq` the event was appended under, paired with the\n * decoded `event`. `seq` is the store's own append counter (the key the event\n * lives under), so it both orders the stream and bounds an {@link EventLogRange}.\n */\nexport interface PersistedEvent<M> {\n /** The append sequence number this event was logged under (1-based, gap-free). */\n readonly seq: number;\n /** The decoded `Msg`, parsed through the store's `parseEvent` boundary. */\n readonly event: M;\n}\n\n/**\n * Optional inclusive bounds for {@link EventSourcedStore.readEvents}. Both ends\n * are inclusive and match against the event's `seq`; omitting an end leaves it\n * unbounded. `{ fromSeq: 5, toSeq: 10 }` yields seqs 5..10; `{}` (or no\n * argument) yields the whole log.\n */\nexport interface EventLogRange {\n /** Lowest `seq` to include (inclusive). Default: the first event. */\n readonly fromSeq?: number;\n /** Highest `seq` to include (inclusive). Default: the last event. */\n readonly toSeq?: number;\n}\n\n/**\n * The handle returned by {@link doEventSourcedStore}: a `Store<S>` to hand to\n * `run(...)`, plus the append + recovery surface the cooperating DO drives.\n */\nexport interface EventSourcedStore<S, M> {\n /**\n * The `Store<S>` to pass as `run(machine, { ctx, store })`. Its `load()`\n * performs the fold (snapshot + log replay); its `save(state)` is the\n * count-based snapshot writer. `migrate` forwards to `parse`.\n */\n readonly store: Store<S>;\n /**\n * Append one applied `Msg` to the log. Call from `runtime.observe` for every\n * non-null msg. Resolves once the event is durably written. Triggers a\n * snapshot when the event count crosses a `snapshotEvery` boundary.\n */\n append(msg: M): Promise<void>;\n /**\n * Take a snapshot NOW, independent of the count-based `snapshotEvery` trigger\n * (a time/tick-based retention trigger, #190). The count-based trigger bounds\n * the replay tail by raw EVENT COUNT; a grain that derives many state\n * transitions per logged event (e.g. simulation ticks between sparse intents)\n * needs the tail bounded by ELAPSED TIME instead — so it calls `snapshotNow()`\n * on its own cadence (every N derived ticks, or on a wall-clock interval) to\n * checkpoint the current folded state without injecting filler events purely\n * to advance the counter.\n *\n * It checkpoints the latest state the substrate handed `save()` at the current\n * highest sequence, then advances the count-based baseline (`snapshotEvery`\n * counts afresh from here, so a manual snapshot defers the next automatic one).\n *\n * Returns `true` if a snapshot was written, `false` on a no-op: nothing has\n * been applied yet (fresh actor), or no new event exists since the last\n * snapshot (already current). Like the count-based path, it pairs the held\n * state with the highest sequence, so the caller MUST invoke it between\n * settled transitions — after the turn's `append()`s have resolved — never\n * mid-transition (the same boundary discipline `append`'s own snapshot relies\n * on). Resolves once the snapshot is durably written.\n */\n snapshotNow(): Promise<boolean>;\n /**\n * Stream the persisted event log in `seq` order, optionally bounded by an\n * inclusive {@link EventLogRange}. This is the PUBLIC read side of the\n * event-sourced write model — the surface replay / projection / audit\n * consumers (CQRS reads, recovery.md / projections.md) read through, instead\n * of mirroring the store's private `@@es/evt/` key convention.\n *\n * The log is APPEND-ONLY and is never truncated by snapshotting (a snapshot\n * only bounds the *replay* tail, never the log), so this yields EVERY event\n * in range — including ones a snapshot already folds. Each entry carries its\n * `seq`, so a consumer can resume from a known offset (`{ fromSeq }`) or read\n * a window (`{ fromSeq, toSeq }`). An empty (or fully out-of-range) log yields\n * nothing. Events whose `parseEvent` returns `null` (a retired Msg variant)\n * are dropped, identical to the recovery fold.\n *\n * Read-only: it never appends, snapshots, or mutates store state, so streaming\n * the log can never perturb the live actor.\n */\n readEvents(range?: EventLogRange): AsyncIterable<PersistedEvent<M>>;\n}\n\nconst defaultParse = <S>(raw: unknown): S | null =>\n raw !== null && typeof raw === \"object\" && !Array.isArray(raw)\n ? (raw as S)\n : null;\n\nconst defaultParseEvent = <M>(raw: unknown): M | null =>\n raw !== null &&\n typeof raw === \"object\" &&\n !Array.isArray(raw) &&\n typeof (raw as { type?: unknown }).type === \"string\"\n ? (raw as M)\n : null;\n\n/**\n * Build an event-sourced `Store<S>` over `DurableObjectStorage`.\n *\n * The substrate calls `store.load()` once at boot — that is where the fold\n * happens — and `store.save(state)` after every transition — that is where the\n * count-based snapshot is taken. The cooperating DO additionally calls\n * `append(msg)` from `runtime.observe` to grow the log.\n *\n * Snapshot recovery folds update directly from the captured State; it does\n * not initialize that State again. With no snapshot, `ctx` supplies the fresh\n * init used to establish the event log's starting State. Live resume commands\n * belong to the runtime's subsequent init over the recovered State.\n */\n// Cmd/Sub are INFERRED type params (`C`/`U`) that DEFAULT to the substrate's\n// base unions, not pinned to them. The fold itself only ever touches `init` +\n// `update`, so the body never depends on the concrete C/U — but pinning the\n// machine param to the base `Cmd` made `interpret` *structurally required* via\n// the `Machine` conditional (`[C] extends [Cmd<never>] ? { interpret? } : {\n// interpret }`): a CMDLESS grain (`Cmd = never`, which legitimately declares no\n// `interpret`) then could not satisfy the param without a cast (#195). Threading\n// `C`/`U` lets TS infer `C = never` for such a grain, relaxing `interpret` to\n// optional exactly as `run`/`replay` already do for a `Cmd = never` machine — so\n// a cmdless machine type-checks with no cast and no no-op `interpret`. The\n// defaults keep inference byte-for-byte identical for a commandful machine (and\n// for the existing `<S, M, Ctx>`-explicit callers, whose C/U fall to the base\n// defaults exactly as before); the machine keeps its precise types at its own\n// definition site.\nexport function doEventSourcedStore<\n S,\n M extends { type: string },\n Ctx,\n C extends Cmd = Cmd,\n U extends Sub = Sub,\n>(\n storage: DurableObjectStorage,\n machine: Machine<S, M, C, U, Ctx>,\n ctx: Ctx,\n opts: EventSourcedOptions<S, M> = {},\n): EventSourcedStore<S, M> {\n const snapshotEvery = opts.snapshotEvery ?? 100;\n if (snapshotEvery < 1 || !Number.isInteger(snapshotEvery)) {\n throw new Error(\n `@demlik/tea/do: snapshotEvery must be an integer >= 1, got ${snapshotEvery}`,\n );\n }\n const parse = opts.parse ?? defaultParse;\n const parseEvent = opts.parseEvent ?? defaultParseEvent;\n const snapshotKey = opts.keys?.snapshot ?? DEFAULT_SNAPSHOT_KEY;\n const metaKey = opts.keys?.meta ?? DEFAULT_META_KEY;\n const eventPrefix = opts.keys?.eventPrefix ?? DEFAULT_EVENT_PREFIX;\n\n // In-memory mirror of the durable seq counter, set during load() and kept in\n // step by append(). Before load() it is unknown; append() lazily reads it.\n let lastSeq: number | null = null;\n // The seq the latest snapshot already folds (so we know the replay tail).\n let snapshotSeq = 0;\n // The latest state the substrate handed us via save(). The substrate calls\n // save(state) BEFORE the observer fires append(msg) for the same transition\n // (run()'s order: state=next → save → reconcile → interpret → observers), so\n // at append(N) time this holds the post-event-N state. The snapshot is taken\n // from the append path (not save) so its recorded `seq` and its `state` agree\n // on the same event boundary — recording seq at save time would lag the state\n // by one event and re-apply that event on recovery.\n let latestState: S | null = null;\n // Idempotency latch for onReady within a single activation.\n let readyFired = false;\n\n async function readMeta(): Promise<number> {\n const raw = await storage.get<string>(metaKey);\n if (raw === undefined || raw === null) return 0;\n const meta = JSON.parse(raw) as MetaCell;\n return typeof meta.lastSeq === \"number\" ? meta.lastSeq : 0;\n }\n\n /**\n * Return the durable `lastSeq`, lazily learning it from meta if a write\n * (`append`/`snapshotNow`) fires before any `load()` in this isolate\n * (defensive; the documented flow loads first). The guard lives here once so\n * `append` and `snapshotNow` can't drift on the append-before-load path.\n */\n async function ensureLastSeq(): Promise<number> {\n if (lastSeq === null) lastSeq = await readMeta();\n return lastSeq;\n }\n\n /**\n * Read the persisted snapshot cell, parsed through the boundary. Returns the\n * folded state + the seq it covers, or `{ state: null, seq: 0 }` when absent\n * or unrecognized (fold then starts from a fresh `init`).\n */\n async function readSnapshot(): Promise<{ state: S | null; seq: number }> {\n const raw = await storage.get<string>(snapshotKey);\n if (raw === undefined || raw === null) return { state: null, seq: 0 };\n // Structural JSON malformation throws here, as in doStore — infra error,\n // not a schema mismatch.\n const cell = JSON.parse(raw) as SnapshotCell<unknown>;\n const parsed = parse(cell.state);\n if (parsed === null) return { state: null, seq: 0 };\n const seq = typeof cell.seq === \"number\" ? cell.seq : 0;\n return { state: parsed, seq };\n }\n\n /**\n * Stream the persisted event log in `seq` order, bounded by the optional\n * inclusive `[fromSeq, toSeq]` range. The single owner of the `@@es/evt/`\n * key convention on the read side — both the public {@link EventSourcedStore.readEvents}\n * and the private fold's `readLog` flow through it, so the prefix/decode rule\n * lives in exactly one place.\n */\n async function* readEvents(\n range: EventLogRange = {},\n ): AsyncGenerator<PersistedEvent<M>> {\n const fromSeq = range.fromSeq ?? Number.NEGATIVE_INFINITY;\n const toSeq = range.toSeq ?? Number.POSITIVE_INFINITY;\n // list() returns keys in lexicographic order; zero-padded seq keys sort by\n // sequence, so this is already seq-ascending. Bounds are matched on the\n // parsed seq (inclusive both ends).\n const rows = await storage.list<string>({ prefix: eventPrefix });\n for (const [key, value] of rows) {\n const seq = Number(key.slice(eventPrefix.length));\n if (!Number.isFinite(seq) || seq < fromSeq || seq > toSeq) continue;\n const decoded = parseEvent(JSON.parse(value));\n // A `null` decode drops a retired Msg variant (documented in parseEvent).\n if (decoded !== null) yield { seq, event: decoded };\n }\n }\n\n /** Read the logged events strictly after `afterSeq`, in seq order. */\n async function readLog(afterSeq: number): Promise<M[]> {\n // The replay tail is the public reader bounded to seqs > afterSeq — one\n // shared source for the key convention, no second `storage.list` walk.\n const events: M[] = [];\n for await (const { event } of readEvents({ fromSeq: afterSeq + 1 })) {\n events.push(event);\n }\n return events;\n }\n\n const store: Store<S> = {\n async load(): Promise<unknown> {\n const { state: snapshot, seq: snapSeq } = await readSnapshot();\n snapshotSeq = snapSeq;\n lastSeq = await readMeta();\n\n const events = await readLog(snapSeq);\n\n const state =\n snapshot === null\n ? replay(machine, { msgs: events, ctx }).state\n : foldMsgs(machine, snapshot, events);\n\n // RecoveryCompleted: fire once per activation, AFTER the fold, even when\n // the log + snapshot were both empty (fresh actor). Idempotent within the\n // activation via `readyFired`; may fire again on a future activation.\n if (!readyFired) {\n readyFired = true;\n opts.onReady?.(state);\n }\n\n // Returned as `unknown` and re-validated through `migrate` (= parse), same\n // boundary discipline as doStore. The substrate feeds it to `init` as\n // `loaded`, so the live `init` rehydrate branch runs on the folded state.\n return state;\n },\n\n async save(state: S): Promise<void> {\n // The substrate calls save() after every transition, BEFORE the observer\n // fires append() for that same transition. We don't snapshot here — we\n // only hold the latest state so the append path can pair it with the\n // freshly-advanced seq (see `latestState`). Holding (not snapshotting)\n // here is what keeps the snapshot's seq and state on the same boundary.\n latestState = state;\n },\n\n migrate(raw: unknown): S | null {\n return parse(raw);\n },\n };\n\n /**\n * Durably write the snapshot cell pairing `seq` with `state`, then advance the\n * in-memory `snapshotSeq` so the recovery fold replays only events strictly\n * after `seq`. Shared by the count-based path (in `append`) and the\n * time/tick-based path (`snapshotNow`) so both record the boundary the one,\n * identical way — a snapshot bounds replay length; it never changes the fold's\n * result (snapshotting.md).\n */\n async function writeSnapshot(seq: number, state: S): Promise<void> {\n const cell: SnapshotCell<S> = { seq, state };\n await storage.put(snapshotKey, JSON.stringify(cell));\n snapshotSeq = seq;\n }\n\n async function append(msg: M): Promise<void> {\n const seq = (await ensureLastSeq()) + 1;\n lastSeq = seq;\n // Write the event then advance the meta cell. DO storage is transactional\n // within a single `alarm`/fetch turn; both puts land together.\n await storage.put(eventKey(eventPrefix, seq), JSON.stringify(msg));\n await storage.put(\n metaKey,\n JSON.stringify({ lastSeq: seq } satisfies MetaCell),\n );\n\n // Count-based retention (Akka `RetentionCriteria.snapshotEvery`). When the\n // events since the last snapshot reach `snapshotEvery`, checkpoint the\n // post-this-event state under THIS seq. `latestState` was set by the\n // save(state) that ran earlier in this same transition, so its state folds\n // exactly events 1..seq — recording `seq` here is consistent, and the\n // recovery fold replays only events strictly after `seq`. A snapshot bounds\n // replay length; it never changes the fold's result (snapshotting.md).\n if (seq - snapshotSeq >= snapshotEvery && latestState !== null) {\n await writeSnapshot(seq, latestState);\n }\n }\n\n /**\n * Time/tick-based snapshot trigger (#190) — checkpoint NOW, independent of the\n * count-based `snapshotEvery`. See {@link EventSourcedStore.snapshotNow}.\n */\n async function snapshotNow(): Promise<boolean> {\n const seq = await ensureLastSeq();\n // No-op when there is nothing new to checkpoint: a fresh actor with no\n // applied state yet (`latestState === null`), or no event appended since the\n // last snapshot (`seq <= snapshotSeq`). Both keep the snapshot's `seq`\n // and `state` paired on the same boundary the count-based path guarantees.\n if (latestState === null || seq <= snapshotSeq) return false;\n await writeSnapshot(seq, latestState);\n return true;\n }\n\n return { store, append, snapshotNow, readEvents };\n}\n","/**\n * Cold-wake resume — the boot/hibernation re-fire path: the generalized\n * `bootResume` port, its agent specialization `autoBoot`, and the wake-time\n * re-emit of surviving tool round-trips. See `./host` for the transport-model\n * rationale.\n */\n\nimport {\n type AgentMachineMsg,\n type AgentState,\n agentBootMsg,\n status,\n} from \"../agent/index\";\nimport type { RunHandle } from \"../index\";\nimport type { DurableCommandCarrier } from \"./deferred-gateway\";\n\n// Optional host-message resume. Machines that need a first move ahead of Sub\n// messages return resume Cmds and an in-flight State directly from init.\n\n/**\n * The typed cold-wake resume port `bootResume` fires through — the agent's\n * `AgentBootPort`, generalized to any DO-hosted machine.\n *\n * - `isResumable(state)` — true iff the rehydrated State is mid-loop and needs a\n * resume dispatch. False on a fresh boot, which makes `bootResume` a no-op.\n * - `resumeMsg(now)` — the TYPED constructor for the single resume Msg (never a\n * hand-built `{ type: … }` literal); the `agentBootMsg` shape lifted out so a\n * non-agent grain supplies its own boot Msg constructor.\n */\nexport interface ResumePort<S, M extends { type: string }> {\n readonly isResumable: (state: S) => boolean;\n readonly resumeMsg: (now: number) => M;\n}\n\n/**\n * After `runtime.ready`, derive the single resume Msg from the rehydrated State\n * via `port` and dispatch it exactly once — the generalized AgentBoot. On a\n * fresh (non-rehydrated) machine `port.isResumable` is false and this is a\n * no-op. Call once right after building the runtime; it standardizes the\n * hand-written `if (isResumable(state)) await runtime.dispatch(bootMsg(now()))`\n * every DO-hosted machine otherwise re-derives.\n *\n * Use this when the host deliberately supplies a Msg after boot, including\n * the existing agent boot protocol. Subs are already open after `ready`; this\n * helper cannot order resume ahead of their Msgs. Prefer restored init Cmds\n * when the machine owns its first move, and do not resume the same work twice.\n *\n * `await ready` is the single boot gate (issue #45): `getState()` is total only\n * on the booted handle, so state cannot be inspected before boot populates\n * it. Takes any engine's {@link RunHandle}. `now` is injected (the sole boot clock read) so a test can pin it.\n */\nexport async function bootResume<\n S,\n M extends { type: string },\n E extends { type: string } = never,\n>(\n booting: RunHandle<S, M, E>,\n port: ResumePort<S, M>,\n now: () => number = Date.now,\n): Promise<void> {\n const runtime = await booting.ready;\n if (port.isResumable(runtime.getState())) {\n await runtime.dispatch(port.resumeMsg(now()));\n }\n}\n\n/**\n * True iff an agent slice loaded from storage is mid-loop (running + awaiting\n * tools) — the resumable case `agent_boot` re-fires.\n *\n * The resumability question IS \"is the run suspended on tools?\" — so this\n * delegates to the agent's own typed `status` channel (issue #49) rather than\n * re-deriving the private `run.phase` / `conversation.awaiting` shape by hand.\n * The host no longer leaks the agent's internal slice structure; a change to\n * `Awaiting` surfaces as a compile error in `status`, not a silent break here.\n */\nexport function agentIsResumable<\n Stage,\n P extends string,\n O extends Record<P, unknown>,\n R,\n>(state: AgentState<Stage, P, O, R>): boolean {\n return status(state).kind === \"suspended\";\n}\n\n/**\n * The AGENT specialization of {@link bootResume}: after\n * `runtime.ready`, self-dispatch `agent_boot` iff the rehydrated slice is\n * resumable, a no-op on a fresh DO. Wires the agent slice's `agentIsResumable`\n * predicate + the agent-owned `agentBootMsg` typed port into the\n * generalized helper, so the resume dispatch lives in ONE place — see\n * `bootResume`'s docblock for the post-boot ordering boundary.\n *\n * `now` is injected (the host's only clock read for boot) so a test can pin it.\n */\nexport async function autoBoot<\n Stage,\n P extends string,\n O extends Record<P, unknown>,\n R,\n E extends { type: string } = never,\n>(\n booting: RunHandle<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R>, E>,\n now: () => number = Date.now,\n): Promise<void> {\n await bootResume(\n booting,\n { isResumable: agentIsResumable, resumeMsg: agentBootMsg },\n now,\n );\n}\n\n/**\n * Re-fire every round-trip a rehydrated actor still owes, on wake. Given a\n * durable carrier whose recorder was rehydrated from the persisted ledger\n * events (e.g.\n * `durableCommandCarrier(deferredGateway(), pendingEffectsLedger({ events }),\n * …)` on a wake), re-fire every owed-but-unconfirmed round-trip via\n * `reissue(callId)` — the consumer's \"re-send the command frame for this\n * `callId`\" callback. The survivors are read straight off the carrier's own\n * recorder (`recorder.surviving()`), which folded the restore log at\n * construction — the carrier is the single source of the in-flight set, so the\n * caller does not re-fold the log here.\n *\n * Idempotent by construction:\n * - the ledger's monotonic `deliveryId` is the dedup key the RECEIVER uses, so\n * a late/duplicate reply for an already-resolved round-trip is absorbed\n * (at-least-once delivery — see `survivingEffects`);\n * - re-issuing the SAME survivor twice within one activation re-sends once —\n * if `reissue` routes through the carrier's `await`, the gateway returns the\n * open Promise for an in-flight `callId` without re-firing `send`.\n *\n * Returns the `callId`s re-emitted (oldest-first by id) — the host can log /\n * assert the re-emit set. On a fresh or fully-confirmed carrier this is empty\n * (nothing re-fired).\n *\n * Call this once, right after the runtime's boot gate resolves (alongside\n * {@link autoBoot}): `autoBoot` re-fires the agent's own `agent_boot`;\n * `reissueSurvivingEffects` re-fires the in-flight TOOL round-trips. Together\n * they restore a suspended run's full in-flight state after a wake.\n */\nexport function reissueSurvivingEffects<R>(\n carrier: DurableCommandCarrier<R>,\n reissue: (callId: string) => void,\n): readonly string[] {\n const reemitted: string[] = [];\n for (const { effect } of carrier.recorder.surviving()) {\n reissue(effect.callId);\n reemitted.push(effect.callId);\n }\n return reemitted;\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * The runtime→SSE plumbing — the hub, its projection expression, and the\n * semantic-event adapter. See `./host` for the transport-model rationale.\n */\n\nimport { AGENT_EVENT_TYPES, type AgentEvent } from \"../agent/index\";\nimport type { Projection } from \"./projection\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// sseStream — runtime→SSE adapter (seam D).\n//\n// The sink-set + ReadableStream + encoder + cancel is generic; only the\n// Msg→event mapping is domain. Sibling-in-spirit to `@demlik/tea/react`: the\n// host owns the plumbing, the consumer supplies the mapping.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A set of SSE sinks plus the plumbing to fan an event out to all of them and\n * to open a `text/event-stream` Response wired to a fresh sink.\n *\n * The consumer:\n * 1. holds one `SseHub<E>` per DO,\n * 2. calls `hub.emit(event)` from its Msg→event mapping (driven off\n * `runtime.observe`), and\n * 3. returns `hub.open()` from the `/sse` route.\n *\n * `register(sink)` exists for tests: subscribe a sink directly and observe the\n * exact event sequence a live client would receive (a live `text/event-stream`\n * fights vitest-pool-workers teardown). Deletes the consumer's `sseSinks` set,\n * `openSse`, `emitSse`, and `onSseEvent`.\n */\nexport interface SseHub<E> {\n emit(event: E): void;\n open(): Response;\n register(sink: (event: E) => void): () => void;\n}\n\n/** Build an SSE hub for event type `E` (the consumer's domain event shape). */\nexport function sseHub<E>(): SseHub<E> {\n const sinks = new Set<(event: E) => void>();\n const encoder = new TextEncoder();\n\n return {\n emit(event) {\n for (const sink of sinks) {\n try {\n sink(event);\n } catch {\n /* broken stream — dropped on its cancel */\n }\n }\n },\n open() {\n let sink!: (event: E) => void;\n const stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n sink = (event) => {\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(event)}\\n\\n`),\n );\n };\n sinks.add(sink);\n },\n cancel: () => {\n sinks.delete(sink);\n },\n });\n return new Response(stream, {\n headers: {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n },\n });\n },\n register(sink) {\n sinks.add(sink);\n return () => {\n sinks.delete(sink);\n };\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// sseProjection — express `sseHub` as ONE projection over the seam (#69).\n//\n// `sseHub` (above) is the ad-hoc form: a sink-set the consumer fans\n// `runtime.observe` events out to by hand. `sseProjection` names it: the SSE\n// view is ONE projection (`name: \"sse\"`) over the `Projection<Model, View>`\n// seam in `./projection`. Its `apply` maps the write-model update to the\n// consumer's SSE event shape; its `emit` is `hub.emit` — the SAME hub, the SAME\n// public API. Register it on a `projectionRegistry` (driven off `observe`) and\n// the host's runtime→SSE plumbing is one projection among many, not a special\n// case.\n//\n// ADDITIVE, not a replacement: `sseHub()` keeps working untouched for every\n// existing caller (it is the volatile, offset-free case — a live `/sse` stream\n// has no durable read model to resume). `sseProjection` is the seam expression\n// of it for a consumer that wants SSE to sit on the same registry as a durable\n// report/storage projection.\n//\n// The SSE view is the most-recently-emitted event (a \"latest\" read model); the\n// fold ignores the previous view and re-derives from each update via\n// `toEvent`. `toEvent` returns `null` to SKIP an update the SSE view does not\n// care about (the canon's `Future.successful(Done)` skip) — the fold then keeps\n// the prior view and does not re-emit.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Express an {@link SseHub} as a {@link Projection}. The projection's view is\n * the latest emitted SSE event (or `null` before the first); `apply` maps each\n * write-model update to an event via `toEvent` and emits it through `hub.emit`,\n * so the SSE stream is driven by the SAME hub `sseHub()` returns — no API\n * change for the `/sse` route (`hub.open()`) or any existing caller.\n *\n * - `toEvent(msg, model)` returns the SSE event for this update, or `null` to\n * skip it (the view stays, nothing is emitted).\n * - `name`/`key` default to `\"sse\"`/`\"main\"`; override for a second SSE view.\n *\n * @example\n * ```ts\n * import type { Runtime } from \"@demlik/tea\";\n * import {\n * driveProjections,\n * projectionRegistry,\n * sseHub,\n * sseProjection,\n * } from \"@demlik/tea/do\";\n *\n * type MyState = { readonly turns: number };\n * type MyMsg = { type: \"turn_done\"; turn: number } | { type: \"tick\" };\n * type MyEvent = { kind: \"turn\"; turn: number };\n * declare const runtime: Runtime<MyState, MyMsg>;\n *\n * const hub = sseHub<MyEvent>();\n * const registry = projectionRegistry<MyState, MyMsg>();\n * registry.register(sseProjection(hub, (msg: MyMsg | null) =>\n * msg?.type === \"turn_done\" ? { kind: \"turn\", turn: msg.turn } : null,\n * ));\n * driveProjections(registry, runtime); // SSE is now one projection\n * // /sse route unchanged:\n * const sseRoute = (): Response => hub.open();\n * ```\n */\nexport function sseProjection<Model, Msg extends { type: string }, E>(\n hub: SseHub<E>,\n toEvent: (msg: Msg | null, model: Model) => E | null,\n id: { name?: string; key?: string } = {},\n): Projection<Model, Msg, E | null> {\n // The SSE view is \"the latest event to push, or null when the update was a\n // skip\". `apply` derives it; `emit` pushes the non-null case at the hub. The\n // fold stays pure (no side effect in `apply`); the sink stays the hub (no API\n // change). A skip resolves to a `null` view → `emit` pushes nothing.\n return {\n id: { name: id.name ?? \"sse\", key: id.key ?? \"main\" },\n initial: null,\n apply(_view, update) {\n // Each SSE update re-derives from the write model — the view is the\n // latest event, not an accumulation, so the prior view is ignored.\n // Idempotent by construction: re-deriving the same update yields the same\n // event (the exclusive-offset guard already drops re-presented offsets).\n return toEvent(update.msg, update.model);\n },\n emit(view) {\n // The runner calls `emit` after every APPLIED update (offset strictly past\n // the stored one). `null` is the skip — push nothing. A real event is\n // pushed through the hub, so `hub.open()` / existing callers are unchanged.\n if (view !== null) hub.emit(view);\n },\n };\n}\n\n// captureLastTurn DELETED (issue #46). It existed only to reconstruct the run's\n// terminal output off the `observe` firehose — match the private `resilient_ok`\n// Msg, validate `result.output`, and snapshot it BEFORE `succeed` cleared the\n// conversation on stage retire. The agent now models termination first-class:\n// `state.output` holds the terminal turn on `done`, and `Runtime.result()` /\n// `Runtime.done()` read it. A consumer reads `runtime.result()?.output` — no\n// observer hook, no internal-Msg coupling, no state-clear race.\n\n// ─────────────────────────────────────────────────────────────────────────────\n// sseFromAgentEvents — drive an SseHub off the SEMANTIC AgentEvent stream.\n//\n// The pre-#47 host fanned SSE frames off `runtime.observe` by hand-matching the\n// agent's PRIVATE Msg names (`resilient_ok` / `agent_tool_ok`) — coupling the\n// transport to the retry/loop plumbing. This wires the hub to `runtime.on`\n// instead: the runtime (built with `events: agentEvents()`) projects the public\n// `AgentEvent` union, and this helper subscribes ONE `on(type, …)` per event\n// type and pushes `toFrame(event)` (or `null` to skip) at the hub. The SSE seam\n// now reads the named lifecycle events, never the firehose, never a private Msg.\n//\n// Built on `runtime.on` (the typed semantic channel) rather than `sseProjection`\n// because SSE is the volatile, offset-free case — a live `/sse` stream has no\n// durable read model to resume — so the projection-registry's offset machinery\n// buys nothing here. A consumer wanting SSE on the SAME registry as a durable\n// report projection still uses `sseProjection`; this is the lighter path that\n// expresses \"SSE = a projection of the semantic event stream\" directly.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Wire an {@link SseHub} to a runtime's semantic {@link AgentEvent} stream.\n * For each event `type` the runtime can emit, subscribes via `runtime.on` and\n * pushes `toFrame(event)` at the hub (returning `null` skips that event). The\n * runtime MUST have been built with `run(machine, { events: agentEvents() })`,\n * which is what makes `on` deliver the `AgentEvent`s. Returns a cleanup that\n * detaches every subscription.\n *\n * This is the runtime→SSE adapter expressed over the typed event channel: no\n * `observe` firehose, no private-Msg coupling. `hub.open()` and every existing\n * `sseHub` caller are unchanged — only the SOURCE of `hub.emit` moves from a\n * hand-rolled Msg switch to `on`.\n *\n * @example\n * ```ts\n * import type { Runtime } from \"@demlik/tea\";\n * import type { AgentEndedStatus, AgentEvent } from \"@demlik/tea/agent\";\n * import { sseFromAgentEvents, sseHub } from \"@demlik/tea/do\";\n *\n * type MyFrame = { kind: \"ended\"; status: AgentEndedStatus };\n * // Built with `run(machine, { ctx, events: agentEvents() }).ready`.\n * declare const runtime: Runtime<unknown, { type: string }, AgentEvent<string>>;\n *\n * const hub = sseHub<MyFrame>();\n * const off = sseFromAgentEvents(runtime, hub, (e) =>\n * e.type === \"RunDone\" ? { kind: \"ended\", status: e.status } : null,\n * );\n * // /sse route unchanged:\n * const sseRoute = (): Response => hub.open();\n * ```\n */\nexport function sseFromAgentEvents<R, Frame>(\n runtime: {\n on<K extends AgentEvent<R>[\"type\"]>(\n type: K,\n handler: (event: Extract<AgentEvent<R>, { type: K }>) => void,\n ): () => void;\n },\n hub: SseHub<Frame>,\n toFrame: (event: AgentEvent<R>) => Frame | null,\n): () => void {\n const push = (event: AgentEvent<R>): void => {\n const frame = toFrame(event);\n if (frame !== null) hub.emit(frame);\n };\n // One subscription per event type, off the one list that is checked against\n // the `AgentEvent` union, so a new variant reaches `toFrame` without an edit\n // here.\n const offs = AGENT_EVENT_TYPES.map((type) => runtime.on(type, push));\n return () => {\n for (const off of offs) off();\n };\n}\n","/**\n * `createAgentHost` — the runtime+events+autoBoot assembly + the test seam,\n * ONCE (issue #53). The facade over the concern modules: the deferred gateway\n * (`./deferred-gateway`), the WS carrier (`./command-socket`), the SSE plumbing\n * (`./sse`), and the cold-wake resume (`./resume`). See `./host` for the\n * transport-model rationale.\n */\n\nimport {\n type AgentEvent,\n type AgentMachineMsg,\n type AgentState,\n type AgentStatus,\n type AgentTurn,\n agentEvents,\n status,\n} from \"../agent/index\";\nimport type {\n BootedRunHandle,\n Cmd,\n CtxArg,\n EngineRun,\n RunHandle,\n RunOptions,\n Store,\n StoreAndOnError,\n Sub,\n Wired,\n} from \"../index\";\nimport { autoBoot } from \"./resume\";\nimport { type SseHub, sseFromAgentEvents, sseHub } from \"./sse\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// createAgentHost — the runtime+events+autoBoot assembly + the test seam, ONCE\n// (issue #53).\n//\n// Every DO that drives a `createAgent` runtime hand-rolls the SAME ordered\n// wiring inside its `getRuntime()`: build the machine → `doStore` → `run(...)`\n// with `terminal` + `events: agentEvents()` → wire `sseFromAgentEvents` →\n// `autoBoot` → `await ready` → cache. And every one re-exposes the SAME\n// framework test surface as public methods (`isSuspended` / `runPhase` /\n// `verdict` / `settle`). The concern modules (`deferredGateway`, `autoBoot`,\n// `sseFromAgentEvents`, …) already lift each STEP; this lifts the ASSEMBLY.\n//\n// `createAgentHost(config)` owns:\n// - the lazy build-once-boot-once runtime cell (the `if (this.runtime) return`\n// dance + the `await booting.ready` gate),\n// - the `events: agentEvents()` wiring + `sseFromAgentEvents(runtime, hub, …)`\n// so SSE is driven off the SEMANTIC `AgentEvent` stream, never the private\n// Msg firehose,\n// - the `autoBoot` re-fire on a rehydrated suspended run,\n// - the SSE hub (so `host.sse.open()` / `host.sse.register(...)` is the route +\n// the test seam), and\n// - the framework test/lifecycle seam ONCE: `status()`, `result()`, `reset()`.\n//\n// The consumer supplies ONLY its mappings: the engine's `run`, how to build the\n// machine, the Store, the Ctx, the terminal predicate, and the `AgentEvent → SSE frame` projection.\n// Its `getRuntime()` collapses to `host.runtime()`; its `isSuspended` /\n// `runPhase` / `verdict` collapse to reads off `host.status()` / `host.result()`.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * What the consumer supplies to {@link createAgentHost} — the domain mappings,\n * nothing of the wiring. `Stage`/`P`/`O`/`R` are the agent slice's type\n * parameters (the consumer names them once); `Frame` is its SSE event shape.\n * `C`/`U`/`Ctx` are the machine's own Cmd/Sub/Ctx — inferred from\n * `buildMachine` at the `createAgentHost` call site (never name them by hand;\n * an erased `any` here cost consumers all checking on this layer — #278).\n *\n * `store` is required, and a fenced one needs an `onError` (see\n * {@link StoreAndOnError}).\n */\nexport type AgentHostConfig<\n Stage,\n P extends string,\n O extends Record<P, AgentTurn>,\n R,\n Frame,\n C extends Cmd = Cmd,\n U extends Sub = Sub,\n Ctx = unknown,\n> = AgentHostWiring<Stage, P, O, R, Frame, C, U, Ctx> &\n StoreAndOnError<AgentState<Stage, P, O, R>> & {\n /** The durable `Store` for the agent slice (typically `doStore(storage, parse)`). */\n readonly store: Store<AgentState<Stage, P, O, R>>;\n };\n\n/** The part of {@link AgentHostConfig} that is not the store and its sink. */\nexport interface AgentHostWiring<\n Stage,\n P extends string,\n O extends Record<P, AgentTurn>,\n R,\n Frame,\n C extends Cmd = Cmd,\n U extends Sub = Sub,\n Ctx = unknown,\n> {\n /**\n * The engine's `run` — `run` from `@demlik/tea/promise`, or any engine's. The\n * host imports no engine; it boots the machine through this.\n */\n readonly run: EngineRun<\n AgentState<Stage, P, O, R>,\n AgentMachineMsg<P, O, R>,\n C,\n U,\n Ctx,\n AgentEvent<R>\n >;\n /**\n * Build the wired agent machine and the handlers it runs under — its\n * `interpret` table and `subscribe` runners (a machine carries none — #278,\n * #279). Called once per host build (per activation), AFTER the previous\n * runtime — if any — was torn down. The consumer wires its per-tool interpret\n * here, and `agent.toMachine({ toolInterpret })` already returns exactly this.\n */\n readonly buildMachine: () => Wired<\n AgentState<Stage, P, O, R>,\n AgentMachineMsg<P, O, R>,\n C,\n U,\n Ctx\n >;\n /** The plain Ctx the machine threads to its interpret cells. */\n readonly ctx: Ctx;\n /**\n * The run-terminality predicate (#46) — what makes `result()` first-class.\n * Defaults to the agent's own terminal phases (`done` / `failed`).\n */\n readonly terminal?: (state: AgentState<Stage, P, O, R>) => boolean;\n /**\n * Map one semantic {@link AgentEvent} to the consumer's SSE frame, or `null`\n * to skip it. This is the ONLY place the consumer touches the SSE seam — the\n * host owns the subscription wiring (`sseFromAgentEvents`) and the hub.\n */\n readonly toSseFrame: (event: AgentEvent<R>) => Frame | null;\n /** Clock injected for `autoBoot` (the host's only boot clock read). */\n readonly now?: () => number;\n}\n\n/**\n * The assembled host: the runtime handle + the SSE hub + the framework test seam,\n * owned ONCE. A DO holds one `AgentHost` and delegates to it; its own surface\n * shrinks to the domain (the gateway, the WS bridge, the command-send mapping).\n *\n * @typeParam Stage Pipeline stage type.\n * @typeParam P Brain-call purposes.\n * @typeParam O Per-purpose outputs (bound to {@link AgentTurn}).\n * @typeParam R Tool result type.\n * @typeParam Frame The consumer's SSE event shape.\n */\nexport interface AgentHost<\n Stage,\n P extends string,\n O extends Record<P, AgentTurn>,\n R,\n Frame,\n> {\n /**\n * Get (or build) the booted runtime. Build-once-boot-once: the first call\n * builds the machine, wires SSE off the semantic event stream, awaits the boot\n * gate, runs the `autoBoot` re-fire for a rehydrated suspended run, and caches\n * the booted handle; subsequent calls return the cached handle. The single\n * assembly every consumer used to hand-roll in `getRuntime()`.\n */\n runtime(): Promise<\n BootedRunHandle<\n AgentState<Stage, P, O, R>,\n AgentMachineMsg<P, O, R>,\n AgentEvent<R>\n >\n >;\n /**\n * The agent's lifecycle status (#49) — the ONE typed channel a consumer reads\n * instead of re-deriving `run.phase` / `awaiting` by hand. Ensures the runtime\n * is built + boot-reconciled first. Replaces the per-consumer `isSuspended` /\n * `runPhase` test methods (read `status().kind`: `idle` / `running` /\n * `suspended` / `done` / `failed`). A hydrated DO that has not yet seen\n * `agent_start` reads `idle`, not `running` (#92).\n */\n status(): Promise<AgentStatus<Stage>>;\n /**\n * The run's terminal State (#46), or `undefined` while in flight. Read the\n * run's product off it (e.g. `result()?.output`). Replaces the per-consumer\n * `verdict` plumbing's source. Ensures the runtime is built first.\n */\n result(): Promise<AgentState<Stage, P, O, R> | undefined>;\n /**\n * The SSE hub — the route (`host.sse.open()`) AND the test seam\n * (`host.sse.register(sink)`). The host drives `hub.emit` off the semantic\n * event stream; the consumer never touches it except to open / register.\n */\n readonly sse: SseHub<Frame>;\n /**\n * Tear down the cached runtime (drain its tail via `stop()`, up to\n * `disposeTimeoutMs`, then drop the handle) so the next `runtime()` rebuilds\n * from storage. A Cmd handler that has not returned by the limit is given up\n * on, so `reset()` returns either way. The framework `settle` test seam,\n * owned once.\n */\n reset(): Promise<void>;\n}\n\n/**\n * Build an {@link AgentHost} from the consumer's domain mappings. Owns the\n * runtime+events+autoBoot assembly and the framework test seam — the wiring\n * issue #53 had every consumer re-write.\n *\n * @example\n * ```ts\n * import type { Interpret, Migrated } from \"@demlik/tea\";\n * import type {\n * AgentKnob,\n * AgentMachineMsg,\n * AgentState,\n * AgentTurn,\n * ToolCall,\n * } from \"@demlik/tea/agent\";\n * import { createAgentHost, doStore } from \"@demlik/tea/do\";\n * import { run } from \"@demlik/tea/promise\";\n *\n * type Stage = \"plan\" | \"act\";\n * type Purpose = \"plan_turn\" | \"act_turn\";\n * type Outputs = Record<Purpose, AgentTurn>;\n * type ClientResult = string;\n * type ToolCmd = { readonly type: \"run_tool\" } & ToolCall;\n * type SseEvent =\n * | { kind: \"result\"; callId: string; result: ClientResult }\n * | { kind: \"verdict\"; verdict: string }\n * | { kind: \"phase\"; phase: string };\n * interface Ctx { readonly apiKey: string }\n * type S = AgentState<Stage, Purpose, Outputs, ClientResult>;\n *\n * // From `createAgent(...)`, and the interpret for its tool Cmd.\n * declare const agent: AgentKnob<Stage, Purpose, Outputs, ClientResult, ToolCmd, false, false>;\n * declare const toolInterpret: Interpret<AgentMachineMsg<Purpose, Outputs, ClientResult>, ToolCmd, Ctx>;\n * declare const storage: DurableObjectStorage;\n * declare const parse: (raw: unknown) => Migrated<S>;\n * declare const ctx: Ctx;\n *\n * const host = createAgentHost({\n * run,\n * buildMachine: () => agent.toMachine<Ctx>({ toolInterpret }),\n * store: doStore(storage, parse),\n * ctx,\n * toSseFrame: (e): SseEvent | null =>\n * e.type === \"ToolSettled\" ? { kind: \"result\", callId: e.callId, result: e.result } :\n * e.type === \"TurnSettled\" && e.turn.toolCalls.length === 0\n * ? { kind: \"verdict\", verdict: e.turn.content } :\n * e.type === \"RunDone\" ? { kind: \"phase\", phase: e.status.kind } : null,\n * });\n * // route:\n * const sseRoute = (): Response => host.sse.open();\n * // start:\n * await (await host.runtime()).dispatch({ type: \"agent_start\", runId: \"run-1\", at: Date.now() });\n * // test:\n * const suspended = (await host.status()).kind === \"suspended\";\n * ```\n */\nexport function createAgentHost<\n Stage,\n P extends string,\n O extends Record<P, AgentTurn>,\n R,\n Frame,\n C extends Cmd = Cmd,\n U extends Sub = Sub,\n Ctx = unknown,\n>(\n config: AgentHostConfig<Stage, P, O, R, Frame, C, U, Ctx>,\n): AgentHost<Stage, P, O, R, Frame> {\n type S = AgentState<Stage, P, O, R>;\n type M = AgentMachineMsg<P, O, R>;\n type E = AgentEvent<R>;\n\n const sse = sseHub<Frame>();\n const now = config.now ?? Date.now;\n // Every terminal phase, `cancelled` included: a run stopped from outside has\n // ended, so `done()` must resolve on it rather than wait for a `done` that is\n // not coming.\n const isTerminal =\n config.terminal ??\n ((s: S) =>\n s.run.phase === \"done\" ||\n s.run.phase === \"failed\" ||\n s.run.phase === \"cancelled\");\n\n // The build-once cell memoizes the IN-FLIGHT build (a Promise), not just the\n // settled runtime (#313). Were it the settled handle, two concurrent\n // activations — e.g. an `/sse` open and an inbound dispatch arriving in the\n // same wake — would each pass the fast-path guard (the cell is empty until\n // after the boot await) and each `run(buildMachine(), …)` + wire SSE + autoBoot\n // against the same storage: a double boot with duplicate SSE wiring, not the\n // build-once-boot-once this host promises. Assigning the promise synchronously,\n // before the first await, means the second caller shares the one build.\n let cached: Promise<BootedRunHandle<S, M, E>> | null = null;\n\n async function build(): Promise<BootedRunHandle<S, M, E>> {\n // `run()` hands back a `RunHandle` synchronously (#45). Wire the SEMANTIC\n // event projector (#47) so `runtime.on(...)` lights up, then drive the SSE\n // hub off that named stream (never the private-Msg firehose).\n const wired = config.buildMachine();\n // `ctx` goes in as its own `CtxArg<Ctx>`: over a generic `Ctx` that\n // conditional stays deferred, and tsc relates it only to itself, not to an\n // object that spreads the handlers beside it.\n const ctxArg = { ctx: config.ctx } as CtxArg<Ctx>;\n // Lifted out of `config`, `store` and `onError` no longer read as one member\n // of its union, so the options are read at `run`'s own parameter type.\n const booting: RunHandle<S, M, E> = config.run(wired.machine, {\n ...wired,\n ...ctxArg,\n store: config.store,\n onError: config.onError,\n events: agentEvents<Stage, P, O, R>(),\n } as RunOptions<S, M, C, U, Ctx, E>);\n\n // SSE off the semantic stream. Subscriptions attach on the booting handle\n // (total before boot), so the hub catches every event from the first\n // transition. The cleanup is implicit: the host tears the whole runtime\n // down in `reset()`, dropping every subscription with it.\n sseFromAgentEvents(booting, sse, config.toSseFrame);\n\n // Re-fire the one outstanding tool effect IFF this wake rehydrated a\n // suspended run (no-op on a fresh DO). `autoBoot` awaits the same boot gate.\n await autoBoot(booting, now);\n return booting.ready;\n }\n\n function runtime(): Promise<BootedRunHandle<S, M, E>> {\n if (cached !== null) return cached;\n const building = build();\n cached = building;\n // A failed build clears the cell so a later call retries rather than\n // caching the rejection forever (matches the pre-#313 `cached`-stays-null\n // semantics). Guard on identity so a reset+rebuild isn't clobbered.\n building.catch(() => {\n if (cached === building) cached = null;\n });\n return cached;\n }\n\n return {\n runtime,\n async status(): Promise<AgentStatus<Stage>> {\n return status((await runtime()).getState());\n },\n async result(): Promise<S | undefined> {\n // The terminal read, off the host's own predicate rather than an engine's\n // `result()` — the handle every engine returns does not carry one.\n const state = (await runtime()).getState();\n return isTerminal(state) ? state : undefined;\n },\n sse,\n async reset(): Promise<void> {\n if (cached === null) return;\n // Await the settled build before tearing down — a reset racing an\n // in-flight build still drains the one runtime rather than orphaning it.\n // A failed build has no runtime to stop.\n const built = await cached.catch(() => null);\n cached = null;\n await built?.stop();\n },\n };\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @demlik/tea/do — generic hibernatable PRESENCE + BROADCAST for a NATIVE\n * (non-agent) Durable-Object grain.\n *\n * ## The gap this fills\n *\n * `./host` ships `acceptCommandSocket` / `acceptDurableCommandSocket` /\n * `broadcast` / `broadcastHibernatable`, but those are framed for — and live\n * alongside — the LLM-agent **deferred-tool gateway** (`createAgentHost`): the\n * socket is \"the command-runner socket\", the frame is \"a command\", and the\n * fan-out is the agent's `sendCommand` loop. A plain realtime grain — a game\n * room, a presence list, a collab document — does not have an agent, a gateway,\n * or commands. It just wants to accept hibernatable client sockets and fan a\n * frame out to whoever is connected. Today every such grain (vortex's\n * `ArenaGrain`, #162) hand-rolls the same `ctx.acceptWebSocket(server)` +\n * `for (const ws of ctx.getWebSockets()) ws.send(...)` boilerplate (#181).\n *\n * This module lifts exactly that, decoupled from the agent host:\n *\n * - {@link broadcastFrame} — the fan-out, with the **socket set as the input**\n * (not a `ctx`), so it is a pure function testable with fake sockets and\n * reusable over any iterable (`ctx.getWebSockets()`, a filtered subset, a\n * test array). It serializes once, sends to every OPEN socket, and skips a\n * closed/errored one — returning a {@link BroadcastReport} count rather than\n * swallowing the skip silently (errors are data).\n * - {@link registerHibernatableSocket} — register an already-created server\n * socket on the DO via the Cloudflare **Hibernation API**\n * (`ctx.acceptWebSocket`), optionally tagging it and persisting a\n * per-socket attachment that survives eviction. Pure over the `ctx` +\n * socket seam, so the registration logic is testable with fakes.\n * - {@link acceptPresenceSocket} — the full upgrade: build the\n * `WebSocketPair`, register the server end, and return the `server` socket\n * plus the 101 upgrade `Response`. The one piece that touches the\n * runtime-only `WebSocketPair` (so it is exercised by type, not by a unit\n * test without workerd) — the registration it delegates to *is* unit-tested.\n * - {@link presenceCount} — the live connection count off `ctx.getWebSockets`,\n * the trivial roster-size lift a presence grain reaches for.\n *\n * It is transport/Msg-agnostic: `frame` is `unknown` (JSON-serialized by\n * default, or via a caller `serialize`), and nothing here references an agent,\n * a gateway, a Sub, or a Msg. It is ADDITIVE — `./host`'s agent-coupled\n * helpers keep working untouched for the agent consumer.\n */\n\n/**\n * The minimal \"send a string frame\" surface {@link broadcastFrame} needs. A real\n * Cloudflare `WebSocket` satisfies it structurally (its `send` accepts a string\n * and it carries a numeric `readyState`), so `ctx.getWebSockets()` flows in\n * directly; a test supplies a fake with just `send` (and optionally\n * `readyState`).\n *\n * `readyState` is OPTIONAL: when present and not {@link WS_READY_STATE_OPEN},\n * the socket is skipped WITHOUT calling `send`; when absent, `send` is attempted\n * and a throw is caught (a fake that models only liveness via a throwing `send`\n * needs no `readyState`).\n */\nexport interface PresenceSocket {\n send(message: string): void;\n readonly readyState?: number;\n}\n\n/**\n * The Hibernation slice of `DurableObjectState` a presence grain needs — the\n * accept that hands the socket to the runtime (surviving eviction) and the\n * getter that repopulates the live set after a wake. A real `DurableObjectState`\n * satisfies this structurally; a test supplies a fake (no live Workers runtime\n * needed to exercise the accept/registry seam).\n */\nexport interface PresenceCtx {\n acceptWebSocket(ws: WebSocket, tags?: string[]): void;\n getWebSockets(tag?: string): WebSocket[];\n}\n\n/**\n * The OPEN `readyState` value (`WebSocket.READY_STATE_OPEN` in the Cloudflare\n * runtime). Named here so {@link broadcastFrame} can skip a non-open socket\n * without referencing the runtime constant (keeping the fold pure + testable).\n */\nexport const WS_READY_STATE_OPEN = 1;\n\n/**\n * What a {@link broadcastFrame} fan-out did, surfaced rather than swallowed:\n * `sent` is the number of sockets the frame reached; `skipped` is the number\n * passed over (not OPEN, errored on `send`, or the `except` socket). A caller\n * can log or assert these — a silent fan-out hides a room going dark.\n */\nexport interface BroadcastReport {\n readonly sent: number;\n readonly skipped: number;\n}\n\n/** Optional settings for {@link broadcastFrame}. */\nexport interface BroadcastOptions<S extends PresenceSocket> {\n /**\n * A socket to exclude from the fan-out — typically the sender, so a collab /\n * chat frame is not echoed back to its originator. Compared by identity.\n */\n readonly except?: S;\n /**\n * How to turn `frame` into the wire string. Defaults to `JSON.stringify`.\n * Override for a non-JSON wire format (e.g. a pre-encoded string passthrough).\n */\n readonly serialize?: (frame: unknown) => string;\n}\n\n/**\n * Serialize `frame` ONCE and send it to every OPEN socket in `sockets`, skipping\n * any that is closed, errors on `send`, or is the `except` socket. The socket\n * set is the INPUT (not a `ctx`), so this is a pure function: pass\n * `ctx.getWebSockets()` in a live grain, a filtered subset for a targeted\n * fan-out, or a fake array in a test.\n *\n * Skip semantics (a dead socket never crashes the broadcast):\n * - `readyState` present and not {@link WS_READY_STATE_OPEN} → skipped, `send`\n * not called;\n * - `send` throws (a socket closing mid-fan-out) → skipped, the throw is\n * caught (the close lifecycle reaps it);\n * - the `except` socket → skipped.\n *\n * Returns a {@link BroadcastReport} of `{ sent, skipped }` — the skip is data,\n * not silence.\n */\nexport function broadcastFrame<S extends PresenceSocket>(\n sockets: Iterable<S>,\n frame: unknown,\n options?: BroadcastOptions<S>,\n): BroadcastReport {\n const serialize = options?.serialize ?? JSON.stringify;\n const except = options?.except;\n const json = serialize(frame);\n let sent = 0;\n let skipped = 0;\n for (const ws of sockets) {\n if (except !== undefined && ws === except) {\n skipped++;\n continue;\n }\n if (ws.readyState !== undefined && ws.readyState !== WS_READY_STATE_OPEN) {\n skipped++;\n continue;\n }\n try {\n ws.send(json);\n sent++;\n } catch {\n // A socket closing mid-broadcast is reaped by its close lifecycle; the\n // failed send is safe to drop here, surfaced as a skip rather than a throw.\n skipped++;\n }\n }\n return { sent, skipped };\n}\n\n/** Optional settings for {@link registerHibernatableSocket} /\n * {@link acceptPresenceSocket}. */\nexport interface RegisterOptions<A> {\n /**\n * Hibernation tags for the socket — the same strings `ctx.getWebSockets(tag)`\n * filters on, so a grain can fan out to a subset (e.g. a team, a channel).\n */\n readonly tags?: readonly string[];\n /**\n * A per-socket value to persist via `server.serializeAttachment` so it\n * survives DO eviction (e.g. the player/session id the grain re-reads in\n * `webSocketMessage`/`webSocketClose`). Omitted ⇒ no attachment written.\n */\n readonly attachment?: A;\n}\n\n/**\n * The subset of `WebSocket` {@link registerHibernatableSocket} writes to — the\n * attachment serializer. A real server `WebSocket` satisfies it; a test supplies\n * a fake that records the serialized attachment.\n */\nexport interface AttachableSocket {\n serializeAttachment(value: unknown): void;\n}\n\n/**\n * Register an already-created `server` socket on the DO via the Cloudflare\n * **Hibernation API**: `ctx.acceptWebSocket(server, tags)` hands the socket to\n * the runtime (it survives eviction and re-delivers inbound frames to the DO's\n * `webSocketMessage` lifecycle method), and — when an `attachment` is supplied —\n * persists it via `server.serializeAttachment` so it survives the wake.\n *\n * Pure over the `ctx` + socket seam (no `WebSocketPair` construction), so the\n * registration logic — that accept is called with the right tags, that the\n * attachment is serialized iff present — is unit-testable with fakes.\n * {@link acceptPresenceSocket} is the thin wrapper that also builds the pair.\n */\nexport function registerHibernatableSocket<S extends AttachableSocket, A>(\n ctx: PresenceCtx,\n server: S,\n options?: RegisterOptions<A>,\n): void {\n // The Hibernation API accept: the runtime owns the socket across eviction; no\n // isolate-resident `message`/`close` listeners (those are lost on eviction) —\n // inbound frames arrive via the DO's `webSocketMessage` lifecycle method.\n ctx.acceptWebSocket(\n server as unknown as WebSocket,\n options?.tags === undefined ? undefined : [...options.tags],\n );\n if (options?.attachment !== undefined) {\n server.serializeAttachment(options.attachment);\n }\n}\n\n/** What {@link acceptPresenceSocket} hands back: the live server end (to send\n * initial frames on) and the 101 upgrade `Response` (to return from `fetch`). */\nexport interface PresenceUpgrade {\n /** The server end of the pair — send the welcome / initial snapshot on it. */\n readonly server: WebSocket;\n /** The 101 Switching-Protocols upgrade response carrying the client end. */\n readonly response: Response;\n}\n\n/**\n * Accept a client WebSocket upgrade on a native DO and register it for\n * hibernation. Builds the `WebSocketPair`, registers the server end via\n * {@link registerHibernatableSocket} (tags + optional attachment), and returns\n * the `server` socket plus the 101 upgrade {@link PresenceUpgrade.response}.\n *\n * The caller's `fetch` collapses to: send any initial frames on `server`\n * (welcome, current roster/snapshot), then `return upgrade.response`. This lifts\n * the `new WebSocketPair()` + `ctx.acceptWebSocket` + `serializeAttachment` +\n * `new Response(null, { status: 101, webSocket })` boilerplate a presence grain\n * hand-rolls.\n *\n * NOTE: `WebSocketPair` is a runtime-only global (no workerd in unit tests), so\n * this assembly is exercised by TYPE here; its registration seam is unit-tested\n * via {@link registerHibernatableSocket}.\n *\n * @example\n * ```ts\n * import { acceptPresenceSocket, type PresenceCtx } from \"@demlik/tea/do\";\n *\n * declare const room: { snapshot(): unknown };\n *\n * class Lobby {\n * constructor(readonly ctx: PresenceCtx) {}\n *\n * async fetch(request: Request): Promise<Response> {\n * if (request.headers.get(\"Upgrade\") !== \"websocket\") {\n * return new Response(\"expected a websocket upgrade\", { status: 426 });\n * }\n * const playerId = new URL(request.url).searchParams.get(\"player\");\n * const welcome = { type: \"welcome\", playerId };\n * const { server, response } = acceptPresenceSocket(this.ctx, {\n * attachment: { playerId },\n * });\n * server.send(JSON.stringify(welcome));\n * server.send(JSON.stringify(room.snapshot()));\n * return response;\n * }\n * }\n * ```\n */\nexport function acceptPresenceSocket<A>(\n ctx: PresenceCtx,\n options?: RegisterOptions<A>,\n): PresenceUpgrade {\n const pair = new WebSocketPair();\n const client = pair[0];\n const server = pair[1];\n registerHibernatableSocket(ctx, server, options);\n return {\n server,\n response: new Response(null, { status: 101, webSocket: client }),\n };\n}\n\n/**\n * The live connection count for a presence grain — `ctx.getWebSockets(tag)\n * .length`, repopulated by the runtime after a wake. Pass a `tag` to count only\n * the sockets registered under it. The trivial roster-size lift a presence /\n * game grain reaches for (vortex's `connectionCount`).\n */\nexport function presenceCount(ctx: PresenceCtx, tag?: string): number {\n return ctx.getWebSockets(tag).length;\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * The command-runner WebSocket host + broadcast — the WS half of the\n * `@demlik/tea/do` transport, in both the resident (`acceptCommandSocket`) and\n * hibernation-aware (`acceptDurableCommandSocket`) forms. See `./host` for the\n * transport-model rationale (ONE Sub type, gateway-bridged I/O).\n */\n\nimport { broadcastFrame } from \"./presence\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// acceptCommandSocket — WS host + inbound bridge (seam C).\n//\n// Accept the command-runner socket, keep it in a client registry, and route\n// inbound string frames to a handler (which the consumer points at\n// `gateway.settle(...)`). Lifts the consumer's `acceptClient` boilerplate.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Accept a command-runner WebSocket on the DO's `fetch`. Adds the server end to\n * `clients`, wires `message` → `onFrame(data)` (string frames only) and\n * `close` → deregister, and returns the 101 upgrade Response.\n *\n * The consumer's `onFrame` parses the frame and calls `gateway.settle(callId,\n * result)` — the inbound bridge. Deletes the consumer's `acceptClient` method.\n */\nexport function acceptCommandSocket(\n clients: Set<WebSocket>,\n onFrame: (data: string) => void,\n): Response {\n const pair = new WebSocketPair();\n const [client, server] = [pair[0], pair[1]];\n server.accept();\n clients.add(server);\n server.addEventListener(\"message\", (ev) => {\n if (typeof ev.data === \"string\") onFrame(ev.data);\n });\n server.addEventListener(\"close\", () => clients.delete(server));\n return new Response(null, { status: 101, webSocket: client });\n}\n\n/**\n * Broadcast a JSON frame to every connected command-runner socket. A dead\n * socket is skipped (dropped on its own `close` event). Lifts the consumer's\n * fan-out loop in `sendCommand`.\n *\n * Thin delegation to {@link broadcastFrame} — the general fan-out (serialize\n * once, send to every OPEN socket, skip a closed/errored one). The\n * {@link BroadcastReport} it returns is discarded here to keep the `void`\n * command-socket signature.\n */\nexport function broadcast(clients: Set<WebSocket>, frame: unknown): void {\n broadcastFrame(clients, frame);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// acceptDurableCommandSocket — a HIBERNATION-AWARE command-runner socket (#91).\n//\n// `acceptCommandSocket` (above) uses `server.accept()` — the NON-hibernatable\n// WebSocket API. The DO must stay resident to keep the socket alive; on eviction\n// the runtime tears the socket down (close 1006) and the inbound bridge dies.\n// That is half of #91. The other half — the LOAD-BEARING half — is that the\n// in-flight tool round-trips themselves are volatile: `deferredGateway`'s\n// `pending` Map is isolate-local, so a round-trip awaiting a reply when the DO\n// hibernates is lost with the heap, and nothing in replayed state records it is\n// still OWED. The durable carrier fixes BOTH:\n//\n// - the socket is accepted with `ctx.acceptWebSocket(server)` — the Cloudflare\n// Hibernation API. The runtime may evict the isolate while the socket stays\n// open; on the next inbound frame it re-creates the DO and calls the\n// `webSocketMessage(ws, data)` lifecycle method. The consumer routes that to\n// `onFrame` (which calls `carrier.settle(...)`). No isolate-resident\n// `message` listener to lose.\n// - each round-trip is recorded in the durable-effects ledger via\n// `durableDeferredGateway`, so the consumer PERSISTS an `effect_owed` before\n// the send and an `effect_confirmed` on settle/fail. On wake,\n// `reissueSurvivingEffects(carrier, persistedEvents, reissue)` folds the log\n// and re-fires every owed-but-unconfirmed round-trip — idempotent by the\n// ledger's monotonic `deliveryId`, deduped at the receiver.\n//\n// The carrier stays OUTSIDE the agent's Sub system (the `./host` rationale): the\n// round-trip is still a Promise the interpret cell awaits, bridged into\n// `dispatch` by the gateway. The DURABILITY lives in the ledger + the consumer's\n// storage, NOT in widening the machine's Sub type. `acceptCommandSocket` keeps\n// working untouched for callers that do not need eviction survival. (The carrier\n// itself lives in `./deferred-gateway`; the wake-path re-emit in `./resume`.)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The slice of `DurableObjectState` the durable carrier needs: the Hibernation\n * API accept + the registry of hibernatable sockets. A real `DurableObjectState`\n * satisfies this structurally; a test supplies a fake (no live Workers runtime\n * needed to exercise the accept/registry seam).\n */\nexport interface HibernatableCtx {\n acceptWebSocket(ws: WebSocket): void;\n getWebSockets(): WebSocket[];\n}\n\n/**\n * Accept a command-runner WebSocket using the Cloudflare **Hibernation API** so\n * the socket survives DO eviction. Unlike {@link acceptCommandSocket}, this does\n * NOT attach an isolate-resident `message` listener (that listener is what dies\n * on eviction) — instead `ctx.acceptWebSocket(server)` hands the socket to the\n * runtime, which re-creates the DO and calls its `webSocketMessage(ws, data)`\n * lifecycle method on the next inbound frame. The consumer routes that method to\n * its `onFrame` (which calls `carrier.settle(callId, result)`).\n *\n * Returns the 101 upgrade Response. There is no `clients` registry to keep — the\n * live sockets are `ctx.getWebSockets()` (see {@link broadcastHibernatable}),\n * which the runtime repopulates after a wake.\n */\nexport function acceptDurableCommandSocket(ctx: HibernatableCtx): Response {\n const pair = new WebSocketPair();\n const [client, server] = [pair[0], pair[1]];\n // Hibernation API: the runtime owns the socket across eviction; no resident\n // `message`/`close` listeners (they would be lost on eviction). Inbound frames\n // arrive via the DO's `webSocketMessage` lifecycle method instead.\n ctx.acceptWebSocket(server);\n return new Response(null, { status: 101, webSocket: client });\n}\n\n/**\n * Broadcast a JSON frame to every hibernatable command-runner socket. The live\n * set is read from `ctx.getWebSockets()` (the runtime repopulates it after a\n * wake), NOT a resident `Set<WebSocket>` — so it is correct immediately after an\n * eviction, before any other lifecycle method has run. A dead socket is skipped.\n *\n * Thin delegation to {@link broadcastFrame} over `ctx.getWebSockets()` — the\n * general fan-out (serialize once, send to every OPEN socket, skip a\n * closed/errored one). The {@link BroadcastReport} it returns is discarded here\n * to keep the `void` command-socket signature.\n */\nexport function broadcastHibernatable(\n ctx: HibernatableCtx,\n frame: unknown,\n): void {\n broadcastFrame(ctx.getWebSockets(), frame);\n}\n","/**\n * The deferred-tool gateway + its durable wrappers — the Promise-owning half of\n * the `@demlik/tea/do` transport. See `./host` for the transport-model\n * rationale (ONE Sub type, gateway-bridged I/O).\n */\n\nimport type {\n EffectConfirmed,\n EffectOwed,\n PendingEffectsRecorder,\n} from \"./durable-effects\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// deferredGateway — the deferred-tool gateway (HEADLINE).\n//\n// Lifts the consumer's `pending` Map + `awaitClient` + `Promise.race` timeout +\n// settle-on-reply. A tool's interpret cell calls `gateway.await(callId, send,\n// deadlineMs)`: the gateway registers the round-trip, fires `send`, and returns\n// a Promise the transport settles via `gateway.settle(callId, result)` (or that\n// the deadline rejects). The cell awaits it and maps Ok/Err to\n// `agent_tool_ok` / `agent_tool_err` — that is ALL the consumer's tool cell does\n// after this.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** A single in-flight tool round-trip, keyed by `callId`. */\ninterface PendingCall<R> {\n resolve(result: R): void;\n reject(reason: Error): void;\n readonly promise: Promise<R>;\n}\n\n/**\n * The deferred-tool gateway. `R` is the consumer's tool-result type (what\n * `agent_tool_ok` carries).\n *\n * - `await(callId, send, deadlineMs)` — register the round-trip, fire `send`,\n * return the Promise the reply (or the deadline) settles. Idempotent per\n * `callId`: a second call for an in-flight id returns the SAME promise\n * without re-firing `send` (the boot-reconcile re-fire is a no-op at the\n * gateway, mirroring the agent's idempotent boot).\n * - `settle(callId, result)` — the transport calls this on a reply; resolves\n * the awaited promise. A settle for an unknown / already-settled id is a\n * no-op (the callId guard the consumer used to keep in its pending Map).\n * - `fail(callId, reason)` — settle the round-trip as an error on demand\n * (e.g. a transport-level close). Same unknown-id no-op.\n * - `inFlight()` — the callIds with an open round-trip (tests / introspection).\n *\n * The deadline → reject path is what makes \"timeout is data\": the tool cell's\n * `catch` maps the rejection to `agent_tool_err`, recorded on the conversation,\n * never a hang.\n */\nexport interface DeferredGateway<R> {\n await(callId: string, send: () => void, deadlineMs: number): Promise<R>;\n settle(callId: string, result: R): void;\n fail(callId: string, reason: string): void;\n inFlight(): readonly string[];\n}\n\n/**\n * Build a deferred-tool gateway. No clock / RNG captured at construction — the\n * deadline is per-call (`await(..., deadlineMs)`), so one gateway serves every\n * tool family with its own timeout.\n *\n * Deletes from the consumer: the `pending` Map, the `Pending` interface, the\n * `awaitClient` method, the `forceTimeout` plumbing, and the `deliverResult`\n * callId-guard body — all of it collapses to this object plus the tool cell's\n * `await` + try/catch.\n */\nexport function deferredGateway<R>(): DeferredGateway<R> {\n const pending = new Map<string, PendingCall<R>>();\n\n return {\n await(callId, send, deadlineMs) {\n const existing = pending.get(callId);\n // Idempotent re-fire (boot reconcile re-issues the same callId): return\n // the open promise, do NOT re-fire `send`.\n if (existing !== undefined) return existing.promise;\n\n let resolve!: (r: R) => void;\n let reject!: (e: Error) => void;\n const promise = new Promise<R>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n const timer = setTimeout(() => {\n if (pending.delete(callId)) {\n reject(new Error(`deadline_exceeded after ${deadlineMs}ms`));\n }\n }, deadlineMs);\n pending.set(callId, {\n resolve: (v) => {\n clearTimeout(timer);\n resolve(v);\n },\n reject: (e) => {\n clearTimeout(timer);\n reject(e);\n },\n promise,\n });\n send();\n return promise;\n },\n\n settle(callId, result) {\n const entry = pending.get(callId);\n if (entry === undefined) return; // unknown / stale / already settled.\n pending.delete(callId);\n entry.resolve(result);\n },\n\n fail(callId, reason) {\n const entry = pending.get(callId);\n if (entry === undefined) return;\n pending.delete(callId);\n entry.reject(new Error(reason));\n },\n\n inFlight() {\n return [...pending.keys()];\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// durableDeferredGateway — bridge `deferredGateway` to the durable ledger.\n//\n// `deferredGateway` is volatile: its `pending` Map lives in the isolate and is\n// LOST on hibernation, so a tool round-trip in flight when the DO sleeps is\n// gone — exactly the durable-effects tax (ADR 0003 #1). This bridge makes the\n// round-trip a DURABLE owed effect WITHOUT changing the gateway's fast path:\n//\n// - the in-memory `await`/`settle`/`fail`/`inFlight` behavior is untouched\n// (the Promise the interpret cell awaits is still resolved in-isolate);\n// - around it, `await` records an `effect_owed` event and `settle`/`fail`\n// record an `effect_confirmed`, which the consumer PERSISTS into the same\n// event log the actor replays. On the next activation the rebuilt ledger's\n// `survivingEffects` are the round-trips to re-fire — idempotent by id.\n//\n// It is OPT-IN and ADDITIVE: a consumer that wants only the volatile gateway\n// keeps calling `deferredGateway()` directly; one that wants durability wraps\n// it here. The `record*` callbacks hand the consumer the events to persist (the\n// host does not own the consumer's log, so it cannot persist them itself —\n// keeping the bridge pure of storage and faithful to \"persist the intent before\n// deliver\").\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** A `callId`-keyed durable round-trip: maps the gateway's string id to the\n * ledger's monotonic delivery id so a `settle(callId)` finds the owed entry. */\ninterface DurableDeferredGateway<R> extends DeferredGateway<R> {\n /** The ledger recorder this gateway feeds (for `surviving()` on activation). */\n readonly recorder: PendingEffectsRecorder<{ callId: string }>;\n}\n\n/**\n * Wrap a `DeferredGateway<R>` so every round-trip is also recorded in a durable\n * `PendingEffectsRecorder`. The in-memory gateway is the fast path; the recorder\n * is the durability slice.\n *\n * - `recordOwed(callId, event)` — fires from `await`, BEFORE `send`, carrying\n * the `effect_owed` event the consumer must persist (then deliver).\n * - `recordConfirmed(callId, event)` — fires from `settle`/`fail` for a known\n * id, carrying the `effect_confirmed` event to persist.\n * - `reissue(callId, send, deadlineMs)` — re-arm a surviving round-trip on\n * activation. Idempotent: the gateway's own `await` returns the open promise\n * without re-firing `send` for an in-flight `callId`, so re-issuing an id\n * that is somehow still pending is a no-op (mirrors the boot-reconcile\n * re-fire). The effect is already owed, so no second `effect_owed` is\n * recorded.\n *\n * The effect payload the ledger stores is the `callId` (the correlation key the\n * consumer round-trips); the consumer maps it back to its tool-call context.\n */\nexport function durableDeferredGateway<R>(\n inner: DeferredGateway<R>,\n recorder: PendingEffectsRecorder<{ callId: string }>,\n hooks: {\n recordOwed(callId: string, event: EffectOwed<{ callId: string }>): void;\n recordConfirmed(callId: string, event: EffectConfirmed): void;\n },\n): DurableDeferredGateway<R> {\n // callId → ledger deliveryId, so settle/fail can confirm the right entry.\n const idByCallId = new Map<string, number>();\n\n return {\n recorder,\n\n await(callId, send, deadlineMs) {\n // Already owed (re-entrant await for an in-flight id) → don't re-owe;\n // delegate so the gateway returns the SAME open promise without re-send.\n if (!idByCallId.has(callId)) {\n const { id, event } = recorder.owe({ callId });\n idByCallId.set(callId, id);\n // Persist the intent BEFORE the send fires (which `inner.await` does).\n hooks.recordOwed(callId, event);\n }\n return inner.await(callId, send, deadlineMs);\n },\n\n settle(callId, result) {\n confirm(callId);\n inner.settle(callId, result);\n },\n\n fail(callId, reason) {\n confirm(callId);\n inner.fail(callId, reason);\n },\n\n inFlight() {\n return inner.inFlight();\n },\n };\n\n function confirm(callId: string): void {\n const id = idByCallId.get(callId);\n if (id === undefined) return; // unknown / already confirmed — no-op.\n idByCallId.delete(callId);\n const { event } = recorder.confirm(id);\n hooks.recordConfirmed(callId, event);\n }\n}\n\n/**\n * The durable command carrier — a {@link DurableDeferredGateway} whose every\n * tool round-trip is also a durable owed effect.\n *\n * A consumer's tool interpret cell still calls `carrier.await(callId, send,\n * deadlineMs)` and awaits the Promise (mapping Ok/Err to `agent_tool_ok` /\n * `agent_tool_err`) — the SAME fast path as the volatile gateway. The addition\n * is durability: `await` hands the consumer an `effect_owed` event (via\n * `recordOwed`) to PERSIST before the send, and `settle`/`fail` hand it an\n * `effect_confirmed` (via `recordConfirmed`) to persist. On the next activation\n * the consumer reads its persisted ledger events back and calls\n * `reissueSurvivingEffects` (see `./resume`) to re-fire the survivors.\n *\n * It IS a `DurableDeferredGateway` (so it is a drop-in for the gateway the\n * interpret cell already awaits) and exposes the underlying `recorder` for a\n * host that wants to read `surviving()` directly. The #91 \"command carrier\"\n * concept is exactly the durable gateway under a domain name — a type alias, not\n * a second byte-identical interface that could drift.\n */\nexport type DurableCommandCarrier<R> = DurableDeferredGateway<R>;\n\n/**\n * Build a durable command carrier over a (volatile) `DeferredGateway<R>` and a\n * `PendingEffectsRecorder`. Thin assembly over {@link durableDeferredGateway}:\n * it names the #91 piece — \"the command carrier whose round-trips survive\n * eviction\" — and keeps the recorder reachable for the activation-time re-emit.\n *\n * - `inner` — the volatile gateway whose in-isolate Promise the interpret cell\n * awaits (typically `deferredGateway<R>()`).\n * - `recorder` — the durable-effects recorder (typically\n * `pendingEffectsLedger({ events, lastId })` rehydrated from the persisted\n * log on a wake, or a fresh `pendingEffectsLedger()` on a cold boot).\n * - `hooks.recordOwed` / `hooks.recordConfirmed` — the persist callbacks the\n * consumer points at its event log (so the ledger survives eviction).\n *\n * The carrier never persists anything itself (it does not own the consumer's\n * log) — it hands the consumer the events, faithful to \"persist the intent\n * before deliver\".\n */\nexport function durableCommandCarrier<R>(\n inner: DeferredGateway<R>,\n recorder: PendingEffectsRecorder<{ callId: string }>,\n hooks: {\n recordOwed(callId: string, event: EffectOwed<{ callId: string }>): void;\n recordConfirmed(callId: string, event: EffectConfirmed): void;\n },\n): DurableCommandCarrier<R> {\n return durableDeferredGateway(inner, recorder, hooks);\n}\n","/**\n * @demlik/tea/do — the idempotent-effect brick (the receiver/effect-side dedup).\n *\n * ## The half this closes (do NOT re-implement the ledger)\n *\n * The runtime's `stepDispatch` step order is SAVE-BEFORE-EFFECTS: state is\n * persisted (`store.save(next)`) *before* its commands are interpreted\n * (`packages/tea/src/index.ts` — \"Save-before-effects is the hard ordering;\n * tests pin it\"). So a DO evicted after `save` but before an effect's result is\n * folded RE-FIRES that effect on the next activation.\n *\n * `durable-effects.ts` (sibling) owns the SENDER half of this: the at-least-once\n * `pendingEffectsLedger`, whose surviving owed entries `reissueSurvivingEffects`\n * re-fires on activation. Its own header states the contract this file fulfils:\n * \"At-least-once ⇒ a confirmed-but-not-yet-persisted delivery WILL be re-sent\n * after a crash, so the receiver must deduplicate by id\" and \"the receiver owns\n * its own dedup.\" This module IS that receiver-side dedup — the piece that\n * upgrades the ledger's at-least-once re-fire to **effectively-once at the\n * external write**:\n *\n * - ledger (`survivingEffects` / `reissueSurvivingEffects`) = at-least-once re-fire;\n * - this brick (`idempotentEffect` + `appliedEffects`) = effect-side dedup\n * ⇒ together, effectively-once.\n *\n * It COMPLEMENTS the ledger; it does not re-implement it. The ledger keys owed\n * effects by a monotonic `DeliveryId` (its private sender bookkeeping); this\n * brick keys applied effects by a caller-supplied durable `EffectKey` (e.g.\n * `(runId, violationId)`), because dedup identity must be stable across the\n * re-fire — the same external write must resolve to the same key on every\n * activation, which a fresh monotonic id cannot guarantee.\n *\n * ## The load-bearing rule (mirrors `durable-effects.ts`)\n *\n * The applied-marker set is a PURE FOLD over the same kind of event stream the\n * actor already replays — it is **NOT** a separately-persisted side table:\n *\n * - `effect_applied` — `{ key }` ADDS the key to the applied set.\n * - `effect_forgotten` — `{ key }` REMOVES it (retention — see below).\n *\n * `foldApplied(events)` is `events.reduce(applyAppliedEvent, empty)` — the same\n * fold shape `replay` / `foldLedger` use. This makes the marker live \"alongside\n * State (single authority)\" for BOTH persistence modes:\n *\n * - `doEventSourcedStore`: persist the two events into the SAME log it already\n * appends; folding the log on activation rebuilds the applied set for free.\n * - default snapshot `doStore`: carry the `AppliedEffects` set as a field of\n * the Model, round-tripped by the store's `serialize`/`parse` hooks (a `Set`\n * flattens under `JSON.stringify`, exactly the `Map` sharp edge #182), so it\n * is checkpointed with the rest of `S`.\n *\n * Either way the marker is folded/snapshotted with State, never a side channel.\n *\n * ## Ordering — why the marker is recorded AFTER `fn` (at-least-once, not at-most)\n *\n * `run` invokes `fn` and only THEN yields the `effect_applied` event to persist.\n * An eviction in the window between `fn` succeeding and that event being folded\n * re-fires the effect (at-least-once) — correct, because recording the marker\n * BEFORE `fn` would let a crash strand the marker with the write never made\n * (at-most-once ⇒ a silently-dropped effect). Once the marker is folded, every\n * subsequent re-fire of that key is a proven no-op: `fn` is not invoked again.\n * The ledger guarantees the re-fire; this brick swallows the duplicate.\n *\n * ## Retention — pruning mirrors the ledger's confirm-removes-entry (not a new policy)\n *\n * An applied-marker is only needed while its effect can still re-fire. The\n * ledger stops re-firing an effect once its `effect_confirmed` removes the owed\n * entry — so at that same point the marker is dead weight and is pruned via\n * `effect_forgotten` (`guard.forget(key)`), keeping the applied set bounded. This\n * deliberately reuses the ledger's confirm-removes-entry retention shape rather\n * than inventing a second policy (the #227 triage's one open question).\n */\n\nimport type { Cmd } from \"../index\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// The two events the applied-marker set folds. Ordinary `Cmd` (Msg) variants —\n// persist them into the same log `doEventSourcedStore` appends, or carry the\n// folded set in the snapshot Model; either way the set rebuilds with no side\n// table. Mirrors `durable-effects.ts`'s `effect_owed`/`effect_confirmed`.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The caller-supplied durable dedup identity for an effect — stable across the\n * re-fire (e.g. `` `${runId}:${violationId}` ``). Unlike the ledger's monotonic\n * `DeliveryId`, it is chosen by the domain so the SAME external write resolves\n * to the SAME key on every activation.\n */\nexport type EffectKey = string;\n\n/** An effect keyed `key` has been APPLIED. Adds `key` to the set when folded. */\nexport interface EffectApplied extends Cmd<\"effect_applied\"> {\n readonly key: EffectKey;\n}\n\n/**\n * An applied-marker is no longer needed (its effect can no longer re-fire, e.g.\n * its ledger entry was confirmed) and may be pruned. Removes `key` when folded.\n */\nexport interface EffectForgotten extends Cmd<\"effect_forgotten\"> {\n readonly key: EffectKey;\n}\n\n/** The event union the applied-marker set folds over. */\nexport type AppliedEffectsEvent = EffectApplied | EffectForgotten;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// The applied-marker set — a pure value reconstructed by `foldApplied`. Never\n// persisted on its own; the events (or the snapshot that embeds it) are.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** The set of keys whose effect has been applied. A pure fold result. */\nexport type AppliedEffects = ReadonlySet<EffectKey>;\n\n/** The empty applied set — the fold's starting value (a fresh actor has\n * applied nothing). */\nexport function emptyApplied(): AppliedEffects {\n return new Set<EffectKey>();\n}\n\n/**\n * Apply ONE event — the reducer at the heart of the fold.\n *\n * - `effect_applied` → add `key` (a duplicate add is a no-op — a re-fired\n * already-applied key tolerated by at-least-once).\n * - `effect_forgotten` → remove `key` (a no-op if already absent).\n *\n * Pure: returns a NEW set on change, never mutates the input — so folding the\n * same events twice (live and post-eviction) yields equal results.\n */\nexport function applyAppliedEvent(\n applied: AppliedEffects,\n event: AppliedEffectsEvent,\n): AppliedEffects {\n switch (event.type) {\n case \"effect_applied\": {\n if (applied.has(event.key)) return applied;\n const next = new Set(applied);\n next.add(event.key);\n return next;\n }\n case \"effect_forgotten\": {\n if (!applied.has(event.key)) return applied;\n const next = new Set(applied);\n next.delete(event.key);\n return next;\n }\n }\n}\n\n/**\n * THE FOLD. Rebuild the applied set from an event stream — the same fold shape\n * `replay`/`foldLedger` use. After a simulated eviction this is called over the\n * persisted events and yields a set equal to the never-evicted one, because a\n * pure fold over identical input is identical. This is what proves \"no side\n * table\": the set is `events` reduced, nothing more.\n */\nexport function foldApplied(\n events: Iterable<AppliedEffectsEvent>,\n): AppliedEffects {\n let applied = emptyApplied();\n for (const event of events) applied = applyAppliedEvent(applied, event);\n return applied;\n}\n\n/** Has this key's effect already been applied (folded into the set)? */\nexport function isApplied(applied: AppliedEffects, key: EffectKey): boolean {\n return applied.has(key);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// idempotentEffect — the effect descriptor: a durable key paired with the effect\n// thunk. It is only a pairing; the `appliedEffects` guard decides whether to run\n// it. Mirrors how the ledger's `owe(effect)` pairs a payload with its bookkeeping.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A keyed external effect. `key` is the durable dedup identity; `fn` is the\n * irreversible external write (HTTP write, DB upsert, finding/ticket publish).\n */\nexport interface IdempotentEffect<A> {\n readonly key: EffectKey;\n readonly fn: () => Promise<A> | A;\n}\n\n/**\n * Pair a durable `key` with an external effect `fn` — this module's primary\n * surface. Run it through {@link appliedEffects}'s guard; a re-fire whose key is\n * already applied is a proven no-op (`fn` is not invoked a second time).\n */\nexport function idempotentEffect<A>(\n key: EffectKey,\n fn: () => Promise<A> | A,\n): IdempotentEffect<A> {\n return { key, fn };\n}\n\n/**\n * The outcome of running a keyed effect through the guard. A boolean-discriminated\n * union so the skipped arm cannot carry a (never-produced) `result`/`event`:\n *\n * - `ran: true` — first application: `fn` ran, `result` is its value, and\n * `event` is the `effect_applied` marker the caller MUST persist (fold into\n * State) so future re-fires are swallowed.\n * - `ran: false` — the key was already applied: `fn` was NOT invoked, the\n * duplicate is swallowed, and there is no new event to persist.\n */\nexport type IdempotentOutcome<A> =\n | {\n readonly ran: true;\n readonly key: EffectKey;\n readonly result: A;\n readonly event: EffectApplied;\n }\n | { readonly ran: false; readonly key: EffectKey };\n\n/**\n * A live guard over an applied-marker set. Mirrors `pendingEffectsLedger`'s\n * recorder shape: it keeps an in-memory mirror folded from the same events the\n * caller persists, so the dedup decision is available without a re-read.\n *\n * - `run(effect)` — apply-once: skip (no `fn`) if the key is already applied,\n * else await `fn` and yield the `effect_applied` event to PERSIST.\n * - `forget(key)` — prune a marker once its effect can no longer re-fire;\n * yields the `effect_forgotten` event to PERSIST.\n * - `applied()` — the current folded set.\n * - `isApplied(key)`— convenience membership test against the mirror.\n */\nexport interface AppliedEffectsGuard {\n run<A>(effect: IdempotentEffect<A>): Promise<IdempotentOutcome<A>>;\n forget(key: EffectKey): { readonly event: EffectForgotten };\n applied(): AppliedEffects;\n isApplied(key: EffectKey): boolean;\n}\n\n/**\n * Build a live guard. `restore` rehydrates it after an eviction: pass the events\n * read back from the log (or the applied set carried in the reloaded snapshot,\n * as single-element `effect_applied` events) so the mirror reflects what was\n * already applied. Omit `restore` for a fresh actor.\n */\nexport function appliedEffects(restore?: {\n readonly events?: Iterable<AppliedEffectsEvent>;\n}): AppliedEffectsGuard {\n let mirror: AppliedEffects = restore?.events\n ? foldApplied(restore.events)\n : emptyApplied();\n\n return {\n async run<A>(effect: IdempotentEffect<A>): Promise<IdempotentOutcome<A>> {\n // Already-applied ⇒ swallow the re-fire WITHOUT invoking `fn` — the proven\n // no-op the receiver owes the at-least-once ledger.\n if (mirror.has(effect.key)) {\n return { ran: false, key: effect.key };\n }\n // First application: run the effect, THEN record the marker (see the\n // ordering rationale in the module header — after, never before).\n const result = await effect.fn();\n const event: EffectApplied = { type: \"effect_applied\", key: effect.key };\n mirror = applyAppliedEvent(mirror, event);\n return { ran: true, key: effect.key, result, event };\n },\n forget(key: EffectKey): { readonly event: EffectForgotten } {\n const event: EffectForgotten = { type: \"effect_forgotten\", key };\n mirror = applyAppliedEvent(mirror, event);\n return { event };\n },\n applied(): AppliedEffects {\n return mirror;\n },\n isApplied(key: EffectKey): boolean {\n return mirror.has(key);\n },\n };\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @demlik/tea/do — CQRS projections as a first-class seam.\n *\n * TODAY (`./host`), \"Model → views\" is ad-hoc: `sseHub` is a hand-rolled\n * sink-set fanning `runtime.observe` events out. It re-derives a common shape:\n * subscribe to `observe`, fold each update into a private view, push the view\n * at a sink. (The run's terminal output was once another such observe-driven\n * read model — `captureLastTurn` — but that is now modelled first-class on the\n * write model itself as `state.output` / `runtime.result()`, #46.)\n *\n * This file names that shape. A `Projection<Model, View>` is the canon's\n * \"one write model, many projections\" contract made concrete on the tea\n * substrate (`.patterns/tea-do/projections.md`):\n *\n * 1. ONE WRITE MODEL, MANY PROJECTIONS. The write model is the agent's `run()`\n * State (`Model`) plus its `Msg` stream. Each projection is an INDEPENDENT\n * `(events|Model) → view` derivation into its OWN read model, with its own\n * id-scoped offset. Adding a projection never touches the write model or\n * any other projection — it only registers another consumer of `observe`.\n *\n * 2. OFFSET-RESUME + IDEMPOTENT APPLY ARE ONE UNIT. A durable projection\n * tracks an EXCLUSIVE offset — the last applied position. On restart it\n * resumes STRICTLY AFTER the stored offset (never ±1); at-least-once\n * delivery means the same update may be re-presented, so `apply` must be\n * idempotent (an upsert of the derived view, never a blind increment).\n * WHEN the offset is committed relative to the view write IS the delivery\n * semantic (`.patterns/tea-do/delivery-semantics.md`):\n * - commit AFTER the view write → at-least-once (may reprocess)\n * - commit in the SAME step → exactly-once (the default here)\n * We persist offset-with-view as one unit (a single `OffsetView` cell), so\n * a durable projection is exactly-once by construction; the at-least-once\n * replay window is still exercised because `drive()` may re-present an\n * update at/below the stored offset (boot reconcile, double-observe), and\n * the exclusive-offset guard makes that a no-op.\n *\n * 3. A VIEW IS ALWAYS REBUILDABLE. Clearing the view and resetting the offset\n * to the start (`rebuild`) replays the `Msg`/`Model` stream from zero. The\n * live view and the rebuilt view are equal by construction — both are the\n * same pure `apply` fold over the same ordered updates.\n *\n * Relationship to `./host`: `sseHub` is re-expressed here as ONE projection\n * (`sseProjection`) WITHOUT changing its public API — `host.sseHub()` keeps\n * working for existing callers (it is the volatile, offset-free case: a live\n * SSE stream has no durable read model to resume).\n *\n * Relationship to `./event-sourced-store`: a durable projection resumes over\n * the SAME append-only event log `doEventSourcedStore` (#68) writes. The fold\n * is the projection's `apply`, scoped to the projection's offset — independent\n * of the write model's snapshot/replay. This file does NOT import the store; it\n * is offset-shaped so a consumer can wire either the live `observe` stream or a\n * replay over the log into the same `apply`.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ProjectionId — name + key identifies a running instance.\n//\n// `name` is the view (\"sse\", \"report\", \"last-turn\"); `key` distinguishes\n// instances (shards, partitions). The stored offset is scoped to the id, so the\n// key is also what lets two instances of one view track offsets independently\n// (projections.md \"ProjectionId\"). Two projections sharing an id share an offset\n// row and would fight over it — so an id is the unit of offset isolation.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** A projection's identity: `name` (the view) + `key` (the instance). */\nexport interface ProjectionId {\n readonly name: string;\n readonly key: string;\n}\n\n/** Render a `ProjectionId` to its canonical `name-key` string. */\nexport function projectionIdString(id: ProjectionId): string {\n return `${id.name}-${id.key}`;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ProjectionErrorContext / ProjectionOnError — the isolation sink.\n//\n// A projection's `apply`/`emit` may throw. The driver isolates that throw so it\n// never strands sibling projections — but isolation is NOT swallowing\n// (errors-are-data). The caught throw routes to an injected `onError` sink,\n// tagged with the id of the projection that failed, so a broken projection\n// surfaces instead of vanishing. The runtime layer's `OnError`/`RuntimeError\n// Context` twin, kept here so `do/projection` stays a types-only boundary that\n// imports nothing from the substrate.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Context handed to a {@link ProjectionOnError} sink alongside the throw. */\nexport interface ProjectionErrorContext {\n /** The id of the projection whose `apply`/`emit` threw. */\n readonly id: ProjectionId;\n}\n\n/**\n * Sink for a projection `apply`/`emit` throw the driver isolated. Injected into\n * {@link projectionRegistry}; called with the original error and the id of the\n * failing projection so a broken projection surfaces instead of being silently\n * discarded (errors-are-data). Should be total.\n */\nexport type ProjectionOnError = (\n error: unknown,\n context: ProjectionErrorContext,\n) => void;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Update — the envelope a projection folds.\n//\n// What flows past `runtime.observe(observer)` is `(msg, state)` — the applied\n// `Msg` (the event) and the post-transition `Model` (the write-model state). A\n// projection can fold either: an event-shaped projection reads `msg`, a\n// state-shaped one reads `model`. `offset` is the exclusive position of THIS\n// update in the ordered stream (monotonic, 1-based; 0 means \"before the first\n// update\", i.e. the NoOffset start). The boot observe (`msg === null`) carries\n// the initial state and is offset 0 — a projection that only folds events skips\n// it; one that derives from state may seed its view from it.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * One unit the projection driver presents to a projection's `apply`. Mirrors\n * the `(msg, model)` pair `runtime.observe` fans out, plus the exclusive\n * `offset` the driver assigns. `msg === null` is the boot update (no event).\n */\nexport interface ProjectionUpdate<Model, Msg> {\n /** The applied event, or `null` on the boot update (initial state). */\n readonly msg: Msg | null;\n /** The post-transition write-model state. */\n readonly model: Model;\n /**\n * The exclusive position of this update in the ordered stream — monotonic,\n * 1-based; 0 is the boot update (the `NoOffset` start). The stored offset is\n * EXCLUSIVE: re-presenting an update whose offset is `<=` the stored offset\n * must be a no-op (the at-least-once replay window). Do NOT ±1 it.\n */\n readonly offset: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Projection — the named fold + its sink.\n//\n// `apply(view, update)` is the per-envelope reducer (projections.md \"Handler\"):\n// it derives the next `View` from the previous `View` and one update. It MUST be\n// pure and idempotent w.r.t. the update's offset — re-applying the same offset\n// yields the same view (upsert, not blind increment). `emit(view)` is the sink:\n// SSE frame, durable cell, report row. `initial` seeds the view for a fresh /\n// rebuilt run (the `NoOffset` start state).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A named CQRS projection: an independent fold of the write model's\n * `(Msg|Model)` stream into a private `View`, plus a sink to publish it.\n *\n * @typeParam Model The write-model state (`run()`'s `S`).\n * @typeParam Msg The write-model event (`run()`'s `M`).\n * @typeParam View The read model this projection derives.\n */\nexport interface Projection<Model, Msg extends { type: string }, View> {\n /** This projection's id (offset-isolation unit). */\n readonly id: ProjectionId;\n /** The seed view for a fresh or rebuilt run (the `NoOffset` start). */\n readonly initial: View;\n /**\n * The per-envelope reducer: derive the next view from the previous view and\n * one update. Pure; MUST be idempotent — re-applying an update at the same\n * offset yields the same view (upsert the derived value, never a blind\n * increment). Returning the SAME view reference (or an equal one) for an\n * update the view does not care about is the canonical \"skip\".\n */\n apply(view: View, update: ProjectionUpdate<Model, Msg>): View;\n /**\n * The sink — publish the derived view. SSE frame, durable cell, report row.\n * Fired by the driver after `apply` produces a new view. A throwing `emit` is\n * isolated by the driver (it must not strand sibling projections).\n */\n emit(view: View): void;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ProjectionRunner — a single projection's live read model + offset.\n//\n// Wraps one `Projection` with its current `view` and its EXCLUSIVE stored\n// `offset`. `present(update)` is the at-least-once entry point: it folds the\n// update IFF the update is strictly after the stored offset (the exclusivity\n// guard), commits the offset WITH the view as one unit (exactly-once by\n// construction), then emits. `reset()` clears the view to `initial` and the\n// offset to 0 — the canonical view-rebuild operation.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A live, running projection instance: the projection's current `View`, its\n * exclusive stored `offset`, and the `present`/`reset` operations the driver\n * (or a rebuild) calls.\n */\nexport interface ProjectionRunner<Model, Msg extends { type: string }, View> {\n readonly id: ProjectionId;\n /** The current derived view (the read model). */\n view(): View;\n /**\n * The exclusive stored offset — the position of the LAST applied update.\n * Resume reads strictly after this; `0` is the `NoOffset` start.\n */\n offset(): number;\n /**\n * Present one update to the projection. At-least-once entry point:\n * - if `update.offset <= storedOffset` the update was already applied →\n * NO-OP (idempotent replay window; offset stays, view stays, no emit).\n * - otherwise fold via `apply`, advance the offset TO `update.offset`, and\n * `emit` the new view. Offset-and-view advance together (one unit) →\n * exactly-once: the stored offset always matches the folded view.\n * Returns true iff the update was applied (false on the no-op replay).\n */\n present(update: ProjectionUpdate<Model, Msg>): boolean;\n /**\n * Clear the view to `initial` and the offset to 0 — the canonical rebuild\n * reset (offset-tracking.md: \"resetting it to NoOffset is the view-rebuild\n * operation\"). After `reset`, re-presenting the full stream rebuilds the view.\n */\n reset(): void;\n}\n\n/**\n * Build a {@link ProjectionRunner} for a projection, starting from a stored\n * offset (default 0 = `NoOffset`, a fresh run). The view is seeded to\n * `projection.initial`; the caller resumes by presenting updates strictly after\n * `startOffset` (the driver's offset guard enforces exclusivity even if an\n * already-applied update is re-presented).\n *\n * The runner never emits the seed: a fresh runner holds `initial` but does not\n * push it at the sink until the first real update lands (an SSE client that just\n * connected wants the next event, not a replay of the seed), and `reset()`\n * likewise re-seeds `view`/`offset` silently — `emit` fires only from `present`\n * when a strictly-newer update is folded.\n */\nexport function runProjection<Model, Msg extends { type: string }, View>(\n projection: Projection<Model, Msg, View>,\n startOffset = 0,\n): ProjectionRunner<Model, Msg, View> {\n let view = projection.initial;\n let storedOffset = startOffset;\n\n return {\n id: projection.id,\n view: () => view,\n offset: () => storedOffset,\n present(update) {\n // EXCLUSIVE-OFFSET GUARD. The stored offset is the last applied position;\n // an update at or below it was already folded into `view`. Re-presenting\n // it (boot reconcile, double-observe, log replay overlap) is the\n // at-least-once replay window — fold it again and a non-idempotent view\n // double-counts. We make the guard structural: skip it entirely. This is\n // what \"exclusive offset, do NOT ±1\" means in code — the stored value is\n // re-usable as-is as the resume point; the next applied update is the\n // first one strictly greater than it.\n if (update.offset <= storedOffset) return false;\n const next = projection.apply(view, update);\n // Offset advances WITH the view, as one unit — exactly-once. The stored\n // offset is never ahead of (nor behind) the view it describes.\n view = next;\n storedOffset = update.offset;\n projection.emit(next);\n return true;\n },\n reset() {\n view = projection.initial;\n storedOffset = 0;\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ProjectionRegistry — the driver. Fans `runtime.observe` to N projections.\n//\n// One write model, many projections: the registry holds a set of independent\n// runners and `dispatch(update)` presents the SAME update to each. Each runner\n// applies its own `apply` into its own view at its own offset — a failure or\n// reset in one never touches another (the driver isolates `apply`/`emit`\n// throws). `from(runtime)` wires the registry to `runtime.observe`, assigning a\n// monotonic exclusive offset per observed transition (the boot observe is\n// offset 0; the first real Msg is offset 1).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A driver over a set of projections sharing one write-model stream. Holds the\n * monotonic offset counter; presents each observed update to every registered\n * runner independently.\n */\nexport interface ProjectionRegistry<Model, Msg extends { type: string }> {\n /** Register a projection; returns its live runner. */\n register<View>(\n projection: Projection<Model, Msg, View>,\n startOffset?: number,\n ): ProjectionRunner<Model, Msg, View>;\n /**\n * Present one update to EVERY registered runner. Each folds independently; an\n * `apply`/`emit` throw in one runner is caught so it cannot strand the others\n * (errors-are-data: a broken projection is dropped from THIS update, not\n * allowed to corrupt siblings) and routed to the registry's `onError` sink so\n * it surfaces rather than vanishing. Returns the offset assigned to this update.\n */\n dispatch(msg: Msg | null, model: Model): number;\n /** The current monotonic offset (the last dispatched update's position). */\n offset(): number;\n /** The registered runners (introspection / tests). */\n runners(): readonly ProjectionRunner<Model, Msg, unknown>[];\n}\n\n/**\n * Build an empty {@link ProjectionRegistry}. Pass an `onError` sink to receive\n * any `apply`/`emit` throw the driver isolates — without it a broken projection\n * is still isolated from its siblings, but its failure is not surfaced (the\n * substrate's `run(machine, { onError })` is the natural source to forward).\n */\nexport function projectionRegistry<Model, Msg extends { type: string }>(\n onError?: ProjectionOnError,\n): ProjectionRegistry<Model, Msg> {\n const runners = new Set<ProjectionRunner<Model, Msg, unknown>>();\n // The boot observe is offset 0; the first applied Msg is offset 1. We start at\n // -1 and pre-increment so `dispatch(null, initialState)` (the boot fanout)\n // lands on 0 and the first real Msg on 1.\n let counter = -1;\n\n return {\n register(projection, startOffset) {\n const runner = runProjection(projection, startOffset);\n runners.add(runner as ProjectionRunner<Model, Msg, unknown>);\n return runner;\n },\n dispatch(msg, model) {\n const offset = ++counter;\n const update = { msg, model, offset };\n for (const runner of runners) {\n try {\n runner.present(update);\n } catch (err) {\n // A projection's apply/emit threw. Isolate it — the sibling runners\n // still see this update. The broken runner keeps its prior view +\n // offset (it did not advance), so it self-heals on a later update or\n // a rebuild. Isolation is NOT swallowing: route the throw to the\n // injected `onError` sink, tagged with the failing projection's id, so\n // it surfaces (errors-are-data: surface via the sink, never strand).\n onError?.(err, { id: runner.id });\n }\n }\n return offset;\n },\n offset: () => counter,\n runners: () => [...runners],\n };\n}\n\n/**\n * Wire a {@link ProjectionRegistry} to a runtime's transition stream. The boot\n * update (the initial state, offset 0) is presented via `onBoot`; every applied\n * transition (offset 1, 2, …) via `observe` — together they reproduce the\n * \"every transition including boot\" stream the single `observe(msg | null)`\n * channel used to carry, now that `observe`'s `msg` is total (#47). The registry\n * still assigns its monotonic exclusive offset (boot → 0, first Msg → 1).\n * Returns a cleanup that detaches both subscriptions.\n *\n * Typed structurally against `{ observe, onBoot }` so it accepts any\n * `Runtime<Model, Msg>` without importing the substrate's `Runtime` (types-only\n * boundary).\n */\nexport function driveProjections<Model, Msg extends { type: string }>(\n registry: ProjectionRegistry<Model, Msg>,\n runtime: {\n observe(observer: (msg: Msg, model: Model) => void): () => void;\n onBoot(handler: (model: Model) => void): () => void;\n },\n): () => void {\n // Boot first → the registry's pre-incremented counter lands it on offset 0,\n // exactly as the old `dispatch(null, model)` boot observe did.\n const unboot = runtime.onBoot((model) => {\n registry.dispatch(null, model);\n });\n const unobserve = runtime.observe((msg, model) => {\n registry.dispatch(msg, model);\n });\n return () => {\n unobserve();\n unboot();\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// rebuildProjection — replay a Msg/Model stream into a fresh runner.\n//\n// The rebuild operation (projections.md \"a view is rebuildable\"): start a fresh\n// runner (offset 0, view = initial) and present the ordered stream from the\n// start. The result equals the live view by construction — both are the same\n// pure `apply` fold over the same ordered updates. The exclusive-offset guard\n// makes the replay safe even if the stream overlaps an already-applied prefix.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Rebuild a projection's view by folding an ordered event stream from the\n * `NoOffset` start. The runner is seeded to `projection.initial` (the implicit\n * boot / `NoOffset` state), then each `updates[i]` is presented at offset\n * `i + 1` — so the FIRST event (`updates[0]`) lands at offset 1 and is folded,\n * exactly mirroring the live driver, which assigns the first applied `Msg`\n * offset 1 (the boot observe is offset 0 and never enters this list). Returns\n * the rebuilt runner whose `view()` equals the live runner's view for the same\n * ordered stream.\n *\n * Pass the ordered events only; do NOT prepend a boot (`msg === null`) entry.\n * With the `i + 1` offsets a leading boot entry would be applied at offset 1\n * like any event (the seed already covers the `NoOffset` start), not skipped —\n * the offset-0 skip that drops the boot is the live driver's, not this fold's.\n *\n * This is the rebuild-equivalence oracle: a view cleared and replayed from the\n * stream equals the live view.\n */\nexport function rebuildProjection<Model, Msg extends { type: string }, View>(\n projection: Projection<Model, Msg, View>,\n updates: readonly { readonly msg: Msg | null; readonly model: Model }[],\n): ProjectionRunner<Model, Msg, View> {\n const runner = runProjection(projection, 0);\n updates.forEach((u, i) => {\n // Present at offset `i + 1`, NOT `i`: the runner's stored offset starts at 0\n // (the `NoOffset` seed) and `present` skips any update with `offset <= 0`, so\n // an offset-`i` first event (offset 0) would be silently dropped. Offsetting\n // by one starts the stream at offset 1 — the live driver's first-Msg offset —\n // so `updates[0]` is folded and the rebuild mirrors the live view.\n runner.present({ msg: u.msg, model: u.model, offset: i + 1 });\n });\n return runner;\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @demlik/tea/do — `stepHost`, the HTTP-PULL control carrier (#92).\n *\n * ## Carrier selection — pick by WHO INITIATES the message\n *\n * `stepHost` is a SIBLING to {@link acceptCommandSocket}, never a replacement.\n * Both carriers commonly run together in ONE Durable Object; choose between\n * them per *plane*, by who sends the next message:\n *\n * - **Pull (this file — `stepHost` over `/step`)** — the CONTROL plane. One\n * driver (the hands) initiates every message: it asks for the next tool,\n * runs it, posts the result, asks again. There is exactly one outstanding\n * question at a time and the DO is COLD between steps — nothing is owed\n * while no request is in flight. Hibernation is the resting state, not a\n * failure: each POST wakes the DO, advances the run one step from its\n * durable checkpoint, and lets it hibernate again.\n * - **Push (`acceptCommandSocket` / `sseHub` — WS + SSE)** — the VIEW /\n * fan-out plane. The DO initiates UNSOLICITED messages: broadcast a frame\n * to every connected client, stream a live dashboard, push an event no\n * client asked for. A long-lived socket is the right carrier because the\n * sender is the DO, not a polling client.\n *\n * The rule in one line: **pull when the client drives a cold-between-steps\n * control loop; push when the DO drives unsolicited fan-out.** A hibernating\n * agent whose only traffic is \"run the next tool\" wants pull — that is the case\n * a production consumer adopted this carrier for.\n *\n * ## Why pull is hibernation-correct\n *\n * The WS push carrier keeps a socket open across steps; if the DO evicts, the\n * socket dies (or, with #91's `acceptDurableCommandSocket`, survives but still\n * holds an in-flight round-trip the heap forgot). The pull carrier holds NO\n * cross-step state in the heap. Every `/step` POST is a FRESH request that:\n *\n * 1. **validates** the per-run capability token (constant-time compare),\n * 2. **re-arms** the eviction-safe give-up alarm via `ctx.storage.setAlarm`\n * (the DO-native alarm API — the `do_alarm` Sub was dropped, see the\n * `host.ts` header; the alarm is a host concern, not a machine Sub),\n * 3. **settles** the posted tool result IDEMPOTENTLY through\n * `idempotent-intake` — a re-POSTed result is absorbed exactly once and\n * the duplicate replays the same next step,\n * 4. **resumes** the run from its durable checkpoint (the #68 event-sourced\n * store / #91 in-flight ledger) by dispatching the settle and running to\n * DISPATCH-QUIESCENCE,\n * 5. **returns** the next step (the next tool call) — or the terminal output\n * read off `runtime.result()` — and lets the DO HIBERNATE.\n *\n * No warm loop is held between responses. The durable checkpoint IS the loop's\n * continuation; the heap is disposable.\n *\n * ## It is ASSEMBLY over bricks tea already ships\n *\n * - `idempotent-intake` (`createIntake`) — the dedupe-by-key receive-once\n * settle. The posted result's `callId` is the idempotency key, so a\n * re-POST lands as a duplicate and replays the cached decision.\n * - `poller` primitives (`retry-backoff`) — the hands-side {@link runStepLoop}\n * pull loop: absolute deadline + backoff + dedupe-by-callId.\n * - the #68/#91 durable checkpoint — read by the consumer's `resume` callback\n * (it dispatches the settle into the runtime, whose `doEventSourcedStore`\n * rebuilt state from the log on this cold wake).\n *\n * This file owns NONE of those primitives — it wires them into the `/step`\n * request/response contract and the token lifecycle.\n */\n\nimport { createIntake } from \"../internal/idempotency/idempotent-intake/index\";\nimport {\n defaultRetryPolicy,\n initRetry,\n nextDelayMs,\n type RetryPolicy,\n type Rng,\n recordFailure,\n shouldRetry,\n} from \"../retry-backoff/index\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// The `/step` request/response envelope (#92.1).\n//\n// The control plane's wire contract. A request carries the capability token,\n// the runId, and the RESULT of the previously-returned step (null on the first\n// request, before any step has been handed out). A response carries the NEXT\n// step — the next tool call to execute — or a terminal `{ done, output }`.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The tool result the hands POST back for the step they just executed. `callId`\n * matches the step the previous response handed out (it is BOTH the correlation\n * id and the idempotency key — a re-POST carries the same `callId` and is\n * absorbed once). `result` is the opaque tool output `R`.\n */\nexport interface StepResult<R> {\n readonly callId: string;\n readonly result: R;\n}\n\n/**\n * A `/step` request. `token` is the per-run capability (validated\n * constant-time). `runId` names the run. `posted` is the result of the\n * previously-returned step, or `null` on the FIRST request (no step has been\n * handed out yet, so there is nothing to settle).\n */\nexport interface StepRequest<R> {\n readonly token: string;\n readonly runId: string;\n readonly posted: StepResult<R> | null;\n}\n\n/**\n * The next tool call to execute — the \"continue\" arm of a `/step` response.\n * `callId` is the correlation/idempotency id the hands echo back in the next\n * request's `posted`. `tool` + `input` describe the call (opaque to the host).\n */\nexport interface NextStep<I> {\n readonly callId: string;\n readonly tool: string;\n readonly input: I;\n}\n\n/**\n * A `/step` response — a discriminated union on `done`:\n * - `{ done: false, step }` — execute `step`, POST its result, ask again.\n * - `{ done: true, output }` — the run is terminal; `output` is its result.\n *\n * This is the ORIGINAL 2-arm INLINE contract, kept byte-identical: a host that\n * resolves each step inside the held request returns exactly these two shapes.\n * The not-ready arm ({@link StepWorking}) is a SEPARATE, opt-in type reachable\n * only through the defer-resume hook — see {@link DeferredStepResponse}. Keeping\n * `StepResponse` a strict subset is what makes inline adopters unaffected: their\n * `if (r.done) … else r.step` narrowing never sees a third member.\n */\nexport type StepResponse<I, O> =\n | { readonly done: false; readonly step: NextStep<I> }\n | { readonly done: true; readonly output: O };\n\n/**\n * The NOT-READY arm — the run is computing OUT-OF-BAND under a defer-resume host\n * (a production incident: a non-blocking pull carrier must\n * answer a pull with an explicit \"computing, poll again\" instead of holding the\n * request across a multi-second step). The held `/step` request returns this\n * IMMEDIATELY instead of inline-awaiting `engine.resume`; the hands re-poll\n * (optionally after `retryAfterMs`) until the deferred compute lands in the\n * durable checkpoint and a real {@link StepResponse} step/done arm is returned.\n *\n * It is a first-class discriminated member (`working: true`), NOT a hollow\n * `{ done: false }` with an absent/stale `step` — the not-ready state is made\n * representable so it can never be confused with a real pending step.\n */\nexport interface StepWorking {\n readonly done: false;\n readonly working: true;\n /**\n * Advisory earliest re-poll delay as a hint to the hands (ms to wait before\n * the next pull). Purely advisory — a host may omit it and the hands fall back\n * to their own backoff curve.\n */\n readonly retryAfterMs?: number;\n}\n\n/**\n * The 3-arm response a DEFER-RESUME host returns — the inline {@link StepResponse}\n * arms PLUS {@link StepWorking}. A superset of `StepResponse`, so a 2-arm value is\n * assignable to it (the widening is safe for existing transports); discriminate\n * with `if (r.done)` for terminal, then `\"working\" in r` for the not-ready arm,\n * else the step arm.\n */\nexport type DeferredStepResponse<I, O> = StepResponse<I, O> | StepWorking;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Capability token (#92.3) — a per-run bearer secret, constant-time compared.\n//\n// NOT OAuth: `token-refresh` is OAuth-shaped (access/refresh pairs, expiry,\n// rotation) and the WRONG tool here. A `/step` token is a single random secret\n// minted once per run and persisted in DO storage; every `/step` POST for that\n// run must present it. The compare is constant-time so a caller cannot probe\n// the secret byte-by-byte via response timing.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Mint a fresh per-run capability token: 32 random bytes, hex-encoded. Uses the\n * Web Crypto `crypto.getRandomValues` available in the Workers runtime (and in\n * Node's test runtime via the global `crypto`). Persist the returned string in\n * DO storage keyed by runId; hand the SAME string to the hands once, at run\n * start.\n */\nexport function mintRunToken(\n randomBytes: (n: number) => Uint8Array = defaultRandomBytes,\n): string {\n const bytes = randomBytes(32);\n let hex = \"\";\n for (const b of bytes) hex += b.toString(16).padStart(2, \"0\");\n return hex;\n}\n\nfunction defaultRandomBytes(n: number): Uint8Array {\n const out = new Uint8Array(n);\n crypto.getRandomValues(out);\n return out;\n}\n\n/**\n * Constant-time string equality. Returns `true` iff `a` and `b` are\n * byte-for-byte equal, in time that depends ONLY on `a.length` — never on WHERE\n * the first mismatch is. A length mismatch still returns `false`, but the\n * comparison loop always runs `a.length` iterations against a same-length view,\n * so the early-exit timing oracle a naive `===` leaks is closed.\n *\n * The XOR-accumulate idiom: fold every position's difference into one\n * accumulator and test it once at the end. No `break`, no short-circuit.\n */\nexport function constantTimeEqual(a: string, b: string): boolean {\n // Length is not secret (it is bounded + fixed by `mintRunToken`), so folding\n // it into the verdict up front is safe; the loop below still runs a fixed\n // number of iterations for a given `a`, leaking no position information.\n const lengthMatches = a.length === b.length;\n // Compare against `a` itself when lengths differ, so the loop body does the\n // same work every call and the mismatch is carried solely by `lengthMatches`.\n const rhs = lengthMatches ? b : a;\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ rhs.charCodeAt(i);\n }\n return diff === 0 && lengthMatches;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// stepHost (#92.2) — the `/step` handler.\n//\n// The host-layer assembly. The consumer supplies a `StepEngine<R, I, O>` — the\n// run-specific operations the handler orchestrates (token lookup, settle into\n// the runtime, resume to quiescence, read next step / terminal). `stepHost`\n// owns the ORDER (validate → setAlarm → idempotent-settle → resume → return →\n// hibernate), the idempotency, and the HTTP status mapping; the engine owns the\n// run-specific I/O. This is the same dependency-injection shape as\n// `durableCommandCarrier` (hooks) and `createAgentHost` (mappings).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The DO-native alarm slice `stepHost` re-arms. A real `DurableObjectStorage`\n * satisfies this structurally; a test supplies a fake (no live Workers runtime).\n * Only `setAlarm` is needed — the give-up deadline is re-armed on every step so\n * a run abandoned mid-loop (the hands stop POSTing) is eventually evicted by the\n * alarm firing, not left resident forever.\n */\nexport interface AlarmStorage {\n setAlarm(scheduledTime: number): void | Promise<void>;\n}\n\n/** The minimal `ctx` slice `stepHost` reads — just the alarm-bearing storage. */\nexport interface StepCtx {\n readonly storage: AlarmStorage;\n}\n\n/**\n * The run-specific operations `stepHost` orchestrates. The consumer implements\n * these against its runtime + storage; `stepHost` never reaches into the agent\n * machine itself (that lives in `../agent`, off-limits to this carrier).\n */\nexport interface StepEngine<R, I, O> {\n /**\n * The persisted capability token for `runId`, or `null` if the run is\n * unknown. Read from DO storage. `stepHost` compares the request token\n * against this constant-time.\n */\n tokenFor(runId: string): Promise<string | null> | string | null;\n /**\n * Settle the posted tool result into the run, then RESUME from the durable\n * checkpoint: dispatch the settle and run to DISPATCH-QUIESCENCE (e.g.\n * `await runtime.dispatch(settleMsg)` — dispatch drains the follow-up chain\n * by default, #50). Called at most once per distinct\n * `callId` — `stepHost` dedupes re-POSTs via `idempotent-intake` BEFORE\n * calling this, so the side effect runs exactly once. On the first request\n * (`posted === null`) `stepHost` skips settle and calls {@link nextStep}\n * directly to hand out the first step.\n */\n resume(runId: string, posted: StepResult<R>): Promise<void> | void;\n /**\n * Read the next step the run is now waiting on — the next tool call — off the\n * resumed runtime's durable state, or `null` if the run has reached its\n * terminal State (then {@link terminalOutput} supplies the output).\n */\n nextStep(runId: string): Promise<NextStep<I> | null> | NextStep<I> | null;\n /**\n * Read the run's terminal output (off `runtime.result()`). Only called when\n * {@link nextStep} returned `null`.\n */\n terminalOutput(runId: string): Promise<O> | O;\n}\n\n/** Tuning for `stepHost`'s give-up alarm. */\nexport interface StepHostConfig {\n /**\n * How far ahead, in ms, to re-arm the give-up alarm on every step. A run that\n * stops POSTing for longer than this is abandoned and the alarm fires (the\n * consumer's `alarm()` handler tears the run down). Defaults to 5 minutes.\n */\n readonly giveUpAfterMs?: number;\n}\n\n/**\n * The DEFER-RESUME hook — the opt-in seam that drives `engine.resume` OUT of the\n * held `/step` request. A non-blocking host (a non-blocking production audit carrier)\n * cannot inline-await a multi-second step inside the held request, so instead of\n * `stepHost` calling `engine.resume` inline it hands the posted result to this\n * hook to SETTLE-AND-ENQUEUE (record it durably, schedule the compute in a\n * returning activation) and returns the {@link StepWorking} arm PROMPTLY. A later\n * pull re-reads the durable next-step once {@link DeferResumeHook.settled} reports\n * the enqueued compute has landed.\n *\n * Providing this hook is what flips `stepHost` from the 2-arm inline contract to\n * the 3-arm deferred contract — see the `stepHost` overloads. Omit it and the\n * host stays byte-identical inline.\n */\nexport interface DeferResumeHook<R> {\n /**\n * SETTLE-AND-ENQUEUE the posted result to resume OUT of the held request:\n * durably record it and schedule the compute (e.g. `ctx.storage.setAlarm` or a\n * queue) so a returning activation runs `engine.resume`. MUST return promptly —\n * it never awaits the step compute. Called at most once per distinct `callId`\n * (`stepHost` dedupes re-POSTs through `idempotent-intake` first), so a re-POST\n * of the same step does not re-enqueue.\n */\n enqueue(runId: string, posted: StepResult<R>): void | Promise<void>;\n /**\n * Has the enqueued resume for `callId` COMPLETED and been written to the\n * durable checkpoint? `false` ⇒ the held request returns {@link StepWorking}\n * (poll again); `true` ⇒ `stepHost` reads the next step / terminal output off\n * the now-advanced checkpoint, exactly as the inline path does.\n */\n settled(runId: string, callId: string): boolean | Promise<boolean>;\n /**\n * Optional advisory re-poll delay (ms) surfaced on the {@link StepWorking} arm\n * so the hands can back off to the host's expected step latency.\n */\n readonly retryAfterMs?: number;\n}\n\n/**\n * `StepHostConfig` with the defer-resume hook engaged — the presence of\n * `deferResume` is the type-level switch that selects the 3-arm response (see the\n * `stepHost` overloads). Extends the inline config, so the alarm tuning is shared.\n */\nexport interface DeferStepHostConfig<R> extends StepHostConfig {\n readonly deferResume: DeferResumeHook<R>;\n}\n\n/**\n * A structured `/step` outcome `stepHost` returns — the response body plus the\n * HTTP status the consumer's route should send. Kept transport-agnostic (no\n * `Response`) so the same handler is unit-testable without a live `fetch`.\n */\nexport type StepOutcome<I, O> =\n | { readonly status: 200; readonly body: StepResponse<I, O> }\n | { readonly status: 401 | 404; readonly body: { readonly error: string } };\n\n/**\n * The `/step` outcome a DEFER-RESUME host returns — like {@link StepOutcome} but\n * its 200 body is the 3-arm {@link DeferredStepResponse} (it can carry the\n * not-ready arm). Returned only from the `deferResume` overload of `stepHost`;\n * the inline overload keeps the narrower {@link StepOutcome}.\n */\nexport type DeferredStepOutcome<I, O> =\n | { readonly status: 200; readonly body: DeferredStepResponse<I, O> }\n | { readonly status: 401 | 404; readonly body: { readonly error: string } };\n\nconst DEFAULT_GIVE_UP_MS = 5 * 60_000;\n\n/**\n * Build the {@link StepWorking} arm, attaching `retryAfterMs` only when the host\n * supplied one — so the wire body stays minimal (`{ done, working }`) unless a\n * re-poll hint is offered.\n */\nfunction workingBody(retryAfterMs?: number): StepWorking {\n return retryAfterMs === undefined\n ? { done: false, working: true }\n : { done: false, working: true, retryAfterMs };\n}\n\n/**\n * Build the `/step` handler for a DO that drives one or more runs over the pull\n * carrier. Returns `handle(request, now)` — call it from the DO's `/step`\n * route, map the `StepOutcome` to a `Response`, and let the DO hibernate.\n *\n * The handler is STATELESS between calls except for its `idempotent-intake`\n * dedupe ledger, which the consumer should itself persist alongside the run's\n * checkpoint if cross-eviction dedupe of an identical re-POST is required; the\n * in-isolate ledger here dedupes re-POSTs within one activation (the common\n * \"the hands' POST timed out and got retried\" case), and the underlying\n * `resume` is itself idempotent against the durable checkpoint, so a re-POST\n * that crosses an eviction re-settles harmlessly.\n *\n * @param ctx the DO state slice (alarm-bearing storage)\n * @param engine the run-specific operations\n * @param config alarm tuning\n */\n// Overloads (specific → general): a config carrying `deferResume` selects the\n// 3-arm deferred handler; every other call — no config, or alarm-only config —\n// keeps the 2-arm inline handler, byte-and-type-identical to before this change.\nexport function stepHost<R, I, O>(\n ctx: StepCtx,\n engine: StepEngine<R, I, O>,\n config: DeferStepHostConfig<R>,\n): {\n handle(\n request: StepRequest<R>,\n now: number,\n ): Promise<DeferredStepOutcome<I, O>>;\n};\nexport function stepHost<R, I, O>(\n ctx: StepCtx,\n engine: StepEngine<R, I, O>,\n config?: StepHostConfig,\n): {\n handle(request: StepRequest<R>, now: number): Promise<StepOutcome<I, O>>;\n};\nexport function stepHost<R, I, O>(\n ctx: StepCtx,\n engine: StepEngine<R, I, O>,\n config: StepHostConfig | DeferStepHostConfig<R> = {},\n): {\n handle(\n request: StepRequest<R>,\n now: number,\n ): Promise<DeferredStepOutcome<I, O>>;\n} {\n const giveUpAfterMs = config.giveUpAfterMs ?? DEFAULT_GIVE_UP_MS;\n // The opt-in defer-resume hook. Present ⇒ resume runs OUT of the held request\n // and a not-ready pull returns the working arm; absent ⇒ the inline path below\n // runs unchanged. `\"deferResume\" in config` is the runtime switch matching the\n // type-level overload split.\n const deferResume: DeferResumeHook<R> | null =\n \"deferResume\" in config ? config.deferResume : null;\n\n // The receive-once settle ledger. Key = the posted result's `callId`, so a\n // re-POST of the SAME step lands as a duplicate and replays the cached\n // decision instead of re-settling. `R = \"settled\"` — the cached value is just\n // a marker that this callId's settle already happened; the actual next step is\n // re-read off the (idempotent) durable checkpoint either way.\n const intake = createIntake<StepResult<R>, \"settled\">({\n keyOf: (posted) => posted.callId,\n });\n // The intake slice lives in the isolate for this activation. It is a plain\n // serializable value (the consumer MAY persist `intakeState` next to its\n // checkpoint for cross-eviction dedupe); within one activation it absorbs the\n // duplicate POST so `resume` fires at most once per callId here.\n let intakeState = intake.init();\n\n async function handle(\n request: StepRequest<R>,\n now: number,\n ): Promise<DeferredStepOutcome<I, O>> {\n // 1 — VALIDATE the capability token (constant-time). A missing run is 404;\n // a present run with a wrong/missing token is 401. The constant-time\n // compare runs regardless of whether the stored token exists (compare\n // against the empty string) so the 401 path leaks no timing.\n const stored = await engine.tokenFor(request.runId);\n if (stored === null) {\n return { status: 404, body: { error: \"unknown run\" } };\n }\n if (!constantTimeEqual(request.token, stored)) {\n return { status: 401, body: { error: \"invalid token\" } };\n }\n\n // 2 — RE-ARM the eviction-safe give-up alarm via the DO-native alarm API.\n // Every step pushes the deadline out; a run that stops POSTing is\n // eventually evicted when the alarm fires. (The `do_alarm` Sub was\n // dropped — see the `host.ts` header — so this is a direct storage call,\n // not a machine Sub.)\n await ctx.storage.setAlarm(now + giveUpAfterMs);\n\n // 3 — SETTLE the posted result (skip on the first request, where there is\n // nothing to settle). Two shapes, chosen by whether the defer-resume\n // hook is engaged:\n const posted = request.posted;\n if (posted !== null) {\n if (deferResume === null) {\n // INLINE (default, unchanged) — settle + resume idempotently INSIDE the\n // held request. `intake.receive` decides new-vs-duplicate and emits the\n // decision as a Cmd; a genuinely-new callId runs `resume` exactly once, a\n // duplicate replays (no re-settle). Either way step 4 re-reads the next\n // step off the durable checkpoint, so a duplicate POST returns the SAME\n // next step.\n const [nextIntake, cmds] = intake.receive(\n intakeState,\n posted,\n now,\n posted.callId,\n );\n intakeState = nextIntake;\n for (const cmd of cmds) {\n if (cmd.type === \"intake:process\") {\n await engine.resume(request.runId, posted);\n const [completed] = intake.complete(\n intakeState,\n cmd.key,\n \"settled\",\n now,\n );\n intakeState = completed;\n }\n // `intake:replay` (a duplicate) needs no action: `resume` already ran\n // for this callId, and step 4 re-derives the next step below.\n }\n } else {\n // DEFER (opt-in) — resume runs OUT of the held request. Readiness is the\n // gate: if the enqueued resume has already landed in the durable\n // checkpoint, fall through to step 4 and read the next step (identical to\n // the inline tail). If not, (idempotently) enqueue the resume to run in a\n // returning activation and return the WORKING arm — the held request\n // never awaits the step compute (the same production incident). The\n // per-callId enqueue is idempotent host-side, so re-POSTs across cold\n // wakes do not re-enqueue; the in-isolate `intake` ledger is not used on\n // this path because it cannot survive the eviction between pulls.\n const ready = await deferResume.settled(request.runId, posted.callId);\n if (!ready) {\n await deferResume.enqueue(request.runId, posted);\n return { status: 200, body: workingBody(deferResume.retryAfterMs) };\n }\n }\n }\n\n // 4 — RETURN the next step, or the terminal output. Read off the resumed\n // run's durable state — NOT off any warm loop. The DO hibernates after\n // this response; the checkpoint is the continuation.\n const next = await engine.nextStep(request.runId);\n if (next === null) {\n const output = await engine.terminalOutput(request.runId);\n return { status: 200, body: { done: true, output } };\n }\n return { status: 200, body: { done: false, step: next } };\n }\n\n return { handle };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// runStepLoop (#92.4) — the hands-side pull loop over `/step`.\n//\n// The thin client the HANDS run: POST `/step` → get the next tool → execute it\n// (consumer callback) → POST the result → repeat until terminal. Built on the\n// `poller` primitives (`retry-backoff`) — absolute deadline + backoff + dedupe\n// by callId — exactly the loop the poller knob composes internally, lifted to a\n// standalone async driver (the hands are not a TEA machine; they drive one).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The `/step` transport the hands POST through (injected; faked in tests).\n * Returns the 3-arm {@link DeferredStepResponse} — a defer-resume host may answer\n * with the not-ready arm, which {@link runStepLoop} re-polls. An inline host's\n * 2-arm transport (`=> Promise<StepResponse>`) is assignable here (the narrower\n * return widens), so existing inline adopters are unaffected.\n */\nexport type StepTransport<R, I, O> = (\n request: StepRequest<R>,\n) => Promise<DeferredStepResponse<I, O>>;\n\n/** Execute one tool call and produce its result (the consumer's hands). */\nexport type ExecuteStep<I, R> = (step: NextStep<I>) => Promise<R> | R;\n\n/**\n * What {@link ExecutedSteps} holds for a step that ran. The box is what tells a\n * step whose result is `undefined` apart from a step that never ran, and it\n * still does after a JSON round trip (`{}` is a box; a missing entry is not).\n */\nexport interface ExecutedStep<R> {\n readonly result: R;\n}\n\n/**\n * The hands' memory of the steps they already ran, keyed by `callId`. Pass one\n * that outlives the process and a restarted {@link runStepLoop} re-POSTs a saved\n * result instead of running the step again.\n *\n * Use one store per run: a host's `callId` only has to be unique inside its run.\n * The caller owns where it lives and when it is cleared.\n */\nexport interface ExecutedSteps<R> {\n /** The box saved for `callId`, or `undefined` if that step never ran. */\n get(\n callId: string,\n ): Promise<ExecutedStep<R> | undefined> | ExecutedStep<R> | undefined;\n /** Save the box for `callId`. Settled before the result is POSTed. */\n set(callId: string, step: ExecutedStep<R>): Promise<void> | void;\n}\n\n/**\n * Tuning for {@link runStepLoop}.\n *\n * `R` is the step result type, and only {@link RunStepLoopConfig.executed} reads\n * it. It defaults to `never` because a bare `RunStepLoopConfig` says nothing\n * about results: it has to fit a drive of any `R`, and a store of `unknown`\n * could not hand a drive its `R` back.\n */\nexport interface RunStepLoopConfig<R = never> {\n /**\n * Absolute wall-clock deadline (ms epoch). The loop stops asking once `now()`\n * reaches it, surfacing a `deadline_exceeded` outcome. Mirrors the poller's\n * absolute-deadline contract (a resumed loop honors the SAME moment).\n */\n readonly deadlineMs: number;\n /**\n * Where the hands remember the steps they ran. Defaults to a fresh in-memory\n * store per drive, which a restart forgets. See {@link runStepLoop}.\n */\n readonly executed?: ExecutedSteps<R>;\n /** Backoff policy for FAILED `/step` POSTs. Defaults to `defaultRetryPolicy`. */\n readonly retry?: RetryPolicy;\n /** Clock, injected for determinism. Defaults to `Date.now`. */\n readonly now?: () => number;\n /** `(ms) => Promise` sleep, injected for determinism. Defaults to real sleep. */\n readonly sleep?: (ms: number) => Promise<void>;\n /** Backoff jitter RNG, injected for determinism. Defaults to `Math.random`. */\n readonly rng?: Rng;\n}\n\n/** The terminal outcome of a full {@link runStepLoop} drive. */\nexport type StepLoopOutcome<O> =\n | { readonly kind: \"done\"; readonly output: O }\n | { readonly kind: \"deadline_exceeded\" }\n | { readonly kind: \"gave_up\"; readonly error: unknown };\n\n/**\n * Drive the pull loop to completion. Starting from `{ posted: null }`, POST\n * `/step`, and for each non-terminal response execute the step and POST its\n * result, until the response is terminal (`done: true`) — then return its\n * output.\n *\n * Resilience composed from `poller`'s primitives:\n * - **absolute deadline** — `config.deadlineMs`. Before each POST the loop\n * checks `now() >= deadlineMs` and stops with `deadline_exceeded` rather\n * than asking forever.\n * - **backoff** — a FAILED POST (transport reject) is retried on the\n * `retry-backoff` curve; once `shouldRetry` is exhausted the loop stops\n * with `gave_up`. A success resets the retry counter.\n * - **dedupe** — a step whose `callId` is already in `config.executed` is not\n * re-executed; its saved result is re-POSTed. Closes the \"the host re-handed\n * the same step after a duplicate settle\" path (the host side is idempotent;\n * the hands match it). That memory lives in `config.executed`. Left out, it\n * is a fresh in-memory store that lasts one drive, so hands that restart\n * forget it and run the re-handed step again; pass a store that outlives the\n * process to keep it across restarts.\n * - **store failure** — a `get` or `set` that throws or rejects ends the drive\n * with `gave_up` carrying the store's error. A failed `get` runs nothing. A\n * failed `set` means the step ran but is not remembered, and its result is\n * not POSTed, so a later drive over that store would run it again.\n * - **not-ready re-poll** — a {@link StepWorking} arm (a defer-resume host\n * computing the step out-of-band) is not a step: the loop re-POSTs the SAME\n * `posted` after a backoff (honoring the host's advisory `retryAfterMs`),\n * until a real step / done arrives. Bounded only by the absolute deadline —\n * an inline host never returns this arm, so its drive is unchanged.\n *\n * **Crash window:** a store gives \"run once per `callId`\" except between\n * `execute` finishing and `set` landing. Hands that die there have run the step\n * with no record of it, and the next drive runs it again. No store can close\n * that: the step itself has to be safe to repeat, as with `idempotentEffect`.\n */\nexport async function runStepLoop<R, I, O>(\n token: string,\n runId: string,\n transport: StepTransport<R, I, O>,\n execute: ExecuteStep<I, R>,\n config: RunStepLoopConfig<R>,\n): Promise<StepLoopOutcome<O>> {\n const policy = config.retry ?? defaultRetryPolicy;\n const now = config.now ?? Date.now;\n const sleep = config.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n const rng = config.rng;\n\n // Dedupe-by-callId: the result already produced for a callId, so a re-handed\n // step re-POSTs the saved result instead of re-executing.\n const executed = config.executed ?? stepsInMemory<R>();\n // The result to POST on the NEXT request (null on the first request).\n let posted: StepResult<R> | null = null;\n // Consecutive not-ready (\"working\") polls, driving the re-poll backoff curve.\n // Reset on any real step / terminal response. Bounded by the absolute deadline,\n // NOT by `maxAttempts` — a host may legitimately compute for many polls.\n let pollRetry = initRetry();\n\n for (;;) {\n if (now() >= config.deadlineMs) return { kind: \"deadline_exceeded\" };\n\n // POST `/step` with backoff on transport failure.\n let response: DeferredStepResponse<I, O>;\n let retry = initRetry();\n for (;;) {\n try {\n response = await transport({ token, runId, posted });\n break;\n } catch (error) {\n retry = recordFailure(retry, error);\n if (!shouldRetry(retry, policy)) return { kind: \"gave_up\", error };\n if (now() >= config.deadlineMs) return { kind: \"deadline_exceeded\" };\n await sleep(nextDelayMs(retry, policy, rng));\n }\n }\n\n if (response.done) return { kind: \"done\", output: response.output };\n\n // NOT-READY arm — a defer-resume host is computing the step OUT-OF-BAND. Do\n // NOT execute anything: re-poll with the SAME `posted` (idempotent re-POST)\n // after a backoff, until the host yields a real step / done. Honor the host's\n // advisory `retryAfterMs` when present, else fall back to the poll backoff\n // curve. The absolute deadline is the only terminal bound here.\n if (\"working\" in response) {\n pollRetry = recordFailure(pollRetry, \"working\");\n if (now() >= config.deadlineMs) return { kind: \"deadline_exceeded\" };\n await sleep(response.retryAfterMs ?? nextDelayMs(pollRetry, policy, rng));\n continue;\n }\n pollRetry = initRetry(); // a real step resets the poll backoff\n\n // Execute the next step (dedupe: a callId already remembered re-POSTs its\n // saved result rather than executing twice). Only the store's own failures\n // become `gave_up`; a throwing `execute` still rejects the drive.\n const step = response.step;\n let saved: ExecutedStep<R> | undefined;\n try {\n saved = await executed.get(step.callId);\n } catch (error) {\n return { kind: \"gave_up\", error };\n }\n if (saved === undefined) {\n saved = { result: await execute(step) };\n try {\n await executed.set(step.callId, saved);\n } catch (error) {\n return { kind: \"gave_up\", error };\n }\n }\n posted = { callId: step.callId, result: saved.result };\n }\n}\n\n/** The default {@link ExecutedSteps}: lives as long as one drive. */\nfunction stepsInMemory<R>(): ExecutedSteps<R> {\n const steps = new Map<string, ExecutedStep<R>>();\n return {\n get: (callId) => steps.get(callId),\n set: (callId, step) => {\n steps.set(callId, step);\n },\n };\n}\n","/// <reference types=\"@cloudflare/workers-types\" />\n/**\n * @packageDocumentation\n * @demlik/tea/do — Durable Object adapter for `@demlik/tea`.\n *\n * The integration is built on ONE transport model and one durability primitive:\n *\n * 1. `doStore<S>(storage, parse, key?)` — `Store<S>` impl backed by\n * `DurableObjectStorage`. JSON-stringifies on save, JSON-parses on load\n * and returns `unknown` (invariant-8 boundary); the substrate then calls\n * `migrate(raw)` (which forwards to `parse`) to turn it into `S`, `null`\n * or a refusal. Returns `null` (typed as `unknown`) when the key is\n * absent. Structural JSON malformation throws at load, and `run()`'s boot\n * path turns that into a `StoreRefusedError` on `ready`. A shape\n * mismatch is `parse`'s to answer with `refuse(reason)` (#316).\n *\n * 2. `deferredGateway` + `createAgentHost` (from `./host`) — THE transport for\n * a DO-hosted agent. A `createAgent().toMachine()` machine owns exactly one\n * Sub type, `DeadlinesSub` (the agent's own retry + watchdog timers); it has\n * no DO-native Sub variant to compose, and it needs none. The gateway owns\n * each deferred tool round-trip as a Promise the interpret cell awaits:\n * - Inbound WebSocket frames are bridged straight into `gateway.settle(...)`\n * from the DO's `webSocketMessage` lifecycle method — a direct dispatch,\n * NOT a Sub.\n * - The per-tool deadline is a `setTimeout` inside the gateway, NOT a DO\n * alarm.\n * One model: the transport lives outside the Sub system, bridged into\n * `dispatch` by the gateway. See `./host`'s module doc for the full rationale\n * and `.patterns/tea/durable-actors.md` for the host-layer north star.\n *\n * (Historical note: this module previously also shipped `do_ws` / `do_alarm`\n * Subs + a `doSubscribe` registry. Because `toMachine` fixes the agent machine's\n * Sub type to `DeadlinesSub`, those Subs could never union into an agent host's\n * Sub set — they were structurally unusable by the flagship consumer and had no\n * callers. They were dropped in favor of the gateway as the single transport.)\n */\n\nimport type { DeletableStore, FencedStore, Migrated } from \"../index\";\nimport { StoreConflictError } from \"../index\";\n\n// Durable pending-effects ledger (ADR 0003 primitive #1 — durable effects).\n// A pure fold over `effect_owed` / `effect_confirmed` events (NOT a side\n// table): owed adds, confirmed removes, and the surviving entries re-emit on\n// activation, idempotent by monotonic delivery id. Persist the two events into\n// the same log `doEventSourcedStore` appends and the ledger rebuilds for free.\nexport {\n applyEffectEvent,\n type DeliveryId,\n type EffectConfirmed,\n type EffectLedgerEvent,\n type EffectOwed,\n emptyLedger,\n foldLedger,\n isOwed,\n type OwedEffect,\n type PendingEffectsLedger,\n type PendingEffectsRecorder,\n pendingEffectsLedger,\n survivingEffects,\n} from \"./durable-effects\";\n// The first-class durable-timer / alarm-carrier (#180). Every native-DO grain\n// (raft's election/heartbeat deadline, vortex's game tick) hand-rolled the same\n// compute-next-deadline → `setAlarm` → `alarm()` → fire → re-arm loop after the\n// `do_alarm` Sub was dropped (see the historical note above). `durableTimer`\n// owns that loop in ONE tested place: idle-when-no-work (`nextDeadline` → null),\n// eviction-safe across hibernation (the deadline is a pure function of persisted\n// state, re-armed on cold wake), alarm injected as the same minimal\n// `AlarmStorage` port `stepHost` takes. See `./durable-timer`.\nexport {\n type DurableTimer,\n type DurableTimerConfig,\n durableTimer,\n} from \"./durable-timer\";\n// Opt-in event-sourcing persistence mode. `doStore` below stays the DEFAULT\n// (snapshot-only, byte-for-byte unchanged); `doEventSourcedStore` is the\n// explicit alternative that appends each Msg to a log, snapshots periodically,\n// and rebuilds state by folding the log on the latest snapshot at activation.\nexport {\n doEventSourcedStore,\n type EventLogRange,\n type EventSourcedOptions,\n type EventSourcedStore,\n type PersistedEvent,\n} from \"./event-sourced-store\";\n// The minimum-viable DO HOST for a `createAgent` runtime — the deferred-tool\n// gateway, auto-boot, WS accept + inbound bridge, runtime→SSE plumbing, the\n// terminal-output capture. Kept in a sibling file so this\n// module stays the durability + subs surface; the host re-exports through the\n// same `@demlik/tea/do` subpath the consumer already imports `doStore` from.\nexport {\n type AgentHost,\n type AgentHostConfig,\n type AgentHostWiring,\n acceptCommandSocket,\n acceptDurableCommandSocket,\n agentIsResumable,\n autoBoot,\n bootResume,\n broadcast,\n broadcastHibernatable,\n createAgentHost,\n type DeferredGateway,\n type DurableCommandCarrier,\n deferredGateway,\n durableCommandCarrier,\n durableDeferredGateway,\n type HibernatableCtx,\n type ResumePort,\n reissueSurvivingEffects,\n type SseHub,\n sseFromAgentEvents,\n sseHub,\n sseProjection,\n} from \"./host\";\n// The receiver/effect-side dedup half of durable effects (#227). The ledger\n// above is at-least-once (surviving owed effects re-fire on activation); this\n// brick swallows the duplicate at the external write, keyed by a caller-supplied\n// durable `EffectKey` folded into the Store alongside State (an `effect_applied`\n// marker, pruned by `effect_forgotten` — the ledger's confirm-removes-entry\n// retention). Together: effectively-once. See `./idempotent-effect`.\nexport {\n type AppliedEffects,\n type AppliedEffectsEvent,\n type AppliedEffectsGuard,\n appliedEffects,\n applyAppliedEvent,\n type EffectApplied,\n type EffectForgotten,\n type EffectKey,\n emptyApplied,\n foldApplied,\n type IdempotentEffect,\n type IdempotentOutcome,\n idempotentEffect,\n isApplied,\n} from \"./idempotent-effect\";\n// Generic hibernatable PRESENCE + BROADCAST for a NATIVE (non-agent) DO grain\n// (#181). The `./host` broadcast/accept helpers above are framed for the\n// LLM-agent deferred-tool gateway (`createAgentHost`); these are the decoupled,\n// transport/Msg-agnostic equivalents a game / presence / collab grain uses —\n// `broadcastFrame` takes the socket SET as input (DI-friendly, pure, testable\n// with fakes), and `acceptPresenceSocket` / `registerHibernatableSocket` lift\n// the `WebSocketPair` + `ctx.acceptWebSocket` + `serializeAttachment` boilerplate.\nexport {\n type AttachableSocket,\n acceptPresenceSocket,\n type BroadcastOptions,\n type BroadcastReport,\n broadcastFrame,\n type PresenceCtx,\n type PresenceSocket,\n type PresenceUpgrade,\n presenceCount,\n type RegisterOptions,\n registerHibernatableSocket,\n WS_READY_STATE_OPEN,\n} from \"./presence\";\n// CQRS projections as a first-class seam (#69). One write model, many\n// projections: each is an independent `(events|Model) → view` fold into its own\n// id-scoped read model with an EXCLUSIVE offset (resume + idempotent apply are\n// one unit). `sseHub` (re-exported above) is now expressible as ONE projection\n// via `sseProjection`. See `.patterns/tea-do/projections.md`,\n// `offset-tracking.md`, `delivery-semantics.md`.\nexport {\n driveProjections,\n type Projection,\n type ProjectionErrorContext,\n type ProjectionId,\n type ProjectionOnError,\n type ProjectionRegistry,\n type ProjectionRunner,\n type ProjectionUpdate,\n projectionIdString,\n projectionRegistry,\n rebuildProjection,\n runProjection,\n} from \"./projection\";\n// The HTTP-PULL control carrier (#92) — a hibernation-correct SIBLING to\n// `acceptCommandSocket`. The hands drive a `/step` loop (execute a tool, POST\n// the result, get the next tool); each POST wakes the DO, settles the result\n// idempotently, resumes from the durable checkpoint, returns the next step, and\n// lets the DO hibernate. Carrier-selection rule lives in the `step-host.ts`\n// header: PULL for the cold-between-steps control plane, PUSH (WS/SSE above)\n// for unsolicited view/fan-out. Both commonly run in one DO.\nexport {\n type AlarmStorage,\n constantTimeEqual,\n type DeferResumeHook,\n type DeferredStepOutcome,\n type DeferredStepResponse,\n type DeferStepHostConfig,\n type ExecutedStep,\n type ExecutedSteps,\n type ExecuteStep,\n mintRunToken,\n type NextStep,\n type RunStepLoopConfig,\n runStepLoop,\n type StepCtx,\n type StepEngine,\n type StepHostConfig,\n type StepLoopOutcome,\n type StepOutcome,\n type StepRequest,\n type StepResponse,\n type StepResult,\n type StepTransport,\n type StepWorking,\n stepHost,\n} from \"./step-host\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// doStore — `Store<S>` over DurableObjectStorage.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_STATE_KEY = \"@@state\";\n\n/**\n * Options for {@link doStore}. Mirrors the optional-bag shape\n * `doEventSourcedStore` already uses for its `parse`/`keys`, so the two DO\n * stores read alike.\n */\nexport interface DoStoreOptions<S> {\n /** Storage key the snapshot cell lives at. Defaults to `@@state`. */\n readonly key?: string;\n /**\n * The serialize half of the boundary (#182) — the INVERSE of `doStore`'s\n * `parse`. Maps the live `S` to a JSON-safe carrier before `JSON.stringify`,\n * so a Model holding a `Map`/`Set`/`Date` (which `JSON.stringify` would\n * silently flatten) round-trips: `parse` reconstructs the rich `S` from the\n * carrier on load. Omit for a plain-JSON Model (the identity default — the\n * `Record`-not-`Map` constraint, unchanged). When present, `parse` MUST\n * accept whatever `serialize` produces.\n */\n readonly serialize?: (state: S) => unknown;\n /**\n * Refuse a second live writer (#143). With `{ fenced: true }` the returned\n * store is a `FencedStore<S>`: the version lives in its own storage cell\n * (`<key>@@version`) and the compare-and-swap runs inside\n * `storage.transaction`, so the read, the compare and the two writes are one\n * atomic unit. Inside a single DO the platform already guarantees one writer;\n * fencing is what refuses a SECOND grain — a stale zombie isolate mid-migration,\n * or a second DO id pointed at the same state by a routing bug. A run on a\n * fenced store must be given an `onError` (see `StoreAndOnError`).\n */\n readonly fenced?: true;\n}\n\n/**\n * `Store<S>` over `DurableObjectStorage`.\n *\n * `parse` is REQUIRED because DO storage is a real serialization boundary —\n * the bytes that come back through `JSON.parse` are structurally `unknown`,\n * and the caller (the DO that owns `S`) is the only party with enough\n * information to validate the shape. `parse` returns `S` for a shape it\n * recognizes, `null` when nothing was saved (the substrate boots `init` with\n * `loaded = null`), and `refuse(reason)` for saved bytes it cannot read.\n * `parse` must NOT throw per the `Store<S>.migrate` contract; a shape\n * mismatch is a value (a refusal), not a panic.\n *\n * Structural malformations (the stored cell isn't even JSON) still throw at\n * `load()` — that's an infrastructure error, distinct from a schema\n * mismatch. Either way `runtime.ready` rejects with `StoreRefusedError`\n * before `init` runs, so nothing is saved over the old bytes.\n *\n * Concrete bite this closes: when a `State` variant is renamed between\n * deploys, the DO's new code previously received a value typed as the new\n * `S` that held a runtime string from the old `S` — silent undefined\n * behavior in `init`/`update`. After this, the boundary parse refuses the\n * old shape and the DO fails to start instead of running on it or saving\n * a fresh state over it.\n *\n * Serialization sharp edge (#182): `save` JSON-serializes the Model, and\n * `JSON.stringify` does NOT survive a `Map`/`Set`/`Date` (a `Map` stringifies\n * to `{}`, silently losing every entry). A Model that wants a `Map` (the\n * natural \"roster keyed by id\") therefore supplies a `serialize` hook — the\n * INVERSE of `parse` — so the boundary owns both directions: `serialize` turns\n * the live `S` into a JSON-safe carrier on the way out, `parse` reconstructs\n * the rich `S` from that carrier on the way back in. They are a matched pair:\n * `parse(JSON.parse(JSON.stringify(serialize(state))))` must round-trip to a\n * value equal to `state`. Omit `serialize` and the Model must stay plain-JSON\n * (the `Record`-not-`Map` constraint the default imposes, unchanged).\n *\n * @param parse - boundary parse, the deserialize half (`unknown -> S | null`,\n * or `refuse(reason)` for bytes it cannot read).\n * @param keyOrOptions - either the storage key (legacy positional form,\n * defaults to `@@state`) or a {@link DoStoreOptions} bag carrying `key`\n * and/or the `serialize` hook. A bare string is treated as `{ key }`.\n *\n * Either form is a `DeletableStore<S>`: `delete()` removes the state cell and\n * its `<key>@@version` cell in one `storage.delete` call.\n *\n * Options whose `fenced` is known only at runtime, such as a value typed\n * {@link DoStoreOptions}, return a store that may be fenced, so a run on it must\n * be given an `onError` (see `StoreAndOnError`).\n */\nexport function doStore<S>(\n storage: DurableObjectStorage,\n parse: (raw: unknown) => Migrated<S>,\n keyOrOptions: DoStoreOptions<S> & { readonly fenced: true },\n): FencedStore<S> & DeletableStore<S>;\nexport function doStore<S>(\n storage: DurableObjectStorage,\n parse: (raw: unknown) => Migrated<S>,\n keyOrOptions?: string | (DoStoreOptions<S> & { readonly fenced?: never }),\n): DeletableStore<S>;\nexport function doStore<S>(\n storage: DurableObjectStorage,\n parse: (raw: unknown) => Migrated<S>,\n keyOrOptions?: string | DoStoreOptions<S>,\n): DeletableStore<S> | (FencedStore<S> & DeletableStore<S>);\nexport function doStore<S>(\n storage: DurableObjectStorage,\n parse: (raw: unknown) => Migrated<S>,\n keyOrOptions: string | DoStoreOptions<S> = DEFAULT_STATE_KEY,\n): DeletableStore<S> | (FencedStore<S> & DeletableStore<S>) {\n // Normalize the legacy positional `key` string and the options bag to one\n // shape, so the body reads a single `key` + `serialize` regardless of which\n // call form the caller used. Backward-compatible: a string `keyOrOptions`\n // is exactly the old behavior with the identity serializer.\n const options: DoStoreOptions<S> =\n typeof keyOrOptions === \"string\" ? { key: keyOrOptions } : keyOrOptions;\n const key = options.key ?? DEFAULT_STATE_KEY;\n // Identity by default: the Model is serialized as-is (the plain-JSON path).\n const serialize: (state: S) => unknown =\n options.serialize ?? ((state: S): unknown => state);\n const versionKey = `${key}@@version`;\n const base: DeletableStore<S> = {\n async load(): Promise<unknown> {\n const raw = await storage.get<string>(key);\n if (raw === undefined || raw === null) return null;\n // JSON.parse throws on malformed — propagate per PRD throw-semantics.\n // The decoded value is intentionally returned as `unknown`; the\n // substrate's `migrate` callback (forwarded from `parse`) is the\n // boundary parse that turns it into `S`, `null` or a refusal.\n return JSON.parse(raw);\n },\n async save(state: S): Promise<void> {\n // `serialize` is the inverse of `parse` — it maps the live `S` (which may\n // hold a Map/Set) to a JSON-safe carrier before `JSON.stringify` (#182).\n await storage.put(key, JSON.stringify(serialize(state)));\n },\n migrate(raw: unknown): Migrated<S> {\n return parse(raw);\n },\n async delete(): Promise<void> {\n // Both cells, fenced or not: a key written fenced can be opened unfenced,\n // and a version left behind would outlive its state. One multi-key\n // `delete` commits atomically, so no reader sees one cell without the\n // other.\n await storage.delete([key, versionKey]);\n },\n };\n if (options.fenced !== true) return base;\n\n return {\n ...base,\n fenced: true,\n async loadFenced(): Promise<{ raw: unknown; version: number }> {\n const stored = await storage.get<number>(versionKey);\n return { raw: await base.load(), version: stored ?? 0 };\n },\n async saveFenced(state: S, expectedVersion: number): Promise<number> {\n // `storage.transaction` is the DO's own compare-and-swap primitive: the\n // read, the compare and both writes commit as one unit, and a throw from\n // the closure rolls the whole thing back — so a refused save leaves the\n // state cell exactly as the winner wrote it.\n return await storage.transaction(async (txn) => {\n const actual = (await txn.get<number>(versionKey)) ?? 0;\n if (actual !== expectedVersion) {\n throw new StoreConflictError(expectedVersion, actual);\n }\n const next = expectedVersion + 1;\n await txn.put(key, JSON.stringify(serialize(state)));\n await txn.put(versionKey, next);\n return next;\n });\n },\n };\n}\n"]}