@sleep2agi/agent-network-dashboard 0.5.3-preview.261 → 0.5.3-preview.263

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 (141) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +5 -5
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/server/app/_global-error.html +1 -1
  6. package/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found.html +1 -1
  13. package/.next/server/app/_not-found.rsc +1 -1
  14. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  20. package/.next/server/app/admin.html +1 -1
  21. package/.next/server/app/admin.rsc +1 -1
  22. package/.next/server/app/admin.segments/_full.segment.rsc +1 -1
  23. package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
  24. package/.next/server/app/admin.segments/_index.segment.rsc +1 -1
  25. package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
  26. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
  27. package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
  28. package/.next/server/app/index.html +2 -2
  29. package/.next/server/app/index.rsc +2 -2
  30. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  31. package/.next/server/app/index.segments/_full.segment.rsc +2 -2
  32. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  33. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  34. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  35. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  36. package/.next/server/app/login.html +2 -2
  37. package/.next/server/app/login.rsc +2 -2
  38. package/.next/server/app/login.segments/_full.segment.rsc +2 -2
  39. package/.next/server/app/login.segments/_head.segment.rsc +1 -1
  40. package/.next/server/app/login.segments/_index.segment.rsc +1 -1
  41. package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
  42. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
  43. package/.next/server/app/login.segments/login.segment.rsc +1 -1
  44. package/.next/server/app/logs.html +1 -1
  45. package/.next/server/app/logs.rsc +1 -1
  46. package/.next/server/app/logs.segments/_full.segment.rsc +1 -1
  47. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  48. package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
  49. package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
  50. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
  51. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  52. package/.next/server/app/messages.html +1 -1
  53. package/.next/server/app/messages.rsc +1 -1
  54. package/.next/server/app/messages.segments/_full.segment.rsc +1 -1
  55. package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
  56. package/.next/server/app/messages.segments/_index.segment.rsc +1 -1
  57. package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
  58. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
  59. package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
  60. package/.next/server/app/node.html +1 -1
  61. package/.next/server/app/node.rsc +1 -1
  62. package/.next/server/app/node.segments/_full.segment.rsc +1 -1
  63. package/.next/server/app/node.segments/_head.segment.rsc +1 -1
  64. package/.next/server/app/node.segments/_index.segment.rsc +1 -1
  65. package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
  66. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
  67. package/.next/server/app/node.segments/node.segment.rsc +1 -1
  68. package/.next/server/app/nodes.html +1 -1
  69. package/.next/server/app/nodes.rsc +1 -1
  70. package/.next/server/app/nodes.segments/_full.segment.rsc +1 -1
  71. package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
  72. package/.next/server/app/nodes.segments/_index.segment.rsc +1 -1
  73. package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
  74. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
  75. package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
  76. package/.next/server/app/page_client-reference-manifest.js +1 -1
  77. package/.next/server/app/server-logs.html +1 -1
  78. package/.next/server/app/server-logs.rsc +1 -1
  79. package/.next/server/app/server-logs.segments/_full.segment.rsc +1 -1
  80. package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
  81. package/.next/server/app/server-logs.segments/_index.segment.rsc +1 -1
  82. package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
  83. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
  84. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
  85. package/.next/server/app/settings/networks.html +1 -1
  86. package/.next/server/app/settings/networks.rsc +1 -1
  87. package/.next/server/app/settings/networks.segments/_full.segment.rsc +1 -1
  88. package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
  89. package/.next/server/app/settings/networks.segments/_index.segment.rsc +1 -1
  90. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
  91. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
  92. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
  93. package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
  94. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/settings/tokens.html +1 -1
  96. package/.next/server/app/settings/tokens.rsc +1 -1
  97. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +1 -1
  98. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
  99. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +1 -1
  100. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
  101. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
  102. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
  103. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
  104. package/.next/server/app/settings.html +2 -2
  105. package/.next/server/app/settings.rsc +2 -2
  106. package/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  107. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  108. package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  109. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  110. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  111. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  112. package/.next/server/app/tasks.html +1 -1
  113. package/.next/server/app/tasks.rsc +1 -1
  114. package/.next/server/app/tasks.segments/_full.segment.rsc +1 -1
  115. package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
  116. package/.next/server/app/tasks.segments/_index.segment.rsc +1 -1
  117. package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
  118. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
  119. package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
  120. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  121. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  122. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +1 -1
  123. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  124. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  125. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  126. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  127. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  128. package/.next/server/middleware-build-manifest.js +3 -3
  129. package/.next/server/pages/404.html +1 -1
  130. package/.next/server/pages/500.html +1 -1
  131. package/.next/static/chunks/{0ztakmtfxkgya.js → 02t4qx0vnsbnj.js} +1 -1
  132. package/.next/static/chunks/{0nd-y~i5proep.js → 0_jsqyukmaf5a.js} +1 -1
  133. package/.next/static/chunks/{0-vlr8~b0f-cx.js → 0hf7fag69h3.7.js} +1 -1
  134. package/.next/static/chunks/{08.pwokcpknmp.js → 11gdlkf54jqv9.js} +1 -1
  135. package/.next/trace +2 -2
  136. package/.next/trace-build +1 -1
  137. package/app/components/TopoGraph.tsx +111 -148
  138. package/package.json +4 -4
  139. /package/.next/static/{CLsgsYpGMRUiIcJJK3xkR → IoxDnk-zAN2bqUJxKcKN-}/_buildManifest.js +0 -0
  140. /package/.next/static/{CLsgsYpGMRUiIcJJK3xkR → IoxDnk-zAN2bqUJxKcKN-}/_clientMiddlewareManifest.js +0 -0
  141. /package/.next/static/{CLsgsYpGMRUiIcJJK3xkR → IoxDnk-zAN2bqUJxKcKN-}/_ssgManifest.js +0 -0
