@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 +44 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -5
- package/dist/index.d.ts +0 -5
- package/dist/index.js +44 -105
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
|
|
128
|
+
pendingVerificationFramesRef.current = 0;
|
|
145
129
|
};
|
|
146
|
-
}, [
|
|
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
|
|
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
|
|
585
|
-
|
|
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
|
-
}, [
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
699
|
+
setMessages,
|
|
761
700
|
stop
|
|
762
701
|
};
|
|
763
702
|
}
|
package/dist/index.cjs.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":"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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
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
+
pendingVerificationFramesRef.current = 0;
|
|
121
105
|
};
|
|
122
|
-
}, [
|
|
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
|
|
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
|
|
561
|
-
|
|
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
|
-
}, [
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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":"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","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
|
+
"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": "
|
|
40
|
+
"@deltakit/core": "^0.2.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@testing-library/react": "^16.3.0",
|