@sinas/sdk 0.1.1 → 0.2.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/client.js CHANGED
@@ -5,16 +5,21 @@ import {
5
5
  SinasNotFoundError,
6
6
  SinasPermissionError,
7
7
  SinasTimeoutError,
8
+ approveToolCall,
8
9
  createChat,
9
10
  executeFunction,
10
11
  executeQuery,
12
+ getChat,
13
+ listCollections,
14
+ listFiles,
11
15
  sendMessage,
12
16
  stateDelete,
13
17
  stateGet,
14
18
  stateList,
15
19
  stateSet,
20
+ streamFromChannel,
16
21
  streamMessage
17
- } from "./chunk-M7YHE5Y6.js";
22
+ } from "./chunk-EV5ARSUJ.js";
18
23
  export {
19
24
  SinasAuthError,
20
25
  SinasClient,
@@ -22,14 +27,19 @@ export {
22
27
  SinasNotFoundError,
23
28
  SinasPermissionError,
24
29
  SinasTimeoutError,
30
+ approveToolCall,
25
31
  createChat,
26
32
  executeFunction,
27
33
  executeQuery,
34
+ getChat,
35
+ listCollections,
36
+ listFiles,
28
37
  sendMessage,
29
38
  stateDelete,
30
39
  stateGet,
31
40
  stateList,
32
41
  stateSet,
42
+ streamFromChannel,
33
43
  streamMessage
34
44
  };
35
45
  //# sourceMappingURL=client.js.map
package/dist/index.cjs CHANGED
@@ -37,14 +37,19 @@ __export(src_exports, {
37
37
  SinasPermissionError: () => SinasPermissionError,
38
38
  SinasProvider: () => SinasProvider,
39
39
  SinasTimeoutError: () => SinasTimeoutError,
40
+ approveToolCall: () => approveToolCall,
40
41
  createChat: () => createChat,
41
42
  executeFunction: () => executeFunction,
42
43
  executeQuery: () => executeQuery,
44
+ getChat: () => getChat,
45
+ listCollections: () => listCollections,
46
+ listFiles: () => listFiles,
43
47
  sendMessage: () => sendMessage,
44
48
  stateDelete: () => stateDelete,
45
49
  stateGet: () => stateGet,
46
50
  stateList: () => stateList,
47
51
  stateSet: () => stateSet,
52
+ streamFromChannel: () => streamFromChannel,
48
53
  streamMessage: () => streamMessage,
49
54
  useChat: () => useChat,
50
55
  useExecute: () => useExecute,
@@ -268,6 +273,88 @@ async function sendMessage(client, chatId, content) {
268
273
  createdAt: data.created_at
269
274
  };
270
275
  }
276
+ function mapMessage(data) {
277
+ const toolCalls = data.tool_calls;
278
+ return {
279
+ id: data.id,
280
+ chatId: data.chat_id,
281
+ role: data.role,
282
+ content: data.content ?? null,
283
+ toolCalls: toolCalls ?? null,
284
+ toolCallId: data.tool_call_id ?? null,
285
+ name: data.name ?? null,
286
+ createdAt: data.created_at
287
+ };
288
+ }
289
+ async function getChat(client, chatId) {
290
+ const config = client.getConfig();
291
+ const res = await client.fetch(`${config.apiBase}/chats/${chatId}`);
292
+ const data = await res.json();
293
+ return {
294
+ id: data.id,
295
+ agentNamespace: data.agent_namespace,
296
+ agentName: data.agent_name,
297
+ title: data.title,
298
+ messages: (data.messages || []).map(mapMessage)
299
+ };
300
+ }
301
+ async function approveToolCall(client, chatId, toolCallId, approved) {
302
+ const config = client.getConfig();
303
+ const res = await client.fetch(
304
+ `${config.apiBase}/chats/${chatId}/approve-tool/${toolCallId}`,
305
+ { method: "POST", body: JSON.stringify({ approved }) }
306
+ );
307
+ const data = await res.json();
308
+ return {
309
+ status: data.status,
310
+ toolCallId: data.tool_call_id,
311
+ channelId: data.channel_id
312
+ };
313
+ }
314
+ function streamFromChannel(client, chatId, channelId, onChunk, onDone, onError) {
315
+ const controller = new AbortController();
316
+ const config = client.getConfig();
317
+ (async () => {
318
+ try {
319
+ const res = await fetch(
320
+ `${config.apiBase}/chats/${chatId}/stream/${channelId}?last_id=0`,
321
+ { headers: client.getAuthHeaders(), signal: controller.signal }
322
+ );
323
+ if (!res.ok) {
324
+ const body = await res.json().catch(() => ({ detail: res.statusText }));
325
+ throw new Error(body.detail || "Stream reconnect failed");
326
+ }
327
+ const reader = res.body.getReader();
328
+ const decoder = new TextDecoder();
329
+ let buffer = "";
330
+ while (true) {
331
+ const { done, value } = await reader.read();
332
+ if (done) break;
333
+ buffer += decoder.decode(value, { stream: true });
334
+ const { chunks, remaining } = parseSSE(buffer);
335
+ buffer = remaining;
336
+ for (const chunk of chunks) {
337
+ onChunk(chunk);
338
+ if (chunk.event === "done") {
339
+ onDone?.();
340
+ return;
341
+ }
342
+ }
343
+ }
344
+ if (buffer.trim()) {
345
+ const { chunks } = parseSSE(buffer + "\n\n");
346
+ for (const chunk of chunks) {
347
+ onChunk(chunk);
348
+ }
349
+ }
350
+ onDone?.();
351
+ } catch (err) {
352
+ if (err.name === "AbortError") return;
353
+ onError?.(err);
354
+ }
355
+ })();
356
+ return controller;
357
+ }
271
358
  function parseSSE(buffer) {
272
359
  const chunks = [];
273
360
  const lines = buffer.split("\n");
@@ -341,6 +428,18 @@ function streamMessage(client, chatId, content, onChunk, onDone, onError) {
341
428
  return controller;
342
429
  }
343
430
 
431
+ // src/client/collections.ts
432
+ async function listCollections(client) {
433
+ const base = client.getProxyBase();
434
+ const res = await client.fetch(`${base}/collections`);
435
+ return res.json();
436
+ }
437
+ async function listFiles(client, namespace, collection) {
438
+ const base = client.getProxyBase();
439
+ const res = await client.fetch(`${base}/files/${namespace}/${collection}`);
440
+ return res.json();
441
+ }
442
+
344
443
  // src/hooks/useQuery.ts
345
444
  var import_react2 = __toESM(require("react"), 1);
346
445
 
@@ -516,125 +615,235 @@ function useStateStore(ns, key) {
516
615
 
517
616
  // src/hooks/useChat.ts
518
617
  var import_react5 = __toESM(require("react"), 1);
519
- function useChat(agentRef) {
618
+ function useChat(agentRef, options) {
520
619
  const client = useClient();
521
- const [chatId, setChatId] = import_react5.default.useState(null);
620
+ const [chatId, setChatId] = import_react5.default.useState(options?.chatId ?? null);
522
621
  const [messages, setMessages] = import_react5.default.useState([]);
523
622
  const [loading, setLoading] = import_react5.default.useState(false);
524
623
  const [streaming, setStreaming] = import_react5.default.useState(false);
525
624
  const [error, setError] = import_react5.default.useState(null);
625
+ const [pendingApprovals, setPendingApprovals] = import_react5.default.useState([]);
526
626
  const abortRef = import_react5.default.useRef(null);
627
+ const chatIdRef = import_react5.default.useRef(chatId);
628
+ chatIdRef.current = chatId;
629
+ import_react5.default.useEffect(() => {
630
+ if (!options?.chatId) return;
631
+ let cancelled = false;
632
+ setLoading(true);
633
+ getChat(client, options.chatId).then((detail) => {
634
+ if (cancelled) return;
635
+ setChatId(detail.id);
636
+ setMessages(detail.messages);
637
+ }).catch((e) => {
638
+ if (cancelled) return;
639
+ setError(e.message);
640
+ }).finally(() => {
641
+ if (!cancelled) setLoading(false);
642
+ });
643
+ return () => {
644
+ cancelled = true;
645
+ };
646
+ }, [client, options?.chatId]);
527
647
  import_react5.default.useEffect(() => {
528
648
  return () => {
529
649
  abortRef.current?.abort();
530
650
  };
531
651
  }, []);
532
- const doCreateChat = import_react5.default.useCallback(
533
- async (input) => {
534
- setLoading(true);
535
- setError(null);
536
- try {
537
- const result = await createChat(client, agentRef, input);
538
- setChatId(result.id);
539
- setMessages([]);
540
- return result.id;
541
- } catch (e) {
542
- setError(e.message);
543
- } finally {
544
- setLoading(false);
652
+ const handleStreamChunks = import_react5.default.useCallback(
653
+ (assistantId) => (chunk) => {
654
+ if (chunk.event === "message") {
655
+ try {
656
+ const parsed = JSON.parse(chunk.data);
657
+ if (parsed.content) {
658
+ setMessages(
659
+ (prev) => prev.map(
660
+ (m) => m.id === assistantId ? { ...m, content: (m.content || "") + parsed.content } : m
661
+ )
662
+ );
663
+ }
664
+ } catch {
665
+ setMessages(
666
+ (prev) => prev.map(
667
+ (m) => m.id === assistantId ? { ...m, content: (m.content || "") + chunk.data } : m
668
+ )
669
+ );
670
+ }
671
+ } else if (chunk.event === "approval_required") {
672
+ try {
673
+ const parsed = JSON.parse(chunk.data);
674
+ setPendingApprovals((prev) => [...prev, parsed]);
675
+ } catch {
676
+ }
677
+ } else if (chunk.event === "error") {
678
+ try {
679
+ const parsed = JSON.parse(chunk.data);
680
+ setError(parsed.error || "Stream error");
681
+ } catch {
682
+ setError(chunk.data || "Stream error");
683
+ }
545
684
  }
546
685
  },
547
- [client, agentRef]
686
+ []
548
687
  );
549
- const doSendMessage = import_react5.default.useCallback(
550
- async (content) => {
551
- if (!chatId) {
552
- setError("No active chat. Call createChat first.");
553
- return;
554
- }
555
- setLoading(true);
556
- setError(null);
557
- const userMsg = {
558
- id: `tmp-${Date.now()}`,
559
- chatId,
560
- role: "user",
561
- content,
562
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
563
- };
564
- setMessages((prev) => [...prev, userMsg]);
688
+ const refreshMessages = import_react5.default.useCallback(
689
+ async (id) => {
565
690
  try {
566
- const msg = await sendMessage(client, chatId, content);
567
- setMessages((prev) => [...prev, msg]);
568
- return msg;
569
- } catch (e) {
570
- setError(e.message);
571
- } finally {
572
- setLoading(false);
691
+ const detail = await getChat(client, id);
692
+ setMessages(detail.messages);
693
+ } catch {
573
694
  }
574
695
  },
575
- [client, chatId]
696
+ [client]
576
697
  );
577
- const doStreamMessage = import_react5.default.useCallback(
698
+ const send = import_react5.default.useCallback(
578
699
  (content) => {
579
- if (!chatId) {
580
- setError("No active chat. Call createChat first.");
581
- return;
582
- }
583
- abortRef.current?.abort();
700
+ const doSend = async () => {
701
+ let activeChatId = chatIdRef.current;
702
+ if (!activeChatId) {
703
+ setLoading(true);
704
+ setError(null);
705
+ try {
706
+ const result = await createChat(client, agentRef, options?.input);
707
+ activeChatId = result.id;
708
+ setChatId(result.id);
709
+ chatIdRef.current = result.id;
710
+ } catch (e) {
711
+ setError(e.message);
712
+ setLoading(false);
713
+ return;
714
+ }
715
+ setLoading(false);
716
+ }
717
+ abortRef.current?.abort();
718
+ setStreaming(true);
719
+ setError(null);
720
+ setPendingApprovals([]);
721
+ const displayContent = typeof content === "string" ? content : JSON.stringify(content);
722
+ const userMsg = {
723
+ id: `tmp-user-${Date.now()}`,
724
+ chatId: activeChatId,
725
+ role: "user",
726
+ content: displayContent,
727
+ toolCalls: null,
728
+ toolCallId: null,
729
+ name: null,
730
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
731
+ };
732
+ setMessages((prev) => [...prev, userMsg]);
733
+ const assistantId = `tmp-assistant-${Date.now()}`;
734
+ const assistantMsg = {
735
+ id: assistantId,
736
+ chatId: activeChatId,
737
+ role: "assistant",
738
+ content: "",
739
+ toolCalls: null,
740
+ toolCallId: null,
741
+ name: null,
742
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
743
+ streaming: true
744
+ };
745
+ setMessages((prev) => [...prev, assistantMsg]);
746
+ const controller = streamMessage(
747
+ client,
748
+ activeChatId,
749
+ content,
750
+ handleStreamChunks(assistantId),
751
+ () => {
752
+ setStreaming(false);
753
+ refreshMessages(activeChatId);
754
+ },
755
+ (err) => {
756
+ setError(err.message);
757
+ setStreaming(false);
758
+ }
759
+ );
760
+ abortRef.current = controller;
761
+ };
762
+ doSend();
763
+ },
764
+ [client, agentRef, options?.input, handleStreamChunks, refreshMessages]
765
+ );
766
+ const handleApproval = import_react5.default.useCallback(
767
+ (toolCallId, approved) => {
768
+ const currentChatId = chatIdRef.current;
769
+ if (!currentChatId) return;
770
+ setPendingApprovals((prev) => prev.filter((a) => a.toolCallId !== toolCallId));
584
771
  setStreaming(true);
585
772
  setError(null);
586
- const userMsg = {
587
- id: `tmp-${Date.now()}`,
588
- chatId,
589
- role: "user",
590
- content,
591
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
592
- };
593
- setMessages((prev) => [...prev, userMsg]);
594
- const assistantId = `stream-${Date.now()}`;
595
- const assistantMsg = {
596
- id: assistantId,
597
- chatId,
598
- role: "assistant",
599
- content: "",
600
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
601
- };
602
- setMessages((prev) => [...prev, assistantMsg]);
603
- const controller = streamMessage(
604
- client,
605
- chatId,
606
- content,
607
- (chunk) => {
608
- if (chunk.event === "content" || chunk.event === "message") {
609
- setMessages(
610
- (prev) => prev.map(
611
- (m) => m.id === assistantId ? { ...m, content: m.content + chunk.data } : m
612
- )
613
- );
773
+ const assistantId = `tmp-resume-${Date.now()}`;
774
+ if (approved) {
775
+ const assistantMsg = {
776
+ id: assistantId,
777
+ chatId: currentChatId,
778
+ role: "assistant",
779
+ content: "",
780
+ toolCalls: null,
781
+ toolCallId: null,
782
+ name: null,
783
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
784
+ streaming: true
785
+ };
786
+ setMessages((prev) => [...prev, assistantMsg]);
787
+ }
788
+ (async () => {
789
+ try {
790
+ const result = await approveToolCall(client, currentChatId, toolCallId, approved);
791
+ if (!approved) {
792
+ setStreaming(false);
793
+ refreshMessages(currentChatId);
794
+ return;
614
795
  }
615
- },
616
- () => {
617
- setStreaming(false);
618
- },
619
- (err) => {
620
- setError(err.message);
796
+ const controller = streamFromChannel(
797
+ client,
798
+ currentChatId,
799
+ result.channelId,
800
+ handleStreamChunks(assistantId),
801
+ () => {
802
+ setStreaming(false);
803
+ refreshMessages(currentChatId);
804
+ },
805
+ (err) => {
806
+ setError(err.message);
807
+ setStreaming(false);
808
+ }
809
+ );
810
+ abortRef.current = controller;
811
+ } catch (e) {
812
+ setError(e.message);
621
813
  setStreaming(false);
622
814
  }
623
- );
624
- abortRef.current = controller;
625
- return controller;
815
+ })();
626
816
  },
627
- [client, chatId]
817
+ [client, handleStreamChunks, refreshMessages]
818
+ );
819
+ const approve = import_react5.default.useCallback(
820
+ (toolCallId) => handleApproval(toolCallId, true),
821
+ [handleApproval]
628
822
  );
823
+ const reject = import_react5.default.useCallback(
824
+ (toolCallId) => handleApproval(toolCallId, false),
825
+ [handleApproval]
826
+ );
827
+ const stop = import_react5.default.useCallback(() => {
828
+ abortRef.current?.abort();
829
+ abortRef.current = null;
830
+ setStreaming(false);
831
+ }, []);
832
+ const clearError = import_react5.default.useCallback(() => {
833
+ setError(null);
834
+ }, []);
629
835
  return {
630
836
  chatId,
631
837
  messages,
632
838
  loading,
633
839
  streaming,
634
840
  error,
635
- createChat: doCreateChat,
636
- sendMessage: doSendMessage,
637
- streamMessage: doStreamMessage
841
+ pendingApprovals,
842
+ send,
843
+ approve,
844
+ reject,
845
+ stop,
846
+ clearError
638
847
  };
639
848
  }
640
849
 
@@ -660,14 +869,19 @@ function useSinas() {
660
869
  SinasPermissionError,
661
870
  SinasProvider,
662
871
  SinasTimeoutError,
872
+ approveToolCall,
663
873
  createChat,
664
874
  executeFunction,
665
875
  executeQuery,
876
+ getChat,
877
+ listCollections,
878
+ listFiles,
666
879
  sendMessage,
667
880
  stateDelete,
668
881
  stateGet,
669
882
  stateList,
670
883
  stateSet,
884
+ streamFromChannel,
671
885
  streamMessage,
672
886
  useChat,
673
887
  useExecute,