@personaai/react 0.7.10 → 0.8.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/dist/index.cjs +200 -216
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +200 -216
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -440,6 +440,11 @@ function useChat(options = {}) {
|
|
|
440
440
|
const chatLogger = (0, import_react2.useMemo)(() => logger.child("chat"), [logger]);
|
|
441
441
|
const agentId = options.agentId || defaultAgentId;
|
|
442
442
|
const threadId = options.threadId;
|
|
443
|
+
const [internalThreadId, setInternalThreadId] = (0, import_react2.useState)(void 0);
|
|
444
|
+
const effectiveThreadId = threadId ?? internalThreadId;
|
|
445
|
+
(0, import_react2.useEffect)(() => {
|
|
446
|
+
if (threadId !== void 0) setInternalThreadId(threadId);
|
|
447
|
+
}, [threadId]);
|
|
443
448
|
const didLogInitRef = (0, import_react2.useRef)(false);
|
|
444
449
|
if (!didLogInitRef.current) {
|
|
445
450
|
didLogInitRef.current = true;
|
|
@@ -476,9 +481,10 @@ function useChat(options = {}) {
|
|
|
476
481
|
}, [messages]);
|
|
477
482
|
const abortControllerRef = (0, import_react2.useRef)(null);
|
|
478
483
|
const loadedThreadIdRef = (0, import_react2.useRef)(void 0);
|
|
479
|
-
const voiceThreadRef = (0, import_react2.useRef)(
|
|
484
|
+
const voiceThreadRef = (0, import_react2.useRef)(effectiveThreadId);
|
|
480
485
|
const voicePrevLenRef = (0, import_react2.useRef)(0);
|
|
481
486
|
const voiceStreamingIdRef = (0, import_react2.useRef)(null);
|
|
487
|
+
const voiceUserPartialIdRef = (0, import_react2.useRef)(null);
|
|
482
488
|
const stop = (0, import_react2.useCallback)(() => {
|
|
483
489
|
if (abortControllerRef.current) {
|
|
484
490
|
chatLogger.info("stop streaming", {});
|
|
@@ -503,7 +509,25 @@ function useChat(options = {}) {
|
|
|
503
509
|
setFiles({});
|
|
504
510
|
setTodos([]);
|
|
505
511
|
setPresentedFile(null);
|
|
512
|
+
loadedThreadIdRef.current = void 0;
|
|
506
513
|
}, [stop, chatLogger, messages.length]);
|
|
514
|
+
const startNewChat = (0, import_react2.useCallback)(() => {
|
|
515
|
+
chatLogger.info("startNewChat \u2014 entering ephemeral mode");
|
|
516
|
+
stop();
|
|
517
|
+
setMessages([]);
|
|
518
|
+
setError(null);
|
|
519
|
+
setInterrupt(null);
|
|
520
|
+
setFiles({});
|
|
521
|
+
setTodos([]);
|
|
522
|
+
setPresentedFile(null);
|
|
523
|
+
setInput("");
|
|
524
|
+
loadedThreadIdRef.current = void 0;
|
|
525
|
+
voiceThreadRef.current = void 0;
|
|
526
|
+
voicePrevLenRef.current = 0;
|
|
527
|
+
voiceStreamingIdRef.current = null;
|
|
528
|
+
voiceUserPartialIdRef.current = null;
|
|
529
|
+
setInternalThreadId(void 0);
|
|
530
|
+
}, [stop, chatLogger]);
|
|
507
531
|
const loadThreadMessages = (0, import_react2.useCallback)(
|
|
508
532
|
async (id) => {
|
|
509
533
|
chatLogger.debug("loadThreadMessages start", { threadId: id });
|
|
@@ -535,6 +559,7 @@ function useChat(options = {}) {
|
|
|
535
559
|
setInterrupt(normalizePendingInterrupt(data?.pendingInterrupt));
|
|
536
560
|
setFiles(normalizeWorkspaceFiles(data?.state?.files ?? {}));
|
|
537
561
|
setTodos(data?.state?.todos ?? []);
|
|
562
|
+
loadedThreadIdRef.current = id;
|
|
538
563
|
chatLogger.info("loadThreadMessages succeeded", {
|
|
539
564
|
threadId: id,
|
|
540
565
|
messageCount: loaded.length
|
|
@@ -555,6 +580,7 @@ function useChat(options = {}) {
|
|
|
555
580
|
error: errorObj.message
|
|
556
581
|
});
|
|
557
582
|
setError(errorObj);
|
|
583
|
+
if (loadedThreadIdRef.current === id) loadedThreadIdRef.current = void 0;
|
|
558
584
|
return [];
|
|
559
585
|
} finally {
|
|
560
586
|
setIsLoadingHistory(false);
|
|
@@ -564,33 +590,33 @@ function useChat(options = {}) {
|
|
|
564
590
|
[fetchWithAuth, chatLogger]
|
|
565
591
|
);
|
|
566
592
|
(0, import_react2.useEffect)(() => {
|
|
567
|
-
if (!
|
|
568
|
-
chatLogger.trace("auto-load skipped", { threadId, isStreaming });
|
|
593
|
+
if (!effectiveThreadId || isStreaming) {
|
|
594
|
+
chatLogger.trace("auto-load skipped", { threadId: effectiveThreadId, isStreaming });
|
|
569
595
|
return;
|
|
570
596
|
}
|
|
571
|
-
if (loadedThreadIdRef.current ===
|
|
572
|
-
chatLogger.trace("auto-load already loaded", { threadId });
|
|
597
|
+
if (loadedThreadIdRef.current === effectiveThreadId) {
|
|
598
|
+
chatLogger.trace("auto-load already loaded", { threadId: effectiveThreadId });
|
|
573
599
|
return;
|
|
574
600
|
}
|
|
575
601
|
if (messages.length > 0) {
|
|
576
602
|
chatLogger.trace("auto-load has messages", {
|
|
577
|
-
threadId,
|
|
603
|
+
threadId: effectiveThreadId,
|
|
578
604
|
count: messages.length
|
|
579
605
|
});
|
|
580
606
|
return;
|
|
581
607
|
}
|
|
582
|
-
|
|
583
|
-
chatLogger.
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
}, [threadId, isStreaming, messages.length, loadThreadMessages, chatLogger]);
|
|
608
|
+
chatLogger.info("auto-load thread history", { threadId: effectiveThreadId });
|
|
609
|
+
chatLogger.debug("loadThreadMessages trigger", { threadId: effectiveThreadId });
|
|
610
|
+
void loadThreadMessages(effectiveThreadId);
|
|
611
|
+
}, [effectiveThreadId, isStreaming, messages.length, loadThreadMessages, chatLogger]);
|
|
587
612
|
const voice = options.voice;
|
|
588
613
|
(0, import_react2.useEffect)(() => {
|
|
589
614
|
if (!voice) return;
|
|
590
|
-
if (voiceThreadRef.current !==
|
|
591
|
-
voiceThreadRef.current =
|
|
615
|
+
if (voiceThreadRef.current !== effectiveThreadId) {
|
|
616
|
+
voiceThreadRef.current = effectiveThreadId;
|
|
592
617
|
voicePrevLenRef.current = voice.transcript.length;
|
|
593
618
|
voiceStreamingIdRef.current = null;
|
|
619
|
+
voiceUserPartialIdRef.current = null;
|
|
594
620
|
return;
|
|
595
621
|
}
|
|
596
622
|
const isVoiceActive = voice.state !== "idle" && voice.state !== "ended" && voice.state !== "error";
|
|
@@ -598,15 +624,40 @@ function useChat(options = {}) {
|
|
|
598
624
|
if (curLen < voicePrevLenRef.current) {
|
|
599
625
|
voicePrevLenRef.current = 0;
|
|
600
626
|
voiceStreamingIdRef.current = null;
|
|
627
|
+
voiceUserPartialIdRef.current = null;
|
|
601
628
|
}
|
|
602
629
|
if (!isVoiceActive) {
|
|
603
|
-
voicePrevLenRef.current
|
|
630
|
+
if (curLen > voicePrevLenRef.current) {
|
|
631
|
+
const pendingLines = voice.transcript.slice(voicePrevLenRef.current);
|
|
632
|
+
voicePrevLenRef.current = curLen;
|
|
633
|
+
for (const line of pendingLines) {
|
|
634
|
+
const text = (line.text || "").trim();
|
|
635
|
+
if (!text) continue;
|
|
636
|
+
const role = line.speaker === "user" ? "user" : "assistant";
|
|
637
|
+
const voiceId = `voice-${line.id}`;
|
|
638
|
+
setMessages((prev) => {
|
|
639
|
+
if (prev.some((m) => m.id === voiceId)) return prev;
|
|
640
|
+
if (prev.some((m) => !m.id.startsWith("voice-") && m.role === role && m.content.trim() === text)) return prev;
|
|
641
|
+
const last = prev[prev.length - 1];
|
|
642
|
+
if (last?.id.startsWith("voice-") && last.role === "assistant" && role === "assistant") {
|
|
643
|
+
const merged = text.startsWith(last.content) ? text : `${last.content} ${text}`.trim();
|
|
644
|
+
return [...prev.slice(0, -1), { ...last, content: merged, isStreaming: false }];
|
|
645
|
+
}
|
|
646
|
+
return [...prev, { id: voiceId, role, content: text, createdAt: /* @__PURE__ */ new Date() }];
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
} else {
|
|
650
|
+
voicePrevLenRef.current = voice.transcript.length;
|
|
651
|
+
}
|
|
604
652
|
if (voiceStreamingIdRef.current) {
|
|
605
653
|
const doneId = voiceStreamingIdRef.current;
|
|
606
654
|
voiceStreamingIdRef.current = null;
|
|
607
|
-
setMessages(
|
|
608
|
-
|
|
609
|
-
|
|
655
|
+
setMessages((prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m));
|
|
656
|
+
}
|
|
657
|
+
if (voiceUserPartialIdRef.current) {
|
|
658
|
+
const doneId = voiceUserPartialIdRef.current;
|
|
659
|
+
voiceUserPartialIdRef.current = null;
|
|
660
|
+
setMessages((prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m));
|
|
610
661
|
}
|
|
611
662
|
return;
|
|
612
663
|
}
|
|
@@ -633,54 +684,71 @@ function useChat(options = {}) {
|
|
|
633
684
|
});
|
|
634
685
|
}
|
|
635
686
|
}
|
|
636
|
-
const
|
|
637
|
-
|
|
687
|
+
const agentPartialText = voice.partial?.speaker === "agent" ? voice.partial.text.trim() : "";
|
|
688
|
+
const userPartialText = voice.partial?.speaker === "user" ? voice.partial.text.trim() : "";
|
|
689
|
+
if (agentPartialText) {
|
|
690
|
+
if (voiceUserPartialIdRef.current) {
|
|
691
|
+
const doneId = voiceUserPartialIdRef.current;
|
|
692
|
+
voiceUserPartialIdRef.current = null;
|
|
693
|
+
setMessages((prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m));
|
|
694
|
+
}
|
|
638
695
|
setMessages((prev) => {
|
|
639
696
|
const last = prev[prev.length - 1];
|
|
640
697
|
if (last?.id.startsWith("voice-") && last.role === "assistant") {
|
|
641
698
|
voiceStreamingIdRef.current = last.id;
|
|
642
|
-
return [...prev.slice(0, -1), { ...last, content:
|
|
699
|
+
return [...prev.slice(0, -1), { ...last, content: agentPartialText, isStreaming: true }];
|
|
643
700
|
}
|
|
644
701
|
const id = voiceStreamingIdRef.current || `voice-partial-${Date.now()}`;
|
|
645
702
|
voiceStreamingIdRef.current = id;
|
|
646
|
-
return [...prev, { id, role: "assistant", content:
|
|
703
|
+
return [...prev, { id, role: "assistant", content: agentPartialText, isStreaming: true, createdAt: /* @__PURE__ */ new Date() }];
|
|
647
704
|
});
|
|
648
|
-
} else if (
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
(prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m)
|
|
653
|
-
);
|
|
654
|
-
}
|
|
655
|
-
}, [voice?.transcript, voice?.partial, voice?.state, threadId]);
|
|
656
|
-
const sendMessage = (0, import_react2.useCallback)(
|
|
657
|
-
async (contentToSend, overrideOptions) => {
|
|
658
|
-
const prompt = (contentToSend ?? input).trim();
|
|
659
|
-
if (!prompt || isStreaming) {
|
|
660
|
-
chatLogger.trace("sendMessage skipped", {
|
|
661
|
-
hasPrompt: !!prompt,
|
|
662
|
-
isStreaming
|
|
663
|
-
});
|
|
664
|
-
return;
|
|
705
|
+
} else if (userPartialText) {
|
|
706
|
+
if (voiceStreamingIdRef.current) {
|
|
707
|
+
const doneId = voiceStreamingIdRef.current;
|
|
708
|
+
voiceStreamingIdRef.current = null;
|
|
709
|
+
setMessages((prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m));
|
|
665
710
|
}
|
|
711
|
+
setMessages((prev) => {
|
|
712
|
+
const last = prev[prev.length - 1];
|
|
713
|
+
if (last?.id.startsWith("voice-") && last.role === "user" && last.isStreaming) {
|
|
714
|
+
voiceUserPartialIdRef.current = last.id;
|
|
715
|
+
return [...prev.slice(0, -1), { ...last, content: userPartialText, isStreaming: true }];
|
|
716
|
+
}
|
|
717
|
+
const id = voiceUserPartialIdRef.current || `voice-partial-user-${Date.now()}`;
|
|
718
|
+
voiceUserPartialIdRef.current = id;
|
|
719
|
+
return [...prev, { id, role: "user", content: userPartialText, isStreaming: true, createdAt: /* @__PURE__ */ new Date() }];
|
|
720
|
+
});
|
|
721
|
+
} else {
|
|
722
|
+
if (voiceStreamingIdRef.current && voice.state !== "speaking") {
|
|
723
|
+
const doneId = voiceStreamingIdRef.current;
|
|
724
|
+
voiceStreamingIdRef.current = null;
|
|
725
|
+
setMessages((prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m));
|
|
726
|
+
}
|
|
727
|
+
if (voiceUserPartialIdRef.current) {
|
|
728
|
+
const doneId = voiceUserPartialIdRef.current;
|
|
729
|
+
voiceUserPartialIdRef.current = null;
|
|
730
|
+
setMessages((prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m));
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}, [voice?.transcript, voice?.partial, voice?.state, effectiveThreadId]);
|
|
734
|
+
const doSend = (0, import_react2.useCallback)(
|
|
735
|
+
async (prompt, baseMessages, overrideOptions) => {
|
|
666
736
|
const targetAgentId = overrideOptions?.agentId || agentId;
|
|
667
737
|
if (!targetAgentId) {
|
|
668
738
|
const err = new Error("No Agent ID specified for useChat.");
|
|
669
739
|
chatLogger.warn("sendMessage no agentId", {});
|
|
670
|
-
chatLogger.error("sendMessage failed \u2014 no agent", {
|
|
671
|
-
error: err.message
|
|
672
|
-
});
|
|
740
|
+
chatLogger.error("sendMessage failed \u2014 no agent", { error: err.message });
|
|
673
741
|
setError(err);
|
|
674
742
|
options.onError?.(err);
|
|
675
|
-
return;
|
|
743
|
+
return false;
|
|
676
744
|
}
|
|
677
745
|
chatLogger.debug("sendMessage start", {
|
|
678
746
|
promptPreview: prompt.slice(0, 100),
|
|
679
747
|
agentId: targetAgentId,
|
|
680
|
-
threadId:
|
|
748
|
+
threadId: effectiveThreadId ?? overrideOptions?.threadId,
|
|
681
749
|
hasResume: !!overrideOptions?.resume,
|
|
682
750
|
hasContextOverride: !!overrideOptions?.contextOverride,
|
|
683
|
-
messageCount:
|
|
751
|
+
messageCount: baseMessages.length
|
|
684
752
|
});
|
|
685
753
|
chatLogger.trace("sendMessage details", {
|
|
686
754
|
agentId: targetAgentId,
|
|
@@ -703,7 +771,7 @@ function useChat(options = {}) {
|
|
|
703
771
|
isStreaming: true,
|
|
704
772
|
toolCalls: []
|
|
705
773
|
};
|
|
706
|
-
const nextMessages = [...
|
|
774
|
+
const nextMessages = [...baseMessages, userMessage];
|
|
707
775
|
setMessages([...nextMessages, placeholderAssistant]);
|
|
708
776
|
setInput("");
|
|
709
777
|
setIsStreaming(true);
|
|
@@ -713,9 +781,7 @@ function useChat(options = {}) {
|
|
|
713
781
|
abortControllerRef.current = controller;
|
|
714
782
|
const finalizeReasoning = () => {
|
|
715
783
|
setMessages(
|
|
716
|
-
(prev) => prev.map(
|
|
717
|
-
(m) => m.role === "reasoning" && m.isStreaming ? { ...m, isStreaming: false } : m
|
|
718
|
-
)
|
|
784
|
+
(prev) => prev.map((m) => m.role === "reasoning" && m.isStreaming ? { ...m, isStreaming: false } : m)
|
|
719
785
|
);
|
|
720
786
|
};
|
|
721
787
|
try {
|
|
@@ -723,7 +789,24 @@ function useChat(options = {}) {
|
|
|
723
789
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
724
790
|
content: m.content
|
|
725
791
|
}));
|
|
726
|
-
|
|
792
|
+
let resolvedThreadId = await overrideOptions?.threadId ?? effectiveThreadId;
|
|
793
|
+
if (!resolvedThreadId) {
|
|
794
|
+
chatLogger.info("minting real thread for ephemeral chat", { agentId: targetAgentId });
|
|
795
|
+
const createRes = await fetchWithAuth("/threads", {
|
|
796
|
+
method: "POST",
|
|
797
|
+
headers: { "Content-Type": "application/json" },
|
|
798
|
+
body: JSON.stringify({ agentId: targetAgentId })
|
|
799
|
+
});
|
|
800
|
+
if (!createRes.ok) throw new Error(`Failed to create thread: ${createRes.statusText}`);
|
|
801
|
+
const createBody = await createRes.json();
|
|
802
|
+
const created = createBody?.data ?? createBody;
|
|
803
|
+
resolvedThreadId = created._id ?? created.id;
|
|
804
|
+
if (!resolvedThreadId) throw new Error("Thread creation returned no id");
|
|
805
|
+
setInternalThreadId(resolvedThreadId);
|
|
806
|
+
loadedThreadIdRef.current = resolvedThreadId;
|
|
807
|
+
options.onThreadCreated?.(resolvedThreadId);
|
|
808
|
+
chatLogger.info("ephemeral chat minted", { threadId: resolvedThreadId });
|
|
809
|
+
}
|
|
727
810
|
chatLogger.trace("resolved threadId", { threadId: resolvedThreadId });
|
|
728
811
|
const token = getAuthToken ? await getAuthToken() : null;
|
|
729
812
|
const resolvedHookContext = typeof options.context === "function" ? options.context() : options.context;
|
|
@@ -752,27 +835,12 @@ function useChat(options = {}) {
|
|
|
752
835
|
signal: controller.signal
|
|
753
836
|
});
|
|
754
837
|
if (!stream.ok) {
|
|
755
|
-
chatLogger.warn("SSE stream not ok", {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
});
|
|
759
|
-
chatLogger.error("chat stream failed", {
|
|
760
|
-
agentId: targetAgentId,
|
|
761
|
-
status: stream.status,
|
|
762
|
-
error: stream.errorText
|
|
763
|
-
});
|
|
764
|
-
throw new Error(
|
|
765
|
-
`Chat error (${stream.status}): ${stream.errorText ?? "Stream failed"}`
|
|
766
|
-
);
|
|
838
|
+
chatLogger.warn("SSE stream not ok", { status: stream.status, errorText: stream.errorText });
|
|
839
|
+
chatLogger.error("chat stream failed", { agentId: targetAgentId, status: stream.status, error: stream.errorText });
|
|
840
|
+
throw new Error(`Chat error (${stream.status}): ${stream.errorText ?? "Stream failed"}`);
|
|
767
841
|
}
|
|
768
|
-
chatLogger.debug("SSE stream opened", {
|
|
769
|
-
|
|
770
|
-
status: stream.status
|
|
771
|
-
});
|
|
772
|
-
chatLogger.info("chat stream started", {
|
|
773
|
-
agentId: targetAgentId,
|
|
774
|
-
threadId: resolvedThreadId
|
|
775
|
-
});
|
|
842
|
+
chatLogger.debug("SSE stream opened", { agentId: targetAgentId, status: stream.status });
|
|
843
|
+
chatLogger.info("chat stream started", { agentId: targetAgentId, threadId: resolvedThreadId });
|
|
776
844
|
const reader = stream.reader;
|
|
777
845
|
let buffer = "";
|
|
778
846
|
let accumulatedText = "";
|
|
@@ -783,23 +851,12 @@ function useChat(options = {}) {
|
|
|
783
851
|
const patchAssistant = (patch) => {
|
|
784
852
|
setMessages(
|
|
785
853
|
(prev) => prev.map(
|
|
786
|
-
(msg) => msg.id === assistantMessageId ? {
|
|
787
|
-
...msg,
|
|
788
|
-
toolCalls: Array.from(toolCallsMap.values()),
|
|
789
|
-
...patch
|
|
790
|
-
} : msg
|
|
854
|
+
(msg) => msg.id === assistantMessageId ? { ...msg, toolCalls: Array.from(toolCallsMap.values()), ...patch } : msg
|
|
791
855
|
)
|
|
792
856
|
);
|
|
793
857
|
};
|
|
794
858
|
const insertReasoningMessage = (id, seq) => {
|
|
795
|
-
const msg = {
|
|
796
|
-
id,
|
|
797
|
-
role: "reasoning",
|
|
798
|
-
content: "",
|
|
799
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
800
|
-
isStreaming: true,
|
|
801
|
-
seq
|
|
802
|
-
};
|
|
859
|
+
const msg = { id, role: "reasoning", content: "", createdAt: /* @__PURE__ */ new Date(), isStreaming: true, seq };
|
|
803
860
|
setMessages((prev) => {
|
|
804
861
|
const idx = prev.findIndex((m) => m.id === assistantMessageId);
|
|
805
862
|
if (idx === -1) return [...prev, msg];
|
|
@@ -824,16 +881,11 @@ function useChat(options = {}) {
|
|
|
824
881
|
chatLogger.trace("stream event", { type: event.type, event });
|
|
825
882
|
options.onEvent?.(event);
|
|
826
883
|
if (event.type === "TEXT_MESSAGE_CHUNK" && event.delta) {
|
|
827
|
-
chatLogger.debug("text chunk", {
|
|
828
|
-
deltaLength: event.delta.length
|
|
829
|
-
});
|
|
884
|
+
chatLogger.debug("text chunk", { deltaLength: event.delta.length });
|
|
830
885
|
accumulatedText += event.delta;
|
|
831
886
|
patchAssistant({ content: accumulatedText, isStreaming: true });
|
|
832
887
|
} else if (event.type === "TOOL_CALL_CHUNK" && event.toolCallId) {
|
|
833
|
-
chatLogger.debug("tool call chunk", {
|
|
834
|
-
toolCallId: event.toolCallId,
|
|
835
|
-
toolCallName: event.toolCallName
|
|
836
|
-
});
|
|
888
|
+
chatLogger.debug("tool call chunk", { toolCallId: event.toolCallId, toolCallName: event.toolCallName });
|
|
837
889
|
const existing = toolCallsMap.get(event.toolCallId);
|
|
838
890
|
if (existing) {
|
|
839
891
|
existing.args = (existing.args || "") + (event.delta || "");
|
|
@@ -847,17 +899,12 @@ function useChat(options = {}) {
|
|
|
847
899
|
}
|
|
848
900
|
patchAssistant({});
|
|
849
901
|
} else if (event.type === "TOOL_CALL_RESULT") {
|
|
850
|
-
chatLogger.debug("tool call result", {
|
|
851
|
-
toolCallId: event.toolCallId
|
|
852
|
-
});
|
|
902
|
+
chatLogger.debug("tool call result", { toolCallId: event.toolCallId });
|
|
853
903
|
const existing = toolCallsMap.get(event.toolCallId);
|
|
854
904
|
if (existing) {
|
|
855
905
|
existing.result = event.content;
|
|
856
906
|
existing.isError = isErrorToolContent(event.content);
|
|
857
|
-
if (existing.isError)
|
|
858
|
-
chatLogger.warn("tool call error", {
|
|
859
|
-
toolCallId: event.toolCallId
|
|
860
|
-
});
|
|
907
|
+
if (existing.isError) chatLogger.warn("tool call error", { toolCallId: event.toolCallId });
|
|
861
908
|
if (existing.toolName === "present_file" && !existing.isError) {
|
|
862
909
|
const presented = parsePresentedFile(event.content);
|
|
863
910
|
if (presented) {
|
|
@@ -872,12 +919,10 @@ function useChat(options = {}) {
|
|
|
872
919
|
fileCount: Object.keys(event.snapshot.files ?? {}).length,
|
|
873
920
|
todoCount: event.snapshot.todos?.length ?? 0
|
|
874
921
|
});
|
|
875
|
-
setFiles(normalizeWorkspaceFiles(event.snapshot.files));
|
|
876
|
-
setTodos(event.snapshot.todos);
|
|
922
|
+
setFiles(normalizeWorkspaceFiles(event.snapshot.files ?? {}));
|
|
923
|
+
setTodos(event.snapshot.todos ?? []);
|
|
877
924
|
} else if (event.type === "REASONING_MESSAGE_START" && event.messageId) {
|
|
878
|
-
chatLogger.debug("reasoning start", {
|
|
879
|
-
messageId: event.messageId
|
|
880
|
-
});
|
|
925
|
+
chatLogger.debug("reasoning start", { messageId: event.messageId });
|
|
881
926
|
activeReasoningId = event.messageId;
|
|
882
927
|
reasoningById.set(event.messageId, { content: "" });
|
|
883
928
|
insertReasoningMessage(event.messageId, streamSeq++);
|
|
@@ -892,61 +937,31 @@ function useChat(options = {}) {
|
|
|
892
937
|
}
|
|
893
938
|
const entry = reasoningById.get(rid);
|
|
894
939
|
entry.content += event.delta;
|
|
895
|
-
chatLogger.trace("reasoning content", {
|
|
896
|
-
|
|
897
|
-
deltaLength: event.delta?.length ?? 0
|
|
898
|
-
});
|
|
899
|
-
setMessages(
|
|
900
|
-
(prev) => prev.map(
|
|
901
|
-
(m) => m.id === rid ? { ...m, content: entry.content, isStreaming: true } : m
|
|
902
|
-
)
|
|
903
|
-
);
|
|
940
|
+
chatLogger.trace("reasoning content", { messageId: rid, deltaLength: event.delta?.length ?? 0 });
|
|
941
|
+
setMessages((prev) => prev.map((m) => m.id === rid ? { ...m, content: entry.content, isStreaming: true } : m));
|
|
904
942
|
} else if (event.type === "REASONING_END") {
|
|
905
|
-
chatLogger.debug("reasoning end", {
|
|
906
|
-
messageId: activeReasoningId
|
|
907
|
-
});
|
|
943
|
+
chatLogger.debug("reasoning end", { messageId: activeReasoningId });
|
|
908
944
|
const rid = activeReasoningId;
|
|
909
945
|
activeReasoningId = null;
|
|
910
946
|
if (rid) {
|
|
911
|
-
setMessages(
|
|
912
|
-
(prev) => prev.map(
|
|
913
|
-
(m) => m.id === rid ? { ...m, isStreaming: false } : m
|
|
914
|
-
)
|
|
915
|
-
);
|
|
947
|
+
setMessages((prev) => prev.map((m) => m.id === rid ? { ...m, isStreaming: false } : m));
|
|
916
948
|
}
|
|
917
949
|
} else if (event.type === "CUSTOM") {
|
|
918
950
|
chatLogger.debug("custom event", { name: event.name });
|
|
919
951
|
if (event.name === "hitl_request") {
|
|
920
952
|
const value2 = event.value;
|
|
921
|
-
chatLogger.info("hitl interrupt", {
|
|
922
|
-
|
|
923
|
-
});
|
|
924
|
-
setInterrupt({
|
|
925
|
-
kind: "hitl",
|
|
926
|
-
actionRequests: value2.actionRequests,
|
|
927
|
-
reviewConfigs: value2.reviewConfigs
|
|
928
|
-
});
|
|
953
|
+
chatLogger.info("hitl interrupt", { actionCount: value2.actionRequests?.length ?? 0 });
|
|
954
|
+
setInterrupt({ kind: "hitl", actionRequests: value2.actionRequests, reviewConfigs: value2.reviewConfigs });
|
|
929
955
|
} else if (event.name === "clarification_request") {
|
|
930
956
|
const value2 = event.value;
|
|
931
|
-
chatLogger.info("clarification interrupt", {
|
|
932
|
-
|
|
933
|
-
});
|
|
934
|
-
setInterrupt({
|
|
935
|
-
kind: "clarification",
|
|
936
|
-
questions: value2.questions
|
|
937
|
-
});
|
|
957
|
+
chatLogger.info("clarification interrupt", { questionCount: value2.questions?.length ?? 0 });
|
|
958
|
+
setInterrupt({ kind: "clarification", questions: value2.questions });
|
|
938
959
|
} else if (event.name === "subagent_activity") {
|
|
939
960
|
const { toolCallId, ...entry } = event.value;
|
|
940
|
-
chatLogger.trace("subagent activity", {
|
|
941
|
-
toolCallId,
|
|
942
|
-
kind: entry.kind
|
|
943
|
-
});
|
|
961
|
+
chatLogger.trace("subagent activity", { toolCallId, kind: entry.kind });
|
|
944
962
|
const existing = toolCallsMap.get(toolCallId);
|
|
945
963
|
if (existing) {
|
|
946
|
-
existing.subagentActivity = [
|
|
947
|
-
...existing.subagentActivity || [],
|
|
948
|
-
entry
|
|
949
|
-
];
|
|
964
|
+
existing.subagentActivity = [...existing.subagentActivity || [], entry];
|
|
950
965
|
patchAssistant({});
|
|
951
966
|
}
|
|
952
967
|
} else if (event.name === "mcp_app") {
|
|
@@ -954,35 +969,21 @@ function useChat(options = {}) {
|
|
|
954
969
|
if (val?.toolCallId && val?.resourceUri && val?.mcpId) {
|
|
955
970
|
const existing = toolCallsMap.get(val.toolCallId);
|
|
956
971
|
if (existing) {
|
|
957
|
-
existing.mcpApp = {
|
|
958
|
-
resourceUri: val.resourceUri,
|
|
959
|
-
mcpId: val.mcpId
|
|
960
|
-
};
|
|
972
|
+
existing.mcpApp = { resourceUri: val.resourceUri, mcpId: val.mcpId };
|
|
961
973
|
patchAssistant({});
|
|
962
974
|
}
|
|
963
975
|
}
|
|
964
976
|
}
|
|
965
977
|
} else if (event.type === "RUN_ERROR") {
|
|
966
|
-
chatLogger.warn("run error", {
|
|
967
|
-
|
|
968
|
-
code: event.code
|
|
969
|
-
});
|
|
970
|
-
chatLogger.error("stream run error", {
|
|
971
|
-
code: event.code,
|
|
972
|
-
message: event.message
|
|
973
|
-
});
|
|
978
|
+
chatLogger.warn("run error", { message: event.message, code: event.code });
|
|
979
|
+
chatLogger.error("stream run error", { code: event.code, message: event.message });
|
|
974
980
|
throw new Error(event.message || "Stream error from agent");
|
|
975
981
|
} else {
|
|
976
982
|
chatLogger.trace("unhandled event", { type: event.type });
|
|
977
983
|
}
|
|
978
984
|
} catch (e) {
|
|
979
|
-
if (e instanceof Error && e.message.startsWith("Stream error"))
|
|
980
|
-
|
|
981
|
-
}
|
|
982
|
-
chatLogger.warn("event parse error", {
|
|
983
|
-
raw: raw.slice(0, 200),
|
|
984
|
-
error: e instanceof Error ? e.message : String(e)
|
|
985
|
-
});
|
|
985
|
+
if (e instanceof Error && e.message.startsWith("Stream error")) throw e;
|
|
986
|
+
chatLogger.warn("event parse error", { raw: raw.slice(0, 200), error: e instanceof Error ? e.message : String(e) });
|
|
986
987
|
}
|
|
987
988
|
}
|
|
988
989
|
}
|
|
@@ -994,75 +995,55 @@ function useChat(options = {}) {
|
|
|
994
995
|
isStreaming: false,
|
|
995
996
|
toolCalls: Array.from(toolCallsMap.values())
|
|
996
997
|
};
|
|
997
|
-
setMessages(
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
)
|
|
1001
|
-
);
|
|
1002
|
-
chatLogger.info("sendMessage succeeded", {
|
|
1003
|
-
agentId: targetAgentId,
|
|
1004
|
-
textLength: accumulatedText.length,
|
|
1005
|
-
toolCallCount: toolCallsMap.size
|
|
1006
|
-
});
|
|
1007
|
-
chatLogger.debug("sendMessage completed", {
|
|
1008
|
-
agentId: targetAgentId,
|
|
1009
|
-
textLength: accumulatedText.length,
|
|
1010
|
-
eventCount: toolCallsMap.size + 1
|
|
1011
|
-
});
|
|
998
|
+
setMessages((prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMessage : msg));
|
|
999
|
+
chatLogger.info("sendMessage succeeded", { agentId: targetAgentId, textLength: accumulatedText.length, toolCallCount: toolCallsMap.size });
|
|
1000
|
+
chatLogger.debug("sendMessage completed", { agentId: targetAgentId, textLength: accumulatedText.length, eventCount: toolCallsMap.size + 1 });
|
|
1012
1001
|
options.onFinish?.(finalMessage);
|
|
1002
|
+
return true;
|
|
1013
1003
|
} catch (err) {
|
|
1014
1004
|
if (controller.signal.aborted) {
|
|
1015
1005
|
chatLogger.info("sendMessage aborted", { agentId: targetAgentId });
|
|
1016
1006
|
chatLogger.debug("stream aborted", { agentId: targetAgentId });
|
|
1017
|
-
setMessages(
|
|
1018
|
-
|
|
1019
|
-
(msg) => msg.id === assistantMessageId ? { ...msg, isStreaming: false } : msg
|
|
1020
|
-
)
|
|
1021
|
-
);
|
|
1022
|
-
return;
|
|
1007
|
+
setMessages((prev) => prev.map((msg) => msg.id === assistantMessageId ? { ...msg, isStreaming: false } : msg));
|
|
1008
|
+
return false;
|
|
1023
1009
|
}
|
|
1024
1010
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
1025
|
-
chatLogger.warn("sendMessage failed", {
|
|
1026
|
-
|
|
1027
|
-
error: errorObj.message
|
|
1028
|
-
});
|
|
1029
|
-
chatLogger.error("sendMessage error", {
|
|
1030
|
-
agentId: targetAgentId,
|
|
1031
|
-
error: errorObj.message
|
|
1032
|
-
});
|
|
1011
|
+
chatLogger.warn("sendMessage failed", { agentId: targetAgentId, error: errorObj.message });
|
|
1012
|
+
chatLogger.error("sendMessage error", { agentId: targetAgentId, error: errorObj.message });
|
|
1033
1013
|
setError(errorObj);
|
|
1034
1014
|
options.onError?.(errorObj);
|
|
1035
1015
|
setMessages(
|
|
1036
1016
|
(prev) => prev.map(
|
|
1037
|
-
(msg) => msg.id === assistantMessageId ? {
|
|
1038
|
-
...msg,
|
|
1039
|
-
content: msg.content || `\u26A0\uFE0F Error: ${errorObj.message || "Failed to get response."}`,
|
|
1040
|
-
isStreaming: false
|
|
1041
|
-
} : msg
|
|
1017
|
+
(msg) => msg.id === assistantMessageId ? { ...msg, content: msg.content || `\u26A0\uFE0F Error: ${errorObj.message || "Failed to get response."}`, isStreaming: false } : msg
|
|
1042
1018
|
)
|
|
1043
1019
|
);
|
|
1020
|
+
return false;
|
|
1044
1021
|
} finally {
|
|
1045
1022
|
setIsStreaming(false);
|
|
1046
1023
|
abortControllerRef.current = null;
|
|
1047
1024
|
finalizeReasoning();
|
|
1048
|
-
chatLogger.debug("sendMessage finally", {
|
|
1049
|
-
agentId: targetAgentId,
|
|
1050
|
-
isStreaming: false
|
|
1051
|
-
});
|
|
1025
|
+
chatLogger.debug("sendMessage finally", { agentId: targetAgentId, isStreaming: false });
|
|
1052
1026
|
chatLogger.trace("sendMessage end", { agentId: targetAgentId });
|
|
1053
1027
|
}
|
|
1054
1028
|
},
|
|
1055
|
-
[
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
input
|
|
1060
|
-
isStreaming
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1029
|
+
[agentId, baseUrl, getAuthToken, fetchWithAuth, options, chatLogger, effectiveThreadId]
|
|
1030
|
+
);
|
|
1031
|
+
const sendMessage = (0, import_react2.useCallback)(
|
|
1032
|
+
async (contentToSend, overrideOptions) => {
|
|
1033
|
+
const prompt = (contentToSend ?? input).trim();
|
|
1034
|
+
if (!prompt || isStreaming) {
|
|
1035
|
+
chatLogger.trace("sendMessage skipped", {
|
|
1036
|
+
hasPrompt: !!prompt,
|
|
1037
|
+
isStreaming
|
|
1038
|
+
});
|
|
1039
|
+
if (isStreaming) {
|
|
1040
|
+
chatLogger.warn("sendMessage dropped while streaming \u2014 caller should queue or disable send", {});
|
|
1041
|
+
}
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
return doSend(prompt, messages, overrideOptions);
|
|
1045
|
+
},
|
|
1046
|
+
[input, isStreaming, messages, doSend, chatLogger]
|
|
1066
1047
|
);
|
|
1067
1048
|
const handleInputChange = (0, import_react2.useCallback)(
|
|
1068
1049
|
(e) => {
|
|
@@ -1079,13 +1060,13 @@ function useChat(options = {}) {
|
|
|
1079
1060
|
},
|
|
1080
1061
|
[sendMessage, chatLogger]
|
|
1081
1062
|
);
|
|
1082
|
-
const reload = (0, import_react2.useCallback)(() => {
|
|
1063
|
+
const reload = (0, import_react2.useCallback)(async () => {
|
|
1083
1064
|
if (messages.length === 0 || isStreaming) {
|
|
1084
1065
|
chatLogger.trace("reload skipped", {
|
|
1085
1066
|
messageCount: messages.length,
|
|
1086
1067
|
isStreaming
|
|
1087
1068
|
});
|
|
1088
|
-
return;
|
|
1069
|
+
return false;
|
|
1089
1070
|
}
|
|
1090
1071
|
let lastUserIndex = -1;
|
|
1091
1072
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -1096,16 +1077,16 @@ function useChat(options = {}) {
|
|
|
1096
1077
|
}
|
|
1097
1078
|
if (lastUserIndex === -1) {
|
|
1098
1079
|
chatLogger.warn("reload no user message", {});
|
|
1099
|
-
return;
|
|
1080
|
+
return false;
|
|
1100
1081
|
}
|
|
1101
1082
|
const lastUserMessage = messages[lastUserIndex];
|
|
1083
|
+
const truncated = messages.slice(0, lastUserIndex);
|
|
1102
1084
|
chatLogger.info("reload", { messageId: lastUserMessage.id });
|
|
1103
1085
|
chatLogger.debug("reload last user", {
|
|
1104
1086
|
preview: lastUserMessage.content.slice(0, 100)
|
|
1105
1087
|
});
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
}, [isStreaming, messages, sendMessage, chatLogger]);
|
|
1088
|
+
return doSend(lastUserMessage.content, truncated);
|
|
1089
|
+
}, [isStreaming, messages, doSend, chatLogger]);
|
|
1109
1090
|
const resumeInterrupt = (0, import_react2.useCallback)(
|
|
1110
1091
|
(resume, displayContent) => {
|
|
1111
1092
|
chatLogger.info("resumeInterrupt", {
|
|
@@ -1153,6 +1134,9 @@ function useChat(options = {}) {
|
|
|
1153
1134
|
stop,
|
|
1154
1135
|
reload,
|
|
1155
1136
|
clear,
|
|
1137
|
+
startNewChat,
|
|
1138
|
+
currentThreadId: effectiveThreadId,
|
|
1139
|
+
isEphemeral: !effectiveThreadId,
|
|
1156
1140
|
setMessages,
|
|
1157
1141
|
loadThreadMessages
|
|
1158
1142
|
};
|
|
@@ -2115,7 +2099,7 @@ function useMcp(options = {}) {
|
|
|
2115
2099
|
|
|
2116
2100
|
// src/index.ts
|
|
2117
2101
|
var import_logger2 = require("@personaai/logger");
|
|
2118
|
-
var VERSION = "0.
|
|
2102
|
+
var VERSION = "0.8.0";
|
|
2119
2103
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2120
2104
|
0 && (module.exports = {
|
|
2121
2105
|
PersonaProvider,
|