@agents24/chat-react 0.1.9 → 0.2.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/README.md +21 -5
- package/dist/{chunk-E3TJ3I6G.js → chunk-FFO6KEB4.js} +2 -1
- package/dist/chunk-FFO6KEB4.js.map +1 -0
- package/dist/controller-actions.d.ts +45 -0
- package/dist/controller-helpers.d.ts +27 -0
- package/dist/controller.d.ts +2 -7
- package/dist/index.cjs +600 -225
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +543 -175
- package/dist/index.js.map +1 -1
- package/dist/message-lifecycle.d.ts +15 -0
- package/dist/model.d.ts +2 -1
- package/dist/renderers.d.ts +1 -1
- package/dist/sse.d.ts +3 -1
- package/dist/templates/agent-chat-shell.d.ts +13 -1
- package/dist/templates/index.cjs +91 -24
- package/dist/templates/index.cjs.map +1 -1
- package/dist/templates/index.js +89 -24
- package/dist/templates/index.js.map +1 -1
- package/dist/transport.d.ts +14 -0
- package/dist/types.d.ts +63 -6
- package/dist/ui/adapters.d.ts +1 -2
- package/dist/ui/agent-chat-actions.d.ts +16 -0
- package/dist/ui/agent-chat-composer.d.ts +28 -0
- package/dist/ui/agent-chat-message.d.ts +40 -0
- package/dist/ui/agent-response-timeline.d.ts +24 -0
- package/dist/ui/index.cjs +944 -206
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.js +932 -202
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/mcp-connect-required-card.d.ts +13 -0
- package/package.json +2 -1
- package/dist/chunk-E3TJ3I6G.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
ChatTransportError: () => ChatTransportError,
|
|
33
34
|
DEFAULT_THREAD_PAGE_SIZE: () => DEFAULT_THREAD_PAGE_SIZE,
|
|
34
35
|
DefaultChatPart: () => DefaultChatPart,
|
|
35
36
|
DefaultToolPart: () => DefaultToolPart,
|
|
@@ -44,33 +45,38 @@ __export(index_exports, {
|
|
|
44
45
|
MessageScroller: () => import_message_scroller2.MessageScroller,
|
|
45
46
|
activeRunIdFromThread: () => activeRunIdFromThread,
|
|
46
47
|
activeRunIdFromThreadDetail: () => activeRunIdFromThreadDetail,
|
|
48
|
+
appendStableStreamingTurn: () => appendStableStreamingTurn,
|
|
47
49
|
assistantTextFromParts: () => assistantTextFromParts,
|
|
48
50
|
assistantTextFromResponseBlocks: () => assistantTextFromResponseBlocks,
|
|
51
|
+
autoAttachRunIdFromThread: () => autoAttachRunIdFromThread,
|
|
49
52
|
compressionFromContextWindow: () => compressionFromContextWindow,
|
|
50
53
|
consumeSseResponse: () => consumeSseResponse,
|
|
51
54
|
createChatId: () => createChatId,
|
|
52
55
|
createFetchChatTransport: () => createFetchChatTransport,
|
|
56
|
+
findStableAssistantMessageIndex: () => findStableAssistantMessageIndex,
|
|
53
57
|
getActiveStreamingTextPartId: () => getActiveStreamingTextPartId,
|
|
54
58
|
hasStaleUnfinishedAssistantCache: () => hasStaleUnfinishedAssistantCache,
|
|
55
59
|
hasUnfinishedAssistantMessage: () => hasUnfinishedAssistantMessage,
|
|
56
60
|
isActiveStreamingTextPart: () => isActiveStreamingTextPart,
|
|
61
|
+
isAutoAttachThreadStatus: () => isAutoAttachThreadStatus,
|
|
57
62
|
isRunningThreadStatus: () => isRunningThreadStatus,
|
|
58
63
|
latestContextWindowFromThread: () => latestContextWindowFromThread,
|
|
59
64
|
mergeContextWindow: () => mergeContextWindow,
|
|
60
65
|
mergeContextWindowUpdate: () => mergeContextWindowUpdate,
|
|
61
66
|
mergeReasoningSteps: () => mergeReasoningSteps,
|
|
67
|
+
mergeStoredThreadsForRefresh: () => mergeStoredThreadsForRefresh,
|
|
62
68
|
normalizeContextCompression: () => normalizeContextCompression,
|
|
63
69
|
normalizeContextWindow: () => normalizeContextWindow,
|
|
64
70
|
parseSseBlock: () => parseSseBlock,
|
|
65
71
|
partsFromResponseBlocks: () => partsFromResponseBlocks,
|
|
66
72
|
reasoningStepsFromParts: () => reasoningStepsFromParts,
|
|
67
73
|
renderChatPart: () => renderChatPart,
|
|
68
|
-
textFromFinalOutput: () => textFromFinalOutput,
|
|
69
74
|
threadActivityDate: () => threadActivityDate,
|
|
70
75
|
threadDetailToMessages: () => threadDetailToMessages,
|
|
71
76
|
threadPaging: () => threadPaging,
|
|
72
77
|
titleFromMessage: () => titleFromMessage,
|
|
73
78
|
toolStateFromStatus: () => toolStateFromStatus,
|
|
79
|
+
upsertStableAssistantMessage: () => upsertStableAssistantMessage,
|
|
74
80
|
useAgents24ChatController: () => useAgents24ChatController,
|
|
75
81
|
useMessageScroller: () => import_message_scroller2.useMessageScroller,
|
|
76
82
|
useMessageScrollerScrollable: () => import_message_scroller2.useMessageScrollerScrollable,
|
|
@@ -80,7 +86,79 @@ __export(index_exports, {
|
|
|
80
86
|
module.exports = __toCommonJS(index_exports);
|
|
81
87
|
|
|
82
88
|
// src/controller.ts
|
|
89
|
+
var import_react2 = require("react");
|
|
90
|
+
|
|
91
|
+
// src/controller-actions.ts
|
|
83
92
|
var import_react = require("react");
|
|
93
|
+
function useControllerMessageActions(input) {
|
|
94
|
+
const handleCopy = (0, import_react.useCallback)((content, messageId) => {
|
|
95
|
+
navigator.clipboard?.writeText(content);
|
|
96
|
+
input.setCopiedMessageId(messageId);
|
|
97
|
+
setTimeout(() => input.setCopiedMessageId(null), 200);
|
|
98
|
+
}, [input]);
|
|
99
|
+
const handleLike = (0, import_react.useCallback)(async (msg) => {
|
|
100
|
+
const nextLiked = !input.liked[msg.id];
|
|
101
|
+
input.setLiked((prev) => ({ ...prev, [msg.id]: nextLiked }));
|
|
102
|
+
if (nextLiked) input.setDisliked((prev) => ({ ...prev, [msg.id]: false }));
|
|
103
|
+
}, [input]);
|
|
104
|
+
const handleDislike = (0, import_react.useCallback)(async (msg) => {
|
|
105
|
+
const nextDisliked = !input.disliked[msg.id];
|
|
106
|
+
input.setDisliked((prev) => ({ ...prev, [msg.id]: nextDisliked }));
|
|
107
|
+
if (nextDisliked) input.setLiked((prev) => ({ ...prev, [msg.id]: false }));
|
|
108
|
+
}, [input]);
|
|
109
|
+
const handleRetry = (0, import_react.useCallback)(async (msg) => {
|
|
110
|
+
const index = input.messagesRef.current.findIndex((message) => message.id === msg.id);
|
|
111
|
+
if (index <= 0) return;
|
|
112
|
+
const userMessage = input.messagesRef.current[index - 1];
|
|
113
|
+
if (userMessage.role !== "user") return;
|
|
114
|
+
const trimmed = input.messagesRef.current.slice(0, index);
|
|
115
|
+
input.setMessages(trimmed);
|
|
116
|
+
input.messagesRef.current = trimmed;
|
|
117
|
+
if (input.activeThreadIdRef.current) input.persistThread(input.activeThreadIdRef.current, trimmed);
|
|
118
|
+
await input.handleSubmit({ text: userMessage.content, files: userMessage.attachments || [] });
|
|
119
|
+
}, [input]);
|
|
120
|
+
const upsertLiveVoiceMessage = (0, import_react.useCallback)((payload) => {
|
|
121
|
+
const content = payload.content?.trim() ?? "";
|
|
122
|
+
if (!content && !payload.citations?.length && !payload.reasoningSteps?.length) return;
|
|
123
|
+
input.setMessages((prev) => {
|
|
124
|
+
const currentId = input.liveVoiceIdsRef.current[payload.role];
|
|
125
|
+
const index = currentId ? prev.findIndex((message) => message.id === currentId) : -1;
|
|
126
|
+
const nextMessage = {
|
|
127
|
+
id: currentId || input.createId(),
|
|
128
|
+
role: payload.role,
|
|
129
|
+
content,
|
|
130
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
131
|
+
isFinal: Boolean(payload.isFinal),
|
|
132
|
+
isVoice: payload.role === "user",
|
|
133
|
+
parts: content ? [{ id: input.createId(), type: "text", kind: "text", text: content }] : [],
|
|
134
|
+
citations: payload.citations,
|
|
135
|
+
reasoningSteps: payload.reasoningSteps
|
|
136
|
+
};
|
|
137
|
+
const next = index === -1 ? [...prev, nextMessage] : prev.map((message, itemIndex) => itemIndex === index ? { ...message, ...nextMessage } : message);
|
|
138
|
+
input.liveVoiceIdsRef.current[payload.role] = payload.isFinal ? void 0 : nextMessage.id;
|
|
139
|
+
input.messagesRef.current = next;
|
|
140
|
+
if (input.activeThreadIdRef.current) input.persistThread(input.activeThreadIdRef.current, next);
|
|
141
|
+
return next;
|
|
142
|
+
});
|
|
143
|
+
}, [input]);
|
|
144
|
+
const startNewThread = (0, import_react.useCallback)(() => {
|
|
145
|
+
input.detachActiveStream();
|
|
146
|
+
input.requestSeqRef.current += 1;
|
|
147
|
+
input.activeThreadIdRef.current = null;
|
|
148
|
+
input.loadedThreadIdRef.current = null;
|
|
149
|
+
input.nextBeforeTurnIndexRef.current = null;
|
|
150
|
+
input.hasOlderTurnsRef.current = false;
|
|
151
|
+
input.storage.setActiveThreadId?.(null);
|
|
152
|
+
input.onActiveThreadIdChange?.(null);
|
|
153
|
+
input.setMessages([]);
|
|
154
|
+
input.messagesRef.current = [];
|
|
155
|
+
input.setHasOlderTurns(false);
|
|
156
|
+
input.setIsLoadingOlder(false);
|
|
157
|
+
input.setContextStatus(null);
|
|
158
|
+
input.setLoadingHistory(false);
|
|
159
|
+
}, [input]);
|
|
160
|
+
return { handleCopy, handleDislike, handleLike, handleRetry, startNewThread, upsertLiveVoiceMessage };
|
|
161
|
+
}
|
|
84
162
|
|
|
85
163
|
// src/context-window.ts
|
|
86
164
|
var SOURCE_PRIORITY = {
|
|
@@ -203,7 +281,8 @@ function compressionFromContextWindow(contextWindow) {
|
|
|
203
281
|
|
|
204
282
|
// src/model.ts
|
|
205
283
|
var DEFAULT_THREAD_PAGE_SIZE = 5;
|
|
206
|
-
var isRunningThreadStatus = (status) => (/* @__PURE__ */ new Set(["queued", "running", "cancelling"])).has(String(status || "").toLowerCase());
|
|
284
|
+
var isRunningThreadStatus = (status) => (/* @__PURE__ */ new Set(["queued", "running", "cancelling", "paused"])).has(String(status || "").toLowerCase());
|
|
285
|
+
var isAutoAttachThreadStatus = (status) => (/* @__PURE__ */ new Set(["queued", "running", "cancelling"])).has(String(status || "").toLowerCase());
|
|
207
286
|
var createChatId = () => {
|
|
208
287
|
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
|
209
288
|
return `chat-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -215,12 +294,6 @@ var titleFromMessage = (text, files = []) => {
|
|
|
215
294
|
var threadActivityDate = (thread) => String(thread.updated_at || thread.last_activity_at || thread.created_at || (/* @__PURE__ */ new Date()).toISOString());
|
|
216
295
|
var assistantTextFromResponseBlocks = (blocks) => (blocks || []).filter((block) => block.kind === "assistant_text" && typeof block.text === "string").map((block) => String(block.text)).join("\n\n").trim();
|
|
217
296
|
var assistantTextFromParts = (parts) => (parts || []).filter((part) => part.kind === "text").map((part) => part.text).join("\n\n").trim();
|
|
218
|
-
var textFromFinalOutput = (value) => {
|
|
219
|
-
if (typeof value === "string") return value;
|
|
220
|
-
if (!value || typeof value !== "object") return "";
|
|
221
|
-
const record = value;
|
|
222
|
-
return String(record.message || record.text || record.answer || "");
|
|
223
|
-
};
|
|
224
297
|
var displayTextWithoutInlineAttachments = (text, hasAttachments) => {
|
|
225
298
|
if (!hasAttachments) return text;
|
|
226
299
|
const marker = "Attached text file (";
|
|
@@ -267,7 +340,7 @@ var responseBlocksFromTurn = (turn) => {
|
|
|
267
340
|
var assistantTextFromEvents = (events) => {
|
|
268
341
|
const assistantText = latestEventPayloadValue(events, "assistant_output_text");
|
|
269
342
|
if (typeof assistantText === "string" && assistantText.trim()) return assistantText;
|
|
270
|
-
return
|
|
343
|
+
return "";
|
|
271
344
|
};
|
|
272
345
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
273
346
|
var optionalString = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
@@ -369,8 +442,17 @@ var partsFromResponseBlocks = (blocks, fallbackText) => {
|
|
|
369
442
|
});
|
|
370
443
|
return;
|
|
371
444
|
}
|
|
372
|
-
if (block.kind === "
|
|
373
|
-
|
|
445
|
+
if (block.kind === "hitl_request") {
|
|
446
|
+
const hitl = asRecord(block.hitl) || block;
|
|
447
|
+
parts.push({
|
|
448
|
+
id,
|
|
449
|
+
type: "hitl",
|
|
450
|
+
kind: "hitl",
|
|
451
|
+
hitl,
|
|
452
|
+
interruptId: optionalString(block.interruptId) || optionalString(hitl.interrupt_id) || null,
|
|
453
|
+
hitlKind: optionalString(block.hitlKind) || optionalString(hitl.kind) || null,
|
|
454
|
+
raw: block
|
|
455
|
+
});
|
|
374
456
|
return;
|
|
375
457
|
}
|
|
376
458
|
if (block.kind === "error") {
|
|
@@ -442,7 +524,7 @@ var turnToMessages = (turn, activeRunId) => {
|
|
|
442
524
|
attachments
|
|
443
525
|
});
|
|
444
526
|
}
|
|
445
|
-
const assistantText = turn.assistant_output_text || assistantTextFromResponseBlocks(responseBlocks) || assistantTextFromEvents(turn.run_events)
|
|
527
|
+
const assistantText = turn.assistant_output_text || assistantTextFromResponseBlocks(responseBlocks) || assistantTextFromEvents(turn.run_events);
|
|
446
528
|
if (assistantText || responseBlocks.length > 0 || isRunning) {
|
|
447
529
|
const parts = partsFromResponseBlocks(responseBlocks, assistantText);
|
|
448
530
|
messages.push({
|
|
@@ -496,6 +578,23 @@ var activeRunIdFromThread = (thread) => {
|
|
|
496
578
|
const lastRunId = hasCamelLastRunId ? thread?.lastRunId : thread?.last_run_id || thread?.lastRunId;
|
|
497
579
|
return lastRunId && isRunningThreadStatus(lastRunStatus) ? String(lastRunId) : null;
|
|
498
580
|
};
|
|
581
|
+
var autoAttachRunIdFromThread = (thread) => {
|
|
582
|
+
const hasCamelActiveRun = Boolean(
|
|
583
|
+
thread && Object.prototype.hasOwnProperty.call(thread, "activeRun")
|
|
584
|
+
);
|
|
585
|
+
const hasCamelLastRunStatus = Boolean(
|
|
586
|
+
thread && Object.prototype.hasOwnProperty.call(thread, "lastRunStatus")
|
|
587
|
+
);
|
|
588
|
+
const hasCamelLastRunId = Boolean(
|
|
589
|
+
thread && Object.prototype.hasOwnProperty.call(thread, "lastRunId")
|
|
590
|
+
);
|
|
591
|
+
const activeRun = hasCamelActiveRun ? thread?.activeRun || null : thread?.active_run || null;
|
|
592
|
+
const lastRunStatus = hasCamelLastRunStatus ? thread?.lastRunStatus || null : thread?.last_run_status || thread?.lastRunStatus || null;
|
|
593
|
+
const activeRunId = activeRun?.run_id ? String(activeRun.run_id) : "";
|
|
594
|
+
if (activeRunId && isAutoAttachThreadStatus(activeRun?.status || lastRunStatus)) return activeRunId;
|
|
595
|
+
const lastRunId = hasCamelLastRunId ? thread?.lastRunId : thread?.last_run_id || thread?.lastRunId;
|
|
596
|
+
return lastRunId && isAutoAttachThreadStatus(lastRunStatus) ? String(lastRunId) : null;
|
|
597
|
+
};
|
|
499
598
|
var hasUnfinishedAssistantMessage = (messages) => Boolean(messages?.some((message) => message.role === "assistant" && message.isFinal === false));
|
|
500
599
|
var hasStaleUnfinishedAssistantCache = (thread) => hasUnfinishedAssistantMessage(thread?.messages) && !activeRunIdFromThread(thread);
|
|
501
600
|
var activeRunIdFromThreadDetail = (thread) => {
|
|
@@ -505,7 +604,7 @@ var activeRunIdFromThreadDetail = (thread) => {
|
|
|
505
604
|
return runningTurn?.run_id ? String(runningTurn.run_id) : null;
|
|
506
605
|
};
|
|
507
606
|
|
|
508
|
-
// src/controller.ts
|
|
607
|
+
// src/controller-helpers.ts
|
|
509
608
|
var threadSummaryToStored = (thread) => ({
|
|
510
609
|
...thread,
|
|
511
610
|
id: String(thread.id),
|
|
@@ -514,6 +613,121 @@ var threadSummaryToStored = (thread) => ({
|
|
|
514
613
|
messages: [],
|
|
515
614
|
isHydrated: false
|
|
516
615
|
});
|
|
616
|
+
var isThreadNotFoundError = (error) => {
|
|
617
|
+
if (!error || typeof error !== "object" || !("status" in error)) return false;
|
|
618
|
+
return Number(error.status) === 404;
|
|
619
|
+
};
|
|
620
|
+
var isAbortError = (error) => {
|
|
621
|
+
if (!error || typeof error !== "object") return false;
|
|
622
|
+
const maybe = error;
|
|
623
|
+
return maybe.name === "AbortError" || String(maybe.message || "").toLowerCase().includes("aborted");
|
|
624
|
+
};
|
|
625
|
+
var abortDetachedStream = (controller) => {
|
|
626
|
+
if (!controller || controller.signal.aborted) return;
|
|
627
|
+
try {
|
|
628
|
+
const reason = typeof DOMException !== "undefined" ? new DOMException("Chat stream detached.", "AbortError") : new Error("Chat stream detached.");
|
|
629
|
+
controller.abort(reason);
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
var hasRunState = (thread) => Boolean(
|
|
634
|
+
thread.active_run || thread.activeRun || thread.last_run_id || thread.lastRunId || thread.last_run_status || thread.lastRunStatus || thread.isRunning
|
|
635
|
+
);
|
|
636
|
+
var mergeStoredThreadsForRefresh = (currentThreads, serverThreads) => {
|
|
637
|
+
const currentById = new Map(currentThreads.map((thread) => [thread.id, thread]));
|
|
638
|
+
const serverIds = new Set(serverThreads.map((thread) => thread.id));
|
|
639
|
+
const mergedServerThreads = serverThreads.map((serverThread) => {
|
|
640
|
+
const current = currentById.get(serverThread.id);
|
|
641
|
+
if (!current) return serverThread;
|
|
642
|
+
const incomingHasRunState = hasRunState(serverThread);
|
|
643
|
+
return {
|
|
644
|
+
...current,
|
|
645
|
+
...serverThread,
|
|
646
|
+
messages: current.messages || [],
|
|
647
|
+
isHydrated: current.isHydrated,
|
|
648
|
+
hasOlderTurns: current.hasOlderTurns,
|
|
649
|
+
nextBeforeTurnIndex: current.nextBeforeTurnIndex,
|
|
650
|
+
active_run: incomingHasRunState ? serverThread.active_run : current.active_run,
|
|
651
|
+
activeRun: incomingHasRunState ? serverThread.activeRun : current.activeRun,
|
|
652
|
+
last_run_id: incomingHasRunState ? serverThread.last_run_id : current.last_run_id,
|
|
653
|
+
lastRunId: incomingHasRunState ? serverThread.lastRunId : current.lastRunId,
|
|
654
|
+
last_run_status: incomingHasRunState ? serverThread.last_run_status : current.last_run_status,
|
|
655
|
+
lastRunStatus: incomingHasRunState ? serverThread.lastRunStatus : current.lastRunStatus,
|
|
656
|
+
lastEventSeq: incomingHasRunState ? serverThread.lastEventSeq : current.lastEventSeq,
|
|
657
|
+
isRunning: incomingHasRunState ? serverThread.isRunning : current.isRunning
|
|
658
|
+
};
|
|
659
|
+
});
|
|
660
|
+
const localOnlyThreads = currentThreads.filter((thread) => {
|
|
661
|
+
if (serverIds.has(thread.id)) return false;
|
|
662
|
+
return Boolean(thread.isHydrated || thread.messages?.length || thread.isRunning || thread.activeRun || thread.active_run);
|
|
663
|
+
});
|
|
664
|
+
return [...mergedServerThreads, ...localOnlyThreads];
|
|
665
|
+
};
|
|
666
|
+
var stableStringify = (value) => JSON.stringify(value ?? null);
|
|
667
|
+
var sameStoredThread = (left, right) => {
|
|
668
|
+
if (left === right) return true;
|
|
669
|
+
if (!left || !right) return false;
|
|
670
|
+
return stableStringify(left) === stableStringify(right);
|
|
671
|
+
};
|
|
672
|
+
var applyThreadSummaryEvent = (currentThreads, event) => {
|
|
673
|
+
if (event.event === "snapshot_required") return currentThreads;
|
|
674
|
+
if (event.event === "thread.deleted") {
|
|
675
|
+
const next2 = currentThreads.filter((thread) => thread.id !== event.thread_id);
|
|
676
|
+
return next2.length === currentThreads.length ? currentThreads : next2;
|
|
677
|
+
}
|
|
678
|
+
const incoming = threadSummaryToStored(event.thread);
|
|
679
|
+
const index = currentThreads.findIndex((thread) => thread.id === incoming.id);
|
|
680
|
+
const sortByActivity = (threads) => [...threads].sort((a, b) => {
|
|
681
|
+
const left = Date.parse(String(a.updated_at || a.last_activity_at || a.created_at || ""));
|
|
682
|
+
const right = Date.parse(String(b.updated_at || b.last_activity_at || b.created_at || ""));
|
|
683
|
+
return (Number.isFinite(right) ? right : 0) - (Number.isFinite(left) ? left : 0);
|
|
684
|
+
});
|
|
685
|
+
if (index === -1) return sortByActivity([...currentThreads, incoming]);
|
|
686
|
+
const [merged] = mergeStoredThreadsForRefresh([currentThreads[index]], [incoming]);
|
|
687
|
+
if (sameStoredThread(currentThreads[index], merged)) return currentThreads;
|
|
688
|
+
const next = [...currentThreads];
|
|
689
|
+
next[index] = merged;
|
|
690
|
+
return sortByActivity(next);
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
// src/message-lifecycle.ts
|
|
694
|
+
function findStableAssistantMessageIndex(messages, input) {
|
|
695
|
+
return messages.findIndex(
|
|
696
|
+
(message) => message.role === "assistant" && (input.messageId && message.id === input.messageId || Boolean(input.runId && message.runId === input.runId))
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
function appendStableStreamingTurn(messages, userMessage, assistantMessage) {
|
|
700
|
+
const next = [...messages];
|
|
701
|
+
if (!next.some((message) => message.id === userMessage.id)) {
|
|
702
|
+
next.push(userMessage);
|
|
703
|
+
}
|
|
704
|
+
if (!next.some(
|
|
705
|
+
(message) => message.id === assistantMessage.id || Boolean(
|
|
706
|
+
assistantMessage.runId && message.role === "assistant" && message.runId === assistantMessage.runId
|
|
707
|
+
)
|
|
708
|
+
)) {
|
|
709
|
+
next.push(assistantMessage);
|
|
710
|
+
}
|
|
711
|
+
return next;
|
|
712
|
+
}
|
|
713
|
+
function upsertStableAssistantMessage(messages, input) {
|
|
714
|
+
const next = [...messages];
|
|
715
|
+
for (const baseMessage of input.baseMessages || []) {
|
|
716
|
+
const matchesTarget = baseMessage.role === "assistant" && (input.messageId && baseMessage.id === input.messageId || Boolean(input.runId && baseMessage.runId === input.runId));
|
|
717
|
+
if (!matchesTarget && !next.some((message) => message.id === baseMessage.id)) {
|
|
718
|
+
next.push(baseMessage);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
const index = findStableAssistantMessageIndex(next, input);
|
|
722
|
+
if (index === -1) {
|
|
723
|
+
next.push(input.create());
|
|
724
|
+
return next;
|
|
725
|
+
}
|
|
726
|
+
next[index] = input.update(next[index]);
|
|
727
|
+
return next;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/controller.ts
|
|
517
731
|
function useAgents24ChatController({
|
|
518
732
|
transport,
|
|
519
733
|
storage,
|
|
@@ -529,56 +743,60 @@ function useAgents24ChatController({
|
|
|
529
743
|
}) {
|
|
530
744
|
const activeThreadId = controlledActiveThreadId ?? storage.getActiveThreadId?.() ?? null;
|
|
531
745
|
const initialCached = activeThreadId ? storage.getThread(activeThreadId) : void 0;
|
|
532
|
-
const [messages, setMessages] = (0,
|
|
533
|
-
const [isLoading, setIsLoading] = (0,
|
|
534
|
-
const [isLoadingHistory, setIsLoadingHistory] = (0,
|
|
535
|
-
const [isLoadingOlder, setIsLoadingOlder] = (0,
|
|
536
|
-
const [hasOlderTurns, setHasOlderTurns] = (0,
|
|
537
|
-
const [streamingContent, setStreamingContent] = (0,
|
|
538
|
-
const [streamingMessageId, setStreamingMessageId] = (0,
|
|
539
|
-
const [contextStatus, setContextStatus] = (0,
|
|
540
|
-
const [currentReasoning, setCurrentReasoning] = (0,
|
|
541
|
-
const [liked, setLiked] = (0,
|
|
542
|
-
const [disliked, setDisliked] = (0,
|
|
543
|
-
const [copiedMessageId, setCopiedMessageId] = (0,
|
|
544
|
-
const [lastThinkingDurationMs, setLastThinkingDurationMs] = (0,
|
|
545
|
-
const [threads, setThreads] = (0,
|
|
546
|
-
const [isRefreshingThreads, setIsRefreshingThreads] = (0,
|
|
547
|
-
const [
|
|
548
|
-
const
|
|
549
|
-
const
|
|
550
|
-
const
|
|
551
|
-
const
|
|
552
|
-
const
|
|
553
|
-
const
|
|
554
|
-
const
|
|
555
|
-
const
|
|
556
|
-
const
|
|
557
|
-
const
|
|
558
|
-
const
|
|
559
|
-
const
|
|
560
|
-
const
|
|
561
|
-
const
|
|
562
|
-
const
|
|
563
|
-
const
|
|
564
|
-
const
|
|
746
|
+
const [messages, setMessages] = (0, import_react2.useState)(() => initialCached?.messages || []);
|
|
747
|
+
const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
|
|
748
|
+
const [isLoadingHistory, setIsLoadingHistory] = (0, import_react2.useState)(() => Boolean(activeThreadId) && !initialCached?.messages?.length);
|
|
749
|
+
const [isLoadingOlder, setIsLoadingOlder] = (0, import_react2.useState)(false);
|
|
750
|
+
const [hasOlderTurns, setHasOlderTurns] = (0, import_react2.useState)(Boolean(initialCached?.hasOlderTurns));
|
|
751
|
+
const [streamingContent, setStreamingContent] = (0, import_react2.useState)("");
|
|
752
|
+
const [streamingMessageId, setStreamingMessageId] = (0, import_react2.useState)(null);
|
|
753
|
+
const [contextStatus, setContextStatus] = (0, import_react2.useState)(null);
|
|
754
|
+
const [currentReasoning, setCurrentReasoning] = (0, import_react2.useState)([]);
|
|
755
|
+
const [liked, setLiked] = (0, import_react2.useState)({});
|
|
756
|
+
const [disliked, setDisliked] = (0, import_react2.useState)({});
|
|
757
|
+
const [copiedMessageId, setCopiedMessageId] = (0, import_react2.useState)(null);
|
|
758
|
+
const [lastThinkingDurationMs, setLastThinkingDurationMs] = (0, import_react2.useState)(null);
|
|
759
|
+
const [threads, setThreads] = (0, import_react2.useState)(() => storage.listThreads());
|
|
760
|
+
const [isRefreshingThreads, setIsRefreshingThreads] = (0, import_react2.useState)(false);
|
|
761
|
+
const [isSelectingThread, setIsSelectingThread] = (0, import_react2.useState)(false);
|
|
762
|
+
const [activeRunId, setActiveRunId] = (0, import_react2.useState)(null);
|
|
763
|
+
const textareaRef = (0, import_react2.useRef)(null);
|
|
764
|
+
const activeThreadIdRef = (0, import_react2.useRef)(activeThreadId);
|
|
765
|
+
const messagesRef = (0, import_react2.useRef)(messages);
|
|
766
|
+
const nextBeforeTurnIndexRef = (0, import_react2.useRef)(initialCached?.nextBeforeTurnIndex ?? null);
|
|
767
|
+
const hasOlderTurnsRef = (0, import_react2.useRef)(Boolean(initialCached?.hasOlderTurns));
|
|
768
|
+
const isLoadingOlderRef = (0, import_react2.useRef)(false);
|
|
769
|
+
const loadedThreadIdRef = (0, import_react2.useRef)(initialCached?.messages?.length ? activeThreadId : null);
|
|
770
|
+
const requestSeqRef = (0, import_react2.useRef)(0);
|
|
771
|
+
const isLoadingHistoryRef = (0, import_react2.useRef)(isLoadingHistory);
|
|
772
|
+
const activeRunIdRef = (0, import_react2.useRef)(null);
|
|
773
|
+
const reattachedRunIdRef = (0, import_react2.useRef)(null);
|
|
774
|
+
const abortControllerRef = (0, import_react2.useRef)(null);
|
|
775
|
+
const [lifecycleAbortController] = (0, import_react2.useState)(() => new AbortController());
|
|
776
|
+
const streamingContentRef = (0, import_react2.useRef)("");
|
|
777
|
+
const streamingMessageIdRef = (0, import_react2.useRef)(null);
|
|
778
|
+
const reasoningRef = (0, import_react2.useRef)([]);
|
|
779
|
+
const liveVoiceIdsRef = (0, import_react2.useRef)({});
|
|
780
|
+
const refreshSeqRef = (0, import_react2.useRef)(0);
|
|
781
|
+
const threadEventsCursorRef = (0, import_react2.useRef)(null);
|
|
782
|
+
const setActiveRunIdValue = (0, import_react2.useCallback)((runId) => {
|
|
565
783
|
activeRunIdRef.current = runId;
|
|
566
784
|
setActiveRunId(runId);
|
|
567
785
|
}, []);
|
|
568
|
-
const syncThreadsFromStorage = (0,
|
|
786
|
+
const syncThreadsFromStorage = (0, import_react2.useCallback)(() => {
|
|
569
787
|
setThreads(storage.listThreads());
|
|
570
788
|
}, [storage]);
|
|
571
|
-
const upsertStoredThread = (0,
|
|
789
|
+
const upsertStoredThread = (0, import_react2.useCallback)(
|
|
572
790
|
(thread) => {
|
|
573
791
|
storage.upsertThread(thread);
|
|
574
792
|
syncThreadsFromStorage();
|
|
575
793
|
},
|
|
576
794
|
[storage, syncThreadsFromStorage]
|
|
577
795
|
);
|
|
578
|
-
(0,
|
|
796
|
+
(0, import_react2.useEffect)(() => {
|
|
579
797
|
messagesRef.current = messages;
|
|
580
798
|
}, [messages]);
|
|
581
|
-
const persistThread = (0,
|
|
799
|
+
const persistThread = (0, import_react2.useCallback)(
|
|
582
800
|
(threadId, nextMessages, paging, options) => {
|
|
583
801
|
const existing = storage.getThread(threadId);
|
|
584
802
|
const firstUser = nextMessages.find((message) => message.role === "user");
|
|
@@ -595,7 +813,7 @@ function useAgents24ChatController({
|
|
|
595
813
|
},
|
|
596
814
|
[storage, upsertStoredThread]
|
|
597
815
|
);
|
|
598
|
-
const markThreadRunStatus = (0,
|
|
816
|
+
const markThreadRunStatus = (0, import_react2.useCallback)(
|
|
599
817
|
(threadId, runId, status, lastEventSeq) => {
|
|
600
818
|
const existing = storage.getThread(threadId);
|
|
601
819
|
if (!existing || !runId) return;
|
|
@@ -625,18 +843,21 @@ function useAgents24ChatController({
|
|
|
625
843
|
},
|
|
626
844
|
[storage, upsertStoredThread]
|
|
627
845
|
);
|
|
628
|
-
const refresh = (0,
|
|
846
|
+
const refresh = (0, import_react2.useCallback)(async () => {
|
|
847
|
+
const seq = ++refreshSeqRef.current;
|
|
629
848
|
setIsRefreshingThreads(true);
|
|
630
849
|
try {
|
|
631
|
-
const data = await transport.listThreads();
|
|
850
|
+
const data = await transport.listThreads({ signal: lifecycleAbortController.signal });
|
|
851
|
+
if (lifecycleAbortController.signal.aborted) return;
|
|
852
|
+
if (seq !== refreshSeqRef.current) return;
|
|
632
853
|
const nextThreads = (data.items || []).map((item) => threadSummaryToStored(item));
|
|
633
|
-
storage.setThreads(nextThreads);
|
|
854
|
+
storage.setThreads(mergeStoredThreadsForRefresh(storage.listThreads(), nextThreads));
|
|
634
855
|
setThreads(storage.listThreads());
|
|
635
856
|
} finally {
|
|
636
|
-
setIsRefreshingThreads(false);
|
|
857
|
+
if (!lifecycleAbortController.signal.aborted && seq === refreshSeqRef.current) setIsRefreshingThreads(false);
|
|
637
858
|
}
|
|
638
|
-
}, [storage, transport]);
|
|
639
|
-
const applyThreadId = (0,
|
|
859
|
+
}, [lifecycleAbortController, storage, transport]);
|
|
860
|
+
const applyThreadId = (0, import_react2.useCallback)(
|
|
640
861
|
(threadId, baseMessages) => {
|
|
641
862
|
if (!threadId || activeThreadIdRef.current === threadId) return;
|
|
642
863
|
activeThreadIdRef.current = threadId;
|
|
@@ -650,18 +871,18 @@ function useAgents24ChatController({
|
|
|
650
871
|
streamingContentRef.current = value;
|
|
651
872
|
setStreamingContent(value);
|
|
652
873
|
};
|
|
653
|
-
const setLoadingHistory = (0,
|
|
874
|
+
const setLoadingHistory = (0, import_react2.useCallback)((value) => {
|
|
654
875
|
isLoadingHistoryRef.current = value;
|
|
655
876
|
setIsLoadingHistory(value);
|
|
656
877
|
}, []);
|
|
657
|
-
const setReasoningSteps = (0,
|
|
878
|
+
const setReasoningSteps = (0, import_react2.useCallback)((value) => {
|
|
658
879
|
reasoningRef.current = value || [];
|
|
659
880
|
setCurrentReasoning(value || []);
|
|
660
881
|
}, []);
|
|
661
|
-
const detachActiveStream = (0,
|
|
882
|
+
const detachActiveStream = (0, import_react2.useCallback)(() => {
|
|
662
883
|
const controller = abortControllerRef.current;
|
|
663
884
|
abortControllerRef.current = null;
|
|
664
|
-
controller
|
|
885
|
+
abortDetachedStream(controller);
|
|
665
886
|
setActiveRunIdValue(null);
|
|
666
887
|
reattachedRunIdRef.current = null;
|
|
667
888
|
streamingMessageIdRef.current = null;
|
|
@@ -672,21 +893,36 @@ function useAgents24ChatController({
|
|
|
672
893
|
setStreamingContent("");
|
|
673
894
|
setCurrentReasoning([]);
|
|
674
895
|
}, [setActiveRunIdValue]);
|
|
675
|
-
const
|
|
896
|
+
const clearMissingThread = (0, import_react2.useCallback)(
|
|
897
|
+
(threadId) => {
|
|
898
|
+
storage.deleteThread?.(threadId);
|
|
899
|
+
syncThreadsFromStorage();
|
|
900
|
+
if (activeThreadIdRef.current !== threadId) return;
|
|
901
|
+
requestSeqRef.current += 1;
|
|
902
|
+
detachActiveStream();
|
|
903
|
+
activeThreadIdRef.current = null;
|
|
904
|
+
loadedThreadIdRef.current = null;
|
|
905
|
+
nextBeforeTurnIndexRef.current = null;
|
|
906
|
+
hasOlderTurnsRef.current = false;
|
|
907
|
+
storage.setActiveThreadId?.(null);
|
|
908
|
+
onActiveThreadIdChange?.(null);
|
|
909
|
+
setMessages([]);
|
|
910
|
+
messagesRef.current = [];
|
|
911
|
+
setHasOlderTurns(false);
|
|
912
|
+
setIsLoadingOlder(false);
|
|
913
|
+
setContextStatus(null);
|
|
914
|
+
setLoadingHistory(false);
|
|
915
|
+
},
|
|
916
|
+
[detachActiveStream, onActiveThreadIdChange, setLoadingHistory, storage, syncThreadsFromStorage]
|
|
917
|
+
);
|
|
918
|
+
const setLiveAssistantMessage = (0, import_react2.useCallback)(
|
|
676
919
|
(input) => {
|
|
677
920
|
setMessages((prev) => {
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
(input.baseMessages || []).forEach((message) => {
|
|
684
|
-
const isSameAssistant = message.id === input.messageId || Boolean(input.runId && message.role === "assistant" && message.runId === input.runId);
|
|
685
|
-
if (!isSameAssistant && !next2.some((item) => item.id === message.id)) {
|
|
686
|
-
next2.push(message);
|
|
687
|
-
}
|
|
688
|
-
});
|
|
689
|
-
next2.push({
|
|
921
|
+
const next = upsertStableAssistantMessage(prev, {
|
|
922
|
+
baseMessages: input.baseMessages,
|
|
923
|
+
messageId: input.messageId,
|
|
924
|
+
runId: input.runId,
|
|
925
|
+
create: () => ({
|
|
690
926
|
id: input.messageId,
|
|
691
927
|
role: "assistant",
|
|
692
928
|
runId: input.runId ?? null,
|
|
@@ -695,26 +931,23 @@ function useAgents24ChatController({
|
|
|
695
931
|
reasoningSteps: input.reasoning,
|
|
696
932
|
isFinal: false,
|
|
697
933
|
parts: input.parts || []
|
|
698
|
-
})
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
isFinal: false,
|
|
709
|
-
parts: input.parts ?? next[index].parts
|
|
710
|
-
};
|
|
934
|
+
}),
|
|
935
|
+
update: (message) => ({
|
|
936
|
+
...message,
|
|
937
|
+
runId: input.runId ?? message.runId ?? null,
|
|
938
|
+
content: input.content,
|
|
939
|
+
reasoningSteps: input.reasoning,
|
|
940
|
+
isFinal: false,
|
|
941
|
+
parts: input.parts ?? message.parts
|
|
942
|
+
})
|
|
943
|
+
});
|
|
711
944
|
messagesRef.current = next;
|
|
712
945
|
return next;
|
|
713
946
|
});
|
|
714
947
|
},
|
|
715
948
|
[]
|
|
716
949
|
);
|
|
717
|
-
const finalizeAssistantMessage = (0,
|
|
950
|
+
const finalizeAssistantMessage = (0, import_react2.useCallback)(
|
|
718
951
|
(input) => {
|
|
719
952
|
const content = input.error || input.assistantText.trim();
|
|
720
953
|
if (!content) return input.baseMessages;
|
|
@@ -734,7 +967,17 @@ function useAgents24ChatController({
|
|
|
734
967
|
reasoningSteps: mergeReasoningSteps(input.reasoning, { finalize: true }),
|
|
735
968
|
thinkingDurationMs: input.thinkingDurationMs
|
|
736
969
|
};
|
|
737
|
-
const completed =
|
|
970
|
+
const completed = upsertStableAssistantMessage(input.baseMessages, {
|
|
971
|
+
messageId: input.messageId,
|
|
972
|
+
runId: input.runId,
|
|
973
|
+
create: () => assistant,
|
|
974
|
+
update: (message) => ({
|
|
975
|
+
...message,
|
|
976
|
+
...assistant,
|
|
977
|
+
id: message.id,
|
|
978
|
+
createdAt: message.createdAt
|
|
979
|
+
})
|
|
980
|
+
});
|
|
738
981
|
setMessages(completed);
|
|
739
982
|
messagesRef.current = completed;
|
|
740
983
|
if (input.threadId) {
|
|
@@ -758,7 +1001,7 @@ function useAgents24ChatController({
|
|
|
758
1001
|
},
|
|
759
1002
|
[createId, persistThread, storage, upsertStoredThread]
|
|
760
1003
|
);
|
|
761
|
-
const loadThread = (0,
|
|
1004
|
+
const loadThread = (0, import_react2.useCallback)(
|
|
762
1005
|
async (threadId) => {
|
|
763
1006
|
const seq = ++requestSeqRef.current;
|
|
764
1007
|
setLoadingHistory(true);
|
|
@@ -768,7 +1011,8 @@ function useAgents24ChatController({
|
|
|
768
1011
|
const detail = await transport.getThread({
|
|
769
1012
|
threadId,
|
|
770
1013
|
limit: pageSize,
|
|
771
|
-
includeRunEvents: false
|
|
1014
|
+
includeRunEvents: false,
|
|
1015
|
+
signal: lifecycleAbortController.signal
|
|
772
1016
|
});
|
|
773
1017
|
if (activeThreadIdRef.current !== threadId || seq !== requestSeqRef.current) return;
|
|
774
1018
|
void onThreadDetailLoaded?.(detail);
|
|
@@ -789,13 +1033,20 @@ function useAgents24ChatController({
|
|
|
789
1033
|
nextBeforeTurnIndex: paging.nextBeforeTurnIndex,
|
|
790
1034
|
updated_at: threadActivityDate(detail)
|
|
791
1035
|
});
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
if (isAbortError(error)) return;
|
|
1038
|
+
if (isThreadNotFoundError(error)) {
|
|
1039
|
+
clearMissingThread(threadId);
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
throw error;
|
|
792
1043
|
} finally {
|
|
793
|
-
if (activeThreadIdRef.current === threadId) setLoadingHistory(false);
|
|
1044
|
+
if (!lifecycleAbortController.signal.aborted && activeThreadIdRef.current === threadId && seq === requestSeqRef.current) setLoadingHistory(false);
|
|
794
1045
|
}
|
|
795
1046
|
},
|
|
796
|
-
[onThreadDetailLoaded, pageSize, setLoadingHistory, transport, upsertStoredThread]
|
|
1047
|
+
[clearMissingThread, lifecycleAbortController, onThreadDetailLoaded, pageSize, setLoadingHistory, transport, upsertStoredThread]
|
|
797
1048
|
);
|
|
798
|
-
const loadOlderTurns = (0,
|
|
1049
|
+
const loadOlderTurns = (0, import_react2.useCallback)(async () => {
|
|
799
1050
|
const threadId = activeThreadIdRef.current;
|
|
800
1051
|
const beforeTurnIndex = nextBeforeTurnIndexRef.current;
|
|
801
1052
|
if (!threadId || beforeTurnIndex === null || isLoadingOlderRef.current) return;
|
|
@@ -806,7 +1057,8 @@ function useAgents24ChatController({
|
|
|
806
1057
|
threadId,
|
|
807
1058
|
limit: pageSize,
|
|
808
1059
|
beforeTurnIndex,
|
|
809
|
-
includeRunEvents: false
|
|
1060
|
+
includeRunEvents: false,
|
|
1061
|
+
signal: lifecycleAbortController.signal
|
|
810
1062
|
});
|
|
811
1063
|
if (activeThreadIdRef.current !== threadId) return;
|
|
812
1064
|
const older = threadDetailToMessages(detail);
|
|
@@ -818,12 +1070,19 @@ function useAgents24ChatController({
|
|
|
818
1070
|
hasOlderTurnsRef.current = paging.hasOlderTurns;
|
|
819
1071
|
nextBeforeTurnIndexRef.current = paging.nextBeforeTurnIndex;
|
|
820
1072
|
persistThread(threadId, next, paging);
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
if (isAbortError(error)) return;
|
|
1075
|
+
if (isThreadNotFoundError(error)) {
|
|
1076
|
+
clearMissingThread(threadId);
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
throw error;
|
|
821
1080
|
} finally {
|
|
822
|
-
if (activeThreadIdRef.current === threadId) setIsLoadingOlder(false);
|
|
1081
|
+
if (!lifecycleAbortController.signal.aborted && activeThreadIdRef.current === threadId) setIsLoadingOlder(false);
|
|
823
1082
|
isLoadingOlderRef.current = false;
|
|
824
1083
|
}
|
|
825
|
-
}, [pageSize, persistThread, transport]);
|
|
826
|
-
const handleStreamEvent = (0,
|
|
1084
|
+
}, [clearMissingThread, lifecycleAbortController, pageSize, persistThread, transport]);
|
|
1085
|
+
const handleStreamEvent = (0, import_react2.useCallback)(
|
|
827
1086
|
(input) => {
|
|
828
1087
|
const { mode, event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages } = input;
|
|
829
1088
|
const payload = event.payload || {};
|
|
@@ -869,7 +1128,7 @@ function useAgents24ChatController({
|
|
|
869
1128
|
streamThreadIdRef.current = terminalThreadId;
|
|
870
1129
|
applyThreadId(terminalThreadId, baseMessages);
|
|
871
1130
|
}
|
|
872
|
-
const finalText = String(payload.assistant_output_text ||
|
|
1131
|
+
const finalText = String(payload.assistant_output_text || assistantTextFromResponseBlocks(responseBlocks || []) || streamingContentRef.current || "");
|
|
873
1132
|
finalizeAssistantMessage({
|
|
874
1133
|
threadId: streamThreadIdRef.current || activeThreadIdRef.current,
|
|
875
1134
|
baseMessages: messagesRef.current,
|
|
@@ -884,7 +1143,7 @@ function useAgents24ChatController({
|
|
|
884
1143
|
},
|
|
885
1144
|
[applyThreadId, finalizeAssistantMessage, markThreadRunStatus, onRuntimeEvent, onStreamErrorMessage, setActiveRunIdValue, setLiveAssistantMessage, setReasoningSteps]
|
|
886
1145
|
);
|
|
887
|
-
const runStream = (0,
|
|
1146
|
+
const runStream = (0, import_react2.useCallback)(
|
|
888
1147
|
async (input) => {
|
|
889
1148
|
const startedAt = Date.now();
|
|
890
1149
|
const controller = new AbortController();
|
|
@@ -943,19 +1202,28 @@ function useAgents24ChatController({
|
|
|
943
1202
|
);
|
|
944
1203
|
}
|
|
945
1204
|
try {
|
|
1205
|
+
let streamResult = null;
|
|
946
1206
|
if (input.mode === "attach") {
|
|
947
1207
|
setActiveRunIdValue(input.runId);
|
|
948
1208
|
reattachedRunIdRef.current = input.runId;
|
|
949
|
-
await transport.attachRun(
|
|
1209
|
+
streamResult = await transport.attachRun(
|
|
950
1210
|
{ runId: input.runId, signal: controller.signal },
|
|
951
1211
|
(event) => handleStreamEvent({ mode: "attach", event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages })
|
|
952
1212
|
);
|
|
953
1213
|
} else {
|
|
954
|
-
await transport.streamMessage(
|
|
1214
|
+
streamResult = await transport.streamMessage(
|
|
955
1215
|
{ ...input.message, files: input.message.files || [], threadId: activeThreadIdRef.current, signal: controller.signal },
|
|
956
1216
|
(event) => handleStreamEvent({ mode: "submit", event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages })
|
|
957
1217
|
);
|
|
958
1218
|
}
|
|
1219
|
+
if (streamResult?.runId) setActiveRunIdValue(streamResult.runId);
|
|
1220
|
+
if (streamResult?.threadId) {
|
|
1221
|
+
streamThreadIdRef.current = streamResult.threadId;
|
|
1222
|
+
applyThreadId(streamResult.threadId, baseMessages);
|
|
1223
|
+
if (streamResult.runId) {
|
|
1224
|
+
markThreadRunStatus(streamResult.threadId, streamResult.runId, "running");
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
959
1227
|
if (streamingContentRef.current.trim() && streamingMessageIdRef.current) {
|
|
960
1228
|
finalizeAssistantMessage({
|
|
961
1229
|
threadId: streamThreadIdRef.current,
|
|
@@ -969,7 +1237,7 @@ function useAgents24ChatController({
|
|
|
969
1237
|
}
|
|
970
1238
|
await refresh().catch(() => void 0);
|
|
971
1239
|
} catch (error) {
|
|
972
|
-
if (error
|
|
1240
|
+
if (!isAbortError(error)) {
|
|
973
1241
|
finalizeAssistantMessage({
|
|
974
1242
|
threadId: streamThreadIdRef.current,
|
|
975
1243
|
baseMessages: messagesRef.current,
|
|
@@ -998,18 +1266,22 @@ function useAgents24ChatController({
|
|
|
998
1266
|
},
|
|
999
1267
|
[createId, finalizeAssistantMessage, handleStreamEvent, onStreamErrorMessage, persistThread, refresh, setActiveRunIdValue, setReasoningSteps, transport]
|
|
1000
1268
|
);
|
|
1001
|
-
const handleSubmit = (0,
|
|
1269
|
+
const handleSubmit = (0, import_react2.useCallback)(
|
|
1002
1270
|
async (message) => {
|
|
1003
1271
|
if (!message.text.trim() && !(message.files || []).length) return;
|
|
1004
1272
|
await runStream({ mode: "submit", message });
|
|
1005
1273
|
},
|
|
1006
1274
|
[runStream]
|
|
1007
1275
|
);
|
|
1008
|
-
const
|
|
1276
|
+
const attachRun = (0, import_react2.useCallback)(async (runId, threadId) => {
|
|
1277
|
+
const resolvedThreadId = threadId ?? activeThreadIdRef.current;
|
|
1278
|
+
if (runId && resolvedThreadId) await runStream({ mode: "attach", runId, threadId: resolvedThreadId });
|
|
1279
|
+
}, [runStream]);
|
|
1280
|
+
const handleStop = (0, import_react2.useCallback)(() => {
|
|
1009
1281
|
const runId = activeRunIdRef.current;
|
|
1010
1282
|
const partial = streamingContentRef.current;
|
|
1011
1283
|
const liveMessageId = streamingMessageIdRef.current;
|
|
1012
|
-
abortControllerRef.current
|
|
1284
|
+
abortDetachedStream(abortControllerRef.current);
|
|
1013
1285
|
abortControllerRef.current = null;
|
|
1014
1286
|
setActiveRunIdValue(null);
|
|
1015
1287
|
streamingMessageIdRef.current = null;
|
|
@@ -1029,16 +1301,56 @@ function useAgents24ChatController({
|
|
|
1029
1301
|
});
|
|
1030
1302
|
}
|
|
1031
1303
|
}, [finalizeAssistantMessage, lastThinkingDurationMs, setActiveRunIdValue, setReasoningSteps, transport]);
|
|
1032
|
-
(0,
|
|
1033
|
-
refresh().catch(() => {
|
|
1034
|
-
|
|
1035
|
-
|
|
1304
|
+
(0, import_react2.useEffect)(() => {
|
|
1305
|
+
refresh().catch((error) => {
|
|
1306
|
+
if (!isAbortError(error) && !lifecycleAbortController.signal.aborted) {
|
|
1307
|
+
setThreads(storage.listThreads());
|
|
1308
|
+
}
|
|
1036
1309
|
});
|
|
1037
|
-
}, [refresh, storage]);
|
|
1038
|
-
(0,
|
|
1310
|
+
}, [lifecycleAbortController, refresh, storage]);
|
|
1311
|
+
(0, import_react2.useEffect)(() => {
|
|
1312
|
+
return () => {
|
|
1313
|
+
lifecycleAbortController.abort();
|
|
1314
|
+
abortDetachedStream(abortControllerRef.current);
|
|
1315
|
+
abortControllerRef.current = null;
|
|
1316
|
+
};
|
|
1317
|
+
}, [lifecycleAbortController]);
|
|
1318
|
+
(0, import_react2.useEffect)(() => {
|
|
1319
|
+
if (!transport.subscribeThreadEvents) return;
|
|
1320
|
+
let cancelled = false;
|
|
1321
|
+
let retryTimeout = null;
|
|
1322
|
+
let controller = null;
|
|
1323
|
+
const connect = () => {
|
|
1324
|
+
if (cancelled) return;
|
|
1325
|
+
controller = new AbortController();
|
|
1326
|
+
transport.subscribeThreadEvents?.(
|
|
1327
|
+
{ cursor: threadEventsCursorRef.current, signal: controller.signal },
|
|
1328
|
+
async (event) => {
|
|
1329
|
+
if (typeof event.cursor === "number") threadEventsCursorRef.current = event.cursor;
|
|
1330
|
+
if (event.event === "snapshot_required") {
|
|
1331
|
+
await refresh().catch(() => void 0);
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
const next = applyThreadSummaryEvent(storage.listThreads(), event);
|
|
1335
|
+
storage.setThreads(next);
|
|
1336
|
+
setThreads(storage.listThreads());
|
|
1337
|
+
}
|
|
1338
|
+
).catch((error) => {
|
|
1339
|
+
if (cancelled || isAbortError(error)) return;
|
|
1340
|
+
retryTimeout = setTimeout(connect, 1500);
|
|
1341
|
+
});
|
|
1342
|
+
};
|
|
1343
|
+
connect();
|
|
1344
|
+
return () => {
|
|
1345
|
+
cancelled = true;
|
|
1346
|
+
if (retryTimeout) clearTimeout(retryTimeout);
|
|
1347
|
+
controller?.abort();
|
|
1348
|
+
};
|
|
1349
|
+
}, [refresh, storage, transport]);
|
|
1350
|
+
(0, import_react2.useEffect)(() => {
|
|
1039
1351
|
syncThreadsFromStorage();
|
|
1040
1352
|
}, [storageKey, syncThreadsFromStorage]);
|
|
1041
|
-
(0,
|
|
1353
|
+
(0, import_react2.useEffect)(() => {
|
|
1042
1354
|
const previous = activeThreadIdRef.current;
|
|
1043
1355
|
activeThreadIdRef.current = activeThreadId;
|
|
1044
1356
|
if (activeThreadId && previous === activeThreadId && (activeRunIdRef.current || streamingMessageIdRef.current)) {
|
|
@@ -1048,6 +1360,9 @@ function useAgents24ChatController({
|
|
|
1048
1360
|
detachActiveStream();
|
|
1049
1361
|
}
|
|
1050
1362
|
if (!activeThreadId) {
|
|
1363
|
+
if (previous === null && loadedThreadIdRef.current === null && messagesRef.current.length === 0 && !hasOlderTurnsRef.current && !isLoadingHistoryRef.current && !isLoadingOlderRef.current && nextBeforeTurnIndexRef.current === null) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1051
1366
|
requestSeqRef.current += 1;
|
|
1052
1367
|
loadedThreadIdRef.current = null;
|
|
1053
1368
|
setMessages([]);
|
|
@@ -1079,14 +1394,14 @@ function useAgents24ChatController({
|
|
|
1079
1394
|
}
|
|
1080
1395
|
void loadThread(activeThreadId).catch(() => setLoadingHistory(false));
|
|
1081
1396
|
}, [activeThreadId, detachActiveStream, loadThread, setLoadingHistory, storage]);
|
|
1082
|
-
(0,
|
|
1397
|
+
(0, import_react2.useEffect)(() => {
|
|
1083
1398
|
const threadId = activeThreadId;
|
|
1084
1399
|
if (!threadId || isLoadingHistory || loadedThreadIdRef.current !== threadId || activeRunIdRef.current) return;
|
|
1085
|
-
const runId =
|
|
1400
|
+
const runId = autoAttachRunIdFromThread(storage.getThread(threadId));
|
|
1086
1401
|
if (!runId || reattachedRunIdRef.current === runId) return;
|
|
1087
1402
|
void runStream({ mode: "attach", threadId, runId });
|
|
1088
1403
|
}, [activeThreadId, isLoadingHistory, runStream, storage, storageKey]);
|
|
1089
|
-
(0,
|
|
1404
|
+
(0, import_react2.useEffect)(() => {
|
|
1090
1405
|
const threadId = activeThreadId;
|
|
1091
1406
|
if (!threadId || isLoadingHistoryRef.current || activeRunIdRef.current || streamingMessageIdRef.current) {
|
|
1092
1407
|
return;
|
|
@@ -1095,87 +1410,58 @@ function useAgents24ChatController({
|
|
|
1095
1410
|
if (!hasStaleUnfinishedAssistantCache(cached)) return;
|
|
1096
1411
|
void loadThread(threadId).catch(() => setLoadingHistory(false));
|
|
1097
1412
|
}, [activeThreadId, loadThread, setLoadingHistory, storage, storageKey]);
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
setMessages
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
id: currentId || createId(),
|
|
1132
|
-
role: input.role,
|
|
1133
|
-
content,
|
|
1134
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
1135
|
-
isFinal: Boolean(input.isFinal),
|
|
1136
|
-
isVoice: input.role === "user",
|
|
1137
|
-
parts: content ? [{ id: createId(), type: "text", kind: "text", text: content }] : [],
|
|
1138
|
-
citations: input.citations,
|
|
1139
|
-
reasoningSteps: input.reasoningSteps
|
|
1140
|
-
};
|
|
1141
|
-
const next = index === -1 ? [...prev, nextMessage] : prev.map((message, itemIndex) => itemIndex === index ? { ...message, ...nextMessage } : message);
|
|
1142
|
-
liveVoiceIdsRef.current[input.role] = input.isFinal ? void 0 : nextMessage.id;
|
|
1143
|
-
messagesRef.current = next;
|
|
1144
|
-
if (activeThreadIdRef.current) persistThread(activeThreadIdRef.current, next);
|
|
1145
|
-
return next;
|
|
1146
|
-
});
|
|
1147
|
-
}, [createId, persistThread]);
|
|
1148
|
-
const startNewThread = (0, import_react.useCallback)(() => {
|
|
1149
|
-
detachActiveStream();
|
|
1150
|
-
requestSeqRef.current += 1;
|
|
1151
|
-
activeThreadIdRef.current = null;
|
|
1152
|
-
loadedThreadIdRef.current = null;
|
|
1153
|
-
nextBeforeTurnIndexRef.current = null;
|
|
1154
|
-
hasOlderTurnsRef.current = false;
|
|
1155
|
-
storage.setActiveThreadId?.(null);
|
|
1156
|
-
onActiveThreadIdChange?.(null);
|
|
1157
|
-
setMessages([]);
|
|
1158
|
-
messagesRef.current = [];
|
|
1159
|
-
setHasOlderTurns(false);
|
|
1160
|
-
setIsLoadingOlder(false);
|
|
1161
|
-
setContextStatus(null);
|
|
1162
|
-
setLoadingHistory(false);
|
|
1163
|
-
}, [detachActiveStream, onActiveThreadIdChange, setLoadingHistory, storage]);
|
|
1164
|
-
const loadThreadById = (0, import_react.useCallback)(
|
|
1413
|
+
const {
|
|
1414
|
+
handleCopy,
|
|
1415
|
+
handleDislike,
|
|
1416
|
+
handleLike,
|
|
1417
|
+
handleRetry,
|
|
1418
|
+
startNewThread,
|
|
1419
|
+
upsertLiveVoiceMessage
|
|
1420
|
+
} = useControllerMessageActions({
|
|
1421
|
+
activeThreadIdRef,
|
|
1422
|
+
createId,
|
|
1423
|
+
detachActiveStream,
|
|
1424
|
+
disliked,
|
|
1425
|
+
handleSubmit,
|
|
1426
|
+
hasOlderTurnsRef,
|
|
1427
|
+
liked,
|
|
1428
|
+
liveVoiceIdsRef,
|
|
1429
|
+
loadedThreadIdRef,
|
|
1430
|
+
messagesRef,
|
|
1431
|
+
nextBeforeTurnIndexRef,
|
|
1432
|
+
onActiveThreadIdChange,
|
|
1433
|
+
persistThread,
|
|
1434
|
+
requestSeqRef,
|
|
1435
|
+
setContextStatus,
|
|
1436
|
+
setCopiedMessageId,
|
|
1437
|
+
setDisliked,
|
|
1438
|
+
setHasOlderTurns,
|
|
1439
|
+
setIsLoadingOlder,
|
|
1440
|
+
setLiked,
|
|
1441
|
+
setLoadingHistory,
|
|
1442
|
+
setMessages,
|
|
1443
|
+
storage
|
|
1444
|
+
});
|
|
1445
|
+
const loadThreadById = (0, import_react2.useCallback)(
|
|
1165
1446
|
async (threadId) => {
|
|
1166
1447
|
if (!threadId) return;
|
|
1448
|
+
setIsSelectingThread(true);
|
|
1167
1449
|
if (activeThreadIdRef.current !== threadId) {
|
|
1168
1450
|
if (abortControllerRef.current) detachActiveStream();
|
|
1169
1451
|
activeThreadIdRef.current = threadId;
|
|
1170
1452
|
storage.setActiveThreadId?.(threadId);
|
|
1171
1453
|
onActiveThreadIdChange?.(threadId);
|
|
1172
1454
|
}
|
|
1173
|
-
|
|
1455
|
+
try {
|
|
1456
|
+
await loadThread(threadId);
|
|
1457
|
+
} finally {
|
|
1458
|
+
if (activeThreadIdRef.current === threadId) setIsSelectingThread(false);
|
|
1459
|
+
}
|
|
1174
1460
|
},
|
|
1175
1461
|
[detachActiveStream, loadThread, onActiveThreadIdChange, storage]
|
|
1176
1462
|
);
|
|
1177
1463
|
const activeThread = activeThreadId ? storage.getThread(activeThreadId) || threads.find((thread) => thread.id === activeThreadId) || null : null;
|
|
1178
|
-
return (0,
|
|
1464
|
+
return (0, import_react2.useMemo)(() => ({
|
|
1179
1465
|
threads,
|
|
1180
1466
|
activeThreadId,
|
|
1181
1467
|
activeThread,
|
|
@@ -1188,6 +1474,7 @@ function useAgents24ChatController({
|
|
|
1188
1474
|
isLoadingHistory,
|
|
1189
1475
|
isLoadingOlder,
|
|
1190
1476
|
isRefreshingThreads,
|
|
1477
|
+
isSelectingThread,
|
|
1191
1478
|
hasOlderTurns,
|
|
1192
1479
|
liked,
|
|
1193
1480
|
disliked,
|
|
@@ -1195,6 +1482,7 @@ function useAgents24ChatController({
|
|
|
1195
1482
|
lastThinkingDurationMs,
|
|
1196
1483
|
activeRunId,
|
|
1197
1484
|
handleSubmit,
|
|
1485
|
+
attachRun,
|
|
1198
1486
|
handleStop,
|
|
1199
1487
|
handleCopy,
|
|
1200
1488
|
handleLike,
|
|
@@ -1212,6 +1500,7 @@ function useAgents24ChatController({
|
|
|
1212
1500
|
activeRunId,
|
|
1213
1501
|
activeThread,
|
|
1214
1502
|
activeThreadId,
|
|
1503
|
+
attachRun,
|
|
1215
1504
|
contextStatus,
|
|
1216
1505
|
currentReasoning,
|
|
1217
1506
|
disliked,
|
|
@@ -1226,6 +1515,7 @@ function useAgents24ChatController({
|
|
|
1226
1515
|
isLoadingHistory,
|
|
1227
1516
|
isLoadingOlder,
|
|
1228
1517
|
isRefreshingThreads,
|
|
1518
|
+
isSelectingThread,
|
|
1229
1519
|
lastThinkingDurationMs,
|
|
1230
1520
|
liked,
|
|
1231
1521
|
loadThreadById,
|
|
@@ -1455,6 +1745,7 @@ function LatestThreadScrollerOutline({
|
|
|
1455
1745
|
);
|
|
1456
1746
|
};
|
|
1457
1747
|
updateSideRoom();
|
|
1748
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
1458
1749
|
const resizeObserver = new ResizeObserver(updateSideRoom);
|
|
1459
1750
|
resizeObserver.observe(root);
|
|
1460
1751
|
const content = layout?.contentElement;
|
|
@@ -1613,13 +1904,16 @@ var DefaultChatPart = ({
|
|
|
1613
1904
|
if (part.kind === "ui-blocks") {
|
|
1614
1905
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-ui-blocks-part": true, "data-state": part.state });
|
|
1615
1906
|
}
|
|
1616
|
-
if (part.kind === "
|
|
1617
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-
|
|
1907
|
+
if (part.kind === "hitl") {
|
|
1908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-hitl-part": part.interruptId || "" });
|
|
1618
1909
|
}
|
|
1619
1910
|
if (part.kind === "error") {
|
|
1620
1911
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-error-part": true, children: part.errorText });
|
|
1621
1912
|
}
|
|
1622
|
-
|
|
1913
|
+
if (part.kind === "data") {
|
|
1914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-data-part": part.name });
|
|
1915
|
+
}
|
|
1916
|
+
return null;
|
|
1623
1917
|
};
|
|
1624
1918
|
var renderChatPart = (part, message, renderOptions) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1625
1919
|
DefaultChatPart,
|
|
@@ -1632,12 +1926,17 @@ var renderChatPart = (part, message, renderOptions) => /* @__PURE__ */ (0, impor
|
|
|
1632
1926
|
);
|
|
1633
1927
|
|
|
1634
1928
|
// src/sse.ts
|
|
1929
|
+
var isAbortError2 = (error) => {
|
|
1930
|
+
if (!error || typeof error !== "object") return false;
|
|
1931
|
+
const maybe = error;
|
|
1932
|
+
return maybe.name === "AbortError" || String(maybe.message || "").toLowerCase().includes("aborted");
|
|
1933
|
+
};
|
|
1635
1934
|
var parseSseBlock = (block) => {
|
|
1636
1935
|
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.replace(/^data:\s?/, "")).join("\n").trim();
|
|
1637
1936
|
if (!data) return null;
|
|
1638
1937
|
return JSON.parse(data);
|
|
1639
1938
|
};
|
|
1640
|
-
var consumeSseResponse = async (response, onEvent) => {
|
|
1939
|
+
var consumeSseResponse = async (response, onEvent, options = {}) => {
|
|
1641
1940
|
if (!response.ok) {
|
|
1642
1941
|
let message = response.statusText || "Failed to open chat stream.";
|
|
1643
1942
|
try {
|
|
@@ -1651,30 +1950,49 @@ var consumeSseResponse = async (response, onEvent) => {
|
|
|
1651
1950
|
if (!reader) throw new Error("The chat stream did not return a readable body.");
|
|
1652
1951
|
const decoder = new TextDecoder();
|
|
1653
1952
|
let buffer = "";
|
|
1654
|
-
let threadId = null;
|
|
1655
|
-
let runId = null;
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1953
|
+
let threadId = response.headers.get("X-Thread-ID") || null;
|
|
1954
|
+
let runId = response.headers.get("X-Run-ID") || null;
|
|
1955
|
+
let closed = false;
|
|
1956
|
+
const closeReader = () => {
|
|
1957
|
+
closed = true;
|
|
1958
|
+
queueMicrotask(() => {
|
|
1959
|
+
void reader.cancel().catch(() => {
|
|
1960
|
+
});
|
|
1961
|
+
});
|
|
1962
|
+
};
|
|
1963
|
+
if (options.signal?.aborted) {
|
|
1964
|
+
closeReader();
|
|
1965
|
+
return { threadId, runId };
|
|
1966
|
+
}
|
|
1967
|
+
options.signal?.addEventListener("abort", closeReader, { once: true });
|
|
1968
|
+
try {
|
|
1969
|
+
while (!closed) {
|
|
1970
|
+
const { value, done } = await reader.read();
|
|
1971
|
+
if (done) break;
|
|
1972
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1973
|
+
let boundary = buffer.indexOf("\n\n");
|
|
1974
|
+
while (boundary !== -1) {
|
|
1975
|
+
const block = buffer.slice(0, boundary);
|
|
1976
|
+
buffer = buffer.slice(boundary + 2);
|
|
1977
|
+
boundary = buffer.indexOf("\n\n");
|
|
1978
|
+
const event = parseSseBlock(block);
|
|
1979
|
+
if (!event) continue;
|
|
1980
|
+
if (event.run_id) runId = event.run_id;
|
|
1981
|
+
const payloadThreadId = event.payload?.thread_id;
|
|
1982
|
+
if (payloadThreadId) threadId = String(payloadThreadId);
|
|
1983
|
+
await onEvent(event);
|
|
1984
|
+
}
|
|
1671
1985
|
}
|
|
1986
|
+
} catch (error) {
|
|
1987
|
+
if (!isAbortError2(error)) throw error;
|
|
1988
|
+
} finally {
|
|
1989
|
+
options.signal?.removeEventListener("abort", closeReader);
|
|
1672
1990
|
}
|
|
1673
1991
|
return { threadId, runId };
|
|
1674
1992
|
};
|
|
1675
1993
|
|
|
1676
1994
|
// src/streaming-text.ts
|
|
1677
|
-
var
|
|
1995
|
+
var import_react3 = require("react");
|
|
1678
1996
|
var DEFAULT_COMPLETED_TEXT_CACHE_SIZE = 200;
|
|
1679
1997
|
var defaultCompletedTextCache = /* @__PURE__ */ new Map();
|
|
1680
1998
|
var defaultStreamingTextCache = {
|
|
@@ -1702,24 +2020,24 @@ function useStreamingText({
|
|
|
1702
2020
|
maxCatchupChars = 20
|
|
1703
2021
|
}) {
|
|
1704
2022
|
const cacheAdapter = cache === false ? null : cache;
|
|
1705
|
-
const [displayedText, setDisplayedText] = (0,
|
|
2023
|
+
const [displayedText, setDisplayedText] = (0, import_react3.useState)(() => {
|
|
1706
2024
|
const cachedText = cacheAdapter?.get(id);
|
|
1707
2025
|
return isStreaming && cachedText !== text ? "" : text;
|
|
1708
2026
|
});
|
|
1709
|
-
const targetRef = (0,
|
|
1710
|
-
const displayedRef = (0,
|
|
1711
|
-
const rafRef = (0,
|
|
1712
|
-
const lastFrameAtRef = (0,
|
|
1713
|
-
const idRef = (0,
|
|
1714
|
-
const shouldAnimateRef = (0,
|
|
1715
|
-
(0,
|
|
2027
|
+
const targetRef = (0, import_react3.useRef)(text);
|
|
2028
|
+
const displayedRef = (0, import_react3.useRef)(displayedText);
|
|
2029
|
+
const rafRef = (0, import_react3.useRef)(null);
|
|
2030
|
+
const lastFrameAtRef = (0, import_react3.useRef)(null);
|
|
2031
|
+
const idRef = (0, import_react3.useRef)(id);
|
|
2032
|
+
const shouldAnimateRef = (0, import_react3.useRef)(isStreaming);
|
|
2033
|
+
(0, import_react3.useEffect)(() => {
|
|
1716
2034
|
if (isStreaming || !text) return;
|
|
1717
2035
|
cacheAdapter?.set(id, text);
|
|
1718
2036
|
}, [cacheAdapter, id, isStreaming, text]);
|
|
1719
|
-
(0,
|
|
2037
|
+
(0, import_react3.useEffect)(() => {
|
|
1720
2038
|
targetRef.current = text;
|
|
1721
2039
|
}, [text]);
|
|
1722
|
-
(0,
|
|
2040
|
+
(0, import_react3.useEffect)(() => {
|
|
1723
2041
|
if (idRef.current === id) return;
|
|
1724
2042
|
idRef.current = id;
|
|
1725
2043
|
const cachedText = cacheAdapter?.get(id);
|
|
@@ -1733,7 +2051,7 @@ function useStreamingText({
|
|
|
1733
2051
|
}
|
|
1734
2052
|
lastFrameAtRef.current = null;
|
|
1735
2053
|
}, [cacheAdapter, id, isStreaming, text]);
|
|
1736
|
-
(0,
|
|
2054
|
+
(0, import_react3.useEffect)(() => {
|
|
1737
2055
|
if (typeof window === "undefined") {
|
|
1738
2056
|
displayedRef.current = text;
|
|
1739
2057
|
setDisplayedText(text);
|
|
@@ -1818,6 +2136,14 @@ var streamHeaders = (headers) => ({
|
|
|
1818
2136
|
Accept: "text/event-stream",
|
|
1819
2137
|
"Content-Type": "application/json"
|
|
1820
2138
|
});
|
|
2139
|
+
var ChatTransportError = class extends Error {
|
|
2140
|
+
constructor(message, status) {
|
|
2141
|
+
super(message);
|
|
2142
|
+
this.name = "ChatTransportError";
|
|
2143
|
+
this.status = status;
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
2146
|
+
var transportError = (response, fallback) => new ChatTransportError(response.statusText || fallback, response.status);
|
|
1821
2147
|
var createFetchChatTransport = ({
|
|
1822
2148
|
routes,
|
|
1823
2149
|
fetchImpl = fetch,
|
|
@@ -1831,7 +2157,7 @@ var createFetchChatTransport = ({
|
|
|
1831
2157
|
signal: input?.signal,
|
|
1832
2158
|
headers: jsonHeaders(await loadHeaders())
|
|
1833
2159
|
});
|
|
1834
|
-
if (!response.ok) throw
|
|
2160
|
+
if (!response.ok) throw transportError(response, "Failed to list chat threads.");
|
|
1835
2161
|
return response.json();
|
|
1836
2162
|
},
|
|
1837
2163
|
async getThread(input) {
|
|
@@ -1839,7 +2165,7 @@ var createFetchChatTransport = ({
|
|
|
1839
2165
|
signal: input.signal,
|
|
1840
2166
|
headers: jsonHeaders(await loadHeaders())
|
|
1841
2167
|
});
|
|
1842
|
-
if (!response.ok) throw
|
|
2168
|
+
if (!response.ok) throw transportError(response, "Failed to load chat thread.");
|
|
1843
2169
|
return response.json();
|
|
1844
2170
|
},
|
|
1845
2171
|
async streamMessage(input, onEvent) {
|
|
@@ -1855,7 +2181,7 @@ var createFetchChatTransport = ({
|
|
|
1855
2181
|
}
|
|
1856
2182
|
)
|
|
1857
2183
|
});
|
|
1858
|
-
return consumeSseResponse(response, onEvent);
|
|
2184
|
+
return consumeSseResponse(response, onEvent, { signal: input.signal });
|
|
1859
2185
|
},
|
|
1860
2186
|
async attachRun(input, onEvent) {
|
|
1861
2187
|
const response = await fetchImpl(routes.attachRun(input), {
|
|
@@ -1864,7 +2190,7 @@ var createFetchChatTransport = ({
|
|
|
1864
2190
|
headers: streamHeaders(await loadHeaders()),
|
|
1865
2191
|
body: JSON.stringify({})
|
|
1866
2192
|
});
|
|
1867
|
-
return consumeSseResponse(response, onEvent);
|
|
2193
|
+
return consumeSseResponse(response, onEvent, { signal: input.signal });
|
|
1868
2194
|
},
|
|
1869
2195
|
async cancelRun(input) {
|
|
1870
2196
|
const response = await fetchImpl(routes.cancelRun(input), {
|
|
@@ -1872,7 +2198,7 @@ var createFetchChatTransport = ({
|
|
|
1872
2198
|
headers: jsonHeaders(await loadHeaders()),
|
|
1873
2199
|
body: JSON.stringify({ assistant_output_text: input.assistantOutputText || void 0 })
|
|
1874
2200
|
});
|
|
1875
|
-
if (!response.ok) throw
|
|
2201
|
+
if (!response.ok) throw transportError(response, "Failed to cancel chat run.");
|
|
1876
2202
|
return response.json();
|
|
1877
2203
|
},
|
|
1878
2204
|
async deleteThread(input) {
|
|
@@ -1881,7 +2207,50 @@ var createFetchChatTransport = ({
|
|
|
1881
2207
|
method: "DELETE",
|
|
1882
2208
|
headers: jsonHeaders(await loadHeaders())
|
|
1883
2209
|
});
|
|
1884
|
-
if (!response.ok) throw
|
|
2210
|
+
if (!response.ok) throw transportError(response, "Failed to delete chat thread.");
|
|
2211
|
+
return response.json();
|
|
2212
|
+
},
|
|
2213
|
+
async subscribeThreadEvents(input, onEvent) {
|
|
2214
|
+
if (!routes.threadEvents) return;
|
|
2215
|
+
const response = await fetchImpl(routes.threadEvents({ cursor: input.cursor }), {
|
|
2216
|
+
method: "GET",
|
|
2217
|
+
signal: input.signal,
|
|
2218
|
+
headers: streamHeaders(await loadHeaders())
|
|
2219
|
+
});
|
|
2220
|
+
await consumeSseResponse(
|
|
2221
|
+
response,
|
|
2222
|
+
(event) => onEvent(event),
|
|
2223
|
+
{ signal: input.signal }
|
|
2224
|
+
);
|
|
2225
|
+
},
|
|
2226
|
+
async resumeHitl(input) {
|
|
2227
|
+
if (!routes.resumeRun) throw new ChatTransportError("HITL resume is not configured.", 501);
|
|
2228
|
+
const response = await fetchImpl(routes.resumeRun({ runId: input.runId }), {
|
|
2229
|
+
method: "POST",
|
|
2230
|
+
headers: jsonHeaders(await loadHeaders()),
|
|
2231
|
+
body: JSON.stringify({
|
|
2232
|
+
schema_version: "agents24.hitl.resume.v1",
|
|
2233
|
+
interrupt_id: input.interruptId,
|
|
2234
|
+
decisions: input.decisions,
|
|
2235
|
+
client: input.client
|
|
2236
|
+
})
|
|
2237
|
+
});
|
|
2238
|
+
if (!response.ok) throw transportError(response, "Failed to resume chat run.");
|
|
2239
|
+
return response.json();
|
|
2240
|
+
},
|
|
2241
|
+
async startMcpAuth(input) {
|
|
2242
|
+
if (!routes.startMcpAuth) throw new ChatTransportError("MCP auth is not configured.", 501);
|
|
2243
|
+
const response = await fetchImpl(routes.startMcpAuth({ runId: input.runId, serverId: input.serverId }), {
|
|
2244
|
+
method: "POST",
|
|
2245
|
+
headers: jsonHeaders(await loadHeaders()),
|
|
2246
|
+
body: JSON.stringify({
|
|
2247
|
+
interrupt_id: input.interruptId || void 0,
|
|
2248
|
+
principal_type: input.principalType || void 0,
|
|
2249
|
+
principal_id: input.principalId || void 0,
|
|
2250
|
+
client: input.client
|
|
2251
|
+
})
|
|
2252
|
+
});
|
|
2253
|
+
if (!response.ok) throw transportError(response, "Failed to start MCP authorization.");
|
|
1885
2254
|
return response.json();
|
|
1886
2255
|
}
|
|
1887
2256
|
};
|
|
@@ -1891,6 +2260,7 @@ var createFetchChatTransport = ({
|
|
|
1891
2260
|
var import_message_scroller2 = require("@shadcn/react/message-scroller");
|
|
1892
2261
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1893
2262
|
0 && (module.exports = {
|
|
2263
|
+
ChatTransportError,
|
|
1894
2264
|
DEFAULT_THREAD_PAGE_SIZE,
|
|
1895
2265
|
DefaultChatPart,
|
|
1896
2266
|
DefaultToolPart,
|
|
@@ -1905,33 +2275,38 @@ var import_message_scroller2 = require("@shadcn/react/message-scroller");
|
|
|
1905
2275
|
MessageScroller,
|
|
1906
2276
|
activeRunIdFromThread,
|
|
1907
2277
|
activeRunIdFromThreadDetail,
|
|
2278
|
+
appendStableStreamingTurn,
|
|
1908
2279
|
assistantTextFromParts,
|
|
1909
2280
|
assistantTextFromResponseBlocks,
|
|
2281
|
+
autoAttachRunIdFromThread,
|
|
1910
2282
|
compressionFromContextWindow,
|
|
1911
2283
|
consumeSseResponse,
|
|
1912
2284
|
createChatId,
|
|
1913
2285
|
createFetchChatTransport,
|
|
2286
|
+
findStableAssistantMessageIndex,
|
|
1914
2287
|
getActiveStreamingTextPartId,
|
|
1915
2288
|
hasStaleUnfinishedAssistantCache,
|
|
1916
2289
|
hasUnfinishedAssistantMessage,
|
|
1917
2290
|
isActiveStreamingTextPart,
|
|
2291
|
+
isAutoAttachThreadStatus,
|
|
1918
2292
|
isRunningThreadStatus,
|
|
1919
2293
|
latestContextWindowFromThread,
|
|
1920
2294
|
mergeContextWindow,
|
|
1921
2295
|
mergeContextWindowUpdate,
|
|
1922
2296
|
mergeReasoningSteps,
|
|
2297
|
+
mergeStoredThreadsForRefresh,
|
|
1923
2298
|
normalizeContextCompression,
|
|
1924
2299
|
normalizeContextWindow,
|
|
1925
2300
|
parseSseBlock,
|
|
1926
2301
|
partsFromResponseBlocks,
|
|
1927
2302
|
reasoningStepsFromParts,
|
|
1928
2303
|
renderChatPart,
|
|
1929
|
-
textFromFinalOutput,
|
|
1930
2304
|
threadActivityDate,
|
|
1931
2305
|
threadDetailToMessages,
|
|
1932
2306
|
threadPaging,
|
|
1933
2307
|
titleFromMessage,
|
|
1934
2308
|
toolStateFromStatus,
|
|
2309
|
+
upsertStableAssistantMessage,
|
|
1935
2310
|
useAgents24ChatController,
|
|
1936
2311
|
useMessageScroller,
|
|
1937
2312
|
useMessageScrollerScrollable,
|