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

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 (142) 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 +3 -3
  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/{02t4qx0vnsbnj.js → 089t1exs6apb8.js} +3 -3
  132. package/.next/static/chunks/{11gdlkf54jqv9.js → 0cjutq9mapkb7.js} +1 -1
  133. package/.next/static/chunks/{0hf7fag69h3.7.js → 0nkzy-apq7r0c.js} +1 -1
  134. package/.next/static/chunks/{0_jsqyukmaf5a.js → 13lpe1ad6sd-p.js} +1 -1
  135. package/.next/trace +2 -2
  136. package/.next/trace-build +1 -1
  137. package/app/components/TopoGraph.tsx +117 -48
  138. package/package.json +2 -2
  139. package/scripts/topo-live-lanes.mjs +48 -0
  140. /package/.next/static/{IoxDnk-zAN2bqUJxKcKN- → f2Eapuqm0T-focdAHRC28}/_buildManifest.js +0 -0
  141. /package/.next/static/{IoxDnk-zAN2bqUJxKcKN- → f2Eapuqm0T-focdAHRC28}/_clientMiddlewareManifest.js +0 -0
  142. /package/.next/static/{IoxDnk-zAN2bqUJxKcKN- → f2Eapuqm0T-focdAHRC28}/_ssgManifest.js +0 -0
@@ -72,6 +72,19 @@ interface MessageFlow {
72
72
  created_at: string;
73
73
  }
74
74
 
