@blocksdiy/react-common 1.42.0 → 1.43.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 +13 -0
- package/dist/components/new-agent-chat.d.ts.map +1 -1
- package/dist/components/new-agent-chat.js +43 -12
- package/dist/hooks/agent-thread-live-update.d.ts +7 -0
- package/dist/hooks/agent-thread-live-update.d.ts.map +1 -1
- package/dist/hooks/agent-thread-live-update.js +11 -0
- package/dist/hooks/use-agent-threads.d.ts +14 -25
- package/dist/hooks/use-agent-threads.d.ts.map +1 -1
- package/dist/hooks/use-agent-threads.js +24 -113
- package/package.json +1 -1
|
@@ -122,6 +122,19 @@ export interface AgentChatRootProps {
|
|
|
122
122
|
* routed through `sendMessage`, not the auto-sent `initialPrompt`.
|
|
123
123
|
*/
|
|
124
124
|
onUserMessageSent?: (text: string) => void;
|
|
125
|
+
/**
|
|
126
|
+
* Skip the agent-chat block's `initialPrompt` auto-send. For surfaces that mint a fresh
|
|
127
|
+
* thread per conversation (an inbox's "New chat"): the auto-send fires once PER THREAD,
|
|
128
|
+
* so it would index a ghost turn for every draft. Default off — plain chats keep it.
|
|
129
|
+
*/
|
|
130
|
+
disableInitialPrompt?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Fires when the agent-chat block or its agent block cannot be loaded — the request
|
|
133
|
+
* failed, or the platform answered with no such block (a 403/404 body). The root renders
|
|
134
|
+
* nothing in that state, so a host that gates its own chrome on the chat resolving
|
|
135
|
+
* (a layout's skeleton) must use this to show an error instead of waiting forever.
|
|
136
|
+
*/
|
|
137
|
+
onLoadError?: (error: Error) => void;
|
|
125
138
|
}
|
|
126
139
|
export declare const AgentChatRoot: ({ ...props }: AgentChatRootProps) => import("react").JSX.Element | null;
|
|
127
140
|
export interface AgentChatMessageProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"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"}
|
|
@@ -243,7 +243,7 @@ const SessionReadables = () => {
|
|
|
243
243
|
// });
|
|
244
244
|
return null;
|
|
245
245
|
};
|
|
246
|
-
const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, components, currentThreadId, setCurrentThreadId, children, onUserMessageSent, }) => {
|
|
246
|
+
const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, components, currentThreadId, setCurrentThreadId, children, onUserMessageSent, disableInitialPrompt, }) => {
|
|
247
247
|
const [agentChat, setAgentChat] = useState(null);
|
|
248
248
|
const [agent, setAgent] = useState(null);
|
|
249
249
|
// `isConfigLoaded` covers the (small, parallel) async fetch of the
|
|
@@ -586,8 +586,12 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
586
586
|
// `isLoading`). Without this guard the optimistic `initialPromptSentRef`
|
|
587
587
|
// flip below would lock the prompt out forever after a single
|
|
588
588
|
// dropped call. Re-runs as soon as the gate opens.
|
|
589
|
+
//
|
|
590
|
+
// `disableInitialPrompt` skips the auto-send entirely: an inbox surface mints a fresh
|
|
591
|
+
// thread for every "New chat", and an auto-sent prompt would index a ghost turn for
|
|
592
|
+
// each of them before the user has said anything.
|
|
589
593
|
useEffect(() => {
|
|
590
|
-
if (initialPromptSentRef.current) {
|
|
594
|
+
if (disableInitialPrompt || initialPromptSentRef.current) {
|
|
591
595
|
return;
|
|
592
596
|
}
|
|
593
597
|
if (!isAgentReady || !isConfigLoaded) {
|
|
@@ -620,7 +624,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
620
624
|
content: prompt,
|
|
621
625
|
});
|
|
622
626
|
void runAgentWithRetry(copilotAgent, copilotAgent.messages.length).catch(() => undefined);
|
|
623
|
-
}, [isAgentReady, isConfigLoaded, agentChat, copilotAgent, canSendMessage, runAgentWithRetry]);
|
|
627
|
+
}, [isAgentReady, isConfigLoaded, agentChat, copilotAgent, canSendMessage, runAgentWithRetry, disableInitialPrompt]);
|
|
624
628
|
const buildMessageContentFromInput = (currentPrompt = prompt, currentAttachments = attachments) => {
|
|
625
629
|
if (currentAttachments.length === 0) {
|
|
626
630
|
return currentPrompt;
|
|
@@ -767,7 +771,13 @@ export const AgentChatRoot = ({ ...props
|
|
|
767
771
|
// memoryEnabled = false,
|
|
768
772
|
// shortTermMemory,
|
|
769
773
|
}) => {
|
|
770
|
-
const { appId, token, children, agentId, agentHarness, useAgentBlockDirectChat, agentChatId, componentIds, chatId, defaultThreadId, noPersistency, chatContext, chatContextFiles, selfEditEnabled, supportsServerInterrupt, } = props;
|
|
774
|
+
const { appId, token, children, agentId, agentHarness, useAgentBlockDirectChat, agentChatId, componentIds, chatId, defaultThreadId, noPersistency, chatContext, chatContextFiles, selfEditEnabled, supportsServerInterrupt, onLoadError, } = props;
|
|
775
|
+
// Read at call time via a ref so an inline handler doesn't re-run the block fetch below
|
|
776
|
+
// (the loads are async, so the ref is current by the time a failure is reported).
|
|
777
|
+
const onLoadErrorRef = useRef(onLoadError);
|
|
778
|
+
useEffect(() => {
|
|
779
|
+
onLoadErrorRef.current = onLoadError;
|
|
780
|
+
});
|
|
771
781
|
const socketId = websocketsService.getSocketId();
|
|
772
782
|
const headers = useMemo(() => ({
|
|
773
783
|
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
@@ -803,8 +813,10 @@ export const AgentChatRoot = ({ ...props
|
|
|
803
813
|
if (!agentChatId) {
|
|
804
814
|
return null;
|
|
805
815
|
}
|
|
816
|
+
// getBlock resolves with the response body whatever the status, so a 403/404 error
|
|
817
|
+
// envelope arrives here as a truthy object — a block is only a block when it has an id.
|
|
806
818
|
const agentChatBlock = await blocksApiService.getBlock(agentChatId, appId, "agent", "agent_chat");
|
|
807
|
-
if (!agentChatBlock) {
|
|
819
|
+
if (!agentChatBlock?.id) {
|
|
808
820
|
throw new Error("Agent chat block not found");
|
|
809
821
|
}
|
|
810
822
|
return agentChatBlock;
|
|
@@ -814,8 +826,8 @@ export const AgentChatRoot = ({ ...props
|
|
|
814
826
|
return null;
|
|
815
827
|
}
|
|
816
828
|
const agentBlock = await blocksApiService.getBlock(agentBlockId, appId, "agent", "agent");
|
|
817
|
-
if (!agentBlock) {
|
|
818
|
-
throw new Error("Agent
|
|
829
|
+
if (!agentBlock?.id) {
|
|
830
|
+
throw new Error("Agent block not found");
|
|
819
831
|
}
|
|
820
832
|
return agentBlock;
|
|
821
833
|
}, [appId, blocksApiService]);
|
|
@@ -849,20 +861,39 @@ export const AgentChatRoot = ({ ...props
|
|
|
849
861
|
return maybeComponentBlocks.filter((component) => Boolean(component));
|
|
850
862
|
}, [blocksApiService, componentIds]);
|
|
851
863
|
useEffect(() => {
|
|
852
|
-
|
|
864
|
+
// A failed block load leaves the root rendering nothing (below) — report it, or a host
|
|
865
|
+
// waiting on the chat would wait forever. Only the load this effect still owns reports:
|
|
866
|
+
// a superseded one (inputs changed, or the root unmounted) is the newer load's to tell.
|
|
867
|
+
// Its state writes are left as they were (last writer wins) — hosts re-render during
|
|
868
|
+
// loads, and resolution must never depend on them going quiet.
|
|
869
|
+
let superseded = false;
|
|
870
|
+
const reportLoadError = (error) => {
|
|
871
|
+
if (!superseded) {
|
|
872
|
+
onLoadErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
getAgentChatBlock()
|
|
876
|
+
.then((agentChatBlock) => {
|
|
853
877
|
setAgentChatBlock(agentChatBlock);
|
|
854
878
|
// The chat block's stored agentBlockId is authoritative — sync resolves it
|
|
855
879
|
// to a real block id, while the `agentId` prop from generated product-types
|
|
856
880
|
// is a name that can drift from the agent's `block.name`. The prop is only
|
|
857
881
|
// the fallback for direct agent chats without an agent-chat block.
|
|
858
882
|
const effectiveAgentId = agentChatBlock?.data?.agentBlockId ?? agentId;
|
|
859
|
-
getAgentBlock(effectiveAgentId)
|
|
883
|
+
getAgentBlock(effectiveAgentId)
|
|
884
|
+
.then((agentBlock) => {
|
|
860
885
|
setAgentBlock(agentBlock);
|
|
861
|
-
})
|
|
886
|
+
})
|
|
887
|
+
.catch(reportLoadError);
|
|
888
|
+
// Never rejects: each component load guards itself and drops to null.
|
|
862
889
|
getChatComponentBlocks().then((chatComponentBlocks) => {
|
|
863
890
|
setComponents(chatComponentBlocks);
|
|
864
891
|
});
|
|
865
|
-
})
|
|
892
|
+
})
|
|
893
|
+
.catch(reportLoadError);
|
|
894
|
+
return () => {
|
|
895
|
+
superseded = true;
|
|
896
|
+
};
|
|
866
897
|
}, [getAgentChatBlock, getAgentBlock, getChatComponentBlocks, agentId]);
|
|
867
898
|
if (!appId || !agentBlock || !agentChatBlock) {
|
|
868
899
|
return null;
|
|
@@ -885,7 +916,7 @@ export const AgentChatRoot = ({ ...props
|
|
|
885
916
|
},
|
|
886
917
|
// Explicit false: when undefined, CopilotKit auto-enables its inspector
|
|
887
918
|
// devtools on localhost hostnames.
|
|
888
|
-
enableInspector: false, children: [_jsx(SessionReadables, {}), _jsx(ChatInitializer, { agentBlock: agentBlock, agentChatBlock: agentChatBlock, chatId: chatId, noPersistency: noPersistency, token: token, appId: appId, components: components, currentThreadId: currentThreadId, setCurrentThreadId: setCurrentThreadId, onUserMessageSent: props.onUserMessageSent, children: children })] }));
|
|
919
|
+
enableInspector: false, children: [_jsx(SessionReadables, {}), _jsx(ChatInitializer, { agentBlock: agentBlock, agentChatBlock: agentChatBlock, chatId: chatId, noPersistency: noPersistency, token: token, appId: appId, components: components, currentThreadId: currentThreadId, setCurrentThreadId: setCurrentThreadId, onUserMessageSent: props.onUserMessageSent, disableInitialPrompt: props.disableInitialPrompt, children: children })] }));
|
|
889
920
|
};
|
|
890
921
|
export function AgentChatMessage({ message, index, ...props }) {
|
|
891
922
|
if (message.role === "user" && message.name === HIDDEN_INITIAL_PROMPT_MESSAGE_NAME) {
|
|
@@ -28,6 +28,13 @@ export declare const isAgentThreadItem: (value: unknown) => value is AgentThread
|
|
|
28
28
|
* there's nothing usable (attachment-only sends) — the UI keeps its id-based fallback.
|
|
29
29
|
*/
|
|
30
30
|
export declare const provisionalThreadTitle: (prompt: string | undefined) => string | null;
|
|
31
|
+
/**
|
|
32
|
+
* The optimistic row for a user send on `thread`, replacing `previous` (its current
|
|
33
|
+
* optimistic entry, if any): the same identity, its recency bumped to `now`, and a title
|
|
34
|
+
* that never regresses — the provisional title already shown, else the row's own, else
|
|
35
|
+
* one from this prompt (null when the prompt has nothing usable).
|
|
36
|
+
*/
|
|
37
|
+
export declare const optimisticThreadAfterSend: (thread: AgentThreadItem, previous: AgentThreadItem | undefined, prompt: string | undefined, now: string) => AgentThreadItem;
|
|
31
38
|
export interface AgentThreadLiveUpdateOptions {
|
|
32
39
|
/** Active server-side filters — an updated row that stops matching leaves the list. */
|
|
33
40
|
status?: AgentThreadStatus;
|
|
@@ -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;AAE1F,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,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;AAK7C;;;;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,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"}
|
|
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;AAE1F,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,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;AAK7C;;;;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"}
|
|
@@ -42,6 +42,17 @@ export const provisionalThreadTitle = (prompt) => {
|
|
|
42
42
|
? `${collapsed.slice(0, PROVISIONAL_TITLE_MAX_CHARS - 1).trimEnd()}…`
|
|
43
43
|
: collapsed;
|
|
44
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* The optimistic row for a user send on `thread`, replacing `previous` (its current
|
|
47
|
+
* optimistic entry, if any): the same identity, its recency bumped to `now`, and a title
|
|
48
|
+
* that never regresses — the provisional title already shown, else the row's own, else
|
|
49
|
+
* one from this prompt (null when the prompt has nothing usable).
|
|
50
|
+
*/
|
|
51
|
+
export const optimisticThreadAfterSend = (thread, previous, prompt, now) => ({
|
|
52
|
+
...thread,
|
|
53
|
+
title: previous?.title ?? thread.title ?? provisionalThreadTitle(prompt),
|
|
54
|
+
lastActivityAt: now,
|
|
55
|
+
});
|
|
45
56
|
const sortTime = (item, sort) => {
|
|
46
57
|
const parsed = new Date(sort === "firstSeen" ? item.firstSeenAt : item.lastActivityAt).getTime();
|
|
47
58
|
return Number.isNaN(parsed) ? 0 : parsed;
|
|
@@ -22,27 +22,14 @@ export interface UseAgentThreadsOptions {
|
|
|
22
22
|
export interface UseAgentThreadsResult {
|
|
23
23
|
threads: AgentThreadItem[];
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* item when the row isn't in the loaded pages, so selection never vanishes.
|
|
25
|
+
* Report a user send on a conversation — ChatGPT-style optimistic listing. Pass the
|
|
26
|
+
* conversation's row (the listed one when the index has it, else one the consumer
|
|
27
|
+
* builds from the (chatId, chatThreadId) pair) and the prompt text: a conversation with
|
|
28
|
+
* no server row yet appears in `threads` immediately, titled by the truncated prompt,
|
|
29
|
+
* and every send bumps the row's recency; server rows take over under the same identity
|
|
30
|
+
* as events land. Identity-stable — safe to hand to memoized chat surfaces.
|
|
32
31
|
*/
|
|
33
|
-
|
|
34
|
-
/** True while currentThread is an auto-generated draft not yet in the index. */
|
|
35
|
-
isDraftThread: boolean;
|
|
36
|
-
/** Move between threads; null mints a fresh draft ("new chat"). Resets when agentId changes. */
|
|
37
|
-
setCurrentThread: (thread: AgentThreadItem | null) => void;
|
|
38
|
-
/**
|
|
39
|
-
* Report a user send on the current conversation (pass the prompt text) — ChatGPT-style
|
|
40
|
-
* optimistic listing. A conversation with no server row yet appears in `threads`
|
|
41
|
-
* immediately, titled by the truncated prompt, and every send bumps the row's recency;
|
|
42
|
-
* server rows take over under the same identity as events land. Identity-stable — safe
|
|
43
|
-
* to hand to memoized chat surfaces (e.g. a chat root's onUserMessageSent).
|
|
44
|
-
*/
|
|
45
|
-
notifyThreadActivity: (prompt?: string) => void;
|
|
32
|
+
notifyThreadActivity: (thread: AgentThreadItem, prompt?: string) => void;
|
|
46
33
|
/**
|
|
47
34
|
* Rename a conversation. Optimistic — the list shows the new title immediately and
|
|
48
35
|
* rolls back on failure (the promise rejects so callers can surface an error).
|
|
@@ -86,11 +73,13 @@ export declare const agentThreadDisplayTitle: (thread: Pick<AgentThreadItem, "ch
|
|
|
86
73
|
* publishes AGENT_THREAD_UPDATED on the app's Pusher channel and the hook upserts the row,
|
|
87
74
|
* falling back to the getOne endpoint when a payload lacks the full row. New conversations
|
|
88
75
|
* list optimistically the moment the first prompt is sent (wire notifyThreadActivity to the
|
|
89
|
-
* chat's send seam).
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
76
|
+
* chat's send seam). The LIST only — which conversation is open is the consumer's (a
|
|
77
|
+
* conversation is addressed by its (chatId, chatThreadId) pair; a new one is a fresh UUID
|
|
78
|
+
* thread id under the surface's chatId — CopilotKit replaces non-UUID ids per load).
|
|
79
|
+
* Pair it with whatever list UI the host app uses. No per-host wiring: both hosts already
|
|
80
|
+
* run the shared websocketsService singleton and subscribe the app channel (the platform
|
|
81
|
+
* via its layouts' useWebsockets hooks, a boilerplate app via the client SDK's
|
|
82
|
+
* ClientProvider), and listening before the socket connects just parks the handler.
|
|
94
83
|
*/
|
|
95
84
|
export declare const useAgentThreads: (options: UseAgentThreadsOptions) => UseAgentThreadsResult;
|
|
96
85
|
export default useAgentThreads;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent-threads.d.ts","sourceRoot":"","sources":["../../src/hooks/use-agent-threads.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EAMvB,MAAM,4BAA4B,CAAC;AAKpC,cAAc,4BAA4B,CAAC;AAO3C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,GAAG,WAAW,CAAC;IACpC,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B
|
|
1
|
+
{"version":3,"file":"use-agent-threads.d.ts","sourceRoot":"","sources":["../../src/hooks/use-agent-threads.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EAMvB,MAAM,4BAA4B,CAAC;AAKpC,cAAc,4BAA4B,CAAC;AAO3C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,GAAG,WAAW,CAAC;IACpC,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B;;;;;;;OAOG;IACH,oBAAoB,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzE;;;OAGG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,cAAc,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzG,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B;;;;OAIG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AACtD,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,KAAG,MAAgD,CAAC;AAEpG;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,cAAc,CAAC,KAAG,MAIhG,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,cAAc,GAAG,OAAO,CAAC,KAAG,MACpD,CAAC;AAuC1D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,sBAAsB,KAAG,qBAqYjE,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getApiHost } from "@blocksdiy/blocks-client-api/envService";
|
|
2
2
|
import { websocketsService } from "@blocksdiy/blocks-client-api/websocketService";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import { agentThreadKey, applyAgentThreadUpdate, isAgentThreadItem, matchesAgentThreadFilters,
|
|
4
|
+
import { agentThreadKey, applyAgentThreadUpdate, isAgentThreadItem, matchesAgentThreadFilters, optimisticThreadAfterSend, overlayOptimisticThreads, } from "./agent-thread-live-update.js";
|
|
5
5
|
// Wire types, the identity key, and the realtime merge logic live in
|
|
6
6
|
// ./agent-thread-live-update (dependency-free, unit-testable); re-exported so
|
|
7
7
|
// consumers keep importing everything from this module.
|
|
@@ -62,39 +62,19 @@ const appendPage = (existing, page) => {
|
|
|
62
62
|
const seen = new Set(existing.map(agentThreadKey));
|
|
63
63
|
return [...existing, ...page.filter((thread) => !seen.has(agentThreadKey(thread)))];
|
|
64
64
|
};
|
|
65
|
-
/**
|
|
66
|
-
* A not-yet-indexed conversation. chatId is ALWAYS the agent's static inbox id (the
|
|
67
|
-
* surface marker — identical for every inbox conversation with this agent); only the
|
|
68
|
-
* thread id makes each conversation unique, and it must be a UUID — CopilotKit
|
|
69
|
-
* replaces non-UUID thread ids per load, which breaks history. Sharing the chatId
|
|
70
|
-
* also means inbox thread switches are pure thread-id changes, the chat stack's
|
|
71
|
-
* native reconnect path.
|
|
72
|
-
*/
|
|
73
|
-
const makeDraftThread = (agentId) => {
|
|
74
|
-
const now = new Date().toISOString();
|
|
75
|
-
return {
|
|
76
|
-
agentId: agentId || null,
|
|
77
|
-
chatId: agentInboxChatId(agentId),
|
|
78
|
-
chatThreadId: crypto.randomUUID(),
|
|
79
|
-
source: "direct_chat",
|
|
80
|
-
status: "idle",
|
|
81
|
-
title: null,
|
|
82
|
-
turnCount: 0,
|
|
83
|
-
firstSeenAt: now,
|
|
84
|
-
lastActivityAt: now,
|
|
85
|
-
};
|
|
86
|
-
};
|
|
87
65
|
/**
|
|
88
66
|
* Headless agent-threads index: fetches an agent's conversation threads from the platform
|
|
89
67
|
* endpoint with keyset-cursor pagination, then keeps them live — every threads-table write
|
|
90
68
|
* publishes AGENT_THREAD_UPDATED on the app's Pusher channel and the hook upserts the row,
|
|
91
69
|
* falling back to the getOne endpoint when a payload lacks the full row. New conversations
|
|
92
70
|
* list optimistically the moment the first prompt is sent (wire notifyThreadActivity to the
|
|
93
|
-
* chat's send seam).
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
71
|
+
* chat's send seam). The LIST only — which conversation is open is the consumer's (a
|
|
72
|
+
* conversation is addressed by its (chatId, chatThreadId) pair; a new one is a fresh UUID
|
|
73
|
+
* thread id under the surface's chatId — CopilotKit replaces non-UUID ids per load).
|
|
74
|
+
* Pair it with whatever list UI the host app uses. No per-host wiring: both hosts already
|
|
75
|
+
* run the shared websocketsService singleton and subscribe the app channel (the platform
|
|
76
|
+
* via its layouts' useWebsockets hooks, a boilerplate app via the client SDK's
|
|
77
|
+
* ClientProvider), and listening before the socket connects just parks the handler.
|
|
98
78
|
*/
|
|
99
79
|
export const useAgentThreads = (options) => {
|
|
100
80
|
const { agentId, status, source, chatId, limit, sort, order, baseUrl } = options;
|
|
@@ -121,8 +101,6 @@ export const useAgentThreads = (options) => {
|
|
|
121
101
|
const [isLoading, setIsLoading] = useState(Boolean(agentId));
|
|
122
102
|
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
123
103
|
const [error, setError] = useState(null);
|
|
124
|
-
const [selectedThread, setSelectedThread] = useState(null);
|
|
125
|
-
const [draftThread, setDraftThread] = useState(() => makeDraftThread(agentId));
|
|
126
104
|
// ChatGPT-style optimistic rows, keyed by thread identity: conversations listed from
|
|
127
105
|
// the moment a prompt is sent (see notifyThreadActivity), before/ahead of their server
|
|
128
106
|
// row. The display overlay hands each one over to its server row as events land.
|
|
@@ -133,78 +111,22 @@ export const useAgentThreads = (options) => {
|
|
|
133
111
|
optimisticThreadsRef.current = next;
|
|
134
112
|
setOptimisticThreads(next);
|
|
135
113
|
}, []);
|
|
136
|
-
const setCurrentThread = useCallback((thread) => {
|
|
137
|
-
// Null means "new chat": mint a fresh draft so each one is its own conversation.
|
|
138
|
-
if (!thread) {
|
|
139
|
-
setDraftThread(makeDraftThread(agentId));
|
|
140
|
-
}
|
|
141
|
-
setSelectedThread(thread);
|
|
142
|
-
}, [agentId]);
|
|
143
|
-
// Selection follows the agent, not the query: filter changes keep it (the fallback in
|
|
144
|
-
// `currentThread` covers rows the new filter drops), but another agent's thread — or
|
|
145
|
-
// draft — is meaningless here. Guarded by the previous id so the mount render keeps the
|
|
146
|
-
// initial draft (regenerating it would remount a consumer's keyed chat surface for nothing).
|
|
147
|
-
const prevAgentIdRef = useRef(agentId);
|
|
148
|
-
useEffect(() => {
|
|
149
|
-
if (prevAgentIdRef.current === agentId) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
prevAgentIdRef.current = agentId;
|
|
153
|
-
setSelectedThread(null);
|
|
154
|
-
setDraftThread(makeDraftThread(agentId));
|
|
155
|
-
applyOptimisticThreads(new Map());
|
|
156
|
-
}, [agentId, applyOptimisticThreads]);
|
|
157
114
|
// The realtime merge's list-shape inputs, shared by the display overlay, the live
|
|
158
115
|
// event handler, and the walk-result reconciliation in loadFirstPage.
|
|
159
116
|
const liveUpdateOptions = useMemo(() => ({ status, source, chatId, sort, order }), [status, source, chatId, sort, order]);
|
|
160
117
|
// What consumers see: the loaded pages with the optimistic rows overlaid.
|
|
161
118
|
const displayThreads = useMemo(() => overlayOptimisticThreads(threads, optimisticThreads, liveUpdateOptions), [threads, optimisticThreads, liveUpdateOptions]);
|
|
162
|
-
// Rows are rewritten server-side every turn, so after a refresh the stored item is stale;
|
|
163
|
-
// prefer the list's row with the same identity, keep the stored one if it's not loaded.
|
|
164
|
-
// The draft goes through the same lookup: once its row is listed — optimistically on the
|
|
165
|
-
// first send, or when its first turn is indexed — that row wins under the same key: the
|
|
166
|
-
// draft has materialized, without changing identity.
|
|
167
|
-
const currentThread = useMemo(() => {
|
|
168
|
-
const stored = selectedThread ?? draftThread;
|
|
169
|
-
const storedKey = agentThreadKey(stored);
|
|
170
|
-
return displayThreads.find((thread) => agentThreadKey(thread) === storedKey) ?? stored;
|
|
171
|
-
}, [displayThreads, selectedThread, draftThread]);
|
|
172
|
-
const isDraftThread = !selectedThread && currentThread === draftThread;
|
|
173
|
-
// Once the draft's row is listed, it IS the conversation — adopt it as the selection.
|
|
174
|
-
// Otherwise a later filter change (which blanks the list before refetching) would flip
|
|
175
|
-
// `isDraftThread` back to true for a conversation that has messages, and the consumer's
|
|
176
|
-
// draft-only presentation ("New chat" header, empty-state slot) with it.
|
|
177
|
-
useEffect(() => {
|
|
178
|
-
if (selectedThread) {
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const draftKey = agentThreadKey(draftThread);
|
|
182
|
-
const materialized = displayThreads.find((thread) => agentThreadKey(thread) === draftKey);
|
|
183
|
-
if (materialized) {
|
|
184
|
-
setSelectedThread(materialized);
|
|
185
|
-
}
|
|
186
|
-
}, [displayThreads, selectedThread, draftThread]);
|
|
187
|
-
// Read at call time via a ref so notifyThreadActivity stays identity-stable for
|
|
188
|
-
// memoized chat surfaces (same render-time-assignment pattern as authRef below).
|
|
189
|
-
const currentThreadRef = useRef(currentThread);
|
|
190
|
-
currentThreadRef.current = currentThread;
|
|
191
119
|
/**
|
|
192
|
-
* Report a user send on
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
120
|
+
* Report a user send on a conversation — ChatGPT-style optimistic listing. A
|
|
121
|
+
* conversation with no server row yet appears in `threads` immediately, titled by the
|
|
122
|
+
* truncated prompt, and every send bumps the row's recency. Server rows take over under
|
|
123
|
+
* the same identity as events land (turn end, title generation), the provisional title
|
|
124
|
+
* holding until the generated one arrives.
|
|
197
125
|
*/
|
|
198
|
-
const notifyThreadActivity = useCallback((prompt) => {
|
|
199
|
-
const current = currentThreadRef.current;
|
|
200
|
-
const key = agentThreadKey(current);
|
|
126
|
+
const notifyThreadActivity = useCallback((thread, prompt) => {
|
|
201
127
|
const prev = optimisticThreadsRef.current;
|
|
202
128
|
const next = new Map(prev);
|
|
203
|
-
next.set(
|
|
204
|
-
...current,
|
|
205
|
-
title: prev.get(key)?.title ?? current.title ?? provisionalThreadTitle(prompt),
|
|
206
|
-
lastActivityAt: new Date().toISOString(),
|
|
207
|
-
});
|
|
129
|
+
next.set(agentThreadKey(thread), optimisticThreadAfterSend(thread, prev.get(agentThreadKey(thread)), prompt, new Date().toISOString()));
|
|
208
130
|
applyOptimisticThreads(next);
|
|
209
131
|
}, [applyOptimisticThreads]);
|
|
210
132
|
// Auth material is read at request time via refs so an inline `headers` object literal
|
|
@@ -237,12 +159,12 @@ export const useAgentThreads = (options) => {
|
|
|
237
159
|
const fetchThread = useCallback((threadChatId, threadChatThreadId) => fetchJson(`/agent-threads/${encodeURIComponent(agentId)}/thread?${new URLSearchParams({ chatId: threadChatId, chatThreadId: threadChatThreadId })}`), [agentId, fetchJson]);
|
|
238
160
|
/**
|
|
239
161
|
* Rename a conversation — optimistic with rollback, rejecting on failure so callers
|
|
240
|
-
* can surface it. The new title is applied to
|
|
241
|
-
* row
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
162
|
+
* can surface it. The new title is applied to every local title source: the loaded
|
|
163
|
+
* row and any optimistic entry (whose snapshot the overlay may currently be showing).
|
|
164
|
+
* A conversation the list does not hold (a filter dropped its row) has no local title
|
|
165
|
+
* here — the consumer keeps its own copy of that row and applies the rename itself.
|
|
166
|
+
* Other tabs get the rename via the pusher event; this tab is excluded from the echo
|
|
167
|
+
* (x-socket-id), matching the optimistic apply.
|
|
246
168
|
*/
|
|
247
169
|
const renameThread = useCallback(async (thread, title) => {
|
|
248
170
|
const trimmed = title.trim();
|
|
@@ -253,7 +175,7 @@ export const useAgentThreads = (options) => {
|
|
|
253
175
|
// `undefined` means "that source held no row when we started" — leave whatever is
|
|
254
176
|
// there now alone. Collapsing it to null would let a rollback erase a title that
|
|
255
177
|
// landed from the server while the request was in flight.
|
|
256
|
-
const setLocalTitle = (rowTitle, entryTitle
|
|
178
|
+
const setLocalTitle = (rowTitle, entryTitle) => {
|
|
257
179
|
const row = threadsRef.current.find((item) => agentThreadKey(item) === key);
|
|
258
180
|
if (row && rowTitle !== undefined && row.title !== rowTitle) {
|
|
259
181
|
applyThreads(threadsRef.current.map((item) => (item === row ? { ...item, title: rowTitle } : item)));
|
|
@@ -264,18 +186,10 @@ export const useAgentThreads = (options) => {
|
|
|
264
186
|
next.set(key, { ...entry, title: entryTitle });
|
|
265
187
|
applyOptimisticThreads(next);
|
|
266
188
|
}
|
|
267
|
-
setSelectedThread((prev) => prev && agentThreadKey(prev) === key && selectedTitle !== undefined && prev.title !== selectedTitle
|
|
268
|
-
? { ...prev, title: selectedTitle }
|
|
269
|
-
: prev);
|
|
270
189
|
};
|
|
271
190
|
const previousRowTitle = threadsRef.current.find((item) => agentThreadKey(item) === key)?.title;
|
|
272
191
|
const previousEntryTitle = optimisticThreadsRef.current.get(key)?.title;
|
|
273
|
-
|
|
274
|
-
// fallback (and the fresher list row otherwise) — either way, the right value
|
|
275
|
-
// to restore the selection's title to.
|
|
276
|
-
const current = currentThreadRef.current;
|
|
277
|
-
const previousSelectedTitle = agentThreadKey(current) === key ? current.title : undefined;
|
|
278
|
-
setLocalTitle(trimmed, trimmed, trimmed);
|
|
192
|
+
setLocalTitle(trimmed, trimmed);
|
|
279
193
|
try {
|
|
280
194
|
const socketId = websocketsService.getSocketId();
|
|
281
195
|
const { updated } = await fetchJson(`/agent-threads/${encodeURIComponent(agentId)}/thread`, {
|
|
@@ -294,7 +208,7 @@ export const useAgentThreads = (options) => {
|
|
|
294
208
|
}
|
|
295
209
|
}
|
|
296
210
|
catch (error) {
|
|
297
|
-
setLocalTitle(previousRowTitle, previousEntryTitle
|
|
211
|
+
setLocalTitle(previousRowTitle, previousEntryTitle);
|
|
298
212
|
throw error;
|
|
299
213
|
}
|
|
300
214
|
}, [agentId, fetchJson, applyThreads, applyOptimisticThreads]);
|
|
@@ -494,9 +408,6 @@ export const useAgentThreads = (options) => {
|
|
|
494
408
|
}, [agentId, liveUpdateOptions, fetchThread, applyThreads, applyOptimisticThreads]);
|
|
495
409
|
return {
|
|
496
410
|
threads: displayThreads,
|
|
497
|
-
currentThread,
|
|
498
|
-
isDraftThread,
|
|
499
|
-
setCurrentThread,
|
|
500
411
|
notifyThreadActivity,
|
|
501
412
|
renameThread,
|
|
502
413
|
isLoading,
|