@langchain/langgraph-sdk 1.5.6 → 1.6.1

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 (68) hide show
  1. package/dist/index.d.cts +5 -1
  2. package/dist/index.d.ts +5 -1
  3. package/dist/react/index.cjs +6 -0
  4. package/dist/react/index.d.cts +7 -2
  5. package/dist/react/index.d.ts +7 -2
  6. package/dist/react/index.js +2 -1
  7. package/dist/react/stream.cjs.map +1 -1
  8. package/dist/react/stream.custom.cjs +36 -7
  9. package/dist/react/stream.custom.cjs.map +1 -1
  10. package/dist/react/stream.custom.d.cts.map +1 -1
  11. package/dist/react/stream.custom.d.ts.map +1 -1
  12. package/dist/react/stream.custom.js +36 -7
  13. package/dist/react/stream.custom.js.map +1 -1
  14. package/dist/react/stream.d.cts +51 -31
  15. package/dist/react/stream.d.cts.map +1 -1
  16. package/dist/react/stream.d.ts +51 -31
  17. package/dist/react/stream.d.ts.map +1 -1
  18. package/dist/react/stream.js.map +1 -1
  19. package/dist/react/stream.lgp.cjs +45 -13
  20. package/dist/react/stream.lgp.cjs.map +1 -1
  21. package/dist/react/stream.lgp.js +45 -13
  22. package/dist/react/stream.lgp.js.map +1 -1
  23. package/dist/react/types.d.cts +5 -88
  24. package/dist/react/types.d.cts.map +1 -1
  25. package/dist/react/types.d.ts +5 -88
  26. package/dist/react/types.d.ts.map +1 -1
  27. package/dist/react-ui/client.cjs.map +1 -1
  28. package/dist/react-ui/client.d.cts +5 -3
  29. package/dist/react-ui/client.d.cts.map +1 -1
  30. package/dist/react-ui/client.d.ts +5 -3
  31. package/dist/react-ui/client.d.ts.map +1 -1
  32. package/dist/react-ui/client.js.map +1 -1
  33. package/dist/schema.d.cts.map +1 -1
  34. package/dist/schema.d.ts.map +1 -1
  35. package/dist/types.stream.d.ts.map +1 -1
  36. package/dist/ui/manager.cjs +140 -14
  37. package/dist/ui/manager.cjs.map +1 -1
  38. package/dist/ui/manager.js +140 -14
  39. package/dist/ui/manager.js.map +1 -1
  40. package/dist/ui/stream/agent.d.cts +143 -0
  41. package/dist/ui/stream/agent.d.cts.map +1 -0
  42. package/dist/ui/stream/agent.d.ts +143 -0
  43. package/dist/ui/stream/agent.d.ts.map +1 -0
  44. package/dist/ui/stream/base.d.cts +144 -0
  45. package/dist/ui/stream/base.d.cts.map +1 -0
  46. package/dist/ui/stream/base.d.ts +144 -0
  47. package/dist/ui/stream/base.d.ts.map +1 -0
  48. package/dist/ui/stream/deep-agent.d.cts +277 -0
  49. package/dist/ui/stream/deep-agent.d.cts.map +1 -0
  50. package/dist/ui/stream/deep-agent.d.ts +277 -0
  51. package/dist/ui/stream/deep-agent.d.ts.map +1 -0
  52. package/dist/ui/stream/index.d.cts +172 -0
  53. package/dist/ui/stream/index.d.cts.map +1 -0
  54. package/dist/ui/stream/index.d.ts +172 -0
  55. package/dist/ui/stream/index.d.ts.map +1 -0
  56. package/dist/ui/subagents.cjs +593 -0
  57. package/dist/ui/subagents.cjs.map +1 -0
  58. package/dist/ui/subagents.d.cts +291 -0
  59. package/dist/ui/subagents.d.cts.map +1 -0
  60. package/dist/ui/subagents.d.ts +291 -0
  61. package/dist/ui/subagents.d.ts.map +1 -0
  62. package/dist/ui/subagents.js +589 -0
  63. package/dist/ui/subagents.js.map +1 -0
  64. package/dist/ui/types.d.cts +384 -5
  65. package/dist/ui/types.d.cts.map +1 -1
  66. package/dist/ui/types.d.ts +384 -5
  67. package/dist/ui/types.d.ts.map +1 -1
  68. package/package.json +6 -3
@@ -1,5 +1,6 @@
1
1
  const require_errors = require('./errors.cjs');
2
2
  const require_messages = require('./messages.cjs');
3
+ const require_subagents = require('./subagents.cjs');
3
4
 
4
5
  //#region src/ui/manager.ts
5
6
  /**
@@ -10,8 +11,10 @@ const REMOVE_ALL_MESSAGES = "__remove_all__";
10
11
  var StreamManager = class {
11
12
  abortRef = new AbortController();
12
13
  messages;
14
+ subagentManager;
13
15
  listeners = /* @__PURE__ */ new Set();
14
16
  throttle;
17
+ filterSubagentMessages;
15
18
  queue = Promise.resolve();
16
19
  queueSize = 0;
17
20
  state;
@@ -20,9 +23,78 @@ var StreamManager = class {
20
23
  this.state = {
21
24
  isLoading: false,
22
25
  values: null,
23
- error: void 0
26
+ error: void 0,
27
+ version: 0
24
28
  };
25
29
  this.throttle = options.throttle;
30
+ this.filterSubagentMessages = options.filterSubagentMessages ?? false;
31
+ this.subagentManager = new require_subagents.SubagentManager({
32
+ subagentToolNames: options.subagentToolNames,
33
+ onSubagentChange: () => this.bumpVersion()
34
+ });
35
+ }
36
+ /**
37
+ * Increment version counter to trigger React re-renders.
38
+ * Called when subagent state changes.
39
+ */
40
+ bumpVersion = () => {
41
+ this.state = {
42
+ ...this.state,
43
+ version: this.state.version + 1
44
+ };
45
+ this.notifyListeners();
46
+ };
47
+ /**
48
+ * Get all subagents as a Map.
49
+ */
50
+ getSubagents() {
51
+ return this.subagentManager.getSubagents();
52
+ }
53
+ /**
54
+ * Get all currently running subagents.
55
+ */
56
+ getActiveSubagents() {
57
+ return this.subagentManager.getActiveSubagents();
58
+ }
59
+ /**
60
+ * Get a specific subagent by tool call ID.
61
+ */
62
+ getSubagent(toolCallId) {
63
+ return this.subagentManager.getSubagent(toolCallId);
64
+ }
65
+ /**
66
+ * Get all subagents of a specific type.
67
+ */
68
+ getSubagentsByType(type) {
69
+ return this.subagentManager.getSubagentsByType(type);
70
+ }
71
+ /**
72
+ * Get all subagents triggered by a specific AI message.
73
+ */
74
+ getSubagentsByMessage(messageId) {
75
+ return this.subagentManager.getSubagentsByMessage(messageId);
76
+ }
77
+ /**
78
+ * Reconstruct subagent state from historical messages.
79
+ *
80
+ * This method should be called when loading thread history to restore
81
+ * subagent visualization after:
82
+ * - Page refresh (when stream has already completed)
83
+ * - Loading thread history
84
+ * - Navigating between threads
85
+ *
86
+ * @param messages - Array of messages from thread history
87
+ * @param options - Optional configuration
88
+ * @param options.skipIfPopulated - If true, skip reconstruction if subagents already exist
89
+ */
90
+ reconstructSubagents(messages, options) {
91
+ this.subagentManager.reconstructFromMessages(messages, options);
92
+ }
93
+ /**
94
+ * Check if any subagents are currently tracked.
95
+ */
96
+ hasSubagents() {
97
+ return this.subagentManager.hasSubagents();
26
98
  }
