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
@@ -0,0 +1,54 @@
1
+ import type { ReactElement } from 'react'
2
+ import { beforeEach, expect, it, vi } from 'vitest'
3
+ import AgentPage from './agent/[agentId]/page'
4
+ import ChatPage from './chat/[chatId]/page'
5
+
6
+ const agentReads = vi.hoisted(() => ({
7
+ initial: { state: { agent: true }, index: 7 },
8
+ state: vi.fn<() => Promise<{ state: { agent: boolean }; index: number }>>(),
9
+ history: vi.fn<() => never>(),
10
+ }))
11
+ const reads = vi.hoisted(() => ({
12
+ initial: { state: { marker: true }, index: 42 },
13
+ state: vi.fn<() => Promise<{ state: { marker: boolean }; index: number }>>(),
14
+ history: vi.fn<() => never>(),
15
+ }))
16
+ vi.mock('./agent/server', () => ({ agentServer: { session: () => reads } }))
17
+ vi.mock('./chat/server', () => ({
18
+ chatServer: { session: () => reads },
19
+ chatAgentServer: { session: () => agentReads },
20
+ }))
21
+ vi.mock('./agent/[agentId]/agent-client', () => ({ AgentClient: () => null }))
22
+ vi.mock('./chat/[chatId]/chat-client', () => ({ ChatClient: () => null }))
23
+
24
+ beforeEach(() => {
25
+ reads.state.mockReset().mockResolvedValue(reads.initial)
26
+ agentReads.state.mockReset().mockResolvedValue(agentReads.initial)
27
+ reads.history.mockReset().mockImplementation(() => {
28
+ throw new Error('unexpected history read')
29
+ })
30
+ })
31
+
32
+ it.each(['agent', 'chat'] as const)(
33
+ 'seeds %s from one state read without fetching raw history',
34
+ async (page) => {
35
+ const element = (await (page === 'agent'
36
+ ? AgentPage({ params: Promise.resolve({ agentId: 'one' }) })
37
+ : ChatPage({
38
+ params: Promise.resolve({ chatId: 'one' }),
39
+ }))) as ReactElement<{ initial: unknown; initialEvents?: unknown }>
40
+ expect(reads.state).toHaveBeenCalledExactlyOnceWith()
41
+ expect(reads.history).not.toHaveBeenCalled()
42
+ expect(element.props.initial).toBe(reads.initial)
43
+ expect(element.props.initialEvents).toBeUndefined()
44
+ },
45
+ )
46
+
47
+ it("seeds the chat's agent session from its own state read", async () => {
48
+ const element = (await ChatPage({
49
+ params: Promise.resolve({ chatId: 'one' }),
50
+ })) as ReactElement<{ children: ReactElement<{ agentInitial: unknown }> }>
51
+ expect(agentReads.state).toHaveBeenCalledExactlyOnceWith()
52
+ expect(agentReads.history).not.toHaveBeenCalled()
53
+ expect(element.props.children.props.agentInitial).toBe(agentReads.initial)
54
+ })
@@ -1,6 +1,7 @@
1
- import type { Metadata } from 'next'
1
+ import type { Metadata, Viewport } from 'next'
2
2
  import type { ReactNode } from 'react'
3
- import Link from 'next/link'
3
+ import { GeistMono } from 'geist/font/mono'
4
+ import { SiteHeader } from './components/site-header'
4
5
  import './globals.css'
5
6
 
