experimental-a2 0.16.1 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (406) hide show
  1. package/CHANGELOG.md +373 -0
  2. package/README.md +1 -1
  3. package/dist/{actor-ohPC-81x.d.ts → actor-Bjce9_5U.d.ts} +21 -11
  4. package/dist/actor-Bjce9_5U.d.ts.map +1 -0
  5. package/dist/actor-client.d.ts +2 -5
  6. package/dist/actor-client.d.ts.map +1 -1
  7. package/dist/actor-client.js +13 -4
  8. package/dist/actor-client.js.map +1 -1
  9. package/dist/actor-react.d.ts +5 -3
  10. package/dist/actor-react.d.ts.map +1 -1
  11. package/dist/actor-react.js +20 -10
  12. package/dist/actor-react.js.map +1 -1
  13. package/dist/{actor-shared-B5tJfzt-.js → actor-shared-t7w8xIKP.js} +12 -7
  14. package/dist/actor-shared-t7w8xIKP.js.map +1 -0
  15. package/dist/actor.d.ts +1 -1
  16. package/dist/actor.js +42 -23
  17. package/dist/actor.js.map +1 -1
  18. package/dist/{ai-DgOBltJ_.js → ai-BpOGm-zB.js} +621 -216
  19. package/dist/ai-BpOGm-zB.js.map +1 -0
  20. package/dist/{ai-CrEf6p_W.d.ts → ai-DrUaMyRt.d.ts} +114 -100
  21. package/dist/ai-DrUaMyRt.d.ts.map +1 -0
  22. package/dist/ai-client.d.ts +27 -0
  23. package/dist/ai-client.d.ts.map +1 -0
  24. package/dist/ai-client.js +29 -0
  25. package/dist/ai-client.js.map +1 -0
  26. package/dist/ai-fx.browser.d.ts +1 -0
  27. package/dist/ai-fx.browser.js +6 -0
  28. package/dist/ai-fx.browser.js.map +1 -0
  29. package/dist/ai-fx.d.ts +10 -0
  30. package/dist/ai-fx.d.ts.map +1 -0
  31. package/dist/ai-fx.js +634 -0
  32. package/dist/ai-fx.js.map +1 -0
  33. package/dist/ai-harness-BuLh_EYi.js +273 -0
  34. package/dist/ai-harness-BuLh_EYi.js.map +1 -0
  35. package/dist/ai-harness-DvsPeJeB.d.ts +26 -0
  36. package/dist/ai-harness-DvsPeJeB.d.ts.map +1 -0
  37. package/dist/ai-pi.browser.d.ts +1 -0
  38. package/dist/ai-pi.browser.js +6 -0
  39. package/dist/ai-pi.browser.js.map +1 -0
  40. package/dist/ai-pi.d.ts +17 -0
  41. package/dist/ai-pi.d.ts.map +1 -0
  42. package/dist/ai-pi.js +904 -0
  43. package/dist/ai-pi.js.map +1 -0
  44. package/dist/ai-react.d.ts +96 -0
  45. package/dist/ai-react.d.ts.map +1 -0
  46. package/dist/ai-react.js +142 -0
  47. package/dist/ai-react.js.map +1 -0
  48. package/dist/ai-server.browser.js +2 -2
  49. package/dist/ai-server.browser.js.map +1 -1
  50. package/dist/ai-server.d.ts +109 -43
  51. package/dist/ai-server.d.ts.map +1 -1
  52. package/dist/ai-server.js +526 -283
  53. package/dist/ai-server.js.map +1 -1
  54. package/dist/ai.d.ts +2 -2
  55. package/dist/ai.js +2 -2
  56. package/dist/bounded-cache-CPHWF-8h.js +41 -0
  57. package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
  58. package/dist/{client-BYzHjkwU.js → client-BReKqwZC.js} +447 -80
  59. package/dist/client-BReKqwZC.js.map +1 -0
  60. package/dist/{client-CzyacQpJ.d.ts → client-h_DKFAjj.d.ts} +14 -13
  61. package/dist/client-h_DKFAjj.d.ts.map +1 -0
  62. package/dist/client.d.ts +4 -2
  63. package/dist/client.js +1 -1
  64. package/dist/{contract-CKRg_E4q.js → contract-BYKASlCV.js} +12 -21
  65. package/dist/contract-BYKASlCV.js.map +1 -0
  66. package/dist/{idempotent-replay-DVOlyYbx.js → idempotent-replay-JdgMQRRm.js} +2 -2
  67. package/dist/{idempotent-replay-DVOlyYbx.js.map → idempotent-replay-JdgMQRRm.js.map} +1 -1
  68. package/dist/index.d.ts +6 -5
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +4 -3
  71. package/dist/{errors-DvhSXnxk.d.ts → initial-snapshot-Bx-c0pDP.d.ts} +7 -3
  72. package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +1 -0
  73. package/dist/{internal-Dq2qYxou.js → internal-B2T3U4Rq.js} +29 -2
  74. package/dist/internal-B2T3U4Rq.js.map +1 -0
  75. package/dist/otel.d.ts +1 -1
  76. package/dist/react-BBXgHWFc.d.ts +86 -0
  77. package/dist/react-BBXgHWFc.d.ts.map +1 -0
  78. package/dist/react.d.ts +2 -122
  79. package/dist/react.js +107 -100
  80. package/dist/react.js.map +1 -1
  81. package/dist/reducer-BAXc8JyF.js +87 -0
  82. package/dist/reducer-BAXc8JyF.js.map +1 -0
  83. package/dist/{reducer-otzHjuJj.d.ts → reducer-CqA59Q6U.d.ts} +46 -30
  84. package/dist/reducer-CqA59Q6U.d.ts.map +1 -0
  85. package/dist/reducer-runtime-DQwz_J59.js +28 -0
  86. package/dist/reducer-runtime-DQwz_J59.js.map +1 -0
  87. package/dist/scheduler-qstash.d.ts +3 -3
  88. package/dist/scheduler-qstash.d.ts.map +1 -1
  89. package/dist/scheduler-qstash.js +33 -10
  90. package/dist/scheduler-qstash.js.map +1 -1
  91. package/dist/{scheduler-task-BpzhPnRS.js → scheduler-task-X1SToDjP.js} +18 -3
  92. package/dist/scheduler-task-X1SToDjP.js.map +1 -0
  93. package/dist/scheduler-vercel.d.ts +4 -9
  94. package/dist/scheduler-vercel.d.ts.map +1 -1
  95. package/dist/scheduler-vercel.js +25 -44
  96. package/dist/scheduler-vercel.js.map +1 -1
  97. package/dist/{server-BD5ckxZb.js → server-Dw_k_7kO.js} +904 -219
  98. package/dist/server-Dw_k_7kO.js.map +1 -0
  99. package/dist/{server-DbD7IJVK.d.ts → server-En7cbi3v.d.ts} +116 -26
  100. package/dist/server-En7cbi3v.d.ts.map +1 -0
  101. package/dist/server.d.ts +4 -4
  102. package/dist/server.js +2 -2
  103. package/dist/session-clear-Bo-XTFEy.d.ts +12 -0
  104. package/dist/session-clear-Bo-XTFEy.d.ts.map +1 -0
  105. package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
  106. package/dist/session-clear-DGI4jTQ5.js.map +1 -0
  107. package/dist/{store-DyZM6fS5.d.ts → store-Djdv0bnZ.d.ts} +73 -3
  108. package/dist/store-Djdv0bnZ.d.ts.map +1 -0
  109. package/dist/store-N8PXxDAS.js.map +1 -1
  110. package/dist/store-memory.d.ts +1 -1
  111. package/dist/store-memory.d.ts.map +1 -1
  112. package/dist/store-memory.js +205 -66
  113. package/dist/store-memory.js.map +1 -1
  114. package/dist/{store-polling-CmxUbV93.js → store-polling-B5FcwJfo.js} +21 -4
  115. package/dist/store-polling-B5FcwJfo.js.map +1 -0
  116. package/dist/store-postgres.d.ts +1 -1
  117. package/dist/store-postgres.d.ts.map +1 -1
  118. package/dist/store-postgres.js +450 -108
  119. package/dist/store-postgres.js.map +1 -1
  120. package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-BAwS8RWn.js} +3 -3
  121. package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-BAwS8RWn.js.map} +1 -1
  122. package/dist/store-redis-http.d.ts +1 -1
  123. package/dist/store-redis-http.d.ts.map +1 -1
  124. package/dist/store-redis-http.js +293 -11
  125. package/dist/store-redis-http.js.map +1 -1
  126. package/dist/store-redis-notify-BRITti_o.js +2493 -0
  127. package/dist/store-redis-notify-BRITti_o.js.map +1 -0
  128. package/dist/store-redis.d.ts +1 -1
  129. package/dist/store-redis.d.ts.map +1 -1
  130. package/dist/store-redis.js +11 -11
  131. package/dist/store-redis.js.map +1 -1
  132. package/dist/store-sqlite.d.ts +1 -1
  133. package/dist/store-sqlite.d.ts.map +1 -1
  134. package/dist/store-sqlite.js +197 -33
  135. package/dist/store-sqlite.js.map +1 -1
  136. package/dist/stream-buffer-Cx7AAPGq.js +23 -0
  137. package/dist/stream-buffer-Cx7AAPGq.js.map +1 -0
  138. package/dist/{telemetry-B5jzpy6y.d.ts → telemetry-Cyfqo6Qt.d.ts} +2 -2
  139. package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
  140. package/dist/{wire-BO5wWCb1.js → wire-rSUglYo8.js} +132 -10
  141. package/dist/wire-rSUglYo8.js.map +1 -0
  142. package/docs/01-quickstart.mdx +50 -84
  143. package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
  144. package/docs/actors/01-introduction.mdx +48 -82
  145. package/docs/actors/02-concurrency.mdx +43 -70
  146. package/docs/actors/03-timers.mdx +28 -42
  147. package/docs/actors/04-routes.mdx +110 -137
  148. package/docs/actors/meta.ts +1 -1
  149. package/docs/agents/01-introduction.mdx +257 -0
  150. package/docs/agents/02-tools.mdx +247 -0
  151. package/docs/agents/03-conversations.mdx +538 -0
  152. package/docs/agents/04-harnesses.mdx +136 -0
  153. package/docs/agents/meta.ts +1 -0
  154. package/docs/client/01-react.mdx +294 -0
  155. package/docs/client/02-transports.mdx +86 -0
  156. package/docs/client/03-presence.mdx +224 -0
  157. package/docs/client/meta.ts +1 -0
  158. package/docs/contract/01-events.mdx +57 -0
  159. package/docs/contract/02-state.mdx +142 -0
  160. package/docs/contract/03-sessions.mdx +175 -0
  161. package/docs/index.mdx +112 -156
  162. package/docs/reference/01-core.mdx +139 -0
  163. package/docs/reference/02-server.mdx +855 -0
  164. package/docs/reference/03-client.mdx +301 -0
  165. package/docs/reference/04-ai.mdx +722 -0
  166. package/docs/reference/05-adapters.mdx +351 -0
  167. package/docs/reference/06-errors.mdx +138 -0
  168. package/docs/reference/meta.ts +1 -1
  169. package/docs/server/01-handlers.mdx +200 -0
  170. package/docs/server/02-timers.mdx +130 -0
  171. package/docs/server/03-cancellation.mdx +141 -0
  172. package/docs/server/04-routes.mdx +243 -0
  173. package/docs/server/05-production.mdx +322 -0
  174. package/docs/server/06-guarantees.mdx +123 -0
  175. package/examples/playground/DEPLOY.md +53 -7
  176. package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
  177. package/examples/playground/app/a0/[appId]/page.tsx +53 -0
  178. package/examples/playground/app/a0/a0.css +818 -0
  179. package/examples/playground/app/a0/app-list.tsx +218 -0
  180. package/examples/playground/app/a0/app-view.tsx +63 -0
  181. package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
  182. package/examples/playground/app/a0/apps/[appId]/route.ts +28 -0
  183. package/examples/playground/app/a0/apps.test.ts +128 -0
  184. package/examples/playground/app/a0/apps.ts +102 -0
  185. package/examples/playground/app/a0/chat.tsx +410 -0
  186. package/examples/playground/app/a0/columns.tsx +109 -0
  187. package/examples/playground/app/a0/context-menu.tsx +116 -0
  188. package/examples/playground/app/a0/cookies.ts +46 -0
  189. package/examples/playground/app/a0/crash-on-request.ts +72 -0
  190. package/examples/playground/app/a0/crashes/route.ts +3 -0
  191. package/examples/playground/app/a0/crashes.test.ts +33 -0
  192. package/examples/playground/app/a0/crashes.ts +28 -0
  193. package/examples/playground/app/a0/devtools.tsx +164 -0
  194. package/examples/playground/app/a0/events/route.ts +6 -0
  195. package/examples/playground/app/a0/flags.ts +30 -0
  196. package/examples/playground/app/a0/fx/events/route.ts +9 -0
  197. package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
  198. package/examples/playground/app/a0/fx/short/events/route.ts +11 -0
  199. package/examples/playground/app/a0/layout.tsx +38 -0
  200. package/examples/playground/app/a0/list/route.ts +10 -0
  201. package/examples/playground/app/a0/model.test.ts +73 -0
  202. package/examples/playground/app/a0/model.ts +95 -0
  203. package/examples/playground/app/a0/no-photon.cjs +4 -0
  204. package/examples/playground/app/a0/no-quickjs.cjs +3 -0
  205. package/examples/playground/app/a0/page.tsx +14 -0
  206. package/examples/playground/app/a0/pi/events/route.ts +9 -0
  207. package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
  208. package/examples/playground/app/a0/pi/short/events/route.ts +11 -0
  209. package/examples/playground/app/a0/preview-pane.tsx +145 -0
  210. package/examples/playground/app/a0/preview.test.ts +53 -0
  211. package/examples/playground/app/a0/preview.ts +26 -0
  212. package/examples/playground/app/a0/sandbox-log.ts +75 -0
  213. package/examples/playground/app/a0/sandbox-session.ts +152 -0
  214. package/examples/playground/app/a0/sandbox.ts +191 -0
  215. package/examples/playground/app/a0/sandboxes/scheduler/route.ts +6 -0
  216. package/examples/playground/app/a0/scheduler/route.ts +7 -0
  217. package/examples/playground/app/a0/server.ts +213 -0
  218. package/examples/playground/app/a0/session.ts +59 -0
  219. package/examples/playground/app/a0/shell.tsx +158 -0
  220. package/examples/playground/app/a0/short/events/route.ts +9 -0
  221. package/examples/playground/app/a0/splitter.tsx +120 -0
  222. package/examples/playground/app/agent/[agentId]/agent-client.tsx +144 -174
  223. package/examples/playground/app/agent/[agentId]/command-receipt.test.tsx +65 -0
  224. package/examples/playground/app/agent/[agentId]/command-receipt.tsx +27 -0
  225. package/examples/playground/app/agent/[agentId]/compaction-event.tsx +25 -24
  226. package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +119 -0
  227. package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +208 -165
  228. package/examples/playground/app/agent/[agentId]/page.tsx +2 -16
  229. package/examples/playground/app/agent/events/route.ts +5 -2
  230. package/examples/playground/app/agent/ingress.test.ts +101 -0
  231. package/examples/playground/app/agent/model-input.test.tsx +58 -0
  232. package/examples/playground/app/agent/model-input.tsx +115 -0
  233. package/examples/playground/app/agent/model.test.ts +73 -0
  234. package/examples/playground/app/agent/model.ts +58 -26
  235. package/examples/playground/app/agent/new-agent-session.tsx +37 -19
  236. package/examples/playground/app/agent/page.tsx +4 -4
  237. package/examples/playground/app/agent/server.ts +19 -15
  238. package/examples/playground/app/agent/session.ts +5 -6
  239. package/examples/playground/app/agent/telemetry.test.ts +189 -0
  240. package/examples/playground/app/agent/telemetry.ts +30 -0
  241. package/examples/playground/app/agent/trace.test.ts +23 -0
  242. package/examples/playground/app/agent/trace.ts +18 -0
  243. package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +12 -12
  244. package/examples/playground/app/canvas/[canvasId]/page.tsx +1 -2
  245. package/examples/playground/app/canvas/[canvasId]/session.ts +2 -2
  246. package/examples/playground/app/canvas/model.ts +53 -53
  247. package/examples/playground/app/chat/[chatId]/agent-state.test.ts +176 -0
  248. package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +16 -1
  249. package/examples/playground/app/chat/[chatId]/chat-client.tsx +55 -77
  250. package/examples/playground/app/chat/[chatId]/chat-view.ts +37 -3
  251. package/examples/playground/app/chat/[chatId]/page.tsx +7 -15
  252. package/examples/playground/app/chat/[chatId]/session.ts +2 -19
  253. package/examples/playground/app/chat/agent/route.ts +9 -0
  254. package/examples/playground/app/chat/model.test.ts +46 -119
  255. package/examples/playground/app/chat/model.ts +82 -128
  256. package/examples/playground/app/chat/scheduler/route.ts +2 -2
  257. package/examples/playground/app/chat/server.test.ts +112 -0
  258. package/examples/playground/app/chat/server.ts +113 -117
  259. package/examples/playground/app/components/activity-feed.tsx +30 -1
  260. package/examples/playground/app/components/activity-history.test.tsx +35 -0
  261. package/examples/playground/app/components/demo-icons.tsx +89 -0
  262. package/examples/playground/app/components/demos.tsx +33 -0
  263. package/examples/playground/app/components/fast-link.test.tsx +58 -0
  264. package/examples/playground/app/components/fast-link.tsx +55 -0
  265. package/examples/playground/app/components/site-header.tsx +96 -0
  266. package/examples/playground/app/counter/counter-client.tsx +3 -3
  267. package/examples/playground/app/counter/model.test.ts +3 -3
  268. package/examples/playground/app/counter/model.ts +11 -21
  269. package/examples/playground/app/counter/page.tsx +2 -15
  270. package/examples/playground/app/counter/session.ts +2 -2
  271. package/examples/playground/app/documents/[documentId]/code-editor.tsx +12 -1
  272. package/examples/playground/app/documents/[documentId]/document-client.tsx +10 -11
  273. package/examples/playground/app/documents/[documentId]/page.tsx +1 -2
  274. package/examples/playground/app/documents/[documentId]/session.ts +2 -2
  275. package/examples/playground/app/documents/model.ts +27 -27
  276. package/examples/playground/app/globals.css +420 -207
  277. package/examples/playground/app/initial-state.test.tsx +54 -0
  278. package/examples/playground/app/layout.tsx +7 -23
  279. package/examples/playground/app/orders/[orderId]/page.tsx +1 -2
  280. package/examples/playground/app/orders/[orderId]/session.ts +2 -2
  281. package/examples/playground/app/orders/model.ts +49 -49
  282. package/examples/playground/app/page.tsx +13 -102
  283. package/examples/playground/app/recovery/[recoveryId]/page.tsx +1 -2
  284. package/examples/playground/app/recovery/[recoveryId]/session.ts +2 -2
  285. package/examples/playground/app/recovery/model.ts +30 -30
  286. package/examples/playground/instrumentation.ts +5 -0
  287. package/examples/playground/next.config.ts +17 -5
  288. package/examples/playground/package.json +13 -2
  289. package/examples/playground/scripts/a0-template.ts +77 -0
  290. package/examples/playground/vercel.json +36 -0
  291. package/package.json +29 -3
  292. package/src/actor-client.ts +16 -4
  293. package/src/actor-react.ts +25 -11
  294. package/src/actor-shared.ts +21 -6
  295. package/src/actor.ts +76 -31
  296. package/src/ai-agent-actions.ts +219 -0
  297. package/src/ai-agent.ts +2517 -0
  298. package/src/ai-app-state.ts +91 -0
  299. package/src/ai-client-state.ts +6 -3
  300. package/src/ai-client.ts +62 -0
  301. package/src/ai-context-schema.ts +25 -5
  302. package/src/ai-context.ts +90 -40
  303. package/src/ai-control-server.ts +92 -72
  304. package/src/ai-coordinator.ts +23 -22
  305. package/src/ai-fx.browser.ts +5 -0
  306. package/src/ai-fx.ts +833 -0
  307. package/src/ai-harness.ts +518 -0
  308. package/src/ai-message-base.ts +209 -0
  309. package/src/ai-message-projection.ts +262 -64
  310. package/src/ai-model-metadata.ts +11 -6
  311. package/src/ai-pi.browser.ts +5 -0
  312. package/src/ai-pi.ts +1263 -0
  313. package/src/ai-progress-batches.ts +26 -0
  314. package/src/ai-projector.ts +95 -14
  315. package/src/ai-react.ts +146 -0
  316. package/src/ai-sdk-step.ts +2 -2
  317. package/src/ai-server.browser.ts +2 -2
  318. package/src/ai-server.ts +23 -1965
  319. package/src/ai-stored-state.ts +48 -163
  320. package/src/ai.ts +339 -155
  321. package/src/bootstrap-stream.ts +72 -0
  322. package/src/bounded-cache.ts +55 -0
  323. package/src/client-render-session.ts +49 -0
  324. package/src/client.ts +688 -151
  325. package/src/contract.ts +92 -42
  326. package/src/deterministic-id.ts +37 -0
  327. package/src/equal-json.ts +32 -0
  328. package/src/errors.ts +13 -1
  329. package/src/index.ts +5 -1
  330. package/src/initial-snapshot.ts +39 -0
  331. package/src/internal.ts +52 -0
  332. package/src/libfx.d.ts +34 -0
  333. package/src/postgres-notification-scope.ts +14 -3
  334. package/src/react.ts +171 -206
  335. package/src/redis-http-reply.ts +256 -0
  336. package/src/redis-http-stream.ts +73 -0
  337. package/src/redis-http-subscriptions.ts +13 -5
  338. package/src/reducer-runtime.ts +52 -0
  339. package/src/reducer.ts +93 -11
  340. package/src/scheduler-qstash.ts +49 -20
  341. package/src/scheduler-task.ts +34 -2
  342. package/src/scheduler-vercel.ts +32 -84
  343. package/src/server-fetch.ts +139 -35
  344. package/src/server.ts +1133 -255
  345. package/src/session-clear.ts +23 -0
  346. package/src/session-socket.ts +154 -49
  347. package/src/sse.ts +71 -4
  348. package/src/store-memory.ts +252 -75
  349. package/src/store-polling.ts +18 -4
  350. package/src/store-postgres.ts +541 -114
  351. package/src/store-redis-core.ts +1450 -493
  352. package/src/store-redis-http.ts +18 -2
  353. package/src/store-redis-notify.ts +262 -85
  354. package/src/store-redis.ts +19 -9
  355. package/src/store-sqlite.ts +319 -45
  356. package/src/store.ts +79 -1
  357. package/src/stream-buffer.ts +43 -0
  358. package/src/telemetry.ts +14 -1
  359. package/src/wire.ts +241 -13
  360. package/dist/actor-ohPC-81x.d.ts.map +0 -1
  361. package/dist/actor-shared-B5tJfzt-.js.map +0 -1
  362. package/dist/ai-CrEf6p_W.d.ts.map +0 -1
  363. package/dist/ai-DgOBltJ_.js.map +0 -1
  364. package/dist/client-BYzHjkwU.js.map +0 -1
  365. package/dist/client-CzyacQpJ.d.ts.map +0 -1
  366. package/dist/contract-CKRg_E4q.js.map +0 -1
  367. package/dist/errors-DCk6ch5n.js.map +0 -1
  368. package/dist/errors-DvhSXnxk.d.ts.map +0 -1
  369. package/dist/internal-Dq2qYxou.js.map +0 -1
  370. package/dist/react.d.ts.map +0 -1
  371. package/dist/reducer-DEMjEY_O.js +0 -29
  372. package/dist/reducer-DEMjEY_O.js.map +0 -1
  373. package/dist/reducer-otzHjuJj.d.ts.map +0 -1
  374. package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
  375. package/dist/server-BD5ckxZb.js.map +0 -1
  376. package/dist/server-DbD7IJVK.d.ts.map +0 -1
  377. package/dist/store-DyZM6fS5.d.ts.map +0 -1
  378. package/dist/store-polling-CmxUbV93.js.map +0 -1
  379. package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
  380. package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
  381. package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
  382. package/dist/validate-XKT4FSNn.js +0 -30
  383. package/dist/validate-XKT4FSNn.js.map +0 -1
  384. package/dist/wire-BO5wWCb1.js.map +0 -1
  385. package/docs/concepts/01-contracts.mdx +0 -142
  386. package/docs/concepts/02-handlers.mdx +0 -284
  387. package/docs/concepts/03-durability.mdx +0 -314
  388. package/docs/concepts/04-state.mdx +0 -209
  389. package/docs/guides/01-timers.mdx +0 -185
  390. package/docs/guides/02-cancellation.mdx +0 -161
  391. package/docs/guides/03-react.mdx +0 -384
  392. package/docs/guides/04-local-first.mdx +0 -88
  393. package/docs/guides/05-production.mdx +0 -521
  394. package/docs/guides/06-ai-agents.mdx +0 -1318
  395. package/docs/guides/08-application-data.mdx +0 -113
  396. package/docs/guides/09-presence.mdx +0 -266
  397. package/docs/guides/10-transports.mdx +0 -221
  398. package/docs/reference/01-api.mdx +0 -1896
  399. package/docs/reference/02-errors.mdx +0 -149
  400. package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
  401. package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
  402. package/examples/playground/app/agent/compaction-settings.ts +0 -49
  403. package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
  404. package/examples/playground/app/agent/compaction-timeline.ts +0 -208
  405. /package/docs/{concepts → contract}/meta.ts +0 -0
  406. /package/docs/{guides → server}/meta.ts +0 -0
