synthos 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/README.md +215 -65
  2. package/default-pages/application.json +1 -0
  3. package/default-pages/json_tools.json +1 -1
  4. package/default-pages/oregon_trail.html +321 -0
  5. package/default-pages/oregon_trail.json +12 -0
  6. package/default-pages/sidebar_page.json +1 -0
  7. package/default-pages/solar_explorer.html +10 -18
  8. package/default-pages/solar_explorer.json +2 -2
  9. package/default-pages/two-panel_page.json +1 -0
  10. package/default-pages/us_map.html +192 -0
  11. package/default-pages/us_map.json +12 -0
  12. package/default-pages/us_map_1850.html +325 -0
  13. package/default-pages/us_map_1850.json +12 -0
  14. package/default-pages/western_cities_1850.html +526 -0
  15. package/default-pages/western_cities_1850.json +12 -0
  16. package/default-themes/{nebula-dawn.css → nebula-dawn.v2.css} +24 -0
  17. package/default-themes/{nebula-dusk.css → nebula-dusk.v2.css} +24 -0
  18. package/dist/agents/a2a/a2aProvider.d.ts +3 -0
  19. package/dist/agents/a2a/a2aProvider.d.ts.map +1 -0
  20. package/dist/agents/a2a/a2aProvider.js +126 -0
  21. package/dist/agents/a2a/a2aProvider.js.map +1 -0
  22. package/dist/agents/discovery.d.ts +30 -0
  23. package/dist/agents/discovery.d.ts.map +1 -0
  24. package/dist/agents/discovery.js +52 -0
  25. package/dist/agents/discovery.js.map +1 -0
  26. package/dist/agents/index.d.ts +7 -0
  27. package/dist/agents/index.d.ts.map +1 -0
  28. package/dist/agents/index.js +19 -0
  29. package/dist/agents/index.js.map +1 -0
  30. package/dist/agents/openclaw/gatewayManager.d.ts +113 -0
  31. package/dist/agents/openclaw/gatewayManager.d.ts.map +1 -0
  32. package/dist/agents/openclaw/gatewayManager.js +470 -0
  33. package/dist/agents/openclaw/gatewayManager.js.map +1 -0
  34. package/dist/agents/openclaw/openclawProvider.d.ts +3 -0
  35. package/dist/agents/openclaw/openclawProvider.d.ts.map +1 -0
  36. package/dist/agents/openclaw/openclawProvider.js +239 -0
  37. package/dist/agents/openclaw/openclawProvider.js.map +1 -0
  38. package/dist/agents/openclaw/sshTunnelManager.d.ts +23 -0
  39. package/dist/agents/openclaw/sshTunnelManager.d.ts.map +1 -0
  40. package/dist/agents/openclaw/sshTunnelManager.js +340 -0
  41. package/dist/agents/openclaw/sshTunnelManager.js.map +1 -0
  42. package/dist/agents/types.d.ts +64 -0
  43. package/dist/agents/types.d.ts.map +1 -0
  44. package/dist/agents/types.js +6 -0
  45. package/dist/agents/types.js.map +1 -0
  46. package/dist/connectors/airtable/connector.json +27 -0
  47. package/dist/connectors/alpha-vantage/connector.json +26 -0
  48. package/dist/connectors/brave-search/connector.json +26 -0
  49. package/dist/connectors/cloudinary/connector.json +27 -0
  50. package/dist/connectors/deepl/connector.json +28 -0
  51. package/dist/connectors/elevenlabs/connector.json +30 -0
  52. package/dist/connectors/giphy/connector.json +27 -0
  53. package/dist/connectors/github/connector.json +29 -0
  54. package/dist/connectors/huggingface/connector.json +27 -0
  55. package/dist/connectors/imgur/connector.json +29 -0
  56. package/dist/connectors/index.d.ts +1 -1
  57. package/dist/connectors/index.d.ts.map +1 -1
  58. package/dist/connectors/instagram/connector.json +43 -0
  59. package/dist/connectors/jira/connector.json +28 -0
  60. package/dist/connectors/mapbox/connector.json +26 -0
  61. package/dist/connectors/nasa/connector.json +27 -0
  62. package/dist/connectors/newsapi/connector.json +27 -0
  63. package/dist/connectors/notion/connector.json +28 -0
  64. package/dist/connectors/open-exchange-rates/connector.json +27 -0
  65. package/dist/connectors/openweathermap/connector.json +26 -0
  66. package/dist/connectors/pexels/connector.json +27 -0
  67. package/dist/connectors/registry.d.ts.map +1 -1
  68. package/dist/connectors/registry.js +42 -96
  69. package/dist/connectors/registry.js.map +1 -1
  70. package/dist/connectors/resend/connector.json +29 -0
  71. package/dist/connectors/rss2json/connector.json +27 -0
  72. package/dist/connectors/sendgrid/connector.json +27 -0
  73. package/dist/connectors/spoonacular/connector.json +28 -0
  74. package/dist/connectors/stability-ai/connector.json +27 -0
  75. package/dist/connectors/twilio/connector.json +28 -0
  76. package/dist/connectors/types.d.ts +23 -0
  77. package/dist/connectors/types.d.ts.map +1 -1
  78. package/dist/connectors/unsplash/connector.json +27 -0
  79. package/dist/connectors/wolfram-alpha/connector.json +26 -0
  80. package/dist/connectors/youtube-data/connector.json +30 -0
  81. package/dist/files.d.ts +1 -0
  82. package/dist/files.d.ts.map +1 -1
  83. package/dist/files.js +16 -1
  84. package/dist/files.js.map +1 -1
  85. package/dist/init.d.ts.map +1 -1
  86. package/dist/init.js +28 -0
  87. package/dist/init.js.map +1 -1
  88. package/dist/migrations.d.ts +3 -2
  89. package/dist/migrations.d.ts.map +1 -1
  90. package/dist/migrations.js +122 -138
  91. package/dist/migrations.js.map +1 -1
  92. package/dist/models/anthropic.d.ts +22 -0
  93. package/dist/models/anthropic.d.ts.map +1 -0
  94. package/dist/models/anthropic.js +76 -0
  95. package/dist/models/anthropic.js.map +1 -0
  96. package/dist/models/chainOfThought.d.ts +12 -0
  97. package/dist/models/chainOfThought.d.ts.map +1 -0
  98. package/dist/models/chainOfThought.js +45 -0
  99. package/dist/models/chainOfThought.js.map +1 -0
  100. package/dist/models/fireworksai.d.ts +30 -0
  101. package/dist/models/fireworksai.d.ts.map +1 -0
  102. package/dist/models/fireworksai.js +133 -0
  103. package/dist/models/fireworksai.js.map +1 -0
  104. package/dist/models/index.d.ts +7 -1
  105. package/dist/models/index.d.ts.map +1 -1
  106. package/dist/models/index.js +19 -1
  107. package/dist/models/index.js.map +1 -1
  108. package/dist/models/logCompletePrompt.d.ts +3 -0
  109. package/dist/models/logCompletePrompt.d.ts.map +1 -0
  110. package/dist/models/logCompletePrompt.js +23 -0
  111. package/dist/models/logCompletePrompt.js.map +1 -0
  112. package/dist/models/openai.d.ts +24 -0
  113. package/dist/models/openai.d.ts.map +1 -0
  114. package/dist/models/openai.js +80 -0
  115. package/dist/models/openai.js.map +1 -0
  116. package/dist/models/providers.d.ts +1 -0
  117. package/dist/models/providers.d.ts.map +1 -1
  118. package/dist/models/providers.js +12 -4
  119. package/dist/models/providers.js.map +1 -1
  120. package/dist/models/types.d.ts +34 -2
  121. package/dist/models/types.d.ts.map +1 -1
  122. package/dist/models/types.js +16 -0
  123. package/dist/models/types.js.map +1 -1
  124. package/dist/models/utils.d.ts +6 -0
  125. package/dist/models/utils.d.ts.map +1 -0
  126. package/dist/models/utils.js +21 -0
  127. package/dist/models/utils.js.map +1 -0
  128. package/dist/scripts.d.ts +2 -1
  129. package/dist/scripts.d.ts.map +1 -1
  130. package/dist/scripts.js +4 -3
  131. package/dist/scripts.js.map +1 -1
  132. package/dist/service/createCompletePrompt.d.ts +1 -1
  133. package/dist/service/createCompletePrompt.d.ts.map +1 -1
  134. package/dist/service/createCompletePrompt.js +9 -6
  135. package/dist/service/createCompletePrompt.js.map +1 -1
  136. package/dist/service/generateImage.d.ts +1 -1
  137. package/dist/service/generateImage.d.ts.map +1 -1
  138. package/dist/service/generateImage.js +3 -3
  139. package/dist/service/generateImage.js.map +1 -1
  140. package/dist/service/server.d.ts.map +1 -1
  141. package/dist/service/server.js +3 -0
  142. package/dist/service/server.js.map +1 -1
  143. package/dist/service/transformPage.d.ts +4 -2
  144. package/dist/service/transformPage.d.ts.map +1 -1
  145. package/dist/service/transformPage.js +74 -6
  146. package/dist/service/transformPage.js.map +1 -1
  147. package/dist/service/useAgentRoutes.d.ts +4 -0
  148. package/dist/service/useAgentRoutes.d.ts.map +1 -0
  149. package/dist/service/useAgentRoutes.js +389 -0
  150. package/dist/service/useAgentRoutes.js.map +1 -0
  151. package/dist/service/useApiRoutes.d.ts.map +1 -1
  152. package/dist/service/useApiRoutes.js +157 -16
  153. package/dist/service/useApiRoutes.js.map +1 -1
  154. package/dist/service/useConnectorRoutes.d.ts.map +1 -1
  155. package/dist/service/useConnectorRoutes.js +14 -3
  156. package/dist/service/useConnectorRoutes.js.map +1 -1
  157. package/dist/service/useGatewayRoutes.d.ts +4 -0
  158. package/dist/service/useGatewayRoutes.d.ts.map +1 -0
  159. package/dist/service/useGatewayRoutes.js +168 -0
  160. package/dist/service/useGatewayRoutes.js.map +1 -0
  161. package/dist/service/usePageRoutes.d.ts.map +1 -1
  162. package/dist/service/usePageRoutes.js +16 -5
  163. package/dist/service/usePageRoutes.js.map +1 -1
  164. package/dist/settings.d.ts +2 -1
  165. package/dist/settings.d.ts.map +1 -1
  166. package/dist/settings.js +4 -8
  167. package/dist/settings.js.map +1 -1
  168. package/dist/themes.d.ts +14 -0
  169. package/dist/themes.d.ts.map +1 -1
  170. package/dist/themes.js +86 -13
  171. package/dist/themes.js.map +1 -1
  172. package/package.json +10 -5
  173. package/page-scripts/helpers-v2.js +222 -0
  174. package/page-scripts/page-v2.js +656 -0
  175. package/required-pages/builder.html +1 -27
  176. package/required-pages/pages.html +745 -22
  177. package/required-pages/settings.html +819 -21
  178. package/required-pages/synthos_apis.html +56 -1
  179. package/src/agents/a2a/a2aProvider.ts +110 -0
  180. package/src/agents/discovery.ts +74 -0
  181. package/src/agents/index.ts +6 -0
  182. package/src/agents/openclaw/gatewayManager.ts +559 -0
  183. package/src/agents/openclaw/openclawProvider.ts +261 -0
  184. package/src/agents/openclaw/sshTunnelManager.ts +385 -0
  185. package/src/agents/types.ts +82 -0
  186. package/src/connectors/airtable/connector.json +27 -0
  187. package/src/connectors/alpha-vantage/connector.json +26 -0
  188. package/src/connectors/brave-search/connector.json +26 -0
  189. package/src/connectors/cloudinary/connector.json +27 -0
  190. package/src/connectors/deepl/connector.json +28 -0
  191. package/src/connectors/elevenlabs/connector.json +30 -0
  192. package/src/connectors/giphy/connector.json +27 -0
  193. package/src/connectors/github/connector.json +29 -0
  194. package/src/connectors/huggingface/connector.json +27 -0
  195. package/src/connectors/imgur/connector.json +29 -0
  196. package/src/connectors/index.ts +2 -0
  197. package/src/connectors/instagram/connector.json +43 -0
  198. package/src/connectors/jira/connector.json +28 -0
  199. package/src/connectors/mapbox/connector.json +26 -0
  200. package/src/connectors/nasa/connector.json +27 -0
  201. package/src/connectors/newsapi/connector.json +27 -0
  202. package/src/connectors/notion/connector.json +28 -0
  203. package/src/connectors/open-exchange-rates/connector.json +27 -0
  204. package/src/connectors/openweathermap/connector.json +26 -0
  205. package/src/connectors/pexels/connector.json +27 -0
  206. package/src/connectors/registry.ts +21 -97
  207. package/src/connectors/resend/connector.json +29 -0
  208. package/src/connectors/rss2json/connector.json +27 -0
  209. package/src/connectors/sendgrid/connector.json +27 -0
  210. package/src/connectors/spoonacular/connector.json +28 -0
  211. package/src/connectors/stability-ai/connector.json +27 -0
  212. package/src/connectors/twilio/connector.json +28 -0
  213. package/src/connectors/types.ts +25 -0
  214. package/src/connectors/unsplash/connector.json +27 -0
  215. package/src/connectors/wolfram-alpha/connector.json +26 -0
  216. package/src/connectors/youtube-data/connector.json +30 -0
  217. package/src/files.ts +14 -0
  218. package/src/init.ts +27 -0
  219. package/src/migrations.ts +121 -138
  220. package/src/models/anthropic.ts +89 -0
  221. package/src/models/chainOfThought.ts +56 -0
  222. package/src/models/fireworksai.ts +136 -0
  223. package/src/models/index.ts +7 -1
  224. package/src/models/logCompletePrompt.ts +25 -0
  225. package/src/models/openai.ts +90 -0
  226. package/src/models/providers.ts +12 -3
  227. package/src/models/types.ts +67 -2
  228. package/src/models/utils.ts +16 -0
  229. package/src/scripts.ts +2 -2
  230. package/src/service/createCompletePrompt.ts +3 -1
  231. package/src/service/generateImage.ts +2 -2
  232. package/src/service/server.ts +4 -0
  233. package/src/service/transformPage.ts +81 -8
  234. package/src/service/useAgentRoutes.ts +423 -0
  235. package/src/service/useApiRoutes.ts +173 -18
  236. package/src/service/useConnectorRoutes.ts +14 -3
  237. package/src/service/usePageRoutes.ts +20 -6
  238. package/src/settings.ts +6 -10
  239. package/src/themes.ts +84 -12
  240. package/tests/README.md +12 -0
  241. package/tests/anthropic.spec.ts +84 -0
  242. package/tests/chainOfThought.spec.ts +108 -0
  243. package/tests/ensureScripts.spec.ts +82 -0
  244. package/tests/files.spec.ts +233 -0
  245. package/tests/fireworksai.spec.ts +92 -0
  246. package/tests/logCompletePrompt.spec.ts +74 -0
  247. package/tests/migrations.spec.ts +169 -0
  248. package/tests/openai.spec.ts +71 -0
  249. package/tests/pages.spec.ts +328 -0
  250. package/tests/providers.spec.ts +144 -0
  251. package/tests/scripts.spec.ts +209 -0
  252. package/tests/transformPage.spec.ts +931 -0
  253. package/tests/types.spec.ts +23 -0
  254. package/default-pages/app_builder.json +0 -1
  255. package/default-pages/sidebar_builder.json +0 -1
  256. package/default-pages/two-panel_builder.json +0 -1
  257. package/images/home.png +0 -0
  258. package/images/page-management.png +0 -0
  259. package/images/settings.png +0 -0
  260. package/images/synthos-square.png +0 -0
  261. /package/default-pages/{app_builder.html → application.html} +0 -0
  262. /package/default-pages/{sidebar_builder.html → sidebar_page.html} +0 -0
  263. /package/default-pages/{two-panel_builder.html → two-panel_page.html} +0 -0
