langchain_agentx_stream_ui 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  fixDiagramSvg,
24
24
  markEventIdSeen,
25
25
  shouldSkipDuplicateEvent
26
- } from "./chunk-ED67MXSG.js";
26
+ } from "./chunk-YHCK76OU.js";
27
27
  import {
28
28
  DefaultToolBody,
29
29
  InteractionBusContext,
@@ -32,8 +32,8 @@ import {
32
32
  getNoopInteractionBus,
33
33
  resolveToolBody,
34
34
  useInteractionBus
35
- } from "./chunk-4TG4TGIL.js";
36
- import "./chunk-DP7V33X7.js";
35
+ } from "./chunk-5XELRP3S.js";
36
+ import "./chunk-6Z4ZF36Z.js";
37
37
  import {
38
38
  CollapseKind,
39
39
  CollapsedExploreNode,
@@ -119,7 +119,7 @@ import {
119
119
  useSessionStatus,
120
120
  useSessionViewOptions,
121
121
  useTimeline
122
- } from "./chunk-DJPNJBFL.js";
122
+ } from "./chunk-O5GERXTY.js";
123
123
  import {
124
124
  AGENT_PROGRESS_GUTTER,
125
125
  AGENT_PROGRESS_INDENT,
@@ -149,7 +149,7 @@ import {
149
149
  createDefaultToolRegistry,
150
150
  formatAgentTitle,
151
151
  formatExploreSummaryText
152
- } from "./chunk-ZR37OKV6.js";
152
+ } from "./chunk-GYFC2DI7.js";
153
153
  import {
154
154
  BodyBlockList,
155
155
  DiffView,
@@ -165,7 +165,7 @@ import {
165
165
  formatTimeoutFooter,
166
166
  parseDiffText,
167
167
  truncateCommand
168
- } from "./chunk-4RIOBLGB.js";
168
+ } from "./chunk-GBY5DJ7L.js";
169
169
 
170
170
  // src/view/AgentSession.tsx
171
171
  import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "react";
@@ -839,6 +839,157 @@ function hydrateAskFromSnapshot(state, snapshot) {
839
839
  };
840
840
  }
841
841
 
842
+ // src/core/sessionTreeSoftTrim.ts
843
+ var DEFAULT_MAX_RUNNING_LOOP_NODES = 5e3;
844
+ var DEFAULT_RETAIN_RUNNING_LOOP_NODES = 3500;
845
+ function isProtectedNode(node) {
846
+ switch (node.kind) {
847
+ case "tool_call":
848
+ case "subagent":
849
+ return node.status === "running";
850
+ case "permission":
851
+ return node.status === "pending";
852
+ case "text":
853
+ case "reasoning":
854
+ return node.status === "streaming";
855
+ case "step":
856
+ return node.status === "running";
857
+ default:
858
+ return false;
859
+ }
860
+ }
861
+ function collectSubtreeIds(rootId, byId) {
862
+ const out = [];
863
+ const stack = [rootId];
864
+ while (stack.length > 0) {
865
+ const id = stack.pop();
866
+ if (!byId[id]) continue;
867
+ out.push(id);
868
+ const node = byId[id];
869
+ if (node.kind === "subagent") {
870
+ for (const childId of node.childIds) {
871
+ stack.push(childId);
872
+ }
873
+ }
874
+ }
875
+ return out;
876
+ }
877
+ function collectProtectedIds(tree) {
878
+ const protectedIds = /* @__PURE__ */ new Set();
879
+ for (const [id, node] of Object.entries(tree.byId)) {
880
+ if (isProtectedNode(node)) {
881
+ protectedIds.add(id);
882
+ }
883
+ }
884
+ for (const [id, node] of Object.entries(tree.byId)) {
885
+ if (node.kind !== "subagent") continue;
886
+ for (const childId of collectSubtreeIds(id, tree.byId)) {
887
+ if (childId !== id && protectedIds.has(childId)) {
888
+ protectedIds.add(id);
889
+ break;
890
+ }
891
+ }
892
+ }
893
+ return protectedIds;
894
+ }
895
+ function subtreeContainsProtected(rootId, byId, protectedIds) {
896
+ for (const id of collectSubtreeIds(rootId, byId)) {
897
+ if (protectedIds.has(id)) return true;
898
+ }
899
+ return false;
900
+ }
901
+ function filterActiveToolByStepName(active, byId) {
902
+ const next = {};
903
+ for (const [key, nodeId] of Object.entries(active)) {
904
+ if (byId[nodeId]) next[key] = nodeId;
905
+ }
906
+ return next;
907
+ }
908
+ function trimSessionTreeSoft(tree, options) {
909
+ const maxNodes = Math.max(
910
+ 1,
911
+ options?.maxRunningLoopNodes ?? DEFAULT_MAX_RUNNING_LOOP_NODES
912
+ );
913
+ const retainTarget = Math.min(
914
+ maxNodes,
915
+ Math.max(
916
+ 1,
917
+ options?.retainRunningLoopNodes ?? DEFAULT_RETAIN_RUNNING_LOOP_NODES
918
+ )
919
+ );
920
+ const nodeCount = Object.keys(tree.byId).length;
921
+ if (nodeCount <= maxNodes) {
922
+ return tree;
923
+ }
924
+ const protectedIds = collectProtectedIds(tree);
925
+ const dropIds = /* @__PURE__ */ new Set();
926
+ const dropRootIds = /* @__PURE__ */ new Set();
927
+ let projectedCount = nodeCount;
928
+ for (const rootId of tree.rootChildren) {
929
+ if (projectedCount <= retainTarget) break;
930
+ if (!tree.byId[rootId] || dropIds.has(rootId)) continue;
931
+ if (subtreeContainsProtected(rootId, tree.byId, protectedIds)) continue;
932
+ const subtree = collectSubtreeIds(rootId, tree.byId);
933
+ for (const id of subtree) {
934
+ dropIds.add(id);
935
+ }
936
+ dropRootIds.add(rootId);
937
+ projectedCount -= subtree.length;
938
+ }
939
+ if (dropIds.size === 0) {
940
+ return tree;
941
+ }
942
+ const nextById = {};
943
+ for (const [id, node] of Object.entries(tree.byId)) {
944
+ if (!dropIds.has(id)) {
945
+ nextById[id] = node;
946
+ }
947
+ }
948
+ const rootChildren = tree.rootChildren.filter((id) => !dropRootIds.has(id));
949
+ const dropped = dropIds.size;
950
+ return {
951
+ ...tree,
952
+ byId: nextById,
953
+ rootChildren,
954
+ openSubAgentStack: tree.openSubAgentStack.filter((id) => Boolean(nextById[id])),
955
+ activeToolByStepName: filterActiveToolByStepName(
956
+ tree.activeToolByStepName,
957
+ nextById
958
+ ),
959
+ meta: {
960
+ ...tree.meta,
961
+ displayTruncated: true,
962
+ displayTruncatedDropped: (tree.meta.displayTruncatedDropped ?? 0) + dropped
963
+ }
964
+ };
965
+ }
966
+ function trimWorkflowLoopTreesSoft(state, options) {
967
+ const nextLoops = {};
968
+ let anyTruncated = Boolean(state.meta.loopDisplayTruncated);
969
+ let changed = false;
970
+ for (const [sessionId, tree] of Object.entries(state.loopTreesBySessionId)) {
971
+ const trimmed = trimSessionTreeSoft(tree, options);
972
+ nextLoops[sessionId] = trimmed;
973
+ if (trimmed !== tree) {
974
+ changed = true;
975
+ }
976
+ if (trimmed.meta.displayTruncated) {
977
+ anyTruncated = true;
978
+ }
979
+ }
980
+ if (!changed && anyTruncated === Boolean(state.meta.loopDisplayTruncated)) {
981
+ return state;
982
+ }
983
+ return {
984
+ ...state,
985
+ loopTreesBySessionId: changed ? nextLoops : state.loopTreesBySessionId,
986
+ meta: {
987
+ ...state.meta,
988
+ loopDisplayTruncated: anyTruncated
989
+ }
990
+ };
991
+ }
992
+
842
993
  // src/core/store.ts
