experimental-a2 0.16.1 → 0.17.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 (333) hide show
  1. package/CHANGELOG.md +352 -0
  2. package/dist/{actor-ohPC-81x.d.ts → actor-DBbe5xeE.d.ts} +21 -11
  3. package/dist/actor-DBbe5xeE.d.ts.map +1 -0
  4. package/dist/actor-client.d.ts +2 -5
  5. package/dist/actor-client.d.ts.map +1 -1
  6. package/dist/actor-client.js +13 -4
  7. package/dist/actor-client.js.map +1 -1
  8. package/dist/actor-react.d.ts +5 -3
  9. package/dist/actor-react.d.ts.map +1 -1
  10. package/dist/actor-react.js +20 -10
  11. package/dist/actor-react.js.map +1 -1
  12. package/dist/{actor-shared-B5tJfzt-.js → actor-shared-t7w8xIKP.js} +12 -7
  13. package/dist/actor-shared-t7w8xIKP.js.map +1 -0
  14. package/dist/actor.d.ts +1 -1
  15. package/dist/actor.js +42 -23
  16. package/dist/actor.js.map +1 -1
  17. package/dist/{ai-DgOBltJ_.js → ai-UrWfKeuv.js} +621 -216
  18. package/dist/ai-UrWfKeuv.js.map +1 -0
  19. package/dist/ai-client.d.ts +27 -0
  20. package/dist/ai-client.d.ts.map +1 -0
  21. package/dist/ai-client.js +29 -0
  22. package/dist/ai-client.js.map +1 -0
  23. package/dist/ai-react.d.ts +96 -0
  24. package/dist/ai-react.d.ts.map +1 -0
  25. package/dist/ai-react.js +142 -0
  26. package/dist/ai-react.js.map +1 -0
  27. package/dist/ai-server.browser.js +2 -2
  28. package/dist/ai-server.browser.js.map +1 -1
  29. package/dist/ai-server.d.ts +80 -36
  30. package/dist/ai-server.d.ts.map +1 -1
  31. package/dist/ai-server.js +432 -258
  32. package/dist/ai-server.js.map +1 -1
  33. package/dist/{ai-CrEf6p_W.d.ts → ai-vpGtcaCB.d.ts} +114 -100
  34. package/dist/ai-vpGtcaCB.d.ts.map +1 -0
  35. package/dist/ai.d.ts +2 -2
  36. package/dist/ai.js +2 -2
  37. package/dist/{client-BYzHjkwU.js → client-COaiaDub.js} +447 -80
  38. package/dist/client-COaiaDub.js.map +1 -0
  39. package/dist/{client-CzyacQpJ.d.ts → client-qRnRUzBS.d.ts} +14 -13
  40. package/dist/client-qRnRUzBS.d.ts.map +1 -0
  41. package/dist/client.d.ts +4 -2
  42. package/dist/client.js +1 -1
  43. package/dist/{contract-CKRg_E4q.js → contract-BYKASlCV.js} +12 -21
  44. package/dist/contract-BYKASlCV.js.map +1 -0
  45. package/dist/{idempotent-replay-DVOlyYbx.js → idempotent-replay-JdgMQRRm.js} +2 -2
  46. package/dist/{idempotent-replay-DVOlyYbx.js.map → idempotent-replay-JdgMQRRm.js.map} +1 -1
  47. package/dist/index.d.ts +6 -5
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +4 -3
  50. package/dist/{errors-DvhSXnxk.d.ts → initial-snapshot-BrLso5x_.d.ts} +7 -3
  51. package/dist/initial-snapshot-BrLso5x_.d.ts.map +1 -0
  52. package/dist/{internal-Dq2qYxou.js → internal-CbVvEwad.js} +23 -2
  53. package/dist/internal-CbVvEwad.js.map +1 -0
  54. package/dist/otel.d.ts +1 -1
  55. package/dist/react-BbJ-g6Uu.d.ts +86 -0
  56. package/dist/react-BbJ-g6Uu.d.ts.map +1 -0
  57. package/dist/react.d.ts +2 -122
  58. package/dist/react.js +107 -100
  59. package/dist/react.js.map +1 -1
  60. package/dist/reducer-BAXc8JyF.js +87 -0
  61. package/dist/reducer-BAXc8JyF.js.map +1 -0
  62. package/dist/{reducer-otzHjuJj.d.ts → reducer-BYu6DT7h.d.ts} +45 -29
  63. package/dist/reducer-BYu6DT7h.d.ts.map +1 -0
  64. package/dist/reducer-runtime-DQwz_J59.js +28 -0
  65. package/dist/reducer-runtime-DQwz_J59.js.map +1 -0
  66. package/dist/scheduler-qstash.d.ts +3 -3
  67. package/dist/scheduler-qstash.d.ts.map +1 -1
  68. package/dist/scheduler-qstash.js +33 -10
  69. package/dist/scheduler-qstash.js.map +1 -1
  70. package/dist/{scheduler-task-BpzhPnRS.js → scheduler-task-X1SToDjP.js} +18 -3
  71. package/dist/scheduler-task-X1SToDjP.js.map +1 -0
  72. package/dist/scheduler-vercel.d.ts +4 -9
  73. package/dist/scheduler-vercel.d.ts.map +1 -1
  74. package/dist/scheduler-vercel.js +25 -44
  75. package/dist/scheduler-vercel.js.map +1 -1
  76. package/dist/{server-DbD7IJVK.d.ts → server-CGM8WzRG.d.ts} +87 -26
  77. package/dist/server-CGM8WzRG.d.ts.map +1 -0
  78. package/dist/{server-BD5ckxZb.js → server-Rj64fNvX.js} +854 -202
  79. package/dist/server-Rj64fNvX.js.map +1 -0
  80. package/dist/server.d.ts +4 -4
  81. package/dist/server.js +2 -2
  82. package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
  83. package/dist/session-clear-DGI4jTQ5.js.map +1 -0
  84. package/dist/session-clear-Dshpuhff.d.ts +12 -0
  85. package/dist/session-clear-Dshpuhff.d.ts.map +1 -0
  86. package/dist/{store-DyZM6fS5.d.ts → store-Gpkq7WBo.d.ts} +44 -3
  87. package/dist/store-Gpkq7WBo.d.ts.map +1 -0
  88. package/dist/store-N8PXxDAS.js.map +1 -1
  89. package/dist/store-memory.d.ts +1 -1
  90. package/dist/store-memory.d.ts.map +1 -1
  91. package/dist/store-memory.js +182 -69
  92. package/dist/store-memory.js.map +1 -1
  93. package/dist/{store-polling-CmxUbV93.js → store-polling-4_y8lETm.js} +21 -4
  94. package/dist/store-polling-4_y8lETm.js.map +1 -0
  95. package/dist/store-postgres.d.ts +1 -1
  96. package/dist/store-postgres.d.ts.map +1 -1
  97. package/dist/store-postgres.js +399 -108
  98. package/dist/store-postgres.js.map +1 -1
  99. package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-Q1C-k2dd.js} +3 -3
  100. package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-Q1C-k2dd.js.map} +1 -1
  101. package/dist/store-redis-http.d.ts +1 -1
  102. package/dist/store-redis-http.d.ts.map +1 -1
  103. package/dist/store-redis-http.js +293 -11
  104. package/dist/store-redis-http.js.map +1 -1
  105. package/dist/store-redis-notify-tqWNnMRu.js +2374 -0
  106. package/dist/store-redis-notify-tqWNnMRu.js.map +1 -0
  107. package/dist/store-redis.d.ts +1 -1
  108. package/dist/store-redis.d.ts.map +1 -1
  109. package/dist/store-redis.js +11 -11
  110. package/dist/store-redis.js.map +1 -1
  111. package/dist/store-sqlite.d.ts +1 -1
  112. package/dist/store-sqlite.d.ts.map +1 -1
  113. package/dist/store-sqlite.js +166 -32
  114. package/dist/store-sqlite.js.map +1 -1
  115. package/dist/stream-buffer-Cx7AAPGq.js +23 -0
  116. package/dist/stream-buffer-Cx7AAPGq.js.map +1 -0
  117. package/dist/{telemetry-B5jzpy6y.d.ts → telemetry-cZH-cibj.d.ts} +2 -2
  118. package/dist/telemetry-cZH-cibj.d.ts.map +1 -0
  119. package/dist/{wire-BO5wWCb1.js → wire-DjfHOvdW.js} +132 -10
  120. package/dist/wire-DjfHOvdW.js.map +1 -0
  121. package/docs/01-quickstart.mdx +2 -2
  122. package/docs/actors/01-introduction.mdx +4 -0
  123. package/docs/actors/04-routes.mdx +14 -1
  124. package/docs/concepts/01-contracts.mdx +95 -0
  125. package/docs/concepts/04-state.mdx +3 -3
  126. package/docs/guides/01-timers.mdx +22 -14
  127. package/docs/guides/02-cancellation.mdx +1 -1
  128. package/docs/guides/03-react.mdx +268 -328
  129. package/docs/guides/04-local-first.mdx +32 -73
  130. package/docs/guides/05-production.mdx +40 -27
  131. package/docs/guides/06-ai-agents.mdx +671 -581
  132. package/docs/guides/09-presence.mdx +3 -7
  133. package/docs/guides/10-transports.mdx +5 -5
  134. package/docs/index.mdx +10 -18
  135. package/docs/reference/01-api.mdx +656 -194
  136. package/docs/reference/02-errors.mdx +2 -1
  137. package/examples/playground/DEPLOY.md +52 -6
  138. package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
  139. package/examples/playground/app/a0/[appId]/page.tsx +48 -0
  140. package/examples/playground/app/a0/a0.css +818 -0
  141. package/examples/playground/app/a0/app-list.tsx +211 -0
  142. package/examples/playground/app/a0/app-view.tsx +55 -0
  143. package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
  144. package/examples/playground/app/a0/apps/[appId]/route.ts +23 -0
  145. package/examples/playground/app/a0/apps.test.ts +116 -0
  146. package/examples/playground/app/a0/apps.ts +87 -0
  147. package/examples/playground/app/a0/chat.tsx +364 -0
  148. package/examples/playground/app/a0/columns.tsx +109 -0
  149. package/examples/playground/app/a0/context-menu.tsx +116 -0
  150. package/examples/playground/app/a0/cookies.ts +46 -0
  151. package/examples/playground/app/a0/crash-on-request.ts +72 -0
  152. package/examples/playground/app/a0/crashes/route.ts +3 -0
  153. package/examples/playground/app/a0/crashes.test.ts +33 -0
  154. package/examples/playground/app/a0/crashes.ts +28 -0
  155. package/examples/playground/app/a0/devtools.tsx +164 -0
  156. package/examples/playground/app/a0/events/route.ts +6 -0
  157. package/examples/playground/app/a0/flags.ts +30 -0
  158. package/examples/playground/app/a0/layout.tsx +38 -0
  159. package/examples/playground/app/a0/list/route.ts +10 -0
  160. package/examples/playground/app/a0/model.test.ts +56 -0
  161. package/examples/playground/app/a0/model.ts +58 -0
  162. package/examples/playground/app/a0/page.tsx +14 -0
  163. package/examples/playground/app/a0/preview-pane.tsx +145 -0
  164. package/examples/playground/app/a0/preview.test.ts +53 -0
  165. package/examples/playground/app/a0/preview.ts +26 -0
  166. package/examples/playground/app/a0/sandbox-log.ts +69 -0
  167. package/examples/playground/app/a0/sandbox.ts +191 -0
  168. package/examples/playground/app/a0/sandboxes/scheduler/route.ts +6 -0
  169. package/examples/playground/app/a0/scheduler/route.ts +7 -0
  170. package/examples/playground/app/a0/server.ts +158 -0
  171. package/examples/playground/app/a0/session.ts +47 -0
  172. package/examples/playground/app/a0/shell.tsx +150 -0
  173. package/examples/playground/app/a0/short/events/route.ts +9 -0
  174. package/examples/playground/app/a0/splitter.tsx +120 -0
  175. package/examples/playground/app/agent/[agentId]/agent-client.tsx +144 -174
  176. package/examples/playground/app/agent/[agentId]/command-receipt.test.tsx +65 -0
  177. package/examples/playground/app/agent/[agentId]/command-receipt.tsx +27 -0
  178. package/examples/playground/app/agent/[agentId]/compaction-event.tsx +25 -24
  179. package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +119 -0
  180. package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +208 -165
  181. package/examples/playground/app/agent/[agentId]/page.tsx +2 -16
  182. package/examples/playground/app/agent/events/route.ts +5 -2
  183. package/examples/playground/app/agent/ingress.test.ts +98 -0
  184. package/examples/playground/app/agent/model-input.test.tsx +58 -0
  185. package/examples/playground/app/agent/model-input.tsx +115 -0
  186. package/examples/playground/app/agent/model.test.ts +73 -0
  187. package/examples/playground/app/agent/model.ts +58 -26
  188. package/examples/playground/app/agent/new-agent-session.tsx +37 -19
  189. package/examples/playground/app/agent/page.tsx +4 -4
  190. package/examples/playground/app/agent/server.ts +15 -14
  191. package/examples/playground/app/agent/session.ts +5 -6
  192. package/examples/playground/app/agent/telemetry.test.ts +168 -0
  193. package/examples/playground/app/agent/telemetry.ts +30 -0
  194. package/examples/playground/app/agent/trace.test.ts +23 -0
  195. package/examples/playground/app/agent/trace.ts +18 -0
  196. package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +11 -11
  197. package/examples/playground/app/canvas/[canvasId]/page.tsx +1 -2
  198. package/examples/playground/app/canvas/[canvasId]/session.ts +2 -2
  199. package/examples/playground/app/canvas/model.ts +53 -53
  200. package/examples/playground/app/chat/[chatId]/agent-state.test.ts +176 -0
  201. package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +16 -1
  202. package/examples/playground/app/chat/[chatId]/chat-client.tsx +55 -77
  203. package/examples/playground/app/chat/[chatId]/chat-view.ts +37 -3
  204. package/examples/playground/app/chat/[chatId]/page.tsx +7 -15
  205. package/examples/playground/app/chat/[chatId]/session.ts +2 -19
  206. package/examples/playground/app/chat/agent/route.ts +9 -0
  207. package/examples/playground/app/chat/model.test.ts +46 -119
  208. package/examples/playground/app/chat/model.ts +82 -128
  209. package/examples/playground/app/chat/scheduler/route.ts +2 -2
  210. package/examples/playground/app/chat/server.test.ts +108 -0
  211. package/examples/playground/app/chat/server.ts +69 -73
  212. package/examples/playground/app/components/activity-feed.tsx +30 -1
  213. package/examples/playground/app/components/activity-history.test.tsx +35 -0
  214. package/examples/playground/app/components/demo-icons.tsx +89 -0
  215. package/examples/playground/app/components/demos.tsx +33 -0
  216. package/examples/playground/app/components/fast-link.test.tsx +58 -0
  217. package/examples/playground/app/components/fast-link.tsx +55 -0
  218. package/examples/playground/app/components/site-header.tsx +96 -0
  219. package/examples/playground/app/counter/counter-client.tsx +3 -3
  220. package/examples/playground/app/counter/model.test.ts +3 -3
  221. package/examples/playground/app/counter/model.ts +11 -21
  222. package/examples/playground/app/counter/page.tsx +2 -15
  223. package/examples/playground/app/counter/session.ts +2 -2
  224. package/examples/playground/app/documents/[documentId]/code-editor.tsx +12 -1
  225. package/examples/playground/app/documents/[documentId]/document-client.tsx +10 -11
  226. package/examples/playground/app/documents/[documentId]/page.tsx +1 -2
  227. package/examples/playground/app/documents/[documentId]/session.ts +2 -2
  228. package/examples/playground/app/documents/model.ts +27 -27
  229. package/examples/playground/app/globals.css +420 -207
  230. package/examples/playground/app/initial-state.test.tsx +54 -0
  231. package/examples/playground/app/layout.tsx +7 -23
  232. package/examples/playground/app/orders/[orderId]/page.tsx +1 -2
  233. package/examples/playground/app/orders/[orderId]/session.ts +2 -2
  234. package/examples/playground/app/orders/model.ts +49 -49
  235. package/examples/playground/app/page.tsx +13 -102
  236. package/examples/playground/app/recovery/[recoveryId]/page.tsx +1 -2
  237. package/examples/playground/app/recovery/[recoveryId]/session.ts +2 -2
  238. package/examples/playground/app/recovery/model.ts +30 -30
  239. package/examples/playground/instrumentation.ts +5 -0
  240. package/examples/playground/package.json +10 -2
  241. package/examples/playground/scripts/a0-template.ts +77 -0
  242. package/examples/playground/vercel.json +18 -0
  243. package/package.json +3 -1
  244. package/src/actor-client.ts +16 -4
  245. package/src/actor-react.ts +25 -11
  246. package/src/actor-shared.ts +21 -6
  247. package/src/actor.ts +76 -31
  248. package/src/ai-agent-actions.ts +219 -0
  249. package/src/ai-agent.ts +2343 -0
  250. package/src/ai-app-state.ts +91 -0
  251. package/src/ai-client-state.ts +6 -3
  252. package/src/ai-client.ts +62 -0
  253. package/src/ai-context-schema.ts +25 -5
  254. package/src/ai-context.ts +90 -40
  255. package/src/ai-control-server.ts +92 -72
  256. package/src/ai-coordinator.ts +23 -22
  257. package/src/ai-message-base.ts +209 -0
  258. package/src/ai-message-projection.ts +262 -64
  259. package/src/ai-progress-batches.ts +26 -0
  260. package/src/ai-projector.ts +95 -14
  261. package/src/ai-react.ts +146 -0
  262. package/src/ai-server.browser.ts +2 -2
  263. package/src/ai-server.ts +20 -1964
  264. package/src/ai-stored-state.ts +48 -163
  265. package/src/ai.ts +339 -155
  266. package/src/bootstrap-stream.ts +72 -0
  267. package/src/bounded-cache.ts +55 -0
  268. package/src/client-render-session.ts +49 -0
  269. package/src/client.ts +688 -151
  270. package/src/contract.ts +92 -42
  271. package/src/deterministic-id.ts +37 -0
  272. package/src/equal-json.ts +32 -0
  273. package/src/errors.ts +13 -1
  274. package/src/index.ts +5 -1
  275. package/src/initial-snapshot.ts +39 -0
  276. package/src/internal.ts +45 -0
  277. package/src/postgres-notification-scope.ts +14 -3
  278. package/src/react.ts +171 -206
  279. package/src/redis-http-reply.ts +256 -0
  280. package/src/redis-http-stream.ts +73 -0
  281. package/src/redis-http-subscriptions.ts +13 -5
  282. package/src/reducer-runtime.ts +52 -0
  283. package/src/reducer.ts +92 -10
  284. package/src/scheduler-qstash.ts +49 -20
  285. package/src/scheduler-task.ts +34 -2
  286. package/src/scheduler-vercel.ts +32 -84
  287. package/src/server-fetch.ts +139 -35
  288. package/src/server.ts +957 -237
  289. package/src/session-clear.ts +23 -0
  290. package/src/session-socket.ts +154 -49
  291. package/src/sse.ts +71 -4
  292. package/src/store-memory.ts +220 -78
  293. package/src/store-polling.ts +18 -4
  294. package/src/store-postgres.ts +461 -114
  295. package/src/store-redis-core.ts +1260 -434
  296. package/src/store-redis-http.ts +18 -2
  297. package/src/store-redis-notify.ts +260 -85
  298. package/src/store-redis.ts +19 -9
  299. package/src/store-sqlite.ts +259 -45
  300. package/src/store.ts +46 -1
  301. package/src/stream-buffer.ts +43 -0
  302. package/src/telemetry.ts +13 -1
  303. package/src/wire.ts +241 -13
  304. package/dist/actor-ohPC-81x.d.ts.map +0 -1
  305. package/dist/actor-shared-B5tJfzt-.js.map +0 -1
  306. package/dist/ai-CrEf6p_W.d.ts.map +0 -1
  307. package/dist/ai-DgOBltJ_.js.map +0 -1
  308. package/dist/client-BYzHjkwU.js.map +0 -1
  309. package/dist/client-CzyacQpJ.d.ts.map +0 -1
  310. package/dist/contract-CKRg_E4q.js.map +0 -1
  311. package/dist/errors-DCk6ch5n.js.map +0 -1
  312. package/dist/errors-DvhSXnxk.d.ts.map +0 -1
  313. package/dist/internal-Dq2qYxou.js.map +0 -1
  314. package/dist/react.d.ts.map +0 -1
  315. package/dist/reducer-DEMjEY_O.js +0 -29
  316. package/dist/reducer-DEMjEY_O.js.map +0 -1
  317. package/dist/reducer-otzHjuJj.d.ts.map +0 -1
  318. package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
  319. package/dist/server-BD5ckxZb.js.map +0 -1
  320. package/dist/server-DbD7IJVK.d.ts.map +0 -1
  321. package/dist/store-DyZM6fS5.d.ts.map +0 -1
  322. package/dist/store-polling-CmxUbV93.js.map +0 -1
  323. package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
  324. package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
  325. package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
  326. package/dist/validate-XKT4FSNn.js +0 -30
  327. package/dist/validate-XKT4FSNn.js.map +0 -1
  328. package/dist/wire-BO5wWCb1.js.map +0 -1
  329. package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
  330. package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
  331. package/examples/playground/app/agent/compaction-settings.ts +0 -49
  332. package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
  333. package/examples/playground/app/agent/compaction-timeline.ts +0 -208
