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
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 interruptUIMessage, S as progressBatches, _ as projectedMessage, a as events, b as upsertResponse, d as AI_EVENT_ID_PREFIX, f as aiEventId, g as advanceProjection, h as controlCommit, l as aiStateReducer, m as applyControlChanges, p as initialControlState, u as storedAIReducer, v as reduceToolActivity, w as pauseUIMessage, x as flattenProgressBatches, y as upsertMessage } from "./ai-DgOBltJ_.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,7 +239,9 @@ 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
+ const telemetry = options.telemetry ?? CONSOLE_TELEMETRY;
243
+ const reducer$2 = reducer({
244
+ contract: options.contract,
242
245
  name: "a2.ai.control.v5",
243
246
  initialState: initialControlState()
244
247
  }).fold((state, event) => {
@@ -268,9 +271,9 @@ const createControlRuntime = (options) => {
268
271
  changes: event.payload.changes
269
272
  }) : state;
270
273
  });
271
- const coordinator = aiCoordinatorReducer(options.agent.contract);
274
+ const coordinator = aiCoordinatorReducer(options.contract);
272
275
  const handler = async (ctx) => {
273
- const snapshot = await ctx.session.state(reducer, { through: "latest" });
276
+ const snapshot = await ctx.session.state(reducer$2, { through: "latest" });
274
277
  const before = structuredClone(snapshot.state);
275
278
  const moves = [];
276
279
  const state = structuredClone(snapshot.state);
@@ -502,7 +505,7 @@ const createControlRuntime = (options) => {
502
505
  responseMessageId: response.responseMessageId,
503
506
  reason: "retry"
504
507
  },
505
- 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)
506
509
  });
507
510
  } else reason = "stale-turn";
508
511
  break;
@@ -634,7 +637,7 @@ const createControlRuntime = (options) => {
634
637
  message: item.message,
635
638
  generate: item.generate
636
639
  },
637
- 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)
638
641
  });
639
642
  if (!item.generate) continue;
640
643
  state.active = {
@@ -645,7 +648,7 @@ const createControlRuntime = (options) => {
645
648
  suspended: false,
646
649
  model: null
647
650
  };
648
- 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);
649
652
  requestModel({
650
653
  request: {
651
654
  messageId: item.id,
@@ -685,7 +688,7 @@ const createControlRuntime = (options) => {
685
688
  const payload = ctx.event.payload;
686
689
  const { turnId, version } = "turnId" in payload ? payload : payload.control;
687
690
  for (const event of events) {
688
- const schema = options.agent.contract.events[event.type];
691
+ const schema = options.contract.events[event.type];
689
692
  const result = validateSync(schema, event.payload, `AI worker ${event.type}`);
690
693
  if (result.issues) throw new TypeError(result.issues.map((issue) => issue.message).join("; "));
691
694
  }
@@ -719,32 +722,39 @@ const createControlRuntime = (options) => {
719
722
  });
720
723
  const persisted = await ctx.session.append(name, ...progress, input);
721
724
  const index = persisted.at(-1).index;
722
- const stream = ctx.session.stream({ startAfter: index })[Symbol.asyncIterator]();
723
- const canonical = [];
724
- const wanted = new Set(lifecycle.map((event) => event.id));
725
- let rejectAbort;
726
- const aborted = new Promise((_, reject) => {
727
- rejectAbort = reject;
728
- });
729
- const onAbort = () => {
730
- rejectAbort(ctx.signal.reason);
731
- };
732
- ctx.signal.addEventListener("abort", onAbort, { once: true });
733
- try {
734
- ctx.signal.throwIfAborted();
735
- for (;;) {
736
- const item = await Promise.race([stream.next(), aborted]);
737
- if (item.done) throw new Error("AI control acknowledgement stream ended");
738
- if (wanted.has(item.value.id)) canonical.push(item.value);
739
- if (item.value.type === "ai.control.decided" && item.value.payload.commandId === input.id) {
740
- if (item.value.payload.outcome === "rejected") throw new ControlCancelled("stale AI work");
741
- 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
+ }
742
752
  }
753
+ } finally {
754
+ ctx.signal.removeEventListener("abort", onAbort);
755
+ await stream.return?.();
743
756
  }
744
- } finally {
745
- ctx.signal.removeEventListener("abort", onAbort);
746
- await stream.return?.();
747
- }
757
+ });
748
758
  };
