langchain_agentx_stream_ui 0.1.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.
@@ -0,0 +1,785 @@
1
+ import {
2
+ InteractionBusContext,
3
+ ToolCallShell,
4
+ formatToolTitle,
5
+ getNoopInteractionBus,
6
+ resolveToolBody,
7
+ useInteractionBus
8
+ } from "./chunk-WM6Y6APP.js";
9
+ import {
10
+ DEFAULT_SESSION_VIEW_OPTIONS,
11
+ MultiSessionStoreContext,
12
+ NodeRegistryContext,
13
+ SessionStoreContext,
14
+ SessionTimeline,
15
+ SessionViewOptionsContext,
16
+ ToolDisplayOptionsContext,
17
+ createEmptyTree,
18
+ reduceTree,
19
+ replayEvents,
20
+ resolveEffectiveToolBodyMode,
21
+ useInternalErrors,
22
+ useNodeTyped,
23
+ usePendingPermissionForTool,
24
+ useSessionStatus,
25
+ useSessionViewOptions,
26
+ useToolDisplayOptions
27
+ } from "./chunk-G2KYJCMM.js";
28
+ import {
29
+ createDefaultToolRegistry
30
+ } from "./chunk-MHK53ZHC.js";
31
+ import {
32
+ MarkdownRendererContext
33
+ } from "./chunk-4RIOBLGB.js";
34
+
35
+ // src/view/nodes/ReasoningNode.tsx
36
+ import { jsx, jsxs } from "react/jsx-runtime";
37
+ var REASONING_DOT = "\u25CF";
38
+ function ReasoningBubble({ nodeId }) {
39
+ const node = useNodeTyped(nodeId, "reasoning");
40
+ if (!node) return null;
41
+ const text = node.accumulated.trim();
42
+ if (!text) return null;
43
+ return /* @__PURE__ */ jsx("div", { className: "lax-reasoning-bubble", "data-status": node.status, "data-round-id": node.roundId, children: /* @__PURE__ */ jsxs("div", { className: "lax-reasoning-bubble__body", children: [
44
+ /* @__PURE__ */ jsx("span", { className: "lax-reasoning-bubble__dot", "aria-hidden": "true", children: REASONING_DOT }),
45
+ /* @__PURE__ */ jsx("span", { className: "lax-reasoning-bubble__content", children: node.accumulated })
46
+ ] }) });
47
+ }
48
+
49
+ // src/view/tools/presentation/StreamingMarkdown.tsx
50
+ import { useRef, useMemo, useState } from "react";
51
+ import { marked } from "marked";
52
+ import ReactMarkdown from "react-markdown";
53
+ import remarkGfm from "remark-gfm";
54
+ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
55
+ import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";
56
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
57
+ var TOKEN_CACHE_MAX = 500;
58
+ var tokenCache = /* @__PURE__ */ new Map();
59
+ var MD_SYNTAX_RE = /[#*`|\[>\\\-~]|\n\n|^\d+\. |\n\d+\. /;
60
+ function hasMarkdownSyntax(s) {
61
+ return MD_SYNTAX_RE.test(s.length > 500 ? s.slice(0, 500) : s);
62
+ }
63
+ function hashContent(content) {
64
+ let hash = 5381;
65
+ for (let i = 0; i < content.length; i++) {
66
+ hash = (hash << 5) + hash + content.charCodeAt(i) & 4294967295;
67
+ }
68
+ return hash.toString(36);
69
+ }
70
+ function cachedLexer(content) {
71
+ if (!hasMarkdownSyntax(content)) {
72
+ return [{
73
+ type: "paragraph",
74
+ raw: content,
75
+ text: content,
76
+ tokens: [{
77
+ type: "text",
78
+ raw: content,
79
+ text: content
80
+ }]
81
+ }];
82
+ }
83
+ const key = hashContent(content);
84
+ const hit = tokenCache.get(key);
85
+ if (hit) {
86
+ tokenCache.delete(key);
87
+ tokenCache.set(key, hit);
88
+ return hit;
89
+ }
90
+ const tokens = marked.lexer(content);
91
+ if (tokenCache.size >= TOKEN_CACHE_MAX) {
92
+ const first = tokenCache.keys().next().value;
93
+ if (first !== void 0) tokenCache.delete(first);
94
+ }
95
+ tokenCache.set(key, tokens);
96
+ return tokens;
97
+ }
98
+ var MARKDOWN_COMPONENTS = {
99
+ code({ node, className, children, ...props }) {
100
+ const match = /language-(\w+)/.exec(className || "");
101
+ const codeContent = String(children).replace(/\n$/, "");
102
+ const inline = !className;
103
+ if (!inline && match) {
104
+ return /* @__PURE__ */ jsx2(CodeBlock, { language: match[1], content: codeContent });
105
+ }
106
+ return /* @__PURE__ */ jsx2("code", { className: "lax-markdown__inline-code", ...props, children });
107
+ },
108
+ h1({ children }) {
109
+ return /* @__PURE__ */ jsx2("h1", { className: "lax-markdown__heading lax-markdown__h1", children });
110
+ },
111
+ h2({ children }) {
112
+ return /* @__PURE__ */ jsx2("h2", { className: "lax-markdown__heading lax-markdown__h2", children });
113
+ },
114
+ h3({ children }) {
115
+ return /* @__PURE__ */ jsx2("h3", { className: "lax-markdown__heading lax-markdown__h3", children });
116
+ },
117
+ h4({ children }) {
118
+ return /* @__PURE__ */ jsx2("h4", { className: "lax-markdown__heading lax-markdown__h4", children });
119
+ },
120
+ h5({ children }) {
121
+ return /* @__PURE__ */ jsx2("h5", { className: "lax-markdown__heading lax-markdown__h5", children });
122
+ },
123
+ h6({ children }) {
124
+ return /* @__PURE__ */ jsx2("h6", { className: "lax-markdown__heading lax-markdown__h6", children });
125
+ },
126
+ p({ children }) {
127
+ return /* @__PURE__ */ jsx2("p", { className: "lax-markdown__paragraph", children });
128
+ },
129
+ ul({ children }) {
130
+ return /* @__PURE__ */ jsx2("ul", { className: "lax-markdown__list lax-markdown__ul", children });
131
+ },
132
+ ol({ children }) {
133
+ return /* @__PURE__ */ jsx2("ol", { className: "lax-markdown__list lax-markdown__ol", children });
134
+ },
135
+ li({ children }) {
136
+ return /* @__PURE__ */ jsx2("li", { className: "lax-markdown__li", children });
137
+ },
138
+ a({ children, href }) {
139
+ return /* @__PURE__ */ jsx2("a", { href, className: "lax-markdown__link", target: "_blank", rel: "noopener noreferrer", children });
140
+ },
141
+ blockquote({ children }) {
142
+ return /* @__PURE__ */ jsx2("blockquote", { className: "lax-markdown__blockquote", children });
143
+ },
144
+ table({ children }) {
145
+ return /* @__PURE__ */ jsx2("div", { className: "lax-markdown__table-wrapper", children: /* @__PURE__ */ jsx2("table", { className: "lax-markdown__table", children }) });
146
+ },
147
+ thead({ children }) {
148
+ return /* @__PURE__ */ jsx2("thead", { className: "lax-markdown__thead", children });
149
+ },
150
+ tbody({ children }) {
151
+ return /* @__PURE__ */ jsx2("tbody", { className: "lax-markdown__tbody", children });
152
+ },
153
+ tr({ children }) {
154
+ return /* @__PURE__ */ jsx2("tr", { className: "lax-markdown__tr", children });
155
+ },
156
+ th({ children }) {
157
+ return /* @__PURE__ */ jsx2("th", { className: "lax-markdown__th", children });
158
+ },
159
+ td({ children }) {
160
+ return /* @__PURE__ */ jsx2("td", { className: "lax-markdown__td", children });
161
+ },
162
+ hr() {
163
+ return /* @__PURE__ */ jsx2("hr", { className: "lax-markdown__hr" });
164
+ },
165
+ strong({ children }) {
166
+ return /* @__PURE__ */ jsx2("strong", { className: "lax-markdown__strong", children });
167
+ },
168
+ em({ children }) {
169
+ return /* @__PURE__ */ jsx2("em", { className: "lax-markdown__em", children });
170
+ }
171
+ };
172
+ function CodeBlock({ language, content }) {
173
+ const [copied, setCopied] = useState(false);
174
+ const handleCopy = async () => {
175
+ try {
176
+ await navigator.clipboard.writeText(content);
177
+ setCopied(true);
178
+ setTimeout(() => setCopied(false), 2e3);
179
+ } catch (err) {
180
+ console.error("Failed to copy:", err);
181
+ }
182
+ };
183
+ return /* @__PURE__ */ jsxs2("div", { className: "lax-code-block", children: [
184
+ /* @__PURE__ */ jsxs2("div", { className: "lax-code-block__header", children: [
185
+ /* @__PURE__ */ jsx2("span", { className: "lax-code-block__lang", children: language || "code" }),
186
+ /* @__PURE__ */ jsx2(
187
+ "button",
188
+ {
189
+ type: "button",
190
+ className: `lax-code-block__copy ${copied ? "lax-code-block__copy--copied" : ""}`,
191
+ onClick: handleCopy,
192
+ title: copied ? "\u5DF2\u62F7\u8D1D\uFF01" : "\u62F7\u8D1D\u4EE3\u7801",
193
+ children: copied ? /* @__PURE__ */ jsxs2(Fragment, { children: [
194
+ /* @__PURE__ */ jsx2("svg", { className: "lax-code-block__icon", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3, children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) }),
195
+ /* @__PURE__ */ jsx2("span", { children: "\u5DF2\u62F7\u8D1D" })
196
+ ] }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
197
+ /* @__PURE__ */ jsx2("svg", { className: "lax-code-block__icon", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx2(
198
+ "path",
199
+ {
200
+ strokeLinecap: "round",
201
+ strokeLinejoin: "round",
202
+ d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
203
+ }
204
+ ) }),
205
+ /* @__PURE__ */ jsx2("span", { children: "\u62F7\u8D1D" })
206
+ ] })
207
+ }
208
+ )
209
+ ] }),
210
+ /* @__PURE__ */ jsx2(
211
+ SyntaxHighlighter,
212
+ {
213
+ language,
214
+ style: oneDark,
215
+ customStyle: {
216
+ margin: 0,
217
+ padding: 0,
218
+ background: "transparent"
219
+ },
220
+ showLineNumbers: false,
221
+ wrapLongLines: true,
222
+ children: content
223
+ }
224
+ )
225
+ ] });
226
+ }
227
+ function MarkdownRenderer({ content }) {
228
+ if (!content.trim()) {
229
+ return null;
230
+ }
231
+ const elements = useMemo(() => {
232
+ const hash = hashContent(content);
233
+ if (!hasMarkdownSyntax(content)) {
234
+ return /* @__PURE__ */ jsx2("p", { className: "lax-markdown__paragraph", children: content }, hash.toString(36));
235
+ }
236
+ return /* @__PURE__ */ jsx2(
237
+ ReactMarkdown,
238
+ {
239
+ remarkPlugins: [remarkGfm],
240
+ components: MARKDOWN_COMPONENTS,
241
+ children: content
242
+ },
243
+ hash.toString(36)
244
+ );
245
+ }, [content]);
246
+ return /* @__PURE__ */ jsx2(Fragment, { children: elements });
247
+ }
248
+ function StreamingMarkdown({ children }) {
249
+ "use no memo";
250
+ const stablePrefixRef = useRef("");
251
+ if (!children.startsWith(stablePrefixRef.current)) {
252
+ stablePrefixRef.current = "";
253
+ }
254
+ const boundary = stablePrefixRef.current.length;
255
+ const tokens = cachedLexer(children.substring(boundary));
256
+ let lastContentIdx = tokens.length - 1;
257
+ while (lastContentIdx >= 0 && tokens[lastContentIdx].type === "space") {
258
+ lastContentIdx--;
259
+ }
260
+ let advance = 0;
261
+ for (let i = 0; i < lastContentIdx; i++) {
262
+ advance += tokens[i].raw.length;
263
+ }
264
+ if (advance > 0) {
265
+ stablePrefixRef.current = children.substring(0, boundary + advance);
266
+ }
267
+ const stablePrefix = stablePrefixRef.current;
268
+ const unstableSuffix = children.substring(stablePrefix.length);
269
+ return /* @__PURE__ */ jsxs2("div", { className: "lax-markdown", children: [
270
+ stablePrefix && /* @__PURE__ */ jsx2(MarkdownRenderer, { content: stablePrefix }, `stable-${hashContent(stablePrefix)}`),
271
+ unstableSuffix && /* @__PURE__ */ jsx2(MarkdownRenderer, { content: unstableSuffix }, `unstable-${hashContent(unstableSuffix)}`)
272
+ ] });
273
+ }
274
+
275
+ // src/view/nodes/TextNode.tsx
276
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
277
+ function TextBubble({ nodeId }) {
278
+ const node = useNodeTyped(nodeId, "text");
279
+ if (!node) return null;
280
+ return /* @__PURE__ */ jsx3("div", { className: "lax-text-bubble", "data-status": node.status, children: /* @__PURE__ */ jsxs3("div", { className: "lax-text-bubble__body", children: [
281
+ /* @__PURE__ */ jsx3("span", { className: "lax-text-bubble__dot", "aria-hidden": "true", children: REASONING_DOT }),
282
+ /* @__PURE__ */ jsx3("div", { className: "lax-text-bubble__content lax-text-bubble__content--markdown", children: /* @__PURE__ */ jsx3(StreamingMarkdown, { children: node.accumulated }) })
283
+ ] }) });
284
+ }
285
+
286
+ // src/view/nodes/ToolCallNode.tsx
287
+ import { useEffect, useState as useState2 } from "react";
288
+ import { jsx as jsx4 } from "react/jsx-runtime";
289
+ function ToolCallCard({ nodeId, layout = "default" }) {
290
+ const node = useNodeTyped(nodeId, "tool_call");
291
+ const { registry, defaultBodyMode } = useToolDisplayOptions();
292
+ const viewOptions = useSessionViewOptions();
293
+ const pendingPermission = usePendingPermissionForTool(node?.toolName ?? "");
294
+ const effectiveDefault = resolveEffectiveToolBodyMode(viewOptions, defaultBodyMode);
295
+ const [bodyMode, setBodyMode] = useState2(effectiveDefault);
296
+ useEffect(() => {
297
+ setBodyMode(resolveEffectiveToolBodyMode(viewOptions, defaultBodyMode));
298
+ }, [viewOptions.displayMode, viewOptions.verbose, defaultBodyMode]);
299
+ if (!node) return null;
300
+ if (node.hidden) return null;
301
+ const details = node.details;
302
+ const inputStream = details?.inputStream ?? node.inputDelta;
303
+ const result = node.result;
304
+ const displayBody = result?.display ?? null;
305
+ const hasSubagentProgress = (node.subagentProgress?.length ?? 0) > 0;
306
+ const showBody = hasSubagentProgress || !(node.status === "running" && result == null);
307
+ const BodyComponent = resolveToolBody(node.toolName, displayBody, registry);
308
+ const title = formatToolTitle(
309
+ node.toolName,
310
+ details?.input ?? node.input,
311
+ displayBody,
312
+ result?.summary,
313
+ result?.meta
314
+ );
315
+ const bodyProps = {
316
+ nodeId,
317
+ toolName: node.toolName,
318
+ status: node.status,
319
+ input: details?.input ?? node.input,
320
+ inputStream,
321
+ result,
322
+ progress: node.progress ?? void 0,
323
+ subagentProgress: node.subagentProgress,
324
+ subagentSessions: node.subagentSessions,
325
+ bodyMode,
326
+ onBodyModeChange: setBodyMode
327
+ };
328
+ const errorMessage = result?.error?.message ?? null;
329
+ const permissionWait = viewOptions.permissionUiMode !== "standalone" && node.status === "running" ? pendingPermission : void 0;
330
+ return /* @__PURE__ */ jsx4(
331
+ ToolCallShell,
332
+ {
333
+ status: node.status,
334
+ title,
335
+ progress: node.progress ?? void 0,
336
+ errorMessage,
337
+ layout,
338
+ permissionWait,
339
+ permissionUiMode: viewOptions.permissionUiMode,
340
+ children: showBody ? /* @__PURE__ */ jsx4("div", { className: "lax-tool-shell__body", children: /* @__PURE__ */ jsx4(BodyComponent, { ...bodyProps }) }) : null
341
+ }
342
+ );
343
+ }
344
+
345
+ // src/view/nodes/StepNode.tsx
346
+ import { jsx as jsx5 } from "react/jsx-runtime";
347
+ function StepCard({ nodeId }) {
348
+ const node = useNodeTyped(nodeId, "step");
349
+ if (!node) return null;
350
+ return /* @__PURE__ */ jsx5("details", { className: "lax-step-card", "data-status": node.status, children: /* @__PURE__ */ jsx5("summary", { className: "lax-step-card__summary", children: node.label }) });
351
+ }
352
+
353
+ // src/view/nodes/ErrorNode.tsx
354
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
355
+ function ErrorCard({ nodeId }) {
356
+ const node = useNodeTyped(nodeId, "error");
357
+ if (!node) return null;
358
+ return /* @__PURE__ */ jsxs4("div", { className: "lax-error-card", role: "alert", children: [
359
+ /* @__PURE__ */ jsx6("div", { className: "lax-error-card__message", children: node.message }),
360
+ node.errorType ? /* @__PURE__ */ jsx6("div", { className: "lax-error-card__type", children: node.errorType }) : null
361
+ ] });
362
+ }
363
+
364
+ // src/view/nodes/UnknownNode.tsx
365
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
366
+ function UnknownCard({ nodeId }) {
367
+ const node = useNodeTyped(nodeId, "unknown");
368
+ if (!node) return null;
369
+ return /* @__PURE__ */ jsxs5("div", { className: "lax-unknown-card", children: [
370
+ /* @__PURE__ */ jsxs5("div", { className: "lax-unknown-card__type", children: [
371
+ "Unknown: ",
372
+ node.eventType
373
+ ] }),
374
+ /* @__PURE__ */ jsx7("pre", { className: "lax-unknown-card__data", children: JSON.stringify(node.rawData, null, 2) })
375
+ ] });
376
+ }
377
+
378
+ // src/view/nodes/PermissionNode.tsx
379
+ import { useState as useState3 } from "react";
380
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
381
+ function PermissionCard({ nodeId }) {
382
+ const node = useNodeTyped(nodeId, "permission");
383
+ const bus = useInteractionBus();
384
+ const [resolvedLocally, setResolvedLocally] = useState3(false);
385
+ if (!node) return null;
386
+ const pending = node.status === "pending" && !resolvedLocally;
387
+ const onAllow = () => {
388
+ void bus.resolvePermission({ requestId: node.requestId, allow: true });
389
+ setResolvedLocally(true);
390
+ };
391
+ const onDeny = () => {
392
+ void bus.resolvePermission({ requestId: node.requestId, allow: false });
393
+ setResolvedLocally(true);
394
+ };
395
+ return /* @__PURE__ */ jsxs6(
396
+ "div",
397
+ {
398
+ className: "lax-permission-card",
399
+ "data-status": pending ? "pending" : "resolved",
400
+ "data-request-id": node.requestId,
401
+ children: [
402
+ /* @__PURE__ */ jsxs6("div", { className: "lax-permission-card__header", children: [
403
+ /* @__PURE__ */ jsx8("span", { className: "lax-permission-card__label", children: "Permission" }),
404
+ /* @__PURE__ */ jsx8("span", { className: "lax-permission-card__tool", children: node.toolName })
405
+ ] }),
406
+ /* @__PURE__ */ jsx8("div", { className: "lax-permission-card__message", children: node.message }),
407
+ node.askPrompt && node.askPrompt !== node.message ? /* @__PURE__ */ jsx8("div", { className: "lax-permission-card__prompt", children: node.askPrompt }) : null,
408
+ pending ? /* @__PURE__ */ jsxs6("div", { className: "lax-permission-card__actions", children: [
409
+ /* @__PURE__ */ jsx8("button", { type: "button", className: "lax-permission-card__allow", onClick: onAllow, children: "Allow" }),
410
+ /* @__PURE__ */ jsx8("button", { type: "button", className: "lax-permission-card__deny", onClick: onDeny, children: "Deny" })
411
+ ] }) : /* @__PURE__ */ jsx8("div", { className: "lax-permission-card__resolved", children: "Resolved" })
412
+ ]
413
+ }
414
+ );
415
+ }
416
+
417
+ // src/view/nodes/MemorySavedNode.tsx
418
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
419
+ function MemorySavedRow({ nodeId }) {
420
+ const node = useNodeTyped(nodeId, "memory_saved");
421
+ if (!node) return null;
422
+ const hasPaths = node.writtenPaths.length > 0;
423
+ return /* @__PURE__ */ jsxs7(
424
+ "details",
425
+ {
426
+ className: "lax-memory-saved",
427
+ "data-testid": "lax-memory-saved",
428
+ "data-node-id": nodeId,
429
+ children: [
430
+ /* @__PURE__ */ jsxs7("summary", { className: "lax-memory-saved__summary", children: [
431
+ /* @__PURE__ */ jsx9("span", { className: "lax-memory-saved__icon", "aria-hidden": "true", children: "\u{1F4BE}" }),
432
+ /* @__PURE__ */ jsx9("span", { className: "lax-memory-saved__hint", children: node.displayHint })
433
+ ] }),
434
+ hasPaths ? /* @__PURE__ */ jsx9("ul", { className: "lax-memory-saved__paths", children: node.writtenPaths.map((p) => /* @__PURE__ */ jsx9("li", { className: "lax-memory-saved__path", children: /* @__PURE__ */ jsx9("code", { children: p }) }, p)) }) : null
435
+ ]
436
+ }
437
+ );
438
+ }
439
+
440
+ // src/view/NodeRegistry.ts
441
+ var DEFAULT_WIDGETS = /* @__PURE__ */ new Map([
442
+ ["text", TextBubble],
443
+ ["reasoning", ReasoningBubble],
444
+ ["tool_call", ToolCallCard],
445
+ ["step", StepCard],
446
+ ["error", ErrorCard],
447
+ ["unknown", UnknownCard],
448
+ ["permission", PermissionCard],
449
+ ["memory_saved", MemorySavedRow]
450
+ ]);
451
+ var NodeRegistry = class _NodeRegistry {
452
+ widgets;
453
+ constructor(widgets) {
454
+ this.widgets = widgets;
455
+ }
456
+ static default() {
457
+ return new _NodeRegistry(new Map(DEFAULT_WIDGETS));
458
+ }
459
+ override(kind, widget) {
460
+ const next = new Map(this.widgets);
461
+ next.set(kind, widget);
462
+ return new _NodeRegistry(next);
463
+ }
464
+ register(kind, widget) {
465
+ return this.override(kind, widget);
466
+ }
467
+ get(kind) {
468
+ return this.widgets.get(kind);
469
+ }
470
+ has(kind) {
471
+ return this.widgets.has(kind);
472
+ }
473
+ };
474
+
475
+ // src/view/defaultRegistry.ts
476
+ function createDefaultRegistry() {
477
+ return NodeRegistry.default();
478
+ }
479
+
480
+ // src/core/sessionStore.ts
481
+ import { createStore } from "zustand/vanilla";
482
+
483
+ // src/core/dedupe.ts
484
+ function shouldSkipDuplicateEvent(seenEventIds, sseEventId) {
485
+ if (!sseEventId) return false;
486
+ return seenEventIds.has(sseEventId);
487
+ }
488
+ function markEventIdSeen(seenEventIds, sseEventId) {
489
+ if (sseEventId) seenEventIds.add(sseEventId);
490
+ }
491
+
492
+ // src/core/sessionStore.ts
493
+ var DEFAULT_MAX_SESSIONS = 10;
494
+ function touchOrder(order, sessionId) {
495
+ return [...order.filter((id) => id !== sessionId), sessionId];
496
+ }
497
+ function safeReduce(tree, event, eventIndex, options) {
498
+ try {
499
+ return reduceTree(tree, event, eventIndex, options);
500
+ } catch (err) {
501
+ const message = err instanceof Error ? err.message : String(err);
502
+ const stack = err instanceof Error ? err.stack : void 0;
503
+ if (import.meta.env?.DEV) {
504
+ console.error("[langchain_agentx_stream_ui] reducer error:", err);
505
+ }
506
+ const reducerError = {
507
+ eventIndex,
508
+ eventType: event.event_type,
509
+ message,
510
+ stack
511
+ };
512
+ return {
513
+ ...tree,
514
+ internalErrors: [...tree.internalErrors, reducerError]
515
+ };
516
+ }
517
+ }
518
+ function evictSessions(sessions, order, maxSessions, activeSessionId) {
519
+ let nextSessions = { ...sessions };
520
+ let nextOrder = [...order];
521
+ while (nextOrder.length > maxSessions) {
522
+ const candidate = nextOrder.find((id) => id !== activeSessionId) ?? nextOrder[0];
523
+ if (!candidate) break;
524
+ const { [candidate]: _removed, ...rest } = nextSessions;
525
+ nextSessions = rest;
526
+ nextOrder = nextOrder.filter((id) => id !== candidate);
527
+ }
528
+ return { sessions: nextSessions, order: nextOrder };
529
+ }
530
+ function createMultiSessionStore(options) {
531
+ const maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
532
+ const reduceOpts = {
533
+ tierOverrides: options?.tierOverrides,
534
+ collectDebug: options?.debug === true
535
+ };
536
+ const replayOpts = {
537
+ tierOverrides: options?.tierOverrides,
538
+ collectDebug: options?.debug === true
539
+ };
540
+ const seenBySession = /* @__PURE__ */ new Map();
541
+ function getSeenSet(sessionId) {
542
+ let set = seenBySession.get(sessionId);
543
+ if (!set) {
544
+ set = /* @__PURE__ */ new Set();
545
+ seenBySession.set(sessionId, set);
546
+ }
547
+ return set;
548
+ }
549
+ return createStore((set, get) => ({
550
+ sessions: {},
551
+ activeSessionId: options?.activeSessionId ?? null,
552
+ order: [],
553
+ getSessionTree(sessionId) {
554
+ return get().sessions[sessionId]?.tree;
555
+ },
556
+ ensureSession(sessionId, initialEvents) {
557
+ const state = get();
558
+ if (state.sessions[sessionId]) return;
559
+ const tree = initialEvents && initialEvents.length > 0 ? replayEvents(initialEvents, replayOpts) : createEmptyTree();
560
+ const slice = {
561
+ tree,
562
+ eventCount: initialEvents?.length ?? 0
563
+ };
564
+ let order = touchOrder(state.order, sessionId);
565
+ let sessions = { ...state.sessions, [sessionId]: slice };
566
+ ({ sessions, order } = evictSessions(sessions, order, maxSessions, state.activeSessionId));
567
+ set({
568
+ sessions,
569
+ order,
570
+ activeSessionId: state.activeSessionId ?? sessionId
571
+ });
572
+ },
573
+ applyEvent(sessionId, event, ctx) {
574
+ const sseEventId = ctx?.sseEventId;
575
+ const seen = getSeenSet(sessionId);
576
+ if (shouldSkipDuplicateEvent(seen, sseEventId)) return;
577
+ const state = get();
578
+ let slice = state.sessions[sessionId];
579
+ if (!slice) {
580
+ get().ensureSession(sessionId);
581
+ slice = get().sessions[sessionId];
582
+ }
583
+ const reduced = safeReduce(slice.tree, event, slice.eventCount, reduceOpts);
584
+ const nextTree = sseEventId ? { ...reduced, meta: { ...reduced.meta, lastEventId: sseEventId } } : reduced;
585
+ const nextSlice = {
586
+ tree: nextTree,
587
+ eventCount: slice.eventCount + 1
588
+ };
589
+ markEventIdSeen(seen, sseEventId);
590
+ let sessions = { ...state.sessions, [sessionId]: nextSlice };
591
+ let order = touchOrder(state.order, sessionId);
592
+ ({ sessions, order } = evictSessions(sessions, order, maxSessions, state.activeSessionId));
593
+ set({ sessions, order });
594
+ },
595
+ applyEvents(sessionId, events) {
596
+ for (const event of events) {
597
+ get().applyEvent(sessionId, event);
598
+ }
599
+ },
600
+ setActiveSessionId(id) {
601
+ set({ activeSessionId: id });
602
+ },
603
+ removeSession(sessionId) {
604
+ const state = get();
605
+ if (!state.sessions[sessionId]) return;
606
+ seenBySession.delete(sessionId);
607
+ const { [sessionId]: _removed, ...sessions } = state.sessions;
608
+ const order = state.order.filter((id) => id !== sessionId);
609
+ const activeSessionId = state.activeSessionId === sessionId ? order[order.length - 1] ?? null : state.activeSessionId;
610
+ set({ sessions, order, activeSessionId });
611
+ },
612
+ replaySession(sessionId, events) {
613
+ const tree = replayEvents(events, replayOpts);
614
+ const slice = { tree, eventCount: events.length };
615
+ const state = get();
616
+ let order = touchOrder(state.order, sessionId);
617
+ let sessions = { ...state.sessions, [sessionId]: slice };
618
+ ({ sessions, order } = evictSessions(sessions, order, maxSessions, state.activeSessionId));
619
+ set({ sessions, order });
620
+ }
621
+ }));
622
+ }
623
+ function createActiveSessionBridge(multiStore) {
624
+ const bridge = createStore(() => ({
625
+ tree: createEmptyTree(),
626
+ eventCount: 0,
627
+ applyEvent(event, ctx) {
628
+ const id = multiStore.getState().activeSessionId;
629
+ if (id) multiStore.getState().applyEvent(id, event, ctx);
630
+ },
631
+ applyEvents(events) {
632
+ const id = multiStore.getState().activeSessionId;
633
+ if (id) multiStore.getState().applyEvents(id, events);
634
+ },
635
+ reset() {
636
+ const id = multiStore.getState().activeSessionId;
637
+ if (id) multiStore.getState().replaySession(id, []);
638
+ }
639
+ }));
640
+ const sync = () => {
641
+ const { activeSessionId, sessions } = multiStore.getState();
642
+ const slice = activeSessionId ? sessions[activeSessionId] : void 0;
643
+ bridge.setState({
644
+ tree: slice?.tree ?? createEmptyTree(),
645
+ eventCount: slice?.eventCount ?? 0
646
+ });
647
+ };
648
+ multiStore.subscribe(sync);
649
+ sync();
650
+ return bridge;
651
+ }
652
+
653
+ // src/view/MultiAgentSession.tsx
654
+ import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2 } from "react";
655
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
656
+ function DebugPanel() {
657
+ const status = useSessionStatus();
658
+ const errors = useInternalErrors();
659
+ if (errors.length === 0) return null;
660
+ return /* @__PURE__ */ jsxs8("div", { className: "lax-debug-panel", "data-testid": "lax-debug-panel", children: [
661
+ /* @__PURE__ */ jsxs8("div", { children: [
662
+ "status: ",
663
+ status
664
+ ] }),
665
+ /* @__PURE__ */ jsx10("ul", { children: errors.map((err, i) => /* @__PURE__ */ jsxs8("li", { children: [
666
+ "[",
667
+ err.eventType,
668
+ "] ",
669
+ err.message
670
+ ] }, `${err.eventIndex}-${i}`)) })
671
+ ] });
672
+ }
673
+ function MultiAgentSession({
674
+ sessions,
675
+ activeSessionId: controlledActiveId,
676
+ defaultActiveSessionId,
677
+ onActiveSessionChange,
678
+ maxSessions,
679
+ tierOverrides,
680
+ debug = false,
681
+ registry,
682
+ onError,
683
+ virtualized = false,
684
+ virtualizeThreshold,
685
+ interactionBus,
686
+ toolDisplayRegistry,
687
+ defaultBodyMode = "preview",
688
+ groupParallelTools = false,
689
+ permissionUiMode = "standalone",
690
+ markdownRenderer,
691
+ children
692
+ }) {
693
+ const multiStoreRef = useRef2(null);
694
+ const bridgeRef = useRef2(null);
695
+ if (multiStoreRef.current === null) {
696
+ const defaultId = defaultActiveSessionId ?? controlledActiveId ?? sessions[0]?.sessionId ?? null;
697
+ multiStoreRef.current = createMultiSessionStore({
698
+ maxSessions,
699
+ tierOverrides,
700
+ debug,
701
+ activeSessionId: defaultId
702
+ });
703
+ bridgeRef.current = createActiveSessionBridge(multiStoreRef.current);
704
+ for (const cfg of sessions) {
705
+ multiStoreRef.current.getState().ensureSession(cfg.sessionId, cfg.initialEvents);
706
+ }
707
+ }
708
+ const registryRef = useMemo2(() => registry ?? createDefaultRegistry(), [registry]);
709
+ const busRef = useMemo2(
710
+ () => interactionBus ?? getNoopInteractionBus(),
711
+ [interactionBus]
712
+ );
713
+ const toolDisplayRef = useMemo2(
714
+ () => ({
715
+ registry: toolDisplayRegistry ?? createDefaultToolRegistry(),
716
+ defaultBodyMode
717
+ }),
718
+ [toolDisplayRegistry, defaultBodyMode]
719
+ );
720
+ const sessionViewRef = useMemo2(
721
+ () => ({
722
+ ...DEFAULT_SESSION_VIEW_OPTIONS,
723
+ groupParallelTools,
724
+ permissionUiMode,
725
+ verboseReasoning: debug
726
+ }),
727
+ [groupParallelTools, permissionUiMode, debug]
728
+ );
729
+ useEffect2(() => {
730
+ if (controlledActiveId === void 0) return;
731
+ multiStoreRef.current.getState().setActiveSessionId(controlledActiveId);
732
+ }, [controlledActiveId]);
733
+ useEffect2(() => {
734
+ if (!onActiveSessionChange) return;
735
+ return multiStoreRef.current.subscribe((state) => {
736
+ onActiveSessionChange(state.activeSessionId);
737
+ });
738
+ }, [onActiveSessionChange]);
739
+ useEffect2(() => {
740
+ const multiStore = multiStoreRef.current;
741
+ const controllers = sessions.map((cfg) => {
742
+ const controller = new AbortController();
743
+ multiStore.getState().ensureSession(cfg.sessionId, cfg.initialEvents);
744
+ void cfg.source.start((event, ctx) => {
745
+ multiStore.getState().applyEvent(cfg.sessionId, event, ctx);
746
+ }, controller.signal).catch((err) => {
747
+ const error = err instanceof Error ? err : new Error(String(err));
748
+ onError?.(error, cfg.sessionId);
749
+ });
750
+ return controller;
751
+ });
752
+ return () => {
753
+ for (const c of controllers) c.abort();
754
+ };
755
+ }, [sessions, onError]);
756
+ return /* @__PURE__ */ jsx10(MultiSessionStoreContext.Provider, { value: multiStoreRef.current, children: /* @__PURE__ */ jsx10(SessionStoreContext.Provider, { value: bridgeRef.current, children: /* @__PURE__ */ jsx10(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx10(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx10(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx10(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx10(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs8("div", { className: "lax-agent-session lax-multi-agent-session", "data-testid": "lax-multi-agent-session", children: [
757
+ children ?? /* @__PURE__ */ jsx10(
758
+ SessionTimeline,
759
+ {
760
+ virtualized,
761
+ virtualizeThreshold,
762
+ groupParallelTools
763
+ }
764
+ ),
765
+ debug ? /* @__PURE__ */ jsx10(DebugPanel, {}) : null
766
+ ] }) }) }) }) }) }) }) });
767
+ }
768
+
769
+ export {
770
+ shouldSkipDuplicateEvent,
771
+ markEventIdSeen,
772
+ ReasoningBubble,
773
+ TextBubble,
774
+ ToolCallCard,
775
+ StepCard,
776
+ ErrorCard,
777
+ UnknownCard,
778
+ PermissionCard,
779
+ NodeRegistry,
780
+ createDefaultRegistry,
781
+ createMultiSessionStore,
782
+ createActiveSessionBridge,
783
+ MultiAgentSession
784
+ };
785
+ //# sourceMappingURL=chunk-W6QQHTJ4.js.map