6
7
  export const metadata: Metadata = {
@@ -9,35 +10,18 @@ export const metadata: Metadata = {
9
10
  'A durable event log for serverless apps — counters, agents, orders, streaming, cancellation, multiplayer documents, and a shared canvas.',
10
11
  }
11
12
 
13
+ export const viewport: Viewport = { colorScheme: 'light dark' }
14
+
12
15
  export default function RootLayout({
13
16
  children,
14
17
  }: {
15
18
  children: ReactNode
16
19
  }): ReactNode {
17
20
  return (
18
- <html lang="en">
21
+ <html lang="en" className={GeistMono.variable}>
19
22
  <body>
20
- <header className="site-header">
21
- <nav>
22
- <Link href="/" className="brand">
23
- a2 <span>example</span>
24
- </Link>
25
- <Link href="/orders">Orders</Link>
26
- <Link href="/chat">Chat</Link>
27
- <Link href="/agent">Agent</Link>
28
- <Link href="/canvas">Canvas</Link>
29
- <Link href="/documents">Documents</Link>
30
- <Link href="/recovery">Recovery</Link>
31
- <Link href="/counter">Counter</Link>
32
- </nav>
33
- </header>
23
+ <SiteHeader />
34
24
  <main>{children}</main>
35
- <footer className="site-footer">
36
- <p>
37
- Everything on these pages is a fold over an append-only log — check{' '}
38
- <code>.a2/dev.db</code>.
39
- </p>
40
- </footer>
41
25
  </body>
42
26
  </html>
43
27
  )
@@ -20,8 +20,7 @@ export default async function OrderPage({
20
20
  <SessionProvider
21
21
  sessionId={decodeURIComponent(orderId)}
22
22
  api="/orders/events"
23
- initialState={state}
24
- initialIndex={index}
23
+ initial={{ state: state, index: index }}
25
24
  >
26
25
  <OrderClient sessionId={decodeURIComponent(orderId)} />
27
26
  </SessionProvider>
@@ -4,8 +4,8 @@
4
4
  * pair. Nothing in this file touches the machine module.
5
5
  */
6
6
  import { createReact } from 'experimental-a2/react'
7
- import { ordersReducer } from '../model'
7
+ import { orders } from '../model'
8
8
 
9
9
  export const { SessionProvider, useSession } = createReact({
10
- reducer: ordersReducer,
10
+ contract: orders,
11
11
  })
@@ -1,21 +1,6 @@
1
1
  import * as a2 from 'experimental-a2'
2
2
  import { z } from 'zod'
3
3
 
4
- export const orders = a2.contract({
5
- name: 'orders',
6
- events: {
7
- created: z.object({
8
- shopId: z.string(),
9
- items: z.array(z.string()).min(1),
10
- }),
11
- 'shop.notified': z.object({}),
12
- 'shop.started': z.object({}),
13
- 'driver.notified': z.object({ driverId: z.string() }),
14
- 'courier.picked_up': z.object({ etaMinutes: z.number().int().positive() }),
15
- delivered: z.object({}),
16
- },
17
- })
18
-
19
4
  const orderState = z.object({
20
5
  status: z.enum([
21
6
  'new',
@@ -34,8 +19,21 @@ const orderState = z.object({
34
19
 
35
20
  export type OrderState = z.output<typeof orderState>
36
21
 
37
- export const ordersReducer = orders
38
- .reducer({
22
+ export const orders = a2.contract({
23
+ name: 'orders',
24
+ events: {
25
+ created: z.object({
26
+ shopId: z.string(),
27
+ items: z.array(z.string()).min(1),
28
+ }),
29
+ 'shop.notified': z.object({}),
30
+ 'shop.started': z.object({}),
31
+ 'driver.notified': z.object({ driverId: z.string() }),
32
+ 'courier.picked_up': z.object({ etaMinutes: z.number().int().positive() }),
33
+ delivered: z.object({}),
34
+ },
35
+
36
+ reducer: {
39
37
  name: 'order-status',
40
38
  initialState: {
41
39
  status: 'new',
@@ -45,35 +43,37 @@ export const ordersReducer = orders
45
43
  etaMinutes: null,
46
44
  },
47
45
  stateSchema: orderState,
48
- })
49
- .fold((state, event) => {
50
- switch (event.type) {
51
- case 'created':
52
- return {
53
- ...state,
54
- status: 'created',
55
- shopId: event.payload.shopId,
56
- items: event.payload.items,
57
- }
58
- case 'shop.notified':
59
- return { ...state, status: 'notified' }
60
- case 'shop.started':
61
- return { ...state, status: 'preparing' }
62
- case 'driver.notified':
63
- return {
64
- ...state,
65
- status: 'courier-assigned',
66
- driverId: event.payload.driverId,
67
- }
68
- case 'courier.picked_up':
69
- return {
70
- ...state,
71
- status: 'delivering',
72
- etaMinutes: event.payload.etaMinutes,
73
- }
74
- case 'delivered':
75
- return { ...state, status: 'delivered', etaMinutes: null }
76
- default:
77
- return state
78
- }
79
- })
46
+ fold: (state, event): OrderState => {
47
+ switch (event.type) {
48
+ case 'created':
49
+ return {
50
+ ...state,
51
+ status: 'created',
52
+ shopId: event.payload.shopId,
53
+ items: event.payload.items,
54
+ }
55
+ case 'shop.notified':
56
+ return { ...state, status: 'notified' }
57
+ case 'shop.started':
58
+ return { ...state, status: 'preparing' }
59
+ case 'driver.notified':
60
+ return {
61
+ ...state,
62
+ status: 'courier-assigned',
63
+ driverId: event.payload.driverId,
64
+ }
65
+ case 'courier.picked_up':
66
+ return {
67
+ ...state,
68
+ status: 'delivering',
69
+ etaMinutes: event.payload.etaMinutes,
70
+ }
71
+ case 'delivered':
72
+ return { ...state, status: 'delivered', etaMinutes: null }
73
+ default:
74
+ return state
75
+ }
76
+ },
77
+ },
78
+ })
79
+ export const ordersReducer = orders.reducer
@@ -1,111 +1,22 @@
1
1
  import type { ReactNode } from 'react'
2
- import Link from 'next/link'
2
+ import { FastLink } from './components/fast-link'
3
+ import { demos } from './components/demos'
3
4
 
4
5
  export default function HomePage(): ReactNode {
5
6
  return (
6
7
  <article>
7
- <h1>A durable event log, live in your browser</h1>
8
- <p className="lede">
9
- Eight small demos, one model: append an event, let a handler react, fold
10
- the log into a view — on the server for the first paint, in the browser
11
- for every moment after.
12
- </p>
13
-
14
- <div className="card-grid">
15
- <Link className="card" href="/orders">
16
- <h2>Order tracker</h2>
17
- <p>
18
- A form appends <code>created</code>, and handlers chain the rest —
19
- shop notified, courier assigned, picked up, delivered — while the
20
- page watches over a live stream. One optimistic click, then the
21
- order runs itself to the end.
22
- </p>
23
- </Link>
24
- <Link className="card" href="/chat">
25
- <h2>Multiplayer chat + agent</h2>
26
- <p>
27
- Messages, “is typing…” presence, and an AI participant share one
28
- stream. Mention <code>@agent</code>, or enable ambient mode for the
29
- room. The model can stay silent or call a durable <code>post</code>{' '}
30
- tool; its private generation appears only as “thinking” or
31
- “writing.”
32
- </p>
33
- </Link>
34
- <Link className="card" href="/agent">
35
- <h2>Agent</h2>
36
- <p>
37
- A durable AI agent inspects a service, records tool calls, pauses
38
- for approval, runs Bash in a persistent Vercel Sandbox, and sets
39
- reminders that return as new user messages. Messages, progress,
40
- tools, timers, and the human decision are all ordinary A2 events.
41
- </p>
42
- </Link>
43
- <Link className="card" href="/canvas">
44
- <h2>Multiplayer canvas</h2>
45
- <p>
46
- Shapes are durable events you can scrub back through after the
47
- server snapshot. Cursors and drag ghosts are presence: replicated
48
- live, stored never.
49
- </p>
50
- </Link>
51
- <Link className="card" href="/documents">
52
- <h2>Multiplayer document</h2>
53
- <p>
54
- Two CodeMirror editors share one Yjs document. A2 durably orders the
55
- CRDT updates while Yjs Awareness — live cursors and selections —
56
- rides the session's presence plane, never touching the log.
57
- </p>
58
- </Link>
59
- <Link className="card" href="/recovery">
60
- <h2>Cross-invocation recovery</h2>
61
- <p>
62
- Chain four 8-second handlers under a 20-second function limit, then
63
- watch Vercel stop one mid-handler and A2 retry that pending event
64
- under a fresh claim.
65
- </p>
66
- </Link>
67
- <Link className="card" href="/vault">
68
- <h2>Vault (actor)</h2>
69
- <p>
70
- A durable actor: send messages to one vault's mailbox, watch it
71
- process them one at a time against its state — serialized
72
- read-modify-write, refusals as answers, and the whole transcript in
73
- the log. Built on <code>experimental-a2/actor</code>.
74
- </p>
75
- </Link>
76
- <Link className="card" href="/counter">
77
- <h2>Counter</h2>
78
- <p>
79
- Increment and decrement one shared value. Every click is a durable
80
- event applied optimistically in the browser, then confirmed by the
81
- stream — open two tabs and watch both folds converge.
82
- </p>
83
- </Link>
84
- </div>
85
-
86
- <section>
87
- <h2>Where things live</h2>
88
- <ul>
89
- <li>
90
- Each demo owns a <code>model.ts</code> with its contract, reducer,
91
- and shared types.
92
- </li>
93
- <li>
94
- Its <code>server.ts</code> implements handlers and configures the
95
- production store and scheduler it actually needs.
96
- </li>
97
- <li>
98
- Its <code>events/route.ts</code> exposes <code>server.fetch</code>{' '}
99
- directly, adding only feature-specific authorization or WebSocket
100
- capabilities.
101
- </li>
102
- <li>
103
- Demos with handlers or timers own a private{' '}
104
- <code>scheduler/route.ts</code> and queue topic. Counter, canvas,
105
- and documents need no scheduler.
8
+ <h1>A durable event log</h1>
9
+ <p className="lede">Every demo here is a fold over an append-only log.</p>
10
+ <ul className="app-grid" aria-label="Demos">
11
+ {demos.map((demo) => (
12
+ <li key={demo.href}>
13
+ <FastLink href={demo.href} className="app-tile">
14
+ <span className="app-icon">{demo.icon}</span>
15
+ <span>{demo.name}</span>
16
+ </FastLink>
106
17
  </li>
107
- </ul>
108
- </section>
18
+ ))}
19
+ </ul>
109
20
  </article>
110
21
  )
111
22
  }
@@ -19,8 +19,7 @@ export default async function RecoveryRunPage({
19
19
  <SessionProvider
20
20
  sessionId={sessionId}
21
21
  api="/recovery/events"
22
- initialState={state}
23
- initialIndex={index}
22
+ initial={{ state: state, index: index }}
24
23
  >
25
24
  <RecoveryClient
26
25
  sessionId={sessionId}
@@ -1,7 +1,7 @@
1
1
  'use client'
2
2
  import { createReact } from 'experimental-a2/react'
3
- import { recoveryReducer } from '../model'
3
+ import { recovery } from '../model'
4
4
 
5
5
  export const { SessionProvider, useSession } = createReact({
6
- reducer: recoveryReducer,
6
+ contract: recovery,
7
7
  })
@@ -1,6 +1,15 @@
1
1
  import * as a2 from 'experimental-a2'
2
2
  import { z } from 'zod'
3
3
 
4
+ const recoveryState = z.object({
5
+ phase: z.enum(['idle', 'running', 'completed']),
6
+ stepCount: z.number().int().positive().nullable(),
7
+ stepDurationSeconds: z.number().int().positive().nullable(),
8
+ completedSteps: z.number().int().nonnegative(),
9
+ })
10
+
11
+ export type RecoveryState = z.output<typeof recoveryState>
12
+
4
13
  export const recovery = a2.contract({
5
14
  name: 'recovery',
6
15
  events: {
@@ -14,19 +23,8 @@ export const recovery = a2.contract({
14
23
  }),
15
24
  'pipeline.completed': z.object({ stepCount: z.literal(4) }),
16
25
  },
17
- })
18
-
19
- const recoveryState = z.object({
20
- phase: z.enum(['idle', 'running', 'completed']),
21
- stepCount: z.number().int().positive().nullable(),
22
- stepDurationSeconds: z.number().int().positive().nullable(),
23
- completedSteps: z.number().int().nonnegative(),
24
- })
25
26
 
26
- export type RecoveryState = z.output<typeof recoveryState>
27
-
28
- export const recoveryReducer = recovery
29
- .reducer({
27
+ reducer: {
30
28
  name: 'recovery',
31
29
  initialState: {
32
30
  phase: 'idle',
@@ -35,21 +33,23 @@ export const recoveryReducer = recovery
35
33
  completedSteps: 0,
36
34
  },
37
35
  stateSchema: recoveryState,
38
- })
39
- .fold((state, event) => {
40
- switch (event.type) {
41
- case 'pipeline.started':
42
- return {
43
- ...state,
44
- phase: 'running',
45
- stepCount: event.payload.stepCount,
46
- stepDurationSeconds: event.payload.stepDurationSeconds,
47
- }
48
- case 'step.completed':
49
- return { ...state, completedSteps: event.payload.step }
50
- case 'pipeline.completed':
51
- return { ...state, phase: 'completed' }
52
- default:
53
- return state
54
- }
55
- })
36
+ fold: (state, event): RecoveryState => {
37
+ switch (event.type) {
38
+ case 'pipeline.started':
39
+ return {
40
+ ...state,
41
+ phase: 'running',
42
+ stepCount: event.payload.stepCount,
43
+ stepDurationSeconds: event.payload.stepDurationSeconds,
44
+ }
45
+ case 'step.completed':
46
+ return { ...state, completedSteps: event.payload.step }
47
+ case 'pipeline.completed':
48
+ return { ...state, phase: 'completed' }
49
+ default:
50
+ return state
51
+ }
52
+ },
53
+ },
54
+ })
55
+ export const recoveryReducer = recovery.reducer
@@ -0,0 +1,5 @@
1
+ import { registerOTel } from '@vercel/otel'
2
+
3
+ export function register(): void {
4
+ registerOTel({ serviceName: 'a2-playground' })
5
+ }
@@ -1,10 +1,22 @@
1
- // No workarounds needed: experimental-a2/server is a separate server-only entry
2
- // point, so client bundles never reach a store backend; the sqlite dev
3
- // default carries its own turbopackIgnore annotation in a2's dist, so
4
- // output tracing stays scoped without externalizing anything; and
1
+ // experimental-a2/server is a separate server-only entry point, so client
2
+ // bundles never reach a store backend; the sqlite dev default carries its own
3
+ // turbopackIgnore annotation in a2's dist, so output tracing stays scoped; and
5
4
  // Next 16 transpiles this file with the TS7 (tsgo) compiler.
6
5
  import type { NextConfig } from 'next'
7
6
 
8
- const nextConfig: NextConfig = {}
7
+ const nextConfig: NextConfig = {
8
+ // libfx loads a native binary for the platform, falling back to its
9
+ // WebAssembly, by paths the bundler cannot follow; Node loads both from
10
+ // node_modules at runtime.
11
+ serverExternalPackages: ['libfx'],
12
+ turbopack: {
13
+ // WebAssembly Pi's code reaches that the bundler cannot trace.
14
+ resolveAlias: {
15
+ '@silvia-odwyer/photon-node': './app/a0/no-photon.cjs',
16
+ 'quickjs-wasi': './app/a0/no-quickjs.cjs',
17
+ 'quickjs-wasi/quickjs.wasm': './app/a0/no-quickjs.cjs',
18
+ },
19
+ },
20
+ }
9
21
 
10
22
  export default nextConfig
@@ -12,18 +12,27 @@
12
12
  "dev": "next dev",
13
13
  "build": "next build",
14
14
  "test": "vitest run",
15
- "typecheck": "tsc --noEmit"
15
+ "typecheck": "tsc --noEmit",
16
+ "a0:template": "node --env-file-if-exists=.env.local scripts/a0-template.ts"
16
17
  },
17
18
  "dependencies": {
19
+ "@ai-sdk/otel": "1.0.58",
18
20
  "@codemirror/state": "^6.7.1",
19
21
  "@codemirror/view": "^6.43.8",
22
+ "@earendil-works/pi-ai": "1.0.3",
23
+ "@earendil-works/pi-coding-agent": "1.0.2",
24
+ "@opentelemetry/api": "^1.9.0",
20
25
  "@vercel/functions": "^3.9.3",
26
+ "@vercel/otel": "^2.1.0",
21
27
  "@vercel/queue": "^0.2.0",
22
28
  "@vercel/sandbox": "^3.0.0",
23
29
  "ai": "^7.0.58",
24
30
  "codemirror": "^6.0.2",
25
- "experimental-a2": "0.16.1",
31
+ "experimental-a2": "0.18.0",
32
+ "geist": "^1.7.2",
26
33
  "ioredis": "^5.9.0",
34
+ "libfx": "0.0.12",
35
+ "lucide-react": "^1.34.0",
27
36
  "next": "^16.3.0",
28
37
  "pg": "^8.16.0",
29
38
  "react": "^19.0.0",
@@ -36,6 +45,8 @@
36
45
  "zod": "^4.4.3"
37
46
  },
38
47
  "devDependencies": {
48
+ "@opentelemetry/context-async-hooks": "^2.0.0",
49
+ "@opentelemetry/sdk-trace-base": "^2.0.0",
39
50
  "@testing-library/react": "^16.1.0",
40
51
  "jsdom": "^26.0.0",
41
52
  "@types/node": "^24.0.0",
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Builds A0's template: a sandbox with a blank Next.js app, its
3
+ * dependencies installed and its dev cache warm, snapshotted with no
4
+ * expiry. Prints the snapshot id to set as A0_TEMPLATE_SNAPSHOT_ID.
5
+ *
6
+ * pnpm --filter playground a0:template
7
+ *
8
+ * Run it again when the recipe changes, then update the variable.
9
+ */
10
+ import { Sandbox } from '@vercel/sandbox'
11
+
12
+ const appDir = '/vercel/app'
13
+ const appPort = 3000
14
+
15
+ const RECIPE = `set -e
16
+ cd /vercel
17
+ npx --yes create-next-app@latest app --yes --use-npm --disable-git
18
+ cd ${appDir}
19
+ rm -f public/*.svg
20
+ cat > next.config.ts <<'EOF'
21
+ import type { NextConfig } from 'next'
22
+
23
+ // The preview is served from the sandbox's public *.vercel.run host.
24
+ const nextConfig: NextConfig = { allowedDevOrigins: ['*.vercel.run'] }
25
+
26
+ export default nextConfig
27
+ EOF
28
+ cat > app/globals.css <<'EOF'
29
+ @import 'tailwindcss';
30
+ EOF
31
+ cat > app/layout.tsx <<'EOF'
32
+ import type { Metadata } from 'next'
33
+ import './globals.css'
34
+
35
+ export const metadata: Metadata = { title: 'App' }
36
+
37
+ export default function RootLayout({
38
+ children,
39
+ }: Readonly<{ children: React.ReactNode }>) {
40
+ return (
41
+ <html lang="en">
42
+ <body>{children}</body>
43
+ </html>
44
+ )
45
+ }
46
+ EOF
47
+ cat > app/page.tsx <<'EOF'
48
+ export default function Home() {
49
+ return <main />
50
+ }
51
+ EOF
52
+ # Warms the dev cache: next dev compiles the page once, then stops with the
53
+ # snapshot. setsid -f keeps the command from waiting on the server.
54
+ setsid -f npm run dev -- --port ${appPort} > /tmp/next.log 2>&1 < /dev/null
55
+ timeout 120 bash -c 'until curl -sf localhost:${appPort} > /dev/null; do sleep 1; done'`
56
+
57
+ // The snapshot outlives its builder, so nothing but the snapshot stays.
58
+ const builder = await Sandbox.create({
59
+ persistent: false,
60
+ timeout: 10 * 60 * 1000,
61
+ })
62
+ try {
63
+ const result = await builder.runCommand({
64
+ cmd: 'bash',
65
+ args: ['-lc', RECIPE],
66
+ })
67
+ if (result.exitCode !== 0) {
68
+ const output = await result.output('both')
69
+ throw new Error(
70
+ `The template didn't build (exit ${result.exitCode}):\n${output.slice(-2000)}`,
71
+ )
72
+ }
73
+ const { snapshotId } = await builder.snapshot({ expiration: 0 })
74
+ process.stdout.write(`A0_TEMPLATE_SNAPSHOT_ID=${snapshotId}\n`)
75
+ } finally {
76
+ await builder.delete().catch(() => {})
77
+ }
@@ -35,6 +35,42 @@
35
35
  "retryAfterSeconds": 30
36
36
  }
37
37
  ]
38
+ },
39
+ "app/a0/scheduler/route.ts": {
40
+ "experimentalTriggers": [
41
+ {
42
+ "type": "queue/v2beta",
43
+ "topic": "a2-a0",
44
+ "retryAfterSeconds": 30
45
+ }
46
+ ]
47
+ },
48
+ "app/a0/fx/scheduler/route.ts": {
49
+ "experimentalTriggers": [
50
+ {
51
+ "type": "queue/v2beta",
52
+ "topic": "a2-a0-fx",
53
+ "retryAfterSeconds": 30
54
+ }
55
+ ]
56
+ },
57
+ "app/a0/pi/scheduler/route.ts": {
58
+ "experimentalTriggers": [
59
+ {
60
+ "type": "queue/v2beta",
61
+ "topic": "a2-a0-pi",
62
+ "retryAfterSeconds": 30
63
+ }
64
+ ]
65
+ },
66
+ "app/a0/sandboxes/scheduler/route.ts": {
67
+ "experimentalTriggers": [
68
+ {
69
+ "type": "queue/v2beta",
70
+ "topic": "a2-a0-sandboxes",
71
+ "retryAfterSeconds": 30
72
+ }
73
+ ]
38
74
  }
39
75
  }
40
76
  }