octo-agent 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. checksums.yaml +7 -0
  2. data/.clacky/skills/commit/SKILL.md +423 -0
  3. data/.clacky/skills/gem-release/SKILL.md +199 -0
  4. data/.clacky/skills/gem-release/scripts/release.sh +304 -0
  5. data/.clacky/skills/oss-upload/SKILL.md +47 -0
  6. data/.octorules +106 -0
  7. data/.rspec +3 -0
  8. data/.rubocop.yml +8 -0
  9. data/CHANGELOG.md +76 -0
  10. data/CODE_OF_CONDUCT.md +132 -0
  11. data/CONTRIBUTING.md +92 -0
  12. data/Dockerfile +28 -0
  13. data/LICENSE.txt +22 -0
  14. data/POSITIONING.md +46 -0
  15. data/README.md +134 -0
  16. data/README_CN.md +134 -0
  17. data/Rakefile +34 -0
  18. data/benchmark/fixtures/sample_project/Gemfile +3 -0
  19. data/benchmark/fixtures/sample_project/lib/api_handler.rb +32 -0
  20. data/benchmark/fixtures/sample_project/lib/order_calculator.rb +23 -0
  21. data/benchmark/fixtures/sample_project/lib/user_renderer.rb +20 -0
  22. data/benchmark/fixtures/sample_project/spec/order_calculator_spec.rb +20 -0
  23. data/benchmark/results/EVALUATION_REPORT.md +165 -0
  24. data/benchmark/results/baseline_20260511_174424.json +128 -0
  25. data/benchmark/results/report_20260511_175256.json +271 -0
  26. data/benchmark/results/report_20260511_175444.json +271 -0
  27. data/benchmark/results/treatment_20260511_175103.json +130 -0
  28. data/benchmark/runner.rb +441 -0
  29. data/bin/octo +7 -0
  30. data/docs/agent-first-ui-design.md +77 -0
  31. data/docs/billing-system.md +318 -0
  32. data/docs/channel-architecture.md +235 -0
  33. data/docs/engineering-article.md +343 -0
  34. data/docs/session-skill-invocation.md +69 -0
  35. data/docs/time_machine_design.md +247 -0
  36. data/docs/ui2-architecture.md +124 -0
  37. data/homebrew/README.md +96 -0
  38. data/homebrew/openocto.rb +24 -0
  39. data/lib/octo/agent/hook_manager.rb +61 -0
  40. data/lib/octo/agent/llm_caller.rb +800 -0
  41. data/lib/octo/agent/memory_updater.rb +246 -0
  42. data/lib/octo/agent/message_compressor.rb +225 -0
  43. data/lib/octo/agent/message_compressor_helper.rb +869 -0
  44. data/lib/octo/agent/next_message_suggester.rb +215 -0
  45. data/lib/octo/agent/session_serializer.rb +685 -0
  46. data/lib/octo/agent/skill_auto_creator.rb +114 -0
  47. data/lib/octo/agent/skill_evolution.rb +61 -0
  48. data/lib/octo/agent/skill_manager.rb +466 -0
  49. data/lib/octo/agent/skill_reflector.rb +89 -0
  50. data/lib/octo/agent/system_prompt_builder.rb +101 -0
  51. data/lib/octo/agent/time_machine.rb +214 -0
  52. data/lib/octo/agent/tool_executor.rb +454 -0
  53. data/lib/octo/agent/tool_registry.rb +150 -0
  54. data/lib/octo/agent.rb +2180 -0
  55. data/lib/octo/agent_config.rb +989 -0
  56. data/lib/octo/agent_profile.rb +112 -0
  57. data/lib/octo/anthropic_stream_aggregator.rb +137 -0
  58. data/lib/octo/background_task_registry.rb +324 -0
  59. data/lib/octo/banner.rb +34 -0
  60. data/lib/octo/bedrock_stream_aggregator.rb +137 -0
  61. data/lib/octo/block_font.rb +331 -0
  62. data/lib/octo/cli.rb +968 -0
  63. data/lib/octo/client.rb +623 -0
  64. data/lib/octo/default_agents/SOUL.md +3 -0
  65. data/lib/octo/default_agents/USER.md +1 -0
  66. data/lib/octo/default_agents/base_prompt.md +66 -0
  67. data/lib/octo/default_agents/coding/profile.yml +2 -0
  68. data/lib/octo/default_agents/coding/system_prompt.md +67 -0
  69. data/lib/octo/default_agents/general/profile.yml +2 -0
  70. data/lib/octo/default_agents/general/system_prompt.md +16 -0
  71. data/lib/octo/default_parsers/doc_parser.rb +69 -0
  72. data/lib/octo/default_parsers/docx_parser.rb +188 -0
  73. data/lib/octo/default_parsers/pdf_parser.rb +120 -0
  74. data/lib/octo/default_parsers/pdf_parser_ocr.py +103 -0
  75. data/lib/octo/default_parsers/pdf_parser_plumber.py +62 -0
  76. data/lib/octo/default_parsers/pptx_parser.rb +140 -0
  77. data/lib/octo/default_parsers/xlsx_parser.rb +121 -0
  78. data/lib/octo/default_skills/browser-setup/SKILL.md +426 -0
  79. data/lib/octo/default_skills/channel-manager/SKILL.md +623 -0
  80. data/lib/octo/default_skills/channel-manager/dingtalk_setup.rb +191 -0
  81. data/lib/octo/default_skills/channel-manager/discord_setup.rb +199 -0
  82. data/lib/octo/default_skills/channel-manager/feishu_setup.rb +574 -0
  83. data/lib/octo/default_skills/channel-manager/import_lark_skills.rb +97 -0
  84. data/lib/octo/default_skills/channel-manager/install_feishu_skills.rb +105 -0
  85. data/lib/octo/default_skills/channel-manager/weixin_setup.rb +274 -0
  86. data/lib/octo/default_skills/code-explorer/SKILL.md +36 -0
  87. data/lib/octo/default_skills/cron-task-creator/SKILL.md +257 -0
  88. data/lib/octo/default_skills/cron-task-creator/evals/evals.json +38 -0
  89. data/lib/octo/default_skills/onboard/SKILL.md +578 -0
  90. data/lib/octo/default_skills/onboard/scripts/import_external_skills.rb +413 -0
  91. data/lib/octo/default_skills/onboard/scripts/install_builtin_skills.rb +97 -0
  92. data/lib/octo/default_skills/persist-memory/SKILL.md +59 -0
  93. data/lib/octo/default_skills/personal-website/SKILL.md +113 -0
  94. data/lib/octo/default_skills/personal-website/publish.rb +235 -0
  95. data/lib/octo/default_skills/product-help/SKILL.md +123 -0
  96. data/lib/octo/default_skills/product-help/docs/agent-config.md +74 -0
  97. data/lib/octo/default_skills/product-help/docs/best-practices.md +49 -0
  98. data/lib/octo/default_skills/product-help/docs/browser-tool.md +53 -0
  99. data/lib/octo/default_skills/product-help/docs/built-in-skills.md +43 -0
  100. data/lib/octo/default_skills/product-help/docs/cli-reference.md +82 -0
  101. data/lib/octo/default_skills/product-help/docs/create-your-first-skill.md +47 -0
  102. data/lib/octo/default_skills/product-help/docs/faq.md +98 -0
  103. data/lib/octo/default_skills/product-help/docs/how-to-use-a-skill.md +58 -0
  104. data/lib/octo/default_skills/product-help/docs/installation.md +59 -0
  105. data/lib/octo/default_skills/product-help/docs/memory-system.md +61 -0
  106. data/lib/octo/default_skills/product-help/docs/octorules.md +62 -0
  107. data/lib/octo/default_skills/product-help/docs/session-management.md +63 -0
  108. data/lib/octo/default_skills/product-help/docs/skill-basics.md +55 -0
  109. data/lib/octo/default_skills/product-help/docs/skill-frontmatter.md +61 -0
  110. data/lib/octo/default_skills/product-help/docs/web-server.md +49 -0
  111. data/lib/octo/default_skills/product-help/docs/what-is-octo.md +37 -0
  112. data/lib/octo/default_skills/product-help/docs/windows-installation.md +36 -0
  113. data/lib/octo/default_skills/product-help/docs/writing-tips.md +53 -0
  114. data/lib/octo/default_skills/recall-memory/SKILL.md +65 -0
  115. data/lib/octo/default_skills/skill-add/SKILL.md +59 -0
  116. data/lib/octo/default_skills/skill-add/scripts/install_from_zip.rb +295 -0
  117. data/lib/octo/default_skills/skill-creator/SKILL.md +602 -0
  118. data/lib/octo/default_skills/skill-creator/agents/analyzer.md +274 -0
  119. data/lib/octo/default_skills/skill-creator/agents/comparator.md +202 -0
  120. data/lib/octo/default_skills/skill-creator/agents/grader.md +223 -0
  121. data/lib/octo/default_skills/skill-creator/eval-viewer/generate_review.py +471 -0
  122. data/lib/octo/default_skills/skill-creator/eval-viewer/viewer.html +1325 -0
  123. data/lib/octo/default_skills/skill-creator/references/schemas.md +430 -0
  124. data/lib/octo/default_skills/skill-creator/scripts/__init__.py +0 -0
  125. data/lib/octo/default_skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  126. data/lib/octo/default_skills/skill-creator/scripts/generate_report.py +326 -0
  127. data/lib/octo/default_skills/skill-creator/scripts/improve_description.py +310 -0
  128. data/lib/octo/default_skills/skill-creator/scripts/quick_validate.py +103 -0
  129. data/lib/octo/default_skills/skill-creator/scripts/run_eval.py +317 -0
  130. data/lib/octo/default_skills/skill-creator/scripts/run_loop.py +331 -0
  131. data/lib/octo/default_skills/skill-creator/scripts/utils.py +47 -0
  132. data/lib/octo/default_skills/skill-creator/scripts/validate_skill_frontmatter.rb +143 -0
  133. data/lib/octo/idle_compression_timer.rb +115 -0
  134. data/lib/octo/json_ui_controller.rb +204 -0
  135. data/lib/octo/message_format/anthropic.rb +409 -0
  136. data/lib/octo/message_format/bedrock.rb +361 -0
  137. data/lib/octo/message_format/open_ai.rb +222 -0
  138. data/lib/octo/message_history.rb +373 -0
  139. data/lib/octo/openai_stream_aggregator.rb +130 -0
  140. data/lib/octo/plain_ui_controller.rb +166 -0
  141. data/lib/octo/providers.rb +534 -0
  142. data/lib/octo/server/browser_manager.rb +397 -0
  143. data/lib/octo/server/channel/adapters/base.rb +82 -0
  144. data/lib/octo/server/channel/adapters/dingtalk/adapter.rb +314 -0
  145. data/lib/octo/server/channel/adapters/dingtalk/api_client.rb +391 -0
  146. data/lib/octo/server/channel/adapters/dingtalk/stream_client.rb +203 -0
  147. data/lib/octo/server/channel/adapters/discord/adapter.rb +229 -0
  148. data/lib/octo/server/channel/adapters/discord/api_client.rb +107 -0
  149. data/lib/octo/server/channel/adapters/discord/gateway_client.rb +270 -0
  150. data/lib/octo/server/channel/adapters/feishu/adapter.rb +320 -0
  151. data/lib/octo/server/channel/adapters/feishu/bot.rb +478 -0
  152. data/lib/octo/server/channel/adapters/feishu/file_processor.rb +36 -0
  153. data/lib/octo/server/channel/adapters/feishu/message_parser.rb +129 -0
  154. data/lib/octo/server/channel/adapters/feishu/ws_client.rb +423 -0
  155. data/lib/octo/server/channel/adapters/telegram/adapter.rb +375 -0
  156. data/lib/octo/server/channel/adapters/telegram/api_client.rb +205 -0
  157. data/lib/octo/server/channel/adapters/wecom/adapter.rb +148 -0
  158. data/lib/octo/server/channel/adapters/wecom/media_downloader.rb +115 -0
  159. data/lib/octo/server/channel/adapters/wecom/ws_client.rb +395 -0
  160. data/lib/octo/server/channel/adapters/weixin/adapter.rb +692 -0
  161. data/lib/octo/server/channel/adapters/weixin/api_client.rb +402 -0
  162. data/lib/octo/server/channel/channel_config.rb +178 -0
  163. data/lib/octo/server/channel/channel_manager.rb +468 -0
  164. data/lib/octo/server/channel/channel_ui_controller.rb +224 -0
  165. data/lib/octo/server/channel.rb +33 -0
  166. data/lib/octo/server/discover.rb +77 -0
  167. data/lib/octo/server/epipe_safe_io.rb +105 -0
  168. data/lib/octo/server/http_server.rb +3554 -0
  169. data/lib/octo/server/scheduler.rb +317 -0
  170. data/lib/octo/server/server_master.rb +325 -0
  171. data/lib/octo/server/session_registry.rb +431 -0
  172. data/lib/octo/server/web_ui_controller.rb +487 -0
  173. data/lib/octo/session_manager.rb +385 -0
  174. data/lib/octo/skill.rb +466 -0
  175. data/lib/octo/skill_loader.rb +328 -0
  176. data/lib/octo/tools/base.rb +118 -0
  177. data/lib/octo/tools/browser.rb +625 -0
  178. data/lib/octo/tools/edit.rb +165 -0
  179. data/lib/octo/tools/file_reader.rb +549 -0
  180. data/lib/octo/tools/glob.rb +162 -0
  181. data/lib/octo/tools/grep.rb +356 -0
  182. data/lib/octo/tools/invoke_skill.rb +96 -0
  183. data/lib/octo/tools/list_tasks.rb +54 -0
  184. data/lib/octo/tools/redo_task.rb +41 -0
  185. data/lib/octo/tools/request_user_feedback.rb +84 -0
  186. data/lib/octo/tools/security.rb +333 -0
  187. data/lib/octo/tools/terminal/output_cleaner.rb +63 -0
  188. data/lib/octo/tools/terminal/persistent_session.rb +268 -0
  189. data/lib/octo/tools/terminal/safe_rm.sh +106 -0
  190. data/lib/octo/tools/terminal/session_manager.rb +213 -0
  191. data/lib/octo/tools/terminal.rb +1828 -0
  192. data/lib/octo/tools/todo_manager.rb +374 -0
  193. data/lib/octo/tools/trash_manager.rb +388 -0
  194. data/lib/octo/tools/undo_task.rb +35 -0
  195. data/lib/octo/tools/web_fetch.rb +242 -0
  196. data/lib/octo/tools/web_search.rb +260 -0
  197. data/lib/octo/tools/write.rb +77 -0
  198. data/lib/octo/ui2/block_font.rb +10 -0
  199. data/lib/octo/ui2/components/base_component.rb +163 -0
  200. data/lib/octo/ui2/components/command_suggestions.rb +290 -0
  201. data/lib/octo/ui2/components/common_component.rb +96 -0
  202. data/lib/octo/ui2/components/inline_input.rb +226 -0
  203. data/lib/octo/ui2/components/input_area.rb +1338 -0
  204. data/lib/octo/ui2/components/message_component.rb +99 -0
  205. data/lib/octo/ui2/components/modal_component.rb +419 -0
  206. data/lib/octo/ui2/components/todo_area.rb +149 -0
  207. data/lib/octo/ui2/components/tool_component.rb +107 -0
  208. data/lib/octo/ui2/components/welcome_banner.rb +139 -0
  209. data/lib/octo/ui2/layout_manager.rb +807 -0
  210. data/lib/octo/ui2/line_editor.rb +363 -0
  211. data/lib/octo/ui2/markdown_renderer.rb +100 -0
  212. data/lib/octo/ui2/output_buffer.rb +370 -0
  213. data/lib/octo/ui2/progress_handle.rb +362 -0
  214. data/lib/octo/ui2/progress_indicator.rb +55 -0
  215. data/lib/octo/ui2/screen_buffer.rb +273 -0
  216. data/lib/octo/ui2/terminal_detector.rb +119 -0
  217. data/lib/octo/ui2/theme_manager.rb +85 -0
  218. data/lib/octo/ui2/themes/base_theme.rb +105 -0
  219. data/lib/octo/ui2/themes/hacker_theme.rb +62 -0
  220. data/lib/octo/ui2/themes/minimal_theme.rb +56 -0
  221. data/lib/octo/ui2/thinking_verbs.rb +26 -0
  222. data/lib/octo/ui2/ui_controller.rb +1625 -0
  223. data/lib/octo/ui2/view_renderer.rb +177 -0
  224. data/lib/octo/ui2.rb +40 -0
  225. data/lib/octo/ui_interface.rb +154 -0
  226. data/lib/octo/utils/arguments_parser.rb +191 -0
  227. data/lib/octo/utils/browser_detector.rb +195 -0
  228. data/lib/octo/utils/encoding.rb +92 -0
  229. data/lib/octo/utils/environment_detector.rb +140 -0
  230. data/lib/octo/utils/file_ignore_helper.rb +170 -0
  231. data/lib/octo/utils/file_processor.rb +601 -0
  232. data/lib/octo/utils/gitignore_parser.rb +154 -0
  233. data/lib/octo/utils/limit_stack.rb +152 -0
  234. data/lib/octo/utils/logger.rb +124 -0
  235. data/lib/octo/utils/login_shell.rb +72 -0
  236. data/lib/octo/utils/model_pricing.rb +646 -0
  237. data/lib/octo/utils/parser_manager.rb +165 -0
  238. data/lib/octo/utils/path_helper.rb +15 -0
  239. data/lib/octo/utils/scripts_manager.rb +59 -0
  240. data/lib/octo/utils/string_matcher.rb +158 -0
  241. data/lib/octo/utils/trash_directory.rb +112 -0
  242. data/lib/octo/utils/workspace_rules.rb +46 -0
  243. data/lib/octo/version.rb +5 -0
  244. data/lib/octo/web/app.css +7141 -0
  245. data/lib/octo/web/app.js +543 -0
  246. data/lib/octo/web/apple-touch-icon.png +0 -0
  247. data/lib/octo/web/auth.js +150 -0
  248. data/lib/octo/web/channels.js +276 -0
  249. data/lib/octo/web/datepicker.js +205 -0
  250. data/lib/octo/web/favicon.png +0 -0
  251. data/lib/octo/web/i18n.js +1073 -0
  252. data/lib/octo/web/icon-512.png +0 -0
  253. data/lib/octo/web/icon-dark.svg +25 -0
  254. data/lib/octo/web/icon.svg +29 -0
  255. data/lib/octo/web/index.html +871 -0
  256. data/lib/octo/web/marked.min.js +69 -0
  257. data/lib/octo/web/onboard.js +491 -0
  258. data/lib/octo/web/profile.js +442 -0
  259. data/lib/octo/web/sessions.js +4421 -0
  260. data/lib/octo/web/settings.js +913 -0
  261. data/lib/octo/web/sidebar.js +32 -0
  262. data/lib/octo/web/skills.js +885 -0
  263. data/lib/octo/web/tasks.js +297 -0
  264. data/lib/octo/web/theme.js +105 -0
  265. data/lib/octo/web/trash.js +343 -0
  266. data/lib/octo/web/vendor/hljs/highlight.min.js +1244 -0
  267. data/lib/octo/web/vendor/hljs/hljs-theme.css +95 -0
  268. data/lib/octo/web/vendor/katex/auto-render.min.js +1 -0
  269. data/lib/octo/web/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  270. data/lib/octo/web/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  271. data/lib/octo/web/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  272. data/lib/octo/web/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  273. data/lib/octo/web/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  274. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  275. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  276. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  277. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  278. data/lib/octo/web/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  279. data/lib/octo/web/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  280. data/lib/octo/web/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  281. data/lib/octo/web/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  282. data/lib/octo/web/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  283. data/lib/octo/web/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  284. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  285. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  286. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  287. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  288. data/lib/octo/web/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  289. data/lib/octo/web/vendor/katex/katex.min.css +1 -0
  290. data/lib/octo/web/vendor/katex/katex.min.js +1 -0
  291. data/lib/octo/web/version.js +449 -0
  292. data/lib/octo/web/weixin-qr.html +209 -0
  293. data/lib/octo/web/ws-dispatcher.js +357 -0
  294. data/lib/octo/web/ws.js +128 -0
  295. data/lib/octo.rb +145 -0
  296. data/scripts/build/build.sh +329 -0
  297. data/scripts/build/lib/apt.sh +56 -0
  298. data/scripts/build/lib/brew.sh +89 -0
  299. data/scripts/build/lib/colors.sh +17 -0
  300. data/scripts/build/lib/gem.sh +95 -0
  301. data/scripts/build/lib/mise.sh +125 -0
  302. data/scripts/build/lib/network.sh +157 -0
  303. data/scripts/build/lib/os.sh +57 -0
  304. data/scripts/build/lib/shell.sh +37 -0
  305. data/scripts/build/src/install.sh.cc +174 -0
  306. data/scripts/build/src/install_browser.sh.cc +101 -0
  307. data/scripts/build/src/install_full.sh.cc +290 -0
  308. data/scripts/build/src/install_rails_deps.sh.cc +145 -0
  309. data/scripts/build/src/install_system_deps.sh.cc +123 -0
  310. data/scripts/build/src/uninstall.sh.cc +101 -0
  311. data/scripts/install.ps1 +532 -0
  312. data/scripts/install.sh +567 -0
  313. data/scripts/install_browser.sh +479 -0
  314. data/scripts/install_full.sh +838 -0
  315. data/scripts/install_rails_deps.sh +746 -0
  316. data/scripts/install_system_deps.sh +518 -0
  317. data/scripts/uninstall.sh +287 -0
  318. data/sig/octo.rbs +4 -0
  319. metadata +614 -0
