experimental-a2 0.0.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/dist/ai-server.browser.d.ts +1 -0
  3. package/dist/ai-server.browser.js +4 -0
  4. package/dist/ai-server.d.ts +65 -0
  5. package/dist/ai-server.js +494 -0
  6. package/dist/ai.d.ts +282 -0
  7. package/dist/ai.js +922 -0
  8. package/dist/cache-indexeddb.d.ts +1 -0
  9. package/dist/cache-indexeddb.js +0 -0
  10. package/dist/client.d.ts +90 -0
  11. package/dist/client.js +410 -0
  12. package/dist/contract-B0kAXoaL.js +60 -0
  13. package/dist/contract-DL8btVd9.d.ts +161 -0
  14. package/dist/devtools-server.browser.d.ts +1 -0
  15. package/dist/devtools-server.browser.js +4 -0
  16. package/dist/devtools-server.d.ts +22 -0
  17. package/dist/devtools-server.js +1087 -0
  18. package/dist/errors-BJRMd-h6.js +23 -0
  19. package/dist/errors-xL_JTXsY.d.ts +20 -0
  20. package/dist/http.d.ts +44 -0
  21. package/dist/http.js +119 -0
  22. package/dist/index.d.ts +5 -0
  23. package/dist/index.js +3 -0
  24. package/dist/inspection-E7qbD0Xj.js +10 -0
  25. package/dist/internal-Dm8Ejnud.js +36 -0
  26. package/dist/log-Dg1I8NRr.d.ts +245 -0
  27. package/dist/log-memory.d.ts +11 -0
  28. package/dist/log-memory.js +345 -0
  29. package/dist/log-polling-RO7kclzR.js +83 -0
  30. package/dist/log-postgres.d.ts +40 -0
  31. package/dist/log-postgres.js +628 -0
  32. package/dist/log-redis.d.ts +31 -0
  33. package/dist/log-redis.js +711 -0
  34. package/dist/log-sqlite.d.ts +17 -0
  35. package/dist/log-sqlite.js +450 -0
  36. package/dist/log-yJbXUf72.js +5 -0
  37. package/dist/otel.d.ts +12 -0
  38. package/dist/otel.js +41 -0
  39. package/dist/react.d.ts +54 -0
  40. package/dist/react.js +85 -0
  41. package/dist/recovery-vercel.d.ts +60 -0
  42. package/dist/recovery-vercel.js +120 -0
  43. package/dist/retryable-lazy-DZWmHpii.js +19 -0
  44. package/dist/server-DYsnKTTy.js +780 -0
  45. package/dist/server.browser.d.ts +1 -0
  46. package/dist/server.browser.js +11 -0
  47. package/dist/server.d.ts +136 -0
  48. package/dist/server.js +2 -0
  49. package/dist/telemetry-C78al20p.d.ts +32 -0
  50. package/dist/validate-XKT4FSNn.js +28 -0
  51. package/dist/wire-2QpU1EtJ.js +62 -0
  52. package/docs/01-quickstart.mdx +214 -0
  53. package/docs/concepts/01-contracts.mdx +138 -0
  54. package/docs/concepts/02-handlers.mdx +146 -0
  55. package/docs/concepts/03-durability.mdx +230 -0
  56. package/docs/concepts/04-state.mdx +133 -0
  57. package/docs/guides/01-timers.mdx +85 -0
  58. package/docs/guides/02-cancellation.mdx +107 -0
  59. package/docs/guides/03-react.mdx +234 -0
  60. package/docs/guides/04-local-first.mdx +88 -0
  61. package/docs/guides/05-production.mdx +179 -0
  62. package/docs/guides/06-ai-agents.mdx +659 -0
  63. package/docs/guides/07-devtools.mdx +101 -0
  64. package/docs/guides/08-application-data.mdx +114 -0
  65. package/docs/index.mdx +282 -0
  66. package/docs/reference/01-api.mdx +637 -0
  67. package/docs/reference/02-errors.mdx +77 -0
  68. package/package.json +111 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,128 @@
