experimental-a2 0.16.1 → 0.18.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 (406) hide show
  1. package/CHANGELOG.md +373 -0
  2. package/README.md +1 -1
  3. package/dist/{actor-ohPC-81x.d.ts → actor-Bjce9_5U.d.ts} +21 -11
  4. package/dist/actor-Bjce9_5U.d.ts.map +1 -0
  5. package/dist/actor-client.d.ts +2 -5
  6. package/dist/actor-client.d.ts.map +1 -1
  7. package/dist/actor-client.js +13 -4
  8. package/dist/actor-client.js.map +1 -1
  9. package/dist/actor-react.d.ts +5 -3
  10. package/dist/actor-react.d.ts.map +1 -1
  11. package/dist/actor-react.js +20 -10
  12. package/dist/actor-react.js.map +1 -1
  13. package/dist/{actor-shared-B5tJfzt-.js → actor-shared-t7w8xIKP.js} +12 -7
  14. package/dist/actor-shared-t7w8xIKP.js.map +1 -0
  15. package/dist/actor.d.ts +1 -1
  16. package/dist/actor.js +42 -23
  17. package/dist/actor.js.map +1 -1
  18. package/dist/{ai-DgOBltJ_.js → ai-BpOGm-zB.js} +621 -216
  19. package/dist/ai-BpOGm-zB.js.map +1 -0
  20. package/dist/{ai-CrEf6p_W.d.ts → ai-DrUaMyRt.d.ts} +114 -100
  21. package/dist/ai-DrUaMyRt.d.ts.map +1 -0
  22. package/dist/ai-client.d.ts +27 -0
  23. package/dist/ai-client.d.ts.map +1 -0
  24. package/dist/ai-client.js +29 -0
  25. package/dist/ai-client.js.map +1 -0
  26. package/dist/ai-fx.browser.d.ts +1 -0
  27. package/dist/ai-fx.browser.js +6 -0
  28. package/dist/ai-fx.browser.js.map +1 -0
  29. package/dist/ai-fx.d.ts +10 -0
  30. package/dist/ai-fx.d.ts.map +1 -0
  31. package/dist/ai-fx.js +634 -0
  32. package/dist/ai-fx.js.map +1 -0
  33. package/dist/ai-harness-BuLh_EYi.js +273 -0
  34. package/dist/ai-harness-BuLh_EYi.js.map +1 -0
  35. package/dist/ai-harness-DvsPeJeB.d.ts +26 -0
  36. package/dist/ai-harness-DvsPeJeB.d.ts.map +1 -0
  37. package/dist/ai-pi.browser.d.ts +1 -0
  38. package/dist/ai-pi.browser.js +6 -0
  39. package/dist/ai-pi.browser.js.map +1 -0
  40. package/dist/ai-pi.d.ts +17 -0
  41. package/dist/ai-pi.d.ts.map +1 -0
  42. package/dist/ai-pi.js +904 -0
  43. package/dist/ai-pi.js.map +1 -0
  44. package/dist/ai-react.d.ts +96 -0
  45. package/dist/ai-react.d.ts.map +1 -0
  46. package/dist/ai-react.js +142 -0
  47. package/dist/ai-react.js.map +1 -0
  48. package/dist/ai-server.browser.js +2 -2
  49. package/dist/ai-server.browser.js.map +1 -1
  50. package/dist/ai-server.d.ts +109 -43
  51. package/dist/ai-server.d.ts.map +1 -1
  52. package/dist/ai-server.js +526 -283
  53. package/dist/ai-server.js.map +1 -1
  54. package/dist/ai.d.ts +2 -2
  55. package/dist/ai.js +2 -2
  56. package/dist/bounded-cache-CPHWF-8h.js +41 -0
  57. package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
  58. package/dist/{client-BYzHjkwU.js → client-BReKqwZC.js} +447 -80
  59. package/dist/client-BReKqwZC.js.map +1 -0
  60. package/dist/{client-CzyacQpJ.d.ts → client-h_DKFAjj.d.ts} +14 -13
  61. package/dist/client-h_DKFAjj.d.ts.map +1 -0
  62. package/dist/client.d.ts +4 -2
  63. package/dist/client.js +1 -1
  64. package/dist/{contract-CKRg_E4q.js → contract-BYKASlCV.js} +12 -21
  65. package/dist/contract-BYKASlCV.js.map +1 -0
  66. package/dist/{idempotent-replay-DVOlyYbx.js → idempotent-replay-JdgMQRRm.js} +2 -2
  67. package/dist/{idempotent-replay-DVOlyYbx.js.map → idempotent-replay-JdgMQRRm.js.map} +1 -1
  68. package/dist/index.d.ts +6 -5
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +4 -3
  71. package/dist/{errors-DvhSXnxk.d.ts → initial-snapshot-Bx-c0pDP.d.ts} +7 -3
  72. package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +1 -0
  73. package/dist/{internal-Dq2qYxou.js → internal-B2T3U4Rq.js} +29 -2
  74. package/dist/internal-B2T3U4Rq.js.map +1 -0
  75. package/dist/otel.d.ts +1 -1
  76. package/dist/react-BBXgHWFc.d.ts +86 -0
  77. package/dist/react-BBXgHWFc.d.ts.map +1 -0
  78. package/dist/react.d.ts +2 -122
  79. package/dist/react.js +107 -100
  80. package/dist/react.js.map +1 -1
  81. package/dist/reducer-BAXc8JyF.js +87 -0
  82. package/dist/reducer-BAXc8JyF.js.map +1 -0
  83. package/dist/{reducer-otzHjuJj.d.ts → reducer-CqA59Q6U.d.ts} +46 -30
  84. package/dist/reducer-CqA59Q6U.d.ts.map +1 -0
  85. package/dist/reducer-runtime-DQwz_J59.js +28 -0
  86. package/dist/reducer-runtime-DQwz_J59.js.map +1 -0
  87. package/dist/scheduler-qstash.d.ts +3 -3
  88. package/dist/scheduler-qstash.d.ts.map +1 -1
  89. package/dist/scheduler-qstash.js +33 -10
  90. package/dist/scheduler-qstash.js.map +1 -1
  91. package/dist/{scheduler-task-BpzhPnRS.js → scheduler-task-X1SToDjP.js} +18 -3
  92. package/dist/scheduler-task-X1SToDjP.js.map +1 -0
  93. package/dist/scheduler-vercel.d.ts +4 -9
  94. package/dist/scheduler-vercel.d.ts.map +1 -1
  95. package/dist/scheduler-vercel.js +25 -44
  96. package/dist/scheduler-vercel.js.map +1 -1
  97. package/dist/{server-BD5ckxZb.js → server-Dw_k_7kO.js} +904 -219
  98. package/dist/server-Dw_k_7kO.js.map +1 -0
  99. package/dist/{server-DbD7IJVK.d.ts → server-En7cbi3v.d.ts} +116 -26
  100. package/dist/server-En7cbi3v.d.ts.map +1 -0
  101. package/dist/server.d.ts +4 -4
  102. package/dist/server.js +2 -2
  103. package/dist/session-clear-Bo-XTFEy.d.ts +12 -0
  104. package/dist/session-clear-Bo-XTFEy.d.ts.map +1 -0
  105. package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
  106. package/dist/session-clear-DGI4jTQ5.js.map +1 -0
  107. package/dist/{store-DyZM6fS5.d.ts → store-Djdv0bnZ.d.ts} +73 -3
  108. package/dist/store-Djdv0bnZ.d.ts.map +1 -0
  109. package/dist/store-N8PXxDAS.js.map +1 -1
  110. package/dist/store-memory.d.ts +1 -1
  111. package/dist/store-memory.d.ts.map +1 -1
  112. package/dist/store-memory.js +205 -66
  113. package/dist/store-memory.js.map +1 -1
  114. package/dist/{store-polling-CmxUbV93.js → store-polling-B5FcwJfo.js} +21 -4
  115. package/dist/store-polling-B5FcwJfo.js.map +1 -0
  116. package/dist/store-postgres.d.ts +1 -1
  117. package/dist/store-postgres.d.ts.map +1 -1
  118. package/dist/store-postgres.js +450 -108
  119. package/dist/store-postgres.js.map +1 -1
  120. package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-BAwS8RWn.js} +3 -3
  121. package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-BAwS8RWn.js.map} +1 -1
  122. package/dist/store-redis-http.d.ts +1 -1
  123. package/dist/store-redis-http.d.ts.map +1 -1
  124. package/dist/store-redis-http.js +293 -11
  125. package/dist/store-redis-http.js.map +1 -1
  126. package/dist/store-redis-notify-BRITti_o.js +2493 -0
  127. package/dist/store-redis-notify-BRITti_o.js.map +1 -0
  128. package/dist/store-redis.d.ts +1 -1
  129. package/dist/store-redis.d.ts.map +1 -1
  130. package/dist/store-redis.js +11 -11
  131. package/dist/store-redis.js.map +1 -1
  132. package/dist/store-sqlite.d.ts +1 -1
  133. package/dist/store-sqlite.d.ts.map +1 -1
  134. package/dist/store-sqlite.js +197 -33
  135. package/dist/store-sqlite.js.map +1 -1
  136. package/dist/stream-buffer-Cx7AAPGq.js +23 -0
  137. package/dist/stream-buffer-Cx7AAPGq.js.map +1 -0
  138. package/dist/{telemetry-B5jzpy6y.d.ts → telemetry-Cyfqo6Qt.d.ts} +2 -2
  139. package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
  140. package/dist/{wire-BO5wWCb1.js → wire-rSUglYo8.js} +132 -10
  141. package/dist/wire-rSUglYo8.js.map +1 -0
  142. package/docs/01-quickstart.mdx +50 -84
  143. package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
  144. package/docs/actors/01-introduction.mdx +48 -82
  145. package/docs/actors/02-concurrency.mdx +43 -70
  146. package/docs/actors/03-timers.mdx +28 -42
  147. package/docs/actors/04-routes.mdx +110 -137
  148. package/docs/actors/meta.ts +1 -1
  149. package/docs/agents/01-introduction.mdx +257 -0
  150. package/docs/agents/02-tools.mdx +247 -0
  151. package/docs/agents/03-conversations.mdx +538 -0
  152. package/docs/agents/04-harnesses.mdx +136 -0
  153. package/docs/agents/meta.ts +1 -0
  154. package/docs/client/01-react.mdx +294 -0
  155. package/docs/client/02-transports.mdx +86 -0
  156. package/docs/client/03-presence.mdx +224 -0
  157. package/docs/client/meta.ts +1 -0
  158. package/docs/contract/01-events.mdx +57 -0
  159. package/docs/contract/02-state.mdx +142 -0
  160. package/docs/contract/03-sessions.mdx +175 -0
  161. package/docs/index.mdx +112 -156
  162. package/docs/reference/01-core.mdx +139 -0
  163. package/docs/reference/02-server.mdx +855 -0
  164. package/docs/reference/03-client.mdx +301 -0
  165. package/docs/reference/04-ai.mdx +722 -0
  166. package/docs/reference/05-adapters.mdx +351 -0
  167. package/docs/reference/06-errors.mdx +138 -0
  168. package/docs/reference/meta.ts +1 -1
  169. package/docs/server/01-handlers.mdx +200 -0
  170. package/docs/server/02-timers.mdx +130 -0
  171. package/docs/server/03-cancellation.mdx +141 -0
  172. package/docs/server/04-routes.mdx +243 -0
  173. package/docs/server/05-production.mdx +322 -0
  174. package/docs/server/06-guarantees.mdx +123 -0
  175. package/examples/playground/DEPLOY.md +53 -7
  176. package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
  177. package/examples/playground/app/a0/[appId]/page.tsx +53 -0
  178. package/examples/playground/app/a0/a0.css +818 -0
  179. package/examples/playground/app/a0/app-list.tsx +218 -0
  180. package/examples/playground/app/a0/app-view.tsx +63 -0
  181. package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
  182. package/examples/playground/app/a0/apps/[appId]/route.ts +28 -0
  183. package/examples/playground/app/a0/apps.test.ts +128 -0
  184. package/examples/playground/app/a0/apps.ts +102 -0
  185. package/examples/playground/app/a0/chat.tsx +410 -0
  186. package/examples/playground/app/a0/columns.tsx +109 -0
  187. package/examples/playground/app/a0/context-menu.tsx +116 -0
  188. package/examples/playground/app/a0/cookies.ts +46 -0
  189. package/examples/playground/app/a0/crash-on-request.ts +72 -0
  190. package/examples/playground/app/a0/crashes/route.ts +3 -0
  191. package/examples/playground/app/a0/crashes.test.ts +33 -0
  192. package/examples/playground/app/a0/crashes.ts +28 -0
  193. package/examples/playground/app/a0/devtools.tsx +164 -0
  194. package/examples/playground/app/a0/events/route.ts +6 -0
  195. package/examples/playground/app/a0/flags.ts +30 -0
  196. package/examples/playground/app/a0/fx/events/route.ts +9 -0
  197. package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
  198. package/examples/playground/app/a0/fx/short/events/route.ts +11 -0
  199. package/examples/playground/app/a0/layout.tsx +38 -0
  200. package/examples/playground/app/a0/list/route.ts +10 -0
  201. package/examples/playground/app/a0/model.test.ts +73 -0
  202. package/examples/playground/app/a0/model.ts +95 -0
  203. package/examples/playground/app/a0/no-photon.cjs +4 -0
  204. package/examples/playground/app/a0/no-quickjs.cjs +3 -0
  205. package/examples/playground/app/a0/page.tsx +14 -0
  206. package/examples/playground/app/a0/pi/events/route.ts +9 -0
  207. package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
  208. package/examples/playground/app/a0/pi/short/events/route.ts +11 -0
  209. package/examples/playground/app/a0/preview-pane.tsx +145 -0
  210. package/examples/playground/app/a0/preview.test.ts +53 -0
  211. package/examples/playground/app/a0/preview.ts +26 -0
  212. package/examples/playground/app/a0/sandbox-log.ts +75 -0
  213. package/examples/playground/app/a0/sandbox-session.ts +152 -0
  214. package/examples/playground/app/a0/sandbox.ts +191 -0
  215. package/examples/playground/app/a0/sandboxes/scheduler/route.ts +6 -0
  216. package/examples/playground/app/a0/scheduler/route.ts +7 -0
  217. package/examples/playground/app/a0/server.ts +213 -0
  218. package/examples/playground/app/a0/session.ts +59 -0
  219. package/examples/playground/app/a0/shell.tsx +158 -0
  220. package/examples/playground/app/a0/short/events/route.ts +9 -0
  221. package/examples/playground/app/a0/splitter.tsx +120 -0
  222. package/examples/playground/app/agent/[agentId]/agent-client.tsx +144 -174
  223. package/examples/playground/app/agent/[agentId]/command-receipt.test.tsx +65 -0
  224. package/examples/playground/app/agent/[agentId]/command-receipt.tsx +27 -0
  225. package/examples/playground/app/agent/[agentId]/compaction-event.tsx +25 -24
  226. package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +119 -0
  227. package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +208 -165
  228. package/examples/playground/app/agent/[agentId]/page.tsx +2 -16
  229. package/examples/playground/app/agent/events/route.ts +5 -2
  230. package/examples/playground/app/agent/ingress.test.ts +101 -0
  231. package/examples/playground/app/agent/model-input.test.tsx +58 -0
  232. package/examples/playground/app/agent/model-input.tsx +115 -0
  233. package/examples/playground/app/agent/model.test.ts +73 -0
  234. package/examples/playground/app/agent/model.ts +58 -26
  235. package/examples/playground/app/agent/new-agent-session.tsx +37 -19
  236. package/examples/playground/app/agent/page.tsx +4 -4
  237. package/examples/playground/app/agent/server.ts +19 -15
  238. package/examples/playground/app/agent/session.ts +5 -6
  239. package/examples/playground/app/agent/telemetry.test.ts +189 -0
  240. package/examples/playground/app/agent/telemetry.ts +30 -0
  241. package/examples/playground/app/agent/trace.test.ts +23 -0
  242. package/examples/playground/app/agent/trace.ts +18 -0
  243. package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +12 -12
  244. package/examples/playground/app/canvas/[canvasId]/page.tsx +1 -2
  245. package/examples/playground/app/canvas/[canvasId]/session.ts +2 -2
  246. package/examples/playground/app/canvas/model.ts +53 -53
  247. package/examples/playground/app/chat/[chatId]/agent-state.test.ts +176 -0
  248. package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +16 -1
  249. package/examples/playground/app/chat/[chatId]/chat-client.tsx +55 -77
  250. package/examples/playground/app/chat/[chatId]/chat-view.ts +37 -3
  251. package/examples/playground/app/chat/[chatId]/page.tsx +7 -15
  252. package/examples/playground/app/chat/[chatId]/session.ts +2 -19
  253. package/examples/playground/app/chat/agent/route.ts +9 -0
  254. package/examples/playground/app/chat/model.test.ts +46 -119
  255. package/examples/playground/app/chat/model.ts +82 -128
  256. package/examples/playground/app/chat/scheduler/route.ts +2 -2
  257. package/examples/playground/app/chat/server.test.ts +112 -0
  258. package/examples/playground/app/chat/server.ts +113 -117
  259. package/examples/playground/app/components/activity-feed.tsx +30 -1
  260. package/examples/playground/app/components/activity-history.test.tsx +35 -0
  261. package/examples/playground/app/components/demo-icons.tsx +89 -0
  262. package/examples/playground/app/components/demos.tsx +33 -0
  263. package/examples/playground/app/components/fast-link.test.tsx +58 -0
  264. package/examples/playground/app/components/fast-link.tsx +55 -0
  265. package/examples/playground/app/components/site-header.tsx +96 -0
  266. package/examples/playground/app/counter/counter-client.tsx +3 -3
  267. package/examples/playground/app/counter/model.test.ts +3 -3
  268. package/examples/playground/app/counter/model.ts +11 -21
  269. package/examples/playground/app/counter/page.tsx +2 -15
  270. package/examples/playground/app/counter/session.ts +2 -2
  271. package/examples/playground/app/documents/[documentId]/code-editor.tsx +12 -1
  272. package/examples/playground/app/documents/[documentId]/document-client.tsx +10 -11
  273. package/examples/playground/app/documents/[documentId]/page.tsx +1 -2
  274. package/examples/playground/app/documents/[documentId]/session.ts +2 -2
  275. package/examples/playground/app/documents/model.ts +27 -27
  276. package/examples/playground/app/globals.css +420 -207
  277. package/examples/playground/app/initial-state.test.tsx +54 -0
  278. package/examples/playground/app/layout.tsx +7 -23
  279. package/examples/playground/app/orders/[orderId]/page.tsx +1 -2
  280. package/examples/playground/app/orders/[orderId]/session.ts +2 -2
  281. package/examples/playground/app/orders/model.ts +49 -49
  282. package/examples/playground/app/page.tsx +13 -102
  283. package/examples/playground/app/recovery/[recoveryId]/page.tsx +1 -2
  284. package/examples/playground/app/recovery/[recoveryId]/session.ts +2 -2
  285. package/examples/playground/app/recovery/model.ts +30 -30
  286. package/examples/playground/instrumentation.ts +5 -0
  287. package/examples/playground/next.config.ts +17 -5
  288. package/examples/playground/package.json +13 -2
  289. package/examples/playground/scripts/a0-template.ts +77 -0
  290. package/examples/playground/vercel.json +36 -0
  291. package/package.json +29 -3
  292. package/src/actor-client.ts +16 -4
  293. package/src/actor-react.ts +25 -11
  294. package/src/actor-shared.ts +21 -6
  295. package/src/actor.ts +76 -31
  296. package/src/ai-agent-actions.ts +219 -0
  297. package/src/ai-agent.ts +2517 -0
  298. package/src/ai-app-state.ts +91 -0
  299. package/src/ai-client-state.ts +6 -3
  300. package/src/ai-client.ts +62 -0
  301. package/src/ai-context-schema.ts +25 -5
  302. package/src/ai-context.ts +90 -40
  303. package/src/ai-control-server.ts +92 -72
  304. package/src/ai-coordinator.ts +23 -22
  305. package/src/ai-fx.browser.ts +5 -0
  306. package/src/ai-fx.ts +833 -0
  307. package/src/ai-harness.ts +518 -0
  308. package/src/ai-message-base.ts +209 -0
  309. package/src/ai-message-projection.ts +262 -64
  310. package/src/ai-model-metadata.ts +11 -6
  311. package/src/ai-pi.browser.ts +5 -0
  312. package/src/ai-pi.ts +1263 -0
  313. package/src/ai-progress-batches.ts +26 -0
  314. package/src/ai-projector.ts +95 -14
  315. package/src/ai-react.ts +146 -0
  316. package/src/ai-sdk-step.ts +2 -2
  317. package/src/ai-server.browser.ts +2 -2
  318. package/src/ai-server.ts +23 -1965
  319. package/src/ai-stored-state.ts +48 -163
  320. package/src/ai.ts +339 -155
  321. package/src/bootstrap-stream.ts +72 -0
  322. package/src/bounded-cache.ts +55 -0
  323. package/src/client-render-session.ts +49 -0
  324. package/src/client.ts +688 -151
  325. package/src/contract.ts +92 -42
  326. package/src/deterministic-id.ts +37 -0
  327. package/src/equal-json.ts +32 -0
  328. package/src/errors.ts +13 -1
  329. package/src/index.ts +5 -1
  330. package/src/initial-snapshot.ts +39 -0
  331. package/src/internal.ts +52 -0
  332. package/src/libfx.d.ts +34 -0
  333. package/src/postgres-notification-scope.ts +14 -3
  334. package/src/react.ts +171 -206
  335. package/src/redis-http-reply.ts +256 -0
  336. package/src/redis-http-stream.ts +73 -0
  337. package/src/redis-http-subscriptions.ts +13 -5
  338. package/src/reducer-runtime.ts +52 -0
  339. package/src/reducer.ts +93 -11
  340. package/src/scheduler-qstash.ts +49 -20
  341. package/src/scheduler-task.ts +34 -2
  342. package/src/scheduler-vercel.ts +32 -84
  343. package/src/server-fetch.ts +139 -35
  344. package/src/server.ts +1133 -255
  345. package/src/session-clear.ts +23 -0
  346. package/src/session-socket.ts +154 -49
  347. package/src/sse.ts +71 -4
  348. package/src/store-memory.ts +252 -75
  349. package/src/store-polling.ts +18 -4
  350. package/src/store-postgres.ts +541 -114
  351. package/src/store-redis-core.ts +1450 -493
  352. package/src/store-redis-http.ts +18 -2
  353. package/src/store-redis-notify.ts +262 -85
  354. package/src/store-redis.ts +19 -9
  355. package/src/store-sqlite.ts +319 -45
  356. package/src/store.ts +79 -1
  357. package/src/stream-buffer.ts +43 -0
  358. package/src/telemetry.ts +14 -1
  359. package/src/wire.ts +241 -13
  360. package/dist/actor-ohPC-81x.d.ts.map +0 -1
  361. package/dist/actor-shared-B5tJfzt-.js.map +0 -1
  362. package/dist/ai-CrEf6p_W.d.ts.map +0 -1
  363. package/dist/ai-DgOBltJ_.js.map +0 -1
  364. package/dist/client-BYzHjkwU.js.map +0 -1
  365. package/dist/client-CzyacQpJ.d.ts.map +0 -1
  366. package/dist/contract-CKRg_E4q.js.map +0 -1
  367. package/dist/errors-DCk6ch5n.js.map +0 -1
  368. package/dist/errors-DvhSXnxk.d.ts.map +0 -1
  369. package/dist/internal-Dq2qYxou.js.map +0 -1
  370. package/dist/react.d.ts.map +0 -1
  371. package/dist/reducer-DEMjEY_O.js +0 -29
  372. package/dist/reducer-DEMjEY_O.js.map +0 -1
  373. package/dist/reducer-otzHjuJj.d.ts.map +0 -1
  374. package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
  375. package/dist/server-BD5ckxZb.js.map +0 -1
  376. package/dist/server-DbD7IJVK.d.ts.map +0 -1
  377. package/dist/store-DyZM6fS5.d.ts.map +0 -1
  378. package/dist/store-polling-CmxUbV93.js.map +0 -1
  379. package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
  380. package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
  381. package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
  382. package/dist/validate-XKT4FSNn.js +0 -30
  383. package/dist/validate-XKT4FSNn.js.map +0 -1
  384. package/dist/wire-BO5wWCb1.js.map +0 -1
  385. package/docs/concepts/01-contracts.mdx +0 -142
  386. package/docs/concepts/02-handlers.mdx +0 -284
  387. package/docs/concepts/03-durability.mdx +0 -314
  388. package/docs/concepts/04-state.mdx +0 -209
  389. package/docs/guides/01-timers.mdx +0 -185
  390. package/docs/guides/02-cancellation.mdx +0 -161
  391. package/docs/guides/03-react.mdx +0 -384
  392. package/docs/guides/04-local-first.mdx +0 -88
  393. package/docs/guides/05-production.mdx +0 -521
  394. package/docs/guides/06-ai-agents.mdx +0 -1318
  395. package/docs/guides/08-application-data.mdx +0 -113
  396. package/docs/guides/09-presence.mdx +0 -266
  397. package/docs/guides/10-transports.mdx +0 -221
  398. package/docs/reference/01-api.mdx +0 -1896
  399. package/docs/reference/02-errors.mdx +0 -149
  400. package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
  401. package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
  402. package/examples/playground/app/agent/compaction-settings.ts +0 -49
  403. package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
  404. package/examples/playground/app/agent/compaction-timeline.ts +0 -208
  405. /package/docs/{concepts → contract}/meta.ts +0 -0
  406. /package/docs/{guides → server}/meta.ts +0 -0
