agents 0.23.0 → 0.25.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.
Files changed (187) hide show
  1. package/dist/{agent-routing-DE5zmCQ8.d.ts → agent-routing-0DR0Tk2v.d.ts} +270 -190
  2. package/dist/agent-routing.d.ts +1 -1
  3. package/dist/agent-routing.js.map +1 -1
  4. package/dist/agent-tool-types.d.ts +13 -7
  5. package/dist/agent-tool-types.js +23 -1
  6. package/dist/agent-tool-types.js.map +1 -1
  7. package/dist/{agent-tools-y7zLfw4Q.js → agent-tools-CdJC-8Dc.js} +72 -9
  8. package/dist/agent-tools-CdJC-8Dc.js.map +1 -0
  9. package/dist/{agent-tools-DtXMTDGM.d.ts → agent-tools-YrATZJu2.d.ts} +29 -15
  10. package/dist/agent-tools.d.ts +6 -4
  11. package/dist/agent-tools.js.map +1 -1
  12. package/dist/ai-fm7xx1BK.js +312 -0
  13. package/dist/ai-fm7xx1BK.js.map +1 -0
  14. package/dist/base64-redaction-C1Uu29t5.js +59 -0
  15. package/dist/base64-redaction-C1Uu29t5.js.map +1 -0
  16. package/dist/browser/ai-sdk.d.ts +90 -0
  17. package/dist/browser/ai-sdk.js +51 -0
  18. package/dist/browser/ai-sdk.js.map +1 -0
  19. package/dist/browser/ai.d.ts +4 -4
  20. package/dist/browser/ai.js +1 -347
  21. package/dist/browser/index.d.ts +81 -79
  22. package/dist/browser/index.js +361 -2
  23. package/dist/browser/index.js.map +1 -0
  24. package/dist/browser/tanstack-ai.d.ts +96 -1
  25. package/dist/browser/tanstack-ai.js +62 -2
  26. package/dist/browser/tanstack-ai.js.map +1 -1
  27. package/dist/browser-DE3d-RLk.d.ts +275 -0
  28. package/dist/browser-tool-CQ10e6Kj.js +480 -0
  29. package/dist/browser-tool-CQ10e6Kj.js.map +1 -0
  30. package/dist/browser-tool-DuNdlwZ9.d.ts +73 -0
  31. package/dist/{capability-BjSKYpzg.js → capability-B4WbF81e.js} +2 -1
  32. package/dist/capability-B4WbF81e.js.map +1 -0
  33. package/dist/{capability-runner-Be_-PLR1.d.ts → capability-runner-DBAnGr6L.d.ts} +45 -15
  34. package/dist/chat/index.d.ts +341 -232
  35. package/dist/chat/index.js +602 -72
  36. package/dist/chat/index.js.map +1 -1
  37. package/dist/chat/react.d.ts +42 -2
  38. package/dist/chat/react.js +191 -35
  39. package/dist/chat/react.js.map +1 -1
  40. package/dist/chat/transport.d.ts +1 -1
  41. package/dist/chat/transport.js +1 -1
  42. package/dist/chat-sdk/index.d.ts +17 -3
  43. package/dist/chat-sdk/index.js +28 -2
  44. package/dist/chat-sdk/index.js.map +1 -1
  45. package/dist/client-4OlMIJx-.js +591 -0
  46. package/dist/client-4OlMIJx-.js.map +1 -0
  47. package/dist/{client-jagG8a9_.js → client-fyQrLGkD.js} +2 -2
  48. package/dist/{client-jagG8a9_.js.map → client-fyQrLGkD.js.map} +1 -1
  49. package/dist/client-je3koD9Z.d.ts +446 -0
  50. package/dist/client.d.ts +29 -283
  51. package/dist/client.js +2 -274
  52. package/dist/{connector-CptFKzRh.js → connector-58XDGvc3.js} +251 -120
  53. package/dist/connector-58XDGvc3.js.map +1 -0
  54. package/dist/{connector-CkQD4MK3.d.ts → connector-Cl-VqPmA.d.ts} +244 -146
  55. package/dist/context/index.d.ts +29 -0
  56. package/dist/context/index.js +64 -17
  57. package/dist/context/index.js.map +1 -1
  58. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  59. package/dist/current-agent-vAk_5JMt.d.ts +93 -0
  60. package/dist/{current-agent-Da_C9a3b.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts} +26 -127
  61. package/dist/harnesses/pi/index.d.ts +348 -0
  62. package/dist/harnesses/pi/index.js +754 -0
  63. package/dist/harnesses/pi/index.js.map +1 -0
  64. package/dist/images-iiTDSyRu.js +1631 -0
  65. package/dist/images-iiTDSyRu.js.map +1 -0
  66. package/dist/index-BreL0YrQ.d.ts +78 -0
  67. package/dist/index-kbSUIer3.d.ts +343 -0
  68. package/dist/index.d.ts +43 -41
  69. package/dist/index.js +2 -2
  70. package/dist/internal_context.d.ts +1 -1
  71. package/dist/lifecycle/index.d.ts +19 -17
  72. package/dist/lifecycle/index.js +2 -2
  73. package/dist/{lifecycle-CMRGjZdw.js → lifecycle-CFu6OV03.js} +73 -29
  74. package/dist/lifecycle-CFu6OV03.js.map +1 -0
  75. package/dist/mcp/client/index.d.ts +20 -20
  76. package/dist/mcp/client/index.js +1 -1
  77. package/dist/mcp/client/x402.js +21 -19
  78. package/dist/mcp/client/x402.js.map +1 -1
  79. package/dist/mcp/index.d.ts +35 -35
  80. package/dist/mcp/index.js +2 -2
  81. package/dist/{message-builder-BymO4N_D.js → message-builder-DAl9AhHH.js} +89 -4
  82. package/dist/message-builder-DAl9AhHH.js.map +1 -0
  83. package/dist/models/ai-sdk/index.d.ts +353 -0
  84. package/dist/models/ai-sdk/index.js +2443 -0
  85. package/dist/models/ai-sdk/index.js.map +1 -0
  86. package/dist/models/pi-ai/index.d.ts +313 -0
  87. package/dist/models/pi-ai/index.js +1795 -0
  88. package/dist/models/pi-ai/index.js.map +1 -0
  89. package/dist/observability/ai/index.js +20 -4
  90. package/dist/observability/ai/index.js.map +1 -1
  91. package/dist/{protocol-B0nh6KNf.js → protocol-kHsr-f0X.js} +26 -12
  92. package/dist/{protocol-B0nh6KNf.js.map → protocol-kHsr-f0X.js.map} +1 -1
  93. package/dist/queue/index.d.ts +123 -0
  94. package/dist/queue/index.js +2 -0
  95. package/dist/queue-BB-zfIYF.js +406 -0
  96. package/dist/queue-BB-zfIYF.js.map +1 -0
  97. package/dist/react.d.ts +31 -13
  98. package/dist/react.js +190 -71
  99. package/dist/react.js.map +1 -1
  100. package/dist/{retries-D9Ds-1lz.d.ts → retries-CV6AkXmQ.d.ts} +23 -8
  101. package/dist/retries.d.ts +12 -8
  102. package/dist/retries.js +19 -1
  103. package/dist/retries.js.map +1 -1
  104. package/dist/routing/index.d.ts +2 -2
  105. package/dist/routing/index.js +1 -1
  106. package/dist/{scheduler-DD9NdYbF.js → scheduler-DZ3xQfeK.js} +4 -11
  107. package/dist/scheduler-DZ3xQfeK.js.map +1 -0
  108. package/dist/{scheduler-Dwh85ZGl.d.ts → scheduler-RdACNXDP.d.ts} +6 -6
  109. package/dist/schedules/index.d.ts +1 -1
  110. package/dist/schedules/index.js +1 -1
  111. package/dist/serializable.d.ts +1 -1
  112. package/dist/sessions/index.d.ts +70 -3
  113. package/dist/sessions/index.js +276 -67
  114. package/dist/sessions/index.js.map +1 -1
  115. package/dist/skills/index.d.ts +29 -314
  116. package/dist/socket-address.d.ts +16 -0
  117. package/dist/socket-address.js +19 -0
  118. package/dist/socket-address.js.map +1 -0
  119. package/dist/{src-DlSHshb2.js → src-BZeu-SCD.js} +541 -500
  120. package/dist/src-BZeu-SCD.js.map +1 -0
  121. package/dist/state/index.d.ts +6 -0
  122. package/dist/state/index.js +137 -0
  123. package/dist/state/index.js.map +1 -0
  124. package/dist/streams/index.d.ts +1 -1
  125. package/dist/streams/index.js +1 -1
  126. package/dist/{streams-D6tJ0NN9.d.ts → streams-Cmkj9QfW.d.ts} +4 -2
  127. package/dist/{streams-DZKgAj9b.js → streams-DW-UCOUc.js} +9 -2
  128. package/dist/streams-DW-UCOUc.js.map +1 -0
  129. package/dist/sub-routing.d.ts +12 -12
  130. package/dist/tasks/index.d.ts +1 -1
  131. package/dist/tasks/index.js +1 -1
  132. package/dist/{tasks-ylZgBjhj.js → tasks-68DESxf0.js} +3 -3
  133. package/dist/{tasks-ylZgBjhj.js.map → tasks-68DESxf0.js.map} +1 -1
  134. package/dist/{tasks-BRJ5zgya.d.ts → tasks-9YxYtByh.d.ts} +5 -5
  135. package/dist/transport-PfFnt-Ld.d.ts +525 -0
  136. package/dist/transport-protocol-BxyngQ_R.js +25 -0
  137. package/dist/transport-protocol-BxyngQ_R.js.map +1 -0
  138. package/dist/transport-protocol-ZTew5Wso.d.ts +27 -0
  139. package/dist/types-BA4anrjI.d.ts +70 -0
  140. package/dist/voice/index.d.ts +2 -2
  141. package/dist/websockets/index.d.ts +195 -69
  142. package/dist/websockets/index.js +3 -2
  143. package/dist/websockets-Ci-LZFHH.js +1030 -0
  144. package/dist/websockets-Ci-LZFHH.js.map +1 -0
  145. package/dist/wire-types-CnMt6_HR.js.map +1 -1
  146. package/dist/wire-types-CrjKvDyi.d.ts +300 -0
  147. package/dist/workflow-types.d.ts +25 -25
  148. package/dist/workflows.d.ts +21 -21
  149. package/dist/workflows.js +1 -1
  150. package/dist/{ws-chat-transport-UNRIS2xl.d.ts → ws-chat-transport-BbT1A5Lc.d.ts} +51 -2
  151. package/dist/{ws-chat-transport-rWwta645.js → ws-chat-transport-cqMKq3IY.js} +143 -16
  152. package/dist/ws-chat-transport-cqMKq3IY.js.map +1 -0
  153. package/docs/agent-class.md +2 -2
  154. package/docs/agent-tools.md +29 -4
  155. package/docs/browse-the-web.md +87 -5
  156. package/docs/chat-agents.md +124 -16
  157. package/docs/client-sdk.md +30 -0
  158. package/docs/context.md +27 -0
  159. package/docs/durable-execution.md +2 -0
  160. package/docs/harnesses/pi.md +91 -0
  161. package/docs/human-in-the-loop.md +4 -0
  162. package/docs/index.md +4 -1
  163. package/docs/lifecycle.md +114 -23
  164. package/docs/models-pi-ai.md +325 -0
  165. package/docs/models.md +504 -0
  166. package/docs/queue.md +135 -241
  167. package/docs/resumable-streaming.md +2 -0
  168. package/docs/retries.md +1 -1
  169. package/docs/routing.md +13 -13
  170. package/docs/sessions.md +36 -1
  171. package/docs/state.md +1 -0
  172. package/docs/sub-agents.md +12 -3
  173. package/package.json +51 -3
  174. package/dist/agent-tools-y7zLfw4Q.js.map +0 -1
  175. package/dist/browser/ai.js.map +0 -1
  176. package/dist/capability-BjSKYpzg.js.map +0 -1
  177. package/dist/client-tools-aIBO0Fk7.d.ts +0 -53
  178. package/dist/client.js.map +0 -1
  179. package/dist/connector-CptFKzRh.js.map +0 -1
  180. package/dist/lifecycle-CMRGjZdw.js.map +0 -1
  181. package/dist/message-builder-BymO4N_D.js.map +0 -1
  182. package/dist/scheduler-DD9NdYbF.js.map +0 -1
  183. package/dist/src-DlSHshb2.js.map +0 -1
  184. package/dist/streams-DZKgAj9b.js.map +0 -1
  185. package/dist/websockets-DUfRHPRq.js +0 -502
  186. package/dist/websockets-DUfRHPRq.js.map +0 -1
  187. package/dist/ws-chat-transport-rWwta645.js.map +0 -1
