@veroai/chat 0.1.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.
@@ -0,0 +1,920 @@
1
+ import EventEmitter from 'eventemitter3';
2
+
3
+ // src/api/index.ts
4
+ var ChatApi = class {
5
+ constructor(config) {
6
+ this.apiUrl = config.apiUrl.replace(/\/$/, "");
7
+ this.getToken = config.getToken;
8
+ }
9
+ async getHeaders() {
10
+ const token = await this.getToken();
11
+ return {
12
+ "Content-Type": "application/json",
13
+ ...token && { Authorization: `Bearer ${token}` }
14
+ };
15
+ }
16
+ async handleResponse(response) {
17
+ if (!response.ok) {
18
+ const errorText = await response.text();
19
+ let errorMessage;
20
+ try {
21
+ const errorJson = JSON.parse(errorText);
22
+ errorMessage = errorJson.error?.message || errorJson.message || errorText;
23
+ } catch {
24
+ errorMessage = errorText;
25
+ }
26
+ throw new Error(errorMessage);
27
+ }
28
+ return response.json();
29
+ }
30
+ // ============================================================================
31
+ // Conversations
32
+ // ============================================================================
33
+ /**
34
+ * List all conversations for the current user
35
+ */
36
+ async listConversations() {
37
+ const response = await fetch(`${this.apiUrl}/v1/chat/conversations`, {
38
+ headers: await this.getHeaders()
39
+ });
40
+ const data = await this.handleResponse(response);
41
+ return data.conversations.map(transformConversation);
42
+ }
43
+ /**
44
+ * Get a specific conversation
45
+ */
46
+ async getConversation(conversationId) {
47
+ const response = await fetch(`${this.apiUrl}/v1/chat/conversations/${conversationId}`, {
48
+ headers: await this.getHeaders()
49
+ });
50
+ const data = await this.handleResponse(response);
51
+ return transformConversation(data.conversation);
52
+ }
53
+ /**
54
+ * Create a new conversation
55
+ */
56
+ async createConversation(params) {
57
+ const response = await fetch(`${this.apiUrl}/v1/chat/conversations`, {
58
+ method: "POST",
59
+ headers: await this.getHeaders(),
60
+ body: JSON.stringify({
61
+ type: params.type || "direct",
62
+ name: params.name,
63
+ participant_ids: params.participantIds,
64
+ agent_config_id: params.agentConfigId,
65
+ metadata: params.metadata
66
+ })
67
+ });
68
+ const data = await this.handleResponse(response);
69
+ return transformConversation(data.conversation);
70
+ }
71
+ /**
72
+ * Mark conversation as read
73
+ */
74
+ async markConversationRead(conversationId) {
75
+ const response = await fetch(`${this.apiUrl}/v1/chat/conversations/${conversationId}/read`, {
76
+ method: "POST",
77
+ headers: await this.getHeaders()
78
+ });
79
+ await this.handleResponse(response);
80
+ }
81
+ /**
82
+ * Leave a conversation
83
+ */
84
+ async leaveConversation(conversationId) {
85
+ const response = await fetch(
86
+ `${this.apiUrl}/v1/chat/conversations/${conversationId}/participants/me`,
87
+ {
88
+ method: "DELETE",
89
+ headers: await this.getHeaders()
90
+ }
91
+ );
92
+ await this.handleResponse(response);
93
+ }
94
+ // ============================================================================
95
+ // Messages
96
+ // ============================================================================
97
+ /**
98
+ * Get messages for a conversation
99
+ */
100
+ async getMessages(conversationId, params) {
101
+ const searchParams = new URLSearchParams();
102
+ if (params?.limit) searchParams.set("limit", String(params.limit));
103
+ if (params?.offset) searchParams.set("offset", String(params.offset));
104
+ if (params?.before) searchParams.set("before", params.before);
105
+ const url = `${this.apiUrl}/v1/chat/conversations/${conversationId}/messages?${searchParams}`;
106
+ const response = await fetch(url, {
107
+ headers: await this.getHeaders()
108
+ });
109
+ const data = await this.handleResponse(response);
110
+ return {
111
+ messages: data.messages.map(transformMessage),
112
+ total: data.total,
113
+ hasMore: data.has_more,
114
+ limit: data.limit,
115
+ offset: data.offset
116
+ };
117
+ }
118
+ /**
119
+ * Send a message to a conversation
120
+ */
121
+ async sendMessage(conversationId, params) {
122
+ const response = await fetch(`${this.apiUrl}/v1/chat/conversations/${conversationId}/messages`, {
123
+ method: "POST",
124
+ headers: await this.getHeaders(),
125
+ body: JSON.stringify({
126
+ content: params.content,
127
+ message_type: params.messageType || "text",
128
+ metadata: params.metadata
129
+ })
130
+ });
131
+ const data = await this.handleResponse(response);
132
+ return transformMessage(data.message);
133
+ }
134
+ // ============================================================================
135
+ // Users
136
+ // ============================================================================
137
+ /**
138
+ * List all users (contacts)
139
+ */
140
+ async listUsers(options) {
141
+ const searchParams = new URLSearchParams();
142
+ if (options?.includeVirtual) searchParams.set("include_virtual", "true");
143
+ const url = `${this.apiUrl}/v1/chat/users?${searchParams}`;
144
+ const response = await fetch(url, {
145
+ headers: await this.getHeaders()
146
+ });
147
+ const data = await this.handleResponse(response);
148
+ return data.users.map(transformUser);
149
+ }
150
+ /**
151
+ * Get online users
152
+ */
153
+ async getOnlineUsers() {
154
+ const response = await fetch(`${this.apiUrl}/v1/chat/users/online`, {
155
+ headers: await this.getHeaders()
156
+ });
157
+ const data = await this.handleResponse(response);
158
+ return data.users.map(transformUser);
159
+ }
160
+ /**
161
+ * Get current user profile
162
+ */
163
+ async getCurrentUser() {
164
+ const response = await fetch(`${this.apiUrl}/v1/chat/users/me`, {
165
+ headers: await this.getHeaders()
166
+ });
167
+ const data = await this.handleResponse(response);
168
+ return transformUser(data.user);
169
+ }
170
+ /**
171
+ * Get a specific user
172
+ */
173
+ async getUser(userId) {
174
+ const response = await fetch(`${this.apiUrl}/v1/chat/users/${userId}`, {
175
+ headers: await this.getHeaders()
176
+ });
177
+ const data = await this.handleResponse(response);
178
+ return transformUser(data.user);
179
+ }
180
+ /**
181
+ * Update current user's presence status
182
+ */
183
+ async updateStatus(status, statusMessage) {
184
+ const response = await fetch(`${this.apiUrl}/v1/chat/users/me/status`, {
185
+ method: "PUT",
186
+ headers: await this.getHeaders(),
187
+ body: JSON.stringify({
188
+ status,
189
+ status_message: statusMessage
190
+ })
191
+ });
192
+ await this.handleResponse(response);
193
+ }
194
+ // ============================================================================
195
+ // Agents
196
+ // ============================================================================
197
+ /**
198
+ * Add agent to conversation
199
+ */
200
+ async addAgentToConversation(conversationId, agentConfigId, addAsParticipant = true) {
201
+ const response = await fetch(`${this.apiUrl}/v1/chat/conversations/${conversationId}/agent`, {
202
+ method: "POST",
203
+ headers: await this.getHeaders(),
204
+ body: JSON.stringify({
205
+ agent_config_id: agentConfigId,
206
+ add_as_participant: addAsParticipant
207
+ })
208
+ });
209
+ await this.handleResponse(response);
210
+ }
211
+ /**
212
+ * Remove agent from conversation
213
+ */
214
+ async removeAgentFromConversation(conversationId) {
215
+ const response = await fetch(`${this.apiUrl}/v1/chat/conversations/${conversationId}/agent`, {
216
+ method: "DELETE",
217
+ headers: await this.getHeaders()
218
+ });
219
+ await this.handleResponse(response);
220
+ }
221
+ /**
222
+ * Toggle agent enabled/disabled
223
+ */
224
+ async toggleAgent(conversationId, enabled) {
225
+ const response = await fetch(`${this.apiUrl}/v1/chat/conversations/${conversationId}/agent`, {
226
+ method: "PATCH",
227
+ headers: await this.getHeaders(),
228
+ body: JSON.stringify({ enabled })
229
+ });
230
+ await this.handleResponse(response);
231
+ }
232
+ /**
233
+ * List available agents
234
+ */
235
+ async listAgents() {
236
+ const response = await fetch(`${this.apiUrl}/v1/agent-configurations`, {
237
+ headers: await this.getHeaders()
238
+ });
239
+ const data = await this.handleResponse(response);
240
+ return data;
241
+ }
242
+ // ============================================================================
243
+ // Voice Rooms (LiveKit)
244
+ // ============================================================================
245
+ /**
246
+ * Create a new voice/video room
247
+ */
248
+ async createRoom(params) {
249
+ const response = await fetch(`${this.apiUrl}/v1/voice/rooms`, {
250
+ method: "POST",
251
+ headers: await this.getHeaders(),
252
+ body: JSON.stringify({
253
+ name: params.name,
254
+ empty_timeout: params.emptyTimeout,
255
+ max_participants: params.maxParticipants
256
+ })
257
+ });
258
+ const data = await this.handleResponse(response);
259
+ return transformRoomInfo(data.room);
260
+ }
261
+ /**
262
+ * Join an existing room and get access token
263
+ */
264
+ async joinRoom(params) {
265
+ const response = await fetch(
266
+ `${this.apiUrl}/v1/voice/rooms/${encodeURIComponent(params.roomName)}/join`,
267
+ {
268
+ method: "POST",
269
+ headers: await this.getHeaders(),
270
+ body: JSON.stringify({
271
+ participant_name: params.participantName,
272
+ can_publish: params.canPublish ?? true,
273
+ can_subscribe: params.canSubscribe ?? true
274
+ })
275
+ }
276
+ );
277
+ const data = await this.handleResponse(response);
278
+ return {
279
+ name: data.room_name,
280
+ wsUrl: data.ws_url,
281
+ token: data.token
282
+ };
283
+ }
284
+ /**
285
+ * Get room token for an existing room
286
+ * Convenience method for getting a token without creating
287
+ */
288
+ async getRoomToken(roomName, participantName) {
289
+ return this.joinRoom({ roomName, participantName });
290
+ }
291
+ };
292
+ function transformUser(raw) {
293
+ return {
294
+ id: raw.id,
295
+ email: raw.email,
296
+ firstName: raw.first_name,
297
+ lastName: raw.last_name,
298
+ isVirtual: raw.is_virtual,
299
+ agentConfigId: raw.agent_config_id,
300
+ status: raw.status,
301
+ statusMessage: raw.status_message,
302
+ lastSeen: raw.last_seen,
303
+ createdAt: raw.created_at
304
+ };
305
+ }
306
+ function transformParticipant(raw) {
307
+ return {
308
+ userId: raw.user_id,
309
+ role: raw.role,
310
+ isActive: raw.is_active,
311
+ joinedAt: raw.joined_at,
312
+ lastSeen: raw.last_seen,
313
+ user: raw.user ? transformUser(raw.user) : void 0
314
+ };
315
+ }
316
+ function transformConversation(raw) {
317
+ return {
318
+ id: raw.id,
319
+ name: raw.name,
320
+ type: raw.type,
321
+ isActive: raw.is_active,
322
+ lastMessageAt: raw.last_message_at,
323
+ agentEnabled: raw.agent_enabled,
324
+ agentConfigId: raw.agent_config_id,
325
+ participants: raw.participants?.map(transformParticipant),
326
+ unreadCount: raw.unread_count,
327
+ metadata: raw.metadata,
328
+ createdAt: raw.created_at,
329
+ updatedAt: raw.updated_at
330
+ };
331
+ }
332
+ function transformMessage(raw) {
333
+ return {
334
+ id: raw.id,
335
+ conversationId: raw.conversation_id,
336
+ content: raw.content,
337
+ messageType: raw.message_type,
338
+ senderId: raw.sender_id,
339
+ sender: raw.sender ? transformUser(raw.sender) : void 0,
340
+ readBy: raw.read_by?.map((r) => ({
341
+ userId: r.user_id,
342
+ readAt: r.read_at
343
+ })),
344
+ metadata: raw.metadata,
345
+ createdAt: raw.created_at,
346
+ editedAt: raw.edited_at
347
+ };
348
+ }
349
+ function transformRoomInfo(raw) {
350
+ return {
351
+ name: raw.name,
352
+ wsUrl: raw.ws_url,
353
+ token: raw.token
354
+ };
355
+ }
356
+ var WebSocketManager = class extends EventEmitter {
357
+ constructor(config) {
358
+ super();
359
+ this.ws = null;
360
+ this.state = "disconnected";
361
+ this.reconnectAttempts = 0;
362
+ this.reconnectTimer = null;
363
+ this.heartbeatTimer = null;
364
+ this.pendingMessages = [];
365
+ this.config = {
366
+ url: config.url,
367
+ getToken: config.getToken,
368
+ autoReconnect: config.autoReconnect ?? true,
369
+ reconnectInterval: config.reconnectInterval ?? 3e3,
370
+ maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
371
+ heartbeatInterval: config.heartbeatInterval ?? 3e4
372
+ };
373
+ }
374
+ /**
375
+ * Get current connection state
376
+ */
377
+ getState() {
378
+ return this.state;
379
+ }
380
+ /**
381
+ * Check if connected
382
+ */
383
+ isConnected() {
384
+ return this.state === "connected" && this.ws?.readyState === WebSocket.OPEN;
385
+ }
386
+ /**
387
+ * Connect to the WebSocket server
388
+ */
389
+ async connect() {
390
+ if (this.state === "connecting" || this.state === "connected") {
391
+ return;
392
+ }
393
+ this.state = "connecting";
394
+ const token = await this.config.getToken();
395
+ if (!token) {
396
+ this.state = "disconnected";
397
+ throw new Error("No authentication token available");
398
+ }
399
+ return new Promise((resolve, reject) => {
400
+ try {
401
+ const url = new URL(this.config.url);
402
+ url.searchParams.set("token", token);
403
+ this.ws = new WebSocket(url.toString());
404
+ this.ws.onopen = () => {
405
+ this.state = "connected";
406
+ this.reconnectAttempts = 0;
407
+ this.startHeartbeat();
408
+ this.flushPendingMessages();
409
+ this.emit("connected");
410
+ resolve();
411
+ };
412
+ this.ws.onclose = (event) => {
413
+ this.handleClose(event.reason);
414
+ };
415
+ this.ws.onerror = (event) => {
416
+ const error = new Error("WebSocket error");
417
+ this.emit("error", error);
418
+ if (this.state === "connecting") {
419
+ reject(error);
420
+ }
421
+ };
422
+ this.ws.onmessage = (event) => {
423
+ this.handleMessage(event.data);
424
+ };
425
+ } catch (error) {
426
+ this.state = "disconnected";
427
+ reject(error);
428
+ }
429
+ });
430
+ }
431
+ /**
432
+ * Disconnect from the WebSocket server
433
+ */
434
+ disconnect() {
435
+ this.config.autoReconnect = false;
436
+ this.clearTimers();
437
+ if (this.ws) {
438
+ this.ws.close(1e3, "Client disconnect");
439
+ this.ws = null;
440
+ }
441
+ this.state = "disconnected";
442
+ }
443
+ /**
444
+ * Send a message through the WebSocket
445
+ */
446
+ send(type, payload) {
447
+ const message = JSON.stringify({ type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
448
+ if (this.isConnected()) {
449
+ this.ws.send(message);
450
+ } else {
451
+ this.pendingMessages.push(message);
452
+ }
453
+ }
454
+ /**
455
+ * Send typing indicator
456
+ */
457
+ sendTypingStart(conversationId) {
458
+ this.send("typing:start", { conversationId });
459
+ }
460
+ /**
461
+ * Stop typing indicator
462
+ */
463
+ sendTypingStop(conversationId) {
464
+ this.send("typing:stop", { conversationId });
465
+ }
466
+ /**
467
+ * Subscribe to a conversation for real-time updates
468
+ */
469
+ subscribeToConversation(conversationId) {
470
+ this.send("subscribe", { conversationId });
471
+ }
472
+ /**
473
+ * Unsubscribe from a conversation
474
+ */
475
+ unsubscribeFromConversation(conversationId) {
476
+ this.send("unsubscribe", { conversationId });
477
+ }
478
+ /**
479
+ * Update presence status
480
+ */
481
+ updatePresence(status, statusMessage) {
482
+ this.send("presence:update", { status, statusMessage });
483
+ }
484
+ /**
485
+ * Send call notification (ring, accept, reject, end)
486
+ * Note: Actual WebRTC signaling is handled by LiveKit
487
+ */
488
+ sendCallNotification(conversationId, action, callType, roomName) {
489
+ this.send("call", { conversationId, action, callType, roomName });
490
+ }
491
+ handleClose(reason) {
492
+ this.stopHeartbeat();
493
+ const wasConnected = this.state === "connected";
494
+ this.state = "disconnected";
495
+ this.ws = null;
496
+ if (wasConnected) {
497
+ this.emit("disconnected", reason);
498
+ }
499
+ if (this.config.autoReconnect && this.reconnectAttempts < this.config.maxReconnectAttempts) {
500
+ this.scheduleReconnect();
501
+ }
502
+ }
503
+ scheduleReconnect() {
504
+ if (this.reconnectTimer) {
505
+ return;
506
+ }
507
+ this.state = "reconnecting";
508
+ this.reconnectAttempts++;
509
+ const delay = Math.min(
510
+ this.config.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1),
511
+ 3e4
512
+ // Max 30 seconds
513
+ );
514
+ this.reconnectTimer = setTimeout(async () => {
515
+ this.reconnectTimer = null;
516
+ try {
517
+ await this.connect();
518
+ } catch (error) {
519
+ }
520
+ }, delay);
521
+ }
522
+ handleMessage(data) {
523
+ try {
524
+ const message = JSON.parse(data);
525
+ switch (message.type) {
526
+ case "message:new":
527
+ this.emit("message:new", message.payload);
528
+ break;
529
+ case "message:updated":
530
+ this.emit("message:updated", message.payload);
531
+ break;
532
+ case "message:deleted": {
533
+ const { messageId, conversationId } = message.payload;
534
+ this.emit("message:deleted", messageId, conversationId);
535
+ break;
536
+ }
537
+ case "conversation:created":
538
+ this.emit("conversation:created", message.payload);
539
+ break;
540
+ case "conversation:updated":
541
+ this.emit("conversation:updated", message.payload);
542
+ break;
543
+ case "participant:joined": {
544
+ const { conversationId, participant } = message.payload;
545
+ this.emit("participant:joined", conversationId, participant);
546
+ break;
547
+ }
548
+ case "participant:left": {
549
+ const payload = message.payload;
550
+ this.emit("participant:left", payload.conversationId, payload.userId);
551
+ break;
552
+ }
553
+ case "presence:updated":
554
+ this.emit("presence:updated", message.payload);
555
+ break;
556
+ case "typing:start":
557
+ this.emit("typing:start", message.payload);
558
+ break;
559
+ case "typing:stop":
560
+ this.emit("typing:stop", message.payload);
561
+ break;
562
+ case "read:receipt":
563
+ this.emit("read:receipt", message.payload);
564
+ break;
565
+ case "call:ring":
566
+ this.emit("call:ring", message.payload);
567
+ break;
568
+ case "call:accept":
569
+ this.emit("call:accept", message.payload);
570
+ break;
571
+ case "call:reject":
572
+ this.emit("call:reject", message.payload);
573
+ break;
574
+ case "call:end":
575
+ this.emit("call:end", message.payload);
576
+ break;
577
+ }
578
+ } catch (error) {
579
+ console.error("[ChatWS] Failed to parse message:", error);
580
+ }
581
+ }
582
+ flushPendingMessages() {
583
+ while (this.pendingMessages.length > 0 && this.isConnected()) {
584
+ const message = this.pendingMessages.shift();
585
+ if (message) {
586
+ this.ws.send(message);
587
+ }
588
+ }
589
+ }
590
+ startHeartbeat() {
591
+ this.stopHeartbeat();
592
+ this.heartbeatTimer = setInterval(() => {
593
+ if (this.isConnected()) {
594
+ this.send("ping", {});
595
+ }
596
+ }, this.config.heartbeatInterval);
597
+ }
598
+ stopHeartbeat() {
599
+ if (this.heartbeatTimer) {
600
+ clearInterval(this.heartbeatTimer);
601
+ this.heartbeatTimer = null;
602
+ }
603
+ }
604
+ clearTimers() {
605
+ this.stopHeartbeat();
606
+ if (this.reconnectTimer) {
607
+ clearTimeout(this.reconnectTimer);
608
+ this.reconnectTimer = null;
609
+ }
610
+ }
611
+ };
612
+ var ChatClient = class extends EventEmitter {
613
+ constructor(config) {
614
+ super();
615
+ this.ws = null;
616
+ this.config = config;
617
+ this.tokenGetter = config.getToken || (() => config.token || null);
618
+ this.api = new ChatApi({
619
+ apiUrl: config.apiUrl,
620
+ getToken: this.tokenGetter
621
+ });
622
+ if (config.wsUrl) {
623
+ this.ws = new WebSocketManager({
624
+ url: config.wsUrl,
625
+ getToken: this.tokenGetter,
626
+ autoReconnect: config.autoReconnect ?? true,
627
+ reconnectInterval: config.reconnectInterval,
628
+ maxReconnectAttempts: config.maxReconnectAttempts
629
+ });
630
+ this.forwardWebSocketEvents();
631
+ }
632
+ if (config.autoConnect && config.wsUrl) {
633
+ this.connect().catch((error) => {
634
+ console.error("[ChatClient] Auto-connect failed:", error);
635
+ });
636
+ }
637
+ }
638
+ // ============================================================================
639
+ // Connection Management
640
+ // ============================================================================
641
+ /**
642
+ * Connect to WebSocket for real-time updates
643
+ */
644
+ async connect() {
645
+ if (!this.ws) {
646
+ throw new Error("WebSocket URL not configured");
647
+ }
648
+ await this.ws.connect();
649
+ }
650
+ /**
651
+ * Disconnect from WebSocket
652
+ */
653
+ disconnect() {
654
+ this.ws?.disconnect();
655
+ }
656
+ /**
657
+ * Check if WebSocket is connected
658
+ */
659
+ isConnected() {
660
+ return this.ws?.isConnected() ?? false;
661
+ }
662
+ /**
663
+ * Update authentication token
664
+ */
665
+ setToken(token) {
666
+ this.config.token = token;
667
+ }
668
+ // ============================================================================
669
+ // Conversations
670
+ // ============================================================================
671
+ /**
672
+ * List all conversations for the current user
673
+ */
674
+ async listConversations() {
675
+ return this.api.listConversations();
676
+ }
677
+ /**
678
+ * Get a specific conversation
679
+ */
680
+ async getConversation(conversationId) {
681
+ return this.api.getConversation(conversationId);
682
+ }
683
+ /**
684
+ * Create a new conversation
685
+ */
686
+ async createConversation(params) {
687
+ const conversation = await this.api.createConversation(params);
688
+ if (this.ws?.isConnected()) {
689
+ this.ws.subscribeToConversation(conversation.id);
690
+ }
691
+ return conversation;
692
+ }
693
+ /**
694
+ * Mark conversation as read
695
+ */
696
+ async markConversationRead(conversationId) {
697
+ return this.api.markConversationRead(conversationId);
698
+ }
699
+ /**
700
+ * Leave a conversation
701
+ */
702
+ async leaveConversation(conversationId) {
703
+ if (this.ws?.isConnected()) {
704
+ this.ws.unsubscribeFromConversation(conversationId);
705
+ }
706
+ return this.api.leaveConversation(conversationId);
707
+ }
708
+ /**
709
+ * Subscribe to real-time updates for a conversation
710
+ */
711
+ subscribeToConversation(conversationId) {
712
+ this.ws?.subscribeToConversation(conversationId);
713
+ }
714
+ /**
715
+ * Unsubscribe from real-time updates for a conversation
716
+ */
717
+ unsubscribeFromConversation(conversationId) {
718
+ this.ws?.unsubscribeFromConversation(conversationId);
719
+ }
720
+ // ============================================================================
721
+ // Messages
722
+ // ============================================================================
723
+ /**
724
+ * Get messages for a conversation
725
+ */
726
+ async getMessages(conversationId, params) {
727
+ return this.api.getMessages(conversationId, params);
728
+ }
729
+ /**
730
+ * Send a message to a conversation
731
+ */
732
+ async sendMessage(conversationId, params) {
733
+ return this.api.sendMessage(conversationId, params);
734
+ }
735
+ /**
736
+ * Send a text message (convenience method)
737
+ */
738
+ async send(conversationId, content) {
739
+ return this.sendMessage(conversationId, { content });
740
+ }
741
+ // ============================================================================
742
+ // Typing Indicators
743
+ // ============================================================================
744
+ /**
745
+ * Send typing start indicator
746
+ */
747
+ sendTypingStart(conversationId) {
748
+ this.ws?.sendTypingStart(conversationId);
749
+ }
750
+ /**
751
+ * Send typing stop indicator
752
+ */
753
+ sendTypingStop(conversationId) {
754
+ this.ws?.sendTypingStop(conversationId);
755
+ }
756
+ // ============================================================================
757
+ // Users & Presence
758
+ // ============================================================================
759
+ /**
760
+ * List all users (contacts)
761
+ */
762
+ async listUsers(options) {
763
+ return this.api.listUsers(options);
764
+ }
765
+ /**
766
+ * Get online users
767
+ */
768
+ async getOnlineUsers() {
769
+ return this.api.getOnlineUsers();
770
+ }
771
+ /**
772
+ * Get current user profile
773
+ */
774
+ async getCurrentUser() {
775
+ return this.api.getCurrentUser();
776
+ }
777
+ /**
778
+ * Get a specific user
779
+ */
780
+ async getUser(userId) {
781
+ return this.api.getUser(userId);
782
+ }
783
+ /**
784
+ * Update current user's presence status
785
+ */
786
+ async updateStatus(status, statusMessage) {
787
+ await this.api.updateStatus(status, statusMessage);
788
+ this.ws?.updatePresence(status, statusMessage);
789
+ }
790
+ // ============================================================================
791
+ // Agents
792
+ // ============================================================================
793
+ /**
794
+ * Add agent to conversation
795
+ */
796
+ async addAgentToConversation(conversationId, agentConfigId, addAsParticipant = true) {
797
+ return this.api.addAgentToConversation(conversationId, agentConfigId, addAsParticipant);
798
+ }
799
+ /**
800
+ * Remove agent from conversation
801
+ */
802
+ async removeAgentFromConversation(conversationId) {
803
+ return this.api.removeAgentFromConversation(conversationId);
804
+ }
805
+ /**
806
+ * Toggle agent enabled/disabled
807
+ */
808
+ async toggleAgent(conversationId, enabled) {
809
+ return this.api.toggleAgent(conversationId, enabled);
810
+ }
811
+ /**
812
+ * List available agents
813
+ */
814
+ async listAgents() {
815
+ return this.api.listAgents();
816
+ }
817
+ // ============================================================================
818
+ // Voice/Video Calls (LiveKit)
819
+ // ============================================================================
820
+ /**
821
+ * Create a new voice/video room
822
+ *
823
+ * @example
824
+ * ```typescript
825
+ * const room = await chat.createRoom({ name: `call-${conversationId}` });
826
+ * // Use room.wsUrl and room.token with LiveKit client
827
+ * ```
828
+ */
829
+ async createRoom(params) {
830
+ return this.api.createRoom(params);
831
+ }
832
+ /**
833
+ * Join an existing room and get access token
834
+ *
835
+ * @example
836
+ * ```typescript
837
+ * const room = await chat.joinRoom({
838
+ * roomName: `call-${conversationId}`,
839
+ * participantName: 'John Doe',
840
+ * });
841
+ * // Connect to room using LiveKit client SDK with room.wsUrl and room.token
842
+ * ```
843
+ */
844
+ async joinRoom(params) {
845
+ return this.api.joinRoom(params);
846
+ }
847
+ /**
848
+ * Initiate a call in a conversation
849
+ * This notifies other participants that you're calling
850
+ *
851
+ * @example
852
+ * ```typescript
853
+ * // Start a video call
854
+ * const room = await chat.startCall(conversationId, 'video');
855
+ * // room contains { name, wsUrl, token } for LiveKit
856
+ * ```
857
+ */
858
+ async startCall(conversationId, callType = "audio") {
859
+ const roomName = `call-${conversationId}-${Date.now()}`;
860
+ const room = await this.api.createRoom({ name: roomName });
861
+ this.ws?.sendCallNotification(conversationId, "ring", callType, roomName);
862
+ return room;
863
+ }
864
+ /**
865
+ * Accept an incoming call
866
+ *
867
+ * @example
868
+ * ```typescript
869
+ * chat.on('call:ring', async ({ conversationId, roomName, callType }) => {
870
+ * const room = await chat.acceptCall(conversationId, roomName);
871
+ * // Connect to room using LiveKit client SDK
872
+ * });
873
+ * ```
874
+ */
875
+ async acceptCall(conversationId, roomName, participantName) {
876
+ const room = await this.api.joinRoom({ roomName, participantName });
877
+ this.ws?.sendCallNotification(conversationId, "accept", void 0, roomName);
878
+ return room;
879
+ }
880
+ /**
881
+ * Reject an incoming call
882
+ */
883
+ rejectCall(conversationId) {
884
+ this.ws?.sendCallNotification(conversationId, "reject");
885
+ }
886
+ /**
887
+ * End an ongoing call
888
+ */
889
+ endCall(conversationId) {
890
+ this.ws?.sendCallNotification(conversationId, "end");
891
+ }
892
+ // ============================================================================
893
+ // Internal
894
+ // ============================================================================
895
+ forwardWebSocketEvents() {
896
+ if (!this.ws) return;
897
+ this.ws.on("connected", () => this.emit("connected"));
898
+ this.ws.on("disconnected", (reason) => this.emit("disconnected", reason));
899
+ this.ws.on("error", (error) => this.emit("error", error));
900
+ this.ws.on("message:new", (event) => this.emit("message:new", event));
901
+ this.ws.on("message:updated", (msg) => this.emit("message:updated", msg));
902
+ this.ws.on("message:deleted", (msgId, convId) => this.emit("message:deleted", msgId, convId));
903
+ this.ws.on("conversation:created", (conv) => this.emit("conversation:created", conv));
904
+ this.ws.on("conversation:updated", (conv) => this.emit("conversation:updated", conv));
905
+ this.ws.on("participant:joined", (convId, p) => this.emit("participant:joined", convId, p));
906
+ this.ws.on("participant:left", (convId, userId) => this.emit("participant:left", convId, userId));
907
+ this.ws.on("presence:updated", (event) => this.emit("presence:updated", event));
908
+ this.ws.on("typing:start", (event) => this.emit("typing:start", event));
909
+ this.ws.on("typing:stop", (event) => this.emit("typing:stop", event));
910
+ this.ws.on("read:receipt", (event) => this.emit("read:receipt", event));
911
+ this.ws.on("call:ring", (event) => this.emit("call:ring", event));
912
+ this.ws.on("call:accept", (event) => this.emit("call:accept", event));
913
+ this.ws.on("call:reject", (event) => this.emit("call:reject", event));
914
+ this.ws.on("call:end", (event) => this.emit("call:end", event));
915
+ }
916
+ };
917
+
918
+ export { ChatApi, ChatClient, WebSocketManager };
919
+ //# sourceMappingURL=chunk-JI6KXOLF.js.map
920
+ //# sourceMappingURL=chunk-JI6KXOLF.js.map