@personaai/react 0.7.2 → 0.7.4
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 +31 -0
- package/dist/index.cjs +76 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +76 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -258,6 +258,16 @@ interface UseChatOptions {
|
|
|
258
258
|
onError?: (error: Error) => void;
|
|
259
259
|
/** Hook for receiving every low-level AG-UI streaming event (tool calls, steps, subagents) */
|
|
260
260
|
onEvent?: (event: PersonaStreamingEvent) => void;
|
|
261
|
+
/**
|
|
262
|
+
* Pass the object returned by `useVoice()` (sharing the same `threadId`) to have `useChat`
|
|
263
|
+
* merge live voice turns into `messages` automatically — one bubble per utterance, deduped
|
|
264
|
+
* against thread history and against a voice turn that already persisted back into this
|
|
265
|
+
* thread, with the in-progress agent line updated in place while it's still being spoken.
|
|
266
|
+
* No manual sync effects needed in the host app; injected messages get a `voice-` prefixed id.
|
|
267
|
+
* Only applied while the passed hook's `state` is active (not `idle`/`ended`/`error`); the
|
|
268
|
+
* host app still owns starting/stopping the call itself (e.g. `voice.start()`/`voice.stop()`).
|
|
269
|
+
*/
|
|
270
|
+
voice?: UseVoiceResult;
|
|
261
271
|
}
|
|
262
272
|
interface SendMessageOverride {
|
|
263
273
|
agentId?: string;
|
|
@@ -574,6 +584,6 @@ declare function supportsStreamingFetch(): boolean;
|
|
|
574
584
|
*/
|
|
575
585
|
declare function openSSEStream(opts: OpenSSEOptions): Promise<SSEStream>;
|
|
576
586
|
|
|
577
|
-
declare const VERSION = "0.7.
|
|
587
|
+
declare const VERSION = "0.7.3";
|
|
578
588
|
|
|
579
589
|
export { type OpenSSEOptions, type PersonaAgentSummary, type PersonaClarificationQuestion, type PersonaFileItem, type PersonaHealthInfo, type PersonaHitlActionRequest, type PersonaInterrupt, type PersonaMcpConnection, type PersonaMemoryAgentGroup, type PersonaMemoryFile, type PersonaMemoryList, type PersonaMessage, type PersonaPresentedFile, PersonaProvider, type PersonaProviderProps, type PersonaResumeValue, type PersonaRole, type PersonaStreamingEvent, type PersonaSubagentActivityEntry, type PersonaThread, type PersonaTodo, type PersonaToolCall, type PersonaVoiceEndReason, type PersonaVoiceState, type PersonaVoiceToolCall, type PersonaVoiceTranscriptLine, type PersonaWorkspaceFile, type SSEReader, type SSEStream, type SendMessageOverride, type UseChatOptions, type UseMcpConnectionsOptions, type UseVoiceOptions, type UseVoiceResult, VERSION, openSSEStream, supportsStreamingFetch, useAgents, useChat, useConnection, useFiles, useMcpConnections, useMemory, usePersonaContext, useThreads, useVoice };
|
package/dist/index.d.ts
CHANGED
|
@@ -258,6 +258,16 @@ interface UseChatOptions {
|
|
|
258
258
|
onError?: (error: Error) => void;
|
|
259
259
|
/** Hook for receiving every low-level AG-UI streaming event (tool calls, steps, subagents) */
|
|
260
260
|
onEvent?: (event: PersonaStreamingEvent) => void;
|
|
261
|
+
/**
|
|
262
|
+
* Pass the object returned by `useVoice()` (sharing the same `threadId`) to have `useChat`
|
|
263
|
+
* merge live voice turns into `messages` automatically — one bubble per utterance, deduped
|
|
264
|
+
* against thread history and against a voice turn that already persisted back into this
|
|
265
|
+
* thread, with the in-progress agent line updated in place while it's still being spoken.
|
|
266
|
+
* No manual sync effects needed in the host app; injected messages get a `voice-` prefixed id.
|
|
267
|
+
* Only applied while the passed hook's `state` is active (not `idle`/`ended`/`error`); the
|
|
268
|
+
* host app still owns starting/stopping the call itself (e.g. `voice.start()`/`voice.stop()`).
|
|
269
|
+
*/
|
|
270
|
+
voice?: UseVoiceResult;
|
|
261
271
|
}
|
|
262
272
|
interface SendMessageOverride {
|
|
263
273
|
agentId?: string;
|
|
@@ -574,6 +584,6 @@ declare function supportsStreamingFetch(): boolean;
|
|
|
574
584
|
*/
|
|
575
585
|
declare function openSSEStream(opts: OpenSSEOptions): Promise<SSEStream>;
|
|
576
586
|
|
|
577
|
-
declare const VERSION = "0.7.
|
|
587
|
+
declare const VERSION = "0.7.3";
|
|
578
588
|
|
|
579
589
|
export { type OpenSSEOptions, type PersonaAgentSummary, type PersonaClarificationQuestion, type PersonaFileItem, type PersonaHealthInfo, type PersonaHitlActionRequest, type PersonaInterrupt, type PersonaMcpConnection, type PersonaMemoryAgentGroup, type PersonaMemoryFile, type PersonaMemoryList, type PersonaMessage, type PersonaPresentedFile, PersonaProvider, type PersonaProviderProps, type PersonaResumeValue, type PersonaRole, type PersonaStreamingEvent, type PersonaSubagentActivityEntry, type PersonaThread, type PersonaTodo, type PersonaToolCall, type PersonaVoiceEndReason, type PersonaVoiceState, type PersonaVoiceToolCall, type PersonaVoiceTranscriptLine, type PersonaWorkspaceFile, type SSEReader, type SSEStream, type SendMessageOverride, type UseChatOptions, type UseMcpConnectionsOptions, type UseVoiceOptions, type UseVoiceResult, VERSION, openSSEStream, supportsStreamingFetch, useAgents, useChat, useConnection, useFiles, useMcpConnections, useMemory, usePersonaContext, useThreads, useVoice };
|
package/dist/index.js
CHANGED
|
@@ -392,6 +392,9 @@ function useChat(options = {}) {
|
|
|
392
392
|
const [presentedFile, setPresentedFile] = useState(null);
|
|
393
393
|
const abortControllerRef = useRef(null);
|
|
394
394
|
const loadedThreadIdRef = useRef(void 0);
|
|
395
|
+
const voiceThreadRef = useRef(threadId);
|
|
396
|
+
const voicePrevLenRef = useRef(0);
|
|
397
|
+
const voiceStreamingIdRef = useRef(null);
|
|
395
398
|
const stop = useCallback(() => {
|
|
396
399
|
if (abortControllerRef.current) {
|
|
397
400
|
chatLogger.info("stop streaming", {});
|
|
@@ -497,6 +500,75 @@ function useChat(options = {}) {
|
|
|
497
500
|
chatLogger.debug("loadThreadMessages trigger", { threadId });
|
|
498
501
|
void loadThreadMessages(threadId);
|
|
499
502
|
}, [threadId, isStreaming, messages.length, loadThreadMessages, chatLogger]);
|
|
503
|
+
const voice = options.voice;
|
|
504
|
+
useEffect(() => {
|
|
505
|
+
if (!voice) return;
|
|
506
|
+
if (voiceThreadRef.current !== threadId) {
|
|
507
|
+
voiceThreadRef.current = threadId;
|
|
508
|
+
voicePrevLenRef.current = voice.transcript.length;
|
|
509
|
+
voiceStreamingIdRef.current = null;
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const isVoiceActive = voice.state !== "idle" && voice.state !== "ended" && voice.state !== "error";
|
|
513
|
+
const curLen = voice.transcript.length;
|
|
514
|
+
if (curLen < voicePrevLenRef.current) {
|
|
515
|
+
voicePrevLenRef.current = 0;
|
|
516
|
+
voiceStreamingIdRef.current = null;
|
|
517
|
+
}
|
|
518
|
+
if (!isVoiceActive) {
|
|
519
|
+
voicePrevLenRef.current = voice.transcript.length;
|
|
520
|
+
if (voiceStreamingIdRef.current) {
|
|
521
|
+
const doneId = voiceStreamingIdRef.current;
|
|
522
|
+
voiceStreamingIdRef.current = null;
|
|
523
|
+
setMessages(
|
|
524
|
+
(prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m)
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (curLen > voicePrevLenRef.current) {
|
|
530
|
+
const newLines = voice.transcript.slice(voicePrevLenRef.current);
|
|
531
|
+
voicePrevLenRef.current = curLen;
|
|
532
|
+
for (const line of newLines) {
|
|
533
|
+
const text = (line.text || "").trim();
|
|
534
|
+
if (!text) continue;
|
|
535
|
+
const role = line.speaker === "user" ? "user" : "assistant";
|
|
536
|
+
const voiceId = `voice-${line.id}`;
|
|
537
|
+
setMessages((prev) => {
|
|
538
|
+
if (prev.some((m) => m.id === voiceId)) return prev;
|
|
539
|
+
if (prev.some(
|
|
540
|
+
(m) => !m.id.startsWith("voice-") && m.role === role && m.content.trim() === text
|
|
541
|
+
))
|
|
542
|
+
return prev;
|
|
543
|
+
const last = prev[prev.length - 1];
|
|
544
|
+
if (last?.id.startsWith("voice-") && last.role === "assistant" && role === "assistant") {
|
|
545
|
+
const merged = text.startsWith(last.content) ? text : `${last.content} ${text}`.trim();
|
|
546
|
+
return [...prev.slice(0, -1), { ...last, content: merged, isStreaming: false }];
|
|
547
|
+
}
|
|
548
|
+
return [...prev, { id: voiceId, role, content: text, createdAt: /* @__PURE__ */ new Date() }];
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const partialText = voice.partial?.speaker === "agent" ? voice.partial.text.trim() : "";
|
|
553
|
+
if (partialText) {
|
|
554
|
+
setMessages((prev) => {
|
|
555
|
+
const last = prev[prev.length - 1];
|
|
556
|
+
if (last?.id.startsWith("voice-") && last.role === "assistant") {
|
|
557
|
+
voiceStreamingIdRef.current = last.id;
|
|
558
|
+
return [...prev.slice(0, -1), { ...last, content: partialText, isStreaming: true }];
|
|
559
|
+
}
|
|
560
|
+
const id = voiceStreamingIdRef.current || `voice-partial-${Date.now()}`;
|
|
561
|
+
voiceStreamingIdRef.current = id;
|
|
562
|
+
return [...prev, { id, role: "assistant", content: partialText, isStreaming: true, createdAt: /* @__PURE__ */ new Date() }];
|
|
563
|
+
});
|
|
564
|
+
} else if (voice.state !== "speaking" && voiceStreamingIdRef.current) {
|
|
565
|
+
const doneId = voiceStreamingIdRef.current;
|
|
566
|
+
voiceStreamingIdRef.current = null;
|
|
567
|
+
setMessages(
|
|
568
|
+
(prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m)
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
}, [voice?.transcript, voice?.partial, voice?.state, threadId]);
|
|
500
572
|
const sendMessage = useCallback(
|
|
501
573
|
async (contentToSend, overrideOptions) => {
|
|
502
574
|
const prompt = (contentToSend ?? input).trim();
|
|
@@ -1439,10 +1511,11 @@ function useVoice(options = {}) {
|
|
|
1439
1511
|
}
|
|
1440
1512
|
setPartial((prev) => prev?.speaker === speaker ? null : prev);
|
|
1441
1513
|
const last = lastFinalTurnRef.current;
|
|
1442
|
-
const
|
|
1514
|
+
const sameUserBurst = speaker === "user" && last && last.speaker === speaker && last.turnSeq === turnSeq;
|
|
1443
1515
|
setTranscript((prev) => {
|
|
1444
1516
|
const tail = prev[prev.length - 1];
|
|
1445
|
-
|
|
1517
|
+
const agentContinues = speaker === "agent" && tail?.speaker === "agent";
|
|
1518
|
+
if (agentContinues || sameUserBurst && tail?.speaker === "user") {
|
|
1446
1519
|
const merged = mergeTranscriptText(tail.text, text);
|
|
1447
1520
|
if (merged === tail.text) return prev;
|
|
1448
1521
|
return prev.map(
|
|
@@ -1888,7 +1961,7 @@ import {
|
|
|
1888
1961
|
getLogLevel,
|
|
1889
1962
|
isLevelEnabled
|
|
1890
1963
|
} from "@personaai/logger";
|
|
1891
|
-
var VERSION = "0.7.
|
|
1964
|
+
var VERSION = "0.7.3";
|
|
1892
1965
|
export {
|
|
1893
1966
|
PersonaProvider,
|
|
1894
1967
|
VERSION,
|