@@ -1,8 +1,8 @@
1
- import { n as validateSync } from "./validate-XKT4FSNn.js";
2
- import { t as clientStateProjection } from "./reducer-DEMjEY_O.js";
3
- import { t as A2Error } from "./errors-DCk6ch5n.js";
4
- import { a as PRESENCE_TIMINGS, c as STREAM_TIMINGS, h as nullProtoRecord, o as RESERVED_PARTICIPANT_IDS } from "./internal-Dq2qYxou.js";
5
- import { c as isWireEvent, d as parseSocketFrame, f as presencePatchFromWire, l as isWirePresencePatch, m as presenceSnapshotFromWire, o as eventFromWire, r as errorFromWire, u as isWirePresenceSnapshot } from "./wire-BO5wWCb1.js";
1
+ import { i as A2Error, n as isSessionCleared } from "./session-clear-DGI4jTQ5.js";
2
+ import { n as foldEvent, s as validateSync } from "./reducer-BAXc8JyF.js";
3
+ import { r as projectClientState, t as decodeState } from "./reducer-runtime-DQwz_J59.js";
4
+ import { _ as nullProtoRecord, a as PRESENCE_TIMINGS, l as STREAM_TIMINGS, o as RESERVED_PARTICIPANT_IDS } from "./internal-B2T3U4Rq.js";
5
+ import { _ as isWirePresenceSnapshot, a as errorFromWire, c as eventFromCompactWire, f as isCompactWireEvent, g as isWirePresencePatch, h as isWireEvent, l as eventFromWire, p as isStateSnapshotFrame, v as parseSocketFrame, x as presenceSnapshotFromWire, y as presencePatchFromWire } from "./wire-rSUglYo8.js";
6
6
  //#region src/client-errors.ts
