@langchain/react 0.3.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +48 -523
  2. package/dist/context.cjs +12 -30
  3. package/dist/context.cjs.map +1 -1
  4. package/dist/context.d.cts +22 -39
  5. package/dist/context.d.cts.map +1 -1
  6. package/dist/context.d.ts +22 -39
  7. package/dist/context.d.ts.map +1 -1
  8. package/dist/context.js +11 -29
  9. package/dist/context.js.map +1 -1
  10. package/dist/index.cjs +29 -30
  11. package/dist/index.d.cts +10 -7
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.ts +10 -7
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +10 -6
  16. package/dist/selectors.cjs +178 -0
  17. package/dist/selectors.cjs.map +1 -0
  18. package/dist/selectors.d.cts +183 -0
  19. package/dist/selectors.d.cts.map +1 -0
  20. package/dist/selectors.d.ts +183 -0
  21. package/dist/selectors.d.ts.map +1 -0
  22. package/dist/selectors.js +168 -0
  23. package/dist/selectors.js.map +1 -0
  24. package/dist/suspense-stream.cjs +34 -159
  25. package/dist/suspense-stream.cjs.map +1 -1
  26. package/dist/suspense-stream.d.cts +15 -71
  27. package/dist/suspense-stream.d.cts.map +1 -1
  28. package/dist/suspense-stream.d.ts +15 -71
  29. package/dist/suspense-stream.d.ts.map +1 -1
  30. package/dist/suspense-stream.js +35 -158
  31. package/dist/suspense-stream.js.map +1 -1
  32. package/dist/use-audio-player.cjs +679 -0
  33. package/dist/use-audio-player.cjs.map +1 -0
  34. package/dist/use-audio-player.d.cts +161 -0
  35. package/dist/use-audio-player.d.cts.map +1 -0
  36. package/dist/use-audio-player.d.ts +161 -0
  37. package/dist/use-audio-player.d.ts.map +1 -0
  38. package/dist/use-audio-player.js +679 -0
  39. package/dist/use-audio-player.js.map +1 -0
  40. package/dist/use-media-url.cjs +49 -0
  41. package/dist/use-media-url.cjs.map +1 -0
  42. package/dist/use-media-url.d.cts +28 -0
  43. package/dist/use-media-url.d.cts.map +1 -0
  44. package/dist/use-media-url.d.ts +28 -0
  45. package/dist/use-media-url.d.ts.map +1 -0
  46. package/dist/use-media-url.js +49 -0
  47. package/dist/use-media-url.js.map +1 -0
  48. package/dist/use-projection.cjs +41 -0
  49. package/dist/use-projection.cjs.map +1 -0
  50. package/dist/use-projection.d.cts +27 -0
  51. package/dist/use-projection.d.cts.map +1 -0
  52. package/dist/use-projection.d.ts +27 -0
  53. package/dist/use-projection.d.ts.map +1 -0
  54. package/dist/use-projection.js +41 -0
  55. package/dist/use-projection.js.map +1 -0
  56. package/dist/use-stream.cjs +185 -0
  57. package/dist/use-stream.cjs.map +1 -0
  58. package/dist/use-stream.d.cts +184 -0
  59. package/dist/use-stream.d.cts.map +1 -0
  60. package/dist/use-stream.d.ts +184 -0
  61. package/dist/use-stream.d.ts.map +1 -0
  62. package/dist/use-stream.js +183 -0
  63. package/dist/use-stream.js.map +1 -0
  64. package/dist/use-video-player.cjs +218 -0
  65. package/dist/use-video-player.cjs.map +1 -0
  66. package/dist/use-video-player.d.cts +65 -0
  67. package/dist/use-video-player.d.cts.map +1 -0
  68. package/dist/use-video-player.d.ts +65 -0
  69. package/dist/use-video-player.d.ts.map +1 -0
  70. package/dist/use-video-player.js +218 -0
  71. package/dist/use-video-player.js.map +1 -0
  72. package/package.json +9 -8
  73. package/dist/stream.cjs +0 -18
  74. package/dist/stream.cjs.map +0 -1
  75. package/dist/stream.custom.cjs +0 -209
  76. package/dist/stream.custom.cjs.map +0 -1
  77. package/dist/stream.custom.d.cts +0 -3
  78. package/dist/stream.custom.d.ts +0 -3
  79. package/dist/stream.custom.js +0 -209
  80. package/dist/stream.custom.js.map +0 -1
  81. package/dist/stream.d.cts +0 -174
  82. package/dist/stream.d.cts.map +0 -1
  83. package/dist/stream.d.ts +0 -174
  84. package/dist/stream.d.ts.map +0 -1
  85. package/dist/stream.js +0 -18
  86. package/dist/stream.js.map +0 -1
  87. package/dist/stream.lgp.cjs +0 -671
  88. package/dist/stream.lgp.cjs.map +0 -1
  89. package/dist/stream.lgp.js +0 -671
  90. package/dist/stream.lgp.js.map +0 -1
  91. package/dist/thread.cjs +0 -18
  92. package/dist/thread.cjs.map +0 -1
  93. package/dist/thread.js +0 -18
  94. package/dist/thread.js.map +0 -1
  95. package/dist/types.d.cts +0 -109
  96. package/dist/types.d.cts.map +0 -1
  97. package/dist/types.d.ts +0 -109
  98. package/dist/types.d.ts.map +0 -1