@@ -1024,180 +1024,135 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
1024
1024
  return { key, lead, deputy, members: others };
1025
1025
  });
1026
1026
 
1027
- // ---- box-based org layout (Vincent #170 feedback) ----------------
1028
- // Each team is a rectangular BOX (reusing the grid-mode cluster-box
1029
- // visual via groupBoxes); its members pack into a compact ~√n grid
1030
- // INSIDE the box rather than one wide row an 8-person team is a
1031
- // ~3×3 block, not a 1×8 strip. Boxes sit side-by-side at layer 2;
1032
- // 副指挥 (layer 1) centre over their contiguous box-run; the root
1033
- // (layer 0) centres over the 副指挥. Keeps the whole chart compact
1034
- // and screenshot-friendly even at dozens of nodes.
1027
+ // ---- swimlane org layout (Vincent dispatch: "一行一个团队,成员
1028
+ // 不换行,最左边放总指挥和副指挥") ------------------------------------
1029
+ // Each team is ONE horizontal lane a wide box whose members sit in
1030
+ // a single NON-WRAPPING row. Lanes stack top-to-bottom (one team per
1031
+ // row). The command layer (总指挥 + 副指挥) is pinned in its own box
1032
+ // down the left edge. Reverses the iter1–3 2D box-grid; the iter5
1033
+ // treeContentBox auto-fit still scales + centres the whole chart.
1035
1034
  const nodeR = Math.round(26 * nodeScale);
1036
- const cellW = Math.max(108, 2 * nodeR + 56); // member grid cell fits a label card
1037
- const cellH = Math.max(96, 2 * nodeR + 48); // + label drop room below the node
1038
- const BOX_PAD = 16; // box inner padding
1035
+ const cellW = Math.max(118, 2 * nodeR + 66); // member slotnode + dense label
1039
1036
  const BOX_LABEL = 26; // box top label band