27
99
  setState = (newState) => {
28
100
  this.state = {
@@ -79,12 +151,12 @@ var StreamManager = class {
79
151
  const stateValues = (this.state.values ?? [null, "stream"])[0];
80
152
  const prev = {
81
153
  ...historyValues,
82
- ...stateValues ?? {}
154
+ ...stateValues
83
155
  };
84
156
  const next = typeof update === "function" ? update(prev) : update;
85
157
  this.setStreamValues({
86
158
  ...prev,
87
- ...next ?? {}
159
+ ...next
88
160
  }, kind);
89
161
  };
90
162
  };
@@ -110,10 +182,32 @@ var StreamManager = class {
110
182
  const mutate = this.getMutateFn("stream", options.initialValues);
111
183
  if (event === "metadata") options.callbacks.onMetadataEvent?.(data);
112
184
  if (event === "events") options.callbacks.onLangChainEvent?.(data);
113
- if (this.matchEventType("updates", event, data)) options.callbacks.onUpdateEvent?.(data, {
114
- namespace,
115
- mutate
116
- });
185
+ if (this.matchEventType("updates", event, data)) {
186
+ options.callbacks.onUpdateEvent?.(data, {
187
+ namespace,
188
+ mutate
189
+ });
190
+ if (namespace && require_subagents.isSubagentNamespace(namespace)) {
191
+ const namespaceId = require_subagents.extractToolCallIdFromNamespace(namespace);
192
+ if (namespaceId && this.filterSubagentMessages) this.subagentManager.markRunningFromNamespace(namespaceId, namespace);
193
+ }
194
+ if (!namespace || !require_subagents.isSubagentNamespace(namespace)) {
195
+ const updateData = data;
196
+ for (const nodeData of Object.values(updateData)) if (nodeData && typeof nodeData === "object" && "messages" in nodeData) {
197
+ const { messages } = nodeData;
198
+ if (Array.isArray(messages)) for (const msg of messages) {
199
+ if (!msg || typeof msg !== "object") continue;
200
+ const msgObj = msg;
201
+ if (msgObj.type === "ai" && "tool_calls" in msgObj && Array.isArray(msgObj.tool_calls)) this.subagentManager.registerFromToolCalls(msgObj.tool_calls, msgObj.id);
202
+ if (msgObj.type === "tool" && "tool_call_id" in msgObj && typeof msgObj.tool_call_id === "string") {
203
+ const content = typeof msgObj.content === "string" ? msgObj.content : JSON.stringify(msgObj.content);
204
+ const status = "status" in msgObj && msgObj.status === "error" ? "error" : "success";
205
+ this.subagentManager.processToolMessage(msgObj.tool_call_id, content, status);
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
117
211
  if (this.matchEventType("custom", event, data)) options.callbacks.onCustomEvent?.(data, {
118
212
  namespace,
119
213
  mutate
@@ -121,13 +215,34 @@ var StreamManager = class {
121
215
  if (this.matchEventType("checkpoints", event, data)) options.callbacks.onCheckpointEvent?.(data, { namespace });
122
216
  if (this.matchEventType("tasks", event, data)) options.callbacks.onTaskEvent?.(data, { namespace });
123
217
  if (this.matchEventType("debug", event, data)) options.callbacks.onDebugEvent?.(data, { namespace });
124
- if (event === "values") if (data != null && typeof data === "object" && "__interrupt__" in data) this.setStreamValues((prev) => ({
125
- ...prev ?? {},
126
- ...data
127
- }));
128
- else this.setStreamValues(data);
218
+ if (event === "values" || event.startsWith("values|")) if (namespace && require_subagents.isSubagentNamespace(namespace)) {
219
+ const namespaceId = require_subagents.extractToolCallIdFromNamespace(namespace);
220
+ if (namespaceId && this.filterSubagentMessages) {
221
+ const valuesData = data;
222
+ const messages = valuesData.messages;
223
+ if (Array.isArray(messages) && messages.length > 0) {
224
+ const firstMsg = messages[0];
225
+ if (firstMsg?.type === "human" && typeof firstMsg?.content === "string") this.subagentManager.matchSubgraphToSubagent(namespaceId, firstMsg.content);
226
+ }
227
+ this.subagentManager.updateSubagentValues(namespaceId, valuesData);
228
+ }
229
+ } else if (data && typeof data === "object" && "__interrupt__" in data) {
230
+ const interruptData = data;
231
+ this.setStreamValues((prev) => ({
232
+ ...prev,
233
+ ...interruptData
234
+ }));
235
+ } else this.setStreamValues(data);
129
236
  if (this.matchEventType("messages", event, data)) {
130
237
  const [serialized, metadata] = data;
238
+ const rawCheckpointNs = metadata?.langgraph_checkpoint_ns || metadata?.checkpoint_ns;
239
+ const checkpointNs = typeof rawCheckpointNs === "string" ? rawCheckpointNs : void 0;
240
+ const isFromSubagent = require_subagents.isSubagentNamespace(checkpointNs);
241
+ const toolCallId = isFromSubagent ? require_subagents.extractToolCallIdFromNamespace(checkpointNs?.split("|")) : void 0;
242
+ if (this.filterSubagentMessages && isFromSubagent && toolCallId) {
243
+ this.subagentManager.addMessageToSubagent(toolCallId, serialized, metadata);
244
+ continue;
245
+ }
131
246
  const messageId = this.messages.add(serialized, metadata);
132
247
  if (!messageId) {
133
248
  console.warn("Failed to add message to manager, no message ID found");
@@ -136,14 +251,24 @@ var StreamManager = class {
136
251
  this.setStreamValues((streamValues) => {
137
252
  const values = {
138
253
  ...options.initialValues,
139
- ...streamValues ?? {}
254
+ ...streamValues
140
255
  };
141
256
  let messages = options.getMessages(values).slice();
142
257
  const { chunk, index } = this.messages.get(messageId, messages.length) ?? {};
143
258
  if (!chunk || index == null) return values;
144
259
  if (chunk.getType() === "remove") if (chunk.id === REMOVE_ALL_MESSAGES) messages = [];
145
260
  else messages.splice(index, 1);
146
- else messages[index] = require_messages.toMessageDict(chunk);
261
+ else {
262
+ const msgDict = require_messages.toMessageDict(chunk);
263
+ messages[index] = msgDict;
264
+ if (!isFromSubagent && msgDict.type === "ai" && "tool_calls" in msgDict && Array.isArray(msgDict.tool_calls)) this.subagentManager.registerFromToolCalls(msgDict.tool_calls, msgDict.id);
265
+ if (!isFromSubagent && msgDict.type === "tool" && "tool_call_id" in msgDict) {
266
+ const tcId = msgDict.tool_call_id;
267
+ const content = typeof msgDict.content === "string" ? msgDict.content : JSON.stringify(msgDict.content);
268
+ const status = "status" in msgDict && msgDict.status === "error" ? "error" : "success";
269
+ this.subagentManager.processToolMessage(tcId, content, status);
270
+ }
271
+ }
147
272
  return options.setMessages(values, messages);
148
273
  });
149
274
  }
@@ -181,6 +306,7 @@ var StreamManager = class {
181
306
  isLoading: false
182
307
  });
183
308
  this.messages.clear();
309
+ this.subagentManager.clear();
184
310
  };
185
311
  };
186
312
 
@@ -1 +1 @@
1
- {"version":3,"file":"manager.cjs","names":["StreamError","toMessageDict"],"sources":["../../src/ui/manager.ts"],"sourcesContent":["import type {\n CheckpointsStreamEvent,\n CustomStreamEvent,\n DebugStreamEvent,\n ErrorStreamEvent,\n EventsStreamEvent,\n FeedbackStreamEvent,\n MessagesTupleStreamEvent,\n MetadataStreamEvent,\n TasksStreamEvent,\n UpdatesStreamEvent,\n ValuesStreamEvent,\n} from \"../types.stream.js\";\nimport { MessageTupleManager, toMessageDict } from \"./messages.js\";\nimport { StreamError } from \"./errors.js\";\nimport type { Message } from \"../types.messages.js\";\nimport type { BagTemplate } from \"../types.template.js\";\n\n/**\n * Special ID used by LangGraph's messagesStateReducer to signal\n * that all messages should be removed from the state.\n */\nexport const REMOVE_ALL_MESSAGES = \"__remove_all__\";\n\ntype GetUpdateType<\n Bag extends BagTemplate,\n StateType extends Record<string, unknown>\n> = Bag extends { UpdateType: unknown }\n ? Bag[\"UpdateType\"]\n : Partial<StateType>;\n\ntype GetCustomEventType<Bag extends BagTemplate> = Bag extends {\n CustomEventType: unknown;\n}\n ? Bag[\"CustomEventType\"]\n : unknown;\n\ntype EventStreamMap<StateType, UpdateType, CustomType> = {\n values: ValuesStreamEvent<StateType>;\n updates: UpdatesStreamEvent<UpdateType>;\n custom: CustomStreamEvent<CustomType>;\n debug: DebugStreamEvent;\n messages: MessagesTupleStreamEvent;\n events: EventsStreamEvent;\n metadata: MetadataStreamEvent;\n checkpoints: CheckpointsStreamEvent<StateType>;\n tasks: TasksStreamEvent<StateType, UpdateType>;\n error: ErrorStreamEvent;\n feedback: FeedbackStreamEvent;\n};\n\nexport type EventStreamEvent<StateType, UpdateType, CustomType> =\n EventStreamMap<StateType, UpdateType, CustomType>[keyof EventStreamMap<\n StateType,\n UpdateType,\n CustomType\n >];\n\ninterface StreamManagerEventCallbacks<\n StateType extends Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate\n> {\n onUpdateEvent?: (\n data: UpdatesStreamEvent<GetUpdateType<Bag, StateType>>[\"data\"],\n options: {\n namespace: string[] | undefined;\n mutate: (\n update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)\n ) => void;\n }\n ) => void;\n onCustomEvent?: (\n data: GetCustomEventType<Bag>,\n options: {\n namespace: string[] | undefined;\n mutate: (\n update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)\n ) => void;\n }\n ) => void;\n onMetadataEvent?: (data: MetadataStreamEvent[\"data\"]) => void;\n onLangChainEvent?: (data: EventsStreamEvent[\"data\"]) => void;\n onDebugEvent?: (\n data: DebugStreamEvent[\"data\"],\n options: { namespace: string[] | undefined }\n ) => void;\n onCheckpointEvent?: (\n data: CheckpointsStreamEvent<StateType>[\"data\"],\n options: { namespace: string[] | undefined }\n ) => void;\n onTaskEvent?: (\n data: TasksStreamEvent<StateType, GetUpdateType<Bag, StateType>>[\"data\"],\n options: { namespace: string[] | undefined }\n ) => void;\n}\n\nexport class StreamManager<\n StateType extends Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate\n> {\n private abortRef = new AbortController();\n\n private messages: MessageTupleManager;\n\n private listeners = new Set<() => void>();\n\n private throttle: number | boolean;\n\n private queue: Promise<unknown> = Promise.resolve();\n\n private queueSize: number = 0;\n\n private state: {\n isLoading: boolean;\n values: [values: StateType, kind: \"stream\" | \"stop\"] | null;\n error: unknown;\n };\n\n constructor(\n messages: MessageTupleManager,\n options: { throttle: number | boolean }\n ) {\n this.messages = messages;\n this.state = { isLoading: false, values: null, error: undefined };\n this.throttle = options.throttle;\n }\n\n private setState = (newState: Partial<typeof this.state>) => {\n this.state = { ...this.state, ...newState };\n this.notifyListeners();\n };\n\n private notifyListeners = () => {\n this.listeners.forEach((listener) => listener());\n };\n\n subscribe = (listener: () => void): (() => void) => {\n if (this.throttle === false) {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n const timeoutMs = this.throttle === true ? 0 : this.throttle;\n let timeoutId: NodeJS.Timeout | number | undefined;\n\n const throttledListener = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n clearTimeout(timeoutId);\n listener();\n }, timeoutMs);\n };\n\n this.listeners.add(throttledListener);\n return () => {\n clearTimeout(timeoutId);\n this.listeners.delete(throttledListener);\n };\n };\n\n getSnapshot = () => this.state;\n\n get isLoading() {\n return this.state.isLoading;\n }\n\n get values() {\n return this.state.values?.[0] ?? null;\n }\n\n get error() {\n return this.state.error;\n }\n\n setStreamValues = (\n values:\n | (StateType | null)\n | ((prev: StateType | null, kind: \"stream\" | \"stop\") => StateType | null),\n kind: \"stream\" | \"stop\" = \"stream\"\n ) => {\n if (typeof values === \"function\") {\n const [prevValues, prevKind] = this.state.values ?? [null, \"stream\"];\n const nextValues = values(prevValues, prevKind);\n this.setState({ values: nextValues != null ? [nextValues, kind] : null });\n } else {\n const nextValues = values != null ? [values, kind] : null;\n this.setState({ values: nextValues as [StateType, \"stream\" | \"stop\"] });\n }\n };\n\n private getMutateFn = (kind: \"stream\" | \"stop\", historyValues: StateType) => {\n return (\n update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)\n ) => {\n const stateValues = (this.state.values ?? [null, \"stream\"])[0];\n const prev = {\n ...historyValues,\n ...(stateValues ?? {}),\n };\n const next = typeof update === \"function\" ? update(prev) : update;\n this.setStreamValues({ ...prev, ...(next ?? {}) }, kind);\n };\n };\n\n private matchEventType = <\n T extends keyof EventStreamMap<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >\n >(\n expected: T,\n actual: EventStreamEvent<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >[\"event\"],\n _data: EventStreamEvent<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >[\"data\"]\n ): _data is EventStreamMap<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >[T][\"data\"] => {\n return expected === actual || actual.startsWith(`${expected}|`);\n };\n\n protected enqueue = async (\n action: (\n signal: AbortSignal\n ) => Promise<\n AsyncGenerator<\n EventStreamEvent<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >\n >\n >,\n options: {\n getMessages: (values: StateType) => Message[];\n\n setMessages: (current: StateType, messages: Message[]) => StateType;\n\n initialValues: StateType;\n\n callbacks: StreamManagerEventCallbacks<StateType, Bag>;\n\n onSuccess: () =>\n | StateType\n | null\n | undefined\n | void\n | Promise<StateType | null | undefined | void>;\n\n onError: (error: unknown) => void | Promise<void>;\n\n onFinish?: () => void;\n }\n ) => {\n try {\n this.queueSize = Math.max(0, this.queueSize - 1);\n this.setState({ isLoading: true, error: undefined });\n this.abortRef = new AbortController();\n\n const run = await action(this.abortRef.signal);\n\n let streamError: StreamError | undefined;\n for await (const { event, data } of run) {\n if (event === \"error\") {\n streamError = new StreamError(data);\n break;\n }\n\n const namespace = event.includes(\"|\")\n ? event.split(\"|\").slice(1)\n : undefined;\n\n const mutate = this.getMutateFn(\"stream\", options.initialValues);\n\n if (event === \"metadata\") options.callbacks.onMetadataEvent?.(data);\n if (event === \"events\") options.callbacks.onLangChainEvent?.(data);\n\n if (this.matchEventType(\"updates\", event, data)) {\n options.callbacks.onUpdateEvent?.(data, { namespace, mutate });\n }\n\n if (this.matchEventType(\"custom\", event, data)) {\n options.callbacks.onCustomEvent?.(data, { namespace, mutate });\n }\n\n if (this.matchEventType(\"checkpoints\", event, data)) {\n options.callbacks.onCheckpointEvent?.(data, { namespace });\n }\n\n if (this.matchEventType(\"tasks\", event, data)) {\n options.callbacks.onTaskEvent?.(data, { namespace });\n }\n\n if (this.matchEventType(\"debug\", event, data)) {\n options.callbacks.onDebugEvent?.(data, { namespace });\n }\n\n if (event === \"values\") {\n if (\n data != null &&\n typeof data === \"object\" &&\n \"__interrupt__\" in data\n ) {\n this.setStreamValues((prev) => ({ ...(prev ?? {}), ...data }));\n } else {\n this.setStreamValues(data);\n }\n }\n\n if (this.matchEventType(\"messages\", event, data)) {\n const [serialized, metadata] = data;\n\n const messageId = this.messages.add(serialized, metadata);\n if (!messageId) {\n console.warn(\n \"Failed to add message to manager, no message ID found\"\n );\n continue;\n }\n\n this.setStreamValues((streamValues) => {\n const values = {\n ...options.initialValues,\n ...(streamValues ?? {}),\n };\n\n // Assumption: we're concatenating the message\n let messages = options.getMessages(values).slice();\n const { chunk, index } =\n this.messages.get(messageId, messages.length) ?? {};\n\n if (!chunk || index == null) return values;\n if (chunk.getType() === \"remove\") {\n // Check for special REMOVE_ALL_MESSAGES sentinel\n if (chunk.id === REMOVE_ALL_MESSAGES) {\n // Clear all messages when __remove_all__ is received\n messages = [];\n } else {\n messages.splice(index, 1);\n }\n } else {\n messages[index] = toMessageDict(chunk);\n }\n\n return options.setMessages(values, messages);\n });\n }\n }\n\n if (streamError != null) throw streamError;\n\n const values = await options.onSuccess?.();\n if (typeof values !== \"undefined\" && this.queueSize === 0) {\n this.setStreamValues(values);\n }\n } catch (error) {\n if (\n !(\n error instanceof Error && // eslint-disable-line no-instanceof/no-instanceof\n (error.name === \"AbortError\" || error.name === \"TimeoutError\")\n )\n ) {\n console.error(error);\n this.setState({ error });\n await options.onError?.(error);\n }\n } finally {\n this.setState({ isLoading: false });\n this.abortRef = new AbortController();\n options.onFinish?.();\n }\n };\n\n start = async (\n action: (\n signal: AbortSignal\n ) => Promise<\n AsyncGenerator<\n EventStreamEvent<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >\n >\n >,\n options: {\n getMessages: (values: StateType) => Message[];\n\n setMessages: (current: StateType, messages: Message[]) => StateType;\n\n initialValues: StateType;\n\n callbacks: StreamManagerEventCallbacks<StateType, Bag>;\n\n onSuccess: () =>\n | StateType\n | null\n | undefined\n | void\n | Promise<StateType | null | undefined | void>;\n\n onError: (error: unknown) => void | Promise<void>;\n\n onFinish?: () => void;\n }\n ): Promise<void> => {\n this.queueSize += 1;\n this.queue = this.queue.then(() => this.enqueue(action, options));\n };\n\n stop = async (\n historyValues: StateType,\n options: {\n onStop?: (options: {\n mutate: (\n update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)\n ) => void;\n }) => void;\n }\n ): Promise<void> => {\n this.abortRef.abort();\n this.abortRef = new AbortController();\n\n options.onStop?.({ mutate: this.getMutateFn(\"stop\", historyValues) });\n };\n\n clear = () => {\n // Cancel any running streams\n this.abortRef.abort();\n this.abortRef = new AbortController();\n\n // Set the stream state to null\n this.setState({ error: undefined, values: null, isLoading: false });\n\n // Clear any pending messages\n this.messages.clear();\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"manager.cjs","names":["SubagentManager","StreamError","isSubagentNamespace","extractToolCallIdFromNamespace","toMessageDict"],"sources":["../../src/ui/manager.ts"],"sourcesContent":["import type {\n CheckpointsStreamEvent,\n CustomStreamEvent,\n DebugStreamEvent,\n ErrorStreamEvent,\n EventsStreamEvent,\n FeedbackStreamEvent,\n MessagesTupleStreamEvent,\n MetadataStreamEvent,\n TasksStreamEvent,\n UpdatesStreamEvent,\n ValuesStreamEvent,\n} from \"../types.stream.js\";\nimport { MessageTupleManager, toMessageDict } from \"./messages.js\";\nimport { StreamError } from \"./errors.js\";\nimport type { Message } from \"../types.messages.js\";\nimport type { BagTemplate } from \"../types.template.js\";\nimport {\n SubagentManager,\n extractToolCallIdFromNamespace,\n isSubagentNamespace,\n} from \"./subagents.js\";\nimport type { SubagentStream } from \"./types.js\";\n\n/**\n * Special ID used by LangGraph's messagesStateReducer to signal\n * that all messages should be removed from the state.\n */\nexport const REMOVE_ALL_MESSAGES = \"__remove_all__\";\n\ntype GetUpdateType<\n Bag extends BagTemplate,\n StateType extends Record<string, unknown>\n> = Bag extends { UpdateType: unknown }\n ? Bag[\"UpdateType\"]\n : Partial<StateType>;\n\ntype GetCustomEventType<Bag extends BagTemplate> = Bag extends {\n CustomEventType: unknown;\n}\n ? Bag[\"CustomEventType\"]\n : unknown;\n\ntype EventStreamMap<StateType, UpdateType, CustomType> = {\n values: ValuesStreamEvent<StateType>;\n updates: UpdatesStreamEvent<UpdateType>;\n custom: CustomStreamEvent<CustomType>;\n debug: DebugStreamEvent;\n messages: MessagesTupleStreamEvent;\n events: EventsStreamEvent;\n metadata: MetadataStreamEvent;\n checkpoints: CheckpointsStreamEvent<StateType>;\n tasks: TasksStreamEvent<StateType, UpdateType>;\n error: ErrorStreamEvent;\n feedback: FeedbackStreamEvent;\n};\n\nexport type EventStreamEvent<StateType, UpdateType, CustomType> =\n EventStreamMap<StateType, UpdateType, CustomType>[keyof EventStreamMap<\n StateType,\n UpdateType,\n CustomType\n >];\n\ninterface StreamManagerEventCallbacks<\n StateType extends Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate\n> {\n onUpdateEvent?: (\n data: UpdatesStreamEvent<GetUpdateType<Bag, StateType>>[\"data\"],\n options: {\n namespace: string[] | undefined;\n mutate: (\n update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)\n ) => void;\n }\n ) => void;\n onCustomEvent?: (\n data: GetCustomEventType<Bag>,\n options: {\n namespace: string[] | undefined;\n mutate: (\n update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)\n ) => void;\n }\n ) => void;\n onMetadataEvent?: (data: MetadataStreamEvent[\"data\"]) => void;\n onLangChainEvent?: (data: EventsStreamEvent[\"data\"]) => void;\n onDebugEvent?: (\n data: DebugStreamEvent[\"data\"],\n options: { namespace: string[] | undefined }\n ) => void;\n onCheckpointEvent?: (\n data: CheckpointsStreamEvent<StateType>[\"data\"],\n options: { namespace: string[] | undefined }\n ) => void;\n onTaskEvent?: (\n data: TasksStreamEvent<StateType, GetUpdateType<Bag, StateType>>[\"data\"],\n options: { namespace: string[] | undefined }\n ) => void;\n}\n\n/**\n * Options for StreamManager constructor.\n */\nexport interface StreamManagerOptions {\n /**\n * Throttle the stream updates.\n * If a number is provided, updates are throttled to the given milliseconds.\n * If `true`, updates are batched in a single macrotask.\n * If `false`, updates are not throttled.\n */\n throttle: number | boolean;\n\n /**\n * Tool names that indicate subagent invocation.\n *\n * When an AI message contains tool calls with these names, they are\n * automatically tracked as subagent executions. This enables the\n * `subagents`, `activeSubagents`, `getSubagent()`, and `getSubagentsByType()`\n * properties on the stream.\n *\n * @default [\"task\"]\n *\n * @example\n * ```typescript\n * // Track both \"task\" and \"delegate\" as subagent tools\n * subagentToolNames: [\"task\", \"delegate\", \"spawn_agent\"]\n * ```\n */\n subagentToolNames?: string[];\n\n /**\n * Filter out messages from subagent streams in the main messages array.\n *\n * When enabled, messages from subagraph executions (those with a `tools:` namespace)\n * are excluded from `stream.messages`. Instead, these messages are tracked\n * per-subagent and accessible via `stream.subagents.get(id).messages`.\n *\n * This is useful for deep agent architectures where you want to display\n * the main conversation separately from subagent activity.\n *\n * @default false\n *\n * @example\n * ```typescript\n * const stream = useStream({\n * assistantId: \"my-agent\",\n * filterSubagentMessages: true,\n * });\n *\n * // Main thread messages only (no subagent messages)\n * stream.messages\n *\n * // Access subagent messages individually\n * stream.subagents.get(\"call_xyz\").messages\n * ```\n */\n filterSubagentMessages?: boolean;\n}\n\nexport class StreamManager<\n StateType extends Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate\n> {\n private abortRef = new AbortController();\n\n private messages: MessageTupleManager;\n\n private subagentManager: SubagentManager;\n\n private listeners = new Set<() => void>();\n\n private throttle: number | boolean;\n\n private filterSubagentMessages: boolean;\n\n private queue: Promise<unknown> = Promise.resolve();\n\n private queueSize: number = 0;\n\n private state: {\n isLoading: boolean;\n values: [values: StateType, kind: \"stream\" | \"stop\"] | null;\n error: unknown;\n /** Version counter to force React re-renders on subagent changes */\n version: number;\n };\n\n constructor(messages: MessageTupleManager, options: StreamManagerOptions) {\n this.messages = messages;\n this.state = {\n isLoading: false,\n values: null,\n error: undefined,\n version: 0,\n };\n this.throttle = options.throttle;\n this.filterSubagentMessages = options.filterSubagentMessages ?? false;\n this.subagentManager = new SubagentManager({\n subagentToolNames: options.subagentToolNames,\n onSubagentChange: () => this.bumpVersion(),\n });\n }\n\n /**\n * Increment version counter to trigger React re-renders.\n * Called when subagent state changes.\n */\n private bumpVersion = () => {\n this.state = { ...this.state, version: this.state.version + 1 };\n this.notifyListeners();\n };\n\n /**\n * Get all subagents as a Map.\n */\n getSubagents(): Map<string, SubagentStream> {\n return this.subagentManager.getSubagents();\n }\n\n /**\n * Get all currently running subagents.\n */\n getActiveSubagents(): SubagentStream[] {\n return this.subagentManager.getActiveSubagents();\n }\n\n /**\n * Get a specific subagent by tool call ID.\n */\n getSubagent(toolCallId: string): SubagentStream | undefined {\n return this.subagentManager.getSubagent(toolCallId);\n }\n\n /**\n * Get all subagents of a specific type.\n */\n getSubagentsByType(type: string): SubagentStream[] {\n return this.subagentManager.getSubagentsByType(type);\n }\n\n /**\n * Get all subagents triggered by a specific AI message.\n */\n getSubagentsByMessage(messageId: string): SubagentStream[] {\n return this.subagentManager.getSubagentsByMessage(messageId);\n }\n\n /**\n * Reconstruct subagent state from historical messages.\n *\n * This method should be called when loading thread history to restore\n * subagent visualization after:\n * - Page refresh (when stream has already completed)\n * - Loading thread history\n * - Navigating between threads\n *\n * @param messages - Array of messages from thread history\n * @param options - Optional configuration\n * @param options.skipIfPopulated - If true, skip reconstruction if subagents already exist\n */\n reconstructSubagents(\n messages: Message[],\n options?: { skipIfPopulated?: boolean }\n ): void {\n this.subagentManager.reconstructFromMessages(messages, options);\n }\n\n /**\n * Check if any subagents are currently tracked.\n */\n hasSubagents(): boolean {\n return this.subagentManager.hasSubagents();\n }\n\n private setState = (newState: Partial<typeof this.state>) => {\n this.state = { ...this.state, ...newState };\n this.notifyListeners();\n };\n\n private notifyListeners = () => {\n this.listeners.forEach((listener) => listener());\n };\n\n subscribe = (listener: () => void): (() => void) => {\n if (this.throttle === false) {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n const timeoutMs = this.throttle === true ? 0 : this.throttle;\n let timeoutId: NodeJS.Timeout | number | undefined;\n\n const throttledListener = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n clearTimeout(timeoutId);\n listener();\n }, timeoutMs);\n };\n\n this.listeners.add(throttledListener);\n return () => {\n clearTimeout(timeoutId);\n this.listeners.delete(throttledListener);\n };\n };\n\n getSnapshot = () => this.state;\n\n get isLoading() {\n return this.state.isLoading;\n }\n\n get values() {\n return this.state.values?.[0] ?? null;\n }\n\n get error() {\n return this.state.error;\n }\n\n setStreamValues = (\n values:\n | (StateType | null)\n | ((prev: StateType | null, kind: \"stream\" | \"stop\") => StateType | null),\n kind: \"stream\" | \"stop\" = \"stream\"\n ) => {\n if (typeof values === \"function\") {\n const [prevValues, prevKind] = this.state.values ?? [null, \"stream\"];\n const nextValues = values(prevValues, prevKind);\n this.setState({ values: nextValues != null ? [nextValues, kind] : null });\n } else {\n const nextValues = values != null ? [values, kind] : null;\n this.setState({ values: nextValues as [StateType, \"stream\" | \"stop\"] });\n }\n };\n\n private getMutateFn = (kind: \"stream\" | \"stop\", historyValues: StateType) => {\n return (\n update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)\n ) => {\n const stateValues = (this.state.values ?? [null, \"stream\"])[0];\n const prev = {\n ...historyValues,\n ...stateValues,\n };\n const next = typeof update === \"function\" ? update(prev) : update;\n this.setStreamValues({ ...prev, ...next }, kind);\n };\n };\n\n private matchEventType = <\n T extends keyof EventStreamMap<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >\n >(\n expected: T,\n actual: EventStreamEvent<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >[\"event\"],\n _data: EventStreamEvent<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >[\"data\"]\n ): _data is EventStreamMap<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >[T][\"data\"] => {\n return expected === actual || actual.startsWith(`${expected}|`);\n };\n\n protected enqueue = async (\n action: (\n signal: AbortSignal\n ) => Promise<\n AsyncGenerator<\n EventStreamEvent<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >\n >\n >,\n options: {\n getMessages: (values: StateType) => Message[];\n\n setMessages: (current: StateType, messages: Message[]) => StateType;\n\n initialValues: StateType;\n\n callbacks: StreamManagerEventCallbacks<StateType, Bag>;\n\n onSuccess: () =>\n | StateType\n | null\n | undefined\n | void\n | Promise<StateType | null | undefined | void>;\n\n onError: (error: unknown) => void | Promise<void>;\n\n onFinish?: () => void;\n }\n ) => {\n try {\n this.queueSize = Math.max(0, this.queueSize - 1);\n this.setState({ isLoading: true, error: undefined });\n this.abortRef = new AbortController();\n\n const run = await action(this.abortRef.signal);\n\n let streamError: StreamError | undefined;\n for await (const { event, data } of run) {\n if (event === \"error\") {\n streamError = new StreamError(data);\n break;\n }\n\n const namespace = event.includes(\"|\")\n ? event.split(\"|\").slice(1)\n : undefined;\n\n const mutate = this.getMutateFn(\"stream\", options.initialValues);\n\n if (event === \"metadata\") options.callbacks.onMetadataEvent?.(data);\n if (event === \"events\") options.callbacks.onLangChainEvent?.(data);\n\n if (this.matchEventType(\"updates\", event, data)) {\n options.callbacks.onUpdateEvent?.(data, { namespace, mutate });\n\n // Track subagent streaming updates from subgraph namespaces\n // Mark the subagent as running when we receive updates\n // The actual message content is handled via addMessageToSubagent\n if (namespace && isSubagentNamespace(namespace)) {\n const namespaceId = extractToolCallIdFromNamespace(namespace);\n if (namespaceId && this.filterSubagentMessages) {\n this.subagentManager.markRunningFromNamespace(\n namespaceId,\n namespace\n );\n }\n }\n\n // Also register subagents from main agent updates (tool_calls in messages)\n // AND process tool results to complete subagents\n // This is needed because tool_calls often appear complete in updates\n // before they appear in the messages stream\n if (!namespace || !isSubagentNamespace(namespace)) {\n const updateData = data as Record<string, unknown>;\n for (const nodeData of Object.values(updateData)) {\n if (\n nodeData &&\n typeof nodeData === \"object\" &&\n \"messages\" in nodeData\n ) {\n const { messages } = nodeData as { messages: unknown[] };\n if (Array.isArray(messages)) {\n for (const msg of messages) {\n if (!msg || typeof msg !== \"object\") continue;\n const msgObj = msg as Record<string, unknown>;\n\n // Register subagents from AI messages with tool_calls\n if (\n msgObj.type === \"ai\" &&\n \"tool_calls\" in msgObj &&\n Array.isArray(msgObj.tool_calls)\n ) {\n this.subagentManager.registerFromToolCalls(\n msgObj.tool_calls as Array<{\n id?: string;\n name: string;\n args: Record<string, unknown> | string;\n }>,\n msgObj.id as string | undefined\n );\n }\n\n // Complete subagents from tool messages (task results)\n if (\n msgObj.type === \"tool\" &&\n \"tool_call_id\" in msgObj &&\n typeof msgObj.tool_call_id === \"string\"\n ) {\n const content =\n typeof msgObj.content === \"string\"\n ? msgObj.content\n : JSON.stringify(msgObj.content);\n const status =\n \"status\" in msgObj && msgObj.status === \"error\"\n ? \"error\"\n : \"success\";\n this.subagentManager.processToolMessage(\n msgObj.tool_call_id,\n content,\n status\n );\n }\n }\n }\n }\n }\n }\n }\n\n if (this.matchEventType(\"custom\", event, data)) {\n options.callbacks.onCustomEvent?.(data, { namespace, mutate });\n }\n\n if (this.matchEventType(\"checkpoints\", event, data)) {\n options.callbacks.onCheckpointEvent?.(data, { namespace });\n }\n\n if (this.matchEventType(\"tasks\", event, data)) {\n options.callbacks.onTaskEvent?.(data, { namespace });\n }\n\n if (this.matchEventType(\"debug\", event, data)) {\n options.callbacks.onDebugEvent?.(data, { namespace });\n }\n\n // Handle values events - use startsWith to match both \"values\" and \"values|tools:xxx\"\n if (event === \"values\" || event.startsWith(\"values|\")) {\n // Check if this is a subgraph values event (for namespace mapping and values)\n if (namespace && isSubagentNamespace(namespace)) {\n const namespaceId = extractToolCallIdFromNamespace(namespace);\n if (namespaceId && this.filterSubagentMessages) {\n const valuesData = data as Record<string, unknown>;\n\n // Try to establish namespace mapping from the initial human message\n const messages = valuesData.messages as unknown[];\n if (Array.isArray(messages) && messages.length > 0) {\n const firstMsg = messages[0] as Record<string, unknown>;\n if (\n firstMsg?.type === \"human\" &&\n typeof firstMsg?.content === \"string\"\n ) {\n this.subagentManager.matchSubgraphToSubagent(\n namespaceId,\n firstMsg.content\n );\n }\n }\n\n // Update the subagent's values with the full state\n this.subagentManager.updateSubagentValues(\n namespaceId,\n valuesData\n );\n }\n } else if (\n data &&\n typeof data === \"object\" &&\n \"__interrupt__\" in data\n ) {\n const interruptData = data as Partial<StateType>;\n this.setStreamValues(\n (prev) => ({ ...prev, ...interruptData } as StateType)\n );\n } else {\n this.setStreamValues(data as StateType);\n }\n }\n\n if (this.matchEventType(\"messages\", event, data)) {\n const [serialized, metadata] = data;\n\n // Check if this message is from a subagent namespace\n const rawCheckpointNs =\n (metadata?.langgraph_checkpoint_ns as string | undefined) ||\n (metadata?.checkpoint_ns as string | undefined);\n const checkpointNs: string | undefined =\n typeof rawCheckpointNs === \"string\" ? rawCheckpointNs : undefined;\n const isFromSubagent = isSubagentNamespace(checkpointNs);\n const toolCallId = isFromSubagent\n ? extractToolCallIdFromNamespace(checkpointNs?.split(\"|\"))\n : undefined;\n\n // If filtering is enabled and this is a subagent message,\n // add it to the subagent's messages instead of the main stream\n if (this.filterSubagentMessages && isFromSubagent && toolCallId) {\n // Add to subagent's message list\n this.subagentManager.addMessageToSubagent(\n toolCallId,\n serialized,\n metadata\n );\n continue;\n }\n\n const messageId = this.messages.add(serialized, metadata);\n if (!messageId) {\n console.warn(\n \"Failed to add message to manager, no message ID found\"\n );\n continue;\n }\n\n this.setStreamValues((streamValues) => {\n const values = {\n ...options.initialValues,\n ...streamValues,\n };\n\n // Assumption: we're concatenating the message\n let messages = options.getMessages(values).slice();\n const { chunk, index } =\n this.messages.get(messageId, messages.length) ?? {};\n\n if (!chunk || index == null) return values;\n if (chunk.getType() === \"remove\") {\n // Check for special REMOVE_ALL_MESSAGES sentinel\n if (chunk.id === REMOVE_ALL_MESSAGES) {\n // Clear all messages when __remove_all__ is received\n messages = [];\n } else {\n messages.splice(index, 1);\n }\n } else {\n const msgDict = toMessageDict(chunk);\n messages[index] = msgDict;\n\n // Track subagents from AI messages with tool calls (main agent only)\n if (\n !isFromSubagent &&\n msgDict.type === \"ai\" &&\n \"tool_calls\" in msgDict &&\n Array.isArray(msgDict.tool_calls)\n ) {\n this.subagentManager.registerFromToolCalls(\n msgDict.tool_calls,\n msgDict.id as string | undefined\n );\n }\n\n // Complete subagents when tool messages arrive (main agent only)\n if (\n !isFromSubagent &&\n msgDict.type === \"tool\" &&\n \"tool_call_id\" in msgDict\n ) {\n const tcId = msgDict.tool_call_id as string;\n const content =\n typeof msgDict.content === \"string\"\n ? msgDict.content\n : JSON.stringify(msgDict.content);\n const status =\n \"status\" in msgDict && msgDict.status === \"error\"\n ? \"error\"\n : \"success\";\n this.subagentManager.processToolMessage(tcId, content, status);\n }\n }\n\n return options.setMessages(values, messages);\n });\n }\n }\n\n if (streamError != null) throw streamError;\n\n const values = await options.onSuccess?.();\n if (typeof values !== \"undefined\" && this.queueSize === 0) {\n this.setStreamValues(values);\n }\n } catch (error) {\n if (\n !(\n error instanceof Error && // eslint-disable-line no-instanceof/no-instanceof\n (error.name === \"AbortError\" || error.name === \"TimeoutError\")\n )\n ) {\n console.error(error);\n this.setState({ error });\n await options.onError?.(error);\n }\n } finally {\n this.setState({ isLoading: false });\n this.abortRef = new AbortController();\n options.onFinish?.();\n }\n };\n\n start = async (\n action: (\n signal: AbortSignal\n ) => Promise<\n AsyncGenerator<\n EventStreamEvent<\n StateType,\n GetUpdateType<Bag, StateType>,\n GetCustomEventType<Bag>\n >\n >\n >,\n options: {\n getMessages: (values: StateType) => Message[];\n\n setMessages: (current: StateType, messages: Message[]) => StateType;\n\n initialValues: StateType;\n\n callbacks: StreamManagerEventCallbacks<StateType, Bag>;\n\n onSuccess: () =>\n | StateType\n | null\n | undefined\n | void\n | Promise<StateType | null | undefined | void>;\n\n onError: (error: unknown) => void | Promise<void>;\n\n onFinish?: () => void;\n }\n ): Promise<void> => {\n this.queueSize += 1;\n this.queue = this.queue.then(() => this.enqueue(action, options));\n };\n\n stop = async (\n historyValues: StateType,\n options: {\n onStop?: (options: {\n mutate: (\n update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)\n ) => void;\n }) => void;\n }\n ): Promise<void> => {\n this.abortRef.abort();\n this.abortRef = new AbortController();\n\n options.onStop?.({ mutate: this.getMutateFn(\"stop\", historyValues) });\n };\n\n clear = () => {\n // Cancel any running streams\n this.abortRef.abort();\n this.abortRef = new AbortController();\n\n // Set the stream state to null\n this.setState({ error: undefined, values: null, isLoading: false });\n\n // Clear any pending messages\n this.messages.clear();\n\n // Clear subagent state\n this.subagentManager.clear();\n };\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { StreamError } from "./errors.js";
2
2
  import { toMessageDict } from "./messages.js";
3
+ import { SubagentManager, extractToolCallIdFromNamespace, isSubagentNamespace } from "./subagents.js";
3
4
 
4
5
  //#region src/ui/manager.ts
5
6
  /**
@@ -10,8 +11,10 @@ const REMOVE_ALL_MESSAGES = "__remove_all__";
10
11
  var StreamManager = class {
11
12
  abortRef = new AbortController();
12
13
  messages;
14
+ subagentManager;
13
15
  listeners = /* @__PURE__ */ new Set();
14
16
  throttle;
17
+ filterSubagentMessages;
15
18
  queue = Promise.resolve();
16
19
  queueSize = 0;
17
20
  state;
@@ -20,9 +23,78 @@ var StreamManager = class {
20
23
  this.state = {
21
24
  isLoading: false,
22
25
  values: null,
23
- error: void 0
26
+ error: void 0,
27
+ version: 0
24
28
  };
25
29
  this.throttle = options.throttle;
30
+ this.filterSubagentMessages = options.filterSubagentMessages ?? false;
31
+ this.subagentManager = new SubagentManager({
32
+ subagentToolNames: options.subagentToolNames,
33
+ onSubagentChange: () => this.bumpVersion()
34
+ });
35
+ }
36
+ /**
37
+ * Increment version counter to trigger React re-renders.
38
+ * Called when subagent state changes.
39
+ */
40
+ bumpVersion = () => {
41
+ this.state = {
42
+ ...this.state,
43
+ version: this.state.version + 1
44
+ };
45
+ this.notifyListeners();
46
+ };
47
+ /**
48
+ * Get all subagents as a Map.
49
+ */
50
+ getSubagents() {
51
+ return this.subagentManager.getSubagents();
52
+ }
53
+ /**
54
+ * Get all currently running subagents.
55
+ */
56
+ getActiveSubagents() {
57
+ return this.subagentManager.getActiveSubagents();
58
+ }
59
+ /**
60
+ * Get a specific subagent by tool call ID.
61
+ */
62
+ getSubagent(toolCallId) {
63
+ return this.subagentManager.getSubagent(toolCallId);
64
+ }
65
+ /**
66
+ * Get all subagents of a specific type.
67
+ */
68
+ getSubagentsByType(type) {
69
+ return this.subagentManager.getSubagentsByType(type);
70
+ }
71
+ /**
72
+ * Get all subagents triggered by a specific AI message.
73
+ */
74
+ getSubagentsByMessage(messageId) {
75
+ return this.subagentManager.getSubagentsByMessage(messageId);
76
+ }
77
+ /**
78
+ * Reconstruct subagent state from historical messages.
79
+ *
80
+ * This method should be called when loading thread history to restore
81
+ * subagent visualization after:
82
+ * - Page refresh (when stream has already completed)
83
+ * - Loading thread history
84
+ * - Navigating between threads
85
+ *
86
+ * @param messages - Array of messages from thread history
87
+ * @param options - Optional configuration
88
+ * @param options.skipIfPopulated - If true, skip reconstruction if subagents already exist
89
+ */
90
+ reconstructSubagents(messages, options) {
91
+ this.subagentManager.reconstructFromMessages(messages, options);
92
+ }
93
+ /**
94
+ * Check if any subagents are currently tracked.
95
+ */
96
+ hasSubagents() {
97
+ return this.subagentManager.hasSubagents();
26
98
  }
27
99
  setState = (newState) => {
28
100
  this.state = {
@@ -79,12 +151,12 @@ var StreamManager = class {
79
151
  const stateValues = (this.state.values ?? [null, "stream"])[0];
80
152
  const prev = {
81
153
  ...historyValues,
82
- ...stateValues ?? {}
154
+ ...stateValues
83
155
  };
84
156
  const next = typeof update === "function" ? update(prev) : update;
85
157
  this.setStreamValues({
86
158
  ...prev,
87
- ...next ?? {}
159
+ ...next
88
160
  }, kind);
89
161
  };
90
162
  };
@@ -110,10 +182,32 @@ var StreamManager = class {
110
182
  const mutate = this.getMutateFn("stream", options.initialValues);
111
183
  if (event === "metadata") options.callbacks.onMetadataEvent?.(data);
112
184
  if (event === "events") options.callbacks.onLangChainEvent?.(data);
113
- if (this.matchEventType("updates", event, data)) options.callbacks.onUpdateEvent?.(data, {
114
- namespace,
115
- mutate
116
- });
185
+ if (this.matchEventType("updates", event, data)) {
186
+ options.callbacks.onUpdateEvent?.(data, {
187
+ namespace,
188
+ mutate
189
+ });
190
+ if (namespace && isSubagentNamespace(namespace)) {
191
+ const namespaceId = extractToolCallIdFromNamespace(namespace);
192
+ if (namespaceId && this.filterSubagentMessages) this.subagentManager.markRunningFromNamespace(namespaceId, namespace);
193
+ }
194
+ if (!namespace || !isSubagentNamespace(namespace)) {
195
+ const updateData = data;
196
+ for (const nodeData of Object.values(updateData)) if (nodeData && typeof nodeData === "object" && "messages" in nodeData) {
197
+ const { messages } = nodeData;
198
+ if (Array.isArray(messages)) for (const msg of messages) {
199
+ if (!msg || typeof msg !== "object") continue;
200
+ const msgObj = msg;
201
+ if (msgObj.type === "ai" && "tool_calls" in msgObj && Array.isArray(msgObj.tool_calls)) this.subagentManager.registerFromToolCalls(msgObj.tool_calls, msgObj.id);
202
+ if (msgObj.type === "tool" && "tool_call_id" in msgObj && typeof msgObj.tool_call_id === "string") {
203
+ const content = typeof msgObj.content === "string" ? msgObj.content : JSON.stringify(msgObj.content);
204
+ const status = "status" in msgObj && msgObj.status === "error" ? "error" : "success";
205
+ this.subagentManager.processToolMessage(msgObj.tool_call_id, content, status);
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
117
211
  if (this.matchEventType("custom", event, data)) options.callbacks.onCustomEvent?.(data, {
118
212
  namespace,
119
213
  mutate
@@ -121,13 +215,34 @@ var StreamManager = class {
121
215
  if (this.matchEventType("checkpoints", event, data)) options.callbacks.onCheckpointEvent?.(data, { namespace });
122
216
  if (this.matchEventType("tasks", event, data)) options.callbacks.onTaskEvent?.(data, { namespace });
123
217
  if (this.matchEventType("debug", event, data)) options.callbacks.onDebugEvent?.(data, { namespace });
124
- if (event === "values") if (data != null && typeof data === "object" && "__interrupt__" in data) this.setStreamValues((prev) => ({
125
- ...prev ?? {},
126
- ...data
127
- }));
128
- else this.setStreamValues(data);
218
+ if (event === "values" || event.startsWith("values|")) if (namespace && isSubagentNamespace(namespace)) {
219
+ const namespaceId = extractToolCallIdFromNamespace(namespace);
220
+ if (namespaceId && this.filterSubagentMessages) {
221
+ const valuesData = data;
222
+ const messages = valuesData.messages;
223
+ if (Array.isArray(messages) && messages.length > 0) {
224
+ const firstMsg = messages[0];
225
+ if (firstMsg?.type === "human" && typeof firstMsg?.content === "string") this.subagentManager.matchSubgraphToSubagent(namespaceId, firstMsg.content);
226
+ }
227
+ this.subagentManager.updateSubagentValues(namespaceId, valuesData);
228
+ }
229
+ } else if (data && typeof data === "object" && "__interrupt__" in data) {
230
+ const interruptData = data;
231
+ this.setStreamValues((prev) => ({
232
+ ...prev,
233
+ ...interruptData
234
+ }));
235
+ } else this.setStreamValues(data);
129
236
  if (this.matchEventType("messages", event, data)) {
130
237
  const [serialized, metadata] = data;
238
+ const rawCheckpointNs = metadata?.langgraph_checkpoint_ns || metadata?.checkpoint_ns;
239
+ const checkpointNs = typeof rawCheckpointNs === "string" ? rawCheckpointNs : void 0;
240
+ const isFromSubagent = isSubagentNamespace(checkpointNs);
241
+ const toolCallId = isFromSubagent ? extractToolCallIdFromNamespace(checkpointNs?.split("|")) : void 0;
242
+ if (this.filterSubagentMessages && isFromSubagent && toolCallId) {
243
+ this.subagentManager.addMessageToSubagent(toolCallId, serialized, metadata);
244
+ continue;
245
+ }
131
246
  const messageId = this.messages.add(serialized, metadata);
132
247
  if (!messageId) {
133
248
  console.warn("Failed to add message to manager, no message ID found");
@@ -136,14 +251,24 @@ var StreamManager = class {
136
251
  this.setStreamValues((streamValues) => {
137
252
  const values = {
138
253
  ...options.initialValues,
139
- ...streamValues ?? {}
254
+ ...streamValues
140
255
  };
141
256
  let messages = options.getMessages(values).slice();
142
257
  const { chunk, index } = this.messages.get(messageId, messages.length) ?? {};
143
258
  if (!chunk || index == null) return values;
144
259
  if (chunk.getType() === "remove") if (chunk.id === REMOVE_ALL_MESSAGES) messages = [];
145
260
  else messages.splice(index, 1);
146
- else messages[index] = toMessageDict(chunk);
261
+ else {
262
+ const msgDict = toMessageDict(chunk);
263
+ messages[index] = msgDict;
264
+ if (!isFromSubagent && msgDict.type === "ai" && "tool_calls" in msgDict && Array.isArray(msgDict.tool_calls)) this.subagentManager.registerFromToolCalls(msgDict.tool_calls, msgDict.id);
265
+ if (!isFromSubagent && msgDict.type === "tool" && "tool_call_id" in msgDict) {
266
+ const tcId = msgDict.tool_call_id;
267
+ const content = typeof msgDict.content === "string" ? msgDict.content : JSON.stringify(msgDict.content);
268
+ const status = "status" in msgDict && msgDict.status === "error" ? "error" : "success";
269
+ this.subagentManager.processToolMessage(tcId, content, status);
270
+ }
271
+ }
147
272
  return options.setMessages(values, messages);
148
273
  });
149
274
  }
@@ -181,6 +306,7 @@ var StreamManager = class {
181
306
  isLoading: false
182
307
  });
183
308
  this.messages.clear();
309
+ this.subagentManager.clear();
184
310
  };
185
311
  };
186
312