langchain_agentx_stream_ui 0.6.2 → 0.6.8

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 (37) hide show
  1. package/dist/{AskUserQuestionToolBody-J8MqSUEs.d.ts → AskUserQuestionToolBody-CutLY-dS.d.ts} +2 -2
  2. package/dist/{MarkdownBlock-DeICJUtu.d.ts → MarkdownBlock-Hf-kcLh6.d.ts} +1 -1
  3. package/dist/{chunk-DP7V33X7.js → chunk-6Z4ZF36Z.js} +1 -1
  4. package/dist/chunk-6Z4ZF36Z.js.map +1 -0
  5. package/dist/{chunk-ZR37OKV6.js → chunk-7XSQYG3X.js} +6 -5
  6. package/dist/chunk-7XSQYG3X.js.map +1 -0
  7. package/dist/{chunk-4TG4TGIL.js → chunk-B2NG5KGH.js} +4 -4
  8. package/dist/chunk-B2NG5KGH.js.map +1 -0
  9. package/dist/{chunk-4RIOBLGB.js → chunk-GBY5DJ7L.js} +1 -1
  10. package/dist/chunk-GBY5DJ7L.js.map +1 -0
  11. package/dist/{chunk-DJPNJBFL.js → chunk-UBYOUDQE.js} +204 -9
  12. package/dist/chunk-UBYOUDQE.js.map +1 -0
  13. package/dist/{chunk-ED67MXSG.js → chunk-VBUKRPEO.js} +19 -9
  14. package/dist/chunk-VBUKRPEO.js.map +1 -0
  15. package/dist/default.css +12 -0
  16. package/dist/index.d.ts +45 -16
  17. package/dist/index.js +418 -92
  18. package/dist/index.js.map +1 -1
  19. package/dist/{multi-session-B7kIVfyG.d.ts → multi-session-t7dWsecW.d.ts} +32 -3
  20. package/dist/multi-session.d.ts +4 -4
  21. package/dist/multi-session.js +6 -6
  22. package/dist/{nodes-7_0rCAc4.d.ts → nodes-C2rGC3J7.d.ts} +5 -0
  23. package/dist/{sessionViewOptions-EIBugOkf.d.ts → sessionViewOptions-2Hv9BGeg.d.ts} +1 -1
  24. package/dist/tools-presentation.d.ts +3 -3
  25. package/dist/tools-presentation.js +2 -2
  26. package/dist/tools.d.ts +5 -5
  27. package/dist/tools.js +4 -4
  28. package/dist/{types-DAkw2VOv.d.ts → types-kH4WQSM2.d.ts} +1 -1
  29. package/dist/virtual.d.ts +1 -1
  30. package/dist/virtual.js +3 -3
  31. package/package.json +1 -1
  32. package/dist/chunk-4RIOBLGB.js.map +0 -1
  33. package/dist/chunk-4TG4TGIL.js.map +0 -1
  34. package/dist/chunk-DJPNJBFL.js.map +0 -1
  35. package/dist/chunk-DP7V33X7.js.map +0 -1
  36. package/dist/chunk-ED67MXSG.js.map +0 -1
  37. package/dist/chunk-ZR37OKV6.js.map +0 -1
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-VBUKRPEO.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-B2NG5KGH.js";
36
+ import "./chunk-6Z4ZF36Z.js";
37
37
  import {
38
38
  CollapseKind,
39
39
  CollapsedExploreNode,
@@ -79,6 +79,7 @@ import {
79
79
  getChildIds,
80
80
  getDisplayProjectionStatus,
81
81
  getMainStageIds,
82
+ isAgentInputLocked,
82
83
  isAuthorityTerminal,
83
84
  isAuthorityTerminalStatus,
84
85
  isGitOperationCommand,
@@ -119,7 +120,7 @@ import {
119
120
  useSessionStatus,
120
121
  useSessionViewOptions,
121
122
  useTimeline
122
- } from "./chunk-DJPNJBFL.js";
123
+ } from "./chunk-UBYOUDQE.js";
123
124
  import {
124
125
  AGENT_PROGRESS_GUTTER,
125
126
  AGENT_PROGRESS_INDENT,
@@ -149,7 +150,7 @@ import {
149
150
  createDefaultToolRegistry,
150
151
  formatAgentTitle,
151
152
  formatExploreSummaryText
152
- } from "./chunk-ZR37OKV6.js";
153
+ } from "./chunk-7XSQYG3X.js";
153
154
  import {
154
155
  BodyBlockList,
155
156
  DiffView,
@@ -165,7 +166,7 @@ import {
165
166
  formatTimeoutFooter,
166
167
  parseDiffText,
167
168
  truncateCommand
168
- } from "./chunk-4RIOBLGB.js";
169
+ } from "./chunk-GBY5DJ7L.js";
169
170
 
170
171
  // src/view/AgentSession.tsx
171
172
  import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "react";
@@ -839,6 +840,221 @@ function hydrateAskFromSnapshot(state, snapshot) {
839
840
  };
840
841
  }
841
842
 