@@ -4,20 +4,21 @@ import type {
4
4
  KeyboardEvent as ReactKeyboardEvent,
5
5
  ReactNode,
6
6
  } from 'react'
7
- import { useEffect, useRef, useState } from 'react'
7
+ import { useEffect, useId, useRef, useState } from 'react'
8
8
  import type { UIMessage } from 'ai'
9
- import {
10
- inputs,
11
- type ControlCommand,
12
- type PendingApproval,
13
- } from 'experimental-a2/ai'
9
+ import { isCompactionPart } from 'experimental-a2/ai'
10
+ import { useAgent, type AgentIndicator } from 'experimental-a2/ai/react'
11
+ import type { InitialSnapshot } from 'experimental-a2'
14
12
  import { ActivityFeed } from '@/app/components/activity-feed'
15
13
  import { ConnectionPill } from '@/app/components/connection-pill'
16
- import { useSession } from '../session'
17
- import { compactionTimeline } from '../compaction-timeline'
14
+ import { agentClient } from '../session'
15
+ import type { AgentState } from '../model'
16
+ import { ModelInput } from '../model-input'
18
17
  import { CompactionEvent } from './compaction-event'
19
18
  import { CompactionPanel } from './compaction-panel'
20
19
  import { AgentQueue } from './agent-queue'