1040
- const BOX_GAP = 48; // gap between sibling boxes
1041
- // iter5 (Vincent UX tree presentation): the two header layers
1042
- // (总指挥 / 副指挥) hold single small nodes; the iter-2 ROW=116 gap
1043
- // gave them ~362px of pure header before the first team box. HEAD_ROW
1044
- // tightens those two layer gaps still clears node radius + label
1045
- // drop + connector elbow (needs >72px; 88 leaves a safe margin) but
1046
- // trims header bloat so the box-layout auto-fit lands at a larger
1047
- // zoom. The team-box rows below keep their own spacing untouched.
1048
- const HEAD_ROW = Math.max(88, nodeR + 62); // header layer gap (iter5)
1049
- const TOP = 130; // y of layer-0 root (clears corner panels)
1050
- const LEFT = 70;
1051
-
1052
- // a "box unit" = a team (or the 未分组 bucket) drawn as a grid box.
1053
- type BoxUnit = {
1054
- key: string; isOrphan: boolean;
1055
- members: Session[]; // grid order: lead, deputy, …
1056
- cols: number; rows: number; w: number; h: number;
1057
- x: number; y: number; // top-left, filled below
1058
- };
1059
- const mkBox = (key: string, isOrphan: boolean, members: Session[]): BoxUnit => {
1060
- const n = Math.max(1, members.length);
1061
- const cols = Math.ceil(Math.sqrt(n));
1062
- const rows = Math.ceil(n / cols);
1063
- return {
1064
- key, isOrphan, members, cols, rows,
1065
- w: cols * cellW + 2 * BOX_PAD,
1066
- h: rows * cellH + 2 * BOX_PAD + BOX_LABEL,
1067
- x: 0, y: 0,
1068
- };
1069
- };
1070
- const boxes: BoxUnit[] = teamTrees.map(tt =>
1071
- mkBox(tt.key, false, [tt.lead, ...(tt.deputy ? [tt.deputy] : []), ...tt.members]));
1072
- // orphan bucket — every underivable agent in its own 未分组 box so
1073
- // none are ever dropped from the chart.
1037
+ const LANE_BODY = Math.max(82, 2 * nodeR + 36);// member-row band height
1038
+ const LANE_H = BOX_LABEL + LANE_BODY; // full lane box height
1039
+ const LANE_GAP = 16; // vertical gap between lanes
1040
+ const BOX_PAD = 14; // lane inner side padding
1041
+ const CMD_W = Math.max(150, 2 * nodeR + 100); // command column width
1042
+ const CMD_GAP = 44; // gap: command column lanes
1043
+ const CMD_SLOT = Math.max(116, 2 * nodeR + 64);// command node vertical pitch
1044
+ const TOP = 92;
1045
+ const LEFT = 44;
1046
+ const laneX0 = LEFT + CMD_W + CMD_GAP; // x where team lanes start
1047
+
1048
+ // ordered lanes: one per team (teamTrees is already alias-sorted) +
1049
+ // a trailing 未分组 lane collecting every orphan so none are dropped.
1050
+ type Lane = { key: string; isOrphan: boolean; members: Session[] };
1051
+ const lanes: Lane[] = teamTrees.map(tt => ({
1052
+ key: tt.key, isOrphan: false,
1053
+ members: [tt.lead, ...(tt.deputy ? [tt.deputy] : []), ...tt.members],
1054
+ }));
1074
1055
  if (orphans.length > 0) {
1075
- boxes.push(mkBox('未分组', true,
1076
- [...orphans].sort((a, b) => a.alias.localeCompare(b.alias))));
1077
- }
1078
-
1079
- // lay boxes into a 2D wrapped grid (Vincent iter 3: team boxes need
1080
- // not sit in one long row — wrap into rows so the chart stays
1081
- // compact / screenshot-friendly even with many teams).
1082
- const layer2Y = TOP + 2 * HEAD_ROW;
1083
- const BOX_ROW_GAP = 40; // vertical gap between box rows
1084
- const ROW_W_TARGET = 1240; // wrap once a row would exceed this
1085
- let rowX = LEFT, rowY = layer2Y, rowMaxH = 0, rowCount = 0;
1086
- let lastRowBottom = layer2Y;
1087
- for (const b of boxes) {
1088
- if (rowCount > 0 && rowX + b.w > LEFT + ROW_W_TARGET) {
1089
- rowY += rowMaxH + BOX_ROW_GAP; // wrap to next box row
1090
- rowX = LEFT; rowMaxH = 0; rowCount = 0;
1091
- }
1092
- b.x = rowX; b.y = rowY;
1093
- rowX += b.w + BOX_GAP;
1094
- rowMaxH = Math.max(rowMaxH, b.h);
1095
- rowCount++;
1096
- lastRowBottom = rowY + rowMaxH;
1056
+ lanes.push({
1057
+ key: '未分组', isOrphan: true,
1058
+ members: [...orphans].sort((a, b) => a.alias.localeCompare(b.alias)),
1059
+ });
1097
1060
  }
