@anvia/client 1.2.0 → 1.3.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/README.md CHANGED
@@ -1,83 +1,75 @@
1
1
  # @anvia/client
2
2
 
3
- Framework-neutral client protocol, transports, message conversion, and stream state for Anvia.
3
+ Connect any frontend to Anvia with a framework-neutral streaming protocol. This package provides
4
+ HTTP and direct transports, runtime validation, and UI message state for text, tools, and interactions.
4
5
 
5
- `@anvia/core` owns native completion and Agent events. `@anvia/client` owns the public wire boundary:
6
+ ## Install
7
+
8
+ ```sh
9
+ pnpm add @anvia/client @anvia/core
10
+ ```
11
+
12
+ ## Quickstart
13
+
14
+ Consume a chat endpoint that returns an Anvia client stream, such as one created with
15
+ `@anvia/server`:
6
16
 
7
17
  ```ts
8
- import { completionToClientStream, parseClientStreamRequest } from "@anvia/client";
9
- import { streamCompletion } from "@anvia/core";
10
-
11
- const request = parseClientStreamRequest(await httpRequest.json());
12
- if (request.type !== "messages") throw new Error("This endpoint does not resume interactions.");
13
- const events = completionToClientStream({
14
- events: streamCompletion({ model, messages: request.messages }),
15
- });
18
+ import {
19
+ applyClientStreamEvent,
20
+ createHttpClientTransport,
21
+ messagesToUIMessages,
22
+ type UIMessage,
23
+ } from "@anvia/client";
24
+ import type { Message } from "@anvia/core/completion";
25
+
26
+ const transport = createHttpClientTransport({ endpoint: "/api/chat" });
27
+ const messages: Message[] = [{ role: "user", content: "Hello!" }];
28
+ let uiMessages: readonly UIMessage[] = messagesToUIMessages(messages);
29
+
30
+ for await (const frame of transport.send({ request: { type: "messages", messages } })) {
31
+ if (frame.type === "stream_event") {
32
+ uiMessages = applyClientStreamEvent(uiMessages, frame.event);
33
+ console.log(uiMessages);
34
+ }
35
+ }
16
36
  ```
17
37
 
18
- The request carries core `Message[]`; client-side `UIMessage[]` never crosses the server boundary.
19
- The response uses `ClientStreamEvent` records inside an always-framed `anvia.client.v3` stream.
20
- Agent endpoints accept either a `messages` request or an `interaction_response` request, while
21
- keeping the matching `AgentContinuation` exclusively on the server.
22
- Agent interaction wire contracts come from the browser-safe `@anvia/core/agent/interactions`
23
- subpath; importing Client does not load the Agent runtime or its infrastructure dependencies.
38
+ The transport validates framing, event order, and stream identity. Send Core `Message[]` to the
39
+ server; keep `UIMessage[]` as presentation state in your application.
24
40
 
25
- ## Bun
41
+ ## Capabilities
26
42
 
27
- Bun 1.3.14 is the currently tested and supported runtime baseline:
43
+ - HTTP transport for JSONL or SSE, plus direct transport for in-process integrations.
44
+ - Adapters for native completion and Agent events at the server boundary.
45
+ - Validated requests, events, frames, and persisted UI messages.
46
+ - Message conversion and reduction, including streamed tool arguments and generation usage.
47
+ - Typed custom stream data, Agent interactions, and HTTP resume cursors.
28
48
 
29
- ```sh
30
- bun add @anvia/client @anvia/core
31
- ```
49
+ For React, `@anvia/react` manages this state through `useChat` and `useCompletion`. Generic JSONL/SSE
50
+ readers are also available from `@anvia/client/transport` for other event contracts.
32
51
 
