@personaai/react 0.7.10 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -10
- package/dist/index.cjs +206 -216
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -4
- package/dist/index.d.ts +14 -4
- package/dist/index.js +206 -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
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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));
|
|
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));
|
|
665
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,27 @@ 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
|
}
|
|
977
|
+
} else if (event.type === "title") {
|
|
978
|
+
const title = event.title;
|
|
979
|
+
if (typeof title === "string" && title.trim()) {
|
|
980
|
+
chatLogger.info("auto title", { title });
|
|
981
|
+
options.onTitle?.(title);
|
|
982
|
+
}
|
|
965
983
|
} 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
|
-
});
|
|
984
|
+
chatLogger.warn("run error", { message: event.message, code: event.code });
|
|
985
|
+
chatLogger.error("stream run error", { code: event.code, message: event.message });
|
|
974
986
|
throw new Error(event.message || "Stream error from agent");
|
|
975
987
|
} else {
|
|
976
988
|
chatLogger.trace("unhandled event", { type: event.type });
|
|
977
989
|
}
|
|
978
990
|
} 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
|
-
});
|
|
991
|
+
if (e instanceof Error && e.message.startsWith("Stream error")) throw e;
|
|
992
|
+
chatLogger.warn("event parse error", { raw: raw.slice(0, 200), error: e instanceof Error ? e.message : String(e) });
|
|
986
993
|
}
|
|
987
994
|
}
|
|
988
995
|
}
|
|
@@ -994,75 +1001,55 @@ function useChat(options = {}) {
|
|
|
994
1001
|
isStreaming: false,
|
|
995
1002
|
toolCalls: Array.from(toolCallsMap.values())
|
|
996
1003
|
};
|
|
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
|
-
});
|
|
1004
|
+
setMessages((prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMessage : msg));
|
|
1005
|
+
chatLogger.info("sendMessage succeeded", { agentId: targetAgentId, textLength: accumulatedText.length, toolCallCount: toolCallsMap.size });
|
|
1006
|
+
chatLogger.debug("sendMessage completed", { agentId: targetAgentId, textLength: accumulatedText.length, eventCount: toolCallsMap.size + 1 });
|
|
1012
1007
|
options.onFinish?.(finalMessage);
|
|
1008
|
+
return true;
|
|
1013
1009
|
} catch (err) {
|
|
1014
1010
|
if (controller.signal.aborted) {
|
|
1015
1011
|
chatLogger.info("sendMessage aborted", { agentId: targetAgentId });
|
|
1016
1012
|
chatLogger.debug("stream aborted", { agentId: targetAgentId });
|
|
1017
|
-
setMessages(
|
|
1018
|
-
|
|
1019
|
-
(msg) => msg.id === assistantMessageId ? { ...msg, isStreaming: false } : msg
|
|
1020
|
-
)
|
|
1021
|
-
);
|
|
1022
|
-
return;
|
|
1013
|
+
setMessages((prev) => prev.map((msg) => msg.id === assistantMessageId ? { ...msg, isStreaming: false } : msg));
|
|
1014
|
+
return false;
|
|
1023
1015
|
}
|
|
1024
1016
|
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
|
-
});
|
|
1017
|
+
chatLogger.warn("sendMessage failed", { agentId: targetAgentId, error: errorObj.message });
|
|
1018
|
+
chatLogger.error("sendMessage error", { agentId: targetAgentId, error: errorObj.message });
|
|
1033
1019
|
setError(errorObj);
|
|
1034
1020
|
options.onError?.(errorObj);
|
|
1035
1021
|
setMessages(
|
|
1036
1022
|
(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
|
|
1023
|
+
(msg) => msg.id === assistantMessageId ? { ...msg, content: msg.content || `\u26A0\uFE0F Error: ${errorObj.message || "Failed to get response."}`, isStreaming: false } : msg
|
|
1042
1024
|
)
|
|
1043
1025
|
);
|
|
1026
|
+
return false;
|
|
1044
1027
|
} finally {
|
|
1045
1028
|
setIsStreaming(false);
|
|
1046
1029
|
abortControllerRef.current = null;
|
|
1047
1030
|
finalizeReasoning();
|
|
1048
|
-
chatLogger.debug("sendMessage finally", {
|
|
1049
|
-
agentId: targetAgentId,
|
|
1050
|
-
isStreaming: false
|
|
1051
|
-
});
|
|
1031
|
+
chatLogger.debug("sendMessage finally", { agentId: targetAgentId, isStreaming: false });
|
|
1052
1032
|
chatLogger.trace("sendMessage end", { agentId: targetAgentId });
|
|
1053
1033
|
}
|
|
1054
1034
|
},
|
|
1055
|
-
[
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
input
|
|
1060
|
-
isStreaming
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1035
|
+
[agentId, baseUrl, getAuthToken, fetchWithAuth, options, chatLogger, effectiveThreadId]
|
|
1036
|
+
);
|
|
1037
|
+
const sendMessage = (0, import_react2.useCallback)(
|
|
1038
|
+
async (contentToSend, overrideOptions) => {
|
|
1039
|
+
const prompt = (contentToSend ?? input).trim();
|
|
1040
|
+
if (!prompt || isStreaming) {
|
|
1041
|
+
chatLogger.trace("sendMessage skipped", {
|
|
1042
|
+
hasPrompt: !!prompt,
|
|
1043
|
+
isStreaming
|
|
1044
|
+
});
|
|
1045
|
+
if (isStreaming) {
|
|
1046
|
+
chatLogger.warn("sendMessage dropped while streaming \u2014 caller should queue or disable send", {});
|
|
1047
|
+
}
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
return doSend(prompt, messages, overrideOptions);
|
|
1051
|
+
},
|
|
1052
|
+
[input, isStreaming, messages, doSend, chatLogger]
|
|
1066
1053
|
);
|
|
1067
1054
|
const handleInputChange = (0, import_react2.useCallback)(
|
|
1068
1055
|
(e) => {
|
|
@@ -1079,13 +1066,13 @@ function useChat(options = {}) {
|
|
|
1079
1066
|
},
|
|
1080
1067
|
[sendMessage, chatLogger]
|
|
1081
1068
|
);
|
|
1082
|
-
const reload = (0, import_react2.useCallback)(() => {
|
|
1069
|
+
const reload = (0, import_react2.useCallback)(async () => {
|
|
1083
1070
|
if (messages.length === 0 || isStreaming) {
|
|
1084
1071
|
chatLogger.trace("reload skipped", {
|
|
1085
1072
|
messageCount: messages.length,
|
|
1086
1073
|
isStreaming
|
|
1087
1074
|
});
|
|
1088
|
-
return;
|
|
1075
|
+
return false;
|
|
1089
1076
|
}
|
|
1090
1077
|
let lastUserIndex = -1;
|
|
1091
1078
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -1096,16 +1083,16 @@ function useChat(options = {}) {
|
|
|
1096
1083
|
}
|
|
1097
1084
|
if (lastUserIndex === -1) {
|
|
1098
1085
|
chatLogger.warn("reload no user message", {});
|
|
1099
|
-
return;
|
|
1086
|
+
return false;
|
|
1100
1087
|
}
|
|
1101
1088
|
const lastUserMessage = messages[lastUserIndex];
|
|
1089
|
+
const truncated = messages.slice(0, lastUserIndex);
|
|
1102
1090
|
chatLogger.info("reload", { messageId: lastUserMessage.id });
|
|
1103
1091
|
chatLogger.debug("reload last user", {
|
|
1104
1092
|
preview: lastUserMessage.content.slice(0, 100)
|
|
1105
1093
|
});
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
}, [isStreaming, messages, sendMessage, chatLogger]);
|
|
1094
|
+
return doSend(lastUserMessage.content, truncated);
|
|
1095
|
+
}, [isStreaming, messages, doSend, chatLogger]);
|
|
1109
1096
|
const resumeInterrupt = (0, import_react2.useCallback)(
|
|
1110
1097
|
(resume, displayContent) => {
|
|
1111
1098
|
chatLogger.info("resumeInterrupt", {
|
|
@@ -1153,6 +1140,9 @@ function useChat(options = {}) {
|
|
|
1153
1140
|
stop,
|
|
1154
1141
|
reload,
|
|
1155
1142
|
clear,
|
|
1143
|
+
startNewChat,
|
|
1144
|
+
currentThreadId: effectiveThreadId,
|
|
1145
|
+
isEphemeral: !effectiveThreadId,
|
|
1156
1146
|
setMessages,
|
|
1157
1147
|
loadThreadMessages
|
|
1158
1148
|
};
|
|
@@ -2115,7 +2105,7 @@ function useMcp(options = {}) {
|
|
|
2115
2105
|
|
|
2116
2106
|
// src/index.ts
|
|
2117
2107
|
var import_logger2 = require("@personaai/logger");
|
|
2118
|
-
var VERSION = "0.
|
|
2108
|
+
var VERSION = "0.8.1";
|
|
2119
2109
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2120
2110
|
0 && (module.exports = {
|
|
2121
2111
|
PersonaProvider,
|