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
@@ -1,10 +1,10 @@
1
- import { t as A2Error } from "./errors-DCk6ch5n.js";
2
- import { n as pollingStream } from "./store-polling-CmxUbV93.js";
3
- import { t as idempotentReplay } from "./idempotent-replay-DVOlyYbx.js";
1
+ import { i as A2Error, r as sessionClearedError, s as supersededAttemptError, t as CLEAR_EVENT_TYPE } from "./session-clear-DGI4jTQ5.js";
2
+ import { n as pollingStream } from "./store-polling-B5FcwJfo.js";
3
+ import { t as idempotentReplay } from "./idempotent-replay-JdgMQRRm.js";
4
4
  import { n as SYSTEM_CLOCK, t as RANDOM_IDS } from "./store-N8PXxDAS.js";
5
5
  import { t as decodeReturnedEventIds } from "./store-codec-DTG0Ftek.js";
6
- import { mkdirSync } from "node:fs";
7
6
  import { dirname, resolve } from "node:path";
7
+ import { mkdirSync } from "node:fs";
8
8
  import { DatabaseSync } from "node:sqlite";
9
9
  //#region src/store-sqlite.ts
10
10
  /**
@@ -88,6 +88,11 @@ create table if not exists a2_snapshot_pins (
88
88
  create index if not exists a2_snapshot_pins_checkpoint
89
89
  on a2_snapshot_pins (session_id, reducer_name, up_to_index);
90
90
 
91
+ create table if not exists a2_clears (
92
+ session_id text not null primary key,
93
+ clear_index integer not null
94
+ ) strict;
95
+
91
96
  create table if not exists a2_presence (
92
97
  ns text not null,
93
98
  participant text not null,
@@ -166,6 +171,7 @@ const toEvent = (row) => ({
166
171
  sessionId: row.session_id,
167
172
  createdAt: new Date(Number(row.created_at))
168
173
  });
174
+ const RECLAIM_BATCH = 256;
169
175
  function sqlite(options = {}) {
170
176
  const path = options.path ?? ".a2/dev.db";
171
177
  const clock = options.clock ?? SYSTEM_CLOCK;
@@ -188,6 +194,7 @@ function sqlite(options = {}) {
188
194
  when not exists (
189
195
  select 1 from a2_events
190
196
  where session_id = ? and lane = ? and processed_at is null
197
+ and idx > ?
191
198
  ) then 1
192
199
  else 0
193
200
  end,
@@ -197,6 +204,7 @@ function sqlite(options = {}) {
197
204
  and idx = (
198
205
  select idx from a2_events
199
206
  where session_id = ? and lane = ? and processed_at is null
207
+ and idx > ?
200
208
  order by idx limit 1
201
209
  )`);
202
210
  const appendStatus = db.prepare(`select coalesce(
@@ -205,7 +213,7 @@ function sqlite(options = {}) {
205
213
  ) as max,
206
214
  exists(
207
215
  select 1 from a2_events
208
- where session_id = ? and processed_at is null
216
+ where session_id = ? and processed_at is null and idx > ?
209
217
  ) as has_pending`);
210
218
  const upsertPresence = db.prepare(`insert into a2_presence (ns, participant, field, value, seen, at, expires_at)
211
219
  values (?, ?, ?, ?, ?, ?, ?)
@@ -227,10 +235,34 @@ function sqlite(options = {}) {
227
235
  select 1 from a2_events pending
228
236
  where pending.session_id = ?
229
237
  and pending.processed_at is null
238
+ and pending.idx > ?
230
239
  ) as session_has_pending
231
240
  from a2_events stored
232
241
  where stored.event_id in (${Array.from({ length: count }, () => "?").join(", ")})
233
242
  order by stored.idx`);
243
+ const selectClearIndex = db.prepare("select clear_index from a2_clears where session_id = ?");
244
+ const clearIndexOf = (sessionId) => Number(selectClearIndex.get(sessionId)?.clear_index ?? 0);
245
+ const deleteEventById = db.prepare("delete from a2_events where session_id = ? and event_id = ?");
246
+ /**
247
+ * The rows owning `ids`. This session's rows below its clear marker do
248
+ * not own their ids: they are deleted here so the ids can be reused.
249
+ */
250
+ const liveOwnersOf = (sessionId, clearIndex, ids) => {
251
+ if (ids.length === 0) return [];
252
+ return selectByIds(ids.length).all(sessionId, clearIndex, ...ids).filter((row) => {
253
+ if (row.session_id !== sessionId || Number(row.idx) >= clearIndex) return true;
254
+ deleteEventById.run(sessionId, row.event_id);
255
+ return false;
256
+ });
257
+ };
258
+ const selectCauseParent = db.prepare("select attempt_count, failed_at from a2_events where session_id = ? and idx = ?");
259
+ /** The attempt-currency fence shared by handler appends and clears. */
260
+ const assertCurrentCause = ({ sessionId, cause, clearIndex }) => {
261
+ if (cause.index <= clearIndex) throw supersededAttemptError(sessionId, cause);
262
+ const parent = selectCauseParent.get(sessionId, cause.index);
263
+ if (!parent) throw new TypeError(`no event at index ${cause.index} in session '${sessionId}'`);
264
+ if (Number(parent.attempt_count) !== cause.attempt || parent.failed_at !== null) throw supersededAttemptError(sessionId, cause);
265
+ };
234
266
  const tx = (fn) => {
235
267
  db.exec("begin immediate");
236
268
  try {
@@ -245,19 +277,20 @@ function sqlite(options = {}) {
245
277
  }
246
278
  };
247
279
  return {
248
- async append(sessionId, events) {
280
+ async append(sessionId, events, appendOptions) {
249
281
  if (events.length === 0) return wrap(() => {
250
- const status = appendStatus.get(sessionId, sessionId);
282
+ const status = appendStatus.get(sessionId, sessionId, clearIndexOf(sessionId));
251
283
  return {
252
284
  events: [],
253
285
  hasPending: Number(status.has_pending) === 1
254
286
  };
255
287
  });
256
288
  return wrap(() => tx(() => {
289
+ const clearIndex = clearIndexOf(sessionId);
257
290
  const suppliedIds = events.filter((e) => e.id !== void 0).map((e) => e.id);
258
291
  if (new Set(suppliedIds).size !== suppliedIds.length) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "batch contains the same event id more than once");
259
292
  if (suppliedIds.length > 0) {
260
- const existing = selectByIds(suppliedIds.length).all(sessionId, ...suppliedIds);
293
+ const existing = liveOwnersOf(sessionId, clearIndex, suppliedIds);
261
294
  if (existing.length > 0) {
262
295
  const foreign = existing.find((row) => row.session_id !== sessionId);
263
296
  if (foreign) throw new A2Error("PARTIAL_DUPLICATE_BATCH", `event id '${foreign.event_id}' already exists in another session`);
@@ -268,20 +301,21 @@ function sqlite(options = {}) {
268
301
  throw new A2Error("PARTIAL_DUPLICATE_BATCH", `batch mixes ${existing.length} already-appended and ${events.length - existing.length} fresh events`);
269
302
  }
270
303
  }
271
- for (const e of events) {
272
- if (!e.cause) continue;
273
- const parent = db.prepare("select attempt_count, failed_at from a2_events where session_id = ? and idx = ?").get(sessionId, e.cause.index);
274
- if (!parent) throw new TypeError(`no event at index ${e.cause.index} in session '${sessionId}'`);
275
- if (Number(parent.attempt_count) !== e.cause.attempt || parent.failed_at !== null) throw new A2Error("SUPERSEDED_ATTEMPT", `attempt ${e.cause.attempt} no longer owns event ${e.cause.index} in session '${sessionId}'`);
276
- }
277
- const status = appendStatus.get(sessionId, sessionId);
304
+ const originIndex = appendOptions?.originIndex;
305
+ if (originIndex !== void 0 && originIndex <= clearIndex) throw sessionClearedError(`scheduled work from event ${originIndex} was cleared in session '${sessionId}'`);
306
+ for (const e of events) if (e.cause) assertCurrentCause({
307
+ sessionId,
308
+ cause: e.cause,
309
+ clearIndex
310
+ });
311
+ const status = appendStatus.get(sessionId, sessionId, clearIndex);
278
312
  const base = Number(status.max);
279
313
  const now = clock.now().getTime();
280
314
  return {
281
315
  events: events.map((e, i) => {
282
316
  const id = e.id ?? generateId();
283
317
  const index = base + 1 + i;
284
- insertEvent.run(sessionId, index, e.type, JSON.stringify(e.payload) ?? "null", id, now, e.cause ? JSON.stringify(e.cause) : null, e.lane ?? null, e.lane ?? null, sessionId, e.lane ?? null, e.settled ? now : null);
318
+ insertEvent.run(sessionId, index, e.type, JSON.stringify(e.payload) ?? "null", id, now, e.cause ? JSON.stringify(e.cause) : null, e.lane ?? null, e.lane ?? null, sessionId, e.lane ?? null, clearIndex, e.settled ? now : null);
285
319
  return {
286
320
  id,
287
321
  type: e.type,
@@ -312,8 +346,8 @@ function sqlite(options = {}) {
312
346
  },
313
347
  async read(sessionId, opts) {
314
348
  return wrap(() => {
315
- const conditions = ["session_id = ?"];
316
- const params = [sessionId];
349
+ const conditions = ["session_id = ?", "idx >= coalesce((select clear_index from a2_clears where session_id = ?), 0)"];
350
+ const params = [sessionId, sessionId];
317
351
  if (opts?.afterIndex !== void 0) {
318
352
  conditions.push("idx > ?");
319
353
  params.push(opts.afterIndex);
@@ -329,6 +363,7 @@ function sqlite(options = {}) {
329
363
  return wrap(() => tx(() => {
330
364
  const now = clock.now().getTime();
331
365
  const expiresAt = expiresAtMs ?? now + ttlMs;
366
+ const clearIndex = clearIndexOf(sessionId);
332
367
  const exclusion = excludeIndexes.length === 0 ? "" : `and event.idx not in (${excludeIndexes.map(() => "?").join(", ")})`;
333
368
  const eligible = db.prepare(`select event.idx
334
369
  from a2_events event
@@ -336,9 +371,10 @@ function sqlite(options = {}) {
336
371
  and event.processed_at is null
337
372
  and event.failed_at is null
338
373
  and event.lane_ready = 1
374
+ and event.idx > ?
339
375
  and (event.claim_expires_at is null or event.claim_expires_at <= ?)
340
376
  ${exclusion}
341
- order by event.idx`).all(sessionId, now, ...excludeIndexes);
377
+ order by event.idx`).all(sessionId, clearIndex, now, ...excludeIndexes);
342
378
  if (eligible.length > 0) {
343
379
  const update = db.prepare(`update a2_events set
344
380
  attempt_count = attempt_count + 1,
@@ -352,7 +388,9 @@ function sqlite(options = {}) {
352
388
  outcome: "claimed",
353
389
  events: eligible.map((row) => {
354
390
  const claimed = update.get(now, now, holder, expiresAt, sessionId, row.idx);
355
- return toStored(claimed);
391
+ const stored = toStored(claimed);
392
+ if (clearIndex > 0) stored.clearIndex = clearIndex;
393
+ return stored;
356
394
  })
357
395
  };
358
396
  }
@@ -362,7 +400,8 @@ function sqlite(options = {}) {
362
400
  and processed_at is null
363
401
  and failed_at is null
364
402
  and lane_ready = 1
365
- and claim_expires_at > ?`).get(sessionId, now);
403
+ and idx > ?
404
+ and claim_expires_at > ?`).get(sessionId, clearIndex, now);
366
405
  return active.retry_at === null ? { outcome: "settled" } : {
367
406
  outcome: "busy",
368
407
  retryAt: new Date(Number(active.retry_at))
@@ -388,7 +427,12 @@ function sqlite(options = {}) {
388
427
  and failed_at is null
389
428
  and claim_holder = ?
390
429
  and claim_expires_at > ?`);
430
+ const clearIndex = clearIndexOf(sessionId);
391
431
  for (const claim of claims) {
432
+ if (claim.index <= clearIndex) {
433
+ superseded.push(claim.index);
434
+ continue;
435
+ }
392
436
  const row = currentAttempt.get(sessionId, claim.index);
393
437
  if (!row) continue;
394
438
  if (Number(row.attempt_count) > claim.attempt) {
@@ -406,6 +450,8 @@ function sqlite(options = {}) {
406
450
  },
407
451
  async completeAttempt({ sessionId, index, attempt, events }) {
408
452
  return wrap(() => tx(() => {
453
+ const clearIndex = clearIndexOf(sessionId);
454
+ if (index <= clearIndex) return { outcome: "superseded" };
409
455
  const parent = db.prepare("select * from a2_events where session_id = ? and idx = ?").get(sessionId, index);
410
456
  if (!parent) throw new TypeError(`no event at index ${index} in session '${sessionId}'`);
411
457
  const ids = events.map((event) => event.id);
@@ -414,7 +460,7 @@ function sqlite(options = {}) {
414
460
  if (Number(parent.processed_by_attempt) !== attempt) return { outcome: "superseded" };
415
461
  const returnedIds = parent.returned_event_ids === null ? null : decodeReturnedEventIds(parent.returned_event_ids);
416
462
  if (returnedIds === null || returnedIds.length !== ids.length || returnedIds.some((id, offset) => id !== ids[offset])) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "completed attempt does not match the returned event batch");
417
- const existing = ids.length === 0 ? [] : selectByIds(ids.length).all(sessionId, ...ids);
463
+ const existing = ids.length === 0 ? [] : selectByIds(ids.length).all(sessionId, clearIndex, ...ids);
418
464
  const byId = new Map(existing.map((row) => [row.event_id, row]));
419
465
  const ordered = ids.map((id) => byId.get(id));
420
466
  if (ordered.some((row) => !row || row.session_id !== sessionId || row.cause === null || toCause(row.cause)?.index !== index || toCause(row.cause)?.attempt !== attempt)) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "returned event retry does not match the committed batch");
@@ -424,8 +470,8 @@ function sqlite(options = {}) {
424
470
  };
425
471
  }
426
472
  if (Number(parent.attempt_count) !== attempt || parent.claim_holder === null || parent.failed_at !== null) return { outcome: "superseded" };
427
- if (ids.length > 0 && selectByIds(ids.length).all(sessionId, ...ids).length > 0) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "returned event batch contains an already-appended event id");
428
- const status = appendStatus.get(sessionId, sessionId);
473
+ if (liveOwnersOf(sessionId, clearIndex, ids).length > 0) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "returned event batch contains an already-appended event id");
474
+ const status = appendStatus.get(sessionId, sessionId, clearIndex);
429
475
  const now = clock.now().getTime();
430
476
  db.prepare(`update a2_events set
431
477
  processed_at = ?,
@@ -444,7 +490,7 @@ function sqlite(options = {}) {
444
490
  and pin.reducer_name = a2_snapshot_history.reducer_name
445
491
  and pin.up_to_index = a2_snapshot_history.up_to_index
446
492
  )`).run(sessionId);
447
- if (parent.lane !== null) promoteLaneHead.run(sessionId, sessionId, parent.lane);
493
+ if (parent.lane !== null) promoteLaneHead.run(sessionId, sessionId, parent.lane, clearIndex);
448
494
  return {
449
495
  outcome: "completed",
450
496
  events: events.map((event, offset) => {
@@ -454,7 +500,7 @@ function sqlite(options = {}) {
454
500
  index,
455
501
  attempt
456
502
  };
457
- insertEvent.run(sessionId, childIndex, event.type, JSON.stringify(event.payload) ?? "null", id, now, JSON.stringify(cause), event.lane ?? null, event.lane ?? null, sessionId, event.lane ?? null, event.settled ? now : null);
503
+ insertEvent.run(sessionId, childIndex, event.type, JSON.stringify(event.payload) ?? "null", id, now, JSON.stringify(cause), event.lane ?? null, event.lane ?? null, sessionId, event.lane ?? null, clearIndex, event.settled ? now : null);
458
504
  return toStored(db.prepare("select * from a2_events where session_id = ? and idx = ?").get(sessionId, childIndex));
459
505
  })
460
506
  };
@@ -462,6 +508,10 @@ function sqlite(options = {}) {
462
508
  },
463
509
  async failAttempt({ sessionId, index, attempt, error, maxFailures }) {
464
510
  return wrap(() => tx(() => {
511
+ if (index <= clearIndexOf(sessionId)) return {
512
+ outcome: "superseded",
513
+ failureCount: 0
514
+ };
465
515
  const current = db.prepare("select * from a2_events where session_id = ? and idx = ?").get(sessionId, index);
466
516
  if (!current) throw new TypeError(`no event at index ${index} in session '${sessionId}'`);
467
517
  const failureCount = Number(current.failure_count);
@@ -510,14 +560,49 @@ function sqlite(options = {}) {
510
560
  };
511
561
  }));
512
562
  },
563
+ async deadLetters(sessionId) {
564
+ return wrap(() => {
565
+ return db.prepare(`select * from a2_events
566
+ where session_id = ?
567
+ and processed_at is null
568
+ and failed_at is not null
569
+ and idx > coalesce(
570
+ (select clear_index from a2_clears where session_id = ?), 0
571
+ )
572
+ order by idx`).all(sessionId, sessionId).map(toStored);
573
+ });
574
+ },
575
+ async redrive({ sessionId, index, action }) {
576
+ return wrap(() => tx(() => {
577
+ const clearIndex = clearIndexOf(sessionId);
578
+ const row = index > clearIndex ? db.prepare("select lane, processed_at, failed_at from a2_events where session_id = ? and idx = ?").get(sessionId, index) : void 0;
579
+ const redriven = row !== void 0 && row.processed_at === null && row.failed_at !== null;
580
+ if (redriven && action === "skip") {
581
+ db.prepare(`update a2_events set processed_at = ?, lane_ready = 0
582
+ where session_id = ? and idx = ?`).run(clock.now().getTime(), sessionId, index);
583
+ if (row.lane !== null) promoteLaneHead.run(sessionId, sessionId, row.lane, clearIndex);
584
+ } else if (redriven) db.prepare(`update a2_events set failed_at = null, failure_count = 0
585
+ where session_id = ? and idx = ?`).run(sessionId, index);
586
+ const status = appendStatus.get(sessionId, sessionId, clearIndex);
587
+ return {
588
+ redriven,
589
+ hasPending: Number(status.has_pending) === 1
590
+ };
591
+ }));
592
+ },
513
593
  async readState(sessionId, reducerName, stateOptions) {
514
594
  return wrap(() => {
515
595
  const throughIndex = stateOptions?.throughIndex ?? null;
516
596
  const snapshotThroughIndex = stateOptions?.snapshotThroughIndex ?? throughIndex;
517
- const rows = db.prepare(`with head as materialized (
597
+ const rows = db.prepare(`with boundary as materialized (
598
+ select coalesce(
599
+ (select clear_index from a2_clears where session_id = ?), 0
600
+ ) as clear_index
601
+ ), head as materialized (
518
602
  select up_to_index, state, updated_at
519
603
  from a2_snapshots
520
604
  where session_id = ? and reducer_name = ?
605
+ and up_to_index >= (select clear_index from boundary)
521
606
  ), snapshot as materialized (
522
607
  select up_to_index, state from (
523
608
  select up_to_index, state from head
@@ -526,10 +611,12 @@ function sqlite(options = {}) {
526
611
  select up_to_index, state
527
612
  from a2_snapshot_history
528
613
  where session_id = ? and reducer_name = ?
614
+ and up_to_index >= (select clear_index from boundary)
529
615
  and (? is null or up_to_index <= ?)
530
616
  ) order by up_to_index desc limit 1
531
617
  )
532
618
  select 0 as row_order, 'snapshot' as row_kind,
619
+ (select clear_index from boundary) as clear_index,
533
620
  (select up_to_index from head) as head_index,
534
621
  (select up_to_index from snapshot) as snapshot_index,
535
622
  (select state from snapshot) as snapshot_state,
@@ -546,16 +633,22 @@ function sqlite(options = {}) {
546
633
  null as claim_expires_at
547
634
  union all
548
635
  select 1 as row_order, 'event' as row_kind,
636
+ null as clear_index,
549
637
  (select up_to_index from head) as head_index,
550
638
  null as snapshot_index, null as snapshot_state,
551
639
  event.*
552
640
  from a2_events as event
553
641
  where event.session_id = ?
554
- and event.idx > coalesce((select up_to_index from snapshot), 0)
642
+ and event.idx > max(
643
+ coalesce((select up_to_index from snapshot), 0),
644
+ (select clear_index from boundary) - 1
645
+ )
555
646
  and (? is null or event.idx <= ?)
556
- order by row_order, idx`).all(sessionId, reducerName, snapshotThroughIndex, snapshotThroughIndex, sessionId, reducerName, snapshotThroughIndex, snapshotThroughIndex, sessionId, throughIndex, throughIndex);
647
+ order by row_order, idx`).all(sessionId, sessionId, reducerName, snapshotThroughIndex, snapshotThroughIndex, sessionId, reducerName, snapshotThroughIndex, snapshotThroughIndex, sessionId, throughIndex, throughIndex);
557
648
  const snapshot = rows.find((row) => row.row_kind === "snapshot");
649
+ const clearIndex = Number(snapshot?.clear_index ?? 0);
558
650
  return {
651
+ ...clearIndex > 0 ? { clearIndex } : {},
559
652
  headIndex: snapshot?.head_index === null || snapshot?.head_index === void 0 ? null : Number(snapshot.head_index),
560
653
  snapshot: snapshot?.snapshot_index !== null && snapshot?.snapshot_index !== void 0 ? {
561
654
  index: Number(snapshot.snapshot_index),
@@ -570,7 +663,7 @@ function sqlite(options = {}) {
570
663
  const selectHead = db.prepare(`select up_to_index, state, updated_at from a2_snapshots
571
664
  where session_id = ? and reducer_name = ?`);
572
665
  const unfinished = db.prepare(`select 1 from a2_events
573
- where session_id = ? and idx = ?
666
+ where session_id = ? and idx = ? and idx > ?
574
667
  and processed_at is null and failed_at is null`);
575
668
  const insertPin = db.prepare(`insert or ignore into a2_snapshot_pins
576
669
  (session_id, event_index, reducer_name, up_to_index)
@@ -593,12 +686,14 @@ function sqlite(options = {}) {
593
686
  where excluded.up_to_index >= a2_snapshots.up_to_index`);
594
687
  const deleteHistory = db.prepare(`delete from a2_snapshot_history
595
688
  where session_id = ? and reducer_name = ? and up_to_index = ?`);
689
+ const clearIndex = clearIndexOf(sessionId);
596
690
  for (const write of writes.toSorted((a, b) => a.index - b.index)) {
597
- for (const eventIndex of write.pinEventIndexes ?? []) if (unfinished.get(sessionId, eventIndex)) insertPin.run(sessionId, eventIndex, reducerName, write.index);
691
+ if (write.index < clearIndex) continue;
692
+ for (const eventIndex of write.pinEventIndexes ?? []) if (unfinished.get(sessionId, eventIndex, clearIndex)) insertPin.run(sessionId, eventIndex, reducerName, write.index);
598
693
  const head = selectHead.get(sessionId, reducerName);
599
694
  const now = clock.now().getTime();
600
695
  if (!head || write.index >= Number(head.up_to_index)) {
601
- if (head && write.index > Number(head.up_to_index) && hasPin.get(sessionId, reducerName, Number(head.up_to_index))) putHistory.run(sessionId, reducerName, Number(head.up_to_index), head.state, Number(head.updated_at));
696
+ if (head && write.index > Number(head.up_to_index) && Number(head.up_to_index) >= clearIndex && hasPin.get(sessionId, reducerName, Number(head.up_to_index))) putHistory.run(sessionId, reducerName, Number(head.up_to_index), head.state, Number(head.updated_at));
602
697
  putHead.run(sessionId, reducerName, write.index, JSON.stringify(write.state) ?? "null", now);
603
698
  deleteHistory.run(sessionId, reducerName, write.index);
604
699
  } else if (write.index < Number(head.up_to_index) && hasPin.get(sessionId, reducerName, write.index)) putHistory.run(sessionId, reducerName, write.index, JSON.stringify(write.state) ?? "null", now);
@@ -633,10 +728,79 @@ function sqlite(options = {}) {
633
728
  },
634
729
  stream(sessionId, opts) {
635
730
  const readAfter = (afterIndex) => wrap(() => {
636
- return db.prepare("select * from a2_events where session_id = ? and idx > ? order by idx").all(sessionId, afterIndex).map(toStored);
731
+ return db.prepare(`select * from a2_events
732
+ where session_id = ? and idx > ?
733
+ and idx >= coalesce((select clear_index from a2_clears where session_id = ?), 0)
734
+ order by idx`).all(sessionId, afterIndex, sessionId).map(toStored);
637
735
  });
638
736
  return pollingStream(readAfter, opts?.startAfter !== void 0 ? { startAfter: opts.startAfter } : {});
639
737
  },
738
+ async clear({ sessionId, ifLastIndex, cause }) {
739
+ return wrap(() => tx(() => {
740
+ const clearIndex = clearIndexOf(sessionId);
741
+ if (cause) assertCurrentCause({
742
+ sessionId,
743
+ cause,
744
+ clearIndex
745
+ });
746
+ const head = Number(appendStatus.get(sessionId, sessionId, clearIndex).max);
747
+ if (ifLastIndex !== void 0 && clearIndex === ifLastIndex + 1) return {
748
+ cleared: true,
749
+ index: head,
750
+ clearIndex
751
+ };
752
+ if (ifLastIndex !== void 0 && ifLastIndex !== head) return {
753
+ cleared: false,
754
+ index: head,
755
+ clearIndex
756
+ };
757
+ if (head === 0 || clearIndex === head) return {
758
+ cleared: true,
759
+ index: head,
760
+ clearIndex
761
+ };
762
+ const marker = head + 1;
763
+ const now = clock.now().getTime();
764
+ insertEvent.run(sessionId, marker, CLEAR_EVENT_TYPE, "null", generateId(), now, null, null, null, sessionId, null, clearIndex, now);
765
+ db.prepare(`insert into a2_clears (session_id, clear_index) values (?, ?)
766
+ on conflict (session_id) do update set clear_index = excluded.clear_index`).run(sessionId, marker);
767
+ return {
768
+ cleared: true,
769
+ index: marker,
770
+ clearIndex: marker
771
+ };
772
+ }));
773
+ },
774
+ async reclaim(sessionId) {
775
+ return wrap(() => tx(() => {
776
+ const clearIndex = clearIndexOf(sessionId);
777
+ if (clearIndex === 0) return {
778
+ deleted: 0,
779
+ done: true
780
+ };
781
+ const last = db.prepare(`select max(idx) as last from (
782
+ select idx from a2_events
783
+ where session_id = ? and idx < ?
784
+ order by idx limit ?
785
+ )`).get(sessionId, clearIndex, RECLAIM_BATCH);
786
+ let deleted = 0;
787
+ if (last.last !== null) {
788
+ const through = Number(last.last);
789
+ deleted = Number(db.prepare("delete from a2_events where session_id = ? and idx <= ?").run(sessionId, through).changes);
790
+ db.prepare("delete from a2_snapshot_pins where session_id = ? and event_index <= ?").run(sessionId, through);
791
+ }
792
+ const done = db.prepare("select 1 from a2_events where session_id = ? and idx < ? limit 1").get(sessionId, clearIndex) === void 0;
793
+ if (done) for (const table of [
794
+ "a2_snapshot_pins",
795
+ "a2_snapshot_history",
796
+ "a2_snapshots"
797
+ ]) db.prepare(`delete from ${table} where session_id = ? and up_to_index < ?`).run(sessionId, clearIndex);
798
+ return {
799
+ deleted,
800
+ done
801
+ };
802
+ }));
803
+ },
640
804
  close() {
641
805
  db.close();
642
806
  }