7
7
  var A2ClientError = class extends A2Error {
8
8
  status;
@@ -29,6 +29,37 @@ function asClientError(error, options) {
29
29
  return new A2ClientError(options?.code ?? "UNKNOWN", error instanceof Error ? error.message : "client operation failed", { cause: error });
30
30
  }
31
31
  //#endregion
32
+ //#region src/initial-snapshot.ts
33
+ function isInitialPromise(initial) {
34
+ return initial !== null && typeof initial === "object" && "then" in initial && typeof initial.then === "function";
35
+ }
36
+ function observeInitial(options) {
37
+ let listener = options.settled;
38
+ const deliver = (result) => {
39
+ const current = listener;
40
+ listener = void 0;
41
+ current?.(result);
42
+ };
43
+ Promise.resolve(options.promise).then((value) => deliver({ value }), (error) => deliver({ error }));
44
+ return () => {
45
+ listener = void 0;
46
+ };
47
+ }
48
+ //#endregion
49
+ //#region src/client-render-session.ts
50
+ const factories = /* @__PURE__ */ new WeakMap();
51
+ function registerRenderSessionFactory(options) {
52
+ factories.set(options.client, options);
53
+ }
54
+ function resolveInitialForReact(options) {
55
+ factories.get(options.client)?.resolveInitial(options);
56
+ }
57
+ function createRenderSession(options) {
58
+ const factory = factories.get(options.client);
59
+ if (!factory) throw new TypeError("React requires a client returned by createClient");
60
+ return factory.factory(options.sessionId, options.settings);
61
+ }
62
+ //#endregion
32
63
  //#region src/client.ts
33
64
  /**
34
65
  * experimental-a2/client — the framework-agnostic session client.
@@ -44,6 +75,13 @@ function asClientError(error, options) {
44
75
  * The client is a replica, never an access path — the routes it talks
45
76
  * to authorize every read and write.
46
77
  */
78
+ function assertSessionSeed(options) {
79
+ const initial = options?.initial;
80
+ if (initial == null || isInitialPromise(initial)) return;
81
+ if (initial === null || typeof initial !== "object" || !Object.hasOwn(initial, "state") || !Object.hasOwn(initial, "index") || !("index" in initial) || typeof initial.index !== "number" || !Number.isSafeInteger(initial.index) || initial.index < 0) throw new TypeError("initial must contain state and a non-negative safe integer index");
82
+ const clearIndex = initial.clearIndex;
83
+ if (clearIndex !== void 0 && (typeof clearIndex !== "number" || !Number.isSafeInteger(clearIndex) || clearIndex < 0 || clearIndex > initial.index)) throw new TypeError("initial.clearIndex must be a non-negative safe integer no greater than its index");
84
+ }
47
85
  const PUSH_ATTEMPTS = 3;
48
86
  const RETRYABLE_PUSH_CODES = /* @__PURE__ */ new Set([
49
87
  "STORE_UNAVAILABLE",
@@ -68,9 +106,14 @@ const httpError = async (response) => {
68
106
  });
69
107
  };
70
108
  const httpTransport = (routes, fetchImpl, presence) => ({
71
- async *connect({ sessionId, startAfter, signal }) {
109
+ async *connect({ sessionId, startAfter, reducer: reducerName, signal }) {
72
110
  const streamUrl = sessionUrl(routes.stream, sessionId);
73
- const res = await fetchImpl(`${streamUrl}${streamUrl.includes("?") ? "&" : "?"}sessionId=${encodeURIComponent(sessionId)}&index=${startAfter}`, {
111
+ const res = await fetchImpl(`${streamUrl}${streamUrl.includes("?") ? "&" : "?"}${new URLSearchParams({
112
+ sessionId,
113
+ reducer: reducerName,
114
+ wire: "2",
115
+ ...startAfter === void 0 ? { bootstrap: "1" } : { index: String(startAfter) }
116
+ })}`, {
74
117
  headers: { accept: "text/event-stream" },
75
118
  signal
76
119
  });
@@ -99,10 +142,20 @@ const httpTransport = (routes, fetchImpl, presence) => ({
99
142
  if (!data) continue;
100
143
  if (eventName === void 0) {
101
144
  const parsed = JSON.parse(data);
102
- if (isWireEvent(parsed)) yield {
145
+ if (Array.isArray(parsed)) {
146
+ if (!parsed.every(isCompactWireEvent)) throw new A2ClientError("INVALID_PAYLOAD", "invalid event batch");
147
+ yield {
148
+ kind: "events",
149
+ events: parsed.map((row) => eventFromCompactWire(row, sessionId))
150
+ };
151
+ } else if (isWireEvent(parsed)) yield {
103
152
  kind: "event",
104
153
  event: parsed
105
154
  };
155
+ } else if (eventName === "a2-snapshot") {
156
+ const parsed = JSON.parse(data);
157
+ if (!isStateSnapshotFrame(parsed)) throw new A2ClientError("INVALID_PAYLOAD", "invalid bootstrap snapshot");
158
+ yield parsed;
106
159
  } else if (presence && eventName === "presence-snapshot") {
107
160
  const parsed = JSON.parse(data);
108
161
  if (isWirePresenceSnapshot(parsed)) yield {
@@ -144,7 +197,11 @@ const httpTransport = (routes, fetchImpl, presence) => ({
144
197
  if (!res.ok) throw await httpError(res);
145
198
  const rows = await res.json();
146
199
  if (!Array.isArray(rows) || !rows.every(isWireEvent)) throw new A2ClientError("STORE_UNAVAILABLE", "history response was not a list of events");
147
- return rows;
200
+ const clearIndex = Number(res.headers.get("a2-clear-index") ?? NaN);
201
+ return Number.isSafeInteger(clearIndex) && clearIndex > 0 ? {
202
+ events: rows,
203
+ clearIndex
204
+ } : { events: rows };
148
205
  },
149
206
  sendPresence(envelope) {
150
207
  (async () => {
@@ -214,16 +271,21 @@ const sessionWsTransport = (url, webSocketImpl, presence) => {
214
271
  const connections = /* @__PURE__ */ new Map();
215
272
  const ready = socketReadyWaiters();
216
273
  let nextReq = 1;
217
- const socketUrl = (sessionId, startAfter) => {
274
+ const socketUrl = (sessionId, startAfter, reducerName) => {
218
275
  const resolved = webSocketUrl(sessionUrl(url, sessionId));
219
276
  resolved.searchParams.set("sessionId", sessionId);
220
- resolved.searchParams.set("index", String(startAfter));
277
+ resolved.searchParams.delete("index");
278
+ resolved.searchParams.delete("bootstrap");
279
+ resolved.searchParams.set("reducer", reducerName);
280
+ resolved.searchParams.set("wire", "2");
281
+ if (startAfter === void 0) resolved.searchParams.set("bootstrap", "1");
282
+ else resolved.searchParams.set("index", String(startAfter));
221
283
  return resolved.href;
222
284
  };
223
285
  return {
224
- async *connect({ sessionId, startAfter, signal }) {
286
+ async *connect({ sessionId, startAfter, reducer: reducerName, signal }) {
225
287
  if (webSocketImpl === void 0) throw new TypeError("no WebSocket implementation available — pass one via createClient({ webSocket }) where the global is missing");
226
- const socket = new webSocketImpl(socketUrl(sessionId, startAfter));
288
+ const socket = new webSocketImpl(socketUrl(sessionId, startAfter, reducerName));
227
289
  const connection = {
228
290
  socket,
229
291
  pending: /* @__PURE__ */ new Map()
@@ -241,7 +303,10 @@ const sessionWsTransport = (url, webSocketImpl, presence) => {
241
303
  if (ended) return;
242
304
  ended = true;
243
305
  failure = error;
244
- if (connections.get(sessionId) === connection) connections.delete(sessionId);
306
+ if (connections.get(sessionId) === connection) {
307
+ connections.delete(sessionId);
308
+ ready.notify(sessionId);
309
+ }
245
310
  rejectPendingAcks(connection.pending);
246
311
  notify();
247
312
  };
@@ -254,9 +319,18 @@ const sessionWsTransport = (url, webSocketImpl, presence) => {
254
319
  socket.addEventListener("message", (event) => {
255
320
  if (connections.get(sessionId) !== connection || ended) return;
256
321
  if (typeof event.data !== "string") return;
257
- const frame = parseSocketFrame(event.data);
322
+ let frame;
323
+ try {
324
+ frame = parseSocketFrame(event.data);
325
+ } catch (error) {
326
+ finish(asClientError(error));
327
+ return;
328
+ }
258
329
  if (frame === null) return;
259
330
  switch (frame.kind) {
331
+ case "snapshot":
332
+ queue.push(frame);
333
+ break;
260
334
  case "ack": {
261
335
  const waiter = connection.pending.get(frame.req);
262
336
  if (waiter === void 0) return;
@@ -276,6 +350,13 @@ const sessionWsTransport = (url, webSocketImpl, presence) => {
276
350
  event: frame.event
277
351
  });
278
352
  break;
353
+ case "events":
354
+ if (frame.sessionId !== void 0) return;
355
+ queue.push({
356
+ kind: "events",
357
+ events: frame.events.map((row) => eventFromCompactWire(row, sessionId))
358
+ });
359
+ break;
279
360
  case "presence":
280
361
  if (presence && frame.sessionId === void 0) queue.push({
281
362
  kind: "presence",
@@ -378,6 +459,14 @@ const wakeChannel = (channel) => {
378
459
  channel.wake?.();
379
460
  channel.wake = null;
380
461
  };
462
+ const answersAttempt = (frame, channel) => frame.attempt === void 0 || frame.attempt === channel.attempt;
463
+ const subscriptionStart = (index, reducer) => index === void 0 ? {
464
+ bootstrap: true,
465
+ reducer
466
+ } : {
467
+ index,
468
+ reducer
469
+ };
381
470
  const subscribeFrame = (sessions) => {
382
471
  return JSON.stringify({
383
472
  kind: "subscribe",
@@ -394,6 +483,7 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
394
483
  let socketOpen = false;
395
484
  let watchdog;
396
485
  let nextReq = 1;
486
+ let nextAttempt = 1;
397
487
  const trySend = (data) => {
398
488
  if (socket === null || !socketOpen) return;
399
489
  try {
@@ -429,6 +519,7 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
429
519
  const detach = (channel) => {
430
520
  if (channels.get(channel.sessionId) !== channel) return;
431
521
  channels.delete(channel.sessionId);
522
+ ready.notify(channel.sessionId);
432
523
  if (!channel.dropped) {
433
524
  const frame = {
434
525
  kind: "unsubscribe",
@@ -456,12 +547,17 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
456
547
  };
457
548
  const route = (frame) => {
458
549
  switch (frame.kind) {
550
+ case "snapshot": {
551
+ const channel = channels.get(frame.sessionId);
552
+ if (channel && channel.live && !channel.ended) deliver(channel, frame);
553
+ return;
554
+ }
459
555
  case "ping":
460
556
  for (const channel of channels.values()) if (channel.live && !channel.ended) deliver(channel, { kind: "ping" });
461
557
  return;
462
558
  case "subscribed": {
463
559
  const channel = channels.get(frame.sessionId);
464
- if (channel === void 0 || channel.ended || channel.live) return;
560
+ if (channel === void 0 || channel.ended || channel.live || !answersAttempt(frame, channel)) return;
465
561
  channel.live = true;
466
562
  deliver(channel, { kind: "ping" });
467
563
  ready.notify(frame.sessionId);
@@ -469,7 +565,7 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
469
565
  }
470
566
  case "unsubscribed": {
471
567
  const channel = channels.get(frame.sessionId);
472
- if (channel === void 0) return;
568
+ if (channel === void 0 || !answersAttempt(frame, channel)) return;
473
569
  channel.dropped = true;
474
570
  settleChannel(channel, frame.error ? asClientError(frame.error) : frame.reason === void 0 ? null : new A2ClientError("CONNECTION_FAILED", frame.reason));
475
571
  return;
@@ -490,6 +586,16 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
490
586
  });
491
587
  return;
492
588
  }
589
+ case "events": {
590
+ const { sessionId } = frame;
591
+ if (sessionId === void 0) return;
592
+ const channel = channels.get(sessionId);
593
+ if (channel !== void 0 && channel.live && !channel.ended) deliver(channel, {
594
+ kind: "events",
595
+ events: frame.events.map((row) => eventFromCompactWire(row, sessionId))
596
+ });
597
+ return;
598
+ }
493
599
  case "presence": {
494
600
  if (!presence || frame.sessionId === void 0) return;
495
601
  const channel = channels.get(frame.sessionId);
@@ -513,7 +619,9 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
513
619
  const ensureSocket = () => {
514
620
  if (socket !== null) return;
515
621
  if (webSocketImpl === void 0) throw new TypeError("no WebSocket implementation available — pass one via createClient({ webSocket }) where the global is missing");
516
- const created = new webSocketImpl(webSocketUrl(url).href);
622
+ const target = webSocketUrl(url);
623
+ target.searchParams.set("wire", "2");
624
+ const created = new webSocketImpl(target.href);
517
625
  socket = created;
518
626
  socketOpen = false;
519
627
  armWatchdog(created);
@@ -523,7 +631,8 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
523
631
  armWatchdog(created);
524
632
  const sessions = [...channels.values()].filter((channel) => !channel.ended).map((channel) => ({
525
633
  id: channel.sessionId,
526
- index: channel.startAfter
634
+ attempt: channel.attempt,
635
+ ...subscriptionStart(channel.startAfter, channel.reducer)
527
636
  }));
528
637
  if (sessions.length > 0) trySend(subscribeFrame(sessions));
529
638
  });
@@ -531,7 +640,13 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
531
640
  if (socket !== created) return;
532
641
  if (typeof event.data !== "string") return;
533
642
  armWatchdog(created);
534
- const frame = parseSocketFrame(event.data);
643
+ let frame;
644
+ try {
645
+ frame = parseSocketFrame(event.data);
646
+ } catch (error) {
647
+ failSocket(asClientError(error));
648
+ return;
649
+ }
535
650
  if (frame === null) return;
536
651
  route(frame);
537
652
  });
@@ -548,10 +663,12 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
548
663
  });
549
664
  };
550
665
  return {
551
- async *connect({ sessionId, startAfter, signal }) {
666
+ async *connect({ sessionId, startAfter, reducer: reducerName, signal }) {
552
667
  const channel = {
553
668
  sessionId,
669
+ attempt: nextAttempt++,
554
670
  startAfter,
671
+ reducer: reducerName,
555
672
  queue: [],
556
673
  live: false,
557
674
  dropped: false,
@@ -564,7 +681,8 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
564
681
  ensureSocket();
565
682
  if (socketOpen) trySend(subscribeFrame([{
566
683
  id: sessionId,
567
- index: startAfter
684
+ attempt: channel.attempt,
685
+ ...subscriptionStart(startAfter, reducerName)
568
686
  }]));
569
687
  } catch (error) {
570
688
  if (channels.get(sessionId) === channel) channels.delete(sessionId);
@@ -660,8 +778,10 @@ const transportFor = (api, fetchImpl, webSocketImpl, presence) => {
660
778
  return sessionWsTransport(api.url, webSocketImpl, presence);
661
779
  };
662
780
  function createClient(options) {
663
- const { reducer, api } = options;
664
- const presenceDefs = reducer.presence;
781
+ const { contract, api } = options;
782
+ const reducer = contract?.reducer;
783
+ if (!reducer) throw new TypeError("createClient requires a contract with a public reducer");
784
+ const presenceDefs = contract.presence;
665
785
  const declaresPresence = Object.keys(presenceDefs).length > 0;
666
786
  const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
667
787
  const clientFetch = async (input, init) => {
@@ -684,6 +804,8 @@ function createClient(options) {
684
804
  entry.timer = setTimeout(() => {
685
805
  if (sessions.get(sessionId) !== entry) return;
686
806
  if (entry.runtime.canEvict()) {
807
+ entry.runtime.close();
808
+ clearTimeout(entry.timer);
687
809
  sessions.delete(sessionId);
688
810
  return;
689
811
  }
@@ -695,10 +817,21 @@ function createClient(options) {
695
817
  scheduleGc(sessionId);
696
818
  };
697
819
  const makeSession = (sessionId, sessionOptions) => {
698
- let stateIndex = sessionOptions?.initialIndex ?? 0;
699
- let streamIndex = sessionOptions?.initialState === void 0 ? sessionOptions?.initialIndex : stateIndex;
700
- let foldedState = sessionOptions?.initialState !== void 0 ? sessionOptions.initialState : reducer.initialState;
701
- const serverEvents = (sessionOptions?.initialEvents ?? []).filter((event) => event.index <= stateIndex).toSorted((a, b) => a.index - b.index);
820
+ assertSessionSeed(sessionOptions);
821
+ const supplied = sessionOptions?.initial;
822
+ const initial = isInitialPromise(supplied) ? void 0 : supplied;
823
+ let hydrated = initial != null;
824
+ let bootstrapGeneration;
825
+ let stateIndex = initial?.index ?? 0;
826
+ let streamIndex = initial?.index;
827
+ let foldedState = initial == null ? reducer.initialState : initial.state;
828
+ /** The latest clear marker this session has learned of, or 0. */
829
+ let clearIndex = initial?.clearIndex ?? 0;
830
+ let initialVersion = 0;
831
+ const seenInitials = /* @__PURE__ */ new WeakSet();
832
+ if (initial != null) seenInitials.add(initial);
833
+ let preload;
834
+ const serverEvents = (sessionOptions?.initialEvents ?? []).filter((event) => event.index <= stateIndex && event.index > clearIndex).toSorted((a, b) => a.index - b.index);
702
835
  let pending = [];
703
836
  let participant = sessionOptions?.participant ?? options.participant;
704
837
  let presenceState = nullProtoRecord();
@@ -720,6 +853,7 @@ function createClient(options) {
720
853
  let inFlightPushes = 0;
721
854
  let pushWireTail = Promise.resolve();
722
855
  let deferredHydration;
856
+ let resumeInitialError;
723
857
  let hydrationNotification;
724
858
  const emit = () => {
725
859
  for (const listener of [...listeners]) listener();
@@ -728,10 +862,16 @@ function createClient(options) {
728
862
  snapshot = null;
729
863
  emit();
730
864
  };
731
- const notifyHydrated = () => {
865
+ const notifyHydrated = (resume) => {
866
+ resumeInitialError ??= resume;
732
867
  snapshot = null;
733
868
  clearTimeout(hydrationNotification);
734
- hydrationNotification = setTimeout(emit, 0);
869
+ hydrationNotification = setTimeout(() => {
870
+ const failure = resumeInitialError;
871
+ resumeInitialError = void 0;
872
+ if (failure && active && status === "paused" && lastError === failure) reconnectStream();
873
+ else emit();
874
+ }, 0);
735
875
  hydrationNotification.unref?.();
736
876
  };
737
877
  const mergeServerEvents = (events, throughIndex) => {
@@ -739,7 +879,7 @@ function createClient(options) {
739
879
  const byIndex = new Map(serverEvents.map((event) => [event.index, event]));
740
880
  let changed = false;
741
881
  for (const event of events) {
742
- if (event.index > throughIndex) continue;
882
+ if (event.index > throughIndex || event.index <= clearIndex) continue;
743
883
  if (byIndex.get(event.index)?.id === event.id) continue;
744
884
  byIndex.set(event.index, event);
745
885
  changed = true;
@@ -748,10 +888,25 @@ function createClient(options) {
748
888
  serverEvents.splice(0, serverEvents.length, ...[...byIndex.values()].toSorted((a, b) => a.index - b.index));
749
889
  return true;
750
890
  };
891
+ /**
892
+ * Raise the latest known clear marker (a2-implementation.md §17.4):
893
+ * drop the served history it ended and every push acked below it.
894
+ * The boundary never moves backward.
895
+ */
896
+ const applyClearBoundary = (index) => {
897
+ if (index <= clearIndex) return false;
898
+ clearIndex = index;
899
+ const kept = serverEvents.filter((event) => event.index > index);
900
+ serverEvents.splice(0, serverEvents.length, ...kept);
901
+ pending = pending.filter((entry) => entry.acked === void 0 || entry.acked.index > index);
902
+ return true;
903
+ };
751
904
  const overlayEvents = () => {
752
905
  const acked = pending.filter((p) => p.acked).map((p) => p.acked).toSorted((a, b) => a.index - b.index);
753
906
  const maxKnown = Math.max(stateIndex, acked.at(-1)?.index ?? stateIndex);
754
- const unacked = pending.filter((p) => !p.acked).map((p, i) => ({
907
+ const waiting = pending.filter((p) => !p.acked);
908
+ const held = waiting.findIndex((p) => p.heldUntilAck);
909
+ const unacked = (held === -1 ? waiting : waiting.slice(0, held)).map((p, i) => ({
755
910
  id: p.id,
756
911
  type: p.type,
757
912
  payload: p.payload,
@@ -781,14 +936,14 @@ function createClient(options) {
781
936
  const oldestLoaded = serverEvents[0]?.index ?? null;
782
937
  return {
783
938
  loading: historyLoads > 0,
784
- complete: oldestLoaded === 1 || stateIndex === 0,
939
+ complete: hydrated && (oldestLoaded === clearIndex + 1 || stateIndex === clearIndex),
785
940
  oldestLoaded
786
941
  };
787
942
  };
788
943
  const buildSnapshot = () => {
789
944
  const overlay = overlayEvents();
790
945
  let state = foldedState;
791
- for (const event of overlay) if (event.index > stateIndex) state = reducer.fold(state, event);
946
+ for (const event of hydrated ? overlay : []) if (event.index > stateIndex) state = reducer.fold(state, event);
792
947
  const displayedOverlay = overlay.filter((event) => {
793
948
  if (event.index > stateIndex) return true;
794
949
  let low = 0;
@@ -803,7 +958,11 @@ function createClient(options) {
803
958
  return true;
804
959
  });
805
960
  return {
806
- state: reducer[clientStateProjection]?.(state) ?? state,
961
+ state: projectClientState({
962
+ reducer,
963
+ state
964
+ }),
965
+ hydrated,
807
966
  events: [...serverEvents, ...displayedOverlay],
808
967
  index: stateIndex,
809
968
  history: historyState(),
@@ -939,30 +1098,65 @@ function createClient(options) {
939
1098
  queuePresenceSend(validated, at);
940
1099
  touch(sessionId);
941
1100
  };
942
- /** Pushes awaiting stream confirmation — resolved by `ingest` the
943
- * moment the state index passes their batch. */
1101
+ /** Pushes awaiting stream confirmation — resolved the moment the
1102
+ * stream, by event or bootstrap snapshot, passes their batch. */
944
1103
  let confirmWatchers = [];
1104
+ const resolveConfirmed = () => {
1105
+ const delivered = streamIndex ?? 0;
1106
+ const due = confirmWatchers.filter((watcher) => watcher.index <= delivered);
1107
+ if (due.length === 0) return;
1108
+ confirmWatchers = confirmWatchers.filter((watcher) => watcher.index > delivered);
1109
+ for (const watcher of due) watcher.resolve();
1110
+ };
1111
+ const retireCoveredPending = () => {
1112
+ pending = pending.filter((entry) => !entry.acked || entry.acked.index > Math.min(stateIndex, streamIndex ?? stateIndex));
1113
+ };
1114
+ const adoptBaseline = (baseline) => {
1115
+ hydrated = true;
1116
+ stateIndex = baseline.index;
1117
+ foldedState = baseline.state;
1118
+ for (const entry of pending) if (entry.sent && !entry.acked) entry.heldUntilAck = true;
1119
+ retireCoveredPending();
1120
+ };
1121
+ const bootstrapState = (frame) => {
1122
+ let state;
1123
+ try {
1124
+ state = decodeState({
1125
+ reducer,
1126
+ state: frame.stateUndefined ? void 0 : frame.state
1127
+ });
1128
+ } catch (error) {
1129
+ throw asClientError(error, { code: "INVALID_PAYLOAD" });
1130
+ }
1131
+ if (!reducer.stateSchema) return state;
1132
+ const result = validateSync(reducer.stateSchema, state, "bootstrap stateSchema");
1133
+ if (result.issues) throw new A2ClientError("INVALID_PAYLOAD", "bootstrap state failed stateSchema", { details: result.issues });
1134
+ return result.value;
1135
+ };
945
1136
  /** The single ingest point: every server-confirmed event, in log
946
1137
  * order, from the stream. */
947
- const ingest = (event) => {
948
- if (event.index <= (streamIndex ?? 0)) return;
949
- streamIndex = event.index;
950
- if (event.index > stateIndex) {
951
- stateIndex = event.index;
952
- serverEvents.push(event);
953
- foldedState = reducer.fold(foldedState, event);
954
- } else mergeServerEvents([event], stateIndex);
955
- pending = pending.filter((p) => p.id !== event.id);
956
- if (confirmWatchers.some((w) => w.index <= stateIndex)) {
957
- const due = confirmWatchers.filter((w) => w.index <= stateIndex);
958
- confirmWatchers = confirmWatchers.filter((w) => w.index > stateIndex);
959
- for (const watcher of due) watcher.resolve();
1138
+ const ingest = (events) => {
1139
+ let delivered = false;
1140
+ for (const event of events) {
1141
+ if (event.index <= (streamIndex ?? 0)) continue;
1142
+ delivered = true;
1143
+ streamIndex = event.index;
1144
+ const cleared = isSessionCleared(event);
1145
+ if (event.index > stateIndex) {
1146
+ stateIndex = event.index;
1147
+ if (!cleared) serverEvents.push(event);
1148
+ foldedState = foldEvent(reducer, foldedState, event);
1149
+ } else if (!cleared) mergeServerEvents([event], stateIndex);
1150
+ if (cleared) applyClearBoundary(event.index);
1151
+ pending = pending.filter((p) => p.id !== event.id);
960
1152
  }
1153
+ if (!delivered) return;
1154
+ resolveConfirmed();
961
1155
  notify();
962
1156
  touch(sessionId);
963
1157
  };
964
1158
  const validated = (events) => events.map((event) => {
965
- const schema = Object.hasOwn(reducer.events, event.type) ? reducer.events[event.type] : void 0;
1159
+ const schema = Object.hasOwn(contract.events, event.type) ? contract.events[event.type] : void 0;
966
1160
  if (!schema) throw new A2ClientError("UNKNOWN_EVENT_TYPE", `no event type '${String(event.type)}' in the reducer's vocabulary`);
967
1161
  const result = validateSync(schema, event.payload, `event '${String(event.type)}'`);
968
1162
  if (result.issues) throw new A2ClientError("INVALID_PAYLOAD", `invalid payload for event '${String(event.type)}'`, { details: result.issues });
@@ -998,7 +1192,7 @@ function createClient(options) {
998
1192
  return Object.defineProperty(ack, "confirmed", { get() {
999
1193
  confirmed ??= ack.then((acked) => new Promise((resolve) => {
1000
1194
  const last = acked.at(-1)?.index ?? 0;
1001
- if (stateIndex >= last) {
1195
+ if ((streamIndex ?? 0) >= last) {
1002
1196
  resolve(acked);
1003
1197
  return;
1004
1198
  }
@@ -1010,6 +1204,20 @@ function createClient(options) {
1010
1204
  return confirmed;
1011
1205
  } });
1012
1206
  };
1207
+ const initialSettled = (epoch) => new Promise((resolve, reject) => {
1208
+ const check = () => {
1209
+ const failure = leaseEpoch !== epoch || status === "closed" ? new DOMException("session closed", "AbortError") : !hydrated && preload?.error;
1210
+ if (failure) {
1211
+ listeners.delete(check);
1212
+ reject(failure);
1213
+ } else if (hydrated || !preload) {
1214
+ listeners.delete(check);
1215
+ resolve();
1216
+ }
1217
+ };
1218
+ listeners.add(check);
1219
+ check();
1220
+ });
1013
1221
  const push = (...events) => {
1014
1222
  touch(sessionId);
1015
1223
  return withConfirmed((async () => {
@@ -1027,10 +1235,19 @@ function createClient(options) {
1027
1235
  });
1028
1236
  pending.push(...entries);
1029
1237
  inFlightPushes += 1;
1238
+ let acknowledgedIndex;
1239
+ const pushEpoch = leaseEpoch;
1240
+ const releaseConnection = !hydrated || transport.awaitReady || active ? runtime.connect() : void 0;
1030
1241
  try {
1031
1242
  refreshIdleStream();
1032
1243
  notify();
1033
1244
  await waitForPriorPush;
1245
+ if (transport.awaitReady) {
1246
+ await initialSettled(pushEpoch);
1247
+ await transport.awaitReady(sessionId, STREAM_TIMINGS.stallTimeoutMs);
1248
+ }
1249
+ if (leaseEpoch !== pushEpoch) throw new DOMException("session closed", "AbortError");
1250
+ for (const entry of entries) entry.sent = true;
1034
1251
  const acked = await post({
1035
1252
  sessionId,
1036
1253
  events: entries.map(({ id, type, payload }) => ({
@@ -1039,11 +1256,12 @@ function createClient(options) {
1039
1256
  payload
1040
1257
  }))
1041
1258
  });
1259
+ acknowledgedIndex = acked.at(-1)?.index;
1042
1260
  for (const event of acked) {
1043
1261
  const entry = pending.find((p) => p.id === event.id);
1044
1262
  if (entry) entry.acked = event;
1045
1263
  }
1046
- pending = pending.filter((entry) => entry.acked === void 0 || entry.acked.index > Math.min(stateIndex, streamIndex ?? stateIndex));
1264
+ retireCoveredPending();
1047
1265
  if ((acked.at(-1)?.index ?? 0) > (streamIndex ?? stateIndex)) refreshIdleStream();
1048
1266
  notify();
1049
1267
  return acked;
@@ -1053,12 +1271,20 @@ function createClient(options) {
1053
1271
  notify();
1054
1272
  throw err;
1055
1273
  } finally {
1274
+ if (releaseConnection && pushEpoch === leaseEpoch && acknowledgedIndex !== void 0 && acknowledgedIndex > (streamIndex ?? 0)) confirmWatchers.push({
1275
+ index: acknowledgedIndex,
1276
+ resolve: releaseConnection,
1277
+ lease: true
1278
+ });
1279
+ else releaseConnection?.();
1056
1280
  releasePush();
1057
1281
  inFlightPushes -= 1;
1058
1282
  if (inFlightPushes === 0 && deferredHydration) {
1059
1283
  const next = deferredHydration;
1060
1284
  deferredHydration = void 0;
1061
- runtime.hydrate(next);
1285
+ if (next.version === void 0 || next.version === initialVersion) {
1286
+ if (applyInitial(next)) notifyHydrated();
1287
+ }
1062
1288
  }
1063
1289
  touch(sessionId);
1064
1290
  }
@@ -1095,18 +1321,19 @@ function createClient(options) {
1095
1321
  const run = historyTail.then(async () => {
1096
1322
  const upper = before ?? serverEvents[0]?.index ?? stateIndex + 1;
1097
1323
  const lte = Math.min(upper - 1, stateIndex);
1098
- const gte = Math.max(1, upper - (limit ?? DEFAULT_HISTORY_LIMIT));
1324
+ const gte = Math.max(clearIndex + 1, upper - (limit ?? DEFAULT_HISTORY_LIMIT));
1099
1325
  if (gte > lte) return [];
1100
1326
  const missing = missingBetween(gte, lte);
1101
1327
  if (missing === null) return eventsBetween(gte, lte);
1102
1328
  historyLoads += 1;
1103
1329
  notify();
1104
1330
  try {
1105
- const rows = await readHistory({
1331
+ const page = await readHistory({
1106
1332
  sessionId,
1107
1333
  ...missing
1108
1334
  });
1109
- mergeServerEvents(rows.map((row) => eventFromWire(row)), stateIndex);
1335
+ if (page.clearIndex !== void 0) applyClearBoundary(page.clearIndex);
1336
+ mergeServerEvents(page.events.map((row) => eventFromWire(row)), stateIndex);
1110
1337
  return eventsBetween(gte, lte);
1111
1338
  } catch (error) {
1112
1339
  throw asClientError(error);
@@ -1158,14 +1385,46 @@ function createClient(options) {
1158
1385
  };
1159
1386
  let wasLive = false;
1160
1387
  try {
1388
+ while (!hydrated && preload && !preload.error) await waitForInitial(controller.signal);
1389
+ if (generation !== run || controller.signal.aborted) break;
1390
+ if (!hydrated && preload?.error) {
1391
+ lastError = preload.error;
1392
+ status = "paused";
1393
+ notify();
1394
+ return;
1395
+ }
1396
+ if (hydrated && streamIndex === void 0) streamIndex = stateIndex;
1397
+ resolveConfirmed();
1398
+ if (generation !== run) break;
1399
+ bootstrapGeneration = streamIndex === void 0 ? run : void 0;
1161
1400
  const frames = transport.connect({
1162
1401
  sessionId,
1163
- startAfter: streamIndex ??= stateIndex,
1402
+ startAfter: streamIndex,
1403
+ reducer: reducer.name,
1164
1404
  signal: controller.signal
1165
1405
  });
1166
1406
  for await (const frame of frames) {
1167
1407
  if (generation !== run) break;
1168
1408
  armStall();
1409
+ if (frame.kind === "snapshot") {
1410
+ if (frame.sessionId !== sessionId || frame.name !== reducer.name || !isStateSnapshotFrame(frame)) throw new A2ClientError("INVALID_PAYLOAD", "bootstrap snapshot belongs to another session or reducer");
1411
+ if (bootstrapGeneration !== run) throw new A2ClientError("INVALID_PAYLOAD", "unexpected bootstrap snapshot");
1412
+ const baseline = !hydrated || frame.index >= stateIndex ? {
1413
+ state: bootstrapState(frame),
1414
+ index: frame.index
1415
+ } : void 0;
1416
+ streamIndex = frame.index;
1417
+ bootstrapGeneration = void 0;
1418
+ applyClearBoundary(frame.clearIndex ?? 0);
1419
+ if (baseline) adoptBaseline(baseline);
1420
+ resolveConfirmed();
1421
+ notify();
1422
+ if (generation !== run) break;
1423
+ }
1424
+ if (!hydrated || streamIndex === void 0) {
1425
+ if (frame.kind === "event" || frame.kind === "events") throw new A2ClientError("INVALID_PAYLOAD", "received an event before the bootstrap snapshot");
1426
+ continue;
1427
+ }
1169
1428
  if (!wasLive) {
1170
1429
  backoff = STREAM_TIMINGS.reconnectBaseMs;
1171
1430
  attempt = 0;
@@ -1182,7 +1441,10 @@ function createClient(options) {
1182
1441
  }
1183
1442
  if (frame.kind === "event") {
1184
1443
  streamIdle = false;
1185
- ingest(eventFromWire(frame.event));
1444
+ ingest([eventFromWire(frame.event)]);
1445
+ } else if (frame.kind === "events") {
1446
+ streamIdle = false;
1447
+ ingest(frame.events);
1186
1448
  } else if (frame.kind === "activity") {
1187
1449
  streamIdle = frame.idle;
1188
1450
  if (streamIdle && (inFlightPushes > 0 || pending.some((entry) => (entry.acked?.index ?? 0) > (streamIndex ?? stateIndex)))) refreshIdleStream();
@@ -1196,6 +1458,7 @@ function createClient(options) {
1196
1458
  timeoutMs: STREAM_TIMINGS.stallTimeoutMs
1197
1459
  }) : asClientError(err);
1198
1460
  } finally {
1461
+ if (bootstrapGeneration === run) bootstrapGeneration = void 0;
1199
1462
  clearTimeout(stall);
1200
1463
  }
1201
1464
  if (generation !== run) break;
@@ -1262,6 +1525,71 @@ function createClient(options) {
1262
1525
  if (active && generation === run) runStream(run);
1263
1526
  previous?.abort();
1264
1527
  };
1528
+ const applyInitial = ({ settings: next, version }) => {
1529
+ const nextIndex = next.initial?.index ?? 0;
1530
+ if (hydrated && nextIndex > stateIndex && inFlightPushes > 0) {
1531
+ if (!deferredHydration || nextIndex > (deferredHydration.settings.initial?.index ?? 0)) deferredHydration = {
1532
+ settings: next,
1533
+ ...version === void 0 ? {} : { version }
1534
+ };
1535
+ return false;
1536
+ }
1537
+ const boundaryChanged = applyClearBoundary(next.initial?.clearIndex ?? 0);
1538
+ const historyChanged = mergeServerEvents(next.initialEvents, Math.min(nextIndex, stateIndex)) || boundaryChanged;
1539
+ if (next.initial != null && bootstrapGeneration === void 0) streamIndex ??= nextIndex;
1540
+ if (next.initial == null || hydrated && nextIndex <= stateIndex) return historyChanged;
1541
+ adoptBaseline(next.initial);
1542
+ mergeServerEvents(next.initialEvents, stateIndex);
1543
+ return true;
1544
+ };
1545
+ const invalidateInitial = () => {
1546
+ initialVersion++;
1547
+ preload?.cancel();
1548
+ preload = void 0;
1549
+ if (deferredHydration?.version !== void 0) deferredHydration = void 0;
1550
+ };
1551
+ const settleInitial = (result) => {
1552
+ const entry = preload;
1553
+ if (!entry || entry.source !== result.source || entry.version !== initialVersion) return;
1554
+ entry.cancel();
1555
+ const cold = !hydrated;
1556
+ let changed = false;
1557
+ if ("error" in result) {
1558
+ if (cold) entry.error = asClientError(result.error);
1559
+ else preload = void 0;
1560
+ } else try {
1561
+ assertSessionSeed({ initial: result.value });
1562
+ if (isInitialPromise(result.value)) throw new TypeError("an initial Promise must resolve to a snapshot");
1563
+ preload = void 0;
1564
+ changed = applyInitial({
1565
+ settings: {
1566
+ ...entry.settings,
1567
+ initial: result.value
1568
+ },
1569
+ version: entry.version
1570
+ });
1571
+ } catch (error) {
1572
+ if (cold) {
1573
+ preload = entry;
1574
+ entry.error = asClientError(error, { code: "INVALID_PAYLOAD" });
1575
+ } else preload = void 0;
1576
+ }
1577
+ if (cold || changed) notifyHydrated();
1578
+ };
1579
+ const waitForInitial = (signal) => new Promise((resolve, reject) => {
1580
+ let settled = false;
1581
+ const finish = () => {
1582
+ if (settled || !signal.aborted && !hydrated && preload && !preload.error) return;
1583
+ settled = true;
1584
+ listeners.delete(finish);
1585
+ signal.removeEventListener("abort", finish);
1586
+ if (signal.aborted) reject(signal.reason);
1587
+ else resolve();
1588
+ };
1589
+ listeners.add(finish);
1590
+ signal.addEventListener("abort", finish, { once: true });
1591
+ finish();
1592
+ });
1265
1593
  const runtime = {
1266
1594
  sessionId,
1267
1595
  ...declaresPresence ? { setPresence } : {},
@@ -1302,43 +1630,71 @@ function createClient(options) {
1302
1630
  };
1303
1631
  },
1304
1632
  close() {
1633
+ invalidateInitial();
1634
+ resumeInitialError = void 0;
1305
1635
  leases = 0;
1306
1636
  leaseEpoch += 1;
1637
+ confirmWatchers = confirmWatchers.filter((watcher) => !watcher.lease);
1307
1638
  stopStream();
1308
1639
  },
1640
+ resolveInitial: settleInitial,
1309
1641
  hydrate(next) {
1642
+ assertSessionSeed(next);
1310
1643
  touch(sessionId);
1311
1644
  if (!next) return;
1312
1645
  participant ??= next.participant;
1313
- const nextIndex = next.initialIndex ?? 0;
1314
- if (nextIndex > stateIndex && inFlightPushes > 0) {
1315
- if (!deferredHydration || nextIndex > (deferredHydration.initialIndex ?? 0)) deferredHydration = next;
1316
- return;
1317
- }
1318
- const historyChanged = mergeServerEvents(next.initialEvents, Math.min(nextIndex, stateIndex));
1319
- if (next.initialState !== void 0) streamIndex ??= nextIndex;
1320
- if (next.initialState === void 0 || nextIndex <= stateIndex) {
1321
- if (historyChanged) notifyHydrated();
1646
+ const input = next.initial;
1647
+ const resume = !hydrated && status === "paused" && preload?.error === lastError ? preload.error : void 0;
1648
+ if (isInitialPromise(input)) {
1649
+ if (seenInitials.has(input)) {
1650
+ if (preload?.source === input && next.initialEvents !== void 0) preload.settings.initialEvents = next.initialEvents;
1651
+ if (mergeServerEvents(next.initialEvents, stateIndex)) notifyHydrated();
1652
+ return;
1653
+ }
1654
+ invalidateInitial();
1655
+ seenInitials.add(input);
1656
+ const { initial: _initial, ...settings } = next;
1657
+ const entry = {
1658
+ source: input,
1659
+ version: initialVersion,
1660
+ settings,
1661
+ cancel() {}
1662
+ };
1663
+ preload = entry;
1664
+ entry.cancel = observeInitial({
1665
+ promise: input,
1666
+ settled: (result) => settleInitial({
1667
+ source: input,
1668
+ ...result
1669
+ })
1670
+ });
1671
+ if (resume) notifyHydrated(resume);
1322
1672
  return;
1323
1673
  }
1324
- stateIndex = nextIndex;
1325
- foldedState = next.initialState;
1326
- mergeServerEvents(next.initialEvents, stateIndex);
1327
- pending = pending.filter((entry) => !entry.acked || entry.acked.index > Math.min(stateIndex, streamIndex ?? stateIndex));
1328
- if (confirmWatchers.some((watcher) => watcher.index <= stateIndex)) {
1329
- const due = confirmWatchers.filter((watcher) => watcher.index <= stateIndex);
1330
- confirmWatchers = confirmWatchers.filter((watcher) => watcher.index > stateIndex);
1331
- for (const watcher of due) watcher.resolve();
1674
+ if (input != null) {
1675
+ const repeated = seenInitials.has(input);
1676
+ seenInitials.add(input);
1677
+ if (repeated || hydrated && input.index <= stateIndex) {
1678
+ if (deferredHydration?.settings.initial === input && next.initialEvents !== void 0) deferredHydration.settings.initialEvents = next.initialEvents;
1679
+ const boundaryChanged = applyClearBoundary(input.clearIndex ?? 0);
1680
+ if (mergeServerEvents(next.initialEvents, Math.min(input.index, stateIndex)) || boundaryChanged) notifyHydrated();
1681
+ return;
1682
+ }
1332
1683
  }
1333
- notifyHydrated();
1684
+ if (input !== void 0) invalidateInitial();
1685
+ if (applyInitial({ settings: {
1686
+ ...next,
1687
+ initial: input
1688
+ } }) || resume || input === null) notifyHydrated(resume);
1334
1689
  },
1335
1690
  canEvict() {
1336
1691
  return listeners.size === 0 && !active && inFlightPushes === 0 && historyLoads === 0 && confirmWatchers.length === 0 && presenceTimer === void 0;
1337
1692
  }
1338
1693
  };
1694
+ if (isInitialPromise(supplied)) runtime.hydrate(sessionOptions);
1339
1695
  return runtime;
1340
1696
  };
1341
- return { session(sessionId, sessionOptions) {
1697
+ const client = { session(sessionId, sessionOptions) {
1342
1698
  const existing = sessions.get(sessionId);
1343
1699
  if (existing) {
1344
1700
  existing.runtime.hydrate(sessionOptions);
@@ -1350,8 +1706,19 @@ function createClient(options) {
1350
1706
  touch(sessionId);
1351
1707
  return runtime;
1352
1708
  } };
1709
+ registerRenderSessionFactory({
1710
+ client,
1711
+ factory: (sessionId, settings) => makeSession(sessionId, settings),
1712
+ resolveInitial: ({ session, source, value }) => {
1713
+ session.resolveInitial({
1714
+ source,
1715
+ value
1716
+ });
1717
+ }
1718
+ });
1719
+ return client;
1353
1720
  }
1354
1721
  //#endregion
1355
- export { A2ClientError as n, createClient as t };
1722
+ export { A2ClientError as a, isInitialPromise as i, createRenderSession as n, resolveInitialForReact as r, createClient as t };
1356
1723
 
1357
- //# sourceMappingURL=client-BYzHjkwU.js.map
1724
+ //# sourceMappingURL=client-BReKqwZC.js.map