@agents24/chat-react 0.2.0 → 0.3.1
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 +7 -3
- package/dist/controller-hitl.d.ts +22 -0
- package/dist/controller-options.d.ts +15 -0
- package/dist/controller-thread-events.d.ts +7 -0
- package/dist/controller.d.ts +4 -16
- package/dist/index.cjs +295 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +288 -126
- package/dist/index.js.map +1 -1
- package/dist/mcp-oauth.d.ts +2 -0
- package/dist/types.d.ts +46 -18
- package/dist/ui/agent-response-timeline.d.ts +3 -3
- package/dist/ui/hitl-presentation.d.ts +4 -0
- package/dist/ui/index.cjs +177 -163
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +174 -163
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -81,12 +81,13 @@ __export(index_exports, {
|
|
|
81
81
|
useMessageScroller: () => import_message_scroller2.useMessageScroller,
|
|
82
82
|
useMessageScrollerScrollable: () => import_message_scroller2.useMessageScrollerScrollable,
|
|
83
83
|
useMessageScrollerVisibility: () => import_message_scroller2.useMessageScrollerVisibility,
|
|
84
|
-
useStreamingText: () => useStreamingText
|
|
84
|
+
useStreamingText: () => useStreamingText,
|
|
85
|
+
waitForMcpOauthComplete: () => waitForMcpOauthComplete
|
|
85
86
|
});
|
|
86
87
|
module.exports = __toCommonJS(index_exports);
|
|
87
88
|
|
|
88
89
|
// src/controller.ts
|
|
89
|
-
var
|
|
90
|
+
var import_react4 = require("react");
|
|
90
91
|
|
|
91
92
|
// src/controller-actions.ts
|
|
92
93
|
var import_react = require("react");
|
|
@@ -160,6 +161,47 @@ function useControllerMessageActions(input) {
|
|
|
160
161
|
return { handleCopy, handleDislike, handleLike, handleRetry, startNewThread, upsertLiveVoiceMessage };
|
|
161
162
|
}
|
|
162
163
|
|
|
164
|
+
// src/controller-hitl.ts
|
|
165
|
+
var import_react2 = require("react");
|
|
166
|
+
function useControllerHitl({
|
|
167
|
+
messages,
|
|
168
|
+
transport,
|
|
169
|
+
activeRunId,
|
|
170
|
+
activeRunIdRef,
|
|
171
|
+
activeThreadIdRef,
|
|
172
|
+
runStream
|
|
173
|
+
}) {
|
|
174
|
+
const [isResolvingHitl, setIsResolvingHitl] = (0, import_react2.useState)(false);
|
|
175
|
+
const pendingHitl = (0, import_react2.useMemo)(() => {
|
|
176
|
+
for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
|
|
177
|
+
const parts = messages[messageIndex]?.parts || [];
|
|
178
|
+
for (let partIndex = parts.length - 1; partIndex >= 0; partIndex -= 1) {
|
|
179
|
+
const part = parts[partIndex];
|
|
180
|
+
if (part?.kind === "hitl" && part.status === "pending") return part;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}, [messages]);
|
|
185
|
+
const resumeHitl = (0, import_react2.useCallback)(async (input) => {
|
|
186
|
+
if (!transport.resumeHitl) throw new Error("HITL resume is not configured.");
|
|
187
|
+
const runId = input.runId || activeRunIdRef.current || activeRunId;
|
|
188
|
+
if (!runId) throw new Error("No paused run is available to resume.");
|
|
189
|
+
setIsResolvingHitl(true);
|
|
190
|
+
try {
|
|
191
|
+
const result = await transport.resumeHitl({ ...input, runId });
|
|
192
|
+
const threadId = result.thread_id || activeThreadIdRef.current;
|
|
193
|
+
if (threadId) await runStream({ mode: "attach", runId: result.run_id || runId, threadId });
|
|
194
|
+
return result;
|
|
195
|
+
} finally {
|
|
196
|
+
setIsResolvingHitl(false);
|
|
197
|
+
}
|
|
198
|
+
}, [activeRunId, activeRunIdRef, activeThreadIdRef, runStream, transport]);
|
|
199
|
+
return { pendingHitl, isResolvingHitl, resumeHitl };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/controller-thread-events.ts
|
|
203
|
+
var import_react3 = require("react");
|
|
204
|
+
|
|
163
205
|
// src/context-window.ts
|
|
164
206
|
var SOURCE_PRIORITY = {
|
|
165
207
|
unknown: 0,
|
|
@@ -344,6 +386,22 @@ var assistantTextFromEvents = (events) => {
|
|
|
344
386
|
};
|
|
345
387
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
346
388
|
var optionalString = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
389
|
+
var HITL_ACTIONS_BY_KIND = {
|
|
390
|
+
tool_review: ["approve", "reject"],
|
|
391
|
+
mcp_auth: ["connect", "skip"],
|
|
392
|
+
user_approval: ["approve", "reject"],
|
|
393
|
+
app_data_permission: ["approve", "reject"]
|
|
394
|
+
};
|
|
395
|
+
var HITL_STATUSES = ["pending", "resolved", "expired", "cancelled", "invalidated"];
|
|
396
|
+
function exactHitlActions(kind, value) {
|
|
397
|
+
if (!Array.isArray(value)) throw new Error("Invalid V2 HITL response block.");
|
|
398
|
+
const actions = value.map((item) => String(item));
|
|
399
|
+
const expected = HITL_ACTIONS_BY_KIND[kind];
|
|
400
|
+
if (actions.length !== expected.length || actions.some((action, index) => action !== expected[index])) {
|
|
401
|
+
throw new Error("Invalid V2 HITL response block.");
|
|
402
|
+
}
|
|
403
|
+
return actions;
|
|
404
|
+
}
|
|
347
405
|
var toolStateFromStatus = (status) => {
|
|
348
406
|
const normalized = String(status || "").trim().toLowerCase();
|
|
349
407
|
if (["failed", "error"].includes(normalized)) return "output-error";
|
|
@@ -444,13 +502,39 @@ var partsFromResponseBlocks = (blocks, fallbackText) => {
|
|
|
444
502
|
}
|
|
445
503
|
if (block.kind === "hitl_request") {
|
|
446
504
|
const hitl = asRecord(block.hitl) || block;
|
|
505
|
+
if (hitl.schema_version !== "agents24.hitl.interrupt.v2") {
|
|
506
|
+
throw new Error("Invalid V2 HITL response block.");
|
|
507
|
+
}
|
|
508
|
+
const interruptId = optionalString(block.interruptId) || optionalString(hitl.interrupt_id);
|
|
509
|
+
const hitlKind = optionalString(block.hitlKind) || optionalString(hitl.kind);
|
|
510
|
+
const status = optionalString(block.status) || optionalString(hitl.status) || "pending";
|
|
511
|
+
if (!interruptId || !["tool_review", "mcp_auth", "user_approval", "app_data_permission"].includes(String(hitlKind)) || !HITL_STATUSES.includes(status)) {
|
|
512
|
+
throw new Error("Invalid V2 HITL response block.");
|
|
513
|
+
}
|
|
514
|
+
const kind = hitlKind;
|
|
515
|
+
const allowedActions = exactHitlActions(kind, hitl.allowed_actions);
|
|
516
|
+
const resolution = asRecord(block.resolution);
|
|
517
|
+
const resolver = asRecord(resolution?.resolver);
|
|
447
518
|
parts.push({
|
|
448
519
|
id,
|
|
449
520
|
type: "hitl",
|
|
450
521
|
kind: "hitl",
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
522
|
+
interruptId,
|
|
523
|
+
hitlKind: kind,
|
|
524
|
+
message: optionalString(hitl.message) || optionalString(block.text) || "Input is required to continue.",
|
|
525
|
+
allowedActions,
|
|
526
|
+
status,
|
|
527
|
+
presentation: asRecord(hitl.presentation) || {},
|
|
528
|
+
resolution: resolution ? {
|
|
529
|
+
action: ["approve", "reject", "connect", "skip"].includes(String(resolution.action)) ? String(resolution.action) : null,
|
|
530
|
+
outcome: optionalString(resolution.outcome),
|
|
531
|
+
reason: optionalString(resolution.reason),
|
|
532
|
+
resolvedAt: optionalString(resolution.resolved_at),
|
|
533
|
+
resolver: resolver ? {
|
|
534
|
+
principalType: optionalString(resolver.principal_type) || optionalString(resolver.principalType) || null,
|
|
535
|
+
principalId: optionalString(resolver.principal_id) || optionalString(resolver.principalId) || null
|
|
536
|
+
} : null
|
|
537
|
+
} : null,
|
|
454
538
|
raw: block
|
|
455
539
|
});
|
|
456
540
|
return;
|
|
@@ -690,6 +774,47 @@ var applyThreadSummaryEvent = (currentThreads, event) => {
|
|
|
690
774
|
return sortByActivity(next);
|
|
691
775
|
};
|
|
692
776
|
|
|
777
|
+
// src/controller-thread-events.ts
|
|
778
|
+
function useControllerThreadEvents({
|
|
779
|
+
transport,
|
|
780
|
+
storage,
|
|
781
|
+
refresh,
|
|
782
|
+
setThreads
|
|
783
|
+
}) {
|
|
784
|
+
const cursorRef = (0, import_react3.useRef)(null);
|
|
785
|
+
(0, import_react3.useEffect)(() => {
|
|
786
|
+
if (!transport.subscribeThreadEvents) return;
|
|
787
|
+
let cancelled = false;
|
|
788
|
+
let retryTimeout = null;
|
|
789
|
+
let controller = null;
|
|
790
|
+
const connect = () => {
|
|
791
|
+
if (cancelled) return;
|
|
792
|
+
controller = new AbortController();
|
|
793
|
+
transport.subscribeThreadEvents?.(
|
|
794
|
+
{ cursor: cursorRef.current, signal: controller.signal },
|
|
795
|
+
async (event) => {
|
|
796
|
+
if (typeof event.cursor === "number") cursorRef.current = event.cursor;
|
|
797
|
+
if (event.event === "snapshot_required") {
|
|
798
|
+
await refresh().catch(() => void 0);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
storage.setThreads(applyThreadSummaryEvent(storage.listThreads(), event));
|
|
802
|
+
setThreads(storage.listThreads());
|
|
803
|
+
}
|
|
804
|
+
).catch((error) => {
|
|
805
|
+
if (cancelled || isAbortError(error)) return;
|
|
806
|
+
retryTimeout = setTimeout(connect, 1500);
|
|
807
|
+
});
|
|
808
|
+
};
|
|
809
|
+
connect();
|
|
810
|
+
return () => {
|
|
811
|
+
cancelled = true;
|
|
812
|
+
if (retryTimeout) clearTimeout(retryTimeout);
|
|
813
|
+
controller?.abort();
|
|
814
|
+
};
|
|
815
|
+
}, [refresh, setThreads, storage, transport]);
|
|
816
|
+
}
|
|
817
|
+
|
|
693
818
|
// src/message-lifecycle.ts
|
|
694
819
|
function findStableAssistantMessageIndex(messages, input) {
|
|
695
820
|
return messages.findIndex(
|
|
@@ -728,83 +853,83 @@ function upsertStableAssistantMessage(messages, input) {
|
|
|
728
853
|
}
|
|
729
854
|
|
|
730
855
|
// src/controller.ts
|
|
731
|
-
function useAgents24ChatController({
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
856
|
+
function useAgents24ChatController(options) {
|
|
857
|
+
const {
|
|
858
|
+
transport,
|
|
859
|
+
storage,
|
|
860
|
+
activeThreadId: controlledActiveThreadId,
|
|
861
|
+
pageSize = DEFAULT_THREAD_PAGE_SIZE,
|
|
862
|
+
storageKey,
|
|
863
|
+
createId = createChatId,
|
|
864
|
+
onActiveThreadIdChange,
|
|
865
|
+
onSourceClick,
|
|
866
|
+
onStreamErrorMessage,
|
|
867
|
+
onRuntimeEvent,
|
|
868
|
+
onThreadDetailLoaded
|
|
869
|
+
} = options;
|
|
744
870
|
const activeThreadId = controlledActiveThreadId ?? storage.getActiveThreadId?.() ?? null;
|
|
745
871
|
const initialCached = activeThreadId ? storage.getThread(activeThreadId) : void 0;
|
|
746
|
-
const [messages, setMessages] = (0,
|
|
747
|
-
const [isLoading, setIsLoading] = (0,
|
|
748
|
-
const [isLoadingHistory, setIsLoadingHistory] = (0,
|
|
749
|
-
const [isLoadingOlder, setIsLoadingOlder] = (0,
|
|
750
|
-
const [hasOlderTurns, setHasOlderTurns] = (0,
|
|
751
|
-
const [streamingContent, setStreamingContent] = (0,
|
|
752
|
-
const [streamingMessageId, setStreamingMessageId] = (0,
|
|
753
|
-
const [contextStatus, setContextStatus] = (0,
|
|
754
|
-
const [currentReasoning, setCurrentReasoning] = (0,
|
|
755
|
-
const [liked, setLiked] = (0,
|
|
756
|
-
const [disliked, setDisliked] = (0,
|
|
757
|
-
const [copiedMessageId, setCopiedMessageId] = (0,
|
|
758
|
-
const [lastThinkingDurationMs, setLastThinkingDurationMs] = (0,
|
|
759
|
-
const [threads, setThreads] = (0,
|
|
760
|
-
const [isRefreshingThreads, setIsRefreshingThreads] = (0,
|
|
761
|
-
const [isSelectingThread, setIsSelectingThread] = (0,
|
|
762
|
-
const [activeRunId, setActiveRunId] = (0,
|
|
763
|
-
const textareaRef = (0,
|
|
764
|
-
const activeThreadIdRef = (0,
|
|
765
|
-
const messagesRef = (0,
|
|
766
|
-
const nextBeforeTurnIndexRef = (0,
|
|
767
|
-
const hasOlderTurnsRef = (0,
|
|
768
|
-
const isLoadingOlderRef = (0,
|
|
769
|
-
const loadedThreadIdRef = (0,
|
|
770
|
-
const requestSeqRef = (0,
|
|
771
|
-
const isLoadingHistoryRef = (0,
|
|
772
|
-
const activeRunIdRef = (0,
|
|
773
|
-
const reattachedRunIdRef = (0,
|
|
774
|
-
const abortControllerRef = (0,
|
|
775
|
-
const [lifecycleAbortController] = (0,
|
|
776
|
-
const streamingContentRef = (0,
|
|
777
|
-
const streamingMessageIdRef = (0,
|
|
778
|
-
const reasoningRef = (0,
|
|
779
|
-
const liveVoiceIdsRef = (0,
|
|
780
|
-
const refreshSeqRef = (0,
|
|
781
|
-
const
|
|
782
|
-
const setActiveRunIdValue = (0, import_react2.useCallback)((runId) => {
|
|
872
|
+
const [messages, setMessages] = (0, import_react4.useState)(() => initialCached?.messages || []);
|
|
873
|
+
const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
|
|
874
|
+
const [isLoadingHistory, setIsLoadingHistory] = (0, import_react4.useState)(() => Boolean(activeThreadId) && !initialCached?.messages?.length);
|
|
875
|
+
const [isLoadingOlder, setIsLoadingOlder] = (0, import_react4.useState)(false);
|
|
876
|
+
const [hasOlderTurns, setHasOlderTurns] = (0, import_react4.useState)(Boolean(initialCached?.hasOlderTurns));
|
|
877
|
+
const [streamingContent, setStreamingContent] = (0, import_react4.useState)("");
|
|
878
|
+
const [streamingMessageId, setStreamingMessageId] = (0, import_react4.useState)(null);
|
|
879
|
+
const [contextStatus, setContextStatus] = (0, import_react4.useState)(null);
|
|
880
|
+
const [currentReasoning, setCurrentReasoning] = (0, import_react4.useState)([]);
|
|
881
|
+
const [liked, setLiked] = (0, import_react4.useState)({});
|
|
882
|
+
const [disliked, setDisliked] = (0, import_react4.useState)({});
|
|
883
|
+
const [copiedMessageId, setCopiedMessageId] = (0, import_react4.useState)(null);
|
|
884
|
+
const [lastThinkingDurationMs, setLastThinkingDurationMs] = (0, import_react4.useState)(null);
|
|
885
|
+
const [threads, setThreads] = (0, import_react4.useState)(() => storage.listThreads());
|
|
886
|
+
const [isRefreshingThreads, setIsRefreshingThreads] = (0, import_react4.useState)(false);
|
|
887
|
+
const [isSelectingThread, setIsSelectingThread] = (0, import_react4.useState)(false);
|
|
888
|
+
const [activeRunId, setActiveRunId] = (0, import_react4.useState)(null);
|
|
889
|
+
const textareaRef = (0, import_react4.useRef)(null);
|
|
890
|
+
const activeThreadIdRef = (0, import_react4.useRef)(activeThreadId);
|
|
891
|
+
const messagesRef = (0, import_react4.useRef)(messages);
|
|
892
|
+
const nextBeforeTurnIndexRef = (0, import_react4.useRef)(initialCached?.nextBeforeTurnIndex ?? null);
|
|
893
|
+
const hasOlderTurnsRef = (0, import_react4.useRef)(Boolean(initialCached?.hasOlderTurns));
|
|
894
|
+
const isLoadingOlderRef = (0, import_react4.useRef)(false);
|
|
895
|
+
const loadedThreadIdRef = (0, import_react4.useRef)(initialCached?.messages?.length ? activeThreadId : null);
|
|
896
|
+
const requestSeqRef = (0, import_react4.useRef)(0);
|
|
897
|
+
const isLoadingHistoryRef = (0, import_react4.useRef)(isLoadingHistory);
|
|
898
|
+
const activeRunIdRef = (0, import_react4.useRef)(null);
|
|
899
|
+
const reattachedRunIdRef = (0, import_react4.useRef)(null);
|
|
900
|
+
const abortControllerRef = (0, import_react4.useRef)(null);
|
|
901
|
+
const [lifecycleAbortController] = (0, import_react4.useState)(() => new AbortController());
|
|
902
|
+
const streamingContentRef = (0, import_react4.useRef)("");
|
|
903
|
+
const streamingMessageIdRef = (0, import_react4.useRef)(null);
|
|
904
|
+
const reasoningRef = (0, import_react4.useRef)([]);
|
|
905
|
+
const liveVoiceIdsRef = (0, import_react4.useRef)({});
|
|
906
|
+
const refreshSeqRef = (0, import_react4.useRef)(0);
|
|
907
|
+
const setActiveRunIdValue = (0, import_react4.useCallback)((runId) => {
|
|
783
908
|
activeRunIdRef.current = runId;
|
|
784
909
|
setActiveRunId(runId);
|
|
785
910
|
}, []);
|
|
786
|
-
const syncThreadsFromStorage = (0,
|
|
911
|
+
const syncThreadsFromStorage = (0, import_react4.useCallback)(() => {
|
|
787
912
|
setThreads(storage.listThreads());
|
|
788
913
|
}, [storage]);
|
|
789
|
-
const upsertStoredThread = (0,
|
|
914
|
+
const upsertStoredThread = (0, import_react4.useCallback)(
|
|
790
915
|
(thread) => {
|
|
791
916
|
storage.upsertThread(thread);
|
|
792
917
|
syncThreadsFromStorage();
|
|
793
918
|
},
|
|
794
919
|
[storage, syncThreadsFromStorage]
|
|
795
920
|
);
|
|
796
|
-
(0,
|
|
921
|
+
(0, import_react4.useEffect)(() => {
|
|
797
922
|
messagesRef.current = messages;
|
|
798
923
|
}, [messages]);
|
|
799
|
-
const persistThread = (0,
|
|
800
|
-
(threadId, nextMessages, paging,
|
|
924
|
+
const persistThread = (0, import_react4.useCallback)(
|
|
925
|
+
(threadId, nextMessages, paging, options2) => {
|
|
801
926
|
const existing = storage.getThread(threadId);
|
|
802
927
|
const firstUser = nextMessages.find((message) => message.role === "user");
|
|
803
928
|
upsertStoredThread({
|
|
804
929
|
...existing || {},
|
|
805
930
|
id: threadId,
|
|
806
931
|
title: existing?.title || (firstUser ? titleFromMessage(firstUser.content, firstUser.attachments || []) : "New chat"),
|
|
807
|
-
updated_at:
|
|
932
|
+
updated_at: options2?.updatedAt || (options2?.touch ? (/* @__PURE__ */ new Date()).toISOString() : existing?.updated_at) || (/* @__PURE__ */ new Date()).toISOString(),
|
|
808
933
|
messages: nextMessages,
|
|
809
934
|
isHydrated: true,
|
|
810
935
|
hasOlderTurns: paging?.hasOlderTurns ?? hasOlderTurnsRef.current,
|
|
@@ -813,7 +938,7 @@ function useAgents24ChatController({
|
|
|
813
938
|
},
|
|
814
939
|
[storage, upsertStoredThread]
|
|
815
940
|
);
|
|
816
|
-
const markThreadRunStatus = (0,
|
|
941
|
+
const markThreadRunStatus = (0, import_react4.useCallback)(
|
|
817
942
|
(threadId, runId, status, lastEventSeq) => {
|
|
818
943
|
const existing = storage.getThread(threadId);
|
|
819
944
|
if (!existing || !runId) return;
|
|
@@ -843,7 +968,7 @@ function useAgents24ChatController({
|
|
|
843
968
|
},
|
|
844
969
|
[storage, upsertStoredThread]
|
|
845
970
|
);
|
|
846
|
-
const refresh = (0,
|
|
971
|
+
const refresh = (0, import_react4.useCallback)(async () => {
|
|
847
972
|
const seq = ++refreshSeqRef.current;
|
|
848
973
|
setIsRefreshingThreads(true);
|
|
849
974
|
try {
|
|
@@ -857,7 +982,7 @@ function useAgents24ChatController({
|
|
|
857
982
|
if (!lifecycleAbortController.signal.aborted && seq === refreshSeqRef.current) setIsRefreshingThreads(false);
|
|
858
983
|
}
|
|
859
984
|
}, [lifecycleAbortController, storage, transport]);
|
|
860
|
-
const applyThreadId = (0,
|
|
985
|
+
const applyThreadId = (0, import_react4.useCallback)(
|
|
861
986
|
(threadId, baseMessages) => {
|
|
862
987
|
if (!threadId || activeThreadIdRef.current === threadId) return;
|
|
863
988
|
activeThreadIdRef.current = threadId;
|
|
@@ -871,15 +996,15 @@ function useAgents24ChatController({
|
|
|
871
996
|
streamingContentRef.current = value;
|
|
872
997
|
setStreamingContent(value);
|
|
873
998
|
};
|
|
874
|
-
const setLoadingHistory = (0,
|
|
999
|
+
const setLoadingHistory = (0, import_react4.useCallback)((value) => {
|
|
875
1000
|
isLoadingHistoryRef.current = value;
|
|
876
1001
|
setIsLoadingHistory(value);
|
|
877
1002
|
}, []);
|
|
878
|
-
const setReasoningSteps = (0,
|
|
1003
|
+
const setReasoningSteps = (0, import_react4.useCallback)((value) => {
|
|
879
1004
|
reasoningRef.current = value || [];
|
|
880
1005
|
setCurrentReasoning(value || []);
|
|
881
1006
|
}, []);
|
|
882
|
-
const detachActiveStream = (0,
|
|
1007
|
+
const detachActiveStream = (0, import_react4.useCallback)(() => {
|
|
883
1008
|
const controller = abortControllerRef.current;
|
|
884
1009
|
abortControllerRef.current = null;
|
|
885
1010
|
abortDetachedStream(controller);
|
|
@@ -893,7 +1018,7 @@ function useAgents24ChatController({
|
|
|
893
1018
|
setStreamingContent("");
|
|
894
1019
|
setCurrentReasoning([]);
|
|
895
1020
|
}, [setActiveRunIdValue]);
|
|
896
|
-
const clearMissingThread = (0,
|
|
1021
|
+
const clearMissingThread = (0, import_react4.useCallback)(
|
|
897
1022
|
(threadId) => {
|
|
898
1023
|
storage.deleteThread?.(threadId);
|
|
899
1024
|
syncThreadsFromStorage();
|
|
@@ -915,7 +1040,7 @@ function useAgents24ChatController({
|
|
|
915
1040
|
},
|
|
916
1041
|
[detachActiveStream, onActiveThreadIdChange, setLoadingHistory, storage, syncThreadsFromStorage]
|
|
917
1042
|
);
|
|
918
|
-
const setLiveAssistantMessage = (0,
|
|
1043
|
+
const setLiveAssistantMessage = (0, import_react4.useCallback)(
|
|
919
1044
|
(input) => {
|
|
920
1045
|
setMessages((prev) => {
|
|
921
1046
|
const next = upsertStableAssistantMessage(prev, {
|
|
@@ -947,7 +1072,7 @@ function useAgents24ChatController({
|
|
|
947
1072
|
},
|
|
948
1073
|
[]
|
|
949
1074
|
);
|
|
950
|
-
const finalizeAssistantMessage = (0,
|
|
1075
|
+
const finalizeAssistantMessage = (0, import_react4.useCallback)(
|
|
951
1076
|
(input) => {
|
|
952
1077
|
const content = input.error || input.assistantText.trim();
|
|
953
1078
|
if (!content) return input.baseMessages;
|
|
@@ -1001,7 +1126,7 @@ function useAgents24ChatController({
|
|
|
1001
1126
|
},
|
|
1002
1127
|
[createId, persistThread, storage, upsertStoredThread]
|
|
1003
1128
|
);
|
|
1004
|
-
const loadThread = (0,
|
|
1129
|
+
const loadThread = (0, import_react4.useCallback)(
|
|
1005
1130
|
async (threadId) => {
|
|
1006
1131
|
const seq = ++requestSeqRef.current;
|
|
1007
1132
|
setLoadingHistory(true);
|
|
@@ -1046,7 +1171,7 @@ function useAgents24ChatController({
|
|
|
1046
1171
|
},
|
|
1047
1172
|
[clearMissingThread, lifecycleAbortController, onThreadDetailLoaded, pageSize, setLoadingHistory, transport, upsertStoredThread]
|
|
1048
1173
|
);
|
|
1049
|
-
const loadOlderTurns = (0,
|
|
1174
|
+
const loadOlderTurns = (0, import_react4.useCallback)(async () => {
|
|
1050
1175
|
const threadId = activeThreadIdRef.current;
|
|
1051
1176
|
const beforeTurnIndex = nextBeforeTurnIndexRef.current;
|
|
1052
1177
|
if (!threadId || beforeTurnIndex === null || isLoadingOlderRef.current) return;
|
|
@@ -1082,7 +1207,7 @@ function useAgents24ChatController({
|
|
|
1082
1207
|
isLoadingOlderRef.current = false;
|
|
1083
1208
|
}
|
|
1084
1209
|
}, [clearMissingThread, lifecycleAbortController, pageSize, persistThread, transport]);
|
|
1085
|
-
const handleStreamEvent = (0,
|
|
1210
|
+
const handleStreamEvent = (0, import_react4.useCallback)(
|
|
1086
1211
|
(input) => {
|
|
1087
1212
|
const { mode, event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages } = input;
|
|
1088
1213
|
const payload = event.payload || {};
|
|
@@ -1143,7 +1268,7 @@ function useAgents24ChatController({
|
|
|
1143
1268
|
},
|
|
1144
1269
|
[applyThreadId, finalizeAssistantMessage, markThreadRunStatus, onRuntimeEvent, onStreamErrorMessage, setActiveRunIdValue, setLiveAssistantMessage, setReasoningSteps]
|
|
1145
1270
|
);
|
|
1146
|
-
const runStream = (0,
|
|
1271
|
+
const runStream = (0, import_react4.useCallback)(
|
|
1147
1272
|
async (input) => {
|
|
1148
1273
|
const startedAt = Date.now();
|
|
1149
1274
|
const controller = new AbortController();
|
|
@@ -1266,18 +1391,26 @@ function useAgents24ChatController({
|
|
|
1266
1391
|
},
|
|
1267
1392
|
[createId, finalizeAssistantMessage, handleStreamEvent, onStreamErrorMessage, persistThread, refresh, setActiveRunIdValue, setReasoningSteps, transport]
|
|
1268
1393
|
);
|
|
1269
|
-
const handleSubmit = (0,
|
|
1394
|
+
const handleSubmit = (0, import_react4.useCallback)(
|
|
1270
1395
|
async (message) => {
|
|
1271
1396
|
if (!message.text.trim() && !(message.files || []).length) return;
|
|
1272
1397
|
await runStream({ mode: "submit", message });
|
|
1273
1398
|
},
|
|
1274
1399
|
[runStream]
|
|
1275
1400
|
);
|
|
1276
|
-
const attachRun = (0,
|
|
1401
|
+
const attachRun = (0, import_react4.useCallback)(async (runId, threadId) => {
|
|
1277
1402
|
const resolvedThreadId = threadId ?? activeThreadIdRef.current;
|
|
1278
1403
|
if (runId && resolvedThreadId) await runStream({ mode: "attach", runId, threadId: resolvedThreadId });
|
|
1279
1404
|
}, [runStream]);
|
|
1280
|
-
const
|
|
1405
|
+
const { pendingHitl, isResolvingHitl, resumeHitl } = useControllerHitl({
|
|
1406
|
+
messages,
|
|
1407
|
+
transport,
|
|
1408
|
+
activeRunId,
|
|
1409
|
+
activeRunIdRef,
|
|
1410
|
+
activeThreadIdRef,
|
|
1411
|
+
runStream
|
|
1412
|
+
});
|
|
1413
|
+
const handleStop = (0, import_react4.useCallback)(() => {
|
|
1281
1414
|
const runId = activeRunIdRef.current;
|
|
1282
1415
|
const partial = streamingContentRef.current;
|
|
1283
1416
|
const liveMessageId = streamingMessageIdRef.current;
|
|
@@ -1301,56 +1434,25 @@ function useAgents24ChatController({
|
|
|
1301
1434
|
});
|
|
1302
1435
|
}
|
|
1303
1436
|
}, [finalizeAssistantMessage, lastThinkingDurationMs, setActiveRunIdValue, setReasoningSteps, transport]);
|
|
1304
|
-
(0,
|
|
1437
|
+
(0, import_react4.useEffect)(() => {
|
|
1305
1438
|
refresh().catch((error) => {
|
|
1306
1439
|
if (!isAbortError(error) && !lifecycleAbortController.signal.aborted) {
|
|
1307
1440
|
setThreads(storage.listThreads());
|
|
1308
1441
|
}
|
|
1309
1442
|
});
|
|
1310
1443
|
}, [lifecycleAbortController, refresh, storage]);
|
|
1311
|
-
(0,
|
|
1444
|
+
(0, import_react4.useEffect)(() => {
|
|
1312
1445
|
return () => {
|
|
1313
1446
|
lifecycleAbortController.abort();
|
|
1314
1447
|
abortDetachedStream(abortControllerRef.current);
|
|
1315
1448
|
abortControllerRef.current = null;
|
|
1316
1449
|
};
|
|
1317
1450
|
}, [lifecycleAbortController]);
|
|
1318
|
-
(
|
|
1319
|
-
|
|
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)(() => {
|
|
1451
|
+
useControllerThreadEvents({ transport, storage, refresh, setThreads });
|
|
1452
|
+
(0, import_react4.useEffect)(() => {
|
|
1351
1453
|
syncThreadsFromStorage();
|
|
1352
1454
|
}, [storageKey, syncThreadsFromStorage]);
|
|
1353
|
-
(0,
|
|
1455
|
+
(0, import_react4.useEffect)(() => {
|
|
1354
1456
|
const previous = activeThreadIdRef.current;
|
|
1355
1457
|
activeThreadIdRef.current = activeThreadId;
|
|
1356
1458
|
if (activeThreadId && previous === activeThreadId && (activeRunIdRef.current || streamingMessageIdRef.current)) {
|
|
@@ -1394,14 +1496,14 @@ function useAgents24ChatController({
|
|
|
1394
1496
|
}
|
|
1395
1497
|
void loadThread(activeThreadId).catch(() => setLoadingHistory(false));
|
|
1396
1498
|
}, [activeThreadId, detachActiveStream, loadThread, setLoadingHistory, storage]);
|
|
1397
|
-
(0,
|
|
1499
|
+
(0, import_react4.useEffect)(() => {
|
|
1398
1500
|
const threadId = activeThreadId;
|
|
1399
1501
|
if (!threadId || isLoadingHistory || loadedThreadIdRef.current !== threadId || activeRunIdRef.current) return;
|
|
1400
1502
|
const runId = autoAttachRunIdFromThread(storage.getThread(threadId));
|
|
1401
1503
|
if (!runId || reattachedRunIdRef.current === runId) return;
|
|
1402
1504
|
void runStream({ mode: "attach", threadId, runId });
|
|
1403
1505
|
}, [activeThreadId, isLoadingHistory, runStream, storage, storageKey]);
|
|
1404
|
-
(0,
|
|
1506
|
+
(0, import_react4.useEffect)(() => {
|
|
1405
1507
|
const threadId = activeThreadId;
|
|
1406
1508
|
if (!threadId || isLoadingHistoryRef.current || activeRunIdRef.current || streamingMessageIdRef.current) {
|
|
1407
1509
|
return;
|
|
@@ -1442,7 +1544,7 @@ function useAgents24ChatController({
|
|
|
1442
1544
|
setMessages,
|
|
1443
1545
|
storage
|
|
1444
1546
|
});
|
|
1445
|
-
const loadThreadById = (0,
|
|
1547
|
+
const loadThreadById = (0, import_react4.useCallback)(
|
|
1446
1548
|
async (threadId) => {
|
|
1447
1549
|
if (!threadId) return;
|
|
1448
1550
|
setIsSelectingThread(true);
|
|
@@ -1461,7 +1563,7 @@ function useAgents24ChatController({
|
|
|
1461
1563
|
[detachActiveStream, loadThread, onActiveThreadIdChange, storage]
|
|
1462
1564
|
);
|
|
1463
1565
|
const activeThread = activeThreadId ? storage.getThread(activeThreadId) || threads.find((thread) => thread.id === activeThreadId) || null : null;
|
|
1464
|
-
return (0,
|
|
1566
|
+
return (0, import_react4.useMemo)(() => ({
|
|
1465
1567
|
threads,
|
|
1466
1568
|
activeThreadId,
|
|
1467
1569
|
activeThread,
|
|
@@ -1481,8 +1583,11 @@ function useAgents24ChatController({
|
|
|
1481
1583
|
copiedMessageId,
|
|
1482
1584
|
lastThinkingDurationMs,
|
|
1483
1585
|
activeRunId,
|
|
1586
|
+
pendingHitl,
|
|
1587
|
+
isResolvingHitl,
|
|
1484
1588
|
handleSubmit,
|
|
1485
1589
|
attachRun,
|
|
1590
|
+
resumeHitl,
|
|
1486
1591
|
handleStop,
|
|
1487
1592
|
handleCopy,
|
|
1488
1593
|
handleLike,
|
|
@@ -1521,8 +1626,11 @@ function useAgents24ChatController({
|
|
|
1521
1626
|
loadThreadById,
|
|
1522
1627
|
loadOlderTurns,
|
|
1523
1628
|
messages,
|
|
1629
|
+
isResolvingHitl,
|
|
1524
1630
|
onSourceClick,
|
|
1631
|
+
pendingHitl,
|
|
1525
1632
|
refresh,
|
|
1633
|
+
resumeHitl,
|
|
1526
1634
|
streamingContent,
|
|
1527
1635
|
streamingMessageId,
|
|
1528
1636
|
startNewThread,
|
|
@@ -1875,6 +1983,59 @@ var LatestThreadScroller = {
|
|
|
1875
1983
|
Outline: LatestThreadScrollerOutline
|
|
1876
1984
|
};
|
|
1877
1985
|
|
|
1986
|
+
// src/mcp-oauth.ts
|
|
1987
|
+
var CONNECTION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1988
|
+
function normalizedOrigin(value) {
|
|
1989
|
+
try {
|
|
1990
|
+
return new URL(value).origin;
|
|
1991
|
+
} catch {
|
|
1992
|
+
throw new Error("MCP authorization returned an invalid callback origin.");
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
function waitForMcpOauthComplete({
|
|
1996
|
+
popup,
|
|
1997
|
+
callbackOrigin,
|
|
1998
|
+
serverId,
|
|
1999
|
+
timeoutMs = 12e4
|
|
2000
|
+
}) {
|
|
2001
|
+
if (!popup) return Promise.reject(new Error("Could not open the MCP authorization popup."));
|
|
2002
|
+
const expectedOrigin = normalizedOrigin(callbackOrigin);
|
|
2003
|
+
const expectedServerId = String(serverId || "").trim();
|
|
2004
|
+
if (!expectedServerId) return Promise.reject(new Error("MCP authorization is missing its server identity."));
|
|
2005
|
+
return new Promise((resolve, reject) => {
|
|
2006
|
+
let settled = false;
|
|
2007
|
+
const finish = (result) => {
|
|
2008
|
+
if (settled) return;
|
|
2009
|
+
settled = true;
|
|
2010
|
+
window.clearTimeout(timeout);
|
|
2011
|
+
window.clearInterval(closeTimer);
|
|
2012
|
+
window.removeEventListener("message", onMessage);
|
|
2013
|
+
if (result instanceof Error) reject(result);
|
|
2014
|
+
else resolve(result);
|
|
2015
|
+
};
|
|
2016
|
+
const onMessage = (event) => {
|
|
2017
|
+
if (event.source !== popup || event.origin !== expectedOrigin) return;
|
|
2018
|
+
const data = event.data;
|
|
2019
|
+
if (data?.type !== "mcp-oauth-complete" || data.server_id !== expectedServerId) return;
|
|
2020
|
+
if (!data.success) {
|
|
2021
|
+
finish(new Error("MCP authorization failed."));
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
const connectionId = String(data.connection_id || "").trim();
|
|
2025
|
+
if (!CONNECTION_ID_PATTERN.test(connectionId)) return;
|
|
2026
|
+
finish({ connectionId });
|
|
2027
|
+
};
|
|
2028
|
+
const timeout = window.setTimeout(
|
|
2029
|
+
() => finish(new Error("MCP authorization timed out.")),
|
|
2030
|
+
Math.max(1, timeoutMs)
|
|
2031
|
+
);
|
|
2032
|
+
const closeTimer = window.setInterval(() => {
|
|
2033
|
+
if (popup.closed) finish(new Error("MCP authorization window was closed before connecting."));
|
|
2034
|
+
}, 500);
|
|
2035
|
+
window.addEventListener("message", onMessage);
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
|
|
1878
2039
|
// src/renderers.tsx
|
|
1879
2040
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1880
2041
|
var DefaultToolPart = ({ part }) => {
|
|
@@ -1992,7 +2153,7 @@ var consumeSseResponse = async (response, onEvent, options = {}) => {
|
|
|
1992
2153
|
};
|
|
1993
2154
|
|
|
1994
2155
|
// src/streaming-text.ts
|
|
1995
|
-
var
|
|
2156
|
+
var import_react5 = require("react");
|
|
1996
2157
|
var DEFAULT_COMPLETED_TEXT_CACHE_SIZE = 200;
|
|
1997
2158
|
var defaultCompletedTextCache = /* @__PURE__ */ new Map();
|
|
1998
2159
|
var defaultStreamingTextCache = {
|
|
@@ -2020,24 +2181,24 @@ function useStreamingText({
|
|
|
2020
2181
|
maxCatchupChars = 20
|
|
2021
2182
|
}) {
|
|
2022
2183
|
const cacheAdapter = cache === false ? null : cache;
|
|
2023
|
-
const [displayedText, setDisplayedText] = (0,
|
|
2184
|
+
const [displayedText, setDisplayedText] = (0, import_react5.useState)(() => {
|
|
2024
2185
|
const cachedText = cacheAdapter?.get(id);
|
|
2025
2186
|
return isStreaming && cachedText !== text ? "" : text;
|
|
2026
2187
|
});
|
|
2027
|
-
const targetRef = (0,
|
|
2028
|
-
const displayedRef = (0,
|
|
2029
|
-
const rafRef = (0,
|
|
2030
|
-
const lastFrameAtRef = (0,
|
|
2031
|
-
const idRef = (0,
|
|
2032
|
-
const shouldAnimateRef = (0,
|
|
2033
|
-
(0,
|
|
2188
|
+
const targetRef = (0, import_react5.useRef)(text);
|
|
2189
|
+
const displayedRef = (0, import_react5.useRef)(displayedText);
|
|
2190
|
+
const rafRef = (0, import_react5.useRef)(null);
|
|
2191
|
+
const lastFrameAtRef = (0, import_react5.useRef)(null);
|
|
2192
|
+
const idRef = (0, import_react5.useRef)(id);
|
|
2193
|
+
const shouldAnimateRef = (0, import_react5.useRef)(isStreaming);
|
|
2194
|
+
(0, import_react5.useEffect)(() => {
|
|
2034
2195
|
if (isStreaming || !text) return;
|
|
2035
2196
|
cacheAdapter?.set(id, text);
|
|
2036
2197
|
}, [cacheAdapter, id, isStreaming, text]);
|
|
2037
|
-
(0,
|
|
2198
|
+
(0, import_react5.useEffect)(() => {
|
|
2038
2199
|
targetRef.current = text;
|
|
2039
2200
|
}, [text]);
|
|
2040
|
-
(0,
|
|
2201
|
+
(0, import_react5.useEffect)(() => {
|
|
2041
2202
|
if (idRef.current === id) return;
|
|
2042
2203
|
idRef.current = id;
|
|
2043
2204
|
const cachedText = cacheAdapter?.get(id);
|
|
@@ -2051,7 +2212,7 @@ function useStreamingText({
|
|
|
2051
2212
|
}
|
|
2052
2213
|
lastFrameAtRef.current = null;
|
|
2053
2214
|
}, [cacheAdapter, id, isStreaming, text]);
|
|
2054
|
-
(0,
|
|
2215
|
+
(0, import_react5.useEffect)(() => {
|
|
2055
2216
|
if (typeof window === "undefined") {
|
|
2056
2217
|
displayedRef.current = text;
|
|
2057
2218
|
setDisplayedText(text);
|
|
@@ -2229,9 +2390,10 @@ var createFetchChatTransport = ({
|
|
|
2229
2390
|
method: "POST",
|
|
2230
2391
|
headers: jsonHeaders(await loadHeaders()),
|
|
2231
2392
|
body: JSON.stringify({
|
|
2232
|
-
schema_version: "agents24.hitl.resume.
|
|
2393
|
+
schema_version: "agents24.hitl.resume.v2",
|
|
2233
2394
|
interrupt_id: input.interruptId,
|
|
2234
|
-
|
|
2395
|
+
action: input.action,
|
|
2396
|
+
comment: input.comment,
|
|
2235
2397
|
client: input.client
|
|
2236
2398
|
})
|
|
2237
2399
|
});
|
|
@@ -2311,6 +2473,7 @@ var import_message_scroller2 = require("@shadcn/react/message-scroller");
|
|
|
2311
2473
|
useMessageScroller,
|
|
2312
2474
|
useMessageScrollerScrollable,
|
|
2313
2475
|
useMessageScrollerVisibility,
|
|
2314
|
-
useStreamingText
|
|
2476
|
+
useStreamingText,
|
|
2477
|
+
waitForMcpOauthComplete
|
|
2315
2478
|
});
|
|
2316
2479
|
//# sourceMappingURL=index.cjs.map
|