@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 +62 -70
- package/dist/chunk-63JX2QNP.js +86 -0
- package/dist/chunk-63JX2QNP.js.map +1 -0
- package/dist/{chunk-2KGXQZWA.js → chunk-HZZDET2H.js} +5 -83
- package/dist/chunk-HZZDET2H.js.map +1 -0
- package/dist/durable.d.ts +64 -0
- package/dist/durable.js +242 -0
- package/dist/durable.js.map +1 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/transport/index.js +4 -2
- package/package.json +16 -5
- package/dist/chunk-2KGXQZWA.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,83 +1,75 @@
|
|
|
1
1
|
# @anvia/client
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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 {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
19
|
-
|
|
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
|
-
##
|
|
41
|
+
## Capabilities
|
|
26
42
|
|
|
27
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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-
|
|
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 };
|