dooers-agents-client 0.9.0 → 0.9.3
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/main.cjs +295 -50
- package/dist/main.cjs.map +1 -1
- package/dist/main.d.cts +202 -3
- package/dist/main.d.ts +202 -3
- package/dist/main.js +295 -52
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
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.9.3";
|
|
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
|
}
|
|
@@ -246,6 +254,8 @@ function toWireContentPart(p) {
|
|
|
246
254
|
ref_id: p.refId
|
|
247
255
|
};
|
|
248
256
|
if (p.duration != null) w.duration = p.duration;
|
|
257
|
+
if (p.filename?.trim()) w.filename = p.filename.trim();
|
|
258
|
+
if (p.mimeType?.trim()) w.mime_type = p.mimeType.trim();
|
|
249
259
|
if (p.url) w.url = p.url;
|
|
250
260
|
return w;
|
|
251
261
|
}
|
|
@@ -254,6 +264,8 @@ function toWireContentPart(p) {
|
|
|
254
264
|
type: "image",
|
|
255
265
|
ref_id: p.refId
|
|
256
266
|
};
|
|
267
|
+
if (p.filename?.trim()) w.filename = p.filename.trim();
|
|
268
|
+
if (p.mimeType?.trim()) w.mime_type = p.mimeType.trim();
|
|
257
269
|
if (p.url) w.url = p.url;
|
|
258
270
|
return w;
|
|
259
271
|
}
|
|
@@ -262,6 +274,8 @@ function toWireContentPart(p) {
|
|
|
262
274
|
type: "document",
|
|
263
275
|
ref_id: p.refId
|
|
264
276
|
};
|
|
277
|
+
if (p.filename?.trim()) w.filename = p.filename.trim();
|
|
278
|
+
if (p.mimeType?.trim()) w.mime_type = p.mimeType.trim();
|
|
265
279
|
if (p.url) w.url = p.url;
|
|
266
280
|
return w;
|
|
267
281
|
}
|
|
@@ -272,6 +286,35 @@ function toWireContentPart(p) {
|
|
|
272
286
|
var MAX_RECONNECT_ATTEMPTS = 5;
|
|
273
287
|
var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3];
|
|
274
288
|
var SEND_MESSAGE_TIMEOUT = 3e4;
|
|
289
|
+
function blobPartFilename(blob, override) {
|
|
290
|
+
const o = (override ?? "").trim();
|
|
291
|
+
if (o) return o;
|
|
292
|
+
const t = (blob.type || "").toLowerCase();
|
|
293
|
+
if (t.includes("webm")) return "recording.webm";
|
|
294
|
+
if (t.includes("ogg")) return "recording.ogg";
|
|
295
|
+
if (t.includes("mpeg") || t.includes("mp3")) return "recording.mp3";
|
|
296
|
+
if (t.includes("mp4") || t.includes("m4a") || t.includes("aac")) return "recording.m4a";
|
|
297
|
+
if (t.startsWith("audio/")) return `recording.${t.split("/")[1]?.split("+")[0] || "bin"}`;
|
|
298
|
+
return "recording.bin";
|
|
299
|
+
}
|
|
300
|
+
async function readHttpErrorMessage(response) {
|
|
301
|
+
const raw = await response.text().catch(() => "") || "";
|
|
302
|
+
const trimmed = raw.trim();
|
|
303
|
+
if (!trimmed) {
|
|
304
|
+
return `Upload failed: ${response.status} ${response.statusText}`.trim();
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
const j = JSON.parse(trimmed);
|
|
308
|
+
const d = j.detail;
|
|
309
|
+
if (typeof d === "string") return d;
|
|
310
|
+
if (Array.isArray(d) && d.length > 0) {
|
|
311
|
+
const first = d[0];
|
|
312
|
+
if (first && typeof first.msg === "string") return first.msg;
|
|
313
|
+
}
|
|
314
|
+
} catch {
|
|
315
|
+
}
|
|
316
|
+
return trimmed.length > 300 ? `${trimmed.slice(0, 300)}\u2026` : trimmed;
|
|
317
|
+
}
|
|
275
318
|
var AgentServerClient = class {
|
|
276
319
|
wsUrl;
|
|
277
320
|
constructor(apiMessagesUrl) {
|
|
@@ -365,11 +408,18 @@ var AgentClient = class {
|
|
|
365
408
|
url = "";
|
|
366
409
|
httpBaseUrl = "";
|
|
367
410
|
agentId = "";
|
|
411
|
+
/** Mirrors ``AgentProvider`` ``agentId`` so HTTP uploads work before WS ``connect`` completes. */
|
|
412
|
+
uploadAgentId = "";
|
|
413
|
+
/** Current chat thread id for ``POST /uploads`` (aligns GCS path with ``hydrate_thread_events``). */
|
|
414
|
+
uploadThreadId = "";
|
|
415
|
+
/** Optional run id for ``POST /uploads`` (metadata only). */
|
|
416
|
+
uploadRunId = "";
|
|
368
417
|
uploadUrl;
|
|
369
418
|
config = {
|
|
370
419
|
organizationId: "",
|
|
371
420
|
workspaceId: "",
|
|
372
|
-
userId: ""
|
|
421
|
+
userId: "",
|
|
422
|
+
channel: "dooers-platform"
|
|
373
423
|
};
|
|
374
424
|
connectFrameId = "";
|
|
375
425
|
isIntentionallyClosed = false;
|
|
@@ -379,7 +429,7 @@ var AgentClient = class {
|
|
|
379
429
|
subscriptionRefs = /* @__PURE__ */ new Map();
|
|
380
430
|
// Track optimistic events for reconciliation
|
|
381
431
|
pendingOptimistic = /* @__PURE__ */ new Map();
|
|
382
|
-
// Pending message promises —
|
|
432
|
+
// Pending message promises — keyed by outbound frame id (same as client_event_id for sends).
|
|
383
433
|
pendingMessages = /* @__PURE__ */ new Map();
|
|
384
434
|
// Track last event ID per thread for gap recovery on reconnect
|
|
385
435
|
lastEventIds = /* @__PURE__ */ new Map();
|
|
@@ -397,14 +447,40 @@ var AgentClient = class {
|
|
|
397
447
|
setUploadUrl(url) {
|
|
398
448
|
this.uploadUrl = url;
|
|
399
449
|
}
|
|
400
|
-
|
|
450
|
+
/** Sync from ``AgentProvider`` ``agentId``; used for ``POST /uploads`` ``agent_id`` (non-WS). */
|
|
451
|
+
setUploadAgentId(agentId) {
|
|
452
|
+
this.uploadAgentId = (agentId ?? "").trim();
|
|
453
|
+
}
|
|
454
|
+
/** Sync active thread id for chat uploads (must match persisted thread for signed URL keys). */
|
|
455
|
+
setUploadThreadId(threadId) {
|
|
456
|
+
this.uploadThreadId = (threadId ?? "").trim();
|
|
457
|
+
}
|
|
458
|
+
setUploadRunId(runId) {
|
|
459
|
+
this.uploadRunId = (runId ?? "").trim();
|
|
460
|
+
}
|
|
461
|
+
async upload(file, options) {
|
|
401
462
|
if (!this.uploadUrl) {
|
|
402
463
|
throw new Error("uploadUrl not configured");
|
|
403
464
|
}
|
|
465
|
+
const aid = (this.uploadAgentId || this.agentId || "").trim();
|
|
466
|
+
if (!aid) {
|
|
467
|
+
throw new Error("agentId is required for upload (set AgentProvider agentId)");
|
|
468
|
+
}
|
|
404
469
|
const formData = new FormData();
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
formData.append("
|
|
470
|
+
if (file instanceof File) {
|
|
471
|
+
const partName = options?.filename?.trim() || file.name.trim() || "upload";
|
|
472
|
+
formData.append("file", file, partName);
|
|
473
|
+
} else {
|
|
474
|
+
formData.append("file", file, blobPartFilename(file, options?.filename));
|
|
475
|
+
}
|
|
476
|
+
formData.append("agent_id", aid);
|
|
477
|
+
const tid = this.uploadThreadId.trim();
|
|
478
|
+
if (tid) {
|
|
479
|
+
formData.append("thread_id", tid);
|
|
480
|
+
}
|
|
481
|
+
const rid = this.uploadRunId.trim();
|
|
482
|
+
if (rid) {
|
|
483
|
+
formData.append("run_id", rid);
|
|
408
484
|
}
|
|
409
485
|
const headers = {};
|
|
410
486
|
if (this.config.authToken) {
|
|
@@ -416,7 +492,8 @@ var AgentClient = class {
|
|
|
416
492
|
headers
|
|
417
493
|
});
|
|
418
494
|
if (!response.ok) {
|
|
419
|
-
|
|
495
|
+
const detail = await readHttpErrorMessage(response);
|
|
496
|
+
throw new Error(detail);
|
|
420
497
|
}
|
|
421
498
|
const result = await response.json();
|
|
422
499
|
return {
|
|
@@ -430,6 +507,7 @@ var AgentClient = class {
|
|
|
430
507
|
connect(url, agentId, config) {
|
|
431
508
|
this.url = apiMessagesUrlToWebSocketUrl(url);
|
|
432
509
|
this.agentId = agentId;
|
|
510
|
+
this.uploadAgentId = agentId.trim();
|
|
433
511
|
this.config = config ?? { organizationId: "", workspaceId: "", userId: "" };
|
|
434
512
|
this.isIntentionallyClosed = false;
|
|
435
513
|
try {
|
|
@@ -445,16 +523,29 @@ var AgentClient = class {
|
|
|
445
523
|
this.callbacks.setConnectionStatus("connecting");
|
|
446
524
|
this.createConnection();
|
|
447
525
|
}
|
|
526
|
+
/** Clear the last in-chat send error banner (see ``connection.sendError``). */
|
|
527
|
+
clearSendError() {
|
|
528
|
+
this.callbacks.setSendError(null);
|
|
529
|
+
}
|
|
530
|
+
abortPendingMessages(reason) {
|
|
531
|
+
for (const [clientEventId, pending] of this.pendingMessages) {
|
|
532
|
+
clearTimeout(pending.timer);
|
|
533
|
+
const opt = this.pendingOptimistic.get(clientEventId);
|
|
534
|
+
if (opt?.threadId) {
|
|
535
|
+
this.callbacks.removeOptimistic(opt.threadId, clientEventId);
|
|
536
|
+
}
|
|
537
|
+
this.pendingOptimistic.delete(clientEventId);
|
|
538
|
+
pending.reject(new Error(reason));
|
|
539
|
+
}
|
|
540
|
+
this.pendingMessages.clear();
|
|
541
|
+
}
|
|
448
542
|
disconnect() {
|
|
449
543
|
this.isIntentionallyClosed = true;
|
|
450
544
|
if (this.reconnectTimer) {
|
|
451
545
|
clearTimeout(this.reconnectTimer);
|
|
452
546
|
this.reconnectTimer = null;
|
|
453
547
|
}
|
|
454
|
-
|
|
455
|
-
clearTimeout(pending.timer);
|
|
456
|
-
}
|
|
457
|
-
this.pendingMessages.clear();
|
|
548
|
+
this.abortPendingMessages("Disconnected");
|
|
458
549
|
if (this.ws) {
|
|
459
550
|
this.ws.onopen = null;
|
|
460
551
|
this.ws.onmessage = null;
|
|
@@ -546,6 +637,7 @@ var AgentClient = class {
|
|
|
546
637
|
}
|
|
547
638
|
// --- Messaging ---
|
|
548
639
|
sendMessage(params) {
|
|
640
|
+
this.callbacks.setSendError(null);
|
|
549
641
|
const clientEventId = crypto.randomUUID();
|
|
550
642
|
const content = params.content ? params.content.map(toWireContentPart) : [{ type: "text", text: params.text ?? "" }];
|
|
551
643
|
const displayContent = params.content ? params.content.map((p) => {
|
|
@@ -553,11 +645,27 @@ var AgentClient = class {
|
|
|
553
645
|
case "text":
|
|
554
646
|
return { type: "text", text: p.text };
|
|
555
647
|
case "audio":
|
|
556
|
-
return {
|
|
648
|
+
return {
|
|
649
|
+
type: "audio",
|
|
650
|
+
duration: p.duration,
|
|
651
|
+
...p.url ? { url: p.url } : {},
|
|
652
|
+
...p.mimeType ? { mimeType: p.mimeType } : {},
|
|
653
|
+
...p.filename ? { filename: p.filename } : {}
|
|
654
|
+
};
|
|
557
655
|
case "image":
|
|
558
|
-
return {
|
|
656
|
+
return {
|
|
657
|
+
type: "image",
|
|
658
|
+
...p.url ? { url: p.url } : {},
|
|
659
|
+
...p.mimeType ? { mimeType: p.mimeType } : {},
|
|
660
|
+
...p.filename ? { filename: p.filename } : {}
|
|
661
|
+
};
|
|
559
662
|
case "document":
|
|
560
|
-
return {
|
|
663
|
+
return {
|
|
664
|
+
type: "document",
|
|
665
|
+
...p.url ? { url: p.url } : {},
|
|
666
|
+
...p.mimeType ? { mimeType: p.mimeType } : {},
|
|
667
|
+
...p.filename ? { filename: p.filename } : {}
|
|
668
|
+
};
|
|
561
669
|
default:
|
|
562
670
|
return { type: "text", text: "" };
|
|
563
671
|
}
|
|
@@ -598,28 +706,36 @@ var AgentClient = class {
|
|
|
598
706
|
content
|
|
599
707
|
}
|
|
600
708
|
};
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
709
|
+
payload.metadata = this.withChannelMetadata(params.metadata);
|
|
710
|
+
this.sendRaw({
|
|
711
|
+
id: clientEventId,
|
|
712
|
+
type: "event.create",
|
|
713
|
+
payload
|
|
714
|
+
});
|
|
605
715
|
return promise;
|
|
606
716
|
}
|
|
607
717
|
sendFormResponse(params) {
|
|
718
|
+
this.callbacks.setSendError(null);
|
|
608
719
|
const clientEventId = crypto.randomUUID();
|
|
609
720
|
const promise = this.createPendingPromise(clientEventId);
|
|
610
|
-
this.
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
721
|
+
this.sendRaw({
|
|
722
|
+
id: clientEventId,
|
|
723
|
+
type: "event.create",
|
|
724
|
+
payload: {
|
|
725
|
+
thread_id: params.threadId,
|
|
726
|
+
client_event_id: clientEventId,
|
|
727
|
+
event: {
|
|
728
|
+
type: "form.response",
|
|
729
|
+
actor: "user",
|
|
730
|
+
content: [],
|
|
731
|
+
data: {
|
|
732
|
+
form_event_id: params.formEventId,
|
|
733
|
+
cancelled: params.cancelled,
|
|
734
|
+
values: params.values
|
|
735
|
+
}
|
|
621
736
|
}
|
|
622
|
-
}
|
|
737
|
+
},
|
|
738
|
+
metadata: this.withChannelMetadata(params.metadata)
|
|
623
739
|
});
|
|
624
740
|
return promise;
|
|
625
741
|
}
|
|
@@ -627,14 +743,20 @@ var AgentClient = class {
|
|
|
627
743
|
createPendingPromise(clientEventId) {
|
|
628
744
|
return new Promise((resolve, reject) => {
|
|
629
745
|
const timer = setTimeout(() => {
|
|
746
|
+
const opt = this.pendingOptimistic.get(clientEventId);
|
|
747
|
+
if (opt?.threadId) {
|
|
748
|
+
this.callbacks.removeOptimistic(opt.threadId, clientEventId);
|
|
749
|
+
}
|
|
750
|
+
this.pendingOptimistic.delete(clientEventId);
|
|
630
751
|
this.pendingMessages.delete(clientEventId);
|
|
631
752
|
reject(new Error("Request timed out waiting for server response"));
|
|
632
753
|
}, SEND_MESSAGE_TIMEOUT);
|
|
633
|
-
this.pendingMessages.set(clientEventId, { resolve, timer });
|
|
754
|
+
this.pendingMessages.set(clientEventId, { resolve, reject, timer });
|
|
634
755
|
});
|
|
635
756
|
}
|
|
636
757
|
createConnection() {
|
|
637
758
|
if (this.ws) {
|
|
759
|
+
this.abortPendingMessages("Connection lost; reconnecting");
|
|
638
760
|
this.ws.onopen = null;
|
|
639
761
|
this.ws.onmessage = null;
|
|
640
762
|
this.ws.onclose = null;
|
|
@@ -673,13 +795,15 @@ var AgentClient = class {
|
|
|
673
795
|
user_id: this.config.userId ?? "",
|
|
674
796
|
user_name: this.config.userName ?? null,
|
|
675
797
|
user_email: this.config.userEmail ?? null,
|
|
798
|
+
user_mobile_number: this.config.userMobileNumber ?? null,
|
|
799
|
+
user_whatsapp_number: this.config.userWhatsappNumber ?? null,
|
|
676
800
|
identity_ids: this.config.identityIds ?? [],
|
|
677
801
|
system_role: this.config.systemRole ?? "user",
|
|
678
802
|
organization_role: this.config.organizationRole ?? "member",
|
|
679
803
|
workspace_role: this.config.workspaceRole ?? "member"
|
|
680
804
|
},
|
|
681
805
|
auth_token: this.config.authToken,
|
|
682
|
-
client: { name: "dooers-agents-client", version:
|
|
806
|
+
client: { name: "dooers-agents-client", version: PACKAGE_VERSION }
|
|
683
807
|
}
|
|
684
808
|
});
|
|
685
809
|
}
|
|
@@ -702,9 +826,26 @@ var AgentClient = class {
|
|
|
702
826
|
this.callbacks.setConnectionStatus("error", frame.payload.error?.message);
|
|
703
827
|
}
|
|
704
828
|
} else if (!frame.payload.ok && frame.payload.error) {
|
|
829
|
+
const ackId = frame.payload.ack_id;
|
|
830
|
+
const err = frame.payload.error;
|
|
831
|
+
if (ackId) {
|
|
832
|
+
const pending = this.pendingMessages.get(ackId);
|
|
833
|
+
if (pending) {
|
|
834
|
+
clearTimeout(pending.timer);
|
|
835
|
+
this.pendingMessages.delete(ackId);
|
|
836
|
+
const msg = err.message || "Request rejected";
|
|
837
|
+
const opt = this.pendingOptimistic.get(ackId);
|
|
838
|
+
if (opt?.threadId) {
|
|
839
|
+
this.callbacks.removeOptimistic(opt.threadId, ackId);
|
|
840
|
+
}
|
|
841
|
+
this.pendingOptimistic.delete(ackId);
|
|
842
|
+
pending.reject(new Error(msg));
|
|
843
|
+
this.callbacks.setSendError(msg);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
705
846
|
this.onError?.({
|
|
706
|
-
code:
|
|
707
|
-
message:
|
|
847
|
+
code: err.code,
|
|
848
|
+
message: err.message,
|
|
708
849
|
frameType: "ack"
|
|
709
850
|
});
|
|
710
851
|
}
|
|
@@ -741,6 +882,7 @@ var AgentClient = class {
|
|
|
741
882
|
case "event.append": {
|
|
742
883
|
const events = this.resolveEventUrls(frame.payload.events.map(toThreadEvent));
|
|
743
884
|
const resolvedClientEventIds = [];
|
|
885
|
+
const pendingIdsToResolve = [];
|
|
744
886
|
for (const event of events) {
|
|
745
887
|
if (event.clientEventId && this.pendingOptimistic.has(event.clientEventId)) {
|
|
746
888
|
resolvedClientEventIds.push(event.clientEventId);
|
|
@@ -750,8 +892,7 @@ var AgentClient = class {
|
|
|
750
892
|
const pendingMessage = this.pendingMessages.get(event.clientEventId);
|
|
751
893
|
if (pendingMessage) {
|
|
752
894
|
clearTimeout(pendingMessage.timer);
|
|
753
|
-
|
|
754
|
-
this.pendingMessages.delete(event.clientEventId);
|
|
895
|
+
pendingIdsToResolve.push(event.clientEventId);
|
|
755
896
|
}
|
|
756
897
|
}
|
|
757
898
|
}
|
|
@@ -764,6 +905,14 @@ var AgentClient = class {
|
|
|
764
905
|
} else {
|
|
765
906
|
this.callbacks.onEventAppend(frame.payload.thread_id, events);
|
|
766
907
|
}
|
|
908
|
+
const tid = frame.payload.thread_id;
|
|
909
|
+
for (const id of pendingIdsToResolve) {
|
|
910
|
+
const pending = this.pendingMessages.get(id);
|
|
911
|
+
if (pending) {
|
|
912
|
+
pending.resolve({ threadId: tid });
|
|
913
|
+
this.pendingMessages.delete(id);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
767
916
|
break;
|
|
768
917
|
}
|
|
769
918
|
case "event.list.result": {
|
|
@@ -816,6 +965,7 @@ var AgentClient = class {
|
|
|
816
965
|
}
|
|
817
966
|
scheduleReconnect() {
|
|
818
967
|
if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
968
|
+
this.abortPendingMessages("Connection lost after maximum retries");
|
|
819
969
|
this.callbacks.setReconnectFailed();
|
|
820
970
|
this.callbacks.setConnectionStatus("error", "Connection lost after maximum retries");
|
|
821
971
|
return;
|
|
@@ -831,6 +981,17 @@ var AgentClient = class {
|
|
|
831
981
|
send(type, payload) {
|
|
832
982
|
this.sendRaw({ id: crypto.randomUUID(), type, payload });
|
|
833
983
|
}
|
|
984
|
+
withChannelMetadata(metadata) {
|
|
985
|
+
const channel = (this.config.channel || "dooers-platform").trim() || "dooers-platform";
|
|
986
|
+
const base = metadata ? { ...metadata } : {};
|
|
987
|
+
if (!("channel" in base)) {
|
|
988
|
+
base.channel = channel;
|
|
989
|
+
}
|
|
990
|
+
if (this.config.channelMeta && !("channel_meta" in base)) {
|
|
991
|
+
base.channel_meta = this.config.channelMeta;
|
|
992
|
+
}
|
|
993
|
+
return Object.keys(base).length > 0 ? base : void 0;
|
|
994
|
+
}
|
|
834
995
|
sendRaw(frame) {
|
|
835
996
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
|
|
836
997
|
this.ws.send(JSON.stringify(frame));
|
|
@@ -850,6 +1011,43 @@ var AgentClient = class {
|
|
|
850
1011
|
});
|
|
851
1012
|
}
|
|
852
1013
|
};
|
|
1014
|
+
function mergeDisplayContentParts(prev, next) {
|
|
1015
|
+
if (!next?.length) return prev;
|
|
1016
|
+
if (!prev?.length) return next;
|
|
1017
|
+
const out = [];
|
|
1018
|
+
const len = Math.max(prev.length, next.length);
|
|
1019
|
+
for (let i = 0; i < len; i++) {
|
|
1020
|
+
const p = prev[i];
|
|
1021
|
+
const n = next[i];
|
|
1022
|
+
if (!n) {
|
|
1023
|
+
if (p) out.push(p);
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
if (!p) {
|
|
1027
|
+
out.push(n);
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
if (p.type !== n.type) {
|
|
1031
|
+
out.push(n);
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
if (n.type === "image" || n.type === "audio" || n.type === "document") {
|
|
1035
|
+
const url = n.url ?? ("url" in p ? p.url : void 0);
|
|
1036
|
+
out.push({ ...p, ...n, ...url ? { url } : {} });
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
out.push(n);
|
|
1040
|
+
}
|
|
1041
|
+
return out;
|
|
1042
|
+
}
|
|
1043
|
+
function mergeThreadEventById(prev, next) {
|
|
1044
|
+
const content = mergeDisplayContentParts(prev.content, next.content);
|
|
1045
|
+
return {
|
|
1046
|
+
...prev,
|
|
1047
|
+
...next,
|
|
1048
|
+
...content !== void 0 ? { content } : {}
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
853
1051
|
function extractFormStates(events, existing) {
|
|
854
1052
|
let result = existing;
|
|
855
1053
|
for (const e of events) {
|
|
@@ -872,6 +1070,7 @@ function createAgentStore() {
|
|
|
872
1070
|
connection: {
|
|
873
1071
|
status: "idle",
|
|
874
1072
|
error: null,
|
|
1073
|
+
sendError: null,
|
|
875
1074
|
reconnectAttempts: 0,
|
|
876
1075
|
reconnectFailed: false
|
|
877
1076
|
},
|
|
@@ -900,7 +1099,15 @@ function createAgentStore() {
|
|
|
900
1099
|
loadingThreads: /* @__PURE__ */ new Set(),
|
|
901
1100
|
actions: {
|
|
902
1101
|
setConnectionStatus: (status, error) => set((s) => ({
|
|
903
|
-
connection: {
|
|
1102
|
+
connection: {
|
|
1103
|
+
...s.connection,
|
|
1104
|
+
status,
|
|
1105
|
+
error: error ?? null,
|
|
1106
|
+
...status === "connected" ? { sendError: null } : {}
|
|
1107
|
+
}
|
|
1108
|
+
})),
|
|
1109
|
+
setSendError: (message) => set((s) => ({
|
|
1110
|
+
connection: { ...s.connection, sendError: message }
|
|
904
1111
|
})),
|
|
905
1112
|
setReconnectFailed: () => set((s) => ({
|
|
906
1113
|
connection: { ...s.connection, reconnectFailed: true }
|
|
@@ -988,8 +1195,13 @@ function createAgentStore() {
|
|
|
988
1195
|
mergedEvents = snapshotEvents;
|
|
989
1196
|
} else {
|
|
990
1197
|
const existingIds = new Set(existing.map((e) => e.id));
|
|
1198
|
+
const snapById = new Map(snapshotEvents.map((e) => [e.id, e]));
|
|
1199
|
+
const mergedExisting = existing.map((e) => {
|
|
1200
|
+
const snap = snapById.get(e.id);
|
|
1201
|
+
return snap ? mergeThreadEventById(e, snap) : e;
|
|
1202
|
+
});
|
|
991
1203
|
const newFromSnapshot = snapshotEvents.filter((e) => !existingIds.has(e.id));
|
|
992
|
-
mergedEvents = newFromSnapshot.length > 0 ? [...
|
|
1204
|
+
mergedEvents = newFromSnapshot.length > 0 ? [...mergedExisting, ...newFromSnapshot] : mergedExisting;
|
|
993
1205
|
}
|
|
994
1206
|
const optimistic = { ...s.optimistic };
|
|
995
1207
|
const optimisticKeys = { ...s.optimisticKeys };
|
|
@@ -1013,7 +1225,8 @@ function createAgentStore() {
|
|
|
1013
1225
|
const appends = [];
|
|
1014
1226
|
for (const e of newEvents) {
|
|
1015
1227
|
if (existingIds.has(e.id)) {
|
|
1016
|
-
|
|
1228
|
+
const prev = existing.find((x) => x.id === e.id);
|
|
1229
|
+
updates.set(e.id, mergeThreadEventById(prev, e));
|
|
1017
1230
|
} else {
|
|
1018
1231
|
appends.push(e);
|
|
1019
1232
|
}
|
|
@@ -1027,6 +1240,10 @@ function createAgentStore() {
|
|
|
1027
1240
|
reconcileEvents: (threadId, newEvents, resolvedClientEventIds) => set((s) => {
|
|
1028
1241
|
const existing = s.events[threadId] ?? [];
|
|
1029
1242
|
const existingIds = new Set(existing.map((e) => e.id));
|
|
1243
|
+
const mergedExisting = newEvents.length > 0 ? existing.map((e) => {
|
|
1244
|
+
const up = newEvents.find((n) => n.id === e.id);
|
|
1245
|
+
return up ? mergeThreadEventById(e, up) : e;
|
|
1246
|
+
}) : existing;
|
|
1030
1247
|
const unique = newEvents.filter((e) => !existingIds.has(e.id));
|
|
1031
1248
|
let optEvents = s.optimistic[threadId] ?? [];
|
|
1032
1249
|
let optKeys = s.optimisticKeys[threadId] ?? [];
|
|
@@ -1040,7 +1257,7 @@ function createAgentStore() {
|
|
|
1040
1257
|
}
|
|
1041
1258
|
}
|
|
1042
1259
|
return {
|
|
1043
|
-
events: { ...s.events, [threadId]: [...
|
|
1260
|
+
events: { ...s.events, [threadId]: [...mergedExisting, ...unique] },
|
|
1044
1261
|
optimistic: { ...s.optimistic, [threadId]: optEvents },
|
|
1045
1262
|
optimisticKeys: { ...s.optimisticKeys, [threadId]: optKeys }
|
|
1046
1263
|
};
|
|
@@ -1090,7 +1307,10 @@ function createAgentStore() {
|
|
|
1090
1307
|
const subscriptions = new Set(s.subscriptions);
|
|
1091
1308
|
subscriptions.add(threadId);
|
|
1092
1309
|
const loadingThreads = new Set(s.loadingThreads);
|
|
1093
|
-
|
|
1310
|
+
const alreadyHadEvents = (s.events[threadId]?.length ?? 0) > 0;
|
|
1311
|
+
if (!alreadyHadEvents) {
|
|
1312
|
+
loadingThreads.add(threadId);
|
|
1313
|
+
}
|
|
1094
1314
|
return { subscriptions, loadingThreads };
|
|
1095
1315
|
}),
|
|
1096
1316
|
removeSubscription: (threadId) => set((s) => {
|
|
@@ -1213,11 +1433,15 @@ function AgentProvider({
|
|
|
1213
1433
|
userId,
|
|
1214
1434
|
userName,
|
|
1215
1435
|
userEmail,
|
|
1436
|
+
userMobileNumber,
|
|
1437
|
+
userWhatsappNumber,
|
|
1216
1438
|
identityIds,
|
|
1217
1439
|
systemRole,
|
|
1218
1440
|
organizationRole,
|
|
1219
1441
|
workspaceRole,
|
|
1220
1442
|
authToken,
|
|
1443
|
+
channel,
|
|
1444
|
+
channelMeta,
|
|
1221
1445
|
uploadUrl,
|
|
1222
1446
|
onError,
|
|
1223
1447
|
children
|
|
@@ -1234,6 +1458,9 @@ function AgentProvider({
|
|
|
1234
1458
|
react.useEffect(() => {
|
|
1235
1459
|
clientRef.current?.setUploadUrl(uploadUrl);
|
|
1236
1460
|
}, [uploadUrl]);
|
|
1461
|
+
react.useEffect(() => {
|
|
1462
|
+
clientRef.current?.setUploadAgentId(agentId);
|
|
1463
|
+
}, [agentId]);
|
|
1237
1464
|
const identityIdsKey = identityIds?.join(",") ?? "";
|
|
1238
1465
|
react.useEffect(() => {
|
|
1239
1466
|
if (!url || !agentId) return;
|
|
@@ -1243,11 +1470,15 @@ function AgentProvider({
|
|
|
1243
1470
|
userId,
|
|
1244
1471
|
userName,
|
|
1245
1472
|
userEmail,
|
|
1473
|
+
userMobileNumber,
|
|
1474
|
+
userWhatsappNumber,
|
|
1246
1475
|
identityIds: identityIdsKey ? identityIdsKey.split(",") : void 0,
|
|
1247
1476
|
systemRole,
|
|
1248
1477
|
organizationRole,
|
|
1249
1478
|
workspaceRole,
|
|
1250
|
-
authToken
|
|
1479
|
+
authToken,
|
|
1480
|
+
channel,
|
|
1481
|
+
channelMeta
|
|
1251
1482
|
});
|
|
1252
1483
|
return () => clientRef.current?.disconnect();
|
|
1253
1484
|
}, [
|
|
@@ -1258,11 +1489,15 @@ function AgentProvider({
|
|
|
1258
1489
|
userId,
|
|
1259
1490
|
userName,
|
|
1260
1491
|
userEmail,
|
|
1492
|
+
userMobileNumber,
|
|
1493
|
+
userWhatsappNumber,
|
|
1261
1494
|
identityIdsKey,
|
|
1262
1495
|
systemRole,
|
|
1263
1496
|
organizationRole,
|
|
1264
1497
|
workspaceRole,
|
|
1265
|
-
authToken
|
|
1498
|
+
authToken,
|
|
1499
|
+
channel,
|
|
1500
|
+
channelMeta
|
|
1266
1501
|
]);
|
|
1267
1502
|
const contextValue = react.useMemo(
|
|
1268
1503
|
() => ({ store: storeRef.current, client: clientRef.current }),
|
|
@@ -1403,9 +1638,13 @@ function useConnection() {
|
|
|
1403
1638
|
const { client } = useAgentContext();
|
|
1404
1639
|
const status = useStore((s) => s.connection.status);
|
|
1405
1640
|
const error = useStore((s) => s.connection.error);
|
|
1641
|
+
const sendError = useStore((s) => s.connection.sendError);
|
|
1406
1642
|
const reconnectFailed = useStore((s) => s.connection.reconnectFailed);
|
|
1407
1643
|
const reconnect = react.useCallback(() => client.retry(), [client]);
|
|
1408
|
-
|
|
1644
|
+
const dismissSendError = react.useCallback(() => {
|
|
1645
|
+
client.clearSendError();
|
|
1646
|
+
}, [client]);
|
|
1647
|
+
return { status, error, sendError, dismissSendError, reconnectFailed, reconnect };
|
|
1409
1648
|
}
|
|
1410
1649
|
function useFeedback(targetId, targetType = "event") {
|
|
1411
1650
|
const { client } = useAgentContext();
|
|
@@ -1534,11 +1773,15 @@ function useFormFileUpload() {
|
|
|
1534
1773
|
setIsUploading(true);
|
|
1535
1774
|
setError(null);
|
|
1536
1775
|
try {
|
|
1776
|
+
const aid = (params.agentId || "").trim();
|
|
1777
|
+
if (!aid) {
|
|
1778
|
+
throw new Error("agentId is required for upload");
|
|
1779
|
+
}
|
|
1537
1780
|
const formData = new FormData();
|
|
1538
1781
|
formData.append("file", params.file);
|
|
1539
1782
|
formData.append("field_id", params.fieldId);
|
|
1540
1783
|
formData.append("source", "chat");
|
|
1541
|
-
formData.append("agent_id",
|
|
1784
|
+
formData.append("agent_id", aid);
|
|
1542
1785
|
formData.append("run_id", params.runId);
|
|
1543
1786
|
formData.append("thread_id", params.threadId);
|
|
1544
1787
|
const response = await fetch(params.uploadUrl, {
|
|
@@ -1690,11 +1933,11 @@ function useUpload() {
|
|
|
1690
1933
|
const [isUploading, setIsUploading] = react.useState(false);
|
|
1691
1934
|
const activeCountRef = react.useRef(0);
|
|
1692
1935
|
const upload = react.useCallback(
|
|
1693
|
-
async (file) => {
|
|
1936
|
+
async (file, options) => {
|
|
1694
1937
|
activeCountRef.current++;
|
|
1695
1938
|
setIsUploading(true);
|
|
1696
1939
|
try {
|
|
1697
|
-
return await client.upload(file);
|
|
1940
|
+
return await client.upload(file, options);
|
|
1698
1941
|
} finally {
|
|
1699
1942
|
activeCountRef.current--;
|
|
1700
1943
|
if (activeCountRef.current === 0) {
|
|
@@ -1709,11 +1952,13 @@ function useUpload() {
|
|
|
1709
1952
|
|
|
1710
1953
|
exports.AgentProvider = AgentProvider;
|
|
1711
1954
|
exports.AgentServerClient = AgentServerClient;
|
|
1955
|
+
exports.PACKAGE_VERSION = PACKAGE_VERSION;
|
|
1712
1956
|
exports.apiMessagesUrlToWebSocketUrl = apiMessagesUrlToWebSocketUrl;
|
|
1713
1957
|
exports.isSettingsFieldGroup = isSettingsFieldGroup;
|
|
1714
1958
|
exports.toFormElement = toFormElement;
|
|
1715
1959
|
exports.toFormEventData = toFormEventData;
|
|
1716
1960
|
exports.toFormResponseEventData = toFormResponseEventData;
|
|
1961
|
+
exports.useAgentContext = useAgentContext;
|
|
1717
1962
|
exports.useAnalytics = useAnalytics;
|
|
1718
1963
|
exports.useAudioRecorder = useAudioRecorder;
|
|
1719
1964
|
exports.useConnection = useConnection;
|