@@ -1,8 +1,135 @@
1
- import { n as validateSync, t as assertSyncSchema } from "./validate-XKT4FSNn.js";
2
- import { t as clientStateProjection } from "./reducer-DEMjEY_O.js";
3
- import { t as contract } from "./contract-CKRg_E4q.js";
1
+ import { a as reducer, o as assertSyncSchema, s as validateSync } from "./reducer-BAXc8JyF.js";
2
+ import { t as contract } from "./contract-BYKASlCV.js";
4
3
  import { t as idempotentId } from "./idempotent-id-BRJVeylj.js";
5
- import { l as ambientToolScopeStorage } from "./internal-Dq2qYxou.js";
4
+ import { i as withReducerRuntime } from "./reducer-runtime-DQwz_J59.js";
5
+ import { u as ambientToolScopeStorage } from "./internal-CbVvEwad.js";
6
+ //#region src/equal-json.ts
7
+ const sameValue = (left, right) => {
8
+ if (Object.is(left, right)) return true;
9
+ if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) return false;
10
+ const prototype = Object.getPrototypeOf(left);
11
+ if (prototype !== Object.getPrototypeOf(right) || prototype !== Object.prototype && prototype !== Array.prototype && prototype !== null) return false;
12
+ if (Array.isArray(left) && (!Array.isArray(right) || left.length !== right.length)) return false;
13
+ const keys = Reflect.ownKeys(left);
14
+ return keys.length === Reflect.ownKeys(right).length && keys.every((key) => Object.hasOwn(right, key) && sameValue(Reflect.get(left, key), Reflect.get(right, key)));
15
+ };
16
+ //#endregion
17
+ //#region src/ai-message-base.ts
18
+ const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19
+ const assertFields = (fields) => {
20
+ if (fields === void 0) return;
21
+ if (!record(fields)) throw new TypeError("invalid AI message base fields");
22
+ for (const [key, undo] of Object.entries(fields)) if (!record(undo) || Object.keys(undo).length > 1 || !(Object.keys(undo).length === 0 || "value" in undo || undo["remove"] === true || key === "text" && Number.isSafeInteger(undo["length"]) && undo["length"] >= 0)) throw new TypeError("invalid AI message base field");
23
+ };
24
+ /** Checks decoded recovery data; `restoreMessageBase` trusts this shape. */
25
+ function assertMessageBase(value) {
26
+ if (!record(value) || !Number.isSafeInteger(value["partCount"]) || value["partCount"] < 0 || value["partCount"] > 4294967294 || Object.keys(value).some((key) => ![
27
+ "partCount",
28
+ "fields",
29
+ "parts"
30
+ ].includes(key)) || value["parts"] !== void 0 && !record(value["parts"])) throw new TypeError("invalid AI message base");
31
+ assertFields(value["fields"]);
32
+ const partCount = value["partCount"];
33
+ for (const [key, undo] of Object.entries(value["parts"] ?? {})) {
34
+ const index = Number(key);
35
+ if (!Number.isSafeInteger(index) || String(index) !== key || index < 0 || index >= partCount || !record(undo) || Object.keys(undo).length !== 1 || !("value" in undo && record(undo["value"]) || "fields" in undo)) throw new TypeError("invalid AI message base part");
36
+ if ("fields" in undo) assertFields(undo["fields"]);
37
+ }
38
+ }
39
+ const restoreFields = (current, fields) => {
40
+ if (fields === void 0) return current;
41
+ const restored = { ...current };
42
+ for (const [key, undo] of Object.entries(fields)) if ("remove" in undo) delete restored[key];
43
+ else if ("length" in undo) {
44
+ const text = current[key];
45
+ if (typeof text !== "string" || undo.length > text.length) return void 0;
46
+ restored[key] = text.slice(0, undo.length);
47
+ } else Object.defineProperty(restored, key, {
48
+ value: undo.value,
49
+ enumerable: true,
50
+ writable: true,
51
+ configurable: true
52
+ });
53
+ return restored;
54
+ };
55
+ /** `undefined` when `base` no longer describes `current`, including a removed message. */
56
+ const restoreMessageBase = (options) => {
57
+ const { current, base } = options;
58
+ if (base === void 0 || current === void 0) return void 0;
59
+ const parts = current.parts.slice(0, base.partCount);
60
+ for (const [key, undo] of Object.entries(base.parts ?? {})) {
61
+ const index = Number(key);
62
+ if ("value" in undo) {
63
+ parts[index] = undo.value;
64
+ continue;
65
+ }
66
+ const part = parts[index];
67
+ const restored = part === void 0 ? void 0 : restoreFields(part, undo.fields);
68
+ if (!restored) return void 0;
69
+ parts[index] = restored;
70
+ }
71
+ if (parts.length !== base.partCount || Object.keys(parts).length !== parts.length) return void 0;
72
+ const { parts: _parts, ...fields } = current;
73
+ const restored = restoreFields(fields, base.fields);
74
+ return restored && {
75
+ ...restored,
76
+ parts
77
+ };
78
+ };
79
+ const updateFields = (options) => {
80
+ const { before, after, undo } = options;
81
+ const original = restoreFields(before, undo);
82
+ if (!original) return void 0;
83
+ return Object.fromEntries([.../* @__PURE__ */ new Set([...Object.keys(original), ...Object.keys(after)])].flatMap((key) => {
84
+ if (Object.hasOwn(original, key) === Object.hasOwn(after, key) && sameValue(original[key], after[key])) return [];
85
+ const value = original[key];
86
+ const next = after[key];
87
+ return [[key, !Object.hasOwn(original, key) ? { remove: true } : key === "text" && typeof value === "string" && typeof next === "string" && next.startsWith(value) ? { length: value.length } : value === void 0 ? {} : { value }]];
88
+ }));
89
+ };
90
+ /** `undefined` when `before` no longer matches `base`, so the recovery must be dropped. */
91
+ const updateMessageBase = (options) => {
92
+ const { base, before, after } = options;
93
+ if (before === after) return base;
94
+ const { parts: beforeParts, ...beforeFields } = before;
95
+ const { parts: afterParts, ...afterFields } = after;
96
+ const fields = updateFields({
97
+ before: beforeFields,
98
+ after: afterFields,
99
+ ...base.fields && { undo: base.fields }
100
+ });
101
+ if (!fields) return void 0;
102
+ const parts = [];
103
+ for (let index = 0; index < base.partCount; index += 1) {
104
+ const previous = beforeParts[index];
105
+ const current = afterParts[index];
106
+ const undo = base.parts?.[index];
107
+ const original = undo && "value" in undo ? undo.value : previous;
108
+ if (!original) return void 0;
109
+ if (previous === current && undo === void 0) continue;
110
+ const originalFields = original;
111
+ const undoFields = undo && "fields" in undo ? undo.fields : void 0;
112
+ if (!current) {
113
+ const value = restoreFields(originalFields, undoFields);
114
+ if (!value) return void 0;
115
+ parts.push([String(index), { value }]);
116
+ continue;
117
+ }
118
+ const changed = updateFields({
119
+ before: originalFields,
120
+ after: current,
121
+ ...undoFields && { undo: undoFields }
122
+ });
123
+ if (!changed) return void 0;
124
+ if (Object.keys(changed).length > 0) parts.push([String(index), { fields: changed }]);
125
+ }
126
+ return {
127
+ partCount: base.partCount,
128
+ ...Object.keys(fields).length > 0 && { fields },
129
+ ...parts.length > 0 && { parts: Object.fromEntries(parts) }
130
+ };
131
+ };
132
+ //#endregion
6
133
  //#region src/parse-partial-json.ts
