agents-vue 0.2.0 → 0.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/CHANGELOG.md +37 -0
- package/README.md +77 -12
- package/dist/index.d.mts +37 -95
- package/dist/index.d.ts +37 -95
- package/dist/index.mjs +208 -516
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v0.3.0
|
|
5
|
+
|
|
6
|
+
Rebased onto AI SDK v7 and `agents` 0.27, with upstream's own transport and the four client-side bugs the Vue port carried fixed against the React client.
|
|
7
|
+
|
|
8
|
+
### ⚠️ Breaking changes
|
|
9
|
+
|
|
10
|
+
- Peer dependencies: `ai ^7`, `@ai-sdk/vue ^4`, `agents >=0.27.0 <1`, `vue ^3.5`
|
|
11
|
+
- `WebSocketChatTransport` and `WebSocketLike` are no longer exported: the hand-rolled transport is deleted and the composable wires upstream's `WebSocketChatTransport` from `agents/chat/transport`. Import it from `agents` if you were using it directly.
|
|
12
|
+
|
|
13
|
+
### 🩹 Fixes
|
|
14
|
+
|
|
15
|
+
- Client-side tool calls are dispatched off the rendered transcript, deduped by tool-call id, rather than off the AI SDK's own callback alone. A call in a turn this client only observed — a resumed turn, a turn another tab started — used to render as a chip and never run.
|
|
16
|
+
- An early `cf_agent_stream_resuming` is either fed to the continuation this client armed or observed as the server's own turn, never both. Doing both delivered the same turn twice.
|
|
17
|
+
- The continuation is armed before the `cf_agent_tool_result` / `cf_agent_tool_approval` frame is sent, so the common case does not race at all.
|
|
18
|
+
- A streaming assistant tail that matches nothing in an incoming sync is dropped rather than appended, which is what put the same turn on screen twice.
|
|
19
|
+
- Message ids are minted with `crypto.randomUUID` and otherwise from `crypto.getRandomValues`, never `Math.random`.
|
|
20
|
+
- A turn another tab started can be stopped from the watching tab, and an errored observed turn completes: the chunk handler registers a turn this client is only watching, and the completion branch reads an error frame as an ending rather than waiting for a done that never comes.
|
|
21
|
+
- A continuation handed to the server is released when the socket carrying that turn is replaced: the reconnect probe runs for exactly that case, and a `cf_agent_stream_resume_none` settles the continuation it answers. Without it a dropped socket left the page unable to auto-continue after any later tool result.
|
|
22
|
+
|
|
23
|
+
### 🚀 Enhancements
|
|
24
|
+
|
|
25
|
+
- `onFrame` option: every socket frame, parsed, before the chat protocol reads it; return `true` to consume it. This is where an application's own frames belong.
|
|
26
|
+
- `mergeMessages` option: how a reconnect's read of the stored transcript joins the one on screen. Defaults to merge-by-id with the stored copy winning.
|
|
27
|
+
- `onStateUpdate` forwarded to the `AgentClient`, `setAgent()` exposed, `connectionState` derived from the socket's own open/close, `retryPendingResume()` on open, and an async `get-messages` seed with a re-read on a replacement socket.
|
|
28
|
+
|
|
29
|
+
## v0.2.1
|
|
30
|
+
|
|
31
|
+
[compare changes](https://github.com/ymansurozer/agents-vue/compare/v0.2.0...v0.2.1)
|
|
32
|
+
|
|
33
|
+
### 🩹 Fixes
|
|
34
|
+
|
|
35
|
+
- Serialize reconnect resume re-probes (parity with agents 0.17.2, #1837) ([#1837](https://github.com/ymansurozer/agents-vue/issues/1837))
|
|
36
|
+
|
|
37
|
+
### ❤️ Contributors
|
|
38
|
+
|
|
39
|
+
- Ymansurozer <ymansurozer@gmail.com>
|
|
40
|
+
|
|
4
41
|
## v0.2.0
|
|
5
42
|
|
|
6
43
|
[compare changes](https://github.com/ymansurozer/agents-vue/compare/v0.1.0...v0.2.0)
|
package/README.md
CHANGED
|
@@ -6,7 +6,9 @@ Vue 3 composable for [Cloudflare Agents](https://github.com/cloudflare/agents)
|
|
|
6
6
|
|
|
7
7
|
## Status
|
|
8
8
|
|
|
9
|
-
Covers the surface needed for a working chat UI — streaming responses, client-side tools, tool approvals, stream resumption, broadcast handling — at parity with upstream `@cloudflare/ai-chat` 0.
|
|
9
|
+
Covers the surface needed for a working chat UI — streaming responses, client-side tools, tool approvals, stream resumption, broadcast handling — at parity with upstream `@cloudflare/ai-chat` 0.12.x on resume/recovery: resume dedupe, `isRecovering`, `cancelOnClientAbort`, pre-stream `cf_agent_stream_pending`, and `connectionError`. Deeper upstream features (the `useAgent` connection hook, the cross-tab observer, `isServerStreaming` / `isStreaming` flags) are on the roadmap.
|
|
10
|
+
|
|
11
|
+
0.3.0 moves the transport to upstream's own `WebSocketChatTransport` from `agents/chat/transport` and fixes four client-side bugs — see [CHANGELOG](./CHANGELOG.md).
|
|
10
12
|
|
|
11
13
|
## Install
|
|
12
14
|
|
|
@@ -18,10 +20,10 @@ Peer dependencies (must also be installed):
|
|
|
18
20
|
|
|
19
21
|
| Peer | Version |
|
|
20
22
|
|---|---|
|
|
21
|
-
| `vue` | `^3.
|
|
22
|
-
| `@ai-sdk/vue` | `^
|
|
23
|
-
| `ai` | `^
|
|
24
|
-
| `agents` | `>=0.
|
|
23
|
+
| `vue` | `^3.5.0` |
|
|
24
|
+
| `@ai-sdk/vue` | `^4.0.0` |
|
|
25
|
+
| `ai` | `^7.0.0` |
|
|
26
|
+
| `agents` | `>=0.27.0 <1.0.0` |
|
|
25
27
|
|
|
26
28
|
## Usage
|
|
27
29
|
|
|
@@ -98,6 +100,58 @@ Returns:
|
|
|
98
100
|
| `onClose` | `(event: CloseEvent) => void` | — | WebSocket close. |
|
|
99
101
|
| `onSocketError` | `(event: Event) => void` | — | Connection-level error. |
|
|
100
102
|
| `onError` | `(error: Error) => void` | — | Chat-turn error from the AI SDK. |
|
|
103
|
+
| `onStateUpdate` | `(state: State, source: "server" \| "client") => void` | — | The agent's state, on connect and on every change the server broadcasts. |
|
|
104
|
+
| `onFrame` | `(frame: unknown) => boolean \| void` | — | Every socket frame, parsed, **before** the chat protocol reads it. Return `true` to consume it. |
|
|
105
|
+
| `mergeMessages` | `(onScreen, stored) => ChatMessage[]` | merge by id | How a reconnect's read of the stored transcript joins the one on screen. |
|
|
106
|
+
|
|
107
|
+
### `onFrame` — your own frames
|
|
108
|
+
|
|
109
|
+
The chat protocol parser narrows a frame to the fields `cf_agent_*` messages have and drops every
|
|
110
|
+
other one, so a frame of your own would arrive with its contents removed. `onFrame` runs first and
|
|
111
|
+
sees the frame whole; returning `true` consumes it, and the protocol handling never sees it.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
useAgentChat({
|
|
115
|
+
client: { /* ... */ },
|
|
116
|
+
onFrame: (frame) => {
|
|
117
|
+
if (!isRecord(frame) || frame.type !== "my_app_event") return;
|
|
118
|
+
showBanner(frame);
|
|
119
|
+
return true; // consumed
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### `mergeMessages` — what a reconnect does to the transcript
|
|
125
|
+
|
|
126
|
+
Nothing pushes a snapshot on connect, so a replacement socket re-reads the stored transcript — and
|
|
127
|
+
that read is a round trip of its own: it can be issued before a turn is persisted and land after the
|
|
128
|
+
socket has already delivered the finished answer. The default merges by id with the stored copy
|
|
129
|
+
winning and anything only on screen kept after it. Pass your own policy to decide differently.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import type { UIMessage } from "ai";
|
|
133
|
+
|
|
134
|
+
const textLength = (message: UIMessage) =>
|
|
135
|
+
message.parts.reduce((total, part) => total + (part.type === "text" ? part.text.length : 0), 0);
|
|
136
|
+
|
|
137
|
+
/** The stored copy can be behind the stream, so the longer of the two wins. */
|
|
138
|
+
const fresher = (stored: UIMessage, onScreen: UIMessage | undefined) =>
|
|
139
|
+
onScreen && textLength(onScreen) > textLength(stored) ? onScreen : stored;
|
|
140
|
+
|
|
141
|
+
useAgentChat({
|
|
142
|
+
client: { /* ... */ },
|
|
143
|
+
mergeMessages: (onScreen, stored) => {
|
|
144
|
+
const live = new Map(onScreen.map((message) => [message.id, message]));
|
|
145
|
+
const storedIds = new Set(stored.map((message) => message.id));
|
|
146
|
+
|
|
147
|
+
return [
|
|
148
|
+
...stored.map((message) => fresher(message, live.get(message.id))),
|
|
149
|
+
// Anything only on screen — a question typed while the socket was down — is kept after.
|
|
150
|
+
...onScreen.filter((message) => !storedIds.has(message.id)),
|
|
151
|
+
];
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
```
|
|
101
155
|
|
|
102
156
|
### Client-side tools
|
|
103
157
|
|
|
@@ -133,25 +187,27 @@ The composable matches the approval id against a `tool-approve` part in `chat.me
|
|
|
133
187
|
|
|
134
188
|
### `WebSocketChatTransport`
|
|
135
189
|
|
|
136
|
-
|
|
190
|
+
No longer exported from here. Since 0.3.0 the composable wires upstream's own transport, so import
|
|
191
|
+
it from `agents` directly if you are building a non-`@ai-sdk/vue` flow over the same `cf_agent_*`
|
|
192
|
+
wire protocol:
|
|
137
193
|
|
|
138
194
|
```ts
|
|
139
|
-
import { WebSocketChatTransport
|
|
195
|
+
import { WebSocketChatTransport } from "agents/chat/transport";
|
|
140
196
|
|
|
141
197
|
const transport = new WebSocketChatTransport({
|
|
142
|
-
|
|
198
|
+
agent, // an AgentClient
|
|
143
199
|
prepareBody, // optional
|
|
144
200
|
});
|
|
145
201
|
```
|
|
146
202
|
|
|
147
203
|
## Coverage vs upstream
|
|
148
204
|
|
|
149
|
-
`agents-vue
|
|
205
|
+
`agents-vue` covers the surface real apps use. Wire protocol is identical to upstream — same `cf_agent_*` message shapes, same resumable streaming, same broadcast transitions, same tool continuation flow.
|
|
150
206
|
|
|
151
207
|
| Feature | Status |
|
|
152
208
|
|---|---|
|
|
153
209
|
| `useAgentChat` composable | ✅ |
|
|
154
|
-
| `WebSocketChatTransport` | ✅ |
|
|
210
|
+
| `WebSocketChatTransport` (upstream's, from `agents/chat/transport`) | ✅ |
|
|
155
211
|
| Streaming responses | ✅ |
|
|
156
212
|
| Client-side tools with `execute` | ✅ |
|
|
157
213
|
| Tool approvals (HITL) | ✅ |
|
|
@@ -165,8 +221,17 @@ const transport = new WebSocketChatTransport({
|
|
|
165
221
|
| `cancelOnClientAbort` (local-only abort, default) | ✅ |
|
|
166
222
|
| Pre-stream `cf_agent_stream_pending` (extended resume probe) | ✅ |
|
|
167
223
|
| `connectionError` (terminal WebSocket close) | ✅ |
|
|
224
|
+
| Reconnect resume serialization (in-flight gate, `agents` 0.17.2 / #1837) | ✅ |
|
|
225
|
+
| `setAgent()`, which invalidates the transport's resume generation | ✅ |
|
|
226
|
+
| `connectionState`, from the socket's own open/close | ✅ |
|
|
227
|
+
| `onStateUpdate`, forwarded to the `AgentClient` | ✅ |
|
|
228
|
+
| Initial `get-messages` seed, and a re-read on a replacement socket | ✅ |
|
|
229
|
+
| Client-side tool calls dispatched off the rendered transcript | ✅ |
|
|
230
|
+
| Early-continuation branch (observe the announced turn, or launch — never both) | ✅ |
|
|
231
|
+
| Continuation armed before the tool frame is sent | ✅ |
|
|
232
|
+
| An unmatchable streaming tail dropped rather than appended | ✅ |
|
|
168
233
|
| `useAgent` connection hook | 🚧 roadmap |
|
|
169
|
-
|
|
|
234
|
+
| Cross-tab observer | 🚧 roadmap |
|
|
170
235
|
| `isServerStreaming` / `isStreaming` flags | 🚧 roadmap |
|
|
171
236
|
|
|
172
237
|
## Contributing
|
|
@@ -174,7 +239,7 @@ const transport = new WebSocketChatTransport({
|
|
|
174
239
|
```bash
|
|
175
240
|
pnpm install
|
|
176
241
|
pnpm exec playwright install chromium
|
|
177
|
-
pnpm test #
|
|
242
|
+
pnpm test # chromium (vitest-browser-vue)
|
|
178
243
|
pnpm typecheck
|
|
179
244
|
pnpm lint
|
|
180
245
|
pnpm build
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
2
|
import * as ai from 'ai';
|
|
3
|
-
import { UIMessage, Tool, JSONSchema7
|
|
3
|
+
import { UIMessage, Tool, JSONSchema7 } from 'ai';
|
|
4
4
|
import { Chat } from '@ai-sdk/vue';
|
|
5
5
|
import { AgentClientOptions, AgentClient, AgentConnectionError } from 'agents/client';
|
|
6
6
|
|
|
@@ -53,6 +53,35 @@ interface UseAgentChatOptions<AgentT = unknown, ChatMessage extends UIMessage =
|
|
|
53
53
|
onSocketError?: (event: Event) => void;
|
|
54
54
|
/** Called for chat-turn errors (AI SDK). */
|
|
55
55
|
onError?: (error: Error) => void;
|
|
56
|
+
/**
|
|
57
|
+
* Called with every socket frame, parsed, **before** the chat protocol reads it. Return `true`
|
|
58
|
+
* to consume the frame, which stops the protocol handling below from seeing it at all.
|
|
59
|
+
*
|
|
60
|
+
* This is where an application's own frames belong. The protocol parser narrows a frame to the
|
|
61
|
+
* fields the chat protocol has and drops every other one, so a frame of your own would otherwise
|
|
62
|
+
* arrive with its contents removed.
|
|
63
|
+
*/
|
|
64
|
+
onFrame?: (frame: unknown) => boolean | void;
|
|
65
|
+
/**
|
|
66
|
+
* How a transcript read back from the server is merged into the one on screen, used when a
|
|
67
|
+
* replacement socket re-reads `get-messages`.
|
|
68
|
+
*
|
|
69
|
+
* The read is a round trip of its own: it can be issued before a turn is persisted and land
|
|
70
|
+
* after the socket has already delivered the finished answer, so replacing the transcript with
|
|
71
|
+
* it wholesale would erase a reply that is on screen. The default merges by id with the stored
|
|
72
|
+
* copy winning and anything only on screen appended after; pass your own policy to decide
|
|
73
|
+
* differently — which copy of a message is fresher, say.
|
|
74
|
+
*/
|
|
75
|
+
mergeMessages?: (onScreen: ChatMessage[], stored: ChatMessage[]) => ChatMessage[];
|
|
76
|
+
/**
|
|
77
|
+
* Called with the agent's state, on connect and on every change the server broadcasts.
|
|
78
|
+
*
|
|
79
|
+
* The same option the React hook passes to `useAgent`. It is forwarded to `AgentClient`, which
|
|
80
|
+
* owns the `cf_agent_state` frame; the dispatcher below never sees one. `source` is `"server"` for
|
|
81
|
+
* a broadcast and `"client"` for this client's own `setState` — which a server-owned state refuses,
|
|
82
|
+
* so nothing here sends one.
|
|
83
|
+
*/
|
|
84
|
+
onStateUpdate?: (state: State, source: "server" | "client") => void;
|
|
56
85
|
}
|
|
57
86
|
declare function useAgentChat<AgentT = unknown, ChatMessage extends UIMessage = UIMessage, State = StateOf<AgentT>>(options: UseAgentChatOptions<AgentT, ChatMessage, State>): {
|
|
58
87
|
addToolApprovalResponse: (args_0: {
|
|
@@ -77,104 +106,17 @@ declare function useAgentChat<AgentT = unknown, ChatMessage extends UIMessage =
|
|
|
77
106
|
clearHistory: () => void;
|
|
78
107
|
chat: Chat<ChatMessage>;
|
|
79
108
|
socket: AgentClient<AgentT, State>;
|
|
109
|
+
setAgent: (next: AgentClient<AgentT, State>) => void;
|
|
80
110
|
stop: () => Promise<void>;
|
|
111
|
+
/** Reactive: true once the `get-messages` seed has settled (or was skipped). */
|
|
112
|
+
initialMessagesLoaded: vue.Ref<boolean, boolean>;
|
|
81
113
|
/** Reactive: true while the server is recovering a durable chat turn. */
|
|
82
114
|
isRecovering: vue.Ref<boolean, boolean>;
|
|
83
115
|
/** Reactive: terminal WebSocket connection failure, or null. */
|
|
84
116
|
connectionError: vue.Ref<AgentConnectionError | null, AgentConnectionError | null>;
|
|
117
|
+
/** Reactive: whether the socket is up, for the header's "Reconnecting…". */
|
|
118
|
+
connectionState: vue.Ref<"connected" | "reconnecting", "connected" | "reconnecting">;
|
|
85
119
|
};
|
|
86
120
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
send(data: string): void;
|
|
90
|
-
addEventListener(type: string, listener: (event: MessageEvent) => void, options?: AddEventListenerOptions): void;
|
|
91
|
-
}
|
|
92
|
-
interface StreamResumingData {
|
|
93
|
-
type: string;
|
|
94
|
-
id: string;
|
|
95
|
-
}
|
|
96
|
-
interface WebSocketChatTransportOptions {
|
|
97
|
-
socket: WebSocketLike;
|
|
98
|
-
activeRequestIds?: Set<string>;
|
|
99
|
-
prepareBody?: (options: {
|
|
100
|
-
messages: UIMessage[];
|
|
101
|
-
trigger: "submit-message" | "regenerate-message";
|
|
102
|
-
messageId?: string;
|
|
103
|
-
}) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
104
|
-
/**
|
|
105
|
-
* Whether a generic client-side abort (e.g. the AI SDK aborting the request
|
|
106
|
-
* signal, or the stream being cancelled on teardown) should cancel the
|
|
107
|
-
* *server* turn. Local-only by default — the server turn keeps running and can
|
|
108
|
-
* be resumed/observed. Explicit `stop()` always cancels via
|
|
109
|
-
* `cancelActiveServerTurn()`, regardless of this flag. Matches upstream 0.7.0.
|
|
110
|
-
* @default false
|
|
111
|
-
*/
|
|
112
|
-
cancelOnClientAbort?: boolean;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* ChatTransport that communicates with a Cloudflare Agent over WebSocket.
|
|
116
|
-
* Mirrors the protocol implemented by `@cloudflare/ai-chat`'s
|
|
117
|
-
* `WebSocketChatTransport`: same `cf_agent_*` message shapes, same resumable
|
|
118
|
-
* streaming handshake, same tool continuation flow. Framework-agnostic — the
|
|
119
|
-
* Vue composable consumes this; another framework binding could too.
|
|
120
|
-
*/
|
|
121
|
-
declare class WebSocketChatTransport implements ChatTransport<UIMessage> {
|
|
122
|
-
private socket;
|
|
123
|
-
private prepareBody?;
|
|
124
|
-
private activeRequestIds;
|
|
125
|
-
_resumeResolver: ((data: StreamResumingData) => void) | null;
|
|
126
|
-
_resumeNoneResolver: (() => void) | null;
|
|
127
|
-
private _expectToolContinuation;
|
|
128
|
-
private _abortToolContinuation;
|
|
129
|
-
private cancelOnClientAbort;
|
|
130
|
-
private _activeServerTurnId;
|
|
131
|
-
private _cancelAttachedStream;
|
|
132
|
-
private _onStreamPending;
|
|
133
|
-
constructor(options: WebSocketChatTransportOptions);
|
|
134
|
-
/** Mark that the next reconnectToStream() should attach to a server-initiated tool continuation. */
|
|
135
|
-
expectToolContinuation(): void;
|
|
136
|
-
/** Abort the active client-side tool continuation stream. */
|
|
137
|
-
abortActiveToolContinuation(): boolean;
|
|
138
|
-
/** Send a server-turn cancel frame for a request id (best-effort). */
|
|
139
|
-
private sendCancelFrame;
|
|
140
|
-
private setActiveServerTurn;
|
|
141
|
-
private clearActiveServerTurn;
|
|
142
|
-
/** Register a server-observed (broadcast / resume-fallback) turn as the cancellable one. */
|
|
143
|
-
observeServerTurn(requestId: string): void;
|
|
144
|
-
/** Mark a server turn complete so it is no longer the cancellable turn. */
|
|
145
|
-
handleServerTurnCompleted(requestId: string): void;
|
|
146
|
-
/**
|
|
147
|
-
* Explicitly cancel the active server turn (and any tool continuation). Unlike a
|
|
148
|
-
* generic client abort, this always signals the server. Drives `useAgentChat.stop()`.
|
|
149
|
-
*/
|
|
150
|
-
cancelActiveServerTurn(): boolean;
|
|
151
|
-
/** Called by the composable on cf_agent_stream_pending — extend the in-flight resume probe. */
|
|
152
|
-
handleStreamPending(): boolean;
|
|
153
|
-
/** True when the transport is waiting for a resume handshake. */
|
|
154
|
-
isAwaitingResume(): boolean;
|
|
155
|
-
/** Called by the composable when it receives cf_agent_stream_resuming. */
|
|
156
|
-
handleStreamResuming(data: StreamResumingData): boolean;
|
|
157
|
-
/** Called by the composable when it receives cf_agent_stream_resume_none. */
|
|
158
|
-
handleStreamResumeNone(): boolean;
|
|
159
|
-
sendMessages(options: {
|
|
160
|
-
trigger: "submit-message" | "regenerate-message";
|
|
161
|
-
chatId: string;
|
|
162
|
-
messageId: string | undefined;
|
|
163
|
-
messages: UIMessage[];
|
|
164
|
-
abortSignal: AbortSignal | undefined;
|
|
165
|
-
} & ChatRequestOptions): Promise<ReadableStream<UIMessageChunk>>;
|
|
166
|
-
reconnectToStream(_options: {
|
|
167
|
-
chatId: string;
|
|
168
|
-
} & ChatRequestOptions): Promise<ReadableStream<UIMessageChunk> | null>;
|
|
169
|
-
/**
|
|
170
|
-
* Creates a deferred ReadableStream for client-side tool continuations.
|
|
171
|
-
* Returned immediately so AI SDK status becomes "submitted" right after tool output,
|
|
172
|
-
* then waits for the server to announce continuation via STREAM_RESUMING.
|
|
173
|
-
*/
|
|
174
|
-
private _createToolContinuationStream;
|
|
175
|
-
/** Creates a ReadableStream that receives resumed stream chunks. */
|
|
176
|
-
private _createResumeStream;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export { WebSocketChatTransport, useAgentChat };
|
|
180
|
-
export type { UseAgentChatOptions, WebSocketChatTransportOptions, WebSocketLike };
|
|
121
|
+
export { useAgentChat };
|
|
122
|
+
export type { UseAgentChatOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
2
|
import * as ai from 'ai';
|
|
3
|
-
import { UIMessage, Tool, JSONSchema7
|
|
3
|
+
import { UIMessage, Tool, JSONSchema7 } from 'ai';
|
|
4
4
|
import { Chat } from '@ai-sdk/vue';
|
|
5
5
|
import { AgentClientOptions, AgentClient, AgentConnectionError } from 'agents/client';
|
|
6
6
|
|
|
@@ -53,6 +53,35 @@ interface UseAgentChatOptions<AgentT = unknown, ChatMessage extends UIMessage =
|
|
|
53
53
|
onSocketError?: (event: Event) => void;
|
|
54
54
|
/** Called for chat-turn errors (AI SDK). */
|
|
55
55
|
onError?: (error: Error) => void;
|
|
56
|
+
/**
|
|
57
|
+
* Called with every socket frame, parsed, **before** the chat protocol reads it. Return `true`
|
|
58
|
+
* to consume the frame, which stops the protocol handling below from seeing it at all.
|
|
59
|
+
*
|
|
60
|
+
* This is where an application's own frames belong. The protocol parser narrows a frame to the
|
|
61
|
+
* fields the chat protocol has and drops every other one, so a frame of your own would otherwise
|
|
62
|
+
* arrive with its contents removed.
|
|
63
|
+
*/
|
|
64
|
+
onFrame?: (frame: unknown) => boolean | void;
|
|
65
|
+
/**
|
|
66
|
+
* How a transcript read back from the server is merged into the one on screen, used when a
|
|
67
|
+
* replacement socket re-reads `get-messages`.
|
|
68
|
+
*
|
|
69
|
+
* The read is a round trip of its own: it can be issued before a turn is persisted and land
|
|
70
|
+
* after the socket has already delivered the finished answer, so replacing the transcript with
|
|
71
|
+
* it wholesale would erase a reply that is on screen. The default merges by id with the stored
|
|
72
|
+
* copy winning and anything only on screen appended after; pass your own policy to decide
|
|
73
|
+
* differently — which copy of a message is fresher, say.
|
|
74
|
+
*/
|
|
75
|
+
mergeMessages?: (onScreen: ChatMessage[], stored: ChatMessage[]) => ChatMessage[];
|
|
76
|
+
/**
|
|
77
|
+
* Called with the agent's state, on connect and on every change the server broadcasts.
|
|
78
|
+
*
|
|
79
|
+
* The same option the React hook passes to `useAgent`. It is forwarded to `AgentClient`, which
|
|
80
|
+
* owns the `cf_agent_state` frame; the dispatcher below never sees one. `source` is `"server"` for
|
|
81
|
+
* a broadcast and `"client"` for this client's own `setState` — which a server-owned state refuses,
|
|
82
|
+
* so nothing here sends one.
|
|
83
|
+
*/
|
|
84
|
+
onStateUpdate?: (state: State, source: "server" | "client") => void;
|
|
56
85
|
}
|
|
57
86
|
declare function useAgentChat<AgentT = unknown, ChatMessage extends UIMessage = UIMessage, State = StateOf<AgentT>>(options: UseAgentChatOptions<AgentT, ChatMessage, State>): {
|
|
58
87
|
addToolApprovalResponse: (args_0: {
|
|
@@ -77,104 +106,17 @@ declare function useAgentChat<AgentT = unknown, ChatMessage extends UIMessage =
|
|
|
77
106
|
clearHistory: () => void;
|
|
78
107
|
chat: Chat<ChatMessage>;
|
|
79
108
|
socket: AgentClient<AgentT, State>;
|
|
109
|
+
setAgent: (next: AgentClient<AgentT, State>) => void;
|
|
80
110
|
stop: () => Promise<void>;
|
|
111
|
+
/** Reactive: true once the `get-messages` seed has settled (or was skipped). */
|
|
112
|
+
initialMessagesLoaded: vue.Ref<boolean, boolean>;
|
|
81
113
|
/** Reactive: true while the server is recovering a durable chat turn. */
|
|
82
114
|
isRecovering: vue.Ref<boolean, boolean>;
|
|
83
115
|
/** Reactive: terminal WebSocket connection failure, or null. */
|
|
84
116
|
connectionError: vue.Ref<AgentConnectionError | null, AgentConnectionError | null>;
|
|
117
|
+
/** Reactive: whether the socket is up, for the header's "Reconnecting…". */
|
|
118
|
+
connectionState: vue.Ref<"connected" | "reconnecting", "connected" | "reconnecting">;
|
|
85
119
|
};
|
|
86
120
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
send(data: string): void;
|
|
90
|
-
addEventListener(type: string, listener: (event: MessageEvent) => void, options?: AddEventListenerOptions): void;
|
|
91
|
-
}
|
|
92
|
-
interface StreamResumingData {
|
|
93
|
-
type: string;
|
|
94
|
-
id: string;
|
|
95
|
-
}
|
|
96
|
-
interface WebSocketChatTransportOptions {
|
|
97
|
-
socket: WebSocketLike;
|
|
98
|
-
activeRequestIds?: Set<string>;
|
|
99
|
-
prepareBody?: (options: {
|
|
100
|
-
messages: UIMessage[];
|
|
101
|
-
trigger: "submit-message" | "regenerate-message";
|
|
102
|
-
messageId?: string;
|
|
103
|
-
}) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
104
|
-
/**
|
|
105
|
-
* Whether a generic client-side abort (e.g. the AI SDK aborting the request
|
|
106
|
-
* signal, or the stream being cancelled on teardown) should cancel the
|
|
107
|
-
* *server* turn. Local-only by default — the server turn keeps running and can
|
|
108
|
-
* be resumed/observed. Explicit `stop()` always cancels via
|
|
109
|
-
* `cancelActiveServerTurn()`, regardless of this flag. Matches upstream 0.7.0.
|
|
110
|
-
* @default false
|
|
111
|
-
*/
|
|
112
|
-
cancelOnClientAbort?: boolean;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* ChatTransport that communicates with a Cloudflare Agent over WebSocket.
|
|
116
|
-
* Mirrors the protocol implemented by `@cloudflare/ai-chat`'s
|
|
117
|
-
* `WebSocketChatTransport`: same `cf_agent_*` message shapes, same resumable
|
|
118
|
-
* streaming handshake, same tool continuation flow. Framework-agnostic — the
|
|
119
|
-
* Vue composable consumes this; another framework binding could too.
|
|
120
|
-
*/
|
|
121
|
-
declare class WebSocketChatTransport implements ChatTransport<UIMessage> {
|
|
122
|
-
private socket;
|
|
123
|
-
private prepareBody?;
|
|
124
|
-
private activeRequestIds;
|
|
125
|
-
_resumeResolver: ((data: StreamResumingData) => void) | null;
|
|
126
|
-
_resumeNoneResolver: (() => void) | null;
|
|
127
|
-
private _expectToolContinuation;
|
|
128
|
-
private _abortToolContinuation;
|
|
129
|
-
private cancelOnClientAbort;
|
|
130
|
-
private _activeServerTurnId;
|
|
131
|
-
private _cancelAttachedStream;
|
|
132
|
-
private _onStreamPending;
|
|
133
|
-
constructor(options: WebSocketChatTransportOptions);
|
|
134
|
-
/** Mark that the next reconnectToStream() should attach to a server-initiated tool continuation. */
|
|
135
|
-
expectToolContinuation(): void;
|
|
136
|
-
/** Abort the active client-side tool continuation stream. */
|
|
137
|
-
abortActiveToolContinuation(): boolean;
|
|
138
|
-
/** Send a server-turn cancel frame for a request id (best-effort). */
|
|
139
|
-
private sendCancelFrame;
|
|
140
|
-
private setActiveServerTurn;
|
|
141
|
-
private clearActiveServerTurn;
|
|
142
|
-
/** Register a server-observed (broadcast / resume-fallback) turn as the cancellable one. */
|
|
143
|
-
observeServerTurn(requestId: string): void;
|
|
144
|
-
/** Mark a server turn complete so it is no longer the cancellable turn. */
|
|
145
|
-
handleServerTurnCompleted(requestId: string): void;
|
|
146
|
-
/**
|
|
147
|
-
* Explicitly cancel the active server turn (and any tool continuation). Unlike a
|
|
148
|
-
* generic client abort, this always signals the server. Drives `useAgentChat.stop()`.
|
|
149
|
-
*/
|
|
150
|
-
cancelActiveServerTurn(): boolean;
|
|
151
|
-
/** Called by the composable on cf_agent_stream_pending — extend the in-flight resume probe. */
|
|
152
|
-
handleStreamPending(): boolean;
|
|
153
|
-
/** True when the transport is waiting for a resume handshake. */
|
|
154
|
-
isAwaitingResume(): boolean;
|
|
155
|
-
/** Called by the composable when it receives cf_agent_stream_resuming. */
|
|
156
|
-
handleStreamResuming(data: StreamResumingData): boolean;
|
|
157
|
-
/** Called by the composable when it receives cf_agent_stream_resume_none. */
|
|
158
|
-
handleStreamResumeNone(): boolean;
|
|
159
|
-
sendMessages(options: {
|
|
160
|
-
trigger: "submit-message" | "regenerate-message";
|
|
161
|
-
chatId: string;
|
|
162
|
-
messageId: string | undefined;
|
|
163
|
-
messages: UIMessage[];
|
|
164
|
-
abortSignal: AbortSignal | undefined;
|
|
165
|
-
} & ChatRequestOptions): Promise<ReadableStream<UIMessageChunk>>;
|
|
166
|
-
reconnectToStream(_options: {
|
|
167
|
-
chatId: string;
|
|
168
|
-
} & ChatRequestOptions): Promise<ReadableStream<UIMessageChunk> | null>;
|
|
169
|
-
/**
|
|
170
|
-
* Creates a deferred ReadableStream for client-side tool continuations.
|
|
171
|
-
* Returned immediately so AI SDK status becomes "submitted" right after tool output,
|
|
172
|
-
* then waits for the server to announce continuation via STREAM_RESUMING.
|
|
173
|
-
*/
|
|
174
|
-
private _createToolContinuationStream;
|
|
175
|
-
/** Creates a ReadableStream that receives resumed stream chunks. */
|
|
176
|
-
private _createResumeStream;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export { WebSocketChatTransport, useAgentChat };
|
|
180
|
-
export type { UseAgentChatOptions, WebSocketChatTransportOptions, WebSocketLike };
|
|
121
|
+
export { useAgentChat };
|
|
122
|
+
export type { UseAgentChatOptions };
|