agents 0.24.0 → 0.26.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-routing-B2XLNMxq.d.ts → agent-routing-0DR0Tk2v.d.ts} +205 -132
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-routing.js.map +1 -1
- package/dist/agent-tool-types.d.ts +28 -22
- package/dist/agent-tool-types.js +23 -1
- package/dist/agent-tool-types.js.map +1 -1
- package/dist/{agent-tools-y7zLfw4Q.js → agent-tools-CdJC-8Dc.js} +72 -9
- package/dist/agent-tools-CdJC-8Dc.js.map +1 -0
- package/dist/{agent-tools-Dj5Mzlw7.d.ts → agent-tools-YrATZJu2.d.ts} +34 -20
- package/dist/agent-tools.d.ts +20 -18
- package/dist/agent-tools.js.map +1 -1
- package/dist/ai-fm7xx1BK.js +312 -0
- package/dist/ai-fm7xx1BK.js.map +1 -0
- package/dist/base64-redaction-C1Uu29t5.js +59 -0
- package/dist/base64-redaction-C1Uu29t5.js.map +1 -0
- package/dist/browser/ai-sdk.d.ts +90 -0
- package/dist/browser/ai-sdk.js +51 -0
- package/dist/browser/ai-sdk.js.map +1 -0
- package/dist/browser/ai.d.ts +4 -4
- package/dist/browser/ai.js +1 -347
- package/dist/browser/index.d.ts +81 -79
- package/dist/browser/index.js +361 -2
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/tanstack-ai.d.ts +96 -1
- package/dist/browser/tanstack-ai.js +62 -2
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/browser-DE3d-RLk.d.ts +275 -0
- package/dist/browser-tool-CQ10e6Kj.js +480 -0
- package/dist/browser-tool-CQ10e6Kj.js.map +1 -0
- package/dist/browser-tool-DuNdlwZ9.d.ts +73 -0
- package/dist/capability-B4WbF81e.js.map +1 -1
- package/dist/{capability-runner-BUBa6Ake.d.ts → capability-runner-DBAnGr6L.d.ts} +7 -2
- package/dist/chat/index.d.ts +340 -234
- package/dist/chat/index.js +592 -66
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +42 -2
- package/dist/chat/react.js +191 -35
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/transport.d.ts +1 -1
- package/dist/chat/transport.js +1 -1
- package/dist/chat-sdk/index.d.ts +17 -3
- package/dist/chat-sdk/index.js +28 -2
- package/dist/chat-sdk/index.js.map +1 -1
- package/dist/{client-C9DAjZIY.js → client-4OlMIJx-.js} +102 -29
- package/dist/client-4OlMIJx-.js.map +1 -0
- package/dist/{client-BxdkCuVy.d.ts → client-je3koD9Z.d.ts} +45 -10
- package/dist/client.d.ts +7 -5
- package/dist/client.js +2 -2
- package/dist/{connector-CptFKzRh.js → connector-58XDGvc3.js} +251 -120
- package/dist/connector-58XDGvc3.js.map +1 -0
- package/dist/{connector-CkQD4MK3.d.ts → connector-Cl-VqPmA.d.ts} +244 -146
- package/dist/context/index.d.ts +29 -0
- package/dist/context/index.js +64 -17
- package/dist/context/index.js.map +1 -1
- package/dist/current-agent-DhoDkSnH.js.map +1 -1
- package/dist/{current-agent-C4w86kcN.d.ts → current-agent-vAk_5JMt.d.ts} +4 -4
- package/dist/{durable-object-lifecycle-ei7M7lqB.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts} +15 -7
- package/dist/harness/pi/index.d.ts +344 -0
- package/dist/harness/pi/index.js +873 -0
- package/dist/harness/pi/index.js.map +1 -0
- package/dist/images-iiTDSyRu.js +1631 -0
- package/dist/images-iiTDSyRu.js.map +1 -0
- package/dist/{index-Dlgtd3Mj.d.ts → index-BreL0YrQ.d.ts} +2 -2
- package/dist/index-kbSUIer3.d.ts +343 -0
- package/dist/index.d.ts +46 -44
- package/dist/index.js +1 -1
- package/dist/internal_context.d.ts +1 -1
- package/dist/lifecycle/index.d.ts +3 -3
- package/dist/lifecycle/index.js +1 -1
- package/dist/{lifecycle-Mm_jQh7r.js → lifecycle-CFu6OV03.js} +39 -10
- package/dist/{lifecycle-Mm_jQh7r.js.map → lifecycle-CFu6OV03.js.map} +1 -1
- package/dist/mcp/client/index.d.ts +20 -20
- package/dist/mcp/client/x402.js +21 -19
- package/dist/mcp/client/x402.js.map +1 -1
- package/dist/mcp/index.d.ts +35 -35
- package/dist/mcp/index.js +1 -1
- package/dist/{message-builder-BymO4N_D.js → message-builder-DAl9AhHH.js} +89 -4
- package/dist/message-builder-DAl9AhHH.js.map +1 -0
- package/dist/models/ai-sdk/index.d.ts +353 -0
- package/dist/models/ai-sdk/index.js +2443 -0
- package/dist/models/ai-sdk/index.js.map +1 -0
- package/dist/models/pi-ai/index.d.ts +313 -0
- package/dist/models/pi-ai/index.js +1795 -0
- package/dist/models/pi-ai/index.js.map +1 -0
- package/dist/observability/ai/index.js +20 -4
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/{protocol-B0nh6KNf.js → protocol-kHsr-f0X.js} +26 -12
- package/dist/{protocol-B0nh6KNf.js.map → protocol-kHsr-f0X.js.map} +1 -1
- package/dist/queue/index.d.ts +3 -3
- package/dist/queue/index.js +1 -1
- package/dist/{queue-BC9F5cUo.js → queue-BB-zfIYF.js} +2 -2
- package/dist/{queue-BC9F5cUo.js.map → queue-BB-zfIYF.js.map} +1 -1
- package/dist/react.d.ts +6 -6
- package/dist/react.js +160 -64
- package/dist/react.js.map +1 -1
- package/dist/{retries-D9B2UCq3.d.ts → retries-CV6AkXmQ.d.ts} +18 -9
- package/dist/retries.d.ts +11 -9
- package/dist/retries.js +11 -1
- package/dist/retries.js.map +1 -1
- package/dist/routing/index.d.ts +2 -2
- package/dist/routing/index.js +1 -1
- package/dist/{scheduler-D_KHqxBa.js → scheduler-DZ3xQfeK.js} +2 -2
- package/dist/{scheduler-D_KHqxBa.js.map → scheduler-DZ3xQfeK.js.map} +1 -1
- package/dist/{scheduler-BGq6M5Kd.d.ts → scheduler-RdACNXDP.d.ts} +3 -3
- package/dist/schedules/index.d.ts +1 -1
- package/dist/schedules/index.js +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +70 -3
- package/dist/sessions/index.js +276 -67
- package/dist/sessions/index.js.map +1 -1
- package/dist/skills/index.d.ts +29 -314
- package/dist/socket-address.d.ts +16 -0
- package/dist/socket-address.js +19 -0
- package/dist/socket-address.js.map +1 -0
- package/dist/{src-BNU3ZiJM.js → src-BZeu-SCD.js} +423 -115
- package/dist/src-BZeu-SCD.js.map +1 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/state/index.js +1 -1
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.js +1 -1
- package/dist/{streams-BcE8JP2X.d.ts → streams-Cmkj9QfW.d.ts} +4 -2
- package/dist/{streams-CCPRV6dt.js → streams-DW-UCOUc.js} +9 -2
- package/dist/streams-DW-UCOUc.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/tasks/index.d.ts +1 -1
- package/dist/tasks/index.js +1 -1
- package/dist/{tasks-D4nLqVSI.js → tasks-68DESxf0.js} +2 -2
- package/dist/{tasks-D4nLqVSI.js.map → tasks-68DESxf0.js.map} +1 -1
- package/dist/{tasks-V2lCT8ZV.d.ts → tasks-9YxYtByh.d.ts} +2 -2
- package/dist/transport-PfFnt-Ld.d.ts +525 -0
- package/dist/{types-DCD6BNZH.d.ts → types-BA4anrjI.d.ts} +2 -2
- package/dist/voice/index.d.ts +2 -2
- package/dist/websockets/index.d.ts +23 -6
- package/dist/websockets/index.js +1 -1
- package/dist/{websockets-D7IzWbZb.js → websockets-Ci-LZFHH.js} +45 -20
- package/dist/{websockets-D7IzWbZb.js.map → websockets-Ci-LZFHH.js.map} +1 -1
- package/dist/wire-types-CnMt6_HR.js.map +1 -1
- package/dist/wire-types-CrjKvDyi.d.ts +300 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflows.d.ts +21 -21
- package/dist/workflows.js +1 -1
- package/dist/{ws-chat-transport-UNRIS2xl.d.ts → ws-chat-transport-BbT1A5Lc.d.ts} +51 -2
- package/dist/{ws-chat-transport-rWwta645.js → ws-chat-transport-cqMKq3IY.js} +143 -16
- package/dist/ws-chat-transport-cqMKq3IY.js.map +1 -0
- package/docs/agent-tools.md +29 -4
- package/docs/browse-the-web.md +87 -5
- package/docs/chat-agents.md +124 -16
- package/docs/context.md +27 -0
- package/docs/durable-execution.md +2 -0
- package/docs/harnesses/pi.md +173 -0
- package/docs/human-in-the-loop.md +4 -0
- package/docs/index.md +3 -0
- package/docs/lifecycle.md +20 -4
- package/docs/models-pi-ai.md +325 -0
- package/docs/models.md +504 -0
- package/docs/resumable-streaming.md +2 -0
- package/docs/routing.md +13 -13
- package/docs/sessions.md +36 -1
- package/docs/state.md +1 -0
- package/docs/sub-agents.md +9 -0
- package/package.json +40 -2
- package/dist/agent-tools-y7zLfw4Q.js.map +0 -1
- package/dist/browser/ai.js.map +0 -1
- package/dist/client-C9DAjZIY.js.map +0 -1
- package/dist/client-tools-aIBO0Fk7.d.ts +0 -53
- package/dist/connector-CptFKzRh.js.map +0 -1
- package/dist/message-builder-BymO4N_D.js.map +0 -1
- package/dist/src-BNU3ZiJM.js.map +0 -1
- package/dist/streams-CCPRV6dt.js.map +0 -1
- package/dist/ws-chat-transport-rWwta645.js.map +0 -1
package/dist/chat/react.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
l as ClientToolSchema,
|
|
3
|
+
t as ChatTurnOutcome
|
|
4
|
+
} from "../wire-types-CrjKvDyi.js";
|
|
2
5
|
import {
|
|
3
6
|
n as WebSocketChatTransport,
|
|
4
7
|
r as WebSocketChatTransportOptions,
|
|
5
8
|
t as AgentConnection
|
|
6
|
-
} from "../ws-chat-transport-
|
|
9
|
+
} from "../ws-chat-transport-BbT1A5Lc.js";
|
|
7
10
|
import { ChatInit, JSONSchema7, Tool, UIMessage } from "ai";
|
|
8
11
|
import { UseChatOptions, useChat } from "@ai-sdk/react";
|
|
9
12
|
|
|
@@ -190,6 +193,23 @@ type AddToolOutputOptions = {
|
|
|
190
193
|
| "output-error" /** Error message when state is "output-error" */;
|
|
191
194
|
errorText?: string;
|
|
192
195
|
};
|
|
196
|
+
/**
|
|
197
|
+
* A chat request that ended, passed to `onTurnEnd`.
|
|
198
|
+
*/
|
|
199
|
+
type ChatTurnEndEvent = {
|
|
200
|
+
/** The transport id of the request. */ requestId: string;
|
|
201
|
+
/**
|
|
202
|
+
* Ids of the user messages the request ended with. Absent when it did not
|
|
203
|
+
* end with a user message, or the server no longer has a record of it.
|
|
204
|
+
*/
|
|
205
|
+
messageIds?: string[];
|
|
206
|
+
outcome: Exclude<
|
|
207
|
+
ChatTurnOutcome,
|
|
208
|
+
"recovering"
|
|
209
|
+
> /** The error message, when `outcome` is `"error"`. */;
|
|
210
|
+
error?: string /** Whether this outcome was replayed on reconnect rather than seen live. */;
|
|
211
|
+
replay: boolean;
|
|
212
|
+
};
|
|
193
213
|
/**
|
|
194
214
|
* Callback for handling client-side tool execution.
|
|
195
215
|
* Called when a tool without server-side execute is invoked.
|
|
@@ -243,6 +263,9 @@ type UseAgentChatOptions<
|
|
|
243
263
|
/**
|
|
244
264
|
* Callback for handling client-side tool execution.
|
|
245
265
|
* Called when a tool without server-side `execute` is invoked by the LLM.
|
|
266
|
+
* It fires once the response stream ends, for each tool call still waiting
|
|
267
|
+
* for a result, so server tools that resolve in the same stream never
|
|
268
|
+
* reach it.
|
|
246
269
|
*
|
|
247
270
|
* Use this for:
|
|
248
271
|
* - Tools that need browser APIs (geolocation, camera, etc.)
|
|
@@ -263,6 +286,21 @@ type UseAgentChatOptions<
|
|
|
263
286
|
* ```
|
|
264
287
|
*/
|
|
265
288
|
onToolCall?: OnToolCallCallback;
|
|
289
|
+
/**
|
|
290
|
+
* Called once for each chat request that ends, with the ids of the user
|
|
291
|
+
* messages it carried. Use it to settle optimistic sends by the ids the
|
|
292
|
+
* client minted. It fires for this tab's requests, for requests from other
|
|
293
|
+
* connections, and for outcomes replayed on reconnect. It does not fire for
|
|
294
|
+
* a request that recovery continues under a new request; that request
|
|
295
|
+
* reports the same `messageIds` when it ends.
|
|
296
|
+
*
|
|
297
|
+
* A message id can appear in more than one event, for example when a send
|
|
298
|
+
* is skipped and a later turn answers it. The last event for an id wins.
|
|
299
|
+
*
|
|
300
|
+
* For a request this tab is streaming, it fires once `status` has
|
|
301
|
+
* settled, so it can call `sendMessage`.
|
|
302
|
+
*/
|
|
303
|
+
onTurnEnd?: (event: ChatTurnEndEvent) => void;
|
|
266
304
|
/**
|
|
267
305
|
* @deprecated Use `onToolCall` callback instead for automatic tool execution.
|
|
268
306
|
* @description Whether to automatically resolve tool calls that do not require human interaction.
|
|
@@ -444,6 +482,8 @@ declare function useAgentChat<
|
|
|
444
482
|
export {
|
|
445
483
|
AITool,
|
|
446
484
|
type AgentConnection,
|
|
485
|
+
ChatTurnEndEvent,
|
|
486
|
+
type ChatTurnOutcome,
|
|
447
487
|
type ClientToolSchema,
|
|
448
488
|
JSONSchemaType,
|
|
449
489
|
OnToolCallCallback,
|
package/dist/chat/react.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isSocketAddressPending } from "../socket-address.js";
|
|
2
|
+
import { i as transition, n as STREAM_RESUME_NONE_REASONS, r as observedDivergesFrom } from "../protocol-kHsr-f0X.js";
|
|
2
3
|
import "../wire-types-CnMt6_HR.js";
|
|
3
|
-
import { t as WebSocketChatTransport } from "../ws-chat-transport-
|
|
4
|
+
import { t as WebSocketChatTransport } from "../ws-chat-transport-cqMKq3IY.js";
|
|
4
5
|
import { getToolName, isToolUIPart } from "ai";
|
|
5
6
|
import { nanoid } from "nanoid";
|
|
6
7
|
import { use, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -165,6 +166,11 @@ async function getAgentMessages(options) {
|
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
/**
|
|
169
|
+
* Replays re-send only recent terminals, so duplicate suppression needs only
|
|
170
|
+
* a recent window of ended request ids.
|
|
171
|
+
*/
|
|
172
|
+
const MAX_REMEMBERED_ENDED_TURNS = 500;
|
|
173
|
+
/**
|
|
168
174
|
* Module-level cache for initial message fetches. Intentionally shared across
|
|
169
175
|
* all useAgentChat instances to deduplicate requests during React Strict Mode
|
|
170
176
|
* double-renders and re-renders. Cache keys include the agent URL, agent type,
|
|
@@ -197,6 +203,12 @@ function prependMissingHydratedMessages(hydratedMessages, currentMessages) {
|
|
|
197
203
|
if (missingHydratedMessages.length === 0) return currentMessages;
|
|
198
204
|
return [...missingHydratedMessages, ...currentMessages];
|
|
199
205
|
}
|
|
206
|
+
function restoreBufferedSends(snapshot, local, bufferedIds) {
|
|
207
|
+
if (bufferedIds.size === 0) return snapshot;
|
|
208
|
+
const snapshotIds = new Set(snapshot.map((message) => message.id));
|
|
209
|
+
const restored = local.filter((message) => bufferedIds.has(message.id) && !snapshotIds.has(message.id));
|
|
210
|
+
return restored.length === 0 ? snapshot : [...snapshot, ...restored];
|
|
211
|
+
}
|
|
200
212
|
/**
|
|
201
213
|
* React hook for building AI chat interfaces using an Agent
|
|
202
214
|
* @param options Chat options including the agent connection
|
|
@@ -216,7 +228,7 @@ function detectToolsRequiringConfirmation(tools) {
|
|
|
216
228
|
return Object.entries(tools).filter(([_name, tool]) => !tool.execute).map(([name]) => name);
|
|
217
229
|
}
|
|
218
230
|
function useAgentChat(options) {
|
|
219
|
-
const { agent, getInitialMessages, messages: optionsInitialMessages, onToolCall, onData, experimental_automaticToolResolution, tools, toolsRequiringConfirmation: manualToolsRequiringConfirmation, autoContinueAfterToolResult = true, autoSendAfterAllConfirmationsResolved = true, resume = true, cancelOnClientAbort = false, syncMessagesToServer = true, body: bodyOption, prepareSendMessagesRequest, throttle, experimental_throttle, ...rest } = options;
|
|
231
|
+
const { agent, getInitialMessages, messages: optionsInitialMessages, onToolCall, onTurnEnd, onData, experimental_automaticToolResolution, tools, toolsRequiringConfirmation: manualToolsRequiringConfirmation, autoContinueAfterToolResult = true, autoSendAfterAllConfirmationsResolved = true, resume = true, cancelOnClientAbort = false, syncMessagesToServer = true, body: bodyOption, prepareSendMessagesRequest, throttle, experimental_throttle, ...rest } = options;
|
|
220
232
|
if (manualToolsRequiringConfirmation) warnDeprecated("useAgentChat.toolsRequiringConfirmation", "The 'toolsRequiringConfirmation' option is deprecated. Use needsApproval on server-side tools instead. Will be removed in the next major version.");
|
|
221
233
|
if (experimental_automaticToolResolution) warnDeprecated("useAgentChat.experimental_automaticToolResolution", "The 'experimental_automaticToolResolution' option is deprecated. Use the onToolCall callback instead. Will be removed in the next major version.");
|
|
222
234
|
if (options.autoSendAfterAllConfirmationsResolved !== void 0) warnDeprecated("useAgentChat.autoSendAfterAllConfirmationsResolved", "The 'autoSendAfterAllConfirmationsResolved' option is deprecated. Use sendAutomaticallyWhen from AI SDK instead. Will be removed in the next major version.");
|
|
@@ -227,13 +239,38 @@ function useAgentChat(options) {
|
|
|
227
239
|
}, [manualToolsRequiringConfirmation, tools]);
|
|
228
240
|
const onToolCallRef = useRef(onToolCall);
|
|
229
241
|
onToolCallRef.current = onToolCall;
|
|
242
|
+
const onTurnEndRef = useRef(onTurnEnd);
|
|
243
|
+
onTurnEndRef.current = onTurnEnd;
|
|
244
|
+
const turnErrorsRef = useRef(/* @__PURE__ */ new Map());
|
|
245
|
+
const endedTurnIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
246
|
+
const pendingTurnEndsRef = useRef([]);
|
|
230
247
|
const onDataRef = useRef(onData);
|
|
231
248
|
onDataRef.current = onData;
|
|
232
|
-
const
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
249
|
+
const socketAddressPending = isSocketAddressPending(agent);
|
|
250
|
+
const committedAddressRef = useRef(null);
|
|
251
|
+
let address = committedAddressRef.current;
|
|
252
|
+
if (!socketAddressPending || address === null) {
|
|
253
|
+
const rawHttpUrl = agent.getHttpUrl();
|
|
254
|
+
const url = rawHttpUrl ? new URL(rawHttpUrl) : null;
|
|
255
|
+
url?.searchParams.delete("_pk");
|
|
256
|
+
const route = agent.partySocketOptions;
|
|
257
|
+
address = {
|
|
258
|
+
urlString: url?.toString() ?? null,
|
|
259
|
+
addressKey: JSON.stringify([Array.isArray(agent.path) ? agent.path.map((step) => [step.agent, step.name]) : [[agent.agent ?? "", agent.name ?? ""]], route ? [
|
|
260
|
+
route.host ?? null,
|
|
261
|
+
route.basePath ?? null,
|
|
262
|
+
route.prefix ?? null,
|
|
263
|
+
route.party ?? null,
|
|
264
|
+
route.path ?? null
|
|
265
|
+
] : null]),
|
|
266
|
+
agent: agent.agent,
|
|
267
|
+
name: agent.name
|
|
268
|
+
};
|
|
269
|
+
if (!socketAddressPending) committedAddressRef.current = address;
|
|
270
|
+
}
|
|
271
|
+
const agentUrl = address.urlString ? new URL(address.urlString) : null;
|
|
272
|
+
const agentUrlString = address.urlString;
|
|
273
|
+
const agentAddressKey = address.addressKey;
|
|
237
274
|
const resolvedInitialMessagesCacheKey = agentUrl ? `${agentUrl.origin}${agentUrl.pathname}|${agentAddressKey}` : null;
|
|
238
275
|
const initialMessagesCacheKey = agentAddressKey;
|
|
239
276
|
const stableChatIdRef = useRef(null);
|
|
@@ -275,9 +312,9 @@ function useAgentChat(options) {
|
|
|
275
312
|
requestCache.set(cacheKey, promise);
|
|
276
313
|
return promise;
|
|
277
314
|
}
|
|
278
|
-
const initialMessagesPromise = !(getInitialMessages === null ? false : getInitialMessages ? true : !!agentUrlString) ? null : doGetInitialMessages({
|
|
279
|
-
agent:
|
|
280
|
-
name:
|
|
315
|
+
const initialMessagesPromise = !(getInitialMessages === null || committedAddressRef.current === null ? false : getInitialMessages ? true : !!agentUrlString) ? null : doGetInitialMessages({
|
|
316
|
+
agent: address.agent,
|
|
317
|
+
name: address.name,
|
|
281
318
|
url: agentUrlString ?? void 0
|
|
282
319
|
}, initialMessagesCacheKey);
|
|
283
320
|
const initialMessages = initialMessagesPromise ? use(initialMessagesPromise) : optionsInitialMessages ?? [];
|
|
@@ -299,6 +336,7 @@ function useAgentChat(options) {
|
|
|
299
336
|
* Used by onAgentMessage to skip messages already handled by the transport.
|
|
300
337
|
*/
|
|
301
338
|
const localRequestIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
339
|
+
const pendingBufferedSendIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
302
340
|
const pendingReplayResumeRequestIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
303
341
|
const replayHydratedAssistantMessageIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
304
342
|
/**
|
|
@@ -317,6 +355,9 @@ function useAgentChat(options) {
|
|
|
317
355
|
agent: agentRef.current,
|
|
318
356
|
activeRequestIds: localRequestIdsRef.current,
|
|
319
357
|
cancelOnClientAbort,
|
|
358
|
+
onRequestBuffered: (messageId) => {
|
|
359
|
+
if (messageId) pendingBufferedSendIdsRef.current.add(messageId);
|
|
360
|
+
},
|
|
320
361
|
prepareBody: async ({ messages: msgs, trigger, messageId }) => {
|
|
321
362
|
let extraBody = {};
|
|
322
363
|
const currentBody = bodyOptionRef.current;
|
|
@@ -449,6 +490,8 @@ function useAgentChat(options) {
|
|
|
449
490
|
const isResolvingToolsRef = useRef(false);
|
|
450
491
|
const [toolResolutionTrigger, setToolResolutionTrigger] = useState(0);
|
|
451
492
|
const [clientToolResults, setClientToolResults] = useState(/* @__PURE__ */ new Map());
|
|
493
|
+
const clientToolResultsRef = useRef(clientToolResults);
|
|
494
|
+
clientToolResultsRef.current = clientToolResults;
|
|
452
495
|
const initialMessagesRef = useRef(initialMessages);
|
|
453
496
|
initialMessagesRef.current = initialMessages;
|
|
454
497
|
const seededInitialMessagesKeyRef = useRef(null);
|
|
@@ -569,10 +612,15 @@ function useAgentChat(options) {
|
|
|
569
612
|
fallbackAckedResumeRequestIdsRef.current.clear();
|
|
570
613
|
replayHydratedAssistantMessageIdsRef.current.clear();
|
|
571
614
|
protectedStreamingAssistantRef.current = null;
|
|
615
|
+
pendingBufferedSendIdsRef.current.clear();
|
|
616
|
+
customTransport.appliedChunks.clear();
|
|
617
|
+
turnErrorsRef.current.clear();
|
|
618
|
+
pendingTurnEndsRef.current = [];
|
|
572
619
|
}, [
|
|
573
620
|
markInitialMessagesSeeded,
|
|
574
621
|
setMessages,
|
|
575
|
-
resetToolContinuation
|
|
622
|
+
resetToolContinuation,
|
|
623
|
+
customTransport
|
|
576
624
|
]);
|
|
577
625
|
const sendMessageWithStreamingProtection = useCallback(async (message, options) => {
|
|
578
626
|
const request = sendMessage(message, options);
|
|
@@ -695,9 +743,33 @@ function useAgentChat(options) {
|
|
|
695
743
|
}));
|
|
696
744
|
if (autoContinueAfterToolResult) startToolContinuation();
|
|
697
745
|
}, [autoContinueAfterToolResult, startToolContinuation]);
|
|
746
|
+
const [isServerStreaming, setIsServerStreaming] = useState(false);
|
|
747
|
+
const [unresolvedObservedRequestId, setUnresolvedObservedRequestId] = useState(null);
|
|
748
|
+
const unresolvedObservedRequestIdRef = useRef(unresolvedObservedRequestId);
|
|
749
|
+
unresolvedObservedRequestIdRef.current = unresolvedObservedRequestId;
|
|
750
|
+
const [recoveringRequestId, setRecoveringRequestIdState] = useState(null);
|
|
751
|
+
const recoveringRequestIdRef = useRef(null);
|
|
752
|
+
const setRecoveringRequestId = useCallback((requestId) => {
|
|
753
|
+
recoveringRequestIdRef.current = requestId;
|
|
754
|
+
setRecoveringRequestIdState(requestId);
|
|
755
|
+
}, []);
|
|
756
|
+
useEffect(() => {
|
|
757
|
+
if (status === "submitted") {
|
|
758
|
+
setUnresolvedObservedRequestId(null);
|
|
759
|
+
setRecoveringRequestId(null);
|
|
760
|
+
}
|
|
761
|
+
}, [status, setRecoveringRequestId]);
|
|
762
|
+
useEffect(() => {
|
|
763
|
+
if (status === "submitted" || status === "streaming") return;
|
|
764
|
+
const events = pendingTurnEndsRef.current;
|
|
765
|
+
if (events.length === 0) return;
|
|
766
|
+
pendingTurnEndsRef.current = [];
|
|
767
|
+
for (const event of events) onTurnEndRef.current?.(event);
|
|
768
|
+
}, [status]);
|
|
698
769
|
useEffect(() => {
|
|
699
770
|
const currentOnToolCall = onToolCallRef.current;
|
|
700
771
|
if (!currentOnToolCall) return;
|
|
772
|
+
if (status === "streaming" || status === "submitted" || isServerStreaming || unresolvedObservedRequestId !== null || recoveringRequestId !== null) return;
|
|
701
773
|
const lastMsg = chatMessages[chatMessages.length - 1];
|
|
702
774
|
if (!lastMsg || lastMsg.role !== "assistant") return;
|
|
703
775
|
const pendingToolCalls = lastMsg.parts.filter((part) => isToolUIPart(part) && part.state === "input-available" && !processedToolCalls.current.has(part.toolCallId));
|
|
@@ -739,15 +811,53 @@ function useAgentChat(options) {
|
|
|
739
811
|
}
|
|
740
812
|
}, [
|
|
741
813
|
chatMessages,
|
|
814
|
+
status,
|
|
815
|
+
isServerStreaming,
|
|
816
|
+
unresolvedObservedRequestId,
|
|
817
|
+
recoveringRequestId,
|
|
742
818
|
sendToolOutputToServer,
|
|
743
819
|
addToolResult,
|
|
744
820
|
finishOnToolCall
|
|
745
821
|
]);
|
|
746
822
|
const streamStateRef = useRef({ status: "idle" });
|
|
747
|
-
const [isServerStreaming, setIsServerStreaming] = useState(false);
|
|
748
823
|
const [isRecovering, setIsRecovering] = useState(false);
|
|
749
824
|
useEffect(() => {
|
|
750
825
|
const localResponseIds = localResponseMessageIdsRef.current;
|
|
826
|
+
const turnErrors = turnErrorsRef.current;
|
|
827
|
+
const endedTurnIds = endedTurnIdsRef.current;
|
|
828
|
+
let idleProbeNeeded = false;
|
|
829
|
+
let idleProbeId = null;
|
|
830
|
+
let serverReportsRecovery = false;
|
|
831
|
+
function reportTurnEnd(frame) {
|
|
832
|
+
if (frame.error && !frame.done) {
|
|
833
|
+
turnErrors.set(frame.id, frame.body);
|
|
834
|
+
if (turnErrors.size > MAX_REMEMBERED_ENDED_TURNS) {
|
|
835
|
+
const oldest = turnErrors.keys().next().value;
|
|
836
|
+
if (oldest !== void 0) turnErrors.delete(oldest);
|
|
837
|
+
}
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
if (!frame.done) return;
|
|
841
|
+
const earlierError = turnErrors.get(frame.id);
|
|
842
|
+
turnErrors.delete(frame.id);
|
|
843
|
+
const outcome = frame.error ? "error" : frame.outcome ?? (earlierError !== void 0 ? "error" : "completed");
|
|
844
|
+
if (outcome === "recovering" || endedTurnIds.has(frame.id)) return;
|
|
845
|
+
endedTurnIds.add(frame.id);
|
|
846
|
+
if (endedTurnIds.size > MAX_REMEMBERED_ENDED_TURNS) {
|
|
847
|
+
const oldest = endedTurnIds.values().next().value;
|
|
848
|
+
if (oldest !== void 0) endedTurnIds.delete(oldest);
|
|
849
|
+
}
|
|
850
|
+
const error = frame.error ? frame.body : earlierError;
|
|
851
|
+
const event = {
|
|
852
|
+
requestId: frame.id,
|
|
853
|
+
...frame.messageIds ? { messageIds: frame.messageIds } : {},
|
|
854
|
+
outcome,
|
|
855
|
+
...outcome === "error" && error ? { error } : {},
|
|
856
|
+
replay: frame.replay === true
|
|
857
|
+
};
|
|
858
|
+
if (localRequestIdsRef.current.has(frame.id) && (statusRef.current === "submitted" || statusRef.current === "streaming")) pendingTurnEndsRef.current.push(event);
|
|
859
|
+
else onTurnEndRef.current?.(event);
|
|
860
|
+
}
|
|
751
861
|
/**
|
|
752
862
|
* Unified message handler that parses JSON once and dispatches based on type.
|
|
753
863
|
* Avoids duplicate parsing overhead from separate listeners.
|
|
@@ -764,24 +874,31 @@ function useAgentChat(options) {
|
|
|
764
874
|
case "cf_agent_chat_clear":
|
|
765
875
|
streamStateRef.current = transition(streamStateRef.current, { type: "clear" }).state;
|
|
766
876
|
setIsServerStreaming(false);
|
|
877
|
+
setUnresolvedObservedRequestId(null);
|
|
878
|
+
setRecoveringRequestId(null);
|
|
767
879
|
setIsRecovering(false);
|
|
768
880
|
resetLocalChatState();
|
|
769
881
|
break;
|
|
770
882
|
case "cf_agent_chat_recovering":
|
|
771
883
|
setIsRecovering(Boolean(data.recovering));
|
|
884
|
+
serverReportsRecovery = Boolean(data.recovering);
|
|
885
|
+
if (!data.recovering) setRecoveringRequestId(null);
|
|
772
886
|
break;
|
|
773
|
-
case "cf_agent_chat_messages":
|
|
887
|
+
case "cf_agent_chat_messages": {
|
|
888
|
+
const bufferedSendIds = data.connect ? new Set(pendingBufferedSendIdsRef.current) : /* @__PURE__ */ new Set();
|
|
889
|
+
pendingBufferedSendIdsRef.current.clear();
|
|
774
890
|
setMessages((currentMessages) => {
|
|
775
891
|
let next = preserveProtectedStreamingAssistant(data.messages, currentMessages);
|
|
776
892
|
const observed = streamStateRef.current;
|
|
777
893
|
if (observed.status === "observing" && observed.accumulator.parts.length > 0) {
|
|
778
894
|
const snapshotIdx = next.findIndex((m) => m.id === observed.accumulator.messageId);
|
|
779
895
|
const snapshotParts = snapshotIdx >= 0 ? next[snapshotIdx].parts.length : 0;
|
|
780
|
-
if (observed.accumulator.parts.length >= snapshotParts) next = observed.accumulator.mergeInto(next);
|
|
896
|
+
if (observed.accumulator.parts.length >= snapshotParts && !observedDivergesFrom(observed.accumulator, next)) next = observed.accumulator.mergeInto(next);
|
|
781
897
|
}
|
|
782
|
-
return next;
|
|
898
|
+
return restoreBufferedSends(next, currentMessages, bufferedSendIds);
|
|
783
899
|
});
|
|
784
900
|
break;
|
|
901
|
+
}
|
|
785
902
|
case "cf_agent_message_updated":
|
|
786
903
|
setMessages((prevMessages) => {
|
|
787
904
|
const updatedMessage = data.message;
|
|
@@ -801,21 +918,33 @@ function useAgentChat(options) {
|
|
|
801
918
|
return prevMessages;
|
|
802
919
|
});
|
|
803
920
|
break;
|
|
804
|
-
case "cf_agent_stream_resume_none":
|
|
805
|
-
|
|
921
|
+
case "cf_agent_stream_resume_none": {
|
|
922
|
+
const handled = customTransport.handleStreamResumeNone(data);
|
|
923
|
+
const answersIdleProbe = idleProbeId !== null && data.probeId === idleProbeId;
|
|
924
|
+
if (answersIdleProbe) idleProbeId = null;
|
|
925
|
+
if ((handled || answersIdleProbe) && data.reason === STREAM_RESUME_NONE_REASONS.IDLE && typeof data.probeId === "string") {
|
|
806
926
|
const result = transition(streamStateRef.current, { type: "clear" });
|
|
807
927
|
streamStateRef.current = result.state;
|
|
808
928
|
setIsServerStreaming(result.isStreaming);
|
|
929
|
+
setUnresolvedObservedRequestId(null);
|
|
930
|
+
if (!serverReportsRecovery) setRecoveringRequestId(null);
|
|
809
931
|
if (observedToolContinuationRequestIdRef.current !== null) resetToolContinuation();
|
|
810
932
|
}
|
|
811
933
|
break;
|
|
934
|
+
}
|
|
812
935
|
case "cf_agent_stream_pending":
|
|
813
936
|
customTransport.handleStreamPending();
|
|
814
937
|
break;
|
|
815
938
|
case "cf_agent_stream_resuming": {
|
|
939
|
+
setUnresolvedObservedRequestId((current) => current === data.id ? current : null);
|
|
940
|
+
if (data.probeId !== void 0 && data.probeId === idleProbeId) idleProbeId = null;
|
|
816
941
|
const isEarlyToolContinuation = resumingToolContinuationRef.current && !customTransport.isAwaitingResume();
|
|
817
942
|
if (!resume && !customTransport.isAwaitingResume()) {
|
|
818
|
-
if (!isEarlyToolContinuation)
|
|
943
|
+
if (!isEarlyToolContinuation) {
|
|
944
|
+
if (data.probeId === void 0) setUnresolvedObservedRequestId(data.id);
|
|
945
|
+
if (recoveringRequestIdRef.current !== null && recoveringRequestIdRef.current !== data.id) setRecoveringRequestId(null);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
819
948
|
}
|
|
820
949
|
if (!resumingToolContinuationRef.current) pendingReplayResumeRequestIdsRef.current.add(data.id);
|
|
821
950
|
if (customTransport.handleStreamResuming(data)) return;
|
|
@@ -836,6 +965,7 @@ function useAgentChat(options) {
|
|
|
836
965
|
}).state;
|
|
837
966
|
customTransport.observeServerTurn(data.id);
|
|
838
967
|
setIsServerStreaming(true);
|
|
968
|
+
if (recoveringRequestIdRef.current !== null && recoveringRequestIdRef.current !== data.id) setRecoveringRequestId(null);
|
|
839
969
|
setIsRecovering(false);
|
|
840
970
|
fallbackAckedResumeRequestIdsRef.current.add(data.id);
|
|
841
971
|
agentRef.current.send(JSON.stringify({
|
|
@@ -845,6 +975,10 @@ function useAgentChat(options) {
|
|
|
845
975
|
break;
|
|
846
976
|
}
|
|
847
977
|
case "cf_agent_use_chat_response": {
|
|
978
|
+
if (data.done || data.error) setUnresolvedObservedRequestId((current) => current === data.id ? null : current);
|
|
979
|
+
if (data.done && data.outcome === "recovering") setRecoveringRequestId(data.id);
|
|
980
|
+
else if (recoveringRequestIdRef.current !== null && recoveringRequestIdRef.current !== data.id && !data.replay && data.outcome !== "skipped") setRecoveringRequestId(null);
|
|
981
|
+
reportTurnEnd(data);
|
|
848
982
|
if (localRequestIdsRef.current.has(data.id)) {
|
|
849
983
|
if (data.body?.trim()) try {
|
|
850
984
|
const chunkData = JSON.parse(data.body);
|
|
@@ -884,8 +1018,15 @@ function useAgentChat(options) {
|
|
|
884
1018
|
}
|
|
885
1019
|
let chunkData;
|
|
886
1020
|
if (data.replay && streamStateRef.current.status !== "observing" && !pendingReplayResumeRequestIdsRef.current.has(data.id)) return;
|
|
1021
|
+
if (data.done && !data.error && !(streamStateRef.current.status === "observing" && streamStateRef.current.streamId === data.id) && !pendingReplayResumeRequestIdsRef.current.has(data.id) && observedToolContinuationRequestIdRef.current !== data.id) {
|
|
1022
|
+
customTransport.appliedChunks.forget(data.id);
|
|
1023
|
+
customTransport.handleServerTurnCompleted(data.id);
|
|
1024
|
+
fallbackAckedResumeRequestIdsRef.current.delete(data.id);
|
|
1025
|
+
break;
|
|
1026
|
+
}
|
|
887
1027
|
if (data.error) {
|
|
888
1028
|
pendingReplayResumeRequestIdsRef.current.delete(data.id);
|
|
1029
|
+
customTransport.appliedChunks.forget(data.id);
|
|
889
1030
|
customTransport.handleServerTurnCompleted(data.id);
|
|
890
1031
|
fallbackAckedResumeRequestIdsRef.current.delete(data.id);
|
|
891
1032
|
setIsRecovering(false);
|
|
@@ -897,13 +1038,14 @@ function useAgentChat(options) {
|
|
|
897
1038
|
if (observedToolContinuationRequestIdRef.current === data.id) resetToolContinuation();
|
|
898
1039
|
break;
|
|
899
1040
|
}
|
|
1041
|
+
const appliedReplay = customTransport.appliedChunks.isAppliedReplay(data.id, data);
|
|
900
1042
|
if (data.body?.trim()) try {
|
|
901
1043
|
chunkData = JSON.parse(data.body);
|
|
902
1044
|
if (data.replay && !data.continuation && !resumingToolContinuationRef.current && observedToolContinuationRequestIdRef.current !== data.id && typeof chunkData.messageId === "string" && chunkData.type === "start") {
|
|
903
1045
|
pendingReplayResumeRequestIdsRef.current.delete(data.id);
|
|
904
1046
|
resetMatchingHydratedAssistantForReplay(chunkData.messageId);
|
|
905
1047
|
}
|
|
906
|
-
if (typeof chunkData.type === "string" && chunkData.type.startsWith("data-") && onDataRef.current) onDataRef.current(chunkData);
|
|
1048
|
+
if (typeof chunkData.type === "string" && chunkData.type.startsWith("data-") && !appliedReplay && onDataRef.current) onDataRef.current(chunkData);
|
|
907
1049
|
} catch (parseError) {
|
|
908
1050
|
console.warn("[useAgentChat] Failed to parse stream chunk:", parseError instanceof Error ? parseError.message : parseError, "body:", data.body?.slice(0, 100));
|
|
909
1051
|
}
|
|
@@ -911,14 +1053,17 @@ function useAgentChat(options) {
|
|
|
911
1053
|
if (data.done) {
|
|
912
1054
|
customTransport.handleServerTurnCompleted(data.id);
|
|
913
1055
|
fallbackAckedResumeRequestIdsRef.current.delete(data.id);
|
|
914
|
-
setIsRecovering(false);
|
|
1056
|
+
if (data.outcome !== "recovering") setIsRecovering(false);
|
|
915
1057
|
}
|
|
916
1058
|
const completedObservedToolContinuation = data.done && observedToolContinuationRequestIdRef.current === data.id;
|
|
1059
|
+
const alreadyApplied = appliedReplay && chunkData?.type !== "start";
|
|
1060
|
+
if (data.done) customTransport.appliedChunks.forget(data.id);
|
|
1061
|
+
else if (!alreadyApplied) customTransport.appliedChunks.record(data.id, data.seq);
|
|
917
1062
|
const result = transition(streamStateRef.current, {
|
|
918
1063
|
type: "response",
|
|
919
1064
|
streamId: data.id,
|
|
920
1065
|
messageId: nanoid(),
|
|
921
|
-
chunkData,
|
|
1066
|
+
chunkData: alreadyApplied ? void 0 : chunkData,
|
|
922
1067
|
done: data.done,
|
|
923
1068
|
error: data.error,
|
|
924
1069
|
replay: data.replay,
|
|
@@ -938,6 +1083,7 @@ function useAgentChat(options) {
|
|
|
938
1083
|
let sawClose = false;
|
|
939
1084
|
let disposed = false;
|
|
940
1085
|
const clearFallbackObserver = () => {
|
|
1086
|
+
if (streamStateRef.current.status === "observing") setUnresolvedObservedRequestId(streamStateRef.current.streamId);
|
|
941
1087
|
const result = transition(streamStateRef.current, { type: "clear" });
|
|
942
1088
|
streamStateRef.current = result.state;
|
|
943
1089
|
setIsServerStreaming(result.isStreaming);
|
|
@@ -947,6 +1093,14 @@ function useAgentChat(options) {
|
|
|
947
1093
|
if (disposed || !socketIsOpen || !reconnectProbePendingRef.current) return;
|
|
948
1094
|
if (!resume) {
|
|
949
1095
|
reconnectProbePendingRef.current = false;
|
|
1096
|
+
if (idleProbeNeeded) {
|
|
1097
|
+
idleProbeNeeded = false;
|
|
1098
|
+
idleProbeId = nanoid();
|
|
1099
|
+
agentRef.current.send(JSON.stringify({
|
|
1100
|
+
type: "cf_agent_stream_resume_request",
|
|
1101
|
+
probeId: idleProbeId
|
|
1102
|
+
}));
|
|
1103
|
+
}
|
|
950
1104
|
return;
|
|
951
1105
|
}
|
|
952
1106
|
if (customTransport.retryPendingResume()) {
|
|
@@ -962,8 +1116,14 @@ function useAgentChat(options) {
|
|
|
962
1116
|
function onAgentClose() {
|
|
963
1117
|
socketIsOpen = false;
|
|
964
1118
|
sawClose = true;
|
|
1119
|
+
serverReportsRecovery = false;
|
|
965
1120
|
fallbackAckedResumeRequestIds.clear();
|
|
966
|
-
|
|
1121
|
+
const unfinishedTurnId = customTransport.activeServerTurnId;
|
|
1122
|
+
if (unfinishedTurnId !== null) setUnresolvedObservedRequestId(unfinishedTurnId);
|
|
1123
|
+
if (!resume) {
|
|
1124
|
+
idleProbeNeeded ||= unfinishedTurnId !== null || streamStateRef.current.status === "observing" || unresolvedObservedRequestIdRef.current !== null || recoveringRequestIdRef.current !== null;
|
|
1125
|
+
clearFallbackObserver();
|
|
1126
|
+
}
|
|
967
1127
|
}
|
|
968
1128
|
function onAgentOpen() {
|
|
969
1129
|
socketIsOpen = true;
|
|
@@ -985,6 +1145,7 @@ function useAgentChat(options) {
|
|
|
985
1145
|
fallbackAckedResumeRequestIds.clear();
|
|
986
1146
|
streamStateRef.current = { status: "idle" };
|
|
987
1147
|
setIsServerStreaming(false);
|
|
1148
|
+
setUnresolvedObservedRequestId(null);
|
|
988
1149
|
setIsRecovering(false);
|
|
989
1150
|
protectedStreamingAssistantRef.current = null;
|
|
990
1151
|
localResponseIds.clear();
|
|
@@ -1001,7 +1162,8 @@ function useAgentChat(options) {
|
|
|
1001
1162
|
resetMatchingHydratedAssistantForReplay,
|
|
1002
1163
|
restoreProtectedStreamingAssistant,
|
|
1003
1164
|
resetLocalChatState,
|
|
1004
|
-
invalidateResumeGeneration
|
|
1165
|
+
invalidateResumeGeneration,
|
|
1166
|
+
setRecoveringRequestId
|
|
1005
1167
|
]);
|
|
1006
1168
|
useEffect(() => {
|
|
1007
1169
|
if (!resume) return;
|
|
@@ -1071,17 +1233,11 @@ function useAgentChat(options) {
|
|
|
1071
1233
|
useEffect(() => {
|
|
1072
1234
|
const currentToolCallIds = /* @__PURE__ */ new Set();
|
|
1073
1235
|
for (const msg of chatMessages) for (const part of msg.parts) if ("toolCallId" in part && part.toolCallId) currentToolCallIds.add(part.toolCallId);
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
for (const
|
|
1078
|
-
|
|
1079
|
-
break;
|
|
1080
|
-
}
|
|
1081
|
-
if (!hasStaleEntries) return prev;
|
|
1082
|
-
const newMap = /* @__PURE__ */ new Map();
|
|
1083
|
-
for (const [id, output] of prev) if (currentToolCallIds.has(id)) newMap.set(id, output);
|
|
1084
|
-
return newMap;
|
|
1236
|
+
const isStale = (id) => !currentToolCallIds.has(id);
|
|
1237
|
+
if ([...clientToolResultsRef.current.keys()].some(isStale)) setClientToolResults((prev) => {
|
|
1238
|
+
const next = /* @__PURE__ */ new Map();
|
|
1239
|
+
for (const [id, output] of prev) if (!isStale(id)) next.set(id, output);
|
|
1240
|
+
return next.size === prev.size ? prev : next;
|
|
1085
1241
|
});
|
|
1086
1242
|
for (const toolCallId of processedToolCalls.current) if (!currentToolCallIds.has(toolCallId)) processedToolCalls.current.delete(toolCallId);
|
|
1087
1243
|
}, [chatMessages]);
|