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
package/src/ai.ts CHANGED
@@ -1,14 +1,17 @@
1
+ import type { MessageBase } from './ai-message-base.ts'
2
+ import { reducer as makeReducer } from './reducer.ts'
1
3
  /**
2
- * experimental-a2/ai — an isomorphic, extensible agent definition.
4
+ * experimental-a2/ai — the isomorphic AI protocol.
3
5
  *
4
- * `agent()` assembles a normal A2 contract, a reducer over the built-in
5
- * AI protocol, and pure builders for the events applications append.
6
- * Model calls and tool implementations stay in experimental-a2/ai/server.
6
+ * Event schemas, the `AIState` reducer, `agentContract()` for extending
7
+ * both, and pure builders for the events applications append. Model calls
8
+ * and tool implementations stay in experimental-a2/ai/server.
7
9
  */
8
10
 
9
11
  import type {
10
12
  FinishReason,
11
13
  JSONValue,
14
+ LanguageModelCallOptions,
12
15
  LanguageModelUsage,
13
16
  UIMessage,
14
17
  UIMessageChunk,
@@ -38,35 +41,55 @@ export type {
38
41
  InboxItem,
39
42
  } from './ai-control.ts'
40
43
  import { contract as createContract } from './contract.ts'
41
- import type { Contract, EventDefs } from './contract.ts'
44
+ import type { Contract, ContractEvent, EventDefs } from './contract.ts'
42
45
  import {
43
46
  interruptUIMessage,
44
47
  pauseUIMessage,
45
48
  projectUIMessage,
49
+ projectUIMessageStream,
50
+ hasToolCall,
51
+ type ProjectionChunk,
46
52
  type UIMessageCursor,
47
53
  } from './ai-projector.ts'
48
54
  import { ambientToolScopeStorage } from './internal.ts'
49
55
  import {
50
56
  upsertMessage,
57
+ retainMessageBase,
58
+ findMessage,
51
59
  upsertResponse,
52
60
  answerApproval,
53
61
  approvalRequestedChunk,
54
62
  toolResultChunk,
63
+ toolCalledChunk,
55
64
  advanceProjection,
56
65
  projectedTools,
57
66
  restoreGenerationBase,
58
67
  stopGenerationTools,
59
68
  interruptGenerationProjection,
60
69
  projectedMessage,
70
+ projectedMessageStream,
61
71
  reduceToolActivity,
72
+ addCompactionPart,
73
+ isCompactionPart,
74
+ settleAbandonedCompaction,
75
+ settleCompactionPart,
62
76
  } from './ai-message-projection.ts'
63
- import { clientStateProjection, type Reducer } from './reducer.ts'
64
- import { storedAIReducer, type AIStoredReducer } from './ai-stored-state.ts'
65
- export type { AIStoredState, AIStoredReducer } from './ai-stored-state.ts'
77
+ import type { Reducer } from './reducer.ts'
78
+ import { withReducerRuntime } from './reducer-runtime.ts'
79
+ import { withAIStorage } from './ai-stored-state.ts'
80
+ import {
81
+ appSlice,
82
+ isAIStateKey,
83
+ registerAgentReducer,
84
+ withAppSlice,
85
+ type AppReducer,
86
+ } from './ai-app-state.ts'
66
87
  import {
67
88
  flattenProgressBatches,
68
89
  progressBatches,
90
+ progressOwnedBy,
69
91
  type ProgressBatches,
92
+ type StoredProgressPayload,
70
93
  } from './ai-progress-batches.ts'
71
94
  import type { HandlerContext } from './server.ts'
72
95
  import type { StandardSchemaV1 } from './standard-schema.ts'
@@ -77,6 +100,43 @@ export type AgentStatus =
77
100
 
78
101
  export type GenerationReason = 'message' | 'tool' | 'input' | 'retry' | 'resume'
79
102
 
103
+ export type ReasoningLevel = NonNullable<LanguageModelCallOptions['reasoning']>
104
+
105
+ export const reasoningLevels: readonly ReasoningLevel[] = [
106
+ 'provider-default',
107
+ 'none',
108
+ 'minimal',
109
+ 'low',
110
+ 'medium',
111
+ 'high',
112
+ 'xhigh',
113
+ ]
114
+
115
+ export type CompactionPartData = {
116
+ status: 'running' | 'completed' | 'failed' | 'interrupted'
117
+ inputTokens?: number
118
+ thresholdTokens?: number
119
+ summary?: string
120
+ usage?: LanguageModelUsage
121
+ }
122
+
123
+ /** The transcript part A2 adds to a response where its context was compacted. */
124
+ export type CompactionPart = {
125
+ type: 'data-a2-compaction'
126
+ id: string
127
+ data: CompactionPartData
128
+ }
129
+
130
+ export { isCompactionPart }
131
+
132
+ /**
133
+ * A transcript message: `M` plus the compaction part A2 adds, so
134
+ * `isCompactionPart` narrows even when `M` declares its own data parts.
135
+ */
136
+ export type TranscriptMessage<M extends UIMessage> = Omit<M, 'parts'> & {
137
+ parts: Array<M['parts'][number] | CompactionPart>
138
+ }
139
+
80
140
  export type SessionCreatedPayload = { metadata?: JSONValue }
81
141
  export type SessionClosedPayload = { reason?: string }
82
142
  export type MessageCreatedPayload<M extends UIMessage> = {
@@ -115,15 +175,13 @@ export type GenerationStartedPayload = {
115
175
  generationId: string
116
176
  responseMessageId: string
117
177
  attempt: number
118
- model: string
178
+ /** Absent when the attempt failed before its model resolved. */
179
+ model?: string
119
180
  promptThroughIndex?: number
120
181
  }
121
182
  export type GenerationProgressPayload = {
122
- requestId: string
123
- messageId: string
124
- generationId: string
125
- responseMessageId: string
126
- sequence: number
183
+ /** Log index of the generation's `ai.generation.started`. */
184
+ startIndex: number
127
185
  chunks: UIMessageChunk[]
128
186
  }
129
187
  export type GenerationCompletedPayload = {
@@ -275,17 +333,20 @@ export type AIEventDefs<M extends UIMessage = UIMessage> = {
275
333
  'ai.model.metadata.resolved': StandardSchemaV1<ModelMetadataResolvedPayload>
276
334
  }
277
335
 
278
- export type ActiveGeneration = GenerationStartedPayload
336
+ export type ActiveGeneration = GenerationStartedPayload & {
337
+ /** Log index of this generation's `ai.generation.started`. */
338
+ startIndex: number
339
+ }
279
340
 
280
- export type ActiveGenerationProjection<M extends UIMessage = UIMessage> = {
341
+ export type ActiveGenerationProjection = {
281
342
  generationId: string
282
343
  responseMessageId: string
283
344
  /** Log index of the completed model step while its response remains interruptible. */
284
345
  completionIndex?: number
285
- baseMessage?: M
346
+ base?: MessageBase
286
347
  promptThroughIndex?: number
287
348
  cursor?: { throughIndex: number; stream: UIMessageCursor } | null
288
- baseCompaction?: CompactionState<M> | null
349
+ baseCompaction?: CompactionState | null
289
350
  batches: ProgressBatches
290
351
  baseTools: ToolActivity[]
291
352
  toolEvents: Array<
@@ -315,28 +376,20 @@ export type ToolActivity = {
315
376
  generationId: string
316
377
  toolCallId: string
317
378
  toolName?: string
318
- input?: unknown
319
- rawInput?: unknown
320
- output?: unknown
321
379
  error?: string
322
380
  preliminary?: boolean
323
381
  phase?: 'input' | 'execution'
324
382
  dynamic?: boolean
325
383
  providerExecuted?: boolean
326
- callProviderMetadata?: unknown
327
- resultProviderMetadata?: unknown
328
- toolMetadata?: unknown
329
384
  title?: string
330
385
  status: 'running' | 'completed' | 'failed' | 'denied'
331
386
  }
332
387
 
333
- export type CompactionState<M extends UIMessage> = {
388
+ export type CompactionState = {
334
389
  status: 'running' | 'completed'
335
390
  generationId: string
336
391
  throughMessageId: string
337
392
  throughIndex?: number
338
- messages?: M[]
339
- retainedMessageIds?: string[]
340
393
  summary?: string
341
394
  usage?: LanguageModelUsage
342
395
  }
@@ -354,14 +407,14 @@ export type AIState<M extends UIMessage = UIMessage> = {
354
407
  receipt: ControlReceipt | null
355
408
  pausedGenerationIds?: Record<string, true>
356
409
  session: { created: boolean; metadata?: JSONValue }
357
- messages: M[]
410
+ messages: TranscriptMessage<M>[]
358
411
  status: AgentStatus
359
412
  activeGeneration: ActiveGeneration | null
360
413
  activeRequestId: string | null
361
414
  activeRequestReason: GenerationReason | null
362
415
  activeRequestSourceGenerationId: string | null
363
416
  activeResponseMessageId: string | null
364
- activeProjection: ActiveGenerationProjection<M> | null
417
+ activeProjection: ActiveGenerationProjection | null
365
418
  responseOwners: Record<
366
419
  string,
367
420
  Pick<GenerationStartedPayload, 'generationId' | 'requestId' | 'attempt'>
@@ -374,19 +427,29 @@ export type AIState<M extends UIMessage = UIMessage> = {
374
427
  pendingApprovals: PendingApproval[]
375
428
  pendingInputs: PendingInput[]
376
429
  tools: ToolActivity[]
377
- compaction: CompactionState<M> | null
430
+ compaction: CompactionState | null
378
431
  modelMetadata: Record<string, ModelMetadataState>
379
432
  usage: GenerationUsage[]
380
433
  error: string | null
381
434
  }
382
435
 
383
- export type AgentDefinition<
436
+ /** The `UIMessage` type a contract's `ai.message.created` schema carries. */
437
+ export type AIMessageOf<D> = D extends {
438
+ readonly 'ai.message.created': infer S extends StandardSchemaV1
439
+ }
440
+ ? StandardSchemaV1.InferOutput<S> extends MessageCreatedPayload<
441
+ infer M extends UIMessage
442
+ >
443
+ ? M
444
+ : UIMessage
445
+ : UIMessage
446
+
447
+ /** What `agentContract()` returns: the AI protocol, with `X` as application state. */
448
+ export type AgentContract<
384
449
  M extends UIMessage = UIMessage,
385
450
  D extends EventDefs = AIEventDefs<M>,
386
- > = {
387
- readonly contract: Contract<D>
388
- readonly reducer: AIStoredReducer<D, AIState<M>>
389
- }
451
+ X extends object = Record<never, never>,
452
+ > = Contract<D, Record<never, never>, AgentReducer<M, D, X>>
390
453
 
391
454
  export type AgentToolEventDefs = EventDefs &
392
455
  Pick<AIEventDefs, 'ai.tool.execution.requested'>
@@ -397,24 +460,50 @@ export type AgentToolContext<D extends AgentToolEventDefs> = HandlerContext<
397
460
  'ai.tool.execution.requested'
398
461
  >
399
462
 
400
- export type AgentOptions<M extends UIMessage, E extends EventDefs> = {
463
+ /** Application state folded after the AI protocol, on the server and in clients. */
464
+ /** Rejects application state keys that would shadow `AIState`'s. */
465
+ type OwnKeys<X> = { [K in Extract<keyof X, keyof AIState>]: never }
466
+ /** Rejects a stateless application reducer, whose name would not be built in. */
467
+ type NonEmpty<X> = keyof X extends never ? never : unknown
468
+
469
+ /**
470
+ * The reducer `agentContract()` builds. Without application state it carries
471
+ * the built-in name, which is what lets the contract-free client fold it.
472
+ * `~appState` is type-only, absent at runtime: it carries the application
473
+ * state's type to `agent()`, and marks the reducer as `agentContract()`'s.
474
+ */
475
+ export type AgentReducer<
476
+ M extends UIMessage,
477
+ D extends EventDefs,
478
+ X extends object,
479
+ > = (keyof X extends never
480
+ ? Reducer<D, AIState<M>> & { readonly name: typeof AI_STATE_REDUCER }
481
+ : Reducer<D, AIState<M> & X>) & { readonly '~appState': X }
482
+
483
+ export type AgentContractOptions<
484
+ M extends UIMessage,
485
+ E extends EventDefs,
486
+ X extends object,
487
+ > = {
401
488
  name: string
402
489
  events?: E
403
490
  /** Optional stricter schema for custom UIMessage metadata/data/tool types. */
404
491
  messageSchema?: StandardSchemaV1<M>
405
- /** Snapshot identity. Bump when application-level projection semantics change. */
406
- reducerName?: string
492
+ /**
493
+ * Application state, folded from events beside `AIState` and merged into
494
+ * it. `fold` sees only this state, never the transcript.
495
+ */
496
+ reducer?: {
497
+ /** Snapshot identity of the application state. Rename it when `fold` changes. */
498
+ name: string
499
+ initialState: X & OwnKeys<X> & NonEmpty<X>
500
+ fold: (
501
+ state: NoInfer<X>,
502
+ event: ContractEvent<AIEventDefs<M> & NoInfer<E>>,
503
+ ) => NoInfer<X>
504
+ }
407
505
  }
408
506
 
409
- type AIMessageFrom<D extends EventDefs> =
410
- D extends Record<'ai.message.created', infer S extends StandardSchemaV1>
411
- ? StandardSchemaV1.InferOutput<S> extends MessageCreatedPayload<
412
- infer M extends UIMessage
413
- >
414
- ? M
415
- : UIMessage
416
- : UIMessage
417
-
418
507
  type SchemaResult<T> = StandardSchemaV1.Result<T>
419
508
 
420
509
  const issue = <T>(message: string): SchemaResult<T> => ({
@@ -540,13 +629,8 @@ const progressPayloadSchema: StandardSchemaV1<GenerationProgressPayload> =
540
629
  if (
541
630
  !isRecord(value) ||
542
631
  !isJSONCompatible(value) ||
543
- !stringField(value, 'requestId') ||
544
- !stringField(value, 'messageId') ||
545
- !stringField(value, 'generationId') ||
546
- !stringField(value, 'responseMessageId') ||
547
- typeof value['sequence'] !== 'number' ||
548
- !Number.isInteger(value['sequence']) ||
549
- value['sequence'] < 0 ||
632
+ typeof value['startIndex'] !== 'number' ||
633
+ !optionalIndexField(value, 'startIndex') ||
550
634
  !Array.isArray(value['chunks']) ||
551
635
  !value['chunks'].every(
552
636
  (chunk) => isRecord(chunk) && typeof chunk['type'] === 'string',
@@ -822,7 +906,7 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
822
906
  typeof value['attempt'] === 'number' &&
823
907
  Number.isInteger(value['attempt']) &&
824
908
  value['attempt'] > 0 &&
825
- stringField(value, 'model') &&
909
+ (value['model'] === undefined || stringField(value, 'model')) &&
826
910
  optionalIndexField(value, 'promptThroughIndex'),
827
911
  ),
828
912
  'ai.generation.progress': progressPayloadSchema,
@@ -979,13 +1063,18 @@ const initialState = <M extends UIMessage>(): AIState<M> => ({
979
1063
  const projectMessageChunks = <M extends UIMessage>(
980
1064
  messages: M[],
981
1065
  messageId: string,
982
- chunks: UIMessageChunk[],
983
- ): M[] =>
984
- messages.map((message) =>
985
- message.id === messageId
986
- ? projectUIMessage({ messageId, base: message, chunks })
987
- : message,
1066
+ chunks: ProjectionChunk[],
1067
+ ): M[] => {
1068
+ const base = findMessage(messages, messageId)
1069
+ return upsertMessage(
1070
+ messages,
1071
+ projectUIMessage<M>({
1072
+ messageId,
1073
+ ...(base === undefined ? {} : { base }),
1074
+ chunks,
1075
+ }),
988
1076
  )
1077
+ }
989
1078
 
990
1079
  const waitingStatus = <M extends UIMessage>(state: AIState<M>): AgentStatus => {
991
1080
  if (state.pendingApprovals.length > 0 || state.pendingInputs.length > 0) {
@@ -1018,9 +1107,15 @@ const isStaleGeneration = <M extends UIMessage>(
1018
1107
 
1019
1108
  const projectedResponse = <M extends UIMessage>(options: {
1020
1109
  state: AIState<M>
1021
- projection: ActiveGenerationProjection<M>
1022
- }): M => {
1023
- const message = projectedMessage(options.projection)
1110
+ projection: ActiveGenerationProjection
1111
+ }): TranscriptMessage<M> => {
1112
+ const message = projectedMessage({
1113
+ projection: options.projection,
1114
+ current: findMessage(
1115
+ options.state.messages,
1116
+ options.projection.responseMessageId,
1117
+ ),
1118
+ })
1024
1119
  return options.state.pausedGenerationIds?.[options.projection.generationId]
1025
1120
  ? pauseUIMessage({
1026
1121
  message,
@@ -1048,6 +1143,19 @@ const terminalGenerationReason = <M extends UIMessage>(
1048
1143
  export function reduceAIState<M extends UIMessage>(
1049
1144
  state: AIState<M>,
1050
1145
  event: { type: string; payload: unknown; index: number; id?: string },
1146
+ ): AIState<M> {
1147
+ return settleAbandonedCompaction(
1148
+ state,
1149
+ retainMessageBase({
1150
+ before: state,
1151
+ after: reduceAIEvent(state, event),
1152
+ }),
1153
+ )
1154
+ }
1155
+
1156
+ function reduceAIEvent<M extends UIMessage>(
1157
+ state: AIState<M>,
1158
+ event: { type: string; payload: unknown; index: number; id?: string },
1051
1159
  ): AIState<M> {
1052
1160
  if (state.status === 'closed') return state
1053
1161
  switch (event.type) {
@@ -1371,13 +1479,14 @@ export function reduceAIState<M extends UIMessage>(
1371
1479
  'superseded',
1372
1480
  )
1373
1481
  : state
1374
- const baseMessage = baseState.messages.find(
1375
- (message) => message.id === payload.responseMessageId,
1482
+ const baseMessage = findMessage(
1483
+ baseState.messages,
1484
+ payload.responseMessageId,
1376
1485
  )
1377
1486
  return {
1378
1487
  ...baseState,
1379
1488
  status: 'generating',
1380
- activeGeneration: payload,
1489
+ activeGeneration: { ...payload, startIndex: event.index },
1381
1490
  activeResponseMessageId: null,
1382
1491
  responseOwners: {
1383
1492
  ...baseState.responseOwners,
@@ -1391,7 +1500,9 @@ export function reduceAIState<M extends UIMessage>(
1391
1500
  generationId: payload.generationId,
1392
1501
  responseMessageId: payload.responseMessageId,
1393
1502
  promptThroughIndex: payload.promptThroughIndex ?? event.index - 1,
1394
- ...(baseMessage === undefined ? {} : { baseMessage }),
1503
+ ...(baseMessage === undefined
1504
+ ? {}
1505
+ : { base: { partCount: baseMessage.parts.length } }),
1395
1506
  baseCompaction: baseState.compaction,
1396
1507
  batches: progressBatches(),
1397
1508
  baseTools: baseState.tools,
@@ -1402,30 +1513,29 @@ export function reduceAIState<M extends UIMessage>(
1402
1513
  }
1403
1514
  }
1404
1515
  case 'ai.generation.progress': {
1405
- const payload = event.payload as GenerationProgressPayload
1406
- if (state.pausedGenerationIds?.[payload.generationId]) return state
1516
+ const payload = event.payload as StoredProgressPayload
1517
+ const generation = state.activeGeneration
1518
+ const activeProjection = state.activeProjection
1407
1519
  if (
1520
+ generation === null ||
1521
+ !progressOwnedBy({
1522
+ payload,
1523
+ owner: generation,
1524
+ startIndex: generation.startIndex,
1525
+ }) ||
1526
+ state.pausedGenerationIds?.[generation.generationId] ||
1408
1527
  terminalGenerationReason(
1409
1528
  state,
1410
- payload.generationId,
1411
- payload.requestId,
1412
- ) !== undefined
1413
- ) {
1414
- return state
1415
- }
1416
- const activeProjection = state.activeProjection
1417
- if (
1418
- state.activeGeneration?.generationId !== payload.generationId ||
1419
- state.activeGeneration.requestId !== payload.requestId ||
1420
- activeProjection?.generationId !== payload.generationId
1529
+ generation.generationId,
1530
+ generation.requestId,
1531
+ ) !== undefined ||
1532
+ activeProjection?.generationId !== generation.generationId
1421
1533
  ) {
1422
1534
  return state
1423
1535
  }
1424
1536
  const progress = advanceProjection({
1425
1537
  projection: activeProjection,
1426
- base: state.messages.find(
1427
- (message) => message.id === activeProjection.responseMessageId,
1428
- ),
1538
+ base: findMessage(state.messages, activeProjection.responseMessageId),
1429
1539
  chunks: payload.chunks,
1430
1540
  index: event.index,
1431
1541
  })
@@ -1434,7 +1544,7 @@ export function reduceAIState<M extends UIMessage>(
1434
1544
  messages: upsertResponse({
1435
1545
  messages: state.messages,
1436
1546
  message: progress.message,
1437
- inputId: state.activeGeneration.messageId,
1547
+ inputId: generation.messageId,
1438
1548
  }),
1439
1549
  activeProjection: {
1440
1550
  ...activeProjection,
@@ -1521,14 +1631,20 @@ export function reduceAIState<M extends UIMessage>(
1521
1631
  : null
1522
1632
  const shouldProject =
1523
1633
  projection !== null &&
1524
- (projection.baseMessage !== undefined || projection.batches.length > 0)
1634
+ (projection.base !== undefined || projection.batches.length > 0)
1525
1635
  return {
1526
1636
  ...state,
1527
1637
  messages: shouldProject
1528
1638
  ? upsertMessage(
1529
1639
  state.messages,
1530
1640
  interruptUIMessage(
1531
- projectedMessage(projection),
1641
+ projectedMessage({
1642
+ projection,
1643
+ current: findMessage(
1644
+ state.messages,
1645
+ projection.responseMessageId,
1646
+ ),
1647
+ }),
1532
1648
  `Generation failed before tool completion: ${payload.error}`,
1533
1649
  ),
1534
1650
  )
@@ -1832,6 +1948,9 @@ export function reduceAIState<M extends UIMessage>(
1832
1948
  if (projection?.generationId !== payload.generationId) {
1833
1949
  const next = {
1834
1950
  ...state,
1951
+ messages: projectMessageChunks(state.messages, payload.messageId, [
1952
+ toolCalledChunk(payload),
1953
+ ]),
1835
1954
  tools: reduceToolActivity(state.tools, {
1836
1955
  type: 'ai.tool.called',
1837
1956
  payload,
@@ -1839,16 +1958,51 @@ export function reduceAIState<M extends UIMessage>(
1839
1958
  }
1840
1959
  return { ...next, status: lifecycleStatus(state, next) }
1841
1960
  }
1842
- const activeProjection: ActiveGenerationProjection<M> = {
1961
+ const activeProjection: ActiveGenerationProjection = {
1843
1962
  ...projection,
1963
+ cursor: null,
1844
1964
  toolEvents: [
1845
1965
  ...projection.toolEvents,
1846
1966
  { index: event.index, type: 'ai.tool.called', payload },
1847
1967
  ],
1848
1968
  }
1969
+ const base = findMessage(state.messages, payload.messageId)
1970
+ const paused = state.pausedGenerationIds?.[payload.generationId]
1971
+ const cursor = projection.cursor
1972
+ const incremental =
1973
+ base && cursor && !paused && event.index > cursor.throughIndex
1974
+ const projected = incremental
1975
+ ? {
1976
+ ...(hasToolCall({ message: base, call: payload })
1977
+ ? { message: base, cursor: cursor.stream }
1978
+ : projectUIMessageStream({
1979
+ messageId: payload.messageId,
1980
+ base,
1981
+ cursor: cursor.stream,
1982
+ chunks: [toolCalledChunk(payload)],
1983
+ })),
1984
+ throughIndex: event.index,
1985
+ }
1986
+ : projectedMessageStream({
1987
+ projection: activeProjection,
1988
+ current: base,
1989
+ })
1990
+ activeProjection.cursor = paused
1991
+ ? null
1992
+ : { throughIndex: projected.throughIndex, stream: projected.cursor }
1849
1993
  const next = {
1850
1994
  ...state,
1851
1995
  activeProjection,
1996
+ messages: upsertResponse({
1997
+ messages: state.messages,
1998
+ message: paused
1999
+ ? projectedResponse({ state, projection: activeProjection })
2000
+ : projected.message,
2001
+ inputId:
2002
+ state.activeGeneration?.messageId ??
2003
+ state.active?.input.id ??
2004
+ payload.messageId,
2005
+ }),
1852
2006
  tools: projectedTools(activeProjection),
1853
2007
  }
1854
2008
  return { ...next, status: lifecycleStatus(state, next) }
@@ -1903,7 +2057,7 @@ export function reduceAIState<M extends UIMessage>(
1903
2057
  }
1904
2058
  return { ...next, status: lifecycleStatus(state, next) }
1905
2059
  }
1906
- const activeProjection: ActiveGenerationProjection<M> = {
2060
+ const activeProjection: ActiveGenerationProjection = {
1907
2061
  ...projection,
1908
2062
  cursor: null,
1909
2063
  toolEvents: [
@@ -2136,10 +2290,10 @@ export function reduceAIState<M extends UIMessage>(
2136
2290
  if (state.activeGeneration?.generationId !== payload.generationId) {
2137
2291
  return state
2138
2292
  }
2139
- return {
2140
- ...state,
2141
- compaction: { status: 'running', ...payload },
2142
- }
2293
+ return addCompactionPart(
2294
+ { ...state, compaction: { status: 'running', ...payload } },
2295
+ payload,
2296
+ )
2143
2297
  }
2144
2298
  case 'ai.compaction.completed': {
2145
2299
  const payload = event.payload as CompactionCompletedPayload<M>
@@ -2150,20 +2304,41 @@ export function reduceAIState<M extends UIMessage>(
2150
2304
  ) {
2151
2305
  return state
2152
2306
  }
2153
- const compaction: CompactionState<M> = {
2307
+ const {
2308
+ messages: _messages,
2309
+ retainedMessageIds: _retained,
2310
+ ...summary
2311
+ } = payload
2312
+ const compaction: CompactionState = {
2154
2313
  status: 'completed',
2155
- ...payload,
2314
+ ...summary,
2156
2315
  throughIndex:
2157
2316
  payload.throughIndex ?? state.activeProjection!.promptThroughIndex!,
2158
2317
  }
2159
- return {
2160
- ...state,
2161
- compaction,
2162
- activeProjection: state.activeProjection && {
2163
- ...state.activeProjection,
2164
- baseCompaction: compaction,
2318
+ const projection = state.activeProjection
2319
+ return settleCompactionPart({
2320
+ state: {
2321
+ ...state,
2322
+ compaction,
2323
+ activeProjection: projection && {
2324
+ ...projection,
2325
+ baseCompaction: compaction,
2326
+ // A new base identity records the completed part as the step's
2327
+ // starting point, so a later cutoff cannot revert it to running.
2328
+ ...(projection.base === undefined
2329
+ ? {}
2330
+ : { base: { ...projection.base } }),
2331
+ },
2165
2332
  },
2166
- }
2333
+ generationId: payload.generationId,
2334
+ data: {
2335
+ status: 'completed',
2336
+ ...(summary.summary === undefined
2337
+ ? {}
2338
+ : { summary: summary.summary }),
2339
+ ...(summary.usage === undefined ? {} : { usage: summary.usage }),
2340
+ },
2341
+ })
2167
2342
  }
2168
2343
  default:
2169
2344
  return state
@@ -2173,35 +2348,12 @@ export function reduceAIState<M extends UIMessage>(
2173
2348
  /** Derive AI SDK UI messages directly from an A2 event history. */
2174
2349
  export function deriveUIMessages<M extends UIMessage = UIMessage>(
2175
2350
  history: readonly { type: string; payload: unknown; index: number }[],
2176
- ): M[] {
2351
+ ): TranscriptMessage<M>[] {
2177
2352
  let state = initialState<M>()
2178
2353
  for (const event of history) state = reduceAIState(state, event)
2179
2354
  return state.messages
2180
2355
  }
2181
2356
 
2182
- /** Build the standard AI state projection for any contract containing the protocol. */
2183
- export function createReducer<D extends EventDefs>(options: {
2184
- contract: Contract<D>
2185
- name?: string
2186
- fold?: Reducer<D, AIState<AIMessageFrom<D>>>['fold']
2187
- stateSchema?: StandardSchemaV1<unknown, AIState<AIMessageFrom<D>>>
2188
- }): AIStoredReducer<D, AIState<AIMessageFrom<D>>> {
2189
- type M = AIMessageFrom<D>
2190
- const reducer = options.contract
2191
- .reducer({
2192
- name: options.name ?? 'a2.ai.state.v22',
2193
- initialState: initialState<M>(),
2194
- ...(options.stateSchema === undefined
2195
- ? {}
2196
- : { stateSchema: options.stateSchema }),
2197
- })
2198
- .fold(options.fold ?? ((state, event) => reduceAIState(state, event)))
2199
- return storedAIReducer({
2200
- ...reducer,
2201
- [clientStateProjection]: projectAIClientState,
2202
- })
2203
- }
2204
-
2205
2357
  type CommandInput<C> = { type: 'ai.control.requested'; payload: C; id?: string }
2206
2358
  type ControlInput<
2207
2359
  M extends UIMessage,
@@ -2338,22 +2490,24 @@ export const inputs: AIInputs = {
2338
2490
  * Read the durable handler context ambient to the current AI tool
2339
2491
  * execution. A2 runs each local tool's `execute` inside this scope, so
2340
2492
  * plain AI SDK `tool()` definitions reach their `event`, `attempt`,
2341
- * `session`, and `signal` without an A2-specific wrapper. The agent (or
2342
- * contract) argument carries the event types and is verified against
2343
- * the executing server's contract.
2493
+ * `session`, and `signal` without an A2-specific wrapper. Pass the
2494
+ * agent's contract to type application events; it is verified against
2495
+ * the executing agent.
2344
2496
  */
2345
- export function handlerContext<D extends AgentToolEventDefs>(
2346
- agentOrContract: Contract<D> | { readonly contract: Contract<D> },
2497
+ export function handlerContext<D extends AgentToolEventDefs = AIEventDefs>(
2498
+ contract?: Contract<D>,
2347
2499
  ): AgentToolContext<D> {
2348
- const contract =
2349
- 'contract' in agentOrContract ? agentOrContract.contract : agentOrContract
2350
2500
  const scope = ambientToolScopeStorage()?.getStore()
2351
2501
  if (scope === undefined) {
2352
2502
  throw new TypeError(
2353
- `handlerContext('${contract.name}') requires an ambient A2 tool execution: call it inside a local tool's execute, or wrap direct tool tests with runWithHandlerContext`,
2503
+ "handlerContext() requires an ambient A2 tool execution: call it inside a local tool's execute, or wrap direct tool tests with runWithHandlerContext",
2354
2504
  )
2355
2505
  }
2356
- if (scope.contract !== undefined && scope.contract.name !== contract.name) {
2506
+ if (
2507
+ contract !== undefined &&
2508
+ scope.contract !== undefined &&
2509
+ scope.contract.name !== contract.name
2510
+ ) {
2357
2511
  throw new TypeError(
2358
2512
  `handlerContext('${contract.name}') was called during a tool execution for agent '${scope.contract.name}'`,
2359
2513
  )
@@ -2361,14 +2515,23 @@ export function handlerContext<D extends AgentToolEventDefs>(
2361
2515
  return scope.context as AgentToolContext<D>
2362
2516
  }
2363
2517
 
2518
+ /** The built-in AI reducer's name, and so its checkpoint identity. */
2519
+ export const AI_STATE_REDUCER = 'a2.ai.state.v26'
2520
+
2364
2521
  /**
2365
- * Define an agent: built-in AI protocol + application events and one state
2366
- * reducer. The result is isomorphic.
2522
+ * The built-in AI protocol plus application events, as a core contract whose
2523
+ * reducer folds `AIState`. Isomorphic: share it when a client needs
2524
+ * application events or extended state; otherwise `agent({ name })` on the
2525
+ * server builds one and clients use the built-in protocol.
2367
2526
  */
2368
- export function agent<
2527
+ export function agentContract<
2369
2528
  M extends UIMessage = UIMessage,
2370
2529
  E extends EventDefs = Record<never, never>,
2371
- >(options: AgentOptions<M, E>): AgentDefinition<M, AIEventDefs<M> & E> {
2530
+ X extends object = Record<never, never>,
2531
+ >(
2532
+ options: AgentContractOptions<M, E, X>,
2533
+ ): AgentContract<M, AIEventDefs<M> & E, X> {
2534
+ type D = AIEventDefs<M> & E
2372
2535
  const builtIns = createEvents(
2373
2536
  options.messageSchema === undefined
2374
2537
  ? undefined
@@ -2382,25 +2545,46 @@ export function agent<
2382
2545
  )
2383
2546
  }
2384
2547
  }
2385
- const agentContract = createContract({
2548
+ const vocabulary = createContract({
2386
2549
  name: options.name,
2387
- events: { ...builtIns, ...extensions },
2550
+ events: { ...builtIns, ...extensions } as D,
2388
2551
  })
2389
- const reducer = agentContract
2390
- .reducer({
2391
- name: options.reducerName ?? 'a2.ai.state.v22',
2392
- initialState: initialState<M>(),
2393
- })
2394
- .fold((state, event) => reduceAIState(state, event))
2395
-
2396
- const definition = {
2397
- contract: agentContract,
2398
- reducer: storedAIReducer({
2399
- ...reducer,
2400
- [clientStateProjection]: projectAIClientState<M>,
2401
- }),
2552
+ const app = options.reducer as AppReducer<D, X> | undefined
2553
+ const aiState = initialState<M>()
2554
+ for (const key of Object.keys(app?.initialState ?? {})) {
2555
+ if (isAIStateKey(key))
2556
+ throw new TypeError(`agent state '${key}' conflicts with AIState`)
2402
2557
  }
2403
- return definition as unknown as AgentDefinition<M, AIEventDefs<M> & E>
2558
+ const reducer = makeReducer({
2559
+ contract: vocabulary,
2560
+ // Application checkpoints embed AI state, so they advance with it.
2561
+ name:
2562
+ app === undefined ? AI_STATE_REDUCER : `${app.name}+${AI_STATE_REDUCER}`,
2563
+ initialState: { ...aiState, ...app?.initialState } as AIState<M> & X,
2564
+ }).fold((state, event) => {
2565
+ const next = reduceAIState(state, event) as AIState<M> & X
2566
+ if (app === undefined) return next
2567
+ const before = appSlice<X>(state)
2568
+ const after = app.fold(before, event as ContractEvent<D>)
2569
+ return after === before ? next : withAppSlice(next, after)
2570
+ })
2571
+ const shared = createContract({
2572
+ name: options.name,
2573
+ events: vocabulary.events,
2574
+ reducer: withAIStorage(
2575
+ withReducerRuntime({
2576
+ reducer,
2577
+ project: projectAIClientState<M> as unknown as (
2578
+ state: AIState<M> & X,
2579
+ ) => AIState<M> & X,
2580
+ }),
2581
+ ),
2582
+ })
2583
+ registerAgentReducer(
2584
+ shared.reducer,
2585
+ app as unknown as AppReducer<EventDefs, object> | undefined,
2586
+ )
2587
+ return shared as unknown as AgentContract<M, AIEventDefs<M> & E, X>
2404
2588
  }
2405
2589
 
2406
2590
  export type {