langchain_agentx_stream_ui 0.1.3 → 0.1.9

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
@@ -1,4 +1,6 @@
1
1
  import {
2
+ DiagramZoomShell,
3
+ DiagramZoomView,
2
4
  ErrorCard,
3
5
  MermaidDiagram,
4
6
  MultiAgentSession,
@@ -12,14 +14,16 @@ import {
12
14
  TextBubble,
13
15
  ToolCallCard,
14
16
  UnknownCard,
17
+ computeViewportFitTransform,
15
18
  configureMermaid,
16
19
  configurePlantumlServer,
17
20
  createActiveSessionBridge,
18
21
  createDefaultRegistry,
19
22
  createMultiSessionStore,
23
+ fixDiagramSvg,
20
24
  markEventIdSeen,
21
25
  shouldSkipDuplicateEvent
22
- } from "./chunk-A4W6B7C3.js";
26
+ } from "./chunk-2T26L3VT.js";
23
27
  import {
24
28
  DefaultToolBody,
25
29
  InteractionBusContext,
@@ -28,7 +32,7 @@ import {
28
32
  getNoopInteractionBus,
29
33
  resolveToolBody,
30
34
  useInteractionBus
31
- } from "./chunk-WM6Y6APP.js";
35
+ } from "./chunk-6DYISADG.js";
32
36
  import "./chunk-DP7V33X7.js";
