@sleep2agi/agent-network-dashboard 0.4.5-preview.9 → 0.4.5-preview.91

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 (319) hide show
  1. package/.mcp.json +11 -0
  2. package/.next/BUILD_ID +1 -1
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +48 -48
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +3 -3
  7. package/.next/server/app/_global-error.html +1 -1
  8. package/.next/server/app/_global-error.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/server/app/_not-found/page/next-font-manifest.json +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +4 -4
  17. package/.next/server/app/_not-found.rsc +12 -12
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  19. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  20. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  22. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  23. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  24. package/.next/server/app/admin/page/next-font-manifest.json +1 -1
  25. package/.next/server/app/admin/page.js.nft.json +1 -1
  26. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  27. package/.next/server/app/admin.html +4 -4
  28. package/.next/server/app/admin.rsc +15 -15
  29. package/.next/server/app/admin.segments/_full.segment.rsc +15 -15
  30. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  31. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  32. package/.next/server/app/admin.segments/_tree.segment.rsc +3 -3
  33. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  34. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  35. package/.next/server/app/index.html +4 -4
  36. package/.next/server/app/index.rsc +15 -15
  37. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  38. package/.next/server/app/index.segments/_full.segment.rsc +15 -15
  39. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  40. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  41. package/.next/server/app/index.segments/_tree.segment.rsc +3 -3
  42. package/.next/server/app/login/page/next-font-manifest.json +1 -1
  43. package/.next/server/app/login/page.js.nft.json +1 -1
  44. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  45. package/.next/server/app/login.html +2 -2
  46. package/.next/server/app/login.rsc +15 -15
  47. package/.next/server/app/login.segments/_full.segment.rsc +15 -15
  48. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  49. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  50. package/.next/server/app/login.segments/_tree.segment.rsc +3 -3
  51. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  52. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  53. package/.next/server/app/logs/page/next-font-manifest.json +1 -1
  54. package/.next/server/app/logs/page.js.nft.json +1 -1
  55. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  56. package/.next/server/app/logs.html +4 -4
  57. package/.next/server/app/logs.rsc +15 -15
  58. package/.next/server/app/logs.segments/_full.segment.rsc +15 -15
  59. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  60. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  61. package/.next/server/app/logs.segments/_tree.segment.rsc +3 -3
  62. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  63. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  64. package/.next/server/app/messages/page/next-font-manifest.json +1 -1
  65. package/.next/server/app/messages/page.js.nft.json +1 -1
  66. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  67. package/.next/server/app/messages.html +4 -4
  68. package/.next/server/app/messages.rsc +15 -15
  69. package/.next/server/app/messages.segments/_full.segment.rsc +15 -15
  70. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  71. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  72. package/.next/server/app/messages.segments/_tree.segment.rsc +3 -3
  73. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  74. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  75. package/.next/server/app/node/page/next-font-manifest.json +1 -1
  76. package/.next/server/app/node/page.js.nft.json +1 -1
  77. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  78. package/.next/server/app/node.html +4 -4
  79. package/.next/server/app/node.rsc +15 -15
  80. package/.next/server/app/node.segments/_full.segment.rsc +15 -15
  81. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  82. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  83. package/.next/server/app/node.segments/_tree.segment.rsc +3 -3
  84. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  85. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  86. package/.next/server/app/nodes/page/next-font-manifest.json +1 -1
  87. package/.next/server/app/nodes/page.js.nft.json +1 -1
  88. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  89. package/.next/server/app/nodes.html +4 -4
  90. package/.next/server/app/nodes.rsc +15 -15
  91. package/.next/server/app/nodes.segments/_full.segment.rsc +15 -15
  92. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  93. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  94. package/.next/server/app/nodes.segments/_tree.segment.rsc +3 -3
  95. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  96. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  97. package/.next/server/app/page/next-font-manifest.json +1 -1
  98. package/.next/server/app/page.js.nft.json +1 -1
  99. package/.next/server/app/page_client-reference-manifest.js +1 -1
  100. package/.next/server/app/server-logs/page/next-font-manifest.json +1 -1
  101. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  102. package/.next/server/app/server-logs.html +4 -4
  103. package/.next/server/app/server-logs.rsc +15 -15
  104. package/.next/server/app/server-logs.segments/_full.segment.rsc +15 -15
  105. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  106. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  107. package/.next/server/app/server-logs.segments/_tree.segment.rsc +3 -3
  108. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  109. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  110. package/.next/server/app/settings/networks/page/next-font-manifest.json +1 -1
  111. package/.next/server/app/settings/networks/page.js.nft.json +1 -1
  112. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  113. package/.next/server/app/settings/networks.html +4 -4
  114. package/.next/server/app/settings/networks.rsc +15 -15
  115. package/.next/server/app/settings/networks.segments/_full.segment.rsc +15 -15
  116. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  117. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  118. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +3 -3
  119. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  120. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  121. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  122. package/.next/server/app/settings/page/next-font-manifest.json +1 -1
  123. package/.next/server/app/settings/page.js.nft.json +1 -1
  124. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  125. package/.next/server/app/settings/tokens/page/next-font-manifest.json +1 -1
  126. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  127. package/.next/server/app/settings/tokens.html +4 -4
  128. package/.next/server/app/settings/tokens.rsc +15 -15
  129. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +15 -15
  130. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  131. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  132. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +3 -3
  133. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  134. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  135. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  136. package/.next/server/app/settings.html +4 -4
  137. package/.next/server/app/settings.rsc +15 -15
  138. package/.next/server/app/settings.segments/_full.segment.rsc +15 -15
  139. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  140. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  141. package/.next/server/app/settings.segments/_tree.segment.rsc +3 -3
  142. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  143. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  144. package/.next/server/app/tasks/[id]/page/next-font-manifest.json +1 -1
  145. package/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
  146. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  147. package/.next/server/app/tasks/page/next-font-manifest.json +1 -1
  148. package/.next/server/app/tasks/page.js.nft.json +1 -1
  149. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  150. package/.next/server/app/tasks.html +4 -4
  151. package/.next/server/app/tasks.rsc +15 -15
  152. package/.next/server/app/tasks.segments/_full.segment.rsc +15 -15
  153. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  154. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  155. package/.next/server/app/tasks.segments/_tree.segment.rsc +3 -3
  156. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  157. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  158. package/.next/server/chunks/ssr/[root-of-the-server]__0jl4ukn._.js +3 -0
  159. package/.next/server/chunks/ssr/[root-of-the-server]__0jl4ukn._.js.map +1 -0
  160. package/.next/server/chunks/ssr/[root-of-the-server]__0n46s~k._.js +3 -0
  161. package/.next/server/chunks/ssr/[root-of-the-server]__0n46s~k._.js.map +1 -0
  162. package/.next/server/chunks/ssr/[root-of-the-server]__0w2ay2h._.js +3 -0
  163. package/.next/server/chunks/ssr/[root-of-the-server]__0w2ay2h._.js.map +1 -0
  164. package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js +1 -1
  165. package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js.map +1 -1
  166. package/.next/server/chunks/ssr/_0nne7mz._.js +3 -0
  167. package/.next/server/chunks/ssr/_0nne7mz._.js.map +1 -0
  168. package/.next/server/chunks/ssr/app_0.zbtn5._.js +9 -0
  169. package/.next/server/chunks/ssr/app_0.zbtn5._.js.map +1 -0
  170. package/.next/server/chunks/ssr/app_01y-~mp._.js +3 -0
  171. package/.next/server/chunks/ssr/app_01y-~mp._.js.map +1 -0
  172. package/.next/server/chunks/ssr/app_04n78pr._.js +3 -0
  173. package/.next/server/chunks/ssr/app_04n78pr._.js.map +1 -0
  174. package/.next/server/chunks/ssr/app_0_agk0r._.js +3 -0
  175. package/.next/server/chunks/ssr/app_0_agk0r._.js.map +1 -0
  176. package/.next/server/chunks/ssr/app_0aenxef._.js +3 -0
  177. package/.next/server/chunks/ssr/app_0aenxef._.js.map +1 -0
  178. package/.next/server/chunks/ssr/app_0aum_g5._.js +3 -0
  179. package/.next/server/chunks/ssr/app_0aum_g5._.js.map +1 -0
  180. package/.next/server/chunks/ssr/app_0emvkn2._.js +3 -0
  181. package/.next/server/chunks/ssr/app_0emvkn2._.js.map +1 -0
  182. package/.next/server/chunks/ssr/app_0esuljp._.js +3 -0
  183. package/.next/server/chunks/ssr/app_0esuljp._.js.map +1 -0
  184. package/.next/server/chunks/ssr/app_0izi28j._.js +1 -1
  185. package/.next/server/chunks/ssr/app_0izi28j._.js.map +1 -1
  186. package/.next/server/chunks/ssr/app_components_08ys5y3._.js +5 -5
  187. package/.next/server/chunks/ssr/app_components_08ys5y3._.js.map +1 -1
  188. package/.next/server/chunks/ssr/app_server-logs_page_tsx_0_kpp1p._.js +1 -1
  189. package/.next/server/chunks/ssr/app_server-logs_page_tsx_0_kpp1p._.js.map +1 -1
  190. package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js +1 -1
  191. package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js.map +1 -1
  192. package/.next/server/middleware-build-manifest.js +3 -3
  193. package/.next/server/next-font-manifest.js +1 -1
  194. package/.next/server/next-font-manifest.json +14 -14
  195. package/.next/server/pages/404.html +4 -4
  196. package/.next/server/pages/500.html +1 -1
  197. package/.next/server/server-reference-manifest.js +1 -1
  198. package/.next/server/server-reference-manifest.json +1 -1
  199. package/.next/static/chunks/0-hlcuoemhe7w.js +7 -0
  200. package/.next/static/chunks/02_zpo7taex9o.js +7 -0
  201. package/.next/static/chunks/03b6jkic-1r9u.css +2 -0
  202. package/.next/static/chunks/06fliypy0.f0r.js +1 -0
  203. package/.next/static/chunks/06n3d~fbbtagk.js +1 -0
  204. package/.next/static/chunks/07eba1lz2u3p4.js +1 -0
  205. package/.next/static/chunks/0av2~0.96~d~g.js +1 -0
  206. package/.next/static/chunks/{07okhs5561y5y.js → 0c1bp~g7.8ru..js} +1 -1
  207. package/.next/static/chunks/0g9aof0ob_fcj.js +1 -0
  208. package/.next/static/chunks/0gtbwcilfu6b7.js +1 -0
  209. package/.next/static/chunks/0s8jo6jypzkx5.js +1 -0
  210. package/.next/static/chunks/0ubqqhhpsaez4.js +1 -0
  211. package/.next/static/chunks/0vmfu_gz9tp4f.js +1 -0
  212. package/.next/static/chunks/0~qw9kxksr1v2.js +1 -0
  213. package/.next/static/chunks/12gprw3l0kvvf.js +1 -0
  214. package/.next/static/chunks/12mjtyh-j74~k.js +1 -0
  215. package/.next/static/chunks/12yuo353giomx.js +1 -0
  216. package/.next/static/media/4fa387ec64143e14-s.0.qu-9752pffj.woff2 +0 -0
  217. package/.next/static/media/5ce348bf30bf5439-s.0ee55_hj9qcer.woff2 +0 -0
  218. package/.next/static/media/6306c77e7c8268e4-s.0mao5jbfbduzp.woff2 +0 -0
  219. package/.next/static/media/797e433ab948586e-s.p.09zddjkbdep5a.woff2 +0 -0
  220. package/.next/static/media/7d817b4c03b0c5f1-s.0uzt.a6d44yda.woff2 +0 -0
  221. package/.next/static/media/bbc41e54d2fcbd21-s.0mvwgmnhv29no.woff2 +0 -0
  222. package/.next/trace +2 -2
  223. package/.next/trace-build +1 -1
  224. package/app/admin/page.tsx +97 -27
  225. package/app/components/AgentCard.tsx +19 -5
  226. package/app/components/BroadcastBar.tsx +22 -4
  227. package/app/components/DispatchPanel.tsx +9 -5
  228. package/app/components/EmptyState.tsx +127 -12
  229. package/app/components/HealthBanner.tsx +11 -2
  230. package/app/components/HelpOverlay.tsx +50 -10
  231. package/app/components/InboxPanel.tsx +7 -4
  232. package/app/components/Sidebar.tsx +81 -44
  233. package/app/components/StatsBar.tsx +59 -36
  234. package/app/components/TaskChatPanel.tsx +8 -10
  235. package/app/components/TaskDrawer.tsx +72 -28
  236. package/app/components/TopoGraph.tsx +527 -41
  237. package/app/components/utils.ts +13 -0
  238. package/app/globals.css +90 -0
  239. package/app/lib/status.ts +85 -0
  240. package/app/lib/version.ts +7 -0
  241. package/app/login/page.tsx +40 -7
  242. package/app/logs/page.tsx +70 -21
  243. package/app/messages/page.tsx +34 -9
  244. package/app/node/page.tsx +52 -27
  245. package/app/nodes/page.tsx +45 -21
  246. package/app/page.tsx +103 -32
  247. package/app/server-logs/page.tsx +104 -28
  248. package/app/settings/networks/page.tsx +40 -16
  249. package/app/settings/page.tsx +39 -11
  250. package/app/tasks/[id]/page.tsx +2 -13
  251. package/app/tasks/page.tsx +66 -73
  252. package/package.json +1 -1
  253. package/public/intern_avatar.png +0 -0
  254. package/scripts/empty-state-test.mjs +41 -0
  255. package/scripts/fold-only.mjs +47 -0
  256. package/scripts/intern-alias-test.mjs +46 -0
  257. package/scripts/loop-review.mjs +270 -0
  258. package/scripts/network-derive-shot.mjs +16 -0
  259. package/scripts/network-derive-test.mjs +28 -0
  260. package/scripts/topo-brand-shot.mjs +61 -0
  261. package/scripts/topo-cluster-test.mjs +41 -0
  262. package/scripts/topo-controls-shot.mjs +24 -0
  263. package/scripts/topo-group-test.mjs +37 -0
  264. package/scripts/topo-inspect.mjs +56 -0
  265. package/scripts/topo-interaction-test.mjs +80 -0
  266. package/scripts/topo-mock-large.mjs +58 -0
  267. package/scripts/topo-r104-test.mjs +54 -0
  268. package/scripts/topo-shot.mjs +31 -0
  269. package/.next/server/chunks/ssr/[root-of-the-server]__06w_u7t._.js +0 -3
  270. package/.next/server/chunks/ssr/[root-of-the-server]__06w_u7t._.js.map +0 -1
  271. package/.next/server/chunks/ssr/[root-of-the-server]__0ehu0jb._.js +0 -3
  272. package/.next/server/chunks/ssr/[root-of-the-server]__0ehu0jb._.js.map +0 -1
  273. package/.next/server/chunks/ssr/[root-of-the-server]__0nrckjd._.js +0 -3
  274. package/.next/server/chunks/ssr/[root-of-the-server]__0nrckjd._.js.map +0 -1
  275. package/.next/server/chunks/ssr/_13_v7-3._.js +0 -3
  276. package/.next/server/chunks/ssr/_13_v7-3._.js.map +0 -1
  277. package/.next/server/chunks/ssr/app_0.0wpa~._.js +0 -3
  278. package/.next/server/chunks/ssr/app_0.0wpa~._.js.map +0 -1
  279. package/.next/server/chunks/ssr/app_0ap_oiq._.js +0 -3
  280. package/.next/server/chunks/ssr/app_0ap_oiq._.js.map +0 -1
  281. package/.next/server/chunks/ssr/app_0dk5wao._.js +0 -3
  282. package/.next/server/chunks/ssr/app_0dk5wao._.js.map +0 -1
  283. package/.next/server/chunks/ssr/app_0et5yua._.js +0 -3
  284. package/.next/server/chunks/ssr/app_0et5yua._.js.map +0 -1
  285. package/.next/server/chunks/ssr/app_10b5blc._.js +0 -9
  286. package/.next/server/chunks/ssr/app_10b5blc._.js.map +0 -1
  287. package/.next/server/chunks/ssr/app_11vl967._.js +0 -3
  288. package/.next/server/chunks/ssr/app_11vl967._.js.map +0 -1
  289. package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js +0 -3
  290. package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js.map +0 -1
  291. package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js +0 -3
  292. package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js.map +0 -1
  293. package/.next/static/chunks/0-.hjh--kcdiy.js +0 -1
  294. package/.next/static/chunks/02afyxgpty3uf.js +0 -1
  295. package/.next/static/chunks/02flrewxlf_m_.js +0 -1
  296. package/.next/static/chunks/051gslfjka1kj.js +0 -1
  297. package/.next/static/chunks/07oobuohrpigy.js +0 -1
  298. package/.next/static/chunks/09al3qm76q7nd.js +0 -1
  299. package/.next/static/chunks/0e7c40p~wq665.js +0 -1
  300. package/.next/static/chunks/0g7m7zk.v7g6h.js +0 -1
  301. package/.next/static/chunks/0h.ajsk_uxigl.js +0 -1
  302. package/.next/static/chunks/0msa~bub38ytf.css +0 -2
  303. package/.next/static/chunks/0ogeals5ufhho.js +0 -1
  304. package/.next/static/chunks/10ir.g.n~hre0.js +0 -7
  305. package/.next/static/chunks/10njj7kn9ylkf.js +0 -1
  306. package/.next/static/chunks/12dcb86elsl-e.js +0 -7
  307. package/.next/static/chunks/13.dst7liziwo.js +0 -1
  308. package/.next/static/chunks/132e__998_q9r.js +0 -1
  309. package/.next/static/media/4fa387ec64143e14-s.0q3udbd2bu5yp.woff2 +0 -0
  310. package/.next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2 +0 -0
  311. package/.next/static/media/bbc41e54d2fcbd21-s.0gw~uztddq1df.woff2 +0 -0
  312. package/public/file.svg +0 -1
  313. package/public/globe.svg +0 -1
  314. package/public/next.svg +0 -1
  315. package/public/vercel.svg +0 -1
  316. package/public/window.svg +0 -1
  317. /package/.next/static/{stSZtBF9uGYc3L399m0h1 → u-HIIfy-O0UVQ02APphkX}/_buildManifest.js +0 -0
  318. /package/.next/static/{stSZtBF9uGYc3L399m0h1 → u-HIIfy-O0UVQ02APphkX}/_clientMiddlewareManifest.js +0 -0
  319. /package/.next/static/{stSZtBF9uGYc3L399m0h1 → u-HIIfy-O0UVQ02APphkX}/_ssgManifest.js +0 -0