749
759
  const settled = ({ ctx, events }) => report({
750
760
  ctx,
@@ -761,7 +771,7 @@ const createControlRuntime = (options) => {
761
771
  append,
762
772
  settled,
763
773
  cancelled,
764
- reducer,
774
+ reducer: reducer$2,
765
775
  coordinator
766
776
  };
767
777
  };
@@ -802,19 +812,29 @@ const progress = (value) => {
802
812
  return count === value["length"];
803
813
  };
804
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
+ };
805
822
  const projection = (value, messages) => {
806
823
  if (!record(value) || Object.keys(value).some((key) => ![
807
824
  "generationId",
808
825
  "responseMessageId",
809
826
  "promptThroughIndex",
810
827
  "completionIndex",
811
- "baseMessage",
828
+ "base",
812
829
  "baseCompaction",
813
830
  "batches",
814
831
  "toolEvents",
815
832
  "approvalEvents",
816
833
  "cursor"
817
- ].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;
818
838
  const cursor = value["cursor"];
819
839
  if (cursor === void 0 || cursor === null) return true;
820
840
  if (!record(cursor) || !index(cursor["throughIndex"]) || !record(cursor["stream"])) return false;
@@ -848,7 +868,7 @@ const authority = (value) => value === null || record(value) && Object.keys(valu
848
868
  "pausing",
849
869
  "paused"
850
870
  ].includes(String(value["phase"])));
851
- const contextSchema = () => ({ "~standard": {
871
+ const contextSchema = (app) => ({ "~standard": {
852
872
  version: 1,
853
873
  vendor: "a2-context",
854
874
  validate(value) {
@@ -861,7 +881,8 @@ const contextSchema = () => ({ "~standard": {
861
881
  "authority",
862
882
  "generation",
863
883
  "activeProjection",
864
- "messageIndexes"
884
+ "messageIndexes",
885
+ ...app === void 0 ? [] : ["app"]
865
886
  ];
866
887
  try {
867
888
  if (!record(value) || keys.some((key) => !Object.hasOwn(value, key)) || Object.keys(value).some((key) => !keys.includes(key))) return invalid;
@@ -873,7 +894,7 @@ const contextSchema = () => ({ "~standard": {
873
894
  modelId,
874
895
  limits: entry["limits"]
875
896
  }))) || !indexes(value["messageIndexes"], messages)) return invalid;
876
- 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"]) || ![
877
898
  "streaming",
878
899
  "completed",
879
900
  "paused",
@@ -884,6 +905,7 @@ const contextSchema = () => ({ "~standard": {
884
905
  if (current !== null && (!projection(current, messages) || !record(current) || current["generationId"] !== owner?.["generationId"] || current["responseMessageId"] !== owner?.["responseMessageId"])) return invalid;
885
906
  if (generation !== null && generation["phase"] !== "superseded" && current === null) return invalid;
886
907
  if (value["closed"] && (generation !== null || current !== null)) return invalid;
908
+ if (app !== void 0 && !record(value["app"])) return invalid;
887
909
  return { value };
888
910
  } catch {
889
911
  return invalid;
@@ -911,14 +933,24 @@ const replaceResponse = ({ state, message, index }) => {
911
933
  }
912
934
  };
913
935
  };
914
- 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)]);
915
940
  const renderProjection = ({ state, index }) => {
916
941
  const projection = state.activeProjection;
917
- let message = projectedMessage(projection);
918
- 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;
919
948
  if (state.generation?.phase === "paused") message = pauseUIMessage({
920
949
  message,
921
- toolCallIds: calledTools(projection)
950
+ toolCallIds: calledTools({
951
+ projection,
952
+ current
953
+ })
922
954
  });
923
955
  if (state.generation?.phase === "failed") message = interruptUIMessage(message, `Generation failed before tool completion: ${state.generation.error}`);
924
956
  return replaceResponse({
@@ -936,9 +968,13 @@ const renderProjection = ({ state, index }) => {
936
968
  const generationBase = (state) => {
937
969
  const projection = state.activeProjection;
938
970
  if (!projection) return state;
971
+ const base = restoreMessageBase({
972
+ current: findMessage(state.messages, projection.responseMessageId),
973
+ base: projection.base
974
+ });
939
975
  return {
940
976
  ...state,
941
- 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),
942
978
  compaction: projection.baseCompaction ?? null
943
979
  };
944
980
  };
@@ -957,22 +993,37 @@ const endGeneration = (state) => ({
957
993
  activeProjection: null
958
994
  });
959
995
  const ownsGeneration = (state, owner) => state.generation?.owner.generationId === owner.generationId && state.generation.owner.requestId === owner.requestId && state.generation.phase !== "superseded";
960
- const createContextReducer = ({ agent }) => {
961
- const reducer = agent.contract.reducer({
962
- name: "a2.ai.context.v3",
963
- initialState: {
964
- messages: [],
965
- compaction: null,
966
- modelMetadata: {},
967
- closed: false,
968
- authority: null,
969
- generation: null,
970
- activeProjection: null,
971
- messageIndexes: {}
972
- },
973
- stateSchema: contextSchema()
974
- }).fold((state, event) => reduceContext(state, event));
975
- 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);
976
1027
  };
977
1028
  const reduceContext = (state, event) => {
978
1029
  if (state.closed) return state;
@@ -1029,6 +1080,7 @@ const reduceContext = (state, event) => {
1029
1080
  ...base,
1030
1081
  generation: {
1031
1082
  owner,
1083
+ startIndex: event.index,
1032
1084
  phase: "streaming",
1033
1085
  ...previous?.owner.responseMessageId === owner.responseMessageId && previous.responseIndex !== void 0 ? { responseIndex: previous.responseIndex } : {}
1034
1086
  },
@@ -1036,7 +1088,7 @@ const reduceContext = (state, event) => {
1036
1088
  generationId: owner.generationId,
1037
1089
  responseMessageId: owner.responseMessageId,
1038
1090
  promptThroughIndex: owner.promptThroughIndex ?? event.index - 1,
1039
- ...baseMessage === void 0 ? {} : { baseMessage },
1091
+ ...baseMessage === void 0 ? {} : { base: { partCount: baseMessage.parts.length } },
1040
1092
  baseCompaction: base.compaction,
1041
1093
  batches: progressBatches(),
1042
1094
  toolEvents: [],
@@ -1046,7 +1098,12 @@ const reduceContext = (state, event) => {
1046
1098
  }
1047
1099
  case "ai.generation.progress": {
1048
1100
  const payload = event.payload;
1049
- 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;
1050
1107
  const projection = state.activeProjection;
1051
1108
  const progress = advanceProjection({
1052
1109
  projection,
@@ -1125,9 +1182,12 @@ const reduceContext = (state, event) => {
1125
1182
  approvalEvents: projection.approvalEvents.filter((item) => item.index <= cutoff)
1126
1183
  };
1127
1184
  let next = generationBase(state);
1128
- if (retained.baseMessage !== void 0 || retained.batches.length > 0) next = replaceResponse({
1185
+ if (retained.base !== void 0 || retained.batches.length > 0) next = replaceResponse({
1129
1186
  state: next,
1130
- message: interruptUIMessage(projectedMessage(retained)),
1187
+ message: interruptUIMessage(projectedMessage({
1188
+ projection: retained,
1189
+ current: findMessage(state.messages, retained.responseMessageId)
1190
+ })),
1131
1191
  index: event.index
1132
1192
  });
1133
1193
  return endGeneration(next);
@@ -1139,7 +1199,10 @@ const reduceContext = (state, event) => {
1139
1199
  case "ai.generation.paused": {
1140
1200
  const { generationId } = event.payload;
1141
1201
  if (state.generation?.owner.generationId !== generationId || state.generation.phase === "failed" || state.generation.phase === "superseded" || !state.activeProjection) return state;
1142
- const toolCallIds = calledTools(state.activeProjection);
1202
+ const toolCallIds = calledTools({
1203
+ projection: state.activeProjection,
1204
+ current: findMessage(state.messages, state.activeProjection.responseMessageId)
1205
+ });
1143
1206
  return renderProjection({
1144
1207
  state: {
1145
1208
  ...state,
@@ -1242,9 +1305,9 @@ const reduceContext = (state, event) => {
1242
1305
  ...projection,
1243
1306
  cursor: null
1244
1307
  };
1245
- delete activeProjection.baseMessage;
1308
+ delete activeProjection.base;
1246
1309
  const baseMessage = messages.find((message) => message.id === projection.responseMessageId);
1247
- if (baseMessage !== void 0) activeProjection.baseMessage = baseMessage;
1310
+ if (baseMessage !== void 0) activeProjection.base = { partCount: baseMessage.parts.length };
1248
1311
  const compaction = {
1249
1312
  status: "completed",
1250
1313
  generationId: payload.generationId,
@@ -1467,23 +1530,24 @@ const measuredInputTokens = (options) => {
1467
1530
  return previous !== void 0 && previous.model === options.model && options.estimate >= previous.estimate ? Math.ceil(previous.inputTokens + options.estimate - previous.estimate) : options.estimate;
1468
1531
  };
1469
1532
  //#endregion
1470
- //#region src/ai-server.ts
1533
+ //#region src/ai-agent.ts
1471
1534
  /**
1472
- * experimental-a2/ai/server — the server-only implementation of an experimental-a2/ai definition.
1473
- *
1474
- * `createHandlers()` exposes the ordinary A2 handler table;
1475
- * `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.
1476
1537
  */
1477
- function validateAgentIngress(context) {
1478
- const rejected = context.events.find((event) => {
1479
- if (event.type !== "ai.control.requested") return true;
1480
- const command = event.payload;
1481
- if (command["action"] === "request-input" || command["action"] === "tool-result") return true;
1482
- if (command["action"] === "send" || command["action"] === "edit" || command["action"] === "steer") return command["message"]?.role !== "user";
1483
- return false;
1484
- });
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);
1485
1549
  if (rejected !== void 0) throw new A2Error("INVALID_PAYLOAD", `event '${rejected.type}' is server-only for an AI agent`);
1486
- }
1550
+ };
1487
1551
  const checkAbort = (signal) => {
1488
1552
  if (!signal.aborted) return false;
1489
1553
  const reason = signal.reason;
@@ -1501,6 +1565,22 @@ const resolve = async (value, context) => {
1501
1565
  checkAbort(context.signal);
1502
1566
  return result;
1503
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
+ };
1504
1584
  const modelName = (model) => {
1505
1585
  if (typeof model === "string") return model;
1506
1586
  if ("modelId" in model && typeof model.modelId === "string") return model.modelId;
@@ -1512,7 +1592,8 @@ const toolScopeStorage = installAmbientToolScopeStorage(() => new AsyncLocalStor
1512
1592
  function runWithHandlerContext(ctx, fn) {
1513
1593
  return toolScopeStorage.run({ context: ctx }, fn);
1514
1594
  }
1515
- 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;
1516
1597
  const asyncIterator = (value) => {
1517
1598
  if (typeof value !== "object" || value === null) return void 0;
1518
1599
  const factory = Reflect.get(value, Symbol.asyncIterator);
@@ -1520,6 +1601,49 @@ const asyncIterator = (value) => {
1520
1601
  return factory.call(value);
1521
1602
  };
1522
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
+ };
1523
1647
  const nextOrTimer = async (next, delayMs) => {
1524
1648
  let timer;
1525
1649
  try {
@@ -1541,7 +1665,7 @@ async function generateWithAISDK(options) {
1541
1665
  ...context.generation,
1542
1666
  headers: Object.keys(headers).some((key) => key.toLowerCase() === "x-session-affinity") ? headers : {
1543
1667
  ...headers,
1544
- "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")
1545
1669
  },
1546
1670
  providerOptions: {
1547
1671
  ...context.generation.providerOptions,
@@ -1556,7 +1680,7 @@ async function generateWithAISDK(options) {
1556
1680
  tools: context.tools,
1557
1681
  messages: context.messages,
1558
1682
  modelMessages: context.modelMessages,
1559
- responseMessageId: context.responseMessageId,
1683
+ responseMessageId: context.event.payload.responseMessageId,
1560
1684
  abortSignal: context.signal,
1561
1685
  ...context.instructions === void 0 ? {} : { instructions: context.instructions },
1562
1686
  settings: generation,
@@ -1575,9 +1699,13 @@ async function* consumeGeneration(options) {
1575
1699
  const pending = [];
1576
1700
  let streamedFinishReason;
1577
1701
  let streamedError;
1578
- const maxChunks = options.progress?.maxChunks ?? 16;
1579
- const maxDelayMs = options.progress?.maxDelayMs ?? 30;
1702
+ const maxChunks = options.progress?.maxChunks ?? 128;
1703
+ const maxDelayMs = options.progress?.maxDelayMs ?? 0;
1580
1704
  let lastFlush = Date.now();
1705
+ const batch = () => ({
1706
+ type: "progress",
1707
+ chunks: mergeDeltas(pending.splice(0))
1708
+ });
1581
1709
  const reader = options.source.stream.getReader();
1582
1710
  let pendingChunk = reader.read();
1583
1711
  try {
@@ -1587,10 +1715,7 @@ async function* consumeGeneration(options) {
1587
1715
  const remaining = Math.max(0, maxDelayMs - (Date.now() - lastFlush));
1588
1716
  const outcome = await nextOrTimer(pendingChunk, remaining);
1589
1717
  if (outcome.type === "timer") {
1590
- yield {
1591
- type: "progress",
1592
- chunks: pending.splice(0)
1593
- };
1718
+ yield batch();
1594
1719
  lastFlush = Date.now();
1595
1720
  continue;
1596
1721
  }
@@ -1603,18 +1728,12 @@ async function* consumeGeneration(options) {
1603
1728
  if (chunk.type === "error") streamedError = chunk.errorText;
1604
1729
  pendingChunk = reader.read();
1605
1730
  if (pending.length >= maxChunks || isBoundaryChunk(pending.at(-1))) {
1606
- yield {
1607
- type: "progress",
1608
- chunks: pending.splice(0)
1609
- };
1731
+ yield batch();
1610
1732
  lastFlush = Date.now();
1611
1733
  }
1612
1734
  }
1613
1735
  } catch (error) {
1614
- if (pending.length > 0) yield {
1615
- type: "progress",
1616
- chunks: pending.splice(0)
1617
- };
1736
+ if (pending.length > 0) yield batch();
1618
1737
  throw error;
1619
1738
  } finally {
1620
1739
  pendingChunk.catch(() => {});
@@ -1628,16 +1747,10 @@ async function* consumeGeneration(options) {
1628
1747
  try {
1629
1748
  completion = options.source.completion?.();
1630
1749
  } catch (error) {
1631
- if (pending.length > 0) yield {
1632
- type: "progress",
1633
- chunks: pending.splice(0)
1634
- };
1750
+ if (pending.length > 0) yield batch();
1635
1751
  throw error;
1636
1752
  }
1637
- if (pending.length > 0) yield {
1638
- type: "progress",
1639
- chunks: pending.splice(0)
1640
- };
1753
+ if (pending.length > 0) yield batch();
1641
1754
  if (streamedError !== void 0) throw new Error(streamedError);
1642
1755
  const finishReason = completion?.finishReason ?? streamedFinishReason;
1643
1756
  yield {
@@ -1688,8 +1801,7 @@ const summarize = async (options) => {
1688
1801
  modelMessages: [...options.context.modelMessages, {
1689
1802
  role: "user",
1690
1803
  content: instruction
1691
- }],
1692
- responseMessageId: await aiEventId("a2.ai.summary", options.context.generationId)
1804
+ }]
1693
1805
  },
1694
1806
  messageMetadata: void 0
1695
1807
  });
@@ -1916,12 +2028,8 @@ const validateCompaction = (options) => {
1916
2028
  }
1917
2029
  return compaction;
1918
2030
  };
1919
- /**
1920
- * Build the ordinary A2 handler table for the built-in agent protocol.
1921
- * Application handlers can be spread beside this table.
1922
- */
1923
2031
  function createHandlers(options) {
1924
- if (options.model === void 0) throw new TypeError("createHandlers requires model");
2032
+ if (options.model === void 0) throw new TypeError("agent requires model");
1925
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");
1926
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");
1927
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");
@@ -1929,23 +2037,39 @@ function createHandlers(options) {
1929
2037
  const staticCompaction = typeof configuredCompaction === "function" ? void 0 : validateCompaction({
1930
2038
  compaction: configuredCompaction,
1931
2039
  explicit: options.compaction !== void 0,
1932
- generation: options.generation,
2040
+ generation: typeof options.generation === "function" ? void 0 : options.generation,
1933
2041
  tools: options.tools
1934
2042
  });
1935
2043
  const tools = options.tools ?? {};
1936
- const generation = options.generation ?? {};
2044
+ const telemetry = options.telemetry ?? CONSOLE_TELEMETRY;
1937
2045
  const maxSteps = options.maxSteps ?? Number.POSITIVE_INFINITY;
1938
- 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));
1939
2057
  const coordinator = control.coordinator;
1940
- const contextReducer = createContextReducer({ agent: options.agent });
2058
+ const contextReducer = createContextReducer({ contract: options.contract });
1941
2059
  const readContext = async (session, through) => {
1942
- const snapshot = await session.state(contextReducer, { through });
1943
- return {
1944
- state: contextReducer.view(snapshot.state),
1945
- index: snapshot.index
1946
- };
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);
1947
2072
  };
1948
- const promptCache = /* @__PURE__ */ new Map();
1949
2073
  const resultEvent = async (call, suffix, result) => ({
1950
2074
  type: "ai.tool.result",
1951
2075
  id: await aiEventId("a2.ai.tool.result", call.generationId, call.toolCallId, suffix),
@@ -1968,25 +2092,32 @@ function createHandlers(options) {
1968
2092
  });
1969
2093
  const promptMessages = (input) => {
1970
2094
  const { ctx, call, generation: owner } = input;
1971
- const key = promptCacheKey(ctx.event.sessionId, call.generationId);
1972
- const cached = promptCache.get(key);
1973
- 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;
1974
2098
  const computation = (async () => {
1975
2099
  const frontier = owner.promptThroughIndex;
1976
2100
  if (frontier === void 0) throw new TypeError("AI work requires a prompt checkpoint");
1977
2101
  const snapshot = await readContext(ctx.session, ctx.event.index);
1978
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");
1979
2103
  const state = generationBase(snapshot.state);
1980
- 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({
1981
2110
  messages: state.messages.filter((message) => state.messageIndexes[message.id] <= frontier),
1982
2111
  state,
1983
2112
  tools
1984
- });
2113
+ }));
1985
2114
  })();
1986
- promptCache.set(key, computation);
1987
- computation.catch(() => {
1988
- if (promptCache.get(key) === computation) promptCache.delete(key);
2115
+ cachePrompt({
2116
+ sessionId,
2117
+ generationId: call.generationId,
2118
+ messages: computation
1989
2119
  });
2120
+ computation.catch(() => forgetPrompt(sessionId, call.generationId));
1990
2121
  return computation;
1991
2122
  };
1992
2123
  const toolExecutionFailure = async (ctx, call, error) => {
@@ -2055,7 +2186,7 @@ function createHandlers(options) {
2055
2186
  const messages = await promptMessages(input);
2056
2187
  if (checkAbort(ctx.signal)) return;
2057
2188
  const scope = {
2058
- contract: options.agent.contract,
2189
+ contract: options.contract,
2059
2190
  context: ctx
2060
2191
  };
2061
2192
  return toolScopeStorage.run(scope, () => runToolExecution(ctx, call, execute, messages));
@@ -2115,109 +2246,124 @@ function createHandlers(options) {
2115
2246
  state,
2116
2247
  coordinator: coordinatorState
2117
2248
  }),
2249
+ state: snapshot.state.app ?? {},
2118
2250
  session: { state: (reducer, readOptions) => ctx.session.state(reducer, {
2119
2251
  ...readOptions,
2120
2252
  through: readOptions?.through ?? snapshot.index
2121
2253
  }) },
2122
2254
  signal: ctx.signal
2123
2255
  };
2124
- let generationStarted = false;
2125
- try {
2126
- const resolvedModel = await resolve(options.model, resolverContext);
2127
- if (resolvedModel === void 0) throw new TypeError("the model resolver returned undefined");
2128
- if (checkAbort(ctx.signal)) return;
2129
- const resolvedInstructions = options.instructions === void 0 ? void 0 : await resolve(options.instructions, resolverContext);
2130
- if (checkAbort(ctx.signal)) return;
2131
- const compaction = typeof configuredCompaction === "function" ? validateCompaction({
2132
- compaction: await resolve(configuredCompaction, resolverContext),
2133
- explicit: true,
2134
- generation: options.generation,
2135
- tools: options.tools
2136
- }) : staticCompaction;
2137
- if (checkAbort(ctx.signal)) return;
2138
- if (request.reason === "tool" && responseStepCount >= maxSteps) {
2139
- const source = response.source?.generation;
2140
- if (source === void 0) return;
2141
- return {
2142
- type: "ai.generation.failed",
2143
- id: await aiEventId("a2.ai.step-limit", requestId),
2144
- payload: {
2145
- requestId: source.requestId,
2146
- messageId: source.messageId,
2147
- generationId: source.generationId,
2148
- responseMessageId,
2149
- error: `agent exceeded the ${maxSteps}-step limit`,
2150
- stepLimit: true
2151
- }
2152
- };
2153
- }
2154
- const started = {
2155
- requestId,
2156
- messageId: request.messageId,
2157
- generationId,
2158
- responseMessageId,
2159
- attempt,
2160
- model: modelName(resolvedModel),
2161
- 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
+ }
2162
2270
  };
2163
- const catalogModelId = gatewayModelId(resolvedModel);
2164
- const gatewayOptions = options.generation?.providerOptions?.["gateway"];
2165
- const usesFallbackModels = Array.isArray(gatewayOptions?.["models"]) && gatewayOptions["models"].length > 0;
2166
- const discoversMetadata = compaction !== false && !("shouldCompact" in compaction) && compaction.thresholdTokens === void 0 && !usesFallbackModels && catalogModelId !== void 0;
2167
- const startEvents = [];
2168
- 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({
2169
2275
  type: "ai.model.metadata.requested",
2170
- id: await aiEventId("a2.ai.model.metadata", options.agent.contract.name, ctx.event.sessionId, catalogModelId),
2171
- payload: { modelId: catalogModelId }
2276
+ id: await aiEventId("a2.ai.model.metadata", options.contract.name, ctx.event.sessionId, metadataModelId),
2277
+ payload: { modelId: metadataModelId }
2172
2278
  });
2173
2279
  if (previous) {
2174
- const payload = previous;
2175
2280
  const superseded = {
2176
2281
  requestId,
2177
- messageId: payload.messageId,
2178
- generationId: payload.generationId,
2179
- responseMessageId: payload.responseMessageId,
2282
+ messageId: previous.messageId,
2283
+ generationId: previous.generationId,
2284
+ responseMessageId: previous.responseMessageId,
2180
2285
  error: "generation attempt was superseded after an incomplete run",
2181
2286
  superseded: true
2182
2287
  };
2183
- startEvents.push({
2288
+ events.push({
2184
2289
  type: "ai.generation.failed",
2185
- id: await aiEventId("a2.ai.superseded", payload.generationId),
2290
+ id: await aiEventId("a2.ai.superseded", previous.generationId),
2186
2291
  payload: superseded
2187
2292
  });
2188
2293
  }
2189
- startEvents.push({
2294
+ events.push({
2190
2295
  type: "ai.generation.started",
2191
2296
  id: generationId,
2192
2297
  payload: started
2193
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;
2194
2342
  const generationEvents = await control.append({
2195
2343
  ctx,
2196
2344
  name: "generation-start",
2197
- events: startEvents
2345
+ events: await startEvents(startedPayload(model), discoversMetadata && !Object.hasOwn(state.modelMetadata, catalogModelId) ? catalogModelId : void 0)
2198
2346
  });
2347
+ const startIndex = generationEvents.find((event) => event.id === generationId).index;
2199
2348
  generationStarted = true;
2200
2349
  if (checkAbort(ctx.signal)) return;
2201
2350
  const promptCoordinatorState = coordinatorState;
2202
- const messages = contextMessages({
2203
- state,
2204
- coordinator: promptCoordinatorState
2205
- });
2351
+ const { messages } = resolverContext;
2206
2352
  let generationMessages = messages;
2207
- 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({
2208
2359
  messages,
2209
2360
  state,
2210
2361
  tools
2211
- });
2362
+ }));
2212
2363
  const baseContext = {
2213
- request: ctx.event,
2214
- requestId,
2364
+ ...resolverContext,
2215
2365
  generationId,
2216
- responseMessageId,
2217
- messages,
2218
2366
  modelMessages,
2219
- session: resolverContext.session,
2220
- signal: ctx.signal,
2221
2367
  model: resolvedModel,
2222
2368
  tools,
2223
2369
  ...resolvedInstructions === void 0 ? {} : { instructions: resolvedInstructions },
@@ -2282,14 +2428,20 @@ function createHandlers(options) {
2282
2428
  }
2283
2429
  }]
2284
2430
  }));
2285
- 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) ? {
2286
2438
  messages: [],
2287
2439
  ...await summarize({
2288
- agentName: options.agent.contract.name,
2440
+ agentName: options.contract.name,
2289
2441
  context: baseContext,
2290
2442
  instructions: policy.instructions
2291
2443
  })
2292
- } : { messages: await policy.compact(compactionContext) };
2444
+ } : { messages: await policy.compact(compactionContext) });
2293
2445
  if (checkAbort(ctx.signal)) return;
2294
2446
  const completed = {
2295
2447
  generationId,
@@ -2313,7 +2465,7 @@ function createHandlers(options) {
2313
2465
  }
2314
2466
  }
2315
2467
  const currentState = foldAIEvents({
2316
- reducer: contextReducer[aiStateReducer],
2468
+ reducer: contextReducer,
2317
2469
  events: generationEvents,
2318
2470
  state
2319
2471
  });
@@ -2321,12 +2473,21 @@ function createHandlers(options) {
2321
2473
  state: currentState,
2322
2474
  coordinator: promptCoordinatorState
2323
2475
  });
2324
- 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({
2325
2482
  messages: generationMessages,
2326
2483
  state: currentState,
2327
2484
  tools
2485
+ }));
2486
+ cachePrompt({
2487
+ sessionId: ctx.event.sessionId,
2488
+ generationId,
2489
+ messages: Promise.resolve(modelMessages)
2328
2490
  });
2329
- promptCache.set(promptCacheKey(ctx.event.sessionId, generationId), Promise.resolve(modelMessages));
2330
2491
  if (tracksInput && (inputTokenEstimate === void 0 || compacted)) inputTokenEstimate = await estimateInputTokens({
2331
2492
  messages: modelMessages,
2332
2493
  instructions: resolvedInstructions,
@@ -2335,23 +2496,14 @@ function createHandlers(options) {
2335
2496
  let pendingToolCalls = [];
2336
2497
  if (checkAbort(ctx.signal)) return;
2337
2498
  const generateContext = {
2338
- request: ctx.event,
2339
- requestId,
2340
- generationId,
2341
- responseMessageId,
2499
+ ...baseContext,
2342
2500
  messages: generationMessages,
2343
- modelMessages,
2344
- session: resolverContext.session,
2345
- signal: ctx.signal,
2346
- model: resolvedModel,
2347
- tools,
2348
- ...resolvedInstructions === void 0 ? {} : { instructions: resolvedInstructions },
2349
- generation: options.generation ?? {}
2501
+ modelMessages
2350
2502
  };
2351
2503
  const custom = options.generate !== void 0;
2352
2504
  const updates = consumeGeneration({
2353
2505
  source: options.generate === void 0 ? await generateWithAISDK({
2354
- agentName: options.agent.contract.name,
2506
+ agentName: options.contract.name,
2355
2507
  context: generateContext,
2356
2508
  messageMetadata: options.messageMetadata
2357
2509
  }) : { stream: await options.generate(generateContext) },
@@ -2366,11 +2518,7 @@ function createHandlers(options) {
2366
2518
  continue;
2367
2519
  }
2368
2520
  const progress = {
2369
- requestId,
2370
- messageId: request.messageId,
2371
- generationId,
2372
- responseMessageId,
2373
- sequence,
2521
+ startIndex,
2374
2522
  chunks: update.chunks
2375
2523
  };
2376
2524
  const lifecycle = await lifecycleEvents({
@@ -2415,6 +2563,7 @@ function createHandlers(options) {
2415
2563
  if (unresolved.length > 0) throw new Error(`generation ended with unresolved tool authorization for '${unresolved[0].call.toolName}'`);
2416
2564
  const pendingEvents = await Promise.all(pendingToolCalls.filter((pending) => pending.classification === "automatic" || options.generate === void 0 && pending.classification === "unknown").map((pending) => toolCalledEvent(pending.call)));
2417
2565
  if (finish.finishReason === "tool-calls") return [...pendingEvents, completionEvent];
2566
+ forgetPrompt(ctx.event.sessionId, generationId);
2418
2567
  return [
2419
2568
  ...pendingEvents,
2420
2569
  completionEvent,
@@ -2431,7 +2580,13 @@ function createHandlers(options) {
2431
2580
  throw error;
2432
2581
  }
2433
2582
  if (checkAbort(ctx.signal)) return;
2434
- 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);
2435
2590
  const failed = {
2436
2591
  requestId,
2437
2592
  messageId: request.messageId,
@@ -2446,10 +2601,6 @@ function createHandlers(options) {
2446
2601
  };
2447
2602
  }
2448
2603
  };
2449
- const clearPromptCache = (sessionId) => {
2450
- const prefix = `${sessionId}\u001f`;
2451
- for (const key of promptCache.keys()) if (key.startsWith(prefix)) promptCache.delete(key);
2452
- };
2453
2604
  return {
2454
2605
  "ai.model.metadata.requested": { handler: async (ctx) => {
2455
2606
  const limits = await readModelLimits({
@@ -2474,29 +2625,16 @@ function createHandlers(options) {
2474
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");
2475
2626
  return "a2.ai.control";
2476
2627
  },
2477
- handler: control.handler
2628
+ handler: controlHandler
2478
2629
  },
2479
2630
  "ai.work.reported": {
2480
2631
  lane: "a2.ai.control",
2481
- handler: control.handler
2632
+ handler: controlHandler
2482
2633
  },
2483
- "ai.message.completed": { handler: async (ctx) => {
2484
- clearPromptCache(ctx.event.sessionId);
2485
- } },
2486
- "ai.message.interrupted": { handler: async (ctx) => {
2487
- clearPromptCache(ctx.event.sessionId);
2488
- } },
2489
2634
  "ai.session.closed": {
2490
2635
  lane: "a2.ai.control",
2491
- handler: async (ctx) => {
2492
- clearPromptCache(ctx.event.sessionId);
2493
- return control.handler(ctx);
2494
- }
2636
+ handler: controlHandler
2495
2637
  },
2496
- "ai.generation.failed": { handler: (ctx) => {
2497
- promptCache.delete(promptCacheKey(ctx.event.sessionId, ctx.event.payload.generationId));
2498
- return Promise.resolve();
2499
- } },
2500
2638
  "ai.generation.requested": {
2501
2639
  lane: "a2.ai.model",
2502
2640
  abortOn: {
@@ -2507,7 +2645,12 @@ function createHandlers(options) {
2507
2645
  },
2508
2646
  "ai.session.closed": true
2509
2647
  },
2510
- 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 () => {
2511
2654
  try {
2512
2655
  return control.settled({
2513
2656
  ctx,
@@ -2523,9 +2666,10 @@ function createHandlers(options) {
2523
2666
  });
2524
2667
  throw error;
2525
2668
  }
2526
- }
2669
+ })
2527
2670
  },
2528
2671
  "ai.tool.execution.requested": {
2672
+ ...options.toolLane === void 0 ? {} : { lane: options.toolLane },
2529
2673
  abortOn: {
2530
2674
  "ai.control.requested": (event, trigger) => event.payload.action === "stop" && event.payload.turnId === trigger.payload.turnId,
2531
2675
  "ai.generation.failed": (event, trigger) => event.payload.generationId === trigger.payload.call.generationId,
@@ -2535,16 +2679,40 @@ function createHandlers(options) {
2535
2679
  },
2536
2680
  "ai.session.closed": true
2537
2681
  },
2538
- 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))
2539
2690
  }
2540
2691
  };
2541
2692
  }
2542
- /** Assemble an A2 server with the built-in agent handlers and app extensions. */
2543
- function createAgentServer(options) {
2544
- 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
+ }
2545
2712
  const builtIns = createHandlers({
2546
- agent: definition,
2713
+ contract,
2547
2714
  model,
2715
+ ...serverOptions.telemetry === void 0 ? {} : { telemetry: serverOptions.telemetry },
2548
2716
  ...tools === void 0 ? {} : { tools },
2549
2717
  ...instructions === void 0 ? {} : { instructions },
2550
2718
  ...generation === void 0 ? {} : { generation },
@@ -2552,20 +2720,26 @@ function createAgentServer(options) {
2552
2720
  ...messageMetadata === void 0 ? {} : { messageMetadata },
2553
2721
  ...generate === void 0 ? {} : { generate },
2554
2722
  ...compaction === void 0 ? {} : { compaction },
2555
- ...progress === void 0 ? {} : { progress }
2723
+ ...progress === void 0 ? {} : { progress },
2724
+ ...toolLane === void 0 ? {} : { toolLane }
2556
2725
  });
2726
+ for (const type of Object.keys(handlers ?? {})) if (Object.hasOwn(builtIns, type)) throw new TypeError(`agent handles '${type}' itself`);
2557
2727
  const server = createServer({
2558
2728
  ...serverOptions,
2559
- contract: definition.contract,
2729
+ contract,
2560
2730
  handlers: {
2561
2731
  ...builtIns,
2562
2732
  ...handlers
2563
2733
  }
2564
2734
  });
2565
- setServerFetchHooks(server, { validateIngress: validateAgentIngress });
2735
+ const applicationEvents = "contract" in rest;
2736
+ setServerFetchHooks(server, { validateIngress: (context) => validateAgentIngress({
2737
+ context,
2738
+ applicationEvents
2739
+ }) });
2566
2740
  return server;
2567
2741
  }
2568
2742
  //#endregion
2569
- export { createAgentServer, createHandlers, runWithHandlerContext };
2743
+ export { agent, runWithHandlerContext };
2570
2744
 
2571
2745
  //# sourceMappingURL=ai-server.js.map