33
37
  import {
34
38
  CollapseKind,
@@ -107,7 +111,7 @@ import {
107
111
  useSessionStatus,
108
112
  useSessionViewOptions,
109
113
  useTimeline
110
- } from "./chunk-3B4WC67E.js";
114
+ } from "./chunk-ZNXAQ4ZZ.js";
111
115
  import {
112
116
  Agent,
113
117
  AgentToolBody,
@@ -134,7 +138,7 @@ import {
134
138
  WriteToolBody,
135
139
  createDefaultToolRegistry,
136
140
  formatAgentTitle
137
- } from "./chunk-MHK53ZHC.js";
141
+ } from "./chunk-7CLAU74Y.js";
138
142
  import {
139
143
  BodyBlockList,
140
144
  DiffView,
@@ -764,6 +768,30 @@ function createWorkflowRunningLoopTree(seedMs = Date.now()) {
764
768
  tree.meta.startedAt = seedMs;
765
769
  return tree;
766
770
  }
771
+ function finalizeWorkflowLoopTree(tree, terminalStatus = "done") {
772
+ if (tree.status === "done" || tree.status === "error") {
773
+ return tree;
774
+ }
775
+ const byId = { ...tree.byId };
776
+ for (const [id, node] of Object.entries(byId)) {
777
+ if (!node) continue;
778
+ if ((node.kind === "text" || node.kind === "reasoning") && node.status === "streaming") {
779
+ byId[id] = { ...node, status: "done" };
780
+ }
781
+ if (node.kind === "tool_call" && node.status === "running") {
782
+ byId[id] = {
783
+ ...node,
784
+ status: terminalStatus === "error" ? "failed" : "done"
785
+ };
786
+ }
787
+ }
788
+ return {
789
+ ...tree,
790
+ byId,
791
+ activeRoundId: null,
792
+ status: terminalStatus
793
+ };
794
+ }
767
795
 
768
796
  // src/types/workflowDisplay.ts
769
797
  function readDisplayPayload(data) {
@@ -922,7 +950,7 @@ function contentBlockFromEvent(event) {
922
950
  return null;
923
951
  }
924
952
  function resolveContentScopeKey(data) {
925
- for (const keyName of ["stage_key", "item_key", "branch_key", "debug_key"]) {
953
+ for (const keyName of ["stage_key", "item_key", "branch_key", "aggregate_key", "debug_key"]) {
926
954
  const value = readString(data, keyName);
927
955
  if (value) return value;
928
956
  }
@@ -1222,6 +1250,7 @@ function readString2(data, key) {
1222
1250
  var LOOP_TASK_OPEN_SCOPES = {
1223
1251
  "stage-start": "stage",
1224
1252
  "parallel-item-start": "item",
1253
+ "parallel-aggregate-start": "aggregate",
1225
1254
  "route-branch-start": "branch"
1226
1255
  };
1227
1256
  function buildLoopSessionId(workflowId, taskKey) {
@@ -1240,7 +1269,7 @@ function deriveLoopSessionIdFromAgentEvent(event) {
1240
1269
  const data = event.data ?? {};
1241
1270
  const workflowId = readString2(data, "workflow_id") ?? readString2(data, "workflow_path");
1242
1271
  if (!workflowId) return null;
1243
- const taskKey = readString2(data, "stage_key") ?? readString2(data, "item_key") ?? readString2(data, "branch_key");
1272
+ const taskKey = readString2(data, "stage_key") ?? readString2(data, "item_key") ?? readString2(data, "branch_key") ?? readString2(data, "aggregate_key");
1244
1273
  if (!taskKey) return null;
1245
1274
  return buildLoopSessionId(workflowId, taskKey);
1246
1275
  }
@@ -1248,6 +1277,259 @@ function loopTaskScopeForStructureOpen(eventType) {
1248
1277
  return LOOP_TASK_OPEN_SCOPES[eventType];
1249
1278
  }
1250
1279
 
1280
+ // src/core/workflow/embeddedSubworkflowUtils.ts
1281
+ function parseLoopSessionParts(sessionId) {
1282
+ const trimmed = sessionId.trim();
1283
+ const dash = trimmed.lastIndexOf("-");
1284
+ if (dash <= 0 || dash >= trimmed.length - 1) return null;
1285
+ return {
1286
+ workflowId: trimmed.slice(0, dash),
1287
+ taskKey: trimmed.slice(dash + 1)
1288
+ };
1289
+ }
1290
+ function isEmbeddedChildLoopSession(sessionId, rootWorkflowId) {
1291
+ const parts = parseLoopSessionParts(sessionId);
1292
+ if (!parts) return false;
1293
+ if (parts.workflowId === rootWorkflowId) return false;
1294
+ if (parts.workflowId.startsWith(`${rootWorkflowId}-`)) return false;
1295
+ if (rootWorkflowId.startsWith(`${parts.workflowId}-`)) return false;
1296
+ return parts.workflowId.split("-").length >= 3;
1297
+ }
1298
+ function readWorkflowIdFromData(data) {
1299
+ const value = data.workflow_id;
1300
+ return typeof value === "string" ? value : void 0;
1301
+ }
1302
+ function readStageKeyFromData(data) {
1303
+ const value = data.stage_key;
1304
+ return typeof value === "string" ? value : void 0;
1305
+ }
1306
+ function shouldPrebindLoopSessionFromStructureOpen(eventType, eventSessionId, derivedLoopSessionId, data = {}) {
1307
+ if (!eventSessionId) return true;
1308
+ if (eventType !== "stage-start") return true;
1309
+ if (eventSessionId === derivedLoopSessionId) return true;
1310
+ const rootWorkflowId = readWorkflowIdFromData(data);
1311
+ const stageKey = readStageKeyFromData(data);
1312
+ const parsed = parseLoopSessionParts(eventSessionId);
1313
+ if (rootWorkflowId && stageKey && parsed) {
1314
+ if (parsed.workflowId === rootWorkflowId && parsed.taskKey !== stageKey) {
1315
+ return false;
1316
+ }
1317
+ }
1318
+ return true;
1319
+ }
1320
+ function resolveRootWorkflow(tree) {
1321
+ const rootId = tree.rootContainerIds[0];
1322
+ if (!rootId) return null;
1323
+ const root = tree.containersById[rootId];
1324
+ if (!root || root.scope !== "workflow") return null;
1325
+ return {
1326
+ workflowId: root.scope_key,
1327
+ workflowPath: root.workflow_path
1328
+ };
1329
+ }
1330
+ function humanizeTaskTitle(taskKey) {
1331
+ if (taskKey === "aggregate") return "Merge Perspectives";
1332
+ if (taskKey === "architecture") return "Architecture Path";
1333
+ if (taskKey === "implementation") return "Implementation Path";
1334
+ return taskKey.split("_").map((part) => part ? part[0].toUpperCase() + part.slice(1) : part).join(" ");
1335
+ }
1336
+ function findEmbeddedHostStage(tree, rootWorkflowPath, childWorkflowId) {
1337
+ const childPath = `${rootWorkflowPath}>${childWorkflowId}`;
1338
+ const subId = buildContainerId(childPath, "subworkflow", childWorkflowId);
1339
+ const existingSub = tree.containersById[subId];
1340
+ if (existingSub?.parent_container_id) {
1341
+ return tree.containersById[existingSub.parent_container_id] ?? null;
1342
+ }
1343
+ const runningStages = tree.activeContainerIds.map((id) => tree.containersById[id]).filter(
1344
+ (node) => node != null && node.scope === "stage" && node.workflow_path === rootWorkflowPath && node.is_open
1345
+ );
1346
+ if (runningStages.length === 0) return null;
1347
+ return runningStages.reduce(
1348
+ (best, node) => (node.display?.progress_current ?? 0) >= (best.display?.progress_current ?? 0) ? node : best
1349
+ );
1350
+ }
1351
+ function upsertRuntimeNode(tree, node) {
1352
+ const activeContainerIds = tree.activeContainerIds.includes(node.container_id) ? tree.activeContainerIds : node.is_open ? [...tree.activeContainerIds, node.container_id] : tree.activeContainerIds;
1353
+ return {
1354
+ ...tree,
1355
+ activeContainerIds,
1356
+ containersById: {
1357
+ ...tree.containersById,
1358
+ [node.container_id]: node
1359
+ }
1360
+ };
1361
+ }
1362
+ function completeOpenItemsUnderSubworkflow(tree, subworkflowId) {
1363
+ let next = tree;
1364
+ for (const node of Object.values(tree.containersById)) {
1365
+ if (node.parent_container_id === subworkflowId && node.scope === "item" && node.status === "running") {
1366
+ next = upsertRuntimeNode(next, {
1367
+ ...node,
1368
+ status: "completed",
1369
+ is_open: false
1370
+ });
1371
+ }
1372
+ }
1373
+ const activeContainerIds = next.activeContainerIds.filter(
1374
+ (id) => next.containersById[id]?.is_open
1375
+ );
1376
+ return { ...next, activeContainerIds };
1377
+ }
1378
+ function resolveLeafScope(taskKey) {
1379
+ return taskKey === "aggregate" ? "aggregate" : "item";
1380
+ }
1381
+ function ensureEmbeddedSubworkflowContainers(tree, loopSessionId) {
1382
+ const root = resolveRootWorkflow(tree);
1383
+ const parts = parseLoopSessionParts(loopSessionId);
1384
+ if (!root || !parts || !isEmbeddedChildLoopSession(loopSessionId, root.workflowId)) {
1385
+ return tree;
1386
+ }
1387
+ const hostStage = findEmbeddedHostStage(tree, root.workflowPath, parts.workflowId);
1388
+ if (!hostStage) return tree;
1389
+ let next = tree;
1390
+ if (hostStage.loopSessionId) {
1391
+ next = upsertRuntimeNode(next, { ...hostStage, loopSessionId: null });
1392
+ }
1393
+ const childPath = `${root.workflowPath}>${parts.workflowId}`;
1394
+ const subId = buildContainerId(childPath, "subworkflow", parts.workflowId);
1395
+ let subNode = next.containersById[subId];
1396
+ if (!subNode) {
1397
+ subNode = {
1398
+ container_id: subId,
1399
+ scope: "subworkflow",
1400
+ workflow_path: childPath,
1401
+ workflow_depth: hostStage.workflow_depth + 1,
1402
+ scope_key: parts.workflowId,
1403
+ title: "Dual-Perspective Review",
1404
+ subtitle: "Nested parallel sub-workflow",
1405
+ status: "running",
1406
+ pattern: "parallelization",
1407
+ parent_container_id: hostStage.container_id,
1408
+ display: {
1409
+ title: "Dual-Perspective Review",
1410
+ description: "Nested parallel sub-workflow",
1411
+ pattern: "parallelization",
1412
+ status: "running"
1413
+ },
1414
+ content_blocks: [],
1415
+ is_open: true,
1416
+ loopSessionId: null
1417
+ };
1418
+ next = upsertRuntimeNode(next, subNode);
1419
+ }
1420
+ const leafScope = resolveLeafScope(parts.taskKey);
1421
+ const leafId = buildContainerId(childPath, leafScope, parts.taskKey);
1422
+ let leafNode = next.containersById[leafId];
1423
+ if (!leafNode) {
1424
+ leafNode = {
1425
+ container_id: leafId,
1426
+ scope: leafScope,
1427
+ workflow_path: childPath,
1428
+ workflow_depth: hostStage.workflow_depth + 1,
1429
+ scope_key: parts.taskKey,
1430
+ title: humanizeTaskTitle(parts.taskKey),
1431
+ subtitle: "",
1432
+ status: "running",
1433
+ pattern: "parallelization",
1434
+ parent_container_id: subId,
1435
+ display: {
1436
+ title: humanizeTaskTitle(parts.taskKey),
1437
+ pattern: "parallelization",
1438
+ status: "running"
1439
+ },
1440
+ content_blocks: [],
1441
+ is_open: true,
1442
+ loopSessionId: null
1443
+ };
1444
+ next = upsertRuntimeNode(next, leafNode);
1445
+ }
1446
+ if (leafScope === "aggregate") {
1447
+ next = completeOpenItemsUnderSubworkflow(next, subId);
1448
+ }
1449
+ if (leafNode.loopSessionId !== loopSessionId) {
1450
+ next = upsertRuntimeNode(next, {
1451
+ ...leafNode,
1452
+ loopSessionId,
1453
+ content_blocks: []
1454
+ });
1455
+ }
1456
+ return next;
1457
+ }
1458
+ function findContainerIdByLoopSession(tree, loopSessionId) {
1459
+ for (const [containerId, node] of Object.entries(tree.containersById)) {
1460
+ if (node.loopSessionId === loopSessionId) return containerId;
1461
+ }
1462
+ return null;
1463
+ }
1464
+ function resolveStructureOpenLoopSessionId(eventType, eventSessionId, derivedLoopSessionId, data) {
1465
+ const rootWorkflowId = readWorkflowId(data) ?? "";
1466
+ if ((eventType === "parallel-aggregate-start" || eventType === "parallel-aggregate-done" || eventType === "parallel-aggregate-failed") && eventSessionId && isEmbeddedChildLoopSession(eventSessionId, rootWorkflowId)) {
1467
+ return eventSessionId;
1468
+ }
1469
+ return derivedLoopSessionId;
1470
+ }
1471
+ var EMBEDDED_AGGREGATE_STRUCTURE_EVENTS = /* @__PURE__ */ new Set([
1472
+ "parallel-aggregate-start",
1473
+ "parallel-aggregate-done",
1474
+ "parallel-aggregate-failed"
1475
+ ]);
1476
+ function patchEmbeddedStructureEvent(event, rootWorkflowId) {
1477
+ if (!EMBEDDED_AGGREGATE_STRUCTURE_EVENTS.has(event.event_type)) {
1478
+ return event;
1479
+ }
1480
+ if (!event.session_id || !isEmbeddedChildLoopSession(event.session_id, rootWorkflowId)) {
1481
+ return event;
1482
+ }
1483
+ const parts = parseLoopSessionParts(event.session_id);
1484
+ if (!parts) return event;
1485
+ const data = event.data ?? {};
1486
+ const rootPath = readString3(data, "workflow_path") ?? rootWorkflowId;
1487
+ const childPath = `${rootPath}>${parts.workflowId}`;
1488
+ const workflowDepth = typeof data.workflow_depth === "number" ? data.workflow_depth + 1 : 1;
1489
+ return {
1490
+ ...event,
1491
+ data: {
1492
+ ...data,
1493
+ workflow_path: childPath,
1494
+ workflow_depth: workflowDepth
1495
+ }
1496
+ };
1497
+ }
1498
+ function readString3(data, key) {
1499
+ const value = data[key];
1500
+ return typeof value === "string" ? value : void 0;
1501
+ }
1502
+ function isDescendantOf(containersById, ancestorId, nodeId) {
1503
+ let current = containersById[nodeId]?.parent_container_id ?? null;
1504
+ while (current) {
1505
+ if (current === ancestorId) return true;
1506
+ current = containersById[current]?.parent_container_id ?? null;
1507
+ }
1508
+ return false;
1509
+ }
1510
+ function settleCompletedStageSubtree(tree, stageContainerId) {
1511
+ let next = tree;
1512
+ for (const node of Object.values(tree.containersById)) {
1513
+ if (node.container_id !== stageContainerId && !isDescendantOf(tree.containersById, stageContainerId, node.container_id)) {
1514
+ continue;
1515
+ }
1516
+ if (node.container_id === stageContainerId) continue;
1517
+ if (node.status === "completed" || node.status === "failed") continue;
1518
+ next = upsertRuntimeNode(next, {
1519
+ ...node,
1520
+ status: "completed",
1521
+ is_open: false
1522
+ });
1523
+ }
1524
+ const activeContainerIds = next.activeContainerIds.filter(
1525
+ (id) => next.containersById[id]?.is_open
1526
+ );
1527
+ return { ...next, activeContainerIds };
1528
+ }
1529
+ function readWorkflowId(data) {
1530
+ return readWorkflowIdFromData(data);
1531
+ }
1532
+
1251
1533
  // src/core/workflowStructureEvents.ts
1252
1534
  var WORKFLOW_STRUCTURE_EVENT_PREFIXES = [
1253
1535
  "workflow-",
@@ -1292,7 +1574,7 @@ function filterAgentLoopReplayEvents(events) {
1292
1574
  }
1293
1575
 
1294
1576
  // src/core/workflowReducer.ts
1295
- function readString3(data, key) {
1577
+ function readString4(data, key) {
1296
1578
  const value = data[key];
1297
1579
  return typeof value === "string" ? value : void 0;
1298
1580
  }
@@ -1372,23 +1654,43 @@ function applyStructureToContainerTree(tree, event) {
1372
1654
  };
1373
1655
  }
1374
1656
  function prebindLoopSessionFromStructureOpen(tree, event) {
1375
- const loopSessionId = deriveLoopSessionIdFromStructureOpen(event);
1657
+ const derivedLoopSessionId = deriveLoopSessionIdFromStructureOpen(event);
1376
1658
  const scope = loopTaskScopeForStructureOpen(event.event_type);
1377
- if (!loopSessionId || !scope) return tree;
1659
+ if (!derivedLoopSessionId || !scope) return tree;
1378
1660
  const data = event.data ?? {};
1379
- const workflowPath = readString3(data, "workflow_path") ?? "";
1661
+ let loopSessionId = resolveStructureOpenLoopSessionId(
1662
+ event.event_type,
1663
+ event.session_id,
1664
+ derivedLoopSessionId,
1665
+ data
1666
+ );
1667
+ if (!shouldPrebindLoopSessionFromStructureOpen(
1668
+ event.event_type,
1669
+ event.session_id,
1670
+ derivedLoopSessionId,
1671
+ data
1672
+ )) {
1673
+ loopSessionId = derivedLoopSessionId;
1674
+ }
1675
+ let nextTree = event.event_type === "parallel-aggregate-start" ? ensureEmbeddedSubworkflowContainers(tree, loopSessionId) : tree;
1676
+ const workflowPath = readString4(data, "workflow_path") ?? "";
1380
1677
  const scopeKey = resolveScopeKey(event.event_type, data);
1381
1678
  const containerId = buildContainerId(workflowPath, scope, scopeKey);
1382
- const node = tree.containersById[containerId];
1383
- if (!node) return tree;
1679
+ let targetId = containerId;
1680
+ const embeddedTargetId = findContainerIdByLoopSession(nextTree, loopSessionId);
1681
+ if (embeddedTargetId) {
1682
+ targetId = embeddedTargetId;
1683
+ }
1684
+ const node = nextTree.containersById[targetId];
1685
+ if (!node) return nextTree;
1384
1686
  if (node.loopSessionId === loopSessionId && node.content_blocks.length === 0) {
1385
- return tree;
1687
+ return nextTree;
1386
1688
  }
1387
1689
  return {
1388
- ...tree,
1690
+ ...nextTree,
1389
1691
  containersById: {
1390
- ...tree.containersById,
1391
- [containerId]: {
1692
+ ...nextTree.containersById,
1693
+ [targetId]: {
1392
1694
  ...node,
1393
1695
  loopSessionId,
1394
1696
  content_blocks: []
@@ -1434,10 +1736,7 @@ function enrichWorkflowRootFromStages(containersById, rootContainerIds) {
1434
1736
  };
1435
1737
  }
1436
1738
  function findContainerByLoopSession(tree, loopSessionId) {
1437
- for (const [containerId, node] of Object.entries(tree.containersById)) {
1438
- if (node.loopSessionId === loopSessionId) return containerId;
1439
- }
1440
- return null;
1739
+ return findContainerIdByLoopSession(tree, loopSessionId);
1441
1740
  }
1442
1741
  var WORKFLOW_DEFAULT_LOOP_SESSION_ID = "workflow-loop-default";
1443
1742
  function resolveAgentLoopSessionId(event, activeLoopSessionId) {
@@ -1452,22 +1751,23 @@ function resolveAgentLoopSessionId(event, activeLoopSessionId) {
1452
1751
  function bindLoopSessionToContainerTree(tree, event, activeLoopSessionId) {
1453
1752
  const sessionId = resolveAgentLoopSessionId(event, activeLoopSessionId);
1454
1753
  if (!sessionId) return tree;
1455
- if (findContainerByLoopSession(tree, sessionId)) return tree;
1754
+ let nextTree = ensureEmbeddedSubworkflowContainers(tree, sessionId);
1755
+ if (findContainerByLoopSession(nextTree, sessionId)) return nextTree;
1456
1756
  const data = event.data ?? {};
1457
- const workflowPath = readString3(data, "workflow_path") ?? null;
1757
+ const workflowPath = readString4(data, "workflow_path") ?? null;
1458
1758
  const scopeKey = resolveContentScopeKey(data);
1459
- let targetId = null;
1460
- if (scopeKey) {
1759
+ let targetId = findContainerByLoopSession(nextTree, sessionId);
1760
+ if (!targetId && scopeKey) {
1461
1761
  targetId = resolveContentTargetFromSnapshot(
1462
- containerTreeToSnapshot(tree),
1762
+ containerTreeToSnapshot(nextTree),
1463
1763
  workflowPath,
1464
1764
  scopeKey
1465
1765
  );
1466
1766
  }
1467
1767
  if (!targetId) {
1468
- for (const containerId of tree.activeContainerIds) {
1469
- const node2 = tree.containersById[containerId];
1470
- if ((node2?.scope === "item" || node2?.scope === "stage") && !node2.loopSessionId) {
1768
+ for (const containerId of nextTree.activeContainerIds) {
1769
+ const node2 = nextTree.containersById[containerId];
1770
+ if ((node2?.scope === "item" || node2?.scope === "stage" || node2?.scope === "aggregate") && !node2.loopSessionId) {
1471
1771
  targetId = containerId;
1472
1772
  break;
1473
1773
  }
@@ -1475,33 +1775,145 @@ function bindLoopSessionToContainerTree(tree, event, activeLoopSessionId) {
1475
1775
  }
1476
1776
  if (!targetId) {
1477
1777
  targetId = resolveContentTargetFromSnapshot(
1478
- containerTreeToSnapshot(tree),
1778
+ containerTreeToSnapshot(nextTree),
1479
1779
  workflowPath,
1480
1780
  null
1481
1781
  );
1482
1782
  }
1483
- if (!targetId) return tree;
1484
- const node = tree.containersById[targetId];
1485
- if (!node || node.loopSessionId) return tree;
1486
- if (node.scope !== "stage" && node.scope !== "item" && node.scope !== "branch") {
1487
- return tree;
1783
+ if (!targetId) return nextTree;
1784
+ const node = nextTree.containersById[targetId];
1785
+ if (!node || node.loopSessionId) return nextTree;
1786
+ if (node.scope !== "stage" && node.scope !== "item" && node.scope !== "branch" && node.scope !== "aggregate") {
1787
+ return nextTree;
1488
1788
  }
1489
1789
  return {
1490
- ...tree,
1790
+ ...nextTree,
1491
1791
  containersById: {
1492
- ...tree.containersById,
1792
+ ...nextTree.containersById,
1493
1793
  [targetId]: { ...node, loopSessionId: sessionId }
1494
1794
  }
1495
1795
  };
1496
1796
  }
1797
+ var LOOP_TASK_STRUCTURE_CLOSE_EVENTS = /* @__PURE__ */ new Set([
1798
+ "stage-done",
1799
+ "stage-failed",
1800
+ "parallel-item-done",
1801
+ "parallel-item-failed",
1802
+ "parallel-aggregate-done",
1803
+ "parallel-aggregate-failed",
1804
+ "route-branch-done",
1805
+ "route-branch-failed"
1806
+ ]);
1807
+ function resolveStructureCloseLoopSessionId(event) {
1808
+ const data = event.data ?? {};
1809
+ const derived = deriveLoopSessionIdFromStructureOpen(event) ?? deriveLoopSessionIdFromAgentEvent(event);
1810
+ if (!derived && !event.session_id) return null;
1811
+ if (!derived) return event.session_id ?? null;
1812
+ return resolveStructureOpenLoopSessionId(
1813
+ event.event_type,
1814
+ event.session_id,
1815
+ derived,
1816
+ data
1817
+ );
1818
+ }
1819
+ function finalizeLoopTreesForCompletedContainers(state, containerTree) {
1820
+ let loopTreesBySessionId = state.loopTreesBySessionId;
1821
+ for (const node of Object.values(containerTree.containersById)) {
1822
+ if (!node.loopSessionId) continue;
1823
+ if (node.status !== "completed" && node.status !== "failed") continue;
1824
+ const loopTree = loopTreesBySessionId[node.loopSessionId];
1825
+ if (!loopTree || loopTree.status === "done" || loopTree.status === "error") continue;
1826
+ loopTreesBySessionId = {
1827
+ ...loopTreesBySessionId,
1828
+ [node.loopSessionId]: finalizeWorkflowLoopTree(
1829
+ loopTree,
1830
+ node.status === "failed" ? "error" : "done"
1831
+ )
1832
+ };
1833
+ }
1834
+ return { ...state, loopTreesBySessionId };
1835
+ }
1836
+ function clearActiveLoopIfFinalized(state) {
1837
+ const activeId = state.activeLoopSessionId;
1838
+ if (!activeId) return state;
1839
+ const loop = state.loopTreesBySessionId[activeId];
1840
+ if (loop && (loop.status === "done" || loop.status === "error")) {
1841
+ return { ...state, activeLoopSessionId: null };
1842
+ }
1843
+ return state;
1844
+ }
1845
+ function resolveRootWorkflowId(containerTree) {
1846
+ const rootId = containerTree.rootContainerIds[0];
1847
+ const root = rootId ? containerTree.containersById[rootId] : void 0;
1848
+ return root?.scope === "workflow" ? root.scope_key : null;
1849
+ }
1850
+ function findContainerForLoopSession(tree, loopSessionId) {
1851
+ return Object.values(tree.containersById).find(
1852
+ (node) => node.loopSessionId === loopSessionId
1853
+ );
1854
+ }
1855
+ function reconcileLoopTreeWithContainerStatus(containerTree, loopSessionId, loopTree) {
1856
+ const container = findContainerForLoopSession(containerTree, loopSessionId);
1857
+ if (!container) return loopTree;
1858
+ if (container.status === "completed") {
1859
+ return finalizeWorkflowLoopTree(loopTree, "done");
1860
+ }
1861
+ if (container.status === "failed") {
1862
+ return finalizeWorkflowLoopTree(loopTree, "error");
1863
+ }
1864
+ return loopTree;
1865
+ }
1866
+ function finalizeLoopTreeOnStructureClose(state, event) {
1867
+ if (!LOOP_TASK_STRUCTURE_CLOSE_EVENTS.has(event.event_type)) {
1868
+ return state;
1869
+ }
1870
+ const loopSessionId = resolveStructureCloseLoopSessionId(event);
1871
+ if (!loopSessionId) return state;
1872
+ const loopTree = state.loopTreesBySessionId[loopSessionId];
1873
+ if (!loopTree) return state;
1874
+ const terminalStatus = event.event_type.endsWith("-failed") ? "error" : "done";
1875
+ return {
1876
+ ...state,
1877
+ loopTreesBySessionId: {
1878
+ ...state.loopTreesBySessionId,
1879
+ [loopSessionId]: finalizeWorkflowLoopTree(loopTree, terminalStatus)
1880
+ }
1881
+ };
1882
+ }
1497
1883
  function applyStructureEvent(state, event) {
1498
- let containerTree = applyStructureToContainerTree(state.containerTree, event);
1499
- containerTree = prebindLoopSessionFromStructureOpen(containerTree, event);
1500
- const loopSessionId = deriveLoopSessionIdFromStructureOpen(event);
1884
+ const rootWorkflowId = resolveRootWorkflowId(state.containerTree);
1885
+ const projectedEvent = rootWorkflowId != null ? patchEmbeddedStructureEvent(event, rootWorkflowId) : event;
1886
+ let containerTree = applyStructureToContainerTree(state.containerTree, projectedEvent);
1887
+ const derivedLoopSessionId = deriveLoopSessionIdFromStructureOpen(projectedEvent);
1888
+ const structureData = projectedEvent.data ?? {};
1889
+ const structureLoopSessionId = derivedLoopSessionId ? resolveStructureOpenLoopSessionId(
1890
+ projectedEvent.event_type,
1891
+ projectedEvent.session_id,
1892
+ derivedLoopSessionId,
1893
+ structureData
1894
+ ) : null;
1895
+ if (structureLoopSessionId) {
1896
+ containerTree = ensureEmbeddedSubworkflowContainers(containerTree, structureLoopSessionId);
1897
+ }
1898
+ containerTree = prebindLoopSessionFromStructureOpen(containerTree, projectedEvent);
1899
+ if (projectedEvent.event_type === "stage-done" || projectedEvent.event_type === "stage-failed") {
1900
+ const stageKey = readString4(structureData, "stage_key");
1901
+ const workflowPath = readString4(structureData, "workflow_path") ?? "";
1902
+ if (stageKey) {
1903
+ const stageId = buildContainerId(workflowPath, "stage", stageKey);
1904
+ if (containerTree.containersById[stageId]) {
1905
+ containerTree = settleCompletedStageSubtree(containerTree, stageId);
1906
+ }
1907
+ }
1908
+ }
1909
+ const loopSessionId = structureLoopSessionId ?? derivedLoopSessionId;
1501
1910
  let nextState = { ...state, containerTree };
1502
1911
  if (loopSessionId) {
1503
1912
  nextState = ensureLoopTree(nextState, loopSessionId);
1504
1913
  }
1914
+ nextState = finalizeLoopTreeOnStructureClose(nextState, projectedEvent);
1915
+ nextState = finalizeLoopTreesForCompletedContainers(nextState, containerTree);
1916
+ nextState = clearActiveLoopIfFinalized(nextState);
1505
1917
  const workflowProgress = syncWorkflowProgressFromContainerTree(
1506
1918
  containerTree,
1507
1919
  state.workflowProgress
@@ -1543,7 +1955,12 @@ function reduceWorkflowSession(state, event, eventIndex, options) {
1543
1955
  const loopSessionId2 = taskListId ?? event.session_id ?? "workflow-loop-default";
1544
1956
  let next2 = ensureLoopTree(state, loopSessionId2);
1545
1957
  const loopTree2 = next2.loopTreesBySessionId[loopSessionId2];
1546
- const reducedLoop2 = reduceTree(loopTree2, event, eventIndex, options);
1958
+ let reducedLoop2 = reduceTree(loopTree2, event, eventIndex, options);
1959
+ reducedLoop2 = reconcileLoopTreeWithContainerStatus(
1960
+ next2.containerTree,
1961
+ loopSessionId2,
1962
+ reducedLoop2
1963
+ );
1547
1964
  return {
1548
1965
  ...next2,
1549
1966
  loopTreesBySessionId: {
@@ -1565,7 +1982,12 @@ function reduceWorkflowSession(state, event, eventIndex, options) {
1565
1982
  containerTree = applyStructureToContainerTree(containerTree, event);
1566
1983
  let next = ensureLoopTree({ ...state, containerTree }, loopSessionId);
1567
1984
  const loopTree = next.loopTreesBySessionId[loopSessionId];
1568
- const reducedLoop = reduceTree(loopTree, event, eventIndex, options);
1985
+ let reducedLoop = reduceTree(loopTree, event, eventIndex, options);
1986
+ reducedLoop = reconcileLoopTreeWithContainerStatus(
1987
+ next.containerTree,
1988
+ loopSessionId,
1989
+ reducedLoop
1990
+ );
1569
1991
  const workflowProgress = syncWorkflowProgressFromContainerTree(
1570
1992
  next.containerTree,
1571
1993
  next.workflowProgress
@@ -1994,6 +2416,17 @@ function WorkflowExpandTrigger({
1994
2416
  );
1995
2417
  }
1996
2418
 
2419
+ // src/view/workflow/WorkflowMarkdownPreview.tsx
2420
+ import { jsx as jsx7 } from "react/jsx-runtime";
2421
+ function WorkflowMarkdownPreview({
2422
+ content,
2423
+ testId,
2424
+ className = "lax-workflow-markdown-preview"
2425
+ }) {
2426
+ if (!content.trim()) return null;
2427
+ return /* @__PURE__ */ jsx7("div", { className, "data-testid": testId, children: /* @__PURE__ */ jsx7(RichMarkdown, { content, deferDiagrams: true }) });
2428
+ }
2429
+
1997
2430
  // src/view/workflow/workflowContainerLineUtils.ts
1998
2431
  function formatContainerScopeMetaLabel(scope) {
1999
2432
  return scope;
@@ -2119,12 +2552,11 @@ function mapAggregateUiStatus(node) {
2119
2552
  }
2120
2553
 
2121
2554
  // src/view/workflow/WorkflowAggregateContainer.tsx
2122
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
2123
- function mergeTeaser(node) {
2555
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
2556
+ function mergeContentPreview(node) {
2124
2557
  const blocks = node.content_blocks;
2125
2558
  if (blocks.length === 0) return "";
2126
- const last = blocks[blocks.length - 1];
2127
- return last?.preview ?? "";
2559
+ return blocks.map((block) => block.preview?.trim() ?? "").filter(Boolean).join("\n\n");
2128
2560
  }
2129
2561
  function WorkflowAggregateContainer({
2130
2562
  node,
@@ -2138,8 +2570,8 @@ function WorkflowAggregateContainer({
2138
2570
  const isRunning = node.status === "running" || node.status === "retrying";
2139
2571
  const isWaiting = node.status === "pending" || node.status === "blocked";
2140
2572
  const isDone = node.status === "completed" || node.status === "failed";
2141
- const teaser = useMemo2(() => mergeTeaser(node), [node]);
2142
- const waitingTeaser = isWaiting ? teaser || node.subtitle || "" : "";
2573
+ const contentPreview = useMemo2(() => mergeContentPreview(node), [node]);
2574
+ const waitingTeaser = isWaiting ? contentPreview || node.subtitle || "" : "";
2143
2575
  const testId = `lax-workflow-aggregate-${node.scope_key}`;
2144
2576
  const uiStatus = mapAggregateUiStatus(node);
2145
2577
  const statusLabel = buildAggregateStatusLabel(node, siblingItems);
@@ -2152,6 +2584,14 @@ function WorkflowAggregateContainer({
2152
2584
  setExpanded((prev) => !prev);
2153
2585
  }, []);
2154
2586
  const shellToggle = isRunning ? toggleExpanded : isDone && expanded ? toggleExpanded : void 0;
2587
+ const markdownPreview = contentPreview ? /* @__PURE__ */ jsx8(
2588
+ WorkflowMarkdownPreview,
2589
+ {
2590
+ content: contentPreview,
2591
+ testId: `${testId}-markdown`,
2592
+ className: "lax-workflow-aggregate__markdown lax-workflow-markdown-preview"
2593
+ }
2594
+ ) : null;
2155
2595
  return /* @__PURE__ */ jsxs4(
2156
2596
  "div",
2157
2597
  {
@@ -2160,7 +2600,7 @@ function WorkflowAggregateContainer({
2160
2600
  "data-container-scope": "aggregate",
2161
2601
  style: { "--lax-workflow-depth": depth },
2162
2602
  children: [
2163
- /* @__PURE__ */ jsx7(
2603
+ /* @__PURE__ */ jsx8(
2164
2604
  WorkflowContainerLine,
2165
2605
  {
2166
2606
  title: node.title,
@@ -2176,10 +2616,16 @@ function WorkflowAggregateContainer({
2176
2616
  }
2177
2617
  ),
2178
2618
  isWaiting && waitingTeaser ? /* @__PURE__ */ jsxs4("div", { className: "lax-workflow-aggregate__waiting-teaser", "data-testid": `${testId}-waiting`, children: [
2179
- "\u23BF ",
2180
- waitingTeaser
2619
+ /* @__PURE__ */ jsx8("span", { className: "lax-workflow-aggregate__waiting-prefix", children: "\u23BF " }),
2620
+ /* @__PURE__ */ jsx8(
2621
+ WorkflowMarkdownPreview,
2622
+ {
2623
+ content: waitingTeaser,
2624
+ className: "lax-workflow-aggregate__markdown lax-workflow-markdown-preview"
2625
+ }
2626
+ )
2181
2627
  ] }) : null,
2182
- expanded && isRunning && loopTree ? /* @__PURE__ */ jsx7("div", { className: "lax-workflow-stage-container__body", "data-testid": `${testId}-body`, children: /* @__PURE__ */ jsx7(
2628
+ expanded && isRunning && loopTree ? /* @__PURE__ */ jsx8("div", { className: "lax-workflow-stage-container__body", "data-testid": `${testId}-body`, children: /* @__PURE__ */ jsx8(
2183
2629
  AgentLoopView,
2184
2630
  {
2185
2631
  tree: loopTree,
@@ -2189,18 +2635,28 @@ function WorkflowAggregateContainer({
2189
2635
  groupParallelTools
2190
2636
  }
2191
2637
  ) }) : null,
2192
- isDone && !expanded ? /* @__PURE__ */ jsx7(
2193
- WorkflowExpandTrigger,
2638
+ expanded && isRunning && !loopTree && contentPreview ? /* @__PURE__ */ jsx8(
2639
+ "div",
2194
2640
  {
2195
- summary: `${node.title} \xB7 done`,
2196
- teaser: teaser || void 0,
2197
- onToggle: () => setExpanded(true),
2198
- expanded: false,
2199
- testId: `${testId}-done`,
2200
- teaserTestId: `${testId}-teaser`
2641
+ className: "lax-workflow-stage-container__body lax-workflow-stage-container__content-preview",
2642
+ "data-testid": `${testId}-body`,
2643
+ children: markdownPreview
2201
2644
  }
2202
2645
  ) : null,
2203
- isDone && expanded && loopTree ? /* @__PURE__ */ jsx7("div", { className: "lax-workflow-stage-container__body", "data-testid": `${testId}-body`, children: /* @__PURE__ */ jsx7(
2646
+ isDone && !expanded ? /* @__PURE__ */ jsxs4("div", { className: "lax-workflow-aggregate__done", "data-testid": `${testId}-done`, children: [
2647
+ /* @__PURE__ */ jsx8(
2648
+ WorkflowExpandTrigger,
2649
+ {
2650
+ summary: `${node.title} \xB7 done`,
2651
+ onToggle: () => setExpanded(true),
2652
+ expanded: false,
2653
+ expandHint: contentPreview ? "(click to expand)" : void 0,
2654
+ testId: `${testId}-done-trigger`
2655
+ }
2656
+ ),
2657
+ markdownPreview ? /* @__PURE__ */ jsx8("div", { className: "lax-workflow-aggregate__done-preview", "data-testid": `${testId}-teaser`, children: markdownPreview }) : null
2658
+ ] }) : null,
2659
+ isDone && expanded && loopTree ? /* @__PURE__ */ jsx8("div", { className: "lax-workflow-stage-container__body", "data-testid": `${testId}-body`, children: /* @__PURE__ */ jsx8(
2204
2660
  AgentLoopView,
2205
2661
  {
2206
2662
  tree: loopTree,
@@ -2209,7 +2665,15 @@ function WorkflowAggregateContainer({
2209
2665
  virtualizeThreshold,
2210
2666
  groupParallelTools
2211
2667
  }
2212
- ) }) : null
2668
+ ) }) : null,
2669
+ isDone && expanded && !loopTree && contentPreview ? /* @__PURE__ */ jsx8(
2670
+ "div",
2671
+ {
2672
+ className: "lax-workflow-stage-container__body lax-workflow-stage-container__content-preview",
2673
+ "data-testid": `${testId}-body`,
2674
+ children: markdownPreview
2675
+ }
2676
+ ) : null
2213
2677
  ]
2214
2678
  }
2215
2679
  );
@@ -2293,7 +2757,7 @@ function hasTimelineNodes(tree) {
2293
2757
  }
2294
2758
 
2295
2759
  // src/view/workflow/workflowStageDoneBody.tsx
2296
- import { jsx as jsx8 } from "react/jsx-runtime";
2760
+ import { jsx as jsx9 } from "react/jsx-runtime";
2297
2761
  function WorkflowStageDoneBody({
2298
2762
  loopTree,
2299
2763
  testId,
@@ -2303,7 +2767,7 @@ function WorkflowStageDoneBody({
2303
2767
  bodyClassName = "lax-workflow-stage-container__body"
2304
2768
  }) {
2305
2769
  if (loopTree && hasTimelineNodes(loopTree)) {
2306
- return /* @__PURE__ */ jsx8("div", { className: bodyClassName, "data-testid": testId, children: /* @__PURE__ */ jsx8(
2770
+ return /* @__PURE__ */ jsx9("div", { className: bodyClassName, "data-testid": testId, children: /* @__PURE__ */ jsx9(
2307
2771
  AgentLoopView,
2308
2772
  {
2309
2773
  tree: loopTree,
@@ -2314,12 +2778,12 @@ function WorkflowStageDoneBody({
2314
2778
  }
2315
2779
  ) });
2316
2780
  }
2317
- return /* @__PURE__ */ jsx8(
2781
+ return /* @__PURE__ */ jsx9(
2318
2782
  "div",
2319
2783
  {
2320
2784
  className: `${bodyClassName} lax-workflow-stage-container__empty-hint`,
2321
2785
  "data-testid": testId,
2322
- children: /* @__PURE__ */ jsx8("span", { className: "lax-workflow-stage-container__empty-hint-text", children: "\uFF08\u6B64\u9636\u6BB5\u6682\u65E0 Agent \u65F6\u95F4\u7EBF\u5185\u5BB9\uFF09" })
2786
+ children: /* @__PURE__ */ jsx9("span", { className: "lax-workflow-stage-container__empty-hint-text", children: "\uFF08\u6B64\u9636\u6BB5\u6682\u65E0 Agent \u65F6\u95F4\u7EBF\u5185\u5BB9\uFF09" })
2323
2787
  }
2324
2788
  );
2325
2789
  }
@@ -2363,7 +2827,7 @@ function isActiveWorkflowContainer(node, activeLoopSessionId, activeContainerIds
2363
2827
  }
2364
2828
 
2365
2829
  // src/view/workflow/WorkflowStageContainer.tsx
2366
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
2830
+ import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
2367
2831
  function containerTestId(node) {
2368
2832
  if (node.scope === "item") return `lax-workflow-parallel-${node.scope_key}`;
2369
2833
  if (node.scope === "stage") return `lax-workflow-stage-${node.scope_key}`;
@@ -2466,7 +2930,7 @@ function WorkflowStageContainer({
2466
2930
  "--lax-workflow-indent-unit": workflowIndentUnit(depth)
2467
2931
  },
2468
2932
  children: [
2469
- /* @__PURE__ */ jsx9(
2933
+ /* @__PURE__ */ jsx10(
2470
2934
  WorkflowContainerLine,
2471
2935
  {
2472
2936
  title: displayTitle,
@@ -2484,7 +2948,7 @@ function WorkflowStageContainer({
2484
2948
  }
2485
2949
  ),
2486
2950
  isDone && !expanded && (teaserText || expandHint) ? /* @__PURE__ */ jsxs5("div", { className: "lax-workflow-stage-container__done-teaser", children: [
2487
- teaserText ? /* @__PURE__ */ jsx9(
2951
+ teaserText ? /* @__PURE__ */ jsx10(
2488
2952
  "span",
2489
2953
  {
2490
2954
  className: "lax-workflow-stage-container__done-teaser-line",
@@ -2492,14 +2956,14 @@ function WorkflowStageContainer({
2492
2956
  children: `\u23BF ${teaserText}`
2493
2957
  }
2494
2958
  ) : null,
2495
- expandHint ? /* @__PURE__ */ jsx9("span", { className: "lax-workflow-expand-trigger__hint", children: expandHint }) : null
2959
+ expandHint ? /* @__PURE__ */ jsx10("span", { className: "lax-workflow-expand-trigger__hint", children: expandHint }) : null
2496
2960
  ] }) : null,
2497
- expanded && isRunning && !usesScopedLoop && node.content_blocks.length > 0 ? /* @__PURE__ */ jsx9(
2961
+ expanded && isRunning && !usesScopedLoop && node.content_blocks.length > 0 ? /* @__PURE__ */ jsx10(
2498
2962
  "div",
2499
2963
  {
2500
2964
  className: "lax-workflow-stage-container__body lax-workflow-stage-container__content-preview",
2501
2965
  "data-testid": bodyTestId(node),
2502
- children: node.content_blocks.map((block, index) => /* @__PURE__ */ jsx9(
2966
+ children: node.content_blocks.map((block, index) => /* @__PURE__ */ jsx10(
2503
2967
  "div",
2504
2968
  {
2505
2969
  className: "lax-workflow-stage-container__teaser",
@@ -2509,12 +2973,12 @@ function WorkflowStageContainer({
2509
2973
  ))
2510
2974
  }
2511
2975
  ) : null,
2512
- expanded && isRunning && runningLoopTree ? /* @__PURE__ */ jsx9(
2976
+ expanded && isRunning && runningLoopTree ? /* @__PURE__ */ jsx10(
2513
2977
  "div",
2514
2978
  {
2515
2979
  className: isItem ? "lax-workflow-parallel-item__body lax-workflow-stage-container__body" : "lax-workflow-stage-container__body",
2516
2980
  "data-testid": bodyTestId(node),
2517
- children: /* @__PURE__ */ jsx9(
2981
+ children: /* @__PURE__ */ jsx10(
2518
2982
  AgentLoopView,
2519
2983
  {
2520
2984
  tree: runningLoopTree,
@@ -2526,7 +2990,7 @@ function WorkflowStageContainer({
2526
2990
  )
2527
2991
  }
2528
2992
  ) : null,
2529
- isDone && expanded ? loopTree ? /* @__PURE__ */ jsx9(
2993
+ isDone && expanded ? loopTree ? /* @__PURE__ */ jsx10(
2530
2994
  WorkflowStageDoneBody,
2531
2995
  {
2532
2996
  node,
@@ -2543,8 +3007,8 @@ function WorkflowStageContainer({
2543
3007
  className: "lax-workflow-stage-container__body lax-workflow-loop-replay-pending",
2544
3008
  "data-testid": bodyTestId(node),
2545
3009
  children: [
2546
- teaserText ? /* @__PURE__ */ jsx9("p", { className: "lax-workflow-stage-container__teaser", children: teaserText }) : null,
2547
- /* @__PURE__ */ jsx9("p", { className: "lax-workflow-loop-replay-pending__hint", children: replayHandler ? "Loading loop details\u2026" : "Loop details evicted from memory." })
3010
+ teaserText ? /* @__PURE__ */ jsx10("p", { className: "lax-workflow-stage-container__teaser", children: teaserText }) : null,
3011
+ /* @__PURE__ */ jsx10("p", { className: "lax-workflow-loop-replay-pending__hint", children: replayHandler ? "Loading loop details\u2026" : "Loop details evicted from memory." })
2548
3012
  ]
2549
3013
  }
2550
3014
  ) : null
@@ -2554,7 +3018,7 @@ function WorkflowStageContainer({
2554
3018
  }
2555
3019
 
2556
3020
  // src/view/workflow/WorkflowParallelGroup.tsx
2557
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
3021
+ import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
2558
3022
  function resolveLoopTree(node, loopTreesBySessionId) {
2559
3023
  if (!node.loopSessionId) return void 0;
2560
3024
  return loopTreesBySessionId[node.loopSessionId];
@@ -2608,7 +3072,7 @@ function WorkflowParallelGroup({
2608
3072
  }, []);
2609
3073
  const renderItem = (index) => {
2610
3074
  const item = items[index];
2611
- return /* @__PURE__ */ jsx10(
3075
+ return /* @__PURE__ */ jsx11(
2612
3076
  WorkflowStageContainer,
2613
3077
  {
2614
3078
  node: item,
@@ -2627,7 +3091,7 @@ function WorkflowParallelGroup({
2627
3091
  className: "lax-workflow-parallel-group",
2628
3092
  "data-testid": `lax-workflow-parallel-group-${groupKey}`,
2629
3093
  children: [
2630
- /* @__PURE__ */ jsx10(
3094
+ /* @__PURE__ */ jsx11(
2631
3095
  WorkflowExpandTrigger,
2632
3096
  {
2633
3097
  summary,
@@ -2638,14 +3102,14 @@ function WorkflowParallelGroup({
2638
3102
  className: "lax-workflow-parallel-group__summary"
2639
3103
  }
2640
3104
  ),
2641
- expanded ? /* @__PURE__ */ jsx10("div", { className: "lax-workflow-parallel-list", "data-testid": "lax-workflow-parallel-list", children: items.map((_, index) => renderItem(index)) }) : null
3105
+ expanded ? /* @__PURE__ */ jsx11("div", { className: "lax-workflow-parallel-list", "data-testid": "lax-workflow-parallel-list", children: items.map((_, index) => renderItem(index)) }) : null
2642
3106
  ]
2643
3107
  }
2644
3108
  );
2645
3109
  }
2646
3110
 
2647
3111
  // src/view/workflow/WorkflowRootContainer.tsx
2648
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
3112
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
2649
3113
  function WorkflowRootContainer({
2650
3114
  node,
2651
3115
  children
@@ -2654,7 +3118,7 @@ function WorkflowRootContainer({
2654
3118
  const uiStatus = mapContainerUiStatus(node.status);
2655
3119
  const statusLabel = mapWorkflowRootStatusLabel(node.status);
2656
3120
  return /* @__PURE__ */ jsxs7("div", { className: "lax-workflow-root", "data-testid": "lax-workflow-root", children: [
2657
- /* @__PURE__ */ jsx11(
3121
+ /* @__PURE__ */ jsx12(
2658
3122
  WorkflowContainerLine,
2659
3123
  {
2660
3124
  variant: "root",
@@ -2669,13 +3133,13 @@ function WorkflowRootContainer({
2669
3133
  verbose
2670
3134
  }
2671
3135
  ),
2672
- children ? /* @__PURE__ */ jsx11("div", { className: "lax-workflow-container-tree", children }) : null
3136
+ children ? /* @__PURE__ */ jsx12("div", { className: "lax-workflow-container-tree", children }) : null
2673
3137
  ] });
2674
3138
  }
2675
3139
 
2676
3140
  // src/view/workflow/WorkflowRouteContainer.tsx
2677
3141
  import { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo5, useState as useState4 } from "react";
2678
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
3142
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
2679
3143
  function WorkflowRouteContainer({
2680
3144
  node,
2681
3145
  loopTree,
@@ -2721,7 +3185,7 @@ function WorkflowRouteContainer({
2721
3185
  "data-container-scope": "branch",
2722
3186
  style: depthStyle,
2723
3187
  children: [
2724
- /* @__PURE__ */ jsx12(
3188
+ /* @__PURE__ */ jsx13(
2725
3189
  WorkflowContainerLine,
2726
3190
  {
2727
3191
  title: node.title,
@@ -2736,7 +3200,7 @@ function WorkflowRouteContainer({
2736
3200
  descriptionTestId: `${testId}-desc`
2737
3201
  }
2738
3202
  ),
2739
- !isSkipped && expanded && isRunning && loopTree ? /* @__PURE__ */ jsx12("div", { className: "lax-workflow-stage-container__body", "data-testid": `${testId}-body`, children: /* @__PURE__ */ jsx12(
3203
+ !isSkipped && expanded && isRunning && loopTree ? /* @__PURE__ */ jsx13("div", { className: "lax-workflow-stage-container__body", "data-testid": `${testId}-body`, children: /* @__PURE__ */ jsx13(
2740
3204
  AgentLoopView,
2741
3205
  {
2742
3206
  tree: loopTree,
@@ -2746,7 +3210,7 @@ function WorkflowRouteContainer({
2746
3210
  groupParallelTools
2747
3211
  }
2748
3212
  ) }) : null,
2749
- !isSkipped && isDone && !expanded ? /* @__PURE__ */ jsx12(
3213
+ !isSkipped && isDone && !expanded ? /* @__PURE__ */ jsx13(
2750
3214
  WorkflowExpandTrigger,
2751
3215
  {
2752
3216
  summary: `${node.title} \xB7 ${doneLabel}`,
@@ -2758,7 +3222,7 @@ function WorkflowRouteContainer({
2758
3222
  teaserTestId: teaserText ? `${testId}-teaser` : void 0
2759
3223
  }
2760
3224
  ) : null,
2761
- !isSkipped && isDone && expanded ? /* @__PURE__ */ jsx12(
3225
+ !isSkipped && isDone && expanded ? /* @__PURE__ */ jsx13(
2762
3226
  WorkflowStageDoneBody,
2763
3227
  {
2764
3228
  node,
@@ -2776,7 +3240,7 @@ function WorkflowRouteContainer({
2776
3240
 
2777
3241
  // src/view/workflow/WorkflowSubworkflowContainer.tsx
2778
3242
  import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo6, useState as useState5 } from "react";
2779
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
3243
+ import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
2780
3244
  function buildDoneSummary(node) {
2781
3245
  const display = node.display;
2782
3246
  if (display?.progress_total != null) {
@@ -2822,7 +3286,7 @@ function WorkflowSubworkflowContainer({
2822
3286
  "data-container-scope": "subworkflow",
2823
3287
  style: depthStyle,
2824
3288
  children: [
2825
- showShellLine ? /* @__PURE__ */ jsx13(
3289
+ showShellLine ? /* @__PURE__ */ jsx14(
2826
3290
  WorkflowContainerLine,
2827
3291
  {
2828
3292
  variant: "root",
@@ -2840,7 +3304,7 @@ function WorkflowSubworkflowContainer({
2840
3304
  verbose
2841
3305
  }
2842
3306
  ) : null,
2843
- expanded && children ? /* @__PURE__ */ jsx13(
3307
+ expanded && children ? /* @__PURE__ */ jsx14(
2844
3308
  "div",
2845
3309
  {
2846
3310
  className: "lax-workflow-subworkflow-boundary",
@@ -2849,7 +3313,7 @@ function WorkflowSubworkflowContainer({
2849
3313
  children
2850
3314
  }
2851
3315
  ) : null,
2852
- isDone && !expanded ? /* @__PURE__ */ jsx13(
3316
+ isDone && !expanded ? /* @__PURE__ */ jsx14(
2853
3317
  WorkflowExpandTrigger,
2854
3318
  {
2855
3319
  summary: `${node.title} \xB7 ${buildDoneSummary(node)}`,
@@ -2867,7 +3331,7 @@ function WorkflowSubworkflowContainer({
2867
3331
  }
2868
3332
 
2869
3333
  // src/view/workflow/WorkflowChrome.tsx
2870
- import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
3334
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
2871
3335
  function collectChildContainers(containersById, parentId) {
2872
3336
  return Object.values(containersById).filter((node) => node.parent_container_id === parentId).sort((a, b) => a.scope_key.localeCompare(b.scope_key));
2873
3337
  }
@@ -2885,7 +3349,7 @@ function renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, v
2885
3349
  const loopTree = resolveLoopTree2(node, loopTreesBySessionId);
2886
3350
  switch (node.scope) {
2887
3351
  case "aggregate":
2888
- return /* @__PURE__ */ jsx14(
3352
+ return /* @__PURE__ */ jsx15(
2889
3353
  WorkflowAggregateContainer,
2890
3354
  {
2891
3355
  node,
@@ -2897,7 +3361,7 @@ function renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, v
2897
3361
  node.container_id
2898
3362
  );
2899
3363
  case "branch":
2900
- return /* @__PURE__ */ jsx14(
3364
+ return /* @__PURE__ */ jsx15(
2901
3365
  WorkflowRouteContainer,
2902
3366
  {
2903
3367
  node,
@@ -2908,7 +3372,7 @@ function renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, v
2908
3372
  node.container_id
2909
3373
  );
2910
3374
  default:
2911
- return /* @__PURE__ */ jsx14(
3375
+ return /* @__PURE__ */ jsx15(
2912
3376
  WorkflowStageContainer,
2913
3377
  {
2914
3378
  node,
@@ -2922,7 +3386,7 @@ function renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, v
2922
3386
  }
2923
3387
  function renderItemSiblings(items, groupKey, depth, containerTree, loopTreesBySessionId, viewProps) {
2924
3388
  if (items.length >= 2) {
2925
- return /* @__PURE__ */ jsx14(
3389
+ return /* @__PURE__ */ jsx15(
2926
3390
  WorkflowParallelGroup,
2927
3391
  {
2928
3392
  groupKey,
@@ -2957,7 +3421,7 @@ function renderContainerNode(node, containerTree, loopTreesBySessionId, viewProp
2957
3421
  );
2958
3422
  const hasNested = collectChildContainers(containerTree.containersById, node.container_id).length > 0;
2959
3423
  if (node.scope === "subworkflow") {
2960
- return /* @__PURE__ */ jsx14(
3424
+ return /* @__PURE__ */ jsx15(
2961
3425
  WorkflowSubworkflowContainer,
2962
3426
  {
2963
3427
  node,
@@ -2975,7 +3439,7 @@ function renderContainerNode(node, containerTree, loopTreesBySessionId, viewProp
2975
3439
  "data-testid": `lax-workflow-nested-${node.scope}-${node.scope_key}`,
2976
3440
  children: [
2977
3441
  renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, viewProps),
2978
- hasNested ? /* @__PURE__ */ jsx14("div", { className: "lax-workflow-container-children", children: nested }) : null
3442
+ hasNested ? /* @__PURE__ */ jsx15("div", { className: "lax-workflow-container-children", children: nested }) : null
2979
3443
  ]
2980
3444
  },
2981
3445
  node.container_id
@@ -3001,9 +3465,9 @@ function renderWorkflowStageRow(node, containerTree, loopTreesBySessionId, viewP
3001
3465
  viewProps
3002
3466
  );
3003
3467
  if (as === "div") {
3004
- return /* @__PURE__ */ jsx14("div", { className, role: "listitem", children: content }, node.container_id);
3468
+ return /* @__PURE__ */ jsx15("div", { className, role: "listitem", children: content }, node.container_id);
3005
3469
  }
3006
- return /* @__PURE__ */ jsx14("li", { className, children: content }, node.container_id);
3470
+ return /* @__PURE__ */ jsx15("li", { className, children: content }, node.container_id);
3007
3471
  }
3008
3472
  function WorkflowStageListPanel({
3009
3473
  stages,
@@ -3024,7 +3488,7 @@ function WorkflowStageListPanel({
3024
3488
  partition.totalDoneCount - partition.visibleDoneCount
3025
3489
  );
3026
3490
  return /* @__PURE__ */ jsxs10(Fragment2, { children: [
3027
- /* @__PURE__ */ jsx14("ol", { className: "lax-workflow-stage-list", "data-testid": "lax-workflow-stage-list", children: partition.visible.map(
3491
+ /* @__PURE__ */ jsx15("ol", { className: "lax-workflow-stage-list", "data-testid": "lax-workflow-stage-list", children: partition.visible.map(
3028
3492
  (node) => renderWorkflowStageRow(node, containerTree, loopTreesBySessionId, viewProps)
3029
3493
  ) }),
3030
3494
  partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs10(
@@ -3043,7 +3507,7 @@ function WorkflowStageListPanel({
3043
3507
  ]
3044
3508
  }
3045
3509
  ) : null,
3046
- extraCompletedPages > 0 && partition.hiddenCount === 0 ? /* @__PURE__ */ jsx14(
3510
+ extraCompletedPages > 0 && partition.hiddenCount === 0 ? /* @__PURE__ */ jsx15(
3047
3511
  "button",
3048
3512
  {
3049
3513
  type: "button",
@@ -3079,7 +3543,7 @@ function renderWorkflowChildren(workflowNode, containerTree, loopTreesBySessionI
3079
3543
  (node) => node.scope !== "stage" && node.scope !== "item"
3080
3544
  );
3081
3545
  return /* @__PURE__ */ jsxs10(Fragment2, { children: [
3082
- stages.length > 0 ? /* @__PURE__ */ jsx14(
3546
+ stages.length > 0 ? /* @__PURE__ */ jsx15(
3083
3547
  WorkflowStageListPanel,
3084
3548
  {
3085
3549
  stages,
@@ -3104,7 +3568,7 @@ function WorkflowChrome({
3104
3568
  const viewProps = { virtualized, virtualizeThreshold, groupParallelTools };
3105
3569
  const rootNodes = containerTree.rootContainerIds.map((id) => containerTree.containersById[id]).filter((node) => node != null);
3106
3570
  if (rootNodes.length === 0) {
3107
- return /* @__PURE__ */ jsx14("div", { className: "lax-workflow-chrome", "data-testid": "lax-workflow-chrome", children: /* @__PURE__ */ jsx14(
3571
+ return /* @__PURE__ */ jsx15("div", { className: "lax-workflow-chrome", "data-testid": "lax-workflow-chrome", children: /* @__PURE__ */ jsx15(
3108
3572
  WorkflowContainerLine,
3109
3573
  {
3110
3574
  variant: "root",
@@ -3114,7 +3578,7 @@ function WorkflowChrome({
3114
3578
  }
3115
3579
  ) });
3116
3580
  }
3117
- return /* @__PURE__ */ jsx14("div", { className: "lax-workflow-chrome", "data-testid": "lax-workflow-chrome", children: rootNodes.map((workflowNode) => /* @__PURE__ */ jsx14(WorkflowRootContainer, { node: workflowNode, children: renderWorkflowChildren(
3581
+ return /* @__PURE__ */ jsx15("div", { className: "lax-workflow-chrome", "data-testid": "lax-workflow-chrome", children: rootNodes.map((workflowNode) => /* @__PURE__ */ jsx15(WorkflowRootContainer, { node: workflowNode, children: renderWorkflowChildren(
3118
3582
  workflowNode,
3119
3583
  containerTree,
3120
3584
  loopTreesBySessionId,
@@ -3195,11 +3659,11 @@ function loopSessionDisplayLabel(loopSessionId) {
3195
3659
  }
3196
3660
 
3197
3661
  // src/view/workflow/WorkflowTaskListFooter.tsx
3198
- import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
3662
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
3199
3663
  function MessageResponse({ children }) {
3200
3664
  return /* @__PURE__ */ jsxs11("div", { className: "lax-message-response", children: [
3201
- /* @__PURE__ */ jsx15("span", { className: "lax-message-response__marker", children: "\u23BF " }),
3202
- /* @__PURE__ */ jsx15("span", { className: "lax-message-response__content", children })
3665
+ /* @__PURE__ */ jsx16("span", { className: "lax-message-response__marker", children: "\u23BF " }),
3666
+ /* @__PURE__ */ jsx16("span", { className: "lax-message-response__content", children })
3203
3667
  ] });
3204
3668
  }
3205
3669
  function WorkflowTaskListFooter() {
@@ -3224,18 +3688,18 @@ function WorkflowTaskListFooter() {
3224
3688
  onClick: () => setExpanded((v) => !v),
3225
3689
  children: [
3226
3690
  label,
3227
- /* @__PURE__ */ jsx15("span", { className: "lax-task-list-footer__hint", children: expanded ? " \xB7 \u2191 to hide" : " \xB7 \u2193 to view" })
3691
+ /* @__PURE__ */ jsx16("span", { className: "lax-task-list-footer__hint", children: expanded ? " \xB7 \u2191 to hide" : " \xB7 \u2193 to view" })
3228
3692
  ]
3229
3693
  }
3230
3694
  ),
3231
- expanded ? /* @__PURE__ */ jsx15(MessageResponse, { children: /* @__PURE__ */ jsx15("div", { className: "lax-workflow-task-list-groups", children: groups.map((group) => /* @__PURE__ */ jsxs11(
3695
+ expanded ? /* @__PURE__ */ jsx16(MessageResponse, { children: /* @__PURE__ */ jsx16("div", { className: "lax-workflow-task-list-groups", children: groups.map((group) => /* @__PURE__ */ jsxs11(
3232
3696
  "div",
3233
3697
  {
3234
3698
  className: "lax-workflow-task-list-group",
3235
3699
  "data-testid": `lax-workflow-task-group-${group.loopSessionId}`,
3236
3700
  children: [
3237
- /* @__PURE__ */ jsx15("div", { className: "lax-workflow-task-list-group-title", children: loopSessionDisplayLabel(group.loopSessionId) }),
3238
- /* @__PURE__ */ jsx15(TaskList, { tasks: group.tasks })
3701
+ /* @__PURE__ */ jsx16("div", { className: "lax-workflow-task-list-group-title", children: loopSessionDisplayLabel(group.loopSessionId) }),
3702
+ /* @__PURE__ */ jsx16(TaskList, { tasks: group.tasks })
3239
3703
  ]
3240
3704
  },
3241
3705
  group.loopSessionId
@@ -3245,7 +3709,7 @@ function WorkflowTaskListFooter() {
3245
3709
 
3246
3710
  // src/view/workflow/WorkflowSession.tsx
3247
3711
  import { useStore as useStore5 } from "zustand";
3248
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
3712
+ import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
3249
3713
  function WorkflowSession({
3250
3714
  source,
3251
3715
  initialEvents,
@@ -3341,13 +3805,13 @@ function WorkflowSession({
3341
3805
  controller.abort();
3342
3806
  };
3343
3807
  }, [source, onError]);
3344
- return /* @__PURE__ */ jsx16(WorkflowSessionStoreContext.Provider, { value: storeRef.current, children: /* @__PURE__ */ jsx16(WorkflowScaleContext.Provider, { value: scaleOptions, children: /* @__PURE__ */ jsx16(WorkflowLoopReplayProvider, { onRequestLoopReplay, children: /* @__PURE__ */ jsx16(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx16(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx16(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx16(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx16(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs12(
3808
+ return /* @__PURE__ */ jsx17(WorkflowSessionStoreContext.Provider, { value: storeRef.current, children: /* @__PURE__ */ jsx17(WorkflowScaleContext.Provider, { value: scaleOptions, children: /* @__PURE__ */ jsx17(WorkflowLoopReplayProvider, { onRequestLoopReplay, children: /* @__PURE__ */ jsx17(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx17(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx17(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx17(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx17(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs12(
3345
3809
  "div",
3346
3810
  {
3347
3811
  className: "lax-agent-session lax-workflow-session",
3348
3812
  "data-testid": "lax-workflow-session",
3349
3813
  children: [
3350
- /* @__PURE__ */ jsx16(
3814
+ /* @__PURE__ */ jsx17(
3351
3815
  WorkflowChrome,
3352
3816
  {
3353
3817
  containerTree,
@@ -3357,7 +3821,7 @@ function WorkflowSession({
3357
3821
  groupParallelTools
3358
3822
  }
3359
3823
  ),
3360
- /* @__PURE__ */ jsx16(WorkflowTaskListFooter, {}),
3824
+ /* @__PURE__ */ jsx17(WorkflowTaskListFooter, {}),
3361
3825
  children
3362
3826
  ]
3363
3827
  }
@@ -3401,7 +3865,7 @@ function buildTimelineEntries(rootIds, byId, options = {}) {
3401
3865
  }
3402
3866
 
3403
3867
  // src/view/nodes/SubAgentNode.tsx
3404
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
3868
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
3405
3869
  function SubAgentBlock({ nodeId }) {
3406
3870
  const node = useNodeTyped(nodeId, "subagent");
3407
3871
  const childIds = useChildren(nodeId);
@@ -3412,7 +3876,7 @@ function SubAgentBlock({ nodeId }) {
3412
3876
  "SubAgent: ",
3413
3877
  node.subagentId
3414
3878
  ] }),
3415
- /* @__PURE__ */ jsx17("div", { className: "lax-subagent-block__children", children: childIds.map((childId) => /* @__PURE__ */ jsx17(TimelineItem, { nodeId: childId, registry }, childId)) })
3879
+ /* @__PURE__ */ jsx18("div", { className: "lax-subagent-block__children", children: childIds.map((childId) => /* @__PURE__ */ jsx18(TimelineItem, { nodeId: childId, registry }, childId)) })
3416
3880
  ] });
3417
3881
  }
3418
3882
 
@@ -3464,6 +3928,8 @@ export {
3464
3928
  DEFAULT_VIRTUALIZE_THRESHOLD,
3465
3929
  DEFAULT_WORKFLOW_SCALE_OPTIONS,
3466
3930
  DefaultToolBody,
3931
+ DiagramZoomShell,
3932
+ DiagramZoomView,
3467
3933
  DiffView,
3468
3934
  EXPECTED_PROTOCOL_VERSION,
3469
3935
  Edit,
@@ -3536,6 +4002,7 @@ export {
3536
4002
  buildTimelineEntries,
3537
4003
  classifyEvent,
3538
4004
  commandAsHint,
4005
+ computeViewportFitTransform,
3539
4006
  configureMermaid,
3540
4007
  configurePlantumlServer,
3541
4008
  createActiveSessionBridge,
@@ -3554,6 +4021,7 @@ export {
3554
4021
  displayPath,
3555
4022
  entryKey,
3556
4023
  filterAgentLoopReplayEvents,
4024
+ fixDiagramSvg,
3557
4025
  formatAgentTitle,
3558
4026
  formatDiffFromStrings,
3559
4027
  formatExploreSummaryText,