@jungjaehoon/mama-os 0.1.1

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 (356) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +643 -0
  3. package/dist/agent/agent-loop.d.ts +98 -0
  4. package/dist/agent/agent-loop.d.ts.map +1 -0
  5. package/dist/agent/agent-loop.js +417 -0
  6. package/dist/agent/agent-loop.js.map +1 -0
  7. package/dist/agent/auto-recall.d.ts +48 -0
  8. package/dist/agent/auto-recall.d.ts.map +1 -0
  9. package/dist/agent/auto-recall.js +178 -0
  10. package/dist/agent/auto-recall.js.map +1 -0
  11. package/dist/agent/claude-cli-wrapper.d.ts +130 -0
  12. package/dist/agent/claude-cli-wrapper.d.ts.map +1 -0
  13. package/dist/agent/claude-cli-wrapper.js +227 -0
  14. package/dist/agent/claude-cli-wrapper.js.map +1 -0
  15. package/dist/agent/claude-client.d.ts +50 -0
  16. package/dist/agent/claude-client.d.ts.map +1 -0
  17. package/dist/agent/claude-client.js +214 -0
  18. package/dist/agent/claude-client.js.map +1 -0
  19. package/dist/agent/gateway-tool-executor.d.ts +75 -0
  20. package/dist/agent/gateway-tool-executor.d.ts.map +1 -0
  21. package/dist/agent/gateway-tool-executor.js +348 -0
  22. package/dist/agent/gateway-tool-executor.js.map +1 -0
  23. package/dist/agent/index.d.ts +13 -0
  24. package/dist/agent/index.d.ts.map +1 -0
  25. package/dist/agent/index.js +18 -0
  26. package/dist/agent/index.js.map +1 -0
  27. package/dist/agent/mcp-executor.d.ts +75 -0
  28. package/dist/agent/mcp-executor.d.ts.map +1 -0
  29. package/dist/agent/mcp-executor.js +307 -0
  30. package/dist/agent/mcp-executor.js.map +1 -0
  31. package/dist/agent/session-pool.d.ts +148 -0
  32. package/dist/agent/session-pool.d.ts.map +1 -0
  33. package/dist/agent/session-pool.js +272 -0
  34. package/dist/agent/session-pool.js.map +1 -0
  35. package/dist/agent/streaming-callback-manager.d.ts +85 -0
  36. package/dist/agent/streaming-callback-manager.d.ts.map +1 -0
  37. package/dist/agent/streaming-callback-manager.js +103 -0
  38. package/dist/agent/streaming-callback-manager.js.map +1 -0
  39. package/dist/agent/types.d.ts +437 -0
  40. package/dist/agent/types.d.ts.map +1 -0
  41. package/dist/agent/types.js +29 -0
  42. package/dist/agent/types.js.map +1 -0
  43. package/dist/api/cron-handler.d.ts +44 -0
  44. package/dist/api/cron-handler.d.ts.map +1 -0
  45. package/dist/api/cron-handler.js +195 -0
  46. package/dist/api/cron-handler.js.map +1 -0
  47. package/dist/api/error-handler.d.ts +22 -0
  48. package/dist/api/error-handler.d.ts.map +1 -0
  49. package/dist/api/error-handler.js +104 -0
  50. package/dist/api/error-handler.js.map +1 -0
  51. package/dist/api/heartbeat-handler.d.ts +49 -0
  52. package/dist/api/heartbeat-handler.d.ts.map +1 -0
  53. package/dist/api/heartbeat-handler.js +91 -0
  54. package/dist/api/heartbeat-handler.js.map +1 -0
  55. package/dist/api/index.d.ts +61 -0
  56. package/dist/api/index.d.ts.map +1 -0
  57. package/dist/api/index.js +145 -0
  58. package/dist/api/index.js.map +1 -0
  59. package/dist/api/types.d.ts +156 -0
  60. package/dist/api/types.d.ts.map +1 -0
  61. package/dist/api/types.js +62 -0
  62. package/dist/api/types.js.map +1 -0
  63. package/dist/auth/index.d.ts +7 -0
  64. package/dist/auth/index.d.ts.map +1 -0
  65. package/dist/auth/index.js +11 -0
  66. package/dist/auth/index.js.map +1 -0
  67. package/dist/auth/oauth-manager.d.ts +59 -0
  68. package/dist/auth/oauth-manager.d.ts.map +1 -0
  69. package/dist/auth/oauth-manager.js +237 -0
  70. package/dist/auth/oauth-manager.js.map +1 -0
  71. package/dist/auth/types.d.ts +92 -0
  72. package/dist/auth/types.d.ts.map +1 -0
  73. package/dist/auth/types.js +23 -0
  74. package/dist/auth/types.js.map +1 -0
  75. package/dist/cli/commands/init.d.ts +19 -0
  76. package/dist/cli/commands/init.d.ts.map +1 -0
  77. package/dist/cli/commands/init.js +155 -0
  78. package/dist/cli/commands/init.js.map +1 -0
  79. package/dist/cli/commands/run.d.ts +19 -0
  80. package/dist/cli/commands/run.d.ts.map +1 -0
  81. package/dist/cli/commands/run.js +89 -0
  82. package/dist/cli/commands/run.js.map +1 -0
  83. package/dist/cli/commands/setup.d.ts +19 -0
  84. package/dist/cli/commands/setup.d.ts.map +1 -0
  85. package/dist/cli/commands/setup.js +134 -0
  86. package/dist/cli/commands/setup.js.map +1 -0
  87. package/dist/cli/commands/start.d.ts +24 -0
  88. package/dist/cli/commands/start.d.ts.map +1 -0
  89. package/dist/cli/commands/start.js +1073 -0
  90. package/dist/cli/commands/start.js.map +1 -0
  91. package/dist/cli/commands/status.d.ts +10 -0
  92. package/dist/cli/commands/status.d.ts.map +1 -0
  93. package/dist/cli/commands/status.js +85 -0
  94. package/dist/cli/commands/status.js.map +1 -0
  95. package/dist/cli/commands/stop.d.ts +10 -0
  96. package/dist/cli/commands/stop.d.ts.map +1 -0
  97. package/dist/cli/commands/stop.js +65 -0
  98. package/dist/cli/commands/stop.js.map +1 -0
  99. package/dist/cli/config/config-manager.d.ts +51 -0
  100. package/dist/cli/config/config-manager.d.ts.map +1 -0
  101. package/dist/cli/config/config-manager.js +216 -0
  102. package/dist/cli/config/config-manager.js.map +1 -0
  103. package/dist/cli/config/types.d.ts +172 -0
  104. package/dist/cli/config/types.d.ts.map +1 -0
  105. package/dist/cli/config/types.js +48 -0
  106. package/dist/cli/config/types.js.map +1 -0
  107. package/dist/cli/index.d.ts +8 -0
  108. package/dist/cli/index.d.ts.map +1 -0
  109. package/dist/cli/index.js +92 -0
  110. package/dist/cli/index.js.map +1 -0
  111. package/dist/cli/utils/pid-manager.d.ts +66 -0
  112. package/dist/cli/utils/pid-manager.d.ts.map +1 -0
  113. package/dist/cli/utils/pid-manager.js +167 -0
  114. package/dist/cli/utils/pid-manager.js.map +1 -0
  115. package/dist/concurrency/index.d.ts +13 -0
  116. package/dist/concurrency/index.d.ts.map +1 -0
  117. package/dist/concurrency/index.js +22 -0
  118. package/dist/concurrency/index.js.map +1 -0
  119. package/dist/concurrency/lane-manager.d.ts +113 -0
  120. package/dist/concurrency/lane-manager.d.ts.map +1 -0
  121. package/dist/concurrency/lane-manager.js +245 -0
  122. package/dist/concurrency/lane-manager.js.map +1 -0
  123. package/dist/concurrency/session-key.d.ts +41 -0
  124. package/dist/concurrency/session-key.d.ts.map +1 -0
  125. package/dist/concurrency/session-key.js +61 -0
  126. package/dist/concurrency/session-key.js.map +1 -0
  127. package/dist/concurrency/types.d.ts +69 -0
  128. package/dist/concurrency/types.d.ts.map +1 -0
  129. package/dist/concurrency/types.js +16 -0
  130. package/dist/concurrency/types.js.map +1 -0
  131. package/dist/gateways/channel-history.d.ts +102 -0
  132. package/dist/gateways/channel-history.d.ts.map +1 -0
  133. package/dist/gateways/channel-history.js +181 -0
  134. package/dist/gateways/channel-history.js.map +1 -0
  135. package/dist/gateways/context-injector.d.ts +74 -0
  136. package/dist/gateways/context-injector.d.ts.map +1 -0
  137. package/dist/gateways/context-injector.js +121 -0
  138. package/dist/gateways/context-injector.js.map +1 -0
  139. package/dist/gateways/discord.d.ts +122 -0
  140. package/dist/gateways/discord.d.ts.map +1 -0
  141. package/dist/gateways/discord.js +602 -0
  142. package/dist/gateways/discord.js.map +1 -0
  143. package/dist/gateways/index.d.ts +30 -0
  144. package/dist/gateways/index.d.ts.map +1 -0
  145. package/dist/gateways/index.js +49 -0
  146. package/dist/gateways/index.js.map +1 -0
  147. package/dist/gateways/message-router.d.ts +116 -0
  148. package/dist/gateways/message-router.d.ts.map +1 -0
  149. package/dist/gateways/message-router.js +315 -0
  150. package/dist/gateways/message-router.js.map +1 -0
  151. package/dist/gateways/message-splitter.d.ts +54 -0
  152. package/dist/gateways/message-splitter.d.ts.map +1 -0
  153. package/dist/gateways/message-splitter.js +146 -0
  154. package/dist/gateways/message-splitter.js.map +1 -0
  155. package/dist/gateways/plugin-loader.d.ts +76 -0
  156. package/dist/gateways/plugin-loader.d.ts.map +1 -0
  157. package/dist/gateways/plugin-loader.js +221 -0
  158. package/dist/gateways/plugin-loader.js.map +1 -0
  159. package/dist/gateways/session-store.d.ts +77 -0
  160. package/dist/gateways/session-store.d.ts.map +1 -0
  161. package/dist/gateways/session-store.js +233 -0
  162. package/dist/gateways/session-store.js.map +1 -0
  163. package/dist/gateways/slack.d.ts +90 -0
  164. package/dist/gateways/slack.d.ts.map +1 -0
  165. package/dist/gateways/slack.js +281 -0
  166. package/dist/gateways/slack.js.map +1 -0
  167. package/dist/gateways/telegram.d.ts +79 -0
  168. package/dist/gateways/telegram.d.ts.map +1 -0
  169. package/dist/gateways/telegram.js +207 -0
  170. package/dist/gateways/telegram.js.map +1 -0
  171. package/dist/gateways/types.d.ts +340 -0
  172. package/dist/gateways/types.d.ts.map +1 -0
  173. package/dist/gateways/types.js +6 -0
  174. package/dist/gateways/types.js.map +1 -0
  175. package/dist/index.d.ts +7 -0
  176. package/dist/index.d.ts.map +1 -0
  177. package/dist/index.js +26 -0
  178. package/dist/index.js.map +1 -0
  179. package/dist/memory/memory-logger.d.ts +47 -0
  180. package/dist/memory/memory-logger.d.ts.map +1 -0
  181. package/dist/memory/memory-logger.js +126 -0
  182. package/dist/memory/memory-logger.js.map +1 -0
  183. package/dist/onboarding/all-tools.d.ts +18 -0
  184. package/dist/onboarding/all-tools.d.ts.map +1 -0
  185. package/dist/onboarding/all-tools.js +149 -0
  186. package/dist/onboarding/all-tools.js.map +1 -0
  187. package/dist/onboarding/autonomous-discovery-tools.d.ts +13 -0
  188. package/dist/onboarding/autonomous-discovery-tools.d.ts.map +1 -0
  189. package/dist/onboarding/autonomous-discovery-tools.js +268 -0
  190. package/dist/onboarding/autonomous-discovery-tools.js.map +1 -0
  191. package/dist/onboarding/bootstrap-template.d.ts +5 -0
  192. package/dist/onboarding/bootstrap-template.d.ts.map +1 -0
  193. package/dist/onboarding/bootstrap-template.js +142 -0
  194. package/dist/onboarding/bootstrap-template.js.map +1 -0
  195. package/dist/onboarding/complete-autonomous-prompt.d.ts +13 -0
  196. package/dist/onboarding/complete-autonomous-prompt.d.ts.map +1 -0
  197. package/dist/onboarding/complete-autonomous-prompt.js +1220 -0
  198. package/dist/onboarding/complete-autonomous-prompt.js.map +1 -0
  199. package/dist/onboarding/onboarding-state.d.ts +70 -0
  200. package/dist/onboarding/onboarding-state.d.ts.map +1 -0
  201. package/dist/onboarding/onboarding-state.js +184 -0
  202. package/dist/onboarding/onboarding-state.js.map +1 -0
  203. package/dist/onboarding/personality-quiz.d.ts +35 -0
  204. package/dist/onboarding/personality-quiz.d.ts.map +1 -0
  205. package/dist/onboarding/personality-quiz.js +219 -0
  206. package/dist/onboarding/personality-quiz.js.map +1 -0
  207. package/dist/onboarding/phase-5-summary.d.ts +22 -0
  208. package/dist/onboarding/phase-5-summary.d.ts.map +1 -0
  209. package/dist/onboarding/phase-5-summary.js +151 -0
  210. package/dist/onboarding/phase-5-summary.js.map +1 -0
  211. package/dist/onboarding/phase-6-security.d.ts +33 -0
  212. package/dist/onboarding/phase-6-security.d.ts.map +1 -0
  213. package/dist/onboarding/phase-6-security.js +473 -0
  214. package/dist/onboarding/phase-6-security.js.map +1 -0
  215. package/dist/onboarding/phase-7-integrations.d.ts +66 -0
  216. package/dist/onboarding/phase-7-integrations.d.ts.map +1 -0
  217. package/dist/onboarding/phase-7-integrations.js +619 -0
  218. package/dist/onboarding/phase-7-integrations.js.map +1 -0
  219. package/dist/onboarding/phase-8-demo.d.ts +43 -0
  220. package/dist/onboarding/phase-8-demo.d.ts.map +1 -0
  221. package/dist/onboarding/phase-8-demo.js +346 -0
  222. package/dist/onboarding/phase-8-demo.js.map +1 -0
  223. package/dist/onboarding/phase-9-finalization.d.ts +22 -0
  224. package/dist/onboarding/phase-9-finalization.d.ts.map +1 -0
  225. package/dist/onboarding/phase-9-finalization.js +375 -0
  226. package/dist/onboarding/phase-9-finalization.js.map +1 -0
  227. package/dist/onboarding/ritual-prompt.d.ts +2 -0
  228. package/dist/onboarding/ritual-prompt.d.ts.map +1 -0
  229. package/dist/onboarding/ritual-prompt.js +285 -0
  230. package/dist/onboarding/ritual-prompt.js.map +1 -0
  231. package/dist/onboarding/ritual-tools.d.ts +13 -0
  232. package/dist/onboarding/ritual-tools.d.ts.map +1 -0
  233. package/dist/onboarding/ritual-tools.js +93 -0
  234. package/dist/onboarding/ritual-tools.js.map +1 -0
  235. package/dist/runners/cli-runner.d.ts +59 -0
  236. package/dist/runners/cli-runner.d.ts.map +1 -0
  237. package/dist/runners/cli-runner.js +190 -0
  238. package/dist/runners/cli-runner.js.map +1 -0
  239. package/dist/runners/index.d.ts +11 -0
  240. package/dist/runners/index.d.ts.map +1 -0
  241. package/dist/runners/index.js +15 -0
  242. package/dist/runners/index.js.map +1 -0
  243. package/dist/runners/types.d.ts +81 -0
  244. package/dist/runners/types.d.ts.map +1 -0
  245. package/dist/runners/types.js +31 -0
  246. package/dist/runners/types.js.map +1 -0
  247. package/dist/scheduler/cron-scheduler.d.ts +115 -0
  248. package/dist/scheduler/cron-scheduler.d.ts.map +1 -0
  249. package/dist/scheduler/cron-scheduler.js +320 -0
  250. package/dist/scheduler/cron-scheduler.js.map +1 -0
  251. package/dist/scheduler/heartbeat.d.ts +53 -0
  252. package/dist/scheduler/heartbeat.d.ts.map +1 -0
  253. package/dist/scheduler/heartbeat.js +160 -0
  254. package/dist/scheduler/heartbeat.js.map +1 -0
  255. package/dist/scheduler/index.d.ts +22 -0
  256. package/dist/scheduler/index.d.ts.map +1 -0
  257. package/dist/scheduler/index.js +31 -0
  258. package/dist/scheduler/index.js.map +1 -0
  259. package/dist/scheduler/job-lock.d.ts +85 -0
  260. package/dist/scheduler/job-lock.d.ts.map +1 -0
  261. package/dist/scheduler/job-lock.js +137 -0
  262. package/dist/scheduler/job-lock.js.map +1 -0
  263. package/dist/scheduler/recovery.d.ts +78 -0
  264. package/dist/scheduler/recovery.d.ts.map +1 -0
  265. package/dist/scheduler/recovery.js +124 -0
  266. package/dist/scheduler/recovery.js.map +1 -0
  267. package/dist/scheduler/schedule-store.d.ts +112 -0
  268. package/dist/scheduler/schedule-store.d.ts.map +1 -0
  269. package/dist/scheduler/schedule-store.js +259 -0
  270. package/dist/scheduler/schedule-store.js.map +1 -0
  271. package/dist/scheduler/token-keep-alive.d.ts +49 -0
  272. package/dist/scheduler/token-keep-alive.d.ts.map +1 -0
  273. package/dist/scheduler/token-keep-alive.js +102 -0
  274. package/dist/scheduler/token-keep-alive.js.map +1 -0
  275. package/dist/scheduler/types.d.ts +96 -0
  276. package/dist/scheduler/types.d.ts.map +1 -0
  277. package/dist/scheduler/types.js +21 -0
  278. package/dist/scheduler/types.js.map +1 -0
  279. package/dist/setup/setup-prompt.d.ts +2 -0
  280. package/dist/setup/setup-prompt.d.ts.map +1 -0
  281. package/dist/setup/setup-prompt.js +138 -0
  282. package/dist/setup/setup-prompt.js.map +1 -0
  283. package/dist/setup/setup-server.d.ts +8 -0
  284. package/dist/setup/setup-server.d.ts.map +1 -0
  285. package/dist/setup/setup-server.js +71 -0
  286. package/dist/setup/setup-server.js.map +1 -0
  287. package/dist/setup/setup-tools.d.ts +13 -0
  288. package/dist/setup/setup-tools.d.ts.map +1 -0
  289. package/dist/setup/setup-tools.js +103 -0
  290. package/dist/setup/setup-tools.js.map +1 -0
  291. package/dist/setup/setup-websocket.d.ts +6 -0
  292. package/dist/setup/setup-websocket.d.ts.map +1 -0
  293. package/dist/setup/setup-websocket.js +312 -0
  294. package/dist/setup/setup-websocket.js.map +1 -0
  295. package/dist/skills/index.d.ts +10 -0
  296. package/dist/skills/index.d.ts.map +1 -0
  297. package/dist/skills/index.js +26 -0
  298. package/dist/skills/index.js.map +1 -0
  299. package/dist/skills/skill-executor.d.ts +48 -0
  300. package/dist/skills/skill-executor.d.ts.map +1 -0
  301. package/dist/skills/skill-executor.js +483 -0
  302. package/dist/skills/skill-executor.js.map +1 -0
  303. package/dist/skills/skill-loader.d.ts +40 -0
  304. package/dist/skills/skill-loader.d.ts.map +1 -0
  305. package/dist/skills/skill-loader.js +225 -0
  306. package/dist/skills/skill-loader.js.map +1 -0
  307. package/dist/skills/skill-matcher.d.ts +33 -0
  308. package/dist/skills/skill-matcher.d.ts.map +1 -0
  309. package/dist/skills/skill-matcher.js +190 -0
  310. package/dist/skills/skill-matcher.js.map +1 -0
  311. package/dist/skills/types.d.ts +123 -0
  312. package/dist/skills/types.d.ts.map +1 -0
  313. package/dist/skills/types.js +12 -0
  314. package/dist/skills/types.js.map +1 -0
  315. package/dist/tools/browser-tool.d.ts +149 -0
  316. package/dist/tools/browser-tool.d.ts.map +1 -0
  317. package/dist/tools/browser-tool.js +257 -0
  318. package/dist/tools/browser-tool.js.map +1 -0
  319. package/package.json +84 -0
  320. package/public/favicon.ico +0 -0
  321. package/public/setup.html +1026 -0
  322. package/public/viewer/icons/icon-192.png +0 -0
  323. package/public/viewer/icons/icon-512.png +0 -0
  324. package/public/viewer/js/modules/chat.js +1587 -0
  325. package/public/viewer/js/modules/dashboard.js +275 -0
  326. package/public/viewer/js/modules/graph.js +997 -0
  327. package/public/viewer/js/modules/memory.js +353 -0
  328. package/public/viewer/js/modules/settings.js +255 -0
  329. package/public/viewer/js/utils/api.js +169 -0
  330. package/public/viewer/js/utils/dom.js +92 -0
  331. package/public/viewer/js/utils/format.js +192 -0
  332. package/public/viewer/manifest.json +26 -0
  333. package/public/viewer/sw.js +131 -0
  334. package/public/viewer/viewer.css +500 -0
  335. package/public/viewer/viewer.html +1535 -0
  336. package/scripts/postinstall.js +118 -0
  337. package/templates/skills/document-analyze.md +63 -0
  338. package/templates/skills/heartbeat-report.md +75 -0
  339. package/templates/skills/image-translate.md +67 -0
  340. package/templates/workspace/skill-forge/DESIGN.md +115 -0
  341. package/templates/workspace/skill-forge/agents/architect.ts +295 -0
  342. package/templates/workspace/skill-forge/agents/developer.ts +364 -0
  343. package/templates/workspace/skill-forge/agents/qa.ts +313 -0
  344. package/templates/workspace/skill-forge/claude-api.ts +353 -0
  345. package/templates/workspace/skill-forge/discord-ui.ts +580 -0
  346. package/templates/workspace/skill-forge/error-handler.ts +354 -0
  347. package/templates/workspace/skill-forge/mama-integration.ts +357 -0
  348. package/templates/workspace/skill-forge/orchestrator.ts +495 -0
  349. package/templates/workspace/skill-forge/output/generated-skills/skills/hello-world/README.md +24 -0
  350. package/templates/workspace/skill-forge/output/generated-skills/skills/hello-world/index.ts +79 -0
  351. package/templates/workspace/skill-forge/output/generated-skills/skills/hello-world/types.ts +17 -0
  352. package/templates/workspace/skill-forge/package.json +21 -0
  353. package/templates/workspace/skill-forge/state/session.json +132 -0
  354. package/templates/workspace/skill-forge/test-e2e.ts +139 -0
  355. package/templates/workspace/skill-forge/tsconfig.json +20 -0
  356. package/templates/workspace/skill-forge/types.ts +159 -0
