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 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.x on resume/recovery: resume dedupe, `isRecovering`, `cancelOnClientAbort`, pre-stream `cf_agent_stream_pending`, and `connectionError`. Deeper upstream features (the `useAgent` connection hook, initial-message HTTP cache, `isServerStreaming` / `isStreaming` flags) are on the roadmap.
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.3.4` |
22
- | `@ai-sdk/vue` | `^3.0.0` |
23
- | `ai` | `^6.0.0` |
24
- | `agents` | `>=0.17.0 <1.0.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
- Exported for advanced use cases. The composable wires this internally; you only need it directly if you're building a non-`@ai-sdk/vue` flow over the same `cf_agent_*` wire protocol.
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, type WebSocketLike } from "agents-vue";
195
+ import { WebSocketChatTransport } from "agents/chat/transport";
140
196
 
141
197
  const transport = new WebSocketChatTransport({
142
- socket, // any WebSocketLike
198
+ agent, // an AgentClient
143
199
  prepareBody, // optional
144
200
  });
145
201
  ```
146
202
 
147
203
  ## Coverage vs upstream
148
204
 
149
- `agents-vue@0.1` 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.
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
- | Initial-message HTTP cache | 🚧 roadmap |
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 # 79 tests in chromium (vitest-browser-vue)
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, ChatTransport, ChatRequestOptions, UIMessageChunk } from 'ai';
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
- /** Minimal interface a WebSocket must satisfy for the transport. */
88
- interface WebSocketLike {
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, ChatTransport, ChatRequestOptions, UIMessageChunk } from 'ai';
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
- /** Minimal interface a WebSocket must satisfy for the transport. */
88
- interface WebSocketLike {
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 };