7
134
  /*!
8
135
  * Adapted from AI SDK 7.0.58, packages/ai/src/util/fix-json.ts.
@@ -284,7 +411,13 @@ const cloneMessage = (messageId, base) => base === void 0 ? {
284
411
  } : {
285
412
  ...base,
286
413
  id: messageId,
287
- parts: base.parts.map((part) => ({ ...part }))
414
+ parts: [...base.parts]
415
+ };
416
+ const editPart = ({ parts, part }) => {
417
+ const index = parts.indexOf(part);
418
+ const next = { ...part };
419
+ parts[index] = next;
420
+ return next;
288
421
  };
289
422
  const currentStepParts = (parts) => {
290
423
  let start = parts.length - 1;
@@ -297,6 +430,10 @@ const findToolPart = (parts, toolCallId) => {
297
430
  if (current) return current;
298
431
  return parts.findLast((part) => isToolPart(part) && part["toolCallId"] === toolCallId);
299
432
  };
433
+ const hasToolCall = ({ message, call }) => {
434
+ const part = findToolPart(message.parts, call.toolCallId);
435
+ return part !== void 0 && part["state"] !== "input-streaming" && sameValue(part["input"], call.input) && (part["callProviderMetadata"] !== void 0 || call.providerMetadata === void 0) && (part["toolMetadata"] !== void 0 || call.toolMetadata === void 0) && (call.providerExecuted === void 0 || part["providerExecuted"] === call.providerExecuted) && (call.title === void 0 || part["title"] === call.title);
436
+ };
300
437
  const findApprovalPart = (parts, approvalId) => parts.find((part) => {
301
438
  const approval = part["approval"];
302
439
  return isToolPart(part) && isRecord$3(approval) && approval["id"] === approvalId;
@@ -313,7 +450,10 @@ const setToolPart = (options) => {
313
450
  toolCallId: options.toolCallId
314
451
  };
315
452
  options.parts.push(part);
316
- }
453
+ } else part = editPart({
454
+ parts: options.parts,
455
+ part
456
+ });
317
457
  part["state"] = options.state;
318
458
  part["input"] = options.input;
319
459
  delete part["output"];
@@ -371,13 +511,22 @@ function projectUIMessageStream(options) {
371
511
  activeText.set(chunk.id, part);
372
512
  parts.push(part);
373
513
  }
514
+ part = editPart({
515
+ parts,
516
+ part
517
+ });
518
+ activeText.set(chunk.id, part);
374
519
  part["text"] = `${typeof part["text"] === "string" ? part["text"] : ""}${chunk.delta}`;
375
520
  setOptional(part, "providerMetadata", chunk.providerMetadata);
376
521
  break;
377
522
  }
378
523
  case "text-end": {
379
- const part = activeText.get(chunk.id);
380
- if (part) {
524
+ const current = activeText.get(chunk.id);
525
+ if (current) {
526
+ const part = editPart({
527
+ parts,
528
+ part: current
529
+ });
381
530
  part["state"] = "done";
382
531
  setOptional(part, "providerMetadata", chunk.providerMetadata);
383
532
  activeText.delete(chunk.id);
@@ -406,13 +555,22 @@ function projectUIMessageStream(options) {
406
555
  activeReasoning.set(chunk.id, part);
407
556
  parts.push(part);
408
557
  }
558
+ part = editPart({
559
+ parts,
560
+ part
561
+ });
562
+ activeReasoning.set(chunk.id, part);
409
563
  part["text"] = `${typeof part["text"] === "string" ? part["text"] : ""}${chunk.delta}`;
410
564
  setOptional(part, "providerMetadata", chunk.providerMetadata);
411
565
  break;
412
566
  }
413
567
  case "reasoning-end": {
414
- const part = activeReasoning.get(chunk.id);
415
- if (part) {
568
+ const current = activeReasoning.get(chunk.id);
569
+ if (current) {
570
+ const part = editPart({
571
+ parts,
572
+ part: current
573
+ });
416
574
  part["state"] = "done";
417
575
  setOptional(part, "providerMetadata", chunk.providerMetadata);
418
576
  activeReasoning.delete(chunk.id);
@@ -494,6 +652,35 @@ function projectUIMessageStream(options) {
494
652
  });
495
653
  break;
496
654
  }
655
+ case "a2-tool-called": {
656
+ const call = chunk.call;
657
+ const current = findToolPart(parts, call.toolCallId);
658
+ if (current) {
659
+ const existing = editPart({
660
+ parts,
661
+ part: current
662
+ });
663
+ existing["input"] = call.input;
664
+ if (existing["state"] === "input-streaming") existing["state"] = "input-available";
665
+ if (existing["callProviderMetadata"] === void 0) setOptional(existing, "callProviderMetadata", call.providerMetadata);
666
+ if (existing["toolMetadata"] === void 0) setOptional(existing, "toolMetadata", call.toolMetadata);
667
+ setOptional(existing, "providerExecuted", call.providerExecuted);
668
+ setOptional(existing, "title", call.title);
669
+ } else setToolPart({
670
+ parts,
671
+ toolCallId: call.toolCallId,
672
+ toolName: call.toolName,
673
+ dynamic: call.dynamic === true,
674
+ state: "input-available",
675
+ input: call.input,
676
+ providerExecuted: call.providerExecuted,
677
+ providerMetadata: call.providerMetadata,
678
+ toolMetadata: call.toolMetadata,
679
+ title: call.title
680
+ });
681
+ partialTools.delete(call.toolCallId);
682
+ break;
683
+ }
497
684
  case "tool-input-available":
498
685
  setToolPart({
499
686
  parts,
@@ -529,8 +716,12 @@ function projectUIMessageStream(options) {
529
716
  break;
530
717
  }
531
718
  case "tool-approval-request": {
532
- const part = findToolPart(parts, chunk.toolCallId);
533
- if (!part) break;
719
+ const current = findToolPart(parts, chunk.toolCallId);
720
+ if (!current) break;
721
+ const part = editPart({
722
+ parts,
723
+ part: current
724
+ });
534
725
  part["state"] = "approval-requested";
535
726
  part["approval"] = {
536
727
  id: chunk.approvalId,
@@ -540,8 +731,12 @@ function projectUIMessageStream(options) {
540
731
  break;
541
732
  }
542
733
  case "tool-approval-response": {
543
- const part = findApprovalPart(parts, chunk.approvalId);
544
- if (!part) break;
734
+ const current = findApprovalPart(parts, chunk.approvalId);
735
+ if (!current) break;
736
+ const part = editPart({
737
+ parts,
738
+ part: current
739
+ });
545
740
  const approval = isRecord$3(part["approval"]) ? part["approval"] : {};
546
741
  part["state"] = "approval-responded";
547
742
  part["approval"] = {
@@ -595,7 +790,10 @@ function projectUIMessageStream(options) {
595
790
  }
596
791
  case "tool-output-denied": {
597
792
  const part = findToolPart(parts, chunk.toolCallId);
598
- if (part) part["state"] = "output-denied";
793
+ if (part) editPart({
794
+ parts,
795
+ part
796
+ })["state"] = "output-denied";
599
797
  break;
600
798
  }
601
799
  case "start-step":
@@ -619,7 +817,10 @@ function projectUIMessageStream(options) {
619
817
  default: {
620
818
  if (!chunk.type.startsWith("data-") || chunk.transient) break;
621
819
  const existing = chunk.id === void 0 ? void 0 : parts.find((part) => part["type"] === chunk.type && part["id"] === chunk.id);
622
- if (existing) existing["data"] = chunk.data;
820
+ if (existing) editPart({
821
+ parts,
822
+ part: existing
823
+ })["data"] = chunk.data;
623
824
  else parts.push({
624
825
  type: chunk.type,
625
826
  ...chunk.id === void 0 ? {} : { id: chunk.id },
@@ -680,6 +881,7 @@ function interruptUIMessage(message, toolError = "Tool execution was interrupted
680
881
  }
681
882
  //#endregion
682
883
  //#region src/ai-progress-batches.ts
884
+ const progressOwnedBy = ({ payload, owner, startIndex }) => "startIndex" in payload ? payload.startIndex === startIndex : payload.requestId === owner.requestId && payload.generationId === owner.generationId && payload.responseMessageId === owner.responseMessageId;
683
885
  const PAGE_SIZE = 64;
684
886
  const appendProgressBatch = ({ batches, batch }) => {
685
887
  if (batches.tail.length < PAGE_SIZE) return {
@@ -738,22 +940,22 @@ const flattenProgressBatches = (batches) => {
738
940
  //#endregion
739
941
  //#region src/ai-message-projection.ts
740
942
  const isRecord$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
943
+ const positionOf = (messages, id) => messages.findLastIndex((message) => message.id === id);
944
+ const findMessage = (messages, id) => messages.findLast((message) => message.id === id);
741
945
  const upsertMessage = (messages, message) => {
742
- const index = messages.findIndex((candidate) => candidate.id === message.id);
946
+ const index = positionOf(messages, message.id);
743
947
  if (index === -1) return [...messages, message];
744
- const next = [...messages];
745
- next[index] = message;
746
- return next;
948
+ return messages[index] === message ? messages : messages.with(index, message);
747
949
  };
748
950
  const upsertResponse = (options) => {
749
951
  const { messages, message, inputId } = options;
750
- if (messages.some((item) => item.id === message.id)) return upsertMessage(messages, message);
751
- const inputIndex = messages.findIndex((item) => item.id === inputId);
752
- if (inputIndex === -1) return [...messages, message];
952
+ if (positionOf(messages, message.id) !== -1) return upsertMessage(messages, message);
953
+ const inputIndex = positionOf(messages, inputId);
954
+ const position = inputIndex === -1 ? messages.length : inputIndex + 1;
753
955
  return [
754
- ...messages.slice(0, inputIndex + 1),
956
+ ...messages.slice(0, position),
755
957
  message,
756
- ...messages.slice(inputIndex + 1)
958
+ ...messages.slice(position)
757
959
  ];
758
960
  };
759
961
  const answerApproval = (messages, response) => messages.map((message) => {
@@ -791,6 +993,10 @@ const approvalRespondedChunk = (payload) => ({
791
993
  approved: payload.approved,
792
994
  ...payload.reason === void 0 ? {} : { reason: payload.reason }
793
995
  });
996
+ const toolCalledChunk = (payload) => ({
997
+ type: "a2-tool-called",
998
+ call: payload
999
+ });
794
1000
  const toolResultChunk = (payload) => {
795
1001
  if (payload.phase === "input") return {
796
1002
  type: "tool-input-error",
@@ -827,30 +1033,43 @@ const toolResultChunk = (payload) => {
827
1033
  ...payload.toolMetadata === void 0 ? {} : { toolMetadata: payload.toolMetadata }
828
1034
  };
829
1035
  };
830
- const projectedMessageStream = (projection) => {
1036
+ const projectedMessageStream = ({ projection, current }) => {
1037
+ const base = restoreMessageBase({
1038
+ current,
1039
+ base: projection.base
1040
+ });
831
1041
  const projectionEvents = flattenProgressBatches(projection.batches);
832
- for (const event of projection.toolEvents) if (event.type === "ai.tool.result") projectionEvents.push({
1042
+ for (const event of projection.toolEvents) projectionEvents.push({
833
1043
  index: event.index,
834
- chunks: [toolResultChunk(event.payload)]
1044
+ chunks: [event.type === "ai.tool.called" ? toolCalledChunk(event.payload) : toolResultChunk(event.payload)]
835
1045
  });
836
1046
  for (const event of projection.approvalEvents) projectionEvents.push({
837
1047
  index: event.index,
838
1048
  chunks: [event.type === "ai.approval.requested" ? approvalRequestedChunk(event.payload) : approvalRespondedChunk(event.payload)]
839
1049
  });
840
- return projectUIMessageStream({
841
- messageId: projection.responseMessageId,
842
- ...projection.baseMessage === void 0 ? {} : { base: projection.baseMessage },
843
- chunks: projectionEvents.toSorted((left, right) => left.index - right.index).flatMap((event) => event.chunks)
844
- });
1050
+ const ordered = projectionEvents.toSorted((left, right) => left.index - right.index);
1051
+ return {
1052
+ ...projectUIMessageStream({
1053
+ messageId: projection.responseMessageId,
1054
+ ...base === void 0 ? {} : { base },
1055
+ chunks: ordered.flatMap((event) => event.chunks)
1056
+ }),
1057
+ throughIndex: ordered.at(-1)?.index ?? 0
1058
+ };
845
1059
  };
846
- const projectedMessage = (projection) => projectedMessageStream(projection).message;
1060
+ const projectedMessage = (options) => projectedMessageStream(options).message;
847
1061
  const reduceToolActivity = (tools, event) => {
848
1062
  if (event.type === "ai.tool.called") {
849
1063
  const payload = event.payload;
850
- const { providerMetadata, ...called } = payload;
851
1064
  const activity = {
852
- ...called,
853
- ...providerMetadata === void 0 ? {} : { callProviderMetadata: providerMetadata },
1065
+ requestId: payload.requestId,
1066
+ messageId: payload.messageId,
1067
+ generationId: payload.generationId,
1068
+ toolCallId: payload.toolCallId,
1069
+ toolName: payload.toolName,
1070
+ ...payload.dynamic === void 0 ? {} : { dynamic: payload.dynamic },
1071
+ ...payload.providerExecuted === void 0 ? {} : { providerExecuted: payload.providerExecuted },
1072
+ ...payload.title === void 0 ? {} : { title: payload.title },
854
1073
  status: "running"
855
1074
  };
856
1075
  return [...tools.filter((candidate) => candidate.generationId !== payload.generationId || candidate.toolCallId !== payload.toolCallId), activity];
@@ -858,28 +1077,19 @@ const reduceToolActivity = (tools, event) => {
858
1077
  const payload = event.payload;
859
1078
  const existing = tools.find((candidate) => candidate.generationId === payload.generationId && candidate.toolCallId === payload.toolCallId);
860
1079
  const toolName = payload.toolName ?? existing?.toolName;
861
- const input = payload.input === void 0 ? existing?.input : payload.input;
862
1080
  const dynamic = payload.dynamic ?? existing?.dynamic;
863
1081
  const providerExecuted = payload.providerExecuted ?? existing?.providerExecuted;
864
- const toolMetadata = payload.toolMetadata ?? existing?.toolMetadata;
865
- const resultProviderMetadata = payload.providerMetadata ?? existing?.resultProviderMetadata;
866
1082
  const activity = {
867
1083
  requestId: payload.requestId,
868
1084
  messageId: payload.messageId,
869
1085
  generationId: payload.generationId,
870
1086
  toolCallId: payload.toolCallId,
871
1087
  ...toolName === void 0 ? {} : { toolName },
872
- ...input === void 0 ? {} : { input },
873
- ...payload.rawInput === void 0 ? {} : { rawInput: payload.rawInput },
874
- ...payload.output === void 0 ? {} : { output: payload.output },
875
1088
  ...payload.error === void 0 ? {} : { error: payload.error },
876
1089
  ...payload.preliminary === void 0 ? {} : { preliminary: payload.preliminary },
877
1090
  ...payload.phase === void 0 ? {} : { phase: payload.phase },
878
1091
  ...dynamic === void 0 ? {} : { dynamic },
879
1092
  ...providerExecuted === void 0 ? {} : { providerExecuted },
880
- ...existing?.callProviderMetadata === void 0 ? {} : { callProviderMetadata: existing.callProviderMetadata },
881
- ...resultProviderMetadata === void 0 ? {} : { resultProviderMetadata },
882
- ...toolMetadata === void 0 ? {} : { toolMetadata },
883
1093
  ...existing?.title === void 0 ? {} : { title: existing.title },
884
1094
  status: payload.denied ? "denied" : payload.error ? "failed" : payload.preliminary ? "running" : "completed"
885
1095
  };
@@ -903,8 +1113,11 @@ const advanceProjection = ({ projection, base, chunks, index }) => {
903
1113
  cursor: projection.cursor.stream,
904
1114
  chunks
905
1115
  }) : projectedMessageStream({
906
- ...projection,
907
- batches
1116
+ projection: {
1117
+ ...projection,
1118
+ batches
1119
+ },
1120
+ current: base
908
1121
  });
909
1122
  return {
910
1123
  message: projected.message,
@@ -918,9 +1131,14 @@ const advanceProjection = ({ projection, base, chunks, index }) => {
918
1131
  const projectedTools = (projection) => projection.toolEvents.reduce((tools, event) => reduceToolActivity(tools, event), projection.baseTools);
919
1132
  const restoreGenerationBase = (state, generationId, responseMessageId, reason) => {
920
1133
  const projection = state.activeProjection?.generationId === generationId ? state.activeProjection : null;
1134
+ const restored = restoreMessageBase({
1135
+ current: findMessage(state.messages, responseMessageId),
1136
+ base: projection?.base
1137
+ });
1138
+ const base = restored !== void 0 && reason === "superseded" ? withoutRunningCompactionPart(restored, generationId) : restored;
921
1139
  return {
922
1140
  ...state,
923
- messages: projection?.baseMessage === void 0 ? state.messages.filter((message) => message.id !== responseMessageId) : upsertMessage(state.messages, projection.baseMessage),
1141
+ messages: base === void 0 ? state.messages.filter((message) => message.id !== responseMessageId) : upsertMessage(state.messages, base),
924
1142
  tools: projection === null ? state.tools.filter((activity) => activity.generationId !== generationId) : projection.baseTools,
925
1143
  pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== generationId),
926
1144
  pendingInputs: state.pendingInputs.filter((input) => input.generationId !== generationId),
@@ -948,15 +1166,126 @@ const interruptGenerationProjection = (state, generationId, responseMessageId, c
948
1166
  toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff),
949
1167
  approvalEvents: projection.approvalEvents.filter((approvalEvent) => approvalEvent.index <= cutoff)
950
1168
  };
951
- const shouldProject = retained.baseMessage !== void 0 || retained.batches.length > 0;
1169
+ const shouldProject = retained.base !== void 0 || retained.batches.length > 0;
952
1170
  const restored = restoreGenerationBase(state, generationId, responseMessageId, "interrupted");
953
1171
  return {
954
1172
  ...restored,
955
- messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : restored.messages,
1173
+ messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage({
1174
+ projection: retained,
1175
+ current: findMessage(state.messages, responseMessageId)
1176
+ }))) : restored.messages,
956
1177
  tools: stopGenerationTools(projectedTools(retained), generationId, responseMessageId, "Tool execution was interrupted."),
957
1178
  usage: projection.completionIndex === void 0 || projection.completionIndex <= cutoff ? restored.usage : restored.usage.filter((entry) => entry.generationId !== generationId)
958
1179
  };
959
1180
  };
1181
+ const retainMessageBase = (options) => {
1182
+ const { before, after } = options;
1183
+ const projection = after.activeProjection;
1184
+ if (before.messages === after.messages || !projection?.base || projection.base !== before.activeProjection?.base) return after;
1185
+ const previous = findMessage(before.messages, projection.responseMessageId);
1186
+ const current = findMessage(after.messages, projection.responseMessageId);
1187
+ if (previous !== void 0 && previous === current) return after;
1188
+ const base = previous && current && updateMessageBase({
1189
+ base: projection.base,
1190
+ before: previous,
1191
+ after: current
1192
+ });
1193
+ const { base: _stale, ...unrecoverable } = projection;
1194
+ return {
1195
+ ...after,
1196
+ activeProjection: base ? {
1197
+ ...unrecoverable,
1198
+ base
1199
+ } : unrecoverable
1200
+ };
1201
+ };
1202
+ const isCompactionPart = (part) => part.type === "data-a2-compaction";
1203
+ const compactionPartOf = (message, generationId) => message.parts.findIndex((part) => isCompactionPart(part) && part.id === generationId);
1204
+ const withoutRunningCompactionPart = (message, generationId) => {
1205
+ const index = compactionPartOf(message, generationId);
1206
+ const part = message.parts[index];
1207
+ return part !== void 0 && isCompactionPart(part) && part.data.status === "running" ? {
1208
+ ...message,
1209
+ parts: message.parts.toSpliced(index, 1)
1210
+ } : message;
1211
+ };
1212
+ /**
1213
+ * Compaction runs before its model step streams, so the part lands between
1214
+ * the response's earlier steps and this one. Counting it in the step's base
1215
+ * keeps it through Stop cutoffs, failures, and retries of that step.
1216
+ */
1217
+ const addCompactionPart = (state, payload) => {
1218
+ const projection = state.activeProjection;
1219
+ if (projection?.generationId !== payload.generationId) return state;
1220
+ const current = findMessage(state.messages, projection.responseMessageId);
1221
+ if (current === void 0 ? projection.base !== void 0 : projection.base?.partCount !== current.parts.length) return state;
1222
+ const inputTokens = payload.inputTokens ?? payload.inputTokenEstimate;
1223
+ const part = {
1224
+ type: "data-a2-compaction",
1225
+ id: payload.generationId,
1226
+ data: {
1227
+ status: "running",
1228
+ ...inputTokens === void 0 ? {} : { inputTokens },
1229
+ ...payload.thresholdTokens === void 0 ? {} : { thresholdTokens: payload.thresholdTokens }
1230
+ }
1231
+ };
1232
+ const message = current === void 0 ? {
1233
+ id: projection.responseMessageId,
1234
+ role: "assistant",
1235
+ parts: [part]
1236
+ } : {
1237
+ ...current,
1238
+ parts: [...current.parts, part]
1239
+ };
1240
+ return {
1241
+ ...state,
1242
+ messages: upsertResponse({
1243
+ messages: state.messages,
1244
+ message,
1245
+ inputId: state.activeGeneration?.messageId ?? message.id
1246
+ }),
1247
+ activeProjection: {
1248
+ ...projection,
1249
+ cursor: null,
1250
+ base: projection.base === void 0 ? { partCount: 1 } : {
1251
+ ...projection.base,
1252
+ partCount: projection.base.partCount + 1
1253
+ }
1254
+ }
1255
+ };
1256
+ };
1257
+ const settleCompactionPart = (options) => {
1258
+ const { state, generationId, data } = options;
1259
+ const position = state.messages.findLastIndex((message) => compactionPartOf(message, generationId) !== -1);
1260
+ if (position === -1) return state;
1261
+ const message = state.messages[position];
1262
+ const index = compactionPartOf(message, generationId);
1263
+ const part = message.parts[index];
1264
+ return {
1265
+ ...state,
1266
+ messages: state.messages.with(position, {
1267
+ ...message,
1268
+ parts: message.parts.with(index, {
1269
+ ...part,
1270
+ data: {
1271
+ ...part.data,
1272
+ ...data
1273
+ }
1274
+ })
1275
+ })
1276
+ };
1277
+ };
1278
+ /** A running compaction whose state reverted without completing was abandoned. */
1279
+ const settleAbandonedCompaction = (before, after) => {
1280
+ const running = before.compaction;
1281
+ if (running?.status !== "running" || after.compaction === running || after.compaction?.status === "completed" && after.compaction.generationId === running.generationId) return after;
1282
+ const interrupted = after.status === "closed" || after.pausedGenerationIds?.[running.generationId] === true || after.terminalGenerations?.[running.generationId] === "interrupted";
1283
+ return settleCompactionPart({
1284
+ state: after,
1285
+ generationId: running.generationId,
1286
+ data: { status: interrupted ? "interrupted" : "failed" }
1287
+ });
1288
+ };
960
1289
  //#endregion
