myaiforone 1.0.0

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 (315) hide show
  1. package/README.md +113 -0
  2. package/agents/_template/CLAUDE.md +18 -0
  3. package/agents/_template/agent.json +7 -0
  4. package/agents/platform/agentcreator/CLAUDE.md +300 -0
  5. package/agents/platform/appcreator/CLAUDE.md +158 -0
  6. package/agents/platform/gym/CLAUDE.md +486 -0
  7. package/agents/platform/gym/agent.json +40 -0
  8. package/agents/platform/gym/programs/agent-building/program.json +160 -0
  9. package/agents/platform/gym/programs/automations-mastery/program.json +129 -0
  10. package/agents/platform/gym/programs/getting-started/program.json +124 -0
  11. package/agents/platform/gym/programs/mcp-integrations/program.json +116 -0
  12. package/agents/platform/gym/programs/multi-model-strategy/program.json +115 -0
  13. package/agents/platform/gym/programs/prompt-engineering/program.json +136 -0
  14. package/agents/platform/gym/souls/alex.md +12 -0
  15. package/agents/platform/gym/souls/jordan.md +12 -0
  16. package/agents/platform/gym/souls/morgan.md +12 -0
  17. package/agents/platform/gym/souls/riley.md +12 -0
  18. package/agents/platform/gym/souls/sam.md +12 -0
  19. package/agents/platform/hub/CLAUDE.md +372 -0
  20. package/agents/platform/promptcreator/CLAUDE.md +130 -0
  21. package/agents/platform/skillcreator/CLAUDE.md +163 -0
  22. package/bin/cli.js +566 -0
  23. package/config.example.json +310 -0
  24. package/dist/agent-registry.d.ts +32 -0
  25. package/dist/agent-registry.d.ts.map +1 -0
  26. package/dist/agent-registry.js +144 -0
  27. package/dist/agent-registry.js.map +1 -0
  28. package/dist/channels/discord.d.ts +17 -0
  29. package/dist/channels/discord.d.ts.map +1 -0
  30. package/dist/channels/discord.js +114 -0
  31. package/dist/channels/discord.js.map +1 -0
  32. package/dist/channels/imessage.d.ts +23 -0
  33. package/dist/channels/imessage.d.ts.map +1 -0
  34. package/dist/channels/imessage.js +214 -0
  35. package/dist/channels/imessage.js.map +1 -0
  36. package/dist/channels/slack.d.ts +19 -0
  37. package/dist/channels/slack.d.ts.map +1 -0
  38. package/dist/channels/slack.js +167 -0
  39. package/dist/channels/slack.js.map +1 -0
  40. package/dist/channels/telegram.d.ts +19 -0
  41. package/dist/channels/telegram.d.ts.map +1 -0
  42. package/dist/channels/telegram.js +274 -0
  43. package/dist/channels/telegram.js.map +1 -0
  44. package/dist/channels/types.d.ts +44 -0
  45. package/dist/channels/types.d.ts.map +1 -0
  46. package/dist/channels/types.js +18 -0
  47. package/dist/channels/types.js.map +1 -0
  48. package/dist/channels/whatsapp.d.ts +23 -0
  49. package/dist/channels/whatsapp.d.ts.map +1 -0
  50. package/dist/channels/whatsapp.js +189 -0
  51. package/dist/channels/whatsapp.js.map +1 -0
  52. package/dist/config.d.ts +134 -0
  53. package/dist/config.d.ts.map +1 -0
  54. package/dist/config.js +127 -0
  55. package/dist/config.js.map +1 -0
  56. package/dist/cron.d.ts +8 -0
  57. package/dist/cron.d.ts.map +1 -0
  58. package/dist/cron.js +35 -0
  59. package/dist/cron.js.map +1 -0
  60. package/dist/decrypt-keys.d.ts +7 -0
  61. package/dist/decrypt-keys.d.ts.map +1 -0
  62. package/dist/decrypt-keys.js +53 -0
  63. package/dist/decrypt-keys.js.map +1 -0
  64. package/dist/encrypt-keys.d.ts +8 -0
  65. package/dist/encrypt-keys.d.ts.map +1 -0
  66. package/dist/encrypt-keys.js +62 -0
  67. package/dist/encrypt-keys.js.map +1 -0
  68. package/dist/executor.d.ts +31 -0
  69. package/dist/executor.d.ts.map +1 -0
  70. package/dist/executor.js +2009 -0
  71. package/dist/executor.js.map +1 -0
  72. package/dist/gemini-executor.d.ts +27 -0
  73. package/dist/gemini-executor.d.ts.map +1 -0
  74. package/dist/gemini-executor.js +160 -0
  75. package/dist/gemini-executor.js.map +1 -0
  76. package/dist/goals.d.ts +24 -0
  77. package/dist/goals.d.ts.map +1 -0
  78. package/dist/goals.js +189 -0
  79. package/dist/goals.js.map +1 -0
  80. package/dist/gym/activity-digest.d.ts +30 -0
  81. package/dist/gym/activity-digest.d.ts.map +1 -0
  82. package/dist/gym/activity-digest.js +506 -0
  83. package/dist/gym/activity-digest.js.map +1 -0
  84. package/dist/gym/dimension-scorer.d.ts +76 -0
  85. package/dist/gym/dimension-scorer.d.ts.map +1 -0
  86. package/dist/gym/dimension-scorer.js +236 -0
  87. package/dist/gym/dimension-scorer.js.map +1 -0
  88. package/dist/gym/gym-router.d.ts +7 -0
  89. package/dist/gym/gym-router.d.ts.map +1 -0
  90. package/dist/gym/gym-router.js +718 -0
  91. package/dist/gym/gym-router.js.map +1 -0
  92. package/dist/gym/index.d.ts +11 -0
  93. package/dist/gym/index.d.ts.map +1 -0
  94. package/dist/gym/index.js +11 -0
  95. package/dist/gym/index.js.map +1 -0
  96. package/dist/heartbeat.d.ts +21 -0
  97. package/dist/heartbeat.d.ts.map +1 -0
  98. package/dist/heartbeat.js +163 -0
  99. package/dist/heartbeat.js.map +1 -0
  100. package/dist/index.d.ts +2 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +254 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/keystore.d.ts +22 -0
  105. package/dist/keystore.d.ts.map +1 -0
  106. package/dist/keystore.js +178 -0
  107. package/dist/keystore.js.map +1 -0
  108. package/dist/logger.d.ts +9 -0
  109. package/dist/logger.d.ts.map +1 -0
  110. package/dist/logger.js +45 -0
  111. package/dist/logger.js.map +1 -0
  112. package/dist/memory/daily.d.ts +22 -0
  113. package/dist/memory/daily.d.ts.map +1 -0
  114. package/dist/memory/daily.js +82 -0
  115. package/dist/memory/daily.js.map +1 -0
  116. package/dist/memory/embeddings.d.ts +15 -0
  117. package/dist/memory/embeddings.d.ts.map +1 -0
  118. package/dist/memory/embeddings.js +154 -0
  119. package/dist/memory/embeddings.js.map +1 -0
  120. package/dist/memory/index.d.ts +32 -0
  121. package/dist/memory/index.d.ts.map +1 -0
  122. package/dist/memory/index.js +159 -0
  123. package/dist/memory/index.js.map +1 -0
  124. package/dist/memory/search.d.ts +21 -0
  125. package/dist/memory/search.d.ts.map +1 -0
  126. package/dist/memory/search.js +77 -0
  127. package/dist/memory/search.js.map +1 -0
  128. package/dist/memory/store.d.ts +23 -0
  129. package/dist/memory/store.d.ts.map +1 -0
  130. package/dist/memory/store.js +144 -0
  131. package/dist/memory/store.js.map +1 -0
  132. package/dist/ollama-executor.d.ts +17 -0
  133. package/dist/ollama-executor.d.ts.map +1 -0
  134. package/dist/ollama-executor.js +112 -0
  135. package/dist/ollama-executor.js.map +1 -0
  136. package/dist/openai-executor.d.ts +38 -0
  137. package/dist/openai-executor.d.ts.map +1 -0
  138. package/dist/openai-executor.js +197 -0
  139. package/dist/openai-executor.js.map +1 -0
  140. package/dist/router.d.ts +11 -0
  141. package/dist/router.d.ts.map +1 -0
  142. package/dist/router.js +185 -0
  143. package/dist/router.js.map +1 -0
  144. package/dist/test-message.d.ts +2 -0
  145. package/dist/test-message.d.ts.map +1 -0
  146. package/dist/test-message.js +60 -0
  147. package/dist/test-message.js.map +1 -0
  148. package/dist/utils/imsg-db-reader.d.ts +24 -0
  149. package/dist/utils/imsg-db-reader.d.ts.map +1 -0
  150. package/dist/utils/imsg-db-reader.js +92 -0
  151. package/dist/utils/imsg-db-reader.js.map +1 -0
  152. package/dist/utils/imsg-rpc.d.ts +25 -0
  153. package/dist/utils/imsg-rpc.d.ts.map +1 -0
  154. package/dist/utils/imsg-rpc.js +149 -0
  155. package/dist/utils/imsg-rpc.js.map +1 -0
  156. package/dist/utils/message-formatter.d.ts +3 -0
  157. package/dist/utils/message-formatter.d.ts.map +1 -0
  158. package/dist/utils/message-formatter.js +69 -0
  159. package/dist/utils/message-formatter.js.map +1 -0
  160. package/dist/web-ui.d.ts +12 -0
  161. package/dist/web-ui.d.ts.map +1 -0
  162. package/dist/web-ui.js +5784 -0
  163. package/dist/web-ui.js.map +1 -0
  164. package/dist/whatsapp-chats.d.ts +2 -0
  165. package/dist/whatsapp-chats.d.ts.map +1 -0
  166. package/dist/whatsapp-chats.js +76 -0
  167. package/dist/whatsapp-chats.js.map +1 -0
  168. package/dist/whatsapp-login.d.ts +2 -0
  169. package/dist/whatsapp-login.d.ts.map +1 -0
  170. package/dist/whatsapp-login.js +90 -0
  171. package/dist/whatsapp-login.js.map +1 -0
  172. package/dist/wiki-sync.d.ts +21 -0
  173. package/dist/wiki-sync.d.ts.map +1 -0
  174. package/dist/wiki-sync.js +147 -0
  175. package/dist/wiki-sync.js.map +1 -0
  176. package/docs/AddNewAgentGuide.md +100 -0
  177. package/docs/AddNewMcpGuide.md +72 -0
  178. package/docs/Architecture.md +795 -0
  179. package/docs/CLAUDE-AI-SETUP.md +166 -0
  180. package/docs/Setup.md +297 -0
  181. package/docs/ai-gym-architecture.md +1040 -0
  182. package/docs/ai-gym-build-plan.md +343 -0
  183. package/docs/ai-gym-onboarding.md +122 -0
  184. package/docs/appcreator_plan.md +348 -0
  185. package/docs/platform-mcp-audit.md +320 -0
  186. package/docs/server-deployment-plan.md +503 -0
  187. package/docs/superpowers/plans/2026-03-25-marketplace.md +1281 -0
  188. package/docs/superpowers/specs/2026-03-25-marketplace-design.md +287 -0
  189. package/docs/user-guide.md +2016 -0
  190. package/mcp-catalog.json +628 -0
  191. package/package.json +63 -0
  192. package/public/MyAIforOne-logomark-512.svg +16 -0
  193. package/public/MyAIforOne-logomark-transparent.svg +15 -0
  194. package/public/activity.html +314 -0
  195. package/public/admin.html +1674 -0
  196. package/public/agent-dashboard.html +670 -0
  197. package/public/api-docs.html +1106 -0
  198. package/public/automations.html +722 -0
  199. package/public/canvas.css +223 -0
  200. package/public/canvas.js +588 -0
  201. package/public/changelog.html +231 -0
  202. package/public/gym.html +2766 -0
  203. package/public/home.html +1930 -0
  204. package/public/index.html +2809 -0
  205. package/public/lab.html +1643 -0
  206. package/public/library.html +1442 -0
  207. package/public/marketplace.html +1101 -0
  208. package/public/mcp-docs.html +441 -0
  209. package/public/mini.html +390 -0
  210. package/public/monitor.html +584 -0
  211. package/public/org.html +4304 -0
  212. package/public/projects.html +734 -0
  213. package/public/settings.html +645 -0
  214. package/public/tasks.html +932 -0
  215. package/public/trainers/alex.svg +12 -0
  216. package/public/trainers/jordan.svg +12 -0
  217. package/public/trainers/morgan.svg +12 -0
  218. package/public/trainers/riley.svg +12 -0
  219. package/public/trainers/sam.svg +12 -0
  220. package/public/user-guide.html +218 -0
  221. package/registry/agents.json +3 -0
  222. package/registry/apps.json +20 -0
  223. package/registry/installed-drafts.json +3 -0
  224. package/registry/mcps.json +1084 -0
  225. package/registry/prompts/personal/mcp-test-prompt.md +6 -0
  226. package/registry/prompts/personal/memory-recall.md +6 -0
  227. package/registry/prompts/platform/brainstorm.md +15 -0
  228. package/registry/prompts/platform/code-review.md +16 -0
  229. package/registry/prompts/platform/explain.md +16 -0
  230. package/registry/prompts.json +58 -0
  231. package/registry/skills/external/brainstorming.md +5 -0
  232. package/registry/skills/external/code-review.md +40 -0
  233. package/registry/skills/external/frontend-patterns.md +642 -0
  234. package/registry/skills/external/frontend-slides.md +184 -0
  235. package/registry/skills/external/systematic-debugging.md +5 -0
  236. package/registry/skills/external/tdd.md +328 -0
  237. package/registry/skills/external/verification-before-completion.md +5 -0
  238. package/registry/skills/external/writing-plans.md +5 -0
  239. package/registry/skills/platform/ai41_app_build.md +930 -0
  240. package/registry/skills/platform/ai41_app_deploy.md +168 -0
  241. package/registry/skills/platform/ai41_app_orchestrator.md +239 -0
  242. package/registry/skills/platform/ai41_app_patterns.md +359 -0
  243. package/registry/skills/platform/ai41_app_register.md +85 -0
  244. package/registry/skills/platform/ai41_app_scaffold.md +421 -0
  245. package/registry/skills/platform/ai41_app_verify.md +107 -0
  246. package/registry/skills/platform/opProjectCreate.md +239 -0
  247. package/registry/skills/platform/op_devbrowser.md +136 -0
  248. package/registry/skills/platform/sop_brandguidelines.md +103 -0
  249. package/registry/skills/platform/sop_docx.md +117 -0
  250. package/registry/skills/platform/sop_frontenddesign.md +44 -0
  251. package/registry/skills/platform/sop_frontenddesign_v2.md +659 -0
  252. package/registry/skills/platform/sop_mcpbuilder.md +133 -0
  253. package/registry/skills/platform/sop_pdf.md +172 -0
  254. package/registry/skills/platform/sop_pptx.md +133 -0
  255. package/registry/skills/platform/sop_skillcreator.md +104 -0
  256. package/registry/skills/platform/sop_themefactory.md +128 -0
  257. package/registry/skills/platform/sop_webapptesting.md +75 -0
  258. package/registry/skills/platform/sop_webartifactsbuilder.md +97 -0
  259. package/registry/skills/platform/sop_xlsx.md +134 -0
  260. package/registry/skills.json +1055 -0
  261. package/scripts/discover-chats.sh +11 -0
  262. package/scripts/install-service-windows.ps1 +87 -0
  263. package/scripts/install-service.sh +52 -0
  264. package/scripts/seed-registry.ts +195 -0
  265. package/scripts/test-send.sh +5 -0
  266. package/scripts/tray-indicator.ps1 +35 -0
  267. package/scripts/uninstall-service-windows.ps1 +23 -0
  268. package/scripts/uninstall-service.sh +15 -0
  269. package/scripts/xbar-myagent.5s.sh +32 -0
  270. package/server/mcp-server/dist/index.d.ts +11 -0
  271. package/server/mcp-server/dist/index.js +1332 -0
  272. package/server/mcp-server/dist/lib/api-client.d.ts +165 -0
  273. package/server/mcp-server/dist/lib/api-client.js +241 -0
  274. package/server/mcp-server/index.ts +1545 -0
  275. package/server/mcp-server/lib/api-client.ts +366 -0
  276. package/server/mcp-server/tsconfig.json +14 -0
  277. package/src/agent-registry.ts +180 -0
  278. package/src/channels/discord.ts +129 -0
  279. package/src/channels/imessage.ts +261 -0
  280. package/src/channels/slack.ts +208 -0
  281. package/src/channels/telegram.ts +307 -0
  282. package/src/channels/types.ts +62 -0
  283. package/src/channels/whatsapp.ts +227 -0
  284. package/src/config.ts +281 -0
  285. package/src/cron.ts +43 -0
  286. package/src/decrypt-keys.ts +60 -0
  287. package/src/encrypt-keys.ts +70 -0
  288. package/src/executor.ts +2190 -0
  289. package/src/gemini-executor.ts +212 -0
  290. package/src/goals.ts +240 -0
  291. package/src/gym/activity-digest.ts +546 -0
  292. package/src/gym/dimension-scorer.ts +297 -0
  293. package/src/gym/gym-router.ts +801 -0
  294. package/src/gym/index.ts +19 -0
  295. package/src/heartbeat.ts +220 -0
  296. package/src/index.ts +275 -0
  297. package/src/keystore.ts +190 -0
  298. package/src/logger.ts +51 -0
  299. package/src/memory/daily.ts +101 -0
  300. package/src/memory/embeddings.ts +185 -0
  301. package/src/memory/index.ts +218 -0
  302. package/src/memory/search.ts +124 -0
  303. package/src/memory/store.ts +189 -0
  304. package/src/ollama-executor.ts +126 -0
  305. package/src/openai-executor.ts +259 -0
  306. package/src/router.ts +230 -0
  307. package/src/test-message.ts +72 -0
  308. package/src/utils/imsg-db-reader.ts +109 -0
  309. package/src/utils/imsg-rpc.ts +178 -0
  310. package/src/utils/message-formatter.ts +90 -0
  311. package/src/web-ui.ts +5778 -0
  312. package/src/whatsapp-chats.ts +91 -0
  313. package/src/whatsapp-login.ts +110 -0
  314. package/src/wiki-sync.ts +199 -0
  315. package/tsconfig.json +19 -0
