experimental-a2 0.19.0 → 0.21.1
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 +50 -0
- package/README.md +1 -1
- package/dist/{actor-C7b-1arn.d.ts → actor-HnGmrOZZ.d.ts} +5 -5
- package/dist/{actor-C7b-1arn.d.ts.map → actor-HnGmrOZZ.d.ts.map} +1 -1
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-client.js +2 -2
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor-react.js +5 -5
- package/dist/{actor-shared-t7w8xIKP.js → actor-shared-DFET5q16.js} +3 -3
- package/dist/{actor-shared-t7w8xIKP.js.map → actor-shared-DFET5q16.js.map} +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +54 -43
- package/dist/actor.js.map +1 -1
- package/dist/{ai-Cz3hqVhk.d.ts → ai-CvEnqVfo.d.ts} +8 -8
- package/dist/{ai-Cz3hqVhk.d.ts.map → ai-CvEnqVfo.d.ts.map} +1 -1
- package/dist/{ai-mMiWul1T.js → ai-CzE672Q3.js} +9 -9
- package/dist/ai-CzE672Q3.js.map +1 -0
- package/dist/ai-client.d.ts +2 -2
- package/dist/ai-client.js +2 -2
- package/dist/ai-fx.d.ts +1 -1
- package/dist/ai-fx.d.ts.map +1 -1
- package/dist/ai-fx.js +36 -9
- package/dist/ai-fx.js.map +1 -1
- package/dist/{ai-harness-CV6rXbB3.d.ts → ai-harness-Bfk-Lycn.d.ts} +2 -2
- package/dist/ai-harness-Bfk-Lycn.d.ts.map +1 -0
- package/dist/{ai-harness-BuuC-gJ3.js → ai-harness-dQeJaR1t.js} +17 -4
- package/dist/ai-harness-dQeJaR1t.js.map +1 -0
- package/dist/ai-pi.d.ts +1 -1
- package/dist/ai-pi.js +4 -4
- package/dist/ai-pi.js.map +1 -1
- package/dist/ai-react.d.ts +5 -5
- package/dist/ai-react.js +2 -2
- package/dist/ai-server.d.ts +14 -17
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +84 -75
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/{client-BhHLoNKF.js → client-9IeTsiAs.js} +5 -5
- package/dist/{client-BhHLoNKF.js.map → client-9IeTsiAs.js.map} +1 -1
- package/dist/{client-h_DKFAjj.d.ts → client-CUMbS_38.d.ts} +3 -3
- package/dist/{client-h_DKFAjj.d.ts.map → client-CUMbS_38.d.ts.map} +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/{contract-BYKASlCV.js → contract-CWukt01d.js} +2 -2
- package/dist/contract-CWukt01d.js.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/{initial-snapshot-Bx-c0pDP.d.ts → initial-snapshot-D3Lve43o.d.ts} +4 -4
- package/dist/initial-snapshot-D3Lve43o.d.ts.map +1 -0
- package/dist/{internal-CscSY7K_.js → internal-Bc4PvfHk.js} +10 -3
- package/dist/internal-Bc4PvfHk.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/otel.js +1 -1
- package/dist/otel.js.map +1 -1
- package/dist/{react-BBXgHWFc.d.ts → react-BoHvC3v9.d.ts} +4 -4
- package/dist/{react-BBXgHWFc.d.ts.map → react-BoHvC3v9.d.ts.map} +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{reducer-CqA59Q6U.d.ts → reducer-CYPHsSmd.d.ts} +2 -2
- package/dist/{reducer-CqA59Q6U.d.ts.map → reducer-CYPHsSmd.d.ts.map} +1 -1
- package/dist/{reducer-BAXc8JyF.js → reducer-CjmvZSAb.js} +2 -2
- package/dist/{reducer-BAXc8JyF.js.map → reducer-CjmvZSAb.js.map} +1 -1
- package/dist/runtime-consumer-BWmht60z.js +12 -0
- package/dist/runtime-consumer-BWmht60z.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.d.ts.map +1 -1
- package/dist/scheduler-qstash.js +7 -7
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/scheduler-task-X1SToDjP.js.map +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.d.ts.map +1 -1
- package/dist/scheduler-vercel.js +5 -4
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-DYAATFuT.d.ts → server-BshFZ0eL.d.ts} +108 -41
- package/dist/server-BshFZ0eL.d.ts.map +1 -0
- package/dist/{server-D-Y4pqwu.js → server-DvIB8kj5.js} +423 -135
- package/dist/server-DvIB8kj5.js.map +1 -0
- package/dist/server.browser.js +1 -1
- package/dist/server.browser.js.map +1 -1
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/{session-clear-C-QwTXV1.d.ts → session-clear-Bk_29eEw.d.ts} +2 -2
- package/dist/session-clear-Bk_29eEw.d.ts.map +1 -0
- package/dist/{session-clear-DGI4jTQ5.js → session-clear-wHd5GCpZ.js} +5 -4
- package/dist/session-clear-wHd5GCpZ.js.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-Z2qjw50n.d.ts +523 -0
- package/dist/store-Z2qjw50n.d.ts.map +1 -0
- package/dist/store-codec-DwrWlgQx.js +65 -0
- package/dist/store-codec-DwrWlgQx.js.map +1 -0
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +393 -210
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-7WJV0YWP.js → store-polling-DYNyxCev.js} +2 -2
- package/dist/{store-polling-7WJV0YWP.js.map → store-polling-DYNyxCev.js.map} +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +1595 -673
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-B1ixeqIS.js → store-presence-polling-CzQ0wdtM.js} +3 -3
- package/dist/{store-presence-polling-B1ixeqIS.js.map → store-presence-polling-CzQ0wdtM.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +4 -4
- package/dist/store-redis-notify-IiVdlYHc.js +2919 -0
- package/dist/store-redis-notify-IiVdlYHc.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +2 -2
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +517 -287
- package/dist/store-sqlite.js.map +1 -1
- package/dist/{telemetry-Cyfqo6Qt.d.ts → telemetry-CWwsh-Gx.d.ts} +3 -3
- package/dist/telemetry-CWwsh-Gx.d.ts.map +1 -0
- package/dist/{wire-BKxH1vbP.js → wire-CZGqNZRx.js} +5 -4
- package/dist/wire-CZGqNZRx.js.map +1 -0
- package/docs/01-quickstart.mdx +28 -19
- package/docs/02-examples.mdx +1 -1
- package/docs/actors/01-introduction.mdx +9 -1
- package/docs/actors/02-concurrency.mdx +1 -1
- package/docs/actors/meta.ts +1 -1
- package/docs/agents/01-introduction.mdx +1 -1
- package/docs/agents/02-tools.mdx +13 -12
- package/docs/agents/03-conversations.mdx +31 -21
- package/docs/agents/04-harnesses.mdx +7 -3
- package/docs/agents/meta.ts +1 -1
- package/docs/client/03-presence.mdx +18 -14
- package/docs/client/meta.ts +1 -1
- package/docs/contract/01-events.mdx +2 -2
- package/docs/contract/02-state.mdx +18 -14
- package/docs/contract/03-sessions.mdx +32 -25
- package/docs/index.mdx +31 -26
- package/docs/reference/01-core.mdx +12 -12
- package/docs/reference/02-server.mdx +247 -192
- package/docs/reference/04-ai.mdx +29 -20
- package/docs/reference/05-telemetry.mdx +146 -0
- package/docs/reference/06-errors.mdx +24 -18
- package/docs/reference/meta.ts +1 -1
- package/docs/schedulers/01-overview.mdx +112 -0
- package/docs/schedulers/02-vercel-queues.mdx +87 -0
- package/docs/schedulers/03-qstash.mdx +123 -0
- package/docs/schedulers/meta.ts +1 -0
- package/docs/server/01-consumers.mdx +415 -0
- package/docs/server/02-timers.mdx +33 -23
- package/docs/server/03-cancellation.mdx +52 -44
- package/docs/server/04-routes.mdx +4 -1
- package/docs/server/05-sql-projections.mdx +391 -0
- package/docs/server/06-guarantees.mdx +60 -50
- package/docs/stores/01-overview.mdx +37 -0
- package/docs/stores/02-redis.mdx +57 -0
- package/docs/stores/03-redis-http.mdx +56 -0
- package/docs/stores/04-postgres.mdx +172 -0
- package/docs/stores/05-sqlite.mdx +36 -0
- package/docs/stores/06-memory.mdx +22 -0
- package/docs/stores/meta.ts +1 -0
- package/docs/upgrades/01-overview.mdx +30 -0
- package/docs/upgrades/79-0-21.mdx +165 -0
- package/docs/upgrades/80-0-20.mdx +43 -0
- package/docs/upgrades/81-0-19.mdx +33 -0
- package/docs/upgrades/82-0-18.mdx +33 -0
- package/docs/upgrades/83-0-17.mdx +69 -0
- package/docs/upgrades/meta.ts +3 -0
- package/examples/playground/DEPLOY.md +48 -11
- package/examples/playground/app/a0/apps/[appId]/route.ts +6 -10
- package/examples/playground/app/a0/chat.test.tsx +421 -330
- package/examples/playground/app/a0/sandbox-log.ts +17 -15
- package/examples/playground/app/a0/server.ts +4 -4
- package/examples/playground/app/a0/shell.tsx +1 -1
- package/examples/playground/app/a0/uploads/route.test.ts +59 -0
- package/examples/playground/app/a2slack/a2slack.css +1067 -0
- package/examples/playground/app/a2slack/c/[channel]/page.tsx +20 -0
- package/examples/playground/app/a2slack/consumers/agent.ts +63 -0
- package/examples/playground/app/a2slack/consumers/feed.ts +164 -0
- package/examples/playground/app/a2slack/consumers/notify.ts +116 -0
- package/examples/playground/app/a2slack/consumers/search.ts +37 -0
- package/examples/playground/app/a2slack/consumers/triage.ts +31 -0
- package/examples/playground/app/a2slack/consumers/welcome.ts +25 -0
- package/examples/playground/app/a2slack/db/a2.ts +206 -0
- package/examples/playground/app/a2slack/db/index.ts +73 -0
- package/examples/playground/app/a2slack/db/migrations/0000_extensions.sql +4 -0
- package/examples/playground/app/a2slack/db/migrations/0001_workspace.sql +74 -0
- package/examples/playground/app/a2slack/db/migrations/0002_a2.sql +215 -0
- package/examples/playground/app/a2slack/db/migrations/0003_a2_batch.sql +67 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0000_snapshot.json +18 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0001_snapshot.json +500 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0002_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0003_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/_journal.json +34 -0
- package/examples/playground/app/a2slack/db/queries.ts +263 -0
- package/examples/playground/app/a2slack/db/schema.ts +145 -0
- package/examples/playground/app/a2slack/drills/a2slack.drill.ts +708 -0
- package/examples/playground/app/a2slack/drills/harness.ts +244 -0
- package/examples/playground/app/a2slack/events/route.ts +36 -0
- package/examples/playground/app/a2slack/identity.ts +44 -0
- package/examples/playground/app/a2slack/inbox/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/data/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/page.tsx +101 -0
- package/examples/playground/app/a2slack/lag.ts +73 -0
- package/examples/playground/app/a2slack/layout.tsx +43 -0
- package/examples/playground/app/a2slack/messages/route.ts +17 -0
- package/examples/playground/app/a2slack/model.test.ts +208 -0
- package/examples/playground/app/a2slack/model.ts +369 -0
- package/examples/playground/app/a2slack/models.test.ts +72 -0
- package/examples/playground/app/a2slack/page.tsx +10 -0
- package/examples/playground/app/a2slack/scheduler/route.ts +5 -0
- package/examples/playground/app/a2slack/search/route.ts +40 -0
- package/examples/playground/app/a2slack/search-index.ts +108 -0
- package/examples/playground/app/a2slack/server.test.ts +315 -0
- package/examples/playground/app/a2slack/server.ts +55 -0
- package/examples/playground/app/a2slack/sessions.ts +50 -0
- package/examples/playground/app/a2slack/thread/route.ts +8 -0
- package/examples/playground/app/a2slack/ui/ChannelView.tsx +87 -0
- package/examples/playground/app/a2slack/ui/Composer.tsx +117 -0
- package/examples/playground/app/a2slack/ui/DotsShadow.tsx +42 -0
- package/examples/playground/app/a2slack/ui/Feed.tsx +157 -0
- package/examples/playground/app/a2slack/ui/InboxContext.tsx +63 -0
- package/examples/playground/app/a2slack/ui/InboxView.tsx +82 -0
- package/examples/playground/app/a2slack/ui/Menu.tsx +128 -0
- package/examples/playground/app/a2slack/ui/MessageRow.tsx +314 -0
- package/examples/playground/app/a2slack/ui/NameGate.tsx +35 -0
- package/examples/playground/app/a2slack/ui/SearchContext.tsx +146 -0
- package/examples/playground/app/a2slack/ui/SearchResults.tsx +97 -0
- package/examples/playground/app/a2slack/ui/Shell.tsx +34 -0
- package/examples/playground/app/a2slack/ui/Sidebar.tsx +199 -0
- package/examples/playground/app/a2slack/ui/ThreadPanel.tsx +134 -0
- package/examples/playground/app/a2slack/ui/useInbox.ts +71 -0
- package/examples/playground/app/agent/[agentId]/agent-queue.test.tsx +116 -90
- package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +107 -76
- package/examples/playground/app/agent/ingress.test.ts +41 -17
- package/examples/playground/app/agent/model-input.test.tsx +92 -43
- package/examples/playground/app/agent/server.ts +3 -3
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.test.tsx +2 -2
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +1 -1
- package/examples/playground/app/chat/server.test.ts +38 -65
- package/examples/playground/app/chat/server.ts +45 -35
- package/examples/playground/app/components/activity-history.test.tsx +60 -18
- package/examples/playground/app/components/demo-icons.tsx +7 -0
- package/examples/playground/app/components/demos.tsx +7 -0
- package/examples/playground/app/components/fast-link.test.tsx +3 -5
- package/examples/playground/app/initial-state.test.tsx +86 -45
- package/examples/playground/app/orders/[orderId]/order-client.tsx +2 -2
- package/examples/playground/app/orders/page.tsx +1 -1
- package/examples/playground/app/orders/server.ts +49 -30
- package/examples/playground/app/recovery/[recoveryId]/recovery-client.tsx +14 -15
- package/examples/playground/app/recovery/page.tsx +4 -4
- package/examples/playground/app/recovery/server.ts +23 -32
- package/examples/playground/drizzle.config.ts +14 -0
- package/examples/playground/next.config.ts +6 -1
- package/examples/playground/package.json +13 -1
- package/examples/playground/scripts/migrate.mjs +31 -0
- package/examples/playground/test/agent.ts +35 -0
- package/examples/playground/test/model.ts +149 -0
- package/examples/playground/test/serve.ts +184 -0
- package/examples/playground/test/server-only.ts +1 -0
- package/examples/playground/vercel.json +9 -0
- package/examples/playground/vitest.config.ts +27 -0
- package/examples/playground/vitest.drills.config.ts +32 -0
- package/package.json +7 -2
- package/sql/postgres/drizzle-schema.ts +206 -0
- package/sql/postgres/notifications.sql +24 -0
- package/sql/postgres/schema.sql +276 -0
- package/src/actor.ts +101 -71
- package/src/ai-agent.ts +154 -115
- package/src/ai-control-server.ts +8 -8
- package/src/ai-fx.ts +41 -9
- package/src/ai-harness.ts +28 -2
- package/src/ai-pi.ts +2 -2
- package/src/ai-server.ts +2 -2
- package/src/ai.ts +7 -7
- package/src/contract.ts +1 -1
- package/src/deterministic-id.ts +26 -3
- package/src/errors.ts +9 -6
- package/src/idempotent-replay.ts +0 -1
- package/src/index.ts +1 -1
- package/src/internal.ts +17 -6
- package/src/otel.ts +1 -1
- package/src/postgres-notifications.ts +0 -22
- package/src/postgres-resources.ts +6 -9
- package/src/postgres-schema.ts +121 -0
- package/src/push-envelope.ts +20 -0
- package/src/runtime-consumer.ts +10 -0
- package/src/scheduler-qstash.ts +12 -5
- package/src/scheduler-task.ts +2 -2
- package/src/scheduler-vercel.ts +10 -4
- package/src/server.browser.ts +1 -1
- package/src/server.ts +839 -238
- package/src/session-socket.ts +1 -1
- package/src/store-codec.ts +66 -0
- package/src/store-memory.ts +581 -255
- package/src/store-postgres.ts +1984 -943
- package/src/store-redis-core.ts +1624 -1152
- package/src/store-redis-notify.ts +2 -0
- package/src/store-sqlite.ts +891 -486
- package/src/store.ts +227 -97
- package/src/telemetry.ts +2 -1
- package/src/wire.ts +1 -0
- package/dist/ai-harness-BuuC-gJ3.js.map +0 -1
- package/dist/ai-harness-CV6rXbB3.d.ts.map +0 -1
- package/dist/ai-mMiWul1T.js.map +0 -1
- package/dist/contract-BYKASlCV.js.map +0 -1
- package/dist/idempotent-replay-JdgMQRRm.js +0 -21
- package/dist/idempotent-replay-JdgMQRRm.js.map +0 -1
- package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +0 -1
- package/dist/internal-CscSY7K_.js.map +0 -1
- package/dist/server-D-Y4pqwu.js.map +0 -1
- package/dist/server-DYAATFuT.d.ts.map +0 -1
- package/dist/session-clear-C-QwTXV1.d.ts.map +0 -1
- package/dist/session-clear-DGI4jTQ5.js.map +0 -1
- package/dist/store-CyLUy86v.d.ts +0 -404
- package/dist/store-CyLUy86v.d.ts.map +0 -1
- package/dist/store-codec-DTG0Ftek.js +0 -10
- package/dist/store-codec-DTG0Ftek.js.map +0 -1
- package/dist/store-redis-notify-CM0AfLY0.js +0 -2544
- package/dist/store-redis-notify-CM0AfLY0.js.map +0 -1
- package/dist/telemetry-Cyfqo6Qt.d.ts.map +0 -1
- package/dist/wire-BKxH1vbP.js.map +0 -1
- package/docs/reference/05-adapters.mdx +0 -351
- package/docs/server/01-handlers.mdx +0 -200
- package/docs/server/05-production.mdx +0 -323
package/src/actor.ts
CHANGED
|
@@ -82,7 +82,14 @@ import { setServerFetchHooks } from './server-fetch.ts'
|
|
|
82
82
|
import { sseResponse } from './sse.ts'
|
|
83
83
|
import { isSessionCleared, sessionClearedError } from './session-clear.ts'
|
|
84
84
|
import type { SessionCleared } from './session-clear.ts'
|
|
85
|
-
import type {
|
|
85
|
+
import type {
|
|
86
|
+
A2Scheduler,
|
|
87
|
+
A2Server,
|
|
88
|
+
ConsumerContext,
|
|
89
|
+
ConsumerResult,
|
|
90
|
+
Session,
|
|
91
|
+
} from './server.ts'
|
|
92
|
+
import { runtimeConsumer } from './runtime-consumer.ts'
|
|
86
93
|
import type { A2Store } from './store.ts'
|
|
87
94
|
import type { StandardSchemaV1 } from './standard-schema.ts'
|
|
88
95
|
import type { A2Telemetry } from './telemetry.ts'
|
|
@@ -460,14 +467,16 @@ export function actor<D extends ActorProtocol>(
|
|
|
460
467
|
return send as ActorSend<D['events']>
|
|
461
468
|
}
|
|
462
469
|
|
|
463
|
-
|
|
470
|
+
type Run = (ctx: ConsumerContext<EventDefs>) => ConsumerResult<EventDefs>
|
|
471
|
+
const concurrentRuns = new Map<string, Run>()
|
|
472
|
+
const serialRuns = new Map<string, Run>()
|
|
464
473
|
for (const [eventName, def] of handlersByEvent) {
|
|
465
474
|
if (def.kind === 'concurrent') {
|
|
466
475
|
// No lane: concurrent handlers run in parallel — with the lane
|
|
467
476
|
// and with each other. They cannot write state (no draft to
|
|
468
477
|
// commit), so no serialization is needed for correctness; slow
|
|
469
478
|
// I/O lives here without blocking the actor.
|
|
470
|
-
|
|
479
|
+
concurrentRuns.set(eventName, async (ctx) => {
|
|
471
480
|
const sends: { type: string; payload: unknown }[] = []
|
|
472
481
|
try {
|
|
473
482
|
await def.handle(
|
|
@@ -509,78 +518,70 @@ export function actor<D extends ActorProtocol>(
|
|
|
509
518
|
}
|
|
510
519
|
// Sends commit atomically with settlement; re-runs converge on
|
|
511
520
|
// the same deterministic message ids.
|
|
512
|
-
return sends
|
|
513
|
-
}
|
|
521
|
+
return sends as never
|
|
522
|
+
})
|
|
514
523
|
continue
|
|
515
524
|
}
|
|
516
|
-
|
|
517
|
-
//
|
|
518
|
-
//
|
|
519
|
-
//
|
|
520
|
-
//
|
|
521
|
-
//
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
signal: ctx.signal,
|
|
542
|
-
send: makeSend(sends),
|
|
543
|
-
schedule: makeSchedule((taskName, timing, event) =>
|
|
544
|
-
ctx.session.schedule(
|
|
545
|
-
taskName,
|
|
546
|
-
timing as Parameters<typeof ctx.session.schedule>[1],
|
|
547
|
-
event,
|
|
548
|
-
),
|
|
525
|
+
serialRuns.set(eventName, async (ctx) => {
|
|
526
|
+
// Explicitly 'latest', not the event-relative default: prior
|
|
527
|
+
// lane messages commit *after* this message was appended, so
|
|
528
|
+
// their state commits carry higher indexes than the trigger —
|
|
529
|
+
// an event-relative read would miss them and lose updates.
|
|
530
|
+
// 'latest' is deterministic here because the lane makes this
|
|
531
|
+
// attempt the only possible writer while it runs.
|
|
532
|
+
const { state, clearIndex } = await ctx.session.state(serverReducer, {
|
|
533
|
+
through: 'latest',
|
|
534
|
+
})
|
|
535
|
+
const draft = structuredClone(state)
|
|
536
|
+
const sends: { type: string; payload: unknown }[] = []
|
|
537
|
+
try {
|
|
538
|
+
await def.handle(
|
|
539
|
+
{
|
|
540
|
+
state: draft,
|
|
541
|
+
id: ctx.event.id,
|
|
542
|
+
attempt: ctx.attempt,
|
|
543
|
+
signal: ctx.signal,
|
|
544
|
+
send: makeSend(sends),
|
|
545
|
+
schedule: makeSchedule((taskName, timing, event) =>
|
|
546
|
+
ctx.session.schedule(
|
|
547
|
+
taskName,
|
|
548
|
+
timing as Parameters<typeof ctx.session.schedule>[1],
|
|
549
|
+
event,
|
|
549
550
|
),
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
event: eventName,
|
|
566
|
-
error: describeError(error),
|
|
567
|
-
}
|
|
568
|
-
return { type: ACTOR_FAILED_EVENT, payload }
|
|
569
|
-
}
|
|
570
|
-
// One returned batch: buffered sends plus the state commit —
|
|
571
|
-
// committed atomically with completion, so a reserve and its
|
|
572
|
-
// trigger can never half-happen. The state event stays the
|
|
573
|
-
// outcome marker a waiting call resolves on. (Unconditional
|
|
574
|
-
// for now; a dirty check is a compatible later optimization.)
|
|
575
|
-
const payload: ActorStatePayload<D['state']> = {
|
|
576
|
-
state: draft,
|
|
577
|
-
event: eventName,
|
|
551
|
+
),
|
|
552
|
+
},
|
|
553
|
+
ctx.event.payload,
|
|
554
|
+
)
|
|
555
|
+
} catch (error) {
|
|
556
|
+
// A NonRetriableError is an answer: settle the message,
|
|
557
|
+
// record the refusal, deliver it to the waiting caller —
|
|
558
|
+
// no retry, no failure budget spent (buffered sends are
|
|
559
|
+
// discarded with the draft: refusals are total). Every
|
|
560
|
+
// other throw is an ordinary A2 handler failure: rethrown
|
|
561
|
+
// into core's retry machinery (backoff, ten-failure
|
|
562
|
+
// budget, dead letter) — including surrendered lease
|
|
563
|
+
// aborts, which core recognizes by identity.
|
|
564
|
+
if (!(error instanceof NonRetriableError)) throw error
|
|
565
|
+
const payload: ActorFailedPayload = {
|
|
578
566
|
message: ctx.event.id,
|
|
579
|
-
|
|
567
|
+
event: eventName,
|
|
568
|
+
error: describeError(error),
|
|
580
569
|
}
|
|
581
|
-
return
|
|
582
|
-
}
|
|
583
|
-
|
|
570
|
+
return { type: ACTOR_FAILED_EVENT, payload }
|
|
571
|
+
}
|
|
572
|
+
// One returned batch: buffered sends plus the state commit —
|
|
573
|
+
// committed atomically with completion, so a reserve and its
|
|
574
|
+
// trigger can never half-happen. The state event stays the
|
|
575
|
+
// outcome marker a waiting call resolves on. (Unconditional
|
|
576
|
+
// for now; a dirty check is a compatible later optimization.)
|
|
577
|
+
const payload: ActorStatePayload<D['state']> = {
|
|
578
|
+
state: draft,
|
|
579
|
+
event: eventName,
|
|
580
|
+
message: ctx.event.id,
|
|
581
|
+
...(clearIndex === undefined ? {} : { clearIndex }),
|
|
582
|
+
}
|
|
583
|
+
return [...sends, { type: ACTOR_STATE_EVENT, payload }] as never
|
|
584
|
+
})
|
|
584
585
|
}
|
|
585
586
|
|
|
586
587
|
const presenceEnabled = (options as { presence?: unknown }).presence === true
|
|
@@ -590,9 +591,38 @@ export function actor<D extends ActorProtocol>(
|
|
|
590
591
|
const contract = presenceEnabled
|
|
591
592
|
? createContract({ name, events, presence: openPresenceDefs })
|
|
592
593
|
: createContract({ name, events })
|
|
594
|
+
// Serial messages share one lane per instance: the next starts only after
|
|
595
|
+
// the previous one completed, and completion commits the state event
|
|
596
|
+
// atomically, so read-modify-write on `ctx.state` is safe by exclusion,
|
|
597
|
+
// not by liveness. The lane is a function, so each message is queued on
|
|
598
|
+
// it as it's appended. Concurrent messages run alongside it and each
|
|
599
|
+
// other: they write no state.
|
|
600
|
+
const serialTypes = [...serialRuns.keys()]
|
|
601
|
+
const concurrentTypes = [...concurrentRuns.keys()]
|
|
602
|
+
const consumers = [
|
|
603
|
+
...(serialTypes.length === 0
|
|
604
|
+
? []
|
|
605
|
+
: [
|
|
606
|
+
runtimeConsumer({
|
|
607
|
+
contract,
|
|
608
|
+
name: 'a2.actor',
|
|
609
|
+
types: serialTypes,
|
|
610
|
+
lane: () => ACTOR_LANE,
|
|
611
|
+
}).run((ctx) => serialRuns.get(ctx.event.type)!(ctx as never)),
|
|
612
|
+
]),
|
|
613
|
+
...(concurrentTypes.length === 0
|
|
614
|
+
? []
|
|
615
|
+
: [
|
|
616
|
+
runtimeConsumer({
|
|
617
|
+
contract,
|
|
618
|
+
name: 'a2.actor.concurrent',
|
|
619
|
+
types: concurrentTypes,
|
|
620
|
+
}).run((ctx) => concurrentRuns.get(ctx.event.type)!(ctx as never)),
|
|
621
|
+
]),
|
|
622
|
+
]
|
|
593
623
|
const server = createServer({
|
|
594
624
|
contract,
|
|
595
|
-
|
|
625
|
+
consumers,
|
|
596
626
|
...(options.store ? { store: options.store } : {}),
|
|
597
627
|
...(options.scheduler ? { scheduler: options.scheduler } : {}),
|
|
598
628
|
...(options.telemetry ? { telemetry: options.telemetry } : {}),
|