20
+ import { CommandReceipt } from './command-receipt'
21
+ import { traceUrl } from '../trace'
21
22
 
22
23
  type MessagePart = UIMessage['parts'][number]
23
24
  type ToolPartView = {
@@ -110,34 +111,34 @@ function ToolCard({ part }: { part: ToolPartView }): ReactNode {
110
111
 
111
112
  function MessagePartView({ part }: { part: MessagePart }): ReactNode {
112
113
  if (part.type === 'text') return part.text ? <p>{part.text}</p> : null
114
+ if (isCompactionPart(part)) return <CompactionEvent data={part.data} />
113
115
  const tool = toolPartView(part)
114
116
  if (tool) return <ToolCard part={tool} />
115
117
  return null
116
118
  }
117
119
 
118
- const hasVisibleParts = (message: UIMessage): boolean =>
119
- message.parts.some(
120
- (part) =>
121
- (part.type === 'text' && part.text.length > 0) ||
122
- toolPartView(part) !== null,
123
- )
124
-
125
- function Thinking({ starting = false }: { starting?: boolean }): ReactNode {
120
+ function Thinking({
121
+ indicator,
122
+ }: {
123
+ indicator: Exclude<AgentIndicator, null>
124
+ }): ReactNode {
126
125
  return (
127
126
  <p className="agent-thinking" role="status">
128
127
  <span aria-hidden="true" />
129
- {starting ? 'Starting…' : 'Thinking…'}
128
+ {indicator === 'starting' ? 'Starting…' : 'Thinking…'}
130
129
  </p>
131
130
  )
132
131
  }
133
132
 
134
133
  function AgentMessage({
135
134
  message,
136
- thinking,
135
+ indicator,
137
136
  }: {
138
137
  message: UIMessage
139
- thinking: boolean
138
+ indicator: AgentIndicator
140
139
  }): ReactNode {
140
+ const traceHref =
141
+ message.role === 'assistant' ? traceUrl(message.metadata) : undefined
141
142
  const role =
142
143
  message.role === 'user'
143
144
  ? message.id.startsWith('reminder:')
@@ -152,7 +153,17 @@ function AgentMessage({
152
153
  {message.parts.map((part, partIndex) => (
153
154
  <MessagePartView key={`${message.id}:${partIndex}`} part={part} />
154
155
  ))}
155
- {thinking ? <Thinking /> : null}
156
+ {indicator ? <Thinking indicator={indicator} /> : null}
157
+ {traceHref ? (
158
+ <a
159
+ className="agent-trace"
160
+ href={traceHref}
161
+ target="_blank"
162
+ rel="noreferrer"
163
+ >
164
+ View trace ↗
165
+ </a>
166
+ ) : null}
156
167
  </div>
157
168
  </div>
158
169
  )
@@ -170,27 +181,41 @@ function submitOnEnter(event: ReactKeyboardEvent<HTMLTextAreaElement>): void {
170
181
  event.currentTarget.form?.requestSubmit()
171
182
  }
172
183
 
173
- export function AgentClient({ agentId }: { agentId: string }): ReactNode {
174
- const { state, push, events, index, connection } = useSession()
175
- const { inbox } = state
184
+ export function AgentClient({
185
+ agentId,
186
+ initial,
187
+ }: {
188
+ agentId: string
189
+ initial: InitialSnapshot<AgentState>
190
+ }): ReactNode {
191
+ const agent = useAgent({ client: agentClient, id: agentId, initial })
192
+ const { state, messages, indicator } = agent
193
+ const promptId = useId()
176
194
  const promptRef = useRef<HTMLTextAreaElement>(null)
177
195
  const [prompt, setPrompt] = useState('')
178
196
  const [error, setError] = useState<string | null>(null)
179
197
  const [responding, setResponding] = useState(false)
180
-
181
- const generating = state.status === 'generating'
182
- const compacting = state.compaction?.status === 'running'
198
+ const [stopCommandIds, setStopCommandIds] = useState<string[]>([])
183
199
  const canSend = state.status !== 'closed'
184
- const responseMessageId =
185
- state.activeGeneration?.responseMessageId ??
186
- state.activeResponseMessageId ??
187
- state.activeProjection?.responseMessageId
188
- const timeline = compactionTimeline({ state, events })
200
+ // The indicator joins the response it belongs to, or opens a row for it.
201
+ const thinking =
202
+ indicator === 'starting' || indicator === 'thinking' ? indicator : null
203
+ const last = messages.at(-1)
204
+ const joinsLast = thinking !== null && last?.role === 'assistant'
189
205
 
190
206
  useEffect(() => {
191
207
  if (canSend) promptRef.current?.focus({ preventScroll: true })
192
208
  }, [canSend])
193
209
 
210
+ const run = async (action: () => Promise<unknown>): Promise<void> => {
211
+ setError(null)
212
+ try {
213
+ await action()
214
+ } catch (err) {
215
+ setError(err instanceof Error ? err.message : String(err))
216
+ }
217
+ }
218
+
194
219
  const send = async (event?: FormEvent, steer = false): Promise<void> => {
195
220
  event?.preventDefault()
196
221
  const submittedPrompt = prompt
@@ -200,77 +225,22 @@ export function AgentClient({ agentId }: { agentId: string }): ReactNode {
200
225
  promptRef.current?.focus({ preventScroll: true })
201
226
  setError(null)
202
227
  try {
203
- const message: UIMessage = {
204
- id: crypto.randomUUID(),
205
- role: 'user',
206
- parts: [{ type: 'text', text }],
207
- }
208
- await push(
209
- ...(steer && state.active
210
- ? inputs.steer({
211
- turnId: state.active.turnId,
212
- message,
213
- })
214
- : inputs.message(message)),
215
- )
228
+ await (steer ? agent.steer(text) : agent.sendMessage(text))
216
229
  } catch (err) {
217
230
  setPrompt((current) => (current === '' ? submittedPrompt : current))
218
231
  setError(err instanceof Error ? err.message : String(err))
219
232
  }
220
233
  }
221
234
 
222
- const answerApproval = async ({
223
- approval,
224
- approved,
225
- }: {
226
- approval: PendingApproval
227
- approved: boolean
228
- }): Promise<void> => {
229
- setResponding(true)
230
- setError(null)
231
- try {
232
- await push(
233
- ...inputs.approval({
234
- requestId: approval.requestId,
235
- messageId: approval.messageId,
236
- generationId: approval.generationId,
237
- approvalId: approval.approvalId,
238
- approved,
239
- ...(!approved ? { reason: 'operator denied the restart' } : {}),
240
- }),
241
- )
242
- } catch (err) {
243
- setError(err instanceof Error ? err.message : String(err))
244
- } finally {
245
- setResponding(false)
246
- }
247
- }
248
-
249
- const command = async (
250
- commands: {
251
- type: 'ai.control.requested'
252
- payload: ControlCommand
253
- id?: string
254
- }[],
255
- ): Promise<void> => {
256
- setError(null)
257
- try {
258
- await push(...commands)
259
- } catch (err) {
260
- setError(err instanceof Error ? err.message : String(err))
261
- }
235
+ const stop = async (): Promise<void> => {
236
+ const stops = await agent.stop()
237
+ setStopCommandIds((ids) => [...ids, ...stops.map((event) => event.id)])
262
238
  }
263
239
 
264
- const stop = async (): Promise<void> => {
265
- if (!state.active) return
266
- setError(null)
267
- try {
268
- await push(
269
- ...inputs.stop({ turnId: state.active.turnId, lastSeenIndex: index }),
270
- )
271
- } catch (err) {
272
- setError(err instanceof Error ? err.message : String(err))
273
- }
240
+ const answer = async (action: () => Promise<unknown>): Promise<void> => {
241
+ setResponding(true)
242
+ await run(action)
243
+ setResponding(false)
274
244
  }
275
245
 
276
246
  return (
@@ -279,62 +249,44 @@ export function AgentClient({ agentId }: { agentId: string }): ReactNode {
279
249
  <h1>
280
250
  Agent <code>{agentId}</code>
281
251
  </h1>
282
- <ConnectionPill connection={connection} />
252
+ <ConnectionPill connection={agent.connection} />
283
253
  <span
284
- className={`badge${generating ? ' live' : ''}${
285
- state.status === 'waiting' ? ' waiting' : ''
286
- }${state.status === 'failed' ? ' cut' : ''}`}
254
+ className={`badge${agent.status === 'generating' ? ' live' : ''}${
255
+ agent.status === 'waiting' ? ' waiting' : ''
256
+ }${agent.status === 'failed' ? ' cut' : ''}`}
287
257
  >
288
- {state.starting
258
+ {indicator === 'starting'
289
259
  ? 'starting…'
290
- : compacting
260
+ : indicator === 'compacting'
291
261
  ? 'compacting…'
292
- : STATUS_LABEL[state.status]}
262
+ : STATUS_LABEL[agent.status]}
293
263
  </span>
294
264
  </header>
295
265
 
296
- <CompactionPanel agentId={agentId} state={state} events={events} />
266
+ <CompactionPanel key={agentId} session={agent} />
297
267
 
298
268
  <section className="agent-conversation" aria-label="Conversation">
299
269
  <div className="agent-transcript" aria-live="polite">
300
- {timeline.length === 0 ? (
270
+ {messages.length === 0 && thinking === null ? (
301
271
  <p className="agent-empty">
302
272
  Ask the agent to investigate an incident, run a Bash command, or
303
273
  set a reminder for itself. Its tools and decisions will appear
304
274
  here as they enter the log.
305
275
  </p>
306
276
  ) : (
307
- timeline.map((entry) =>
308
- entry.type === 'compaction' ? (
309
- <CompactionEvent key={entry.id} entry={entry} />
310
- ) : (
311
- <AgentMessage
312
- key={entry.id}
313
- message={entry.message}
314
- thinking={
315
- generating &&
316
- !state.starting &&
317
- !compacting &&
318
- entry.lastSegment &&
319
- entry.message.id ===
320
- state.activeGeneration?.responseMessageId &&
321
- !hasVisibleParts(entry.message)
322
- }
323
- />
324
- ),
325
- )
277
+ messages.map((message) => (
278
+ <AgentMessage
279
+ key={message.id}
280
+ message={message}
281
+ indicator={joinsLast && message === last ? thinking : null}
282
+ />
283
+ ))
326
284
  )}
327
- {state.starting ||
328
- (generating &&
329
- !compacting &&
330
- state.messages.at(-1)?.role === 'user' &&
331
- !state.messages.some(
332
- (message) => message.id === responseMessageId,
333
- )) ? (
285
+ {thinking !== null && !joinsLast ? (
334
286
  <div className="agent-message assistant">
335
287
  <span className="agent-role">Agent</span>
336
288
  <div>
337
- <Thinking starting={state.starting} />
289
+ <Thinking indicator={thinking} />
338
290
  </div>
339
291
  </div>
340
292
  ) : null}
@@ -354,9 +306,7 @@ export function AgentClient({ agentId }: { agentId: string }): ReactNode {
354
306
  <button
355
307
  type="button"
356
308
  disabled={responding}
357
- onClick={() =>
358
- void answerApproval({ approval, approved: true })
359
- }
309
+ onClick={() => void answer(() => agent.approve(approval))}
360
310
  >
361
311
  Approve restart
362
312
  </button>
@@ -365,7 +315,11 @@ export function AgentClient({ agentId }: { agentId: string }): ReactNode {
365
315
  className="secondary"
366
316
  disabled={responding}
367
317
  onClick={() =>
368
- void answerApproval({ approval, approved: false })
318
+ void answer(() =>
319
+ agent.deny(approval, {
320
+ reason: 'operator denied the restart',
321
+ }),
322
+ )
369
323
  }
370
324
  >
371
325
  Deny
@@ -381,45 +335,52 @@ export function AgentClient({ agentId }: { agentId: string }): ReactNode {
381
335
  ) : null}
382
336
 
383
337
  <AgentQueue
384
- items={inbox.items}
385
- paused={inbox.paused}
386
- remove={async (inputId) => {
387
- await command(inputs.queue.remove({ inputId }))
388
- }}
389
- sendNow={async (inputId) => {
390
- await command(
391
- inputs.queue.sendNow({
392
- inputId,
393
- turnId: state.active?.turnId ?? null,
394
- }),
395
- )
396
- }}
338
+ items={state.inbox.items}
339
+ paused={state.inbox.paused}
340
+ remove={(inputId) => run(() => agent.queue.remove(inputId))}
341
+ sendNow={(inputId) => run(() => agent.queue.sendNow(inputId))}
397
342
  edit={async (message) => {
398
- await push(...inputs.queue.edit({ message }))
343
+ await agent.queue.edit(message)
399
344
  }}
400
345
  move={(inputId, beforeId) =>
401
- void command(inputs.queue.move({ inputId, beforeId }))
346
+ void run(() => agent.queue.move({ inputId, beforeId }))
402
347
  }
403
348
  />
404
- {state.receipt?.outcome === 'rejected' ? (
405
- <p className="error" role="alert">
406
- Command rejected: {state.receipt.reason}. Refresh the input or turn
407
- you want to change.
408
- </p>
409
- ) : null}
349
+ <CommandReceipt
350
+ receipt={state.receipt}
351
+ stopCommandIds={stopCommandIds}
352
+ />
410
353
 
411
354
  <form className="agent-form" onSubmit={(event) => void send(event)}>
412
- <label>
413
- Message
414
- <textarea
415
- ref={promptRef}
416
- rows={3}
417
- value={prompt}
418
- onChange={(event) => setPrompt(event.target.value)}
419
- onKeyDown={submitOnEnter}
420
- disabled={!canSend}
421
- />
422
- </label>
355
+ <div className="agent-message-field">
356
+ <label htmlFor={promptId}>Message</label>
357
+ <div className="agent-composer">
358
+ <textarea
359
+ id={promptId}
360
+ ref={promptRef}
361
+ rows={3}
362
+ placeholder="Message the agent…"
363
+ value={prompt}
364
+ onChange={(event) => setPrompt(event.target.value)}
365
+ onKeyDown={submitOnEnter}
366
+ disabled={!canSend}
367
+ />
368
+ <div className="agent-composer-toolbar">
369
+ <ModelInput
370
+ value={state.modelChoice}
371
+ onChange={(next) =>
372
+ void run(() =>
373
+ agent.push({
374
+ type: 'model.choice.updated',
375
+ payload: next,
376
+ }),
377
+ )
378
+ }
379
+ disabled={!canSend}
380
+ />
381
+ </div>
382
+ </div>
383
+ </div>
423
384
  <div className="agent-form-actions">
424
385
  <button type="submit" disabled={!canSend || !prompt.trim()}>
425
386
  Send to agent
@@ -428,12 +389,13 @@ export function AgentClient({ agentId }: { agentId: string }): ReactNode {
428
389
  type="button"
429
390
  className="danger"
430
391
  disabled={!state.active}
431
- onClick={() => void stop()}
392
+ onClick={() => void run(stop)}
432
393
  >
433
394
  Stop
434
395
  </button>
435
396
  <button
436
397
  type="button"
398
+ className="secondary"
437
399
  disabled={!canSend || !prompt.trim()}
438
400
  onClick={() => void send(undefined, true)}
439
401
  >
@@ -442,7 +404,8 @@ export function AgentClient({ agentId }: { agentId: string }): ReactNode {
442
404
  {state.inbox.paused ? (
443
405
  <button
444
406
  type="button"
445
- onClick={() => void command(inputs.resume())}
407
+ className="secondary"
408
+ onClick={() => void run(agent.resume)}
446
409
  >
447
410
  Resume
448
411
  </button>
@@ -450,15 +413,17 @@ export function AgentClient({ agentId }: { agentId: string }): ReactNode {
450
413
  <>
451
414
  <button
452
415
  type="button"
416
+ className="secondary"
453
417
  onClick={() =>
454
- void command(inputs.pause({ when: 'after-turn' }))
418
+ void run(() => agent.pause({ when: 'after-turn' }))
455
419
  }
456
420
  >
457
421
  Pause after turn
458
422
  </button>
459
423
  <button
460
424
  type="button"
461
- onClick={() => void command(inputs.pause({ when: 'now' }))}
425
+ className="secondary"
426
+ onClick={() => void run(() => agent.pause({ when: 'now' }))}
462
427
  >
463
428
  Pause now
464
429
  </button>
@@ -469,12 +434,17 @@ export function AgentClient({ agentId }: { agentId: string }): ReactNode {
469
434
  </section>
470
435
  {error ? <p className="error">{error}</p> : null}
471
436
  <p className="hint">
472
- The browser only appends inputs. <code>createAgentServer()</code>{' '}
473
- records the generation, progress snapshots, tool activity, and pause;
474
- approving or delivering a reminder wakes the same durable session.
437
+ The browser only appends inputs. <code>agent()</code> records the
438
+ generation, progress snapshots, tool activity, and pause; approving or
439
+ delivering a reminder wakes the same durable session.
475
440
  </p>
476
441
 
477
- <ActivityFeed events={events} index={index} />
442
+ <ActivityFeed
443
+ events={agent.events}
444
+ index={agent.index}
445
+ history={agent.history}
446
+ loadHistory={agent.loadHistory}
447
+ />
478
448
  </article>
479
449
  )
480
450
  }
@@ -0,0 +1,65 @@
1
+ // @vitest-environment jsdom
2
+ import { cleanup, render, screen } from '@testing-library/react'
3
+ import { afterEach, expect, it } from 'vitest'
4
+ import { CommandReceipt } from './command-receipt'
5
+
6
+ afterEach(cleanup)
7
+
8
+ it('reports a Stop that arrived after its turn finished as a status', () => {
9
+ render(
10
+ <CommandReceipt
11
+ receipt={{
12
+ commandId: 'stop-1',
13
+ outcome: 'rejected',
14
+ reason: 'stale-turn',
15
+ }}
16
+ stopCommandIds={['stop-1']}
17
+ />,
18
+ )
19
+ expect(screen.getByRole('status').textContent).toBe(
20
+ 'That turn had already finished.',
21
+ )
22
+ expect(screen.queryByRole('alert')).toBeNull()
23
+ })
24
+
25
+ it('keeps other rejections as alerts', () => {
26
+ render(
27
+ <CommandReceipt
28
+ receipt={{
29
+ commandId: 'send-now-1',
30
+ outcome: 'rejected',
31
+ reason: 'stale-turn',
32
+ }}
33
+ stopCommandIds={['stop-1']}
34
+ />,
35
+ )
36
+ expect(screen.getByRole('alert').textContent).toContain(
37
+ 'Command rejected: stale-turn.',
38
+ )
39
+ })
40
+
41
+ it('renders nothing for applied commands', () => {
42
+ const { container } = render(
43
+ <CommandReceipt
44
+ receipt={{ commandId: 'stop-1', outcome: 'applied' }}
45
+ stopCommandIds={['stop-1']}
46
+ />,
47
+ )
48
+ expect(container.textContent).toBe('')
49
+ })
50
+
51
+ it('reports an earlier Stop after a later Stop was issued', () => {
52
+ render(
53
+ <CommandReceipt
54
+ receipt={{
55
+ commandId: 'stop-1',
56
+ outcome: 'rejected',
57
+ reason: 'stale-turn',
58
+ }}
59
+ stopCommandIds={['stop-1', 'stop-2']}
60
+ />,
61
+ )
62
+ expect(screen.getByRole('status').textContent).toBe(
63
+ 'That turn had already finished.',
64
+ )
65
+ })
@@ -0,0 +1,27 @@
1
+ import type { ControlReceipt } from 'experimental-a2/ai'
2
+
3
+ export function CommandReceipt({
4
+ receipt,
5
+ stopCommandIds,
6
+ }: {
7
+ receipt: ControlReceipt | null
8
+ stopCommandIds: readonly string[]
9
+ }) {
10
+ if (receipt?.outcome !== 'rejected') return null
11
+ if (
12
+ receipt.reason === 'stale-turn' &&
13
+ stopCommandIds.includes(receipt.commandId)
14
+ ) {
15
+ return (
16
+ <p className="hint" role="status">
17
+ That turn had already finished.
18
+ </p>
19
+ )
20
+ }
21
+ return (
22
+ <p className="error" role="alert">
23
+ Command rejected: {receipt.reason}. Refresh the input or turn you want to
24
+ change.
25
+ </p>
26
+ )
27
+ }
@@ -1,38 +1,39 @@
1
1
  import type { ReactNode } from 'react'
2
- import { formatActivityTime } from '@/app/components/activity-feed'
3
- import type { CompactionEntry } from '../compaction-timeline'
2
+ import type { CompactionPartData } from 'experimental-a2/ai'
3
+
4
+ const number = new Intl.NumberFormat('en-US')
4
5
 
5
6
  export function CompactionEvent({
6
- entry,
7
+ data,
7
8
  }: {
8
- entry: CompactionEntry
9
+ data: CompactionPartData
9
10
  }): ReactNode {
10
- const { event, running, outcome } = entry
11
- const completed = event.type === 'ai.compaction.completed'
12
- const label = completed
13
- ? 'Context compacted'
14
- : running
15
- ? 'Compacting…'
16
- : 'Compaction did not complete'
11
+ const { status, summary, inputTokens, thresholdTokens } = data
12
+ const running = status === 'running'
13
+ const label =
14
+ status === 'completed'
15
+ ? 'Context compacted'
16
+ : running
17
+ ? 'Compacting…'
18
+ : 'Compaction did not complete'
19
+ const hint = [
20
+ inputTokens === undefined || thresholdTokens === undefined
21
+ ? undefined
22
+ : `${number.format(inputTokens)} tokens against a ${number.format(thresholdTokens)} token threshold`,
23
+ status === 'failed' || status === 'interrupted'
24
+ ? `Generation ${status}`
25
+ : undefined,
26
+ ]
27
+ .filter((line) => line !== undefined)
28
+ .join(' · ')
17
29
 
18
30
  return (
19
31
  <details className="agent-compaction-event">
20
32
  <summary>
21
33
  <span role={running ? 'status' : undefined}>{label}</span>
22
- <time dateTime={event.createdAt.toISOString()}>
23
- {formatActivityTime(event.createdAt)}
24
- </time>
25
34
  </summary>
26
- <p className="hint">
27
- Event #{event.index} ·{' '}
28
- {event.payload.throughIndex === undefined
29
- ? `Through message ${event.payload.throughMessageId}`
30
- : `Summarized through event #${event.payload.throughIndex}`}
31
- {outcome === undefined ? null : ` · Generation ${outcome}`}
32
- </p>
33
- {completed && event.payload.summary ? (
34
- <pre>{event.payload.summary}</pre>
35
- ) : null}
35
+ {hint ? <p className="hint">{hint}</p> : null}
36
+ {summary ? <pre>{summary}</pre> : null}
36
37
  </details>
37
38
  )
38
39
  }