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