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/src/scheduler-task.ts
CHANGED
|
@@ -26,10 +26,27 @@ export type SchedulerAppendTask = {
|
|
|
26
26
|
/** Earliest useful delivery as epoch milliseconds. */
|
|
27
27
|
dueAt: number
|
|
28
28
|
events: readonly ScheduledEvent[]
|
|
29
|
+
/** The scheduling handler's event; delivery skips once it is cleared. */
|
|
30
|
+
originIndex?: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A durable conditional clear. */
|
|
34
|
+
export type SchedulerClearTask = {
|
|
35
|
+
version: 1
|
|
36
|
+
kind: 'clear'
|
|
37
|
+
/** Stable identity for provider-level deduplication. */
|
|
38
|
+
id: string
|
|
39
|
+
contract: string
|
|
40
|
+
sessionId: string
|
|
41
|
+
/** Earliest useful delivery as epoch milliseconds. */
|
|
42
|
+
dueAt: number
|
|
43
|
+
/** Clear only while this is still the head. */
|
|
44
|
+
ifLastIndex: number
|
|
29
45
|
}
|
|
30
46
|
|
|
31
47
|
/** The versioned message protocol shared by scheduler adapters. */
|
|
32
|
-
export type SchedulerTask =
|
|
48
|
+
export type SchedulerTask =
|
|
49
|
+
SchedulerDrainTask | SchedulerAppendTask | SchedulerClearTask
|
|
33
50
|
|
|
34
51
|
const recordOf = (value: unknown): Record<string, unknown> | null =>
|
|
35
52
|
value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
@@ -42,6 +59,9 @@ const validName = (value: unknown): value is string =>
|
|
|
42
59
|
const validDueAt = (value: unknown): value is number =>
|
|
43
60
|
typeof value === 'number' && Number.isInteger(value) && value % 1_000 === 0
|
|
44
61
|
|
|
62
|
+
const validIndex = (value: unknown): value is number =>
|
|
63
|
+
typeof value === 'number' && Number.isSafeInteger(value) && value >= 0
|
|
64
|
+
|
|
45
65
|
const baseTask = (
|
|
46
66
|
record: Record<string, unknown>,
|
|
47
67
|
): { contract: string; sessionId: string } | null =>
|
|
@@ -68,10 +88,21 @@ export function normalizeSchedulerTask(value: unknown): SchedulerTask | null {
|
|
|
68
88
|
if (!validDueAt(dueAt)) return null
|
|
69
89
|
return { version: 1, kind: 'drain', ...base, dueAt }
|
|
70
90
|
}
|
|
91
|
+
if (record['kind'] === 'clear') {
|
|
92
|
+
const { dueAt, id, ifLastIndex } = record
|
|
93
|
+
if (!validName(id) || !validDueAt(dueAt) || !validIndex(ifLastIndex))
|
|
94
|
+
return null
|
|
95
|
+
return { version: 1, kind: 'clear', id, ...base, dueAt, ifLastIndex }
|
|
96
|
+
}
|
|
71
97
|
if (record['kind'] !== 'append') return null
|
|
72
98
|
|
|
73
|
-
const { dueAt, events, id } = record
|
|
99
|
+
const { dueAt, events, id, originIndex } = record
|
|
74
100
|
if (!validName(id) || !validDueAt(dueAt)) return null
|
|
101
|
+
if (
|
|
102
|
+
originIndex !== undefined &&
|
|
103
|
+
(!validIndex(originIndex) || originIndex === 0)
|
|
104
|
+
)
|
|
105
|
+
return null
|
|
75
106
|
if (!Array.isArray(events) || events.length === 0) return null
|
|
76
107
|
const parsedEvents: ScheduledEvent[] = []
|
|
77
108
|
for (const event of events) {
|
|
@@ -102,5 +133,6 @@ export function normalizeSchedulerTask(value: unknown): SchedulerTask | null {
|
|
|
102
133
|
...base,
|
|
103
134
|
dueAt,
|
|
104
135
|
events: parsedEvents,
|
|
136
|
+
...(originIndex === undefined ? {} : { originIndex }),
|
|
105
137
|
}
|
|
106
138
|
}
|
package/src/scheduler-vercel.ts
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* - append tasks hash their stable task id with the topic for provider
|
|
11
11
|
* deduplication. Their six-day maximum delay leaves a full day for
|
|
12
12
|
* delivery and retries inside the provider's seven-day retention.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Clear tasks follow the same identity and delay rules. All tasks use
|
|
14
|
+
* the configured deployment scope for sending and receiving.
|
|
15
15
|
* - `handler(...servers)` is the delivery route: look up the server for
|
|
16
16
|
* the contract named in the message and drain it. Settled drains ack;
|
|
17
17
|
* contention arms a later watchdog before acking; handler failure keeps
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
} from './scheduler-task.ts'
|
|
39
39
|
import {
|
|
40
40
|
deliverSchedulerAppend,
|
|
41
|
+
deliverSchedulerClear,
|
|
41
42
|
type A2Scheduler,
|
|
42
43
|
type DrainableServer,
|
|
43
44
|
} from './server.ts'
|
|
@@ -58,14 +59,6 @@ export type QueueDeliveryMetadata = {
|
|
|
58
59
|
deliveryCount: number
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
type VercelQueueMessage = SchedulerTask & {
|
|
62
|
-
/** Provider-private state used to address callback lease operations. */
|
|
63
|
-
_a2Vercel?: {
|
|
64
|
-
version: 1
|
|
65
|
-
deployment: 'deployment' | 'deploymentless'
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
62
|
/** What this module needs from a Vercel Queues-compatible transport. */
|
|
70
63
|
export type QueueTransport = {
|
|
71
64
|
send(
|
|
@@ -76,10 +69,6 @@ export type QueueTransport = {
|
|
|
76
69
|
idempotencyKey?: string
|
|
77
70
|
retentionSeconds?: number
|
|
78
71
|
},
|
|
79
|
-
/** Resolved A2 affinity. Custom transports own its provider mapping. */
|
|
80
|
-
routing?: {
|
|
81
|
-
deployment: 'deployment' | 'deploymentless'
|
|
82
|
-
},
|
|
83
72
|
): Promise<unknown>
|
|
84
73
|
/**
|
|
85
74
|
* Wrap the delivery handler into a route. The handler throws
|
|
@@ -101,11 +90,8 @@ export type QueueTransport = {
|
|
|
101
90
|
export type VercelQueuesOptions = {
|
|
102
91
|
/** Queue topic matching `[A-Za-z0-9_-]+`. Default `'a2'`. */
|
|
103
92
|
topic?: string
|
|
104
|
-
/**
|
|
105
|
-
|
|
106
|
-
* always target the publishing deployment. Default `'deployment'`.
|
|
107
|
-
*/
|
|
108
|
-
scheduledAppends?: 'deployment' | 'deploymentless'
|
|
93
|
+
/** Queue SDK deployment scope for all tasks. Default: VERCEL_DEPLOYMENT_ID; null opts out. */
|
|
94
|
+
deploymentId?: string | null
|
|
109
95
|
/** Injectable transport (tests). Default: `@vercel/queue`. */
|
|
110
96
|
transport?: QueueTransport
|
|
111
97
|
/** Injectable clock for due-time calculations (tests). */
|
|
@@ -200,7 +186,9 @@ const unsettledBackoffSeconds = (deliveryCount: number): number =>
|
|
|
200
186
|
60,
|
|
201
187
|
)
|
|
202
188
|
|
|
203
|
-
const vercelQueueTransport = async (
|
|
189
|
+
const vercelQueueTransport = async (
|
|
190
|
+
clientOptions: Pick<VercelQueuesOptions, 'deploymentId'>,
|
|
191
|
+
): Promise<QueueTransport> => {
|
|
204
192
|
const queue = await import('@vercel/queue').catch(() => {
|
|
205
193
|
throw classifySchedulerSendFailure(
|
|
206
194
|
new Error(
|
|
@@ -209,23 +197,11 @@ const vercelQueueTransport = async (): Promise<QueueTransport> => {
|
|
|
209
197
|
'terminal',
|
|
210
198
|
)
|
|
211
199
|
})
|
|
212
|
-
const
|
|
213
|
-
const deploymentlessClient = new queue.QueueClient({ deploymentId: null })
|
|
200
|
+
const client = new queue.QueueClient(clientOptions)
|
|
214
201
|
return {
|
|
215
|
-
send: (topic, message, options
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
...message,
|
|
219
|
-
_a2Vercel: { version: 1, deployment },
|
|
220
|
-
}
|
|
221
|
-
return (
|
|
222
|
-
deployment === 'deploymentless'
|
|
223
|
-
? deploymentlessClient
|
|
224
|
-
: deploymentClient
|
|
225
|
-
).send(topic, body, options)
|
|
226
|
-
},
|
|
227
|
-
handleCallback: (handler) => {
|
|
228
|
-
const callbackOptions = {
|
|
202
|
+
send: (topic, message, options) => client.send(topic, message, options),
|
|
203
|
+
handleCallback: (handler) =>
|
|
204
|
+
client.handleCallback(handler, {
|
|
229
205
|
// A hard-killed callback normally has a fresh claim watchdog. If
|
|
230
206
|
// its successor send also failed, make the old unacked delivery's
|
|
231
207
|
// fallback as prompt as Vercel Queues permits.
|
|
@@ -234,36 +210,13 @@ const vercelQueueTransport = async (): Promise<QueueTransport> => {
|
|
|
234
210
|
// unacked) delivery via a visibility change — 200, no error
|
|
235
211
|
// log. Everything else returns undefined → the SDK's default
|
|
236
212
|
// throw path, which is loud on purpose.
|
|
237
|
-
retry: (error
|
|
213
|
+
retry: (error, metadata) =>
|
|
238
214
|
error instanceof DrainUnsettled
|
|
239
215
|
? {
|
|
240
216
|
afterSeconds: unsettledBackoffSeconds(metadata.deliveryCount),
|
|
241
217
|
}
|
|
242
218
|
: undefined,
|
|
243
|
-
}
|
|
244
|
-
const deploymentHandler = deploymentClient.handleCallback(
|
|
245
|
-
handler,
|
|
246
|
-
callbackOptions,
|
|
247
|
-
) as (req: Request) => Promise<Response>
|
|
248
|
-
const deploymentlessHandler = deploymentlessClient.handleCallback(
|
|
249
|
-
handler,
|
|
250
|
-
callbackOptions,
|
|
251
|
-
) as (req: Request) => Promise<Response>
|
|
252
|
-
return async (req: Request): Promise<Response> => {
|
|
253
|
-
let delivered: unknown
|
|
254
|
-
try {
|
|
255
|
-
delivered = await req.clone().json()
|
|
256
|
-
} catch {
|
|
257
|
-
return deploymentlessHandler(req)
|
|
258
|
-
}
|
|
259
|
-
const metadata = recordOf(recordOf(delivered)?.['_a2Vercel'])
|
|
260
|
-
const routing =
|
|
261
|
-
metadata?.['version'] === 1 ? metadata['deployment'] : undefined
|
|
262
|
-
return routing === 'deployment'
|
|
263
|
-
? deploymentHandler(req)
|
|
264
|
-
: deploymentlessHandler(req)
|
|
265
|
-
}
|
|
266
|
-
},
|
|
219
|
+
}) as (req: Request) => Promise<Response>,
|
|
267
220
|
isDuplicate: (error) => error instanceof queue.DuplicateMessageError,
|
|
268
221
|
}
|
|
269
222
|
}
|
|
@@ -273,23 +226,18 @@ export function vercelQueues(options: VercelQueuesOptions = {}): A2Scheduler {
|
|
|
273
226
|
if (typeof topic !== 'string' || !TOPIC_PATTERN.test(topic)) {
|
|
274
227
|
throw new TypeError('vercelQueues() topic must match [A-Za-z0-9_-]+')
|
|
275
228
|
}
|
|
276
|
-
const scheduledAppends = options.scheduledAppends ?? 'deployment'
|
|
277
|
-
if (
|
|
278
|
-
scheduledAppends !== 'deployment' &&
|
|
279
|
-
scheduledAppends !== 'deploymentless'
|
|
280
|
-
) {
|
|
281
|
-
throw new TypeError(
|
|
282
|
-
"vercelQueues() scheduledAppends must be 'deployment' or 'deploymentless'",
|
|
283
|
-
)
|
|
284
|
-
}
|
|
285
229
|
const clock = options.clock ?? SYSTEM_CLOCK
|
|
230
|
+
const clientOptions =
|
|
231
|
+
options.deploymentId === undefined
|
|
232
|
+
? {}
|
|
233
|
+
: { deploymentId: options.deploymentId }
|
|
286
234
|
|
|
287
235
|
// Lazy: constructing the scheduler value does no I/O and works without
|
|
288
236
|
// the peer dep until the first task or delivery.
|
|
289
237
|
const transport = retryableLazy(() =>
|
|
290
238
|
options.transport
|
|
291
239
|
? Promise.resolve(options.transport)
|
|
292
|
-
: vercelQueueTransport(),
|
|
240
|
+
: vercelQueueTransport(clientOptions),
|
|
293
241
|
).get
|
|
294
242
|
|
|
295
243
|
const sendTask = async (task: SchedulerTask): Promise<void> => {
|
|
@@ -305,7 +253,7 @@ export function vercelQueues(options: VercelQueuesOptions = {}): A2Scheduler {
|
|
|
305
253
|
requestedDueAt <= nowMs
|
|
306
254
|
? Math.floor(nowMs / 1_000) * 1_000
|
|
307
255
|
: Math.ceil(requestedDueAt / 1_000) * 1_000
|
|
308
|
-
const deliveryTargetAt = task.kind === '
|
|
256
|
+
const deliveryTargetAt = task.kind === 'drain' ? dueAt : requestedDueAt
|
|
309
257
|
const deliveryDelaySeconds = Math.max(
|
|
310
258
|
0,
|
|
311
259
|
Math.ceil((deliveryTargetAt - nowMs) / 1_000),
|
|
@@ -318,7 +266,6 @@ export function vercelQueues(options: VercelQueuesOptions = {}): A2Scheduler {
|
|
|
318
266
|
'terminal',
|
|
319
267
|
)
|
|
320
268
|
}
|
|
321
|
-
const deployment = task.kind === 'append' ? scheduledAppends : 'deployment'
|
|
322
269
|
const message: SchedulerTask = { ...task, dueAt }
|
|
323
270
|
const idempotencyKey =
|
|
324
271
|
task.kind === 'drain'
|
|
@@ -329,19 +276,16 @@ export function vercelQueues(options: VercelQueuesOptions = {}): A2Scheduler {
|
|
|
329
276
|
task.sessionId,
|
|
330
277
|
String(dueAt),
|
|
331
278
|
])
|
|
332
|
-
:
|
|
279
|
+
: task.kind === 'append'
|
|
280
|
+
? digestId(['a2:scheduler-vercel:append:v1', topic, task.id])
|
|
281
|
+
: digestId(['a2:scheduler-vercel:clear:v1', topic, task.id])
|
|
333
282
|
const t = await transport()
|
|
334
283
|
try {
|
|
335
|
-
await t.send(
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
idempotencyKey,
|
|
341
|
-
retentionSeconds: MAX_RETENTION_SECONDS,
|
|
342
|
-
},
|
|
343
|
-
{ deployment },
|
|
344
|
-
)
|
|
284
|
+
await t.send(topic, message, {
|
|
285
|
+
delaySeconds: deliveryDelaySeconds,
|
|
286
|
+
idempotencyKey,
|
|
287
|
+
retentionSeconds: MAX_RETENTION_SECONDS,
|
|
288
|
+
})
|
|
345
289
|
} catch (err) {
|
|
346
290
|
if (t.isDuplicate(err)) return
|
|
347
291
|
throw classifyVercelSendFailure(err)
|
|
@@ -394,6 +338,10 @@ export function vercelQueues(options: VercelQueuesOptions = {}): A2Scheduler {
|
|
|
394
338
|
await deliverSchedulerAppend(server, task)
|
|
395
339
|
return
|
|
396
340
|
}
|
|
341
|
+
if (task.kind === 'clear') {
|
|
342
|
+
await deliverSchedulerClear(server, task)
|
|
343
|
+
return
|
|
344
|
+
}
|
|
397
345
|
|
|
398
346
|
const nextDueAt = successorDueAt(task)
|
|
399
347
|
const result = internals
|
package/src/server-fetch.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { encodeState } from './reducer-runtime.ts'
|
|
1
2
|
import type {
|
|
2
3
|
AppendInput,
|
|
3
4
|
ContractEvent,
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
type ParsedPushEvent,
|
|
13
14
|
type ParsedPushPresence,
|
|
14
15
|
} from './push-envelope.ts'
|
|
15
|
-
import type { A2Server } from './server.ts'
|
|
16
|
+
import type { A2Server, Session, SessionAppend } from './server.ts'
|
|
16
17
|
import {
|
|
17
18
|
sessionSocket,
|
|
18
19
|
sessionsSocket,
|
|
@@ -20,7 +21,16 @@ import {
|
|
|
20
21
|
type SocketSession,
|
|
21
22
|
} from './session-socket.ts'
|
|
22
23
|
import { sseResponse } from './sse.ts'
|
|
23
|
-
import {
|
|
24
|
+
import { bootstrapStream } from './bootstrap-stream.ts'
|
|
25
|
+
import type { Reducer } from './reducer.ts'
|
|
26
|
+
import type { SubscriptionStart, WireVersion } from './wire.ts'
|
|
27
|
+
import {
|
|
28
|
+
asA2Error,
|
|
29
|
+
errorStatus,
|
|
30
|
+
errorToWire,
|
|
31
|
+
eventToWire,
|
|
32
|
+
wireVersion,
|
|
33
|
+
} from './wire.ts'
|
|
24
34
|
|
|
25
35
|
type SuggestedEventType<D extends EventDefs> =
|
|
26
36
|
(keyof D & string) | (string & Record<never, never>)
|
|
@@ -50,6 +60,7 @@ export type A2Operation<
|
|
|
50
60
|
readonly type: 'stream'
|
|
51
61
|
readonly sessionId: string
|
|
52
62
|
readonly startAfter: number
|
|
63
|
+
readonly bootstrap?: true
|
|
53
64
|
readonly transport: 'http' | 'websocket'
|
|
54
65
|
}
|
|
55
66
|
| {
|
|
@@ -100,10 +111,15 @@ export function setServerFetchHooks<
|
|
|
100
111
|
serverFetchHooks.set(server.fetch, hooks)
|
|
101
112
|
}
|
|
102
113
|
|
|
103
|
-
type FetchServer<D extends EventDefs, P extends PresenceDefs> =
|
|
104
|
-
A2Server<D, P
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
type FetchServer<D extends EventDefs, P extends PresenceDefs> = {
|
|
115
|
+
contract: A2Server<D, P>['contract']
|
|
116
|
+
session(id: string): Session<D, SessionAppend<D>, P>
|
|
117
|
+
/** Public history plus the latest clear marker when the range includes it. */
|
|
118
|
+
historyPage(
|
|
119
|
+
sessionId: string,
|
|
120
|
+
bounds: { gte: number; lte: number },
|
|
121
|
+
): Promise<{ events: ContractEvent<D>[]; clearIndex?: number }>
|
|
122
|
+
}
|
|
107
123
|
|
|
108
124
|
type ServerFetch<D extends EventDefs, P extends PresenceDefs> = {
|
|
109
125
|
(request: Request): Promise<Response>
|
|
@@ -150,6 +166,25 @@ const parseResumeIndex = (raw: string | null): number => {
|
|
|
150
166
|
return index
|
|
151
167
|
}
|
|
152
168
|
|
|
169
|
+
const parseSubscription = (params: URLSearchParams): SubscriptionStart => {
|
|
170
|
+
const reducer = params.get('reducer')
|
|
171
|
+
const bootstrap = params.get('bootstrap')
|
|
172
|
+
const index = params.get('index')
|
|
173
|
+
if (reducer === '') throw new A2Error('INVALID_PAYLOAD', 'empty reducer name')
|
|
174
|
+
if (bootstrap !== null) {
|
|
175
|
+
if (bootstrap !== '1' || index !== null || reducer === null)
|
|
176
|
+
throw new A2Error(
|
|
177
|
+
'INVALID_PAYLOAD',
|
|
178
|
+
'bootstrap requires a reducer and no index',
|
|
179
|
+
)
|
|
180
|
+
return { bootstrap: true, reducer }
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
index: parseResumeIndex(index),
|
|
184
|
+
...(reducer === null ? {} : { reducer }),
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
153
188
|
const errorResponse = (error: unknown): Response => {
|
|
154
189
|
const a2error = asA2Error(error)
|
|
155
190
|
return Response.json(errorToWire(a2error), {
|
|
@@ -235,29 +270,82 @@ export function createServerFetch<
|
|
|
235
270
|
}
|
|
236
271
|
}
|
|
237
272
|
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
return (await authorized(operation))
|
|
249
|
-
? server.session(sessionId)
|
|
250
|
-
: null
|
|
251
|
-
},
|
|
252
|
-
socket,
|
|
253
|
-
{
|
|
254
|
-
presence,
|
|
255
|
-
...(deadline === null ? {} : { deadline }),
|
|
256
|
-
gatePush: gateSocketPush,
|
|
257
|
-
},
|
|
273
|
+
const selectedSession = (
|
|
274
|
+
sessionId: string,
|
|
275
|
+
selection: SubscriptionStart,
|
|
276
|
+
): SocketSession => {
|
|
277
|
+
const reducer = server.contract.reducer
|
|
278
|
+
if (
|
|
279
|
+
selection.reducer !== undefined &&
|
|
280
|
+
reducer !== undefined &&
|
|
281
|
+
reducer.name !== selection.reducer
|
|
258
282
|
)
|
|
283
|
+
throw new A2Error(
|
|
284
|
+
'INVALID_PAYLOAD',
|
|
285
|
+
'client reducer does not match the public contract reducer',
|
|
286
|
+
)
|
|
287
|
+
const target = server.session(sessionId)
|
|
288
|
+
const streamTarget = target as unknown as SocketSession
|
|
289
|
+
if (!selection.bootstrap) return streamTarget
|
|
290
|
+
if (!reducer)
|
|
291
|
+
throw new A2Error('INVALID_PAYLOAD', 'contract has no public reducer')
|
|
292
|
+
return {
|
|
293
|
+
...streamTarget,
|
|
294
|
+
stream: (streamOptions) =>
|
|
295
|
+
bootstrapStream({
|
|
296
|
+
snapshot: async () => {
|
|
297
|
+
const initial = await target.state(
|
|
298
|
+
reducer as unknown as Reducer<D, unknown>,
|
|
299
|
+
)
|
|
300
|
+
return {
|
|
301
|
+
kind: 'snapshot',
|
|
302
|
+
sessionId,
|
|
303
|
+
name: reducer.name,
|
|
304
|
+
index: initial.index,
|
|
305
|
+
state: encodeState({ reducer, state: initial.state }),
|
|
306
|
+
...(initial.clearIndex === undefined
|
|
307
|
+
? {}
|
|
308
|
+
: { clearIndex: initial.clearIndex }),
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
stream: (index) => {
|
|
312
|
+
const bounds = {
|
|
313
|
+
startAfter: index,
|
|
314
|
+
presence: streamOptions.presence === true,
|
|
315
|
+
}
|
|
316
|
+
return target.stream(bounds)
|
|
317
|
+
},
|
|
318
|
+
}),
|
|
319
|
+
}
|
|
259
320
|
}
|
|
260
321
|
|
|
322
|
+
const attachMultiplexed =
|
|
323
|
+
(wire: WireVersion) =>
|
|
324
|
+
(socket: A2Socket): void => {
|
|
325
|
+
const deadline = invocationDeadlineMs()
|
|
326
|
+
sessionsSocket<D>(
|
|
327
|
+
async (sessionId, startAfter, selection = { index: startAfter }) => {
|
|
328
|
+
const operation: A2Operation<D, P> = {
|
|
329
|
+
type: 'stream',
|
|
330
|
+
sessionId,
|
|
331
|
+
startAfter,
|
|
332
|
+
...(selection.bootstrap ? { bootstrap: true as const } : {}),
|
|
333
|
+
transport: 'websocket',
|
|
334
|
+
}
|
|
335
|
+
return (await authorized(operation))
|
|
336
|
+
? selectedSession(sessionId, selection)
|
|
337
|
+
: null
|
|
338
|
+
},
|
|
339
|
+
socket,
|
|
340
|
+
{
|
|
341
|
+
presence,
|
|
342
|
+
wire,
|
|
343
|
+
...(deadline === null ? {} : { deadline }),
|
|
344
|
+
gatePush: gateSocketPush,
|
|
345
|
+
},
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
|
|
261
349
|
try {
|
|
262
350
|
if (request.method === 'GET') {
|
|
263
351
|
if (request.headers.get('upgrade')?.toLowerCase() === 'websocket') {
|
|
@@ -267,28 +355,32 @@ export function createServerFetch<
|
|
|
267
355
|
{ status: 426 },
|
|
268
356
|
)
|
|
269
357
|
}
|
|
358
|
+
const { searchParams } = new URL(request.url)
|
|
359
|
+
const wire = wireVersion(searchParams.get('wire'))
|
|
270
360
|
if (options.multiplexWebSocket === true) {
|
|
271
|
-
return await options.upgradeWebSocket(attachMultiplexed)
|
|
361
|
+
return await options.upgradeWebSocket(attachMultiplexed(wire))
|
|
272
362
|
}
|
|
273
363
|
|
|
274
|
-
const { searchParams } = new URL(request.url)
|
|
275
364
|
const sessionId = searchParams.get('sessionId')
|
|
276
365
|
if (sessionId === null || sessionId.length === 0) {
|
|
277
366
|
throw new A2Error('INVALID_PAYLOAD', 'missing sessionId')
|
|
278
367
|
}
|
|
279
|
-
const
|
|
368
|
+
const selection = parseSubscription(searchParams)
|
|
369
|
+
const startAfter = selection.index ?? 0
|
|
280
370
|
await authorize({
|
|
281
371
|
type: 'stream',
|
|
282
372
|
sessionId,
|
|
283
373
|
startAfter,
|
|
374
|
+
...(selection.bootstrap ? { bootstrap: true as const } : {}),
|
|
284
375
|
transport: 'websocket',
|
|
285
376
|
})
|
|
286
|
-
const target =
|
|
377
|
+
const target = selectedSession(sessionId, selection)
|
|
287
378
|
const attach = (socket: A2Socket): void => {
|
|
288
379
|
const deadline = invocationDeadlineMs()
|
|
289
380
|
sessionSocket(sessionId, target, socket, {
|
|
290
381
|
startAfter,
|
|
291
382
|
presence,
|
|
383
|
+
wire,
|
|
292
384
|
...(deadline === null ? {} : { deadline }),
|
|
293
385
|
gatePush: gateSocketPush,
|
|
294
386
|
})
|
|
@@ -311,25 +403,37 @@ export function createServerFetch<
|
|
|
311
403
|
...bounds,
|
|
312
404
|
transport: 'http',
|
|
313
405
|
})
|
|
314
|
-
const events = await server.
|
|
315
|
-
|
|
406
|
+
const { events, clearIndex } = await server.historyPage(
|
|
407
|
+
sessionId,
|
|
408
|
+
bounds,
|
|
409
|
+
)
|
|
410
|
+
const first = Math.max(bounds.gte, (clearIndex ?? 0) + 1)
|
|
411
|
+
const covered = events.length === Math.max(0, bounds.lte - first + 1)
|
|
316
412
|
return Response.json(events.map(eventToWire), {
|
|
317
|
-
headers: {
|
|
413
|
+
headers: {
|
|
414
|
+
'a2-history-covered': String(covered),
|
|
415
|
+
...(clearIndex === undefined
|
|
416
|
+
? {}
|
|
417
|
+
: { 'a2-clear-index': String(clearIndex) }),
|
|
418
|
+
},
|
|
318
419
|
})
|
|
319
420
|
}
|
|
320
421
|
|
|
321
|
-
const
|
|
422
|
+
const selection = parseSubscription(searchParams)
|
|
423
|
+
const startAfter = selection.index ?? 0
|
|
322
424
|
await authorize({
|
|
323
425
|
type: 'stream',
|
|
324
426
|
sessionId,
|
|
325
427
|
startAfter,
|
|
428
|
+
...(selection.bootstrap ? { bootstrap: true as const } : {}),
|
|
326
429
|
transport: 'http',
|
|
327
430
|
})
|
|
328
|
-
const target =
|
|
431
|
+
const target = selectedSession(sessionId, selection)
|
|
329
432
|
return sseResponse(
|
|
330
433
|
presence
|
|
331
434
|
? target.stream({ startAfter, presence: true })
|
|
332
435
|
: target.stream({ startAfter }),
|
|
436
|
+
{ wire: wireVersion(searchParams.get('wire')) },
|
|
333
437
|
)
|
|
334
438
|
}
|
|
335
439
|
|