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,50 +1,35 @@
1
1
  ---
2
2
  title: Concurrency
3
- description: "Each instance processes one message at a time. Slow work goes in concurrent handlers: they run in parallel and report back by sending events."
3
+ description: "Each instance processes one message at a time. Slow work goes in concurrent handlers, which run in parallel and report back by sending events."
4
4
  ---
5
5
 
6
6
  ## One message at a time
7
7
 
8
- Every message an instance receives runs on its
9
- [lane](/concepts/handlers#lanes): a serial queue, one handler at a
10
- time, in arrival order. This is what makes handlers easy to trust. A
11
- handler always sees the state every previous message produced, its
12
- checks cannot be raced by another message, and its changes commit
13
- before the next message runs.
8
+ Every message runs on the instance's [lane](/server/handlers#lanes):
9
+ one handler at a time, in arrival order. A handler sees the state every
10
+ previous message produced, no other message can race its checks, and
11
+ its changes commit before the next message runs.
14
12
 
15
- The lane is per instance. A thousand vaults process in parallel; each
16
- vault processes in order. If a single instance receives more traffic
17
- than one queue can absorb, split the identity and give the work more
18
- instances (per user, per order, per document) instead of trying to
19
- make one instance concurrent.
20
-
21
- ## The problem with slow work
22
-
23
- A serial handler holds the lane while it runs. If `withdraw` awaits a
24
- two-second bank API, every deposit waits behind it. The instance stays
25
- correct, but it stops responding.
26
-
27
- The fix is not to weaken the lane. It is to split the work: serial
28
- handlers make the decisions, concurrent handlers do the slow parts,
29
- and the results come back as events.
13
+ Lanes are per instance: a thousand vaults process in parallel, each in
14
+ order. If one instance gets more traffic than one queue can absorb,
15
+ split the identity (per user, per order, per document) instead of
16
+ making one instance concurrent.
30
17
 
31
18
  ## Concurrent handlers
32
19
 
33
- Wrap a handler in `concurrent(...)` and it leaves the lane: it runs in
34
- parallel with serial handlers and with other concurrent handlers. Two
35
- things change inside it:
20
+ A serial handler holds the lane while it runs: if `withdraw` awaits a
21
+ two-second bank API, every deposit waits behind it. Keep decisions
22
+ serial and move the slow part into a handler wrapped in
23
+ `concurrent(...)`. It leaves the lane and runs in parallel with serial
24
+ handlers and other concurrent ones. Inside it:
36
25
 
37
- - There is no `ctx.state` draft to mutate. Reading state becomes a
38
- function call, `ctx.state()`, and the snapshot it returns can go
39
- stale while the handler keeps running.
40
- - State changes happen by sending events. Their serial handlers run on
41
- the lane and decide against current state.
26
+ - `ctx.state()` returns a snapshot, which can go stale while the
27
+ handler runs. There is no draft to mutate.
28
+ - State changes happen by sending events, whose serial handlers decide
29
+ against current state.
42
30
 
43
31
  ## Reserve, transfer, settle
44
32
 
45
- A withdrawal that talks to a slow bank, without ever blocking the
46
- vault: reserve the money, run the transfer, then settle or refund.
47
-
48
33
  ```ts vault.ts
49
34
  import { NonRetriableError } from 'experimental-a2'
50
35
  import { actor, concurrent } from 'experimental-a2/actor'
@@ -109,46 +94,34 @@ export const vault = actor<Vault>({
109
94
  })
110
95
  ```
111
96
 
112
- Step by step:
113
-
114
- 1. **`withdraw` reserves.** It runs on the lane, so the balance check
115
- cannot be raced by another withdrawal. The balance drops, `pending`
116
- records the reserved amount, and the `transfer` event commits
117
- atomically with both. The withdrawal cannot half-start.
118
- 2. **`transfer` does the slow part.** It runs off the lane, so
119
- deposits keep flowing meanwhile. It finishes by sending an event:
120
- `settle` on success, `refund` when the bank says no. It never
121
- changes state itself, and it reports failure by sending, not by
122
- throwing.
123
- 3. **`settle` and `refund` finish the job.** Both check `pending[ref]`
124
- first and return when the entry is gone, so a duplicate or late
125
- message does nothing. The refund amount comes from state, not from
126
- recomputing it.
127
-
128
- A crash at any step re-runs that one message from the state it started
129
- with. Wherever the process dies, the vault lands in a named state:
130
- reserved, settled, or refunded. Money is never half-transferred.
97
+ 1. **`withdraw` reserves** on the lane, so no other withdrawal can race
98
+ the balance check. The balance drop, the `pending` entry, and the
99
+ `transfer` event commit atomically: the withdrawal cannot
100
+ half-start.
101
+ 2. **`transfer` does the slow part** off the lane, so deposits keep
102
+ flowing. It never changes state, and it reports failure by sending
103
+ `refund`, not by throwing.
104
+ 3. **`settle` and `refund` finish.** Both return when `pending[ref]` is
105
+ gone, so a duplicate or late message does nothing. The refund amount
106
+ comes from state.
107
+
108
+ A crash re-runs only that message from the state it started with, so
109
+ the vault always lands reserved, settled, or refunded.
131
110
 
132
111
  ## Buffered sends
133
112
 
134
- `ctx.send` does not append immediately. Everything a handler sends is
135
- buffered and commits when the handler completes: for a serial handler,
136
- in the same commit as its state change; for a concurrent handler, when
137
- it finishes. A refusal discards the buffer along with the state
138
- changes, so a refused message leaves no trace. Re-runs produce the
139
- same message ids, so a retried handler does not append its sends
140
- twice.
113
+ `ctx.send` buffers. A serial handler's sends commit with its state
114
+ change; a concurrent handler's commit when it finishes. A refusal
115
+ discards the buffer with the state changes, so a refused message leaves
116
+ no trace. Re-runs produce the same message ids, so a retried handler
117
+ never appends its sends twice. `ctx.schedule` is
118
+ [not buffered](/actors/timers#scheduling-is-immediate).
141
119
 
142
120
  ## When handlers fail
143
121
 
144
- - A **serial** handler that throws `NonRetriableError` refuses the
145
- message: nothing commits, and the caller gets the answer (see
146
- [Introduction](/actors/introduction#refusals)).
147
- - A **serial** handler that throws anything else is retried on the
148
- lane, and the instance waits. This is deliberate: state never
149
- advances past a failure.
150
- - A **concurrent** handler that throws is retried off the lane;
151
- nothing else waits for it.
152
- - `call` works on serial events only. A concurrent handler produces no
153
- answer to wait for, so calling one is rejected: `send` those events
154
- instead.
122
+ A concurrent handler that throws retries off the lane, and nothing
123
+ waits for it. Serial handlers fail as described in
124
+ [Refusals](/actors/introduction#refusals).
125
+
126
+ `call` works on serial events only. A concurrent handler produces no
127
+ answer, so calling one is rejected: `send` those events instead.
@@ -5,11 +5,6 @@ description: "Schedule a typed event back to the same instance. Deadlines surviv
5
5
 
6
6
  ## A deadline is a message, delivered later
7
7
 
8
- `ctx.schedule` mirrors `ctx.send`, one method per declared event, with
9
- a timing argument: exactly one of `{ delay: '1h' }` or `{ at: date }`.
10
- It hands the event to the configured scheduler, and delivery lands on
11
- the instance's lane like any other message.
12
-
13
8
  ```ts escrow.ts
14
9
  import { actor } from 'experimental-a2/actor'
15
10
  import { vercelQueues } from 'experimental-a2/scheduler-vercel'
@@ -46,38 +41,29 @@ export const escrow = actor<Escrow>({
46
41
  })
47
42
  ```
48
43
 
49
- Held funds now have two futures, and both are handled: someone accepts
50
- the offer, or the hour passes and the funds release themselves. The
51
- deadline is durable: it outlives the invocation that armed it and any
52
- deploys in between.
44
+ `ctx.schedule` mirrors `ctx.send`, one method per declared event, with
45
+ a timing argument: exactly one of `{ delay: '1h' }` or `{ at: date }`.
46
+ It requires the `scheduler` option. The deadline outlives the
47
+ invocation that armed it and any deploys in between, and delivery lands
48
+ on the instance's lane like any other message.
53
49
 
54
- Scheduling requires the `scheduler` option. Adapters, delay limits, and
55
- delivery semantics are the session timers underneath; see
56
- [Timers and delays](/guides/timers).
50
+ Adapters, delay limits, and delivery semantics are the
51
+ [session timers](/server/timers) underneath.
57
52
 
58
53
  ## Stale timers are no-ops
59
54
 
60
- When `accept` wins the race, the release timer still fires an hour
61
- later. It meets the same first-line guard the finalizer pattern already
62
- uses (`held[offerId] === undefined`) and does nothing. When the timer
63
- wins, a late `accept` meets that guard instead.
64
-
65
- There is no cancellation API, and the log records that the timer fired.
66
- Guarded delivery is the pattern: let the event land, read the state,
67
- return when it no longer applies. The guard that makes finalizers
68
- idempotent is the same guard that makes deadlines safe.
55
+ When `accept` wins, the release timer still fires an hour later, meets
56
+ the `held[offerId] === undefined` guard, and does nothing. When the
57
+ timer wins, a late `accept` meets the same guard. There is no
58
+ cancellation API, and the log records that the timer fired: let the
59
+ event land and return when it no longer applies.
69
60
 
70
61
  ## Identity and retries
71
62
 
72
63
  A timer's name defaults to the target event's name, scoped to the
73
- triggering message. A re-run of a crashed handler therefore addresses
74
- the same timer rather than arming a second one, and provider
75
- deduplication plus stable event ids fold duplicate deliveries into one
76
- append. Relative delays anchor to the triggering message's durable
77
- `createdAt`, so re-runs resolve the same due time.
78
-
79
- One handler run gets one timer per target event. To arm several toward
80
- the same event, name them apart:
64
+ triggering message, so a re-run of a crashed handler addresses the same
65
+ timer instead of arming a second. One handler run gets one timer per
66
+ target event; to arm several toward the same event, name them apart:
81
67
 
82
68
  ```ts
83
69
  // inside a handler:
@@ -87,24 +73,22 @@ await ctx.schedule.release(
87
73
  )
88
74
  ```
89
75
 
76
+ Duplicate deliveries fold into one append, and relative delays anchor
77
+ to the triggering message's `createdAt`; see
78
+ [Identity and retries](/server/timers#identity-and-retries).
79
+
90
80
  ## Scheduling is immediate
91
81
 
92
82
  :::warning
93
- Unlike `ctx.send`, `ctx.schedule` is not buffered. A timer lives at the
94
- provider, not in the log, so it cannot ride the handler's atomic
95
- commit: the call awaits provider acceptance right where it happens. A
96
- handler that schedules and then refuses has still armed the timer.
97
- Guarded delivery makes that safe (the stale timer fires into a no-op),
98
- but order handlers accordingly: guards and refusals first, `ctx.schedule`
99
- last.
83
+ Unlike `ctx.send`, `ctx.schedule` is not buffered: a timer lives at
84
+ the provider, not in the log, so the call awaits provider acceptance
85
+ right where it happens. A handler that schedules and then refuses has
86
+ still armed the timer. Guarded delivery makes that safe, but order
87
+ handlers accordingly: guards and refusals first, `ctx.schedule` last.
100
88
  :::
101
89
 
102
90
  ## From outside a handler
103
91
 
104
- The typed surface lives on handler contexts. To schedule from ordinary
105
- server code, use the instance's underlying session, which takes an
106
- explicit name, timing, and event:
107
-
108
92
  ```ts
109
93
  // anywhere on the server:
110
94
  import { escrow } from '@/escrow'
@@ -116,5 +100,7 @@ await escrow.actor('offer-42').session.schedule(
116
100
  )
117
101
  ```
118
102
 
119
- Root scheduling semantics apply (`{ at }` for retry-stable targets);
120
- see [Timers and delays](/guides/timers#schedule-from-ordinary-server-code).
103
+ Outside handler contexts, schedule through the instance's session with
104
+ an explicit name, timing, and event. Root scheduling semantics apply
105
+ (`{ at }` for retry-stable targets); see
106
+ [Schedule from ordinary server code](/server/timers#schedule-from-ordinary-server-code).
@@ -1,13 +1,10 @@
1
1
  ---
2
2
  title: Routes, clients, and React
3
- description: "One route serves an instance: GET streams live state, POST answers calls. Authorize per operation, project per mount, and mount it in React with one hook."
3
+ description: "One route serves an instance: GET streams live state, POST answers calls. Authorize per operation, project per mount, and call it from any client or one React hook."
4
4
  ---
5
5
 
6
6
  ## The actor for this page
7
7
 
8
- A vault with one internal field, so the projection section below has
9
- something to hide:
10
-
11
8
  ```ts vault.ts
12
9
  import { NonRetriableError } from 'experimental-a2'
13
10
  import { actor } from 'experimental-a2/actor'
@@ -47,9 +44,6 @@ export const vault = actor<Vault>({
47
44
 
48
45
  ## The route
49
46
 
50
- `handle.fetch` serves one instance over HTTP. Your route authenticates
51
- and picks the instance; the library serves the rest.
52
-
53
47
  ```ts app/vault/[vaultId]/route.ts
54
48
  import { vault } from '@/vault'
55
49
 
@@ -66,33 +60,43 @@ export const GET = respond
66
60
  export const POST = respond
67
61
  ```
68
62
 
69
- `GET` is the live stream: a server-sent events response of the
70
- instance's state commits, resumed after the `index` query parameter,
71
- with heartbeats. The stream carries state and nothing else: subscribers
72
- see what the actor is, never other callers' inputs, and a refusal
73
- answers only its caller. The full mailbox stays server-side.
63
+ `handle.fetch` serves one instance; your route authenticates and picks
64
+ which:
74
65
 
75
- `POST` is the call lane. The body names a declared event:
66
+ - **`GET`** streams state commits as server-sent events, resumed after
67
+ the `index` query parameter, with heartbeats. It carries state and
68
+ nothing else: subscribers never see other callers' inputs, and a
69
+ refusal answers only its caller.
70
+ - **`GET ?snapshot=1`** returns the current `{ state, index }`.
71
+ - **`POST`** makes a call and responds with its answer.
72
+
73
+ A `POST` body names a declared event:
76
74
 
77
75
  ```json
78
76
  { "event": "deposit", "input": { "amount": 100 }, "messageId": "m-1" }
79
77
  ```
80
78
 
81
- The response is the answer: `{ state, index }` on success, `409` with
82
- the refusal's message when the handler says no, `400` for an undeclared
83
- event. `messageId` is optional; send one to make retrying the request
84
- idempotent.
79
+ It answers `{ state, index }`, `409` with the refusal's message, or
80
+ `400` for an undeclared event. The optional `messageId` makes retrying
81
+ the request idempotent. Snapshot and call responses use
82
+ `Cache-Control: private, no-store`.
85
83
 
86
- `GET ?snapshot=1` reads the current `{ state, index }` without opening a
87
- stream. It passes through `authorize` as `{ type: 'state', id }` and uses
88
- the same `view` projection as streams and call answers. Snapshot and call
89
- responses use `Cache-Control: private, no-store`.
84
+ ### After a clear
90
85
 
91
- ## Authorize
86
+ After `handle.session.clear()`, snapshots, call answers, and stream
87
+ commits include `clearIndex`; keep it in any snapshot you pass to
88
+ `useActor`'s `initial`. The stream then sends the actor's initial state
89
+ (through `view`) as a reset, and the hook drops older history. A
90
+ delayed reset never replaces a newer snapshot.
92
91
 
93
- `authorize` is per-operation policy, checked before any read, write, or
94
- subscription. Authentication (who is asking) stays in your route;
95
- `authorize` decides whether this operation is allowed.
92
+ A call cleared before its answer gets `410` with
93
+ `{ code: 'SESSION_CLEARED', error }`, which the actor client raises as
94
+ an `ActorRequestError` with status `410` and the A2 error as its
95
+ `cause`. The handler may already have run, so treat its effects as
96
+ unknown, as after a timeout. A later message reusing the id can't
97
+ answer the cleared call.
98
+
99
+ ## Authorize
96
100
 
97
101
  ```ts
98
102
  // app/vault/[vaultId]/route.ts, now with per-operation policy:
@@ -110,19 +114,17 @@ export async function POST(
110
114
  }
111
115
  ```
112
116
 
113
- The operation is a discriminated union: `{ type: 'state', id }` for
114
- snapshot reads, `{ type: 'stream', id, startAfter }` for subscriptions,
115
- `{ type: 'call', id, event, input,
116
- messageId? }` for calls. Returning `false` answers `403`.
117
+ Authentication stays in your route; `authorize` decides whether each
118
+ operation is allowed, before any read, write, or subscription.
119
+ Returning `false` answers `403`. The operation is one of:
117
120
 
118
- ## Views: what each mount shows
121
+ - `{ type: 'state', id }` for snapshot reads
122
+ - `{ type: 'stream', id, startAfter }` for subscriptions
123
+ - `{ type: 'call', id, event, input, messageId? }` for calls
124
+ - `{ type: 'presence', id, participant, values }` for
125
+ [presence](#presence) announcements
119
126
 
120
- State can be mixed-audience: internal bookkeeping next to public
121
- fields. The definition stays audience-blind; audiences are a route
122
- concern, like auth. A `view` is a request-time projection applied to
123
- everything that mount serves: snapshots, every state frame on the stream,
124
- and every call answer (they must agree, or the call lane would leak
125
- what the stream hides).
127
+ ## Views: what each mount shows
126
128
 
127
129
  ```ts
128
130
  // app/vault/[vaultId]/public/route.ts, a projected mount of the same actor:
@@ -139,21 +141,23 @@ export async function GET(
139
141
  }
140
142
  ```
141
143
 
142
- Different routes project the same actor differently: an admin mount
143
- with no view sees everything, the public mount above hides `audit`.
144
- Per-viewer views need no extra machinery, because the route already
145
- holds the identity; a closure does it
146
- (`view: (state) => ({ ...visible, mine: state.perUser[user.id] })`).
147
- View output holds to the same plain-JSON floor as state itself.
144
+ A `view` projects everything its mount serves: snapshots, stream
145
+ frames, and call answers alike, so calls never leak what the stream
146
+ hides. Audiences are a route concern, like auth: an admin mount with no
147
+ view sees everything, and the mount above hides `audit`. For a
148
+ per-viewer view, close over your route's identity:
149
+ `view: (state) => ({ ...visible, mine: state.perUser[user.id] })`. View
150
+ output must be plain JSON, like state.
148
151
 
149
- Views are for internal-but-not-secret fields. Secrets do not belong in
150
- state at all: state commits are log rows, and the log is forever. Keep
151
- secrets outside, pass references, resolve them inside handlers.
152
+ Clients of a projected mount pass its shape as the second type argument
153
+ (`createActorClient<typeof vault, PublicVault>` or
154
+ `useActor<typeof vault, PublicVault>`).
152
155
 
153
- ## Any client
156
+ Views hide internal fields, not secrets. State commits are log rows,
157
+ and the log is forever: keep secrets outside, pass references, and
158
+ resolve them inside handlers.
154
159
 
155
- `createActorClient` is the typed call surface over that route's `POST`
156
- lane. It works anywhere: browsers, other frameworks, other servers.
160
+ ## Any client
157
161
 
158
162
  ```ts
159
163
  // in the browser, or any client at all:
@@ -175,26 +179,20 @@ try {
175
179
  }
176
180
  ```
177
181
 
178
- The import of `vault` is type-only, so it is erased at build time: the
179
- server module never enters a client bundle. On a view-projected mount,
180
- pass the projected shape as the second type argument
181
- (`createActorClient<typeof vault, PublicVault>`) so answers carry it.
182
-
183
- `client.state()` reads a fresh snapshot. The optional `onSnapshot`
184
- callback receives successful state reads and call answers before their
185
- promises resolve. It receives each answer, including older results from
186
- idempotent retries; consumers compare indexes before replacing live state.
182
+ `createActorClient` is the typed call surface over the route's `POST`,
183
+ for browsers, other frameworks, and other servers. The `vault` import
184
+ is type-only, so the server module never enters a client bundle. `client.state()` reads a fresh snapshot. The optional
185
+ `onSnapshot` callback receives successful reads and call answers before
186
+ their promises resolve, including older results from idempotent
187
+ retries, so compare indexes before replacing live state.
187
188
 
188
- `ActorRequestError` carries an HTTP `status` when available. Network
189
- failures, timeouts, and server errors do not establish whether a call
190
- committed. Retry with the same explicit `id` and input to recover its
191
- original answer. A client does not retry calls automatically.
189
+ `ActorRequestError` carries an HTTP `status` when available. A network
190
+ failure, timeout, or server error doesn't establish whether a call
191
+ committed: retry with the same explicit `id` and input to recover its
192
+ original answer. The client never retries calls automatically.
192
193
 
193
194
  ## React
194
195
 
195
- One hook. The server component hands down the first fold, the hook
196
- keeps it live and exposes the same typed calls.
197
-
198
196
  ```tsx app/vault/[vaultId]/page.tsx
199
197
  import { vault } from '@/vault'
200
198
  import { VaultClient } from './vault-client'
@@ -243,56 +241,34 @@ export function VaultClient({
243
241
  }
244
242
  ```
245
243
 
246
- What the hook gives you:
247
-
248
- - **`state`**: the live view. Call answers and stream commits update it;
249
- the fold is library code, so no reducers, schemas, or contracts
250
- appear in browser code.
251
- - **`call`**: the same typed surface as `createActorClient`, because it
252
- is one; `useActor` is a thin React wrapper over the client primitive
253
- and the stream.
254
- - **`index`**: the latest adopted state index, and **`events`**: the
255
- state-commit feed observed through the stream, starting after the initial
256
- snapshot. Call answers and refreshes can advance state before the feed
257
- catches up; delayed commits still enter the feed once.
258
- - **`refresh()`**: reads a fresh snapshot and adopts it before resolving.
259
- Use it after a refusal or when returning to a suspended browser tab.
260
- - **`connection`**: the same discriminated union as
261
- [`useSession`](/guides/react#the-client-component), heartbeat
262
- watchdog included.
263
-
264
- Live, not optimistic. Handlers are server code deciding against
265
- serialized fresh state, so there is nothing correct for the browser to
266
- apply early; a local guess would be wrong exactly when the actor
267
- matters. If a control needs pending UI, `await call.deposit(...)` is a
268
- promise like any other.
269
-
270
- Successful calls update the shared client state before their promises
271
- resolve. Snapshots only advance state: an older call answer or delayed
272
- stream frame never replaces a newer state. React renders that update on
273
- its normal schedule. A call still returns its own answer, even if a newer
274
- state has already arrived. Refusals do not change state or trigger an
275
- automatic refresh.
276
-
277
- The stream tracks its own position for deduplication and reconnects.
278
- Adopting a newer snapshot does not skip stream events: delayed commits
279
- enter `events` without replacing newer state. Events from before the
280
- initial snapshot are not loaded into this feed.
281
-
282
- On a view-projected mount, pass the projected shape as the second type
283
- argument (`useActor<typeof vault, PublicVault>`); `initial`, `state`,
284
- and the call answers all carry it.
244
+ The server component hands down the first snapshot; `useActor` keeps it
245
+ live and returns:
246
+
247
+ - **`state`**: the live view (no reducers, schemas, or contracts in
248
+ browser code).
249
+ - **`call`**: the same typed calls as `createActorClient`, which the
250
+ hook wraps.
251
+ - **`index`**: the latest adopted state index.
252
+ - **`events`**: the state commits the stream observed after the initial
253
+ snapshot. A delayed commit still enters once, even when a call answer
254
+ or refresh already advanced `state`.
255
+ - **`refresh()`**: reads and adopts a fresh snapshot before resolving.
256
+ Use it after a refusal or when a suspended tab returns.
257
+ - **`connection`**: the same union as
258
+ [`useSession`](/client/react#events-and-history), heartbeat watchdog
259
+ included.
260
+
261
+ State is live, not optimistic: handlers decide on the server against
262
+ fresh state. For pending UI, await the call.
263
+
264
+ A successful call updates `state` before its promise resolves (React
265
+ renders on its normal schedule) and returns its own answer, even if
266
+ newer state already arrived. State only advances: an older answer or
267
+ delayed stream frame never replaces newer state. Refusals don't change
268
+ state or trigger a refresh.
285
269
 
286
270
  ## Presence
287
271
 
288
- Who is here, live: cursors, names, viewer counts. Presence is
289
- ephemeral audience state. It replicates to the instance's subscribers
290
- and expires; it never enters the log, and handlers cannot see it (an
291
- actor decides from its log, never from who is watching).
292
-
293
- Declare the vocabulary in the protocol as types, and arm the wire with
294
- `presence: true`:
295
-
296
272
  ```ts board.ts
297
273
  import { actor } from 'experimental-a2/actor'
298
274
 
@@ -314,10 +290,12 @@ export const board = actor<Board>({
314
290
  })
315
291
  ```
316
292
 
317
- The route does not change: the same `GET` stream interleaves presence
318
- updates between state commits, and the same `POST` lane accepts
319
- presence announcements. In React, pass a `participant` identity and
320
- use the map:
293
+ Declare [presence](/client/presence) fields as types in the protocol
294
+ and arm the wire with `presence: true`. Presence replicates to the
295
+ instance's subscribers and expires; it never enters the log, and
296
+ handlers can't see it. The route doesn't change: `GET` interleaves
297
+ presence with state commits, and `POST` accepts announcements. In
298
+ React, pass a `participant` identity and read the map:
321
299
 
322
300
  ```tsx app/board/[boardId]/board-client.tsx
323
301
  'use client'
@@ -353,32 +331,27 @@ export function BoardClient({
353
331
  }
354
332
  ```
355
333
 
356
- `setPresence` is fire-and-forget: throttled on the way out, resent
357
- after a reconnect, `null` clears a field. The map holds each
358
- participant's latest fields with their stamps; how long a value stays
359
- painted is render logic, decided against `at`.
334
+ `setPresence` is fire-and-forget: throttled, resent after a reconnect,
335
+ and `null` clears a field. The map holds each participant's latest
336
+ fields with their stamps; decide how long to paint a value from `at`.
360
337
 
361
- Three guards stand where presence enters, and none of them is a
362
- schema:
338
+ Three guards, none of them a schema:
363
339
 
364
340
  - **Types**: the protocol types `setPresence` and the map, so honest
365
- clients cannot send the wrong shape.
366
- - **The floor**: the library rejects values that are not plain JSON
367
- trees, values over 8 KB, and object-plumbing field names.
368
- - **`authorize`**: every announcement arrives as
369
- `{ type: 'presence', id, participant, values }` before it is
370
- accepted. Clamp, verify the participant matches the authenticated
371
- user, or rate-limit here.
341
+ clients can't send the wrong shape.
342
+ - **The floor**: A2 rejects values that aren't plain JSON trees, values
343
+ over 8 KB, and object-plumbing field names.
344
+ - **[`authorize`](#authorize)**: every announcement arrives as a
345
+ `presence` operation before it is accepted. Clamp, verify the
346
+ participant matches the authenticated user, or rate-limit here.
372
347
 
373
- What no guard does is make peer values trustworthy. Presence is
374
- authored by whoever holds a connection: render it like user input.
348
+ No guard makes peer values trustworthy: presence is authored by whoever
349
+ holds a connection, so render it like user input.
375
350
 
376
351
  ## The session wire underneath
377
352
 
378
- `handle.fetch` serves the actor's two lanes and nothing else. When you
379
- want the raw session protocol instead (the full event feed, pushes,
380
- presence, the `ws` transport), the definition exposes the underlying
381
- server: mount `def.server.fetch` and use the core
382
- [client](/guides/react) against it. That wire shows the whole mailbox
383
- to its subscribers, inputs included, so reserve it for trusted
384
- audiences.
353
+ For the raw session protocol (the full event feed, pushes, presence,
354
+ the `ws` transport), mount `def.server.fetch` as a
355
+ [session route](/server/routes) and use the core
356
+ [client](/client/react). That wire shows the whole mailbox, inputs
357
+ included, so reserve it for trusted audiences.
@@ -1 +1 @@
1
- export default { order: 4 }
1
+ export default { order: 6 }