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.
- package/CHANGELOG.md +128 -0
- package/dist/ai-server.browser.d.ts +1 -0
- package/dist/ai-server.browser.js +4 -0
- package/dist/ai-server.d.ts +65 -0
- package/dist/ai-server.js +494 -0
- package/dist/ai.d.ts +282 -0
- package/dist/ai.js +922 -0
- package/dist/cache-indexeddb.d.ts +1 -0
- package/dist/cache-indexeddb.js +0 -0
- package/dist/client.d.ts +90 -0
- package/dist/client.js +410 -0
- package/dist/contract-B0kAXoaL.js +60 -0
- package/dist/contract-DL8btVd9.d.ts +161 -0
- package/dist/devtools-server.browser.d.ts +1 -0
- package/dist/devtools-server.browser.js +4 -0
- package/dist/devtools-server.d.ts +22 -0
- package/dist/devtools-server.js +1087 -0
- package/dist/errors-BJRMd-h6.js +23 -0
- package/dist/errors-xL_JTXsY.d.ts +20 -0
- package/dist/http.d.ts +44 -0
- package/dist/http.js +119 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/inspection-E7qbD0Xj.js +10 -0
- package/dist/internal-Dm8Ejnud.js +36 -0
- package/dist/log-Dg1I8NRr.d.ts +245 -0
- package/dist/log-memory.d.ts +11 -0
- package/dist/log-memory.js +345 -0
- package/dist/log-polling-RO7kclzR.js +83 -0
- package/dist/log-postgres.d.ts +40 -0
- package/dist/log-postgres.js +628 -0
- package/dist/log-redis.d.ts +31 -0
- package/dist/log-redis.js +711 -0
- package/dist/log-sqlite.d.ts +17 -0
- package/dist/log-sqlite.js +450 -0
- package/dist/log-yJbXUf72.js +5 -0
- package/dist/otel.d.ts +12 -0
- package/dist/otel.js +41 -0
- package/dist/react.d.ts +54 -0
- package/dist/react.js +85 -0
- package/dist/recovery-vercel.d.ts +60 -0
- package/dist/recovery-vercel.js +120 -0
- package/dist/retryable-lazy-DZWmHpii.js +19 -0
- package/dist/server-DYsnKTTy.js +780 -0
- package/dist/server.browser.d.ts +1 -0
- package/dist/server.browser.js +11 -0
- package/dist/server.d.ts +136 -0
- package/dist/server.js +2 -0
- package/dist/telemetry-C78al20p.d.ts +32 -0
- package/dist/validate-XKT4FSNn.js +28 -0
- package/dist/wire-2QpU1EtJ.js +62 -0
- package/docs/01-quickstart.mdx +214 -0
- package/docs/concepts/01-contracts.mdx +138 -0
- package/docs/concepts/02-handlers.mdx +146 -0
- package/docs/concepts/03-durability.mdx +230 -0
- package/docs/concepts/04-state.mdx +133 -0
- package/docs/guides/01-timers.mdx +85 -0
- package/docs/guides/02-cancellation.mdx +107 -0
- package/docs/guides/03-react.mdx +234 -0
- package/docs/guides/04-local-first.mdx +88 -0
- package/docs/guides/05-production.mdx +179 -0
- package/docs/guides/06-ai-agents.mdx +659 -0
- package/docs/guides/07-devtools.mdx +101 -0
- package/docs/guides/08-application-data.mdx +114 -0
- package/docs/index.mdx +282 -0
- package/docs/reference/01-api.mdx +637 -0
- package/docs/reference/02-errors.mdx +77 -0
- 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,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 };
|