package/dist/react.js CHANGED
@@ -1,12 +1,44 @@
1
1
  import { camelCaseToKebabCase } from "./utils.js";
2
2
  import "./types.js";
3
- import { AgentConnectionError, createStubProxy, isTerminalCloseEvent } from "./client.js";
3
+ import { r as CAPNWEB_TRANSPORT_VALUE, t as CAPNWEB_TRANSPORT_QUERY } from "./transport-protocol-BxyngQ_R.js";
4
+ import { d as boundCapnWebSocket, i as NativeCallQueue, l as nativeCall, n as AgentConnectionError, o as createCloseClassifier, s as createStubProxy, u as splitCallOptions } from "./client-4OlMIJx-.js";
4
5
  import { buildSubAgentPathUnchecked } from "./sub-routing.js";
5
- import { n as applyAgentToolEvent, r as createAgentToolEventState } from "./agent-tools-y7zLfw4Q.js";
6
+ import { SOCKET_ADDRESS_PENDING } from "./socket-address.js";
7
+ import { i as createAgentToolEventState, n as agentToolEventDedupeKey, r as applyAgentToolEvent } from "./agent-tools-CdJC-8Dc.js";
6
8
  import { usePartySocket } from "partysocket/react";
7
9
  import { use, useCallback, useEffect, useMemo, useRef, useState } from "react";