@@ -0,0 +1,183 @@
1
+ import { UseStreamReturn } from "./use-stream.js";
2
+ import { AssembledToolCall, AudioMedia, Channel, ChannelProjectionOptions, Event, FileMedia, ImageMedia, InferStateType, MessageMetadata, SubagentDiscoverySnapshot, SubgraphDiscoverySnapshot, SubmissionQueueEntry, SubmissionQueueSnapshot, VideoMedia } from "@langchain/langgraph-sdk/stream";
3
+ import { BaseMessage } from "@langchain/core/messages";
4
+
5
+ //#region src/selectors.d.ts
6
+ /**
7
+ * Selector hooks don't need to carry `InterruptType` /
8
+ * `ConfigurableType` — they only ever read state. Accepting a
9
+ * `StateType`-parameterised stream (with the other two generics
10
+ * widened to `any`) lets callers keep their full
11
+ * `useStream<State, Interrupt, Configurable>()` handle
12
+ * without re-declaring the interrupt / configurable shapes at every
13
+ * selector call site.
14
+ */
15
+ type StreamHandle<StateType extends object> = UseStreamReturn<StateType, any, any>;
16
+ /**
17
+ * What a selector hook can be targeted at. Callers can pass any of:
18
+ * - `undefined` — root namespace (cheap: served by the always-on root store)
19
+ * - a {@link SubagentDiscoverySnapshot} — the snapshot returned by `stream.subagents.get(...)`
20
+ * - a {@link SubgraphDiscoverySnapshot} — the snapshot returned by `stream.subgraphs.get(...)`
21
+ * - an explicit `{ namespace: string[] }` — any other namespaced scope
22
+ * - a raw `string[]` — escape hatch identical to the object form
23
+ */
24
+ type SelectorTarget = undefined | null | readonly string[] | {
25
+ namespace: readonly string[];
26
+ } | SubagentDiscoverySnapshot | SubgraphDiscoverySnapshot;
27
+ type AnyStream = UseStreamReturn<any, any, any>;
28
+ /**
29
+ * Subscribe to a scoped `messages` stream. Pass `stream` and
30
+ * optionally a subagent/subgraph snapshot (or any namespaced target).
31
+ *
32
+ * Contract:
33
+ * - At the root (no target) this returns `stream.messages` directly
34
+ * — no extra subscription is opened. `stream.messages` is the
35
+ * live merge of `messages`-channel token deltas and
36
+ * `values.messages` snapshots (see
37
+ * {@link UseStreamReturn.messages}), so token-by-token
38
+ * streaming here depends on the backend emitting `messages`
39
+ * channel events. Backends that only emit `values` updates will
40
+ * render full turns at once rather than streaming.
41
+ * - For any other namespace, the mount triggers a ref-counted
42
+ * `messages` subscription scoped to that namespace. Unmounting
43
+ * the last component that watches this namespace closes the
44
+ * subscription automatically.
45
+ *
46
+ * Messages are always `BaseMessage` class instances from
47
+ * `@langchain/core/messages`.
48
+ */
49
+ declare function useMessages(stream: AnyStream, target?: SelectorTarget): BaseMessage[];
50
+ /**
51
+ * Subscribe to a scoped `tools` (tool-call) stream. Same target and
52
+ * lifecycle rules as {@link useMessages}; at the root this just returns
53
+ * `stream.toolCalls`.
54
+ *
55
+ * The optional generic `T` can be passed to narrow the type of
56
+ * `toolCall.args` on the returned array. Accepts either:
57
+ * - an agent brand (`typeof agent`) — union is derived from the
58
+ * agent's declared tools;
59
+ * - an array of LangGraph tools (`typeof tools`) — union is derived
60
+ * from `ToolCallFromTool<T[number]>`;
61
+ * - any direct `DefaultToolCall` shape.
62
+ *
63
+ * When omitted, returns the plain `AssembledToolCall[]` union used by
64
+ * the controller.
65
+ */
66
+ declare function useToolCalls(stream: AnyStream, target?: SelectorTarget): AssembledToolCall[];
67
+ declare function useToolCalls(stream: AnyStream, target?: SelectorTarget): AssembledToolCall[];
68
+ /**
69
+ * Subscribe to a scoped `values` stream — most-recent state payload
70
+ * for a namespace. Equivalent to reading `stream.values` at the root.
71
+ *
72
+ * When the payload carries a `messages` array, it is coerced to
73
+ * `BaseMessage` instances to keep parity with the root projection.
74
+ *
75
+ * Typing:
76
+ * - **Root** (`useValues(stream)`): returns the `StateType` declared
77
+ * on the parent `useStream<State>()` — no explicit
78
+ * generic required. Non-nullable because the root snapshot always
79
+ * carries `values` (falling back to `initialValues ?? {}`).
80
+ * - **Scoped** (`useValues(stream, target)`): the scoped payload can
81
+ * have a different shape than the root state (e.g. a subagent
82
+ * returning its own substate). Callers should annotate the
83
+ * expected shape explicitly: `useValues<SubagentState>(stream, sub)`.
84
+ * Defaults to `unknown` when not annotated.
85
+ */
86
+ declare function useValues<StateType extends object>(stream: StreamHandle<StateType>): StateType;
87
+ /**
88
+ * Explicit-generic override. Accepts:
89
+ * - an agent brand or compiled graph (unwrapped via
90
+ * {@link InferStateType});
91
+ * - a plain state shape (returned as-is).
92
+ *
93
+ * The root-call form is non-nullable (the root snapshot is always
94
+ * present); the scoped form returns `T | undefined` because a
95
+ * projection may not have emitted a payload yet.
96
+ */
97
+ declare function useValues<T>(stream: AnyStream): InferStateType<T>;
98
+ declare function useValues<T = unknown>(stream: AnyStream, target: SelectorTarget, options?: {
99
+ messagesKey?: string;
100
+ }): T | undefined;
101
+ /**
102
+ * Subscribe to a `custom:<name>` stream extension — most-recent
103
+ * payload emitted by the transformer, scoped to the target namespace.
104
+ */
105
+ declare function useExtension<T = unknown>(stream: AnyStream, name: string, target?: SelectorTarget): T | undefined;
106
+ /**
107
+ * Raw-events escape hatch. Subscribes to one or more channels at a
108
+ * namespace and returns a bounded buffer of raw protocol events.
109
+ * Prefer {@link useMessages} / {@link useToolCalls} / {@link useValues}
110
+ * for the common cases.
111
+ */
112
+ /**
113
+ * Subscribe to a scoped audio-media stream. Returns an array of
114
+ * {@link AudioMedia} handles, one per message containing at least one
115
+ * `AudioBlock` in the target namespace.
116
+ *
117
+ * Each handle is yielded on its first matching `content-block-start`,
118
+ * exposes `.partialBytes` for live access, settles `.blob` /
119
+ * `.objectURL` / `.transcript` on `message-finish`, and surfaces
120
+ * fail-loud errors via `.error`.
121
+ *
122
+ * Pair with {@link useMediaURL} to turn a handle into an `<audio src>`.
123
+ */
124
+ declare function useAudio(stream: AnyStream, target?: SelectorTarget): AudioMedia[];
125
+ /**
126
+ * Subscribe to a scoped image-media stream. See {@link useAudio} for
127
+ * shared semantics; pair with {@link useMediaURL} for `<img src>`.
128
+ */
129
+ declare function useImages(stream: AnyStream, target?: SelectorTarget): ImageMedia[];
130
+ /**
131
+ * Subscribe to a scoped video-media stream. See {@link useAudio} for
132
+ * shared semantics; pair with {@link useMediaURL} for `<video src>`.
133
+ */
134
+ declare function useVideo(stream: AnyStream, target?: SelectorTarget): VideoMedia[];
135
+ /**
136
+ * Subscribe to a scoped file-media stream. See {@link useAudio} for
137
+ * shared semantics; pair with {@link useMediaURL} for an
138
+ * `<a download href>` target.
139
+ */
140
+ declare function useFiles(stream: AnyStream, target?: SelectorTarget): FileMedia[];
141
+ type UseChannelOptions = ChannelProjectionOptions;
142
+ declare function useChannel(stream: AnyStream, channels: readonly Channel[], target?: SelectorTarget, options?: UseChannelOptions): Event[];
143
+ /**
144
+ * Read metadata recorded for a specific message id — today exposes
145
+ * `parentCheckpointId`, the checkpoint the message was first seen on.
146
+ * Designed for fork / edit flows:
147
+ *
148
+ * ```tsx
149
+ * const { parentCheckpointId } = useMessageMetadata(stream, msg.id) ?? {};
150
+ * if (parentCheckpointId) {
151
+ * await stream.submit(input, { forkFrom: { checkpointId: parentCheckpointId } });
152
+ * }
153
+ * ```
154
+ *
155
+ * Returns `undefined` when the id isn't known yet (e.g. the server
156
+ * hasn't emitted `parent_checkpoint` for that message, or the message
157
+ * arrived via `messages`-channel deltas only and no `values` snapshot
158
+ * has landed for it yet).
159
+ */
160
+ declare function useMessageMetadata(stream: AnyStream, messageId: string | undefined): MessageMetadata | undefined;
161
+ /**
162
+ * Reactive handle on the server-side submission queue.
163
+ *
164
+ * Populated when `submit()` is invoked with `multitaskStrategy:
165
+ * "enqueue"` while another run is in flight. The returned object is
166
+ * stable per snapshot so consumers can pass `entries` straight into a
167
+ * `<Fragment key={e.id}>` list without extra memoisation.
168
+ *
169
+ * Today the queue is maintained client-side; once the server starts
170
+ * emitting a dedicated queue channel (roadmap A0.3) the controller
171
+ * will mirror that state directly — the hook surface will not change.
172
+ */
173
+ interface UseSubmissionQueueReturn<StateType extends object = Record<string, unknown>> {
174
+ readonly entries: SubmissionQueueSnapshot<StateType>;
175
+ readonly size: number;
176
+ cancel(id: string): Promise<boolean>;
177
+ clear(): Promise<void>;
178
+ }
179
+ declare function useSubmissionQueue<StateType extends object>(stream: StreamHandle<StateType>): UseSubmissionQueueReturn<StateType>;
180
+ declare function useSubmissionQueue(stream: AnyStream): UseSubmissionQueueReturn;
181
+ //#endregion
182
+ export { SelectorTarget, type SubmissionQueueEntry, type SubmissionQueueSnapshot, UseSubmissionQueueReturn, useAudio, useChannel, useExtension, useFiles, useImages, useMessageMetadata, useMessages, useSubmissionQueue, useToolCalls, useValues, useVideo };
183
+ //# sourceMappingURL=selectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectors.d.ts","names":[],"sources":["../src/selectors.ts"],"mappings":";;;;;;AAuCyB;;;;;;;;KAapB,YAAA,6BAAyC,eAAA,CAC5C,SAAA;AAeF;;;;;;;;AAAA,KAAY,cAAA;EAIN,SAAA;AAAA,IACF,yBAAA,GACA,yBAAA;AAAA,KAiCC,SAAA,GAAY,eAAA;;;AAuBjB;;;;;;;;;;;;;;;AAmCA;;;;iBAnCgB,WAAA,CACd,MAAA,EAAQ,SAAA,EACR,MAAA,GAAS,cAAA,GACR,WAAA;;;;;;;;;;;AAoCH;;;;;;iBAJgB,YAAA,CACd,MAAA,EAAQ,SAAA,EACR,MAAA,GAAS,cAAA,GACR,iBAAA;AAAA,iBACa,YAAA,CACd,MAAA,EAAQ,SAAA,EACR,MAAA,GAAS,cAAA,GACR,iBAAA;;;;;;;;AAqCH;;;;;;;;;;;iBAAgB,SAAA,0BAAA,CACd,MAAA,EAAQ,YAAA,CAAa,SAAA,IACpB,SAAA;;;;AAWH;;;;;;;iBAAgB,SAAA,GAAA,CAAa,MAAA,EAAQ,SAAA,GAAY,cAAA,CAAe,CAAA;AAAA,iBAChD,SAAA,aAAA,CACd,MAAA,EAAQ,SAAA,EACR,MAAA,EAAQ,cAAA,EACR,OAAA;EAAY,WAAA;AAAA,IACX,CAAA;;;;;iBAuBa,YAAA,aAAA,CACd,MAAA,EAAQ,SAAA,EACR,IAAA,UACA,MAAA,GAAS,cAAA,GACR,CAAA;;;;;;;;;;;;;;;;;;AAJH;iBAiCgB,QAAA,CACd,MAAA,EAAQ,SAAA,EACR,MAAA,GAAS,cAAA,GACR,UAAA;;;;;iBAiBa,SAAA,CACd,MAAA,EAAQ,SAAA,EACR,MAAA,GAAS,cAAA,GACR,UAAA;;;;;iBAiBa,QAAA,CACd,MAAA,EAAQ,SAAA,EACR,MAAA,GAAS,cAAA,GACR,UAAA;;;;;;iBAkBa,QAAA,CACd,MAAA,EAAQ,SAAA,EACR,MAAA,GAAS,cAAA,GACR,SAAA;AAAA,KAaS,iBAAA,GAAoB,wBAAA;AAAA,iBAEhB,UAAA,CACd,MAAA,EAAQ,SAAA,EACR,QAAA,WAAmB,OAAA,IACnB,MAAA,GAAS,cAAA,EACT,OAAA,GAAU,iBAAA,GACT,KAAA;;;;;;;;;;;;;AAhEH;;;;;iBA+FgB,kBAAA,CACd,MAAA,EAAQ,SAAA,EACR,SAAA,uBACC,eAAA;;;;;;;;;;AA9EH;;;UAoGiB,wBAAA,4BACY,MAAA;EAAA,SAElB,OAAA,EAAS,uBAAA,CAAwB,SAAA;EAAA,SACjC,IAAA;EACT,MAAA,CAAO,EAAA,WAAa,OAAA;EACpB,KAAA,IAAS,OAAA;AAAA;AAAA,iBAGK,kBAAA,0BAAA,CACd,MAAA,EAAQ,YAAA,CAAa,SAAA,IACpB,wBAAA,CAAyB,SAAA;AAAA,iBACZ,kBAAA,CAAmB,MAAA,EAAQ,SAAA,GAAY,wBAAA"}
@@ -0,0 +1,168 @@
1
+ "use client";
2
+ import { STREAM_CONTROLLER, getRegistry } from "./use-stream.js";
3
+ import { useProjection } from "./use-projection.js";
4
+ import { useMemo, useSyncExternalStore } from "react";
5
+ import { NAMESPACE_SEPARATOR, audioProjection, channelProjection, extensionProjection, filesProjection, imagesProjection, messagesProjection, toolCallsProjection, valuesProjection, videoProjection } from "@langchain/langgraph-sdk/stream";
6
+ //#region src/selectors.ts
7
+ function resolveNamespace(target) {
8
+ if (target == null) return EMPTY_NAMESPACE;
9
+ if (Array.isArray(target)) return target;
10
+ return target.namespace ?? EMPTY_NAMESPACE;
11
+ }
12
+ const EMPTY_NAMESPACE = [];
13
+ function isRoot(namespace) {
14
+ return namespace.length === 0;
15
+ }
16
+ function namespaceKey(namespace) {
17
+ return namespace.join(NAMESPACE_SEPARATOR);
18
+ }
19
+ /**
20
+ * Subscribe to a scoped `messages` stream. Pass `stream` and
21
+ * optionally a subagent/subgraph snapshot (or any namespaced target).
22
+ *
23
+ * Contract:
24
+ * - At the root (no target) this returns `stream.messages` directly
25
+ * — no extra subscription is opened. `stream.messages` is the
26
+ * live merge of `messages`-channel token deltas and
27
+ * `values.messages` snapshots (see
28
+ * {@link UseStreamReturn.messages}), so token-by-token
29
+ * streaming here depends on the backend emitting `messages`
30
+ * channel events. Backends that only emit `values` updates will
31
+ * render full turns at once rather than streaming.
32
+ * - For any other namespace, the mount triggers a ref-counted
33
+ * `messages` subscription scoped to that namespace. Unmounting
34
+ * the last component that watches this namespace closes the
35
+ * subscription automatically.
36
+ *
37
+ * Messages are always `BaseMessage` class instances from
38
+ * `@langchain/core/messages`.
39
+ */
40
+ function useMessages(stream, target) {
41
+ const namespace = resolveNamespace(target);
42
+ const key = `messages|${namespaceKey(namespace)}`;
43
+ const scoped = useProjection(isRoot(namespace) ? null : getRegistry(stream), () => messagesProjection(namespace), key, EMPTY_MESSAGES);
44
+ return isRoot(namespace) ? stream.messages : scoped;
45
+ }
46
+ const EMPTY_MESSAGES = [];
47
+ function useToolCalls(stream, target) {
48
+ const namespace = resolveNamespace(target);
49
+ const key = `toolCalls|${namespaceKey(namespace)}`;
50
+ const scoped = useProjection(isRoot(namespace) ? null : getRegistry(stream), () => toolCallsProjection(namespace), key, EMPTY_TOOLCALLS);
51
+ return isRoot(namespace) ? stream.toolCalls : scoped;
52
+ }
53
+ const EMPTY_TOOLCALLS = [];
54
+ function useValues(stream, target, options) {
55
+ const namespace = resolveNamespace(target);
56
+ const messagesKey = options?.messagesKey ?? "messages";
57
+ const key = `values|${messagesKey}|${namespaceKey(namespace)}`;
58
+ const scoped = useProjection(isRoot(namespace) ? null : getRegistry(stream), () => valuesProjection(namespace, messagesKey), key, void 0);
59
+ return isRoot(namespace) ? stream.values : scoped;
60
+ }
61
+ /**
62
+ * Subscribe to a `custom:<name>` stream extension — most-recent
63
+ * payload emitted by the transformer, scoped to the target namespace.
64
+ */
65
+ function useExtension(stream, name, target) {
66
+ const namespace = resolveNamespace(target);
67
+ const key = `extension|${name}|${namespaceKey(namespace)}`;
68
+ return useProjection(getRegistry(stream), () => extensionProjection(name, namespace), key, void 0);
69
+ }
70
+ /**
71
+ * Raw-events escape hatch. Subscribes to one or more channels at a
72
+ * namespace and returns a bounded buffer of raw protocol events.
73
+ * Prefer {@link useMessages} / {@link useToolCalls} / {@link useValues}
74
+ * for the common cases.
75
+ */
76
+ /**
77
+ * Subscribe to a scoped audio-media stream. Returns an array of
78
+ * {@link AudioMedia} handles, one per message containing at least one
79
+ * `AudioBlock` in the target namespace.
80
+ *
81
+ * Each handle is yielded on its first matching `content-block-start`,
82
+ * exposes `.partialBytes` for live access, settles `.blob` /
83
+ * `.objectURL` / `.transcript` on `message-finish`, and surfaces
84
+ * fail-loud errors via `.error`.
85
+ *
86
+ * Pair with {@link useMediaURL} to turn a handle into an `<audio src>`.
87
+ */
88
+ function useAudio(stream, target) {
89
+ const namespace = resolveNamespace(target);
90
+ const key = `audio|${namespaceKey(namespace)}`;
91
+ return useProjection(getRegistry(stream), () => audioProjection(namespace), key, EMPTY_AUDIO);
92
+ }
93
+ const EMPTY_AUDIO = [];
94
+ /**
95
+ * Subscribe to a scoped image-media stream. See {@link useAudio} for
96
+ * shared semantics; pair with {@link useMediaURL} for `<img src>`.
97
+ */
98
+ function useImages(stream, target) {
99
+ const namespace = resolveNamespace(target);
100
+ const key = `images|${namespaceKey(namespace)}`;
101
+ return useProjection(getRegistry(stream), () => imagesProjection(namespace), key, EMPTY_IMAGES);
102
+ }
103
+ const EMPTY_IMAGES = [];
104
+ /**
105
+ * Subscribe to a scoped video-media stream. See {@link useAudio} for
106
+ * shared semantics; pair with {@link useMediaURL} for `<video src>`.
107
+ */
108
+ function useVideo(stream, target) {
109
+ const namespace = resolveNamespace(target);
110
+ const key = `video|${namespaceKey(namespace)}`;
111
+ return useProjection(getRegistry(stream), () => videoProjection(namespace), key, EMPTY_VIDEO);
112
+ }
113
+ const EMPTY_VIDEO = [];
114
+ /**
115
+ * Subscribe to a scoped file-media stream. See {@link useAudio} for
116
+ * shared semantics; pair with {@link useMediaURL} for an
117
+ * `<a download href>` target.
118
+ */
119
+ function useFiles(stream, target) {
120
+ const namespace = resolveNamespace(target);
121
+ const key = `files|${namespaceKey(namespace)}`;
122
+ return useProjection(getRegistry(stream), () => filesProjection(namespace), key, EMPTY_FILES);
123
+ }
124
+ const EMPTY_FILES = [];
125
+ function useChannel(stream, channels, target, options) {
126
+ const namespace = resolveNamespace(target);
127
+ const channelKey = useMemo(() => [...channels].sort().join(","), [channels]);
128
+ const key = `channel|${options?.bufferSize ?? "default"}|${options?.replay ?? true ? "replay" : "live"}|${channelKey}|${namespaceKey(namespace)}`;
129
+ return useProjection(getRegistry(stream), () => channelProjection(channels, namespace, options), key, EMPTY_EVENTS);
130
+ }
131
+ const EMPTY_EVENTS = [];
132
+ /**
133
+ * Read metadata recorded for a specific message id — today exposes
134
+ * `parentCheckpointId`, the checkpoint the message was first seen on.
135
+ * Designed for fork / edit flows:
136
+ *
137
+ * ```tsx
138
+ * const { parentCheckpointId } = useMessageMetadata(stream, msg.id) ?? {};
139
+ * if (parentCheckpointId) {
140
+ * await stream.submit(input, { forkFrom: { checkpointId: parentCheckpointId } });
141
+ * }
142
+ * ```
143
+ *
144
+ * Returns `undefined` when the id isn't known yet (e.g. the server
145
+ * hasn't emitted `parent_checkpoint` for that message, or the message
146
+ * arrived via `messages`-channel deltas only and no `values` snapshot
147
+ * has landed for it yet).
148
+ */
149
+ function useMessageMetadata(stream, messageId) {
150
+ const store = stream[STREAM_CONTROLLER].messageMetadataStore;
151
+ const snapshot = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
152
+ return messageId == null ? void 0 : snapshot.get(messageId);
153
+ }
154
+ function useSubmissionQueue(stream) {
155
+ const controller = stream[STREAM_CONTROLLER];
156
+ const store = controller.queueStore;
157
+ const entries = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
158
+ return useMemo(() => ({
159
+ entries,
160
+ size: entries.length,
161
+ cancel: (id) => controller.cancelQueued(id),
162
+ clear: () => controller.clearQueue()
163
+ }), [entries, controller]);
164
+ }
165
+ //#endregion
166
+ export { useAudio, useChannel, useExtension, useFiles, useImages, useMessageMetadata, useMessages, useSubmissionQueue, useToolCalls, useValues, useVideo };
167
+
168
+ //# sourceMappingURL=selectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectors.js","names":[],"sources":["../src/selectors.ts"],"sourcesContent":["/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */\n\n\"use client\";\n\nimport { useMemo, useSyncExternalStore } from \"react\";\nimport type { BaseMessage } from \"@langchain/core/messages\";\nimport type {\n MessageMetadata,\n MessageMetadataMap,\n SubmissionQueueEntry,\n SubmissionQueueSnapshot,\n} from \"@langchain/langgraph-sdk/stream\";\nimport {\n NAMESPACE_SEPARATOR,\n audioProjection,\n channelProjection,\n extensionProjection,\n filesProjection,\n imagesProjection,\n messagesProjection,\n toolCallsProjection,\n valuesProjection,\n videoProjection,\n type AssembledToolCall,\n type AudioMedia,\n type Channel,\n type ChannelProjectionOptions,\n type Event,\n type FileMedia,\n type ImageMedia,\n type InferStateType,\n type SubagentDiscoverySnapshot,\n type SubgraphDiscoverySnapshot,\n type VideoMedia,\n} from \"@langchain/langgraph-sdk/stream\";\nimport {\n getRegistry,\n STREAM_CONTROLLER,\n type UseStreamReturn,\n} from \"./use-stream.js\";\nimport { useProjection } from \"./use-projection.js\";\n\n/**\n * Selector hooks don't need to carry `InterruptType` /\n * `ConfigurableType` — they only ever read state. Accepting a\n * `StateType`-parameterised stream (with the other two generics\n * widened to `any`) lets callers keep their full\n * `useStream<State, Interrupt, Configurable>()` handle\n * without re-declaring the interrupt / configurable shapes at every\n * selector call site.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype StreamHandle<StateType extends object> = UseStreamReturn<\n StateType,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n>;\n\n/**\n * What a selector hook can be targeted at. Callers can pass any of:\n * - `undefined` — root namespace (cheap: served by the always-on root store)\n * - a {@link SubagentDiscoverySnapshot} — the snapshot returned by `stream.subagents.get(...)`\n * - a {@link SubgraphDiscoverySnapshot} — the snapshot returned by `stream.subgraphs.get(...)`\n * - an explicit `{ namespace: string[] }` — any other namespaced scope\n * - a raw `string[]` — escape hatch identical to the object form\n */\nexport type SelectorTarget =\n | undefined\n | null\n | readonly string[]\n | { namespace: readonly string[] }\n | SubagentDiscoverySnapshot\n | SubgraphDiscoverySnapshot;\n\nfunction resolveNamespace(target: SelectorTarget): readonly string[] {\n if (target == null) return EMPTY_NAMESPACE;\n if (Array.isArray(target)) return target;\n const obj = target as { namespace?: readonly string[] };\n return obj.namespace ?? EMPTY_NAMESPACE;\n}\n\nconst EMPTY_NAMESPACE: readonly string[] = [];\n\nfunction isRoot(namespace: readonly string[]): boolean {\n return namespace.length === 0;\n}\n\nfunction namespaceKey(namespace: readonly string[]): string {\n return namespace.join(NAMESPACE_SEPARATOR);\n}\n\n// The stream type we accept for selectors — purposely loose so\n// selector hooks remain callable from components that don't carry\n// the exact State/Interrupt/Configurable generics. We use `any` for\n// all three generics because `UseStreamReturn` is\n// invariant in `State` and `Configurable` (they flow through both\n// reader and writer positions), so a concrete\n// `useStream<typeof agent>()` handle wouldn't flow into\n// a `<object, unknown, object>` slot otherwise.\n//\n// Typed selectors (`useValues<S>` etc.) use {@link StreamHandle}\n// above so the concrete `StateType` flows into the return; hooks\n// that don't depend on state (`useMessages`, `useAudio`, …) stay on\n// `AnyStream` for maximum flexibility.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyStream = UseStreamReturn<any, any, any>;\n\n/**\n * Subscribe to a scoped `messages` stream. Pass `stream` and\n * optionally a subagent/subgraph snapshot (or any namespaced target).\n *\n * Contract:\n * - At the root (no target) this returns `stream.messages` directly\n * — no extra subscription is opened. `stream.messages` is the\n * live merge of `messages`-channel token deltas and\n * `values.messages` snapshots (see\n * {@link UseStreamReturn.messages}), so token-by-token\n * streaming here depends on the backend emitting `messages`\n * channel events. Backends that only emit `values` updates will\n * render full turns at once rather than streaming.\n * - For any other namespace, the mount triggers a ref-counted\n * `messages` subscription scoped to that namespace. Unmounting\n * the last component that watches this namespace closes the\n * subscription automatically.\n *\n * Messages are always `BaseMessage` class instances from\n * `@langchain/core/messages`.\n */\nexport function useMessages(\n stream: AnyStream,\n target?: SelectorTarget\n): BaseMessage[] {\n const namespace = resolveNamespace(target);\n const key = `messages|${namespaceKey(namespace)}`;\n const registry = isRoot(namespace) ? null : getRegistry(stream);\n const scoped = useProjection<BaseMessage[]>(\n registry,\n () => messagesProjection(namespace),\n key,\n EMPTY_MESSAGES\n );\n return isRoot(namespace) ? stream.messages : scoped;\n}\n\nconst EMPTY_MESSAGES: BaseMessage[] = [];\n\n/**\n * Subscribe to a scoped `tools` (tool-call) stream. Same target and\n * lifecycle rules as {@link useMessages}; at the root this just returns\n * `stream.toolCalls`.\n *\n * The optional generic `T` can be passed to narrow the type of\n * `toolCall.args` on the returned array. Accepts either:\n * - an agent brand (`typeof agent`) — union is derived from the\n * agent's declared tools;\n * - an array of LangGraph tools (`typeof tools`) — union is derived\n * from `ToolCallFromTool<T[number]>`;\n * - any direct `DefaultToolCall` shape.\n *\n * When omitted, returns the plain `AssembledToolCall[]` union used by\n * the controller.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function useToolCalls(\n stream: AnyStream,\n target?: SelectorTarget\n): AssembledToolCall[];\nexport function useToolCalls(\n stream: AnyStream,\n target?: SelectorTarget\n): AssembledToolCall[];\nexport function useToolCalls(\n stream: AnyStream,\n target?: SelectorTarget\n): AssembledToolCall[] {\n const namespace = resolveNamespace(target);\n const key = `toolCalls|${namespaceKey(namespace)}`;\n const registry = isRoot(namespace) ? null : getRegistry(stream);\n const scoped = useProjection<AssembledToolCall[]>(\n registry,\n () => toolCallsProjection(namespace),\n key,\n EMPTY_TOOLCALLS\n );\n return isRoot(namespace) ? stream.toolCalls : scoped;\n}\n\nconst EMPTY_TOOLCALLS: AssembledToolCall[] = [];\n\n/**\n * Subscribe to a scoped `values` stream — most-recent state payload\n * for a namespace. Equivalent to reading `stream.values` at the root.\n *\n * When the payload carries a `messages` array, it is coerced to\n * `BaseMessage` instances to keep parity with the root projection.\n *\n * Typing:\n * - **Root** (`useValues(stream)`): returns the `StateType` declared\n * on the parent `useStream<State>()` — no explicit\n * generic required. Non-nullable because the root snapshot always\n * carries `values` (falling back to `initialValues ?? {}`).\n * - **Scoped** (`useValues(stream, target)`): the scoped payload can\n * have a different shape than the root state (e.g. a subagent\n * returning its own substate). Callers should annotate the\n * expected shape explicitly: `useValues<SubagentState>(stream, sub)`.\n * Defaults to `unknown` when not annotated.\n */\nexport function useValues<StateType extends object>(\n stream: StreamHandle<StateType>\n): StateType;\n/**\n * Explicit-generic override. Accepts:\n * - an agent brand or compiled graph (unwrapped via\n * {@link InferStateType});\n * - a plain state shape (returned as-is).\n *\n * The root-call form is non-nullable (the root snapshot is always\n * present); the scoped form returns `T | undefined` because a\n * projection may not have emitted a payload yet.\n */\nexport function useValues<T>(stream: AnyStream): InferStateType<T>;\nexport function useValues<T = unknown>(\n stream: AnyStream,\n target: SelectorTarget,\n options?: { messagesKey?: string }\n): T | undefined;\nexport function useValues(\n stream: AnyStream,\n target?: SelectorTarget,\n options?: { messagesKey?: string }\n): unknown {\n const namespace = resolveNamespace(target);\n const messagesKey = options?.messagesKey ?? \"messages\";\n const key = `values|${messagesKey}|${namespaceKey(namespace)}`;\n const registry = isRoot(namespace) ? null : getRegistry(stream);\n const scoped = useProjection<unknown>(\n registry,\n () => valuesProjection<unknown>(namespace, messagesKey),\n key,\n undefined\n );\n return isRoot(namespace) ? stream.values : scoped;\n}\n\n/**\n * Subscribe to a `custom:<name>` stream extension — most-recent\n * payload emitted by the transformer, scoped to the target namespace.\n */\nexport function useExtension<T = unknown>(\n stream: AnyStream,\n name: string,\n target?: SelectorTarget\n): T | undefined {\n const namespace = resolveNamespace(target);\n const key = `extension|${name}|${namespaceKey(namespace)}`;\n return useProjection<T | undefined>(\n getRegistry(stream),\n () => extensionProjection<T>(name, namespace),\n key,\n undefined\n );\n}\n\n/**\n * Raw-events escape hatch. Subscribes to one or more channels at a\n * namespace and returns a bounded buffer of raw protocol events.\n * Prefer {@link useMessages} / {@link useToolCalls} / {@link useValues}\n * for the common cases.\n */\n/**\n * Subscribe to a scoped audio-media stream. Returns an array of\n * {@link AudioMedia} handles, one per message containing at least one\n * `AudioBlock` in the target namespace.\n *\n * Each handle is yielded on its first matching `content-block-start`,\n * exposes `.partialBytes` for live access, settles `.blob` /\n * `.objectURL` / `.transcript` on `message-finish`, and surfaces\n * fail-loud errors via `.error`.\n *\n * Pair with {@link useMediaURL} to turn a handle into an `<audio src>`.\n */\nexport function useAudio(\n stream: AnyStream,\n target?: SelectorTarget\n): AudioMedia[] {\n const namespace = resolveNamespace(target);\n const key = `audio|${namespaceKey(namespace)}`;\n return useProjection<AudioMedia[]>(\n getRegistry(stream),\n () => audioProjection(namespace),\n key,\n EMPTY_AUDIO\n );\n}\n\nconst EMPTY_AUDIO: AudioMedia[] = [];\n\n/**\n * Subscribe to a scoped image-media stream. See {@link useAudio} for\n * shared semantics; pair with {@link useMediaURL} for `<img src>`.\n */\nexport function useImages(\n stream: AnyStream,\n target?: SelectorTarget\n): ImageMedia[] {\n const namespace = resolveNamespace(target);\n const key = `images|${namespaceKey(namespace)}`;\n return useProjection<ImageMedia[]>(\n getRegistry(stream),\n () => imagesProjection(namespace),\n key,\n EMPTY_IMAGES\n );\n}\n\nconst EMPTY_IMAGES: ImageMedia[] = [];\n\n/**\n * Subscribe to a scoped video-media stream. See {@link useAudio} for\n * shared semantics; pair with {@link useMediaURL} for `<video src>`.\n */\nexport function useVideo(\n stream: AnyStream,\n target?: SelectorTarget\n): VideoMedia[] {\n const namespace = resolveNamespace(target);\n const key = `video|${namespaceKey(namespace)}`;\n return useProjection<VideoMedia[]>(\n getRegistry(stream),\n () => videoProjection(namespace),\n key,\n EMPTY_VIDEO\n );\n}\n\nconst EMPTY_VIDEO: VideoMedia[] = [];\n\n/**\n * Subscribe to a scoped file-media stream. See {@link useAudio} for\n * shared semantics; pair with {@link useMediaURL} for an\n * `<a download href>` target.\n */\nexport function useFiles(\n stream: AnyStream,\n target?: SelectorTarget\n): FileMedia[] {\n const namespace = resolveNamespace(target);\n const key = `files|${namespaceKey(namespace)}`;\n return useProjection<FileMedia[]>(\n getRegistry(stream),\n () => filesProjection(namespace),\n key,\n EMPTY_FILES\n );\n}\n\nconst EMPTY_FILES: FileMedia[] = [];\n\nexport type UseChannelOptions = ChannelProjectionOptions;\n\nexport function useChannel(\n stream: AnyStream,\n channels: readonly Channel[],\n target?: SelectorTarget,\n options?: UseChannelOptions\n): Event[] {\n const namespace = resolveNamespace(target);\n const channelKey = useMemo(() => [...channels].sort().join(\",\"), [channels]);\n const key = `channel|${options?.bufferSize ?? \"default\"}|${(options?.replay ?? true) ? \"replay\" : \"live\"}|${channelKey}|${namespaceKey(namespace)}`;\n return useProjection<Event[]>(\n getRegistry(stream),\n () => channelProjection(channels, namespace, options),\n key,\n EMPTY_EVENTS\n );\n}\n\nconst EMPTY_EVENTS: Event[] = [];\n\n/**\n * Read metadata recorded for a specific message id — today exposes\n * `parentCheckpointId`, the checkpoint the message was first seen on.\n * Designed for fork / edit flows:\n *\n * ```tsx\n * const { parentCheckpointId } = useMessageMetadata(stream, msg.id) ?? {};\n * if (parentCheckpointId) {\n * await stream.submit(input, { forkFrom: { checkpointId: parentCheckpointId } });\n * }\n * ```\n *\n * Returns `undefined` when the id isn't known yet (e.g. the server\n * hasn't emitted `parent_checkpoint` for that message, or the message\n * arrived via `messages`-channel deltas only and no `values` snapshot\n * has landed for it yet).\n */\nexport function useMessageMetadata(\n stream: AnyStream,\n messageId: string | undefined\n): MessageMetadata | undefined {\n const store = stream[STREAM_CONTROLLER].messageMetadataStore;\n const snapshot = useSyncExternalStore<MessageMetadataMap>(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot\n );\n return messageId == null ? undefined : snapshot.get(messageId);\n}\n\n/**\n * Reactive handle on the server-side submission queue.\n *\n * Populated when `submit()` is invoked with `multitaskStrategy:\n * \"enqueue\"` while another run is in flight. The returned object is\n * stable per snapshot so consumers can pass `entries` straight into a\n * `<Fragment key={e.id}>` list without extra memoisation.\n *\n * Today the queue is maintained client-side; once the server starts\n * emitting a dedicated queue channel (roadmap A0.3) the controller\n * will mirror that state directly — the hook surface will not change.\n */\nexport interface UseSubmissionQueueReturn<\n StateType extends object = Record<string, unknown>,\n> {\n readonly entries: SubmissionQueueSnapshot<StateType>;\n readonly size: number;\n cancel(id: string): Promise<boolean>;\n clear(): Promise<void>;\n}\n\nexport function useSubmissionQueue<StateType extends object>(\n stream: StreamHandle<StateType>\n): UseSubmissionQueueReturn<StateType>;\nexport function useSubmissionQueue(stream: AnyStream): UseSubmissionQueueReturn;\nexport function useSubmissionQueue(\n stream: AnyStream\n): UseSubmissionQueueReturn {\n const controller = stream[STREAM_CONTROLLER];\n const store = controller.queueStore;\n const entries = useSyncExternalStore<SubmissionQueueSnapshot>(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot\n );\n return useMemo<UseSubmissionQueueReturn>(\n () => ({\n entries,\n size: entries.length,\n cancel: (id) => controller.cancelQueued(id),\n clear: () => controller.clearQueue(),\n }),\n [entries, controller]\n );\n}\n\nexport type { SubmissionQueueEntry, SubmissionQueueSnapshot };\n"],"mappings":"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"}
@@ -1,141 +1,30 @@
1
1
  "use client";
