@blocksdiy/react-common 1.43.1 → 1.45.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/components/new-agent-chat.d.ts +44 -2
- package/dist/components/new-agent-chat.d.ts.map +1 -1
- package/dist/components/new-agent-chat.js +92 -10
- package/dist/hooks/agent-thread-live-update.d.ts +16 -1
- package/dist/hooks/agent-thread-live-update.d.ts.map +1 -1
- package/dist/hooks/agent-thread-live-update.js +7 -1
- package/dist/utils/message-run-ids.d.ts +26 -0
- package/dist/utils/message-run-ids.d.ts.map +1 -0
- package/dist/utils/message-run-ids.js +65 -0
- package/package.json +2 -2
|
@@ -2,7 +2,9 @@ import { type Message, ToolMessage, type UserMessage } from "@ag-ui/client";
|
|
|
2
2
|
import { useRenderToolCall } from "@copilotkit/react-core/v2";
|
|
3
3
|
import { ReactNode, SetStateAction } from "react";
|
|
4
4
|
import { type RenderProp } from "../utils/compose-render.js";
|
|
5
|
+
import { type MessageRunIds } from "../utils/message-run-ids.js";
|
|
5
6
|
export { useTextGrowthAnimating } from "../hooks/text-growth-animation.js";
|
|
7
|
+
export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun, type MessageRunIds } from "../utils/message-run-ids.js";
|
|
6
8
|
export { type Message, type AssistantMessage, type UserMessage } from "@ag-ui/client";
|
|
7
9
|
export { useRenderTool, useDefaultRenderTool, useHumanInTheLoop, useFrontendTool, ToolCallStatus, } from "@copilotkit/react-core/v2";
|
|
8
10
|
export interface Attachment {
|
|
@@ -58,10 +60,29 @@ export interface AgentChatContextValue {
|
|
|
58
60
|
role: "assistant" | "user";
|
|
59
61
|
}[];
|
|
60
62
|
messageById: Map<string, Message>;
|
|
63
|
+
/**
|
|
64
|
+
* The agent run (turn) that produced each replayed message — the `agentRunId` the rows
|
|
65
|
+
* it wrote carry. Filled from the connect replay; messages from before run attribution
|
|
66
|
+
* (and live-streamed ones) have no entry. Optional so contexts a host builds by hand
|
|
67
|
+
* (previews, stubs) need not state it; absent reads as "no run known".
|
|
68
|
+
*/
|
|
69
|
+
runIdByMessageId?: MessageRunIds;
|
|
61
70
|
toolMessages: ToolMessage[];
|
|
62
71
|
renderToolCall: ReturnType<typeof useRenderToolCall>;
|
|
63
72
|
sendMessage: (message: Omit<UserMessage, "id" | "role">) => Promise<void>;
|
|
64
73
|
sendFromInputs: () => Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Replay this conversation's transcript from the server, in place.
|
|
76
|
+
*
|
|
77
|
+
* The same round-trip a fresh mount does — the connect endpoint reads the checkpoint and
|
|
78
|
+
* answers with a MESSAGES_SNAPSHOT, which replaces the mirrored list. For turns that ran
|
|
79
|
+
* SERVER-SIDE, which no client streamed and so no client has: without this they appear only
|
|
80
|
+
* after a reload or a conversation switch.
|
|
81
|
+
*
|
|
82
|
+
* A no-op while a run is in flight: that run's own stream is the authoritative view of the
|
|
83
|
+
* conversation, and replacing the list underneath it would fight the events still arriving.
|
|
84
|
+
*/
|
|
85
|
+
reloadMessages: () => Promise<void>;
|
|
65
86
|
/** Resumes a run parked on a server-side interrupt. Rejects if the resume fails. */
|
|
66
87
|
/** Resolves false when a run was already in flight, so nothing was sent. */
|
|
67
88
|
resolveInterrupt: (resume: unknown) => Promise<boolean>;
|
|
@@ -151,8 +172,29 @@ export interface AgentChatMessageGroupProps {
|
|
|
151
172
|
export declare function AgentChatMessageGroup({ messageGroup, ...props }: React.ComponentProps<"div"> & AgentChatMessageGroupProps): import("react").JSX.Element;
|
|
152
173
|
export interface AgentChatMessagesProps {
|
|
153
174
|
scrollAreaClassName?: string;
|
|
154
|
-
|
|
155
|
-
|
|
175
|
+
/**
|
|
176
|
+
* An agent run (turn) to open the conversation at — the `agentRunId` a row the agent
|
|
177
|
+
* wrote carries. Once the transcript has replayed, the first message group of that run
|
|
178
|
+
* is scrolled into view and the run's messages are marked highlighted for
|
|
179
|
+
* `focusHighlightMs` (read it with `useAgentChatRunFocus`). Happens once per
|
|
180
|
+
* conversation; a no-op when the run is not in this conversation (an older thread,
|
|
181
|
+
* another conversation's run).
|
|
182
|
+
*/
|
|
183
|
+
focusRunId?: string;
|
|
184
|
+
/** How long a focused run stays highlighted; defaults to 2400ms. */
|
|
185
|
+
focusHighlightMs?: number;
|
|
186
|
+
}
|
|
187
|
+
export interface AgentChatRunFocusValue {
|
|
188
|
+
/** The run being focused (the `focusRunId` passed to AgentChatMessages), if any. */
|
|
189
|
+
runId?: string;
|
|
190
|
+
/** Ids of the messages that run produced, in transcript order. */
|
|
191
|
+
messageIds: ReadonlySet<string>;
|
|
192
|
+
/** True for a moment after the run is scrolled into view — drive the highlight off it. */
|
|
193
|
+
isHighlighted: boolean;
|
|
194
|
+
}
|
|
195
|
+
/** The run focus of the enclosing AgentChatMessages (see its `focusRunId`). */
|
|
196
|
+
export declare const useAgentChatRunFocus: () => AgentChatRunFocusValue;
|
|
197
|
+
export declare function AgentChatMessages({ scrollAreaClassName, focusRunId, focusHighlightMs, ...props }: React.ComponentProps<"div"> & AgentChatMessagesProps): import("react").JSX.Element;
|
|
156
198
|
export interface AgentChatThinkingProps {
|
|
157
199
|
asChild?: boolean;
|
|
158
200
|
render?: RenderProp;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAI5E,OAAO,EAAiB,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAEL,SAAS,EACT,cAAc,EAQf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,9 @@ import { useCopilotKit, useRenderToolCall } from "@copilotkit/react-core/v2";
|
|
|
6
6
|
import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
7
7
|
import { useStickToBottom } from "use-stick-to-bottom";
|
|
8
8
|
import { ComposeRender } from "../utils/compose-render.js";
|
|
9
|
+
import { EMPTY_MESSAGE_RUN_IDS, findMessageGroupElement, mergeMessageRunIds, MESSAGE_IDS_ATTRIBUTE, messageIdsOfRun, readMessageRunIdsEvent, } from "../utils/message-run-ids.js";
|
|
9
10
|
export { useTextGrowthAnimating } from "../hooks/text-growth-animation.js";
|
|
11
|
+
export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun } from "../utils/message-run-ids.js";
|
|
10
12
|
export { useRenderTool, useDefaultRenderTool, useHumanInTheLoop, useFrontendTool, ToolCallStatus, } from "@copilotkit/react-core/v2";
|
|
11
13
|
// Client-only `useLayoutEffect`, falls back to `useEffect` during SSR.
|
|
12
14
|
// Needed so the `agent.threadId` mirror commits before CopilotKit's
|
|
@@ -309,6 +311,23 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
309
311
|
});
|
|
310
312
|
return groups;
|
|
311
313
|
}, [copilotMessages]);
|
|
314
|
+
// Which run produced each replayed message: the connect replay sends it as a CUSTOM
|
|
315
|
+
// event (see utils/message-run-ids). Cleared with the transcript on a conversation swap.
|
|
316
|
+
const [runIdByMessageId, setRunIdByMessageId] = useState(EMPTY_MESSAGE_RUN_IDS);
|
|
317
|
+
useEffect(() => {
|
|
318
|
+
if (!copilotAgent) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const { unsubscribe } = copilotAgent.subscribe({
|
|
322
|
+
onCustomEvent: ({ event }) => {
|
|
323
|
+
const runIds = readMessageRunIdsEvent(event);
|
|
324
|
+
if (runIds) {
|
|
325
|
+
setRunIdByMessageId((previous) => mergeMessageRunIds(previous, runIds));
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
return unsubscribe;
|
|
330
|
+
}, [copilotAgent]);
|
|
312
331
|
const interruptAnswersRef = useRef(new Map());
|
|
313
332
|
// Mirror `currentThreadId` onto the agent instance — the `<CopilotKit>`
|
|
314
333
|
// prop only feeds React context, but `agent.threadId` is what hits the
|
|
@@ -340,6 +359,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
340
359
|
}
|
|
341
360
|
prevConversationKeyRef.current = conversationKey;
|
|
342
361
|
copilotAgent?.setMessages?.([]);
|
|
362
|
+
setRunIdByMessageId(EMPTY_MESSAGE_RUN_IDS);
|
|
343
363
|
setPrompt("");
|
|
344
364
|
setAttachments([]);
|
|
345
365
|
// The clear above runs in the layout phase, but the previous conversation's run is
|
|
@@ -453,6 +473,20 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
453
473
|
}
|
|
454
474
|
await runAgentWithRetry(copilotAgent, copilotAgent.messages.length);
|
|
455
475
|
}, [canSendMessage, copilotAgent, runAgentWithRetry, onUserMessageSent]);
|
|
476
|
+
const reloadMessages = useCallback(async () => {
|
|
477
|
+
if (!copilotAgent || copilotAgent.isRunning) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
// Replay cursors are keyed by threadId and record what has already been replayed, so a
|
|
481
|
+
// stale one makes connect a no-op — exactly the case here, where the thread is unchanged
|
|
482
|
+
// and only the server-side content moved. Messages are NOT cleared first: connect answers
|
|
483
|
+
// with a full snapshot that replaces them, and clearing would blank the transcript for the
|
|
484
|
+
// round-trip.
|
|
485
|
+
copilotAgent.clearReplayCursor?.(currentThreadId);
|
|
486
|
+
// Same guard convention as the composer send path: a failed reconnect already surfaces
|
|
487
|
+
// through the availability state; the rejection carries no extra signal.
|
|
488
|
+
await copilotkit.connectAgent({ agent: copilotAgent }).catch(() => undefined);
|
|
489
|
+
}, [copilotAgent, copilotkit, currentThreadId]);
|
|
456
490
|
// Not sourced from `useInterrupt`: that hook only hands `resolve` to its own
|
|
457
491
|
// `render`, which it skips whenever nothing is pending — so after a reload there
|
|
458
492
|
// is no way to obtain it. `runAgentWithRetry`'s `isRunning` guard makes a
|
|
@@ -741,11 +775,13 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
741
775
|
messageGroups,
|
|
742
776
|
renderToolCall,
|
|
743
777
|
messageById,
|
|
778
|
+
runIdByMessageId,
|
|
744
779
|
toolMessages,
|
|
745
780
|
prompt,
|
|
746
781
|
attachments,
|
|
747
782
|
sendMessage,
|
|
748
783
|
sendFromInputs,
|
|
784
|
+
reloadMessages,
|
|
749
785
|
resolveInterrupt,
|
|
750
786
|
answerInterrupt,
|
|
751
787
|
stopGeneration,
|
|
@@ -925,16 +961,26 @@ export function AgentChatMessage({ message, index, ...props }) {
|
|
|
925
961
|
return _jsx("div", { "data-message-index": index, "data-message-role": message.role, ...props });
|
|
926
962
|
}
|
|
927
963
|
export function AgentChatMessageGroup({ messageGroup, ...props }) {
|
|
928
|
-
return (_jsx("div", { "data-slot": "agent-chat-message-group", "data-message-group": messageGroup.role, "data-message-group-role": messageGroup.role, ...props }));
|
|
964
|
+
return (_jsx("div", { "data-slot": "agent-chat-message-group", "data-message-group": messageGroup.role, "data-message-group-role": messageGroup.role, [MESSAGE_IDS_ATTRIBUTE]: messageGroup.messageIds.join(" "), ...props }));
|
|
929
965
|
}
|
|
930
|
-
|
|
931
|
-
|
|
966
|
+
const NO_RUN_FOCUS = { messageIds: new Set(), isHighlighted: false };
|
|
967
|
+
const AgentChatRunFocusContext = createContext(NO_RUN_FOCUS);
|
|
968
|
+
/** The run focus of the enclosing AgentChatMessages (see its `focusRunId`). */
|
|
969
|
+
export const useAgentChatRunFocus = () => useContext(AgentChatRunFocusContext);
|
|
970
|
+
const DEFAULT_FOCUS_HIGHLIGHT_MS = 2400;
|
|
971
|
+
export function AgentChatMessages({ scrollAreaClassName, focusRunId, focusHighlightMs = DEFAULT_FOCUS_HIGHLIGHT_MS, ...props }) {
|
|
972
|
+
const { messages, runIdByMessageId = EMPTY_MESSAGE_RUN_IDS, isFetchingMessages, currentThreadId } = useAgentChat();
|
|
932
973
|
const userMessageCount = messages.filter((message) => message.role === "user" && message.name !== HIDDEN_INITIAL_PROMPT_MESSAGE_NAME).length;
|
|
933
974
|
const previousUserMessageCountRef = useRef(userMessageCount);
|
|
934
|
-
const { scrollRef, contentRef, scrollToBottom } = useStickToBottom({
|
|
975
|
+
const { scrollRef, contentRef, scrollToBottom, stopScroll } = useStickToBottom({
|
|
935
976
|
resize: "smooth",
|
|
936
977
|
initial: { damping: 1, stiffness: 1 },
|
|
937
978
|
});
|
|
979
|
+
const contentElementRef = useRef(null);
|
|
980
|
+
const setContentRef = useCallback((element) => {
|
|
981
|
+
contentElementRef.current = element;
|
|
982
|
+
contentRef(element);
|
|
983
|
+
}, [contentRef]);
|
|
938
984
|
useEffect(() => {
|
|
939
985
|
if (userMessageCount > previousUserMessageCountRef.current) {
|
|
940
986
|
void scrollToBottom({
|
|
@@ -944,12 +990,48 @@ export function AgentChatMessages({ scrollAreaClassName, ...props }) {
|
|
|
944
990
|
}
|
|
945
991
|
previousUserMessageCountRef.current = userMessageCount;
|
|
946
992
|
}, [scrollToBottom, userMessageCount]);
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
993
|
+
const runMessageIds = useMemo(() => messageIdsOfRun(messages, runIdByMessageId, focusRunId), [messages, runIdByMessageId, focusRunId]);
|
|
994
|
+
const [isHighlighted, setIsHighlighted] = useState(false);
|
|
995
|
+
const focusedKeyRef = useRef(null);
|
|
996
|
+
// Declared after the stick-to-bottom effect above on purpose: the replay that lands the
|
|
997
|
+
// run also bumps the user-message count, and this effect has to run second so its
|
|
998
|
+
// `stopScroll` cancels that scroll-to-bottom instead of being overridden by it.
|
|
999
|
+
useEffect(() => {
|
|
1000
|
+
const firstMessageId = runMessageIds[0];
|
|
1001
|
+
if (!focusRunId || isFetchingMessages || !firstMessageId) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
const focusKey = `${currentThreadId ?? ""}|${focusRunId}`;
|
|
1005
|
+
if (focusedKeyRef.current === focusKey) {
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
focusedKeyRef.current = focusKey;
|
|
1009
|
+
// Leave the bottom lock, or the next resize (a replayed group fading in, a markdown
|
|
1010
|
+
// block laying out) pulls the transcript back down past the run.
|
|
1011
|
+
stopScroll();
|
|
1012
|
+
requestAnimationFrame(() => {
|
|
1013
|
+
const reduceMotion = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
1014
|
+
findMessageGroupElement(contentElementRef.current, firstMessageId)?.scrollIntoView({
|
|
1015
|
+
block: "start",
|
|
1016
|
+
behavior: reduceMotion ? "auto" : "smooth",
|
|
1017
|
+
});
|
|
1018
|
+
setIsHighlighted(true);
|
|
1019
|
+
});
|
|
1020
|
+
}, [focusRunId, isFetchingMessages, runMessageIds, currentThreadId, stopScroll]);
|
|
1021
|
+
useEffect(() => {
|
|
1022
|
+
if (!isHighlighted) {
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
const timeoutId = window.setTimeout(() => setIsHighlighted(false), focusHighlightMs);
|
|
1026
|
+
return () => window.clearTimeout(timeoutId);
|
|
1027
|
+
}, [isHighlighted, focusHighlightMs]);
|
|
1028
|
+
const runFocus = useMemo(() => (focusRunId ? { runId: focusRunId, messageIds: new Set(runMessageIds), isHighlighted } : NO_RUN_FOCUS), [focusRunId, runMessageIds, isHighlighted]);
|
|
1029
|
+
return (_jsx(AgentChatRunFocusContext.Provider, { value: runFocus, children: _jsx("div", { ref: scrollRef, className: scrollAreaClassName, style: {
|
|
1030
|
+
height: "100%",
|
|
1031
|
+
width: "100%",
|
|
1032
|
+
overflow: "auto",
|
|
1033
|
+
scrollbarGutter: "stable both-edges",
|
|
1034
|
+
}, children: _jsx("div", { ref: setContentRef, "data-slot": "agent-chat-messages", ...props }) }) }));
|
|
953
1035
|
}
|
|
954
1036
|
export function AgentChatThinking({ asChild = false, render, ...props }) {
|
|
955
1037
|
const { isThinking } = useAgentChat();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type AgentThreadStatus = "idle" | "interrupted" | "error";
|
|
2
|
-
export type AgentThreadSource = "direct_chat" | "agent_call" | "send_message" | "unknown";
|
|
2
|
+
export type AgentThreadSource = "direct_chat" | "agent_call" | "send_message" | "feedback" | "unknown";
|
|
3
3
|
export interface AgentThreadItem {
|
|
4
4
|
agentId: string | null;
|
|
5
5
|
/** Never null — pass back verbatim to open the conversation. */
|
|
@@ -10,6 +10,21 @@ export interface AgentThreadItem {
|
|
|
10
10
|
status: AgentThreadStatus;
|
|
11
11
|
title: string | null;
|
|
12
12
|
turnCount: number;
|
|
13
|
+
/**
|
|
14
|
+
* What KIND of conversation this is — `"feedback"` for a thread opened by a feedback send,
|
|
15
|
+
* absent/null for an ordinary chat. Optional in the type, not on the wire: a server that
|
|
16
|
+
* predates it sends nothing, and `undefined` must read as "an ordinary chat", not as a
|
|
17
|
+
* missing field to work around.
|
|
18
|
+
*/
|
|
19
|
+
type?: string | null;
|
|
20
|
+
/**
|
|
21
|
+
* For a thread spawned from another conversation: that conversation's `chatThreadId` — pass
|
|
22
|
+
* it back verbatim to open the source, exactly like `chatThreadId` itself. Null when the
|
|
23
|
+
* source predates this field; a UI must then say where the thread came from without linking.
|
|
24
|
+
*/
|
|
25
|
+
fromSourceChatThreadId?: string | null;
|
|
26
|
+
/** The message the feedback was about, when it was about one rather than the conversation. */
|
|
27
|
+
fromSourceMessageId?: string | null;
|
|
13
28
|
/** First seen by the index — NOT thread creation time. */
|
|
14
29
|
firstSeenAt: string;
|
|
15
30
|
lastActivityAt: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-thread-live-update.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-thread-live-update.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-thread-live-update.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-thread-live-update.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,UAAU,GAAG,SAAS,CAAC;AAEvG,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,8FAA8F;IAC9F,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,kFAAkF;AAClF,eAAO,MAAM,cAAc,GAAI,QAAQ,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,cAAc,CAAC,KAAG,MAC9C,CAAC;AAW7C;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,eAiB3D,CAAC;AAKF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,QAAQ,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,IAQ5E,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACpC,QAAQ,eAAe,EACvB,UAAU,eAAe,GAAG,SAAS,EACrC,QAAQ,MAAM,GAAG,SAAS,EAC1B,KAAK,MAAM,KACV,eAID,CAAC;AAEH,MAAM,WAAW,4BAA4B;IAC3C,uFAAuF;IACvF,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,cAAc,GAAG,WAAW,CAAC;IACpC,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAOD;;;;;;;;;;;;;GAaG;AACH,kEAAkE;AAClE,eAAO,MAAM,yBAAyB,GAAI,MAAM,eAAe,EAAE,UAAS,4BAAiC,KAAG,OAGzD,CAAC;AAEtD,eAAO,MAAM,sBAAsB,GACjC,SAAS,eAAe,EAAE,EAC1B,UAAU,eAAe,EACzB,UAAS,4BAAiC,KACzC,eAAe,EAqBjB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,eAAe,EAAE,EAC1B,YAAY,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,EAChD,UAAS,4BAAiC,KACzC,eAAe,EAkBjB,CAAC"}
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
/** The (chatId, chatThreadId) pair — the only stable identity of a thread row. */
|
|
5
5
|
export const agentThreadKey = (thread) => `${thread.chatId}::${thread.chatThreadId}`;
|
|
6
6
|
const THREAD_STATUSES = ["idle", "interrupted", "error"];
|
|
7
|
-
const THREAD_SOURCES = [
|
|
7
|
+
const THREAD_SOURCES = [
|
|
8
|
+
"direct_chat",
|
|
9
|
+
"agent_call",
|
|
10
|
+
"send_message",
|
|
11
|
+
"feedback",
|
|
12
|
+
"unknown",
|
|
13
|
+
];
|
|
8
14
|
/**
|
|
9
15
|
* Whether a realtime payload's `thread` is a complete list-item row. The server
|
|
10
16
|
* serializes the exact item shape into every event, so this normally passes; when
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which agent run (turn) produced each message of a conversation.
|
|
3
|
+
*
|
|
4
|
+
* A row an app agent writes records the run that wrote it (`agentRunId`). On
|
|
5
|
+
* `agent/connect` the server replays the transcript and then sends one AG-UI CUSTOM
|
|
6
|
+
* event, `blocks.message_run_ids`, carrying `{ runIds: { [messageId]: runId } }` for
|
|
7
|
+
* the replayed messages it can attribute (older messages have none). The run id
|
|
8
|
+
* rides in an event rather than on the messages because the AG-UI client validates
|
|
9
|
+
* snapshot messages against a strict schema that drops unknown fields.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MESSAGE_RUN_IDS_EVENT_NAME = "blocks.message_run_ids";
|
|
12
|
+
export type MessageRunIds = ReadonlyMap<string, string>;
|
|
13
|
+
export declare const EMPTY_MESSAGE_RUN_IDS: MessageRunIds;
|
|
14
|
+
/** The `{ messageId: runId }` payload of a run-ids CUSTOM event, or null for any other event. */
|
|
15
|
+
export declare const readMessageRunIdsEvent: (event: unknown) => Record<string, string> | null;
|
|
16
|
+
/** `previous` plus `incoming`; returns `previous` itself when nothing changes (no re-render). */
|
|
17
|
+
export declare const mergeMessageRunIds: (previous: MessageRunIds, incoming: Record<string, string>) => MessageRunIds;
|
|
18
|
+
/** Ids of the messages `runId` produced, in transcript order (empty when the run isn't in it). */
|
|
19
|
+
export declare const messageIdsOfRun: (messages: readonly {
|
|
20
|
+
id: string;
|
|
21
|
+
}[], runIds: MessageRunIds, runId: string | undefined) => string[];
|
|
22
|
+
/** The attribute a message group carries so a run's first message can be found in the DOM. */
|
|
23
|
+
export declare const MESSAGE_IDS_ATTRIBUTE = "data-message-ids";
|
|
24
|
+
/** The rendered message group containing `messageId` inside `root`, if any. */
|
|
25
|
+
export declare const findMessageGroupElement: (root: ParentNode | null, messageId: string) => HTMLElement | null;
|
|
26
|
+
//# sourceMappingURL=message-run-ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-run-ids.d.ts","sourceRoot":"","sources":["../../src/utils/message-run-ids.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAEnE,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAExD,eAAO,MAAM,qBAAqB,EAAE,aAAyB,CAAC;AAK9D,iGAAiG;AACjG,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAehF,CAAC;AAEF,iGAAiG;AACjG,eAAO,MAAM,kBAAkB,GAAI,UAAU,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,aAU9F,CAAC;AAEF,kGAAkG;AAClG,eAAO,MAAM,eAAe,GAC1B,UAAU,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,EACnC,QAAQ,aAAa,EACrB,OAAO,MAAM,GAAG,SAAS,KACxB,MAAM,EAKR,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,GAAI,MAAM,UAAU,GAAG,IAAI,EAAE,WAAW,MAAM,KAAG,WAAW,GAAG,IAYlG,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which agent run (turn) produced each message of a conversation.
|
|
3
|
+
*
|
|
4
|
+
* A row an app agent writes records the run that wrote it (`agentRunId`). On
|
|
5
|
+
* `agent/connect` the server replays the transcript and then sends one AG-UI CUSTOM
|
|
6
|
+
* event, `blocks.message_run_ids`, carrying `{ runIds: { [messageId]: runId } }` for
|
|
7
|
+
* the replayed messages it can attribute (older messages have none). The run id
|
|
8
|
+
* rides in an event rather than on the messages because the AG-UI client validates
|
|
9
|
+
* snapshot messages against a strict schema that drops unknown fields.
|
|
10
|
+
*/
|
|
11
|
+
export const MESSAGE_RUN_IDS_EVENT_NAME = "blocks.message_run_ids";
|
|
12
|
+
export const EMPTY_MESSAGE_RUN_IDS = new Map();
|
|
13
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
/** The `{ messageId: runId }` payload of a run-ids CUSTOM event, or null for any other event. */
|
|
15
|
+
export const readMessageRunIdsEvent = (event) => {
|
|
16
|
+
if (!isRecord(event) || event.name !== MESSAGE_RUN_IDS_EVENT_NAME || !isRecord(event.value)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const runIds = event.value.runIds;
|
|
20
|
+
if (!isRecord(runIds)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const out = {};
|
|
24
|
+
for (const [messageId, runId] of Object.entries(runIds)) {
|
|
25
|
+
if (messageId && typeof runId === "string" && runId) {
|
|
26
|
+
out[messageId] = runId;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
};
|
|
31
|
+
/** `previous` plus `incoming`; returns `previous` itself when nothing changes (no re-render). */
|
|
32
|
+
export const mergeMessageRunIds = (previous, incoming) => {
|
|
33
|
+
let next = null;
|
|
34
|
+
for (const [messageId, runId] of Object.entries(incoming)) {
|
|
35
|
+
if (previous.get(messageId) === runId) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
next ??= new Map(previous);
|
|
39
|
+
next.set(messageId, runId);
|
|
40
|
+
}
|
|
41
|
+
return next ?? previous;
|
|
42
|
+
};
|
|
43
|
+
/** Ids of the messages `runId` produced, in transcript order (empty when the run isn't in it). */
|
|
44
|
+
export const messageIdsOfRun = (messages, runIds, runId) => {
|
|
45
|
+
if (!runId) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
return messages.filter((message) => runIds.get(message.id) === runId).map((message) => message.id);
|
|
49
|
+
};
|
|
50
|
+
/** The attribute a message group carries so a run's first message can be found in the DOM. */
|
|
51
|
+
export const MESSAGE_IDS_ATTRIBUTE = "data-message-ids";
|
|
52
|
+
/** The rendered message group containing `messageId` inside `root`, if any. */
|
|
53
|
+
export const findMessageGroupElement = (root, messageId) => {
|
|
54
|
+
if (!root) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const groups = root.querySelectorAll(`[${MESSAGE_IDS_ATTRIBUTE}]`);
|
|
58
|
+
for (const group of Array.from(groups)) {
|
|
59
|
+
const ids = (group.getAttribute(MESSAGE_IDS_ATTRIBUTE) ?? "").split(" ");
|
|
60
|
+
if (ids.includes(messageId)) {
|
|
61
|
+
return group;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocksdiy/react-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.45.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React common",
|
|
6
6
|
"keywords": [],
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"penpal": "^8.0.0",
|
|
40
40
|
"react": "^19.2.8",
|
|
41
41
|
"use-stick-to-bottom": "1.1.4",
|
|
42
|
-
"@blocksdiy/blocks-client-api": "1.
|
|
42
|
+
"@blocksdiy/blocks-client-api": "1.23.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/react": "^19.2.18",
|