@@ -0,0 +1,1535 @@
1
+ <!doctype html>
2
+ <html class="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta
6
+ name="viewport"
7
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
8
+ />
9
+ <title>MAMA - Memory-Augmented Assistant</title>
10
+
11
+ <meta name="theme-color" content="#0a0a0f" />
12
+ <meta name="mobile-web-app-capable" content="yes" />
13
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
14
+ <meta name="apple-mobile-web-app-title" content="MAMA" />
15
+ <link rel="apple-touch-icon" href="/viewer/icons/icon-192.png" />
16
+
17
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
18
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
19
+ <link
20
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
21
+ rel="stylesheet"
22
+ />
23
+
24
+ <script src="https://unpkg.com/vis-network@9/dist/vis-network.min.js"></script>
25
+ <script src="https://unpkg.com/lucide@latest"></script>
26
+ <script src="https://cdn.jsdelivr.net/npm/marked@11/marked.min.js"></script>
27
+ <script src="https://cdn.tailwindcss.com"></script>
28
+ <script>
29
+ tailwind.config = {
30
+ darkMode: 'class',
31
+ theme: {
32
+ extend: {
33
+ colors: {
34
+ background: 'hsl(var(--background))',
35
+ foreground: 'hsl(var(--foreground))',
36
+ card: 'hsl(var(--card))',
37
+ 'card-foreground': 'hsl(var(--card-foreground))',
38
+ border: 'hsl(var(--border))',
39
+ muted: 'hsl(var(--muted))',
40
+ 'muted-foreground': 'hsl(var(--muted-foreground))',
41
+ primary: 'hsl(var(--primary))',
42
+ 'primary-foreground': 'hsl(var(--primary-foreground))',
43
+ secondary: 'hsl(var(--secondary))',
44
+ 'secondary-foreground': 'hsl(var(--secondary-foreground))',
45
+ destructive: 'hsl(var(--destructive))',
46
+ 'destructive-foreground': 'hsl(var(--destructive-foreground))',
47
+ success: 'hsl(var(--success))',
48
+ warning: 'hsl(var(--warning))',
49
+ info: 'hsl(var(--info))',
50
+ ring: 'hsl(var(--ring))',
51
+ },
52
+ spacing: {
53
+ 'header': 'var(--header-height)',
54
+ 'sidebar': 'var(--sidebar-width)',
55
+ },
56
+ borderRadius: {
57
+ DEFAULT: 'var(--radius)',
58
+ sm: 'var(--radius-sm)',
59
+ lg: 'var(--radius-lg)',
60
+ },
61
+ fontFamily: {
62
+ sans: ['Inter', 'sans-serif'],
63
+ mono: ['var(--font-mono)', 'monospace'],
64
+ },
65
+ }
66
+ }
67
+ }
68
+ </script>
69
+ <style>
70
+ :root {
71
+ --background: 0 0% 100%;
72
+ --foreground: 0 0% 9%;
73
+ --card: 0 0% 98%;
74
+ --card-foreground: 0 0% 9%;
75
+ --border: 0 0% 89%;
76
+ --muted: 0 0% 96%;
77
+ --muted-foreground: 0 0% 45%;
78
+ --primary: 240 80% 60%;
79
+ --primary-foreground: 0 0% 100%;
80
+ --secondary: 240 4.8% 95.9%;
81
+ --secondary-foreground: 240 5.9% 10%;
82
+ --destructive: 0 84.2% 60.2%;
83
+ --destructive-foreground: 0 0% 98%;
84
+ --success: 142 76% 36%;
85
+ --warning: 38 92% 50%;
86
+ --info: 199 89% 48%;
87
+ --ring: 240 80% 60%;
88
+ --radius: 0.5rem;
89
+ --radius-sm: 0.375rem;
90
+ --radius-lg: 0.75rem;
91
+ --header-height: 56px;
92
+ --sidebar-width: 280px;
93
+ --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
94
+ }
95
+ .dark {
96
+ --background: 222.2 84% 4.9%;
97
+ --foreground: 210 40% 98%;
98
+ --card: 222.2 84% 7%;
99
+ --card-foreground: 210 40% 98%;
100
+ --border: 217.2 32.6% 17.5%;
101
+ --muted: 217.2 32.6% 17.5%;
102
+ --muted-foreground: 215 20.2% 65.1%;
103
+ --primary: 240 80% 60%;
104
+ --primary-foreground: 222.2 47.4% 11.2%;
105
+ --secondary: 217.2 32.6% 17.5%;
106
+ --secondary-foreground: 210 40% 98%;
107
+ --destructive: 0 62.8% 30.6%;
108
+ --destructive-foreground: 210 40% 98%;
109
+ --success: 142 76% 36%;
110
+ --warning: 38 92% 50%;
111
+ --info: 199 89% 48%;
112
+ --ring: 240 80% 60%;
113
+ }
114
+ </style>
115
+
116
+ <link rel="stylesheet" href="/viewer/viewer.css" />
117
+ </head>
118
+ <body class="bg-white dark:bg-gray-950 text-gray-900 dark:text-gray-100">
119
+ <div id="app" class="h-screen flex flex-col">
120
+ <!-- Header -->
121
+ <header class="fixed top-0 left-0 right-0 h-14 bg-white/80 dark:bg-gray-950/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-800 z-50">
122
+ <div class="h-full max-w-[1920px] mx-auto px-4 flex items-center justify-between">
123
+ <!-- Logo -->
124
+ <a href="/" class="flex items-center gap-2 text-lg font-semibold hover:opacity-80 transition-opacity">
125
+ <div class="w-6 h-6 rounded bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-white text-sm">✦</div>
126
+ <span class="hidden sm:inline">MAMA</span>
127
+ </a>
128
+
129
+ <!-- Navigation Tabs -->
130
+ <nav class="flex items-center gap-1" id="nav-tabs">
131
+ <button
132
+ class="hidden px-3 py-1.5 rounded-lg text-sm font-medium transition-colors hover:bg-gray-100 dark:hover:bg-gray-800"
133
+ data-tab="setup"
134
+ onclick="window.switchTab && window.switchTab('setup')"
135
+ >
136
+ <svg
137
+ xmlns="http://www.w3.org/2000/svg"
138
+ width="16"
139
+ height="16"
140
+ viewBox="0 0 24 24"
141
+ fill="none"
142
+ stroke="currentColor"
143
+ stroke-width="2"
144
+ stroke-linecap="round"
145
+ stroke-linejoin="round"
146
+ >
147
+ <path
148
+ d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"
149
+ />
150
+ <circle cx="12" cy="12" r="3" />
151
+ </svg>
152
+ <span class="hidden sm:inline">Setup</span>
153
+ </button>
154
+ <button class="px-3 py-1.5 rounded-lg text-sm font-medium transition-colors hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center gap-2" data-tab="dashboard" onclick="window.switchTab && window.switchTab('dashboard')">
155
+ <svg
156
+ xmlns="http://www.w3.org/2000/svg"
157
+ width="16"
158
+ height="16"
159
+ viewBox="0 0 24 24"
160
+ fill="none"
161
+ stroke="currentColor"
162
+ stroke-width="2"
163
+ stroke-linecap="round"
164
+ stroke-linejoin="round"
165
+ >
166
+ <rect x="3" y="3" width="7" height="7" />
167
+ <rect x="14" y="3" width="7" height="7" />
168
+ <rect x="14" y="14" width="7" height="7" />
169
+ <rect x="3" y="14" width="7" height="7" />
170
+ </svg>
171
+ <span class="hidden sm:inline">Dashboard</span>
172
+ </button>
173
+ <button class="px-3 py-1.5 rounded-lg text-sm font-medium transition-colors bg-indigo-100 dark:bg-indigo-900/30 text-indigo-600 dark:text-indigo-400 flex items-center gap-2" data-tab="chat" onclick="window.switchTab && window.switchTab('chat')">
174
+ <svg
175
+ xmlns="http://www.w3.org/2000/svg"
176
+ width="16"
177
+ height="16"
178
+ viewBox="0 0 24 24"
179
+ fill="none"
180
+ stroke="currentColor"
181
+ stroke-width="2"
182
+ stroke-linecap="round"
183
+ stroke-linejoin="round"
184
+ >
185
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
186
+ </svg>
187
+ <span class="hidden sm:inline">Chat</span>
188
+ </button>
189
+ <button class="px-3 py-1.5 rounded-lg text-sm font-medium transition-colors hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center gap-2" data-tab="memory" onclick="window.switchTab && window.switchTab('memory')">
190
+ <svg
191
+ xmlns="http://www.w3.org/2000/svg"
192
+ width="16"
193
+ height="16"
194
+ viewBox="0 0 24 24"
195
+ fill="none"
196
+ stroke="currentColor"
197
+ stroke-width="2"
198
+ stroke-linecap="round"
199
+ stroke-linejoin="round"
200
+ >
201
+ <path
202
+ d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"
203
+ />
204
+ <polyline points="3.29 7 12 12 20.71 7" />
205
+ <line x1="12" y1="22" x2="12" y2="12" />
206
+ </svg>
207
+ <span class="hidden sm:inline">Memory</span>
208
+ </button>
209
+ <button class="px-3 py-1.5 rounded-lg text-sm font-medium transition-colors hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center gap-2" data-tab="settings" onclick="window.switchTab && window.switchTab('settings')">
210
+ <svg
211
+ xmlns="http://www.w3.org/2000/svg"
212
+ width="16"
213
+ height="16"
214
+ viewBox="0 0 24 24"
215
+ fill="none"
216
+ stroke="currentColor"
217
+ stroke-width="2"
218
+ stroke-linecap="round"
219
+ stroke-linejoin="round"
220
+ >
221
+ <path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
222
+ <circle cx="12" cy="12" r="3" />
223
+ </svg>
224
+ <span class="hidden sm:inline">Settings</span>
225
+ </button>
226
+ </nav>
227
+
228
+ <!-- Header Actions -->
229
+ <div class="flex items-center gap-2">
230
+ <button
231
+ class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
232
+ onclick="toggleTheme()"
233
+ title="Toggle theme"
234
+ >
235
+ <svg
236
+ id="theme-icon-light"
237
+ style="display: none"
238
+ xmlns="http://www.w3.org/2000/svg"
239
+ width="16"
240
+ height="16"
241
+ viewBox="0 0 24 24"
242
+ fill="none"
243
+ stroke="currentColor"
244
+ stroke-width="2"
245
+ stroke-linecap="round"
246
+ stroke-linejoin="round"
247
+ >
248
+ <circle cx="12" cy="12" r="4" />
249
+ <path d="M12 2v2" />
250
+ <path d="M12 20v2" />
251
+ <path d="m4.93 4.93 1.41 1.41" />
252
+ <path d="m17.66 17.66 1.41 1.41" />
253
+ <path d="M2 12h2" />
254
+ <path d="M20 12h2" />
255
+ <path d="m6.34 17.66-1.41 1.41" />
256
+ <path d="m19.07 4.93-1.41 1.41" />
257
+ </svg>
258
+ <svg
259
+ id="theme-icon-dark"
260
+ xmlns="http://www.w3.org/2000/svg"
261
+ width="16"
262
+ height="16"
263
+ viewBox="0 0 24 24"
264
+ fill="none"
265
+ stroke="currentColor"
266
+ stroke-width="2"
267
+ stroke-linecap="round"
268
+ stroke-linejoin="round"
269
+ >
270
+ <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
271
+ </svg>
272
+ </button>
273
+ <button
274
+ class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
275
+ onclick="showTunnelSetup()"
276
+ title="External Access"
277
+ >
278
+ <svg
279
+ xmlns="http://www.w3.org/2000/svg"
280
+ width="16"
281
+ height="16"
282
+ viewBox="0 0 24 24"
283
+ fill="none"
284
+ stroke="currentColor"
285
+ stroke-width="2"
286
+ stroke-linecap="round"
287
+ stroke-linejoin="round"
288
+ >
289
+ <circle cx="12" cy="12" r="10" />
290
+ <line x1="2" y1="12" x2="22" y2="12" />
291
+ <path
292
+ d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"
293
+ />
294
+ </svg>
295
+ </button>
296
+ </div>
297
+ </div>
298
+ </header>
299
+
300
+ <!-- Main Content -->
301
+ <main class="flex-1 pt-14 overflow-hidden flex flex-col">
302
+ <!-- Setup tab hidden: runs on port 3848 -->
303
+ <div class="tab-content" id="tab-setup" style="display: none">
304
+ <div class="setup-container">
305
+ <div class="progress-section" id="progress-section" style="display: none">
306
+ <div class="progress-header">
307
+ <span class="progress-label" id="progress-label">Getting started...</span>
308
+ <span class="progress-count" id="progress-count">1 / 9</span>
309
+ </div>
310
+ <div class="progress-bar" id="progress-bar">
311
+ <div class="progress-step"></div>
312
+ <div class="progress-step"></div>
313
+ <div class="progress-step"></div>
314
+ <div class="progress-step"></div>
315
+ <div class="progress-step"></div>
316
+ <div class="progress-step"></div>
317
+ <div class="progress-step"></div>
318
+ <div class="progress-step"></div>
319
+ <div class="progress-step"></div>
320
+ </div>
321
+ </div>
322
+
323
+ <div class="chat-container" id="setup-chat">
324
+ <div class="chat-messages" id="setup-messages"></div>
325
+
326
+ <div class="typing-indicator" id="setup-typing">
327
+ <div class="typing-dot"></div>
328
+ <div class="typing-dot"></div>
329
+ <div class="typing-dot"></div>
330
+ </div>
331
+
332
+ <div class="chat-input-area">
333
+ <div class="input-row">
334
+ <div class="input-wrapper">
335
+ <textarea
336
+ class="chat-input"
337
+ id="setup-input"
338
+ placeholder="Type your message..."
339
+ rows="1"
340
+ disabled
341
+ ></textarea>
342
+ </div>
343
+ <button
344
+ class="btn btn-send"
345
+ id="setup-send"
346
+ onclick="sendSetupMessage()"
347
+ disabled
348
+ >
349
+ <svg
350
+ xmlns="http://www.w3.org/2000/svg"
351
+ width="16"
352
+ height="16"
353
+ viewBox="0 0 24 24"
354
+ fill="none"
355
+ stroke="currentColor"
356
+ stroke-width="2"
357
+ stroke-linecap="round"
358
+ stroke-linejoin="round"
359
+ >
360
+ <line x1="22" y1="2" x2="11" y2="13" />
361
+ <polygon points="22 2 15 22 11 13 2 9 22 2" />
362
+ </svg>
363
+ <span>Send</span>
364
+ </button>
365
+ </div>
366
+ </div>
367
+ </div>
368
+ </div>
369
+ </div>
370
+
371
+ <!-- Dashboard Tab -->
372
+ <div class="tab-content" id="tab-dashboard">
373
+ <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-4 md:p-6">
374
+ <!-- Header -->
375
+ <div class="mb-6">
376
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">MAMA OS Dashboard</h1>
377
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">System status and configuration overview</p>
378
+ </div>
379
+
380
+ <!-- Gateway Status Cards -->
381
+ <section class="mb-6">
382
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">Gateway Status</h2>
383
+ <div id="dashboard-gateways" class="grid grid-cols-2 md:grid-cols-4 gap-4">
384
+ <!-- Gateway cards will be rendered by JavaScript -->
385
+ <div class="animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-24"></div>
386
+ <div class="animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-24"></div>
387
+ <div class="animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-24"></div>
388
+ <div class="animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-24"></div>
389
+ </div>
390
+ </section>
391
+
392
+ <!-- Memory Stats -->
393
+ <section class="mb-6">
394
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">Memory Statistics</h2>
395
+ <div id="dashboard-memory" class="grid grid-cols-2 md:grid-cols-4 gap-4">
396
+ <!-- Memory stats will be rendered by JavaScript -->
397
+ <div class="animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-24"></div>
398
+ <div class="animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-24"></div>
399
+ <div class="animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-24"></div>
400
+ <div class="animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-24"></div>
401
+ </div>
402
+ </section>
403
+
404
+ <!-- Agent Configuration -->
405
+ <section class="mb-6">
406
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">Agent Configuration</h2>
407
+ <div id="dashboard-agent" class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
408
+ <!-- Agent config will be rendered by JavaScript -->
409
+ <div class="animate-pulse space-y-2">
410
+ <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2"></div>
411
+ <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/3"></div>
412
+ <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-2/3"></div>
413
+ </div>
414
+ </div>
415
+ </section>
416
+
417
+ <!-- Top Topics -->
418
+ <section class="mb-6">
419
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">Top Topics</h2>
420
+ <div id="dashboard-topics" class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
421
+ <!-- Topics will be rendered by JavaScript -->
422
+ <div class="animate-pulse space-y-2">
423
+ <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-3/4"></div>
424
+ <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2"></div>
425
+ <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-2/3"></div>
426
+ </div>
427
+ </div>
428
+ </section>
429
+
430
+ <!-- Status Bar -->
431
+ <div id="dashboard-status" class="text-sm text-gray-500 dark:text-gray-400 text-center py-2">
432
+ Loading...
433
+ </div>
434
+ </div>
435
+ </div>
436
+
437
+ <!-- Settings Tab -->
438
+ <div class="tab-content" id="tab-settings">
439
+ <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-4 md:p-6">
440
+ <!-- Header -->
441
+ <div class="mb-6">
442
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">Settings</h1>
443
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Configure MAMA gateways and agent settings</p>
444
+ </div>
445
+
446
+ <!-- Gateway Settings -->
447
+ <section class="mb-6">
448
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">Gateway Connections</h2>
449
+ <div class="grid gap-4">
450
+ <!-- Discord -->
451
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
452
+ <div class="flex items-center justify-between mb-3">
453
+ <div class="flex items-center gap-2">
454
+ <span class="text-xl">💬</span>
455
+ <h3 class="font-semibold text-gray-900 dark:text-gray-100">Discord</h3>
456
+ </div>
457
+ <label class="relative inline-flex items-center cursor-pointer">
458
+ <input type="checkbox" id="settings-discord-enabled" class="sr-only peer">
459
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-300 dark:peer-focus:ring-indigo-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600"></div>
460
+ </label>
461
+ </div>
462
+ <div class="space-y-3">
463
+ <div>
464
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Bot Token</label>
465
+ <input type="password" id="settings-discord-token" placeholder="Enter Discord bot token" class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
466
+ </div>
467
+ <div>
468
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Default Channel ID</label>
469
+ <input type="text" id="settings-discord-channel" placeholder="Channel ID for notifications" class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
470
+ </div>
471
+ </div>
472
+ </div>
473
+
474
+ <!-- Slack -->
475
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
476
+ <div class="flex items-center justify-between mb-3">
477
+ <div class="flex items-center gap-2">
478
+ <span class="text-xl">📱</span>
479
+ <h3 class="font-semibold text-gray-900 dark:text-gray-100">Slack</h3>
480
+ </div>
481
+ <label class="relative inline-flex items-center cursor-pointer">
482
+ <input type="checkbox" id="settings-slack-enabled" class="sr-only peer">
483
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-300 dark:peer-focus:ring-indigo-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600"></div>
484
+ </label>
485
+ </div>
486
+ <div class="space-y-3">
487
+ <div>
488
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Bot Token</label>
489
+ <input type="password" id="settings-slack-bot-token" placeholder="xoxb-..." class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
490
+ </div>
491
+ <div>
492
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">App Token</label>
493
+ <input type="password" id="settings-slack-app-token" placeholder="xapp-..." class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
494
+ </div>
495
+ </div>
496
+ </div>
497
+
498
+ <!-- Telegram -->
499
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
500
+ <div class="flex items-center justify-between mb-3">
501
+ <div class="flex items-center gap-2">
502
+ <span class="text-xl">✈️</span>
503
+ <h3 class="font-semibold text-gray-900 dark:text-gray-100">Telegram</h3>
504
+ </div>
505
+ <label class="relative inline-flex items-center cursor-pointer">
506
+ <input type="checkbox" id="settings-telegram-enabled" class="sr-only peer">
507
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-300 dark:peer-focus:ring-indigo-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600"></div>
508
+ </label>
509
+ </div>
510
+ <div>
511
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Bot Token</label>
512
+ <input type="password" id="settings-telegram-token" placeholder="123456:ABC-DEF..." class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
513
+ </div>
514
+ </div>
515
+
516
+ <!-- Chatwork -->
517
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
518
+ <div class="flex items-center justify-between mb-3">
519
+ <div class="flex items-center gap-2">
520
+ <span class="text-xl">💼</span>
521
+ <h3 class="font-semibold text-gray-900 dark:text-gray-100">Chatwork</h3>
522
+ </div>
523
+ <label class="relative inline-flex items-center cursor-pointer">
524
+ <input type="checkbox" id="settings-chatwork-enabled" class="sr-only peer">
525
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-300 dark:peer-focus:ring-indigo-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600"></div>
526
+ </label>
527
+ </div>
528
+ <div>
529
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">API Token</label>
530
+ <input type="password" id="settings-chatwork-token" placeholder="Enter Chatwork API token" class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
531
+ </div>
532
+ </div>
533
+ </div>
534
+ </section>
535
+
536
+ <!-- Heartbeat Settings -->
537
+ <section class="mb-6">
538
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">Heartbeat Scheduler</h2>
539
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
540
+ <div class="flex items-center justify-between mb-4">
541
+ <span class="text-sm text-gray-700 dark:text-gray-300">Enable Heartbeat</span>
542
+ <label class="relative inline-flex items-center cursor-pointer">
543
+ <input type="checkbox" id="settings-heartbeat-enabled" class="sr-only peer">
544
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-300 dark:peer-focus:ring-indigo-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600"></div>
545
+ </label>
546
+ </div>
547
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
548
+ <div>
549
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Interval (minutes)</label>
550
+ <input type="number" id="settings-heartbeat-interval" value="30" min="5" max="1440" class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
551
+ </div>
552
+ <div>
553
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Quiet Start (hour)</label>
554
+ <input type="number" id="settings-heartbeat-quiet-start" value="23" min="0" max="23" class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
555
+ </div>
556
+ <div>
557
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Quiet End (hour)</label>
558
+ <input type="number" id="settings-heartbeat-quiet-end" value="8" min="0" max="23" class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
559
+ </div>
560
+ </div>
561
+ </div>
562
+ </section>
563
+
564
+ <!-- Agent Settings -->
565
+ <section class="mb-6">
566
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">Agent Configuration</h2>
567
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
568
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
569
+ <div>
570
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Model</label>
571
+ <select id="settings-agent-model" class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
572
+ <option value="claude-sonnet-4-20250514">Claude Sonnet 4</option>
573
+ <option value="claude-3-5-sonnet-20241022">Claude 3.5 Sonnet</option>
574
+ <option value="claude-3-opus-20240229">Claude 3 Opus</option>
575
+ <option value="claude-3-haiku-20240307">Claude 3 Haiku</option>
576
+ </select>
577
+ </div>
578
+ <div>
579
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Max Turns</label>
580
+ <input type="number" id="settings-agent-max-turns" value="10" min="1" max="50" class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
581
+ </div>
582
+ <div>
583
+ <label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Timeout (seconds)</label>
584
+ <input type="number" id="settings-agent-timeout" value="300" min="30" max="600" class="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
585
+ </div>
586
+ </div>
587
+ </div>
588
+ </section>
589
+
590
+ <!-- Action Buttons -->
591
+ <div class="flex items-center justify-between pt-4 border-t border-gray-200 dark:border-gray-700">
592
+ <button onclick="settingsModule.resetForm()" class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-lg transition-colors">
593
+ Reset
594
+ </button>
595
+ <div class="flex items-center gap-3">
596
+ <span id="settings-status" class="text-sm text-gray-500 dark:text-gray-400"></span>
597
+ <button onclick="settingsModule.saveSettings()" class="px-6 py-2 text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg transition-colors flex items-center gap-2">
598
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
599
+ <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
600
+ <polyline points="17 21 17 13 7 13 7 21"/>
601
+ <polyline points="7 3 7 8 15 8"/>
602
+ </svg>
603
+ Save Settings
604
+ </button>
605
+ </div>
606
+ </div>
607
+ </div>
608
+ </div>
609
+
610
+ <div class="tab-content" id="tab-memory">
611
+ <div class="flex-1 flex min-h-0">
612
+ <aside class="w-72 border-r border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-900 flex flex-col min-h-0">
613
+ <div class="p-4 border-b border-gray-200 dark:border-gray-800">
614
+ <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
615
+ <svg
616
+ xmlns="http://www.w3.org/2000/svg"
617
+ width="16"
618
+ height="16"
619
+ viewBox="0 0 24 24"
620
+ fill="none"
621
+ stroke="currentColor"
622
+ stroke-width="2"
623
+ stroke-linecap="round"
624
+ stroke-linejoin="round"
625
+ >
626
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
627
+ <line x1="3" y1="9" x2="21" y2="9" />
628
+ <line x1="9" y1="21" x2="9" y2="9" />
629
+ </svg>
630
+ Checkpoints
631
+ </h3>
632
+ </div>
633
+ <div class="flex-1 flex flex-col gap-2 p-3 overflow-y-auto" id="checkpoint-list">
634
+ <div class="flex flex-col items-center justify-center py-8 text-gray-500">
635
+ <div class="w-6 h-6 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin"></div>
636
+ <span class="mt-2 text-sm">Loading...</span>
637
+ </div>
638
+ </div>
639
+ </aside>
640
+
641
+ <div class="flex-1 flex flex-col relative min-h-0" id="graph-container">
642
+ <!-- Analytics Bar -->
643
+ <div class="border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-950">
644
+ <div class="flex items-center justify-between px-4 py-2">
645
+ <div class="flex items-center gap-4">
646
+ <div id="memory-stats-total" class="text-center">
647
+ <p class="text-xl font-bold text-gray-900 dark:text-gray-100">-</p>
648
+ <p class="text-xs text-gray-500">Total</p>
649
+ </div>
650
+ <div class="h-8 w-px bg-gray-200 dark:bg-gray-700"></div>
651
+ <div id="memory-stats-week" class="text-center">
652
+ <p class="text-xl font-bold text-indigo-600 dark:text-indigo-400">-</p>
653
+ <p class="text-xs text-gray-500">This Week</p>
654
+ </div>
655
+ <div class="h-8 w-px bg-gray-200 dark:bg-gray-700"></div>
656
+ <div id="memory-stats-outcomes" class="flex items-center gap-2">
657
+ <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded-full bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400">
658
+ <span id="outcome-success">0</span> success
659
+ </span>
660
+ <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded-full bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400">
661
+ <span id="outcome-failed">0</span> failed
662
+ </span>
663
+ <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded-full bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400">
664
+ <span id="outcome-partial">0</span> partial
665
+ </span>
666
+ </div>
667
+ </div>
668
+ <div class="flex items-center gap-2">
669
+ <button onclick="exportDecisions('json')" class="px-3 py-1.5 text-xs font-medium bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg transition-colors">
670
+ Export JSON
671
+ </button>
672
+ <button onclick="exportDecisions('markdown')" class="px-3 py-1.5 text-xs font-medium bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg transition-colors">
673
+ Export MD
674
+ </button>
675
+ <button onclick="exportDecisions('csv')" class="px-3 py-1.5 text-xs font-medium bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg transition-colors">
676
+ Export CSV
677
+ </button>
678
+ </div>
679
+ </div>
680
+ </div>
681
+
682
+ <!-- Filters Bar -->
683
+ <div class="flex items-center gap-3 px-4 py-3 border-b border-gray-200 dark:border-gray-800 bg-gray-50/50 dark:bg-gray-900/50">
684
+ <select
685
+ class="px-3 py-1.5 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg text-sm text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 min-w-[140px]"
686
+ id="topic-filter"
687
+ onchange="filterByTopic(this.value)"
688
+ >
689
+ <option value="">All Topics</option>
690
+ </select>
691
+ <select
692
+ class="px-3 py-1.5 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg text-sm text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 min-w-[120px]"
693
+ id="outcome-filter"
694
+ onchange="filterByOutcome(this.value)"
695
+ >
696
+ <option value="">All Outcomes</option>
697
+ <option value="success">Success</option>
698
+ <option value="failed">Failed</option>
699
+ <option value="partial">Partial</option>
700
+ <option value="pending">Pending</option>
701
+ </select>
702
+ <input
703
+ type="text"
704
+ class="px-3 py-1.5 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg text-sm text-gray-900 dark:text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 w-[180px]"
705
+ id="search-input"
706
+ placeholder="Search decisions..."
707
+ onkeyup="handleGraphSearch(event)"
708
+ />
709
+ <button onclick="clearFilters()" class="px-3 py-1.5 text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
710
+ Clear
711
+ </button>
712
+ </div>
713
+
714
+ <div class="px-4 py-2 text-xs text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-950" id="graph-stats">Loading...</div>
715
+
716
+ <div id="graph-canvas" class="flex-1 w-full h-full min-h-[400px] bg-white dark:bg-gray-950"></div>
717
+
718
+ <div class="absolute inset-0 flex flex-col items-center justify-center bg-white dark:bg-gray-950" id="graph-loading">
719
+ <div class="w-8 h-8 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin"></div>
720
+ <span class="mt-3 text-sm text-gray-500 dark:text-gray-400">Loading graph data...</span>
721
+ </div>
722
+ </div>
723
+
724
+ <div class="legend-panel absolute bottom-16 right-4 w-48 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-lg shadow-lg p-3 z-10" id="legend-panel">
725
+ <button class="absolute top-2 right-2 p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors" onclick="toggleLegend()">
726
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
727
+ <line x1="18" y1="6" x2="6" y2="18" />
728
+ <line x1="6" y1="6" x2="18" y2="18" />
729
+ </svg>
730
+ </button>
731
+ <h4 class="text-xs font-semibold text-gray-700 dark:text-gray-300 flex items-center gap-1.5 mb-3">
732
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
733
+ <circle cx="12" cy="12" r="10" />
734
+ <line x1="12" y1="16" x2="12" y2="12" />
735
+ <line x1="12" y1="8" x2="12.01" y2="8" />
736
+ </svg>
737
+ Legend
738
+ </h4>
739
+ <div class="legend-content space-y-3">
740
+ <div>
741
+ <div class="text-[10px] font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1.5">Edge Types</div>
742
+ <div class="flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400 mb-1">
743
+ <span class="w-6 h-0.5 bg-gray-500"></span>
744
+ <span>supersedes</span>
745
+ </div>
746
+ <div class="flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400 mb-1">
747
+ <span class="w-6 h-0.5 bg-blue-500 border-dashed"></span>
748
+ <span>builds_on</span>
749
+ </div>
750
+ <div class="flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400 mb-1">
751
+ <span class="w-6 h-0.5 bg-red-500 border-dashed"></span>
752
+ <span>debates</span>
753
+ </div>
754
+ <div class="flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400">
755
+ <span class="w-6 h-1 bg-purple-500"></span>
756
+ <span>synthesizes</span>
757
+ </div>
758
+ </div>
759
+ <div>
760
+ <div class="text-[10px] font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1.5">Node Size</div>
761
+ <div class="flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400 mb-1">
762
+ <span class="w-2 h-2 rounded-full bg-indigo-500"></span>
763
+ <span>1-2 connections</span>
764
+ </div>
765
+ <div class="flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400 mb-1">
766
+ <span class="w-3 h-3 rounded-full bg-indigo-500"></span>
767
+ <span>3-5 connections</span>
768
+ </div>
769
+ <div class="flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400">
770
+ <span class="w-4 h-4 rounded-full bg-indigo-500"></span>
771
+ <span>6+ connections</span>
772
+ </div>
773
+ </div>
774
+ </div>
775
+ </div>
776
+ <button class="legend-toggle absolute bottom-4 right-4 px-3 py-1.5 text-xs font-medium bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg transition-colors flex items-center gap-1.5 z-10" onclick="toggleLegend()">
777
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
778
+ <circle cx="12" cy="12" r="10" />
779
+ <line x1="12" y1="16" x2="12" y2="12" />
780
+ <line x1="12" y1="8" x2="12.01" y2="8" />
781
+ </svg>
782
+ Legend
783
+ </button>
784
+ </div>
785
+ </div>
786
+
787
+ <div class="floating-panel fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[90%] max-w-[600px] max-h-[80vh] bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-xl shadow-2xl z-[1000] flex-col overflow-hidden" id="decision-detail-modal">
788
+ <div class="flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-900/50">
789
+ <h3 class="text-base font-semibold text-gray-900 dark:text-gray-100" id="detail-topic">Decision Detail</h3>
790
+ <button class="p-1.5 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-gray-500 hover:text-gray-700 dark:hover:text-gray-300" onclick="closeDetailModal()">
791
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
792
+ </button>
793
+ </div>
794
+ <div class="flex-1 p-4 overflow-y-auto space-y-4">
795
+ <div>
796
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">Decision</div>
797
+ <div class="text-sm text-gray-900 dark:text-gray-100 leading-relaxed markdown-content" id="detail-decision"></div>
798
+ </div>
799
+ <div>
800
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2 cursor-pointer" onclick="toggleReasoning()">
801
+ <span id="reasoning-arrow">▶</span> Reasoning
802
+ </div>
803
+ <div class="text-sm text-gray-900 dark:text-gray-100 leading-relaxed markdown-content hidden" id="detail-reasoning"></div>
804
+ </div>
805
+ <div>
806
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">Outcome</div>
807
+ <div class="flex items-center gap-2">
808
+ <select class="px-2 py-1 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded text-sm text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-500" id="detail-outcome-select">
809
+ <option value="PENDING">PENDING</option>
810
+ <option value="SUCCESS">SUCCESS</option>
811
+ <option value="FAILED">FAILED</option>
812
+ <option value="PARTIAL">PARTIAL</option>
813
+ </select>
814
+ <button class="px-2 py-1 text-sm bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 rounded transition-colors" onclick="saveOutcome()">Save</button>
815
+ <span id="outcome-status"></span>
816
+ </div>
817
+ </div>
818
+ <div>
819
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">Confidence</div>
820
+ <div class="text-sm text-gray-900 dark:text-gray-100" id="detail-confidence"></div>
821
+ </div>
822
+ <div>
823
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">Created</div>
824
+ <div class="text-sm text-gray-900 dark:text-gray-100" id="detail-created"></div>
825
+ </div>
826
+ <div>
827
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">Similar Decisions</div>
828
+ <div class="text-sm text-gray-900 dark:text-gray-100" id="detail-similar">
829
+ <span class="text-gray-400">Searching...</span>
830
+ </div>
831
+ </div>
832
+ </div>
833
+ </div>
834
+
835
+ <div class="tab-content active" id="tab-chat">
836
+ <div class="flex-1 flex flex-col w-full max-w-6xl mx-auto p-4 h-full">
837
+ <div class="flex-1 flex flex-col bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-xl overflow-hidden shadow-sm min-h-0" id="general-chat">
838
+ <div class="flex-1 overflow-y-auto p-4 flex flex-col gap-4 min-h-0" id="chat-messages">
839
+ <div class="flex-1 flex flex-col items-center justify-center text-center p-12 text-gray-500">
840
+ <svg
841
+ xmlns="http://www.w3.org/2000/svg"
842
+ width="48"
843
+ height="48"
844
+ viewBox="0 0 24 24"
845
+ fill="none"
846
+ stroke="currentColor"
847
+ stroke-width="1.5"
848
+ stroke-linecap="round"
849
+ stroke-linejoin="round"
850
+ class="mb-4 opacity-50"
851
+ >
852
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
853
+ </svg>
854
+ <h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2">Start a conversation</h3>
855
+ <p class="text-sm text-gray-500 dark:text-gray-400 max-w-sm leading-relaxed">
856
+ Connect to a Claude session to start chatting. Your conversation will be saved
857
+ and can be resumed later.
858
+ </p>
859
+ </div>
860
+ </div>
861
+
862
+ <div class="border-t border-gray-200 dark:border-gray-700 p-4 space-y-3">
863
+ <div class="flex gap-2">
864
+ <textarea
865
+ class="flex-1 px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder-gray-500 resize-none focus:outline-none focus:ring-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed"
866
+ id="chat-input"
867
+ placeholder="Type your message..."
868
+ rows="2"
869
+ disabled
870
+ ></textarea>
871
+ <button
872
+ class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 disabled:bg-gray-400 text-white rounded-lg font-medium transition-colors disabled:cursor-not-allowed flex items-center gap-2"
873
+ id="chat-send"
874
+ onclick="sendChatMessage()"
875
+ disabled
876
+ >
877
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
878
+ <line x1="22" y1="2" x2="11" y2="13" stroke-width="2" stroke-linecap="round"/>
879
+ <polygon points="22 2 15 22 11 13 2 9 22 2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
880
+ </svg>
881
+ <span>Send</span>
882
+ </button>
883
+ </div>
884
+
885
+ <div class="flex items-center gap-2">
886
+ <button
887
+ class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
888
+ id="chat-tts-toggle"
889
+ onclick="toggleTTS()"
890
+ title="TTS Off"
891
+ >
892
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
893
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
894
+ <path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
895
+ </svg>
896
+ </button>
897
+ <div class="relative">
898
+ <span class="text-xs text-gray-600 dark:text-gray-400 cursor-pointer px-2 py-1 hover:bg-gray-100 dark:hover:bg-gray-800 rounded" id="tts-speed-label" onclick="toggleTTSPopup()">
899
+ 1.8x
900
+ </span>
901
+ <div class="absolute bottom-full mb-2 left-1/2 -translate-x-1/2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-3 shadow-lg hidden" id="tts-speed-popup">
902
+ <div class="text-center text-sm font-semibold mb-2" id="tts-speed-value">1.8x</div>
903
+ <input
904
+ type="range"
905
+ class="w-full accent-indigo-600"
906
+ id="tts-speed-slider"
907
+ min="0.5"
908
+ max="2.0"
909
+ step="0.1"
910
+ value="1.8"
911
+ oninput="updateTTSSpeed(this.value)"
912
+ />
913
+ </div>
914
+ </div>
915
+ <button
916
+ class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
917
+ id="chat-handsfree-toggle"
918
+ onclick="toggleHandsFree()"
919
+ title="Hands-free Off"
920
+ >
921
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
922
+ <circle cx="12" cy="12" r="2" stroke-width="2"/>
923
+ <path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
924
+ </svg>
925
+ </button>
926
+ <button
927
+ class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors relative"
928
+ id="chat-mic"
929
+ onclick="toggleVoiceInput()"
930
+ title="Voice input"
931
+ >
932
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
933
+ <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
934
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
935
+ <line x1="12" y1="19" x2="12" y2="22" stroke-width="2" stroke-linecap="round"/>
936
+ </svg>
937
+ <span class="recording-dot absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full hidden"></span>
938
+ </button>
939
+ </div>
940
+
941
+ <div class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400" id="chat-status">
942
+ <span class="status-indicator w-2 h-2 rounded-full bg-red-500"></span>
943
+ <span>Not connected</span>
944
+ <button
945
+ class="ml-2 p-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
946
+ onclick="showTunnelSetup()"
947
+ title="External Access"
948
+ >
949
+ <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
950
+ <circle cx="12" cy="12" r="10" stroke-width="2"/>
951
+ <line x1="2" y1="12" x2="22" y2="12" stroke-width="2"/>
952
+ <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" stroke-width="2"/>
953
+ </svg>
954
+ </button>
955
+ </div>
956
+ </div>
957
+ </div>
958
+ </div>
959
+ </div>
960
+ </div>
961
+ </main>
962
+
963
+ <div class="modal-overlay fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center" id="save-decision-modal">
964
+ <div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-xl shadow-2xl w-[90%] max-w-md max-h-[90vh] flex flex-col overflow-hidden">
965
+ <div class="flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-900/50">
966
+ <h3 class="flex items-center gap-2 text-base font-semibold text-gray-900 dark:text-gray-100">
967
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
968
+ <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
969
+ <polyline points="17 21 17 13 7 13 7 21" />
970
+ <polyline points="7 3 7 8 15 8" />
971
+ </svg>
972
+ Save Decision
973
+ </h3>
974
+ <button class="p-1.5 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-gray-500 hover:text-gray-700 dark:hover:text-gray-300" onclick="hideSaveDecisionForm()">
975
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
976
+ <line x1="18" y1="6" x2="6" y2="18" />
977
+ <line x1="6" y1="6" x2="18" y2="18" />
978
+ </svg>
979
+ </button>
980
+ </div>
981
+ <div class="p-4 space-y-4">
982
+ <div class="flex flex-col gap-2">
983
+ <label class="text-sm font-medium text-gray-900 dark:text-gray-100" for="save-topic">Topic</label>
984
+ <input
985
+ type="text"
986
+ class="w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg text-sm text-gray-900 dark:text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors"
987
+ id="save-topic"
988
+ placeholder="e.g., auth_strategy"
989
+ required
990
+ />
991
+ </div>
992
+ <div class="flex flex-col gap-2">
993
+ <label class="text-sm font-medium text-gray-900 dark:text-gray-100" for="save-decision">Decision</label>
994
+ <textarea
995
+ class="w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg text-sm text-gray-900 dark:text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors resize-y min-h-[60px]"
996
+ id="save-decision"
997
+ placeholder="What did you decide?"
998
+ rows="2"
999
+ required
1000
+ ></textarea>
1001
+ </div>
1002
+ <div class="flex flex-col gap-2">
1003
+ <label class="text-sm font-medium text-gray-900 dark:text-gray-100" for="save-reasoning">Reasoning</label>
1004
+ <textarea
1005
+ class="w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg text-sm text-gray-900 dark:text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors resize-y min-h-[100px]"
1006
+ id="save-reasoning"
1007
+ placeholder="Why did you make this decision?"
1008
+ rows="4"
1009
+ required
1010
+ ></textarea>
1011
+ </div>
1012
+ <div class="flex flex-col gap-2">
1013
+ <label class="text-sm font-medium text-gray-900 dark:text-gray-100" for="save-confidence">Confidence (0.0 - 1.0)</label>
1014
+ <input
1015
+ type="number"
1016
+ class="w-28 px-3 py-2 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg text-sm text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors"
1017
+ id="save-confidence"
1018
+ min="0"
1019
+ max="1"
1020
+ step="0.1"
1021
+ value="0.8"
1022
+ />
1023
+ </div>
1024
+ </div>
1025
+ <div class="flex justify-end gap-2 px-4 py-3 border-t border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-900/50">
1026
+ <button class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition-colors" onclick="hideSaveDecisionForm()">Cancel</button>
1027
+ <button class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg transition-colors" onclick="submitSaveDecision()">Save</button>
1028
+ </div>
1029
+ <div id="save-form-status" class="px-5 pb-4 text-center text-sm"></div>
1030
+ </div>
1031
+ </div>
1032
+
1033
+ <div class="modal-overlay fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center" id="tunnel-setup-modal">
1034
+ <div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-xl shadow-2xl w-[90%] max-w-[600px] max-h-[90vh] flex flex-col overflow-hidden">
1035
+ <div class="flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-900/50">
1036
+ <h3 class="flex items-center gap-2 text-base font-semibold text-gray-900 dark:text-gray-100">
1037
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1038
+ <circle cx="12" cy="12" r="10" />
1039
+ <line x1="2" y1="12" x2="22" y2="12" />
1040
+ <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
1041
+ </svg>
1042
+ External Access Setup
1043
+ </h3>
1044
+ <button class="p-1.5 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-gray-500 hover:text-gray-700 dark:hover:text-gray-300" onclick="hideTunnelSetup()">
1045
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1046
+ <line x1="18" y1="6" x2="6" y2="18" />
1047
+ <line x1="6" y1="6" x2="18" y2="18" />
1048
+ </svg>
1049
+ </button>
1050
+ </div>
1051
+ <div class="flex-1 p-4 overflow-y-auto space-y-4">
1052
+ <!-- ngrok section -->
1053
+ <div class="bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
1054
+ <h4 class="text-sm text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5">
1055
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1056
+ <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
1057
+ </svg>
1058
+ ngrok (Quick Setup)
1059
+ </h4>
1060
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">Tunnel your local server to a public URL</p>
1061
+ <div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded px-3 py-2 flex items-center justify-between gap-2 mb-2">
1062
+ <code id="ngrok-command" class="font-mono text-sm text-green-600 dark:text-green-400">ngrok http 3847</code>
1063
+ <button class="p-1.5 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors" onclick="copyNgrokCommand()">
1064
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1065
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
1066
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
1067
+ </svg>
1068
+ </button>
1069
+ </div>
1070
+ <a href="https://ngrok.com/download" target="_blank" rel="noopener noreferrer" class="text-xs text-indigo-600 dark:text-indigo-400 hover:underline flex items-center gap-1">
1071
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1072
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
1073
+ <polyline points="7 10 12 15 17 10" />
1074
+ <line x1="12" y1="15" x2="12" y2="3" />
1075
+ </svg>
1076
+ Download ngrok
1077
+ </a>
1078
+ </div>
1079
+
1080
+ <!-- Cloudflare section -->
1081
+ <div class="bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
1082
+ <h4 class="text-sm text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5">
1083
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1084
+ <path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" />
1085
+ </svg>
1086
+ Cloudflare Tunnel (Production)
1087
+ </h4>
1088
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Secure tunnel with custom domain support</p>
1089
+ <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/" target="_blank" rel="noopener noreferrer" class="text-xs text-indigo-600 dark:text-indigo-400 hover:underline flex items-center gap-1">
1090
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1091
+ <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
1092
+ <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
1093
+ </svg>
1094
+ Setup Guide
1095
+ </a>
1096
+ </div>
1097
+
1098
+ <!-- Security warning -->
1099
+ <div class="bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-700/50 rounded-lg p-3 flex gap-3">
1100
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-amber-500 flex-shrink-0 mt-0.5">
1101
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
1102
+ <line x1="12" y1="9" x2="12" y2="13" />
1103
+ <line x1="12" y1="17" x2="12.01" y2="17" />
1104
+ </svg>
1105
+ <div class="text-xs">
1106
+ <strong class="text-amber-600 dark:text-amber-400 block mb-1">Security Notice</strong>
1107
+ <span class="text-gray-700 dark:text-gray-300">Always set MAMA_AUTH_TOKEN environment variable before exposing your server externally.</span>
1108
+ <pre class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded px-2 py-1.5 mt-2 text-[11px] text-green-600 dark:text-green-400 overflow-x-auto">export MAMA_AUTH_TOKEN="your-secure-token-here"</pre>
1109
+ </div>
1110
+ </div>
1111
+
1112
+ <!-- Test connection -->
1113
+ <div class="border-t border-gray-200 dark:border-gray-800 pt-4">
1114
+ <button class="w-full px-4 py-2 text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg transition-colors flex items-center justify-center gap-2" onclick="testConnection()">
1115
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1116
+ <path d="M5 12.55a11 11 0 0 1 14.08 0" />
1117
+ <path d="M1.42 9a16 16 0 0 1 21.16 0" />
1118
+ <path d="M8.53 16.11a6 6 0 0 1 6.95 0" />
1119
+ <line x1="12" y1="20" x2="12.01" y2="20" />
1120
+ </svg>
1121
+ Test Connection
1122
+ </button>
1123
+ <div id="tunnel-test-result" class="mt-3 min-h-[40px]"></div>
1124
+ </div>
1125
+ </div>
1126
+ </div>
1127
+ </div>
1128
+
1129
+ <div class="toast" id="toast">
1130
+ <span id="toast-message"></span>
1131
+ </div>
1132
+ </div>
1133
+
1134
+ <script type="module">
1135
+ import { ChatModule } from '/viewer/js/modules/chat.js';
1136
+ import { GraphModule } from '/viewer/js/modules/graph.js';
1137
+ import { MemoryModule } from '/viewer/js/modules/memory.js';
1138
+ import { DashboardModule } from '/viewer/js/modules/dashboard.js';
1139
+ import { SettingsModule } from '/viewer/js/modules/settings.js';
1140
+
1141
+ const memory = new MemoryModule();
1142
+ const chat = new ChatModule(memory);
1143
+ const graph = new GraphModule();
1144
+ const dashboard = new DashboardModule();
1145
+ const settings = new SettingsModule();
1146
+
1147
+ // Expose modules globally for debugging and onclick handlers
1148
+ window.chatModule = chat;
1149
+ window.graphModule = graph;
1150
+ window.memoryModule = memory;
1151
+ window.dashboardModule = dashboard;
1152
+ window.settingsModule = settings;
1153
+
1154
+ // Quiz choice button handler
1155
+ window.sendQuizChoice = (choice) => {
1156
+ chat.sendQuizChoice(choice);
1157
+ };
1158
+
1159
+ const STATE = {
1160
+ currentTab: 'chat',
1161
+ theme: 'dark',
1162
+ graphLoaded: false
1163
+ };
1164
+
1165
+ async function switchTab(tabName) {
1166
+ STATE.currentTab = tabName;
1167
+
1168
+ document.querySelectorAll('[data-tab]').forEach(btn => {
1169
+ const isActive = btn.dataset.tab === tabName;
1170
+ btn.classList.toggle('bg-indigo-100', isActive);
1171
+ btn.classList.toggle('dark:bg-indigo-900/30', isActive);
1172
+ btn.classList.toggle('text-indigo-600', isActive);
1173
+ btn.classList.toggle('dark:text-indigo-400', isActive);
1174
+ });
1175
+
1176
+ document.querySelectorAll('.tab-content').forEach(content => {
1177
+ content.classList.toggle('active', content.id === `tab-${tabName}`);
1178
+ });
1179
+
1180
+ if (tabName === 'chat') {
1181
+ chat.initSession();
1182
+ } else if (tabName === 'dashboard') {
1183
+ dashboard.init();
1184
+ } else if (tabName === 'settings') {
1185
+ settings.init();
1186
+ } else if (tabName === 'memory') {
1187
+ // Load checkpoints
1188
+ loadCheckpoints();
1189
+
1190
+ // Load memory stats for analytics bar
1191
+ updateMemoryStats();
1192
+
1193
+ // Load graph
1194
+ if (!STATE.graphLoaded) {
1195
+ try {
1196
+ const data = await graph.fetchData();
1197
+ graph.init(data);
1198
+ STATE.graphLoaded = true;
1199
+ const loadingEl = document.getElementById('graph-loading');
1200
+ if (loadingEl) loadingEl.style.display = 'none';
1201
+ } catch (error) {
1202
+ console.error('[MAMA] Failed to load graph:', error);
1203
+ const loadingEl = document.getElementById('graph-loading');
1204
+ if (loadingEl) loadingEl.innerHTML = '<span class="text-red-500">Failed to load graph data</span>';
1205
+ }
1206
+ }
1207
+ }
1208
+ }
1209
+
1210
+ function toggleTheme() {
1211
+ const html = document.documentElement;
1212
+ const isDark = html.classList.toggle('dark');
1213
+ STATE.theme = isDark ? 'dark' : 'light';
1214
+
1215
+ const lightIcon = document.getElementById('theme-icon-light');
1216
+ const darkIcon = document.getElementById('theme-icon-dark');
1217
+
1218
+ if (isDark) {
1219
+ lightIcon.style.display = 'none';
1220
+ darkIcon.style.display = 'block';
1221
+ } else {
1222
+ lightIcon.style.display = 'block';
1223
+ darkIcon.style.display = 'none';
1224
+ }
1225
+
1226
+ localStorage.setItem('mama_theme', STATE.theme);
1227
+ }
1228
+
1229
+ function initTheme() {
1230
+ const saved = localStorage.getItem('mama_theme');
1231
+ const html = document.documentElement;
1232
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
1233
+
1234
+ const lightIcon = document.getElementById('theme-icon-light');
1235
+ const darkIcon = document.getElementById('theme-icon-dark');
1236
+
1237
+ if (saved === 'dark' || (!saved && prefersDark)) {
1238
+ html.classList.add('dark');
1239
+ STATE.theme = 'dark';
1240
+ lightIcon.style.display = 'none';
1241
+ darkIcon.style.display = 'block';
1242
+ } else {
1243
+ html.classList.remove('dark');
1244
+ STATE.theme = 'light';
1245
+ lightIcon.style.display = 'block';
1246
+ darkIcon.style.display = 'none';
1247
+ }
1248
+ }
1249
+
1250
+ function toggleLegend() {
1251
+ const panel = document.getElementById('legend-panel');
1252
+ panel.classList.toggle('visible');
1253
+ }
1254
+
1255
+ function closeDetailModal() {
1256
+ const modal = document.getElementById('decision-detail-modal');
1257
+ modal.classList.remove('visible');
1258
+ }
1259
+
1260
+ function toggleReasoning() {
1261
+ const reasoning = document.getElementById('detail-reasoning');
1262
+ const arrow = document.getElementById('reasoning-arrow');
1263
+ if (reasoning.classList.contains('hidden')) {
1264
+ reasoning.classList.remove('hidden');
1265
+ arrow.textContent = '▼';
1266
+ } else {
1267
+ reasoning.classList.add('hidden');
1268
+ arrow.textContent = '▶';
1269
+ }
1270
+ }
1271
+
1272
+ function showTunnelSetup() {
1273
+ const modal = document.getElementById('tunnel-setup-modal');
1274
+ modal.classList.add('visible');
1275
+ }
1276
+
1277
+ function hideTunnelSetup() {
1278
+ const modal = document.getElementById('tunnel-setup-modal');
1279
+ modal.classList.remove('visible');
1280
+ }
1281
+
1282
+ function showSaveDecisionForm() {
1283
+ memory.showSaveForm();
1284
+ }
1285
+
1286
+ function hideSaveDecisionForm() {
1287
+ memory.hideSaveForm();
1288
+ }
1289
+
1290
+ function submitSaveDecision() {
1291
+ memory.submitSaveForm();
1292
+ }
1293
+
1294
+ function copyNgrokCommand() {
1295
+ const cmd = document.getElementById('ngrok-command').textContent;
1296
+ navigator.clipboard.writeText(cmd);
1297
+ alert('Copied: ' + cmd);
1298
+ }
1299
+
1300
+ function testConnection() {
1301
+ const result = document.getElementById('tunnel-test-result');
1302
+ result.innerHTML = '<span class="text-gray-500">Testing...</span>';
1303
+ fetch('/health')
1304
+ .then(r => r.ok ? result.innerHTML = '<span class="text-green-500">Connection OK</span>'
1305
+ : result.innerHTML = '<span class="text-red-500">Connection failed</span>')
1306
+ .catch(() => result.innerHTML = '<span class="text-red-500">Connection failed</span>');
1307
+ }
1308
+
1309
+ function sendChatMessage() {
1310
+ chat.send();
1311
+ }
1312
+
1313
+ // Send quiz choice as message
1314
+ window.sendQuizChoice = function(choice) {
1315
+ if (chat && chat.ws && chat.ws.readyState === WebSocket.OPEN) {
1316
+ // Set the input to the choice and send
1317
+ const input = document.getElementById('chat-input');
1318
+ if (input) {
1319
+ input.value = choice;
1320
+ chat.send();
1321
+ }
1322
+ }
1323
+ };
1324
+
1325
+ function toggleTTS() {
1326
+ chat.toggleTTS && chat.toggleTTS();
1327
+ }
1328
+
1329
+ function toggleTTSPopup() {
1330
+ const popup = document.getElementById('tts-speed-popup');
1331
+ popup.classList.toggle('hidden');
1332
+ }
1333
+
1334
+ function updateTTSSpeed(value) {
1335
+ document.getElementById('tts-speed-label').textContent = value + 'x';
1336
+ document.getElementById('tts-speed-value').textContent = value + 'x';
1337
+ chat.setTTSSpeed && chat.setTTSSpeed(parseFloat(value));
1338
+ }
1339
+
1340
+ function toggleHandsFree() {
1341
+ chat.toggleHandsFree && chat.toggleHandsFree();
1342
+ }
1343
+
1344
+ function toggleVoiceInput() {
1345
+ chat.toggleVoiceInput && chat.toggleVoiceInput();
1346
+ }
1347
+
1348
+ function filterByTopic(topic) {
1349
+ graph.filterByTopic && graph.filterByTopic(topic);
1350
+ }
1351
+
1352
+ function filterByOutcome(outcome) {
1353
+ graph.filterByOutcome && graph.filterByOutcome(outcome);
1354
+ }
1355
+
1356
+ function clearFilters() {
1357
+ document.getElementById('topic-filter').value = '';
1358
+ document.getElementById('outcome-filter').value = '';
1359
+ document.getElementById('search-input').value = '';
1360
+ graph.clearFilters && graph.clearFilters();
1361
+ }
1362
+
1363
+ async function exportDecisions(format) {
1364
+ try {
1365
+ const response = await fetch(`/api/memory/export?format=${format}`);
1366
+ if (!response.ok) throw new Error('Export failed');
1367
+
1368
+ const blob = await response.blob();
1369
+ const filename = response.headers.get('Content-Disposition')?.match(/filename="(.+)"/)?.[1]
1370
+ || `mama-export.${format}`;
1371
+
1372
+ const url = URL.createObjectURL(blob);
1373
+ const a = document.createElement('a');
1374
+ a.href = url;
1375
+ a.download = filename;
1376
+ document.body.appendChild(a);
1377
+ a.click();
1378
+ document.body.removeChild(a);
1379
+ URL.revokeObjectURL(url);
1380
+ } catch (error) {
1381
+ console.error('[Export] Error:', error);
1382
+ alert('Export failed: ' + error.message);
1383
+ }
1384
+ }
1385
+
1386
+ async function updateMemoryStats() {
1387
+ try {
1388
+ const response = await fetch('/api/dashboard/status');
1389
+ if (!response.ok) return;
1390
+
1391
+ const data = await response.json();
1392
+ const memory = data.memory || {};
1393
+
1394
+ document.querySelector('#memory-stats-total p').textContent = memory.total || 0;
1395
+ document.querySelector('#memory-stats-week p').textContent = memory.thisWeek || 0;
1396
+ document.getElementById('outcome-success').textContent = memory.outcomes?.success || 0;
1397
+ document.getElementById('outcome-failed').textContent = memory.outcomes?.failed || 0;
1398
+ document.getElementById('outcome-partial').textContent = memory.outcomes?.partial || 0;
1399
+ } catch (error) {
1400
+ console.error('[MemoryStats] Error:', error);
1401
+ }
1402
+ }
1403
+
1404
+ function handleGraphSearch(event) {
1405
+ if (event.key === 'Enter') {
1406
+ graph.search && graph.search();
1407
+ }
1408
+ }
1409
+
1410
+ function saveOutcome() {
1411
+ const select = document.getElementById('detail-outcome-select');
1412
+ const outcome = select.value;
1413
+ const nodeId = graph.currentNodeId;
1414
+ if (nodeId && outcome) {
1415
+ graph.updateOutcome && graph.updateOutcome(nodeId, outcome);
1416
+ }
1417
+ }
1418
+
1419
+ let checkpointsData = [];
1420
+
1421
+ function renderMarkdown(text) {
1422
+ if (!text) return '';
1423
+ try {
1424
+ if (typeof marked !== 'undefined' && marked.parse) {
1425
+ return marked.parse(text);
1426
+ }
1427
+ } catch (e) {
1428
+ console.error('[MAMA] Markdown parse error:', e);
1429
+ }
1430
+ return text.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\n/g, '<br>');
1431
+ }
1432
+
1433
+ async function loadCheckpoints() {
1434
+ const container = document.getElementById('checkpoint-list');
1435
+ if (!container) {
1436
+ console.error('[MAMA] checkpoint-list container not found');
1437
+ return;
1438
+ }
1439
+
1440
+ console.log('[MAMA] Loading checkpoints...');
1441
+
1442
+ try {
1443
+ const response = await fetch('/api/checkpoints');
1444
+ const data = await response.json();
1445
+ checkpointsData = data.checkpoints || [];
1446
+
1447
+ console.log('[MAMA] Loaded', checkpointsData.length, 'checkpoints');
1448
+
1449
+ if (checkpointsData.length > 0) {
1450
+ container.innerHTML = checkpointsData.map((cp, idx) => {
1451
+ const fullSummary = renderMarkdown(cp.summary || '');
1452
+ return `
1453
+ <div class="checkpoint-item p-3 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg cursor-pointer hover:border-indigo-500 transition-colors" onclick="toggleCheckpoint(${idx})">
1454
+ <div class="text-xs font-semibold text-indigo-600 dark:text-indigo-400 mb-1">${new Date(cp.timestamp).toLocaleString()}</div>
1455
+ <div class="checkpoint-summary text-sm text-gray-700 dark:text-gray-300 markdown-content line-clamp-3 overflow-hidden">${fullSummary}</div>
1456
+ <div class="checkpoint-details hidden mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
1457
+ <div class="text-sm text-gray-700 dark:text-gray-300 markdown-content max-h-64 overflow-y-auto">${fullSummary}</div>
1458
+ </div>
1459
+ </div>
1460
+ `}).join('');
1461
+ } else {
1462
+ container.innerHTML = '<div class="text-center text-sm text-gray-500 py-4">No checkpoints found</div>';
1463
+ }
1464
+ } catch (error) {
1465
+ console.error('[MAMA] Failed to load checkpoints:', error);
1466
+ container.innerHTML = '<div class="text-center text-sm text-red-500 py-4">Failed to load checkpoints</div>';
1467
+ }
1468
+ }
1469
+
1470
+ function toggleCheckpoint(idx) {
1471
+ const items = document.querySelectorAll('.checkpoint-item');
1472
+ items.forEach((item, i) => {
1473
+ const details = item.querySelector('.checkpoint-details');
1474
+ const summary = item.querySelector('.checkpoint-summary');
1475
+ if (i === idx) {
1476
+ const isExpanded = !details.classList.contains('hidden');
1477
+ details.classList.toggle('hidden');
1478
+ summary.classList.toggle('hidden', !isExpanded);
1479
+ item.classList.toggle('border-indigo-500', !isExpanded);
1480
+ } else {
1481
+ details.classList.add('hidden');
1482
+ summary.classList.remove('hidden');
1483
+ item.classList.remove('border-indigo-500');
1484
+ }
1485
+ });
1486
+ }
1487
+
1488
+ // Expose to window for onclick handlers
1489
+ window.switchTab = switchTab;
1490
+ window.toggleTheme = toggleTheme;
1491
+ window.toggleLegend = toggleLegend;
1492
+ window.closeDetailModal = closeDetailModal;
1493
+ window.toggleReasoning = toggleReasoning;
1494
+ window.showTunnelSetup = showTunnelSetup;
1495
+ window.hideTunnelSetup = hideTunnelSetup;
1496
+ window.showSaveDecisionForm = showSaveDecisionForm;
1497
+ window.hideSaveDecisionForm = hideSaveDecisionForm;
1498
+ window.submitSaveDecision = submitSaveDecision;
1499
+ window.copyNgrokCommand = copyNgrokCommand;
1500
+ window.testConnection = testConnection;
1501
+ window.sendChatMessage = sendChatMessage;
1502
+ window.toggleTTS = toggleTTS;
1503
+ window.toggleTTSPopup = toggleTTSPopup;
1504
+ window.updateTTSSpeed = updateTTSSpeed;
1505
+ window.toggleHandsFree = toggleHandsFree;
1506
+ window.toggleVoiceInput = toggleVoiceInput;
1507
+ window.filterByTopic = filterByTopic;
1508
+ window.handleGraphSearch = handleGraphSearch;
1509
+ window.saveOutcome = saveOutcome;
1510
+ window.loadCheckpoints = loadCheckpoints;
1511
+ window.toggleCheckpoint = toggleCheckpoint;
1512
+
1513
+ document.addEventListener('DOMContentLoaded', () => {
1514
+ const userLang = navigator.language || navigator.userLanguage;
1515
+ document.documentElement.lang = userLang.split('-')[0];
1516
+
1517
+ initTheme();
1518
+ switchTab('chat');
1519
+ });
1520
+
1521
+ // Cleanup resources on page unload
1522
+ window.addEventListener('beforeunload', () => {
1523
+ // Cleanup dashboard intervals
1524
+ if (window.dashboardModule?.cleanup) {
1525
+ window.dashboardModule.cleanup();
1526
+ }
1527
+ // Cleanup chat resources
1528
+ if (window.chatModule?.cleanup) {
1529
+ window.chatModule.cleanup();
1530
+ }
1531
+ console.log('[MAMA] Resources cleaned up');
1532
+ });
1533
+ </script>
1534
+ </body>
1535
+ </html>