dooers-agents-client 0.9.2 → 0.11.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
@@ -24,6 +24,9 @@ function apiMessagesUrlToWebSocketUrl(url) {
24
24
  throw new Error("API Messages URL must start with http://, https://, ws://, or wss://");
25
25
  }
26
26
 
27
+ // src/version.ts
28
+ var PACKAGE_VERSION = "0.10.0";
29
+
27
30
  // src/types.ts
28
31
  function isSettingsFieldGroup(item) {
29
32
  return "fields" in item && Array.isArray(item.fields);
@@ -34,6 +37,8 @@ function toUser(w) {
34
37
  userId: w.user_id,
35
38
  userName: w.user_name,
36
39
  userEmail: w.user_email,
40
+ userMobileNumber: w.user_mobile_number,
41
+ userWhatsappNumber: w.user_whatsapp_number,
37
42
  identityIds: w.identity_ids,
38
43
  systemRole: w.system_role,
39
44
  organizationRole: w.organization_role,
@@ -65,7 +70,8 @@ function toDisplayContentPart(w) {
65
70
  url: w.url,
66
71
  mimeType: w.mime_type,
67
72
  duration: w.duration,
68
- filename: w.filename
73
+ filename: w.filename,
74
+ ...w.ref_id ? { refId: w.ref_id } : {}
69
75
  };
70
76
  case "image":
71
77
  return {
@@ -75,7 +81,8 @@ function toDisplayContentPart(w) {
75
81
  width: w.width,
76
82
  height: w.height,
77
83
  alt: w.alt,
78
- filename: w.filename
84
+ filename: w.filename,
85
+ ...w.ref_id ? { refId: w.ref_id } : {}
79
86
  };
80
87
  case "document":
81
88
  return {
@@ -83,7 +90,8 @@ function toDisplayContentPart(w) {
83
90
  url: w.url,
84
91
  filename: w.filename,
85
92
  mimeType: w.mime_type,
86
- sizeBytes: w.size_bytes
93
+ sizeBytes: w.size_bytes,
94
+ ...w.ref_id ? { refId: w.ref_id } : {}
87
95
  };
88
96
  }
89
97
  }
@@ -278,6 +286,7 @@ function toWireContentPart(p) {
278
286
  var MAX_RECONNECT_ATTEMPTS = 5;
279
287
  var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3];
280
288
  var SEND_MESSAGE_TIMEOUT = 3e4;
289
+ var PING_INTERVAL_MS = 3e4;
281
290
  function blobPartFilename(blob, override) {
282
291
  const o = (override ?? "").trim();
283
292
  if (o) return o;
@@ -410,7 +419,8 @@ var AgentClient = class {
410
419
  config = {
411
420
  organizationId: "",
412
421
  workspaceId: "",
413
- userId: ""
422
+ userId: "",
423
+ channel: "dooers-platform"
414
424
  };
415
425
  connectFrameId = "";
416
426
  isIntentionallyClosed = false;
@@ -429,6 +439,9 @@ var AgentClient = class {
429
439
  isLoadingMore = false;
430
440
  // Event pagination cursors per thread
431
441
  eventPaginationCursors = /* @__PURE__ */ new Map();
442
+ /** Active settings subscription — restored after reconnect. */
443
+ settingsSubscription = null;
444
+ pingTimer = null;
432
445
  constructor(callbacks) {
433
446
  this.callbacks = callbacks;
434
447
  }
@@ -449,6 +462,16 @@ var AgentClient = class {
449
462
  setUploadRunId(runId) {
450
463
  this.uploadRunId = (runId ?? "").trim();
451
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
+ }
452
475
  async upload(file, options) {
453
476
  if (!this.uploadUrl) {
454
477
  throw new Error("uploadUrl not configured");
@@ -532,6 +555,7 @@ var AgentClient = class {
532
555
  }
533
556
  disconnect() {
534
557
  this.isIntentionallyClosed = true;
558
+ this.stopHeartbeat();
535
559
  if (this.reconnectTimer) {
536
560
  clearTimeout(this.reconnectTimer);
537
561
  this.reconnectTimer = null;
@@ -597,13 +621,17 @@ var AgentClient = class {
597
621
  }
598
622
  // --- Settings ---
599
623
  subscribeSettings(options) {
624
+ const audience = options?.audience ?? "user";
625
+ const agentOwnerUserId = options?.agentOwnerUserId ?? null;
626
+ this.settingsSubscription = { audience, agentOwnerUserId };
600
627
  this.send("settings.subscribe", {
601
628
  agent_id: this.agentId,
602
- audience: options?.audience ?? "user",
603
- agent_owner_user_id: options?.agentOwnerUserId ?? null
629
+ audience,
630
+ agent_owner_user_id: agentOwnerUserId
604
631
  });
605
632
  }
606
633
  unsubscribeSettings() {
634
+ this.settingsSubscription = null;
607
635
  this.send("settings.unsubscribe", { agent_id: this.agentId });
608
636
  }
609
637
  patchSetting(fieldId, value) {
@@ -697,9 +725,7 @@ var AgentClient = class {
697
725
  content
698
726
  }
699
727
  };
700
- if (params.metadata) {
701
- payload.metadata = params.metadata;
702
- }
728
+ payload.metadata = this.withChannelMetadata(params.metadata);
703
729
  this.sendRaw({
704
730
  id: clientEventId,
705
731
  type: "event.create",
@@ -727,7 +753,8 @@ var AgentClient = class {
727
753
  values: params.values
728
754
  }
729
755
  }
730
- }
756
+ },
757
+ metadata: this.withChannelMetadata(params.metadata)
731
758
  });
732
759
  return promise;
733
760
  }
@@ -747,6 +774,7 @@ var AgentClient = class {
747
774
  });
748
775
  }
749
776
  createConnection() {
777
+ this.stopHeartbeat();
750
778
  if (this.ws) {
751
779
  this.abortPendingMessages("Connection lost; reconnecting");
752
780
  this.ws.onopen = null;
@@ -787,13 +815,15 @@ var AgentClient = class {
787
815
  user_id: this.config.userId ?? "",
788
816
  user_name: this.config.userName ?? null,
789
817
  user_email: this.config.userEmail ?? null,
818
+ user_mobile_number: this.config.userMobileNumber ?? null,
819
+ user_whatsapp_number: this.config.userWhatsappNumber ?? null,
790
820
  identity_ids: this.config.identityIds ?? [],
791
821
  system_role: this.config.systemRole ?? "user",
792
822
  organization_role: this.config.organizationRole ?? "member",
793
823
  workspace_role: this.config.workspaceRole ?? "member"
794
824
  },
795
825
  auth_token: this.config.authToken,
796
- client: { name: "dooers-agents-client", version: "0.1.0" }
826
+ client: { name: "dooers-agents-client", version: PACKAGE_VERSION }
797
827
  }
798
828
  });
799
829
  }
@@ -812,6 +842,8 @@ var AgentClient = class {
812
842
  after_event_id: afterEventId
813
843
  });
814
844
  }
