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,722 @@
1
+ ---
2
+ title: AI
3
+ description: "`experimental-a2/ai`, `experimental-a2/ai/server`, `experimental-a2/ai/client`, and `experimental-a2/ai/react`."
4
+ ---
5
+
6
+ ## `experimental-a2/ai`
7
+
8
+ The isomorphic AI protocol: event schemas, the `AIState` reducer, pure input
9
+ builders, and `agentContract()` for extending both. Its peer dependency, AI SDK
10
+ v7, is used as types only here. See [Agents](/agents/introduction) for the
11
+ protocol and complete examples.
12
+
13
+ ### `agentContract(options)`
14
+
15
+ ```ts
16
+ agentContract<M extends UIMessage = UIMessage>(options: {
17
+ name: string
18
+ events?: Record<string, StandardSchemaV1>
19
+ messageSchema?: StandardSchemaV1<M>
20
+ reducer?: {
21
+ name: string // snapshot identity: rename it when fold changes
22
+ initialState: X // its keys may not shadow AIState's
23
+ fold: (state: X, event: ContractEvent) => X
24
+ }
25
+ }): Contract<Events, {}, AgentReducer<M, Events, X>>
26
+ ```
27
+
28
+ A core contract with the built-in AI protocol plus `events`; the session's
29
+ state is `AIState<M> & X`. Share it when a client needs application events or
30
+ state: serve it with `agent({ contract })` and use it with
31
+ [`useAgent({ api, id, contract })`](#useagentoptions). Otherwise
32
+ `agent({ name })` builds one on the server.
33
+
34
+ - `events` cannot replace built-in event names.
35
+ - `messageSchema` validates a narrower `UIMessage` in message payloads; without
36
+ it, the type argument types messages.
37
+ - `reducer.fold` sees `X` and every event, never the transcript, and runs after
38
+ the AI fold on the server and in every client, so optimistic display carries
39
+ through.
40
+ - An `initialState` key that `AIState` also has throws a `TypeError` and fails
41
+ to typecheck.
42
+ - The reducer name is `AI_STATE_REDUCER` (`a2.ai.state.v26`) without
43
+ `reducer`, else `${reducer.name}+a2.ai.state.v26`, so checkpoints advance when
44
+ either fold changes. `AgentReducer` types that name (so
45
+ [`createAgentClient()`](#createagentclientoptions) accepts a plain
46
+ `agentContract()`) and carries `X` to `agent()`'s resolvers.
47
+
48
+ See [Add your own events and state](/agents/conversations#add-your-own-events-and-state).
49
+
50
+ ### `TranscriptMessage<M>`
51
+
52
+ ```ts
53
+ type TranscriptMessage<M extends UIMessage> = Omit<M, 'parts'> & {
54
+ parts: Array<M['parts'][number] | CompactionPart>
55
+ }
56
+ ```
57
+
58
+ The message type of `AIState<M>['messages']` and `deriveUIMessages()`: `M` plus
59
+ the [compaction part](#iscompactionpartpart), so `isCompactionPart()` narrows
60
+ even when `M` declares its own data parts.
61
+
62
+ ### `AIMessageOf<Events>`
63
+
64
+ The `UIMessage` type a contract's `ai.message.created` schema carries, such as
65
+ `AIMessageOf<typeof contract.events>`; `UIMessage` otherwise. `agent()` types
66
+ messages with it.
67
+
68
+ ### `handlerContext(contract?)`
69
+
70
+ Returns the current tool execution's durable handler context, an
71
+ `AgentToolContext<D>` with `event`, `attempt`, `session`, and `signal`.
72
+
73
+ - Tools are plain AI SDK `tool()` definitions, passed through uninterpreted;
74
+ each local `execute` runs inside the ambient scope. Call it anywhere below
75
+ `execute`, including awaited helpers and async iteration; elsewhere it throws
76
+ a `TypeError`.
77
+ - Typed for the built-in protocol by default. Pass the shared `agentContract()`
78
+ to type application events; a mismatch with the executing agent throws a
79
+ `TypeError`.
80
+ - `event` is the `ai.tool.execution.requested` work event, whose payload holds
81
+ the admitted `call`, `generation`, `turnId`, and execution `version`.
82
+ `ctx.session.schedule()` uses the executing agent's scheduler.
83
+ - Built per attempt, never persisted, never sent to the model or a
84
+ provider-executed tool; each retry gets a fresh one.
85
+ - Model-phase tool options (`contextSchema`, dynamic descriptions,
86
+ `needsApproval` predicates, input lifecycle callbacks) pass through untouched,
87
+ durable execution passes no AI SDK `context` option, and model-phase hooks see
88
+ tools without local `execute`.
89
+ - `needsApproval` and `generation.toolApproval` predicates are re-evaluated on
90
+ recovery for the same durable call: keep them deterministic over the input.
91
+
92
+ See [Tools and approvals](/agents/tools).
93
+
94
+ ### `inputs`
95
+
96
+ Pure typed builders of `ai.control.requested` commands, for `append()` and
97
+ `push()`. The `useAgent()` actions build the browser ones.
98
+
99
+ | Input | Events |
100
+ | --- | --- |
101
+ | `inputs.message(message, { generate? })` | queues input; `generate: false` records passive context at admission |
102
+ | `inputs.seed(message)` | queues passive context; non-user roles require a trusted append |
103
+ | `inputs.approval(response)` | answers a pending tool approval |
104
+ | `inputs.input(response)` | answers a pending application input |
105
+ | `inputs.requestInput(request)` | requests application input from trusted server code |
106
+ | `inputs.retry(options)` | retries a failed response |
107
+ | `inputs.stop({ turnId, lastSeenIndex? })` | ends the observed turn and advances when admission is open |
108
+ | `inputs.steer({ turnId, message, lastSeenIndex? })` | puts the new input first for a handoff after the observed turn's current step |
109
+ | `inputs.queue.edit({ message })` | changes a pending input; its ID stays fixed |
110
+ | `inputs.queue.move({ inputId, beforeId })` | moves pending input before an ID; `null` means the end |
111
+ | `inputs.queue.remove({ inputId })` | removes pending input; its submission stays in the log |
112
+ | `inputs.queue.sendNow({ inputId, turnId, lastSeenIndex? })` | promotes an existing queued input for a handoff after the current step; preserves pause |
113
+ | `inputs.pause({ when: 'now' \| 'after-turn' })` | pauses execution now or inbox admission after the turn |
114
+ | `inputs.resume()` | resumes retained work, then inbox admission |
115
+ | `inputs.toolResult(result)` | records a deferred provider-executed tool result from trusted server code |
116
+ | `inputs.interrupt(options)` | interrupts a specific generation/request owner |
117
+
118
+ - **Receipts.** A push confirms persistence; `ai.control.decided` confirms
119
+ application with `{ commandId, outcome, reason? }` on the existing stream.
120
+ `outcome` is `applied` or `rejected`; reasons include `not-pending`,
121
+ `role-mismatch`, `stale-turn`, `not-found`, `not-user-input`, and `closed`.
122
+ `state.receipt` holds the latest user receipt.
123
+ - **Trust.** Browser ingress accepts user messages, approval and input
124
+ responses, interruptions, and explicit retries. `inputs.requestInput()`,
125
+ `inputs.toolResult()`, and non-user `inputs.seed()` messages are trusted
126
+ server appends; only built-in handlers append generation requests and AI
127
+ lifecycle events. No builder covers session lifecycle: append
128
+ `ai.session.created` and `ai.session.closed` from trusted server code.
129
+ - **IDs.** Message, seed, and steer commands use the input message ID as their
130
+ event ID, and must. Approval, input-response, and retry builders use stable
131
+ interaction IDs; other commands use the ID `push()` or `append()` assigns,
132
+ kept across transport retries. Caller IDs are nonempty, never start with the
133
+ reserved `a2.ai:`, and are unique across the store; identical events replay
134
+ and conflicting reuse rejects before append. A2's own IDs are opaque,
135
+ fixed-length, deterministic `a2.ai:` values; an assistant message's ID is its
136
+ initial model-request event's ID.
137
+ - **Passive input.** `generate: false` is valid browser input: it admits the
138
+ user message to `AIState.messages` without a turn. The next generating user
139
+ message includes the passive ones before it; later ones wait. `inputs.seed()`
140
+ sets the same flag. Trusted assistant and system seeds are context, not queue
141
+ cutpoints, and never scheduled, whatever `generate` says. Model output is
142
+ generation progress, not `ai.message.created`, so a response never starts a
143
+ turn.
144
+ - **Queue.** Edits are last-write-wins in log order, keep identity and role,
145
+ and need no confirmation between edits; after admission they return
146
+ `not-pending`. Removal keeps the submission in the log, so replay cannot
147
+ recreate it.
148
+ - **Steer and Send now.** Steering prioritizes new input even after the
149
+ observed turn ends; a matching active turn yields after its current model
150
+ response and tool batch settle, including authorized deferred provider
151
+ results (unanswered approval and input waits can close; a denied approval
152
+ never blocks the handoff); otherwise it starts when idle or waits first
153
+ behind a different active turn. Both the reprioritization and the later
154
+ handoff commit atomically. Send now keeps the queued input's ID and content,
155
+ needs an observed `turnId` (`null` when idle), accepts only user-role input,
156
+ and promotes passive input; missing targets and stale turns reject before the
157
+ queue changes. Neither resumes a pause or cuts output at `lastSeenIndex`.
158
+ - **Resume** uses a fresh model request and execution version. Tools without a
159
+ terminal outcome can rerun; key external effects on their stable
160
+ `toolCallId`.
161
+ - **Approvals and input.** Request and response payloads carry the active
162
+ `requestId` and `generationId`, copied from the pending request, so a late
163
+ response cannot satisfy a newer step.
164
+ - **Retry.** `ai.retry.requested` carries `{ messageId, responseMessageId,
165
+ retryId }` (one `retryId` per user action) and becomes an
166
+ `ai.generation.requested` with reason `retry`. It reruns the failed model
167
+ call, keeping earlier completed steps, their tool results, and committed
168
+ compactions, and drops the failed call's uncommitted step. Forks and
169
+ edit-and-resend are application-owned.
170
+ - **Generation events.** `ai.generation.requested` requires
171
+ `responseMessageId`, plus `sourceGenerationId` for a tool continuation.
172
+ `ai.generation.failed` sets `stepLimit: true` when `maxSteps` rejects a
173
+ continuation.
174
+ - **Interruption.** `ai.message.interrupted` is `{ messageId, requestId,
175
+ reason?, lastSeenIndex? }` or `{ messageId, generationId, reason?,
176
+ lastSeenIndex? }`: copy `activeRequestId` before the generation starts,
177
+ `activeGeneration.generationId` after. A request-owned interruption survives
178
+ that request starting first. An older or mismatched message/owner pair is a
179
+ no-op. An accepted one fences its owner for good: later events stay in raw
180
+ history but cannot alter `AIState`, satisfy pending work, or reactivate the
181
+ response. Tool results completed at or before `lastSeenIndex` stay
182
+ completed; a final tool result racing a generation failure wins in either
183
+ order, and the generation stays failed. Interruption and failure drop
184
+ `input-streaming` tool drafts from the projection and later prompts (raw
185
+ progress stays); other tool parts keep their terminal handling.
186
+
187
+ See [Inbox and turn controls](/agents/conversations#inbox-and-turn-controls)
188
+ and [Interrupt and retry](/agents/tools#interrupt-and-retry).
189
+
190
+ ### `reasoningLevels`
191
+
192
+ ```ts
193
+ type ReasoningLevel = 'provider-default' | 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'
194
+ const reasoningLevels: readonly ReasoningLevel[]
195
+ ```
196
+
197
+ Every `ReasoningLevel`, in order, for pickers. A `model` option returns one in
198
+ [`ModelSettings`](#modelsettings).
199
+
200
+ ### `isCompactionPart(part)`
201
+
202
+ ```ts
203
+ isCompactionPart(part: { type: string }): part is CompactionPart
204
+
205
+ type CompactionPart = {
206
+ type: 'data-a2-compaction'
207
+ id: string // the compacting generation's ID
208
+ data: {
209
+ status: 'running' | 'completed' | 'failed' | 'interrupted'
210
+ inputTokens?: number
211
+ thresholdTokens?: number
212
+ summary?: string
213
+ usage?: LanguageModelUsage
214
+ }
215
+ }
216
+ ```
217
+
218
+ The transcript part that marks a compaction.
219
+
220
+ - Folding `ai.compaction.requested` appends it to the active response before
221
+ the step it compacts for (or opens the response with it). It counts toward
222
+ that step's recovery base, so Stop cutoffs, failures, and retries keep it.
223
+ - `ai.compaction.completed` settles it in place with `summary` and `usage`. If
224
+ its generation ends first: `interrupted` (Stop, Pause now, session close) or
225
+ `failed`. A pending Stop previews `interrupted` on the client.
226
+ - A superseded worker attempt's part is removed; the replacement compacts again
227
+ if needed.
228
+ - `convertToModelMessages()` skips data parts by default; model context never
229
+ contains it.
230
+
231
+ See [Show compaction in the conversation](/agents/conversations#show-compaction-in-the-conversation).
232
+
233
+ ### `events` and `createEvents(options?)`
234
+
235
+ The built-in Standard Schema definitions, both isomorphic: `events` for the
236
+ default AI SDK `UIMessage`, `createEvents({ messageSchema })` for a more
237
+ specific message type.
238
+
239
+ ### Optimistic AI client state
240
+
241
+ Every client folding an AI reducer (`agentContract({ reducer })` included)
242
+ shows pending send, edit, move, remove, Send now, steer, pause, resume, and
243
+ Stop commands in `state.messages`, `state.inbox`, and `state.starting`, with no
244
+ extra I/O or raw-history reads.
245
+
246
+ - An idle, unpaused send joins the conversation immediately with
247
+ `starting: true` (a speculative turn, not model execution); other sends
248
+ queue. Turn identities come only from accepted decisions; confirmed turns
249
+ win.
250
+ - Steer, and Send now against an active turn, show in `state.messages` after
251
+ the current response, not the inbox, until admission; they stay out of the
252
+ prompt. Several steering inputs show in admission order, most recent first,
253
+ and the display survives receipt and hydration, including while paused.
254
+ - A pending Stop clears the displayed `state.active`, generation, and pending
255
+ approvals and inputs, previews an idle status (paused when the inbox is), and
256
+ freezes the retained projection at `lastSeenIndex`, until its decision; a
257
+ rejection restores them.
258
+ - Later local edits stay visible through earlier receipts. Pending commands
259
+ retire on their `ai.control.decided` receipt or session close; failed pushes
260
+ roll back.
261
+ - Server reads and folds hold confirmed entries only (`starting: false`).
262
+ Custom reducers that call `reduceAIState()` or nest the AI fold don't get
263
+ this display.
264
+
265
+ See [Optimistic conversation display](/agents/conversations#optimistic-conversation-display).
266
+
267
+ ### `AIState`
268
+
269
+ The type of the AI reducer's `initialState` and `fold`, server
270
+ `session.state()`, and client snapshots. Hydrate with state and its log index
271
+ together, so seen history never folds twice. The AI fold ignores application
272
+ events. Encoding is internal; no event or tool payload is truncated.
273
+
274
+ | Field | What it holds |
275
+ | --- | --- |
276
+ | `messages` | [`TranscriptMessage<M>`](#transcriptmessagem) values; server-side, without pending inputs |
277
+ | `inbox` | `{ paused, items }`; items are `{ id, message, generate, afterStepOf? }`, where `afterStepOf` is an observed steering turn that hands off after its current step (removal cancels it, admission consumes it) |
278
+ | `active` | `null` or `{ turnId, input, phase }`; admission freezes `input` |
279
+ | `status` | generation status |
280
+ | `starting` | a speculative optimistic turn |
281
+ | `receipt` | the latest user command receipt |
282
+ | `pendingQueueCommands` | the unresolved queue commands, for the optimistic display |
283
+ | `session` | session lifecycle |
284
+ | `pendingApprovals`, `pendingInputs` | pending tool approvals and application input |
285
+ | `tools` | tool activity, identity and status only; payloads live in message parts |
286
+ | `compaction` | the current or latest compaction |
287
+ | `modelMetadata` | each model's pending, resolved, or unavailable limits |
288
+ | `usage`, `error` | usage per generation; the last error |
289
+ | `activeGeneration` | the started generation |
290
+ | `activeRequestId` | the server-authorized generation request; fences delayed requests before start |
291
+ | `activeRequestReason`, `activeRequestSourceGenerationId` | that request's reason and source generation |
292
+ | `activeResponseMessageId` | the requested response, until `activeGeneration` exists |
293
+ | `activeProjection` | the indexed generation frontier, for exact interruption cutoffs |
294
+ | `responseOwners` | `Record<string, { generationId: string; requestId: string; attempt: number }>`: each response's latest owner, so a superseded owner's late events change nothing |
295
+ | `terminalRequestIds`, `terminalGenerations` | optional `Record<string, true>` and `Record<string, 'completed' \| 'failed' \| 'interrupted' \| 'superseded'>` fences, kept across snapshots and recovery |
296
+
297
+ - The `paused` status and the `pausing` and `paused` phases mean retained work.
298
+ - `activeProjection` lives from generation start until the response completes,
299
+ is interrupted or superseded, or a retry or later generation replaces it,
300
+ including while a step waits on tool, approval, or input barriers, while
301
+ paused, and while a failed generation awaits retry or interruption. Ordered
302
+ progress applies only new chunks. It is plain JSON, so checkpoints serialize
303
+ and clone, but their size grows with retained progress.
304
+ - `input-streaming` tool parts carry best-effort partial JSON for display until
305
+ the completed input arrives; it never authorizes execution or changes
306
+ interruption.
307
+
308
+ See [What happens after a send](/agents/introduction#what-happens-after-a-send).
309
+
310
+ ### `deriveUIMessages(history)` and `reduceAIState(state, event)`
311
+
312
+ `deriveUIMessages()` folds a raw event history into `TranscriptMessage<M>[]`.
313
+ `reduceAIState()` is the event-at-a-time form the standard reducer uses. Both
314
+ project the same chunk-only protocol synchronously.
315
+
316
+ ## `experimental-a2/ai/server`
317
+
318
+ ### `agent(options)`
319
+
320
+ ```ts
321
+ agent({
322
+ name: string, // or contract: agentContract(...), never both
323
+ events?, messageSchema?, // with name: server-only application events
324
+ model: string | ModelSettings | (context => string | ModelSettings), // an AI Gateway model ID
325
+ tools?,
326
+ instructions?,
327
+ maxSteps?,
328
+ harness?: // the agent's loop; defaults to { type: 'ai-sdk' }
329
+ | { type: 'ai-sdk', generation?, generate?, compaction?, messageMetadata? }
330
+ | Harness, // fx() or pi()
331
+ progress?,
332
+ toolLane?: string, // run local tool executions one at a time on this lane
333
+ toolRecovery?: { [tool]?: ToolRecovery }, // 'rerun' (default) | 'report'
334
+ handlers?, // application event handlers
335
+ store?, scheduler?, telemetry?,
336
+ }): Agent<Events, State, Name> // an A2Server
337
+ ```
338
+
339
+ Serves an agent as one ordinary A2 server, returning an `A2Server` (`fetch`,
340
+ `session(id)`, `drain()`, `contract`). Server-only: the browser gets a throwing
341
+ stub.
342
+
343
+ | Option | What it does |
344
+ | --- | --- |
345
+ | `name` | the built-in protocol, with optional server-only `events` and `messageSchema` |
346
+ | `contract` | a shared `agentContract()`, when clients fold application state |
347
+ | `model` | an AI Gateway model ID, or [`ModelSettings`](#modelsettings) with one, under every harness; a value or per-generation async resolver |
348
+ | `tools` | AI SDK `tool()` definitions; local tools read context with [`handlerContext()`](#handlercontextcontract) |
349
+ | `instructions` | a value or per-generation async resolver |
350
+ | `maxSteps` | a positive integer capping one complete assistant response, under every harness; no limit by default |
351
+ | `harness` | the agent's loop: `{ type: 'ai-sdk', … }` (`AISDKHarness`, the default), [`fx()`](#fx), or [`pi()`](#pisandbox) |
352
+ | `harness.generation` | per-step settings, or a resolver |
353
+ | `harness.messageMetadata` | maps default stream parts to message metadata |
354
+ | `harness.generate` | replaces the default model step |
355
+ | `harness.compaction` | automatic for supported Gateway requests by default |
356
+ | `progress` | durable batching: `maxChunks` (128), `maxDelayMs` (0) |
357
+ | `toolLane` | one lane for every local tool execution, one at a time in admission order; model steps keep their own lane |
358
+ | `toolRecovery` | `'rerun'` (default) or `'report'` per tool, the harness's own tools included |
359
+ | `handlers` | application event handlers; naming an event the agent handles throws a `TypeError` |
360
+ | `store`, `scheduler`, `telemetry` | as for any A2 server; `telemetry` covers Core and AI in one trace ([span catalogue](/reference/adapters#the-span-catalogue)) |
361
+
362
+ **Contracts.** A contract not built by `agentContract()` throws a `TypeError`
363
+ and fails to typecheck (serve custom contracts with `createServer()`). Messages
364
+ are typed through [`AIMessageOf`](#aimessageofevents). `typeof` the agent types
365
+ `useAgent()` and `createAgentClient()` through a type-only import; `Name`, its
366
+ reducer name, decides whether the contract-free client accepts it.
367
+
368
+ **AI Gateway.** For Gateway models, A2 merges `caching: 'auto'` into
369
+ `generation.providerOptions.gateway`, and automatic compaction uses the same
370
+ default; your values take precedence and other options are kept. A custom
371
+ `generate` receives your original settings and owns its caching. Gateway calls
372
+ also send an `x-session-affinity` header, stable for the session and free of
373
+ the raw session ID, so supporting providers can reuse their cache; it selects
374
+ no provider and guarantees no hit. Override or suppress it (`undefined`) in
375
+ `generation.headers`, in any casing, not on the model: A2's per-call default
376
+ overrides model headers. See
377
+ [Gateway automatic caching](https://vercel.com/docs/ai-gateway/models-and-providers/automatic-caching).
378
+
379
+ **`harness`** is the agent's loop. `{ type: 'ai-sdk' }` (`AISDKHarness`), the
380
+ default, runs one AI SDK `streamText()` step per durable generation, with the
381
+ `generation`, `messageMetadata`, `generate`, and `compaction` options below.
382
+ [`fx()`](#fx) and [`pi()`](#pisandbox) run a coding agent's own loop instead.
383
+ The four options exist only in `{ type: 'ai-sdk' }`; passing one to `agent()`
384
+ itself throws a `TypeError` naming where it belongs.
385
+
386
+ **`fetch`** accepts reads and pushes of `ai.control.requested`, except the
387
+ server-authored `request-input` and `tool-result`, with `send`, `edit`, and
388
+ `steer` limited to user messages. With `contract` it also accepts the
389
+ contract's events; `events` given with `name` stay server-only. Other pushed
390
+ events answer `400`. `fetch(request, { authorize })` adds per-operation policy.
391
+
392
+ **Model steps.** Under `{ type: 'ai-sdk' }`, each durable generation runs one AI
393
+ SDK `streamText()` step, whose chunks A2 persists once in durable progress
394
+ batches. It passes the model ID to the AI SDK, which resolves it through its
395
+ global provider, AI Gateway unless replaced. Generation requests
396
+ share one durable lane, so a session's model calls never overlap, and a later
397
+ user message waits until the active response and its tool loop reach a terminal
398
+ event (a failed response, until retried or interrupted). A `model`,
399
+ `instructions`, or `generation` resolver that throws or returns an invalid
400
+ value fails the turn once, like a provider error, without retrying; that
401
+ attempt's `ai.generation.started` omits `model` if it never resolved, and
402
+ `retry()` runs the step again. Store failures (`A2Error`) still retry durably.
403
+ See [What happens after a send](/agents/introduction#what-happens-after-a-send).
404
+
405
+ **Resolver context.** `model`, `instructions`, and `generation` resolvers get
406
+ `{ event, messages, state, session, signal }`, where `state` is the
407
+ application state (`{}` without a contract `reducer`) at the captured
408
+ snapshot, read for free from the model-context checkpoint. Compaction,
409
+ `messageMetadata`, and `generate` callbacks also get `modelMessages`, the
410
+ model-ready prompt with committed summaries. No context exposes raw history or
411
+ an `execution` argument. `context.session.state(reducer)` reads at the captured
412
+ prompt frontier by default; `context.session.state()` returns `AIState` with
413
+ its log index.
414
+
415
+ **`harness.generation`** holds per-step settings such as `temperature`,
416
+ `maxOutputTokens`, `topP`, provider options, and tool approval policy, as the
417
+ provider supports them. It excludes `stopWhen`, tool execution callbacks, tool
418
+ callers, sandbox execution, and the tool approval secret; everything else,
419
+ `toolsContext` included, reaches the AI SDK untouched. Model-step timeouts work;
420
+ tool-execution timeouts don't. A resolver returns `AgentGenerationSettings` (or
421
+ a promise) once per attempt, before compaction and model dispatch, and also
422
+ governs tool approvals and automatic-compaction compatibility. Static settings
423
+ are validated at construction, resolved ones before use; failures and
424
+ cancellation follow model resolution.
425
+
426
+ **`harness.messageMetadata(context)`** synchronously maps each default stream part to
427
+ your `UIMessage` metadata type. It gets the `generate` context plus the
428
+ `TextStreamPart` as `part`; its `generation` includes A2's Gateway defaults.
429
+ Metadata for `start` and `finish` parts is recorded with progress and
430
+ shallow-merged onto the assistant message (use `finish`'s `totalUsage` for
431
+ final usage). A custom `generate` skips it. See
432
+ [Store assistant metadata](/agents/conversations#store-assistant-metadata).
433
+
434
+ **`harness.generate(context)`** gets the resolver context plus the step's
435
+ `generationId`, `modelMessages`, and the resolved `model`, `instructions`,
436
+ `tools`, and `generation`; `messages` and `modelMessages` reflect this step's
437
+ compaction. It returns exactly one model step as a
438
+ `ReadableStream<UIMessageChunk>` writing message
439
+ `event.payload.responseMessageId`; A2 keeps tool execution, continuation, and
440
+ the durable lifecycle. See
441
+ [Replace generation, not durability](/agents/conversations#replace-generation-not-durability).
442
+
443
+ **Tools.** Independent authorized tool handlers run concurrently with no
444
+ fixed limit. Results and generation completion arrive in either order; a
445
+ continuation needs the closed step and every required terminal result.
446
+ Single-owner terminal events commit atomically with the parent handler's
447
+ completion and carry its durable cause; many-owner joins and preliminary
448
+ outputs append immediately and idempotently. Provider calls and external side
449
+ effects are at least once: give them their own idempotency. An approved
450
+ provider-executed call can satisfy the join from its approval response, and a
451
+ provider tool with declared deferred-result support waits for its provider
452
+ result, which trusted server code submits with
453
+ [`inputs.toolResult(result)`](#inputs); the controller checks it against the
454
+ admitted call (`fetch` rejects it from browsers).
455
+
456
+ **`toolRecovery`**
457
+
458
+ - `'report'` skips `execute` when the previous attempt lapsed (its claim
459
+ expired after a crash, timeout, or frozen process), recording an error result
460
+ saying the call was interrupted and may have partly run; the model continues
461
+ from it. A2 can't tell whether the lapsed attempt reached the tool, so a call
462
+ that never started is reported too.
463
+ - Once `execute` has started, a cancelling Stop or Pause now, or a failure A2
464
+ would otherwise retry (such as a transient scheduler send failure), records
465
+ the same result; resuming after Pause now doesn't rerun the call.
466
+ - These still retry and run the tool: an attempt that failed with an error
467
+ before `execute` (such as a failed store read), and a failure inside A2 while
468
+ or just after the tool runs (its abort subscription, its result validation).
469
+ - An unknown tool name, a tool without `execute`, or another value throws a
470
+ `TypeError` at construction.
471
+
472
+ See [Delivery semantics](/agents/conversations#delivery-semantics).
473
+
474
+ **`harness.compaction`**
475
+
476
+ | Value | Behavior |
477
+ | --- | --- |
478
+ | omitted | automatic for supported Gateway requests |
479
+ | `false` | no compaction and no model metadata discovery |
480
+ | `{ thresholdTokens?, instructions? }` | tunes automatic compaction. `thresholdTokens`: a positive safe integer. `instructions`: added to A2's summary prompt; the agent's instructions stay |
481
+ | `{ shouldCompact(context), compact(context) }` | a custom policy |
482
+ | `(context: AgentResolverContext) => CompactionOptions \| Promise<CompactionOptions>` | resolves once per generation to a valid policy or `false`; can await `context.session.state(reducer)` |
483
+
484
+ - Static options are validated at construction, resolved ones before their
485
+ generation; each session uses its own value.
486
+ - Discovery: without an explicit threshold, the first generation batches a
487
+ metadata request into its start write, and an independent handler records the
488
+ model's limits from the public Gateway catalog in `state.modelMetadata`
489
+ without delaying dispatch (shared one-hour cache, five-second timeout; no new
490
+ request per turn, even after exhausted retries). The default threshold is 75%
491
+ of the context window, lowered for a larger output allowance. While it is
492
+ unknown, the first request proceeds and context-limit errors fail normally.
493
+ - Direct or custom providers, fallback model lists, and custom generators need
494
+ explicit configuration. Provider-executed tools, structured output, and
495
+ forced tool choices skip implicit compaction and reject explicit automatic
496
+ policies.
497
+ - The estimate is serialized bytes of messages, tools, and instructions,
498
+ excluding message and part provider options (they still reach the model
499
+ unchanged); not a tokenizer. A preceding same-model completion calibrates it
500
+ to reported input usage until a smaller prompt, retry, compaction, or model
501
+ change.
502
+ - Completed generations record `inputTokenEstimate`. Automatic
503
+ `ai.compaction.requested` events also record the pre-compaction
504
+ `inputTokenEstimate`, the calibrated `inputTokens`, and `thresholdTokens`
505
+ (custom policies omit them), at no extra model call or round trip.
506
+ - The built-in summary is one extra model request that keeps the request prefix
507
+ for caching and runs no application callbacks, transforms, or tool hooks.
508
+ A2 stores built-in summaries apart from your typed messages, so a summary
509
+ needs no message metadata and need not match `messageSchema`.
510
+ `state.messages` stays intact, marked with a
511
+ [`data-a2-compaction` part](#iscompactionpartpart);
512
+ `state.compaction?.status === 'running'` shows activity.
513
+ - Successful compaction is permanent across failure, interruption, retry, and
514
+ recovery. It defers while tool exchanges or A2-owned tool work are unsettled
515
+ (one coordinator state read per candidate), and while dead-lettered tool
516
+ work awaits repair and settlement or session close.
517
+
518
+ See [Compact long conversations](/agents/conversations#compact-long-conversations).
519
+
520
+ **Harnesses.** Under `fx()` and `pi()`, each model call the harness makes is one
521
+ durable step of the response, with the same events, progress, steering,
522
+ interruption, `maxSteps`, and recovery as any step. Tool calls it makes become
523
+ ordinary A2 tool calls, executed by A2's tool handlers, and `toolRecovery` may
524
+ name the harness's own tools, such as Pi's `bash`. `model` is resolved per step
525
+ as for any agent. `instructions` and `tools` reach the harness; how they
526
+ combine with its own prompt and tools depends on the harness. Tool approval
527
+ policy lives in `harness.generation`, so a harness agent's tools run without
528
+ approval, and a tool that sets `needsApproval` is rejected.
529
+
530
+ A harness resumes after a crash at the interrupted step. Finished model calls
531
+ are not made again and finished tool calls do not run again. A2 rebuilds the
532
+ harness for each step from the log, and stores nothing outside it. Each agent
533
+ keeps, in its process's memory, the harness's state at the end of the last
534
+ response it ran there, FX's checkpoint or Pi's session, so a warm process reads
535
+ and replays only the log since; a cold one reads and replays the session from
536
+ its start. A saved state counts only once the step that wrote it is committed,
537
+ so a stale or unusable one costs a longer rebuild, never correctness. See
538
+ [Harnesses](/agents/harnesses).
539
+
540
+ **`progress`.** A batch is written as soon as the previous write finishes;
541
+ chunks arriving mid-write share the next batch, up to `maxChunks` (128).
542
+ `maxDelayMs` (default 0) lets a batch collect that long after the previous
543
+ write; boundary chunks and `maxChunks` write sooner. Raising it trades token
544
+ latency for fewer progress events.
545
+
546
+ ### `ModelSettings`
547
+
548
+ ```ts
549
+ type ModelSettings = {
550
+ model: string // an AI Gateway model ID
551
+ reasoning?: ReasoningLevel
552
+ speed?: 'fast' | 'standard'
553
+ }
554
+ ```
555
+
556
+ A model with its reasoning level and speed, returned by a `model` option or
557
+ resolver. `reasoning` becomes the AI SDK `reasoning` call option
558
+ ([`reasoningLevels`](#reasoninglevels)), FX's effort, or Pi's thinking level. `speed: 'fast'` sets
559
+ `providerOptions.gateway.speed` to prefer AI Gateway's faster tier when the
560
+ model has one; `'standard'` removes a configured speed. Both override the same
561
+ settings in `generation`. See [Choose the model](/agents/introduction#choose-the-model).
562
+
563
+ ### `runWithHandlerContext(ctx, fn)`
564
+
565
+ The test seam for context-reading tools: runs `fn` with `ctx` as the ambient
566
+ handler context, so tests can call `execute` directly. Inside `fn`,
567
+ `handlerContext()` returns `ctx`, with or without a contract argument.
568
+
569
+ ## `experimental-a2/ai/fx`
570
+
571
+ ### `fx()`
572
+
573
+ ```ts
574
+ fx(): Harness<'fx'>
575
+ ```
576
+
577
+ The [FX](https://fx.sh) harness for `agent({ harness })`, running FX's kernel
578
+ from `libfx` (an optional peer dependency) in the server process. FX adds no
579
+ system prompt and brings no tools: `instructions` is the whole system prompt
580
+ and the agent's `tools` are all FX can call. `reasoning` becomes FX's effort and
581
+ `speed: 'fast'` its fast tier. FX compacts its own context; a compaction
582
+ appears in the transcript as a `data-a2-compaction` part with its summary.
583
+
584
+ Every model call FX makes goes through A2, which records it as a step. To
585
+ continue a response, A2 restores FX's latest checkpoint and replays the
586
+ response so far from the log: recorded model responses and tool results answer
587
+ the kernel without network calls or tool runs, then FX makes its next model
588
+ call. Replay grows with the response, roughly 0.1 to 0.5 seconds for one of 80
589
+ to 200 tool calls. FX checkpoints hold at most 4 MiB of conversation; past that,
590
+ A2 replays from an earlier checkpoint.
591
+
592
+ Credentials come from `AI_GATEWAY_API_KEY`, or Vercel's OIDC token on Vercel.
593
+ This entry point is server-only.
594
+
595
+ ## `experimental-a2/ai/pi`
596
+
597
+ ### `pi({ sandbox })`
598
+
599
+ ```ts
600
+ pi({
601
+ sandbox: ({ session, signal }) => SandboxSession | Promise<SandboxSession>,
602
+ }): Harness<'pi'>
603
+ ```
604
+
605
+ The [Pi](https://pi.dev) harness for `agent({ harness })`, running Pi's own
606
+ agent session from `@earendil-works/pi-coding-agent` (with
607
+ `@earendil-works/pi-ai`, both optional peer dependencies). Pi brings its system
608
+ prompt and its `read`, `write`, `edit`, and `bash` tools, which run in the
609
+ session's sandbox. `instructions` is added to Pi's prompt. The agent's `tools`
610
+ are added to Pi's; a tool with one of their names replaces Pi's. `reasoning`
611
+ becomes Pi's thinking level, and `model` is one of the Gateway models Pi's
612
+ catalog lists. Pi works in the sandbox's working directory; its `bash` runs
613
+ with the sandbox's environment, never the server's. Pi compacts its own
614
+ context; a compaction appears in the transcript as a `data-a2-compaction` part
615
+ with its summary.
616
+
617
+ - `sandbox` (`HarnessSandbox`, given a `HarnessSandboxContext`) resolves the
618
+ session's AI SDK sandbox session (`Experimental_SandboxSession` from `ai`).
619
+ It runs for each local tool execution, whose `execute` receives the sandbox
620
+ as `experimental_sandbox`, Pi's tools and the agent's alike, and for a step
621
+ until Pi's session records the sandbox's working directory.
622
+ - It must return the same sandbox for a session every time, and concurrent tool
623
+ calls call it concurrently. A resolver failure fails that attempt, which A2
624
+ retries. A2 never closes the sandbox. `pi()` without a `sandbox` function
625
+ throws a `TypeError`.
626
+ - For each step, A2 restores Pi's session from the end of the last response,
627
+ replays the response so far from the log, and lets Pi make its next model
628
+ call. Pi's tool calls are ordinary A2 tool calls: A2 runs Pi's real tool
629
+ implementations against the sandbox and records their results.
630
+ - Pi's tools move at most 4 MiB from the sandbox to your server per file read
631
+ or command. A larger file fails `read` or `edit`, and a command whose output
632
+ passes 4 MiB is stopped and fails `bash`; both errors tell the model to read
633
+ in parts. Files arrive through the sandbox's `readFile`, so implement it as a
634
+ stream for the limit to bound the download too.
635
+
636
+ `pi()` takes `PiOptions`. Credentials come from `AI_GATEWAY_API_KEY`, or
637
+ Vercel's OIDC token on Vercel. This entry point is server-only.
638
+
639
+ ## `experimental-a2/ai/client`
640
+
641
+ ### `createAgentClient(options)`
642
+
643
+ ```ts
644
+ createAgentClient<typeof agent>(options: {
645
+ api: ClientApi
646
+ gcTime?: number
647
+ participant?: string
648
+ reconnect?, fetch?, webSocket?, // as createClient
649
+ }): AgentClient // an A2Client over the built-in AI protocol
650
+ ```
651
+
652
+ A contract-free client for an `agent()` route, typed by a type-only import of
653
+ the server module. It pushes the built-in AI controls only.
654
+
655
+ - Accepts only agents whose reducer has the built-in name
656
+ (`typeof AI_STATE_REDUCER`): `agent({ name })`, or `agent({ contract })` with
657
+ an `agentContract()` without `reducer`. Extended contracts use
658
+ `createClient({ contract })`, so the client folds the same state.
659
+ - Use it for pushes outside React, such as a first message before navigating,
660
+ and pass it to `useAgent({ client })` to share its sessions.
661
+
662
+ See [Start a session from another page](/agents/introduction#start-a-session-from-another-page).
663
+
664
+ ## `experimental-a2/ai/react`
665
+
666
+ ### `useAgent(options)`
667
+
668
+ ```ts
669
+ useAgent<typeof agent>(options: {
670
+ id: string
671
+ initial?: InitialSnapshot<AIState>
672
+ participant?: string
673
+ } & ({ api: string } | { client: AgentClient })): UseAgentResult
674
+
675
+ useAgent(options: {
676
+ id: string
677
+ initial?: InitialSnapshot<State>
678
+ participant?: string
679
+ } & (
680
+ | { api: string; contract: Contract } // a shared agentContract(), imported for real
681
+ | { client: A2Client } // createClient({ contract, api })
682
+ )): UseAgentResult
683
+ ```
684
+
685
+ One agent session in React: its live optimistic `AIState` and actions that
686
+ fill in protocol identities.
687
+
688
+ - `api`: one shared client per route for the page's lifetime, reading
689
+ `globalThis.fetch` per request. `client`: yours, for a custom `fetch`,
690
+ headers, or `gcTime`. Both accept only built-in-name agents.
691
+ - `contract`: also folds its application state, one client per contract and
692
+ route; `state` is `AIState & X` and `push` accepts its events. Pass
693
+ `createClient({ contract, api })` as `client` to share it outside React.
694
+ - Contracts not built by `agentContract()` use core `useSession()` and
695
+ `inputs`.
696
+ - Keep one options form per call site, unchanged between renders.
697
+
698
+ The result is the session result (`state`, `index`, `connection`, `push`,
699
+ `events`, `hydrated`, `reconnect`, `loadHistory`, `history`) plus:
700
+
701
+ | Member | Does |
702
+ | --- | --- |
703
+ | `messages` | `state.messages` |
704
+ | `status` | `state.status` |
705
+ | `indicator` | `'starting'` for an optimistic turn awaiting acceptance, `'compacting'` while a summary runs, `'thinking'` while the current step has no visible part (reasoning and step boundaries don't count), else `null` |
706
+ | `sendMessage(message, { generate? })` | queues a string (one text part) or a `UIMessage` |
707
+ | `steer(message)` | steers the active turn after its current step, or sends when idle |
708
+ | `stop()` | stops the active turn at the rendered frontier |
709
+ | `approve(approval)`, `deny(approval, { reason? })` | answers a pending approval |
710
+ | `respond(input, value)` | answers a pending application input |
711
+ | `retry()` | retries the failed response with a fresh `retryId` |
712
+ | `pause({ when })`, `resume()` | pauses and resumes |
713
+ | `queue.edit(message)`, `queue.move({ inputId, beforeId })`, `queue.remove(inputId)`, `queue.sendNow(inputId)` | edits the inbox |
714
+
715
+ Every action returns a `PushResult`, like `push()`: it resolves to the
716
+ persisted command events (each `id` is its receipt's `commandId`), rejects
717
+ when the push fails, and its `.confirmed` resolves when the stream delivers
718
+ them back. Actions read identities from the render that created them, so
719
+ `stop()` and `queue.sendNow()` carry the frontier the user saw. An action with
720
+ nothing to address (`stop()` with no active turn, `retry()` with no failed
721
+ one) resolves to an empty array. Actions are rebuilt every render; their
722
+ identity is not stable.