8
10
  //#region src/react.tsx
9
11
  const queryCache = /* @__PURE__ */ new Map();
12
+ function socketDestinationKey(options) {
13
+ return JSON.stringify([
14
+ options.host ?? null,
15
+ options.basePath ?? null,
16
+ options.party ?? null,
17
+ options.prefix ?? null,
18
+ options.room ?? null,
19
+ options.path ?? null,
20
+ options.protocol ?? null,
21
+ options.protocols ?? null
22
+ ]);
23
+ }
24
+ function createReadyEntry() {
25
+ let resolvePromise;
26
+ const entry = {
27
+ promise: new Promise((r) => {
28
+ resolvePromise = r;
29
+ }),
30
+ resolved: false,
31
+ resolve: () => {
32
+ entry.resolved = true;
33
+ resolvePromise();
34
+ }
35
+ };
36
+ return entry;
37
+ }
38
+ /** The host `usePartySocket` connects to when none is given. */
39
+ function defaultSocketHost() {
40
+ return typeof window !== "undefined" ? window.location.host : "dummy-domain.com";
41
+ }
10
42
  function createCacheKey(agentNamespace, name, subChainOrDeps, deps) {
11
43
  if (deps === void 0) return JSON.stringify([
12
44
  agentNamespace,
@@ -64,7 +96,14 @@ const _testUtils = {
64
96
  };
65
97
  function useAgent(options) {
66
98
  const agentNamespace = camelCaseToKebabCase(options.agent);
67
- const { query, queryDeps, cacheTtl, sub: subOption, path: userPath, defaultCallTimeout, onConnectionError, shouldReconnectOnClose, ...restOptions } = options;
99
+ const { query, queryDeps, cacheTtl, sub: subOption, path: userPath, defaultCallTimeout, onConnectionError, shouldReconnectOnClose, transport = "cf-websocket", ...restOptions } = options;
100
+ const capnWebRef = useRef(null);
101
+ const transportRef = useRef(transport);
102
+ transportRef.current = transport;
103
+ const nativeQueueRef = useRef(new NativeCallQueue());
104
+ const socketClass = useMemo(() => transport === "capnweb" ? { WebSocket: boundCapnWebSocket((socket) => {
105
+ capnWebRef.current = socket;
106
+ }) } : {}, [transport]);
68
107
  const subChain = useMemo(() => (subOption ?? []).map((s) => ({
69
108
  agent: s.agent,
70
109
  name: s.name
@@ -101,9 +140,12 @@ function useAgent(options) {
101
140
  socket.send(pending.request);
102
141
  pending.sentOn = socket;
103
142
  }
143
+ const native = transportRef.current === "capnweb" ? capnWebRef.current : null;
144
+ if (native) nativeQueueRef.current.flush(native);
104
145
  };
105
146
  /** Reject (and remove) every still-queued (never transmitted) call. */
106
147
  const rejectQueuedCalls = (reason) => {
148
+ nativeQueueRef.current.rejectAll(reason);
107
149
  const error = new Error(reason);
108
150
  for (const [id, pending] of pendingCallsRef.current) if (pending.sentOn === null) {
109
151
  if (pending.timeoutId) clearTimeout(pending.timeoutId);
@@ -174,42 +216,57 @@ function useAgent(options) {
174
216
  const connectionErrorAddressKeyRef = useRef(null);
175
217
  const shouldReconnectOnCloseRef = useRef(shouldReconnectOnClose);
176
218
  shouldReconnectOnCloseRef.current = shouldReconnectOnClose;
177
- const classifyReconnect = useCallback((event) => (shouldReconnectOnCloseRef.current?.(event) ?? true) && !isTerminalCloseEvent(event), []);
178
- const [identity, setIdentity] = useState({
219
+ const maxRetriesRef = useRef(restOptions.maxRetries);
220
+ maxRetriesRef.current = restOptions.maxRetries;
221
+ const [closeClassifier] = useState(() => createCloseClassifier({
222
+ socket: () => socketRef.current,
223
+ shouldReconnectOnClose: () => shouldReconnectOnCloseRef.current,
224
+ maxRetries: () => maxRetriesRef.current
225
+ }));
226
+ const classifyReconnect = closeClassifier.shouldReconnectOnClose;
227
+ const [identity, setIdentity] = useState(() => ({
179
228
  name: leafName,
180
229
  agent: camelCaseToKebabCase(leafAgent),
181
- identified: false
182
- });
230
+ identified: false,
231
+ destination: null
232
+ }));
183
233
  const previousIdentityRef = useRef({
184
234
  name: null,
185
235
  agent: null
186
236
  });
187
- const readyRef = useRef(void 0);
188
- const resetReady = () => {
189
- let resolve;
190
- readyRef.current = {
191
- promise: new Promise((r) => {
192
- resolve = r;
193
- }),
194
- resolve
195
- };
237
+ const readyRef = useRef(/* @__PURE__ */ new Map());
238
+ const readyFor = (destination) => {
239
+ let entry = readyRef.current.get(destination);
240
+ if (!entry) {
241
+ entry = createReadyEntry();
242
+ readyRef.current.set(destination, entry);
243
+ }
244
+ return entry;
245
+ };
246
+ const resetReady = (destination) => {
247
+ if (readyRef.current.get(destination)?.resolved !== false) readyRef.current.set(destination, createReadyEntry());
196
248
  };
197
- if (!readyRef.current) resetReady();
198
249
  const mutableAgentRef = useRef(null);
199
250
  const combinedPath = useMemo(() => buildSubPath(subChain, userPath), [subChain, userPath]);
251
+ const socketQuery = transport === "capnweb" ? {
252
+ ...resolvedQuery,
253
+ [CAPNWEB_TRANSPORT_QUERY]: CAPNWEB_TRANSPORT_VALUE
254
+ } : resolvedQuery;
200
255
  const socketOptions = options.basePath ? {
201
256
  basePath: options.basePath,
202
257
  path: combinedPath || void 0,
203
- query: resolvedQuery,
258
+ query: socketQuery,
204
259
  ...restOptions,
260
+ ...socketClass,
205
261
  shouldReconnectOnClose: classifyReconnect
206
262
  } : {
207
263
  party: agentNamespace,
208
264
  prefix: "agents",
209
265
  room: options.name || "default",
210
266
  path: combinedPath || void 0,
211
- query: resolvedQuery,
267
+ query: socketQuery,
212
268
  ...restOptions,
269
+ ...socketClass,
213
270
  shouldReconnectOnClose: classifyReconnect
214
271
  };
215
272
  const socketEnabled = !awaitingQueryRefresh && (restOptions.enabled ?? true);
@@ -218,10 +275,65 @@ function useAgent(options) {
218
275
  options.basePath ?? null,
219
276
  agentNamespace,
220
277
  options.name || "default",
221
- combinedPath || null
278
+ combinedPath || null,
279
+ transport
222
280
  ]);
281
+ const destination = socketDestinationKey({
282
+ ...socketOptions,
283
+ host: socketOptions.host || defaultSocketHost()
284
+ });
285
+ const seedDestinationRef = useRef(destination);
223
286
  const visibleConnectionError = connectionErrorAddressKeyRef.current === addressKey ? connectionError : null;
224
287
  connectionErrorRef.current = visibleConnectionError;
288
+ const connectProgressRef = useRef(/* @__PURE__ */ new WeakMap());
289
+ const connectProgress = (socket) => {
290
+ if (!socket) return {
291
+ stateSeen: false,
292
+ pendingIdentity: null
293
+ };
294
+ let progress = connectProgressRef.current.get(socket);
295
+ if (!progress) {
296
+ progress = {
297
+ stateSeen: false,
298
+ pendingIdentity: null
299
+ };
300
+ connectProgressRef.current.set(socket, progress);
301
+ }
302
+ return progress;
303
+ };
304
+ const applyIdentity = (newName, newAgent, identifiedSocket) => {
305
+ const oldName = previousIdentityRef.current.name;
306
+ const oldAgent = previousIdentityRef.current.agent;
307
+ const currentAgent = mutableAgentRef.current;
308
+ if (currentAgent) {
309
+ currentAgent.name = newName;
310
+ currentAgent.agent = newAgent;
311
+ currentAgent.identified = true;
312
+ }
313
+ const identifiedDestination = identifiedSocket ? socketDestinationKey(identifiedSocket.partySocketOptions) : null;
314
+ setIdentity({
315
+ name: newName,
316
+ agent: newAgent,
317
+ identified: true,
318
+ destination: identifiedDestination
319
+ });
320
+ if (identifiedDestination !== null) readyFor(identifiedDestination).resolve();
321
+ if (oldName !== null && oldAgent !== null && (oldName !== newName || oldAgent !== newAgent)) if (options.onIdentityChange) options.onIdentityChange(oldName, newName, oldAgent, newAgent);
322
+ else {
323
+ const agentChanged = oldAgent !== newAgent;
324
+ const nameChanged = oldName !== newName;
325
+ let changeDescription = "";
326
+ if (agentChanged && nameChanged) changeDescription = `agent "${oldAgent}" → "${newAgent}", instance "${oldName}" → "${newName}"`;
327
+ else if (agentChanged) changeDescription = `agent "${oldAgent}" → "${newAgent}"`;
328
+ else changeDescription = `instance "${oldName}" → "${newName}"`;
329
+ console.warn(`[agents] Identity changed on reconnect: ${changeDescription}. This can happen with server-side routing (e.g., basePath with getAgentByName) where the instance is determined by auth/session. Provide onIdentityChange callback to handle this explicitly, or ignore if this is expected for your routing pattern.`);
330
+ }
331
+ previousIdentityRef.current = {
332
+ name: newName,
333
+ agent: newAgent
334
+ };
335
+ options.onIdentity?.(newName, newAgent);
336
+ };
225
337
  const agent = usePartySocket({
226
338
  ...socketOptions,
227
339
  enabled: socketEnabled,
@@ -240,42 +352,32 @@ function useAgent(options) {
240
352
  return options.onMessage?.(message);
241
353
  }
242
354
  if (parsedMessage.type === "cf_agent_identity") {
243
- const oldName = previousIdentityRef.current.name;
244
- const oldAgent = previousIdentityRef.current.agent;
355
+ const identifiedSocket = message.target ?? socketRef.current;
245
356
  const newName = parsedMessage.name;
246
357
  const newAgent = parsedMessage.agent;
247
- const currentAgent = mutableAgentRef.current;
248
- if (currentAgent) {
249
- currentAgent.name = newName;
250
- currentAgent.agent = newAgent;
251
- currentAgent.identified = true;
252
- }
253
- setIdentity({
254
- name: newName,
255
- agent: newAgent,
256
- identified: true
257
- });
258
- readyRef.current?.resolve();
259
- if (oldName !== null && oldAgent !== null && (oldName !== newName || oldAgent !== newAgent)) if (options.onIdentityChange) options.onIdentityChange(oldName, newName, oldAgent, newAgent);
260
- else {
261
- const agentChanged = oldAgent !== newAgent;
262
- const nameChanged = oldName !== newName;
263
- let changeDescription = "";
264
- if (agentChanged && nameChanged) changeDescription = `agent "${oldAgent}" → "${newAgent}", instance "${oldName}" → "${newName}"`;
265
- else if (agentChanged) changeDescription = `agent "${oldAgent}" → "${newAgent}"`;
266
- else changeDescription = `instance "${oldName}" → "${newName}"`;
267
- console.warn(`[agents] Identity changed on reconnect: ${changeDescription}. This can happen with server-side routing (e.g., basePath with getAgentByName) where the instance is determined by auth/session. Provide onIdentityChange callback to handle this explicitly, or ignore if this is expected for your routing pattern.`);
358
+ const progress = connectProgress(identifiedSocket);
359
+ if (parsedMessage.stateFollows === true && !progress.stateSeen) {
360
+ progress.pendingIdentity = [newName, newAgent];
361
+ return;
268
362
  }
269
- previousIdentityRef.current = {
270
- name: newName,
271
- agent: newAgent
272
- };
273
- options.onIdentity?.(newName, newAgent);
363
+ applyIdentity(newName, newAgent, identifiedSocket);
274
364
  return;
275
365
  }
276
366
  if (parsedMessage.type === "cf_agent_state") {
277
367
  setAgentState(parsedMessage.state);
278
- options.onStateUpdate?.(parsedMessage.state, "server");
368
+ if (mutableAgentRef.current) mutableAgentRef.current.state = parsedMessage.state;
369
+ const stateSocket = message.target ?? socketRef.current;
370
+ try {
371
+ options.onStateUpdate?.(parsedMessage.state, "server");
372
+ } finally {
373
+ const progress = connectProgress(stateSocket);
374
+ progress.stateSeen = true;
375
+ const pending = progress.pendingIdentity;
376
+ if (pending) {
377
+ progress.pendingIdentity = null;
378
+ applyIdentity(pending[0], pending[1], stateSocket);
379
+ }
380
+ }
279
381
  return;
280
382
  }
281
383
  if (parsedMessage.type === "cf_agent_state_error") {
@@ -319,24 +421,26 @@ function useAgent(options) {
319
421
  onClose: (event) => {
320
422
  const closedSocket = event.target ?? socketRef.current;
321
423
  const isCurrentSocket = closedSocket === socketRef.current;
322
- const terminalClose = isTerminalCloseEvent(event);
424
+ const finalClose = closeClassifier.takeFinalClose();
425
+ if (closedSocket) connectProgressRef.current.delete(closedSocket);
426
+ const reconnecting = !!closedSocket?.shouldReconnect && !finalClose;
323
427
  if (closedSocket) {
324
428
  rejectCallsSentOn(closedSocket, "Connection closed");
325
- if (isCurrentSocket && !closedSocket.shouldReconnect) rejectQueuedCalls("Connection closed");
429
+ if (isCurrentSocket && !reconnecting) rejectQueuedCalls("Connection closed");
326
430
  }
327
431
  if (isCurrentSocket) {
328
- resetReady();
432
+ if (closedSocket) resetReady(socketDestinationKey(closedSocket.partySocketOptions));
329
433
  if (mutableAgentRef.current) mutableAgentRef.current.identified = false;
330
434
  setIdentity((prev) => ({
331
435
  ...prev,
332
436
  identified: false
333
437
  }));
334
- if (closedSocket?.shouldReconnect) {
438
+ if (reconnecting) {
335
439
  if (isAsyncQuery) setAwaitingQueryRefresh(true);
336
440
  deleteCacheEntry(cacheKeyRef.current);
337
441
  setCacheInvalidatedAt(Date.now());
338
442
  }
339
- if (!closedSocket?.shouldReconnect && terminalClose) {
443
+ if (finalClose) {
340
444
  const error = new AgentConnectionError(event);
341
445
  connectionErrorAddressKeyRef.current = addressKey;
342
446
  setConnectionError(error);
@@ -347,29 +451,49 @@ function useAgent(options) {
347
451
  }
348
452
  });
349
453
  socketRef.current = agent;
454
+ const socketAddressPending = socketDestinationKey(agent.partySocketOptions) !== destination;
350
455
  const prevSocketRef = useRef(null);
351
456
  const prevAddressKeyRef = useRef(addressKey);
457
+ const prevDestinationRef = useRef(destination);
352
458
  useEffect(() => {
353
459
  const prev = prevSocketRef.current;
354
460
  prevSocketRef.current = agent;
355
461
  const prevAddress = prevAddressKeyRef.current;
356
462
  prevAddressKeyRef.current = addressKey;
463
+ const prevDestination = prevDestinationRef.current;
464
+ prevDestinationRef.current = destination;
465
+ if (prevDestination !== destination) previousIdentityRef.current = {
466
+ name: null,
467
+ agent: null
468
+ };
357
469
  if (prevAddress !== addressKey) {
358
470
  connectionErrorAddressKeyRef.current = null;
359
471
  setConnectionError(null);
360
472
  rejectQueuedCalls("Call discarded: the agent address changed before the request could be sent");
361
473
  }
474
+ for (const [key, entry] of readyRef.current) if (key !== destination && entry.resolved) readyRef.current.delete(key);
362
475
  if (prev && prev !== agent) {
363
476
  rejectCallsSentOn(prev, "Connection closed");
364
- resetReady();
477
+ resetReady(socketDestinationKey(agent.partySocketOptions));
365
478
  if (mutableAgentRef.current) mutableAgentRef.current.identified = false;
366
479
  setIdentity((current) => current.identified ? {
367
480
  ...current,
368
481
  identified: false
369
482
  } : current);
370
483
  }
371
- }, [agent, addressKey]);
484
+ }, [
485
+ agent,
486
+ addressKey,
487
+ destination
488
+ ]);
372
489
  const call = useCallback((method, args = [], options) => {
490
+ if (transportRef.current === "capnweb") {
491
+ const native = capnWebRef.current;
492
+ const socket = socketRef.current;
493
+ if (native && socket?.readyState === WebSocket.OPEN) return nativeCall(native, method, args, options, defaultCallTimeoutRef.current);
494
+ if (socket && connectionErrorRef.current && socket.readyState === socket.CLOSED) return Promise.reject(/* @__PURE__ */ new Error("Connection closed"));
495
+ return nativeQueueRef.current.enqueue(method, args, options, defaultCallTimeoutRef.current);
496
+ }
373
497
  return new Promise((resolve, reject) => {
374
498
  const socket = socketRef.current;
375
499
  if (socket && connectionErrorRef.current && socket.readyState === socket.CLOSED) {
@@ -378,9 +502,7 @@ function useAgent(options) {
378
502
  }
379
503
  const id = crypto.randomUUID();
380
504
  let timeoutId;
381
- const isLegacyFormat = options && ("onChunk" in options || "onDone" in options || "onError" in options);
382
- const streamOptions = isLegacyFormat ? options : options?.stream;
383
- const timeout = isLegacyFormat ? void 0 : options?.timeout;
505
+ const { stream: streamOptions, timeout } = splitCallOptions(options);
384
506
  const effectiveTimeout = timeout !== void 0 ? timeout : streamOptions ? void 0 : defaultCallTimeoutRef.current;
385
507
  if (effectiveTimeout) timeoutId = setTimeout(() => {
386
508
  const pending = pendingCallsRef.current.get(id);
@@ -415,32 +537,29 @@ function useAgent(options) {
415
537
  state: newState,
416
538
  type: "cf_agent_state"
417
539
  }));
540
+ if (mutableAgentRef.current) mutableAgentRef.current.state = newState;
418
541
  setAgentState(newState);
419
542
  options.onStateUpdate?.(newState, "client");
420
543
  };
421
544
  agent.call = call;
422
- agent.agent = identity.agent;
423
- agent.name = identity.name;
545
+ const identityIsCurrent = !!options.basePath || (identity.destination ?? seedDestinationRef.current) === destination;
546
+ agent.agent = identityIsCurrent ? identity.agent : camelCaseToKebabCase(leafAgent);
547
+ agent.name = identityIsCurrent ? identity.name : leafName;
424
548
  agent.path = fullPath;
425
- agent.identified = identity.identified;
426
- agent.ready = readyRef.current.promise;
549
+ agent.identified = identityIsCurrent && identity.identified;
550
+ agent.ready = readyFor(destination).promise;
427
551
  agent.state = agentState;
428
552
  agent.connectionError = visibleConnectionError;
429
553
  mutableAgentRef.current = agent;
430
554
  agent.stub = useMemo(() => createStubProxy(call), [call]);
555
+ agent[SOCKET_ADDRESS_PENDING] = socketAddressPending;
431
556
  agent.getHttpUrl = () => {
557
+ if (socketAddressPending) return "";
432
558
  return (agent._url || agent._pkurl || "").replace("ws://", "http://").replace("wss://", "https://");
433
559
  };
434
560
  if (identity.agent !== identity.agent.toLowerCase()) console.warn("Agent name: " + identity.agent + " should probably be in lowercase. Received: " + identity.agent);
435
561
  return agent;
436
562
  }
437
- function agentToolDedupeKey(message) {
438
- return [
439
- message.parentToolCallId ?? "",
440
- message.event.runId,
441
- String(message.sequence)
442
- ].join("\0");
443
- }
444
563
  function useAgentToolEvents(options) {
445
564
  const { agent } = options;
446
565
  const [state, setState] = useState(() => createAgentToolEventState());
@@ -455,7 +574,7 @@ function useAgentToolEvents(options) {
455
574
  return;
456
575
  }
457
576
  if (message.type !== "agent-tool-event") return;
458
- const key = agentToolDedupeKey(message);
577
+ const key = agentToolEventDedupeKey(message);
459
578
  if (seenRef.current.has(key)) return;
460
579
  seenRef.current.add(key);
461
580
  setState((prev) => applyAgentToolEvent(prev, message));