845
+ this.resubscribeSettings();
846
+ this.startHeartbeat();
815
847
  } else {
816
848
  this.callbacks.setConnectionStatus("error", frame.payload.error?.message);
817
849
  }
@@ -953,7 +985,28 @@ var AgentClient = class {
953
985
  break;
954
986
  }
955
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
+ }
956
1008
  scheduleReconnect() {
1009
+ this.stopHeartbeat();
957
1010
  if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
958
1011
  this.abortPendingMessages("Connection lost after maximum retries");
959
1012
  this.callbacks.setReconnectFailed();
@@ -971,6 +1024,17 @@ var AgentClient = class {
971
1024
  send(type, payload) {
972
1025
  this.sendRaw({ id: crypto.randomUUID(), type, payload });
973
1026
  }
1027
+ withChannelMetadata(metadata) {
1028
+ const channel = (this.config.channel || "dooers-platform").trim() || "dooers-platform";
1029
+ const base = metadata ? { ...metadata } : {};
1030
+ if (!("channel" in base)) {
1031
+ base.channel = channel;
1032
+ }
1033
+ if (this.config.channelMeta && !("channel_meta" in base)) {
1034
+ base.channel_meta = this.config.channelMeta;
1035
+ }
1036
+ return Object.keys(base).length > 0 ? base : void 0;
1037
+ }
974
1038
  sendRaw(frame) {
975
1039
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
976
1040
  this.ws.send(JSON.stringify(frame));
@@ -1412,11 +1476,15 @@ function AgentProvider({
1412
1476
  userId,
1413
1477
  userName,
1414
1478
  userEmail,
1479
+ userMobileNumber,
1480
+ userWhatsappNumber,
1415
1481
  identityIds,
1416
1482
  systemRole,
1417
1483
  organizationRole,
1418
1484
  workspaceRole,
1419
1485
  authToken,
1486
+ channel,
1487
+ channelMeta,
1420
1488
  uploadUrl,
1421
1489
  onError,
1422
1490
  children
@@ -1437,35 +1505,63 @@ function AgentProvider({
1437
1505
  clientRef.current?.setUploadAgentId(agentId);
1438
1506
  }, [agentId]);
1439
1507
  const identityIdsKey = identityIds?.join(",") ?? "";
1440
- react.useEffect(() => {
1441
- if (!url || !agentId) return;
1442
- 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 {
1443
1519
  organizationId,
1444
1520
  workspaceId,
1445
1521
  userId,
1446
1522
  userName,
1447
1523
  userEmail,
1524
+ userMobileNumber,
1525
+ userWhatsappNumber,
1448
1526
  identityIds: identityIdsKey ? identityIdsKey.split(",") : void 0,
1449
1527
  systemRole,
1450
1528
  organizationRole,
1451
1529
  workspaceRole,
1452
- authToken
1453
- });
1454
- return () => clientRef.current?.disconnect();
1530
+ authToken,
1531
+ channel,
1532
+ channelMeta: parsedChannelMeta
1533
+ };
1455
1534
  }, [
1456
- url,
1457
- agentId,
1458
1535
  organizationId,
1459
1536
  workspaceId,
1460
1537
  userId,
1461
1538
  userName,
1462
1539
  userEmail,
1540
+ userMobileNumber,
1541
+ userWhatsappNumber,
1463
1542
  identityIdsKey,
1464
1543
  systemRole,
1465
1544
  organizationRole,
1466
1545
  workspaceRole,
1467
- authToken
1546
+ authToken,
1547
+ channel,
1548
+ channelMetaKey,
1549
+ channelMeta
1468
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]);
1469
1565
  const contextValue = react.useMemo(
1470
1566
  () => ({ store: storeRef.current, client: clientRef.current }),
1471
1567
  []
@@ -1919,6 +2015,7 @@ function useUpload() {
1919
2015
 
1920
2016
  exports.AgentProvider = AgentProvider;
1921
2017
  exports.AgentServerClient = AgentServerClient;
2018
+ exports.PACKAGE_VERSION = PACKAGE_VERSION;
1922
2019
  exports.apiMessagesUrlToWebSocketUrl = apiMessagesUrlToWebSocketUrl;
1923
2020
  exports.isSettingsFieldGroup = isSettingsFieldGroup;
1924
2021
  exports.toFormElement = toFormElement;