843
994
  function safeReduce(tree, event, eventIndex, options) {
844
995
  try {
@@ -869,6 +1020,10 @@ function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
869
1020
  const initialEventCount = storeOptions?.initialEventCount ?? 0;
870
1021
  const seenEventIds = new Set(storeOptions?.initialSeenEventIds);
871
1022
  const authorityBindingActive = storeOptions?.authorityBindingActive === true;
1023
+ const softTrimOpts = {
1024
+ maxRunningLoopNodes: storeOptions?.maxRunningLoopNodes ?? DEFAULT_MAX_RUNNING_LOOP_NODES,
1025
+ retainRunningLoopNodes: storeOptions?.retainRunningLoopNodes ?? DEFAULT_RETAIN_RUNNING_LOOP_NODES
1026
+ };
872
1027
  return createStore((set, get) => ({
873
1028
  tree: initialTree,
874
1029
  eventCount: initialEventCount,
@@ -886,10 +1041,11 @@ function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
886
1041
  if (isAtOrBeforeProjectionCursor(projectionCursor, sseEventId)) return;
887
1042
  const { tree, eventCount } = get();
888
1043
  const { tree: reduced } = safeReduce(tree, event, eventCount, reduceOpts);
1044
+ const trimmed = trimSessionTreeSoft(reduced, softTrimOpts);
889
1045
  const nextTree = sseEventId ? {
890
- ...reduced,
891
- meta: { ...reduced.meta, lastEventId: sseEventId }
892
- } : reduced;
1046
+ ...trimmed,
1047
+ meta: { ...trimmed.meta, lastEventId: sseEventId }
1048
+ } : trimmed;
893
1049
  markEventIdSeen(seenEventIds, sseEventId);
894
1050
  set({ tree: nextTree, eventCount: eventCount + 1 });
895
1051
  },
@@ -897,7 +1053,7 @@ function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
897
1053
  let { tree, eventCount } = get();
898
1054
  for (const event of events) {
899
1055
  const result = safeReduce(tree, event, eventCount, reduceOpts);
900
- tree = result.tree;
1056
+ tree = trimSessionTreeSoft(result.tree, softTrimOpts);
901
1057
  eventCount += 1;
902
1058
  }
903
1059
  set({ tree, eventCount });
@@ -912,7 +1068,10 @@ function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
912
1068
  const patch = hydrateAskFromSnapshot(get(), snapshot);
913
1069
  seenEventIds.clear();
914
1070
  markEventIdSeen(seenEventIds, snapshot.cursor);
915
- set(patch);
1071
+ set({
1072
+ ...patch,
1073
+ tree: trimSessionTreeSoft(patch.tree, softTrimOpts)
1074
+ });
916
1075
  },
917
1076
  markSnapshotHydrateFailed() {
918
1077
  set({
@@ -1542,33 +1701,47 @@ function AgentLoopView({
1542
1701
  }
1543
1702
 
1544
1703
  // src/view/AgentSessionBanners.tsx
1545
- import { jsx as jsx3 } from "react/jsx-runtime";
1546
- function AgentSessionBanners({ snapshotHydrateFailed }) {
1547
- if (!snapshotHydrateFailed) return null;
1548
- return /* @__PURE__ */ jsx3(
1549
- "div",
1550
- {
1551
- className: "lax-agent-banner lax-agent-banner--snapshot-unavailable",
1552
- "data-testid": "lax-agent-snapshot-unavailable-banner",
1553
- role: "status",
1554
- children: "\u5C55\u793A\u6001\u4E0D\u53EF\u7528 / \u4F1A\u8BDD\u5DF2\u8FC7\u671F"
1555
- }
1556
- );
1704
+ import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
1705
+ function AgentSessionBanners({
1706
+ snapshotHydrateFailed,
1707
+ displayTruncated = false
1708
+ }) {
1709
+ if (!snapshotHydrateFailed && !displayTruncated) return null;
1710
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1711
+ snapshotHydrateFailed ? /* @__PURE__ */ jsx3(
1712
+ "div",
1713
+ {
1714
+ className: "lax-agent-banner lax-agent-banner--snapshot-unavailable",
1715
+ "data-testid": "lax-agent-snapshot-unavailable-banner",
1716
+ role: "status",
1717
+ children: "\u5C55\u793A\u6001\u4E0D\u53EF\u7528 / \u4F1A\u8BDD\u5DF2\u8FC7\u671F"
1718
+ }
1719
+ ) : null,
1720
+ displayTruncated ? /* @__PURE__ */ jsx3(
1721
+ "div",
1722
+ {
1723
+ className: "lax-agent-banner lax-agent-banner--display-truncated",
1724
+ "data-testid": "lax-agent-display-truncated-banner",
1725
+ role: "status",
1726
+ children: "\u8F83\u65E9\u7684\u65F6\u95F4\u7EBF\u5DF2\u622A\u65AD\u4EE5\u63A7\u5236\u5185\u5B58\uFF0C\u6700\u65B0\u5DE5\u5177\u6D3B\u52A8\u4ECD\u5728\u66F4\u65B0"
1727
+ }
1728
+ ) : null
1729
+ ] });
1557
1730
  }
1558
1731
 
1559
1732
  // src/view/AgentSession.tsx
1560
1733
  import { useStore } from "zustand";
1561
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
1734
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1562
1735
  function DebugPanel() {
1563
1736
  const status = useSessionStatus();
1564
1737
  const errors = useInternalErrors();
1565
1738
  if (errors.length === 0) return null;
1566
- return /* @__PURE__ */ jsxs("div", { className: "lax-debug-panel", "data-testid": "lax-debug-panel", children: [
1567
- /* @__PURE__ */ jsxs("div", { children: [
1739
+ return /* @__PURE__ */ jsxs2("div", { className: "lax-debug-panel", "data-testid": "lax-debug-panel", children: [
1740
+ /* @__PURE__ */ jsxs2("div", { children: [
1568
1741
  "status: ",
1569
1742
  status
1570
1743
  ] }),
1571
- /* @__PURE__ */ jsx4("ul", { children: errors.map((err, i) => /* @__PURE__ */ jsxs("li", { children: [
1744
+ /* @__PURE__ */ jsx4("ul", { children: errors.map((err, i) => /* @__PURE__ */ jsxs2("li", { children: [
1572
1745
  "[",
1573
1746
  err.eventType,
1574
1747
  "] ",
@@ -1600,7 +1773,17 @@ function AgentSnapshotBootstrap({
1600
1773
  }
1601
1774
  }, [snapshotHydrateFailed, store]);
1602
1775
  const showBanner = useStore(store, (s) => s.snapshotHydrateFailed);
1603
- return /* @__PURE__ */ jsx4(AgentSessionBanners, { snapshotHydrateFailed: showBanner });
1776
+ const displayTruncated = useStore(
1777
+ store,
1778
+ (s) => Boolean(s.tree.meta.displayTruncated)
1779
+ );
1780
+ return /* @__PURE__ */ jsx4(
1781
+ AgentSessionBanners,
1782
+ {
1783
+ snapshotHydrateFailed: showBanner,
1784
+ displayTruncated
1785
+ }
1786
+ );
1604
1787
  }
1605
1788
  function AgentSession({
1606
1789
  source,
@@ -1710,7 +1893,7 @@ function AgentSession({
1710
1893
  controller.abort();
1711
1894
  };
1712
1895
  }, [source]);
1713
- return /* @__PURE__ */ jsx4(SessionStoreContext.Provider, { value: storeRef.current, children: /* @__PURE__ */ jsx4(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx4(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx4(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx4(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx4(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs("div", { className: "lax-agent-session", "data-testid": "lax-agent-session", children: [
1896
+ return /* @__PURE__ */ jsx4(SessionStoreContext.Provider, { value: storeRef.current, children: /* @__PURE__ */ jsx4(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx4(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx4(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx4(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx4(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs2("div", { className: "lax-agent-session", "data-testid": "lax-agent-session", children: [
1714
1897
  /* @__PURE__ */ jsx4(
1715
1898
  AgentSnapshotBootstrap,
1716
1899
  {
@@ -1774,7 +1957,9 @@ var DEFAULT_WORKFLOW_SCALE_OPTIONS = {
1774
1957
  containerCountTier3: 120,
1775
1958
  tierEvalMinFrames: 30,
1776
1959
  hydrateChunkSize: 50,
1777
- aggressiveMemoryTier3: true
1960
+ aggressiveMemoryTier3: true,
1961
+ maxRunningLoopNodes: 5e3,
1962
+ retainRunningLoopNodes: 3500
1778
1963
  };
1779
1964
  function resolveWorkflowScale(storeOptions) {
1780
1965
  const scale = {
@@ -3245,11 +3430,14 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
3245
3430
  let frameCounter = initialEventCount;
3246
3431
  let lastTierEvalFrame = 0;
3247
3432
  const finalizeState = (reduced, options) => {
3433
+ let next = trimWorkflowLoopTreesSoft(reduced, {
3434
+ maxRunningLoopNodes: scale.maxRunningLoopNodes,
3435
+ retainRunningLoopNodes: scale.retainRunningLoopNodes
3436
+ });
3248
3437
  if (!scale.evictCompletedLoops) {
3249
- return reduced;
3438
+ return next;
3250
3439
  }
3251
3440
  frameCounter += 1;
3252
- let next = reduced;
3253
3441
  ({ state: next, lastTierEvalFrame } = applyMemoryPressureTier(next, scale, {
3254
3442
  frameCounter,
3255
3443
  lastTierEvalFrame,
@@ -3572,7 +3760,7 @@ import { useCallback, useEffect as useEffect6, useMemo as useMemo2, useState, me
3572
3760
  import { useStore as useStore3 } from "zustand";
3573
3761
 
3574
3762
  // src/view/workflow/WorkflowContainerLine.tsx
3575
- import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
3763
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
3576
3764
  function WorkflowContainerLine({
3577
3765
  title,
3578
3766
  description,
@@ -3598,7 +3786,7 @@ function WorkflowContainerLine({
3598
3786
  onToggle && interactive !== false ? "lax-workflow-container-line--interactive" : "",
3599
3787
  className
3600
3788
  ].filter(Boolean).join(" ");
3601
- const content = /* @__PURE__ */ jsxs2(Fragment, { children: [
3789
+ const content = /* @__PURE__ */ jsxs3(Fragment2, { children: [
3602
3790
  showDot ? /* @__PURE__ */ jsx5(
3603
3791
  "span",
3604
3792
  {
@@ -3608,7 +3796,7 @@ function WorkflowContainerLine({
3608
3796
  }
3609
3797
  ) : null,
3610
3798
  /* @__PURE__ */ jsx5("span", { className: "lax-workflow-container-line__title", "data-testid": titleTestId, children: title }),
3611
- description ? /* @__PURE__ */ jsxs2(
3799
+ description ? /* @__PURE__ */ jsxs3(
3612
3800
  "span",
3613
3801
  {
3614
3802
  className: "lax-workflow-container-line__desc",
@@ -3680,7 +3868,7 @@ function hasTimelineNodes(tree) {
3680
3868
  }
3681
3869
 
3682
3870
  // src/view/workflow/workflowStageDoneBody.tsx
3683
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
3871
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
3684
3872
  function WorkflowStageDoneBody({
3685
3873
  loopTree,
3686
3874
  testId,
@@ -3717,7 +3905,7 @@ function WorkflowStageSummaryOnlyBody({
3717
3905
  bodyClassName = "lax-workflow-stage-container__body lax-workflow-stage-container__body--summary-only"
3718
3906
  }) {
3719
3907
  const expandHint = formatTeaserExpandHint(summary.extraLines, verbose);
3720
- return /* @__PURE__ */ jsxs3(
3908
+ return /* @__PURE__ */ jsxs4(
3721
3909
  "div",
3722
3910
  {
3723
3911
  className: bodyClassName,
@@ -3980,7 +4168,7 @@ function workflowContainerViewPropsEqual(prev, next) {
3980
4168
  }
3981
4169
 
3982
4170
  // src/view/workflow/WorkflowAggregateContainer.tsx
3983
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
4171
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
3984
4172
  function mergeContentPreview(node) {
3985
4173
  const blocks = node.content_blocks;
3986
4174
  if (blocks.length === 0) return "";
@@ -4054,7 +4242,7 @@ function WorkflowAggregateContainerInner({
4054
4242
  ) : null;
4055
4243
  const canExpandDone = isDone && (Boolean(loopTree) || Boolean(bodyMarkdown) || Boolean(node.loopSessionId));
4056
4244
  const doneExpandHint = expandHint ?? (canExpandDone ? "(click to expand)" : null);
4057
- return /* @__PURE__ */ jsxs4(
4245
+ return /* @__PURE__ */ jsxs5(
4058
4246
  "div",
4059
4247
  {
4060
4248
  className: `lax-workflow-aggregate lax-workflow-stage-container lax-workflow-stage-container--${isWaiting ? "pending" : isDone ? "done" : "running"}`,
@@ -4077,7 +4265,7 @@ function WorkflowAggregateContainerInner({
4077
4265
  descriptionTestId: `${testId}-desc`
4078
4266
  }
4079
4267
  ),
4080
- isWaiting && waitingTeaser ? /* @__PURE__ */ jsxs4(
4268
+ isWaiting && waitingTeaser ? /* @__PURE__ */ jsxs5(
4081
4269
  "div",
4082
4270
  {
4083
4271
  className: "lax-workflow-stage-container__done-teaser",
@@ -4119,7 +4307,7 @@ function WorkflowAggregateContainerInner({
4119
4307
  children: markdownPreview
4120
4308
  }
4121
4309
  ) : null,
4122
- isDone && !expanded && (bodyMarkdown || doneExpandHint) ? /* @__PURE__ */ jsx8("div", { "data-testid": `${testId}-done`, children: /* @__PURE__ */ jsxs4(
4310
+ isDone && !expanded && (bodyMarkdown || doneExpandHint) ? /* @__PURE__ */ jsx8("div", { "data-testid": `${testId}-done`, children: /* @__PURE__ */ jsxs5(
4123
4311
  "div",
4124
4312
  {
4125
4313
  className: "lax-workflow-stage-container__done-teaser",
@@ -4133,7 +4321,7 @@ function WorkflowAggregateContainerInner({
4133
4321
  children: bodyMarkdown ? AGENT_PROGRESS_GUTTER : AGENT_PROGRESS_INDENT
4134
4322
  }
4135
4323
  ),
4136
- /* @__PURE__ */ jsxs4("div", { className: "lax-workflow-stage-container__done-teaser-body", children: [
4324
+ /* @__PURE__ */ jsxs5("div", { className: "lax-workflow-stage-container__done-teaser-body", children: [
4137
4325
  bodyMarkdown ? /* @__PURE__ */ jsx8(
4138
4326
  WorkflowMarkdownPreview,
4139
4327
  {
@@ -4287,7 +4475,7 @@ function formatParallelGroupSummary(metrics) {
4287
4475
  }
4288
4476
 
4289
4477
  // src/view/workflow/WorkflowExpandTrigger.tsx
4290
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
4478
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
4291
4479
  function WorkflowExpandTrigger({
4292
4480
  summary,
4293
4481
  summaryTestId,
@@ -4307,9 +4495,9 @@ function WorkflowExpandTrigger({
4307
4495
  onClick: onToggle,
4308
4496
  "aria-expanded": expanded,
4309
4497
  "data-testid": testId,
4310
- children: /* @__PURE__ */ jsxs5("span", { className: "lax-workflow-expand-trigger__hang", children: [
4498
+ children: /* @__PURE__ */ jsxs6("span", { className: "lax-workflow-expand-trigger__hang", children: [
4311
4499
  /* @__PURE__ */ jsx9("span", { className: "lax-workflow-expand-trigger__prefix", "aria-hidden": true, children: AGENT_PROGRESS_GUTTER }),
4312
- /* @__PURE__ */ jsxs5("span", { className: "lax-workflow-expand-trigger__body", children: [
4500
+ /* @__PURE__ */ jsxs6("span", { className: "lax-workflow-expand-trigger__body", children: [
4313
4501
  /* @__PURE__ */ jsx9(
4314
4502
  "span",
4315
4503
  {
@@ -4336,7 +4524,7 @@ function WorkflowExpandTrigger({
4336
4524
  // src/view/workflow/WorkflowStageContainer.tsx
4337
4525
  import { useCallback as useCallback2, useEffect as useEffect7, useMemo as useMemo3, useState as useState2, memo as memo2 } from "react";
4338
4526
  import { useStore as useStore4 } from "zustand";
4339
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
4527
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
4340
4528
  function containerTestId(node) {
4341
4529
  if (node.scope === "item") return `lax-workflow-parallel-${node.scope_key}`;
4342
4530
  if (node.scope === "stage") return `lax-workflow-stage-${node.scope_key}`;
@@ -4417,7 +4605,7 @@ function WorkflowStageContainerInner({
4417
4605
  const statusLabel = effectivelyDone ? doneLabel : suppressHostScopedLoop && effectivelyRunning ? "running" : mapContainerStatusLabel(effectivelyRunning ? "running" : node.status);
4418
4606
  const canToggleShell = effectivelyRunning && !isSkipped || effectivelyDone;
4419
4607
  const shellToggle = canToggleShell ? toggleExpanded : void 0;
4420
- return /* @__PURE__ */ jsxs6(
4608
+ return /* @__PURE__ */ jsxs7(
4421
4609
  "div",
4422
4610
  {
4423
4611
  className: `lax-workflow-stage-container lax-workflow-stage-container--${uiStatus}${isSkipped ? " lax-workflow-stage-container--skipped" : ""}${isItem ? " lax-workflow-parallel-item" : " lax-workflow-stage"}`,
@@ -4448,7 +4636,7 @@ function WorkflowStageContainerInner({
4448
4636
  className: isItem ? "lax-workflow-parallel-item__header" : void 0
4449
4637
  }
4450
4638
  ),
4451
- effectivelyDone && !expanded && (teaserText || expandHint) ? /* @__PURE__ */ jsxs6(
4639
+ effectivelyDone && !expanded && (teaserText || expandHint) ? /* @__PURE__ */ jsxs7(
4452
4640
  "div",
4453
4641
  {
4454
4642
  className: "lax-workflow-stage-container__done-teaser",
@@ -4462,7 +4650,7 @@ function WorkflowStageContainerInner({
4462
4650
  children: teaserText ? AGENT_PROGRESS_GUTTER : AGENT_PROGRESS_INDENT
4463
4651
  }
4464
4652
  ),
4465
- /* @__PURE__ */ jsxs6("div", { className: "lax-workflow-stage-container__done-teaser-body", children: [
4653
+ /* @__PURE__ */ jsxs7("div", { className: "lax-workflow-stage-container__done-teaser-body", children: [
4466
4654
  teaserText ? /* @__PURE__ */ jsx10(
4467
4655
  WorkflowMarkdownPreview,
4468
4656
  {
@@ -4549,7 +4737,7 @@ function workflowStageContainerPropsEqual(prev, next) {
4549
4737
  var WorkflowStageContainer = memo2(WorkflowStageContainerInner, workflowStageContainerPropsEqual);
4550
4738
 
4551
4739
  // src/view/workflow/WorkflowParallelGroup.tsx
4552
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
4740
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
4553
4741
  function resolveLoopTree(node, loopTreesBySessionId) {
4554
4742
  if (!node.loopSessionId) return void 0;
4555
4743
  return loopTreesBySessionId[node.loopSessionId];
@@ -4606,7 +4794,7 @@ function WorkflowParallelGroupInner({
4606
4794
  item.container_id
4607
4795
  );
4608
4796
  };
4609
- return /* @__PURE__ */ jsxs7(
4797
+ return /* @__PURE__ */ jsxs8(
4610
4798
  "div",
4611
4799
  {
4612
4800
  className: "lax-workflow-parallel-group",
@@ -4648,7 +4836,7 @@ function workflowParallelGroupPropsEqual(prev, next) {
4648
4836
  var WorkflowParallelGroup = memo3(WorkflowParallelGroupInner, workflowParallelGroupPropsEqual);
4649
4837
 
4650
4838
  // src/view/workflow/WorkflowRootContainer.tsx
4651
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
4839
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
4652
4840
  function WorkflowRootContainer({
4653
4841
  node,
4654
4842
  children,
@@ -4662,7 +4850,7 @@ function WorkflowRootContainer({
4662
4850
  const uiStatus = forcedCloseout ? "warning" : mapContainerUiStatus(nodeStatus);
4663
4851
  const statusLabel = mapWorkflowRootStatusLabel(nodeStatus, sessionStatus);
4664
4852
  const meta = containersById ? buildEffectiveWorkflowRootMeta(node, containersById) : buildWorkflowRootMeta(node);
4665
- return /* @__PURE__ */ jsxs8("div", { className: "lax-workflow-root", "data-testid": "lax-workflow-root", children: [
4853
+ return /* @__PURE__ */ jsxs9("div", { className: "lax-workflow-root", "data-testid": "lax-workflow-root", children: [
4666
4854
  /* @__PURE__ */ jsx12(
4667
4855
  WorkflowContainerLine,
4668
4856
  {
@@ -4684,7 +4872,7 @@ function WorkflowRootContainer({
4684
4872
 
4685
4873
  // src/view/workflow/WorkflowRouteContainer.tsx
4686
4874
  import { useCallback as useCallback4, useEffect as useEffect9, useMemo as useMemo5, useState as useState4 } from "react";
4687
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
4875
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
4688
4876
  function WorkflowRouteContainer({
4689
4877
  node,
4690
4878
  loopTree,
@@ -4721,7 +4909,7 @@ function WorkflowRouteContainer({
4721
4909
  "--lax-workflow-depth": depth,
4722
4910
  "--lax-workflow-indent-unit": workflowIndentUnit(depth)
4723
4911
  };
4724
- return /* @__PURE__ */ jsxs9(
4912
+ return /* @__PURE__ */ jsxs10(
4725
4913
  "div",
4726
4914
  {
4727
4915
  className: `lax-workflow-route-branch lax-workflow-stage-container lax-workflow-stage-container--${uiStatus}${isSkipped ? " lax-workflow-route-branch--skipped" : ""}`,
@@ -4785,7 +4973,7 @@ function WorkflowRouteContainer({
4785
4973
 
4786
4974
  // src/view/workflow/WorkflowSubworkflowContainer.tsx
4787
4975
  import { useCallback as useCallback5, useEffect as useEffect10, useMemo as useMemo6, useState as useState5 } from "react";
4788
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
4976
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
4789
4977
  function buildDoneSummary(node) {
4790
4978
  const display = node.display;
4791
4979
  if (display?.progress_total != null) {
@@ -4828,7 +5016,7 @@ function WorkflowSubworkflowContainer({
4828
5016
  "--lax-workflow-indent-unit": workflowIndentUnit(depth)
4829
5017
  };
4830
5018
  const showShellLine = !(effectivelyDone && !expanded);
4831
- return /* @__PURE__ */ jsxs10(
5019
+ return /* @__PURE__ */ jsxs11(
4832
5020
  "div",
4833
5021
  {
4834
5022
  className: "lax-workflow-subworkflow",
@@ -4884,7 +5072,7 @@ function WorkflowSubworkflowContainer({
4884
5072
  }
4885
5073
 
4886
5074
  // src/view/workflow/WorkflowChrome.tsx
4887
- import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
5075
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
4888
5076
  function collectChildContainers(containersById, parentId) {
4889
5077
  return Object.values(containersById).filter((node) => node.parent_container_id === parentId).sort((a, b) => a.scope_key.localeCompare(b.scope_key));
4890
5078
  }
@@ -4967,7 +5155,7 @@ function renderContainerChildren(parentId, containerTree, loopTreesBySessionId,
4967
5155
  const children = collectChildContainers(containerTree.containersById, parentId);
4968
5156
  const items = children.filter((node) => node.scope === "item");
4969
5157
  const rest = children.filter((node) => node.scope !== "item");
4970
- return /* @__PURE__ */ jsxs11(Fragment2, { children: [
5158
+ return /* @__PURE__ */ jsxs12(Fragment3, { children: [
4971
5159
  renderItemSiblings(items, parentId, depth, containerTree, loopTreesBySessionId, viewProps),
4972
5160
  rest.map(
4973
5161
  (node) => renderContainerNode(node, containerTree, loopTreesBySessionId, viewProps, depth, ancestorVisited)
@@ -5006,7 +5194,7 @@ function renderContainerNode(node, containerTree, loopTreesBySessionId, viewProp
5006
5194
  }
5007
5195
  if (node.scope === "stage") {
5008
5196
  const suppressHostScopedLoop = stageHasSubworkflowChild(node.container_id, containerTree);
5009
- return /* @__PURE__ */ jsxs11(
5197
+ return /* @__PURE__ */ jsxs12(
5010
5198
  "div",
5011
5199
  {
5012
5200
  className: "lax-workflow-nested-block",
@@ -5027,7 +5215,7 @@ function renderContainerNode(node, containerTree, loopTreesBySessionId, viewProp
5027
5215
  );
5028
5216
  }
5029
5217
  if (node.scope === "aggregate" || node.scope === "branch") {
5030
- return /* @__PURE__ */ jsxs11("div", { className: "lax-workflow-container-children", children: [
5218
+ return /* @__PURE__ */ jsxs12("div", { className: "lax-workflow-container-children", children: [
5031
5219
  renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, viewProps),
5032
5220
  hasNested ? nested : null
5033
5221
  ] }, node.container_id);
@@ -5068,11 +5256,11 @@ function WorkflowStageListPanel({
5068
5256
  scale.completedStagePageSize,
5069
5257
  partition.totalDoneCount - partition.visibleDoneCount
5070
5258
  );
5071
- return /* @__PURE__ */ jsxs11(Fragment2, { children: [
5259
+ return /* @__PURE__ */ jsxs12(Fragment3, { children: [
5072
5260
  /* @__PURE__ */ jsx15("ol", { className: "lax-workflow-stage-list", "data-testid": "lax-workflow-stage-list", children: partition.visible.map(
5073
5261
  (node) => renderWorkflowStageRow(node, containerTree, loopTreesBySessionId, viewProps)
5074
5262
  ) }),
5075
- partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs11(
5263
+ partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs12(
5076
5264
  "button",
5077
5265
  {
5078
5266
  type: "button",
@@ -5098,7 +5286,7 @@ function WorkflowStageListPanel({
5098
5286
  children: "Show fewer completed stages"
5099
5287
  }
5100
5288
  ) : null,
5101
- partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs11(
5289
+ partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs12(
5102
5290
  "p",
5103
5291
  {
5104
5292
  className: "lax-workflow-stage-list__hidden-hint",
@@ -5123,7 +5311,7 @@ function renderWorkflowChildren(workflowNode, containerTree, loopTreesBySessionI
5123
5311
  const other = children.filter(
5124
5312
  (node) => node.scope !== "stage" && node.scope !== "item"
5125
5313
  );
5126
- return /* @__PURE__ */ jsxs11(Fragment2, { children: [
5314
+ return /* @__PURE__ */ jsxs12(Fragment3, { children: [
5127
5315
  stages.length > 0 ? /* @__PURE__ */ jsx15(
5128
5316
  WorkflowStageListPanel,
5129
5317
  {
@@ -5177,7 +5365,7 @@ function WorkflowChrome({
5177
5365
 
5178
5366
  // src/view/workflow/WorkflowGlobalSpinner.tsx
5179
5367
  import { useStore as useStore5 } from "zustand";
5180
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
5368
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
5181
5369
  function WorkflowGlobalSpinner() {
5182
5370
  const store = useWorkflowSessionStoreApi();
5183
5371
  const show = useStore5(store, (s) => shouldShowGlobalSpinner(s.state));
@@ -5188,7 +5376,7 @@ function WorkflowGlobalSpinner() {
5188
5376
  className: "lax-workflow-global-spinner lax-spinner-container",
5189
5377
  "data-testid": "lax-workflow-global-spinner",
5190
5378
  role: "status",
5191
- children: /* @__PURE__ */ jsxs12("span", { className: "lax-spinner lax-workflow-global-spinner__inner", children: [
5379
+ children: /* @__PURE__ */ jsxs13("span", { className: "lax-spinner lax-workflow-global-spinner__inner", children: [
5192
5380
  /* @__PURE__ */ jsx16("span", { className: "lax-spinner-frame", children: "\u280B" }),
5193
5381
  /* @__PURE__ */ jsx16("span", { className: "lax-spinner-verb lax-spinner-verb--shimmer", children: "Workflow" })
5194
5382
  ] })
@@ -5197,7 +5385,7 @@ function WorkflowGlobalSpinner() {
5197
5385
  }
5198
5386
 
5199
5387
  // src/view/workflow/WorkflowSessionBanners.tsx
5200
- import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
5388
+ import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
5201
5389
  function WorkflowSessionBanners({ state }) {
5202
5390
  const banner = resolveWorkflowBanner(state);
5203
5391
  const { display } = state;
@@ -5205,7 +5393,7 @@ function WorkflowSessionBanners({ state }) {
5205
5393
  return null;
5206
5394
  }
5207
5395
  const showTransportError = (display.projectionStatus === "error" || state.internalErrors.length > 0) && banner.kind !== "projection_degraded" && banner.kind !== "authority_error";
5208
- return /* @__PURE__ */ jsxs13(Fragment3, { children: [
5396
+ return /* @__PURE__ */ jsxs14(Fragment4, { children: [
5209
5397
  banner.kind === "authority_error" ? /* @__PURE__ */ jsx17(
5210
5398
  "div",
5211
5399
  {
@@ -5224,6 +5412,15 @@ function WorkflowSessionBanners({ state }) {
5224
5412
  children: banner.message
5225
5413
  }
5226
5414
  ) : null,
5415
+ banner.kind === "loop_display_truncated" ? /* @__PURE__ */ jsx17(
5416
+ "div",
5417
+ {
5418
+ className: "lax-workflow-banner lax-workflow-banner--loop-truncated",
5419
+ "data-testid": "lax-workflow-loop-truncated-banner",
5420
+ role: "status",
5421
+ children: banner.message
5422
+ }
5423
+ ) : null,
5227
5424
  banner.kind === "snapshot_unavailable" ? /* @__PURE__ */ jsx17(
5228
5425
  "div",
5229
5426
  {
@@ -5242,7 +5439,7 @@ function WorkflowSessionBanners({ state }) {
5242
5439
  children: banner.message
5243
5440
  }
5244
5441
  ) : null,
5245
- banner.kind === "forced_closeout" || display.projectionStatus === "done_with_warning" ? /* @__PURE__ */ jsxs13(
5442
+ banner.kind === "forced_closeout" || display.projectionStatus === "done_with_warning" ? /* @__PURE__ */ jsxs14(
5246
5443
  "div",
5247
5444
  {
5248
5445
  className: "lax-workflow-closeout-warning",
@@ -5250,7 +5447,7 @@ function WorkflowSessionBanners({ state }) {
5250
5447
  role: "status",
5251
5448
  children: [
5252
5449
  /* @__PURE__ */ jsx17("strong", { children: "Workflow forced closeout" }),
5253
- /* @__PURE__ */ jsxs13("span", { children: [
5450
+ /* @__PURE__ */ jsxs14("span", { children: [
5254
5451
  " ",
5255
5452
  "\u2014 stream ended with unresolved child scopes; not a healthy completion."
5256
5453
  ] }),
@@ -5361,9 +5558,9 @@ function loopSessionDisplayLabel(loopSessionId) {
5361
5558
  }
5362
5559
 
5363
5560
  // src/view/workflow/WorkflowTaskListFooter.tsx
5364
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
5561
+ import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
5365
5562
  function MessageResponse({ children }) {
5366
- return /* @__PURE__ */ jsxs14("div", { className: "lax-message-response", children: [
5563
+ return /* @__PURE__ */ jsxs15("div", { className: "lax-message-response", children: [
5367
5564
  /* @__PURE__ */ jsx18("span", { className: "lax-message-response__marker", children: "\u23BF " }),
5368
5565
  /* @__PURE__ */ jsx18("span", { className: "lax-message-response__content", children })
5369
5566
  ] });
@@ -5379,8 +5576,8 @@ function WorkflowTaskListFooter() {
5379
5576
  }
5380
5577
  const label = formatWorkflowTaskFooterLabel(state);
5381
5578
  const groups = groupWorkflowTasksByLoop(footerState.tasks);
5382
- return /* @__PURE__ */ jsxs14("div", { className: "lax-workflow-task-list-footer", "data-testid": "lax-workflow-task-list-footer", children: [
5383
- /* @__PURE__ */ jsxs14(
5579
+ return /* @__PURE__ */ jsxs15("div", { className: "lax-workflow-task-list-footer", "data-testid": "lax-workflow-task-list-footer", children: [
5580
+ /* @__PURE__ */ jsxs15(
5384
5581
  "button",
5385
5582
  {
5386
5583
  type: "button",
@@ -5394,7 +5591,7 @@ function WorkflowTaskListFooter() {
5394
5591
  ]
5395
5592
  }
5396
5593
  ),
5397
- expanded ? /* @__PURE__ */ jsx18(MessageResponse, { children: /* @__PURE__ */ jsx18("div", { className: "lax-workflow-task-list-groups", children: groups.map((group) => /* @__PURE__ */ jsxs14(
5594
+ expanded ? /* @__PURE__ */ jsx18(MessageResponse, { children: /* @__PURE__ */ jsx18("div", { className: "lax-workflow-task-list-groups", children: groups.map((group) => /* @__PURE__ */ jsxs15(
5398
5595
  "div",
5399
5596
  {
5400
5597
  className: "lax-workflow-task-list-group",
@@ -5411,7 +5608,7 @@ function WorkflowTaskListFooter() {
5411
5608
 
5412
5609
  // src/view/workflow/WorkflowSession.tsx
5413
5610
  import { useStore as useStore7 } from "zustand";
5414
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
5611
+ import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
5415
5612
  function WorkflowSession({
5416
5613
  source,
5417
5614
  authoritySource,
@@ -5535,7 +5732,7 @@ function WorkflowSession({
5535
5732
  streamAbortRef.current?.abort();
5536
5733
  }
5537
5734
  }, [authorityStatus]);
5538
- return /* @__PURE__ */ jsx19(WorkflowSessionStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx19(WorkflowScaleContext.Provider, { value: scaleOptions, children: /* @__PURE__ */ jsx19(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx19(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx19(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx19(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx19(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs15(
5735
+ return /* @__PURE__ */ jsx19(WorkflowSessionStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx19(WorkflowScaleContext.Provider, { value: scaleOptions, children: /* @__PURE__ */ jsx19(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx19(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx19(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx19(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx19(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs16(
5539
5736
  "div",
5540
5737
  {
5541
5738
  className: "lax-agent-session lax-workflow-session",
@@ -5772,14 +5969,14 @@ function buildTimelineEntries(rootIds, byId, options = {}) {
5772
5969
  }
5773
5970
 
5774
5971
  // src/view/nodes/SubAgentNode.tsx
5775
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
5972
+ import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
5776
5973
  function SubAgentBlock({ nodeId }) {
5777
5974
  const node = useNodeTyped(nodeId, "subagent");
5778
5975
  const childIds = useChildren(nodeId);
5779
5976
  const registry = useNodeRegistry();
5780
5977
  if (!node) return null;
5781
- return /* @__PURE__ */ jsxs16("div", { className: "lax-subagent-block", "data-status": node.status, children: [
5782
- /* @__PURE__ */ jsxs16("div", { className: "lax-subagent-block__header", children: [
5978
+ return /* @__PURE__ */ jsxs17("div", { className: "lax-subagent-block", "data-status": node.status, children: [
5979
+ /* @__PURE__ */ jsxs17("div", { className: "lax-subagent-block__header", children: [
5783
5980
  "SubAgent: ",
5784
5981
  node.subagentId
5785
5982
  ] }),