843
+ // src/core/sessionTreeSoftTrim.ts
844
+ var DEFAULT_MAX_RUNNING_LOOP_NODES = 5e3;
845
+ var DEFAULT_RETAIN_RUNNING_LOOP_NODES = 3500;
846
+ function isProtectedNode(node) {
847
+ switch (node.kind) {
848
+ case "tool_call":
849
+ case "subagent":
850
+ return node.status === "running";
851
+ case "permission":
852
+ return node.status === "pending";
853
+ case "text":
854
+ case "reasoning":
855
+ return node.status === "streaming";
856
+ case "step":
857
+ return node.status === "running";
858
+ default:
859
+ return false;
860
+ }
861
+ }
862
+ function collectSubtreeIds(rootId, byId) {
863
+ const out = [];
864
+ const stack = [rootId];
865
+ while (stack.length > 0) {
866
+ const id = stack.pop();
867
+ if (!byId[id]) continue;
868
+ out.push(id);
869
+ const node = byId[id];
870
+ if (node.kind === "subagent") {
871
+ for (const childId of node.childIds) {
872
+ stack.push(childId);
873
+ }
874
+ }
875
+ }
876
+ return out;
877
+ }
878
+ function collectProtectedIds(tree) {
879
+ const protectedIds = /* @__PURE__ */ new Set();
880
+ for (const [id, node] of Object.entries(tree.byId)) {
881
+ if (isProtectedNode(node)) {
882
+ protectedIds.add(id);
883
+ }
884
+ }
885
+ for (const [id, node] of Object.entries(tree.byId)) {
886
+ if (node.kind !== "subagent") continue;
887
+ for (const childId of collectSubtreeIds(id, tree.byId)) {
888
+ if (childId !== id && protectedIds.has(childId)) {
889
+ protectedIds.add(id);
890
+ break;
891
+ }
892
+ }
893
+ }
894
+ return protectedIds;
895
+ }
896
+ function subtreeContainsProtected(rootId, byId, protectedIds) {
897
+ for (const id of collectSubtreeIds(rootId, byId)) {
898
+ if (protectedIds.has(id)) return true;
899
+ }
900
+ return false;
901
+ }
902
+ function filterActiveToolByStepName(active, byId) {
903
+ const next = {};
904
+ for (const [key, nodeId] of Object.entries(active)) {
905
+ if (byId[nodeId]) next[key] = nodeId;
906
+ }
907
+ return next;
908
+ }
909
+ function trimSessionTreeSoft(tree, options) {
910
+ const maxNodes = Math.max(
911
+ 1,
912
+ options?.maxRunningLoopNodes ?? DEFAULT_MAX_RUNNING_LOOP_NODES
913
+ );
914
+ const retainTarget = Math.min(
915
+ maxNodes,
916
+ Math.max(
917
+ 1,
918
+ options?.retainRunningLoopNodes ?? DEFAULT_RETAIN_RUNNING_LOOP_NODES
919
+ )
920
+ );
921
+ const nodeCount = Object.keys(tree.byId).length;
922
+ if (nodeCount <= maxNodes) {
923
+ return tree;
924
+ }
925
+ const protectedIds = collectProtectedIds(tree);
926
+ const dropIds = /* @__PURE__ */ new Set();
927
+ const dropRootIds = /* @__PURE__ */ new Set();
928
+ let projectedCount = nodeCount;
929
+ for (const rootId of tree.rootChildren) {
930
+ if (projectedCount <= retainTarget) break;
931
+ if (!tree.byId[rootId] || dropIds.has(rootId)) continue;
932
+ if (subtreeContainsProtected(rootId, tree.byId, protectedIds)) continue;
933
+ const subtree = collectSubtreeIds(rootId, tree.byId);
934
+ for (const id of subtree) {
935
+ dropIds.add(id);
936
+ }
937
+ dropRootIds.add(rootId);
938
+ projectedCount -= subtree.length;
939
+ }
940
+ if (dropIds.size === 0) {
941
+ return tree;
942
+ }
943
+ const nextById = {};
944
+ for (const [id, node] of Object.entries(tree.byId)) {
945
+ if (!dropIds.has(id)) {
946
+ nextById[id] = node;
947
+ }
948
+ }
949
+ const rootChildren = tree.rootChildren.filter((id) => !dropRootIds.has(id));
950
+ const dropped = dropIds.size;
951
+ return {
952
+ ...tree,
953
+ byId: nextById,
954
+ rootChildren,
955
+ openSubAgentStack: tree.openSubAgentStack.filter((id) => Boolean(nextById[id])),
956
+ activeToolByStepName: filterActiveToolByStepName(
957
+ tree.activeToolByStepName,
958
+ nextById
959
+ ),
960
+ meta: {
961
+ ...tree.meta,
962
+ displayTruncated: true,
963
+ displayTruncatedDropped: (tree.meta.displayTruncatedDropped ?? 0) + dropped
964
+ }
965
+ };
966
+ }
967
+ function trimWorkflowLoopTreesSoft(state, options) {
968
+ const nextLoops = {};
969
+ let anyTruncated = Boolean(state.meta.loopDisplayTruncated);
970
+ let changed = false;
971
+ for (const [sessionId, tree] of Object.entries(state.loopTreesBySessionId)) {
972
+ const trimmed = trimSessionTreeSoft(tree, options);
973
+ nextLoops[sessionId] = trimmed;
974
+ if (trimmed !== tree) {
975
+ changed = true;
976
+ }
977
+ if (trimmed.meta.displayTruncated) {
978
+ anyTruncated = true;
979
+ }
980
+ }
981
+ if (!changed && anyTruncated === Boolean(state.meta.loopDisplayTruncated)) {
982
+ return state;
983
+ }
984
+ return {
985
+ ...state,
986
+ loopTreesBySessionId: changed ? nextLoops : state.loopTreesBySessionId,
987
+ meta: {
988
+ ...state.meta,
989
+ loopDisplayTruncated: anyTruncated
990
+ }
991
+ };
992
+ }
993
+
994
+ // src/core/sessionTreeAuthorityFinalize.ts
995
+ function finalizeSessionTreeOnAuthorityCanceled(tree, endedAtMs = Date.now()) {
996
+ const byId = { ...tree.byId };
997
+ let changed = false;
998
+ for (const [id, node] of Object.entries(byId)) {
999
+ if (!node) continue;
1000
+ if ((node.kind === "text" || node.kind === "reasoning") && node.status === "streaming") {
1001
+ byId[id] = { ...node, status: "done" };
1002
+ changed = true;
1003
+ continue;
1004
+ }
1005
+ if (node.kind !== "tool_call") continue;
1006
+ let next = node;
1007
+ if (node.status === "running") {
1008
+ next = {
1009
+ ...next,
1010
+ status: "failed",
1011
+ endedAt: node.endedAt ?? endedAtMs
1012
+ };
1013
+ changed = true;
1014
+ }
1015
+ if (next.subagentSessions) {
1016
+ const sessions = { ...next.subagentSessions };
1017
+ let sessChanged = false;
1018
+ for (const [agentId, marker] of Object.entries(sessions)) {
1019
+ if (marker.status === "running") {
1020
+ sessions[agentId] = {
1021
+ ...marker,
1022
+ status: "done",
1023
+ endedAt: endedAtMs
1024
+ };
1025
+ sessChanged = true;
1026
+ }
1027
+ }
1028
+ if (sessChanged) {
1029
+ next = { ...next, subagentSessions: sessions };
1030
+ changed = true;
1031
+ }
1032
+ }
1033
+ if (next.subagentProgress?.some((e) => e.status === "running")) {
1034
+ next = {
1035
+ ...next,
1036
+ subagentProgress: next.subagentProgress.map(
1037
+ (entry) => entry.status === "running" ? { ...entry, status: "error" } : entry
1038
+ )
1039
+ };
1040
+ changed = true;
1041
+ }
1042
+ if (next !== node) {
1043
+ byId[id] = next;
1044
+ }
1045
+ }
1046
+ if (!changed) {
1047
+ return tree;
1048
+ }
1049
+ return {
1050
+ ...tree,
1051
+ byId,
1052
+ activeRoundId: null,
1053
+ // 展示树终态;不升格 / 不写 REST authority(P-AUTH)
1054
+ status: tree.status === "done" || tree.status === "done_with_warning" ? tree.status : "error"
1055
+ };
1056
+ }
1057
+
842
1058
  // src/core/store.ts