@@ -0,0 +1,390 @@
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>MyAIforOne · Mini</title>
7
+ <style>
8
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
9
+ :root{
10
+ --bg:#0a0f1e;--bg-card:rgba(16,22,40,0.98);--border:rgba(56,189,248,0.12);
11
+ --border-glow:rgba(56,189,248,0.3);--accent:#22d3ee;--accent-bg:rgba(6,182,212,0.15);
12
+ --text:rgba(255,255,255,0.92);--text-muted:rgba(148,163,184,0.6);
13
+ --green:#4ade80;--red:#f87171;--amber:#fbbf24;
14
+ --font:'DM Sans',system-ui,sans-serif;--font-mono:'IBM Plex Mono',monospace;
15
+ }
16
+ [data-theme="light"]{
17
+ --bg:#f0f4f8;--bg-card:rgba(255,255,255,0.98);--border:rgba(0,0,0,0.08);
18
+ --border-glow:rgba(14,116,144,0.3);--accent:#0891b2;--accent-bg:rgba(14,116,144,0.08);
19
+ --text:rgba(15,23,42,0.92);--text-muted:rgba(100,116,139,0.7);
20
+ }
21
+ html,body{width:100%;height:100%;background:var(--bg);font-family:var(--font);overflow:hidden}
22
+
23
+ .bar{
24
+ background:var(--bg-card);border:1px solid var(--border);border-radius:12px;
25
+ padding:10px 10px;display:flex;flex-direction:column;gap:7px;
26
+ height:calc(100vh - 12px);
27
+ box-shadow:0 8px 32px rgba(0,0,0,0.4);
28
+ width:100%;
29
+ }
30
+
31
+ /* ── Top row ── */
32
+ .top-row{display:flex;align-items:center;gap:5px;flex-wrap:nowrap;min-width:0}
33
+
34
+ .agent-select{
35
+ flex:1;min-width:0;background:rgba(255,255,255,0.04);border:1px solid var(--border);
36
+ border-radius:7px;padding:5px 6px;font-family:var(--font);font-size:11px;
37
+ font-weight:600;color:var(--text);outline:none;cursor:pointer;
38
+ transition:border-color .2s;
39
+ }
40
+ .agent-select:focus{border-color:var(--border-glow)}
41
+ .agent-select option{background:#1e293b;color:var(--text)}
42
+
43
+ .status-dot{width:6px;height:6px;border-radius:50%;background:var(--text-muted);flex-shrink:0;transition:background .3s}
44
+ .status-dot.listening{background:var(--green);box-shadow:0 0 5px var(--green);animation:pulse .8s infinite alternate}
45
+ .status-dot.thinking{background:var(--amber);box-shadow:0 0 5px var(--amber);animation:pulse .5s infinite alternate}
46
+ @keyframes pulse{from{opacity:.5}to{opacity:1}}
47
+
48
+ .icon-btn{
49
+ width:26px;height:26px;border-radius:7px;border:1px solid var(--border);
50
+ background:transparent;color:var(--text-muted);cursor:pointer;display:flex;
51
+ align-items:center;justify-content:center;font-size:13px;transition:all .2s;flex-shrink:0;
52
+ }
53
+ .icon-btn:hover{border-color:var(--border-glow);color:var(--text)}
54
+ .icon-btn.active{border-color:var(--accent);color:var(--accent);background:var(--accent-bg)}
55
+ .icon-btn.listening{border-color:var(--green);color:var(--green);background:rgba(74,222,128,0.1);animation:pulse .8s infinite alternate}
56
+
57
+ /* ── Thought bubble ── */
58
+ .response{
59
+ font-size:12px;line-height:1.6;color:rgba(255,255,255,0.75);
60
+ font-style:italic;letter-spacing:.01em;
61
+ flex:1;min-height:0;overflow-y:auto;padding:10px 12px;
62
+ border-radius:12px;
63
+ background:linear-gradient(135deg,rgba(139,92,246,0.08) 0%,rgba(34,211,238,0.06) 100%);
64
+ border:1px solid rgba(139,92,246,0.18);
65
+ box-shadow:0 0 16px rgba(139,92,246,0.08),inset 0 1px 0 rgba(255,255,255,0.05);
66
+ display:none;opacity:0;
67
+ transition:opacity .4s ease;
68
+ scrollbar-width:thin;
69
+ position:relative;
70
+ }
71
+ .response::before{
72
+ content:'';position:absolute;bottom:-6px;left:18px;
73
+ width:10px;height:10px;
74
+ background:rgba(139,92,246,0.15);
75
+ border-right:1px solid rgba(139,92,246,0.18);
76
+ border-bottom:1px solid rgba(139,92,246,0.18);
77
+ transform:rotate(45deg);
78
+ }
79
+ .response.visible{display:block}
80
+ .response.shown{opacity:1}
81
+ .response.fading{opacity:0}
82
+ [data-theme="light"] .response{
83
+ color:rgba(15,23,42,0.65);
84
+ background:linear-gradient(135deg,rgba(139,92,246,0.06) 0%,rgba(14,116,144,0.04) 100%);
85
+ border-color:rgba(139,92,246,0.15);
86
+ }
87
+ .response::-webkit-scrollbar{width:3px}
88
+ .response::-webkit-scrollbar-thumb{background:rgba(139,92,246,0.3);border-radius:2px}
89
+
90
+ /* ── Input row ── */
91
+ .input-row{display:flex;align-items:center;gap:5px}
92
+ .text-input{
93
+ flex:1;min-width:0;background:rgba(255,255,255,0.04);border:1px solid var(--border);
94
+ border-radius:7px;padding:5px 8px;font-family:var(--font);font-size:12px;
95
+ color:var(--text);outline:none;transition:border-color .2s;
96
+ resize:none;overflow-y:auto;max-height:80px;line-height:1.4;
97
+ }
98
+ .text-input::placeholder{color:var(--text-muted)}
99
+ .text-input:focus{border-color:var(--border-glow)}
100
+ .send-btn{
101
+ width:26px;height:26px;border-radius:7px;border:none;
102
+ background:var(--accent-bg);color:var(--accent);cursor:pointer;
103
+ display:flex;align-items:center;justify-content:center;font-size:14px;
104
+ transition:all .2s;flex-shrink:0;
105
+ }
106
+ .send-btn:hover{background:var(--accent);color:#000}
107
+ .send-btn:disabled{opacity:.4;cursor:not-allowed}
108
+
109
+ /* ── Status line ── */
110
+ .status-line{font-family:var(--font-mono);font-size:9px;color:var(--text-muted);text-align:center;letter-spacing:.04em}
111
+
112
+ </style>
113
+ <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
114
+ </head>
115
+ <body style="padding:6px">
116
+
117
+ <div class="bar" id="bar">
118
+
119
+ <!-- Top row: agent selector + mic + voice out + theme -->
120
+ <div class="top-row">
121
+ <div class="status-dot" id="statusDot"></div>
122
+ <select class="agent-select" id="agentSelect">
123
+ <option value="">Loading agents…</option>
124
+ </select>
125
+ <button class="icon-btn" id="micBtn" onclick="toggleListen()" title="Listen mode">🎤</button>
126
+ <button class="icon-btn" id="voiceBtn" onclick="toggleVoice()" title="Read responses aloud">🔇</button>
127
+ <button class="icon-btn" onclick="toggleTheme()" title="Theme" style="font-size:12px" id="themeBtn">☀</button>
128
+ <a class="icon-btn" href="/" target="_blank" title="Open full UI" style="text-decoration:none;font-size:11px;font-weight:700;color:var(--text-muted)">⤢</a>
129
+ </div>
130
+
131
+ <!-- Response -->
132
+ <div class="response" id="response"></div>
133
+
134
+ <!-- Input row -->
135
+ <div class="input-row">
136
+ <textarea class="text-input" id="textInput" rows="1" placeholder="Type a message…" onkeydown="handleKey(event)" oninput="autoGrow(this)"></textarea>
137
+ <button class="send-btn" id="sendBtn" onclick="send()" title="Send">→</button>
138
+ </div>
139
+
140
+ <div class="status-line" id="statusLine">idle · select an agent above</div>
141
+ </div>
142
+
143
+ <script>
144
+ // ─── Theme ────────────────────────────────────────────
145
+ (function(){
146
+ const s = localStorage.getItem('mini-theme') || 'dark';
147
+ document.documentElement.setAttribute('data-theme', s);
148
+ document.getElementById('themeBtn').textContent = s==='dark'?'☀':'☾';
149
+ })();
150
+ function toggleTheme(){
151
+ const c = document.documentElement.getAttribute('data-theme')||'dark';
152
+ const n = c==='dark'?'light':'dark';
153
+ document.documentElement.setAttribute('data-theme',n);
154
+ localStorage.setItem('mini-theme',n);
155
+ document.getElementById('themeBtn').textContent = n==='dark'?'☀':'☾';
156
+ }
157
+
158
+ // ─── State ────────────────────────────────────────────
159
+ let agents = [];
160
+ let selectedAgentId = '';
161
+ let isListening = false;
162
+ let voiceOn = false;
163
+ let isThinking = false;
164
+ let recognition = null;
165
+ let streamingText = '';
166
+
167
+ // ─── Load agents ──────────────────────────────────────
168
+ async function loadAgents(){
169
+ try{
170
+ const d = await (await fetch('/api/dashboard')).json();
171
+ agents = (d.agents||[]).filter(a => !a.subAgents);
172
+ const sel = document.getElementById('agentSelect');
173
+ sel.innerHTML = '';
174
+ // Default hub agent first
175
+ const hub = d.defaultGroupAgent || agents.find(a=>a.subAgents!==undefined)?.id || agents[0]?.id;
176
+ agents.forEach(a => {
177
+ const opt = document.createElement('option');
178
+ opt.value = a.id;
179
+ opt.textContent = (a.aliases?.[0] ? a.aliases[0]+' · ' : '') + a.name;
180
+ if(a.id === hub) opt.selected = true;
181
+ sel.appendChild(opt);
182
+ });
183
+ selectedAgentId = sel.value;
184
+ sel.onchange = () => { selectedAgentId = sel.value; updateStatus(); };
185
+ updateStatus();
186
+ } catch{ document.getElementById('agentSelect').innerHTML = '<option>No agents</option>'; }
187
+ }
188
+
189
+ // ─── Status helpers ───────────────────────────────────
190
+ function setDot(state){ // idle | listening | thinking
191
+ const d = document.getElementById('statusDot');
192
+ d.className = 'status-dot' + (state!=='idle' ? ' '+state : '');
193
+ }
194
+ function updateStatus(msg){
195
+ const agent = agents.find(a=>a.id===selectedAgentId);
196
+ const alias = agent?.aliases?.[0] || (agent ? '@'+agent.id : '?');
197
+ const state = isThinking ? 'thinking…' : isListening ? 'listening…' : 'idle';
198
+ document.getElementById('statusLine').textContent = msg || `${alias} · ${state}`;
199
+ }
200
+
201
+ // ─── Voice output ─────────────────────────────────────
202
+ function toggleVoice(){
203
+ voiceOn = !voiceOn;
204
+ const btn = document.getElementById('voiceBtn');
205
+ btn.textContent = voiceOn ? '🔊' : '🔇';
206
+ btn.classList.toggle('active', voiceOn);
207
+ if(!voiceOn) window.speechSynthesis?.cancel();
208
+ }
209
+ function speak(text){
210
+ if(!voiceOn || !window.speechSynthesis) return;
211
+ const clean = text.replace(/\[via:[^\]]+\]/g,'').replace(/[*_`#>~]/g,'').replace(/\n+/g,' ').trim();
212
+ if(!clean) return;
213
+ window.speechSynthesis.cancel();
214
+ const u = new SpeechSynthesisUtterance(clean);
215
+ u.rate = 1.05;
216
+ window.speechSynthesis.speak(u);
217
+ }
218
+
219
+ // ─── Listen mode (speech recognition) ────────────────
220
+ function toggleListen(){
221
+ if(isListening){ stopListen(); return; }
222
+ startListen();
223
+ }
224
+ function startListen(){
225
+ const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
226
+ if(!SR){ alert('Speech recognition not supported in this browser. Try Chrome.'); return; }
227
+ recognition = new SR();
228
+ recognition.continuous = true;
229
+ recognition.interimResults = true;
230
+ recognition.lang = 'en-US';
231
+
232
+ let silenceTimer = null;
233
+
234
+ recognition.onstart = () => {
235
+ isListening = true;
236
+ document.getElementById('micBtn').classList.add('listening');
237
+ setDot('listening');
238
+ updateStatus();
239
+ };
240
+ recognition.onresult = (e) => {
241
+ const transcript = Array.from(e.results).map(r=>r[0].transcript).join('');
242
+ document.getElementById('textInput').value = transcript;
243
+ // No auto-send — just populate the text box, user clicks send
244
+ };
245
+ recognition.onerror = (e) => {
246
+ // 'no-speech' is normal — just restart if still in listen mode
247
+ if(e.error === 'no-speech' || e.error === 'aborted') return;
248
+ stopListen();
249
+ };
250
+ recognition.onend = () => {
251
+ // Auto-restart if listen mode is still on (keeps mic hot)
252
+ if(isListening && !isThinking){
253
+ try { recognition.start(); } catch{ /* ignore */ }
254
+ }
255
+ };
256
+ recognition.start();
257
+ }
258
+ function stopListen(){
259
+ isListening = false;
260
+ recognition?.stop();
261
+ recognition = null;
262
+ document.getElementById('micBtn').classList.remove('listening');
263
+ setDot(isThinking ? 'thinking' : 'idle');
264
+ updateStatus();
265
+ }
266
+
267
+ // ─── Send message ─────────────────────────────────────
268
+ async function send(){
269
+ const input = document.getElementById('textInput');
270
+ const text = input.value.trim();
271
+ if(!text || !selectedAgentId || isThinking) return;
272
+ const listenWasOn = isListening;
273
+ input.value = '';
274
+ isThinking = true;
275
+ setDot('thinking');
276
+ document.getElementById('sendBtn').disabled = true;
277
+ showResponse('Starting...');
278
+ updateStatus();
279
+
280
+ try{
281
+ const startRes = await fetch(`/api/chat/${selectedAgentId}/stream`, {
282
+ method:'POST', headers:{'Content-Type':'application/json'},
283
+ body: JSON.stringify({ text: text, channel: 'web', chatId: 'mini' })
284
+ });
285
+ if(!startRes.ok) throw new Error('Failed to start');
286
+ const { jobId } = await startRes.json();
287
+ if(!jobId) throw new Error('No jobId');
288
+
289
+ streamingText = '';
290
+ let lastEventId = 0;
291
+ let isDone = false;
292
+ let retries = 0;
293
+ const MAX_RETRIES = 5;
294
+
295
+ while(!isDone && retries < MAX_RETRIES){
296
+ try{
297
+ const streamRes = await fetch(`/api/chat/jobs/${jobId}/stream?after=${lastEventId}`);
298
+ if(!streamRes.ok){ retries++; await sleep(1000); continue; }
299
+ const reader = streamRes.body.getReader();
300
+ const decoder = new TextDecoder();
301
+ let buffer = '';
302
+ while(true){
303
+ const { done, value } = await reader.read();
304
+ if(done) break;
305
+ buffer += decoder.decode(value, { stream: true });
306
+ const lines = buffer.split('\n');
307
+ buffer = lines.pop();
308
+ for(const line of lines){
309
+ if(!line.startsWith('data:')) continue;
310
+ const data = line.slice(5).trim();
311
+ if(!data) continue;
312
+ lastEventId++;
313
+ if(data==='[DONE]'){ isDone=true; continue; }
314
+ try{
315
+ const ev = JSON.parse(data);
316
+ if(ev.type==='text'){
317
+ streamingText += ev.data;
318
+ showResponse(streamingText);
319
+ } else if(ev.type==='error'){
320
+ streamingText = 'Error: '+ev.data;
321
+ isDone = true;
322
+ }
323
+ } catch{ /* skip */ }
324
+ }
325
+ }
326
+ if(!isDone){ retries++; await sleep(800); }
327
+ } catch{ retries++; await sleep(1000*retries); }
328
+ }
329
+
330
+ if(streamingText){
331
+ speak(streamingText);
332
+ showResponse(streamingText);
333
+ scheduleResponseFade();
334
+ }
335
+ } catch(e){
336
+ showResponse('Error: '+e.message);
337
+ scheduleResponseFade(8000);
338
+ } finally{
339
+ isThinking = false;
340
+ setDot('idle');
341
+ document.getElementById('sendBtn').disabled = false;
342
+ updateStatus();
343
+ // Re-arm listen mode if mic was on before this send
344
+ if(isListening && recognition) {
345
+ // recognition.onend auto-restarts, just make sure it's going
346
+ try { recognition.start(); } catch{ /* already running */ }
347
+ } else if(listenWasOn) {
348
+ setTimeout(startListen, 600);
349
+ }
350
+ }
351
+ }
352
+
353
+ let fadeTimer = null;
354
+
355
+ function showResponse(text){
356
+ const el = document.getElementById('response');
357
+ el.textContent = text.replace(/\[via:[^\]]+\]/g,'').trim();
358
+ // Clear any pending fade
359
+ if(fadeTimer) clearTimeout(fadeTimer);
360
+ el.classList.remove('fading');
361
+ el.classList.add('visible');
362
+ // Trigger fade-in on next frame
363
+ requestAnimationFrame(() => el.classList.add('shown'));
364
+ el.scrollTop = el.scrollHeight;
365
+ }
366
+
367
+ function scheduleResponseFade(delayMs = 15000){
368
+ if(fadeTimer) clearTimeout(fadeTimer);
369
+ fadeTimer = setTimeout(() => {
370
+ const el = document.getElementById('response');
371
+ el.classList.add('fading');
372
+ setTimeout(() => { el.classList.remove('visible','shown','fading'); }, 400);
373
+ }, delayMs);
374
+ }
375
+
376
+ function handleKey(e){
377
+ if(e.key==='Enter' && !e.shiftKey){ e.preventDefault(); send(); }
378
+ }
379
+ function autoGrow(el){
380
+ el.style.height = 'auto';
381
+ el.style.height = Math.min(el.scrollHeight, 80) + 'px';
382
+ }
383
+
384
+ function sleep(ms){ return new Promise(r=>setTimeout(r,ms)); }
385
+
386
+ // ─── Init ─────────────────────────────────────────────
387
+ loadAgents();
388
+ </script>
389
+ </body>
390
+ </html>