@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.js
CHANGED
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
SinasNotFoundError,
|
|
7
7
|
SinasPermissionError,
|
|
8
8
|
SinasTimeoutError,
|
|
9
|
+
answerPendingInput,
|
|
10
|
+
answerResumeChannel,
|
|
11
|
+
approvalResumeChannel,
|
|
9
12
|
approveToolCall,
|
|
10
13
|
cancelBatch,
|
|
11
14
|
checkPermissions,
|
|
@@ -18,11 +21,17 @@ import {
|
|
|
18
21
|
getChat,
|
|
19
22
|
getInfo,
|
|
20
23
|
getMe,
|
|
24
|
+
invokeAgent,
|
|
21
25
|
listBatchExecutions,
|
|
22
26
|
listCollections,
|
|
23
27
|
listFiles,
|
|
28
|
+
listPendingInputs,
|
|
24
29
|
login,
|
|
25
30
|
logout,
|
|
31
|
+
parseApproval,
|
|
32
|
+
parsePendingInput,
|
|
33
|
+
parsePendingInputs,
|
|
34
|
+
pendingInputFromChunk,
|
|
26
35
|
refresh,
|
|
27
36
|
sendMessage,
|
|
28
37
|
stateDelete,
|
|
@@ -35,7 +44,7 @@ import {
|
|
|
35
44
|
submitFunctionBatch,
|
|
36
45
|
uploadFile,
|
|
37
46
|
verifyOTP
|
|
38
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-RHQVV5O6.js";
|
|
39
48
|
|
|
40
49
|
// src/hooks/useQuery.ts
|
|
41
50
|
import React2 from "react";
|
|
@@ -218,29 +227,172 @@ function useStateStore(ns, key) {
|
|
|
218
227
|
|
|
219
228
|
// src/hooks/useChat.ts
|
|
220
229
|
import React5 from "react";
|
|
230
|
+
|
|
231
|
+
// src/hooks/chatState.ts
|
|
232
|
+
function parseMessageChunk(data) {
|
|
233
|
+
let parsed;
|
|
234
|
+
try {
|
|
235
|
+
parsed = JSON.parse(data);
|
|
236
|
+
} catch {
|
|
237
|
+
return { text: data };
|
|
238
|
+
}
|
|
239
|
+
if (!parsed || typeof parsed !== "object") return { text: "" };
|
|
240
|
+
const p = parsed;
|
|
241
|
+
const out = { text: typeof p.content === "string" ? p.content : "" };
|
|
242
|
+
if (typeof p.thinking === "string") out.thinking = p.thinking;
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
function applyTextDelta(messages, text, chatId, now = /* @__PURE__ */ new Date()) {
|
|
246
|
+
if (!text) return messages;
|
|
247
|
+
const last = messages[messages.length - 1];
|
|
248
|
+
if (last && last.role === "assistant" && !last.toolCalls && last.streaming) {
|
|
249
|
+
const next = [...messages];
|
|
250
|
+
next[next.length - 1] = { ...last, content: (last.content || "") + text };
|
|
251
|
+
return next;
|
|
252
|
+
}
|
|
253
|
+
return [
|
|
254
|
+
...messages,
|
|
255
|
+
{
|
|
256
|
+
id: `tmp-text-${now.getTime()}`,
|
|
257
|
+
chatId,
|
|
258
|
+
role: "assistant",
|
|
259
|
+
content: text,
|
|
260
|
+
toolCalls: null,
|
|
261
|
+
toolCallId: null,
|
|
262
|
+
name: null,
|
|
263
|
+
createdAt: now.toISOString(),
|
|
264
|
+
streaming: true
|
|
265
|
+
}
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
function applyToolStart(messages, event, chatId, now = /* @__PURE__ */ new Date()) {
|
|
269
|
+
if (messages.some((m) => m.toolCalls?.some((tc) => tc.id === event.tool_call_id))) return messages;
|
|
270
|
+
const frozen = messages.map(
|
|
271
|
+
(m, i) => i === messages.length - 1 && m.streaming && m.role === "assistant" && !m.toolCalls ? { ...m, streaming: false } : m
|
|
272
|
+
);
|
|
273
|
+
return [
|
|
274
|
+
...frozen,
|
|
275
|
+
{
|
|
276
|
+
id: `tmp-tc-${event.tool_call_id}`,
|
|
277
|
+
chatId,
|
|
278
|
+
role: "assistant",
|
|
279
|
+
content: null,
|
|
280
|
+
toolCalls: [
|
|
281
|
+
{
|
|
282
|
+
id: event.tool_call_id,
|
|
283
|
+
type: "function",
|
|
284
|
+
function: { name: event.name, arguments: event.arguments || "{}" },
|
|
285
|
+
description: event.description
|
|
286
|
+
}
|
|
287
|
+
],
|
|
288
|
+
toolCallId: null,
|
|
289
|
+
name: null,
|
|
290
|
+
createdAt: now.toISOString(),
|
|
291
|
+
streaming: true
|
|
292
|
+
}
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
function applyToolEnd(messages, event) {
|
|
296
|
+
if (event.result === void 0) return messages;
|
|
297
|
+
const content = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
|
|
298
|
+
return messages.map(
|
|
299
|
+
(m) => m.toolCalls?.some((tc) => tc.id === event.tool_call_id) ? { ...m, toolResults: { ...m.toolResults, [event.tool_call_id]: { content, name: event.name } } } : m
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
function addApproval(current, approval) {
|
|
303
|
+
return current.some((a) => a.toolCallId === approval.toolCallId) ? current : [...current, approval];
|
|
304
|
+
}
|
|
305
|
+
function mergePendingInputs(current, incoming) {
|
|
306
|
+
const seen = new Set(current.map((p) => p.toolCallId));
|
|
307
|
+
const added = incoming.filter((p) => !seen.has(p.toolCallId) && seen.add(p.toolCallId));
|
|
308
|
+
return added.length ? [...current, ...added] : current;
|
|
309
|
+
}
|
|
310
|
+
var NOT_THINKING = { active: false, rounds: [] };
|
|
311
|
+
function applyThinking(state, thinking) {
|
|
312
|
+
if (!state.active) return { active: true, rounds: [...state.rounds, thinking] };
|
|
313
|
+
if (!thinking) return state;
|
|
314
|
+
const rounds = [...state.rounds];
|
|
315
|
+
rounds[rounds.length - 1] += thinking;
|
|
316
|
+
return { active: true, rounds };
|
|
317
|
+
}
|
|
318
|
+
function stopThinking(state) {
|
|
319
|
+
return state.active ? { ...state, active: false } : state;
|
|
320
|
+
}
|
|
321
|
+
function thinkingView(state) {
|
|
322
|
+
return { active: state.active, text: state.rounds.filter((r) => r).join("\n\n") };
|
|
323
|
+
}
|
|
324
|
+
function createLatestGate() {
|
|
325
|
+
let latest = 0;
|
|
326
|
+
return {
|
|
327
|
+
next: () => ++latest,
|
|
328
|
+
isCurrent: (ticket) => ticket === latest
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
var PENDING_POLL_MAX_MS = 3e4;
|
|
332
|
+
var PENDING_POLL_MIN_MS = 1e3;
|
|
333
|
+
var EXPIRY_GRACE_MS = 5e3;
|
|
334
|
+
var OVERDUE_POLL_MS = 1e4;
|
|
335
|
+
function nextPendingPollMs(inputs, nowMs) {
|
|
336
|
+
let wait = PENDING_POLL_MAX_MS;
|
|
337
|
+
for (const input of inputs) {
|
|
338
|
+
const deadline = input.expiresAt ? Date.parse(input.expiresAt) : NaN;
|
|
339
|
+
if (Number.isNaN(deadline)) continue;
|
|
340
|
+
const untilDue = deadline + EXPIRY_GRACE_MS - nowMs;
|
|
341
|
+
wait = Math.min(wait, untilDue > 0 ? untilDue : OVERDUE_POLL_MS);
|
|
342
|
+
}
|
|
343
|
+
return Math.max(PENDING_POLL_MIN_MS, wait);
|
|
344
|
+
}
|
|
345
|
+
var TRAILING_POLLS_MS = [5e3, 15e3, 3e4];
|
|
346
|
+
|
|
347
|
+
// src/hooks/useChat.ts
|
|
221
348
|
function useChat(agentRef, options) {
|
|
222
349
|
const client = useClient();
|
|
350
|
+
const includeThinking = !!options?.includeThinking;
|
|
351
|
+
const pollPendingInputs = options?.pollPendingInputs !== false;
|
|
223
352
|
const [chatId, setChatId] = React5.useState(options?.chatId ?? null);
|
|
224
353
|
const [messages, setMessages] = React5.useState([]);
|
|
225
354
|
const [loading, setLoading] = React5.useState(false);
|
|
226
355
|
const [streaming, setStreaming] = React5.useState(false);
|
|
227
356
|
const [error, setError] = React5.useState(null);
|
|
228
357
|
const [pendingApprovals, setPendingApprovals] = React5.useState([]);
|
|
358
|
+
const [pendingInputs, setPendingInputs] = React5.useState([]);
|
|
359
|
+
const [thinking, setThinking] = React5.useState(NOT_THINKING);
|
|
360
|
+
const thinkingShown = React5.useMemo(() => thinkingView(thinking), [thinking]);
|
|
229
361
|
const [toolStatus, setToolStatus] = React5.useState([]);
|
|
362
|
+
const [trailingPoll, setTrailingPoll] = React5.useState(TRAILING_POLLS_MS.length);
|
|
363
|
+
const [pollTick, setPollTick] = React5.useState(0);
|
|
230
364
|
const abortRef = React5.useRef(null);
|
|
231
365
|
const chatIdRef = React5.useRef(chatId);
|
|
232
366
|
chatIdRef.current = chatId;
|
|
367
|
+
const [gate] = React5.useState(createLatestGate);
|
|
368
|
+
const refresh2 = React5.useCallback(
|
|
369
|
+
async (id) => {
|
|
370
|
+
const ticket = gate.next();
|
|
371
|
+
try {
|
|
372
|
+
const detail = await getChat(client, id);
|
|
373
|
+
if (!gate.isCurrent(ticket)) return null;
|
|
374
|
+
setMessages(detail.messages);
|
|
375
|
+
setPendingApprovals(detail.pendingApprovals ?? []);
|
|
376
|
+
const inputs = detail.pendingInputs ?? [];
|
|
377
|
+
setPendingInputs(inputs);
|
|
378
|
+
return inputs;
|
|
379
|
+
} catch {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
[client, gate]
|
|
384
|
+
);
|
|
233
385
|
React5.useEffect(() => {
|
|
234
386
|
if (!options?.chatId) return;
|
|
235
387
|
let cancelled = false;
|
|
388
|
+
const ticket = gate.next();
|
|
236
389
|
setLoading(true);
|
|
237
390
|
getChat(client, options.chatId).then((detail) => {
|
|
238
|
-
if (cancelled) return;
|
|
391
|
+
if (cancelled || !gate.isCurrent(ticket)) return;
|
|
239
392
|
setChatId(detail.id);
|
|
240
393
|
setMessages(detail.messages);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
394
|
+
setPendingApprovals(detail.pendingApprovals ?? []);
|
|
395
|
+
setPendingInputs(detail.pendingInputs ?? []);
|
|
244
396
|
}).catch((e) => {
|
|
245
397
|
if (cancelled) return;
|
|
246
398
|
setError(e.message);
|
|
@@ -250,74 +402,52 @@ function useChat(agentRef, options) {
|
|
|
250
402
|
return () => {
|
|
251
403
|
cancelled = true;
|
|
252
404
|
};
|
|
253
|
-
}, [client, options?.chatId]);
|
|
405
|
+
}, [client, gate, options?.chatId]);
|
|
254
406
|
React5.useEffect(() => {
|
|
255
407
|
return () => {
|
|
256
408
|
abortRef.current?.abort();
|
|
257
409
|
};
|
|
258
410
|
}, []);
|
|
259
|
-
|
|
411
|
+
React5.useEffect(() => {
|
|
412
|
+
if (!pollPendingInputs || streaming || !chatId) return;
|
|
413
|
+
let delay = null;
|
|
414
|
+
if (pendingInputs.length > 0) delay = nextPendingPollMs(pendingInputs, Date.now());
|
|
415
|
+
else if (trailingPoll < TRAILING_POLLS_MS.length) delay = TRAILING_POLLS_MS[trailingPoll];
|
|
416
|
+
if (delay === null) return;
|
|
417
|
+
const waiting = pendingInputs.map((p) => p.toolCallId);
|
|
418
|
+
const timer = setTimeout(async () => {
|
|
419
|
+
const now = await refresh2(chatId);
|
|
420
|
+
if (waiting.length === 0) setTrailingPoll((i) => i + 1);
|
|
421
|
+
else if (now && waiting.some((id) => !now.some((p) => p.toolCallId === id))) setTrailingPoll(0);
|
|
422
|
+
setPollTick((t) => t + 1);
|
|
423
|
+
}, delay);
|
|
424
|
+
return () => clearTimeout(timer);
|
|
425
|
+
}, [pollPendingInputs, chatId, streaming, pendingInputs, trailingPoll, pollTick, refresh2]);
|
|
426
|
+
const handleStreamChunk = React5.useCallback(
|
|
260
427
|
(chunk) => {
|
|
428
|
+
const asked = pendingInputFromChunk(chunk);
|
|
429
|
+
if (asked) {
|
|
430
|
+
setThinking(stopThinking);
|
|
431
|
+
setPendingInputs((prev) => mergePendingInputs(prev, [asked]));
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const chatIdNow = chatIdRef.current || "";
|
|
261
435
|
if (chunk.event === "message") {
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
})();
|
|
270
|
-
if (!newContent) return;
|
|
271
|
-
setMessages((prev) => {
|
|
272
|
-
const last = prev[prev.length - 1];
|
|
273
|
-
if (last && last.role === "assistant" && !last.toolCalls && last.streaming) {
|
|
274
|
-
const next = [...prev];
|
|
275
|
-
next[next.length - 1] = { ...last, content: (last.content || "") + newContent };
|
|
276
|
-
return next;
|
|
277
|
-
}
|
|
278
|
-
return [...prev, {
|
|
279
|
-
id: `tmp-text-${Date.now()}`,
|
|
280
|
-
chatId: chatIdRef.current || "",
|
|
281
|
-
role: "assistant",
|
|
282
|
-
content: newContent,
|
|
283
|
-
toolCalls: null,
|
|
284
|
-
toolCallId: null,
|
|
285
|
-
name: null,
|
|
286
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
287
|
-
streaming: true
|
|
288
|
-
}];
|
|
289
|
-
});
|
|
436
|
+
const { text, thinking: thought } = parseMessageChunk(chunk.data);
|
|
437
|
+
if (thought !== void 0) setThinking((prev) => applyThinking(prev, thought));
|
|
438
|
+
if (text) {
|
|
439
|
+
setThinking(stopThinking);
|
|
440
|
+
setMessages((prev) => applyTextDelta(prev, text, chatIdNow));
|
|
441
|
+
}
|
|
290
442
|
} else if (chunk.event === "tool_start") {
|
|
443
|
+
setThinking(stopThinking);
|
|
291
444
|
try {
|
|
292
445
|
const parsed = JSON.parse(chunk.data);
|
|
293
446
|
setToolStatus((prev) => [
|
|
294
447
|
...prev,
|
|
295
448
|
{ toolCallId: parsed.tool_call_id, name: parsed.name, description: parsed.description, status: "running" }
|
|
296
449
|
]);
|
|
297
|
-
setMessages((prev) =>
|
|
298
|
-
if (prev.some((m) => m.toolCalls?.some((tc) => tc.id === parsed.tool_call_id))) {
|
|
299
|
-
return prev;
|
|
300
|
-
}
|
|
301
|
-
const next = prev.map(
|
|
302
|
-
(m, i) => i === prev.length - 1 && m.streaming && m.role === "assistant" && !m.toolCalls ? { ...m, streaming: false } : m
|
|
303
|
-
);
|
|
304
|
-
return [...next, {
|
|
305
|
-
id: `tmp-tc-${parsed.tool_call_id}`,
|
|
306
|
-
chatId: chatIdRef.current || "",
|
|
307
|
-
role: "assistant",
|
|
308
|
-
content: null,
|
|
309
|
-
toolCalls: [{
|
|
310
|
-
id: parsed.tool_call_id,
|
|
311
|
-
type: "function",
|
|
312
|
-
function: { name: parsed.name, arguments: parsed.arguments || "{}" },
|
|
313
|
-
description: parsed.description
|
|
314
|
-
}],
|
|
315
|
-
toolCallId: null,
|
|
316
|
-
name: null,
|
|
317
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
318
|
-
streaming: true
|
|
319
|
-
}];
|
|
320
|
-
});
|
|
450
|
+
setMessages((prev) => applyToolStart(prev, parsed, chatIdNow));
|
|
321
451
|
} catch {
|
|
322
452
|
}
|
|
323
453
|
} else if (chunk.event === "tool_end") {
|
|
@@ -326,39 +456,18 @@ function useChat(agentRef, options) {
|
|
|
326
456
|
setToolStatus(
|
|
327
457
|
(prev) => prev.map((t) => t.toolCallId === parsed.tool_call_id ? { ...t, status: "complete" } : t)
|
|
328
458
|
);
|
|
329
|
-
|
|
330
|
-
const resultContent = typeof parsed.result === "string" ? parsed.result : JSON.stringify(parsed.result);
|
|
331
|
-
setMessages(
|
|
332
|
-
(prev) => prev.map((m) => {
|
|
333
|
-
if (m.toolCalls?.some((tc) => tc.id === parsed.tool_call_id)) {
|
|
334
|
-
return {
|
|
335
|
-
...m,
|
|
336
|
-
toolResults: {
|
|
337
|
-
...m.toolResults,
|
|
338
|
-
[parsed.tool_call_id]: { content: resultContent, name: parsed.name }
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
return m;
|
|
343
|
-
})
|
|
344
|
-
);
|
|
345
|
-
}
|
|
459
|
+
setMessages((prev) => applyToolEnd(prev, parsed));
|
|
346
460
|
} catch {
|
|
347
461
|
}
|
|
348
462
|
} else if (chunk.event === "approval_required") {
|
|
463
|
+
setThinking(stopThinking);
|
|
349
464
|
try {
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
type: raw.type,
|
|
353
|
-
toolCallId: raw.tool_call_id,
|
|
354
|
-
functionNamespace: raw.function_namespace,
|
|
355
|
-
functionName: raw.function_name,
|
|
356
|
-
arguments: raw.arguments
|
|
357
|
-
};
|
|
358
|
-
setPendingApprovals((prev) => [...prev, parsed]);
|
|
465
|
+
const approval = parseApproval(JSON.parse(chunk.data));
|
|
466
|
+
if (approval) setPendingApprovals((prev) => addApproval(prev, approval));
|
|
359
467
|
} catch {
|
|
360
468
|
}
|
|
361
469
|
} else if (chunk.event === "error") {
|
|
470
|
+
setThinking(stopThinking);
|
|
362
471
|
try {
|
|
363
472
|
const parsed = JSON.parse(chunk.data);
|
|
364
473
|
setError(parsed.error || "Stream error");
|
|
@@ -369,18 +478,37 @@ function useChat(agentRef, options) {
|
|
|
369
478
|
},
|
|
370
479
|
[]
|
|
371
480
|
);
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
481
|
+
const streamCallbacks = React5.useCallback(
|
|
482
|
+
(id) => [
|
|
483
|
+
(_done) => {
|
|
484
|
+
setStreaming(false);
|
|
485
|
+
setToolStatus([]);
|
|
486
|
+
setThinking(stopThinking);
|
|
487
|
+
refresh2(id);
|
|
488
|
+
},
|
|
489
|
+
(err) => {
|
|
490
|
+
setError(err.message);
|
|
491
|
+
setStreaming(false);
|
|
492
|
+
setToolStatus([]);
|
|
493
|
+
setThinking(stopThinking);
|
|
381
494
|
}
|
|
495
|
+
],
|
|
496
|
+
[refresh2]
|
|
497
|
+
);
|
|
498
|
+
const followChannel = React5.useCallback(
|
|
499
|
+
(id, channelId, lastId) => {
|
|
500
|
+
abortRef.current?.abort();
|
|
501
|
+
gate.next();
|
|
502
|
+
setStreaming(true);
|
|
503
|
+
setError(null);
|
|
504
|
+
setThinking(NOT_THINKING);
|
|
505
|
+
const [onDone, onError] = streamCallbacks(id);
|
|
506
|
+
abortRef.current = streamFromChannel(client, id, channelId, handleStreamChunk, onDone, onError, {
|
|
507
|
+
includeThinking,
|
|
508
|
+
lastId
|
|
509
|
+
});
|
|
382
510
|
},
|
|
383
|
-
[client]
|
|
511
|
+
[client, gate, includeThinking, handleStreamChunk, streamCallbacks]
|
|
384
512
|
);
|
|
385
513
|
const send = React5.useCallback(
|
|
386
514
|
(content) => {
|
|
@@ -402,10 +530,12 @@ function useChat(agentRef, options) {
|
|
|
402
530
|
setLoading(false);
|
|
403
531
|
}
|
|
404
532
|
abortRef.current?.abort();
|
|
533
|
+
gate.next();
|
|
405
534
|
setStreaming(true);
|
|
406
535
|
setError(null);
|
|
407
536
|
setPendingApprovals([]);
|
|
408
537
|
setToolStatus([]);
|
|
538
|
+
setThinking(NOT_THINKING);
|
|
409
539
|
const displayContent = typeof content === "string" ? content : JSON.stringify(content);
|
|
410
540
|
const userMsg = {
|
|
411
541
|
id: `tmp-user-${Date.now()}`,
|
|
@@ -418,80 +548,77 @@ function useChat(agentRef, options) {
|
|
|
418
548
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
419
549
|
};
|
|
420
550
|
setMessages((prev) => [...prev, userMsg]);
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
handleStreamChunks,
|
|
426
|
-
() => {
|
|
427
|
-
setStreaming(false);
|
|
428
|
-
setToolStatus([]);
|
|
429
|
-
refreshMessages(activeChatId);
|
|
430
|
-
},
|
|
431
|
-
(err) => {
|
|
432
|
-
setError(err.message);
|
|
433
|
-
setStreaming(false);
|
|
434
|
-
setToolStatus([]);
|
|
435
|
-
}
|
|
436
|
-
);
|
|
437
|
-
abortRef.current = controller;
|
|
551
|
+
const [onDone, onError] = streamCallbacks(activeChatId);
|
|
552
|
+
abortRef.current = streamMessage(client, activeChatId, content, handleStreamChunk, onDone, onError, {
|
|
553
|
+
includeThinking
|
|
554
|
+
});
|
|
438
555
|
};
|
|
439
556
|
doSend();
|
|
440
557
|
},
|
|
441
|
-
[client, agentRef, options?.input,
|
|
558
|
+
[client, agentRef, options?.input, gate, includeThinking, handleStreamChunk, streamCallbacks]
|
|
442
559
|
);
|
|
443
|
-
const
|
|
444
|
-
(toolCallId, approved) => {
|
|
445
|
-
const
|
|
446
|
-
if (!
|
|
560
|
+
const decide = React5.useCallback(
|
|
561
|
+
(toolCallId, approved, alwaysAllow) => {
|
|
562
|
+
const id = chatIdRef.current;
|
|
563
|
+
if (!id) return;
|
|
564
|
+
gate.next();
|
|
447
565
|
setPendingApprovals((prev) => prev.filter((a) => a.toolCallId !== toolCallId));
|
|
448
|
-
setStreaming(true);
|
|
449
566
|
setError(null);
|
|
450
567
|
(async () => {
|
|
451
568
|
try {
|
|
452
|
-
const result = await approveToolCall(client,
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
const controller = streamFromChannel(
|
|
459
|
-
client,
|
|
460
|
-
currentChatId,
|
|
461
|
-
result.channelId,
|
|
462
|
-
handleStreamChunks,
|
|
463
|
-
() => {
|
|
464
|
-
setStreaming(false);
|
|
465
|
-
setToolStatus([]);
|
|
466
|
-
refreshMessages(currentChatId);
|
|
467
|
-
},
|
|
468
|
-
(err) => {
|
|
469
|
-
setError(err.message);
|
|
470
|
-
setStreaming(false);
|
|
471
|
-
setToolStatus([]);
|
|
472
|
-
}
|
|
473
|
-
);
|
|
474
|
-
abortRef.current = controller;
|
|
569
|
+
const result = await approveToolCall(client, id, toolCallId, approved, { alwaysAllow });
|
|
570
|
+
const channelId = approvalResumeChannel(result, approved);
|
|
571
|
+
if (channelId) followChannel(id, channelId);
|
|
572
|
+
else if (result.resume === void 0) refresh2(id);
|
|
475
573
|
} catch (e) {
|
|
476
574
|
setError(e.message);
|
|
477
|
-
|
|
575
|
+
refresh2(id);
|
|
478
576
|
}
|
|
479
577
|
})();
|
|
480
578
|
},
|
|
481
|
-
[client,
|
|
579
|
+
[client, gate, followChannel, refresh2]
|
|
482
580
|
);
|
|
483
581
|
const approve = React5.useCallback(
|
|
484
|
-
(toolCallId) =>
|
|
485
|
-
[
|
|
582
|
+
(toolCallId, opts) => decide(toolCallId, true, !!opts?.alwaysAllow),
|
|
583
|
+
[decide]
|
|
486
584
|
);
|
|
487
585
|
const reject = React5.useCallback(
|
|
488
|
-
(toolCallId) =>
|
|
489
|
-
[
|
|
586
|
+
(toolCallId) => decide(toolCallId, false, false),
|
|
587
|
+
[decide]
|
|
588
|
+
);
|
|
589
|
+
const answer = React5.useCallback(
|
|
590
|
+
async (toolCallId, text) => {
|
|
591
|
+
const id = chatIdRef.current;
|
|
592
|
+
if (!id) return false;
|
|
593
|
+
setError(null);
|
|
594
|
+
try {
|
|
595
|
+
const result = await answerPendingInput(client, id, toolCallId, text);
|
|
596
|
+
gate.next();
|
|
597
|
+
setPendingInputs((prev) => prev.filter((p) => p.toolCallId !== toolCallId));
|
|
598
|
+
const channelId = answerResumeChannel(result);
|
|
599
|
+
if (channelId) followChannel(id, channelId);
|
|
600
|
+
return true;
|
|
601
|
+
} catch (e) {
|
|
602
|
+
setError(e.message);
|
|
603
|
+
refresh2(id);
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
},
|
|
607
|
+
[client, gate, followChannel, refresh2]
|
|
608
|
+
);
|
|
609
|
+
const follow = React5.useCallback(
|
|
610
|
+
(channelId, opts) => {
|
|
611
|
+
const id = chatIdRef.current;
|
|
612
|
+
if (!id) return;
|
|
613
|
+
followChannel(id, channelId, opts?.lastId);
|
|
614
|
+
},
|
|
615
|
+
[followChannel]
|
|
490
616
|
);
|
|
491
617
|
const stop = React5.useCallback(() => {
|
|
492
618
|
abortRef.current?.abort();
|
|
493
619
|
abortRef.current = null;
|
|
494
620
|
setStreaming(false);
|
|
621
|
+
setThinking(stopThinking);
|
|
495
622
|
}, []);
|
|
496
623
|
const clearError = React5.useCallback(() => {
|
|
497
624
|
setError(null);
|
|
@@ -503,10 +630,14 @@ function useChat(agentRef, options) {
|
|
|
503
630
|
streaming,
|
|
504
631
|
error,
|
|
505
632
|
pendingApprovals,
|
|
633
|
+
pendingInputs,
|
|
634
|
+
thinking: thinkingShown,
|
|
506
635
|
toolStatus,
|
|
507
636
|
send,
|
|
508
637
|
approve,
|
|
509
638
|
reject,
|
|
639
|
+
answer,
|
|
640
|
+
follow,
|
|
510
641
|
stop,
|
|
511
642
|
clearError
|
|
512
643
|
};
|
|
@@ -609,6 +740,9 @@ export {
|
|
|
609
740
|
SinasPermissionError,
|
|
610
741
|
SinasProvider,
|
|
611
742
|
SinasTimeoutError,
|
|
743
|
+
answerPendingInput,
|
|
744
|
+
answerResumeChannel,
|
|
745
|
+
approvalResumeChannel,
|
|
612
746
|
approveToolCall,
|
|
613
747
|
cancelBatch,
|
|
614
748
|
checkPermissions,
|
|
@@ -621,11 +755,16 @@ export {
|
|
|
621
755
|
getChat,
|
|
622
756
|
getInfo,
|
|
623
757
|
getMe,
|
|
758
|
+
invokeAgent,
|
|
624
759
|
listBatchExecutions,
|
|
625
760
|
listCollections,
|
|
626
761
|
listFiles,
|
|
762
|
+
listPendingInputs,
|
|
627
763
|
login,
|
|
628
764
|
logout,
|
|
765
|
+
parsePendingInput,
|
|
766
|
+
parsePendingInputs,
|
|
767
|
+
pendingInputFromChunk,
|
|
629
768
|
refresh,
|
|
630
769
|
sendMessage,
|
|
631
770
|
stateDelete,
|