@monoes/monomindcli 1.11.11 → 1.11.12

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 (316) hide show
  1. package/.claude/commands/mastermind/idea.md +1 -1
  2. package/.claude/commands/mastermind/master.md +1 -1
  3. package/.claude/skills/mastermind/_protocol.md +4 -4
  4. package/.claude/skills/mastermind/autodev.md +1 -1
  5. package/.claude/skills/mastermind/build.md +3 -3
  6. package/.claude/skills/mastermind/content.md +3 -3
  7. package/.claude/skills/mastermind/createorg.md +2 -2
  8. package/.claude/skills/mastermind/finance.md +3 -3
  9. package/.claude/skills/mastermind/marketing.md +3 -3
  10. package/.claude/skills/mastermind/ops.md +3 -3
  11. package/.claude/skills/mastermind/release.md +3 -3
  12. package/.claude/skills/mastermind/research.md +3 -3
  13. package/.claude/skills/mastermind/review.md +3 -3
  14. package/.claude/skills/mastermind/sales.md +3 -3
  15. package/dist/src/init/statusline-generator.js +3 -3
  16. package/dist/src/observability/replay-reader.d.ts +1 -1
  17. package/dist/src/observability/replay-reader.d.ts.map +1 -1
  18. package/dist/src/update/checker.d.ts.map +1 -1
  19. package/dist/src/update/checker.js +24 -7
  20. package/dist/src/update/checker.js.map +1 -1
  21. package/dist/src/update/index.d.ts.map +1 -1
  22. package/dist/src/update/index.js +3 -6
  23. package/dist/src/update/index.js.map +1 -1
  24. package/dist/tsconfig.tsbuildinfo +1 -1
  25. package/package.json +1 -1
  26. package/dist/src/agents/halt-signal.d.ts +0 -25
  27. package/dist/src/agents/halt-signal.d.ts.map +0 -1
  28. package/dist/src/agents/halt-signal.js +0 -76
  29. package/dist/src/agents/halt-signal.js.map +0 -1
  30. package/dist/src/agents/index.d.ts +0 -18
  31. package/dist/src/agents/index.d.ts.map +0 -1
  32. package/dist/src/agents/index.js +0 -13
  33. package/dist/src/agents/index.js.map +0 -1
  34. package/dist/src/agents/managed-agent.d.ts +0 -41
  35. package/dist/src/agents/managed-agent.d.ts.map +0 -1
  36. package/dist/src/agents/managed-agent.js +0 -69
  37. package/dist/src/agents/managed-agent.js.map +0 -1
  38. package/dist/src/agents/prompt-experiment.d.ts +0 -23
  39. package/dist/src/agents/prompt-experiment.d.ts.map +0 -1
  40. package/dist/src/agents/prompt-experiment.js +0 -49
  41. package/dist/src/agents/prompt-experiment.js.map +0 -1
  42. package/dist/src/agents/prompt-version-manager.d.ts +0 -22
  43. package/dist/src/agents/prompt-version-manager.d.ts.map +0 -1
  44. package/dist/src/agents/prompt-version-manager.js +0 -80
  45. package/dist/src/agents/prompt-version-manager.js.map +0 -1
  46. package/dist/src/agents/registry-query.d.ts +0 -71
  47. package/dist/src/agents/registry-query.d.ts.map +0 -1
  48. package/dist/src/agents/registry-query.js +0 -125
  49. package/dist/src/agents/registry-query.js.map +0 -1
  50. package/dist/src/agents/score-decay.d.ts +0 -19
  51. package/dist/src/agents/score-decay.d.ts.map +0 -1
  52. package/dist/src/agents/score-decay.js +0 -22
  53. package/dist/src/agents/score-decay.js.map +0 -1
  54. package/dist/src/agents/shared-instructions-loader.d.ts +0 -13
  55. package/dist/src/agents/shared-instructions-loader.d.ts.map +0 -1
  56. package/dist/src/agents/shared-instructions-loader.js +0 -40
  57. package/dist/src/agents/shared-instructions-loader.js.map +0 -1
  58. package/dist/src/agents/specialization-scorer.d.ts +0 -54
  59. package/dist/src/agents/specialization-scorer.d.ts.map +0 -1
  60. package/dist/src/agents/specialization-scorer.js +0 -212
  61. package/dist/src/agents/specialization-scorer.js.map +0 -1
  62. package/dist/src/agents/termination-watcher.d.ts +0 -30
  63. package/dist/src/agents/termination-watcher.d.ts.map +0 -1
  64. package/dist/src/agents/termination-watcher.js +0 -84
  65. package/dist/src/agents/termination-watcher.js.map +0 -1
  66. package/dist/src/agents/trigger-index.d.ts +0 -20
  67. package/dist/src/agents/trigger-index.d.ts.map +0 -1
  68. package/dist/src/agents/trigger-index.js +0 -38
  69. package/dist/src/agents/trigger-index.js.map +0 -1
  70. package/dist/src/agents/trigger-scanner.d.ts +0 -64
  71. package/dist/src/agents/trigger-scanner.d.ts.map +0 -1
  72. package/dist/src/agents/trigger-scanner.js +0 -308
  73. package/dist/src/agents/trigger-scanner.js.map +0 -1
  74. package/dist/src/agents/version-diff.d.ts +0 -18
  75. package/dist/src/agents/version-diff.d.ts.map +0 -1
  76. package/dist/src/agents/version-diff.js +0 -64
  77. package/dist/src/agents/version-diff.js.map +0 -1
  78. package/dist/src/agents/version-store.d.ts +0 -60
  79. package/dist/src/agents/version-store.d.ts.map +0 -1
  80. package/dist/src/agents/version-store.js +0 -235
  81. package/dist/src/agents/version-store.js.map +0 -1
  82. package/dist/src/benchmarks/pretrain/index.d.ts +0 -45
  83. package/dist/src/benchmarks/pretrain/index.d.ts.map +0 -1
  84. package/dist/src/benchmarks/pretrain/index.js +0 -404
  85. package/dist/src/benchmarks/pretrain/index.js.map +0 -1
  86. package/dist/src/commands/agent-wasm.d.ts +0 -14
  87. package/dist/src/commands/agent-wasm.d.ts.map +0 -1
  88. package/dist/src/commands/agent-wasm.js +0 -333
  89. package/dist/src/commands/agent-wasm.js.map +0 -1
  90. package/dist/src/commands/embeddings.d.ts.map +0 -1
  91. package/dist/src/commands/embeddings.js.map +0 -1
  92. package/dist/src/commands/ui.js +0 -68
  93. package/dist/src/consensus/index.d.ts +0 -7
  94. package/dist/src/consensus/index.d.ts.map +0 -1
  95. package/dist/src/consensus/index.js +0 -6
  96. package/dist/src/consensus/index.js.map +0 -1
  97. package/dist/src/context/context-provider.d.ts +0 -44
  98. package/dist/src/context/context-provider.d.ts.map +0 -1
  99. package/dist/src/context/context-provider.js +0 -25
  100. package/dist/src/context/context-provider.js.map +0 -1
  101. package/dist/src/context/git-state-provider.d.ts +0 -12
  102. package/dist/src/context/git-state-provider.d.ts.map +0 -1
  103. package/dist/src/context/git-state-provider.js +0 -34
  104. package/dist/src/context/git-state-provider.js.map +0 -1
  105. package/dist/src/context/index.d.ts +0 -12
  106. package/dist/src/context/index.d.ts.map +0 -1
  107. package/dist/src/context/index.js +0 -12
  108. package/dist/src/context/index.js.map +0 -1
  109. package/dist/src/context/project-conventions-provider.d.ts +0 -15
  110. package/dist/src/context/project-conventions-provider.d.ts.map +0 -1
  111. package/dist/src/context/project-conventions-provider.js +0 -19
  112. package/dist/src/context/project-conventions-provider.js.map +0 -1
  113. package/dist/src/context/prompt-assembler.d.ts +0 -26
  114. package/dist/src/context/prompt-assembler.d.ts.map +0 -1
  115. package/dist/src/context/prompt-assembler.js +0 -93
  116. package/dist/src/context/prompt-assembler.js.map +0 -1
  117. package/dist/src/context/task-history-provider.d.ts +0 -24
  118. package/dist/src/context/task-history-provider.d.ts.map +0 -1
  119. package/dist/src/context/task-history-provider.js +0 -32
  120. package/dist/src/context/task-history-provider.js.map +0 -1
  121. package/dist/src/context/user-preferences-provider.d.ts +0 -14
  122. package/dist/src/context/user-preferences-provider.d.ts.map +0 -1
  123. package/dist/src/context/user-preferences-provider.js +0 -27
  124. package/dist/src/context/user-preferences-provider.js.map +0 -1
  125. package/dist/src/dlq/dlq-reader.d.ts +0 -31
  126. package/dist/src/dlq/dlq-reader.d.ts.map +0 -1
  127. package/dist/src/dlq/dlq-reader.js +0 -81
  128. package/dist/src/dlq/dlq-reader.js.map +0 -1
  129. package/dist/src/dlq/dlq-writer.d.ts +0 -24
  130. package/dist/src/dlq/dlq-writer.d.ts.map +0 -1
  131. package/dist/src/dlq/dlq-writer.js +0 -65
  132. package/dist/src/dlq/dlq-writer.js.map +0 -1
  133. package/dist/src/dlq/index.d.ts +0 -10
  134. package/dist/src/dlq/index.d.ts.map +0 -1
  135. package/dist/src/dlq/index.js +0 -7
  136. package/dist/src/dlq/index.js.map +0 -1
  137. package/dist/src/eval/dataset-manager.d.ts +0 -33
  138. package/dist/src/eval/dataset-manager.d.ts.map +0 -1
  139. package/dist/src/eval/dataset-manager.js +0 -107
  140. package/dist/src/eval/dataset-manager.js.map +0 -1
  141. package/dist/src/eval/dataset-runner.d.ts +0 -23
  142. package/dist/src/eval/dataset-runner.d.ts.map +0 -1
  143. package/dist/src/eval/dataset-runner.js +0 -59
  144. package/dist/src/eval/dataset-runner.js.map +0 -1
  145. package/dist/src/eval/index.d.ts +0 -10
  146. package/dist/src/eval/index.d.ts.map +0 -1
  147. package/dist/src/eval/index.js +0 -7
  148. package/dist/src/eval/index.js.map +0 -1
  149. package/dist/src/eval/trace-collector.d.ts +0 -40
  150. package/dist/src/eval/trace-collector.d.ts.map +0 -1
  151. package/dist/src/eval/trace-collector.js +0 -102
  152. package/dist/src/eval/trace-collector.js.map +0 -1
  153. package/dist/src/graph/enrich.mjs +0 -362
  154. package/dist/src/infrastructure/in-memory-repositories.d.ts +0 -68
  155. package/dist/src/infrastructure/in-memory-repositories.d.ts.map +0 -1
  156. package/dist/src/infrastructure/in-memory-repositories.js +0 -264
  157. package/dist/src/infrastructure/in-memory-repositories.js.map +0 -1
  158. package/dist/src/interactive/interrupt.d.ts +0 -22
  159. package/dist/src/interactive/interrupt.d.ts.map +0 -1
  160. package/dist/src/interactive/interrupt.js +0 -71
  161. package/dist/src/interactive/interrupt.js.map +0 -1
  162. package/dist/src/mcp/deprecation-injector.d.ts +0 -25
  163. package/dist/src/mcp/deprecation-injector.d.ts.map +0 -1
  164. package/dist/src/mcp/deprecation-injector.js +0 -48
  165. package/dist/src/mcp/deprecation-injector.js.map +0 -1
  166. package/dist/src/mcp/tool-registry.d.ts +0 -61
  167. package/dist/src/mcp/tool-registry.d.ts.map +0 -1
  168. package/dist/src/mcp/tool-registry.js +0 -246
  169. package/dist/src/mcp/tool-registry.js.map +0 -1
  170. package/dist/src/mcp-tools/wasm-agent-tools.d.ts +0 -9
  171. package/dist/src/mcp-tools/wasm-agent-tools.d.ts.map +0 -1
  172. package/dist/src/mcp-tools/wasm-agent-tools.js +0 -230
  173. package/dist/src/mcp-tools/wasm-agent-tools.js.map +0 -1
  174. package/dist/src/model/complexity-scorer.d.ts +0 -21
  175. package/dist/src/model/complexity-scorer.d.ts.map +0 -1
  176. package/dist/src/model/complexity-scorer.js +0 -106
  177. package/dist/src/model/complexity-scorer.js.map +0 -1
  178. package/dist/src/model/index.d.ts +0 -4
  179. package/dist/src/model/index.d.ts.map +0 -1
  180. package/dist/src/model/index.js +0 -4
  181. package/dist/src/model/index.js.map +0 -1
  182. package/dist/src/model/model-settings.d.ts +0 -22
  183. package/dist/src/model/model-settings.d.ts.map +0 -1
  184. package/dist/src/model/model-settings.js +0 -33
  185. package/dist/src/model/model-settings.js.map +0 -1
  186. package/dist/src/model/model-tier-resolver.d.ts +0 -24
  187. package/dist/src/model/model-tier-resolver.d.ts.map +0 -1
  188. package/dist/src/model/model-tier-resolver.js +0 -65
  189. package/dist/src/model/model-tier-resolver.js.map +0 -1
  190. package/dist/src/monovector/capabilities.d.ts +0 -34
  191. package/dist/src/monovector/capabilities.d.ts.map +0 -1
  192. package/dist/src/monovector/capabilities.js +0 -37
  193. package/dist/src/monovector/capabilities.js.map +0 -1
  194. package/dist/src/orchestration/index.d.ts +0 -7
  195. package/dist/src/orchestration/index.d.ts.map +0 -1
  196. package/dist/src/orchestration/index.js +0 -6
  197. package/dist/src/orchestration/index.js.map +0 -1
  198. package/dist/src/orchestration/mode-dispatcher.d.ts +0 -11
  199. package/dist/src/orchestration/mode-dispatcher.d.ts.map +0 -1
  200. package/dist/src/orchestration/mode-dispatcher.js +0 -31
  201. package/dist/src/orchestration/mode-dispatcher.js.map +0 -1
  202. package/dist/src/orchestration/routing-modes.d.ts +0 -68
  203. package/dist/src/orchestration/routing-modes.d.ts.map +0 -1
  204. package/dist/src/orchestration/routing-modes.js +0 -180
  205. package/dist/src/orchestration/routing-modes.js.map +0 -1
  206. package/dist/src/plugins/tests/demo-plugin-store.d.ts +0 -7
  207. package/dist/src/plugins/tests/demo-plugin-store.d.ts.map +0 -1
  208. package/dist/src/plugins/tests/demo-plugin-store.js +0 -126
  209. package/dist/src/plugins/tests/demo-plugin-store.js.map +0 -1
  210. package/dist/src/plugins/tests/standalone-test.d.ts +0 -12
  211. package/dist/src/plugins/tests/standalone-test.d.ts.map +0 -1
  212. package/dist/src/plugins/tests/standalone-test.js +0 -188
  213. package/dist/src/plugins/tests/standalone-test.js.map +0 -1
  214. package/dist/src/plugins/tests/test-plugin-store.d.ts +0 -7
  215. package/dist/src/plugins/tests/test-plugin-store.d.ts.map +0 -1
  216. package/dist/src/plugins/tests/test-plugin-store.js +0 -206
  217. package/dist/src/plugins/tests/test-plugin-store.js.map +0 -1
  218. package/dist/src/runtime/headless.d.ts +0 -60
  219. package/dist/src/runtime/headless.d.ts.map +0 -1
  220. package/dist/src/runtime/headless.js +0 -284
  221. package/dist/src/runtime/headless.js.map +0 -1
  222. package/dist/src/services/agentic-flow-bridge.d.ts +0 -50
  223. package/dist/src/services/agentic-flow-bridge.d.ts.map +0 -1
  224. package/dist/src/services/agentic-flow-bridge.js +0 -95
  225. package/dist/src/services/agentic-flow-bridge.js.map +0 -1
  226. package/dist/src/services/container-worker-pool.d.ts +0 -197
  227. package/dist/src/services/container-worker-pool.d.ts.map +0 -1
  228. package/dist/src/services/container-worker-pool.js +0 -623
  229. package/dist/src/services/container-worker-pool.js.map +0 -1
  230. package/dist/src/services/index.d.ts +0 -13
  231. package/dist/src/services/index.d.ts.map +0 -1
  232. package/dist/src/services/index.js +0 -11
  233. package/dist/src/services/index.js.map +0 -1
  234. package/dist/src/services/worker-queue.d.ts +0 -201
  235. package/dist/src/services/worker-queue.d.ts.map +0 -1
  236. package/dist/src/services/worker-queue.js +0 -594
  237. package/dist/src/services/worker-queue.js.map +0 -1
  238. package/dist/src/swarm/communication-graph.d.ts +0 -25
  239. package/dist/src/swarm/communication-graph.d.ts.map +0 -1
  240. package/dist/src/swarm/communication-graph.js +0 -77
  241. package/dist/src/swarm/communication-graph.js.map +0 -1
  242. package/dist/src/swarm/flow-enforcer.d.ts +0 -31
  243. package/dist/src/swarm/flow-enforcer.d.ts.map +0 -1
  244. package/dist/src/swarm/flow-enforcer.js +0 -61
  245. package/dist/src/swarm/flow-enforcer.js.map +0 -1
  246. package/dist/src/swarm/flow-visualizer.d.ts +0 -19
  247. package/dist/src/swarm/flow-visualizer.d.ts.map +0 -1
  248. package/dist/src/swarm/flow-visualizer.js +0 -68
  249. package/dist/src/swarm/flow-visualizer.js.map +0 -1
  250. package/dist/src/transfer/deploy-seraphine.d.ts +0 -13
  251. package/dist/src/transfer/deploy-seraphine.d.ts.map +0 -1
  252. package/dist/src/transfer/deploy-seraphine.js +0 -205
  253. package/dist/src/transfer/deploy-seraphine.js.map +0 -1
  254. package/dist/src/transfer/store/tests/standalone-test.d.ts +0 -12
  255. package/dist/src/transfer/store/tests/standalone-test.d.ts.map +0 -1
  256. package/dist/src/transfer/store/tests/standalone-test.js +0 -190
  257. package/dist/src/transfer/store/tests/standalone-test.js.map +0 -1
  258. package/dist/src/transfer/test-seraphine.d.ts +0 -6
  259. package/dist/src/transfer/test-seraphine.d.ts.map +0 -1
  260. package/dist/src/transfer/test-seraphine.js +0 -105
  261. package/dist/src/transfer/test-seraphine.js.map +0 -1
  262. package/dist/src/transfer/tests/test-store.d.ts +0 -7
  263. package/dist/src/transfer/tests/test-store.d.ts.map +0 -1
  264. package/dist/src/transfer/tests/test-store.js +0 -214
  265. package/dist/src/transfer/tests/test-store.js.map +0 -1
  266. package/dist/src/ui/.monomind/data/pending-insights.jsonl +0 -0
  267. package/dist/src/ui/.monomind/data/ranked-context.json +0 -5
  268. package/dist/src/ui/.monomind/loops/mastermind-review-1778664132789.json +0 -16
  269. package/dist/src/ui/.monomind/sessions/current.json +0 -13
  270. package/dist/src/ui/.monomind/sessions/session-1776778451399.json +0 -15
  271. package/dist/src/ui/collector.mjs +0 -646
  272. package/dist/src/ui/dashboard.html +0 -9694
  273. package/dist/src/ui/data/mastermind-events.jsonl +0 -59
  274. package/dist/src/ui/data/mastermind-sessions.json +0 -1
  275. package/dist/src/ui/orgs.html +0 -1360
  276. package/dist/src/ui/server.mjs +0 -4334
  277. package/dist/src/workflow/condition-evaluator.d.ts +0 -10
  278. package/dist/src/workflow/condition-evaluator.d.ts.map +0 -1
  279. package/dist/src/workflow/condition-evaluator.js +0 -82
  280. package/dist/src/workflow/condition-evaluator.js.map +0 -1
  281. package/dist/src/workflow/context-resolver.d.ts +0 -12
  282. package/dist/src/workflow/context-resolver.d.ts.map +0 -1
  283. package/dist/src/workflow/context-resolver.js +0 -23
  284. package/dist/src/workflow/context-resolver.js.map +0 -1
  285. package/dist/src/workflow/dag-builder.d.ts +0 -17
  286. package/dist/src/workflow/dag-builder.d.ts.map +0 -1
  287. package/dist/src/workflow/dag-builder.js +0 -129
  288. package/dist/src/workflow/dag-builder.js.map +0 -1
  289. package/dist/src/workflow/dag-executor.d.ts +0 -9
  290. package/dist/src/workflow/dag-executor.d.ts.map +0 -1
  291. package/dist/src/workflow/dag-executor.js +0 -116
  292. package/dist/src/workflow/dag-executor.js.map +0 -1
  293. package/dist/src/workflow/dag-types.d.ts +0 -41
  294. package/dist/src/workflow/dag-types.d.ts.map +0 -1
  295. package/dist/src/workflow/dag-types.js +0 -8
  296. package/dist/src/workflow/dag-types.js.map +0 -1
  297. package/dist/src/workflow/dsl-parser.d.ts +0 -12
  298. package/dist/src/workflow/dsl-parser.d.ts.map +0 -1
  299. package/dist/src/workflow/dsl-parser.js +0 -20
  300. package/dist/src/workflow/dsl-parser.js.map +0 -1
  301. package/dist/src/workflow/dsl-schema.d.ts +0 -165
  302. package/dist/src/workflow/dsl-schema.d.ts.map +0 -1
  303. package/dist/src/workflow/dsl-schema.js +0 -82
  304. package/dist/src/workflow/dsl-schema.js.map +0 -1
  305. package/dist/src/workflow/index.d.ts +0 -13
  306. package/dist/src/workflow/index.d.ts.map +0 -1
  307. package/dist/src/workflow/index.js +0 -11
  308. package/dist/src/workflow/index.js.map +0 -1
  309. package/dist/src/workflow/template-engine.d.ts +0 -11
  310. package/dist/src/workflow/template-engine.d.ts.map +0 -1
  311. package/dist/src/workflow/template-engine.js +0 -40
  312. package/dist/src/workflow/template-engine.js.map +0 -1
  313. package/dist/src/workflow/workflow-executor.d.ts +0 -29
  314. package/dist/src/workflow/workflow-executor.d.ts.map +0 -1
  315. package/dist/src/workflow/workflow-executor.js +0 -227
  316. package/dist/src/workflow/workflow-executor.js.map +0 -1
