@blocksdiy/react-common 1.48.0 → 1.49.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 +11 -28
- package/dist/components/new-agent-chat.d.ts.map +1 -1
- package/dist/components/new-agent-chat.js +79 -82
- package/dist/hooks/agent-conversation-context.d.ts +32 -0
- package/dist/hooks/agent-conversation-context.d.ts.map +1 -0
- package/dist/hooks/agent-conversation-context.js +39 -0
- package/dist/hooks/agent-thread-live-update.d.ts +35 -64
- package/dist/hooks/agent-thread-live-update.d.ts.map +1 -1
- package/dist/hooks/agent-thread-live-update.js +81 -64
- package/dist/hooks/use-agent-conversation.d.ts +105 -0
- package/dist/hooks/use-agent-conversation.d.ts.map +1 -0
- package/dist/hooks/use-agent-conversation.js +204 -0
- package/dist/hooks/use-agent-threads.d.ts +3 -50
- package/dist/hooks/use-agent-threads.d.ts.map +1 -1
- package/dist/hooks/use-agent-threads.js +53 -150
- package/dist/utils/composer-message.d.ts +57 -0
- package/dist/utils/composer-message.d.ts.map +1 -0
- package/dist/utils/composer-message.js +96 -0
- package/package.json +1 -1
|
@@ -2,8 +2,10 @@ 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 ComposerMessageMeta } from "../utils/composer-message.js";
|
|
5
6
|
import { type MessageRunIds, type RunStartedAt } from "../utils/message-run-ids.js";
|
|
6
7
|
export { useTextGrowthAnimating } from "../hooks/text-growth-animation.js";
|
|
8
|
+
export { type ComposerMessageMeta, FEEDBACK_MESSAGE_NAME, type FeedbackDraft, type FeedbackRate, feedbackComposerMeta, feedbackDraftOf, feedbackMessage, feedbackThreadId, isFeedbackMessage, isFeedbackTag, parseFeedbackContent, parseFeedbackTag, type ParsedFeedback, } from "../utils/composer-message.js";
|
|
7
9
|
export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun, type MessageRunIds, type RunStartedAt, } from "../utils/message-run-ids.js";
|
|
8
10
|
export { type Message, type AssistantMessage, type UserMessage } from "@ag-ui/client";
|
|
9
11
|
export { useRenderTool, useDefaultRenderTool, useHumanInTheLoop, useFrontendTool, ToolCallStatus, } from "@copilotkit/react-core/v2";
|
|
@@ -43,6 +45,7 @@ export interface Agent {
|
|
|
43
45
|
voiceId?: string;
|
|
44
46
|
harness?: string;
|
|
45
47
|
}
|
|
48
|
+
export type OnUserMessageSent = (text: string, message: Omit<UserMessage, "id" | "role">) => void;
|
|
46
49
|
export interface AgentChatContextValue {
|
|
47
50
|
messages: Message[];
|
|
48
51
|
attachments: Attachment[];
|
|
@@ -60,40 +63,14 @@ export interface AgentChatContextValue {
|
|
|
60
63
|
role: "assistant" | "user";
|
|
61
64
|
}[];
|
|
62
65
|
messageById: Map<string, Message>;
|
|
63
|
-
/**
|
|
64
|
-
* The agent run (turn) that produced each message — the `agentRunId` the rows it wrote
|
|
65
|
-
* carry. Replayed messages come attributed by the connect replay, live ones by the run
|
|
66
|
-
* that streams them; messages from before run attribution have no entry. Optional so
|
|
67
|
-
* contexts a host builds by hand (previews, stubs) need not state it; absent reads as
|
|
68
|
-
* "no run known".
|
|
69
|
-
*/
|
|
70
66
|
runIdByMessageId?: MessageRunIds;
|
|
71
|
-
/**
|
|
72
|
-
* When each run in `runIdByMessageId` started (epoch ms) — the time of a turn, which is
|
|
73
|
-
* what a chat shows under a message. A live run is stamped as it starts; a replayed
|
|
74
|
-
* one comes with the server's time. Runs from before run times have no entry.
|
|
75
|
-
*/
|
|
76
67
|
runStartedAt?: RunStartedAt;
|
|
77
68
|
toolMessages: ToolMessage[];
|
|
78
69
|
renderToolCall: ReturnType<typeof useRenderToolCall>;
|
|
79
70
|
sendMessage: (message: Omit<UserMessage, "id" | "role">) => Promise<void>;
|
|
80
71
|
sendFromInputs: () => Promise<void>;
|
|
81
|
-
/**
|
|
82
|
-
* Replay this conversation's transcript from the server, in place.
|
|
83
|
-
*
|
|
84
|
-
* The same round-trip a fresh mount does — the connect endpoint reads the checkpoint and
|
|
85
|
-
* answers with a MESSAGES_SNAPSHOT, which replaces the mirrored list. For turns that ran
|
|
86
|
-
* SERVER-SIDE, which no client streamed and so no client has: without this they appear only
|
|
87
|
-
* after a reload or a conversation switch.
|
|
88
|
-
*
|
|
89
|
-
* A no-op while a run is in flight: that run's own stream is the authoritative view of the
|
|
90
|
-
* conversation, and replacing the list underneath it would fight the events still arriving.
|
|
91
|
-
*/
|
|
92
72
|
reloadMessages: () => Promise<void>;
|
|
93
|
-
/** Resumes a run parked on a server-side interrupt. Rejects if the resume fails. */
|
|
94
|
-
/** Resolves false when a run was already in flight, so nothing was sent. */
|
|
95
73
|
resolveInterrupt: (resume: unknown) => Promise<boolean>;
|
|
96
|
-
/** Answers one parked card; resumes once every card of the turn is answered. */
|
|
97
74
|
answerInterrupt: (answer: {
|
|
98
75
|
toolCallId: string;
|
|
99
76
|
answer: unknown;
|
|
@@ -106,6 +83,10 @@ export interface AgentChatContextValue {
|
|
|
106
83
|
setAttachments: React.Dispatch<SetStateAction<Attachment[]>>;
|
|
107
84
|
setIsDraggingFiles: React.Dispatch<SetStateAction<boolean>>;
|
|
108
85
|
setCurrentThreadId: (currentThreadId: string) => void;
|
|
86
|
+
/** Arms the next send; cleared by send and conversation switch. */
|
|
87
|
+
composerMessageMeta?: ComposerMessageMeta | null;
|
|
88
|
+
setComposerMessageMeta?: (meta: ComposerMessageMeta | null) => void;
|
|
89
|
+
setRunningElsewhere?: (runId: string | null) => void;
|
|
109
90
|
}
|
|
110
91
|
export declare const AgentChatContext: import("react").Context<AgentChatContextValue | null>;
|
|
111
92
|
export declare const useAgentChat: () => AgentChatContextValue;
|
|
@@ -147,9 +128,11 @@ export interface AgentChatRootProps {
|
|
|
147
128
|
* Fires once a user send has been added to the conversation, with its text (empty
|
|
148
129
|
* string for attachment-only sends) — the seam for ChatGPT-style optimistic thread
|
|
149
130
|
* listing: hosts pair it with useAgentThreads' notifyThreadActivity. Covers sends
|
|
150
|
-
* routed through `sendMessage`, not the auto-sent `initialPrompt`.
|
|
131
|
+
* routed through `sendMessage`, not the auto-sent `initialPrompt`. The second argument
|
|
132
|
+
* is the message as sent, for hosts that treat a protocol send (one carrying a `name`)
|
|
133
|
+
* differently from an ordinary turn.
|
|
151
134
|
*/
|
|
152
|
-
onUserMessageSent?:
|
|
135
|
+
onUserMessageSent?: OnUserMessageSent;
|
|
153
136
|
/**
|
|
154
137
|
* Skip the agent-chat block's `initialPrompt` auto-send. For surfaces that mint a fresh
|
|
155
138
|
* thread per conversation (an inbox's "New chat"): the auto-send fires once PER THREAD,
|
|
@@ -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;AACzE,OAAO,EAQL,KAAK,aAAa,EAGlB,KAAK,YAAY,EAClB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EACL,0BAA0B,EAC1B,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,cAAc,GACf,MAAM,2BAA2B,CAAC;AAOnC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,6BAA6B,GAAI,MAAM,MAAM,WAEzD,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,aAAa,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"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"}
|
|
@@ -6,8 +6,10 @@ 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 { buildArmedMessageContent, userTextOfContent } from "../utils/composer-message.js";
|
|
9
10
|
import { claimRunInput, EMPTY_MESSAGE_RUN_IDS, EMPTY_RUN_STARTED_AT, findMessageGroupElement, mergeEntries, MESSAGE_IDS_ATTRIBUTE, messageIdsOfRun, readMessageRunIdsEvent, readRunStartedAtEvent, } from "../utils/message-run-ids.js";
|
|
10
11
|
export { useTextGrowthAnimating } from "../hooks/text-growth-animation.js";
|
|
12
|
+
export { FEEDBACK_MESSAGE_NAME, feedbackComposerMeta, feedbackDraftOf, feedbackMessage, feedbackThreadId, isFeedbackMessage, isFeedbackTag, parseFeedbackContent, parseFeedbackTag, } from "../utils/composer-message.js";
|
|
11
13
|
export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun, } from "../utils/message-run-ids.js";
|
|
12
14
|
export { useRenderTool, useDefaultRenderTool, useHumanInTheLoop, useFrontendTool, ToolCallStatus, } from "@copilotkit/react-core/v2";
|
|
13
15
|
// Client-only `useLayoutEffect`, falls back to `useEffect` during SSR.
|
|
@@ -173,9 +175,9 @@ const uploadAttachments = async ({ attachments, appId, token, }) => {
|
|
|
173
175
|
};
|
|
174
176
|
}));
|
|
175
177
|
};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
// An armed send needs text: the composer is the comment path — a rating alone goes out as
|
|
179
|
+
// `feedbackMessage` — and an unrated marker on its own is an ordinary turn on the server.
|
|
180
|
+
const hasSendableInput = (prompt, attachments, meta) => prompt.trim().length > 0 || (!meta && attachments.length > 0);
|
|
179
181
|
const isRecord = (value) => {
|
|
180
182
|
return typeof value === "object" && value !== null;
|
|
181
183
|
};
|
|
@@ -311,19 +313,10 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
311
313
|
});
|
|
312
314
|
return groups;
|
|
313
315
|
}, [copilotMessages]);
|
|
314
|
-
// Which run produced each message, and when it started (see utils/message-run-ids).
|
|
315
|
-
// Replayed messages: the connect replay sends both as one CUSTOM event, and its word is
|
|
316
|
-
// final — it overwrites what the session recorded. Live messages: RUN_STARTED names the
|
|
317
|
-
// run (the server mints the id, so the one in the event, not the one we sent), times it,
|
|
318
|
-
// and shows the transcript as it stood — the run's input is what sits after the last
|
|
319
|
-
// reply. All of that is recorded with the FIRST message the run streams, and each
|
|
320
|
-
// message it streams is its; first claim wins, as on the server. A run that streams
|
|
321
|
-
// nothing leaves no trace: connect (a reconnect sees the whole transcript, and must not
|
|
322
|
-
// adopt an old unanswered message), or a run that fails before its first token. Cleared
|
|
323
|
-
// with the transcript on a swap.
|
|
324
316
|
const [runIdByMessageId, setRunIdByMessageId] = useState(EMPTY_MESSAGE_RUN_IDS);
|
|
325
317
|
const [runStartedAt, setRunStartedAt] = useState(EMPTY_RUN_STARTED_AT);
|
|
326
318
|
const liveRunRef = useRef(null);
|
|
319
|
+
const ownRunIdsRef = useRef(new Set());
|
|
327
320
|
useEffect(() => {
|
|
328
321
|
if (!copilotAgent) {
|
|
329
322
|
return;
|
|
@@ -349,10 +342,9 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
349
342
|
const { unsubscribe } = copilotAgent.subscribe({
|
|
350
343
|
onRunStartedEvent: ({ event, messages }) => {
|
|
351
344
|
liveRunRef.current = { runId: event.runId, startedAt: event.timestamp ?? Date.now(), input: messages };
|
|
345
|
+
ownRunIdsRef.current.add(event.runId);
|
|
352
346
|
},
|
|
353
347
|
onTextMessageStartEvent: ({ event }) => claimLive(event.messageId),
|
|
354
|
-
// A tool-call-only reply is created by its first tool call, under `parentMessageId`
|
|
355
|
-
// when the server names one and the tool call's own id otherwise (the client's rule).
|
|
356
348
|
onToolCallStartEvent: ({ event }) => claimLive(event.parentMessageId ?? event.toolCallId),
|
|
357
349
|
onRunFinalized: () => {
|
|
358
350
|
liveRunRef.current = null;
|
|
@@ -370,8 +362,6 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
370
362
|
});
|
|
371
363
|
return () => {
|
|
372
364
|
unsubscribe();
|
|
373
|
-
// A run in flight on the agent we leave finalizes without us; it must not claim
|
|
374
|
-
// the next agent's messages.
|
|
375
365
|
liveRunRef.current = null;
|
|
376
366
|
};
|
|
377
367
|
}, [copilotAgent]);
|
|
@@ -393,11 +383,13 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
393
383
|
}, [copilotAgent, currentThreadId]);
|
|
394
384
|
const [prompt, setPrompt] = useState("");
|
|
395
385
|
const [attachments, setAttachments] = useState([]);
|
|
396
|
-
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
386
|
+
const [composerMessageMeta, setComposerMessageMeta] = useState(null);
|
|
387
|
+
// setRunningElsewhere: a run elsewhere reads as this chat's own; own runs match RUN_STARTED id; refused at send time via ref (upload awaits).
|
|
388
|
+
const [runningElsewhereRunId, setRunningElsewhere] = useState(null);
|
|
389
|
+
const [ownRunInFlight, setOwnRunInFlight] = useState(false);
|
|
390
|
+
const isRunningElsewhere = runningElsewhereRunId !== null && !ownRunInFlight && !ownRunIdsRef.current.has(runningElsewhereRunId);
|
|
391
|
+
const isRunningElsewhereRef = useRef(isRunningElsewhere);
|
|
392
|
+
isRunningElsewhereRef.current = isRunningElsewhere;
|
|
401
393
|
const conversationKey = `${chatId ?? ""}|${currentThreadId ?? ""}`;
|
|
402
394
|
const prevConversationKeyRef = useRef(conversationKey);
|
|
403
395
|
useIsomorphicLayoutEffect(() => {
|
|
@@ -410,14 +402,8 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
410
402
|
setRunStartedAt(EMPTY_RUN_STARTED_AT);
|
|
411
403
|
setPrompt("");
|
|
412
404
|
setAttachments([]);
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
// apply pipeline keep applying, each reassigning `agent.messages` WHOLESALE. One late
|
|
416
|
-
// chunk would resurrect the entire previous transcript (and re-arm its auto-executing
|
|
417
|
-
// tool-call follow-ups) under the new conversation's header. Detach here too and
|
|
418
|
-
// re-clear once the drain settles; the reconnect that follows replays on top of the
|
|
419
|
-
// (guaranteed-empty) mirror. On threadId switches the core's fresh-restore re-clears
|
|
420
|
-
// as well — harmless overlap — but on chatId-only switches this is the only re-clear.
|
|
405
|
+
setComposerMessageMeta(null);
|
|
406
|
+
setOwnRunInFlight(false);
|
|
421
407
|
const detachable = copilotAgent;
|
|
422
408
|
const drained = detachable?.detachActiveRun?.();
|
|
423
409
|
if (drained) {
|
|
@@ -429,15 +415,6 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
429
415
|
});
|
|
430
416
|
}
|
|
431
417
|
}, [conversationKey]);
|
|
432
|
-
// CopilotKit reconnects only when its threadId changes — but a conversation is the
|
|
433
|
-
// (chatId, threadId) PAIR, and rows written by surfaces that use the fixed default
|
|
434
|
-
// thread id differ only by chatId. On such a chatId-only switch the library's connect
|
|
435
|
-
// effect sees unchanged deps and does nothing, which would leave the (just cleared)
|
|
436
|
-
// transcript empty forever. Reconnect manually, replicating the core's fresh-restore
|
|
437
|
-
// steps: reset state and drop the replay cursor — cursors are keyed by threadId, so
|
|
438
|
-
// the shared id makes the previous conversation's cursor actively wrong here — then
|
|
439
|
-
// connect (the core detaches any in-flight run itself). The core reads `properties`
|
|
440
|
-
// after an await, so the new chatId is already synced by the time the request builds.
|
|
441
418
|
const prevConnectionRef = useRef({ chatId, threadId: currentThreadId });
|
|
442
419
|
useEffect(() => {
|
|
443
420
|
const previous = prevConnectionRef.current;
|
|
@@ -449,23 +426,16 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
449
426
|
}
|
|
450
427
|
copilotAgent.setState?.({});
|
|
451
428
|
copilotAgent.clearReplayCursor?.(currentThreadId);
|
|
452
|
-
// Same guard convention as the composer send path: a failed reconnect already
|
|
453
|
-
// surfaces through the availability state; the rejection carries no extra signal.
|
|
454
429
|
void copilotkit.connectAgent({ agent: copilotAgent }).catch(() => undefined);
|
|
455
430
|
}, [chatId, currentThreadId, copilotAgent, copilotkit]);
|
|
456
431
|
const [isDraggingFiles, setIsDraggingFiles] = useState(false);
|
|
457
432
|
const attachmentsEnabled = agentChat ? agentChat.disableAttachments !== true : false;
|
|
458
|
-
const canSendMessage = Boolean(agentChatBlock && agentChat && agent && isConfigLoaded && isAgentReady && !isAgentRunning);
|
|
433
|
+
const canSendMessage = Boolean(agentChatBlock && agentChat && agent && isConfigLoaded && isAgentReady && !isAgentRunning && !isRunningElsewhere);
|
|
459
434
|
const isFetchingMessages = !isConfigLoaded || (!isAgentReady && copilotMessages.length === 0);
|
|
460
|
-
|
|
461
|
-
// thinking once the bootstrap connect phase has completed — and that needs the readiness
|
|
462
|
-
// latch, not just `isFetchingMessages`, which goes false as soon as ONE replayed message
|
|
463
|
-
// exists: without `isAgentReady`, thinking leaks true for the tail of every replay, and
|
|
464
|
-
// every load or thread switch flashes the typing dots under the transcript. Real runs are
|
|
465
|
-
// unaffected — sending requires readiness (canSendMessage), so the latch is already set.
|
|
466
|
-
const isThinking = isAgentRunning && !isFetchingMessages && isAgentReady;
|
|
435
|
+
const isThinking = (isAgentRunning || isRunningElsewhere) && !isFetchingMessages && isAgentReady;
|
|
467
436
|
const runAgentWithRetry = useCallback(async (agentToRun, messageCountAtRunStart, forwardedProps) => {
|
|
468
|
-
|
|
437
|
+
// A run elsewhere is on this conversation's checkpoint too: ours would race it.
|
|
438
|
+
if (agentToRun.isRunning || isRunningElsewhereRef.current) {
|
|
469
439
|
return false;
|
|
470
440
|
}
|
|
471
441
|
const runAgentAndThrowError = async () => {
|
|
@@ -488,8 +458,13 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
488
458
|
unsubscribe();
|
|
489
459
|
}
|
|
490
460
|
};
|
|
461
|
+
// In flight until the run ends, whichever way (see `runningElsewhereRunId`) — unless
|
|
462
|
+
// the conversation switched under it, which resets this itself.
|
|
463
|
+
const ranOn = prevConversationKeyRef.current;
|
|
464
|
+
setOwnRunInFlight(true);
|
|
491
465
|
try {
|
|
492
466
|
await runAgentAndThrowError();
|
|
467
|
+
return true;
|
|
493
468
|
}
|
|
494
469
|
catch (error) {
|
|
495
470
|
const hasAgentStartedResponding = agentToRun.messages.length > messageCountAtRunStart;
|
|
@@ -500,24 +475,19 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
500
475
|
}
|
|
501
476
|
throw error;
|
|
502
477
|
}
|
|
503
|
-
|
|
478
|
+
finally {
|
|
479
|
+
if (prevConversationKeyRef.current === ranOn) {
|
|
480
|
+
setOwnRunInFlight(false);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
504
483
|
}, [copilotkit]);
|
|
505
484
|
const sendMessage = useCallback(async (message) => {
|
|
506
|
-
if (!canSendMessage || !copilotAgent || copilotAgent.isRunning) {
|
|
485
|
+
if (!canSendMessage || !copilotAgent || copilotAgent.isRunning || isRunningElsewhereRef.current) {
|
|
507
486
|
return;
|
|
508
487
|
}
|
|
509
488
|
copilotAgent.addMessage({ ...message, id: generateId(), role: "user" });
|
|
510
|
-
// The send seam, fired once the message is IN the conversation, so hosts can react
|
|
511
|
-
// to it (e.g. list the thread optimistically). Covers every send routed through
|
|
512
|
-
// `sendMessage` — composer, empty-state heroes, user-generated chat components —
|
|
513
|
-
// but deliberately NOT the auto-sent `initialPrompt` below, whose hidden variant is
|
|
514
|
-
// a primer rather than something the user wrote.
|
|
515
489
|
if (onUserMessageSent) {
|
|
516
|
-
|
|
517
|
-
const textPart = Array.isArray(content)
|
|
518
|
-
? content.find((part) => part.type === "text")
|
|
519
|
-
: undefined;
|
|
520
|
-
onUserMessageSent(typeof content === "string" ? content : (textPart?.text ?? ""));
|
|
490
|
+
onUserMessageSent(userTextOfContent(message.content), message);
|
|
521
491
|
}
|
|
522
492
|
await runAgentWithRetry(copilotAgent, copilotAgent.messages.length);
|
|
523
493
|
}, [canSendMessage, copilotAgent, runAgentWithRetry, onUserMessageSent]);
|
|
@@ -525,14 +495,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
525
495
|
if (!copilotAgent || copilotAgent.isRunning) {
|
|
526
496
|
return;
|
|
527
497
|
}
|
|
528
|
-
// Replay cursors are keyed by threadId and record what has already been replayed, so a
|
|
529
|
-
// stale one makes connect a no-op — exactly the case here, where the thread is unchanged
|
|
530
|
-
// and only the server-side content moved. Messages are NOT cleared first: connect answers
|
|
531
|
-
// with a full snapshot that replaces them, and clearing would blank the transcript for the
|
|
532
|
-
// round-trip.
|
|
533
498
|
copilotAgent.clearReplayCursor?.(currentThreadId);
|
|
534
|
-
// Same guard convention as the composer send path: a failed reconnect already surfaces
|
|
535
|
-
// through the availability state; the rejection carries no extra signal.
|
|
536
499
|
await copilotkit.connectAgent({ agent: copilotAgent }).catch(() => undefined);
|
|
537
500
|
}, [copilotAgent, copilotkit, currentThreadId]);
|
|
538
501
|
// Not sourced from `useInterrupt`: that hook only hands `resolve` to its own
|
|
@@ -735,17 +698,44 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
735
698
|
return contentArray;
|
|
736
699
|
};
|
|
737
700
|
const sendFromInputs = async () => {
|
|
738
|
-
|
|
701
|
+
const currentMeta = composerMessageMeta;
|
|
702
|
+
if (!canSendMessage || !hasSendableInput(prompt, attachments, currentMeta)) {
|
|
739
703
|
return;
|
|
740
704
|
}
|
|
741
705
|
const currentPrompt = prompt;
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
706
|
+
const composedOn = prevConversationKeyRef.current;
|
|
707
|
+
if (currentMeta) {
|
|
708
|
+
setComposerMessageMeta(null);
|
|
709
|
+
}
|
|
710
|
+
let uploadedAttachments;
|
|
711
|
+
try {
|
|
712
|
+
uploadedAttachments = await uploadAttachments({
|
|
713
|
+
attachments,
|
|
714
|
+
appId,
|
|
715
|
+
token,
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
catch (error) {
|
|
719
|
+
// Failed upload: re-arm unless the conversation switched.
|
|
720
|
+
if (currentMeta && prevConversationKeyRef.current === composedOn) {
|
|
721
|
+
setComposerMessageMeta((armed) => armed ?? currentMeta);
|
|
722
|
+
}
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
// Post-upload: conversation switch → stop; run elsewhere → keep input and re-arm; failed upload handled above.
|
|
726
|
+
if (prevConversationKeyRef.current !== composedOn) {
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (isRunningElsewhereRef.current || copilotAgent?.isRunning) {
|
|
730
|
+
if (currentMeta) {
|
|
731
|
+
setComposerMessageMeta((armed) => armed ?? currentMeta);
|
|
732
|
+
}
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
const composed = buildMessageContentFromInput(currentPrompt, uploadedAttachments ?? []);
|
|
736
|
+
const messageContent = currentMeta
|
|
737
|
+
? buildArmedMessageContent(currentMeta, composed)
|
|
738
|
+
: composed;
|
|
749
739
|
setPrompt("");
|
|
750
740
|
setAttachments([]);
|
|
751
741
|
// Auto-skip any frontend user-choice tool call that's still waiting.
|
|
@@ -791,7 +781,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
791
781
|
});
|
|
792
782
|
}
|
|
793
783
|
}
|
|
794
|
-
await sendMessage({ content: messageContent }).catch(() => undefined);
|
|
784
|
+
await sendMessage(currentMeta?.name ? { content: messageContent, name: currentMeta.name } : { content: messageContent }).catch(() => undefined);
|
|
795
785
|
};
|
|
796
786
|
const addAttachments = (attachments) => {
|
|
797
787
|
if (!attachmentsEnabled) {
|
|
@@ -841,6 +831,9 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
|
|
|
841
831
|
setAttachments: setChatAttachments,
|
|
842
832
|
setIsDraggingFiles: setChatIsDraggingFiles,
|
|
843
833
|
setCurrentThreadId,
|
|
834
|
+
composerMessageMeta,
|
|
835
|
+
setComposerMessageMeta,
|
|
836
|
+
setRunningElsewhere,
|
|
844
837
|
isDraggingFiles,
|
|
845
838
|
isFetchingMessages,
|
|
846
839
|
isThinking,
|
|
@@ -1106,10 +1099,10 @@ export function AgentChatFetching({ asChild = false, render, ...props }) {
|
|
|
1106
1099
|
return (_jsx(ComposeRender, { render: render, asChild: asChild, defaultTag: "div", "data-state": isFetchingMessages ? "open" : "closed", "data-slot": "agent-chat-fetching", ...props }));
|
|
1107
1100
|
}
|
|
1108
1101
|
export function AgentChatInput({ asChild = false, render, onChange, onKeyDown, onPaste, onDragOver, onDragLeave, onDrop, onSubmit, ...props }) {
|
|
1109
|
-
const { setIsDraggingFiles, addAttachments, attachments, prompt, setPrompt, sendFromInputs, isThinking, isAgentReady, isFetchingMessages, agentChat, } = useAgentChat();
|
|
1102
|
+
const { setIsDraggingFiles, addAttachments, attachments, prompt, setPrompt, sendFromInputs, isThinking, isAgentReady, isFetchingMessages, agentChat, composerMessageMeta, } = useAgentChat();
|
|
1110
1103
|
const attachmentsEnabled = agentChat ? agentChat.disableAttachments !== true : false;
|
|
1111
1104
|
const isDisabled = props.disabled || isThinking || !isAgentReady || isFetchingMessages;
|
|
1112
|
-
const isSubmitDisabled = isDisabled || !hasSendableInput(prompt, attachmentsEnabled ? attachments : []);
|
|
1105
|
+
const isSubmitDisabled = isDisabled || !hasSendableInput(prompt, attachmentsEnabled ? attachments : [], composerMessageMeta);
|
|
1113
1106
|
const handleDragOver = useCallback((e) => {
|
|
1114
1107
|
if (!attachmentsEnabled) {
|
|
1115
1108
|
return;
|
|
@@ -1199,10 +1192,14 @@ export function AgentChatInput({ asChild = false, render, onChange, onKeyDown, o
|
|
|
1199
1192
|
: onDrop, "data-slot": "agent-chat-input", ...props }));
|
|
1200
1193
|
}
|
|
1201
1194
|
export function AgentChatSendTrigger({ asChild = false, render, onClick, disabled, ...props }) {
|
|
1202
|
-
const { attachments, prompt, isThinking, isAgentReady, isFetchingMessages, sendFromInputs,
|
|
1195
|
+
const { attachments, prompt, isThinking, isAgentReady, isFetchingMessages, sendFromInputs, composerMessageMeta,
|
|
1203
1196
|
/* , stopGeneration */
|
|
1204
1197
|
} = useAgentChat();
|
|
1205
|
-
const isDisabled = Boolean(disabled ||
|
|
1198
|
+
const isDisabled = Boolean(disabled ||
|
|
1199
|
+
isThinking ||
|
|
1200
|
+
!isAgentReady ||
|
|
1201
|
+
isFetchingMessages ||
|
|
1202
|
+
!hasSendableInput(prompt, attachments, composerMessageMeta));
|
|
1206
1203
|
return (_jsx(ComposeRender, { render: render, asChild: asChild, defaultTag: "button", onClick: (e) => {
|
|
1207
1204
|
if (isDisabled) {
|
|
1208
1205
|
e.preventDefault();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { AgentThreadItem } from "./agent-thread-live-update.js";
|
|
3
|
+
import { type AgentConversationChat, type AgentConversationRef, type UseAgentConversationResult } from "./use-agent-conversation.js";
|
|
4
|
+
import { type UseAgentThreadsOptions, type UseAgentThreadsResult } from "./use-agent-threads.js";
|
|
5
|
+
export type AgentThreadFilters = Pick<UseAgentThreadsOptions, "status" | "source">;
|
|
6
|
+
export interface AgentConversationContextValue {
|
|
7
|
+
agentId: string;
|
|
8
|
+
conversation: AgentConversationRef;
|
|
9
|
+
/** The new chat, nothing sent on it yet. */
|
|
10
|
+
isDraft: boolean;
|
|
11
|
+
/** The index under `filters`; its `renameThread` also keeps the open conversation's remembered title current. */
|
|
12
|
+
threads: UseAgentThreadsResult;
|
|
13
|
+
/** The open conversation's row: listed, else the last listed, else a stand-in (`isStandIn`). */
|
|
14
|
+
row: AgentThreadItem;
|
|
15
|
+
isStandIn: boolean;
|
|
16
|
+
filters: AgentThreadFilters;
|
|
17
|
+
setFilters: (filters: AgentThreadFilters) => void;
|
|
18
|
+
hasActiveFilters: boolean;
|
|
19
|
+
/** After this client sends on the conversation (the chat root's onUserMessageSent). */
|
|
20
|
+
notifyMessageSent: UseAgentConversationResult["notifyMessageSent"];
|
|
21
|
+
}
|
|
22
|
+
export interface AgentConversationProviderProps extends Pick<UseAgentThreadsOptions, "token" | "headers" | "baseUrl"> {
|
|
23
|
+
agentId: string;
|
|
24
|
+
conversation: AgentConversationRef;
|
|
25
|
+
isDraft: boolean;
|
|
26
|
+
/** The chat runtime's side of the bridge (react-common's useAgentChat). */
|
|
27
|
+
chat: AgentConversationChat;
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
}
|
|
30
|
+
export declare function AgentConversationProvider({ agentId, conversation, isDraft, chat, token, headers, baseUrl, children, }: AgentConversationProviderProps): import("react").JSX.Element;
|
|
31
|
+
export declare const useAgentConversationContext: () => AgentConversationContextValue;
|
|
32
|
+
//# sourceMappingURL=agent-conversation-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-conversation-context.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-conversation-context.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAEzB,KAAK,0BAA0B,EAChC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAmB,KAAK,sBAAsB,EAAE,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE/G,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAEnF,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,oBAAoB,CAAC;IACnC,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,iHAAiH;IACjH,OAAO,EAAE,qBAAqB,CAAC;IAC/B,gGAAgG;IAChG,GAAG,EAAE,eAAe,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uFAAuF;IACvF,iBAAiB,EAAE,0BAA0B,CAAC,mBAAmB,CAAC,CAAC;CACpE;AAID,MAAM,WAAW,8BAA+B,SAAQ,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IACnH,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,oBAAoB,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,YAAY,EACZ,OAAO,EACP,IAAI,EACJ,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,GACT,EAAE,8BAA8B,+BA4BhC;AAED,eAAO,MAAM,2BAA2B,QAAO,6BAM9C,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// The headless Root of an agent conversation surface: one provider that owns the conversation
|
|
3
|
+
// index (useAgentThreads) under the surface's filters and the open conversation's bridge to the
|
|
4
|
+
// chat (useAgentConversation), for parts to read through useAgentConversationContext. The
|
|
5
|
+
// agent-layout registry item renders it; a custom UI over the same behaviour can too.
|
|
6
|
+
import { createContext, useContext, useState } from "react";
|
|
7
|
+
import { useAgentConversation, } from "./use-agent-conversation.js";
|
|
8
|
+
import { useAgentThreads } from "./use-agent-threads.js";
|
|
9
|
+
const AgentConversationContext = createContext(null);
|
|
10
|
+
export function AgentConversationProvider({ agentId, conversation, isDraft, chat, token, headers, baseUrl, children, }) {
|
|
11
|
+
const [filters, setFilters] = useState({});
|
|
12
|
+
const threads = useAgentThreads({ agentId, status: filters.status, source: filters.source, token, headers, baseUrl });
|
|
13
|
+
const { row, isStandIn, renameThread, notifyMessageSent } = useAgentConversation({
|
|
14
|
+
threads,
|
|
15
|
+
agentId,
|
|
16
|
+
conversation,
|
|
17
|
+
isDraft,
|
|
18
|
+
chat,
|
|
19
|
+
});
|
|
20
|
+
return (_jsx(AgentConversationContext.Provider, { value: {
|
|
21
|
+
agentId,
|
|
22
|
+
conversation,
|
|
23
|
+
isDraft,
|
|
24
|
+
threads: { ...threads, renameThread },
|
|
25
|
+
row,
|
|
26
|
+
isStandIn,
|
|
27
|
+
filters,
|
|
28
|
+
setFilters,
|
|
29
|
+
hasActiveFilters: Boolean(filters.status ?? filters.source),
|
|
30
|
+
notifyMessageSent,
|
|
31
|
+
}, children: children }));
|
|
32
|
+
}
|
|
33
|
+
export const useAgentConversationContext = () => {
|
|
34
|
+
const context = useContext(AgentConversationContext);
|
|
35
|
+
if (!context) {
|
|
36
|
+
throw new Error("useAgentConversationContext must be used within <AgentConversationProvider>.");
|
|
37
|
+
}
|
|
38
|
+
return context;
|
|
39
|
+
};
|
|
@@ -1,94 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
export type
|
|
1
|
+
declare const THREAD_STATUSES: readonly ["idle", "interrupted", "waiting_on_agent", "busy", "error"];
|
|
2
|
+
export type AgentThreadStatus = (typeof THREAD_STATUSES)[number];
|
|
3
|
+
declare const THREAD_SOURCES: readonly ["direct_chat", "send_message", "feedback", "self_config_resume", "unknown"];
|
|
4
|
+
export type AgentThreadSource = (typeof THREAD_SOURCES)[number] | "agent_call";
|
|
3
5
|
export interface AgentThreadItem {
|
|
4
6
|
agentId: string | null;
|
|
5
|
-
/** Never null — pass back verbatim to open the conversation. */
|
|
6
7
|
chatId: string;
|
|
7
|
-
/** Never null — pass back verbatim to open the conversation. */
|
|
8
8
|
chatThreadId: string;
|
|
9
9
|
source: AgentThreadSource;
|
|
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
13
|
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
14
|
fromSourceChatThreadId?: string | null;
|
|
26
|
-
/** The message the feedback was about, when it was about one rather than the conversation. */
|
|
27
15
|
fromSourceMessageId?: string | null;
|
|
28
|
-
/** First seen by the index — NOT thread creation time. */
|
|
29
16
|
firstSeenAt: string;
|
|
30
17
|
lastActivityAt: string;
|
|
18
|
+
runningSince?: string | null;
|
|
19
|
+
runningRunId?: string | null;
|
|
31
20
|
}
|
|
32
|
-
/** The (chatId, chatThreadId) pair — the only stable identity of a thread row. */
|
|
33
21
|
export declare const agentThreadKey: (thread: Pick<AgentThreadItem, "chatId" | "chatThreadId">) => string;
|
|
34
|
-
/**
|
|
35
|
-
* Whether a realtime payload's `thread` is a complete list-item row. The server
|
|
36
|
-
* serializes the exact item shape into every event, so this normally passes; when
|
|
37
|
-
* it doesn't (contract drift), the consumer falls back to the getOne endpoint.
|
|
38
|
-
*/
|
|
39
22
|
export declare const isAgentThreadItem: (value: unknown) => value is AgentThreadItem;
|
|
40
|
-
/**
|
|
41
|
-
* Provisional row title from the prompt that started a conversation (ChatGPT-style):
|
|
42
|
-
* one line, whitespace collapsed, capped like the server's generated titles. Null when
|
|
43
|
-
* there's nothing usable (attachment-only sends) — the UI keeps its id-based fallback.
|
|
44
|
-
*/
|
|
45
23
|
export declare const provisionalThreadTitle: (prompt: string | undefined) => string | null;
|
|
46
|
-
/**
|
|
47
|
-
* The optimistic row for a user send on `thread`, replacing `previous` (its current
|
|
48
|
-
* optimistic entry, if any): the same identity, its recency bumped to `now`, and a title
|
|
49
|
-
* that never regresses — the provisional title already shown, else the row's own, else
|
|
50
|
-
* one from this prompt (null when the prompt has nothing usable).
|
|
51
|
-
*/
|
|
52
24
|
export declare const optimisticThreadAfterSend: (thread: AgentThreadItem, previous: AgentThreadItem | undefined, prompt: string | undefined, now: string) => AgentThreadItem;
|
|
25
|
+
export type RemovedAgentThread = Pick<AgentThreadItem, "chatId" | "chatThreadId" | "runningRunId">;
|
|
26
|
+
export declare const removeAgentThread: (threads: AgentThreadItem[], optimistic: ReadonlyMap<string, AgentThreadItem>, removed: RemovedAgentThread) => {
|
|
27
|
+
threads: AgentThreadItem[];
|
|
28
|
+
optimistic: ReadonlyMap<string, AgentThreadItem>;
|
|
29
|
+
} | null;
|
|
53
30
|
export interface AgentThreadLiveUpdateOptions {
|
|
54
|
-
/** Active server-side filters — an updated row that stops matching leaves the list. */
|
|
55
31
|
status?: AgentThreadStatus;
|
|
56
32
|
source?: AgentThreadSource;
|
|
57
33
|
chatId?: string;
|
|
58
|
-
/** Active sort, mirroring the endpoint's defaults (lastActivity, desc). */
|
|
59
34
|
sort?: "lastActivity" | "firstSeen";
|
|
60
35
|
order?: "asc" | "desc";
|
|
61
|
-
/** True when more pages exist server-side beyond the loaded window. */
|
|
62
36
|
hasMore?: boolean;
|
|
63
37
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Merge one realtime thread update into a loaded list. Pure; returns the input array
|
|
66
|
-
* untouched when the event is stale, filtered out, or out of the loaded window.
|
|
67
|
-
*
|
|
68
|
-
* - Out-of-order delivery can't regress a row: events older than the loaded row (by
|
|
69
|
-
* lastActivityAt) are dropped — checked FIRST, so a stale event can't evict a
|
|
70
|
-
* still-current row through the filter check below either. Renames keep
|
|
71
|
-
* lastActivityAt, so equal passes.
|
|
72
|
-
* - A row that stops matching the active filters is removed; a matching one is upserted
|
|
73
|
-
* and the list re-sorted by the active sort (stable, so ties keep their server order).
|
|
74
|
-
* - A NEW row sorting past every loaded row while more pages exist may belong beyond the
|
|
75
|
-
* loaded window; it's skipped so the list stays contiguous with the pagination cursor
|
|
76
|
-
* (unreachable under the default lastActivity-desc sort, where fresh activity sorts first).
|
|
77
|
-
*/
|
|
78
|
-
/** Whether a row passes the list's active server-side filters. */
|
|
79
38
|
export declare const matchesAgentThreadFilters: (item: AgentThreadItem, options?: AgentThreadLiveUpdateOptions) => boolean;
|
|
80
39
|
export declare const applyAgentThreadUpdate: (threads: AgentThreadItem[], incoming: AgentThreadItem, options?: AgentThreadLiveUpdateOptions) => AgentThreadItem[];
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
* when nothing changes.
|
|
87
|
-
*
|
|
88
|
-
* An entry steps aside once the loaded row has recorded MORE turns than it did: turnCount
|
|
89
|
-
* is server-monotonic, so the handover can't be held off by a client clock running ahead
|
|
90
|
-
* of the server (the entry's lastActivityAt is stamped locally, and the merge's staleness
|
|
91
|
-
* guard compares it against server timestamps).
|
|
92
|
-
*/
|
|
40
|
+
/** Parked removal for an in-flight refresh walk (see reconcileWalkWithLive). */
|
|
41
|
+
export interface ParkedRemoval {
|
|
42
|
+
removed: RemovedAgentThread;
|
|
43
|
+
}
|
|
44
|
+
export declare const reconcileWalkWithLive: (items: AgentThreadItem[], live: ReadonlyMap<string, AgentThreadItem | ParkedRemoval>, options?: AgentThreadLiveUpdateOptions) => AgentThreadItem[];
|
|
93
45
|
export declare const overlayOptimisticThreads: (threads: AgentThreadItem[], optimistic: ReadonlyMap<string, AgentThreadItem>, options?: AgentThreadLiveUpdateOptions) => AgentThreadItem[];
|
|
46
|
+
export declare const retireOptimisticThreadsTakenOver: (optimistic: ReadonlyMap<string, AgentThreadItem>, threads: AgentThreadItem[]) => ReadonlyMap<string, AgentThreadItem>;
|
|
47
|
+
/** The server's cap on a title a person sets (RENAME_TITLE_MAX_CHARS in wf-actions-py). */
|
|
48
|
+
export declare const RENAME_TITLE_MAX_CHARS = 255;
|
|
49
|
+
/** Conversation-index groups, in display order; a row's group follows its write-once `source`. */
|
|
50
|
+
export declare const AGENT_THREAD_GROUPS: readonly ["chats", "workflows", "feedback"];
|
|
51
|
+
export type AgentThreadGroup = (typeof AGENT_THREAD_GROUPS)[number];
|
|
52
|
+
/** Where a row files; a source this copy predates files under chats, so a row never renders nowhere. */
|
|
53
|
+
export declare const agentThreadGroup: (source: AgentThreadSource) => AgentThreadGroup;
|
|
54
|
+
export interface AgentThreadGroupRows {
|
|
55
|
+
group: AgentThreadGroup;
|
|
56
|
+
rows: AgentThreadItem[];
|
|
57
|
+
}
|
|
58
|
+
/** Rows by group in group order, each group in input order; empty groups dropped. */
|
|
59
|
+
export declare const groupAgentThreads: (threads: readonly AgentThreadItem[]) => AgentThreadGroupRows[];
|
|
60
|
+
/** A thread the agent opened to act on feedback — by `type`, or by `source` for rows from before the column. */
|
|
61
|
+
export declare const isFeedbackThread: (row: Pick<AgentThreadItem, "type" | "source">) => boolean;
|
|
62
|
+
/** The conversation a feedback thread was opened from, when `threads` lists it (same chat, the row's pointer). */
|
|
63
|
+
export declare const feedbackSourceThread: (row: AgentThreadItem, threads: readonly AgentThreadItem[]) => AgentThreadItem | undefined;
|
|
64
|
+
export {};
|
|
94
65
|
//# sourceMappingURL=agent-thread-live-update.d.ts.map
|