@@ -4,7 +4,7 @@
4
4
  <title>SynthOS - Settings</title>
5
5
  <script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
6
6
  <link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
7
- <style>.settings-title{font-size:22px;font-weight:700;min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-radius:12px 12px 0 0;width:100%;box-shadow:0 6px 25px var(--accent-glow);letter-spacing:2px;text-shadow:0 2px 10px rgba(0,0,0,.3)}.settings-container{display:flex;flex-direction:column;width:100%;flex-grow:1;background:rgba(15,15,35,.8);border-radius:0 0 12px 12px;border:1px solid rgba(138,43,226,.2);border-top:none;overflow:hidden}.accordion-section{display:flex;flex-direction:column;flex-shrink:0;overflow:hidden;border-bottom:1px solid var(--border-color)}.accordion-section:last-child{border-bottom:none}.accordion-section.active{flex-shrink:1;flex-grow:1;min-height:0}.accordion-header{display:flex;justify-content:space-between;align-items:center;width:100%;padding:16px 25px;background:none;border:none;color:var(--text-primary);font-size:15px;font-weight:600;cursor:pointer;transition:background .3s;letter-spacing:.5px;flex-shrink:0}.accordion-header:hover{background:rgba(138,43,226,.05)}.accordion-section.active .accordion-header{background:rgba(138,43,226,.08)}.accordion-chevron{font-size:11px;color:var(--text-secondary);transition:transform .3s}.accordion-section.active .accordion-chevron{transform:rotate(180deg)}.accordion-body{display:none;flex-direction:column;flex-grow:1;min-height:0;overflow:hidden}.accordion-section.active .accordion-body{display:flex}.accordion-content{display:flex;flex-direction:column;gap:15px;overflow-y:auto;padding:20px 25px;flex-grow:1}.button-row{display:flex;justify-content:flex-end;padding:15px 25px;border-top:1px solid var(--border-color);flex-shrink:0}.apply-btn{padding:12px 30px;border:none;border-radius:12px;font-size:15px;background:linear-gradient(135deg,var(--accent-primary) 0,var(--accent-secondary) 50%,var(--accent-tertiary) 100%);color:#fff;cursor:pointer;transition:.3s;font-weight:600;letter-spacing:1px;box-shadow:0 4px 20px rgba(102,126,234,.4)}.apply-btn:hover{transform:translateY(-2px);box-shadow:0 6px 25px rgba(102,126,234,.6)}.apply-btn:active{transform:translateY(0)}.form-group{display:flex;flex-direction:column;width:100%}.form-group label{display:block;margin-bottom:8px;color:var(--text-secondary);font-weight:600;font-size:14px}.form-group input,.form-group select,.form-group textarea{width:100%;padding:14px 18px;border-radius:12px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:14px;transition:.3s;box-shadow:inset 0 2px 10px rgba(0,0,0,.3)}.form-group input:focus,.form-group select:focus,.form-group textarea:focus{outline:0;border-color:rgba(183,148,246,.6);box-shadow:inset 0 2px 10px rgba(0,0,0,.3),0 0 20px var(--accent-glow)}.form-group input::placeholder,.form-group textarea::placeholder{color:rgba(183,148,246,.5)}.form-group textarea{resize:vertical;min-height:100px}.form-group select{cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23b794f6' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 15px center}.form-group select option{background:var(--bg-tertiary);color:var(--text-primary);padding:10px}.info-text{color:rgba(183,148,246,.7);font-size:13px;line-height:1.6;text-align:center;padding:15px;background:rgba(15,15,35,.4);border-radius:10px;border:1px solid var(--border-color)}.info-text a{color:var(--accent-tertiary);text-decoration:none;transition:.3s;font-weight:500}.info-text a:hover{color:var(--text-secondary);text-shadow:0 0 10px var(--accent-glow)}.config-required-banner{padding:12px 20px;background:rgba(255,180,50,.12);border:1px solid rgba(255,180,50,.3);border-radius:10px;color:rgba(255,200,100,.9);font-size:13px;font-weight:500;text-align:center;line-height:1.5}.model-card{border:1px solid var(--border-color);border-radius:12px;padding:18px;display:flex;flex-direction:column;gap:12px;background:rgba(15,15,35,.3)}.model-card-title{font-size:14px;font-weight:700;color:var(--text-primary);letter-spacing:.5px;margin:0}.filter-bar{display:flex;align-items:center;gap:8px;flex-wrap:nowrap}.filter-btn{padding:6px 14px;border-radius:20px;border:1px solid var(--border-color);background:0 0;color:var(--text-secondary);font-size:13px;cursor:pointer;transition:.2s;white-space:nowrap;flex-shrink:0}.filter-btn:hover{border-color:var(--accent-primary);color:var(--accent-primary)}.filter-btn.active{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-color:transparent}.services-grid{display:grid;grid-template-columns:1fr;gap:12px}.service-card{border-radius:12px;border:1px solid var(--border-color);background:rgba(15,15,35,.5);padding:18px;transition:.3s}.service-card:hover{border-color:rgba(138,43,226,.3)}.service-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.service-card-name{font-size:15px;font-weight:600;color:var(--text-primary)}.service-card-category{font-size:11px;color:var(--text-secondary);background:rgba(138,43,226,.15);padding:2px 8px;border-radius:10px}.service-card-desc{font-size:13px;color:var(--text-secondary);line-height:1.5;margin-bottom:12px}.service-card-fields{display:flex;flex-direction:column;gap:8px}.service-card-fields input{width:100%;padding:10px 14px;border-radius:8px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px;transition:.3s;box-shadow:inset 0 2px 8px rgba(0,0,0,.2)}.service-card-fields input:focus{outline:0;border-color:rgba(183,148,246,.6);box-shadow:inset 0 2px 8px rgba(0,0,0,.2),0 0 15px var(--accent-glow)}.service-card-fields input::placeholder{color:rgba(183,148,246,.5)}.toggle-switch{position:relative;width:44px;height:24px;flex-shrink:0}.toggle-switch input{opacity:0;width:0;height:0}.toggle-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:rgba(100,100,100,.4);border-radius:24px;transition:.3s}.toggle-slider:before{content:"";position:absolute;height:18px;width:18px;left:3px;bottom:3px;background:#fff;border-radius:50%;transition:.3s}.toggle-switch input:checked+.toggle-slider{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary))}.toggle-switch input:checked+.toggle-slider:before{transform:translateX(20px)}.light-mode .settings-container{background:rgba(255,255,255,.8)}.light-mode .accordion-header:hover{background:rgba(118,75,162,.05)}.light-mode .accordion-section.active .accordion-header{background:rgba(118,75,162,.06)}.light-mode .form-group input,.light-mode .form-group select,.light-mode .form-group textarea{background:rgba(255,255,255,.8);box-shadow:inset 0 2px 10px rgba(118,75,162,.05)}.light-mode .form-group input:focus,.light-mode .form-group select:focus,.light-mode .form-group textarea:focus{box-shadow:inset 0 2px 10px rgba(118,75,162,.05),0 0 20px var(--accent-glow)}.light-mode .form-group input::placeholder,.light-mode .form-group textarea::placeholder{color:rgba(107,79,138,.5)}.light-mode .info-text{color:rgba(107,79,138,.7);background:rgba(255,255,255,.4)}.light-mode .config-required-banner{background:rgba(200,150,50,.1);border-color:rgba(200,150,50,.3);color:rgba(160,120,30,.9)}.light-mode .model-card{background:rgba(255,255,255,.3)}.light-mode .service-card{background:rgba(255,255,255,.5)}.light-mode .service-card-fields input{background:rgba(255,255,255,.8);box-shadow:inset 0 2px 8px rgba(118,75,162,.05)}.light-mode .service-card-fields input:focus{box-shadow:inset 0 2px 8px rgba(118,75,162,.05),0 0 15px var(--accent-glow)}.light-mode .service-card-fields input::placeholder{color:rgba(107,79,138,.5)}.model-card.disabled{opacity:0.35;pointer-events:none;user-select:none;transition:opacity .3s}.wizard-hidden{display:none !important}.more-settings-link{display:inline-block;color:var(--accent-tertiary);font-size:13px;cursor:pointer;text-decoration:none;padding:4px 0;transition:.3s;user-select:none}.more-settings-link:hover{color:var(--text-secondary);text-shadow:0 0 10px var(--accent-glow)}.light-mode .model-card.disabled{opacity:0.35}.accordion-section.disabled .accordion-header{opacity:0.35;pointer-events:none;cursor:default}.apply-btn:disabled{opacity:0.35;pointer-events:none;cursor:default}.connector-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:12px}.connector-tile{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:20px 12px;border-radius:14px;border:1px solid var(--border-color);background:linear-gradient(160deg,rgba(30,30,60,.6),rgba(20,20,50,.8));cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;gap:8px}.connector-tile:hover{transform:translateY(-3px);box-shadow:0 6px 20px var(--accent-glow);border-color:var(--accent-primary)}.connector-tile.configured{background:linear-gradient(160deg,rgba(60,30,90,.6),rgba(40,20,70,.8));border-color:var(--accent-secondary);box-shadow:0 2px 12px var(--accent-glow)}.connector-tile-name{font-size:14px;font-weight:600;color:var(--text-primary)}.connector-category{font-size:11px;color:var(--text-secondary);background:rgba(138,43,226,.15);padding:2px 8px;border-radius:10px}.search-input{flex:1;min-width:100px;padding:6px 14px;border-radius:20px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px;transition:.3s}.search-input:focus{outline:0;border-color:rgba(183,148,246,.6)}.search-input::placeholder{color:rgba(183,148,246,.5)}.filter-buttons-container{display:flex;gap:8px;flex-wrap:nowrap}.conn-modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;z-index:1000}.conn-modal-content{background:rgba(20,20,50,.95);border:1px solid var(--border-color);border-radius:16px;padding:28px;max-width:420px;width:90%;display:flex;flex-direction:column;gap:16px;box-shadow:0 8px 40px rgba(0,0,0,.5)}.conn-modal-title{font-size:18px;font-weight:700;color:var(--text-primary)}.conn-modal-desc{font-size:13px;color:var(--text-secondary);line-height:1.5}.conn-modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:8px}.conn-modal-btn{padding:12px 24px;border:none;border-radius:12px;font-size:14px;background:linear-gradient(135deg,var(--accent-primary) 0,var(--accent-secondary) 50%,var(--accent-tertiary) 100%);color:#fff;cursor:pointer;transition:.3s;font-weight:600;letter-spacing:.5px;box-shadow:0 4px 20px rgba(102,126,234,.4)}.conn-modal-btn:hover{transform:translateY(-2px);box-shadow:0 6px 25px rgba(102,126,234,.6)}.conn-modal-btn.cancel{background:rgba(100,100,100,.4);box-shadow:none}.conn-modal-btn.cancel:hover{background:rgba(100,100,100,.6);transform:translateY(-1px)}.conn-modal-btn.remove{background:rgba(180,50,50,.6);box-shadow:none}.conn-modal-btn.remove:hover{background:rgba(200,60,60,.8);transform:translateY(-1px)}.light-mode .connector-tile{background:linear-gradient(160deg,rgba(240,235,255,.8),rgba(250,248,255,.9))}.light-mode .connector-tile.configured{background:linear-gradient(160deg,rgba(220,200,255,.8),rgba(235,220,255,.9))}.light-mode .search-input{background:rgba(255,255,255,.8)}.light-mode .search-input::placeholder{color:rgba(107,79,138,.5)}.light-mode .conn-modal-content{background:rgba(250,248,255,.98)}</style>
7
+ <style>.settings-title{font-size:22px;font-weight:700;min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-radius:12px 12px 0 0;width:100%;box-shadow:0 6px 25px var(--accent-glow);letter-spacing:2px;text-shadow:0 2px 10px rgba(0,0,0,.3)}.settings-container{display:flex;flex-direction:column;width:100%;flex-grow:1;background:rgba(15,15,35,.8);border-radius:0 0 12px 12px;border:1px solid rgba(138,43,226,.2);border-top:none;overflow:hidden}.accordion-section{display:flex;flex-direction:column;flex-shrink:0;overflow:hidden;border-bottom:1px solid var(--border-color)}.accordion-section:last-child{border-bottom:none}.accordion-section.active{flex-shrink:1;flex-grow:1;min-height:0}.accordion-header{display:flex;justify-content:space-between;align-items:center;width:100%;padding:16px 25px;background:none;border:none;color:var(--text-primary);font-size:15px;font-weight:600;cursor:pointer;transition:background .3s;letter-spacing:.5px;flex-shrink:0}.accordion-header:hover{background:rgba(138,43,226,.05)}.accordion-section.active .accordion-header{background:rgba(138,43,226,.08)}.accordion-chevron{font-size:11px;color:var(--text-secondary);transition:transform .3s}.accordion-section.active .accordion-chevron{transform:rotate(180deg)}.accordion-body{display:none;flex-direction:column;flex-grow:1;min-height:0;overflow:hidden}.accordion-section.active .accordion-body{display:flex}.accordion-content{display:flex;flex-direction:column;gap:15px;overflow-y:auto;padding:20px 25px;flex-grow:1}.button-row{display:flex;justify-content:flex-end;padding:15px 25px;border-top:1px solid var(--border-color);flex-shrink:0}.apply-btn{padding:12px 30px;border:none;border-radius:12px;font-size:15px;background:linear-gradient(135deg,var(--accent-primary) 0,var(--accent-secondary) 50%,var(--accent-tertiary) 100%);color:#fff;cursor:pointer;transition:.3s;font-weight:600;letter-spacing:1px;box-shadow:0 4px 20px rgba(102,126,234,.4)}.apply-btn:hover{transform:translateY(-2px);box-shadow:0 6px 25px rgba(102,126,234,.6)}.apply-btn:active{transform:translateY(0)}.form-group{display:flex;flex-direction:column;width:100%}.form-group label{display:block;margin-bottom:8px;color:var(--text-secondary);font-weight:600;font-size:14px}.form-group input,.form-group select,.form-group textarea{width:100%;padding:14px 18px;border-radius:12px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:14px;transition:.3s;box-shadow:inset 0 2px 10px rgba(0,0,0,.3)}.form-group input:focus,.form-group select:focus,.form-group textarea:focus{outline:0;border-color:rgba(183,148,246,.6);box-shadow:inset 0 2px 10px rgba(0,0,0,.3),0 0 20px var(--accent-glow)}.form-group input::placeholder,.form-group textarea::placeholder{color:rgba(183,148,246,.5)}.form-group textarea{resize:vertical;min-height:100px}.form-group select{cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23b794f6' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 15px center}.form-group select option{background:var(--bg-tertiary);color:var(--text-primary);padding:10px}.info-text{color:rgba(183,148,246,.7);font-size:13px;line-height:1.6;text-align:center;padding:15px;background:rgba(15,15,35,.4);border-radius:10px;border:1px solid var(--border-color)}.info-text a{color:var(--accent-tertiary);text-decoration:none;transition:.3s;font-weight:500}.info-text a:hover{color:var(--text-secondary);text-shadow:0 0 10px var(--accent-glow)}.config-required-banner{padding:12px 20px;background:rgba(255,180,50,.12);border:1px solid rgba(255,180,50,.3);border-radius:10px;color:rgba(255,200,100,.9);font-size:13px;font-weight:500;text-align:center;line-height:1.5}.model-card{border:1px solid var(--border-color);border-radius:12px;padding:18px;display:flex;flex-direction:column;gap:12px;background:rgba(15,15,35,.3)}.model-card-title{font-size:14px;font-weight:700;color:var(--text-primary);letter-spacing:.5px;margin:0}.filter-bar{display:flex;align-items:center;gap:8px;flex-wrap:nowrap}.filter-btn{padding:6px 14px;border-radius:20px;border:1px solid var(--border-color);background:0 0;color:var(--text-secondary);font-size:13px;cursor:pointer;transition:.2s;white-space:nowrap;flex-shrink:0}.filter-btn:hover{border-color:var(--accent-primary);color:var(--accent-primary)}.filter-btn.active{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-color:transparent}.services-grid{display:grid;grid-template-columns:1fr;gap:12px}.service-card{border-radius:12px;border:1px solid var(--border-color);background:rgba(15,15,35,.5);padding:18px;transition:.3s}.service-card:hover{border-color:rgba(138,43,226,.3)}.service-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.service-card-name{font-size:15px;font-weight:600;color:var(--text-primary)}.service-card-category{font-size:11px;color:var(--text-secondary);background:rgba(138,43,226,.15);padding:2px 8px;border-radius:10px}.service-card-desc{font-size:13px;color:var(--text-secondary);line-height:1.5;margin-bottom:12px}.service-card-fields{display:flex;flex-direction:column;gap:8px}.service-card-fields input{width:100%;padding:10px 14px;border-radius:8px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px;transition:.3s;box-shadow:inset 0 2px 8px rgba(0,0,0,.2)}.service-card-fields input:focus{outline:0;border-color:rgba(183,148,246,.6);box-shadow:inset 0 2px 8px rgba(0,0,0,.2),0 0 15px var(--accent-glow)}.service-card-fields input::placeholder{color:rgba(183,148,246,.5)}.toggle-switch{position:relative;width:44px;height:24px;flex-shrink:0}.toggle-switch input{opacity:0;width:0;height:0}.toggle-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:rgba(100,100,100,.4);border-radius:24px;transition:.3s}.toggle-slider:before{content:"";position:absolute;height:18px;width:18px;left:3px;bottom:3px;background:#fff;border-radius:50%;transition:.3s}.toggle-switch input:checked+.toggle-slider{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary))}.toggle-switch input:checked+.toggle-slider:before{transform:translateX(20px)}.light-mode .settings-container{background:rgba(255,255,255,.8)}.light-mode .accordion-header:hover{background:rgba(118,75,162,.05)}.light-mode .accordion-section.active .accordion-header{background:rgba(118,75,162,.06)}.light-mode .form-group input,.light-mode .form-group select,.light-mode .form-group textarea{background:rgba(255,255,255,.8);box-shadow:inset 0 2px 10px rgba(118,75,162,.05)}.light-mode .form-group input:focus,.light-mode .form-group select:focus,.light-mode .form-group textarea:focus{box-shadow:inset 0 2px 10px rgba(118,75,162,.05),0 0 20px var(--accent-glow)}.light-mode .form-group input::placeholder,.light-mode .form-group textarea::placeholder{color:rgba(107,79,138,.5)}.light-mode .info-text{color:rgba(107,79,138,.7);background:rgba(255,255,255,.4)}.light-mode .config-required-banner{background:rgba(200,150,50,.1);border-color:rgba(200,150,50,.3);color:rgba(160,120,30,.9)}.light-mode .model-card{background:rgba(255,255,255,.3)}.light-mode .service-card{background:rgba(255,255,255,.5)}.light-mode .service-card-fields input{background:rgba(255,255,255,.8);box-shadow:inset 0 2px 8px rgba(118,75,162,.05)}.light-mode .service-card-fields input:focus{box-shadow:inset 0 2px 8px rgba(118,75,162,.05),0 0 15px var(--accent-glow)}.light-mode .service-card-fields input::placeholder{color:rgba(107,79,138,.5)}.model-card.disabled{opacity:0.35;pointer-events:none;user-select:none;transition:opacity .3s}.wizard-hidden{display:none !important}.more-settings-link{display:inline-block;color:var(--accent-tertiary);font-size:13px;cursor:pointer;text-decoration:none;padding:4px 0;transition:.3s;user-select:none}.more-settings-link:hover{color:var(--text-secondary);text-shadow:0 0 10px var(--accent-glow)}.light-mode .model-card.disabled{opacity:0.35}.accordion-section.disabled .accordion-header{opacity:0.35;pointer-events:none;cursor:default}.apply-btn:disabled{opacity:0.35;pointer-events:none;cursor:default}.connector-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:12px}.connector-tile{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:20px 12px;border-radius:14px;border:1px solid var(--border-color);background:linear-gradient(160deg,rgba(30,30,60,.6),rgba(20,20,50,.8));cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;gap:8px}.connector-tile:hover{transform:translateY(-3px);box-shadow:0 6px 20px var(--accent-glow);border-color:var(--accent-primary)}.connector-tile.configured{background:linear-gradient(160deg,rgba(60,30,90,.6),rgba(40,20,70,.8));border-color:var(--accent-secondary);box-shadow:0 2px 12px var(--accent-glow)}.connector-tile-name{font-size:14px;font-weight:600;color:var(--text-primary)}.connector-category{font-size:11px;color:var(--text-secondary);background:rgba(138,43,226,.15);padding:2px 8px;border-radius:10px}.search-input{margin-left:auto;width:180px;min-width:120px;flex-shrink:0;padding:6px 14px;border-radius:20px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px;transition:.3s}.search-input:focus{outline:0;border-color:rgba(183,148,246,.6)}.search-input::placeholder{color:rgba(183,148,246,.5)}.filter-buttons-container{display:flex;gap:8px;flex-shrink:1;min-width:0;overflow:hidden;flex-wrap:nowrap}.conn-modal-content{background:rgba(20,20,50,.95);border:1px solid var(--border-color);border-radius:16px;padding:28px;max-width:520px;width:90%;max-height:85vh;display:flex;flex-direction:column;gap:16px;box-shadow:0 8px 40px rgba(0,0,0,.5);overflow-y:auto}.conn-modal-title{font-size:18px;font-weight:700;color:var(--text-primary)}.conn-modal-desc{font-size:13px;color:var(--text-secondary);line-height:1.5}.conn-modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:8px}.conn-modal-btn{padding:12px 24px;border:none;border-radius:12px;font-size:14px;background:linear-gradient(135deg,var(--accent-primary) 0,var(--accent-secondary) 50%,var(--accent-tertiary) 100%);color:#fff;cursor:pointer;transition:.3s;font-weight:600;letter-spacing:.5px;box-shadow:0 4px 20px rgba(102,126,234,.4)}.conn-modal-btn:hover{transform:translateY(-2px);box-shadow:0 6px 25px rgba(102,126,234,.6)}.conn-modal-btn.cancel{background:rgba(100,100,100,.4);box-shadow:none}.conn-modal-btn.cancel:hover{background:rgba(100,100,100,.6);transform:translateY(-1px)}.conn-modal-btn.remove{background:rgba(180,50,50,.6);box-shadow:none}.conn-modal-btn.remove:hover{background:rgba(200,60,60,.8);transform:translateY(-1px)}.light-mode .connector-tile{background:linear-gradient(160deg,rgba(240,235,255,.8),rgba(250,248,255,.9))}.light-mode .connector-tile.configured{background:linear-gradient(160deg,rgba(220,200,255,.8),rgba(235,220,255,.9))}.light-mode .search-input{background:rgba(255,255,255,.8)}.light-mode .search-input::placeholder{color:rgba(107,79,138,.5)}.more-dropdown{position:relative;flex-shrink:0}.more-btn{position:relative}.more-menu{position:absolute;top:100%;left:0;margin-top:4px;min-width:150px;max-height:250px;overflow-y:auto;border-radius:8px;padding:4px 0;box-shadow:0 8px 30px rgba(0,0,0,.4);display:none;border:1px solid var(--border-color);background:rgba(30,30,50,.95);z-index:100}.more-menu.show{display:block}.more-menu-item{padding:8px 16px;font-size:13px;cursor:pointer;color:var(--text-primary);transition:background .15s;white-space:nowrap}.more-menu-item:hover{background:linear-gradient(135deg,rgba(102,126,234,.3) 0,rgba(118,75,162,.3) 100%)}.more-menu-item.active{background:linear-gradient(135deg,rgba(102,126,234,.2) 0,rgba(118,75,162,.2) 100%);color:var(--accent-tertiary)}.conn-tooltip{position:fixed;padding:8px 12px;background:var(--bg-tertiary,#0f0f23);color:var(--text-secondary,#b794f6);border:1px solid var(--border-color,rgba(138,43,226,0.3));border-radius:8px;font-size:12px;line-height:1.5;max-width:260px;pointer-events:none;z-index:10000;box-shadow:0 4px 16px rgba(0,0,0,.4);opacity:0;transition:opacity .15s}.conn-tooltip.visible{opacity:1}.light-mode .more-menu{background:rgba(255,255,255,.97);box-shadow:0 8px 30px rgba(0,0,0,.15)}.light-mode .conn-modal-content{background:rgba(250,248,255,.98)}.agent-toggle-row{display:flex;align-items:center;gap:10px}.agent-list{display:flex;flex-direction:column;gap:0;max-height:400px;overflow-y:auto;border:1px solid var(--border-color);border-radius:12px}.agent-row{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border-color);cursor:pointer;transition:background .15s}.agent-row:last-child{border-bottom:none}.agent-row:hover{background:rgba(138,43,226,.06)}.agent-row.selected{background:rgba(138,43,226,.1)}.agent-row-info{flex:1;min-width:0;display:flex;align-items:center;gap:10px}.agent-row-name{font-size:14px;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.agent-row-desc{font-size:12px;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.agent-row-badge{font-size:11px;color:var(--text-secondary);background:rgba(138,43,226,.15);padding:2px 8px;border-radius:10px;white-space:nowrap;flex-shrink:0}.agent-status-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.agent-status-dot.connected{background:#4ade80}.agent-status-dot.disconnected{background:rgba(100,100,100,.5)}.agent-row-actions{display:flex;align-items:center;gap:8px;flex-shrink:0}.agent-chat-panel{border:1px solid var(--border-color);border-radius:12px;margin-top:12px;overflow:hidden;display:none}.agent-chat-panel.open{display:flex;flex-direction:column}.agent-chat-header{display:flex;justify-content:space-between;align-items:center;padding:10px 16px;background:rgba(138,43,226,.08);font-size:13px;font-weight:600;color:var(--text-primary)}.agent-chat-close{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:16px;padding:0 4px}.agent-chat-close:hover{color:var(--text-primary)}.agent-chat-messages{max-height:250px;overflow-y:auto;padding:12px 16px;display:flex;flex-direction:column;gap:8px;font-size:13px}.agent-chat-msg{padding:8px 12px;border-radius:10px;max-width:85%;line-height:1.5;word-wrap:break-word;white-space:pre-wrap}.agent-chat-msg.user{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;align-self:flex-end}.agent-chat-msg.agent{background:rgba(15,15,35,.4);color:var(--text-primary);align-self:flex-start;border:1px solid var(--border-color)}.agent-chat-msg.error{background:rgba(180,50,50,.2);color:rgba(255,150,150,.9);align-self:center;font-size:12px}.agent-chat-msg.status{color:var(--text-secondary);align-self:center;font-size:12px;font-style:italic}.agent-chat-input-row{display:flex;gap:8px;padding:12px 16px;border-top:1px solid var(--border-color)}.agent-chat-input{flex:1;padding:10px 14px;border-radius:10px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px}.agent-chat-input:focus{outline:0;border-color:rgba(183,148,246,.6)}.agent-chat-input::placeholder{color:rgba(183,148,246,.5)}.agent-chat-send{padding:10px 18px;border:none;border-radius:10px;font-size:13px;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;cursor:pointer;font-weight:600;transition:.2s}.agent-chat-send:hover{transform:translateY(-1px)}.agent-chat-send:disabled{opacity:.5;pointer-events:none}.light-mode .agent-list{border-color:rgba(118,75,162,.15)}.light-mode .agent-row:hover{background:rgba(118,75,162,.05)}.light-mode .agent-row.selected{background:rgba(118,75,162,.08)}.light-mode .agent-chat-msg.agent{background:rgba(255,255,255,.6)}.light-mode .agent-chat-input{background:rgba(255,255,255,.8)}</style>
8
8
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
9
9
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
10
10
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
@@ -136,13 +136,17 @@
136
136
 
137
137
  <div class="accordion-section" data-section="connectors">
138
138
  <button class="accordion-header">
139
- <span>Connectors</span>
139
+ <span>Connectors <span style="display:inline-block;margin-left:6px;padding:1px 7px;font-size:10px;font-weight:600;letter-spacing:.5px;border-radius:6px;background:var(--accent-tertiary, #8a2be2);color:#fff;vertical-align:middle;line-height:16px;text-transform:uppercase;">Preview</span></span>
140
140
  <span class="accordion-chevron">&#9662;</span>
141
141
  </button>
142
142
  <div class="accordion-body">
143
143
  <div class="accordion-content">
144
144
  <div class="filter-bar" id="connectorFilterBar">
145
145
  <div class="filter-buttons-container" id="connectorFilterButtons"></div>
146
+ <div class="more-dropdown" id="connMoreDropdown" style="display:none;">
147
+ <button class="filter-btn more-btn" id="connMoreBtn">More &#9662;</button>
148
+ <div class="more-menu" id="connMoreMenu"></div>
149
+ </div>
146
150
  <input type="text" class="search-input" id="connectorSearch" placeholder="Search connectors...">
147
151
  </div>
148
152
  <div class="connector-grid" id="connectorGrid"></div>
@@ -150,13 +154,119 @@
150
154
  </div>
151
155
  </div>
152
156
 
157
+ <div class="accordion-section" data-section="agents">
158
+ <button class="accordion-header">
159
+ <span>Agents <span style="display:inline-block;margin-left:6px;padding:1px 7px;font-size:10px;font-weight:600;letter-spacing:.5px;border-radius:6px;background:var(--accent-tertiary, #8a2be2);color:#fff;vertical-align:middle;line-height:16px;text-transform:uppercase;">Preview</span></span>
160
+ <span class="accordion-chevron">&#9662;</span>
161
+ </button>
162
+ <div class="accordion-body">
163
+ <div class="accordion-content">
164
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
165
+ <div style="font-size:13px;color:var(--text-secondary);">Configure agents (A2A or OpenClaw) that your pages can communicate with.</div>
166
+ <button class="conn-modal-btn" id="addAgentBtn" style="padding:8px 18px;font-size:13px;">+ Add Agent</button>
167
+ </div>
168
+ <div class="agent-list" id="agentList"></div>
169
+ <div id="agentEmptyState" style="display:none;text-align:center;padding:30px;color:var(--text-secondary);font-size:14px;">
170
+ No agents configured yet. Click "+ Add Agent" to get started.
171
+ </div>
172
+ <div class="agent-chat-panel" id="agentChatPanel">
173
+ <div class="agent-chat-header">
174
+ <span id="agentChatTitle">Chat with Agent</span>
175
+ <button class="agent-chat-close" id="agentChatClose">&times;</button>
176
+ </div>
177
+ <div class="agent-chat-messages" id="agentChatMessages"></div>
178
+ <div class="agent-chat-input-row">
179
+ <input type="text" class="agent-chat-input" id="agentChatInput" placeholder="Type a test message...">
180
+ <button class="agent-chat-send" id="agentChatSendBtn">Send</button>
181
+ </div>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </div>
186
+
153
187
  </div>
154
188
  <div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
155
189
  </div>
156
- <div class="conn-modal-overlay" id="connectorModal" style="display:none;">
190
+ <div class="modal-overlay" id="agentModal" style="display:none;">
191
+ <div class="conn-modal-content">
192
+ <div class="conn-modal-title" id="agentModalTitle">Add Agent</div>
193
+ <div id="agentFormGroup">
194
+ <div class="form-group" style="margin-bottom:10px;">
195
+ <label>Agent Type</label>
196
+ <div style="display:flex;gap:8px;">
197
+ <button class="filter-btn active" id="agentTypeA2A">A2A Agent</button>
198
+ <button class="filter-btn" id="agentTypeOpenClaw">OpenClaw Agent</button>
199
+ </div>
200
+ </div>
201
+ <div class="form-group" style="margin-bottom:10px;">
202
+ <label for="agentUrl">Agent URL</label>
203
+ <div style="display:flex;gap:8px;">
204
+ <input type="text" id="agentUrl" placeholder="https://example.com" style="flex:1;">
205
+ <button class="conn-modal-btn" id="agentDiscoverBtn" style="padding:10px 16px;white-space:nowrap;font-size:13px;">Discover</button>
206
+ </div>
207
+ <div id="agentUrlHint" style="font-size:11px;color:var(--text-secondary);margin-top:4px;">Enter a URL and click Discover to auto-fill from the agent card, or fill in the fields manually.</div>
208
+ </div>
209
+ <div class="form-group" id="agentTokenGroup" style="margin-bottom:10px;display:none;">
210
+ <label for="agentToken">Token</label>
211
+ <input type="password" id="agentToken" placeholder="Gateway authentication token">
212
+ </div>
213
+ <div class="form-group" id="agentSessionKeyGroup" style="margin-bottom:10px;display:none;">
214
+ <label for="agentSessionKey">Default Session Key</label>
215
+ <input type="text" id="agentSessionKey" placeholder="e.g. agent:main:main">
216
+ </div>
217
+ <div id="agentSshTunnelGroup" style="display:none;margin-bottom:10px;">
218
+ <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;cursor:pointer;" id="agentSshTunnelToggleHeader">
219
+ <span style="font-size:12px;color:var(--text-secondary);" id="agentSshTunnelArrow">&#9654;</span>
220
+ <label style="margin:0;cursor:pointer;font-size:13px;font-weight:600;color:var(--text-secondary);">SSH Tunnel</label>
221
+ <label class="toggle-switch" style="margin-left:auto;" onclick="event.stopPropagation();">
222
+ <input type="checkbox" id="agentSshTunnelEnabled">
223
+ <span class="toggle-slider"></span>
224
+ </label>
225
+ </div>
226
+ <div id="agentSshTunnelFields" style="display:none;padding-left:4px;">
227
+ <div class="form-group" style="margin-bottom:8px;">
228
+ <label for="agentSshCommand" style="font-size:12px;">SSH Command</label>
229
+ <input type="text" id="agentSshCommand" placeholder="ssh -p 22 -N -L 18789:127.0.0.1:43901 root@0.0.0.0">
230
+ </div>
231
+ <div class="form-group" style="margin-bottom:8px;">
232
+ <label for="agentSshPassword" style="font-size:12px;">Password</label>
233
+ <input type="password" id="agentSshPassword" placeholder="SSH password">
234
+ </div>
235
+ </div>
236
+ </div>
237
+ <div class="form-group" style="margin-bottom:10px;">
238
+ <label for="agentName">Name</label>
239
+ <input type="text" id="agentName" placeholder="My Agent">
240
+ </div>
241
+ <div class="form-group" style="margin-bottom:10px;">
242
+ <label for="agentDescription">Description</label>
243
+ <textarea id="agentDescription" placeholder="Describe what this agent does and when to use it..." style="min-height:60px;"></textarea>
244
+ </div>
245
+ <div id="agentSkillsPreview" style="display:none;padding:10px;border-radius:8px;border:1px solid var(--border-color);background:rgba(15,15,35,.3);margin-bottom:10px;">
246
+ <div style="font-size:12px;font-weight:600;color:var(--text-secondary);margin-bottom:4px;">Discovered Skills</div>
247
+ <div id="agentSkillsList" style="font-size:12px;color:var(--text-secondary);"></div>
248
+ </div>
249
+ </div>
250
+ <div class="conn-modal-actions">
251
+ <button class="conn-modal-btn" id="agentSaveBtn">Save</button>
252
+ <button class="conn-modal-btn cancel" id="agentCancelBtn">Cancel</button>
253
+ <button class="conn-modal-btn remove" id="agentRemoveBtn" style="display:none;">Remove</button>
254
+ </div>
255
+ </div>
256
+ </div>
257
+ <div class="modal-overlay" id="connectorModal" style="display:none;">
157
258
  <div class="conn-modal-content">
158
259
  <div class="conn-modal-title" id="connectorModalTitle"></div>
159
260
  <div class="conn-modal-desc" id="connectorModalDesc"></div>
261
+ <div id="connectorOnboarding" style="display:none;">
262
+ <a id="connectorOnboardingLink" href="#" target="_blank"
263
+ style="color:var(--accent-tertiary);font-size:13px;font-weight:500;text-decoration:none;transition:.3s;">
264
+ Get your API key &#8594;
265
+ </a>
266
+ <ol id="connectorOnboardingSteps"
267
+ style="font-size:13px;color:var(--text-secondary);margin:8px 0 0;padding-left:20px;line-height:1.8;">
268
+ </ol>
269
+ </div>
160
270
  <div id="connectorApiKeyGroup" class="form-group">
161
271
  <label for="connectorApiKey">API Key</label>
162
272
  <input type="password" id="connectorApiKey" placeholder="Enter your API key">
@@ -231,46 +341,223 @@ document.querySelectorAll('.accordion-header').forEach(function(header) {
231
341
  // --- URL param: open requested tab ---
232
342
  var params = new URLSearchParams(window.location.search);
233
343
  var tabParam = params.get('tab');
234
- if (tabParam && ['general', 'models', 'connectors'].indexOf(tabParam) !== -1) {
344
+ if (tabParam && ['general', 'models', 'connectors', 'agents'].indexOf(tabParam) !== -1) {
235
345
  openSection(tabParam);
236
346
  }
237
347
 
238
348
  // --- Connectors logic ---
239
349
  var connectorList = [], activeConnectorCategory = 'All', currentConnectorId = null;
350
+ var connVisibleCats = [], connOverflowCats = [];
351
+
352
+ // MRU: most-recently-used categories get priority for visible slots
353
+ var connMru = (function() {
354
+ try { return JSON.parse(localStorage.getItem('synthos_connMru')) || []; }
355
+ catch(e) { return []; }
356
+ })();
357
+ function connMruTouch(cat) {
358
+ if (cat === 'All' || cat === 'Enabled') return;
359
+ connMru = connMru.filter(function(c) { return c !== cat; });
360
+ connMru.unshift(cat);
361
+ if (connMru.length > 20) connMru.length = 20;
362
+ try { localStorage.setItem('synthos_connMru', JSON.stringify(connMru)); } catch(e) {}
363
+ }
240
364
 
241
- function loadConnectors() {
242
- fetch('/api/connectors').then(function(r) { return r.json(); }).then(function(list) {
243
- connectorList = list;
244
- renderConnectorFilterBar();
245
- renderConnectorGrid();
365
+ function getAllConnectorCategories() {
366
+ var cats = new Set();
367
+ connectorList.forEach(function(c) { cats.add(c.category); });
368
+ return Array.from(cats).sort();
369
+ }
370
+
371
+ function calculateConnectorVisibleCats() {
372
+ var allCats = getAllConnectorCategories();
373
+ var filterBar = document.getElementById('connectorFilterBar');
374
+ var searchEl = document.getElementById('connectorSearch');
375
+ var moreDropdown = document.getElementById('connMoreDropdown');
376
+ if (!filterBar) return;
377
+
378
+ var availableWidth = filterBar.offsetWidth - (searchEl.offsetWidth + 8) - 16;
379
+
380
+ // Measure button widths
381
+ var temp = document.createElement('button');
382
+ temp.className = 'filter-btn';
383
+ temp.style.visibility = 'hidden';
384
+ temp.style.position = 'absolute';
385
+ document.body.appendChild(temp);
386
+
387
+ // Measure "All" button
388
+ temp.textContent = 'All';
389
+ var allBtnWidth = temp.offsetWidth + 8;
390
+
391
+ // Measure "Enabled" button (always shown)
392
+ temp.textContent = 'Enabled';
393
+ var enabledBtnWidth = temp.offsetWidth + 8;
394
+
395
+ // Measure "More" button
396
+ temp.textContent = 'More \u25BE';
397
+ var moreBtnWidth = temp.offsetWidth + 8;
398
+
399
+ // Measure each category button
400
+ var catWidths = {};
401
+ allCats.forEach(function(cat) {
402
+ temp.textContent = cat;
403
+ catWidths[cat] = temp.offsetWidth + 8;
404
+ });
405
+ document.body.removeChild(temp);
406
+
407
+ // Sort categories: MRU first, then alphabetical
408
+ var sorted = allCats.slice().sort(function(a, b) {
409
+ var ai = connMru.indexOf(a);
410
+ var bi = connMru.indexOf(b);
411
+ var aInMru = ai !== -1;
412
+ var bInMru = bi !== -1;
413
+ if (aInMru && !bInMru) return -1;
414
+ if (!aInMru && bInMru) return 1;
415
+ if (aInMru && bInMru) return ai - bi;
416
+ return a.localeCompare(b);
246
417
  });
418
+
419
+ connVisibleCats = [];
420
+ connOverflowCats = [];
421
+ var usedWidth = allBtnWidth + enabledBtnWidth;
422
+ var overflowing = false;
423
+
424
+ for (var i = 0; i < sorted.length; i++) {
425
+ var cat = sorted[i];
426
+ var remaining = sorted.length - i;
427
+ // When we'd start overflowing, reserve space for the More button
428
+ var widthIfAdded = usedWidth + catWidths[cat];
429
+ if (!overflowing && remaining > 1) widthIfAdded += moreBtnWidth;
430
+
431
+ if (!overflowing && usedWidth + catWidths[cat] <= availableWidth - (remaining > 1 ? moreBtnWidth : 0)) {
432
+ connVisibleCats.push(cat);
433
+ usedWidth += catWidths[cat];
434
+ } else {
435
+ overflowing = true;
436
+ connOverflowCats.push(cat);
437
+ }
438
+ }
439
+
440
+ // If only one overflow, try to fit it without the More button
441
+ if (connOverflowCats.length === 1) {
442
+ if (usedWidth + catWidths[connOverflowCats[0]] <= availableWidth) {
443
+ connVisibleCats.push(connOverflowCats.pop());
444
+ }
445
+ }
247
446
  }
248
447
 
249
448
  function renderConnectorFilterBar() {
449
+ calculateConnectorVisibleCats();
250
450
  var container = document.getElementById('connectorFilterButtons');
451
+ var moreDropdown = document.getElementById('connMoreDropdown');
452
+ var moreBtn = document.getElementById('connMoreBtn');
453
+ var moreMenu = document.getElementById('connMoreMenu');
251
454
  container.innerHTML = '';
252
- var cats = new Set();
253
- connectorList.forEach(function(c) { cats.add(c.category); });
254
- var sorted = Array.from(cats).sort();
255
- ['All'].concat(sorted).forEach(function(cat) {
455
+ moreMenu.innerHTML = '';
456
+
457
+ // "All" button
458
+ var allBtn = document.createElement('button');
459
+ allBtn.className = 'filter-btn' + (activeConnectorCategory === 'All' ? ' active' : '');
460
+ allBtn.textContent = 'All';
461
+ allBtn.addEventListener('click', function() { setConnectorCategory('All'); });
462
+ container.appendChild(allBtn);
463
+
464
+ // "Enabled" button (fixed, always visible)
465
+ var enabledBtn = document.createElement('button');
466
+ enabledBtn.className = 'filter-btn' + (activeConnectorCategory === 'Enabled' ? ' active' : '');
467
+ enabledBtn.textContent = 'Enabled';
468
+ enabledBtn.addEventListener('click', function() { setConnectorCategory('Enabled'); });
469
+ container.appendChild(enabledBtn);
470
+
471
+ // Visible category buttons
472
+ connVisibleCats.forEach(function(cat) {
256
473
  var btn = document.createElement('button');
257
474
  btn.className = 'filter-btn' + (activeConnectorCategory === cat ? ' active' : '');
258
475
  btn.textContent = cat;
259
- btn.addEventListener('click', function() {
260
- activeConnectorCategory = cat;
261
- renderConnectorFilterBar();
262
- renderConnectorGrid();
263
- });
476
+ btn.addEventListener('click', function() { setConnectorCategory(cat); });
264
477
  container.appendChild(btn);
265
478
  });
479
+
480
+ // Overflow dropdown
481
+ if (connOverflowCats.length > 0) {
482
+ moreDropdown.style.display = '';
483
+ moreBtn.className = 'filter-btn more-btn' + (connOverflowCats.indexOf(activeConnectorCategory) !== -1 ? ' active' : '');
484
+ connOverflowCats.forEach(function(cat) {
485
+ var item = document.createElement('div');
486
+ item.className = 'more-menu-item' + (activeConnectorCategory === cat ? ' active' : '');
487
+ item.textContent = cat;
488
+ item.addEventListener('click', function() {
489
+ setConnectorCategory(cat);
490
+ moreMenu.classList.remove('show');
491
+ });
492
+ moreMenu.appendChild(item);
493
+ });
494
+ } else {
495
+ moreDropdown.style.display = 'none';
496
+ }
497
+ }
498
+
499
+ function setConnectorCategory(cat) {
500
+ activeConnectorCategory = cat;
501
+ connMruTouch(cat);
502
+ renderConnectorFilterBar();
503
+ renderConnectorGrid();
266
504
  }
267
505
 
506
+ function loadConnectors() {
507
+ fetch('/api/connectors').then(function(r) { return r.json(); }).then(function(list) {
508
+ connectorList = list;
509
+ renderConnectorFilterBar();
510
+ renderConnectorGrid();
511
+ });
512
+ }
513
+
514
+ // --- Connector tooltip ---
515
+ var connTip = document.createElement('div');
516
+ connTip.className = 'conn-tooltip';
517
+ document.body.appendChild(connTip);
518
+ var connTipTimer = null;
519
+
520
+ function showConnTip(el, text) {
521
+ clearTimeout(connTipTimer);
522
+ connTip.textContent = text;
523
+ connTip.style.display = 'block';
524
+ connTip.classList.remove('visible');
525
+ var r = el.getBoundingClientRect();
526
+ var tw = connTip.offsetWidth;
527
+ var th = connTip.offsetHeight;
528
+ // Position below the tile by default
529
+ var left = r.left + (r.width / 2) - (tw / 2);
530
+ var top = r.bottom + 6;
531
+ // Clamp horizontal
532
+ if (left < 4) left = 4;
533
+ if (left + tw > window.innerWidth - 4) left = window.innerWidth - tw - 4;
534
+ // Flip above if it would go off bottom
535
+ if (top + th > window.innerHeight - 4) {
536
+ top = r.top - th - 6;
537
+ }
538
+ connTip.style.left = left + 'px';
539
+ connTip.style.top = top + 'px';
540
+ void connTip.offsetWidth;
541
+ connTip.classList.add('visible');
542
+ }
543
+
544
+ function hideConnTip() {
545
+ clearTimeout(connTipTimer);
546
+ connTip.classList.remove('visible');
547
+ connTip.style.display = 'none';
548
+ }
549
+ hideConnTip();
550
+
268
551
  function renderConnectorGrid() {
269
552
  var grid = document.getElementById('connectorGrid');
270
553
  var searchTerm = (document.getElementById('connectorSearch').value || '').toLowerCase();
271
554
  grid.innerHTML = '';
272
555
  connectorList.filter(function(c) {
273
- if (activeConnectorCategory !== 'All' && c.category !== activeConnectorCategory) return false;
556
+ if (activeConnectorCategory === 'Enabled') {
557
+ if (!c.configured) return false;
558
+ } else if (activeConnectorCategory !== 'All' && c.category !== activeConnectorCategory) {
559
+ return false;
560
+ }
274
561
  if (searchTerm && c.name.toLowerCase().indexOf(searchTerm) === -1) return false;
275
562
  return true;
276
563
  }).forEach(function(c) {
@@ -278,6 +565,11 @@ function renderConnectorGrid() {
278
565
  tile.className = 'connector-tile' + (c.configured ? ' configured' : '');
279
566
  tile.addEventListener('click', function() { openConnectorModal(c.id); });
280
567
 
568
+ tile.addEventListener('mouseenter', function() {
569
+ connTipTimer = setTimeout(function() { showConnTip(tile, c.description); }, 400);
570
+ });
571
+ tile.addEventListener('mouseleave', hideConnTip);
572
+
281
573
  var name = document.createElement('div');
282
574
  name.className = 'connector-tile-name';
283
575
  name.textContent = c.name;
@@ -300,6 +592,19 @@ function openConnectorModal(id) {
300
592
  currentConnectorDetail = detail;
301
593
  document.getElementById('connectorModalTitle').textContent = detail.name;
302
594
  document.getElementById('connectorModalDesc').textContent = detail.description;
595
+
596
+ var onboardingEl = document.getElementById('connectorOnboarding');
597
+ if (detail.onboarding && detail.onboarding.url) {
598
+ document.getElementById('connectorOnboardingLink').href = detail.onboarding.url;
599
+ var stepsOl = document.getElementById('connectorOnboardingSteps');
600
+ stepsOl.innerHTML = (detail.onboarding.steps || []).map(function(s) {
601
+ return '<li>' + s + '</li>';
602
+ }).join('');
603
+ onboardingEl.style.display = '';
604
+ } else {
605
+ onboardingEl.style.display = 'none';
606
+ }
607
+
303
608
  document.getElementById('connectorEnabled').checked = detail.enabled;
304
609
  document.getElementById('connectorRemoveBtn').style.display = (detail.configured || detail.hasKey) ? '' : 'none';
305
610
 
@@ -381,8 +686,11 @@ function closeConnectorModal() {
381
686
 
382
687
  document.getElementById('connectorCancelBtn').addEventListener('click', closeConnectorModal);
383
688
 
689
+ var connectorModalMouseDownTarget = null;
690
+ document.getElementById('connectorModal').addEventListener('mousedown', function(e) { connectorModalMouseDownTarget = e.target; });
384
691
  document.getElementById('connectorModal').addEventListener('click', function(e) {
385
- if (e.target === this) closeConnectorModal();
692
+ if (e.target === this && connectorModalMouseDownTarget === this) closeConnectorModal();
693
+ connectorModalMouseDownTarget = null;
386
694
  });
387
695
 
388
696
  document.getElementById('connectorSaveBtn').addEventListener('click', function() {
@@ -454,6 +762,24 @@ document.getElementById('connectorRemoveBtn').addEventListener('click', function
454
762
 
455
763
  document.getElementById('connectorSearch').addEventListener('input', renderConnectorGrid);
456
764
 
765
+ // More dropdown toggle
766
+ document.getElementById('connMoreBtn').addEventListener('click', function(e) {
767
+ e.stopPropagation();
768
+ document.getElementById('connMoreMenu').classList.toggle('show');
769
+ });
770
+ document.addEventListener('click', function() {
771
+ document.getElementById('connMoreMenu').classList.remove('show');
772
+ });
773
+
774
+ // Resize observer — recalculate visible categories when filter bar resizes
775
+ var connFilterBar = document.getElementById('connectorFilterBar');
776
+ if (connFilterBar && typeof ResizeObserver !== 'undefined') {
777
+ var connResizeObserver = new ResizeObserver(function() {
778
+ if (connectorList.length > 0) renderConnectorFilterBar();
779
+ });
780
+ connResizeObserver.observe(connFilterBar);
781
+ }
782
+
457
783
  // Detect OAuth success/error from URL params
458
784
  var connectedParam = params.get('connected');
459
785
  var errorParam = params.get('error');
@@ -474,6 +800,475 @@ if (errorParam) {
474
800
  history.replaceState(null, '', cleanUrl2);
475
801
  }
476
802
 
803
+ // --- Agents logic ---
804
+ var agentList = [], currentAgentId = null, currentAgentSkills = null, currentAgentCapabilities = null;
805
+ var currentAgentType = 'a2a';
806
+ var chatAgentId = null;
807
+
808
+ function loadAgents() {
809
+ fetch('/api/agents').then(function(r) { return r.json(); }).then(function(list) {
810
+ agentList = list;
811
+ renderAgentList();
812
+ });
813
+ }
814
+
815
+ function renderAgentList() {
816
+ var listEl = document.getElementById('agentList');
817
+ var emptyState = document.getElementById('agentEmptyState');
818
+ listEl.innerHTML = '';
819
+ listEl.style.display = agentList.length === 0 ? 'none' : '';
820
+ emptyState.style.display = agentList.length === 0 ? '' : 'none';
821
+
822
+ agentList.forEach(function(a) {
823
+ var row = document.createElement('div');
824
+ row.className = 'agent-row';
825
+
826
+ // Status dot (openclaw only)
827
+ if (a.provider === 'openclaw') {
828
+ var dot = document.createElement('div');
829
+ dot.className = 'agent-status-dot ' + (a.connected ? 'connected' : 'disconnected');
830
+ dot.title = a.connected ? 'Connected' : 'Disconnected';
831
+ row.appendChild(dot);
832
+ }
833
+
834
+ // Info section
835
+ var info = document.createElement('div');
836
+ info.className = 'agent-row-info';
837
+
838
+ var nameEl = document.createElement('div');
839
+ nameEl.className = 'agent-row-name';
840
+ nameEl.textContent = a.name;
841
+
842
+ var descEl = document.createElement('div');
843
+ descEl.className = 'agent-row-desc';
844
+ descEl.textContent = a.description || '';
845
+
846
+ info.appendChild(nameEl);
847
+ info.appendChild(descEl);
848
+ row.appendChild(info);
849
+
850
+ // Badge
851
+ var badge = document.createElement('div');
852
+ badge.className = 'agent-row-badge';
853
+ badge.textContent = a.provider === 'openclaw' ? 'OpenClaw' : 'A2A';
854
+ row.appendChild(badge);
855
+
856
+ // Actions
857
+ var actions = document.createElement('div');
858
+ actions.className = 'agent-row-actions';
859
+
860
+ // Chat or Reconnect button (depending on connection state)
861
+ var isDisconnected = a.provider === 'openclaw' && a.enabled !== false && !a.connected;
862
+ var chatBtn = document.createElement('button');
863
+ chatBtn.className = 'filter-btn';
864
+ chatBtn.style.fontSize = '12px';
865
+ chatBtn.style.padding = '4px 10px';
866
+ if (isDisconnected) {
867
+ chatBtn.textContent = 'Reconnect';
868
+ chatBtn.addEventListener('click', (function(agent) {
869
+ return function(e) {
870
+ e.stopPropagation();
871
+ chatBtn.disabled = true;
872
+ chatBtn.textContent = 'Connecting...';
873
+ fetch('/api/agents/' + encodeURIComponent(agent.id) + '/connect', { method: 'POST' })
874
+ .then(function() { loadAgents(); })
875
+ .catch(function() { chatBtn.disabled = false; chatBtn.textContent = 'Reconnect'; });
876
+ };
877
+ })(a));
878
+ } else {
879
+ chatBtn.textContent = 'Chat';
880
+ chatBtn.addEventListener('click', function(e) {
881
+ e.stopPropagation();
882
+ openAgentChat(a);
883
+ });
884
+ }
885
+ actions.appendChild(chatBtn);
886
+
887
+ // Edit button
888
+ var editBtn = document.createElement('button');
889
+ editBtn.className = 'filter-btn';
890
+ editBtn.textContent = 'Edit';
891
+ editBtn.style.fontSize = '12px';
892
+ editBtn.style.padding = '4px 10px';
893
+ editBtn.addEventListener('click', function(e) {
894
+ e.stopPropagation();
895
+ openAgentEditModal(a);
896
+ });
897
+ actions.appendChild(editBtn);
898
+
899
+ // Enable/disable toggle
900
+ var toggleLabel = document.createElement('label');
901
+ toggleLabel.className = 'toggle-switch';
902
+ var toggleInput = document.createElement('input');
903
+ toggleInput.type = 'checkbox';
904
+ toggleInput.checked = a.enabled !== false;
905
+ toggleInput.addEventListener('click', function(e) { e.stopPropagation(); });
906
+ toggleInput.addEventListener('change', (function(agentId, inp) {
907
+ return function() {
908
+ fetch('/api/agents/' + encodeURIComponent(agentId), {
909
+ method: 'PATCH',
910
+ headers: { 'Content-Type': 'application/json' },
911
+ body: JSON.stringify({ enabled: inp.checked })
912
+ }).then(function() { loadAgents(); });
913
+ };
914
+ })(a.id, toggleInput));
915
+ var toggleSlider = document.createElement('span');
916
+ toggleSlider.className = 'toggle-slider';
917
+ toggleLabel.appendChild(toggleInput);
918
+ toggleLabel.appendChild(toggleSlider);
919
+ actions.appendChild(toggleLabel);
920
+
921
+ row.appendChild(actions);
922
+ listEl.appendChild(row);
923
+ });
924
+ }
925
+
926
+ // --- Agent quick chat ---
927
+ function openAgentChat(agent) {
928
+ chatAgentId = agent.id;
929
+ document.getElementById('agentChatTitle').textContent = 'Chat with ' + agent.name;
930
+ document.getElementById('agentChatMessages').innerHTML = '';
931
+ document.getElementById('agentChatInput').value = '';
932
+ var panel = document.getElementById('agentChatPanel');
933
+ panel.classList.add('open');
934
+ document.getElementById('agentChatInput').focus();
935
+ }
936
+
937
+ function closeAgentChat() {
938
+ chatAgentId = null;
939
+ document.getElementById('agentChatPanel').classList.remove('open');
940
+ }
941
+
942
+ function addChatMsg(role, text) {
943
+ var msgs = document.getElementById('agentChatMessages');
944
+ var div = document.createElement('div');
945
+ div.className = 'agent-chat-msg ' + role;
946
+ div.textContent = text;
947
+ msgs.appendChild(div);
948
+ msgs.scrollTop = msgs.scrollHeight;
949
+ return div;
950
+ }
951
+
952
+ function sendAgentChatMessage() {
953
+ if (!chatAgentId) return;
954
+ var input = document.getElementById('agentChatInput');
955
+ var message = input.value.trim();
956
+ if (!message) return;
957
+
958
+ input.value = '';
959
+ addChatMsg('user', message);
960
+
961
+ var sendBtn = document.getElementById('agentChatSendBtn');
962
+ sendBtn.disabled = true;
963
+ sendBtn.textContent = '...';
964
+
965
+ // Check if agent supports streaming
966
+ var agent = agentList.find(function(a) { return a.id === chatAgentId; });
967
+ var supportsStreaming = agent && agent.capabilities && agent.capabilities.streaming;
968
+
969
+ if (supportsStreaming) {
970
+ // Use streaming
971
+ var responseDiv = addChatMsg('agent', '');
972
+ var responseText = '';
973
+
974
+ fetch('/api/agents/' + encodeURIComponent(chatAgentId) + '/stream', {
975
+ method: 'POST',
976
+ headers: { 'Content-Type': 'application/json' },
977
+ body: JSON.stringify({ message: message })
978
+ }).then(function(res) {
979
+ if (!res.ok) return res.json().then(function(d) { throw new Error(d.error || 'Send failed'); });
980
+ var reader = res.body.getReader();
981
+ var decoder = new TextDecoder();
982
+ var buffer = '';
983
+ function pump() {
984
+ return reader.read().then(function(result) {
985
+ if (result.done) { sendBtn.disabled = false; sendBtn.textContent = 'Send'; return; }
986
+ buffer += decoder.decode(result.value, { stream: true });
987
+ var lines = buffer.split('\n');
988
+ buffer = lines.pop() || '';
989
+ for (var i = 0; i < lines.length; i++) {
990
+ var line = lines[i];
991
+ if (line.indexOf('data: ') === 0) {
992
+ var data = line.substring(6);
993
+ if (data === '[DONE]') { sendBtn.disabled = false; sendBtn.textContent = 'Send'; return; }
994
+ try {
995
+ var evt = JSON.parse(data);
996
+ if (evt.kind === 'text' && evt.data) {
997
+ responseText += evt.data;
998
+ responseDiv.textContent = responseText;
999
+ document.getElementById('agentChatMessages').scrollTop = document.getElementById('agentChatMessages').scrollHeight;
1000
+ } else if (evt.kind === 'error') {
1001
+ addChatMsg('error', 'Error: ' + (evt.data || 'Unknown error'));
1002
+ sendBtn.disabled = false; sendBtn.textContent = 'Send';
1003
+ return;
1004
+ }
1005
+ } catch (e) {}
1006
+ }
1007
+ }
1008
+ return pump();
1009
+ });
1010
+ }
1011
+ return pump();
1012
+ }).catch(function(err) {
1013
+ if (!responseText) responseDiv.remove();
1014
+ addChatMsg('error', 'Error: ' + err.message);
1015
+ sendBtn.disabled = false; sendBtn.textContent = 'Send';
1016
+ });
1017
+ } else {
1018
+ // Non-streaming
1019
+ addChatMsg('status', 'Waiting for response...');
1020
+ fetch('/api/agents/' + encodeURIComponent(chatAgentId) + '/send', {
1021
+ method: 'POST',
1022
+ headers: { 'Content-Type': 'application/json' },
1023
+ body: JSON.stringify({ message: message })
1024
+ }).then(function(r) {
1025
+ if (!r.ok) return r.json().then(function(d) { throw new Error(d.error || 'Send failed'); });
1026
+ return r.json();
1027
+ }).then(function(result) {
1028
+ // Remove "waiting" status
1029
+ var msgs = document.getElementById('agentChatMessages');
1030
+ var last = msgs.lastElementChild;
1031
+ if (last && last.classList.contains('status')) last.remove();
1032
+ addChatMsg('agent', result.text || JSON.stringify(result.raw, null, 2));
1033
+ }).catch(function(err) {
1034
+ var msgs = document.getElementById('agentChatMessages');
1035
+ var last = msgs.lastElementChild;
1036
+ if (last && last.classList.contains('status')) last.remove();
1037
+ addChatMsg('error', 'Error: ' + err.message);
1038
+ }).finally(function() {
1039
+ sendBtn.disabled = false; sendBtn.textContent = 'Send';
1040
+ });
1041
+ }
1042
+ }
1043
+
1044
+ document.getElementById('agentChatClose').addEventListener('click', closeAgentChat);
1045
+ document.getElementById('agentChatSendBtn').addEventListener('click', sendAgentChatMessage);
1046
+ document.getElementById('agentChatInput').addEventListener('keydown', function(e) {
1047
+ if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendAgentChatMessage(); }
1048
+ });
1049
+
1050
+ function setAgentType(type) {
1051
+ currentAgentType = type;
1052
+ document.getElementById('agentTypeA2A').className = 'filter-btn' + (type === 'a2a' ? ' active' : '');
1053
+ document.getElementById('agentTypeOpenClaw').className = 'filter-btn' + (type === 'openclaw' ? ' active' : '');
1054
+ document.getElementById('agentTokenGroup').style.display = type === 'openclaw' ? '' : 'none';
1055
+ document.getElementById('agentSessionKeyGroup').style.display = type === 'openclaw' ? '' : 'none';
1056
+ document.getElementById('agentSshTunnelGroup').style.display = type === 'openclaw' ? '' : 'none';
1057
+ document.getElementById('agentUrlHint').textContent = type === 'openclaw'
1058
+ ? 'Enter the gateway HTTP URL and token, then click Discover to verify connectivity.'
1059
+ : 'Enter a URL and click Discover to auto-fill from the agent card, or fill in the fields manually.';
1060
+ }
1061
+
1062
+ function resetAgentModal() {
1063
+ currentAgentId = null;
1064
+ currentAgentSkills = null;
1065
+ currentAgentCapabilities = null;
1066
+ setAgentType('a2a');
1067
+ document.getElementById('agentUrl').value = '';
1068
+ document.getElementById('agentToken').value = '';
1069
+ document.getElementById('agentSessionKey').value = '';
1070
+ document.getElementById('agentName').value = '';
1071
+ document.getElementById('agentDescription').value = '';
1072
+ document.getElementById('agentSkillsPreview').style.display = 'none';
1073
+ document.getElementById('agentSkillsList').innerHTML = '';
1074
+ document.getElementById('agentRemoveBtn').style.display = 'none';
1075
+ // Reset SSH tunnel fields
1076
+ document.getElementById('agentSshTunnelEnabled').checked = false;
1077
+ document.getElementById('agentSshCommand').value = '';
1078
+ document.getElementById('agentSshPassword').value = '';
1079
+ document.getElementById('agentSshTunnelFields').style.display = 'none';
1080
+ document.getElementById('agentSshTunnelArrow').innerHTML = '&#9654;';
1081
+ }
1082
+
1083
+ function openAgentAddModal() {
1084
+ resetAgentModal();
1085
+ document.getElementById('agentModalTitle').textContent = 'Add Agent';
1086
+ document.getElementById('agentModal').style.display = 'flex';
1087
+ }
1088
+
1089
+ function openAgentEditModal(agent) {
1090
+ resetAgentModal();
1091
+ currentAgentId = agent.id;
1092
+ setAgentType(agent.provider || 'a2a');
1093
+ document.getElementById('agentModalTitle').textContent = 'Edit Agent';
1094
+ document.getElementById('agentUrl').value = agent.url || '';
1095
+ document.getElementById('agentName').value = agent.name || '';
1096
+ document.getElementById('agentDescription').value = agent.description || '';
1097
+ // Token is stripped by the server — show placeholder in edit mode for openclaw
1098
+ if (agent.provider === 'openclaw') {
1099
+ document.getElementById('agentToken').placeholder = '(token saved — leave blank to keep)';
1100
+ document.getElementById('agentSessionKey').value = agent.sessionKey || '';
1101
+ // Populate SSH tunnel fields (password is stripped by server)
1102
+ if (agent.sshTunnel) {
1103
+ document.getElementById('agentSshTunnelEnabled').checked = !!agent.sshTunnel.enabled;
1104
+ document.getElementById('agentSshCommand').value = agent.sshTunnel.command || '';
1105
+ document.getElementById('agentSshPassword').placeholder = '(password saved — leave blank to keep)';
1106
+ if (agent.sshTunnel.enabled) {
1107
+ document.getElementById('agentSshTunnelFields').style.display = '';
1108
+ document.getElementById('agentSshTunnelArrow').innerHTML = '&#9660;';
1109
+ }
1110
+ }
1111
+ }
1112
+ document.getElementById('agentRemoveBtn').style.display = '';
1113
+ document.getElementById('agentModal').style.display = 'flex';
1114
+ }
1115
+
1116
+ function closeAgentModal() {
1117
+ document.getElementById('agentModal').style.display = 'none';
1118
+ resetAgentModal();
1119
+ }
1120
+
1121
+ function renderSkillsList(skills) {
1122
+ var container = document.getElementById('agentSkillsList');
1123
+ var wrapper = document.getElementById('agentSkillsPreview');
1124
+ if (!skills || skills.length === 0) {
1125
+ wrapper.style.display = 'none';
1126
+ container.innerHTML = '';
1127
+ return;
1128
+ }
1129
+ wrapper.style.display = '';
1130
+ container.innerHTML = skills.map(function(s) {
1131
+ var label = s.name || s.id || 'Skill';
1132
+ var desc = s.description ? ' — ' + s.description : '';
1133
+ return '<span style="display:inline-block;padding:2px 8px;margin:2px;border-radius:8px;background:rgba(138,43,226,.15);font-size:11px;">' + label + desc + '</span>';
1134
+ }).join('');
1135
+ }
1136
+
1137
+ document.getElementById('agentTypeA2A').addEventListener('click', function() { setAgentType('a2a'); });
1138
+ document.getElementById('agentTypeOpenClaw').addEventListener('click', function() { setAgentType('openclaw'); });
1139
+
1140
+ // SSH Tunnel collapsible section
1141
+ document.getElementById('agentSshTunnelToggleHeader').addEventListener('click', function() {
1142
+ var fields = document.getElementById('agentSshTunnelFields');
1143
+ var arrow = document.getElementById('agentSshTunnelArrow');
1144
+ var isOpen = fields.style.display !== 'none';
1145
+ fields.style.display = isOpen ? 'none' : '';
1146
+ arrow.innerHTML = isOpen ? '&#9654;' : '&#9660;';
1147
+ });
1148
+ document.getElementById('agentSshTunnelEnabled').addEventListener('change', function() {
1149
+ if (this.checked) {
1150
+ document.getElementById('agentSshTunnelFields').style.display = '';
1151
+ document.getElementById('agentSshTunnelArrow').innerHTML = '&#9660;';
1152
+ }
1153
+ });
1154
+
1155
+ document.getElementById('addAgentBtn').addEventListener('click', function(e) {
1156
+ e.stopPropagation();
1157
+ openAgentAddModal();
1158
+ });
1159
+ document.getElementById('agentCancelBtn').addEventListener('click', closeAgentModal);
1160
+
1161
+ var agentModalMouseDownTarget = null;
1162
+ document.getElementById('agentModal').addEventListener('mousedown', function(e) { agentModalMouseDownTarget = e.target; });
1163
+ document.getElementById('agentModal').addEventListener('click', function(e) {
1164
+ if (e.target === this && agentModalMouseDownTarget === this) closeAgentModal();
1165
+ agentModalMouseDownTarget = null;
1166
+ });
1167
+
1168
+ document.getElementById('agentDiscoverBtn').addEventListener('click', function() {
1169
+ var url = document.getElementById('agentUrl').value.trim();
1170
+ if (!url) return;
1171
+ var btn = this;
1172
+ btn.disabled = true;
1173
+ btn.textContent = 'Discovering...';
1174
+
1175
+ var body = { url: url, type: currentAgentType };
1176
+ if (currentAgentType === 'openclaw') {
1177
+ var token = document.getElementById('agentToken').value.trim();
1178
+ if (!token) { alert('Token is required for OpenClaw discovery.'); btn.disabled = false; btn.textContent = 'Discover'; return; }
1179
+ body.token = token;
1180
+ }
1181
+
1182
+ fetch('/api/agents/discover', {
1183
+ method: 'POST',
1184
+ headers: { 'Content-Type': 'application/json' },
1185
+ body: JSON.stringify(body)
1186
+ }).then(function(r) {
1187
+ if (!r.ok) return r.json().then(function(d) { throw new Error(d.error || 'Discovery failed'); });
1188
+ return r.json();
1189
+ }).then(function(result) {
1190
+ var nameEl = document.getElementById('agentName');
1191
+ var descEl = document.getElementById('agentDescription');
1192
+ if (!nameEl.value.trim() && result.name) nameEl.value = result.name;
1193
+ if (!descEl.value.trim() && result.description) descEl.value = result.description;
1194
+ if (result.skills) {
1195
+ currentAgentSkills = result.skills;
1196
+ renderSkillsList(currentAgentSkills);
1197
+ }
1198
+ if (result.capabilities) {
1199
+ currentAgentCapabilities = result.capabilities;
1200
+ }
1201
+ if (currentAgentType === 'openclaw' && result.verified) {
1202
+ alert('Gateway verified successfully!');
1203
+ }
1204
+ }).catch(function(err) {
1205
+ alert('Discovery failed: ' + err.message);
1206
+ }).finally(function() {
1207
+ btn.disabled = false;
1208
+ btn.textContent = 'Discover';
1209
+ });
1210
+ });
1211
+
1212
+ document.getElementById('agentSaveBtn').addEventListener('click', function() {
1213
+ var name = document.getElementById('agentName').value.trim();
1214
+ var url = document.getElementById('agentUrl').value.trim();
1215
+ var description = document.getElementById('agentDescription').value.trim();
1216
+ if (!name) { alert('Name is required.'); return; }
1217
+ if (!url) { alert('Agent URL is required.'); return; }
1218
+ if (!description) { alert('Description is required — explain what this agent does so pages know when to use it.'); return; }
1219
+
1220
+ var body = {
1221
+ url: url,
1222
+ name: name,
1223
+ description: description,
1224
+ enabled: true,
1225
+ provider: currentAgentType
1226
+ };
1227
+ if (currentAgentId) body.id = currentAgentId;
1228
+ if (currentAgentSkills) body.skills = currentAgentSkills;
1229
+ if (currentAgentCapabilities) body.capabilities = currentAgentCapabilities;
1230
+
1231
+ // Include token for openclaw agents
1232
+ if (currentAgentType === 'openclaw') {
1233
+ var token = document.getElementById('agentToken').value.trim();
1234
+ if (token) body.token = token;
1235
+ // For new openclaw agents, token is required
1236
+ if (!token && !currentAgentId) { alert('Token is required for OpenClaw agents.'); return; }
1237
+ // Set streaming capability by default for openclaw
1238
+ if (!body.capabilities) body.capabilities = { streaming: true };
1239
+ var sessionKey = document.getElementById('agentSessionKey').value.trim();
1240
+ if (sessionKey) body.sessionKey = sessionKey;
1241
+
1242
+ // SSH tunnel config
1243
+ var sshEnabled = document.getElementById('agentSshTunnelEnabled').checked;
1244
+ var sshCommand = document.getElementById('agentSshCommand').value.trim();
1245
+ var sshPassword = document.getElementById('agentSshPassword').value;
1246
+ if (sshEnabled || sshCommand) {
1247
+ body.sshTunnel = {
1248
+ enabled: sshEnabled,
1249
+ command: sshCommand
1250
+ };
1251
+ // Only include password if provided (otherwise server preserves existing)
1252
+ if (sshPassword) body.sshTunnel.password = sshPassword;
1253
+ }
1254
+ }
1255
+
1256
+ fetch('/api/agents', {
1257
+ method: 'POST',
1258
+ headers: { 'Content-Type': 'application/json' },
1259
+ body: JSON.stringify(body)
1260
+ }).then(function(r) {
1261
+ if (r.ok) { closeAgentModal(); loadAgents(); }
1262
+ });
1263
+ });
1264
+
1265
+ document.getElementById('agentRemoveBtn').addEventListener('click', function() {
1266
+ if (!currentAgentId) return;
1267
+ fetch('/api/agents/' + encodeURIComponent(currentAgentId), { method: 'DELETE' }).then(function(r) {
1268
+ if (r.ok) { closeAgentModal(); loadAgents(); }
1269
+ });
1270
+ });
1271
+
477
1272
  // --- Save logic ---
478
1273
  function saveAllSettings() {
479
1274
  var builderApiKey = document.getElementById('serviceApiKey-builder').value;
@@ -574,7 +1369,8 @@ document.getElementById('provider-chat').addEventListener('change', function() {
574
1369
  // --- Provider signup instructions ---
575
1370
  var providerInstructions = {
576
1371
  'Anthropic': 'Sign up at <a href="https://platform.claude.com" target="_blank">platform.claude.com</a>, then get your key at <a href="https://console.anthropic.com/settings/keys" target="_blank">console.anthropic.com/settings/keys</a>.',
577
- 'OpenAI': 'Sign up at <a href="https://auth.openai.com/create-account" target="_blank">auth.openai.com/create-account</a>, then get your key at <a href="https://platform.openai.com/api-keys" target="_blank">platform.openai.com/api-keys</a>.'
1372
+ 'OpenAI': 'Sign up at <a href="https://auth.openai.com/create-account" target="_blank">auth.openai.com/create-account</a>, then get your key at <a href="https://platform.openai.com/api-keys" target="_blank">platform.openai.com/api-keys</a>.',
1373
+ 'FireworksAI': 'Sign up at <a href="https://fireworks.ai" target="_blank">fireworks.ai</a>, then get your key at <a href="https://fireworks.ai/account/api-keys" target="_blank">fireworks.ai/account/api-keys</a>.'
578
1374
  };
579
1375
 
580
1376
  // --- "More settings" toggle state (works for all users) ---
@@ -761,6 +1557,7 @@ Promise.all([
761
1557
  document.getElementById('theme').value = currentTheme;
762
1558
 
763
1559
  loadConnectors();
1560
+ loadAgents();
764
1561
 
765
1562
  var isFirstRun = params.get('firstRun') === '1';
766
1563
 
@@ -804,6 +1601,7 @@ Promise.all([
804
1601
  // Disable other accordion tabs
805
1602
  document.querySelector('.accordion-section[data-section="general"]').classList.add('disabled');
806
1603
  document.querySelector('.accordion-section[data-section="connectors"]').classList.add('disabled');
1604
+ document.querySelector('.accordion-section[data-section="agents"]').classList.add('disabled');
807
1605
 
808
1606
  // Clear any existing values so wizard starts fresh
809
1607
  document.getElementById('provider-builder').value = '';