experimental-a2 0.16.0 → 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 (334) hide show
  1. package/CHANGELOG.md +360 -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-CrLYwNEx.js → ai-UrWfKeuv.js} +966 -541
  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 +466 -265
  32. package/dist/ai-server.js.map +1 -1
  33. package/dist/{ai-HJ9fHfYI.d.ts → ai-vpGtcaCB.d.ts} +115 -101
  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 +680 -580
  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 +662 -195
  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 +39 -2
  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 +94 -55
  256. package/src/ai-control.ts +2 -0
  257. package/src/ai-coordinator.ts +23 -22
  258. package/src/ai-message-base.ts +209 -0
  259. package/src/ai-message-projection.ts +382 -58
  260. package/src/ai-progress-batches.ts +26 -0
  261. package/src/ai-projector.ts +95 -14
  262. package/src/ai-react.ts +146 -0
  263. package/src/ai-server.browser.ts +2 -2
  264. package/src/ai-server.ts +20 -1951
  265. package/src/ai-stored-state.ts +48 -163
  266. package/src/ai.ts +343 -276
  267. package/src/bootstrap-stream.ts +72 -0
  268. package/src/bounded-cache.ts +55 -0
  269. package/src/client-render-session.ts +49 -0
  270. package/src/client.ts +688 -151
  271. package/src/contract.ts +92 -42
  272. package/src/deterministic-id.ts +37 -0
  273. package/src/equal-json.ts +32 -0
  274. package/src/errors.ts +13 -1
  275. package/src/index.ts +5 -1
  276. package/src/initial-snapshot.ts +39 -0
  277. package/src/internal.ts +45 -0
  278. package/src/postgres-notification-scope.ts +14 -3
  279. package/src/react.ts +171 -206
  280. package/src/redis-http-reply.ts +256 -0
  281. package/src/redis-http-stream.ts +73 -0
  282. package/src/redis-http-subscriptions.ts +13 -5
  283. package/src/reducer-runtime.ts +52 -0
  284. package/src/reducer.ts +92 -10
  285. package/src/scheduler-qstash.ts +49 -20
  286. package/src/scheduler-task.ts +34 -2
  287. package/src/scheduler-vercel.ts +32 -84
  288. package/src/server-fetch.ts +139 -35
  289. package/src/server.ts +957 -237
  290. package/src/session-clear.ts +23 -0
  291. package/src/session-socket.ts +154 -49
  292. package/src/sse.ts +71 -4
  293. package/src/store-memory.ts +220 -78
  294. package/src/store-polling.ts +18 -4
  295. package/src/store-postgres.ts +461 -114
  296. package/src/store-redis-core.ts +1260 -434
  297. package/src/store-redis-http.ts +18 -2
  298. package/src/store-redis-notify.ts +260 -85
  299. package/src/store-redis.ts +19 -9
  300. package/src/store-sqlite.ts +259 -45
  301. package/src/store.ts +46 -1
  302. package/src/stream-buffer.ts +43 -0
  303. package/src/telemetry.ts +13 -1
  304. package/src/wire.ts +241 -13
  305. package/dist/actor-ohPC-81x.d.ts.map +0 -1
  306. package/dist/actor-shared-B5tJfzt-.js.map +0 -1
  307. package/dist/ai-CrLYwNEx.js.map +0 -1
  308. package/dist/ai-HJ9fHfYI.d.ts.map +0 -1
  309. package/dist/client-BYzHjkwU.js.map +0 -1
  310. package/dist/client-CzyacQpJ.d.ts.map +0 -1
  311. package/dist/contract-CKRg_E4q.js.map +0 -1
  312. package/dist/errors-DCk6ch5n.js.map +0 -1
  313. package/dist/errors-DvhSXnxk.d.ts.map +0 -1
  314. package/dist/internal-Dq2qYxou.js.map +0 -1
  315. package/dist/react.d.ts.map +0 -1
  316. package/dist/reducer-DEMjEY_O.js +0 -29
  317. package/dist/reducer-DEMjEY_O.js.map +0 -1
  318. package/dist/reducer-otzHjuJj.d.ts.map +0 -1
  319. package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
  320. package/dist/server-BD5ckxZb.js.map +0 -1
  321. package/dist/server-DbD7IJVK.d.ts.map +0 -1
  322. package/dist/store-DyZM6fS5.d.ts.map +0 -1
  323. package/dist/store-polling-CmxUbV93.js.map +0 -1
  324. package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
  325. package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
  326. package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
  327. package/dist/validate-XKT4FSNn.js +0 -30
  328. package/dist/validate-XKT4FSNn.js.map +0 -1
  329. package/dist/wire-BO5wWCb1.js.map +0 -1
  330. package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
  331. package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
  332. package/examples/playground/app/agent/compaction-settings.ts +0 -49
  333. package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
  334. package/examples/playground/app/agent/compaction-timeline.ts +0 -208
package/dist/ai-server.js CHANGED
@@ -1,8 +1,8 @@
1
- import { n as validateSync } from "./validate-XKT4FSNn.js";
2
- import { t as A2Error } from "./errors-DCk6ch5n.js";
3
- import { f as consumeSchedulerSendFailure, p as installAmbientToolScopeStorage } from "./internal-Dq2qYxou.js";
4
- import { i as setServerFetchHooks, t as createServer } from "./server-BD5ckxZb.js";
5
- import { C as applyControlChanges, S as initialControlState, _ as progressBatches, a as events, b as AI_EVENT_ID_PREFIX, d as advanceProjection, f as projectedMessage, g as flattenProgressBatches, h as upsertResponse, l as aiStateReducer, m as upsertMessage, p as reduceToolActivity, u as storedAIReducer, v as interruptUIMessage, w as controlCommit, x as aiEventId, y as pauseUIMessage } from "./ai-CrLYwNEx.js";
1
+ import { i as A2Error } from "./session-clear-DGI4jTQ5.js";
2
+ import { a as reducer, s as validateSync } from "./reducer-BAXc8JyF.js";
3
+ import { m as installAmbientToolScopeStorage, p as consumeSchedulerSendFailure } from "./internal-CbVvEwad.js";
4
+ import { a as CONSOLE_TELEMETRY, l as boundedCache, o as setServerFetchHooks, t as createServer } from "./server-Rj64fNvX.js";
5
+ import { A as interruptUIMessage, C as reduceToolActivity, D as flattenProgressBatches, E as upsertResponse, M as assertMessageBase, N as restoreMessageBase, O as progressBatches, S as projectedMessage, T as upsertMessage, _ as applyControlChanges, a as events, b as findMessage, d as appSlice, f as isAgentContract, g as initialControlState, h as aiEventId, j as pauseUIMessage, k as progressOwnedBy, m as AI_EVENT_ID_PREFIX, n as agentContract, p as withAIStorage, u as appReducerOf, v as controlCommit, w as retainMessageBase, y as advanceProjection } from "./ai-UrWfKeuv.js";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { createHash } from "node:crypto";
8
8
  import { asSchema, convertToModelMessages, isToolUIPart, stepCountIs, streamText, toUIMessageStream } from "ai";
@@ -118,7 +118,7 @@ const foldCoordinator = (state, event) => {
118
118
  }));
119
119
  const inputTokens = completion.usage?.inputTokens;
120
120
  const estimate = completion.inputTokenEstimate;
