@agents24/chat-react 0.2.0 → 0.3.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 +4 -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 +217 -130
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +205 -118
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +36 -18
- package/dist/ui/agent-response-timeline.d.ts +3 -3
- package/dist/ui/index.cjs +169 -156
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +169 -156
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "./chunk-EWZO4QJI.js";
|
|
16
16
|
|
|
17
17
|
// src/controller.ts
|
|
18
|
-
import { useCallback as
|
|
18
|
+
import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
19
19
|
|
|
20
20
|
// src/controller-actions.ts
|
|
21
21
|
import { useCallback } from "react";
|
|
@@ -89,6 +89,47 @@ function useControllerMessageActions(input) {
|
|
|
89
89
|
return { handleCopy, handleDislike, handleLike, handleRetry, startNewThread, upsertLiveVoiceMessage };
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// src/controller-hitl.ts
|
|
93
|
+
import { useCallback as useCallback2, useMemo, useState } from "react";
|
|
94
|
+
function useControllerHitl({
|
|
95
|
+
messages,
|
|
96
|
+
transport,
|
|
97
|
+
activeRunId,
|
|
98
|
+
activeRunIdRef,
|
|
99
|
+
activeThreadIdRef,
|
|
100
|
+
runStream
|
|
101
|
+
}) {
|
|
102
|
+
const [isResolvingHitl, setIsResolvingHitl] = useState(false);
|
|
103
|
+
const pendingHitl = useMemo(() => {
|
|
104
|
+
for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
|
|
105
|
+
const parts = messages[messageIndex]?.parts || [];
|
|
106
|
+
for (let partIndex = parts.length - 1; partIndex >= 0; partIndex -= 1) {
|
|
107
|
+
const part = parts[partIndex];
|
|
108
|
+
if (part?.kind === "hitl" && part.status === "pending") return part;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}, [messages]);
|
|
113
|
+
const resumeHitl = useCallback2(async (input) => {
|
|
114
|
+
if (!transport.resumeHitl) throw new Error("HITL resume is not configured.");
|
|
115
|
+
const runId = input.runId || activeRunIdRef.current || activeRunId;
|
|
116
|
+
if (!runId) throw new Error("No paused run is available to resume.");
|
|
117
|
+
setIsResolvingHitl(true);
|
|
118
|
+
try {
|
|
119
|
+
const result = await transport.resumeHitl({ ...input, runId });
|
|
120
|
+
const threadId = result.thread_id || activeThreadIdRef.current;
|
|
121
|
+
if (threadId) await runStream({ mode: "attach", runId: result.run_id || runId, threadId });
|
|
122
|
+
return result;
|
|
123
|
+
} finally {
|
|
124
|
+
setIsResolvingHitl(false);
|
|
125
|
+
}
|
|
126
|
+
}, [activeRunId, activeRunIdRef, activeThreadIdRef, runStream, transport]);
|
|
127
|
+
return { pendingHitl, isResolvingHitl, resumeHitl };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/controller-thread-events.ts
|
|
131
|
+
import { useEffect, useRef } from "react";
|
|
132
|
+
|
|
92
133
|
// src/context-window.ts
|
|
93
134
|
var SOURCE_PRIORITY = {
|
|
94
135
|
unknown: 0,
|
|
@@ -373,13 +414,34 @@ var partsFromResponseBlocks = (blocks, fallbackText) => {
|
|
|
373
414
|
}
|
|
374
415
|
if (block.kind === "hitl_request") {
|
|
375
416
|
const hitl = asRecord(block.hitl) || block;
|
|
417
|
+
if (hitl.schema_version !== "agents24.hitl.interrupt.v2") {
|
|
418
|
+
throw new Error("Invalid V2 HITL response block.");
|
|
419
|
+
}
|
|
420
|
+
const interruptId = optionalString(block.interruptId) || optionalString(hitl.interrupt_id);
|
|
421
|
+
const hitlKind = optionalString(block.hitlKind) || optionalString(hitl.kind);
|
|
422
|
+
const status = optionalString(block.status) || optionalString(hitl.status) || "pending";
|
|
423
|
+
const allowedActions = Array.isArray(hitl.allowed_actions) ? hitl.allowed_actions.filter((value) => ["approve", "reject", "connect", "skip"].includes(String(value))) : [];
|
|
424
|
+
if (!interruptId || allowedActions.length === 0 || !["tool_review", "mcp_auth", "user_approval", "app_data_permission"].includes(String(hitlKind))) {
|
|
425
|
+
throw new Error("Invalid V2 HITL response block.");
|
|
426
|
+
}
|
|
427
|
+
const resolution = asRecord(block.resolution);
|
|
376
428
|
parts.push({
|
|
377
429
|
id,
|
|
378
430
|
type: "hitl",
|
|
379
431
|
kind: "hitl",
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
432
|
+
interruptId,
|
|
433
|
+
hitlKind,
|
|
434
|
+
message: optionalString(hitl.message) || optionalString(block.text) || "Input is required to continue.",
|
|
435
|
+
allowedActions,
|
|
436
|
+
status,
|
|
437
|
+
presentation: asRecord(hitl.presentation) || {},
|
|
438
|
+
resolution: resolution ? {
|
|
439
|
+
action: ["approve", "reject", "connect", "skip"].includes(String(resolution.action)) ? String(resolution.action) : null,
|
|
440
|
+
outcome: optionalString(resolution.outcome),
|
|
441
|
+
reason: optionalString(resolution.reason),
|
|
442
|
+
resolvedAt: optionalString(resolution.resolved_at),
|
|
443
|
+
resolver: asRecord(resolution.resolver)
|
|
444
|
+
} : null,
|
|
383
445
|
raw: block
|
|
384
446
|
});
|
|
385
447
|
return;
|
|
@@ -619,6 +681,47 @@ var applyThreadSummaryEvent = (currentThreads, event) => {
|
|
|
619
681
|
return sortByActivity(next);
|
|
620
682
|
};
|
|
621
683
|
|
|
684
|
+
// src/controller-thread-events.ts
|
|
685
|
+
function useControllerThreadEvents({
|
|
686
|
+
transport,
|
|
687
|
+
storage,
|
|
688
|
+
refresh,
|
|
689
|
+
setThreads
|
|
690
|
+
}) {
|
|
691
|
+
const cursorRef = useRef(null);
|
|
692
|
+
useEffect(() => {
|
|
693
|
+
if (!transport.subscribeThreadEvents) return;
|
|
694
|
+
let cancelled = false;
|
|
695
|
+
let retryTimeout = null;
|
|
696
|
+
let controller = null;
|
|
697
|
+
const connect = () => {
|
|
698
|
+
if (cancelled) return;
|
|
699
|
+
controller = new AbortController();
|
|
700
|
+
transport.subscribeThreadEvents?.(
|
|
701
|
+
{ cursor: cursorRef.current, signal: controller.signal },
|
|
702
|
+
async (event) => {
|
|
703
|
+
if (typeof event.cursor === "number") cursorRef.current = event.cursor;
|
|
704
|
+
if (event.event === "snapshot_required") {
|
|
705
|
+
await refresh().catch(() => void 0);
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
storage.setThreads(applyThreadSummaryEvent(storage.listThreads(), event));
|
|
709
|
+
setThreads(storage.listThreads());
|
|
710
|
+
}
|
|
711
|
+
).catch((error) => {
|
|
712
|
+
if (cancelled || isAbortError(error)) return;
|
|
713
|
+
retryTimeout = setTimeout(connect, 1500);
|
|
714
|
+
});
|
|
715
|
+
};
|
|
716
|
+
connect();
|
|
717
|
+
return () => {
|
|
718
|
+
cancelled = true;
|
|
719
|
+
if (retryTimeout) clearTimeout(retryTimeout);
|
|
720
|
+
controller?.abort();
|
|
721
|
+
};
|
|
722
|
+
}, [refresh, setThreads, storage, transport]);
|
|
723
|
+
}
|
|
724
|
+
|
|
622
725
|
// src/message-lifecycle.ts
|
|
623
726
|
function findStableAssistantMessageIndex(messages, input) {
|
|
624
727
|
return messages.findIndex(
|
|
@@ -657,83 +760,83 @@ function upsertStableAssistantMessage(messages, input) {
|
|
|
657
760
|
}
|
|
658
761
|
|
|
659
762
|
// src/controller.ts
|
|
660
|
-
function useAgents24ChatController({
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
763
|
+
function useAgents24ChatController(options) {
|
|
764
|
+
const {
|
|
765
|
+
transport,
|
|
766
|
+
storage,
|
|
767
|
+
activeThreadId: controlledActiveThreadId,
|
|
768
|
+
pageSize = DEFAULT_THREAD_PAGE_SIZE,
|
|
769
|
+
storageKey,
|
|
770
|
+
createId = createChatId,
|
|
771
|
+
onActiveThreadIdChange,
|
|
772
|
+
onSourceClick,
|
|
773
|
+
onStreamErrorMessage,
|
|
774
|
+
onRuntimeEvent,
|
|
775
|
+
onThreadDetailLoaded
|
|
776
|
+
} = options;
|
|
673
777
|
const activeThreadId = controlledActiveThreadId ?? storage.getActiveThreadId?.() ?? null;
|
|
674
778
|
const initialCached = activeThreadId ? storage.getThread(activeThreadId) : void 0;
|
|
675
|
-
const [messages, setMessages] =
|
|
676
|
-
const [isLoading, setIsLoading] =
|
|
677
|
-
const [isLoadingHistory, setIsLoadingHistory] =
|
|
678
|
-
const [isLoadingOlder, setIsLoadingOlder] =
|
|
679
|
-
const [hasOlderTurns, setHasOlderTurns] =
|
|
680
|
-
const [streamingContent, setStreamingContent] =
|
|
681
|
-
const [streamingMessageId, setStreamingMessageId] =
|
|
682
|
-
const [contextStatus, setContextStatus] =
|
|
683
|
-
const [currentReasoning, setCurrentReasoning] =
|
|
684
|
-
const [liked, setLiked] =
|
|
685
|
-
const [disliked, setDisliked] =
|
|
686
|
-
const [copiedMessageId, setCopiedMessageId] =
|
|
687
|
-
const [lastThinkingDurationMs, setLastThinkingDurationMs] =
|
|
688
|
-
const [threads, setThreads] =
|
|
689
|
-
const [isRefreshingThreads, setIsRefreshingThreads] =
|
|
690
|
-
const [isSelectingThread, setIsSelectingThread] =
|
|
691
|
-
const [activeRunId, setActiveRunId] =
|
|
692
|
-
const textareaRef =
|
|
693
|
-
const activeThreadIdRef =
|
|
694
|
-
const messagesRef =
|
|
695
|
-
const nextBeforeTurnIndexRef =
|
|
696
|
-
const hasOlderTurnsRef =
|
|
697
|
-
const isLoadingOlderRef =
|
|
698
|
-
const loadedThreadIdRef =
|
|
699
|
-
const requestSeqRef =
|
|
700
|
-
const isLoadingHistoryRef =
|
|
701
|
-
const activeRunIdRef =
|
|
702
|
-
const reattachedRunIdRef =
|
|
703
|
-
const abortControllerRef =
|
|
704
|
-
const [lifecycleAbortController] =
|
|
705
|
-
const streamingContentRef =
|
|
706
|
-
const streamingMessageIdRef =
|
|
707
|
-
const reasoningRef =
|
|
708
|
-
const liveVoiceIdsRef =
|
|
709
|
-
const refreshSeqRef =
|
|
710
|
-
const
|
|
711
|
-
const setActiveRunIdValue = useCallback2((runId) => {
|
|
779
|
+
const [messages, setMessages] = useState2(() => initialCached?.messages || []);
|
|
780
|
+
const [isLoading, setIsLoading] = useState2(false);
|
|
781
|
+
const [isLoadingHistory, setIsLoadingHistory] = useState2(() => Boolean(activeThreadId) && !initialCached?.messages?.length);
|
|
782
|
+
const [isLoadingOlder, setIsLoadingOlder] = useState2(false);
|
|
783
|
+
const [hasOlderTurns, setHasOlderTurns] = useState2(Boolean(initialCached?.hasOlderTurns));
|
|
784
|
+
const [streamingContent, setStreamingContent] = useState2("");
|
|
785
|
+
const [streamingMessageId, setStreamingMessageId] = useState2(null);
|
|
786
|
+
const [contextStatus, setContextStatus] = useState2(null);
|
|
787
|
+
const [currentReasoning, setCurrentReasoning] = useState2([]);
|
|
788
|
+
const [liked, setLiked] = useState2({});
|
|
789
|
+
const [disliked, setDisliked] = useState2({});
|
|
790
|
+
const [copiedMessageId, setCopiedMessageId] = useState2(null);
|
|
791
|
+
const [lastThinkingDurationMs, setLastThinkingDurationMs] = useState2(null);
|
|
792
|
+
const [threads, setThreads] = useState2(() => storage.listThreads());
|
|
793
|
+
const [isRefreshingThreads, setIsRefreshingThreads] = useState2(false);
|
|
794
|
+
const [isSelectingThread, setIsSelectingThread] = useState2(false);
|
|
795
|
+
const [activeRunId, setActiveRunId] = useState2(null);
|
|
796
|
+
const textareaRef = useRef2(null);
|
|
797
|
+
const activeThreadIdRef = useRef2(activeThreadId);
|
|
798
|
+
const messagesRef = useRef2(messages);
|
|
799
|
+
const nextBeforeTurnIndexRef = useRef2(initialCached?.nextBeforeTurnIndex ?? null);
|
|
800
|
+
const hasOlderTurnsRef = useRef2(Boolean(initialCached?.hasOlderTurns));
|
|
801
|
+
const isLoadingOlderRef = useRef2(false);
|
|
802
|
+
const loadedThreadIdRef = useRef2(initialCached?.messages?.length ? activeThreadId : null);
|
|
803
|
+
const requestSeqRef = useRef2(0);
|
|
804
|
+
const isLoadingHistoryRef = useRef2(isLoadingHistory);
|
|
805
|
+
const activeRunIdRef = useRef2(null);
|
|
806
|
+
const reattachedRunIdRef = useRef2(null);
|
|
807
|
+
const abortControllerRef = useRef2(null);
|
|
808
|
+
const [lifecycleAbortController] = useState2(() => new AbortController());
|
|
809
|
+
const streamingContentRef = useRef2("");
|
|
810
|
+
const streamingMessageIdRef = useRef2(null);
|
|
811
|
+
const reasoningRef = useRef2([]);
|
|
812
|
+
const liveVoiceIdsRef = useRef2({});
|
|
813
|
+
const refreshSeqRef = useRef2(0);
|
|
814
|
+
const setActiveRunIdValue = useCallback3((runId) => {
|
|
712
815
|
activeRunIdRef.current = runId;
|
|
713
816
|
setActiveRunId(runId);
|
|
714
817
|
}, []);
|
|
715
|
-
const syncThreadsFromStorage =
|
|
818
|
+
const syncThreadsFromStorage = useCallback3(() => {
|
|
716
819
|
setThreads(storage.listThreads());
|
|
717
820
|
}, [storage]);
|
|
718
|
-
const upsertStoredThread =
|
|
821
|
+
const upsertStoredThread = useCallback3(
|
|
719
822
|
(thread) => {
|
|
720
823
|
storage.upsertThread(thread);
|
|
721
824
|
syncThreadsFromStorage();
|
|
722
825
|
},
|
|
723
826
|
[storage, syncThreadsFromStorage]
|
|
724
827
|
);
|
|
725
|
-
|
|
828
|
+
useEffect2(() => {
|
|
726
829
|
messagesRef.current = messages;
|
|
727
830
|
}, [messages]);
|
|
728
|
-
const persistThread =
|
|
729
|
-
(threadId, nextMessages, paging,
|
|
831
|
+
const persistThread = useCallback3(
|
|
832
|
+
(threadId, nextMessages, paging, options2) => {
|
|
730
833
|
const existing = storage.getThread(threadId);
|
|
731
834
|
const firstUser = nextMessages.find((message) => message.role === "user");
|
|
732
835
|
upsertStoredThread({
|
|
733
836
|
...existing || {},
|
|
734
837
|
id: threadId,
|
|
735
838
|
title: existing?.title || (firstUser ? titleFromMessage(firstUser.content, firstUser.attachments || []) : "New chat"),
|
|
736
|
-
updated_at:
|
|
839
|
+
updated_at: options2?.updatedAt || (options2?.touch ? (/* @__PURE__ */ new Date()).toISOString() : existing?.updated_at) || (/* @__PURE__ */ new Date()).toISOString(),
|
|
737
840
|
messages: nextMessages,
|
|
738
841
|
isHydrated: true,
|
|
739
842
|
hasOlderTurns: paging?.hasOlderTurns ?? hasOlderTurnsRef.current,
|
|
@@ -742,7 +845,7 @@ function useAgents24ChatController({
|
|
|
742
845
|
},
|
|
743
846
|
[storage, upsertStoredThread]
|
|
744
847
|
);
|
|
745
|
-
const markThreadRunStatus =
|
|
848
|
+
const markThreadRunStatus = useCallback3(
|
|
746
849
|
(threadId, runId, status, lastEventSeq) => {
|
|
747
850
|
const existing = storage.getThread(threadId);
|
|
748
851
|
if (!existing || !runId) return;
|
|
@@ -772,7 +875,7 @@ function useAgents24ChatController({
|
|
|
772
875
|
},
|
|
773
876
|
[storage, upsertStoredThread]
|
|
774
877
|
);
|
|
775
|
-
const refresh =
|
|
878
|
+
const refresh = useCallback3(async () => {
|
|
776
879
|
const seq = ++refreshSeqRef.current;
|
|
777
880
|
setIsRefreshingThreads(true);
|
|
778
881
|
try {
|
|
@@ -786,7 +889,7 @@ function useAgents24ChatController({
|
|
|
786
889
|
if (!lifecycleAbortController.signal.aborted && seq === refreshSeqRef.current) setIsRefreshingThreads(false);
|
|
787
890
|
}
|
|
788
891
|
}, [lifecycleAbortController, storage, transport]);
|
|
789
|
-
const applyThreadId =
|
|
892
|
+
const applyThreadId = useCallback3(
|
|
790
893
|
(threadId, baseMessages) => {
|
|
791
894
|
if (!threadId || activeThreadIdRef.current === threadId) return;
|
|
792
895
|
activeThreadIdRef.current = threadId;
|
|
@@ -800,15 +903,15 @@ function useAgents24ChatController({
|
|
|
800
903
|
streamingContentRef.current = value;
|
|
801
904
|
setStreamingContent(value);
|
|
802
905
|
};
|
|
803
|
-
const setLoadingHistory =
|
|
906
|
+
const setLoadingHistory = useCallback3((value) => {
|
|
804
907
|
isLoadingHistoryRef.current = value;
|
|
805
908
|
setIsLoadingHistory(value);
|
|
806
909
|
}, []);
|
|
807
|
-
const setReasoningSteps =
|
|
910
|
+
const setReasoningSteps = useCallback3((value) => {
|
|
808
911
|
reasoningRef.current = value || [];
|
|
809
912
|
setCurrentReasoning(value || []);
|
|
810
913
|
}, []);
|
|
811
|
-
const detachActiveStream =
|
|
914
|
+
const detachActiveStream = useCallback3(() => {
|
|
812
915
|
const controller = abortControllerRef.current;
|
|
813
916
|
abortControllerRef.current = null;
|
|
814
917
|
abortDetachedStream(controller);
|
|
@@ -822,7 +925,7 @@ function useAgents24ChatController({
|
|
|
822
925
|
setStreamingContent("");
|
|
823
926
|
setCurrentReasoning([]);
|
|
824
927
|
}, [setActiveRunIdValue]);
|
|
825
|
-
const clearMissingThread =
|
|
928
|
+
const clearMissingThread = useCallback3(
|
|
826
929
|
(threadId) => {
|
|
827
930
|
storage.deleteThread?.(threadId);
|
|
828
931
|
syncThreadsFromStorage();
|
|
@@ -844,7 +947,7 @@ function useAgents24ChatController({
|
|
|
844
947
|
},
|
|
845
948
|
[detachActiveStream, onActiveThreadIdChange, setLoadingHistory, storage, syncThreadsFromStorage]
|
|
846
949
|
);
|
|
847
|
-
const setLiveAssistantMessage =
|
|
950
|
+
const setLiveAssistantMessage = useCallback3(
|
|
848
951
|
(input) => {
|
|
849
952
|
setMessages((prev) => {
|
|
850
953
|
const next = upsertStableAssistantMessage(prev, {
|
|
@@ -876,7 +979,7 @@ function useAgents24ChatController({
|
|
|
876
979
|
},
|
|
877
980
|
[]
|
|
878
981
|
);
|
|
879
|
-
const finalizeAssistantMessage =
|
|
982
|
+
const finalizeAssistantMessage = useCallback3(
|
|
880
983
|
(input) => {
|
|
881
984
|
const content = input.error || input.assistantText.trim();
|
|
882
985
|
if (!content) return input.baseMessages;
|
|
@@ -930,7 +1033,7 @@ function useAgents24ChatController({
|
|
|
930
1033
|
},
|
|
931
1034
|
[createId, persistThread, storage, upsertStoredThread]
|
|
932
1035
|
);
|
|
933
|
-
const loadThread =
|
|
1036
|
+
const loadThread = useCallback3(
|
|
934
1037
|
async (threadId) => {
|
|
935
1038
|
const seq = ++requestSeqRef.current;
|
|
936
1039
|
setLoadingHistory(true);
|
|
@@ -975,7 +1078,7 @@ function useAgents24ChatController({
|
|
|
975
1078
|
},
|
|
976
1079
|
[clearMissingThread, lifecycleAbortController, onThreadDetailLoaded, pageSize, setLoadingHistory, transport, upsertStoredThread]
|
|
977
1080
|
);
|
|
978
|
-
const loadOlderTurns =
|
|
1081
|
+
const loadOlderTurns = useCallback3(async () => {
|
|
979
1082
|
const threadId = activeThreadIdRef.current;
|
|
980
1083
|
const beforeTurnIndex = nextBeforeTurnIndexRef.current;
|
|
981
1084
|
if (!threadId || beforeTurnIndex === null || isLoadingOlderRef.current) return;
|
|
@@ -1011,7 +1114,7 @@ function useAgents24ChatController({
|
|
|
1011
1114
|
isLoadingOlderRef.current = false;
|
|
1012
1115
|
}
|
|
1013
1116
|
}, [clearMissingThread, lifecycleAbortController, pageSize, persistThread, transport]);
|
|
1014
|
-
const handleStreamEvent =
|
|
1117
|
+
const handleStreamEvent = useCallback3(
|
|
1015
1118
|
(input) => {
|
|
1016
1119
|
const { mode, event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages } = input;
|
|
1017
1120
|
const payload = event.payload || {};
|
|
@@ -1072,7 +1175,7 @@ function useAgents24ChatController({
|
|
|
1072
1175
|
},
|
|
1073
1176
|
[applyThreadId, finalizeAssistantMessage, markThreadRunStatus, onRuntimeEvent, onStreamErrorMessage, setActiveRunIdValue, setLiveAssistantMessage, setReasoningSteps]
|
|
1074
1177
|
);
|
|
1075
|
-
const runStream =
|
|
1178
|
+
const runStream = useCallback3(
|
|
1076
1179
|
async (input) => {
|
|
1077
1180
|
const startedAt = Date.now();
|
|
1078
1181
|
const controller = new AbortController();
|
|
@@ -1195,18 +1298,26 @@ function useAgents24ChatController({
|
|
|
1195
1298
|
},
|
|
1196
1299
|
[createId, finalizeAssistantMessage, handleStreamEvent, onStreamErrorMessage, persistThread, refresh, setActiveRunIdValue, setReasoningSteps, transport]
|
|
1197
1300
|
);
|
|
1198
|
-
const handleSubmit =
|
|
1301
|
+
const handleSubmit = useCallback3(
|
|
1199
1302
|
async (message) => {
|
|
1200
1303
|
if (!message.text.trim() && !(message.files || []).length) return;
|
|
1201
1304
|
await runStream({ mode: "submit", message });
|
|
1202
1305
|
},
|
|
1203
1306
|
[runStream]
|
|
1204
1307
|
);
|
|
1205
|
-
const attachRun =
|
|
1308
|
+
const attachRun = useCallback3(async (runId, threadId) => {
|
|
1206
1309
|
const resolvedThreadId = threadId ?? activeThreadIdRef.current;
|
|
1207
1310
|
if (runId && resolvedThreadId) await runStream({ mode: "attach", runId, threadId: resolvedThreadId });
|
|
1208
1311
|
}, [runStream]);
|
|
1209
|
-
const
|
|
1312
|
+
const { pendingHitl, isResolvingHitl, resumeHitl } = useControllerHitl({
|
|
1313
|
+
messages,
|
|
1314
|
+
transport,
|
|
1315
|
+
activeRunId,
|
|
1316
|
+
activeRunIdRef,
|
|
1317
|
+
activeThreadIdRef,
|
|
1318
|
+
runStream
|
|
1319
|
+
});
|
|
1320
|
+
const handleStop = useCallback3(() => {
|
|
1210
1321
|
const runId = activeRunIdRef.current;
|
|
1211
1322
|
const partial = streamingContentRef.current;
|
|
1212
1323
|
const liveMessageId = streamingMessageIdRef.current;
|
|
@@ -1230,56 +1341,25 @@ function useAgents24ChatController({
|
|
|
1230
1341
|
});
|
|
1231
1342
|
}
|
|
1232
1343
|
}, [finalizeAssistantMessage, lastThinkingDurationMs, setActiveRunIdValue, setReasoningSteps, transport]);
|
|
1233
|
-
|
|
1344
|
+
useEffect2(() => {
|
|
1234
1345
|
refresh().catch((error) => {
|
|
1235
1346
|
if (!isAbortError(error) && !lifecycleAbortController.signal.aborted) {
|
|
1236
1347
|
setThreads(storage.listThreads());
|
|
1237
1348
|
}
|
|
1238
1349
|
});
|
|
1239
1350
|
}, [lifecycleAbortController, refresh, storage]);
|
|
1240
|
-
|
|
1351
|
+
useEffect2(() => {
|
|
1241
1352
|
return () => {
|
|
1242
1353
|
lifecycleAbortController.abort();
|
|
1243
1354
|
abortDetachedStream(abortControllerRef.current);
|
|
1244
1355
|
abortControllerRef.current = null;
|
|
1245
1356
|
};
|
|
1246
1357
|
}, [lifecycleAbortController]);
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
let cancelled = false;
|
|
1250
|
-
let retryTimeout = null;
|
|
1251
|
-
let controller = null;
|
|
1252
|
-
const connect = () => {
|
|
1253
|
-
if (cancelled) return;
|
|
1254
|
-
controller = new AbortController();
|
|
1255
|
-
transport.subscribeThreadEvents?.(
|
|
1256
|
-
{ cursor: threadEventsCursorRef.current, signal: controller.signal },
|
|
1257
|
-
async (event) => {
|
|
1258
|
-
if (typeof event.cursor === "number") threadEventsCursorRef.current = event.cursor;
|
|
1259
|
-
if (event.event === "snapshot_required") {
|
|
1260
|
-
await refresh().catch(() => void 0);
|
|
1261
|
-
return;
|
|
1262
|
-
}
|
|
1263
|
-
const next = applyThreadSummaryEvent(storage.listThreads(), event);
|
|
1264
|
-
storage.setThreads(next);
|
|
1265
|
-
setThreads(storage.listThreads());
|
|
1266
|
-
}
|
|
1267
|
-
).catch((error) => {
|
|
1268
|
-
if (cancelled || isAbortError(error)) return;
|
|
1269
|
-
retryTimeout = setTimeout(connect, 1500);
|
|
1270
|
-
});
|
|
1271
|
-
};
|
|
1272
|
-
connect();
|
|
1273
|
-
return () => {
|
|
1274
|
-
cancelled = true;
|
|
1275
|
-
if (retryTimeout) clearTimeout(retryTimeout);
|
|
1276
|
-
controller?.abort();
|
|
1277
|
-
};
|
|
1278
|
-
}, [refresh, storage, transport]);
|
|
1279
|
-
useEffect(() => {
|
|
1358
|
+
useControllerThreadEvents({ transport, storage, refresh, setThreads });
|
|
1359
|
+
useEffect2(() => {
|
|
1280
1360
|
syncThreadsFromStorage();
|
|
1281
1361
|
}, [storageKey, syncThreadsFromStorage]);
|
|
1282
|
-
|
|
1362
|
+
useEffect2(() => {
|
|
1283
1363
|
const previous = activeThreadIdRef.current;
|
|
1284
1364
|
activeThreadIdRef.current = activeThreadId;
|
|
1285
1365
|
if (activeThreadId && previous === activeThreadId && (activeRunIdRef.current || streamingMessageIdRef.current)) {
|
|
@@ -1323,14 +1403,14 @@ function useAgents24ChatController({
|
|
|
1323
1403
|
}
|
|
1324
1404
|
void loadThread(activeThreadId).catch(() => setLoadingHistory(false));
|
|
1325
1405
|
}, [activeThreadId, detachActiveStream, loadThread, setLoadingHistory, storage]);
|
|
1326
|
-
|
|
1406
|
+
useEffect2(() => {
|
|
1327
1407
|
const threadId = activeThreadId;
|
|
1328
1408
|
if (!threadId || isLoadingHistory || loadedThreadIdRef.current !== threadId || activeRunIdRef.current) return;
|
|
1329
1409
|
const runId = autoAttachRunIdFromThread(storage.getThread(threadId));
|
|
1330
1410
|
if (!runId || reattachedRunIdRef.current === runId) return;
|
|
1331
1411
|
void runStream({ mode: "attach", threadId, runId });
|
|
1332
1412
|
}, [activeThreadId, isLoadingHistory, runStream, storage, storageKey]);
|
|
1333
|
-
|
|
1413
|
+
useEffect2(() => {
|
|
1334
1414
|
const threadId = activeThreadId;
|
|
1335
1415
|
if (!threadId || isLoadingHistoryRef.current || activeRunIdRef.current || streamingMessageIdRef.current) {
|
|
1336
1416
|
return;
|
|
@@ -1371,7 +1451,7 @@ function useAgents24ChatController({
|
|
|
1371
1451
|
setMessages,
|
|
1372
1452
|
storage
|
|
1373
1453
|
});
|
|
1374
|
-
const loadThreadById =
|
|
1454
|
+
const loadThreadById = useCallback3(
|
|
1375
1455
|
async (threadId) => {
|
|
1376
1456
|
if (!threadId) return;
|
|
1377
1457
|
setIsSelectingThread(true);
|
|
@@ -1390,7 +1470,7 @@ function useAgents24ChatController({
|
|
|
1390
1470
|
[detachActiveStream, loadThread, onActiveThreadIdChange, storage]
|
|
1391
1471
|
);
|
|
1392
1472
|
const activeThread = activeThreadId ? storage.getThread(activeThreadId) || threads.find((thread) => thread.id === activeThreadId) || null : null;
|
|
1393
|
-
return
|
|
1473
|
+
return useMemo2(() => ({
|
|
1394
1474
|
threads,
|
|
1395
1475
|
activeThreadId,
|
|
1396
1476
|
activeThread,
|
|
@@ -1410,8 +1490,11 @@ function useAgents24ChatController({
|
|
|
1410
1490
|
copiedMessageId,
|
|
1411
1491
|
lastThinkingDurationMs,
|
|
1412
1492
|
activeRunId,
|
|
1493
|
+
pendingHitl,
|
|
1494
|
+
isResolvingHitl,
|
|
1413
1495
|
handleSubmit,
|
|
1414
1496
|
attachRun,
|
|
1497
|
+
resumeHitl,
|
|
1415
1498
|
handleStop,
|
|
1416
1499
|
handleCopy,
|
|
1417
1500
|
handleLike,
|
|
@@ -1450,8 +1533,11 @@ function useAgents24ChatController({
|
|
|
1450
1533
|
loadThreadById,
|
|
1451
1534
|
loadOlderTurns,
|
|
1452
1535
|
messages,
|
|
1536
|
+
isResolvingHitl,
|
|
1453
1537
|
onSourceClick,
|
|
1538
|
+
pendingHitl,
|
|
1454
1539
|
refresh,
|
|
1540
|
+
resumeHitl,
|
|
1455
1541
|
streamingContent,
|
|
1456
1542
|
streamingMessageId,
|
|
1457
1543
|
startNewThread,
|
|
@@ -1680,9 +1766,10 @@ var createFetchChatTransport = ({
|
|
|
1680
1766
|
method: "POST",
|
|
1681
1767
|
headers: jsonHeaders(await loadHeaders()),
|
|
1682
1768
|
body: JSON.stringify({
|
|
1683
|
-
schema_version: "agents24.hitl.resume.
|
|
1769
|
+
schema_version: "agents24.hitl.resume.v2",
|
|
1684
1770
|
interrupt_id: input.interruptId,
|
|
1685
|
-
|
|
1771
|
+
action: input.action,
|
|
1772
|
+
comment: input.comment,
|
|
1686
1773
|
client: input.client
|
|
1687
1774
|
})
|
|
1688
1775
|
});
|