@deltakit/react 0.2.3 → 0.2.4

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/index.cjs CHANGED
@@ -42,56 +42,40 @@ function useAutoScroll(dependencies, options) {
42
42
  const isAtBottomRef = (0, import_react.useRef)(true);
43
43
  const [isAtBottom, setIsAtBottom] = (0, import_react.useState)(true);
44
44
  const rafRef = (0, import_react.useRef)(null);
45
- const smoothRafRef = (0, import_react.useRef)(null);
46
- const smoothTargetRef = (0, import_react.useRef)(null);
47
45
  const lastAutoScrollHeightRef = (0, import_react.useRef)(null);
48
- const cancelSmoothScroll = (0, import_react.useCallback)(() => {
49
- if (smoothRafRef.current != null) {
50
- cancelAnimationFrame(smoothRafRef.current);
51
- smoothRafRef.current = null;
52
- }
53
- smoothTargetRef.current = null;
54
- }, []);
55
- const runSmoothScroll = (0, import_react.useCallback)(() => {
56
- if (smoothRafRef.current != null) return;
46
+ const pendingVerificationFramesRef = (0, import_react.useRef)(0);
47
+ const scheduleScroll = (0, import_react.useCallback)(() => {
48
+ pendingVerificationFramesRef.current = Math.max(
49
+ pendingVerificationFramesRef.current,
50
+ 1
51
+ );
52
+ if (rafRef.current != null) return;
57
53
  const tick = () => {
54
+ rafRef.current = null;
58
55
  const el = ref.current;
59
56
  if (!el || !isAtBottomRef.current) {
60
- cancelSmoothScroll();
61
- return;
62
- }
63
- const target = smoothTargetRef.current ?? el.scrollHeight;
64
- const delta = target - el.scrollTop;
65
- if (delta <= 1) {
66
- el.scrollTop = target;
67
- lastAutoScrollHeightRef.current = target;
68
- smoothRafRef.current = null;
57
+ pendingVerificationFramesRef.current = 0;
69
58
  return;
70
59
  }
71
- const step = Math.min(Math.max(delta * 0.35, 12), 120);
72
- el.scrollTop = Math.min(target, el.scrollTop + step);
73
- smoothRafRef.current = requestAnimationFrame(tick);
74
- };
75
- smoothRafRef.current = requestAnimationFrame(tick);
76
- }, [cancelSmoothScroll]);
77
- const scheduleScroll = (0, import_react.useCallback)(() => {
78
- if (rafRef.current != null) return;
79
- rafRef.current = requestAnimationFrame(() => {
80
- rafRef.current = null;
81
- const el = ref.current;
82
- if (!el || !isAtBottomRef.current) return;
83
60
  const nextHeight = el.scrollHeight;
84
- if (lastAutoScrollHeightRef.current === nextHeight) return;
85
- lastAutoScrollHeightRef.current = nextHeight;
86
- if (behavior === "smooth") {
87
- smoothTargetRef.current = nextHeight;
88
- runSmoothScroll();
89
- return;
61
+ const heightChanged = lastAutoScrollHeightRef.current !== nextHeight;
62
+ const distanceFromBottom = nextHeight - el.scrollTop - el.clientHeight;
63
+ if (heightChanged || distanceFromBottom > 0) {
64
+ lastAutoScrollHeightRef.current = nextHeight;
65
+ pendingVerificationFramesRef.current = 1;
66
+ el.scrollTop = nextHeight;
67
+ } else {
68
+ pendingVerificationFramesRef.current = Math.max(
69
+ pendingVerificationFramesRef.current - 1,
70
+ 0
71
+ );
90
72
  }
91
- cancelSmoothScroll();
92
- el.scrollTop = nextHeight;
93
- });
94
- }, [behavior, cancelSmoothScroll, runSmoothScroll]);
73
+ if (pendingVerificationFramesRef.current > 0) {
74
+ rafRef.current = requestAnimationFrame(tick);
75
+ }
76
+ };
77
+ rafRef.current = requestAnimationFrame(tick);
78
+ }, []);
95
79
  (0, import_react.useEffect)(() => {
96
80
  const el = ref.current;
97
81
  if (!el || !enabled) return;
@@ -100,7 +84,7 @@ function useAutoScroll(dependencies, options) {
100
84
  isAtBottomRef.current = atBottom;
101
85
  if (!atBottom) {
102
86
  lastAutoScrollHeightRef.current = null;
103
- cancelSmoothScroll();
87
+ pendingVerificationFramesRef.current = 0;
104
88
  }
105
89
  setIsAtBottom((prev) => prev === atBottom ? prev : atBottom);
106
90
  };
@@ -141,18 +125,18 @@ function useAutoScroll(dependencies, options) {
141
125
  cancelAnimationFrame(rafRef.current);
142
126
  rafRef.current = null;
143
127
  }
144
- cancelSmoothScroll();
128
+ pendingVerificationFramesRef.current = 0;
145
129
  };
146
- }, [cancelSmoothScroll]);
130
+ }, []);
147
131
  const scrollToBottom = (0, import_react.useCallback)(() => {
148
132
  const el = ref.current;
149
133
  if (!el) return;
150
- cancelSmoothScroll();
151
134
  isAtBottomRef.current = true;
152
135
  lastAutoScrollHeightRef.current = el.scrollHeight;
136
+ pendingVerificationFramesRef.current = 0;
153
137
  setIsAtBottom(true);
154
138
  el.scrollTo({ top: el.scrollHeight, behavior });
155
- }, [behavior, cancelSmoothScroll]);
139
+ }, [behavior]);
156
140
  return { ref, scrollToBottom, isAtBottom };
157
141
  }
158
142
 
@@ -552,14 +536,7 @@ function defaultOnEvent(event, helpers) {
552
536
  }
553
537
  }