@@ -1,1360 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>MASTERMIND ORGS</title>
7
- <style>
8
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
9
-
10
- :root {
11
- --bg: oklch(7% 0.010 186);
12
- --bg-panel: oklch(9% 0.012 186);
13
- --bg-hover: oklch(62% 0.20 186 / 0.06);
14
- --bg-active: oklch(62% 0.20 186 / 0.11);
15
- --teal: oklch(62% 0.20 186);
16
- --teal-dim: oklch(62% 0.20 186 / 0.18);
17
- --teal-glow: oklch(62% 0.20 186 / 0.08);
18
- --indigo: oklch(68% 0.18 252);
19
- --green: oklch(68% 0.20 150);
20
- --green-glow:oklch(68% 0.20 150 / 0.25);
21
- --amber: oklch(78% 0.18 80);
22
- --red: oklch(62% 0.22 25);
23
- --text: oklch(80% 0.012 186);
24
- --muted: oklch(52% 0.010 186);
25
- --dim: oklch(36% 0.008 186);
26
- --border: oklch(62% 0.20 186 / 0.12);
27
- --border-hi: oklch(62% 0.20 186 / 0.35);
28
- --mono: 'Azeret Mono', 'Space Mono', 'Courier New', monospace;
29
- }
30
-
31
- html, body {
32
- width: 100%; height: 100%; overflow: hidden;
33
- background: var(--bg);
34
- font-family: var(--mono);
35
- color: var(--text);
36
- font-size: 13px;
37
- line-height: 1.5;
38
- user-select: none;
39
- }
40
-
41
- /* ── Layout ─────────────────────────────────────────────────── */
42
- #app { display: flex; height: 100vh; }
43
-
44
- #sidebar {
45
- width: 260px; flex-shrink: 0;
46
- background: var(--bg-panel);
47
- border-right: 1px solid var(--border);
48
- display: flex; flex-direction: column;
49
- overflow: hidden;
50
- }
51
-
52
- #main {
53
- flex: 1; display: flex; flex-direction: column;
54
- overflow: hidden; min-width: 0;
55
- }
56
-
57
- /* ── Sidebar ─────────────────────────────────────────────────── */
58
- #sb-header {
59
- padding: 14px 14px 12px;
60
- border-bottom: 1px solid var(--border);
61
- flex-shrink: 0;
62
- }
63
-
64
- #sb-wordmark {
65
- display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
66
- }
67
-
68
- #sb-back {
69
- font-size: 9px; letter-spacing: 1px; color: var(--dim);
70
- text-decoration: none; padding: 3px 7px;
71
- border: 1px solid var(--border); border-radius: 3px;
72
- transition: color 0.12s, border-color 0.12s;
73
- }
74
- #sb-back:hover { color: var(--teal); border-color: var(--teal-dim); }
75
-
76
- #sb-title {
77
- font-size: 9px; letter-spacing: 4px; color: var(--teal);
78
- opacity: 0.8;
79
- }
80
-
81
- #sb-status-row {
82
- display: flex; align-items: center; gap: 6px;
83
- }
84
-
85
- .live-dot {
86
- width: 6px; height: 6px; border-radius: 50%;
87
- background: var(--dim); flex-shrink: 0;
88
- transition: background 0.4s, box-shadow 0.4s;
89
- }
90
- .live-dot.on {
91
- background: var(--green);
92
- box-shadow: 0 0 6px var(--green-glow);
93
- }
94
- @media (prefers-reduced-motion: no-preference) {
95
- .live-dot.on { animation: livepulse 2.2s ease-in-out infinite; }
96
- }
97
- @keyframes livepulse { 0%,100%{opacity:1} 50%{opacity:0.45} }
98
-
99
- #sb-status-text { font-size: 9px; letter-spacing: 2px; color: var(--dim); }
100
- #sb-running-count { font-size: 8px; color: var(--green); margin-left: auto; letter-spacing: 1px; }
101
-
102
- #sb-list {
103
- flex: 1; overflow-y: auto;
104
- padding: 6px 0;
105
- scrollbar-width: thin;
106
- scrollbar-color: var(--teal-dim) transparent;
107
- }
108
- #sb-list::-webkit-scrollbar { width: 3px; }
109
- #sb-list::-webkit-scrollbar-thumb { background: var(--teal-dim); border-radius: 2px; }
110
-
111
- .org-item {
112
- padding: 10px 14px;
113
- cursor: pointer;
114
- border-left: 2px solid transparent;
115
- transition: background 0.12s, border-color 0.12s;
116
- position: relative;
117
- }
118
- .org-item:hover { background: var(--bg-hover); }
119
- .org-item.active {
120
- background: var(--bg-active);
121
- border-left-color: var(--teal);
122
- }
123
- .org-item.running { border-left-color: var(--green); }
124
- .org-item.active.running { border-left-color: var(--teal); }
125
-
126
- .oi-header { display: flex; align-items: center; gap: 7px; margin-bottom: 3px; }
127
-
128
- .oi-dot {
129
- width: 5px; height: 5px; border-radius: 50%;
130
- background: var(--dim); flex-shrink: 0;
131
- }
132
- .oi-dot.running {
133
- background: var(--green);
134
- box-shadow: 0 0 4px var(--green);
135
- }
136
- @media (prefers-reduced-motion: no-preference) {
137
- .oi-dot.running { animation: livepulse 2s ease-in-out infinite; }
138
- }
139
-
140
- .oi-name {
141
- font-size: 11px; color: var(--text); font-weight: 500;
142
- white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
143
- flex: 1;
144
- }
145
-
146
- .oi-badge {
147
- font-size: 7px; padding: 1px 6px; border-radius: 2px; flex-shrink: 0;
148
- text-transform: uppercase; letter-spacing: 0.06em;
149
- }
150
- .oi-badge.running {
151
- background: oklch(68% 0.20 150 / 0.12);
152
- color: var(--green);
153
- border: 1px solid oklch(68% 0.20 150 / 0.28);
154
- }
155
- .oi-badge.idle {
156
- background: transparent; color: var(--dim);
157
- border: 1px solid var(--border);
158
- }
159
-
160
- .oi-goal {
161
- font-size: 9px; color: var(--muted); line-height: 1.5;
162
- white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
163
- padding-left: 12px;
164
- }
165
-
166
- .oi-meta {
167
- display: flex; gap: 8px; padding-left: 12px; margin-top: 3px;
168
- }
169
- .oi-chip {
170
- font-size: 8px; color: var(--dim); letter-spacing: 0.5px;
171
- }
172
- .oi-chip span { color: var(--muted); }
173
-
174
- #sb-empty {
175
- padding: 28px 16px; text-align: center;
176
- color: var(--dim); font-size: 9px; line-height: 1.8; letter-spacing: 0.5px;
177
- }
178
- #sb-empty .empty-icon {
179
- font-size: 24px; display: block; margin-bottom: 10px; opacity: 0.35;
180
- }
181
- #sb-empty .empty-cmd {
182
- display: inline-block; margin-top: 10px;
183
- color: var(--teal); border: 1px solid var(--teal-dim);
184
- padding: 3px 10px; border-radius: 3px; font-size: 8px; letter-spacing: 1px;
185
- }
186
-
187
- /* ── Main area ─────────────────────────────────────────────────── */
188
- #main-header {
189
- padding: 12px 20px 11px;
190
- border-bottom: 1px solid var(--border);
191
- display: flex; align-items: center; gap: 12px;
192
- flex-shrink: 0;
193
- }
194
-
195
- #org-label {
196
- font-size: 9px; letter-spacing: 4px; color: var(--teal);
197
- opacity: 0.7; text-transform: uppercase;
198
- }
199
-
200
- #org-name {
201
- font-size: 15px; letter-spacing: 2px; color: var(--text);
202
- font-weight: 600; text-transform: uppercase;
203
- }
204
-
205
- #org-status-badge {
206
- font-size: 8px; padding: 2px 8px; border-radius: 2px;
207
- text-transform: uppercase; letter-spacing: 0.1em; margin-left: 4px;
208
- }
209
- #org-status-badge.running {
210
- background: oklch(68% 0.20 150 / 0.12);
211
- color: var(--green); border: 1px solid oklch(68% 0.20 150 / 0.25);
212
- }
213
- #org-status-badge.idle {
214
- background: transparent; color: var(--dim);
215
- border: 1px solid var(--border);
216
- }
217
-
218
- .hdr-chip {
219
- font-size: 8px; color: var(--dim); letter-spacing: 0.5px;
220
- padding: 2px 8px; border: 1px solid var(--border); border-radius: 2px;
221
- }
222
- .hdr-chip span { color: var(--muted); }
223
-
224
- #main-tabs {
225
- display: flex; gap: 0; border-bottom: 1px solid var(--border);
226
- flex-shrink: 0; padding: 0 20px;
227
- }
228
-
229
- .tab-btn {
230
- font-size: 9px; letter-spacing: 2px; color: var(--dim);
231
- padding: 8px 14px; background: none; border: none; cursor: pointer;
232
- font-family: var(--mono); text-transform: uppercase;
233
- border-bottom: 2px solid transparent; margin-bottom: -1px;
234
- transition: color 0.12s, border-color 0.12s;
235
- }
236
- .tab-btn:hover { color: var(--muted); }
237
- .tab-btn.active { color: var(--teal); border-bottom-color: var(--teal); }
238
-
239
- #main-body { flex: 1; overflow-y: auto; overflow-x: hidden; }
240
- #main-body::-webkit-scrollbar { width: 4px; }
241
- #main-body::-webkit-scrollbar-thumb { background: var(--teal-dim); border-radius: 2px; }
242
-
243
- .tab-pane { display: none; }
244
- .tab-pane.active { display: block; }
245
-
246
- /* ── Empty / no-selection state ─────────────────────────────────── */
247
- #no-org-state {
248
- display: flex; flex-direction: column; align-items: center;
249
- justify-content: center; height: 100%; gap: 10px; opacity: 0.5;
250
- padding: 40px;
251
- }
252
- #no-org-state .ns-icon { font-size: 48px; opacity: 0.25; }
253
- #no-org-state .ns-text {
254
- font-size: 9px; letter-spacing: 3px; color: var(--dim);
255
- text-align: center;
256
- }
257
-
258
- /* ── Org Chart ──────────────────────────────────────────────────── */
259
- #chart-pane {
260
- padding: 20px;
261
- display: flex; flex-direction: column; gap: 16px;
262
- }
263
-
264
- #chart-svg-wrap {
265
- background: oklch(8% 0.01 186);
266
- border: 1px solid var(--border);
267
- border-radius: 4px;
268
- overflow: hidden; position: relative;
269
- }
270
-
271
- #org-chart-svg {
272
- width: 100%; display: block;
273
- }
274
-
275
- /* ── Info grid (goal / topology / created) ─────────────────────── */
276
- #org-meta-grid {
277
- display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1px;
278
- background: var(--border); border: 1px solid var(--border);
279
- border-radius: 3px; overflow: hidden;
280
- }
281
- .meta-cell {
282
- background: var(--bg-panel);
283
- padding: 10px 14px;
284
- }
285
- .meta-cell-label { font-size: 8px; letter-spacing: 2px; color: var(--dim); margin-bottom: 4px; }
286
- .meta-cell-value { font-size: 11px; color: var(--text); }
287
-
288
- /* ── Roles list ─────────────────────────────────────────────────── */
289
- #roles-pane { padding: 20px; }
290
-
291
- .roles-grid {
292
- display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
293
- gap: 10px;
294
- }
295
-
296
- .role-block {
297
- background: var(--bg-panel);
298
- border: 1px solid var(--border);
299
- border-radius: 3px; padding: 12px 14px;
300
- transition: border-color 0.12s;
301
- }
302
- .role-block:hover { border-color: var(--teal-dim); }
303
-
304
- .rb-header { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 6px; }
305
- .rb-dot {
306
- width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; margin-top: 4px;
307
- }
308
- .rb-title { font-size: 12px; color: var(--text); font-weight: 500; line-height: 1.3; }
309
- .rb-agent {
310
- margin-left: auto; flex-shrink: 0;
311
- font-size: 8px; padding: 1px 7px; border-radius: 2px;
312
- border: 1px solid var(--teal-dim); color: var(--teal); opacity: 0.85;
313
- letter-spacing: 0.5px;
314
- }
315
- .rb-id { font-size: 9px; color: var(--dim); margin-bottom: 5px; padding-left: 15px; }
316
- .rb-reports { font-size: 9px; color: var(--muted); padding-left: 15px; margin-bottom: 6px; }
317
- .rb-reports span { color: var(--indigo); }
318
-
319
- .rb-resps { padding-left: 15px; }
320
- .rb-resp-item {
321
- font-size: 9px; color: var(--dim); line-height: 1.6;
322
- display: flex; gap: 6px; align-items: flex-start;
323
- }
324
- .rb-resp-item::before {
325
- content: '—'; color: oklch(36% 0.008 186 / 0.5); flex-shrink: 0; margin-top: 1px;
326
- }
327
-
328
- /* ── Activity / event log ───────────────────────────────────────── */
329
- #activity-pane { padding: 20px; }
330
- #activity-log { display: flex; flex-direction: column; gap: 0; }
331
-
332
- .act-row {
333
- display: grid;
334
- grid-template-columns: 60px 90px 1fr;
335
- gap: 8px; padding: 5px 0;
336
- border-bottom: 1px solid oklch(62% 0.20 186 / 0.05);
337
- align-items: baseline; font-size: 10px;
338
- }
339
- .act-row:last-child { border-bottom: none; }
340
-
341
- .act-time { color: var(--dim); font-variant-numeric: tabular-nums; font-size: 9px; }
342
- .act-type { color: var(--teal); font-weight: 500; letter-spacing: 0.5px; }
343
- .act-msg { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
344
-
345
- .act-empty { color: var(--dim); font-size: 9px; padding: 16px 0; letter-spacing: 1px; }
346
-
347
- /* ── Health pane ─────────────────────────────────────────────────── */
348
- #health-pane { padding: 20px; }
349
-
350
- .health-grid {
351
- display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
352
- gap: 10px; margin-bottom: 16px;
353
- }
354
-
355
- .health-cell {
356
- background: var(--bg-panel);
357
- border: 1px solid var(--border); border-radius: 3px;
358
- padding: 12px 14px;
359
- }
360
- .hc-label { font-size: 8px; letter-spacing: 2px; color: var(--dim); margin-bottom: 4px; }
361
- .hc-value { font-size: 18px; color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
362
- .hc-sub { font-size: 9px; color: var(--muted); margin-top: 2px; }
363
- .hc-value.green { color: var(--green); }
364
- .hc-value.amber { color: var(--amber); }
365
- .hc-value.red { color: var(--red); }
366
-
367
- /* ── Communication edges (SVG in chart) ─────────────────────────── */
368
- .oc-edge { stroke: var(--teal); stroke-opacity: 0.25; stroke-width: 1; fill: none; }
369
- .oc-edge.command { stroke: var(--teal); stroke-opacity: 0.4; }
370
- .oc-edge.report { stroke: var(--indigo); stroke-opacity: 0.35; stroke-dasharray: 4 3; }
371
- .oc-edge.feedback { stroke: var(--amber); stroke-opacity: 0.25; stroke-dasharray: 2 4; }
372
- .oc-edge.handoff { stroke: var(--green); stroke-opacity: 0.3; }
373
-
374
- .oc-node-bg { fill: oklch(8% 0.01 186); stroke-width: 1.5; }
375
- .oc-node-bg.boss { stroke: var(--teal); }
376
- .oc-node-bg.peer { stroke: oklch(68% 0.18 252); }
377
- .oc-node-text {
378
- fill: var(--text); font-size: 10px;
379
- font-family: 'Azeret Mono', 'Space Mono', monospace;
380
- text-anchor: middle; dominant-baseline: middle;
381
- pointer-events: none;
382
- }
383
- .oc-node-sub {
384
- fill: var(--muted); font-size: 8px;
385
- font-family: 'Azeret Mono', 'Space Mono', monospace;
386
- text-anchor: middle; dominant-baseline: middle;
387
- pointer-events: none;
388
- }
389
- .oc-node-pulse { fill: none; stroke-width: 1; opacity: 0; }
390
- @media (prefers-reduced-motion: no-preference) {
391
- .oc-node-pulse.running {
392
- animation: nodepulse 2.5s ease-in-out infinite;
393
- stroke: var(--green); opacity: 0.35;
394
- }
395
- }
396
- @keyframes nodepulse { 0%,100%{r:26;opacity:0.2} 50%{r:30;opacity:0.55} }
397
-
398
- /* Arrow markers are inline in the SVG */
399
-
400
- /* ── Copy button on org items ───────────────────────────────────── */
401
- .oi-copy-btn {
402
- display: none;
403
- flex-shrink: 0;
404
- background: none;
405
- border: 1px solid var(--border);
406
- border-radius: 2px;
407
- color: var(--dim);
408
- font-family: var(--mono);
409
- font-size: 7px;
410
- letter-spacing: 0.5px;
411
- padding: 1px 5px;
412
- cursor: pointer;
413
- transition: color 0.12s, border-color 0.12s, background 0.12s;
414
- line-height: 1.4;
415
- white-space: nowrap;
416
- }
417
- .org-item:hover .oi-copy-btn { display: inline-block; }
418
- .oi-copy-btn:hover {
419
- color: var(--teal);
420
- border-color: var(--teal-dim);
421
- background: var(--teal-glow);
422
- }
423
-
424
- /* ── Copy modal ─────────────────────────────────────────────────── */
425
- #copy-modal-overlay {
426
- display: none;
427
- position: fixed; inset: 0;
428
- background: oklch(5% 0.008 186 / 0.82);
429
- backdrop-filter: blur(2px);
430
- z-index: 200;
431
- align-items: center; justify-content: center;
432
- }
433
- #copy-modal-overlay.open { display: flex; }
434
-
435
- #copy-modal {
436
- background: var(--bg-panel);
437
- border: 1px solid var(--border-hi);
438
- border-radius: 4px;
439
- padding: 22px 24px 18px;
440
- width: 440px;
441
- max-width: calc(100vw - 32px);
442
- box-shadow: 0 8px 40px oklch(0% 0 0 / 0.5);
443
- }
444
-
445
- #copy-modal-title {
446
- font-size: 9px; letter-spacing: 3px; color: var(--teal);
447
- margin-bottom: 4px;
448
- }
449
- #copy-modal-org {
450
- font-size: 14px; font-weight: 600; color: var(--text);
451
- letter-spacing: 1px; margin-bottom: 16px;
452
- }
453
-
454
- .copy-modal-label {
455
- font-size: 8px; letter-spacing: 2px; color: var(--dim);
456
- margin-bottom: 6px;
457
- }
458
-
459
- #copy-dest-input {
460
- width: 100%;
461
- background: oklch(6% 0.008 186);
462
- border: 1px solid var(--border-hi);
463
- border-radius: 3px;
464
- color: var(--text);
465
- font-family: var(--mono);
466
- font-size: 12px;
467
- padding: 8px 10px;
468
- outline: none;
469
- transition: border-color 0.12s;
470
- margin-bottom: 6px;
471
- }
472
- #copy-dest-input:focus { border-color: var(--teal); }
473
- #copy-dest-input::placeholder { color: var(--dim); }
474
-
475
- #copy-modal-hint {
476
- font-size: 9px; color: var(--dim); margin-bottom: 16px; line-height: 1.5;
477
- }
478
-
479
- #copy-modal-status {
480
- font-size: 9px; min-height: 16px; margin-bottom: 12px;
481
- letter-spacing: 0.5px;
482
- }
483
- #copy-modal-status.ok { color: var(--green); }
484
- #copy-modal-status.err { color: var(--red); }
485
-
486
- .copy-modal-actions {
487
- display: flex; gap: 8px; justify-content: flex-end;
488
- }
489
-
490
- .cm-btn {
491
- background: none;
492
- border: 1px solid var(--border-hi);
493
- border-radius: 3px;
494
- color: var(--muted);
495
- font-family: var(--mono);
496
- font-size: 9px; letter-spacing: 1px;
497
- padding: 5px 14px;
498
- cursor: pointer;
499
- text-transform: uppercase;
500
- transition: color 0.12s, border-color 0.12s, background 0.12s;
501
- }
502
- .cm-btn:hover { color: var(--text); border-color: var(--muted); }
503
- .cm-btn.primary {
504
- color: var(--teal); border-color: var(--teal-dim);
505
- }
506
- .cm-btn.primary:hover { background: var(--teal-glow); border-color: var(--teal); }
507
- .cm-btn:disabled { opacity: 0.4; cursor: not-allowed; }
508
- </style>
509
- </head>
510
- <body>
511
- <div id="app">
512
-
513
- <!-- ── Sidebar: org list ── -->
514
- <div id="sidebar">
515
- <div id="sb-header">
516
- <div id="sb-wordmark">
517
- <a id="sb-back" href="/">← CONTROL</a>
518
- <span id="sb-title">ORGS</span>
519
- </div>
520
- <div id="sb-status-row">
521
- <div class="live-dot" id="sb-live-dot"></div>
522
- <span id="sb-status-text">LOADING</span>
523
- <span id="sb-running-count"></span>
524
- </div>
525
- </div>
526
- <div id="sb-list">
527
- <div id="sb-empty" style="display:none">
528
- <span class="empty-icon">⬡</span>
529
- NO ORGS FOUND<br><br>
530
- Create a named agent team<br>that coordinates across sessions.
531
- <span class="empty-cmd">/mastermind:createorg</span>
532
- </div>
533
- </div>
534
- </div>
535
-
536
- <!-- ── Main content ── -->
537
- <div id="main">
538
-
539
- <!-- No-selection splash -->
540
- <div id="no-org-state">
541
- <div class="ns-icon">⬡</div>
542
- <div class="ns-text">SELECT AN ORG</div>
543
- </div>
544
-
545
- <!-- Org detail (hidden until org selected) -->
546
- <div id="org-detail" style="display:none; height:100%; flex-direction:column; overflow:hidden;">
547
-
548
- <div id="main-header">
549
- <div>
550
- <div id="org-label">MASTERMIND ORG</div>
551
- <div style="display:flex; align-items:center; gap:8px; margin-top:2px;">
552
- <div id="org-name">—</div>
553
- <span id="org-status-badge" class="idle">IDLE</span>
554
- </div>
555
- </div>
556
- <div style="margin-left:auto; display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end;">
557
- <div class="hdr-chip">ROLES <span id="hdr-roles">0</span></div>
558
- <div class="hdr-chip">TOPOLOGY <span id="hdr-topology">—</span></div>
559
- <div class="hdr-chip" id="hdr-created-wrap" style="display:none">SINCE <span id="hdr-created">—</span></div>
560
- </div>
561
- <button id="stop-btn" onclick="stopCurrentOrg()" style="display:none;
562
- font-size:9px; font-family:var(--mono); letter-spacing:1px; color:var(--red);
563
- border:1px solid oklch(62% 0.22 25 / 0.35); background:none; padding:4px 12px;
564
- border-radius:3px; cursor:pointer; text-transform:uppercase; transition:background 0.12s;"
565
- onmouseover="this.style.background='oklch(62% 0.22 25 / 0.08)'"
566
- onmouseout="this.style.background='none'">STOP</button>
567
- </div>
568
-
569
- <div id="main-tabs">
570
- <button class="tab-btn active" onclick="switchTab('chart')">ORG CHART</button>
571
- <button class="tab-btn" onclick="switchTab('roles')">ROLES</button>
572
- <button class="tab-btn" onclick="switchTab('activity')">ACTIVITY</button>
573
- <button class="tab-btn" onclick="switchTab('health')">HEALTH</button>
574
- </div>
575
-
576
- <div id="main-body">
577
-
578
- <!-- Chart tab -->
579
- <div class="tab-pane active" id="tab-chart">
580
- <div id="chart-pane">
581
- <div id="org-meta-grid">
582
- <div class="meta-cell">
583
- <div class="meta-cell-label">GOAL</div>
584
- <div class="meta-cell-value" id="meta-goal" style="font-size:10px;line-height:1.5;white-space:normal;color:var(--muted)">—</div>
585
- </div>
586
- <div class="meta-cell">
587
- <div class="meta-cell-label">TOPOLOGY</div>
588
- <div class="meta-cell-value" id="meta-topology">—</div>
589
- </div>
590
- <div class="meta-cell">
591
- <div class="meta-cell-label">CREATED</div>
592
- <div class="meta-cell-value" id="meta-created" style="font-size:10px;color:var(--muted)">—</div>
593
- </div>
594
- </div>
595
- <div id="chart-svg-wrap">
596
- <svg id="org-chart-svg" viewBox="0 0 720 320">
597
- <defs>
598
- <marker id="arr-teal" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
599
- <path d="M0,0 L0,6 L6,3 z" fill="oklch(62% 0.20 186 / 0.5)"/>
600
- </marker>
601
- <marker id="arr-indigo" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
602
- <path d="M0,0 L0,6 L6,3 z" fill="oklch(68% 0.18 252 / 0.45)"/>
603
- </marker>
604
- <marker id="arr-amber" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
605
- <path d="M0,0 L0,6 L6,3 z" fill="oklch(78% 0.18 80 / 0.35)"/>
606
- </marker>
607
- <marker id="arr-green" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
608
- <path d="M0,0 L0,6 L6,3 z" fill="oklch(68% 0.20 150 / 0.4)"/>
609
- </marker>
610
- <filter id="node-glow" x="-50%" y="-50%" width="200%" height="200%">
611
- <feGaussianBlur in="SourceGraphic" stdDeviation="3" result="b"/>
612
- <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
613
- </filter>
614
- </defs>
615
- <rect width="720" height="320" fill="oklch(7% 0.008 186)"/>
616
- <g id="oc-edges"></g>
617
- <g id="oc-nodes"></g>
618
- </svg>
619
- </div>
620
-
621
- <!-- Legend -->
622
- <div style="display:flex; gap:16px; flex-wrap:wrap; padding:0 2px;">
623
- <div style="display:flex; align-items:center; gap:5px; font-size:8px; color:var(--dim);">
624
- <svg width="24" height="8"><line x1="0" y1="4" x2="24" y2="4" stroke="oklch(62% 0.20 186)" stroke-opacity="0.5" stroke-width="1.5" marker-end="url(#arr-teal)"/></svg>
625
- COMMAND
626
- </div>
627
- <div style="display:flex; align-items:center; gap:5px; font-size:8px; color:var(--dim);">
628
- <svg width="24" height="8"><line x1="0" y1="4" x2="24" y2="4" stroke="oklch(68% 0.18 252)" stroke-opacity="0.45" stroke-width="1" stroke-dasharray="4 3" marker-end="url(#arr-indigo)"/></svg>
629
- REPORT
630
- </div>
631
- <div style="display:flex; align-items:center; gap:5px; font-size:8px; color:var(--dim);">
632
- <svg width="24" height="8"><line x1="0" y1="4" x2="24" y2="4" stroke="oklch(78% 0.18 80)" stroke-opacity="0.35" stroke-width="1" stroke-dasharray="2 4" marker-end="url(#arr-amber)"/></svg>
633
- FEEDBACK
634
- </div>
635
- <div style="display:flex; align-items:center; gap:5px; font-size:8px; color:var(--dim);">
636
- <svg width="24" height="8"><line x1="0" y1="4" x2="24" y2="4" stroke="oklch(68% 0.20 150)" stroke-opacity="0.4" stroke-width="1.5" marker-end="url(#arr-green)"/></svg>
637
- HANDOFF
638
- </div>
639
- </div>
640
- </div>
641
- </div>
642
-
643
- <!-- Roles tab -->
644
- <div class="tab-pane" id="tab-roles">
645
- <div id="roles-pane">
646
- <div id="roles-grid" class="roles-grid"></div>
647
- </div>
648
- </div>
649
-
650
- <!-- Activity tab -->
651
- <div class="tab-pane" id="tab-activity">
652
- <div id="activity-pane">
653
- <div id="activity-log"></div>
654
- </div>
655
- </div>
656
-
657
- <!-- Health tab -->
658
- <div class="tab-pane" id="tab-health">
659
- <div id="health-pane">
660
- <div id="health-grid" class="health-grid"></div>
661
- <div id="health-detail"></div>
662
- </div>
663
- </div>
664
-
665
- </div><!-- end main-body -->
666
- </div><!-- end org-detail -->
667
- </div><!-- end main -->
668
- </div><!-- end app -->
669
-
670
- <!-- ── Copy-to-project modal ── -->
671
- <div id="copy-modal-overlay" onclick="if(event.target===this)closeCopyModal()">
672
- <div id="copy-modal">
673
- <div id="copy-modal-title">COPY ORG TO PROJECT</div>
674
- <div id="copy-modal-org">—</div>
675
- <div class="copy-modal-label">DESTINATION PROJECT PATH</div>
676
- <input id="copy-dest-input" type="text" placeholder="/absolute/path/to/project"
677
- onkeydown="if(event.key==='Enter')executeCopy(); if(event.key==='Escape')closeCopyModal()">
678
- <div id="copy-modal-hint">
679
- The org config will be written to<br>
680
- <span style="color:var(--muted)">&lt;destination&gt;/.monomind/orgs/&lt;name&gt;.json</span>
681
- </div>
682
- <div id="copy-modal-status"></div>
683
- <div class="copy-modal-actions">
684
- <button class="cm-btn" onclick="closeCopyModal()">CANCEL</button>
685
- <button class="cm-btn primary" id="copy-confirm-btn" onclick="executeCopy()">COPY</button>
686
- </div>
687
- </div>
688
- </div>
689
-
690
- <script>
691
- 'use strict';
692
-
693
- // ── State ─────────────────────────────────────────────────────────
694
- let allOrgs = [];
695
- let selectedOrg = null;
696
- let orgDetailData = null;
697
- let currentTab = 'chart';
698
-
699
- // ── Utilities ──────────────────────────────────────────────────────
700
- function esc(s) {
701
- return String(s ?? '')
702
- .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
703
- .replace(/"/g,'&quot;').replace(/'/g,'&#39;');
704
- }
705
- function fmtDate(ts) {
706
- if (!ts) return '—';
707
- const d = new Date(typeof ts === 'number' ? ts : Date.parse(ts));
708
- if (isNaN(d)) return ts;
709
- return d.toLocaleDateString('en', {month:'short', day:'numeric', year:'numeric'});
710
- }
711
- function fmtTime(ts) {
712
- if (!ts) return '';
713
- const d = new Date(typeof ts === 'number' ? ts : Date.parse(ts));
714
- if (isNaN(d)) return '';
715
- return d.toLocaleTimeString('en', {hour12:false, hour:'2-digit', minute:'2-digit', second:'2-digit'});
716
- }
717
- function fmtEventType(t) {
718
- const m = {
719
- 'org:start':'START','org:stop':'STOP','org:complete':'COMPLETE',
720
- 'org:create':'CREATE','org:heartbeat':'HB','org:agent:online':'AGENT ON',
721
- 'org:comms':'COMMS','org:checkpoint':'CHECKPOINT',
722
- };
723
- return m[t] || (t || '').replace(/^org:/,'').toUpperCase();
724
- }
725
-
726
- // Role colors cycle
727
- const ROLE_COLORS = [
728
- 'oklch(62% 0.20 186)',
729
- 'oklch(68% 0.18 252)',
730
- 'oklch(68% 0.20 150)',
731
- 'oklch(78% 0.18 80)',
732
- 'oklch(62% 0.22 25)',
733
- 'oklch(74% 0.16 310)',
734
- ];
735
- function roleColor(i) { return ROLE_COLORS[i % ROLE_COLORS.length]; }
736
-
737
- // ── Tab switching ─────────────────────────────────────────────────
738
- window.switchTab = function(tab) {
739
- currentTab = tab;
740
- document.querySelectorAll('.tab-btn').forEach(b => {
741
- b.classList.toggle('active', b.textContent.toLowerCase().includes(tab) ||
742
- (tab === 'chart' && b.textContent === 'ORG CHART') ||
743
- (tab === 'roles' && b.textContent === 'ROLES') ||
744
- (tab === 'activity' && b.textContent === 'ACTIVITY') ||
745
- (tab === 'health' && b.textContent === 'HEALTH'));
746
- });
747
- document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active'));
748
- const pane = document.getElementById('tab-' + tab);
749
- if (pane) pane.classList.add('active');
750
- if (orgDetailData) renderTab(tab);
751
- };
752
-
753
- // ── Render all orgs in sidebar ────────────────────────────────────
754
- function renderSidebar() {
755
- const list = document.getElementById('sb-list');
756
- const empty = document.getElementById('sb-empty');
757
- const dot = document.getElementById('sb-live-dot');
758
- const statusText = document.getElementById('sb-status-text');
759
- const runCount = document.getElementById('sb-running-count');
760
-
761
- // Clear existing items
762
- list.querySelectorAll('.org-item').forEach(el => el.remove());
763
-
764
- const running = allOrgs.filter(o => o.running).length;
765
- if (running > 0) {
766
- dot.classList.add('on');
767
- statusText.textContent = 'LIVE';
768
- runCount.textContent = running + ' RUNNING';
769
- } else {
770
- dot.classList.remove('on');
771
- statusText.textContent = allOrgs.length ? 'IDLE' : 'EMPTY';
772
- runCount.textContent = '';
773
- }
774
-
775
- if (!allOrgs.length) {
776
- empty.style.display = 'block';
777
- return;
778
- }
779
- empty.style.display = 'none';
780
-
781
- allOrgs.forEach(org => {
782
- const item = document.createElement('div');
783
- item.className = 'org-item' +
784
- (org.running ? ' running' : '') +
785
- (selectedOrg === org.name ? ' active' : '');
786
- item.dataset.orgName = org.name;
787
-
788
- const rolesCount = Array.isArray(org.roles) ? org.roles.length : (org.roles || 0);
789
- const topo = org.topology || 'hierarchical';
790
- const goalText = (org.goal || '').slice(0, 55) + ((org.goal || '').length > 55 ? '…' : '');
791
-
792
- item.innerHTML = `
793
- <div class="oi-header">
794
- <div class="oi-dot ${org.running ? 'running' : ''}"></div>
795
- <div class="oi-name">${esc(org.name)}</div>
796
- <span class="oi-badge ${org.running ? 'running' : 'idle'}">${org.running ? 'LIVE' : 'IDLE'}</span>
797
- <button class="oi-copy-btn" title="Copy org to another project">COPY</button>
798
- </div>
799
- ${goalText ? `<div class="oi-goal">${esc(goalText)}</div>` : ''}
800
- <div class="oi-meta">
801
- <span class="oi-chip">${rolesCount} <span>roles</span></span>
802
- <span class="oi-chip">${esc(topo)}</span>
803
- </div>`;
804
-
805
- item.querySelector('.oi-copy-btn').addEventListener('click', e => {
806
- e.stopPropagation();
807
- openCopyModal(org.name);
808
- });
809
- item.addEventListener('click', () => selectOrg(org.name));
810
- list.appendChild(item);
811
- });
812
- }
813
-
814
- // ── Select and load an org ────────────────────────────────────────
815
- async function selectOrg(name) {
816
- selectedOrg = name;
817
-
818
- // Update sidebar active state
819
- document.querySelectorAll('.org-item').forEach(el => {
820
- el.classList.toggle('active', el.dataset.orgName === name);
821
- });
822
-
823
- // Show detail area
824
- document.getElementById('no-org-state').style.display = 'none';
825
- const detail = document.getElementById('org-detail');
826
- detail.style.display = 'flex';
827
-
828
- // Basic data from list
829
- const listOrg = allOrgs.find(o => o.name === name) || {};
830
- updateHeader(listOrg, null);
831
-
832
- // Fetch full data
833
- orgDetailData = null;
834
- try {
835
- const [mainR, actR, healthR] = await Promise.all([
836
- fetch(`/api/org/${encodeURIComponent(name)}`),
837
- fetch(`/api/org/${encodeURIComponent(name)}/activity`).catch(() => null),
838
- fetch(`/api/org/${encodeURIComponent(name)}/health`).catch(() => null),
839
- ]);
840
- if (!mainR.ok) throw new Error('not found');
841
- const raw = await mainR.json();
842
- // API returns {config, state, goals, routines, approvals, running, tasks} — flatten config to top level
843
- orgDetailData = raw.config ? { ...raw.config, ...raw, running: raw.running } : raw;
844
- orgDetailData._activity = actR && actR.ok ? await actR.json() : [];
845
- orgDetailData._health = healthR && healthR.ok ? await healthR.json() : null;
846
- } catch (e) {
847
- orgDetailData = { ...listOrg, roles: listOrg.roles || [], communication: [] };
848
- orgDetailData._activity = [];
849
- orgDetailData._health = null;
850
- }
851
-
852
- updateHeader(listOrg, orgDetailData);
853
- renderTab(currentTab);
854
- }
855
-
856
- function updateHeader(listOrg, data) {
857
- const name = listOrg.name || selectedOrg || '—';
858
- document.getElementById('org-name').textContent = name.toUpperCase();
859
-
860
- const running = listOrg.running;
861
- const badge = document.getElementById('org-status-badge');
862
- badge.textContent = running ? 'LIVE' : 'IDLE';
863
- badge.className = 'oi-badge ' + (running ? 'running' : 'idle');
864
-
865
- const rolesArr = data ? (data.roles || []) : [];
866
- const rolesCount = Array.isArray(rolesArr) ? rolesArr.length : (listOrg.roles || 0);
867
- document.getElementById('hdr-roles').textContent = rolesCount;
868
- document.getElementById('hdr-topology').textContent = (data?.topology || listOrg.topology || '—').toUpperCase();
869
-
870
- const created = data?.created_at || listOrg.created_at;
871
- if (created) {
872
- document.getElementById('hdr-created').textContent = fmtDate(created);
873
- document.getElementById('hdr-created-wrap').style.display = '';
874
- }
875
-
876
- document.getElementById('stop-btn').style.display = running ? '' : 'none';
877
- }
878
-
879
- // ── Tab renderers ─────────────────────────────────────────────────
880
- function renderTab(tab) {
881
- if (!orgDetailData) return;
882
- if (tab === 'chart') renderChart();
883
- else if (tab === 'roles') renderRoles();
884
- else if (tab === 'activity') renderActivity();
885
- else if (tab === 'health') renderHealth();
886
- }
887
-
888
- // ── CHART TAB ─────────────────────────────────────────────────────
889
- function renderChart() {
890
- const d = orgDetailData;
891
- const roles = Array.isArray(d.roles) ? d.roles : [];
892
- const comms = Array.isArray(d.communication) ? d.communication : [];
893
- const running = !!(allOrgs.find(o => o.name === selectedOrg) || {}).running;
894
-
895
- // Meta bar
896
- document.getElementById('meta-goal').textContent = d.goal || '—';
897
- document.getElementById('meta-topology').textContent = (d.topology || '—').toUpperCase();
898
- document.getElementById('meta-created').textContent = fmtDate(d.created_at);
899
-
900
- // Build SVG chart
901
- const svgEl = document.getElementById('org-chart-svg');
902
- const edgesG = document.getElementById('oc-edges');
903
- const nodesG = document.getElementById('oc-nodes');
904
- edgesG.innerHTML = '';
905
- nodesG.innerHTML = '';
906
-
907
- if (!roles.length) {
908
- // Empty chart placeholder
909
- const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
910
- text.setAttribute('x', '360');
911
- text.setAttribute('y', '160');
912
- text.setAttribute('text-anchor', 'middle');
913
- text.setAttribute('fill', 'oklch(36% 0.008 186)');
914
- text.setAttribute('font-size', '11');
915
- text.setAttribute('font-family', "'Azeret Mono','Space Mono',monospace");
916
- text.setAttribute('letter-spacing', '2');
917
- text.textContent = 'NO ROLES DEFINED';
918
- nodesG.appendChild(text);
919
- return;
920
- }
921
-
922
- // Layout: compute node positions
923
- const W = 720, H = 320;
924
- const topo = (d.topology || 'hierarchical').toLowerCase();
925
- const positions = computeLayout(roles, comms, topo, W, H);
926
-
927
- const NS = 'http://www.w3.org/2000/svg';
928
-
929
- // Draw edges first
930
- const markerMap = {
931
- command: 'arr-teal', report: 'arr-indigo', feedback: 'arr-amber', handoff: 'arr-green'
932
- };
933
- const strokeMap = {
934
- command: { color: 'oklch(62% 0.20 186)', opacity: 0.5, width: 1.5, dash: '' },
935
- report: { color: 'oklch(68% 0.18 252)', opacity: 0.4, width: 1, dash: '4 3' },
936
- feedback: { color: 'oklch(78% 0.18 80)', opacity: 0.3, width: 1, dash: '2 4' },
937
- handoff: { color: 'oklch(68% 0.20 150)', opacity: 0.45, width: 1.5, dash: '' },
938
- };
939
-
940
- // Build set of edge pairs to detect anti-parallel edges (A→B + B→A)
941
- const edgePairSet = new Set(comms.map(c => `${c.from}|${c.to}`));
942
-
943
- comms.forEach(edge => {
944
- const fromPos = positions[edge.from];
945
- const toPos = positions[edge.to];
946
- if (!fromPos || !toPos || edge.from === edge.to) return;
947
-
948
- const style = strokeMap[edge.type] || strokeMap.command;
949
- const marker = markerMap[edge.type] || 'arr-teal';
950
-
951
- // Nudge endpoints toward each edge center to avoid overlap with nodes
952
- const dx = toPos.x - fromPos.x, dy = toPos.y - fromPos.y;
953
- const len = Math.sqrt(dx*dx + dy*dy) || 1;
954
- const R = 24; // node radius to clear
955
-
956
- // Perpendicular offset for anti-parallel pairs so both edges remain visible
957
- let px = 0, py = 0;
958
- if (edgePairSet.has(`${edge.to}|${edge.from}`)) {
959
- const [canonFrom, canonTo] = edge.from < edge.to ? [edge.from, edge.to] : [edge.to, edge.from];
960
- const cp = positions[canonFrom], ct = positions[canonTo];
961
- const cdx = ct.x - cp.x, cdy = ct.y - cp.y;
962
- const clen = Math.sqrt(cdx*cdx + cdy*cdy) || 1;
963
- const sign = edge.from === canonFrom ? 1 : -1;
964
- px = sign * (-cdy / clen) * 6;
965
- py = sign * ( cdx / clen) * 6;
966
- }
967
-
968
- const x1 = fromPos.x + (dx/len)*R + px;
969
- const y1 = fromPos.y + (dy/len)*R + py;
970
- const x2 = toPos.x - (dx/len)*(R + 4) + px;
971
- const y2 = toPos.y - (dy/len)*(R + 4) + py;
972
-
973
- const line = document.createElementNS(NS, 'line');
974
- line.setAttribute('x1', x1.toFixed(1));
975
- line.setAttribute('y1', y1.toFixed(1));
976
- line.setAttribute('x2', x2.toFixed(1));
977
- line.setAttribute('y2', y2.toFixed(1));
978
- line.setAttribute('stroke', style.color);
979
- line.setAttribute('stroke-opacity', style.opacity);
980
- line.setAttribute('stroke-width', style.width);
981
- if (style.dash) line.setAttribute('stroke-dasharray', style.dash);
982
- line.setAttribute('marker-end', `url(#${marker})`);
983
- edgesG.appendChild(line);
984
- });
985
-
986
- // Draw nodes
987
- roles.forEach((role, i) => {
988
- const pos = positions[role.id];
989
- if (!pos) return;
990
- const isBoss = !role.reports_to || role.reports_to === role.id;
991
- const color = roleColor(i);
992
-
993
- const g = document.createElementNS(NS, 'g');
994
- g.setAttribute('transform', `translate(${pos.x.toFixed(1)},${pos.y.toFixed(1)})`);
995
-
996
- // Pulse ring (running state)
997
- if (running) {
998
- const pulse = document.createElementNS(NS, 'circle');
999
- pulse.setAttribute('r', '26');
1000
- pulse.setAttribute('class', 'oc-node-pulse running');
1001
- pulse.setAttribute('stroke', isBoss ? 'oklch(62% 0.20 186)' : 'oklch(68% 0.20 150)');
1002
- g.appendChild(pulse);
1003
- }
1004
-
1005
- // Node circle
1006
- const circle = document.createElementNS(NS, 'circle');
1007
- circle.setAttribute('r', '22');
1008
- circle.setAttribute('class', `oc-node-bg ${isBoss ? 'boss' : 'peer'}`);
1009
- circle.setAttribute('stroke', color);
1010
- circle.setAttribute('filter', 'url(#node-glow)');
1011
- g.appendChild(circle);
1012
-
1013
- // Role title — abbreviated to fit circle
1014
- const titleText = document.createElementNS(NS, 'text');
1015
- titleText.setAttribute('class', 'oc-node-text');
1016
- titleText.setAttribute('y', '-4');
1017
- const title = role.title || role.id;
1018
- const shortTitle = title.length > 10 ? title.slice(0, 9) + '…' : title;
1019
- titleText.textContent = shortTitle;
1020
- titleText.style.fill = color;
1021
- g.appendChild(titleText);
1022
-
1023
- // Agent type
1024
- if (role.agent_type) {
1025
- const subText = document.createElementNS(NS, 'text');
1026
- subText.setAttribute('class', 'oc-node-sub');
1027
- subText.setAttribute('y', '10');
1028
- const agent = role.agent_type.length > 12 ? role.agent_type.slice(0, 11) + '…' : role.agent_type;
1029
- subText.textContent = agent;
1030
- g.appendChild(subText);
1031
- }
1032
-
1033
- nodesG.appendChild(g);
1034
- });
1035
- }
1036
-
1037
- function computeLayout(roles, comms, topology, W, H) {
1038
- const positions = {};
1039
- const n = roles.length;
1040
- if (n === 0) return positions;
1041
-
1042
- const cx = W / 2, cy = H / 2;
1043
- const PAD = 60;
1044
-
1045
- if (topology === 'mesh') {
1046
- // All nodes in a circle
1047
- roles.forEach((role, i) => {
1048
- const angle = (2 * Math.PI * i / n) - Math.PI / 2;
1049
- const r = Math.min((W - PAD*2) / 2, (H - PAD*2) / 2) * 0.8;
1050
- positions[role.id] = {
1051
- x: cx + r * Math.cos(angle),
1052
- y: cy + r * Math.sin(angle),
1053
- };
1054
- });
1055
-
1056
- } else if (topology === 'star') {
1057
- // Boss in center, others around edge
1058
- const boss = roles.find(r => !r.reports_to) || roles[0];
1059
- const rest = roles.filter(r => r.id !== boss.id);
1060
- positions[boss.id] = { x: cx, y: cy };
1061
- const r2 = Math.min((W - PAD*2) / 2, (H - PAD*2) / 2) * 0.75;
1062
- rest.forEach((role, i) => {
1063
- const angle = (2 * Math.PI * i / rest.length) - Math.PI / 2;
1064
- positions[role.id] = {
1065
- x: cx + r2 * Math.cos(angle),
1066
- y: cy + r2 * Math.sin(angle),
1067
- };
1068
- });
1069
-
1070
- } else {
1071
- // Hierarchical: layer by reports_to depth
1072
- const depthMap = {};
1073
- const childMap = {};
1074
- roles.forEach(r => {
1075
- if (!r.reports_to || r.reports_to === r.id) { depthMap[r.id] = 0; }
1076
- const parent = r.reports_to;
1077
- if (parent && parent !== r.id) {
1078
- if (!childMap[parent]) childMap[parent] = [];
1079
- childMap[parent].push(r.id);
1080
- }
1081
- });
1082
- // BFS to assign depths
1083
- let changed = true, iter = 0;
1084
- while (changed && iter < 20) {
1085
- changed = false; iter++;
1086
- roles.forEach(r => {
1087
- if (r.reports_to && r.reports_to !== r.id && depthMap[r.reports_to] !== undefined) {
1088
- const newDepth = (depthMap[r.reports_to] || 0) + 1;
1089
- if (depthMap[r.id] !== newDepth) { depthMap[r.id] = newDepth; changed = true; }
1090
- }
1091
- });
1092
- }
1093
- // Assign depth 0 to any unresolved
1094
- roles.forEach(r => { if (depthMap[r.id] === undefined) depthMap[r.id] = 1; });
1095
-
1096
- const maxDepth = Math.max(...Object.values(depthMap));
1097
- const layerMap = {};
1098
- Object.entries(depthMap).forEach(([id, depth]) => {
1099
- if (!layerMap[depth]) layerMap[depth] = [];
1100
- layerMap[depth].push(id);
1101
- });
1102
-
1103
- const rowH = maxDepth > 0 ? (H - PAD * 2) / (maxDepth + 1) : H - PAD * 2;
1104
- Object.entries(layerMap).forEach(([depth, ids]) => {
1105
- const y = PAD + Number(depth) * rowH + (maxDepth > 0 ? rowH / 2 : H / 2);
1106
- ids.forEach((id, i) => {
1107
- const colW = (W - PAD * 2) / ids.length;
1108
- const x = PAD + colW * i + colW / 2;
1109
- positions[id] = { x, y };
1110
- });
1111
- });
1112
- }
1113
-
1114
- return positions;
1115
- }
1116
-
1117
- // ── ROLES TAB ─────────────────────────────────────────────────────
1118
- function renderRoles() {
1119
- const d = orgDetailData;
1120
- const roles = Array.isArray(d.roles) ? d.roles : [];
1121
- const grid = document.getElementById('roles-grid');
1122
- grid.innerHTML = '';
1123
-
1124
- if (!roles.length) {
1125
- grid.innerHTML = '<div style="color:var(--dim);font-size:9px;letter-spacing:1px;">NO ROLES DEFINED</div>';
1126
- return;
1127
- }
1128
-
1129
- roles.forEach((role, i) => {
1130
- const color = roleColor(i);
1131
- const isBoss = !role.reports_to || role.reports_to === role.id;
1132
- const resps = Array.isArray(role.responsibilities) ? role.responsibilities : [];
1133
-
1134
- const block = document.createElement('div');
1135
- block.className = 'role-block';
1136
-
1137
- block.innerHTML = `
1138
- <div class="rb-header">
1139
- <div class="rb-dot" style="background:${color}; box-shadow:0 0 4px ${color.replace(')', ' / 0.31)')};"></div>
1140
- <div class="rb-title">${esc(role.title || role.id)}</div>
1141
- ${role.agent_type ? `<span class="rb-agent">${esc(role.agent_type)}</span>` : ''}
1142
- </div>
1143
- <div class="rb-id">id: <span style="color:var(--muted)">${esc(role.id)}</span>${isBoss ? ' <span style="color:var(--teal);font-size:8px;margin-left:4px">BOSS</span>' : ''}</div>
1144
- ${role.reports_to && !isBoss ? `<div class="rb-reports">reports to: <span>${esc(role.reports_to)}</span></div>` : ''}
1145
- ${resps.length ? `<div class="rb-resps">${resps.map(r => `<div class="rb-resp-item">${esc(r)}</div>`).join('')}</div>` : ''}
1146
- `;
1147
-
1148
- grid.appendChild(block);
1149
- });
1150
- }
1151
-
1152
- // ── ACTIVITY TAB ─────────────────────────────────────────────────
1153
- function renderActivity() {
1154
- const log = document.getElementById('activity-log');
1155
- const activity = orgDetailData._activity;
1156
-
1157
- // Also inject org event log from SSE-captured events
1158
- const orgEvents = orgEventLog[selectedOrg] || [];
1159
-
1160
- // Merge and sort
1161
- let events = [];
1162
- if (Array.isArray(activity)) events = [...activity];
1163
- events.push(...orgEvents);
1164
- events.sort((a, b) => (b.ts || 0) - (a.ts || 0));
1165
-
1166
- if (!events.length) {
1167
- log.innerHTML = '<div class="act-empty">NO ACTIVITY RECORDED</div>';
1168
- return;
1169
- }
1170
-
1171
- log.innerHTML = events.slice(0, 80).map(ev => {
1172
- const t = fmtTime(ev.ts);
1173
- const type = fmtEventType(ev.type);
1174
- const detail = ev.role ? ev.role : ev.msg ? ev.msg : ev.agent ? ev.agent : '';
1175
- return `<div class="act-row">
1176
- <span class="act-time">${esc(t)}</span>
1177
- <span class="act-type">${esc(type)}</span>
1178
- <span class="act-msg">${esc(detail)}</span>
1179
- </div>`;
1180
- }).join('');
1181
- }
1182
-
1183
- // ── HEALTH TAB ─────────────────────────────────────────────────────
1184
- function renderHealth() {
1185
- const health = orgDetailData._health;
1186
- const listOrg = allOrgs.find(o => o.name === selectedOrg) || {};
1187
- const roles = Array.isArray(orgDetailData.roles) ? orgDetailData.roles : [];
1188
- const running = listOrg.running;
1189
-
1190
- const grid = document.getElementById('health-grid');
1191
- const detail = document.getElementById('health-detail');
1192
-
1193
- grid.innerHTML = `
1194
- <div class="health-cell">
1195
- <div class="hc-label">STATUS</div>
1196
- <div class="hc-value ${running ? 'green' : ''}">${running ? 'LIVE' : 'IDLE'}</div>
1197
- </div>
1198
- <div class="health-cell">
1199
- <div class="hc-label">ROLES</div>
1200
- <div class="hc-value">${roles.length}</div>
1201
- </div>
1202
- <div class="health-cell">
1203
- <div class="hc-label">TOPOLOGY</div>
1204
- <div class="hc-value" style="font-size:13px">${esc((orgDetailData.topology || '—').toUpperCase())}</div>
1205
- </div>
1206
- ${health?.agents_active !== undefined ? `
1207
- <div class="health-cell">
1208
- <div class="hc-label">AGENTS</div>
1209
- <div class="hc-value ${health.agents_active > 0 ? 'green' : ''}">${health.agents_active}</div>
1210
- <div class="hc-sub">active</div>
1211
- </div>` : ''}
1212
- ${health?.tasks_pending !== undefined ? `
1213
- <div class="health-cell">
1214
- <div class="hc-label">TASKS</div>
1215
- <div class="hc-value ${health.tasks_pending > 5 ? 'amber' : ''}">${health.tasks_pending}</div>
1216
- <div class="hc-sub">pending</div>
1217
- </div>` : ''}
1218
- `;
1219
-
1220
- if (health?.errors && health.errors.length) {
1221
- detail.innerHTML = `
1222
- <div style="margin-top:16px">
1223
- <div style="font-size:8px;letter-spacing:2px;color:var(--dim);margin-bottom:8px">RECENT ERRORS</div>
1224
- ${health.errors.slice(0,5).map(e => `
1225
- <div style="font-size:9px;color:var(--red);padding:5px 0;border-bottom:1px solid oklch(62% 0.22 25 / 0.1)">
1226
- ${esc(e)}
1227
- </div>`).join('')}
1228
- </div>`;
1229
- } else {
1230
- detail.innerHTML = health ? `
1231
- <div style="margin-top:16px;font-size:9px;color:var(--dim);letter-spacing:1px;">
1232
- ${running ? 'ORG IS RUNNING — NO ERRORS DETECTED' : 'ORG IS IDLE'}
1233
- </div>` : `
1234
- <div style="margin-top:16px;font-size:9px;color:var(--dim);letter-spacing:1px;">
1235
- HEALTH DATA UNAVAILABLE
1236
- </div>`;
1237
- }
1238
- }
1239
-
1240
- // ── Stop org action ────────────────────────────────────────────────
1241
- window.stopCurrentOrg = async function() {
1242
- if (!selectedOrg) return;
1243
- try {
1244
- await fetch(`/api/orgs/${encodeURIComponent(selectedOrg)}/stop`, { method: 'POST' });
1245
- setTimeout(loadOrgs, 600);
1246
- } catch (_) {}
1247
- };
1248
-
1249
- // ── SSE org events ────────────────────────────────────────────────
1250
- const orgEventLog = {};
1251
-
1252
- let evtSource = null;
1253
- function connectSSE() {
1254
- if (evtSource) evtSource.close();
1255
- evtSource = new EventSource('/api/events');
1256
- evtSource.onmessage = e => {
1257
- try {
1258
- const ev = JSON.parse(e.data);
1259
- if (ev && typeof ev.org === 'string' && ev.type && ev.type.startsWith('org:')) {
1260
- const name = ev.org;
1261
- if (!orgEventLog[name]) orgEventLog[name] = [];
1262
- orgEventLog[name].push(ev);
1263
- if (orgEventLog[name].length > 50) orgEventLog[name].shift();
1264
-
1265
- // Refresh if this org is selected
1266
- if (name === selectedOrg && currentTab === 'activity') {
1267
- renderActivity();
1268
- }
1269
- // Reload org list if start/stop event
1270
- if (ev.type === 'org:start' || ev.type === 'org:stop') {
1271
- setTimeout(loadOrgs, 400);
1272
- }
1273
- }
1274
- } catch (_) {}
1275
- };
1276
- evtSource.onerror = () => {
1277
- setTimeout(connectSSE, 5000);
1278
- };
1279
- }
1280
-
1281
- // ── Data loading ──────────────────────────────────────────────────
1282
- async function loadOrgs() {
1283
- try {
1284
- const r = await fetch('/api/orgs');
1285
- allOrgs = await r.json();
1286
- if (!Array.isArray(allOrgs)) allOrgs = [];
1287
- } catch (_) {
1288
- allOrgs = [];
1289
- }
1290
- renderSidebar();
1291
-
1292
- // Re-render detail if selected org changed running state
1293
- if (selectedOrg && orgDetailData) {
1294
- const listOrg = allOrgs.find(o => o.name === selectedOrg);
1295
- if (listOrg) updateHeader(listOrg, orgDetailData);
1296
- }
1297
- }
1298
-
1299
- // ── Copy org modal ────────────────────────────────────────────────
1300
- let copyTargetOrg = null;
1301
-
1302
- window.openCopyModal = function(name) {
1303
- copyTargetOrg = name;
1304
- document.getElementById('copy-modal-org').textContent = name.toUpperCase();
1305
- document.getElementById('copy-dest-input').value = '';
1306
- document.getElementById('copy-modal-status').textContent = '';
1307
- document.getElementById('copy-modal-status').className = '';
1308
- document.getElementById('copy-confirm-btn').disabled = false;
1309
- document.getElementById('copy-modal-overlay').classList.add('open');
1310
- setTimeout(() => document.getElementById('copy-dest-input').focus(), 60);
1311
- };
1312
-
1313
- window.closeCopyModal = function() {
1314
- document.getElementById('copy-modal-overlay').classList.remove('open');
1315
- copyTargetOrg = null;
1316
- };
1317
-
1318
- window.executeCopy = async function() {
1319
- if (!copyTargetOrg) return;
1320
- const destination = document.getElementById('copy-dest-input').value.trim();
1321
- const status = document.getElementById('copy-modal-status');
1322
- const btn = document.getElementById('copy-confirm-btn');
1323
- if (!destination) {
1324
- status.textContent = 'ENTER A DESTINATION PATH';
1325
- status.className = 'err';
1326
- return;
1327
- }
1328
- btn.disabled = true;
1329
- status.textContent = 'COPYING…';
1330
- status.className = '';
1331
- try {
1332
- const r = await fetch(`/api/orgs/${encodeURIComponent(copyTargetOrg)}/copy`, {
1333
- method: 'POST',
1334
- headers: { 'Content-Type': 'application/json' },
1335
- body: JSON.stringify({ destination }),
1336
- });
1337
- const data = await r.json();
1338
- if (r.ok && data.ok) {
1339
- status.textContent = 'COPIED — ' + (data.destFile || destination);
1340
- status.className = 'ok';
1341
- setTimeout(closeCopyModal, 2000);
1342
- } else {
1343
- status.textContent = 'ERROR: ' + (data.error || r.statusText);
1344
- status.className = 'err';
1345
- btn.disabled = false;
1346
- }
1347
- } catch(e) {
1348
- status.textContent = 'ERROR: ' + e.message;
1349
- status.className = 'err';
1350
- btn.disabled = false;
1351
- }
1352
- };
1353
-
1354
- // ── Bootstrap ─────────────────────────────────────────────────────
1355
- connectSSE();
1356
- loadOrgs();
1357
- setInterval(loadOrgs, 8000);
1358
- </script>
1359
- </body>
1360
- </html>