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/store.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The A2Store interface — the storage contract every store backend
|
|
3
3
|
* implements. See specs/a2-implementation.md §2–3.
|
|
4
4
|
*
|
|
5
|
-
* This is the whole storage contract: append, read,
|
|
5
|
+
* This is the whole storage contract: append, read, consumer cursors,
|
|
6
6
|
* failure markers, snapshots, and the live stream. The scheduler
|
|
7
7
|
* needs nothing extra — the armed queue message is its own state, and
|
|
8
8
|
* the store is the only thing it consults.
|
|
@@ -21,58 +21,102 @@ export type Event = {
|
|
|
21
21
|
createdAt: Date
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/** The
|
|
24
|
+
/** The consumer attempt whose append first persisted a child event. */
|
|
25
25
|
export type EventCause = {
|
|
26
|
+
consumer: string
|
|
27
|
+
/** The cursor's lane; null for a concurrent consumer's cursor of one event. */
|
|
28
|
+
lane: string | null
|
|
26
29
|
index: number
|
|
27
30
|
attempt: number
|
|
28
|
-
/** Size of one named
|
|
31
|
+
/** Size of one named consumer append, used to reject truncated retries. */
|
|
29
32
|
batchSize?: number
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
/**
|
|
33
|
-
* What the store persists: immutable event history, including its causal edge,
|
|
34
|
-
* plus derived dispatch and failure bookkeeping. The bookkeeping is disposable;
|
|
35
|
-
* the event and its cause are not.
|
|
36
|
-
*/
|
|
35
|
+
/** What the store persists: immutable event history and its causal edge. */
|
|
37
36
|
export type StoredEvent = Event & {
|
|
38
|
-
/** Same-session
|
|
37
|
+
/** Same-session consumer attempt that appended this event; null means root. */
|
|
39
38
|
cause: EventCause | null
|
|
40
|
-
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Where a consumer's cursor stands on its head event. A cursor is one record
|
|
43
|
+
* per (session, consumer, lane): a log cursor for a consumer with one string
|
|
44
|
+
* lane, a lane cursor per lane for a lane function, and a cursor per event
|
|
45
|
+
* (lane null) for a concurrent consumer. Its attempt and failure fields
|
|
46
|
+
* describe the current head and reset when the cursor moves past it.
|
|
47
|
+
*/
|
|
48
|
+
export type HeadDispatch = {
|
|
49
|
+
consumer: string
|
|
41
50
|
lane: string | null
|
|
42
|
-
/** Adapter clock time recorded by
|
|
43
|
-
processedAt: Date | null
|
|
44
|
-
/** Dispatch attempt that completed this event; null without dispatch or while pending. */
|
|
45
|
-
processedByAttempt: number | null
|
|
46
|
-
/** Exact ordered child ids atomically returned by the completing attempt. */
|
|
47
|
-
returnedEventIds: string[] | null
|
|
48
|
-
/** Adapter clock time recorded by the first durable dispatch claim. */
|
|
51
|
+
/** Adapter clock time recorded by the first durable claim of this head. */
|
|
49
52
|
firstClaimedAt: Date | null
|
|
50
|
-
/** Adapter clock time recorded by the most recent durable
|
|
53
|
+
/** Adapter clock time recorded by the most recent durable claim of this head. */
|
|
51
54
|
lastClaimedAt: Date | null
|
|
52
|
-
/** Durable
|
|
55
|
+
/** Durable claims of this head, including claims abandoned by hard kills. */
|
|
53
56
|
attemptCount: number
|
|
54
|
-
/** Current
|
|
57
|
+
/** Current holder; null when the head is not claimed. */
|
|
55
58
|
claimHolder: string | null
|
|
56
59
|
/**
|
|
57
|
-
* Adapter clock expiry for the current
|
|
58
|
-
*
|
|
60
|
+
* Adapter clock expiry for the current claim, or, with no holder, the end
|
|
61
|
+
* of a failed attempt's retry hold.
|
|
59
62
|
*/
|
|
60
63
|
claimExpiresAt: Date | null
|
|
61
64
|
/**
|
|
62
|
-
* Failed attempts: caught
|
|
63
|
-
* took over. This alone drives dead-lettering.
|
|
65
|
+
* Failed attempts on this head: caught failures, and lapsed claims a later
|
|
66
|
+
* claim took over. This alone drives dead-lettering.
|
|
64
67
|
*/
|
|
65
68
|
failureCount: number
|
|
66
|
-
/** Adapter clock time recorded by the most recent caught
|
|
69
|
+
/** Adapter clock time recorded by the most recent caught failure. */
|
|
67
70
|
lastFailedAt: Date | null
|
|
68
|
-
/**
|
|
71
|
+
/** Attempt that produced the most recent caught failure. */
|
|
69
72
|
lastFailedAttempt: number | null
|
|
70
|
-
/** The last
|
|
73
|
+
/** The last failure, stringified. */
|
|
71
74
|
lastError: string | null
|
|
72
75
|
/** Adapter clock time recorded when dead-lettered; null otherwise. */
|
|
73
76
|
failedAt: Date | null
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Where one cursor stands: its position, its head, and the head's dispatch state. */
|
|
80
|
+
export type StoredCursor = HeadDispatch & {
|
|
81
|
+
/** The last index this cursor finished; a log cursor counts from the latest clear marker. */
|
|
82
|
+
position: number
|
|
83
|
+
/** The event it runs next; null when it has nothing to run. */
|
|
84
|
+
head: number | null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** A cursor's head event together with its dispatch state. */
|
|
88
|
+
export type HeadEvent = StoredEvent &
|
|
89
|
+
HeadDispatch & {
|
|
90
|
+
/** Latest clear marker, on claimed heads of a cleared session. */
|
|
91
|
+
clearIndex?: number
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Names one cursor head: the consumer, its lane, and the head's index. */
|
|
95
|
+
export type HeadRef = {
|
|
96
|
+
consumer: string
|
|
97
|
+
lane: string | null
|
|
98
|
+
index: number
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A consumer the caller serves. Work for consumers it does not name is
|
|
103
|
+
* neither pending nor claimed, so a removed consumer's cursors wait for its
|
|
104
|
+
* name to return.
|
|
105
|
+
*/
|
|
106
|
+
export type StoreConsumer = {
|
|
107
|
+
name: string
|
|
108
|
+
/**
|
|
109
|
+
* Set for a consumer with one string lane. Its cursor keeps one position
|
|
110
|
+
* over the log and runs the next event whose type is in `types` (null:
|
|
111
|
+
* every type). A missing cursor is created by the first append that
|
|
112
|
+
* carries one of those types: at the start of the log with `backfill`,
|
|
113
|
+
* otherwise just before that append.
|
|
114
|
+
*/
|
|
115
|
+
log?: {
|
|
116
|
+
lane: string
|
|
117
|
+
types: readonly string[] | null
|
|
118
|
+
backfill: boolean
|
|
119
|
+
}
|
|
76
120
|
}
|
|
77
121
|
|
|
78
122
|
/**
|
|
@@ -106,8 +150,8 @@ export type StoreStateRead = {
|
|
|
106
150
|
export type StoreSnapshotWrite = {
|
|
107
151
|
index: number
|
|
108
152
|
state: unknown
|
|
109
|
-
/** Unfinished
|
|
110
|
-
|
|
153
|
+
/** Unfinished cursor heads that durably retain this exact checkpoint. */
|
|
154
|
+
pins?: readonly HeadRef[]
|
|
111
155
|
}
|
|
112
156
|
|
|
113
157
|
export type StoreStateReadRequest = {
|
|
@@ -115,9 +159,15 @@ export type StoreStateReadRequest = {
|
|
|
115
159
|
throughIndex?: number
|
|
116
160
|
snapshotThroughIndex?: number
|
|
117
161
|
}
|
|
118
|
-
|
|
162
|
+
|
|
163
|
+
/** The result of atomically claiming every currently eligible head. */
|
|
119
164
|
export type StoreClaimAvailableResult =
|
|
120
|
-
| {
|
|
165
|
+
| {
|
|
166
|
+
outcome: 'claimed'
|
|
167
|
+
events: HeadEvent[]
|
|
168
|
+
/** The session's head when the claim was made: how far behind each event is. */
|
|
169
|
+
head?: number
|
|
170
|
+
}
|
|
121
171
|
| { outcome: 'busy'; retryAt: Date }
|
|
122
172
|
| { outcome: 'settled' }
|
|
123
173
|
|
|
@@ -125,48 +175,61 @@ export type StoreClaimAvailableResult =
|
|
|
125
175
|
export type CompleteAttemptResult =
|
|
126
176
|
{ outcome: 'completed'; events: StoredEvent[] } | { outcome: 'superseded' }
|
|
127
177
|
|
|
128
|
-
/** The result of one claim-renewal heartbeat, both lists in
|
|
178
|
+
/** The result of one claim-renewal heartbeat, both lists in head order. */
|
|
129
179
|
export type RenewClaimsResult = {
|
|
130
180
|
/** Listed claims that remain owned by the holder. */
|
|
131
|
-
renewed:
|
|
132
|
-
/** Listed claims
|
|
133
|
-
superseded:
|
|
181
|
+
renewed: HeadRef[]
|
|
182
|
+
/** Listed claims a newer attempt has durably taken, or whose cursor moved on. */
|
|
183
|
+
superseded: HeadRef[]
|
|
134
184
|
}
|
|
135
185
|
|
|
136
|
-
/** The result of atomically recording a caught
|
|
186
|
+
/** The result of atomically recording a caught failure. */
|
|
137
187
|
export type FailAttemptResult = {
|
|
138
188
|
outcome: 'failed' | 'dead_lettered' | 'superseded'
|
|
139
189
|
failureCount: number
|
|
140
190
|
}
|
|
141
191
|
|
|
192
|
+
/** A consumer an appended event starts work for, with its resolved lane. */
|
|
193
|
+
export type EventTarget = {
|
|
194
|
+
consumer: string
|
|
195
|
+
/** The lane a lane function resolved; null runs the event concurrently. */
|
|
196
|
+
lane: string | null
|
|
197
|
+
}
|
|
198
|
+
|
|
142
199
|
/** Input to `A2Store.append` — already validated by the machine. */
|
|
143
200
|
export type AppendEvent = {
|
|
144
201
|
type: string
|
|
145
202
|
payload: unknown
|
|
146
203
|
/** Caller-supplied idempotency key; generated when absent. */
|
|
147
204
|
id?: string
|
|
148
|
-
/** Internal causal edge supplied atomically by
|
|
205
|
+
/** Internal causal edge supplied atomically by consumer `session.append`. */
|
|
149
206
|
cause?: EventCause
|
|
150
|
-
/**
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
207
|
+
/**
|
|
208
|
+
* Lane-function and concurrent consumers this event starts work for,
|
|
209
|
+
* resolved before persistence. Log-cursor consumers are matched by type
|
|
210
|
+
* from `StoreConsumer.log` instead.
|
|
211
|
+
*/
|
|
212
|
+
targets?: readonly EventTarget[]
|
|
154
213
|
}
|
|
155
214
|
|
|
156
|
-
/** A
|
|
215
|
+
/** A consumer-returned event with the deterministic id retries require. */
|
|
157
216
|
export type ReturnedEvent = AppendEvent & { id: string }
|
|
158
217
|
|
|
159
218
|
/** The rows written by an append and the session's pending state. */
|
|
160
219
|
export type StoreAppendResult = {
|
|
161
220
|
events: StoredEvent[]
|
|
162
|
-
/**
|
|
221
|
+
/**
|
|
222
|
+
* Whether a served consumer has a live head in this session: one that is
|
|
223
|
+
* not dead-lettered. An append whose own batch starts work for a served
|
|
224
|
+
* consumer may report true without checking that work is runnable.
|
|
225
|
+
*/
|
|
163
226
|
hasPending: boolean
|
|
164
227
|
}
|
|
165
228
|
|
|
166
229
|
export type StoreClearOptions = {
|
|
167
230
|
sessionId: string
|
|
168
231
|
ifLastIndex?: number
|
|
169
|
-
/** The clearing
|
|
232
|
+
/** The clearing consumer's attempt, fenced like a consumer append. */
|
|
170
233
|
cause?: EventCause
|
|
171
234
|
}
|
|
172
235
|
|
|
@@ -182,15 +245,17 @@ export type StoreReclaimResult = { deleted: number; done: boolean }
|
|
|
182
245
|
|
|
183
246
|
export type StoreRedriveOptions = {
|
|
184
247
|
sessionId: string
|
|
248
|
+
consumer: string
|
|
185
249
|
index: number
|
|
186
|
-
/** `retry` clears the dead letter; `skip`
|
|
250
|
+
/** `retry` clears the dead letter; `skip` moves the cursor past it. */
|
|
187
251
|
action: 'retry' | 'skip'
|
|
252
|
+
consumers?: readonly StoreConsumer[]
|
|
188
253
|
}
|
|
189
254
|
|
|
190
255
|
export type StoreRedriveResult = {
|
|
191
|
-
/** Whether the
|
|
256
|
+
/** Whether the head was dead-lettered and this operation resolved it. */
|
|
192
257
|
redriven: boolean
|
|
193
|
-
/** Whether
|
|
258
|
+
/** Whether a served consumer has a live head in this session: one that is not dead-lettered. */
|
|
194
259
|
hasPending: boolean
|
|
195
260
|
}
|
|
196
261
|
|
|
@@ -212,6 +277,20 @@ export const SYSTEM_CLOCK: Clock = {
|
|
|
212
277
|
|
|
213
278
|
export const RANDOM_IDS: IdSource = () => crypto.randomUUID()
|
|
214
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Cursor rules every operation shares:
|
|
282
|
+
*
|
|
283
|
+
* - `consumers` names the consumers the caller serves. Omitted, every cursor
|
|
284
|
+
* counts and no log cursor is created or advanced by type.
|
|
285
|
+
* - A cursor's head is the next event it runs: the lowest unfinished event of
|
|
286
|
+
* its lane, the event itself for a concurrent cursor, or for a log cursor
|
|
287
|
+
* the next event after its position whose type it takes. The clear marker
|
|
288
|
+
* is never a head.
|
|
289
|
+
* - A head is eligible when no live claim or retry hold owns it, it is not
|
|
290
|
+
* dead-lettered, and it lies above the latest clear marker.
|
|
291
|
+
* - Moving a cursor past its head resets the head's attempt and failure
|
|
292
|
+
* fields and releases snapshot pins on it.
|
|
293
|
+
*/
|
|
215
294
|
export interface A2Store {
|
|
216
295
|
/**
|
|
217
296
|
* Accepts a batch; the batch is atomic — one transaction, consecutive
|
|
@@ -221,18 +300,24 @@ export interface A2Store {
|
|
|
221
300
|
* lost — return the existing rows as success. If only *some* ids
|
|
222
301
|
* exist, the caller mixed an already-sent batch with fresh events —
|
|
223
302
|
* always a caller bug — so throw `A2Error('PARTIAL_DUPLICATE_BATCH')`.
|
|
224
|
-
* Events carrying `settled: true` get `processedAt` in this same atomic
|
|
225
|
-
* operation, with no dispatch claim or `processedByAttempt`. The result's
|
|
226
|
-
* `hasPending` reflects the whole session in the same atomic operation,
|
|
227
|
-
* including older events and idempotent retries.
|
|
228
303
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
304
|
+
* In the same atomic operation, each event's `targets` start cursor work:
|
|
305
|
+
* a lane target queues the event in that consumer's lane, a null lane
|
|
306
|
+
* creates a cursor for that event alone. Each log consumer in `consumers`
|
|
307
|
+
* whose types the batch carries gets its cursor created or, when idle, a
|
|
308
|
+
* head: the next event after its position that it takes, which may
|
|
309
|
+
* precede the batch when an appender that doesn't serve it wrote in
|
|
310
|
+
* between. `hasPending` reflects the whole session for the served consumers,
|
|
311
|
+
* including older work and idempotent retries.
|
|
312
|
+
*
|
|
313
|
+
* An event carrying `cause` is a consumer append, fenced by attempt
|
|
314
|
+
* currency: accept it only while the cause's cursor still has
|
|
315
|
+
* `cause.index` as its head, `cause.attempt` is that head's latest
|
|
316
|
+
* attempt, and the head is not dead-lettered; otherwise throw
|
|
317
|
+
* `A2Error('SUPERSEDED_ATTEMPT')` and write nothing. The check is part of
|
|
318
|
+
* this atomic operation and must serialize against a concurrent
|
|
319
|
+
* `claimAvailable` — an unlocked read of the cursor admits write skew. The
|
|
320
|
+
* idempotent-replay path runs first, so a batch whose ids all exist
|
|
236
321
|
* replays regardless of the current attempt.
|
|
237
322
|
*
|
|
238
323
|
* Clearing stores (a2-implementation.md §17.2) fence one level earlier:
|
|
@@ -243,118 +328,162 @@ export interface A2Store {
|
|
|
243
328
|
append(
|
|
244
329
|
sessionId: string,
|
|
245
330
|
events: AppendEvent[],
|
|
246
|
-
options?: {
|
|
331
|
+
options?: {
|
|
332
|
+
originIndex?: number
|
|
333
|
+
consumers?: readonly StoreConsumer[]
|
|
334
|
+
},
|
|
247
335
|
): Promise<StoreAppendResult>
|
|
248
336
|
|
|
249
337
|
/**
|
|
250
338
|
* Optional with `reclaim`. Serialized with `append`: checks
|
|
251
339
|
* `ifLastIndex` against the head and the latest marker, then appends a
|
|
252
|
-
*
|
|
253
|
-
*
|
|
340
|
+
* clear marker at the next index without touching existing rows.
|
|
341
|
+
* Afterwards every event at or below the marker is superseded: no head
|
|
342
|
+
* lies there, and every log cursor's position counts from the marker.
|
|
254
343
|
*/
|
|
255
344
|
clear?(options: StoreClearOptions): Promise<StoreClearResult>
|
|
256
345
|
|
|
257
346
|
/**
|
|
258
347
|
* Deletes one bounded batch of rows below the latest clear marker, with
|
|
259
|
-
* their
|
|
348
|
+
* their cursor records, id records, and cached folds. `done` once nothing
|
|
260
349
|
* remains below it. Never waits on a lock that other operations hold.
|
|
261
350
|
*/
|
|
262
351
|
reclaim?(sessionId: string): Promise<StoreReclaimResult>
|
|
263
352
|
|
|
264
353
|
/**
|
|
265
|
-
* Optional with `redrive`. The session's dead-lettered
|
|
266
|
-
* latest clear marker,
|
|
354
|
+
* Optional with `redrive`. The session's dead-lettered heads above the
|
|
355
|
+
* latest clear marker, in event order, then by consumer name.
|
|
267
356
|
*/
|
|
268
|
-
deadLetters?(sessionId: string): Promise<
|
|
357
|
+
deadLetters?(sessionId: string): Promise<HeadEvent[]>
|
|
269
358
|
|
|
270
359
|
/**
|
|
271
|
-
* Optional with `deadLetters`. Atomically resolves
|
|
272
|
-
* above the latest clear marker;
|
|
273
|
-
* `redriven: false`. `retry` clears
|
|
274
|
-
*
|
|
275
|
-
* history and last failure. `skip`
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* `completeAttempt`. `hasPending` is read in the same step.
|
|
360
|
+
* Optional with `deadLetters`. Atomically resolves the consumer's
|
|
361
|
+
* dead-lettered head at `index` above the latest clear marker; anything
|
|
362
|
+
* else is untouched and reports `redriven: false`. `retry` clears
|
|
363
|
+
* `failedAt` and `failureCount`, so the head is claimable again with a
|
|
364
|
+
* fresh failure budget, keeping its claim history and last failure. `skip`
|
|
365
|
+
* moves the cursor past the head with no completion. Both serialize with
|
|
366
|
+
* `claimAvailable` and the consumer-append fence. `hasPending` is read in
|
|
367
|
+
* the same step.
|
|
280
368
|
*/
|
|
281
369
|
redrive?(options: StoreRedriveOptions): Promise<StoreRedriveResult>
|
|
282
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Brings one log consumer's cursor up to date without an append, for
|
|
373
|
+
* `session.consume()`. A missing cursor is created: with `backfill`, at
|
|
374
|
+
* the latest clear marker, taking the first event of its types after it;
|
|
375
|
+
* otherwise at the head, with nothing to run. When the session has nothing
|
|
376
|
+
* after its latest clear (or was never written), a missing cursor stays
|
|
377
|
+
* missing: nothing is written, and the cursor comes back idle at the
|
|
378
|
+
* marker, so a consume of an unknown session leaves no trace. That is
|
|
379
|
+
* exact with `backfill`; without it, the consumer starts at the first
|
|
380
|
+
* consume or serving append that finds events. An
|
|
381
|
+
* idle cursor takes the next event of its types after its position, so it
|
|
382
|
+
* finds events appended by deployments that don't serve it. A cursor with
|
|
383
|
+
* a head, claimed or not, is left as it is. Records the consumer's types,
|
|
384
|
+
* as an append would, and returns the cursor and the session's head.
|
|
385
|
+
*/
|
|
386
|
+
wakeLog(options: {
|
|
387
|
+
sessionId: string
|
|
388
|
+
consumer: StoreConsumer & { log: NonNullable<StoreConsumer['log']> }
|
|
389
|
+
}): Promise<{ cursor: StoredCursor; head: number }>
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* The session's cursors: every log and lane cursor, and every concurrent
|
|
393
|
+
* cursor that still has a head, by consumer, then lane (concurrent cursors
|
|
394
|
+
* last, by head). Lane and concurrent cursors whose work lies at or below
|
|
395
|
+
* the latest clear marker are not listed.
|
|
396
|
+
*/
|
|
397
|
+
cursors(sessionId: string): Promise<StoredCursor[]>
|
|
398
|
+
|
|
283
399
|
/** Events for one session, oldest first. Bounds form `(afterIndex, throughIndex]`. */
|
|
284
400
|
read(
|
|
285
401
|
sessionId: string,
|
|
286
402
|
opts?: { afterIndex?: number; throughIndex?: number },
|
|
287
403
|
): Promise<StoredEvent[]>
|
|
288
404
|
|
|
289
|
-
/** Public history without
|
|
405
|
+
/** Public history without causal edges; core falls back to read when absent. */
|
|
290
406
|
readHistory?(
|
|
291
407
|
sessionId: string,
|
|
292
408
|
opts?: { afterIndex?: number; throughIndex?: number },
|
|
293
409
|
): Promise<Event[]>
|
|
294
410
|
|
|
295
411
|
/**
|
|
296
|
-
* Atomically claims every eligible
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
412
|
+
* Atomically claims every eligible head of the served consumers. A live
|
|
413
|
+
* claim or retry hold produces `busy` only when it is the sole remaining
|
|
414
|
+
* obstacle to actionable work. Claimed heads are returned in event order,
|
|
415
|
+
* then by consumer name. Excluded heads stay unclaimed and keep blocking
|
|
416
|
+
* their lane.
|
|
301
417
|
*/
|
|
302
418
|
claimAvailable(options: {
|
|
303
419
|
sessionId: string
|
|
304
420
|
holder: string
|
|
305
421
|
ttlMs: number
|
|
306
422
|
expiresAtMs?: number
|
|
307
|
-
|
|
423
|
+
exclude?: readonly HeadRef[]
|
|
424
|
+
consumers?: readonly StoreConsumer[]
|
|
308
425
|
/**
|
|
309
426
|
* Taking over a lapsed claim (one whose holder is still recorded, never
|
|
310
427
|
* completed or failed) adds one to `failureCount`. When that reaches
|
|
311
|
-
* `maxFailures`, the
|
|
428
|
+
* `maxFailures`, the head dead-letters instead of being claimed: it
|
|
312
429
|
* comes back in `events` with `failedAt` set and no claim. Its
|
|
313
430
|
* `attemptCount` still advances, fencing the lapsed attempt, and its
|
|
314
431
|
* `lastFailedAttempt` is unchanged.
|
|
315
432
|
*/
|
|
316
433
|
maxFailures?: number
|
|
434
|
+
/**
|
|
435
|
+
* Claims only heads at or below this index: a head past it is neither
|
|
436
|
+
* claimed nor reported `busy`, and stays claimable without it.
|
|
437
|
+
*/
|
|
438
|
+
through?: number
|
|
317
439
|
}): Promise<StoreClaimAvailableResult>
|
|
318
440
|
|
|
319
441
|
/**
|
|
320
442
|
* Renews the listed live claims still owned by `holder`. Each listed claim
|
|
321
|
-
* carries the attempt
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
* expired claim no successor has taken appears in neither list — its
|
|
443
|
+
* carries the attempt the holder owns. `renewed` lists the claims that
|
|
444
|
+
* remain owned after the operation; `superseded` lists the claims whose
|
|
445
|
+
* head a newer attempt has durably taken, or whose cursor has moved past
|
|
446
|
+
* it. An expired claim no successor has taken appears in neither list — its
|
|
325
447
|
* attempt may still complete (`completeAttempt` is the fence), so it is
|
|
326
448
|
* not reported as lost. Renewal never revives an expired claim.
|
|
327
449
|
*/
|
|
328
450
|
renewClaims(options: {
|
|
329
451
|
sessionId: string
|
|
330
452
|
holder: string
|
|
331
|
-
claims: readonly
|
|
453
|
+
claims: readonly (HeadRef & { attempt: number })[]
|
|
332
454
|
ttlMs: number
|
|
333
455
|
expiresAtMs?: number
|
|
334
456
|
}): Promise<RenewClaimsResult>
|
|
335
457
|
|
|
336
458
|
/**
|
|
337
|
-
* Atomically completes one current attempt
|
|
338
|
-
*
|
|
339
|
-
*
|
|
459
|
+
* Atomically completes one current attempt: appends its returned events
|
|
460
|
+
* (with their own targets, waking the served log consumers) and moves the
|
|
461
|
+
* cursor past its head. Retrying a committed completion with the same
|
|
462
|
+
* attempt and deterministic child ids returns the existing children. A
|
|
463
|
+
* stale attempt never appends.
|
|
340
464
|
*/
|
|
341
465
|
completeAttempt(options: {
|
|
342
466
|
sessionId: string
|
|
467
|
+
consumer: string
|
|
468
|
+
lane: string | null
|
|
343
469
|
index: number
|
|
344
470
|
attempt: number
|
|
345
471
|
events: ReturnedEvent[]
|
|
472
|
+
consumers?: readonly StoreConsumer[]
|
|
346
473
|
}): Promise<CompleteAttemptResult>
|
|
347
474
|
|
|
348
475
|
/**
|
|
349
476
|
* Atomically records a caught failure for one claimed attempt. A stale
|
|
350
|
-
* attempt cannot poison a
|
|
477
|
+
* attempt cannot poison a finished head or a newer attempt. Accepted
|
|
351
478
|
* failures record the operation's clock time. A failure that does not
|
|
352
|
-
* dead-letter releases the claim and, with `retryAfterMs`, keeps the
|
|
479
|
+
* dead-letter releases the claim and, with `retryAfterMs`, keeps the head
|
|
353
480
|
* unclaimable for that long: its claim expiry is set with no holder, so
|
|
354
481
|
* `claimAvailable` reports it `busy` until then.
|
|
355
482
|
*/
|
|
356
483
|
failAttempt(options: {
|
|
357
484
|
sessionId: string
|
|
485
|
+
consumer: string
|
|
486
|
+
lane: string | null
|
|
358
487
|
index: number
|
|
359
488
|
attempt: number
|
|
360
489
|
error: string
|
|
@@ -389,9 +518,10 @@ export interface A2Store {
|
|
|
389
518
|
|
|
390
519
|
/**
|
|
391
520
|
* Atomically writes disposable reducer checkpoints. The greatest index
|
|
392
|
-
* advances the head cache. Older checkpoints survive only
|
|
393
|
-
* listed
|
|
394
|
-
*
|
|
521
|
+
* advances the head cache. Older checkpoints survive only while at least
|
|
522
|
+
* one listed cursor head is still unfinished; moving that cursor past it
|
|
523
|
+
* or dead-lettering it releases the pin and collects unreferenced
|
|
524
|
+
* historical checkpoints.
|
|
395
525
|
*/
|
|
396
526
|
putSnapshots(
|
|
397
527
|
sessionId: string,
|
package/src/telemetry.ts
CHANGED
|
@@ -18,6 +18,7 @@ export type A2SpanName =
|
|
|
18
18
|
| 'a2.state'
|
|
19
19
|
| 'a2.snapshot'
|
|
20
20
|
| 'a2.redrive'
|
|
21
|
+
| 'a2.consume'
|
|
21
22
|
| 'a2.ai.control'
|
|
22
23
|
| 'a2.ai.ack'
|
|
23
24
|
| 'a2.ai.generate'
|
|
@@ -30,7 +31,7 @@ export type A2SpanHandle = {
|
|
|
30
31
|
setAttribute(key: string, value: A2AttributeValue): void
|
|
31
32
|
/**
|
|
32
33
|
* Mark the span failed without a2's control flow throwing — used for
|
|
33
|
-
*
|
|
34
|
+
* consumer failures, which a2 swallows by design (they go into the
|
|
34
35
|
* retry machinery, not up the stack) but telemetry must still see.
|
|
35
36
|
*/
|
|
36
37
|
recordError(error: unknown): void
|
package/src/wire.ts
CHANGED
|
@@ -314,6 +314,7 @@ const ERROR_STATUS: Record<A2ServerErrorCode, number> = {
|
|
|
314
314
|
STORE_NOT_CONFIGURED: 500,
|
|
315
315
|
UNKNOWN_PRESENCE_FIELD: 400,
|
|
316
316
|
PRESENCE_NOT_SUPPORTED: 500,
|
|
317
|
+
CONSUMER_DEAD_LETTERED: 500,
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
export function errorStatus(code: A2ErrorCode): number {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ai-harness-BuuC-gJ3.js","names":[],"sources":["../src/ai-harness.ts"],"sourcesContent":["import {\n createDownload,\n type Experimental_SandboxSession,\n type FileUIPart,\n type FinishReason,\n type LanguageModelUsage,\n type ToolSet,\n type UIMessage,\n type UIMessageChunk,\n} from 'ai'\nimport type {\n GenerationCompletedPayload,\n MessageCreatedPayload,\n ReasoningLevel,\n ToolResultPayload,\n} from './ai.ts'\nimport { STEERED_REASON } from './ai-control.ts'\nimport { boundedCache, type BoundedCache } from './bounded-cache.ts'\nimport type { ContractEvent, EventDefs } from './contract.ts'\n\ndeclare const harnessBrand: unique symbol\n\n/**\n * A coding agent's own loop for `agent({ harness })`, from `fx()` or `pi()`.\n * `Tools` names the tools the harness brings, which `toolRecovery` may name.\n */\nexport type Harness<\n Type extends 'fx' | 'pi' = 'fx' | 'pi',\n Tools extends string = string,\n> = {\n readonly type: Type\n readonly [harnessBrand]: { readonly tools: Tools }\n}\n\n/** What a harness's sandbox resolver receives for one step or tool execution. */\nexport type HarnessSandboxContext = {\n session: { readonly id: string }\n signal: AbortSignal\n}\n\n/** Resolves the AI SDK sandbox session for an agent session, the same one every time. */\nexport type HarnessSandbox = (\n context: HarnessSandboxContext,\n) => Experimental_SandboxSession | Promise<Experimental_SandboxSession>\n\n/** What a harness step streams, as the default generator does. */\nexport type HarnessGeneration = {\n stream: ReadableStream<UIMessageChunk>\n completion: () => { finishReason?: FinishReason; usage?: LanguageModelUsage }\n}\n\n/** One stored snapshot and the log after it, read in one store operation. */\nexport type HarnessLogRead = {\n snapshot: { index: number; state: unknown } | null\n events: ContractEvent<EventDefs>[]\n}\n\n/** The session log as a harness step reads it, plus the state saved in it. */\nexport type HarnessLog = {\n /** The latest saved state under `name` and the log after it; `fresh` reads it all. */\n read(options: {\n name: string\n throughIndex: number\n fresh?: boolean\n }): Promise<HarnessLogRead>\n write(options: { name: string; index: number; state: unknown }): Promise<void>\n}\n\n/** Everything one durable generation hands its harness. */\nexport type HarnessStep = {\n agentName: string\n sessionId: string\n requestIndex: number\n /** This attempt at the generation. */\n generationId: string\n responseMessageId: string\n /** The log index this step's context was read through. */\n throughIndex: number\n /** The transcript through that index. */\n messages: readonly UIMessage[]\n model: string\n reasoning?: ReasoningLevel\n speed?: 'fast' | 'standard'\n instructions: string[]\n tools: ToolSet\n log: HarnessLog\n signal: AbortSignal\n}\n\n/** The internal side of a harness value. */\nexport type HarnessDriver = {\n /** The session's sandbox; every local tool execution receives it. */\n readonly sandbox?: HarnessSandbox\n /** Tools the harness brings; A2 executes them like the agent's own. */\n readonly tools: ToolSet\n step(step: HarnessStep): Promise<HarnessGeneration>\n}\n\n// A global symbol, so harness values work across separately bundled copies.\nconst DRIVER = Symbol.for('experimental-a2.ai.harness')\n\nexport function defineHarness<\n Type extends 'fx' | 'pi',\n Tools extends string = never,\n>(type: Type, driver: HarnessDriver): Harness<Type, Tools> {\n const harness = { type }\n Object.defineProperty(harness, DRIVER, { value: driver })\n return Object.freeze(harness) as unknown as Harness<Type, Tools>\n}\n\nexport function harnessDriver(harness: unknown): HarnessDriver {\n const driver =\n typeof harness === 'object' && harness !== null\n ? (harness as { [DRIVER]?: HarnessDriver })[DRIVER]\n : undefined\n if (driver === undefined)\n throw new TypeError(\n \"harness must be { type: 'ai-sdk' }, or come from fx() or pi()\",\n )\n return driver\n}\n\n// ------------------------------------------------------- saved state\n\n/**\n * What an agent's steps saved in this process at the end of a response, per\n * session and harness. A cache: a cold process rebuilds from the whole log.\n */\nexport type HarnessStates = BoundedCache<\n string,\n { index: number; state: unknown }\n>\n\nexport const harnessStates = (): HarnessStates =>\n boundedCache({ maxEntries: 1_000, maxBytes: 64 * 1024 * 1024 })\n\nconst savedKey = (sessionId: string, name: string): string =>\n `${sessionId}\\u0000${name}`\n\n// Binary state counts its bytes; everything else its JSON text.\nconst sizeOf = (value: unknown): number => {\n let binary = 0\n const text = JSON.stringify(value, (_key, item: unknown) => {\n if (!(item instanceof Uint8Array)) return item\n binary += item.byteLength\n return null\n })\n return binary + (text?.length ?? 0)\n}\n\n/**\n * The session log as a harness step reads it: the state this process saved\n * at the end of the last response, when it has one, and the log after it.\n * Saved states are copied in and out, so a step cannot change one in place.\n */\nexport function harnessLog(options: {\n states: HarnessStates\n session: {\n readonly id: string\n history(bounds: {\n gte?: number\n lte?: number\n }): Promise<ContractEvent<EventDefs>[]>\n }\n}): HarnessLog {\n const { states, session } = options\n return {\n read: async ({ name, throughIndex, fresh }) => {\n const entry = fresh ? undefined : states.get(savedKey(session.id, name))\n const snapshot =\n entry === undefined || entry.index > throughIndex\n ? null\n : { index: entry.index, state: structuredClone(entry.state) }\n const events = await session.history(\n snapshot === null\n ? { lte: throughIndex }\n : { gte: snapshot.index + 1, lte: throughIndex },\n )\n return { snapshot, events }\n },\n write: async ({ name, index, state }) => {\n const copy = structuredClone(state)\n states.set(\n savedKey(session.id, name),\n { index, state: copy },\n sizeOf(copy),\n )\n },\n }\n}\n\nconst agentStates = new WeakMap<object, HarnessStates>()\n\n/** Registers an agent server's saved harness states, for `savedHarnessState`. */\nexport function registerHarnessStates(\n server: object,\n states: HarnessStates,\n): void {\n agentStates.set(server, states)\n}\n\n/** What an agent's process saved for one session's harness; tests read it. */\nexport function savedHarnessState(\n server: object,\n options: { sessionId: string; name: string },\n): { index: number; state: unknown } | undefined {\n return agentStates.get(server)?.get(savedKey(options.sessionId, options.name))\n}\n\n/** A harness's state at the end of a response, from the attempt that ran it. */\ntype SavedEnvelope = {\n generationId: string\n responseMessageId: string\n state: unknown\n}\n\nexport type HarnessRead<S> = {\n /** The state saved at the end of the last response, when it is usable. */\n saved: S | undefined\n turns: HarnessTurn[]\n results: Map<string, ToolResultPayload>\n}\n\n/**\n * Reads a harness's saved state and the log since, as turns to replay. A\n * snapshot counts only when the attempt that wrote it committed: a stopped,\n * superseded, or still-running attempt can write one the log never kept.\n * `replayAll` reads the whole log instead, for a state that fails to restore.\n */\nexport async function readHarness<S>({\n step,\n name,\n parse,\n}: {\n step: HarnessStep\n name: string\n parse: (state: unknown) => S | undefined\n}): Promise<HarnessRead<S> & { replayAll: () => Promise<HarnessRead<S>> }> {\n const replayAll = async (): Promise<HarnessRead<S>> => {\n const read = await step.log.read({\n name,\n throughIndex: step.throughIndex,\n fresh: true,\n })\n return { saved: undefined, ...harnessTurns(step, read.events, undefined) }\n }\n const read = await step.log.read({ name, throughIndex: step.throughIndex })\n if (read.snapshot === null)\n return {\n saved: undefined,\n ...harnessTurns(step, read.events, undefined),\n replayAll,\n }\n const envelope = savedEnvelope(read.snapshot.state)\n const saved = envelope === undefined ? undefined : parse(envelope.state)\n const committed =\n envelope !== undefined &&\n read.events.some(\n (event) =>\n event.type === 'ai.generation.completed' &&\n (event.payload as { generationId?: unknown }).generationId ===\n envelope.generationId,\n )\n // A snapshot of this very response belongs to an earlier attempt at it.\n if (\n saved === undefined ||\n !committed ||\n envelope.responseMessageId === step.responseMessageId\n )\n return { ...(await replayAll()), replayAll }\n return {\n saved,\n ...harnessTurns(step, read.events, envelope.responseMessageId),\n replayAll,\n }\n}\n\n/** Saves a harness's state at the end of a response; losing it costs a longer replay. */\nexport async function saveHarness({\n step,\n name,\n state,\n}: {\n step: HarnessStep\n name: string\n state: unknown\n}): Promise<void> {\n const envelope: SavedEnvelope = {\n generationId: step.generationId,\n responseMessageId: step.responseMessageId,\n state,\n }\n try {\n await step.log.write({ name, index: step.requestIndex, state: envelope })\n } catch {\n // The snapshot is a cache.\n }\n}\n\nfunction savedEnvelope(value: unknown): SavedEnvelope | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const { generationId, responseMessageId, state } = value as Record<\n string,\n unknown\n >\n return typeof generationId === 'string' &&\n typeof responseMessageId === 'string'\n ? { generationId, responseMessageId, state }\n : undefined\n}\n\n// ------------------------------------------------- the log as harness turns\n\n/** A completed generation as the log recorded it. */\nexport type RecordedGeneration = {\n chunks: UIMessageChunk[]\n completed: GenerationCompletedPayload\n}\n\n/** What user messages hand a harness: their text, and the files they attach. */\nexport type HarnessInput = { text: string; files: FileUIPart[] }\n\n/** One harness turn to rebuild: its prompt, completed generations, and end. */\nexport type HarnessTurn = {\n prompt: HarnessInput\n generations: RecordedGeneration[]\n /** Steering that entered after the generation at that index. */\n steers: Map<number, HarnessInput[]>\n end: 'stop' | 'interrupted' | 'live'\n}\n\n/**\n * Groups the log since a harness snapshot into harness turns. A response\n * steered after a step continues its turn, with the next response's prompt as\n * steering. `covered` is the response the snapshot already holds.\n */\nexport function harnessTurns(\n step: HarnessStep,\n events: readonly ContractEvent<EventDefs>[],\n covered: string | undefined,\n): { turns: HarnessTurn[]; results: Map<string, ToolResultPayload> } {\n type Generation = {\n id: string\n chunks: UIMessageChunk[]\n completed?: GenerationCompletedPayload\n }\n type Response = {\n id: string\n prompt: HarnessInput[]\n generations: Generation[]\n end?: 'stop' | 'steered' | 'interrupted'\n }\n // Prompts take their content from the transcript: a harness agent never\n // runs A2's compaction, so the step's messages hold every input admitted by\n // its read, before the snapshot or after it. A session another loop\n // compacted can't move to a harness.\n const userInput = new Map<string, HarnessInput>(\n step.messages\n .filter((message) => message.role === 'user')\n .map((message) => [message.id, inputOf(message)]),\n )\n let passive: string[] = []\n const responses = new Map<string, Response>()\n const byStart = new Map<number, Generation>()\n const byId = new Map<string, Generation>()\n const results = new Map<string, ToolResultPayload>()\n\n for (const event of events) {\n const payload = event.payload as Record<string, unknown>\n switch (event.type) {\n case 'ai.message.created': {\n const admitted = event.payload as MessageCreatedPayload\n if (admitted.role === 'user' && admitted.generate === false)\n passive.push(admitted.messageId)\n break\n }\n case 'ai.generation.requested': {\n const id = payload['responseMessageId'] as string\n if (id === covered || responses.has(id)) break\n const messageId = payload['messageId'] as string\n const ids = [...passive.filter((p) => p !== messageId), messageId]\n passive = []\n responses.set(id, {\n id,\n prompt: ids.flatMap((p) => userInput.get(p) ?? []),\n generations: [],\n })\n break\n }\n case 'ai.generation.started': {\n const response = responses.get(payload['responseMessageId'] as string)\n if (response === undefined) break\n const generation: Generation = {\n id: payload['generationId'] as string,\n chunks: [],\n }\n response.generations.push(generation)\n byStart.set(event.index, generation)\n byId.set(generation.id, generation)\n break\n }\n case 'ai.generation.progress':\n byStart\n .get(payload['startIndex'] as number)\n ?.chunks.push(...(payload['chunks'] as UIMessageChunk[]))\n break\n case 'ai.generation.completed': {\n const generation = byId.get(payload['generationId'] as string)\n if (generation)\n generation.completed = event.payload as GenerationCompletedPayload\n break\n }\n case 'ai.tool.result':\n if (payload['preliminary'] !== true)\n results.set(\n payload['toolCallId'] as string,\n event.payload as ToolResultPayload,\n )\n break\n case 'ai.message.completed': {\n const response = responses.get(payload['messageId'] as string)\n if (response) response.end = 'stop'\n break\n }\n case 'ai.message.interrupted': {\n const response = responses.get(payload['messageId'] as string)\n if (response)\n response.end =\n payload['reason'] === STEERED_REASON ? 'steered' : 'interrupted'\n break\n }\n }\n }\n\n const turns: HarnessTurn[] = []\n let steered = false\n for (const response of responses.values()) {\n const input: HarnessInput = {\n text: response.prompt.map((prompt) => prompt.text).join('\\n\\n'),\n files: response.prompt.flatMap((prompt) => prompt.files),\n }\n let turn = turns.at(-1)\n if (steered && turn !== undefined) {\n const at = Math.max(0, turn.generations.length - 1)\n turn.steers.set(at, [...(turn.steers.get(at) ?? []), input])\n } else {\n turn = { prompt: input, generations: [], steers: new Map(), end: 'live' }\n turns.push(turn)\n }\n for (const generation of response.generations)\n if (generation.completed !== undefined)\n turn.generations.push({\n chunks: generation.chunks,\n completed: generation.completed,\n })\n turn.end =\n response.id === step.responseMessageId\n ? 'live'\n : response.end === 'stop'\n ? 'stop'\n : 'interrupted'\n steered = response.end === 'steered'\n }\n return { turns, results }\n}\n\nfunction inputOf(message: UIMessage): HarnessInput {\n return {\n text: message.parts\n .flatMap((part) => (part.type === 'text' ? [part.text] : []))\n .join('\\n'),\n files: message.parts.filter(\n (part): part is FileUIPart => part.type === 'file',\n ),\n }\n}\n\n// ------------------------------------------------------------- attachments\n\n/** An attached image as an engine takes it: its bytes in base64. */\nexport type HarnessImage = { mediaType: string; data: string }\n\n// What one message's files hold in a step's memory, at most.\nconst MAX_ATTACHMENT_BYTES = 32 * 1024 * 1024\n\ntype Attachment = { data: string; bytes: number }\n\n// Every step of a response replays its turn, and a URL's file doesn't change,\n// so the process keeps what it fetched.\nconst attachments = boundedCache<string, Attachment>({\n maxEntries: 256,\n maxBytes: 64 * 1024 * 1024,\n})\n\n/**\n * The images an input attaches, as its engine takes them: engines take\n * bytes, not URLs. A file it can't attach fails the step, naming the file,\n * and is never left out: the failure may pass, and the step is retried like\n * any other.\n */\nexport async function harnessImages({\n harness,\n input,\n signal,\n}: {\n /** As the error names it: `fx()` or `pi()`. */\n harness: string\n input: HarnessInput\n signal: AbortSignal\n}): Promise<HarnessImage[]> {\n const images: HarnessImage[] = []\n let budget = MAX_ATTACHMENT_BYTES\n // One at a time, so the budget bounds what the step holds.\n for (const file of input.files) {\n try {\n if (!file.mediaType.startsWith('image/'))\n throw new Error(`it takes images only, not ${file.mediaType}`)\n const attached = await attachment({ url: file.url, budget, signal })\n budget -= attached.bytes\n images.push({\n mediaType: file.mediaType,\n data: attached.data,\n })\n } catch (error) {\n if (signal.aborted) throw error\n throw new Error(\n `${harness} couldn't attach ${file.filename ?? 'a file'}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n { cause: error },\n )\n }\n }\n return images\n}\n\nasync function attachment({\n url,\n budget,\n signal,\n}: {\n url: string\n budget: number\n signal: AbortSignal\n}): Promise<Attachment> {\n const tooLarge = () =>\n new Error(\n `a message's files may hold ${MAX_ATTACHMENT_BYTES / 1024 / 1024} MiB in a step`,\n )\n const cached = attachments.get(url)\n if (cached !== undefined) {\n if (cached.bytes > budget) throw tooLarge()\n return cached\n }\n const { protocol } = new URL(url)\n if (protocol !== 'https:' && protocol !== 'data:')\n throw new Error(`attachments must be https: or data: URLs, not ${protocol}`)\n // AI SDK's download checks an https: URL's address and its redirects,\n // which a message's sender chooses; a data: URL holds its bytes.\n const bytes =\n protocol === 'data:'\n ? dataBytes(url)\n : (\n await createDownload({ maxBytes: budget })({\n url: new URL(url),\n abortSignal: signal,\n })\n ).data\n if (bytes.byteLength > budget) throw tooLarge()\n const attached: Attachment = {\n data: Buffer.from(bytes).toString('base64'),\n bytes: bytes.byteLength,\n }\n if (protocol === 'https:')\n attachments.set(url, attached, attached.data.length)\n return attached\n}\n\nfunction dataBytes(url: string): Uint8Array {\n const comma = url.indexOf(',')\n if (comma === -1) throw new Error('its data: URL has no data')\n const body = url.slice(comma + 1)\n return url.slice(0, comma).endsWith(';base64')\n ? Buffer.from(body, 'base64')\n : Buffer.from(decodeURIComponent(body))\n}\n\n// ------------------------------------------------------------- Gateway\n\nexport type GatewayCredential = { token: string; method: 'api-key' | 'oidc' }\n\n/** Read per step: on Vercel the OIDC token rides on each function request. */\nexport function gatewayCredential(harness: string): GatewayCredential {\n const key = process.env['AI_GATEWAY_API_KEY']\n if (key) return { token: key, method: 'api-key' }\n const oidc = requestOidcToken() ?? process.env['VERCEL_OIDC_TOKEN']\n if (oidc) return { token: oidc, method: 'oidc' }\n throw new Error(`${harness}() needs AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN`)\n}\n\n// Where Vercel Functions keep the current request, as @vercel/oidc reads it.\nconst REQUEST_CONTEXT = Symbol.for('@vercel/request-context')\n\nfunction requestOidcToken(): string | undefined {\n const context = (\n globalThis as {\n [REQUEST_CONTEXT]?: { get?: () => { headers?: Record<string, unknown> } }\n }\n )[REQUEST_CONTEXT]\n const token = context?.get?.()?.headers?.['x-vercel-oidc-token']\n return typeof token === 'string' && token !== '' ? token : undefined\n}\n\n// ------------------------------------------------------------- helpers\n\nexport class Divergence extends Error {\n constructor(harness: string, message: string) {\n super(`${harness} replay diverged from the log: ${message}`)\n }\n}\n\nexport type Deferred = { promise: Promise<void>; resolve: () => void }\n\nexport function deferred(): Deferred {\n let resolve!: () => void\n const promise = new Promise<void>((done) => {\n resolve = done\n })\n return { promise, resolve }\n}\n\n/** A result that never settles; the harness is stopped instead. */\nexport function pending(\n signal: AbortSignal | null | undefined,\n): Promise<never> {\n return new Promise((_, reject) => {\n if (signal?.aborted) reject(signal.reason)\n signal?.addEventListener('abort', () => reject(signal.reason), {\n once: true,\n })\n })\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ai-harness-CV6rXbB3.d.ts","names":[],"sources":["../src/ai-harness.ts"],"mappings":";;;cAoBc;;;;;KAMF,QACV,wCACA;WAES,MAAM;YACL;aAA0B,OAAO;;;;KAIjC;EACV;aAAoB;;EACpB,QAAQ;;;KAIE,kBACV,SAAS,0BACN,8BAA8B,QAAQ"}
|