554
538
  function useStreamChat(options) {
555
- const {
556
- initialMessages,
557
- onEvent,
558
- onMessage,
559
- onError,
560
- onFinish,
561
- textBatchMs = 0
562
- } = options;
539
+ const { initialMessages, onEvent, onMessage, onError, onFinish } = options;
563
540
  const [messages, setMessages] = (0, import_react2.useState)(initialMessages ?? []);
564
541
  const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
565
542
  const [error, setError] = (0, import_react2.useState)(null);
@@ -569,29 +546,10 @@ function useStreamChat(options) {
569
546
  const manuallyStoppedRef = (0, import_react2.useRef)(false);
570
547
  const messagesRef = (0, import_react2.useRef)(messages);
571
548
  messagesRef.current = messages;
572
- const updateMessages = (0, import_react2.useCallback)(
573
- (next) => {
574
- setMessages((prev) => {
575
- const resolved = typeof next === "function" ? next(prev) : next;
576
- messagesRef.current = resolved;
577
- return resolved;
578
- });
579
- },
580
- []
581
- );
582
549
  const transportOptionsRef = (0, import_react2.useRef)(options.transportOptions);
583
550
  transportOptionsRef.current = options.transportOptions;
584
- const pendingTextRef = (0, import_react2.useRef)("");
585
- const textFlushTimerRef = (0, import_react2.useRef)(null);
586
- const flushPendingText = (0, import_react2.useCallback)(() => {
587
- if (textFlushTimerRef.current !== null) {
588
- clearTimeout(textFlushTimerRef.current);
589
- textFlushTimerRef.current = null;
590
- }
591
- const delta = pendingTextRef.current;
592
- if (!delta) return;
593
- pendingTextRef.current = "";
594
- updateMessages((prev) => {
551
+ const appendText = (0, import_react2.useCallback)((delta) => {
552
+ setMessages((prev) => {
595
553
  const last = prev[prev.length - 1];
596
554
  if (!last || last.role !== "assistant") return prev;
597
555
  const parts = [...last.parts];
@@ -607,22 +565,9 @@ function useStreamChat(options) {
607
565
  }
608
566
  return [...prev.slice(0, -1), { ...last, parts }];
609
567
  });
610
- }, [updateMessages]);
611
- const appendText = (0, import_react2.useCallback)((delta) => {
612
- if (textBatchMs <= 0) {
613
- pendingTextRef.current += delta;
614
- flushPendingText();
615
- return;
616
- }
617
- pendingTextRef.current += delta;
618
- if (textFlushTimerRef.current !== null) return;
619
- textFlushTimerRef.current = setTimeout(() => {
620
- flushPendingText();
621
- }, textBatchMs);
622
- }, [flushPendingText, textBatchMs]);
568
+ }, []);
623
569
  const appendPart = (0, import_react2.useCallback)((part) => {
624
- flushPendingText();
625
- updateMessages((prev) => {
570
+ setMessages((prev) => {
626
571
  const last = prev[prev.length - 1];
627
572
  if (!last || last.role !== "assistant") return prev;
628
573
  return [
@@ -633,7 +578,7 @@ function useStreamChat(options) {
633
578
  }
634
579
  ];
635
580
  });
636
- }, [flushPendingText, updateMessages]);
581
+ }, []);
637
582
  const transportRef = (0, import_react2.useRef)(null);
638
583
  if (!transportRef.current) {
639
584
  transportRef.current = resolveTransport(options);
@@ -653,23 +598,20 @@ function useStreamChat(options) {
653
598
  appendPart,
654
599
  appendText,
655
600
  eventHandler: (event, helpers) => eventHandlerRef.current(event, helpers),
656
- getMessages: () => {
657
- flushPendingText();
658
- return messagesRef.current;
659
- },
601
+ getMessages: () => messagesRef.current,
660
602
  onError: (...args) => onErrorRef.current?.(...args),
661
603
  onFinish: (...args) => onFinishRef.current?.(...args),
662
604
  onMessage: (...args) => onMessageRef.current?.(...args),
663
605
  setError,
664
606
  setIsLoading,
665
- setMessages: updateMessages,
607
+ setMessages,
666
608
  setRunId: (next) => {
667
609
  setRunId(next);
668
610
  transportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);
669
611
  transportOptionsRef.current?.websocket?.onRunIdChange?.(next);
670
612
  }
671
613
  }),
672
- [appendPart, appendText, flushPendingText, updateMessages]
614
+ [appendPart, appendText]
673
615
  );
674
616
  const stop = (0, import_react2.useCallback)(() => {
675
617
  const activeRun = runRef.current;
@@ -690,8 +632,9 @@ function useStreamChat(options) {
690
632
  { type: "text", text }
691
633
  ]);
692
634
  const assistantMessage = createMessage("assistant", []);
693
- updateMessages((prev) => {
635
+ setMessages((prev) => {
694
636
  const next = [...prev, userMessage, assistantMessage];
637
+ messagesRef.current = next;
695
638
  return next;
696
639
  });
697
640
  onMessage?.(userMessage);
@@ -705,7 +648,7 @@ function useStreamChat(options) {
705
648
  setRunId(run.runId);
706
649
  }
707
650
  },
708
- [isLoading, onMessage, transport, transportContext, updateMessages]
651
+ [isLoading, onMessage, transport, transportContext]
709
652
  );
710
653
  const candidateRunId = options.transportOptions?.backgroundSSE?.runId ?? options.transportOptions?.backgroundSSE?.getResumeKey?.() ?? options.transportOptions?.websocket?.runId ?? options.transportOptions?.websocket?.getResumeKey?.() ?? null;
711
654
  (0, import_react2.useEffect)(() => {
@@ -736,10 +679,6 @@ function useStreamChat(options) {
736
679
  }, [candidateRunId, transport, transportContext]);
737
680
  (0, import_react2.useEffect)(() => {
738
681
  return () => {
739
- if (textFlushTimerRef.current !== null) {
740
- clearTimeout(textFlushTimerRef.current);
741
- textFlushTimerRef.current = null;
742
- }
743
682
  void runRef.current?.close?.();
744
683
  runRef.current = null;
745
684
  };
@@ -757,7 +696,7 @@ function useStreamChat(options) {
757
696
  messages,
758
697
  runId,
759
698
  sendMessage,
760
- setMessages: updateMessages,
699
+ setMessages,
761
700
  stop
762
701
  };
763
702
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/use-auto-scroll.ts","../src/use-stream-chat.ts","../src/chat-controller.ts","../src/transports.ts"],"sourcesContent":["// Re-export core types so consumers only need to import from @deltakit/react\nexport type {\n\tContentPart,\n\tMessage,\n\tReasoningPart,\n\tSSEEvent,\n\tTextDeltaEvent,\n\tTextPart,\n\tToolCallEvent,\n\tToolCallPart,\n\tToolResultEvent,\n} from \"@deltakit/core\";\nexport {\n\tfromAgnoAgents,\n\tfromOpenAiAgents,\n\tparseSSEStream,\n} from \"@deltakit/core\";\n\nexport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tEventHelpers,\n\tTransportOptions,\n\tUseAutoScrollOptions,\n\tUseAutoScrollReturn,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n\tWebSocketTransportOptions,\n} from \"./types\";\nexport { useAutoScroll } from \"./use-auto-scroll\";\nexport { useStreamChat } from \"./use-stream-chat\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseAutoScrollOptions, UseAutoScrollReturn } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_THRESHOLD = 50;\n\n// ---------------------------------------------------------------------------\n// useAutoScroll\n// ---------------------------------------------------------------------------\n\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>(\n\tdependencies: unknown[],\n\toptions?: UseAutoScrollOptions,\n): UseAutoScrollReturn<T> {\n\tconst {\n\t\tbehavior = \"instant\",\n\t\tenabled = true,\n\t\tthreshold = DEFAULT_THRESHOLD,\n\t} = options ?? {};\n\n\tconst ref = useRef<T | null>(null);\n\tconst isAtBottomRef = useRef(true);\n\tconst [isAtBottom, setIsAtBottom] = useState(true);\n\n\t// A single rAF id shared across all scroll sources — ensures we never\n\t// call scrollTo() more than once per frame, no matter how many\n\t// MutationObserver / ResizeObserver callbacks fire.\n\tconst rafRef = useRef<number | null>(null);\n\tconst smoothRafRef = useRef<number | null>(null);\n\tconst smoothTargetRef = useRef<number | null>(null);\n\tconst lastAutoScrollHeightRef = useRef<number | null>(null);\n\n\tconst cancelSmoothScroll = useCallback(() => {\n\t\tif (smoothRafRef.current != null) {\n\t\t\tcancelAnimationFrame(smoothRafRef.current);\n\t\t\tsmoothRafRef.current = null;\n\t\t}\n\t\tsmoothTargetRef.current = null;\n\t}, []);\n\n\tconst runSmoothScroll = useCallback(() => {\n\t\tif (smoothRafRef.current != null) return;\n\n\t\tconst tick = () => {\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) {\n\t\t\t\tcancelSmoothScroll();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst target = smoothTargetRef.current ?? el.scrollHeight;\n\t\t\tconst delta = target - el.scrollTop;\n\n\t\t\tif (delta <= 1) {\n\t\t\t\tel.scrollTop = target;\n\t\t\t\tlastAutoScrollHeightRef.current = target;\n\t\t\t\tsmoothRafRef.current = null;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst step = Math.min(Math.max(delta * 0.35, 12), 120);\n\t\t\tel.scrollTop = Math.min(target, el.scrollTop + step);\n\t\t\tsmoothRafRef.current = requestAnimationFrame(tick);\n\t\t};\n\n\t\tsmoothRafRef.current = requestAnimationFrame(tick);\n\t}, [cancelSmoothScroll]);\n\n\tconst scheduleScroll = useCallback(() => {\n\t\tif (rafRef.current != null) return;\n\t\trafRef.current = requestAnimationFrame(() => {\n\t\t\trafRef.current = null;\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) return;\n\n\t\t\tconst nextHeight = el.scrollHeight;\n\t\t\tif (lastAutoScrollHeightRef.current === nextHeight) return;\n\n\t\t\tlastAutoScrollHeightRef.current = nextHeight;\n\n\t\t\tif (behavior === \"smooth\") {\n\t\t\t\tsmoothTargetRef.current = nextHeight;\n\t\t\t\trunSmoothScroll();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcancelSmoothScroll();\n\t\t\tel.scrollTop = nextHeight;\n\t\t});\n\t}, [behavior, cancelSmoothScroll, runSmoothScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Track whether the user is near the bottom via scroll events.\n\t// Only triggers a React re-render when the boolean actually changes.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst handleScroll = () => {\n\t\t\tconst atBottom =\n\t\t\t\tel.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n\t\t\tisAtBottomRef.current = atBottom;\n\t\t\tif (!atBottom) {\n\t\t\t\tlastAutoScrollHeightRef.current = null;\n\t\t\t\tcancelSmoothScroll();\n\t\t\t}\n\t\t\tsetIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n\t\t};\n\n\t\tel.addEventListener(\"scroll\", handleScroll, { passive: true });\n\t\thandleScroll();\n\t\treturn () => el.removeEventListener(\"scroll\", handleScroll);\n\t}, [enabled, threshold]);\n\n\t// -----------------------------------------------------------------------\n\t// Scroll to bottom when dependencies change (if pinned).\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tif (!enabled || !isAtBottomRef.current) return;\n\t\tscheduleScroll();\n\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies are passed dynamically by the consumer\n\t}, dependencies);\n\n\t// -----------------------------------------------------------------------\n\t// MutationObserver + ResizeObserver — catch content changes during\n\t// streaming that happen between React re-renders (e.g. DOM mutations\n\t// from markdown renderers). Scroll calls are batched via rAF so we\n\t// scroll at most once per frame.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst resizeObserver = new ResizeObserver(scheduleScroll);\n\n\t\t// Observe existing children for size changes.\n\t\tfor (const child of el.children) {\n\t\t\tresizeObserver.observe(child);\n\t\t}\n\n\t\t// Watch for new children added to the container.\n\t\tconst mutationObserver = new MutationObserver((mutations) => {\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tfor (const node of mutation.addedNodes) {\n\t\t\t\t\tif (node instanceof Element) {\n\t\t\t\t\t\tresizeObserver.observe(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tscheduleScroll();\n\t\t});\n\n\t\tmutationObserver.observe(el, { childList: true, subtree: true });\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [enabled, scheduleScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Cancel any pending rAF on unmount.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (rafRef.current != null) {\n\t\t\t\tcancelAnimationFrame(rafRef.current);\n\t\t\t\trafRef.current = null;\n\t\t\t}\n\t\t\tcancelSmoothScroll();\n\t\t};\n\t}, [cancelSmoothScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// scrollToBottom — imperative function that scrolls to the bottom and\n\t// re-pins auto-scroll.\n\t// -----------------------------------------------------------------------\n\n\tconst scrollToBottom = useCallback(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tcancelSmoothScroll();\n\t\tisAtBottomRef.current = true;\n\t\tlastAutoScrollHeightRef.current = el.scrollHeight;\n\t\tsetIsAtBottom(true);\n\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t}, [behavior, cancelSmoothScroll]);\n\n\treturn { ref, scrollToBottom, isAtBottom };\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { createChatTransportContext, createMessage } from \"./chat-controller\";\nimport { resolveTransport } from \"./transports\";\nimport type {\n\tChatTransportRun,\n\tEventHelpers,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Default event handler — accumulates `text_delta` into the last\n// assistant message's parts.\n// ---------------------------------------------------------------------------\n\nfunction defaultOnEvent(\n\tevent: SSEEvent,\n\thelpers: EventHelpers<ContentPart>,\n): void {\n\tif (event.type === \"text_delta\") {\n\t\thelpers.appendText(event.delta);\n\t}\n\t// Other event types (e.g. tool_call) are silently ignored by default.\n\t// Users can provide their own `onEvent` to handle them.\n}\n\n// ---------------------------------------------------------------------------\n// useStreamChat\n// ---------------------------------------------------------------------------\n\nexport function useStreamChat<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): UseStreamChatReturn<TPart> {\n\tconst {\n\t\tinitialMessages,\n\t\tonEvent,\n\t\tonMessage,\n\t\tonError,\n\t\tonFinish,\n\t\ttextBatchMs = 0,\n\t} = options;\n\n\tconst [messages, setMessages] = useState(initialMessages ?? []);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [runId, setRunId] = useState<string | null>(null);\n\n\tconst runRef = useRef<ChatTransportRun | null>(null);\n\n\t// Track which run id has already been resumed to prevent re-triggering.\n\tconst resumedRunIdRef = useRef<string | null>(null);\n\n\t// When the user manually stops a run, suppress auto-resume until the\n\t// next explicit `sendMessage` call.\n\tconst manuallyStoppedRef = useRef(false);\n\n\t// We use a ref for the latest messages so callbacks created inside\n\t// transport handlers always see the current value without re-creating\n\t// closures.\n\tconst messagesRef = useRef(messages);\n\tmessagesRef.current = messages;\n\n\tconst updateMessages = useCallback(\n\t\t(next:\n\t\t\t| Message<TPart>[]\n\t\t\t| ((prev: Message<TPart>[]) => Message<TPart>[])) => {\n\t\t\tsetMessages((prev) => {\n\t\t\t\tconst resolved =\n\t\t\t\t\ttypeof next === \"function\"\n\t\t\t\t\t\t? (next as (prev: Message<TPart>[]) => Message<TPart>[])(prev)\n\t\t\t\t\t\t: next;\n\t\t\t\tmessagesRef.current = resolved;\n\t\t\t\treturn resolved;\n\t\t\t});\n\t\t},\n\t\t[],\n\t);\n\n\t// Keep transport options in a ref so that callbacks always read the\n\t// latest values without causing memoisation instability.\n\tconst transportOptionsRef = useRef(options.transportOptions);\n\ttransportOptionsRef.current = options.transportOptions;\n\n\tconst pendingTextRef = useRef(\"\");\n\tconst textFlushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\tconst flushPendingText = useCallback(() => {\n\t\tif (textFlushTimerRef.current !== null) {\n\t\t\tclearTimeout(textFlushTimerRef.current);\n\t\t\ttextFlushTimerRef.current = null;\n\t\t}\n\n\t\tconst delta = pendingTextRef.current;\n\t\tif (!delta) return;\n\n\t\tpendingTextRef.current = \"\";\n\t\tupdateMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\tconst parts = [...last.parts];\n\t\t\tconst lastPart = parts[parts.length - 1];\n\n\t\t\tif (lastPart && lastPart.type === \"text\" && \"text\" in lastPart) {\n\t\t\t\tconst textPart = lastPart as { type: \"text\"; text: string };\n\t\t\t\tparts[parts.length - 1] = {\n\t\t\t\t\t...lastPart,\n\t\t\t\t\ttext: textPart.text + delta,\n\t\t\t\t} as unknown as TPart;\n\t\t\t} else {\n\t\t\t\tparts.push({ type: \"text\", text: delta } as unknown as TPart);\n\t\t\t}\n\n\t\t\treturn [...prev.slice(0, -1), { ...last, parts }];\n\t\t});\n\t}, [updateMessages]);\n\n\tconst appendText = useCallback((delta: string) => {\n\t\tif (textBatchMs <= 0) {\n\t\t\tpendingTextRef.current += delta;\n\t\t\tflushPendingText();\n\t\t\treturn;\n\t\t}\n\n\t\tpendingTextRef.current += delta;\n\t\tif (textFlushTimerRef.current !== null) return;\n\n\t\ttextFlushTimerRef.current = setTimeout(() => {\n\t\t\tflushPendingText();\n\t\t}, textBatchMs);\n\t}, [flushPendingText, textBatchMs]);\n\n\tconst appendPart = useCallback((part: TPart) => {\n\t\tflushPendingText();\n\t\tupdateMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\treturn [\n\t\t\t\t...prev.slice(0, -1),\n\t\t\t\t{\n\t\t\t\t\t...last,\n\t\t\t\t\tparts: [...last.parts, part],\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t}, [flushPendingText, updateMessages]);\n\n\t// Stabilise transport creation: resolve once and store in a ref so that\n\t// changing values like `runId` in transportOptions won't cause a new\n\t// transport instance (and therefore a new WebSocket) to be created.\n\tconst transportRef = useRef<ReturnType<\n\t\ttypeof resolveTransport<TPart, TEvent>\n\t> | null>(null);\n\tif (!transportRef.current) {\n\t\ttransportRef.current = resolveTransport(options);\n\t}\n\tconst transport = transportRef.current;\n\n\tconst eventHandler =\n\t\tonEvent ??\n\t\t(defaultOnEvent as unknown as (\n\t\t\tevent: TEvent,\n\t\t\thelpers: EventHelpers<TPart>,\n\t\t) => void);\n\n\t// Stabilise the transport context: use refs for values that change\n\t// frequently (transportOptions callbacks) so the context object itself\n\t// stays referentially stable.\n\tconst eventHandlerRef = useRef(eventHandler);\n\teventHandlerRef.current = eventHandler;\n\n\tconst onErrorRef = useRef(onError);\n\tonErrorRef.current = onError;\n\n\tconst onFinishRef = useRef(onFinish);\n\tonFinishRef.current = onFinish;\n\n\tconst onMessageRef = useRef(onMessage);\n\tonMessageRef.current = onMessage;\n\n\tconst transportContext = useMemo(\n\t\t() =>\n\t\t\tcreateChatTransportContext({\n\t\t\t\tappendPart,\n\t\t\t\tappendText,\n\t\t\t\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) =>\n\t\t\t\t\teventHandlerRef.current(event, helpers),\n\t\t\t\tgetMessages: () => {\n\t\t\t\t\tflushPendingText();\n\t\t\t\t\treturn messagesRef.current;\n\t\t\t\t},\n\t\t\t\tonError: (...args) => onErrorRef.current?.(...args),\n\t\t\t\tonFinish: (...args) => onFinishRef.current?.(...args),\n\t\t\t\tonMessage: (...args) => onMessageRef.current?.(...args),\n\t\t\t\tsetError,\n\t\t\t\tsetIsLoading,\n\t\t\t\tsetMessages: updateMessages,\n\t\t\t\tsetRunId: (next) => {\n\t\t\t\t\tsetRunId(next);\n\t\t\t\t\ttransportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);\n\t\t\t\t\ttransportOptionsRef.current?.websocket?.onRunIdChange?.(next);\n\t\t\t\t},\n\t\t\t}),\n\t\t[appendPart, appendText, flushPendingText, updateMessages],\n\t);\n\n\tconst stop = useCallback(() => {\n\t\tconst activeRun = runRef.current;\n\t\tif (!activeRun?.stop) {\n\t\t\treturn;\n\t\t}\n\n\t\tmanuallyStoppedRef.current = true;\n\t\tvoid activeRun.stop();\n\t\trunRef.current = null;\n\t\tsetIsLoading(false);\n\t}, []);\n\n\tconst sendMessage = useCallback(\n\t\t(text: string) => {\n\t\t\tif (runRef.current || isLoading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst userMessage = createMessage<TPart>(\"user\", [\n\t\t\t\t{ type: \"text\", text } as unknown as TPart,\n\t\t\t]);\n\t\t\tconst assistantMessage = createMessage<TPart>(\"assistant\", []);\n\n\t\t\tupdateMessages((prev) => {\n\t\t\t\tconst next = [...prev, userMessage, assistantMessage];\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t\tonMessage?.(userMessage);\n\t\t\tsetError(null);\n\t\t\tsetIsLoading(true);\n\n\t\t\t// Reset the resume guard so a future resume for a new run is allowed.\n\t\t\tresumedRunIdRef.current = null;\n\t\t\tmanuallyStoppedRef.current = false;\n\n\t\t\tconst run = transport.start({ context: transportContext, message: text });\n\t\t\trunRef.current = run ?? null;\n\t\t\tif (run?.runId) {\n\t\t\t\tsetRunId(run.runId);\n\t\t\t}\n\t\t},\n\t\t[isLoading, onMessage, transport, transportContext, updateMessages],\n\t);\n\n\t// -----------------------------------------------------------------------\n\t// Auto-resume effect: attempt to reconnect to an in-flight run on mount.\n\t//\n\t// The candidate run id is read from transport options (which may change\n\t// when the app updates state). We guard against duplicate resumes for the\n\t// same run id using `resumedRunIdRef`.\n\t// -----------------------------------------------------------------------\n\n\tconst candidateRunId =\n\t\toptions.transportOptions?.backgroundSSE?.runId ??\n\t\toptions.transportOptions?.backgroundSSE?.getResumeKey?.() ??\n\t\toptions.transportOptions?.websocket?.runId ??\n\t\toptions.transportOptions?.websocket?.getResumeKey?.() ??\n\t\tnull;\n\n\tuseEffect(() => {\n\t\t// Already have an active run — don't start another.\n\t\tif (runRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User explicitly stopped — don't auto-resume until next sendMessage.\n\t\tif (manuallyStoppedRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Already resumed this exact run id — don't retry.\n\t\tif (resumedRunIdRef.current === candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!transport.resume) {\n\t\t\treturn;\n\t\t}\n\n\t\tresumedRunIdRef.current = candidateRunId;\n\n\t\tsetError(null);\n\t\tsetIsLoading(true);\n\t\tconst run = transport.resume({\n\t\t\tcontext: transportContext,\n\t\t\trunId: candidateRunId,\n\t\t});\n\t\trunRef.current = run ?? null;\n\t\tsetRunId(candidateRunId);\n\t}, [candidateRunId, transport, transportContext]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (textFlushTimerRef.current !== null) {\n\t\t\t\tclearTimeout(textFlushTimerRef.current);\n\t\t\t\ttextFlushTimerRef.current = null;\n\t\t\t}\n\t\t\tvoid runRef.current?.close?.();\n\t\t\trunRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst prevIsLoadingRef = useRef(isLoading);\n\tuseEffect(() => {\n\t\t// Only clear the run ref on a true → false transition, not on mount\n\t\t// where isLoading starts as false. Clearing on mount would race with\n\t\t// the auto-resume effect and null out the run it just created.\n\t\tif (prevIsLoadingRef.current && !isLoading) {\n\t\t\trunRef.current = null;\n\t\t}\n\t\tprevIsLoadingRef.current = isLoading;\n\t}, [isLoading]);\n\n\treturn {\n\t\terror,\n\t\tisLoading,\n\t\tmessages,\n\t\trunId,\n\t\tsendMessage,\n\t\tsetMessages: updateMessages,\n\t\tstop,\n\t};\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport type {\n\tChatTransportContext,\n\tEventHelpers,\n\tUseStreamChatOptions,\n} from \"./types\";\n\nexport interface ChatControllerOptions<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n> {\n\tappendPart: EventHelpers<TPart>[\"appendPart\"];\n\tappendText: EventHelpers<TPart>[\"appendText\"];\n\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) => void;\n\tgetMessages: () => Message<TPart>[];\n\tonError?: UseStreamChatOptions<TPart, TEvent>[\"onError\"];\n\tonFinish?: UseStreamChatOptions<TPart, TEvent>[\"onFinish\"];\n\tonMessage?: UseStreamChatOptions<TPart, TEvent>[\"onMessage\"];\n\tsetError: Dispatch<SetStateAction<Error | null>>;\n\tsetIsLoading: Dispatch<SetStateAction<boolean>>;\n\tsetMessages: Dispatch<SetStateAction<Message<TPart>[]>>;\n\tsetRunId: (runId: string | null) => void;\n}\n\nlet counter = 0;\n\nexport function generateId(): string {\n\treturn `msg_${Date.now()}_${++counter}`;\n}\n\nexport function createMessage<TPart extends { type: string }>(\n\trole: Message[\"role\"],\n\tparts: TPart[],\n): Message<TPart> {\n\treturn { id: generateId(), role, parts };\n}\n\nexport function createChatTransportContext<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(\n\toptions: ChatControllerOptions<TPart, TEvent>,\n): ChatTransportContext<TPart, TEvent> {\n\tconst helpers: EventHelpers<TPart> = {\n\t\tappendPart: options.appendPart,\n\t\tappendText: options.appendText,\n\t\tsetMessages: options.setMessages,\n\t};\n\n\treturn {\n\t\temit: (event) => {\n\t\t\toptions.eventHandler(event, helpers);\n\t\t},\n\t\tensureAssistantMessage: () => {\n\t\t\toptions.setMessages((prev) => {\n\t\t\t\tconst last = prev[prev.length - 1];\n\t\t\t\tif (last?.role === \"assistant\") {\n\t\t\t\t\treturn prev;\n\t\t\t\t}\n\n\t\t\t\treturn [...prev, createMessage<TPart>(\"assistant\", [])];\n\t\t\t});\n\t\t},\n\t\tfail: (error) => {\n\t\t\toptions.setError(error);\n\t\t\toptions.onError?.(error);\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\t\t},\n\t\tfinish: () => {\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\n\t\t\tconst finalMessages = options.getMessages();\n\t\t\tconst lastMessage = finalMessages[finalMessages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\toptions.onMessage?.(lastMessage);\n\t\t\t}\n\n\t\t\toptions.onFinish?.(finalMessages);\n\t\t},\n\t\tgetMessages: options.getMessages,\n\t\tsetRunId: (runId) => {\n\t\t\toptions.setRunId(runId);\n\t\t},\n\t};\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { parseSSEStream } from \"@deltakit/core\";\nimport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tUseStreamChatOptions,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction resolveRunId(response: unknown): string {\n\tif (!response || typeof response !== \"object\") {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\tconst maybeRunId =\n\t\t\"runId\" in response\n\t\t\t? response.runId\n\t\t\t: \"job_id\" in response\n\t\t\t\t? response.job_id\n\t\t\t\t: null;\n\n\tif (typeof maybeRunId !== \"string\" || maybeRunId.length === 0) {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\treturn maybeRunId;\n}\n\nfunction resolveUrl(\n\turl: string | ((runId: string) => string),\n\trunId: string,\n): string {\n\treturn typeof url === \"function\" ? url(runId) : url.replace(\":runId\", runId);\n}\n\nasync function streamFetchSSE<\n\tTPart extends { type: string },\n\tTEvent extends { type: string },\n>(\n\tresponse: Response,\n\tcontext: ChatTransportContext<TPart, TEvent>,\n\tsignal: AbortSignal,\n): Promise<void> {\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`SSE request failed: ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"Response body is null — SSE streaming not supported\");\n\t}\n\n\tfor await (const event of parseSSEStream(response.body, signal)) {\n\t\tcontext.emit(event as unknown as TEvent);\n\t}\n}\n\nexport function createDirectSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: DirectSSETransportOptions): ChatTransport<TPart, TEvent> {\n\treturn {\n\t\tstart: ({ context, message }) => {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst run: ChatTransportRun = {\n\t\t\t\tclose: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst fetchImpl = config.fetch ?? fetch;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.api, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.body }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.headers,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.method ?? \"POST\",\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\t\tcontext.finish();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn run;\n\t\t},\n\t};\n}\n\nexport function createBackgroundSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: BackgroundSSETransportOptions): ChatTransport<TPart, TEvent> {\n\tconst fetchImpl = config.fetch ?? fetch;\n\n\tconst connect = (\n\t\trunId: string,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t): ChatTransportRun => {\n\t\tconst controller = new AbortController();\n\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst response = await fetchImpl(resolveUrl(config.eventsApi, runId), {\n\t\t\t\t\theaders: config.eventHeaders,\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\tcontext.finish();\n\t\t\t} catch (error) {\n\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t},\n\t\t\tstop: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t\tif (config.cancelApi) {\n\t\t\t\t\tvoid fetchImpl(resolveUrl(config.cancelApi, runId), {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\trunId,\n\t\t};\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\treturn connect(runId, context);\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst startController = new AbortController();\n\t\t\tlet activeRun: ChatTransportRun | undefined;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.startApi, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.startBody }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.startHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.startMethod ?? \"POST\",\n\t\t\t\t\t\tsignal: startController.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Background SSE start failed: ${response.status} ${response.statusText}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as unknown;\n\t\t\t\t\tconst runId = (config.resolveRunId ?? resolveRunId)(data);\n\t\t\t\t\tcontext.setRunId(runId);\n\t\t\t\t\tactiveRun = connect(runId, context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tvoid activeRun?.close?.();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tactiveRun?.stop?.();\n\t\t\t\t},\n\t\t\t\trunId: null,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction defaultParseWebSocketMessage<TEvent extends { type: string }>(\n\tdata: unknown,\n): TEvent | TEvent[] | null {\n\tif (typeof data !== \"string\") {\n\t\treturn null;\n\t}\n\n\tconst parsed = JSON.parse(data) as TEvent | TEvent[];\n\treturn parsed;\n}\n\nexport function createWebSocketTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: WebSocketTransportOptions<TEvent>): ChatTransport<TPart, TEvent> {\n\tconst parseMessage = config.parseMessage ?? defaultParseWebSocketMessage;\n\tconst serializeMessage = config.serializeMessage ?? JSON.stringify;\n\n\tlet resolvedRunId: string | null = null;\n\n\tconst applyIncomingEvents = (\n\t\tparsed: TEvent | TEvent[] | null,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t) => {\n\t\tconst events = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];\n\t\tfor (const item of events) {\n\t\t\tconst nextRunId = config.resolveRunId?.(item) ?? null;\n\t\t\tif (nextRunId) {\n\t\t\t\tresolvedRunId = nextRunId;\n\t\t\t\tcontext.setRunId(nextRunId);\n\t\t\t}\n\t\t\tcontext.emit(item);\n\n\t\t\tif (item.type === \"done\") {\n\t\t\t\tcontext.finish();\n\t\t\t} else if (item.type === \"error\") {\n\t\t\t\tcontext.fail(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\"message\" in item && typeof item.message === \"string\"\n\t\t\t\t\t\t\t? item.message\n\t\t\t\t\t\t\t: \"Stream error\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\tcontext.ensureAssistantMessage();\n\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentResumePayload = false;\n\n\t\t\tconst sendResumePayload = () => {\n\t\t\t\tif (sentResumePayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentResumePayload = true;\n\t\t\t\tconst payload =\n\t\t\t\t\tconfig.buildResumePayload?.(runId) ??\n\t\t\t\t\t({ [config.runIdKey ?? \"runId\"]: runId } as Record<string, unknown>);\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendResumePayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendResumePayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst runId = config.runId ?? config.getResumeKey?.() ?? null;\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentStartPayload = false;\n\n\t\t\tconst sendStartPayload = () => {\n\t\t\t\tif (sentStartPayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentStartPayload = true;\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst payload: Record<string, unknown> = {\n\t\t\t\t\tmessage,\n\t\t\t\t\t...config.body,\n\t\t\t\t};\n\n\t\t\t\tif (runId) {\n\t\t\t\t\tpayload[config.runIdKey ?? \"runId\"] = runId;\n\t\t\t\t}\n\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendStartPayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendStartPayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function resolveTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): ChatTransport<TPart, TEvent> {\n\tif (typeof options.transport === \"object\" && options.transport) {\n\t\treturn options.transport;\n\t}\n\n\tconst transportKind = options.transport ?? \"sse\";\n\n\tif (transportKind === \"background-sse\") {\n\t\tconst config = options.transportOptions?.backgroundSSE;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.backgroundSSE` is required when transport is \"background-sse\"',\n\t\t\t);\n\t\t}\n\t\treturn createBackgroundSSETransport(config);\n\t}\n\n\tif (transportKind === \"websocket\") {\n\t\tconst config = options.transportOptions?.websocket;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.websocket` is required when transport is \"websocket\"',\n\t\t\t);\n\t\t}\n\t\treturn createWebSocketTransport(config);\n\t}\n\n\tconst sseConfig = options.transportOptions?.sse ?? {\n\t\tapi: options.api,\n\t\tbody: options.body,\n\t\theaders: options.headers,\n\t};\n\n\tif (!sseConfig.api) {\n\t\tthrow new Error(\n\t\t\t\"`api` or `transportOptions.sse.api` is required when using the default SSE transport\",\n\t\t);\n\t}\n\n\treturn createDirectSSETransport({\n\t\t...sseConfig,\n\t\tapi: sseConfig.api,\n\t});\n}\n"],"mappings":"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","names":["import_core","import_react"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/use-auto-scroll.ts","../src/use-stream-chat.ts","../src/chat-controller.ts","../src/transports.ts"],"sourcesContent":["// Re-export core types so consumers only need to import from @deltakit/react\nexport type {\n\tContentPart,\n\tMessage,\n\tReasoningPart,\n\tSSEEvent,\n\tTextDeltaEvent,\n\tTextPart,\n\tToolCallEvent,\n\tToolCallPart,\n\tToolResultEvent,\n} from \"@deltakit/core\";\nexport {\n\tfromAgnoAgents,\n\tfromOpenAiAgents,\n\tparseSSEStream,\n} from \"@deltakit/core\";\n\nexport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tEventHelpers,\n\tTransportOptions,\n\tUseAutoScrollOptions,\n\tUseAutoScrollReturn,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n\tWebSocketTransportOptions,\n} from \"./types\";\nexport { useAutoScroll } from \"./use-auto-scroll\";\nexport { useStreamChat } from \"./use-stream-chat\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseAutoScrollOptions, UseAutoScrollReturn } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_THRESHOLD = 50;\n\n// ---------------------------------------------------------------------------\n// useAutoScroll\n// ---------------------------------------------------------------------------\n\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>(\n\tdependencies: unknown[],\n\toptions?: UseAutoScrollOptions,\n): UseAutoScrollReturn<T> {\n\tconst {\n\t\tbehavior = \"instant\",\n\t\tenabled = true,\n\t\tthreshold = DEFAULT_THRESHOLD,\n\t} = options ?? {};\n\n\tconst ref = useRef<T | null>(null);\n\tconst isAtBottomRef = useRef(true);\n\tconst [isAtBottom, setIsAtBottom] = useState(true);\n\n\t// A single rAF id shared across all scroll sources — ensures we never\n\t// call scrollTo() more than once per frame, no matter how many\n\t// MutationObserver / ResizeObserver callbacks fire.\n\tconst rafRef = useRef<number | null>(null);\n\tconst lastAutoScrollHeightRef = useRef<number | null>(null);\n\tconst pendingVerificationFramesRef = useRef(0);\n\n\tconst scheduleScroll = useCallback(() => {\n\t\tpendingVerificationFramesRef.current = Math.max(\n\t\t\tpendingVerificationFramesRef.current,\n\t\t\t1,\n\t\t);\n\t\tif (rafRef.current != null) return;\n\n\t\tconst tick = () => {\n\t\t\trafRef.current = null;\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) {\n\t\t\t\tpendingVerificationFramesRef.current = 0;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst nextHeight = el.scrollHeight;\n\t\t\tconst heightChanged = lastAutoScrollHeightRef.current !== nextHeight;\n\t\t\tconst distanceFromBottom = nextHeight - el.scrollTop - el.clientHeight;\n\n\t\t\tif (heightChanged || distanceFromBottom > 0) {\n\t\t\t\tlastAutoScrollHeightRef.current = nextHeight;\n\t\t\t\tpendingVerificationFramesRef.current = 1;\n\n\t\t\t\t// Keep the viewport pinned during streaming without restarting a\n\t\t\t\t// smooth scroll animation on every DOM mutation.\n\t\t\t\tel.scrollTop = nextHeight;\n\t\t\t} else {\n\t\t\t\tpendingVerificationFramesRef.current = Math.max(\n\t\t\t\t\tpendingVerificationFramesRef.current - 1,\n\t\t\t\t\t0,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (pendingVerificationFramesRef.current > 0) {\n\t\t\t\trafRef.current = requestAnimationFrame(tick);\n\t\t\t}\n\t\t};\n\n\t\trafRef.current = requestAnimationFrame(tick);\n\t}, []);\n\n\t// -----------------------------------------------------------------------\n\t// Track whether the user is near the bottom via scroll events.\n\t// Only triggers a React re-render when the boolean actually changes.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst handleScroll = () => {\n\t\t\tconst atBottom =\n\t\t\t\tel.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n\t\t\tisAtBottomRef.current = atBottom;\n\t\t\tif (!atBottom) {\n\t\t\t\tlastAutoScrollHeightRef.current = null;\n\t\t\t\tpendingVerificationFramesRef.current = 0;\n\t\t\t}\n\t\t\tsetIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n\t\t};\n\n\t\tel.addEventListener(\"scroll\", handleScroll, { passive: true });\n\t\thandleScroll();\n\t\treturn () => el.removeEventListener(\"scroll\", handleScroll);\n\t}, [enabled, threshold]);\n\n\t// -----------------------------------------------------------------------\n\t// Scroll to bottom when dependencies change (if pinned).\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tif (!enabled || !isAtBottomRef.current) return;\n\t\tscheduleScroll();\n\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies are passed dynamically by the consumer\n\t}, dependencies);\n\n\t// -----------------------------------------------------------------------\n\t// MutationObserver + ResizeObserver — catch content changes during\n\t// streaming that happen between React re-renders (e.g. DOM mutations\n\t// from markdown renderers). Scroll calls are batched via rAF so we\n\t// scroll at most once per frame.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst resizeObserver = new ResizeObserver(scheduleScroll);\n\n\t\t// Observe existing children for size changes.\n\t\tfor (const child of el.children) {\n\t\t\tresizeObserver.observe(child);\n\t\t}\n\n\t\t// Watch for new children added to the container.\n\t\tconst mutationObserver = new MutationObserver((mutations) => {\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tfor (const node of mutation.addedNodes) {\n\t\t\t\t\tif (node instanceof Element) {\n\t\t\t\t\t\tresizeObserver.observe(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tscheduleScroll();\n\t\t});\n\n\t\tmutationObserver.observe(el, { childList: true, subtree: true });\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [enabled, scheduleScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Cancel any pending rAF on unmount.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (rafRef.current != null) {\n\t\t\t\tcancelAnimationFrame(rafRef.current);\n\t\t\t\trafRef.current = null;\n\t\t\t}\n\t\t\tpendingVerificationFramesRef.current = 0;\n\t\t};\n\t}, []);\n\n\t// -----------------------------------------------------------------------\n\t// scrollToBottom — imperative function that scrolls to the bottom and\n\t// re-pins auto-scroll.\n\t// -----------------------------------------------------------------------\n\n\tconst scrollToBottom = useCallback(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tisAtBottomRef.current = true;\n\t\tlastAutoScrollHeightRef.current = el.scrollHeight;\n\t\tpendingVerificationFramesRef.current = 0;\n\t\tsetIsAtBottom(true);\n\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t}, [behavior]);\n\n\treturn { ref, scrollToBottom, isAtBottom };\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { createChatTransportContext, createMessage } from \"./chat-controller\";\nimport { resolveTransport } from \"./transports\";\nimport type {\n\tChatTransportRun,\n\tEventHelpers,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Default event handler — accumulates `text_delta` into the last\n// assistant message's parts.\n// ---------------------------------------------------------------------------\n\nfunction defaultOnEvent(\n\tevent: SSEEvent,\n\thelpers: EventHelpers<ContentPart>,\n): void {\n\tif (event.type === \"text_delta\") {\n\t\thelpers.appendText(event.delta);\n\t}\n\t// Other event types (e.g. tool_call) are silently ignored by default.\n\t// Users can provide their own `onEvent` to handle them.\n}\n\n// ---------------------------------------------------------------------------\n// useStreamChat\n// ---------------------------------------------------------------------------\n\nexport function useStreamChat<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): UseStreamChatReturn<TPart> {\n\tconst { initialMessages, onEvent, onMessage, onError, onFinish } = options;\n\n\tconst [messages, setMessages] = useState(initialMessages ?? []);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [runId, setRunId] = useState<string | null>(null);\n\n\tconst runRef = useRef<ChatTransportRun | null>(null);\n\n\t// Track which run id has already been resumed to prevent re-triggering.\n\tconst resumedRunIdRef = useRef<string | null>(null);\n\n\t// When the user manually stops a run, suppress auto-resume until the\n\t// next explicit `sendMessage` call.\n\tconst manuallyStoppedRef = useRef(false);\n\n\t// We use a ref for the latest messages so callbacks created inside\n\t// transport handlers always see the current value without re-creating\n\t// closures.\n\tconst messagesRef = useRef(messages);\n\tmessagesRef.current = messages;\n\n\t// Keep transport options in a ref so that callbacks always read the\n\t// latest values without causing memoisation instability.\n\tconst transportOptionsRef = useRef(options.transportOptions);\n\ttransportOptionsRef.current = options.transportOptions;\n\n\tconst appendText = useCallback((delta: string) => {\n\t\tsetMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\tconst parts = [...last.parts];\n\t\t\tconst lastPart = parts[parts.length - 1];\n\n\t\t\tif (lastPart && lastPart.type === \"text\" && \"text\" in lastPart) {\n\t\t\t\tconst textPart = lastPart as { type: \"text\"; text: string };\n\t\t\t\tparts[parts.length - 1] = {\n\t\t\t\t\t...lastPart,\n\t\t\t\t\ttext: textPart.text + delta,\n\t\t\t\t} as unknown as TPart;\n\t\t\t} else {\n\t\t\t\tparts.push({ type: \"text\", text: delta } as unknown as TPart);\n\t\t\t}\n\n\t\t\treturn [...prev.slice(0, -1), { ...last, parts }];\n\t\t});\n\t}, []);\n\n\tconst appendPart = useCallback((part: TPart) => {\n\t\tsetMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\treturn [\n\t\t\t\t...prev.slice(0, -1),\n\t\t\t\t{\n\t\t\t\t\t...last,\n\t\t\t\t\tparts: [...last.parts, part],\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t}, []);\n\n\t// Stabilise transport creation: resolve once and store in a ref so that\n\t// changing values like `runId` in transportOptions won't cause a new\n\t// transport instance (and therefore a new WebSocket) to be created.\n\tconst transportRef = useRef<ReturnType<\n\t\ttypeof resolveTransport<TPart, TEvent>\n\t> | null>(null);\n\tif (!transportRef.current) {\n\t\ttransportRef.current = resolveTransport(options);\n\t}\n\tconst transport = transportRef.current;\n\n\tconst eventHandler =\n\t\tonEvent ??\n\t\t(defaultOnEvent as unknown as (\n\t\t\tevent: TEvent,\n\t\t\thelpers: EventHelpers<TPart>,\n\t\t) => void);\n\n\t// Stabilise the transport context: use refs for values that change\n\t// frequently (transportOptions callbacks) so the context object itself\n\t// stays referentially stable.\n\tconst eventHandlerRef = useRef(eventHandler);\n\teventHandlerRef.current = eventHandler;\n\n\tconst onErrorRef = useRef(onError);\n\tonErrorRef.current = onError;\n\n\tconst onFinishRef = useRef(onFinish);\n\tonFinishRef.current = onFinish;\n\n\tconst onMessageRef = useRef(onMessage);\n\tonMessageRef.current = onMessage;\n\n\tconst transportContext = useMemo(\n\t\t() =>\n\t\t\tcreateChatTransportContext({\n\t\t\t\tappendPart,\n\t\t\t\tappendText,\n\t\t\t\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) =>\n\t\t\t\t\teventHandlerRef.current(event, helpers),\n\t\t\t\tgetMessages: () => messagesRef.current,\n\t\t\t\tonError: (...args) => onErrorRef.current?.(...args),\n\t\t\t\tonFinish: (...args) => onFinishRef.current?.(...args),\n\t\t\t\tonMessage: (...args) => onMessageRef.current?.(...args),\n\t\t\t\tsetError,\n\t\t\t\tsetIsLoading,\n\t\t\t\tsetMessages,\n\t\t\t\tsetRunId: (next) => {\n\t\t\t\t\tsetRunId(next);\n\t\t\t\t\ttransportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);\n\t\t\t\t\ttransportOptionsRef.current?.websocket?.onRunIdChange?.(next);\n\t\t\t\t},\n\t\t\t}),\n\t\t[appendPart, appendText],\n\t);\n\n\tconst stop = useCallback(() => {\n\t\tconst activeRun = runRef.current;\n\t\tif (!activeRun?.stop) {\n\t\t\treturn;\n\t\t}\n\n\t\tmanuallyStoppedRef.current = true;\n\t\tvoid activeRun.stop();\n\t\trunRef.current = null;\n\t\tsetIsLoading(false);\n\t}, []);\n\n\tconst sendMessage = useCallback(\n\t\t(text: string) => {\n\t\t\tif (runRef.current || isLoading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst userMessage = createMessage<TPart>(\"user\", [\n\t\t\t\t{ type: \"text\", text } as unknown as TPart,\n\t\t\t]);\n\t\t\tconst assistantMessage = createMessage<TPart>(\"assistant\", []);\n\n\t\t\tsetMessages((prev) => {\n\t\t\t\tconst next = [...prev, userMessage, assistantMessage];\n\t\t\t\tmessagesRef.current = next;\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t\tonMessage?.(userMessage);\n\t\t\tsetError(null);\n\t\t\tsetIsLoading(true);\n\n\t\t\t// Reset the resume guard so a future resume for a new run is allowed.\n\t\t\tresumedRunIdRef.current = null;\n\t\t\tmanuallyStoppedRef.current = false;\n\n\t\t\tconst run = transport.start({ context: transportContext, message: text });\n\t\t\trunRef.current = run ?? null;\n\t\t\tif (run?.runId) {\n\t\t\t\tsetRunId(run.runId);\n\t\t\t}\n\t\t},\n\t\t[isLoading, onMessage, transport, transportContext],\n\t);\n\n\t// -----------------------------------------------------------------------\n\t// Auto-resume effect: attempt to reconnect to an in-flight run on mount.\n\t//\n\t// The candidate run id is read from transport options (which may change\n\t// when the app updates state). We guard against duplicate resumes for the\n\t// same run id using `resumedRunIdRef`.\n\t// -----------------------------------------------------------------------\n\n\tconst candidateRunId =\n\t\toptions.transportOptions?.backgroundSSE?.runId ??\n\t\toptions.transportOptions?.backgroundSSE?.getResumeKey?.() ??\n\t\toptions.transportOptions?.websocket?.runId ??\n\t\toptions.transportOptions?.websocket?.getResumeKey?.() ??\n\t\tnull;\n\n\tuseEffect(() => {\n\t\t// Already have an active run — don't start another.\n\t\tif (runRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User explicitly stopped — don't auto-resume until next sendMessage.\n\t\tif (manuallyStoppedRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Already resumed this exact run id — don't retry.\n\t\tif (resumedRunIdRef.current === candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!transport.resume) {\n\t\t\treturn;\n\t\t}\n\n\t\tresumedRunIdRef.current = candidateRunId;\n\n\t\tsetError(null);\n\t\tsetIsLoading(true);\n\t\tconst run = transport.resume({\n\t\t\tcontext: transportContext,\n\t\t\trunId: candidateRunId,\n\t\t});\n\t\trunRef.current = run ?? null;\n\t\tsetRunId(candidateRunId);\n\t}, [candidateRunId, transport, transportContext]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tvoid runRef.current?.close?.();\n\t\t\trunRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst prevIsLoadingRef = useRef(isLoading);\n\tuseEffect(() => {\n\t\t// Only clear the run ref on a true → false transition, not on mount\n\t\t// where isLoading starts as false. Clearing on mount would race with\n\t\t// the auto-resume effect and null out the run it just created.\n\t\tif (prevIsLoadingRef.current && !isLoading) {\n\t\t\trunRef.current = null;\n\t\t}\n\t\tprevIsLoadingRef.current = isLoading;\n\t}, [isLoading]);\n\n\treturn {\n\t\terror,\n\t\tisLoading,\n\t\tmessages,\n\t\trunId,\n\t\tsendMessage,\n\t\tsetMessages,\n\t\tstop,\n\t};\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport type {\n\tChatTransportContext,\n\tEventHelpers,\n\tUseStreamChatOptions,\n} from \"./types\";\n\nexport interface ChatControllerOptions<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n> {\n\tappendPart: EventHelpers<TPart>[\"appendPart\"];\n\tappendText: EventHelpers<TPart>[\"appendText\"];\n\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) => void;\n\tgetMessages: () => Message<TPart>[];\n\tonError?: UseStreamChatOptions<TPart, TEvent>[\"onError\"];\n\tonFinish?: UseStreamChatOptions<TPart, TEvent>[\"onFinish\"];\n\tonMessage?: UseStreamChatOptions<TPart, TEvent>[\"onMessage\"];\n\tsetError: Dispatch<SetStateAction<Error | null>>;\n\tsetIsLoading: Dispatch<SetStateAction<boolean>>;\n\tsetMessages: Dispatch<SetStateAction<Message<TPart>[]>>;\n\tsetRunId: (runId: string | null) => void;\n}\n\nlet counter = 0;\n\nexport function generateId(): string {\n\treturn `msg_${Date.now()}_${++counter}`;\n}\n\nexport function createMessage<TPart extends { type: string }>(\n\trole: Message[\"role\"],\n\tparts: TPart[],\n): Message<TPart> {\n\treturn { id: generateId(), role, parts };\n}\n\nexport function createChatTransportContext<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(\n\toptions: ChatControllerOptions<TPart, TEvent>,\n): ChatTransportContext<TPart, TEvent> {\n\tconst helpers: EventHelpers<TPart> = {\n\t\tappendPart: options.appendPart,\n\t\tappendText: options.appendText,\n\t\tsetMessages: options.setMessages,\n\t};\n\n\treturn {\n\t\temit: (event) => {\n\t\t\toptions.eventHandler(event, helpers);\n\t\t},\n\t\tensureAssistantMessage: () => {\n\t\t\toptions.setMessages((prev) => {\n\t\t\t\tconst last = prev[prev.length - 1];\n\t\t\t\tif (last?.role === \"assistant\") {\n\t\t\t\t\treturn prev;\n\t\t\t\t}\n\n\t\t\t\treturn [...prev, createMessage<TPart>(\"assistant\", [])];\n\t\t\t});\n\t\t},\n\t\tfail: (error) => {\n\t\t\toptions.setError(error);\n\t\t\toptions.onError?.(error);\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\t\t},\n\t\tfinish: () => {\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\n\t\t\tconst finalMessages = options.getMessages();\n\t\t\tconst lastMessage = finalMessages[finalMessages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\toptions.onMessage?.(lastMessage);\n\t\t\t}\n\n\t\t\toptions.onFinish?.(finalMessages);\n\t\t},\n\t\tgetMessages: options.getMessages,\n\t\tsetRunId: (runId) => {\n\t\t\toptions.setRunId(runId);\n\t\t},\n\t};\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { parseSSEStream } from \"@deltakit/core\";\nimport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tUseStreamChatOptions,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction resolveRunId(response: unknown): string {\n\tif (!response || typeof response !== \"object\") {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\tconst maybeRunId =\n\t\t\"runId\" in response\n\t\t\t? response.runId\n\t\t\t: \"job_id\" in response\n\t\t\t\t? response.job_id\n\t\t\t\t: null;\n\n\tif (typeof maybeRunId !== \"string\" || maybeRunId.length === 0) {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\treturn maybeRunId;\n}\n\nfunction resolveUrl(\n\turl: string | ((runId: string) => string),\n\trunId: string,\n): string {\n\treturn typeof url === \"function\" ? url(runId) : url.replace(\":runId\", runId);\n}\n\nasync function streamFetchSSE<\n\tTPart extends { type: string },\n\tTEvent extends { type: string },\n>(\n\tresponse: Response,\n\tcontext: ChatTransportContext<TPart, TEvent>,\n\tsignal: AbortSignal,\n): Promise<void> {\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`SSE request failed: ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"Response body is null — SSE streaming not supported\");\n\t}\n\n\tfor await (const event of parseSSEStream(response.body, signal)) {\n\t\tcontext.emit(event as unknown as TEvent);\n\t}\n}\n\nexport function createDirectSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: DirectSSETransportOptions): ChatTransport<TPart, TEvent> {\n\treturn {\n\t\tstart: ({ context, message }) => {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst run: ChatTransportRun = {\n\t\t\t\tclose: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst fetchImpl = config.fetch ?? fetch;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.api, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.body }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.headers,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.method ?? \"POST\",\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\t\tcontext.finish();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn run;\n\t\t},\n\t};\n}\n\nexport function createBackgroundSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: BackgroundSSETransportOptions): ChatTransport<TPart, TEvent> {\n\tconst fetchImpl = config.fetch ?? fetch;\n\n\tconst connect = (\n\t\trunId: string,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t): ChatTransportRun => {\n\t\tconst controller = new AbortController();\n\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst response = await fetchImpl(resolveUrl(config.eventsApi, runId), {\n\t\t\t\t\theaders: config.eventHeaders,\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\tcontext.finish();\n\t\t\t} catch (error) {\n\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t},\n\t\t\tstop: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t\tif (config.cancelApi) {\n\t\t\t\t\tvoid fetchImpl(resolveUrl(config.cancelApi, runId), {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\trunId,\n\t\t};\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\treturn connect(runId, context);\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst startController = new AbortController();\n\t\t\tlet activeRun: ChatTransportRun | undefined;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.startApi, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.startBody }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.startHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.startMethod ?? \"POST\",\n\t\t\t\t\t\tsignal: startController.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Background SSE start failed: ${response.status} ${response.statusText}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as unknown;\n\t\t\t\t\tconst runId = (config.resolveRunId ?? resolveRunId)(data);\n\t\t\t\t\tcontext.setRunId(runId);\n\t\t\t\t\tactiveRun = connect(runId, context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tvoid activeRun?.close?.();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tactiveRun?.stop?.();\n\t\t\t\t},\n\t\t\t\trunId: null,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction defaultParseWebSocketMessage<TEvent extends { type: string }>(\n\tdata: unknown,\n): TEvent | TEvent[] | null {\n\tif (typeof data !== \"string\") {\n\t\treturn null;\n\t}\n\n\tconst parsed = JSON.parse(data) as TEvent | TEvent[];\n\treturn parsed;\n}\n\nexport function createWebSocketTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: WebSocketTransportOptions<TEvent>): ChatTransport<TPart, TEvent> {\n\tconst parseMessage = config.parseMessage ?? defaultParseWebSocketMessage;\n\tconst serializeMessage = config.serializeMessage ?? JSON.stringify;\n\n\tlet resolvedRunId: string | null = null;\n\n\tconst applyIncomingEvents = (\n\t\tparsed: TEvent | TEvent[] | null,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t) => {\n\t\tconst events = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];\n\t\tfor (const item of events) {\n\t\t\tconst nextRunId = config.resolveRunId?.(item) ?? null;\n\t\t\tif (nextRunId) {\n\t\t\t\tresolvedRunId = nextRunId;\n\t\t\t\tcontext.setRunId(nextRunId);\n\t\t\t}\n\t\t\tcontext.emit(item);\n\n\t\t\tif (item.type === \"done\") {\n\t\t\t\tcontext.finish();\n\t\t\t} else if (item.type === \"error\") {\n\t\t\t\tcontext.fail(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\"message\" in item && typeof item.message === \"string\"\n\t\t\t\t\t\t\t? item.message\n\t\t\t\t\t\t\t: \"Stream error\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\tcontext.ensureAssistantMessage();\n\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentResumePayload = false;\n\n\t\t\tconst sendResumePayload = () => {\n\t\t\t\tif (sentResumePayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentResumePayload = true;\n\t\t\t\tconst payload =\n\t\t\t\t\tconfig.buildResumePayload?.(runId) ??\n\t\t\t\t\t({ [config.runIdKey ?? \"runId\"]: runId } as Record<string, unknown>);\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendResumePayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendResumePayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst runId = config.runId ?? config.getResumeKey?.() ?? null;\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentStartPayload = false;\n\n\t\t\tconst sendStartPayload = () => {\n\t\t\t\tif (sentStartPayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentStartPayload = true;\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst payload: Record<string, unknown> = {\n\t\t\t\t\tmessage,\n\t\t\t\t\t...config.body,\n\t\t\t\t};\n\n\t\t\t\tif (runId) {\n\t\t\t\t\tpayload[config.runIdKey ?? \"runId\"] = runId;\n\t\t\t\t}\n\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendStartPayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendStartPayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function resolveTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): ChatTransport<TPart, TEvent> {\n\tif (typeof options.transport === \"object\" && options.transport) {\n\t\treturn options.transport;\n\t}\n\n\tconst transportKind = options.transport ?? \"sse\";\n\n\tif (transportKind === \"background-sse\") {\n\t\tconst config = options.transportOptions?.backgroundSSE;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.backgroundSSE` is required when transport is \"background-sse\"',\n\t\t\t);\n\t\t}\n\t\treturn createBackgroundSSETransport(config);\n\t}\n\n\tif (transportKind === \"websocket\") {\n\t\tconst config = options.transportOptions?.websocket;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.websocket` is required when transport is \"websocket\"',\n\t\t\t);\n\t\t}\n\t\treturn createWebSocketTransport(config);\n\t}\n\n\tconst sseConfig = options.transportOptions?.sse ?? {\n\t\tapi: options.api,\n\t\tbody: options.body,\n\t\theaders: options.headers,\n\t};\n\n\tif (!sseConfig.api) {\n\t\tthrow new Error(\n\t\t\t\"`api` or `transportOptions.sse.api` is required when using the default SSE transport\",\n\t\t);\n\t}\n\n\treturn createDirectSSETransport({\n\t\t...sseConfig,\n\t\tapi: sseConfig.api,\n\t});\n}\n"],"mappings":"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","names":["import_core","import_react"]}
package/dist/index.d.cts CHANGED
@@ -163,11 +163,6 @@ interface UseStreamChatOptions<TPart extends {
163
163
  headers?: Record<string, string>;
164
164
  /** Extra fields merged into the direct-SSE POST body alongside `message`. */
165
165
  body?: Record<string, unknown>;
166
- /**
167
- * Buffer incoming `text_delta` events and commit them to React state on a
168
- * short cadence instead of every token. Default: `0` (disabled).
169
- */
170
- textBatchMs?: number;
171
166
  /**
172
167
  * Custom handler for each SSE event. When provided, this **replaces** the
173
168
  * default `text_delta` handling — giving you full control over how events
package/dist/index.d.ts CHANGED
@@ -163,11 +163,6 @@ interface UseStreamChatOptions<TPart extends {
163
163
  headers?: Record<string, string>;
164
164
  /** Extra fields merged into the direct-SSE POST body alongside `message`. */
165
165
  body?: Record<string, unknown>;
166
- /**
167
- * Buffer incoming `text_delta` events and commit them to React state on a
168
- * short cadence instead of every token. Default: `0` (disabled).
169
- */
170
- textBatchMs?: number;
171
166
  /**
172
167
  * Custom handler for each SSE event. When provided, this **replaces** the
173
168
  * default `text_delta` handling — giving you full control over how events
package/dist/index.js CHANGED
@@ -18,56 +18,40 @@ function useAutoScroll(dependencies, options) {
18
18
  const isAtBottomRef = useRef(true);
19
19
  const [isAtBottom, setIsAtBottom] = useState(true);
20
20
  const rafRef = useRef(null);
21
- const smoothRafRef = useRef(null);
22
- const smoothTargetRef = useRef(null);
23
21
  const lastAutoScrollHeightRef = useRef(null);
24
- const cancelSmoothScroll = useCallback(() => {
25
- if (smoothRafRef.current != null) {
26
- cancelAnimationFrame(smoothRafRef.current);
27
- smoothRafRef.current = null;
28
- }
29
- smoothTargetRef.current = null;
30
- }, []);
31
- const runSmoothScroll = useCallback(() => {
32
- if (smoothRafRef.current != null) return;
22
+ const pendingVerificationFramesRef = useRef(0);
23
+ const scheduleScroll = useCallback(() => {
24
+ pendingVerificationFramesRef.current = Math.max(
25
+ pendingVerificationFramesRef.current,
26
+ 1
27
+ );
28
+ if (rafRef.current != null) return;
33
29
  const tick = () => {
30
+ rafRef.current = null;
34
31
  const el = ref.current;
35
32
  if (!el || !isAtBottomRef.current) {
36
- cancelSmoothScroll();
37
- return;
38
- }
39
- const target = smoothTargetRef.current ?? el.scrollHeight;
40
- const delta = target - el.scrollTop;
41
- if (delta <= 1) {
42
- el.scrollTop = target;
43
- lastAutoScrollHeightRef.current = target;
44
- smoothRafRef.current = null;
33
+ pendingVerificationFramesRef.current = 0;
45
34
  return;
46
35
  }
47
- const step = Math.min(Math.max(delta * 0.35, 12), 120);
48
- el.scrollTop = Math.min(target, el.scrollTop + step);
49
- smoothRafRef.current = requestAnimationFrame(tick);
50
- };
51
- smoothRafRef.current = requestAnimationFrame(tick);
52
- }, [cancelSmoothScroll]);
53
- const scheduleScroll = useCallback(() => {
54
- if (rafRef.current != null) return;
55
- rafRef.current = requestAnimationFrame(() => {
56
- rafRef.current = null;
57
- const el = ref.current;
58
- if (!el || !isAtBottomRef.current) return;
59
36
  const nextHeight = el.scrollHeight;
60
- if (lastAutoScrollHeightRef.current === nextHeight) return;
61
- lastAutoScrollHeightRef.current = nextHeight;
62
- if (behavior === "smooth") {
63
- smoothTargetRef.current = nextHeight;
64
- runSmoothScroll();
65
- return;
37
+ const heightChanged = lastAutoScrollHeightRef.current !== nextHeight;
38
+ const distanceFromBottom = nextHeight - el.scrollTop - el.clientHeight;
39
+ if (heightChanged || distanceFromBottom > 0) {
40
+ lastAutoScrollHeightRef.current = nextHeight;
41
+ pendingVerificationFramesRef.current = 1;
42
+ el.scrollTop = nextHeight;
43
+ } else {
44
+ pendingVerificationFramesRef.current = Math.max(
45
+ pendingVerificationFramesRef.current - 1,
46
+ 0
47
+ );
66
48
  }
67
- cancelSmoothScroll();
68
- el.scrollTop = nextHeight;
69
- });
70
- }, [behavior, cancelSmoothScroll, runSmoothScroll]);
49
+ if (pendingVerificationFramesRef.current > 0) {
50
+ rafRef.current = requestAnimationFrame(tick);
51
+ }
52
+ };
53
+ rafRef.current = requestAnimationFrame(tick);
54
+ }, []);
71
55
  useEffect(() => {
72
56
  const el = ref.current;
73
57
  if (!el || !enabled) return;
@@ -76,7 +60,7 @@ function useAutoScroll(dependencies, options) {
76
60
  isAtBottomRef.current = atBottom;
77
61
  if (!atBottom) {
78
62
  lastAutoScrollHeightRef.current = null;
79
- cancelSmoothScroll();
63
+ pendingVerificationFramesRef.current = 0;
80
64
  }
81
65
  setIsAtBottom((prev) => prev === atBottom ? prev : atBottom);
82
66
  };
@@ -117,18 +101,18 @@ function useAutoScroll(dependencies, options) {
117
101
  cancelAnimationFrame(rafRef.current);
118
102
  rafRef.current = null;
119
103
  }
120
- cancelSmoothScroll();
104
+ pendingVerificationFramesRef.current = 0;
121
105
  };
122
- }, [cancelSmoothScroll]);
106
+ }, []);
123
107
  const scrollToBottom = useCallback(() => {
124
108
  const el = ref.current;
125
109
  if (!el) return;
126
- cancelSmoothScroll();
127
110
  isAtBottomRef.current = true;
128
111
  lastAutoScrollHeightRef.current = el.scrollHeight;
112
+ pendingVerificationFramesRef.current = 0;
129
113
  setIsAtBottom(true);
130
114
  el.scrollTo({ top: el.scrollHeight, behavior });
131
- }, [behavior, cancelSmoothScroll]);
115
+ }, [behavior]);
132
116
  return { ref, scrollToBottom, isAtBottom };
133
117
  }
134
118
 
@@ -528,14 +512,7 @@ function defaultOnEvent(event, helpers) {
528
512
  }
529
513
  }
530
514
  function useStreamChat(options) {
531
- const {
532
- initialMessages,
533
- onEvent,
534
- onMessage,
535
- onError,
536
- onFinish,
537
- textBatchMs = 0
538
- } = options;
515
+ const { initialMessages, onEvent, onMessage, onError, onFinish } = options;
539
516
  const [messages, setMessages] = useState2(initialMessages ?? []);
540
517
  const [isLoading, setIsLoading] = useState2(false);
541
518
  const [error, setError] = useState2(null);
@@ -545,29 +522,10 @@ function useStreamChat(options) {
545
522
  const manuallyStoppedRef = useRef2(false);
546
523
  const messagesRef = useRef2(messages);
547
524
  messagesRef.current = messages;
548
- const updateMessages = useCallback2(
549
- (next) => {
550
- setMessages((prev) => {
551
- const resolved = typeof next === "function" ? next(prev) : next;
552
- messagesRef.current = resolved;
553
- return resolved;
554
- });
555
- },
556
- []
557
- );
558
525
  const transportOptionsRef = useRef2(options.transportOptions);
559
526
  transportOptionsRef.current = options.transportOptions;
560
- const pendingTextRef = useRef2("");
561
- const textFlushTimerRef = useRef2(null);
562
- const flushPendingText = useCallback2(() => {
563
- if (textFlushTimerRef.current !== null) {
564
- clearTimeout(textFlushTimerRef.current);
565
- textFlushTimerRef.current = null;
566
- }
567
- const delta = pendingTextRef.current;
568
- if (!delta) return;
569
- pendingTextRef.current = "";
570
- updateMessages((prev) => {
527
+ const appendText = useCallback2((delta) => {
528
+ setMessages((prev) => {
571
529
  const last = prev[prev.length - 1];
572
530
  if (!last || last.role !== "assistant") return prev;
573
531
  const parts = [...last.parts];
@@ -583,22 +541,9 @@ function useStreamChat(options) {
583
541
  }
584
542
  return [...prev.slice(0, -1), { ...last, parts }];
585
543
  });
586
- }, [updateMessages]);
587
- const appendText = useCallback2((delta) => {
588
- if (textBatchMs <= 0) {
589
- pendingTextRef.current += delta;
590
- flushPendingText();
591
- return;
592
- }
593
- pendingTextRef.current += delta;
594
- if (textFlushTimerRef.current !== null) return;
595
- textFlushTimerRef.current = setTimeout(() => {
596
- flushPendingText();
597
- }, textBatchMs);
598
- }, [flushPendingText, textBatchMs]);
544
+ }, []);
599
545
  const appendPart = useCallback2((part) => {
600
- flushPendingText();
601
- updateMessages((prev) => {
546
+ setMessages((prev) => {
602
547
  const last = prev[prev.length - 1];
603
548
  if (!last || last.role !== "assistant") return prev;
604
549
  return [
@@ -609,7 +554,7 @@ function useStreamChat(options) {
609
554
  }
610
555
  ];
611
556
  });
612
- }, [flushPendingText, updateMessages]);
557
+ }, []);
613
558
  const transportRef = useRef2(null);
614
559
  if (!transportRef.current) {
615
560
  transportRef.current = resolveTransport(options);
@@ -629,23 +574,20 @@ function useStreamChat(options) {
629
574
  appendPart,
630
575
  appendText,
631
576
  eventHandler: (event, helpers) => eventHandlerRef.current(event, helpers),
632
- getMessages: () => {
633
- flushPendingText();
634
- return messagesRef.current;
635
- },
577
+ getMessages: () => messagesRef.current,
636
578
  onError: (...args) => onErrorRef.current?.(...args),
637
579
  onFinish: (...args) => onFinishRef.current?.(...args),
638
580
  onMessage: (...args) => onMessageRef.current?.(...args),
639
581
  setError,
640
582
  setIsLoading,
641
- setMessages: updateMessages,
583
+ setMessages,
642
584
  setRunId: (next) => {
643
585
  setRunId(next);
644
586
  transportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);
645
587
  transportOptionsRef.current?.websocket?.onRunIdChange?.(next);
646
588
  }
647
589
  }),
648
- [appendPart, appendText, flushPendingText, updateMessages]
590
+ [appendPart, appendText]
649
591
  );
650
592
  const stop = useCallback2(() => {
651
593
  const activeRun = runRef.current;
@@ -666,8 +608,9 @@ function useStreamChat(options) {
666
608
  { type: "text", text }
667
609
  ]);
668
610
  const assistantMessage = createMessage("assistant", []);
669
- updateMessages((prev) => {
611
+ setMessages((prev) => {
670
612
  const next = [...prev, userMessage, assistantMessage];
613
+ messagesRef.current = next;
671
614
  return next;
672
615
  });
673
616
  onMessage?.(userMessage);
@@ -681,7 +624,7 @@ function useStreamChat(options) {
681
624
  setRunId(run.runId);
682
625
  }
683
626
  },
684
- [isLoading, onMessage, transport, transportContext, updateMessages]
627
+ [isLoading, onMessage, transport, transportContext]
685
628
  );
686
629
  const candidateRunId = options.transportOptions?.backgroundSSE?.runId ?? options.transportOptions?.backgroundSSE?.getResumeKey?.() ?? options.transportOptions?.websocket?.runId ?? options.transportOptions?.websocket?.getResumeKey?.() ?? null;
687
630
  useEffect2(() => {
@@ -712,10 +655,6 @@ function useStreamChat(options) {
712
655
  }, [candidateRunId, transport, transportContext]);
713
656
  useEffect2(() => {
714
657
  return () => {
715
- if (textFlushTimerRef.current !== null) {
716
- clearTimeout(textFlushTimerRef.current);
717
- textFlushTimerRef.current = null;
718
- }
719
658
  void runRef.current?.close?.();
720
659
  runRef.current = null;
721
660
  };
@@ -733,7 +672,7 @@ function useStreamChat(options) {
733
672
  messages,
734
673
  runId,
735
674
  sendMessage,
736
- setMessages: updateMessages,
675
+ setMessages,
737
676
  stop
738
677
  };
739
678
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/use-auto-scroll.ts","../src/use-stream-chat.ts","../src/chat-controller.ts","../src/transports.ts"],"sourcesContent":["// Re-export core types so consumers only need to import from @deltakit/react\nexport type {\n\tContentPart,\n\tMessage,\n\tReasoningPart,\n\tSSEEvent,\n\tTextDeltaEvent,\n\tTextPart,\n\tToolCallEvent,\n\tToolCallPart,\n\tToolResultEvent,\n} from \"@deltakit/core\";\nexport {\n\tfromAgnoAgents,\n\tfromOpenAiAgents,\n\tparseSSEStream,\n} from \"@deltakit/core\";\n\nexport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tEventHelpers,\n\tTransportOptions,\n\tUseAutoScrollOptions,\n\tUseAutoScrollReturn,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n\tWebSocketTransportOptions,\n} from \"./types\";\nexport { useAutoScroll } from \"./use-auto-scroll\";\nexport { useStreamChat } from \"./use-stream-chat\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseAutoScrollOptions, UseAutoScrollReturn } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_THRESHOLD = 50;\n\n// ---------------------------------------------------------------------------\n// useAutoScroll\n// ---------------------------------------------------------------------------\n\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>(\n\tdependencies: unknown[],\n\toptions?: UseAutoScrollOptions,\n): UseAutoScrollReturn<T> {\n\tconst {\n\t\tbehavior = \"instant\",\n\t\tenabled = true,\n\t\tthreshold = DEFAULT_THRESHOLD,\n\t} = options ?? {};\n\n\tconst ref = useRef<T | null>(null);\n\tconst isAtBottomRef = useRef(true);\n\tconst [isAtBottom, setIsAtBottom] = useState(true);\n\n\t// A single rAF id shared across all scroll sources — ensures we never\n\t// call scrollTo() more than once per frame, no matter how many\n\t// MutationObserver / ResizeObserver callbacks fire.\n\tconst rafRef = useRef<number | null>(null);\n\tconst smoothRafRef = useRef<number | null>(null);\n\tconst smoothTargetRef = useRef<number | null>(null);\n\tconst lastAutoScrollHeightRef = useRef<number | null>(null);\n\n\tconst cancelSmoothScroll = useCallback(() => {\n\t\tif (smoothRafRef.current != null) {\n\t\t\tcancelAnimationFrame(smoothRafRef.current);\n\t\t\tsmoothRafRef.current = null;\n\t\t}\n\t\tsmoothTargetRef.current = null;\n\t}, []);\n\n\tconst runSmoothScroll = useCallback(() => {\n\t\tif (smoothRafRef.current != null) return;\n\n\t\tconst tick = () => {\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) {\n\t\t\t\tcancelSmoothScroll();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst target = smoothTargetRef.current ?? el.scrollHeight;\n\t\t\tconst delta = target - el.scrollTop;\n\n\t\t\tif (delta <= 1) {\n\t\t\t\tel.scrollTop = target;\n\t\t\t\tlastAutoScrollHeightRef.current = target;\n\t\t\t\tsmoothRafRef.current = null;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst step = Math.min(Math.max(delta * 0.35, 12), 120);\n\t\t\tel.scrollTop = Math.min(target, el.scrollTop + step);\n\t\t\tsmoothRafRef.current = requestAnimationFrame(tick);\n\t\t};\n\n\t\tsmoothRafRef.current = requestAnimationFrame(tick);\n\t}, [cancelSmoothScroll]);\n\n\tconst scheduleScroll = useCallback(() => {\n\t\tif (rafRef.current != null) return;\n\t\trafRef.current = requestAnimationFrame(() => {\n\t\t\trafRef.current = null;\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) return;\n\n\t\t\tconst nextHeight = el.scrollHeight;\n\t\t\tif (lastAutoScrollHeightRef.current === nextHeight) return;\n\n\t\t\tlastAutoScrollHeightRef.current = nextHeight;\n\n\t\t\tif (behavior === \"smooth\") {\n\t\t\t\tsmoothTargetRef.current = nextHeight;\n\t\t\t\trunSmoothScroll();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcancelSmoothScroll();\n\t\t\tel.scrollTop = nextHeight;\n\t\t});\n\t}, [behavior, cancelSmoothScroll, runSmoothScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Track whether the user is near the bottom via scroll events.\n\t// Only triggers a React re-render when the boolean actually changes.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst handleScroll = () => {\n\t\t\tconst atBottom =\n\t\t\t\tel.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n\t\t\tisAtBottomRef.current = atBottom;\n\t\t\tif (!atBottom) {\n\t\t\t\tlastAutoScrollHeightRef.current = null;\n\t\t\t\tcancelSmoothScroll();\n\t\t\t}\n\t\t\tsetIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n\t\t};\n\n\t\tel.addEventListener(\"scroll\", handleScroll, { passive: true });\n\t\thandleScroll();\n\t\treturn () => el.removeEventListener(\"scroll\", handleScroll);\n\t}, [enabled, threshold]);\n\n\t// -----------------------------------------------------------------------\n\t// Scroll to bottom when dependencies change (if pinned).\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tif (!enabled || !isAtBottomRef.current) return;\n\t\tscheduleScroll();\n\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies are passed dynamically by the consumer\n\t}, dependencies);\n\n\t// -----------------------------------------------------------------------\n\t// MutationObserver + ResizeObserver — catch content changes during\n\t// streaming that happen between React re-renders (e.g. DOM mutations\n\t// from markdown renderers). Scroll calls are batched via rAF so we\n\t// scroll at most once per frame.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst resizeObserver = new ResizeObserver(scheduleScroll);\n\n\t\t// Observe existing children for size changes.\n\t\tfor (const child of el.children) {\n\t\t\tresizeObserver.observe(child);\n\t\t}\n\n\t\t// Watch for new children added to the container.\n\t\tconst mutationObserver = new MutationObserver((mutations) => {\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tfor (const node of mutation.addedNodes) {\n\t\t\t\t\tif (node instanceof Element) {\n\t\t\t\t\t\tresizeObserver.observe(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tscheduleScroll();\n\t\t});\n\n\t\tmutationObserver.observe(el, { childList: true, subtree: true });\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [enabled, scheduleScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Cancel any pending rAF on unmount.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (rafRef.current != null) {\n\t\t\t\tcancelAnimationFrame(rafRef.current);\n\t\t\t\trafRef.current = null;\n\t\t\t}\n\t\t\tcancelSmoothScroll();\n\t\t};\n\t}, [cancelSmoothScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// scrollToBottom — imperative function that scrolls to the bottom and\n\t// re-pins auto-scroll.\n\t// -----------------------------------------------------------------------\n\n\tconst scrollToBottom = useCallback(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tcancelSmoothScroll();\n\t\tisAtBottomRef.current = true;\n\t\tlastAutoScrollHeightRef.current = el.scrollHeight;\n\t\tsetIsAtBottom(true);\n\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t}, [behavior, cancelSmoothScroll]);\n\n\treturn { ref, scrollToBottom, isAtBottom };\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { createChatTransportContext, createMessage } from \"./chat-controller\";\nimport { resolveTransport } from \"./transports\";\nimport type {\n\tChatTransportRun,\n\tEventHelpers,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Default event handler — accumulates `text_delta` into the last\n// assistant message's parts.\n// ---------------------------------------------------------------------------\n\nfunction defaultOnEvent(\n\tevent: SSEEvent,\n\thelpers: EventHelpers<ContentPart>,\n): void {\n\tif (event.type === \"text_delta\") {\n\t\thelpers.appendText(event.delta);\n\t}\n\t// Other event types (e.g. tool_call) are silently ignored by default.\n\t// Users can provide their own `onEvent` to handle them.\n}\n\n// ---------------------------------------------------------------------------\n// useStreamChat\n// ---------------------------------------------------------------------------\n\nexport function useStreamChat<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): UseStreamChatReturn<TPart> {\n\tconst {\n\t\tinitialMessages,\n\t\tonEvent,\n\t\tonMessage,\n\t\tonError,\n\t\tonFinish,\n\t\ttextBatchMs = 0,\n\t} = options;\n\n\tconst [messages, setMessages] = useState(initialMessages ?? []);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [runId, setRunId] = useState<string | null>(null);\n\n\tconst runRef = useRef<ChatTransportRun | null>(null);\n\n\t// Track which run id has already been resumed to prevent re-triggering.\n\tconst resumedRunIdRef = useRef<string | null>(null);\n\n\t// When the user manually stops a run, suppress auto-resume until the\n\t// next explicit `sendMessage` call.\n\tconst manuallyStoppedRef = useRef(false);\n\n\t// We use a ref for the latest messages so callbacks created inside\n\t// transport handlers always see the current value without re-creating\n\t// closures.\n\tconst messagesRef = useRef(messages);\n\tmessagesRef.current = messages;\n\n\tconst updateMessages = useCallback(\n\t\t(next:\n\t\t\t| Message<TPart>[]\n\t\t\t| ((prev: Message<TPart>[]) => Message<TPart>[])) => {\n\t\t\tsetMessages((prev) => {\n\t\t\t\tconst resolved =\n\t\t\t\t\ttypeof next === \"function\"\n\t\t\t\t\t\t? (next as (prev: Message<TPart>[]) => Message<TPart>[])(prev)\n\t\t\t\t\t\t: next;\n\t\t\t\tmessagesRef.current = resolved;\n\t\t\t\treturn resolved;\n\t\t\t});\n\t\t},\n\t\t[],\n\t);\n\n\t// Keep transport options in a ref so that callbacks always read the\n\t// latest values without causing memoisation instability.\n\tconst transportOptionsRef = useRef(options.transportOptions);\n\ttransportOptionsRef.current = options.transportOptions;\n\n\tconst pendingTextRef = useRef(\"\");\n\tconst textFlushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\tconst flushPendingText = useCallback(() => {\n\t\tif (textFlushTimerRef.current !== null) {\n\t\t\tclearTimeout(textFlushTimerRef.current);\n\t\t\ttextFlushTimerRef.current = null;\n\t\t}\n\n\t\tconst delta = pendingTextRef.current;\n\t\tif (!delta) return;\n\n\t\tpendingTextRef.current = \"\";\n\t\tupdateMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\tconst parts = [...last.parts];\n\t\t\tconst lastPart = parts[parts.length - 1];\n\n\t\t\tif (lastPart && lastPart.type === \"text\" && \"text\" in lastPart) {\n\t\t\t\tconst textPart = lastPart as { type: \"text\"; text: string };\n\t\t\t\tparts[parts.length - 1] = {\n\t\t\t\t\t...lastPart,\n\t\t\t\t\ttext: textPart.text + delta,\n\t\t\t\t} as unknown as TPart;\n\t\t\t} else {\n\t\t\t\tparts.push({ type: \"text\", text: delta } as unknown as TPart);\n\t\t\t}\n\n\t\t\treturn [...prev.slice(0, -1), { ...last, parts }];\n\t\t});\n\t}, [updateMessages]);\n\n\tconst appendText = useCallback((delta: string) => {\n\t\tif (textBatchMs <= 0) {\n\t\t\tpendingTextRef.current += delta;\n\t\t\tflushPendingText();\n\t\t\treturn;\n\t\t}\n\n\t\tpendingTextRef.current += delta;\n\t\tif (textFlushTimerRef.current !== null) return;\n\n\t\ttextFlushTimerRef.current = setTimeout(() => {\n\t\t\tflushPendingText();\n\t\t}, textBatchMs);\n\t}, [flushPendingText, textBatchMs]);\n\n\tconst appendPart = useCallback((part: TPart) => {\n\t\tflushPendingText();\n\t\tupdateMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\treturn [\n\t\t\t\t...prev.slice(0, -1),\n\t\t\t\t{\n\t\t\t\t\t...last,\n\t\t\t\t\tparts: [...last.parts, part],\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t}, [flushPendingText, updateMessages]);\n\n\t// Stabilise transport creation: resolve once and store in a ref so that\n\t// changing values like `runId` in transportOptions won't cause a new\n\t// transport instance (and therefore a new WebSocket) to be created.\n\tconst transportRef = useRef<ReturnType<\n\t\ttypeof resolveTransport<TPart, TEvent>\n\t> | null>(null);\n\tif (!transportRef.current) {\n\t\ttransportRef.current = resolveTransport(options);\n\t}\n\tconst transport = transportRef.current;\n\n\tconst eventHandler =\n\t\tonEvent ??\n\t\t(defaultOnEvent as unknown as (\n\t\t\tevent: TEvent,\n\t\t\thelpers: EventHelpers<TPart>,\n\t\t) => void);\n\n\t// Stabilise the transport context: use refs for values that change\n\t// frequently (transportOptions callbacks) so the context object itself\n\t// stays referentially stable.\n\tconst eventHandlerRef = useRef(eventHandler);\n\teventHandlerRef.current = eventHandler;\n\n\tconst onErrorRef = useRef(onError);\n\tonErrorRef.current = onError;\n\n\tconst onFinishRef = useRef(onFinish);\n\tonFinishRef.current = onFinish;\n\n\tconst onMessageRef = useRef(onMessage);\n\tonMessageRef.current = onMessage;\n\n\tconst transportContext = useMemo(\n\t\t() =>\n\t\t\tcreateChatTransportContext({\n\t\t\t\tappendPart,\n\t\t\t\tappendText,\n\t\t\t\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) =>\n\t\t\t\t\teventHandlerRef.current(event, helpers),\n\t\t\t\tgetMessages: () => {\n\t\t\t\t\tflushPendingText();\n\t\t\t\t\treturn messagesRef.current;\n\t\t\t\t},\n\t\t\t\tonError: (...args) => onErrorRef.current?.(...args),\n\t\t\t\tonFinish: (...args) => onFinishRef.current?.(...args),\n\t\t\t\tonMessage: (...args) => onMessageRef.current?.(...args),\n\t\t\t\tsetError,\n\t\t\t\tsetIsLoading,\n\t\t\t\tsetMessages: updateMessages,\n\t\t\t\tsetRunId: (next) => {\n\t\t\t\t\tsetRunId(next);\n\t\t\t\t\ttransportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);\n\t\t\t\t\ttransportOptionsRef.current?.websocket?.onRunIdChange?.(next);\n\t\t\t\t},\n\t\t\t}),\n\t\t[appendPart, appendText, flushPendingText, updateMessages],\n\t);\n\n\tconst stop = useCallback(() => {\n\t\tconst activeRun = runRef.current;\n\t\tif (!activeRun?.stop) {\n\t\t\treturn;\n\t\t}\n\n\t\tmanuallyStoppedRef.current = true;\n\t\tvoid activeRun.stop();\n\t\trunRef.current = null;\n\t\tsetIsLoading(false);\n\t}, []);\n\n\tconst sendMessage = useCallback(\n\t\t(text: string) => {\n\t\t\tif (runRef.current || isLoading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst userMessage = createMessage<TPart>(\"user\", [\n\t\t\t\t{ type: \"text\", text } as unknown as TPart,\n\t\t\t]);\n\t\t\tconst assistantMessage = createMessage<TPart>(\"assistant\", []);\n\n\t\t\tupdateMessages((prev) => {\n\t\t\t\tconst next = [...prev, userMessage, assistantMessage];\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t\tonMessage?.(userMessage);\n\t\t\tsetError(null);\n\t\t\tsetIsLoading(true);\n\n\t\t\t// Reset the resume guard so a future resume for a new run is allowed.\n\t\t\tresumedRunIdRef.current = null;\n\t\t\tmanuallyStoppedRef.current = false;\n\n\t\t\tconst run = transport.start({ context: transportContext, message: text });\n\t\t\trunRef.current = run ?? null;\n\t\t\tif (run?.runId) {\n\t\t\t\tsetRunId(run.runId);\n\t\t\t}\n\t\t},\n\t\t[isLoading, onMessage, transport, transportContext, updateMessages],\n\t);\n\n\t// -----------------------------------------------------------------------\n\t// Auto-resume effect: attempt to reconnect to an in-flight run on mount.\n\t//\n\t// The candidate run id is read from transport options (which may change\n\t// when the app updates state). We guard against duplicate resumes for the\n\t// same run id using `resumedRunIdRef`.\n\t// -----------------------------------------------------------------------\n\n\tconst candidateRunId =\n\t\toptions.transportOptions?.backgroundSSE?.runId ??\n\t\toptions.transportOptions?.backgroundSSE?.getResumeKey?.() ??\n\t\toptions.transportOptions?.websocket?.runId ??\n\t\toptions.transportOptions?.websocket?.getResumeKey?.() ??\n\t\tnull;\n\n\tuseEffect(() => {\n\t\t// Already have an active run — don't start another.\n\t\tif (runRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User explicitly stopped — don't auto-resume until next sendMessage.\n\t\tif (manuallyStoppedRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Already resumed this exact run id — don't retry.\n\t\tif (resumedRunIdRef.current === candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!transport.resume) {\n\t\t\treturn;\n\t\t}\n\n\t\tresumedRunIdRef.current = candidateRunId;\n\n\t\tsetError(null);\n\t\tsetIsLoading(true);\n\t\tconst run = transport.resume({\n\t\t\tcontext: transportContext,\n\t\t\trunId: candidateRunId,\n\t\t});\n\t\trunRef.current = run ?? null;\n\t\tsetRunId(candidateRunId);\n\t}, [candidateRunId, transport, transportContext]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (textFlushTimerRef.current !== null) {\n\t\t\t\tclearTimeout(textFlushTimerRef.current);\n\t\t\t\ttextFlushTimerRef.current = null;\n\t\t\t}\n\t\t\tvoid runRef.current?.close?.();\n\t\t\trunRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst prevIsLoadingRef = useRef(isLoading);\n\tuseEffect(() => {\n\t\t// Only clear the run ref on a true → false transition, not on mount\n\t\t// where isLoading starts as false. Clearing on mount would race with\n\t\t// the auto-resume effect and null out the run it just created.\n\t\tif (prevIsLoadingRef.current && !isLoading) {\n\t\t\trunRef.current = null;\n\t\t}\n\t\tprevIsLoadingRef.current = isLoading;\n\t}, [isLoading]);\n\n\treturn {\n\t\terror,\n\t\tisLoading,\n\t\tmessages,\n\t\trunId,\n\t\tsendMessage,\n\t\tsetMessages: updateMessages,\n\t\tstop,\n\t};\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport type {\n\tChatTransportContext,\n\tEventHelpers,\n\tUseStreamChatOptions,\n} from \"./types\";\n\nexport interface ChatControllerOptions<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n> {\n\tappendPart: EventHelpers<TPart>[\"appendPart\"];\n\tappendText: EventHelpers<TPart>[\"appendText\"];\n\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) => void;\n\tgetMessages: () => Message<TPart>[];\n\tonError?: UseStreamChatOptions<TPart, TEvent>[\"onError\"];\n\tonFinish?: UseStreamChatOptions<TPart, TEvent>[\"onFinish\"];\n\tonMessage?: UseStreamChatOptions<TPart, TEvent>[\"onMessage\"];\n\tsetError: Dispatch<SetStateAction<Error | null>>;\n\tsetIsLoading: Dispatch<SetStateAction<boolean>>;\n\tsetMessages: Dispatch<SetStateAction<Message<TPart>[]>>;\n\tsetRunId: (runId: string | null) => void;\n}\n\nlet counter = 0;\n\nexport function generateId(): string {\n\treturn `msg_${Date.now()}_${++counter}`;\n}\n\nexport function createMessage<TPart extends { type: string }>(\n\trole: Message[\"role\"],\n\tparts: TPart[],\n): Message<TPart> {\n\treturn { id: generateId(), role, parts };\n}\n\nexport function createChatTransportContext<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(\n\toptions: ChatControllerOptions<TPart, TEvent>,\n): ChatTransportContext<TPart, TEvent> {\n\tconst helpers: EventHelpers<TPart> = {\n\t\tappendPart: options.appendPart,\n\t\tappendText: options.appendText,\n\t\tsetMessages: options.setMessages,\n\t};\n\n\treturn {\n\t\temit: (event) => {\n\t\t\toptions.eventHandler(event, helpers);\n\t\t},\n\t\tensureAssistantMessage: () => {\n\t\t\toptions.setMessages((prev) => {\n\t\t\t\tconst last = prev[prev.length - 1];\n\t\t\t\tif (last?.role === \"assistant\") {\n\t\t\t\t\treturn prev;\n\t\t\t\t}\n\n\t\t\t\treturn [...prev, createMessage<TPart>(\"assistant\", [])];\n\t\t\t});\n\t\t},\n\t\tfail: (error) => {\n\t\t\toptions.setError(error);\n\t\t\toptions.onError?.(error);\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\t\t},\n\t\tfinish: () => {\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\n\t\t\tconst finalMessages = options.getMessages();\n\t\t\tconst lastMessage = finalMessages[finalMessages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\toptions.onMessage?.(lastMessage);\n\t\t\t}\n\n\t\t\toptions.onFinish?.(finalMessages);\n\t\t},\n\t\tgetMessages: options.getMessages,\n\t\tsetRunId: (runId) => {\n\t\t\toptions.setRunId(runId);\n\t\t},\n\t};\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { parseSSEStream } from \"@deltakit/core\";\nimport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tUseStreamChatOptions,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction resolveRunId(response: unknown): string {\n\tif (!response || typeof response !== \"object\") {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\tconst maybeRunId =\n\t\t\"runId\" in response\n\t\t\t? response.runId\n\t\t\t: \"job_id\" in response\n\t\t\t\t? response.job_id\n\t\t\t\t: null;\n\n\tif (typeof maybeRunId !== \"string\" || maybeRunId.length === 0) {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\treturn maybeRunId;\n}\n\nfunction resolveUrl(\n\turl: string | ((runId: string) => string),\n\trunId: string,\n): string {\n\treturn typeof url === \"function\" ? url(runId) : url.replace(\":runId\", runId);\n}\n\nasync function streamFetchSSE<\n\tTPart extends { type: string },\n\tTEvent extends { type: string },\n>(\n\tresponse: Response,\n\tcontext: ChatTransportContext<TPart, TEvent>,\n\tsignal: AbortSignal,\n): Promise<void> {\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`SSE request failed: ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"Response body is null — SSE streaming not supported\");\n\t}\n\n\tfor await (const event of parseSSEStream(response.body, signal)) {\n\t\tcontext.emit(event as unknown as TEvent);\n\t}\n}\n\nexport function createDirectSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: DirectSSETransportOptions): ChatTransport<TPart, TEvent> {\n\treturn {\n\t\tstart: ({ context, message }) => {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst run: ChatTransportRun = {\n\t\t\t\tclose: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst fetchImpl = config.fetch ?? fetch;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.api, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.body }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.headers,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.method ?? \"POST\",\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\t\tcontext.finish();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn run;\n\t\t},\n\t};\n}\n\nexport function createBackgroundSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: BackgroundSSETransportOptions): ChatTransport<TPart, TEvent> {\n\tconst fetchImpl = config.fetch ?? fetch;\n\n\tconst connect = (\n\t\trunId: string,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t): ChatTransportRun => {\n\t\tconst controller = new AbortController();\n\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst response = await fetchImpl(resolveUrl(config.eventsApi, runId), {\n\t\t\t\t\theaders: config.eventHeaders,\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\tcontext.finish();\n\t\t\t} catch (error) {\n\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t},\n\t\t\tstop: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t\tif (config.cancelApi) {\n\t\t\t\t\tvoid fetchImpl(resolveUrl(config.cancelApi, runId), {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\trunId,\n\t\t};\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\treturn connect(runId, context);\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst startController = new AbortController();\n\t\t\tlet activeRun: ChatTransportRun | undefined;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.startApi, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.startBody }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.startHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.startMethod ?? \"POST\",\n\t\t\t\t\t\tsignal: startController.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Background SSE start failed: ${response.status} ${response.statusText}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as unknown;\n\t\t\t\t\tconst runId = (config.resolveRunId ?? resolveRunId)(data);\n\t\t\t\t\tcontext.setRunId(runId);\n\t\t\t\t\tactiveRun = connect(runId, context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tvoid activeRun?.close?.();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tactiveRun?.stop?.();\n\t\t\t\t},\n\t\t\t\trunId: null,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction defaultParseWebSocketMessage<TEvent extends { type: string }>(\n\tdata: unknown,\n): TEvent | TEvent[] | null {\n\tif (typeof data !== \"string\") {\n\t\treturn null;\n\t}\n\n\tconst parsed = JSON.parse(data) as TEvent | TEvent[];\n\treturn parsed;\n}\n\nexport function createWebSocketTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: WebSocketTransportOptions<TEvent>): ChatTransport<TPart, TEvent> {\n\tconst parseMessage = config.parseMessage ?? defaultParseWebSocketMessage;\n\tconst serializeMessage = config.serializeMessage ?? JSON.stringify;\n\n\tlet resolvedRunId: string | null = null;\n\n\tconst applyIncomingEvents = (\n\t\tparsed: TEvent | TEvent[] | null,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t) => {\n\t\tconst events = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];\n\t\tfor (const item of events) {\n\t\t\tconst nextRunId = config.resolveRunId?.(item) ?? null;\n\t\t\tif (nextRunId) {\n\t\t\t\tresolvedRunId = nextRunId;\n\t\t\t\tcontext.setRunId(nextRunId);\n\t\t\t}\n\t\t\tcontext.emit(item);\n\n\t\t\tif (item.type === \"done\") {\n\t\t\t\tcontext.finish();\n\t\t\t} else if (item.type === \"error\") {\n\t\t\t\tcontext.fail(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\"message\" in item && typeof item.message === \"string\"\n\t\t\t\t\t\t\t? item.message\n\t\t\t\t\t\t\t: \"Stream error\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\tcontext.ensureAssistantMessage();\n\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentResumePayload = false;\n\n\t\t\tconst sendResumePayload = () => {\n\t\t\t\tif (sentResumePayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentResumePayload = true;\n\t\t\t\tconst payload =\n\t\t\t\t\tconfig.buildResumePayload?.(runId) ??\n\t\t\t\t\t({ [config.runIdKey ?? \"runId\"]: runId } as Record<string, unknown>);\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendResumePayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendResumePayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst runId = config.runId ?? config.getResumeKey?.() ?? null;\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentStartPayload = false;\n\n\t\t\tconst sendStartPayload = () => {\n\t\t\t\tif (sentStartPayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentStartPayload = true;\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst payload: Record<string, unknown> = {\n\t\t\t\t\tmessage,\n\t\t\t\t\t...config.body,\n\t\t\t\t};\n\n\t\t\t\tif (runId) {\n\t\t\t\t\tpayload[config.runIdKey ?? \"runId\"] = runId;\n\t\t\t\t}\n\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendStartPayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendStartPayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function resolveTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): ChatTransport<TPart, TEvent> {\n\tif (typeof options.transport === \"object\" && options.transport) {\n\t\treturn options.transport;\n\t}\n\n\tconst transportKind = options.transport ?? \"sse\";\n\n\tif (transportKind === \"background-sse\") {\n\t\tconst config = options.transportOptions?.backgroundSSE;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.backgroundSSE` is required when transport is \"background-sse\"',\n\t\t\t);\n\t\t}\n\t\treturn createBackgroundSSETransport(config);\n\t}\n\n\tif (transportKind === \"websocket\") {\n\t\tconst config = options.transportOptions?.websocket;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.websocket` is required when transport is \"websocket\"',\n\t\t\t);\n\t\t}\n\t\treturn createWebSocketTransport(config);\n\t}\n\n\tconst sseConfig = options.transportOptions?.sse ?? {\n\t\tapi: options.api,\n\t\tbody: options.body,\n\t\theaders: options.headers,\n\t};\n\n\tif (!sseConfig.api) {\n\t\tthrow new Error(\n\t\t\t\"`api` or `transportOptions.sse.api` is required when using the default SSE transport\",\n\t\t);\n\t}\n\n\treturn createDirectSSETransport({\n\t\t...sseConfig,\n\t\tapi: sseConfig.api,\n\t});\n}\n"],"mappings":";AAYA;AAAA,EACC;AAAA,EACA;AAAA,EACA,kBAAAA;AAAA,OACM;;;AChBP,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AAOzD,IAAM,oBAAoB;AAMnB,SAAS,cACf,cACA,SACyB;AACzB,QAAM;AAAA,IACL,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY;AAAA,EACb,IAAI,WAAW,CAAC;AAEhB,QAAM,MAAM,OAAiB,IAAI;AACjC,QAAM,gBAAgB,OAAO,IAAI;AACjC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,IAAI;AAKjD,QAAM,SAAS,OAAsB,IAAI;AACzC,QAAM,eAAe,OAAsB,IAAI;AAC/C,QAAM,kBAAkB,OAAsB,IAAI;AAClD,QAAM,0BAA0B,OAAsB,IAAI;AAE1D,QAAM,qBAAqB,YAAY,MAAM;AAC5C,QAAI,aAAa,WAAW,MAAM;AACjC,2BAAqB,aAAa,OAAO;AACzC,mBAAa,UAAU;AAAA,IACxB;AACA,oBAAgB,UAAU;AAAA,EAC3B,GAAG,CAAC,CAAC;AAEL,QAAM,kBAAkB,YAAY,MAAM;AACzC,QAAI,aAAa,WAAW,KAAM;AAElC,UAAM,OAAO,MAAM;AAClB,YAAM,KAAK,IAAI;AACf,UAAI,CAAC,MAAM,CAAC,cAAc,SAAS;AAClC,2BAAmB;AACnB;AAAA,MACD;AAEA,YAAM,SAAS,gBAAgB,WAAW,GAAG;AAC7C,YAAM,QAAQ,SAAS,GAAG;AAE1B,UAAI,SAAS,GAAG;AACf,WAAG,YAAY;AACf,gCAAwB,UAAU;AAClC,qBAAa,UAAU;AACvB;AAAA,MACD;AAEA,YAAM,OAAO,KAAK,IAAI,KAAK,IAAI,QAAQ,MAAM,EAAE,GAAG,GAAG;AACrD,SAAG,YAAY,KAAK,IAAI,QAAQ,GAAG,YAAY,IAAI;AACnD,mBAAa,UAAU,sBAAsB,IAAI;AAAA,IAClD;AAEA,iBAAa,UAAU,sBAAsB,IAAI;AAAA,EAClD,GAAG,CAAC,kBAAkB,CAAC;AAEvB,QAAM,iBAAiB,YAAY,MAAM;AACxC,QAAI,OAAO,WAAW,KAAM;AAC5B,WAAO,UAAU,sBAAsB,MAAM;AAC5C,aAAO,UAAU;AACjB,YAAM,KAAK,IAAI;AACf,UAAI,CAAC,MAAM,CAAC,cAAc,QAAS;AAEnC,YAAM,aAAa,GAAG;AACtB,UAAI,wBAAwB,YAAY,WAAY;AAEpD,8BAAwB,UAAU;AAElC,UAAI,aAAa,UAAU;AAC1B,wBAAgB,UAAU;AAC1B,wBAAgB;AAChB;AAAA,MACD;AAEA,yBAAmB;AACnB,SAAG,YAAY;AAAA,IAChB,CAAC;AAAA,EACF,GAAG,CAAC,UAAU,oBAAoB,eAAe,CAAC;AAOlD,YAAU,MAAM;AACf,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,MAAM,CAAC,QAAS;AAErB,UAAM,eAAe,MAAM;AAC1B,YAAM,WACL,GAAG,eAAe,GAAG,YAAY,GAAG,gBAAgB;AACrD,oBAAc,UAAU;AACxB,UAAI,CAAC,UAAU;AACd,gCAAwB,UAAU;AAClC,2BAAmB;AAAA,MACpB;AACA,oBAAc,CAAC,SAAU,SAAS,WAAW,OAAO,QAAS;AAAA,IAC9D;AAEA,OAAG,iBAAiB,UAAU,cAAc,EAAE,SAAS,KAAK,CAAC;AAC7D,iBAAa;AACb,WAAO,MAAM,GAAG,oBAAoB,UAAU,YAAY;AAAA,EAC3D,GAAG,CAAC,SAAS,SAAS,CAAC;AAMvB,YAAU,MAAM;AACf,QAAI,CAAC,WAAW,CAAC,cAAc,QAAS;AACxC,mBAAe;AAAA,EAEhB,GAAG,YAAY;AASf,YAAU,MAAM;AACf,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,MAAM,CAAC,QAAS;AAErB,UAAM,iBAAiB,IAAI,eAAe,cAAc;AAGxD,eAAW,SAAS,GAAG,UAAU;AAChC,qBAAe,QAAQ,KAAK;AAAA,IAC7B;AAGA,UAAM,mBAAmB,IAAI,iBAAiB,CAAC,cAAc;AAC5D,iBAAW,YAAY,WAAW;AACjC,mBAAW,QAAQ,SAAS,YAAY;AACvC,cAAI,gBAAgB,SAAS;AAC5B,2BAAe,QAAQ,IAAI;AAAA,UAC5B;AAAA,QACD;AAAA,MACD;AACA,qBAAe;AAAA,IAChB,CAAC;AAED,qBAAiB,QAAQ,IAAI,EAAE,WAAW,MAAM,SAAS,KAAK,CAAC;AAE/D,WAAO,MAAM;AACZ,qBAAe,WAAW;AAC1B,uBAAiB,WAAW;AAAA,IAC7B;AAAA,EACD,GAAG,CAAC,SAAS,cAAc,CAAC;AAM5B,YAAU,MAAM;AACf,WAAO,MAAM;AACZ,UAAI,OAAO,WAAW,MAAM;AAC3B,6BAAqB,OAAO,OAAO;AACnC,eAAO,UAAU;AAAA,MAClB;AACA,yBAAmB;AAAA,IACpB;AAAA,EACD,GAAG,CAAC,kBAAkB,CAAC;AAOvB,QAAM,iBAAiB,YAAY,MAAM;AACxC,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AAET,uBAAmB;AACnB,kBAAc,UAAU;AACxB,4BAAwB,UAAU,GAAG;AACrC,kBAAc,IAAI;AAClB,OAAG,SAAS,EAAE,KAAK,GAAG,cAAc,SAAS,CAAC;AAAA,EAC/C,GAAG,CAAC,UAAU,kBAAkB,CAAC;AAEjC,SAAO,EAAE,KAAK,gBAAgB,WAAW;AAC1C;;;ACrMA,SAAS,eAAAC,cAAa,aAAAC,YAAW,SAAS,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACwBlE,IAAI,UAAU;AAEP,SAAS,aAAqB;AACpC,SAAO,OAAO,KAAK,IAAI,CAAC,IAAI,EAAE,OAAO;AACtC;AAEO,SAAS,cACf,MACA,OACiB;AACjB,SAAO,EAAE,IAAI,WAAW,GAAG,MAAM,MAAM;AACxC;AAEO,SAAS,2BAIf,SACsC;AACtC,QAAM,UAA+B;AAAA,IACpC,YAAY,QAAQ;AAAA,IACpB,YAAY,QAAQ;AAAA,IACpB,aAAa,QAAQ;AAAA,EACtB;AAEA,SAAO;AAAA,IACN,MAAM,CAAC,UAAU;AAChB,cAAQ,aAAa,OAAO,OAAO;AAAA,IACpC;AAAA,IACA,wBAAwB,MAAM;AAC7B,cAAQ,YAAY,CAAC,SAAS;AAC7B,cAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,YAAI,MAAM,SAAS,aAAa;AAC/B,iBAAO;AAAA,QACR;AAEA,eAAO,CAAC,GAAG,MAAM,cAAqB,aAAa,CAAC,CAAC,CAAC;AAAA,MACvD,CAAC;AAAA,IACF;AAAA,IACA,MAAM,CAAC,UAAU;AAChB,cAAQ,SAAS,KAAK;AACtB,cAAQ,UAAU,KAAK;AACvB,cAAQ,aAAa,KAAK;AAC1B,cAAQ,SAAS,IAAI;AAAA,IACtB;AAAA,IACA,QAAQ,MAAM;AACb,cAAQ,aAAa,KAAK;AAC1B,cAAQ,SAAS,IAAI;AAErB,YAAM,gBAAgB,QAAQ,YAAY;AAC1C,YAAM,cAAc,cAAc,cAAc,SAAS,CAAC;AAE1D,UAAI,aAAa,SAAS,aAAa;AACtC,gBAAQ,YAAY,WAAW;AAAA,MAChC;AAEA,cAAQ,WAAW,aAAa;AAAA,IACjC;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,UAAU,CAAC,UAAU;AACpB,cAAQ,SAAS,KAAK;AAAA,IACvB;AAAA,EACD;AACD;;;ACvFA,SAAS,sBAAsB;AAW/B,SAAS,QAAQ,OAAuB;AACvC,SAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAChE;AAEA,SAAS,aAAa,OAAyB;AAC9C,SAAO,iBAAiB,gBAAgB,MAAM,SAAS;AACxD;AAEA,SAAS,aAAa,UAA2B;AAChD,MAAI,CAAC,YAAY,OAAO,aAAa,UAAU;AAC9C,UAAM,IAAI,MAAM,wDAAwD;AAAA,EACzE;AAEA,QAAM,aACL,WAAW,WACR,SAAS,QACT,YAAY,WACX,SAAS,SACT;AAEL,MAAI,OAAO,eAAe,YAAY,WAAW,WAAW,GAAG;AAC9D,UAAM,IAAI,MAAM,wDAAwD;AAAA,EACzE;AAEA,SAAO;AACR;AAEA,SAAS,WACR,KACA,OACS;AACT,SAAO,OAAO,QAAQ,aAAa,IAAI,KAAK,IAAI,IAAI,QAAQ,UAAU,KAAK;AAC5E;AAEA,eAAe,eAId,UACA,SACA,QACgB;AAChB,MAAI,CAAC,SAAS,IAAI;AACjB,UAAM,IAAI;AAAA,MACT,uBAAuB,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,IAC9D;AAAA,EACD;AAEA,MAAI,CAAC,SAAS,MAAM;AACnB,UAAM,IAAI,MAAM,0DAAqD;AAAA,EACtE;AAEA,mBAAiB,SAAS,eAAe,SAAS,MAAM,MAAM,GAAG;AAChE,YAAQ,KAAK,KAA0B;AAAA,EACxC;AACD;AAEO,SAAS,yBAGd,QAAiE;AAClE,SAAO;AAAA,IACN,OAAO,CAAC,EAAE,SAAS,QAAQ,MAAM;AAChC,YAAM,aAAa,IAAI,gBAAgB;AACvC,YAAM,MAAwB;AAAA,QAC7B,OAAO,MAAM;AACZ,qBAAW,MAAM;AAAA,QAClB;AAAA,QACA,MAAM,MAAM;AACX,qBAAW,MAAM;AAAA,QAClB;AAAA,MACD;AAEA,YAAM,YAAY,OAAO,SAAS;AAElC,YAAM,YAAY;AACjB,YAAI;AACH,gBAAM,WAAW,MAAM,UAAU,OAAO,KAAK;AAAA,YAC5C,MAAM,KAAK,UAAU,EAAE,SAAS,GAAG,OAAO,KAAK,CAAC;AAAA,YAChD,SAAS;AAAA,cACR,gBAAgB;AAAA,cAChB,GAAG,OAAO;AAAA,YACX;AAAA,YACA,QAAQ,OAAO,UAAU;AAAA,YACzB,QAAQ,WAAW;AAAA,UACpB,CAAC;AAED,gBAAM,eAAe,UAAU,SAAS,WAAW,MAAM;AACzD,kBAAQ,OAAO;AAAA,QAChB,SAAS,OAAO;AACf,cAAI,CAAC,aAAa,KAAK,GAAG;AACzB,oBAAQ,KAAK,QAAQ,KAAK,CAAC;AAAA,UAC5B;AAAA,QACD;AAAA,MACD,GAAG;AAEH,aAAO;AAAA,IACR;AAAA,EACD;AACD;AAEO,SAAS,6BAGd,QAAqE;AACtE,QAAM,YAAY,OAAO,SAAS;AAElC,QAAM,UAAU,CACf,OACA,YACsB;AACtB,UAAM,aAAa,IAAI,gBAAgB;AAEvC,UAAM,YAAY;AACjB,UAAI;AACH,gBAAQ,uBAAuB;AAC/B,cAAM,WAAW,MAAM,UAAU,WAAW,OAAO,WAAW,KAAK,GAAG;AAAA,UACrE,SAAS,OAAO;AAAA,UAChB,QAAQ;AAAA,UACR,QAAQ,WAAW;AAAA,QACpB,CAAC;AACD,cAAM,eAAe,UAAU,SAAS,WAAW,MAAM;AACzD,gBAAQ,OAAO;AAAA,MAChB,SAAS,OAAO;AACf,YAAI,CAAC,aAAa,KAAK,GAAG;AACzB,kBAAQ,KAAK,QAAQ,KAAK,CAAC;AAAA,QAC5B;AAAA,MACD;AAAA,IACD,GAAG;AAEH,WAAO;AAAA,MACN,OAAO,MAAM;AACZ,mBAAW,MAAM;AAAA,MAClB;AAAA,MACA,MAAM,MAAM;AACX,mBAAW,MAAM;AACjB,YAAI,OAAO,WAAW;AACrB,eAAK,UAAU,WAAW,OAAO,WAAW,KAAK,GAAG;AAAA,YACnD,QAAQ;AAAA,UACT,CAAC;AAAA,QACF;AAAA,MACD;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AAAA,IACN,QAAQ,CAAC,EAAE,SAAS,MAAM,MAAM;AAC/B,cAAQ,SAAS,KAAK;AACtB,aAAO,QAAQ,OAAO,OAAO;AAAA,IAC9B;AAAA,IACA,OAAO,CAAC,EAAE,SAAS,QAAQ,MAAM;AAChC,YAAM,kBAAkB,IAAI,gBAAgB;AAC5C,UAAI;AAEJ,YAAM,YAAY;AACjB,YAAI;AACH,gBAAM,WAAW,MAAM,UAAU,OAAO,UAAU;AAAA,YACjD,MAAM,KAAK,UAAU,EAAE,SAAS,GAAG,OAAO,UAAU,CAAC;AAAA,YACrD,SAAS;AAAA,cACR,gBAAgB;AAAA,cAChB,GAAG,OAAO;AAAA,YACX;AAAA,YACA,QAAQ,OAAO,eAAe;AAAA,YAC9B,QAAQ,gBAAgB;AAAA,UACzB,CAAC;AAED,cAAI,CAAC,SAAS,IAAI;AACjB,kBAAM,IAAI;AAAA,cACT,gCAAgC,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,YACvE;AAAA,UACD;AAEA,gBAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,gBAAM,SAAS,OAAO,gBAAgB,cAAc,IAAI;AACxD,kBAAQ,SAAS,KAAK;AACtB,sBAAY,QAAQ,OAAO,OAAO;AAAA,QACnC,SAAS,OAAO;AACf,cAAI,CAAC,aAAa,KAAK,GAAG;AACzB,oBAAQ,KAAK,QAAQ,KAAK,CAAC;AAAA,UAC5B;AAAA,QACD;AAAA,MACD,GAAG;AAEH,aAAO;AAAA,QACN,OAAO,MAAM;AACZ,0BAAgB,MAAM;AACtB,eAAK,WAAW,QAAQ;AAAA,QACzB;AAAA,QACA,MAAM,MAAM;AACX,0BAAgB,MAAM;AACtB,qBAAW,OAAO;AAAA,QACnB;AAAA,QACA,OAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AACD;AAEA,SAAS,6BACR,MAC2B;AAC3B,MAAI,OAAO,SAAS,UAAU;AAC7B,WAAO;AAAA,EACR;AAEA,QAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,SAAO;AACR;AAEO,SAAS,yBAGd,QAAyE;AAC1E,QAAM,eAAe,OAAO,gBAAgB;AAC5C,QAAM,mBAAmB,OAAO,oBAAoB,KAAK;AAEzD,MAAI,gBAA+B;AAEnC,QAAM,sBAAsB,CAC3B,QACA,YACI;AACJ,UAAM,SAAS,MAAM,QAAQ,MAAM,IAAI,SAAS,SAAS,CAAC,MAAM,IAAI,CAAC;AACrE,eAAW,QAAQ,QAAQ;AAC1B,YAAM,YAAY,OAAO,eAAe,IAAI,KAAK;AACjD,UAAI,WAAW;AACd,wBAAgB;AAChB,gBAAQ,SAAS,SAAS;AAAA,MAC3B;AACA,cAAQ,KAAK,IAAI;AAEjB,UAAI,KAAK,SAAS,QAAQ;AACzB,gBAAQ,OAAO;AAAA,MAChB,WAAW,KAAK,SAAS,SAAS;AACjC,gBAAQ;AAAA,UACP,IAAI;AAAA,YACH,aAAa,QAAQ,OAAO,KAAK,YAAY,WAC1C,KAAK,UACL;AAAA,UACJ;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AAAA,IACN,QAAQ,CAAC,EAAE,SAAS,MAAM,MAAM;AAC/B,cAAQ,SAAS,KAAK;AACtB,cAAQ,uBAAuB;AAE/B,YAAM,SAAS,IAAI;AAAA,QAClB,OAAO,OAAO,QAAQ,aAAa,OAAO,IAAI,KAAK,IAAI,OAAO;AAAA,QAC9D,OAAO;AAAA,MACR;AACA,UAAI,iBAAiB;AACrB,UAAI,oBAAoB;AAExB,YAAM,oBAAoB,MAAM;AAC/B,YAAI,qBAAqB,OAAO,eAAe,UAAU,MAAM;AAC9D;AAAA,QACD;AACA,4BAAoB;AACpB,cAAM,UACL,OAAO,qBAAqB,KAAK,KAChC,EAAE,CAAC,OAAO,YAAY,OAAO,GAAG,MAAM;AACxC,eAAO,KAAK,iBAAiB,OAAO,CAAC;AAAA,MACtC;AAEA,aAAO,SAAS;AAEhB,aAAO,YAAY,CAAC,UAAU;AAC7B,YAAI;AACH,8BAAoB,aAAa,MAAM,IAAI,GAAG,OAAO;AAAA,QACtD,SAAS,OAAO;AACf,kBAAQ,KAAK,QAAQ,KAAK,CAAC;AAAA,QAC5B;AAAA,MACD;AAEA,aAAO,UAAU,MAAM;AACtB,gBAAQ,KAAK,IAAI,MAAM,6BAA6B,CAAC;AAAA,MACtD;AAEA,aAAO,UAAU,MAAM;AACtB,YAAI,CAAC,gBAAgB;AACpB,kBAAQ,OAAO;AAAA,QAChB;AAAA,MACD;AAEA,qBAAe,iBAAiB;AAEhC,aAAO;AAAA,QACN,OAAO,MAAM;AACZ,2BAAiB;AACjB,iBAAO,MAAM;AAAA,QACd;AAAA,QACA,MAAM,MAAM;AACX,2BAAiB;AACjB,gBAAM,YAAY,iBAAiB;AACnC,cAAI,aAAa,OAAO,WAAW;AAClC,iBAAK,MAAM,WAAW,OAAO,WAAW,SAAS,GAAG;AAAA,cACnD,QAAQ;AAAA,YACT,CAAC;AAAA,UACF;AACA,iBAAO,MAAM;AAAA,QACd;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAAA,IACA,OAAO,CAAC,EAAE,SAAS,QAAQ,MAAM;AAChC,YAAM,QAAQ,OAAO,SAAS,OAAO,eAAe,KAAK;AACzD,YAAM,SAAS,IAAI;AAAA,QAClB,OAAO,OAAO,QAAQ,aAAa,OAAO,IAAI,KAAK,IAAI,OAAO;AAAA,QAC9D,OAAO;AAAA,MACR;AACA,UAAI,iBAAiB;AACrB,UAAI,mBAAmB;AAEvB,YAAM,mBAAmB,MAAM;AAC9B,YAAI,oBAAoB,OAAO,eAAe,UAAU,MAAM;AAC7D;AAAA,QACD;AACA,2BAAmB;AACnB,gBAAQ,uBAAuB;AAC/B,cAAM,UAAmC;AAAA,UACxC;AAAA,UACA,GAAG,OAAO;AAAA,QACX;AAEA,YAAI,OAAO;AACV,kBAAQ,OAAO,YAAY,OAAO,IAAI;AAAA,QACvC;AAEA,eAAO,KAAK,iBAAiB,OAAO,CAAC;AAAA,MACtC;AAEA,aAAO,SAAS;AAEhB,aAAO,YAAY,CAAC,UAAU;AAC7B,YAAI;AACH,8BAAoB,aAAa,MAAM,IAAI,GAAG,OAAO;AAAA,QACtD,SAAS,OAAO;AACf,kBAAQ,KAAK,QAAQ,KAAK,CAAC;AAAA,QAC5B;AAAA,MACD;AAEA,aAAO,UAAU,MAAM;AACtB,gBAAQ,KAAK,IAAI,MAAM,6BAA6B,CAAC;AAAA,MACtD;AAEA,aAAO,UAAU,MAAM;AACtB,YAAI,CAAC,gBAAgB;AACpB,kBAAQ,OAAO;AAAA,QAChB;AAAA,MACD;AAEA,qBAAe,gBAAgB;AAE/B,aAAO;AAAA,QACN,OAAO,MAAM;AACZ,2BAAiB;AACjB,iBAAO,MAAM;AAAA,QACd;AAAA,QACA,MAAM,MAAM;AACX,2BAAiB;AACjB,gBAAM,YAAY,iBAAiB;AACnC,cAAI,aAAa,OAAO,WAAW;AAClC,iBAAK,MAAM,WAAW,OAAO,WAAW,SAAS,GAAG;AAAA,cACnD,QAAQ;AAAA,YACT,CAAC;AAAA,UACF;AACA,iBAAO,MAAM;AAAA,QACd;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD;AAEO,SAAS,iBAGd,SAA4E;AAC7E,MAAI,OAAO,QAAQ,cAAc,YAAY,QAAQ,WAAW;AAC/D,WAAO,QAAQ;AAAA,EAChB;AAEA,QAAM,gBAAgB,QAAQ,aAAa;AAE3C,MAAI,kBAAkB,kBAAkB;AACvC,UAAM,SAAS,QAAQ,kBAAkB;AACzC,QAAI,CAAC,QAAQ;AACZ,YAAM,IAAI;AAAA,QACT;AAAA,MACD;AAAA,IACD;AACA,WAAO,6BAA6B,MAAM;AAAA,EAC3C;AAEA,MAAI,kBAAkB,aAAa;AAClC,UAAM,SAAS,QAAQ,kBAAkB;AACzC,QAAI,CAAC,QAAQ;AACZ,YAAM,IAAI;AAAA,QACT;AAAA,MACD;AAAA,IACD;AACA,WAAO,yBAAyB,MAAM;AAAA,EACvC;AAEA,QAAM,YAAY,QAAQ,kBAAkB,OAAO;AAAA,IAClD,KAAK,QAAQ;AAAA,IACb,MAAM,QAAQ;AAAA,IACd,SAAS,QAAQ;AAAA,EAClB;AAEA,MAAI,CAAC,UAAU,KAAK;AACnB,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AAEA,SAAO,yBAAyB;AAAA,IAC/B,GAAG;AAAA,IACH,KAAK,UAAU;AAAA,EAChB,CAAC;AACF;;;AFraA,SAAS,eACR,OACA,SACO;AACP,MAAI,MAAM,SAAS,cAAc;AAChC,YAAQ,WAAW,MAAM,KAAK;AAAA,EAC/B;AAGD;AAMO,SAAS,cAGd,SAA0E;AAC3E,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,EACf,IAAI;AAEJ,QAAM,CAAC,UAAU,WAAW,IAAIC,UAAS,mBAAmB,CAAC,CAAC;AAC9D,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AAEtD,QAAM,SAASC,QAAgC,IAAI;AAGnD,QAAM,kBAAkBA,QAAsB,IAAI;AAIlD,QAAM,qBAAqBA,QAAO,KAAK;AAKvC,QAAM,cAAcA,QAAO,QAAQ;AACnC,cAAY,UAAU;AAEtB,QAAM,iBAAiBC;AAAA,IACtB,CAAC,SAEqD;AACrD,kBAAY,CAAC,SAAS;AACrB,cAAM,WACL,OAAO,SAAS,aACZ,KAAsD,IAAI,IAC3D;AACJ,oBAAY,UAAU;AACtB,eAAO;AAAA,MACR,CAAC;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACF;AAIA,QAAM,sBAAsBD,QAAO,QAAQ,gBAAgB;AAC3D,sBAAoB,UAAU,QAAQ;AAEtC,QAAM,iBAAiBA,QAAO,EAAE;AAChC,QAAM,oBAAoBA,QAA6C,IAAI;AAE3E,QAAM,mBAAmBC,aAAY,MAAM;AAC1C,QAAI,kBAAkB,YAAY,MAAM;AACvC,mBAAa,kBAAkB,OAAO;AACtC,wBAAkB,UAAU;AAAA,IAC7B;AAEA,UAAM,QAAQ,eAAe;AAC7B,QAAI,CAAC,MAAO;AAEZ,mBAAe,UAAU;AACzB,mBAAe,CAAC,SAAS;AACxB,YAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,UAAI,CAAC,QAAQ,KAAK,SAAS,YAAa,QAAO;AAE/C,YAAM,QAAQ,CAAC,GAAG,KAAK,KAAK;AAC5B,YAAM,WAAW,MAAM,MAAM,SAAS,CAAC;AAEvC,UAAI,YAAY,SAAS,SAAS,UAAU,UAAU,UAAU;AAC/D,cAAM,WAAW;AACjB,cAAM,MAAM,SAAS,CAAC,IAAI;AAAA,UACzB,GAAG;AAAA,UACH,MAAM,SAAS,OAAO;AAAA,QACvB;AAAA,MACD,OAAO;AACN,cAAM,KAAK,EAAE,MAAM,QAAQ,MAAM,MAAM,CAAqB;AAAA,MAC7D;AAEA,aAAO,CAAC,GAAG,KAAK,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC;AAAA,IACjD,CAAC;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,aAAaA,aAAY,CAAC,UAAkB;AACjD,QAAI,eAAe,GAAG;AACrB,qBAAe,WAAW;AAC1B,uBAAiB;AACjB;AAAA,IACD;AAEA,mBAAe,WAAW;AAC1B,QAAI,kBAAkB,YAAY,KAAM;AAExC,sBAAkB,UAAU,WAAW,MAAM;AAC5C,uBAAiB;AAAA,IAClB,GAAG,WAAW;AAAA,EACf,GAAG,CAAC,kBAAkB,WAAW,CAAC;AAElC,QAAM,aAAaA,aAAY,CAAC,SAAgB;AAC/C,qBAAiB;AACjB,mBAAe,CAAC,SAAS;AACxB,YAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,UAAI,CAAC,QAAQ,KAAK,SAAS,YAAa,QAAO;AAE/C,aAAO;AAAA,QACN,GAAG,KAAK,MAAM,GAAG,EAAE;AAAA,QACnB;AAAA,UACC,GAAG;AAAA,UACH,OAAO,CAAC,GAAG,KAAK,OAAO,IAAI;AAAA,QAC5B;AAAA,MACD;AAAA,IACD,CAAC;AAAA,EACF,GAAG,CAAC,kBAAkB,cAAc,CAAC;AAKrC,QAAM,eAAeD,QAEX,IAAI;AACd,MAAI,CAAC,aAAa,SAAS;AAC1B,iBAAa,UAAU,iBAAiB,OAAO;AAAA,EAChD;AACA,QAAM,YAAY,aAAa;AAE/B,QAAM,eACL,WACC;AAQF,QAAM,kBAAkBA,QAAO,YAAY;AAC3C,kBAAgB,UAAU;AAE1B,QAAM,aAAaA,QAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,cAAcA,QAAO,QAAQ;AACnC,cAAY,UAAU;AAEtB,QAAM,eAAeA,QAAO,SAAS;AACrC,eAAa,UAAU;AAEvB,QAAM,mBAAmB;AAAA,IACxB,MACC,2BAA2B;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,cAAc,CAAC,OAAe,YAC7B,gBAAgB,QAAQ,OAAO,OAAO;AAAA,MACvC,aAAa,MAAM;AAClB,yBAAiB;AACjB,eAAO,YAAY;AAAA,MACpB;AAAA,MACA,SAAS,IAAI,SAAS,WAAW,UAAU,GAAG,IAAI;AAAA,MAClD,UAAU,IAAI,SAAS,YAAY,UAAU,GAAG,IAAI;AAAA,MACpD,WAAW,IAAI,SAAS,aAAa,UAAU,GAAG,IAAI;AAAA,MACtD;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,UAAU,CAAC,SAAS;AACnB,iBAAS,IAAI;AACb,4BAAoB,SAAS,eAAe,gBAAgB,IAAI;AAChE,4BAAoB,SAAS,WAAW,gBAAgB,IAAI;AAAA,MAC7D;AAAA,IACD,CAAC;AAAA,IACF,CAAC,YAAY,YAAY,kBAAkB,cAAc;AAAA,EAC1D;AAEA,QAAM,OAAOC,aAAY,MAAM;AAC9B,UAAM,YAAY,OAAO;AACzB,QAAI,CAAC,WAAW,MAAM;AACrB;AAAA,IACD;AAEA,uBAAmB,UAAU;AAC7B,SAAK,UAAU,KAAK;AACpB,WAAO,UAAU;AACjB,iBAAa,KAAK;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,QAAM,cAAcA;AAAA,IACnB,CAAC,SAAiB;AACjB,UAAI,OAAO,WAAW,WAAW;AAChC;AAAA,MACD;AAEA,YAAM,cAAc,cAAqB,QAAQ;AAAA,QAChD,EAAE,MAAM,QAAQ,KAAK;AAAA,MACtB,CAAC;AACD,YAAM,mBAAmB,cAAqB,aAAa,CAAC,CAAC;AAE7D,qBAAe,CAAC,SAAS;AACxB,cAAM,OAAO,CAAC,GAAG,MAAM,aAAa,gBAAgB;AACpD,eAAO;AAAA,MACR,CAAC;AAED,kBAAY,WAAW;AACvB,eAAS,IAAI;AACb,mBAAa,IAAI;AAGjB,sBAAgB,UAAU;AAC1B,yBAAmB,UAAU;AAE7B,YAAM,MAAM,UAAU,MAAM,EAAE,SAAS,kBAAkB,SAAS,KAAK,CAAC;AACxE,aAAO,UAAU,OAAO;AACxB,UAAI,KAAK,OAAO;AACf,iBAAS,IAAI,KAAK;AAAA,MACnB;AAAA,IACD;AAAA,IACA,CAAC,WAAW,WAAW,WAAW,kBAAkB,cAAc;AAAA,EACnE;AAUA,QAAM,iBACL,QAAQ,kBAAkB,eAAe,SACzC,QAAQ,kBAAkB,eAAe,eAAe,KACxD,QAAQ,kBAAkB,WAAW,SACrC,QAAQ,kBAAkB,WAAW,eAAe,KACpD;AAED,EAAAC,WAAU,MAAM;AAEf,QAAI,OAAO,SAAS;AACnB;AAAA,IACD;AAEA,QAAI,CAAC,gBAAgB;AACpB;AAAA,IACD;AAGA,QAAI,mBAAmB,SAAS;AAC/B;AAAA,IACD;AAGA,QAAI,gBAAgB,YAAY,gBAAgB;AAC/C;AAAA,IACD;AAEA,QAAI,CAAC,UAAU,QAAQ;AACtB;AAAA,IACD;AAEA,oBAAgB,UAAU;AAE1B,aAAS,IAAI;AACb,iBAAa,IAAI;AACjB,UAAM,MAAM,UAAU,OAAO;AAAA,MAC5B,SAAS;AAAA,MACT,OAAO;AAAA,IACR,CAAC;AACD,WAAO,UAAU,OAAO;AACxB,aAAS,cAAc;AAAA,EACxB,GAAG,CAAC,gBAAgB,WAAW,gBAAgB,CAAC;AAEhD,EAAAA,WAAU,MAAM;AACf,WAAO,MAAM;AACZ,UAAI,kBAAkB,YAAY,MAAM;AACvC,qBAAa,kBAAkB,OAAO;AACtC,0BAAkB,UAAU;AAAA,MAC7B;AACA,WAAK,OAAO,SAAS,QAAQ;AAC7B,aAAO,UAAU;AAAA,IAClB;AAAA,EACD,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAmBF,QAAO,SAAS;AACzC,EAAAE,WAAU,MAAM;AAIf,QAAI,iBAAiB,WAAW,CAAC,WAAW;AAC3C,aAAO,UAAU;AAAA,IAClB;AACA,qBAAiB,UAAU;AAAA,EAC5B,GAAG,CAAC,SAAS,CAAC;AAEd,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb;AAAA,EACD;AACD;","names":["parseSSEStream","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/use-auto-scroll.ts","../src/use-stream-chat.ts","../src/chat-controller.ts","../src/transports.ts"],"sourcesContent":["// Re-export core types so consumers only need to import from @deltakit/react\nexport type {\n\tContentPart,\n\tMessage,\n\tReasoningPart,\n\tSSEEvent,\n\tTextDeltaEvent,\n\tTextPart,\n\tToolCallEvent,\n\tToolCallPart,\n\tToolResultEvent,\n} from \"@deltakit/core\";\nexport {\n\tfromAgnoAgents,\n\tfromOpenAiAgents,\n\tparseSSEStream,\n} from \"@deltakit/core\";\n\nexport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tEventHelpers,\n\tTransportOptions,\n\tUseAutoScrollOptions,\n\tUseAutoScrollReturn,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n\tWebSocketTransportOptions,\n} from \"./types\";\nexport { useAutoScroll } from \"./use-auto-scroll\";\nexport { useStreamChat } from \"./use-stream-chat\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseAutoScrollOptions, UseAutoScrollReturn } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_THRESHOLD = 50;\n\n// ---------------------------------------------------------------------------\n// useAutoScroll\n// ---------------------------------------------------------------------------\n\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>(\n\tdependencies: unknown[],\n\toptions?: UseAutoScrollOptions,\n): UseAutoScrollReturn<T> {\n\tconst {\n\t\tbehavior = \"instant\",\n\t\tenabled = true,\n\t\tthreshold = DEFAULT_THRESHOLD,\n\t} = options ?? {};\n\n\tconst ref = useRef<T | null>(null);\n\tconst isAtBottomRef = useRef(true);\n\tconst [isAtBottom, setIsAtBottom] = useState(true);\n\n\t// A single rAF id shared across all scroll sources — ensures we never\n\t// call scrollTo() more than once per frame, no matter how many\n\t// MutationObserver / ResizeObserver callbacks fire.\n\tconst rafRef = useRef<number | null>(null);\n\tconst lastAutoScrollHeightRef = useRef<number | null>(null);\n\tconst pendingVerificationFramesRef = useRef(0);\n\n\tconst scheduleScroll = useCallback(() => {\n\t\tpendingVerificationFramesRef.current = Math.max(\n\t\t\tpendingVerificationFramesRef.current,\n\t\t\t1,\n\t\t);\n\t\tif (rafRef.current != null) return;\n\n\t\tconst tick = () => {\n\t\t\trafRef.current = null;\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) {\n\t\t\t\tpendingVerificationFramesRef.current = 0;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst nextHeight = el.scrollHeight;\n\t\t\tconst heightChanged = lastAutoScrollHeightRef.current !== nextHeight;\n\t\t\tconst distanceFromBottom = nextHeight - el.scrollTop - el.clientHeight;\n\n\t\t\tif (heightChanged || distanceFromBottom > 0) {\n\t\t\t\tlastAutoScrollHeightRef.current = nextHeight;\n\t\t\t\tpendingVerificationFramesRef.current = 1;\n\n\t\t\t\t// Keep the viewport pinned during streaming without restarting a\n\t\t\t\t// smooth scroll animation on every DOM mutation.\n\t\t\t\tel.scrollTop = nextHeight;\n\t\t\t} else {\n\t\t\t\tpendingVerificationFramesRef.current = Math.max(\n\t\t\t\t\tpendingVerificationFramesRef.current - 1,\n\t\t\t\t\t0,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (pendingVerificationFramesRef.current > 0) {\n\t\t\t\trafRef.current = requestAnimationFrame(tick);\n\t\t\t}\n\t\t};\n\n\t\trafRef.current = requestAnimationFrame(tick);\n\t}, []);\n\n\t// -----------------------------------------------------------------------\n\t// Track whether the user is near the bottom via scroll events.\n\t// Only triggers a React re-render when the boolean actually changes.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst handleScroll = () => {\n\t\t\tconst atBottom =\n\t\t\t\tel.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n\t\t\tisAtBottomRef.current = atBottom;\n\t\t\tif (!atBottom) {\n\t\t\t\tlastAutoScrollHeightRef.current = null;\n\t\t\t\tpendingVerificationFramesRef.current = 0;\n\t\t\t}\n\t\t\tsetIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n\t\t};\n\n\t\tel.addEventListener(\"scroll\", handleScroll, { passive: true });\n\t\thandleScroll();\n\t\treturn () => el.removeEventListener(\"scroll\", handleScroll);\n\t}, [enabled, threshold]);\n\n\t// -----------------------------------------------------------------------\n\t// Scroll to bottom when dependencies change (if pinned).\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tif (!enabled || !isAtBottomRef.current) return;\n\t\tscheduleScroll();\n\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies are passed dynamically by the consumer\n\t}, dependencies);\n\n\t// -----------------------------------------------------------------------\n\t// MutationObserver + ResizeObserver — catch content changes during\n\t// streaming that happen between React re-renders (e.g. DOM mutations\n\t// from markdown renderers). Scroll calls are batched via rAF so we\n\t// scroll at most once per frame.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst resizeObserver = new ResizeObserver(scheduleScroll);\n\n\t\t// Observe existing children for size changes.\n\t\tfor (const child of el.children) {\n\t\t\tresizeObserver.observe(child);\n\t\t}\n\n\t\t// Watch for new children added to the container.\n\t\tconst mutationObserver = new MutationObserver((mutations) => {\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tfor (const node of mutation.addedNodes) {\n\t\t\t\t\tif (node instanceof Element) {\n\t\t\t\t\t\tresizeObserver.observe(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tscheduleScroll();\n\t\t});\n\n\t\tmutationObserver.observe(el, { childList: true, subtree: true });\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [enabled, scheduleScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Cancel any pending rAF on unmount.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (rafRef.current != null) {\n\t\t\t\tcancelAnimationFrame(rafRef.current);\n\t\t\t\trafRef.current = null;\n\t\t\t}\n\t\t\tpendingVerificationFramesRef.current = 0;\n\t\t};\n\t}, []);\n\n\t// -----------------------------------------------------------------------\n\t// scrollToBottom — imperative function that scrolls to the bottom and\n\t// re-pins auto-scroll.\n\t// -----------------------------------------------------------------------\n\n\tconst scrollToBottom = useCallback(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tisAtBottomRef.current = true;\n\t\tlastAutoScrollHeightRef.current = el.scrollHeight;\n\t\tpendingVerificationFramesRef.current = 0;\n\t\tsetIsAtBottom(true);\n\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t}, [behavior]);\n\n\treturn { ref, scrollToBottom, isAtBottom };\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { createChatTransportContext, createMessage } from \"./chat-controller\";\nimport { resolveTransport } from \"./transports\";\nimport type {\n\tChatTransportRun,\n\tEventHelpers,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Default event handler — accumulates `text_delta` into the last\n// assistant message's parts.\n// ---------------------------------------------------------------------------\n\nfunction defaultOnEvent(\n\tevent: SSEEvent,\n\thelpers: EventHelpers<ContentPart>,\n): void {\n\tif (event.type === \"text_delta\") {\n\t\thelpers.appendText(event.delta);\n\t}\n\t// Other event types (e.g. tool_call) are silently ignored by default.\n\t// Users can provide their own `onEvent` to handle them.\n}\n\n// ---------------------------------------------------------------------------\n// useStreamChat\n// ---------------------------------------------------------------------------\n\nexport function useStreamChat<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): UseStreamChatReturn<TPart> {\n\tconst { initialMessages, onEvent, onMessage, onError, onFinish } = options;\n\n\tconst [messages, setMessages] = useState(initialMessages ?? []);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [runId, setRunId] = useState<string | null>(null);\n\n\tconst runRef = useRef<ChatTransportRun | null>(null);\n\n\t// Track which run id has already been resumed to prevent re-triggering.\n\tconst resumedRunIdRef = useRef<string | null>(null);\n\n\t// When the user manually stops a run, suppress auto-resume until the\n\t// next explicit `sendMessage` call.\n\tconst manuallyStoppedRef = useRef(false);\n\n\t// We use a ref for the latest messages so callbacks created inside\n\t// transport handlers always see the current value without re-creating\n\t// closures.\n\tconst messagesRef = useRef(messages);\n\tmessagesRef.current = messages;\n\n\t// Keep transport options in a ref so that callbacks always read the\n\t// latest values without causing memoisation instability.\n\tconst transportOptionsRef = useRef(options.transportOptions);\n\ttransportOptionsRef.current = options.transportOptions;\n\n\tconst appendText = useCallback((delta: string) => {\n\t\tsetMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\tconst parts = [...last.parts];\n\t\t\tconst lastPart = parts[parts.length - 1];\n\n\t\t\tif (lastPart && lastPart.type === \"text\" && \"text\" in lastPart) {\n\t\t\t\tconst textPart = lastPart as { type: \"text\"; text: string };\n\t\t\t\tparts[parts.length - 1] = {\n\t\t\t\t\t...lastPart,\n\t\t\t\t\ttext: textPart.text + delta,\n\t\t\t\t} as unknown as TPart;\n\t\t\t} else {\n\t\t\t\tparts.push({ type: \"text\", text: delta } as unknown as TPart);\n\t\t\t}\n\n\t\t\treturn [...prev.slice(0, -1), { ...last, parts }];\n\t\t});\n\t}, []);\n\n\tconst appendPart = useCallback((part: TPart) => {\n\t\tsetMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\treturn [\n\t\t\t\t...prev.slice(0, -1),\n\t\t\t\t{\n\t\t\t\t\t...last,\n\t\t\t\t\tparts: [...last.parts, part],\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t}, []);\n\n\t// Stabilise transport creation: resolve once and store in a ref so that\n\t// changing values like `runId` in transportOptions won't cause a new\n\t// transport instance (and therefore a new WebSocket) to be created.\n\tconst transportRef = useRef<ReturnType<\n\t\ttypeof resolveTransport<TPart, TEvent>\n\t> | null>(null);\n\tif (!transportRef.current) {\n\t\ttransportRef.current = resolveTransport(options);\n\t}\n\tconst transport = transportRef.current;\n\n\tconst eventHandler =\n\t\tonEvent ??\n\t\t(defaultOnEvent as unknown as (\n\t\t\tevent: TEvent,\n\t\t\thelpers: EventHelpers<TPart>,\n\t\t) => void);\n\n\t// Stabilise the transport context: use refs for values that change\n\t// frequently (transportOptions callbacks) so the context object itself\n\t// stays referentially stable.\n\tconst eventHandlerRef = useRef(eventHandler);\n\teventHandlerRef.current = eventHandler;\n\n\tconst onErrorRef = useRef(onError);\n\tonErrorRef.current = onError;\n\n\tconst onFinishRef = useRef(onFinish);\n\tonFinishRef.current = onFinish;\n\n\tconst onMessageRef = useRef(onMessage);\n\tonMessageRef.current = onMessage;\n\n\tconst transportContext = useMemo(\n\t\t() =>\n\t\t\tcreateChatTransportContext({\n\t\t\t\tappendPart,\n\t\t\t\tappendText,\n\t\t\t\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) =>\n\t\t\t\t\teventHandlerRef.current(event, helpers),\n\t\t\t\tgetMessages: () => messagesRef.current,\n\t\t\t\tonError: (...args) => onErrorRef.current?.(...args),\n\t\t\t\tonFinish: (...args) => onFinishRef.current?.(...args),\n\t\t\t\tonMessage: (...args) => onMessageRef.current?.(...args),\n\t\t\t\tsetError,\n\t\t\t\tsetIsLoading,\n\t\t\t\tsetMessages,\n\t\t\t\tsetRunId: (next) => {\n\t\t\t\t\tsetRunId(next);\n\t\t\t\t\ttransportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);\n\t\t\t\t\ttransportOptionsRef.current?.websocket?.onRunIdChange?.(next);\n\t\t\t\t},\n\t\t\t}),\n\t\t[appendPart, appendText],\n\t);\n\n\tconst stop = useCallback(() => {\n\t\tconst activeRun = runRef.current;\n\t\tif (!activeRun?.stop) {\n\t\t\treturn;\n\t\t}\n\n\t\tmanuallyStoppedRef.current = true;\n\t\tvoid activeRun.stop();\n\t\trunRef.current = null;\n\t\tsetIsLoading(false);\n\t}, []);\n\n\tconst sendMessage = useCallback(\n\t\t(text: string) => {\n\t\t\tif (runRef.current || isLoading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst userMessage = createMessage<TPart>(\"user\", [\n\t\t\t\t{ type: \"text\", text } as unknown as TPart,\n\t\t\t]);\n\t\t\tconst assistantMessage = createMessage<TPart>(\"assistant\", []);\n\n\t\t\tsetMessages((prev) => {\n\t\t\t\tconst next = [...prev, userMessage, assistantMessage];\n\t\t\t\tmessagesRef.current = next;\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t\tonMessage?.(userMessage);\n\t\t\tsetError(null);\n\t\t\tsetIsLoading(true);\n\n\t\t\t// Reset the resume guard so a future resume for a new run is allowed.\n\t\t\tresumedRunIdRef.current = null;\n\t\t\tmanuallyStoppedRef.current = false;\n\n\t\t\tconst run = transport.start({ context: transportContext, message: text });\n\t\t\trunRef.current = run ?? null;\n\t\t\tif (run?.runId) {\n\t\t\t\tsetRunId(run.runId);\n\t\t\t}\n\t\t},\n\t\t[isLoading, onMessage, transport, transportContext],\n\t);\n\n\t// -----------------------------------------------------------------------\n\t// Auto-resume effect: attempt to reconnect to an in-flight run on mount.\n\t//\n\t// The candidate run id is read from transport options (which may change\n\t// when the app updates state). We guard against duplicate resumes for the\n\t// same run id using `resumedRunIdRef`.\n\t// -----------------------------------------------------------------------\n\n\tconst candidateRunId =\n\t\toptions.transportOptions?.backgroundSSE?.runId ??\n\t\toptions.transportOptions?.backgroundSSE?.getResumeKey?.() ??\n\t\toptions.transportOptions?.websocket?.runId ??\n\t\toptions.transportOptions?.websocket?.getResumeKey?.() ??\n\t\tnull;\n\n\tuseEffect(() => {\n\t\t// Already have an active run — don't start another.\n\t\tif (runRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User explicitly stopped — don't auto-resume until next sendMessage.\n\t\tif (manuallyStoppedRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Already resumed this exact run id — don't retry.\n\t\tif (resumedRunIdRef.current === candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!transport.resume) {\n\t\t\treturn;\n\t\t}\n\n\t\tresumedRunIdRef.current = candidateRunId;\n\n\t\tsetError(null);\n\t\tsetIsLoading(true);\n\t\tconst run = transport.resume({\n\t\t\tcontext: transportContext,\n\t\t\trunId: candidateRunId,\n\t\t});\n\t\trunRef.current = run ?? null;\n\t\tsetRunId(candidateRunId);\n\t}, [candidateRunId, transport, transportContext]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tvoid runRef.current?.close?.();\n\t\t\trunRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst prevIsLoadingRef = useRef(isLoading);\n\tuseEffect(() => {\n\t\t// Only clear the run ref on a true → false transition, not on mount\n\t\t// where isLoading starts as false. Clearing on mount would race with\n\t\t// the auto-resume effect and null out the run it just created.\n\t\tif (prevIsLoadingRef.current && !isLoading) {\n\t\t\trunRef.current = null;\n\t\t}\n\t\tprevIsLoadingRef.current = isLoading;\n\t}, [isLoading]);\n\n\treturn {\n\t\terror,\n\t\tisLoading,\n\t\tmessages,\n\t\trunId,\n\t\tsendMessage,\n\t\tsetMessages,\n\t\tstop,\n\t};\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport type {\n\tChatTransportContext,\n\tEventHelpers,\n\tUseStreamChatOptions,\n} from \"./types\";\n\nexport interface ChatControllerOptions<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n> {\n\tappendPart: EventHelpers<TPart>[\"appendPart\"];\n\tappendText: EventHelpers<TPart>[\"appendText\"];\n\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) => void;\n\tgetMessages: () => Message<TPart>[];\n\tonError?: UseStreamChatOptions<TPart, TEvent>[\"onError\"];\n\tonFinish?: UseStreamChatOptions<TPart, TEvent>[\"onFinish\"];\n\tonMessage?: UseStreamChatOptions<TPart, TEvent>[\"onMessage\"];\n\tsetError: Dispatch<SetStateAction<Error | null>>;\n\tsetIsLoading: Dispatch<SetStateAction<boolean>>;\n\tsetMessages: Dispatch<SetStateAction<Message<TPart>[]>>;\n\tsetRunId: (runId: string | null) => void;\n}\n\nlet counter = 0;\n\nexport function generateId(): string {\n\treturn `msg_${Date.now()}_${++counter}`;\n}\n\nexport function createMessage<TPart extends { type: string }>(\n\trole: Message[\"role\"],\n\tparts: TPart[],\n): Message<TPart> {\n\treturn { id: generateId(), role, parts };\n}\n\nexport function createChatTransportContext<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(\n\toptions: ChatControllerOptions<TPart, TEvent>,\n): ChatTransportContext<TPart, TEvent> {\n\tconst helpers: EventHelpers<TPart> = {\n\t\tappendPart: options.appendPart,\n\t\tappendText: options.appendText,\n\t\tsetMessages: options.setMessages,\n\t};\n\n\treturn {\n\t\temit: (event) => {\n\t\t\toptions.eventHandler(event, helpers);\n\t\t},\n\t\tensureAssistantMessage: () => {\n\t\t\toptions.setMessages((prev) => {\n\t\t\t\tconst last = prev[prev.length - 1];\n\t\t\t\tif (last?.role === \"assistant\") {\n\t\t\t\t\treturn prev;\n\t\t\t\t}\n\n\t\t\t\treturn [...prev, createMessage<TPart>(\"assistant\", [])];\n\t\t\t});\n\t\t},\n\t\tfail: (error) => {\n\t\t\toptions.setError(error);\n\t\t\toptions.onError?.(error);\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\t\t},\n\t\tfinish: () => {\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\n\t\t\tconst finalMessages = options.getMessages();\n\t\t\tconst lastMessage = finalMessages[finalMessages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\toptions.onMessage?.(lastMessage);\n\t\t\t}\n\n\t\t\toptions.onFinish?.(finalMessages);\n\t\t},\n\t\tgetMessages: options.getMessages,\n\t\tsetRunId: (runId) => {\n\t\t\toptions.setRunId(runId);\n\t\t},\n\t};\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { parseSSEStream } from \"@deltakit/core\";\nimport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tUseStreamChatOptions,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction resolveRunId(response: unknown): string {\n\tif (!response || typeof response !== \"object\") {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\tconst maybeRunId =\n\t\t\"runId\" in response\n\t\t\t? response.runId\n\t\t\t: \"job_id\" in response\n\t\t\t\t? response.job_id\n\t\t\t\t: null;\n\n\tif (typeof maybeRunId !== \"string\" || maybeRunId.length === 0) {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\treturn maybeRunId;\n}\n\nfunction resolveUrl(\n\turl: string | ((runId: string) => string),\n\trunId: string,\n): string {\n\treturn typeof url === \"function\" ? url(runId) : url.replace(\":runId\", runId);\n}\n\nasync function streamFetchSSE<\n\tTPart extends { type: string },\n\tTEvent extends { type: string },\n>(\n\tresponse: Response,\n\tcontext: ChatTransportContext<TPart, TEvent>,\n\tsignal: AbortSignal,\n): Promise<void> {\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`SSE request failed: ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"Response body is null — SSE streaming not supported\");\n\t}\n\n\tfor await (const event of parseSSEStream(response.body, signal)) {\n\t\tcontext.emit(event as unknown as TEvent);\n\t}\n}\n\nexport function createDirectSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: DirectSSETransportOptions): ChatTransport<TPart, TEvent> {\n\treturn {\n\t\tstart: ({ context, message }) => {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst run: ChatTransportRun = {\n\t\t\t\tclose: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst fetchImpl = config.fetch ?? fetch;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.api, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.body }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.headers,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.method ?? \"POST\",\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\t\tcontext.finish();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn run;\n\t\t},\n\t};\n}\n\nexport function createBackgroundSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: BackgroundSSETransportOptions): ChatTransport<TPart, TEvent> {\n\tconst fetchImpl = config.fetch ?? fetch;\n\n\tconst connect = (\n\t\trunId: string,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t): ChatTransportRun => {\n\t\tconst controller = new AbortController();\n\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst response = await fetchImpl(resolveUrl(config.eventsApi, runId), {\n\t\t\t\t\theaders: config.eventHeaders,\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\tcontext.finish();\n\t\t\t} catch (error) {\n\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t},\n\t\t\tstop: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t\tif (config.cancelApi) {\n\t\t\t\t\tvoid fetchImpl(resolveUrl(config.cancelApi, runId), {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\trunId,\n\t\t};\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\treturn connect(runId, context);\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst startController = new AbortController();\n\t\t\tlet activeRun: ChatTransportRun | undefined;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.startApi, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.startBody }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.startHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.startMethod ?? \"POST\",\n\t\t\t\t\t\tsignal: startController.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Background SSE start failed: ${response.status} ${response.statusText}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as unknown;\n\t\t\t\t\tconst runId = (config.resolveRunId ?? resolveRunId)(data);\n\t\t\t\t\tcontext.setRunId(runId);\n\t\t\t\t\tactiveRun = connect(runId, context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tvoid activeRun?.close?.();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tactiveRun?.stop?.();\n\t\t\t\t},\n\t\t\t\trunId: null,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction defaultParseWebSocketMessage<TEvent extends { type: string }>(\n\tdata: unknown,\n): TEvent | TEvent[] | null {\n\tif (typeof data !== \"string\") {\n\t\treturn null;\n\t}\n\n\tconst parsed = JSON.parse(data) as TEvent | TEvent[];\n\treturn parsed;\n}\n\nexport function createWebSocketTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: WebSocketTransportOptions<TEvent>): ChatTransport<TPart, TEvent> {\n\tconst parseMessage = config.parseMessage ?? defaultParseWebSocketMessage;\n\tconst serializeMessage = config.serializeMessage ?? JSON.stringify;\n\n\tlet resolvedRunId: string | null = null;\n\n\tconst applyIncomingEvents = (\n\t\tparsed: TEvent | TEvent[] | null,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t) => {\n\t\tconst events = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];\n\t\tfor (const item of events) {\n\t\t\tconst nextRunId = config.resolveRunId?.(item) ?? null;\n\t\t\tif (nextRunId) {\n\t\t\t\tresolvedRunId = nextRunId;\n\t\t\t\tcontext.setRunId(nextRunId);\n\t\t\t}\n\t\t\tcontext.emit(item);\n\n\t\t\tif (item.type === \"done\") {\n\t\t\t\tcontext.finish();\n\t\t\t} else if (item.type === \"error\") {\n\t\t\t\tcontext.fail(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\"message\" in item && typeof item.message === \"string\"\n\t\t\t\t\t\t\t? item.message\n\t\t\t\t\t\t\t: \"Stream error\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\tcontext.ensureAssistantMessage();\n\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentResumePayload = false;\n\n\t\t\tconst sendResumePayload = () => {\n\t\t\t\tif (sentResumePayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentResumePayload = true;\n\t\t\t\tconst payload =\n\t\t\t\t\tconfig.buildResumePayload?.(runId) ??\n\t\t\t\t\t({ [config.runIdKey ?? \"runId\"]: runId } as Record<string, unknown>);\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendResumePayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendResumePayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst runId = config.runId ?? config.getResumeKey?.() ?? null;\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentStartPayload = false;\n\n\t\t\tconst sendStartPayload = () => {\n\t\t\t\tif (sentStartPayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentStartPayload = true;\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst payload: Record<string, unknown> = {\n\t\t\t\t\tmessage,\n\t\t\t\t\t...config.body,\n\t\t\t\t};\n\n\t\t\t\tif (runId) {\n\t\t\t\t\tpayload[config.runIdKey ?? \"runId\"] = runId;\n\t\t\t\t}\n\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendStartPayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendStartPayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function resolveTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): ChatTransport<TPart, TEvent> {\n\tif (typeof options.transport === \"object\" && options.transport) {\n\t\treturn options.transport;\n\t}\n\n\tconst transportKind = options.transport ?? \"sse\";\n\n\tif (transportKind === \"background-sse\") {\n\t\tconst config = options.transportOptions?.backgroundSSE;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.backgroundSSE` is required when transport is \"background-sse\"',\n\t\t\t);\n\t\t}\n\t\treturn createBackgroundSSETransport(config);\n\t}\n\n\tif (transportKind === \"websocket\") {\n\t\tconst config = options.transportOptions?.websocket;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.websocket` is required when transport is \"websocket\"',\n\t\t\t);\n\t\t}\n\t\treturn createWebSocketTransport(config);\n\t}\n\n\tconst sseConfig = options.transportOptions?.sse ?? {\n\t\tapi: options.api,\n\t\tbody: options.body,\n\t\theaders: options.headers,\n\t};\n\n\tif (!sseConfig.api) {\n\t\tthrow new Error(\n\t\t\t\"`api` or `transportOptions.sse.api` is required when using the default SSE transport\",\n\t\t);\n\t}\n\n\treturn createDirectSSETransport({\n\t\t...sseConfig,\n\t\tapi: sseConfig.api,\n\t});\n}\n"],"mappings":"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","names":["parseSSEStream","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deltakit/react",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "React hooks for streaming AI conversations with real-time updates",
5
5
  "homepage": "https://deltakit.dev",
6
6
  "type": "module",
@@ -37,7 +37,7 @@
37
37
  "clean": "rm -rf dist"
38
38
  },
39
39
  "dependencies": {
40
- "@deltakit/core": "workspace:*"
40
+ "@deltakit/core": "^0.2.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@testing-library/react": "^16.3.0",