@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.d.ts
CHANGED
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,
|
|
@@ -111,16 +152,16 @@ var STAGE_PRIORITY = {
|
|
|
111
152
|
function numberOrNull(value) {
|
|
112
153
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
113
154
|
}
|
|
114
|
-
function stagePriority(
|
|
115
|
-
return STAGE_PRIORITY[
|
|
155
|
+
function stagePriority(window2) {
|
|
156
|
+
return STAGE_PRIORITY[window2?.stage || "sent_prompt"] ?? STAGE_PRIORITY.sent_prompt;
|
|
116
157
|
}
|
|
117
|
-
function hasRenderableContextWindow(
|
|
118
|
-
const maxTokens =
|
|
158
|
+
function hasRenderableContextWindow(window2) {
|
|
159
|
+
const maxTokens = window2?.max_tokens;
|
|
119
160
|
return typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0;
|
|
120
161
|
}
|
|
121
|
-
function windowWeight(
|
|
122
|
-
if (!
|
|
123
|
-
return [stagePriority(
|
|
162
|
+
function windowWeight(window2) {
|
|
163
|
+
if (!window2) return [0, 0];
|
|
164
|
+
return [stagePriority(window2), SOURCE_PRIORITY[window2.source] || 0];
|
|
124
165
|
}
|
|
125
166
|
function normalizeContextCompression(value) {
|
|
126
167
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
@@ -273,6 +314,22 @@ var assistantTextFromEvents = (events) => {
|
|
|
273
314
|
};
|
|
274
315
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
275
316
|
var optionalString = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
317
|
+
var HITL_ACTIONS_BY_KIND = {
|
|
318
|
+
tool_review: ["approve", "reject"],
|
|
319
|
+
mcp_auth: ["connect", "skip"],
|
|
320
|
+
user_approval: ["approve", "reject"],
|
|
321
|
+
app_data_permission: ["approve", "reject"]
|
|
322
|
+
};
|
|
323
|
+
var HITL_STATUSES = ["pending", "resolved", "expired", "cancelled", "invalidated"];
|
|
324
|
+
function exactHitlActions(kind, value) {
|
|
325
|
+
if (!Array.isArray(value)) throw new Error("Invalid V2 HITL response block.");
|
|
326
|
+
const actions = value.map((item) => String(item));
|
|
327
|
+
const expected = HITL_ACTIONS_BY_KIND[kind];
|
|
328
|
+
if (actions.length !== expected.length || actions.some((action, index) => action !== expected[index])) {
|
|
329
|
+
throw new Error("Invalid V2 HITL response block.");
|
|
330
|
+
}
|
|
331
|
+
return actions;
|
|
332
|
+
}
|
|
276
333
|
var toolStateFromStatus = (status) => {
|
|
277
334
|
const normalized = String(status || "").trim().toLowerCase();
|
|
278
335
|
if (["failed", "error"].includes(normalized)) return "output-error";
|
|
@@ -373,13 +430,39 @@ var partsFromResponseBlocks = (blocks, fallbackText) => {
|
|
|
373
430
|
}
|
|
374
431
|
if (block.kind === "hitl_request") {
|
|
375
432
|
const hitl = asRecord(block.hitl) || block;
|
|
433
|
+
if (hitl.schema_version !== "agents24.hitl.interrupt.v2") {
|
|
434
|
+
throw new Error("Invalid V2 HITL response block.");
|
|
435
|
+
}
|
|
436
|
+
const interruptId = optionalString(block.interruptId) || optionalString(hitl.interrupt_id);
|
|
437
|
+
const hitlKind = optionalString(block.hitlKind) || optionalString(hitl.kind);
|
|
438
|
+
const status = optionalString(block.status) || optionalString(hitl.status) || "pending";
|
|
439
|
+
if (!interruptId || !["tool_review", "mcp_auth", "user_approval", "app_data_permission"].includes(String(hitlKind)) || !HITL_STATUSES.includes(status)) {
|
|
440
|
+
throw new Error("Invalid V2 HITL response block.");
|
|
441
|
+
}
|
|
442
|
+
const kind = hitlKind;
|
|
443
|
+
const allowedActions = exactHitlActions(kind, hitl.allowed_actions);
|
|
444
|
+
const resolution = asRecord(block.resolution);
|
|
445
|
+
const resolver = asRecord(resolution?.resolver);
|
|
376
446
|
parts.push({
|
|
377
447
|
id,
|
|
378
448
|
type: "hitl",
|
|
379
449
|
kind: "hitl",
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
450
|
+
interruptId,
|
|
451
|
+
hitlKind: kind,
|
|
452
|
+
message: optionalString(hitl.message) || optionalString(block.text) || "Input is required to continue.",
|
|
453
|
+
allowedActions,
|
|
454
|
+
status,
|
|
455
|
+
presentation: asRecord(hitl.presentation) || {},
|
|
456
|
+
resolution: resolution ? {
|
|
457
|
+
action: ["approve", "reject", "connect", "skip"].includes(String(resolution.action)) ? String(resolution.action) : null,
|
|
458
|
+
outcome: optionalString(resolution.outcome),
|
|
459
|
+
reason: optionalString(resolution.reason),
|
|
460
|
+
resolvedAt: optionalString(resolution.resolved_at),
|
|
461
|
+
resolver: resolver ? {
|
|
462
|
+
principalType: optionalString(resolver.principal_type) || optionalString(resolver.principalType) || null,
|
|
463
|
+
principalId: optionalString(resolver.principal_id) || optionalString(resolver.principalId) || null
|
|
464
|
+
} : null
|
|
465
|
+
} : null,
|
|
383
466
|
raw: block
|
|
384
467
|
});
|
|
385
468
|
return;
|
|
@@ -619,6 +702,47 @@ var applyThreadSummaryEvent = (currentThreads, event) => {
|
|
|
619
702
|
return sortByActivity(next);
|
|
620
703
|
};
|
|
621
704
|
|
|
705
|
+
// src/controller-thread-events.ts
|
|
706
|
+
function useControllerThreadEvents({
|
|
707
|
+
transport,
|
|
708
|
+
storage,
|
|
709
|
+
refresh,
|
|
710
|
+
setThreads
|
|
711
|
+
}) {
|
|
712
|
+
const cursorRef = useRef(null);
|
|
713
|
+
useEffect(() => {
|
|
714
|
+
if (!transport.subscribeThreadEvents) return;
|
|
715
|
+
let cancelled = false;
|
|
716
|
+
let retryTimeout = null;
|
|
717
|
+
let controller = null;
|
|
718
|
+
const connect = () => {
|
|
719
|
+
if (cancelled) return;
|
|
720
|
+
controller = new AbortController();
|
|
721
|
+
transport.subscribeThreadEvents?.(
|
|
722
|
+
{ cursor: cursorRef.current, signal: controller.signal },
|
|
723
|
+
async (event) => {
|
|
724
|
+
if (typeof event.cursor === "number") cursorRef.current = event.cursor;
|
|
725
|
+
if (event.event === "snapshot_required") {
|
|
726
|
+
await refresh().catch(() => void 0);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
storage.setThreads(applyThreadSummaryEvent(storage.listThreads(), event));
|
|
730
|
+
setThreads(storage.listThreads());
|
|
731
|
+
}
|
|
732
|
+
).catch((error) => {
|
|
733
|
+
if (cancelled || isAbortError(error)) return;
|
|
734
|
+
retryTimeout = setTimeout(connect, 1500);
|
|
735
|
+
});
|
|
736
|
+
};
|
|
737
|
+
connect();
|
|
738
|
+
return () => {
|
|
739
|
+
cancelled = true;
|
|
740
|
+
if (retryTimeout) clearTimeout(retryTimeout);
|
|
741
|
+
controller?.abort();
|
|
742
|
+
};
|
|
743
|
+
}, [refresh, setThreads, storage, transport]);
|
|
744
|
+
}
|
|
745
|
+
|
|
622
746
|
// src/message-lifecycle.ts
|
|
623
747
|
function findStableAssistantMessageIndex(messages, input) {
|
|
624
748
|
return messages.findIndex(
|
|
@@ -657,83 +781,83 @@ function upsertStableAssistantMessage(messages, input) {
|
|
|
657
781
|
}
|
|
658
782
|
|
|
659
783
|
// src/controller.ts
|
|
660
|
-
function useAgents24ChatController({
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
784
|
+
function useAgents24ChatController(options) {
|
|
785
|
+
const {
|
|
786
|
+
transport,
|
|
787
|
+
storage,
|
|
788
|
+
activeThreadId: controlledActiveThreadId,
|
|
789
|
+
pageSize = DEFAULT_THREAD_PAGE_SIZE,
|
|
790
|
+
storageKey,
|
|
791
|
+
createId = createChatId,
|
|
792
|
+
onActiveThreadIdChange,
|
|
793
|
+
onSourceClick,
|
|
794
|
+
onStreamErrorMessage,
|
|
795
|
+
onRuntimeEvent,
|
|
796
|
+
onThreadDetailLoaded
|
|
797
|
+
} = options;
|
|
673
798
|
const activeThreadId = controlledActiveThreadId ?? storage.getActiveThreadId?.() ?? null;
|
|
674
799
|
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) => {
|
|
800
|
+
const [messages, setMessages] = useState2(() => initialCached?.messages || []);
|
|
801
|
+
const [isLoading, setIsLoading] = useState2(false);
|
|
802
|
+
const [isLoadingHistory, setIsLoadingHistory] = useState2(() => Boolean(activeThreadId) && !initialCached?.messages?.length);
|
|
803
|
+
const [isLoadingOlder, setIsLoadingOlder] = useState2(false);
|
|
804
|
+
const [hasOlderTurns, setHasOlderTurns] = useState2(Boolean(initialCached?.hasOlderTurns));
|
|
805
|
+
const [streamingContent, setStreamingContent] = useState2("");
|
|
806
|
+
const [streamingMessageId, setStreamingMessageId] = useState2(null);
|
|
807
|
+
const [contextStatus, setContextStatus] = useState2(null);
|
|
808
|
+
const [currentReasoning, setCurrentReasoning] = useState2([]);
|
|
809
|
+
const [liked, setLiked] = useState2({});
|
|
810
|
+
const [disliked, setDisliked] = useState2({});
|
|
811
|
+
const [copiedMessageId, setCopiedMessageId] = useState2(null);
|
|
812
|
+
const [lastThinkingDurationMs, setLastThinkingDurationMs] = useState2(null);
|
|
813
|
+
const [threads, setThreads] = useState2(() => storage.listThreads());
|
|
814
|
+
const [isRefreshingThreads, setIsRefreshingThreads] = useState2(false);
|
|
815
|
+
const [isSelectingThread, setIsSelectingThread] = useState2(false);
|
|
816
|
+
const [activeRunId, setActiveRunId] = useState2(null);
|
|
817
|
+
const textareaRef = useRef2(null);
|
|
818
|
+
const activeThreadIdRef = useRef2(activeThreadId);
|
|
819
|
+
const messagesRef = useRef2(messages);
|
|
820
|
+
const nextBeforeTurnIndexRef = useRef2(initialCached?.nextBeforeTurnIndex ?? null);
|
|
821
|
+
const hasOlderTurnsRef = useRef2(Boolean(initialCached?.hasOlderTurns));
|
|
822
|
+
const isLoadingOlderRef = useRef2(false);
|
|
823
|
+
const loadedThreadIdRef = useRef2(initialCached?.messages?.length ? activeThreadId : null);
|
|
824
|
+
const requestSeqRef = useRef2(0);
|
|
825
|
+
const isLoadingHistoryRef = useRef2(isLoadingHistory);
|
|
826
|
+
const activeRunIdRef = useRef2(null);
|
|
827
|
+
const reattachedRunIdRef = useRef2(null);
|
|
828
|
+
const abortControllerRef = useRef2(null);
|
|
829
|
+
const [lifecycleAbortController] = useState2(() => new AbortController());
|
|
830
|
+
const streamingContentRef = useRef2("");
|
|
831
|
+
const streamingMessageIdRef = useRef2(null);
|
|
832
|
+
const reasoningRef = useRef2([]);
|
|
833
|
+
const liveVoiceIdsRef = useRef2({});
|
|
834
|
+
const refreshSeqRef = useRef2(0);
|
|
835
|
+
const setActiveRunIdValue = useCallback3((runId) => {
|
|
712
836
|
activeRunIdRef.current = runId;
|
|
713
837
|
setActiveRunId(runId);
|
|
714
838
|
}, []);
|
|
715
|
-
const syncThreadsFromStorage =
|
|
839
|
+
const syncThreadsFromStorage = useCallback3(() => {
|
|
716
840
|
setThreads(storage.listThreads());
|
|
717
841
|
}, [storage]);
|
|
718
|
-
const upsertStoredThread =
|
|
842
|
+
const upsertStoredThread = useCallback3(
|
|
719
843
|
(thread) => {
|
|
720
844
|
storage.upsertThread(thread);
|
|
721
845
|
syncThreadsFromStorage();
|
|
722
846
|
},
|
|
723
847
|
[storage, syncThreadsFromStorage]
|
|
724
848
|
);
|
|
725
|
-
|
|
849
|
+
useEffect2(() => {
|
|
726
850
|
messagesRef.current = messages;
|
|
727
851
|
}, [messages]);
|
|
728
|
-
const persistThread =
|
|
729
|
-
(threadId, nextMessages, paging,
|
|
852
|
+
const persistThread = useCallback3(
|
|
853
|
+
(threadId, nextMessages, paging, options2) => {
|
|
730
854
|
const existing = storage.getThread(threadId);
|
|
731
855
|
const firstUser = nextMessages.find((message) => message.role === "user");
|
|
732
856
|
upsertStoredThread({
|
|
733
857
|
...existing || {},
|
|
734
858
|
id: threadId,
|
|
735
859
|
title: existing?.title || (firstUser ? titleFromMessage(firstUser.content, firstUser.attachments || []) : "New chat"),
|
|
736
|
-
updated_at:
|
|
860
|
+
updated_at: options2?.updatedAt || (options2?.touch ? (/* @__PURE__ */ new Date()).toISOString() : existing?.updated_at) || (/* @__PURE__ */ new Date()).toISOString(),
|
|
737
861
|
messages: nextMessages,
|
|
738
862
|
isHydrated: true,
|
|
739
863
|
hasOlderTurns: paging?.hasOlderTurns ?? hasOlderTurnsRef.current,
|
|
@@ -742,7 +866,7 @@ function useAgents24ChatController({
|
|
|
742
866
|
},
|
|
743
867
|
[storage, upsertStoredThread]
|
|
744
868
|
);
|
|
745
|
-
const markThreadRunStatus =
|
|
869
|
+
const markThreadRunStatus = useCallback3(
|
|
746
870
|
(threadId, runId, status, lastEventSeq) => {
|
|
747
871
|
const existing = storage.getThread(threadId);
|
|
748
872
|
if (!existing || !runId) return;
|
|
@@ -772,7 +896,7 @@ function useAgents24ChatController({
|
|
|
772
896
|
},
|
|
773
897
|
[storage, upsertStoredThread]
|
|
774
898
|
);
|
|
775
|
-
const refresh =
|
|
899
|
+
const refresh = useCallback3(async () => {
|
|
776
900
|
const seq = ++refreshSeqRef.current;
|
|
777
901
|
setIsRefreshingThreads(true);
|
|
778
902
|
try {
|
|
@@ -786,7 +910,7 @@ function useAgents24ChatController({
|
|
|
786
910
|
if (!lifecycleAbortController.signal.aborted && seq === refreshSeqRef.current) setIsRefreshingThreads(false);
|
|
787
911
|
}
|
|
788
912
|
}, [lifecycleAbortController, storage, transport]);
|
|
789
|
-
const applyThreadId =
|
|
913
|
+
const applyThreadId = useCallback3(
|
|
790
914
|
(threadId, baseMessages) => {
|
|
791
915
|
if (!threadId || activeThreadIdRef.current === threadId) return;
|
|
792
916
|
activeThreadIdRef.current = threadId;
|
|
@@ -800,15 +924,15 @@ function useAgents24ChatController({
|
|
|
800
924
|
streamingContentRef.current = value;
|
|
801
925
|
setStreamingContent(value);
|
|
802
926
|
};
|
|
803
|
-
const setLoadingHistory =
|
|
927
|
+
const setLoadingHistory = useCallback3((value) => {
|
|
804
928
|
isLoadingHistoryRef.current = value;
|
|
805
929
|
setIsLoadingHistory(value);
|
|
806
930
|
}, []);
|
|
807
|
-
const setReasoningSteps =
|
|
931
|
+
const setReasoningSteps = useCallback3((value) => {
|
|
808
932
|
reasoningRef.current = value || [];
|
|
809
933
|
setCurrentReasoning(value || []);
|
|
810
934
|
}, []);
|
|
811
|
-
const detachActiveStream =
|
|
935
|
+
const detachActiveStream = useCallback3(() => {
|
|
812
936
|
const controller = abortControllerRef.current;
|
|
813
937
|
abortControllerRef.current = null;
|
|
814
938
|
abortDetachedStream(controller);
|
|
@@ -822,7 +946,7 @@ function useAgents24ChatController({
|
|
|
822
946
|
setStreamingContent("");
|
|
823
947
|
setCurrentReasoning([]);
|
|
824
948
|
}, [setActiveRunIdValue]);
|
|
825
|
-
const clearMissingThread =
|
|
949
|
+
const clearMissingThread = useCallback3(
|
|
826
950
|
(threadId) => {
|
|
827
951
|
storage.deleteThread?.(threadId);
|
|
828
952
|
syncThreadsFromStorage();
|
|
@@ -844,7 +968,7 @@ function useAgents24ChatController({
|
|
|
844
968
|
},
|
|
845
969
|
[detachActiveStream, onActiveThreadIdChange, setLoadingHistory, storage, syncThreadsFromStorage]
|
|
846
970
|
);
|
|
847
|
-
const setLiveAssistantMessage =
|
|
971
|
+
const setLiveAssistantMessage = useCallback3(
|
|
848
972
|
(input) => {
|
|
849
973
|
setMessages((prev) => {
|
|
850
974
|
const next = upsertStableAssistantMessage(prev, {
|
|
@@ -876,7 +1000,7 @@ function useAgents24ChatController({
|
|
|
876
1000
|
},
|
|
877
1001
|
[]
|
|
878
1002
|
);
|
|
879
|
-
const finalizeAssistantMessage =
|
|
1003
|
+
const finalizeAssistantMessage = useCallback3(
|
|
880
1004
|
(input) => {
|
|
881
1005
|
const content = input.error || input.assistantText.trim();
|
|
882
1006
|
if (!content) return input.baseMessages;
|
|
@@ -930,7 +1054,7 @@ function useAgents24ChatController({
|
|
|
930
1054
|
},
|
|
931
1055
|
[createId, persistThread, storage, upsertStoredThread]
|
|
932
1056
|
);
|
|
933
|
-
const loadThread =
|
|
1057
|
+
const loadThread = useCallback3(
|
|
934
1058
|
async (threadId) => {
|
|
935
1059
|
const seq = ++requestSeqRef.current;
|
|
936
1060
|
setLoadingHistory(true);
|
|
@@ -975,7 +1099,7 @@ function useAgents24ChatController({
|
|
|
975
1099
|
},
|
|
976
1100
|
[clearMissingThread, lifecycleAbortController, onThreadDetailLoaded, pageSize, setLoadingHistory, transport, upsertStoredThread]
|
|
977
1101
|
);
|
|
978
|
-
const loadOlderTurns =
|
|
1102
|
+
const loadOlderTurns = useCallback3(async () => {
|
|
979
1103
|
const threadId = activeThreadIdRef.current;
|
|
980
1104
|
const beforeTurnIndex = nextBeforeTurnIndexRef.current;
|
|
981
1105
|
if (!threadId || beforeTurnIndex === null || isLoadingOlderRef.current) return;
|
|
@@ -1011,7 +1135,7 @@ function useAgents24ChatController({
|
|
|
1011
1135
|
isLoadingOlderRef.current = false;
|
|
1012
1136
|
}
|
|
1013
1137
|
}, [clearMissingThread, lifecycleAbortController, pageSize, persistThread, transport]);
|
|
1014
|
-
const handleStreamEvent =
|
|
1138
|
+
const handleStreamEvent = useCallback3(
|
|
1015
1139
|
(input) => {
|
|
1016
1140
|
const { mode, event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages } = input;
|
|
1017
1141
|
const payload = event.payload || {};
|
|
@@ -1072,7 +1196,7 @@ function useAgents24ChatController({
|
|
|
1072
1196
|
},
|
|
1073
1197
|
[applyThreadId, finalizeAssistantMessage, markThreadRunStatus, onRuntimeEvent, onStreamErrorMessage, setActiveRunIdValue, setLiveAssistantMessage, setReasoningSteps]
|
|
1074
1198
|
);
|
|
1075
|
-
const runStream =
|
|
1199
|
+
const runStream = useCallback3(
|
|
1076
1200
|
async (input) => {
|
|
1077
1201
|
const startedAt = Date.now();
|
|
1078
1202
|
const controller = new AbortController();
|
|
@@ -1195,18 +1319,26 @@ function useAgents24ChatController({
|
|
|
1195
1319
|
},
|
|
1196
1320
|
[createId, finalizeAssistantMessage, handleStreamEvent, onStreamErrorMessage, persistThread, refresh, setActiveRunIdValue, setReasoningSteps, transport]
|
|
1197
1321
|
);
|
|
1198
|
-
const handleSubmit =
|
|
1322
|
+
const handleSubmit = useCallback3(
|
|
1199
1323
|
async (message) => {
|
|
1200
1324
|
if (!message.text.trim() && !(message.files || []).length) return;
|
|
1201
1325
|
await runStream({ mode: "submit", message });
|
|
1202
1326
|
},
|
|
1203
1327
|
[runStream]
|
|
1204
1328
|
);
|
|
1205
|
-
const attachRun =
|
|
1329
|
+
const attachRun = useCallback3(async (runId, threadId) => {
|
|
1206
1330
|
const resolvedThreadId = threadId ?? activeThreadIdRef.current;
|
|
1207
1331
|
if (runId && resolvedThreadId) await runStream({ mode: "attach", runId, threadId: resolvedThreadId });
|
|
1208
1332
|
}, [runStream]);
|
|
1209
|
-
const
|
|
1333
|
+
const { pendingHitl, isResolvingHitl, resumeHitl } = useControllerHitl({
|
|
1334
|
+
messages,
|
|
1335
|
+
transport,
|
|
1336
|
+
activeRunId,
|
|
1337
|
+
activeRunIdRef,
|
|
1338
|
+
activeThreadIdRef,
|
|
1339
|
+
runStream
|
|
1340
|
+
});
|
|
1341
|
+
const handleStop = useCallback3(() => {
|
|
1210
1342
|
const runId = activeRunIdRef.current;
|
|
1211
1343
|
const partial = streamingContentRef.current;
|
|
1212
1344
|
const liveMessageId = streamingMessageIdRef.current;
|
|
@@ -1230,56 +1362,25 @@ function useAgents24ChatController({
|
|
|
1230
1362
|
});
|
|
1231
1363
|
}
|
|
1232
1364
|
}, [finalizeAssistantMessage, lastThinkingDurationMs, setActiveRunIdValue, setReasoningSteps, transport]);
|
|
1233
|
-
|
|
1365
|
+
useEffect2(() => {
|
|
1234
1366
|
refresh().catch((error) => {
|
|
1235
1367
|
if (!isAbortError(error) && !lifecycleAbortController.signal.aborted) {
|
|
1236
1368
|
setThreads(storage.listThreads());
|
|
1237
1369
|
}
|
|
1238
1370
|
});
|
|
1239
1371
|
}, [lifecycleAbortController, refresh, storage]);
|
|
1240
|
-
|
|
1372
|
+
useEffect2(() => {
|
|
1241
1373
|
return () => {
|
|
1242
1374
|
lifecycleAbortController.abort();
|
|
1243
1375
|
abortDetachedStream(abortControllerRef.current);
|
|
1244
1376
|
abortControllerRef.current = null;
|
|
1245
1377
|
};
|
|
1246
1378
|
}, [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(() => {
|
|
1379
|
+
useControllerThreadEvents({ transport, storage, refresh, setThreads });
|
|
1380
|
+
useEffect2(() => {
|
|
1280
1381
|
syncThreadsFromStorage();
|
|
1281
1382
|
}, [storageKey, syncThreadsFromStorage]);
|
|
1282
|
-
|
|
1383
|
+
useEffect2(() => {
|
|
1283
1384
|
const previous = activeThreadIdRef.current;
|
|
1284
1385
|
activeThreadIdRef.current = activeThreadId;
|
|
1285
1386
|
if (activeThreadId && previous === activeThreadId && (activeRunIdRef.current || streamingMessageIdRef.current)) {
|
|
@@ -1323,14 +1424,14 @@ function useAgents24ChatController({
|
|
|
1323
1424
|
}
|
|
1324
1425
|
void loadThread(activeThreadId).catch(() => setLoadingHistory(false));
|
|
1325
1426
|
}, [activeThreadId, detachActiveStream, loadThread, setLoadingHistory, storage]);
|
|
1326
|
-
|
|
1427
|
+
useEffect2(() => {
|
|
1327
1428
|
const threadId = activeThreadId;
|
|
1328
1429
|
if (!threadId || isLoadingHistory || loadedThreadIdRef.current !== threadId || activeRunIdRef.current) return;
|
|
1329
1430
|
const runId = autoAttachRunIdFromThread(storage.getThread(threadId));
|
|
1330
1431
|
if (!runId || reattachedRunIdRef.current === runId) return;
|
|
1331
1432
|
void runStream({ mode: "attach", threadId, runId });
|
|
1332
1433
|
}, [activeThreadId, isLoadingHistory, runStream, storage, storageKey]);
|
|
1333
|
-
|
|
1434
|
+
useEffect2(() => {
|
|
1334
1435
|
const threadId = activeThreadId;
|
|
1335
1436
|
if (!threadId || isLoadingHistoryRef.current || activeRunIdRef.current || streamingMessageIdRef.current) {
|
|
1336
1437
|
return;
|
|
@@ -1371,7 +1472,7 @@ function useAgents24ChatController({
|
|
|
1371
1472
|
setMessages,
|
|
1372
1473
|
storage
|
|
1373
1474
|
});
|
|
1374
|
-
const loadThreadById =
|
|
1475
|
+
const loadThreadById = useCallback3(
|
|
1375
1476
|
async (threadId) => {
|
|
1376
1477
|
if (!threadId) return;
|
|
1377
1478
|
setIsSelectingThread(true);
|
|
@@ -1390,7 +1491,7 @@ function useAgents24ChatController({
|
|
|
1390
1491
|
[detachActiveStream, loadThread, onActiveThreadIdChange, storage]
|
|
1391
1492
|
);
|
|
1392
1493
|
const activeThread = activeThreadId ? storage.getThread(activeThreadId) || threads.find((thread) => thread.id === activeThreadId) || null : null;
|
|
1393
|
-
return
|
|
1494
|
+
return useMemo2(() => ({
|
|
1394
1495
|
threads,
|
|
1395
1496
|
activeThreadId,
|
|
1396
1497
|
activeThread,
|
|
@@ -1410,8 +1511,11 @@ function useAgents24ChatController({
|
|
|
1410
1511
|
copiedMessageId,
|
|
1411
1512
|
lastThinkingDurationMs,
|
|
1412
1513
|
activeRunId,
|
|
1514
|
+
pendingHitl,
|
|
1515
|
+
isResolvingHitl,
|
|
1413
1516
|
handleSubmit,
|
|
1414
1517
|
attachRun,
|
|
1518
|
+
resumeHitl,
|
|
1415
1519
|
handleStop,
|
|
1416
1520
|
handleCopy,
|
|
1417
1521
|
handleLike,
|
|
@@ -1450,8 +1554,11 @@ function useAgents24ChatController({
|
|
|
1450
1554
|
loadThreadById,
|
|
1451
1555
|
loadOlderTurns,
|
|
1452
1556
|
messages,
|
|
1557
|
+
isResolvingHitl,
|
|
1453
1558
|
onSourceClick,
|
|
1559
|
+
pendingHitl,
|
|
1454
1560
|
refresh,
|
|
1561
|
+
resumeHitl,
|
|
1455
1562
|
streamingContent,
|
|
1456
1563
|
streamingMessageId,
|
|
1457
1564
|
startNewThread,
|
|
@@ -1460,6 +1567,59 @@ function useAgents24ChatController({
|
|
|
1460
1567
|
]);
|
|
1461
1568
|
}
|
|
1462
1569
|
|
|
1570
|
+
// src/mcp-oauth.ts
|
|
1571
|
+
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;
|
|
1572
|
+
function normalizedOrigin(value) {
|
|
1573
|
+
try {
|
|
1574
|
+
return new URL(value).origin;
|
|
1575
|
+
} catch {
|
|
1576
|
+
throw new Error("MCP authorization returned an invalid callback origin.");
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
function waitForMcpOauthComplete({
|
|
1580
|
+
popup,
|
|
1581
|
+
callbackOrigin,
|
|
1582
|
+
serverId,
|
|
1583
|
+
timeoutMs = 12e4
|
|
1584
|
+
}) {
|
|
1585
|
+
if (!popup) return Promise.reject(new Error("Could not open the MCP authorization popup."));
|
|
1586
|
+
const expectedOrigin = normalizedOrigin(callbackOrigin);
|
|
1587
|
+
const expectedServerId = String(serverId || "").trim();
|
|
1588
|
+
if (!expectedServerId) return Promise.reject(new Error("MCP authorization is missing its server identity."));
|
|
1589
|
+
return new Promise((resolve, reject) => {
|
|
1590
|
+
let settled = false;
|
|
1591
|
+
const finish = (result) => {
|
|
1592
|
+
if (settled) return;
|
|
1593
|
+
settled = true;
|
|
1594
|
+
window.clearTimeout(timeout);
|
|
1595
|
+
window.clearInterval(closeTimer);
|
|
1596
|
+
window.removeEventListener("message", onMessage);
|
|
1597
|
+
if (result instanceof Error) reject(result);
|
|
1598
|
+
else resolve(result);
|
|
1599
|
+
};
|
|
1600
|
+
const onMessage = (event) => {
|
|
1601
|
+
if (event.source !== popup || event.origin !== expectedOrigin) return;
|
|
1602
|
+
const data = event.data;
|
|
1603
|
+
if (data?.type !== "mcp-oauth-complete" || data.server_id !== expectedServerId) return;
|
|
1604
|
+
if (!data.success) {
|
|
1605
|
+
finish(new Error("MCP authorization failed."));
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
const connectionId = String(data.connection_id || "").trim();
|
|
1609
|
+
if (!CONNECTION_ID_PATTERN.test(connectionId)) return;
|
|
1610
|
+
finish({ connectionId });
|
|
1611
|
+
};
|
|
1612
|
+
const timeout = window.setTimeout(
|
|
1613
|
+
() => finish(new Error("MCP authorization timed out.")),
|
|
1614
|
+
Math.max(1, timeoutMs)
|
|
1615
|
+
);
|
|
1616
|
+
const closeTimer = window.setInterval(() => {
|
|
1617
|
+
if (popup.closed) finish(new Error("MCP authorization window was closed before connecting."));
|
|
1618
|
+
}, 500);
|
|
1619
|
+
window.addEventListener("message", onMessage);
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1463
1623
|
// src/renderers.tsx
|
|
1464
1624
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
1465
1625
|
var DefaultToolPart = ({ part }) => {
|
|
@@ -1680,9 +1840,10 @@ var createFetchChatTransport = ({
|
|
|
1680
1840
|
method: "POST",
|
|
1681
1841
|
headers: jsonHeaders(await loadHeaders()),
|
|
1682
1842
|
body: JSON.stringify({
|
|
1683
|
-
schema_version: "agents24.hitl.resume.
|
|
1843
|
+
schema_version: "agents24.hitl.resume.v2",
|
|
1684
1844
|
interrupt_id: input.interruptId,
|
|
1685
|
-
|
|
1845
|
+
action: input.action,
|
|
1846
|
+
comment: input.comment,
|
|
1686
1847
|
client: input.client
|
|
1687
1848
|
})
|
|
1688
1849
|
});
|
|
@@ -1766,6 +1927,7 @@ export {
|
|
|
1766
1927
|
useMessageScroller,
|
|
1767
1928
|
useMessageScrollerScrollable,
|
|
1768
1929
|
useMessageScrollerVisibility,
|
|
1769
|
-
useStreamingText
|
|
1930
|
+
useStreamingText,
|
|
1931
|
+
waitForMcpOauthComplete
|
|
1770
1932
|
};
|
|
1771
1933
|
//# sourceMappingURL=index.js.map
|