961
1290
  //#region src/ai-client-state.ts
962
1291
  const isQueueCommand = (command) => command.action === "send" || command.action === "edit" || command.action === "move" || command.action === "remove" || command.action === "send-now" || command.action === "steer" || command.action === "pause" || command.action === "resume" || command.action === "stop";
@@ -1076,8 +1405,9 @@ function projectAIClientState(state) {
1076
1405
  const stop = pending.find(({ command }) => command.action === "stop" && command.turnId === state.active?.turnId)?.command;
1077
1406
  if (stop?.action !== "stop") return displayed;
1078
1407
  const projection = state.activeProjection;
1079
- return {
1080
- ...projection === null ? displayed : interruptGenerationProjection(displayed, projection.generationId, projection.responseMessageId, stop.lastSeenIndex ?? Number.POSITIVE_INFINITY) ?? displayed,
1408
+ const stopped = projection === null ? displayed : interruptGenerationProjection(displayed, projection.generationId, projection.responseMessageId, stop.lastSeenIndex ?? Number.POSITIVE_INFINITY) ?? displayed;
1409
+ return settleAbandonedCompaction(displayed, {
1410
+ ...stopped,
1081
1411
  active: null,
1082
1412
  status: paused ? "paused" : "idle",
1083
1413
  starting: false,
@@ -1089,7 +1419,7 @@ function projectAIClientState(state) {
1089
1419
  pendingApprovals: [],
1090
1420
  pendingInputs: [],
1091
1421
  error: null
1092
- };
1422
+ });
1093
1423
  }
1094
1424
  //#endregion
1095
1425
  //#region src/ai-control-state.ts
@@ -1222,7 +1552,6 @@ const AI_EVENT_ID_PREFIX = "a2.ai:";
1222
1552
  const aiEventId = async (...parts) => `${AI_EVENT_ID_PREFIX}${await idempotentId(...parts)}`;
1223
1553
  //#endregion
1224
1554
  //#region src/ai-stored-state.ts
1225
- const aiStateReducer = Symbol("a2.ai.stateReducer");
1226
1555
  const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1227
1556
  const hasKeys = (value, { required, optional = [] }) => isRecord$1(value) && required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
1228
1557
  const reference = (value) => {
@@ -1243,15 +1572,6 @@ const payloadFields = /* @__PURE__ */ new Set([
1243
1572
  "data"
1244
1573
  ]);
1245
1574
  const keyFor = (value) => value === void 0 ? "undefined" : JSON.stringify(value);
1246
- const sameValue = (left, right) => {
1247
- if (Object.is(left, right)) return true;
1248
- if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) return false;
1249
- const prototype = Object.getPrototypeOf(left);
1250
- if (prototype !== Object.getPrototypeOf(right) || prototype !== Object.prototype && prototype !== Array.prototype && prototype !== null) return false;
1251
- if (Array.isArray(left) && (!Array.isArray(right) || left.length !== right.length)) return false;
1252
- const keys = Reflect.ownKeys(left);
1253
- return keys.length === Reflect.ownKeys(right).length && keys.every((key) => Object.hasOwn(right, key) && sameValue(Reflect.get(left, key), Reflect.get(right, key)));
1254
- };
1255
1575
  const restore = (current, undo) => {
1256
1576
  if (undo === void 0) return current;
1257
1577
  if (!isRecord$1(undo)) throw new TypeError("invalid AI recovery change");
@@ -1350,22 +1670,18 @@ const storeState = (state) => {
1350
1670
  packed.set(value, result);
1351
1671
  return result;
1352
1672
  };
1353
- const saveProjection = (projection, messages, tools) => {
1354
- const { baseMessage, baseTools, ...fields } = projection;
1355
- const current = messages.find((message) => message.id === projection.responseMessageId);
1356
- const message = baseMessage === void 0 ? void 0 : difference(pack(current), pack(baseMessage));
1673
+ const saveProjection = (projection, tools) => {
1674
+ const { baseTools, ...fields } = projection;
1357
1675
  const toolPatch = baseTools === void 0 ? void 0 : difference(pack(tools), pack(baseTools));
1358
1676
  return {
1359
1677
  fields: pack(fields),
1360
- hasMessage: baseMessage !== void 0,
1361
- ...message === void 0 ? {} : { message },
1362
1678
  ...toolPatch === void 0 ? {} : { tools: toolPatch }
1363
1679
  };
1364
1680
  };
1365
1681
  const { activeProjection, ...fields } = state;
1366
- const projection = activeProjection === null ? null : saveProjection(activeProjection, state.messages, state.tools);
1682
+ const projection = activeProjection === null ? null : saveProjection(activeProjection, state.tools);
1367
1683
  return {
1368
- version: 2,
1684
+ version: 3,
1369
1685
  fields: pack(fields),
1370
1686
  projection,
1371
1687
  values
@@ -1388,99 +1704,114 @@ const loadState = (stored) => {
1388
1704
  };
1389
1705
  const fields = stored.fields;
1390
1706
  const state = unpack(stored.fields);
1391
- const loadProjection = (saved, messages, tools) => {
1707
+ const loadProjection = (saved, tools) => {
1392
1708
  if (!hasKeys(saved, {
1393
- required: ["fields", "hasMessage"],
1394
- optional: ["message", "tools"]
1395
- }) || typeof saved.hasMessage !== "boolean" || !isRecord$1(saved.fields) || "message" in saved && (!saved.hasMessage || saved.message === void 0) || "tools" in saved && saved.tools === void 0) throw new TypeError("invalid AI projection recovery");
1709
+ required: ["fields"],
1710
+ optional: ["tools"]
1711
+ }) || !isRecord$1(saved.fields) || "tools" in saved && saved.tools === void 0) throw new TypeError("invalid AI projection recovery");
1396
1712
  const projection = unpack(saved.fields);
1397
- const index = unpack(messages).findIndex((message) => message.id === projection.responseMessageId);
1398
- if (saved.hasMessage) {
1399
- const current = index === -1 ? null : messages[index];
1400
- projection.baseMessage = unpack(restore(current, saved.message));
1713
+ if (projection.base !== void 0) {
1714
+ assertMessageBase(projection.base);
1715
+ const current = state.messages.find((message) => message.id === projection.responseMessageId);
1716
+ if (current !== void 0 && restoreMessageBase({
1717
+ current,
1718
+ base: projection.base
1719
+ }) === void 0) throw new TypeError("AI message base does not match its message");
1401
1720
  }
1402
1721
  if (tools !== void 0) projection.baseTools = unpack(restore(tools, saved.tools));
1403
1722
  else if (saved.tools !== void 0) throw new TypeError("missing AI recovery tools");
1404
1723
  return projection;
1405
1724
  };
1406
- state.activeProjection = stored.projection === null ? null : loadProjection(stored.projection, fields["messages"], fields["tools"]);
1725
+ state.activeProjection = stored.projection === null ? null : loadProjection(stored.projection, fields["tools"]);
1407
1726
  return state;
1408
1727
  };
1409
- const storedAIReducer = (source) => {
1410
- const views = /* @__PURE__ */ new WeakMap();
1411
- const view = (state) => {
1412
- let result = views.get(state);
1413
- if (result === void 0) {
1414
- result = loadState(state);
1415
- views.set(state, result);
1728
+ const withAIStorage = (source) => withReducerRuntime({
1729
+ reducer: source,
1730
+ storage: {
1731
+ encode: (state) => storeState(state),
1732
+ decode(value) {
1733
+ if (!hasKeys(value, { required: [
1734
+ "version",
1735
+ "fields",
1736
+ "projection",
1737
+ "values"
1738
+ ] }) || value["version"] !== 3 || !Array.isArray(value["values"]) || !isRecord$1(value["fields"]) || value["projection"] !== null && !isRecord$1(value["projection"])) throw new TypeError("invalid AI stored state");
1739
+ const state = value;
1740
+ if (Object.keys(state.values).length !== state.values.length) throw new TypeError("invalid AI payload table");
1741
+ for (const payload of state.values) if (!hasKeys(payload, {
1742
+ required: [],
1743
+ optional: ["value"]
1744
+ })) throw new TypeError("invalid AI payload table");
1745
+ return loadState(state);
1416
1746
  }
1417
- return result;
1418
- };
1419
- const defer = (next) => {
1420
- let stored;
1421
- const read = () => stored ??= storeState(next);
1422
- const state = {
1423
- version: 2,
1424
- get fields() {
1425
- return read().fields;
1426
- },
1427
- get projection() {
1428
- return read().projection;
1429
- },
1430
- get values() {
1431
- return read().values;
1432
- }
1433
- };
1434
- views.set(state, next);
1435
- return state;
1436
- };
1437
- return {
1438
- ...source,
1439
- name: `${source.name}:records-v2`,
1440
- initialState: storeState(source.initialState),
1441
- [aiStateReducer]: source,
1442
- view,
1443
- fromView: (state) => storeState(state),
1444
- [clientStateProjection]: (state) => source[clientStateProjection]?.(view(state)) ?? view(state),
1445
- stateSchema: { "~standard": {
1446
- version: 1,
1447
- vendor: "a2-ai",
1448
- validate(value) {
1449
- try {
1450
- if (!hasKeys(value, { required: [
1451
- "version",
1452
- "fields",
1453
- "projection",
1454
- "values"
1455
- ] }) || value["version"] !== 2 || !Array.isArray(value["values"]) || !isRecord$1(value["fields"]) || value["projection"] !== null && !isRecord$1(value["projection"])) return { issues: [{ message: "invalid AI stored state" }] };
1456
- const state = value;
1457
- if (Object.keys(state.values).length !== state.values.length) return { issues: [{ message: "invalid AI payload table" }] };
1458
- for (const payload of state.values) if (!hasKeys(payload, {
1459
- required: [],
1460
- optional: ["value"]
1461
- })) return { issues: [{ message: "invalid AI payload table" }] };
1462
- const decoded = loadState(state);
1463
- if (source.stateSchema) {
1464
- const result = validateSync(source.stateSchema, decoded, "AI view stateSchema");
1465
- if (result.issues) return result;
1466
- return { value: storeState(result.value) };
1467
- }
1468
- return { value: state };
1469
- } catch {
1470
- return { issues: [{ message: "invalid AI stored state" }] };
1471
- }
1472
- }
1473
- } },
1474
- fold(state, event) {
1475
- const current = view(state);
1476
- const next = source.fold(current, event);
1477
- if (next === current) return state;
1478
- return defer(next);
1479
- }
1480
- };
1747
+ }
1748
+ });
1749
+ //#endregion
1750
+ //#region src/ai-app-state.ts
1751
+ const aiStateKeys = {
1752
+ inbox: true,
1753
+ pendingQueueCommands: true,
1754
+ active: true,
1755
+ starting: true,
1756
+ receipt: true,
1757
+ pausedGenerationIds: true,
1758
+ session: true,
1759
+ messages: true,
1760
+ status: true,
1761
+ activeGeneration: true,
1762
+ activeRequestId: true,
1763
+ activeRequestReason: true,
1764
+ activeRequestSourceGenerationId: true,
1765
+ activeResponseMessageId: true,
1766
+ activeProjection: true,
1767
+ responseOwners: true,
1768
+ terminalRequestIds: true,
1769
+ terminalGenerations: true,
1770
+ pendingApprovals: true,
1771
+ pendingInputs: true,
1772
+ tools: true,
1773
+ compaction: true,
1774
+ modelMetadata: true,
1775
+ usage: true,
1776
+ error: true
1777
+ };
1778
+ const isAIStateKey = (key) => Object.hasOwn(aiStateKeys, key);
1779
+ const keep = (state, ai) => {
1780
+ const kept = {};
1781
+ for (const [key, value] of Object.entries(state)) if (isAIStateKey(key) === ai) kept[key] = value;
1782
+ return kept;
1783
+ };
1784
+ /** The application part of a merged state: every key `AIState` does not own. */
1785
+ const appSlice = (state) => keep(state, false);
1786
+ /** A merged state with its application part replaced. */
1787
+ const withAppSlice = (state, app) => ({
1788
+ ...keep(state, true),
1789
+ ...keep(app, false)
1790
+ });
1791
+ const agentReducers = /* @__PURE__ */ new WeakMap();
1792
+ const registerAgentReducer = (reducer, app) => {
1793
+ agentReducers.set(reducer, app ?? null);
1794
+ };
1795
+ const reducerOf = (contract) => typeof contract.reducer === "object" && contract.reducer !== null ? contract.reducer : void 0;
1796
+ const isAgentContract = (contract) => {
1797
+ const reducer = reducerOf(contract);
1798
+ return reducer !== void 0 && agentReducers.has(reducer);
1799
+ };
1800
+ const appReducerOf = (contract) => {
1801
+ const reducer = reducerOf(contract);
1802
+ return reducer === void 0 ? void 0 : agentReducers.get(reducer) ?? void 0;
1481
1803
  };
1482
1804
  //#endregion
1483
1805
  //#region src/ai.ts
1806
+ const reasoningLevels = [
1807
+ "provider-default",
1808
+ "none",
1809
+ "minimal",
1810
+ "low",
1811
+ "medium",
1812
+ "high",
1813
+ "xhigh"
1814
+ ];
1484
1815
  const issue = (message) => ({ issues: [{ message }] });
1485
1816
  const schema = (label, parse) => ({ "~standard": {
1486
1817
  version: 1,
@@ -1530,7 +1861,7 @@ const messagePayloadSchema = (messageSchema) => schema("ai.message.created", (va
1530
1861
  } };
1531
1862
  });
1532
1863
  const progressPayloadSchema = schema("ai.generation.progress", (value) => {
1533
- if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "requestId") || !stringField(value, "messageId") || !stringField(value, "generationId") || !stringField(value, "responseMessageId") || typeof value["sequence"] !== "number" || !Number.isInteger(value["sequence"]) || value["sequence"] < 0 || !Array.isArray(value["chunks"]) || !value["chunks"].every((chunk) => isRecord(chunk) && typeof chunk["type"] === "string")) return issue("invalid ai.generation.progress payload");
1864
+ if (!isRecord(value) || !isJSONCompatible(value) || typeof value["startIndex"] !== "number" || !optionalIndexField(value, "startIndex") || !Array.isArray(value["chunks"]) || !value["chunks"].every((chunk) => isRecord(chunk) && typeof chunk["type"] === "string")) return issue("invalid ai.generation.progress payload");
1534
1865
  return { value };
1535
1866
  });
1536
1867
  const completedPayloadSchema = schema("ai.generation.completed", (value) => {
@@ -1633,7 +1964,7 @@ function createEvents(options) {
1633
1964
  "ai.message.interrupted": jsonObject("ai.message.interrupted", (value) => stringField(value, "messageId") && (stringField(value, "generationId") && value["requestId"] === void 0 || stringField(value, "requestId") && value["generationId"] === void 0) && optionalStringField(value, "reason") && (value["lastSeenIndex"] === void 0 || typeof value["lastSeenIndex"] === "number" && Number.isInteger(value["lastSeenIndex"]) && value["lastSeenIndex"] >= 0)),
1634
1965
  "ai.generation.requested": jsonObject("ai.generation.requested", (value) => stringField(value, "messageId") && stringField(value, "responseMessageId") && optionalStringField(value, "sourceGenerationId") && (value["reason"] !== "tool" || stringField(value, "sourceGenerationId")) && (value["reason"] === "message" || value["reason"] === "tool" || value["reason"] === "input" || value["reason"] === "retry" || value["reason"] === "resume")),
1635
1966
  "ai.retry.requested": jsonObject("ai.retry.requested", (value) => stringField(value, "messageId") && stringField(value, "responseMessageId") && stringField(value, "retryId")),
1636
- "ai.generation.started": jsonObject("ai.generation.started", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "responseMessageId") && typeof value["attempt"] === "number" && Number.isInteger(value["attempt"]) && value["attempt"] > 0 && stringField(value, "model") && optionalIndexField(value, "promptThroughIndex")),
1967
+ "ai.generation.started": jsonObject("ai.generation.started", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "responseMessageId") && typeof value["attempt"] === "number" && Number.isInteger(value["attempt"]) && value["attempt"] > 0 && (value["model"] === void 0 || stringField(value, "model")) && optionalIndexField(value, "promptThroughIndex")),
1637
1968
  "ai.generation.progress": progressPayloadSchema,
1638
1969
  "ai.generation.completed": completedPayloadSchema,
1639
1970
  "ai.generation.failed": jsonObject("ai.generation.failed", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "responseMessageId") && stringField(value, "error") && (value["superseded"] === void 0 || typeof value["superseded"] === "boolean") && (value["stepLimit"] === void 0 || typeof value["stepLimit"] === "boolean")),
@@ -1675,11 +2006,14 @@ const initialState = () => ({
1675
2006
  usage: [],
1676
2007
  error: null
1677
2008
  });
1678
- const projectMessageChunks = (messages, messageId, chunks) => messages.map((message) => message.id === messageId ? projectUIMessage({
1679
- messageId,
1680
- base: message,
1681
- chunks
1682
- }) : message);
2009
+ const projectMessageChunks = (messages, messageId, chunks) => {
2010
+ const base = findMessage(messages, messageId);
2011
+ return upsertMessage(messages, projectUIMessage({
2012
+ messageId,
2013
+ ...base === void 0 ? {} : { base },
2014
+ chunks
2015
+ }));
2016
+ };
1683
2017
  const waitingStatus = (state) => {
1684
2018
  if (state.pendingApprovals.length > 0 || state.pendingInputs.length > 0) return "waiting";
1685
2019
  return state.activeGeneration !== null || state.tools.some((activity) => activity.status === "running") ? "generating" : "idle";
@@ -1690,7 +2024,10 @@ const isStaleGeneration = (state, messageId, owner) => {
1690
2024
  return current !== void 0 && (current.generationId !== owner.generationId || current.requestId !== owner.requestId);
1691
2025
  };
1692
2026
  const projectedResponse = (options) => {
1693
- const message = projectedMessage(options.projection);
2027
+ const message = projectedMessage({
2028
+ projection: options.projection,
2029
+ current: findMessage(options.state.messages, options.projection.responseMessageId)
2030
+ });
1694
2031
  return options.state.pausedGenerationIds?.[options.projection.generationId] ? pauseUIMessage({
1695
2032
  message,
1696
2033
  toolCallIds: new Set(options.state.tools.map((tool) => tool.toolCallId))
@@ -1703,6 +2040,12 @@ const terminalGenerationReason = (state, generationId, requestId) => {
1703
2040
  };
1704
2041
  /** Pure projection of the built-in AI protocol. Unknown extension events are ignored. */
1705
2042
  function reduceAIState(state, event) {
2043
+ return settleAbandonedCompaction(state, retainMessageBase({
2044
+ before: state,
2045
+ after: reduceAIEvent(state, event)
2046
+ }));
2047
+ }
2048
+ function reduceAIEvent(state, event) {
1706
2049
  if (state.status === "closed") return state;
1707
2050
  switch (event.type) {
1708
2051
  case "ai.control.requested": {
@@ -1893,11 +2236,14 @@ function reduceAIState(state, event) {
1893
2236
  if (currentOwner?.requestId === payload.requestId && currentOwner.attempt >= payload.attempt) return state;
1894
2237
  const activeGeneration = state.activeGeneration;
1895
2238
  const baseState = activeGeneration !== null && activeGeneration.requestId === payload.requestId && activeGeneration.responseMessageId === payload.responseMessageId && activeGeneration.attempt < payload.attempt && state.activeProjection?.generationId === activeGeneration.generationId ? restoreGenerationBase(state, activeGeneration.generationId, activeGeneration.responseMessageId, "superseded") : state;
1896
- const baseMessage = baseState.messages.find((message) => message.id === payload.responseMessageId);
2239
+ const baseMessage = findMessage(baseState.messages, payload.responseMessageId);
1897
2240
  return {
1898
2241
  ...baseState,
1899
2242
  status: "generating",
1900
- activeGeneration: payload,
2243
+ activeGeneration: {
2244
+ ...payload,
2245
+ startIndex: event.index
2246
+ },
1901
2247
  activeResponseMessageId: null,
1902
2248
  responseOwners: {
1903
2249
  ...baseState.responseOwners,
@@ -1911,7 +2257,7 @@ function reduceAIState(state, event) {
1911
2257
  generationId: payload.generationId,
1912
2258
  responseMessageId: payload.responseMessageId,
1913
2259
  promptThroughIndex: payload.promptThroughIndex ?? event.index - 1,
1914
- ...baseMessage === void 0 ? {} : { baseMessage },
2260
+ ...baseMessage === void 0 ? {} : { base: { partCount: baseMessage.parts.length } },
1915
2261
  baseCompaction: baseState.compaction,
1916
2262
  batches: progressBatches(),
1917
2263
  baseTools: baseState.tools,
@@ -1923,13 +2269,16 @@ function reduceAIState(state, event) {
1923
2269
  }
1924
2270
  case "ai.generation.progress": {
1925
2271
  const payload = event.payload;
1926
- if (state.pausedGenerationIds?.[payload.generationId]) return state;
1927
- if (terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
2272
+ const generation = state.activeGeneration;
1928
2273
  const activeProjection = state.activeProjection;
1929
- if (state.activeGeneration?.generationId !== payload.generationId || state.activeGeneration.requestId !== payload.requestId || activeProjection?.generationId !== payload.generationId) return state;
2274
+ if (generation === null || !progressOwnedBy({
2275
+ payload,
2276
+ owner: generation,
2277
+ startIndex: generation.startIndex
2278
+ }) || state.pausedGenerationIds?.[generation.generationId] || terminalGenerationReason(state, generation.generationId, generation.requestId) !== void 0 || activeProjection?.generationId !== generation.generationId) return state;
1930
2279
  const progress = advanceProjection({
1931
2280
  projection: activeProjection,
1932
- base: state.messages.find((message) => message.id === activeProjection.responseMessageId),
2281
+ base: findMessage(state.messages, activeProjection.responseMessageId),
1933
2282
  chunks: payload.chunks,
1934
2283
  index: event.index
1935
2284
  });
@@ -1938,7 +2287,7 @@ function reduceAIState(state, event) {
1938
2287
  messages: upsertResponse({
1939
2288
  messages: state.messages,
1940
2289
  message: progress.message,
1941
- inputId: state.activeGeneration.messageId
2290
+ inputId: generation.messageId
1942
2291
  }),
1943
2292
  activeProjection: {
1944
2293
  ...activeProjection,
@@ -1976,10 +2325,13 @@ function reduceAIState(state, event) {
1976
2325
  if ((state.activeGeneration?.generationId !== payload.generationId || state.activeGeneration.requestId !== payload.requestId) && !(payload.stepLimit === true && state.activeRequestReason === "tool" && state.responseOwners[payload.responseMessageId]?.requestId === payload.requestId && state.responseOwners[payload.responseMessageId]?.generationId === payload.generationId && state.activeRequestSourceGenerationId === payload.generationId && state.activeResponseMessageId === payload.responseMessageId)) return state;
1977
2326
  if (payload.superseded === true) return restoreGenerationBase(state, payload.generationId, payload.responseMessageId, "superseded");
1978
2327
  const projection = state.activeProjection?.generationId === payload.generationId ? state.activeProjection : null;
1979
- const shouldProject = projection !== null && (projection.baseMessage !== void 0 || projection.batches.length > 0);
2328
+ const shouldProject = projection !== null && (projection.base !== void 0 || projection.batches.length > 0);
1980
2329
  return {
1981
2330
  ...state,
1982
- messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(projection), `Generation failed before tool completion: ${payload.error}`)) : state.messages,
2331
+ messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage({
2332
+ projection,
2333
+ current: findMessage(state.messages, projection.responseMessageId)
2334
+ }), `Generation failed before tool completion: ${payload.error}`)) : state.messages,
1983
2335
  status: "failed",
1984
2336
  activeGeneration: null,
1985
2337
  activeResponseMessageId: null,
@@ -2098,6 +2450,7 @@ function reduceAIState(state, event) {
2098
2450
  if (projection?.generationId !== payload.generationId) {
2099
2451
  const next = {
2100
2452
  ...state,
2453
+ messages: projectMessageChunks(state.messages, payload.messageId, [toolCalledChunk(payload)]),
2101
2454
  tools: reduceToolActivity(state.tools, {
2102
2455
  type: "ai.tool.called",
2103
2456
  payload
@@ -2110,15 +2463,49 @@ function reduceAIState(state, event) {
2110
2463
  }
2111
2464
  const activeProjection = {
2112
2465
  ...projection,
2466
+ cursor: null,
2113
2467
  toolEvents: [...projection.toolEvents, {
2114
2468
  index: event.index,
2115
2469
  type: "ai.tool.called",
2116
2470
  payload
2117
2471
  }]
2118
2472
  };
2473
+ const base = findMessage(state.messages, payload.messageId);
2474
+ const paused = state.pausedGenerationIds?.[payload.generationId];
2475
+ const cursor = projection.cursor;
2476
+ const projected = base && cursor && !paused && event.index > cursor.throughIndex ? {
2477
+ ...hasToolCall({
2478
+ message: base,
2479
+ call: payload
2480
+ }) ? {
2481
+ message: base,
2482
+ cursor: cursor.stream
2483
+ } : projectUIMessageStream({
2484
+ messageId: payload.messageId,
2485
+ base,
2486
+ cursor: cursor.stream,
2487
+ chunks: [toolCalledChunk(payload)]
2488
+ }),
2489
+ throughIndex: event.index
2490
+ } : projectedMessageStream({
2491
+ projection: activeProjection,
2492
+ current: base
2493
+ });
2494
+ activeProjection.cursor = paused ? null : {
2495
+ throughIndex: projected.throughIndex,
2496
+ stream: projected.cursor
2497
+ };
2119
2498
  const next = {
2120
2499
  ...state,
2121
2500
  activeProjection,
2501
+ messages: upsertResponse({
2502
+ messages: state.messages,
2503
+ message: paused ? projectedResponse({
2504
+ state,
2505
+ projection: activeProjection
2506
+ }) : projected.message,
2507
+ inputId: state.activeGeneration?.messageId ?? state.active?.input.id ?? payload.messageId
2508
+ }),
2122
2509
  tools: projectedTools(activeProjection)
2123
2510
  };
2124
2511
  return {
@@ -2261,30 +2648,41 @@ function reduceAIState(state, event) {
2261
2648
  case "ai.compaction.requested": {
2262
2649
  const payload = event.payload;
2263
2650
  if (state.activeGeneration?.generationId !== payload.generationId) return state;
2264
- return {
2651
+ return addCompactionPart({
2265
2652
  ...state,
2266
2653
  compaction: {
2267
2654
  status: "running",
2268
2655
  ...payload
2269
2656
  }
2270
- };
2657
+ }, payload);
2271
2658
  }
2272
2659
  case "ai.compaction.completed": {
2273
2660
  const payload = event.payload;
2274
2661
  if (state.activeGeneration?.generationId !== payload.generationId || state.compaction?.status !== "running" || state.compaction.generationId !== payload.generationId) return state;
2662
+ const { messages: _messages, retainedMessageIds: _retained, ...summary } = payload;
2275
2663
  const compaction = {
2276
2664
  status: "completed",
2277
- ...payload,
2665
+ ...summary,
2278
2666
  throughIndex: payload.throughIndex ?? state.activeProjection.promptThroughIndex
2279
2667
  };
2280
- return {
2281
- ...state,
2282
- compaction,
2283
- activeProjection: state.activeProjection && {
2284
- ...state.activeProjection,
2285
- baseCompaction: compaction
2668
+ const projection = state.activeProjection;
2669
+ return settleCompactionPart({
2670
+ state: {
2671
+ ...state,
2672
+ compaction,
2673
+ activeProjection: projection && {
2674
+ ...projection,
2675
+ baseCompaction: compaction,
2676
+ ...projection.base === void 0 ? {} : { base: { ...projection.base } }
2677
+ }
2678
+ },
2679
+ generationId: payload.generationId,
2680
+ data: {
2681
+ status: "completed",
2682
+ ...summary.summary === void 0 ? {} : { summary: summary.summary },
2683
+ ...summary.usage === void 0 ? {} : { usage: summary.usage }
2286
2684
  }
2287
- };
2685
+ });
2288
2686
  }
2289
2687
  default: return state;
2290
2688
  }
@@ -2295,18 +2693,6 @@ function deriveUIMessages(history) {
2295
2693
  for (const event of history) state = reduceAIState(state, event);
2296
2694
  return state.messages;
2297
2695
  }
2298
- /** Build the standard AI state projection for any contract containing the protocol. */
2299
- function createReducer(options) {
2300
- const reducer = options.contract.reducer({
2301
- name: options.name ?? "a2.ai.state.v22",
2302
- initialState: initialState(),
2303
- ...options.stateSchema === void 0 ? {} : { stateSchema: options.stateSchema }
2304
- }).fold(options.fold ?? ((state, event) => reduceAIState(state, event)));
2305
- return storedAIReducer({
2306
- ...reducer,
2307
- [clientStateProjection]: projectAIClientState
2308
- });
2309
- }
2310
2696
  const commandInput = (payload) => [{
2311
2697
  type: "ai.control.requested",
2312
2698
  payload
@@ -2407,45 +2793,64 @@ const inputs = {
2407
2793
  * Read the durable handler context ambient to the current AI tool
2408
2794
  * execution. A2 runs each local tool's `execute` inside this scope, so
2409
2795
  * plain AI SDK `tool()` definitions reach their `event`, `attempt`,
2410
- * `session`, and `signal` without an A2-specific wrapper. The agent (or
2411
- * contract) argument carries the event types and is verified against
2412
- * the executing server's contract.
2796
+ * `session`, and `signal` without an A2-specific wrapper. Pass the
2797
+ * agent's contract to type application events; it is verified against
2798
+ * the executing agent.
2413
2799
  */
2414
- function handlerContext(agentOrContract) {
2415
- const contract = "contract" in agentOrContract ? agentOrContract.contract : agentOrContract;
2800
+ function handlerContext(contract) {
2416
2801
  const scope = ambientToolScopeStorage()?.getStore();
2417
- if (scope === void 0) throw new TypeError(`handlerContext('${contract.name}') requires an ambient A2 tool execution: call it inside a local tool's execute, or wrap direct tool tests with runWithHandlerContext`);
2418
- if (scope.contract !== void 0 && scope.contract.name !== contract.name) throw new TypeError(`handlerContext('${contract.name}') was called during a tool execution for agent '${scope.contract.name}'`);
2802
+ if (scope === void 0) throw new TypeError("handlerContext() requires an ambient A2 tool execution: call it inside a local tool's execute, or wrap direct tool tests with runWithHandlerContext");
2803
+ if (contract !== void 0 && scope.contract !== void 0 && scope.contract.name !== contract.name) throw new TypeError(`handlerContext('${contract.name}') was called during a tool execution for agent '${scope.contract.name}'`);
2419
2804
  return scope.context;
2420
2805
  }
2806
+ /** The built-in AI reducer's name, and so its checkpoint identity. */
2807
+ const AI_STATE_REDUCER = "a2.ai.state.v26";
2421
2808
  /**
2422
- * Define an agent: built-in AI protocol + application events and one state
2423
- * reducer. The result is isomorphic.
2809
+ * The built-in AI protocol plus application events, as a core contract whose
2810
+ * reducer folds `AIState`. Isomorphic: share it when a client needs
2811
+ * application events or extended state; otherwise `agent({ name })` on the
2812
+ * server builds one and clients use the built-in protocol.
2424
2813
  */
2425
- function agent(options) {
2814
+ function agentContract(options) {
2426
2815
  const builtIns = createEvents(options.messageSchema === void 0 ? void 0 : { messageSchema: options.messageSchema });
2427
2816
  const extensions = options.events ?? {};
2428
2817
  for (const key of Object.keys(extensions)) if (Object.hasOwn(builtIns, key)) throw new TypeError(`agent event '${key}' conflicts with a built-in event`);
2429
- const agentContract = contract({
2818
+ const vocabulary = contract({
2430
2819
  name: options.name,
2431
2820
  events: {
2432
2821
  ...builtIns,
2433
2822
  ...extensions
2434
2823
  }
2435
2824
  });
2436
- const reducer = agentContract.reducer({
2437
- name: options.reducerName ?? "a2.ai.state.v22",
2438
- initialState: initialState()
2439
- }).fold((state, event) => reduceAIState(state, event));
2440
- return {
2441
- contract: agentContract,
2442
- reducer: storedAIReducer({
2443
- ...reducer,
2444
- [clientStateProjection]: projectAIClientState
2445
- })
2446
- };
2825
+ const app = options.reducer;
2826
+ const aiState = initialState();
2827
+ for (const key of Object.keys(app?.initialState ?? {})) if (isAIStateKey(key)) throw new TypeError(`agent state '${key}' conflicts with AIState`);
2828
+ const reducer$1 = reducer({
2829
+ contract: vocabulary,
2830
+ name: app === void 0 ? AI_STATE_REDUCER : `${app.name}+${AI_STATE_REDUCER}`,
2831
+ initialState: {
2832
+ ...aiState,
2833
+ ...app?.initialState
2834
+ }
2835
+ }).fold((state, event) => {
2836
+ const next = reduceAIState(state, event);
2837
+ if (app === void 0) return next;
2838
+ const before = appSlice(state);
2839
+ const after = app.fold(before, event);
2840
+ return after === before ? next : withAppSlice(next, after);
2841
+ });
2842
+ const shared = contract({
2843
+ name: options.name,
2844
+ events: vocabulary.events,
2845
+ reducer: withAIStorage(withReducerRuntime({
2846
+ reducer: reducer$1,
2847
+ project: projectAIClientState
2848
+ }))
2849
+ });
2850
+ registerAgentReducer(shared.reducer, app);
2851
+ return shared;
2447
2852
  }
2448
2853
  //#endregion
2449
- export { interruptUIMessage as C, progressBatches as S, projectedMessage as _, events as a, upsertResponse as b, reduceAIState as c, AI_EVENT_ID_PREFIX as d, aiEventId as f, advanceProjection as g, controlCommit as h, deriveUIMessages as i, aiStateReducer as l, applyControlChanges as m, createEvents as n, handlerContext as o, initialControlState as p, createReducer as r, inputs as s, agent as t, storedAIReducer as u, reduceToolActivity as v, pauseUIMessage as w, flattenProgressBatches as x, upsertMessage as y };
2854
+ export { interruptUIMessage as A, reduceToolActivity as C, flattenProgressBatches as D, upsertResponse as E, assertMessageBase as M, restoreMessageBase as N, progressBatches as O, projectedMessage as S, upsertMessage as T, applyControlChanges as _, events as a, findMessage as b, reasoningLevels as c, appSlice as d, isAgentContract as f, initialControlState as g, aiEventId as h, deriveUIMessages as i, pauseUIMessage as j, progressOwnedBy as k, reduceAIState as l, AI_EVENT_ID_PREFIX as m, agentContract as n, handlerContext as o, withAIStorage as p, createEvents as r, inputs as s, AI_STATE_REDUCER as t, appReducerOf as u, controlCommit as v, retainMessageBase as w, isCompactionPart as x, advanceProjection as y };
2450
2855
 
2451
- //# sourceMappingURL=ai-DgOBltJ_.js.map
2856
+ //# sourceMappingURL=ai-UrWfKeuv.js.map