843
1059
  function safeReduce(tree, event, eventIndex, options) {
844
1060
  try {
@@ -869,6 +1085,10 @@ function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
869
1085
  const initialEventCount = storeOptions?.initialEventCount ?? 0;
870
1086
  const seenEventIds = new Set(storeOptions?.initialSeenEventIds);
871
1087
  const authorityBindingActive = storeOptions?.authorityBindingActive === true;
1088
+ const softTrimOpts = {
1089
+ maxRunningLoopNodes: storeOptions?.maxRunningLoopNodes ?? DEFAULT_MAX_RUNNING_LOOP_NODES,
1090
+ retainRunningLoopNodes: storeOptions?.retainRunningLoopNodes ?? DEFAULT_RETAIN_RUNNING_LOOP_NODES
1091
+ };
872
1092
  return createStore((set, get) => ({
873
1093
  tree: initialTree,
874
1094
  eventCount: initialEventCount,
@@ -886,10 +1106,11 @@ function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
886
1106
  if (isAtOrBeforeProjectionCursor(projectionCursor, sseEventId)) return;
887
1107
  const { tree, eventCount } = get();
888
1108
  const { tree: reduced } = safeReduce(tree, event, eventCount, reduceOpts);
1109
+ const trimmed = trimSessionTreeSoft(reduced, softTrimOpts);
889
1110
  const nextTree = sseEventId ? {
890
- ...reduced,
891
- meta: { ...reduced.meta, lastEventId: sseEventId }
892
- } : reduced;
1111
+ ...trimmed,
1112
+ meta: { ...trimmed.meta, lastEventId: sseEventId }
1113
+ } : trimmed;
893
1114
  markEventIdSeen(seenEventIds, sseEventId);
894
1115
  set({ tree: nextTree, eventCount: eventCount + 1 });
895
1116
  },
@@ -897,13 +1118,18 @@ function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
897
1118
  let { tree, eventCount } = get();
898
1119
  for (const event of events) {
899
1120
  const result = safeReduce(tree, event, eventCount, reduceOpts);
900
- tree = result.tree;
1121
+ tree = trimSessionTreeSoft(result.tree, softTrimOpts);
901
1122
  eventCount += 1;
902
1123
  }
903
1124
  set({ tree, eventCount });
904
1125
  },
905
1126
  applyAuthoritySnapshot(snapshot) {
906
- set({ authority: snapshot });
1127
+ const current = get();
1128
+ let tree = current.tree;
1129
+ if (snapshot.status === "canceled") {
1130
+ tree = finalizeSessionTreeOnAuthorityCanceled(tree);
1131
+ }
1132
+ set({ authority: snapshot, tree });
907
1133
  },
908
1134
  setAuthoritySubscribeFailed(failed) {
909
1135
  set({ authoritySubscribeFailed: failed });
@@ -912,7 +1138,10 @@ function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
912
1138
  const patch = hydrateAskFromSnapshot(get(), snapshot);
913
1139
  seenEventIds.clear();
914
1140
  markEventIdSeen(seenEventIds, snapshot.cursor);
915
- set(patch);
1141
+ set({
1142
+ ...patch,
1143
+ tree: trimSessionTreeSoft(patch.tree, softTrimOpts)
1144
+ });
916
1145
  },
917
1146
  markSnapshotHydrateFailed() {
918
1147
  set({
@@ -1542,33 +1771,47 @@ function AgentLoopView({
1542
1771
  }
1543
1772
 
1544
1773
  // 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
- );
1774
+ import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
1775
+ function AgentSessionBanners({
1776
+ snapshotHydrateFailed,
1777
+ displayTruncated = false
1778
+ }) {
1779
+ if (!snapshotHydrateFailed && !displayTruncated) return null;
1780
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1781
+ snapshotHydrateFailed ? /* @__PURE__ */ jsx3(
1782
+ "div",
1783
+ {
1784
+ className: "lax-agent-banner lax-agent-banner--snapshot-unavailable",
1785
+ "data-testid": "lax-agent-snapshot-unavailable-banner",
1786
+ role: "status",
1787
+ children: "\u5C55\u793A\u6001\u4E0D\u53EF\u7528 / \u4F1A\u8BDD\u5DF2\u8FC7\u671F"
1788
+ }
1789
+ ) : null,
1790
+ displayTruncated ? /* @__PURE__ */ jsx3(
1791
+ "div",
1792
+ {
1793
+ className: "lax-agent-banner lax-agent-banner--display-truncated",
1794
+ "data-testid": "lax-agent-display-truncated-banner",
1795
+ role: "status",
1796
+ 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"
1797
+ }
1798
+ ) : null
1799
+ ] });
1557
1800
  }
1558
1801
 
1559
1802
  // src/view/AgentSession.tsx
1560
1803
  import { useStore } from "zustand";
1561
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
1804
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1562
1805
  function DebugPanel() {
1563
1806
  const status = useSessionStatus();
1564
1807
  const errors = useInternalErrors();
1565
1808
  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: [
1809
+ return /* @__PURE__ */ jsxs2("div", { className: "lax-debug-panel", "data-testid": "lax-debug-panel", children: [
1810
+ /* @__PURE__ */ jsxs2("div", { children: [
1568
1811
  "status: ",
1569
1812
  status
1570
1813
  ] }),
1571
- /* @__PURE__ */ jsx4("ul", { children: errors.map((err, i) => /* @__PURE__ */ jsxs("li", { children: [
1814
+ /* @__PURE__ */ jsx4("ul", { children: errors.map((err, i) => /* @__PURE__ */ jsxs2("li", { children: [
1572
1815
  "[",
1573
1816
  err.eventType,
1574
1817
  "] ",
@@ -1600,7 +1843,17 @@ function AgentSnapshotBootstrap({
1600
1843
  }
1601
1844
  }, [snapshotHydrateFailed, store]);
1602
1845
  const showBanner = useStore(store, (s) => s.snapshotHydrateFailed);
1603
- return /* @__PURE__ */ jsx4(AgentSessionBanners, { snapshotHydrateFailed: showBanner });
1846
+ const displayTruncated = useStore(
1847
+ store,
1848
+ (s) => Boolean(s.tree.meta.displayTruncated)
1849
+ );
1850
+ return /* @__PURE__ */ jsx4(
1851
+ AgentSessionBanners,
1852
+ {
1853
+ snapshotHydrateFailed: showBanner,
1854
+ displayTruncated
1855
+ }
1856
+ );
1604
1857
  }
1605
1858
  function AgentSession({
1606
1859
  source,
@@ -1710,7 +1963,7 @@ function AgentSession({
1710
1963
  controller.abort();
1711
1964
  };
1712
1965
  }, [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: [
1966
+ 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
1967
  /* @__PURE__ */ jsx4(
1715
1968
  AgentSnapshotBootstrap,
1716
1969
  {
@@ -1774,7 +2027,9 @@ var DEFAULT_WORKFLOW_SCALE_OPTIONS = {
1774
2027
  containerCountTier3: 120,
1775
2028
  tierEvalMinFrames: 30,
1776
2029
  hydrateChunkSize: 50,
1777
- aggressiveMemoryTier3: true
2030
+ aggressiveMemoryTier3: true,
2031
+ maxRunningLoopNodes: 5e3,
2032
+ retainRunningLoopNodes: 3500
1778
2033
  };
1779
2034
  function resolveWorkflowScale(storeOptions) {
1780
2035
  const scale = {
@@ -3198,6 +3453,38 @@ function safeReduceWorkflow(state, event, eventIndex, options) {
3198
3453
  };
3199
3454
  }
3200
3455
  }
3456
+ function finalizeWorkflowContainersOnAuthorityCanceled(state) {
3457
+ const containersById = {
3458
+ ...state.containerTree.containersById
3459
+ };
3460
+ let changed = false;
3461
+ for (const [id, node] of Object.entries(containersById)) {
3462
+ if (node.status !== "running" && node.status !== "pending") {
3463
+ continue;
3464
+ }
3465
+ const display = node.display ? { ...node.display, metrics_running: 0 } : node.display;
3466
+ containersById[id] = {
3467
+ ...node,
3468
+ status: "failed",
3469
+ is_open: false,
3470
+ display
3471
+ };
3472
+ changed = true;
3473
+ }
3474
+ if (!changed) {
3475
+ return state;
3476
+ }
3477
+ return {
3478
+ ...state,
3479
+ containerTree: {
3480
+ ...state.containerTree,
3481
+ containersById,
3482
+ activeContainerIds: state.containerTree.activeContainerIds.filter(
3483
+ (id) => containersById[id]?.is_open
3484
+ )
3485
+ }
3486
+ };
3487
+ }
3201
3488
  function finalizeWorkflowSessionOnStreamError(state, message, eventIndex, stack) {
3202
3489
  const containersById = {
3203
3490
  ...state.containerTree.containersById
@@ -3245,11 +3532,14 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
3245
3532
  let frameCounter = initialEventCount;
3246
3533
  let lastTierEvalFrame = 0;
3247
3534
  const finalizeState = (reduced, options) => {
3535
+ let next = trimWorkflowLoopTreesSoft(reduced, {
3536
+ maxRunningLoopNodes: scale.maxRunningLoopNodes,
3537
+ retainRunningLoopNodes: scale.retainRunningLoopNodes
3538
+ });
3248
3539
  if (!scale.evictCompletedLoops) {
3249
- return reduced;
3540
+ return next;
3250
3541
  }
3251
3542
  frameCounter += 1;
3252
- let next = reduced;
3253
3543
  ({ state: next, lastTierEvalFrame } = applyMemoryPressureTier(next, scale, {
3254
3544
  frameCounter,
3255
3545
  lastTierEvalFrame,
@@ -3334,17 +3624,33 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
3334
3624
  }));
3335
3625
  },
3336
3626
  applyAuthoritySnapshot(snapshot) {
3337
- set((current) => ({
3338
- state: {
3339
- ...current.state,
3340
- authority: snapshot,
3341
- display: {
3342
- ...current.state.display,
3343
- awaitingAuthority: false,
3344
- authoritySyncFailed: false
3627
+ set((current) => {
3628
+ let nextState = current.state;
3629
+ if (snapshot.status === "canceled") {
3630
+ nextState = finalizeWorkflowContainersOnAuthorityCanceled(nextState);
3631
+ const nextLoops = {
3632
+ ...nextState.loopTreesBySessionId
3633
+ };
3634
+ for (const [loopSessionId, loopTree] of Object.entries(nextLoops)) {
3635
+ nextLoops[loopSessionId] = finalizeSessionTreeOnAuthorityCanceled(loopTree);
3345
3636
  }
3637
+ nextState = {
3638
+ ...nextState,
3639
+ loopTreesBySessionId: nextLoops
3640
+ };
3346
3641
  }
3347
- }));
3642
+ return {
3643
+ state: {
3644
+ ...nextState,
3645
+ authority: snapshot,
3646
+ display: {
3647
+ ...nextState.display,
3648
+ awaitingAuthority: false,
3649
+ authoritySyncFailed: false
3650
+ }
3651
+ }
3652
+ };
3653
+ });
3348
3654
  },
3349
3655
  setAwaitingAuthority(value) {
3350
3656
  set((current) => ({
@@ -3572,7 +3878,7 @@ import { useCallback, useEffect as useEffect6, useMemo as useMemo2, useState, me
3572
3878
  import { useStore as useStore3 } from "zustand";
3573
3879
 
3574
3880
  // src/view/workflow/WorkflowContainerLine.tsx
3575
- import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
3881
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
3576
3882
  function WorkflowContainerLine({
3577
3883
  title,
3578
3884
  description,
@@ -3598,7 +3904,7 @@ function WorkflowContainerLine({
3598
3904
  onToggle && interactive !== false ? "lax-workflow-container-line--interactive" : "",
3599
3905
  className
3600
3906
  ].filter(Boolean).join(" ");
3601
- const content = /* @__PURE__ */ jsxs2(Fragment, { children: [
3907
+ const content = /* @__PURE__ */ jsxs3(Fragment2, { children: [
3602
3908
  showDot ? /* @__PURE__ */ jsx5(
3603
3909
  "span",
3604
3910
  {
@@ -3608,7 +3914,7 @@ function WorkflowContainerLine({
3608
3914
  }
3609
3915
  ) : null,
3610
3916
  /* @__PURE__ */ jsx5("span", { className: "lax-workflow-container-line__title", "data-testid": titleTestId, children: title }),
3611
- description ? /* @__PURE__ */ jsxs2(
3917
+ description ? /* @__PURE__ */ jsxs3(
3612
3918
  "span",
3613
3919
  {
3614
3920
  className: "lax-workflow-container-line__desc",
@@ -3680,7 +3986,7 @@ function hasTimelineNodes(tree) {
3680
3986
  }
3681
3987
 
3682
3988
  // src/view/workflow/workflowStageDoneBody.tsx
3683
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
3989
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
3684
3990
  function WorkflowStageDoneBody({
3685
3991
  loopTree,
3686
3992
  testId,
@@ -3717,7 +4023,7 @@ function WorkflowStageSummaryOnlyBody({
3717
4023
  bodyClassName = "lax-workflow-stage-container__body lax-workflow-stage-container__body--summary-only"
3718
4024
  }) {
3719
4025
  const expandHint = formatTeaserExpandHint(summary.extraLines, verbose);
3720
- return /* @__PURE__ */ jsxs3(
4026
+ return /* @__PURE__ */ jsxs4(
3721
4027
  "div",
3722
4028
  {
3723
4029
  className: bodyClassName,
@@ -3980,7 +4286,7 @@ function workflowContainerViewPropsEqual(prev, next) {
3980
4286
  }
3981
4287
 
3982
4288
  // src/view/workflow/WorkflowAggregateContainer.tsx
3983
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
4289
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
3984
4290
  function mergeContentPreview(node) {
3985
4291
  const blocks = node.content_blocks;
3986
4292
  if (blocks.length === 0) return "";
@@ -4054,7 +4360,7 @@ function WorkflowAggregateContainerInner({
4054
4360
  ) : null;
4055
4361
  const canExpandDone = isDone && (Boolean(loopTree) || Boolean(bodyMarkdown) || Boolean(node.loopSessionId));
4056
4362
  const doneExpandHint = expandHint ?? (canExpandDone ? "(click to expand)" : null);
4057
- return /* @__PURE__ */ jsxs4(
4363
+ return /* @__PURE__ */ jsxs5(
4058
4364
  "div",
4059
4365
  {
4060
4366
  className: `lax-workflow-aggregate lax-workflow-stage-container lax-workflow-stage-container--${isWaiting ? "pending" : isDone ? "done" : "running"}`,
@@ -4077,7 +4383,7 @@ function WorkflowAggregateContainerInner({
4077
4383
  descriptionTestId: `${testId}-desc`
4078
4384
  }
4079
4385
  ),
4080
- isWaiting && waitingTeaser ? /* @__PURE__ */ jsxs4(
4386
+ isWaiting && waitingTeaser ? /* @__PURE__ */ jsxs5(
4081
4387
  "div",
4082
4388
  {
4083
4389
  className: "lax-workflow-stage-container__done-teaser",
@@ -4119,7 +4425,7 @@ function WorkflowAggregateContainerInner({
4119
4425
  children: markdownPreview
4120
4426
  }
4121
4427
  ) : null,
4122
- isDone && !expanded && (bodyMarkdown || doneExpandHint) ? /* @__PURE__ */ jsx8("div", { "data-testid": `${testId}-done`, children: /* @__PURE__ */ jsxs4(
4428
+ isDone && !expanded && (bodyMarkdown || doneExpandHint) ? /* @__PURE__ */ jsx8("div", { "data-testid": `${testId}-done`, children: /* @__PURE__ */ jsxs5(
4123
4429
  "div",
4124
4430
  {
4125
4431
  className: "lax-workflow-stage-container__done-teaser",
@@ -4133,7 +4439,7 @@ function WorkflowAggregateContainerInner({
4133
4439
  children: bodyMarkdown ? AGENT_PROGRESS_GUTTER : AGENT_PROGRESS_INDENT
4134
4440
  }
4135
4441
  ),
4136
- /* @__PURE__ */ jsxs4("div", { className: "lax-workflow-stage-container__done-teaser-body", children: [
4442
+ /* @__PURE__ */ jsxs5("div", { className: "lax-workflow-stage-container__done-teaser-body", children: [
4137
4443
  bodyMarkdown ? /* @__PURE__ */ jsx8(
4138
4444
  WorkflowMarkdownPreview,
4139
4445
  {
@@ -4287,7 +4593,7 @@ function formatParallelGroupSummary(metrics) {
4287
4593
  }
4288
4594
 
4289
4595
  // src/view/workflow/WorkflowExpandTrigger.tsx
4290
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
4596
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
4291
4597
  function WorkflowExpandTrigger({
4292
4598
  summary,
4293
4599
  summaryTestId,
@@ -4307,9 +4613,9 @@ function WorkflowExpandTrigger({
4307
4613
  onClick: onToggle,
4308
4614
  "aria-expanded": expanded,
4309
4615
  "data-testid": testId,
4310
- children: /* @__PURE__ */ jsxs5("span", { className: "lax-workflow-expand-trigger__hang", children: [
4616
+ children: /* @__PURE__ */ jsxs6("span", { className: "lax-workflow-expand-trigger__hang", children: [
4311
4617
  /* @__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: [
4618
+ /* @__PURE__ */ jsxs6("span", { className: "lax-workflow-expand-trigger__body", children: [
4313
4619
  /* @__PURE__ */ jsx9(
4314
4620
  "span",
4315
4621
  {
@@ -4336,7 +4642,7 @@ function WorkflowExpandTrigger({
4336
4642
  // src/view/workflow/WorkflowStageContainer.tsx
4337
4643
  import { useCallback as useCallback2, useEffect as useEffect7, useMemo as useMemo3, useState as useState2, memo as memo2 } from "react";
4338
4644
  import { useStore as useStore4 } from "zustand";
4339
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
4645
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
4340
4646
  function containerTestId(node) {
4341
4647
  if (node.scope === "item") return `lax-workflow-parallel-${node.scope_key}`;
4342
4648
  if (node.scope === "stage") return `lax-workflow-stage-${node.scope_key}`;
@@ -4417,7 +4723,7 @@ function WorkflowStageContainerInner({
4417
4723
  const statusLabel = effectivelyDone ? doneLabel : suppressHostScopedLoop && effectivelyRunning ? "running" : mapContainerStatusLabel(effectivelyRunning ? "running" : node.status);
4418
4724
  const canToggleShell = effectivelyRunning && !isSkipped || effectivelyDone;
4419
4725
  const shellToggle = canToggleShell ? toggleExpanded : void 0;
4420
- return /* @__PURE__ */ jsxs6(
4726
+ return /* @__PURE__ */ jsxs7(
4421
4727
  "div",
4422
4728
  {
4423
4729
  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 +4754,7 @@ function WorkflowStageContainerInner({
4448
4754
  className: isItem ? "lax-workflow-parallel-item__header" : void 0
4449
4755
  }
4450
4756
  ),
4451
- effectivelyDone && !expanded && (teaserText || expandHint) ? /* @__PURE__ */ jsxs6(
4757
+ effectivelyDone && !expanded && (teaserText || expandHint) ? /* @__PURE__ */ jsxs7(
4452
4758
  "div",
4453
4759
  {
4454
4760
  className: "lax-workflow-stage-container__done-teaser",
@@ -4462,7 +4768,7 @@ function WorkflowStageContainerInner({
4462
4768
  children: teaserText ? AGENT_PROGRESS_GUTTER : AGENT_PROGRESS_INDENT
4463
4769
  }
4464
4770
  ),
4465
- /* @__PURE__ */ jsxs6("div", { className: "lax-workflow-stage-container__done-teaser-body", children: [
4771
+ /* @__PURE__ */ jsxs7("div", { className: "lax-workflow-stage-container__done-teaser-body", children: [
4466
4772
  teaserText ? /* @__PURE__ */ jsx10(
4467
4773
  WorkflowMarkdownPreview,
4468
4774
  {
@@ -4549,7 +4855,7 @@ function workflowStageContainerPropsEqual(prev, next) {
4549
4855
  var WorkflowStageContainer = memo2(WorkflowStageContainerInner, workflowStageContainerPropsEqual);
4550
4856
 
4551
4857
  // src/view/workflow/WorkflowParallelGroup.tsx
4552
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
4858
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
4553
4859
  function resolveLoopTree(node, loopTreesBySessionId) {
4554
4860
  if (!node.loopSessionId) return void 0;
4555
4861
  return loopTreesBySessionId[node.loopSessionId];
@@ -4606,7 +4912,7 @@ function WorkflowParallelGroupInner({
4606
4912
  item.container_id
4607
4913
  );
4608
4914
  };
4609
- return /* @__PURE__ */ jsxs7(
4915
+ return /* @__PURE__ */ jsxs8(
4610
4916
  "div",
4611
4917
  {
4612
4918
  className: "lax-workflow-parallel-group",
@@ -4648,7 +4954,7 @@ function workflowParallelGroupPropsEqual(prev, next) {
4648
4954
  var WorkflowParallelGroup = memo3(WorkflowParallelGroupInner, workflowParallelGroupPropsEqual);
4649
4955
 
4650
4956
  // src/view/workflow/WorkflowRootContainer.tsx
4651
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
4957
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
4652
4958
  function WorkflowRootContainer({
4653
4959
  node,
4654
4960
  children,
@@ -4662,7 +4968,7 @@ function WorkflowRootContainer({
4662
4968
  const uiStatus = forcedCloseout ? "warning" : mapContainerUiStatus(nodeStatus);
4663
4969
  const statusLabel = mapWorkflowRootStatusLabel(nodeStatus, sessionStatus);
4664
4970
  const meta = containersById ? buildEffectiveWorkflowRootMeta(node, containersById) : buildWorkflowRootMeta(node);
4665
- return /* @__PURE__ */ jsxs8("div", { className: "lax-workflow-root", "data-testid": "lax-workflow-root", children: [
4971
+ return /* @__PURE__ */ jsxs9("div", { className: "lax-workflow-root", "data-testid": "lax-workflow-root", children: [
4666
4972
  /* @__PURE__ */ jsx12(
4667
4973
  WorkflowContainerLine,
4668
4974
  {
@@ -4684,7 +4990,7 @@ function WorkflowRootContainer({
4684
4990
 
4685
4991
  // src/view/workflow/WorkflowRouteContainer.tsx
4686
4992
  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";
4993
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
4688
4994
  function WorkflowRouteContainer({
4689
4995
  node,
4690
4996
  loopTree,
@@ -4721,7 +5027,7 @@ function WorkflowRouteContainer({
4721
5027
  "--lax-workflow-depth": depth,
4722
5028
  "--lax-workflow-indent-unit": workflowIndentUnit(depth)
4723
5029
  };
4724
- return /* @__PURE__ */ jsxs9(
5030
+ return /* @__PURE__ */ jsxs10(
4725
5031
  "div",
4726
5032
  {
4727
5033
  className: `lax-workflow-route-branch lax-workflow-stage-container lax-workflow-stage-container--${uiStatus}${isSkipped ? " lax-workflow-route-branch--skipped" : ""}`,
@@ -4785,7 +5091,7 @@ function WorkflowRouteContainer({
4785
5091
 
4786
5092
  // src/view/workflow/WorkflowSubworkflowContainer.tsx
4787
5093
  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";
5094
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
4789
5095
  function buildDoneSummary(node) {
4790
5096
  const display = node.display;
4791
5097
  if (display?.progress_total != null) {
@@ -4828,7 +5134,7 @@ function WorkflowSubworkflowContainer({
4828
5134
  "--lax-workflow-indent-unit": workflowIndentUnit(depth)
4829
5135
  };
4830
5136
  const showShellLine = !(effectivelyDone && !expanded);
4831
- return /* @__PURE__ */ jsxs10(
5137
+ return /* @__PURE__ */ jsxs11(
4832
5138
  "div",
4833
5139
  {
4834
5140
  className: "lax-workflow-subworkflow",
@@ -4884,7 +5190,7 @@ function WorkflowSubworkflowContainer({
4884
5190
  }
4885
5191
 
4886
5192
  // src/view/workflow/WorkflowChrome.tsx
4887
- import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
5193
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
4888
5194
  function collectChildContainers(containersById, parentId) {
4889
5195
  return Object.values(containersById).filter((node) => node.parent_container_id === parentId).sort((a, b) => a.scope_key.localeCompare(b.scope_key));
4890
5196
  }
@@ -4967,7 +5273,7 @@ function renderContainerChildren(parentId, containerTree, loopTreesBySessionId,
4967
5273
  const children = collectChildContainers(containerTree.containersById, parentId);
4968
5274
  const items = children.filter((node) => node.scope === "item");
4969
5275
  const rest = children.filter((node) => node.scope !== "item");
4970
- return /* @__PURE__ */ jsxs11(Fragment2, { children: [
5276
+ return /* @__PURE__ */ jsxs12(Fragment3, { children: [
4971
5277
  renderItemSiblings(items, parentId, depth, containerTree, loopTreesBySessionId, viewProps),
4972
5278
  rest.map(
4973
5279
  (node) => renderContainerNode(node, containerTree, loopTreesBySessionId, viewProps, depth, ancestorVisited)
@@ -5006,7 +5312,7 @@ function renderContainerNode(node, containerTree, loopTreesBySessionId, viewProp
5006
5312
  }
5007
5313
  if (node.scope === "stage") {
5008
5314
  const suppressHostScopedLoop = stageHasSubworkflowChild(node.container_id, containerTree);
5009
- return /* @__PURE__ */ jsxs11(
5315
+ return /* @__PURE__ */ jsxs12(
5010
5316
  "div",
5011
5317
  {
5012
5318
  className: "lax-workflow-nested-block",
@@ -5027,7 +5333,7 @@ function renderContainerNode(node, containerTree, loopTreesBySessionId, viewProp
5027
5333
  );
5028
5334
  }
5029
5335
  if (node.scope === "aggregate" || node.scope === "branch") {
5030
- return /* @__PURE__ */ jsxs11("div", { className: "lax-workflow-container-children", children: [
5336
+ return /* @__PURE__ */ jsxs12("div", { className: "lax-workflow-container-children", children: [
5031
5337
  renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, viewProps),
5032
5338
  hasNested ? nested : null
5033
5339
  ] }, node.container_id);
@@ -5068,11 +5374,11 @@ function WorkflowStageListPanel({
5068
5374
  scale.completedStagePageSize,
5069
5375
  partition.totalDoneCount - partition.visibleDoneCount
5070
5376
  );
5071
- return /* @__PURE__ */ jsxs11(Fragment2, { children: [
5377
+ return /* @__PURE__ */ jsxs12(Fragment3, { children: [
5072
5378
  /* @__PURE__ */ jsx15("ol", { className: "lax-workflow-stage-list", "data-testid": "lax-workflow-stage-list", children: partition.visible.map(
5073
5379
  (node) => renderWorkflowStageRow(node, containerTree, loopTreesBySessionId, viewProps)
5074
5380
  ) }),
5075
- partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs11(
5381
+ partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs12(
5076
5382
  "button",
5077
5383
  {
5078
5384
  type: "button",
@@ -5098,7 +5404,7 @@ function WorkflowStageListPanel({
5098
5404
  children: "Show fewer completed stages"
5099
5405
  }
5100
5406
  ) : null,
5101
- partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs11(
5407
+ partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs12(
5102
5408
  "p",
5103
5409
  {
5104
5410
  className: "lax-workflow-stage-list__hidden-hint",
@@ -5123,7 +5429,7 @@ function renderWorkflowChildren(workflowNode, containerTree, loopTreesBySessionI
5123
5429
  const other = children.filter(
5124
5430
  (node) => node.scope !== "stage" && node.scope !== "item"
5125
5431
  );
5126
- return /* @__PURE__ */ jsxs11(Fragment2, { children: [
5432
+ return /* @__PURE__ */ jsxs12(Fragment3, { children: [
5127
5433
  stages.length > 0 ? /* @__PURE__ */ jsx15(
5128
5434
  WorkflowStageListPanel,
5129
5435
  {
@@ -5177,7 +5483,7 @@ function WorkflowChrome({
5177
5483
 
5178
5484
  // src/view/workflow/WorkflowGlobalSpinner.tsx
5179
5485
  import { useStore as useStore5 } from "zustand";
5180
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
5486
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
5181
5487
  function WorkflowGlobalSpinner() {
5182
5488
  const store = useWorkflowSessionStoreApi();
5183
5489
  const show = useStore5(store, (s) => shouldShowGlobalSpinner(s.state));
@@ -5188,7 +5494,7 @@ function WorkflowGlobalSpinner() {
5188
5494
  className: "lax-workflow-global-spinner lax-spinner-container",
5189
5495
  "data-testid": "lax-workflow-global-spinner",
5190
5496
  role: "status",
5191
- children: /* @__PURE__ */ jsxs12("span", { className: "lax-spinner lax-workflow-global-spinner__inner", children: [
5497
+ children: /* @__PURE__ */ jsxs13("span", { className: "lax-spinner lax-workflow-global-spinner__inner", children: [
5192
5498
  /* @__PURE__ */ jsx16("span", { className: "lax-spinner-frame", children: "\u280B" }),
5193
5499
  /* @__PURE__ */ jsx16("span", { className: "lax-spinner-verb lax-spinner-verb--shimmer", children: "Workflow" })
5194
5500
  ] })
@@ -5197,15 +5503,24 @@ function WorkflowGlobalSpinner() {
5197
5503
  }
5198
5504
 
5199
5505
  // src/view/workflow/WorkflowSessionBanners.tsx
5200
- import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
5506
+ import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
5201
5507
  function WorkflowSessionBanners({ state }) {
5202
5508
  const banner = resolveWorkflowBanner(state);
5203
5509
  const { display } = state;
5204
5510
  if (banner.kind === "none" && display.projectionStatus !== "error" && display.projectionStatus !== "done_with_warning" && state.internalErrors.length === 0) {
5205
5511
  return null;
5206
5512
  }
5207
- const showTransportError = (display.projectionStatus === "error" || state.internalErrors.length > 0) && banner.kind !== "projection_degraded" && banner.kind !== "authority_error";
5208
- return /* @__PURE__ */ jsxs13(Fragment3, { children: [
5513
+ const showTransportError = (display.projectionStatus === "error" || state.internalErrors.length > 0) && banner.kind !== "projection_degraded" && banner.kind !== "authority_error" && banner.kind !== "canceled";
5514
+ return /* @__PURE__ */ jsxs14(Fragment4, { children: [
5515
+ banner.kind === "canceled" ? /* @__PURE__ */ jsx17(
5516
+ "div",
5517
+ {
5518
+ className: "lax-workflow-banner lax-workflow-banner--canceled",
5519
+ "data-testid": "lax-workflow-canceled-banner",
5520
+ role: "status",
5521
+ children: banner.message
5522
+ }
5523
+ ) : null,
5209
5524
  banner.kind === "authority_error" ? /* @__PURE__ */ jsx17(
5210
5525
  "div",
5211
5526
  {
@@ -5224,6 +5539,15 @@ function WorkflowSessionBanners({ state }) {
5224
5539
  children: banner.message
5225
5540
  }
5226
5541
  ) : null,
5542
+ banner.kind === "loop_display_truncated" ? /* @__PURE__ */ jsx17(
5543
+ "div",
5544
+ {
5545
+ className: "lax-workflow-banner lax-workflow-banner--loop-truncated",
5546
+ "data-testid": "lax-workflow-loop-truncated-banner",
5547
+ role: "status",
5548
+ children: banner.message
5549
+ }
5550
+ ) : null,
5227
5551
  banner.kind === "snapshot_unavailable" ? /* @__PURE__ */ jsx17(
5228
5552
  "div",
5229
5553
  {
@@ -5242,7 +5566,7 @@ function WorkflowSessionBanners({ state }) {
5242
5566
  children: banner.message
5243
5567
  }
5244
5568
  ) : null,
5245
- banner.kind === "forced_closeout" || display.projectionStatus === "done_with_warning" ? /* @__PURE__ */ jsxs13(
5569
+ banner.kind === "forced_closeout" || display.projectionStatus === "done_with_warning" ? /* @__PURE__ */ jsxs14(
5246
5570
  "div",
5247
5571
  {
5248
5572
  className: "lax-workflow-closeout-warning",
@@ -5250,7 +5574,7 @@ function WorkflowSessionBanners({ state }) {
5250
5574
  role: "status",
5251
5575
  children: [
5252
5576
  /* @__PURE__ */ jsx17("strong", { children: "Workflow forced closeout" }),
5253
- /* @__PURE__ */ jsxs13("span", { children: [
5577
+ /* @__PURE__ */ jsxs14("span", { children: [
5254
5578
  " ",
5255
5579
  "\u2014 stream ended with unresolved child scopes; not a healthy completion."
5256
5580
  ] }),
@@ -5361,9 +5685,9 @@ function loopSessionDisplayLabel(loopSessionId) {
5361
5685
  }
5362
5686
 
5363
5687
  // src/view/workflow/WorkflowTaskListFooter.tsx
5364
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
5688
+ import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
5365
5689
  function MessageResponse({ children }) {
5366
- return /* @__PURE__ */ jsxs14("div", { className: "lax-message-response", children: [
5690
+ return /* @__PURE__ */ jsxs15("div", { className: "lax-message-response", children: [
5367
5691
  /* @__PURE__ */ jsx18("span", { className: "lax-message-response__marker", children: "\u23BF " }),
5368
5692
  /* @__PURE__ */ jsx18("span", { className: "lax-message-response__content", children })
5369
5693
  ] });
@@ -5379,8 +5703,8 @@ function WorkflowTaskListFooter() {
5379
5703
  }
5380
5704
  const label = formatWorkflowTaskFooterLabel(state);
5381
5705
  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(
5706
+ return /* @__PURE__ */ jsxs15("div", { className: "lax-workflow-task-list-footer", "data-testid": "lax-workflow-task-list-footer", children: [
5707
+ /* @__PURE__ */ jsxs15(
5384
5708
  "button",
5385
5709
  {
5386
5710
  type: "button",
@@ -5394,7 +5718,7 @@ function WorkflowTaskListFooter() {
5394
5718
  ]
5395
5719
  }
5396
5720
  ),
5397
- expanded ? /* @__PURE__ */ jsx18(MessageResponse, { children: /* @__PURE__ */ jsx18("div", { className: "lax-workflow-task-list-groups", children: groups.map((group) => /* @__PURE__ */ jsxs14(
5721
+ expanded ? /* @__PURE__ */ jsx18(MessageResponse, { children: /* @__PURE__ */ jsx18("div", { className: "lax-workflow-task-list-groups", children: groups.map((group) => /* @__PURE__ */ jsxs15(
5398
5722
  "div",
5399
5723
  {
5400
5724
  className: "lax-workflow-task-list-group",
@@ -5411,7 +5735,7 @@ function WorkflowTaskListFooter() {
5411
5735
 
5412
5736
  // src/view/workflow/WorkflowSession.tsx
5413
5737
  import { useStore as useStore7 } from "zustand";
5414
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
5738
+ import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
5415
5739
  function WorkflowSession({
5416
5740
  source,
5417
5741
  authoritySource,
@@ -5535,7 +5859,7 @@ function WorkflowSession({
5535
5859
  streamAbortRef.current?.abort();
5536
5860
  }
5537
5861
  }, [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(
5862
+ 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
5863
  "div",
5540
5864
  {
5541
5865
  className: "lax-agent-session lax-workflow-session",
@@ -5772,14 +6096,14 @@ function buildTimelineEntries(rootIds, byId, options = {}) {
5772
6096
  }
5773
6097
 
5774
6098
  // src/view/nodes/SubAgentNode.tsx
5775
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
6099
+ import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
5776
6100
  function SubAgentBlock({ nodeId }) {
5777
6101
  const node = useNodeTyped(nodeId, "subagent");
5778
6102
  const childIds = useChildren(nodeId);
5779
6103
  const registry = useNodeRegistry();
5780
6104
  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: [
6105
+ return /* @__PURE__ */ jsxs17("div", { className: "lax-subagent-block", "data-status": node.status, children: [
6106
+ /* @__PURE__ */ jsxs17("div", { className: "lax-subagent-block__header", children: [
5783
6107
  "SubAgent: ",
5784
6108
  node.subagentId
5785
6109
  ] }),
@@ -5941,6 +6265,7 @@ export {
5941
6265
  displayPath,
5942
6266
  entryKey,
5943
6267
  filterAgentLoopReplayEvents,
6268
+ finalizeSessionTreeOnAuthorityCanceled,
5944
6269
  finalizeWorkflowDisplayOnStreamEnded,
5945
6270
  fixDiagramSvg,
5946
6271
  formatAgentTitle,
@@ -5959,6 +6284,7 @@ export {
5959
6284
  getNoopInteractionBus,
5960
6285
  hydrateAskFromSnapshot,
5961
6286
  hydrateWorkflowFromSnapshot,
6287
+ isAgentInputLocked,
5962
6288
  isAgentLoopReplayEventType,
5963
6289
  isAtOrBeforeProjectionCursor,
5964
6290
  isAuthorityTerminal,