agents 0.17.3 → 0.18.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/dist/{agent-tool-types-CNyE1iz_.d.ts → agent-tool-types-BNUGGBzQ.d.ts} +191 -61
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-BeOheFBK.d.ts → agent-tools-BFbzVLFc.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/chat/index.d.ts +29 -9
- package/dist/chat/index.js +31 -41
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +25 -1
- package/dist/chat/react.js +249 -93
- package/dist/chat/react.js.map +1 -1
- package/dist/chat-sdk/index.d.ts +1 -1
- package/dist/{client-BZ-B3NhC.js → client-CcjiFpTf.js} +352 -81
- package/dist/client-CcjiFpTf.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/cloudflare-BldFV0Pa.js +117 -0
- package/dist/cloudflare-BldFV0Pa.js.map +1 -0
- package/dist/index.d.ts +13 -11
- package/dist/index.js +153 -48
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +18 -14
- package/dist/mcp/client.js +1 -1
- package/dist/mcp/index.d.ts +38 -30
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/observability/ai/index.d.ts +155 -0
- package/dist/observability/ai/index.js +1845 -0
- package/dist/observability/ai/index.js.map +1 -0
- package/dist/react.d.ts +1 -1
- package/dist/{retries-CvHJwSuh.d.ts → retries-CAvxtG9d.d.ts} +16 -7
- package/dist/retries.d.ts +8 -6
- package/dist/retries.js +20 -2
- package/dist/retries.js.map +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sub-routing.d.ts +6 -6
- package/dist/vite.d.ts +4 -4
- package/dist/vite.js +4 -2
- package/dist/vite.js.map +1 -1
- package/dist/{wire-types-nflOzNuU.js → wire-types-CU9rLoeS.js} +33 -2
- package/dist/wire-types-CU9rLoeS.js.map +1 -0
- package/dist/workflows.d.ts +1 -1
- package/docs/agent-class.md +9 -1
- package/docs/configuration.md +20 -0
- package/docs/mcp-client.md +52 -4
- package/docs/observability.md +282 -0
- package/package.json +7 -2
- package/dist/client-BZ-B3NhC.js.map +0 -1
- package/dist/wire-types-nflOzNuU.js.map +0 -1
package/dist/chat/react.d.ts
CHANGED
|
@@ -69,11 +69,19 @@ declare class WebSocketChatTransport<
|
|
|
69
69
|
private _resumeResolver;
|
|
70
70
|
private _resumeNoneResolver;
|
|
71
71
|
private _onStreamPending;
|
|
72
|
+
private _retryResumeProbe;
|
|
72
73
|
private _expectToolContinuation;
|
|
73
74
|
private _abortToolContinuation;
|
|
74
75
|
private _activeServerTurnId;
|
|
75
76
|
private _cancelAttachedStream;
|
|
77
|
+
private _detachResumeStream;
|
|
76
78
|
constructor(options: WebSocketChatTransportOptions<ChatMessage>);
|
|
79
|
+
/**
|
|
80
|
+
* Point the singleton transport at a new Agent connection. A pending resolver
|
|
81
|
+
* belongs to the old Chat/socket generation and must settle before messages
|
|
82
|
+
* from the replacement connection can be consumed (#1914 review).
|
|
83
|
+
*/
|
|
84
|
+
setAgent(agent: AgentConnection): void;
|
|
77
85
|
setCancelOnClientAbort(cancelOnClientAbort: boolean): void;
|
|
78
86
|
/**
|
|
79
87
|
* Explicitly cancel the active server turn, if any.
|
|
@@ -98,6 +106,22 @@ declare class WebSocketChatTransport<
|
|
|
98
106
|
* True when the transport is waiting for a resume handshake.
|
|
99
107
|
*/
|
|
100
108
|
isAwaitingResume(): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Settle and detach the current handshake without interpreting it as a
|
|
111
|
+
* server-idle response. Used when the owning hook/agent generation changes.
|
|
112
|
+
*/
|
|
113
|
+
cancelPendingResume(): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Invalidate all client-side resume state for an obsolete hook/agent
|
|
116
|
+
* generation without cancelling its durable server turn.
|
|
117
|
+
*/
|
|
118
|
+
resetResumeState(): void;
|
|
119
|
+
/**
|
|
120
|
+
* Re-send the active handshake request on the latest socket generation. This
|
|
121
|
+
* preserves one AI SDK resume operation while recovering a request/reply lost
|
|
122
|
+
* with the previous WebSocket.
|
|
123
|
+
*/
|
|
124
|
+
retryPendingResume(): boolean;
|
|
101
125
|
/**
|
|
102
126
|
* Called by onAgentMessage when it receives CF_AGENT_STREAM_RESUMING.
|
|
103
127
|
* If reconnectToStream is waiting, this handles the resume handshake
|
|
@@ -110,7 +134,7 @@ declare class WebSocketChatTransport<
|
|
|
110
134
|
* If reconnectToStream is waiting, resolves the promise with null
|
|
111
135
|
* immediately (no 5-second timeout). Returns true if handled.
|
|
112
136
|
*/
|
|
113
|
-
handleStreamResumeNone(): boolean;
|
|
137
|
+
handleStreamResumeNone(data?: { probeId?: string }): boolean;
|
|
114
138
|
/**
|
|
115
139
|
* Called by onAgentMessage when it receives CF_AGENT_STREAM_PENDING (#1784):
|
|
116
140
|
* the server accepted a turn but its stream has not started yet. If a resume
|
package/dist/chat/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as transition, r as STREAM_RESUME_NONE_REASONS } from "../wire-types-CU9rLoeS.js";
|
|
2
2
|
import { getToolName, isToolUIPart } from "ai";
|
|
3
3
|
import { nanoid } from "nanoid";
|
|
4
4
|
import { use, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -29,15 +29,27 @@ var WebSocketChatTransport = class {
|
|
|
29
29
|
this._resumeResolver = null;
|
|
30
30
|
this._resumeNoneResolver = null;
|
|
31
31
|
this._onStreamPending = null;
|
|
32
|
+
this._retryResumeProbe = null;
|
|
32
33
|
this._expectToolContinuation = false;
|
|
33
34
|
this._abortToolContinuation = null;
|
|
34
35
|
this._activeServerTurnId = null;
|
|
35
36
|
this._cancelAttachedStream = null;
|
|
37
|
+
this._detachResumeStream = null;
|
|
36
38
|
this.agent = options.agent;
|
|
37
39
|
this.prepareBody = options.prepareBody;
|
|
38
40
|
this.activeRequestIds = options.activeRequestIds;
|
|
39
41
|
this.cancelOnClientAbort = options.cancelOnClientAbort ?? false;
|
|
40
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Point the singleton transport at a new Agent connection. A pending resolver
|
|
45
|
+
* belongs to the old Chat/socket generation and must settle before messages
|
|
46
|
+
* from the replacement connection can be consumed (#1914 review).
|
|
47
|
+
*/
|
|
48
|
+
setAgent(agent) {
|
|
49
|
+
if (this.agent === agent) return;
|
|
50
|
+
this.resetResumeState();
|
|
51
|
+
this.agent = agent;
|
|
52
|
+
}
|
|
41
53
|
setCancelOnClientAbort(cancelOnClientAbort) {
|
|
42
54
|
this.cancelOnClientAbort = cancelOnClientAbort;
|
|
43
55
|
}
|
|
@@ -97,6 +109,35 @@ var WebSocketChatTransport = class {
|
|
|
97
109
|
return this._resumeResolver !== null || this._resumeNoneResolver !== null;
|
|
98
110
|
}
|
|
99
111
|
/**
|
|
112
|
+
* Settle and detach the current handshake without interpreting it as a
|
|
113
|
+
* server-idle response. Used when the owning hook/agent generation changes.
|
|
114
|
+
*/
|
|
115
|
+
cancelPendingResume() {
|
|
116
|
+
const resolveNone = this._resumeNoneResolver;
|
|
117
|
+
if (!resolveNone) return false;
|
|
118
|
+
return resolveNone({});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Invalidate all client-side resume state for an obsolete hook/agent
|
|
122
|
+
* generation without cancelling its durable server turn.
|
|
123
|
+
*/
|
|
124
|
+
resetResumeState() {
|
|
125
|
+
this._expectToolContinuation = false;
|
|
126
|
+
this.cancelPendingResume();
|
|
127
|
+
this._detachResumeStream?.();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Re-send the active handshake request on the latest socket generation. This
|
|
131
|
+
* preserves one AI SDK resume operation while recovering a request/reply lost
|
|
132
|
+
* with the previous WebSocket.
|
|
133
|
+
*/
|
|
134
|
+
retryPendingResume() {
|
|
135
|
+
const retry = this._retryResumeProbe;
|
|
136
|
+
if (!retry) return false;
|
|
137
|
+
retry();
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
100
141
|
* Called by onAgentMessage when it receives CF_AGENT_STREAM_RESUMING.
|
|
101
142
|
* If reconnectToStream is waiting, this handles the resume handshake
|
|
102
143
|
* (ACK + stream creation) and returns true. Otherwise returns false
|
|
@@ -112,10 +153,9 @@ var WebSocketChatTransport = class {
|
|
|
112
153
|
* If reconnectToStream is waiting, resolves the promise with null
|
|
113
154
|
* immediately (no 5-second timeout). Returns true if handled.
|
|
114
155
|
*/
|
|
115
|
-
handleStreamResumeNone() {
|
|
156
|
+
handleStreamResumeNone(data = {}) {
|
|
116
157
|
if (!this._resumeNoneResolver) return false;
|
|
117
|
-
this._resumeNoneResolver();
|
|
118
|
-
return true;
|
|
158
|
+
return this._resumeNoneResolver(data);
|
|
119
159
|
}
|
|
120
160
|
/**
|
|
121
161
|
* Called by onAgentMessage when it receives CF_AGENT_STREAM_PENDING (#1784):
|
|
@@ -239,30 +279,47 @@ var WebSocketChatTransport = class {
|
|
|
239
279
|
return stream;
|
|
240
280
|
}
|
|
241
281
|
async reconnectToStream(_options) {
|
|
282
|
+
if (this.isAwaitingResume()) return null;
|
|
242
283
|
if (this._expectToolContinuation) {
|
|
243
284
|
this._expectToolContinuation = false;
|
|
244
285
|
return this._createToolContinuationStream();
|
|
245
286
|
}
|
|
246
287
|
const activeIds = this.activeRequestIds;
|
|
288
|
+
const probeId = nanoid(8);
|
|
247
289
|
return new Promise((resolve) => {
|
|
248
290
|
let resolved = false;
|
|
249
291
|
let timeout;
|
|
292
|
+
let resumeResolver = null;
|
|
293
|
+
let resumeNoneResolver = null;
|
|
294
|
+
let onStreamPending = null;
|
|
295
|
+
let retryResumeProbe = null;
|
|
296
|
+
const clearOwnedCallbacks = () => {
|
|
297
|
+
if (resumeResolver && this._resumeResolver === resumeResolver) this._resumeResolver = null;
|
|
298
|
+
if (resumeNoneResolver && this._resumeNoneResolver === resumeNoneResolver) this._resumeNoneResolver = null;
|
|
299
|
+
if (onStreamPending && this._onStreamPending === onStreamPending) this._onStreamPending = null;
|
|
300
|
+
if (retryResumeProbe && this._retryResumeProbe === retryResumeProbe) this._retryResumeProbe = null;
|
|
301
|
+
};
|
|
250
302
|
const done = (value) => {
|
|
251
303
|
if (resolved) return;
|
|
252
304
|
resolved = true;
|
|
253
|
-
|
|
254
|
-
this._resumeNoneResolver = null;
|
|
255
|
-
this._onStreamPending = null;
|
|
305
|
+
clearOwnedCallbacks();
|
|
256
306
|
if (timeout) clearTimeout(timeout);
|
|
257
307
|
resolve(value);
|
|
258
308
|
};
|
|
259
|
-
|
|
260
|
-
if (resolved) return;
|
|
309
|
+
const armTimeout = (delay) => {
|
|
261
310
|
if (timeout) clearTimeout(timeout);
|
|
262
|
-
timeout = setTimeout(() => done(null),
|
|
311
|
+
timeout = setTimeout(() => done(null), delay);
|
|
312
|
+
};
|
|
313
|
+
onStreamPending = () => {
|
|
314
|
+
if (resolved) return;
|
|
315
|
+
armTimeout(RESUME_PENDING_TIMEOUT_MS);
|
|
263
316
|
};
|
|
264
|
-
|
|
265
|
-
|
|
317
|
+
resumeNoneResolver = (data) => {
|
|
318
|
+
if (data.probeId && data.probeId !== probeId) return false;
|
|
319
|
+
done(null);
|
|
320
|
+
return true;
|
|
321
|
+
};
|
|
322
|
+
resumeResolver = (data) => {
|
|
266
323
|
const requestId = data.id;
|
|
267
324
|
activeIds?.add(requestId);
|
|
268
325
|
const stream = this._createResumeStream(requestId);
|
|
@@ -272,10 +329,21 @@ var WebSocketChatTransport = class {
|
|
|
272
329
|
}));
|
|
273
330
|
done(stream);
|
|
274
331
|
};
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
332
|
+
retryResumeProbe = () => {
|
|
333
|
+
if (resolved) return;
|
|
334
|
+
armTimeout(RESUME_PROBE_TIMEOUT_MS);
|
|
335
|
+
try {
|
|
336
|
+
this.agent.send(JSON.stringify({
|
|
337
|
+
type: "cf_agent_stream_resume_request",
|
|
338
|
+
probeId
|
|
339
|
+
}));
|
|
340
|
+
} catch {}
|
|
341
|
+
};
|
|
342
|
+
this._onStreamPending = onStreamPending;
|
|
343
|
+
this._resumeNoneResolver = resumeNoneResolver;
|
|
344
|
+
this._resumeResolver = resumeResolver;
|
|
345
|
+
this._retryResumeProbe = retryResumeProbe;
|
|
346
|
+
retryResumeProbe();
|
|
279
347
|
});
|
|
280
348
|
}
|
|
281
349
|
/**
|
|
@@ -292,24 +360,26 @@ var WebSocketChatTransport = class {
|
|
|
292
360
|
abortError.name = "AbortError";
|
|
293
361
|
let completed = false;
|
|
294
362
|
let requestId = null;
|
|
295
|
-
let readerController;
|
|
363
|
+
let readerController = null;
|
|
364
|
+
const probeId = nanoid(8);
|
|
296
365
|
let onResumeRef = null;
|
|
297
366
|
let onResumeNoneRef = null;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (
|
|
305
|
-
if (
|
|
367
|
+
let onStreamPendingRef = null;
|
|
368
|
+
let retryResumeProbeRef = null;
|
|
369
|
+
let abortToolContinuationRef = null;
|
|
370
|
+
let detachResumeStreamRef = null;
|
|
371
|
+
const clearOwnedHandshake = () => {
|
|
372
|
+
if (onResumeRef && this._resumeResolver === onResumeRef) this._resumeResolver = null;
|
|
373
|
+
if (onResumeNoneRef && this._resumeNoneResolver === onResumeNoneRef) this._resumeNoneResolver = null;
|
|
374
|
+
if (onStreamPendingRef && this._onStreamPending === onStreamPendingRef) this._onStreamPending = null;
|
|
375
|
+
if (retryResumeProbeRef && this._retryResumeProbe === retryResumeProbeRef) this._retryResumeProbe = null;
|
|
306
376
|
};
|
|
307
|
-
const finish = (action,
|
|
377
|
+
const finish = (action, keepRequestId = false) => {
|
|
308
378
|
if (completed) return;
|
|
309
379
|
completed = true;
|
|
310
|
-
this._abortToolContinuation = null;
|
|
311
|
-
this.
|
|
312
|
-
|
|
380
|
+
if (this._abortToolContinuation === abortToolContinuationRef) this._abortToolContinuation = null;
|
|
381
|
+
if (this._detachResumeStream === detachResumeStreamRef) this._detachResumeStream = null;
|
|
382
|
+
clearOwnedHandshake();
|
|
313
383
|
try {
|
|
314
384
|
action();
|
|
315
385
|
} catch {}
|
|
@@ -317,10 +387,10 @@ var WebSocketChatTransport = class {
|
|
|
317
387
|
streamController.abort();
|
|
318
388
|
};
|
|
319
389
|
const transport = this;
|
|
320
|
-
|
|
390
|
+
abortToolContinuationRef = () => {
|
|
321
391
|
if (completed) return false;
|
|
322
392
|
if (requestId === null) {
|
|
323
|
-
finish(() => readerController
|
|
393
|
+
finish(() => readerController?.error(abortError));
|
|
324
394
|
return true;
|
|
325
395
|
}
|
|
326
396
|
try {
|
|
@@ -329,71 +399,87 @@ var WebSocketChatTransport = class {
|
|
|
329
399
|
id: requestId
|
|
330
400
|
}));
|
|
331
401
|
} catch {}
|
|
332
|
-
finish(() => readerController
|
|
402
|
+
finish(() => readerController?.error(abortError), true);
|
|
333
403
|
return true;
|
|
334
404
|
};
|
|
405
|
+
this._abortToolContinuation = abortToolContinuationRef;
|
|
406
|
+
detachResumeStreamRef = () => {
|
|
407
|
+
if (completed) return false;
|
|
408
|
+
finish(() => readerController?.close());
|
|
409
|
+
return true;
|
|
410
|
+
};
|
|
411
|
+
this._detachResumeStream = detachResumeStreamRef;
|
|
335
412
|
return new ReadableStream({
|
|
336
413
|
start(controller) {
|
|
337
414
|
readerController = controller;
|
|
338
415
|
let timeout;
|
|
339
|
-
const
|
|
416
|
+
const armTimeout = (delay) => {
|
|
340
417
|
if (timeout) clearTimeout(timeout);
|
|
341
|
-
finish(() => controller.close(),
|
|
418
|
+
timeout = setTimeout(() => finish(() => controller.close()), delay);
|
|
419
|
+
};
|
|
420
|
+
const onResumeNone = (data) => {
|
|
421
|
+
if (data.probeId && data.probeId !== probeId) return false;
|
|
422
|
+
finish(() => controller.close());
|
|
423
|
+
return true;
|
|
342
424
|
};
|
|
343
425
|
const onResume = (data) => {
|
|
344
426
|
if (requestId) return;
|
|
345
427
|
requestId = data.id;
|
|
346
428
|
activeIds?.add(requestId);
|
|
347
|
-
|
|
348
|
-
transport._onStreamPending = null;
|
|
429
|
+
clearOwnedHandshake();
|
|
349
430
|
if (timeout) clearTimeout(timeout);
|
|
350
431
|
agent.send(JSON.stringify({
|
|
351
432
|
type: "cf_agent_stream_resume_ack",
|
|
352
433
|
id: requestId
|
|
353
434
|
}));
|
|
354
435
|
};
|
|
355
|
-
|
|
356
|
-
onResumeNoneRef = onResumeNone;
|
|
357
|
-
timeout = setTimeout(() => finish(() => controller.close(), onResume, onResumeNone), RESUME_PROBE_TIMEOUT_MS);
|
|
358
|
-
transport._onStreamPending = () => {
|
|
436
|
+
const onStreamPending = () => {
|
|
359
437
|
if (completed) return;
|
|
360
|
-
|
|
361
|
-
|
|
438
|
+
armTimeout(RESUME_PENDING_TIMEOUT_MS);
|
|
439
|
+
};
|
|
440
|
+
const retryResumeProbe = () => {
|
|
441
|
+
if (completed || requestId !== null) return;
|
|
442
|
+
armTimeout(RESUME_PROBE_TIMEOUT_MS);
|
|
443
|
+
try {
|
|
444
|
+
transport.agent.send(JSON.stringify({
|
|
445
|
+
type: "cf_agent_stream_resume_request",
|
|
446
|
+
probeId
|
|
447
|
+
}));
|
|
448
|
+
} catch {}
|
|
362
449
|
};
|
|
450
|
+
onResumeRef = onResume;
|
|
451
|
+
onResumeNoneRef = onResumeNone;
|
|
452
|
+
onStreamPendingRef = onStreamPending;
|
|
453
|
+
retryResumeProbeRef = retryResumeProbe;
|
|
363
454
|
transport._resumeResolver = onResume;
|
|
364
455
|
transport._resumeNoneResolver = onResumeNone;
|
|
456
|
+
transport._onStreamPending = onStreamPending;
|
|
457
|
+
transport._retryResumeProbe = retryResumeProbe;
|
|
365
458
|
const onMessage = (event) => {
|
|
366
459
|
try {
|
|
367
460
|
const data = JSON.parse(event.data);
|
|
368
461
|
if (data.type !== "cf_agent_use_chat_response" || requestId == null || data.id !== requestId) return;
|
|
369
462
|
if (data.error) {
|
|
370
|
-
finish(() => controller.error(new Error(data.body || "Stream error"))
|
|
463
|
+
finish(() => controller.error(new Error(data.body || "Stream error")));
|
|
371
464
|
return;
|
|
372
465
|
}
|
|
373
466
|
if (data.body?.trim()) try {
|
|
374
467
|
const chunk = JSON.parse(data.body);
|
|
375
468
|
controller.enqueue(chunk);
|
|
376
469
|
} catch {}
|
|
377
|
-
if (data.done) finish(() => controller.close()
|
|
470
|
+
if (data.done) finish(() => controller.close());
|
|
378
471
|
} catch {}
|
|
379
472
|
};
|
|
380
|
-
const onClose = () =>
|
|
381
|
-
if (timeout) clearTimeout(timeout);
|
|
382
|
-
finish(() => controller.close(), onResume, onResumeNone);
|
|
383
|
-
};
|
|
473
|
+
const onClose = () => finish(() => controller.close());
|
|
384
474
|
agent.addEventListener("message", onMessage, { signal: streamController.signal });
|
|
385
475
|
agent.addEventListener("close", onClose, { signal: streamController.signal });
|
|
386
|
-
|
|
387
|
-
agent.send(JSON.stringify({ type: "cf_agent_stream_resume_request" }));
|
|
388
|
-
} catch {
|
|
389
|
-
finish(() => controller.close());
|
|
390
|
-
}
|
|
476
|
+
retryResumeProbe();
|
|
391
477
|
},
|
|
392
478
|
cancel() {
|
|
393
479
|
if (requestId && transport.cancelOnClientAbort) {
|
|
394
480
|
transport.sendCancelFrame(requestId);
|
|
395
|
-
finish(() => {},
|
|
396
|
-
} else finish(() => {}
|
|
481
|
+
finish(() => {}, true);
|
|
482
|
+
} else finish(() => {});
|
|
397
483
|
}
|
|
398
484
|
});
|
|
399
485
|
}
|
|
@@ -408,24 +494,32 @@ var WebSocketChatTransport = class {
|
|
|
408
494
|
const abortError = /* @__PURE__ */ new Error("Aborted");
|
|
409
495
|
abortError.name = "AbortError";
|
|
410
496
|
let completed = false;
|
|
497
|
+
let detachResumeStream = null;
|
|
411
498
|
const finish = (action, keepId = false, clearServerTurn = true) => {
|
|
412
499
|
if (completed) return;
|
|
413
500
|
completed = true;
|
|
414
501
|
if (clearServerTurn) this.clearActiveServerTurn(requestId);
|
|
502
|
+
if (this._detachResumeStream === detachResumeStream) this._detachResumeStream = null;
|
|
415
503
|
try {
|
|
416
504
|
action();
|
|
417
505
|
} catch {}
|
|
418
506
|
if (!keepId) activeIds?.delete(requestId);
|
|
419
507
|
chunkController.abort();
|
|
420
508
|
};
|
|
509
|
+
let streamController = null;
|
|
421
510
|
const cancelActiveRequest = () => {
|
|
422
511
|
if (completed) return false;
|
|
423
|
-
finish(() => streamController
|
|
512
|
+
finish(() => streamController?.error(abortError), true);
|
|
424
513
|
return true;
|
|
425
514
|
};
|
|
426
515
|
this.setActiveServerTurn(requestId, cancelActiveRequest);
|
|
427
|
-
let streamController;
|
|
428
516
|
const transport = this;
|
|
517
|
+
detachResumeStream = () => {
|
|
518
|
+
if (completed) return false;
|
|
519
|
+
finish(() => streamController?.close());
|
|
520
|
+
return true;
|
|
521
|
+
};
|
|
522
|
+
this._detachResumeStream = detachResumeStream;
|
|
429
523
|
return new ReadableStream({
|
|
430
524
|
start(controller) {
|
|
431
525
|
streamController = controller;
|
|
@@ -763,7 +857,7 @@ function useAgentChat(options) {
|
|
|
763
857
|
return extraBody;
|
|
764
858
|
}
|
|
765
859
|
});
|
|
766
|
-
customTransportRef.current.
|
|
860
|
+
customTransportRef.current.setAgent(agentRef.current);
|
|
767
861
|
customTransportRef.current.setCancelOnClientAbort(cancelOnClientAbort);
|
|
768
862
|
const customTransport = customTransportRef.current;
|
|
769
863
|
const useChatHelpers = useChat({
|
|
@@ -772,16 +866,36 @@ function useAgentChat(options) {
|
|
|
772
866
|
messages: initialMessages,
|
|
773
867
|
transport: customTransport,
|
|
774
868
|
id: stableChatIdRef.current,
|
|
775
|
-
resume
|
|
869
|
+
resume: false
|
|
776
870
|
});
|
|
777
|
-
const { messages: chatMessages, setMessages, addToolResult, addToolApprovalResponse, sendMessage, resumeStream, status, stop } = useChatHelpers;
|
|
871
|
+
const { messages: chatMessages, setMessages, addToolResult, addToolApprovalResponse, sendMessage, resumeStream: rawResumeStream, status, stop } = useChatHelpers;
|
|
778
872
|
const statusRef = useRef(status);
|
|
779
873
|
statusRef.current = status;
|
|
874
|
+
const resumeGenerationRef = useRef(0);
|
|
875
|
+
const resumeOperationRef = useRef(null);
|
|
876
|
+
const reconnectProbePendingRef = useRef(false);
|
|
877
|
+
const reconnectProbeRunnerRef = useRef(null);
|
|
878
|
+
const invalidateResumeGeneration = useCallback(() => {
|
|
879
|
+
resumeGenerationRef.current++;
|
|
880
|
+
resumeOperationRef.current = null;
|
|
881
|
+
}, []);
|
|
882
|
+
const resumeStream = useCallback((...args) => {
|
|
883
|
+
const active = resumeOperationRef.current;
|
|
884
|
+
if (active) return active.promise;
|
|
885
|
+
const operation = {
|
|
886
|
+
generation: resumeGenerationRef.current,
|
|
887
|
+
promise: Promise.resolve()
|
|
888
|
+
};
|
|
889
|
+
resumeOperationRef.current = operation;
|
|
890
|
+
operation.promise = rawResumeStream(...args).finally(() => {
|
|
891
|
+
if (resumeOperationRef.current !== operation || resumeGenerationRef.current !== operation.generation) return;
|
|
892
|
+
resumeOperationRef.current = null;
|
|
893
|
+
reconnectProbeRunnerRef.current?.();
|
|
894
|
+
});
|
|
895
|
+
return operation.promise;
|
|
896
|
+
}, [rawResumeStream]);
|
|
780
897
|
const resumeStreamRef = useRef(resumeStream);
|
|
781
898
|
resumeStreamRef.current = resumeStream;
|
|
782
|
-
const hasConnectedOnceRef = useRef(false);
|
|
783
|
-
const resumeInFlightRef = useRef(false);
|
|
784
|
-
const resumeGenerationRef = useRef(0);
|
|
785
899
|
const resumingToolContinuationRef = useRef(false);
|
|
786
900
|
const pendingToolContinuationRef = useRef(false);
|
|
787
901
|
const observedToolContinuationRequestIdRef = useRef(null);
|
|
@@ -801,20 +915,24 @@ function useAgentChat(options) {
|
|
|
801
915
|
}, []);
|
|
802
916
|
const scheduleToolContinuationLaunch = useCallback(() => {
|
|
803
917
|
if (!pendingToolContinuationRef.current || statusRef.current !== "ready" || continuationLaunchTimerRef.current) return;
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
918
|
+
const timer = setTimeout(() => {
|
|
919
|
+
(async () => {
|
|
920
|
+
while (resumeOperationRef.current) await resumeOperationRef.current.promise.catch(() => {});
|
|
921
|
+
if (continuationLaunchTimerRef.current === timer) continuationLaunchTimerRef.current = null;
|
|
922
|
+
if (!pendingToolContinuationRef.current || statusRef.current !== "ready") return;
|
|
923
|
+
pendingToolContinuationRef.current = false;
|
|
924
|
+
const myGeneration = continuationGenerationRef.current;
|
|
925
|
+
customTransport.expectToolContinuation();
|
|
926
|
+
await resumeStream().catch((error) => {
|
|
927
|
+
console.error("[useAgentChat] Tool continuation resume failed:", error);
|
|
928
|
+
}).finally(() => {
|
|
929
|
+
if (continuationGenerationRef.current !== myGeneration) return;
|
|
930
|
+
resumingToolContinuationRef.current = false;
|
|
931
|
+
setIsToolContinuation(false);
|
|
932
|
+
});
|
|
933
|
+
})();
|
|
817
934
|
}, 0);
|
|
935
|
+
continuationLaunchTimerRef.current = timer;
|
|
818
936
|
}, [customTransport, resumeStream]);
|
|
819
937
|
const startToolContinuation = useCallback(() => {
|
|
820
938
|
if (!autoContinueAfterToolResult || resumingToolContinuationRef.current) return;
|
|
@@ -1203,7 +1321,12 @@ function useAgentChat(options) {
|
|
|
1203
1321
|
});
|
|
1204
1322
|
break;
|
|
1205
1323
|
case "cf_agent_stream_resume_none":
|
|
1206
|
-
customTransport.handleStreamResumeNone()
|
|
1324
|
+
if (customTransport.handleStreamResumeNone(data) && data.reason === STREAM_RESUME_NONE_REASONS.IDLE && typeof data.probeId === "string") {
|
|
1325
|
+
const result = transition(streamStateRef.current, { type: "clear" });
|
|
1326
|
+
streamStateRef.current = result.state;
|
|
1327
|
+
setIsServerStreaming(result.isStreaming);
|
|
1328
|
+
if (observedToolContinuationRequestIdRef.current !== null) resetToolContinuation();
|
|
1329
|
+
}
|
|
1207
1330
|
break;
|
|
1208
1331
|
case "cf_agent_stream_pending":
|
|
1209
1332
|
customTransport.handleStreamPending();
|
|
@@ -1274,6 +1397,7 @@ function useAgentChat(options) {
|
|
|
1274
1397
|
localResponseIds.delete(data.id);
|
|
1275
1398
|
localRequestIdsRef.current.delete(data.id);
|
|
1276
1399
|
fallbackAckedResumeRequestIdsRef.current.delete(data.id);
|
|
1400
|
+
if (observedToolContinuationRequestIdRef.current === data.id) resetToolContinuation();
|
|
1277
1401
|
}
|
|
1278
1402
|
return;
|
|
1279
1403
|
}
|
|
@@ -1317,28 +1441,51 @@ function useAgentChat(options) {
|
|
|
1317
1441
|
}
|
|
1318
1442
|
}
|
|
1319
1443
|
const fallbackAckedResumeRequestIds = fallbackAckedResumeRequestIdsRef.current;
|
|
1444
|
+
let socketIsOpen = false;
|
|
1445
|
+
let sawClose = false;
|
|
1446
|
+
let disposed = false;
|
|
1447
|
+
const clearFallbackObserver = () => {
|
|
1448
|
+
const result = transition(streamStateRef.current, { type: "clear" });
|
|
1449
|
+
streamStateRef.current = result.state;
|
|
1450
|
+
setIsServerStreaming(result.isStreaming);
|
|
1451
|
+
if (observedToolContinuationRequestIdRef.current !== null) resetToolContinuation();
|
|
1452
|
+
};
|
|
1453
|
+
const tryPendingReconnectProbe = () => {
|
|
1454
|
+
if (disposed || !socketIsOpen || !reconnectProbePendingRef.current) return;
|
|
1455
|
+
if (!resume) {
|
|
1456
|
+
reconnectProbePendingRef.current = false;
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
if (customTransport.retryPendingResume()) {
|
|
1460
|
+
reconnectProbePendingRef.current = false;
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
const canReconcileObservedContinuation = observedToolContinuationRequestIdRef.current !== null;
|
|
1464
|
+
if (statusRef.current !== "ready" && statusRef.current !== "error" || resumingToolContinuationRef.current && !canReconcileObservedContinuation || resumeOperationRef.current !== null) return;
|
|
1465
|
+
reconnectProbePendingRef.current = false;
|
|
1466
|
+
resumeStreamRef.current().catch(() => {});
|
|
1467
|
+
};
|
|
1468
|
+
reconnectProbeRunnerRef.current = tryPendingReconnectProbe;
|
|
1320
1469
|
function onAgentClose() {
|
|
1470
|
+
socketIsOpen = false;
|
|
1471
|
+
sawClose = true;
|
|
1321
1472
|
fallbackAckedResumeRequestIds.clear();
|
|
1473
|
+
if (!resume) clearFallbackObserver();
|
|
1322
1474
|
}
|
|
1323
1475
|
function onAgentOpen() {
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
const resumeStreamFn = resumeStreamRef.current;
|
|
1330
|
-
if (!resumeStreamFn) return;
|
|
1331
|
-
resumeInFlightRef.current = true;
|
|
1332
|
-
const myGeneration = resumeGenerationRef.current;
|
|
1333
|
-
Promise.resolve(resumeStreamFn()).catch(() => {}).finally(() => {
|
|
1334
|
-
if (resumeGenerationRef.current !== myGeneration) return;
|
|
1335
|
-
resumeInFlightRef.current = false;
|
|
1336
|
-
});
|
|
1476
|
+
socketIsOpen = true;
|
|
1477
|
+
if (!sawClose) return;
|
|
1478
|
+
sawClose = false;
|
|
1479
|
+
reconnectProbePendingRef.current = true;
|
|
1480
|
+
tryPendingReconnectProbe();
|
|
1337
1481
|
}
|
|
1338
1482
|
agent.addEventListener("message", onAgentMessage);
|
|
1339
1483
|
agent.addEventListener("close", onAgentClose);
|
|
1340
1484
|
agent.addEventListener("open", onAgentOpen);
|
|
1341
1485
|
return () => {
|
|
1486
|
+
disposed = true;
|
|
1487
|
+
if (reconnectProbeRunnerRef.current === tryPendingReconnectProbe) reconnectProbeRunnerRef.current = null;
|
|
1488
|
+
reconnectProbePendingRef.current = false;
|
|
1342
1489
|
agent.removeEventListener("message", onAgentMessage);
|
|
1343
1490
|
agent.removeEventListener("close", onAgentClose);
|
|
1344
1491
|
agent.removeEventListener("open", onAgentOpen);
|
|
@@ -1348,8 +1495,8 @@ function useAgentChat(options) {
|
|
|
1348
1495
|
setIsRecovering(false);
|
|
1349
1496
|
protectedStreamingAssistantRef.current = null;
|
|
1350
1497
|
localResponseIds.clear();
|
|
1351
|
-
|
|
1352
|
-
|
|
1498
|
+
customTransport.resetResumeState();
|
|
1499
|
+
invalidateResumeGeneration();
|
|
1353
1500
|
};
|
|
1354
1501
|
}, [
|
|
1355
1502
|
agent,
|
|
@@ -1360,8 +1507,16 @@ function useAgentChat(options) {
|
|
|
1360
1507
|
resetToolContinuation,
|
|
1361
1508
|
resetMatchingHydratedAssistantForReplay,
|
|
1362
1509
|
restoreProtectedStreamingAssistant,
|
|
1363
|
-
resetLocalChatState
|
|
1510
|
+
resetLocalChatState,
|
|
1511
|
+
invalidateResumeGeneration
|
|
1364
1512
|
]);
|
|
1513
|
+
useEffect(() => {
|
|
1514
|
+
if (!resume) return;
|
|
1515
|
+
resumeStream().catch(() => {});
|
|
1516
|
+
}, [resume, resumeStream]);
|
|
1517
|
+
useEffect(() => {
|
|
1518
|
+
reconnectProbeRunnerRef.current?.();
|
|
1519
|
+
}, [isToolContinuation, status]);
|
|
1365
1520
|
const addToolResultAndSendMessage = async (args) => {
|
|
1366
1521
|
const { toolCallId } = args;
|
|
1367
1522
|
const toolName = "tool" in args ? args.tool : "";
|
|
@@ -1464,6 +1619,7 @@ function useAgentChat(options) {
|
|
|
1464
1619
|
const isStreaming = status === "streaming" || effectiveIsServerStreaming;
|
|
1465
1620
|
return {
|
|
1466
1621
|
...useChatHelpers,
|
|
1622
|
+
resumeStream,
|
|
1467
1623
|
messages: messagesWithToolResults,
|
|
1468
1624
|
isServerStreaming: effectiveIsServerStreaming,
|
|
1469
1625
|
isStreaming,
|