dooers-agents-client 0.8.0 → 0.9.2
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 +255 -39
- package/dist/main.cjs.map +1 -1
- package/dist/main.d.cts +184 -2
- package/dist/main.d.ts +184 -2
- package/dist/main.js +256 -41
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -49,6 +49,7 @@ function toThread(w) {
|
|
|
49
49
|
owner: toUser(w.owner),
|
|
50
50
|
users: w.users.map((u) => toUser(u)),
|
|
51
51
|
title: w.title,
|
|
52
|
+
metadata: w.metadata ?? null,
|
|
52
53
|
createdAt: w.created_at,
|
|
53
54
|
updatedAt: w.updated_at,
|
|
54
55
|
lastEventAt: w.last_event_at
|
|
@@ -245,6 +246,8 @@ function toWireContentPart(p) {
|
|
|
245
246
|
ref_id: p.refId
|
|
246
247
|
};
|
|
247
248
|
if (p.duration != null) w.duration = p.duration;
|
|
249
|
+
if (p.filename?.trim()) w.filename = p.filename.trim();
|
|
250
|
+
if (p.mimeType?.trim()) w.mime_type = p.mimeType.trim();
|
|
248
251
|
if (p.url) w.url = p.url;
|
|
249
252
|
return w;
|
|
250
253
|
}
|
|
@@ -253,6 +256,8 @@ function toWireContentPart(p) {
|
|
|
253
256
|
type: "image",
|
|
254
257
|
ref_id: p.refId
|
|
255
258
|
};
|
|
259
|
+
if (p.filename?.trim()) w.filename = p.filename.trim();
|
|
260
|
+
if (p.mimeType?.trim()) w.mime_type = p.mimeType.trim();
|
|
256
261
|
if (p.url) w.url = p.url;
|
|
257
262
|
return w;
|
|
258
263
|
}
|
|
@@ -261,6 +266,8 @@ function toWireContentPart(p) {
|
|
|
261
266
|
type: "document",
|
|
262
267
|
ref_id: p.refId
|
|
263
268
|
};
|
|
269
|
+
if (p.filename?.trim()) w.filename = p.filename.trim();
|
|
270
|
+
if (p.mimeType?.trim()) w.mime_type = p.mimeType.trim();
|
|
264
271
|
if (p.url) w.url = p.url;
|
|
265
272
|
return w;
|
|
266
273
|
}
|
|
@@ -271,6 +278,35 @@ function toWireContentPart(p) {
|
|
|
271
278
|
var MAX_RECONNECT_ATTEMPTS = 5;
|
|
272
279
|
var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3];
|
|
273
280
|
var SEND_MESSAGE_TIMEOUT = 3e4;
|
|
281
|
+
function blobPartFilename(blob, override) {
|
|
282
|
+
const o = (override ?? "").trim();
|
|
283
|
+
if (o) return o;
|
|
284
|
+
const t = (blob.type || "").toLowerCase();
|
|
285
|
+
if (t.includes("webm")) return "recording.webm";
|
|
286
|
+
if (t.includes("ogg")) return "recording.ogg";
|
|
287
|
+
if (t.includes("mpeg") || t.includes("mp3")) return "recording.mp3";
|
|
288
|
+
if (t.includes("mp4") || t.includes("m4a") || t.includes("aac")) return "recording.m4a";
|
|
289
|
+
if (t.startsWith("audio/")) return `recording.${t.split("/")[1]?.split("+")[0] || "bin"}`;
|
|
290
|
+
return "recording.bin";
|
|
291
|
+
}
|
|
292
|
+
async function readHttpErrorMessage(response) {
|
|
293
|
+
const raw = await response.text().catch(() => "") || "";
|
|
294
|
+
const trimmed = raw.trim();
|
|
295
|
+
if (!trimmed) {
|
|
296
|
+
return `Upload failed: ${response.status} ${response.statusText}`.trim();
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
const j = JSON.parse(trimmed);
|
|
300
|
+
const d = j.detail;
|
|
301
|
+
if (typeof d === "string") return d;
|
|
302
|
+
if (Array.isArray(d) && d.length > 0) {
|
|
303
|
+
const first = d[0];
|
|
304
|
+
if (first && typeof first.msg === "string") return first.msg;
|
|
305
|
+
}
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
return trimmed.length > 300 ? `${trimmed.slice(0, 300)}\u2026` : trimmed;
|
|
309
|
+
}
|
|
274
310
|
var AgentServerClient = class {
|
|
275
311
|
wsUrl;
|
|
276
312
|
constructor(apiMessagesUrl) {
|
|
@@ -364,6 +400,12 @@ var AgentClient = class {
|
|
|
364
400
|
url = "";
|
|
365
401
|
httpBaseUrl = "";
|
|
366
402
|
agentId = "";
|
|
403
|
+
/** Mirrors ``AgentProvider`` ``agentId`` so HTTP uploads work before WS ``connect`` completes. */
|
|
404
|
+
uploadAgentId = "";
|
|
405
|
+
/** Current chat thread id for ``POST /uploads`` (aligns GCS path with ``hydrate_thread_events``). */
|
|
406
|
+
uploadThreadId = "";
|
|
407
|
+
/** Optional run id for ``POST /uploads`` (metadata only). */
|
|
408
|
+
uploadRunId = "";
|
|
367
409
|
uploadUrl;
|
|
368
410
|
config = {
|
|
369
411
|
organizationId: "",
|
|
@@ -378,7 +420,7 @@ var AgentClient = class {
|
|
|
378
420
|
subscriptionRefs = /* @__PURE__ */ new Map();
|
|
379
421
|
// Track optimistic events for reconciliation
|
|
380
422
|
pendingOptimistic = /* @__PURE__ */ new Map();
|
|
381
|
-
// Pending message promises —
|
|
423
|
+
// Pending message promises — keyed by outbound frame id (same as client_event_id for sends).
|
|
382
424
|
pendingMessages = /* @__PURE__ */ new Map();
|
|
383
425
|
// Track last event ID per thread for gap recovery on reconnect
|
|
384
426
|
lastEventIds = /* @__PURE__ */ new Map();
|
|
@@ -396,14 +438,40 @@ var AgentClient = class {
|
|
|
396
438
|
setUploadUrl(url) {
|
|
397
439
|
this.uploadUrl = url;
|
|
398
440
|
}
|
|
399
|
-
|
|
441
|
+
/** Sync from ``AgentProvider`` ``agentId``; used for ``POST /uploads`` ``agent_id`` (non-WS). */
|
|
442
|
+
setUploadAgentId(agentId) {
|
|
443
|
+
this.uploadAgentId = (agentId ?? "").trim();
|
|
444
|
+
}
|
|
445
|
+
/** Sync active thread id for chat uploads (must match persisted thread for signed URL keys). */
|
|
446
|
+
setUploadThreadId(threadId) {
|
|
447
|
+
this.uploadThreadId = (threadId ?? "").trim();
|
|
448
|
+
}
|
|
449
|
+
setUploadRunId(runId) {
|
|
450
|
+
this.uploadRunId = (runId ?? "").trim();
|
|
451
|
+
}
|
|
452
|
+
async upload(file, options) {
|
|
400
453
|
if (!this.uploadUrl) {
|
|
401
454
|
throw new Error("uploadUrl not configured");
|
|
402
455
|
}
|
|
456
|
+
const aid = (this.uploadAgentId || this.agentId || "").trim();
|
|
457
|
+
if (!aid) {
|
|
458
|
+
throw new Error("agentId is required for upload (set AgentProvider agentId)");
|
|
459
|
+
}
|
|
403
460
|
const formData = new FormData();
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
formData.append("
|
|
461
|
+
if (file instanceof File) {
|
|
462
|
+
const partName = options?.filename?.trim() || file.name.trim() || "upload";
|
|
463
|
+
formData.append("file", file, partName);
|
|
464
|
+
} else {
|
|
465
|
+
formData.append("file", file, blobPartFilename(file, options?.filename));
|
|
466
|
+
}
|
|
467
|
+
formData.append("agent_id", aid);
|
|
468
|
+
const tid = this.uploadThreadId.trim();
|
|
469
|
+
if (tid) {
|
|
470
|
+
formData.append("thread_id", tid);
|
|
471
|
+
}
|
|
472
|
+
const rid = this.uploadRunId.trim();
|
|
473
|
+
if (rid) {
|
|
474
|
+
formData.append("run_id", rid);
|
|
407
475
|
}
|
|
408
476
|
const headers = {};
|
|
409
477
|
if (this.config.authToken) {
|
|
@@ -415,7 +483,8 @@ var AgentClient = class {
|
|
|
415
483
|
headers
|
|
416
484
|
});
|
|
417
485
|
if (!response.ok) {
|
|
418
|
-
|
|
486
|
+
const detail = await readHttpErrorMessage(response);
|
|
487
|
+
throw new Error(detail);
|
|
419
488
|
}
|
|
420
489
|
const result = await response.json();
|
|
421
490
|
return {
|
|
@@ -429,6 +498,7 @@ var AgentClient = class {
|
|
|
429
498
|
connect(url, agentId, config) {
|
|
430
499
|
this.url = apiMessagesUrlToWebSocketUrl(url);
|
|
431
500
|
this.agentId = agentId;
|
|
501
|
+
this.uploadAgentId = agentId.trim();
|
|
432
502
|
this.config = config ?? { organizationId: "", workspaceId: "", userId: "" };
|
|
433
503
|
this.isIntentionallyClosed = false;
|
|
434
504
|
try {
|
|
@@ -444,16 +514,29 @@ var AgentClient = class {
|
|
|
444
514
|
this.callbacks.setConnectionStatus("connecting");
|
|
445
515
|
this.createConnection();
|
|
446
516
|
}
|
|
517
|
+
/** Clear the last in-chat send error banner (see ``connection.sendError``). */
|
|
518
|
+
clearSendError() {
|
|
519
|
+
this.callbacks.setSendError(null);
|
|
520
|
+
}
|
|
521
|
+
abortPendingMessages(reason) {
|
|
522
|
+
for (const [clientEventId, pending] of this.pendingMessages) {
|
|
523
|
+
clearTimeout(pending.timer);
|
|
524
|
+
const opt = this.pendingOptimistic.get(clientEventId);
|
|
525
|
+
if (opt?.threadId) {
|
|
526
|
+
this.callbacks.removeOptimistic(opt.threadId, clientEventId);
|
|
527
|
+
}
|
|
528
|
+
this.pendingOptimistic.delete(clientEventId);
|
|
529
|
+
pending.reject(new Error(reason));
|
|
530
|
+
}
|
|
531
|
+
this.pendingMessages.clear();
|
|
532
|
+
}
|
|
447
533
|
disconnect() {
|
|
448
534
|
this.isIntentionallyClosed = true;
|
|
449
535
|
if (this.reconnectTimer) {
|
|
450
536
|
clearTimeout(this.reconnectTimer);
|
|
451
537
|
this.reconnectTimer = null;
|
|
452
538
|
}
|
|
453
|
-
|
|
454
|
-
clearTimeout(pending.timer);
|
|
455
|
-
}
|
|
456
|
-
this.pendingMessages.clear();
|
|
539
|
+
this.abortPendingMessages("Disconnected");
|
|
457
540
|
if (this.ws) {
|
|
458
541
|
this.ws.onopen = null;
|
|
459
542
|
this.ws.onmessage = null;
|
|
@@ -545,6 +628,7 @@ var AgentClient = class {
|
|
|
545
628
|
}
|
|
546
629
|
// --- Messaging ---
|
|
547
630
|
sendMessage(params) {
|
|
631
|
+
this.callbacks.setSendError(null);
|
|
548
632
|
const clientEventId = crypto.randomUUID();
|
|
549
633
|
const content = params.content ? params.content.map(toWireContentPart) : [{ type: "text", text: params.text ?? "" }];
|
|
550
634
|
const displayContent = params.content ? params.content.map((p) => {
|
|
@@ -552,11 +636,27 @@ var AgentClient = class {
|
|
|
552
636
|
case "text":
|
|
553
637
|
return { type: "text", text: p.text };
|
|
554
638
|
case "audio":
|
|
555
|
-
return {
|
|
639
|
+
return {
|
|
640
|
+
type: "audio",
|
|
641
|
+
duration: p.duration,
|
|
642
|
+
...p.url ? { url: p.url } : {},
|
|
643
|
+
...p.mimeType ? { mimeType: p.mimeType } : {},
|
|
644
|
+
...p.filename ? { filename: p.filename } : {}
|
|
645
|
+
};
|
|
556
646
|
case "image":
|
|
557
|
-
return {
|
|
647
|
+
return {
|
|
648
|
+
type: "image",
|
|
649
|
+
...p.url ? { url: p.url } : {},
|
|
650
|
+
...p.mimeType ? { mimeType: p.mimeType } : {},
|
|
651
|
+
...p.filename ? { filename: p.filename } : {}
|
|
652
|
+
};
|
|
558
653
|
case "document":
|
|
559
|
-
return {
|
|
654
|
+
return {
|
|
655
|
+
type: "document",
|
|
656
|
+
...p.url ? { url: p.url } : {},
|
|
657
|
+
...p.mimeType ? { mimeType: p.mimeType } : {},
|
|
658
|
+
...p.filename ? { filename: p.filename } : {}
|
|
659
|
+
};
|
|
560
660
|
default:
|
|
561
661
|
return { type: "text", text: "" };
|
|
562
662
|
}
|
|
@@ -588,7 +688,7 @@ var AgentClient = class {
|
|
|
588
688
|
clientEventId
|
|
589
689
|
});
|
|
590
690
|
const promise = this.createPendingPromise(clientEventId);
|
|
591
|
-
|
|
691
|
+
const payload = {
|
|
592
692
|
thread_id: params.threadId,
|
|
593
693
|
client_event_id: clientEventId,
|
|
594
694
|
event: {
|
|
@@ -596,23 +696,36 @@ var AgentClient = class {
|
|
|
596
696
|
actor: "user",
|
|
597
697
|
content
|
|
598
698
|
}
|
|
699
|
+
};
|
|
700
|
+
if (params.metadata) {
|
|
701
|
+
payload.metadata = params.metadata;
|
|
702
|
+
}
|
|
703
|
+
this.sendRaw({
|
|
704
|
+
id: clientEventId,
|
|
705
|
+
type: "event.create",
|
|
706
|
+
payload
|
|
599
707
|
});
|
|
600
708
|
return promise;
|
|
601
709
|
}
|
|
602
710
|
sendFormResponse(params) {
|
|
711
|
+
this.callbacks.setSendError(null);
|
|
603
712
|
const clientEventId = crypto.randomUUID();
|
|
604
713
|
const promise = this.createPendingPromise(clientEventId);
|
|
605
|
-
this.
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
714
|
+
this.sendRaw({
|
|
715
|
+
id: clientEventId,
|
|
716
|
+
type: "event.create",
|
|
717
|
+
payload: {
|
|
718
|
+
thread_id: params.threadId,
|
|
719
|
+
client_event_id: clientEventId,
|
|
720
|
+
event: {
|
|
721
|
+
type: "form.response",
|
|
722
|
+
actor: "user",
|
|
723
|
+
content: [],
|
|
724
|
+
data: {
|
|
725
|
+
form_event_id: params.formEventId,
|
|
726
|
+
cancelled: params.cancelled,
|
|
727
|
+
values: params.values
|
|
728
|
+
}
|
|
616
729
|
}
|
|
617
730
|
}
|
|
618
731
|
});
|
|
@@ -622,14 +735,20 @@ var AgentClient = class {
|
|
|
622
735
|
createPendingPromise(clientEventId) {
|
|
623
736
|
return new Promise((resolve, reject) => {
|
|
624
737
|
const timer = setTimeout(() => {
|
|
738
|
+
const opt = this.pendingOptimistic.get(clientEventId);
|
|
739
|
+
if (opt?.threadId) {
|
|
740
|
+
this.callbacks.removeOptimistic(opt.threadId, clientEventId);
|
|
741
|
+
}
|
|
742
|
+
this.pendingOptimistic.delete(clientEventId);
|
|
625
743
|
this.pendingMessages.delete(clientEventId);
|
|
626
744
|
reject(new Error("Request timed out waiting for server response"));
|
|
627
745
|
}, SEND_MESSAGE_TIMEOUT);
|
|
628
|
-
this.pendingMessages.set(clientEventId, { resolve, timer });
|
|
746
|
+
this.pendingMessages.set(clientEventId, { resolve, reject, timer });
|
|
629
747
|
});
|
|
630
748
|
}
|
|
631
749
|
createConnection() {
|
|
632
750
|
if (this.ws) {
|
|
751
|
+
this.abortPendingMessages("Connection lost; reconnecting");
|
|
633
752
|
this.ws.onopen = null;
|
|
634
753
|
this.ws.onmessage = null;
|
|
635
754
|
this.ws.onclose = null;
|
|
@@ -697,9 +816,26 @@ var AgentClient = class {
|
|
|
697
816
|
this.callbacks.setConnectionStatus("error", frame.payload.error?.message);
|
|
698
817
|
}
|
|
699
818
|
} else if (!frame.payload.ok && frame.payload.error) {
|
|
819
|
+
const ackId = frame.payload.ack_id;
|
|
820
|
+
const err = frame.payload.error;
|
|
821
|
+
if (ackId) {
|
|
822
|
+
const pending = this.pendingMessages.get(ackId);
|
|
823
|
+
if (pending) {
|
|
824
|
+
clearTimeout(pending.timer);
|
|
825
|
+
this.pendingMessages.delete(ackId);
|
|
826
|
+
const msg = err.message || "Request rejected";
|
|
827
|
+
const opt = this.pendingOptimistic.get(ackId);
|
|
828
|
+
if (opt?.threadId) {
|
|
829
|
+
this.callbacks.removeOptimistic(opt.threadId, ackId);
|
|
830
|
+
}
|
|
831
|
+
this.pendingOptimistic.delete(ackId);
|
|
832
|
+
pending.reject(new Error(msg));
|
|
833
|
+
this.callbacks.setSendError(msg);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
700
836
|
this.onError?.({
|
|
701
|
-
code:
|
|
702
|
-
message:
|
|
837
|
+
code: err.code,
|
|
838
|
+
message: err.message,
|
|
703
839
|
frameType: "ack"
|
|
704
840
|
});
|
|
705
841
|
}
|
|
@@ -736,6 +872,7 @@ var AgentClient = class {
|
|
|
736
872
|
case "event.append": {
|
|
737
873
|
const events = this.resolveEventUrls(frame.payload.events.map(toThreadEvent));
|
|
738
874
|
const resolvedClientEventIds = [];
|
|
875
|
+
const pendingIdsToResolve = [];
|
|
739
876
|
for (const event of events) {
|
|
740
877
|
if (event.clientEventId && this.pendingOptimistic.has(event.clientEventId)) {
|
|
741
878
|
resolvedClientEventIds.push(event.clientEventId);
|
|
@@ -745,8 +882,7 @@ var AgentClient = class {
|
|
|
745
882
|
const pendingMessage = this.pendingMessages.get(event.clientEventId);
|
|
746
883
|
if (pendingMessage) {
|
|
747
884
|
clearTimeout(pendingMessage.timer);
|
|
748
|
-
|
|
749
|
-
this.pendingMessages.delete(event.clientEventId);
|
|
885
|
+
pendingIdsToResolve.push(event.clientEventId);
|
|
750
886
|
}
|
|
751
887
|
}
|
|
752
888
|
}
|
|
@@ -759,6 +895,14 @@ var AgentClient = class {
|
|
|
759
895
|
} else {
|
|
760
896
|
this.callbacks.onEventAppend(frame.payload.thread_id, events);
|
|
761
897
|
}
|
|
898
|
+
const tid = frame.payload.thread_id;
|
|
899
|
+
for (const id of pendingIdsToResolve) {
|
|
900
|
+
const pending = this.pendingMessages.get(id);
|
|
901
|
+
if (pending) {
|
|
902
|
+
pending.resolve({ threadId: tid });
|
|
903
|
+
this.pendingMessages.delete(id);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
762
906
|
break;
|
|
763
907
|
}
|
|
764
908
|
case "event.list.result": {
|
|
@@ -811,6 +955,7 @@ var AgentClient = class {
|
|
|
811
955
|
}
|
|
812
956
|
scheduleReconnect() {
|
|
813
957
|
if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
958
|
+
this.abortPendingMessages("Connection lost after maximum retries");
|
|
814
959
|
this.callbacks.setReconnectFailed();
|
|
815
960
|
this.callbacks.setConnectionStatus("error", "Connection lost after maximum retries");
|
|
816
961
|
return;
|
|
@@ -845,6 +990,43 @@ var AgentClient = class {
|
|
|
845
990
|
});
|
|
846
991
|
}
|
|
847
992
|
};
|
|
993
|
+
function mergeDisplayContentParts(prev, next) {
|
|
994
|
+
if (!next?.length) return prev;
|
|
995
|
+
if (!prev?.length) return next;
|
|
996
|
+
const out = [];
|
|
997
|
+
const len = Math.max(prev.length, next.length);
|
|
998
|
+
for (let i = 0; i < len; i++) {
|
|
999
|
+
const p = prev[i];
|
|
1000
|
+
const n = next[i];
|
|
1001
|
+
if (!n) {
|
|
1002
|
+
if (p) out.push(p);
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
if (!p) {
|
|
1006
|
+
out.push(n);
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
if (p.type !== n.type) {
|
|
1010
|
+
out.push(n);
|
|
1011
|
+
continue;
|
|
1012
|
+
}
|
|
1013
|
+
if (n.type === "image" || n.type === "audio" || n.type === "document") {
|
|
1014
|
+
const url = n.url ?? ("url" in p ? p.url : void 0);
|
|
1015
|
+
out.push({ ...p, ...n, ...url ? { url } : {} });
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
out.push(n);
|
|
1019
|
+
}
|
|
1020
|
+
return out;
|
|
1021
|
+
}
|
|
1022
|
+
function mergeThreadEventById(prev, next) {
|
|
1023
|
+
const content = mergeDisplayContentParts(prev.content, next.content);
|
|
1024
|
+
return {
|
|
1025
|
+
...prev,
|
|
1026
|
+
...next,
|
|
1027
|
+
...content !== void 0 ? { content } : {}
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
848
1030
|
function extractFormStates(events, existing) {
|
|
849
1031
|
let result = existing;
|
|
850
1032
|
for (const e of events) {
|
|
@@ -867,6 +1049,7 @@ function createAgentStore() {
|
|
|
867
1049
|
connection: {
|
|
868
1050
|
status: "idle",
|
|
869
1051
|
error: null,
|
|
1052
|
+
sendError: null,
|
|
870
1053
|
reconnectAttempts: 0,
|
|
871
1054
|
reconnectFailed: false
|
|
872
1055
|
},
|
|
@@ -895,7 +1078,15 @@ function createAgentStore() {
|
|
|
895
1078
|
loadingThreads: /* @__PURE__ */ new Set(),
|
|
896
1079
|
actions: {
|
|
897
1080
|
setConnectionStatus: (status, error) => set((s) => ({
|
|
898
|
-
connection: {
|
|
1081
|
+
connection: {
|
|
1082
|
+
...s.connection,
|
|
1083
|
+
status,
|
|
1084
|
+
error: error ?? null,
|
|
1085
|
+
...status === "connected" ? { sendError: null } : {}
|
|
1086
|
+
}
|
|
1087
|
+
})),
|
|
1088
|
+
setSendError: (message) => set((s) => ({
|
|
1089
|
+
connection: { ...s.connection, sendError: message }
|
|
899
1090
|
})),
|
|
900
1091
|
setReconnectFailed: () => set((s) => ({
|
|
901
1092
|
connection: { ...s.connection, reconnectFailed: true }
|
|
@@ -983,8 +1174,13 @@ function createAgentStore() {
|
|
|
983
1174
|
mergedEvents = snapshotEvents;
|
|
984
1175
|
} else {
|
|
985
1176
|
const existingIds = new Set(existing.map((e) => e.id));
|
|
1177
|
+
const snapById = new Map(snapshotEvents.map((e) => [e.id, e]));
|
|
1178
|
+
const mergedExisting = existing.map((e) => {
|
|
1179
|
+
const snap = snapById.get(e.id);
|
|
1180
|
+
return snap ? mergeThreadEventById(e, snap) : e;
|
|
1181
|
+
});
|
|
986
1182
|
const newFromSnapshot = snapshotEvents.filter((e) => !existingIds.has(e.id));
|
|
987
|
-
mergedEvents = newFromSnapshot.length > 0 ? [...
|
|
1183
|
+
mergedEvents = newFromSnapshot.length > 0 ? [...mergedExisting, ...newFromSnapshot] : mergedExisting;
|
|
988
1184
|
}
|
|
989
1185
|
const optimistic = { ...s.optimistic };
|
|
990
1186
|
const optimisticKeys = { ...s.optimisticKeys };
|
|
@@ -1008,7 +1204,8 @@ function createAgentStore() {
|
|
|
1008
1204
|
const appends = [];
|
|
1009
1205
|
for (const e of newEvents) {
|
|
1010
1206
|
if (existingIds.has(e.id)) {
|
|
1011
|
-
|
|
1207
|
+
const prev = existing.find((x) => x.id === e.id);
|
|
1208
|
+
updates.set(e.id, mergeThreadEventById(prev, e));
|
|
1012
1209
|
} else {
|
|
1013
1210
|
appends.push(e);
|
|
1014
1211
|
}
|
|
@@ -1022,6 +1219,10 @@ function createAgentStore() {
|
|
|
1022
1219
|
reconcileEvents: (threadId, newEvents, resolvedClientEventIds) => set((s) => {
|
|
1023
1220
|
const existing = s.events[threadId] ?? [];
|
|
1024
1221
|
const existingIds = new Set(existing.map((e) => e.id));
|
|
1222
|
+
const mergedExisting = newEvents.length > 0 ? existing.map((e) => {
|
|
1223
|
+
const up = newEvents.find((n) => n.id === e.id);
|
|
1224
|
+
return up ? mergeThreadEventById(e, up) : e;
|
|
1225
|
+
}) : existing;
|
|
1025
1226
|
const unique = newEvents.filter((e) => !existingIds.has(e.id));
|
|
1026
1227
|
let optEvents = s.optimistic[threadId] ?? [];
|
|
1027
1228
|
let optKeys = s.optimisticKeys[threadId] ?? [];
|
|
@@ -1035,7 +1236,7 @@ function createAgentStore() {
|
|
|
1035
1236
|
}
|
|
1036
1237
|
}
|
|
1037
1238
|
return {
|
|
1038
|
-
events: { ...s.events, [threadId]: [...
|
|
1239
|
+
events: { ...s.events, [threadId]: [...mergedExisting, ...unique] },
|
|
1039
1240
|
optimistic: { ...s.optimistic, [threadId]: optEvents },
|
|
1040
1241
|
optimisticKeys: { ...s.optimisticKeys, [threadId]: optKeys }
|
|
1041
1242
|
};
|
|
@@ -1085,7 +1286,10 @@ function createAgentStore() {
|
|
|
1085
1286
|
const subscriptions = new Set(s.subscriptions);
|
|
1086
1287
|
subscriptions.add(threadId);
|
|
1087
1288
|
const loadingThreads = new Set(s.loadingThreads);
|
|
1088
|
-
|
|
1289
|
+
const alreadyHadEvents = (s.events[threadId]?.length ?? 0) > 0;
|
|
1290
|
+
if (!alreadyHadEvents) {
|
|
1291
|
+
loadingThreads.add(threadId);
|
|
1292
|
+
}
|
|
1089
1293
|
return { subscriptions, loadingThreads };
|
|
1090
1294
|
}),
|
|
1091
1295
|
removeSubscription: (threadId) => set((s) => {
|
|
@@ -1229,6 +1433,9 @@ function AgentProvider({
|
|
|
1229
1433
|
react.useEffect(() => {
|
|
1230
1434
|
clientRef.current?.setUploadUrl(uploadUrl);
|
|
1231
1435
|
}, [uploadUrl]);
|
|
1436
|
+
react.useEffect(() => {
|
|
1437
|
+
clientRef.current?.setUploadAgentId(agentId);
|
|
1438
|
+
}, [agentId]);
|
|
1232
1439
|
const identityIdsKey = identityIds?.join(",") ?? "";
|
|
1233
1440
|
react.useEffect(() => {
|
|
1234
1441
|
if (!url || !agentId) return;
|
|
@@ -1398,9 +1605,13 @@ function useConnection() {
|
|
|
1398
1605
|
const { client } = useAgentContext();
|
|
1399
1606
|
const status = useStore((s) => s.connection.status);
|
|
1400
1607
|
const error = useStore((s) => s.connection.error);
|
|
1608
|
+
const sendError = useStore((s) => s.connection.sendError);
|
|
1401
1609
|
const reconnectFailed = useStore((s) => s.connection.reconnectFailed);
|
|
1402
1610
|
const reconnect = react.useCallback(() => client.retry(), [client]);
|
|
1403
|
-
|
|
1611
|
+
const dismissSendError = react.useCallback(() => {
|
|
1612
|
+
client.clearSendError();
|
|
1613
|
+
}, [client]);
|
|
1614
|
+
return { status, error, sendError, dismissSendError, reconnectFailed, reconnect };
|
|
1404
1615
|
}
|
|
1405
1616
|
function useFeedback(targetId, targetType = "event") {
|
|
1406
1617
|
const { client } = useAgentContext();
|
|
@@ -1529,11 +1740,15 @@ function useFormFileUpload() {
|
|
|
1529
1740
|
setIsUploading(true);
|
|
1530
1741
|
setError(null);
|
|
1531
1742
|
try {
|
|
1743
|
+
const aid = (params.agentId || "").trim();
|
|
1744
|
+
if (!aid) {
|
|
1745
|
+
throw new Error("agentId is required for upload");
|
|
1746
|
+
}
|
|
1532
1747
|
const formData = new FormData();
|
|
1533
1748
|
formData.append("file", params.file);
|
|
1534
1749
|
formData.append("field_id", params.fieldId);
|
|
1535
1750
|
formData.append("source", "chat");
|
|
1536
|
-
formData.append("agent_id",
|
|
1751
|
+
formData.append("agent_id", aid);
|
|
1537
1752
|
formData.append("run_id", params.runId);
|
|
1538
1753
|
formData.append("thread_id", params.threadId);
|
|
1539
1754
|
const response = await fetch(params.uploadUrl, {
|
|
@@ -1685,11 +1900,11 @@ function useUpload() {
|
|
|
1685
1900
|
const [isUploading, setIsUploading] = react.useState(false);
|
|
1686
1901
|
const activeCountRef = react.useRef(0);
|
|
1687
1902
|
const upload = react.useCallback(
|
|
1688
|
-
async (file) => {
|
|
1903
|
+
async (file, options) => {
|
|
1689
1904
|
activeCountRef.current++;
|
|
1690
1905
|
setIsUploading(true);
|
|
1691
1906
|
try {
|
|
1692
|
-
return await client.upload(file);
|
|
1907
|
+
return await client.upload(file, options);
|
|
1693
1908
|
} finally {
|
|
1694
1909
|
activeCountRef.current--;
|
|
1695
1910
|
if (activeCountRef.current === 0) {
|
|
@@ -1709,6 +1924,7 @@ exports.isSettingsFieldGroup = isSettingsFieldGroup;
|
|
|
1709
1924
|
exports.toFormElement = toFormElement;
|
|
1710
1925
|
exports.toFormEventData = toFormEventData;
|
|
1711
1926
|
exports.toFormResponseEventData = toFormResponseEventData;
|
|
1927
|
+
exports.useAgentContext = useAgentContext;
|
|
1712
1928
|
exports.useAnalytics = useAnalytics;
|
|
1713
1929
|
exports.useAudioRecorder = useAudioRecorder;
|
|
1714
1930
|
exports.useConnection = useConnection;
|