dooers-agents-client 0.9.3 → 0.12.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/README.md CHANGED
@@ -5,7 +5,7 @@ React agents client SDK for agents server SDK.
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- npm install dooers-agents-client
8
+ npm install dooers-agents
9
9
  ```
10
10
 
11
11
  Peer dependency: `react >= 18`
@@ -15,7 +15,7 @@ Peer dependency: `react >= 18`
15
15
  Wrap your component tree in `AgentProvider`, then use hooks anywhere inside.
16
16
 
17
17
  ```tsx
18
- import { AgentProvider, useConnection, useThreadDetails, useMessage } from "dooers-agents-client"
18
+ import { AgentProvider, useConnection, useThreadDetails, useMessage } from "dooers-agents"
19
19
 
20
20
  function App() {
21
21
  return (
@@ -230,9 +230,9 @@ import type {
230
230
  AnalyticsEvent,
231
231
  FeedbackType, // "like" | "dislike"
232
232
  FeedbackTarget, // "event" | "run" | "thread"
233
- } from "dooers-agents-client"
233
+ } from "dooers-agents"
234
234
 
235
- import { isSettingsFieldGroup } from "dooers-agents-client" // type guard
235
+ import { isSettingsFieldGroup } from "dooers-agents" // type guard
236
236
  ```
237
237
 
238
238
  ## Architecture
package/dist/main.cjs CHANGED
@@ -25,7 +25,7 @@ function apiMessagesUrlToWebSocketUrl(url) {
25
25
  }
26
26
 
27
27
  // src/version.ts
28
- var PACKAGE_VERSION = "0.9.3";
28
+ var PACKAGE_VERSION = "0.10.0";
29
29
 
30
30
  // src/types.ts
31
31
  function isSettingsFieldGroup(item) {
@@ -286,6 +286,7 @@ function toWireContentPart(p) {
286
286
  var MAX_RECONNECT_ATTEMPTS = 5;
287
287
  var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3];
288
288
  var SEND_MESSAGE_TIMEOUT = 3e4;
289
+ var PING_INTERVAL_MS = 3e4;
289
290
  function blobPartFilename(blob, override) {
290
291
  const o = (override ?? "").trim();
291
292
  if (o) return o;
@@ -438,6 +439,9 @@ var AgentClient = class {
438
439
  isLoadingMore = false;
439
440
  // Event pagination cursors per thread
440
441
  eventPaginationCursors = /* @__PURE__ */ new Map();
442
+ /** Active settings subscription — restored after reconnect. */
443
+ settingsSubscription = null;
444
+ pingTimer = null;
441
445
  constructor(callbacks) {
442
446
  this.callbacks = callbacks;
443
447
  }
@@ -458,6 +462,16 @@ var AgentClient = class {
458
462
  setUploadRunId(runId) {
459
463
  this.uploadRunId = (runId ?? "").trim();
460
464
  }
465
+ /**
466
+ * Merge connection metadata without tearing down the WebSocket.
467
+ * Use for handshake token refresh and identity/role updates on an open session.
468
+ */
469
+ updateConnectionConfig(config) {
470
+ this.config = { ...this.config, ...config };
471
+ }
472
+ isConnected() {
473
+ return this.ws?.readyState === WebSocket.OPEN;
474
+ }
461
475
  async upload(file, options) {
462
476
  if (!this.uploadUrl) {
463
477
  throw new Error("uploadUrl not configured");
@@ -541,6 +555,7 @@ var AgentClient = class {
541
555
  }
542
556
  disconnect() {
543
557
  this.isIntentionallyClosed = true;
558
+ this.stopHeartbeat();
544
559
  if (this.reconnectTimer) {
545
560
  clearTimeout(this.reconnectTimer);
546
561
  this.reconnectTimer = null;
@@ -606,13 +621,17 @@ var AgentClient = class {
606
621
  }
607
622
  // --- Settings ---
608
623
  subscribeSettings(options) {
624
+ const audience = options?.audience ?? "user";
625
+ const agentOwnerUserId = options?.agentOwnerUserId ?? null;
626
+ this.settingsSubscription = { audience, agentOwnerUserId };
609
627
  this.send("settings.subscribe", {
610
628
  agent_id: this.agentId,
611
- audience: options?.audience ?? "user",
612
- agent_owner_user_id: options?.agentOwnerUserId ?? null
629
+ audience,
630
+ agent_owner_user_id: agentOwnerUserId
613
631
  });
614
632
  }
615
633
  unsubscribeSettings() {
634
+ this.settingsSubscription = null;
616
635
  this.send("settings.unsubscribe", { agent_id: this.agentId });
617
636
  }
618
637
  patchSetting(fieldId, value) {
@@ -755,6 +774,7 @@ var AgentClient = class {
755
774
  });
756
775
  }
757
776
  createConnection() {
777
+ this.stopHeartbeat();
758
778
  if (this.ws) {
759
779
  this.abortPendingMessages("Connection lost; reconnecting");
760
780
  this.ws.onopen = null;
@@ -822,6 +842,8 @@ var AgentClient = class {
822
842
  after_event_id: afterEventId
823
843
  });
824
844
  }
845
+ this.resubscribeSettings();
846
+ this.startHeartbeat();
825
847
  } else {
826
848
  this.callbacks.setConnectionStatus("error", frame.payload.error?.message);
827
849
  }
@@ -963,7 +985,28 @@ var AgentClient = class {
963
985
  break;
964
986
  }
965
987
  }
988
+ resubscribeSettings() {
989
+ if (!this.settingsSubscription) return;
990
+ this.send("settings.subscribe", {
991
+ agent_id: this.agentId,
992
+ audience: this.settingsSubscription.audience,
993
+ agent_owner_user_id: this.settingsSubscription.agentOwnerUserId
994
+ });
995
+ }
996
+ startHeartbeat() {
997
+ this.stopHeartbeat();
998
+ this.pingTimer = setInterval(() => {
999
+ this.send("ping", {});
1000
+ }, PING_INTERVAL_MS);
1001
+ }
1002
+ stopHeartbeat() {
1003
+ if (this.pingTimer) {
1004
+ clearInterval(this.pingTimer);
1005
+ this.pingTimer = null;
1006
+ }
1007
+ }
966
1008
  scheduleReconnect() {
1009
+ this.stopHeartbeat();
967
1010
  if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
968
1011
  this.abortPendingMessages("Connection lost after maximum retries");
969
1012
  this.callbacks.setReconnectFailed();
@@ -1462,9 +1505,17 @@ function AgentProvider({
1462
1505
  clientRef.current?.setUploadAgentId(agentId);
1463
1506
  }, [agentId]);
1464
1507
  const identityIdsKey = identityIds?.join(",") ?? "";
1465
- react.useEffect(() => {
1466
- if (!url || !agentId) return;
1467
- clientRef.current?.connect(url, agentId, {
1508
+ const channelMetaKey = channelMeta ? JSON.stringify(channelMeta) : "";
1509
+ const connectionConfig = react.useMemo(() => {
1510
+ let parsedChannelMeta;
1511
+ if (channelMetaKey) {
1512
+ try {
1513
+ parsedChannelMeta = JSON.parse(channelMetaKey);
1514
+ } catch {
1515
+ parsedChannelMeta = channelMeta;
1516
+ }
1517
+ }
1518
+ return {
1468
1519
  organizationId,
1469
1520
  workspaceId,
1470
1521
  userId,
@@ -1478,12 +1529,9 @@ function AgentProvider({
1478
1529
  workspaceRole,
1479
1530
  authToken,
1480
1531
  channel,
1481
- channelMeta
1482
- });
1483
- return () => clientRef.current?.disconnect();
1532
+ channelMeta: parsedChannelMeta
1533
+ };
1484
1534
  }, [
1485
- url,
1486
- agentId,
1487
1535
  organizationId,
1488
1536
  workspaceId,
1489
1537
  userId,
@@ -1497,8 +1545,23 @@ function AgentProvider({
1497
1545
  workspaceRole,
1498
1546
  authToken,
1499
1547
  channel,
1548
+ channelMetaKey,
1500
1549
  channelMeta
1501
1550
  ]);
1551
+ const connectionConfigRef = react.useRef(connectionConfig);
1552
+ connectionConfigRef.current = connectionConfig;
1553
+ react.useEffect(() => {
1554
+ if (!url || !agentId) {
1555
+ clientRef.current?.disconnect();
1556
+ return;
1557
+ }
1558
+ clientRef.current?.connect(url, agentId, connectionConfigRef.current);
1559
+ return () => clientRef.current?.disconnect();
1560
+ }, [url, agentId]);
1561
+ react.useEffect(() => {
1562
+ if (!url || !agentId) return;
1563
+ clientRef.current?.updateConnectionConfig(connectionConfig);
1564
+ }, [url, agentId, connectionConfig]);
1502
1565
  const contextValue = react.useMemo(
1503
1566
  () => ({ store: storeRef.current, client: clientRef.current }),
1504
1567
  []