33
- Compatibility tests cover framed JSONL and SSE consumption, resumable client streams, fetch
34
- cancellation, and installation from packed package artifacts.
35
-
36
- ## Public API
37
-
38
- - `completionToClientStream({ events, ...options })` adapts native completion events.
39
- - `agentToClientStream({ events, ...options })` adapts native Agent events, including nested-agent
40
- scope.
41
- - `parseClientStreamRequest`, `parseClientStreamEvent`, and `parseClientStreamFrame` validate public
42
- input at runtime.
43
- - `createHttpClientTransport(options)` consumes framed JSONL or SSE responses and validates the
44
- protocol header, frame order, stream identity, and event IDs.
45
- - `createDirectClientTransport({ handler })` provides the same framed contract without HTTP.
46
- - `messagesToUIMessages` and `uiMessagesToMessages` explicitly convert server messages and UI
47
- state.
48
- - `applyClientStreamEvent(messages, event)` applies canonical events to `UIMessage[]` state.
49
- - `parseUIMessage` and `parseUIMessages` validate externally loaded UI state.
50
-
51
- Tool-call start, delta, and end events are automatic when the provider exposes streamed arguments.
52
- `UIToolMessagePart` states are exact: `input-streaming` carries raw partial text,
53
- `input-available` carries parsed JSON input, and terminal `output-available` or `error` parts retain
54
- that input with their result. `uiMessagesToMessages()` rejects partial calls instead of replaying
55
- incomplete JSON or inventing empty arguments.
56
- Errors are masked by default. Use `mapError` only at the server adapter boundary when an application
57
- intentionally exposes a safe error shape. Non-JSON outputs require an explicit `mapOutput`; returning
58
- `undefined` intentionally omits the output, while returning `null` exposes JSON `null`.
59
-
60
- `UIMessage.metadata` remains application-owned and round-trips unchanged. Runtime details such as
61
- run ID, usage, context usage, status, and trace correlation are stored separately in
62
- `UIMessage.generation`. Converting persisted core messages hydrates per-generation usage and context
63
- usage into that UI field and restores persisted sources as UI message parts while preserving the
64
- original metadata.
65
-
66
- Application-specific stream data is explicit and schema-validated:
52
+ ## Learn more
67
53
 