1
+ # Changelog
2
+
3
+ Notable changes to the `experimental-a2` package. The telemetry span/attribute
4
+ catalogue (see the API reference) is treated as a contract: renames and
5
+ additions are breaking for dashboards and are called out here.
6
+
7
+ ## Unreleased
8
+
9
+ ### Breaking
10
+
11
+ - **`a2.machine` is gone.** The API is now `a2.contract({ name, events })`
12
+ (isomorphic identity + vocabulary, from `experimental-a2`) plus
13
+ `createServer({ contract, log?, recovery?, telemetry?, handlers? })`
14
+ (from the new server-only entry point `experimental-a2/server`). Handlers are
15
+ declared at construction — `.on()` no longer exists; an entry is a
16
+ handler function or `{ abortOn, handler }`.
17
+ - **`a2.events` and `a2.reducer` are gone.** The contract carries the
18
+ vocabulary; reducers derive from it:
19
+ `contract.reducer({ name, initialState, stateSchema? }).fold(fn)`.
20
+ - **Definition takes a bag, runtime takes values.** All definition-time
21
+ constructors take a single options object (`contract`, `reducer`,
22
+ `createServer`, matching `createClient`/`createReact`). Runtime ids
23
+ stay positional: `server.session(id)`, `server.drain(sessionId)`.
24
+ - **Lease operations take named options.** Custom `A2Log` adapters and direct
25
+ callers now use `lease.acquire({ sessionId, holder, ttlMs, expiresAtMs })`
26
+ and `lease.release({ sessionId, holder })` instead of positional arguments.
27
+ - **Custom `A2Log` drain operations are atomic.** `recordFailure()` and
28
+ `StoredEvent.attempts` are replaced by `claimNext()`,
29
+ `completeAndClaimNext()`, `failAttempt()`, `attemptCount`, and
30
+ `failureCount`.
31
+ - **Custom `A2Log` state reads are one operation.** Optional
32
+ `getSnapshot()`/`putSnapshot()` hooks are replaced by required
33
+ `readState()`/`putSnapshot()` methods. `readState()` returns one consistent
34
+ snapshot-plus-tail view. Every built-in backend implements it as one storage
35
+ operation.
36
+ - **Reads no longer dispatch handlers.** `state()` and `stream()` are
37
+ observational. Without recovery, a later append or explicit `drain()` is the
38
+ retry path. Snapshot write-back now runs as tracked platform `waitUntil` work
39
+ and does not delay `state()`.
40
+ - **Custom `A2Log` events expose durable lifecycle metadata.** `StoredEvent`
41
+ adds atomic `cause`, `firstClaimedAt`, `lastClaimedAt`,
42
+ `processedByAttempt`, `lastFailedAt`, and `lastFailedAttempt`. Adapter append,
43
+ claim, completion, and failure operations must persist these fields.
44
+ - **Telemetry attribute renames** — dashboards keyed on the old names
45
+ go blank:
46
+ - `a2.machine` → `a2.contract` (every span)
47
+ - `a2.reducer` → `a2.state.reducer` (on `a2.state` spans)
48
+ - **Telemetry outcome addition.** `a2.event.outcome` adds `superseded`;
49
+ exhaustive dashboards must handle it.
50
+
51
+ ### Added
52
+
53
+ - **Durable causal lifecycle metadata.** Stored events now retain the
54
+ same-session parent index and attempt as one atomic cause, plus first/latest
55
+ claim, write-once completion, and latest caught-failure summaries. Built-in
56
+ backends persist the metadata in their existing atomic operations, so causal
57
+ trees and useful lifecycle boundaries require no extra backend round trip.
58
+
59
+ - **Durable `ctx.attempt`.** This 1-based claim ordinal is also reported as
60
+ `a2.event.attempt`. Hard kills consume attempts, but not the failure budget.
61
+ Atomic claim and handoff add no log round trips.
62
+
63
+ - `LOG_NOT_CONFIGURED` defers from construction to first use during
64
+ `next build`'s page-data collection (`NEXT_PHASE`), so CIs without
65
+ runtime env vars can build; real production boots still fail fast.
66
+
67
+ - **`experimental-a2/log-postgres`** — the production log backend. Real
68
+ transactions; appends serialize per session on an advisory lock;
69
+ streaming polls the log (a LISTEN/NOTIFY upgrade can land without
70
+ API change). `pg` is an optional peer dependency — pass
71
+ `connectionString`, or inject any pool-shaped `client` (PGlite
72
+ passes the full conformance suite in-process).
73
+ - **`experimental-a2/log-redis`** — the push-native log backend, on Redis Streams:
74
+ the stream entry ID is the a2 index, so `XREAD BLOCK` parks
75
+ server-side and delivers the instant an event lands — no poll
76
+ interval anywhere (measured: sub-20ms to a parked subscriber).
77
+ Every write path is one atomic Lua script; lease expiry stays on
78
+ the injected clock (real-time `PX` is garbage collection only).
79
+ `ioredis` optional peer, or inject any `call`/`duplicate` client;
80
+ `keyPrefix` isolates apps sharing one Redis. Single-instance /
81
+ non-cluster (Upstash — durable by default — Valkey, your own box).
82
+ - **Lease watchdog recovery.** Handler dispatch no longer predicts the
83
+ platform deadline. Every lease acquisition and renewal arms a watchdog for
84
+ just after that execution window, while the queue send and database lease
85
+ proceed independently. A known invocation deadline caps the final window,
86
+ so a real function timeout retries the same unprocessed event promptly.
87
+ Whole-second due-time keys coalesce racing arms, and busy callbacks advance
88
+ the existing heartbeat-aligned slot series instead of starting another one.
89
+ Queue failure never blocks processing or lease renewal.
90
+ - **`experimental-a2/recovery-vercel`** — queue-backed recovery over Vercel Queues
91
+ (`@vercel/queue` optional peer). `vercelQueues()` returns the
92
+ `A2Recovery` pair: `arm` (absolute `dueAt`, delayed, idempotent) and
93
+ `handler(...servers)` (drain, ack when settled or a later watchdog is
94
+ durable). The initial arm starts alongside inline work and is awaited only
95
+ before `append` resolves, with a bounded wait. A failed or hung arm never
96
+ fails the append; it marks the span `a2.append.armed: false` and degrades
97
+ that append to append-driven healing. Unacknowledged callbacks use Vercel
98
+ Queues' 30-second minimum visibility as the fallback when a fresh watchdog
99
+ send fails.
100
+ - **Activity-adaptive polling** in the sqlite and postgres streams:
101
+ 25ms between polls while a session is producing events, doubling to
102
+ a 250ms ceiling when it goes quiet. Token streams read smoothly
103
+ instead of clumping at the idle interval; an idle stream costs the
104
+ same as before. The cadence is owned, not configurable — there is no
105
+ `pollIntervalMs` option; the floor keeps active streams smooth, and
106
+ every poll is a catch-up read, so correctness is untouched.
107
+ - **SSE heartbeat**: `sseResponse` emits a `: ping` comment frame every
108
+ 15 seconds (in addition to the `: connected` prelude), so clients and
109
+ proxies can tell a quiet stream from a dead one.
110
+ - **Client stall detection**: the session client treats a stream with
111
+ no bytes for two missed heartbeats (~35s) as dead — it aborts and
112
+ reconnects through the normal backoff path. A `live` connection now
113
+ means data is actually flowing.
114
+ - **`connection` on the session snapshot** (and `useSession`),
115
+ replacing the flat `status` field: a discriminated union —
116
+ `{ status: 'idle' } | { status: 'connecting', reconnects, error } |
117
+ { status: 'live', reconnects } | { status: 'closed' }` — so an error
118
+ only exists while disconnected and reconnect state needs no
119
+ status-diffing effects.
120
+ - **`push()` returns a `PushResult`**: awaiting it gives the server ack
121
+ exactly as before, and its lazy `.confirmed` promise resolves when
122
+ the live stream has delivered the batch back — the per-push
123
+ lifecycle (applied → acked → confirmed) as one call and two awaits.
124
+ A rejected push rejects both with the same `A2Error`.
125
+ - The API reference now documents the full span catalogue: all four
126
+ span names (including `a2.state`), which attributes arrive at span
127
+ start vs mid-span via `setAttribute`, and the complete outcome value
128
+ sets.
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,4 @@
1
+ //#region src/ai-server.browser.ts
2
+ /** a2/ai/server is server-only; agent definitions live in a2/ai. */
3
+ throw new Error("a2/ai/server is server-only — a client bundle imported it. Import agent from a2/ai in browser code instead.");
4
+ //#endregion
@@ -0,0 +1,65 @@
1
+ import { i as EventDefs, r as ContractEvent } from "./contract-DL8btVd9.js";
2
+ import { AIEventDefs, AIState, AgentDefinition } from "./ai.js";
3
+ import { A2Server, ServerOptions } from "./server.js";
4
+ import { Instructions, LanguageModel, ToolLoopAgentSettings, ToolSet, UIMessage, UIMessageChunk } from "ai";
5
+ //#region src/ai-server.d.ts
6
+ type AgentGenerateContext<M extends UIMessage, D extends AIEventDefs<M> & EventDefs, T extends ToolSet> = {
7
+ request: ContractEvent<D, "ai.generation.requested">;
8
+ requestId: string;
9
+ generationId: string;
10
+ responseMessageId: string;
11
+ messages: M[];
12
+ state: AIState<M>;
13
+ history: ContractEvent<D>[];
14
+ signal: AbortSignal;
15
+ model: LanguageModel;
16
+ tools: T;
17
+ instructions?: Instructions;
18
+ generation: AgentGenerationSettings<T>;
19
+ };
20
+ type AgentGenerate<M extends UIMessage, D extends AIEventDefs<M> & EventDefs, T extends ToolSet> = (context: AgentGenerateContext<M, D, T>) => ReadableStream<UIMessageChunk> | Promise<ReadableStream<UIMessageChunk>>;
21
+ type AgentResolverContext<M extends UIMessage, D extends AIEventDefs<M> & EventDefs> = {
22
+ event: ContractEvent<D, "ai.generation.requested">;
23
+ state: AIState<M>;
24
+ history: ContractEvent<D>[];
25
+ signal: AbortSignal;
26
+ };
27
+ type Resolvable<T, C> = T | ((context: C) => T | Promise<T>);
28
+ type CompactionPolicy<M extends UIMessage, D extends AIEventDefs<M> & EventDefs> = {
29
+ shouldCompact(context: AgentResolverContext<M, D> & {
30
+ messages: M[];
31
+ }): boolean | Promise<boolean>;
32
+ compact(context: AgentResolverContext<M, D> & {
33
+ messages: M[];
34
+ }): M[] | Promise<M[]>;
35
+ };
36
+ type AgentGenerationSettings<T extends ToolSet> = Partial<Omit<ToolLoopAgentSettings<never, T>, "id" | "model" | "tools" | "instructions" | "callOptionsSchema" | "prepareCall">>;
37
+ type CreateHandlersOptions<M extends UIMessage, D extends AIEventDefs<M> & EventDefs, T extends ToolSet = ToolSet> = {
38
+ agent: AgentDefinition<M, D>;
39
+ /** AI SDK model string or provider model, optionally resolved per generation. */
40
+ model: Resolvable<LanguageModel, AgentResolverContext<M, D>>;
41
+ tools?: T;
42
+ instructions?: Resolvable<Instructions | undefined, AgentResolverContext<M, D>>;
43
+ /** Passed to AI SDK's ToolLoopAgent. */
44
+ generation?: AgentGenerationSettings<T>;
45
+ /** Replace model generation with an AI SDK UI message chunk stream. */
46
+ generate?: AgentGenerate<M, D, T>;
47
+ compaction?: CompactionPolicy<M, D>;
48
+ /** Progress is durably flushed at either limit, whichever is reached first. */
49
+ progress?: {
50
+ maxChunks?: number;
51
+ maxDelayMs?: number;
52
+ };
53
+ };
54
+ type CreateAgentServerOptions<M extends UIMessage, D extends AIEventDefs<M> & EventDefs, T extends ToolSet = ToolSet> = CreateHandlersOptions<M, D, T> & Omit<ServerOptions<D>, "contract" | "handlers"> & {
55
+ handlers?: ServerOptions<D>["handlers"];
56
+ };
57
+ /**
58
+ * Build the ordinary A2 handler table for the built-in agent protocol.
59
+ * Application handlers can be spread beside this table.
60
+ */
61
+ declare function createHandlers<M extends UIMessage, D extends AIEventDefs<M> & EventDefs, T extends ToolSet = ToolSet>(options: CreateHandlersOptions<M, D, T>): NonNullable<ServerOptions<D>["handlers"]>;
62
+ /** Assemble an A2 server with the built-in agent handlers and app extensions. */
63
+ declare function createAgentServer<M extends UIMessage, D extends AIEventDefs<M> & EventDefs, T extends ToolSet = ToolSet>(options: CreateAgentServerOptions<M, D, T>): A2Server<D>;
64
+ //#endregion
65
+ export { AgentGenerate, AgentGenerateContext, AgentGenerationSettings, AgentResolverContext, CompactionPolicy, CreateAgentServerOptions, CreateHandlersOptions, type Instructions, type LanguageModel, Resolvable, type ToolLoopAgentSettings, type ToolSet, createAgentServer, createHandlers };