@@ -0,0 +1,871 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" id="html-root" data-font-size="medium">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title id="page-title">Octo</title>
7
+ <link rel="icon" type="image/png" href="/favicon.png">
8
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png">
9
+ <link rel="stylesheet" href="/vendor/katex/katex.min.css">
10
+ <link rel="stylesheet" href="/vendor/hljs/hljs-theme.css">
11
+ <link rel="stylesheet" href="/app.css">
12
+ <script>
13
+ // Inline theme init — must run before CSS renders to prevent flash of wrong theme.
14
+ (function() {
15
+ var saved = localStorage.getItem("octo-theme");
16
+ var theme = saved || (window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark");
17
+ document.documentElement.setAttribute("data-theme", theme);
18
+ })();
19
+ </script>
20
+ </head>
21
+ <body>
22
+ <div id="offline-banner" style="display:none" data-i18n="offline.banner"></div>
23
+ <div id="app" style="visibility:hidden">
24
+
25
+ <!-- ── TOP HEADER ──────────────────────────────────────────────────────── -->
26
+ <header id="top-header">
27
+ <div id="header-left">
28
+ <button id="btn-toggle-sidebar" class="sidebar-toggle-btn" title="Toggle sidebar">
29
+ <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="icon-sm">
30
+ <rect width="18" height="18" x="3" y="3" rx="2"/>
31
+ <path d="M9 3v18"/>
32
+ </svg>
33
+ </button>
34
+ <div id="header-brand" style="cursor:pointer" onclick="Router.navigate('chat')">
35
+ <img class="header-logo-img" id="header-logo-img" src="" alt="" style="display:none">
36
+ <span class="header-logo-divider"></span>
37
+ <span class="header-logo" id="header-logo">Octo</span>
38
+ </div>
39
+ </div>
40
+ <div id="header-right">
41
+ <button id="theme-toggle-header" class="theme-toggle-btn" title="Toggle theme">
42
+ <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="icon-sm">
43
+ <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>
44
+ </svg>
45
+ </button>
46
+ </div>
47
+ </header>
48
+
49
+ <!-- ── Sidebar overlay (mobile only) ───────────────────────────────────── -->
50
+ <div id="sidebar-overlay"></div>
51
+
52
+ <!-- ── CONTENT ROW (Sidebar + Main) ────────────────────────────────────── -->
53
+ <div id="content-row">
54
+
55
+ <!-- ── SIDEBAR ──────────────────────────────────────────────────────── -->
56
+ <aside id="sidebar">
57
+ <!-- Sidebar navigation groups -->
58
+ <div id="sidebar-list">
59
+ <!-- Chat Group -->
60
+ <div id="chat-section">
61
+ <!-- Header: "Sessions" label + 🔍 + [+ ▾] split button -->
62
+ <div class="sidebar-divider">
63
+ <span data-i18n="sidebar.chat">Sessions</span>
64
+ <div class="sidebar-divider-actions">
65
+ <!-- Magnifier toggle (shown when ≥10 sessions, managed by JS) -->
66
+ <button id="btn-session-search-toggle" class="btn-icon-sm" title="Search sessions" style="display:none" aria-label="Search sessions">
67
+ <svg width="13" height="13" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
68
+ <circle cx="6.5" cy="6.5" r="4.5" stroke="currentColor" stroke-width="1.6"/>
69
+ <line x1="10.3" y1="10.3" x2="14" y2="14" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
70
+ </svg>
71
+ </button>
72
+ <div class="btn-split-wrap">
73
+ <button id="btn-new-session-inline" class="btn-split-main" title="New Session" data-i18n="sessions.newSession">+ New Session</button>
74
+ <button id="btn-new-session-arrow" class="btn-split-arrow" title="Options">▾</button>
75
+ <!-- Dropdown -->
76
+ <div id="new-session-dropdown" class="new-session-dropdown" hidden>
77
+ <div class="dropdown-item" id="btn-new-session-modal" data-i18n="sessions.newSessionAdvanced">
78
+ <svg width="10" height="10" viewBox="0 0 16 16" fill="currentColor" style="vertical-align: -1px; margin-right: 5px; opacity: 0.6;">
79
+ <path d="M8 0a.5.5 0 01.5.5v2a.5.5 0 01-1 0v-2A.5.5 0 018 0zm0 13a.5.5 0 01.5.5v2a.5.5 0 01-1 0v-2A.5.5 0 018 13zm8-5a.5.5 0 01-.5.5h-2a.5.5 0 010-1h2a.5.5 0 01.5.5zM3 8a.5.5 0 01-.5.5h-2a.5.5 0 010-1h2A.5.5 0 013 8zm10.657-5.657a.5.5 0 010 .707l-1.414 1.415a.5.5 0 11-.707-.708l1.414-1.414a.5.5 0 01.707 0zm-9.193 9.193a.5.5 0 010 .707L3.05 13.657a.5.5 0 01-.707-.707l1.414-1.414a.5.5 0 01.707 0zm9.193 2.121a.5.5 0 01-.707 0l-1.414-1.414a.5.5 0 01.707-.707l1.414 1.414a.5.5 0 010 .707zM4.464 4.465a.5.5 0 01-.707 0L2.343 3.05a.5.5 0 11.707-.707l1.414 1.414a.5.5 0 010 .708z"/>
80
+ </svg>
81
+ <span data-i18n="sessions.newSessionAdvanced">More Options</span>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </div>
87
+
88
+ <!-- Search panel (hidden by default, toggled by magnifier button) -->
89
+ <div id="session-search-bar" class="session-search-panel" hidden>
90
+ <div class="search-panel-card">
91
+ <!-- Input row -->
92
+ <div class="search-input-row">
93
+ <span class="search-icon">🔍</span>
94
+ <input id="session-search-q" type="text" class="search-input"
95
+ data-i18n-placeholder="sessions.search.placeholder" placeholder="Search sessions…"
96
+ autocomplete="off" />
97
+ <button id="btn-search-q-clear" class="btn-search-q-clear" aria-label="Clear text" hidden>✕</button>
98
+ </div>
99
+ <!-- Filter row -->
100
+ <div class="search-filter-row">
101
+ <select id="session-search-type" class="search-select">
102
+ <option value="" data-i18n="sessions.search.typeAll">All types</option>
103
+ <option value="manual" data-i18n="sessions.search.typeManual">Default</option>
104
+ <option value="cron" data-i18n="sessions.search.typeCron">Scheduled</option>
105
+ <option value="channel" data-i18n="sessions.search.typeChannel">Channel</option>
106
+ <option value="setup" data-i18n="sessions.search.typeSetup">Setup</option>
107
+ <option value="coding" data-i18n="sessions.search.typeCoding">Coding</option>
108
+ </select>
109
+ <div class="search-date-wrap">
110
+ <button id="session-search-date" class="search-date datepicker-trigger" type="button" data-value="" data-i18n="sessions.search.datePlaceholder"></button>
111
+ </div>
112
+ <button id="btn-search-clear-all" class="btn-search-clear-all" aria-label="Clear filters" hidden>✕</button>
113
+ </div>
114
+ </div>
115
+ </div>
116
+
117
+ <!-- Cron view header (hidden by default, shown when viewing cron sessions) -->
118
+ <div id="cron-view-header" class="sidebar-divider" style="display:none">
119
+ <button id="btn-cron-back" class="btn-icon-sm" title="Back" aria-label="Back to session list">
120
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
121
+ <path d="M10 3L5 8l5 5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
122
+ </svg>
123
+ </button>
124
+ <span data-i18n="sessions.cronGroup">Scheduled Tasks</span>
125
+ <span></span><!-- spacer -->
126
+ </div>
127
+
128
+ <!-- Unified session list (all sources + profiles, newest first) -->
129
+ <div id="session-list"></div>
130
+ <!-- Load more button rendered dynamically by Sessions.renderList() -->
131
+ </div>
132
+
133
+ <!-- Config Group -->
134
+ <div id="config-section">
135
+ <div class="sidebar-divider"><span data-i18n="sidebar.config">Config</span></div>
136
+ <div id="config-nav-items">
137
+ <div id="tasks-sidebar-item" class="task-item task-item-summary">
138
+ <div class="task-row">
139
+ <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="task-icon">
140
+ <circle cx="12" cy="12" r="10"/>
141
+ <polyline points="12 6 12 12 16 14"/>
142
+ </svg>
143
+ <div class="task-info">
144
+ <span class="task-name" id="tasks-sidebar-label" data-i18n="sidebar.tasks">Task Management</span>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ <div id="skills-sidebar-item" class="task-item task-item-summary">
149
+ <div class="task-row">
150
+ <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="task-icon">
151
+ <path d="M19.439 7.85c-.049.322.059.648.289.878l1.568 1.568c.47.47.706 1.087.706 1.704s-.235 1.233-.706 1.704l-1.611 1.611a.98.98 0 0 1-.837.276c-.47-.07-.802-.48-.968-.925a2.501 2.501 0 1 0-3.214 3.214c.446.166.855.497.925.968a.979.979 0 0 1-.276.837l-1.61 1.61a2.404 2.404 0 0 1-1.705.707 2.402 2.402 0 0 1-1.704-.706l-1.568-1.568a1.026 1.026 0 0 0-.877-.29c-.493.074-.84.504-1.02.968a2.5 2.5 0 1 1-3.237-3.237c.464-.18.894-.527.967-1.02a1.026 1.026 0 0 0-.289-.877l-1.568-1.568A2.402 2.402 0 0 1 1.998 12c0-.617.236-1.234.706-1.704L4.23 8.77c.24-.24.581-.353.917-.303.515.077.877.528 1.073 1.01a2.5 2.5 0 1 0 3.259-3.259c-.482-.196-.933-.558-1.01-1.073-.05-.336.062-.676.303-.917l1.525-1.525A2.402 2.402 0 0 1 12 1.998c.617 0 1.234.236 1.704.706l1.568 1.568c.23.23.556.338.877.29.493-.074.84-.504 1.02-.968a2.5 2.5 0 1 1 3.237 3.237c-.464.18-.894.527-.967 1.02Z"/>
152
+ </svg>
153
+ <div class="task-info">
154
+ <span class="task-name" id="skills-sidebar-label" data-i18n="sidebar.skills">Skill Management</span>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ <div id="channels-sidebar-item" class="task-item task-item-summary">
159
+ <div class="task-row">
160
+ <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="task-icon">
161
+ <rect x="5" y="2" width="14" height="20" rx="2"/>
162
+ <path d="M12 18h.01"/>
163
+ </svg>
164
+ <div class="task-info">
165
+ <span class="task-name" id="channels-sidebar-label" data-i18n="sidebar.channels">Channel Management</span>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ </div>
171
+
172
+ <!-- My Data Group — personal data management (profile, memories, trash) -->
173
+ <div id="data-section">
174
+ <div class="sidebar-divider"><span data-i18n="sidebar.dataSection">My Data</span></div>
175
+ <div id="data-nav-items">
176
+ <div id="profile-sidebar-item" class="task-item task-item-summary">
177
+ <div class="task-row">
178
+ <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="task-icon">
179
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
180
+ <circle cx="12" cy="7" r="4"/>
181
+ </svg>
182
+ <div class="task-info">
183
+ <span class="task-name" id="profile-sidebar-label" data-i18n="sidebar.profile">Profile &amp; Soul</span>
184
+ </div>
185
+ </div>
186
+ </div>
187
+ <div id="memories-sidebar-item" class="task-item task-item-summary" style="display:none" aria-hidden="true">
188
+ <!-- Deprecated: Memories is now merged into the Profile panel.
189
+ Kept as a hidden placeholder so any legacy CSS selector that
190
+ targets this id still resolves harmlessly. -->
191
+ </div>
192
+ <div id="trash-sidebar-item" class="task-item task-item-summary">
193
+ <div class="task-row">
194
+ <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="task-icon">
195
+ <polyline points="3 6 5 6 21 6"/>
196
+ <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
197
+ <path d="M10 11v6"/><path d="M14 11v6"/>
198
+ <path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
199
+ </svg>
200
+ <div class="task-info">
201
+ <span class="task-name" id="trash-sidebar-label" data-i18n="sidebar.trash">File Recall</span>
202
+ </div>
203
+ </div>
204
+ </div>
205
+ </div>
206
+ </div>
207
+
208
+ </div>
209
+
210
+ <!-- Bottom Settings -->
211
+ <div id="sidebar-footer">
212
+ <div class="sidebar-nav-row">
213
+ <button id="btn-settings" class="sidebar-nav-btn" title="Settings">
214
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
215
+ <circle cx="12" cy="12" r="3"/>
216
+ <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
217
+ </svg>
218
+ <span data-i18n="sidebar.settings">Settings</span>
219
+ </button>
220
+ <!-- Version badge: independent button, right side of the row -->
221
+ <span id="version-badge" class="version-badge" style="display:none">
222
+ <span id="version-text" class="version-text"></span>
223
+ <span id="version-update-dot" class="version-update-dot" style="display:none"></span>
224
+ <span id="version-restart-dot" class="version-restart-dot" style="display:none"></span>
225
+ <span id="version-spinner" class="version-spinner" style="display:none"></span>
226
+ <span id="version-done-check" class="version-done-check" style="display:none">✓</span>
227
+ </span>
228
+ </div>
229
+ </div>
230
+ </aside>
231
+
232
+ <!-- ── MAIN ─────────────────────────────────────────────────────────── -->
233
+ <main id="main">
234
+
235
+
236
+ <!-- Onboard panel: kept as empty shell (soul_setup auto-launches /onboard session) -->
237
+ <div id="onboard-panel" style="display:none"></div>
238
+
239
+ <!-- Welcome screen -->
240
+ <div id="welcome" class="centered">
241
+ <div class="welcome-icon" style="display:none"></div>
242
+ <h2 id="welcome-title" data-i18n="welcome.title" data-i18n-vars="brand=Octo">Welcome to Octo</h2>
243
+ <p data-i18n="welcome.body">Create a new session or select one from the sidebar.</p>
244
+ <button id="btn-welcome-new" data-i18n="welcome.btn">New Session</button>
245
+ </div>
246
+
247
+ <!-- Scheduled Tasks list panel (shown when user clicks "Scheduled Tasks") -->
248
+ <div id="task-detail-panel" style="display:none">
249
+ <div id="task-detail-body">
250
+ <!-- Title and Description -->
251
+ <div class="task-page-header">
252
+ <h2 class="task-page-title" data-i18n="tasks.title">Scheduled Tasks</h2>
253
+ <p class="task-page-subtitle" data-i18n="tasks.subtitle">Manage and schedule automated tasks for your assistant</p>
254
+ <button id="btn-create-task" class="btn-create-task" title="Create a new task">
255
+ <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="icon-sm">
256
+ <path d="M5 12h14"/>
257
+ <path d="M12 5v14"/>
258
+ </svg>
259
+ <span data-i18n="tasks.btn.create">Create Task</span>
260
+ </button>
261
+ </div>
262
+
263
+ <div id="task-list-table"></div>
264
+ </div>
265
+ </div>
266
+
267
+ <!-- Chat panel -->
268
+ <div id="chat-panel" style="display:none">
269
+
270
+ <div id="messages"></div>
271
+ <!-- New message notification banner -->
272
+ <div id="new-message-banner" class="new-message-banner" style="display:none">
273
+ <span data-i18n="chat.newMessageHint">New messages ↓</span>
274
+ </div>
275
+ <!-- Session info bar — mirrors CLI bottom status bar -->
276
+ <div id="session-info-bar" style="display:none">
277
+ <!-- Order: status | id | dir | model | mode | tasks -->
278
+ <!-- Pattern: element + separator-after-element (except last) -->
279
+ <span id="sib-status"></span>
280
+ <span class="sib-sep sib-sep-after-status">│</span>
281
+ <span id="sib-bgtasks" class="sib-bgtasks" style="display:none" tabindex="0"
282
+ role="status" aria-live="polite"></span>
283
+ <span class="sib-sep sib-sep-after-bgtasks" style="display:none">│</span>
284
+ <span id="sib-id-wrap">
285
+ <span id="sib-id" class="sib-id-clickable" data-i18n-title="sessions.actions.tooltip" title="Click for session actions"></span>
286
+ <div id="sib-actions-dropdown" class="sib-actions-dropdown" style="display:none" role="menu"></div>
287
+ </span>
288
+ <span class="sib-sep sib-sep-after-id">│</span>
289
+ <span id="sib-dir" data-i18n-title="sib.dir.tooltip" title="Click to change directory"></span>
290
+ <span class="sib-sep sib-sep-after-dir">│</span>
291
+ <span id="sib-model-wrap">
292
+ <span id="sib-model" class="sib-model-clickable" data-i18n-title="sib.model.tooltip" title="Click to switch model"></span>
293
+ <div id="sib-model-dropdown" class="sib-model-dropdown" style="display:none"></div>
294
+ </span>
295
+ <span class="sib-sep sib-sep-after-model">│</span>
296
+ <span id="sib-reasoning-wrap">
297
+ <span id="sib-reasoning" class="sib-reasoning-clickable" data-i18n-title="sib.reasoning.tooltip" title="Click to change reasoning effort"></span>
298
+ <div id="sib-reasoning-dropdown" class="sib-reasoning-dropdown" style="display:none" role="menu"></div>
299
+ </span>
300
+ <span class="sib-sep sib-sep-after-reasoning">│</span>
301
+ <!-- Latency signal: 4-bar signal + TTFT number. Hidden until the first LLM
302
+ call completes (see updateInfoBar / Sessions.renderSignalBars). Click
303
+ opens a mini benchmark panel (see Step 3/4 — not yet implemented). -->
304
+ <span id="sib-signal-wrap" style="display:none">
305
+ <span id="sib-signal" class="sib-signal-clickable" data-i18n-title="sib.signal.tooltip" title="Recent LLM latency">
306
+ <span class="sig-bars" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
307
+ <span class="sig-text"></span>
308
+ </span>
309
+ </span>
310
+ <span class="sib-sep sib-sep-after-signal" style="display:none">│</span>
311
+ <!-- Detail fields: mode, tasks -->
312
+ <span class="sib-detail">
313
+ <span id="sib-mode"></span>
314
+ <span class="sib-sep sib-sep-after-mode">│</span>
315
+ <span id="sib-tasks"></span>
316
+ </span>
317
+ </div>
318
+
319
+ <div id="input-area">
320
+ <div id="ws-disconnect-hint" style="display:none"></div>
321
+ <!-- Skill autocomplete dropdown (shown when user types /xxx) -->
322
+ <div id="skill-autocomplete" style="display:none" role="listbox" aria-label="Skills">
323
+ <div class="skill-ac-header-row">
324
+ <div class="skill-ac-left">
325
+ <div class="skill-ac-title" data-i18n="sidebar.skills">Skills</div>
326
+ <label class="skill-ac-filter-toggle">
327
+ <input type="checkbox" id="chk-ac-show-system-skills">
328
+ <span class="skill-ac-toggle-track"></span>
329
+ <span class="skill-ac-filter-label" data-i18n="skills.filter.showSystemShort">System</span>
330
+ </label>
331
+ </div>
332
+ </div>
333
+ <div id="skill-autocomplete-list"></div>
334
+ </div>
335
+ <!-- Image preview strip (hidden when empty) -->
336
+ <div id="image-preview-strip" style="display:none"></div>
337
+ <!-- "N messages waiting" hint — only visible when the agent has
338
+ pending user messages it'll drain at the next iteration boundary -->
339
+ <div id="input-queue-hint" class="input-queue-hint" style="display:none"
340
+ role="status" aria-live="polite"></div>
341
+ <div id="input-bar">
342
+ <!-- Hidden file picker -->
343
+ <input type="file" id="image-file-input" accept="image/png,image/jpeg,image/gif,image/webp,application/pdf,application/zip,application/x-zip-compressed,application/gzip,application/x-gzip,application/x-tar,application/x-compressed-tar,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/msword,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.ms-powerpoint,text/csv,application/csv,text/markdown,text/plain,.csv,.md,.markdown,.txt,.log,.tar,.gz,.tgz,.tar.gz" multiple style="display:none">
344
+ <button id="btn-attach" title="Attach file (image, pdf, docx, md, tar.gz…) — drag & drop / Ctrl+V also work">
345
+ <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">
346
+ <path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66L9.41 17.41a2 2 0 0 1-2.83-2.83l8.49-8.48"/>
347
+ </svg>
348
+ </button>
349
+ <button id="btn-slash" title="Insert skill command (/)">
350
+ <span>/</span>
351
+ </button>
352
+ <textarea id="user-input" rows="1"
353
+ data-i18n-placeholder="chat.input.placeholder"
354
+ placeholder="Message… (Enter to send, Shift+Enter for newline)"></textarea>
355
+ <button id="btn-send" data-i18n="chat.btn.send">Send</button>
356
+ <button id="btn-interrupt" style="display:none" title="Stop"></button>
357
+ </div>
358
+ </div>
359
+ </div>
360
+
361
+ <!-- ── SKILLS PANEL ───────────────────────────────────────────────── -->
362
+ <div id="skills-panel" style="display:none">
363
+ <div id="skills-body">
364
+ <!-- Title and Description -->
365
+ <div class="skills-page-header">
366
+ <h2 class="skills-page-title" data-i18n="skills.title">Skills</h2>
367
+ <p class="skills-page-subtitle" data-i18n="skills.subtitle">Extend your assistant's capabilities with custom skills</p>
368
+ <div class="skill-action-btns">
369
+ <button id="btn-import-skill" class="btn-import-skill" title="Import skill from ZIP or GitHub URL">
370
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon-sm">
371
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
372
+ <polyline points="7 10 12 15 17 10"/>
373
+ <line x1="12" y1="15" x2="12" y2="3"/>
374
+ </svg>
375
+ <span data-i18n="skills.btn.import">Import</span>
376
+ </button>
377
+ <button id="btn-create-skill" class="btn-create-skill" title="Create a new skill with AI">
378
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon-sm">
379
+ <path d="M5 12h14"/>
380
+ <path d="M12 5v14"/>
381
+ </svg>
382
+ <span data-i18n="skills.btn.create">Create</span>
383
+ </button>
384
+ </div>
385
+ </div>
386
+
387
+ <!-- Tab bar with controls -->
388
+ <div id="skills-tabs">
389
+ <div class="skills-tabs-left">
390
+ <button class="skills-tab active" data-tab="my-skills" id="tab-my-skills" data-i18n="skills.tab.my">My Skills</button>
391
+ </div>
392
+ <div class="skills-tabs-controls">
393
+ <!-- Show system skills toggle -->
394
+ <label class="skills-filter-toggle" id="label-show-system">
395
+ <input type="checkbox" id="chk-show-system-skills">
396
+ <span class="skills-filter-toggle-track"></span>
397
+ <span class="skills-filter-label" data-i18n="skills.filter.showSystem">Show system skills</span>
398
+ </label>
399
+ </div>
400
+ </div>
401
+
402
+ <!-- Inline import bar (toggled by Import button) -->
403
+ <div id="skill-import-bar" class="skill-import-bar" style="display:none">
404
+ <div class="skill-import-bar-inner">
405
+ <!-- Link icon -->
406
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="skill-import-link-icon">
407
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
408
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
409
+ </svg>
410
+ <input id="skill-import-input" class="skill-import-input" type="text"
411
+ placeholder="Paste ZIP or GitHub URL…" data-i18n-placeholder="skills.import.placeholder" />
412
+ <!-- Hidden file input for local ZIP selection -->
413
+ <input id="skill-import-file" type="file" accept=".zip" style="display:none" />
414
+ <!-- Browse local file button -->
415
+ <button id="btn-skill-import-browse" class="btn-skill-import-browse" title="Choose local ZIP file">
416
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
417
+ <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
418
+ </svg>
419
+ </button>
420
+ <button id="btn-skill-import-confirm" class="btn-skill-import-confirm">
421
+ <span data-i18n="skills.import.install">Install</span>
422
+ </button>
423
+ <button id="btn-skill-import-cancel" class="btn-skill-import-cancel" title="Cancel">✕</button>
424
+ </div>
425
+ </div>
426
+ <div id="skills-list"></div>
427
+ </div>
428
+ </div>
429
+
430
+ <!-- ── CHANNELS PANEL ─────────────────────────────────────────────── -->
431
+ <div id="channels-panel" style="display:none">
432
+ <div id="channels-body">
433
+ <div class="channels-page-header">
434
+ <h2 class="channels-page-title" data-i18n="channels.title">Channels</h2>
435
+ <p class="channels-page-subtitle" data-i18n="channels.subtitle">Connect IM platforms so your users can chat with the assistant via Feishu or WeCom</p>
436
+ </div>
437
+
438
+ <div id="channels-list"></div>
439
+ </div>
440
+ </div>
441
+
442
+ <!-- ── TRASH PANEL — Recently deleted files across all projects ─── -->
443
+ <div id="trash-panel" style="display:none">
444
+ <div id="trash-body">
445
+ <div class="channels-page-header">
446
+ <h2 class="channels-page-title" data-i18n="trash.title">File Recall</h2>
447
+ <p class="channels-page-subtitle" data-i18n="trash.subtitle">Files the agent moved to trash across all projects. Recall them back to where they were, or clear the ones you don't need.</p>
448
+ </div>
449
+
450
+ <div class="trash-toolbar">
451
+ <span class="trash-summary" id="trash-summary"></span>
452
+ <div class="trash-actions">
453
+ <button id="btn-trash-refresh" class="btn-trash-action" title="Refresh">
454
+ <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
455
+ <path d="M21 2v6h-6"/><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/>
456
+ <path d="M3 22v-6h6"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/>
457
+ </svg>
458
+ <span data-i18n="trash.refresh">Refresh</span>
459
+ </button>
460
+ <button id="btn-trash-empty-old" class="btn-trash-action" title="Permanently delete files older than 7 days">
461
+ <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
462
+ <circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>
463
+ </svg>
464
+ <span data-i18n="trash.emptyOld">Empty &gt;7 days</span>
465
+ </button>
466
+ <button id="btn-trash-empty-orphans" class="btn-trash-action" title="Permanently delete entries whose original project directory no longer exists (e.g. test temp dirs)">
467
+ <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
468
+ <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
469
+ <line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>
470
+ </svg>
471
+ <span data-i18n="trash.emptyOrphans">Clean orphans</span>
472
+ </button>
473
+ <button id="btn-trash-empty-all" class="btn-trash-action btn-trash-danger" title="Permanently delete all trashed files">
474
+ <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
475
+ <polyline points="3 6 5 6 21 6"/>
476
+ <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
477
+ <path d="M10 11v6"/><path d="M14 11v6"/>
478
+ <path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
479
+ </svg>
480
+ <span data-i18n="trash.emptyAll">Empty all</span>
481
+ </button>
482
+ </div>
483
+ </div>
484
+
485
+ <div id="trash-list"></div>
486
+ </div>
487
+ </div>
488
+
489
+ <!-- ── PROFILE PANEL — "Assistant Memory": a 3-tab window into the agent's inner state.
490
+ All mutations happen through sessions (never in-browser editors):
491
+ SOUL tab → [让助理整理性格] → /onboard scope:soul
492
+ USER tab → [让助理更新档案] → /onboard scope:user
493
+ Memory tab → per-card [整理] → /onboard path:<abs>
494
+ per-card [删除] → direct DELETE /api/memories/:filename (with confirm)
495
+ ────────────────────────────────────────────────────────────── -->
496
+ <div id="profile-panel" style="display:none">
497
+ <div id="profile-body">
498
+ <div class="channels-page-header">
499
+ <h2 class="channels-page-title" data-i18n="profile.title">Assistant Memory</h2>
500
+ <p class="channels-page-subtitle" data-i18n="profile.subtitle">A window into the assistant's inner life: who it is, who you are, and what it remembers about your work together.</p>
501
+ </div>
502
+
503
+ <!-- Tab bar -->
504
+ <div class="profile-tabs" role="tablist">
505
+ <button class="profile-tab active" role="tab" aria-selected="true"
506
+ data-tab="soul" id="profile-tab-soul" data-i18n="profile.tab.soul">Soul</button>
507
+ <button class="profile-tab" role="tab" aria-selected="false"
508
+ data-tab="user" id="profile-tab-user" data-i18n="profile.tab.user">User</button>
509
+ <button class="profile-tab" role="tab" aria-selected="false"
510
+ data-tab="memories" id="profile-tab-memories" data-i18n="profile.tab.memories">Memories</button>
511
+ </div>
512
+
513
+ <!-- Tab: SOUL.md -->
514
+ <section class="profile-tab-panel active" id="profile-pane-soul" role="tabpanel" aria-labelledby="profile-tab-soul">
515
+ <header class="profile-section-head">
516
+ <h3 class="profile-section-title" data-i18n="profile.whoIAm">Who I am</h3>
517
+ <div class="profile-section-meta">
518
+ <span class="profile-path" id="profile-soul-path"></span>
519
+ <span class="profile-status" id="profile-soul-status"></span>
520
+ </div>
521
+ </header>
522
+ <div id="profile-soul-body" class="profile-markdown"></div>
523
+ <div class="profile-pane-footer">
524
+ <p class="profile-pane-footer-hint" data-i18n="profile.soul.curateHint">Not quite right? Let the assistant curate this through a short conversation.</p>
525
+ <button id="btn-profile-curate-soul" class="btn-profile-update" data-i18n="profile.soul.curateBtn">Have the assistant curate this</button>
526
+ </div>
527
+ </section>
528
+
529
+ <!-- Tab: USER.md -->
530
+ <section class="profile-tab-panel" id="profile-pane-user" role="tabpanel" aria-labelledby="profile-tab-user" style="display:none">
531
+ <header class="profile-section-head">
532
+ <h3 class="profile-section-title" data-i18n="profile.whoYouAre">Who you are</h3>
533
+ <div class="profile-section-meta">
534
+ <span class="profile-path" id="profile-user-path"></span>
535
+ <span class="profile-status" id="profile-user-status"></span>
536
+ </div>
537
+ </header>
538
+ <div id="profile-user-body" class="profile-markdown"></div>
539
+ <div class="profile-pane-footer">
540
+ <p class="profile-pane-footer-hint" data-i18n="profile.user.curateHint">Changed jobs? Picked up new interests? Let the assistant update your profile.</p>
541
+ <button id="btn-profile-curate-user" class="btn-profile-update" data-i18n="profile.user.curateBtn">Have the assistant update this</button>
542
+ </div>
543
+ </section>
544
+
545
+ <!-- Tab: Memories -->
546
+ <section class="profile-tab-panel" id="profile-pane-memories" role="tabpanel" aria-labelledby="profile-tab-memories" style="display:none">
547
+ <header class="profile-section-head">
548
+ <h3 class="profile-section-title" data-i18n="memories.title">Memories</h3>
549
+ <div class="profile-section-meta">
550
+ <span class="memories-summary" id="memories-summary"></span>
551
+ <button id="btn-memories-refresh-list" class="btn-memories-mini" data-i18n-title="memories.reloadList" data-i18n="memories.reloadList">Reload</button>
552
+ </div>
553
+ </header>
554
+ <p class="profile-section-hint" data-i18n="memories.subtitle">Long-term memories stored under ~/.octo/memories/. Most recent first. Use "Curate" to let the assistant tidy a memory, or "Delete" to drop one.</p>
555
+ <div id="memories-list"></div>
556
+ </section>
557
+ </div>
558
+ </div>
559
+
560
+ <!-- ── SETTINGS PANEL ──────────────────────────────────────────────── -->
561
+ <div id="settings-panel" style="display:none">
562
+ <header id="settings-header">
563
+ <span data-i18n="settings.title">Settings</span>
564
+ </header>
565
+ <div id="settings-body">
566
+
567
+ <!-- Models section -->
568
+ <section class="settings-section">
569
+ <div class="settings-section-title">
570
+ <span data-i18n="settings.models.title">AI Models</span>
571
+ <button id="btn-add-model" class="btn-settings-add" data-i18n="settings.models.add">+ Add Model</button>
572
+ </div>
573
+ <div id="model-cards"></div>
574
+ </section>
575
+
576
+ <!-- Personalize section -->
577
+ <section class="settings-section">
578
+ <div class="settings-section-title">
579
+ <span data-i18n="settings.personalize.title">Personalize</span>
580
+ </div>
581
+ <div class="settings-personalize">
582
+ <p class="settings-personalize-desc" data-i18n="settings.personalize.desc">Re-run the onboarding to update your assistant's personality and user profile (SOUL.md &amp; USER.md).</p>
583
+ <button id="btn-rerun-onboard" class="btn-settings-action" data-i18n="settings.personalize.btn">✨ Re-run Onboard</button>
584
+ </div>
585
+ </section>
586
+
587
+ <!-- Browser section -->
588
+ <section class="settings-section">
589
+ <div class="settings-section-title">
590
+ <span data-i18n="settings.browser.title">Browser</span>
591
+ </div>
592
+ <div class="settings-personalize">
593
+ <p class="settings-personalize-desc" id="browser-status-desc" data-i18n="settings.browser.desc">Connect your browser to enable browser automation.</p>
594
+ <div class="settings-browser-actions">
595
+ <label class="toggle-switch" id="browser-toggle-wrap" style="display:none">
596
+ <input type="checkbox" id="browser-toggle-input">
597
+ <span class="toggle-slider"></span>
598
+ </label>
599
+ <button id="btn-browser-setup" class="btn-settings-action" data-i18n="settings.browser.btn">🌐 Configure Browser</button>
600
+ </div>
601
+ </div>
602
+ </section>
603
+
604
+ <!-- Language section -->
605
+ <section class="settings-section" id="language-section">
606
+ <div class="settings-section-title">
607
+ <span data-i18n="settings.lang.title">Language</span>
608
+ </div>
609
+ <div class="settings-lang-btns">
610
+ <button id="settings-btn-lang-en" class="settings-lang-btn" data-lang="en" data-i18n="settings.lang.en">English</button>
611
+ <button id="settings-btn-lang-zh" class="settings-lang-btn" data-lang="zh" data-i18n="settings.lang.zh">中文</button>
612
+ </div>
613
+ </section>
614
+
615
+ <!-- Font Size section -->
616
+ <section class="settings-section" id="font-size-section">
617
+ <div class="settings-section-title">
618
+ <span data-i18n="settings.fontSize.title">Font Size</span>
619
+ </div>
620
+ <div class="settings-lang-btns">
621
+ <button id="settings-btn-font-small" class="settings-lang-btn" data-font="small" data-i18n="settings.fontSize.small">小</button>
622
+ <button id="settings-btn-font-medium" class="settings-lang-btn" data-font="medium" data-i18n="settings.fontSize.medium">中</button>
623
+ <button id="settings-btn-font-large" class="settings-lang-btn" data-font="large" data-i18n="settings.fontSize.large">大</button>
624
+ </div>
625
+ </section>
626
+
627
+ <!-- Currency section -->
628
+ <section class="settings-section" id="currency-section">
629
+ <div class="settings-section-title">
630
+ <span data-i18n="settings.currency.title">Currency</span>
631
+ </div>
632
+ <div class="settings-lang-btns">
633
+ <button id="settings-btn-currency-usd" class="settings-lang-btn" data-currency="USD">$ USD</button>
634
+ <button id="settings-btn-currency-cny" class="settings-lang-btn" data-currency="CNY">¥ CNY</button>
635
+ </div>
636
+ </section>
637
+
638
+
639
+
640
+ </div>
641
+ </div>
642
+
643
+ </main>
644
+
645
+ </div><!-- end #content-row -->
646
+
647
+ </div><!-- end #app -->
648
+
649
+ <!-- Setup panel: first-run mandatory setup (language + API key).
650
+ Rendered as a fixed full-screen overlay so the sidebar is hidden. -->
651
+ <div id="setup-panel" style="display:none">
652
+ <div id="setup-card">
653
+
654
+ <!-- Header: logo + brand + subtitle -->
655
+ <div id="setup-header">
656
+ <div id="setup-logo">✦</div>
657
+ <h1 id="setup-title" data-i18n="onboard.title" data-i18n-vars="brand=Octo">Welcome to Octo</h1>
658
+ <p id="setup-subtitle" data-i18n="onboard.subtitle">Let's get you set up in a minute.</p>
659
+ </div>
660
+
661
+ <!-- Step indicators -->
662
+ <div id="setup-steps">
663
+ <div class="setup-step active" id="setup-dot-1"><span>1</span></div>
664
+ <div class="setup-step-line"></div>
665
+ <div class="setup-step" id="setup-dot-2"><span>2</span></div>
666
+ </div>
667
+
668
+ <!-- Step 1: Language selection -->
669
+ <div id="setup-phase-lang">
670
+ <p class="setup-phase-label" data-i18n="onboard.lang.prompt">Choose your language</p>
671
+ <div id="setup-lang-row">
672
+ <div id="setup-lang-btns">
673
+ <button id="setup-btn-lang-en" class="setup-lang-btn">English</button>
674
+ <button id="setup-btn-lang-zh" class="setup-lang-btn active">中文</button>
675
+ </div>
676
+ <button id="setup-btn-lang-next" class="setup-next-btn" data-i18n="onboard.lang.next">Continue →</button>
677
+ </div>
678
+ </div>
679
+
680
+ <!-- Step 2: API Key setup -->
681
+ <div id="setup-phase-key" style="display:none">
682
+ <p class="setup-phase-label" data-i18n="onboard.key.title">Connect your AI model</p>
683
+
684
+ <div class="setup-field">
685
+ <label class="setup-label" data-i18n="onboard.key.provider">Provider</label>
686
+ <div class="custom-select-wrapper" id="setup-provider-wrapper">
687
+ <div class="custom-select-trigger" tabindex="0">
688
+ <span class="custom-select-value placeholder" data-i18n="onboard.key.provider.placeholder">— Choose provider —</span>
689
+ <svg class="custom-select-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
690
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
691
+ </svg>
692
+ </div>
693
+ <div class="custom-select-dropdown" id="setup-provider-dropdown">
694
+ <div class="custom-select-option" data-value="" data-i18n="onboard.key.provider.placeholder">— Choose provider —</div>
695
+ </div>
696
+ </div>
697
+ <div id="setup-provider-promo" class="provider-promo-hint"></div>
698
+ </div>
699
+
700
+ <div class="setup-field">
701
+ <label class="setup-label" data-i18n="onboard.key.model">Model</label>
702
+ <div class="model-name-combobox">
703
+ <input id="setup-model" type="text" class="setup-input model-name-input" data-i18n-placeholder="settings.models.placeholder.model" placeholder="e.g. claude-sonnet-4-5">
704
+ <button type="button" id="setup-model-dropdown-btn" class="model-name-dropdown-btn" aria-label="Select model">
705
+ <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
706
+ </button>
707
+ <div id="setup-model-dropdown" class="model-name-dropdown" style="display:none"></div>
708
+ </div>
709
+ </div>
710
+
711
+ <div class="setup-field">
712
+ <label class="setup-label" data-i18n="onboard.key.baseurl">Base URL</label>
713
+ <div class="base-url-combobox">
714
+ <input id="setup-base-url" type="text" class="setup-input base-url-input" data-i18n-placeholder="settings.models.placeholder.baseurl" placeholder="https://api.anthropic.com">
715
+ <button type="button" id="setup-base-url-dropdown-btn" class="base-url-dropdown-btn" aria-label="Select preset endpoint">
716
+ <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
717
+ </button>
718
+ <div id="setup-base-url-dropdown" class="base-url-dropdown" style="display:none"></div>
719
+ </div>
720
+ </div>
721
+
722
+ <div class="setup-field">
723
+ <label class="setup-label">
724
+ <span data-i18n="onboard.key.apikey">API Key</span>
725
+ <a id="setup-get-apikey-link" href="#" target="_blank" rel="noopener" style="display:none;margin-left:0.5rem;font-size:0.75rem;color:var(--accent,#6366f1);text-decoration:none;opacity:0.85;" data-i18n="onboard.key.getApiKey">How to get →</a>
726
+ </label>
727
+ <div class="setup-input-row">
728
+ <input id="setup-api-key" type="password" class="setup-input" data-i18n-placeholder="settings.models.placeholder.apikey" placeholder="sk-…">
729
+ <button id="setup-toggle-key" class="btn-toggle-key" title="Show/hide">
730
+ <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
731
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
732
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
733
+ </svg>
734
+ </button>
735
+ </div>
736
+ </div>
737
+
738
+ <div class="setup-docs-hint" style="font-size:0.75rem;margin-top:2px;text-align:left;">
739
+ <span style="color:var(--muted,#6b7280);" data-i18n="onboard.key.docsGuide.question">New to AI keys?</span>
740
+ <a id="setup-docs-link" href="https://www.octo.com/docs/ai-key-guide" target="_blank" rel="noopener" style="margin-left:0.25rem;color:var(--accent,#6366f1);text-decoration:none;" data-i18n="onboard.key.docsGuide.cta">See the guide →</a>
741
+ </div>
742
+
743
+ <div id="setup-test-result" class="setup-test-result" style="min-height:0;"></div>
744
+
745
+ <div class="setup-key-actions">
746
+ <button id="setup-btn-back" class="setup-back-btn" data-i18n="onboard.key.btn.back">← Back</button>
747
+ <button id="setup-btn-test" class="setup-submit-btn" data-i18n="onboard.key.btn.test">Test & Continue →</button>
748
+ </div>
749
+ </div>
750
+
751
+ </div>
752
+ </div>
753
+
754
+ <!-- ── NEW SESSION MODAL ─────────────────────────────────────────────────── -->
755
+ <div id="new-session-modal" class="modal-overlay" style="display:none">
756
+ <div class="modal-box new-session-modal">
757
+ <div class="modal-header">
758
+ <h3 class="modal-title" data-i18n="sessions.modal.title">Create New Session</h3>
759
+ </div>
760
+ <div class="modal-body">
761
+ <div class="modal-field">
762
+ <label class="modal-label" data-i18n="sessions.modal.agent">Agent</label>
763
+ <select id="new-session-agent" class="modal-select">
764
+ <option value="general" data-i18n="sessions.modal.agent.general">General</option>
765
+ <option value="coding" data-i18n="sessions.modal.agent.coding">Coding</option>
766
+ </select>
767
+ </div>
768
+
769
+ <div class="modal-field">
770
+ <label class="modal-label" data-i18n="sessions.modal.name">Session Name</label>
771
+ <input id="new-session-name" type="text" class="modal-input"
772
+ data-i18n-placeholder="sessions.modal.name.placeholder"
773
+ placeholder="Leave empty to auto-generate">
774
+ </div>
775
+
776
+ <div class="modal-field">
777
+ <label class="modal-label" data-i18n="sessions.modal.model">Model</label>
778
+ <select id="new-session-model" class="modal-select">
779
+ <!-- Populated dynamically from configured models -->
780
+ </select>
781
+ </div>
782
+
783
+ <div class="modal-field">
784
+ <label class="modal-label" data-i18n="sessions.modal.directory">Working Directory</label>
785
+ <input id="new-session-directory" type="text" class="modal-input"
786
+ data-i18n-placeholder="sessions.modal.directory.placeholder"
787
+ placeholder="~/workspace/my-project">
788
+ </div>
789
+
790
+ <div id="new-session-init-project-field" class="modal-field-checkbox" style="display:none">
791
+ <label class="modal-checkbox-label">
792
+ <input id="new-session-init-project" type="checkbox" class="modal-checkbox">
793
+ <span data-i18n="sessions.modal.initProject">Initialize full-stack project (/new)</span>
794
+ </label>
795
+ </div>
796
+ </div>
797
+ <div class="modal-footer">
798
+ <button id="new-session-cancel" class="btn-secondary" data-i18n="modal.cancel">Cancel</button>
799
+ <button id="new-session-create" class="btn-primary" data-i18n="sessions.modal.create">Create Session</button>
800
+ </div>
801
+ </div>
802
+ </div>
803
+
804
+ <!-- ── CONFIRMATION MODAL ────────────────────────────────────────────────── -->
805
+ <div id="modal-overlay" class="modal-overlay" style="display:none">
806
+ <div class="modal-box sm">
807
+ <div id="modal-message" class="modal-confirm-message"></div>
808
+ <div class="modal-actions">
809
+ <button id="modal-no" class="btn-secondary" data-i18n="modal.no">No</button>
810
+ <button id="modal-yes" class="btn-primary" data-i18n="modal.yes">Yes</button>
811
+ </div>
812
+ </div>
813
+ </div>
814
+
815
+ <!-- Prompt modal for text input -->
816
+ <div id="prompt-modal-overlay" class="modal-overlay" style="display:none">
817
+ <div class="modal-box sm">
818
+ <div id="prompt-modal-message" style="font-size:0.875rem;line-height:1.6;margin-bottom:0.75rem"></div>
819
+ <input type="text" id="prompt-modal-input" class="prompt-modal-input" autocomplete="off" spellcheck="false">
820
+ <div class="modal-actions">
821
+ <button id="prompt-modal-cancel" class="btn-secondary" data-i18n="modal.cancel">Cancel</button>
822
+ <button id="prompt-modal-ok" class="btn-primary" data-i18n="modal.ok">OK</button>
823
+ </div>
824
+ </div>
825
+ </div>
826
+
827
+ <!-- Rename Session Modal -->
828
+ <div id="rename-modal-overlay" class="modal-overlay" style="display:none">
829
+ <div class="modal-box sm">
830
+ <div class="modal-title" data-i18n="sessions.actions.rename">Rename</div>
831
+ <div class="modal-body">
832
+ <div class="modal-field">
833
+ <label class="modal-label" for="rename-modal-input">
834
+ <span data-i18n="sessions.modal.name">Name</span>
835
+ <span class="label-required">*</span>
836
+ </label>
837
+ <input type="text" id="rename-modal-input" class="modal-input" autocomplete="off" spellcheck="false">
838
+ </div>
839
+ </div>
840
+ <div class="modal-actions">
841
+ <button id="rename-modal-cancel" class="btn-secondary" data-i18n="modal.cancel">Cancel</button>
842
+ <button id="rename-modal-save" class="btn-primary" data-i18n="modal.ok">OK</button>
843
+ </div>
844
+ </div>
845
+ </div>
846
+
847
+
848
+
849
+ <script src="/marked.min.js"></script>
850
+ <script src="/vendor/hljs/highlight.min.js"></script>
851
+ <script src="/vendor/katex/katex.min.js"></script>
852
+ <script src="/vendor/katex/auto-render.min.js"></script>
853
+ <script src="/i18n.js"></script>
854
+ <script src="/auth.js"></script>
855
+ <script src="/theme.js"></script>
856
+ <script src="/ws.js"></script>
857
+ <script src="/ws-dispatcher.js"></script>
858
+ <script src="/sessions.js"></script>
859
+ <script src="/datepicker.js"></script>
860
+ <script src="/tasks.js"></script>
861
+ <script src="/skills.js"></script>
862
+ <script src="/channels.js"></script>
863
+ <script src="/settings.js"></script>
864
+ <script src="/onboard.js"></script>
865
+ <script src="/trash.js"></script>
866
+ <script src="/profile.js"></script>
867
+ <script src="/version.js"></script>
868
+ <script src="/sidebar.js"></script>
869
+ <script src="/app.js"></script>
870
+ </body>
871
+ </html>