1098
- // member positions compact grid inside each box.
1099
- for (const b of boxes) {
1100
- b.members.forEach((s, i) => {
1101
- const col = i % b.cols;
1102
- const row = Math.floor(i / b.cols);
1061
+ // Vincent feedback: stack lanes longest-team-first sort by member
1062
+ // count DESCENDING so the swimlane steps down long → short. A stable
1063
+ // secondary sort by key keeps equal-length lanes from jittering
1064
+ // between renders.
1065
+ lanes.sort((a, b) => b.members.length - a.members.length || a.key.localeCompare(b.key));
1066
+
1067
+ // each lane = a wide box; its members in a single row inside it.
1068
+ const laneStep = LANE_H + LANE_GAP;
1069
+ const laneBoxes = lanes.map((lane, i) => {
1070
+ const y = TOP + i * laneStep;
1071
+ const w = 2 * BOX_PAD + Math.max(1, lane.members.length) * cellW;
1072
+ lane.members.forEach((s, j) => {
1103
1073
  positions[s.alias] = {
1104
- x: b.x + BOX_PAD + (col + 0.5) * cellW,
1105
- y: b.y + BOX_LABEL + BOX_PAD + (row + 0.5) * cellH,
1074
+ x: laneX0 + BOX_PAD + (j + 0.5) * cellW,
1075
+ y: y + BOX_LABEL + LANE_BODY / 2,
1106
1076
  };
1107
1077
  });
1108
- }
1109
- const boxCentreX = (b: BoxUnit) => b.x + b.w / 2;
1110
-
1111
- // layer 1: 副指挥, each centred over a contiguous run of boxes.
1112
- const layer1Y = TOP + HEAD_ROW;
1113
- const depNodes: { alias: string; x: number; y: number }[] = [];
1114
- const depPer = deputyCommanders.length > 0
1115
- ? Math.ceil(Math.max(boxes.length, 1) / deputyCommanders.length)
1116
- : 0;
1117
- deputyCommanders.forEach((s, di) => {
1118
- const run = boxes.slice(di * depPer, di * depPer + depPer);
1119
- const xs = run.length ? run.map(boxCentreX)
1120
- : [LEFT + di * (cellW + BOX_GAP) + cellW / 2];
1121
- const x = (Math.min(...xs) + Math.max(...xs)) / 2;
1122
- depNodes.push({ alias: s.alias, x, y: layer1Y });
1123
- positions[s.alias] = { x, y: layer1Y };
1078
+ return { lane, x: laneX0, y, w, h: LANE_H };
1124
1079
  });
1125
-
1126
- // layer 0: root single 总指挥, else a synthetic 指挥中心.
1127
- const anchorXs = depNodes.length > 0 ? depNodes.map(d => d.x)
1128
- : boxes.length > 0 ? boxes.map(boxCentreX)
1129
- : [LEFT + cellW / 2];
1130
- const rootX = (Math.min(...anchorXs) + Math.max(...anchorXs)) / 2;
1131
- let synthRoot = false;
1132
- if (commanders.length === 1) {
1133
- positions[commanders[0].alias] = { x: rootX, y: TOP };
1134
- } else {
1135
- synthRoot = true;
1136
- // any 总指挥 (0, or >1) spread along layer 0 beside the synth anchor
1137
- commanders.forEach((c, ci) => {
1138
- positions[c.alias] = {
1139
- x: rootX + (ci - (commanders.length - 1) / 2) * (cellW + 28),
1140
- y: TOP,
1080
+ const laneBlockH = lanes.length ? lanes.length * laneStep - LANE_GAP : LANE_H;
1081
+ const laneBlockBottom = TOP + laneBlockH;
1082
+
1083
+ // command column 总指挥 + 副指挥 stacked in a compact box pinned to
1084
+ // the far left, vertically centred against the lane block.
1085
+ const cmdMembers: Session[] = [...commanders, ...deputyCommanders];
1086
+ const cmdX = LEFT + CMD_W / 2;
1087
+ const cmdH = cmdMembers.length > 0 ? BOX_LABEL + cmdMembers.length * CMD_SLOT : 0;
1088
+ const cmdBox = cmdMembers.length > 0
1089
+ ? { x: LEFT, y: TOP + Math.max(0, (laneBlockH - cmdH) / 2), w: CMD_W, h: cmdH }
1090
+ : null;
1091
+ if (cmdBox) {
1092
+ cmdMembers.forEach((s, i) => {
1093
+ positions[s.alias] = {
1094
+ x: cmdX,
1095
+ y: cmdBox.y + BOX_LABEL + (i + 0.5) * CMD_SLOT,
1141
1096
  };
1142
1097
  });
1143
1098
  }
1144
1099
 
1145
- // elbow connectors: root 副指挥 team-box top edge (render draws
1146
- // the right-angle path behind the nodes).
1100
+ // org link: a light connector from the command column to each lane.
1147
1101
  const connectors: TreeConnectorLine[] = [];
1148
- for (const d of depNodes) {
1149
- connectors.push({ x1: rootX, y1: TOP, x2: d.x, y2: d.y });
1150
- }
1151
- boxes.forEach((b, bi) => {
1152
- let px = rootX, py = TOP;
1153
- if (depNodes.length > 0) {
1154
- const di = Math.min(depNodes.length - 1, Math.floor(bi / Math.max(depPer, 1)));
1155
- px = depNodes[di].x; py = depNodes[di].y;
1102
+ if (cmdBox) {
1103
+ const cmdRight = cmdBox.x + cmdBox.w;
1104
+ const cmdMidY = cmdBox.y + cmdBox.h / 2;
1105
+ for (const lb of laneBoxes) {
1106
+ connectors.push({ x1: cmdRight, y1: cmdMidY, x2: lb.x, y2: lb.y + LANE_H / 2 });
1156
1107
  }
1157
- connectors.push({ x1: px, y1: py, x2: boxCentreX(b), y2: b.y });
1158
- });
1108
+ }
1159
1109
 
1160
- // synthetic root label (only when there is no single 总指挥)
1161
- const synthBoxes = synthRoot ? [{ x: rootX, y: TOP, label: '指挥中心' }] : [];
1110
+ // swimlane has no synthetic root chip the command box (labelled
1111
+ // 指挥中心 when there is no single 总指挥) is the command layer.
1112
+ const synthRoot = false;
1113
+ const synthBoxes: { x: number; y: number; label: string }[] = [];
1162
1114
 
1163
- // team boxes surfaced as groupBoxes — reuses the grid-mode cluster
1115
+ // lanes + command column surfaced as groupBoxes — reuses the cluster
1164
1116
  // box render + per-box working/idle/offline pip strip.
1165
- const teamGroupBoxes = boxes.map(b => {
1117
+ const tally = (members: Session[]) => {
1166
1118
  let w = 0, i = 0, o = 0;
1167
- for (const s of b.members) {
1119
+ for (const s of members) {
1168
1120
  const isOn = s.status !== 'offline' || !!sseCount(s);
1169
1121
  if (s.status === 'working') w++;
1170
1122
  else if (isOn) i++;
1171
1123
  else o++;
1172
1124
  }
1173
- return {
1174
- key: b.key, isOrphan: b.isOrphan, count: b.members.length,
1175
- statuses: { working: w, idle: i, offline: o },
1176
- x: b.x, y: b.y, w: b.w, h: b.h,
1177
- };
1178
- });
1179
-
1180
- // tree mode is a clean org chart (Vincent iter 4): message flow-links
1181
- // between nodes in different team boxes tangle as long curves across
1182
- // the hierarchy and bury the structure. Suppress them in tree — the
1183
- // right-angle report connectors (treeConnectors) carry the org lines.
1184
-
1185
- // content bottom for auto-fit zoom — bottom of the LAST box row.
1186
- // Boxes wrap into multiple rows (iter 3), so this must track the
1187
- // wrapped lastRowBottom, not layer2Y + tallest-box.
1188
- const treeBottom = boxes.length ? lastRowBottom : TOP + HEAD_ROW;
1125
+ return { working: w, idle: i, offline: o };
1126
+ };
1127
+ const teamGroupBoxes = [
1128
+ ...(cmdBox ? [{
1129
+ key: commanders.length === 1 ? commanders[0].alias : '指挥中心',
1130
+ isOrphan: false, count: cmdMembers.length,
1131
+ statuses: tally(cmdMembers),
1132
+ x: cmdBox.x, y: cmdBox.y, w: cmdBox.w, h: cmdBox.h,
1133
+ }] : []),
1134
+ ...laneBoxes.map(lb => ({
1135
+ key: lb.lane.key, isOrphan: lb.lane.isOrphan,
1136
+ count: lb.lane.members.length,
1137
+ statuses: tally(lb.lane.members),
1138
+ x: lb.x, y: lb.y, w: lb.w, h: lb.h,
1139
+ })),
1140
+ ];
1141
+
1142
+ // content bottom for auto-fit — bottom of the lowest box.
1143
+ const treeBottom = Math.max(
1144
+ laneBlockBottom,
1145
+ cmdBox ? cmdBox.y + cmdBox.h : TOP + LANE_H,
1146
+ );
1189
1147
  const gridContentBottom = treeBottom + 48;
1190
1148
 
1191
- // iter5 (Vincent UX): full content bounding box over every team box
1192
- // + every node. The auto-fit effect uses this to scale the org chart
1193
- // to fit BOTH viewBox dimensions AND centre it. Pre-iter5 auto-fit
1194
- // only fit height and left-anchored the chart (LEFT=70), so the tree
1195
- // hugged the top-left corner with dead canvas down the right side.
1149
+ // iter5 (Vincent UX): full content bounding box the auto-fit effect
1150
+ // scales the chart to fit BOTH viewBox dimensions AND centres it.
1196
1151
  const treeBX: number[] = [], treeBY: number[] = [];
1197
- for (const b of boxes) { treeBX.push(b.x, b.x + b.w); treeBY.push(b.y, b.y + b.h); }
1152
+ for (const gb of teamGroupBoxes) { treeBX.push(gb.x, gb.x + gb.w); treeBY.push(gb.y, gb.y + gb.h); }
1198
1153
  for (const a of Object.keys(positions)) { treeBX.push(positions[a].x); treeBY.push(positions[a].y); }
1199
1154
  // TREE_M pads the raw box/node extent so node radii + label drops
1200
- // (root/副指挥 sit above the boxes) never clip at the fitted edge.
1155
+ // never clip at the fitted edge.
1201
1156
  const TREE_M = 52;
1202
1157
  const treeContentBox: { x: number; y: number; w: number; h: number } | null = treeBX.length
1203
1158
  ? {
@@ -1208,12 +1163,20 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
1208
1163
  }
1209
1164
  : null;
1210
1165
 
1166
+ // Vincent feedback: restore the agent↔agent message flow-links in
1167
+ // tree/swimlane. iter4 (a6689b6) suppressed them as "杂线"; Vincent
1168
+ // now explicitly wants them back — same buildFlowLinks the ring/grid
1169
+ // layouts use, drawn against the swimlane node positions.
1170
+ const links = buildFlowLinks(messages, positions);
1171
+ const active = new Set<string>();
1172
+ links.forEach(link => { active.add(link.from); active.add(link.to); });
1173
+
1211
1174
  return {
1212
1175
  onlineNodes: online,
1213
1176
  offlineNodes: offline,
1214
1177
  nodePositions: positions,
1215
- flowLinks: [],
1216
- activeAliases: new Set<string>(),
1178
+ flowLinks: links,
1179
+ activeAliases: active,
1217
1180
  groupKeys,
1218
1181
  groupBoxes: teamGroupBoxes,
1219
1182
  gridContentBottom,
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network-dashboard",
3
- "version": "0.5.3-preview.261",
4
- "description": "Agent Network Dashboard Web UI for managing AI Agent networks",
3
+ "version": "0.5.3-preview.263",
4
+ "description": "Agent Network Dashboard \u2014 Web UI for managing AI Agent networks",
5
5
  "scripts": {
6
6
  "dev": "next dev",
7
7
  "build": "next build",
8
8
  "start": "next start",
9
9
  "lint": "eslint",
10
- "prepublishOnly": "[ -f .next/BUILD_ID ] || (echo 'prepublishOnly: .next/BUILD_ID missing run npm run build first (see commit 05c1ebf body for R224 chunk-500 root cause)' >&2 && exit 1)"
10
+ "prepublishOnly": "[ -f .next/BUILD_ID ] || (echo 'prepublishOnly: .next/BUILD_ID missing \u2014 run npm run build first (see commit 05c1ebf body for R224 chunk-500 root cause)' >&2 && exit 1)"
11
11
  },
12
12
  "bin": {
13
13
  "agent-network-dashboard": "./bin/start.js"
@@ -44,4 +44,4 @@
44
44
  "tailwindcss": "^4",
45
45
  "typescript": "^5"
46
46
  }
47
- }
47
+ }