@@ -23,6 +23,30 @@ const LEVEL_BADGE: Record<LogLine['level'], string> = {
23
23
  error: 'bg-red-500/10 text-red-300 border-red-500/30',
24
24
  };
25
25
 
26
+ /** Left-rail strip color per log level — gives the eye a vertical guide
27
+ * when scanning long log walls. Plain hex so Tailwind purge can't drop it. */
28
+ const LEVEL_STRIPE: Record<LogLine['level'], string> = {
29
+ log: 'transparent',
30
+ info: '#22d3ee',
31
+ warn: '#f59e0b',
32
+ error: '#ef4444',
33
+ };
34
+
35
+ /** Highlight search-matched substring inside a log line. Case-insensitive
36
+ * first-match wrap; mark inherits color so log-level coloring stays. */
37
+ function highlightSearch(line: string, q: string) {
38
+ if (!q) return line;
39
+ const idx = line.toLowerCase().indexOf(q.toLowerCase());
40
+ if (idx < 0) return line;
41
+ return (
42
+ <>
43
+ {line.slice(0, idx)}
44
+ <mark className="bg-yellow-500/30 text-yellow-100 rounded-sm px-0.5">{line.slice(idx, idx + q.length)}</mark>
45
+ {line.slice(idx + q.length)}
46
+ </>
47
+ );
48
+ }
49
+
26
50
  function shortTime(iso: string) {
27
51
  try {
28
52
  const d = new Date(iso);
@@ -93,49 +117,86 @@ export default function ServerLogsPage() {
93
117
 
94
118
  return (
95
119
  <div className="p-6 max-w-[1600px] mx-auto">
96
- <div className="flex items-baseline justify-between mb-4">
97
- <div className="flex items-baseline gap-3">
120
+ <div className="flex items-center justify-between mb-4 gap-3">
121
+ <div className="flex items-center gap-3 min-w-0">
98
122
  <h1 className="text-2xl font-bold text-white">Server Logs</h1>
99
- <span className="text-xs text-gray-500">CommHub stdout/stderr · 最新在上</span>
123
+ {/* Round 86: dropped the {logs.length} header chip — r84 added
124
+ `all <count>` to the filter strip just below, so this duplicated
125
+ the value within 40px of itself. */}
126
+ <span className="text-xs text-gray-500 hidden sm:inline">CommHub stdout/stderr</span>
100
127
  </div>
101
- <div className="flex items-center gap-2 text-xs">
128
+ <div className="flex items-center gap-2 text-xs shrink-0">
102
129
  <button
103
130
  onClick={() => setPaused(p => !p)}
104
- className={`px-3 py-1 rounded border ${paused ? 'bg-amber-500/10 text-amber-300 border-amber-500/30' : 'bg-cyan-500/10 text-cyan-300 border-cyan-500/30'}`}
131
+ className={`flex items-center gap-1.5 px-3 py-1 rounded border ${paused ? 'bg-amber-500/10 text-amber-300 border-amber-500/30' : 'bg-cyan-500/10 text-cyan-300 border-cyan-500/30'}`}
105
132
  >
106
- {paused ? '▶ Resume' : '⏸ Pause'}
133
+ {paused ? (
134
+ <>
135
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" aria-hidden><path d="M8 5v14l11-7z" /></svg>
136
+ Resume
137
+ </>
138
+ ) : (
139
+ <>
140
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
141
+ <rect x="6" y="5" width="4" height="14" rx="0.5" />
142
+ <rect x="14" y="5" width="4" height="14" rx="0.5" />
143
+ </svg>
144
+ Pause
145
+ </>
146
+ )}
107
147
  </button>
108
148
  <label className="flex items-center gap-1 text-gray-400 select-none">
109
149
  <input type="checkbox" checked={autoRefresh} onChange={e => setAutoRefresh(e.target.checked)} className="accent-cyan-500" />
110
- auto-refresh
150
+ <span className="hidden sm:inline">auto-refresh</span>
111
151
  </label>
112
- <button onClick={() => fetchLogs(true)} className="px-3 py-1 rounded border bg-gray-500/10 text-gray-300 border-gray-500/20 hover:bg-gray-500/20">
113
- Reload
152
+ <button onClick={() => fetchLogs(true)} className="flex items-center gap-1.5 px-3 py-1 rounded border bg-gray-500/10 text-gray-300 border-gray-500/20 hover:bg-gray-500/20">
153
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
154
+ <path d="M3 12a9 9 0 0 1 15.5-6.4L21 8" />
155
+ <path d="M21 3v5h-5" />
156
+ <path d="M21 12a9 9 0 0 1-15.5 6.4L3 16" />
157
+ <path d="M3 21v-5h5" />
158
+ </svg>
159
+ <span className="hidden sm:inline">Reload</span>
114
160
  </button>
115
161
  </div>
116
162
  </div>
117
163
 
118
- <div className="flex items-center gap-3 mb-3">
119
- <div className="flex items-center gap-1 text-xs">
120
- {(['all', 'log', 'info', 'warn', 'error'] as const).map(lv => (
121
- <button
122
- key={lv}
123
- onClick={() => setFilterLevel(lv)}
124
- className={`px-2.5 py-1 rounded border ${
125
- filterLevel === lv
126
- ? 'bg-cyan-500/15 text-cyan-200 border-cyan-500/40'
127
- : 'bg-[#11111c] text-gray-400 border-[#2a2a4a] hover:bg-[#1a1a2a]'
128
- }`}
129
- >
130
- {lv}{lv !== 'all' && counts[lv as LogLine['level']] > 0 ? ` (${counts[lv as LogLine['level']]})` : ''}
131
- </button>
132
- ))}
164
+ {/* Round 95: flex-wrap + min-w-0 on the search wrapper so the
165
+ search input drops below the chip strip on mobile instead of
166
+ overflowing past the viewport. Desktop layout (single row)
167
+ unchanged because the row has room. */}
168
+ <div className="flex flex-wrap items-center gap-3 mb-3">
169
+ <div className="flex items-center gap-1 text-xs flex-wrap">
170
+ {/* Round 84: every chip shows its count (matches Tasks r56 +
171
+ Audit Log r43); zero-count non-active chips are disabled
172
+ + muted so the eye lands on the levels that actually have
173
+ entries. */}
174
+ {(['all', 'log', 'info', 'warn', 'error'] as const).map(lv => {
175
+ const count = lv === 'all' ? logs.length : counts[lv as LogLine['level']];
176
+ const isActive = filterLevel === lv;
177
+ const isEmpty = lv !== 'all' && count === 0 && !isActive;
178
+ return (
179
+ <button
180
+ key={lv}
181
+ onClick={() => setFilterLevel(lv)}
182
+ disabled={isEmpty}
183
+ className={`px-2.5 py-1 rounded border flex items-center gap-1 disabled:opacity-30 disabled:cursor-not-allowed ${
184
+ isActive
185
+ ? 'bg-cyan-500/15 text-cyan-200 border-cyan-500/40'
186
+ : 'bg-[#11111c] text-gray-400 border-[#2a2a4a] hover:bg-[#1a1a2a]'
187
+ }`}
188
+ >
189
+ <span>{lv}</span>
190
+ <span className={`text-[10px] tabular-nums ${isActive ? 'opacity-80' : 'text-gray-600'}`}>{count}</span>
191
+ </button>
192
+ );
193
+ })}
133
194
  </div>
134
195
  <input
135
196
  value={search}
136
197
  onChange={e => setSearch(e.target.value)}
137
198
  placeholder="搜索关键字 (alias / task_id / error message)"
138
- className="flex-1 px-3 py-1.5 text-xs bg-[#11111c] border border-[#2a2a4a] rounded text-gray-200 focus:outline-none focus:border-cyan-500/40"
199
+ className="flex-1 min-w-[140px] basis-full sm:basis-0 px-3 py-1.5 text-xs bg-[#11111c] border border-[#2a2a4a] rounded text-gray-200 focus:outline-none focus:border-cyan-500/40"
139
200
  />
140
201
  <span className="text-[10px] text-gray-600">
141
202
  {filtered.length} / {logs.length}
@@ -182,12 +243,27 @@ export default function ServerLogsPage() {
182
243
  ) : (
183
244
  <div className="divide-y divide-[#0d0d18] max-h-[calc(100vh-260px)] overflow-y-auto">
184
245
  {filtered.map((l, i) => (
185
- <div key={`${l.ts}-${i}`} className="px-3 py-1 hover:bg-[#0a0a14] flex gap-3">
186
- <span className="text-gray-600 shrink-0 w-[110px] text-[10px]">{shortTime(l.ts)}</span>
246
+ <div key={`${l.ts}-${i}`} className="relative pl-3 pr-3 py-1 hover:bg-[#0a0a14] flex gap-3">
247
+ {/* Round 32: 2px left rail keyed to level. Makes warn/error
248
+ spike visible in a wall of `log` lines without users having
249
+ to read the level chip on every row. */}
250
+ <span
251
+ aria-hidden
252
+ className="absolute left-0 top-0 bottom-0 w-0.5"
253
+ style={{ backgroundColor: LEVEL_STRIPE[l.level] }}
254
+ />
255
+ <span className="text-gray-600 shrink-0 w-[100px] text-[10px] tabular-nums">{shortTime(l.ts)}</span>
187
256
  <span className={`shrink-0 px-1.5 rounded border text-[9px] uppercase ${LEVEL_BADGE[l.level]}`}>
188
257
  {l.level}
189
258
  </span>
190
- <span className={`break-all whitespace-pre-wrap ${LEVEL_COLOR[l.level]}`}>{l.line}</span>
259
+ {/* Round 85: CommHub stamps each log line with a `[HH:MM:SS]`
260
+ prefix, but the row already shows shortTime(l.ts) in its
261
+ own 100px column on the left. Strip the duplicate
262
+ prefix at display time so the message starts directly
263
+ with the alias / agent / action. */}
264
+ <span className={`break-all whitespace-pre-wrap ${LEVEL_COLOR[l.level]}`}>
265
+ {highlightSearch(l.line.replace(/^\[\d{2}:\d{2}:\d{2}\]\s*/, ''), search)}
266
+ </span>
191
267
  </div>
192
268
  ))}
193
269
  </div>
@@ -4,6 +4,7 @@ import { useEffect, useState, useCallback } from 'react';
4
4
  import Link from 'next/link';
5
5
  import { timeAgo } from '../../components/utils';
6
6
  import { EmptyState } from '../../components/EmptyState';
7
+ import { AliasAvatar } from '../../components/AliasAvatar';
7
8
  import useSWR from 'swr';
8
9
 
9
10
  const fetcher = (url: string) => fetch(url).then(r => r.json());
@@ -99,9 +100,9 @@ export default function NetworksPage() {
99
100
  <div className="flex items-center gap-4 mb-6">
100
101
  <Link href="/settings" className="text-gray-500 hover:text-gray-300 text-sm lg:ml-0 ml-10">&larr; Settings</Link>
101
102
  <h1 className="text-2xl font-bold text-white">Networks</h1>
102
- <span className="text-xs bg-blue-900/30 text-blue-400 px-2 py-0.5 rounded-full border border-blue-800/30">
103
- {networks.length}
104
- </span>
103
+ {/* Round 93: dropped {networks.length} header chip — the
104
+ My Networks panel below already carries `<N> total` in its
105
+ own subhead. Matches r86/r87 pattern. */}
105
106
  </div>
106
107
 
107
108
  <div className="max-w-2xl space-y-6">
@@ -123,7 +124,12 @@ export default function NetworksPage() {
123
124
 
124
125
  {/* List */}
125
126
  <section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
126
- <h2 className="text-sm font-semibold text-gray-300 mb-3">My Networks</h2>
127
+ <div className="flex items-center justify-between mb-3">
128
+ <h2 className="text-sm font-semibold text-gray-300">My Networks</h2>
129
+ {networks.length > 0 && (
130
+ <span className="text-[10px] text-gray-600 uppercase tracking-wide">{networks.length} total</span>
131
+ )}
132
+ </div>
127
133
  {loading ? (
128
134
  <div className="animate-pulse space-y-2">{[1,2].map(i => <div key={i} className="h-14 bg-gray-800/20 rounded" />)}</div>
129
135
  ) : networks.length === 0 ? (
@@ -136,11 +142,14 @@ export default function NetworksPage() {
136
142
  <div className="space-y-3">
137
143
  {networks.map(n => (
138
144
  <div key={n.network_id} className="bg-[#0a0a15] rounded-lg px-4 py-3 border border-[#1a1a2a]">
139
- <div className="flex items-center justify-between">
140
- <div className="min-w-0">
141
- <div className="text-sm text-white font-medium">{n.network_name}</div>
142
- <div className="text-[10px] text-gray-500">{n.network_id} · {timeAgo(n.created_at)}</div>
143
- {n.description && <div className="text-xs text-gray-400 mt-1">{n.description}</div>}
145
+ <div className="flex items-start justify-between gap-3">
146
+ <div className="flex items-start gap-3 min-w-0">
147
+ <AliasAvatar alias={n.network_name} size={28} />
148
+ <div className="min-w-0">
149
+ <div className="text-sm text-white font-medium truncate">{n.network_name}</div>
150
+ <div className="text-[10px] text-gray-500 font-mono truncate" title={n.network_id}>{n.network_id} · {timeAgo(n.created_at)}</div>
151
+ {n.description && <div className="text-xs text-gray-400 mt-1">{n.description}</div>}
152
+ </div>
144
153
  </div>
145
154
  <div className="flex gap-2 shrink-0">
146
155
  <button onClick={() => {
@@ -169,14 +178,20 @@ export default function NetworksPage() {
169
178
  {(members[n.network_id] || []).length === 0 ? (
170
179
  <div className="text-[10px] text-gray-600">No members or V3 auth required</div>
171
180
  ) : (
172
- (members[n.network_id] || []).map(m => (
173
- <div key={m.user_id} className="flex items-center justify-between text-xs py-1">
174
- <span className="text-gray-300">{m.username || m.display_name}</span>
175
- <span className={`px-1.5 py-0.5 rounded border text-[10px] ${
181
+ (members[n.network_id] || []).map(m => {
182
+ const name = m.username || m.display_name || '?';
183
+ return (
184
+ <div key={m.user_id} className="flex items-center gap-2 justify-between text-xs py-1">
185
+ <div className="flex items-center gap-2 min-w-0">
186
+ <AliasAvatar alias={name} size={16} />
187
+ <span className="text-gray-300 truncate">{name}</span>
188
+ </div>
189
+ <span className={`px-1.5 py-0.5 rounded border text-[10px] shrink-0 ${
176
190
  m.role === 'owner' ? 'text-yellow-300 border-yellow-500/20' : 'text-gray-400 border-gray-600/20'
177
191
  }`}>{m.role}</span>
178
192
  </div>
179
- ))
193
+ );
194
+ })
180
195
  )}
181
196
 
182
197
  {/* Invite */}
@@ -196,8 +211,17 @@ export default function NetworksPage() {
196
211
  </div>
197
212
  {inviteCode && (
198
213
  <div className="mt-2 bg-green-500/5 border border-green-500/20 rounded px-3 py-2">
199
- <div className="text-[10px] text-green-400 mb-1">Invite code:</div>
200
- <code className="text-xs text-green-300 select-all">{inviteCode}</code>
214
+ <div className="flex items-center justify-between mb-1">
215
+ <div className="text-[10px] text-green-400 uppercase tracking-wide">Invite code</div>
216
+ <button
217
+ type="button"
218
+ onClick={() => { navigator.clipboard?.writeText(inviteCode).catch(() => {}); }}
219
+ className="text-[10px] text-green-300 hover:text-green-200 px-1.5 py-0.5 rounded border border-green-500/20 hover:bg-green-500/10"
220
+ >
221
+ Copy
222
+ </button>
223
+ </div>
224
+ <code className="text-xs text-green-300 select-all break-all">{inviteCode}</code>
201
225
  </div>
202
226
  )}
203
227
  </div>
@@ -3,6 +3,7 @@
3
3
  import { useState } from 'react';
4
4
  import Link from 'next/link';
5
5
  import { useAnetConfig, useHealth, useLicense } from '../lib/hooks';
6
+ import { DASHBOARD_VERSION } from '../lib/version';
6
7
 
7
8
  export default function SettingsPage() {
8
9
  const { config } = useAnetConfig();
@@ -18,12 +19,31 @@ export default function SettingsPage() {
18
19
 
19
20
  return (
20
21
  <div className="min-h-screen bg-[#0a0a1a] text-gray-100 p-4 sm:p-6 font-mono">
21
- <h1 className="text-2xl font-bold text-white mb-6 lg:ml-0 ml-10">Settings</h1>
22
+ <h1 className="text-2xl font-bold text-white mb-3 lg:ml-0 ml-10">Settings</h1>
22
23
 
23
- <div className="max-w-2xl space-y-8">
24
+ {/* Section anchor nav (round 28) — jump to a group instead of scrolling
25
+ through the whole page. Mirrors the section headers below; pages
26
+ with 7+ cards benefit from a visible table-of-contents. */}
27
+ <nav className="mb-8 flex flex-wrap gap-1 text-xs">
28
+ {[
29
+ { href: '#connection', label: 'Connection' },
30
+ { href: '#account', label: 'Account' },
31
+ { href: '#resources', label: 'Resources' },
32
+ ].map(a => (
33
+ <a key={a.href} href={a.href}
34
+ className="rounded-md px-2.5 py-1 text-gray-500 hover:text-cyan-300 hover:bg-cyan-500/10 transition-colors">
35
+ {a.label}
36
+ </a>
37
+ ))}
38
+ </nav>
39
+
40
+ <div className="max-w-2xl space-y-10">
24
41
  {/* ── Group: Connection ─────────────────────────────────── */}
25
- <div className="space-y-4">
26
- <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 px-1">Connection</div>
42
+ <div id="connection" className="space-y-4 scroll-mt-6">
43
+ <div className="flex items-center gap-2 px-1">
44
+ <div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Connection</div>
45
+ <div className="flex-1 h-px bg-[#2a2a4a]" />
46
+ </div>
27
47
 
28
48
  {/* CommHub Connection */}
29
49
  <section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
@@ -72,7 +92,7 @@ export default function SettingsPage() {
72
92
  <span className={`text-gray-300 ${valueClass}`}>{health?.sessions ?? '--'}</span>
73
93
  </div>
74
94
  <div className={rowClass}>
75
- <span className="text-gray-500">SSE Connections</span>
95
+ <span className="text-gray-500">SSE streams <span className="text-gray-600">· server</span></span>
76
96
  <span className={`text-gray-300 ${valueClass}`}>{health?.sse_connections ?? '--'}</span>
77
97
  </div>
78
98
  <div className={rowClass}>
@@ -88,7 +108,9 @@ export default function SettingsPage() {
88
108
  <div className="space-y-3 text-sm">
89
109
  <div className={rowClass}>
90
110
  <span className="text-gray-500">Version</span>
91
- <span className={`text-gray-300 ${valueClass}`}>V2.1</span>
111
+ <span className={`text-gray-300 font-mono ${valueClass}`} title="From package.json @sleep2agi/agent-network-dashboard">
112
+ {DASHBOARD_VERSION}
113
+ </span>
92
114
  </div>
93
115
  <div className={rowClass}>
94
116
  <span className="text-gray-500">Data Layer</span>
@@ -96,15 +118,18 @@ export default function SettingsPage() {
96
118
  </div>
97
119
  <div className={rowClass}>
98
120
  <span className="text-gray-500">Pages</span>
99
- <span className={`text-gray-300 ${valueClass}`}>Overview, Tasks, Nodes, Messages, Settings</span>
121
+ <span className={`text-gray-300 ${valueClass}`}>Overview, Tasks, Nodes, Messages, Networks, Audit, Server Logs, Admin, Settings</span>
100
122
  </div>
101
123
  </div>
102
124
  </section>
103
125
  </div>
104
126
 
105
127
  {/* ── Group: Account ────────────────────────────────────── */}
106
- <div className="space-y-4">
107
- <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 px-1">Account</div>
128
+ <div id="account" className="space-y-4 scroll-mt-6">
129
+ <div className="flex items-center gap-2 px-1">
130
+ <div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Account</div>
131
+ <div className="flex-1 h-px bg-[#2a2a4a]" />
132
+ </div>
108
133
 
109
134
  {/* License */}
110
135
  <section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
@@ -232,8 +257,11 @@ export default function SettingsPage() {
232
257
  </div>
233
258
 
234
259
  {/* ── Group: Resources ──────────────────────────────────── */}
235
- <div className="space-y-4">
236
- <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 px-1">Resources</div>
260
+ <div id="resources" className="space-y-4 scroll-mt-6">
261
+ <div className="flex items-center gap-2 px-1">
262
+ <div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Resources</div>
263
+ <div className="flex-1 h-px bg-[#2a2a4a]" />
264
+ </div>
237
265
 
238
266
  {/* API Tokens + Networks */}
239
267
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
@@ -4,6 +4,7 @@ import { use } from 'react';
4
4
  import Link from 'next/link';
5
5
  import useSWR from 'swr';
6
6
  import { timeAgo } from '../../components/utils';
7
+ import { STATUS_CHIP_CLASS } from '../../lib/status';
7
8
 
8
9
  const fetcher = async (url: string) => {
9
10
  const res = await fetch(url);
@@ -55,18 +56,6 @@ function Timeline({ steps }: { steps: TimelineStep[] }) {
55
56
  );
56
57
  }
57
58
 
58
- const STATUS_COLORS: Record<string, string> = {
59
- created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',
60
- delivered: 'bg-blue-500/10 text-blue-300 border-blue-500/20',
61
- acked: 'bg-cyan-500/10 text-cyan-300 border-cyan-500/20',
62
- running: 'bg-green-500/10 text-green-300 border-green-500/20',
63
- replied: 'bg-purple-500/10 text-purple-300 border-purple-500/20',
64
- closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
65
- failed: 'bg-red-500/10 text-red-300 border-red-500/20',
66
- cancelled: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',
67
- expired: 'bg-orange-500/10 text-orange-300 border-orange-500/20',
68
- };
69
-
70
59
  export default function TaskDetailPage({ params }: { params: Promise<{ id: string }> }) {
71
60
  const { id } = use(params);
72
61
  const { data, isLoading } = useSWR(
@@ -105,7 +94,7 @@ export default function TaskDetailPage({ params }: { params: Promise<{ id: strin
105
94
  );
106
95
  }
107
96
 
108
- const statusColor = STATUS_COLORS[task.status] || STATUS_COLORS.created;
97
+ const statusColor = STATUS_CHIP_CLASS[task.status] || STATUS_CHIP_CLASS.created;
109
98
  const steps: TimelineStep[] = [
110
99
  { label: 'Created', time: task.created_at, color: 'text-gray-400' },
111
100
  { label: 'Delivered', time: task.delivered_at, color: 'text-blue-400' },
@@ -7,6 +7,8 @@ import { useNetworkId } from '../lib/network-context';
7
7
  import { TaskDrawer } from '../components/TaskDrawer';
8
8
  import { EmptyState } from '../components/EmptyState';
9
9
  import { AliasAvatar } from '../components/AliasAvatar';
10
+ import { previewContent } from '../components/utils';
11
+ import { TASK_STATUSES, STATUS_CHIP_CLASS, STATUS_DOT_HEX, STATUS_BAR_CLASS } from '../lib/status';
10
12
 
11
13
  interface Task {
12
14
  task_id: string;
@@ -24,48 +26,8 @@ interface Task {
24
26
  expires_at: string;
25
27
  }
26
28
 
27
- const STATUS_OPTIONS = [
28
- '',
29
- 'created',
30
- 'delivered',
31
- 'acked',
32
- 'running',
33
- 'replied',
34
- 'closed',
35
- 'failed',
36
- 'cancelled',
37
- 'expired',
38
- ];
39
-
40
- const STATUS_COLORS: Record<string, string> = {
41
- created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',
42
- delivered: 'bg-blue-500/10 text-blue-300 border-blue-500/20',
43
- acked: 'bg-cyan-500/10 text-cyan-300 border-cyan-500/20',
44
- running: 'bg-green-500/10 text-green-300 border-green-500/20',
45
- replied: 'bg-purple-500/10 text-purple-300 border-purple-500/20',
46
- closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
47
- failed: 'bg-red-500/10 text-red-300 border-red-500/20',
48
- cancelled: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',
49
- expired: 'bg-orange-500/10 text-orange-300 border-orange-500/20',
50
- };
51
-
52
- /** Small colored dot per status — visible on every tab (active + inactive)
53
- * so users can scan the row at a glance. Hex inlined to avoid Tailwind
54
- * purging dynamic `bg-${family}-400` class names. */
55
- const STATUS_DOTS: Record<string, string> = {
56
- created: '#9ca3af',
57
- delivered: '#60a5fa',
58
- acked: '#22d3ee',
59
- running: '#4ade80',
60
- replied: '#a78bfa',
61
- closed: '#6b7280',
62
- failed: '#f87171',
63
- cancelled: '#facc15',
64
- expired: '#fb923c',
65
- };
66
-
67
29
  function statusBadge(status: string) {
68
- const color = STATUS_COLORS[status] || 'bg-gray-500/10 text-gray-400 border-gray-500/20';
30
+ const color = STATUS_CHIP_CLASS[status] || 'bg-gray-500/10 text-gray-400 border-gray-500/20';
69
31
  return `text-xs px-2 py-0.5 rounded-md border ${color}`;
70
32
  }
71
33
 
@@ -145,11 +107,25 @@ function TasksContent() {
145
107
  {/* Header */}
146
108
  <div className="flex items-center gap-4 mb-6">
147
109
  <h1 className="text-2xl font-bold text-white lg:ml-0 ml-10">Tasks</h1>
148
- <span className="text-xs bg-blue-900/30 text-blue-400 px-2 py-0.5 rounded-full border border-blue-800/30">
149
- {count}
110
+ {/* Round 88: pagination-aware chip. When tasks.length < count
111
+ the API has more rows than the current limit=100 slice;
112
+ show `loaded / total` so users notice. Otherwise just the
113
+ single total — matches r87 /logs pattern. */}
114
+ <span
115
+ className="text-xs bg-blue-900/30 text-blue-400 px-2 py-0.5 rounded-full border border-blue-800/30 tabular-nums"
116
+ title={tasks.length < count ? `Showing ${tasks.length} of ${count} tasks` : undefined}
117
+ >
118
+ {tasks.length < count ? `${tasks.length} / ${count}` : count}
150
119
  </span>
151
120
  </div>
152
121
 
122
+ {/* Round 75: hide the status tab strip + From/To filter row when
123
+ there are no tasks at all. Each tab would show "0" and the
124
+ filter inputs would have nothing to act on — pure noise above
125
+ the empty-state CTA. When at least one task exists, the chrome
126
+ comes back even if the current filter happens to hide
127
+ everything (so users can clear filters). */}
128
+ {tasks.length > 0 && <>
153
129
  {/* Status Tabs — color-coded dots per status family so users can
154
130
  scan the row at a glance. Active tab gets full chip styling
155
131
  (bg + border + text color), inactive tabs only carry the dot
@@ -159,23 +135,39 @@ function TasksContent() {
159
135
  elements) hinting more content. */}
160
136
  <div className="anet-tabstrip-wrap mb-4">
161
137
  <div className="anet-tabstrip flex sm:flex-wrap gap-1 bg-[#111128] rounded-lg border border-[#2a2a4a] p-1 overflow-x-auto sm:overflow-x-visible scrollbar-thin">
162
- {['', ...STATUS_OPTIONS.filter(Boolean)].map(s => (
163
- <button key={s} onClick={() => setFilterStatus(s)}
164
- className={`px-3 py-1.5 rounded-md text-xs transition-colors flex items-center gap-1.5 shrink-0 whitespace-nowrap ${
165
- filterStatus === s
166
- ? `${STATUS_COLORS[s] || 'bg-cyan-500/10 text-cyan-300 border-cyan-500/20'} border`
167
- : 'text-gray-500 hover:text-gray-300 hover:bg-[#1a1a2a]/40'
168
- }`}>
169
- {s && (
170
- <span
171
- aria-hidden
172
- className="inline-block w-1.5 h-1.5 rounded-full shrink-0"
173
- style={{ backgroundColor: STATUS_DOTS[s] || '#6b7280' }}
174
- />
175
- )}
176
- <span>{s || 'All'}</span>
177
- </button>
178
- ))}
138
+ {(() => {
139
+ // Round 56: compute per-status counts so the tab strip doubles as a
140
+ // status distribution dashboard. Matches Audit Log (r43) + Overview
141
+ // agent filter (r34) chip-with-count pattern.
142
+ const counts: Record<string, number> = {};
143
+ tasks.forEach(t => { counts[t.status] = (counts[t.status] || 0) + 1; });
144
+ return ['', ...TASK_STATUSES].map(s => {
145
+ const count = s === '' ? tasks.length : counts[s] || 0;
146
+ const isActive = filterStatus === s;
147
+ return (
148
+ <button
149
+ key={s}
150
+ onClick={() => setFilterStatus(s)}
151
+ disabled={count === 0 && s !== '' && !isActive}
152
+ className={`px-3 py-1.5 rounded-md text-xs transition-colors flex items-center gap-1.5 shrink-0 whitespace-nowrap disabled:opacity-30 disabled:cursor-not-allowed ${
153
+ isActive
154
+ ? `${STATUS_CHIP_CLASS[s] || 'bg-cyan-500/10 text-cyan-300 border-cyan-500/20'} border`
155
+ : 'text-gray-500 hover:text-gray-300 hover:bg-[#1a1a2a]/40'
156
+ }`}
157
+ >
158
+ {s && (
159
+ <span
160
+ aria-hidden
161
+ className="inline-block w-1.5 h-1.5 rounded-full shrink-0"
162
+ style={{ backgroundColor: STATUS_DOT_HEX[s] || '#6b7280' }}
163
+ />
164
+ )}
165
+ <span>{s || 'All'}</span>
166
+ <span className={`text-[10px] tabular-nums ${isActive ? 'opacity-80' : 'text-gray-600'}`}>{count}</span>
167
+ </button>
168
+ );
169
+ });
170
+ })()}
179
171
  </div>
180
172
  </div>
181
173
 
@@ -211,27 +203,28 @@ function TasksContent() {
211
203
  </button>
212
204
  )}
213
205
  </div>
206
+ </>}
214
207
 
215
208
  {/* Status distribution */}
216
209
  {tasks.length > 0 && !filterStatus && (() => {
217
210
  const stats: Record<string, number> = {};
218
211
  tasks.forEach(t => { stats[t.status] = (stats[t.status] || 0) + 1; });
219
212
  const total = tasks.length || 1;
220
- const bars = [
221
- { key: 'running', color: 'bg-green-500', text: 'text-green-400' },
222
- { key: 'delivered', color: 'bg-blue-500', text: 'text-blue-400' },
223
- { key: 'replied', color: 'bg-purple-500', text: 'text-purple-400' },
224
- { key: 'failed', color: 'bg-red-500', text: 'text-red-400' },
225
- { key: 'expired', color: 'bg-orange-500', text: 'text-orange-400' },
226
- ].filter(b => stats[b.key]);
213
+ // Round 67: derive from shared TASK_STATUSES + STATUS_BAR_CLASS in
214
+ // app/lib/status.ts. Adding a new lifecycle status updates badge,
215
+ // tab dots, and bar in one edit.
216
+ const bars = TASK_STATUSES
217
+ .map(key => ({ key, color: STATUS_BAR_CLASS[key] }))
218
+ .filter(b => stats[b.key]);
227
219
  if (!bars.length) return null;
220
+ // Round 63: removed the per-status legend row beneath the bar —
221
+ // it duplicated the counts already shown in the tab chip strip
222
+ // since r56. The proportional bar stays because it adds a visual
223
+ // "shape of workload" that the chip numbers don't convey.
228
224
  return (
229
225
  <div className="mb-6">
230
226
  <div className="flex h-2 rounded-full overflow-hidden bg-gray-800">
231
- {bars.map(b => <div key={b.key} className={b.color} style={{ width: `${(stats[b.key]/total)*100}%` }} />)}
232
- </div>
233
- <div className="flex flex-wrap gap-3 mt-1.5 text-[10px] text-gray-500">
234
- {bars.map(b => <span key={b.key}><span className={`inline-block w-2 h-2 rounded-full ${b.color} mr-1`} />{stats[b.key]} {b.key}</span>)}
227
+ {bars.map(b => <div key={b.key} className={b.color} style={{ width: `${(stats[b.key]/total)*100}%` }} title={`${b.key}: ${stats[b.key]}`} />)}
235
228
  </div>
236
229
  </div>
237
230
  );
@@ -301,7 +294,7 @@ function TasksContent() {
301
294
  <span className="truncate text-sm text-gray-200">{t.to_name || '--'}</span>
302
295
  </div>
303
296
  <div className="col-span-4 text-xs text-gray-400 truncate" title={t.content}>
304
- {t.content || '--'}
297
+ {previewContent(t.content)}
305
298
  </div>
306
299
  <div className="col-span-1">
307
300
  <span className={`text-xs ${priorityBadge(t.priority)}`}>{t.priority || 'normal'}</span>
@@ -340,7 +333,7 @@ function TasksContent() {
340
333
  {t.to_name && <AliasAvatar alias={t.to_name} size={16} />}
341
334
  <span className="truncate max-w-[40%]">{t.to_name || '--'}</span>
342
335
  </div>
343
- <div className="text-xs text-gray-400 line-clamp-1">{t.content || '--'}</div>
336
+ <div className="text-xs text-gray-400 line-clamp-1" title={t.content}>{previewContent(t.content)}</div>
344
337
  <div className="text-xs text-gray-600">{timeAgo(t.created_at)}</div>
345
338
  </div>
346
339
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network-dashboard",
3
- "version": "0.4.5-preview.9",
3
+ "version": "0.4.5-preview.91",
4
4
  "description": "Agent Network Dashboard — Web UI for managing AI Agent networks",
5
5
  "scripts": {
6
6
  "dev": "next dev",
Binary file