75
+ interface TaskFlow {
76
+ from_name?: string | null;
77
+ from_alias?: string | null;
78
+ to_name?: string | null;
79
+ to_alias?: string | null;
80
+ content?: string | null;
81
+ status?: string | null;
82
+ created_at?: string | null;
83
+ delivered_at?: string | null;
84
+ started_at?: string | null;
85
+ completed_at?: string | null;
86
+ }
87
+
75
88
  interface TopoGraphProps {
76
89
  sessions: Session[];
77
90
  sseSessions: Record<string, number>;
@@ -508,11 +521,21 @@ function commonPrefix(a: string, b: string): string {
508
521
  return a.slice(0, i);
509
522
  }
510
523
 
511
- /** #83 + #111: group nodes that share a ≥2-char alias prefix OR a project_dir
512
- * ("either criterion same group", Vincent 4724). Union-find over the
513
- * sessions; the component label prefers the shared project_dir's basename,
514
- * else the common alias prefix. Returns alias → groupKey. */
515
- function computeGroups(sessions: { alias: string; project_dir?: string | null }[]): Record<string, string> {
524
+ /** #83 + #111: group nodes by shared ≥2-char alias prefix. Union-find over
525
+ * the sessions; the component label is the common alias prefix. Returns
526
+ * alias groupKey.
527
+ *
528
+ * #172/#174 short-term fix: grouping is alias-prefix ONLY. The earlier
529
+ * project_dir co-union (Vincent 4724) was dropped — live /api/status
530
+ * data showed it actively wrong: 视频测试马 reported project_dir
531
+ * `/Users/vansin/intern-aip`, the same dir as the unrelated standalone
532
+ * nodes 知识马 / 群星马, so union-find transitively bridged 知识马 /
533
+ * 群星马 into the 视频 team. project_dir data quality is also poor
534
+ * (corrupted nested paths like `VideoNode/~/code/VideoNode/~/…`).
535
+ * alias prefix is the reliable signal; prefix-less nodes correctly
536
+ * fall out as singletons → the 未分组 bucket. The real fix is a
537
+ * first-class node.team field — tracked as #175. */
538
+ function computeGroups(sessions: { alias: string }[]): Record<string, string> {
516
539
  const n = sessions.length;
517
540
  const parent = sessions.map((_, i) => i);
518
541
  const find = (i: number): number => {
@@ -524,16 +547,6 @@ function computeGroups(sessions: { alias: string; project_dir?: string | null }[
524
547
  if (ra !== rb) parent[ra] = rb;
525
548
  };
526
549
 
527
- // union by shared project_dir
528
- const byDir: Record<string, number[]> = {};
529
- sessions.forEach((s, i) => {
530
- const d = s.project_dir?.trim();
531
- if (d) (byDir[d] ||= []).push(i);
532
- });
533
- for (const idxs of Object.values(byDir)) {
534
- for (let k = 1; k < idxs.length; k++) union(idxs[0], idxs[k]);
535
- }
536
-
537
550
  // union by shared ≥2-char alias prefix — sort, link adjacent pairs
538
551
  const order = sessions.map((_, i) => i).sort((a, b) => sessions[a].alias.localeCompare(sessions[b].alias));
539
552
  for (let k = 0; k + 1 < order.length; k++) {
@@ -542,7 +555,7 @@ function computeGroups(sessions: { alias: string; project_dir?: string | null }[
542
555
  }
543
556
  }
544
557
 
545
- // label each connected component
558
+ // label each connected component with the common alias prefix
546
559
  const comps: Record<number, number[]> = {};
547
560
  for (let i = 0; i < n; i++) (comps[find(i)] ||= []).push(i);
548
561
  const keys: Record<string, string> = {};
@@ -551,53 +564,71 @@ function computeGroups(sessions: { alias: string; project_dir?: string | null }[
551
564
  if (members.length === 1) {
552
565
  label = sessions[members[0]].alias;
553
566
  } else {
554
- const dirs = new Set(members.map(i => sessions[i].project_dir?.trim()).filter(Boolean) as string[]);
555
- if (dirs.size === 1) {
556
- const d = [...dirs][0];
557
- label = d.split('/').filter(Boolean).pop() || d;
558
- } else {
559
- label = members.map(i => sessions[i].alias).reduce((a, b) => commonPrefix(a, b));
560
- if (label.length < 2) label = sessions[members[0]].alias;
561
- }
567
+ label = members.map(i => sessions[i].alias).reduce((a, b) => commonPrefix(a, b));
568
+ if (label.length < 2) label = sessions[members[0]].alias;
562
569
  }
563
570
  for (const i of members) keys[sessions[i].alias] = label;
564
571
  }
565
572
  return keys;
566
573
  }
567
574
 
568
- function buildFlowLinks(messages: MessageFlow[], positions: Record<string, Point>) {
575
+ function buildFlowLinks(messages: MessageFlow[], tasks: TaskFlow[], positions: Record<string, Point>) {
569
576
  const links = new Map<string, FlowLink>();
570
577
 
571
- messages.forEach(message => {
572
- if (
573
- !positions[message.from_alias] ||
574
- !positions[message.to_alias] ||
575
- message.from_alias === message.to_alias
576
- ) {
578
+ const commandAlias = Object.keys(positions).find(a => a.includes('副指挥'))
579
+ ?? Object.keys(positions).find(a => a.includes('总指挥'))
580
+ ?? null;
581
+
582
+ const resolveAlias = (alias: string | null | undefined) => {
583
+ const clean = (alias || '').trim();
584
+ if (!clean) return null;
585
+ if (positions[clean]) return clean;
586
+ if ((clean === 'api' || clean === 'admin') && commandAlias) return commandAlias;
587
+ return clean;
588
+ };
589
+
590
+ const addLink = (fromRaw: string | null | undefined, toRaw: string | null | undefined, content: string, createdAt: string) => {
591
+ const from = resolveAlias(fromRaw);
592
+ const to = resolveAlias(toRaw);
593
+ if (!from || !to || !positions[from] || !positions[to] || from === to) {
577
594
  return;
578
595
  }
579
596
 
580
- const key = `${message.from_alias}->${message.to_alias}`;
597
+ const key = `${from}->${to}`;
581
598
  const current = links.get(key);
582
599
 
583
600
  // Keep the most-recent timestamp per pair so the render can fade
584
601
  // dormant edges (Round 10 freshness fade).
585
- const incoming = message.created_at || '';
602
+ const incoming = createdAt || '';
586
603
  const last_at = !current
587
604
  ? incoming
588
605
  : (incoming > current.last_at ? incoming : current.last_at);
589
606
 
590
607
  links.set(key, {
591
608
  key,
592
- from: message.from_alias,
593
- to: message.to_alias,
609
+ from,
610
+ to,
594
611
  count: (current?.count || 0) + 1,
595
- content: current?.content || message.content,
612
+ content: current?.content || content,
596
613
  last_at,
597
614
  });
615
+ };
616
+
617
+ messages.forEach(message => {
618
+ addLink(message.from_alias, message.to_alias, message.content, message.created_at);
598
619
  });
599
620
 
600
- return [...links.values()].slice(0, 18);
621
+ tasks.forEach(task => {
622
+ const from = task.from_alias ?? task.from_name;
623
+ const to = task.to_alias ?? task.to_name;
624
+ const when = task.completed_at || task.started_at || task.delivered_at || task.created_at || '';
625
+ const label = task.content || (task.status ? `task:${task.status}` : 'task');
626
+ addLink(from, to, label, when);
627
+ });
628
+
629
+ return [...links.values()]
630
+ .sort((a, b) => (b.last_at || '').localeCompare(a.last_at || ''))
631
+ .slice(0, 18);
601
632
  }
602
633
 
603
634
  export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProps) {
@@ -620,6 +651,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
620
651
  // wants to leave: "show me the rest of the flows" → /messages.
621
652
  const router = useRouter();
622
653
  const [messages, setMessages] = useState<MessageFlow[]>([]);
654
+ const [tasks, setTasks] = useState<TaskFlow[]>([]);
623
655
  // Issue #87: ring | grid layout toggle. Ring is the tiered-radial default;
624
656
  // grid arranges nodes in an N×M grid (better for 30+ nodes). Persisted to
625
657
  // localStorage like the zoom/pan view state. Declared above nodePositions
@@ -724,20 +756,24 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
724
756
  };
725
757
 
726
758
  useEffect(() => {
727
- const fetchMessages = async () => {
759
+ const fetchFlows = async () => {
728
760
  try {
729
- const res = await fetch('/api/hub/messages?limit=50');
730
- if (res.status === 401) {
761
+ const [messageRes, taskRes] = await Promise.all([
762
+ fetch('/api/hub/messages?limit=50'),
763
+ fetch('/api/hub/tasks?limit=100'),
764
+ ]);
765
+ if (messageRes.status === 401 || taskRes.status === 401) {
731
766
  window.location.assign('/login');
732
767
  return;
733
768
  }
734
769
 
735
- const data = await res.json();
736
- setMessages(data.messages || []);
770
+ const [messageData, taskData] = await Promise.all([messageRes.json(), taskRes.json()]);
771
+ setMessages(messageData.messages || []);
772
+ setTasks(taskData.tasks || []);
737
773
  } catch {}
738
774
  };
739
- fetchMessages();
740
- const interval = setInterval(fetchMessages, 5000);
775
+ fetchFlows();
776
+ const interval = setInterval(fetchFlows, 5000);
741
777
  return () => clearInterval(interval);
742
778
  }, []);
743
779
 
@@ -886,7 +922,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
886
922
  });
887
923
  }
888
924
 
889
- const links = buildFlowLinks(messages, positions);
925
+ const links = buildFlowLinks(messages, tasks, positions);
890
926
  const active = new Set<string>();
891
927
  links.forEach(link => { active.add(link.from); active.add(link.to); });
892
928
  // #111: one bounding box per multi-member group (Vincent 4722). Each
@@ -1063,6 +1099,39 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
1063
1099
  // secondary sort by key keeps equal-length lanes from jittering
1064
1100
  // between renders.
1065
1101
  lanes.sort((a, b) => b.members.length - a.members.length || a.key.localeCompare(b.key));
1102
+ // Vincent 5923 (#181): keep related team lanes adjacent. Two teams
1103
+ // are "related" when one's key is a substring of the other's
1104
+ // (设计 ⊂ 海报设计师). Union-find over lane keys → substring-relation
1105
+ // components; each component is emitted contiguously, anchored at its
1106
+ // largest lane's size-sorted slot (lanes within a component stay
1107
+ // size-desc). Unrelated teams keep their size order — this only stops
1108
+ // a related pair from being split apart by an unrelated team.
1109
+ {
1110
+ const lp = lanes.map((_, i) => i);
1111
+ const lfind = (i: number): number => {
1112
+ while (lp[i] !== i) { lp[i] = lp[lp[i]]; i = lp[i]; }
1113
+ return i;
1114
+ };
1115
+ for (let i = 0; i < lanes.length; i++) {
1116
+ for (let j = i + 1; j < lanes.length; j++) {
1117
+ const a = lanes[i].key, b = lanes[j].key;
1118
+ if (a.length >= 2 && b.length >= 2 && (a.includes(b) || b.includes(a))) {
1119
+ lp[lfind(i)] = lfind(j);
1120
+ }
1121
+ }
1122
+ }
1123
+ const seenRoot = new Set<number>();
1124
+ const clustered: Lane[] = [];
1125
+ for (let i = 0; i < lanes.length; i++) {
1126
+ const root = lfind(i);
1127
+ if (seenRoot.has(root)) continue;
1128
+ seenRoot.add(root);
1129
+ for (let j = i; j < lanes.length; j++) {
1130
+ if (lfind(j) === root) clustered.push(lanes[j]);
1131
+ }
1132
+ }
1133
+ lanes.splice(0, lanes.length, ...clustered);
1134
+ }
1066
1135
 
1067
1136
  // each lane = a wide box; its members in a single row inside it.
1068
1137
  const laneStep = LANE_H + LANE_GAP;
@@ -1167,7 +1236,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
1167
1236
  // tree/swimlane. iter4 (a6689b6) suppressed them as "杂线"; Vincent
1168
1237
  // now explicitly wants them back — same buildFlowLinks the ring/grid
1169
1238
  // layouts use, drawn against the swimlane node positions.
1170
- const links = buildFlowLinks(messages, positions);
1239
+ const links = buildFlowLinks(messages, tasks, positions);
1171
1240
  const active = new Set<string>();
1172
1241
  links.forEach(link => { active.add(link.from); active.add(link.to); });
1173
1242
 
@@ -1244,7 +1313,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
1244
1313
  positions[s.alias] = polarPoint(index, Math.max(offline.length, 1), offlineR, offlineRotation);
1245
1314
  });
1246
1315
 
1247
- const links = buildFlowLinks(messages, positions);
1316
+ const links = buildFlowLinks(messages, tasks, positions);
1248
1317
  const active = new Set<string>();
1249
1318
  links.forEach(link => {
1250
1319
  active.add(link.from);
@@ -1269,7 +1338,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
1269
1338
  treeConnectors: { lines: [], synthLabels: [], synthRoot: false } as TreeLayout,
1270
1339
  treeContentBox: null as { x: number; y: number; w: number; h: number } | null,
1271
1340
  };
1272
- }, [messages, sessions, sseSessions, layout, nodeScale]);
1341
+ }, [messages, tasks, sessions, sseSessions, layout, nodeScale]);
1273
1342
 
1274
1343
  const workingCount = onlineNodes.filter(s => s.status === 'working').length;
1275
1344
  // Round 744 / Loop: legend header node-count. The legend panel's header
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network-dashboard",
3
- "version": "0.5.3-preview.263",
3
+ "version": "0.5.3-preview.266",
4
4
  "description": "Agent Network Dashboard \u2014 Web UI for managing AI Agent networks",
5
5
  "scripts": {
6
6
  "dev": "next dev",
@@ -44,4 +44,4 @@
44
44
  "tailwindcss": "^4",
45
45
  "typescript": "^5"
46
46
  }
47
- }
47
+ }
@@ -0,0 +1,48 @@
1
+ /* #181 verify: load the LIVE dashboard (real ~99 sessions), tree/swimlane
2
+ * layout, dump team-lane order top→bottom + screenshot. Confirms 设计 lane
3
+ * sits adjacent to 海报设计师 (substring-related cluster). */
4
+ import { chromium } from 'playwright';
5
+ import { readFileSync, mkdirSync } from 'node:fs';
6
+
7
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
8
+ mkdirSync('/tmp/anet-181', { recursive: true });
9
+ const browser = await chromium.launch({ headless: true });
10
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 920 } });
11
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
12
+ await ctx.addInitScript(() => {
13
+ try {
14
+ localStorage.setItem('anet-theme', 'cyber');
15
+ localStorage.removeItem('anet-brand');
16
+ localStorage.removeItem('anet-topo-view');
17
+ localStorage.setItem('anet-topo-layout', 'tree');
18
+ sessionStorage.setItem('anet_v3_auth', '1');
19
+ } catch {}
20
+ });
21
+ const page = await ctx.newPage();
22
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
23
+ await page.waitForFunction(() => {
24
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
25
+ return !!svg && svg.querySelectorAll('g[data-group]').length > 3;
26
+ }, { timeout: 30000 }).catch(() => {});
27
+ await page.waitForTimeout(1200);
28
+ await page.evaluate(() => document.querySelector('svg[viewBox="0 0 1000 680"]')
29
+ ?.scrollIntoView({ behavior: 'instant', block: 'center' }));
30
+ await page.waitForTimeout(400);
31
+
32
+ const lanes = await page.evaluate(() => {
33
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
34
+ return [...svg.querySelectorAll('g[data-group]')]
35
+ .map(g => {
36
+ const r = g.querySelector('rect');
37
+ return { key: g.getAttribute('data-group'), y: +r.getAttribute('y'), x: +r.getAttribute('x') };
38
+ })
39
+ .sort((a, b) => a.y - b.y);
40
+ });
41
+ console.log('lane order (top → bottom):');
42
+ lanes.forEach((l, i) => console.log(` ${String(i + 1).padStart(2)}. ${l.key} (y=${l.y.toFixed(0)}, x=${l.x.toFixed(0)})`));
43
+
44
+ const svgEl = await page.$('svg[viewBox="0 0 1000 680"]');
45
+ await svgEl.screenshot({ path: '/tmp/anet-181/swimlane-live.png', animations: 'disabled' });
46
+ await ctx.close();
47
+ await browser.close();
48
+ console.log('screenshot: /tmp/anet-181/swimlane-live.png');