121
- return inputTokens !== void 0 && Number.isFinite(inputTokens) && inputTokens >= 0 && estimate !== void 0 ? {
121
+ return inputTokens !== void 0 && Number.isFinite(inputTokens) && inputTokens >= 0 && estimate !== void 0 && state.response.generation.model !== void 0 ? {
122
122
  ...uncalibrated,
123
123
  calibration: {
124
124
  model: state.response.generation.model,
@@ -218,7 +218,8 @@ const foldCoordinator = (state, event) => {
218
218
  default: return state;
219
219
  }
220
220
  };
221
- const aiCoordinatorReducer = (contract) => contract.reducer({
221
+ const aiCoordinatorReducer = (contract) => reducer({
222
+ contract,
222
223
  name: "a2.ai.coordinator.v6",
223
224
  initialState: {
224
225
  closed: false,
@@ -238,16 +239,41 @@ var ControlCancelled = class extends Error {};
238
239
  const callReadyForModel = (call) => call.terminal || call.call.providerExecuted === true && !call.call.supportsDeferredResults && call.response !== void 0;
239
240
  const approvalBlocksExecution = (call) => call.approval !== void 0 && call.response?.approved !== true;
240
241
  const createControlRuntime = (options) => {
241
- const reducer = options.agent.contract.reducer({
242
- name: "a2.ai.control.v4",
242
+ const telemetry = options.telemetry ?? CONSOLE_TELEMETRY;
243
+ const reducer$2 = reducer({
244
+ contract: options.contract,
245
+ name: "a2.ai.control.v5",
243
246
  initialState: initialControlState()
244
- }).fold((state, event) => event.type === "ai.control.committed" ? applyControlChanges({
245
- state,
246
- changes: event.payload.changes
247
- }) : state);
248
- const coordinator = aiCoordinatorReducer(options.agent.contract);
247
+ }).fold((state, event) => {
248
+ if (event.type === "ai.control.requested") {
249
+ const command = event.payload;
250
+ if (command.action === "stop") return {
251
+ ...state,
252
+ pendingStops: {
253
+ ...state.pendingStops,
254
+ [event.id]: command.turnId
255
+ }
256
+ };
257
+ }
258
+ if (event.type === "ai.control.decided") {
259
+ const receipt = event.payload;
260
+ if (Object.hasOwn(state.pendingStops, receipt.commandId)) {
261
+ const pendingStops = { ...state.pendingStops };
262
+ delete pendingStops[receipt.commandId];
263
+ return {
264
+ ...state,
265
+ pendingStops
266
+ };
267
+ }
268
+ }
269
+ return event.type === "ai.control.committed" ? applyControlChanges({
270
+ state,
271
+ changes: event.payload.changes
272
+ }) : state;
273
+ });
274
+ const coordinator = aiCoordinatorReducer(options.contract);
249
275
  const handler = async (ctx) => {
250
- const snapshot = await ctx.session.state(reducer, { through: "latest" });
276
+ const snapshot = await ctx.session.state(reducer$2, { through: "latest" });
251
277
  const before = structuredClone(snapshot.state);
252
278
  const moves = [];
253
279
  const state = structuredClone(snapshot.state);
@@ -479,7 +505,7 @@ const createControlRuntime = (options) => {
479
505
  responseMessageId: response.responseMessageId,
480
506
  reason: "retry"
481
507
  },
482
- id: await aiEventId("a2.ai.request.retry", options.agent.contract.name, ctx.event.sessionId, command.request.retryId)
508
+ id: await aiEventId("a2.ai.request.retry", options.contract.name, ctx.event.sessionId, command.request.retryId)
483
509
  });
484
510
  } else reason = "stale-turn";
485
511
  break;
@@ -611,7 +637,7 @@ const createControlRuntime = (options) => {
611
637
  message: item.message,
612
638
  generate: item.generate
613
639
  },
614
- id: await aiEventId("a2.ai.admitted", options.agent.contract.name, ctx.event.sessionId, item.id)
640
+ id: await aiEventId("a2.ai.admitted", options.contract.name, ctx.event.sessionId, item.id)
615
641
  });
616
642
  if (!item.generate) continue;
617
643
  state.active = {
@@ -622,7 +648,7 @@ const createControlRuntime = (options) => {
622
648
  suspended: false,
623
649
  model: null
624
650
  };
625
- const requestId = await aiEventId("a2.ai.request.message", options.agent.contract.name, ctx.event.sessionId, item.id);
651
+ const requestId = await aiEventId("a2.ai.request.message", options.contract.name, ctx.event.sessionId, item.id);
626
652
  requestModel({
627
653
  request: {
628
654
  messageId: item.id,
@@ -662,7 +688,7 @@ const createControlRuntime = (options) => {
662
688
  const payload = ctx.event.payload;
663
689
  const { turnId, version } = "turnId" in payload ? payload : payload.control;
664
690
  for (const event of events) {
665
- const schema = options.agent.contract.events[event.type];
691
+ const schema = options.contract.events[event.type];
666
692
  const result = validateSync(schema, event.payload, `AI worker ${event.type}`);
667
693
  if (result.issues) throw new TypeError(result.issues.map((issue) => issue.message).join("; "));
668
694
  }
@@ -696,32 +722,39 @@ const createControlRuntime = (options) => {
696
722
  });
697
723
  const persisted = await ctx.session.append(name, ...progress, input);
698
724
  const index = persisted.at(-1).index;
699
- const stream = ctx.session.stream({ startAfter: index })[Symbol.asyncIterator]();
700
- const canonical = [];
701
- const wanted = new Set(lifecycle.map((event) => event.id));
702
- let rejectAbort;
703
- const aborted = new Promise((_, reject) => {
704
- rejectAbort = reject;
705
- });
706
- const onAbort = () => {
707
- rejectAbort(ctx.signal.reason);
708
- };
709
- ctx.signal.addEventListener("abort", onAbort, { once: true });
710
- try {
711
- ctx.signal.throwIfAborted();
712
- for (;;) {
713
- const item = await Promise.race([stream.next(), aborted]);
714
- if (item.done) throw new Error("AI control acknowledgement stream ended");
715
- if (wanted.has(item.value.id)) canonical.push(item.value);
716
- if (item.value.type === "ai.control.decided" && item.value.payload.commandId === input.id) {
717
- if (item.value.payload.outcome === "rejected") throw new ControlCancelled("stale AI work");
718
- return [...persisted.filter((event) => event.type !== "ai.work.reported"), ...canonical];
725
+ return telemetry.span("a2.ai.ack", {
726
+ "a2.contract": options.contract.name,
727
+ "a2.session_id": ctx.event.sessionId,
728
+ "a2.ai.ack.request_id": ctx.event.id,
729
+ "a2.ai.ack.report_id": input.id
730
+ }, async () => {
731
+ const stream = ctx.session.stream({ startAfter: index })[Symbol.asyncIterator]();
732
+ const canonical = [];
733
+ const wanted = new Set(lifecycle.map((event) => event.id));
734
+ let rejectAbort;
735
+ const aborted = new Promise((_, reject) => {
736
+ rejectAbort = reject;
737
+ });
738
+ const onAbort = () => {
739
+ rejectAbort(ctx.signal.reason);
740
+ };
741
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
742
+ try {
743
+ ctx.signal.throwIfAborted();
744
+ for (;;) {
745
+ const item = await Promise.race([stream.next(), aborted]);
746
+ if (item.done) throw new Error("AI control acknowledgement stream ended");
747
+ if (wanted.has(item.value.id)) canonical.push(item.value);
748
+ if (item.value.type === "ai.control.decided" && item.value.payload.commandId === input.id) {
749
+ if (item.value.payload.outcome === "rejected") throw new ControlCancelled("stale AI work");
750
+ return [...persisted.filter((event) => event.type !== "ai.work.reported"), ...canonical];
751
+ }
719
752
  }
753
+ } finally {
754
+ ctx.signal.removeEventListener("abort", onAbort);
755
+ await stream.return?.();
720
756
  }
721
- } finally {
722
- ctx.signal.removeEventListener("abort", onAbort);
723
- await stream.return?.();
724
- }
757
+ });
725
758
  };
726
759
  const settled = ({ ctx, events }) => report({
727
760
  ctx,
@@ -738,7 +771,7 @@ const createControlRuntime = (options) => {
738
771
  append,
739
772
  settled,
740
773
  cancelled,
741
- reducer,
774
+ reducer: reducer$2,
742
775
  coordinator
743
776
  };
744
777
  };
@@ -779,19 +812,29 @@ const progress = (value) => {
779
812
  return count === value["length"];
780
813
  };
781
814
  const indexedEvents = (value, types) => Array.isArray(value) && value.every((event) => record(event) && index(event["index"]) && types.includes(event["type"]) && matches(events[event["type"]], event["payload"]));
815
+ const restorableBase = (options) => {
816
+ assertMessageBase(options.base);
817
+ return isMessage(restoreMessageBase({
818
+ current: options.current,
819
+ base: options.base
820
+ }));
821
+ };
782
822
  const projection = (value, messages) => {
783
823
  if (!record(value) || Object.keys(value).some((key) => ![
784
824
  "generationId",
785
825
  "responseMessageId",
786
826
  "promptThroughIndex",
787
827
  "completionIndex",
788
- "baseMessage",
828
+ "base",
789
829
  "baseCompaction",
790
830
  "batches",
791
831
  "toolEvents",
792
832
  "approvalEvents",
793
833
  "cursor"
794
- ].includes(key)) || !id(value["generationId"]) || !id(value["responseMessageId"]) || !index(value["promptThroughIndex"]) || value["completionIndex"] !== void 0 && !index(value["completionIndex"]) || value["baseMessage"] !== void 0 && !isMessage(value["baseMessage"]) || value["baseCompaction"] !== void 0 && !compaction(value["baseCompaction"]) || !progress(value["batches"]) || !indexedEvents(value["toolEvents"], ["ai.tool.called", "ai.tool.result"]) || !indexedEvents(value["approvalEvents"], ["ai.approval.requested", "ai.approval.responded"])) return false;
834
+ ].includes(key)) || !id(value["generationId"]) || !id(value["responseMessageId"]) || !index(value["promptThroughIndex"]) || value["completionIndex"] !== void 0 && !index(value["completionIndex"]) || value["base"] !== void 0 && !restorableBase({
835
+ base: value["base"],
836
+ current: messages.find((message) => message.id === value["responseMessageId"])
837
+ }) || value["baseCompaction"] !== void 0 && !compaction(value["baseCompaction"]) || !progress(value["batches"]) || !indexedEvents(value["toolEvents"], ["ai.tool.called", "ai.tool.result"]) || !indexedEvents(value["approvalEvents"], ["ai.approval.requested", "ai.approval.responded"])) return false;
795
838
  const cursor = value["cursor"];
796
839
  if (cursor === void 0 || cursor === null) return true;
797
840
  if (!record(cursor) || !index(cursor["throughIndex"]) || !record(cursor["stream"])) return false;
@@ -825,7 +868,7 @@ const authority = (value) => value === null || record(value) && Object.keys(valu
825
868
  "pausing",
826
869
  "paused"
827
870
  ].includes(String(value["phase"])));
828
- const contextSchema = () => ({ "~standard": {
871
+ const contextSchema = (app) => ({ "~standard": {
829
872
  version: 1,
830
873
  vendor: "a2-context",
831
874
  validate(value) {
@@ -838,7 +881,8 @@ const contextSchema = () => ({ "~standard": {
838
881
  "authority",
839
882
  "generation",
840
883
  "activeProjection",
841
- "messageIndexes"
884
+ "messageIndexes",
885
+ ...app === void 0 ? [] : ["app"]
842
886
  ];
843
887
  try {
844
888
  if (!record(value) || keys.some((key) => !Object.hasOwn(value, key)) || Object.keys(value).some((key) => !keys.includes(key))) return invalid;
@@ -850,7 +894,7 @@ const contextSchema = () => ({ "~standard": {
850
894
  modelId,
851
895
  limits: entry["limits"]
852
896
  }))) || !indexes(value["messageIndexes"], messages)) return invalid;
853
- if (generation !== null && (!record(generation) || !matches(events["ai.generation.started"], generation["owner"]) || ![
897
+ if (generation !== null && (!record(generation) || !matches(events["ai.generation.started"], generation["owner"]) || !index(generation["startIndex"]) || ![
854
898
  "streaming",
855
899
  "completed",
856
900
  "paused",
@@ -861,6 +905,7 @@ const contextSchema = () => ({ "~standard": {
861
905
  if (current !== null && (!projection(current, messages) || !record(current) || current["generationId"] !== owner?.["generationId"] || current["responseMessageId"] !== owner?.["responseMessageId"])) return invalid;
862
906
  if (generation !== null && generation["phase"] !== "superseded" && current === null) return invalid;
863
907
  if (value["closed"] && (generation !== null || current !== null)) return invalid;
908
+ if (app !== void 0 && !record(value["app"])) return invalid;
864
909
  return { value };
865
910
  } catch {
866
911
  return invalid;
@@ -888,14 +933,24 @@ const replaceResponse = ({ state, message, index }) => {
888
933
  }
889
934
  };
890
935
  };
891
- const calledTools = (projection) => /* @__PURE__ */ new Set([...projection.baseMessage?.parts.flatMap((part) => "toolCallId" in part ? [part.toolCallId] : []) ?? [], ...projection.toolEvents.filter((event) => event.type === "ai.tool.called").map((event) => event.payload.toolCallId)]);
936
+ const calledTools = ({ projection, current }) => /* @__PURE__ */ new Set([...restoreMessageBase({
937
+ current,
938
+ base: projection.base
939
+ })?.parts.flatMap((part) => "toolCallId" in part ? [part.toolCallId] : []) ?? [], ...projection.toolEvents.filter((event) => event.type === "ai.tool.called").map((event) => event.payload.toolCallId)]);
892
940
  const renderProjection = ({ state, index }) => {
893
941
  const projection = state.activeProjection;
894
- let message = projectedMessage(projection);
895
- if (projection.baseMessage === void 0 && projection.batches.length === 0 && message.parts.length === 0) return state;
942
+ const current = findMessage(state.messages, projection.responseMessageId);
943
+ let message = projectedMessage({
944
+ projection,
945
+ current
946
+ });
947
+ if (projection.base === void 0 && projection.batches.length === 0 && message.parts.length === 0) return state;
896
948
  if (state.generation?.phase === "paused") message = pauseUIMessage({
897
949
  message,
898
- toolCallIds: calledTools(projection)
950
+ toolCallIds: calledTools({
951
+ projection,
952
+ current
953
+ })
899
954
  });
900
955
  if (state.generation?.phase === "failed") message = interruptUIMessage(message, `Generation failed before tool completion: ${state.generation.error}`);
901
956
  return replaceResponse({
@@ -913,9 +968,13 @@ const renderProjection = ({ state, index }) => {
913
968
  const generationBase = (state) => {
914
969
  const projection = state.activeProjection;
915
970
  if (!projection) return state;
971
+ const base = restoreMessageBase({
972
+ current: findMessage(state.messages, projection.responseMessageId),
973
+ base: projection.base
974
+ });
916
975
  return {
917
976
  ...state,
918
- messages: projection.baseMessage === void 0 ? state.messages.filter((message) => message.id !== projection.responseMessageId) : upsertMessage(state.messages, projection.baseMessage),
977
+ messages: base === void 0 ? state.messages.filter((message) => message.id !== projection.responseMessageId) : upsertMessage(state.messages, base),
919
978
  compaction: projection.baseCompaction ?? null
920
979
  };
921
980
  };
@@ -934,22 +993,37 @@ const endGeneration = (state) => ({
934
993
  activeProjection: null
935
994
  });
936
995
  const ownsGeneration = (state, owner) => state.generation?.owner.generationId === owner.generationId && state.generation.owner.requestId === owner.requestId && state.generation.phase !== "superseded";
937
- const createContextReducer = ({ agent }) => {
938
- const reducer = agent.contract.reducer({
939
- name: "a2.ai.context.v3",
940
- initialState: {
941
- messages: [],
942
- compaction: null,
943
- modelMetadata: {},
944
- closed: false,
945
- authority: null,
946
- generation: null,
947
- activeProjection: null,
948
- messageIndexes: {}
949
- },
950
- stateSchema: contextSchema()
951
- }).fold((state, event) => reduceContext(state, event));
952
- return storedAIReducer(reducer);
996
+ const createContextReducer = ({ contract }) => {
997
+ const app = appReducerOf(contract);
998
+ const initialState = {
999
+ messages: [],
1000
+ compaction: null,
1001
+ modelMetadata: {},
1002
+ closed: false,
1003
+ authority: null,
1004
+ generation: null,
1005
+ activeProjection: null,
1006
+ messageIndexes: {},
1007
+ ...app === void 0 ? {} : { app: app.initialState }
1008
+ };
1009
+ const reducer$1 = reducer({
1010
+ contract,
1011
+ name: app === void 0 ? "a2.ai.context.v6" : `${app.name}+a2.ai.context.v6`,
1012
+ initialState,
1013
+ stateSchema: contextSchema(app)
1014
+ }).fold((state, event) => {
1015
+ const after = retainMessageBase({
1016
+ before: state,
1017
+ after: reduceContext(state, event)
1018
+ });
1019
+ if (app === void 0) return after;
1020
+ const next = app.fold(state.app, event);
1021
+ return next === state.app ? after : {
1022
+ ...after,
1023
+ app: appSlice(next)
1024
+ };
1025
+ });
1026
+ return withAIStorage(reducer$1);
953
1027
  };
954
1028
  const reduceContext = (state, event) => {
955
1029
  if (state.closed) return state;
@@ -1006,6 +1080,7 @@ const reduceContext = (state, event) => {
1006
1080
  ...base,
1007
1081
  generation: {
1008
1082
  owner,
1083
+ startIndex: event.index,
1009
1084
  phase: "streaming",
1010
1085
  ...previous?.owner.responseMessageId === owner.responseMessageId && previous.responseIndex !== void 0 ? { responseIndex: previous.responseIndex } : {}
1011
1086
  },
@@ -1013,7 +1088,7 @@ const reduceContext = (state, event) => {
1013
1088
  generationId: owner.generationId,
1014
1089
  responseMessageId: owner.responseMessageId,
1015
1090
  promptThroughIndex: owner.promptThroughIndex ?? event.index - 1,
1016
- ...baseMessage === void 0 ? {} : { baseMessage },
1091
+ ...baseMessage === void 0 ? {} : { base: { partCount: baseMessage.parts.length } },
1017
1092
  baseCompaction: base.compaction,
1018
1093
  batches: progressBatches(),
1019
1094
  toolEvents: [],
@@ -1023,7 +1098,12 @@ const reduceContext = (state, event) => {
1023
1098
  }
1024
1099
  case "ai.generation.progress": {
1025
1100
  const payload = event.payload;
1026
- if (!ownsGeneration(state, payload) || state.generation.owner.responseMessageId !== payload.responseMessageId || state.generation.phase !== "streaming" || !state.activeProjection) return state;
1101
+ const generation = state.generation;
1102
+ if (generation === null || generation.phase !== "streaming" || !progressOwnedBy({
1103
+ payload,
1104
+ owner: generation.owner,
1105
+ startIndex: generation.startIndex
1106
+ }) || !state.activeProjection) return state;
1027
1107
  const projection = state.activeProjection;
1028
1108
  const progress = advanceProjection({
1029
1109
  projection,
@@ -1102,9 +1182,12 @@ const reduceContext = (state, event) => {
1102
1182
  approvalEvents: projection.approvalEvents.filter((item) => item.index <= cutoff)
1103
1183
  };
1104
1184
  let next = generationBase(state);
1105
- if (retained.baseMessage !== void 0 || retained.batches.length > 0) next = replaceResponse({
1185
+ if (retained.base !== void 0 || retained.batches.length > 0) next = replaceResponse({
1106
1186
  state: next,
1107
- message: interruptUIMessage(projectedMessage(retained)),
1187
+ message: interruptUIMessage(projectedMessage({
1188
+ projection: retained,
1189
+ current: findMessage(state.messages, retained.responseMessageId)
1190
+ })),
1108
1191
  index: event.index
1109
1192
  });
1110
1193
  return endGeneration(next);
@@ -1116,7 +1199,10 @@ const reduceContext = (state, event) => {
1116
1199
  case "ai.generation.paused": {
1117
1200
  const { generationId } = event.payload;
1118
1201
  if (state.generation?.owner.generationId !== generationId || state.generation.phase === "failed" || state.generation.phase === "superseded" || !state.activeProjection) return state;
1119
- const toolCallIds = calledTools(state.activeProjection);
1202
+ const toolCallIds = calledTools({
1203
+ projection: state.activeProjection,
1204
+ current: findMessage(state.messages, state.activeProjection.responseMessageId)
1205
+ });
1120
1206
  return renderProjection({
1121
1207
  state: {
1122
1208
  ...state,
@@ -1219,9 +1305,9 @@ const reduceContext = (state, event) => {
1219
1305
  ...projection,
1220
1306
  cursor: null
1221
1307
  };
1222
- delete activeProjection.baseMessage;
1308
+ delete activeProjection.base;
1223
1309
  const baseMessage = messages.find((message) => message.id === projection.responseMessageId);
1224
- if (baseMessage !== void 0) activeProjection.baseMessage = baseMessage;
1310
+ if (baseMessage !== void 0) activeProjection.base = { partCount: baseMessage.parts.length };
1225
1311
  const compaction = {
1226
1312
  status: "completed",
1227
1313
  generationId: payload.generationId,
@@ -1444,23 +1530,24 @@ const measuredInputTokens = (options) => {
1444
1530
  return previous !== void 0 && previous.model === options.model && options.estimate >= previous.estimate ? Math.ceil(previous.inputTokens + options.estimate - previous.estimate) : options.estimate;
1445
1531
  };
1446
1532
  //#endregion
1447
- //#region src/ai-server.ts
1533
+ //#region src/ai-agent.ts
1448
1534
  /**
1449
- * experimental-a2/ai/server — the server-only implementation of an experimental-a2/ai definition.
1450
- *
1451
- * `createHandlers()` exposes the ordinary A2 handler table;
1452
- * `createAgentServer()` is its batteries-included assembly with createServer.
1535
+ * The server-only implementation of the experimental-a2/ai protocol: the
1536
+ * handler table and `agent()`, its assembly as an ordinary A2 server.
1453
1537
  */
1454
- function validateAgentIngress(context) {
1455
- const rejected = context.events.find((event) => {
1456
- if (event.type !== "ai.control.requested") return true;
1457
- const command = event.payload;
1458
- if (command["action"] === "request-input" || command["action"] === "tool-result") return true;
1459
- if (command["action"] === "send" || command["action"] === "edit" || command["action"] === "steer") return command["message"]?.role !== "user";
1460
- return false;
1461
- });
1538
+ const isAgentIngressEvent = (event) => {
1539
+ if (event.type !== "ai.control.requested") return false;
1540
+ const command = event.payload;
1541
+ if (typeof command !== "object" || command === null) return false;
1542
+ const action = command.action;
1543
+ if (action === "request-input" || action === "tool-result") return false;
1544
+ if (action === "send" || action === "edit" || action === "steer") return command.message?.role === "user";
1545
+ return true;
1546
+ };
1547
+ const validateAgentIngress = ({ context, applicationEvents }) => {
1548
+ const rejected = context.events.find((event) => Object.hasOwn(events, event.type) ? !isAgentIngressEvent(event) : !applicationEvents);
1462
1549
  if (rejected !== void 0) throw new A2Error("INVALID_PAYLOAD", `event '${rejected.type}' is server-only for an AI agent`);
1463
- }
1550
+ };
1464
1551
  const checkAbort = (signal) => {
1465
1552
  if (!signal.aborted) return false;
1466
1553
  const reason = signal.reason;
@@ -1478,6 +1565,22 @@ const resolve = async (value, context) => {
1478
1565
  checkAbort(context.signal);
1479
1566
  return result;
1480
1567
  };
1568
+ const withModelSettings = (options) => {
1569
+ const { generation, settings } = options;
1570
+ if (settings.reasoning === void 0 && settings.speed === void 0) return generation;
1571
+ const { speed: _speed, ...gateway } = generation.providerOptions?.["gateway"] ?? {};
1572
+ return {
1573
+ ...generation,
1574
+ ...settings.reasoning === void 0 ? {} : { reasoning: settings.reasoning },
1575
+ ...settings.speed === void 0 ? {} : { providerOptions: {
1576
+ ...generation.providerOptions,
1577
+ gateway: settings.speed === "fast" ? {
1578
+ ...gateway,
1579
+ speed: "fast"
1580
+ } : gateway
1581
+ } }
1582
+ };
1583
+ };
1481
1584
  const modelName = (model) => {
1482
1585
  if (typeof model === "string") return model;
1483
1586
  if ("modelId" in model && typeof model.modelId === "string") return model.modelId;
@@ -1489,7 +1592,8 @@ const toolScopeStorage = installAmbientToolScopeStorage(() => new AsyncLocalStor
1489
1592
  function runWithHandlerContext(ctx, fn) {
1490
1593
  return toolScopeStorage.run({ context: ctx }, fn);
1491
1594
  }
1492
- const promptCacheKey = (sessionId, generationId) => `${sessionId}\u001f${generationId}`;
1595
+ /** Sessions whose latest step prompt one process keeps for that step's tools. */
1596
+ const PROMPT_SESSIONS = 64;
1493
1597
  const asyncIterator = (value) => {
1494
1598
  if (typeof value !== "object" || value === null) return void 0;
1495
1599
  const factory = Reflect.get(value, Symbol.asyncIterator);
@@ -1497,6 +1601,49 @@ const asyncIterator = (value) => {
1497
1601
  return factory.call(value);
1498
1602
  };
1499
1603
  const isBoundaryChunk = (chunk) => chunk !== void 0 && (chunk.type === "text-end" || chunk.type === "reasoning-end" || chunk.type === "tool-input-available" || chunk.type === "tool-input-error" || chunk.type === "tool-output-available" || chunk.type === "tool-output-error" || chunk.type === "tool-output-denied" || chunk.type === "tool-approval-request" || chunk.type === "finish" || chunk.type === "abort" || chunk.type === "error");
1604
+ const DELTA_KEYS = {
1605
+ "text-delta": /* @__PURE__ */ new Set([
1606
+ "type",
1607
+ "id",
1608
+ "delta",
1609
+ "providerMetadata"
1610
+ ]),
1611
+ "reasoning-delta": /* @__PURE__ */ new Set([
1612
+ "type",
1613
+ "id",
1614
+ "delta",
1615
+ "providerMetadata"
1616
+ ]),
1617
+ "tool-input-delta": /* @__PURE__ */ new Set([
1618
+ "type",
1619
+ "toolCallId",
1620
+ "inputTextDelta"
1621
+ ])
1622
+ };
1623
+ const isDeltaChunk = (chunk) => Object.hasOwn(DELTA_KEYS, chunk.type) && Object.keys(chunk).every((key) => DELTA_KEYS[chunk.type].has(key)) && typeof (chunk.type === "tool-input-delta" ? chunk.inputTextDelta : chunk.delta) === "string";
1624
+ const joinDeltas = (left, right) => {
1625
+ if (left.type === "tool-input-delta") return right.type === "tool-input-delta" && right.toolCallId === left.toolCallId ? {
1626
+ ...left,
1627
+ inputTextDelta: left.inputTextDelta + right.inputTextDelta
1628
+ } : void 0;
1629
+ if (right.type === "tool-input-delta" || right.type !== left.type || right.id !== left.id) return void 0;
1630
+ const providerMetadata = right.providerMetadata !== void 0 ? right.providerMetadata : left.providerMetadata;
1631
+ return {
1632
+ ...left,
1633
+ delta: left.delta + right.delta,
1634
+ ...providerMetadata === void 0 ? {} : { providerMetadata }
1635
+ };
1636
+ };
1637
+ const mergeDeltas = (chunks) => {
1638
+ const merged = [];
1639
+ for (const chunk of chunks) {
1640
+ const previous = merged.at(-1);
1641
+ const joined = previous !== void 0 && isDeltaChunk(previous) && isDeltaChunk(chunk) ? joinDeltas(previous, chunk) : void 0;
1642
+ if (joined === void 0) merged.push(chunk);
1643
+ else merged[merged.length - 1] = joined;
1644
+ }
1645
+ return merged;
1646
+ };
1500
1647
  const nextOrTimer = async (next, delayMs) => {
1501
1648
  let timer;
1502
1649
  try {
@@ -1518,7 +1665,7 @@ async function generateWithAISDK(options) {
1518
1665
  ...context.generation,
1519
1666
  headers: Object.keys(headers).some((key) => key.toLowerCase() === "x-session-affinity") ? headers : {
1520
1667
  ...headers,
1521
- "x-session-affinity": createHash("sha256").update(JSON.stringify([agentName, context.request.sessionId])).digest("hex")
1668
+ "x-session-affinity": createHash("sha256").update(JSON.stringify([agentName, context.event.sessionId])).digest("hex")
1522
1669
  },
1523
1670
  providerOptions: {
1524
1671
  ...context.generation.providerOptions,
@@ -1533,7 +1680,7 @@ async function generateWithAISDK(options) {
1533
1680
  tools: context.tools,
1534
1681
  messages: context.messages,
1535
1682
  modelMessages: context.modelMessages,
1536
- responseMessageId: context.responseMessageId,
1683
+ responseMessageId: context.event.payload.responseMessageId,
1537
1684
  abortSignal: context.signal,
1538
1685
  ...context.instructions === void 0 ? {} : { instructions: context.instructions },
1539
1686
  settings: generation,
@@ -1552,9 +1699,13 @@ async function* consumeGeneration(options) {
1552
1699
  const pending = [];
1553
1700
  let streamedFinishReason;
1554
1701
  let streamedError;
1555
- const maxChunks = options.progress?.maxChunks ?? 16;
1556
- const maxDelayMs = options.progress?.maxDelayMs ?? 30;
1702
+ const maxChunks = options.progress?.maxChunks ?? 128;
1703
+ const maxDelayMs = options.progress?.maxDelayMs ?? 0;
1557
1704
  let lastFlush = Date.now();
1705
+ const batch = () => ({
1706
+ type: "progress",
1707
+ chunks: mergeDeltas(pending.splice(0))
1708
+ });
1558
1709
  const reader = options.source.stream.getReader();
1559
1710
  let pendingChunk = reader.read();
1560
1711
  try {
@@ -1564,10 +1715,7 @@ async function* consumeGeneration(options) {
1564
1715
  const remaining = Math.max(0, maxDelayMs - (Date.now() - lastFlush));
1565
1716
  const outcome = await nextOrTimer(pendingChunk, remaining);
1566
1717
  if (outcome.type === "timer") {
1567
- yield {
1568
- type: "progress",
1569
- chunks: pending.splice(0)
1570
- };
1718
+ yield batch();
1571
1719
  lastFlush = Date.now();
1572
1720
  continue;
1573
1721
  }
@@ -1580,18 +1728,12 @@ async function* consumeGeneration(options) {
1580
1728
  if (chunk.type === "error") streamedError = chunk.errorText;
1581
1729
  pendingChunk = reader.read();
1582
1730
  if (pending.length >= maxChunks || isBoundaryChunk(pending.at(-1))) {
1583
- yield {
1584
- type: "progress",
1585
- chunks: pending.splice(0)
1586
- };
1731
+ yield batch();
1587
1732
  lastFlush = Date.now();
1588
1733
  }
1589
1734
  }
1590
1735
  } catch (error) {
1591
- if (pending.length > 0) yield {
1592
- type: "progress",
1593
- chunks: pending.splice(0)
1594
- };
1736
+ if (pending.length > 0) yield batch();
1595
1737
  throw error;
1596
1738
  } finally {
1597
1739
  pendingChunk.catch(() => {});
@@ -1605,16 +1747,10 @@ async function* consumeGeneration(options) {
1605
1747
  try {
1606
1748
  completion = options.source.completion?.();
1607
1749
  } catch (error) {
1608
- if (pending.length > 0) yield {
1609
- type: "progress",
1610
- chunks: pending.splice(0)
1611
- };
1750
+ if (pending.length > 0) yield batch();
1612
1751
  throw error;
1613
1752
  }
1614
- if (pending.length > 0) yield {
1615
- type: "progress",
1616
- chunks: pending.splice(0)
1617
- };
1753
+ if (pending.length > 0) yield batch();
1618
1754
  if (streamedError !== void 0) throw new Error(streamedError);
1619
1755
  const finishReason = completion?.finishReason ?? streamedFinishReason;
1620
1756
  yield {
@@ -1665,8 +1801,7 @@ const summarize = async (options) => {
1665
1801
  modelMessages: [...options.context.modelMessages, {
1666
1802
  role: "user",
1667
1803
  content: instruction
1668
- }],
1669
- responseMessageId: await aiEventId("a2.ai.summary", options.context.generationId)
1804
+ }]
1670
1805
  },
1671
1806
  messageMetadata: void 0
1672
1807
  });
@@ -1893,12 +2028,8 @@ const validateCompaction = (options) => {
1893
2028
  }
1894
2029
  return compaction;
1895
2030
  };
1896
- /**
1897
- * Build the ordinary A2 handler table for the built-in agent protocol.
1898
- * Application handlers can be spread beside this table.
1899
- */
1900
2031
  function createHandlers(options) {
1901
- if (options.model === void 0) throw new TypeError("createHandlers requires model");
2032
+ if (options.model === void 0) throw new TypeError("agent requires model");
1902
2033
  if (options.progress?.maxChunks !== void 0 && (!Number.isInteger(options.progress.maxChunks) || options.progress.maxChunks < 1)) throw new TypeError("progress.maxChunks must be a positive integer");
1903
2034
  if (options.progress?.maxDelayMs !== void 0 && (!Number.isFinite(options.progress.maxDelayMs) || options.progress.maxDelayMs < 0)) throw new TypeError("progress.maxDelayMs must be a non-negative number");
1904
2035
  if (options.maxSteps !== void 0 && options.maxSteps !== Number.POSITIVE_INFINITY && (!Number.isInteger(options.maxSteps) || options.maxSteps < 1)) throw new TypeError("maxSteps must be a positive integer or Infinity");
@@ -1906,23 +2037,39 @@ function createHandlers(options) {
1906
2037
  const staticCompaction = typeof configuredCompaction === "function" ? void 0 : validateCompaction({
1907
2038
  compaction: configuredCompaction,
1908
2039
  explicit: options.compaction !== void 0,
1909
- generation: options.generation,
2040
+ generation: typeof options.generation === "function" ? void 0 : options.generation,
1910
2041
  tools: options.tools
1911
2042
  });
1912
2043
  const tools = options.tools ?? {};
1913
- const generation = options.generation ?? {};
2044
+ const telemetry = options.telemetry ?? CONSOLE_TELEMETRY;
1914
2045
  const maxSteps = options.maxSteps ?? Number.POSITIVE_INFINITY;
1915
- const control = createControlRuntime({ agent: options.agent });
2046
+ const control = createControlRuntime({
2047
+ contract: options.contract,
2048
+ telemetry
2049
+ });
2050
+ const controlHandler = (ctx) => telemetry.span("a2.ai.control", {
2051
+ "a2.contract": options.contract.name,
2052
+ "a2.session_id": ctx.event.sessionId,
2053
+ "a2.ai.control.type": ctx.event.type,
2054
+ "a2.ai.control.id": ctx.event.id,
2055
+ "a2.ai.control.attempt": ctx.attempt
2056
+ }, () => control.handler(ctx));
1916
2057
  const coordinator = control.coordinator;
1917
- const contextReducer = createContextReducer({ agent: options.agent });
2058
+ const contextReducer = createContextReducer({ contract: options.contract });
1918
2059
  const readContext = async (session, through) => {
1919
- const snapshot = await session.state(contextReducer, { through });
1920
- return {
1921
- state: contextReducer.view(snapshot.state),
1922
- index: snapshot.index
1923
- };
2060
+ return await session.state(contextReducer, { through });
2061
+ };
2062
+ const prompts = boundedCache({
2063
+ maxEntries: PROMPT_SESSIONS,
2064
+ maxBytes: PROMPT_SESSIONS
2065
+ });
2066
+ const cachePrompt = ({ sessionId, generationId, messages }) => prompts.set(sessionId, {
2067
+ generationId,
2068
+ messages
2069
+ }, 1);
2070
+ const forgetPrompt = (sessionId, generationId) => {
2071
+ if (prompts.get(sessionId)?.generationId === generationId) prompts.delete(sessionId);
1924
2072
  };
1925
- const promptCache = /* @__PURE__ */ new Map();
1926
2073
  const resultEvent = async (call, suffix, result) => ({
1927
2074
  type: "ai.tool.result",
1928
2075
  id: await aiEventId("a2.ai.tool.result", call.generationId, call.toolCallId, suffix),
@@ -1945,25 +2092,32 @@ function createHandlers(options) {
1945
2092
  });
1946
2093
  const promptMessages = (input) => {
1947
2094
  const { ctx, call, generation: owner } = input;
1948
- const key = promptCacheKey(ctx.event.sessionId, call.generationId);
1949
- const cached = promptCache.get(key);
1950
- if (cached) return cached;
2095
+ const sessionId = ctx.event.sessionId;
2096
+ const cached = prompts.get(sessionId);
2097
+ if (cached?.generationId === call.generationId) return cached.messages;
1951
2098
  const computation = (async () => {
1952
2099
  const frontier = owner.promptThroughIndex;
1953
2100
  if (frontier === void 0) throw new TypeError("AI work requires a prompt checkpoint");
1954
2101
  const snapshot = await readContext(ctx.session, ctx.event.index);
1955
2102
  if (snapshot.state.generation?.owner.generationId !== owner.generationId || snapshot.state.generation.owner.requestId !== owner.requestId || snapshot.state.activeProjection?.generationId !== owner.generationId) throw new TypeError("Tool prompt checkpoint does not own its generation");
1956
2103
  const state = generationBase(snapshot.state);
1957
- return modelContext({
2104
+ return telemetry.span("a2.ai.context", {
2105
+ "a2.contract": options.contract.name,
2106
+ "a2.session_id": ctx.event.sessionId,
2107
+ "a2.ai.context.request_id": call.requestId,
2108
+ "a2.ai.context.phase": "tool"
2109
+ }, () => modelContext({
1958
2110
  messages: state.messages.filter((message) => state.messageIndexes[message.id] <= frontier),
1959
2111
  state,
1960
2112
  tools
1961
- });
2113
+ }));
1962
2114
  })();
1963
- promptCache.set(key, computation);
1964
- computation.catch(() => {
1965
- if (promptCache.get(key) === computation) promptCache.delete(key);
2115
+ cachePrompt({
2116
+ sessionId,
2117
+ generationId: call.generationId,
2118
+ messages: computation
1966
2119
  });
2120
+ computation.catch(() => forgetPrompt(sessionId, call.generationId));
1967
2121
  return computation;
1968
2122
  };
1969
2123
  const toolExecutionFailure = async (ctx, call, error) => {
@@ -2032,7 +2186,7 @@ function createHandlers(options) {
2032
2186
  const messages = await promptMessages(input);
2033
2187
  if (checkAbort(ctx.signal)) return;
2034
2188
  const scope = {
2035
- contract: options.agent.contract,
2189
+ contract: options.contract,
2036
2190
  context: ctx
2037
2191
  };
2038
2192
  return toolScopeStorage.run(scope, () => runToolExecution(ctx, call, execute, messages));
@@ -2042,7 +2196,7 @@ function createHandlers(options) {
2042
2196
  const state = (await ctx.session.state(control.reducer, { through: "latest" })).state;
2043
2197
  const request = ctx.event.payload;
2044
2198
  const call = state.coordinator.response?.calls.find((candidate) => candidate.work?.id === ctx.event.id);
2045
- if (state.active?.turnId !== request.turnId || state.active.suspended || state.active.version !== request.version || state.coordinator.response?.failure !== void 0 || !call || call.terminal || call.work?.settled) return control.settled({
2199
+ if (state.active?.turnId !== request.turnId || Object.values(state.pendingStops).includes(request.turnId) || state.active.suspended || state.active.version !== request.version || state.coordinator.response?.failure !== void 0 || !call || call.terminal || call.work?.settled) return control.settled({
2046
2200
  ctx,
2047
2201
  events: void 0
2048
2202
  });
@@ -2071,7 +2225,9 @@ function createHandlers(options) {
2071
2225
  const request = ctx.event.payload;
2072
2226
  const snapshot = await readContext(ctx.session, "latest");
2073
2227
  if (snapshot.state.closed || snapshot.state.authority?.requestId !== requestId || snapshot.state.authority?.phase === "paused" || snapshot.state.authority?.phase === "pausing") return;
2074
- const coordinatorState = (await ctx.session.state(coordinator, { through: snapshot.index })).state;
2228
+ const controlState = (await ctx.session.state(control.reducer, { through: snapshot.index })).state;
2229
+ if (request.control !== void 0 && Object.values(controlState.pendingStops).includes(request.control.turnId)) return;
2230
+ const coordinatorState = controlState.coordinator;
2075
2231
  if (coordinatorState.closed || coordinatorState.response?.activeRequestId !== requestId) return;
2076
2232
  const response = coordinatorState.response;
2077
2233
  const current = response.generation?.requestId === requestId ? response.generation : void 0;
@@ -2090,109 +2246,124 @@ function createHandlers(options) {
2090
2246
  state,
2091
2247
  coordinator: coordinatorState
2092
2248
  }),
2249
+ state: snapshot.state.app ?? {},
2093
2250
  session: { state: (reducer, readOptions) => ctx.session.state(reducer, {
2094
2251
  ...readOptions,
2095
2252
  through: readOptions?.through ?? snapshot.index
2096
2253
  }) },
2097
2254
  signal: ctx.signal
2098
2255
  };
2099
- let generationStarted = false;
2100
- try {
2101
- const resolvedModel = await resolve(options.model, resolverContext);
2102
- if (resolvedModel === void 0) throw new TypeError("the model resolver returned undefined");
2103
- if (checkAbort(ctx.signal)) return;
2104
- const resolvedInstructions = options.instructions === void 0 ? void 0 : await resolve(options.instructions, resolverContext);
2105
- if (checkAbort(ctx.signal)) return;
2106
- const compaction = typeof configuredCompaction === "function" ? validateCompaction({
2107
- compaction: await resolve(configuredCompaction, resolverContext),
2108
- explicit: true,
2109
- generation: options.generation,
2110
- tools: options.tools
2111
- }) : staticCompaction;
2112
- if (checkAbort(ctx.signal)) return;
2113
- if (request.reason === "tool" && responseStepCount >= maxSteps) {
2114
- const source = response.source?.generation;
2115
- if (source === void 0) return;
2116
- return {
2117
- type: "ai.generation.failed",
2118
- id: await aiEventId("a2.ai.step-limit", requestId),
2119
- payload: {
2120
- requestId: source.requestId,
2121
- messageId: source.messageId,
2122
- generationId: source.generationId,
2123
- responseMessageId,
2124
- error: `agent exceeded the ${maxSteps}-step limit`,
2125
- stepLimit: true
2126
- }
2127
- };
2128
- }
2129
- const started = {
2130
- requestId,
2131
- messageId: request.messageId,
2132
- generationId,
2133
- responseMessageId,
2134
- attempt,
2135
- model: modelName(resolvedModel),
2136
- promptThroughIndex: snapshot.index
2256
+ if (request.reason === "tool" && responseStepCount >= maxSteps) {
2257
+ const source = response.source?.generation;
2258
+ if (source === void 0) return;
2259
+ return {
2260
+ type: "ai.generation.failed",
2261
+ id: await aiEventId("a2.ai.step-limit", requestId),
2262
+ payload: {
2263
+ requestId: source.requestId,
2264
+ messageId: source.messageId,
2265
+ generationId: source.generationId,
2266
+ responseMessageId,
2267
+ error: `agent exceeded the ${maxSteps}-step limit`,
2268
+ stepLimit: true
2269
+ }
2137
2270
  };
2138
- const catalogModelId = gatewayModelId(resolvedModel);
2139
- const gatewayOptions = options.generation?.providerOptions?.["gateway"];
2140
- const usesFallbackModels = Array.isArray(gatewayOptions?.["models"]) && gatewayOptions["models"].length > 0;
2141
- const discoversMetadata = compaction !== false && !("shouldCompact" in compaction) && compaction.thresholdTokens === void 0 && !usesFallbackModels && catalogModelId !== void 0;
2142
- const startEvents = [];
2143
- if (discoversMetadata && !Object.hasOwn(state.modelMetadata, catalogModelId)) startEvents.push({
2271
+ }
2272
+ const startEvents = async (started, metadataModelId) => {
2273
+ const events = [];
2274
+ if (metadataModelId !== void 0) events.push({
2144
2275
  type: "ai.model.metadata.requested",
2145
- id: await aiEventId("a2.ai.model.metadata", options.agent.contract.name, ctx.event.sessionId, catalogModelId),
2146
- payload: { modelId: catalogModelId }
2276
+ id: await aiEventId("a2.ai.model.metadata", options.contract.name, ctx.event.sessionId, metadataModelId),
2277
+ payload: { modelId: metadataModelId }
2147
2278
  });
2148
2279
  if (previous) {
2149
- const payload = previous;
2150
2280
  const superseded = {
2151
2281
  requestId,
2152
- messageId: payload.messageId,
2153
- generationId: payload.generationId,
2154
- responseMessageId: payload.responseMessageId,
2282
+ messageId: previous.messageId,
2283
+ generationId: previous.generationId,
2284
+ responseMessageId: previous.responseMessageId,
2155
2285
  error: "generation attempt was superseded after an incomplete run",
2156
2286
  superseded: true
2157
2287
  };
2158
- startEvents.push({
2288
+ events.push({
2159
2289
  type: "ai.generation.failed",
2160
- id: await aiEventId("a2.ai.superseded", payload.generationId),
2290
+ id: await aiEventId("a2.ai.superseded", previous.generationId),
2161
2291
  payload: superseded
2162
2292
  });
2163
2293
  }
2164
- startEvents.push({
2294
+ events.push({
2165
2295
  type: "ai.generation.started",
2166
2296
  id: generationId,
2167
2297
  payload: started
2168
2298
  });
2299
+ return events;
2300
+ };
2301
+ const startedPayload = (model) => ({
2302
+ requestId,
2303
+ messageId: request.messageId,
2304
+ generationId,
2305
+ responseMessageId,
2306
+ attempt,
2307
+ ...model === void 0 ? {} : { model: modelName(model) },
2308
+ promptThroughIndex: snapshot.index
2309
+ });
2310
+ let resolvedModel;
2311
+ let resolving = true;
2312
+ let generationStarted = false;
2313
+ try {
2314
+ const choice = await resolve(options.model, resolverContext);
2315
+ if (choice === void 0) throw new TypeError("the model resolver returned undefined");
2316
+ const settings = typeof choice === "string" || "specificationVersion" in choice ? { model: choice } : choice;
2317
+ const model = settings.model;
2318
+ if (typeof model !== "string" && (typeof model !== "object" || model === null)) throw new TypeError("the model resolver must return a model");
2319
+ resolvedModel = model;
2320
+ if (checkAbort(ctx.signal)) return;
2321
+ const resolvedInstructions = options.instructions === void 0 ? void 0 : await resolve(options.instructions, resolverContext);
2322
+ if (checkAbort(ctx.signal)) return;
2323
+ const resolvedGeneration = await resolve(options.generation ?? {}, resolverContext);
2324
+ if (checkAbort(ctx.signal)) return;
2325
+ if (resolvedGeneration === null || typeof resolvedGeneration !== "object" || Array.isArray(resolvedGeneration)) throw new TypeError("the generation resolver must return settings");
2326
+ const generation = withModelSettings({
2327
+ generation: resolvedGeneration,
2328
+ settings
2329
+ });
2330
+ const compaction = typeof configuredCompaction === "function" || typeof options.generation === "function" ? validateCompaction({
2331
+ compaction: typeof configuredCompaction === "function" ? await resolve(configuredCompaction, resolverContext) : staticCompaction,
2332
+ explicit: options.compaction !== void 0,
2333
+ generation,
2334
+ tools: options.tools
2335
+ }) : staticCompaction;
2336
+ if (checkAbort(ctx.signal)) return;
2337
+ resolving = false;
2338
+ const catalogModelId = gatewayModelId(model);
2339
+ const gatewayOptions = generation.providerOptions?.["gateway"];
2340
+ const usesFallbackModels = Array.isArray(gatewayOptions?.["models"]) && gatewayOptions["models"].length > 0;
2341
+ const discoversMetadata = compaction !== false && !("shouldCompact" in compaction) && compaction.thresholdTokens === void 0 && !usesFallbackModels && catalogModelId !== void 0;
2169
2342
  const generationEvents = await control.append({
2170
2343
  ctx,
2171
2344
  name: "generation-start",
2172
- events: startEvents
2345
+ events: await startEvents(startedPayload(model), discoversMetadata && !Object.hasOwn(state.modelMetadata, catalogModelId) ? catalogModelId : void 0)
2173
2346
  });
2347
+ const startIndex = generationEvents.find((event) => event.id === generationId).index;
2174
2348
  generationStarted = true;
2175
2349
  if (checkAbort(ctx.signal)) return;
2176
2350
  const promptCoordinatorState = coordinatorState;
2177
- const messages = contextMessages({
2178
- state,
2179
- coordinator: promptCoordinatorState
2180
- });
2351
+ const { messages } = resolverContext;
2181
2352
  let generationMessages = messages;
2182
- let modelMessages = await modelContext({
2353
+ let modelMessages = await telemetry.span("a2.ai.context", {
2354
+ "a2.contract": options.contract.name,
2355
+ "a2.session_id": ctx.event.sessionId,
2356
+ "a2.ai.context.request_id": requestId,
2357
+ "a2.ai.context.phase": "generation"
2358
+ }, () => modelContext({
2183
2359
  messages,
2184
2360
  state,
2185
2361
  tools
2186
- });
2362
+ }));
2187
2363
  const baseContext = {
2188
- request: ctx.event,
2189
- requestId,
2364
+ ...resolverContext,
2190
2365
  generationId,
2191
- responseMessageId,
2192
- messages,
2193
2366
  modelMessages,
2194
- session: resolverContext.session,
2195
- signal: ctx.signal,
2196
2367
  model: resolvedModel,
2197
2368
  tools,
2198
2369
  ...resolvedInstructions === void 0 ? {} : { instructions: resolvedInstructions },
@@ -2257,14 +2428,20 @@ function createHandlers(options) {
2257
2428
  }
2258
2429
  }]
2259
2430
  }));
2260
- const result = !("shouldCompact" in policy) ? {
2431
+ const result = await telemetry.span("a2.ai.compaction", {
2432
+ "a2.contract": options.contract.name,
2433
+ "a2.session_id": ctx.event.sessionId,
2434
+ "a2.ai.compaction.request_id": requestId,
2435
+ "a2.ai.compaction.generation_id": generationId,
2436
+ "a2.ai.compaction.policy": "shouldCompact" in policy ? "custom" : "automatic"
2437
+ }, async () => !("shouldCompact" in policy) ? {
2261
2438
  messages: [],
2262
2439
  ...await summarize({
2263
- agentName: options.agent.contract.name,
2440
+ agentName: options.contract.name,
2264
2441
  context: baseContext,
2265
2442
  instructions: policy.instructions
2266
2443
  })
2267
- } : { messages: await policy.compact(compactionContext) };
2444
+ } : { messages: await policy.compact(compactionContext) });
2268
2445
  if (checkAbort(ctx.signal)) return;
2269
2446
  const completed = {
2270
2447
  generationId,
@@ -2288,7 +2465,7 @@ function createHandlers(options) {
2288
2465
  }
2289
2466
  }
2290
2467
  const currentState = foldAIEvents({
2291
- reducer: contextReducer[aiStateReducer],
2468
+ reducer: contextReducer,
2292
2469
  events: generationEvents,
2293
2470
  state
2294
2471
  });
@@ -2296,12 +2473,21 @@ function createHandlers(options) {
2296
2473
  state: currentState,
2297
2474
  coordinator: promptCoordinatorState
2298
2475
  });
2299
- if (policy && "shouldCompact" in policy || generationMessages !== messages || activeCompaction(currentState)?.summary !== activeCompaction(state)?.summary) modelMessages = await modelContext({
2476
+ if (policy && "shouldCompact" in policy || generationMessages !== messages || activeCompaction(currentState)?.summary !== activeCompaction(state)?.summary) modelMessages = await telemetry.span("a2.ai.context", {
2477
+ "a2.contract": options.contract.name,
2478
+ "a2.session_id": ctx.event.sessionId,
2479
+ "a2.ai.context.request_id": requestId,
2480
+ "a2.ai.context.phase": "refresh"
2481
+ }, () => modelContext({
2300
2482
  messages: generationMessages,
2301
2483
  state: currentState,
2302
2484
  tools
2485
+ }));
2486
+ cachePrompt({
2487
+ sessionId: ctx.event.sessionId,
2488
+ generationId,
2489
+ messages: Promise.resolve(modelMessages)
2303
2490
  });
2304
- promptCache.set(promptCacheKey(ctx.event.sessionId, generationId), Promise.resolve(modelMessages));
2305
2491
  if (tracksInput && (inputTokenEstimate === void 0 || compacted)) inputTokenEstimate = await estimateInputTokens({
2306
2492
  messages: modelMessages,
2307
2493
  instructions: resolvedInstructions,
@@ -2310,23 +2496,14 @@ function createHandlers(options) {
2310
2496
  let pendingToolCalls = [];
2311
2497
  if (checkAbort(ctx.signal)) return;
2312
2498
  const generateContext = {
2313
- request: ctx.event,
2314
- requestId,
2315
- generationId,
2316
- responseMessageId,
2499
+ ...baseContext,
2317
2500
  messages: generationMessages,
2318
- modelMessages,
2319
- session: resolverContext.session,
2320
- signal: ctx.signal,
2321
- model: resolvedModel,
2322
- tools,
2323
- ...resolvedInstructions === void 0 ? {} : { instructions: resolvedInstructions },
2324
- generation: options.generation ?? {}
2501
+ modelMessages
2325
2502
  };
2326
2503
  const custom = options.generate !== void 0;
2327
2504
  const updates = consumeGeneration({
2328
2505
  source: options.generate === void 0 ? await generateWithAISDK({
2329
- agentName: options.agent.contract.name,
2506
+ agentName: options.contract.name,
2330
2507
  context: generateContext,
2331
2508
  messageMetadata: options.messageMetadata
2332
2509
  }) : { stream: await options.generate(generateContext) },
@@ -2341,11 +2518,7 @@ function createHandlers(options) {
2341
2518
  continue;
2342
2519
  }
2343
2520
  const progress = {
2344
- requestId,
2345
- messageId: request.messageId,
2346
- generationId,
2347
- responseMessageId,
2348
- sequence,
2521
+ startIndex,
2349
2522
  chunks: update.chunks
2350
2523
  };
2351
2524
  const lifecycle = await lifecycleEvents({
@@ -2390,6 +2563,7 @@ function createHandlers(options) {
2390
2563
  if (unresolved.length > 0) throw new Error(`generation ended with unresolved tool authorization for '${unresolved[0].call.toolName}'`);
2391
2564
  const pendingEvents = await Promise.all(pendingToolCalls.filter((pending) => pending.classification === "automatic" || options.generate === void 0 && pending.classification === "unknown").map((pending) => toolCalledEvent(pending.call)));
2392
2565
  if (finish.finishReason === "tool-calls") return [...pendingEvents, completionEvent];
2566
+ forgetPrompt(ctx.event.sessionId, generationId);
2393
2567
  return [
2394
2568
  ...pendingEvents,
2395
2569
  completionEvent,
@@ -2406,7 +2580,13 @@ function createHandlers(options) {
2406
2580
  throw error;
2407
2581
  }
2408
2582
  if (checkAbort(ctx.signal)) return;
2409
- if (!generationStarted) throw error;
2583
+ if (!generationStarted && !resolving) throw error;
2584
+ if (!generationStarted) await control.append({
2585
+ ctx,
2586
+ name: "generation-start",
2587
+ events: await startEvents(startedPayload(resolvedModel))
2588
+ });
2589
+ forgetPrompt(ctx.event.sessionId, generationId);
2410
2590
  const failed = {
2411
2591
  requestId,
2412
2592
  messageId: request.messageId,
@@ -2421,10 +2601,6 @@ function createHandlers(options) {
2421
2601
  };
2422
2602
  }
2423
2603
  };
2424
- const clearPromptCache = (sessionId) => {
2425
- const prefix = `${sessionId}\u001f`;
2426
- for (const key of promptCache.keys()) if (key.startsWith(prefix)) promptCache.delete(key);
2427
- };
2428
2604
  return {
2429
2605
  "ai.model.metadata.requested": { handler: async (ctx) => {
2430
2606
  const limits = await readModelLimits({
@@ -2449,39 +2625,32 @@ function createHandlers(options) {
2449
2625
  if ((command.action === "send" || command.action === "steer") && event.id !== command.message.id) throw new A2Error("INVALID_PAYLOAD", "AI input event ID must equal message.id");
2450
2626
  return "a2.ai.control";
2451
2627
  },
2452
- handler: control.handler
2628
+ handler: controlHandler
2453
2629
  },
2454
2630
  "ai.work.reported": {
2455
2631
  lane: "a2.ai.control",
2456
- handler: control.handler
2632
+ handler: controlHandler
2457
2633
  },
2458
- "ai.message.completed": { handler: async (ctx) => {
2459
- clearPromptCache(ctx.event.sessionId);
2460
- } },
2461
- "ai.message.interrupted": { handler: async (ctx) => {
2462
- clearPromptCache(ctx.event.sessionId);
2463
- } },
2464
2634
  "ai.session.closed": {
2465
2635
  lane: "a2.ai.control",
2466
- handler: async (ctx) => {
2467
- clearPromptCache(ctx.event.sessionId);
2468
- return control.handler(ctx);
2469
- }
2636
+ handler: controlHandler
2470
2637
  },
2471
- "ai.generation.failed": { handler: (ctx) => {
2472
- promptCache.delete(promptCacheKey(ctx.event.sessionId, ctx.event.payload.generationId));
2473
- return Promise.resolve();
2474
- } },
2475
2638
  "ai.generation.requested": {
2476
2639
  lane: "a2.ai.model",
2477
2640
  abortOn: {
2641
+ "ai.control.requested": (event, trigger) => event.payload.action === "stop" && event.payload.turnId === trigger.payload.control?.turnId,
2478
2642
  "ai.control.committed": (event, trigger) => {
2479
2643
  const active = event.payload.view;
2480
2644
  return active?.turnId !== trigger.payload.control?.turnId || active?.version !== trigger.payload.control?.version;
2481
2645
  },
2482
2646
  "ai.session.closed": true
2483
2647
  },
2484
- handler: async (ctx) => {
2648
+ handler: (ctx) => telemetry.span("a2.ai.generate", {
2649
+ "a2.contract": options.contract.name,
2650
+ "a2.session_id": ctx.event.sessionId,
2651
+ "a2.ai.generate.request_id": ctx.event.id,
2652
+ "a2.ai.generate.attempt": ctx.attempt
2653
+ }, async () => {
2485
2654
  try {
2486
2655
  return control.settled({
2487
2656
  ctx,
@@ -2497,10 +2666,12 @@ function createHandlers(options) {
2497
2666
  });
2498
2667
  throw error;
2499
2668
  }
2500
- }
2669
+ })
2501
2670
  },
2502
2671
  "ai.tool.execution.requested": {
2672
+ ...options.toolLane === void 0 ? {} : { lane: options.toolLane },
2503
2673
  abortOn: {
2674
+ "ai.control.requested": (event, trigger) => event.payload.action === "stop" && event.payload.turnId === trigger.payload.turnId,
2504
2675
  "ai.generation.failed": (event, trigger) => event.payload.generationId === trigger.payload.call.generationId,
2505
2676
  "ai.control.committed": (event, trigger) => {
2506
2677
  const active = event.payload.view;
@@ -2508,16 +2679,40 @@ function createHandlers(options) {
2508
2679
  },
2509
2680
  "ai.session.closed": true
2510
2681
  },
2511
- handler: handleToolExecution
2682
+ handler: (ctx) => telemetry.span("a2.ai.tool", {
2683
+ "a2.contract": options.contract.name,
2684
+ "a2.session_id": ctx.event.sessionId,
2685
+ "a2.ai.tool.request_id": ctx.event.payload.call.requestId,
2686
+ "a2.ai.tool.call_id": ctx.event.payload.call.toolCallId,
2687
+ "a2.ai.tool.name": ctx.event.payload.call.toolName,
2688
+ "a2.ai.tool.attempt": ctx.attempt
2689
+ }, () => handleToolExecution(ctx))
2512
2690
  }
2513
2691
  };
2514
2692
  }
2515
- /** Assemble an A2 server with the built-in agent handlers and app extensions. */
2516
- function createAgentServer(options) {
2517
- const { agent: definition, model, tools, instructions, generation, maxSteps, messageMetadata, generate, compaction, progress, handlers, ...serverOptions } = options;
2693
+ function agent(options) {
2694
+ const { model, tools, instructions, generation, maxSteps, messageMetadata, generate, compaction, progress, toolLane, handlers, ...rest } = options;
2695
+ let contract;
2696
+ let serverOptions;
2697
+ if ("contract" in rest) {
2698
+ const { contract: shared, ...remaining } = rest;
2699
+ if ("name" in remaining) throw new TypeError("agent takes either name or contract, not both");
2700
+ if (!isAgentContract(shared)) throw new TypeError("agent({ contract }) takes an agentContract()");
2701
+ contract = shared;
2702
+ serverOptions = remaining;
2703
+ } else {
2704
+ const { name, events, messageSchema, ...remaining } = rest;
2705
+ contract = agentContract({
2706
+ name,
2707
+ ...events === void 0 ? {} : { events },
2708
+ ...messageSchema === void 0 ? {} : { messageSchema }
2709
+ });
2710
+ serverOptions = remaining;
2711
+ }
2518
2712
  const builtIns = createHandlers({
2519
- agent: definition,
2713
+ contract,
2520
2714
  model,
2715
+ ...serverOptions.telemetry === void 0 ? {} : { telemetry: serverOptions.telemetry },
2521
2716
  ...tools === void 0 ? {} : { tools },
2522
2717
  ...instructions === void 0 ? {} : { instructions },
2523
2718
  ...generation === void 0 ? {} : { generation },
@@ -2525,20 +2720,26 @@ function createAgentServer(options) {
2525
2720
  ...messageMetadata === void 0 ? {} : { messageMetadata },
2526
2721
  ...generate === void 0 ? {} : { generate },
2527
2722
  ...compaction === void 0 ? {} : { compaction },
2528
- ...progress === void 0 ? {} : { progress }
2723
+ ...progress === void 0 ? {} : { progress },
2724
+ ...toolLane === void 0 ? {} : { toolLane }
2529
2725
  });
2726
+ for (const type of Object.keys(handlers ?? {})) if (Object.hasOwn(builtIns, type)) throw new TypeError(`agent handles '${type}' itself`);
2530
2727
  const server = createServer({
2531
2728
  ...serverOptions,
2532
- contract: definition.contract,
2729
+ contract,
2533
2730
  handlers: {
2534
2731
  ...builtIns,
2535
2732
  ...handlers
2536
2733
  }
2537
2734
  });
2538
- setServerFetchHooks(server, { validateIngress: validateAgentIngress });
2735
+ const applicationEvents = "contract" in rest;
2736
+ setServerFetchHooks(server, { validateIngress: (context) => validateAgentIngress({
2737
+ context,
2738
+ applicationEvents
2739
+ }) });
2539
2740
  return server;
2540
2741
  }
2541
2742
  //#endregion
2542
- export { createAgentServer, createHandlers, runWithHandlerContext };
2743
+ export { agent, runWithHandlerContext };
2543
2744
 
2544
2745
  //# sourceMappingURL=ai-server.js.map