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.
- package/CHANGELOG.md +373 -0
- package/README.md +1 -1
- package/dist/{actor-ohPC-81x.d.ts → actor-Bjce9_5U.d.ts} +21 -11
- package/dist/actor-Bjce9_5U.d.ts.map +1 -0
- package/dist/actor-client.d.ts +2 -5
- package/dist/actor-client.d.ts.map +1 -1
- package/dist/actor-client.js +13 -4
- package/dist/actor-client.js.map +1 -1
- package/dist/actor-react.d.ts +5 -3
- package/dist/actor-react.d.ts.map +1 -1
- package/dist/actor-react.js +20 -10
- package/dist/actor-react.js.map +1 -1
- package/dist/{actor-shared-B5tJfzt-.js → actor-shared-t7w8xIKP.js} +12 -7
- package/dist/actor-shared-t7w8xIKP.js.map +1 -0
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +42 -23
- package/dist/actor.js.map +1 -1
- package/dist/{ai-DgOBltJ_.js → ai-BpOGm-zB.js} +621 -216
- package/dist/ai-BpOGm-zB.js.map +1 -0
- package/dist/{ai-CrEf6p_W.d.ts → ai-DrUaMyRt.d.ts} +114 -100
- package/dist/ai-DrUaMyRt.d.ts.map +1 -0
- package/dist/ai-client.d.ts +27 -0
- package/dist/ai-client.d.ts.map +1 -0
- package/dist/ai-client.js +29 -0
- package/dist/ai-client.js.map +1 -0
- package/dist/ai-fx.browser.d.ts +1 -0
- package/dist/ai-fx.browser.js +6 -0
- package/dist/ai-fx.browser.js.map +1 -0
- package/dist/ai-fx.d.ts +10 -0
- package/dist/ai-fx.d.ts.map +1 -0
- package/dist/ai-fx.js +634 -0
- package/dist/ai-fx.js.map +1 -0
- package/dist/ai-harness-BuLh_EYi.js +273 -0
- package/dist/ai-harness-BuLh_EYi.js.map +1 -0
- package/dist/ai-harness-DvsPeJeB.d.ts +26 -0
- package/dist/ai-harness-DvsPeJeB.d.ts.map +1 -0
- package/dist/ai-pi.browser.d.ts +1 -0
- package/dist/ai-pi.browser.js +6 -0
- package/dist/ai-pi.browser.js.map +1 -0
- package/dist/ai-pi.d.ts +17 -0
- package/dist/ai-pi.d.ts.map +1 -0
- package/dist/ai-pi.js +904 -0
- package/dist/ai-pi.js.map +1 -0
- package/dist/ai-react.d.ts +96 -0
- package/dist/ai-react.d.ts.map +1 -0
- package/dist/ai-react.js +142 -0
- package/dist/ai-react.js.map +1 -0
- package/dist/ai-server.browser.js +2 -2
- package/dist/ai-server.browser.js.map +1 -1
- package/dist/ai-server.d.ts +109 -43
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +526 -283
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/bounded-cache-CPHWF-8h.js +41 -0
- package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
- package/dist/{client-BYzHjkwU.js → client-BReKqwZC.js} +447 -80
- package/dist/client-BReKqwZC.js.map +1 -0
- package/dist/{client-CzyacQpJ.d.ts → client-h_DKFAjj.d.ts} +14 -13
- package/dist/client-h_DKFAjj.d.ts.map +1 -0
- package/dist/client.d.ts +4 -2
- package/dist/client.js +1 -1
- package/dist/{contract-CKRg_E4q.js → contract-BYKASlCV.js} +12 -21
- package/dist/contract-BYKASlCV.js.map +1 -0
- package/dist/{idempotent-replay-DVOlyYbx.js → idempotent-replay-JdgMQRRm.js} +2 -2
- package/dist/{idempotent-replay-DVOlyYbx.js.map → idempotent-replay-JdgMQRRm.js.map} +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/{errors-DvhSXnxk.d.ts → initial-snapshot-Bx-c0pDP.d.ts} +7 -3
- package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +1 -0
- package/dist/{internal-Dq2qYxou.js → internal-B2T3U4Rq.js} +29 -2
- package/dist/internal-B2T3U4Rq.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/react-BBXgHWFc.d.ts +86 -0
- package/dist/react-BBXgHWFc.d.ts.map +1 -0
- package/dist/react.d.ts +2 -122
- package/dist/react.js +107 -100
- package/dist/react.js.map +1 -1
- package/dist/reducer-BAXc8JyF.js +87 -0
- package/dist/reducer-BAXc8JyF.js.map +1 -0
- package/dist/{reducer-otzHjuJj.d.ts → reducer-CqA59Q6U.d.ts} +46 -30
- package/dist/reducer-CqA59Q6U.d.ts.map +1 -0
- package/dist/reducer-runtime-DQwz_J59.js +28 -0
- package/dist/reducer-runtime-DQwz_J59.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +3 -3
- package/dist/scheduler-qstash.d.ts.map +1 -1
- package/dist/scheduler-qstash.js +33 -10
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/{scheduler-task-BpzhPnRS.js → scheduler-task-X1SToDjP.js} +18 -3
- package/dist/scheduler-task-X1SToDjP.js.map +1 -0
- package/dist/scheduler-vercel.d.ts +4 -9
- package/dist/scheduler-vercel.d.ts.map +1 -1
- package/dist/scheduler-vercel.js +25 -44
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-BD5ckxZb.js → server-Dw_k_7kO.js} +904 -219
- package/dist/server-Dw_k_7kO.js.map +1 -0
- package/dist/{server-DbD7IJVK.d.ts → server-En7cbi3v.d.ts} +116 -26
- package/dist/server-En7cbi3v.d.ts.map +1 -0
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/session-clear-Bo-XTFEy.d.ts +12 -0
- package/dist/session-clear-Bo-XTFEy.d.ts.map +1 -0
- package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
- package/dist/session-clear-DGI4jTQ5.js.map +1 -0
- package/dist/{store-DyZM6fS5.d.ts → store-Djdv0bnZ.d.ts} +73 -3
- package/dist/store-Djdv0bnZ.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +205 -66
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-CmxUbV93.js → store-polling-B5FcwJfo.js} +21 -4
- package/dist/store-polling-B5FcwJfo.js.map +1 -0
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +450 -108
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-BAwS8RWn.js} +3 -3
- package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-BAwS8RWn.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.d.ts.map +1 -1
- package/dist/store-redis-http.js +293 -11
- package/dist/store-redis-http.js.map +1 -1
- package/dist/store-redis-notify-BRITti_o.js +2493 -0
- package/dist/store-redis-notify-BRITti_o.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.d.ts.map +1 -1
- package/dist/store-redis.js +11 -11
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +197 -33
- package/dist/store-sqlite.js.map +1 -1
- package/dist/stream-buffer-Cx7AAPGq.js +23 -0
- package/dist/stream-buffer-Cx7AAPGq.js.map +1 -0
- package/dist/{telemetry-B5jzpy6y.d.ts → telemetry-Cyfqo6Qt.d.ts} +2 -2
- package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
- package/dist/{wire-BO5wWCb1.js → wire-rSUglYo8.js} +132 -10
- package/dist/wire-rSUglYo8.js.map +1 -0
- package/docs/01-quickstart.mdx +50 -84
- package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
- package/docs/actors/01-introduction.mdx +48 -82
- package/docs/actors/02-concurrency.mdx +43 -70
- package/docs/actors/03-timers.mdx +28 -42
- package/docs/actors/04-routes.mdx +110 -137
- package/docs/actors/meta.ts +1 -1
- package/docs/agents/01-introduction.mdx +257 -0
- package/docs/agents/02-tools.mdx +247 -0
- package/docs/agents/03-conversations.mdx +538 -0
- package/docs/agents/04-harnesses.mdx +136 -0
- package/docs/agents/meta.ts +1 -0
- package/docs/client/01-react.mdx +294 -0
- package/docs/client/02-transports.mdx +86 -0
- package/docs/client/03-presence.mdx +224 -0
- package/docs/client/meta.ts +1 -0
- package/docs/contract/01-events.mdx +57 -0
- package/docs/contract/02-state.mdx +142 -0
- package/docs/contract/03-sessions.mdx +175 -0
- package/docs/index.mdx +112 -156
- package/docs/reference/01-core.mdx +139 -0
- package/docs/reference/02-server.mdx +855 -0
- package/docs/reference/03-client.mdx +301 -0
- package/docs/reference/04-ai.mdx +722 -0
- package/docs/reference/05-adapters.mdx +351 -0
- package/docs/reference/06-errors.mdx +138 -0
- package/docs/reference/meta.ts +1 -1
- package/docs/server/01-handlers.mdx +200 -0
- package/docs/server/02-timers.mdx +130 -0
- package/docs/server/03-cancellation.mdx +141 -0
- package/docs/server/04-routes.mdx +243 -0
- package/docs/server/05-production.mdx +322 -0
- package/docs/server/06-guarantees.mdx +123 -0
- package/examples/playground/DEPLOY.md +53 -7
- package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
- package/examples/playground/app/a0/[appId]/page.tsx +53 -0
- package/examples/playground/app/a0/a0.css +818 -0
- package/examples/playground/app/a0/app-list.tsx +218 -0
- package/examples/playground/app/a0/app-view.tsx +63 -0
- package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
- package/examples/playground/app/a0/apps/[appId]/route.ts +28 -0
- package/examples/playground/app/a0/apps.test.ts +128 -0
- package/examples/playground/app/a0/apps.ts +102 -0
- package/examples/playground/app/a0/chat.tsx +410 -0
- package/examples/playground/app/a0/columns.tsx +109 -0
- package/examples/playground/app/a0/context-menu.tsx +116 -0
- package/examples/playground/app/a0/cookies.ts +46 -0
- package/examples/playground/app/a0/crash-on-request.ts +72 -0
- package/examples/playground/app/a0/crashes/route.ts +3 -0
- package/examples/playground/app/a0/crashes.test.ts +33 -0
- package/examples/playground/app/a0/crashes.ts +28 -0
- package/examples/playground/app/a0/devtools.tsx +164 -0
- package/examples/playground/app/a0/events/route.ts +6 -0
- package/examples/playground/app/a0/flags.ts +30 -0
- package/examples/playground/app/a0/fx/events/route.ts +9 -0
- package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/fx/short/events/route.ts +11 -0
- package/examples/playground/app/a0/layout.tsx +38 -0
- package/examples/playground/app/a0/list/route.ts +10 -0
- package/examples/playground/app/a0/model.test.ts +73 -0
- package/examples/playground/app/a0/model.ts +95 -0
- package/examples/playground/app/a0/no-photon.cjs +4 -0
- package/examples/playground/app/a0/no-quickjs.cjs +3 -0
- package/examples/playground/app/a0/page.tsx +14 -0
- package/examples/playground/app/a0/pi/events/route.ts +9 -0
- package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/pi/short/events/route.ts +11 -0
- package/examples/playground/app/a0/preview-pane.tsx +145 -0
- package/examples/playground/app/a0/preview.test.ts +53 -0
- package/examples/playground/app/a0/preview.ts +26 -0
- package/examples/playground/app/a0/sandbox-log.ts +75 -0
- package/examples/playground/app/a0/sandbox-session.ts +152 -0
- package/examples/playground/app/a0/sandbox.ts +191 -0
- package/examples/playground/app/a0/sandboxes/scheduler/route.ts +6 -0
- package/examples/playground/app/a0/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/server.ts +213 -0
- package/examples/playground/app/a0/session.ts +59 -0
- package/examples/playground/app/a0/shell.tsx +158 -0
- package/examples/playground/app/a0/short/events/route.ts +9 -0
- package/examples/playground/app/a0/splitter.tsx +120 -0
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +144 -174
- package/examples/playground/app/agent/[agentId]/command-receipt.test.tsx +65 -0
- package/examples/playground/app/agent/[agentId]/command-receipt.tsx +27 -0
- package/examples/playground/app/agent/[agentId]/compaction-event.tsx +25 -24
- package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +119 -0
- package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +208 -165
- package/examples/playground/app/agent/[agentId]/page.tsx +2 -16
- package/examples/playground/app/agent/events/route.ts +5 -2
- package/examples/playground/app/agent/ingress.test.ts +101 -0
- package/examples/playground/app/agent/model-input.test.tsx +58 -0
- package/examples/playground/app/agent/model-input.tsx +115 -0
- package/examples/playground/app/agent/model.test.ts +73 -0
- package/examples/playground/app/agent/model.ts +58 -26
- package/examples/playground/app/agent/new-agent-session.tsx +37 -19
- package/examples/playground/app/agent/page.tsx +4 -4
- package/examples/playground/app/agent/server.ts +19 -15
- package/examples/playground/app/agent/session.ts +5 -6
- package/examples/playground/app/agent/telemetry.test.ts +189 -0
- package/examples/playground/app/agent/telemetry.ts +30 -0
- package/examples/playground/app/agent/trace.test.ts +23 -0
- package/examples/playground/app/agent/trace.ts +18 -0
- package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +12 -12
- package/examples/playground/app/canvas/[canvasId]/page.tsx +1 -2
- package/examples/playground/app/canvas/[canvasId]/session.ts +2 -2
- package/examples/playground/app/canvas/model.ts +53 -53
- package/examples/playground/app/chat/[chatId]/agent-state.test.ts +176 -0
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +16 -1
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +55 -77
- package/examples/playground/app/chat/[chatId]/chat-view.ts +37 -3
- package/examples/playground/app/chat/[chatId]/page.tsx +7 -15
- package/examples/playground/app/chat/[chatId]/session.ts +2 -19
- package/examples/playground/app/chat/agent/route.ts +9 -0
- package/examples/playground/app/chat/model.test.ts +46 -119
- package/examples/playground/app/chat/model.ts +82 -128
- package/examples/playground/app/chat/scheduler/route.ts +2 -2
- package/examples/playground/app/chat/server.test.ts +112 -0
- package/examples/playground/app/chat/server.ts +113 -117
- package/examples/playground/app/components/activity-feed.tsx +30 -1
- package/examples/playground/app/components/activity-history.test.tsx +35 -0
- package/examples/playground/app/components/demo-icons.tsx +89 -0
- package/examples/playground/app/components/demos.tsx +33 -0
- package/examples/playground/app/components/fast-link.test.tsx +58 -0
- package/examples/playground/app/components/fast-link.tsx +55 -0
- package/examples/playground/app/components/site-header.tsx +96 -0
- package/examples/playground/app/counter/counter-client.tsx +3 -3
- package/examples/playground/app/counter/model.test.ts +3 -3
- package/examples/playground/app/counter/model.ts +11 -21
- package/examples/playground/app/counter/page.tsx +2 -15
- package/examples/playground/app/counter/session.ts +2 -2
- package/examples/playground/app/documents/[documentId]/code-editor.tsx +12 -1
- package/examples/playground/app/documents/[documentId]/document-client.tsx +10 -11
- package/examples/playground/app/documents/[documentId]/page.tsx +1 -2
- package/examples/playground/app/documents/[documentId]/session.ts +2 -2
- package/examples/playground/app/documents/model.ts +27 -27
- package/examples/playground/app/globals.css +420 -207
- package/examples/playground/app/initial-state.test.tsx +54 -0
- package/examples/playground/app/layout.tsx +7 -23
- package/examples/playground/app/orders/[orderId]/page.tsx +1 -2
- package/examples/playground/app/orders/[orderId]/session.ts +2 -2
- package/examples/playground/app/orders/model.ts +49 -49
- package/examples/playground/app/page.tsx +13 -102
- package/examples/playground/app/recovery/[recoveryId]/page.tsx +1 -2
- package/examples/playground/app/recovery/[recoveryId]/session.ts +2 -2
- package/examples/playground/app/recovery/model.ts +30 -30
- package/examples/playground/instrumentation.ts +5 -0
- package/examples/playground/next.config.ts +17 -5
- package/examples/playground/package.json +13 -2
- package/examples/playground/scripts/a0-template.ts +77 -0
- package/examples/playground/vercel.json +36 -0
- package/package.json +29 -3
- package/src/actor-client.ts +16 -4
- package/src/actor-react.ts +25 -11
- package/src/actor-shared.ts +21 -6
- package/src/actor.ts +76 -31
- package/src/ai-agent-actions.ts +219 -0
- package/src/ai-agent.ts +2517 -0
- package/src/ai-app-state.ts +91 -0
- package/src/ai-client-state.ts +6 -3
- package/src/ai-client.ts +62 -0
- package/src/ai-context-schema.ts +25 -5
- package/src/ai-context.ts +90 -40
- package/src/ai-control-server.ts +92 -72
- package/src/ai-coordinator.ts +23 -22
- package/src/ai-fx.browser.ts +5 -0
- package/src/ai-fx.ts +833 -0
- package/src/ai-harness.ts +518 -0
- package/src/ai-message-base.ts +209 -0
- package/src/ai-message-projection.ts +262 -64
- package/src/ai-model-metadata.ts +11 -6
- package/src/ai-pi.browser.ts +5 -0
- package/src/ai-pi.ts +1263 -0
- package/src/ai-progress-batches.ts +26 -0
- package/src/ai-projector.ts +95 -14
- package/src/ai-react.ts +146 -0
- package/src/ai-sdk-step.ts +2 -2
- package/src/ai-server.browser.ts +2 -2
- package/src/ai-server.ts +23 -1965
- package/src/ai-stored-state.ts +48 -163
- package/src/ai.ts +339 -155
- package/src/bootstrap-stream.ts +72 -0
- package/src/bounded-cache.ts +55 -0
- package/src/client-render-session.ts +49 -0
- package/src/client.ts +688 -151
- package/src/contract.ts +92 -42
- package/src/deterministic-id.ts +37 -0
- package/src/equal-json.ts +32 -0
- package/src/errors.ts +13 -1
- package/src/index.ts +5 -1
- package/src/initial-snapshot.ts +39 -0
- package/src/internal.ts +52 -0
- package/src/libfx.d.ts +34 -0
- package/src/postgres-notification-scope.ts +14 -3
- package/src/react.ts +171 -206
- package/src/redis-http-reply.ts +256 -0
- package/src/redis-http-stream.ts +73 -0
- package/src/redis-http-subscriptions.ts +13 -5
- package/src/reducer-runtime.ts +52 -0
- package/src/reducer.ts +93 -11
- package/src/scheduler-qstash.ts +49 -20
- package/src/scheduler-task.ts +34 -2
- package/src/scheduler-vercel.ts +32 -84
- package/src/server-fetch.ts +139 -35
- package/src/server.ts +1133 -255
- package/src/session-clear.ts +23 -0
- package/src/session-socket.ts +154 -49
- package/src/sse.ts +71 -4
- package/src/store-memory.ts +252 -75
- package/src/store-polling.ts +18 -4
- package/src/store-postgres.ts +541 -114
- package/src/store-redis-core.ts +1450 -493
- package/src/store-redis-http.ts +18 -2
- package/src/store-redis-notify.ts +262 -85
- package/src/store-redis.ts +19 -9
- package/src/store-sqlite.ts +319 -45
- package/src/store.ts +79 -1
- package/src/stream-buffer.ts +43 -0
- package/src/telemetry.ts +14 -1
- package/src/wire.ts +241 -13
- package/dist/actor-ohPC-81x.d.ts.map +0 -1
- package/dist/actor-shared-B5tJfzt-.js.map +0 -1
- package/dist/ai-CrEf6p_W.d.ts.map +0 -1
- package/dist/ai-DgOBltJ_.js.map +0 -1
- package/dist/client-BYzHjkwU.js.map +0 -1
- package/dist/client-CzyacQpJ.d.ts.map +0 -1
- package/dist/contract-CKRg_E4q.js.map +0 -1
- package/dist/errors-DCk6ch5n.js.map +0 -1
- package/dist/errors-DvhSXnxk.d.ts.map +0 -1
- package/dist/internal-Dq2qYxou.js.map +0 -1
- package/dist/react.d.ts.map +0 -1
- package/dist/reducer-DEMjEY_O.js +0 -29
- package/dist/reducer-DEMjEY_O.js.map +0 -1
- package/dist/reducer-otzHjuJj.d.ts.map +0 -1
- package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
- package/dist/server-BD5ckxZb.js.map +0 -1
- package/dist/server-DbD7IJVK.d.ts.map +0 -1
- package/dist/store-DyZM6fS5.d.ts.map +0 -1
- package/dist/store-polling-CmxUbV93.js.map +0 -1
- package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
- package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
- package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
- package/dist/validate-XKT4FSNn.js +0 -30
- package/dist/validate-XKT4FSNn.js.map +0 -1
- package/dist/wire-BO5wWCb1.js.map +0 -1
- package/docs/concepts/01-contracts.mdx +0 -142
- package/docs/concepts/02-handlers.mdx +0 -284
- package/docs/concepts/03-durability.mdx +0 -314
- package/docs/concepts/04-state.mdx +0 -209
- package/docs/guides/01-timers.mdx +0 -185
- package/docs/guides/02-cancellation.mdx +0 -161
- package/docs/guides/03-react.mdx +0 -384
- package/docs/guides/04-local-first.mdx +0 -88
- package/docs/guides/05-production.mdx +0 -521
- package/docs/guides/06-ai-agents.mdx +0 -1318
- package/docs/guides/08-application-data.mdx +0 -113
- package/docs/guides/09-presence.mdx +0 -266
- package/docs/guides/10-transports.mdx +0 -221
- package/docs/reference/01-api.mdx +0 -1896
- package/docs/reference/02-errors.mdx +0 -149
- package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
- package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
- package/examples/playground/app/agent/compaction-settings.ts +0 -49
- package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
- package/examples/playground/app/agent/compaction-timeline.ts +0 -208
- /package/docs/{concepts → contract}/meta.ts +0 -0
- /package/docs/{guides → server}/meta.ts +0 -0
package/dist/store-sqlite.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as pollingStream } from "./store-polling-
|
|
3
|
-
import { t as idempotentReplay } from "./idempotent-replay-
|
|
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 =
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
|
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 >
|
|
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
|
-
|
|
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(
|
|
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
|
}
|