@sinas/sdk 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QDIOYYBA.js → chunk-RHQVV5O6.js} +220 -99
- package/dist/chunk-RHQVV5O6.js.map +1 -0
- package/dist/client.cjs +226 -98
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +228 -5
- package/dist/client.d.ts +228 -5
- package/dist/client.js +17 -1
- package/dist/index.cjs +494 -244
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -4
- package/dist/index.d.ts +62 -4
- package/dist/index.js +286 -147
- package/dist/index.js.map +1 -1
- package/dist/sinas-sdk.umd.js +486 -244
- package/dist/sinas-sdk.umd.js.map +1 -1
- package/package.json +6 -3
- package/dist/chunk-QDIOYYBA.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,9 @@ __export(src_exports, {
|
|
|
38
38
|
SinasPermissionError: () => SinasPermissionError,
|
|
39
39
|
SinasProvider: () => SinasProvider,
|
|
40
40
|
SinasTimeoutError: () => SinasTimeoutError,
|
|
41
|
+
answerPendingInput: () => answerPendingInput,
|
|
42
|
+
answerResumeChannel: () => answerResumeChannel,
|
|
43
|
+
approvalResumeChannel: () => approvalResumeChannel,
|
|
41
44
|
approveToolCall: () => approveToolCall,
|
|
42
45
|
cancelBatch: () => cancelBatch,
|
|
43
46
|
checkPermissions: () => checkPermissions,
|
|
@@ -50,11 +53,16 @@ __export(src_exports, {
|
|
|
50
53
|
getChat: () => getChat,
|
|
51
54
|
getInfo: () => getInfo,
|
|
52
55
|
getMe: () => getMe,
|
|
56
|
+
invokeAgent: () => invokeAgent,
|
|
53
57
|
listBatchExecutions: () => listBatchExecutions,
|
|
54
58
|
listCollections: () => listCollections,
|
|
55
59
|
listFiles: () => listFiles,
|
|
60
|
+
listPendingInputs: () => listPendingInputs,
|
|
56
61
|
login: () => login,
|
|
57
62
|
logout: () => logout,
|
|
63
|
+
parsePendingInput: () => parsePendingInput,
|
|
64
|
+
parsePendingInputs: () => parsePendingInputs,
|
|
65
|
+
pendingInputFromChunk: () => pendingInputFromChunk,
|
|
58
66
|
refresh: () => refresh,
|
|
59
67
|
sendMessage: () => sendMessage,
|
|
60
68
|
stateDelete: () => stateDelete,
|
|
@@ -537,12 +545,111 @@ async function stateList(client, ns) {
|
|
|
537
545
|
return res.json();
|
|
538
546
|
}
|
|
539
547
|
|
|
548
|
+
// src/client/chatEvents.ts
|
|
549
|
+
var isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
550
|
+
var str = (v) => typeof v === "string" ? v : null;
|
|
551
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
552
|
+
var optionsOf = (v) => Array.isArray(v) && v.length > 0 && v.every((o) => typeof o === "string") ? v : null;
|
|
553
|
+
function parsePendingInput(raw) {
|
|
554
|
+
if (!isObject(raw)) return null;
|
|
555
|
+
if (typeof raw.tool_call_id !== "string" || !raw.tool_call_id) return null;
|
|
556
|
+
return {
|
|
557
|
+
toolCallId: raw.tool_call_id,
|
|
558
|
+
question: str(raw.question) ?? "",
|
|
559
|
+
options: optionsOf(raw.options),
|
|
560
|
+
expiresAt: str(raw.expires_at),
|
|
561
|
+
createdAt: str(raw.created_at)
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
function parsePendingInputs(raw) {
|
|
565
|
+
return Array.isArray(raw) ? raw.map(parsePendingInput).filter((p) => p !== null) : [];
|
|
566
|
+
}
|
|
567
|
+
function pendingInputFromChunk(chunk) {
|
|
568
|
+
if (chunk.event !== "input_required" && chunk.event !== "message") return null;
|
|
569
|
+
let parsed;
|
|
570
|
+
try {
|
|
571
|
+
parsed = JSON.parse(chunk.data);
|
|
572
|
+
} catch {
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
575
|
+
if (chunk.event === "message" && (!isObject(parsed) || parsed.type !== "input_required")) return null;
|
|
576
|
+
return parsePendingInput(parsed);
|
|
577
|
+
}
|
|
578
|
+
function parseApproval(raw) {
|
|
579
|
+
if (!isObject(raw) || typeof raw.tool_call_id !== "string") return null;
|
|
580
|
+
return {
|
|
581
|
+
type: str(raw.type) ?? "approval_required",
|
|
582
|
+
toolCallId: raw.tool_call_id,
|
|
583
|
+
functionNamespace: str(raw.function_namespace) ?? "",
|
|
584
|
+
functionName: str(raw.function_name) ?? "",
|
|
585
|
+
arguments: isObject(raw.arguments) ? raw.arguments : {}
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function parseUsage(raw) {
|
|
589
|
+
if (!isObject(raw)) return null;
|
|
590
|
+
return {
|
|
591
|
+
calls: num(raw.calls),
|
|
592
|
+
promptTokens: num(raw.prompt_tokens),
|
|
593
|
+
completionTokens: num(raw.completion_tokens),
|
|
594
|
+
totalTokens: num(raw.total_tokens),
|
|
595
|
+
cacheReadTokens: num(raw.cache_read_tokens),
|
|
596
|
+
cacheWriteTokens: num(raw.cache_write_tokens),
|
|
597
|
+
costUsd: typeof raw.cost_usd === "number" ? raw.cost_usd : null,
|
|
598
|
+
unpricedCalls: num(raw.unpriced_calls),
|
|
599
|
+
errors: num(raw.errors)
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
var TURN_STATUSES = ["completed", "input_required", "in_progress", "injected"];
|
|
603
|
+
function parseTurnState(raw) {
|
|
604
|
+
const status = TURN_STATUSES.includes(raw.status) ? raw.status : "completed";
|
|
605
|
+
return {
|
|
606
|
+
status,
|
|
607
|
+
pendingInputs: parsePendingInputs(raw.pending_inputs),
|
|
608
|
+
channelId: str(raw.channel_id),
|
|
609
|
+
usage: parseUsage(raw.usage)
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function parseMessage(data) {
|
|
613
|
+
const toolCalls = data.tool_calls;
|
|
614
|
+
return {
|
|
615
|
+
id: data.id,
|
|
616
|
+
chatId: data.chat_id,
|
|
617
|
+
role: data.role,
|
|
618
|
+
content: data.content ?? null,
|
|
619
|
+
toolCalls: toolCalls ?? null,
|
|
620
|
+
toolCallId: data.tool_call_id ?? null,
|
|
621
|
+
name: data.name ?? null,
|
|
622
|
+
createdAt: data.created_at
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
function parseDone(data) {
|
|
626
|
+
try {
|
|
627
|
+
const parsed = JSON.parse(data);
|
|
628
|
+
if (isObject(parsed) && typeof parsed.status === "string") return { status: parsed.status };
|
|
629
|
+
} catch {
|
|
630
|
+
}
|
|
631
|
+
return { status: "completed" };
|
|
632
|
+
}
|
|
633
|
+
function approvalResumeChannel(result, approved = true) {
|
|
634
|
+
if (result.resume === "waiting") return null;
|
|
635
|
+
if (result.resume === void 0 && !approved) return null;
|
|
636
|
+
return result.channelId || null;
|
|
637
|
+
}
|
|
638
|
+
function answerResumeChannel(result) {
|
|
639
|
+
if (!result.resumed && !result.resumePending) return null;
|
|
640
|
+
return result.channelId || null;
|
|
641
|
+
}
|
|
642
|
+
|
|
540
643
|
// src/client/chat.ts
|
|
541
|
-
|
|
644
|
+
function splitAgentRef(agentRef) {
|
|
542
645
|
const [ns, name] = agentRef.split("/");
|
|
543
646
|
if (!ns || !name) {
|
|
544
647
|
throw new Error(`Invalid agent ref "${agentRef}": expected "namespace/name"`);
|
|
545
648
|
}
|
|
649
|
+
return [ns, name];
|
|
650
|
+
}
|
|
651
|
+
async function createChat(client, agentRef, input) {
|
|
652
|
+
const [ns, name] = splitAgentRef(agentRef);
|
|
546
653
|
const config = client.getConfig();
|
|
547
654
|
const res = await client.fetch(`${config.apiBase}/agents/${ns}/${name}/chats`, {
|
|
548
655
|
method: "POST",
|
|
@@ -568,66 +675,117 @@ async function sendMessage(client, chatId, content) {
|
|
|
568
675
|
chatId: data.chat_id,
|
|
569
676
|
role: data.role,
|
|
570
677
|
content: data.content,
|
|
571
|
-
createdAt: data.created_at
|
|
678
|
+
createdAt: data.created_at,
|
|
679
|
+
...parseTurnState(data)
|
|
572
680
|
};
|
|
573
681
|
}
|
|
574
|
-
function
|
|
575
|
-
const
|
|
682
|
+
async function invokeAgent(client, agentRef, message, options = {}) {
|
|
683
|
+
const [ns, name] = splitAgentRef(agentRef);
|
|
684
|
+
const config = client.getConfig();
|
|
685
|
+
const body = { message };
|
|
686
|
+
if (options.sessionKey !== void 0) body.session_key = options.sessionKey;
|
|
687
|
+
if (options.reset !== void 0) body.reset = options.reset;
|
|
688
|
+
if (options.input !== void 0) body.input = options.input;
|
|
689
|
+
const res = await client.fetch(`${config.apiBase}/agents/${ns}/${name}/invoke`, {
|
|
690
|
+
method: "POST",
|
|
691
|
+
body: JSON.stringify(body)
|
|
692
|
+
});
|
|
693
|
+
const data = await res.json();
|
|
576
694
|
return {
|
|
577
|
-
|
|
695
|
+
reply: typeof data.reply === "string" ? data.reply : "",
|
|
578
696
|
chatId: data.chat_id,
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
toolCalls: toolCalls ?? null,
|
|
582
|
-
toolCallId: data.tool_call_id ?? null,
|
|
583
|
-
name: data.name ?? null,
|
|
584
|
-
createdAt: data.created_at
|
|
697
|
+
sessionKey: data.session_key ?? null,
|
|
698
|
+
...parseTurnState(data)
|
|
585
699
|
};
|
|
586
700
|
}
|
|
587
701
|
async function getChat(client, chatId) {
|
|
588
702
|
const config = client.getConfig();
|
|
589
703
|
const res = await client.fetch(`${config.apiBase}/chats/${chatId}`);
|
|
590
704
|
const data = await res.json();
|
|
591
|
-
|
|
705
|
+
const detail = {
|
|
592
706
|
id: data.id,
|
|
593
707
|
agentNamespace: data.agent_namespace,
|
|
594
708
|
agentName: data.agent_name,
|
|
595
709
|
title: data.title,
|
|
596
|
-
messages: (data.messages || []).map(
|
|
597
|
-
pendingApprovals: (data.pending_approvals || []).map((
|
|
598
|
-
type: "approval_required",
|
|
599
|
-
toolCallId: pa.tool_call_id,
|
|
600
|
-
functionNamespace: pa.function_namespace,
|
|
601
|
-
functionName: pa.function_name,
|
|
602
|
-
arguments: pa.arguments || {}
|
|
603
|
-
}))
|
|
710
|
+
messages: (data.messages || []).map(parseMessage),
|
|
711
|
+
pendingApprovals: (data.pending_approvals || []).map(parseApproval).filter((a) => a !== null)
|
|
604
712
|
};
|
|
713
|
+
if (Array.isArray(data.pending_inputs)) {
|
|
714
|
+
detail.pendingInputs = parsePendingInputs(data.pending_inputs);
|
|
715
|
+
}
|
|
716
|
+
return detail;
|
|
605
717
|
}
|
|
606
|
-
async function approveToolCall(client, chatId, toolCallId, approved) {
|
|
718
|
+
async function approveToolCall(client, chatId, toolCallId, approved, options = {}) {
|
|
607
719
|
const config = client.getConfig();
|
|
720
|
+
const body = { approved };
|
|
721
|
+
if (approved && options.alwaysAllow) body.always_allow = true;
|
|
608
722
|
const res = await client.fetch(
|
|
609
|
-
`${config.apiBase}/chats/${chatId}/approve-tool/${toolCallId}`,
|
|
610
|
-
{ method: "POST", body: JSON.stringify(
|
|
723
|
+
`${config.apiBase}/chats/${chatId}/approve-tool/${encodeURIComponent(toolCallId)}`,
|
|
724
|
+
{ method: "POST", body: JSON.stringify(body) }
|
|
611
725
|
);
|
|
612
726
|
const data = await res.json();
|
|
613
|
-
|
|
727
|
+
const result = {
|
|
614
728
|
status: data.status,
|
|
615
729
|
toolCallId: data.tool_call_id,
|
|
616
730
|
channelId: data.channel_id
|
|
617
731
|
};
|
|
732
|
+
if (data.resume === "waiting" || data.resume === "queued") result.resume = data.resume;
|
|
733
|
+
if (typeof data.message === "string") result.message = data.message;
|
|
734
|
+
return result;
|
|
618
735
|
}
|
|
619
|
-
function
|
|
620
|
-
const
|
|
736
|
+
async function listPendingInputs(client, chatId) {
|
|
737
|
+
const config = client.getConfig();
|
|
738
|
+
const res = await client.fetch(`${config.apiBase}/chats/${chatId}/pending-input`);
|
|
739
|
+
return parsePendingInputs(await res.json());
|
|
740
|
+
}
|
|
741
|
+
async function answerPendingInput(client, chatId, toolCallId, answer, options = {}) {
|
|
621
742
|
const config = client.getConfig();
|
|
743
|
+
const body = { answer };
|
|
744
|
+
if (options.wait) body.wait = true;
|
|
745
|
+
const res = await client.fetch(
|
|
746
|
+
`${config.apiBase}/chats/${chatId}/pending-input/${encodeURIComponent(toolCallId)}`,
|
|
747
|
+
{ method: "POST", body: JSON.stringify(body) }
|
|
748
|
+
);
|
|
749
|
+
const data = await res.json();
|
|
750
|
+
if (options.wait) {
|
|
751
|
+
return {
|
|
752
|
+
toolCallId: data.tool_call_id,
|
|
753
|
+
chatId: data.chat_id,
|
|
754
|
+
reply: typeof data.reply === "string" ? data.reply : "",
|
|
755
|
+
message: data.message && typeof data.message === "object" ? parseMessage(data.message) : null,
|
|
756
|
+
...parseTurnState(data)
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
return {
|
|
760
|
+
status: "answered",
|
|
761
|
+
toolCallId: data.tool_call_id,
|
|
762
|
+
resumed: !!data.resumed,
|
|
763
|
+
resumePending: !!data.resume_pending,
|
|
764
|
+
channelId: data.channel_id ?? null,
|
|
765
|
+
message: typeof data.message === "string" ? data.message : ""
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
function streamFromChannel(client, chatId, channelId, onChunk, onDone, onError, options = {}) {
|
|
769
|
+
const config = client.getConfig();
|
|
770
|
+
const params = new URLSearchParams({ last_id: options.lastId ?? "0" });
|
|
771
|
+
if (options.includeThinking) params.set("include_thinking", "true");
|
|
772
|
+
return runSSE(
|
|
773
|
+
`${config.apiBase}/chats/${chatId}/stream/${encodeURIComponent(channelId)}?${params}`,
|
|
774
|
+
(signal) => ({ headers: client.getAuthHeaders(), signal }),
|
|
775
|
+
"Stream reconnect failed",
|
|
776
|
+
onChunk,
|
|
777
|
+
onDone,
|
|
778
|
+
onError
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
function runSSE(url, init, failure, onChunk, onDone, onError) {
|
|
782
|
+
const controller = new AbortController();
|
|
622
783
|
(async () => {
|
|
623
784
|
try {
|
|
624
|
-
const res = await fetch(
|
|
625
|
-
`${config.apiBase}/chats/${chatId}/stream/${channelId}?last_id=0`,
|
|
626
|
-
{ headers: client.getAuthHeaders(), signal: controller.signal }
|
|
627
|
-
);
|
|
785
|
+
const res = await fetch(url, init(controller.signal));
|
|
628
786
|
if (!res.ok) {
|
|
629
787
|
const body = await res.json().catch(() => ({ detail: res.statusText }));
|
|
630
|
-
throw new Error(body.detail ||
|
|
788
|
+
throw new Error(body.detail || failure);
|
|
631
789
|
}
|
|
632
790
|
const reader = res.body.getReader();
|
|
633
791
|
const decoder = new TextDecoder();
|
|
@@ -641,7 +799,7 @@ function streamFromChannel(client, chatId, channelId, onChunk, onDone, onError)
|
|
|
641
799
|
for (const chunk of chunks) {
|
|
642
800
|
onChunk(chunk);
|
|
643
801
|
if (chunk.event === "done") {
|
|
644
|
-
onDone?.();
|
|
802
|
+
onDone?.(parseDone(chunk.data));
|
|
645
803
|
return;
|
|
646
804
|
}
|
|
647
805
|
}
|
|
@@ -650,9 +808,13 @@ function streamFromChannel(client, chatId, channelId, onChunk, onDone, onError)
|
|
|
650
808
|
const { chunks } = parseSSE(buffer + "\n\n");
|
|
651
809
|
for (const chunk of chunks) {
|
|
652
810
|
onChunk(chunk);
|
|
811
|
+
if (chunk.event === "done") {
|
|
812
|
+
onDone?.(parseDone(chunk.data));
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
653
815
|
}
|
|
654
816
|
}
|
|
655
|
-
onDone?.();
|
|
817
|
+
onDone?.({ status: "closed" });
|
|
656
818
|
} catch (err) {
|
|
657
819
|
if (err.name === "AbortError") return;
|
|
658
820
|
onError?.(err);
|
|
@@ -663,75 +825,33 @@ function streamFromChannel(client, chatId, channelId, onChunk, onDone, onError)
|
|
|
663
825
|
function parseSSE(buffer) {
|
|
664
826
|
const chunks = [];
|
|
665
827
|
const normalized = buffer.replace(/\r\n/g, "\n");
|
|
666
|
-
|
|
667
|
-
let
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
if (line.startsWith("event:")) {
|
|
677
|
-
event = line.slice(6).trim();
|
|
678
|
-
} else if (line.startsWith("data:")) {
|
|
679
|
-
data = line.slice(5).trim();
|
|
680
|
-
} else if (line === "") {
|
|
681
|
-
if (event || data) {
|
|
682
|
-
chunks.push({ event: event || "message", data });
|
|
683
|
-
event = "";
|
|
684
|
-
data = "";
|
|
685
|
-
}
|
|
828
|
+
let start = 0;
|
|
829
|
+
let end;
|
|
830
|
+
while ((end = normalized.indexOf("\n\n", start)) !== -1) {
|
|
831
|
+
const block = normalized.slice(start, end);
|
|
832
|
+
start = end + 2;
|
|
833
|
+
let event = "";
|
|
834
|
+
const data = [];
|
|
835
|
+
for (const line of block.split("\n")) {
|
|
836
|
+
if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
837
|
+
else if (line.startsWith("data:")) data.push(line.slice(5).trim());
|
|
686
838
|
}
|
|
839
|
+
if (event || data.length) chunks.push({ event: event || "message", data: data.join("\n") });
|
|
687
840
|
}
|
|
688
|
-
return { chunks, remaining };
|
|
841
|
+
return { chunks, remaining: normalized.slice(start) };
|
|
689
842
|
}
|
|
690
|
-
function streamMessage(client, chatId, content, onChunk, onDone, onError) {
|
|
691
|
-
const controller = new AbortController();
|
|
843
|
+
function streamMessage(client, chatId, content, onChunk, onDone, onError, options = {}) {
|
|
692
844
|
const config = client.getConfig();
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
throw new Error(body.detail || "Stream request failed");
|
|
704
|
-
}
|
|
705
|
-
const reader = res.body.getReader();
|
|
706
|
-
const decoder = new TextDecoder();
|
|
707
|
-
let buffer = "";
|
|
708
|
-
while (true) {
|
|
709
|
-
const { done, value } = await reader.read();
|
|
710
|
-
if (done) break;
|
|
711
|
-
buffer += decoder.decode(value, { stream: true });
|
|
712
|
-
const { chunks, remaining } = parseSSE(buffer);
|
|
713
|
-
buffer = remaining;
|
|
714
|
-
for (const chunk of chunks) {
|
|
715
|
-
onChunk(chunk);
|
|
716
|
-
if (chunk.event === "done") {
|
|
717
|
-
onDone?.();
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
if (buffer.trim()) {
|
|
723
|
-
const { chunks } = parseSSE(buffer + "\n\n");
|
|
724
|
-
for (const chunk of chunks) {
|
|
725
|
-
onChunk(chunk);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
onDone?.();
|
|
729
|
-
} catch (err) {
|
|
730
|
-
if (err.name === "AbortError") return;
|
|
731
|
-
onError?.(err);
|
|
732
|
-
}
|
|
733
|
-
})();
|
|
734
|
-
return controller;
|
|
845
|
+
const body = { content };
|
|
846
|
+
if (options.includeThinking) body.include_thinking = true;
|
|
847
|
+
return runSSE(
|
|
848
|
+
`${config.apiBase}/chats/${chatId}/messages/stream`,
|
|
849
|
+
(signal) => ({ method: "POST", headers: client.getAuthHeaders(), body: JSON.stringify(body), signal }),
|
|
850
|
+
"Stream request failed",
|
|
851
|
+
onChunk,
|
|
852
|
+
onDone,
|
|
853
|
+
onError
|
|
854
|
+
);
|
|
735
855
|
}
|
|
736
856
|
|
|
737
857
|
// src/client/collections.ts
|
|
@@ -946,29 +1066,172 @@ function useStateStore(ns, key) {
|
|
|
946
1066
|
|
|
947
1067
|
// src/hooks/useChat.ts
|
|
948
1068
|
var import_react5 = __toESM(require("react"), 1);
|
|
1069
|
+
|
|
1070
|
+
// src/hooks/chatState.ts
|
|
1071
|
+
function parseMessageChunk(data) {
|
|
1072
|
+
let parsed;
|
|
1073
|
+
try {
|
|
1074
|
+
parsed = JSON.parse(data);
|
|
1075
|
+
} catch {
|
|
1076
|
+
return { text: data };
|
|
1077
|
+
}
|
|
1078
|
+
if (!parsed || typeof parsed !== "object") return { text: "" };
|
|
1079
|
+
const p = parsed;
|
|
1080
|
+
const out = { text: typeof p.content === "string" ? p.content : "" };
|
|
1081
|
+
if (typeof p.thinking === "string") out.thinking = p.thinking;
|
|
1082
|
+
return out;
|
|
1083
|
+
}
|
|
1084
|
+
function applyTextDelta(messages, text, chatId, now = /* @__PURE__ */ new Date()) {
|
|
1085
|
+
if (!text) return messages;
|
|
1086
|
+
const last = messages[messages.length - 1];
|
|
1087
|
+
if (last && last.role === "assistant" && !last.toolCalls && last.streaming) {
|
|
1088
|
+
const next = [...messages];
|
|
1089
|
+
next[next.length - 1] = { ...last, content: (last.content || "") + text };
|
|
1090
|
+
return next;
|
|
1091
|
+
}
|
|
1092
|
+
return [
|
|
1093
|
+
...messages,
|
|
1094
|
+
{
|
|
1095
|
+
id: `tmp-text-${now.getTime()}`,
|
|
1096
|
+
chatId,
|
|
1097
|
+
role: "assistant",
|
|
1098
|
+
content: text,
|
|
1099
|
+
toolCalls: null,
|
|
1100
|
+
toolCallId: null,
|
|
1101
|
+
name: null,
|
|
1102
|
+
createdAt: now.toISOString(),
|
|
1103
|
+
streaming: true
|
|
1104
|
+
}
|
|
1105
|
+
];
|
|
1106
|
+
}
|
|
1107
|
+
function applyToolStart(messages, event, chatId, now = /* @__PURE__ */ new Date()) {
|
|
1108
|
+
if (messages.some((m) => m.toolCalls?.some((tc) => tc.id === event.tool_call_id))) return messages;
|
|
1109
|
+
const frozen = messages.map(
|
|
1110
|
+
(m, i) => i === messages.length - 1 && m.streaming && m.role === "assistant" && !m.toolCalls ? { ...m, streaming: false } : m
|
|
1111
|
+
);
|
|
1112
|
+
return [
|
|
1113
|
+
...frozen,
|
|
1114
|
+
{
|
|
1115
|
+
id: `tmp-tc-${event.tool_call_id}`,
|
|
1116
|
+
chatId,
|
|
1117
|
+
role: "assistant",
|
|
1118
|
+
content: null,
|
|
1119
|
+
toolCalls: [
|
|
1120
|
+
{
|
|
1121
|
+
id: event.tool_call_id,
|
|
1122
|
+
type: "function",
|
|
1123
|
+
function: { name: event.name, arguments: event.arguments || "{}" },
|
|
1124
|
+
description: event.description
|
|
1125
|
+
}
|
|
1126
|
+
],
|
|
1127
|
+
toolCallId: null,
|
|
1128
|
+
name: null,
|
|
1129
|
+
createdAt: now.toISOString(),
|
|
1130
|
+
streaming: true
|
|
1131
|
+
}
|
|
1132
|
+
];
|
|
1133
|
+
}
|
|
1134
|
+
function applyToolEnd(messages, event) {
|
|
1135
|
+
if (event.result === void 0) return messages;
|
|
1136
|
+
const content = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
|
|
1137
|
+
return messages.map(
|
|
1138
|
+
(m) => m.toolCalls?.some((tc) => tc.id === event.tool_call_id) ? { ...m, toolResults: { ...m.toolResults, [event.tool_call_id]: { content, name: event.name } } } : m
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
function addApproval(current, approval) {
|
|
1142
|
+
return current.some((a) => a.toolCallId === approval.toolCallId) ? current : [...current, approval];
|
|
1143
|
+
}
|
|
1144
|
+
function mergePendingInputs(current, incoming) {
|
|
1145
|
+
const seen = new Set(current.map((p) => p.toolCallId));
|
|
1146
|
+
const added = incoming.filter((p) => !seen.has(p.toolCallId) && seen.add(p.toolCallId));
|
|
1147
|
+
return added.length ? [...current, ...added] : current;
|
|
1148
|
+
}
|
|
1149
|
+
var NOT_THINKING = { active: false, rounds: [] };
|
|
1150
|
+
function applyThinking(state, thinking) {
|
|
1151
|
+
if (!state.active) return { active: true, rounds: [...state.rounds, thinking] };
|
|
1152
|
+
if (!thinking) return state;
|
|
1153
|
+
const rounds = [...state.rounds];
|
|
1154
|
+
rounds[rounds.length - 1] += thinking;
|
|
1155
|
+
return { active: true, rounds };
|
|
1156
|
+
}
|
|
1157
|
+
function stopThinking(state) {
|
|
1158
|
+
return state.active ? { ...state, active: false } : state;
|
|
1159
|
+
}
|
|
1160
|
+
function thinkingView(state) {
|
|
1161
|
+
return { active: state.active, text: state.rounds.filter((r) => r).join("\n\n") };
|
|
1162
|
+
}
|
|
1163
|
+
function createLatestGate() {
|
|
1164
|
+
let latest = 0;
|
|
1165
|
+
return {
|
|
1166
|
+
next: () => ++latest,
|
|
1167
|
+
isCurrent: (ticket) => ticket === latest
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
var PENDING_POLL_MAX_MS = 3e4;
|
|
1171
|
+
var PENDING_POLL_MIN_MS = 1e3;
|
|
1172
|
+
var EXPIRY_GRACE_MS = 5e3;
|
|
1173
|
+
var OVERDUE_POLL_MS = 1e4;
|
|
1174
|
+
function nextPendingPollMs(inputs, nowMs) {
|
|
1175
|
+
let wait = PENDING_POLL_MAX_MS;
|
|
1176
|
+
for (const input of inputs) {
|
|
1177
|
+
const deadline = input.expiresAt ? Date.parse(input.expiresAt) : NaN;
|
|
1178
|
+
if (Number.isNaN(deadline)) continue;
|
|
1179
|
+
const untilDue = deadline + EXPIRY_GRACE_MS - nowMs;
|
|
1180
|
+
wait = Math.min(wait, untilDue > 0 ? untilDue : OVERDUE_POLL_MS);
|
|
1181
|
+
}
|
|
1182
|
+
return Math.max(PENDING_POLL_MIN_MS, wait);
|
|
1183
|
+
}
|
|
1184
|
+
var TRAILING_POLLS_MS = [5e3, 15e3, 3e4];
|
|
1185
|
+
|
|
1186
|
+
// src/hooks/useChat.ts
|
|
949
1187
|
function useChat(agentRef, options) {
|
|
950
1188
|
const client = useClient();
|
|
1189
|
+
const includeThinking = !!options?.includeThinking;
|
|
1190
|
+
const pollPendingInputs = options?.pollPendingInputs !== false;
|
|
951
1191
|
const [chatId, setChatId] = import_react5.default.useState(options?.chatId ?? null);
|
|
952
1192
|
const [messages, setMessages] = import_react5.default.useState([]);
|
|
953
1193
|
const [loading, setLoading] = import_react5.default.useState(false);
|
|
954
1194
|
const [streaming, setStreaming] = import_react5.default.useState(false);
|
|
955
1195
|
const [error, setError] = import_react5.default.useState(null);
|
|
956
1196
|
const [pendingApprovals, setPendingApprovals] = import_react5.default.useState([]);
|
|
1197
|
+
const [pendingInputs, setPendingInputs] = import_react5.default.useState([]);
|
|
1198
|
+
const [thinking, setThinking] = import_react5.default.useState(NOT_THINKING);
|
|
1199
|
+
const thinkingShown = import_react5.default.useMemo(() => thinkingView(thinking), [thinking]);
|
|
957
1200
|
const [toolStatus, setToolStatus] = import_react5.default.useState([]);
|
|
1201
|
+
const [trailingPoll, setTrailingPoll] = import_react5.default.useState(TRAILING_POLLS_MS.length);
|
|
1202
|
+
const [pollTick, setPollTick] = import_react5.default.useState(0);
|
|
958
1203
|
const abortRef = import_react5.default.useRef(null);
|
|
959
1204
|
const chatIdRef = import_react5.default.useRef(chatId);
|
|
960
1205
|
chatIdRef.current = chatId;
|
|
1206
|
+
const [gate] = import_react5.default.useState(createLatestGate);
|
|
1207
|
+
const refresh2 = import_react5.default.useCallback(
|
|
1208
|
+
async (id) => {
|
|
1209
|
+
const ticket = gate.next();
|
|
1210
|
+
try {
|
|
1211
|
+
const detail = await getChat(client, id);
|
|
1212
|
+
if (!gate.isCurrent(ticket)) return null;
|
|
1213
|
+
setMessages(detail.messages);
|
|
1214
|
+
setPendingApprovals(detail.pendingApprovals ?? []);
|
|
1215
|
+
const inputs = detail.pendingInputs ?? [];
|
|
1216
|
+
setPendingInputs(inputs);
|
|
1217
|
+
return inputs;
|
|
1218
|
+
} catch {
|
|
1219
|
+
return null;
|
|
1220
|
+
}
|
|
1221
|
+
},
|
|
1222
|
+
[client, gate]
|
|
1223
|
+
);
|
|
961
1224
|
import_react5.default.useEffect(() => {
|
|
962
1225
|
if (!options?.chatId) return;
|
|
963
1226
|
let cancelled = false;
|
|
1227
|
+
const ticket = gate.next();
|
|
964
1228
|
setLoading(true);
|
|
965
1229
|
getChat(client, options.chatId).then((detail) => {
|
|
966
|
-
if (cancelled) return;
|
|
1230
|
+
if (cancelled || !gate.isCurrent(ticket)) return;
|
|
967
1231
|
setChatId(detail.id);
|
|
968
1232
|
setMessages(detail.messages);
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
}
|
|
1233
|
+
setPendingApprovals(detail.pendingApprovals ?? []);
|
|
1234
|
+
setPendingInputs(detail.pendingInputs ?? []);
|
|
972
1235
|
}).catch((e) => {
|
|
973
1236
|
if (cancelled) return;
|
|
974
1237
|
setError(e.message);
|
|
@@ -978,74 +1241,52 @@ function useChat(agentRef, options) {
|
|
|
978
1241
|
return () => {
|
|
979
1242
|
cancelled = true;
|
|
980
1243
|
};
|
|
981
|
-
}, [client, options?.chatId]);
|
|
1244
|
+
}, [client, gate, options?.chatId]);
|
|
982
1245
|
import_react5.default.useEffect(() => {
|
|
983
1246
|
return () => {
|
|
984
1247
|
abortRef.current?.abort();
|
|
985
1248
|
};
|
|
986
1249
|
}, []);
|
|
987
|
-
|
|
1250
|
+
import_react5.default.useEffect(() => {
|
|
1251
|
+
if (!pollPendingInputs || streaming || !chatId) return;
|
|
1252
|
+
let delay = null;
|
|
1253
|
+
if (pendingInputs.length > 0) delay = nextPendingPollMs(pendingInputs, Date.now());
|
|
1254
|
+
else if (trailingPoll < TRAILING_POLLS_MS.length) delay = TRAILING_POLLS_MS[trailingPoll];
|
|
1255
|
+
if (delay === null) return;
|
|
1256
|
+
const waiting = pendingInputs.map((p) => p.toolCallId);
|
|
1257
|
+
const timer = setTimeout(async () => {
|
|
1258
|
+
const now = await refresh2(chatId);
|
|
1259
|
+
if (waiting.length === 0) setTrailingPoll((i) => i + 1);
|
|
1260
|
+
else if (now && waiting.some((id) => !now.some((p) => p.toolCallId === id))) setTrailingPoll(0);
|
|
1261
|
+
setPollTick((t) => t + 1);
|
|
1262
|
+
}, delay);
|
|
1263
|
+
return () => clearTimeout(timer);
|
|
1264
|
+
}, [pollPendingInputs, chatId, streaming, pendingInputs, trailingPoll, pollTick, refresh2]);
|
|
1265
|
+
const handleStreamChunk = import_react5.default.useCallback(
|
|
988
1266
|
(chunk) => {
|
|
1267
|
+
const asked = pendingInputFromChunk(chunk);
|
|
1268
|
+
if (asked) {
|
|
1269
|
+
setThinking(stopThinking);
|
|
1270
|
+
setPendingInputs((prev) => mergePendingInputs(prev, [asked]));
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
const chatIdNow = chatIdRef.current || "";
|
|
989
1274
|
if (chunk.event === "message") {
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
}
|
|
997
|
-
})();
|
|
998
|
-
if (!newContent) return;
|
|
999
|
-
setMessages((prev) => {
|
|
1000
|
-
const last = prev[prev.length - 1];
|
|
1001
|
-
if (last && last.role === "assistant" && !last.toolCalls && last.streaming) {
|
|
1002
|
-
const next = [...prev];
|
|
1003
|
-
next[next.length - 1] = { ...last, content: (last.content || "") + newContent };
|
|
1004
|
-
return next;
|
|
1005
|
-
}
|
|
1006
|
-
return [...prev, {
|
|
1007
|
-
id: `tmp-text-${Date.now()}`,
|
|
1008
|
-
chatId: chatIdRef.current || "",
|
|
1009
|
-
role: "assistant",
|
|
1010
|
-
content: newContent,
|
|
1011
|
-
toolCalls: null,
|
|
1012
|
-
toolCallId: null,
|
|
1013
|
-
name: null,
|
|
1014
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1015
|
-
streaming: true
|
|
1016
|
-
}];
|
|
1017
|
-
});
|
|
1275
|
+
const { text, thinking: thought } = parseMessageChunk(chunk.data);
|
|
1276
|
+
if (thought !== void 0) setThinking((prev) => applyThinking(prev, thought));
|
|
1277
|
+
if (text) {
|
|
1278
|
+
setThinking(stopThinking);
|
|
1279
|
+
setMessages((prev) => applyTextDelta(prev, text, chatIdNow));
|
|
1280
|
+
}
|
|
1018
1281
|
} else if (chunk.event === "tool_start") {
|
|
1282
|
+
setThinking(stopThinking);
|
|
1019
1283
|
try {
|
|
1020
1284
|
const parsed = JSON.parse(chunk.data);
|
|
1021
1285
|
setToolStatus((prev) => [
|
|
1022
1286
|
...prev,
|
|
1023
1287
|
{ toolCallId: parsed.tool_call_id, name: parsed.name, description: parsed.description, status: "running" }
|
|
1024
1288
|
]);
|
|
1025
|
-
setMessages((prev) =>
|
|
1026
|
-
if (prev.some((m) => m.toolCalls?.some((tc) => tc.id === parsed.tool_call_id))) {
|
|
1027
|
-
return prev;
|
|
1028
|
-
}
|
|
1029
|
-
const next = prev.map(
|
|
1030
|
-
(m, i) => i === prev.length - 1 && m.streaming && m.role === "assistant" && !m.toolCalls ? { ...m, streaming: false } : m
|
|
1031
|
-
);
|
|
1032
|
-
return [...next, {
|
|
1033
|
-
id: `tmp-tc-${parsed.tool_call_id}`,
|
|
1034
|
-
chatId: chatIdRef.current || "",
|
|
1035
|
-
role: "assistant",
|
|
1036
|
-
content: null,
|
|
1037
|
-
toolCalls: [{
|
|
1038
|
-
id: parsed.tool_call_id,
|
|
1039
|
-
type: "function",
|
|
1040
|
-
function: { name: parsed.name, arguments: parsed.arguments || "{}" },
|
|
1041
|
-
description: parsed.description
|
|
1042
|
-
}],
|
|
1043
|
-
toolCallId: null,
|
|
1044
|
-
name: null,
|
|
1045
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1046
|
-
streaming: true
|
|
1047
|
-
}];
|
|
1048
|
-
});
|
|
1289
|
+
setMessages((prev) => applyToolStart(prev, parsed, chatIdNow));
|
|
1049
1290
|
} catch {
|
|
1050
1291
|
}
|
|
1051
1292
|
} else if (chunk.event === "tool_end") {
|
|
@@ -1054,39 +1295,18 @@ function useChat(agentRef, options) {
|
|
|
1054
1295
|
setToolStatus(
|
|
1055
1296
|
(prev) => prev.map((t) => t.toolCallId === parsed.tool_call_id ? { ...t, status: "complete" } : t)
|
|
1056
1297
|
);
|
|
1057
|
-
|
|
1058
|
-
const resultContent = typeof parsed.result === "string" ? parsed.result : JSON.stringify(parsed.result);
|
|
1059
|
-
setMessages(
|
|
1060
|
-
(prev) => prev.map((m) => {
|
|
1061
|
-
if (m.toolCalls?.some((tc) => tc.id === parsed.tool_call_id)) {
|
|
1062
|
-
return {
|
|
1063
|
-
...m,
|
|
1064
|
-
toolResults: {
|
|
1065
|
-
...m.toolResults,
|
|
1066
|
-
[parsed.tool_call_id]: { content: resultContent, name: parsed.name }
|
|
1067
|
-
}
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
1070
|
-
return m;
|
|
1071
|
-
})
|
|
1072
|
-
);
|
|
1073
|
-
}
|
|
1298
|
+
setMessages((prev) => applyToolEnd(prev, parsed));
|
|
1074
1299
|
} catch {
|
|
1075
1300
|
}
|
|
1076
1301
|
} else if (chunk.event === "approval_required") {
|
|
1302
|
+
setThinking(stopThinking);
|
|
1077
1303
|
try {
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
type: raw.type,
|
|
1081
|
-
toolCallId: raw.tool_call_id,
|
|
1082
|
-
functionNamespace: raw.function_namespace,
|
|
1083
|
-
functionName: raw.function_name,
|
|
1084
|
-
arguments: raw.arguments
|
|
1085
|
-
};
|
|
1086
|
-
setPendingApprovals((prev) => [...prev, parsed]);
|
|
1304
|
+
const approval = parseApproval(JSON.parse(chunk.data));
|
|
1305
|
+
if (approval) setPendingApprovals((prev) => addApproval(prev, approval));
|
|
1087
1306
|
} catch {
|
|
1088
1307
|
}
|
|
1089
1308
|
} else if (chunk.event === "error") {
|
|
1309
|
+
setThinking(stopThinking);
|
|
1090
1310
|
try {
|
|
1091
1311
|
const parsed = JSON.parse(chunk.data);
|
|
1092
1312
|
setError(parsed.error || "Stream error");
|
|
@@ -1097,18 +1317,37 @@ function useChat(agentRef, options) {
|
|
|
1097
1317
|
},
|
|
1098
1318
|
[]
|
|
1099
1319
|
);
|
|
1100
|
-
const
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1320
|
+
const streamCallbacks = import_react5.default.useCallback(
|
|
1321
|
+
(id) => [
|
|
1322
|
+
(_done) => {
|
|
1323
|
+
setStreaming(false);
|
|
1324
|
+
setToolStatus([]);
|
|
1325
|
+
setThinking(stopThinking);
|
|
1326
|
+
refresh2(id);
|
|
1327
|
+
},
|
|
1328
|
+
(err) => {
|
|
1329
|
+
setError(err.message);
|
|
1330
|
+
setStreaming(false);
|
|
1331
|
+
setToolStatus([]);
|
|
1332
|
+
setThinking(stopThinking);
|
|
1109
1333
|
}
|
|
1334
|
+
],
|
|
1335
|
+
[refresh2]
|
|
1336
|
+
);
|
|
1337
|
+
const followChannel = import_react5.default.useCallback(
|
|
1338
|
+
(id, channelId, lastId) => {
|
|
1339
|
+
abortRef.current?.abort();
|
|
1340
|
+
gate.next();
|
|
1341
|
+
setStreaming(true);
|
|
1342
|
+
setError(null);
|
|
1343
|
+
setThinking(NOT_THINKING);
|
|
1344
|
+
const [onDone, onError] = streamCallbacks(id);
|
|
1345
|
+
abortRef.current = streamFromChannel(client, id, channelId, handleStreamChunk, onDone, onError, {
|
|
1346
|
+
includeThinking,
|
|
1347
|
+
lastId
|
|
1348
|
+
});
|
|
1110
1349
|
},
|
|
1111
|
-
[client]
|
|
1350
|
+
[client, gate, includeThinking, handleStreamChunk, streamCallbacks]
|
|
1112
1351
|
);
|
|
1113
1352
|
const send = import_react5.default.useCallback(
|
|
1114
1353
|
(content) => {
|
|
@@ -1130,10 +1369,12 @@ function useChat(agentRef, options) {
|
|
|
1130
1369
|
setLoading(false);
|
|
1131
1370
|
}
|
|
1132
1371
|
abortRef.current?.abort();
|
|
1372
|
+
gate.next();
|
|
1133
1373
|
setStreaming(true);
|
|
1134
1374
|
setError(null);
|
|
1135
1375
|
setPendingApprovals([]);
|
|
1136
1376
|
setToolStatus([]);
|
|
1377
|
+
setThinking(NOT_THINKING);
|
|
1137
1378
|
const displayContent = typeof content === "string" ? content : JSON.stringify(content);
|
|
1138
1379
|
const userMsg = {
|
|
1139
1380
|
id: `tmp-user-${Date.now()}`,
|
|
@@ -1146,80 +1387,77 @@ function useChat(agentRef, options) {
|
|
|
1146
1387
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1147
1388
|
};
|
|
1148
1389
|
setMessages((prev) => [...prev, userMsg]);
|
|
1149
|
-
const
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
handleStreamChunks,
|
|
1154
|
-
() => {
|
|
1155
|
-
setStreaming(false);
|
|
1156
|
-
setToolStatus([]);
|
|
1157
|
-
refreshMessages(activeChatId);
|
|
1158
|
-
},
|
|
1159
|
-
(err) => {
|
|
1160
|
-
setError(err.message);
|
|
1161
|
-
setStreaming(false);
|
|
1162
|
-
setToolStatus([]);
|
|
1163
|
-
}
|
|
1164
|
-
);
|
|
1165
|
-
abortRef.current = controller;
|
|
1390
|
+
const [onDone, onError] = streamCallbacks(activeChatId);
|
|
1391
|
+
abortRef.current = streamMessage(client, activeChatId, content, handleStreamChunk, onDone, onError, {
|
|
1392
|
+
includeThinking
|
|
1393
|
+
});
|
|
1166
1394
|
};
|
|
1167
1395
|
doSend();
|
|
1168
1396
|
},
|
|
1169
|
-
[client, agentRef, options?.input,
|
|
1397
|
+
[client, agentRef, options?.input, gate, includeThinking, handleStreamChunk, streamCallbacks]
|
|
1170
1398
|
);
|
|
1171
|
-
const
|
|
1172
|
-
(toolCallId, approved) => {
|
|
1173
|
-
const
|
|
1174
|
-
if (!
|
|
1399
|
+
const decide = import_react5.default.useCallback(
|
|
1400
|
+
(toolCallId, approved, alwaysAllow) => {
|
|
1401
|
+
const id = chatIdRef.current;
|
|
1402
|
+
if (!id) return;
|
|
1403
|
+
gate.next();
|
|
1175
1404
|
setPendingApprovals((prev) => prev.filter((a) => a.toolCallId !== toolCallId));
|
|
1176
|
-
setStreaming(true);
|
|
1177
1405
|
setError(null);
|
|
1178
1406
|
(async () => {
|
|
1179
1407
|
try {
|
|
1180
|
-
const result = await approveToolCall(client,
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
return;
|
|
1185
|
-
}
|
|
1186
|
-
const controller = streamFromChannel(
|
|
1187
|
-
client,
|
|
1188
|
-
currentChatId,
|
|
1189
|
-
result.channelId,
|
|
1190
|
-
handleStreamChunks,
|
|
1191
|
-
() => {
|
|
1192
|
-
setStreaming(false);
|
|
1193
|
-
setToolStatus([]);
|
|
1194
|
-
refreshMessages(currentChatId);
|
|
1195
|
-
},
|
|
1196
|
-
(err) => {
|
|
1197
|
-
setError(err.message);
|
|
1198
|
-
setStreaming(false);
|
|
1199
|
-
setToolStatus([]);
|
|
1200
|
-
}
|
|
1201
|
-
);
|
|
1202
|
-
abortRef.current = controller;
|
|
1408
|
+
const result = await approveToolCall(client, id, toolCallId, approved, { alwaysAllow });
|
|
1409
|
+
const channelId = approvalResumeChannel(result, approved);
|
|
1410
|
+
if (channelId) followChannel(id, channelId);
|
|
1411
|
+
else if (result.resume === void 0) refresh2(id);
|
|
1203
1412
|
} catch (e) {
|
|
1204
1413
|
setError(e.message);
|
|
1205
|
-
|
|
1414
|
+
refresh2(id);
|
|
1206
1415
|
}
|
|
1207
1416
|
})();
|
|
1208
1417
|
},
|
|
1209
|
-
[client,
|
|
1418
|
+
[client, gate, followChannel, refresh2]
|
|
1210
1419
|
);
|
|
1211
1420
|
const approve = import_react5.default.useCallback(
|
|
1212
|
-
(toolCallId) =>
|
|
1213
|
-
[
|
|
1421
|
+
(toolCallId, opts) => decide(toolCallId, true, !!opts?.alwaysAllow),
|
|
1422
|
+
[decide]
|
|
1214
1423
|
);
|
|
1215
1424
|
const reject = import_react5.default.useCallback(
|
|
1216
|
-
(toolCallId) =>
|
|
1217
|
-
[
|
|
1425
|
+
(toolCallId) => decide(toolCallId, false, false),
|
|
1426
|
+
[decide]
|
|
1427
|
+
);
|
|
1428
|
+
const answer = import_react5.default.useCallback(
|
|
1429
|
+
async (toolCallId, text) => {
|
|
1430
|
+
const id = chatIdRef.current;
|
|
1431
|
+
if (!id) return false;
|
|
1432
|
+
setError(null);
|
|
1433
|
+
try {
|
|
1434
|
+
const result = await answerPendingInput(client, id, toolCallId, text);
|
|
1435
|
+
gate.next();
|
|
1436
|
+
setPendingInputs((prev) => prev.filter((p) => p.toolCallId !== toolCallId));
|
|
1437
|
+
const channelId = answerResumeChannel(result);
|
|
1438
|
+
if (channelId) followChannel(id, channelId);
|
|
1439
|
+
return true;
|
|
1440
|
+
} catch (e) {
|
|
1441
|
+
setError(e.message);
|
|
1442
|
+
refresh2(id);
|
|
1443
|
+
return false;
|
|
1444
|
+
}
|
|
1445
|
+
},
|
|
1446
|
+
[client, gate, followChannel, refresh2]
|
|
1447
|
+
);
|
|
1448
|
+
const follow = import_react5.default.useCallback(
|
|
1449
|
+
(channelId, opts) => {
|
|
1450
|
+
const id = chatIdRef.current;
|
|
1451
|
+
if (!id) return;
|
|
1452
|
+
followChannel(id, channelId, opts?.lastId);
|
|
1453
|
+
},
|
|
1454
|
+
[followChannel]
|
|
1218
1455
|
);
|
|
1219
1456
|
const stop = import_react5.default.useCallback(() => {
|
|
1220
1457
|
abortRef.current?.abort();
|
|
1221
1458
|
abortRef.current = null;
|
|
1222
1459
|
setStreaming(false);
|
|
1460
|
+
setThinking(stopThinking);
|
|
1223
1461
|
}, []);
|
|
1224
1462
|
const clearError = import_react5.default.useCallback(() => {
|
|
1225
1463
|
setError(null);
|
|
@@ -1231,10 +1469,14 @@ function useChat(agentRef, options) {
|
|
|
1231
1469
|
streaming,
|
|
1232
1470
|
error,
|
|
1233
1471
|
pendingApprovals,
|
|
1472
|
+
pendingInputs,
|
|
1473
|
+
thinking: thinkingShown,
|
|
1234
1474
|
toolStatus,
|
|
1235
1475
|
send,
|
|
1236
1476
|
approve,
|
|
1237
1477
|
reject,
|
|
1478
|
+
answer,
|
|
1479
|
+
follow,
|
|
1238
1480
|
stop,
|
|
1239
1481
|
clearError
|
|
1240
1482
|
};
|
|
@@ -1338,6 +1580,9 @@ function useAuth(options = {}) {
|
|
|
1338
1580
|
SinasPermissionError,
|
|
1339
1581
|
SinasProvider,
|
|
1340
1582
|
SinasTimeoutError,
|
|
1583
|
+
answerPendingInput,
|
|
1584
|
+
answerResumeChannel,
|
|
1585
|
+
approvalResumeChannel,
|
|
1341
1586
|
approveToolCall,
|
|
1342
1587
|
cancelBatch,
|
|
1343
1588
|
checkPermissions,
|
|
@@ -1350,11 +1595,16 @@ function useAuth(options = {}) {
|
|
|
1350
1595
|
getChat,
|
|
1351
1596
|
getInfo,
|
|
1352
1597
|
getMe,
|
|
1598
|
+
invokeAgent,
|
|
1353
1599
|
listBatchExecutions,
|
|
1354
1600
|
listCollections,
|
|
1355
1601
|
listFiles,
|
|
1602
|
+
listPendingInputs,
|
|
1356
1603
|
login,
|
|
1357
1604
|
logout,
|
|
1605
|
+
parsePendingInput,
|
|
1606
|
+
parsePendingInputs,
|
|
1607
|
+
pendingInputFromChunk,
|
|
1358
1608
|
refresh,
|
|
1359
1609
|
sendMessage,
|
|
1360
1610
|
stateDelete,
|