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,384 +0,0 @@
1
- ---
2
- title: Live UI with React
3
- description: "Synchronize one session from server to browser: render its state, stream new events, and push writes optimistically through one reducer."
4
- ---
5
-
6
- ## The model
7
-
8
- A session can be read live from the browser. The server renders the first
9
- paint, a stream keeps it fresh, and writes apply instantly. One reducer
10
- produces every one of those views; server and client fold the same log
11
- with the same function, so they can't disagree.
12
-
13
- That's the trick, really. The client receives one state snapshot and its log
14
- position, then syncs later events and folds them locally. It does not keep
15
- synchronizing state. An append-only log with positions is simpler.
16
-
17
- This is session sync. The client follows one known session id; it does not
18
- subscribe to database tables or queries across many sessions. Keep those
19
- collection views in your [application database](/guides/application-data).
20
-
21
- The pieces: two route handlers (read and write), a factory that binds your
22
- reducer, and the provider + hook it returns.
23
-
24
- ## The API route
25
-
26
- `server.fetch` exposes a session over HTTP. It is bound, so the route can
27
- export it directly: `GET` streams events and serves history slices;
28
- `POST` appends.
29
-
30
- ```ts app/api/order-events/route.ts
31
- import { ordersServer } from '@/server/orders'
32
-
33
- export const GET = ordersServer.fetch
34
- export const POST = ordersServer.fetch
35
- ```
36
-
37
- `GET` is the read path. A plain `GET` is the live stream: a server-sent
38
- events response of one session's events, resumed after the `index` query
39
- parameter, so clients pick up exactly where they left off after a first
40
- paint or a dropped connection. A `GET` carrying `gte`/`lte` query
41
- parameters is a history slice instead: the bounded log range as JSON,
42
- the cold read [`loadHistory`](#the-client-component) rides.
43
-
44
- `POST` is the write path. The push envelope is validated, then `append` does the
45
- rest. The response is the appended events: an ack, not a stream. Thrown
46
- [`A2Error`s](/reference/errors#over-the-wire) serialize onto the wire so
47
- the client can branch on the same codes.
48
-
49
- Wrap `server.fetch` when the route needs authentication, per-operation
50
- authorization, or response decoration. `authorize` receives the parsed
51
- A2 operation after envelope parsing and before schema validation or I/O.
52
- Return `false` to answer `FORBIDDEN`. A thrown error is treated as an
53
- infrastructure failure.
54
-
55
- ```ts
56
- // app/api/order-events/route.ts, now with response decoration:
57
- import { ordersServer } from '@/server/orders'
58
-
59
- export async function GET(request: Request): Promise<Response> {
60
- const response = await ordersServer.fetch(request)
61
- if (response.headers.get('a2-history-covered') === 'true') {
62
- response.headers.set('cache-control', 'private, max-age=31536000')
63
- }
64
- return response
65
- }
66
-
67
- export const POST = ordersServer.fetch
68
- ```
69
-
70
- ## The session module
71
-
72
- Bind your reducer once, in a `'use client'` file, and export the pair:
73
-
74
- ```tsx app/orders/[orderId]/session.ts
75
- 'use client'
76
- import { createClient } from 'experimental-a2/client'
77
- import { createReact } from 'experimental-a2/react'
78
- import { ordersReducer } from '@/reducer'
79
-
80
- export const ordersClient = createClient({
81
- reducer: ordersReducer,
82
- api: '/api/order-events',
83
- })
84
- export const { SessionProvider, useSession } = createReact({ client: ordersClient })
85
- ```
86
-
87
- This is the whole client/server seam. `createReact` is a factory (like
88
- `createContext`): everything it returns is typed by the reducer value;
89
- the state from its fold, the pushable events from its vocabulary. No type
90
- arguments anywhere, and nothing in this file touches the server module.
91
- The client also gives every session a stable in-memory identity. Calling
92
- `ordersClient.session(orderId)` before navigation and mounting the provider
93
- after navigation reaches the same optimistic state, with no handoff store.
94
-
95
- ## The server component
96
-
97
- The first paint costs no client JavaScript: fold on the server, pass the
98
- result down.
99
-
100
- ```tsx app/orders/[orderId]/page.tsx
101
- import { ordersServer } from '@/server/orders'
102
- import { ordersReducer } from '@/reducer'
103
- import { SessionProvider } from './session'
104
- import { OrderClient } from './order-client'
105
-
106
- export default async function OrderPage({
107
- params,
108
- }: {
109
- params: Promise<{ orderId: string }>
110
- }) {
111
- const { orderId } = await params
112
- const session = ordersServer.session(orderId)
113
- const { state, index } = await session.state(ordersReducer)
114
-
115
- return (
116
- <SessionProvider
117
- sessionId={orderId}
118
- initialState={state}
119
- initialIndex={index}
120
- >
121
- <OrderClient />
122
- </SessionProvider>
123
- )
124
- }
125
- ```
126
-
127
- Two props do the heavy lifting. `initialState` is the fold. `initialIndex` is
128
- its frontier, the log position the fold reflects. The client opens its stream
129
- after that position. The stream catches up any events appended after the server
130
- fold, then stays live. No history read is needed to hydrate state.
131
- (No `reducer` or `api` prop: the provider got both from the shared client.)
132
-
133
- ## The client component
134
-
135
- ```tsx app/orders/[orderId]/order-client.tsx
136
- 'use client'
137
- import { useSession } from './session'
138
-
139
- export function OrderClient() {
140
- const { state, push } = useSession()
141
-
142
- return (
143
- <button onClick={() => push({ type: 'shop.started', payload: {} })}>
144
- Start
145
- </button>
146
- )
147
- }
148
- ```
149
-
150
- What the hook gives you:
151
-
152
- - **`state`**: the live view, folded through the shared reducer. It
153
- updates three ways: optimistically when you push, corrected when the
154
- server acks, and continuously as the stream delivers events, yours and
155
- everyone else's.
156
- - **`push`**: typed against the contract's vocabulary (via the reducer). Payloads are
157
- validated locally before anything leaves the browser, apply instantly,
158
- swap in the real event when the `POST` acks, and refold without it if
159
- the server says no. Rejections carry the same
160
- [`A2Error` codes](/reference/errors) the server throws, so both sides
161
- branch on `code`. Awaiting `push(...)` gives the server ack; the
162
- result also carries **`.confirmed`**, a promise for the moment the
163
- live stream delivers the batch back and the view shows server truth.
164
- `await push(...).confirmed` is "continue once this is real"; two
165
- `performance.now()` calls around the two awaits are a complete
166
- push→ack→stream latency meter. Overlapping calls on the same
167
- session still apply immediately, then enter the transport in call
168
- order. Controls do not need to wait or disable themselves to preserve
169
- that order.
170
- - **`events`**: the raw events this client has observed or was explicitly
171
- seeded with. With only the server snapshot, it begins after `initialIndex`.
172
- Use it for UI that wants the log itself: an activity feed, a debug panel.
173
- Earlier events are not needed to hydrate `state`. Later hydration can
174
- advance state ahead of this feed; the stream keeps its own resume position
175
- and adds delayed events without folding them again.
176
- - **`loadHistory`**: backscroll. `loadHistory({ before?, limit? })`
177
- fetches a bounded slice of the log from below the frontier (the same
178
- route, `gte`/`lte` query parameters) and merges it into `events`:
179
- deduped, ordered, shared across every handle of the session. By
180
- default each call walks backward 50 events at a time from the oldest
181
- one loaded, including after a newer snapshot hydrates the session.
182
- Pass an explicit `before` to read another range. It never touches `state`
183
- or the optimistic overlay; backscrolled events are display data.
184
- Calls serialize per session, so
185
- a double-tap never fetches the same range twice. The `ws` api has no
186
- history lane; `loadHistory` throws a `TypeError` there.
187
- - **`history`**: backscroll progress, `{ loading, complete,
188
- oldestLoaded }`. `complete` means the feed reaches index 1 (or the
189
- log is empty): nothing older is left, hide the "load older" button.
190
- - **`index`**: the latest server-confirmed state index, advanced by
191
- snapshots and streamed events. This is the `lastSeenIndex` that makes
192
- [cancellation](/guides/cancellation) exact.
193
- - **`connection`**: a discriminated union of `{ status: 'idle' }`,
194
- `{ status: 'connecting', reconnects, error }`,
195
- `{ status: 'paused', reconnects, error }`,
196
- `{ status: 'live', reconnects }`, or `{ status: 'closed' }`. A status
197
- dot is one ternary away; "reconnecting…" is
198
- `status === 'connecting' && reconnects > 0`; `error` (why the last
199
- connection ended) only exists while disconnected, so impossible
200
- states don't compile. It's honest about silence, too: the server
201
- heartbeats the stream (`: ping` every 15s), and a client that hears
202
- nothing for two beats treats the connection as dead and reconnects, so
203
- `live` means bytes are flowing, not "the socket hasn't errored yet".
204
-
205
- The provider opens the stream on mount, closes it on unmount, and
206
- reconnects with backoff, resuming from `index`, when the connection
207
- drops. Components that only write don't need the hook; `POST` to the
208
- route directly, or call `ordersClient.session(id).push(...)` so a later
209
- provider sees the same optimistic session.
210
-
211
- Polling streams start fast and back off to a two-second interval when idle.
212
- Idle is a polling state; the connection remains live and heartbeats continue.
213
- When you call `push()` on an idle SSE stream, the client refreshes that stream
214
- immediately, without reconnect backoff. It resumes from its existing `index`,
215
- so concurrent events before the write's acknowledged index are still read.
216
- Overlapping pushes share the refreshed stream. If a slow push is still pending
217
- when the stream idles again, or its acknowledged events have not arrived, the
218
- client refreshes again. WebSockets wake the session's attached read loop
219
- without replacing the socket. This is internal and requires no component
220
- configuration.
221
-
222
- Use `reconnect()` after a custom REST request commits a change outside
223
- `push()`. It is available on the session client and the `useSession()` result.
224
- It immediately restarts the live subscription from its current `index`, even
225
- when the stream is active. State, optimistic writes, and connection leases
226
- are preserved. Without an active lease it does nothing.
227
-
228
- `reconnect()` returns `void`; observe `connection` and `index` for progress.
229
- SSE opens a fresh GET. A single-session WebSocket opens a fresh socket; a
230
- multiplexed WebSocket re-subscribes only this session on its existing socket.
231
- Other subscribed sessions keep running. Any outstanding write interrupted by
232
- a socket replacement uses the normal idempotent retry path.
233
-
234
- The server stops the old session lane before authorizing its replacement.
235
- That lane stays closed if authorization denies access or fails. Successful
236
- authorization catches up from the cursor. Already accepted writes can finish,
237
- and failed subscription attempts follow the normal reconnect retry policy.
238
-
239
- Configure `createClient({ reconnect })` on a shared client to choose each
240
- retry's delay or return `false` to pause automatic reconnection. The callback
241
- receives `{ sessionId, error, attempt }`; `error` is an `A2ClientError`, or
242
- `null` when the stream ended cleanly. `attempt` starts at 1 and resets when
243
- the connection becomes live or you call `reconnect()` explicitly.
244
-
245
- A paused connection keeps the hydrated session, its writer, and its existing
246
- state. Observe `connection.status === 'paused'` and use the hook's
247
- `reconnect()` to resume when your application is ready. New consumers and
248
- pushes do not automatically resume it. Configure the policy where you create
249
- the client, without changing the hook's `hydrate` input.
250
-
251
- That identity is an in-memory L1, not another source of truth. Repeated
252
- `session(id)` calls reuse it while active and for five idle minutes by
253
- default. A newer server fold advances it, pending pushes stay overlaid,
254
- and a stale server render cannot rewind it. The optional IndexedDB cache
255
- is the L2: it survives reloads; the memory runtime does not. The
256
- stream is shared the same way: `connect()` takes a lease and returns
257
- its release, so however many hooks, providers, or plain calls hold one
258
- session open, the first lease connects and the last release closes
259
- (`close()` is the hard stop).
260
-
261
- One detail worth knowing: every push carries a client-generated event id.
262
- That id is how the ack finds its optimistic entry, and it makes retrying
263
- a failed `POST` idempotent. `push` retries `STORE_UNAVAILABLE` and client
264
- transport failures, with a three-attempt budget.
265
-
266
- Optimistic pushes are also what make [cancellation](/guides/cancellation)
267
- feel instant: the `cancelled` event folds locally before the server ever
268
- sees it.
269
-
270
- Sessions can outlive the tab, too: hand `createReact` a `cache` and
271
- revisits paint from the local copy, the stream resumes from the cached
272
- frontier, and offline pushes queue and replay. See
273
- [Local-first](/guides/local-first).
274
-
275
- ## Client-first sessions
276
-
277
- Everything above assumes a server component hands the first fold down.
278
- A client-rendered app often has nothing to hand: a sidebar showing ten
279
- channel sessions, a user session read from deep inside a menu, panes
280
- that mount from client-side navigation. No server fold, and no natural
281
- provider boundary for components that reach sessions ad hoc.
282
-
283
- For that shape, `experimental-a2/react` exports a standalone hook. Any
284
- component can mount any session, no provider needed:
285
-
286
- ```tsx app/orders/[orderId]/order-badge.tsx
287
- 'use client'
288
- import { useSession } from 'experimental-a2/react'
289
- import { ordersClient } from './session'
290
-
291
- export function OrderBadge({ orderId }: { orderId: string }) {
292
- // your fetch (SWR, a router loader, a parent's payload): anything
293
- // that lands the { state, index } your route returns (below), e.g.
294
- // const { data } = useSWR(`/api/orders/${orderId}/state`)
295
- const data = undefined // still loading
296
-
297
- const { state } = useSession(ordersClient, orderId, { hydrate: data })
298
- if (!data) return <span>…</span> // your loading state IS the pending state
299
- return <span>{state.status}</span>
300
- }
301
- ```
302
-
303
- The fold comes from your own route, and the route is one line of A2:
304
-
305
- ```ts app/api/orders/[orderId]/state/route.ts
306
- import { ordersServer } from '@/server/orders'
307
- import { ordersReducer } from '@/reducer'
308
-
309
- export async function GET(
310
- _req: Request,
311
- { params }: { params: Promise<{ orderId: string }> },
312
- ) {
313
- const { orderId } = await params
314
- // here's where you'd do auth, or any other checks
315
- return Response.json(
316
- await ordersServer.session(orderId).state(ordersReducer),
317
- )
318
- }
319
- ```
320
-
321
- Note what is not on the wire: a reducer name. The route calls
322
- `session.state(reducer)` with the reducer by reference, the same
323
- module your client bundle folds with. That matters because renaming a
324
- reducer is the [snapshot invalidation knob](/concepts/state): if the
325
- name traveled on the wire, every rename would open a version-skew
326
- window between deployed clients and servers. By reference, the knob
327
- stays free. And unlike a history slice, which is immutable once read
328
- and earns a standard wire shape, a state read is a moving snapshot;
329
- there is nothing for the library to standardize, so the read stays
330
- behind your route, under your auth and caching policy.
331
-
332
- `hydrate` is one atomic option: the `{ state, index }` pair
333
- `session.state(reducer)` returns. State and its frontier travel
334
- together or not at all; a half-present handoff cannot compile. While
335
- it is `undefined` (your fetch has not landed), the hook holds the
336
- stream and `state` is the reducer's `initialState`. There is no
337
- pending flag on the result: whether the fold has been handed over is
338
- your own input, so your data layer's loading state is the pending
339
- state. When the value arrives, the session hydrates
340
- and the stream connects at that frontier. The hold is deliberate:
341
- connecting without a fold means replaying the whole log through the
342
- browser, and the library never picks the expensive path silently. When
343
- a full replay is what you want (a short log, a debug view), hand the
344
- fold's true starting point:
345
- `hydrate: { state: ordersReducer.initialState, index: 0 }`. State at
346
- index 0 is the reducer's seed by definition, so the explicit replay
347
- needs no special vocabulary; the stream then delivers every event
348
- live.
349
-
350
- The rest behaves like the provider. The handle is identity-mapped
351
- (every hook and provider mounting the same session shares one runtime)
352
- and each mount holds a lease on the shared stream (see
353
- [the client identity note](#the-client-component)). A later, further
354
- fold handed to the same session
355
- advances it and a stale one is ignored, the same never-move-backward
356
- rule as everywhere else. For presence, identity comes from the
357
- client's `participant` (set once on `createClient`) or the hook's
358
- `participant` option, which overrides it. The
359
- result is exactly the shape the bound hook returns. There is no
360
- loading or error member: the
361
- library performs no fetch here, so your data layer's states are the
362
- states, retried however your data layer retries.
363
-
364
- `SessionProvider` keeps its required `initialState`/`initialIndex`.
365
- The provider is the server-handoff tool; the hook is the client-first
366
- tool.
367
-
368
- ## Keep the backend out of the bundle
369
-
370
- The split is structural, not disciplinary. Core `experimental-a2` (the contract,
371
- reducers, errors) imports no backend, ever; only `experimental-a2/server` can reach
372
- one, and its exports map resolves to a loud error under the browser
373
- condition. The layout above rides that:
374
-
375
- 1. `contracts.ts` and `reducer.ts` import nothing but schemas and `experimental-a2`,
376
- isomorphic by construction.
377
- 2. The session module imports the reducer, never `experimental-a2/server`. Client
378
- components don't need the server's *type*, even: the factory carries
379
- all the typing.
380
-
381
- Belt and suspenders: put `import 'server-only'` at the top of your
382
- server module and enable `verbatimModuleSyntax` in your tsconfig; an
383
- accidental value import then fails your build too, with your own error
384
- message.
@@ -1,88 +0,0 @@
1
- ---
2
- title: Local-first
3
- description: "Cache one A2 session in the browser: instant revisits, stream resumption, and offline writes."
4
- ---
5
-
6
- ## One option
7
-
8
- ```tsx app/orders/[orderId]/session.ts
9
- 'use client'
10
- import { createReact } from 'experimental-a2/react'
11
- import { indexedDb } from 'experimental-a2/cache-indexeddb'
12
- import { ordersReducer } from '@/reducer'
13
-
14
- export const { SessionProvider, useSession } = createReact({
15
- reducer: ordersReducer,
16
- cache: indexedDb(),
17
- })
18
- ```
19
-
20
- This is the [session module](/guides/react#the-session-module) from Live
21
- UI with one addition. The cache is created here, in client code, where
22
- live objects belong; server components keep importing `SessionProvider`
23
- exactly as before; nothing crosses the server boundary.
24
-
25
- With `cache` set:
26
-
27
- - **Revisits paint instantly.** State folds from the local copy before the
28
- network says a word. Works offline too.
29
- - **The stream resumes from the cached frontier.** Reconnects fetch only
30
- what this browser hasn't seen, no matter how long it's been.
31
- - **Pushes queue while offline**, and replay, in order, when the
32
- connection returns.
33
-
34
- Without it, nothing changes; the provider behaves exactly as before.
35
-
36
- ## Why this is safe
37
-
38
- Local-first is usually hard because it means syncing state, and state
39
- needs merging. A2 caches an append-only log with server-assigned positions
40
- instead. A cached event can never be *wrong*; this browser can only be
41
- *behind*. Catching up is fetching events after an index, which is what
42
- `stream({ startAfter })` does anyway. There's no merge function because
43
- there's nothing to merge.
44
-
45
- ## What gets stored
46
-
47
- Three things, in IndexedDB:
48
-
49
- | Stored | Invalidated by |
50
- | --------------------------------------------- | ------------------------------------------------------- |
51
- | the session's events | nothing; they're immutable |
52
- | a folded snapshot, keyed by reducer name | a reducer rename (or a `stateSchema` mismatch, when the reducer declares one): discarded, refolded from cached events |
53
- | pending pushes | the server's ack |
54
-
55
- Note the middle row: the reducer's `name` does the same job here that it
56
- does for [server snapshots](/concepts/state#snapshots-are-a-cache). One
57
- string, both sides of the wire.
58
-
59
- Deleting the cache is always safe. It's a replica of what this browser
60
- already saw; the log on the server stays the only authority.
61
-
62
- ## Offline writes
63
-
64
- `push` while offline persists the events, client ids and all, and
65
- applies them optimistically, same as always. On reconnect they replay in
66
- order. The replay is idempotent by construction: an identical batch is an
67
- idempotent success, so a flaky reconnect, or a second tab replaying the
68
- same queue, causes no duplicates. Rejections refold without the event,
69
- exactly like an online push the server refused.
70
-
71
- What this is *not*: local execution. Handlers run on the server, when the
72
- events arrive. An offline push is a queued intent, not a completed
73
- action. Design the UI accordingly ("sending…" is honest; "sent" is not).
74
-
75
- ## Logout
76
-
77
- Cached events outlive the session cookie. If your app authenticates,
78
- clear the cache when the user leaves:
79
-
80
- ```ts
81
- // in the browser, wherever your logout flow runs:
82
- await cache.clear() // everything, or cache.clear(sessionId) for one session
83
- ```
84
-
85
- That's the one obligation `cache` puts on you. The cache never *grants*
86
- access (your routes still authorize every read and write) but it
87
- *retains* what was legitimately seen, and retained data belongs to the
88
- person who saw it.