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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "experimental-a2",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.18.0",
|
|
4
|
+
"description": "Syncs your immutable event log, from server to browser: one contract, handlers, derived state, and a live client per session.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -39,6 +39,16 @@
|
|
|
39
39
|
"browser": "./dist/ai-server.browser.js",
|
|
40
40
|
"default": "./dist/ai-server.js"
|
|
41
41
|
},
|
|
42
|
+
"./ai/fx": {
|
|
43
|
+
"browser": "./dist/ai-fx.browser.js",
|
|
44
|
+
"default": "./dist/ai-fx.js"
|
|
45
|
+
},
|
|
46
|
+
"./ai/pi": {
|
|
47
|
+
"browser": "./dist/ai-pi.browser.js",
|
|
48
|
+
"default": "./dist/ai-pi.js"
|
|
49
|
+
},
|
|
50
|
+
"./ai/client": "./dist/ai-client.js",
|
|
51
|
+
"./ai/react": "./dist/ai-react.js",
|
|
42
52
|
"./actor": {
|
|
43
53
|
"browser": "./dist/actor.browser.js",
|
|
44
54
|
"default": "./dist/actor.js"
|
|
@@ -59,9 +69,12 @@
|
|
|
59
69
|
"peerDependencies": {
|
|
60
70
|
"@opentelemetry/api": "^1.9.0",
|
|
61
71
|
"@upstash/qstash": "^2.11.0",
|
|
72
|
+
"@earendil-works/pi-ai": "^1.0.2",
|
|
73
|
+
"@earendil-works/pi-coding-agent": "^1.0.2",
|
|
62
74
|
"@vercel/queue": "*",
|
|
63
75
|
"ai": "^7.0.0",
|
|
64
76
|
"ioredis": "^5.4.0",
|
|
77
|
+
"libfx": "^0.0.12",
|
|
65
78
|
"pg": "^8.11.0",
|
|
66
79
|
"react": "^19.0.0"
|
|
67
80
|
},
|
|
@@ -78,9 +91,18 @@
|
|
|
78
91
|
"ai": {
|
|
79
92
|
"optional": true
|
|
80
93
|
},
|
|
94
|
+
"@earendil-works/pi-ai": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
97
|
+
"@earendil-works/pi-coding-agent": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
81
100
|
"ioredis": {
|
|
82
101
|
"optional": true
|
|
83
102
|
},
|
|
103
|
+
"libfx": {
|
|
104
|
+
"optional": true
|
|
105
|
+
},
|
|
84
106
|
"pg": {
|
|
85
107
|
"optional": true
|
|
86
108
|
},
|
|
@@ -113,7 +135,11 @@
|
|
|
113
135
|
"tsdown": "^0.22.14",
|
|
114
136
|
"typescript": "^7.0.2",
|
|
115
137
|
"vitest": "^4.1.10",
|
|
116
|
-
"zod": "^4.4.3"
|
|
138
|
+
"zod": "^4.4.3",
|
|
139
|
+
"libfx": "0.0.12",
|
|
140
|
+
"@earendil-works/pi-ai": "1.0.3",
|
|
141
|
+
"@earendil-works/pi-coding-agent": "1.0.2",
|
|
142
|
+
"@vercel/sandbox": "^3.0.0"
|
|
117
143
|
},
|
|
118
144
|
"scripts": {
|
|
119
145
|
"build": "tsdown",
|
package/src/actor-client.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* `client.call` mirrors the server handle's `call` record over the
|
|
7
7
|
* call lane `handle.fetch` serves — POST `{ event, input, messageId? }`
|
|
8
8
|
* → the answer (post-state), a 409 refusal (revived as
|
|
9
|
-
* `ActorRefusedError`),
|
|
9
|
+
* `ActorRefusedError`), a 410 for a call cleared before its answer
|
|
10
|
+
* (an `ActorRequestError` whose `cause` is the `A2Error`), or a 400. Both ends of that wire are library
|
|
10
11
|
* code: the types here are backed by `handle.fetch`, not by a
|
|
11
12
|
* hand-written convention.
|
|
12
13
|
*
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
|
|
20
21
|
import { ActorRefusedError } from './actor-shared.ts'
|
|
21
22
|
import type { ActorCallOptions, ActorCallResult } from './actor.ts'
|
|
23
|
+
import { A2Error } from './errors.ts'
|
|
22
24
|
|
|
23
25
|
export { ActorRefusedError }
|
|
24
26
|
|
|
@@ -58,7 +60,7 @@ export type ActorClientCall<
|
|
|
58
60
|
readonly [K in keyof HandleOf<D>['call']]: HandleOf<D>['call'][K] extends (
|
|
59
61
|
...args: infer P
|
|
60
62
|
) => Promise<{ state: unknown; index: number }>
|
|
61
|
-
? (...args: P) => Promise<
|
|
63
|
+
? (...args: P) => Promise<ActorCallResult<View>>
|
|
62
64
|
: never
|
|
63
65
|
}
|
|
64
66
|
|
|
@@ -111,7 +113,12 @@ const callOverWire = async (
|
|
|
111
113
|
typeof body['error'] === 'string'
|
|
112
114
|
? body['error']
|
|
113
115
|
: `actor call failed (${response.status})`
|
|
114
|
-
throw new ActorRequestError(message, {
|
|
116
|
+
throw new ActorRequestError(message, {
|
|
117
|
+
status: response.status,
|
|
118
|
+
...(response.status === 410 && body['code'] === 'SESSION_CLEARED'
|
|
119
|
+
? { cause: new A2Error('SESSION_CLEARED', message) }
|
|
120
|
+
: {}),
|
|
121
|
+
})
|
|
115
122
|
}
|
|
116
123
|
return snapshotBody(body, response.status)
|
|
117
124
|
}
|
|
@@ -150,10 +157,15 @@ const snapshotBody = (
|
|
|
150
157
|
body: Record<string, unknown>,
|
|
151
158
|
status: number,
|
|
152
159
|
): ActorCallResult<unknown> => {
|
|
160
|
+
const clearIndex = body['clearIndex']
|
|
153
161
|
if (
|
|
154
162
|
!('state' in body) ||
|
|
155
163
|
!Number.isSafeInteger(body['index']) ||
|
|
156
|
-
(body['index'] as number) < 0
|
|
164
|
+
(body['index'] as number) < 0 ||
|
|
165
|
+
(clearIndex !== undefined &&
|
|
166
|
+
(!Number.isSafeInteger(clearIndex) ||
|
|
167
|
+
(clearIndex as number) < 1 ||
|
|
168
|
+
(clearIndex as number) > (body['index'] as number)))
|
|
157
169
|
) {
|
|
158
170
|
throw new ActorRequestError('actor request returned an invalid snapshot', {
|
|
159
171
|
status,
|
package/src/actor-react.ts
CHANGED
|
@@ -37,14 +37,22 @@ import {
|
|
|
37
37
|
type AnyActorDefinition,
|
|
38
38
|
} from './actor-client.ts'
|
|
39
39
|
import { createClient } from './client.ts'
|
|
40
|
+
import { foldsClearMarkers } from './reducer.ts'
|
|
41
|
+
import { isSessionCleared } from './session-clear.ts'
|
|
40
42
|
import type { A2Client, Connection } from './client.ts'
|
|
41
43
|
import type { ContractEvent, EventDefs, PresenceDefs } from './contract.ts'
|
|
44
|
+
import { internalContract as contract } from './contract.ts'
|
|
42
45
|
import type { Reducer } from './reducer.ts'
|
|
43
46
|
import { useSession } from './react.ts'
|
|
44
47
|
import { useMemo } from 'react'
|
|
45
48
|
|
|
46
49
|
/** The server-to-client handoff: `await def.actor(id).state()`. */
|
|
47
|
-
export type ActorSnapshot<S> = {
|
|
50
|
+
export type ActorSnapshot<S> = {
|
|
51
|
+
state: S
|
|
52
|
+
index: number
|
|
53
|
+
/** The latest clear marker, once the actor has been cleared. */
|
|
54
|
+
clearIndex?: number
|
|
55
|
+
}
|
|
48
56
|
|
|
49
57
|
export type UseActorOptions<View> = {
|
|
50
58
|
/** The route whose handler delegates to `handle.fetch` for this instance. */
|
|
@@ -104,14 +112,23 @@ const followerReducer: Reducer<EventDefs, unknown, PresenceDefs> = {
|
|
|
104
112
|
// server-defined field list, so the server stays the authority and
|
|
105
113
|
// the protocol types honest callers at compile time.
|
|
106
114
|
presence: openPresenceDefs,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
// A clear marker from the actor route carries the actor's initial state.
|
|
116
|
+
fold: foldsClearMarkers((state: unknown, event: ContractEvent<EventDefs>) =>
|
|
117
|
+
event.type === ACTOR_STATE_EVENT || isSessionCleared(event)
|
|
118
|
+
? (event.payload as { state?: unknown } | null)?.state
|
|
110
119
|
: state,
|
|
120
|
+
),
|
|
111
121
|
initialState: undefined,
|
|
112
122
|
stateSchema: undefined,
|
|
113
123
|
}
|
|
114
124
|
|
|
125
|
+
const followerContract = contract({
|
|
126
|
+
name: 'a2.actor.follower',
|
|
127
|
+
events: followerReducer.events,
|
|
128
|
+
presence: followerReducer.presence,
|
|
129
|
+
reducer: followerReducer,
|
|
130
|
+
})
|
|
131
|
+
|
|
115
132
|
const clients = new Map<string, A2Client<EventDefs, unknown, PresenceDefs>>()
|
|
116
133
|
|
|
117
134
|
/** Use one actor instance: live state plus typed calls. No provider. */
|
|
@@ -121,11 +138,11 @@ export function useActor<D extends AnyActorDefinition, View = ActorStateOf<D>>(
|
|
|
121
138
|
const { api } = options
|
|
122
139
|
let client = clients.get(api)
|
|
123
140
|
if (!client) {
|
|
124
|
-
client = createClient({
|
|
141
|
+
client = createClient({ contract: followerContract, api })
|
|
125
142
|
clients.set(api, client)
|
|
126
143
|
}
|
|
127
144
|
const session = useSession(client, options.id, {
|
|
128
|
-
|
|
145
|
+
initial: options.initial,
|
|
129
146
|
...(options.participant === undefined
|
|
130
147
|
? {}
|
|
131
148
|
: { participant: options.participant }),
|
|
@@ -134,11 +151,8 @@ export function useActor<D extends AnyActorDefinition, View = ActorStateOf<D>>(
|
|
|
134
151
|
() =>
|
|
135
152
|
createActorClient<D, View>({
|
|
136
153
|
api,
|
|
137
|
-
onSnapshot: ({ state, index }) => {
|
|
138
|
-
client.session(options.id, {
|
|
139
|
-
initialState: state,
|
|
140
|
-
initialIndex: index,
|
|
141
|
-
})
|
|
154
|
+
onSnapshot: ({ state, index, clearIndex }) => {
|
|
155
|
+
client.session(options.id, { initial: { state, index, clearIndex } })
|
|
142
156
|
},
|
|
143
157
|
}),
|
|
144
158
|
[api, client, options.id],
|
package/src/actor-shared.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { reducer } from './reducer.ts'
|
|
1
2
|
/**
|
|
2
3
|
* Internal shared vocabulary of the actor module — the reserved event
|
|
3
4
|
* envelopes, the protocol and context types, the state fold. Not an
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
* events committed atomically with each handler's completion.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
|
-
import {
|
|
14
|
+
import { internalContract as createContract } from './contract.ts'
|
|
14
15
|
import type { Reducer } from './reducer.ts'
|
|
15
16
|
import type { ScheduleTiming } from './server.ts'
|
|
16
17
|
import type { StandardSchemaV1 } from './standard-schema.ts'
|
|
@@ -169,6 +170,8 @@ export type ActorStatePayload<S> = {
|
|
|
169
170
|
event: string
|
|
170
171
|
/** The message (invocation event) id this state answers. */
|
|
171
172
|
message: string
|
|
173
|
+
/** The session's latest clear marker when this state was read. */
|
|
174
|
+
clearIndex?: number
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
export type ActorFailedPayload = {
|
|
@@ -293,11 +296,21 @@ export function actorStateSchema<S>(): StandardSchemaV1<ActorStatePayload<S>> {
|
|
|
293
296
|
if (!isJsonTree(state)) {
|
|
294
297
|
return issue('the actor state must be a plain JSON tree')
|
|
295
298
|
}
|
|
299
|
+
const clearIndex = value['clearIndex']
|
|
300
|
+
if (
|
|
301
|
+
clearIndex !== undefined &&
|
|
302
|
+
(!Number.isSafeInteger(clearIndex) || (clearIndex as number) < 1)
|
|
303
|
+
) {
|
|
304
|
+
return issue('the actor clearIndex must be a positive safe integer')
|
|
305
|
+
}
|
|
296
306
|
return {
|
|
297
307
|
value: {
|
|
298
308
|
state: state as S,
|
|
299
309
|
event: value['event'],
|
|
300
310
|
message: value['message'],
|
|
311
|
+
...(clearIndex === undefined
|
|
312
|
+
? {}
|
|
313
|
+
: { clearIndex: clearIndex as number }),
|
|
301
314
|
},
|
|
302
315
|
}
|
|
303
316
|
})
|
|
@@ -348,9 +361,11 @@ export function actorReducer<S>(
|
|
|
348
361
|
const events: ActorClientEventDefs<S> = {
|
|
349
362
|
[ACTOR_STATE_EVENT]: actorStateSchema(),
|
|
350
363
|
}
|
|
351
|
-
return
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
364
|
+
return reducer({
|
|
365
|
+
contract: createContract({ name: options.name, events }),
|
|
366
|
+
name: ACTOR_REDUCER_NAME,
|
|
367
|
+
initialState: options.state,
|
|
368
|
+
}).fold((state, event) =>
|
|
369
|
+
event.type === ACTOR_STATE_EVENT ? event.payload.state : state,
|
|
370
|
+
)
|
|
356
371
|
}
|
package/src/actor.ts
CHANGED
|
@@ -71,14 +71,17 @@ import type {
|
|
|
71
71
|
} from './actor-shared.ts'
|
|
72
72
|
import { parsePresenceSibling } from './push-envelope.ts'
|
|
73
73
|
import type { ParsedPushPresence } from './push-envelope.ts'
|
|
74
|
-
import {
|
|
74
|
+
import { internalContract as createContract } from './contract.ts'
|
|
75
75
|
import type { PresencePatch, PresenceSnapshot } from './contract.ts'
|
|
76
76
|
import type { Contract, EventDefs } from './contract.ts'
|
|
77
77
|
import type { Reducer } from './reducer.ts'
|
|
78
78
|
import { A2Error, NonRetriableError } from './errors.ts'
|
|
79
|
+
import { serverInternals } from './internal.ts'
|
|
79
80
|
import { createServer } from './server.ts'
|
|
80
81
|
import { setServerFetchHooks } from './server-fetch.ts'
|
|
81
82
|
import { sseResponse } from './sse.ts'
|
|
83
|
+
import { isSessionCleared, sessionClearedError } from './session-clear.ts'
|
|
84
|
+
import type { SessionCleared } from './session-clear.ts'
|
|
82
85
|
import type { A2Scheduler, A2Server, HandlerEntry, Session } from './server.ts'
|
|
83
86
|
import type { A2Store } from './store.ts'
|
|
84
87
|
import type { StandardSchemaV1 } from './standard-schema.ts'
|
|
@@ -224,6 +227,11 @@ export type ActorCallResult<S> = {
|
|
|
224
227
|
state: S
|
|
225
228
|
/** Log index of the state commit that answered this message. */
|
|
226
229
|
index: number
|
|
230
|
+
/**
|
|
231
|
+
* The clear marker this state's lifetime began at, once the actor has been
|
|
232
|
+
* cleared. For a call answer it is the lifetime the handler read.
|
|
233
|
+
*/
|
|
234
|
+
clearIndex?: number
|
|
227
235
|
}
|
|
228
236
|
|
|
229
237
|
type CallMethod<
|
|
@@ -257,8 +265,10 @@ export type ActorHandle<D extends ActorProtocol> = {
|
|
|
257
265
|
readonly id: string
|
|
258
266
|
/**
|
|
259
267
|
* Send the event and await its answer: the state after the handler
|
|
260
|
-
* ran, or a rejection with `ActorRefusedError`.
|
|
261
|
-
*
|
|
268
|
+
* ran, or a rejection with `ActorRefusedError`. A clear before the call
|
|
269
|
+
* receives its answer rejects with `A2Error('SESSION_CLEARED')`. Serial
|
|
270
|
+
* events only — concurrent handlers produce no answer to await; `send`
|
|
271
|
+
* them.
|
|
262
272
|
*/
|
|
263
273
|
readonly call: { readonly [K in keyof D['events']]: CallMethod<D, K> }
|
|
264
274
|
/** Cast: validate, append the event durably, return without waiting. */
|
|
@@ -268,9 +278,10 @@ export type ActorHandle<D extends ActorProtocol> = {
|
|
|
268
278
|
/**
|
|
269
279
|
* The managed door — the actor mirror of `server.fetch`, bound to
|
|
270
280
|
* this instance. GET streams the state plane (`a2.actor.state`
|
|
271
|
-
* commits
|
|
272
|
-
* POST is the call lane `createActorClient` speaks
|
|
273
|
-
* input, messageId? }` → the answer, 409 for a refusal
|
|
281
|
+
* commits and clear resets, `?index` resume, heartbeat, deadline
|
|
282
|
+
* rotation); POST is the call lane `createActorClient` speaks
|
|
283
|
+
* (`{ event, input, messageId? }` → the answer, 409 for a refusal,
|
|
284
|
+
* 410 for a call cleared before its answer). Your route
|
|
274
285
|
* authenticates and picks the instance; `authorize` sees every
|
|
275
286
|
* operation. Custom endpoints are route code around this call.
|
|
276
287
|
*/
|
|
@@ -294,7 +305,7 @@ export type ActorDefinition<D extends ActorProtocol> = {
|
|
|
294
305
|
readonly contract: Contract<EventDefs>
|
|
295
306
|
/** The underlying A2 server — `server.fetch` mounts the full session wire. */
|
|
296
307
|
readonly server: A2Server<EventDefs>
|
|
297
|
-
/** The state fold
|
|
308
|
+
/** The state fold over commits; a clear resets it to the initial state. */
|
|
298
309
|
readonly reducer: Reducer<ActorClientEventDefs<D['state']>, D['state']>
|
|
299
310
|
actor(id: string): ActorHandle<D>
|
|
300
311
|
}
|
|
@@ -516,7 +527,7 @@ export function actor<D extends ActorProtocol>(
|
|
|
516
527
|
// an event-relative read would miss them and lose updates.
|
|
517
528
|
// 'latest' is deterministic here because the lane makes this
|
|
518
529
|
// attempt the only possible writer while it runs.
|
|
519
|
-
const { state } = await ctx.session.state(serverReducer, {
|
|
530
|
+
const { state, clearIndex } = await ctx.session.state(serverReducer, {
|
|
520
531
|
through: 'latest',
|
|
521
532
|
})
|
|
522
533
|
const draft = structuredClone(state)
|
|
@@ -565,6 +576,7 @@ export function actor<D extends ActorProtocol>(
|
|
|
565
576
|
state: draft,
|
|
566
577
|
event: eventName,
|
|
567
578
|
message: ctx.event.id,
|
|
579
|
+
...(clearIndex === undefined ? {} : { clearIndex }),
|
|
568
580
|
}
|
|
569
581
|
return [...sends, { type: ACTOR_STATE_EVENT, payload }]
|
|
570
582
|
},
|
|
@@ -600,6 +612,8 @@ export function actor<D extends ActorProtocol>(
|
|
|
600
612
|
},
|
|
601
613
|
})
|
|
602
614
|
|
|
615
|
+
const internals = serverInternals.get(server)!
|
|
616
|
+
|
|
603
617
|
const makeHandle = (id: string): ActorHandle<D> => {
|
|
604
618
|
const session = server.session(id)
|
|
605
619
|
|
|
@@ -634,12 +648,26 @@ export function actor<D extends ActorProtocol>(
|
|
|
634
648
|
// Drain participates in processing (usually answering inline in
|
|
635
649
|
// this invocation); claims arbitrate if another process runs.
|
|
636
650
|
await server.drain(id)
|
|
637
|
-
const tail = await
|
|
651
|
+
const tail = await internals.historyWithMarkers(id, cursor)
|
|
638
652
|
for (const event of tail) {
|
|
653
|
+
// Reads start at the latest marker, so a clear after the message
|
|
654
|
+
// arrives before any answer from the next lifetime, including one
|
|
655
|
+
// from a message that reuses this id.
|
|
656
|
+
if (isSessionCleared(event)) {
|
|
657
|
+
throw sessionClearedError(
|
|
658
|
+
`actor '${name}': '${eventName}' (message ${message.id}) was cleared before the call received its answer`,
|
|
659
|
+
)
|
|
660
|
+
}
|
|
639
661
|
if (event.type === ACTOR_STATE_EVENT) {
|
|
640
662
|
const payload = event.payload as ActorStatePayload<D['state']>
|
|
641
663
|
if (payload.message === message.id) {
|
|
642
|
-
return {
|
|
664
|
+
return {
|
|
665
|
+
state: payload.state,
|
|
666
|
+
index: event.index,
|
|
667
|
+
...(payload.clearIndex === undefined
|
|
668
|
+
? {}
|
|
669
|
+
: { clearIndex: payload.clearIndex }),
|
|
670
|
+
}
|
|
643
671
|
}
|
|
644
672
|
}
|
|
645
673
|
if (event.type === ACTOR_FAILED_EVENT) {
|
|
@@ -661,10 +689,19 @@ export function actor<D extends ActorProtocol>(
|
|
|
661
689
|
}
|
|
662
690
|
|
|
663
691
|
const readState = async (): Promise<ActorCallResult<D['state']>> => {
|
|
664
|
-
const { state, index } = await session.state(serverReducer)
|
|
665
|
-
return
|
|
692
|
+
const { state, index, clearIndex } = await session.state(serverReducer)
|
|
693
|
+
return clearIndex === undefined
|
|
694
|
+
? { state, index }
|
|
695
|
+
: { state, index, clearIndex }
|
|
666
696
|
}
|
|
667
697
|
|
|
698
|
+
/** A snapshot or call answer through the route's view. */
|
|
699
|
+
const projected = (
|
|
700
|
+
result: ActorCallResult<D['state']>,
|
|
701
|
+
view: ((state: D['state']) => unknown) | undefined,
|
|
702
|
+
): ActorCallResult<unknown> =>
|
|
703
|
+
view ? { ...result, state: applyView(view, result.state) } : result
|
|
704
|
+
|
|
668
705
|
const applyView = (
|
|
669
706
|
view: (state: D['state']) => unknown,
|
|
670
707
|
state: D['state'],
|
|
@@ -689,6 +726,7 @@ export function actor<D extends ActorProtocol>(
|
|
|
689
726
|
// advance the resume frontier.
|
|
690
727
|
const source: AsyncIterable<
|
|
691
728
|
| Awaited<ReturnType<typeof session.history>>[number]
|
|
729
|
+
| SessionCleared
|
|
692
730
|
| PresencePatch
|
|
693
731
|
| PresenceSnapshot
|
|
694
732
|
> = presenceEnabled
|
|
@@ -707,6 +745,16 @@ export function actor<D extends ActorProtocol>(
|
|
|
707
745
|
continue
|
|
708
746
|
}
|
|
709
747
|
const event = item
|
|
748
|
+
if (isSessionCleared(event)) {
|
|
749
|
+
// The follower has no seed of its own: the marker carries the
|
|
750
|
+
// actor's initial state, projected like any commit.
|
|
751
|
+
const initial = structuredClone(options.state)
|
|
752
|
+
yield {
|
|
753
|
+
...event,
|
|
754
|
+
payload: { state: view ? applyView(view, initial) : initial },
|
|
755
|
+
}
|
|
756
|
+
continue
|
|
757
|
+
}
|
|
710
758
|
// The state plane only: subscribers see what the actor is,
|
|
711
759
|
// never other callers' inputs. Refusals answer only their
|
|
712
760
|
// caller (the POST 409); the full mailbox stays server-side.
|
|
@@ -724,10 +772,11 @@ export function actor<D extends ActorProtocol>(
|
|
|
724
772
|
}
|
|
725
773
|
|
|
726
774
|
// The managed door — the actor mirror of `server.fetch`: GET is
|
|
727
|
-
// the state-plane SSE (`a2.actor.state` commits
|
|
728
|
-
// resume), POST is the call lane (`{ event, input,
|
|
729
|
-
// → the answer, 409 for a refusal
|
|
730
|
-
//
|
|
775
|
+
// the state-plane SSE (`a2.actor.state` commits and clear resets,
|
|
776
|
+
// `?index` resume), POST is the call lane (`{ event, input,
|
|
777
|
+
// messageId? }` → the answer, 409 for a refusal, 410 for a cleared
|
|
778
|
+
// call). Authentication happens in the surrounding route;
|
|
779
|
+
// per-operation policy in `authorize`.
|
|
731
780
|
const handleFetch = async (
|
|
732
781
|
request: Request,
|
|
733
782
|
fetchOptions?: ActorFetchOptions<D>,
|
|
@@ -739,15 +788,11 @@ export function actor<D extends ActorProtocol>(
|
|
|
739
788
|
const url = new URL(request.url)
|
|
740
789
|
if (url.searchParams.has('snapshot')) {
|
|
741
790
|
if (!(await authorized({ type: 'state', id }))) return forbidden()
|
|
742
|
-
const result = await readState()
|
|
743
791
|
return Response.json(
|
|
792
|
+
projected(await readState(), fetchOptions?.view),
|
|
744
793
|
{
|
|
745
|
-
|
|
746
|
-
? applyView(fetchOptions.view, result.state)
|
|
747
|
-
: result.state,
|
|
748
|
-
index: result.index,
|
|
794
|
+
headers: { 'cache-control': 'private, no-store' },
|
|
749
795
|
},
|
|
750
|
-
{ headers: { 'cache-control': 'private, no-store' } },
|
|
751
796
|
)
|
|
752
797
|
}
|
|
753
798
|
const raw = Number(url.searchParams.get('index'))
|
|
@@ -858,15 +903,9 @@ export function actor<D extends ActorProtocol>(
|
|
|
858
903
|
input,
|
|
859
904
|
messageId === undefined ? undefined : { id: messageId },
|
|
860
905
|
)
|
|
861
|
-
return Response.json(
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
state: applyView(fetchOptions.view, result.state),
|
|
865
|
-
index: result.index,
|
|
866
|
-
}
|
|
867
|
-
: result,
|
|
868
|
-
{ headers: { 'cache-control': 'private, no-store' } },
|
|
869
|
-
)
|
|
906
|
+
return Response.json(projected(result, fetchOptions?.view), {
|
|
907
|
+
headers: { 'cache-control': 'private, no-store' },
|
|
908
|
+
})
|
|
870
909
|
} catch (error) {
|
|
871
910
|
if (error instanceof ActorRefusedError) {
|
|
872
911
|
return Response.json(
|
|
@@ -878,6 +917,12 @@ export function actor<D extends ActorProtocol>(
|
|
|
878
917
|
{ status: 409 },
|
|
879
918
|
)
|
|
880
919
|
}
|
|
920
|
+
if (error instanceof A2Error && error.code === 'SESSION_CLEARED') {
|
|
921
|
+
return Response.json(
|
|
922
|
+
{ code: error.code, error: error.message },
|
|
923
|
+
{ status: 410 },
|
|
924
|
+
)
|
|
925
|
+
}
|
|
881
926
|
if (
|
|
882
927
|
error instanceof A2Error &&
|
|
883
928
|
(error.code === 'INVALID_PAYLOAD' ||
|