2
- const require_stream_lgp = require("./stream.lgp.cjs");
3
- let react = require("react");
4
- let _langchain_langgraph_sdk_client = require("@langchain/langgraph-sdk/client");
5
- //#region src/suspense-stream.tsx
6
- const defaultSuspenseCache = /* @__PURE__ */ new Map();
7
- function createSuspenseCache() {
8
- return /* @__PURE__ */ new Map();
9
- }
10
- function getCacheKey(client, threadId, limit) {
11
- return `suspense:${(0, _langchain_langgraph_sdk_client.getClientConfigHash)(client)}:${threadId}:${limit}`;
12
- }
13
- function fetchThreadHistory(client, threadId, options) {
14
- if (options?.limit === false) return client.threads.getState(threadId).then((state) => {
15
- if (state.checkpoint == null) return [];
16
- return [state];
17
- });
18
- const limit = typeof options?.limit === "number" ? options.limit : 10;
19
- return client.threads.getHistory(threadId, { limit });
20
- }
21
- function getOrCreateCacheEntry(cache, client, threadId, limit) {
22
- const key = getCacheKey(client, threadId, limit);
23
- let entry = cache.get(key);
24
- if (!entry) {
25
- entry = {
26
- status: "pending",
27
- promise: fetchThreadHistory(client, threadId, { limit }).then((data) => {
28
- cache.set(key, {
29
- status: "resolved",
30
- data
31
- });
32
- }).catch((error) => {
33
- cache.set(key, {
34
- status: "rejected",
35
- error
36
- });
37
- })
38
- };
39
- cache.set(key, entry);
40
- }
41
- return entry;
42
- }
43
- /**
44
- * Clear the internal Suspense cache used by {@link useSuspenseStream}.
45
- *
46
- * Call this from an Error Boundary's `onReset` callback so that a retry
47
- * triggers a fresh thread-history fetch rather than re-throwing the
48
- * cached error.
49
- *
50
- * @example
51
- * ```tsx
52
- * <ErrorBoundary
53
- * onReset={() => invalidateSuspenseCache()}
54
- * fallbackRender={({ resetErrorBoundary }) => (
55
- * <button onClick={resetErrorBoundary}>Retry</button>
56
- * )}
57
- * >
58
- * <Suspense fallback={<Spinner />}>
59
- * <Chat />
60
- * </Suspense>
61
- * </ErrorBoundary>
62
- * ```
63
- */
64
- function invalidateSuspenseCache(cache = defaultSuspenseCache) {
65
- cache.clear();
2
+ const require_use_stream = require("./use-stream.cjs");
3
+ //#region src/suspense-stream.ts
4
+ const suspenseEntries = /* @__PURE__ */ new Map();
5
+ function suspenseKey(options) {
6
+ if (options.threadId == null) return null;
7
+ return `${options.apiUrl ?? "_"}::${options.assistantId ?? "_"}::${options.threadId}`;
66
8
  }
67
9
  function useSuspenseStream(options) {
68
- const cache = options.suspenseCache ?? defaultSuspenseCache;
69
- const client = (0, react.useMemo)(() => options.client ?? new _langchain_langgraph_sdk_client.Client({
70
- apiUrl: options.apiUrl,
71
- apiKey: options.apiKey,
72
- callerOptions: options.callerOptions,
73
- defaultHeaders: options.defaultHeaders
74
- }), [
75
- options.client,
76
- options.apiKey,
77
- options.apiUrl,
78
- options.callerOptions,
79
- options.defaultHeaders
80
- ]);
81
- const { threadId } = options;
82
- const historyLimit = typeof options.fetchStateHistory === "object" && options.fetchStateHistory != null ? options.fetchStateHistory.limit ?? false : options.fetchStateHistory ?? false;
83
- const needsHistoryFetch = threadId != null && options.thread == null;
84
- let cacheEntry;
85
- if (needsHistoryFetch) cacheEntry = getOrCreateCacheEntry(cache, client, threadId, historyLimit);
86
- const cachedData = cacheEntry?.status === "resolved" ? cacheEntry.data : void 0;
87
- const cachedDataRef = (0, react.useRef)(cachedData);
88
- if (cachedData != null) cachedDataRef.current = cachedData;
89
- const [, setMutateVersion] = (0, react.useState)(0);
90
- const mutate = (0, react.useCallback)(async (mutateId) => {
91
- const fetchId = mutateId ?? threadId;
92
- if (!fetchId) return void 0;
93
- try {
94
- const data = await fetchThreadHistory(client, fetchId, { limit: historyLimit });
95
- const key = getCacheKey(client, fetchId, historyLimit);
96
- cache.set(key, {
97
- status: "resolved",
98
- data
99
- });
100
- cachedDataRef.current = data;
101
- setMutateVersion((v) => v + 1);
102
- return data;
103
- } catch {
104
- return;
105
- }
106
- }, [
107
- cache,
108
- client,
109
- threadId,
110
- historyLimit
111
- ]);
112
- const thread = (0, react.useMemo)(() => {
113
- if (!needsHistoryFetch) return options.thread;
114
- return {
115
- data: cachedDataRef.current,
116
- error: void 0,
117
- isLoading: false,
118
- mutate
10
+ const key = suspenseKey(options);
11
+ const stream = require_use_stream.useStream(options);
12
+ if (key != null && !suspenseEntries.has(key)) {
13
+ const entry = {
14
+ promise: stream.hydrationPromise.then(() => {
15
+ entry.settled = true;
16
+ }, (error) => {
17
+ entry.settled = true;
18
+ entry.error = error instanceof Error ? error : new Error(String(error));
19
+ throw entry.error;
20
+ }),
21
+ settled: false
119
22
  };
120
- }, [
121
- needsHistoryFetch,
122
- options.thread,
123
- cachedData,
124
- mutate
125
- ]);
126
- const stream = require_stream_lgp.useStreamLGP({
127
- ...options,
128
- client,
129
- thread
130
- });
131
- if (needsHistoryFetch && cacheEntry && !stream.isLoading) {
132
- if (cacheEntry.status === "pending") throw cacheEntry.promise;
133
- if (cacheEntry.status === "rejected") {
134
- const key = getCacheKey(client, threadId, historyLimit);
135
- cache.delete(key);
136
- throw cacheEntry.error instanceof Error ? cacheEntry.error : new Error(String(cacheEntry.error));
137
- }
23
+ suspenseEntries.set(key, entry);
138
24
  }
25
+ const entry = key != null ? suspenseEntries.get(key) : void 0;
26
+ if (entry && !entry.settled) throw entry.promise;
27
+ if (entry?.error != null) throw entry.error;
139
28
  if (stream.error != null && !stream.isLoading) throw stream.error instanceof Error ? stream.error : new Error(String(stream.error));
140
29
  return {
141
30
  get values() {
@@ -147,10 +36,6 @@ function useSuspenseStream(options) {
147
36
  get toolCalls() {
148
37
  return stream.toolCalls;
149
38
  },
150
- get toolProgress() {
151
- return stream.toolProgress;
152
- },
153
- getToolCalls: stream.getToolCalls.bind(stream),
154
39
  get interrupt() {
155
40
  return stream.interrupt;
156
41
  },
@@ -160,35 +45,27 @@ function useSuspenseStream(options) {
160
45
  get subagents() {
161
46
  return stream.subagents;
162
47
  },
163
- get activeSubagents() {
164
- return stream.activeSubagents;
48
+ get subgraphs() {
49
+ return stream.subgraphs;
165
50
  },
166
- getSubagent: stream.getSubagent.bind(stream),
167
- getSubagentsByType: stream.getSubagentsByType.bind(stream),
168
- getSubagentsByMessage: stream.getSubagentsByMessage.bind(stream),
169
- getMessagesMetadata: stream.getMessagesMetadata.bind(stream),
170
- get history() {
171
- return stream.history;
51
+ get subgraphsByNode() {
52
+ return stream.subgraphsByNode;
172
53
  },
173
- get experimental_branchTree() {
174
- return stream.experimental_branchTree;
175
- },
176
- stop: stream.stop,
177
54
  submit: stream.submit,
178
- switchThread: stream.switchThread,
179
- joinStream: stream.joinStream,
180
- get branch() {
181
- return stream.branch;
182
- },
183
- setBranch: stream.setBranch,
55
+ stop: stream.stop,
56
+ respond: stream.respond,
57
+ getThread: stream.getThread,
184
58
  get client() {
185
59
  return stream.client;
186
60
  },
187
61
  get assistantId() {
188
62
  return stream.assistantId;
189
63
  },
190
- get queue() {
191
- return stream.queue;
64
+ get threadId() {
65
+ return stream.threadId;
66
+ },
67
+ get error() {
68
+ return stream.error;
192
69
  },
193
70
  get isStreaming() {
194
71
  return stream.isLoading;
@@ -196,8 +73,6 @@ function useSuspenseStream(options) {
196
73
  };
197
74
  }
198
75
  //#endregion
199
- exports.createSuspenseCache = createSuspenseCache;
200
- exports.invalidateSuspenseCache = invalidateSuspenseCache;
201
76
  exports.useSuspenseStream = useSuspenseStream;
202
77
 
203
78
  //# sourceMappingURL=suspense-stream.cjs.map