68
- ```ts
69
- type AppData = {
70
- citation_preview: { title: string; url: string };
71
- };
72
-
73
- const transport = createHttpClientTransport<ClientStreamRequest, AppData>({
74
- endpoint: "/api/chat",
75
- dataSchemas: {
76
- citation_preview: citationPreviewSchema,
77
- },
78
- });
79
- ```
54
+ - [Client protocol guide](https://github.com/anvia-hq/anvia/blob/main/docs/packages/client.md)
55
+ - [Server response helpers](https://github.com/anvia-hq/anvia/tree/main/packages/server#readme)
56
+ - [React hooks](https://github.com/anvia-hq/anvia/tree/main/packages/react#readme)
57
+
58
+ ## Durable HTTP client
59
+
60
+ The optional `@anvia/client/durable` entrypoint exports `DurableClient` and
61
+ `DurableHttpError`. Install `@anvia/durable` to use it. It supports submit, list, snapshot,
62
+ SSE observation, approval responses, retry, cancellation, and tool reconciliation.
63
+
64
+ Construct it with an absolute `endpoint`, optional `fetch`, `credentials`, and static or
65
+ per-request `headers`. Restore UI state from `snapshot(runId)`, then observe
66
+ `stream(runId, { after: snapshot.cursor })`. Incoming records and event ordering are
67
+ validated. Reconnection is explicit; aborting observation does not cancel execution.
68
+ These committed progress events are separate from token-delta chat streams.
69
+
70
+ See the [durable HTTP guide](../../docs/packages/durable.md#http-server-and-client).
80
71
 
81
- Low-level generic JSONL/SSE readers and event transports are available from
82
- `@anvia/client/transport`. They do not imply the Anvia client protocol; use them only for endpoints
83
- that intentionally expose a different event contract.
72
+ `DurableClient` also exposes `submitGraph`, `listGraphs`, `graphSnapshot`, `streamGraph`, and
73
+ `cancelGraph`. Graph snapshots contain renderable nodes/edges and explicit waiting reasons.
74
+ Use each node's `runId` with the existing response/retry/reconciliation methods. Graph streams
75
+ carry `graphId` and `taskId`; refresh the snapshot to obtain the latest graph-wide state.
@@ -0,0 +1,86 @@
1
+ // src/transport/streams.ts
2
+ async function* readJsonlStream(stream) {
3
+ const reader = stream.getReader();
4
+ const decoder = new TextDecoder();
5
+ let buffer = "";
6
+ let completed = false;
7
+ try {
8
+ while (true) {
9
+ const next = await reader.read();
10
+ if (next.done) {
11
+ completed = true;
12
+ break;
13
+ }
14
+ buffer += decoder.decode(next.value, { stream: true });
15
+ const lines = buffer.split(/\r?\n/);
16
+ buffer = lines.pop() ?? "";
17
+ for (const line of lines) {
18
+ const value2 = line.trim();
19
+ if (value2.length > 0) yield JSON.parse(value2);
20
+ }
21
+ }
22
+ buffer += decoder.decode();
23
+ const value = buffer.trim();
24
+ if (value.length > 0) yield JSON.parse(value);
25
+ } finally {
26
+ if (!completed) await cancelReader(reader);
27
+ reader.releaseLock();
28
+ }
29
+ }
30
+ async function* readSseStream(stream) {
31
+ const reader = stream.getReader();
32
+ const decoder = new TextDecoder();
33
+ let buffer = "";
34
+ let data = [];
35
+ let completed = false;
36
+ function parseLine(line) {
37
+ if (line === "") {
38
+ const complete = data.length === 0 ? void 0 : data.join("\n");
39
+ data = [];
40
+ return complete;
41
+ }
42
+ if (line.startsWith(":")) return void 0;
43
+ const separator = line.indexOf(":");
44
+ const field = separator === -1 ? line : line.slice(0, separator);
45
+ const value = separator === -1 ? "" : line.slice(separator + 1).replace(/^ /, "");
46
+ if (field === "data") data.push(value);
47
+ return void 0;
48
+ }
49
+ try {
50
+ while (true) {
51
+ const next = await reader.read();
52
+ if (next.done) {
53
+ completed = true;
54
+ break;
55
+ }
56
+ buffer += decoder.decode(next.value, { stream: true });
57
+ const lines = buffer.split(/\r?\n/);
58
+ buffer = lines.pop() ?? "";
59
+ for (const line of lines) {
60
+ const complete = parseLine(line);
61
+ if (complete !== void 0) yield JSON.parse(complete);
62
+ }
63
+ }
64
+ buffer += decoder.decode();
65
+ if (buffer.length > 0) {
66
+ const complete = parseLine(buffer);
67
+ if (complete !== void 0) yield JSON.parse(complete);
68
+ }
69
+ if (data.length > 0) yield JSON.parse(data.join("\n"));
70
+ } finally {
71
+ if (!completed) await cancelReader(reader);
72
+ reader.releaseLock();
73
+ }
74
+ }
75
+ async function cancelReader(reader) {
76
+ try {
77
+ await reader.cancel();
78
+ } catch {
79
+ }
80
+ }
81
+
82
+ export {
83
+ readJsonlStream,
84
+ readSseStream
85
+ };
86
+ //# sourceMappingURL=chunk-63JX2QNP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transport/streams.ts"],"sourcesContent":["export async function* readJsonlStream<TEvent>(\n stream: ReadableStream<Uint8Array>,\n): AsyncIterable<TEvent> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let completed = false;\n\n try {\n while (true) {\n const next = await reader.read();\n if (next.done) {\n completed = true;\n break;\n }\n buffer += decoder.decode(next.value, { stream: true });\n const lines = buffer.split(/\\r?\\n/);\n buffer = lines.pop() ?? \"\";\n for (const line of lines) {\n const value = line.trim();\n if (value.length > 0) yield JSON.parse(value) as TEvent;\n }\n }\n buffer += decoder.decode();\n const value = buffer.trim();\n if (value.length > 0) yield JSON.parse(value) as TEvent;\n } finally {\n if (!completed) await cancelReader(reader);\n reader.releaseLock();\n }\n}\n\nexport async function* readSseStream<TEvent>(\n stream: ReadableStream<Uint8Array>,\n): AsyncIterable<TEvent> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let data: string[] = [];\n let completed = false;\n\n function parseLine(line: string): string | undefined {\n if (line === \"\") {\n const complete = data.length === 0 ? undefined : data.join(\"\\n\");\n data = [];\n return complete;\n }\n if (line.startsWith(\":\")) return undefined;\n const separator = line.indexOf(\":\");\n const field = separator === -1 ? line : line.slice(0, separator);\n const value = separator === -1 ? \"\" : line.slice(separator + 1).replace(/^ /, \"\");\n if (field === \"data\") data.push(value);\n return undefined;\n }\n\n try {\n while (true) {\n const next = await reader.read();\n if (next.done) {\n completed = true;\n break;\n }\n buffer += decoder.decode(next.value, { stream: true });\n const lines = buffer.split(/\\r?\\n/);\n buffer = lines.pop() ?? \"\";\n for (const line of lines) {\n const complete = parseLine(line);\n if (complete !== undefined) yield JSON.parse(complete) as TEvent;\n }\n }\n buffer += decoder.decode();\n if (buffer.length > 0) {\n const complete = parseLine(buffer);\n if (complete !== undefined) yield JSON.parse(complete) as TEvent;\n }\n if (data.length > 0) yield JSON.parse(data.join(\"\\n\")) as TEvent;\n } finally {\n if (!completed) await cancelReader(reader);\n reader.releaseLock();\n }\n}\n\nasync function cancelReader(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<void> {\n try {\n await reader.cancel();\n } catch {\n // The request may already have been aborted by its owner.\n }\n}\n"],"mappings":";AAAA,gBAAuB,gBACrB,QACuB;AACvB,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AACb,MAAI,YAAY;AAEhB,MAAI;AACF,WAAO,MAAM;AACX,YAAM,OAAO,MAAM,OAAO,KAAK;AAC/B,UAAI,KAAK,MAAM;AACb,oBAAY;AACZ;AAAA,MACF;AACA,gBAAU,QAAQ,OAAO,KAAK,OAAO,EAAE,QAAQ,KAAK,CAAC;AACrD,YAAM,QAAQ,OAAO,MAAM,OAAO;AAClC,eAAS,MAAM,IAAI,KAAK;AACxB,iBAAW,QAAQ,OAAO;AACxB,cAAMA,SAAQ,KAAK,KAAK;AACxB,YAAIA,OAAM,SAAS,EAAG,OAAM,KAAK,MAAMA,MAAK;AAAA,MAC9C;AAAA,IACF;AACA,cAAU,QAAQ,OAAO;AACzB,UAAM,QAAQ,OAAO,KAAK;AAC1B,QAAI,MAAM,SAAS,EAAG,OAAM,KAAK,MAAM,KAAK;AAAA,EAC9C,UAAE;AACA,QAAI,CAAC,UAAW,OAAM,aAAa,MAAM;AACzC,WAAO,YAAY;AAAA,EACrB;AACF;AAEA,gBAAuB,cACrB,QACuB;AACvB,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AACb,MAAI,OAAiB,CAAC;AACtB,MAAI,YAAY;AAEhB,WAAS,UAAU,MAAkC;AACnD,QAAI,SAAS,IAAI;AACf,YAAM,WAAW,KAAK,WAAW,IAAI,SAAY,KAAK,KAAK,IAAI;AAC/D,aAAO,CAAC;AACR,aAAO;AAAA,IACT;AACA,QAAI,KAAK,WAAW,GAAG,EAAG,QAAO;AACjC,UAAM,YAAY,KAAK,QAAQ,GAAG;AAClC,UAAM,QAAQ,cAAc,KAAK,OAAO,KAAK,MAAM,GAAG,SAAS;AAC/D,UAAM,QAAQ,cAAc,KAAK,KAAK,KAAK,MAAM,YAAY,CAAC,EAAE,QAAQ,MAAM,EAAE;AAChF,QAAI,UAAU,OAAQ,MAAK,KAAK,KAAK;AACrC,WAAO;AAAA,EACT;AAEA,MAAI;AACF,WAAO,MAAM;AACX,YAAM,OAAO,MAAM,OAAO,KAAK;AAC/B,UAAI,KAAK,MAAM;AACb,oBAAY;AACZ;AAAA,MACF;AACA,gBAAU,QAAQ,OAAO,KAAK,OAAO,EAAE,QAAQ,KAAK,CAAC;AACrD,YAAM,QAAQ,OAAO,MAAM,OAAO;AAClC,eAAS,MAAM,IAAI,KAAK;AACxB,iBAAW,QAAQ,OAAO;AACxB,cAAM,WAAW,UAAU,IAAI;AAC/B,YAAI,aAAa,OAAW,OAAM,KAAK,MAAM,QAAQ;AAAA,MACvD;AAAA,IACF;AACA,cAAU,QAAQ,OAAO;AACzB,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,WAAW,UAAU,MAAM;AACjC,UAAI,aAAa,OAAW,OAAM,KAAK,MAAM,QAAQ;AAAA,IACvD;AACA,QAAI,KAAK,SAAS,EAAG,OAAM,KAAK,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,EACvD,UAAE;AACA,QAAI,CAAC,UAAW,OAAM,aAAa,MAAM;AACzC,WAAO,YAAY;AAAA,EACrB;AACF;AAEA,eAAe,aAAa,QAAgE;AAC1F,MAAI;AACF,UAAM,OAAO,OAAO;AAAA,EACtB,QAAQ;AAAA,EAER;AACF;","names":["value"]}
@@ -1,83 +1,7 @@
1
- // src/transport/streams.ts
2
- async function* readJsonlStream(stream) {
3
- const reader = stream.getReader();
4
- const decoder = new TextDecoder();
5
- let buffer = "";
6
- let completed = false;
7
- try {
8
- while (true) {
9
- const next = await reader.read();
10
- if (next.done) {
11
- completed = true;
12
- break;
13
- }
14
- buffer += decoder.decode(next.value, { stream: true });
15
- const lines = buffer.split(/\r?\n/);
16
- buffer = lines.pop() ?? "";
17
- for (const line of lines) {
18
- const value2 = line.trim();
19
- if (value2.length > 0) yield JSON.parse(value2);
20
- }
21
- }
22
- buffer += decoder.decode();
23
- const value = buffer.trim();
24
- if (value.length > 0) yield JSON.parse(value);
25
- } finally {
26
- if (!completed) await cancelReader(reader);
27
- reader.releaseLock();
28
- }
29
- }
30
- async function* readSseStream(stream) {
31
- const reader = stream.getReader();
32
- const decoder = new TextDecoder();
33
- let buffer = "";
34
- let data = [];
35
- let completed = false;
36
- function parseLine(line) {
37
- if (line === "") {
38
- const complete = data.length === 0 ? void 0 : data.join("\n");
39
- data = [];
40
- return complete;
41
- }
42
- if (line.startsWith(":")) return void 0;
43
- const separator = line.indexOf(":");
44
- const field = separator === -1 ? line : line.slice(0, separator);
45
- const value = separator === -1 ? "" : line.slice(separator + 1).replace(/^ /, "");
46
- if (field === "data") data.push(value);
47
- return void 0;
48
- }
49
- try {
50
- while (true) {
51
- const next = await reader.read();
52
- if (next.done) {
53
- completed = true;
54
- break;
55
- }
56
- buffer += decoder.decode(next.value, { stream: true });
57
- const lines = buffer.split(/\r?\n/);
58
- buffer = lines.pop() ?? "";
59
- for (const line of lines) {
60
- const complete = parseLine(line);
61
- if (complete !== void 0) yield JSON.parse(complete);
62
- }
63
- }
64
- buffer += decoder.decode();
65
- if (buffer.length > 0) {
66
- const complete = parseLine(buffer);
67
- if (complete !== void 0) yield JSON.parse(complete);
68
- }
69
- if (data.length > 0) yield JSON.parse(data.join("\n"));
70
- } finally {
71
- if (!completed) await cancelReader(reader);
72
- reader.releaseLock();
73
- }
74
- }
75
- async function cancelReader(reader) {
76
- try {
77
- await reader.cancel();
78
- } catch {
79
- }
80
- }
1
+ import {
2
+ readJsonlStream,
3
+ readSseStream
4
+ } from "./chunk-63JX2QNP.js";
81
5
 
82
6
  // src/transport/fetch.ts
83
7
  var EventStreamHttpError = class extends Error {
@@ -191,11 +115,9 @@ function mergeHeaders(...values) {
191
115
  }
192
116
 
193
117
  export {
194
- readJsonlStream,
195
- readSseStream,
196
118
  EventStreamHttpError,
197
119
  fetchEventStream,
198
120
  createFetchEventTransport,
199
121
  createDirectEventTransport
200
122
  };
201
- //# sourceMappingURL=chunk-2KGXQZWA.js.map
123
+ //# sourceMappingURL=chunk-HZZDET2H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transport/fetch.ts","../src/transport/event-transport.ts"],"sourcesContent":["import { readJsonlStream, readSseStream } from \"./streams\";\nimport type { EventStreamFormat } from \"./types\";\n\nexport type FetchEventStreamOptions = Omit<RequestInit, \"headers\"> & {\n input: string | URL | Request;\n format?: EventStreamFormat | \"auto\";\n fetch?: typeof fetch;\n headers?: HeadersInit;\n validateResponse?: (response: Response) => void;\n};\n\nexport class EventStreamHttpError extends Error {\n constructor(\n readonly response: Response,\n readonly body: string,\n ) {\n super(`Event stream request failed with status ${response.status}`);\n this.name = \"EventStreamHttpError\";\n }\n}\n\nexport async function* fetchEventStream<TEvent>(\n options: FetchEventStreamOptions,\n): AsyncIterable<TEvent> {\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (fetchImpl === undefined) throw new Error(\"fetchEventStream requires a fetch implementation.\");\n\n const response = await fetchImpl(options.input, fetchOptions(options));\n if (!response.ok) throw new EventStreamHttpError(response, await response.text());\n options.validateResponse?.(response);\n if (response.body === null) throw new Error(\"Event stream response does not include a body.\");\n\n const format =\n options.format === undefined || options.format === \"auto\"\n ? inferEventStreamFormat(response.headers.get(\"content-type\"))\n : options.format;\n if (format === \"sse\") {\n yield* readSseStream<TEvent>(response.body);\n } else {\n yield* readJsonlStream<TEvent>(response.body);\n }\n}\n\nfunction fetchOptions(options: FetchEventStreamOptions): RequestInit {\n const {\n input: _input,\n format: _format,\n fetch: _fetch,\n validateResponse: _validate,\n ...init\n } = options;\n return init;\n}\n\nfunction inferEventStreamFormat(contentType: string | null): EventStreamFormat {\n return contentType?.toLowerCase().includes(\"text/event-stream\") ? \"sse\" : \"jsonl\";\n}\n","import { type FetchEventStreamOptions, fetchEventStream } from \"./fetch\";\nimport type { EventStreamFormat, EventTransport, EventTransportSendOptions } from \"./types\";\n\nexport type EventTransportContext<TRequest> = EventTransportSendOptions<TRequest>;\n\nexport type EventTransportBodyContext<TRequest> = Omit<\n EventTransportContext<TRequest>,\n \"headers\"\n> & {\n headers: Headers;\n};\n\nexport type CreateFetchEventTransportOptions<TRequest, TEvent> = {\n endpoint: string | URL | ((context: EventTransportContext<TRequest>) => string | URL);\n method?: string;\n format?: EventStreamFormat | \"auto\";\n fetch?: typeof fetch;\n headers?:\n | HeadersInit\n | ((context: EventTransportContext<TRequest>) => HeadersInit | Promise<HeadersInit>);\n body?: (\n context: EventTransportBodyContext<TRequest>,\n ) => BodyInit | null | undefined | Promise<BodyInit | null | undefined>;\n init?: Omit<RequestInit, \"body\" | \"headers\" | \"method\" | \"signal\">;\n mapEvent?: (event: unknown) => TEvent;\n validateResponse?: (response: Response) => void;\n};\n\nexport function createFetchEventTransport<TRequest, TEvent = unknown>(\n options: CreateFetchEventTransportOptions<TRequest, TEvent>,\n): EventTransport<TRequest, TEvent> {\n return {\n async *send(transportOptions) {\n const endpoint =\n typeof options.endpoint === \"function\"\n ? options.endpoint(transportOptions)\n : options.endpoint;\n const requestHeaders = await resolveHeaders(options.headers, transportOptions);\n const headers = mergeHeaders(requestHeaders, transportOptions.headers);\n const method = options.method ?? \"POST\";\n const body = await resolveBody(options.body, transportOptions, headers, method);\n const init: FetchEventStreamOptions = {\n ...options.init,\n input: endpoint,\n method,\n headers,\n format: options.format ?? \"auto\",\n };\n if (body !== undefined) init.body = body;\n if (transportOptions.abortSignal !== undefined) init.signal = transportOptions.abortSignal;\n if (options.fetch !== undefined) init.fetch = options.fetch;\n if (options.validateResponse !== undefined) init.validateResponse = options.validateResponse;\n\n for await (const event of fetchEventStream<unknown>(init)) {\n yield options.mapEvent === undefined ? (event as TEvent) : options.mapEvent(event);\n }\n },\n };\n}\n\nexport function createDirectEventTransport<TRequest, TEvent>(options: {\n handler(context: EventTransportContext<TRequest>): AsyncIterable<TEvent>;\n}): EventTransport<TRequest, TEvent> {\n return {\n async *send(context) {\n const iterator = options.handler(context)[Symbol.asyncIterator]();\n let closePromise: Promise<unknown> | undefined;\n const close = () => {\n closePromise ??= Promise.resolve(iterator.return?.());\n return closePromise;\n };\n const onAbort = () => {\n void close();\n };\n context.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n try {\n while (!context.abortSignal?.aborted) {\n const next = await iterator.next();\n if (next.done) break;\n yield next.value;\n }\n } finally {\n context.abortSignal?.removeEventListener(\"abort\", onAbort);\n await close();\n }\n },\n };\n}\n\nasync function resolveHeaders<TRequest>(\n headers: CreateFetchEventTransportOptions<TRequest, unknown>[\"headers\"],\n context: EventTransportContext<TRequest>,\n): Promise<HeadersInit | undefined> {\n return typeof headers === \"function\" ? headers(context) : headers;\n}\n\nasync function resolveBody<TRequest>(\n body: CreateFetchEventTransportOptions<TRequest, unknown>[\"body\"],\n context: EventTransportContext<TRequest>,\n headers: Headers,\n method: string,\n): Promise<BodyInit | null | undefined> {\n if (body !== undefined) return body({ ...context, headers });\n if (method.toUpperCase() === \"GET\" || method.toUpperCase() === \"HEAD\") return undefined;\n if (!headers.has(\"content-type\")) headers.set(\"content-type\", \"application/json\");\n return JSON.stringify(context.request);\n}\n\nfunction mergeHeaders(...values: Array<HeadersInit | undefined>): Headers {\n const headers = new Headers();\n for (const value of values) {\n if (value === undefined) continue;\n new Headers(value).forEach((headerValue, key) => {\n headers.set(key, headerValue);\n });\n }\n return headers;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,64 @@
1
+ import { TaskSubmission, TaskSnapshot, TaskGraphSnapshot, TaskListOptions, TaskPage, TaskEvent, DurableGraphSubmission, DurableGraphSnapshot, DurableGraphListOptions, DurableGraphPage, DurableGraphEvent, DurableSubmission, DurableSubmitOptions, DurableSnapshot, DurableListOptions, DurableRunPage, DurableEvent } from '@anvia/durable/protocol';
2
+ import { AgentInteractionResponse } from '@anvia/core/agent/interactions';
3
+ import { JsonValue, ToolResultOutput } from '@anvia/core/completion';
4
+
5
+ type DurableRequestOptions = {
6
+ abortSignal?: AbortSignal;
7
+ };
8
+ type DurableClientOptions = {
9
+ /** Absolute HTTP(S) URL matching the server handler's basePath. */
10
+ endpoint: string;
11
+ fetch?: typeof fetch;
12
+ headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
13
+ credentials?: RequestCredentials;
14
+ };
15
+ declare class DurableHttpError extends Error {
16
+ readonly status: number;
17
+ constructor(status: number);
18
+ }
19
+ /** Browser-safe HTTP client. Reconnect explicitly using a snapshot or last applied event cursor. */
20
+ declare class DurableClient {
21
+ private readonly options;
22
+ private readonly endpoint;
23
+ constructor(options: DurableClientOptions);
24
+ submitTask(submission: TaskSubmission, options?: DurableRequestOptions): Promise<TaskSnapshot>;
25
+ taskSnapshot(id: string, options?: DurableRequestOptions): Promise<TaskSnapshot>;
26
+ taskGraph(id: string, options?: DurableRequestOptions): Promise<TaskGraphSnapshot>;
27
+ listTasks(filter: TaskListOptions & {
28
+ sessionId: string;
29
+ }, options?: DurableRequestOptions): Promise<TaskPage>;
30
+ signalTask(id: string, name: string, requestId: string, value: JsonValue, options?: DurableRequestOptions): Promise<void>;
31
+ resolveEffect(id: string, key: string, value: JsonValue, options?: DurableRequestOptions): Promise<void>;
32
+ retryTask(id: string, options?: DurableRequestOptions): Promise<void>;
33
+ cancelTask(id: string, options?: DurableRequestOptions): Promise<void>;
34
+ streamTask(id: string, options?: DurableRequestOptions & {
35
+ after?: number;
36
+ }): AsyncIterable<TaskEvent>;
37
+ private taskPath;
38
+ submitGraph(submission: DurableGraphSubmission, options?: DurableRequestOptions): Promise<DurableGraphSnapshot>;
39
+ graphSnapshot(graphId: string, options?: DurableRequestOptions): Promise<DurableGraphSnapshot>;
40
+ listGraphs(filter: DurableGraphListOptions & {
41
+ sessionId: string;
42
+ }, options?: DurableRequestOptions): Promise<DurableGraphPage>;
43
+ cancelGraph(graphId: string, options?: DurableRequestOptions): Promise<void>;
44
+ streamGraph(graphId: string, options?: DurableRequestOptions & {
45
+ after?: number;
46
+ }): AsyncIterable<DurableGraphEvent>;
47
+ submit(submission: DurableSubmission, options?: DurableSubmitOptions & DurableRequestOptions): Promise<DurableSnapshot>;
48
+ listRuns(filter: DurableListOptions & {
49
+ sessionId: string;
50
+ }, options?: DurableRequestOptions): Promise<DurableRunPage>;
51
+ snapshot(runId: string, options?: DurableRequestOptions): Promise<DurableSnapshot>;
52
+ stream(runId: string, options?: DurableRequestOptions & {
53
+ after?: number;
54
+ }): AsyncIterable<DurableEvent>;
55
+ respond(runId: string, interactionId: string, response: AgentInteractionResponse, options?: DurableRequestOptions): Promise<void>;
56
+ resolveTool(runId: string, operationId: string, output: ToolResultOutput, options?: DurableRequestOptions): Promise<void>;
57
+ retry(runId: string, options?: DurableRequestOptions): Promise<void>;
58
+ cancel(runId: string, options?: DurableRequestOptions): Promise<void>;
59
+ private graphPath;
60
+ private path;
61
+ private request;
62
+ }
63
+
64
+ export { DurableClient, type DurableClientOptions, DurableHttpError, type DurableRequestOptions };